diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index 09422bfd09b866..89e05ef4dc579f 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -15,7 +15,7 @@
]
},
"microsoft.dotnet.xharness.cli": {
- "version": "1.0.0-prerelease.21529.1",
+ "version": "1.0.0-prerelease.21579.1",
"commands": [
"xharness"
]
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index b3dfd99aebd89a..41e9c94c00deee 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -12,7 +12,11 @@
"settings": {
// Loading projects on demand is better for larger codebases
- "omnisharp.enableMsBuildLoadProjectsOnDemand": true
+ "omnisharp.enableMsBuildLoadProjectsOnDemand": true,
+ "omnisharp.enableRoslynAnalyzers": true,
+ "omnisharp.enableEditorConfigSupport": true,
+ "omnisharp.enableAsyncCompletion": true,
+ "omnisharp.testRunSettings": "${containerWorkspaceFolder}/artifacts/obj/vscode/.runsettings"
},
// Add the IDs of extensions you want installed when the container is created.
@@ -23,6 +27,9 @@
// Use 'onCreateCommand' to run pre-build commands inside the codespace
"onCreateCommand": "${containerWorkspaceFolder}/.devcontainer/scripts/onCreateCommand.sh",
+ // Use 'postCreateCommand' to run commands after the container is created.
+ "postCreateCommand": "${containerWorkspaceFolder}/.devcontainer/scripts/postCreateCommand.sh",
+
// Add the locally installed dotnet to the path to ensure that it is activated
// This allows developers to just use 'dotnet build' on the command-line, and the local dotnet version will be used.
"remoteEnv": {
diff --git a/.devcontainer/scripts/onCreateCommand.sh b/.devcontainer/scripts/onCreateCommand.sh
index dc0154c619b82e..faf7cb08d1afae 100755
--- a/.devcontainer/scripts/onCreateCommand.sh
+++ b/.devcontainer/scripts/onCreateCommand.sh
@@ -4,3 +4,8 @@ set -e
# prebuild the repo, so it is ready for development
./build.sh libs+clr -rc Release
+# restore libs tests so that the project is ready to be loaded by OmniSharp
+./build.sh libs.tests -restore
+
+# save the commit hash of the currently built assemblies, so developers know which version was built
+git rev-parse HEAD > ./artifacts/prebuild.sha
diff --git a/.devcontainer/scripts/postCreateCommand.sh b/.devcontainer/scripts/postCreateCommand.sh
new file mode 100755
index 00000000000000..8086037ab9af43
--- /dev/null
+++ b/.devcontainer/scripts/postCreateCommand.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+set -e
+
+# reset the repo to the commit hash that was used to build the prebuilt Codespace
+git reset --hard $(cat ./artifacts/prebuild.sha)
diff --git a/.github/ISSUE_TEMPLATE/02_api_proposal.yml b/.github/ISSUE_TEMPLATE/02_api_proposal.yml
index 145095e3bb42b2..541bcf5b73c7a8 100644
--- a/.github/ISSUE_TEMPLATE/02_api_proposal.yml
+++ b/.github/ISSUE_TEMPLATE/02_api_proposal.yml
@@ -33,7 +33,7 @@ body:
public void Fancy(T item);
}
}
- ```
+ ```
validations:
required: true
- type: textarea
@@ -41,7 +41,7 @@ body:
attributes:
label: API Usage
description: |
- Please provide code examples that highlight how the proposed API additions are meant to be consumed. This will help suggest whether the API has the right shape to be functional, performant and useable.
+ Please provide code examples that highlight how the proposed API additions are meant to be consumed. This will help suggest whether the API has the right shape to be functional, performant and usable.
placeholder: API usage
value: |
```C#
@@ -52,7 +52,7 @@ body:
// Getting the values out
foreach (var v in c)
Console.WriteLine(v);
- ```
+ ```
validations:
required: true
- type: textarea
diff --git a/.github/workflows/aspnetcore-sync-checkdiff.ps1 b/.github/workflows/aspnetcore-sync-checkdiff.ps1
new file mode 100644
index 00000000000000..b6ff80a408429b
--- /dev/null
+++ b/.github/workflows/aspnetcore-sync-checkdiff.ps1
@@ -0,0 +1,3 @@
+# Check the code is in sync
+$changed = (select-string "nothing to commit" artifacts\status.txt).count -eq 0
+return $changed
\ No newline at end of file
diff --git a/.github/workflows/aspnetcore-sync.yml b/.github/workflows/aspnetcore-sync.yml
new file mode 100644
index 00000000000000..01aa3dfdabca60
--- /dev/null
+++ b/.github/workflows/aspnetcore-sync.yml
@@ -0,0 +1,70 @@
+name: AspNetCore->Runtime Code Sync
+on:
+ # Manual run
+ workflow_dispatch:
+
+permissions:
+ contents: write
+ issues: write
+ pull-requests: write
+
+jobs:
+ compare_repos:
+ # Comment out this line to test the scripts in a fork
+ if: github.repository == 'dotnet/runtime'
+ name: Sync Code
+ runs-on: windows-latest
+ steps:
+ - name: Checkout aspnetcore
+ uses: actions/checkout@v2.0.0
+ with:
+ # Test this script using changes in a fork
+ repository: 'dotnet/aspnetcore'
+ path: aspnetcore
+ ref: main
+ - name: Checkout runtime
+ uses: actions/checkout@v2.0.0
+ with:
+ # Test this script using changes in a fork
+ repository: 'dotnet/runtime'
+ path: runtime
+ ref: main
+ - name: Copy
+ shell: cmd
+ working-directory: .\aspnetcore\src\Shared\runtime\
+ env:
+ RUNTIME_REPO: d:\a\runtime\runtime\runtime\
+ run: CopyToRuntime.cmd
+ - name: Diff
+ shell: cmd
+ working-directory: .\runtime\
+ run: |
+ mkdir ..\artifacts
+ git status > ..\artifacts\status.txt
+ git diff > ..\artifacts\diff.txt
+ - uses: actions/upload-artifact@v1
+ with:
+ name: results
+ path: artifacts
+ - name: Check
+ id: check
+ shell: pwsh
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ $changed = .\runtime\.github\workflows\aspnetcore-sync-checkdiff.ps1
+ echo "::set-output name=changed::$changed"
+ - name: Send PR
+ if: steps.check.outputs.changed == 'true'
+ # https://github.com/marketplace/actions/create-pull-request
+ uses: dotnet/actions-create-pull-request@v3
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ path: .\runtime
+ commit-message: 'Sync shared code from aspnetcore'
+ title: 'Sync shared code from aspnetcore'
+ body: 'This PR was automatically generated to sync shared code changes from aspnetcore. Fixes https://github.com/dotnet/aspnetcore/issues/18943'
+ labels: area-System.Net.Http
+ base: main
+ branch: github-action/sync-aspnetcore
+ branch-suffix: timestamp
diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml
index 9699f4db66961c..99ebe3b43d0cf8 100644
--- a/.github/workflows/backport.yml
+++ b/.github/workflows/backport.yml
@@ -3,6 +3,11 @@ on:
issue_comment:
types: [created]
+permissions:
+ contents: write
+ issues: write
+ pull-requests: write
+
jobs:
backport:
if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/backport to')
diff --git a/.github/workflows/create-codespaces-prebuild.yml b/.github/workflows/create-codespaces-prebuild.yml
index db269106e05a5a..cbae714eb4003c 100644
--- a/.github/workflows/create-codespaces-prebuild.yml
+++ b/.github/workflows/create-codespaces-prebuild.yml
@@ -6,6 +6,10 @@ on:
workflow_dispatch:
jobs:
createPrebuild:
+ # Only run in the main repository since it will fail in forks
+ if: github.repository == 'dotnet/runtime'
+ permissions:
+ contents: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
diff --git a/.github/workflows/markdownlint.yml b/.github/workflows/markdownlint.yml
index 202d6bbd2bf038..eeb9eb6fefe77c 100644
--- a/.github/workflows/markdownlint.yml
+++ b/.github/workflows/markdownlint.yml
@@ -1,5 +1,8 @@
name: Markdownlint
+permissions:
+ contents: read
+
on:
pull_request:
paths:
diff --git a/.gitignore b/.gitignore
index dbdbce21b052de..833551119f2d02 100644
--- a/.gitignore
+++ b/.gitignore
@@ -354,3 +354,7 @@ src/coreclr/System.Private.CoreLib/common
!src/coreclr/inc/obj/
!src/coreclr/vm/.vscode/
!src/coreclr/vm/.vscode/c_cpp_properties.json
+
+# Temporary artifacts from local libraries stress builds
+.dotnet-daily/
+run-stress-*
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
index 043dbed1f5f74a..10a7c114340a5d 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -266,13 +266,11 @@
true
- $(MSBuildProjectName.Contains('Experimental'))
$(MSBuildProjectName.Contains('Private'))
-
- true
- false
+
+ true
- false
+ false
$(RepositoryEngineeringDir)_._
@@ -295,9 +293,6 @@
Properties
false
-
- true
diff --git a/docs/area-owners.md b/docs/area-owners.md
index ef6f0bfd3dffca..b5f4b45a61a56d 100644
--- a/docs/area-owners.md
+++ b/docs/area-owners.md
@@ -22,39 +22,39 @@ Note: Editing this file doesn't update the mapping used by the `@msftbot` issue
| area-CrossGen/NGEN-coreclr | @mangod9 | @dotnet/crossgen-contrib | |
| area-crossgen2-coreclr | @mangod9 | @trylek @dotnet/crossgen-contrib | |
| area-Debugger-mono | @lewing | @thaystg @radical | |
-| area-DependencyModel | @ericstj | @maryamariyan @michaelgsharp @safern @tarekgh | Consultants: @eerhardt - Microsoft.Extensions.DependencyModel
|
+| area-DependencyModel | @ericstj | @eerhardt @maryamariyan @tarekgh | - Microsoft.Extensions.DependencyModel
|
| area-Diagnostics-coreclr | @tommcdon | @tommcdon | |
| area-EnC-mono | @marek-safar | @lambdageek | Hot Reload on WebAssembly, Android, iOS, etc |
| area-ExceptionHandling-coreclr | @mangod9 | @janvorli | |
-| area-Extensions-Caching | @ericstj | @maryamariyan @michaelgsharp @safern @tarekgh | Consultants: @eerhardt |
-| area-Extensions-Configuration | @ericstj | @maryamariyan @michaelgsharp @safern @tarekgh | Consultants: @eerhardt |
-| area-Extensions-DependencyInjection | @ericstj | @maryamariyan @michaelgsharp @safern @tarekgh | Consultants: @eerhardt |
-| area-Extensions-FileSystem | @jeffhandley | @adamsitnik @carlossanlop @jozkee | Consultants: @eerhardt @GrabYourPitchforks |
-| area-Extensions-Hosting | @ericstj | @maryamariyan @michaelgsharp @safern @tarekgh | Consultants: @eerhardt |
+| area-Extensions-Caching | @ericstj | @eerhardt @maryamariyan @tarekgh | Consultants: @adamsitnik |
+| area-Extensions-Configuration | @ericstj | @eerhardt @maryamariyan @tarekgh | |
+| area-Extensions-DependencyInjection | @ericstj | @eerhardt @maryamariyan @tarekgh | |
+| area-Extensions-FileSystem | @jeffhandley | @adamsitnik @carlossanlop @jozkee | Consultants: @eerhardt |
+| area-Extensions-Hosting | @ericstj | @eerhardt @maryamariyan @tarekgh | |
| area-Extensions-HttpClientFactory | @karelz | @dotnet/ncl | |
-| area-Extensions-Logging | @ericstj | @maryamariyan @michaelgsharp @safern @tarekgh | Consultants: @eerhardt |
-| area-Extensions-Options | @ericstj | @maryamariyan @michaelgsharp @safern @tarekgh | Consultants: @eerhardt |
-| area-Extensions-Primitives | @ericstj | @maryamariyan @michaelgsharp @safern @tarekgh | Consultants: @eerhardt |
+| area-Extensions-Logging | @ericstj | @eerhardt @maryamariyan @tarekgh | |
+| area-Extensions-Options | @ericstj | @eerhardt @maryamariyan @tarekgh | |
+| area-Extensions-Primitives | @ericstj | @eerhardt @maryamariyan @tarekgh | |
| area-GC-coreclr | @mangod9 | @Maoni0 | |
| area-GC-mono | @SamMonoRT | @BrzVlad | |
| area-Host | @agocke | @jeffschwMSFT @vitek-karas @vsadov | Issues with dotnet.exe including bootstrapping, framework detection, hostfxr.dll and hostpolicy.dll |
| area-HostModel | @agocke | @vitek-karas | |
| area-ILTools-coreclr | @JulieLeeMSFT | @BruceForstall @dotnet/jit-contrib | |
| area-ILVerification | @JulieLeeMSFT | @BruceForstall @dotnet/jit-contrib | |
-| area-Infrastructure | @ericstj | @Anipik @ViktorHofer @jeffschwMSFT @dleeapho | |
+| area-Infrastructure | @agocke | @jeffschwMSFT @dleeapho | |
| area-Infrastructure-coreclr | @agocke | @jeffschwMSFT @trylek | |
| area-Infrastructure-installer | @dleeapho | @dleeapho @NikolaMilosavljevic | |
-| area-Infrastructure-libraries | @ericstj | @Anipik @ViktorHofer | Covers:- Packaging
- Build and test infra for libraries in dotnet/runtime repo
- VS integration
|
+| area-Infrastructure-libraries | @ericstj | @safern | Covers:- Packaging
- Build and test infra for libraries in dotnet/runtime repo
- VS integration
|
| area-Infrastructure-mono | @steveisok | @directhex | |
| area-Interop-coreclr | @jeffschwMSFT | @jeffschwMSFT @AaronRobinsonMSFT | |
| area-Interop-mono | @marek-safar | @lambdageek | |
-| area-Meta | @jeffhandley | @buyaa-n @joperezr | Issues without clear association to any specific API/contract, e.g. - new contract proposals
- cross-cutting code/test pattern changes (e.g. FxCop failures)
- project-wide docs
|
+| area-Meta | @danmoseley | @ericstj @jeffhandley | Cross-cutting concerns that span many or all areas, including project-wide code/test patterns and documentation. |
| area-Microsoft.CSharp | @jaredpar | @cston @333fred | Archived component - limited churn/contributions (see [#27790](https://github.com/dotnet/runtime/issues/27790)) |
-| area-Microsoft.Extensions | @ericstj | @maryamariyan @michaelgsharp @safern @tarekgh | Consultants: @eerhardt |
+| area-Microsoft.Extensions | @ericstj | @eerhardt @maryamariyan @tarekgh | |
| area-Microsoft.VisualBasic | @jaredpar | @cston @333fred | Archived component - limited churn/contributions (see [#27790](https://github.com/dotnet/runtime/issues/27790)) |
-| area-Microsoft.Win32 | @ericstj | @Anipik @ViktorHofer | Including System.Windows.Extensions |
+| area-Microsoft.Win32 | @ericstj | @safern | Including System.Windows.Extensions |
+| area-NativeAOT-coreclr | @agocke | @MichalStrehovsky @jkotas | |
| area-PAL-coreclr | @mangod9 | @janvorli | |
-| area-PAL-libraries | @jeffhandley | @bartonjs @GrabYourPitchforks | |
| area-Performance-mono | @SamMonoRT | @SamMonoRT | |
| area-R2RDump-coreclr | @mangod9 | @trylek | |
| area-ReadyToRun-coreclr | @mangod9 | @trylek | |
@@ -62,75 +62,75 @@ Note: Editing this file doesn't update the mapping used by the `@msftbot` issue
| area-Setup | @dleeapho | @NikolaMilosavljevic @dleeapho | Distro-specific (Linux, Mac and Windows) setup packages and msi files |
| area-Single-File | @agocke | @vitek-karas @vsadov | |
| area-Snap | @dleeapho | @dleeapho @leecow @MichaelSimons | |
-| area-System.Buffers | @jeffhandley | @tannergooding | Consultants: @GrabYourPitchforks |
-| area-System.CodeDom | @jeffhandley | @buyaa-n @joperezr | |
-| area-System.Collections | @ericstj | @eiriktsarpalis @layomia @krwq | Excluded:- System.Array -> System.Runtime
Consultants: @steveharter @GrabYourPitchForks |
-| area-System.ComponentModel | @ericstj | @maryamariyan @michaelgsharp @safern @tarekgh | |
-| area-System.ComponentModel.Composition | @jeffhandley | @buyaa-n @joperezr | Consultants: @steveharter |
+| area-System.Buffers | @jeffhandley | @michaelgsharp @tannergooding | Consultants: @GrabYourPitchforks |
+| area-System.CodeDom | @ericstj | @buyaa-n @joperezr @steveharter | |
+| area-System.Collections | @jeffhandley | @eiriktsarpalis @krwq @layomia | Excluded:- System.Array -> System.Runtime
Consultants: @steveharter @GrabYourPitchForks |
+| area-System.ComponentModel | @ericstj | @eerhardt @maryamariyan @tarekgh | |
+| area-System.ComponentModel.Composition | @jeffhandley | @eerhardt @maryamariyan @tarekgh | |
| area-System.ComponentModel.DataAnnotations | @ajcvickers | @lajones @ajcvickers | Included:- System.ComponentModel.Annotations
|
-| area-System.Composition | @jeffhandley | @buyaa-n @joperezr | Consultants: @steveharter |
-| area-System.Configuration | @jeffhandley | @buyaa-n @joperezr | Consultants: @steveharter |
+| area-System.Composition | @jeffhandley | @eerhardt @maryamariyan @tarekgh | |
+| area-System.Configuration | @ericstj | @buyaa-n @joperezr @steveharter | |
| area-System.Console | @jeffhandley | @adamsitnik @carlossanlop @jozkee | Consultants: @GrabYourPitchForks |
-| area-System.Data | @ajcvickers | @ajcvickers @cheenamalhotra @david-engel | - Odbc, OleDb - @saurabh500
|
+| area-System.Data | @ajcvickers | @ajcvickers @davoudeshtehari @david-engel | - Odbc, OleDb - @saurabh500
|
| area-System.Data.Odbc | @ajcvickers | @ajcvickers | |
| area-System.Data.OleDB | @ajcvickers | @ajcvickers | |
-| area-System.Data.SqlClient | @David-Engel | @cheenamalhotra @david-engel @karinazhou @JRahnama | Archived component - limited churn/contributions (see https://devblogs.microsoft.com/dotnet/introducing-the-new-microsoftdatasqlclient/) |
+| area-System.Data.SqlClient | @David-Engel | @davoudeshtehari @david-engel @jrahnama | Archived component - limited churn/contributions (see https://devblogs.microsoft.com/dotnet/introducing-the-new-microsoftdatasqlclient/) |
| area-System.Diagnostics | @tommcdon | @tommcdon | |
| area-System.Diagnostics-coreclr | @tommcdon | @tommcdon | |
-| area-System.Diagnostics-mono | @lewing | @thaystg @radical | |
-| area-System.Diagnostics.Activity | @tommcdon | @tarekgh | |
-| area-System.Diagnostics.EventLog | @ericstj | @Anipik @ViktorHofer | |
+| area-System.Diagnostics-mono | @lewing | @thaystg @radical | |
+| area-System.Diagnostics.Activity | @tommcdon | @eerhardt @maryamariyan @tarekgh | |
+| area-System.Diagnostics.EventLog | @ericstj | @safern | |
| area-System.Diagnostics.Metric | @tommcdon | @noahfalk | |
-| area-System.Diagnostics.PerformanceCounter | @ericstj | @Anipik @ViktorHofer | |
+| area-System.Diagnostics.PerformanceCounter | @ericstj | @safern | |
| 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
|
-| area-System.Diagnostics.TraceSource | @ericstj | @Anipik @ViktorHofer | |
-| area-System.DirectoryServices | @jeffhandley | @buyaa-n @joperezr | Also: @BRDPM @grubioe @jay98014 |
-| area-System.Drawing | @ericstj | @maryamariyan @michaelgsharp @safern @tarekgh | |
+| area-System.Diagnostics.Tracing | @tommcdon | @noahfalk @tommcdon @safern @tarekgh | Included: - System.Diagnostics.DiagnosticSource
- System.Diagnostics.TraceSource
|
+| area-System.Diagnostics.TraceSource | @ericstj | @safern | |
+| area-System.DirectoryServices | @ericstj | @buyaa-n @joperezr @steveharter | Consultants: @BRDPM @grubioe @jay98014 |
+| area-System.Drawing | @ericstj | @safern | |
| area-System.Dynamic.Runtime | @jaredpar | @cston @333fred | Archived component - limited churn/contributions (see [#27790](https://github.com/dotnet/runtime/issues/27790)) |
| area-System.Formats.Asn1 | @jeffhandley | @bartonjs @GrabYourPitchforks | |
-| area-System.Formats.Cbor | @ericstj | @eiriktsarpalis @layomia @krwq | Consultants: @bartonjs @GrabYourPitchForks |
-| area-System.Globalization | @ericstj | @maryamariyan @michaelgsharp @safern @tarekgh | |
+| area-System.Formats.Cbor | @jeffhandley | @bartonjs @GrabYourPitchforks | Consultants: @eiriktsarpalis |
+| area-System.Globalization | @ericstj | @eerhardt @maryamariyan @tarekgh | |
| area-System.IO | @jeffhandley | @adamsitnik @carlossanlop @jozkee | |
-| area-System.IO.Compression | @jeffhandley | @adamsitnik @carlossanlop @jozkee | Consultants: @ericstj - Also includes System.IO.Packaging
|
+| area-System.IO.Compression | @jeffhandley | @adamsitnik @carlossanlop @jozkee | - Also includes System.IO.Packaging
|
| area-System.IO.Pipelines | @kevinpi | @davidfowl @halter73 @jkotalik | |
-| area-System.Linq | @ericstj | @eiriktsarpalis @layomia @krwq | Consultants: @steveharter |
+| area-System.Linq | @jeffhandley | @eiriktsarpalis @krwq @layomia | |
| area-System.Linq.Expressions | @jaredpar | @cston @333fred | Archived component - limited churn/contributions (see [#27790](https://github.com/dotnet/runtime/issues/27790)) |
| area-System.Linq.Parallel | @jeffhandley | @adamsitnik @carlossanlop @jozkee | Consultants: @stephentoub @kouvel |
-| area-System.Management | @ericstj | @Anipik @ViktorHofer | WMI |
+| area-System.Management | @ericstj | @safern | WMI |
| area-System.Memory | @jeffhandley | @adamsitnik @carlossanlop @jozkee | Consultants: @GrabYourPitchforks |
| area-System.Net | @karelz | @dotnet/ncl | Included: |
| area-System.Net.Http | @karelz | @dotnet/ncl | |
| area-System.Net.Quic | @karelz | @dotnet/ncl | |
| area-System.Net.Security | @karelz | @dotnet/ncl | |
| area-System.Net.Sockets | @karelz | @dotnet/ncl | |
-| area-System.Numerics | @jeffhandley | @tannergooding | |
-| area-System.Numerics.Tensors | @jeffhandley | @tannergooding | |
-| area-System.Reflection | @jeffhandley | @buyaa-n @joperezr @steveharter | |
+| area-System.Numerics | @jeffhandley | @michaelgsharp @tannergooding | |
+| area-System.Numerics.Tensors | @jeffhandley | @michaelgsharp @tannergooding | |
+| area-System.Reflection | @ericstj | @buyaa-n @joperezr @steveharter | |
| area-System.Reflection-mono | @SamMonoRT | @lambdageek | MonoVM-specific reflection and reflection-emit issues |
-| area-System.Reflection.Emit | @jeffhandley | @buyaa-n @joperezr @steveharter | Consultants: @GrabYourPitchforks |
-| area-System.Reflection.Metadata | @jeffhandley | @buyaa-n @joperezr @steveharter | Consultants: @GrabYourPitchforks @tmat |
-| area-System.Resources | @jeffhandley | @buyaa-n @joperezr | |
-| area-System.Runtime | @jeffhandley | @tannergooding | Consultants: @bartonjs @GrabYourPitchforks
Included:- System.Runtime.Serialization.Formatters
- System.Runtime.InteropServices.RuntimeInfo
- System.Array
Excluded:- Path -> System.IO
- StopWatch -> System.Diagnostics
- Uri -> System.Net
- WebUtility -> System.Net
|
+| area-System.Reflection.Emit | @ericstj | @buyaa-n @joperezr @steveharter | Consultants: @GrabYourPitchforks |
+| area-System.Reflection.Metadata | @ericstj | @buyaa-n @joperezr @steveharter | Consultants: @GrabYourPitchforks @tmat |
+| area-System.Resources | @ericstj | @buyaa-n @joperezr @steveharter | Consultants: @tarekgh |
+| area-System.Runtime | @jeffhandley | @michaelgsharp @tannergooding | Included:- System.Runtime.Serialization.Formatters
- System.Runtime.InteropServices.RuntimeInfo
- System.Array
Excluded:- Path -> System.IO
- StopWatch -> System.Diagnostics
- Uri -> System.Net
- WebUtility -> System.Net
|
| area-System.Runtime.Caching | @HongGit | @StephenMolloy @HongGit | |
-| area-System.Runtime.CompilerServices | @jeffhandley | @buyaa-n @joperezr | Consultants: @steveharter |
+| area-System.Runtime.CompilerServices | @ericstj | @buyaa-n @joperezr @steveharter | |
| area-System.Runtime.InteropServices | @jeffschwMSFT | @AaronRobinsonMSFT @jkoritzinsky | Excluded:- System.Runtime.InteropServices.RuntimeInfo
|
| area-System.Runtime.InteropServices.JavaScript | @lewing | @kjpou1 | |
-| area-System.Runtime.Intrinsics | @jeffhandley | @tannergooding @echesakovMSFT | |
+| area-System.Runtime.Intrinsics | @jeffhandley | @michaelgsharp @tannergooding | Consultants: @echesakovMSFT @kunalspathak |
| area-System.Security | @jeffhandley | @bartonjs @GrabYourPitchforks | |
| area-System.ServiceModel | @HongGit | @HongGit @mconnew | Repo: https://github.com/dotnet/WCF
Packages:- System.ServiceModel.Primitives
- System.ServiceModel.Http
- System.ServiceModel.NetTcp
- System.ServiceModel.Duplex
- System.ServiceModel.Security
|
| area-System.ServiceModel.Syndication | @HongGit | @StephenMolloy @HongGit | |
-| area-System.ServiceProcess | @ericstj | @Anipik @ViktorHofer | |
+| area-System.ServiceProcess | @ericstj | @safern | |
| area-System.Speech | @danmoseley | @danmoseley | |
-| area-System.Text.Encoding | @ericstj | @eiriktsarpalis @layomia @krwq | Consultants: @GrabYourPitchforks |
-| area-System.Text.Encodings.Web | @ericstj | @eiriktsarpalis @layomia @krwq | Consultants: @GrabYourPitchforks |
-| area-System.Text.Json | @ericstj | @eiriktsarpalis @layomia @krwq @steveharter | |
-| area-System.Text.RegularExpressions | @jeffhandley | @tannergooding | Consultants: @eerhardt @stephentoub |
+| area-System.Text.Encoding | @jeffhandley | @bartonjs @GrabYourPitchforks | Consultants: @tarekgh |
+| area-System.Text.Encodings.Web | @jeffhandley | @bartonjs @GrabYourPitchforks | |
+| area-System.Text.Json | @jeffhandley | @eiriktsarpalis @krwq @layomia | Consultants: @steveharter |
+| area-System.Text.RegularExpressions | @ericstj | @buyaa-n @joperezr @steveharter | Consultants: @stephentoub |
| area-System.Threading | @mangod9 | @kouvel | |
-| area-System.Threading.Channels | @jeffhandley | @adamsitnik @carlossanlop @jozkee | Consultants: @stephentoub |
-| area-System.Threading.Tasks | @jeffhandley | @adamsitnik @carlossanlop @jozkee | Consultants: @stephentoub |
+| area-System.Threading.Channels | @ericstj | @buyaa-n @joperezr @steveharter | Consultants: @stephentoub |
+| area-System.Threading.Tasks | @ericstj | @buyaa-n @joperezr @steveharter | Consultants: @stephentoub |
| area-System.Transactions | @HongGit | @HongGit | |
-| area-System.Xml | @ericstj | @eiriktsarpalis @layomia @krwq | |
+| area-System.Xml | @jeffhandley | @eiriktsarpalis @krwq @layomia | |
| area-Threading-mono | @SamMonoRT | @lambdageek | |
| area-TieredCompilation-coreclr | @mangod9 | @kouvel | |
| area-Tracing-coreclr | @tommcdon | @sywhang @josalem | |
diff --git a/docs/design/coreclr/botr/vectors-and-intrinsics.md b/docs/design/coreclr/botr/vectors-and-intrinsics.md
index 2688db9ca376b2..3c18eecb0a6446 100644
--- a/docs/design/coreclr/botr/vectors-and-intrinsics.md
+++ b/docs/design/coreclr/botr/vectors-and-intrinsics.md
@@ -16,7 +16,7 @@ Most hardware intrinsics support is tied to the use of various Vector apis. Ther
- The fixed length float vectors. `Vector2`, `Vector3`, and `Vector4`. These vector types represent a struct of floats of various lengths. For type layout, ABI and, interop purposes they are represented in exactly the same way as a structure with an appropriate number of floats in it. Operations on these vector types are supported on all architectures and platforms, although some architectures may optimize various operations.
- The variable length `Vector`. This represents vector data of runtime-determined length. In any given process the length of a `Vector` is the same in all methods, but this length may differ between various machines or environment variable settings read at startup of the process. The `T` type variable may be the following types (`System.Byte`, `System.SByte`, `System.Int16`, `System.UInt16`, `System.Int32`, `System.UInt32`, `System.Int64`, `System.UInt64`, `System.Single`, and `System.Double`), and allows use of integer or double data within a vector. The length and alignment of `Vector` is unknown to the developer at compile time (although discoverable at runtime by using the `Vector.Count` api), and `Vector` may not exist in any interop signature. Operations on these vector types are supported on all architectures and platforms, although some architectures may optimize various operations if the `Vector.IsHardwareAccelerated` api returns true.
- `Vector64`, `Vector128`, and `Vector256` represent fixed-sized vectors that closely resemble the fixed- sized vectors available in C++. These structures can be used in any code that runs, but very few features are supported directly on these types other than creation. They are used primarily in the processor specific hardware intrinsics apis.
-- Processor specific hardware intrinsics apis such as `System.Runtime.Intrinsics.X86.Ssse3`. These apis map directly to individual instructions or short instruction sequences that are specific to a particular hardware instruction. These apis are only useable on hardware that supports the particular instruction. See https://github.com/dotnet/designs/blob/master/accepted/2018/platform-intrinsics.md for the design of these.
+- Processor specific hardware intrinsics apis such as `System.Runtime.Intrinsics.X86.Ssse3`. These apis map directly to individual instructions or short instruction sequences that are specific to a particular hardware instruction. These apis are only usable on hardware that supports the particular instruction. See https://github.com/dotnet/designs/blob/master/accepted/2018/platform-intrinsics.md for the design of these.
# How to use intrinsics apis
@@ -185,7 +185,7 @@ Since System.Private.CoreLib.dll is known to be code reviewed with the code revi
The JIT receives flags which instruct it on what instruction sets are valid to use, and has access to a new jit interface api `notifyInstructionSetUsage(isa, bool supportBehaviorRequired)`.
-The notifyInstructionSetUsage api is used to notify the AOT compiler infrastructure that the code may only execute if the runtime environment of the code is exactly the same as the boolean parameter indicates it should be. For instance, if `notifyInstructionSetUsage(Avx, false)` is used, then the code generated must not be used if the `Avx` instruction set is useable. Similarly `notifyInstructionSetUsage(Avx, true)` will indicate that the code may only be used if the `Avx` instruction set is available.
+The notifyInstructionSetUsage api is used to notify the AOT compiler infrastructure that the code may only execute if the runtime environment of the code is exactly the same as the boolean parameter indicates it should be. For instance, if `notifyInstructionSetUsage(Avx, false)` is used, then the code generated must not be used if the `Avx` instruction set is usable. Similarly `notifyInstructionSetUsage(Avx, true)` will indicate that the code may only be used if the `Avx` instruction set is available.
While the above api exists, it is not expected that general purpose code within the JIT will use it. In general jitted code is expected to use a number of different apis to understand the available hardware instruction support available.
diff --git a/docs/design/features/arm64-intrinsics.md b/docs/design/features/arm64-intrinsics.md
index e814833ad126fc..5b602f24cf7102 100644
--- a/docs/design/features/arm64-intrinsics.md
+++ b/docs/design/features/arm64-intrinsics.md
@@ -173,7 +173,7 @@ It is also worth noting `System.Runtime.Intrinsics.X86` naming conventions will
operations which take vector argument(s), but contain an implicit cast(s) to the base type and therefore operate only
on the first item of the argument vector(s).
-### Intinsic Method Argument and Return Types
+### Intrinsic Method Argument and Return Types
Intrinsic methods will typically use a standard set of argument and return types:
diff --git a/docs/design/libraries/DllImportGenerator/Compatibility.md b/docs/design/libraries/DllImportGenerator/Compatibility.md
index 5f5997a36edaaf..9a22b6f6ed8795 100644
--- a/docs/design/libraries/DllImportGenerator/Compatibility.md
+++ b/docs/design/libraries/DllImportGenerator/Compatibility.md
@@ -33,7 +33,7 @@ These are all part of `NetCoreApp` and will be referenced by default unless [imp
Marshalling of `char` will not be supported when configured with any of the following:
- [`CharSet.Ansi`, `CharSet.Auto`, or `CharSet.None`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.charset) will not be supported.
- - [`UnmangedType.U1` or `UnmangedType.I1`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.unmanagedtype)
+ - [`UnmanagedType.U1` or `UnmanagedType.I1`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.unmanagedtype)
- No explicit marshalling information - either [`DllImportAttribute.CharSet`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.charset) or [`MarshalAsAttribute`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute)
For `CharSet.Ansi` and `CharSet.None`, the built-in system used the [system default Windows ANSI code page](https://docs.microsoft.com/windows/win32/api/stringapiset/nf-stringapiset-widechartomultibyte) when on Windows and took the first byte of the UTF-8 encoding on non-Windows platforms. The above reasoning also applies to marshalling of a `char` as `UnmanagedType.U1` and `UnmanagedType.I1`. All approaches are fundamentally flawed and therefore not supported. If a single-byte character is expected to be marshalled it is left to the caller to convert a .NET `char` into a single `byte` prior to calling the native function.
@@ -44,7 +44,7 @@ For `CharSet.Auto`, the built-in system relied upon detection at runtime of the
Marshalling of `string` will not be supported when configured with any of the following:
- [`CharSet.None`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.charset)
- - [`UnmangedType.VBByRefStr`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.unmanagedtype)
+ - [`UnmanagedType.VBByRefStr`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.unmanagedtype)
- No explicit marshalling information - either [`DllImportAttribute.CharSet`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.dllimportattribute.charset) or [`MarshalAsAttribute`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute)
When converting from native to managed, the built-in system would throw a [`MarshalDirectiveException`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshaldirectiveexception) if the string's length is over 0x7ffffff0. The generated marshalling code will no longer perform this check.
@@ -63,7 +63,7 @@ Using a custom marshaller (i.e. [`ICustomMarshaler`](https://docs.microsoft.com/
### Array marshalling
-Marshalling of arrays will not be supported when using [`UnmangedType.SafeArray`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.unmanagedtype). This implies that the following `MarshalAsAttribute` fields are unsupported: [`SafeArraySubType`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.safearraysubtype) and [`SafeArrayUserDefinedSubType`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.safearrayuserdefinedsubtype)
+Marshalling of arrays will not be supported when using [`UnmanagedType.SafeArray`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.unmanagedtype). This implies that the following `MarshalAsAttribute` fields are unsupported: [`SafeArraySubType`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.safearraysubtype) and [`SafeArrayUserDefinedSubType`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.safearrayuserdefinedsubtype)
Specifying array-specific marshalling members on the `MarshalAsAttribute` such as [`SizeConst`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.sizeconst), [`ArraySubType`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.arraysubtype), and [`SizeParamIndex`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshalasattribute.sizeparamindex) with non-array `UnmanagedType` types is unsupported.
@@ -71,6 +71,8 @@ Only single-dimensional arrays are supported for source generated marshalling.
In the source-generated marshalling, arrays will be allocated on the stack (instead of through [`AllocCoTaskMem`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshal.alloccotaskmem)) if they are passed by value or by read-only reference if they contain at most 256 bytes of data. The built-in system does not support this optimization for arrays.
+In the built-in system, marshalling a `char` array by value with `CharSet.Unicode` would default to also marshalling data out. In the source-generated marshalling, the `char` array must be marked with the `[Out]` attribute for data to be marshalled out by value.
+
### `in` keyword
For some types - blittable or Unicode `char` - passed by read-only reference via the [`in` keyword](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/in-parameter-modifier), the built-in system simply pins the parameter. The generated marshalling code does the same, such that there is no behavioural difference. A consequence of this behaviour is that any modifications made by the invoked function will be reflected in the caller. It is left to the user to avoid the situation in which `in` is used for a parameter that will actually be modified by the invoked function.
@@ -94,6 +96,12 @@ Unlike the built-in system, the source generator does not support marshalling fo
- [`HandleRef`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.handleref)
- [`StringBuilder`](https://docs.microsoft.com/dotnet/api/system.text.stringbuilder)
+The source generator also does not support marshalling objects using the following [`UnmanagedType`](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.unmanagedtype) values:
+- `UnmanagedType.Interface`
+- `UnmanagedType.IDispatch`
+- `UnmanagedType.IInspectable`
+- `UnmanagedType.IUnknown`
+
## Version 0
This version is the built-in IL Stub generation system that is triggered whenever a method marked with `DllImportAttribute` is invoked.
diff --git a/docs/design/libraries/DllImportGenerator/StructMarshalling.md b/docs/design/libraries/DllImportGenerator/StructMarshalling.md
index 2852880103c6c6..ab332616b74132 100644
--- a/docs/design/libraries/DllImportGenerator/StructMarshalling.md
+++ b/docs/design/libraries/DllImportGenerator/StructMarshalling.md
@@ -54,14 +54,16 @@ partial struct TNative
}
```
-The analyzer will report an error if neither the construtor nor the ToManaged method is defined. When one of those two methods is missing, the direction of marshalling (managed to native/native to managed) that relies on the missing method is considered unsupported for the corresponding managed type. The FreeNative method is only required when there are resources that need to be released.
+The analyzer will report an error if neither the constructor nor the `ToManaged` method is defined. When one of those two methods is missing, the direction of marshalling (managed to native/native to managed) that relies on the missing method is considered unsupported for the corresponding managed type. The `FreeNative` method is only required when there are resources that need to be released.
> :question: Does this API surface and shape work for all marshalling scenarios we plan on supporting? It may have issues with the current "layout class" by-value `[Out]` parameter marshalling where the runtime updates a `class` typed object in place. We already recommend against using classes for interop for performance reasons and a struct value passed via `ref` or `out` with the same members would cover this scenario.
If the native type `TNative` also has a public `Value` property, then the value of the `Value` property will be passed to native code instead of the `TNative` value itself. As a result, the type `TNative` will be allowed to be non-blittable and the type of the `Value` property will be required to be blittable. If the `Value` property is settable, then when marshalling in the native-to-managed direction, a default value of `TNative` will have its `Value` property set to the native value. If `Value` does not have a setter, then marshalling from native to managed is not supported.
-A `ref` or `ref readonly` typed `Value` property is unsupported. If a ref-return is required, the type author can supply a `GetPinnableReference` method on the native type. If a `GetPinnableReference` method is supplied, then the `Value` property must have a pointer-sized primitive type.
+If a `Value` property is provided, the developer may also provide a ref-returning or readonly-ref-returning `GetPinnableReference` method. The `GetPinnableReference` method will be called before the `Value` property getter is called. The ref returned by `GetPinnableReference` will be pinned with a `fixed` statement, but the pinned value will not be used (it acts exclusively as a side-effect).
+
+A `ref` or `ref readonly` typed `Value` property is unsupported. If a ref-return is required, the type author can supply a `GetPinnableReference` method on the native type to pin the desired `ref` to return and then use `System.Runtime.CompilerServices.Unsafe.AsPointer` to get a pointer from the `ref` that will have already been pinned by the time the `Value` getter is called.
```csharp
[NativeMarshalling(typeof(TMarshaler))]
@@ -72,14 +74,14 @@ public struct TManaged
public struct TMarshaler
{
- public TNative(TManaged managed) {}
+ public TMarshaler(TManaged managed) {}
public TManaged ToManaged() {}
public void FreeNative() {}
public ref TNative GetPinnableReference() {}
- public TNative Value { get; set; }
+ public TNative* Value { get; set; }
}
```
@@ -88,7 +90,7 @@ public struct TMarshaler
#### Pinning
-Since C# 7.3 added a feature to enable custom pinning logic for user types, we should also add support for custom pinning logic. If the user provides a `GetPinnableReference` method that matches the requirements to be used in a `fixed` statement and the pointed-to type is blittable, then we will support using pinning to marshal the managed value when possible. The analyzer should issue a warning when the pointed-to type would not match the final native type, accounting for the `Value` property on the native type. Since `MarshalUsingAttribute` is applied at usage time instead of at type authoring time, we will not enable the pinning feature since the implementation of `GetPinnableReference` unless the pointed-to return type matches the native type.
+Since C# 7.3 added a feature to enable custom pinning logic for user types, we should also add support for custom pinning logic. If the user provides a `GetPinnableReference` method on the managed type that matches the requirements to be used in a `fixed` statement and the pointed-to type is blittable, then we will support using pinning to marshal the managed value when possible. The analyzer should issue a warning when the pointed-to type would not match the final native type, accounting for the `Value` property on the native type. Since `MarshalUsingAttribute` is applied at usage time instead of at type authoring time, we will not enable the pinning feature since the implementation of `GetPinnableReference` is likely designed to match the default marshalling rules provided by the type author, not the rules provided by the marshaller provided by the `MarshalUsingAttribute`.
#### Caller-allocated memory
@@ -100,14 +102,14 @@ partial struct TNative
public TNative(TManaged managed, Span buffer) {}
public const int BufferSize = /* */;
-
+
public const bool RequiresStackBuffer = /* */;
}
```
When these members are present, the source generator will call the two-parameter constructor with a possibly stack-allocated buffer of `BufferSize` bytes when a stack-allocated buffer is usable. If a stack-allocated buffer is a requirement, the `RequiresStackBuffer` field should be set to `true` and the `buffer` will be guaranteed to be allocated on the stack. Setting the `RequiresStackBuffer` field to `false` is the same as omitting the field definition. Since a dynamically allocated buffer is not usable in all scenarios, for example Reverse P/Invoke and struct marshalling, a one-parameter constructor must also be provided for usage in those scenarios. This may also be provided by providing a two-parameter constructor with a default value for the second parameter.
-Type authors can pass down the `buffer` pointer to native code by defining a `GetPinnableReference()` method on the native type that returns a reference to the first element of the span. When the `RequiresStackBuffer` field is set to `true`, the type author is free to use APIs that would be dangerous in non-stack-allocated scenarios such as `MemoryMarshal.GetReference()` and `Unsafe.AsPointer()`.
+Type authors can pass down the `buffer` pointer to native code by defining a `Value` property that returns a pointer to the first element, generally through code using `MemoryMarshal.GetReference()` and `Unsafe.AsPointer`. If `RequiresStackBuffer` is not provided or set to `false`, the `buffer` span must be pinned to be used safely. The `buffer` span can be pinned by defining a `GetPinnableReference()` method on the native type that returns a reference to the first element of the span.
### Usage
@@ -115,7 +117,7 @@ There are 2 usage mechanisms of these attributes.
#### Usage 1, Source-generated interop
-The user can apply the `GeneratedMarshallingAttribute` to their structure `S`. The source generator will determine if the type is blittable. If it is blittable, the source generator will generate a partial definition and apply the `BlittableTypeAttribute` to the struct type `S`. Otherwise, it will generate a blittable representation of the struct with the aformentioned requried shape and apply the `NativeMarshallingAttribute` and point it to the blittable representation. The blittable representation can either be generated as a separate top-level type or as a nested type on `S`.
+The user can apply the `GeneratedMarshallingAttribute` to their structure `S`. The source generator will determine if the type is blittable. If it is blittable, the source generator will generate a partial definition and apply the `BlittableTypeAttribute` to the struct type `S`. Otherwise, it will generate a blittable representation of the struct with the aformentioned required shape and apply the `NativeMarshallingAttribute` and point it to the blittable representation. The blittable representation can either be generated as a separate top-level type or as a nested type on `S`.
#### Usage 2, Manual interop
diff --git a/docs/design/mono/debugger.md b/docs/design/mono/debugger.md
new file mode 100644
index 00000000000000..3a3a2936b79eb4
--- /dev/null
+++ b/docs/design/mono/debugger.md
@@ -0,0 +1,21 @@
+# WebAssembly Debugger
+
+## Overview
+
+The details of launching a Debugger session for a Blazor WebAssembly application is described [here](https://docs.microsoft.com/en-us/aspnet/core/blazor/debug?view=aspnetcore-6.0&tabs=visual-studio).
+
+## Debugger Attributes
+Web Assembly Debugger supports usage of following attributes:
+- __System.Diagnostics.DebuggerHidden__
+
+ Decorating a method - results:
+ - Visual Studio Breakpoints: results in disabling all existing breakpoints in the method and no possibility to set new,enabled ones.
+ - Stepping In/Over: results in stepping over the line with method call.
+ - Call stack: method does not appear on the call stack, no access to method local variables is provided.
+
+ Decorating a method with a Debugger.Break() call inside:
+ - Running in the Debug mode: results in pausing the program on the line with the method call.
+ - Stepping In/Over: results in an additional stepping need to proceed to the next line.
+- __System.Diagnostics.DebuggerDisplay__
+- __System.Diagnostics.DebuggerTypeProxy__
+- ...
\ No newline at end of file
diff --git a/docs/design/specs/Ecma-335-Augments.md b/docs/design/specs/Ecma-335-Augments.md
index 1ebcbc377c9fa6..1c52fedd4d84ce 100644
--- a/docs/design/specs/Ecma-335-Augments.md
+++ b/docs/design/specs/Ecma-335-Augments.md
@@ -7,6 +7,7 @@ This is a list of additions and edits to be made in ECMA-335 specifications. It
- [Signatures](#signatures)
- [Heap sizes](#heap-sizes)
- [Metadata merging](#metadata-merging)
+- [Metadata logical format](#metadata-logical-format)
- [Module Initializer](#module-initializer)
- [Default Interface Methods](#default-interface-methods)
- [Static Interface Methods](#static-interface-methods)
@@ -362,6 +363,14 @@ This text should be deleted, and the _metadata merging_ entry should be removed
> * ~~If there are duplicates and two or more have an accessibility other than
> **compilercontrolled**, an error has occurred.~~
+## Metadata logical format
+
+The requirement to sort InterfaceImpl table using the Interface column as a secondary key in § II.22 _Metadata logical format: tables_ is a spec bug. The interface declaration order affects resolution and a requirement to sort it would make it impossible to emit certain sequences of interfaces (e.g. not possible to have an interface list I1, I2, while also having interface list I2, I1 elsewhere in the module).
+
+The text should be deleted:
+
+> Furthermore, ~~the InterfaceImpl table is sorted using the Interface column as a secondary key, and~~ the GenericParam table is sorted using the Number column as a secondary key.
+
## Module Initializer
All modules may have a module initializer. A module initializer is defined as the type initializer (§ II.10.5.3) of the `` type (§ II.10.8).
diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md
index 6516b71d45553a..1d6dd75bb0085b 100644
--- a/docs/project/list-of-diagnostics.md
+++ b/docs/project/list-of-diagnostics.md
@@ -91,6 +91,7 @@ The PR that reveals the implementation of the `true`
* Add NuGet references by updating the following [test project](https://github.com/dotnet/runtime/blob/main/src/tests/Common/test_dependencies/test_dependencies.csproj).
-* Get access to System.Private.CoreLib types and methods that are not exposed via public surface by adding the following to the csproj:
- * `true`
* Any System.Private.CoreLib types and methods used by tests must be available for building on all platforms.
This means there must be enough implementation for the C# compiler to find the referenced types and methods. Unsupported target platforms
should simply `throw new PlatformNotSupportedException()` in its dummy method implementations.
diff --git a/docs/workflow/testing/libraries/testing-android.md b/docs/workflow/testing/libraries/testing-android.md
index 23b6e07d924140..e3c1c4e79c38d0 100644
--- a/docs/workflow/testing/libraries/testing-android.md
+++ b/docs/workflow/testing/libraries/testing-android.md
@@ -1,18 +1,23 @@
# Testing Libraries on Android
+## Prerequisites
+
The following dependencies should be installed in order to be able to run tests:
+- OpenJDK
- Android NDK
- Android SDK
-- OpenJDK
-- OpenSSL
+
+To manage the dependencies, you can install them via terminal or using Android Studio.
+
+### Using a terminal
OpenJDK can be installed on Linux (Ubuntu) using `apt-get`:
```bash
sudo apt-get install openjdk-8 zip unzip
```
-Android SDK, NDK and OpenSSL can be automatically installed via the following script:
+Android SDK and NDK can be automatically installed via the following script:
```bash
#!/usr/bin/env bash
set -e
@@ -21,7 +26,6 @@ NDK_VER=r21b
SDK_VER=6200805_latest
SDK_API_LEVEL=29
SDK_BUILD_TOOLS=29.0.3
-OPENSSL_VER=1.1.1g-alpha-1
if [[ "$OSTYPE" == "darwin"* ]]; then
HOST_OS=darwin
@@ -47,8 +51,21 @@ yes | ${ANDROID_SDK_ROOT}/cmdline-tools/tools/bin/sdkmanager --sdk_root=${ANDROI
${ANDROID_SDK_ROOT}/cmdline-tools/tools/bin/sdkmanager --sdk_root=${ANDROID_SDK_ROOT} "platform-tools" "platforms;android-${SDK_API_LEVEL}" "build-tools;${SDK_BUILD_TOOLS}"
```
+### Using Android Studio
+
+Android Studio offers a convenient UI:
+- to install all the dependencies;
+- to manage android virtual devices;
+- to make easy use of adb logs.
+
## Building Libs and Tests for Android
+Before running a build you might want to set the Android SDK and NDK environment variables:
+```
+export ANDROID_SDK_ROOT=
+export ANDROID_NDK_ROOT=
+```
+
Now we're ready to build everything for Android:
```
./build.sh mono+libs -os Android -arch x64
@@ -66,8 +83,35 @@ The following shows how to run tests for a specific library
./dotnet.sh build /t:Test src/libraries/System.Numerics.Vectors/tests /p:TargetOS=Android /p:TargetArchitecture=x64
```
+### Running the functional tests
+
+There are [functional tests](https://github.com/dotnet/runtime/tree/main/src/tests/FunctionalTests/) which aim to test some specific features/configurations/modes on a target mobile platform.
+
+A functional test can be run the same way as any library test suite, e.g.:
+```
+./dotnet.sh build /t:Test -c Release /p:TargetOS=Android /p:TargetArchitecture=x64 src/tests/FunctionalTests/Android/Device_Emulator/PInvoke/Android.Device_Emulator.PInvoke.Test.csproj
+```
+
+Currently functional tests are expected to return `42` as a success code so please be careful when adding a new one.
+
+### Testing various configurations
+
+It's possible to test various configurations by setting a combination of additional MSBuild properties such as `RunAOTCompilation`,`MonoForceInterpreter`, and some more.
+
+1. AOT
+
+To build for AOT only mode, add `/p:RunAOTCompilation=true /p:MonoForceInterpreter=false` to a build command.
+
+2. AOT-LLVM
+
+To build for AOT-LLVM mode, add `/p:RunAOTCompilation=true /p:MonoForceInterpreter=false /p:MonoEnableLLVM=true` to a build command.
+
+3. Interpreter
+
+To build for Interpreter mode, add `/p:RunAOTCompilation=false /p:MonoForceInterpreter=true` to a build command.
+
### Test App Design
-Android app is basically a [Java Instrumentation](https://github.com/dotnet/runtime/blob/main/src/mono/msbuild/AndroidAppBuilder/Templates/MonoRunner.java) and a simple Activity that inits the Mono Runtime via JNI. This Mono Runtime starts a simple xunit test
+Android app is basically a [Java Instrumentation](https://github.com/dotnet/runtime/blob/main/src/tasks/AndroidAppBuilder/Templates/MonoRunner.java) and a simple Activity that inits the Mono Runtime via JNI. This Mono Runtime starts a simple xunit test
runner called XHarness.TestRunner (see https://github.com/dotnet/xharness) which runs tests for all `*.Tests.dll` libs in the bundle. There is also XHarness.CLI tool with ADB embedded to deploy `*.apk` to a target (device or emulator) and obtain logs once tests are completed.
### Obtaining the logs
@@ -75,7 +119,7 @@ XHarness for Android doesn't talk much and only saves test results to a file. Ho
```
adb logcat -s "DOTNET"
```
-Or simply open `logcat` window in Android Studio or Visual Stuido.
+Or simply open `logcat` window in Android Studio or Visual Studio.
### AVD Manager
If Android Studio is installed, [AVD Manager](https://developer.android.com/studio/run/managing-avds) can be used from the IDE to create and start Android virtual devices. Otherwise, the Android SDK provides the [`avdmanager` command line tool](https://developer.android.com/studio/command-line/avdmanager).
diff --git a/docs/workflow/testing/libraries/testing-apple.md b/docs/workflow/testing/libraries/testing-apple.md
index 99adf6a87176b8..c303fc737362d2 100644
--- a/docs/workflow/testing/libraries/testing-apple.md
+++ b/docs/workflow/testing/libraries/testing-apple.md
@@ -1,34 +1,117 @@
-# Testing Libraries on iOS and tvOS
+# Testing Libraries on iOS, tvOS, and MacCatalyst
-In order to build libraries and tests for iOS or tvOS you need recent version of XCode installed (e.g. 11.3 or higher).
+## Prerequisites
-Build Libraries for iOS Simulator:
+- XCode 11.3 or higher
+- a certificate and provisioning profile if using a device
+- a simulator with a proper device type and OS version.
+Go `XCode > Window > Devices and Simulators` to revise the list of the available simulators and then `"+" button on bottom left > OS Version dropdown selection > Download more simulator runtimes` in case you need to download more simulators.
+
+## Building Libs and Tests
+
+You can build and run the library tests:
+- on a simulator;
+- on a device.
+
+Run the following command in a terminal:
+```
+./build.sh mono+libs -os -arch
+```
+where `` is one of the following:
+- iOSSimulator
+- tvOSSimulator
+- MacCatalyst
+- iOS
+- tvOS
+
+and `` is one of the following:
+- x64
+- arm64 (for device)
+
+e.g., to build for an iOS simulator, run:
```
./build.sh mono+libs -os iOSSimulator -arch x64
```
+
Run tests one by one for each test suite on a simulator:
```
./build.sh libs.tests -os iOSSimulator -arch x64 -test
```
+
+### Building for a device
+
In order to run the tests on a device:
-- Set the os to `iOS` instead of `iOSSimulator`
-- Specify `DevTeamProvisioning` (see [developer.apple.com/account/#/membership](https://developer.apple.com/account/#/membership), scroll down to `Team ID`):
+- Set the `-os` parameter to a device-related value (see above)
+- Specify `DevTeamProvisioning` (see [developer.apple.com/account/#/membership](https://developer.apple.com/account/#/membership), scroll down to `Team ID`).
+
+For example:
```
./build.sh libs.tests -os iOS -arch x64 -test /p:DevTeamProvisioning=H1A2B3C4D5
```
-[AppleAppBuilder](https://github.com/dotnet/runtime/blob/main/src/mono/msbuild/AppleAppBuilder/AppleAppBuilder.cs) generates temp Xcode projects you can manually open and resolve provisioning issues there using native UI and deploy to your devices.
+[AppleAppBuilder](https://github.com/dotnet/runtime/blob/main/src/tasks/AppleAppBuilder/AppleAppBuilder.cs) generates temp Xcode projects you can manually open and resolve provisioning issues there using native UI and deploy to your devices.
### Running individual test suites
+
- The following shows how to run tests for a specific library:
```
./dotnet.sh build src/libraries/System.Numerics.Vectors/tests /t:Test /p:TargetOS=iOS /p:TargetArchitecture=x64
```
+Also you can run the built test app through Xcode by opening the corresponding `.xcodeproj` and setting up the right scheme, app, and even signing if using a local device.
+
+There's also an option to run a `.app` through `xcrun`, which is simulator specific. Consider the following shell script:
+
+```
+ xcrun simctl shutdown
+ xcrun simctl boot
+ open -a Simulator
+ xcrun simctl install
+ xcrun simctl launch --console booted
+```
+
+where
+`` is a name of the simulator to start, e.g. `"iPhone 11"`,
+`` is a path to the iOS test app bundle,
+`` is a name of the iOS test app
+
+### Running the functional tests
+
+There are [functional tests](https://github.com/dotnet/runtime/tree/main/src/tests/FunctionalTests/) which aim to test some specific features/configurations/modes on a target mobile platform.
+
+A functional test can be run the same way as any library test suite, e.g.:
+```
+./dotnet.sh build /t:Test -c Release /p:TargetOS=iOSSimulator /p:TargetArchitecture=x64 src/tests/FunctionalTests/iOS/Simulator/PInvoke/iOS.Simulator.PInvoke.Test.csproj
+```
+
+Currently functional tests are expected to return `42` as a success code so please be careful when adding a new one.
+
+### Viewing logs
+- see the logs generated by the XHarness tool
+- use the `Console` app on macOS:
+`Command + Space`, type in `Console`, search the appropriate process (System.Buffers.Tests for example).
+
+### Testing various configurations
+
+It's possible to test various configurations by setting a combination of additional MSBuild properties such as `RunAOTCompilation`,`MonoEnableInterpreter`, and some more.
+
+1. Interpreter Only
+
+This configuration is necessary for hot reload scenarios.
+
+To enable the interpreter, add `/p:RunAOTCompilation=true /p:MonoEnableInterpreter=true` to a build command.
+
+2. AOT only
+
+To build for AOT only mode, add `/p:RunAOTCompilation=true /p:MonoEnableInterpreter=false` to a build command.
+
+3. AOT-LLVM
+
+To build for AOT-LLVM mode, add `/p:RunAOTCompilation=true /p:MonoEnableInterpreter=false /p:MonoEnableLLVM=true` to a build command.
+
### Test App Design
-iOS/tvOS `*.app` (or `*.ipa`) is basically a simple [ObjC app](https://github.com/dotnet/runtime/blob/main/src/mono/msbuild/AppleAppBuilder/Templates/main-console.m) that inits the Mono Runtime. This Mono Runtime starts a simple xunit test
+iOS/tvOS `*.app` (or `*.ipa`) is basically a simple [ObjC app](https://github.com/dotnet/runtime/blob/main/src/tasks/AppleAppBuilder/Templates/main-console.m) that inits the Mono Runtime. This Mono Runtime starts a simple xunit test
runner called XHarness.TestRunner (see https://github.com/dotnet/xharness) which runs tests for all `*.Tests.dll` libs in the bundle. There is also XHarness.CLI tool to deploy `*.app` and `*.ipa` to a target (device or simulator) and listens for logs via network sockets.
### Existing Limitations
-- Most of the test suites crash on devices due to #35674
- Simulator uses JIT mode only at the moment (to be extended with FullAOT and Interpreter)
- Interpreter is not enabled yet.
diff --git a/docs/workflow/testing/mono/testing.md b/docs/workflow/testing/mono/testing.md
index fc5086f52e8f36..14d141e529a3ca 100644
--- a/docs/workflow/testing/mono/testing.md
+++ b/docs/workflow/testing/mono/testing.md
@@ -93,7 +93,20 @@ For example, the following command is for running System.Runtime tests:
make run-tests-corefx-System.Runtime
```
### Mobile targets and WebAssembly
-Build and run library tests against Webassembly, Android or iOS. See instructions located in [Library testing document folder](../libraries/)
+Build and run library tests against WebAssembly, Android or iOS. See instructions located in [Library testing document folder](../libraries/)
+
+## Running the functional tests
+
+There are the [functional tests](https://github.com/dotnet/runtime/tree/main/src/tests/FunctionalTests/) which aim to test some specific features/configurations/modes on Android, iOS-like platforms (iOS/tvOS + simulators, MacCatalyst), and WebAssembly.
+
+A functional test can be run the same way as any library test suite, e.g.:
+```
+./dotnet.sh build /t:Test -c Release /p:TargetOS=Android /p:TargetArchitecture=x64 src/tests/FunctionalTests/Android/Device_Emulator/PInvoke/Android.Device_Emulator.PInvoke.Test.csproj
+```
+
+Currently the functional tests are expected to return `42` as a success code so please be careful when adding a new one.
+
+For more details, see instructions located in [Library testing document folder](../libraries/).
# Running the Mono samples
There are a few convenient samples located in `$(REPO_ROOT)/src/mono/sample`, which could help you test your program easily with different flavors of Mono or do a sanity check on the build. The samples are set up to work with a specific configuration; please refer to the relevant Makefile for specifics. If you would like to work with a different configuration, you can edit the Makefile.
diff --git a/eng/CodeAnalysis.src.globalconfig b/eng/CodeAnalysis.src.globalconfig
index 1f1ba7216dc06b..400c8c432c21bf 100644
--- a/eng/CodeAnalysis.src.globalconfig
+++ b/eng/CodeAnalysis.src.globalconfig
@@ -386,6 +386,12 @@ dotnet_diagnostic.CA1847.severity = warning
# CA1848: Use the LoggerMessage delegates
dotnet_diagnostic.CA1848.severity = none
+# CA1849: Call async methods when in an async method
+dotnet_diagnostic.CA1849.severity = suggestion
+
+# CA1850: Prefer static 'HashData' method over 'ComputeHash'
+dotnet_diagnostic.CA1850.severity = warning
+
# CA2000: Dispose objects before losing scope
dotnet_diagnostic.CA2000.severity = none
diff --git a/eng/CodeAnalysis.test.globalconfig b/eng/CodeAnalysis.test.globalconfig
index 5f31263bf86570..572310d3b86d0a 100644
--- a/eng/CodeAnalysis.test.globalconfig
+++ b/eng/CodeAnalysis.test.globalconfig
@@ -384,6 +384,12 @@ dotnet_diagnostic.CA1847.severity = none
# CA1848: Use the LoggerMessage delegates
dotnet_diagnostic.CA1848.severity = none
+# CA1849: Call async methods when in an async method
+dotnet_diagnostic.CA1849.severity = none
+
+# CA1850: Prefer static 'HashData' method over 'ComputeHash'
+dotnet_diagnostic.CA1850.severity = none
+
# CA2000: Dispose objects before losing scope
dotnet_diagnostic.CA2000.severity = none
diff --git a/eng/SourceBuild.props b/eng/SourceBuild.props
index 340f5720780367..e191178c9068fc 100644
--- a/eng/SourceBuild.props
+++ b/eng/SourceBuild.props
@@ -23,14 +23,6 @@
minimal
-
-
-
-
-
-
-
-
diff --git a/eng/Subsets.props b/eng/Subsets.props
index 03dc4a5b59f437..65da9d8861bd68 100644
--- a/eng/Subsets.props
+++ b/eng/Subsets.props
@@ -280,7 +280,7 @@
-
+
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 004ddccb3d6195..05bd4440e3d153 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -1,16 +1,16 @@
-
+
https://github.com/dotnet/icu
- 5146c6e78ad134305a4b3e857379bdd134db7ac0
+ c454e00c63464ff6b599f23448bcc0a0c9fbac5f
https://github.com/dotnet/msquic
a7213b4676c1803bb251771291a525482d42e511
-
+
https://github.com/dotnet/emsdk
- efad634a7c316b67654b38690c56b3702363d6ae
+ 24412d4288fda1d3c9fed49a7961bb4c476c0ce6
https://github.com/dotnet/wcf
@@ -18,229 +18,229 @@
-
+
https://github.com/dotnet/arcade
- 0558f85d950fee2838bf02b9ba1f20d67f00b504
+ 427c05909067bb2e484116ae2239456bb45adb85
-
+
https://github.com/dotnet/arcade
- 0558f85d950fee2838bf02b9ba1f20d67f00b504
+ 427c05909067bb2e484116ae2239456bb45adb85
-
+
https://github.com/dotnet/arcade
- 0558f85d950fee2838bf02b9ba1f20d67f00b504
+ 427c05909067bb2e484116ae2239456bb45adb85
-
+
https://github.com/dotnet/arcade
- 0558f85d950fee2838bf02b9ba1f20d67f00b504
+ 427c05909067bb2e484116ae2239456bb45adb85
-
+
https://github.com/dotnet/arcade
- 0558f85d950fee2838bf02b9ba1f20d67f00b504
+ 427c05909067bb2e484116ae2239456bb45adb85
-
+
https://github.com/dotnet/arcade
- 0558f85d950fee2838bf02b9ba1f20d67f00b504
+ 427c05909067bb2e484116ae2239456bb45adb85
-
+
https://github.com/dotnet/arcade
- 0558f85d950fee2838bf02b9ba1f20d67f00b504
+ 427c05909067bb2e484116ae2239456bb45adb85
-
+
https://github.com/dotnet/arcade
- 0558f85d950fee2838bf02b9ba1f20d67f00b504
+ 427c05909067bb2e484116ae2239456bb45adb85
-
+
https://github.com/dotnet/arcade
- 0558f85d950fee2838bf02b9ba1f20d67f00b504
+ 427c05909067bb2e484116ae2239456bb45adb85
-
+
https://github.com/dotnet/arcade
- 0558f85d950fee2838bf02b9ba1f20d67f00b504
+ 427c05909067bb2e484116ae2239456bb45adb85
-
+
https://github.com/dotnet/arcade
- 0558f85d950fee2838bf02b9ba1f20d67f00b504
+ 427c05909067bb2e484116ae2239456bb45adb85
-
+
https://github.com/dotnet/arcade
- 0558f85d950fee2838bf02b9ba1f20d67f00b504
+ 427c05909067bb2e484116ae2239456bb45adb85
-
+
https://github.com/dotnet/arcade
- 0558f85d950fee2838bf02b9ba1f20d67f00b504
+ 427c05909067bb2e484116ae2239456bb45adb85
-
+
https://github.com/dotnet/arcade
- 0558f85d950fee2838bf02b9ba1f20d67f00b504
+ 427c05909067bb2e484116ae2239456bb45adb85
-
+
https://github.com/dotnet/arcade
- 0558f85d950fee2838bf02b9ba1f20d67f00b504
+ 427c05909067bb2e484116ae2239456bb45adb85
-
+
https://github.com/dotnet/arcade
- 0558f85d950fee2838bf02b9ba1f20d67f00b504
+ 427c05909067bb2e484116ae2239456bb45adb85
-
+
https://github.com/dotnet/arcade
- 0558f85d950fee2838bf02b9ba1f20d67f00b504
+ 427c05909067bb2e484116ae2239456bb45adb85
-
+
https://github.com/dotnet/arcade
- 0558f85d950fee2838bf02b9ba1f20d67f00b504
+ 427c05909067bb2e484116ae2239456bb45adb85
https://github.com/microsoft/vstest
140434f7109d357d0158ade9e5164a4861513965
-
+
https://github.com/dotnet/runtime-assets
- e444c9ce30e40a75994fe6b4fd5e87a7ef63355b
+ 8784c859561193c0a91b2b47108113a9a7c40038
-
+
https://github.com/dotnet/runtime-assets
- e444c9ce30e40a75994fe6b4fd5e87a7ef63355b
+ 8784c859561193c0a91b2b47108113a9a7c40038
-
+
https://github.com/dotnet/runtime-assets
- e444c9ce30e40a75994fe6b4fd5e87a7ef63355b
+ 8784c859561193c0a91b2b47108113a9a7c40038
-
+
https://github.com/dotnet/runtime-assets
- e444c9ce30e40a75994fe6b4fd5e87a7ef63355b
+ 8784c859561193c0a91b2b47108113a9a7c40038
-
+
https://github.com/dotnet/runtime-assets
- e444c9ce30e40a75994fe6b4fd5e87a7ef63355b
+ 8784c859561193c0a91b2b47108113a9a7c40038
-
+
https://github.com/dotnet/runtime-assets
- e444c9ce30e40a75994fe6b4fd5e87a7ef63355b
+ 8784c859561193c0a91b2b47108113a9a7c40038
-
+
https://github.com/dotnet/runtime-assets
- e444c9ce30e40a75994fe6b4fd5e87a7ef63355b
+ 8784c859561193c0a91b2b47108113a9a7c40038
-
+
https://github.com/dotnet/runtime-assets
- e444c9ce30e40a75994fe6b4fd5e87a7ef63355b
+ 8784c859561193c0a91b2b47108113a9a7c40038
-
+
https://github.com/dotnet/runtime-assets
- e444c9ce30e40a75994fe6b4fd5e87a7ef63355b
+ 8784c859561193c0a91b2b47108113a9a7c40038
-
+
https://github.com/dotnet/runtime-assets
- e444c9ce30e40a75994fe6b4fd5e87a7ef63355b
+ 8784c859561193c0a91b2b47108113a9a7c40038
-
+
https://github.com/dotnet/llvm-project
- b1e4b4a52fe087e726d6bb95c78d234352f9e847
+ 2e69189f031e16ffb36167d89a658e14cbdba099
-
+
https://github.com/dotnet/llvm-project
- b1e4b4a52fe087e726d6bb95c78d234352f9e847
+ 2e69189f031e16ffb36167d89a658e14cbdba099
-
+
https://github.com/dotnet/llvm-project
- b1e4b4a52fe087e726d6bb95c78d234352f9e847
+ 2e69189f031e16ffb36167d89a658e14cbdba099
-
+
https://github.com/dotnet/llvm-project
- b1e4b4a52fe087e726d6bb95c78d234352f9e847
+ 2e69189f031e16ffb36167d89a658e14cbdba099
-
+
https://github.com/dotnet/llvm-project
- b1e4b4a52fe087e726d6bb95c78d234352f9e847
+ 2e69189f031e16ffb36167d89a658e14cbdba099
-
+
https://github.com/dotnet/llvm-project
- b1e4b4a52fe087e726d6bb95c78d234352f9e847
+ 2e69189f031e16ffb36167d89a658e14cbdba099
-
+
https://github.com/dotnet/llvm-project
- b1e4b4a52fe087e726d6bb95c78d234352f9e847
+ 2e69189f031e16ffb36167d89a658e14cbdba099
-
+
https://github.com/dotnet/llvm-project
- b1e4b4a52fe087e726d6bb95c78d234352f9e847
+ 2e69189f031e16ffb36167d89a658e14cbdba099
-
+
https://github.com/dotnet/runtime
- 2a87ffeaedb6b534d6eaa000d5ba9b545f4aac1e
+ 9ede9a29128629ffde91b16efb51c9dd6ad7d0f7
-
+
https://github.com/dotnet/runtime
- 2a87ffeaedb6b534d6eaa000d5ba9b545f4aac1e
+ 9ede9a29128629ffde91b16efb51c9dd6ad7d0f7
-
+
https://github.com/dotnet/runtime
- 2a87ffeaedb6b534d6eaa000d5ba9b545f4aac1e
+ 9ede9a29128629ffde91b16efb51c9dd6ad7d0f7
-
+
https://github.com/dotnet/runtime
- 2a87ffeaedb6b534d6eaa000d5ba9b545f4aac1e
+ 9ede9a29128629ffde91b16efb51c9dd6ad7d0f7
-
+
https://github.com/dotnet/runtime
- 2a87ffeaedb6b534d6eaa000d5ba9b545f4aac1e
+ 9ede9a29128629ffde91b16efb51c9dd6ad7d0f7
-
+
https://github.com/dotnet/runtime
- 2a87ffeaedb6b534d6eaa000d5ba9b545f4aac1e
+ 9ede9a29128629ffde91b16efb51c9dd6ad7d0f7
-
+
https://github.com/dotnet/runtime
- 2a87ffeaedb6b534d6eaa000d5ba9b545f4aac1e
+ 9ede9a29128629ffde91b16efb51c9dd6ad7d0f7
-
+
https://github.com/dotnet/runtime
- 2a87ffeaedb6b534d6eaa000d5ba9b545f4aac1e
+ 9ede9a29128629ffde91b16efb51c9dd6ad7d0f7
-
+
https://github.com/dotnet/linker
- a5f8466cb452f50436544572e38ff5faa17e2094
+ f2dd65f51f6096f25e00e0d1e4c6af0e85d41865
-
+
https://github.com/dotnet/xharness
- 952c5c3e6082b77d282d8158f6a1361ef23e205f
+ ed75873fb3b4c0e8746bce1fb325bfb5061b9851
-
+
https://github.com/dotnet/xharness
- 952c5c3e6082b77d282d8158f6a1361ef23e205f
+ ed75873fb3b4c0e8746bce1fb325bfb5061b9851
-
+
https://github.com/dotnet/arcade
- 0558f85d950fee2838bf02b9ba1f20d67f00b504
+ 427c05909067bb2e484116ae2239456bb45adb85
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-optimization
- 8455e2bb85f250a4f1649fcdc3658c25f4cd78aa
+ 91d6b3c1f51888d166701510189505f35714665c
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-optimization
- 8455e2bb85f250a4f1649fcdc3658c25f4cd78aa
+ 91d6b3c1f51888d166701510189505f35714665c
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-optimization
- 8455e2bb85f250a4f1649fcdc3658c25f4cd78aa
+ 91d6b3c1f51888d166701510189505f35714665c
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-optimization
- 8455e2bb85f250a4f1649fcdc3658c25f4cd78aa
+ 91d6b3c1f51888d166701510189505f35714665c
-
+
https://github.com/dotnet/hotreload-utils
- d7f8f855b9d6d3a7090b3bc2745d1bdc8c323fcc
+ 49922f7ec53df036084899e597f3c94627d709eb
-
+
https://github.com/dotnet/runtime-assets
- e444c9ce30e40a75994fe6b4fd5e87a7ef63355b
+ 8784c859561193c0a91b2b47108113a9a7c40038
-
+
https://github.com/dotnet/roslyn-analyzers
- 3541eb2b9c85dd1416ba7571f49c0e44019756b7
+ ffd024eb265e97e639a2879d869cf82b5b958992
https://github.com/dotnet/sdk
diff --git a/eng/Versions.props b/eng/Versions.props
index f903d0fbc672e5..257997d3583251 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -16,7 +16,6 @@
false
release
- true
true
false
false
@@ -51,36 +50,34 @@
3.3.2
4.0.0-4.final
4.0.0-4.final
- 7.0.0-preview1.21528.1
-
- 4.0.0-5.21453.15
+ 7.0.0-preview1.21572.6
2.0.0-alpha.1.21525.11
- 7.0.0-beta.21529.1
- 7.0.0-beta.21529.1
- 7.0.0-beta.21529.1
- 7.0.0-beta.21529.1
- 7.0.0-beta.21529.1
- 7.0.0-beta.21529.1
- 2.5.1-beta.21529.1
- 7.0.0-beta.21529.1
- 7.0.0-beta.21529.1
- 7.0.0-beta.21529.1
- 7.0.0-beta.21529.1
- 7.0.0-beta.21529.1
- 7.0.0-beta.21529.1
- 7.0.0-beta.21529.1
- 7.0.0-beta.21529.1
+ 7.0.0-beta.21576.4
+ 7.0.0-beta.21576.4
+ 7.0.0-beta.21576.4
+ 7.0.0-beta.21576.4
+ 7.0.0-beta.21576.4
+ 7.0.0-beta.21576.4
+ 2.5.1-beta.21576.4
+ 7.0.0-beta.21576.4
+ 7.0.0-beta.21576.4
+ 7.0.0-beta.21576.4
+ 7.0.0-beta.21576.4
+ 7.0.0-beta.21576.4
+ 7.0.0-beta.21576.4
+ 7.0.0-beta.21576.4
+ 7.0.0-beta.21576.4
6.0.0-preview.1.102
- 7.0.0-alpha.1.21551.1
- 7.0.0-alpha.1.21551.1
- 7.0.0-alpha.1.21551.1
+ 7.0.0-alpha.1.21576.4
+ 7.0.0-alpha.1.21576.4
+ 7.0.0-alpha.1.21576.4
3.1.0
- 7.0.0-alpha.1.21551.1
+ 7.0.0-alpha.1.21576.4
5.0.0
4.3.0
@@ -115,28 +112,28 @@
5.0.0
5.0.0
4.9.0-rc2.21473.1
- 7.0.0-alpha.1.21551.1
- 7.0.0-alpha.1.21551.1
+ 7.0.0-alpha.1.21576.4
+ 7.0.0-alpha.1.21576.4
4.5.4
4.5.0
- 7.0.0-alpha.1.21551.1
+ 7.0.0-alpha.1.21576.4
- 7.0.0-beta.21520.1
- 7.0.0-beta.21520.1
- 7.0.0-beta.21520.1
- 7.0.0-beta.21520.1
- 7.0.0-beta.21520.1
- 7.0.0-beta.21520.1
- 7.0.0-beta.21520.1
- 7.0.0-beta.21520.1
- 7.0.0-beta.21520.1
- 7.0.0-beta.21520.1
- 7.0.0-beta.21520.1
+ 7.0.0-beta.21579.1
+ 7.0.0-beta.21579.1
+ 7.0.0-beta.21579.1
+ 7.0.0-beta.21579.1
+ 7.0.0-beta.21579.1
+ 7.0.0-beta.21579.1
+ 7.0.0-beta.21579.1
+ 7.0.0-beta.21579.1
+ 7.0.0-beta.21579.1
+ 7.0.0-beta.21579.1
+ 7.0.0-beta.21579.1
- 1.0.0-prerelease.21530.2
- 1.0.0-prerelease.21530.2
- 1.0.0-prerelease.21530.2
- 1.0.0-prerelease.21530.2
+ 1.0.0-prerelease.21577.2
+ 1.0.0-prerelease.21577.2
+ 1.0.0-prerelease.21577.2
+ 1.0.0-prerelease.21577.2
16.9.0-beta1.21055.5
2.0.0-beta1.20253.1
@@ -146,7 +143,7 @@
2.0.3
1.0.4-preview6.19326.1
0.2.61701
- 1.0.26
+ 1.0.27
16.10.0
$(MicrosoftBuildVersion)
5.8.0
@@ -154,9 +151,9 @@
1.0.1-prerelease-00006
16.9.0-preview-20201201-01
- 1.0.0-prerelease.21529.1
- 1.0.0-prerelease.21529.1
- 1.0.2-alpha.0.21525.2
+ 1.0.0-prerelease.21579.1
+ 1.0.0-prerelease.21579.1
+ 1.0.2-alpha.0.21579.1
2.4.2-pre.9
2.4.2
1.3.0
@@ -169,23 +166,23 @@
6.0.0-preview-20211019.1
- 7.0.100-1.21528.2
+ 7.0.100-1.21562.1
$(MicrosoftNETILLinkTasksVersion)
- 7.0.0-alpha.1.21525.1
+ 7.0.0-alpha.1.21579.1
7.0.0-alpha.1.21529.3
- 11.1.0-alpha.1.21529.2
- 11.1.0-alpha.1.21529.2
- 11.1.0-alpha.1.21529.2
- 11.1.0-alpha.1.21529.2
- 11.1.0-alpha.1.21529.2
- 11.1.0-alpha.1.21529.2
- 11.1.0-alpha.1.21529.2
- 11.1.0-alpha.1.21529.2
+ 11.1.0-alpha.1.21579.1
+ 11.1.0-alpha.1.21579.1
+ 11.1.0-alpha.1.21579.1
+ 11.1.0-alpha.1.21579.1
+ 11.1.0-alpha.1.21579.1
+ 11.1.0-alpha.1.21579.1
+ 11.1.0-alpha.1.21579.1
+ 11.1.0-alpha.1.21579.1
- 7.0.0-alpha.1.21527.1
+ 7.0.0-alpha.1.21560.2
$(MicrosoftNETWorkloadEmscriptenManifest70100Version)
1.1.87-gba258badda
diff --git a/eng/build.ps1 b/eng/build.ps1
index 08afd6598ce971..83b9e5104d9000 100644
--- a/eng/build.ps1
+++ b/eng/build.ps1
@@ -266,6 +266,10 @@ foreach ($argument in $PSBoundParameters.Keys)
}
}
+# Disable targeting pack caching as we reference a partially constructed targeting pack and update it later.
+# The later changes are ignored when using the cache.
+$env:DOTNETSDK_ALLOW_TARGETING_PACK_CACHING=0
+
$failedBuilds = @()
if ($os -eq "Browser") {
diff --git a/eng/build.sh b/eng/build.sh
index 1b20d7a922bc58..f1ef5958fdbc92 100755
--- a/eng/build.sh
+++ b/eng/build.sh
@@ -166,7 +166,7 @@ while [[ $# > 0 ]]; do
opt="$(echo "${1/#--/-}" | tr "[:upper:]" "[:lower:]")"
if [[ $firstArgumentChecked -eq 0 && $opt =~ ^[a-zA-Z.+]+$ ]]; then
- if [ $opt == "help" ]; then
+ if [[ "$opt" == "help" ]]; then
showSubsetHelp
exit 0
fi
@@ -190,7 +190,7 @@ while [[ $# > 0 ]]; do
exit 0
else
passedSubset="$(echo "$2" | tr "[:upper:]" "[:lower:]")"
- if [ $passedSubset == "help" ]; then
+ if [[ "$passedSubset" == "help" ]]; then
showSubsetHelp
exit 0
fi
@@ -461,13 +461,17 @@ if [ ${#actInt[@]} -eq 0 ]; then
arguments="-restore -build $arguments"
fi
-if [ "$os" = "Browser" ] && [ "$arch" != "wasm" ]; then
+if [[ "$os" == "Browser" && "$arch" != "wasm" ]]; then
# override default arch for Browser, we only support wasm
arch=wasm
fi
initDistroRid $os $arch $crossBuild $portableBuild
+# Disable targeting pack caching as we reference a partially constructed targeting pack and update it later.
+# The later changes are ignored when using the cache.
+export DOTNETSDK_ALLOW_TARGETING_PACK_CACHING=0
+
# URL-encode space (%20) to avoid quoting issues until the msbuild call in /eng/common/tools.sh.
# In *proj files (XML docs), URL-encoded string are rendered in their decoded form.
cmakeargs="${cmakeargs// /%20}"
diff --git a/eng/common/build.sh b/eng/common/build.sh
index bc07a1c6848243..55b298f16ccd1f 100755
--- a/eng/common/build.sh
+++ b/eng/common/build.sh
@@ -187,10 +187,6 @@ function InitializeCustomToolset {
}
function Build {
-
- if [[ "$ci" == true ]]; then
- TryLogClientIpAddress
- fi
InitializeToolset
InitializeCustomToolset
diff --git a/eng/common/darc-init.sh b/eng/common/darc-init.sh
index 39abdbecdcf11b..84c1d0cc2e75ac 100755
--- a/eng/common/darc-init.sh
+++ b/eng/common/darc-init.sh
@@ -53,7 +53,7 @@ fi
function InstallDarcCli {
local darc_cli_package_name="microsoft.dotnet.darc"
- InitializeDotNetCli
+ InitializeDotNetCli true
local dotnet_root=$_InitializeDotNetCli
if [ -z "$toolpath" ]; then
diff --git a/eng/common/msbuild.ps1 b/eng/common/msbuild.ps1
index eea19cd8452fd5..f041e5ddd95892 100644
--- a/eng/common/msbuild.ps1
+++ b/eng/common/msbuild.ps1
@@ -6,6 +6,7 @@ Param(
[switch] $ci,
[switch] $prepareMachine,
[switch] $excludePrereleaseVS,
+ [string] $msbuildEngine = $null,
[Parameter(ValueFromRemainingArguments=$true)][String[]]$extraArgs
)
diff --git a/eng/common/native/init-compiler.sh b/eng/common/native/init-compiler.sh
index 8c944f30b28649..e361e03fabdd2e 100644
--- a/eng/common/native/init-compiler.sh
+++ b/eng/common/native/init-compiler.sh
@@ -2,6 +2,7 @@
#
# This file detects the C/C++ compiler and exports it to the CC/CXX environment variables
#
+# NOTE: some scripts source this file and rely on stdout being empty, make sure to not output anything here!
if [[ "$#" -lt 3 ]]; then
echo "Usage..."
@@ -111,12 +112,10 @@ if [[ -z "$CC" ]]; then
exit 1
fi
-if [[ "$compiler" == "clang" ]]; then
- if command -v "lld$desired_version" > /dev/null; then
- # Only lld version >= 9 can be considered stable
- if [[ "$majorVersion" -ge 9 ]]; then
- LDFLAGS="-fuse-ld=lld"
- fi
+# Only lld version >= 9 can be considered stable
+if [[ "$compiler" == "clang" && "$majorVersion" -ge 9 ]]; then
+ if "$CC" -fuse-ld=lld -Wl,--version >/dev/null 2>&1; then
+ LDFLAGS="-fuse-ld=lld"
fi
fi
diff --git a/eng/common/post-build/symbols-validation.ps1 b/eng/common/post-build/symbols-validation.ps1
index a4a92efbedf9a9..cd2181bafa057d 100644
--- a/eng/common/post-build/symbols-validation.ps1
+++ b/eng/common/post-build/symbols-validation.ps1
@@ -134,17 +134,17 @@ $CountMissingSymbols = {
# Save the output and get diagnostic output
$output = & $dotnetSymbolExe --symbols --modules $WindowsPdbVerificationParam $TargetServerParam $FullPath -o $SymbolsPath --diagnostics | Out-String
- if (Test-Path $PdbPath) {
- return 'PDB'
+ if ((Test-Path $PdbPath) -and (Test-path $SymbolPath)) {
+ return 'Module and PDB for Module'
}
- elseif (Test-Path $NGenPdb) {
- return 'NGen PDB'
+ elseif ((Test-Path $NGenPdb) -and (Test-Path $PdbPath) -and (Test-Path $SymbolPath)) {
+ return 'Dll, PDB and NGen PDB'
}
- elseif (Test-Path $SODbg) {
- return 'DBG for SO'
+ elseif ((Test-Path $SODbg) -and (Test-Path $SymbolPath)) {
+ return 'So and DBG for SO'
}
- elseif (Test-Path $DylibDwarf) {
- return 'Dwarf for Dylib'
+ elseif ((Test-Path $DylibDwarf) -and (Test-Path $SymbolPath)) {
+ return 'Dylib and Dwarf for Dylib'
}
elseif (Test-Path $SymbolPath) {
return 'Module'
diff --git a/eng/common/sdk-task.ps1 b/eng/common/sdk-task.ps1
index 7ab9baac5c8d9a..b1bca63ab1d82c 100644
--- a/eng/common/sdk-task.ps1
+++ b/eng/common/sdk-task.ps1
@@ -83,9 +83,6 @@ try {
}
if ($restore) {
- if ($ci) {
- Try-LogClientIpAddress
- }
Build 'Restore'
}
diff --git a/eng/common/sdl/execute-all-sdl-tools.ps1 b/eng/common/sdl/execute-all-sdl-tools.ps1
index 1157151f4862a2..e5bef8ebd3a3b4 100644
--- a/eng/common/sdl/execute-all-sdl-tools.ps1
+++ b/eng/common/sdl/execute-all-sdl-tools.ps1
@@ -124,7 +124,7 @@ try {
Exec-BlockVerbosely {
& $(Join-Path $PSScriptRoot 'run-sdl.ps1') `
-GuardianCliLocation $guardianCliLocation `
- -WorkingDirectory $workingDirectory `
+ -WorkingDirectory $SourceDirectory `
-UpdateBaseline $UpdateBaseline `
-GdnFolder $gdnFolder
}
diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml
index 37dceb1bab0a93..7678b94ce740c0 100644
--- a/eng/common/templates/job/job.yml
+++ b/eng/common/templates/job/job.yml
@@ -114,6 +114,7 @@ jobs:
continueOnError: ${{ parameters.continueOnError }}
condition: and(succeeded(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT'))
+ - ${{ if and(eq(parameters.runAsPublic, 'false'), eq(variables['System.TeamProject'], 'internal')) }}:
- task: NuGetAuthenticate@0
- ${{ if or(eq(parameters.artifacts.download, 'true'), ne(parameters.artifacts.download, '')) }}:
diff --git a/eng/common/templates/job/source-build.yml b/eng/common/templates/job/source-build.yml
index 5023d36dcb3c5b..5cd5325d7b4e6b 100644
--- a/eng/common/templates/job/source-build.yml
+++ b/eng/common/templates/job/source-build.yml
@@ -31,11 +31,6 @@ parameters:
# container and pool.
platform: {}
- # The default VM host AzDO pool. This should be capable of running Docker containers: almost all
- # source-build builds run in Docker, including the default managed platform.
- defaultContainerHostPool:
- vmImage: ubuntu-20.04
-
jobs:
- job: ${{ parameters.jobNamePrefix }}_${{ parameters.platform.name }}
displayName: Source-Build (${{ parameters.platform.name }})
@@ -47,7 +42,15 @@ jobs:
container: ${{ parameters.platform.container }}
${{ if eq(parameters.platform.pool, '') }}:
- pool: ${{ parameters.defaultContainerHostPool }}
+ # The default VM host AzDO pool. This should be capable of running Docker containers: almost all
+ # source-build builds run in Docker, including the default managed platform.
+ pool:
+ ${{ if eq(variables['System.TeamProject'], 'public') }}:
+ name: NetCore1ESPool-Public
+ demands: ImageOverride -equals Build.Ubuntu.1804.Amd64.Open
+ ${{ if eq(variables['System.TeamProject'], 'internal') }}:
+ name: NetCore1ESPool-Internal
+ demands: ImageOverride -equals Build.Ubuntu.1804.Amd64
${{ if ne(parameters.platform.pool, '') }}:
pool: ${{ parameters.platform.pool }}
diff --git a/eng/common/templates/job/source-index-stage1.yml b/eng/common/templates/job/source-index-stage1.yml
index ae85a99a853f4b..4af724eb1a9ec9 100644
--- a/eng/common/templates/job/source-index-stage1.yml
+++ b/eng/common/templates/job/source-index-stage1.yml
@@ -5,8 +5,6 @@ parameters:
sourceIndexBuildCommand: powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "eng/common/build.ps1 -restore -build -binarylog -ci"
preSteps: []
binlogPath: artifacts/log/Debug/Build.binlog
- pool:
- vmImage: 'windows-2019'
condition: ''
dependsOn: ''
@@ -24,7 +22,13 @@ jobs:
- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
- group: source-dot-net stage1 variables
- pool: ${{ parameters.pool }}
+ pool:
+ ${{ if eq(variables['System.TeamProject'], 'public') }}:
+ name: NetCore1ESPool-Public
+ demands: ImageOverride -equals Build.Server.Amd64.VS2019.Open
+ ${{ if eq(variables['System.TeamProject'], 'internal') }}:
+ name: NetCore1ESPool-Internal
+ demands: ImageOverride -equals Build.Server.Amd64.VS2019
steps:
- ${{ each preStep in parameters.preSteps }}:
- ${{ preStep }}
diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1
index 44484289943ed5..f1e1cb53953bcc 100644
--- a/eng/common/tools.ps1
+++ b/eng/common/tools.ps1
@@ -163,9 +163,6 @@ function InitializeDotNetCli([bool]$install, [bool]$createSdkLocationFile) {
# Disable telemetry on CI.
if ($ci) {
$env:DOTNET_CLI_TELEMETRY_OPTOUT=1
-
- # In case of network error, try to log the current IP for reference
- Try-LogClientIpAddress
}
# Source Build uses DotNetCoreSdkDir variable
@@ -301,32 +298,45 @@ function InstallDotNet([string] $dotnetRoot,
if ($skipNonVersionedFiles) { $installParameters.SkipNonVersionedFiles = $skipNonVersionedFiles }
if ($noPath) { $installParameters.NoPath = $True }
- try {
- & $installScript @installParameters
- }
- catch {
- if ($runtimeSourceFeed -or $runtimeSourceFeedKey) {
- Write-Host "Failed to install dotnet from public location. Trying from '$runtimeSourceFeed'"
- if ($runtimeSourceFeed) { $installParameters.AzureFeed = $runtimeSourceFeed }
+ $variations = @()
+ $variations += @($installParameters)
- if ($runtimeSourceFeedKey) {
- $decodedBytes = [System.Convert]::FromBase64String($runtimeSourceFeedKey)
- $decodedString = [System.Text.Encoding]::UTF8.GetString($decodedBytes)
- $installParameters.FeedCredential = $decodedString
- }
+ $dotnetBuilds = $installParameters.Clone()
+ $dotnetbuilds.AzureFeed = "https://dotnetbuilds.azureedge.net/public"
+ $variations += @($dotnetBuilds)
- try {
- & $installScript @installParameters
- }
- catch {
- Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "Failed to install dotnet from custom location '$runtimeSourceFeed'."
- ExitWithExitCode 1
- }
+ if ($runtimeSourceFeed) {
+ $runtimeSource = $installParameters.Clone()
+ $runtimeSource.AzureFeed = $runtimeSourceFeed
+ if ($runtimeSourceFeedKey) {
+ $decodedBytes = [System.Convert]::FromBase64String($runtimeSourceFeedKey)
+ $decodedString = [System.Text.Encoding]::UTF8.GetString($decodedBytes)
+ $runtimeSource.FeedCredential = $decodedString
+ }
+ $variations += @($runtimeSource)
+ }
+
+ $installSuccess = $false
+ foreach ($variation in $variations) {
+ if ($variation | Get-Member AzureFeed) {
+ $location = $variation.AzureFeed
} else {
- Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "Failed to install dotnet from public location."
- ExitWithExitCode 1
+ $location = "public location";
+ }
+ Write-Host "Attempting to install dotnet from $location."
+ try {
+ & $installScript @variation
+ $installSuccess = $true
+ break
+ }
+ catch {
+ Write-Host "Failed to install dotnet from $location."
}
}
+ if (-not $installSuccess) {
+ Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "Failed to install dotnet from any of the specified locations."
+ ExitWithExitCode 1
+ }
}
#
@@ -882,24 +892,6 @@ if (!$disableConfigureToolsetImport) {
}
}
-function Try-LogClientIpAddress()
-{
- Write-Host "Attempting to log this client's IP for Azure Package feed telemetry purposes"
- try
- {
- $result = Invoke-WebRequest -Uri "http://co1.msedge.net/fdv2/diagnostics.aspx" -UseBasicParsing
- $lines = $result.Content.Split([Environment]::NewLine)
- $socketIp = $lines | Select-String -Pattern "^Socket IP:.*"
- Write-Host $socketIp
- $clientIp = $lines | Select-String -Pattern "^Client IP:.*"
- Write-Host $clientIp
- }
- catch
- {
- Write-Host "Unable to get this machine's effective IP address for logging: $_"
- }
-}
-
#
# If $ci flag is set, turn on (and log that we did) special environment variables for improved Nuget client retry logic.
#
diff --git a/eng/common/tools.sh b/eng/common/tools.sh
index 6a4871ef72b7a9..e555c34269f6e8 100755
--- a/eng/common/tools.sh
+++ b/eng/common/tools.sh
@@ -188,28 +188,29 @@ function InstallDotNet {
GetDotNetInstallScript "$root"
local install_script=$_GetDotNetInstallScript
- local archArg=''
+ local installParameters=(--version $version --install-dir "$root")
+
if [[ -n "${3:-}" ]] && [ "$3" != 'unset' ]; then
- archArg="--architecture $3"
+ installParameters+=(--architecture $3)
fi
- local runtimeArg=''
if [[ -n "${4:-}" ]] && [ "$4" != 'sdk' ]; then
- runtimeArg="--runtime $4"
+ installParameters+=(--runtime $4)
fi
- local skipNonVersionedFilesArg=""
if [[ "$#" -ge "5" ]] && [[ "$5" != 'false' ]]; then
- skipNonVersionedFilesArg="--skip-non-versioned-files"
+ installParameters+=(--skip-non-versioned-files)
fi
- bash "$install_script" --version $version --install-dir "$root" $archArg $runtimeArg $skipNonVersionedFilesArg || {
- local exit_code=$?
- echo "Failed to install dotnet SDK from public location (exit code '$exit_code')."
- local runtimeSourceFeed=''
- if [[ -n "${6:-}" ]]; then
- runtimeSourceFeed="--azure-feed $6"
- fi
+ local variations=() # list of variable names with parameter arrays in them
+
+ local public_location=("${installParameters[@]}")
+ variations+=(public_location)
- local runtimeSourceFeedKey=''
+ local dotnetbuilds=("${installParameters[@]}" --azure-feed "https://dotnetbuilds.azureedge.net/public")
+ variations+=(dotnetbuilds)
+
+ if [[ -n "${6:-}" ]]; then
+ variations+=(private_feed)
+ local private_feed=("${installParameters[@]}" --azure-feed $6)
if [[ -n "${7:-}" ]]; then
# The 'base64' binary on alpine uses '-d' and doesn't support '--decode'
# '-d'. To work around this, do a simple detection and switch the parameter
@@ -219,22 +220,27 @@ function InstallDotNet {
decodeArg="-d"
fi
decodedFeedKey=`echo $7 | base64 $decodeArg`
- runtimeSourceFeedKey="--feed-credential $decodedFeedKey"
+ private_feed+=(--feed-credential $decodedFeedKey)
fi
+ fi
- if [[ -n "$runtimeSourceFeed" || -n "$runtimeSourceFeedKey" ]]; then
- bash "$install_script" --version $version --install-dir "$root" $archArg $runtimeArg $skipNonVersionedFilesArg $runtimeSourceFeed $runtimeSourceFeedKey || {
- local exit_code=$?
- Write-PipelineTelemetryError -category 'InitializeToolset' "Failed to install dotnet SDK from custom location '$runtimeSourceFeed' (exit code '$exit_code')."
- ExitWithExitCode $exit_code
- }
- else
- if [[ $exit_code != 0 ]]; then
- Write-PipelineTelemetryError -category 'InitializeToolset' "Failed to install dotnet SDK from public location (exit code '$exit_code')."
- fi
- ExitWithExitCode $exit_code
+ local installSuccess=0
+ for variationName in "${variations[@]}"; do
+ local name="$variationName[@]"
+ local variation=("${!name}")
+ echo "Attempting to install dotnet from $variationName."
+ bash "$install_script" "${variation[@]}" && installSuccess=1
+ if [[ "$installSuccess" -eq 1 ]]; then
+ break
fi
- }
+
+ echo "Failed to install dotnet from $variationName."
+ done
+
+ if [[ "$installSuccess" -eq 0 ]]; then
+ Write-PipelineTelemetryError -category 'InitializeToolset' "Failed to install dotnet SDK from any of the specified locations."
+ ExitWithExitCode 1
+ fi
}
function with_retries {
@@ -399,13 +405,6 @@ function StopProcesses {
return 0
}
-function TryLogClientIpAddress () {
- echo 'Attempting to log this client''s IP for Azure Package feed telemetry purposes'
- if command -v curl > /dev/null; then
- curl -s 'http://co1.msedge.net/fdv2/diagnostics.aspx' | grep ' IP: ' || true
- fi
-}
-
function MSBuild {
local args=$@
if [[ "$pipelines_log" == true ]]; then
diff --git a/eng/docker/build-docker-sdk.ps1 b/eng/docker/build-docker-sdk.ps1
index e3fbaef77d1d36..570d4c5ba738aa 100755
--- a/eng/docker/build-docker-sdk.ps1
+++ b/eng/docker/build-docker-sdk.ps1
@@ -6,21 +6,16 @@
Param(
[string][Alias('t')]$imageName = "dotnet-sdk-libs-current",
[string][Alias('c')]$configuration = "Release",
- [switch][Alias('w')]$buildWindowsContainers,
- [switch][Alias('pa')]$privateAspNetCore
+ [switch][Alias('w')]$buildWindowsContainers
)
+$dotNetVersion="7.0"
$ErrorActionPreference = "Stop"
$REPO_ROOT_DIR=$(git -C "$PSScriptRoot" rev-parse --show-toplevel)
$dockerFilePrefix="$PSScriptRoot/libraries-sdk"
-if ($privateAspNetCore)
-{
- $dockerFilePrefix="$PSScriptRoot/libraries-sdk-aspnetcore"
-}
-
if ($buildWindowsContainers)
{
# Due to size concerns, we don't currently do docker builds on windows.
@@ -34,12 +29,39 @@ if ($buildWindowsContainers)
}
$dockerFile="$dockerFilePrefix.windows.Dockerfile"
+
+ # Collect the following artifacts to folder, that will be used as build context for the container,
+ # so projects can build and test against the live-built runtime:
+ # 1. Reference assembly pack (microsoft.netcore.app.ref)
+ # 2. Runtime pack (microsoft.netcore.app.runtime.win-x64)
+ # 3. targetingpacks.targets, so stress test builds can target the live-built runtime instead of the one in the pre-installed SDK
+ # 4. testhost
+ $binArtifacts = "$REPO_ROOT_DIR\artifacts\bin"
+ $dockerContext = "$REPO_ROOT_DIR\artifacts\docker-context"
+ if (Test-Path $dockerContext) {
+ Remove-Item -Recurse -Force $dockerContext
+ }
+
+ Copy-Item -Recurse -Path $binArtifacts\microsoft.netcore.app.ref `
+ -Destination $dockerContext\microsoft.netcore.app.ref
+ Copy-Item -Recurse -Path $binArtifacts\microsoft.netcore.app.runtime.win-x64 `
+ -Destination $dockerContext\microsoft.netcore.app.runtime.win-x64
+ Copy-Item -Recurse -Path $binArtifacts\testhost `
+ -Destination $dockerContext\testhost
+ Copy-Item -Recurse -Path $REPO_ROOT_DIR\eng\targetingpacks.targets `
+ -Destination $dockerContext\targetingpacks.targets
+
+ # In case of non-CI builds, testhost may already contain Microsoft.AspNetCore.App (see build-local.ps1 in HttpStress):
+ $testHostAspNetCorePath="$dockerContext\testhost\net$dotNetVersion-windows-$configuration-x64/shared/Microsoft.AspNetCore.App"
+ if (Test-Path $testHostAspNetCorePath) {
+ Remove-Item -Recurse -Force $testHostAspNetCorePath
+ }
+
docker build --tag $imageName `
--build-arg CONFIGURATION=$configuration `
- --build-arg TESTHOST_LOCATION=. `
--file $dockerFile `
- "$REPO_ROOT_DIR/artifacts/bin/testhost"
+ $dockerContext
}
else
{
diff --git a/eng/docker/build-docker-sdk.sh b/eng/docker/build-docker-sdk.sh
index c2cdb81efae9a3..92fc632ec05e2a 100755
--- a/eng/docker/build-docker-sdk.sh
+++ b/eng/docker/build-docker-sdk.sh
@@ -23,7 +23,6 @@ scriptroot="$( cd -P "$( dirname "$source" )" && pwd )"
imagename="dotnet-sdk-libs-current"
configuration="Release"
-privateaspnetcore=0
while [[ $# > 0 ]]; do
opt="$(echo "${1/#--/-}" | tr "[:upper:]" "[:lower:]")"
@@ -36,10 +35,6 @@ while [[ $# > 0 ]]; do
configuration=$2
shift 2
;;
- -privateaspnetcore|-pa)
- privateaspnetcore=1
- shift 1
- ;;
*)
shift 1
;;
@@ -49,13 +44,9 @@ done
repo_root=$(git rev-parse --show-toplevel)
docker_file="$scriptroot/libraries-sdk.linux.Dockerfile"
-if [[ $privateaspnetcore -eq 1 ]]; then
- docker_file="$scriptroot/libraries-sdk-aspnetcore.linux.Dockerfile"
-fi
-
docker build --tag $imagename \
--build-arg CONFIGURATION=$configuration \
--file $docker_file \
$repo_root
-exit $?
+exit $?
\ No newline at end of file
diff --git a/eng/docker/libraries-sdk-aspnetcore.linux.Dockerfile b/eng/docker/libraries-sdk-aspnetcore.linux.Dockerfile
deleted file mode 100644
index 184ffdb8bcc8b4..00000000000000
--- a/eng/docker/libraries-sdk-aspnetcore.linux.Dockerfile
+++ /dev/null
@@ -1,36 +0,0 @@
-# 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:6.0-bullseye-slim
-
-FROM $BUILD_BASE_IMAGE as corefxbuild
-
-WORKDIR /repo
-COPY . .
-
-ARG CONFIGURATION=Release
-RUN ./src/coreclr/build.sh -release -skiptests -clang9 && \
- ./libraries.sh -c $CONFIGURATION -runtimeconfiguration release
-
-FROM $SDK_BASE_IMAGE as target
-
-ARG TESTHOST_LOCATION=/repo/artifacts/bin/testhost
-ARG TFM=net7.0
-ARG OS=Linux
-ARG ARCH=x64
-ARG CONFIGURATION=Release
-
-ARG COREFX_SHARED_FRAMEWORK_NAME=Microsoft.NETCore.App
-ARG ASPNETCORE_SHARED_NAME=Microsoft.AspNetCore.App
-ARG SOURCE_COREFX_VERSION=7.0.0
-ARG TARGET_SHARED_FRAMEWORK=/usr/share/dotnet/shared
-ARG TARGET_COREFX_VERSION=$DOTNET_VERSION
-
-COPY --from=corefxbuild \
- $TESTHOST_LOCATION/$TFM-$OS-$CONFIGURATION-$ARCH/shared/$COREFX_SHARED_FRAMEWORK_NAME/$SOURCE_COREFX_VERSION/* \
- $TARGET_SHARED_FRAMEWORK/$COREFX_SHARED_FRAMEWORK_NAME/$TARGET_COREFX_VERSION/
-COPY --from=corefxbuild \
- $TESTHOST_LOCATION/$TFM-$OS-$CONFIGURATION-$ARCH/shared/$COREFX_SHARED_FRAMEWORK_NAME/$SOURCE_COREFX_VERSION/* \
- $TARGET_SHARED_FRAMEWORK/$COREFX_SHARED_FRAMEWORK_NAME/$SOURCE_COREFX_VERSION/
-COPY --from=corefxbuild \
- $TESTHOST_LOCATION/$TFM-$OS-$CONFIGURATION-$ARCH/shared/$ASPNETCORE_SHARED_NAME/$SOURCE_COREFX_VERSION/* \
- $TARGET_SHARED_FRAMEWORK/$ASPNETCORE_SHARED_NAME/$TARGET_COREFX_VERSION/
\ No newline at end of file
diff --git a/eng/docker/libraries-sdk-aspnetcore.windows.Dockerfile b/eng/docker/libraries-sdk-aspnetcore.windows.Dockerfile
deleted file mode 100644
index 75cc112b6acb3e..00000000000000
--- a/eng/docker/libraries-sdk-aspnetcore.windows.Dockerfile
+++ /dev/null
@@ -1,26 +0,0 @@
-# escape=`
-# Simple Dockerfile which copies library build artifacts into target dotnet sdk image
-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"
-ARG TFM=net7.0
-ARG OS=windows
-ARG ARCH=x64
-ARG CONFIGURATION=Release
-
-ARG COREFX_SHARED_FRAMEWORK_NAME=Microsoft.NETCore.App
-ARG ASPNETCORE_SHARED_NAME=Microsoft.AspNetCore.App
-ARG SOURCE_COREFX_VERSION=7.0.0
-ARG TARGET_SHARED_FRAMEWORK="C:\\Program Files\\dotnet\\shared"
-ARG TARGET_COREFX_VERSION=$DOTNET_VERSION
-
-COPY `
- $TESTHOST_LOCATION\$TFM-$OS-$CONFIGURATION-$ARCH\shared\$COREFX_SHARED_FRAMEWORK_NAME\$SOURCE_COREFX_VERSION\ `
- $TARGET_SHARED_FRAMEWORK\$COREFX_SHARED_FRAMEWORK_NAME\$TARGET_COREFX_VERSION\
-COPY `
- $TESTHOST_LOCATION\$TFM-$OS-$CONFIGURATION-$ARCH\shared\$COREFX_SHARED_FRAMEWORK_NAME\$SOURCE_COREFX_VERSION\ `
- $TARGET_SHARED_FRAMEWORK\$COREFX_SHARED_FRAMEWORK_NAME\$SOURCE_COREFX_VERSION\
-COPY `
- $TESTHOST_LOCATION\$TFM-$OS-$CONFIGURATION-$ARCH\shared\$ASPNETCORE_SHARED_NAME\$SOURCE_COREFX_VERSION\ `
- $TARGET_SHARED_FRAMEWORK\$ASPNETCORE_SHARED_NAME\$TARGET_COREFX_VERSION\
diff --git a/eng/docker/libraries-sdk.linux.Dockerfile b/eng/docker/libraries-sdk.linux.Dockerfile
index 6f79a9c39ee7c3..9d7b339383ac6a 100644
--- a/eng/docker/libraries-sdk.linux.Dockerfile
+++ b/eng/docker/libraries-sdk.linux.Dockerfile
@@ -4,25 +4,47 @@ ARG SDK_BASE_IMAGE=mcr.microsoft.com/dotnet/nightly/sdk:6.0-bullseye-slim
FROM $BUILD_BASE_IMAGE as corefxbuild
+ARG CONFIGURATION=Release
+
WORKDIR /repo
COPY . .
-
-ARG CONFIGURATION=Release
-RUN ./build.sh -ci -subset clr+libs -runtimeconfiguration release -c $CONFIGURATION
+RUN ./build.sh clr+libs -runtimeconfiguration Release -configuration $CONFIGURATION -ci
FROM $SDK_BASE_IMAGE as target
-ARG TESTHOST_LOCATION=/repo/artifacts/bin/testhost
-ARG TFM=net7.0
-ARG OS=Linux
-ARG ARCH=x64
+ARG VERSION=7.0
ARG CONFIGURATION=Release
+ENV _DOTNET_INSTALL_CHANNEL="$VERSION.1xx"
+
+# Install latest daily SDK:
+RUN wget https://dot.net/v1/dotnet-install.sh
+RUN bash ./dotnet-install.sh --channel $_DOTNET_INSTALL_CHANNEL --quality daily --install-dir /usr/share/dotnet
+
+# Collect the following artifacts under /live-runtime-artifacts,
+# so projects can build and test against the live-built runtime:
+# 1. Reference assembly pack (microsoft.netcore.app.ref)
+# 2. Runtime pack (microsoft.netcore.app.runtime.linux-x64)
+# 3. targetingpacks.targets, so stress test builds can target the live-built runtime instead of the one in the pre-installed SDK
+# 4. testhost
-ARG COREFX_SHARED_FRAMEWORK_NAME=Microsoft.NETCore.App
-ARG SOURCE_COREFX_VERSION=7.0.0
-ARG TARGET_SHARED_FRAMEWORK=/usr/share/dotnet/shared
-ARG TARGET_COREFX_VERSION=$DOTNET_VERSION
+COPY --from=corefxbuild \
+ /repo/artifacts/bin/microsoft.netcore.app.ref \
+ /live-runtime-artifacts/microsoft.netcore.app.ref
+
+COPY --from=corefxbuild \
+ /repo/artifacts/bin/microsoft.netcore.app.runtime.linux-x64 \
+ /live-runtime-artifacts/microsoft.netcore.app.runtime.linux-x64
COPY --from=corefxbuild \
- $TESTHOST_LOCATION/$TFM-$OS-$CONFIGURATION-$ARCH/shared/$COREFX_SHARED_FRAMEWORK_NAME/$SOURCE_COREFX_VERSION/* \
- $TARGET_SHARED_FRAMEWORK/$COREFX_SHARED_FRAMEWORK_NAME/$TARGET_COREFX_VERSION/
\ No newline at end of file
+ /repo/eng/targetingpacks.targets \
+ /live-runtime-artifacts/targetingpacks.targets
+
+COPY --from=corefxbuild \
+ /repo/artifacts/bin/testhost \
+ /live-runtime-artifacts/testhost
+
+# Add AspNetCore bits to testhost:
+ENV _ASPNETCORE_SOURCE="/usr/share/dotnet/shared/Microsoft.AspNetCore.App/$VERSION*"
+ENV _ASPNETCORE_DEST="/live-runtime-artifacts/testhost/net$VERSION-Linux-$CONFIGURATION-x64/shared/Microsoft.AspNetCore.App"
+RUN mkdir -p $_ASPNETCORE_DEST
+RUN cp -r $_ASPNETCORE_SOURCE $_ASPNETCORE_DEST
\ No newline at end of file
diff --git a/eng/docker/libraries-sdk.windows.Dockerfile b/eng/docker/libraries-sdk.windows.Dockerfile
index 6a7b7764185b3b..c3be811a2cae73 100644
--- a/eng/docker/libraries-sdk.windows.Dockerfile
+++ b/eng/docker/libraries-sdk.windows.Dockerfile
@@ -3,17 +3,23 @@
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"
-ARG TFM=net7.0
-ARG OS=windows
-ARG ARCH=x64
+SHELL ["pwsh", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
+
+ARG VERSION=7.0
+ENV _DOTNET_INSTALL_CHANNEL="$VERSION.1xx"
ARG CONFIGURATION=Release
-ARG COREFX_SHARED_FRAMEWORK_NAME=Microsoft.NETCore.App
-ARG SOURCE_COREFX_VERSION=7.0.0
-ARG TARGET_SHARED_FRAMEWORK="C:\\Program Files\\dotnet\\shared"
-ARG TARGET_COREFX_VERSION=$DOTNET_VERSION
+USER ContainerAdministrator
+
+RUN Invoke-WebRequest -Uri https://dot.net/v1/dotnet-install.ps1 -OutFile .\dotnet-install.ps1
+RUN & .\dotnet-install.ps1 -Channel $env:_DOTNET_INSTALL_CHANNEL -Quality daily -InstallDir 'C:/Program Files/dotnet'
+
+USER ContainerUser
+
+COPY . /live-runtime-artifacts
-COPY `
- $TESTHOST_LOCATION\$TFM-$OS-$CONFIGURATION-$ARCH\shared\$COREFX_SHARED_FRAMEWORK_NAME\$SOURCE_COREFX_VERSION\ `
- $TARGET_SHARED_FRAMEWORK\$COREFX_SHARED_FRAMEWORK_NAME\$TARGET_COREFX_VERSION\
+# Add AspNetCore bits to testhost:
+ENV _ASPNETCORE_SOURCE="C:/Program Files/dotnet/shared/Microsoft.AspNetCore.App/$VERSION*"
+ENV _ASPNETCORE_DEST="C:/live-runtime-artifacts/testhost/net$VERSION-windows-$CONFIGURATION-x64/shared/Microsoft.AspNetCore.App"
+RUN & New-Item -ItemType Directory -Path $env:_ASPNETCORE_DEST
+RUN Copy-Item -Recurse -Path $env:_ASPNETCORE_SOURCE -Destination $env:_ASPNETCORE_DEST
\ No newline at end of file
diff --git a/eng/formatting/format.sh b/eng/formatting/format.sh
index 5a9edf4c33997c..eb4908c078c8a2 100644
--- a/eng/formatting/format.sh
+++ b/eng/formatting/format.sh
@@ -7,14 +7,14 @@ MANAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM "*.cs" "*.vb" |
exec 1>&2
-if [[ -n "$NATIVE_FILES" ]]; then
+if [ -n "$NATIVE_FILES" ]; then
# Format all selected files
echo "$NATIVE_FILES" | cat | xargs | sed -e 's/ /,/g' | xargs "./artifacts/tools/clang-format" -style=file -i
# Add back the modified files to staging
echo "$NATIVE_FILES" | xargs git add
fi
-if [[ -n "$MANAGED_FILES" ]]; then
+if [ -n "$MANAGED_FILES" ]; then
# Format all selected files
echo "$MANAGED_FILES" | cat | xargs | sed -e 's/ /,/g' | dotnet format --include
@@ -22,5 +22,4 @@ if [[ -n "$MANAGED_FILES" ]]; then
echo "$MANAGED_FILES" | xargs git add
fi
-
exit 0
diff --git a/eng/generators.targets b/eng/generators.targets
index e20fa992012aab..13926ce90bd7ed 100644
--- a/eng/generators.targets
+++ b/eng/generators.targets
@@ -3,7 +3,6 @@
true
- false
true
@@ -11,32 +10,57 @@
+ - references System.Runtime.InteropServices -->
+
+
+
+
+
-
-
-
+
+
+
+
+
+
true
@@ -59,16 +83,6 @@
$(DefineConstants);DLLIMPORTGENERATOR_INTERNALUNSAFE
-
-
-
-
-
-
-
-
$(DefineConstants);DLLIMPORTGENERATOR_ENABLED
diff --git a/eng/illink.targets b/eng/illink.targets
index 6c2d95c8477237..018075d8b660b5 100644
--- a/eng/illink.targets
+++ b/eng/illink.targets
@@ -236,6 +236,7 @@
$(ILLinkArgs) -b true
$(ILLinkArgs) -x "$(ILLinkDescriptorsLibraryBuildXml)"
+ $(ILLinkArgs) --substitutions "$(ILLinkSubstitutionsLibraryBuildXml)"
$(LinkerNoWarn);IL2067;IL2068;IL2069;IL2070;IL2071;IL2072;IL2073;IL2074;IL2075;IL2076;IL2077;IL2078;IL2079;IL2080;IL2081;IL2082;IL2083;IL2084;IL2085;IL2086;IL2087;IL2088;IL2089;IL2090;IL2091
$(ILLinkArgs) --nowarn $(LinkerNoWarn)
+ $(ILLinkArgs) --disable-opt ipconstprop
diff --git a/eng/install-native-dependencies.sh b/eng/install-native-dependencies.sh
index a29dfdf78aae49..0b6a8a07abcc42 100755
--- a/eng/install-native-dependencies.sh
+++ b/eng/install-native-dependencies.sh
@@ -22,7 +22,7 @@ if [ "$1" = "Linux" ]; then
if [ "$?" != "0" ]; then
exit 1;
fi
-elif [ "$1" = "MacCatalyst" ] || [ "$1" = "OSX" ] || [ "$1" = "tvOS" ] || [ "$1" = "iOS" ]; then
+elif [[ "$1" == "MacCatalyst" || "$1" == "OSX" || "$1" == "tvOS" || "$1" == "iOS" ]]; then
engdir=$(dirname "${BASH_SOURCE[0]}")
echo "Installed xcode version: `xcode-select -p`"
diff --git a/eng/liveBuilds.targets b/eng/liveBuilds.targets
index d62f4bcd438d8c..1cd7a1d81a2aae 100644
--- a/eng/liveBuilds.targets
+++ b/eng/liveBuilds.targets
@@ -139,13 +139,20 @@
Text="The 'libs' subset must be built before building this project. Missing artifacts: $(LibrariesNativeArtifactsPath). Configuration: '$(LibrariesConfiguration)'. To use a different configuration, specify the 'LibrariesConfiguration' property." />
-
+
+
+
+
+
+
+
+
-
-
-
+
+
$(RuntimeIdGraphDefinitionFile)
diff --git a/eng/native/build-commons.sh b/eng/native/build-commons.sh
index 93afd4afec4dfb..4aebd45649a04c 100755
--- a/eng/native/build-commons.sh
+++ b/eng/native/build-commons.sh
@@ -48,7 +48,9 @@ check_prereqs()
build_native()
{
- eval "$__RepoRootDir/eng/native/version/copy_version_files.sh"
+ if [[ ! -e "$__RepoRootDir/artifacts/obj/_version.c" ]]; then
+ eval "$__RepoRootDir/eng/native/version/copy_version_files.sh"
+ fi
targetOS="$1"
platformArch="$2"
@@ -257,7 +259,7 @@ while :; do
break
fi
- lowerI="$(echo "$1" | tr "[:upper:]" "[:lower:]")"
+ lowerI="$(echo "${1/--/-}" | tr "[:upper:]" "[:lower:]")"
case "$lowerI" in
-\?|-h|--help)
usage
@@ -452,7 +454,7 @@ if [[ "$__CrossBuild" == 1 ]]; then
CROSSCOMPILE=1
export CROSSCOMPILE
# Darwin that doesn't use rootfs
- if [[ ! -n "$ROOTFS_DIR" && "$platform" != "Darwin" ]]; then
+ if [[ -z "$ROOTFS_DIR" && "$platform" != "Darwin" ]]; then
ROOTFS_DIR="$__RepoRootDir/.tools/rootfs/$__BuildArch"
export ROOTFS_DIR
fi
diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake
index a29857451bc79f..0421f1f03ce0a9 100644
--- a/eng/native/configurecompiler.cmake
+++ b/eng/native/configurecompiler.cmake
@@ -525,7 +525,7 @@ if (MSVC)
# disable C++ RTTI
# /GR is added by default by CMake, so remove it manually.
- string(REPLACE "/GR" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+ string(REPLACE "/GR " " " CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-")
add_compile_options($<$:/FC>) # use full pathnames in diagnostics
diff --git a/eng/native/configurepaths.cmake b/eng/native/configurepaths.cmake
index 19407a40a1957a..3f41026eee2ac4 100644
--- a/eng/native/configurepaths.cmake
+++ b/eng/native/configurepaths.cmake
@@ -1,7 +1,6 @@
get_filename_component(CLR_REPO_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../.. ABSOLUTE)
set(CLR_ENG_NATIVE_DIR ${CMAKE_CURRENT_LIST_DIR})
get_filename_component(CLR_SRC_NATIVE_DIR ${CMAKE_CURRENT_LIST_DIR}/../../src/native ABSOLUTE)
-get_filename_component(CLR_SRC_LIBS_NATIVE_DIR ${CMAKE_CURRENT_LIST_DIR}/../../src/libraries/Native ABSOLUTE)
set (CLR_ARTIFACTS_OBJ_DIR "${CLR_REPO_ROOT_DIR}/artifacts/obj")
set(VERSION_HEADER_PATH "${CLR_ARTIFACTS_OBJ_DIR}/_version.h")
set(VERSION_FILE_PATH "${CLR_ARTIFACTS_OBJ_DIR}/_version.c")
diff --git a/eng/native/init-distro-rid.sh b/eng/native/init-distro-rid.sh
index 5f6b194600124a..97fd64708163fa 100644
--- a/eng/native/init-distro-rid.sh
+++ b/eng/native/init-distro-rid.sh
@@ -41,7 +41,7 @@ initNonPortableDistroRid()
# We have forced __PortableBuild=0. This is because -portablebuld
# has been passed as false.
if (( isPortable == 0 )); then
- if [ "${ID}" = "rhel" ] || [ "${ID}" = "rocky" ]; then
+ if [[ "${ID}" == "rhel" || "${ID}" == "rocky" ]]; then
# remove the last version digit
VERSION_ID="${VERSION_ID%.*}"
fi
diff --git a/eng/outerBuild.targets b/eng/outerBuild.targets
index 2c529a56aa2f91..bb3aa73a8033ff 100644
--- a/eng/outerBuild.targets
+++ b/eng/outerBuild.targets
@@ -1,5 +1,11 @@
-
+
+
+ $(NetCoreAppCurrent)
+
+
+
+
diff --git a/eng/pipelines/common/evaluate-default-paths.yml b/eng/pipelines/common/evaluate-default-paths.yml
index 9a29f4d10ae981..326cc76470341c 100644
--- a/eng/pipelines/common/evaluate-default-paths.yml
+++ b/eng/pipelines/common/evaluate-default-paths.yml
@@ -9,9 +9,9 @@ jobs:
- subset: coreclr
include:
- src/libraries/System.Private.CoreLib/*
- - src/libraries/Native/Unix/System.Globalization.Native/*
- - src/libraries/Native/Unix/Common/*
- - src/native/*
+ - src/native/libs/Common/*
+ - src/native/libs/System.Globalization.Native/*
+ - src/native/libs/System.IO.Compression.Native/*
exclude:
- eng/Version.Details.xml
- '*.md'
@@ -22,6 +22,7 @@ jobs:
- src/installer/*
- src/mono/*
- src/libraries/*
+ - src/native/libs/*
- src/tests/*
- eng/pipelines/installer/*
- eng/pipelines/mono/*
@@ -29,10 +30,9 @@ jobs:
- subset: mono
include:
- src/libraries/System.Private.CoreLib/*
- - src/libraries/Native/Unix/System.Globalization.Native/*
- - src/libraries/Native/Unix/Common/*
- - src/tests/BuildWasmApps/*
- - src/native/*
+ - src/native/libs/Common/*
+ - src/native/libs/System.Globalization.Native/*
+ - src/native/libs/System.IO.Compression.Native/*
exclude:
- eng/Version.Details.xml
- '*.md'
@@ -43,6 +43,7 @@ jobs:
- src/installer/*
- src/coreclr/*
- src/libraries/*
+ - src/native/libs/*
- src/tests/*
- eng/pipelines/installer/*
- eng/pipelines/coreclr/*
@@ -99,6 +100,22 @@ jobs:
- src/coreclr/vm/*
exclude:
- '*'
+ - subset: coreclr_jit
+ include:
+ - src/coreclr/jit/*
+ - subset: wasmbuildtests
+ include:
+ - src/tasks/*
+ - src/tests/BuildWasmApps/*
+ - src/mono/wasm/build/*
+ - src/mono/wasm/runtime/*
+ - src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/*
+ - src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/*
+ - src/mono/nuget/Microsoft.NET.Runtime.wasm.Sample.Mono/*
+ - src/mono/nuget/Microsoft.NETCore.BrowserDebugHost.Transport/*
+ - src/mono/nuget/Microsoft.NET.Runtime.MonoAOTCompiler.Task/*
+ - src/mono/nuget/Microsoft.NET.Runtime.MonoTargets.Sdk/*
+ - src/mono/mono/*
- ${{ if ne(parameters.extraSubsets, '') }}:
- ${{ parameters.extraSubsets }}
diff --git a/eng/pipelines/common/global-build-job.yml b/eng/pipelines/common/global-build-job.yml
index 7db10e91e68b3c..cf30d702dda60d 100644
--- a/eng/pipelines/common/global-build-job.yml
+++ b/eng/pipelines/common/global-build-job.yml
@@ -136,7 +136,7 @@ jobs:
df -h
displayName: Disk Usage before Build
- - ${{ if eq(parameters.nameSuffix, 'Browser_wasm_Windows') }}:
+ - ${{ if contains(parameters.nameSuffix, 'Windows_wasm') }}:
# Update machine certs
- task: PowerShell@2
displayName: Update machine certs
diff --git a/eng/pipelines/common/platform-matrix.yml b/eng/pipelines/common/platform-matrix.yml
index d58606fc5597d1..ad3dbd4db8bc0d 100644
--- a/eng/pipelines/common/platform-matrix.yml
+++ b/eng/pipelines/common/platform-matrix.yml
@@ -65,7 +65,10 @@ jobs:
targetRid: linux-arm64
platform: Linux_arm64
container:
- image: ubuntu-16.04-cross-arm64-20210719121212-8a8d3be
+ ${{ if eq(parameters.container, '') }}:
+ image: ubuntu-16.04-cross-arm64-20210719121212-8a8d3be
+ ${{ if ne(parameters.container, '') }}:
+ image: ${{ parameters.container }}
registry: mcr
jobParameters:
runtimeFlavor: ${{ parameters.runtimeFlavor }}
diff --git a/eng/pipelines/common/templates/runtimes/build-test-job.yml b/eng/pipelines/common/templates/runtimes/build-test-job.yml
index 562b426eebc1fd..f7cc7202d2a25c 100644
--- a/eng/pipelines/common/templates/runtimes/build-test-job.yml
+++ b/eng/pipelines/common/templates/runtimes/build-test-job.yml
@@ -5,14 +5,6 @@ parameters:
osSubgroup: ''
container: ''
testGroup: ''
- liveRuntimeBuildConfig: ''
-
- # Determines librariesbuild configuration to use for the tests. Setting this property implies
- # a dependency of this job on the appropriate libraries build and is used
- # to construct the name of the Azure artifact representing libraries build
- # to use for building the tests.
- liveLibrariesBuildConfig: ''
-
displayNameArgs: ''
condition: true
stagedBuild: false
@@ -45,13 +37,8 @@ jobs:
runtimeVariant: ${{ parameters.runtimeVariant }}
testGroup: ${{ parameters.testGroup }}
stagedBuild: ${{ parameters.stagedBuild }}
- liveLibrariesBuildConfig: ${{ parameters.liveLibrariesBuildConfig }}
pool: ${{ parameters.pool }}
dependOnEvaluatePaths: ${{ parameters.dependOnEvaluatePaths }}
- ${{ if eq(parameters.runtimeFlavor, 'coreclr') }}:
- liveRuntimeBuildParams: ${{ format('clr.corelib+libs.ref+libs.native -rc {0} -c {1} -arch {2} -ci', coalesce(parameters.liveRuntimeBuildConfig, parameters.buildConfig), parameters.liveLibrariesBuildConfig, parameters.archType) }}
- ${{ if eq(parameters.runtimeFlavor, 'mono') }}:
- liveRuntimeBuildParams: ${{ format('mono.corelib+libs.ref+libs.native -rc {0} -c {1} -arch {2} -ci', coalesce(parameters.liveRuntimeBuildConfig, parameters.buildConfig), parameters.liveLibrariesBuildConfig, parameters.archType) }}
${{ if and(ne(parameters.osGroup, 'windows'), ne(parameters.compilerName, 'gcc'), not(and(eq(parameters.osGroup, 'Linux'), eq(parameters.osSubgroup, '_musl'), eq(parameters.archType, 'x64'))), not(eq(parameters.osGroup, 'OSX'))) }}:
compilerArg: '-clang9'
@@ -86,12 +73,8 @@ jobs:
variables:
- ${{ each variable in parameters.variables }}:
- ${{ variable }}
- - ${{ if eq(parameters.runtimeFlavor, 'coreclr') }}:
- - name: liveRuntimeBuildParams
- value: ${{ format('clr.corelib+libs.ref+libs.native -rc {0} -c {1} -arch {2} -ci', coalesce(parameters.liveRuntimeBuildConfig, parameters.buildConfig), parameters.liveLibrariesBuildConfig, parameters.archType) }}
- - ${{ if eq(parameters.runtimeFlavor, 'mono') }}:
- - name: liveRuntimeBuildParams
- value: ${{ format('mono.corelib+libs.ref+libs.native -rc {0} -c {1} -arch {2} -ci', coalesce(parameters.liveRuntimeBuildConfig, parameters.buildConfig), parameters.liveLibrariesBuildConfig, parameters.archType) }}
+ - name: liveRuntimeBuildParams
+ value: 'libs.ref -c Release -ci'
- name: compilerArg
value: ''
- ${{ if and(ne(parameters.osGroup, 'windows'), ne(parameters.compilerName, 'gcc')) }}:
@@ -135,7 +118,7 @@ jobs:
displayName: Disk Usage before Build
# Build managed test components
- - script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) $(logRootNameArg)Managed allTargets skipnative skipgeneratelayout skiptestwrappers $(buildConfig) $(archType) $(runtimeFlavorArgs) $(crossArg) $(priorityArg) ci $(librariesOverrideArg)
+ - script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) $(logRootNameArg)Managed allTargets skipnative skipgeneratelayout skiptestwrappers $(buildConfig) $(archType) $(runtimeFlavorArgs) $(crossArg) $(priorityArg) ci /p:TargetOS=AnyOS
displayName: Build managed test components
- ${{ if in(parameters.osGroup, 'OSX', 'iOS', 'tvOS') }}:
@@ -147,7 +130,7 @@ jobs:
# Zip and publish managed test components
- template: /eng/pipelines/common/upload-artifact-step.yml
parameters:
- rootFolder: $(managedTestArtifactRootFolderPath)
+ rootFolder: '$(binTestsPath)/AnyOS.$(archType).$(buildConfigUpper)'
includeRootFolder: false
archiveExtension: '.tar.gz'
archiveType: tar
@@ -174,6 +157,6 @@ jobs:
displayName: Publish Logs
inputs:
targetPath: $(Build.SourcesDirectory)/artifacts/log
- artifactName: '${{ parameters.runtimeFlavor }}_Common_Runtime_TestBuildLogs_AnyOS_AnyCPU_$(buildConfig)_Lib${{ parameters.liveLibrariesBuildConfig }}_${{ parameters.testGroup }}'
+ artifactName: '${{ parameters.runtimeFlavor }}_Common_Runtime_TestBuildLogs_AnyOS_AnyCPU_$(buildConfig)_${{ parameters.testGroup }}'
continueOnError: true
condition: always()
diff --git a/eng/pipelines/common/templates/runtimes/run-test-job.yml b/eng/pipelines/common/templates/runtimes/run-test-job.yml
index 7439f6ff48ecd1..cc22ce9f1c6774 100644
--- a/eng/pipelines/common/templates/runtimes/run-test-job.yml
+++ b/eng/pipelines/common/templates/runtimes/run-test-job.yml
@@ -554,7 +554,10 @@ jobs:
scenarios:
- jitosr
- jitosr_stress
+ - jitosr_pgo
- jitpartialcompilation
+ - jitpartialcompilation_osr
+ - jitpartialcompilation_osr_pgo
- jitobjectstackallocation
${{ if in(parameters.testGroup, 'ilasm') }}:
scenarios:
diff --git a/eng/pipelines/common/variables.yml b/eng/pipelines/common/variables.yml
index dd7fb894cf5b42..d6b84f837c3276 100644
--- a/eng/pipelines/common/variables.yml
+++ b/eng/pipelines/common/variables.yml
@@ -15,6 +15,10 @@ variables:
value: ${{ and(eq(variables['System.TeamProject'], 'public'), ne(variables['Build.Reason'], 'PullRequest')) }}
- name: isNotFullMatrix
value: ${{ and(eq(variables['System.TeamProject'], 'public'), eq(variables['Build.Reason'], 'PullRequest')) }}
+- name: isNotManualAndIsPR
+ value: ${{ and(not(in(variables['Build.DefinitionName'], 'runtime-staging-manual', 'runtime-manual')), eq(variables['System.TeamProject'], 'public'), eq(variables['Build.Reason'], 'PullRequest')) }}
+- name: isManualOrIsNotPR
+ value: ${{ or(in(variables['Build.DefinitionName'], 'runtime-staging-manual', 'runtime-manual'), and(eq(variables['System.TeamProject'], 'public'), ne(variables['Build.Reason'], 'PullRequest'))) }}
# We only run evaluate paths on runtime, runtime-staging and runtime-community pipelines on PRs
# keep in sync with /eng/pipelines/common/xplat-setup.yml
diff --git a/eng/pipelines/common/xplat-setup.yml b/eng/pipelines/common/xplat-setup.yml
index f82aaa8f5ee571..1c7d6490df5f6b 100644
--- a/eng/pipelines/common/xplat-setup.yml
+++ b/eng/pipelines/common/xplat-setup.yml
@@ -50,7 +50,7 @@ jobs:
value: $(buildConfigUpper)
- name: _runSmokeTestsOnlyArg
- value: '/p:RunSmokeTestsOnly=$(isNotFullMatrix)'
+ value: '/p:RunSmokeTestsOnly=$(isNotManualAndIsPR)'
- ${{ if or(eq(parameters.osGroup, 'windows'), eq(parameters.hostedOs, 'windows')) }}:
- name: archiveExtension
value: '.zip'
diff --git a/eng/pipelines/coreclr/ci.yml b/eng/pipelines/coreclr/ci.yml
index baa13cfef87a26..fab41af74d73e8 100644
--- a/eng/pipelines/coreclr/ci.yml
+++ b/eng/pipelines/coreclr/ci.yml
@@ -111,8 +111,6 @@ jobs:
platforms:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
testGroup: outerloop
- jobParameters:
- liveLibrariesBuildConfig: Release
#
# Checked JIT test runs
diff --git a/eng/pipelines/coreclr/clrinterpreter.yml b/eng/pipelines/coreclr/clrinterpreter.yml
index af74c063ba98e3..204d666e7bde3b 100644
--- a/eng/pipelines/coreclr/clrinterpreter.yml
+++ b/eng/pipelines/coreclr/clrinterpreter.yml
@@ -34,7 +34,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: clrinterpreter
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/crossgen2-composite.yml b/eng/pipelines/coreclr/crossgen2-composite.yml
index df683c547bea9e..feea85ea9d3405 100644
--- a/eng/pipelines/coreclr/crossgen2-composite.yml
+++ b/eng/pipelines/coreclr/crossgen2-composite.yml
@@ -35,7 +35,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: innerloop
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/crossgen2-gcstress.yml b/eng/pipelines/coreclr/crossgen2-gcstress.yml
index 444861b6dfac08..fc00d1d7b9a940 100644
--- a/eng/pipelines/coreclr/crossgen2-gcstress.yml
+++ b/eng/pipelines/coreclr/crossgen2-gcstress.yml
@@ -33,7 +33,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: gcstress-extra
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/crossgen2-outerloop.yml b/eng/pipelines/coreclr/crossgen2-outerloop.yml
index df73f8bddea807..b60cc7860fbc2d 100644
--- a/eng/pipelines/coreclr/crossgen2-outerloop.yml
+++ b/eng/pipelines/coreclr/crossgen2-outerloop.yml
@@ -20,7 +20,7 @@ jobs:
- Linux_arm64
- OSX_arm64
- OSX_x64
- - windows_x86
+ - windows_x86
- windows_x64
- windows_arm64
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
@@ -67,7 +67,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: outerloop
- liveLibrariesBuildConfig: Release
# Test most platforms in composite mode as the expected mainline shipping mode
- template: /eng/pipelines/common/platform-matrix.yml
diff --git a/eng/pipelines/coreclr/crossgen2.yml b/eng/pipelines/coreclr/crossgen2.yml
index ba38b43211d33d..35edb1df223172 100644
--- a/eng/pipelines/coreclr/crossgen2.yml
+++ b/eng/pipelines/coreclr/crossgen2.yml
@@ -33,7 +33,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: innerloop
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/gc-longrunning.yml b/eng/pipelines/coreclr/gc-longrunning.yml
index c188c756538eff..e685b36da1fdf3 100644
--- a/eng/pipelines/coreclr/gc-longrunning.yml
+++ b/eng/pipelines/coreclr/gc-longrunning.yml
@@ -32,7 +32,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: gc-longrunning
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/gc-simulator.yml b/eng/pipelines/coreclr/gc-simulator.yml
index 16c24cb5dae659..bade23db69eeb9 100644
--- a/eng/pipelines/coreclr/gc-simulator.yml
+++ b/eng/pipelines/coreclr/gc-simulator.yml
@@ -33,7 +33,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: gc-simulator
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/gc-standalone.yml b/eng/pipelines/coreclr/gc-standalone.yml
index ab0c4d0734b0c0..3aa02047610c3e 100644
--- a/eng/pipelines/coreclr/gc-standalone.yml
+++ b/eng/pipelines/coreclr/gc-standalone.yml
@@ -30,7 +30,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: gc-standalone
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/gcstress-extra.yml b/eng/pipelines/coreclr/gcstress-extra.yml
index ab867202f80a79..6700a71d0dc8b1 100644
--- a/eng/pipelines/coreclr/gcstress-extra.yml
+++ b/eng/pipelines/coreclr/gcstress-extra.yml
@@ -31,7 +31,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: gcstress-extra
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/gcstress0x3-gcstress0xc.yml b/eng/pipelines/coreclr/gcstress0x3-gcstress0xc.yml
index 436deaca79677a..dba8e21d60497f 100644
--- a/eng/pipelines/coreclr/gcstress0x3-gcstress0xc.yml
+++ b/eng/pipelines/coreclr/gcstress0x3-gcstress0xc.yml
@@ -31,7 +31,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: gcstress0x3-gcstress0xc
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/ilasm.yml b/eng/pipelines/coreclr/ilasm.yml
index 187719b4377ec4..eff00c788fe5ec 100644
--- a/eng/pipelines/coreclr/ilasm.yml
+++ b/eng/pipelines/coreclr/ilasm.yml
@@ -44,7 +44,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: ilasm
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/jit-experimental.yml b/eng/pipelines/coreclr/jit-experimental.yml
index 02048f1bac6734..f6e4edd422bdeb 100644
--- a/eng/pipelines/coreclr/jit-experimental.yml
+++ b/eng/pipelines/coreclr/jit-experimental.yml
@@ -29,7 +29,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: jit-experimental
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/jitstress-isas-arm.yml b/eng/pipelines/coreclr/jitstress-isas-arm.yml
index c1410678e4a9a7..42cf82b96d94e9 100644
--- a/eng/pipelines/coreclr/jitstress-isas-arm.yml
+++ b/eng/pipelines/coreclr/jitstress-isas-arm.yml
@@ -30,7 +30,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: jitstress-isas-arm
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/jitstress-isas-x86.yml b/eng/pipelines/coreclr/jitstress-isas-x86.yml
index 90453431d8114f..65798ce5252d09 100644
--- a/eng/pipelines/coreclr/jitstress-isas-x86.yml
+++ b/eng/pipelines/coreclr/jitstress-isas-x86.yml
@@ -31,7 +31,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: jitstress-isas-x86
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/jitstress.yml b/eng/pipelines/coreclr/jitstress.yml
index f1f50f97b66abd..4ea3c6c2a23831 100644
--- a/eng/pipelines/coreclr/jitstress.yml
+++ b/eng/pipelines/coreclr/jitstress.yml
@@ -35,7 +35,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: jitstress
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/jitstress2-jitstressregs.yml b/eng/pipelines/coreclr/jitstress2-jitstressregs.yml
index 0e826e0db010c8..a3eb7c09fc14cd 100644
--- a/eng/pipelines/coreclr/jitstress2-jitstressregs.yml
+++ b/eng/pipelines/coreclr/jitstress2-jitstressregs.yml
@@ -35,7 +35,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: checked
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/jitstressregs-x86.yml b/eng/pipelines/coreclr/jitstressregs-x86.yml
index 204cf5632f159a..006d4ed57f3f28 100644
--- a/eng/pipelines/coreclr/jitstressregs-x86.yml
+++ b/eng/pipelines/coreclr/jitstressregs-x86.yml
@@ -30,7 +30,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: jitstressregs-x86
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/jitstressregs.yml b/eng/pipelines/coreclr/jitstressregs.yml
index c90d4bb8bc998b..727e0e674b76f3 100644
--- a/eng/pipelines/coreclr/jitstressregs.yml
+++ b/eng/pipelines/coreclr/jitstressregs.yml
@@ -35,7 +35,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: jitstressregs
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/perf_slow.yml b/eng/pipelines/coreclr/perf_slow.yml
index 2be7b5345a1a5f..e8410af6d361f9 100644
--- a/eng/pipelines/coreclr/perf_slow.yml
+++ b/eng/pipelines/coreclr/perf_slow.yml
@@ -39,6 +39,7 @@ jobs:
jobTemplate: /eng/pipelines/mono/templates/build-job.yml
runtimeFlavor: mono
buildConfig: release
+ container: ubuntu-18.04-cross-arm64-20211022152824-b2c2436
platforms:
- Linux_arm64
@@ -47,6 +48,7 @@ jobs:
parameters:
jobTemplate: /eng/pipelines/common/build-coreclr-and-libraries-job.yml
buildConfig: release
+ container: ubuntu-18.04-cross-arm64-20211022152824-b2c2436
platforms:
- Linux_arm64
jobParameters:
@@ -58,6 +60,7 @@ jobs:
jobTemplate: /eng/pipelines/coreclr/templates/perf-job.yml
buildConfig: release
runtimeFlavor: mono
+ container: ubuntu-18.04-cross-arm64-20211022152824-b2c2436
platforms:
- Linux_arm64
jobParameters:
@@ -76,6 +79,7 @@ jobs:
jobTemplate: /eng/pipelines/common/global-build-job.yml
buildConfig: release
runtimeFlavor: mono
+ container: ubuntu-18.04-cross-arm64-20211022152824-b2c2436
platforms:
- Browser_wasm
jobParameters:
@@ -98,6 +102,7 @@ jobs:
parameters:
jobTemplate: /eng/pipelines/common/build-coreclr-and-libraries-job.yml
buildConfig: release
+ container: ubuntu-18.04-cross-arm64-20211022152824-b2c2436
platforms:
- Linux_arm64
- windows_arm64
@@ -109,6 +114,7 @@ jobs:
parameters:
jobTemplate: /eng/pipelines/common/global-build-job.yml
buildConfig: release
+ container: ubuntu-18.04-cross-arm64-20211022152824-b2c2436
runtimeFlavor: mono
platforms:
- Browser_wasm
@@ -129,6 +135,7 @@ jobs:
parameters:
jobTemplate: /eng/pipelines/common/global-build-job.yml
buildConfig: release
+ container: ubuntu-18.04-cross-arm64-20211022152824-b2c2436
runtimeFlavor: mono
platforms:
- Linux_arm64
@@ -154,6 +161,7 @@ jobs:
runtimeFlavor: aot
platforms:
- Linux_arm64
+ container: ubuntu-18.04-cross-arm64-20211022152824-b2c2436
jobParameters:
testGroup: perf
liveLibrariesBuildConfig: Release
@@ -172,6 +180,7 @@ jobs:
runtimeFlavor: coreclr
platforms:
- Linux_arm64
+ container: ubuntu-18.04-cross-arm64-20211022152824-b2c2436
jobParameters:
testGroup: perf
liveLibrariesBuildConfig: Release
diff --git a/eng/pipelines/coreclr/pgo.yml b/eng/pipelines/coreclr/pgo.yml
index 132464b8dad59f..80f1579e1bf1c4 100644
--- a/eng/pipelines/coreclr/pgo.yml
+++ b/eng/pipelines/coreclr/pgo.yml
@@ -35,7 +35,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: pgo
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/r2r-extra.yml b/eng/pipelines/coreclr/r2r-extra.yml
index 9c386ac556e862..940c29d7f3741a 100644
--- a/eng/pipelines/coreclr/r2r-extra.yml
+++ b/eng/pipelines/coreclr/r2r-extra.yml
@@ -31,7 +31,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: r2r-extra
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/r2r.yml b/eng/pipelines/coreclr/r2r.yml
index 4fc7662293c50d..81bee51bc93273 100644
--- a/eng/pipelines/coreclr/r2r.yml
+++ b/eng/pipelines/coreclr/r2r.yml
@@ -35,7 +35,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: outerloop
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/release-tests.yml b/eng/pipelines/coreclr/release-tests.yml
index bfa51bd287826d..fb7cd6ad8adb4e 100644
--- a/eng/pipelines/coreclr/release-tests.yml
+++ b/eng/pipelines/coreclr/release-tests.yml
@@ -36,7 +36,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: outerloop
- liveLibrariesBuildConfig: Release
#
# Release test runs
diff --git a/eng/pipelines/coreclr/runincontext.yml b/eng/pipelines/coreclr/runincontext.yml
index 9dcd8f7a1bc779..1588a59bcf539f 100644
--- a/eng/pipelines/coreclr/runincontext.yml
+++ b/eng/pipelines/coreclr/runincontext.yml
@@ -30,7 +30,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: outerloop
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/superpmi-asmdiffs.yml b/eng/pipelines/coreclr/superpmi-asmdiffs.yml
new file mode 100644
index 00000000000000..d8835b40b8133d
--- /dev/null
+++ b/eng/pipelines/coreclr/superpmi-asmdiffs.yml
@@ -0,0 +1,37 @@
+# This pipeline only runs on GitHub PRs, not on merges.
+trigger: none
+
+# Only run on changes to the JIT directory. Don't run if the JIT-EE GUID has changed,
+# since there won't be any SuperPMI collections with the new GUID until the collection
+# pipeline completes after this PR is merged.
+pr:
+ branches:
+ include:
+ - main
+ paths:
+ include:
+ - src/coreclr/jit/*
+ exclude:
+ - src/coreclr/inc/jiteeversionguid.h
+
+jobs:
+
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/coreclr/templates/build-jit-job.yml
+ buildConfig: checked
+ platforms:
+ - windows_x64
+ - windows_x86
+ jobParameters:
+ uploadAs: 'pipelineArtifacts'
+
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/coreclr/templates/superpmi-asmdiffs-job.yml
+ buildConfig: checked
+ platforms:
+ - windows_x64
+ - windows_x86
+ helixQueueGroup: ci
+ helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
\ No newline at end of file
diff --git a/eng/pipelines/coreclr/superpmi-collect.yml b/eng/pipelines/coreclr/superpmi-collect.yml
index 7b285903a5fef7..0d41f151d6b554 100644
--- a/eng/pipelines/coreclr/superpmi-collect.yml
+++ b/eng/pipelines/coreclr/superpmi-collect.yml
@@ -12,7 +12,7 @@ trigger:
- src/coreclr/inc/jiteeversionguid.h
# This pipeline is supposed to be run only on merged changes
-# and should not be triggerable from a PR.
+# and should not be triggerable from a PR.
pr: none
schedules:
@@ -54,7 +54,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: outerloop
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/coreclr/superpmi-replay.yml b/eng/pipelines/coreclr/superpmi-replay.yml
index 90ed1896c03af0..0dca4217b18383 100644
--- a/eng/pipelines/coreclr/superpmi-replay.yml
+++ b/eng/pipelines/coreclr/superpmi-replay.yml
@@ -1,3 +1,6 @@
+# Only run on changes to the JIT directory. Don't run if the JIT-EE GUID has changed,
+# since there won't be any SuperPMI collections with the new GUID until the collection
+# pipeline completes.
trigger:
batch: false
branches:
@@ -6,6 +9,7 @@ trigger:
paths:
include:
- src/coreclr/jit/*
+ exclude:
- src/coreclr/inc/jiteeversionguid.h
jobs:
diff --git a/eng/pipelines/coreclr/templates/build-job.yml b/eng/pipelines/coreclr/templates/build-job.yml
index 36640298e1f0f3..a8c57eadac6255 100644
--- a/eng/pipelines/coreclr/templates/build-job.yml
+++ b/eng/pipelines/coreclr/templates/build-job.yml
@@ -45,7 +45,7 @@ jobs:
# Compute job name from template parameters
${{ if and(ne(parameters.testGroup, 'clrTools'), eq(parameters.compilerName, 'gcc')) }}:
name: ${{ format('coreclr_{0}_product_build_{1}{1}_{3}_{4}', parameters.compilerName, parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig) }}
- displayName: ${{ format('CoreCLR GCC Product Build {0}{1} {2} {3}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig) }}
+ displayName: ${{ format('GCC Product Build {0}{1} {2} {3}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig) }}
${{ if and(ne(parameters.testGroup, 'clrTools'), ne(parameters.compilerName, 'gcc')) }}:
name: ${{ format('coreclr_{0}_product_build_{1}{2}_{3}_{4}{5}',
parameters.runtimeVariant,
@@ -228,6 +228,11 @@ jobs:
- script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) skipmanaged skipgeneratelayout $(buildConfig) $(archType) $(crossArg) $(osArg) $(priorityArg) $(compilerArg)
displayName: Build native test components
+ # Build libs.native, host.native and mono with gcc
+ - ${{ if eq(parameters.compilerName, 'gcc') }}:
+ - script: $(Build.SourcesDirectory)$(dir)build$(scriptExt) libs.native+host.native+mono $(compilerArg) $(crossArg) -arch $(archType) $(osArg) -c $(buildConfig) $(pgoInstrumentArg) $(officialBuildIdArg) -ci
+ displayName: Build libs.native+host.native+mono
+
# Sign and add entitlements to these MacOS binaries
- ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
- ${{ if eq(parameters.osGroup, 'OSX') }}:
diff --git a/eng/pipelines/coreclr/templates/perf-job.yml b/eng/pipelines/coreclr/templates/perf-job.yml
index 6802056be03a65..50941e60f1c5f5 100644
--- a/eng/pipelines/coreclr/templates/perf-job.yml
+++ b/eng/pipelines/coreclr/templates/perf-job.yml
@@ -135,7 +135,7 @@ jobs:
artifactName: BrowserWasm
displayName: BrowserWasm
- - script: "mkdir $(librariesDownloadDir)/bin/wasm;unzip -o $(librariesDownloadDir)/BrowserWasm/artifacts/packages/Release/Shipping/Microsoft.NETCore.App.Runtime.Mono.browser-wasm.7.0.0-ci.nupkg data/* runtimes/* -d $(librariesDownloadDir)/bin/wasm;cp src/mono/wasm/runtime-test.js $(librariesDownloadDir)/bin/wasm/runtime-test.js;find $(librariesDownloadDir)/bin/wasm -type f -exec chmod 664 {} \\;"
+ - script: "mkdir $(librariesDownloadDir)/bin/wasm;unzip -o $(librariesDownloadDir)/BrowserWasm/artifacts/packages/Release/Shipping/Microsoft.NETCore.App.Runtime.Mono.browser-wasm.7.0.0-ci.nupkg data/* runtimes/* -d $(librariesDownloadDir)/bin/wasm;cp src/mono/wasm/test-main.js $(librariesDownloadDir)/bin/wasm/test-main.js;find $(librariesDownloadDir)/bin/wasm -type f -exec chmod 664 {} \\;"
displayName: "Create wasm directory (Linux)"
# Download mono AOT
diff --git a/eng/pipelines/coreclr/templates/run-superpmi-asmdiffs-job.yml b/eng/pipelines/coreclr/templates/run-superpmi-asmdiffs-job.yml
new file mode 100644
index 00000000000000..6bf5a3e8d01916
--- /dev/null
+++ b/eng/pipelines/coreclr/templates/run-superpmi-asmdiffs-job.yml
@@ -0,0 +1,145 @@
+parameters:
+ steps: [] # optional -- any additional steps that need to happen before pulling down the jitutils repo and sending the jitutils to helix (ie building your repo)
+ variables: [] # optional -- list of additional variables to send to the template
+ jobName: '' # required -- job name
+ displayName: '' # optional -- display name for the job. Will use jobName if not passed
+ pool: '' # required -- name of the Build pool
+ container: '' # required -- name of the container
+ buildConfig: '' # required -- build configuration
+ archType: '' # required -- targeting CPU architecture
+ osGroup: '' # required -- operating system for the job
+ osSubgroup: '' # optional -- operating system subgroup
+ continueOnError: 'false' # optional -- determines whether to continue the build if the step errors
+ dependsOn: '' # optional -- dependencies of the job
+ timeoutInMinutes: 120 # optional -- timeout for the job
+ enableTelemetry: false # optional -- enable for telemetry
+ liveLibrariesBuildConfig: '' # optional -- live-live libraries configuration to use for the run
+ helixQueues: '' # required -- Helix queues
+ dependOnEvaluatePaths: false
+
+jobs:
+- template: xplat-pipeline-job.yml
+ parameters:
+ dependsOn: ${{ parameters.dependsOn }}
+ buildConfig: ${{ parameters.buildConfig }}
+ archType: ${{ parameters.archType }}
+ osGroup: ${{ parameters.osGroup }}
+ osSubgroup: ${{ parameters.osSubgroup }}
+ liveLibrariesBuildConfig: ${{ parameters.liveLibrariesBuildConfig }}
+ enableTelemetry: ${{ parameters.enableTelemetry }}
+ enablePublishBuildArtifacts: true
+ continueOnError: ${{ parameters.continueOnError }}
+ dependOnEvaluatePaths: ${{ parameters.dependOnEvaluatePaths }}
+ timeoutInMinutes: ${{ parameters.timeoutInMinutes }}
+
+ ${{ if ne(parameters.displayName, '') }}:
+ displayName: '${{ parameters.displayName }}'
+ ${{ if eq(parameters.displayName, '') }}:
+ displayName: '${{ parameters.jobName }}'
+
+ variables:
+
+ - ${{ each variable in parameters.variables }}:
+ - ${{ if ne(variable.name, '') }}:
+ - name: ${{ variable.name }}
+ value: ${{ variable.value }}
+ - ${{ if ne(variable.group, '') }}:
+ - group: ${{ variable.group }}
+
+ - name: PythonScript
+ value: 'py -3'
+ - name: PipScript
+ value: 'py -3 -m pip'
+ - name: SpmiCollectionLocation
+ value: '$(Build.SourcesDirectory)\artifacts\spmi\'
+ - name: SpmiLogsLocation
+ value: '$(Build.SourcesDirectory)\artifacts\spmi_logs\'
+ - name: SpmiAsmdiffsLocation
+ value: '$(Build.SourcesDirectory)\artifacts\spmi_asmdiffs\'
+ - name: HelixResultLocation
+ value: '$(Build.SourcesDirectory)\artifacts\helixresults\'
+
+ workspace:
+ clean: all
+ pool:
+ ${{ parameters.pool }}
+ container: ${{ parameters.container }}
+ steps:
+ - ${{ parameters.steps }}
+
+ - script: |
+ mkdir $(SpmiCollectionLocation)
+ mkdir $(SpmiLogsLocation)
+ mkdir $(SpmiAsmdiffsLocation)
+ displayName: Create directories
+
+ - script: $(PythonScript) $(Build.SourcesDirectory)/src/coreclr/scripts/superpmi_asmdiffs_setup.py -source_directory $(Build.SourcesDirectory) -product_directory $(buildProductRootFolderPath) -arch $(archType)
+ displayName: ${{ format('SuperPMI asmdiffs setup ({0})', parameters.archType) }}
+
+ # Run superpmi asmdiffs in helix
+ - template: /eng/pipelines/common/templates/runtimes/send-to-helix-step.yml
+ parameters:
+ displayName: 'Send job to Helix'
+ helixBuild: $(Build.BuildNumber)
+ helixSource: $(_HelixSource)
+ helixType: 'build/tests/'
+ helixQueues: ${{ join(',', parameters.helixQueues) }}
+ creator: dotnet-bot
+ WorkItemTimeout: 2:00 # 2 hours
+ WorkItemDirectory: '$(WorkItemDirectory)'
+ CorrelationPayloadDirectory: '$(CorrelationPayloadDirectory)'
+ helixProjectArguments: '$(Build.SourcesDirectory)/src/coreclr/scripts/superpmi-asmdiffs.proj'
+ BuildConfig: ${{ parameters.buildConfig }}
+ osGroup: ${{ parameters.osGroup }}
+ archType: ${{ parameters.archType }}
+ shouldContinueOnError: true # Run the future step i.e. upload superpmi logs
+
+ # Always upload the available logs for diagnostics
+ - task: CopyFiles@2
+ displayName: Copying superpmi.log of all partitions
+ inputs:
+ sourceFolder: '$(HelixResultLocation)'
+ contents: '**/superpmi_*.log'
+ targetFolder: '$(SpmiLogsLocation)'
+ condition: always()
+
+ - task: CopyFiles@2
+ displayName: Copying superpmi.md of all partitions
+ inputs:
+ sourceFolder: '$(HelixResultLocation)'
+ contents: '**/superpmi_*.md'
+ targetFolder: '$(SpmiAsmdiffsLocation)'
+ condition: always()
+
+ - task: CopyFiles@2
+ displayName: Copying dasm files of all partitions
+ inputs:
+ sourceFolder: '$(HelixResultLocation)'
+ contents: '**/Asmdiffs_*.zip'
+ targetFolder: '$(SpmiAsmdiffsLocation)'
+ condition: always()
+
+ - script: $(PythonScript) $(Build.SourcesDirectory)/src/coreclr/scripts/superpmi_asmdiffs_summarize.py -diff_summary_dir $(SpmiAsmdiffsLocation) -arch $(archType)
+ displayName: ${{ format('Summarize ({0})', parameters.archType) }}
+ condition: always()
+
+ - task: PublishPipelineArtifact@1
+ displayName: Publish SuperPMI logs
+ inputs:
+ targetPath: $(SpmiLogsLocation)
+ artifactName: 'SuperPMI_Logs_$(archType)_$(buildConfig)'
+ condition: always()
+
+ - task: PublishPipelineArtifact@1
+ displayName: Publish SuperPMI asmdiffs files
+ inputs:
+ targetPath: $(SpmiAsmdiffsLocation)
+ artifactName: 'SuperPMI_Asmdiffs_$(archType)_$(buildConfig)'
+ condition: always()
+
+ - task: PublishPipelineArtifact@1
+ displayName: Publish SuperPMI build logs
+ inputs:
+ targetPath: $(Build.SourcesDirectory)/artifacts/log
+ artifactName: 'SuperPMI_BuildLogs_$(archType)_$(buildConfig)'
+ condition: always()
diff --git a/eng/pipelines/coreclr/templates/run-superpmi-collect-job.yml b/eng/pipelines/coreclr/templates/run-superpmi-collect-job.yml
index 01726c6b1a339c..1ac235f6f6d08e 100644
--- a/eng/pipelines/coreclr/templates/run-superpmi-collect-job.yml
+++ b/eng/pipelines/coreclr/templates/run-superpmi-collect-job.yml
@@ -114,7 +114,7 @@ jobs:
- script: |
mkdir -p $(MergedMchFileLocation)
mkdir -p $(SpmiLogsLocation)
- displayName: Create directory for merged collection
+ displayName: Create directories
- ${{ if eq(parameters.osGroup, 'windows') }}:
- script: |
mkdir $(MergedMchFileLocation)
diff --git a/eng/pipelines/coreclr/templates/run-superpmi-replay-job.yml b/eng/pipelines/coreclr/templates/run-superpmi-replay-job.yml
index 696b2e5e043bca..9b64c380d26056 100644
--- a/eng/pipelines/coreclr/templates/run-superpmi-replay-job.yml
+++ b/eng/pipelines/coreclr/templates/run-superpmi-replay-job.yml
@@ -66,11 +66,12 @@ jobs:
- ${{ parameters.steps }}
- script: |
- mkdir -p $(SpmiCollectionLocation)
- displayName: Create directory for SPMI collection
+ mkdir $(SpmiCollectionLocation)
+ mkdir $(SpmiLogsLocation)
+ displayName: Create directories
- script: $(PythonScript) $(Build.SourcesDirectory)/src/coreclr/scripts/superpmi_replay_setup.py -source_directory $(Build.SourcesDirectory) -product_directory $(buildProductRootFolderPath) -arch $(archType)
- displayName: ${{ format('SuperPMI replay setup ({0} {1})', parameters.osGroup, parameters.archType) }}
+ displayName: ${{ format('SuperPMI replay setup ({0})', parameters.archType) }}
# Run superpmi replay in helix
- template: /eng/pipelines/common/templates/runtimes/send-to-helix-step.yml
diff --git a/eng/pipelines/coreclr/templates/superpmi-asmdiffs-job.yml b/eng/pipelines/coreclr/templates/superpmi-asmdiffs-job.yml
new file mode 100644
index 00000000000000..bb05902efe969b
--- /dev/null
+++ b/eng/pipelines/coreclr/templates/superpmi-asmdiffs-job.yml
@@ -0,0 +1,39 @@
+parameters:
+ buildConfig: '' # required -- build configuration
+ archType: '' # required -- targeting CPU architecture
+ osGroup: '' # required -- operating system for the job
+ osSubgroup: '' # optional -- operating system subgroup
+ pool: ''
+ timeoutInMinutes: 120 # build timeout
+ variables: {}
+ helixQueues: ''
+ dependOnEvaluatePaths: false
+ runJobTemplate: '/eng/pipelines/coreclr/templates/run-superpmi-asmdiffs-job.yml'
+
+jobs:
+- template: ${{ parameters.runJobTemplate }}
+ parameters:
+ jobName: ${{ format('superpmi_asmdiffs_{0}{1}_{2}_{3}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig) }}
+ displayName: ${{ format('SuperPMI asmdiffs {0}{1} {2} {3}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig) }}
+ pool: ${{ parameters.pool }}
+ buildConfig: ${{ parameters.buildConfig }}
+ archType: ${{ parameters.archType }}
+ osGroup: ${{ parameters.osGroup }}
+ osSubgroup: ${{ parameters.osSubgroup }}
+ dependOnEvaluatePaths: ${{ parameters.dependOnEvaluatePaths }}
+ timeoutInMinutes: ${{ parameters.timeoutInMinutes }}
+ helixQueues: ${{ parameters.helixQueues }}
+ dependsOn:
+ - ${{ format('coreclr_jit_build_{0}{1}_{2}_{3}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig) }}
+
+ variables: ${{ parameters.variables }}
+
+ steps:
+
+ # Download jit builds
+ - template: /eng/pipelines/common/download-artifact-step.yml
+ parameters:
+ unpackFolder: $(buildProductRootFolderPath)
+ artifactFileName: '$(buildProductArtifactName)$(archiveExtension)'
+ artifactName: '$(buildProductArtifactName)'
+ displayName: 'JIT product build'
\ No newline at end of file
diff --git a/eng/pipelines/coreclr/tieringtest.yml b/eng/pipelines/coreclr/tieringtest.yml
index 7767c3261fbd52..0a57b1e27754af 100644
--- a/eng/pipelines/coreclr/tieringtest.yml
+++ b/eng/pipelines/coreclr/tieringtest.yml
@@ -30,7 +30,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: outerloop
- liveLibrariesBuildConfig: Release
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
diff --git a/eng/pipelines/evaluate-changed-paths.sh b/eng/pipelines/evaluate-changed-paths.sh
index 76bc06d5280143..ee9ae31a3363ab 100755
--- a/eng/pipelines/evaluate-changed-paths.sh
+++ b/eng/pipelines/evaluate-changed-paths.sh
@@ -141,7 +141,7 @@ probePaths() {
echo "******* Probing $_subset exclude paths *******";
for _path in "${exclude_paths[@]}"; do
echo "$_path"
- if [[ "$exclude_path_string" == "" ]]; then
+ if [[ -z "$exclude_path_string" ]]; then
exclude_path_string=":!$_path"
else
exclude_path_string="$exclude_path_string :!$_path"
@@ -159,7 +159,7 @@ probePaths() {
echo "******* Probing $_subset include paths *******";
for _path in "${include_paths[@]}"; do
echo "$_path"
- if [[ "$include_path_string" == "" ]]; then
+ if [[ -z "$include_path_string" ]]; then
include_path_string=":$_path"
else
include_path_string="$include_path_string :$_path"
diff --git a/eng/pipelines/libraries/enterprise/linux.yml b/eng/pipelines/libraries/enterprise/linux.yml
index d958c56c1a354b..aa2ccee679549e 100644
--- a/eng/pipelines/libraries/enterprise/linux.yml
+++ b/eng/pipelines/libraries/enterprise/linux.yml
@@ -14,7 +14,7 @@ pr:
- eng/pipelines/libraries/enterprise/*
- src/libraries/Common/src/System/Net/*
- src/libraries/Common/tests/System/Net/*
- - src/libraries/Native/Unix/System.Net.Security.Native/*
+ - src/native/libs/System.Net.Security.Native/*
- src/libraries/System.Net.Http/*
- src/libraries/System.Net.Security/*
diff --git a/eng/pipelines/libraries/helix-queues-setup.yml b/eng/pipelines/libraries/helix-queues-setup.yml
index 73432c9c408b1a..60527beb6c052e 100644
--- a/eng/pipelines/libraries/helix-queues-setup.yml
+++ b/eng/pipelines/libraries/helix-queues-setup.yml
@@ -132,9 +132,11 @@ jobs:
- Windows.81.Amd64.Open
- Windows.10.Amd64.ServerRS5.Open
- Windows.10.Amd64.Server19H1.Open
+ - Windows.10.Amd64.Server20H2.Open
- ${{ if ne(parameters.jobParameters.runtimeFlavor, 'mono') }}:
- (Windows.Nano.1809.Amd64.Open)windows.10.amd64.serverrs5.open@mcr.microsoft.com/dotnet-buildtools/prereqs:nanoserver-1809-helix-amd64-08e8e40-20200107182504
- (Windows.Server.Core.1909.Amd64.Open)windows.10.amd64.server20h1.open@mcr.microsoft.com/dotnet-buildtools/prereqs:windowsservercore-2004-helix-amd64-20200904200251-272704c
+ - (Windows.Server.Core.20H2.Amd64.Open)windows.10.amd64.server20h2.open@mcr.microsoft.com/dotnet-buildtools/prereqs:windowsservercore-20H2-helix-amd64-20210922171304-edc3733
- ${{ if ne(parameters.jobParameters.isFullMatrix, true) }}:
- Windows.81.Amd64.Open
- Windows.10.Amd64.Server19H1.ES.Open
diff --git a/eng/pipelines/libraries/stress/http.yml b/eng/pipelines/libraries/stress/http.yml
index 049c979b5e32fd..3561011fa6292a 100644
--- a/eng/pipelines/libraries/stress/http.yml
+++ b/eng/pipelines/libraries/stress/http.yml
@@ -57,6 +57,7 @@ jobs:
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
+ timeoutInMinutes: 35 # In case the HTTP/3.0 run hangs, we timeout shortly after the expected 30 minute run
displayName: Run HttpStress - HTTP 3.0
condition: and(eq(variables['buildRuntime.succeeded'], 'true'), eq(variables['buildStress.succeeded'], 'true'))
@@ -149,4 +150,4 @@ jobs:
- powershell: |
Set-NetFirewallProfile -Profile Domain, Public, Private -Enabled True
name: enableFirewall
- displayName: Enable Firewall
+ displayName: Enable Firewall
\ No newline at end of file
diff --git a/eng/pipelines/runtime-manual.yml b/eng/pipelines/runtime-manual.yml
new file mode 100644
index 00000000000000..83f9e891dab573
--- /dev/null
+++ b/eng/pipelines/runtime-manual.yml
@@ -0,0 +1,505 @@
+trigger: none
+
+variables:
+ - template: /eng/pipelines/common/variables.yml
+
+jobs:
+
+#
+# iOS/tvOS interp - requires AOT Compilation and Interp flags
+# Build the whole product using Mono and run libraries tests
+#
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml
+ buildConfig: Release
+ runtimeFlavor: mono
+ platforms:
+ - iOSSimulator_x64
+ - tvOSSimulator_x64
+ - iOSSimulator_arm64
+ 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 /p:BuildDarwinFrameworks=true
+ timeoutInMinutes: 180
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ interpreter: true
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+
+#
+# MacCatalyst interp - requires AOT Compilation and Interp flags
+# Build the whole product using Mono and run libraries tests
+#
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml
+ buildConfig: Release
+ runtimeFlavor: mono
+ platforms:
+ - MacCatalyst_x64
+ # don't run tests on arm64 PRs until we can get significantly more devices
+ - ${{ if eq(variables['isFullMatrix'], true) }}:
+ - MacCatalyst_arm64
+ jobParameters:
+ testGroup: innerloop
+ nameSuffix: AllSubsets_Mono
+ buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:DevTeamProvisioning=adhoc /p:RunAOTCompilation=true /p:MonoForceInterpreter=true /p:BuildDarwinFrameworks=true
+ timeoutInMinutes: 180
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ interpreter: true
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+
+#
+# iOS/tvOS devices - Full AOT + AggressiveTrimming to reduce size
+# Build the whole product using Mono and run libraries tests
+#
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml
+ buildConfig: Release
+ runtimeFlavor: mono
+ platforms:
+ - iOS_arm64
+ - tvOS_arm64
+ jobParameters:
+ testGroup: innerloop
+ nameSuffix: AllSubsets_Mono
+ buildArgs: -s mono+libs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:DevTeamProvisioning=- /p:RunAOTCompilation=true /p:BuildTestsOnHelix=true /p:UsePortableRuntimePack=true /p:BuildDarwinFrameworks=true
+ timeoutInMinutes: 180
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+ extraHelixArguments: /p:NeedsToBuildAppsOnHelix=true
+
+#
+# Build the whole product using Mono and run libraries tests
+#
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml
+ buildConfig: Release
+ runtimeFlavor: mono
+ platforms:
+ - Android_x86
+ - Android_x64
+ jobParameters:
+ testGroup: innerloop
+ nameSuffix: AllSubsets_Mono
+ buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true $(_runSmokeTestsOnlyArg)
+ timeoutInMinutes: 180
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml
+ buildConfig: Release
+ runtimeFlavor: mono
+ platforms:
+ - Android_arm
+ - Android_arm64
+ jobParameters:
+ testGroup: innerloop
+ nameSuffix: AllSubsets_Mono
+ buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true $(_runSmokeTestsOnlyArg)
+ timeoutInMinutes: 180
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+
+#
+# Build the whole product using Mono and run libraries tests
+#
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml
+ buildConfig: Release
+ runtimeFlavor: mono
+ platforms:
+ - Windows_x64
+ jobParameters:
+ testScope: innerloop
+ nameSuffix: AllSubsets_Mono
+ buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true
+ timeoutInMinutes: 120
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+
+#
+# Build the whole product using Mono for Android and run runtime tests with interpreter
+#
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
+ buildConfig: Release
+ runtimeFlavor: mono
+ platforms:
+ - Android_x64
+ variables:
+ - ${{ if and(eq(variables['System.TeamProject'], 'public'), eq(variables['Build.Reason'], 'PullRequest')) }}:
+ - name: _HelixSource
+ value: pr/dotnet/runtime/$(Build.SourceBranch)
+ - ${{ if and(eq(variables['System.TeamProject'], 'public'), ne(variables['Build.Reason'], 'PullRequest')) }}:
+ - name: _HelixSource
+ value: ci/dotnet/runtime/$(Build.SourceBranch)
+ - name: timeoutPerTestInMinutes
+ value: 60
+ - name: timeoutPerTestCollectionInMinutes
+ value: 180
+ jobParameters:
+ testGroup: innerloop
+ nameSuffix: AllSubsets_Mono_RuntimeTests_Interp
+ buildArgs: -s mono+libs -c $(_BuildConfig)
+ timeoutInMinutes: 240
+ runtimeVariant: monointerpreter
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+
+#
+# Build the whole product using Mono and run runtime tests with the JIT.
+#
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
+ buildConfig: Release
+ runtimeFlavor: mono
+ platforms:
+ - iOSSimulator_x64
+ variables:
+ - ${{ if and(eq(variables['System.TeamProject'], 'public'), eq(variables['Build.Reason'], 'PullRequest')) }}:
+ - name: _HelixSource
+ value: pr/dotnet/runtime/$(Build.SourceBranch)
+ - ${{ if and(eq(variables['System.TeamProject'], 'public'), ne(variables['Build.Reason'], 'PullRequest')) }}:
+ - name: _HelixSource
+ value: ci/dotnet/runtime/$(Build.SourceBranch)
+ - name: timeoutPerTestInMinutes
+ value: 60
+ - name: timeoutPerTestCollectionInMinutes
+ value: 180
+ jobParameters:
+ testGroup: innerloop
+ nameSuffix: AllSubsets_Mono_RuntimeTests
+ buildArgs: -s mono+libs -c $(_BuildConfig)
+ timeoutInMinutes: 240
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+
+#
+# Build the whole product using Mono for Android and run runtime tests with Android devices
+#
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
+ buildConfig: Release
+ runtimeFlavor: mono
+ platforms:
+ - Android_arm64
+ variables:
+ - ${{ if and(eq(variables['System.TeamProject'], 'public'), eq(variables['Build.Reason'], 'PullRequest')) }}:
+ - name: _HelixSource
+ value: pr/dotnet/runtime/$(Build.SourceBranch)
+ - ${{ if and(eq(variables['System.TeamProject'], 'public'), ne(variables['Build.Reason'], 'PullRequest')) }}:
+ - name: _HelixSource
+ value: ci/dotnet/runtime/$(Build.SourceBranch)
+ - name: timeoutPerTestInMinutes
+ value: 60
+ - name: timeoutPerTestCollectionInMinutes
+ value: 180
+ jobParameters:
+ testGroup: innerloop
+ nameSuffix: AllSubsets_Mono_RuntimeTests
+ buildArgs: -s mono+libs -c $(_BuildConfig)
+ timeoutInMinutes: 240
+ # don't run tests on PRs until we can get significantly more devices
+ # Turn off the testing for now, until https://github.com/dotnet/xharness/issues/663 gets resolved
+ # ${{ if eq(variables['isFullMatrix'], true) }}:
+ # # extra steps, run tests
+ # extraStepsTemplate: /eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml
+ # extraStepsParameters:
+ # creator: dotnet-bot
+ # testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+
+#
+# Build Browser_wasm, on windows, run console and browser tests
+#
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml
+ buildConfig: release
+ runtimeFlavor: mono
+ platforms:
+ - Browser_wasm_win
+ variables:
+ # map dependencies variables to local variables
+ - name: librariesContainsChange
+ value: $[ dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'] ]
+ - name: monoContainsChange
+ value: $[ dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'] ]
+ jobParameters:
+ testGroup: innerloop
+ nameSuffix: Windows_wasm
+ buildArgs: -subset mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:BrowserHost=windows
+ timeoutInMinutes: 180
+ condition: >-
+ or(
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true),
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true),
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
+ eq(variables['isFullMatrix'], true))
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+ extraHelixArguments: /p:BrowserHost=windows
+ scenarios:
+ - normal
+ - wasmtestonbrowser
+ condition: >-
+ or(
+ eq(variables['librariesContainsChange'], true),
+ eq(variables['monoContainsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
+ eq(variables['isFullMatrix'], true))
+
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml
+ buildConfig: release
+ runtimeFlavor: mono
+ platforms:
+ - Browser_wasm_win
+ variables:
+ # map dependencies variables to local variables
+ - name: wasmbuildtestsContainsChange
+ value: $[ dependencies.evaluate_paths.outputs['SetPathVars_wasmbuildtests.containsChange'] ]
+ jobParameters:
+ testGroup: innerloop
+ nameSuffix: Windows_wasm_WBT
+ buildArgs: -subset mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:BrowserHost=windows /p:TestWasmBuildTests=true /p:TestAssemblies=false
+ timeoutInMinutes: 180
+ condition: >-
+ or(
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_wasmbuildtests.containsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
+ eq(variables['isFullMatrix'], true))
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+ extraHelixArguments: /p:BrowserHost=windows
+ scenarios:
+ - buildwasmapps
+ condition: >-
+ or(
+ eq(variables['wasmbuildtestsContainsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
+ eq(variables['isFullMatrix'], true))
+
+# Disabled due to https://github.com/dotnet/runtime/issues/61721
+#- template: /eng/pipelines/common/platform-matrix.yml
+ #parameters:
+ #jobTemplate: /eng/pipelines/common/global-build-job.yml
+ #helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml
+ #buildConfig: release
+ #runtimeFlavor: mono
+ #platforms:
+ #- Browser_wasm_win
+ #variables:
+ ## map dependencies variables to local variables
+ #- name: librariesContainsChange
+ #value: $[ dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'] ]
+ #- name: monoContainsChange
+ #value: $[ dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'] ]
+ #jobParameters:
+ #testGroup: innerloop
+ #nameSuffix: Windows_wasm_AOT
+ #buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:BrowserHost=windows /p:EnableAggressiveTrimming=true /p:BuildAOTTestsOnHelix=true /p:RunAOTCompilation=true $(_runSmokeTestsOnlyArg)
+ #timeoutInMinutes: 180
+ #condition: >-
+ #or(
+ #eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true),
+ #eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true),
+ #eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true),
+ #eq(variables['isManualOrIsNotPR'], true),
+ #eq(variables['isFullMatrix'], true))
+ ## extra steps, run tests
+ #extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ #extraStepsParameters:
+ #creator: dotnet-bot
+ #testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+ #extraHelixArguments: /p:BrowserHost=windows /p:NeedsToBuildWasmAppsOnHelix=true $(_runSmokeTestsOnlyArg)
+ #scenarios:
+ #- normal
+ #condition: >-
+ #or(
+ #eq(variables['librariesContainsChange'], true),
+ #eq(variables['monoContainsChange'], true),
+ #eq(variables['isManualOrIsNotPR'], true),
+ #eq(variables['isFullMatrix'], true))
+
+#
+# Build for Browser/wasm, with EnableAggressiveTrimming=true
+#
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml
+ buildConfig: Release
+ runtimeFlavor: mono
+ platforms:
+ - Browser_wasm
+ variables:
+ # map dependencies variables to local variables
+ - name: librariesContainsChange
+ value: $[ dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'] ]
+ - name: monoContainsChange
+ value: $[ dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'] ]
+ jobParameters:
+ testGroup: innerloop
+ nameSuffix: AllSubsets_Mono_EAT
+ buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:RunSmokeTestsOnly=false /p:EnableAggressiveTrimming=true /p:BuildAOTTestsOnHelix=true /p:RunAOTCompilation=false
+ timeoutInMinutes: 180
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+ extraHelixArguments: /p:NeedsToBuildWasmAppsOnHelix=true /p:RunSmokeTestsOnly=false
+ scenarios:
+ - normal
+
+#
+# Build for Browser/wasm with RunAOTCompilation=true
+#
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml
+ buildConfig: Release
+ runtimeFlavor: mono
+ platforms:
+ - Browser_wasm
+ variables:
+ # map dependencies variables to local variables
+ - name: librariesContainsChange
+ value: $[ dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'] ]
+ - name: monoContainsChange
+ value: $[ dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'] ]
+ jobParameters:
+ testGroup: innerloop
+ nameSuffix: AllSubsets_Mono_AOT
+ buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:RunSmokeTestsOnly=false /p:EnableAggressiveTrimming=true /p:BuildAOTTestsOnHelix=true /p:RunAOTCompilation=true
+ timeoutInMinutes: 180
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+ extraHelixArguments: /p:NeedsToBuildWasmAppsOnHelix=true /p:RunSmokeTestsOnly=false
+ scenarios:
+ - normal
+
+#
+# Build the whole product using Mono and run runtime tests
+#
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
+ buildConfig: Release
+ runtimeFlavor: mono
+ platforms:
+ - Browser_wasm
+ variables:
+ - ${{ if and(eq(variables['System.TeamProject'], 'public'), eq(variables['Build.Reason'], 'PullRequest')) }}:
+ - name: _HelixSource
+ value: pr/dotnet/runtime/$(Build.SourceBranch)
+ - ${{ if and(eq(variables['System.TeamProject'], 'public'), ne(variables['Build.Reason'], 'PullRequest')) }}:
+ - name: _HelixSource
+ value: ci/dotnet/runtime/$(Build.SourceBranch)
+ - name: timeoutPerTestInMinutes
+ value: 10
+ - name: timeoutPerTestCollectionInMinutes
+ value: 200
+ jobParameters:
+ testGroup: innerloop
+ nameSuffix: AllSubsets_Mono_RuntimeTests
+ buildArgs: -s mono+libs -c $(_BuildConfig)
+ timeoutInMinutes: 180
+ # NOTE: Per PR test execution is not recommended for mobile runtime tests
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/common/templates/runtimes/wasm-runtime-and-send-to-helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+
+#
+# Build the whole product using Mono for Android and run runtime tests with Android emulator
+#
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
+ buildConfig: Release
+ runtimeFlavor: mono
+ platforms:
+ - Android_x64
+ variables:
+ - ${{ if and(eq(variables['System.TeamProject'], 'public'), eq(variables['Build.Reason'], 'PullRequest')) }}:
+ - name: _HelixSource
+ value: pr/dotnet/runtime/$(Build.SourceBranch)
+ - ${{ if and(eq(variables['System.TeamProject'], 'public'), ne(variables['Build.Reason'], 'PullRequest')) }}:
+ - name: _HelixSource
+ value: ci/dotnet/runtime/$(Build.SourceBranch)
+ - name: timeoutPerTestInMinutes
+ value: 60
+ - name: timeoutPerTestCollectionInMinutes
+ value: 180
+ jobParameters:
+ testGroup: innerloop
+ nameSuffix: AllSubsets_Mono_RuntimeTests
+ buildArgs: -s mono+libs -c $(_BuildConfig)
+ timeoutInMinutes: 240
+ # NOTE: Per PR test execution is not recommended for mobile runtime tests
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
diff --git a/eng/pipelines/runtime-official.yml b/eng/pipelines/runtime-official.yml
index ac295a3994a136..34b0ae68c78e25 100644
--- a/eng/pipelines/runtime-official.yml
+++ b/eng/pipelines/runtime-official.yml
@@ -342,6 +342,7 @@ stages:
extraStepsTemplate: /eng/pipelines/common/upload-intermediate-artifacts-step.yml
extraStepsParameters:
name: SourceBuildPackages
+ timeoutInMinutes: 95
#
diff --git a/eng/pipelines/runtime-staging.yml b/eng/pipelines/runtime-staging.yml
index fb60bef5e7b6e8..a38deea1927a5c 100644
--- a/eng/pipelines/runtime-staging.yml
+++ b/eng/pipelines/runtime-staging.yml
@@ -75,7 +75,7 @@ jobs:
- iOSSimulator_x64
- tvOSSimulator_x64
# don't run tests on arm64 PRs until we can get significantly more devices
- - ${{ if eq(variables['isFullMatrix'], true) }}:
+ - ${{ if eq(variables['isManualOrIsNotPR'], true) }}:
- iOSSimulator_arm64
variables:
# map dependencies variables to local variables
@@ -93,6 +93,7 @@ jobs:
eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true),
eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true),
eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
eq(variables['isFullMatrix'], true))
# extra steps, run tests
extraStepsTemplate: /eng/pipelines/libraries/helix.yml
@@ -104,6 +105,7 @@ jobs:
or(
eq(variables['librariesContainsChange'], true),
eq(variables['monoContainsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
eq(variables['isFullMatrix'], true))
#
@@ -137,6 +139,7 @@ jobs:
eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true),
eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true),
eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
eq(variables['isFullMatrix'], true))
# extra steps, run tests
extraStepsTemplate: /eng/pipelines/libraries/helix.yml
@@ -148,6 +151,7 @@ jobs:
or(
eq(variables['librariesContainsChange'], true),
eq(variables['monoContainsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
eq(variables['isFullMatrix'], true))
#
@@ -179,6 +183,7 @@ jobs:
eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true),
eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true),
eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
eq(variables['isFullMatrix'], true))
# extra steps, run tests
extraStepsTemplate: /eng/pipelines/libraries/helix.yml
@@ -190,6 +195,7 @@ jobs:
or(
eq(variables['librariesContainsChange'], true),
eq(variables['monoContainsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
eq(variables['isFullMatrix'], true))
#
@@ -220,6 +226,7 @@ jobs:
eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true),
eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true),
eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
eq(variables['isFullMatrix'], true))
# extra steps, run tests
extraStepsTemplate: /eng/pipelines/libraries/helix.yml
@@ -230,6 +237,7 @@ jobs:
or(
eq(variables['librariesContainsChange'], true),
eq(variables['monoContainsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
eq(variables['isFullMatrix'], true))
- template: /eng/pipelines/common/platform-matrix.yml
@@ -257,19 +265,19 @@ jobs:
eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true),
eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true),
eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
+ eq(variables['isFullMatrix'], true))
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+ condition: >-
+ or(
+ eq(variables['librariesContainsChange'], true),
+ eq(variables['monoContainsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
eq(variables['isFullMatrix'], true))
- # Device tests are rolling build only
- ${{ if eq(variables['isFullMatrix'], true) }}:
- # extra steps, run tests
- extraStepsTemplate: /eng/pipelines/libraries/helix.yml
- extraStepsParameters:
- creator: dotnet-bot
- testRunNamePrefixSuffix: Mono_$(_BuildConfig)
- condition: >-
- or(
- eq(variables['librariesContainsChange'], true),
- eq(variables['monoContainsChange'], true),
- eq(variables['isFullMatrix'], true))
#
# Build the whole product using Mono and run libraries tests
@@ -298,6 +306,7 @@ jobs:
eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true),
eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true),
eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
eq(variables['isFullMatrix'], true))
# extra steps, run tests
extraStepsTemplate: /eng/pipelines/libraries/helix.yml
@@ -308,6 +317,7 @@ jobs:
or(
eq(variables['librariesContainsChange'], true),
eq(variables['monoContainsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
eq(variables['isFullMatrix'], true))
#
@@ -342,9 +352,10 @@ jobs:
or(
eq(dependencies.evaluate_paths.outputs['SetPathVars_runtimetests.containsChange'], true),
eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
eq(variables['isFullMatrix'], true))
# NOTE: Per PR test execution is not recommended for runtime tests
- ${{ if eq(variables['isFullMatrix'], true) }}:
+ ${{ if eq(variables['isManualOrIsNotPR'], true) }}:
# extra steps, run tests
extraStepsTemplate: /eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml
extraStepsParameters:
@@ -382,8 +393,9 @@ jobs:
or(
eq(dependencies.evaluate_paths.outputs['SetPathVars_runtimetests.containsChange'], true),
eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
eq(variables['isFullMatrix'], true))
- ${{ if eq(variables['isFullMatrix'], true) }}:
+ ${{ if eq(variables['isManualOrIsNotPR'], true) }}:
# extra steps, run tests
extraStepsTemplate: /eng/pipelines/common/templates/runtimes/android-runtime-and-send-to-helix.yml
extraStepsParameters:
@@ -421,6 +433,7 @@ jobs:
or(
eq(dependencies.evaluate_paths.outputs['SetPathVars_runtimetests.containsChange'], true),
eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
eq(variables['isFullMatrix'], true))
# don't run tests on PRs until we can get significantly more devices
# Turn off the testing for now, until https://github.com/dotnet/xharness/issues/663 gets resolved
@@ -450,30 +463,116 @@ jobs:
value: $[ dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'] ]
jobParameters:
testGroup: innerloop
- nameSuffix: Browser_wasm_Windows
- buildArgs: -subset mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:BrowserHost=windows
+ nameSuffix: Windows_wasm
+ buildArgs: -subset mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:BrowserHost=windows $(_runSmokeTestsOnlyArg)
timeoutInMinutes: 180
condition: >-
or(
eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true),
eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true),
eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
+ eq(variables['isFullMatrix'], true))
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+ extraHelixArguments: /p:BrowserHost=windows $(_runSmokeTestsOnlyArg)
+ scenarios:
+ - normal
+ - wasmtestonbrowser
+ condition: >-
+ or(
+ eq(variables['librariesContainsChange'], true),
+ eq(variables['monoContainsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
+ eq(variables['isFullMatrix'], true))
+
+#
+# Build Browser_wasm, on windows, and run tests with AOT
+#
+# Disabled due to https://github.com/dotnet/runtime/issues/61721
+#- template: /eng/pipelines/common/platform-matrix.yml
+ #parameters:
+ #jobTemplate: /eng/pipelines/common/global-build-job.yml
+ #helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml
+ #buildConfig: release
+ #runtimeFlavor: mono
+ #platforms:
+ #- Browser_wasm_win
+ #variables:
+ ## map dependencies variables to local variables
+ #- name: librariesContainsChange
+ #value: $[ dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'] ]
+ #- name: monoContainsChange
+ #value: $[ dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'] ]
+ #jobParameters:
+ #testGroup: innerloop
+ #nameSuffix: Windows_wasm_AOT
+ #buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:BrowserHost=windows /p:EnableAggressiveTrimming=true /p:BuildAOTTestsOnHelix=true /p:RunAOTCompilation=true $(_runSmokeTestsOnlyArg)
+ #timeoutInMinutes: 180
+ #condition: >-
+ #or(
+ #eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true),
+ #eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true),
+ #eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true),
+ #eq(variables['isManualOrIsNotPR'], true),
+ #eq(variables['isFullMatrix'], true))
+ ## extra steps, run tests
+ #extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ #extraStepsParameters:
+ #creator: dotnet-bot
+ #testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+ #extraHelixArguments: /p:BrowserHost=windows /p:NeedsToBuildWasmAppsOnHelix=true $(_runSmokeTestsOnlyArg)
+ #scenarios:
+ #- normal
+ #condition: >-
+ #or(
+ #eq(variables['librariesContainsChange'], true),
+ #eq(variables['monoContainsChange'], true),
+ #eq(variables['isManualOrIsNotPR'], true),
+ #eq(variables['isFullMatrix'], true))
+
+
+#
+# Build Browser_wasm, on windows, and run Wasm.Build.Tests
+#
+- template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml
+ buildConfig: release
+ runtimeFlavor: mono
+ platforms:
+ - Browser_wasm_win
+ variables:
+ # map dependencies variables to local variables
+ - name: wasmbuildtestsContainsChange
+ value: $[ dependencies.evaluate_paths.outputs['SetPathVars_wasmbuildtests.containsChange'] ]
+ jobParameters:
+ testGroup: innerloop
+ nameSuffix: Windows_wasm_WBT
+ buildArgs: -subset mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:BrowserHost=windows /p:TestWasmBuildTests=true /p:TestAssemblies=false
+ timeoutInMinutes: 180
+ condition: >-
+ or(
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_wasmbuildtests.containsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
+ eq(variables['isFullMatrix'], true))
+ # extra steps, run tests
+ extraStepsTemplate: /eng/pipelines/libraries/helix.yml
+ extraStepsParameters:
+ creator: dotnet-bot
+ testRunNamePrefixSuffix: Mono_$(_BuildConfig)
+ extraHelixArguments: /p:BrowserHost=windows
+ scenarios:
+ - buildwasmapps
+ condition: >-
+ or(
+ eq(variables['wasmbuildtestsContainsChange'], true),
+ eq(variables['isManualOrIsNotPR'], true),
eq(variables['isFullMatrix'], true))
- ${{ if eq(variables['isFullMatrix'], true) }}:
- # extra steps, run tests
- extraStepsTemplate: /eng/pipelines/libraries/helix.yml
- extraStepsParameters:
- creator: dotnet-bot
- testRunNamePrefixSuffix: Mono_$(_BuildConfig)
- extraHelixArguments: /p:BrowserHost=windows
- scenarios:
- - normal
- - wasmtestonbrowser
- condition: >-
- or(
- eq(variables['librariesContainsChange'], true),
- eq(variables['monoContainsChange'], true),
- eq(variables['isFullMatrix'], true))
#
# CoreCLR Build for running Apple Silicon libraries-innerloop
diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml
index 77361bc2be2245..af8b9c116dd80c 100644
--- a/eng/pipelines/runtime.yml
+++ b/eng/pipelines/runtime.yml
@@ -94,8 +94,8 @@ jobs:
eq(variables['isFullMatrix'], true))
#
-# Build CoreCLR checked using GCC toolchain
-# Only when CoreCLR is changed
+# Build the whole product using GNU compiler toolchain
+# When CoreCLR, Mono, Libraries, Installer and src/tests are changed
#
- template: /eng/pipelines/common/platform-matrix.yml
parameters:
@@ -110,6 +110,9 @@ jobs:
condition: >-
or(
eq(dependencies.evaluate_paths.outputs['SetPathVars_coreclr.containsChange'], true),
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true),
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true),
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true),
eq(dependencies.evaluate_paths.outputs['SetPathVars_runtimetests.containsChange'], true),
eq(variables['isFullMatrix'], true))
@@ -190,7 +193,7 @@ jobs:
eq(variables['Build.SourceBranchName'], 'main'),
eq(variables['System.PullRequest.TargetBranch'], 'main')),
or(
- eq(dependencies.evaluate_paths.outputs['SetPathVars_coreclr.containsChange'], true),
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_coreclr_jit.containsChange'], true),
eq(variables['isFullMatrix'], true)))
# Build and test clr tools
@@ -325,15 +328,11 @@ jobs:
buildConfig: Release
runtimeFlavor: mono
platforms:
- # BuildWasmApps should only happen on the rolling build. No need to duplicate the build on PR's
- - ${{ if eq(variables['isFullMatrix'], true) }}:
- - Browser_wasm
+ - Browser_wasm
variables:
# map dependencies variables to local variables
- - name: monoContainsChange
- value: $[ dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'] ]
- - name: installerContainsChange
- value: $[ dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'] ]
+ - name: wasmbuildtestsContainsChange
+ value: $[ dependencies.evaluate_paths.outputs['SetPathVars_wasmbuildtests.containsChange'] ]
jobParameters:
testGroup: innerloop
nameSuffix: AllSubsets_Mono_WasmBuildTests
@@ -341,8 +340,7 @@ jobs:
timeoutInMinutes: 180
condition: >-
or(
- eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true),
- eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true),
+ eq(dependencies.evaluate_paths.outputs['SetPathVars_wasmbuildtests.containsChange'], true),
eq(variables['isFullMatrix'], true))
# extra steps, run tests
extraStepsTemplate: /eng/pipelines/libraries/helix.yml
@@ -353,8 +351,7 @@ jobs:
- buildwasmapps
condition: >-
or(
- eq(variables['monoContainsChange'], true),
- eq(variables['installerContainsChange'], true),
+ eq(variables['wasmbuildtestsContainsChange'], true),
eq(variables['isFullMatrix'], true))
#
@@ -936,7 +933,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: innerloop
- liveLibrariesBuildConfig: ${{ variables.debugOnPrReleaseOnRolling }}
condition: >-
or(
eq(dependencies.evaluate_paths.outputs['SetPathVars_coreclr.containsChange'], true),
@@ -1014,8 +1010,6 @@ jobs:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: innerloop
- liveLibrariesBuildConfig: ${{ variables.debugOnPrReleaseOnRolling }}
- liveRuntimeBuildConfig: release
condition: >-
or(
eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true),
@@ -1275,4 +1269,4 @@ jobs:
or(
eq(dependencies.evaluate_paths.outputs['SetPathVars_coreclr.containsChange'], true),
eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true),
- eq(variables['isFullMatrix'], true))
\ No newline at end of file
+ eq(variables['isFullMatrix'], true))
diff --git a/eng/pipelines/runtimelab.yml b/eng/pipelines/runtimelab.yml
index db73445d799874..9f1cd90822e832 100644
--- a/eng/pipelines/runtimelab.yml
+++ b/eng/pipelines/runtimelab.yml
@@ -142,7 +142,6 @@ stages:
- CoreClrTestBuildHost # Either OSX_x64 or Linux_x64
jobParameters:
testGroup: innerloop
- liveLibrariesBuildConfig: Release
dependsOn:
- build_Linux_x64_Checked_
- build_Linux_x64_Release_
diff --git a/eng/run-test.sh b/eng/run-test.sh
index e5cf6a3b21d1e0..64474cc435af16 100644
--- a/eng/run-test.sh
+++ b/eng/run-test.sh
@@ -236,26 +236,22 @@ done
# Compute paths to the binaries if they haven't already been computed
-if [ -z "$Runtime" ]
-then
+if [[ -z "$Runtime" ]]; then
Runtime="$ProjectRoot/artifacts/bin/testhost/netcoreapp-$OS-$Configuration-$__Arch"
fi
-if [ -z "$CoreFxTests" ]
-then
+if [[ -z "$CoreFxTests" ]]; then
CoreFxTests="$ProjectRoot/artifacts/bin"
fi
# Check parameters up front for valid values:
-if [ "$Configuration" != "Debug" ] && [ "$Configuration" != "Release" ]
-then
+if [[ "$Configuration" != "Debug" && "$Configuration" != "Release" ]]; then
echo "error: Configuration should be Debug or Release"
exit 1
fi
-if [ "$OS" != "FreeBSD" ] && [ "$OS" != "Linux" ] && [ "$OS" != "NetBSD" ] && [ "$OS" != "illumos" ] && [ "$OS" != "Solaris" ]
-then
+if [[ "$OS" != "FreeBSD" && "$OS" != "Linux" && "$OS" != "NetBSD" && "$OS" != "illumos" && "$OS" != "Solaris" ]]; then
echo "error: OS should be FreeBSD, Linux, NetBSD or Linux"
exit 1
fi
@@ -263,8 +259,7 @@ fi
export CORECLR_SERVER_GC="$serverGC"
export PAL_OUTPUTDEBUGSTRING="1"
-if [ -z "$LANG" ]
-then
+if [[ -z "$LANG" ]]; then
export LANG="en_US.UTF-8"
fi
@@ -281,29 +276,26 @@ ensure_binaries_are_present
TestsFailed=0
numberOfProcesses=0
-if [ $RunTestSequential -eq 1 ]
-then
+if [[ $RunTestSequential -eq 1 ]]; then
maxProcesses=1;
else
platform="$(uname)"
- if [ "$platform" = "FreeBSD" ]; then
+ if [[ "$platform" == "FreeBSD" ]]; then
maxProcesses=$(($(sysctl -n hw.ncpu)+1))
- if [ "$platform" = "NetBSD" ] || [ "$platform" = "SunOS" ] ; then
+ if [[ "$platform" == "NetBSD" || "$platform" == "SunOS" ]]; then
maxProcesses=$(($(getconf NPROCESSORS_ONLN)+1))
else
maxProcesses=$(($(getconf _NPROCESSORS_ONLN)+1))
fi
fi
-if [ -n "$TestDirFile" ] || [ -n "$TestDir" ]
-then
+if [[ -n "$TestDirFile" || -n "$TestDir" ]]; then
run_selected_tests
else
run_all_tests "$CoreFxTests/tests/"*.Tests
fi
-if [ "$TestsFailed" -gt 0 ]
-then
+if [[ "$TestsFailed" -gt 0 ]]; then
echo "$TestsFailed test(s) failed"
else
echo "All tests passed."
diff --git a/eng/testing/AndroidRunnerTemplate.sh b/eng/testing/AndroidRunnerTemplate.sh
index 3dab96f0bf3c43..d981d240c816f9 100644
--- a/eng/testing/AndroidRunnerTemplate.sh
+++ b/eng/testing/AndroidRunnerTemplate.sh
@@ -9,7 +9,7 @@ TARGET_OS=$3
TEST_NAME=$4
XHARNESS_OUT="$EXECUTION_DIR/xharness-output"
-if [ -n "$5" ]; then
+if [[ -n "$5" ]]; then
ADDITIONAL_ARGS=${@:5}
fi
@@ -27,7 +27,7 @@ while true; do
fi
done
-if [ ! -z "$XHARNESS_CLI_PATH" ]; then
+if [[ -n "$XHARNESS_CLI_PATH" ]]; then
# Allow overriding the path to the XHarness CLI DLL,
# we need to call it directly via dotnet exec
HARNESS_RUNNER="dotnet exec $XHARNESS_CLI_PATH"
diff --git a/eng/testing/AppleRunnerTemplate.sh b/eng/testing/AppleRunnerTemplate.sh
index 0ebff54c483934..8f66672f739e66 100644
--- a/eng/testing/AppleRunnerTemplate.sh
+++ b/eng/testing/AppleRunnerTemplate.sh
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/usr/bin/env bash
# NOTE: this script is only used locally, on CI we use the Helix SDK from arcade
@@ -54,7 +54,7 @@ while true; do
fi
done
-if [ ! -z "$XHARNESS_CLI_PATH" ]; then
+if [[ -n "$XHARNESS_CLI_PATH" ]]; then
# Allow overriding the path to the XHarness CLI DLL,
# we need to call it directly via dotnet exec
HARNESS_RUNNER="dotnet exec $XHARNESS_CLI_PATH"
diff --git a/eng/testing/RunnerTemplate.sh b/eng/testing/RunnerTemplate.sh
index 4770323fdc9c7d..bfaa743ef6f414 100644
--- a/eng/testing/RunnerTemplate.sh
+++ b/eng/testing/RunnerTemplate.sh
@@ -39,7 +39,7 @@ while [[ $# > 0 ]]; do
shift
done
-if [ "$RUNTIME_PATH" == "" ]; then
+if [[ -z "$RUNTIME_PATH" ]]; then
echo "error: -r|--runtime-path argument is required."
usage
exit -1
@@ -127,7 +127,7 @@ function copy_core_file_to_temp_location {
}
# ========================= BEGIN Core File Setup ============================
-if [ "$(uname -s)" == "Darwin" ]; then
+if [[ "$(uname -s)" == "Darwin" ]]; then
# On OS X, we will enable core dump generation only if there are no core
# files already in /cores/ at this point. This is being done to prevent
# inadvertently flooding the CI machines with dumps.
@@ -135,7 +135,7 @@ if [ "$(uname -s)" == "Darwin" ]; then
ulimit -c unlimited
fi
-elif [ "$(uname -s)" == "Linux" ]; then
+elif [[ "$(uname -s)" == "Linux" ]]; then
# On Linux, we'll enable core file generation unconditionally, and if a dump
# is generated, we will print some useful information from it and delete the
# dump immediately.
@@ -163,7 +163,7 @@ test_exitcode=$?
popd
echo ----- end $(date) ----- exit code $test_exitcode ----------------------------------------------------------
-if [ "${exitcode_list[$test_exitcode]}" != "" ]; then
+if [[ -n "${exitcode_list[$test_exitcode]}" ]]; then
echo exit code $test_exitcode means ${exitcode_list[$test_exitcode]}
fi
# ========================= END Test Execution ===============================
@@ -200,11 +200,11 @@ if [[ "$(uname -s)" == "Linux" && $test_exitcode -ne 0 ]]; then
# or "core." by default. We read /proc/sys/kernel/core_uses_pid to
# determine which it is.
core_name_uses_pid=0
- if [ -e /proc/sys/kernel/core_uses_pid ] && [ "1" == $(cat /proc/sys/kernel/core_uses_pid) ]; then
+ if [[ -e /proc/sys/kernel/core_uses_pid && "1" == $(cat /proc/sys/kernel/core_uses_pid) ]]; then
core_name_uses_pid=1
fi
- if [ $core_name_uses_pid == "1" ]; then
+ if [[ "$core_name_uses_pid" == "1" ]]; then
# We don't know what the PID of the process was, so let's look at all core
# files whose name matches core.NUMBER
echo Looking for files matching core.* ...
@@ -224,7 +224,7 @@ popd >/dev/null
# ======================== END Core File Inspection ==========================
# The helix work item should not exit with non-zero if tests ran and produced results
# The special console runner for runtime returns 1 when tests fail
-if [ "$test_exitcode" == "1" ]; then
+if [[ "$test_exitcode" == "1" ]]; then
if [ -n "$HELIX_WORKITEM_PAYLOAD" ]; then
exit 0
fi
diff --git a/eng/testing/WasmRunnerAOTTemplate.sh b/eng/testing/WasmRunnerAOTTemplate.sh
index 6922b550551b8b..4aa271b960a537 100644
--- a/eng/testing/WasmRunnerAOTTemplate.sh
+++ b/eng/testing/WasmRunnerAOTTemplate.sh
@@ -11,7 +11,7 @@ else
XHARNESS_OUT="$HELIX_WORKITEM_UPLOAD_ROOT/xharness-output"
fi
-if [ ! -z "$XHARNESS_CLI_PATH" ]; then
+if [[ -n "$XHARNESS_CLI_PATH" ]]; then
# When running in CI, we only have the .NET runtime available
# We need to call the XHarness CLI DLL directly via dotnet exec
HARNESS_RUNNER="dotnet exec $XHARNESS_CLI_PATH"
@@ -19,7 +19,7 @@ else
HARNESS_RUNNER="dotnet xharness"
fi
-if [ "$SCENARIO" == "WasmTestOnBrowser" ]; then
+if [[ "$SCENARIO" == "WasmTestOnBrowser" ]]; then
XHARNESS_COMMAND="test-browser"
elif [ -z "$XHARNESS_COMMAND" ]; then
XHARNESS_COMMAND="test"
@@ -38,13 +38,22 @@ function _buildAOTFunc()
dotnet msbuild $binLog -clp:PerformanceSummary -v:q -nologo
if [[ "$(uname -s)" == "Linux" && $buildExitCode -ne 0 ]]; then
echo "\nLast few messages from dmesg:\n"
- dmesg | tail -n 20
+ local lastLines=`dmesg | tail -n 20`
+ echo $lastLines
+
+ if [[ "$lastLines" =~ "oom-kill" ]]; then
+ return 9200 # OOM
+ fi
fi
echo
echo
- return $buildExitCode
+ if [[ $buildExitCode -ne 0 ]]; then
+ return 9100 # aot build failure
+ fi
+
+ return 0
}
# RunCommands defined in tests.mobile.targets
diff --git a/eng/testing/WasmRunnerTemplate.sh b/eng/testing/WasmRunnerTemplate.sh
index 8997e222177cab..412c33724aa158 100644
--- a/eng/testing/WasmRunnerTemplate.sh
+++ b/eng/testing/WasmRunnerTemplate.sh
@@ -5,13 +5,13 @@ SCENARIO=$3
cd $EXECUTION_DIR
-if [ -z "$HELIX_WORKITEM_UPLOAD_ROOT" ]; then
+if [[ -z "$HELIX_WORKITEM_UPLOAD_ROOT" ]]; then
XHARNESS_OUT="$EXECUTION_DIR/xharness-output"
else
XHARNESS_OUT="$HELIX_WORKITEM_UPLOAD_ROOT/xharness-output"
fi
-if [ ! -z "$XHARNESS_CLI_PATH" ]; then
+if [[ -n "$XHARNESS_CLI_PATH" ]]; then
# When running in CI, we only have the .NET runtime available
# We need to call the XHarness CLI DLL directly via dotnet exec
HARNESS_RUNNER="dotnet exec $XHARNESS_CLI_PATH"
@@ -19,9 +19,9 @@ else
HARNESS_RUNNER="dotnet xharness"
fi
-if [ "$SCENARIO" == "WasmTestOnBrowser" ]; then
+if [[ "$SCENARIO" == "WasmTestOnBrowser" ]]; then
XHARNESS_COMMAND="test-browser"
-elif [ -z "$XHARNESS_COMMAND" ]; then
+elif [[ -z "$XHARNESS_COMMAND" ]]; then
XHARNESS_COMMAND="test"
fi
diff --git a/eng/testing/performance/performance-setup.sh b/eng/testing/performance/performance-setup.sh
index cffd9ca8d6cdb8..b58a031bac3cda 100755
--- a/eng/testing/performance/performance-setup.sh
+++ b/eng/testing/performance/performance-setup.sh
@@ -162,7 +162,7 @@ while (($# > 0)); do
echo " --internal If the benchmarks are running as an official job."
echo " --monodotnet Pass the path to the mono dotnet for mono performance testing."
echo " --wasm Path to the unpacked wasm runtime pack."
- echo " --wasmaot Indicate wasm aot"
+ echo " --wasmaot Indicate wasm aot"
echo " --latestdotnet --dotnet-versions will not be specified. --dotnet-versions defaults to LKG version in global.json "
echo " --alpine Set for runs on Alpine"
echo ""
@@ -171,7 +171,7 @@ while (($# > 0)); do
esac
done
-if [ "$repository" == "dotnet/performance" ] || [ "$repository" == "dotnet-performance" ]; then
+if [[ "$repository" == "dotnet/performance" || "$repository" == "dotnet-performance" ]]; then
run_from_perf_repo=true
fi
@@ -202,38 +202,38 @@ if [[ "$internal" == true ]]; then
helix_source_prefix="official"
creator=
extra_benchmark_dotnet_arguments=
-
- if [[ "$architecture" = "arm64" ]]; then
+
+ if [[ "$architecture" == "arm64" ]]; then
queue=Ubuntu.1804.Arm64.Perf
else
- if [[ "$logical_machine" = "perfowl" ]]; then
+ if [[ "$logical_machine" == "perfowl" ]]; then
queue=Ubuntu.1804.Amd64.Owl.Perf
else
queue=Ubuntu.1804.Amd64.Tiger.Perf
fi
fi
- if [[ "$alpine" = "true" ]]; then
+ if [[ "$alpine" == "true" ]]; then
queue=alpine.amd64.tiger.perf
fi
else
- if [[ "$architecture" = "arm64" ]]; then
+ if [[ "$architecture" == "arm64" ]]; then
queue=ubuntu.1804.armarch.open
else
queue=Ubuntu.1804.Amd64.Open
fi
- if [[ "$alpine" = "true" ]]; then
+ if [[ "$alpine" == "true" ]]; then
queue=alpine.amd64.tiger.perf
fi
fi
-if [[ "$mono_dotnet" != "" ]] && [[ "$monointerpreter" == "false" ]]; then
+if [[ -n "$mono_dotnet" && "$monointerpreter" == "false" ]]; then
configurations="$configurations LLVM=$llvm MonoInterpreter=$monointerpreter MonoAOT=$monoaot"
extra_benchmark_dotnet_arguments="$extra_benchmark_dotnet_arguments --category-exclusion-filter NoMono"
fi
-if [[ "$wasm_runtime_loc" != "" ]]; then
+if [[ -n "$wasm_runtime_loc" ]]; then
if [[ "$wasmaot" == "true" ]]; then
configurations="CompilationMode=wasm AOT=true RunKind=$kind"
else
@@ -245,7 +245,7 @@ if [[ "$wasm_runtime_loc" != "" ]]; then
extra_benchmark_dotnet_arguments="$extra_benchmark_dotnet_arguments --category-exclusion-filter NoInterpreter NoWASM NoMono"
fi
-if [[ "$mono_dotnet" != "" ]] && [[ "$monointerpreter" == "true" ]]; then
+if [[ -n "$mono_dotnet" && "$monointerpreter" == "true" ]]; then
configurations="$configurations LLVM=$llvm MonoInterpreter=$monointerpreter MonoAOT=$monoaot"
extra_benchmark_dotnet_arguments="$extra_benchmark_dotnet_arguments --category-exclusion-filter NoInterpreter NoMono"
fi
@@ -262,7 +262,7 @@ fi
common_setup_arguments="--channel $cleaned_branch_name --queue $queue --build-number $build_number --build-configs $configurations --architecture $architecture"
setup_arguments="--repository https://github.com/$repository --branch $branch --get-perf-hash --commit-sha $commit_sha $common_setup_arguments"
-if [[ "$run_from_perf_repo" = true ]]; then
+if [[ "$run_from_perf_repo" == true ]]; then
payload_directory=
workitem_directory=$source_directory
performance_directory=$workitem_directory
@@ -276,7 +276,7 @@ else
mv $docs_directory $workitem_directory
fi
-if [[ "$wasm_runtime_loc" != "" ]]; then
+if [[ -n "$wasm_runtime_loc" ]]; then
using_wasm=true
wasm_dotnet_path=$payload_directory/dotnet-wasm
mv $wasm_runtime_loc $wasm_dotnet_path
@@ -291,13 +291,13 @@ if [[ "$wasm_runtime_loc" != "" ]]; then
rsync -a --progress $wasm_dotnet_path/artifacts/BrowserWasm/artifacts/* $wasm_dotnet_path/artifacts
rm -r $wasm_dotnet_path/artifacts/BrowserWasm/artifacts
if [[ "$wasmaot" == "true" ]]; then
- extra_benchmark_dotnet_arguments="$extra_benchmark_dotnet_arguments --wasmEngine /home/helixbot/.jsvu/$javascript_engine --runtimeSrcDir \$HELIX_CORRELATION_PAYLOAD/dotnet-wasm --aotcompilermode wasm --buildTimeout 3600"
+ extra_benchmark_dotnet_arguments="$extra_benchmark_dotnet_arguments --wasmEngine /home/helixbot/.jsvu/$javascript_engine --runtimeSrcDir \$HELIX_CORRELATION_PAYLOAD/dotnet-wasm --aotcompilermode wasm --buildTimeout 3600"
else
extra_benchmark_dotnet_arguments="$extra_benchmark_dotnet_arguments --wasmEngine /home/helixbot/.jsvu/$javascript_engine --runtimeSrcDir \$HELIX_CORRELATION_PAYLOAD/dotnet-wasm"
fi
fi
-if [[ "$mono_dotnet" != "" ]] && [[ "$monoaot" == "false" ]]; then
+if [[ -n "$mono_dotnet" && "$monoaot" == "false" ]]; then
using_mono=true
mono_dotnet_path=$payload_directory/dotnet-mono
mv $mono_dotnet $mono_dotnet_path
@@ -309,12 +309,12 @@ if [[ "$monoaot" == "true" ]]; then
extra_benchmark_dotnet_arguments="$extra_benchmark_dotnet_arguments --runtimes monoaotllvm --aotcompilerpath \$HELIX_CORRELATION_PAYLOAD/monoaot/sgen/mini/mono-sgen --customruntimepack \$HELIX_CORRELATION_PAYLOAD/monoaot/pack --aotcompilermode llvm"
fi
-if [[ "$use_core_run" = true ]]; then
+if [[ "$use_core_run" == true ]]; then
new_core_root=$payload_directory/Core_Root
mv $core_root_directory $new_core_root
fi
-if [[ "$use_baseline_core_run" = true ]]; then
+if [[ "$use_baseline_core_run" == true ]]; then
new_baseline_core_root=$payload_directory/Baseline_Core_Root
mv $baseline_core_root_directory $new_baseline_core_root
fi
diff --git a/eng/testing/runsettings.targets b/eng/testing/runsettings.targets
index b5ff64cc9ea26f..a02637840ac456 100644
--- a/eng/testing/runsettings.targets
+++ b/eng/testing/runsettings.targets
@@ -7,6 +7,7 @@
false
$(RunSettingsIntermediateOutputFilePath)
$(RunSettingsAppOutputFilePath)
+ $(ArtifactsObjDir)vscode/.runsettings
$(RunSettingsAppOutputFilePath)
@@ -46,6 +47,12 @@
WriteOnlyWhenDifferent="true"
Overwrite="true" />
+
+
$(RunSettingsOutputFilePath)
diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets
index 00aa7b19f1701f..0286959859cc1e 100644
--- a/eng/testing/tests.wasm.targets
+++ b/eng/testing/tests.wasm.targets
@@ -5,9 +5,14 @@
true
$(BundleTestAppTargets);BundleTestWasmApp
true
+ false
true
+
+
+ <_ShellCommandSeparator Condition="'$(OS)' == 'Windows_NT'">&&
+ <_ShellCommandSeparator Condition="'$(OS)' != 'Windows_NT'">&&
@@ -23,13 +28,13 @@
<_XHarnessArgs Condition="'$(OS)' != 'Windows_NT'">wasm $XHARNESS_COMMAND --app=. --output-directory=$XHARNESS_OUT
<_XHarnessArgs Condition="'$(OS)' == 'Windows_NT'">wasm %XHARNESS_COMMAND% --app=. --output-directory=%XHARNESS_OUT%
- <_XHarnessArgs Condition="'$(Scenario)' != 'WasmTestOnBrowser' and '$(Scenario)' != 'BuildWasmApps'">$(_XHarnessArgs) --engine=$(JSEngine) $(JSEngineArgs) --js-file=runtime.js
+ <_XHarnessArgs Condition="'$(Scenario)' != 'WasmTestOnBrowser' and '$(Scenario)' != 'BuildWasmApps'">$(_XHarnessArgs) --engine=$(JSEngine) $(JSEngineArgs) --js-file=main.js
<_XHarnessArgs Condition="'$(BrowserHost)' == 'windows'">$(_XHarnessArgs) --browser=chrome --browser-path=%HELIX_CORRELATION_PAYLOAD%\chrome-win\chrome.exe
<_XHarnessArgs Condition="'$(IsFunctionalTest)' == 'true'" >$(_XHarnessArgs) --expected-exit-code=$(ExpectedExitCode)
<_XHarnessArgs Condition="'$(WasmXHarnessArgs)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgs)
<_AppArgs Condition="'$(IsFunctionalTest)' != 'true' and '$(Scenario)' != 'BuildWasmApps'">--run WasmTestRunner.dll $(AssemblyName).dll
- <_AppArgs Condition="'$(IsFunctionalTest)' == 'true'">--run $(AssemblyName).dll --testing
+ <_AppArgs Condition="'$(IsFunctionalTest)' == 'true'">--run $(AssemblyName).dll
<_AppArgs Condition="'$(WasmTestAppArgs)' != ''">$(_AppArgs) $(WasmTestAppArgs)
@@ -38,16 +43,19 @@
- <_AOTBuildCommand>_buildAOTFunc publish/ProxyProjectForAOTOnHelix.proj $XHARNESS_OUT/AOTBuild.binlog
+ <_AOTBuildCommand Condition="'$(BrowserHost)' != 'windows'">_buildAOTFunc publish/ProxyProjectForAOTOnHelix.proj $XHARNESS_OUT/AOTBuild.binlog
+ <_AOTBuildCommand Condition="'$(BrowserHost)' == 'windows'">dotnet msbuild publish/ProxyProjectForAOTOnHelix.proj /bl:%XHARNESS_OUT%/AOTBuild.binlog
+
+ <_AOTBuildCommand Condition="'$(BrowserHost)' == 'windows'">$(_AOTBuildCommand) "/p:WasmCachePath=%USERPROFILE%\.emscripten-cache"
<_AOTBuildCommand Condition="'$(ContinuousIntegrationBuild)' != 'true'">$(_AOTBuildCommand) /p:RuntimeSrcDir=$(RepoRoot) /p:RuntimeConfig=$(Configuration)
<_AOTBuildCommand>$(_AOTBuildCommand) /p:RunAOTCompilation=$(RunAOTCompilation)
- <_AOTBuildCommand>$(_AOTBuildCommand) && cd wasm_build/AppBundle
+ <_AOTBuildCommand>$(_AOTBuildCommand) $(_ShellCommandSeparator) cd wasm_build/AppBundle
$(_AOTBuildCommand)
- $(_AOTBuildCommand) && $(RunScriptCommand)
+ $(_AOTBuildCommand) $(_ShellCommandSeparator) $(RunScriptCommand)
@@ -161,7 +169,7 @@
$(BundleDir)
WasmTestRunner.dll
- $(MonoProjectRoot)\wasm\runtime-test.js
+ $(MonoProjectRoot)\wasm\test-main.js
$(InvariantGlobalization)
true
false
diff --git a/eng/testing/workloads-testing.targets b/eng/testing/workloads-testing.targets
index 40f134fc8bb3d8..67a781d1058b67 100644
--- a/eng/testing/workloads-testing.targets
+++ b/eng/testing/workloads-testing.targets
@@ -89,9 +89,6 @@
WorkingDirectory="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App" />
- <_NuGetSourceForWorkloads Include="dotnet6" Value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json" />
- <_NuGetSourceForWorkloads Include="dotnet7" Value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet7/nuget/v3/index.json" />
-
<_BuiltNuGets Include="$(LibrariesShippingPackagesDir)\*.nupkg" />
@@ -111,7 +108,7 @@
WorkloadId="@(WorkloadIdForTesting)"
VersionBand="$(SdkBandVersion)"
LocalNuGetsPath="$(LibrariesShippingPackagesDir)"
- ExtraNuGetSources="@(_NuGetSourceForWorkloads)"
+ TemplateNuGetConfigPath="$(RepoRoot)NuGet.config"
SdkDir="$(SdkWithWorkloadForTestingPath)" />
@@ -124,7 +121,7 @@
WorkloadId="@(WorkloadIdForTesting)"
VersionBand="$(SdkBandVersion)"
LocalNuGetsPath="$(LibrariesShippingPackagesDir)"
- ExtraNuGetSources="@(_NuGetSourceForWorkloads)"
+ TemplateNuGetConfigPath="$(RepoRoot)NuGet.config"
SdkDir="$(SdkWithNoWorkloadForTestingPath)"
OnlyUpdateManifests="true"/>
diff --git a/global.json b/global.json
index c87af2701bf6f0..e13e46fe71ce80 100644
--- a/global.json
+++ b/global.json
@@ -1,23 +1,23 @@
{
"sdk": {
- "version": "6.0.100-rc.1.21430.12",
+ "version": "6.0.100",
"allowPrerelease": true,
"rollForward": "major"
},
"tools": {
- "dotnet": "6.0.100-rc.1.21430.12"
+ "dotnet": "6.0.100"
},
"native-tools": {
"cmake": "3.16.4",
"python3": "3.7.1"
},
"msbuild-sdks": {
- "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "7.0.0-beta.21529.1",
- "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.21529.1",
- "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.21529.1",
- "Microsoft.DotNet.SharedFramework.Sdk": "7.0.0-beta.21529.1",
+ "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "7.0.0-beta.21576.4",
+ "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.21576.4",
+ "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.21576.4",
+ "Microsoft.DotNet.SharedFramework.Sdk": "7.0.0-beta.21576.4",
"Microsoft.Build.NoTargets": "3.1.0",
"Microsoft.Build.Traversal": "3.0.23",
- "Microsoft.NET.Sdk.IL": "7.0.0-alpha.1.21551.1"
+ "Microsoft.NET.Sdk.IL": "7.0.0-alpha.1.21576.4"
}
}
diff --git a/src/coreclr/.nuget/Directory.Build.props b/src/coreclr/.nuget/Directory.Build.props
index ad18679d42d5bb..16c84169f75b3f 100644
--- a/src/coreclr/.nuget/Directory.Build.props
+++ b/src/coreclr/.nuget/Directory.Build.props
@@ -16,10 +16,6 @@
true
-
-
- 7.0.0
- $(PackageVersion)
diff --git a/src/coreclr/.nuget/Directory.Build.targets b/src/coreclr/.nuget/Directory.Build.targets
index 365d5ed7a9ca60..687a6f40253106 100644
--- a/src/coreclr/.nuget/Directory.Build.targets
+++ b/src/coreclr/.nuget/Directory.Build.targets
@@ -1,5 +1,13 @@
+
+
+
+ $(ProductVersion)
+ $(PackageVersion)
+
+
+
" : " ",
pRec->m_construct,
@@ -542,7 +538,7 @@ void CONTRACT_ASSERT(const char *szElaboration,
pRec->m_lineNum
);
- strcat_s(Buf, _countof(Buf), tmpbuf);
+ strcat_s(Buf, ARRAY_SIZE(Buf), tmpbuf);
}
pRec = pRec->m_pNext;
@@ -560,12 +556,12 @@ void CONTRACT_ASSERT(const char *szElaboration,
if (count == 0)
{
- strcat_s(Buf,_countof(Buf), "\n ...");
+ strcat_s(Buf,ARRAY_SIZE(Buf), "\n ...");
}
if (exceptionBuildingStack)
{
- strcat_s(Buf,_countof(Buf),
+ strcat_s(Buf,ARRAY_SIZE(Buf),
"\n"
"\nError forming contract stack. Any contract stack displayed above is correct,"
"\nbut it's most probably truncated. This is probably due to a CONTRACT in a"
@@ -579,17 +575,17 @@ void CONTRACT_ASSERT(const char *szElaboration,
);
}
- strcat_s(Buf,_countof(Buf), "\n\n");
+ strcat_s(Buf,ARRAY_SIZE(Buf), "\n\n");
if (!foundconflict && count != 0)
{
if (whichTest == BaseContract::THROWS_No)
{
- strcat_s(Buf,_countof(Buf), "You can't throw here because there is no handler on the stack.\n");
+ strcat_s(Buf,ARRAY_SIZE(Buf), "You can't throw here because there is no handler on the stack.\n");
}
else
{
- strcat_s(Buf,_countof(Buf), "We can't find the violated contract. Look for an old-style non-holder-based contract.\n");
+ strcat_s(Buf,ARRAY_SIZE(Buf), "We can't find the violated contract. Look for an old-style non-holder-based contract.\n");
}
}
diff --git a/src/coreclr/inc/cordebug.idl b/src/coreclr/inc/cordebug.idl
index 9620b2cf771280..97e53150b055e0 100644
--- a/src/coreclr/inc/cordebug.idl
+++ b/src/coreclr/inc/cordebug.idl
@@ -54,7 +54,7 @@ typedef DWORD CONNID;
cpp_quote("#ifndef _COR_IL_MAP")
cpp_quote("#define _COR_IL_MAP")
-// Note that this structure is also defined in CorProf.idl - PROPOGATE CHANGES
+// Note that this structure is also defined in CorProf.idl - PROPAGATE CHANGES
// BOTH WAYS, or this'll become a really insidious bug some day.
typedef struct _COR_IL_MAP
{
diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h
index 4de783aae98450..e2683d36ddcd79 100644
--- a/src/coreclr/inc/cordebuginfo.h
+++ b/src/coreclr/inc/cordebuginfo.h
@@ -52,7 +52,7 @@ class ICorDebugInfo
struct OffsetMapping
{
uint32_t nativeOffset;
- uint32_t ilOffset;
+ uint32_t ilOffset; // IL offset or one of the special values in MappingTypes
SourceTypes source; // The debugger needs this so that
// we don't put Edit and Continue breakpoints where
// the stack isn't empty. We can put regular breakpoints
diff --git a/src/coreclr/inc/corhdr.h b/src/coreclr/inc/corhdr.h
index 8b3eb42775c451..11dd7029884539 100644
--- a/src/coreclr/inc/corhdr.h
+++ b/src/coreclr/inc/corhdr.h
@@ -249,7 +249,7 @@ typedef struct IMAGE_COR20_HEADER
#else // !__IMAGE_COR20_HEADER_DEFINED__
// @TODO: This is required because we pull in the COM+ 2.0 PE header
-// definition from WinNT.h, and these constants have not yet propogated to there.
+// definition from WinNT.h, and these constants have not yet propagated to there.
//
#define COR_VTABLE_FROM_UNMANAGED_RETAIN_APPDOMAIN 0x08
#define COMIMAGE_FLAGS_32BITPREFERRED 0x00020000
diff --git a/src/coreclr/inc/corhlpr.h b/src/coreclr/inc/corhlpr.h
index 450514da95c180..427e8cdc0ff5c5 100644
--- a/src/coreclr/inc/corhlpr.h
+++ b/src/coreclr/inc/corhlpr.h
@@ -336,7 +336,7 @@ struct COR_ILMETHOD_SECT
const COR_ILMETHOD_SECT* Next() const
{
if (!More()) return(0);
- return ((COR_ILMETHOD_SECT*)(((BYTE *)this) + DataSize()))->Align();
+ return ((COR_ILMETHOD_SECT*)Align(((BYTE *)this) + DataSize()));
}
const BYTE* Data() const
@@ -374,9 +374,9 @@ struct COR_ILMETHOD_SECT
return((AsSmall()->Kind & CorILMethod_Sect_FatFormat) != 0);
}
- const COR_ILMETHOD_SECT* Align() const
+ static const void* Align(const void* p)
{
- return((COR_ILMETHOD_SECT*) ((((UINT_PTR) this) + 3) & ~3));
+ return((void*) ((((UINT_PTR) p) + 3) & ~3));
}
protected:
@@ -579,7 +579,7 @@ typedef struct tagCOR_ILMETHOD_FAT : IMAGE_COR_ILMETHOD_FAT
const COR_ILMETHOD_SECT* GetSect() const {
if (!More()) return (0);
- return(((COR_ILMETHOD_SECT*) (GetCode() + GetCodeSize()))->Align());
+ return(((COR_ILMETHOD_SECT*) COR_ILMETHOD_SECT::Align(GetCode() + GetCodeSize())));
}
} COR_ILMETHOD_FAT;
diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h
index b779d795a16b9a..4cc0c44b83c1df 100644
--- a/src/coreclr/inc/corinfo.h
+++ b/src/coreclr/inc/corinfo.h
@@ -472,7 +472,6 @@ enum CorInfoHelpFunc
CORINFO_HELP_GETFIELDADDR,
- CORINFO_HELP_GETSTATICFIELDADDR_CONTEXT, // Helper for context-static fields
CORINFO_HELP_GETSTATICFIELDADDR_TLS, // Helper for PE TLS fields
// There are a variety of specialized helpers for accessing static fields. The JIT should use
@@ -811,7 +810,7 @@ enum CorInfoFlag
CORINFO_FLG_CONTAINS_GC_PTR = 0x01000000, // does the class contain a gc ptr ?
CORINFO_FLG_DELEGATE = 0x02000000, // is this a subclass of delegate or multicast delegate ?
// CORINFO_FLG_UNUSED = 0x04000000,
- CORINFO_FLG_CONTAINS_STACK_PTR = 0x08000000, // This class has a stack pointer inside it
+ CORINFO_FLG_BYREF_LIKE = 0x08000000, // it is byref-like value type
CORINFO_FLG_VARIANCE = 0x10000000, // MethodTable::HasVariance (sealed does *not* mean uncast-able)
CORINFO_FLG_BEFOREFIELDINIT = 0x20000000, // Additional flexibility for when to run .cctor (see code:#ClassConstructionFlags)
CORINFO_FLG_GENERIC_TYPE_VARIABLE = 0x40000000, // This is really a handle for a variable type
@@ -886,7 +885,6 @@ enum CorInfoIntrinsics
CORINFO_INTRINSIC_Array_Get, // Get the value of an element in an array
CORINFO_INTRINSIC_Array_Address, // Get the address of an element in an array
CORINFO_INTRINSIC_Array_Set, // Set the value of an element in an array
- CORINFO_INTRINSIC_InitializeArray, // initialize an array from static data
CORINFO_INTRINSIC_RTH_GetValueInternal,
CORINFO_INTRINSIC_Object_GetType,
CORINFO_INTRINSIC_StubHelpers_GetStubContext,
@@ -2302,14 +2300,6 @@ class ICorStaticInfo
CORINFO_CLASS_HANDLE cls
) = 0;
- // Returns "TRUE" iff "cls" is a struct type such that return buffers used for returning a value
- // of this type must be stack-allocated. This will generally be true only if the struct
- // contains GC pointers, and does not exceed some size limit. Maintaining this as an invariant allows
- // an optimization: the JIT may assume that return buffer pointers for return types for which this predicate
- // returns TRUE are always stack allocated, and thus, that stores to the GC-pointer fields of such return
- // buffers do not require GC write barriers.
- virtual bool isStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE cls) = 0;
-
virtual CORINFO_MODULE_HANDLE getClassModule (
CORINFO_CLASS_HANDLE cls
) = 0;
diff --git a/src/coreclr/inc/corpriv.h b/src/coreclr/inc/corpriv.h
index 25102cefdd1cb5..d500b187493cd8 100644
--- a/src/coreclr/inc/corpriv.h
+++ b/src/coreclr/inc/corpriv.h
@@ -283,9 +283,6 @@ typedef enum CorOpenFlagsInternal
#endif
// %%Classes: ----------------------------------------------------------------
-#ifndef lengthof
-#define lengthof(rg) (sizeof(rg)/sizeof(rg[0]))
-#endif
#define COR_MODULE_CLASS ""
#define COR_WMODULE_CLASS W("")
diff --git a/src/coreclr/inc/crsttypes.h b/src/coreclr/inc/crsttypes.h
index 23070d7820d61d..d462cbd3c88e1c 100644
--- a/src/coreclr/inc/crsttypes.h
+++ b/src/coreclr/inc/crsttypes.h
@@ -1,7 +1,6 @@
//
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
//
#ifndef __CRST_TYPES_INCLUDED
@@ -11,7 +10,7 @@
// This file describes the range of Crst types available and their mapping to a numeric level (used by the
// runtime in debug mode to validate we're deadlock free). To modify these settings edit the
-// file:CrstTypes.def file and run the clr\bin\CrstTypeTool utility to generate a new version of this file.
+// file:CrstTypes.def file and run the clr\artifacts\CrstTypeTool utility to generate a new version of this file.
// Each Crst type is declared as a value in the following CrstType enum.
enum CrstType
@@ -59,85 +58,83 @@ enum CrstType
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
+ CrstIbcProfile = 43,
+ CrstIJWFixupData = 44,
+ CrstIJWHash = 45,
+ CrstILStubGen = 46,
+ CrstInlineTrackingMap = 47,
+ CrstInstMethodHashTable = 48,
+ CrstInterop = 49,
+ CrstInteropData = 50,
+ CrstIsJMCMethod = 51,
+ CrstISymUnmanagedReader = 52,
+ CrstJit = 53,
+ CrstJitGenericHandleCache = 54,
+ CrstJitInlineTrackingMap = 55,
+ CrstJitPatchpoint = 56,
+ CrstJitPerf = 57,
+ CrstJumpStubCache = 58,
+ CrstLeafLock = 59,
+ CrstListLock = 60,
+ CrstLoaderAllocator = 61,
+ CrstLoaderAllocatorReferences = 62,
+ CrstLoaderHeap = 63,
+ CrstManagedObjectWrapperMap = 64,
+ CrstMethodDescBackpatchInfoTracker = 65,
+ CrstModule = 66,
+ CrstModuleFixup = 67,
+ CrstModuleLookupTable = 68,
+ CrstMulticoreJitHash = 69,
+ CrstMulticoreJitManager = 70,
+ CrstNativeImageEagerFixups = 71,
+ CrstNativeImageLoad = 72,
+ CrstNls = 73,
+ CrstNotifyGdb = 74,
+ CrstObjectList = 75,
+ CrstPEImage = 76,
+ CrstPendingTypeLoadEntry = 77,
+ CrstPgoData = 78,
+ CrstPinnedByrefValidation = 79,
+ CrstProfilerGCRefDataFreeList = 80,
+ CrstProfilingAPIStatus = 81,
+ CrstRCWCache = 82,
+ CrstRCWCleanupList = 83,
+ CrstReadyToRunEntryPointToMethodDescMap = 84,
+ CrstReflection = 85,
+ CrstReJITGlobalRequest = 86,
+ CrstRetThunkCache = 87,
+ CrstSavedExceptionInfo = 88,
+ CrstSaveModuleProfileData = 89,
+ CrstSecurityStackwalkCache = 90,
+ CrstSigConvert = 91,
+ CrstSingleUseLock = 92,
+ CrstSpecialStatics = 93,
+ CrstStackSampler = 94,
+ CrstStressLog = 95,
+ CrstStubCache = 96,
+ CrstStubDispatchCache = 97,
+ CrstStubUnwindInfoHeapSegments = 98,
+ CrstSyncBlockCache = 99,
+ CrstSyncHashLock = 100,
+ CrstSystemBaseDomain = 101,
+ CrstSystemDomain = 102,
+ CrstSystemDomainDelayedUnloadList = 103,
+ CrstThreadIdDispenser = 104,
+ CrstThreadpoolTimerQueue = 105,
+ CrstThreadpoolWaitThreads = 106,
+ CrstThreadpoolWorker = 107,
+ CrstThreadStore = 108,
+ CrstTieredCompilation = 109,
+ CrstTypeEquivalenceMap = 110,
+ CrstTypeIDMap = 111,
+ CrstUMEntryThunkCache = 112,
+ CrstUMEntryThunkFreeListLock = 113,
+ CrstUniqueStack = 114,
+ CrstUnresolvedClassLock = 115,
+ CrstUnwindInfoTableLock = 116,
+ CrstVSDIndirectionCellLock = 117,
+ CrstWrapperTemplate = 118,
+ kNumberOfCrstTypes = 119
};
#endif // __CRST_TYPES_INCLUDED
@@ -191,8 +188,6 @@ int g_rgCrstLevelMap[] =
10, // CrstGCCover
13, // CrstGlobalStrLiteralMap
1, // CrstHandleTable
- 0, // CrstHostAssemblyMap
- 3, // CrstHostAssemblyMapAdd
0, // CrstIbcProfile
8, // CrstIJWFixupData
0, // CrstIJWHash
@@ -317,8 +312,6 @@ LPCSTR g_rgCrstNameMap[] =
"CrstGCCover",
"CrstGlobalStrLiteralMap",
"CrstHandleTable",
- "CrstHostAssemblyMap",
- "CrstHostAssemblyMapAdd",
"CrstIbcProfile",
"CrstIJWFixupData",
"CrstIJWHash",
diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h
index 4cb3e3f012d925..da724e75da6073 100644
--- a/src/coreclr/inc/icorjitinfoimpl_generated.h
+++ b/src/coreclr/inc/icorjitinfoimpl_generated.h
@@ -207,9 +207,6 @@ CorInfoInlineTypeCheck canInlineTypeCheck(
uint32_t getClassAttribs(
CORINFO_CLASS_HANDLE cls) override;
-bool isStructRequiringStackAllocRetBuf(
- CORINFO_CLASS_HANDLE cls) override;
-
CORINFO_MODULE_HANDLE getClassModule(
CORINFO_CLASS_HANDLE cls) override;
diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h
index ea71b2ad61e8dd..b51f56b2df5c78 100644
--- a/src/coreclr/inc/jiteeversionguid.h
+++ b/src/coreclr/inc/jiteeversionguid.h
@@ -43,11 +43,11 @@ typedef const GUID *LPCGUID;
#define GUID_DEFINED
#endif // !GUID_DEFINED
-constexpr GUID JITEEVersionIdentifier = { /* 3df3e3ec-b1f6-4e6d-8439-2e7f3f7fa2ac */
- 0x3df3e3ec,
- 0xb1f6,
- 0x4e6d,
- {0x84, 0x39, 0x2e, 0x7f, 0x3f, 0x7f, 0xa2, 0xac}
+constexpr GUID JITEEVersionIdentifier = { /* 0c6f2d8d-f1b7-4c28-bbe8-36c8f6b35fbf */
+ 0xc6f2d8d,
+ 0xf1b7,
+ 0x4c28,
+ { 0xbb, 0xe8, 0x36, 0xc8, 0xf6, 0xb3, 0x5f, 0xbf}
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h
index b15d770e672891..4d1fc11f0e8cb2 100644
--- a/src/coreclr/inc/jithelpers.h
+++ b/src/coreclr/inc/jithelpers.h
@@ -173,7 +173,6 @@
JITHELPER(CORINFO_HELP_GETFIELDADDR, JIT_GetFieldAddr,CORINFO_HELP_SIG_REG_ONLY)
- JITHELPER(CORINFO_HELP_GETSTATICFIELDADDR_CONTEXT, NULL, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB)
JITHELPER(CORINFO_HELP_GETSTATICFIELDADDR_TLS, NULL, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB)
JITHELPER(CORINFO_HELP_GETGENERICS_GCSTATIC_BASE, JIT_GetGenericsGCStaticBase,CORINFO_HELP_SIG_REG_ONLY)
diff --git a/src/coreclr/inc/metamodelpub.h b/src/coreclr/inc/metamodelpub.h
index 37e77e9123b101..b1eb8b961dbc5f 100644
--- a/src/coreclr/inc/metamodelpub.h
+++ b/src/coreclr/inc/metamodelpub.h
@@ -17,11 +17,6 @@
#include
#include "contract.h"
-
-#ifndef lengthof
-# define lengthof(x) (sizeof(x)/sizeof((x)[0]))
-#endif
-
template inline T Align4(T p)
{
LIMITED_METHOD_CONTRACT;
diff --git a/src/coreclr/inc/nsutilpriv.h b/src/coreclr/inc/nsutilpriv.h
index 519d0acc72f54e..108e8e61479b25 100644
--- a/src/coreclr/inc/nsutilpriv.h
+++ b/src/coreclr/inc/nsutilpriv.h
@@ -170,7 +170,7 @@ bool MakeAssemblyQualifiedName( // true if ok,
static
bool MakeAssemblyQualifiedName( // true ok, false truncation
- __out_ecount (dwBuffer) WCHAR* pBuffer, // Buffer to recieve the results
+ __out_ecount (dwBuffer) WCHAR* pBuffer, // Buffer to receive the results
int dwBuffer, // Number of characters total in buffer
const WCHAR *szTypeName, // Namespace for name.
int dwTypeName, // Number of characters (not including null)
diff --git a/src/coreclr/inc/patchpointinfo.h b/src/coreclr/inc/patchpointinfo.h
index 1445d8f79bce9c..d6bd2b6aee9a41 100644
--- a/src/coreclr/inc/patchpointinfo.h
+++ b/src/coreclr/inc/patchpointinfo.h
@@ -40,6 +40,7 @@ struct PatchpointInfo
m_genericContextArgOffset = -1;
m_keptAliveThisOffset = -1;
m_securityCookieOffset = -1;
+ m_monitorAcquiredOffset = -1;
}
// Total size of this patchpoint info record, in bytes
@@ -108,6 +109,22 @@ struct PatchpointInfo
m_securityCookieOffset = offset;
}
+ // Original method FP relative offset for monitor acquired flag
+ int MonitorAcquiredOffset() const
+ {
+ return m_monitorAcquiredOffset;
+ }
+
+ bool HasMonitorAcquired() const
+ {
+ return m_monitorAcquiredOffset != -1;
+ }
+
+ void SetMonitorAcquiredOffset(int offset)
+ {
+ m_monitorAcquiredOffset = offset;
+ }
+
// True if this local was address exposed in the original method
bool IsExposed(unsigned localNum) const
{
@@ -141,6 +158,7 @@ struct PatchpointInfo
int m_genericContextArgOffset;
int m_keptAliveThisOffset;
int m_securityCookieOffset;
+ int m_monitorAcquiredOffset;
int m_offsetAndExposureData[];
};
diff --git a/src/coreclr/inc/pgo_formatprocessing.h b/src/coreclr/inc/pgo_formatprocessing.h
index 8d111bf2c89491..18fe5601c0d283 100644
--- a/src/coreclr/inc/pgo_formatprocessing.h
+++ b/src/coreclr/inc/pgo_formatprocessing.h
@@ -361,6 +361,35 @@ bool ReadInstrumentationSchemaWithLayout(const uint8_t *pByte, size_t cbDataMax,
});
}
+
+// Return true if schemaTable entries are a subset of the schema described by pByte, with matching entries in the same order.
+// Also updates offset of the matching entries in schemaTable to those of the pByte schema.
+//
+inline bool CheckIfPgoSchemaIsCompatibleAndSetOffsets(const uint8_t *pByte, size_t cbDataMax, ICorJitInfo::PgoInstrumentationSchema* schemaTable, size_t cSchemas)
+{
+ size_t nMatched = 0;
+ size_t initialOffset = cbDataMax;
+
+ auto handler = [schemaTable, cSchemas, &nMatched](const ICorJitInfo::PgoInstrumentationSchema& schema)
+ {
+ if ((nMatched < cSchemas)
+ && (schema.InstrumentationKind == schemaTable[nMatched].InstrumentationKind)
+ && (schema.ILOffset == schemaTable[nMatched].ILOffset)
+ && (schema.Count == schemaTable[nMatched].Count)
+ && (schema.Other == schemaTable[nMatched].Other))
+ {
+ schemaTable[nMatched].Offset = schema.Offset;
+ nMatched++;
+ }
+
+ return true;
+ };
+
+ ReadInstrumentationSchemaWithLayout(pByte, cbDataMax, initialOffset, handler);
+
+ return (nMatched == cSchemas);
+}
+
inline bool ReadInstrumentationSchemaWithLayoutIntoSArray(const uint8_t *pByte, size_t cbDataMax, size_t initialOffset, SArray* pSchemas)
{
auto lambda = [pSchemas](const ICorJitInfo::PgoInstrumentationSchema &schema)
diff --git a/src/coreclr/inc/shash.h b/src/coreclr/inc/shash.h
index 163e2e8c4dfdb1..f2ef79e5c1738b 100644
--- a/src/coreclr/inc/shash.h
+++ b/src/coreclr/inc/shash.h
@@ -264,11 +264,6 @@ class EMPTY_BASES_DECL SHash : public TRAITS
// NoThrow version of CheckGrowth function. Returns FALSE on failure.
BOOL CheckGrowthNoThrow();
- // See if it is OK to grow the hash table by one element. If not, allocate new
- // hash table and return it together with its size *pcNewSize (used by code:AddPhases).
- // Returns NULL if there already is space for one element.
- element_t * CheckGrowth_OnlyAllocateNewTable(count_t * pcNewSize);
-
// Allocates new resized hash table for growth. Does not update the hash table on the object.
// The new size is computed based on the current population, growth factor, and maximum density factor.
element_t * Grow_OnlyAllocateNewTable(count_t * pcNewSize);
@@ -278,7 +273,7 @@ class EMPTY_BASES_DECL SHash : public TRAITS
// Utility function to allocate new table (does not copy the values into it yet). Returns the size of new table in
// *pcNewTableSize (finds next prime).
- // Phase 1 of code:Reallocate - it is split to support code:AddPhases.
+ // Phase 1 of code:Reallocate
element_t * AllocateNewTable(count_t requestedSize, count_t * pcNewTableSize);
// NoThrow version of AllocateNewTable utility function. Returns NULL on failure.
@@ -287,11 +282,11 @@ class EMPTY_BASES_DECL SHash : public TRAITS
// Utility function to replace old table with newly allocated table (as allocated by
// code:AllocateNewTable). Copies all 'old' values into the new table first.
// Returns the old table. Caller is expected to delete it (via code:DeleteOldTable).
- // Phase 2 of code:Reallocate - it is split to support code:AddPhases.
+ // Phase 2 of code:Reallocate
element_t * ReplaceTable(element_t * newTable, count_t newTableSize);
// Utility function to delete old table (as returned by code:ReplaceTable).
- // Phase 3 of code:Reallocate - it is split to support code:AddPhases.
+ // Phase 3 of code:Reallocate
void DeleteOldTable(element_t * oldTable);
// Utility function that does not call code:CheckGrowth.
@@ -502,92 +497,6 @@ class EMPTY_BASES_DECL SHash : public TRAITS
}
};
- // Wrapper and holder for adding an element to the hash table. Useful for Add operations that have to happen
- // under a rare lock that does not allow call out into host.
- // There are 3 phases:
- // 1. code:PreallocateForAdd ... Can allocate memory (calls into host).
- // 2. code:Add ... Adds one element (does NOT call into host).
- // or code:AddNothing_PublishPreallocatedTable ... Publishes the pre-allocated memory from step #1 (if any).
- // 3. code:DeleteOldTable (or destructor) ... Can delete the old memory (calls into host).
- // Example:
- // CrstHolder lockAdd(&crstLockForAdd); // Serialize all Add operations.
- // HostAssemblyMap::AddPhases addCall;
- // addCall.PreallocateForAdd(&shash); // 1. Allocates memory for one Add call (if needed). addCall serves as holder for the allocated memory.
- // {
- // // We cannot call out into host from ForbidSuspend region (i.e. no allocations/deallocations).
- // ForbidSuspendThreadHolder suspend; // Required by the 'special' read-lock
- // {
- // CrstHolder lock(&crstLock);
- // if (some_condition)
- // { // 2a. Add item. This may replace SHash inner table with the one pre-allocated in step 1.
- // addCall.Add(shashItem);
- // }
- // else
- // { // 2b. Skip adding item. This may replace SHash inner table with the one pre-allocated in step 1.
- // addCall.AddNothing_PublishPreallocatedTable();
- // }
- // }
- // }
- // addCall.DeleteOldTable(); // 3. Cleanup old table memory from shash (if it was replaced by pre-allocated table in step 2).
- // // Note: addCall destructor would take care of deleting the memory as well.
- class AddPhases
- {
- public:
- AddPhases();
- ~AddPhases();
-
- // Prepares object for one call to code:Add. Pre-allocates new table memory if needed, does not publish
- // the table yet (it is kept ready only in this holder for call to code:Add).
- // Calls out into host.
- void PreallocateForAdd(SHash * pHash);
-
- // Add an element to the hash table. This will never replace an element; multiple elements may be stored
- // with the same key.
- // Will use/publish pre-allocated memory from code:PreallocateForAdd.
- // Does not call out into host.
- // Only one Add* method can be called once per object! (Create a new object for each call)
- void Add(const element_t & element);
-
- // Element will not be added to the hash table.
- // Will use/publish pre-allocated memory from code:PreallocateForAdd.
- // Does not call out into host.
- // Only one Add* method can be called once per object! (Create a new object for each call)
- void AddNothing_PublishPreallocatedTable();
-
- // Deletes old table if it was replaced by call to code:Add or code:AddNothing_PublishPreallocatedTable.
- // Calls out into host.
- void DeleteOldTable();
-
- private:
- SHash * m_pHash;
- element_t * m_newTable;
- count_t m_newTableSize;
- element_t * m_oldTable;
-
- #ifdef _DEBUG
- PTR_element_t dbg_m_table;
- count_t dbg_m_tableSize;
- count_t dbg_m_tableCount;
- count_t dbg_m_tableOccupied;
- count_t dbg_m_tableMax;
- BOOL dbg_m_fAddCalled;
- #endif //_DEBUG
- }; // class SHash::AddPhases
-
- // Adds an entry to the hash table according to the guidelines above for
- // avoiding a callout to the host while the read lock is held.
- // Returns true if elem was added to the map, otherwise false.
- // When elem was not added (false is returned), and if ppStoredElem is non-null,
- // then it is set to point to the value in the map.
- template
- bool CheckAddInPhases(
- element_t const & elem,
- LockT & lock,
- AddLockT & addLock,
- IUnknown * addRefObject = nullptr);
private:
diff --git a/src/coreclr/inc/shash.inl b/src/coreclr/inc/shash.inl
index ef4e79ed4cf393..d5c2bd41d781e7 100644
--- a/src/coreclr/inc/shash.inl
+++ b/src/coreclr/inc/shash.inl
@@ -374,26 +374,6 @@ BOOL SHash::CheckGrowthNoThrow()
RETURN result;
}
-template
-typename SHash::element_t *
-SHash::CheckGrowth_OnlyAllocateNewTable(count_t * pcNewSize)
-{
- CONTRACT(element_t *)
- {
- THROWS;
- GC_NOTRIGGER;
- INSTANCE_CHECK;
- }
- CONTRACT_END;
-
- if (m_tableOccupied == m_tableMax)
- {
- RETURN Grow_OnlyAllocateNewTable(pcNewSize);
- }
-
- RETURN NULL;
-}
-
template
void SHash::Grow()
{
@@ -898,202 +878,6 @@ COUNT_T SHash::NextPrime(COUNT_T number)
ThrowOutOfMemory();
}
-template
-SHash::AddPhases::AddPhases()
-{
- LIMITED_METHOD_CONTRACT;
-
- m_pHash = NULL;
- m_newTable = NULL;
- m_newTableSize = 0;
- m_oldTable = NULL;
-
- INDEBUG(dbg_m_fAddCalled = FALSE;)
-}
-
-template
-SHash::AddPhases::~AddPhases()
-{
- LIMITED_METHOD_CONTRACT;
-
- if (m_newTable != NULL)
- { // The new table was not applied to the hash yet
- _ASSERTE((m_pHash != NULL) && (m_newTableSize != 0) && (m_oldTable == NULL));
-
- delete [] m_newTable;
- }
- DeleteOldTable();
-}
-
-template
-void SHash::AddPhases::PreallocateForAdd(SHash * pHash)
-{
- CONTRACT_VOID
- {
- THROWS;
- GC_NOTRIGGER;
- }
- CONTRACT_END;
-
- _ASSERTE((m_pHash == NULL) && (m_newTable == NULL) && (m_newTableSize == 0) && (m_oldTable == NULL));
-
- m_pHash = pHash;
- // May return NULL if the allocation was not needed
- m_newTable = m_pHash->CheckGrowth_OnlyAllocateNewTable(&m_newTableSize);
-
-#ifdef _DEBUG
- dbg_m_table = pHash->m_table;
- dbg_m_tableSize = pHash->m_tableSize;
- dbg_m_tableCount = pHash->m_tableCount;
- dbg_m_tableOccupied = pHash->m_tableOccupied;
- dbg_m_tableMax = pHash->m_tableMax;
-#endif //_DEBUG
-
- RETURN;
-}
-
-template
-void SHash::AddPhases::Add(const element_t & element)
-{
- CONTRACT_VOID
- {
- NOTHROW_UNLESS_TRAITS_THROWS;
- GC_NOTRIGGER;
- }
- CONTRACT_END;
-
- _ASSERTE((m_pHash != NULL) && (m_oldTable == NULL));
- // Add can be called only once on this object
- _ASSERTE(!dbg_m_fAddCalled);
-
- // Check that the hash table didn't change since call to code:PreallocateForAdd
- _ASSERTE(dbg_m_table == m_pHash->m_table);
- _ASSERTE(dbg_m_tableSize == m_pHash->m_tableSize);
- _ASSERTE(dbg_m_tableCount >= m_pHash->m_tableCount); // Remove operation might have removed elements
- _ASSERTE(dbg_m_tableOccupied == m_pHash->m_tableOccupied);
- _ASSERTE(dbg_m_tableMax == m_pHash->m_tableMax);
-
- if (m_newTable != NULL)
- { // We have pre-allocated table from code:PreallocateForAdd, use it.
- _ASSERTE(m_newTableSize != 0);
-
- // May return NULL if there was not table allocated yet
- m_oldTable = m_pHash->ReplaceTable(m_newTable, m_newTableSize);
-
- m_newTable = NULL;
- m_newTableSize = 0;
- }
- // We know that we have enough space, direcly add the element
- m_pHash->Add_GrowthChecked(element);
-
- INDEBUG(dbg_m_fAddCalled = TRUE;)
-
- RETURN;
-}
-
-template
-void SHash::AddPhases::AddNothing_PublishPreallocatedTable()
-{
- CONTRACT_VOID
- {
- NOTHROW_UNLESS_TRAITS_THROWS;
- GC_NOTRIGGER;
- }
- CONTRACT_END;
-
- _ASSERTE((m_pHash != NULL) && (m_oldTable == NULL));
- // Add can be called only once on this object
- _ASSERTE(!dbg_m_fAddCalled);
-
- // Check that the hash table didn't change since call to code:PreallocateForAdd
- _ASSERTE(dbg_m_table == m_pHash->m_table);
- _ASSERTE(dbg_m_tableSize == m_pHash->m_tableSize);
- _ASSERTE(dbg_m_tableCount >= m_pHash->m_tableCount); // Remove operation might have removed elements
- _ASSERTE(dbg_m_tableOccupied == m_pHash->m_tableOccupied);
- _ASSERTE(dbg_m_tableMax == m_pHash->m_tableMax);
-
- if (m_newTable != NULL)
- { // We have pre-allocated table from code:PreallocateForAdd, use it.
- _ASSERTE(m_newTableSize != 0);
-
- // May return NULL if there was not table allocated yet
- m_oldTable = m_pHash->ReplaceTable(m_newTable, m_newTableSize);
-
- m_newTable = NULL;
- m_newTableSize = 0;
- }
-
- INDEBUG(dbg_m_fAddCalled = TRUE;)
-
- RETURN;
-}
-
-template
-void SHash::AddPhases::DeleteOldTable()
-{
- LIMITED_METHOD_CONTRACT;
-
- if (m_oldTable != NULL)
- {
- _ASSERTE((m_pHash != NULL) && (m_newTable == NULL) && (m_newTableSize == 0));
-
- delete [] m_oldTable;
- m_oldTable = NULL;
- }
-}
-
-template
-template
-bool SHash::CheckAddInPhases(
- element_t const & elem,
- LockT & lock,
- AddLockT & addLock,
- IUnknown * addRefObject)
-{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- INSTANCE_CHECK;
- }
- CONTRACTL_END;
-
- AddLockHolderT hAddLock(&addLock);
- AddPhases addCall;
-
- // 1. Preallocate one element
- addCall.PreallocateForAdd(this);
- {
- // 2. Take the reader lock. Host callouts now forbidden.
- LockHolderT hLock(&lock);
-
- element_t const * pEntry = LookupPtr(TRAITS::GetKey(elem));
- if (pEntry != nullptr)
- {
- // 3a. Use the newly allocated table (if any) to avoid later redundant allocation.
- addCall.AddNothing_PublishPreallocatedTable();
- return false;
- }
- else
- {
- // 3b. Add the element to the hash table.
- addCall.Add(elem);
-
- if (addRefObject != nullptr)
- {
- clr::SafeAddRef(addRefObject);
- }
-
- return true;
- }
- }
-
- // 4. addCall's destructor will take care of any required cleanup.
-}
-
template
BOOL MapSHash::Lookup(KEY key, VALUE* pValue) const
{
diff --git a/src/coreclr/inc/sigparser.h b/src/coreclr/inc/sigparser.h
index 9152727aee4585..1e861c4cd3edf8 100644
--- a/src/coreclr/inc/sigparser.h
+++ b/src/coreclr/inc/sigparser.h
@@ -13,6 +13,7 @@
#include "corhdr.h"
#include "corinfo.h"
#include "corpriv.h"
+#include
//---------------------------------------------------------------------------------------
// These macros define how arguments are mapped to the stack in the managed calling convention.
@@ -27,7 +28,6 @@
#define STACK_GROWS_UP_ON_ARGS_WALK
#endif
-
//------------------------------------------------------------------------
// Encapsulates how compressed integers and typeref tokens are encoded into
// a bytestream.
@@ -793,7 +793,7 @@ class CorTypeInfo
}
CONTRACTL_END;
- if (type >= (CorElementType)_countof(info))
+ if (type >= (CorElementType)ARRAY_SIZE(info))
{
ThrowHR(COR_E_BADIMAGEFORMAT);
}
@@ -803,7 +803,7 @@ class CorTypeInfo
{
LIMITED_METHOD_DAC_CONTRACT;
- if (type >= (CorElementType)_countof(info))
+ if (type >= (CorElementType)ARRAY_SIZE(info))
{
return info[ELEMENT_TYPE_END];
}
@@ -830,7 +830,7 @@ class CorTypeInfo
{
LIMITED_METHOD_CONTRACT;
- for (int i = 0; i < (int)_countof(info); i++)
+ for (int i = 0; i < (int)ARRAY_SIZE(info); i++)
{
_ASSERTE(info[i].type == i);
}
diff --git a/src/coreclr/inc/sstring.h b/src/coreclr/inc/sstring.h
index 99a5219239578e..57db682e002707 100644
--- a/src/coreclr/inc/sstring.h
+++ b/src/coreclr/inc/sstring.h
@@ -22,7 +22,7 @@
// string.
//
// If you need a direct non-unicode representation, you will have to provide a fresh SString which can
-// recieve a conversion operation if necessary.
+// receive a conversion operation if necessary.
//
// The alternate encodings available are:
// 1. ASCII - string consisting entirely of ASCII (7 bit) characters. This is the only 1 byte encoding
diff --git a/src/coreclr/inc/stdmacros.h b/src/coreclr/inc/stdmacros.h
index f2417c7637545a..2d0a0576172aa5 100644
--- a/src/coreclr/inc/stdmacros.h
+++ b/src/coreclr/inc/stdmacros.h
@@ -331,12 +331,6 @@ inline ULONG RoundUpToPower2(ULONG x)
#define UNIQUE_LABEL(a) UNIQUE_LABEL_DEF_X(_unique_label_##a##_, __LINE__)
#endif
-
-#ifndef _countof
-#define _countof(_array) (sizeof(_array)/sizeof(_array[0]))
-#endif
-
-
// This is temporary. LKG should provide these macros and we should then
// remove STRUNCATE and _TRUNCATE from here.
diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h
index 77df9dfa94d2a9..bae570f3f43ce3 100644
--- a/src/coreclr/inc/utilcode.h
+++ b/src/coreclr/inc/utilcode.h
@@ -39,6 +39,8 @@
#include "contract.h"
#include "entrypoints.h"
+#include
+
#include "clrnt.h"
#include "random.h"
@@ -150,17 +152,7 @@ typedef LPSTR LPUTF8;
#endif
#include // for offsetof
-
-#ifndef NumItems
-// Number of elements in a fixed-size array
-#define NumItems(s) (sizeof(s) / sizeof(s[0]))
-#endif
-
-#ifndef StrLen
-// Number of characters in a string literal. Excludes terminating NULL.
-#define StrLen(str) (NumItems(str) - 1)
-#endif
-
+#include
#define IS_DIGIT(ch) (((ch) >= W('0')) && ((ch) <= W('9')))
#define DIGIT_TO_INT(ch) ((ch) - W('0'))
@@ -647,8 +639,8 @@ class CCulturedHInstance
{
if (id != UICULTUREID_DONTCARE)
{
- wcsncpy_s(m_LangId, NumItems(m_LangId), id, NumItems(m_LangId));
- m_LangId[NumItems(m_LangId)-1] = W('\0');
+ wcsncpy_s(m_LangId, ARRAY_SIZE(m_LangId), id, ARRAY_SIZE(m_LangId));
+ m_LangId[STRING_LENGTH(m_LangId)] = W('\0');
}
else
{
diff --git a/src/coreclr/interop/comwrappers.cpp b/src/coreclr/interop/comwrappers.cpp
index aad3d6fa235913..f07786e580c0c9 100644
--- a/src/coreclr/interop/comwrappers.cpp
+++ b/src/coreclr/interop/comwrappers.cpp
@@ -4,6 +4,7 @@
#include "comwrappers.hpp"
#include
#include
+#include
#ifdef _WIN32
#include // placement new
@@ -398,8 +399,8 @@ HRESULT ManagedObjectWrapper::Create(
curr.IID = IID_IReferenceTrackerTarget;
curr.Vtable = &ManagedObjectWrapper_IReferenceTrackerTargetImpl;
}
-
- _ASSERTE(runtimeDefinedCount <= (int) ARRAYSIZE(runtimeDefinedLocal));
+
+ _ASSERTE(runtimeDefinedCount <= (int) ARRAY_SIZE(runtimeDefinedLocal));
// Compute size for ManagedObjectWrapper instance.
const size_t totalRuntimeDefinedSize = runtimeDefinedCount * sizeof(ABI::ComInterfaceEntry);
@@ -438,7 +439,7 @@ HRESULT ManagedObjectWrapper::Create(
{ userDefined, userDefinedCount }
};
- ABI::ComInterfaceDispatch* dispSection = ABI::PopulateDispatchSection(wrapperMem, dispatchSectionOffset, ARRAYSIZE(AllEntries), AllEntries);
+ ABI::ComInterfaceDispatch* dispSection = ABI::PopulateDispatchSection(wrapperMem, dispatchSectionOffset, ARRAY_SIZE(AllEntries), AllEntries);
ManagedObjectWrapper* wrapper = new (wrapperMem) ManagedObjectWrapper
{
diff --git a/src/coreclr/interop/platform.h b/src/coreclr/interop/platform.h
index 351b5ebf6ec486..64bfd76d352a66 100644
--- a/src/coreclr/interop/platform.h
+++ b/src/coreclr/interop/platform.h
@@ -16,10 +16,6 @@
#include
#endif // _WIN32
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
-#endif // !ARRAYSIZE
-
#if defined(_WIN32) || defined(HOST_UNIX)
#include // COM interfaces
diff --git a/src/coreclr/jit/CMakeLists.txt b/src/coreclr/jit/CMakeLists.txt
index e468b3a59c7c75..c46cdd18a164ee 100644
--- a/src/coreclr/jit/CMakeLists.txt
+++ b/src/coreclr/jit/CMakeLists.txt
@@ -28,14 +28,19 @@ function(create_standalone_jit)
if(TARGETDETAILS_ARCH STREQUAL "x64")
set(JIT_ARCH_SOURCES ${JIT_AMD64_SOURCES})
+ set(JIT_ARCH_HEADERS ${JIT_AMD64_HEADERS})
elseif((TARGETDETAILS_ARCH STREQUAL "arm") OR (TARGETDETAILS_ARCH STREQUAL "armel"))
set(JIT_ARCH_SOURCES ${JIT_ARM_SOURCES})
+ set(JIT_ARCH_HEADERS ${JIT_ARM_HEADERS})
elseif(TARGETDETAILS_ARCH STREQUAL "x86")
set(JIT_ARCH_SOURCES ${JIT_I386_SOURCES})
+ set(JIT_ARCH_HEADERS ${JIT_I386_HEADERS})
elseif(TARGETDETAILS_ARCH STREQUAL "arm64")
set(JIT_ARCH_SOURCES ${JIT_ARM64_SOURCES})
+ set(JIT_ARCH_HEADERS ${JIT_ARM64_HEADERS})
elseif(TARGETDETAILS_ARCH STREQUAL "s390x")
set(JIT_ARCH_SOURCES ${JIT_S390X_SOURCES})
+ set(JIT_ARCH_HEADERS ${JIT_S390X_HEADERS})
else()
clr_unknown_arch()
endif()
@@ -80,6 +85,7 @@ set( JIT_SOURCES
codegenlinear.cpp
compiler.cpp
copyprop.cpp
+ debuginfo.cpp
disasm.cpp
earlyprop.cpp
ee_il_dll.cpp
@@ -149,144 +155,10 @@ set( JIT_SOURCES
valuenum.cpp
)
-# Add header files to Visual Studio vcxproj, not required for unixes
-# change has effect on editor experience and has no impact on build
if (CLR_CMAKE_TARGET_WIN32)
- set( JIT_HEADERS
- ../inc/corinfo.h
- ../inc/corjit.h
- ../inc/corjitflags.h
- ../inc/corjithost.h
- _typeinfo.h
- alloc.h
- arraystack.h
- bitset.h
- layout.h
- bitsetasshortlong.h
- bitsetasuint64.h
- bitsetasuint64inclass.h
- bitsetops.h
- bitvec.h
- block.h
- blockset.h
- codegen.h
- codegeninterface.h
- compiler.h
- compiler.hpp
- compilerbitsettraits.h
- compilerbitsettraits.hpp
- compmemkind.h
- compphases.h
- dataflow.h
- decomposelongs.h
- disasm.h
- emit.h
- emitdef.h
- emitfmts.h
- emitinl.h
- emitjmps.h
- emitpub.h
- error.h
- gentree.h
- gtlist.h
- gtstructs.h
- hashbv.h
- host.h
- hostallocator.h
- hwintrinsic.h
- ICorJitInfo_API_names.h
- ICorJitInfo_API_wrapper.hpp
- inline.h
- inlinepolicy.h
- instr.h
- instrs.h
- jit.h
- jitconfig.h
- jitconfigvalues.h
- jitee.h
- jiteh.h
- jitexpandarray.h
- jitgcinfo.h
- jithashtable.h
- jitpch.h
- jitstd.h
- jittelemetry.h
- lir.h
- loopcloning.h
- loopcloningopts.h
- lower.h
- lsra_reftypes.h
- lsra_stats.h
- lsra_score.h
- lsra.h
- namedintrinsiclist.h
- objectalloc.h
- opcode.h
- phase.h
- rangecheck.h
- rationalize.h
- regalloc.h
- register_arg_convention.h
- register.h
- reglist.h
- regset.h
- sideeffects.h
- simd.h
- simdashwintrinsic.h
- simdintrinsiclist.h
- sm.h
- smallhash.h
- smcommon.h
- smopenum.h
- ssabuilder.h
- ssaconfig.h
- ssarenamestate.h
- stacklevelsetter.h
- target.h
- targetx86.h
- targetamd64.h
- targetarm.h
- targetarm64.h
- tinyarray.h
- titypes.h
- treelifeupdater.h
- typelist.h
- unwind.h
- utils.h
- valuenum.h
- valuenumfuncs.h
- valuenumtype.h
- varset.h
- vartype.h
- vartypesdef.h
- )
-
# Append clrjit.natvis file
list (APPEND JIT_SOURCES
clrjit.natvis)
-
- if (CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_ARM)
- list (APPEND JIT_HEADERS
- emitarm.h
- emitarm64.h
- emitfmtsarm.h
- emitfmtsarm64.h
- hwintrinsic.h
- hwintrinsiclistarm64.h
- instrsarm.h
- instrsarm64.h
- registerarm.h
- registerarm64.h
- simdashwintrinsiclistarm64.h)
- elseif (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386)
- list (APPEND JIT_HEADERS
- emitfmtsxarch.h
- emitxarch.h
- hwintrinsiclistxarch.h
- hwintrinsic.h
- instrsxarch.h
- simdashwintrinsiclistxarch.h)
- endif ()
endif(CLR_CMAKE_TARGET_WIN32)
# Define all the architecture-specific source files
@@ -352,35 +224,184 @@ set( JIT_S390X_SOURCES
# Not supported as JIT target
)
+# We include the headers here for better experience in IDEs.
+set( JIT_HEADERS
+ ../inc/corinfo.h
+ ../inc/corjit.h
+ ../inc/corjitflags.h
+ ../inc/corjithost.h
+ _typeinfo.h
+ alloc.h
+ arraystack.h
+ bitset.h
+ layout.h
+ bitsetasshortlong.h
+ bitsetasuint64.h
+ bitsetasuint64inclass.h
+ bitsetops.h
+ bitvec.h
+ block.h
+ blockset.h
+ codegen.h
+ codegeninterface.h
+ compiler.h
+ compiler.hpp
+ compilerbitsettraits.h
+ compilerbitsettraits.hpp
+ compmemkind.h
+ compphases.h
+ dataflow.h
+ debuginfo.h
+ decomposelongs.h
+ disasm.h
+ emit.h
+ emitdef.h
+ emitfmts.h
+ emitinl.h
+ emitjmps.h
+ emitpub.h
+ error.h
+ gentree.h
+ gtlist.h
+ gtstructs.h
+ hashbv.h
+ host.h
+ hostallocator.h
+ hwintrinsic.h
+ ICorJitInfo_API_names.h
+ ICorJitInfo_API_wrapper.hpp
+ inline.h
+ inlinepolicy.h
+ instr.h
+ instrs.h
+ jit.h
+ jitconfig.h
+ jitconfigvalues.h
+ jitee.h
+ jiteh.h
+ jitexpandarray.h
+ jitgcinfo.h
+ jithashtable.h
+ jitpch.h
+ jitstd.h
+ jittelemetry.h
+ lir.h
+ loopcloning.h
+ loopcloningopts.h
+ lower.h
+ lsra_reftypes.h
+ lsra_stats.h
+ lsra_score.h
+ lsra.h
+ namedintrinsiclist.h
+ objectalloc.h
+ opcode.h
+ phase.h
+ rangecheck.h
+ rationalize.h
+ regalloc.h
+ register_arg_convention.h
+ register.h
+ regset.h
+ sideeffects.h
+ simd.h
+ simdashwintrinsic.h
+ simdintrinsiclist.h
+ sm.h
+ smallhash.h
+ smcommon.h
+ smopenum.h
+ ssabuilder.h
+ ssaconfig.h
+ ssarenamestate.h
+ stacklevelsetter.h
+ target.h
+ targetx86.h
+ targetamd64.h
+ targetarm.h
+ targetarm64.h
+ tinyarray.h
+ titypes.h
+ treelifeupdater.h
+ typelist.h
+ unwind.h
+ utils.h
+ valuenum.h
+ valuenumfuncs.h
+ valuenumtype.h
+ varset.h
+ vartype.h
+ vartypesdef.h
+)
+
+# Arch specific headers
+set( JIT_AMD64_HEADERS
+ emitfmtsxarch.h
+ emitxarch.h
+ hwintrinsiclistxarch.h
+ hwintrinsic.h
+ instrsxarch.h
+ simdashwintrinsiclistxarch.h
+)
+
+set( JIT_I386_HEADERS ${JIT_AMD64_HEADERS} )
+
+set( JIT_ARM64_HEADERS
+ emitarm64.h
+ emitfmtsarm64.h
+ hwintrinsiclistarm64.h
+ instrsarm64.h
+ registerarm64.h
+ simdashwintrinsiclistarm64.h
+)
+
+set( JIT_ARM_HEADERS
+ emitarm.h
+ emitfmtsarm.h
+ instrsarm.h
+ registerarm.h
+)
+
+set ( JIT_S390X_HEADERS
+ # Not supported as JIT target
+)
+
+convert_to_absolute_path(JIT_SOURCES ${JIT_SOURCES})
+convert_to_absolute_path(JIT_HEADERS ${JIT_HEADERS})
+convert_to_absolute_path(JIT_RESOURCES ${JIT_RESOURCES})
+
+# Also convert the per-architecture sources to absolute paths, if the subdirs want to use them.
+
+convert_to_absolute_path(JIT_AMD64_SOURCES ${JIT_AMD64_SOURCES})
+convert_to_absolute_path(JIT_AMD64_HEADERS ${JIT_AMD64_HEADERS})
+convert_to_absolute_path(JIT_ARM_SOURCES ${JIT_ARM_SOURCES})
+convert_to_absolute_path(JIT_ARM_HEADERS ${JIT_ARM_HEADERS})
+convert_to_absolute_path(JIT_I386_SOURCES ${JIT_I386_SOURCES})
+convert_to_absolute_path(JIT_I386_HEADERS ${JIT_I386_HEADERS})
+convert_to_absolute_path(JIT_ARM64_SOURCES ${JIT_ARM64_SOURCES})
+convert_to_absolute_path(JIT_ARM64_HEADERS ${JIT_ARM64_HEADERS})
+convert_to_absolute_path(JIT_S390X_SOURCES ${JIT_S390X_SOURCES})
+convert_to_absolute_path(JIT_S390X_HEADERS ${JIT_S390X_HEADERS})
+
if(CLR_CMAKE_TARGET_ARCH_AMD64)
set(JIT_ARCH_SOURCES ${JIT_AMD64_SOURCES})
+ set(JIT_ARCH_HEADERS ${JIT_AMD64_HEADERS})
elseif(CLR_CMAKE_TARGET_ARCH_ARM)
set(JIT_ARCH_SOURCES ${JIT_ARM_SOURCES})
+ set(JIT_ARCH_HEADERS ${JIT_ARM_HEADERS})
elseif(CLR_CMAKE_TARGET_ARCH_I386)
set(JIT_ARCH_SOURCES ${JIT_I386_SOURCES})
+ set(JIT_ARCH_HEADERS ${JIT_I386_HEADERS})
elseif(CLR_CMAKE_TARGET_ARCH_ARM64)
set(JIT_ARCH_SOURCES ${JIT_ARM64_SOURCES})
+ set(JIT_ARCH_HEADERS ${JIT_ARM64_HEADERS})
elseif(CLR_CMAKE_TARGET_ARCH_S390X)
set(JIT_ARCH_SOURCES ${JIT_S390X_SOURCES})
+ set(JIT_ARCH_HEADERS ${JIT_S390X_HEADERS})
else()
clr_unknown_arch()
endif()
-set(JIT_CORE_SOURCES
- ${JIT_SOURCES}
- ${JIT_HEADERS}
-)
-
-convert_to_absolute_path(JIT_CORE_SOURCES ${JIT_CORE_SOURCES})
-convert_to_absolute_path(JIT_ARCH_SOURCES ${JIT_ARCH_SOURCES})
-convert_to_absolute_path(JIT_RESOURCES ${JIT_RESOURCES})
-
-# Also convert the per-architecture sources to absolute paths, if the subdirs want to use them.
-
-convert_to_absolute_path(JIT_AMD64_SOURCES ${JIT_AMD64_SOURCES})
-convert_to_absolute_path(JIT_ARM_SOURCES ${JIT_ARM_SOURCES})
-convert_to_absolute_path(JIT_I386_SOURCES ${JIT_I386_SOURCES})
-convert_to_absolute_path(JIT_ARM64_SOURCES ${JIT_ARM64_SOURCES})
set(JIT_DLL_MAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/dllmain.cpp)
@@ -445,14 +466,29 @@ function(add_jit jitName)
set_source_files_properties(${JIT_EXPORTS_FILE} PROPERTIES GENERATED TRUE)
- add_library_clr(${jitName}
- SHARED
- ${JIT_CORE_SOURCES}
- ${JIT_RESOURCES}
- ${JIT_ARCH_SOURCES}
- ${JIT_DEF_FILE}
- ${JIT_DLL_MAIN_FILE}
- )
+ if (CLR_CMAKE_TARGET_WIN32)
+ # If generating for Visual Studio then include headers for a better
+ # IDE experience.
+ add_library_clr(${jitName}
+ SHARED
+ ${JIT_SOURCES}
+ ${JIT_ARCH_SOURCES}
+ ${JIT_HEADERS}
+ ${JIT_ARCH_HEADERS}
+ ${JIT_RESOURCES}
+ ${JIT_DEF_FILE}
+ ${JIT_DLL_MAIN_FILE}
+ )
+ else()
+ add_library_clr(${jitName}
+ SHARED
+ ${JIT_SOURCES}
+ ${JIT_ARCH_SOURCES}
+ ${JIT_RESOURCES}
+ ${JIT_DEF_FILE}
+ ${JIT_DLL_MAIN_FILE}
+ )
+ endif(CLR_CMAKE_TARGET_WIN32)
if(CLR_CMAKE_TARGET_WIN32)
target_compile_definitions(${jitName} PRIVATE FX_VER_INTERNALNAME_STR=${jitName}.dll)
diff --git a/src/coreclr/jit/ICorJitInfo_API_names.h b/src/coreclr/jit/ICorJitInfo_API_names.h
index 39e74fc1949f10..64e8db858b9440 100644
--- a/src/coreclr/jit/ICorJitInfo_API_names.h
+++ b/src/coreclr/jit/ICorJitInfo_API_names.h
@@ -49,7 +49,6 @@ DEF_CLR_API(appendClassName)
DEF_CLR_API(isValueClass)
DEF_CLR_API(canInlineTypeCheck)
DEF_CLR_API(getClassAttribs)
-DEF_CLR_API(isStructRequiringStackAllocRetBuf)
DEF_CLR_API(getClassModule)
DEF_CLR_API(getModuleAssembly)
DEF_CLR_API(getAssemblyName)
diff --git a/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp b/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp
index 0fade44a4fef39..9f51c43b765ee0 100644
--- a/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp
+++ b/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp
@@ -455,15 +455,6 @@ uint32_t WrapICorJitInfo::getClassAttribs(
return temp;
}
-bool WrapICorJitInfo::isStructRequiringStackAllocRetBuf(
- CORINFO_CLASS_HANDLE cls)
-{
- API_ENTER(isStructRequiringStackAllocRetBuf);
- bool temp = wrapHnd->isStructRequiringStackAllocRetBuf(cls);
- API_LEAVE(isStructRequiringStackAllocRetBuf);
- return temp;
-}
-
CORINFO_MODULE_HANDLE WrapICorJitInfo::getClassModule(
CORINFO_CLASS_HANDLE cls)
{
diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp
index 2c286ab4bcc850..1ae2176674a7d8 100644
--- a/src/coreclr/jit/assertionprop.cpp
+++ b/src/coreclr/jit/assertionprop.cpp
@@ -660,12 +660,11 @@ void Compiler::optAddCopies()
Statement* stmt;
unsigned copyLclNum = lvaGrabTemp(false DEBUGARG("optAddCopies"));
- // Because lvaGrabTemp may have reallocated the lvaTable, ensure varDsc
- // is still in sync with lvaTable[lclNum];
- varDsc = &lvaTable[lclNum];
+ // Because lvaGrabTemp may have reallocated the lvaTable, ensure varDsc is still in sync.
+ varDsc = lvaGetDesc(lclNum);
// Set lvType on the new Temp Lcl Var
- lvaTable[copyLclNum].lvType = typ;
+ lvaGetDesc(copyLclNum)->lvType = typ;
#ifdef DEBUG
if (verbose)
@@ -909,7 +908,7 @@ void Compiler::optAssertionInit(bool isLocalProp)
// Note this tracks at most only 256 assertions.
static const AssertionIndex countFunc[] = {64, 128, 256, 64};
static const unsigned lowerBound = 0;
- static const unsigned upperBound = _countof(countFunc) - 1;
+ static const unsigned upperBound = ArrLen(countFunc) - 1;
const unsigned codeSize = info.compILCodeSize / 512;
optMaxAssertionCount = countFunc[isLocalProp ? lowerBound : min(upperBound, codeSize)];
@@ -1103,9 +1102,7 @@ void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex asse
else
{
unsigned lclNum = curAssertion->op1.lcl.lclNum;
- assert(lclNum < lvaCount);
- LclVarDsc* varDsc = lvaTable + lclNum;
- op1Type = varDsc->lvType;
+ op1Type = lvaGetDesc(lclNum)->lvType;
}
if (op1Type == TYP_REF)
@@ -1258,8 +1255,7 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
optAssertionKind assertionKind,
bool helperCallArgs)
{
- assert((op1 != nullptr) && !op1->OperIs(GT_LIST));
- assert((op2 == nullptr) || !op2->OperIs(GT_LIST));
+ assert(op1 != nullptr);
assert(!helperCallArgs || (op2 != nullptr));
AssertionDsc assertion = {OAK_INVALID};
@@ -1316,9 +1312,8 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
goto DONE_ASSERTION; // Don't make an assertion
}
- unsigned lclNum = op1->AsLclVarCommon()->GetLclNum();
- noway_assert(lclNum < lvaCount);
- LclVarDsc* lclVar = &lvaTable[lclNum];
+ unsigned lclNum = op1->AsLclVarCommon()->GetLclNum();
+ LclVarDsc* lclVar = lvaGetDesc(lclNum);
ValueNum vn;
@@ -1395,9 +1390,8 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
//
else if (op1->gtOper == GT_LCL_VAR)
{
- unsigned lclNum = op1->AsLclVarCommon()->GetLclNum();
- noway_assert(lclNum < lvaCount);
- LclVarDsc* lclVar = &lvaTable[lclNum];
+ unsigned lclNum = op1->AsLclVarCommon()->GetLclNum();
+ LclVarDsc* lclVar = lvaGetDesc(lclNum);
// If the local variable has its address exposed then bail
if (lclVar->IsAddressExposed())
@@ -1560,9 +1554,8 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
goto DONE_ASSERTION; // Don't make an assertion
}
- unsigned lclNum2 = op2->AsLclVarCommon()->GetLclNum();
- noway_assert(lclNum2 < lvaCount);
- LclVarDsc* lclVar2 = &lvaTable[lclNum2];
+ unsigned lclNum2 = op2->AsLclVarCommon()->GetLclNum();
+ LclVarDsc* lclVar2 = lvaGetDesc(lclNum2);
// If the two locals are the same then bail
if (lclNum == lclNum2)
@@ -1633,7 +1626,6 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
if (op1->gtOper == GT_LCL_VAR)
{
unsigned lclNum = op1->AsLclVarCommon()->GetLclNum();
- noway_assert(lclNum < lvaCount);
// If the local variable is not in SSA then bail
if (!lvaInSsa(lclNum))
@@ -1657,7 +1649,7 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
assert((assertion.op1.lcl.ssaNum == SsaConfig::RESERVED_SSA_NUM) ||
(assertion.op1.vn ==
vnStore->VNConservativeNormalValue(
- lvaTable[lclNum].GetPerSsaData(assertion.op1.lcl.ssaNum)->m_vnPair)));
+ lvaGetDesc(lclNum)->GetPerSsaData(assertion.op1.lcl.ssaNum)->m_vnPair)));
ssize_t cnsValue = 0;
GenTreeFlags iconFlags = GTF_EMPTY;
@@ -1972,9 +1964,8 @@ void Compiler::optDebugCheckAssertion(AssertionDsc* assertion)
case O1K_LCLVAR:
case O1K_EXACT_TYPE:
case O1K_SUBTYPE:
- assert(assertion->op1.lcl.lclNum < lvaCount);
assert(optLocalAssertionProp ||
- lvaTable[assertion->op1.lcl.lclNum].lvPerSsaData.IsValidSsaNum(assertion->op1.lcl.ssaNum));
+ lvaGetDesc(assertion->op1.lcl.lclNum)->lvPerSsaData.IsValidSsaNum(assertion->op1.lcl.ssaNum));
break;
case O1K_ARR_BND:
// It would be good to check that bnd.vnIdx and bnd.vnLen are valid value numbers.
@@ -2007,7 +1998,7 @@ void Compiler::optDebugCheckAssertion(AssertionDsc* assertion)
assert(assertion->op2.u1.iconFlags != GTF_EMPTY);
break;
case O1K_LCLVAR:
- assert((lvaTable[assertion->op1.lcl.lclNum].lvType != TYP_REF) ||
+ assert((lvaGetDesc(assertion->op1.lcl.lclNum)->lvType != TYP_REF) ||
(assertion->op2.u1.iconVal == 0) || doesMethodHaveFrozenString());
break;
case O1K_VALUE_NUMBER:
@@ -5605,8 +5596,9 @@ Compiler::fgWalkResult Compiler::optVNConstantPropCurStmt(BasicBlock* block, Sta
break;
case GT_LCL_VAR:
+ case GT_LCL_FLD:
// Make sure the local variable is an R-value.
- if ((tree->gtFlags & (GTF_VAR_DEF | GTF_DONT_CSE)))
+ if ((tree->gtFlags & (GTF_VAR_USEASG | GTF_VAR_DEF | GTF_DONT_CSE)) != GTF_EMPTY)
{
return WALK_CONTINUE;
}
diff --git a/src/coreclr/jit/bitsetasshortlong.h b/src/coreclr/jit/bitsetasshortlong.h
index d343edeeda4cd2..365cf346a10ac2 100644
--- a/src/coreclr/jit/bitsetasshortlong.h
+++ b/src/coreclr/jit/bitsetasshortlong.h
@@ -345,7 +345,7 @@ class BitSetOps*BitSetType*/ BitSetShortLongRep,
{
if (IsShort(env))
{
- (size_t&)(int&) out = (size_t)out & ((size_t)gen | (size_t)in);
+ out = (BitSetShortLongRep)((size_t)out & ((size_t)gen | (size_t)in));
}
else
{
@@ -361,7 +361,7 @@ class BitSetOps*BitSetType*/ BitSetShortLongRep,
{
if (IsShort(env))
{
- (size_t&)(int&) in = (size_t)use | ((size_t)out & ~(size_t)def);
+ in = (BitSetShortLongRep)((size_t)use | ((size_t)out & ~(size_t)def));
}
else
{
diff --git a/src/coreclr/jit/block.cpp b/src/coreclr/jit/block.cpp
index 4104bd7d0cab30..4472d573b0c92f 100644
--- a/src/coreclr/jit/block.cpp
+++ b/src/coreclr/jit/block.cpp
@@ -720,8 +720,8 @@ const char* BasicBlock::dspToString(int blockNumPadding /* = 0 */)
static int nextBufferIndex = 0;
auto& buffer = buffers[nextBufferIndex];
- nextBufferIndex = (nextBufferIndex + 1) % _countof(buffers);
- _snprintf_s(buffer, _countof(buffer), _countof(buffer), FMT_BB "%*s [%04u]", bbNum, blockNumPadding, "", bbID);
+ nextBufferIndex = (nextBufferIndex + 1) % ArrLen(buffers);
+ _snprintf_s(buffer, ArrLen(buffer), ArrLen(buffer), FMT_BB "%*s [%04u]", bbNum, blockNumPadding, "", bbID);
return buffer;
}
@@ -774,7 +774,7 @@ bool BasicBlock::CloneBlockState(
for (Statement* const fromStmt : from->Statements())
{
- auto newExpr = compiler->gtCloneExpr(fromStmt->GetRootNode(), GTF_EMPTY, varNum, varVal);
+ GenTree* newExpr = compiler->gtCloneExpr(fromStmt->GetRootNode(), GTF_EMPTY, varNum, varVal);
if (!newExpr)
{
// gtCloneExpr doesn't handle all opcodes, so may fail to clone a statement.
@@ -782,7 +782,7 @@ bool BasicBlock::CloneBlockState(
// return `false` to the caller to indicate that cloning was unsuccessful.
return false;
}
- compiler->fgInsertStmtAtEnd(to, compiler->fgNewStmtFromTree(newExpr));
+ compiler->fgInsertStmtAtEnd(to, compiler->fgNewStmtFromTree(newExpr, fromStmt->GetDebugInfo()));
}
return true;
}
@@ -841,14 +841,6 @@ Statement* BasicBlock::lastStmt() const
return result;
}
-//------------------------------------------------------------------------
-// BasicBlock::firstNode: Returns the first node in the block.
-//
-GenTree* BasicBlock::firstNode() const
-{
- return IsLIR() ? GetFirstLIRNode() : Compiler::fgGetFirstNode(firstStmt()->GetRootNode());
-}
-
//------------------------------------------------------------------------
// BasicBlock::lastNode: Returns the last node in the block.
//
@@ -1662,3 +1654,22 @@ BBswtDesc::BBswtDesc(Compiler* comp, const BBswtDesc* other)
bbsDstTab[i] = other->bbsDstTab[i];
}
}
+
+//------------------------------------------------------------------------
+// unmarkLoopAlign: Unmarks the LOOP_ALIGN flag from the block and reduce the
+// loop alignment count.
+//
+// Arguments:
+// compiler - Compiler instance
+// reason - Reason to print in JITDUMP
+//
+void BasicBlock::unmarkLoopAlign(Compiler* compiler DEBUG_ARG(const char* reason))
+{
+ // Make sure we unmark and count just once.
+ if (isLoopAlign())
+ {
+ compiler->loopAlignCandidates--;
+ bbFlags &= ~BBF_LOOP_ALIGN;
+ JITDUMP("Unmarking LOOP_ALIGN from " FMT_BB ". Reason= %s.\n", bbNum, reason);
+ }
+}
diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h
index ec7918d4aac953..2a90e55ab8ddcf 100644
--- a/src/coreclr/jit/block.h
+++ b/src/coreclr/jit/block.h
@@ -552,6 +552,7 @@ enum BasicBlockFlags : unsigned __int64
BBF_PATCHPOINT = MAKE_BBFLAG(36), // Block is a patchpoint
BBF_HAS_CLASS_PROFILE = MAKE_BBFLAG(37), // BB contains a call needing a class profile
BBF_PARTIAL_COMPILATION_PATCHPOINT = MAKE_BBFLAG(38), // Block is a partial compilation patchpoint
+ BBF_HAS_ALIGN = MAKE_BBFLAG(39), // BB ends with 'align' instruction
// The following are sets of flags.
@@ -653,11 +654,19 @@ struct BasicBlock : private LIR::Range
{
return ((bbFlags & BBF_LOOP_HEAD) != 0);
}
+
bool isLoopAlign() const
{
return ((bbFlags & BBF_LOOP_ALIGN) != 0);
}
+ void unmarkLoopAlign(Compiler* comp DEBUG_ARG(const char* reason));
+
+ bool hasAlign() const
+ {
+ return ((bbFlags & BBF_HAS_ALIGN) != 0);
+ }
+
#ifdef DEBUG
void dspFlags(); // Print the flags
unsigned dspCheapPreds(); // Print the predecessors (bbCheapPreds)
@@ -1239,7 +1248,6 @@ struct BasicBlock : private LIR::Range
return StatementList(FirstNonPhiDef());
}
- GenTree* firstNode() const;
GenTree* lastNode() const;
bool endsWithJmpMethod(Compiler* comp) const;
diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h
index c6b789f86f3cb2..6161feb05ef33d 100644
--- a/src/coreclr/jit/codegen.h
+++ b/src/coreclr/jit/codegen.h
@@ -434,10 +434,10 @@ class CodeGen final : public CodeGenInterface
CORINFO_METHOD_HANDLE methHnd,
INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo)
void* addr
- X86_ARG(int argSize),
+ X86_ARG(int argSize),
emitAttr retSize
MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
- IL_OFFSETX ilOffset,
+ const DebugInfo& di,
regNumber base,
bool isJump);
// clang-format on
@@ -447,10 +447,10 @@ class CodeGen final : public CodeGenInterface
CORINFO_METHOD_HANDLE methHnd,
INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo)
GenTreeIndir* indir
- X86_ARG(int argSize),
+ X86_ARG(int argSize),
emitAttr retSize
MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
- IL_OFFSETX ilOffset,
+ const DebugInfo& di,
bool isJump);
// clang-format on
@@ -552,15 +552,21 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
*/
#ifdef DEBUG
- void genIPmappingDisp(unsigned mappingNum, Compiler::IPmappingDsc* ipMapping);
+ void genIPmappingDisp(unsigned mappingNum, IPmappingDsc* ipMapping);
void genIPmappingListDisp();
#endif // DEBUG
- void genIPmappingAdd(IL_OFFSETX offset, bool isLabel);
- void genIPmappingAddToFront(IL_OFFSETX offset);
+ void genIPmappingAdd(IPmappingDscKind kind, const DebugInfo& di, bool isLabel);
+ void genIPmappingAddToFront(IPmappingDscKind kind, const DebugInfo& di, bool isLabel);
void genIPmappingGen();
- void genEnsureCodeEmitted(IL_OFFSETX offsx);
+#ifdef DEBUG
+ void genDumpPreciseDebugInfo();
+ void genDumpPreciseDebugInfoInlineTree(FILE* file, InlineContext* context, bool* first);
+ void genAddPreciseIPMappingHere(const DebugInfo& di);
+#endif
+
+ void genEnsureCodeEmitted(const DebugInfo& di);
//-------------------------------------------------------------------------
// scope info for the variables
@@ -967,9 +973,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
regNumber targetReg);
void genSIMDIntrinsic32BitConvert(GenTreeSIMD* simdNode);
void genSIMDIntrinsic64BitConvert(GenTreeSIMD* simdNode);
- void genSIMDIntrinsicNarrow(GenTreeSIMD* simdNode);
void genSIMDExtractUpperHalf(GenTreeSIMD* simdNode, regNumber srcReg, regNumber tgtReg);
- void genSIMDIntrinsicWiden(GenTreeSIMD* simdNode);
void genSIMDIntrinsic(GenTreeSIMD* simdNode);
// TYP_SIMD12 (i.e Vector3 of size 12 bytes) is not a hardware supported size and requires
@@ -1126,9 +1130,9 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
void genConsumeRegs(GenTree* tree);
void genConsumeOperands(GenTreeOp* tree);
-#ifdef FEATURE_HW_INTRINSICS
- void genConsumeHWIntrinsicOperands(GenTreeHWIntrinsic* tree);
-#endif // FEATURE_HW_INTRINSICS
+#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+ void genConsumeMultiOpOperands(GenTreeMultiOp* tree);
+#endif
void genEmitGSCookieCheck(bool pushReg);
void genCodeForShift(GenTree* tree);
@@ -1248,6 +1252,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
void genCodeForJumpTrue(GenTreeOp* jtrue);
#ifdef TARGET_ARM64
void genCodeForJumpCompare(GenTreeOp* tree);
+ void genCodeForMadd(GenTreeOp* tree);
+ void genCodeForBfiz(GenTreeOp* tree);
#endif // TARGET_ARM64
#if defined(FEATURE_EH_FUNCLETS)
@@ -1296,8 +1302,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
{
return false;
}
- const LclVarDsc* varDsc = &compiler->lvaTable[tree->AsLclVarCommon()->GetLclNum()];
- return (varDsc->lvIsRegCandidate());
+ return compiler->lvaGetDesc(tree->AsLclVarCommon())->lvIsRegCandidate();
}
#ifdef FEATURE_PUT_STRUCT_ARG_STK
@@ -1470,7 +1475,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
static const GenConditionDesc& Get(GenCondition condition)
{
- assert(condition.GetCode() < _countof(map));
+ assert(condition.GetCode() < ArrLen(map));
const GenConditionDesc& desc = map[condition.GetCode()];
assert(desc.jumpKind1 != EJ_NONE);
assert((desc.oper == GT_NONE) || (desc.oper == GT_AND) || (desc.oper == GT_OR));
diff --git a/src/coreclr/jit/codegenarm.cpp b/src/coreclr/jit/codegenarm.cpp
index 23beb66352978d..9dd13cdc125332 100644
--- a/src/coreclr/jit/codegenarm.cpp
+++ b/src/coreclr/jit/codegenarm.cpp
@@ -313,8 +313,8 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode)
var_types targetType = treeNode->TypeGet();
emitter* emit = GetEmitter();
- assert(oper == GT_ADD || oper == GT_SUB || oper == GT_MUL || oper == GT_ADD_LO || oper == GT_ADD_HI ||
- oper == GT_SUB_LO || oper == GT_SUB_HI || oper == GT_OR || oper == GT_XOR || oper == GT_AND);
+ assert(treeNode->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_ADD_LO, GT_ADD_HI, GT_SUB_LO, GT_SUB_HI, GT_OR, GT_XOR, GT_AND,
+ GT_AND_NOT));
GenTree* op1 = treeNode->gtGetOp1();
GenTree* op2 = treeNode->gtGetOp2();
@@ -671,6 +671,9 @@ instruction CodeGen::genGetInsForOper(genTreeOps oper, var_types type)
case GT_AND:
ins = INS_AND;
break;
+ case GT_AND_NOT:
+ ins = INS_bic;
+ break;
case GT_MUL:
ins = INS_MUL;
break;
@@ -963,7 +966,7 @@ void CodeGen::genCodeForLclVar(GenTreeLclVar* tree)
// lcl_vars are not defs
assert((tree->gtFlags & GTF_VAR_DEF) == 0);
- bool isRegCandidate = compiler->lvaTable[tree->GetLclNum()].lvIsRegCandidate();
+ bool isRegCandidate = compiler->lvaGetDesc(tree)->lvIsRegCandidate();
// If this is a register candidate that has been spilled, genConsumeReg() will
// reload it at the point of use. Otherwise, if it's not in a register, we load it here.
@@ -998,9 +1001,8 @@ void CodeGen::genCodeForStoreLclFld(GenTreeLclFld* tree)
// We must have a stack store with GT_STORE_LCL_FLD
noway_assert(targetReg == REG_NA);
- unsigned varNum = tree->GetLclNum();
- assert(varNum < compiler->lvaCount);
- LclVarDsc* varDsc = &(compiler->lvaTable[varNum]);
+ unsigned varNum = tree->GetLclNum();
+ LclVarDsc* varDsc = compiler->lvaGetDesc(varNum);
// Ensure that lclVar nodes are typed correctly.
assert(!varDsc->lvNormalizeOnStore() || targetType == genActualType(varDsc->TypeGet()));
@@ -1080,8 +1082,7 @@ void CodeGen::genCodeForStoreLclVar(GenTreeLclVar* tree)
}
if (regCount == 1)
{
- unsigned varNum = tree->GetLclNum();
- assert(varNum < compiler->lvaCount);
+ unsigned varNum = tree->GetLclNum();
LclVarDsc* varDsc = compiler->lvaGetDesc(varNum);
var_types targetType = varDsc->GetRegisterType(tree);
@@ -1629,8 +1630,7 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize,
GetEmitter()->emitIns_Call(emitter::EC_INDIR_R, compiler->eeFindHelper(helper),
INDEBUG_LDISASM_COMMA(nullptr) NULL, // addr
argSize, retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur,
- gcInfo.gcRegByrefSetCur,
- BAD_IL_OFFSET, // ilOffset
+ gcInfo.gcRegByrefSetCur, DebugInfo(),
callTargetReg, // ireg
REG_NA, 0, 0, // xreg, xmul, disp
false // isJump
@@ -1640,7 +1640,7 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize,
{
GetEmitter()->emitIns_Call(emitter::EC_FUNC_TOKEN, compiler->eeFindHelper(helper),
INDEBUG_LDISASM_COMMA(nullptr) addr, argSize, retSize, gcInfo.gcVarPtrSetCur,
- gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, BAD_IL_OFFSET, REG_NA, REG_NA, 0,
+ gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, DebugInfo(), REG_NA, REG_NA, 0,
0, /* ilOffset, ireg, xreg, xmul, disp */
false /* isJump */
);
diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp
index d68da1a1e2a9bf..076df275868a36 100644
--- a/src/coreclr/jit/codegenarm64.cpp
+++ b/src/coreclr/jit/codegenarm64.cpp
@@ -1817,7 +1817,7 @@ void CodeGen::genCodeForMulHi(GenTreeOp* treeNode)
genProduceReg(treeNode);
}
-// Generate code for ADD, SUB, MUL, DIV, UDIV, AND, OR and XOR
+// Generate code for ADD, SUB, MUL, DIV, UDIV, AND, AND_NOT, OR and XOR
// This method is expected to have called genConsumeOperands() before calling it.
void CodeGen::genCodeForBinary(GenTreeOp* treeNode)
{
@@ -1826,8 +1826,7 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode)
var_types targetType = treeNode->TypeGet();
emitter* emit = GetEmitter();
- assert(oper == GT_ADD || oper == GT_SUB || oper == GT_MUL || oper == GT_DIV || oper == GT_UDIV || oper == GT_AND ||
- oper == GT_OR || oper == GT_XOR);
+ assert(treeNode->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_DIV, GT_UDIV, GT_AND, GT_AND_NOT, GT_OR, GT_XOR));
GenTree* op1 = treeNode->gtGetOp1();
GenTree* op2 = treeNode->gtGetOp2();
@@ -1846,6 +1845,9 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode)
case GT_AND:
ins = INS_ands;
break;
+ case GT_AND_NOT:
+ ins = INS_bics;
+ break;
default:
noway_assert(!"Unexpected BinaryOp with GTF_SET_FLAGS set");
}
@@ -1869,8 +1871,7 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode)
void CodeGen::genCodeForLclVar(GenTreeLclVar* tree)
{
- unsigned varNum = tree->GetLclNum();
- assert(varNum < compiler->lvaCount);
+ unsigned varNum = tree->GetLclNum();
LclVarDsc* varDsc = compiler->lvaGetDesc(varNum);
var_types targetType = varDsc->GetRegisterType(tree);
@@ -1924,9 +1925,8 @@ void CodeGen::genCodeForStoreLclFld(GenTreeLclFld* tree)
// We must have a stack store with GT_STORE_LCL_FLD
noway_assert(targetReg == REG_NA);
- unsigned varNum = tree->GetLclNum();
- assert(varNum < compiler->lvaCount);
- LclVarDsc* varDsc = &(compiler->lvaTable[varNum]);
+ unsigned varNum = tree->GetLclNum();
+ LclVarDsc* varDsc = compiler->lvaGetDesc(varNum);
// Ensure that lclVar nodes are typed correctly.
assert(!varDsc->lvNormalizeOnStore() || targetType == genActualType(varDsc->TypeGet()));
@@ -2118,15 +2118,14 @@ void CodeGen::genSimpleReturn(GenTree* treeNode)
if (op1->OperGet() == GT_LCL_VAR)
{
GenTreeLclVarCommon* lcl = op1->AsLclVarCommon();
- bool isRegCandidate = compiler->lvaTable[lcl->GetLclNum()].lvIsRegCandidate();
+ const LclVarDsc* varDsc = compiler->lvaGetDesc(lcl);
+ bool isRegCandidate = varDsc->lvIsRegCandidate();
if (isRegCandidate && ((op1->gtFlags & GTF_SPILLED) == 0))
{
// We may need to generate a zero-extending mov instruction to load the value from this GT_LCL_VAR
- unsigned lclNum = lcl->GetLclNum();
- LclVarDsc* varDsc = &(compiler->lvaTable[lclNum]);
- var_types op1Type = genActualType(op1->TypeGet());
- var_types lclType = genActualType(varDsc->TypeGet());
+ var_types op1Type = genActualType(op1->TypeGet());
+ var_types lclType = genActualType(varDsc->TypeGet());
if (genTypeSize(op1Type) < genTypeSize(lclType))
{
@@ -3081,7 +3080,7 @@ void CodeGen::genCodeForCmpXchg(GenTreeCmpXchg* treeNode)
instruction CodeGen::genGetInsForOper(genTreeOps oper, var_types type)
{
- instruction ins = INS_brk;
+ instruction ins = INS_BREAKPOINT;
if (varTypeIsFloating(type))
{
@@ -3119,6 +3118,9 @@ instruction CodeGen::genGetInsForOper(genTreeOps oper, var_types type)
case GT_AND:
ins = INS_and;
break;
+ case GT_AND_NOT:
+ ins = INS_bic;
+ break;
case GT_DIV:
ins = INS_sdiv;
break;
@@ -3305,10 +3307,10 @@ void CodeGen::genCodeForSwap(GenTreeOp* tree)
assert(genIsRegCandidateLocal(tree->gtOp1) && genIsRegCandidateLocal(tree->gtOp2));
GenTreeLclVarCommon* lcl1 = tree->gtOp1->AsLclVarCommon();
- LclVarDsc* varDsc1 = &(compiler->lvaTable[lcl1->GetLclNum()]);
+ LclVarDsc* varDsc1 = compiler->lvaGetDesc(lcl1);
var_types type1 = varDsc1->TypeGet();
GenTreeLclVarCommon* lcl2 = tree->gtOp2->AsLclVarCommon();
- LclVarDsc* varDsc2 = &(compiler->lvaTable[lcl2->GetLclNum()]);
+ LclVarDsc* varDsc2 = compiler->lvaGetDesc(lcl2);
var_types type2 = varDsc2->TypeGet();
// We must have both int or both fp regs
@@ -3566,7 +3568,6 @@ void CodeGen::genCodeForCompare(GenTreeOp* tree)
var_types op2Type = genActualType(op2->TypeGet());
assert(!op1->isUsedFromMemory());
- assert(!op2->isUsedFromMemory());
genConsumeOperands(tree);
@@ -3580,7 +3581,7 @@ void CodeGen::genCodeForCompare(GenTreeOp* tree)
assert(!op1->isContained());
assert(op1Type == op2Type);
- if (op2->IsIntegralConst(0))
+ if (op2->IsFPZero())
{
assert(op2->isContained());
emit->emitIns_R_F(INS_fcmp, cmpSize, op1->GetRegNum(), 0.0);
@@ -3841,10 +3842,9 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize,
GetEmitter()->emitIns_Call(callType, compiler->eeFindHelper(helper), INDEBUG_LDISASM_COMMA(nullptr) addr, argSize,
retSize, EA_UNKNOWN, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur,
- gcInfo.gcRegByrefSetCur, BAD_IL_OFFSET, /* IL offset */
- callTarget, /* ireg */
- REG_NA, 0, 0, /* xreg, xmul, disp */
- false /* isJump */
+ gcInfo.gcRegByrefSetCur, DebugInfo(), callTarget, /* ireg */
+ REG_NA, 0, 0, /* xreg, xmul, disp */
+ false /* isJump */
);
regMaskTP killMask = compiler->compHelperCallKillSet((CorInfoHelpFunc)helper);
@@ -3876,7 +3876,7 @@ void CodeGen::genSIMDIntrinsic(GenTreeSIMD* simdNode)
noway_assert(!"SIMD intrinsic with unsupported base type.");
}
- switch (simdNode->gtSIMDIntrinsicID)
+ switch (simdNode->GetSIMDIntrinsicId())
{
case SIMDIntrinsicInit:
genSIMDIntrinsicInit(simdNode);
@@ -3894,15 +3894,6 @@ void CodeGen::genSIMDIntrinsic(GenTreeSIMD* simdNode)
genSIMDIntrinsicUnOp(simdNode);
break;
- case SIMDIntrinsicWidenLo:
- case SIMDIntrinsicWidenHi:
- genSIMDIntrinsicWiden(simdNode);
- break;
-
- case SIMDIntrinsicNarrow:
- genSIMDIntrinsicNarrow(simdNode);
- break;
-
case SIMDIntrinsicSub:
case SIMDIntrinsicBitwiseAnd:
case SIMDIntrinsicBitwiseOr:
@@ -3991,20 +3982,9 @@ instruction CodeGen::getOpForSIMDIntrinsic(SIMDIntrinsicID intrinsicId, var_type
case SIMDIntrinsicEqual:
result = INS_fcmeq;
break;
- case SIMDIntrinsicNarrow:
- // Use INS_fcvtn lower bytes of result followed by INS_fcvtn2 for upper bytes
- // Return lower bytes instruction here
- result = INS_fcvtn;
- break;
case SIMDIntrinsicSub:
result = INS_fsub;
break;
- case SIMDIntrinsicWidenLo:
- result = INS_fcvtl;
- break;
- case SIMDIntrinsicWidenHi:
- result = INS_fcvtl2;
- break;
default:
assert(!"Unsupported SIMD intrinsic");
unreached();
@@ -4032,20 +4012,9 @@ instruction CodeGen::getOpForSIMDIntrinsic(SIMDIntrinsicID intrinsicId, var_type
case SIMDIntrinsicEqual:
result = INS_cmeq;
break;
- case SIMDIntrinsicNarrow:
- // Use INS_xtn lower bytes of result followed by INS_xtn2 for upper bytes
- // Return lower bytes instruction here
- result = INS_xtn;
- break;
case SIMDIntrinsicSub:
result = INS_sub;
break;
- case SIMDIntrinsicWidenLo:
- result = isUnsigned ? INS_uxtl : INS_sxtl;
- break;
- case SIMDIntrinsicWidenHi:
- result = isUnsigned ? INS_uxtl2 : INS_sxtl2;
- break;
default:
assert(!"Unsupported SIMD intrinsic");
unreached();
@@ -4067,15 +4036,15 @@ instruction CodeGen::getOpForSIMDIntrinsic(SIMDIntrinsicID intrinsicId, var_type
//
void CodeGen::genSIMDIntrinsicInit(GenTreeSIMD* simdNode)
{
- assert(simdNode->gtSIMDIntrinsicID == SIMDIntrinsicInit);
+ assert(simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicInit);
- GenTree* op1 = simdNode->gtGetOp1();
+ GenTree* op1 = simdNode->Op(1);
var_types baseType = simdNode->GetSimdBaseType();
regNumber targetReg = simdNode->GetRegNum();
assert(targetReg != REG_NA);
var_types targetType = simdNode->TypeGet();
- genConsumeOperands(simdNode);
+ genConsumeMultiOpOperands(simdNode);
regNumber op1Reg = op1->IsIntegralConst(0) ? REG_ZR : op1->GetRegNum();
// TODO-ARM64-CQ Add LD1R to allow SIMDIntrinsicInit from contained memory
@@ -4118,16 +4087,18 @@ void CodeGen::genSIMDIntrinsicInit(GenTreeSIMD* simdNode)
//
void CodeGen::genSIMDIntrinsicInitN(GenTreeSIMD* simdNode)
{
- assert(simdNode->gtSIMDIntrinsicID == SIMDIntrinsicInitN);
+ assert(simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicInitN);
regNumber targetReg = simdNode->GetRegNum();
assert(targetReg != REG_NA);
- var_types targetType = simdNode->TypeGet();
-
- var_types baseType = simdNode->GetSimdBaseType();
+ var_types targetType = simdNode->TypeGet();
+ var_types baseType = simdNode->GetSimdBaseType();
+ emitAttr baseTypeSize = emitTypeSize(baseType);
+ regNumber vectorReg = targetReg;
+ size_t initCount = simdNode->GetOperandCount();
- regNumber vectorReg = targetReg;
+ assert((initCount * baseTypeSize) <= simdNode->GetSimdSize());
if (varTypeIsFloating(baseType))
{
@@ -4136,24 +4107,17 @@ void CodeGen::genSIMDIntrinsicInitN(GenTreeSIMD* simdNode)
vectorReg = simdNode->GetSingleTempReg(RBM_ALLFLOAT);
}
- emitAttr baseTypeSize = emitTypeSize(baseType);
-
// We will first consume the list items in execution (left to right) order,
// and record the registers.
regNumber operandRegs[FP_REGSIZE_BYTES];
- unsigned initCount = 0;
- for (GenTree* list = simdNode->gtGetOp1(); list != nullptr; list = list->gtGetOp2())
+ for (size_t i = 1; i <= initCount; i++)
{
- assert(list->OperGet() == GT_LIST);
- GenTree* listItem = list->gtGetOp1();
- assert(listItem->TypeGet() == baseType);
- assert(!listItem->isContained());
- regNumber operandReg = genConsumeReg(listItem);
- operandRegs[initCount] = operandReg;
- initCount++;
- }
+ GenTree* operand = simdNode->Op(i);
+ assert(operand->TypeIs(baseType));
+ assert(!operand->isContained());
- assert((initCount * baseTypeSize) <= simdNode->GetSimdSize());
+ operandRegs[i - 1] = genConsumeReg(operand);
+ }
if (initCount * baseTypeSize < EA_16BYTE)
{
@@ -4192,25 +4156,25 @@ void CodeGen::genSIMDIntrinsicInitN(GenTreeSIMD* simdNode)
//
void CodeGen::genSIMDIntrinsicUnOp(GenTreeSIMD* simdNode)
{
- assert(simdNode->gtSIMDIntrinsicID == SIMDIntrinsicCast ||
- simdNode->gtSIMDIntrinsicID == SIMDIntrinsicConvertToSingle ||
- simdNode->gtSIMDIntrinsicID == SIMDIntrinsicConvertToInt32 ||
- simdNode->gtSIMDIntrinsicID == SIMDIntrinsicConvertToDouble ||
- simdNode->gtSIMDIntrinsicID == SIMDIntrinsicConvertToInt64);
+ assert((simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicCast) ||
+ (simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicConvertToSingle) ||
+ (simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicConvertToInt32) ||
+ (simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicConvertToDouble) ||
+ (simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicConvertToInt64));
- GenTree* op1 = simdNode->gtGetOp1();
+ GenTree* op1 = simdNode->Op(1);
var_types baseType = simdNode->GetSimdBaseType();
regNumber targetReg = simdNode->GetRegNum();
assert(targetReg != REG_NA);
var_types targetType = simdNode->TypeGet();
- genConsumeOperands(simdNode);
+ genConsumeMultiOpOperands(simdNode);
regNumber op1Reg = op1->GetRegNum();
assert(genIsValidFloatReg(op1Reg));
assert(genIsValidFloatReg(targetReg));
- instruction ins = getOpForSIMDIntrinsic(simdNode->gtSIMDIntrinsicID, baseType);
+ instruction ins = getOpForSIMDIntrinsic(simdNode->GetSIMDIntrinsicId(), baseType);
emitAttr attr = (simdNode->GetSimdSize() > 8) ? EA_16BYTE : EA_8BYTE;
if (GetEmitter()->IsMovInstruction(ins))
@@ -4224,113 +4188,6 @@ void CodeGen::genSIMDIntrinsicUnOp(GenTreeSIMD* simdNode)
genProduceReg(simdNode);
}
-//--------------------------------------------------------------------------------
-// genSIMDIntrinsicWiden: Generate code for SIMD Intrinsic Widen operations
-//
-// Arguments:
-// simdNode - The GT_SIMD node
-//
-// Notes:
-// The Widen intrinsics are broken into separate intrinsics for the two results.
-//
-void CodeGen::genSIMDIntrinsicWiden(GenTreeSIMD* simdNode)
-{
- assert((simdNode->gtSIMDIntrinsicID == SIMDIntrinsicWidenLo) ||
- (simdNode->gtSIMDIntrinsicID == SIMDIntrinsicWidenHi));
-
- GenTree* op1 = simdNode->gtGetOp1();
- var_types baseType = simdNode->GetSimdBaseType();
- regNumber targetReg = simdNode->GetRegNum();
- assert(targetReg != REG_NA);
- var_types simdType = simdNode->TypeGet();
-
- genConsumeOperands(simdNode);
- regNumber op1Reg = op1->GetRegNum();
- regNumber srcReg = op1Reg;
- emitAttr emitSize = emitActualTypeSize(simdType);
-
- instruction ins = getOpForSIMDIntrinsic(simdNode->gtSIMDIntrinsicID, baseType);
-
- emitAttr attr = (simdNode->gtSIMDIntrinsicID == SIMDIntrinsicWidenHi) ? EA_16BYTE : EA_8BYTE;
- insOpts opt = genGetSimdInsOpt(attr, baseType);
-
- GetEmitter()->emitIns_R_R(ins, attr, targetReg, op1Reg, opt);
-
- genProduceReg(simdNode);
-}
-
-//--------------------------------------------------------------------------------
-// genSIMDIntrinsicNarrow: Generate code for SIMD Intrinsic Narrow operations
-//
-// Arguments:
-// simdNode - The GT_SIMD node
-//
-// Notes:
-// This intrinsic takes two arguments. The first operand is narrowed to produce the
-// lower elements of the results, and the second operand produces the high elements.
-//
-void CodeGen::genSIMDIntrinsicNarrow(GenTreeSIMD* simdNode)
-{
- assert(simdNode->gtSIMDIntrinsicID == SIMDIntrinsicNarrow);
-
- GenTree* op1 = simdNode->gtGetOp1();
- GenTree* op2 = simdNode->gtGetOp2();
- var_types baseType = simdNode->GetSimdBaseType();
- regNumber targetReg = simdNode->GetRegNum();
- assert(targetReg != REG_NA);
- var_types simdType = simdNode->TypeGet();
- emitAttr emitSize = emitTypeSize(simdType);
-
- genConsumeOperands(simdNode);
- regNumber op1Reg = op1->GetRegNum();
- regNumber op2Reg = op2->GetRegNum();
-
- assert(genIsValidFloatReg(op1Reg));
- assert(genIsValidFloatReg(op2Reg));
- assert(genIsValidFloatReg(targetReg));
- assert(op2Reg != targetReg);
- assert(simdNode->GetSimdSize() == 16);
-
- instruction ins = getOpForSIMDIntrinsic(simdNode->gtSIMDIntrinsicID, baseType);
- assert((ins == INS_fcvtn) || (ins == INS_xtn));
-
- instruction ins2 = (ins == INS_fcvtn) ? INS_fcvtn2 : INS_xtn2;
-
- insOpts opt = INS_OPTS_NONE;
- insOpts opt2 = INS_OPTS_NONE;
-
- // This is not the same as genGetSimdInsOpt()
- // Basetype is the soure operand type
- // However encoding is based on the destination operand type which is 1/2 the basetype.
- switch (baseType)
- {
- case TYP_ULONG:
- case TYP_LONG:
- case TYP_DOUBLE:
- opt = INS_OPTS_2S;
- opt2 = INS_OPTS_4S;
- break;
- case TYP_UINT:
- case TYP_INT:
- opt = INS_OPTS_4H;
- opt2 = INS_OPTS_8H;
- break;
- case TYP_USHORT:
- case TYP_SHORT:
- opt = INS_OPTS_8B;
- opt2 = INS_OPTS_16B;
- break;
- default:
- assert(!"Unsupported narrowing element type");
- unreached();
- }
-
- GetEmitter()->emitIns_R_R(ins, EA_8BYTE, targetReg, op1Reg, opt);
- GetEmitter()->emitIns_R_R(ins2, EA_16BYTE, targetReg, op2Reg, opt2);
-
- genProduceReg(simdNode);
-}
-
//--------------------------------------------------------------------------------
// genSIMDIntrinsicBinOp: Generate code for SIMD Intrinsic binary operations
// add, sub, mul, bit-wise And, AndNot and Or.
@@ -4343,17 +4200,19 @@ void CodeGen::genSIMDIntrinsicNarrow(GenTreeSIMD* simdNode)
//
void CodeGen::genSIMDIntrinsicBinOp(GenTreeSIMD* simdNode)
{
- assert(simdNode->gtSIMDIntrinsicID == SIMDIntrinsicSub || simdNode->gtSIMDIntrinsicID == SIMDIntrinsicBitwiseAnd ||
- simdNode->gtSIMDIntrinsicID == SIMDIntrinsicBitwiseOr || simdNode->gtSIMDIntrinsicID == SIMDIntrinsicEqual);
+ assert((simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicSub) ||
+ (simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicBitwiseAnd) ||
+ (simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicBitwiseOr) ||
+ (simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicEqual));
- GenTree* op1 = simdNode->gtGetOp1();
- GenTree* op2 = simdNode->gtGetOp2();
+ GenTree* op1 = simdNode->Op(1);
+ GenTree* op2 = simdNode->Op(2);
var_types baseType = simdNode->GetSimdBaseType();
regNumber targetReg = simdNode->GetRegNum();
assert(targetReg != REG_NA);
var_types targetType = simdNode->TypeGet();
- genConsumeOperands(simdNode);
+ genConsumeMultiOpOperands(simdNode);
regNumber op1Reg = op1->GetRegNum();
regNumber op2Reg = op2->GetRegNum();
@@ -4363,7 +4222,7 @@ void CodeGen::genSIMDIntrinsicBinOp(GenTreeSIMD* simdNode)
// TODO-ARM64-CQ Contain integer constants where posible
- instruction ins = getOpForSIMDIntrinsic(simdNode->gtSIMDIntrinsicID, baseType);
+ instruction ins = getOpForSIMDIntrinsic(simdNode->GetSIMDIntrinsicId(), baseType);
emitAttr attr = (simdNode->GetSimdSize() > 8) ? EA_16BYTE : EA_8BYTE;
insOpts opt = genGetSimdInsOpt(attr, baseType);
@@ -4392,9 +4251,9 @@ void CodeGen::genSIMDIntrinsicBinOp(GenTreeSIMD* simdNode)
//
void CodeGen::genSIMDIntrinsicUpperSave(GenTreeSIMD* simdNode)
{
- assert(simdNode->gtSIMDIntrinsicID == SIMDIntrinsicUpperSave);
+ assert(simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicUpperSave);
- GenTree* op1 = simdNode->gtGetOp1();
+ GenTree* op1 = simdNode->Op(1);
GenTreeLclVar* lclNode = op1->AsLclVar();
LclVarDsc* varDsc = compiler->lvaGetDesc(lclNode);
assert(emitTypeSize(varDsc->GetRegisterType(lclNode)) == 16);
@@ -4442,9 +4301,9 @@ void CodeGen::genSIMDIntrinsicUpperSave(GenTreeSIMD* simdNode)
//
void CodeGen::genSIMDIntrinsicUpperRestore(GenTreeSIMD* simdNode)
{
- assert(simdNode->gtSIMDIntrinsicID == SIMDIntrinsicUpperRestore);
+ assert(simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicUpperRestore);
- GenTree* op1 = simdNode->gtGetOp1();
+ GenTree* op1 = simdNode->Op(1);
assert(op1->IsLocal());
GenTreeLclVar* lclNode = op1->AsLclVar();
LclVarDsc* varDsc = compiler->lvaGetDesc(lclNode);
@@ -9564,4 +9423,76 @@ void CodeGen::instGen_MemoryBarrier(BarrierKind barrierKind)
}
}
+//-----------------------------------------------------------------------------------
+// genCodeForMadd: Emit a madd/msub (Multiply-Add) instruction
+//
+// Arguments:
+// tree - GT_MADD tree where op1 or op2 is GT_ADD
+//
+void CodeGen::genCodeForMadd(GenTreeOp* tree)
+{
+ assert(tree->OperIs(GT_MADD) && varTypeIsIntegral(tree) && !(tree->gtFlags & GTF_SET_FLAGS));
+ genConsumeOperands(tree);
+
+ GenTree* a;
+ GenTree* b;
+ GenTree* c;
+ if (tree->gtGetOp1()->OperIs(GT_MUL) && tree->gtGetOp1()->isContained())
+ {
+ a = tree->gtGetOp1()->gtGetOp1();
+ b = tree->gtGetOp1()->gtGetOp2();
+ c = tree->gtGetOp2();
+ }
+ else
+ {
+ assert(tree->gtGetOp2()->OperIs(GT_MUL) && tree->gtGetOp2()->isContained());
+ a = tree->gtGetOp2()->gtGetOp1();
+ b = tree->gtGetOp2()->gtGetOp2();
+ c = tree->gtGetOp1();
+ }
+
+ bool useMsub = false;
+ if (a->OperIs(GT_NEG) && a->isContained())
+ {
+ a = a->gtGetOp1();
+ useMsub = true;
+ }
+ if (b->OperIs(GT_NEG) && b->isContained())
+ {
+ b = b->gtGetOp1();
+ useMsub = !useMsub; // it's either "a * -b" or "-a * -b" which is the same as "a * b"
+ }
+
+ GetEmitter()->emitIns_R_R_R_R(useMsub ? INS_msub : INS_madd, emitActualTypeSize(tree), tree->GetRegNum(),
+ a->GetRegNum(), b->GetRegNum(), c->GetRegNum());
+ genProduceReg(tree);
+}
+
+//------------------------------------------------------------------------
+// genCodeForBfiz: Generates the code sequence for a GenTree node that
+// represents a bitfield insert in zero with sign/zero extension.
+//
+// Arguments:
+// tree - the bitfield insert in zero node.
+//
+void CodeGen::genCodeForBfiz(GenTreeOp* tree)
+{
+ assert(tree->OperIs(GT_BFIZ));
+
+ emitAttr size = emitActualTypeSize(tree);
+ unsigned shiftBy = (unsigned)tree->gtGetOp2()->AsIntCon()->IconValue();
+ unsigned shiftByImm = shiftBy & (emitter::getBitWidth(size) - 1);
+ GenTreeCast* cast = tree->gtGetOp1()->AsCast();
+ GenTree* castOp = cast->CastOp();
+
+ genConsumeRegs(castOp);
+ unsigned srcBits = varTypeIsSmall(cast->CastToType()) ? genTypeSize(cast->CastToType()) * BITS_PER_BYTE
+ : genTypeSize(castOp) * BITS_PER_BYTE;
+ const bool isUnsigned = cast->IsUnsigned() || varTypeIsUnsigned(cast->CastToType());
+ GetEmitter()->emitIns_R_R_I_I(isUnsigned ? INS_ubfiz : INS_sbfiz, size, tree->GetRegNum(), castOp->GetRegNum(),
+ (int)shiftByImm, (int)srcBits);
+
+ genProduceReg(tree);
+}
+
#endif // TARGET_ARM64
diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp
index 469a145f12dbcf..22379e3c9dbed6 100644
--- a/src/coreclr/jit/codegenarmarch.cpp
+++ b/src/coreclr/jit/codegenarmarch.cpp
@@ -213,6 +213,7 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
case GT_OR:
case GT_XOR:
case GT_AND:
+ case GT_AND_NOT:
assert(varTypeIsIntegralOrI(treeNode));
FALLTHROUGH;
@@ -300,6 +301,9 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
break;
#ifdef TARGET_ARM64
+ case GT_MADD:
+ genCodeForMadd(treeNode->AsOp());
+ break;
case GT_INC_SATURATE:
genCodeForIncSaturate(treeNode);
@@ -312,6 +316,10 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
case GT_SWAP:
genCodeForSwap(treeNode->AsOp());
break;
+
+ case GT_BFIZ:
+ genCodeForBfiz(treeNode->AsOp());
+ break;
#endif // TARGET_ARM64
case GT_JMP:
@@ -382,7 +390,6 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
// This is handled at the time we call genConsumeReg() on the GT_COPY
break;
- case GT_LIST:
case GT_FIELD_LIST:
// Should always be marked contained.
assert(!"LIST, FIELD_LIST nodes should always be marked contained.");
@@ -542,7 +549,7 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
{
#ifdef DEBUG
char message[256];
- _snprintf_s(message, _countof(message), _TRUNCATE, "NYI: Unimplemented node type %s",
+ _snprintf_s(message, ArrLen(message), _TRUNCATE, "NYI: Unimplemented node type %s",
GenTree::OpName(treeNode->OperGet()));
NYIRAW(message);
#else
@@ -705,7 +712,7 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
// Since it is a fast tail call, the existence of first incoming arg is guaranteed
// because fast tail call requires that in-coming arg area of caller is >= out-going
// arg area required for tail call.
- LclVarDsc* varDsc = &(compiler->lvaTable[varNumOut]);
+ LclVarDsc* varDsc = compiler->lvaGetDesc(varNumOut);
assert(varDsc != nullptr);
#endif // FEATURE_FASTTAILCALL
}
@@ -1240,10 +1247,9 @@ void CodeGen::genPutArgSplit(GenTreePutArgSplit* treeNode)
{
assert(varNode->isContained());
srcVarNum = varNode->GetLclNum();
- assert(srcVarNum < compiler->lvaCount);
// handle promote situation
- LclVarDsc* varDsc = compiler->lvaTable + srcVarNum;
+ LclVarDsc* varDsc = compiler->lvaGetDesc(srcVarNum);
// This struct also must live in the stack frame
// And it can't live in a register (SIMD)
@@ -1614,8 +1620,9 @@ void CodeGen::genCodeForShift(GenTree* tree)
genTreeOps oper = tree->OperGet();
instruction ins = genGetInsForOper(oper, targetType);
emitAttr size = emitActualTypeSize(tree);
+ regNumber dstReg = tree->GetRegNum();
- assert(tree->GetRegNum() != REG_NA);
+ assert(dstReg != REG_NA);
genConsumeOperands(tree->AsOp());
@@ -1623,14 +1630,13 @@ void CodeGen::genCodeForShift(GenTree* tree)
GenTree* shiftBy = tree->gtGetOp2();
if (!shiftBy->IsCnsIntOrI())
{
- GetEmitter()->emitIns_R_R_R(ins, size, tree->GetRegNum(), operand->GetRegNum(), shiftBy->GetRegNum());
+ GetEmitter()->emitIns_R_R_R(ins, size, dstReg, operand->GetRegNum(), shiftBy->GetRegNum());
}
else
{
unsigned immWidth = emitter::getBitWidth(size); // For ARM64, immWidth will be set to 32 or 64
unsigned shiftByImm = (unsigned)shiftBy->AsIntCon()->gtIconVal & (immWidth - 1);
-
- GetEmitter()->emitIns_R_R_I(ins, size, tree->GetRegNum(), operand->GetRegNum(), shiftByImm);
+ GetEmitter()->emitIns_R_R_I(ins, size, dstReg, operand->GetRegNum(), shiftByImm);
}
genProduceReg(tree);
@@ -2455,16 +2461,14 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
}
}
- // We need to propagate the IL offset information to the call instruction, so we can emit
+ DebugInfo di;
+ // We need to propagate the debug information to the call instruction, so we can emit
// an IL to native mapping record for the call, to support managed return value debugging.
// We don't want tail call helper calls that were converted from normal calls to get a record,
// so we skip this hash table lookup logic in that case.
-
- IL_OFFSETX ilOffset = BAD_IL_OFFSET;
-
- if (compiler->opts.compDbgInfo && compiler->genCallSite2ILOffsetMap != nullptr && !call->IsTailCall())
+ if (compiler->opts.compDbgInfo && compiler->genCallSite2DebugInfoMap != nullptr && !call->IsTailCall())
{
- (void)compiler->genCallSite2ILOffsetMap->Lookup(call, &ilOffset);
+ (void)compiler->genCallSite2DebugInfoMap->Lookup(call, &di);
}
CORINFO_SIG_INFO* sigInfo = nullptr;
@@ -2504,7 +2508,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
nullptr, // addr
retSize
MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
- ilOffset,
+ di,
target->GetRegNum(),
call->IsFastTailCall());
// clang-format on
@@ -2544,7 +2548,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
nullptr, // addr
retSize
MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
- ilOffset,
+ di,
targetAddrReg,
call->IsFastTailCall());
// clang-format on
@@ -2592,7 +2596,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
INDEBUG_LDISASM_COMMA(sigInfo)
NULL,
retSize,
- ilOffset,
+ di,
tmpReg,
call->IsFastTailCall());
// clang-format on
@@ -2607,7 +2611,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
addr,
retSize
MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
- ilOffset,
+ di,
REG_NA,
call->IsFastTailCall());
// clang-format on
@@ -2643,16 +2647,16 @@ void CodeGen::genJmpMethod(GenTree* jmp)
// But that would require us to deal with circularity while moving values around. Spilling
// to stack makes the implementation simple, which is not a bad trade off given Jmp calls
// are not frequent.
- for (varNum = 0; (varNum < compiler->info.compArgsCount); varNum++)
+ for (varNum = 0; varNum < compiler->info.compArgsCount; varNum++)
{
- varDsc = compiler->lvaTable + varNum;
+ varDsc = compiler->lvaGetDesc(varNum);
if (varDsc->lvPromoted)
{
noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
unsigned fieldVarNum = varDsc->lvFieldLclStart;
- varDsc = compiler->lvaTable + fieldVarNum;
+ varDsc = compiler->lvaGetDesc(fieldVarNum);
}
noway_assert(varDsc->lvIsParam);
@@ -2718,15 +2722,15 @@ void CodeGen::genJmpMethod(GenTree* jmp)
// Next move any un-enregistered register arguments back to their register.
regMaskTP fixedIntArgMask = RBM_NONE; // tracks the int arg regs occupying fixed args in case of a vararg method.
unsigned firstArgVarNum = BAD_VAR_NUM; // varNum of the first argument in case of a vararg method.
- for (varNum = 0; (varNum < compiler->info.compArgsCount); varNum++)
+ for (varNum = 0; varNum < compiler->info.compArgsCount; varNum++)
{
- varDsc = compiler->lvaTable + varNum;
+ varDsc = compiler->lvaGetDesc(varNum);
if (varDsc->lvPromoted)
{
noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
unsigned fieldVarNum = varDsc->lvFieldLclStart;
- varDsc = compiler->lvaTable + fieldVarNum;
+ varDsc = compiler->lvaGetDesc(fieldVarNum);
}
noway_assert(varDsc->lvIsParam);
@@ -3248,9 +3252,9 @@ void CodeGen::genCreateAndStoreGCInfo(unsigned codeSize,
if (compiler->opts.IsReversePInvoke())
{
unsigned reversePInvokeFrameVarNumber = compiler->lvaReversePInvokeFrameVar;
- assert(reversePInvokeFrameVarNumber != BAD_VAR_NUM && reversePInvokeFrameVarNumber < compiler->lvaRefCount);
- LclVarDsc& reversePInvokeFrameVar = compiler->lvaTable[reversePInvokeFrameVarNumber];
- gcInfoEncoder->SetReversePInvokeFrameSlot(reversePInvokeFrameVar.GetStackOffset());
+ assert(reversePInvokeFrameVarNumber != BAD_VAR_NUM);
+ const LclVarDsc* reversePInvokeFrameVar = compiler->lvaGetDesc(reversePInvokeFrameVarNumber);
+ gcInfoEncoder->SetReversePInvokeFrameSlot(reversePInvokeFrameVar->GetStackOffset());
}
gcInfoEncoder->Build();
diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp
index 8c788286f292d0..ef0c34ec5a01d2 100644
--- a/src/coreclr/jit/codegencommon.cpp
+++ b/src/coreclr/jit/codegencommon.cpp
@@ -122,10 +122,7 @@ CodeGen::CodeGen(Compiler* theCompiler) : CodeGenInterface(theCompiler)
compiler->compVSQuirkStackPaddingNeeded = 0;
#endif // TARGET_AMD64
- // Initialize the IP-mapping logic.
- compiler->genIPmappingList = nullptr;
- compiler->genIPmappingLast = nullptr;
- compiler->genCallSite2ILOffsetMap = nullptr;
+ compiler->genCallSite2DebugInfoMap = nullptr;
/* Assume that we not fully interruptible */
@@ -503,15 +500,16 @@ regMaskTP CodeGenInterface::genGetRegMask(GenTree* tree)
assert(tree->gtOper == GT_LCL_VAR);
regMaskTP regMask = RBM_NONE;
- const LclVarDsc* varDsc = compiler->lvaTable + tree->AsLclVarCommon()->GetLclNum();
+ const LclVarDsc* varDsc = compiler->lvaGetDesc(tree->AsLclVarCommon());
if (varDsc->lvPromoted)
{
for (unsigned i = varDsc->lvFieldLclStart; i < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++i)
{
- noway_assert(compiler->lvaTable[i].lvIsStructField);
- if (compiler->lvaTable[i].lvIsInReg())
+ const LclVarDsc* fieldVarDsc = compiler->lvaGetDesc(i);
+ noway_assert(fieldVarDsc->lvIsStructField);
+ if (fieldVarDsc->lvIsInReg())
{
- regMask |= genGetRegMask(&compiler->lvaTable[i]);
+ regMask |= genGetRegMask(fieldVarDsc);
}
}
}
@@ -533,7 +531,8 @@ void CodeGenInterface::genUpdateRegLife(const LclVarDsc* varDsc, bool isBorn, bo
#ifdef DEBUG
if (compiler->verbose)
{
- printf("\t\t\t\t\t\t\tV%02u in reg ", (varDsc - compiler->lvaTable));
+ printf("\t\t\t\t\t\t\tV%02u in reg ", compiler->lvaGetLclNum(varDsc));
+
varDsc->PrintVarReg();
printf(" is becoming %s ", (isDying) ? "dead" : "live");
Compiler::printTreeID(tree);
@@ -1199,8 +1198,7 @@ unsigned CodeGenInterface::InferStructOpSizeAlign(GenTree* op, unsigned* alignme
}
else if (op->gtOper == GT_LCL_VAR)
{
- unsigned varNum = op->AsLclVarCommon()->GetLclNum();
- LclVarDsc* varDsc = compiler->lvaTable + varNum;
+ const LclVarDsc* varDsc = compiler->lvaGetDesc(op->AsLclVarCommon());
assert(varDsc->lvType == TYP_STRUCT);
opSize = varDsc->lvSize();
#ifndef TARGET_64BIT
@@ -1214,43 +1212,6 @@ unsigned CodeGenInterface::InferStructOpSizeAlign(GenTree* op, unsigned* alignme
alignment = TARGET_POINTER_SIZE;
}
}
- else if (op->OperIsCopyBlkOp())
- {
- GenTree* op2 = op->AsOp()->gtOp2;
-
- if (op2->OperGet() == GT_CNS_INT)
- {
- if (op2->IsIconHandle(GTF_ICON_CLASS_HDL))
- {
- CORINFO_CLASS_HANDLE clsHnd = (CORINFO_CLASS_HANDLE)op2->AsIntCon()->gtIconVal;
- opSize = roundUp(compiler->info.compCompHnd->getClassSize(clsHnd), TARGET_POINTER_SIZE);
- alignment =
- roundUp(compiler->info.compCompHnd->getClassAlignmentRequirement(clsHnd), TARGET_POINTER_SIZE);
- }
- else
- {
- opSize = (unsigned)op2->AsIntCon()->gtIconVal;
- GenTree* op1 = op->AsOp()->gtOp1;
- assert(op1->OperGet() == GT_LIST);
- GenTree* dstAddr = op1->AsOp()->gtOp1;
- if (dstAddr->OperGet() == GT_ADDR)
- {
- InferStructOpSizeAlign(dstAddr->AsOp()->gtOp1, &alignment);
- }
- else
- {
- assert(!"Unhandle dstAddr node");
- alignment = TARGET_POINTER_SIZE;
- }
- }
- }
- else
- {
- noway_assert(!"Variable sized COPYBLK register arg!");
- opSize = 0;
- alignment = TARGET_POINTER_SIZE;
- }
- }
else if (op->gtOper == GT_MKREFANY)
{
opSize = TARGET_POINTER_SIZE * 2;
@@ -1798,7 +1759,7 @@ void CodeGen::genExitCode(BasicBlock* block)
that this is ok */
// For non-optimized debuggable code, there is only one epilog.
- genIPmappingAdd((IL_OFFSETX)ICorDebugInfo::EPILOG, true);
+ genIPmappingAdd(IPmappingDscKind::Epilog, DebugInfo(), true);
bool jmpEpilog = ((block->bbFlags & BBF_HAS_JMP) != 0);
if (compiler->getNeedsGSSecurityCookie())
@@ -2446,6 +2407,8 @@ void CodeGen::genEmitUnwindDebugGCandEH()
genIPmappingGen();
+ INDEBUG(genDumpPreciseDebugInfo());
+
/* Finalize the Local Var info in terms of generated code */
genSetScopeInfo();
@@ -3193,7 +3156,7 @@ void CodeGen::genGCWriteBarrier(GenTree* tgt, GCInfo::WriteBarrierForm wbf)
}
else
{
- LclVarDsc* varDsc = &compiler->lvaTable[lclNum];
+ const LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum);
if (varDsc->lvIsParam && varDsc->lvType == TYP_BYREF)
{
wbKind = CWBKind_ByRefArg; // Out (or in/out) arg
@@ -3365,16 +3328,16 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
// In other cases, we simply use the type of the lclVar to determine the type of the register.
var_types getRegType(Compiler* compiler)
{
- const LclVarDsc& varDsc = compiler->lvaTable[varNum];
+ const LclVarDsc* varDsc = compiler->lvaGetDesc(varNum);
// Check if this is an HFA register arg and return the HFA type
- if (varDsc.lvIsHfaRegArg())
+ if (varDsc->lvIsHfaRegArg())
{
// Cannot have hfa types on windows arm targets
// in vararg methods.
assert(!TargetOS::IsWindows || !compiler->info.compIsVarArgs);
- return varDsc.GetHfaType();
+ return varDsc->GetHfaType();
}
- return compiler->mangleVarArgsType(varDsc.lvType);
+ return compiler->mangleVarArgsType(varDsc->lvType);
}
#endif // !UNIX_AMD64_ABI
@@ -3385,7 +3348,7 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
for (varNum = 0; varNum < compiler->lvaCount; ++varNum)
{
- varDsc = compiler->lvaTable + varNum;
+ varDsc = compiler->lvaGetDesc(varNum);
// Is this variable a register arg?
if (!varDsc->lvIsParam)
@@ -3409,7 +3372,7 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
if (varDsc->lvIsStructField)
{
assert(!varDsc->lvPromoted);
- parentVarDsc = &compiler->lvaTable[varDsc->lvParentLcl];
+ parentVarDsc = compiler->lvaGetDesc(varDsc->lvParentLcl);
}
Compiler::lvaPromotionType promotionType = compiler->lvaGetPromotionType(parentVarDsc);
@@ -3756,9 +3719,8 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
continue;
}
- varNum = regArgTab[argNum].varNum;
- noway_assert(varNum < compiler->lvaCount);
- varDsc = compiler->lvaTable + varNum;
+ varNum = regArgTab[argNum].varNum;
+ varDsc = compiler->lvaGetDesc(varNum);
const var_types varRegType = varDsc->GetRegisterType();
noway_assert(varDsc->lvIsParam && varDsc->lvIsRegArg);
@@ -3896,8 +3858,7 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
}
varNum = regArgTab[argNum].varNum;
- noway_assert(varNum < compiler->lvaCount);
- varDsc = compiler->lvaTable + varNum;
+ varDsc = compiler->lvaGetDesc(varNum);
#ifndef TARGET_64BIT
// If this arg is never on the stack, go to the next one.
@@ -4094,14 +4055,12 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
srcReg = regArgTab[argNum].trashBy;
varNumDest = regArgTab[destReg].varNum;
- noway_assert(varNumDest < compiler->lvaCount);
- varDscDest = compiler->lvaTable + varNumDest;
+ varDscDest = compiler->lvaGetDesc(varNumDest);
noway_assert(varDscDest->lvIsParam && varDscDest->lvIsRegArg);
noway_assert(srcReg < argMax);
varNumSrc = regArgTab[srcReg].varNum;
- noway_assert(varNumSrc < compiler->lvaCount);
- varDscSrc = compiler->lvaTable + varNumSrc;
+ varDscSrc = compiler->lvaGetDesc(varNumSrc);
noway_assert(varDscSrc->lvIsParam && varDscSrc->lvIsRegArg);
emitAttr size = EA_PTRSIZE;
@@ -4117,8 +4076,7 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
/* only 2 registers form the circular dependency - use "xchg" */
varNum = regArgTab[argNum].varNum;
- noway_assert(varNum < compiler->lvaCount);
- varDsc = compiler->lvaTable + varNum;
+ varDsc = compiler->lvaGetDesc(varNum);
noway_assert(varDsc->lvIsParam && varDsc->lvIsRegArg);
noway_assert(genTypeSize(genActualType(varDscSrc->TypeGet())) <= REGSIZE_BYTES);
@@ -4163,7 +4121,7 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
unsigned iter = begReg;
do
{
- if (compiler->lvaTable[regArgTab[iter].varNum].TypeGet() != TYP_DOUBLE)
+ if (compiler->lvaGetDesc(regArgTab[iter].varNum)->TypeGet() != TYP_DOUBLE)
{
cycleAllDouble = false;
break;
@@ -4250,8 +4208,7 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
}
#endif
varNumSrc = regArgTab[srcReg].varNum;
- noway_assert(varNumSrc < compiler->lvaCount);
- varDscSrc = compiler->lvaTable + varNumSrc;
+ varDscSrc = compiler->lvaGetDesc(varNumSrc);
noway_assert(varDscSrc->lvIsParam && varDscSrc->lvIsRegArg);
if (destMemType == TYP_REF)
@@ -4312,9 +4269,8 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
continue;
}
- varNum = regArgTab[argNum].varNum;
- noway_assert(varNum < compiler->lvaCount);
- varDsc = compiler->lvaTable + varNum;
+ varNum = regArgTab[argNum].varNum;
+ varDsc = compiler->lvaGetDesc(varNum);
const var_types regType = regArgTab[argNum].getRegType(compiler);
const regNumber regNum = genMapRegArgNumToRegNum(argNum, regType);
const var_types varRegType = varDsc->GetRegisterType();
@@ -6394,7 +6350,7 @@ void CodeGen::genReportGenericContextArg(regNumber initReg, bool* pInitRegZeroed
unsigned contextArg = reportArg ? compiler->info.compTypeCtxtArg : compiler->info.compThisArg;
noway_assert(contextArg != BAD_VAR_NUM);
- LclVarDsc* varDsc = &compiler->lvaTable[contextArg];
+ LclVarDsc* varDsc = compiler->lvaGetDesc(contextArg);
// We are still in the prolog and compiler->info.compTypeCtxtArg has not been
// moved to its final home location. So we need to use it from the
@@ -6978,7 +6934,7 @@ void CodeGen::genFnProlog()
// Do this so we can put the prolog instruction group ahead of
// other instruction groups
- genIPmappingAddToFront((IL_OFFSETX)ICorDebugInfo::PROLOG);
+ genIPmappingAddToFront(IPmappingDscKind::Prolog, DebugInfo(), true);
#ifdef DEBUG
if (compiler->opts.dspCode)
@@ -7029,7 +6985,7 @@ void CodeGen::genFnProlog()
// as it will overwrite the real value
if (compiler->lvaPSPSym != BAD_VAR_NUM)
{
- LclVarDsc* varDsc = &compiler->lvaTable[compiler->lvaPSPSym];
+ const LclVarDsc* varDsc = compiler->lvaGetDesc(compiler->lvaPSPSym);
assert(!varDsc->lvMustInit);
}
@@ -7260,13 +7216,11 @@ void CodeGen::genFnProlog()
assert((!compiler->opts.ShouldUsePInvokeHelpers()) || (compiler->info.compLvFrameListRoot == BAD_VAR_NUM));
if (!compiler->opts.ShouldUsePInvokeHelpers())
{
- noway_assert(compiler->info.compLvFrameListRoot < compiler->lvaCount);
-
excludeMask |= (RBM_PINVOKE_TCB | RBM_PINVOKE_SCRATCH);
// We also must exclude the register used by compLvFrameListRoot when it is enregistered
//
- LclVarDsc* varDsc = &compiler->lvaTable[compiler->info.compLvFrameListRoot];
+ const LclVarDsc* varDsc = compiler->lvaGetDesc(compiler->info.compLvFrameListRoot);
if (varDsc->lvRegister)
{
excludeMask |= genRegMask(varDsc->GetRegNum());
@@ -7679,9 +7633,9 @@ void CodeGen::genFnProlog()
// (our argument pointer register has a refcount > 0).
unsigned argsStartVar = compiler->lvaVarargsBaseOfStkArgs;
- if (compiler->info.compIsVarArgs && compiler->lvaTable[argsStartVar].lvRefCnt() > 0)
+ if (compiler->info.compIsVarArgs && compiler->lvaGetDesc(argsStartVar)->lvRefCnt() > 0)
{
- varDsc = &compiler->lvaTable[argsStartVar];
+ varDsc = compiler->lvaGetDesc(argsStartVar);
noway_assert(compiler->info.compArgsCount > 0);
@@ -7695,7 +7649,7 @@ void CodeGen::genFnProlog()
// EDX might actually be holding something here. So make sure to only use EAX for this code
// sequence.
- LclVarDsc* lastArg = &compiler->lvaTable[compiler->info.compArgsCount - 1];
+ const LclVarDsc* lastArg = compiler->lvaGetDesc(compiler->info.compArgsCount - 1);
noway_assert(!lastArg->lvRegister);
signed offset = lastArg->GetStackOffset();
assert(offset != BAD_STK_OFFS);
@@ -7721,8 +7675,8 @@ void CodeGen::genFnProlog()
if (compiler->opts.compStackCheckOnRet)
{
noway_assert(compiler->lvaReturnSpCheck != 0xCCCCCCCC &&
- compiler->lvaTable[compiler->lvaReturnSpCheck].lvDoNotEnregister &&
- compiler->lvaTable[compiler->lvaReturnSpCheck].lvOnFrame);
+ compiler->lvaGetDesc(compiler->lvaReturnSpCheck)->lvDoNotEnregister &&
+ compiler->lvaGetDesc(compiler->lvaReturnSpCheck)->lvOnFrame);
GetEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_SPBASE, compiler->lvaReturnSpCheck, 0);
}
#endif // defined(DEBUG) && defined(TARGET_XARCH)
@@ -8068,7 +8022,7 @@ void CodeGen::genFnEpilog(BasicBlock* block)
gcInfo.gcVarPtrSetCur,
gcInfo.gcRegGCrefSetCur,
gcInfo.gcRegByrefSetCur,
- BAD_IL_OFFSET, // IL offset
+ DebugInfo(),
indCallReg, // ireg
REG_NA, // xreg
0, // xmul
@@ -8469,7 +8423,8 @@ void CodeGen::genFnEpilog(BasicBlock* block)
gcInfo.gcVarPtrSetCur,
gcInfo.gcRegGCrefSetCur,
gcInfo.gcRegByrefSetCur,
- BAD_IL_OFFSET, indCallReg, REG_NA, 0, 0, /* iloffset, ireg, xreg, xmul, disp */
+ DebugInfo(),
+ indCallReg, REG_NA, 0, 0, /* ireg, xreg, xmul, disp */
true /* isJump */
);
// clang-format on
@@ -9815,7 +9770,7 @@ unsigned CodeGen::getFirstArgWithStackSlot()
LclVarDsc* varDsc = nullptr;
for (unsigned i = 0; i < compiler->info.compArgsCount; i++)
{
- varDsc = &(compiler->lvaTable[i]);
+ varDsc = compiler->lvaGetDesc(i);
// We should have found a stack parameter (and broken out of this loop) before
// we find any non-parameters.
@@ -10266,7 +10221,7 @@ void CodeGen::genSetScopeInfo(unsigned which,
// Is this a varargs function?
if (compiler->info.compIsVarArgs && varNum != compiler->lvaVarargsHandleArg &&
- varNum < compiler->info.compArgsCount && !compiler->lvaTable[varNum].lvIsRegArg)
+ varNum < compiler->info.compArgsCount && !compiler->lvaGetDesc(varNum)->lvIsRegArg)
{
noway_assert(varLoc->vlType == VLT_STK || varLoc->vlType == VLT_STK2);
@@ -10275,7 +10230,7 @@ void CodeGen::genSetScopeInfo(unsigned which,
// and just find its position relative to the varargs handle
PREFIX_ASSUME(compiler->lvaVarargsHandleArg < compiler->info.compArgsCount);
- if (!compiler->lvaTable[compiler->lvaVarargsHandleArg].lvOnFrame)
+ if (!compiler->lvaGetDesc(compiler->lvaVarargsHandleArg)->lvOnFrame)
{
noway_assert(!compiler->opts.compDbgCode);
return;
@@ -10283,9 +10238,9 @@ void CodeGen::genSetScopeInfo(unsigned which,
// Can't check compiler->lvaTable[varNum].lvOnFrame as we don't set it for
// arguments of vararg functions to avoid reporting them to GC.
- noway_assert(!compiler->lvaTable[varNum].lvRegister);
- unsigned cookieOffset = compiler->lvaTable[compiler->lvaVarargsHandleArg].GetStackOffset();
- unsigned varOffset = compiler->lvaTable[varNum].GetStackOffset();
+ noway_assert(!compiler->lvaGetDesc(varNum)->lvRegister);
+ unsigned cookieOffset = compiler->lvaGetDesc(compiler->lvaVarargsHandleArg)->GetStackOffset();
+ unsigned varOffset = compiler->lvaGetDesc(varNum)->GetStackOffset();
noway_assert(cookieOffset < varOffset);
unsigned offset = varOffset - cookieOffset;
@@ -10401,32 +10356,38 @@ const char* CodeGen::siStackVarName(size_t offs, size_t size, unsigned reg, unsi
* Display a IPmappingDsc. Pass -1 as mappingNum to not display a mapping number.
*/
-void CodeGen::genIPmappingDisp(unsigned mappingNum, Compiler::IPmappingDsc* ipMapping)
+void CodeGen::genIPmappingDisp(unsigned mappingNum, IPmappingDsc* ipMapping)
{
if (mappingNum != unsigned(-1))
{
printf("%d: ", mappingNum);
}
- IL_OFFSETX offsx = ipMapping->ipmdILoffsx;
-
- if (offsx == BAD_IL_OFFSET)
+ switch (ipMapping->ipmdKind)
{
- printf("???");
- }
- else
- {
- Compiler::eeDispILOffs(jitGetILoffsAny(offsx));
+ case IPmappingDscKind::Prolog:
+ printf("PROLOG");
+ break;
+ case IPmappingDscKind::Epilog:
+ printf("EPILOG");
+ break;
+ case IPmappingDscKind::NoMapping:
+ printf("NO_MAP");
+ break;
+ case IPmappingDscKind::Normal:
+ const ILLocation& loc = ipMapping->ipmdLoc;
+ Compiler::eeDispILOffs(loc.GetOffset());
+ if (loc.IsStackEmpty())
+ {
+ printf(" STACK_EMPTY");
+ }
- if (jitIsStackEmpty(offsx))
- {
- printf(" STACK_EMPTY");
- }
+ if (loc.IsCall())
+ {
+ printf(" CALL_INSTRUCTION");
+ }
- if (jitIsCallInstruction(offsx))
- {
- printf(" CALL_INSTRUCTION");
- }
+ break;
}
printf(" ");
@@ -10444,12 +10405,11 @@ void CodeGen::genIPmappingDisp(unsigned mappingNum, Compiler::IPmappingDsc* ipMa
void CodeGen::genIPmappingListDisp()
{
- unsigned mappingNum = 0;
- Compiler::IPmappingDsc* ipMapping;
+ unsigned mappingNum = 0;
- for (ipMapping = compiler->genIPmappingList; ipMapping != nullptr; ipMapping = ipMapping->ipmdNext)
+ for (IPmappingDsc& dsc : compiler->genIPmappings)
{
- genIPmappingDisp(mappingNum, ipMapping);
+ genIPmappingDisp(mappingNum, &dsc);
++mappingNum;
}
}
@@ -10463,66 +10423,54 @@ void CodeGen::genIPmappingListDisp()
* Record the instr offset as being at the current code gen position.
*/
-void CodeGen::genIPmappingAdd(IL_OFFSETX offsx, bool isLabel)
+void CodeGen::genIPmappingAdd(IPmappingDscKind kind, const DebugInfo& di, bool isLabel)
{
if (!compiler->opts.compDbgInfo)
{
return;
}
- assert(offsx != BAD_IL_OFFSET);
+ assert((kind == IPmappingDscKind::Normal) == di.IsValid());
- switch ((int)offsx) // Need the cast since offs is unsigned and the case statements are comparing to signed.
+ switch (kind)
{
- case ICorDebugInfo::PROLOG:
- case ICorDebugInfo::EPILOG:
+ case IPmappingDscKind::Prolog:
+ case IPmappingDscKind::Epilog:
break;
default:
- if (offsx != (IL_OFFSETX)ICorDebugInfo::NO_MAPPING)
+ if (kind == IPmappingDscKind::Normal)
{
- noway_assert(jitGetILoffs(offsx) <= compiler->info.compILCodeSize);
+ noway_assert(di.GetLocation().GetOffset() <= compiler->info.compILCodeSize);
}
- // Ignore this one if it's the same IL offset as the last one we saw.
+ // Ignore this one if it's the same IL location as the last one we saw.
// Note that we'll let through two identical IL offsets if the flag bits
// differ, or two identical "special" mappings (e.g., PROLOG).
- if ((compiler->genIPmappingLast != nullptr) && (offsx == compiler->genIPmappingLast->ipmdILoffsx))
+ if ((compiler->genIPmappings.size() > 0) && (kind == compiler->genIPmappings.back().ipmdKind) &&
+ (di.GetLocation() == compiler->genIPmappings.back().ipmdLoc))
{
- JITDUMP("genIPmappingAdd: ignoring duplicate IL offset 0x%x\n", offsx);
+ JITDUMP("genIPmappingAdd: ignoring duplicate IL offset 0x%x\n", di.GetLocation().GetOffset());
return;
}
break;
}
- /* Create a mapping entry and append it to the list */
-
- Compiler::IPmappingDsc* addMapping = compiler->getAllocator(CMK_DebugInfo).allocate(1);
- addMapping->ipmdNativeLoc.CaptureLocation(GetEmitter());
- addMapping->ipmdILoffsx = offsx;
- addMapping->ipmdIsLabel = isLabel;
- addMapping->ipmdNext = nullptr;
-
- if (compiler->genIPmappingList != nullptr)
- {
- assert(compiler->genIPmappingLast != nullptr);
- assert(compiler->genIPmappingLast->ipmdNext == nullptr);
- compiler->genIPmappingLast->ipmdNext = addMapping;
- }
- else
- {
- assert(compiler->genIPmappingLast == nullptr);
- compiler->genIPmappingList = addMapping;
- }
+ IPmappingDsc addMapping;
+ addMapping.ipmdNativeLoc.CaptureLocation(GetEmitter());
+ addMapping.ipmdKind = kind;
+ addMapping.ipmdLoc = di.GetLocation();
+ addMapping.ipmdIsLabel = isLabel;
- compiler->genIPmappingLast = addMapping;
+ assert((kind == IPmappingDscKind::Normal) == addMapping.ipmdLoc.IsValid());
+ compiler->genIPmappings.push_back(addMapping);
#ifdef DEBUG
if (verbose)
{
printf("Added IP mapping: ");
- genIPmappingDisp(unsigned(-1), addMapping);
+ genIPmappingDisp(unsigned(-1), &addMapping);
}
#endif // DEBUG
}
@@ -10531,210 +10479,73 @@ void CodeGen::genIPmappingAdd(IL_OFFSETX offsx, bool isLabel)
*
* Prepend an IPmappingDsc struct to the list that we're maintaining
* for the debugger.
- * Record the instr offset as being at the current code gen position.
*/
-void CodeGen::genIPmappingAddToFront(IL_OFFSETX offsx)
+void CodeGen::genIPmappingAddToFront(IPmappingDscKind kind, const DebugInfo& di, bool isLabel)
{
if (!compiler->opts.compDbgInfo)
{
return;
}
- assert(offsx != BAD_IL_OFFSET);
- assert(compiler->compGeneratingProlog); // We only ever do this during prolog generation.
-
- switch ((int)offsx) // Need the cast since offs is unsigned and the case statements are comparing to signed.
- {
- case ICorDebugInfo::NO_MAPPING:
- case ICorDebugInfo::PROLOG:
- case ICorDebugInfo::EPILOG:
- break;
-
- default:
- noway_assert(jitGetILoffs(offsx) <= compiler->info.compILCodeSize);
- break;
- }
+ noway_assert((kind != IPmappingDscKind::Normal) ||
+ (di.IsValid() && (di.GetLocation().GetOffset() <= compiler->info.compILCodeSize)));
/* Create a mapping entry and prepend it to the list */
- Compiler::IPmappingDsc* addMapping = compiler->getAllocator(CMK_DebugInfo).allocate(1);
- addMapping->ipmdNativeLoc.CaptureLocation(GetEmitter());
- addMapping->ipmdILoffsx = offsx;
- addMapping->ipmdIsLabel = true;
- addMapping->ipmdNext = nullptr;
-
- addMapping->ipmdNext = compiler->genIPmappingList;
- compiler->genIPmappingList = addMapping;
-
- if (compiler->genIPmappingLast == nullptr)
- {
- compiler->genIPmappingLast = addMapping;
- }
+ IPmappingDsc addMapping;
+ addMapping.ipmdNativeLoc.CaptureLocation(GetEmitter());
+ addMapping.ipmdKind = kind;
+ addMapping.ipmdLoc = di.GetLocation();
+ addMapping.ipmdIsLabel = isLabel;
+ compiler->genIPmappings.push_front(addMapping);
#ifdef DEBUG
if (verbose)
{
printf("Added IP mapping to front: ");
- genIPmappingDisp(unsigned(-1), addMapping);
+ genIPmappingDisp(unsigned(-1), &addMapping);
}
#endif // DEBUG
}
/*****************************************************************************/
-C_ASSERT(IL_OFFSETX(ICorDebugInfo::NO_MAPPING) != IL_OFFSETX(BAD_IL_OFFSET));
-C_ASSERT(IL_OFFSETX(ICorDebugInfo::PROLOG) != IL_OFFSETX(BAD_IL_OFFSET));
-C_ASSERT(IL_OFFSETX(ICorDebugInfo::EPILOG) != IL_OFFSETX(BAD_IL_OFFSET));
-
-C_ASSERT(IL_OFFSETX(BAD_IL_OFFSET) > MAX_IL_OFFSET);
-C_ASSERT(IL_OFFSETX(ICorDebugInfo::NO_MAPPING) > MAX_IL_OFFSET);
-C_ASSERT(IL_OFFSETX(ICorDebugInfo::PROLOG) > MAX_IL_OFFSET);
-C_ASSERT(IL_OFFSETX(ICorDebugInfo::EPILOG) > MAX_IL_OFFSET);
-
-//------------------------------------------------------------------------
-// jitGetILoffs: Returns the IL offset portion of the IL_OFFSETX type.
-// Asserts if any ICorDebugInfo distinguished value (like ICorDebugInfo::NO_MAPPING)
-// is seen; these are unexpected here. Also asserts if passed BAD_IL_OFFSET.
-//
-// Arguments:
-// offsx - the IL_OFFSETX value with the IL offset to extract.
-//
-// Return Value:
-// The IL offset.
-
-IL_OFFSET jitGetILoffs(IL_OFFSETX offsx)
-{
- assert(offsx != BAD_IL_OFFSET);
-
- switch ((int)offsx) // Need the cast since offs is unsigned and the case statements are comparing to signed.
- {
- case ICorDebugInfo::NO_MAPPING:
- case ICorDebugInfo::PROLOG:
- case ICorDebugInfo::EPILOG:
- unreached();
-
- default:
- return IL_OFFSET(offsx & ~IL_OFFSETX_BITS);
- }
-}
-
-//------------------------------------------------------------------------
-// jitGetILoffsAny: Similar to jitGetILoffs(), but passes through ICorDebugInfo
-// distinguished values. Asserts if passed BAD_IL_OFFSET.
-//
-// Arguments:
-// offsx - the IL_OFFSETX value with the IL offset to extract.
-//
-// Return Value:
-// The IL offset.
-
-IL_OFFSET jitGetILoffsAny(IL_OFFSETX offsx)
-{
- assert(offsx != BAD_IL_OFFSET);
-
- switch ((int)offsx) // Need the cast since offs is unsigned and the case statements are comparing to signed.
- {
- case ICorDebugInfo::NO_MAPPING:
- case ICorDebugInfo::PROLOG:
- case ICorDebugInfo::EPILOG:
- return IL_OFFSET(offsx);
-
- default:
- return IL_OFFSET(offsx & ~IL_OFFSETX_BITS);
- }
-}
-
-//------------------------------------------------------------------------
-// jitIsStackEmpty: Does the IL offset have the stack empty bit set?
-// Asserts if passed BAD_IL_OFFSET.
-//
-// Arguments:
-// offsx - the IL_OFFSETX value to check
-//
-// Return Value:
-// 'true' if the stack empty bit is set; 'false' otherwise.
-
-bool jitIsStackEmpty(IL_OFFSETX offsx)
-{
- assert(offsx != BAD_IL_OFFSET);
-
- switch ((int)offsx) // Need the cast since offs is unsigned and the case statements are comparing to signed.
- {
- case ICorDebugInfo::NO_MAPPING:
- case ICorDebugInfo::PROLOG:
- case ICorDebugInfo::EPILOG:
- return true;
-
- default:
- return (offsx & IL_OFFSETX_STKBIT) == 0;
- }
-}
-
-//------------------------------------------------------------------------
-// jitIsCallInstruction: Does the IL offset have the call instruction bit set?
-// Asserts if passed BAD_IL_OFFSET.
-//
-// Arguments:
-// offsx - the IL_OFFSETX value to check
-//
-// Return Value:
-// 'true' if the call instruction bit is set; 'false' otherwise.
-
-bool jitIsCallInstruction(IL_OFFSETX offsx)
-{
- assert(offsx != BAD_IL_OFFSET);
-
- switch ((int)offsx) // Need the cast since offs is unsigned and the case statements are comparing to signed.
- {
- case ICorDebugInfo::NO_MAPPING:
- case ICorDebugInfo::PROLOG:
- case ICorDebugInfo::EPILOG:
- return false;
-
- default:
- return (offsx & IL_OFFSETX_CALLINSTRUCTIONBIT) != 0;
- }
-}
-
-/*****************************************************************************/
-
-void CodeGen::genEnsureCodeEmitted(IL_OFFSETX offsx)
+void CodeGen::genEnsureCodeEmitted(const DebugInfo& di)
{
if (!compiler->opts.compDbgCode)
{
return;
}
- if (offsx == BAD_IL_OFFSET)
+ if (!di.IsValid())
{
return;
}
- /* If other IL were offsets reported, skip */
+ // If other IL were offsets reported, skip
- if (compiler->genIPmappingLast == nullptr)
+ if (compiler->genIPmappings.size() <= 0)
{
return;
}
- if (compiler->genIPmappingLast->ipmdILoffsx != offsx)
+ const IPmappingDsc& prev = compiler->genIPmappings.back();
+ if (prev.ipmdLoc != di.GetLocation())
{
return;
}
- /* offsx was the last reported offset. Make sure that we generated native code */
+ // di represents the last reported offset. Make sure that we generated native code
- if (compiler->genIPmappingLast->ipmdNativeLoc.IsCurrentLocation(GetEmitter()))
+ if (prev.ipmdNativeLoc.IsCurrentLocation(GetEmitter()))
{
instGen(INS_nop);
}
}
-/*****************************************************************************
- *
- * Shut down the IP-mapping logic, report the info to the EE.
- */
-
+//------------------------------------------------------------------------
+// genIPmappingGen: Shut down the IP-mapping logic, report the info to the EE.
+//
void CodeGen::genIPmappingGen()
{
if (!compiler->opts.compDbgInfo)
@@ -10749,136 +10560,101 @@ void CodeGen::genIPmappingGen()
}
#endif
- if (compiler->genIPmappingList == nullptr)
+ if (compiler->genIPmappings.size() <= 0)
{
compiler->eeSetLIcount(0);
compiler->eeSetLIdone();
return;
}
- Compiler::IPmappingDsc* tmpMapping;
- Compiler::IPmappingDsc* prevMapping;
- unsigned mappingCnt;
- UNATIVE_OFFSET lastNativeOfs;
-
- /* First count the number of distinct mapping records */
-
- mappingCnt = 0;
- lastNativeOfs = UNATIVE_OFFSET(~0);
-
- for (prevMapping = nullptr, tmpMapping = compiler->genIPmappingList; tmpMapping != nullptr;
- tmpMapping = tmpMapping->ipmdNext)
+ UNATIVE_OFFSET prevNativeOfs = UNATIVE_OFFSET(~0);
+ for (jitstd::list::iterator it = compiler->genIPmappings.begin();
+ it != compiler->genIPmappings.end();)
{
- IL_OFFSETX srcIP = tmpMapping->ipmdILoffsx;
-
- // Managed RetVal - since new sequence points are emitted to identify IL calls,
- // make sure that those are not filtered and do not interfere with filtering of
- // other sequence points.
- if (jitIsCallInstruction(srcIP))
+ UNATIVE_OFFSET dscNativeOfs = it->ipmdNativeLoc.CodeOffset(GetEmitter());
+ if (dscNativeOfs != prevNativeOfs)
{
- mappingCnt++;
+ prevNativeOfs = dscNativeOfs;
+ ++it;
continue;
}
- UNATIVE_OFFSET nextNativeOfs = tmpMapping->ipmdNativeLoc.CodeOffset(GetEmitter());
+ // If we have a previous offset we should have a previous mapping.
+ assert(it != compiler->genIPmappings.begin());
+ jitstd::list::iterator prev = it;
+ --prev;
- if (nextNativeOfs != lastNativeOfs)
+ // Prev and current mappings have same native offset.
+ // If one does not map to IL then remove that one.
+ if (prev->ipmdKind == IPmappingDscKind::NoMapping)
{
- mappingCnt++;
- lastNativeOfs = nextNativeOfs;
- prevMapping = tmpMapping;
+ compiler->genIPmappings.erase(prev);
+ ++it;
continue;
}
- /* If there are mappings with the same native offset, then:
- o If one of them is NO_MAPPING, ignore it
- o If one of them is a label, report that and ignore the other one
- o Else report the higher IL offset
- */
-
- PREFIX_ASSUME(prevMapping != nullptr); // We would exit before if this was true
- if (prevMapping->ipmdILoffsx == (IL_OFFSETX)ICorDebugInfo::NO_MAPPING)
- {
- // If the previous entry was NO_MAPPING, ignore it
- prevMapping->ipmdNativeLoc.Init();
- prevMapping = tmpMapping;
- }
- else if (srcIP == (IL_OFFSETX)ICorDebugInfo::NO_MAPPING)
- {
- // If the current entry is NO_MAPPING, ignore it
- // Leave prevMapping unchanged as tmpMapping is no longer valid
- tmpMapping->ipmdNativeLoc.Init();
- }
- else if (srcIP == (IL_OFFSETX)ICorDebugInfo::EPILOG || srcIP == 0)
+ if (it->ipmdKind == IPmappingDscKind::NoMapping)
{
- // counting for special cases: see below
- mappingCnt++;
- prevMapping = tmpMapping;
+ it = compiler->genIPmappings.erase(it);
+ continue;
}
- else
- {
- noway_assert(prevMapping != nullptr);
- noway_assert(!prevMapping->ipmdNativeLoc.Valid() ||
- lastNativeOfs == prevMapping->ipmdNativeLoc.CodeOffset(GetEmitter()));
- /* The previous block had the same native offset. We have to
- discard one of the mappings. Simply reinitialize ipmdNativeLoc
- and prevMapping will be ignored later. */
-
- if (prevMapping->ipmdIsLabel)
- {
- // Leave prevMapping unchanged as tmpMapping is no longer valid
- tmpMapping->ipmdNativeLoc.Init();
- }
- else
- {
- prevMapping->ipmdNativeLoc.Init();
- prevMapping = tmpMapping;
- }
+ // Both have mappings.
+ // If previous is the prolog, keep both if this one is at IL offset 0.
+ // (TODO: Why? Debugger has no problem breaking on the prolog mapping
+ // it seems.)
+ if ((prev->ipmdKind == IPmappingDscKind::Prolog) && (it->ipmdKind == IPmappingDscKind::Normal) &&
+ (it->ipmdLoc.GetOffset() == 0))
+ {
+ ++it;
+ continue;
}
- }
-
- /* Tell them how many mapping records we've got */
- compiler->eeSetLIcount(mappingCnt);
-
- /* Now tell them about the mappings */
-
- mappingCnt = 0;
- lastNativeOfs = UNATIVE_OFFSET(~0);
-
- for (tmpMapping = compiler->genIPmappingList; tmpMapping != nullptr; tmpMapping = tmpMapping->ipmdNext)
- {
- // Do we have to skip this record ?
- if (!tmpMapping->ipmdNativeLoc.Valid())
+ // For the special case of an IL instruction with no body followed by
+ // the epilog (say ret void immediately preceding the method end), we
+ // leave both entries in, so that we'll stop at the (empty) ret
+ // statement if the user tries to put a breakpoint there, and then have
+ // the option of seeing the epilog or not based on SetUnmappedStopMask
+ // for the stepper.
+ if (it->ipmdKind == IPmappingDscKind::Epilog)
{
+ ++it;
continue;
}
- UNATIVE_OFFSET nextNativeOfs = tmpMapping->ipmdNativeLoc.CodeOffset(GetEmitter());
- IL_OFFSETX srcIP = tmpMapping->ipmdILoffsx;
-
- if (jitIsCallInstruction(srcIP))
+ // For managed return values we store all calls. Keep both in this case
+ // too.
+ if (((prev->ipmdKind == IPmappingDscKind::Normal) && (prev->ipmdLoc.IsCall())) ||
+ ((it->ipmdKind == IPmappingDscKind::Normal) && (it->ipmdLoc.IsCall())))
{
- compiler->eeSetLIinfo(mappingCnt++, nextNativeOfs, jitGetILoffs(srcIP), jitIsStackEmpty(srcIP), true);
+ ++it;
+ continue;
}
- else if (nextNativeOfs != lastNativeOfs)
+
+ // Otherwise report the higher offset unless the previous mapping is a
+ // label.
+ if (prev->ipmdIsLabel)
{
- compiler->eeSetLIinfo(mappingCnt++, nextNativeOfs, jitGetILoffsAny(srcIP), jitIsStackEmpty(srcIP), false);
- lastNativeOfs = nextNativeOfs;
+ it = compiler->genIPmappings.erase(it);
}
- else if (srcIP == (IL_OFFSETX)ICorDebugInfo::EPILOG || srcIP == 0)
+ else
{
- // For the special case of an IL instruction with no body
- // followed by the epilog (say ret void immediately preceding
- // the method end), we put two entries in, so that we'll stop
- // at the (empty) ret statement if the user tries to put a
- // breakpoint there, and then have the option of seeing the
- // epilog or not based on SetUnmappedStopMask for the stepper.
- compiler->eeSetLIinfo(mappingCnt++, nextNativeOfs, jitGetILoffsAny(srcIP), jitIsStackEmpty(srcIP), false);
+ compiler->genIPmappings.erase(prev);
+ ++it;
}
}
+ // Tell them how many mapping records we've got
+
+ compiler->eeSetLIcount(static_cast(compiler->genIPmappings.size()));
+
+ // Now tell them about the mappings
+ unsigned int mappingIdx = 0;
+ for (const IPmappingDsc& dsc : compiler->genIPmappings)
+ {
+ compiler->eeSetLIinfo(mappingIdx++, dsc.ipmdNativeLoc.CodeOffset(GetEmitter()), dsc.ipmdKind, dsc.ipmdLoc);
+ }
+
#if 0
// TODO-Review:
//This check is disabled. It is always true that any time this check asserts, the debugger would have a
@@ -10899,12 +10675,12 @@ void CodeGen::genIPmappingGen()
if ((block->bbRefs > 1) && (stmt != nullptr))
{
bool found = false;
- if (stmt->GetILOffsetX() != BAD_IL_OFFSET)
+ DebugInfo rootInfo = stmt->GetDebugInfo().GetRoot();
+ if (rootInfo.IsValid())
{
- IL_OFFSET ilOffs = jitGetILoffs(stmt->GetILOffsetX());
- for (unsigned i = 0; i < eeBoundariesCount; ++i)
+ for (unsigned i = 0; i < compiler->eeBoundariesCount; ++i)
{
- if (eeBoundaries[i].ilOffset == ilOffs)
+ if (compiler->eeBoundaries[i].ilOffset == rootInfo.GetLocation().GetOffset())
{
found = true;
break;
@@ -10920,6 +10696,87 @@ void CodeGen::genIPmappingGen()
compiler->eeSetLIdone();
}
+#ifdef DEBUG
+void CodeGen::genDumpPreciseDebugInfoInlineTree(FILE* file, InlineContext* context, bool* first)
+{
+ if (context->GetSibling() != nullptr)
+ {
+ genDumpPreciseDebugInfoInlineTree(file, context->GetSibling(), first);
+ }
+
+ if (context->IsSuccess())
+ {
+ if (!*first)
+ {
+ fprintf(file, ",");
+ }
+
+ *first = false;
+
+ fprintf(file, "{\"Ordinal\":%u,", context->GetOrdinal());
+ fprintf(file, "\"MethodID\":%lld,", (INT64)context->GetCallee());
+ const char* className;
+ const char* methodName = compiler->eeGetMethodName(context->GetCallee(), &className);
+ fprintf(file, "\"MethodName\":\"%s\",", methodName);
+ fprintf(file, "\"Inlinees\":[");
+ if (context->GetChild() != nullptr)
+ {
+ bool childFirst = true;
+ genDumpPreciseDebugInfoInlineTree(file, context->GetChild(), &childFirst);
+ }
+ fprintf(file, "]}");
+ }
+}
+
+void CodeGen::genDumpPreciseDebugInfo()
+{
+ if (JitConfig.JitDumpPreciseDebugInfoFile() == nullptr)
+ return;
+
+ static CritSecObject s_critSect;
+ CritSecHolder holder(s_critSect);
+
+ FILE* file = _wfopen(JitConfig.JitDumpPreciseDebugInfoFile(), W("a"));
+ if (file == nullptr)
+ return;
+
+ // MethodID in ETW events are the method handles.
+ fprintf(file, "{\"MethodID\":%lld,", (INT64)compiler->info.compMethodHnd);
+ // Print inline tree.
+ fprintf(file, "\"InlineTree\":");
+
+ bool first = true;
+ genDumpPreciseDebugInfoInlineTree(file, compiler->compInlineContext, &first);
+ fprintf(file, ",\"Mappings\":[");
+ first = true;
+ for (PreciseIPMapping& mapping : compiler->genPreciseIPmappings)
+ {
+ if (!first)
+ {
+ fprintf(file, ",");
+ }
+
+ first = false;
+
+ fprintf(file, "{\"NativeOffset\":%u,\"InlineContext\":%u,\"ILOffset\":%u}",
+ mapping.nativeLoc.CodeOffset(GetEmitter()), mapping.debugInfo.GetInlineContext()->GetOrdinal(),
+ mapping.debugInfo.GetLocation().GetOffset());
+ }
+
+ fprintf(file, "]}\n");
+
+ fclose(file);
+}
+
+void CodeGen::genAddPreciseIPMappingHere(const DebugInfo& di)
+{
+ PreciseIPMapping mapping;
+ mapping.nativeLoc.CaptureLocation(GetEmitter());
+ mapping.debugInfo = di;
+ compiler->genPreciseIPmappings.push_back(mapping);
+}
+#endif
+
/*============================================================================
*
* These are empty stubs to help the late dis-assembler to compile
@@ -11262,7 +11119,7 @@ void CodeGen::genStructReturn(GenTree* treeNode)
LclVarDsc* varDsc = nullptr;
if (actualOp1->OperIs(GT_LCL_VAR))
{
- varDsc = compiler->lvaGetDesc(actualOp1->AsLclVar()->GetLclNum());
+ varDsc = compiler->lvaGetDesc(actualOp1->AsLclVar());
retTypeDesc.InitializeStructReturnType(compiler, varDsc->GetStructHnd(), compiler->info.compCallConv);
assert(varDsc->lvIsMultiRegRet);
}
@@ -11291,7 +11148,7 @@ void CodeGen::genStructReturn(GenTree* treeNode)
else if (actualOp1->OperIs(GT_LCL_VAR) && !actualOp1->AsLclVar()->IsMultiReg())
{
GenTreeLclVar* lclNode = actualOp1->AsLclVar();
- LclVarDsc* varDsc = compiler->lvaGetDesc(lclNode->GetLclNum());
+ LclVarDsc* varDsc = compiler->lvaGetDesc(lclNode);
assert(varDsc->lvIsMultiRegRet);
int offset = 0;
for (unsigned i = 0; i < regCount; ++i)
@@ -11673,7 +11530,7 @@ regNumber CodeGen::genRegCopy(GenTree* treeNode, unsigned multiRegIndex)
var_types type;
if (op1->IsMultiRegLclVar())
{
- LclVarDsc* parentVarDsc = compiler->lvaGetDesc(op1->AsLclVar()->GetLclNum());
+ LclVarDsc* parentVarDsc = compiler->lvaGetDesc(op1->AsLclVar());
unsigned fieldVarNum = parentVarDsc->lvFieldLclStart + multiRegIndex;
LclVarDsc* fieldVarDsc = compiler->lvaGetDesc(fieldVarNum);
type = fieldVarDsc->TypeGet();
@@ -11728,8 +11585,8 @@ void CodeGen::genStackPointerCheck(bool doStackPointerCheck, unsigned lvaStackPo
{
if (doStackPointerCheck)
{
- noway_assert(lvaStackPointerVar != 0xCCCCCCCC && compiler->lvaTable[lvaStackPointerVar].lvDoNotEnregister &&
- compiler->lvaTable[lvaStackPointerVar].lvOnFrame);
+ noway_assert(lvaStackPointerVar != 0xCCCCCCCC && compiler->lvaGetDesc(lvaStackPointerVar)->lvDoNotEnregister &&
+ compiler->lvaGetDesc(lvaStackPointerVar)->lvOnFrame);
GetEmitter()->emitIns_S_R(INS_cmp, EA_PTRSIZE, REG_SPBASE, lvaStackPointerVar, 0);
BasicBlock* sp_check = genCreateTempLabel();
@@ -12565,7 +12422,19 @@ void CodeGenInterface::VariableLiveKeeper::dumpLvaVariableLiveRanges() const
void CodeGen::genPoisonFrame(regMaskTP regLiveIn)
{
assert(compiler->compShouldPoisonFrame());
- assert((regLiveIn & genRegMask(REG_SCRATCH)) == 0);
+#if defined(TARGET_XARCH)
+ regNumber poisonValReg = REG_EAX;
+ assert((regLiveIn & (RBM_EDI | RBM_ECX | RBM_EAX)) == 0);
+#else
+ regNumber poisonValReg = REG_SCRATCH;
+ assert((regLiveIn & (genRegMask(REG_SCRATCH) | RBM_ARG_0 | RBM_ARG_1 | RBM_ARG_2)) == 0);
+#endif
+
+#ifdef TARGET_64BIT
+ const ssize_t poisonVal = (ssize_t)0xcdcdcdcdcdcdcdcd;
+#else
+ const ssize_t poisonVal = (ssize_t)0xcdcdcdcd;
+#endif
// The first time we need to poison something we will initialize a register to the largest immediate cccccccc that
// we can fit.
@@ -12580,39 +12449,63 @@ void CodeGen::genPoisonFrame(regMaskTP regLiveIn)
assert(varDsc->lvOnFrame);
- if (!hasPoisonImm)
+ unsigned int size = compiler->lvaLclSize(varNum);
+ if ((size / TARGET_POINTER_SIZE) > 16)
{
-#ifdef TARGET_64BIT
- instGen_Set_Reg_To_Imm(EA_8BYTE, REG_SCRATCH, (ssize_t)0xcdcdcdcdcdcdcdcd);
+ // This will require more than 16 instructions, switch to rep stosd/memset call.
+ CLANG_FORMAT_COMMENT_ANCHOR;
+#if defined(TARGET_XARCH)
+ GetEmitter()->emitIns_R_S(INS_lea, EA_PTRSIZE, REG_EDI, (int)varNum, 0);
+ assert(size % 4 == 0);
+ instGen_Set_Reg_To_Imm(EA_4BYTE, REG_ECX, size / 4);
+ // On xarch we can leave the value in eax and only set eax once
+ // since rep stosd does not kill eax.
+ if (!hasPoisonImm)
+ {
+ instGen_Set_Reg_To_Imm(EA_PTRSIZE, REG_EAX, poisonVal);
+ hasPoisonImm = true;
+ }
+ instGen(INS_r_stosd);
#else
- instGen_Set_Reg_To_Imm(EA_4BYTE, REG_SCRATCH, (ssize_t)0xcdcdcdcd);
+ GetEmitter()->emitIns_R_S(INS_lea, EA_PTRSIZE, REG_ARG_0, (int)varNum, 0);
+ instGen_Set_Reg_To_Imm(EA_4BYTE, REG_ARG_1, static_cast(poisonVal));
+ instGen_Set_Reg_To_Imm(EA_4BYTE, REG_ARG_2, size);
+ genEmitHelperCall(CORINFO_HELP_MEMSET, 0, EA_UNKNOWN);
+ // May kill REG_SCRATCH, so we need to reload it.
+ hasPoisonImm = false;
#endif
- hasPoisonImm = true;
}
+ else
+ {
+ if (!hasPoisonImm)
+ {
+ instGen_Set_Reg_To_Imm(EA_PTRSIZE, poisonValReg, poisonVal);
+ hasPoisonImm = true;
+ }
// For 64-bit we check if the local is 8-byte aligned. For 32-bit, we assume everything is always 4-byte aligned.
#ifdef TARGET_64BIT
- bool fpBased;
- int addr = compiler->lvaFrameAddress((int)varNum, &fpBased);
+ bool fpBased;
+ int addr = compiler->lvaFrameAddress((int)varNum, &fpBased);
#else
- int addr = 0;
+ int addr = 0;
#endif
- int size = (int)compiler->lvaLclSize(varNum);
- int end = addr + size;
- for (int offs = addr; offs < end;)
- {
-#ifdef TARGET_64BIT
- if ((offs % 8) == 0 && end - offs >= 8)
+ int end = addr + (int)size;
+ for (int offs = addr; offs < end;)
{
- GetEmitter()->emitIns_S_R(ins_Store(TYP_LONG), EA_8BYTE, REG_SCRATCH, (int)varNum, offs - addr);
- offs += 8;
- continue;
- }
+#ifdef TARGET_64BIT
+ if ((offs % 8) == 0 && end - offs >= 8)
+ {
+ GetEmitter()->emitIns_S_R(ins_Store(TYP_LONG), EA_8BYTE, REG_SCRATCH, (int)varNum, offs - addr);
+ offs += 8;
+ continue;
+ }
#endif
- assert((offs % 4) == 0 && end - offs >= 4);
- GetEmitter()->emitIns_S_R(ins_Store(TYP_INT), EA_4BYTE, REG_SCRATCH, (int)varNum, offs - addr);
- offs += 4;
+ assert((offs % 4) == 0 && end - offs >= 4);
+ GetEmitter()->emitIns_S_R(ins_Store(TYP_INT), EA_4BYTE, REG_SCRATCH, (int)varNum, offs - addr);
+ offs += 4;
+ }
}
}
}
diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp
index e0f48954c65cd3..38ac9a92d86d4a 100644
--- a/src/coreclr/jit/codegenlinear.cpp
+++ b/src/coreclr/jit/codegenlinear.cpp
@@ -174,6 +174,7 @@ void CodeGen::genCodeForBBlist()
for (block = compiler->fgFirstBB; block != nullptr; block = block->bbNext)
{
+
#ifdef DEBUG
if (compiler->verbose)
{
@@ -343,8 +344,8 @@ void CodeGen::genCodeForBBlist()
#if FEATURE_LOOP_ALIGN
if (GetEmitter()->emitEndsWithAlignInstr())
{
- // we had better be planning on starting a new IG
- assert(needLabel);
+ // Force new label if current IG ends with an align instruction.
+ needLabel = true;
}
#endif
@@ -376,7 +377,7 @@ void CodeGen::genCodeForBBlist()
!compiler->fgBBisScratch(block)) // If the block is the distinguished first scratch block, then no need to
// emit a NO_MAPPING entry, immediately after the prolog.
{
- genIPmappingAdd((IL_OFFSETX)ICorDebugInfo::NO_MAPPING, true);
+ genIPmappingAdd(IPmappingDscKind::NoMapping, DebugInfo(), true);
}
bool firstMapping = true;
@@ -423,20 +424,33 @@ void CodeGen::genCodeForBBlist()
}
}
}
+
+ bool addPreciseMappings =
+ (JitConfig.JitDumpPreciseDebugInfoFile() != nullptr) || (JitConfig.JitDisasmWithDebugInfo() != 0);
#endif // DEBUG
- IL_OFFSETX currentILOffset = BAD_IL_OFFSET;
+ DebugInfo currentDI;
for (GenTree* node : LIR::AsRange(block))
{
// Do we have a new IL offset?
if (node->OperGet() == GT_IL_OFFSET)
{
GenTreeILOffset* ilOffset = node->AsILOffset();
- genEnsureCodeEmitted(currentILOffset);
- currentILOffset = ilOffset->gtStmtILoffsx;
- genIPmappingAdd(currentILOffset, firstMapping);
- firstMapping = false;
+ DebugInfo rootDI = ilOffset->gtStmtDI.GetRoot();
+ if (rootDI.IsValid())
+ {
+ genEnsureCodeEmitted(currentDI);
+ currentDI = rootDI;
+ genIPmappingAdd(IPmappingDscKind::Normal, currentDI, firstMapping);
+ firstMapping = false;
+ }
+
#ifdef DEBUG
+ if (addPreciseMappings && ilOffset->gtStmtDI.IsValid())
+ {
+ genAddPreciseIPMappingHere(ilOffset->gtStmtDI);
+ }
+
assert(ilOffset->gtStmtLastILoffs <= compiler->info.compILCodeSize ||
ilOffset->gtStmtLastILoffs == BAD_IL_OFFSET);
@@ -529,7 +543,7 @@ void CodeGen::genCodeForBBlist()
//
// This can lead to problems when debugging the generated code. To prevent these issues, make sure
// we've generated code for the last IL offset we saw in the block.
- genEnsureCodeEmitted(currentILOffset);
+ genEnsureCodeEmitted(currentDI);
/* Is this the last block, and are there any open scopes left ? */
@@ -778,21 +792,29 @@ void CodeGen::genCodeForBBlist()
}
#if FEATURE_LOOP_ALIGN
+ if (block->hasAlign())
+ {
+ // If this block has 'align' instruction in the end (identified by BBF_HAS_ALIGN),
+ // then need to add align instruction in the current "block".
+ //
+ // For non-adaptive alignment, add alignment instruction of size depending on the
+ // compJitAlignLoopBoundary.
+ // For adaptive alignment, alignment instruction will always be of 15 bytes for xarch
+ // and 16 bytes for arm64.
+ assert(ShouldAlignLoops());
- // If next block is the first block of a loop (identified by BBF_LOOP_ALIGN),
- // then need to add align instruction in current "block". Also mark the
- // corresponding IG with IGF_LOOP_ALIGN to know that there will be align
- // instructions at the end of that IG.
- //
- // For non-adaptive alignment, add alignment instruction of size depending on the
- // compJitAlignLoopBoundary.
- // For adaptive alignment, alignment instruction will always be of 15 bytes.
+ GetEmitter()->emitLoopAlignment(DEBUG_ARG1(block->bbJumpKind == BBJ_ALWAYS));
+ }
if ((block->bbNext != nullptr) && (block->bbNext->isLoopAlign()))
{
- assert(ShouldAlignLoops());
-
- GetEmitter()->emitLoopAlignment();
+ if (compiler->opts.compJitHideAlignBehindJmp)
+ {
+ // The current IG is the one that is just before the IG having loop start.
+ // Establish a connection of recent align instruction emitted to the loop
+ // it actually is aligning using 'idaLoopHeadPredIG'.
+ GetEmitter()->emitConnectAlignInstrWithCurIG();
+ }
}
#endif
@@ -855,7 +877,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
void CodeGen::genSpillVar(GenTree* tree)
{
unsigned varNum = tree->AsLclVarCommon()->GetLclNum();
- LclVarDsc* varDsc = &(compiler->lvaTable[varNum]);
+ LclVarDsc* varDsc = compiler->lvaGetDesc(varNum);
assert(varDsc->lvIsRegCandidate());
@@ -1184,7 +1206,7 @@ void CodeGen::genUnspillRegIfNeeded(GenTree* tree)
unspillTree->gtFlags &= ~GTF_SPILLED;
GenTreeLclVar* lcl = unspillTree->AsLclVar();
- LclVarDsc* varDsc = compiler->lvaGetDesc(lcl->GetLclNum());
+ LclVarDsc* varDsc = compiler->lvaGetDesc(lcl);
var_types spillType = varDsc->GetRegisterType(lcl);
assert(spillType != TYP_UNDEF);
@@ -1223,7 +1245,7 @@ void CodeGen::genUnspillRegIfNeeded(GenTree* tree)
assert(tree == unspillTree);
GenTreeLclVar* lclNode = unspillTree->AsLclVar();
- LclVarDsc* varDsc = compiler->lvaGetDesc(lclNode->GetLclNum());
+ LclVarDsc* varDsc = compiler->lvaGetDesc(lclNode);
unsigned regCount = varDsc->lvFieldCnt;
for (unsigned i = 0; i < regCount; ++i)
@@ -1453,7 +1475,7 @@ regNumber CodeGen::genConsumeReg(GenTree* tree)
if (genIsRegCandidateLocal(tree))
{
GenTreeLclVarCommon* lcl = tree->AsLclVarCommon();
- LclVarDsc* varDsc = &compiler->lvaTable[lcl->GetLclNum()];
+ LclVarDsc* varDsc = compiler->lvaGetDesc(lcl);
if (varDsc->GetRegNum() != REG_STK)
{
var_types regType = varDsc->GetRegisterType(lcl);
@@ -1476,7 +1498,7 @@ regNumber CodeGen::genConsumeReg(GenTree* tree)
assert(tree->gtHasReg());
GenTreeLclVarCommon* lcl = tree->AsLclVar();
- LclVarDsc* varDsc = &compiler->lvaTable[lcl->GetLclNum()];
+ LclVarDsc* varDsc = compiler->lvaGetDesc(lcl);
assert(varDsc->lvLRACandidate);
if (varDsc->GetRegNum() == REG_STK)
@@ -1497,7 +1519,7 @@ regNumber CodeGen::genConsumeReg(GenTree* tree)
unsigned firstFieldVarNum = varDsc->lvFieldLclStart;
for (unsigned i = 0; i < varDsc->lvFieldCnt; ++i)
{
- LclVarDsc* fldVarDsc = &(compiler->lvaTable[firstFieldVarNum + i]);
+ LclVarDsc* fldVarDsc = compiler->lvaGetDesc(firstFieldVarNum + i);
assert(fldVarDsc->lvLRACandidate);
regNumber reg;
if (tree->OperIs(GT_COPY, GT_RELOAD) && (tree->AsCopyOrReload()->GetRegByIndex(i) != REG_NA))
@@ -1574,12 +1596,21 @@ void CodeGen::genConsumeRegs(GenTree* tree)
{
genConsumeAddress(tree);
}
+#ifdef TARGET_ARM64
+ else if (tree->OperIs(GT_BFIZ))
+ {
+ // Can be contained as part of LEA on ARM64
+ GenTreeCast* cast = tree->gtGetOp1()->AsCast();
+ assert(cast->isContained());
+ genConsumeAddress(cast->CastOp());
+ }
+#endif
else if (tree->OperIsLocalRead())
{
// A contained lcl var must be living on stack and marked as reg optional, or not be a
// register candidate.
unsigned varNum = tree->AsLclVarCommon()->GetLclNum();
- LclVarDsc* varDsc = compiler->lvaTable + varNum;
+ LclVarDsc* varDsc = compiler->lvaGetDesc(varNum);
noway_assert(varDsc->GetRegNum() == REG_STK);
noway_assert(tree->IsRegOptional() || !varDsc->lvLRACandidate);
@@ -1592,14 +1623,18 @@ void CodeGen::genConsumeRegs(GenTree* tree)
else if (tree->OperIs(GT_HWINTRINSIC))
{
// Only load/store HW intrinsics can be contained (and the address may also be contained).
- HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(tree->AsHWIntrinsic()->gtHWIntrinsicId);
+ HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(tree->AsHWIntrinsic()->GetHWIntrinsicId());
assert((category == HW_Category_MemoryLoad) || (category == HW_Category_MemoryStore));
- int numArgs = HWIntrinsicInfo::lookupNumArgs(tree->AsHWIntrinsic());
- genConsumeAddress(tree->gtGetOp1());
+ size_t numArgs = tree->AsHWIntrinsic()->GetOperandCount();
+ genConsumeAddress(tree->AsHWIntrinsic()->Op(1));
if (category == HW_Category_MemoryStore)
{
- assert((numArgs == 2) && !tree->gtGetOp2()->isContained());
- genConsumeReg(tree->gtGetOp2());
+ assert(numArgs == 2);
+
+ GenTree* op2 = tree->AsHWIntrinsic()->Op(2);
+ assert(op2->isContained());
+
+ genConsumeReg(op2);
}
else
{
@@ -1643,7 +1678,6 @@ void CodeGen::genConsumeRegs(GenTree* tree)
// Return Value:
// None.
//
-
void CodeGen::genConsumeOperands(GenTreeOp* tree)
{
GenTree* firstOp = tree->gtOp1;
@@ -1659,54 +1693,25 @@ void CodeGen::genConsumeOperands(GenTreeOp* tree)
}
}
-#ifdef FEATURE_HW_INTRINSICS
+#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
//------------------------------------------------------------------------
-// genConsumeHWIntrinsicOperands: Do liveness update for the operands of a GT_HWINTRINSIC node
+// genConsumeOperands: Do liveness update for the operands of a multi-operand node,
+// currently GT_SIMD or GT_HWINTRINSIC
//
// Arguments:
-// node - the GenTreeHWIntrinsic node whose operands will have their liveness updated.
+// tree - the GenTreeMultiOp whose operands will have their liveness updated.
//
// Return Value:
// None.
//
-
-void CodeGen::genConsumeHWIntrinsicOperands(GenTreeHWIntrinsic* node)
+void CodeGen::genConsumeMultiOpOperands(GenTreeMultiOp* tree)
{
- int numArgs = HWIntrinsicInfo::lookupNumArgs(node);
- GenTree* op1 = node->gtGetOp1();
- if (op1 == nullptr)
- {
- assert((numArgs == 0) && (node->gtGetOp2() == nullptr));
- return;
- }
- if (op1->OperIs(GT_LIST))
- {
- int foundArgs = 0;
- assert(node->gtGetOp2() == nullptr);
- for (GenTreeArgList* list = op1->AsArgList(); list != nullptr; list = list->Rest())
- {
- GenTree* operand = list->Current();
- genConsumeRegs(operand);
- foundArgs++;
- }
- assert(foundArgs == numArgs);
- }
- else
+ for (GenTree* operand : tree->Operands())
{
- genConsumeRegs(op1);
- GenTree* op2 = node->gtGetOp2();
- if (op2 != nullptr)
- {
- genConsumeRegs(op2);
- assert(numArgs == 2);
- }
- else
- {
- assert(numArgs == 1);
- }
+ genConsumeRegs(operand);
}
}
-#endif // FEATURE_HW_INTRINSICS
+#endif // defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
#if FEATURE_PUT_STRUCT_ARG_STK
//------------------------------------------------------------------------
@@ -2246,7 +2251,7 @@ void CodeGen::genProduceReg(GenTree* tree)
{
assert(compiler->lvaEnregMultiRegVars);
GenTreeLclVar* lclNode = tree->AsLclVar();
- LclVarDsc* varDsc = compiler->lvaGetDesc(lclNode->GetLclNum());
+ LclVarDsc* varDsc = compiler->lvaGetDesc(lclNode);
unsigned regCount = varDsc->lvFieldCnt;
for (unsigned i = 0; i < regCount; i++)
{
@@ -2302,7 +2307,7 @@ void CodeGen::genEmitCall(int callType,
X86_ARG(int argSize),
emitAttr retSize
MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
- IL_OFFSETX ilOffset,
+ const DebugInfo& di,
regNumber base,
bool isJump)
{
@@ -2324,7 +2329,7 @@ void CodeGen::genEmitCall(int callType,
gcInfo.gcVarPtrSetCur,
gcInfo.gcRegGCrefSetCur,
gcInfo.gcRegByrefSetCur,
- ilOffset, base, REG_NA, 0, 0, isJump);
+ di, base, REG_NA, 0, 0, isJump);
}
// clang-format on
@@ -2340,7 +2345,7 @@ void CodeGen::genEmitCallIndir(int callType,
X86_ARG(int argSize),
emitAttr retSize
MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
- IL_OFFSETX ilOffset,
+ const DebugInfo& di,
bool isJump)
{
#if !defined(TARGET_X86)
@@ -2365,7 +2370,7 @@ void CodeGen::genEmitCallIndir(int callType,
gcInfo.gcVarPtrSetCur,
gcInfo.gcRegGCrefSetCur,
gcInfo.gcRegByrefSetCur,
- ilOffset,
+ di,
iReg,
xReg,
indir->Scale(),
@@ -2553,7 +2558,7 @@ void CodeGen::genStoreLongLclVar(GenTree* treeNode)
GenTreeLclVarCommon* lclNode = treeNode->AsLclVarCommon();
unsigned lclNum = lclNode->GetLclNum();
- LclVarDsc* varDsc = &(compiler->lvaTable[lclNum]);
+ LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum);
assert(varDsc->TypeGet() == TYP_LONG);
assert(!varDsc->lvPromoted);
GenTree* op1 = treeNode->AsOp()->gtOp1;
@@ -2618,6 +2623,16 @@ void CodeGen::genCodeForJumpTrue(GenTreeOp* jtrue)
condition = GenCondition(GenCondition::P);
}
+
+ if (relop->MarkedForSignJumpOpt())
+ {
+ // If relop was previously marked for a signed jump check optimization because of SF flag
+ // reuse, replace jge/jl with jns/js.
+
+ assert(relop->OperGet() == GT_LT || relop->OperGet() == GT_GE);
+ condition = (relop->OperGet() == GT_LT) ? GenCondition(GenCondition::S) : GenCondition(GenCondition::NS);
+ }
+
#endif
inst_JCC(condition, compiler->compCurBB->bbJumpDest);
diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp
index 128de7c9fcd5b3..7b50388ad3138b 100644
--- a/src/coreclr/jit/codegenxarch.cpp
+++ b/src/coreclr/jit/codegenxarch.cpp
@@ -167,8 +167,8 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg)
// we are generating GS cookie check after a GT_RETURN block.
// Note: On Amd64 System V RDX is an arg register - REG_ARG_2 - as well
// as return register for two-register-returned structs.
- if (compiler->lvaKeepAliveAndReportThis() && compiler->lvaTable[compiler->info.compThisArg].lvIsInReg() &&
- (compiler->lvaTable[compiler->info.compThisArg].GetRegNum() == REG_ARG_0))
+ if (compiler->lvaKeepAliveAndReportThis() && compiler->lvaGetDesc(compiler->info.compThisArg)->lvIsInReg() &&
+ (compiler->lvaGetDesc(compiler->info.compThisArg)->GetRegNum() == REG_ARG_0))
{
regGSCheck = REG_ARG_1;
}
@@ -1197,9 +1197,9 @@ void CodeGen::genFloatReturn(GenTree* treeNode)
GenTree* op1 = treeNode->gtGetOp1();
// Spill the return value register from an XMM register to the stack, then load it on the x87 stack.
// If it already has a home location, use that. Otherwise, we need a temp.
- if (genIsRegCandidateLocal(op1) && compiler->lvaTable[op1->AsLclVarCommon()->GetLclNum()].lvOnFrame)
+ if (genIsRegCandidateLocal(op1) && compiler->lvaGetDesc(op1->AsLclVarCommon())->lvOnFrame)
{
- if (compiler->lvaTable[op1->AsLclVarCommon()->GetLclNum()].GetRegNum() != REG_STK)
+ if (compiler->lvaGetDesc(op1->AsLclVarCommon())->GetRegNum() != REG_STK)
{
op1->gtFlags |= GTF_SPILL;
inst_TT_RV(ins_Store(op1->gtType, compiler->isSIMDTypeLocalAligned(op1->AsLclVarCommon()->GetLclNum())),
@@ -1673,7 +1673,6 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
// This is handled at the time we call genConsumeReg() on the GT_COPY
break;
- case GT_LIST:
case GT_FIELD_LIST:
// Should always be marked contained.
assert(!"LIST, FIELD_LIST nodes should always be marked contained.");
@@ -1853,7 +1852,7 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
{
#ifdef DEBUG
char message[256];
- _snprintf_s(message, _countof(message), _TRUNCATE, "NYI: Unimplemented node type %s\n",
+ _snprintf_s(message, ArrLen(message), _TRUNCATE, "NYI: Unimplemented node type %s\n",
GenTree::OpName(treeNode->OperGet()));
NYIRAW(message);
#endif
@@ -2582,8 +2581,8 @@ void CodeGen::genLclHeap(GenTree* tree)
if (compiler->opts.compStackCheckOnRet)
{
noway_assert(compiler->lvaReturnSpCheck != 0xCCCCCCCC &&
- compiler->lvaTable[compiler->lvaReturnSpCheck].lvDoNotEnregister &&
- compiler->lvaTable[compiler->lvaReturnSpCheck].lvOnFrame);
+ compiler->lvaGetDesc(compiler->lvaReturnSpCheck)->lvDoNotEnregister &&
+ compiler->lvaGetDesc(compiler->lvaReturnSpCheck)->lvOnFrame);
GetEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_SPBASE, compiler->lvaReturnSpCheck, 0);
}
#endif
@@ -3492,7 +3491,7 @@ void CodeGen::genClearStackVec3ArgUpperBits()
for (unsigned varNum = 0; varNum < compiler->info.compArgsCount; varNum++)
{
- LclVarDsc* varDsc = &(compiler->lvaTable[varNum]);
+ const LclVarDsc* varDsc = compiler->lvaGetDesc(varNum);
assert(varDsc->lvIsParam);
// Does var has simd12 type?
@@ -4960,10 +4959,10 @@ void CodeGen::genCodeForSwap(GenTreeOp* tree)
assert(genIsRegCandidateLocal(tree->gtOp1) && genIsRegCandidateLocal(tree->gtOp2));
GenTreeLclVarCommon* lcl1 = tree->gtOp1->AsLclVarCommon();
- LclVarDsc* varDsc1 = &(compiler->lvaTable[lcl1->GetLclNum()]);
+ LclVarDsc* varDsc1 = compiler->lvaGetDesc(lcl1);
var_types type1 = varDsc1->TypeGet();
GenTreeLclVarCommon* lcl2 = tree->gtOp2->AsLclVarCommon();
- LclVarDsc* varDsc2 = &(compiler->lvaTable[lcl2->GetLclNum()]);
+ LclVarDsc* varDsc2 = compiler->lvaGetDesc(lcl2);
var_types type2 = varDsc2->TypeGet();
// We must have both int or both fp regs
@@ -5258,8 +5257,8 @@ void CodeGen::genCall(GenTreeCall* call)
if (compiler->opts.compStackCheckOnCall && call->gtCallType == CT_USER_FUNC)
{
noway_assert(compiler->lvaCallSpCheck != 0xCCCCCCCC &&
- compiler->lvaTable[compiler->lvaCallSpCheck].lvDoNotEnregister &&
- compiler->lvaTable[compiler->lvaCallSpCheck].lvOnFrame);
+ compiler->lvaGetDesc(compiler->lvaCallSpCheck)->lvDoNotEnregister &&
+ compiler->lvaGetDesc(compiler->lvaCallSpCheck)->lvOnFrame);
GetEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_SPBASE, compiler->lvaCallSpCheck, 0);
}
#endif // defined(DEBUG) && defined(TARGET_X86)
@@ -5384,8 +5383,8 @@ void CodeGen::genCall(GenTreeCall* call)
if (compiler->opts.compStackCheckOnCall && call->gtCallType == CT_USER_FUNC)
{
noway_assert(compiler->lvaCallSpCheck != 0xCCCCCCCC &&
- compiler->lvaTable[compiler->lvaCallSpCheck].lvDoNotEnregister &&
- compiler->lvaTable[compiler->lvaCallSpCheck].lvOnFrame);
+ compiler->lvaGetDesc(compiler->lvaCallSpCheck)->lvDoNotEnregister &&
+ compiler->lvaGetDesc(compiler->lvaCallSpCheck)->lvOnFrame);
if (!call->CallerPop() && (stackArgBytes != 0))
{
// ECX is trashed, so can be used to compute the expected SP. We saved the value of SP
@@ -5506,11 +5505,11 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA
// We don't want tail call helper calls that were converted from normal calls to get a record,
// so we skip this hash table lookup logic in that case.
- IL_OFFSETX ilOffset = BAD_IL_OFFSET;
+ DebugInfo di;
- if (compiler->opts.compDbgInfo && compiler->genCallSite2ILOffsetMap != nullptr && !call->IsTailCall())
+ if (compiler->opts.compDbgInfo && compiler->genCallSite2DebugInfoMap != nullptr && !call->IsTailCall())
{
- (void)compiler->genCallSite2ILOffsetMap->Lookup(call, &ilOffset);
+ (void)compiler->genCallSite2DebugInfoMap->Lookup(call, &di);
}
CORINFO_SIG_INFO* sigInfo = nullptr;
@@ -5562,7 +5561,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA
gcInfo.gcVarPtrSetCur,
gcInfo.gcRegGCrefSetCur,
gcInfo.gcRegByrefSetCur,
- ilOffset, REG_VIRTUAL_STUB_TARGET, REG_NA, 1, 0);
+ di, REG_VIRTUAL_STUB_TARGET, REG_NA, 1, 0);
// clang-format on
}
else
@@ -5583,7 +5582,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA
X86_ARG(argSizeForEmitter),
retSize
MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
- ilOffset,
+ di,
REG_NA,
call->IsFastTailCall());
// clang-format on
@@ -5605,7 +5604,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA
X86_ARG(argSizeForEmitter),
retSize
MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
- ilOffset,
+ di,
call->IsFastTailCall());
// clang-format on
}
@@ -5631,7 +5630,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA
X86_ARG(argSizeForEmitter),
retSize
MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
- ilOffset,
+ di,
target->GetRegNum(),
call->IsFastTailCall());
// clang-format on
@@ -5661,7 +5660,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA
gcInfo.gcVarPtrSetCur,
gcInfo.gcRegGCrefSetCur,
gcInfo.gcRegByrefSetCur,
- ilOffset, indirCellReg, REG_NA, 0, 0,
+ di, indirCellReg, REG_NA, 0, 0,
call->IsFastTailCall());
// clang-format on
}
@@ -5678,7 +5677,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA
X86_ARG(argSizeForEmitter),
retSize
MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
- ilOffset,
+ di,
REG_NA,
call->IsFastTailCall());
// clang-format on
@@ -5718,7 +5717,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA
X86_ARG(argSizeForEmitter),
retSize
MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
- ilOffset,
+ di,
REG_NA,
call->IsFastTailCall());
// clang-format on
@@ -5753,16 +5752,16 @@ void CodeGen::genJmpMethod(GenTree* jmp)
// But that would require us to deal with circularity while moving values around. Spilling
// to stack makes the implementation simple, which is not a bad trade off given Jmp calls
// are not frequent.
- for (varNum = 0; (varNum < compiler->info.compArgsCount); varNum++)
+ for (varNum = 0; varNum < compiler->info.compArgsCount; varNum++)
{
- varDsc = compiler->lvaTable + varNum;
+ varDsc = compiler->lvaGetDesc(varNum);
if (varDsc->lvPromoted)
{
noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
unsigned fieldVarNum = varDsc->lvFieldLclStart;
- varDsc = compiler->lvaTable + fieldVarNum;
+ varDsc = compiler->lvaGetDesc(fieldVarNum);
}
noway_assert(varDsc->lvIsParam);
@@ -5789,7 +5788,7 @@ void CodeGen::genJmpMethod(GenTree* jmp)
// assert should hold.
assert(varDsc->GetRegNum() != REG_STK);
- assert(!varDsc->lvIsStructField || (compiler->lvaTable[varDsc->lvParentLcl].lvFieldCnt == 1));
+ assert(!varDsc->lvIsStructField || (compiler->lvaGetDesc(varDsc->lvParentLcl)->lvFieldCnt == 1));
var_types storeType = varDsc->GetActualRegisterType(); // We own the memory and can use the full move.
GetEmitter()->emitIns_S_R(ins_Store(storeType), emitTypeSize(storeType), varDsc->GetRegNum(), varNum, 0);
@@ -5825,15 +5824,15 @@ void CodeGen::genJmpMethod(GenTree* jmp)
// Next move any un-enregistered register arguments back to their register.
regMaskTP fixedIntArgMask = RBM_NONE; // tracks the int arg regs occupying fixed args in case of a vararg method.
unsigned firstArgVarNum = BAD_VAR_NUM; // varNum of the first argument in case of a vararg method.
- for (varNum = 0; (varNum < compiler->info.compArgsCount); varNum++)
+ for (varNum = 0; varNum < compiler->info.compArgsCount; varNum++)
{
- varDsc = compiler->lvaTable + varNum;
+ varDsc = compiler->lvaGetDesc(varNum);
if (varDsc->lvPromoted)
{
noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
unsigned fieldVarNum = varDsc->lvFieldLclStart;
- varDsc = compiler->lvaTable + fieldVarNum;
+ varDsc = compiler->lvaGetDesc(fieldVarNum);
}
noway_assert(varDsc->lvIsParam);
@@ -6227,11 +6226,18 @@ void CodeGen::genCompareInt(GenTree* treeNode)
assert(genTypeSize(type) <= genTypeSize(TYP_I_IMPL));
// TYP_UINT and TYP_ULONG should not appear here, only small types can be unsigned
assert(!varTypeIsUnsigned(type) || varTypeIsSmall(type));
+ // Sign jump optimization should only be set the following check
+ assert((tree->gtFlags & GTF_RELOP_SJUMP_OPT) == 0);
if (canReuseFlags && emit->AreFlagsSetToZeroCmp(op1->GetRegNum(), emitTypeSize(type), tree->OperGet()))
{
JITDUMP("Not emitting compare due to flags being already set\n");
}
+ else if (canReuseFlags && emit->AreFlagsSetForSignJumpOpt(op1->GetRegNum(), emitTypeSize(type), tree))
+ {
+ JITDUMP("Not emitting compare due to sign being already set, follow up instr will transform jump\n");
+ tree->gtFlags |= GTF_RELOP_SJUMP_OPT;
+ }
else
{
emit->emitInsBinary(ins, emitTypeSize(type), op1, op2);
@@ -7141,8 +7147,7 @@ void CodeGen::genSSE41RoundOp(GenTreeOp* treeNode)
case GT_LCL_VAR:
{
- assert(srcNode->IsRegOptional() ||
- !compiler->lvaTable[srcNode->AsLclVar()->GetLclNum()].lvIsRegCandidate());
+ assert(srcNode->IsRegOptional() || !compiler->lvaGetDesc(srcNode->AsLclVar())->lvIsRegCandidate());
varNum = srcNode->AsLclVar()->GetLclNum();
offset = 0;
@@ -7256,7 +7261,7 @@ unsigned CodeGen::getBaseVarForPutArgStk(GenTree* treeNode)
// Since it is a fast tail call, the existence of first incoming arg is guaranteed
// because fast tail call requires that in-coming arg area of caller is >= out-going
// arg area required for tail call.
- LclVarDsc* varDsc = &(compiler->lvaTable[baseVarNum]);
+ LclVarDsc* varDsc = compiler->lvaGetDesc(baseVarNum);
assert(varDsc != nullptr);
#ifdef UNIX_AMD64_ABI
@@ -8323,9 +8328,9 @@ void CodeGen::genCreateAndStoreGCInfoX64(unsigned codeSize, unsigned prologSize
if (compiler->opts.IsReversePInvoke())
{
unsigned reversePInvokeFrameVarNumber = compiler->lvaReversePInvokeFrameVar;
- assert(reversePInvokeFrameVarNumber != BAD_VAR_NUM && reversePInvokeFrameVarNumber < compiler->lvaRefCount);
- LclVarDsc& reversePInvokeFrameVar = compiler->lvaTable[reversePInvokeFrameVarNumber];
- gcInfoEncoder->SetReversePInvokeFrameSlot(reversePInvokeFrameVar.GetStackOffset());
+ assert(reversePInvokeFrameVarNumber != BAD_VAR_NUM);
+ const LclVarDsc* reversePInvokeFrameVar = compiler->lvaGetDesc(reversePInvokeFrameVarNumber);
+ gcInfoEncoder->SetReversePInvokeFrameSlot(reversePInvokeFrameVar->GetStackOffset());
}
gcInfoEncoder->Build();
@@ -8408,7 +8413,7 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize,
gcInfo.gcVarPtrSetCur,
gcInfo.gcRegGCrefSetCur,
gcInfo.gcRegByrefSetCur,
- BAD_IL_OFFSET, // IL offset
+ DebugInfo(),
callTarget, // ireg
REG_NA, 0, 0, // xreg, xmul, disp
false // isJump
@@ -8900,9 +8905,9 @@ void CodeGen::genProfilingLeaveCallback(unsigned helper)
// If thisPtr needs to be kept alive and reported, it cannot be one of the callee trash
// registers that profiler callback kills.
- if (compiler->lvaKeepAliveAndReportThis() && compiler->lvaTable[compiler->info.compThisArg].lvIsInReg())
+ if (compiler->lvaKeepAliveAndReportThis() && compiler->lvaGetDesc(compiler->info.compThisArg)->lvIsInReg())
{
- regMaskTP thisPtrMask = genRegMask(compiler->lvaTable[compiler->info.compThisArg].GetRegNum());
+ regMaskTP thisPtrMask = genRegMask(compiler->lvaGetDesc(compiler->info.compThisArg)->GetRegNum());
noway_assert((RBM_PROFILER_LEAVE_TRASH & thisPtrMask) == 0);
}
@@ -8941,7 +8946,7 @@ void CodeGen::genProfilingLeaveCallback(unsigned helper)
// cannot use caller's SP offset since it is an estimate. For now we require the
// method to have at least a single arg so that we can use it to obtain caller's
// SP.
- LclVarDsc* varDsc = compiler->lvaTable;
+ LclVarDsc* varDsc = compiler->lvaGetDesc(0U);
NYI_IF((varDsc == nullptr) || !varDsc->lvIsParam, "Profiler ELT callback for a method without any params");
// lea rdx, [FramePointer + Arg0's offset]
@@ -8974,7 +8979,7 @@ void CodeGen::genProfilingLeaveCallback(unsigned helper)
}
else
{
- LclVarDsc* varDsc = compiler->lvaTable;
+ LclVarDsc* varDsc = compiler->lvaGetDesc(0U);
NYI_IF((varDsc == nullptr) || !varDsc->lvIsParam, "Profiler ELT callback for a method without any params");
// lea rdx, [FramePointer + Arg0's offset]
diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp
index a26a003a455fb9..e56c96b026c0b1 100644
--- a/src/coreclr/jit/compiler.cpp
+++ b/src/coreclr/jit/compiler.cpp
@@ -174,128 +174,6 @@ void Compiler::JitLogEE(unsigned level, const char* fmt, ...)
va_end(args);
}
-void Compiler::compDspSrcLinesByLineNum(unsigned line, bool seek)
-{
- if (!jitSrcFilePtr)
- {
- return;
- }
-
- if (jitCurSrcLine == line)
- {
- return;
- }
-
- if (jitCurSrcLine > line)
- {
- if (!seek)
- {
- return;
- }
-
- if (fseek(jitSrcFilePtr, 0, SEEK_SET) != 0)
- {
- printf("Compiler::compDspSrcLinesByLineNum: fseek returned an error.\n");
- }
- jitCurSrcLine = 0;
- }
-
- if (!seek)
- {
- printf(";\n");
- }
-
- do
- {
- char temp[128];
- size_t llen;
-
- if (!fgets(temp, sizeof(temp), jitSrcFilePtr))
- {
- return;
- }
-
- if (seek)
- {
- continue;
- }
-
- llen = strlen(temp);
- if (llen && temp[llen - 1] == '\n')
- {
- temp[llen - 1] = 0;
- }
-
- printf("; %s\n", temp);
- } while (++jitCurSrcLine < line);
-
- if (!seek)
- {
- printf(";\n");
- }
-}
-
-/*****************************************************************************/
-
-void Compiler::compDspSrcLinesByNativeIP(UNATIVE_OFFSET curIP)
-{
- static IPmappingDsc* nextMappingDsc;
- static unsigned lastLine;
-
- if (!opts.dspLines)
- {
- return;
- }
-
- if (curIP == 0)
- {
- if (genIPmappingList)
- {
- nextMappingDsc = genIPmappingList;
- lastLine = jitGetILoffs(nextMappingDsc->ipmdILoffsx);
-
- unsigned firstLine = jitGetILoffs(nextMappingDsc->ipmdILoffsx);
-
- unsigned earlierLine = (firstLine < 5) ? 0 : firstLine - 5;
-
- compDspSrcLinesByLineNum(earlierLine, true); // display previous 5 lines
- compDspSrcLinesByLineNum(firstLine, false);
- }
- else
- {
- nextMappingDsc = nullptr;
- }
-
- return;
- }
-
- if (nextMappingDsc)
- {
- UNATIVE_OFFSET offset = nextMappingDsc->ipmdNativeLoc.CodeOffset(GetEmitter());
-
- if (offset <= curIP)
- {
- IL_OFFSET nextOffs = jitGetILoffs(nextMappingDsc->ipmdILoffsx);
-
- if (lastLine < nextOffs)
- {
- compDspSrcLinesByLineNum(nextOffs);
- }
- else
- {
- // This offset corresponds to a previous line. Rewind to that line
-
- compDspSrcLinesByLineNum(nextOffs - 2, true);
- compDspSrcLinesByLineNum(nextOffs);
- }
-
- lastLine = nextOffs;
- nextMappingDsc = nextMappingDsc->ipmdNext;
- }
- }
-}
-
-/*****************************************************************************/
#endif // DEBUG
/*****************************************************************************/
@@ -1969,6 +1847,11 @@ void Compiler::compInit(ArenaAllocator* pAlloc,
impSpillCliquePredMembers = JitExpandArray(getAllocator());
impSpillCliqueSuccMembers = JitExpandArray(getAllocator());
+ new (&genIPmappings, jitstd::placement_t()) jitstd::list(getAllocator(CMK_DebugInfo));
+#ifdef DEBUG
+ new (&genPreciseIPmappings, jitstd::placement_t()) jitstd::list(getAllocator(CMK_DebugOnly));
+#endif
+
lvMemoryPerSsaData = SsaDefArray();
//
@@ -1995,6 +1878,8 @@ void Compiler::compInit(ArenaAllocator* pAlloc,
compJmpOpUsed = false;
compLongUsed = false;
compTailCallUsed = false;
+ compTailPrefixSeen = false;
+ compLocallocSeen = false;
compLocallocUsed = false;
compLocallocOptimized = false;
compQmarkRationalized = false;
@@ -2616,7 +2501,6 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
assert(!jitFlags->IsSet(JitFlags::JIT_FLAG_BBINSTR));
assert(!jitFlags->IsSet(JitFlags::JIT_FLAG_PROF_ENTERLEAVE));
assert(!jitFlags->IsSet(JitFlags::JIT_FLAG_DEBUG_EnC));
- assert(!jitFlags->IsSet(JitFlags::JIT_FLAG_DEBUG_INFO));
assert(!jitFlags->IsSet(JitFlags::JIT_FLAG_REVERSE_PINVOKE));
assert(!jitFlags->IsSet(JitFlags::JIT_FLAG_TRACK_TRANSITIONS));
}
@@ -2670,11 +2554,13 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
opts.compJitAlignLoopForJcc = JitConfig.JitAlignLoopForJcc() == 1;
opts.compJitAlignLoopMaxCodeSize = (unsigned short)JitConfig.JitAlignLoopMaxCodeSize();
+ opts.compJitHideAlignBehindJmp = JitConfig.JitHideAlignBehindJmp() == 1;
#else
opts.compJitAlignLoopAdaptive = true;
opts.compJitAlignLoopBoundary = DEFAULT_ALIGN_LOOP_BOUNDARY;
opts.compJitAlignLoopMinBlockWeight = DEFAULT_ALIGN_LOOP_MIN_BLOCK_WEIGHT;
opts.compJitAlignLoopMaxCodeSize = DEFAULT_MAX_LOOPSIZE_FOR_ALIGN;
+ opts.compJitHideAlignBehindJmp = true;
#endif
#ifdef TARGET_XARCH
@@ -2744,6 +2630,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
verboseSsa = verbose && shouldUseVerboseSsa();
asciiTrees = shouldDumpASCIITrees();
opts.dspDiffable = compIsForInlining() ? impInlineInfo->InlinerCompiler->opts.dspDiffable : false;
+
#endif
opts.altJit = false;
@@ -2899,6 +2786,18 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
verboseDump = (JitConfig.JitDumpTier0() > 0);
}
+ // Optionally suppress dumping some OSR jit requests.
+ //
+ if (verboseDump && jitFlags->IsSet(JitFlags::JIT_FLAG_OSR))
+ {
+ const int desiredOffset = JitConfig.JitDumpAtOSROffset();
+
+ if (desiredOffset != -1)
+ {
+ verboseDump = (((IL_OFFSET)desiredOffset) == info.compILEntry);
+ }
+ }
+
if (verboseDump)
{
verbose = true;
@@ -3740,8 +3639,6 @@ bool Compiler::compPromoteFewerStructs(unsigned lclNum)
void Compiler::compInitDebuggingInfo()
{
- assert(!compIsForInlining());
-
#ifdef DEBUG
if (verbose)
{
@@ -4539,8 +4436,8 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
if (info.compPublishStubParam)
{
assert(lvaStubArgumentVar == BAD_VAR_NUM);
- lvaStubArgumentVar = lvaGrabTempWithImplicitUse(false DEBUGARG("stub argument"));
- lvaTable[lvaStubArgumentVar].lvType = TYP_I_IMPL;
+ lvaStubArgumentVar = lvaGrabTempWithImplicitUse(false DEBUGARG("stub argument"));
+ lvaGetDesc(lvaStubArgumentVar)->lvType = TYP_I_IMPL;
// TODO-CQ: there is no need to mark it as doNotEnreg. There are no stores for this local
// before codegen so liveness and LSRA mark it as "liveIn" and always allocate a stack slot for it.
// However, it would be better to process it like other argument locals and keep it in
@@ -4571,6 +4468,10 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
//
DoPhase(this, PHASE_IMPORTATION, &Compiler::fgImport);
+ // Expand any patchpoints
+ //
+ DoPhase(this, PHASE_PATCHPOINTS, &Compiler::fgTransformPatchpoints);
+
// If instrumenting, add block and class probes.
//
if (compileFlags->IsSet(JitFlags::JIT_FLAG_BBINSTR))
@@ -4582,10 +4483,6 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
//
DoPhase(this, PHASE_INDXCALL, &Compiler::fgTransformIndirectCalls);
- // Expand any patchpoints
- //
- DoPhase(this, PHASE_PATCHPOINTS, &Compiler::fgTransformPatchpoints);
-
// PostImportPhase: cleanup inlinees
//
auto postImportPhase = [this]() {
@@ -4695,7 +4592,7 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
{
for (unsigned i = 0; i < info.compArgsCount; i++)
{
- if (lvaTable[i].TypeGet() == TYP_REF)
+ if (lvaGetDesc(i)->TypeGet() == TYP_REF)
{
// confirm that the argument is a GC pointer (for debugging (GC stress))
GenTree* op = gtNewLclvNode(i, TYP_REF);
@@ -4720,15 +4617,15 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
{
lvaReturnSpCheck = lvaGrabTempWithImplicitUse(false DEBUGARG("ReturnSpCheck"));
lvaSetVarDoNotEnregister(lvaReturnSpCheck, DoNotEnregisterReason::ReturnSpCheck);
- lvaTable[lvaReturnSpCheck].lvType = TYP_I_IMPL;
+ lvaGetDesc(lvaReturnSpCheck)->lvType = TYP_I_IMPL;
}
#endif // defined(DEBUG) && defined(TARGET_XARCH)
#if defined(DEBUG) && defined(TARGET_X86)
if (opts.compStackCheckOnCall)
{
- lvaCallSpCheck = lvaGrabTempWithImplicitUse(false DEBUGARG("CallSpCheck"));
- lvaTable[lvaCallSpCheck].lvType = TYP_I_IMPL;
+ lvaCallSpCheck = lvaGrabTempWithImplicitUse(false DEBUGARG("CallSpCheck"));
+ lvaGetDesc(lvaCallSpCheck)->lvType = TYP_I_IMPL;
}
#endif // defined(DEBUG) && defined(TARGET_X86)
@@ -5001,14 +4898,12 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
//
DoPhase(this, PHASE_COMPUTE_REACHABILITY, &Compiler::fgComputeReachability);
- // Discover and classify natural loops
- // (e.g. mark iterative loops as such). Also marks loop blocks
- // and sets bbWeight to the loop nesting levels
+ // Discover and classify natural loops (e.g. mark iterative loops as such). Also marks loop blocks
+ // and sets bbWeight to the loop nesting levels.
//
DoPhase(this, PHASE_FIND_LOOPS, &Compiler::optFindLoops);
- // Clone loops with optimization opportunities, and
- // choose one based on dynamic condition evaluation.
+ // Clone loops with optimization opportunities, and choose one based on dynamic condition evaluation.
//
DoPhase(this, PHASE_CLONE_LOOPS, &Compiler::optCloneLoops);
@@ -5266,6 +5161,11 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
fgDebugCheckLinks();
#endif
+#if FEATURE_LOOP_ALIGN
+ // Place loop alignment instructions
+ DoPhase(this, PHASE_ALIGN_LOOPS, &Compiler::placeLoopAlignInstructions);
+#endif
+
// Generate code
codeGen->genGenerateCode(methodCodePtr, methodCodeSize);
@@ -5322,6 +5222,83 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
#endif // FUNC_INFO_LOGGING
}
+#if FEATURE_LOOP_ALIGN
+
+//------------------------------------------------------------------------
+// placeLoopAlignInstructions: Iterate over all the blocks and determine
+// the best position to place the 'align' instruction. Inserting 'align'
+// instructions after an unconditional branch is preferred over inserting
+// in the block before the loop. In case there are multiple blocks
+// having 'jmp', the one that has lower weight is preferred.
+// If the block having 'jmp' is hotter than the block before the loop,
+// the align will still be placed after 'jmp' because the processor should
+// be smart enough to not fetch extra instruction beyond jmp.
+//
+void Compiler::placeLoopAlignInstructions()
+{
+ if (loopAlignCandidates == 0)
+ {
+ return;
+ }
+
+ int loopsToProcess = loopAlignCandidates;
+ JITDUMP("Inside placeLoopAlignInstructions for %d loops.\n", loopAlignCandidates);
+
+ // Add align only if there were any loops that needed alignment
+ weight_t minBlockSoFar = BB_MAX_WEIGHT;
+ BasicBlock* bbHavingAlign = nullptr;
+ for (BasicBlock* const block : Blocks())
+ {
+ if ((block == fgFirstBB) && block->isLoopAlign())
+ {
+ // Adding align instruction in prolog is not supported
+ // hence skip the align block if it is the first block.
+ loopsToProcess--;
+ continue;
+ }
+
+ // If there is a unconditional jump
+ if (opts.compJitHideAlignBehindJmp && (block->bbJumpKind == BBJ_ALWAYS))
+ {
+ if (block->bbWeight < minBlockSoFar)
+ {
+ minBlockSoFar = block->bbWeight;
+ bbHavingAlign = block;
+ JITDUMP(FMT_BB ", bbWeight=" FMT_WT " ends with unconditional 'jmp' \n", block->bbNum, block->bbWeight);
+ }
+ }
+
+ if ((block->bbNext != nullptr) && (block->bbNext->isLoopAlign()))
+ {
+ // If jmp was not found, then block before the loop start is where align instruction will be added.
+ if (bbHavingAlign == nullptr)
+ {
+ bbHavingAlign = block;
+ JITDUMP("Marking " FMT_BB " before the loop with BBF_HAS_ALIGN for loop at " FMT_BB "\n", block->bbNum,
+ block->bbNext->bbNum);
+ }
+ else
+ {
+ JITDUMP("Marking " FMT_BB " that ends with unconditional jump with BBF_HAS_ALIGN for loop at " FMT_BB
+ "\n",
+ bbHavingAlign->bbNum, block->bbNext->bbNum);
+ }
+
+ bbHavingAlign->bbFlags |= BBF_HAS_ALIGN;
+ minBlockSoFar = BB_MAX_WEIGHT;
+ bbHavingAlign = nullptr;
+
+ if (--loopsToProcess == 0)
+ {
+ break;
+ }
+ }
+ }
+
+ assert(loopsToProcess == 0);
+}
+#endif
+
//------------------------------------------------------------------------
// generatePatchpointInfo: allocate and fill in patchpoint info data,
// and report it to the VM
@@ -5398,6 +5375,14 @@ void Compiler::generatePatchpointInfo()
patchpointInfo->SecurityCookieOffset());
}
+ if (lvaMonAcquired != BAD_VAR_NUM)
+ {
+ LclVarDsc* const varDsc = lvaGetDesc(lvaMonAcquired);
+ patchpointInfo->SetMonitorAcquiredOffset(varDsc->GetStackOffset());
+ JITDUMP("--OSR-- monitor acquired V%02u offset is FP %d\n", lvaMonAcquired,
+ patchpointInfo->MonitorAcquiredOffset());
+ }
+
// Register this with the runtime.
info.compCompHnd->setPatchpointInfo(patchpointInfo);
}
@@ -6207,7 +6192,8 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr,
assert((methAttr_Old & (~flagsToIgnore)) == (methAttr_New & (~flagsToIgnore)));
#endif
- info.compFlags = impInlineInfo->inlineCandidateInfo->methAttr;
+ info.compFlags = impInlineInfo->inlineCandidateInfo->methAttr;
+ compInlineContext = impInlineInfo->inlineContext;
}
else
{
@@ -6215,6 +6201,7 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr,
#ifdef PSEUDORANDOM_NOP_INSERTION
info.compChecksum = getMethodBodyChecksum((char*)methodInfo->ILCode, methodInfo->ILCodeSize);
#endif
+ compInlineContext = m_inlineStrategy->GetRootContext();
}
compSwitchedToOptimized = false;
@@ -6292,7 +6279,8 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr,
info.compTotalColdCodeSize = 0;
info.compClassProbeCount = 0;
- compHasBackwardJump = false;
+ compHasBackwardJump = false;
+ compHasBackwardJumpInHandler = false;
#ifdef DEBUG
compCurBB = nullptr;
@@ -6352,10 +6340,7 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr,
lvaInitTypeRef();
- if (!compIsForInlining())
- {
- compInitDebuggingInfo();
- }
+ compInitDebuggingInfo();
#ifdef DEBUG
if (compIsForInlining())
@@ -6449,10 +6434,51 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr,
goto _Next;
}
- if (compHasBackwardJump && (info.compFlags & CORINFO_FLG_DISABLE_TIER0_FOR_LOOPS) != 0 && fgCanSwitchToOptimized())
+ // We may decide to optimize this method,
+ // to avoid spending a long time stuck in Tier0 code.
+ //
+ if (fgCanSwitchToOptimized())
{
- // Method likely has a loop, switch to the OptimizedTier to avoid spending too much time running slower code
- fgSwitchToOptimized();
+ // We only expect to be able to do this at Tier0.
+ //
+ assert(opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0));
+
+ // Normal tiering should bail us out of Tier0 tail call induced loops.
+ // So keep these methods in Tier0 if we're gathering PGO data.
+ // If we're not gathering PGO, then switch these to optimized to
+ // minimize the number of tail call helper stubs we might need.
+ // Reconsider this if/when we're able to share those stubs.
+ //
+ // Honor the config setting that tells the jit to
+ // always optimize methods with loops.
+ //
+ // If that's not set, and OSR is enabled, the jit may still
+ // decide to optimize, if there's something in the method that
+ // OSR currently cannot handle.
+ //
+ const char* reason = nullptr;
+
+ if (compTailPrefixSeen && !opts.jitFlags->IsSet(JitFlags::JIT_FLAG_BBINSTR))
+ {
+ reason = "tail.call and not BBINSTR";
+ }
+ else if ((info.compFlags & CORINFO_FLG_DISABLE_TIER0_FOR_LOOPS) != 0)
+ {
+ if (compHasBackwardJump)
+ {
+ reason = "loop";
+ }
+ }
+ else if (JitConfig.TC_OnStackReplacement() > 0)
+ {
+ const bool patchpointsOK = compCanHavePatchpoints(&reason);
+ assert(patchpointsOK || (reason != nullptr));
+ }
+
+ if (reason != nullptr)
+ {
+ fgSwitchToOptimized(reason);
+ }
}
compSetOptimizationLevel();
@@ -6500,9 +6526,21 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr,
#ifdef DEBUG
if ((JitConfig.DumpJittedMethods() == 1) && !compIsForInlining())
{
+ enum
+ {
+ BUFSIZE = 20
+ };
+ char osrBuffer[BUFSIZE] = {0};
+ if (opts.IsOSR())
+ {
+ // Tiering name already includes "OSR", we just want the IL offset
+ //
+ sprintf_s(osrBuffer, BUFSIZE, " @0x%x", info.compILEntry);
+ }
+
printf("Compiling %4d %s::%s, IL size = %u, hash=0x%08x %s%s%s\n", Compiler::jitTotalMethodCompiled,
info.compClassName, info.compMethodName, info.compILCodeSize, info.compMethodHash(),
- compGetTieringName(), opts.IsOSR() ? " OSR" : "", compGetStressMessage());
+ compGetTieringName(), osrBuffer, compGetStressMessage());
}
if (compIsForInlining())
{
@@ -7753,9 +7791,9 @@ CompTimeInfo::CompTimeInfo(unsigned byteCodeBytes)
}
#if MEASURE_CLRAPI_CALLS
- assert(ARRAYSIZE(m_perClrAPIcalls) == API_ICorJitInfo_Names::API_COUNT);
- assert(ARRAYSIZE(m_perClrAPIcycles) == API_ICorJitInfo_Names::API_COUNT);
- assert(ARRAYSIZE(m_maxClrAPIcycles) == API_ICorJitInfo_Names::API_COUNT);
+ assert(ArrLen(m_perClrAPIcalls) == API_ICorJitInfo_Names::API_COUNT);
+ assert(ArrLen(m_perClrAPIcycles) == API_ICorJitInfo_Names::API_COUNT);
+ assert(ArrLen(m_maxClrAPIcycles) == API_ICorJitInfo_Names::API_COUNT);
for (int i = 0; i < API_ICorJitInfo_Names::API_COUNT; i++)
{
m_perClrAPIcalls[i] = 0;
@@ -7931,7 +7969,7 @@ void CompTimeSummaryInfo::Print(FILE* f)
extraHdr2);
// Ensure that at least the names array and the Phases enum have the same number of entries:
- assert(_countof(PhaseNames) == PHASE_NUMBER_OF);
+ assert(ArrLen(PhaseNames) == PHASE_NUMBER_OF);
for (int i = 0; i < PHASE_NUMBER_OF; i++)
{
double phase_tot_ms = (((double)m_total.m_cyclesByPhase[i]) / countsPerSec) * 1000.0;
@@ -7994,7 +8032,7 @@ void CompTimeSummaryInfo::Print(FILE* f)
fprintf(f, " PHASE inv/meth Mcycles time (ms) %% of total\n");
fprintf(f, " --------------------------------------------------------------------------------------\n");
// Ensure that at least the names array and the Phases enum have the same number of entries:
- assert(_countof(PhaseNames) == PHASE_NUMBER_OF);
+ assert(ArrLen(PhaseNames) == PHASE_NUMBER_OF);
for (int i = 0; i < PHASE_NUMBER_OF; i++)
{
double phase_tot_ms = (((double)m_filtered.m_cyclesByPhase[i]) / countsPerSec) * 1000.0;
@@ -8726,7 +8764,7 @@ void cVarDsc(Compiler* comp, LclVarDsc* varDsc)
{
static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
printf("===================================================================== *VarDsc %u\n", sequenceNumber++);
- unsigned lclNum = (unsigned)(varDsc - comp->lvaTable);
+ unsigned lclNum = comp->lvaGetLclNum(varDsc);
comp->lvaDumpEntry(lclNum, Compiler::FINAL_FRAME_LAYOUT);
}
@@ -8800,7 +8838,6 @@ void cLoop(Compiler* comp, Compiler::LoopDsc* loop)
static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
printf("===================================================================== Loop %u\n", sequenceNumber++);
printf("HEAD " FMT_BB "\n", loop->lpHead->bbNum);
- printf("FIRST " FMT_BB "\n", loop->lpFirst->bbNum);
printf("TOP " FMT_BB "\n", loop->lpTop->bbNum);
printf("ENTRY " FMT_BB "\n", loop->lpEntry->bbNum);
if (loop->lpExitCnt == 1)
@@ -8949,8 +8986,6 @@ BasicBlock* dbBlock;
GenTree* dFindTree(GenTree* tree, unsigned id)
{
- GenTree* child;
-
if (tree == nullptr)
{
return nullptr;
@@ -8962,18 +8997,13 @@ GenTree* dFindTree(GenTree* tree, unsigned id)
return tree;
}
- unsigned childCount = tree->NumChildren();
- for (unsigned childIndex = 0; childIndex < childCount; childIndex++)
- {
- child = tree->GetChild(childIndex);
+ GenTree* child = nullptr;
+ tree->VisitOperands([&child, id](GenTree* operand) -> GenTree::VisitResult {
child = dFindTree(child, id);
- if (child != nullptr)
- {
- return child;
- }
- }
+ return (child != nullptr) ? GenTree::VisitResult::Abort : GenTree::VisitResult::Continue;
+ });
- return nullptr;
+ return child;
}
GenTree* dFindTree(unsigned id)
@@ -9252,10 +9282,6 @@ void cTreeFlags(Compiler* comp, GenTree* tree)
{
chars += printf("[RELOP_JMP_USED]");
}
- if (tree->gtFlags & GTF_RELOP_QMARK)
- {
- chars += printf("[RELOP_QMARK]");
- }
break;
case GT_QMARK:
@@ -9277,7 +9303,7 @@ void cTreeFlags(Compiler* comp, GenTree* tree)
case GT_CNS_INT:
{
- unsigned handleKind = (tree->gtFlags & GTF_ICON_HDL_MASK);
+ GenTreeFlags handleKind = (tree->gtFlags & GTF_ICON_HDL_MASK);
switch (handleKind)
{
@@ -9361,6 +9387,10 @@ void cTreeFlags(Compiler* comp, GenTree* tree)
chars += printf("[ICON_FIELD_OFF]");
break;
+
+ default:
+ assert(!"a forgotten handle flag");
+ break;
}
}
break;
@@ -9517,7 +9547,7 @@ void cTreeFlags(Compiler* comp, GenTree* tree)
default:
{
- unsigned flags = (tree->gtFlags & (~(unsigned)(GTF_COMMON_MASK | GTF_OVERFLOW)));
+ GenTreeFlags flags = (tree->gtFlags & (~(GTF_COMMON_MASK | GTF_OVERFLOW)));
if (flags != 0)
{
chars += printf("[%08X]", flags);
diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h
index cd46b491ea95ea..cbd45e04c319ba 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -27,6 +27,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#include "jitstd.h"
#include "jithashtable.h"
#include "gentree.h"
+#include "debuginfo.h"
#include "lir.h"
#include "block.h"
#include "inline.h"
@@ -41,7 +42,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#include "jitexpandarray.h"
#include "tinyarray.h"
#include "valuenum.h"
-#include "reglist.h"
#include "jittelemetry.h"
#include "namedintrinsiclist.h"
#ifdef LATE_DISASM
@@ -2515,6 +2515,30 @@ inline LoopFlags& operator&=(LoopFlags& a, LoopFlags b)
return a = (LoopFlags)((unsigned short)a & (unsigned short)b);
}
+// The following holds information about instr offsets in terms of generated code.
+
+enum class IPmappingDscKind
+{
+ Prolog, // The mapping represents the start of a prolog.
+ Epilog, // The mapping represents the start of an epilog.
+ NoMapping, // This does not map to any IL offset.
+ Normal, // The mapping maps to an IL offset.
+};
+
+struct IPmappingDsc
+{
+ emitLocation ipmdNativeLoc; // the emitter location of the native code corresponding to the IL offset
+ IPmappingDscKind ipmdKind; // The kind of mapping
+ ILLocation ipmdLoc; // The location for normal mappings
+ bool ipmdIsLabel; // Can this code be a branch label?
+};
+
+struct PreciseIPMapping
+{
+ emitLocation nativeLoc;
+ DebugInfo debugInfo;
+};
+
/*
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
@@ -2959,6 +2983,8 @@ class Compiler
bool fgNormalizeEHCase2();
bool fgNormalizeEHCase3();
+ void fgCheckForLoopsInHandlers();
+
#ifdef DEBUG
void dispIncomingEHClause(unsigned num, const CORINFO_EH_CLAUSE& clause);
void dispOutgoingEHClause(unsigned num, const CORINFO_EH_CLAUSE& clause);
@@ -2987,7 +3013,8 @@ class Compiler
*/
// Functions to create nodes
- Statement* gtNewStmt(GenTree* expr = nullptr, IL_OFFSETX offset = BAD_IL_OFFSET);
+ Statement* gtNewStmt(GenTree* expr = nullptr);
+ Statement* gtNewStmt(GenTree* expr, const DebugInfo& di);
// For unary opers.
GenTree* gtNewOperNode(genTreeOps oper, var_types type, GenTree* op1, bool doSimplifications = TRUE);
@@ -3058,8 +3085,6 @@ class Compiler
GenTree* gtNewCpObjNode(GenTree* dst, GenTree* src, CORINFO_CLASS_HANDLE structHnd, bool isVolatile);
- GenTreeArgList* gtNewListNode(GenTree* op1, GenTreeArgList* op2);
-
GenTreeCall::Use* gtNewCallArgs(GenTree* node);
GenTreeCall::Use* gtNewCallArgs(GenTree* node1, GenTree* node2);
GenTreeCall::Use* gtNewCallArgs(GenTree* node1, GenTree* node2, GenTree* node3);
@@ -3071,12 +3096,12 @@ class Compiler
CORINFO_METHOD_HANDLE handle,
var_types type,
GenTreeCall::Use* args,
- IL_OFFSETX ilOffset = BAD_IL_OFFSET);
+ const DebugInfo& di = DebugInfo());
GenTreeCall* gtNewIndCallNode(GenTree* addr,
var_types type,
GenTreeCall::Use* args,
- IL_OFFSETX ilOffset = BAD_IL_OFFSET);
+ const DebugInfo& di = DebugInfo());
GenTreeCall* gtNewHelperCallNode(unsigned helper, var_types type, GenTreeCall::Use* args = nullptr);
@@ -3084,8 +3109,8 @@ class Compiler
GenTree* ctxTree,
void* compileTimeHandle);
- GenTreeLclVar* gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSETX ILoffs = BAD_IL_OFFSET));
- GenTreeLclVar* gtNewLclLNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSETX ILoffs = BAD_IL_OFFSET));
+ GenTreeLclVar* gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSET offs = BAD_IL_OFFSET));
+ GenTreeLclVar* gtNewLclLNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSET offs = BAD_IL_OFFSET));
GenTreeLclVar* gtNewLclVarAddrNode(unsigned lclNum, var_types type = TYP_I_IMPL);
GenTreeLclFld* gtNewLclFldAddrNode(unsigned lclNum,
@@ -3141,6 +3166,19 @@ class Compiler
CorInfoType simdBaseJitType,
unsigned simdSize,
bool isSimdAsHWIntrinsic = false);
+ GenTreeHWIntrinsic* gtNewSimdHWIntrinsicNode(var_types type,
+ GenTree** operands,
+ size_t operandCount,
+ NamedIntrinsic hwIntrinsicID,
+ CorInfoType simdBaseJitType,
+ unsigned simdSize,
+ bool isSimdAsHWIntrinsic = false);
+ GenTreeHWIntrinsic* gtNewSimdHWIntrinsicNode(var_types type,
+ IntrinsicNodeBuilder&& nodeBuilder,
+ NamedIntrinsic hwIntrinsicID,
+ CorInfoType simdBaseJitType,
+ unsigned simdSize,
+ bool isSimdAsHWIntrinsic = false);
GenTreeHWIntrinsic* gtNewSimdAsHWIntrinsicNode(var_types type,
NamedIntrinsic hwIntrinsicID,
@@ -3262,6 +3300,13 @@ class Compiler
unsigned simdSize,
bool isSimdAsHWIntrinsic);
+ GenTree* gtNewSimdNarrowNode(var_types type,
+ GenTree* op1,
+ GenTree* op2,
+ CorInfoType simdBaseJitType,
+ unsigned simdSize,
+ bool isSimdAsHWIntrinsic);
+
GenTree* gtNewSimdSqrtNode(
var_types type, GenTree* op1, CorInfoType simdBaseJitType, unsigned simdSize, bool isSimdAsHWIntrinsic);
@@ -3272,6 +3317,12 @@ class Compiler
unsigned simdSize,
bool isSimdAsHWIntrinsic);
+ GenTree* gtNewSimdWidenLowerNode(
+ var_types type, GenTree* op1, CorInfoType simdBaseJitType, unsigned simdSize, bool isSimdAsHWIntrinsic);
+
+ GenTree* gtNewSimdWidenUpperNode(
+ var_types type, GenTree* op1, CorInfoType simdBaseJitType, unsigned simdSize, bool isSimdAsHWIntrinsic);
+
GenTree* gtNewSimdWithElementNode(var_types type,
GenTree* op1,
GenTree* op2,
@@ -3285,6 +3336,7 @@ class Compiler
unsigned simdSize,
bool isSimdAsHWIntrinsic);
+ GenTreeHWIntrinsic* gtNewScalarHWIntrinsicNode(var_types type, NamedIntrinsic hwIntrinsicID);
GenTreeHWIntrinsic* gtNewScalarHWIntrinsicNode(var_types type, GenTree* op1, NamedIntrinsic hwIntrinsicID);
GenTreeHWIntrinsic* gtNewScalarHWIntrinsicNode(var_types type,
GenTree* op1,
@@ -3316,11 +3368,6 @@ class Compiler
void gtChangeOperToNullCheck(GenTree* tree, BasicBlock* block);
- GenTreeArgList* gtNewArgList(GenTree* op);
- GenTreeArgList* gtNewArgList(GenTree* op1, GenTree* op2);
- GenTreeArgList* gtNewArgList(GenTree* op1, GenTree* op2, GenTree* op3);
- GenTreeArgList* gtNewArgList(GenTree* op1, GenTree* op2, GenTree* op3, GenTree* op4);
-
static fgArgTabEntry* gtArgEntryByArgNum(GenTreeCall* call, unsigned argNum);
static fgArgTabEntry* gtArgEntryByNode(GenTreeCall* call, GenTree* node);
fgArgTabEntry* gtArgEntryByLateArgIndex(GenTreeCall* call, unsigned lateArgInx);
@@ -3328,11 +3375,11 @@ class Compiler
GenTreeOp* gtNewAssignNode(GenTree* dst, GenTree* src);
- GenTree* gtNewTempAssign(unsigned tmp,
- GenTree* val,
- Statement** pAfterStmt = nullptr,
- IL_OFFSETX ilOffset = BAD_IL_OFFSET,
- BasicBlock* block = nullptr);
+ GenTree* gtNewTempAssign(unsigned tmp,
+ GenTree* val,
+ Statement** pAfterStmt = nullptr,
+ const DebugInfo& di = DebugInfo(),
+ BasicBlock* block = nullptr);
GenTree* gtNewRefCOMfield(GenTree* objPtr,
CORINFO_RESOLVED_TOKEN* pResolvedToken,
@@ -3387,7 +3434,7 @@ class Compiler
Statement* gtCloneStmt(Statement* stmt)
{
GenTree* exprClone = gtCloneExpr(stmt->GetRootNode());
- return gtNewStmt(exprClone, stmt->GetILOffsetX());
+ return gtNewStmt(exprClone, stmt->GetDebugInfo());
}
// Internal helper for cloning a call
@@ -3399,8 +3446,6 @@ class Compiler
// Create copy of an inline or guarded devirtualization candidate tree.
GenTreeCall* gtCloneCandidateCall(GenTreeCall* call);
- GenTree* gtReplaceTree(Statement* stmt, GenTree* tree, GenTree* replacementTree);
-
void gtUpdateSideEffects(Statement* stmt, GenTree* tree);
void gtUpdateTreeAncestorsSideEffects(GenTree* tree);
@@ -3419,16 +3464,14 @@ class Compiler
// before they have been set.)
bool gtComplexityExceeds(GenTree** tree, unsigned limit);
- bool gtCompareTree(GenTree* op1, GenTree* op2);
-
GenTree* gtReverseCond(GenTree* tree);
bool gtHasRef(GenTree* tree, ssize_t lclNum, bool defOnly);
bool gtHasLocalsWithAddrOp(GenTree* tree);
- unsigned gtSetListOrder(GenTree* list, bool regs, bool isListCallArgs);
unsigned gtSetCallArgsOrder(const GenTreeCall::UseList& args, bool lateArgs, int* callCostEx, int* callCostSz);
+ unsigned gtSetMultiOpOrder(GenTreeMultiOp* multiOp);
void gtWalkOp(GenTree** op1, GenTree** op2, GenTree* base, bool constOnly);
@@ -3455,20 +3498,20 @@ class Compiler
void gtSetStmtInfo(Statement* stmt);
// Returns "true" iff "node" has any of the side effects in "flags".
- bool gtNodeHasSideEffects(GenTree* node, unsigned flags);
+ bool gtNodeHasSideEffects(GenTree* node, GenTreeFlags flags);
// Returns "true" iff "tree" or its (transitive) children have any of the side effects in "flags".
- bool gtTreeHasSideEffects(GenTree* tree, unsigned flags);
+ bool gtTreeHasSideEffects(GenTree* tree, GenTreeFlags flags);
// Appends 'expr' in front of 'list'
// 'list' will typically start off as 'nullptr'
// when 'list' is non-null a GT_COMMA node is used to insert 'expr'
GenTree* gtBuildCommaList(GenTree* list, GenTree* expr);
- void gtExtractSideEffList(GenTree* expr,
- GenTree** pList,
- unsigned flags = GTF_SIDE_EFFECT,
- bool ignoreRoot = false);
+ void gtExtractSideEffList(GenTree* expr,
+ GenTree** pList,
+ GenTreeFlags GenTreeFlags = GTF_SIDE_EFFECT,
+ bool ignoreRoot = false);
GenTree* gtGetThisArg(GenTreeCall* call);
@@ -3580,11 +3623,12 @@ class Compiler
void gtDispLclVar(unsigned lclNum, bool padForBiggestDisp = true);
void gtDispLclVarStructType(unsigned lclNum);
void gtDispClassLayout(ClassLayout* layout, var_types type);
+ void gtDispILLocation(const ILLocation& loc);
void gtDispStmt(Statement* stmt, const char* msg = nullptr);
void gtDispBlockStmts(BasicBlock* block);
void gtGetArgMsg(GenTreeCall* call, GenTree* arg, unsigned argNum, char* bufp, unsigned bufLength);
void gtGetLateArgMsg(GenTreeCall* call, GenTree* arg, int argNum, char* bufp, unsigned bufLength);
- void gtDispArgList(GenTreeCall* call, IndentStack* indentStack);
+ void gtDispArgList(GenTreeCall* call, GenTree* lastCallOperand, IndentStack* indentStack);
void gtDispFieldSeq(FieldSeqNode* pfsn);
void gtDispRange(LIR::ReadOnlyRange const& range);
@@ -3606,9 +3650,6 @@ class Compiler
typedef fgWalkResult(fgWalkPreFn)(GenTree** pTree, fgWalkData* data);
typedef fgWalkResult(fgWalkPostFn)(GenTree** pTree, fgWalkData* data);
-#ifdef DEBUG
- static fgWalkPreFn gtAssertColonCond;
-#endif
static fgWalkPreFn gtMarkColonCond;
static fgWalkPreFn gtClearColonCond;
@@ -3636,6 +3677,7 @@ class Compiler
#endif
BasicBlock* bbNewBasicBlock(BBjumpKinds jumpKind);
+ void placeLoopAlignInstructions();
/*
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
@@ -3690,7 +3732,6 @@ class Compiler
unsigned lvaCount; // total number of locals, which includes function arguments,
// special arguments, IL local variables, and JIT temporary variables
- unsigned lvaRefCount; // total number of references to locals
LclVarDsc* lvaTable; // variable descriptor table
unsigned lvaTableCnt; // lvaTable size (>= lvaCount)
@@ -3775,15 +3816,6 @@ class Compiler
// where it is used to detect tail-call chains.
unsigned lvaRetAddrVar;
-#ifdef TARGET_ARM
- // On architectures whose ABIs allow structs to be passed in registers, struct promotion will sometimes
- // require us to "rematerialize" a struct from it's separate constituent field variables. Packing several sub-word
- // field variables into an argument register is a hard problem. It's easier to reserve a word of memory into which
- // such field can be copied, after which the assembled memory word can be read into the register. We will allocate
- // this variable to be this scratch word whenever struct promotion occurs.
- unsigned lvaPromotedStructAssemblyScratchVar;
-#endif // TARGET_ARM
-
#if defined(DEBUG) && defined(TARGET_XARCH)
unsigned lvaReturnSpCheck; // Stores SP to confirm it is not corrupted on return.
@@ -3921,10 +3953,15 @@ class Compiler
return &lvaTable[lclNum];
}
+ LclVarDsc* lvaGetDesc(unsigned lclNum) const
+ {
+ assert(lclNum < lvaCount);
+ return &lvaTable[lclNum];
+ }
+
LclVarDsc* lvaGetDesc(const GenTreeLclVarCommon* lclVar)
{
- assert(lclVar->GetLclNum() < lvaCount);
- return &lvaTable[lclVar->GetLclNum()];
+ return lvaGetDesc(lclVar->GetLclNum());
}
unsigned lvaTrackedIndexToLclNum(unsigned trackedIndex)
@@ -3940,6 +3977,16 @@ class Compiler
return lvaGetDesc(lvaTrackedIndexToLclNum(trackedIndex));
}
+ unsigned lvaGetLclNum(const LclVarDsc* varDsc)
+ {
+ assert((lvaTable <= varDsc) && (varDsc < lvaTable + lvaCount)); // varDsc must point within the table
+ assert(((char*)varDsc - (char*)lvaTable) % sizeof(LclVarDsc) ==
+ 0); // varDsc better not point in the middle of a variable
+ unsigned varNum = (unsigned)(varDsc - lvaTable);
+ assert(varDsc == &lvaTable[varNum]);
+ return varNum;
+ }
+
unsigned lvaLclSize(unsigned varNum);
unsigned lvaLclExactSize(unsigned varNum);
@@ -4155,9 +4202,11 @@ class Compiler
unsigned lvaPSPSym; // variable representing the PSPSym
#endif
- InlineInfo* impInlineInfo;
+ InlineInfo* impInlineInfo; // Only present for inlinees
InlineStrategy* m_inlineStrategy;
+ InlineContext* compInlineContext; // Always present
+
// The Compiler* that is the root of the inlining tree of which "this" is a member.
Compiler* impInlineRoot();
@@ -4251,7 +4300,7 @@ class Compiler
CORINFO_CONTEXT_HANDLE* exactContextHandle,
bool isLateDevirtualization,
bool isExplicitTailCall,
- IL_OFFSETX ilOffset = BAD_IL_OFFSET);
+ IL_OFFSET ilOffset = BAD_IL_OFFSET);
//=========================================================================
// PROTECTED
@@ -4298,7 +4347,7 @@ class Compiler
bool impCanPInvokeInlineCallSite(BasicBlock* block);
void impCheckForPInvokeCall(
GenTreeCall* call, CORINFO_METHOD_HANDLE methHnd, CORINFO_SIG_INFO* sig, unsigned mflags, BasicBlock* block);
- GenTreeCall* impImportIndirectCall(CORINFO_SIG_INFO* sig, IL_OFFSETX ilOffset = BAD_IL_OFFSET);
+ GenTreeCall* impImportIndirectCall(CORINFO_SIG_INFO* sig, const DebugInfo& di = DebugInfo());
void impPopArgsForUnmanagedCall(GenTree* call, CORINFO_SIG_INFO* sig);
void impInsertHelperCall(CORINFO_HELPER_DESC* helperCall);
@@ -4434,6 +4483,7 @@ class Compiler
bool readonlyCall,
CorInfoIntrinsics intrinsicID);
GenTree* impInitializeArrayIntrinsic(CORINFO_SIG_INFO* sig);
+ GenTree* impCreateSpanIntrinsic(CORINFO_SIG_INFO* sig);
GenTree* impKeepAliveIntrinsic(GenTree* objToKeepAlive);
@@ -4459,23 +4509,23 @@ class Compiler
void impEndTreeList(BasicBlock* block, Statement* firstStmt, Statement* lastStmt);
void impEndTreeList(BasicBlock* block);
void impAppendStmtCheck(Statement* stmt, unsigned chkLevel);
- void impAppendStmt(Statement* stmt, unsigned chkLevel);
+ void impAppendStmt(Statement* stmt, unsigned chkLevel, bool checkConsumedDebugInfo = true);
void impAppendStmt(Statement* stmt);
void impInsertStmtBefore(Statement* stmt, Statement* stmtBefore);
- Statement* impAppendTree(GenTree* tree, unsigned chkLevel, IL_OFFSETX offset);
- void impInsertTreeBefore(GenTree* tree, IL_OFFSETX offset, Statement* stmtBefore);
- void impAssignTempGen(unsigned tmp,
- GenTree* val,
- unsigned curLevel,
- Statement** pAfterStmt = nullptr,
- IL_OFFSETX ilOffset = BAD_IL_OFFSET,
- BasicBlock* block = nullptr);
+ Statement* impAppendTree(GenTree* tree, unsigned chkLevel, const DebugInfo& di, bool checkConsumedDebugInfo = true);
+ void impInsertTreeBefore(GenTree* tree, const DebugInfo& di, Statement* stmtBefore);
+ void impAssignTempGen(unsigned tmp,
+ GenTree* val,
+ unsigned curLevel,
+ Statement** pAfterStmt = nullptr,
+ const DebugInfo& di = DebugInfo(),
+ BasicBlock* block = nullptr);
void impAssignTempGen(unsigned tmpNum,
GenTree* val,
CORINFO_CLASS_HANDLE structHnd,
unsigned curLevel,
Statement** pAfterStmt = nullptr,
- IL_OFFSETX ilOffset = BAD_IL_OFFSET,
+ const DebugInfo& di = DebugInfo(),
BasicBlock* block = nullptr);
Statement* impExtractLastStmt();
@@ -4489,14 +4539,14 @@ class Compiler
CORINFO_CLASS_HANDLE structHnd,
unsigned curLevel,
Statement** pAfterStmt = nullptr,
- IL_OFFSETX ilOffset = BAD_IL_OFFSET,
+ const DebugInfo& di = DebugInfo(),
BasicBlock* block = nullptr);
GenTree* impAssignStructPtr(GenTree* dest,
GenTree* src,
CORINFO_CLASS_HANDLE structHnd,
unsigned curLevel,
Statement** pAfterStmt = nullptr,
- IL_OFFSETX ilOffset = BAD_IL_OFFSET,
+ const DebugInfo& di = DebugInfo(),
BasicBlock* block = nullptr);
GenTree* impGetStructAddr(GenTree* structVal, CORINFO_CLASS_HANDLE structHnd, unsigned curLevel, bool willDeref);
@@ -4573,21 +4623,20 @@ class Compiler
void impNoteLastILoffs();
#endif
- /* IL offset of the stmt currently being imported. It gets set to
- BAD_IL_OFFSET after it has been set in the appended trees. Then it gets
- updated at IL offsets for which we have to report mapping info.
- It also includes flag bits, so use jitGetILoffs()
- to get the actual IL offset value.
- */
+ // Debug info of current statement being imported. It gets set to contain
+ // no IL location (!impCurStmtDI.GetLocation().IsValid) after it has been
+ // set in the appended trees. Then it gets updated at IL instructions for
+ // which we have to report mapping info.
+ // It will always contain the current inline context.
+ DebugInfo impCurStmtDI;
- IL_OFFSETX impCurStmtOffs;
+ DebugInfo impCreateDIWithCurrentStackInfo(IL_OFFSET offs, bool isCall);
void impCurStmtOffsSet(IL_OFFSET offs);
void impNoteBranchOffs();
unsigned impInitBlockLineInfo();
- GenTree* impCheckForNullPointer(GenTree* obj);
bool impIsThis(GenTree* obj);
bool impIsLDFTN_TOKEN(const BYTE* delegateCreateStart, const BYTE* newobjCodeAddr);
bool impIsDUP_LDVIRTFTN_TOKEN(const BYTE* delegateCreateStart, const BYTE* newobjCodeAddr);
@@ -4603,11 +4652,6 @@ class Compiler
GenTreeCall::Use* impPopReverseCallArgs(unsigned count, CORINFO_SIG_INFO* sig, unsigned skipReverseCount = 0);
- /*
- * Get current IL offset with stack-empty info incoporated
- */
- IL_OFFSETX impCurILOffset(IL_OFFSET offs, bool callInstruction = false);
-
//---------------- Spilling the importer stack ----------------------------
// The maximum number of bytes of IL processed without clean stack state.
@@ -4773,7 +4817,7 @@ class Compiler
void impLoadVar(unsigned lclNum, IL_OFFSET offset, const typeInfo& tiRetVal);
void impLoadVar(unsigned lclNum, IL_OFFSET offset)
{
- impLoadVar(lclNum, offset, lvaTable[lclNum].lvVerTypeInfo);
+ impLoadVar(lclNum, offset, lvaGetDesc(lclNum)->lvVerTypeInfo);
}
void impLoadArg(unsigned ilArgNum, IL_OFFSET offset);
void impLoadLoc(unsigned ilLclNum, IL_OFFSET offset);
@@ -5210,10 +5254,10 @@ class Compiler
BasicBlock* fgSplitBlockAfterNode(BasicBlock* curr, GenTree* node); // for LIR
BasicBlock* fgSplitEdge(BasicBlock* curr, BasicBlock* succ);
- Statement* fgNewStmtFromTree(GenTree* tree, BasicBlock* block, IL_OFFSETX offs);
+ Statement* fgNewStmtFromTree(GenTree* tree, BasicBlock* block, const DebugInfo& di);
Statement* fgNewStmtFromTree(GenTree* tree);
Statement* fgNewStmtFromTree(GenTree* tree, BasicBlock* block);
- Statement* fgNewStmtFromTree(GenTree* tree, IL_OFFSETX offs);
+ Statement* fgNewStmtFromTree(GenTree* tree, const DebugInfo& di);
GenTree* fgGetTopLevelQmark(GenTree* expr, GenTree** ppDst = nullptr);
void fgExpandQmarkForCastInstOf(BasicBlock* block, Statement* stmt);
@@ -5448,6 +5492,8 @@ class Compiler
// For a store to an address-exposed local at curTree, record the new curMemoryVN and update curTree's MemorySsaMap.
void recordAddressExposedLocalStore(GenTree* curTree, ValueNum memoryVN DEBUGARG(const char* msg));
+ void fgSetCurrentMemoryVN(MemoryKind memoryKind, ValueNum newMemoryVN);
+
// Tree caused an update in the current memory VN. If "tree" has an associated heap SSA #, record that
// value in that SSA #.
void fgValueNumberRecordMemorySsa(MemoryKind memoryKind, GenTree* tree);
@@ -5480,12 +5526,12 @@ class Compiler
#ifdef FEATURE_SIMD
// Does value-numbering for a GT_SIMD tree
- void fgValueNumberSimd(GenTree* tree);
+ void fgValueNumberSimd(GenTreeSIMD* tree);
#endif // FEATURE_SIMD
#ifdef FEATURE_HW_INTRINSICS
// Does value-numbering for a GT_HWINTRINSIC tree
- void fgValueNumberHWIntrinsic(GenTree* tree);
+ void fgValueNumberHWIntrinsic(GenTreeHWIntrinsic* tree);
#endif // FEATURE_HW_INTRINSICS
// Does value-numbering for a call. We interpret some helper calls.
@@ -6041,7 +6087,7 @@ class Compiler
BasicBlock* fgLookupBB(unsigned addr);
bool fgCanSwitchToOptimized();
- void fgSwitchToOptimized();
+ void fgSwitchToOptimized(const char* reason);
bool fgMayExplicitTailCall();
@@ -6144,10 +6190,10 @@ class Compiler
#endif
public:
- Statement* fgNewStmtAtBeg(BasicBlock* block, GenTree* tree);
+ Statement* fgNewStmtAtBeg(BasicBlock* block, GenTree* tree, const DebugInfo& di = DebugInfo());
void fgInsertStmtAtEnd(BasicBlock* block, Statement* stmt);
- Statement* fgNewStmtAtEnd(BasicBlock* block, GenTree* tree);
- Statement* fgNewStmtNearEnd(BasicBlock* block, GenTree* tree);
+ Statement* fgNewStmtAtEnd(BasicBlock* block, GenTree* tree, const DebugInfo& di = DebugInfo());
+ Statement* fgNewStmtNearEnd(BasicBlock* block, GenTree* tree, const DebugInfo& di = DebugInfo());
private:
void fgInsertStmtNearEnd(BasicBlock* block, Statement* stmt);
@@ -6290,12 +6336,8 @@ class Compiler
GenTreeFieldList* fgMorphLclArgToFieldlist(GenTreeLclVarCommon* lcl);
void fgInitArgInfo(GenTreeCall* call);
GenTreeCall* fgMorphArgs(GenTreeCall* call);
- GenTreeArgList* fgMorphArgList(GenTreeArgList* args, MorphAddrContext* mac);
- void fgMakeOutgoingStructArgCopy(GenTreeCall* call,
- GenTreeCall::Use* args,
- unsigned argIndex,
- CORINFO_CLASS_HANDLE copyBlkClass);
+ void fgMakeOutgoingStructArgCopy(GenTreeCall* call, GenTreeCall::Use* args, CORINFO_CLASS_HANDLE copyBlkClass);
GenTree* fgMorphLocalVar(GenTree* tree, bool forceRemorph);
@@ -6331,17 +6373,17 @@ class Compiler
GenTree* fgGetStubAddrArg(GenTreeCall* call);
unsigned fgGetArgTabEntryParameterLclNum(GenTreeCall* call, fgArgTabEntry* argTabEntry);
void fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCall* recursiveTailCall);
- Statement* fgAssignRecursiveCallArgToCallerParam(GenTree* arg,
- fgArgTabEntry* argTabEntry,
- unsigned lclParamNum,
- BasicBlock* block,
- IL_OFFSETX callILOffset,
- Statement* tmpAssignmentInsertionPoint,
- Statement* paramAssignmentInsertionPoint);
+ Statement* fgAssignRecursiveCallArgToCallerParam(GenTree* arg,
+ fgArgTabEntry* argTabEntry,
+ unsigned lclParamNum,
+ BasicBlock* block,
+ const DebugInfo& callDI,
+ Statement* tmpAssignmentInsertionPoint,
+ Statement* paramAssignmentInsertionPoint);
GenTree* fgMorphCall(GenTreeCall* call);
GenTree* fgExpandVirtualVtableCallTarget(GenTreeCall* call);
void fgMorphCallInline(GenTreeCall* call, InlineResult* result);
- void fgMorphCallInlineHelper(GenTreeCall* call, InlineResult* result);
+ void fgMorphCallInlineHelper(GenTreeCall* call, InlineResult* result, InlineContext** createdContext);
#if DEBUG
void fgNoteNonInlineCandidate(Statement* stmt, GenTreeCall* call);
static fgWalkPreFn fgFindNonInlineCandidate;
@@ -6366,6 +6408,7 @@ class Compiler
GenTree* fgMorphRetInd(GenTreeUnOp* tree);
GenTree* fgMorphModToSubMulDiv(GenTreeOp* tree);
GenTree* fgMorphSmpOpOptional(GenTreeOp* tree);
+ GenTree* fgMorphMultiOp(GenTreeMultiOp* multiOp);
GenTree* fgMorphConst(GenTree* tree);
bool fgMorphCanUseLclFldForCopy(unsigned lclNum1, unsigned lclNum2);
@@ -6467,7 +6510,7 @@ class Compiler
unsigned fgBigOffsetMorphingTemps[TYP_COUNT];
unsigned fgCheckInlineDepthAndRecursion(InlineInfo* inlineInfo);
- void fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* result);
+ void fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* result, InlineContext** createdContext);
void fgInsertInlineeBlocks(InlineInfo* pInlineInfo);
Statement* fgInlinePrependStatements(InlineInfo* inlineInfo);
void fgInlineAppendStatements(InlineInfo* inlineInfo, BasicBlock* block, Statement* stmt);
@@ -6682,18 +6725,16 @@ class Compiler
// bbNext order) sequence of basic blocks. (At times, we may require the blocks in a loop to be "properly numbered"
// in bbNext order; we use comparisons on the bbNum to decide order.)
// The blocks that define the body are
- // first <= top <= entry <= bottom .
+ // top <= entry <= bottom
// The "head" of the loop is a block outside the loop that has "entry" as a successor. We only support loops with a
// single 'head' block. The meanings of these blocks are given in the definitions below. Also see the picture at
// Compiler::optFindNaturalLoops().
struct LoopDsc
{
- BasicBlock* lpHead; // HEAD of the loop (not part of the looping of the loop) -- has ENTRY as a successor.
- BasicBlock* lpFirst; // FIRST block (in bbNext order) reachable within this loop. (May be part of a nested
- // loop, but not the outer loop.)
- BasicBlock* lpTop; // loop TOP (the back edge from lpBottom reaches here) (in most cases FIRST and TOP are the
- // same)
- BasicBlock* lpEntry; // the ENTRY in the loop (in most cases TOP or BOTTOM)
+ BasicBlock* lpHead; // HEAD of the loop (not part of the looping of the loop) -- has ENTRY as a successor.
+ BasicBlock* lpTop; // loop TOP (the back edge from lpBottom reaches here). Lexically first block (in bbNext
+ // order) reachable in this loop.
+ BasicBlock* lpEntry; // the ENTRY in the loop (in most cases TOP or BOTTOM)
BasicBlock* lpBottom; // loop BOTTOM (from here we have a back edge to the TOP)
BasicBlock* lpExit; // if a single exit loop this is the EXIT (in most cases BOTTOM)
@@ -6794,51 +6835,50 @@ class Compiler
// Returns "true" iff "*this" contains the blk.
bool lpContains(BasicBlock* blk) const
{
- return lpFirst->bbNum <= blk->bbNum && blk->bbNum <= lpBottom->bbNum;
+ return lpTop->bbNum <= blk->bbNum && blk->bbNum <= lpBottom->bbNum;
}
// Returns "true" iff "*this" (properly) contains the range [first, bottom] (allowing firsts
// to be equal, but requiring bottoms to be different.)
bool lpContains(BasicBlock* first, BasicBlock* bottom) const
{
- return lpFirst->bbNum <= first->bbNum && bottom->bbNum < lpBottom->bbNum;
+ return lpTop->bbNum <= first->bbNum && bottom->bbNum < lpBottom->bbNum;
}
// Returns "true" iff "*this" (properly) contains "lp2" (allowing firsts to be equal, but requiring
// bottoms to be different.)
bool lpContains(const LoopDsc& lp2) const
{
- return lpContains(lp2.lpFirst, lp2.lpBottom);
+ return lpContains(lp2.lpTop, lp2.lpBottom);
}
// Returns "true" iff "*this" is (properly) contained by the range [first, bottom]
// (allowing firsts to be equal, but requiring bottoms to be different.)
bool lpContainedBy(BasicBlock* first, BasicBlock* bottom) const
{
- return first->bbNum <= lpFirst->bbNum && lpBottom->bbNum < bottom->bbNum;
+ return first->bbNum <= lpTop->bbNum && lpBottom->bbNum < bottom->bbNum;
}
// Returns "true" iff "*this" is (properly) contained by "lp2"
// (allowing firsts to be equal, but requiring bottoms to be different.)
bool lpContainedBy(const LoopDsc& lp2) const
{
- return lpContains(lp2.lpFirst, lp2.lpBottom);
+ return lpContains(lp2.lpTop, lp2.lpBottom);
}
// Returns "true" iff "*this" is disjoint from the range [top, bottom].
bool lpDisjoint(BasicBlock* first, BasicBlock* bottom) const
{
- return bottom->bbNum < lpFirst->bbNum || lpBottom->bbNum < first->bbNum;
+ return bottom->bbNum < lpTop->bbNum || lpBottom->bbNum < first->bbNum;
}
// Returns "true" iff "*this" is disjoint from "lp2".
bool lpDisjoint(const LoopDsc& lp2) const
{
- return lpDisjoint(lp2.lpFirst, lp2.lpBottom);
+ return lpDisjoint(lp2.lpTop, lp2.lpBottom);
}
// Returns "true" iff the loop is well-formed (see code for defn).
bool lpWellFormed() const
{
- return lpFirst->bbNum <= lpTop->bbNum && lpTop->bbNum <= lpEntry->bbNum &&
- lpEntry->bbNum <= lpBottom->bbNum &&
+ return lpTop->bbNum <= lpEntry->bbNum && lpEntry->bbNum <= lpBottom->bbNum &&
(lpHead->bbNum < lpTop->bbNum || lpHead->bbNum > lpBottom->bbNum);
}
@@ -6846,30 +6886,29 @@ class Compiler
// blocks in a loop, e.g.:
// for (BasicBlock* const block : loop->LoopBlocks()) ...
// Currently, the loop blocks are expected to be in linear, lexical, `bbNext` order
- // from `lpFirst` through `lpBottom`, inclusive. All blocks in this range are considered
+ // from `lpTop` through `lpBottom`, inclusive. All blocks in this range are considered
// to be part of the loop.
//
BasicBlockRangeList LoopBlocks() const
{
- return BasicBlockRangeList(lpFirst, lpBottom);
+ return BasicBlockRangeList(lpTop, lpBottom);
}
};
protected:
- bool fgMightHaveLoop(); // returns true if there are any backedges
+ bool fgMightHaveLoop(); // returns true if there are any back edges
bool fgHasLoops; // True if this method has any loops, set in fgComputeReachability
public:
- LoopDsc* optLoopTable; // loop descriptor table
- unsigned char optLoopCount; // number of tracked loops
+ LoopDsc* optLoopTable; // loop descriptor table
+ unsigned char optLoopCount; // number of tracked loops
+ unsigned char loopAlignCandidates; // number of loops identified for alignment
#ifdef DEBUG
- unsigned char loopAlignCandidates; // number of loops identified for alignment
- unsigned char loopsAligned; // number of loops actually aligned
-#endif // DEBUG
+ unsigned char loopsAligned; // number of loops actually aligned
+#endif // DEBUG
bool optRecordLoop(BasicBlock* head,
- BasicBlock* first,
BasicBlock* top,
BasicBlock* entry,
BasicBlock* bottom,
@@ -6885,7 +6924,6 @@ class Compiler
#ifdef DEBUG
void optPrintLoopInfo(unsigned loopNum,
BasicBlock* lpHead,
- BasicBlock* lpFirst,
BasicBlock* lpTop,
BasicBlock* lpEntry,
BasicBlock* lpBottom,
@@ -6898,9 +6936,12 @@ class Compiler
void optCheckPreds();
#endif
+ // Determine if there are any potential loops, and set BBF_LOOP_HEAD on potential loop heads.
+ void optMarkLoopHeads();
+
void optSetBlockWeights();
- void optMarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk, bool excludeEndBlk);
+ void optScaleLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk);
void optUnmarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk);
@@ -7242,7 +7283,7 @@ class Compiler
//
bool lclNumIsCSE(unsigned lclNum) const
{
- return lvaTable[lclNum].lvIsCSE;
+ return lvaGetDesc(lclNum)->lvIsCSE;
}
#ifdef DEBUG
@@ -7363,7 +7404,7 @@ class Compiler
}
void considerGuardedDevirtualization(GenTreeCall* call,
- IL_OFFSETX iloffset,
+ IL_OFFSET ilOffset,
bool isInterface,
CORINFO_METHOD_HANDLE baseMethod,
CORINFO_CLASS_HANDLE baseClass,
@@ -7470,6 +7511,7 @@ class Compiler
// Redundant branch opts
//
PhaseStatus optRedundantBranches();
+ bool optRedundantRelop(BasicBlock* const block);
bool optRedundantBranch(BasicBlock* const block);
bool optJumpThread(BasicBlock* const block, BasicBlock* const domBlock, bool domIsSameRelop);
bool optReachable(BasicBlock* const fromBlock, BasicBlock* const toBlock, BasicBlock* const excludedBlock);
@@ -7940,7 +7982,7 @@ class Compiler
{
#ifdef TARGET_X86
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ LclVarDsc* varDsc = lvaGetDesc(lclNum);
assert(varDsc->lvIsParam);
@@ -7985,39 +8027,10 @@ class Compiler
bool eeIsJitIntrinsic(CORINFO_METHOD_HANDLE ftn);
bool eeIsFieldStatic(CORINFO_FIELD_HANDLE fldHnd);
- var_types eeGetFieldType(CORINFO_FIELD_HANDLE fldHnd);
+ var_types eeGetFieldType(CORINFO_FIELD_HANDLE fldHnd, CORINFO_CLASS_HANDLE* pStructHnd = nullptr);
#if defined(DEBUG) || defined(FEATURE_JIT_METHOD_PERF) || defined(FEATURE_SIMD) || defined(TRACK_LSRA_STATS)
- bool IsSuperPMIException(unsigned code)
- {
- // Copied from NDP\clr\src\ToolBox\SuperPMI\SuperPMI-Shared\ErrorHandling.h
-
- const unsigned EXCEPTIONCODE_DebugBreakorAV = 0xe0421000;
- const unsigned EXCEPTIONCODE_MC = 0xe0422000;
- const unsigned EXCEPTIONCODE_LWM = 0xe0423000;
- const unsigned EXCEPTIONCODE_SASM = 0xe0424000;
- const unsigned EXCEPTIONCODE_SSYM = 0xe0425000;
- const unsigned EXCEPTIONCODE_CALLUTILS = 0xe0426000;
- const unsigned EXCEPTIONCODE_TYPEUTILS = 0xe0427000;
- const unsigned EXCEPTIONCODE_ASSERT = 0xe0440000;
-
- switch (code)
- {
- case EXCEPTIONCODE_DebugBreakorAV:
- case EXCEPTIONCODE_MC:
- case EXCEPTIONCODE_LWM:
- case EXCEPTIONCODE_SASM:
- case EXCEPTIONCODE_SSYM:
- case EXCEPTIONCODE_CALLUTILS:
- case EXCEPTIONCODE_TYPEUTILS:
- case EXCEPTIONCODE_ASSERT:
- return true;
- default:
- return false;
- }
- }
-
const char* eeGetMethodName(CORINFO_METHOD_HANDLE hnd, const char** className);
const char* eeGetMethodFullName(CORINFO_METHOD_HANDLE hnd);
unsigned compMethodHash(CORINFO_METHOD_HANDLE methodHandle);
@@ -8156,19 +8169,15 @@ class Compiler
unsigned eeBoundariesCount;
- struct boundariesDsc
- {
- UNATIVE_OFFSET nativeIP;
- IL_OFFSET ilOffset;
- unsigned sourceReason;
- } * eeBoundaries; // Boundaries to report to EE
+ ICorDebugInfo::OffsetMapping* eeBoundaries; // Boundaries to report to the EE
void eeSetLIcount(unsigned count);
- void eeSetLIinfo(unsigned which, UNATIVE_OFFSET offs, unsigned srcIP, bool stkEmpty, bool callInstruction);
+ void eeSetLIinfo(unsigned which, UNATIVE_OFFSET offs, IPmappingDscKind kind, const ILLocation& loc);
void eeSetLIdone();
#ifdef DEBUG
static void eeDispILOffs(IL_OFFSET offs);
- static void eeDispLineInfo(const boundariesDsc* line);
+ static void eeDispSourceMappingOffs(uint32_t offs);
+ static void eeDispLineInfo(const ICorDebugInfo::OffsetMapping* line);
void eeDispLineInfos();
#endif // DEBUG
@@ -8259,9 +8268,8 @@ class Compiler
static CORINFO_METHOD_HANDLE eeFindHelper(unsigned helper);
static CorInfoHelpFunc eeGetHelperNum(CORINFO_METHOD_HANDLE method);
- static fgWalkPreFn CountSharedStaticHelper;
static bool IsSharedStaticHelper(GenTree* tree);
- static bool IsGcSafePoint(GenTree* tree);
+ static bool IsGcSafePoint(GenTreeCall* call);
static CORINFO_FIELD_HANDLE eeFindJitDataOffs(unsigned jitDataOffs);
// returns true/false if 'field' is a Jit Data offset
@@ -8284,34 +8292,27 @@ class Compiler
public:
CodeGenInterface* codeGen;
- // The following holds information about instr offsets in terms of generated code.
-
- struct IPmappingDsc
- {
- IPmappingDsc* ipmdNext; // next line# record
- emitLocation ipmdNativeLoc; // the emitter location of the native code corresponding to the IL offset
- IL_OFFSETX ipmdILoffsx; // the instr offset
- bool ipmdIsLabel; // Can this code be a branch label?
- };
-
// Record the instr offset mapping to the generated code
- IPmappingDsc* genIPmappingList;
- IPmappingDsc* genIPmappingLast;
+ jitstd::list genIPmappings;
+
+#ifdef DEBUG
+ jitstd::list genPreciseIPmappings;
+#endif
// Managed RetVal - A side hash table meant to record the mapping from a
- // GT_CALL node to its IL offset. This info is used to emit sequence points
+ // GT_CALL node to its debug info. This info is used to emit sequence points
// that can be used by debugger to determine the native offset at which the
// managed RetVal will be available.
//
- // In fact we can store IL offset in a GT_CALL node. This was ruled out in
- // favor of a side table for two reasons: 1) We need IL offset for only those
+ // In fact we can store debug info in a GT_CALL node. This was ruled out in
+ // favor of a side table for two reasons: 1) We need debug info for only those
// GT_CALL nodes (created during importation) that correspond to an IL call and
// whose return type is other than TYP_VOID. 2) GT_CALL node is a frequently used
// structure and IL offset is needed only when generating debuggable code. Therefore
// it is desirable to avoid memory size penalty in retail scenarios.
- typedef JitHashTable, IL_OFFSETX> CallSiteILOffsetTable;
- CallSiteILOffsetTable* genCallSite2ILOffsetMap;
+ typedef JitHashTable, DebugInfo> CallSiteDebugInfoTable;
+ CallSiteDebugInfoTable* genCallSite2DebugInfoMap;
unsigned genReturnLocal; // Local number for the return value when applicable.
BasicBlock* genReturnBB; // jumped to when not optimizing for speed.
@@ -8886,7 +8887,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// type of an arg node is TYP_BYREF and a local node is TYP_SIMD or TYP_STRUCT.
bool isSIMDTypeLocal(GenTree* tree)
{
- return tree->OperIsLocal() && lvaTable[tree->AsLclVarCommon()->GetLclNum()].lvSIMDType;
+ return tree->OperIsLocal() && lvaGetDesc(tree->AsLclVarCommon())->lvSIMDType;
}
// Returns true if the lclVar is an opaque SIMD type.
@@ -8910,7 +8911,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
{
if (isSIMDTypeLocal(tree))
{
- return lvaTable[tree->AsLclVarCommon()->GetLclNum()].GetSimdBaseJitType();
+ return lvaGetDesc(tree->AsLclVarCommon())->GetSimdBaseJitType();
}
return CORINFO_TYPE_UNDEF;
@@ -9063,7 +9064,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
else
{
// Verify and record that AVX2 isn't supported
- compVerifyInstructionSetUnuseable(InstructionSet_AVX2);
+ compVerifyInstructionSetUnusable(InstructionSet_AVX2);
assert(getSIMDSupportLevel() >= SIMD_SSE2_Supported);
return TYP_SIMD16;
}
@@ -9106,7 +9107,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
assert(getSIMDSupportLevel() >= SIMD_SSE2_Supported);
// Verify and record that AVX2 isn't supported
- compVerifyInstructionSetUnuseable(InstructionSet_AVX2);
+ compVerifyInstructionSetUnusable(InstructionSet_AVX2);
return XMM_REGSIZE_BYTES;
}
#elif defined(TARGET_ARM64)
@@ -9243,8 +9244,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// Is this var is of type simd struct?
bool lclVarIsSIMDType(unsigned varNum)
{
- LclVarDsc* varDsc = lvaTable + varNum;
- return varDsc->lvIsSIMDType();
+ return lvaGetDesc(varNum)->lvIsSIMDType();
}
// Is this Local node a SIMD local?
@@ -9341,15 +9341,15 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#endif
}
- // Ensure that code will not execute if an instruction set is useable. Call only
- // if the instruction set has previously reported as unuseable, but when
+ // Ensure that code will not execute if an instruction set is usable. Call only
+ // if the instruction set has previously reported as unusable, but when
// that that status has not yet been recorded to the AOT compiler
- void compVerifyInstructionSetUnuseable(CORINFO_InstructionSet isa)
+ void compVerifyInstructionSetUnusable(CORINFO_InstructionSet isa)
{
// use compExactlyDependsOn to capture are record the use of the isa
- bool isaUseable = compExactlyDependsOn(isa);
- // Assert that the is unuseable. If true, this function should never be called.
- assert(!isaUseable);
+ bool isaUsable = compExactlyDependsOn(isa);
+ // Assert that the is unusable. If true, this function should never be called.
+ assert(!isaUsable);
}
// Answer the question: Is a particular ISA allowed to be used implicitly by optimizations?
@@ -9403,20 +9403,23 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
InlineResult* compInlineResult; // The result of importing the inlinee method.
- bool compDoAggressiveInlining; // If true, mark every method as CORINFO_FLG_FORCEINLINE
- bool compJmpOpUsed; // Does the method do a JMP
- bool compLongUsed; // Does the method use TYP_LONG
- bool compFloatingPointUsed; // Does the method use TYP_FLOAT or TYP_DOUBLE
- bool compTailCallUsed; // Does the method do a tailcall
- bool compLocallocUsed; // Does the method use localloc.
- bool compLocallocOptimized; // Does the method have an optimized localloc
- bool compQmarkUsed; // Does the method use GT_QMARK/GT_COLON
- bool compQmarkRationalized; // Is it allowed to use a GT_QMARK/GT_COLON node.
- bool compUnsafeCastUsed; // Does the method use LDIND/STIND to cast between scalar/refernce types
- bool compHasBackwardJump; // Does the method (or some inlinee) have a lexically backwards jump?
- bool compSwitchedToOptimized; // Codegen initially was Tier0 but jit switched to FullOpts
- bool compSwitchedToMinOpts; // Codegen initially was Tier1/FullOpts but jit switched to MinOpts
- bool compSuppressedZeroInit; // There are vars with lvSuppressedZeroInit set
+ bool compDoAggressiveInlining; // If true, mark every method as CORINFO_FLG_FORCEINLINE
+ bool compJmpOpUsed; // Does the method do a JMP
+ bool compLongUsed; // Does the method use TYP_LONG
+ bool compFloatingPointUsed; // Does the method use TYP_FLOAT or TYP_DOUBLE
+ bool compTailCallUsed; // Does the method do a tailcall
+ bool compTailPrefixSeen; // Does the method IL have tail. prefix
+ bool compLocallocSeen; // Does the method IL have localloc opcode
+ bool compLocallocUsed; // Does the method use localloc.
+ bool compLocallocOptimized; // Does the method have an optimized localloc
+ bool compQmarkUsed; // Does the method use GT_QMARK/GT_COLON
+ bool compQmarkRationalized; // Is it allowed to use a GT_QMARK/GT_COLON node.
+ bool compUnsafeCastUsed; // Does the method use LDIND/STIND to cast between scalar/refernce types
+ bool compHasBackwardJump; // Does the method (or some inlinee) have a lexically backwards jump?
+ bool compHasBackwardJumpInHandler; // Does the method have a lexically backwards jump in a handler?
+ bool compSwitchedToOptimized; // Codegen initially was Tier0 but jit switched to FullOpts
+ bool compSwitchedToMinOpts; // Codegen initially was Tier1/FullOpts but jit switched to MinOpts
+ bool compSuppressedZeroInit; // There are vars with lvSuppressedZeroInit set
// NOTE: These values are only reliable after
// the importing is completely finished.
@@ -9718,6 +9721,9 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// If set, perform adaptive loop alignment that limits number of padding based on loop size.
bool compJitAlignLoopAdaptive;
+ // If set, tries to hide alignment instructions behind unconditional jumps.
+ bool compJitHideAlignBehindJmp;
+
#ifdef LATE_DISASM
bool doLateDisasm; // Run the late disassembler
#endif // LATE_DISASM
@@ -9999,7 +10005,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
bool compInitMem : 1; // Is the CORINFO_OPT_INIT_LOCALS bit set in the method info options?
bool compProfilerCallback : 1; // JIT inserted a profiler Enter callback
bool compPublishStubParam : 1; // EAX captured in prolog will be available through an intrinsic
- bool compRetBuffDefStack : 1; // The ret buff argument definitely points into the stack.
bool compHasNextCallRetAddr : 1; // The NextCallReturnAddress intrinsic is used.
var_types compRetType; // Return type of the method as declared in IL
@@ -10223,6 +10228,10 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
return !info.compInitMem && opts.compDbgCode;
}
+ // Returns true if the jit supports having patchpoints in this method.
+ // Optionally, get the reason why not.
+ bool compCanHavePatchpoints(const char** reason = nullptr);
+
#if defined(DEBUG)
void compDispLocalVars();
@@ -10452,6 +10461,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
VarName compVarName(regNumber reg, bool isFloatReg = false);
const char* compRegVarName(regNumber reg, bool displayVar = false, bool isFloatReg = false);
const char* compRegNameForSize(regNumber reg, size_t size);
+ const char* compFPregVarName(unsigned fpReg, bool displayVar = false);
void compDspSrcLinesByNativeIP(UNATIVE_OFFSET curIP);
void compDspSrcLinesByLineNum(unsigned line, bool seek = false);
#endif // DEBUG
@@ -11531,6 +11541,42 @@ class GenTreeVisitor
break;
}
+#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+#if defined(FEATURE_SIMD)
+ case GT_SIMD:
+#endif
+#if defined(FEATURE_HW_INTRINSICS)
+ case GT_HWINTRINSIC:
+#endif
+ if (TVisitor::UseExecutionOrder && node->IsReverseOp())
+ {
+ assert(node->AsMultiOp()->GetOperandCount() == 2);
+
+ result = WalkTree(&node->AsMultiOp()->Op(2), node);
+ if (result == fgWalkResult::WALK_ABORT)
+ {
+ return result;
+ }
+ result = WalkTree(&node->AsMultiOp()->Op(1), node);
+ if (result == fgWalkResult::WALK_ABORT)
+ {
+ return result;
+ }
+ }
+ else
+ {
+ for (GenTree** use : node->AsMultiOp()->UseEdges())
+ {
+ result = WalkTree(use, node);
+ if (result == fgWalkResult::WALK_ABORT)
+ {
+ return result;
+ }
+ }
+ }
+ break;
+#endif // defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+
// Binary nodes
default:
{
@@ -11955,7 +12001,9 @@ const instruction INS_SQRT = INS_vsqrt;
const instruction INS_MULADD = INS_madd;
inline const instruction INS_BREAKPOINT_osHelper()
{
- return TargetOS::IsUnix ? INS_brk : INS_bkpt;
+ // GDB needs the encoding of brk #0
+ // Windbg needs the encoding of brk #F000
+ return TargetOS::IsUnix ? INS_brk_unix : INS_brk_windows;
}
#define INS_BREAKPOINT INS_BREAKPOINT_osHelper()
diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp
index 0c446f2020d0b9..8fad38af36f846 100644
--- a/src/coreclr/jit/compiler.hpp
+++ b/src/coreclr/jit/compiler.hpp
@@ -543,7 +543,7 @@ extern const BYTE genTypeSizes[TYP_COUNT];
template
inline unsigned genTypeSize(T value)
{
- assert((unsigned)TypeGet(value) < _countof(genTypeSizes));
+ assert((unsigned)TypeGet(value) < ArrLen(genTypeSizes));
return genTypeSizes[TypeGet(value)];
}
@@ -559,7 +559,7 @@ extern const BYTE genTypeStSzs[TYP_COUNT];
template
inline unsigned genTypeStSz(T value)
{
- assert((unsigned)TypeGet(value) < _countof(genTypeStSzs));
+ assert((unsigned)TypeGet(value) < ArrLen(genTypeStSzs));
return genTypeStSzs[TypeGet(value)];
}
@@ -822,9 +822,16 @@ inline GenTree::GenTree(genTreeOps oper, var_types type DEBUGARG(bool largeNode)
/*****************************************************************************/
-inline Statement* Compiler::gtNewStmt(GenTree* expr, IL_OFFSETX offset)
+inline Statement* Compiler::gtNewStmt(GenTree* expr)
{
- Statement* stmt = new (this->getAllocator(CMK_ASTNode)) Statement(expr, offset DEBUGARG(compStatementID++));
+ Statement* stmt = new (this->getAllocator(CMK_ASTNode)) Statement(expr DEBUGARG(compStatementID++));
+ return stmt;
+}
+
+inline Statement* Compiler::gtNewStmt(GenTree* expr, const DebugInfo& di)
+{
+ Statement* stmt = gtNewStmt(expr);
+ stmt->SetDebugInfo(di);
return stmt;
}
@@ -1377,6 +1384,10 @@ inline void GenTree::SetOperRaw(genTreeOps oper)
// Please do not do anything here other than assign to gtOper (debug-only
// code is OK, but should be kept to a minimum).
RecordOperBashing(OperGet(), oper); // nop unless NODEBASH_STATS is enabled
+
+ // Bashing to MultiOp nodes is not currently supported.
+ assert(!OperIsMultiOp(oper));
+
gtOper = oper;
}
@@ -1778,7 +1789,7 @@ inline unsigned Compiler::lvaGrabTempWithImplicitUse(bool shortLifetime DEBUGARG
unsigned lclNum = lvaGrabTemp(shortLifetime DEBUGARG(reason));
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ LclVarDsc* varDsc = lvaGetDesc(lclNum);
// Note the implicit use
varDsc->lvImplicitlyReferenced = 1;
@@ -1866,7 +1877,7 @@ inline void LclVarDsc::incRefCnts(weight_t weight, Compiler* comp, RefCountState
{
// Depending on the promotion type, increment the ref count for the parent struct as well.
promotionType = comp->lvaGetParentPromotionType(this);
- LclVarDsc* parentvarDsc = &comp->lvaTable[lvParentLcl];
+ LclVarDsc* parentvarDsc = comp->lvaGetDesc(lvParentLcl);
assert(!parentvarDsc->lvRegStruct);
if (promotionType == Compiler::PROMOTION_TYPE_DEPENDENT)
{
@@ -1877,9 +1888,7 @@ inline void LclVarDsc::incRefCnts(weight_t weight, Compiler* comp, RefCountState
#ifdef DEBUG
if (comp->verbose)
{
- unsigned varNum = (unsigned)(this - comp->lvaTable);
- assert(&comp->lvaTable[varNum] == this);
- printf("New refCnts for V%02u: refCnt = %2u, refCntWtd = %s\n", varNum, lvRefCnt(state),
+ printf("New refCnts for V%02u: refCnt = %2u, refCntWtd = %s\n", comp->lvaGetLclNum(this), lvRefCnt(state),
refCntWtd2str(lvRefCntWtd(state)));
}
#endif
@@ -1893,9 +1902,7 @@ inline void LclVarDsc::incRefCnts(weight_t weight, Compiler* comp, RefCountState
inline VARSET_VALRET_TP Compiler::lvaStmtLclMask(Statement* stmt)
{
- unsigned varNum;
- LclVarDsc* varDsc;
- VARSET_TP lclMask(VarSetOps::MakeEmpty(this));
+ VARSET_TP lclMask(VarSetOps::MakeEmpty(this));
assert(fgStmtListThreaded);
@@ -1906,9 +1913,7 @@ inline VARSET_VALRET_TP Compiler::lvaStmtLclMask(Statement* stmt)
continue;
}
- varNum = tree->AsLclVarCommon()->GetLclNum();
- assert(varNum < lvaCount);
- varDsc = lvaTable + varNum;
+ const LclVarDsc* varDsc = lvaGetDesc(tree->AsLclVarCommon());
if (!varDsc->lvTracked)
{
@@ -2067,11 +2072,8 @@ inline
bool fConservative = false;
if (varNum >= 0)
{
- LclVarDsc* varDsc;
-
- assert((unsigned)varNum < lvaCount);
- varDsc = lvaTable + varNum;
- bool isPrespilledArg = false;
+ LclVarDsc* varDsc = lvaGetDesc(varNum);
+ bool isPrespilledArg = false;
#if defined(TARGET_ARM) && defined(PROFILING_SUPPORTED)
isPrespilledArg = varDsc->lvIsParam && compIsProfilerHookNeeded() &&
lvaIsPreSpilled(varNum, codeGen->regSet.rsMaskPreSpillRegs(false));
@@ -2237,21 +2239,13 @@ inline
inline bool Compiler::lvaIsParameter(unsigned varNum)
{
- LclVarDsc* varDsc;
-
- assert(varNum < lvaCount);
- varDsc = lvaTable + varNum;
-
+ const LclVarDsc* varDsc = lvaGetDesc(varNum);
return varDsc->lvIsParam;
}
inline bool Compiler::lvaIsRegArgument(unsigned varNum)
{
- LclVarDsc* varDsc;
-
- assert(varNum < lvaCount);
- varDsc = lvaTable + varNum;
-
+ LclVarDsc* varDsc = lvaGetDesc(varNum);
return varDsc->lvIsRegArg;
}
@@ -2264,7 +2258,7 @@ inline bool Compiler::lvaIsOriginalThisArg(unsigned varNum)
#ifdef DEBUG
if (isOriginalThisArg)
{
- LclVarDsc* varDsc = lvaTable + varNum;
+ LclVarDsc* varDsc = lvaGetDesc(varNum);
// Should never write to or take the address of the original 'this' arg
CLANG_FORMAT_COMMENT_ANCHOR;
@@ -3243,7 +3237,7 @@ inline void Compiler::optAssertionReset(AssertionIndex limit)
AssertionDsc* curAssertion = optGetAssertion(index);
optAssertionCount--;
unsigned lclNum = curAssertion->op1.lcl.lclNum;
- assert(lclNum < lvaTableCnt);
+ assert(lclNum < lvaCount);
BitVecOps::RemoveElemD(apTraits, GetAssertionDep(lclNum), index - 1);
//
@@ -3610,17 +3604,6 @@ inline CorInfoHelpFunc Compiler::eeGetHelperNum(CORINFO_METHOD_HANDLE method)
return ((CorInfoHelpFunc)(((size_t)method) >> 2));
}
-inline Compiler::fgWalkResult Compiler::CountSharedStaticHelper(GenTree** pTree, fgWalkData* data)
-{
- if (Compiler::IsSharedStaticHelper(*pTree))
- {
- int* pCount = (int*)data->pCallbackData;
- (*pCount)++;
- }
-
- return WALK_CONTINUE;
-}
-
// TODO-Cleanup: Replace calls to IsSharedStaticHelper with new HelperCallProperties
//
@@ -3639,9 +3622,8 @@ inline bool Compiler::IsSharedStaticHelper(GenTree* tree)
helper == CORINFO_HELP_STRCNS || helper == CORINFO_HELP_BOX ||
// helpers being added to IsSharedStaticHelper
- helper == CORINFO_HELP_GETSTATICFIELDADDR_CONTEXT || helper == CORINFO_HELP_GETSTATICFIELDADDR_TLS ||
- helper == CORINFO_HELP_GETGENERICS_GCSTATIC_BASE || helper == CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE ||
- helper == CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE ||
+ helper == CORINFO_HELP_GETSTATICFIELDADDR_TLS || helper == CORINFO_HELP_GETGENERICS_GCSTATIC_BASE ||
+ helper == CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE || helper == CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE ||
helper == CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE ||
helper == CORINFO_HELP_GETSHARED_GCSTATIC_BASE || helper == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE ||
@@ -3667,34 +3649,30 @@ inline bool Compiler::IsSharedStaticHelper(GenTree* tree)
return result1;
}
-inline bool Compiler::IsGcSafePoint(GenTree* tree)
+inline bool Compiler::IsGcSafePoint(GenTreeCall* call)
{
- if (tree->IsCall())
+ if (!call->IsFastTailCall())
{
- GenTreeCall* call = tree->AsCall();
- if (!call->IsFastTailCall())
+ if (call->IsUnmanaged() && call->IsSuppressGCTransition())
{
- if (call->IsUnmanaged() && call->IsSuppressGCTransition())
- {
- // Both an indirect and user calls can be unmanaged
- // and have a request to suppress the GC transition so
- // the check is done prior to the separate handling of
- // indirect and user calls.
- return false;
- }
- else if (call->gtCallType == CT_INDIRECT)
+ // Both an indirect and user calls can be unmanaged
+ // and have a request to suppress the GC transition so
+ // the check is done prior to the separate handling of
+ // indirect and user calls.
+ return false;
+ }
+ else if (call->gtCallType == CT_INDIRECT)
+ {
+ return true;
+ }
+ else if (call->gtCallType == CT_USER_FUNC)
+ {
+ if ((call->gtCallMoreFlags & GTF_CALL_M_NOGCCHECK) == 0)
{
return true;
}
- else if (call->gtCallType == CT_USER_FUNC)
- {
- if ((call->gtCallMoreFlags & GTF_CALL_M_NOGCCHECK) == 0)
- {
- return true;
- }
- }
- // otherwise we have a CT_HELPER
}
+ // otherwise we have a CT_HELPER
}
return false;
@@ -3773,59 +3751,6 @@ inline bool Compiler::compIsProfilerHookNeeded()
#endif // !PROFILING_SUPPORTED
}
-/*****************************************************************************
- *
- * Check for the special case where the object is the constant 0.
- * As we can't even fold the tree (null+fldOffs), we are left with
- * op1 and op2 both being a constant. This causes lots of problems.
- * We simply grab a temp and assign 0 to it and use it in place of the NULL.
- */
-
-inline GenTree* Compiler::impCheckForNullPointer(GenTree* obj)
-{
- /* If it is not a GC type, we will be able to fold it.
- So don't need to do anything */
-
- if (!varTypeIsGC(obj->TypeGet()))
- {
- return obj;
- }
-
- if (obj->gtOper == GT_CNS_INT)
- {
- assert(obj->gtType == TYP_REF || obj->gtType == TYP_BYREF);
-
- // We can see non-zero byrefs for RVA statics or for frozen strings.
- if (obj->AsIntCon()->gtIconVal != 0)
- {
-#ifdef DEBUG
- if (!obj->TypeIs(TYP_BYREF))
- {
- assert(obj->TypeIs(TYP_REF));
- assert(obj->IsIconHandle(GTF_ICON_STR_HDL));
- if (!doesMethodHaveFrozenString())
- {
- assert(compIsForInlining());
- assert(impInlineInfo->InlinerCompiler->doesMethodHaveFrozenString());
- }
- }
-#endif // DEBUG
- return obj;
- }
-
- unsigned tmp = lvaGrabTemp(true DEBUGARG("CheckForNullPointer"));
-
- // We don't need to spill while appending as we are only assigning
- // NULL to a freshly-grabbed temp.
-
- impAssignTempGen(tmp, obj, (unsigned)CHECK_SPILL_NONE);
-
- obj = gtNewLclvNode(tmp, obj->gtType);
- }
-
- return obj;
-}
-
/*****************************************************************************
*
* Check for the special case where the object is the methods original 'this' pointer.
@@ -3961,8 +3886,7 @@ inline Compiler::lvaPromotionType Compiler::lvaGetPromotionType(const LclVarDsc*
inline Compiler::lvaPromotionType Compiler::lvaGetPromotionType(unsigned varNum)
{
- assert(varNum < lvaCount);
- return lvaGetPromotionType(&lvaTable[varNum]);
+ return lvaGetPromotionType(lvaGetDesc(varNum));
}
/*****************************************************************************
@@ -3973,7 +3897,6 @@ inline Compiler::lvaPromotionType Compiler::lvaGetPromotionType(unsigned varNum)
inline Compiler::lvaPromotionType Compiler::lvaGetParentPromotionType(const LclVarDsc* varDsc)
{
assert(varDsc->lvIsStructField);
- assert(varDsc->lvParentLcl < lvaCount);
lvaPromotionType promotionType = lvaGetPromotionType(varDsc->lvParentLcl);
assert(promotionType != PROMOTION_TYPE_NONE);
@@ -3987,8 +3910,7 @@ inline Compiler::lvaPromotionType Compiler::lvaGetParentPromotionType(const LclV
inline Compiler::lvaPromotionType Compiler::lvaGetParentPromotionType(unsigned varNum)
{
- assert(varNum < lvaCount);
- return lvaGetParentPromotionType(&lvaTable[varNum]);
+ return lvaGetParentPromotionType(lvaGetDesc(varNum));
}
/*****************************************************************************
@@ -4311,6 +4233,7 @@ void GenTree::VisitOperands(TVisitor visitor)
case GT_BOX:
case GT_ALLOCOBJ:
case GT_INIT_VAL:
+ case GT_RUNTIMELOOKUP:
case GT_JTRUE:
case GT_SWITCH:
case GT_NULLCHECK:
@@ -4327,32 +4250,22 @@ void GenTree::VisitOperands(TVisitor visitor)
return;
// Variadic nodes
-#ifdef FEATURE_SIMD
+#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+#if defined(FEATURE_SIMD)
case GT_SIMD:
- if (this->AsSIMD()->gtSIMDIntrinsicID == SIMDIntrinsicInitN)
- {
- assert(this->AsSIMD()->gtOp1 != nullptr);
- this->AsSIMD()->gtOp1->VisitListOperands(visitor);
- }
- else
- {
- VisitBinOpOperands(visitor);
- }
- return;
-#endif // FEATURE_SIMD
-
-#ifdef FEATURE_HW_INTRINSICS
+#endif
+#if defined(FEATURE_HW_INTRINSICS)
case GT_HWINTRINSIC:
- if ((this->AsHWIntrinsic()->gtOp1 != nullptr) && this->AsHWIntrinsic()->gtOp1->OperIsList())
- {
- this->AsHWIntrinsic()->gtOp1->VisitListOperands(visitor);
- }
- else
+#endif
+ for (GenTree* operand : this->AsMultiOp()->Operands())
{
- VisitBinOpOperands(visitor);
+ if (visitor(operand) == VisitResult::Abort)
+ {
+ break;
+ }
}
return;
-#endif // FEATURE_HW_INTRINSICS
+#endif // defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
// Special nodes
case GT_PHI:
@@ -4498,20 +4411,6 @@ void GenTree::VisitOperands(TVisitor visitor)
}
}
-template
-GenTree::VisitResult GenTree::VisitListOperands(TVisitor visitor)
-{
- for (GenTreeArgList* node = this->AsArgList(); node != nullptr; node = node->Rest())
- {
- if (visitor(node->gtOp1) == VisitResult::Abort)
- {
- return VisitResult::Abort;
- }
- }
-
- return VisitResult::Continue;
-}
-
template
void GenTree::VisitBinOpOperands(TVisitor visitor)
{
@@ -4778,6 +4677,45 @@ inline void LclVarDsc::setLvRefCntWtd(weight_t newValue, RefCountState state)
m_lvRefCntWtd = newValue;
}
+//------------------------------------------------------------------------------
+// compCanHavePatchpoints: return true if patchpoints are supported in this
+// method.
+//
+// Arguments:
+// reason - [out, optional] reason why patchpoints are not supported
+//
+// Returns:
+// True if patchpoints are supported in this method.
+//
+inline bool Compiler::compCanHavePatchpoints(const char** reason)
+{
+ const char* whyNot = nullptr;
+
+#ifdef FEATURE_ON_STACK_REPLACEMENT
+ if (compLocallocSeen)
+ {
+ whyNot = "localloc";
+ }
+ else if (compHasBackwardJumpInHandler)
+ {
+ whyNot = "loop in handler";
+ }
+ else if (opts.IsReversePInvoke())
+ {
+ whyNot = "reverse pinvoke";
+ }
+#else
+ whyNot = "OSR feature not defined in build";
+#endif
+
+ if (reason != nullptr)
+ {
+ *reason = whyNot;
+ }
+
+ return whyNot == nullptr;
+}
+
/*****************************************************************************/
#endif //_COMPILER_HPP_
/*****************************************************************************/
diff --git a/src/coreclr/jit/compphases.h b/src/coreclr/jit/compphases.h
index bb992440f8e3d4..b8805fca753529 100644
--- a/src/coreclr/jit/compphases.h
+++ b/src/coreclr/jit/compphases.h
@@ -87,6 +87,7 @@ CompPhaseNameMacro(PHASE_INSERT_GC_POLLS, "Insert GC Polls",
CompPhaseNameMacro(PHASE_DETERMINE_FIRST_COLD_BLOCK, "Determine first cold block", "COLD-BLK", false, -1, true)
CompPhaseNameMacro(PHASE_RATIONALIZE, "Rationalize IR", "RAT", false, -1, false)
CompPhaseNameMacro(PHASE_SIMPLE_LOWERING, "Do 'simple' lowering", "SMP-LWR", false, -1, false)
+CompPhaseNameMacro(PHASE_ALIGN_LOOPS, "Place 'align' instructions", "LOOP-ALIGN", false, -1, false)
CompPhaseNameMacro(PHASE_LCLVARLIVENESS, "Local var liveness", "LIVENESS", true, -1, false)
CompPhaseNameMacro(PHASE_LCLVARLIVENESS_INIT, "Local var liveness init", "LIV-INIT", false, PHASE_LCLVARLIVENESS, false)
diff --git a/src/coreclr/jit/copyprop.cpp b/src/coreclr/jit/copyprop.cpp
index 6e0583c043e587..1c80436902f90e 100644
--- a/src/coreclr/jit/copyprop.cpp
+++ b/src/coreclr/jit/copyprop.cpp
@@ -213,7 +213,7 @@ void Compiler::optCopyProp(BasicBlock* block, Statement* stmt, GenTree* tree, Lc
{
continue;
}
- if (optCopyProp_LclVarScore(&lvaTable[lclNum], &lvaTable[newLclNum], true) <= 0)
+ if (optCopyProp_LclVarScore(lvaGetDesc(lclNum), lvaGetDesc(newLclNum), true) <= 0)
{
continue;
}
diff --git a/src/coreclr/jit/debuginfo.cpp b/src/coreclr/jit/debuginfo.cpp
new file mode 100644
index 00000000000000..ed5edeb113da3d
--- /dev/null
+++ b/src/coreclr/jit/debuginfo.cpp
@@ -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.
+
+#include "jitpch.h"
+#include "debuginfo.h"
+
+#ifdef DEBUG
+//------------------------------------------------------------------------
+// Dump: Print a textual representation of this ILLocation.
+//
+// Notes:
+// For invalid ILLocations, we print '???'.
+// Otherwise the offset and flags are printed in the format 0xabc[EC].
+//
+void ILLocation::Dump() const
+{
+ if (!IsValid())
+ {
+ printf("???");
+ }
+ else
+ {
+ printf("0x%03X[", GetOffset());
+ printf("%c", IsStackEmpty() ? 'E' : '-');
+ printf("%c", IsCall() ? 'C' : '-');
+ printf("]");
+ }
+}
+
+//------------------------------------------------------------------------
+// Dump: Print a textual representation of this DebugInfo.
+//
+// Parameters:
+// recurse - print the full path back to the root, separated by arrows.
+//
+// Notes:
+// The DebugInfo is printed in the format
+//
+// INL02 @ 0xabc[EC]
+//
+// Before '@' is the ordinal of the inline context, then comes the IL
+// offset, and then comes the IL location flags (stack Empty, isCall).
+//
+// If 'recurse' is specified then dump the full DebugInfo path to the
+// root in the format
+//
+// INL02 @ 0xabc[EC] <- INL01 @ 0x123[EC] <- ... <- INLRT @ 0x456[EC]
+//
+// with the left most entry being the inner most inlined statement.
+void DebugInfo::Dump(bool recurse) const
+{
+ InlineContext* context = GetInlineContext();
+ if (context != nullptr)
+ {
+ if (context->IsRoot())
+ {
+ printf("INLRT @ ");
+ }
+ else if (context->GetOrdinal() != 0)
+ {
+ printf(FMT_INL_CTX " @ ", context->GetOrdinal());
+ }
+ }
+
+ GetLocation().Dump();
+
+ DebugInfo par;
+ if (recurse && GetParent(&par))
+ {
+ printf(" <- ");
+ par.Dump(recurse);
+ }
+}
+
+//------------------------------------------------------------------------
+// Validate: Validate this DebugInfo instance.
+//
+// Notes:
+// This validates that if there is DebugInfo, then it looks sane by checking
+// that the IL location correctly points to the beginning of an IL instruction.
+//
+void DebugInfo::Validate() const
+{
+ DebugInfo di = *this;
+ do
+ {
+ if (!di.IsValid())
+ continue;
+
+ bool isValidOffs = di.GetLocation().GetOffset() < di.GetInlineContext()->GetILSize();
+ if (isValidOffs)
+ {
+ bool isValidStart = di.GetInlineContext()->GetILInstsSet()->bitVectTest(di.GetLocation().GetOffset());
+ assert(isValidStart &&
+ "Detected invalid debug info: IL offset does not refer to the start of an IL instruction");
+ }
+ else
+ {
+ assert(!"Detected invalid debug info: IL offset is out of range");
+ }
+
+ } while (di.GetParent(&di));
+}
+#endif
+
+//------------------------------------------------------------------------
+// GetParent: Get debug info for the parent statement that inlined the
+// statement for this debug info.
+//
+// Parameters:
+// parent [out] - Debug info for the location that inlined this statement.
+//
+// Return Value:
+// True if the current debug info is valid and has a parent; otherwise false.
+// On false return, the 'parent' parameter is unaffected.
+//
+bool DebugInfo::GetParent(DebugInfo* parent) const
+{
+ if ((m_inlineContext == nullptr) || m_inlineContext->IsRoot())
+ return false;
+
+ *parent = DebugInfo(m_inlineContext->GetParent(), m_inlineContext->GetLocation());
+ return true;
+}
+
+//------------------------------------------------------------------------
+// GetRoot: Get debug info for the statement in the root function that
+// eventually led to this debug info through inlines.
+//
+// Return Value:
+// If this DebugInfo instance is valid, returns a DebugInfo instance
+// representing the call in the root function that eventually inlined the
+// statement this DebugInfo describes.
+//
+// If this DebugInfo instance is invalid, returns an invalid DebugInfo instance.
+//
+DebugInfo DebugInfo::GetRoot() const
+{
+ DebugInfo result = *this;
+ while (result.GetParent(&result))
+ {
+ }
+
+ return result;
+}
diff --git a/src/coreclr/jit/debuginfo.h b/src/coreclr/jit/debuginfo.h
new file mode 100644
index 00000000000000..c107c3dc519de8
--- /dev/null
+++ b/src/coreclr/jit/debuginfo.h
@@ -0,0 +1,136 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#ifndef _DEBUGINFO_H_
+#define _DEBUGINFO_H_
+
+#include "jit.h"
+
+class InlineContext;
+
+// Represents information about the location of an IL instruction.
+class ILLocation
+{
+public:
+ ILLocation() : m_offset(BAD_IL_OFFSET), m_isStackEmpty(false), m_isCall(false)
+ {
+ }
+
+ ILLocation(IL_OFFSET offset, bool isStackEmpty, bool isCall)
+ : m_offset(offset), m_isStackEmpty(isStackEmpty), m_isCall(isCall)
+ {
+ }
+
+ IL_OFFSET GetOffset() const
+ {
+ return m_offset;
+ }
+
+ // Is this source location at a stack empty point? We need to be able to
+ // report this information back to the debugger since we only allow EnC
+ // transitions at stack empty points.
+ bool IsStackEmpty() const
+ {
+ return m_isStackEmpty;
+ }
+
+ // Is this a call instruction? Used for managed return values.
+ bool IsCall() const
+ {
+ return m_isCall;
+ }
+
+ bool IsValid() const
+ {
+ return m_offset != BAD_IL_OFFSET;
+ }
+
+ inline bool operator==(const ILLocation& other) const
+ {
+ return (m_offset == other.m_offset) && (m_isStackEmpty == other.m_isStackEmpty) && (m_isCall == other.m_isCall);
+ }
+
+ inline bool operator!=(const ILLocation& other) const
+ {
+ return !(*this == other);
+ }
+
+#ifdef DEBUG
+ // Dump textual representation of this ILLocation to jitstdout.
+ void Dump() const;
+#endif
+
+private:
+ IL_OFFSET m_offset;
+ bool m_isStackEmpty : 1;
+ bool m_isCall : 1;
+};
+
+// Represents debug information about a statement.
+class DebugInfo
+{
+public:
+ DebugInfo() : m_inlineContext(nullptr)
+ {
+ }
+
+ DebugInfo(InlineContext* inlineContext, ILLocation loc) : m_inlineContext(inlineContext), m_location(loc)
+ {
+ }
+
+ InlineContext* GetInlineContext() const
+ {
+ return m_inlineContext;
+ }
+
+ ILLocation GetLocation() const
+ {
+ return m_location;
+ }
+
+ // Retrieve information about the location that inlined this statement.
+ // Note that there can be associated parent information even when IsValid
+ // below returns false.
+ bool GetParent(DebugInfo* parent) const;
+
+ // Get debug info in the root. If this debug info is in the root, then
+ // returns *this. Otherwise returns information of the call in the root
+ // that eventually produced this statement through inlines.
+ DebugInfo GetRoot() const;
+
+#ifdef DEBUG
+ void Validate() const;
+#else
+ void Validate() const
+ {
+ }
+#endif
+
+#ifdef DEBUG
+ // Dump textual representation of this DebugInfo to jitstdout.
+ void Dump(bool recurse) const;
+#endif
+
+ // Check if this debug info has both a valid inline context and valid
+ // location.
+ bool IsValid() const
+ {
+ return m_inlineContext != nullptr && m_location.IsValid();
+ }
+
+ inline bool operator==(const DebugInfo& other) const
+ {
+ return (m_inlineContext == other.m_inlineContext) && (m_location == other.m_location);
+ }
+
+ inline bool operator!=(const DebugInfo& other) const
+ {
+ return !(*this == other);
+ }
+
+private:
+ InlineContext* m_inlineContext;
+ ILLocation m_location;
+};
+
+#endif
diff --git a/src/coreclr/jit/decomposelongs.cpp b/src/coreclr/jit/decomposelongs.cpp
index 50a1c054062852..9b75e4c73727b9 100644
--- a/src/coreclr/jit/decomposelongs.cpp
+++ b/src/coreclr/jit/decomposelongs.cpp
@@ -124,7 +124,7 @@ GenTree* DecomposeLongs::DecomposeNode(GenTree* tree)
// Handle the case where we are implicitly using the lower half of a long lclVar.
if ((tree->TypeGet() == TYP_INT) && tree->OperIsLocal())
{
- LclVarDsc* varDsc = m_compiler->lvaTable + tree->AsLclVarCommon()->GetLclNum();
+ LclVarDsc* varDsc = m_compiler->lvaGetDesc(tree->AsLclVarCommon());
if (varTypeIsLong(varDsc) && varDsc->lvPromoted)
{
#ifdef DEBUG
@@ -346,7 +346,7 @@ GenTree* DecomposeLongs::DecomposeLclVar(LIR::Use& use)
GenTree* tree = use.Def();
unsigned varNum = tree->AsLclVarCommon()->GetLclNum();
- LclVarDsc* varDsc = m_compiler->lvaTable + varNum;
+ LclVarDsc* varDsc = m_compiler->lvaGetDesc(varNum);
GenTree* loResult = tree;
loResult->gtType = TYP_INT;
@@ -426,8 +426,7 @@ GenTree* DecomposeLongs::DecomposeStoreLclVar(LIR::Use& use)
noway_assert(rhs->OperGet() == GT_LONG);
- unsigned varNum = tree->AsLclVarCommon()->GetLclNum();
- LclVarDsc* varDsc = m_compiler->lvaTable + varNum;
+ const LclVarDsc* varDsc = m_compiler->lvaGetDesc(tree->AsLclVarCommon());
if (!varDsc->lvPromoted)
{
// We cannot decompose a st.lclVar that is not promoted because doing so
@@ -1650,7 +1649,7 @@ GenTree* DecomposeLongs::DecomposeHWIntrinsic(LIR::Use& use)
GenTreeHWIntrinsic* hwintrinsicTree = tree->AsHWIntrinsic();
- switch (hwintrinsicTree->gtHWIntrinsicId)
+ switch (hwintrinsicTree->GetHWIntrinsicId())
{
case NI_Vector128_GetElement:
case NI_Vector256_GetElement:
@@ -1693,10 +1692,11 @@ GenTree* DecomposeLongs::DecomposeHWIntrinsicGetElement(LIR::Use& use, GenTreeHW
{
assert(node == use.Def());
assert(varTypeIsLong(node));
- assert((node->gtHWIntrinsicId == NI_Vector128_GetElement) || (node->gtHWIntrinsicId == NI_Vector256_GetElement));
+ assert((node->GetHWIntrinsicId() == NI_Vector128_GetElement) ||
+ (node->GetHWIntrinsicId() == NI_Vector256_GetElement));
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = node->gtGetOp2();
+ GenTree* op1 = node->Op(1);
+ GenTree* op2 = node->Op(2);
var_types simdBaseType = node->GetSimdBaseType();
unsigned simdSize = node->GetSimdSize();
@@ -1712,24 +1712,24 @@ GenTree* DecomposeLongs::DecomposeHWIntrinsicGetElement(LIR::Use& use, GenTreeHW
index = op2->AsIntCon()->IconValue();
}
- GenTree* simdTmpVar = RepresentOpAsLocalVar(op1, node, &node->gtOp1);
+ GenTree* simdTmpVar = RepresentOpAsLocalVar(op1, node, &node->Op(1));
unsigned simdTmpVarNum = simdTmpVar->AsLclVarCommon()->GetLclNum();
JITDUMP("[DecomposeHWIntrinsicGetElement]: Saving op1 tree to a temp var:\n");
DISPTREERANGE(Range(), simdTmpVar);
Range().Remove(simdTmpVar);
- op1 = node->gtGetOp1();
+ op1 = node->Op(1);
GenTree* indexTmpVar = nullptr;
unsigned indexTmpVarNum = 0;
if (!indexIsConst)
{
- indexTmpVar = RepresentOpAsLocalVar(op2, node, &node->gtOp2);
+ indexTmpVar = RepresentOpAsLocalVar(op2, node, &node->Op(2));
indexTmpVarNum = indexTmpVar->AsLclVarCommon()->GetLclNum();
JITDUMP("[DecomposeHWIntrinsicGetElement]: Saving op2 tree to a temp var:\n");
DISPTREERANGE(Range(), indexTmpVar);
Range().Remove(indexTmpVar);
- op2 = node->gtGetOp2();
+ op2 = node->Op(2);
}
// Create:
@@ -1756,7 +1756,7 @@ GenTree* DecomposeLongs::DecomposeHWIntrinsicGetElement(LIR::Use& use, GenTreeHW
}
GenTree* loResult = m_compiler->gtNewSimdHWIntrinsicNode(TYP_INT, simdTmpVar1, indexTimesTwo1,
- node->gtHWIntrinsicId, CORINFO_TYPE_INT, simdSize);
+ node->GetHWIntrinsicId(), CORINFO_TYPE_INT, simdSize);
Range().InsertBefore(node, loResult);
// Create:
@@ -1782,7 +1782,7 @@ GenTree* DecomposeLongs::DecomposeHWIntrinsicGetElement(LIR::Use& use, GenTreeHW
}
GenTree* hiResult = m_compiler->gtNewSimdHWIntrinsicNode(TYP_INT, simdTmpVar2, indexTimesTwoPlusOne,
- node->gtHWIntrinsicId, CORINFO_TYPE_INT, simdSize);
+ node->GetHWIntrinsicId(), CORINFO_TYPE_INT, simdSize);
Range().InsertBefore(node, hiResult);
// Done with the original tree; remove it.
diff --git a/src/coreclr/jit/disasm.cpp b/src/coreclr/jit/disasm.cpp
index 9a91b457107d7e..d92013ed80dd27 100644
--- a/src/coreclr/jit/disasm.cpp
+++ b/src/coreclr/jit/disasm.cpp
@@ -1102,7 +1102,7 @@ size_t DisAssembler::CbDisassemble(DIS* pdis,
}
wchar_t wz[MAX_CLASSNAME_LENGTH];
- pdis->CchFormatInstr(wz, _countof(wz));
+ pdis->CchFormatInstr(wz, ArrLen(wz));
if (printit)
{
@@ -1133,7 +1133,7 @@ size_t DisAssembler::CbDisassemble(DIS* pdis,
wchar_t wzBytes[MAX_CLASSNAME_LENGTH];
assert(cchBytesMax < MAX_CLASSNAME_LENGTH);
- size_t cchBytes = pdis->CchFormatBytes(wzBytes, _countof(wzBytes));
+ size_t cchBytes = pdis->CchFormatBytes(wzBytes, ArrLen(wzBytes));
if (cchBytes > CCH_INDENT)
{
@@ -1168,7 +1168,7 @@ size_t CbDisassembleWithBytes(DIS* pdis, DIS::ADDR addr, const BYTE* pb, size_t
wchar_t wz[MAX_CLASSNAME_LENGTH];
- pdis->CchFormatAddr(addr, wz, _countof(wz));
+ pdis->CchFormatAddr(addr, wz, ArrLen(wz));
size_t cchIndent = (size_t)fprintf(pfile, " %ls: ", wz);
@@ -1190,7 +1190,7 @@ size_t CbDisassembleWithBytes(DIS* pdis, DIS::ADDR addr, const BYTE* pb, size_t
}
wchar_t wzBytes[64];
- size_t cchBytes = pdis->CchFormatBytes(wzBytes, _countof(wzBytes));
+ size_t cchBytes = pdis->CchFormatBytes(wzBytes, ArrLen(wzBytes));
wchar_t* pwzBytes;
wchar_t* pwzNext;
@@ -1228,7 +1228,7 @@ size_t CbDisassembleWithBytes(DIS* pdis, DIS::ADDR addr, const BYTE* pb, size_t
if (fFirst)
{
- pdis->CchFormatInstr(wz, _countof(wz));
+ pdis->CchFormatInstr(wz, ArrLen(wz));
fprintf(pfile, "%-*ls %ls\n", cchBytesMax, pwzBytes, wz);
}
diff --git a/src/coreclr/jit/earlyprop.cpp b/src/coreclr/jit/earlyprop.cpp
index 74f3c36c1bb185..e4b29df2166dd9 100644
--- a/src/coreclr/jit/earlyprop.cpp
+++ b/src/coreclr/jit/earlyprop.cpp
@@ -700,7 +700,7 @@ bool Compiler::optIsNullCheckFoldingLegal(GenTree* tree,
assert(fgStmtListThreaded);
while (canRemoveNullCheck && (currentTree != tree) && (currentTree != nullptr))
{
- if ((*nullCheckParent == nullptr) && (nullCheckTree->gtGetChildPointer(currentTree) != nullptr))
+ if ((*nullCheckParent == nullptr) && currentTree->TryGetUse(nullCheckTree))
{
*nullCheckParent = currentTree;
}
diff --git a/src/coreclr/jit/ee_il_dll.cpp b/src/coreclr/jit/ee_il_dll.cpp
index 07ad7dfc90b9cd..ebf1ea2945195f 100644
--- a/src/coreclr/jit/ee_il_dll.cpp
+++ b/src/coreclr/jit/ee_il_dll.cpp
@@ -608,7 +608,17 @@ void Compiler::eeGetStmtOffsets()
uint32_t* offsets;
ICorDebugInfo::BoundaryTypes offsetsImplicit;
- info.compCompHnd->getBoundaries(info.compMethodHnd, &offsetsCount, &offsets, &offsetsImplicit);
+ if (compIsForInlining())
+ {
+ // We do not get explicit boundaries for inlinees, only implicit ones.
+ offsetsImplicit = impInlineRoot()->info.compStmtOffsetsImplicit;
+ offsetsCount = 0;
+ offsets = nullptr;
+ }
+ else
+ {
+ info.compCompHnd->getBoundaries(info.compMethodHnd, &offsetsCount, &offsets, &offsetsImplicit);
+ }
/* Set the implicit boundaries */
@@ -960,7 +970,8 @@ void Compiler::eeSetLIcount(unsigned count)
eeBoundariesCount = count;
if (eeBoundariesCount)
{
- eeBoundaries = (boundariesDsc*)info.compCompHnd->allocateArray(eeBoundariesCount * sizeof(eeBoundaries[0]));
+ eeBoundaries =
+ (ICorDebugInfo::OffsetMapping*)info.compCompHnd->allocateArray(eeBoundariesCount * sizeof(eeBoundaries[0]));
}
else
{
@@ -968,19 +979,39 @@ void Compiler::eeSetLIcount(unsigned count)
}
}
-void Compiler::eeSetLIinfo(
- unsigned which, UNATIVE_OFFSET nativeOffset, IL_OFFSET ilOffset, bool stkEmpty, bool callInstruction)
+void Compiler::eeSetLIinfo(unsigned which, UNATIVE_OFFSET nativeOffset, IPmappingDscKind kind, const ILLocation& loc)
{
assert(opts.compDbgInfo);
- assert(eeBoundariesCount > 0);
+ assert(eeBoundariesCount > 0 && eeBoundaries != nullptr);
assert(which < eeBoundariesCount);
- if (eeBoundaries != nullptr)
+ eeBoundaries[which].nativeOffset = nativeOffset;
+ eeBoundaries[which].source = (ICorDebugInfo::SourceTypes)0;
+
+ switch (kind)
{
- eeBoundaries[which].nativeIP = nativeOffset;
- eeBoundaries[which].ilOffset = ilOffset;
- eeBoundaries[which].sourceReason = stkEmpty ? ICorDebugInfo::STACK_EMPTY : 0;
- eeBoundaries[which].sourceReason |= callInstruction ? ICorDebugInfo::CALL_INSTRUCTION : 0;
+ int source;
+
+ case IPmappingDscKind::Normal:
+ eeBoundaries[which].ilOffset = loc.GetOffset();
+ source = loc.IsStackEmpty() ? ICorDebugInfo::STACK_EMPTY : 0;
+ source |= loc.IsCall() ? ICorDebugInfo::CALL_INSTRUCTION : 0;
+ eeBoundaries[which].source = (ICorDebugInfo::SourceTypes)source;
+ break;
+ case IPmappingDscKind::Prolog:
+ eeBoundaries[which].ilOffset = ICorDebugInfo::PROLOG;
+ eeBoundaries[which].source = ICorDebugInfo::STACK_EMPTY;
+ break;
+ case IPmappingDscKind::Epilog:
+ eeBoundaries[which].ilOffset = ICorDebugInfo::EPILOG;
+ eeBoundaries[which].source = ICorDebugInfo::STACK_EMPTY;
+ break;
+ case IPmappingDscKind::NoMapping:
+ eeBoundaries[which].ilOffset = ICorDebugInfo::NO_MAPPING;
+ eeBoundaries[which].source = ICorDebugInfo::STACK_EMPTY;
+ break;
+ default:
+ unreached();
}
}
@@ -1005,8 +1036,13 @@ void Compiler::eeSetLIdone()
#if defined(DEBUG)
-/* static */
void Compiler::eeDispILOffs(IL_OFFSET offs)
+{
+ printf("0x%04X", offs);
+}
+
+/* static */
+void Compiler::eeDispSourceMappingOffs(uint32_t offs)
{
const char* specialOffs[] = {"EPILOG", "PROLOG", "NO_MAP"};
@@ -1022,33 +1058,34 @@ void Compiler::eeDispILOffs(IL_OFFSET offs)
printf("%s", specialOffs[specialOffsNum]);
break;
default:
- printf("0x%04X", offs);
+ eeDispILOffs(offs);
+ break;
}
}
/* static */
-void Compiler::eeDispLineInfo(const boundariesDsc* line)
+void Compiler::eeDispLineInfo(const ICorDebugInfo::OffsetMapping* line)
{
printf("IL offs ");
- eeDispILOffs(line->ilOffset);
+ eeDispSourceMappingOffs(line->ilOffset);
- printf(" : 0x%08X", line->nativeIP);
- if (line->sourceReason != 0)
+ printf(" : 0x%08X", line->nativeOffset);
+ if (line->source != 0)
{
// It seems like it should probably never be zero since ICorDebugInfo::SOURCE_TYPE_INVALID is zero.
// However, the JIT has always generated this and printed "stack non-empty".
printf(" ( ");
- if ((line->sourceReason & ICorDebugInfo::STACK_EMPTY) != 0)
+ if ((line->source & ICorDebugInfo::STACK_EMPTY) != 0)
{
printf("STACK_EMPTY ");
}
- if ((line->sourceReason & ICorDebugInfo::CALL_INSTRUCTION) != 0)
+ if ((line->source & ICorDebugInfo::CALL_INSTRUCTION) != 0)
{
printf("CALL_INSTRUCTION ");
}
- if ((line->sourceReason & ICorDebugInfo::CALL_SITE) != 0)
+ if ((line->source & ICorDebugInfo::CALL_SITE) != 0)
{
printf("CALL_SITE ");
}
@@ -1057,7 +1094,7 @@ void Compiler::eeDispLineInfo(const boundariesDsc* line)
printf("\n");
// We don't expect to see any other bits.
- assert((line->sourceReason & ~(ICorDebugInfo::STACK_EMPTY | ICorDebugInfo::CALL_INSTRUCTION)) == 0);
+ assert((line->source & ~(ICorDebugInfo::STACK_EMPTY | ICorDebugInfo::CALL_INSTRUCTION)) == 0);
}
void Compiler::eeDispLineInfos()
diff --git a/src/coreclr/jit/ee_il_dll.hpp b/src/coreclr/jit/ee_il_dll.hpp
index 5f98a701468521..abedfa4ddf0778 100644
--- a/src/coreclr/jit/ee_il_dll.hpp
+++ b/src/coreclr/jit/ee_il_dll.hpp
@@ -68,9 +68,9 @@ bool Compiler::eeIsFieldStatic(CORINFO_FIELD_HANDLE fldHnd)
}
FORCEINLINE
-var_types Compiler::eeGetFieldType(CORINFO_FIELD_HANDLE fldHnd)
+var_types Compiler::eeGetFieldType(CORINFO_FIELD_HANDLE fldHnd, CORINFO_CLASS_HANDLE* pStructHnd)
{
- return JITtype2varType(info.compCompHnd->getFieldType(fldHnd));
+ return JITtype2varType(info.compCompHnd->getFieldType(fldHnd, pStructHnd));
}
FORCEINLINE
diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp
index 9975cd5041945c..ba4ab8f7b6caab 100644
--- a/src/coreclr/jit/emit.cpp
+++ b/src/coreclr/jit/emit.cpp
@@ -115,7 +115,7 @@ const char* emitter::emitIfName(unsigned f)
static char errBuff[32];
- if (f < _countof(ifNames))
+ if (f < ArrLen(ifNames))
{
return ifNames[f];
}
@@ -872,7 +872,7 @@ insGroup* emitter::emitSavIG(bool emitAdd)
// Move align instructions to the global list, update their 'next' links
do
{
- // Grab the jump and remove it from the list
+ // Grab the align and remove it from the list
instrDescAlign* oa = emitCurIGAlignList;
emitCurIGAlignList = oa->idaNext;
@@ -913,6 +913,14 @@ insGroup* emitter::emitSavIG(bool emitAdd)
}
emitAlignLast = last;
+
+ // Point to the first instruction of most recent
+ // align instruction(s) added.
+ //
+ // Since emitCurIGAlignList is created in inverse of
+ // program order, the `list` reverses that in forms it
+ // in correct order.
+ emitAlignLastGroup = list;
}
#endif
@@ -1071,8 +1079,8 @@ void emitter::emitBegFN(bool hasFramePtr
#if FEATURE_LOOP_ALIGN
/* We don't have any align instructions */
- emitAlignList = emitAlignLast = nullptr;
- emitCurIGAlignList = nullptr;
+ emitAlignList = emitAlignLastGroup = emitAlignLast = nullptr;
+ emitCurIGAlignList = nullptr;
#endif
/* We have not recorded any live sets */
@@ -1401,7 +1409,7 @@ void* emitter::emitAllocAnyInstr(size_t sz, emitAttr opsz)
// the prolog/epilog placeholder groups ARE generated in order, and are
// re-used. But generating additional groups would not work.
if (emitComp->compStressCompile(Compiler::STRESS_EMITTER, 1) && emitCurIGinsCnt && !emitIGisInProlog(emitCurIG) &&
- !emitIGisInEpilog(emitCurIG) && !emitCurIG->isLoopAlign()
+ !emitIGisInEpilog(emitCurIG) && !emitCurIG->endsWithAlignInstr()
#if defined(FEATURE_EH_FUNCLETS)
&& !emitIGisInFuncletProlog(emitCurIG) && !emitIGisInFuncletEpilog(emitCurIG)
#endif // FEATURE_EH_FUNCLETS
@@ -1574,7 +1582,7 @@ void emitter::emitCheckIGoffsets()
{
if (tempIG->igOffs != currentOffset)
{
- printf("Block #%u has offset %08X, expected %08X\n", tempIG->igNum, tempIG->igOffs, currentOffset);
+ printf("IG%02u has offset %08X, expected %08X\n", tempIG->igNum, tempIG->igOffs, currentOffset);
assert(!"bad block offset");
}
@@ -1815,11 +1823,11 @@ void emitter::emitCreatePlaceholderIG(insGroupPlaceholderType igType,
{
if (igType == IGPT_FUNCLET_PROLOG)
{
- codeGen->genIPmappingAdd((IL_OFFSETX)ICorDebugInfo::PROLOG, true);
+ codeGen->genIPmappingAdd(IPmappingDscKind::Prolog, DebugInfo(), true);
}
else if (igType == IGPT_FUNCLET_EPILOG)
{
- codeGen->genIPmappingAdd((IL_OFFSETX)ICorDebugInfo::EPILOG, true);
+ codeGen->genIPmappingAdd(IPmappingDscKind::Epilog, DebugInfo(), true);
}
}
#endif // FEATURE_EH_FUNCLETS
@@ -3348,7 +3356,7 @@ const BYTE emitter::emitFmtToOps[] = {
};
#ifdef DEBUG
-const unsigned emitter::emitFmtCount = _countof(emitFmtToOps);
+const unsigned emitter::emitFmtCount = ArrLen(emitFmtToOps);
#endif
//------------------------------------------------------------------------
@@ -3373,6 +3381,15 @@ const size_t hexEncodingSize = 11;
#ifdef DEBUG
//------------------------------------------------------------------------
+// emitDispInsIndent: Print indentation corresponding to an instruction's
+// indentation.
+//
+void emitter::emitDispInsIndent()
+{
+ size_t indent = emitComp->opts.disDiffable ? basicIndent : basicIndent + hexEncodingSize;
+ printf("%.*s", indent, " ");
+}
+//------------------------------------------------------------------------
// emitDispGCDeltaTitle: Print an appropriately indented title for a GC info delta
//
// Arguments:
@@ -3380,8 +3397,8 @@ const size_t hexEncodingSize = 11;
//
void emitter::emitDispGCDeltaTitle(const char* title)
{
- size_t indent = emitComp->opts.disDiffable ? basicIndent : basicIndent + hexEncodingSize;
- printf("%.*s; %s", indent, " ", title);
+ emitDispInsIndent();
+ printf("; %s", title);
}
//------------------------------------------------------------------------
@@ -3555,7 +3572,7 @@ void emitter::emitDispIGflags(unsigned flags)
{
printf(", extend");
}
- if (flags & IGF_LOOP_ALIGN)
+ if (flags & IGF_HAS_ALIGN)
{
printf(", align");
}
@@ -4804,9 +4821,9 @@ void emitter::emitJumpDistBind()
// Arguments:
// nAlignInstr - Number of align instructions about to be added.
//
-void emitter::emitCheckAlignFitInCurIG(unsigned short nAlignInstr)
+void emitter::emitCheckAlignFitInCurIG(unsigned nAlignInstr)
{
- unsigned short instrDescSize = nAlignInstr * sizeof(instrDescAlign);
+ unsigned instrDescSize = nAlignInstr * sizeof(instrDescAlign);
// Ensure that all align instructions fall in same IG.
if (emitCurIGfreeNext + instrDescSize >= emitCurIGfreeEndp)
@@ -4817,11 +4834,15 @@ void emitter::emitCheckAlignFitInCurIG(unsigned short nAlignInstr)
//-----------------------------------------------------------------------------
//
-// The next instruction will be a loop head entry point
-// So insert an alignment instruction here to ensure that
-// we can properly align the code.
+// emitLoopAlign: The next instruction will be a loop head entry point
+// So insert an alignment instruction of "paddingBytes" to ensure that
+// the code is properly aligned.
+// Arguments:
+// paddingBytes - Number of padding bytes to insert.
+// isFirstAlign - For multiple 'align' instructions case, if this is the first
+// 'align' instruction of that group.
//
-void emitter::emitLoopAlign(unsigned short paddingBytes)
+void emitter::emitLoopAlign(unsigned paddingBytes, bool isFirstAlign DEBUG_ARG(bool isPlacedBehindJmp))
{
// Determine if 'align' instruction about to be generated will
// fall in current IG or next.
@@ -4831,7 +4852,7 @@ void emitter::emitLoopAlign(unsigned short paddingBytes)
{
// If align fits in current IG, then mark that it contains alignment
// instruction in the end.
- emitCurIG->igFlags |= IGF_LOOP_ALIGN;
+ emitCurIG->igFlags |= IGF_HAS_ALIGN;
}
/* Insert a pseudo-instruction to ensure that we align
@@ -4842,12 +4863,12 @@ void emitter::emitLoopAlign(unsigned short paddingBytes)
{
// Mark this IG has alignment in the end, so during emitter we can check the instruction count
// heuristics of all IGs that follows this IG that participate in a loop.
- emitCurIG->igFlags |= IGF_LOOP_ALIGN;
+ emitCurIG->igFlags |= IGF_HAS_ALIGN;
}
else
{
// Otherwise, make sure it was already marked such.
- assert(emitCurIG->isLoopAlign());
+ assert(emitCurIG->endsWithAlignInstr());
}
#if defined(TARGET_XARCH)
@@ -4859,6 +4880,22 @@ void emitter::emitLoopAlign(unsigned short paddingBytes)
id->idaIG = emitCurIG;
+ if (isFirstAlign)
+ {
+ // For multiple align instructions, set the idaLoopHeadPredIG only for the
+ // first align instruction
+ id->idaLoopHeadPredIG = emitCurIG;
+ emitAlignLastGroup = id;
+ }
+ else
+ {
+ id->idaLoopHeadPredIG = nullptr;
+ }
+
+#ifdef DEBUG
+ id->isPlacedAfterJmp = isPlacedBehindJmp;
+#endif
+
/* Append this instruction to this IG's alignment list */
id->idaNext = emitCurIGAlignList;
@@ -4870,25 +4907,28 @@ void emitter::emitLoopAlign(unsigned short paddingBytes)
//-----------------------------------------------------------------------------
//
-// The next instruction will be a loop head entry point
+// emitLongLoopAlign: The next instruction will be a loop head entry point
// So insert alignment instruction(s) here to ensure that
// we can properly align the code.
//
// This emits more than one `INS_align` instruction depending on the
// alignmentBoundary parameter.
//
-void emitter::emitLongLoopAlign(unsigned short alignmentBoundary)
+// Arguments:
+// alignmentBoundary - The boundary at which loop needs to be aligned.
+//
+void emitter::emitLongLoopAlign(unsigned alignmentBoundary DEBUG_ARG(bool isPlacedBehindJmp))
{
#if defined(TARGET_XARCH)
- unsigned short nPaddingBytes = alignmentBoundary - 1;
- unsigned short nAlignInstr = (nPaddingBytes + (MAX_ENCODED_SIZE - 1)) / MAX_ENCODED_SIZE;
- unsigned short insAlignCount = nPaddingBytes / MAX_ENCODED_SIZE;
- unsigned short lastInsAlignSize = nPaddingBytes % MAX_ENCODED_SIZE;
- unsigned short paddingBytes = MAX_ENCODED_SIZE;
+ unsigned nPaddingBytes = alignmentBoundary - 1;
+ unsigned nAlignInstr = (nPaddingBytes + (MAX_ENCODED_SIZE - 1)) / MAX_ENCODED_SIZE;
+ unsigned insAlignCount = nPaddingBytes / MAX_ENCODED_SIZE;
+ unsigned lastInsAlignSize = nPaddingBytes % MAX_ENCODED_SIZE;
+ unsigned paddingBytes = MAX_ENCODED_SIZE;
#elif defined(TARGET_ARM64)
- unsigned short nAlignInstr = alignmentBoundary / INSTR_ENCODED_SIZE;
- unsigned short insAlignCount = nAlignInstr;
- unsigned short paddingBytes = INSTR_ENCODED_SIZE;
+ unsigned nAlignInstr = alignmentBoundary / INSTR_ENCODED_SIZE;
+ unsigned insAlignCount = nAlignInstr;
+ unsigned paddingBytes = INSTR_ENCODED_SIZE;
#endif
emitCheckAlignFitInCurIG(nAlignInstr);
@@ -4896,25 +4936,49 @@ void emitter::emitLongLoopAlign(unsigned short alignmentBoundary)
/* Insert a pseudo-instruction to ensure that we align
the next instruction properly */
+ bool isFirstAlign = true;
while (insAlignCount)
{
- emitLoopAlign(paddingBytes);
+ emitLoopAlign(paddingBytes, isFirstAlign DEBUG_ARG(isPlacedBehindJmp));
insAlignCount--;
+ isFirstAlign = false;
}
#if defined(TARGET_XARCH)
- emitLoopAlign(lastInsAlignSize);
+ emitLoopAlign(lastInsAlignSize, isFirstAlign DEBUG_ARG(isPlacedBehindJmp));
#endif
}
+//-----------------------------------------------------------------------------
+// emitConnectAlignInstrWithCurIG: If "align" instruction is not just before the loop start,
+// setting idaLoopHeadPredIG lets us know the exact IG that the "align"
+// instruction is trying to align. This is used to track the last IG that
+// needs alignment after which VEX encoding optimization is enabled.
+//
+// TODO: Once over-estimation problem is solved, consider replacing
+// idaLoopHeadPredIG with idaLoopHeadIG itself.
+//
+void emitter::emitConnectAlignInstrWithCurIG()
+{
+ JITDUMP("Mapping 'align' instruction in IG%02u to target IG%02u\n", emitAlignLastGroup->idaIG->igNum,
+ emitCurIG->igNum);
+ // Since we never align overlapping instructions, it is always guaranteed that
+ // the emitAlignLastGroup points to the loop that is in process of getting aligned.
+
+ emitAlignLastGroup->idaLoopHeadPredIG = emitCurIG;
+
+ // For a new IG to ensure that loop doesn't start from IG that idaLoopHeadPredIG points to.
+ emitNxtIG();
+}
+
//-----------------------------------------------------------------------------
// emitLoopAlignment: Insert an align instruction at the end of emitCurIG and
-// mark it as IGF_LOOP_ALIGN to indicate that next IG is a
-// loop needing alignment.
+// mark it as IGF_HAS_ALIGN to indicate that a next or a future
+// IG is a loop that needs alignment.
//
-void emitter::emitLoopAlignment()
+void emitter::emitLoopAlignment(DEBUG_ARG1(bool isPlacedBehindJmp))
{
- unsigned short paddingBytes;
+ unsigned paddingBytes;
#if defined(TARGET_XARCH)
// For xarch, each align instruction can be maximum of MAX_ENCODED_SIZE bytes and if
@@ -4922,13 +4986,13 @@ void emitter::emitLoopAlignment()
if ((emitComp->opts.compJitAlignLoopBoundary > 16) && (!emitComp->opts.compJitAlignLoopAdaptive))
{
paddingBytes = emitComp->opts.compJitAlignLoopBoundary;
- emitLongLoopAlign(paddingBytes);
+ emitLongLoopAlign(paddingBytes DEBUG_ARG(isPlacedBehindJmp));
}
else
{
emitCheckAlignFitInCurIG(1);
paddingBytes = MAX_ENCODED_SIZE;
- emitLoopAlign(paddingBytes);
+ emitLoopAlign(paddingBytes, true DEBUG_ARG(isPlacedBehindJmp));
}
#elif defined(TARGET_ARM64)
// For Arm64, each align instruction is 4-bytes long because of fixed-length encoding.
@@ -4941,14 +5005,12 @@ void emitter::emitLoopAlignment()
{
paddingBytes = emitComp->opts.compJitAlignLoopBoundary;
}
- emitLongLoopAlign(paddingBytes);
+ emitLongLoopAlign(paddingBytes DEBUG_ARG(isPlacedBehindJmp));
#endif
- JITDUMP("Adding 'align' instruction of %d bytes in %s.\n", paddingBytes, emitLabelString(emitCurIG));
+ assert(emitLastIns->idIns() == INS_align);
-#ifdef DEBUG
- emitComp->loopAlignCandidates++;
-#endif // DEBUG
+ JITDUMP("Adding 'align' instruction of %d bytes in %s.\n", paddingBytes, emitLabelString(emitCurIG));
}
//-----------------------------------------------------------------------------
@@ -4958,7 +5020,7 @@ void emitter::emitLoopAlignment()
//
bool emitter::emitEndsWithAlignInstr()
{
- return emitCurIG->isLoopAlign();
+ return emitCurIG->endsWithAlignInstr();
}
//-----------------------------------------------------------------------------
@@ -4982,9 +5044,13 @@ unsigned emitter::getLoopSize(insGroup* igLoopHeader, unsigned maxLoopSize DEBUG
for (insGroup* igInLoop = igLoopHeader; igInLoop != nullptr; igInLoop = igInLoop->igNext)
{
loopSize += igInLoop->igSize;
- if (igInLoop->isLoopAlign())
+ if (igInLoop->endsWithAlignInstr())
{
- // If igInLoop is marked as "IGF_LOOP_ALIGN", the basic block flow detected a loop start.
+ // If IGF_HAS_ALIGN is present, igInLoop contains align instruction at the end,
+ // for next IG or some future IG.
+ //
+ // For both cases, remove the padding bytes from igInLoop's size so it is not included in loopSize.
+ //
// If the loop was formed because of forward jumps like the loop IG18 below, the backedge is not
// set for them and such loops are not aligned. For such cases, the loop size threshold will never
// be met and we would break as soon as loopSize > maxLoopSize.
@@ -4997,9 +5063,9 @@ unsigned emitter::getLoopSize(insGroup* igLoopHeader, unsigned maxLoopSize DEBUG
// ...
// jne IG05
//
- // If igInLoop is a legitimate loop, and igInLoop's next IG is also a loop that needs alignment,
- // then igInLoop should be the last IG of the current loop and should have backedge to current
- // loop header.
+ // If igInLoop is a legitimate loop, and igInLoop's end with another 'align' instruction for different IG
+ // representing a loop that needs alignment, then igInLoop should be the last IG of the current loop and
+ // should have backedge to current loop header.
//
// Below, IG05 is the last IG of loop IG04-IG05 and its backedge points to IG04.
//
@@ -5149,25 +5215,33 @@ void emitter::emitSetLoopBackEdge(BasicBlock* loopTopBlock)
bool markedCurrLoop = alignCurrentLoop;
while ((alignInstr != nullptr))
{
- // Find the IG before current loop and clear the IGF_LOOP_ALIGN flag
- if (!alignCurrentLoop && (alignInstr->idaIG->igNext == dstIG))
+ insGroup* loopHeadIG = alignInstr->loopHeadIG();
+
+ // Find the IG that has 'align' instruction to align the current loop
+ // and clear the IGF_HAS_ALIGN flag.
+ if (!alignCurrentLoop && (loopHeadIG == dstIG))
{
assert(!markedCurrLoop);
- alignInstr->idaIG->igFlags &= ~IGF_LOOP_ALIGN;
+
+ // This IG should no longer contain alignment instruction
+ alignInstr->removeAlignFlags();
+
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))
+ // Find the IG that has 'align' instruction to align the last loop
+ // and clear the IGF_HAS_ALIGN flag.
+ if (!alignLastLoop && (loopHeadIG != nullptr) && (loopHeadIG->igNum == emitLastLoopStart))
{
assert(!markedLastLoop);
- assert(alignInstr->idaIG->isLoopAlign());
+ assert(alignInstr->idaIG->endsWithAlignInstr());
+
+ // This IG should no longer contain alignment instruction
+ alignInstr->removeAlignFlags();
- 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",
@@ -5179,21 +5253,7 @@ void emitter::emitSetLoopBackEdge(BasicBlock* loopTopBlock)
break;
}
-#if defined(TARGET_XARCH)
- if (!emitComp->opts.compJitAlignLoopAdaptive)
-#endif
- {
- // If there are multiple align instructions, skip the align instructions after
- // the first align instruction and fast forward to the next IG
- insGroup* alignIG = alignInstr->idaIG;
- while ((alignInstr != nullptr) && (alignInstr->idaNext != nullptr) &&
- (alignInstr->idaNext->idaIG == alignIG))
- {
- alignInstr = alignInstr->idaNext;
- }
- }
-
- alignInstr = alignInstr->idaNext;
+ alignInstr = emitAlignInNextIG(alignInstr);
}
assert(markedLastLoop && markedCurrLoop);
@@ -5232,21 +5292,32 @@ void emitter::emitLoopAlignAdjustments()
unsigned loopIGOffset = 0;
instrDescAlign* alignInstr = emitAlignList;
- for (; alignInstr != nullptr; alignInstr = alignInstr->idaNext)
+ for (; alignInstr != nullptr;)
{
assert(alignInstr->idIns() == INS_align);
- insGroup* alignIG = alignInstr->idaIG;
+ insGroup* loopHeadPredIG = alignInstr->idaLoopHeadPredIG;
+ insGroup* loopHeadIG = alignInstr->loopHeadIG();
+ insGroup* containingIG = alignInstr->idaIG;
+
+ JITDUMP(" Adjusting 'align' instruction in IG%02u that is targeted for IG%02u \n", containingIG->igNum,
+ loopHeadIG->igNum);
+
+ // Since we only adjust the padding up to the next align instruction which is behind the jump, we make sure
+ // that we take into account all the alignBytes we removed until that point. Hence " - alignBytesRemoved"
- loopIGOffset = alignIG->igOffs + alignIG->igSize;
+ loopIGOffset = loopHeadIG->igOffs - alignBytesRemoved;
// igSize also includes INS_align instruction, take it off.
loopIGOffset -= estimatedPaddingNeeded;
// IG can be marked as not needing alignment if during setting igLoopBackEdge, it is detected
// that the igLoopBackEdge encloses an IG that is marked for alignment.
+
unsigned actualPaddingNeeded =
- alignIG->isLoopAlign() ? emitCalculatePaddingForLoopAlignment(alignIG, loopIGOffset DEBUG_ARG(false)) : 0;
+ containingIG->endsWithAlignInstr()
+ ? emitCalculatePaddingForLoopAlignment(loopHeadIG, loopIGOffset DEBUG_ARG(false))
+ : 0;
assert(estimatedPaddingNeeded >= actualPaddingNeeded);
@@ -5254,15 +5325,15 @@ void emitter::emitLoopAlignAdjustments()
if (diff != 0)
{
- alignIG->igSize -= diff;
+ containingIG->igSize -= diff;
alignBytesRemoved += diff;
emitTotalCodeSize -= diff;
// Update the flags
- alignIG->igFlags |= IGF_UPD_ISZ;
+ containingIG->igFlags |= IGF_UPD_ISZ;
if (actualPaddingNeeded == 0)
{
- alignIG->igFlags &= ~IGF_LOOP_ALIGN;
+ alignInstr->removeAlignFlags();
}
#ifdef TARGET_XARCH
@@ -5312,21 +5383,19 @@ void emitter::emitLoopAlignAdjustments()
}
assert(paddingToAdj == 0);
assert(instrAdjusted == 0);
-
- // fast forward the align instruction to next IG
- alignInstr = prevAlignInstr;
}
- JITDUMP("Adjusted alignment of %s from %u to %u.\n", emitLabelString(alignIG), estimatedPaddingNeeded,
+ JITDUMP("Adjusted alignment for %s from %u to %u.\n", emitLabelString(loopHeadIG), estimatedPaddingNeeded,
actualPaddingNeeded);
- JITDUMP("Adjusted size of %s from %u to %u.\n", emitLabelString(alignIG), (alignIG->igSize + diff),
- alignIG->igSize);
+ JITDUMP("Adjusted size of %s from %u to %u.\n", emitLabelString(containingIG),
+ (containingIG->igSize + diff), containingIG->igSize);
}
// Adjust the offset of all IGs starting from next IG until we reach the IG having the next
// align instruction or the end of IG list.
- insGroup* adjOffIG = alignIG->igNext;
- insGroup* adjOffUptoIG = alignInstr->idaNext != nullptr ? alignInstr->idaNext->idaIG : emitIGlast;
+ insGroup* adjOffIG = containingIG->igNext;
+ instrDescAlign* nextAlign = emitAlignInNextIG(alignInstr);
+ insGroup* adjOffUptoIG = nextAlign != nullptr ? nextAlign->idaIG : emitIGlast;
while ((adjOffIG != nullptr) && (adjOffIG->igNum <= adjOffUptoIG->igNum))
{
JITDUMP("Adjusted offset of %s from %04X to %04X\n", emitLabelString(adjOffIG), adjOffIG->igOffs,
@@ -5335,11 +5404,14 @@ void emitter::emitLoopAlignAdjustments()
adjOffIG = adjOffIG->igNext;
}
+ alignInstr = nextAlign;
+
if (actualPaddingNeeded > 0)
{
- // Record the last IG that has align instruction. No overestimation
+ // Record the last loop IG that will be aligned. No overestimation
// adjustment will be done after emitLastAlignedIgNum.
- emitLastAlignedIgNum = alignIG->igNum;
+ JITDUMP("Recording last aligned IG: %s\n", emitLabelString(loopHeadPredIG));
+ emitLastAlignedIgNum = loopHeadPredIG->igNum;
}
}
@@ -5353,7 +5425,7 @@ void emitter::emitLoopAlignAdjustments()
// end of 'ig' so the loop that starts after 'ig' is aligned.
//
// Arguments:
-// ig - The IG having 'align' instruction in the end.
+// loopHeadIG - The IG that has the loop head that need to be aligned.
// offset - The offset at which the IG that follows 'ig' starts.
// isAlignAdjusted - Determine if adjustments are done to the align instructions or not.
// During generating code, it is 'false' (because we haven't adjusted the size yet).
@@ -5381,15 +5453,15 @@ void emitter::emitLoopAlignAdjustments()
// 3b. If the loop already fits in minimum alignmentBoundary blocks, then return 0. // already best aligned
// 3c. return paddingNeeded.
//
-unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offset DEBUG_ARG(bool isAlignAdjusted))
+unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* loopHeadIG,
+ size_t offset DEBUG_ARG(bool isAlignAdjusted))
{
- assert(ig->isLoopAlign());
unsigned alignmentBoundary = emitComp->opts.compJitAlignLoopBoundary;
// No padding if loop is already aligned
if ((offset & (alignmentBoundary - 1)) == 0)
{
- JITDUMP(";; Skip alignment: 'Loop at %s already aligned at %dB boundary.'\n", emitLabelString(ig->igNext),
+ JITDUMP(";; Skip alignment: 'Loop at %s already aligned at %dB boundary.'\n", emitLabelString(loopHeadIG),
alignmentBoundary);
return 0;
}
@@ -5409,12 +5481,12 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs
maxLoopSize = emitComp->opts.compJitAlignLoopMaxCodeSize;
}
- unsigned loopSize = getLoopSize(ig->igNext, maxLoopSize DEBUG_ARG(isAlignAdjusted));
+ unsigned loopSize = getLoopSize(loopHeadIG, maxLoopSize DEBUG_ARG(isAlignAdjusted));
// No padding if loop is big
if (loopSize > maxLoopSize)
{
- JITDUMP(";; Skip alignment: 'Loop at %s is big. LoopSize= %d, MaxLoopSize= %d.'\n", emitLabelString(ig->igNext),
+ JITDUMP(";; Skip alignment: 'Loop at %s is big. LoopSize= %d, MaxLoopSize= %d.'\n", emitLabelString(loopHeadIG),
loopSize, maxLoopSize);
return 0;
}
@@ -5451,7 +5523,7 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs
{
skipPadding = true;
JITDUMP(";; Skip alignment: 'Loop at %s already aligned at %uB boundary.'\n",
- emitLabelString(ig->igNext), alignmentBoundary);
+ emitLabelString(loopHeadIG), alignmentBoundary);
}
// Check if the alignment exceeds new maxPadding limit
else if (nPaddingBytes > nMaxPaddingBytes)
@@ -5459,7 +5531,7 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs
skipPadding = true;
JITDUMP(";; Skip alignment: 'Loop at %s PaddingNeeded= %d, MaxPadding= %d, LoopSize= %d, "
"AlignmentBoundary= %dB.'\n",
- emitLabelString(ig->igNext), nPaddingBytes, nMaxPaddingBytes, loopSize, alignmentBoundary);
+ emitLabelString(loopHeadIG), nPaddingBytes, nMaxPaddingBytes, loopSize, alignmentBoundary);
}
}
@@ -5482,7 +5554,7 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs
{
// Otherwise, the loop just fits in minBlocksNeededForLoop and so can skip alignment.
JITDUMP(";; Skip alignment: 'Loop at %s is aligned to fit in %d blocks of %d chunks.'\n",
- emitLabelString(ig->igNext), minBlocksNeededForLoop, alignmentBoundary);
+ emitLabelString(loopHeadIG), minBlocksNeededForLoop, alignmentBoundary);
}
}
}
@@ -5511,12 +5583,12 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs
{
// Otherwise, the loop just fits in minBlocksNeededForLoop and so can skip alignment.
JITDUMP(";; Skip alignment: 'Loop at %s is aligned to fit in %d blocks of %d chunks.'\n",
- emitLabelString(ig->igNext), minBlocksNeededForLoop, alignmentBoundary);
+ emitLabelString(loopHeadIG), minBlocksNeededForLoop, alignmentBoundary);
}
}
JITDUMP(";; Calculated padding to add %d bytes to align %s at %dB boundary.\n", paddingToAdd,
- emitLabelString(ig->igNext), alignmentBoundary);
+ emitLabelString(loopHeadIG), alignmentBoundary);
// Either no padding is added because it is too expensive or the offset gets aligned
// to the alignment boundary
@@ -5525,6 +5597,25 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs
return paddingToAdd;
}
+// emitAlignInNextIG: On xarch, for adaptive alignment, this will usually return the next instruction in
+// 'emitAlignList'. But for arm64 or non-adaptive alignment on xarch, where multiple
+// align instructions are emitted, this method will skip the 'align' instruction present
+// in the same IG and return the first instruction that is present in next IG.
+// Arguments:
+// alignInstr - Current 'align' instruction for which next IG's first 'align' should be returned.
+//
+emitter::instrDescAlign* emitter::emitAlignInNextIG(instrDescAlign* alignInstr)
+{
+ // If there are multiple align instructions, skip the align instructions after
+ // the first align instruction and fast forward to the next IG
+ insGroup* alignIG = alignInstr->idaIG;
+ while ((alignInstr != nullptr) && (alignInstr->idaNext != nullptr) && (alignInstr->idaNext->idaIG == alignIG))
+ {
+ alignInstr = alignInstr->idaNext;
+ }
+ return alignInstr != nullptr ? alignInstr->idaNext : nullptr;
+}
+
#endif // FEATURE_LOOP_ALIGN
void emitter::emitCheckFuncletBranch(instrDesc* jmp, insGroup* jmpIG)
@@ -5982,7 +6073,7 @@ unsigned emitter::emitEndCodeGen(Compiler* comp,
if (emitComp->lvaKeepAliveAndReportThis())
{
assert(emitComp->lvaIsOriginalThisArg(0));
- LclVarDsc* thisDsc = &emitComp->lvaTable[0];
+ LclVarDsc* thisDsc = emitComp->lvaGetDesc(0U);
/* If "this" (which is passed in as a register argument in REG_ARG_0)
is enregistered, we normally spot the "mov REG_ARG_0 -> thisReg"
@@ -6151,7 +6242,8 @@ unsigned emitter::emitEndCodeGen(Compiler* comp,
#define DEFAULT_CODE_BUFFER_INIT 0xcc
#ifdef DEBUG
- *instrCount = 0;
+ *instrCount = 0;
+ jitstd::list::iterator nextMapping = emitComp->genPreciseIPmappings.begin();
#endif
for (insGroup* ig = emitIGlist; ig != nullptr; ig = ig->igNext)
{
@@ -6320,6 +6412,34 @@ unsigned emitter::emitEndCodeGen(Compiler* comp,
#ifdef DEBUG
size_t curInstrAddr = (size_t)cp;
instrDesc* curInstrDesc = id;
+
+ if ((emitComp->opts.disAsm || emitComp->verbose) && (JitConfig.JitDisasmWithDebugInfo() != 0) &&
+ (id->idCodeSize() > 0))
+ {
+ UNATIVE_OFFSET curCodeOffs = emitCurCodeOffs(cp);
+ while (nextMapping != emitComp->genPreciseIPmappings.end())
+ {
+ UNATIVE_OFFSET mappingOffs = nextMapping->nativeLoc.CodeOffset(this);
+
+ if (mappingOffs > curCodeOffs)
+ {
+ // Still haven't reached instruction that next mapping belongs to.
+ break;
+ }
+
+ // We reached the mapping or went past it.
+ if (mappingOffs == curCodeOffs)
+ {
+ emitDispInsIndent();
+ printf("; ");
+ nextMapping->debugInfo.Dump(true);
+ printf("\n");
+ }
+
+ ++nextMapping;
+ }
+ }
+
#endif
castto(id, BYTE*) += emitIssue1Instr(ig, id, &cp);
@@ -7233,7 +7353,7 @@ void emitter::emitDispDataSec(dataSecDsc* section)
{
const char* labelFormat = "%-7s";
char label[64];
- sprintf_s(label, _countof(label), "RWD%02u", offset);
+ sprintf_s(label, ArrLen(label), "RWD%02u", offset);
printf(labelFormat, label);
offset += data->dsSize;
@@ -8251,8 +8371,8 @@ void emitter::emitGCvarLiveUpd(int offs, int varNum, GCtype gcType, BYTE* addr D
if (varNum >= 0)
{
// This is NOT a spill temp
- LclVarDsc* varDsc = &emitComp->lvaTable[varNum];
- isTracked = emitComp->lvaIsGCTracked(varDsc);
+ const LclVarDsc* varDsc = emitComp->lvaGetDesc(varNum);
+ isTracked = emitComp->lvaIsGCTracked(varDsc);
}
if (!isTracked)
diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h
index 3f6607a9966a52..ade4f7c3ca2c12 100644
--- a/src/coreclr/jit/emit.h
+++ b/src/coreclr/jit/emit.h
@@ -275,8 +275,8 @@ struct insGroup
#define IGF_PLACEHOLDER 0x0100 // this is a placeholder group, to be filled in later
#define IGF_EXTEND 0x0200 // this block is conceptually an extension of the previous block
// and the emitter should continue to track GC info as if there was no new block.
-#define IGF_LOOP_ALIGN 0x0400 // this group contains alignment instruction(s) at the end; the next IG is the
- // head of a loop that needs alignment.
+#define IGF_HAS_ALIGN 0x0400 // this group contains an alignment instruction(s) at the end to align either the next
+ // IG, or, if this IG contains with an unconditional branch, some subsequent IG.
// Mask of IGF_* flags that should be propagated to new blocks when they are created.
// This allows prologs and epilogs to be any number of IGs, but still be
@@ -349,9 +349,9 @@ struct insGroup
return *(unsigned*)ptr;
}
- bool isLoopAlign()
+ bool endsWithAlignInstr() const
{
- return (igFlags & IGF_LOOP_ALIGN) != 0;
+ return (igFlags & IGF_HAS_ALIGN) != 0;
}
}; // end of struct insGroup
@@ -1240,21 +1240,22 @@ class emitter
#define PERFSCORE_THROUGHPUT_1C 1.0f // Single Issue
-#define PERFSCORE_THROUGHPUT_2C 2.0f // slower - 2 cycles
-#define PERFSCORE_THROUGHPUT_3C 3.0f // slower - 3 cycles
-#define PERFSCORE_THROUGHPUT_4C 4.0f // slower - 4 cycles
-#define PERFSCORE_THROUGHPUT_5C 5.0f // slower - 5 cycles
-#define PERFSCORE_THROUGHPUT_6C 6.0f // slower - 6 cycles
-#define PERFSCORE_THROUGHPUT_7C 7.0f // slower - 7 cycles
-#define PERFSCORE_THROUGHPUT_8C 8.0f // slower - 8 cycles
-#define PERFSCORE_THROUGHPUT_9C 9.0f // slower - 9 cycles
-#define PERFSCORE_THROUGHPUT_10C 10.0f // slower - 10 cycles
-#define PERFSCORE_THROUGHPUT_13C 13.0f // slower - 13 cycles
-#define PERFSCORE_THROUGHPUT_19C 19.0f // slower - 19 cycles
-#define PERFSCORE_THROUGHPUT_25C 25.0f // slower - 25 cycles
-#define PERFSCORE_THROUGHPUT_33C 33.0f // slower - 33 cycles
-#define PERFSCORE_THROUGHPUT_52C 52.0f // slower - 52 cycles
-#define PERFSCORE_THROUGHPUT_57C 57.0f // slower - 57 cycles
+#define PERFSCORE_THROUGHPUT_2C 2.0f // slower - 2 cycles
+#define PERFSCORE_THROUGHPUT_3C 3.0f // slower - 3 cycles
+#define PERFSCORE_THROUGHPUT_4C 4.0f // slower - 4 cycles
+#define PERFSCORE_THROUGHPUT_5C 5.0f // slower - 5 cycles
+#define PERFSCORE_THROUGHPUT_6C 6.0f // slower - 6 cycles
+#define PERFSCORE_THROUGHPUT_7C 7.0f // slower - 7 cycles
+#define PERFSCORE_THROUGHPUT_8C 8.0f // slower - 8 cycles
+#define PERFSCORE_THROUGHPUT_9C 9.0f // slower - 9 cycles
+#define PERFSCORE_THROUGHPUT_10C 10.0f // slower - 10 cycles
+#define PERFSCORE_THROUGHPUT_13C 13.0f // slower - 13 cycles
+#define PERFSCORE_THROUGHPUT_19C 19.0f // slower - 19 cycles
+#define PERFSCORE_THROUGHPUT_25C 25.0f // slower - 25 cycles
+#define PERFSCORE_THROUGHPUT_33C 33.0f // slower - 33 cycles
+#define PERFSCORE_THROUGHPUT_52C 52.0f // slower - 52 cycles
+#define PERFSCORE_THROUGHPUT_57C 57.0f // slower - 57 cycles
+#define PERFSCORE_THROUGHPUT_140C 140.0f // slower - 140 cycles
#define PERFSCORE_LATENCY_ILLEGAL -1024.0f
@@ -1281,6 +1282,7 @@ class emitter
#define PERFSCORE_LATENCY_26C 26.0f
#define PERFSCORE_LATENCY_62C 62.0f
#define PERFSCORE_LATENCY_69C 69.0f
+#define PERFSCORE_LATENCY_140C 140.0f
#define PERFSCORE_LATENCY_400C 400.0f // Intel microcode issue with these instuctions
#define PERFSCORE_LATENCY_BRANCH_DIRECT 1.0f // cost of an unconditional branch
@@ -1381,14 +1383,29 @@ class emitter
#if FEATURE_LOOP_ALIGN
struct instrDescAlign : instrDesc
{
- instrDescAlign* idaNext; // next align in the group/method
- insGroup* idaIG; // containing group
- };
+ instrDescAlign* idaNext; // next align in the group/method
+ insGroup* idaIG; // containing group
+ insGroup* idaLoopHeadPredIG; // The IG before the loop IG.
+ // If no 'jmp' instructions were found until idaLoopHeadPredIG,
+ // then idaLoopHeadPredIG == idaIG.
+#ifdef DEBUG
+ bool isPlacedAfterJmp; // Is the 'align' instruction placed after jmp. Used to decide
+ // if the instruction cost should be included in PerfScore
+ // calculation or not.
+#endif
- void emitCheckAlignFitInCurIG(unsigned short nAlignInstr);
- void emitLoopAlign(unsigned short paddingBytes);
- void emitLongLoopAlign(unsigned short alignmentBoundary);
+ inline insGroup* loopHeadIG()
+ {
+ assert(idaLoopHeadPredIG);
+ return idaLoopHeadPredIG->igNext;
+ }
+ void removeAlignFlags()
+ {
+ idaIG->igFlags &= ~IGF_HAS_ALIGN;
+ }
+ };
+ void emitCheckAlignFitInCurIG(unsigned nAlignInstr);
#endif // FEATURE_LOOP_ALIGN
#if !defined(TARGET_ARM64) // This shouldn't be needed for ARM32, either, but I don't want to touch the ARM32 JIT.
@@ -1531,6 +1548,7 @@ class emitter
regPtrDsc* debugPrevRegPtrDsc;
regMaskTP debugPrevGCrefRegs;
regMaskTP debugPrevByrefRegs;
+ void emitDispInsIndent();
void emitDispGCDeltaTitle(const char* title);
void emitDispGCRegDelta(const char* title, regMaskTP prevRegs, regMaskTP curRegs);
void emitDispGCVarDelta();
@@ -1546,6 +1564,14 @@ class emitter
void emitDispInsAddr(BYTE* code);
void emitDispInsOffs(unsigned offs, bool doffs);
void emitDispInsHex(instrDesc* id, BYTE* code, size_t sz);
+ void emitDispIns(instrDesc* id,
+ bool isNew,
+ bool doffs,
+ bool asmfm,
+ unsigned offs = 0,
+ BYTE* pCode = nullptr,
+ size_t sz = 0,
+ insGroup* ig = nullptr);
#else // !DEBUG
#define emitVarRefOffs 0
@@ -1778,15 +1804,26 @@ class emitter
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* emitAlignList; // list of all align instructions in method
instrDescAlign* emitAlignLast; // last align instruction in method
+
+ // Points to the most recent added align instruction. If there are multiple align instructions like in arm64 or
+ // non-adaptive alignment on xarch, this points to the first align instruction of the series of align instructions.
+ instrDescAlign* emitAlignLastGroup;
+
unsigned getLoopSize(insGroup* igLoopHeader,
unsigned maxLoopSize DEBUG_ARG(bool isAlignAdjusted)); // Get the smallest loop size
- void emitLoopAlignment();
+ void emitLoopAlignment(DEBUG_ARG1(bool isPlacedBehindJmp));
bool emitEndsWithAlignInstr(); // Validate if newLabel is appropriate
void emitSetLoopBackEdge(BasicBlock* loopTopBlock);
void emitLoopAlignAdjustments(); // Predict if loop alignment is needed and make appropriate adjustments
unsigned emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offset DEBUG_ARG(bool isAlignAdjusted));
+
+ void emitLoopAlign(unsigned paddingBytes, bool isFirstAlign DEBUG_ARG(bool isPlacedBehindJmp));
+ void emitLongLoopAlign(unsigned alignmentBoundary DEBUG_ARG(bool isPlacedBehindJmp));
+ instrDescAlign* emitAlignInNextIG(instrDescAlign* alignInstr);
+ void emitConnectAlignInstrWithCurIG();
+
#endif
void emitCheckFuncletBranch(instrDesc* jmp, insGroup* jmpIG); // Check for illegal branches between funclets
diff --git a/src/coreclr/jit/emitarm.cpp b/src/coreclr/jit/emitarm.cpp
index 7ed84210741924..0b11fcc742b263 100644
--- a/src/coreclr/jit/emitarm.cpp
+++ b/src/coreclr/jit/emitarm.cpp
@@ -4678,7 +4678,7 @@ void emitter::emitIns_Call(EmitCallType callType,
VARSET_VALARG_TP ptrVars,
regMaskTP gcrefRegs,
regMaskTP byrefRegs,
- IL_OFFSETX ilOffset /* = BAD_IL_OFFSET */,
+ const DebugInfo& di /* = DebugInfo() */,
regNumber ireg /* = REG_NA */,
regNumber xreg /* = REG_NA */,
unsigned xmul /* = 0 */,
@@ -4719,9 +4719,9 @@ void emitter::emitIns_Call(EmitCallType callType,
#endif
/* Managed RetVal: emit sequence point for the call */
- if (emitComp->opts.compDbgInfo && ilOffset != BAD_IL_OFFSET)
+ if (emitComp->opts.compDbgInfo && di.GetLocation().IsValid())
{
- codeGen->genIPmappingAdd(ilOffset, false);
+ codeGen->genIPmappingAdd(IPmappingDscKind::Normal, di, false);
}
/*
@@ -7670,6 +7670,22 @@ void emitter::emitDispInsHelp(
printf("\n");
}
+//--------------------------------------------------------------------
+// emitDispIns: Dump the given instruction to jitstdout.
+//
+// Arguments:
+// id - The instruction
+// isNew - Whether the instruction is newly generated (before encoding).
+// doffs - If true, always display the passed-in offset.
+// asmfm - Whether the instruction should be displayed in assembly format.
+// If false some additional information may be printed for the instruction.
+// offset - The offset of the instruction. Only displayed if doffs is true or if
+// !isNew && !asmfm.
+// code - Pointer to the actual code, used for displaying the address and encoded bytes
+// if turned on.
+// sz - The size of the instruction, used to display the encoded bytes.
+// ig - The instruction group containing the instruction.
+//
void emitter::emitDispIns(
instrDesc* id, bool isNew, bool doffs, bool asmfm, unsigned offset, BYTE* code, size_t sz, insGroup* ig)
{
@@ -7767,12 +7783,7 @@ void emitter::emitDispFrameRef(int varx, int disp, int offs, bool asmfm)
if (varx >= 0 && emitComp->opts.varNames)
{
- LclVarDsc* varDsc;
- const char* varName;
-
- assert((unsigned)varx < emitComp->lvaCount);
- varDsc = emitComp->lvaTable + varx;
- varName = emitComp->compLocalVarName(varx, offs);
+ const char* varName = emitComp->compLocalVarName(varx, offs);
if (varName)
{
@@ -7973,9 +7984,7 @@ void emitter::emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataR
// no logic here to track local variable lifetime changes, like we do in the contained case
// above. E.g., for a `str r0,[r1]` for byref `r1` to local `V01`, we won't store the local
// `V01` and so the emitter can't update the GC lifetime for `V01` if this is a variable birth.
- GenTreeLclVarCommon* varNode = addr->AsLclVarCommon();
- unsigned lclNum = varNode->GetLclNum();
- LclVarDsc* varDsc = emitComp->lvaGetDesc(lclNum);
+ LclVarDsc* varDsc = emitComp->lvaGetDesc(addr->AsLclVarCommon());
assert(!varDsc->lvTracked);
}
#endif // DEBUG
@@ -8095,7 +8104,7 @@ regNumber emitter::emitInsTernary(instruction ins, emitAttr attr, GenTree* dst,
if (dst->gtSetFlags())
{
assert((ins == INS_add) || (ins == INS_adc) || (ins == INS_sub) || (ins == INS_sbc) || (ins == INS_and) ||
- (ins == INS_orr) || (ins == INS_eor) || (ins == INS_orn));
+ (ins == INS_orr) || (ins == INS_eor) || (ins == INS_orn) || (ins == INS_bic));
flags = INS_FLAGS_SET;
}
diff --git a/src/coreclr/jit/emitarm.h b/src/coreclr/jit/emitarm.h
index 744b8676c45b2c..9dd2c175190dcb 100644
--- a/src/coreclr/jit/emitarm.h
+++ b/src/coreclr/jit/emitarm.h
@@ -52,14 +52,6 @@ void emitDispInsHelp(instrDesc* id,
BYTE* code = 0,
size_t sz = 0,
insGroup* ig = NULL);
-void emitDispIns(instrDesc* id,
- bool isNew,
- bool doffs,
- bool asmfm,
- unsigned offs = 0,
- BYTE* code = 0,
- size_t sz = 0,
- insGroup* ig = NULL);
#endif // DEBUG
@@ -328,12 +320,12 @@ void emitIns_Call(EmitCallType callType,
VARSET_VALARG_TP ptrVars,
regMaskTP gcrefRegs,
regMaskTP byrefRegs,
- IL_OFFSETX ilOffset = BAD_IL_OFFSET,
- regNumber ireg = REG_NA,
- regNumber xreg = REG_NA,
- unsigned xmul = 0,
- ssize_t disp = 0,
- bool isJump = false);
+ const DebugInfo& di = DebugInfo(),
+ regNumber ireg = REG_NA,
+ regNumber xreg = REG_NA,
+ unsigned xmul = 0,
+ ssize_t disp = 0,
+ bool isJump = false);
/*****************************************************************************
*
diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp
index c810029aa238bf..1bcda1dd0a88a3 100644
--- a/src/coreclr/jit/emitarm64.cpp
+++ b/src/coreclr/jit/emitarm64.cpp
@@ -3649,21 +3649,20 @@ void emitter::emitIns_I(instruction ins, emitAttr attr, ssize_t imm)
insFormat fmt = IF_NONE;
/* Figure out the encoding format of the instruction */
- switch (ins)
+ if (ins == INS_BREAKPOINT)
{
- case INS_brk:
- if ((imm & 0x0000ffff) == imm)
- {
- fmt = IF_SI_0A;
- }
- else
- {
- assert(!"Instruction cannot be encoded: IF_SI_0A");
- }
- break;
- default:
- unreached();
- break;
+ if ((imm & 0x0000ffff) == imm)
+ {
+ fmt = IF_SI_0A;
+ }
+ else
+ {
+ assert(!"Instruction cannot be encoded: IF_SI_0A");
+ }
+ }
+ else
+ {
+ unreached();
}
assert(fmt != IF_NONE);
@@ -6985,6 +6984,14 @@ void emitter::emitIns_R_R_R_Ext(instruction ins,
{
shiftAmount = insOptsLSL(opt) ? scale : 0;
}
+
+ // If target reg is ZR - it means we're doing an implicit nullcheck
+ // where target type was ignored and set to TYP_INT.
+ if ((reg1 == REG_ZR) && (shiftAmount > 0))
+ {
+ shiftAmount = scale;
+ }
+
assert((shiftAmount == scale) || (shiftAmount == 0));
reg2 = encodingSPtoZR(reg2);
@@ -8520,7 +8527,7 @@ void emitter::emitIns_Call(EmitCallType callType,
VARSET_VALARG_TP ptrVars,
regMaskTP gcrefRegs,
regMaskTP byrefRegs,
- IL_OFFSETX ilOffset /* = BAD_IL_OFFSET */,
+ const DebugInfo& di /* = DebugInfo() */,
regNumber ireg /* = REG_NA */,
regNumber xreg /* = REG_NA */,
unsigned xmul /* = 0 */,
@@ -8561,9 +8568,9 @@ void emitter::emitIns_Call(EmitCallType callType,
#endif
/* Managed RetVal: emit sequence point for the call */
- if (emitComp->opts.compDbgInfo && ilOffset != BAD_IL_OFFSET)
+ if (emitComp->opts.compDbgInfo && di.GetLocation().IsValid())
{
- codeGen->genIPmappingAdd(ilOffset, false);
+ codeGen->genIPmappingAdd(IPmappingDscKind::Normal, di, false);
}
/*
@@ -11431,13 +11438,13 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp)
// IG can be marked as not needing alignment after emitting align instruction.
// Alternatively, there are fewer align instructions needed than emitted.
// If that is the case, skip outputting alignment.
- if (!ig->isLoopAlign() || id->idIsEmptyAlign())
+ if (!ig->endsWithAlignInstr() || id->idIsEmptyAlign())
{
skipIns = true;
}
#ifdef DEBUG
- if (!ig->isLoopAlign())
+ if (!ig->endsWithAlignInstr())
{
// Validate if the state is correctly updated
assert(id->idIsEmptyAlign());
@@ -11445,6 +11452,24 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp)
#endif
sz = sizeof(instrDescAlign);
ins = INS_nop;
+
+#ifdef DEBUG
+ // Under STRESS_EMITTER, if this is the 'align' before the 'jmp' instruction,
+ // then add "bkpt" instruction.
+ instrDescAlign* alignInstr = (instrDescAlign*)id;
+
+ if (emitComp->compStressCompile(Compiler::STRESS_EMITTER, 50) && alignInstr->isPlacedAfterJmp &&
+ !skipIns)
+ {
+ // There is no good way to squeeze in "bkpt" as well as display it
+ // in the disassembly because there is no corresponding instrDesc for
+ // it. As such, leave it as is, the "0xD43E0000" bytecode will be seen
+ // next to the nop instruction in disasm.
+ // e.g. D43E0000 align [4 bytes for IG07]
+ ins = INS_BREAKPOINT;
+ fmt = IF_SI_0A;
+ }
+#endif
}
#endif // FEATURE_LOOP_ALIGN
@@ -12203,11 +12228,22 @@ void emitter::emitDispInsHex(instrDesc* id, BYTE* code, size_t sz)
}
}
-/****************************************************************************
- *
- * Display the given instruction.
- */
-
+//--------------------------------------------------------------------
+// emitDispIns: Dump the given instruction to jitstdout.
+//
+// Arguments:
+// id - The instruction
+// isNew - Whether the instruction is newly generated (before encoding).
+// doffs - If true, always display the passed-in offset.
+// asmfm - Whether the instruction should be displayed in assembly format.
+// If false some additional information may be printed for the instruction.
+// offset - The offset of the instruction. Only displayed if doffs is true or if
+// !isNew && !asmfm.
+// code - Pointer to the actual code, used for displaying the address and encoded bytes
+// if turned on.
+// sz - The size of the instruction, used to display the encoded bytes.
+// ig - The instruction group containing the instruction.
+//
void emitter::emitDispIns(
instrDesc* id, bool isNew, bool doffs, bool asmfm, unsigned offset, BYTE* pCode, size_t sz, insGroup* ig)
{
@@ -13322,7 +13358,15 @@ void emitter::emitDispIns(
case IF_SN_0A: // SN_0A ................ ................
if (ins == INS_align)
{
- printf("[%d bytes]", id->idIsEmptyAlign() ? 0 : INSTR_ENCODED_SIZE);
+ instrDescAlign* alignInstrId = (instrDescAlign*)id;
+ printf("[%d bytes", id->idIsEmptyAlign() ? 0 : INSTR_ENCODED_SIZE);
+
+ // targetIG is only set for 1st of the series of align instruction
+ if ((alignInstrId->idaLoopHeadPredIG != nullptr) && (alignInstrId->loopHeadIG() != nullptr))
+ {
+ printf(" for IG%02u", alignInstrId->loopHeadIG()->igNum);
+ }
+ printf("]");
}
break;
@@ -13377,12 +13421,7 @@ void emitter::emitDispFrameRef(int varx, int disp, int offs, bool asmfm)
if (varx >= 0 && emitComp->opts.varNames)
{
- LclVarDsc* varDsc;
- const char* varName;
-
- assert((unsigned)varx < emitComp->lvaCount);
- varDsc = emitComp->lvaTable + varx;
- varName = emitComp->compLocalVarName(varx, offs);
+ const char* varName = emitComp->compLocalVarName(varx, offs);
if (varName)
{
@@ -13481,8 +13520,23 @@ void emitter::emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataR
}
else // no scale
{
- // Then load/store dataReg from/to [memBase + index]
- emitIns_R_R_R(ins, attr, dataReg, memBase->GetRegNum(), index->GetRegNum());
+ if (index->OperIs(GT_BFIZ) && index->isContained())
+ {
+ // Then load/store dataReg from/to [memBase + index*scale with sign/zero extension]
+ GenTreeCast* cast = index->gtGetOp1()->AsCast();
+
+ // For now, this code only supports extensions from i32/u32
+ assert(cast->isContained() && varTypeIsInt(cast->CastFromType()));
+
+ emitIns_R_R_R_Ext(ins, attr, dataReg, memBase->GetRegNum(), cast->CastOp()->GetRegNum(),
+ cast->IsUnsigned() ? INS_OPTS_UXTW : INS_OPTS_SXTW,
+ (int)index->gtGetOp2()->AsIntCon()->IconValue());
+ }
+ else
+ {
+ // Then load/store dataReg from/to [memBase + index]
+ emitIns_R_R_R(ins, attr, dataReg, memBase->GetRegNum(), index->GetRegNum());
+ }
}
}
}
@@ -13535,9 +13589,7 @@ void emitter::emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataR
// no logic here to track local variable lifetime changes, like we do in the contained case
// above. E.g., for a `str r0,[r1]` for byref `r1` to local `V01`, we won't store the local
// `V01` and so the emitter can't update the GC lifetime for `V01` if this is a variable birth.
- GenTreeLclVarCommon* varNode = addr->AsLclVarCommon();
- unsigned lclNum = varNode->GetLclNum();
- LclVarDsc* varDsc = emitComp->lvaGetDesc(lclNum);
+ LclVarDsc* varDsc = emitComp->lvaGetDesc(addr->AsLclVarCommon());
assert(!varDsc->lvTracked);
}
#endif // DEBUG
@@ -13597,44 +13649,6 @@ regNumber emitter::emitInsTernary(instruction ins, emitAttr attr, GenTree* dst,
// src2 can only be a reg
assert(!src2->isContained());
}
- else if ((src1->OperIs(GT_MUL) && src1->isContained()) || (src2->OperIs(GT_MUL) && src2->isContained()))
- {
- assert(ins == INS_add);
-
- GenTree* mul;
- GenTree* c;
- if (src1->OperIs(GT_MUL))
- {
- mul = src1;
- c = src2;
- }
- else
- {
- mul = src2;
- c = src1;
- }
-
- GenTree* a = mul->gtGetOp1();
- GenTree* b = mul->gtGetOp2();
-
- assert(varTypeIsIntegral(mul) && !mul->gtOverflow());
-
- bool msub = false;
- if (a->OperIs(GT_NEG) && a->isContained())
- {
- a = a->gtGetOp1();
- msub = true;
- }
- if (b->OperIs(GT_NEG) && b->isContained())
- {
- b = b->gtGetOp1();
- msub = !msub; // it's either "a * -b" or "-a * -b" which is the same as "a * b"
- }
-
- emitIns_R_R_R_R(msub ? INS_msub : INS_madd, attr, dst->GetRegNum(), a->GetRegNum(), b->GetRegNum(),
- c->GetRegNum());
- return dst->GetRegNum();
- }
else // not floating point
{
// src2 can be immed or reg
@@ -14596,18 +14610,29 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins
}
break;
- case IF_SN_0A: // bkpt, brk, nop
- if (id->idIsEmptyAlign())
+ case IF_SN_0A: // nop, yield, align
+
+ if (id->idIns() == INS_align)
{
- // 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;
+ if ((id->idInsOpt() == INS_OPTS_NONE) || ((instrDescAlign*)id)->isPlacedAfterJmp)
+ {
+ // Either we're not going to generate 'align' instruction, or the 'align'
+ // instruction is placed immediately after unconditional jmp.
+ // In both cases, don't count for PerfScore.
+
+ result.insThroughput = PERFSCORE_THROUGHPUT_ZERO;
+ result.insLatency = PERFSCORE_LATENCY_ZERO;
+ break;
+ }
}
- else
+ else if (ins == INS_yield)
{
- result.insThroughput = PERFSCORE_THROUGHPUT_2X;
- result.insLatency = PERFSCORE_LATENCY_ZERO;
+ // @ToDo - find out the actual latency, match x86/x64 for now
+ result.insThroughput = PERFSCORE_THROUGHPUT_140C;
+ result.insLatency = PERFSCORE_LATENCY_140C;
}
+ result.insThroughput = PERFSCORE_THROUGHPUT_2X;
+ result.insLatency = PERFSCORE_LATENCY_ZERO;
break;
case IF_SI_0B: // dmb, dsb, isb
diff --git a/src/coreclr/jit/emitarm64.h b/src/coreclr/jit/emitarm64.h
index 77a71a90051577..05909f6d2e175f 100644
--- a/src/coreclr/jit/emitarm64.h
+++ b/src/coreclr/jit/emitarm64.h
@@ -45,15 +45,6 @@ void emitDispShiftedReg(regNumber reg, insOpts opt, ssize_t imm, emitAttr attr);
void emitDispExtendReg(regNumber reg, insOpts opt, ssize_t imm);
void emitDispAddrRI(regNumber reg, insOpts opt, ssize_t imm);
void emitDispAddrRRExt(regNumber reg1, regNumber reg2, insOpts opt, bool isScaled, emitAttr size);
-
-void emitDispIns(instrDesc* id,
- bool isNew,
- bool doffs,
- bool asmfm,
- unsigned offs = 0,
- BYTE* pCode = 0,
- size_t sz = 0,
- insGroup* ig = NULL);
#endif // DEBUG
/************************************************************************/
@@ -858,7 +849,7 @@ void emitIns_Call(EmitCallType callType,
VARSET_VALARG_TP ptrVars,
regMaskTP gcrefRegs,
regMaskTP byrefRegs,
- IL_OFFSETX ilOffset,
+ const DebugInfo& di,
regNumber ireg,
regNumber xreg,
unsigned xmul,
diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp
index e07984760e018e..d472b1393518fb 100644
--- a/src/coreclr/jit/emitxarch.cpp
+++ b/src/coreclr/jit/emitxarch.cpp
@@ -164,6 +164,21 @@ bool emitter::DoesWriteZeroFlag(instruction ins)
return (CodeGenInterface::instInfo[ins] & Writes_ZF) != 0;
}
+//------------------------------------------------------------------------
+// DoesWriteSignFlag: check if the instruction writes the
+// SF flag.
+//
+// Arguments:
+// ins - instruction to test
+//
+// Return Value:
+// true if instruction writes the SF flag, false otherwise.
+//
+bool emitter::DoesWriteSignFlag(instruction ins)
+{
+ return (CodeGenInterface::instInfo[ins] & Writes_SF) != 0;
+}
+
//------------------------------------------------------------------------
// DoesResetOverflowAndCarryFlags: check if the instruction resets the
// OF and CF flag to 0.
@@ -338,6 +353,11 @@ bool emitter::AreFlagsSetToZeroCmp(regNumber reg, emitAttr opSize, genTreeOps tr
{
assert(reg != REG_NA);
+ if (!emitComp->opts.OptimizationEnabled())
+ {
+ return false;
+ }
+
// Don't look back across IG boundaries (possible control flow)
if (emitCurIGinsCnt == 0 && ((emitCurIG->igFlags & IGF_EXTEND) == 0))
{
@@ -393,6 +413,79 @@ bool emitter::AreFlagsSetToZeroCmp(regNumber reg, emitAttr opSize, genTreeOps tr
return false;
}
+//------------------------------------------------------------------------
+// AreFlagsSetToForSignJumpOpt: checks if the previous instruction set the SF if the tree
+// node qualifies for a jg/jle to jns/js optimization
+//
+// Arguments:
+// reg - register of interest
+// opSize - size of register
+// relop - relational tree node
+//
+// Return Value:
+// true if the tree node qualifies for the jg/jle to jns/js optimization
+// false if not, or if we can't safely determine
+//
+// Notes:
+// Currently only looks back one instruction.
+bool emitter::AreFlagsSetForSignJumpOpt(regNumber reg, emitAttr opSize, GenTree* relop)
+{
+ assert(reg != REG_NA);
+
+ if (!emitComp->opts.OptimizationEnabled())
+ {
+ return false;
+ }
+
+ // Don't look back across IG boundaries (possible control flow)
+ if (emitCurIGinsCnt == 0 && ((emitCurIG->igFlags & IGF_EXTEND) == 0))
+ {
+ return false;
+ }
+
+ instrDesc* id = emitLastIns;
+ instruction lastIns = id->idIns();
+ insFormat fmt = id->idInsFmt();
+
+ // make sure op1 is a reg
+ switch (fmt)
+ {
+ case IF_RWR_CNS:
+ case IF_RRW_CNS:
+ case IF_RRW_SHF:
+ case IF_RWR_RRD:
+ case IF_RRW_RRD:
+ case IF_RWR_MRD:
+ case IF_RWR_SRD:
+ case IF_RRW_SRD:
+ case IF_RWR_ARD:
+ case IF_RRW_ARD:
+ case IF_RWR:
+ case IF_RRD:
+ case IF_RRW:
+ break;
+ default:
+ return false;
+ }
+
+ if (id->idReg1() != reg)
+ {
+ return false;
+ }
+
+ // If we have a GT_GE/GT_LT which generates an jge/jl, and the previous instruction
+ // sets the SF, we can omit a test instruction and check for jns/js.
+ if ((relop->OperGet() == GT_GE || relop->OperGet() == GT_LT) && !GenCondition::FromRelop(relop).IsUnsigned())
+ {
+ if (DoesWriteSignFlag(lastIns) && IsFlagsAlwaysModified(id))
+ {
+ return id->idOpSize() == opSize;
+ }
+ }
+
+ return false;
+}
+
//------------------------------------------------------------------------
// IsDstSrcImmAvxInstruction: Checks if the instruction has a "reg, reg/mem, imm" or
// "reg/mem, reg, imm" form for the legacy, VEX, and EVEX
@@ -1293,7 +1386,7 @@ const BYTE emitter::emitInsModeFmtTab[] =
// clang-format on
#ifdef DEBUG
-unsigned const emitter::emitInsModeFmtCnt = _countof(emitInsModeFmtTab);
+unsigned const emitter::emitInsModeFmtCnt = ArrLen(emitInsModeFmtTab);
#endif
/*****************************************************************************
@@ -1387,7 +1480,7 @@ inline size_t insCode(instruction ins)
};
// clang-format on
- assert((unsigned)ins < _countof(insCodes));
+ assert((unsigned)ins < ArrLen(insCodes));
assert((insCodes[ins] != BAD_CODE));
return insCodes[ins];
@@ -1420,7 +1513,7 @@ inline size_t insCodeACC(instruction ins)
};
// clang-format on
- assert((unsigned)ins < _countof(insCodesACC));
+ assert((unsigned)ins < ArrLen(insCodesACC));
assert((insCodesACC[ins] != BAD_CODE));
return insCodesACC[ins];
@@ -1453,7 +1546,7 @@ inline size_t insCodeRR(instruction ins)
};
// clang-format on
- assert((unsigned)ins < _countof(insCodesRR));
+ assert((unsigned)ins < ArrLen(insCodesRR));
assert((insCodesRR[ins] != BAD_CODE));
return insCodesRR[ins];
@@ -1482,7 +1575,7 @@ size_t insCodesRM[] =
// Returns true iff the give CPU instruction has an RM encoding.
inline bool hasCodeRM(instruction ins)
{
- assert((unsigned)ins < _countof(insCodesRM));
+ assert((unsigned)ins < ArrLen(insCodesRM));
return ((insCodesRM[ins] != BAD_CODE));
}
@@ -1493,7 +1586,7 @@ inline bool hasCodeRM(instruction ins)
inline size_t insCodeRM(instruction ins)
{
- assert((unsigned)ins < _countof(insCodesRM));
+ assert((unsigned)ins < ArrLen(insCodesRM));
assert((insCodesRM[ins] != BAD_CODE));
return insCodesRM[ins];
@@ -1522,7 +1615,7 @@ size_t insCodesMI[] =
// Returns true iff the give CPU instruction has an MI encoding.
inline bool hasCodeMI(instruction ins)
{
- assert((unsigned)ins < _countof(insCodesMI));
+ assert((unsigned)ins < ArrLen(insCodesMI));
return ((insCodesMI[ins] != BAD_CODE));
}
@@ -1533,7 +1626,7 @@ inline bool hasCodeMI(instruction ins)
inline size_t insCodeMI(instruction ins)
{
- assert((unsigned)ins < _countof(insCodesMI));
+ assert((unsigned)ins < ArrLen(insCodesMI));
assert((insCodesMI[ins] != BAD_CODE));
return insCodesMI[ins];
@@ -1562,7 +1655,7 @@ size_t insCodesMR[] =
// Returns true iff the give CPU instruction has an MR encoding.
inline bool hasCodeMR(instruction ins)
{
- assert((unsigned)ins < _countof(insCodesMR));
+ assert((unsigned)ins < ArrLen(insCodesMR));
return ((insCodesMR[ins] != BAD_CODE));
}
@@ -1573,7 +1666,7 @@ inline bool hasCodeMR(instruction ins)
inline size_t insCodeMR(instruction ins)
{
- assert((unsigned)ins < _countof(insCodesMR));
+ assert((unsigned)ins < ArrLen(insCodesMR));
assert((insCodesMR[ins] != BAD_CODE));
return insCodesMR[ins];
@@ -2177,8 +2270,8 @@ inline UNATIVE_OFFSET emitter::emitInsSizeSV(code_t code, int var, int dsp)
CLANG_FORMAT_COMMENT_ANCHOR;
#ifdef UNIX_AMD64_ABI
- LclVarDsc* varDsc = emitComp->lvaTable + var;
- bool isRegPassedArg = varDsc->lvIsParam && varDsc->lvIsRegArg;
+ const LclVarDsc* varDsc = emitComp->lvaGetDesc(var);
+ bool isRegPassedArg = varDsc->lvIsParam && varDsc->lvIsRegArg;
// Register passed args could have a stack offset of 0.
noway_assert((int)offs < 0 || isRegPassedArg || emitComp->opts.IsOSR());
#else // !UNIX_AMD64_ABI
@@ -2805,7 +2898,8 @@ void emitter::emitIns(instruction ins)
ins == INS_r_movsp || ins == INS_r_stosb || ins == INS_r_stosd || ins == INS_r_stosp || ins == INS_ret ||
ins == INS_sahf || ins == INS_stosb || ins == INS_stosd || ins == INS_stosp
// These instructions take zero operands
- || ins == INS_vzeroupper || ins == INS_lfence || ins == INS_mfence || ins == INS_sfence);
+ || ins == INS_vzeroupper || ins == INS_lfence || ins == INS_mfence || ins == INS_sfence ||
+ ins == INS_pause);
assert(assertCond);
}
@@ -7536,7 +7630,7 @@ void emitter::emitIns_Call(EmitCallType callType,
VARSET_VALARG_TP ptrVars,
regMaskTP gcrefRegs,
regMaskTP byrefRegs,
- IL_OFFSETX ilOffset,
+ const DebugInfo& di,
regNumber ireg,
regNumber xreg,
unsigned xmul,
@@ -7577,9 +7671,9 @@ void emitter::emitIns_Call(EmitCallType callType,
#endif
/* Managed RetVal: emit sequence point for the call */
- if (emitComp->opts.compDbgInfo && ilOffset != BAD_IL_OFFSET)
+ if (emitComp->opts.compDbgInfo && di.IsValid())
{
- codeGen->genIPmappingAdd(ilOffset, false);
+ codeGen->genIPmappingAdd(IPmappingDscKind::Normal, di, false);
}
/*
@@ -8116,7 +8210,7 @@ const char* emitter::emitXMMregName(unsigned reg)
};
assert(reg < REG_COUNT);
- assert(reg < _countof(regNames));
+ assert(reg < ArrLen(regNames));
return regNames[reg];
}
@@ -8134,7 +8228,7 @@ const char* emitter::emitYMMregName(unsigned reg)
};
assert(reg < REG_COUNT);
- assert(reg < _countof(regNames));
+ assert(reg < ArrLen(regNames));
return regNames[reg];
}
@@ -8303,12 +8397,7 @@ void emitter::emitDispFrameRef(int varx, int disp, int offs, bool asmfm)
if (varx >= 0 && emitComp->opts.varNames)
{
- LclVarDsc* varDsc;
- const char* varName;
-
- assert((unsigned)varx < emitComp->lvaCount);
- varDsc = emitComp->lvaTable + varx;
- varName = emitComp->compLocalVarName(varx, offs);
+ const char* varName = emitComp->compLocalVarName(varx, offs);
if (varName)
{
@@ -8628,11 +8717,22 @@ void emitter::emitDispInsHex(instrDesc* id, BYTE* code, size_t sz)
}
}
-/*****************************************************************************
- *
- * Display the given instruction.
- */
-
+//--------------------------------------------------------------------
+// emitDispIns: Dump the given instruction to jitstdout.
+//
+// Arguments:
+// id - The instruction
+// isNew - Whether the instruction is newly generated (before encoding).
+// doffs - If true, always display the passed-in offset.
+// asmfm - Whether the instruction should be displayed in assembly format.
+// If false some additional information may be printed for the instruction.
+// offset - The offset of the instruction. Only displayed if doffs is true or if
+// !isNew && !asmfm.
+// code - Pointer to the actual code, used for displaying the address and encoded bytes
+// if turned on.
+// sz - The size of the instruction, used to display the encoded bytes.
+// ig - The instruction group containing the instruction. Not used on xarch.
+//
void emitter::emitDispIns(
instrDesc* id, bool isNew, bool doffs, bool asmfm, unsigned offset, BYTE* code, size_t sz, insGroup* ig)
{
@@ -9736,7 +9836,14 @@ void emitter::emitDispIns(
#if FEATURE_LOOP_ALIGN
if (ins == INS_align)
{
- printf("[%d bytes]", id->idCodeSize());
+ instrDescAlign* alignInstrId = (instrDescAlign*)id;
+ printf("[%d bytes", alignInstrId->idCodeSize());
+ // targetIG is only set for 1st of the series of align instruction
+ if ((alignInstrId->idaLoopHeadPredIG != nullptr) && (alignInstrId->loopHeadIG() != nullptr))
+ {
+ printf(" for IG%02u", alignInstrId->loopHeadIG()->igNum);
+ }
+ printf("]");
}
#endif
break;
@@ -9924,32 +10031,74 @@ static BYTE* emitOutputNOP(BYTE* dstRW, size_t nBytes)
//
BYTE* emitter::emitOutputAlign(insGroup* ig, instrDesc* id, BYTE* dst)
{
+ instrDescAlign* alignInstr = (instrDescAlign*)id;
+
+#ifdef DEBUG
+ // For cases where 'align' was placed behing a 'jmp' in an IG that does not
+ // immediately preced the loop IG, we do not know in advance the offset of
+ // IG having loop. For such cases, skip the padding calculation validation.
+ bool validatePadding = !alignInstr->isPlacedAfterJmp;
+#endif
+
// Candidate for loop alignment
assert(codeGen->ShouldAlignLoops());
- assert(ig->isLoopAlign());
+ assert(ig->endsWithAlignInstr());
unsigned paddingToAdd = id->idCodeSize();
// Either things are already aligned or align them here.
- assert((paddingToAdd == 0) || (((size_t)dst & (emitComp->opts.compJitAlignLoopBoundary - 1)) != 0));
+ assert(!validatePadding || (paddingToAdd == 0) ||
+ (((size_t)dst & (emitComp->opts.compJitAlignLoopBoundary - 1)) != 0));
// Padding amount should not exceed the alignment boundary
assert(0 <= paddingToAdd && paddingToAdd < emitComp->opts.compJitAlignLoopBoundary);
#ifdef DEBUG
- unsigned paddingNeeded = emitCalculatePaddingForLoopAlignment(ig, (size_t)dst, true);
-
- // For non-adaptive, padding size is spread in multiple instructions, so don't bother checking
- if (emitComp->opts.compJitAlignLoopAdaptive)
+ if (validatePadding)
{
- assert(paddingToAdd == paddingNeeded);
+ unsigned paddingNeeded =
+ emitCalculatePaddingForLoopAlignment(((instrDescAlign*)id)->idaIG->igNext, (size_t)dst, true);
+
+ // For non-adaptive, padding size is spread in multiple instructions, so don't bother checking
+ if (emitComp->opts.compJitAlignLoopAdaptive)
+ {
+ assert(paddingToAdd == paddingNeeded);
+ }
}
emitComp->loopsAligned++;
#endif
BYTE* dstRW = dst + writeableOffset;
- dstRW = emitOutputNOP(dstRW, paddingToAdd);
+
+#ifdef DEBUG
+ // Under STRESS_EMITTER, if this is the 'align' before the 'jmp' instruction,
+ // then add "int3" instruction. Since int3 takes 1 byte, we would only add
+ // it if paddingToAdd >= 1 byte.
+
+ if (emitComp->compStressCompile(Compiler::STRESS_EMITTER, 50) && alignInstr->isPlacedAfterJmp && paddingToAdd >= 1)
+ {
+ size_t int3Code = insCodeMR(INS_BREAKPOINT);
+ // There is no good way to squeeze in "int3" as well as display it
+ // in the disassembly because there is no corresponding instrDesc for
+ // it. As such, leave it as is, the "0xCC" bytecode will be seen next
+ // to the nop instruction in disasm.
+ // e.g. CC align [1 bytes for IG29]
+ //
+ // if (emitComp->opts.disAsm)
+ //{
+ // emitDispInsAddr(dstRW);
+
+ // emitDispInsOffs(0, false);
+
+ // printf(" %-9s ; stress-mode injected interrupt\n", "int3");
+ //}
+ dstRW += emitOutputByte(dstRW, int3Code);
+ paddingToAdd -= 1;
+ }
+#endif
+
+ dstRW = emitOutputNOP(dstRW, paddingToAdd);
return dstRW - writeableOffset;
}
@@ -12240,8 +12389,8 @@ BYTE* emitter::emitOutputRR(BYTE* dst, instrDesc* id)
// Due to elided register moves, we can't have the following assert.
// For example, consider:
// t85 = LCL_VAR byref V01 arg1 rdx (last use) REG rdx
- // /--* t85 byref
- // * STORE_LCL_VAR byref V40 tmp31 rdx REG rdx
+ // /--* t85 byref
+ // * STORE_LCL_VAR byref V40 tmp31 rdx REG rdx
// Here, V01 is type `long` on entry, then is stored as a byref. But because
// the register allocator assigned the same register, no instruction was
// generated, and we only (currently) make gcref/byref changes in emitter GC info
@@ -13279,7 +13428,7 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp)
sz = sizeof(instrDescAlign);
// IG can be marked as not needing alignment after emitting align instruction
// In such case, skip outputting alignment.
- if (ig->isLoopAlign())
+ if (ig->endsWithAlignInstr())
{
dst = emitOutputAlign(ig, id, dst);
}
@@ -14729,9 +14878,12 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins
{
case INS_align:
#if FEATURE_LOOP_ALIGN
- if (id->idCodeSize() == 0)
+ if ((id->idCodeSize() == 0) || ((instrDescAlign*)id)->isPlacedAfterJmp)
{
- // We're not going to generate any instruction, so it doesn't count for PerfScore.
+ // Either we're not going to generate 'align' instruction, or the 'align'
+ // instruction is placed immediately after unconditional jmp.
+ // In both cases, don't count for PerfScore.
+
result.insThroughput = PERFSCORE_THROUGHPUT_ZERO;
result.insLatency = PERFSCORE_LATENCY_ZERO;
break;
@@ -16011,6 +16163,13 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins
result.insThroughput = PERFSCORE_THROUGHPUT_2X;
break;
+ case INS_pause:
+ {
+ result.insLatency = PERFSCORE_LATENCY_140C;
+ result.insThroughput = PERFSCORE_THROUGHPUT_140C;
+ break;
+ }
+
default:
// unhandled instruction insFmt combination
perfScoreUnhandledInstruction(id, &result);
diff --git a/src/coreclr/jit/emitxarch.h b/src/coreclr/jit/emitxarch.h
index 8217d088417cc1..54575044f13867 100644
--- a/src/coreclr/jit/emitxarch.h
+++ b/src/coreclr/jit/emitxarch.h
@@ -116,6 +116,7 @@ static bool IsJmpInstruction(instruction ins);
bool AreUpper32BitsZero(regNumber reg);
bool AreFlagsSetToZeroCmp(regNumber reg, emitAttr opSize, genTreeOps treeOps);
+bool AreFlagsSetForSignJumpOpt(regNumber reg, emitAttr opSize, GenTree* tree);
bool hasRexPrefix(code_t code)
{
@@ -190,6 +191,7 @@ void SetContains256bitAVX(bool value)
bool IsDstDstSrcAVXInstruction(instruction ins);
bool IsDstSrcSrcAVXInstruction(instruction ins);
bool DoesWriteZeroFlag(instruction ins);
+bool DoesWriteSignFlag(instruction ins);
bool DoesResetOverflowAndCarryFlags(instruction ins);
bool IsFlagsAlwaysModified(instrDesc* id);
@@ -220,15 +222,6 @@ void emitDispReloc(ssize_t value);
void emitDispAddrMode(instrDesc* id, bool noDetail = false);
void emitDispShift(instruction ins, int cnt = 0);
-void emitDispIns(instrDesc* id,
- bool isNew,
- bool doffs,
- bool asmfm,
- unsigned offs = 0,
- BYTE* code = nullptr,
- size_t sz = 0,
- insGroup* ig = nullptr);
-
const char* emitXMMregName(unsigned reg);
const char* emitYMMregName(unsigned reg);
@@ -539,7 +532,7 @@ void emitIns_Call(EmitCallType callType,
VARSET_VALARG_TP ptrVars,
regMaskTP gcrefRegs,
regMaskTP byrefRegs,
- IL_OFFSETX ilOffset = BAD_IL_OFFSET,
+ const DebugInfo& di = DebugInfo(),
regNumber ireg = REG_NA,
regNumber xreg = REG_NA,
unsigned xmul = 0,
diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp
index da643cfd6d4db4..a82898c2595970 100644
--- a/src/coreclr/jit/fgbasic.cpp
+++ b/src/coreclr/jit/fgbasic.cpp
@@ -895,6 +895,11 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed
bool preciseScan = makeInlineObservations && compInlineResult->GetPolicy()->RequiresPreciseScan();
const bool resolveTokens = preciseScan && (isPreJit || isTier1);
+ // Track offsets where IL instructions begin in DEBUG builds. Used to
+ // validate debug info generated by the JIT.
+ assert(codeSize == compInlineContext->GetILSize());
+ INDEBUG(FixedBitVect* ilInstsSet = FixedBitVect::bitVectInit(codeSize, this));
+
if (makeInlineObservations)
{
// Set default values for profile (to avoid NoteFailed in CALLEE_IL_CODE_SIZE's handler)
@@ -946,6 +951,9 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed
{
prevOpcode = opcode;
opcode = (OPCODE)getU1LittleEndian(codeAddr);
+
+ INDEBUG(ilInstsSet->bitVectSet((UINT)(codeAddr - codeBegp)));
+
codeAddr += sizeof(__int8);
if (!handled && preciseScan)
@@ -1939,9 +1947,8 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed
// is based in that we know what trees we will
// generate for this ldfld, and we require that we
// won't need the address of this local at all
- noway_assert(varNum < lvaTableCnt);
- const bool notStruct = !varTypeIsStruct(&lvaTable[varNum]);
+ const bool notStruct = !varTypeIsStruct(lvaGetDesc(varNum));
const bool notLastInstr = (codeAddr < codeEndp - sz);
const bool notDebugCode = !opts.compDbgCode;
@@ -2007,6 +2014,8 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed
case CEE_LOCALLOC:
+ compLocallocSeen = true;
+
// We now allow localloc callees to become candidates in some cases.
if (makeInlineObservations)
{
@@ -2092,6 +2101,8 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed
" at offset %04X", (IL_OFFSET)(codeAddr - codeBegp));
}
+ INDEBUG(compInlineContext->SetILInstsSet(ilInstsSet));
+
if (makeInlineObservations)
{
compInlineResult->Note(InlineObservation::CALLEE_END_OPCODE_SCAN);
@@ -2726,15 +2737,9 @@ unsigned Compiler::fgMakeBasicBlocks(const BYTE* codeAddr, IL_OFFSET codeSize, F
BADCODE3("tail call not followed by ret", " at offset %04X", (IL_OFFSET)(codeAddr - codeBegp));
}
- if (fgCanSwitchToOptimized() && fgMayExplicitTailCall())
+ if (fgMayExplicitTailCall())
{
- // Method has an explicit tail call that may run like a loop or may not be generated as a tail
- // call in tier 0, switch to optimized to avoid spending too much time running slower code
- if (!opts.jitFlags->IsSet(JitFlags::JIT_FLAG_BBINSTR) ||
- ((info.compFlags & CORINFO_FLG_DISABLE_TIER0_FOR_LOOPS) != 0))
- {
- fgSwitchToOptimized();
- }
+ compTailPrefixSeen = true;
}
}
else
@@ -3522,6 +3527,57 @@ void Compiler::fgFindBasicBlocks()
#endif
fgNormalizeEH();
+
+ fgCheckForLoopsInHandlers();
+}
+
+//------------------------------------------------------------------------
+// fgCheckForLoopsInHandlers: scan blocks seeing if any handler block
+// is a backedge target.
+//
+// Notes:
+// Sets compHasBackwardJumpInHandler if so. This will disable
+// setting patchpoints in this method and prompt the jit to
+// optimize the method instead.
+//
+// We assume any late-added handler (say for synchronized methods) will
+// not introduce any loops.
+//
+void Compiler::fgCheckForLoopsInHandlers()
+{
+ // We only care about this if we are going to set OSR patchpoints
+ // and the method has exception handling.
+ //
+ if (!opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0))
+ {
+ return;
+ }
+
+ if (JitConfig.TC_OnStackReplacement() == 0)
+ {
+ return;
+ }
+
+ if (info.compXcptnsCount == 0)
+ {
+ return;
+ }
+
+ // Walk blocks in handlers and filters, looing for a backedge target.
+ //
+ assert(!compHasBackwardJumpInHandler);
+ for (BasicBlock* const blk : Blocks())
+ {
+ if (blk->hasHndIndex())
+ {
+ if (blk->bbFlags & BBF_BACKWARD_JUMP_TARGET)
+ {
+ JITDUMP("\nHander block " FMT_BB "is backward jump target; can't have patchpoints in this method\n");
+ compHasBackwardJumpInHandler = true;
+ break;
+ }
+ }
+ }
}
//------------------------------------------------------------------------
@@ -4055,9 +4111,11 @@ IL_OFFSET Compiler::fgFindBlockILOffset(BasicBlock* block)
for (Statement* const stmt : block->Statements())
{
- if (stmt->GetILOffsetX() != BAD_IL_OFFSET)
+ // Blocks always contain IL offsets in the root.
+ DebugInfo di = stmt->GetDebugInfo().GetRoot();
+ if (di.IsValid())
{
- return jitGetILoffs(stmt->GetILOffsetX());
+ return di.GetLocation().GetOffset();
}
}
@@ -4228,9 +4286,10 @@ BasicBlock* Compiler::fgSplitBlockAfterNode(BasicBlock* curr, GenTree* node)
if ((*riter)->gtOper == GT_IL_OFFSET)
{
GenTreeILOffset* ilOffset = (*riter)->AsILOffset();
- if (ilOffset->gtStmtILoffsx != BAD_IL_OFFSET)
+ DebugInfo rootDI = ilOffset->gtStmtDI.GetRoot();
+ if (rootDI.IsValid())
{
- splitPointILOffset = jitGetILoffs(ilOffset->gtStmtILoffsx);
+ splitPointILOffset = rootDI.GetLocation().GetOffset();
break;
}
}
@@ -4636,6 +4695,7 @@ void Compiler::fgRemoveBlock(BasicBlock* block, bool unreachable)
if (block->isLoopAlign())
{
+ loopAlignCandidates++;
succBlock->bbFlags |= BBF_LOOP_ALIGN;
JITDUMP("Propagating LOOP_ALIGN flag from " FMT_BB " to " FMT_BB " for loop# %d.", block->bbNum,
succBlock->bbNum, block->bbNatLoopNum);
@@ -4643,8 +4703,8 @@ void Compiler::fgRemoveBlock(BasicBlock* block, bool unreachable)
if (fgDomsComputed && fgReachable(succBlock, block))
{
- /* Mark all the reachable blocks between 'succBlock' and 'block', excluding 'block' */
- optMarkLoopBlocks(succBlock, block, true);
+ // Mark all the reachable blocks between 'succBlock' and 'bPrev'
+ optScaleLoopBlocks(succBlock, bPrev);
}
}
else if (succBlock->isLoopHead() && bPrev && (succBlock->bbNum <= bPrev->bbNum))
@@ -4652,8 +4712,6 @@ void Compiler::fgRemoveBlock(BasicBlock* block, bool unreachable)
skipUnmarkLoop = true;
}
- noway_assert(succBlock);
-
// If this is the first Cold basic block update fgFirstColdBlock
if (block == fgFirstColdBlock)
{
@@ -4696,12 +4754,6 @@ void Compiler::fgRemoveBlock(BasicBlock* block, bool unreachable)
succBlock->bbRefs++;
}
- fgUnlinkBlock(block);
-
- /* mark the block as removed and set the change flag */
-
- block->bbFlags |= BBF_REMOVED;
-
/* Update bbRefs and bbPreds.
* All blocks jumping to 'block' now jump to 'succBlock'.
* First, remove 'block' from the predecessor list of succBlock.
@@ -4791,8 +4843,14 @@ void Compiler::fgRemoveBlock(BasicBlock* block, bool unreachable)
break;
}
}
+
+ fgUnlinkBlock(block);
+ block->bbFlags |= BBF_REMOVED;
}
+ // If this was marked for alignment, remove it
+ block->unmarkLoopAlign(this DEBUG_ARG("Removed block"));
+
if (bPrev != nullptr)
{
switch (bPrev->bbJumpKind)
@@ -5551,7 +5609,17 @@ BasicBlock* Compiler::fgRelocateEHRange(unsigned regionIndex, FG_RELOCATE_TYPE r
return bLast;
}
-// return true if there is a possibility that the method has a loop (a backedge is present)
+//------------------------------------------------------------------------
+// fgMightHaveLoop: return true if there is a possibility that the method has a loop (a back edge is present).
+// This function doesn't depend on any previous loop computations, including predecessors. It looks for any
+// lexical back edge to a block previously seen in a forward walk of the block list.
+//
+// As it walks all blocks and all successors of each block (including EH successors), it is not cheap.
+// It returns as soon as any possible loop is discovered.
+//
+// Return Value:
+// true if there might be a loop
+//
bool Compiler::fgMightHaveLoop()
{
// Don't use a BlockSet for this temporary bitset of blocks: we don't want to have to call EnsureBasicBlockEpoch()
@@ -5564,7 +5632,7 @@ bool Compiler::fgMightHaveLoop()
{
BitVecOps::AddElemD(&blockVecTraits, blocksSeen, block->bbNum);
- for (BasicBlock* succ : block->GetAllSuccs(this))
+ for (BasicBlock* const succ : block->GetAllSuccs(this))
{
if (BitVecOps::IsMember(&blockVecTraits, blocksSeen, succ->bbNum))
{
diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp
index c1af1a30badfea..8fa10e2f9f18f5 100644
--- a/src/coreclr/jit/fgdiagnostic.cpp
+++ b/src/coreclr/jit/fgdiagnostic.cpp
@@ -1632,7 +1632,7 @@ bool Compiler::fgDumpFlowGraph(Phases phase, PhasePosition pos)
}
}
- // Add regions for the loops. Note that loops are assumed to be contiguous from `lpFirst` to `lpBottom`.
+ // Add regions for the loops. Note that loops are assumed to be contiguous from `lpTop` to `lpBottom`.
if (includeLoops)
{
@@ -1645,7 +1645,7 @@ bool Compiler::fgDumpFlowGraph(Phases phase, PhasePosition pos)
continue;
}
sprintf_s(name, sizeof(name), FMT_LP, loopNum);
- rgnGraph.Insert(name, RegionGraph::RegionType::Loop, loop.lpFirst, loop.lpBottom);
+ rgnGraph.Insert(name, RegionGraph::RegionType::Loop, loop.lpTop, loop.lpBottom);
}
}
@@ -2851,6 +2851,11 @@ void Compiler::fgDebugCheckFlags(GenTree* tree)
chkFlags |= GTF_EXCEPT;
}
+ if (tree->OperRequiresAsgFlag())
+ {
+ chkFlags |= GTF_ASG;
+ }
+
if (tree->OperRequiresCallFlag(this))
{
chkFlags |= GTF_CALL;
@@ -2931,31 +2936,6 @@ void Compiler::fgDebugCheckFlags(GenTree* tree)
}
break;
- case GT_LIST:
- if ((op2 != nullptr) && op2->OperIsAnyList())
- {
- ArrayStack stack(getAllocator(CMK_DebugOnly));
- while ((tree->gtGetOp2() != nullptr) && tree->gtGetOp2()->OperIsAnyList())
- {
- stack.Push(tree);
- tree = tree->gtGetOp2();
- }
-
- fgDebugCheckFlags(tree);
-
- while (!stack.Empty())
- {
- tree = stack.Pop();
- assert((tree->gtFlags & GTF_REVERSE_OPS) == 0);
- fgDebugCheckFlags(tree->AsOp()->gtOp1);
- chkFlags |= (tree->AsOp()->gtOp1->gtFlags & GTF_ALL_EFFECT);
- chkFlags |= (tree->gtGetOp2()->gtFlags & GTF_ALL_EFFECT);
- fgDebugCheckFlagsHelper(tree, (tree->gtFlags & GTF_ALL_EFFECT), chkFlags);
- }
-
- return;
- }
- break;
case GT_ADDR:
assert(!op1->CanCSE());
break;
@@ -2967,7 +2947,7 @@ void Compiler::fgDebugCheckFlags(GenTree* tree)
{
// Is this constant a handle of some kind?
//
- unsigned handleKind = (op1->gtFlags & GTF_ICON_HDL_MASK);
+ GenTreeFlags handleKind = (op1->gtFlags & GTF_ICON_HDL_MASK);
if (handleKind != 0)
{
// Is the GTF_IND_INVARIANT flag set or unset?
@@ -3097,11 +3077,6 @@ void Compiler::fgDebugCheckFlags(GenTree* tree)
*/
}
- if (tree->OperRequiresAsgFlag())
- {
- chkFlags |= GTF_ASG;
- }
-
if (oper == GT_ADDR && (op1->OperIsLocal() || op1->gtOper == GT_CLS_VAR ||
(op1->gtOper == GT_IND && op1->AsOp()->gtOp1->gtOper == GT_CLS_VAR_ADDR)))
{
@@ -3130,6 +3105,8 @@ void Compiler::fgDebugCheckFlags(GenTree* tree)
if ((call->gtCallThisArg->GetNode()->gtFlags & GTF_ASG) != 0)
{
+ // TODO-Cleanup: this is a patch for a violation in our GT_ASG propagation
+ // see https://github.com/dotnet/runtime/issues/13758
treeFlags |= GTF_ASG;
}
}
@@ -3142,6 +3119,8 @@ void Compiler::fgDebugCheckFlags(GenTree* tree)
if ((use.GetNode()->gtFlags & GTF_ASG) != 0)
{
+ // TODO-Cleanup: this is a patch for a violation in our GT_ASG propagation
+ // see https://github.com/dotnet/runtime/issues/13758
treeFlags |= GTF_ASG;
}
}
@@ -3191,6 +3170,23 @@ void Compiler::fgDebugCheckFlags(GenTree* tree)
}
break;
+#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+#if defined(FEATURE_SIMD)
+ case GT_SIMD:
+#endif
+#if defined(FEATURE_HW_INTRINSICS)
+ case GT_HWINTRINSIC:
+#endif
+ // TODO-List-Cleanup: consider using the general Operands() iterator
+ // here for the "special" nodes to reduce code duplication.
+ for (GenTree* operand : tree->AsMultiOp()->Operands())
+ {
+ fgDebugCheckFlags(operand);
+ chkFlags |= (operand->gtFlags & GTF_ALL_EFFECT);
+ }
+ break;
+#endif // defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+
case GT_ARR_ELEM:
GenTree* arrObj;
diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp
index 9b2e4080a62a2d..cf73fa123e6fd9 100644
--- a/src/coreclr/jit/fginline.cpp
+++ b/src/coreclr/jit/fginline.cpp
@@ -29,11 +29,12 @@
unsigned Compiler::fgCheckInlineDepthAndRecursion(InlineInfo* inlineInfo)
{
BYTE* candidateCode = inlineInfo->inlineCandidateInfo->methInfo.ILCode;
- InlineContext* inlineContext = inlineInfo->iciStmt->GetInlineContext();
+ InlineContext* inlineContext = inlineInfo->inlineCandidateInfo->inlinersContext;
InlineResult* inlineResult = inlineInfo->inlineResult;
// There should be a context for all candidates.
assert(inlineContext != nullptr);
+
int depth = 0;
for (; inlineContext != nullptr; inlineContext = inlineContext->GetParent())
@@ -101,17 +102,6 @@ PhaseStatus Compiler::fgInline()
noway_assert(fgFirstBB != nullptr);
- // Set the root inline context on all statements
- InlineContext* rootContext = m_inlineStrategy->GetRootContext();
-
- for (BasicBlock* const block : Blocks())
- {
- for (Statement* const stmt : block->Statements())
- {
- stmt->SetInlineContext(rootContext);
- }
- }
-
BasicBlock* block = fgFirstBB;
bool madeChanges = false;
@@ -123,7 +113,7 @@ PhaseStatus Compiler::fgInline()
for (Statement* const stmt : block->Statements())
{
-#ifdef DEBUG
+#if defined(DEBUG) || defined(INLINE_DATA)
// In debug builds we want the inline tree to show all failed
// inlines. Some inlines may fail very early and never make it to
// candidate stage. So scan the tree looking for those early failures.
@@ -226,7 +216,7 @@ PhaseStatus Compiler::fgInline()
{
JITDUMP("**************** Inline Tree");
printf("\n");
- m_inlineStrategy->Dump(verbose);
+ m_inlineStrategy->Dump(verbose || JitConfig.JitPrintInlinedMethodsVerbose());
}
#endif // DEBUG
@@ -234,7 +224,7 @@ PhaseStatus Compiler::fgInline()
return madeChanges ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING;
}
-#ifdef DEBUG
+#if defined(DEBUG) || defined(INLINE_DATA)
//------------------------------------------------------------------------
// fgFindNonInlineCandidate: tree walk helper to ensure that a tree node
@@ -283,7 +273,7 @@ void Compiler::fgNoteNonInlineCandidate(Statement* stmt, GenTreeCall* call)
return;
}
- InlineResult inlineResult(this, call, nullptr, "fgNotInlineCandidate");
+ InlineResult inlineResult(this, call, nullptr, "fgNoteNonInlineCandidate");
InlineObservation currentObservation = InlineObservation::CALLSITE_NOT_CANDIDATE;
// Try and recover the reason left behind when the jit decided
@@ -301,8 +291,7 @@ void Compiler::fgNoteNonInlineCandidate(Statement* stmt, GenTreeCall* call)
if (call->gtCallType == CT_USER_FUNC)
{
- // Create InlineContext for the failure
- m_inlineStrategy->NewFailure(stmt, &inlineResult);
+ m_inlineStrategy->NewContext(call->gtInlineContext, stmt, call)->SetFailed(&inlineResult);
}
}
@@ -863,7 +852,7 @@ Compiler::fgWalkResult Compiler::fgDebugCheckInlineCandidates(GenTree** pTree, f
#endif // DEBUG
-void Compiler::fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* inlineResult)
+void Compiler::fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* inlineResult, InlineContext** createdContext)
{
noway_assert(call->gtOper == GT_CALL);
noway_assert((call->gtFlags & GTF_CALL_INLINE_CANDIDATE) != 0);
@@ -945,6 +934,14 @@ void Compiler::fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* inlineRe
{
pParam->inlineInfo->InlineRoot = pParam->pThis->impInlineInfo->InlineRoot;
}
+
+ // The inline context is part of debug info and must be created
+ // before we start creating statements; we lazily create it as
+ // late as possible, which is here.
+ pParam->inlineInfo->inlineContext =
+ pParam->inlineInfo->InlineRoot->m_inlineStrategy
+ ->NewContext(pParam->inlineInfo->inlineCandidateInfo->inlinersContext,
+ pParam->inlineInfo->iciStmt, pParam->inlineInfo->iciCall);
pParam->inlineInfo->argCnt = pParam->inlineCandidateInfo->methInfo.args.totalILArgs();
pParam->inlineInfo->tokenLookupContextHandle = pParam->inlineCandidateInfo->exactContextHnd;
@@ -960,7 +957,6 @@ void Compiler::fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* inlineRe
compileFlagsForInlinee.Clear(JitFlags::JIT_FLAG_BBINSTR);
compileFlagsForInlinee.Clear(JitFlags::JIT_FLAG_PROF_ENTERLEAVE);
compileFlagsForInlinee.Clear(JitFlags::JIT_FLAG_DEBUG_EnC);
- compileFlagsForInlinee.Clear(JitFlags::JIT_FLAG_DEBUG_INFO);
compileFlagsForInlinee.Clear(JitFlags::JIT_FLAG_REVERSE_PINVOKE);
compileFlagsForInlinee.Clear(JitFlags::JIT_FLAG_TRACK_TRANSITIONS);
@@ -1011,6 +1007,8 @@ void Compiler::fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* inlineRe
}
}
+ *createdContext = inlineInfo.inlineContext;
+
if (inlineResult->IsFailure())
{
return;
@@ -1117,16 +1115,8 @@ void Compiler::fgInsertInlineeBlocks(InlineInfo* pInlineInfo)
#endif // DEBUG
- // Create a new inline context and mark the inlined statements with it
- InlineContext* calleeContext = m_inlineStrategy->NewSuccess(pInlineInfo);
-
- for (BasicBlock* const block : InlineeCompiler->Blocks())
- {
- for (Statement* const stmt : block->Statements())
- {
- stmt->SetInlineContext(calleeContext);
- }
- }
+ // Mark success.
+ pInlineInfo->inlineContext->SetSucceeded(pInlineInfo);
// Prepend statements
Statement* stmtAfter = fgInlinePrependStatements(pInlineInfo);
@@ -1281,9 +1271,10 @@ void Compiler::fgInsertInlineeBlocks(InlineInfo* pInlineInfo)
block->copyEHRegion(iciBlock);
block->bbFlags |= iciBlock->bbFlags & BBF_BACKWARD_JUMP;
- if (iciStmt->GetILOffsetX() != BAD_IL_OFFSET)
+ DebugInfo di = iciStmt->GetDebugInfo().GetRoot();
+ if (di.IsValid())
{
- block->bbCodeOffs = jitGetILoffs(iciStmt->GetILOffsetX());
+ block->bbCodeOffs = di.GetLocation().GetOffset();
block->bbCodeOffsEnd = block->bbCodeOffs + 1; // TODO: is code size of 1 some magic number for inlining?
}
else
@@ -1469,13 +1460,13 @@ void Compiler::fgInsertInlineeBlocks(InlineInfo* pInlineInfo)
Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo)
{
- BasicBlock* block = inlineInfo->iciBlock;
- Statement* callStmt = inlineInfo->iciStmt;
- IL_OFFSETX callILOffset = callStmt->GetILOffsetX();
- Statement* postStmt = callStmt->GetNextStmt();
- Statement* afterStmt = callStmt; // afterStmt is the place where the new statements should be inserted after.
- Statement* newStmt = nullptr;
- GenTreeCall* call = inlineInfo->iciCall->AsCall();
+ BasicBlock* block = inlineInfo->iciBlock;
+ Statement* callStmt = inlineInfo->iciStmt;
+ const DebugInfo& callDI = callStmt->GetDebugInfo();
+ Statement* postStmt = callStmt->GetNextStmt();
+ Statement* afterStmt = callStmt; // afterStmt is the place where the new statements should be inserted after.
+ Statement* newStmt = nullptr;
+ GenTreeCall* call = inlineInfo->iciCall->AsCall();
noway_assert(call->gtOper == GT_CALL);
@@ -1589,8 +1580,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo)
// argTmpNum here since in-linee compiler instance
// would have iterated over these and marked them
// accordingly.
- impAssignTempGen(tmpNum, argNode, structHnd, (unsigned)CHECK_SPILL_NONE, &afterStmt, callILOffset,
- block);
+ impAssignTempGen(tmpNum, argNode, structHnd, (unsigned)CHECK_SPILL_NONE, &afterStmt, callDI, block);
// We used to refine the temp type here based on
// the actual arg, but we now do this up front, when
@@ -1639,7 +1629,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo)
// Don't put GT_OBJ node under a GT_COMMA.
// Codegen can't deal with it.
// Just hang the address here in case there are side-effect.
- newStmt = gtNewStmt(gtUnusedValNode(argNode->AsOp()->gtOp1), callILOffset);
+ newStmt = gtNewStmt(gtUnusedValNode(argNode->AsOp()->gtOp1), callDI);
}
else
{
@@ -1715,7 +1705,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo)
// just append the arg node as an unused value.
if (newStmt == nullptr)
{
- newStmt = gtNewStmt(gtUnusedValNode(argNode), callILOffset);
+ newStmt = gtNewStmt(gtUnusedValNode(argNode), callDI);
}
fgInsertStmtAfter(block, afterStmt, newStmt);
@@ -1751,7 +1741,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo)
CORINFO_CLASS_HANDLE exactClass = eeGetClassFromContext(inlineInfo->inlineCandidateInfo->exactContextHnd);
tree = fgGetSharedCCtor(exactClass);
- newStmt = gtNewStmt(tree, callILOffset);
+ newStmt = gtNewStmt(tree, callDI);
fgInsertStmtAfter(block, afterStmt, newStmt);
afterStmt = newStmt;
}
@@ -1759,7 +1749,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo)
// Insert the nullcheck statement now.
if (nullcheck)
{
- newStmt = gtNewStmt(nullcheck, callILOffset);
+ newStmt = gtNewStmt(nullcheck, callDI);
fgInsertStmtAfter(block, afterStmt, newStmt);
afterStmt = newStmt;
}
@@ -1812,7 +1802,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo)
// Unsafe value cls check is not needed here since in-linee compiler instance would have
// iterated over locals and marked accordingly.
impAssignTempGen(tmpNum, gtNewZeroConNode(genActualType(lclTyp)), NO_CLASS_HANDLE,
- (unsigned)CHECK_SPILL_NONE, &afterStmt, callILOffset, block);
+ (unsigned)CHECK_SPILL_NONE, &afterStmt, callDI, block);
}
else
{
@@ -1821,7 +1811,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo)
false, // isVolatile
false); // not copyBlock
- newStmt = gtNewStmt(tree, callILOffset);
+ newStmt = gtNewStmt(tree, callDI);
fgInsertStmtAfter(block, afterStmt, newStmt);
afterStmt = newStmt;
}
@@ -1836,15 +1826,6 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo)
}
}
- // Update any newly added statements with the appropriate context.
- InlineContext* context = callStmt->GetInlineContext();
- assert(context != nullptr);
- for (Statement* addedStmt = callStmt->GetNextStmt(); addedStmt != postStmt; addedStmt = addedStmt->GetNextStmt())
- {
- assert(addedStmt->GetInlineContext() == nullptr);
- addedStmt->SetInlineContext(context);
- }
-
return afterStmt;
}
@@ -1881,7 +1862,7 @@ void Compiler::fgInlineAppendStatements(InlineInfo* inlineInfo, BasicBlock* bloc
JITDUMP("fgInlineAppendStatements: nulling out gc ref inlinee locals.\n");
Statement* callStmt = inlineInfo->iciStmt;
- IL_OFFSETX callILOffset = callStmt->GetILOffsetX();
+ const DebugInfo& callDI = callStmt->GetDebugInfo();
CORINFO_METHOD_INFO* InlineeMethodInfo = InlineeCompiler->info.compMethodInfo;
const unsigned lclCnt = InlineeMethodInfo->locals.numArgs;
InlLclVarInfo* lclVarInfo = inlineInfo->lclVarInfo;
@@ -1930,7 +1911,7 @@ void Compiler::fgInlineAppendStatements(InlineInfo* inlineInfo, BasicBlock* bloc
// Assign null to the local.
GenTree* nullExpr = gtNewTempAssign(tmpNum, gtNewZeroConNode(lclTyp));
- Statement* nullStmt = gtNewStmt(nullExpr, callILOffset);
+ Statement* nullStmt = gtNewStmt(nullExpr, callDI);
if (stmtAfter == nullptr)
{
diff --git a/src/coreclr/jit/fgopt.cpp b/src/coreclr/jit/fgopt.cpp
index 59f326b0fc13a4..39e1dae21e3519 100644
--- a/src/coreclr/jit/fgopt.cpp
+++ b/src/coreclr/jit/fgopt.cpp
@@ -364,17 +364,11 @@ void Compiler::fgComputeEnterBlocksSet()
// Assumptions:
// The reachability sets must be computed and valid.
//
-// Notes:
-// Sets `fgHasLoops` if there are any loops in the function.
-// Sets `BBF_LOOP_HEAD` flag on a block if that block is the target of a backward branch and the block can
-// reach the source of the branch.
-//
bool Compiler::fgRemoveUnreachableBlocks()
{
assert(!fgCheapPredsValid);
assert(fgReachabilitySetsValid);
- bool hasLoops = false;
bool hasUnreachableBlocks = false;
bool changed = false;
@@ -384,7 +378,7 @@ bool Compiler::fgRemoveUnreachableBlocks()
/* Internal throw blocks are also reachable */
if (fgIsThrowHlpBlk(block))
{
- goto SKIP_BLOCK;
+ continue;
}
else if (block == genReturnBB)
{
@@ -392,20 +386,20 @@ bool Compiler::fgRemoveUnreachableBlocks()
// For example, in VSW 364383,
// the profiler hookup needs to have the "void GT_RETURN" statement
// to properly set the info.compProfilerCallback flag.
- goto SKIP_BLOCK;
+ continue;
}
else
{
// If any of the entry blocks can reach this block, then we skip it.
if (!BlockSetOps::IsEmptyIntersection(this, fgEnterBlks, block->bbReach))
{
- goto SKIP_BLOCK;
+ continue;
}
#if defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
if (!BlockSetOps::IsEmptyIntersection(this, fgAlwaysBlks, block->bbReach))
{
- goto SKIP_BLOCK;
+ continue;
}
#endif // defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
}
@@ -451,54 +445,22 @@ bool Compiler::fgRemoveUnreachableBlocks()
hasUnreachableBlocks = true;
changed = true;
}
- continue;
-
- SKIP_BLOCK:;
-
- if (block->bbJumpKind == BBJ_RETURN)
- {
- continue;
- }
-
- // Set BBF_LOOP_HEAD if we have backwards branches to this block.
-
- unsigned blockNum = block->bbNum;
- for (BasicBlock* const predBlock : block->PredBlocks())
- {
- if (blockNum <= predBlock->bbNum)
- {
- if (predBlock->bbJumpKind == BBJ_CALLFINALLY)
- {
- continue;
- }
-
- /* If block can reach predBlock then we have a loop head */
- if (BlockSetOps::IsMember(this, predBlock->bbReach, blockNum))
- {
- hasLoops = true;
-
- /* Set the BBF_LOOP_HEAD flag */
- block->bbFlags |= BBF_LOOP_HEAD;
- break;
- }
- }
- }
}
- fgHasLoops = hasLoops;
-
if (hasUnreachableBlocks)
{
// Now remove the unreachable blocks
for (BasicBlock* block = fgFirstBB; block != nullptr; block = block->bbNext)
{
- // If we mark the block with BBF_REMOVED then
- // we need to call fgRemovedBlock() on it
+ // If we marked a block with BBF_REMOVED then we need to call fgRemoveBlock() on it
if (block->bbFlags & BBF_REMOVED)
{
fgRemoveBlock(block, true);
+ // TODO: couldn't we have fgRemoveBlock() return the block after the (last)one removed
+ // so we don't need the code below?
+
// When we have a BBJ_CALLFINALLY, BBJ_ALWAYS pair; fgRemoveBlock will remove
// both blocks, so we must advance 1 extra place in the block list
//
@@ -521,9 +483,6 @@ bool Compiler::fgRemoveUnreachableBlocks()
// Also, compute the list of return blocks `fgReturnBlocks` and set of enter blocks `fgEnterBlks`.
// Delete unreachable blocks.
//
-// Via the call to `fgRemoveUnreachableBlocks`, determine if the flow graph has loops and set 'fgHasLoops'
-// accordingly. Set the BBF_LOOP_HEAD flag on the block target of backwards branches.
-//
// Assumptions:
// Assumes the predecessor lists are computed and correct.
//
@@ -591,8 +550,6 @@ void Compiler::fgComputeReachability()
//
// Use reachability information to delete unreachable blocks.
- // Also, determine if the flow graph has loops and set 'fgHasLoops' accordingly.
- // Set the BBF_LOOP_HEAD flag on the block target of backwards branches.
//
changed = fgRemoveUnreachableBlocks();
@@ -1553,8 +1510,20 @@ void Compiler::fgPostImportationCleanup()
//
auto addConditionalFlow = [this, entryStateVar, &entryJumpTarget](BasicBlock* fromBlock,
BasicBlock* toBlock) {
- fgSplitBlockAtBeginning(fromBlock);
+
+ // We may have previously though this try entry was unreachable, but now we're going to
+ // step through it on the way to the OSR entry. So ensure it has plausible profile weight.
+ //
+ if (fgHaveProfileData() && !fromBlock->hasProfileWeight())
+ {
+ JITDUMP("Updating block weight for now-reachable try entry " FMT_BB " via " FMT_BB "\n",
+ fromBlock->bbNum, fgFirstBB->bbNum);
+ fromBlock->inheritWeight(fgFirstBB);
+ }
+
+ BasicBlock* const newBlock = fgSplitBlockAtBeginning(fromBlock);
fromBlock->bbFlags |= BBF_INTERNAL;
+ newBlock->bbFlags &= ~BBF_DONT_REMOVE;
GenTree* const entryStateLcl = gtNewLclvNode(entryStateVar, TYP_INT);
GenTree* const compareEntryStateToZero =
@@ -1565,6 +1534,7 @@ void Compiler::fgPostImportationCleanup()
fromBlock->bbJumpKind = BBJ_COND;
fromBlock->bbJumpDest = toBlock;
fgAddRefPred(toBlock, fromBlock);
+ newBlock->inheritWeight(fromBlock);
entryJumpTarget = fromBlock;
};
@@ -2239,13 +2209,6 @@ void Compiler::fgUpdateLoopsAfterCompacting(BasicBlock* block, BasicBlock* bNext
optLoopTable[loopNum].lpEntry = block;
}
- /* Check the loop's first block */
-
- if (optLoopTable[loopNum].lpFirst == bNext)
- {
- optLoopTable[loopNum].lpFirst = block;
- }
-
/* Check the loop top */
if (optLoopTable[loopNum].lpTop == bNext)
@@ -3220,7 +3183,9 @@ bool Compiler::fgBlockIsGoodTailDuplicationCandidate(BasicBlock* target, unsigne
{
*lclNum = BAD_VAR_NUM;
- // Here we are looking for blocks with a single statement feeding a conditional branch.
+ // Here we are looking for small blocks where a local live-into the block
+ // ultimately feeds a simple conditional branch.
+ //
// These blocks are small, and when duplicated onto the tail of blocks that end in
// assignments, there is a high probability of the branch completely going away.
//
@@ -3237,22 +3202,27 @@ bool Compiler::fgBlockIsGoodTailDuplicationCandidate(BasicBlock* target, unsigne
return false;
}
- Statement* stmt = target->FirstNonPhiDef();
+ Statement* const lastStmt = target->lastStmt();
+ Statement* const firstStmt = target->FirstNonPhiDef();
- if (stmt != target->lastStmt())
+ // We currently allow just one statement aside from the branch.
+ //
+ if ((firstStmt != lastStmt) && (firstStmt != lastStmt->GetPrevStmt()))
{
return false;
}
- GenTree* tree = stmt->GetRootNode();
+ // Verify the branch is just a simple local compare.
+ //
+ GenTree* const lastTree = lastStmt->GetRootNode();
- if (tree->gtOper != GT_JTRUE)
+ if (lastTree->gtOper != GT_JTRUE)
{
return false;
}
// must be some kind of relational operator
- GenTree* const cond = tree->AsOp()->gtOp1;
+ GenTree* const cond = lastTree->AsOp()->gtOp1;
if (!cond->OperIsCompare())
{
return false;
@@ -3314,6 +3284,109 @@ bool Compiler::fgBlockIsGoodTailDuplicationCandidate(BasicBlock* target, unsigne
return false;
}
+ // If there's no second statement, we're good.
+ //
+ if (firstStmt == lastStmt)
+ {
+ return true;
+ }
+
+ // Otherwise check the first stmt.
+ // Verify the branch is just a simple local compare.
+ //
+ GenTree* const firstTree = firstStmt->GetRootNode();
+
+ if (firstTree->gtOper != GT_ASG)
+ {
+ return false;
+ }
+
+ GenTree* const lhs = firstTree->AsOp()->gtOp1;
+ if (!lhs->OperIs(GT_LCL_VAR))
+ {
+ return false;
+ }
+
+ const unsigned lhsLcl = lhs->AsLclVarCommon()->GetLclNum();
+ if (lhsLcl != *lclNum)
+ {
+ return false;
+ }
+
+ // Could allow unary here too...
+ //
+ GenTree* const rhs = firstTree->AsOp()->gtOp2;
+ if (!rhs->OperIsBinary())
+ {
+ return false;
+ }
+
+ // op1 must be some combinations of casts of local or constant
+ // (or unary)
+ op1 = rhs->AsOp()->gtOp1;
+ while (op1->gtOper == GT_CAST)
+ {
+ op1 = op1->AsOp()->gtOp1;
+ }
+
+ if (!op1->IsLocal() && !op1->OperIsConst())
+ {
+ return false;
+ }
+
+ // op2 must be some combinations of casts of local or constant
+ // (or unary)
+ op2 = rhs->AsOp()->gtOp2;
+
+ // A binop may not actually have an op2.
+ //
+ if (op2 == nullptr)
+ {
+ return false;
+ }
+
+ while (op2->gtOper == GT_CAST)
+ {
+ op2 = op2->AsOp()->gtOp1;
+ }
+
+ if (!op2->IsLocal() && !op2->OperIsConst())
+ {
+ return false;
+ }
+
+ // Tree must have one constant and one local, or be comparing
+ // the same local to itself.
+ lcl1 = BAD_VAR_NUM;
+ lcl2 = BAD_VAR_NUM;
+
+ if (op1->IsLocal())
+ {
+ lcl1 = op1->AsLclVarCommon()->GetLclNum();
+ }
+
+ if (op2->IsLocal())
+ {
+ lcl2 = op2->AsLclVarCommon()->GetLclNum();
+ }
+
+ if ((lcl1 != BAD_VAR_NUM) && op2->OperIsConst())
+ {
+ *lclNum = lcl1;
+ }
+ else if ((lcl2 != BAD_VAR_NUM) && op1->OperIsConst())
+ {
+ *lclNum = lcl2;
+ }
+ else if ((lcl1 != BAD_VAR_NUM) && (lcl1 == lcl2))
+ {
+ *lclNum = lcl1;
+ }
+ else
+ {
+ return false;
+ }
+
return true;
}
@@ -3333,6 +3406,8 @@ bool Compiler::fgBlockIsGoodTailDuplicationCandidate(BasicBlock* target, unsigne
//
bool Compiler::fgOptimizeUncondBranchToSimpleCond(BasicBlock* block, BasicBlock* target)
{
+ JITDUMP("Considering uncond to cond " FMT_BB " -> " FMT_BB "\n", block->bbNum, target->bbNum);
+
if (!BasicBlock::sameEHRegion(block, target))
{
return false;
@@ -3360,23 +3435,35 @@ bool Compiler::fgOptimizeUncondBranchToSimpleCond(BasicBlock* block, BasicBlock*
// backend always calls `fgUpdateFlowGraph` with `doTailDuplication` set to false.
assert(!block->IsLIR());
- Statement* stmt = target->FirstNonPhiDef();
- assert(stmt == target->lastStmt());
-
// Duplicate the target block at the end of this block
- GenTree* cloned = gtCloneExpr(stmt->GetRootNode());
- noway_assert(cloned);
- Statement* jmpStmt = gtNewStmt(cloned);
+ //
+ for (Statement* stmt : target->NonPhiStatements())
+ {
+ GenTree* clone = gtCloneExpr(stmt->GetRootNode());
+ noway_assert(clone);
+ Statement* cloneStmt = gtNewStmt(clone);
+
+ if (fgStmtListThreaded)
+ {
+ gtSetStmtInfo(cloneStmt);
+ }
+
+ fgInsertStmtAtEnd(block, cloneStmt);
+ }
+ // Fix up block's flow
+ //
block->bbJumpKind = BBJ_COND;
block->bbJumpDest = target->bbJumpDest;
fgAddRefPred(block->bbJumpDest, block);
fgRemoveRefPred(target, block);
// add an unconditional block after this block to jump to the target block's fallthrough block
+ //
BasicBlock* next = fgNewBBafter(BBJ_ALWAYS, block, true);
// The new block 'next' will inherit its weight from 'block'
+ //
next->inheritWeight(block);
next->bbJumpDest = target->bbNext;
fgAddRefPred(next, block);
@@ -3384,15 +3471,7 @@ bool Compiler::fgOptimizeUncondBranchToSimpleCond(BasicBlock* block, BasicBlock*
JITDUMP("fgOptimizeUncondBranchToSimpleCond(from " FMT_BB " to cond " FMT_BB "), created new uncond " FMT_BB "\n",
block->bbNum, target->bbNum, next->bbNum);
- JITDUMP(" expecting opts to key off V%02u, added cloned compare [%06u] to " FMT_BB "\n", lclNum,
- dspTreeID(cloned), block->bbNum);
-
- if (fgStmtListThreaded)
- {
- gtSetStmtInfo(jmpStmt);
- }
-
- fgInsertStmtAtEnd(block, jmpStmt);
+ JITDUMP(" expecting opts to key off V%02u in " FMT_BB "\n", lclNum, block->bbNum);
return true;
}
@@ -3953,7 +4032,7 @@ bool Compiler::fgOptimizeSwitchJumps()
//
GenTree* const dominantCaseCompare = gtNewOperNode(GT_EQ, TYP_INT, switchValue, gtNewIconNode(dominantCase));
GenTree* const jmpTree = gtNewOperNode(GT_JTRUE, TYP_VOID, dominantCaseCompare);
- Statement* const jmpStmt = fgNewStmtFromTree(jmpTree, switchStmt->GetILOffsetX());
+ Statement* const jmpStmt = fgNewStmtFromTree(jmpTree, switchStmt->GetDebugInfo());
fgInsertStmtAtEnd(block, jmpStmt);
// Reattach switch value to the switch. This may introduce a comma
@@ -5855,6 +5934,9 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication)
// Update the loop table if we removed the bottom of a loop, for example.
fgUpdateLoopsAfterCompacting(block, bNext);
+ // If this block was aligned, unmark it
+ bNext->unmarkLoopAlign(this DEBUG_ARG("Optimized jump"));
+
// If this is the first Cold basic block update fgFirstColdBlock
if (bNext == fgFirstColdBlock)
{
diff --git a/src/coreclr/jit/fgprofile.cpp b/src/coreclr/jit/fgprofile.cpp
index d301166966fbfa..25ec5f0c877253 100644
--- a/src/coreclr/jit/fgprofile.cpp
+++ b/src/coreclr/jit/fgprofile.cpp
@@ -300,10 +300,10 @@ class Instrumentor
virtual void BuildSchemaElements(BasicBlock* block, Schema& schema)
{
}
- virtual void Instrument(BasicBlock* block, Schema& schema, BYTE* profileMemory)
+ virtual void Instrument(BasicBlock* block, Schema& schema, uint8_t* profileMemory)
{
}
- virtual void InstrumentMethodEntry(Schema& schema, BYTE* profileMemory)
+ virtual void InstrumentMethodEntry(Schema& schema, uint8_t* profileMemory)
{
}
virtual void SuppressProbes()
@@ -349,8 +349,8 @@ class BlockCountInstrumentor : public Instrumentor
}
void Prepare(bool isPreImport) override;
void BuildSchemaElements(BasicBlock* block, Schema& schema) override;
- void Instrument(BasicBlock* block, Schema& schema, BYTE* profileMemory) override;
- void InstrumentMethodEntry(Schema& schema, BYTE* profileMemory) override;
+ void Instrument(BasicBlock* block, Schema& schema, uint8_t* profileMemory) override;
+ void InstrumentMethodEntry(Schema& schema, uint8_t* profileMemory) override;
};
//------------------------------------------------------------------------
@@ -428,7 +428,7 @@ void BlockCountInstrumentor::BuildSchemaElements(BasicBlock* block, Schema& sche
// schema -- instrumentation schema
// profileMemory -- profile data slab
//
-void BlockCountInstrumentor::Instrument(BasicBlock* block, Schema& schema, BYTE* profileMemory)
+void BlockCountInstrumentor::Instrument(BasicBlock* block, Schema& schema, uint8_t* profileMemory)
{
const ICorJitInfo::PgoInstrumentationSchema& entry = schema[block->bbCountSchemaIndex];
@@ -464,7 +464,7 @@ void BlockCountInstrumentor::Instrument(BasicBlock* block, Schema& schema, BYTE*
// Notes:
// When prejitting, add the method entry callback node
//
-void BlockCountInstrumentor::InstrumentMethodEntry(Schema& schema, BYTE* profileMemory)
+void BlockCountInstrumentor::InstrumentMethodEntry(Schema& schema, uint8_t* profileMemory)
{
Compiler::Options& opts = m_comp->opts;
Compiler::Info& info = m_comp->info;
@@ -902,6 +902,26 @@ void Compiler::WalkSpanningTree(SpanningTreeVisitor* visitor)
}
}
+// Map a block into its schema key we will use for storing basic blocks.
+//
+static int32_t EfficientEdgeCountBlockToKey(BasicBlock* block)
+{
+ static const int IS_INTERNAL_BLOCK = (int32_t)0x80000000;
+ int32_t key = (int32_t)block->bbCodeOffs;
+ // We may see empty BBJ_NONE BBF_INTERNAL blocks that were added
+ // by fgNormalizeEH.
+ //
+ // We'll use their bbNum in place of IL offset, and set
+ // a high bit as a "flag"
+ //
+ if ((block->bbFlags & BBF_INTERNAL) == BBF_INTERNAL)
+ {
+ key = block->bbNum | IS_INTERNAL_BLOCK;
+ }
+
+ return key;
+}
+
//------------------------------------------------------------------------
// EfficientEdgeCountInstrumentor: instrumentor that adds a counter to
// selective edges.
@@ -982,7 +1002,7 @@ class EfficientEdgeCountInstrumentor : public Instrumentor, public SpanningTreeV
return ((block->bbFlags & BBF_IMPORTED) == BBF_IMPORTED);
}
void BuildSchemaElements(BasicBlock* block, Schema& schema) override;
- void Instrument(BasicBlock* block, Schema& schema, BYTE* profileMemory) override;
+ void Instrument(BasicBlock* block, Schema& schema, uint8_t* profileMemory) override;
void Badcode() override
{
@@ -1086,35 +1106,19 @@ void EfficientEdgeCountInstrumentor::BuildSchemaElements(BasicBlock* block, Sche
assert(probe->schemaIndex == -1);
probe->schemaIndex = (int)schema.size();
- // Assign the current block's IL offset into the profile data.
- // Use the "other" field to hold the target block IL offset.
- //
- int32_t sourceOffset = (int32_t)block->bbCodeOffs;
- int32_t targetOffset = (int32_t)target->bbCodeOffs;
-
- // We may see empty BBJ_NONE BBF_INTERNAL blocks that were added
- // by fgNormalizeEH.
+ // Normally we use the the offset of the block in the schema, but for certain
+ // blocks we do not have any information we can use and need to use internal BB numbers.
//
- // We'll use their bbNum in place of IL offset, and set
- // a high bit as a "flag"
- //
- if ((block->bbFlags & BBF_INTERNAL) == BBF_INTERNAL)
- {
- sourceOffset = block->bbNum | IL_OFFSETX_CALLINSTRUCTIONBIT;
- }
-
- if ((target->bbFlags & BBF_INTERNAL) == BBF_INTERNAL)
- {
- targetOffset = target->bbNum | IL_OFFSETX_CALLINSTRUCTIONBIT;
- }
+ int32_t sourceKey = EfficientEdgeCountBlockToKey(block);
+ int32_t targetKey = EfficientEdgeCountBlockToKey(target);
ICorJitInfo::PgoInstrumentationSchema schemaElem;
schemaElem.Count = 1;
- schemaElem.Other = targetOffset;
+ schemaElem.Other = targetKey;
schemaElem.InstrumentationKind = JitConfig.JitCollect64BitCounts()
? ICorJitInfo::PgoInstrumentationKind::EdgeLongCount
: ICorJitInfo::PgoInstrumentationKind::EdgeIntCount;
- schemaElem.ILOffset = sourceOffset;
+ schemaElem.ILOffset = sourceKey;
schemaElem.Offset = 0;
schema.push_back(schemaElem);
@@ -1132,7 +1136,7 @@ void EfficientEdgeCountInstrumentor::BuildSchemaElements(BasicBlock* block, Sche
// schema -- instrumentation schema
// profileMemory -- profile data slab
//
-void EfficientEdgeCountInstrumentor::Instrument(BasicBlock* block, Schema& schema, BYTE* profileMemory)
+void EfficientEdgeCountInstrumentor::Instrument(BasicBlock* block, Schema& schema, uint8_t* profileMemory)
{
// Inlinee compilers build their blocks in the root compiler's
// graph. So for NumSucc, we use the root compiler instance.
@@ -1287,7 +1291,7 @@ class BuildClassProbeSchemaGen
schemaElem.InstrumentationKind = JitConfig.JitCollect64BitCounts()
? ICorJitInfo::PgoInstrumentationKind::TypeHandleHistogramLongCount
: ICorJitInfo::PgoInstrumentationKind::TypeHandleHistogramIntCount;
- schemaElem.ILOffset = jitGetILoffs(call->gtClassProfileCandidateInfo->ilOffset);
+ schemaElem.ILOffset = (int32_t)call->gtClassProfileCandidateInfo->ilOffset;
schemaElem.Offset = 0;
m_schema.push_back(schemaElem);
@@ -1307,12 +1311,12 @@ class BuildClassProbeSchemaGen
class ClassProbeInserter
{
Schema& m_schema;
- BYTE* m_profileMemory;
+ uint8_t* m_profileMemory;
int* m_currentSchemaIndex;
unsigned& m_instrCount;
public:
- ClassProbeInserter(Schema& schema, BYTE* profileMemory, int* pCurrentSchemaIndex, unsigned& instrCount)
+ ClassProbeInserter(Schema& schema, uint8_t* profileMemory, int* pCurrentSchemaIndex, unsigned& instrCount)
: m_schema(schema)
, m_profileMemory(profileMemory)
, m_currentSchemaIndex(pCurrentSchemaIndex)
@@ -1349,7 +1353,7 @@ class ClassProbeInserter
// Figure out where the table is located.
//
- BYTE* classProfile = m_schema[*m_currentSchemaIndex].Offset + m_profileMemory;
+ uint8_t* classProfile = m_schema[*m_currentSchemaIndex].Offset + m_profileMemory;
*m_currentSchemaIndex += 2; // There are 2 schema entries per class probe
// Grab a temp to hold the 'this' object as it will be used three times
@@ -1426,7 +1430,7 @@ class ClassProbeInstrumentor : public Instrumentor
}
void Prepare(bool isPreImport) override;
void BuildSchemaElements(BasicBlock* block, Schema& schema) override;
- void Instrument(BasicBlock* block, Schema& schema, BYTE* profileMemory) override;
+ void Instrument(BasicBlock* block, Schema& schema, uint8_t* profileMemory) override;
void SuppressProbes() override;
};
@@ -1490,7 +1494,7 @@ void ClassProbeInstrumentor::BuildSchemaElements(BasicBlock* block, Schema& sche
// schema -- instrumentation schema
// profileMemory -- profile data slab
//
-void ClassProbeInstrumentor::Instrument(BasicBlock* block, Schema& schema, BYTE* profileMemory)
+void ClassProbeInstrumentor::Instrument(BasicBlock* block, Schema& schema, uint8_t* profileMemory)
{
if ((block->bbFlags & BBF_HAS_CLASS_PROFILE) == 0)
{
@@ -1563,21 +1567,43 @@ PhaseStatus Compiler::fgPrepareToInstrumentMethod()
// Choose instrumentation technology.
//
// We enable edge profiling by default, except when:
+ //
// * disabled by option
// * we are prejitting
- // * we are jitting osr methods
+ // * we are jitting tier0 methods with patchpoints
+ // * we are jitting an OSR method
//
- // Currently, OSR is incompatible with edge profiling. So if OSR is enabled,
- // always do block profiling.
+ // OSR is incompatible with edge profiling. Only portions of the Tier0
+ // method will be executed, and the bail-outs at patchpoints won't be obvious
+ // exit points from the method. So for OSR we always do block profiling.
//
// Note this incompatibility only exists for methods that actually have
- // patchpoints, but we won't know that until we import.
+ // patchpoints. Currently we will only place patchponts in methods with
+ // backwards jumps.
+ //
+ // And because we want the Tier1 method to see the full set of profile data,
+ // when OSR is enabled, both Tier0 and any OSR methods need to contribute to
+ // the same profile data set. Since Tier0 has laid down a dense block-based
+ // schema, the OSR methods must use this schema as well.
+ //
+ // Note that OSR methods may also inline. We currently won't instrument
+ // any inlinee contributions (which would also need to carefully "share"
+ // the profile data segment with any Tier0 version and/or any other equivalent
+ // inlnee), so we'll lose a bit of their profile data. We can support this
+ // eventually if it turns out to matter.
+ //
+ // Similar issues arise with partially jitted methods. Because we currently
+ // only defer jitting for throw blocks, we currently ignore the impact of partial
+ // jitting on PGO. If we ever implement a broader pattern of deferral -- say deferring
+ // based on static PGO -- we will need to reconsider.
//
CLANG_FORMAT_COMMENT_ANCHOR;
- const bool prejit = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT);
- const bool osr = (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0) && (JitConfig.TC_OnStackReplacement() > 0));
- const bool useEdgeProfiles = (JitConfig.JitEdgeProfiling() > 0) && !prejit && !osr;
+ const bool prejit = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT);
+ const bool tier0WithPatchpoints = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0) &&
+ (JitConfig.TC_OnStackReplacement() > 0) && compHasBackwardJump;
+ const bool osrMethod = opts.IsOSR();
+ const bool useEdgeProfiles = (JitConfig.JitEdgeProfiling() > 0) && !prejit && !tier0WithPatchpoints && !osrMethod;
if (useEdgeProfiles)
{
@@ -1586,7 +1612,9 @@ PhaseStatus Compiler::fgPrepareToInstrumentMethod()
else
{
JITDUMP("Using block profiling, because %s\n",
- (JitConfig.JitEdgeProfiling() > 0) ? "edge profiles disabled" : prejit ? "prejitting" : "OSR");
+ (JitConfig.JitEdgeProfiling() > 0)
+ ? "edge profiles disabled"
+ : prejit ? "prejitting" : osrMethod ? "OSR" : "tier0 with patchpoints");
fgCountInstrumentor = new (this, CMK_Pgo) BlockCountInstrumentor(this);
}
@@ -1636,7 +1664,7 @@ PhaseStatus Compiler::fgInstrumentMethod()
{
noway_assert(!compIsForInlining());
- // Make post-importpreparations.
+ // Make post-import preparations.
//
const bool isPreImport = false;
fgCountInstrumentor->Prepare(isPreImport);
@@ -1661,7 +1689,17 @@ PhaseStatus Compiler::fgInstrumentMethod()
// Verify we created schema for the calls needing class probes.
// (we counted those when importing)
//
- assert(fgClassInstrumentor->SchemaCount() == info.compClassProbeCount);
+ // This is not true when we do partial compilation; it can/will erase class probes,
+ // and there's no easy way to figure out how many should be left.
+ //
+ if (doesMethodHavePartialCompilationPatchpoints())
+ {
+ assert(fgClassInstrumentor->SchemaCount() <= info.compClassProbeCount);
+ }
+ else
+ {
+ assert(fgClassInstrumentor->SchemaCount() == info.compClassProbeCount);
+ }
// Optionally, when jitting, if there were no class probes and only one count probe,
// suppress instrumentation.
@@ -1694,11 +1732,16 @@ PhaseStatus Compiler::fgInstrumentMethod()
assert(schema.size() > 0);
- // Allocate the profile buffer
+ // Allocate/retrieve the profile buffer.
//
- BYTE* profileMemory;
-
- HRESULT res = info.compCompHnd->allocPgoInstrumentationBySchema(info.compMethodHnd, schema.data(),
+ // If this is an OSR method, we should use the same buffer that the Tier0 method used.
+ //
+ // This is supported by allocPgoInsrumentationDataBySchema, which will verify the schema
+ // we provide here matches the one from Tier0, and will fill in the data offsets in
+ // our schema properly.
+ //
+ uint8_t* profileMemory;
+ HRESULT res = info.compCompHnd->allocPgoInstrumentationBySchema(info.compMethodHnd, schema.data(),
(UINT32)schema.size(), &profileMemory);
// Deal with allocation failures.
@@ -1920,6 +1963,14 @@ void Compiler::fgIncorporateBlockCounts()
fgSetProfileWeight(block, profileWeight);
}
}
+
+ // For OSR, give the method entry (which will be a scratch BB)
+ // the same weight as the OSR Entry.
+ //
+ if (opts.IsOSR())
+ {
+ fgFirstBB->inheritWeight(fgOSREntryBB);
+ }
}
//------------------------------------------------------------------------
@@ -1988,19 +2039,6 @@ class EfficientEdgeCountReconstructor : public SpanningTreeVisitor
unsigned m_unknownEdges;
unsigned m_zeroEdges;
- // Map a block into its schema key.
- //
- static int32_t BlockToKey(BasicBlock* block)
- {
- int32_t key = (int32_t)block->bbCodeOffs;
- if ((block->bbFlags & BBF_INTERNAL) == BBF_INTERNAL)
- {
- key = block->bbNum | IL_OFFSETX_CALLINSTRUCTIONBIT;
- }
-
- return key;
- }
-
// Map correlating block keys to blocks.
//
typedef JitHashTable, BasicBlock*> KeyToBlockMap;
@@ -2018,7 +2056,8 @@ class EfficientEdgeCountReconstructor : public SpanningTreeVisitor
}
EdgeKey(BasicBlock* sourceBlock, BasicBlock* targetBlock)
- : m_sourceKey(BlockToKey(sourceBlock)), m_targetKey(BlockToKey(targetBlock))
+ : m_sourceKey(EfficientEdgeCountBlockToKey(sourceBlock))
+ , m_targetKey(EfficientEdgeCountBlockToKey(targetBlock))
{
}
@@ -2241,7 +2280,7 @@ void EfficientEdgeCountReconstructor::Prepare()
//
for (BasicBlock* const block : m_comp->Blocks())
{
- m_keyToBlockMap.Set(BlockToKey(block), block);
+ m_keyToBlockMap.Set(EfficientEdgeCountBlockToKey(block), block);
BlockInfo* const info = new (m_allocator) BlockInfo();
SetBlockInfo(block, info);
@@ -3285,11 +3324,17 @@ void Compiler::fgComputeCalledCount(weight_t returnWeight)
BasicBlock* firstILBlock = fgFirstBB; // The first block for IL code (i.e. for the IL code at offset 0)
- // Skip past any/all BBF_INTERNAL blocks that may have been added before the first real IL block.
+ // OSR methods can have complex entry flow, and so
+ // for OSR we ensure fgFirstBB has plausible profile data.
//
- while (firstILBlock->bbFlags & BBF_INTERNAL)
+ if (!opts.IsOSR())
{
- firstILBlock = firstILBlock->bbNext;
+ // Skip past any/all BBF_INTERNAL blocks that may have been added before the first real IL block.
+ //
+ while (firstILBlock->bbFlags & BBF_INTERNAL)
+ {
+ firstILBlock = firstILBlock->bbNext;
+ }
}
// The 'firstILBlock' is now expected to have a profile-derived weight
@@ -3725,7 +3770,7 @@ bool Compiler::fgProfileWeightsEqual(weight_t weight1, weight_t weight2)
}
//------------------------------------------------------------------------
-// fgProfileWeightsConsistentEqual: check if two profile weights are within
+// fgProfileWeightsConsistent: check if two profile weights are within
// some small percentage of one another.
//
// Arguments:
diff --git a/src/coreclr/jit/fgstmt.cpp b/src/coreclr/jit/fgstmt.cpp
index 256e8ede73420a..24140b3fc52b98 100644
--- a/src/coreclr/jit/fgstmt.cpp
+++ b/src/coreclr/jit/fgstmt.cpp
@@ -101,13 +101,14 @@ void Compiler::fgInsertStmtAtBeg(BasicBlock* block, Statement* stmt)
// Arguments:
// block - the block into which 'tree' will be inserted;
// tree - the tree to be inserted.
+// di - the debug info to use for the new statement.
//
// Return Value:
// The new created statement with `tree` inserted into `block`.
//
-Statement* Compiler::fgNewStmtAtBeg(BasicBlock* block, GenTree* tree)
+Statement* Compiler::fgNewStmtAtBeg(BasicBlock* block, GenTree* tree, const DebugInfo& di)
{
- Statement* stmt = gtNewStmt(tree);
+ Statement* stmt = gtNewStmt(tree, di);
fgInsertStmtAtBeg(block, stmt);
return stmt;
}
@@ -153,6 +154,7 @@ void Compiler::fgInsertStmtAtEnd(BasicBlock* block, Statement* stmt)
// Arguments:
// block - the block into which 'stmt' will be inserted;
// tree - the tree to be inserted.
+// di - the debug info to use for the new statement.
//
// Return Value:
// The new created statement with `tree` inserted into `block`.
@@ -160,9 +162,9 @@ void Compiler::fgInsertStmtAtEnd(BasicBlock* block, Statement* stmt)
// Note:
// If the block can be a conditional block, use fgNewStmtNearEnd.
//
-Statement* Compiler::fgNewStmtAtEnd(BasicBlock* block, GenTree* tree)
+Statement* Compiler::fgNewStmtAtEnd(BasicBlock* block, GenTree* tree, const DebugInfo& di)
{
- Statement* stmt = gtNewStmt(tree);
+ Statement* stmt = gtNewStmt(tree, di);
fgInsertStmtAtEnd(block, stmt);
return stmt;
}
@@ -241,13 +243,14 @@ void Compiler::fgInsertStmtNearEnd(BasicBlock* block, Statement* stmt)
// Arguments:
// block - the block into which 'stmt' will be inserted;
// tree - the tree to be inserted.
+// di - the debug info to use for the new statement.
//
// Return Value:
// The new created statement with `tree` inserted into `block`.
//
-Statement* Compiler::fgNewStmtNearEnd(BasicBlock* block, GenTree* tree)
+Statement* Compiler::fgNewStmtNearEnd(BasicBlock* block, GenTree* tree, const DebugInfo& di)
{
- Statement* stmt = gtNewStmt(tree);
+ Statement* stmt = gtNewStmt(tree, di);
fgInsertStmtNearEnd(block, stmt);
return stmt;
}
@@ -379,9 +382,9 @@ Statement* Compiler::fgInsertStmtListAfter(BasicBlock* block, Statement* stmtAft
*
* Create a new statement from tree and wire the links up.
*/
-Statement* Compiler::fgNewStmtFromTree(GenTree* tree, BasicBlock* block, IL_OFFSETX offs)
+Statement* Compiler::fgNewStmtFromTree(GenTree* tree, BasicBlock* block, const DebugInfo& di)
{
- Statement* stmt = gtNewStmt(tree, offs);
+ Statement* stmt = gtNewStmt(tree, di);
if (fgStmtListThreaded)
{
@@ -401,17 +404,17 @@ Statement* Compiler::fgNewStmtFromTree(GenTree* tree, BasicBlock* block, IL_OFFS
Statement* Compiler::fgNewStmtFromTree(GenTree* tree)
{
- return fgNewStmtFromTree(tree, nullptr, BAD_IL_OFFSET);
+ return fgNewStmtFromTree(tree, nullptr, DebugInfo());
}
Statement* Compiler::fgNewStmtFromTree(GenTree* tree, BasicBlock* block)
{
- return fgNewStmtFromTree(tree, block, BAD_IL_OFFSET);
+ return fgNewStmtFromTree(tree, block, DebugInfo());
}
-Statement* Compiler::fgNewStmtFromTree(GenTree* tree, IL_OFFSETX offs)
+Statement* Compiler::fgNewStmtFromTree(GenTree* tree, const DebugInfo& di)
{
- return fgNewStmtFromTree(tree, nullptr, offs);
+ return fgNewStmtFromTree(tree, nullptr, di);
}
//------------------------------------------------------------------------
@@ -455,7 +458,7 @@ void Compiler::fgRemoveStmt(BasicBlock* block, Statement* stmt DEBUGARG(bool isU
}
#endif // DEBUG
- if (opts.compDbgCode && stmt->GetPrevStmt() != stmt && stmt->GetILOffsetX() != BAD_IL_OFFSET)
+ if (opts.compDbgCode && stmt->GetPrevStmt() != stmt && stmt->GetDebugInfo().IsValid())
{
/* TODO: For debuggable code, should we remove significant
statement boundaries. Or should we leave a GT_NO_OP in its place? */
diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp
index 5742d8659ac123..e0fdac1da935ca 100644
--- a/src/coreclr/jit/flowgraph.cpp
+++ b/src/coreclr/jit/flowgraph.cpp
@@ -278,7 +278,7 @@ BasicBlock* Compiler::fgCreateGCPoll(GCPollType pollType, BasicBlock* block)
if (nextStmt != nullptr)
{
// Is it possible for gtNextStmt to be NULL?
- newStmt->SetILOffsetX(nextStmt->GetILOffsetX());
+ newStmt->SetDebugInfo(nextStmt->GetDebugInfo());
}
}
@@ -524,6 +524,9 @@ bool Compiler::fgCanSwitchToOptimized()
//------------------------------------------------------------------------
// fgSwitchToOptimized: Switch the opt level from tier 0 to optimized
//
+// Arguments:
+// reason - reason why opt level was switched
+//
// Assumptions:
// - fgCanSwitchToOptimized() is true
// - compSetOptimizationLevel() has not been called
@@ -532,15 +535,16 @@ bool Compiler::fgCanSwitchToOptimized()
// This method is to be called at some point before compSetOptimizationLevel() to switch the opt level to optimized
// based on information gathered in early phases.
-void Compiler::fgSwitchToOptimized()
+void Compiler::fgSwitchToOptimized(const char* reason)
{
assert(fgCanSwitchToOptimized());
// Switch to optimized and re-init options
- JITDUMP("****\n**** JIT Tier0 jit request switching to Tier1 because of loop\n****\n");
+ JITDUMP("****\n**** JIT Tier0 jit request switching to Tier1 because: %s\n****\n", reason);
assert(opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0));
opts.jitFlags->Clear(JitFlags::JIT_FLAG_TIER0);
opts.jitFlags->Clear(JitFlags::JIT_FLAG_BBINSTR);
+ opts.jitFlags->Clear(JitFlags::JIT_FLAG_OSR);
// Leave a note for jit diagnostics
compSwitchedToOptimized = true;
@@ -976,7 +980,7 @@ bool Compiler::fgAddrCouldBeNull(GenTree* addr)
return false;
}
- LclVarDsc* varDsc = &lvaTable[varNum];
+ LclVarDsc* varDsc = lvaGetDesc(varNum);
if (varDsc->lvStackByref)
{
@@ -1333,8 +1337,7 @@ GenTree* Compiler::fgDoNormalizeOnStore(GenTree* tree)
// Small-typed arguments and aliased locals are normalized on load.
// Other small-typed locals are normalized on store.
// If it is an assignment to one of the latter, insert the cast on RHS
- unsigned varNum = op1->AsLclVarCommon()->GetLclNum();
- LclVarDsc* varDsc = &lvaTable[varNum];
+ LclVarDsc* varDsc = lvaGetDesc(op1->AsLclVarCommon()->GetLclNum());
if (varDsc->lvNormalizeOnStore())
{
@@ -1362,7 +1365,7 @@ GenTree* Compiler::fgDoNormalizeOnStore(GenTree* tree)
* execute a call or not.
*/
-inline void Compiler::fgLoopCallTest(BasicBlock* srcBB, BasicBlock* dstBB)
+void Compiler::fgLoopCallTest(BasicBlock* srcBB, BasicBlock* dstBB)
{
/* Bail if this is not a backward edge */
@@ -1436,7 +1439,7 @@ void Compiler::fgLoopCallMark()
* Note the fact that the given block is a loop header.
*/
-inline void Compiler::fgMarkLoopHead(BasicBlock* block)
+void Compiler::fgMarkLoopHead(BasicBlock* block)
{
#ifdef DEBUG
if (verbose)
@@ -1683,12 +1686,9 @@ void Compiler::fgAddSyncMethodEnterExit()
// Create a block for the start of the try region, where the monitor enter call
// will go.
-
- assert(fgFirstBB->bbFallsThrough());
-
- BasicBlock* tryBegBB = fgNewBBafter(BBJ_NONE, fgFirstBB, false);
- BasicBlock* tryNextBB = tryBegBB->bbNext;
- BasicBlock* tryLastBB = fgLastBB;
+ BasicBlock* const tryBegBB = fgSplitBlockAtEnd(fgFirstBB);
+ BasicBlock* const tryNextBB = tryBegBB->bbNext;
+ BasicBlock* const tryLastBB = fgLastBB;
// If we have profile data the new block will inherit the next block's weight
if (tryNextBB->hasProfileWeight())
@@ -1799,10 +1799,10 @@ void Compiler::fgAddSyncMethodEnterExit()
lvaTable[lvaMonAcquired].lvType = typeMonAcquired;
- { // Scope the variables of the variable initialization
-
- // Initialize the 'acquired' boolean.
-
+ // Create IR to initialize the 'acquired' boolean.
+ //
+ if (!opts.IsOSR())
+ {
GenTree* zero = gtNewZeroConNode(genActualType(typeMonAcquired));
GenTree* varNode = gtNewLclvNode(lvaMonAcquired, typeMonAcquired);
GenTree* initNode = gtNewAssignNode(varNode, zero);
@@ -1825,7 +1825,7 @@ void Compiler::fgAddSyncMethodEnterExit()
unsigned lvaCopyThis = 0;
if (!info.compIsStatic)
{
- lvaCopyThis = lvaGrabTemp(true DEBUGARG("Synchronized method monitor acquired boolean"));
+ lvaCopyThis = lvaGrabTemp(true DEBUGARG("Synchronized method copy of this for handler"));
lvaTable[lvaCopyThis].lvType = TYP_REF;
GenTree* thisNode = gtNewLclvNode(info.compThisArg, TYP_REF);
@@ -1835,7 +1835,12 @@ void Compiler::fgAddSyncMethodEnterExit()
fgNewStmtAtEnd(tryBegBB, initNode);
}
- fgCreateMonitorTree(lvaMonAcquired, info.compThisArg, tryBegBB, true /*enter*/);
+ // For OSR, we do not need the enter tree as the monitor is acquired by the original method.
+ //
+ if (!opts.IsOSR())
+ {
+ fgCreateMonitorTree(lvaMonAcquired, info.compThisArg, tryBegBB, true /*enter*/);
+ }
// exceptional case
fgCreateMonitorTree(lvaMonAcquired, lvaCopyThis, faultBB, false /*exit*/);
@@ -1994,7 +1999,7 @@ void Compiler::fgAddReversePInvokeEnterExit()
lvaReversePInvokeFrameVar = lvaGrabTempWithImplicitUse(false DEBUGARG("Reverse Pinvoke FrameVar"));
- LclVarDsc* varDsc = &lvaTable[lvaReversePInvokeFrameVar];
+ LclVarDsc* varDsc = lvaGetDesc(lvaReversePInvokeFrameVar);
varDsc->lvType = TYP_BLK;
varDsc->lvExactSize = eeGetEEInfo()->sizeOfReversePInvokeFrame;
@@ -2750,7 +2755,7 @@ void Compiler::fgAddInternal()
if (!opts.ShouldUsePInvokeHelpers())
{
info.compLvFrameListRoot = lvaGrabTemp(false DEBUGARG("Pinvoke FrameListRoot"));
- LclVarDsc* rootVarDsc = &lvaTable[info.compLvFrameListRoot];
+ LclVarDsc* rootVarDsc = lvaGetDesc(info.compLvFrameListRoot);
rootVarDsc->lvType = TYP_I_IMPL;
rootVarDsc->lvImplicitlyReferenced = 1;
}
@@ -2760,7 +2765,7 @@ void Compiler::fgAddInternal()
// Lowering::InsertPInvokeMethodProlog will create a call with this local addr as an argument.
lvaSetVarAddrExposed(lvaInlinedPInvokeFrameVar DEBUGARG(AddressExposedReason::ESCAPE_ADDRESS));
- LclVarDsc* varDsc = &lvaTable[lvaInlinedPInvokeFrameVar];
+ LclVarDsc* varDsc = lvaGetDesc(lvaInlinedPInvokeFrameVar);
varDsc->lvType = TYP_BLK;
// Make room for the inlined frame.
varDsc->lvExactSize = eeGetEEInfo()->inlinedCallFrameInfo.size;
@@ -2771,7 +2776,7 @@ void Compiler::fgAddInternal()
if (!opts.ShouldUsePInvokeHelpers() && compJmpOpUsed)
{
lvaPInvokeFrameRegSaveVar = lvaGrabTempWithImplicitUse(false DEBUGARG("PInvokeFrameRegSave Var"));
- varDsc = &lvaTable[lvaPInvokeFrameRegSaveVar];
+ varDsc = lvaGetDesc(lvaPInvokeFrameRegSaveVar);
varDsc->lvType = TYP_BLK;
varDsc->lvExactSize = 2 * REGSIZE_BYTES;
}
@@ -3901,6 +3906,7 @@ GenTree* Compiler::fgSetTreeSeq(GenTree* tree, GenTree* prevTree, bool isLIR)
void Compiler::fgSetTreeSeqHelper(GenTree* tree, bool isLIR)
{
+ // TODO-List-Cleanup: measure what using GenTreeVisitor here brings.
genTreeOps oper;
unsigned kind;
@@ -3960,40 +3966,6 @@ void Compiler::fgSetTreeSeqHelper(GenTree* tree, bool isLIR)
GenTree* op1 = tree->AsOp()->gtOp1;
GenTree* op2 = tree->gtGetOp2IfPresent();
- // Special handling for GT_LIST
- if (tree->OperGet() == GT_LIST)
- {
- // First, handle the list items, which will be linked in forward order.
- // As we go, we will link the GT_LIST nodes in reverse order - we will number
- // them and update fgTreeSeqList in a subsequent traversal.
- GenTree* nextList = tree;
- GenTree* list = nullptr;
- while (nextList != nullptr && nextList->OperGet() == GT_LIST)
- {
- list = nextList;
- GenTree* listItem = list->AsOp()->gtOp1;
- fgSetTreeSeqHelper(listItem, isLIR);
- nextList = list->AsOp()->gtOp2;
- if (nextList != nullptr)
- {
- nextList->gtNext = list;
- }
- list->gtPrev = nextList;
- }
- // Next, handle the GT_LIST nodes.
- // Note that fgSetTreeSeqFinish() sets the gtNext to null, so we need to capture the nextList
- // before we call that method.
- nextList = list;
- do
- {
- assert(list != nullptr);
- list = nextList;
- nextList = list->gtNext;
- fgSetTreeSeqFinish(list, isLIR);
- } while (list != tree);
- return;
- }
-
/* Special handling for AddrMode */
if (tree->OperIsAddrMode())
{
@@ -4095,6 +4067,29 @@ void Compiler::fgSetTreeSeqHelper(GenTree* tree, bool isLIR)
break;
+#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+#if defined(FEATURE_SIMD)
+ case GT_SIMD:
+#endif
+#if defined(FEATURE_HW_INTRINSICS)
+ case GT_HWINTRINSIC:
+#endif
+ if (tree->IsReverseOp())
+ {
+ assert(tree->AsMultiOp()->GetOperandCount() == 2);
+ fgSetTreeSeqHelper(tree->AsMultiOp()->Op(2), isLIR);
+ fgSetTreeSeqHelper(tree->AsMultiOp()->Op(1), isLIR);
+ }
+ else
+ {
+ for (GenTree* operand : tree->AsMultiOp()->Operands())
+ {
+ fgSetTreeSeqHelper(operand, isLIR);
+ }
+ }
+ break;
+#endif // defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+
case GT_ARR_ELEM:
fgSetTreeSeqHelper(tree->AsArrElem()->gtArrObj, isLIR);
@@ -4159,7 +4154,7 @@ void Compiler::fgSetTreeSeqFinish(GenTree* tree, bool isLIR)
{
tree->gtFlags &= ~GTF_REVERSE_OPS;
- if (tree->OperIs(GT_LIST, GT_ARGPLACE))
+ if (tree->OperIs(GT_ARGPLACE))
{
return;
}
@@ -4434,32 +4429,29 @@ void Compiler::fgSetBlockOrder(BasicBlock* block)
// Return Value:
// The first node in execution order, that belongs to tree.
//
-// Assumptions:
-// 'tree' must either be a leaf, or all of its constituent nodes must be contiguous
-// in execution order.
-// TODO-Cleanup: Add a debug-only method that verifies this.
-
-/* static */
-GenTree* Compiler::fgGetFirstNode(GenTree* tree)
+// Notes:
+// This function is only correct for HIR trees.
+//
+/* static */ GenTree* Compiler::fgGetFirstNode(GenTree* tree)
{
- GenTree* child = tree;
- while (child->NumChildren() > 0)
+ GenTree* firstNode = tree;
+ while (true)
{
- if (child->OperIsBinary() && child->IsReverseOp())
- {
- child = child->GetChild(1);
- }
- else
+ auto operandsBegin = firstNode->OperandsBegin();
+ auto operandsEnd = firstNode->OperandsEnd();
+
+ if (operandsBegin == operandsEnd)
{
- child = child->GetChild(0);
+ break;
}
+
+ firstNode = *operandsBegin;
}
- return child;
+
+ return firstNode;
}
-/*****************************************************************************/
-/*static*/
-Compiler::fgWalkResult Compiler::fgChkThrowCB(GenTree** pTree, fgWalkData* data)
+/*static*/ Compiler::fgWalkResult Compiler::fgChkThrowCB(GenTree** pTree, fgWalkData* data)
{
GenTree* tree = *pTree;
@@ -4501,9 +4493,7 @@ Compiler::fgWalkResult Compiler::fgChkThrowCB(GenTree** pTree, fgWalkData* data)
return Compiler::WALK_CONTINUE;
}
-/*****************************************************************************/
-/*static*/
-Compiler::fgWalkResult Compiler::fgChkLocAllocCB(GenTree** pTree, fgWalkData* data)
+/*static*/ Compiler::fgWalkResult Compiler::fgChkLocAllocCB(GenTree** pTree, fgWalkData* data)
{
GenTree* tree = *pTree;
@@ -4515,9 +4505,7 @@ Compiler::fgWalkResult Compiler::fgChkLocAllocCB(GenTree** pTree, fgWalkData* da
return Compiler::WALK_CONTINUE;
}
-/*****************************************************************************/
-/*static*/
-Compiler::fgWalkResult Compiler::fgChkQmarkCB(GenTree** pTree, fgWalkData* data)
+/*static*/ Compiler::fgWalkResult Compiler::fgChkQmarkCB(GenTree** pTree, fgWalkData* data)
{
GenTree* tree = *pTree;
diff --git a/src/coreclr/jit/gcinfo.cpp b/src/coreclr/jit/gcinfo.cpp
index cc8e1cd585f849..85e7f688c2f2ef 100644
--- a/src/coreclr/jit/gcinfo.cpp
+++ b/src/coreclr/jit/gcinfo.cpp
@@ -714,7 +714,7 @@ GCInfo::WriteBarrierForm GCInfo::gcWriteBarrierFormFromTargetAddress(GenTree* tg
{
unsigned lclNum = tgtAddr->AsLclVar()->GetLclNum();
- LclVarDsc* varDsc = &compiler->lvaTable[lclNum];
+ LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum);
// Instead of marking LclVar with 'lvStackByref',
// Consider decomposing the Value Number given to this LclVar to see if it was
@@ -728,29 +728,6 @@ GCInfo::WriteBarrierForm GCInfo::gcWriteBarrierFormFromTargetAddress(GenTree* tg
assert(varDsc->TypeGet() == TYP_BYREF);
return GCInfo::WBF_NoBarrier;
}
-
- // We don't eliminate for inlined methods, where we (can) know where the "retBuff" points.
- if (!compiler->compIsForInlining() && lclNum == compiler->info.compRetBuffArg)
- {
- assert(compiler->info.compRetType == TYP_STRUCT); // Else shouldn't have a ret buff.
-
- // Are we assured that the ret buff pointer points into the stack of a caller?
- if (compiler->info.compRetBuffDefStack)
- {
-#if 0
- // This is an optional debugging mode. If the #if 0 above is changed to #if 1,
- // every barrier we remove for stores to GC ref fields of a retbuff use a special
- // helper that asserts that the target is not in the heap.
-#ifdef DEBUG
- return WBF_NoBarrier_CheckNotHeapInDebug;
-#else
- return WBF_NoBarrier;
-#endif
-#else // 0
- return GCInfo::WBF_NoBarrier;
-#endif // 0
- }
- }
}
if (tgtAddr->TypeGet() == TYP_REF)
{
diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp
index 90093f9e7bba6c..00c58d70bae1d2 100644
--- a/src/coreclr/jit/gentree.cpp
+++ b/src/coreclr/jit/gentree.cpp
@@ -170,7 +170,7 @@ static const char* opNames[] = {
const char* GenTree::OpName(genTreeOps op)
{
- assert((unsigned)op < _countof(opNames));
+ assert((unsigned)op < ArrLen(opNames));
return opNames[op];
}
@@ -186,7 +186,7 @@ static const char* opStructNames[] = {
const char* GenTree::OpStructName(genTreeOps op)
{
- assert((unsigned)op < _countof(opStructNames));
+ assert((unsigned)op < ArrLen(opStructNames));
return opStructNames[op];
}
@@ -308,7 +308,6 @@ void GenTree::InitNodeSize()
static_assert_no_msg(sizeof(GenTreeCast) <= TREE_NODE_SZ_LARGE); // *** large node
static_assert_no_msg(sizeof(GenTreeBox) <= TREE_NODE_SZ_LARGE); // *** large node
static_assert_no_msg(sizeof(GenTreeField) <= TREE_NODE_SZ_LARGE); // *** large node
- static_assert_no_msg(sizeof(GenTreeArgList) <= TREE_NODE_SZ_SMALL);
static_assert_no_msg(sizeof(GenTreeFieldList) <= TREE_NODE_SZ_SMALL);
static_assert_no_msg(sizeof(GenTreeColon) <= TREE_NODE_SZ_SMALL);
static_assert_no_msg(sizeof(GenTreeCall) <= TREE_NODE_SZ_LARGE); // *** large node
@@ -1508,29 +1507,6 @@ bool GenTree::Compare(GenTree* op1, GenTree* op2, bool swapOK)
return false;
}
break;
-#ifdef FEATURE_SIMD
- case GT_SIMD:
- if ((op1->AsSIMD()->gtSIMDIntrinsicID != op2->AsSIMD()->gtSIMDIntrinsicID) ||
- (op1->AsSIMD()->GetSimdBaseType() != op2->AsSIMD()->GetSimdBaseType()) ||
- (op1->AsSIMD()->GetSimdSize() != op2->AsSIMD()->GetSimdSize()))
- {
- return false;
- }
- break;
-#endif // FEATURE_SIMD
-
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HWINTRINSIC:
- if ((op1->AsHWIntrinsic()->gtHWIntrinsicId != op2->AsHWIntrinsic()->gtHWIntrinsicId) ||
- (op1->AsHWIntrinsic()->GetSimdBaseType() != op2->AsHWIntrinsic()->GetSimdBaseType()) ||
- (op1->AsHWIntrinsic()->GetSimdSize() != op2->AsHWIntrinsic()->GetSimdSize()) ||
- (op1->AsHWIntrinsic()->GetAuxiliaryType() != op2->AsHWIntrinsic()->GetAuxiliaryType()) ||
- (op1->AsHWIntrinsic()->GetOtherReg() != op2->AsHWIntrinsic()->GetOtherReg()))
- {
- return false;
- }
- break;
-#endif
// For the ones below no extra argument matters for comparison.
case GT_QMARK:
@@ -1592,6 +1568,16 @@ bool GenTree::Compare(GenTree* op1, GenTree* op2, bool swapOK)
case GT_CALL:
return GenTreeCall::Equals(op1->AsCall(), op2->AsCall());
+#ifdef FEATURE_SIMD
+ case GT_SIMD:
+ return GenTreeSIMD::Equals(op1->AsSIMD(), op2->AsSIMD());
+#endif // FEATURE_SIMD
+
+#ifdef FEATURE_HW_INTRINSICS
+ case GT_HWINTRINSIC:
+ return GenTreeHWIntrinsic::Equals(op1->AsHWIntrinsic(), op2->AsHWIntrinsic());
+#endif
+
case GT_ARR_ELEM:
if (op1->AsArrElem()->gtArrRank != op2->AsArrElem()->gtArrRank)
@@ -1653,6 +1639,7 @@ bool GenTree::Compare(GenTree* op1, GenTree* op2, bool swapOK)
* Returns non-zero if the given tree contains a use of a local #lclNum.
*/
+// TODO-List-Cleanup: rewrite with a general visitor.
bool Compiler::gtHasRef(GenTree* tree, ssize_t lclNum, bool defOnly)
{
genTreeOps oper;
@@ -1800,6 +1787,23 @@ bool Compiler::gtHasRef(GenTree* tree, ssize_t lclNum, bool defOnly)
break;
+#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+#if defined(FEATURE_SIMD)
+ case GT_SIMD:
+#endif
+#if defined(FEATURE_HW_INTRINSICS)
+ case GT_HWINTRINSIC:
+#endif
+ for (GenTree* operand : tree->AsMultiOp()->Operands())
+ {
+ if (gtHasRef(operand, lclNum, defOnly))
+ {
+ return true;
+ }
+ }
+ break;
+#endif // defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+
case GT_ARR_ELEM:
if (gtHasRef(tree->AsArrElem()->gtArrObj, lclNum, defOnly))
{
@@ -1902,8 +1906,7 @@ Compiler::fgWalkResult Compiler::gtHasLocalsWithAddrOpCB(GenTree** pTree, fgWalk
if (tree->gtOper == GT_LCL_VAR)
{
- unsigned lclNum = tree->AsLclVarCommon()->GetLclNum();
- LclVarDsc* varDsc = &comp->lvaTable[lclNum];
+ const LclVarDsc* varDsc = comp->lvaGetDesc(tree->AsLclVarCommon());
if (varDsc->lvHasLdAddrOp || varDsc->IsAddressExposed())
{
@@ -2143,7 +2146,7 @@ unsigned Compiler::gtHashValue(GenTree* tree)
#ifdef FEATURE_SIMD
case GT_SIMD:
- hash += tree->AsSIMD()->gtSIMDIntrinsicID;
+ hash += tree->AsSIMD()->GetSIMDIntrinsicId();
hash += tree->AsSIMD()->GetSimdBaseType();
hash += tree->AsSIMD()->GetSimdSize();
break;
@@ -2151,7 +2154,7 @@ unsigned Compiler::gtHashValue(GenTree* tree)
#ifdef FEATURE_HW_INTRINSICS
case GT_HWINTRINSIC:
- hash += tree->AsHWIntrinsic()->gtHWIntrinsicId;
+ hash += tree->AsHWIntrinsic()->GetHWIntrinsicId();
hash += tree->AsHWIntrinsic()->GetSimdBaseType();
hash += tree->AsHWIntrinsic()->GetSimdSize();
hash += tree->AsHWIntrinsic()->GetAuxiliaryType();
@@ -2245,6 +2248,21 @@ unsigned Compiler::gtHashValue(GenTree* tree)
}
break;
+#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+#if defined(FEATURE_SIMD)
+ case GT_SIMD:
+#endif
+#if defined(FEATURE_HW_INTRINSICS)
+ case GT_HWINTRINSIC:
+#endif
+ // TODO-List: rewrite with a general visitor / iterator?
+ for (GenTree* operand : tree->AsMultiOp()->Operands())
+ {
+ hash = genTreeHashAdd(hash, gtHashValue(operand));
+ }
+ break;
+#endif // defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+
case GT_PHI:
for (GenTreePhi::Use& use : tree->AsPhi()->Uses())
{
@@ -2539,123 +2557,6 @@ void GenTreeOp::DebugCheckLongMul()
#endif // !defined(TARGET_64BIT) && defined(DEBUG)
#endif // !defined(TARGET_64BIT) || defined(TARGET_ARM64)
-//------------------------------------------------------------------------------
-// gtSetListOrder : Figure out the evaluation order for a list of values.
-//
-//
-// Arguments:
-// list - List to figure out the evaluation order for
-// isListCallArgs - True iff the list is a list of call arguments
-// callArgsInRegs - True iff the list is a list of call arguments and they are passed in registers
-//
-// Return Value:
-// True if the operation can be a root of a bitwise rotation tree; false otherwise.
-
-unsigned Compiler::gtSetListOrder(GenTree* list, bool isListCallArgs, bool callArgsInRegs)
-{
- assert((list != nullptr) && list->OperIsAnyList());
- assert(!callArgsInRegs || isListCallArgs);
-
- ArrayStack listNodes(getAllocator(CMK_ArrayStack));
-
- do
- {
- listNodes.Push(list);
- list = list->AsOp()->gtOp2;
- } while ((list != nullptr) && (list->OperIsAnyList()));
-
- unsigned nxtlvl = (list == nullptr) ? 0 : gtSetEvalOrder(list);
- while (!listNodes.Empty())
- {
- list = listNodes.Pop();
- assert(list && list->OperIsAnyList());
- GenTree* next = list->AsOp()->gtOp2;
-
- unsigned level = 0;
-
- // TODO: Do we have to compute costs differently for argument lists and
- // all other lists?
- // https://github.com/dotnet/runtime/issues/6622
- unsigned costSz = (isListCallArgs || (next == nullptr)) ? 0 : 1;
- unsigned costEx = (isListCallArgs || (next == nullptr)) ? 0 : 1;
-
- if (next != nullptr)
- {
- if (isListCallArgs)
- {
- if (level < nxtlvl)
- {
- level = nxtlvl;
- }
- }
- costEx += next->GetCostEx();
- costSz += next->GetCostSz();
- }
-
- GenTree* op1 = list->AsOp()->gtOp1;
- unsigned lvl = gtSetEvalOrder(op1);
-
- // Swap the level counts
- if (list->gtFlags & GTF_REVERSE_OPS)
- {
- unsigned tmpl;
-
- tmpl = lvl;
- lvl = nxtlvl;
- nxtlvl = tmpl;
- }
-
- // TODO: Do we have to compute levels differently for argument lists and
- // all other lists?
- // https://github.com/dotnet/runtime/issues/6622
- if (isListCallArgs)
- {
- if (level < lvl)
- {
- level = lvl;
- }
- }
- else
- {
- if (lvl < 1)
- {
- level = nxtlvl;
- }
- else if (lvl == nxtlvl)
- {
- level = lvl + 1;
- }
- else
- {
- level = lvl;
- }
- }
-
- if (op1->GetCostEx() != 0)
- {
- costEx += op1->GetCostEx();
- costEx += (callArgsInRegs || !isListCallArgs) ? 0 : IND_COST_EX;
- }
-
- if (op1->GetCostSz() != 0)
- {
- costSz += op1->GetCostSz();
-#ifdef TARGET_XARCH
- if (callArgsInRegs) // push is smaller than mov to reg
-#endif
- {
- costSz += 1;
- }
- }
-
- list->SetCosts(costEx, costSz);
-
- nxtlvl = level;
- }
-
- return nxtlvl;
-}
-
unsigned Compiler::gtSetCallArgsOrder(const GenTreeCall::UseList& args, bool lateArgs, int* callCostEx, int* callCostSz)
{
unsigned level = 0;
@@ -2696,6 +2597,172 @@ unsigned Compiler::gtSetCallArgsOrder(const GenTreeCall::UseList& args, bool lat
return level;
}
+#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+//------------------------------------------------------------------------
+// gtSetMultiOpOrder: Calculate the costs for a MultiOp.
+//
+// Currently this function just preserves the previous behavior.
+// TODO-List-Cleanup: implement proper costing for these trees.
+//
+// Arguments:
+// multiOp - The MultiOp tree in question
+//
+// Return Value:
+// The Sethi "complexity" for this tree (the idealized number of
+// registers needed to evaluate it).
+//
+unsigned Compiler::gtSetMultiOpOrder(GenTreeMultiOp* multiOp)
+{
+ // These default costs preserve previous behavior.
+ // TODO-CQ: investigate opportunities for tuning them.
+ int costEx = 1;
+ int costSz = 1;
+ unsigned level = 0;
+ unsigned lvl2 = 0;
+
+#if defined(FEATURE_HW_INTRINSICS) && defined(TARGET_XARCH)
+ if (multiOp->OperIs(GT_HWINTRINSIC) && (multiOp->GetOperandCount() == 1) &&
+ multiOp->AsHWIntrinsic()->OperIsMemoryLoadOrStore())
+ {
+ costEx = IND_COST_EX;
+ costSz = 2;
+
+ GenTree* addr = multiOp->Op(1)->gtEffectiveVal();
+ level = gtSetEvalOrder(addr);
+
+ // See if we can form a complex addressing mode.
+ if (addr->OperIs(GT_ADD) && gtMarkAddrMode(addr, &costEx, &costSz, multiOp->TypeGet()))
+ {
+ // Nothing to do, costs have been set.
+ }
+ else
+ {
+ costEx += addr->GetCostEx();
+ costSz += addr->GetCostSz();
+ }
+
+ multiOp->SetCosts(costEx, costSz);
+ return level;
+ }
+#endif // defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+
+ // This code is here to preserve previous behavior.
+ switch (multiOp->GetOperandCount())
+ {
+ case 0:
+ // This is a constant HWIntrinsic, we already have correct costs.
+ break;
+
+ case 1:
+ // A "unary" case.
+ level = gtSetEvalOrder(multiOp->Op(1));
+ costEx += multiOp->Op(1)->GetCostEx();
+ costSz += multiOp->Op(1)->GetCostSz();
+ break;
+
+ case 2:
+ // A "binary" case.
+
+ // This way we have "level" be the complexity of the
+ // first tree to be evaluated, and "lvl2" - the second.
+ if (multiOp->IsReverseOp())
+ {
+ level = gtSetEvalOrder(multiOp->Op(2));
+ lvl2 = gtSetEvalOrder(multiOp->Op(1));
+ }
+ else
+ {
+ level = gtSetEvalOrder(multiOp->Op(1));
+ lvl2 = gtSetEvalOrder(multiOp->Op(2));
+ }
+
+ // We want the more complex tree to be evaluated first.
+ if (level < lvl2)
+ {
+ bool canSwap = multiOp->IsReverseOp() ? gtCanSwapOrder(multiOp->Op(2), multiOp->Op(1))
+ : gtCanSwapOrder(multiOp->Op(1), multiOp->Op(2));
+
+ // The InitN intrinsic for two operands used to be not reversible, so preserve this.
+ // TODO-List-Cleanup: delete this only-needed-for-zero-diffs quirk.
+ if (multiOp->OperIs(GT_SIMD) && (multiOp->AsSIMD()->GetSIMDIntrinsicId() == SIMDIntrinsicInitN))
+ {
+ canSwap = false;
+ }
+
+ if (canSwap)
+ {
+ if (multiOp->IsReverseOp())
+ {
+ multiOp->ClearReverseOp();
+ }
+ else
+ {
+ multiOp->SetReverseOp();
+ }
+
+ std::swap(level, lvl2);
+ }
+ }
+
+ if (level < 1)
+ {
+ level = lvl2;
+ }
+ else if (level == lvl2)
+ {
+ level += 1;
+ }
+
+ costEx += (multiOp->Op(1)->GetCostEx() + multiOp->Op(2)->GetCostEx());
+ costSz += (multiOp->Op(1)->GetCostSz() + multiOp->Op(2)->GetCostSz());
+ break;
+
+ default:
+ // The former "ArgList" case... we'll be emulating it here.
+ // The old implementation pushed the nodes on the list, in pre-order.
+ // Then it popped and costed them in "reverse order", so that's what
+ // we'll be doing here as well.
+
+ unsigned nxtlvl = 0;
+ for (size_t i = multiOp->GetOperandCount(); i >= 1; i--)
+ {
+ GenTree* op = multiOp->Op(i);
+ unsigned lvl = gtSetEvalOrder(op);
+
+ if (lvl < 1)
+ {
+ level = nxtlvl;
+ }
+ else if (lvl == nxtlvl)
+ {
+ level = lvl + 1;
+ }
+ else
+ {
+ level = lvl;
+ }
+
+ costEx += op->GetCostEx();
+ costSz += op->GetCostSz();
+
+ // Preserving previous behavior...
+ CLANG_FORMAT_COMMENT_ANCHOR;
+#ifndef TARGET_XARCH
+ if (op->GetCostSz() != 0)
+ {
+ costSz += 1;
+ }
+#endif
+ nxtlvl = level;
+ }
+ break;
+ }
+
+ multiOp->SetCosts(costEx, costSz);
+ return level;
+}
+#endif
+
//-----------------------------------------------------------------------------
// gtWalkOp: Traverse and mark an address expression
//
@@ -2845,8 +2912,7 @@ bool Compiler::gtIsLikelyRegVar(GenTree* tree)
return false;
}
- assert(tree->AsLclVar()->GetLclNum() < lvaTableCnt);
- LclVarDsc* varDsc = lvaTable + tree->AsLclVar()->GetLclNum();
+ const LclVarDsc* varDsc = lvaGetDesc(tree->AsLclVar());
if (varDsc->lvDoNotEnregister)
{
@@ -3809,7 +3875,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree)
break;
- case GT_LIST:
case GT_NOP:
costEx = 0;
costSz = 0;
@@ -3948,26 +4013,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree)
costSz = 2 * 2;
break;
-#if defined(FEATURE_HW_INTRINSICS) && defined(TARGET_XARCH)
- case GT_HWINTRINSIC:
- {
- if (tree->AsHWIntrinsic()->OperIsMemoryLoadOrStore())
- {
- costEx = IND_COST_EX;
- costSz = 2;
- // See if we can form a complex addressing mode.
-
- GenTree* addr = op1->gtEffectiveVal();
-
- if (addr->OperIs(GT_ADD) && gtMarkAddrMode(addr, &costEx, &costSz, tree->TypeGet()))
- {
- goto DONE;
- }
- }
- }
- break;
-#endif // FEATURE_HW_INTRINSICS && TARGET_XARCH
-
case GT_BLK:
case GT_IND:
@@ -4225,13 +4270,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree)
goto DONE;
- case GT_LIST:
- {
- const bool isListCallArgs = false;
- const bool callArgsInRegs = false;
- return gtSetListOrder(tree, isListCallArgs, callArgsInRegs);
- }
-
case GT_INDEX_ADDR:
costEx = 6; // cmp reg,reg; jae throw; mov reg, [addrmode] (not taken)
costSz = 9; // jump to cold section
@@ -4529,9 +4567,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree)
case GT_MKREFANY:
break;
- case GT_LIST:
- break;
-
default:
/* Mark the operand's evaluation order to be swapped */
@@ -4701,6 +4736,16 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree)
costEx += 3 * IND_COST_EX;
break;
+#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+#if defined(FEATURE_SIMD)
+ case GT_SIMD:
+#endif
+#if defined(FEATURE_HW_INTRINSICS)
+ case GT_HWINTRINSIC:
+#endif
+ return gtSetMultiOpOrder(tree->AsMultiOp());
+#endif // defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+
case GT_ARR_ELEM:
{
GenTreeArrElem* arrElem = tree->AsArrElem();
@@ -4857,16 +4902,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree)
}
DONE:
-
-#ifdef FEATURE_HW_INTRINSICS
- if ((oper == GT_HWINTRINSIC) && (tree->gtGetOp1() == nullptr))
- {
- // We can have nullary HWIntrinsic nodes, and we must have non-zero cost.
- costEx = 1;
- costSz = 1;
- }
-#endif // FEATURE_HW_INTRINSICS
-
// Some path through this function must have set the costs.
assert(costEx != -1);
assert(costSz != -1);
@@ -4949,183 +4984,20 @@ unsigned GenTree::GetScaledIndex()
}
//------------------------------------------------------------------------
-// gtGetChildPointer: If 'parent' is the parent of this node, return the pointer
-// to the child node so that it can be modified; otherwise, return nullptr.
+// TryGetUse: Get the use edge for an operand of this tree.
//
// Arguments:
-// parent - The possible parent of this node
+// operand - the node to find the use for
+// pUse - [out] parameter for the use
//
// Return Value:
-// If "child" is a child of "parent", returns a pointer to the child node in the parent
-// (i.e. a pointer to a GenTree pointer).
-// Otherwise, returns nullptr.
-//
-// Assumptions:
-// 'parent' must be non-null
-//
-// Notes:
-// When FEATURE_MULTIREG_ARGS is defined we can get here with GT_OBJ tree.
-// This happens when we have a struct that is passed in multiple registers.
+// Whether "operand" is a child of this node. If it is, "*pUse" is set,
+// allowing for the replacement of "operand" with some other node.
//
-// Also note that when UNIX_AMD64_ABI is defined the GT_LDOBJ
-// later gets converted to a GT_FIELD_LIST with two GT_LCL_FLDs in Lower/LowerXArch.
-//
-
-GenTree** GenTree::gtGetChildPointer(GenTree* parent) const
-
+bool GenTree::TryGetUse(GenTree* operand, GenTree*** pUse)
{
- switch (parent->OperGet())
- {
- default:
- if (!parent->OperIsSimple())
- {
- return nullptr;
- }
- if (this == parent->AsOp()->gtOp1)
- {
- return &(parent->AsOp()->gtOp1);
- }
- if (this == parent->AsOp()->gtOp2)
- {
- return &(parent->AsOp()->gtOp2);
- }
- break;
-
- case GT_PHI:
- for (GenTreePhi::Use& use : parent->AsPhi()->Uses())
- {
- if (use.GetNode() == this)
- {
- return &use.NodeRef();
- }
- }
- break;
-
- case GT_FIELD_LIST:
- for (GenTreeFieldList::Use& use : parent->AsFieldList()->Uses())
- {
- if (this == use.GetNode())
- {
- return &use.NodeRef();
- }
- }
- break;
-
- case GT_CMPXCHG:
- if (this == parent->AsCmpXchg()->gtOpLocation)
- {
- return &(parent->AsCmpXchg()->gtOpLocation);
- }
- if (this == parent->AsCmpXchg()->gtOpValue)
- {
- return &(parent->AsCmpXchg()->gtOpValue);
- }
- if (this == parent->AsCmpXchg()->gtOpComparand)
- {
- return &(parent->AsCmpXchg()->gtOpComparand);
- }
- break;
-
- case GT_ARR_ELEM:
- if (this == parent->AsArrElem()->gtArrObj)
- {
- return &(parent->AsArrElem()->gtArrObj);
- }
- for (int i = 0; i < GT_ARR_MAX_RANK; i++)
- {
- if (this == parent->AsArrElem()->gtArrInds[i])
- {
- return &(parent->AsArrElem()->gtArrInds[i]);
- }
- }
- break;
-
- case GT_ARR_OFFSET:
- if (this == parent->AsArrOffs()->gtOffset)
- {
- return &(parent->AsArrOffs()->gtOffset);
- }
- if (this == parent->AsArrOffs()->gtIndex)
- {
- return &(parent->AsArrOffs()->gtIndex);
- }
- if (this == parent->AsArrOffs()->gtArrObj)
- {
- return &(parent->AsArrOffs()->gtArrObj);
- }
- break;
-
- case GT_STORE_DYN_BLK:
- case GT_DYN_BLK:
- if (this == parent->AsDynBlk()->gtOp1)
- {
- return &(parent->AsDynBlk()->gtOp1);
- }
- if (this == parent->AsDynBlk()->gtOp2)
- {
- return &(parent->AsDynBlk()->gtOp2);
- }
- if (this == parent->AsDynBlk()->gtDynamicSize)
- {
- return &(parent->AsDynBlk()->gtDynamicSize);
- }
- break;
-
- case GT_RET_EXPR:
- if (this == parent->AsRetExpr()->gtInlineCandidate)
- {
- return &(parent->AsRetExpr()->gtInlineCandidate);
- }
- break;
-
- case GT_CALL:
- {
- GenTreeCall* call = parent->AsCall();
-
- if ((call->gtCallThisArg != nullptr) && (this == call->gtCallThisArg->GetNode()))
- {
- return &call->gtCallThisArg->NodeRef();
- }
- for (GenTreeCall::Use& use : call->Args())
- {
- if (this == use.GetNode())
- {
- return &use.NodeRef();
- }
- }
- for (GenTreeCall::Use& use : call->LateArgs())
- {
- if (this == use.GetNode())
- {
- return &use.NodeRef();
- }
- }
- if (this == call->gtControlExpr)
- {
- return &(call->gtControlExpr);
- }
- if (call->gtCallType == CT_INDIRECT)
- {
- if (this == call->gtCallCookie)
- {
- return &(call->gtCallCookie);
- }
- if (this == call->gtCallAddr)
- {
- return &(call->gtCallAddr);
- }
- }
- }
- break;
- }
-
- return nullptr;
-}
-
-bool GenTree::TryGetUse(GenTree* def, GenTree*** use)
-{
- assert(def != nullptr);
- assert(use != nullptr);
+ assert(operand != nullptr);
+ assert(pUse != nullptr);
switch (OperGet())
{
@@ -5199,9 +5071,9 @@ bool GenTree::TryGetUse(GenTree* def, GenTree*** use)
case GT_BSWAP16:
case GT_KEEPALIVE:
case GT_INC_SATURATE:
- if (def == this->AsUnOp()->gtOp1)
+ if (operand == this->AsUnOp()->gtOp1)
{
- *use = &this->AsUnOp()->gtOp1;
+ *pUse = &this->AsUnOp()->gtOp1;
return true;
}
return false;
@@ -5211,44 +5083,41 @@ bool GenTree::TryGetUse(GenTree* def, GenTree*** use)
case GT_PUTARG_SPLIT:
if (this->AsUnOp()->gtOp1->gtOper == GT_FIELD_LIST)
{
- return this->AsUnOp()->gtOp1->TryGetUse(def, use);
+ return this->AsUnOp()->gtOp1->TryGetUse(operand, pUse);
}
- if (def == this->AsUnOp()->gtOp1)
+ if (operand == this->AsUnOp()->gtOp1)
{
- *use = &this->AsUnOp()->gtOp1;
+ *pUse = &this->AsUnOp()->gtOp1;
return true;
}
return false;
#endif // FEATURE_ARG_SPLIT
-#ifdef FEATURE_SIMD
+#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+#if defined(FEATURE_SIMD)
case GT_SIMD:
- if (this->AsSIMD()->gtSIMDIntrinsicID == SIMDIntrinsicInitN)
- {
- assert(this->AsSIMD()->gtOp1 != nullptr);
- return this->AsSIMD()->gtOp1->TryGetUseList(def, use);
- }
-
- return TryGetUseBinOp(def, use);
-#endif // FEATURE_SIMD
-
-#ifdef FEATURE_HW_INTRINSICS
+#endif
+#if defined(FEATURE_HW_INTRINSICS)
case GT_HWINTRINSIC:
- if ((this->AsHWIntrinsic()->gtOp1 != nullptr) && this->AsHWIntrinsic()->gtOp1->OperIsList())
+#endif
+ for (GenTree** opUse : this->AsMultiOp()->UseEdges())
{
- return this->AsHWIntrinsic()->gtOp1->TryGetUseList(def, use);
+ if (*opUse == operand)
+ {
+ *pUse = opUse;
+ return true;
+ }
}
-
- return TryGetUseBinOp(def, use);
-#endif // FEATURE_HW_INTRINSICS
+ return false;
+#endif // defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
// Special nodes
case GT_PHI:
for (GenTreePhi::Use& phiUse : AsPhi()->Uses())
{
- if (phiUse.GetNode() == def)
+ if (phiUse.GetNode() == operand)
{
- *use = &phiUse.NodeRef();
+ *pUse = &phiUse.NodeRef();
return true;
}
}
@@ -5257,9 +5126,9 @@ bool GenTree::TryGetUse(GenTree* def, GenTree*** use)
case GT_FIELD_LIST:
for (GenTreeFieldList::Use& fieldUse : AsFieldList()->Uses())
{
- if (fieldUse.GetNode() == def)
+ if (fieldUse.GetNode() == operand)
{
- *use = &fieldUse.NodeRef();
+ *pUse = &fieldUse.NodeRef();
return true;
}
}
@@ -5268,19 +5137,19 @@ bool GenTree::TryGetUse(GenTree* def, GenTree*** use)
case GT_CMPXCHG:
{
GenTreeCmpXchg* const cmpXchg = this->AsCmpXchg();
- if (def == cmpXchg->gtOpLocation)
+ if (operand == cmpXchg->gtOpLocation)
{
- *use = &cmpXchg->gtOpLocation;
+ *pUse = &cmpXchg->gtOpLocation;
return true;
}
- if (def == cmpXchg->gtOpValue)
+ if (operand == cmpXchg->gtOpValue)
{
- *use = &cmpXchg->gtOpValue;
+ *pUse = &cmpXchg->gtOpValue;
return true;
}
- if (def == cmpXchg->gtOpComparand)
+ if (operand == cmpXchg->gtOpComparand)
{
- *use = &cmpXchg->gtOpComparand;
+ *pUse = &cmpXchg->gtOpComparand;
return true;
}
return false;
@@ -5289,16 +5158,16 @@ bool GenTree::TryGetUse(GenTree* def, GenTree*** use)
case GT_ARR_ELEM:
{
GenTreeArrElem* const arrElem = this->AsArrElem();
- if (def == arrElem->gtArrObj)
+ if (operand == arrElem->gtArrObj)
{
- *use = &arrElem->gtArrObj;
+ *pUse = &arrElem->gtArrObj;
return true;
}
for (unsigned i = 0; i < arrElem->gtArrRank; i++)
{
- if (def == arrElem->gtArrInds[i])
+ if (operand == arrElem->gtArrInds[i])
{
- *use = &arrElem->gtArrInds[i];
+ *pUse = &arrElem->gtArrInds[i];
return true;
}
}
@@ -5308,19 +5177,19 @@ bool GenTree::TryGetUse(GenTree* def, GenTree*** use)
case GT_ARR_OFFSET:
{
GenTreeArrOffs* const arrOffs = this->AsArrOffs();
- if (def == arrOffs->gtOffset)
+ if (operand == arrOffs->gtOffset)
{
- *use = &arrOffs->gtOffset;
+ *pUse = &arrOffs->gtOffset;
return true;
}
- if (def == arrOffs->gtIndex)
+ if (operand == arrOffs->gtIndex)
{
- *use = &arrOffs->gtIndex;
+ *pUse = &arrOffs->gtIndex;
return true;
}
- if (def == arrOffs->gtArrObj)
+ if (operand == arrOffs->gtArrObj)
{
- *use = &arrOffs->gtArrObj;
+ *pUse = &arrOffs->gtArrObj;
return true;
}
return false;
@@ -5329,14 +5198,14 @@ bool GenTree::TryGetUse(GenTree* def, GenTree*** use)
case GT_DYN_BLK:
{
GenTreeDynBlk* const dynBlock = this->AsDynBlk();
- if (def == dynBlock->gtOp1)
+ if (operand == dynBlock->gtOp1)
{
- *use = &dynBlock->gtOp1;
+ *pUse = &dynBlock->gtOp1;
return true;
}
- if (def == dynBlock->gtDynamicSize)
+ if (operand == dynBlock->gtDynamicSize)
{
- *use = &dynBlock->gtDynamicSize;
+ *pUse = &dynBlock->gtDynamicSize;
return true;
}
return false;
@@ -5345,19 +5214,19 @@ bool GenTree::TryGetUse(GenTree* def, GenTree*** use)
case GT_STORE_DYN_BLK:
{
GenTreeDynBlk* const dynBlock = this->AsDynBlk();
- if (def == dynBlock->gtOp1)
+ if (operand == dynBlock->gtOp1)
{
- *use = &dynBlock->gtOp1;
+ *pUse = &dynBlock->gtOp1;
return true;
}
- if (def == dynBlock->gtOp2)
+ if (operand == dynBlock->gtOp2)
{
- *use = &dynBlock->gtOp2;
+ *pUse = &dynBlock->gtOp2;
return true;
}
- if (def == dynBlock->gtDynamicSize)
+ if (operand == dynBlock->gtDynamicSize)
{
- *use = &dynBlock->gtDynamicSize;
+ *pUse = &dynBlock->gtDynamicSize;
return true;
}
return false;
@@ -5366,42 +5235,42 @@ bool GenTree::TryGetUse(GenTree* def, GenTree*** use)
case GT_CALL:
{
GenTreeCall* const call = this->AsCall();
- if ((call->gtCallThisArg != nullptr) && (def == call->gtCallThisArg->GetNode()))
+ if ((call->gtCallThisArg != nullptr) && (operand == call->gtCallThisArg->GetNode()))
{
- *use = &call->gtCallThisArg->NodeRef();
+ *pUse = &call->gtCallThisArg->NodeRef();
return true;
}
- if (def == call->gtControlExpr)
+ if (operand == call->gtControlExpr)
{
- *use = &call->gtControlExpr;
+ *pUse = &call->gtControlExpr;
return true;
}
if (call->gtCallType == CT_INDIRECT)
{
- if (def == call->gtCallCookie)
+ if (operand == call->gtCallCookie)
{
- *use = &call->gtCallCookie;
+ *pUse = &call->gtCallCookie;
return true;
}
- if (def == call->gtCallAddr)
+ if (operand == call->gtCallAddr)
{
- *use = &call->gtCallAddr;
+ *pUse = &call->gtCallAddr;
return true;
}
}
for (GenTreeCall::Use& argUse : call->Args())
{
- if (argUse.GetNode() == def)
+ if (argUse.GetNode() == operand)
{
- *use = &argUse.NodeRef();
+ *pUse = &argUse.NodeRef();
return true;
}
}
for (GenTreeCall::Use& argUse : call->LateArgs())
{
- if (argUse.GetNode() == def)
+ if (argUse.GetNode() == operand)
{
- *use = &argUse.NodeRef();
+ *pUse = &argUse.NodeRef();
return true;
}
}
@@ -5411,41 +5280,25 @@ bool GenTree::TryGetUse(GenTree* def, GenTree*** use)
// Binary nodes
default:
assert(this->OperIsBinary());
- return TryGetUseBinOp(def, use);
- }
-}
-
-bool GenTree::TryGetUseList(GenTree* def, GenTree*** use)
-{
- assert(def != nullptr);
- assert(use != nullptr);
-
- for (GenTreeArgList* node = this->AsArgList(); node != nullptr; node = node->Rest())
- {
- if (def == node->gtOp1)
- {
- *use = &node->gtOp1;
- return true;
- }
+ return TryGetUseBinOp(operand, pUse);
}
- return false;
}
-bool GenTree::TryGetUseBinOp(GenTree* def, GenTree*** use)
+bool GenTree::TryGetUseBinOp(GenTree* operand, GenTree*** pUse)
{
- assert(def != nullptr);
- assert(use != nullptr);
+ assert(operand != nullptr);
+ assert(pUse != nullptr);
assert(this->OperIsBinary());
GenTreeOp* const binOp = this->AsOp();
- if (def == binOp->gtOp1)
+ if (operand == binOp->gtOp1)
{
- *use = &binOp->gtOp1;
+ *pUse = &binOp->gtOp1;
return true;
}
- if (def == binOp->gtOp2)
+ if (operand == binOp->gtOp2)
{
- *use = &binOp->gtOp2;
+ *pUse = &binOp->gtOp2;
return true;
}
return false;
@@ -5482,37 +5335,38 @@ void GenTree::ReplaceOperand(GenTree** useEdge, GenTree* replacement)
// pointer to the child so that it can be modified.
//
// Arguments:
-// parentChildPointer - A pointer to a GenTree** (yes, that's three
-// levels, i.e. GenTree ***), which if non-null,
-// will be set to point to the field in the parent
-// that points to this node.
-//
-// Return value - The parent of this node.
+// pUse - A pointer to a GenTree** (yes, that's three
+// levels, i.e. GenTree ***), which if non-null,
+// will be set to point to the field in the parent
+// that points to this node.
//
-// Notes:
+// Return value
+// The parent of this node.
//
+// Notes:
// This requires that the execution order must be defined (i.e. gtSetEvalOrder() has been called).
-// To enable the child to be replaced, it accepts an argument, parentChildPointer that, if non-null,
+// To enable the child to be replaced, it accepts an argument, "pUse", that, if non-null,
// will be set to point to the child pointer in the parent that points to this node.
-
-GenTree* GenTree::gtGetParent(GenTree*** parentChildPtrPtr) const
+//
+GenTree* GenTree::gtGetParent(GenTree*** pUse)
{
// Find the parent node; it must be after this node in the execution order.
- GenTree** parentChildPtr = nullptr;
- GenTree* parent;
- for (parent = gtNext; parent != nullptr; parent = parent->gtNext)
+ GenTree* user;
+ GenTree** use = nullptr;
+ for (user = gtNext; user != nullptr; user = user->gtNext)
{
- parentChildPtr = gtGetChildPointer(parent);
- if (parentChildPtr != nullptr)
+ if (user->TryGetUse(this, &use))
{
break;
}
}
- if (parentChildPtrPtr != nullptr)
+
+ if (pUse != nullptr)
{
- *parentChildPtrPtr = parentChildPtr;
+ *pUse = use;
}
- return parent;
+
+ return user;
}
//-------------------------------------------------------------------------
@@ -5972,8 +5826,7 @@ GenTree* Compiler::gtNewOperNode(genTreeOps oper, var_types type, GenTree* op1,
GenTreeQmark* Compiler::gtNewQmarkNode(var_types type, GenTree* cond, GenTreeColon* colon)
{
- compQmarkUsed = true;
- cond->gtFlags |= GTF_RELOP_QMARK;
+ compQmarkUsed = true;
GenTreeQmark* result = new (this, GT_QMARK) GenTreeQmark(type, cond, colon);
#ifdef DEBUG
if (compQmarkRationalized)
@@ -6345,17 +6198,17 @@ GenTree* Compiler::gtNewSIMDVectorZero(var_types simdType, CorInfoType simdBaseJ
var_types simdBaseType = genActualType(JitType2PreciseVarType(simdBaseJitType));
GenTree* initVal = gtNewZeroConNode(simdBaseType);
initVal->gtType = simdBaseType;
- return gtNewSIMDNode(simdType, initVal, nullptr, SIMDIntrinsicInit, simdBaseJitType, simdSize);
+ return gtNewSIMDNode(simdType, initVal, SIMDIntrinsicInit, simdBaseJitType, simdSize);
}
#endif // FEATURE_SIMD
-GenTreeCall* Compiler::gtNewIndCallNode(GenTree* addr, var_types type, GenTreeCall::Use* args, IL_OFFSETX ilOffset)
+GenTreeCall* Compiler::gtNewIndCallNode(GenTree* addr, var_types type, GenTreeCall::Use* args, const DebugInfo& di)
{
- return gtNewCallNode(CT_INDIRECT, (CORINFO_METHOD_HANDLE)addr, type, args, ilOffset);
+ return gtNewCallNode(CT_INDIRECT, (CORINFO_METHOD_HANDLE)addr, type, args, di);
}
GenTreeCall* Compiler::gtNewCallNode(
- gtCallTypes callType, CORINFO_METHOD_HANDLE callHnd, var_types type, GenTreeCall::Use* args, IL_OFFSETX ilOffset)
+ gtCallTypes callType, CORINFO_METHOD_HANDLE callHnd, var_types type, GenTreeCall::Use* args, const DebugInfo& di)
{
GenTreeCall* node = new (this, GT_CALL) GenTreeCall(genActualType(type));
@@ -6399,14 +6252,15 @@ GenTreeCall* Compiler::gtNewCallNode(
// These get updated after call node is built.
node->gtInlineObservation = InlineObservation::CALLEE_UNUSED_INITIAL;
node->gtRawILOffset = BAD_IL_OFFSET;
+ node->gtInlineContext = compInlineContext;
#endif
// Spec: Managed Retval sequence points needs to be generated while generating debug info for debuggable code.
//
// Implementation note: if not generating MRV info genCallSite2ILOffsetMap will be NULL and
- // codegen will pass BAD_IL_OFFSET as IL offset of a call node to emitter, which will cause emitter
+ // codegen will pass DebugInfo() to emitter, which will cause emitter
// not to emit IP mapping entry.
- if (opts.compDbgCode && opts.compDbgInfo)
+ if (opts.compDbgCode && opts.compDbgInfo && di.IsValid())
{
// Managed Retval - IL offset of the call. This offset is used to emit a
// CALL_INSTRUCTION type sequence point while emitting corresponding native call.
@@ -6418,14 +6272,14 @@ GenTreeCall* Compiler::gtNewCallNode(
//
// b) (Opt) Add new sequence points only if requested by debugger through
// a new boundary type - ICorDebugInfo::BoundaryTypes
- if (genCallSite2ILOffsetMap == nullptr)
+ if (genCallSite2DebugInfoMap == nullptr)
{
- genCallSite2ILOffsetMap = new (getAllocator()) CallSiteILOffsetTable(getAllocator());
+ genCallSite2DebugInfoMap = new (getAllocator()) CallSiteDebugInfoTable(getAllocator());
}
// Make sure that there are no duplicate entries for a given call node
- assert(!genCallSite2ILOffsetMap->Lookup(node));
- genCallSite2ILOffsetMap->Set(node, ilOffset);
+ assert(!genCallSite2DebugInfoMap->Lookup(node));
+ genCallSite2DebugInfoMap->Set(node, di);
}
// Initialize gtOtherRegs
@@ -6446,7 +6300,7 @@ GenTreeCall* Compiler::gtNewCallNode(
return node;
}
-GenTreeLclVar* Compiler::gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSETX ILoffs))
+GenTreeLclVar* Compiler::gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSET offs))
{
assert(type != TYP_VOID);
// We need to ensure that all struct values are normalized.
@@ -6466,7 +6320,7 @@ GenTreeLclVar* Compiler::gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL
assert((type == varDsc->lvType) || simd12ToSimd16Widening ||
(lvaIsImplicitByRefLocal(lnum) && fgGlobalMorph && (varDsc->lvType == TYP_BYREF)));
}
- GenTreeLclVar* node = new (this, GT_LCL_VAR) GenTreeLclVar(GT_LCL_VAR, type, lnum DEBUGARG(ILoffs));
+ GenTreeLclVar* node = new (this, GT_LCL_VAR) GenTreeLclVar(GT_LCL_VAR, type, lnum DEBUGARG(offs));
/* Cannot have this assert because the inliner uses this function
* to add temporaries */
@@ -6476,7 +6330,7 @@ GenTreeLclVar* Compiler::gtNewLclvNode(unsigned lnum, var_types type DEBUGARG(IL
return node;
}
-GenTreeLclVar* Compiler::gtNewLclLNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSETX ILoffs))
+GenTreeLclVar* Compiler::gtNewLclLNode(unsigned lnum, var_types type DEBUGARG(IL_OFFSET offs))
{
// We need to ensure that all struct values are normalized.
// It might be nice to assert this in general, but we have assignments of int to long.
@@ -6491,7 +6345,7 @@ GenTreeLclVar* Compiler::gtNewLclLNode(unsigned lnum, var_types type DEBUGARG(IL
// This local variable node may later get transformed into a large node
assert(GenTree::s_gtNodeSizes[LargeOpOpcode()] > GenTree::s_gtNodeSizes[GT_LCL_VAR]);
GenTreeLclVar* node =
- new (this, LargeOpOpcode()) GenTreeLclVar(GT_LCL_VAR, type, lnum DEBUGARG(ILoffs) DEBUGARG(/*largeNode*/ true));
+ new (this, LargeOpOpcode()) GenTreeLclVar(GT_LCL_VAR, type, lnum DEBUGARG(offs) DEBUGARG(/*largeNode*/ true));
return node;
}
@@ -6575,53 +6429,6 @@ GenTreeCall::Use* Compiler::gtNewCallArgs(GenTree* node1, GenTree* node2, GenTre
return new (this, CMK_ASTNode) GenTreeCall::Use(node1, gtNewCallArgs(node2, node3, node4));
}
-GenTreeArgList* Compiler::gtNewListNode(GenTree* op1, GenTreeArgList* op2)
-{
- assert((op1 != nullptr) && (op1->OperGet() != GT_LIST));
-
- return new (this, GT_LIST) GenTreeArgList(op1, op2);
-}
-
-/*****************************************************************************
- *
- * Create a list out of one value.
- */
-
-GenTreeArgList* Compiler::gtNewArgList(GenTree* arg)
-{
- return new (this, GT_LIST) GenTreeArgList(arg);
-}
-
-/*****************************************************************************
- *
- * Create a list out of the two values.
- */
-
-GenTreeArgList* Compiler::gtNewArgList(GenTree* arg1, GenTree* arg2)
-{
- return new (this, GT_LIST) GenTreeArgList(arg1, gtNewArgList(arg2));
-}
-
-/*****************************************************************************
- *
- * Create a list out of the three values.
- */
-
-GenTreeArgList* Compiler::gtNewArgList(GenTree* arg1, GenTree* arg2, GenTree* arg3)
-{
- return new (this, GT_LIST) GenTreeArgList(arg1, gtNewArgList(arg2, arg3));
-}
-
-/*****************************************************************************
- *
- * Create a list out of the three values.
- */
-
-GenTreeArgList* Compiler::gtNewArgList(GenTree* arg1, GenTree* arg2, GenTree* arg3, GenTree* arg4)
-{
- return new (this, GT_LIST) GenTreeArgList(arg1, gtNewArgList(arg2, arg3, arg4));
-}
-
/*****************************************************************************
*
* Given a GT_CALL node, access the fgArgInfo and find the entry
@@ -7798,14 +7605,6 @@ GenTree* Compiler::gtCloneExpr(
tree->AsCast()->gtCastType DEBUGARG(/*largeNode*/ TRUE));
break;
- // The nodes below this are not bashed, so they can be allocated at their individual sizes.
-
- case GT_LIST:
- assert((tree->AsOp()->gtOp2 == nullptr) || tree->AsOp()->gtOp2->OperIsList());
- copy = new (this, GT_LIST) GenTreeArgList(tree->AsOp()->gtOp1);
- copy->AsOp()->gtOp2 = tree->AsOp()->gtOp2;
- break;
-
case GT_INDEX:
{
GenTreeIndex* asInd = tree->AsIndex();
@@ -7934,30 +7733,6 @@ GenTree* Compiler::gtCloneExpr(
}
break;
-#ifdef FEATURE_SIMD
- case GT_SIMD:
- {
- GenTreeSIMD* simdOp = tree->AsSIMD();
- copy = gtNewSIMDNode(simdOp->TypeGet(), simdOp->gtGetOp1(), simdOp->gtGetOp2IfPresent(),
- simdOp->gtSIMDIntrinsicID, simdOp->GetSimdBaseJitType(), simdOp->GetSimdSize());
- }
- break;
-#endif
-
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HWINTRINSIC:
- {
- GenTreeHWIntrinsic* hwintrinsicOp = tree->AsHWIntrinsic();
- copy = new (this, GT_HWINTRINSIC)
- GenTreeHWIntrinsic(hwintrinsicOp->TypeGet(), hwintrinsicOp->gtGetOp1(),
- hwintrinsicOp->gtGetOp2IfPresent(), hwintrinsicOp->gtHWIntrinsicId,
- hwintrinsicOp->GetSimdBaseJitType(), hwintrinsicOp->GetSimdSize(),
- hwintrinsicOp->IsSimdAsHWIntrinsic());
- copy->AsHWIntrinsic()->SetAuxiliaryJitType(hwintrinsicOp->GetAuxiliaryJitType());
- }
- break;
-#endif
-
default:
assert(!GenTree::IsExOp(tree->OperKind()) && tree->OperIsSimple());
// We're in the SimpleOp case, so it's always unary or binary.
@@ -8054,6 +7829,33 @@ GenTree* Compiler::gtCloneExpr(
copy = gtCloneExprCallHelper(tree->AsCall(), addFlags, deepVarNum, deepVarVal);
break;
+#ifdef FEATURE_SIMD
+ case GT_SIMD:
+ copy = new (this, GT_SIMD)
+ GenTreeSIMD(tree->TypeGet(), IntrinsicNodeBuilder(getAllocator(CMK_ASTNode), tree->AsSIMD()),
+ tree->AsSIMD()->GetSIMDIntrinsicId(), tree->AsSIMD()->GetSimdBaseJitType(),
+ tree->AsSIMD()->GetSimdSize());
+ goto CLONE_MULTIOP_OPERANDS;
+#endif
+#ifdef FEATURE_HW_INTRINSICS
+ case GT_HWINTRINSIC:
+ copy = new (this, GT_HWINTRINSIC)
+ GenTreeHWIntrinsic(tree->TypeGet(), IntrinsicNodeBuilder(getAllocator(CMK_ASTNode), tree->AsMultiOp()),
+ tree->AsHWIntrinsic()->GetHWIntrinsicId(),
+ tree->AsHWIntrinsic()->GetSimdBaseJitType(), tree->AsHWIntrinsic()->GetSimdSize(),
+ tree->AsHWIntrinsic()->IsSimdAsHWIntrinsic());
+ copy->AsHWIntrinsic()->SetAuxiliaryJitType(tree->AsHWIntrinsic()->GetAuxiliaryJitType());
+ goto CLONE_MULTIOP_OPERANDS;
+#endif
+#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+ CLONE_MULTIOP_OPERANDS:
+ for (GenTree** use : copy->AsMultiOp()->UseEdges())
+ {
+ *use = gtCloneExpr(*use, addFlags, deepVarNum, deepVarVal);
+ }
+ break;
+#endif
+
case GT_ARR_ELEM:
{
GenTreeArrElem* arrElem = tree->AsArrElem();
@@ -8149,18 +7951,6 @@ GenTree* Compiler::gtCloneExpr(
/* GTF_NODE_MASK should not be propagated from 'tree' to 'copy' */
addFlags &= ~GTF_NODE_MASK;
#endif
- // Some other flags depend on the context of the expression, and should not be preserved.
- // For example, GTF_RELOP_QMARK:
- if (copy->OperKind() & GTK_RELOP)
- {
- addFlags &= ~GTF_RELOP_QMARK;
- }
- // On the other hand, if we're creating such a context, restore this flag.
- if (copy->OperGet() == GT_QMARK)
- {
- copy->AsOp()->gtOp1->gtFlags |= GTF_RELOP_QMARK;
- }
-
copy->gtFlags |= addFlags;
// Update side effect flags since they may be different from the source side effect flags.
@@ -8235,11 +8025,6 @@ GenTreeCall* Compiler::gtCloneExprCallHelper(GenTreeCall* tree,
argsTail = &((*argsTail)->NextRef());
}
-#if !FEATURE_FIXED_OUT_ARGS
- copy->regArgList = tree->regArgList;
- copy->regArgListCount = tree->regArgListCount;
-#endif
-
// The call sig comes from the EE and doesn't change throughout the compilation process, meaning
// we only really need one physical copy of it. Therefore a shallow pointer copy will suffice.
// (Note that this still holds even if the tree we are cloning was created by an inlinee compiler,
@@ -8294,7 +8079,8 @@ GenTreeCall* Compiler::gtCloneExprCallHelper(GenTreeCall* tree,
#if defined(DEBUG) || defined(INLINE_DATA)
copy->gtInlineObservation = tree->gtInlineObservation;
- copy->gtRawILOffset = tree->AsCall()->gtRawILOffset;
+ copy->gtRawILOffset = tree->gtRawILOffset;
+ copy->gtInlineContext = tree->gtInlineContext;
#endif
copy->CopyOtherRegFlags(tree);
@@ -8345,106 +8131,6 @@ GenTreeCall* Compiler::gtCloneCandidateCall(GenTreeCall* call)
return result;
}
-//------------------------------------------------------------------------
-// gtReplaceTree: Replace a tree with a new tree.
-//
-// Arguments:
-// stmt - The top-level root stmt of the tree being replaced.
-// Must not be null.
-// tree - The tree being replaced. Must not be null.
-// replacementTree - The replacement tree. Must not be null.
-//
-// Return Value:
-// The tree node that replaces the old tree.
-//
-// Assumptions:
-// The sequencing of the stmt has been done.
-//
-// Notes:
-// The caller must ensure that the original statement has been sequenced,
-// and the side effect flags are updated on the statement nodes,
-// but this method will sequence 'replacementTree', and insert it into the
-// proper place in the statement sequence.
-
-GenTree* Compiler::gtReplaceTree(Statement* stmt, GenTree* tree, GenTree* replacementTree)
-{
- assert(fgStmtListThreaded);
- assert(tree != nullptr);
- assert(stmt != nullptr);
- assert(replacementTree != nullptr);
-
- GenTree** treePtr = nullptr;
- GenTree* treeParent = tree->gtGetParent(&treePtr);
-
- assert(treeParent != nullptr || tree == stmt->GetRootNode());
-
- if (treePtr == nullptr)
- {
- // Replace the stmt expr and rebuild the linear order for "stmt".
- assert(treeParent == nullptr);
- assert(fgOrder != FGOrderLinear);
- stmt->SetRootNode(tree);
- fgSetStmtSeq(stmt);
- }
- else
- {
- assert(treeParent != nullptr);
-
- // Check to see if the node to be replaced is a call argument and if so,
- // set `treeParent` to the call node.
- GenTree* cursor = treeParent;
- while ((cursor != nullptr) && (cursor->OperGet() == GT_LIST))
- {
- cursor = cursor->gtNext;
- }
-
- if ((cursor != nullptr) && (cursor->OperGet() == GT_CALL))
- {
- treeParent = cursor;
- }
-
-#ifdef DEBUG
- GenTree** useEdge;
- assert(treeParent->TryGetUse(tree, &useEdge));
- assert(useEdge == treePtr);
-#endif // DEBUG
-
- GenTree* treeFirstNode = fgGetFirstNode(tree);
- GenTree* treeLastNode = tree;
- GenTree* treePrevNode = treeFirstNode->gtPrev;
- GenTree* treeNextNode = treeLastNode->gtNext;
-
- treeParent->ReplaceOperand(treePtr, replacementTree);
-
- // Build the linear order for "replacementTree".
- fgSetTreeSeq(replacementTree, treePrevNode);
-
- // Restore linear-order Prev and Next for "replacementTree".
- if (treePrevNode != nullptr)
- {
- treeFirstNode = fgGetFirstNode(replacementTree);
- treeFirstNode->gtPrev = treePrevNode;
- treePrevNode->gtNext = treeFirstNode;
- }
- else
- {
- // Update the linear oder start of "stmt" if treeFirstNode
- // appears to have replaced the original first node.
- assert(treeFirstNode == stmt->GetTreeList());
- stmt->SetTreeList(fgGetFirstNode(replacementTree));
- }
-
- if (treeNextNode != nullptr)
- {
- treeLastNode = replacementTree;
- treeLastNode->gtNext = treeNextNode;
- treeNextNode->gtPrev = treeLastNode;
- }
- }
-
- return replacementTree;
-}
-
//------------------------------------------------------------------------
// gtUpdateSideEffects: Update the side effects of a tree and its ancestors
//
@@ -8586,15 +8272,10 @@ void Compiler::gtUpdateNodeOperSideEffectsPost(GenTree* tree)
void Compiler::gtUpdateNodeSideEffects(GenTree* tree)
{
gtUpdateNodeOperSideEffects(tree);
- unsigned nChildren = tree->NumChildren();
- for (unsigned childNum = 0; childNum < nChildren; childNum++)
- {
- GenTree* child = tree->GetChild(childNum);
- if (child != nullptr)
- {
- tree->gtFlags |= (child->gtFlags & GTF_ALL_EFFECT);
- }
- }
+ tree->VisitOperands([tree](GenTree* operand) -> GenTree::VisitResult {
+ tree->gtFlags |= (operand->gtFlags & GTF_ALL_EFFECT);
+ return GenTree::VisitResult::Continue;
+ });
}
//------------------------------------------------------------------------
@@ -8649,135 +8330,36 @@ Compiler::fgWalkResult Compiler::fgUpdateSideEffectsPost(GenTree** pTree, fgWalk
return WALK_CONTINUE;
}
-/*****************************************************************************
- *
- * Compares two trees and returns true when both trees are the same.
- * Instead of fully comparing the two trees this method can just return false.
- * Thus callers should not assume that the trees are different when false is returned.
- * Only when true is returned can the caller perform code optimizations.
- * The current implementation only compares a limited set of LEAF/CONST node
- * and returns false for all othere trees.
- */
-bool Compiler::gtCompareTree(GenTree* op1, GenTree* op2)
+//------------------------------------------------------------------------
+// gtGetThisArg: Return this pointer node for the call.
+//
+// Arguments:
+// call - the call node with a this argument.
+//
+// Return value:
+// the this pointer node.
+//
+GenTree* Compiler::gtGetThisArg(GenTreeCall* call)
{
- /* Make sure that both trees are of the same GT node kind */
- if (op1->OperGet() != op2->OperGet())
- {
- return false;
- }
+ assert(call->gtCallThisArg != nullptr);
- /* Make sure that both trees are returning the same type */
- if (op1->gtType != op2->gtType)
+ GenTree* thisArg = call->gtCallThisArg->GetNode();
+ if (!thisArg->OperIs(GT_ASG))
{
- return false;
+ if ((thisArg->gtFlags & GTF_LATE_ARG) == 0)
+ {
+ return thisArg;
+ }
}
- /* Figure out what kind of a node we have */
+ assert(call->gtCallLateArgs != nullptr);
- genTreeOps oper = op1->OperGet();
- unsigned kind = op1->OperKind();
+ unsigned argNum = 0;
+ fgArgTabEntry* thisArgTabEntry = gtArgEntryByArgNum(call, argNum);
+ GenTree* result = thisArgTabEntry->GetNode();
- /* Is this a constant or leaf node? */
-
- if (kind & (GTK_CONST | GTK_LEAF))
- {
- switch (oper)
- {
- case GT_CNS_INT:
- if ((op1->AsIntCon()->gtIconVal == op2->AsIntCon()->gtIconVal) && GenTree::SameIconHandleFlag(op1, op2))
- {
- return true;
- }
- break;
-
- case GT_CNS_LNG:
- if (op1->AsLngCon()->gtLconVal == op2->AsLngCon()->gtLconVal)
- {
- return true;
- }
- break;
-
- case GT_CNS_STR:
- if (op1->AsStrCon()->gtSconCPX == op2->AsStrCon()->gtSconCPX)
- {
- return true;
- }
- break;
-
- case GT_LCL_VAR:
- if (op1->AsLclVarCommon()->GetLclNum() == op2->AsLclVarCommon()->GetLclNum())
- {
- return true;
- }
- break;
-
- case GT_CLS_VAR:
- if (op1->AsClsVar()->gtClsVarHnd == op2->AsClsVar()->gtClsVarHnd)
- {
- return true;
- }
- break;
-
- default:
- // we return false for these unhandled 'oper' kinds
- break;
- }
- }
- return false;
-}
-
-//------------------------------------------------------------------------
-// gtGetThisArg: Return this pointer node for the call.
-//
-// Arguments:
-// call - the call node with a this argument.
-//
-// Return value:
-// the this pointer node.
-//
-GenTree* Compiler::gtGetThisArg(GenTreeCall* call)
-{
- assert(call->gtCallThisArg != nullptr);
-
- GenTree* thisArg = call->gtCallThisArg->GetNode();
- if (!thisArg->OperIs(GT_ASG))
- {
- if ((thisArg->gtFlags & GTF_LATE_ARG) == 0)
- {
- return thisArg;
- }
- }
-
- assert(call->gtCallLateArgs != nullptr);
-
- unsigned argNum = 0;
- fgArgTabEntry* thisArgTabEntry = gtArgEntryByArgNum(call, argNum);
- GenTree* result = thisArgTabEntry->GetNode();
-
- // Assert if we used DEBUG_DESTROY_NODE.
- assert(result->gtOper != GT_COUNT);
-
-#if !FEATURE_FIXED_OUT_ARGS && defined(DEBUG)
- // Check that call->fgArgInfo used in gtArgEntryByArgNum was not
- // left outdated by assertion propogation updates.
- // There is no information about registers of late args for platforms
- // with FEATURE_FIXED_OUT_ARGS that is why this debug check is under
- // !FEATURE_FIXED_OUT_ARGS.
- regNumber thisReg = REG_ARG_0;
- regList list = call->regArgList;
- int index = 0;
- for (GenTreeCall::Use& use : call->LateArgs())
- {
- assert(index < call->regArgListCount);
- regNumber curArgReg = list[index];
- if (curArgReg == thisReg)
- {
- assert(result == use.GetNode());
- }
-
- index++;
- }
-#endif // !FEATURE_FIXED_OUT_ARGS && defined(DEBUG)
+ // Assert if we used DEBUG_DESTROY_NODE.
+ assert(result->gtOper != GT_COUNT);
return result;
}
@@ -8859,514 +8441,162 @@ bool GenTree::gtRequestSetFlags()
return result;
}
-unsigned GenTree::NumChildren()
+GenTreeUseEdgeIterator::GenTreeUseEdgeIterator()
+ : m_advance(nullptr), m_node(nullptr), m_edge(nullptr), m_statePtr(nullptr), m_state(-1)
{
- if (OperIsConst() || OperIsLeaf())
- {
- return 0;
- }
- else if (OperIsUnary())
- {
- if (AsUnOp()->gtOp1 == nullptr)
- {
- return 0;
- }
- else
- {
- return 1;
- }
- }
- else if (OperIsBinary())
+}
+
+GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node)
+ : m_advance(nullptr), m_node(node), m_edge(nullptr), m_statePtr(nullptr), m_state(0)
+{
+ assert(m_node != nullptr);
+
+ // NOTE: the switch statement below must be updated when introducing new nodes.
+
+ switch (m_node->OperGet())
{
- // All binary operators except LEA have at least one arg; the second arg may sometimes be null, however.
- if (OperGet() == GT_LEA)
- {
- unsigned childCount = 0;
- if (AsOp()->gtOp1 != nullptr)
+ // Leaf nodes
+ case GT_LCL_VAR:
+ case GT_LCL_FLD:
+ case GT_LCL_VAR_ADDR:
+ case GT_LCL_FLD_ADDR:
+ case GT_CATCH_ARG:
+ case GT_LABEL:
+ case GT_FTN_ADDR:
+ case GT_RET_EXPR:
+ case GT_CNS_INT:
+ case GT_CNS_LNG:
+ case GT_CNS_DBL:
+ case GT_CNS_STR:
+ case GT_MEMORYBARRIER:
+ case GT_JMP:
+ case GT_JCC:
+ case GT_SETCC:
+ case GT_NO_OP:
+ case GT_START_NONGC:
+ case GT_START_PREEMPTGC:
+ case GT_PROF_HOOK:
+#if !defined(FEATURE_EH_FUNCLETS)
+ case GT_END_LFIN:
+#endif // !FEATURE_EH_FUNCLETS
+ case GT_PHI_ARG:
+ case GT_JMPTABLE:
+ case GT_CLS_VAR:
+ case GT_CLS_VAR_ADDR:
+ case GT_ARGPLACE:
+ case GT_PHYSREG:
+ case GT_EMITNOP:
+ case GT_PINVOKE_PROLOG:
+ case GT_PINVOKE_EPILOG:
+ case GT_IL_OFFSET:
+ m_state = -1;
+ return;
+
+ // Standard unary operators
+ case GT_STORE_LCL_VAR:
+ case GT_STORE_LCL_FLD:
+ case GT_NOT:
+ case GT_NEG:
+ case GT_COPY:
+ case GT_RELOAD:
+ case GT_ARR_LENGTH:
+ case GT_CAST:
+ case GT_BITCAST:
+ case GT_CKFINITE:
+ case GT_LCLHEAP:
+ case GT_ADDR:
+ case GT_IND:
+ case GT_OBJ:
+ case GT_BLK:
+ case GT_BOX:
+ case GT_ALLOCOBJ:
+ case GT_RUNTIMELOOKUP:
+ case GT_INIT_VAL:
+ case GT_JTRUE:
+ case GT_SWITCH:
+ case GT_NULLCHECK:
+ case GT_PUTARG_REG:
+ case GT_PUTARG_STK:
+ case GT_PUTARG_TYPE:
+ case GT_BSWAP:
+ case GT_BSWAP16:
+ case GT_KEEPALIVE:
+ case GT_INC_SATURATE:
+#if FEATURE_ARG_SPLIT
+ case GT_PUTARG_SPLIT:
+#endif // FEATURE_ARG_SPLIT
+ case GT_RETURNTRAP:
+ m_edge = &m_node->AsUnOp()->gtOp1;
+ assert(*m_edge != nullptr);
+ m_advance = &GenTreeUseEdgeIterator::Terminate;
+ return;
+
+ // Unary operators with an optional operand
+ case GT_NOP:
+ case GT_FIELD:
+ case GT_RETURN:
+ case GT_RETFILT:
+ if (m_node->AsUnOp()->gtOp1 == nullptr)
{
- childCount++;
+ assert(m_node->NullOp1Legal());
+ m_state = -1;
}
- if (AsOp()->gtOp2 != nullptr)
+ else
{
- childCount++;
+ m_edge = &m_node->AsUnOp()->gtOp1;
+ m_advance = &GenTreeUseEdgeIterator::Terminate;
}
- return childCount;
- }
+ return;
+
+// Variadic nodes
+#ifdef FEATURE_SIMD
+ case GT_SIMD:
+#endif
#ifdef FEATURE_HW_INTRINSICS
- // GT_HWINTRINSIC require special handling
- if (OperGet() == GT_HWINTRINSIC)
- {
- if (AsOp()->gtOp1 == nullptr)
- {
- return 0;
- }
- }
+ case GT_HWINTRINSIC:
#endif
- assert(AsOp()->gtOp1 != nullptr);
- if (AsOp()->gtOp2 == nullptr)
- {
- return 1;
- }
- else
- {
- return 2;
- }
- }
- else
- {
- // Special
- switch (OperGet())
- {
- case GT_PHI:
+#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+ SetEntryStateForMultiOp();
+ return;
+#endif // defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+
+ // LEA, which may have no first operand
+ case GT_LEA:
+ if (m_node->AsAddrMode()->gtOp1 == nullptr)
{
- unsigned count = 0;
- for (GenTreePhi::Use& use : AsPhi()->Uses())
- {
- count++;
- }
- return count;
+ m_edge = &m_node->AsAddrMode()->gtOp2;
+ m_advance = &GenTreeUseEdgeIterator::Terminate;
}
-
- case GT_FIELD_LIST:
+ else
{
- unsigned count = 0;
- for (GenTreeFieldList::Use& use : AsFieldList()->Uses())
- {
- count++;
- }
- return count;
+ SetEntryStateForBinOp();
}
+ return;
- case GT_CMPXCHG:
- return 3;
+ // Special nodes
+ case GT_FIELD_LIST:
+ m_statePtr = m_node->AsFieldList()->Uses().GetHead();
+ m_advance = &GenTreeUseEdgeIterator::AdvanceFieldList;
+ AdvanceFieldList();
+ return;
- case GT_ARR_ELEM:
- return 1 + AsArrElem()->gtArrRank;
+ case GT_PHI:
+ m_statePtr = m_node->AsPhi()->gtUses;
+ m_advance = &GenTreeUseEdgeIterator::AdvancePhi;
+ AdvancePhi();
+ return;
- case GT_DYN_BLK:
- return 2;
+ case GT_CMPXCHG:
+ m_edge = &m_node->AsCmpXchg()->gtOpLocation;
+ assert(*m_edge != nullptr);
+ m_advance = &GenTreeUseEdgeIterator::AdvanceCmpXchg;
+ return;
- case GT_ARR_OFFSET:
- case GT_STORE_DYN_BLK:
- return 3;
-
- case GT_CALL:
- {
- GenTreeCall* call = AsCall();
- unsigned res = 0;
- if (call->gtCallThisArg != nullptr)
- {
- res++;
- }
- for (GenTreeCall::Use& use : call->Args())
- {
- res++;
- }
- for (GenTreeCall::Use& use : call->LateArgs())
- {
- res++;
- }
- if (call->gtControlExpr != nullptr)
- {
- res++;
- }
-
- if (call->gtCallType == CT_INDIRECT)
- {
- if (call->gtCallCookie != nullptr)
- {
- res++;
- }
- if (call->gtCallAddr != nullptr)
- {
- res++;
- }
- }
- return res;
- }
- case GT_NONE:
- return 0;
- default:
- unreached();
- }
- }
-}
-
-GenTree* GenTree::GetChild(unsigned childNum)
-{
- assert(childNum < NumChildren()); // Precondition.
- assert(!(OperIsConst() || OperIsLeaf()));
- if (OperIsUnary())
- {
- return AsUnOp()->gtOp1;
- }
- // Special case for assignment of dynamic block.
- // This code is here to duplicate the former case where the size may be evaluated prior to the
- // source and destination addresses. In order to do this, we treat the size as a child of the
- // assignment.
- // TODO-1stClassStructs: Revisit the need to duplicate former behavior, so that we can remove
- // these special cases.
- if ((OperGet() == GT_ASG) && (AsOp()->gtOp1->OperGet() == GT_DYN_BLK) && (childNum == 2))
- {
- return AsOp()->gtOp1->AsDynBlk()->gtDynamicSize;
- }
- else if (OperIsBinary())
- {
- if (OperIsAddrMode())
- {
- // If this is the first (0th) child, only return op1 if it is non-null
- // Otherwise, we return gtOp2.
- if (childNum == 0 && AsOp()->gtOp1 != nullptr)
- {
- return AsOp()->gtOp1;
- }
- return AsOp()->gtOp2;
- }
- // TODO-Cleanup: Consider handling ReverseOps here, and then we wouldn't have to handle it in
- // fgGetFirstNode(). However, it seems that it causes loop hoisting behavior to change.
- if (childNum == 0)
- {
- return AsOp()->gtOp1;
- }
- else
- {
- return AsOp()->gtOp2;
- }
- }
- else
- {
- // Special
- switch (OperGet())
- {
- case GT_PHI:
- for (GenTreePhi::Use& use : AsPhi()->Uses())
- {
- if (childNum == 0)
- {
- return use.GetNode();
- }
- childNum--;
- }
- unreached();
-
- case GT_FIELD_LIST:
- for (GenTreeFieldList::Use& use : AsFieldList()->Uses())
- {
- if (childNum == 0)
- {
- return use.GetNode();
- }
- childNum--;
- }
- unreached();
-
- case GT_CMPXCHG:
- switch (childNum)
- {
- case 0:
- return AsCmpXchg()->gtOpLocation;
- case 1:
- return AsCmpXchg()->gtOpValue;
- case 2:
- return AsCmpXchg()->gtOpComparand;
- default:
- unreached();
- }
-
- case GT_STORE_DYN_BLK:
- switch (childNum)
- {
- case 0:
- return AsDynBlk()->Addr();
- case 1:
- return AsDynBlk()->Data();
- case 2:
- return AsDynBlk()->gtDynamicSize;
- default:
- unreached();
- }
- case GT_DYN_BLK:
- switch (childNum)
- {
- case 0:
- return AsDynBlk()->gtEvalSizeFirst ? AsDynBlk()->gtDynamicSize : AsDynBlk()->Addr();
- case 1:
- return AsDynBlk()->gtEvalSizeFirst ? AsDynBlk()->Addr() : AsDynBlk()->gtDynamicSize;
- default:
- unreached();
- }
-
- case GT_ARR_ELEM:
- if (childNum == 0)
- {
- return AsArrElem()->gtArrObj;
- }
- else
- {
- return AsArrElem()->gtArrInds[childNum - 1];
- }
-
- case GT_ARR_OFFSET:
- switch (childNum)
- {
- case 0:
- return AsArrOffs()->gtOffset;
- case 1:
- return AsArrOffs()->gtIndex;
- case 2:
- return AsArrOffs()->gtArrObj;
- default:
- unreached();
- }
-
- case GT_CALL:
- {
- GenTreeCall* call = AsCall();
-
- if (call->gtCallThisArg != nullptr)
- {
- if (childNum == 0)
- {
- return call->gtCallThisArg->GetNode();
- }
-
- childNum--;
- }
-
- for (GenTreeCall::Use& use : call->Args())
- {
- if (childNum == 0)
- {
- return use.GetNode();
- }
-
- childNum--;
- }
-
- for (GenTreeCall::Use& use : call->LateArgs())
- {
- if (childNum == 0)
- {
- return use.GetNode();
- }
-
- childNum--;
- }
-
- if (call->gtControlExpr != nullptr)
- {
- if (childNum == 0)
- {
- return call->gtControlExpr;
- }
-
- childNum--;
- }
-
- if ((call->gtCallType == CT_INDIRECT) && (call->gtCallCookie != nullptr))
- {
- if (childNum == 0)
- {
- return call->gtCallCookie;
- }
-
- childNum--;
- }
-
- if (call->gtCallAddr != nullptr)
- {
- if (childNum == 0)
- {
- return call->gtCallAddr;
- }
- }
-
- unreached();
- }
- case GT_NONE:
- unreached();
- default:
- unreached();
- }
- }
-}
-
-GenTreeUseEdgeIterator::GenTreeUseEdgeIterator()
- : m_advance(nullptr), m_node(nullptr), m_edge(nullptr), m_statePtr(nullptr), m_state(-1)
-{
-}
-
-GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node)
- : m_advance(nullptr), m_node(node), m_edge(nullptr), m_statePtr(nullptr), m_state(0)
-{
- assert(m_node != nullptr);
-
- // NOTE: the switch statement below must be updated when introducing new nodes.
-
- switch (m_node->OperGet())
- {
- // Leaf nodes
- case GT_LCL_VAR:
- case GT_LCL_FLD:
- case GT_LCL_VAR_ADDR:
- case GT_LCL_FLD_ADDR:
- case GT_CATCH_ARG:
- case GT_LABEL:
- case GT_FTN_ADDR:
- case GT_RET_EXPR:
- case GT_CNS_INT:
- case GT_CNS_LNG:
- case GT_CNS_DBL:
- case GT_CNS_STR:
- case GT_MEMORYBARRIER:
- case GT_JMP:
- case GT_JCC:
- case GT_SETCC:
- case GT_NO_OP:
- case GT_START_NONGC:
- case GT_START_PREEMPTGC:
- case GT_PROF_HOOK:
-#if !defined(FEATURE_EH_FUNCLETS)
- case GT_END_LFIN:
-#endif // !FEATURE_EH_FUNCLETS
- case GT_PHI_ARG:
- case GT_JMPTABLE:
- case GT_CLS_VAR:
- case GT_CLS_VAR_ADDR:
- case GT_ARGPLACE:
- case GT_PHYSREG:
- case GT_EMITNOP:
- case GT_PINVOKE_PROLOG:
- case GT_PINVOKE_EPILOG:
- case GT_IL_OFFSET:
- m_state = -1;
- return;
-
- // Standard unary operators
- case GT_STORE_LCL_VAR:
- case GT_STORE_LCL_FLD:
- case GT_NOT:
- case GT_NEG:
- case GT_COPY:
- case GT_RELOAD:
- case GT_ARR_LENGTH:
- case GT_CAST:
- case GT_BITCAST:
- case GT_CKFINITE:
- case GT_LCLHEAP:
- case GT_ADDR:
- case GT_IND:
- case GT_OBJ:
- case GT_BLK:
- case GT_BOX:
- case GT_ALLOCOBJ:
- case GT_RUNTIMELOOKUP:
- case GT_INIT_VAL:
- case GT_JTRUE:
- case GT_SWITCH:
- case GT_NULLCHECK:
- case GT_PUTARG_REG:
- case GT_PUTARG_STK:
- case GT_PUTARG_TYPE:
- case GT_BSWAP:
- case GT_BSWAP16:
- case GT_KEEPALIVE:
- case GT_INC_SATURATE:
-#if FEATURE_ARG_SPLIT
- case GT_PUTARG_SPLIT:
-#endif // FEATURE_ARG_SPLIT
- case GT_RETURNTRAP:
- m_edge = &m_node->AsUnOp()->gtOp1;
- assert(*m_edge != nullptr);
- m_advance = &GenTreeUseEdgeIterator::Terminate;
- return;
-
- // Unary operators with an optional operand
- case GT_NOP:
- case GT_FIELD:
- case GT_RETURN:
- case GT_RETFILT:
- if (m_node->AsUnOp()->gtOp1 == nullptr)
- {
- assert(m_node->NullOp1Legal());
- m_state = -1;
- }
- else
- {
- m_edge = &m_node->AsUnOp()->gtOp1;
- m_advance = &GenTreeUseEdgeIterator::Terminate;
- }
- return;
-
-// Variadic nodes
-#ifdef FEATURE_SIMD
- case GT_SIMD:
- if (m_node->AsSIMD()->gtSIMDIntrinsicID == SIMDIntrinsicInitN)
- {
- SetEntryStateForList(m_node->AsSIMD()->gtOp1->AsArgList());
- }
- else
- {
- SetEntryStateForBinOp();
- }
- return;
-#endif // FEATURE_SIMD
-
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HWINTRINSIC:
- if (m_node->AsHWIntrinsic()->gtOp1 == nullptr)
- {
- assert(m_node->NullOp1Legal());
- m_state = -1;
- }
- else if (m_node->AsHWIntrinsic()->gtOp1->OperIsList())
- {
- SetEntryStateForList(m_node->AsHWIntrinsic()->gtOp1->AsArgList());
- }
- else
- {
- SetEntryStateForBinOp();
- }
- return;
-#endif // FEATURE_HW_INTRINSICS
-
- // LEA, which may have no first operand
- case GT_LEA:
- if (m_node->AsAddrMode()->gtOp1 == nullptr)
- {
- m_edge = &m_node->AsAddrMode()->gtOp2;
- m_advance = &GenTreeUseEdgeIterator::Terminate;
- }
- else
- {
- SetEntryStateForBinOp();
- }
- return;
-
- // Special nodes
- case GT_FIELD_LIST:
- m_statePtr = m_node->AsFieldList()->Uses().GetHead();
- m_advance = &GenTreeUseEdgeIterator::AdvanceFieldList;
- AdvanceFieldList();
- return;
-
- case GT_PHI:
- m_statePtr = m_node->AsPhi()->gtUses;
- m_advance = &GenTreeUseEdgeIterator::AdvancePhi;
- AdvancePhi();
- return;
-
- case GT_CMPXCHG:
- m_edge = &m_node->AsCmpXchg()->gtOpLocation;
- assert(*m_edge != nullptr);
- m_advance = &GenTreeUseEdgeIterator::AdvanceCmpXchg;
- return;
-
- case GT_ARR_ELEM:
- m_edge = &m_node->AsArrElem()->gtArrObj;
- assert(*m_edge != nullptr);
- m_advance = &GenTreeUseEdgeIterator::AdvanceArrElem;
- return;
+ case GT_ARR_ELEM:
+ m_edge = &m_node->AsArrElem()->gtArrObj;
+ assert(*m_edge != nullptr);
+ m_advance = &GenTreeUseEdgeIterator::AdvanceArrElem;
+ return;
case GT_ARR_OFFSET:
m_edge = &m_node->AsArrOffs()->gtOffset;
@@ -9616,37 +8846,76 @@ void GenTreeUseEdgeIterator::SetEntryStateForBinOp()
}
}
+#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
//------------------------------------------------------------------------
-// GenTreeUseEdgeIterator::AdvanceList: produces the next operand of a variadic node and advances the state.
+// GenTreeUseEdgeIterator::AdvanceMultiOp: produces the next operand of a multi-op node and advances the state.
//
-// This function does not use `m_state` for anything meaningful; it simply walks the `m_argList` until
-// there are no further entries.
+// Takes advantage of the fact that GenTreeMultiOp stores the operands in a contigious array, simply
+// incrementing the "m_edge" pointer, unless the end, stored in "m_statePtr", has been reached.
//
-void GenTreeUseEdgeIterator::AdvanceList()
+void GenTreeUseEdgeIterator::AdvanceMultiOp()
{
- assert(m_state == 0);
+ assert(m_node != nullptr);
+ assert(m_node->OperIs(GT_SIMD, GT_HWINTRINSIC));
- if (m_statePtr == nullptr)
+ m_edge++;
+ if (m_edge == m_statePtr)
{
- m_state = -1;
+ Terminate();
}
- else
+}
+
+//------------------------------------------------------------------------
+// GenTreeUseEdgeIterator::AdvanceReversedMultiOp: produces the next operand of a multi-op node
+// marked with GTF_REVRESE_OPS and advances the state.
+//
+// Takes advantage of the fact that GenTreeMultiOp stores the operands in a contigious array, simply
+// decrementing the "m_edge" pointer, unless the beginning, stored in "m_statePtr", has been reached.
+//
+void GenTreeUseEdgeIterator::AdvanceReversedMultiOp()
+{
+ assert(m_node != nullptr);
+ assert(m_node->OperIs(GT_SIMD, GT_HWINTRINSIC));
+ assert((m_node->AsMultiOp()->GetOperandCount() == 2) && m_node->IsReverseOp());
+
+ m_edge--;
+ if (m_edge == m_statePtr)
{
- GenTreeArgList* listNode = static_cast(m_statePtr);
- m_edge = &listNode->gtOp1;
- m_statePtr = listNode->Rest();
+ Terminate();
}
}
//------------------------------------------------------------------------
-// GenTreeUseEdgeIterator::SetEntryStateForList: produces the first operand of a list node.
+// GenTreeUseEdgeIterator::SetEntryStateForMultiOp: produces the first operand of a multi-op node and sets the
+// required advance function.
//
-void GenTreeUseEdgeIterator::SetEntryStateForList(GenTreeArgList* list)
+void GenTreeUseEdgeIterator::SetEntryStateForMultiOp()
{
- m_statePtr = list;
- m_advance = &GenTreeUseEdgeIterator::AdvanceList;
- AdvanceList();
+ size_t operandCount = m_node->AsMultiOp()->GetOperandCount();
+
+ if (operandCount == 0)
+ {
+ Terminate();
+ }
+ else
+ {
+ if (m_node->IsReverseOp())
+ {
+ assert(operandCount == 2);
+
+ m_edge = m_node->AsMultiOp()->GetOperandArray() + 1;
+ m_statePtr = m_node->AsMultiOp()->GetOperandArray() - 1;
+ m_advance = &GenTreeUseEdgeIterator::AdvanceReversedMultiOp;
+ }
+ else
+ {
+ m_edge = m_node->AsMultiOp()->GetOperandArray();
+ m_statePtr = m_node->AsMultiOp()->GetOperandArray(operandCount);
+ m_advance = &GenTreeUseEdgeIterator::AdvanceMultiOp;
+ }
+ }
}
+#endif
//------------------------------------------------------------------------
// GenTreeUseEdgeIterator::AdvanceCall: produces the next operand of a call node and advances the state.
@@ -10476,12 +9745,6 @@ void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, __in __in_z _
--msgLength;
break;
}
- if (tree->gtFlags & GTF_RELOP_QMARK)
- {
- printf("Q");
- --msgLength;
- break;
- }
goto DASH;
case GT_JCMP:
@@ -10524,7 +9787,7 @@ void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, __in __in_z _
/* Then print the general purpose flags */
GenTreeFlags flags = tree->gtFlags;
- if (tree->OperIsBinary())
+ if (tree->OperIsBinary() || tree->OperIsMultiOp())
{
genTreeOps oper = tree->OperGet();
@@ -10537,9 +9800,9 @@ void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, __in __in_z _
}
}
}
- else // !tree->OperIsBinary()
+ else // !(tree->OperIsBinary() || tree->OperIsMultiOp())
{
- // the GTF_REVERSE flag only applies to binary operations
+ // the GTF_REVERSE flag only applies to binary operations (which some MultiOp nodes are).
flags &= ~GTF_REVERSE_OPS; // we use this value for GTF_VAR_ARR_INDEX above
}
@@ -10640,7 +9903,7 @@ void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, __in __in_z _
if (tree->gtOper == GT_LCL_VAR || tree->gtOper == GT_STORE_LCL_VAR)
{
- LclVarDsc* varDsc = &lvaTable[tree->AsLclVarCommon()->GetLclNum()];
+ LclVarDsc* varDsc = lvaGetDesc(tree->AsLclVarCommon());
if (varDsc->IsAddressExposed())
{
printf("(AX)"); // Variable has address exposed.
@@ -10864,12 +10127,6 @@ void Compiler::gtGetLclVarNameInfo(unsigned lclNum, const char** ilKindOut, cons
ilName = "OutArgs";
}
#endif // FEATURE_FIXED_OUT_ARGS
-#ifdef TARGET_ARM
- else if (lclNum == lvaPromotedStructAssemblyScratchVar)
- {
- ilName = "PromotedStructScratch";
- }
-#endif // TARGET_ARM
#if !defined(FEATURE_EH_FUNCLETS)
else if (lclNum == lvaShadowSPslotsVar)
{
@@ -10993,7 +10250,7 @@ int Compiler::gtGetLclVarName(unsigned lclNum, char* buf, unsigned buf_remaining
char* Compiler::gtGetLclVarName(unsigned lclNum)
{
char buf[BUF_SIZE];
- int charsPrinted = gtGetLclVarName(lclNum, buf, _countof(buf));
+ int charsPrinted = gtGetLclVarName(lclNum, buf, ArrLen(buf));
if (charsPrinted < 0)
{
return nullptr;
@@ -11008,7 +10265,7 @@ char* Compiler::gtGetLclVarName(unsigned lclNum)
void Compiler::gtDispLclVar(unsigned lclNum, bool padForBiggestDisp)
{
char buf[BUF_SIZE];
- int charsPrinted = gtGetLclVarName(lclNum, buf, _countof(buf));
+ int charsPrinted = gtGetLclVarName(lclNum, buf, ArrLen(buf));
if (charsPrinted < 0)
{
@@ -11364,7 +10621,7 @@ void Compiler::gtDispLeaf(GenTree* tree, IndentStack* indentStack)
for (unsigned i = varDsc->lvFieldLclStart; i < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++i)
{
- LclVarDsc* fieldVarDsc = &lvaTable[i];
+ LclVarDsc* fieldVarDsc = lvaGetDesc(i);
const char* fieldName;
#if !defined(TARGET_64BIT)
if (varTypeIsLong(varDsc))
@@ -11476,15 +10733,8 @@ void Compiler::gtDispLeaf(GenTree* tree, IndentStack* indentStack)
break;
case GT_IL_OFFSET:
- printf(" IL offset: ");
- if (tree->AsILOffset()->gtStmtILoffsx == BAD_IL_OFFSET)
- {
- printf("???");
- }
- else
- {
- printf("0x%x", jitGetILoffs(tree->AsILOffset()->gtStmtILoffsx));
- }
+ printf(" ");
+ tree->AsILOffset()->gtStmtDI.Dump(true);
break;
case GT_JCC:
@@ -11866,53 +11116,24 @@ void Compiler::gtDispTree(GenTree* tree,
}
}
-#ifdef FEATURE_SIMD
- if (tree->gtOper == GT_SIMD)
- {
- printf(" %s %s", varTypeName(tree->AsSIMD()->GetSimdBaseType()),
- simdIntrinsicNames[tree->AsSIMD()->gtSIMDIntrinsicID]);
- }
-#endif // FEATURE_SIMD
-
-#ifdef FEATURE_HW_INTRINSICS
- if (tree->gtOper == GT_HWINTRINSIC)
- {
- printf(" %s %s", tree->AsHWIntrinsic()->GetSimdBaseType() == TYP_UNKNOWN
- ? ""
- : varTypeName(tree->AsHWIntrinsic()->GetSimdBaseType()),
- HWIntrinsicInfo::lookupName(tree->AsHWIntrinsic()->gtHWIntrinsicId));
- }
-#endif // FEATURE_HW_INTRINSICS
-
gtDispCommonEndLine(tree);
if (!topOnly)
{
if (tree->AsOp()->gtOp1 != nullptr)
{
- if (tree->OperIs(GT_PHI))
+ // Label the child of the GT_COLON operator
+ // op1 is the else part
+ if (tree->gtOper == GT_COLON)
{
- for (GenTreeArgList* args = tree->gtGetOp1()->AsArgList(); args != nullptr; args = args->Rest())
- {
- gtDispChild(args->Current(), indentStack, (args->Rest() == nullptr) ? IIArcBottom : IIArc);
- }
+ childMsg = "else";
}
- else
+ else if (tree->gtOper == GT_QMARK)
{
- // Label the child of the GT_COLON operator
- // op1 is the else part
-
- if (tree->gtOper == GT_COLON)
- {
- childMsg = "else";
- }
- else if (tree->gtOper == GT_QMARK)
- {
- childMsg = " if";
- }
- gtDispChild(tree->AsOp()->gtOp1, indentStack,
- (tree->gtGetOp2IfPresent() == nullptr) ? IIArcBottom : IIArc, childMsg, topOnly);
+ childMsg = " if";
}
+ gtDispChild(tree->AsOp()->gtOp1, indentStack,
+ (tree->gtGetOp2IfPresent() == nullptr) ? IIArcBottom : IIArc, childMsg, topOnly);
}
if (tree->gtGetOp2IfPresent())
@@ -11980,14 +11201,12 @@ void Compiler::gtDispTree(GenTree* tree,
case GT_CALL:
{
- GenTreeCall* call = tree->AsCall();
- assert(call->gtFlags & GTF_CALL);
- unsigned numChildren = call->NumChildren();
- GenTree* lastChild = nullptr;
- if (numChildren != 0)
- {
- lastChild = call->GetChild(numChildren - 1);
- }
+ GenTreeCall* call = tree->AsCall();
+ GenTree* lastChild = nullptr;
+ call->VisitOperands([&lastChild](GenTree* operand) -> GenTree::VisitResult {
+ lastChild = operand;
+ return GenTree::VisitResult::Continue;
+ });
if (call->gtCallType != CT_INDIRECT)
{
@@ -12035,7 +11254,7 @@ void Compiler::gtDispTree(GenTree* tree,
if (call->gtCallArgs)
{
- gtDispArgList(call, indentStack);
+ gtDispArgList(call, lastChild, indentStack);
}
if (call->gtCallType == CT_INDIRECT)
@@ -12050,9 +11269,6 @@ void Compiler::gtDispTree(GenTree* tree,
(call->gtControlExpr == lastChild) ? IIArcBottom : IIArc, "control expr", topOnly);
}
-#if !FEATURE_FIXED_OUT_ARGS
- regList list = call->regArgList;
-#endif
int lateArgIndex = 0;
for (GenTreeCall::Use& use : call->LateArgs())
{
@@ -12065,6 +11281,45 @@ void Compiler::gtDispTree(GenTree* tree,
}
break;
+#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+#if defined(FEATURE_SIMD)
+ case GT_SIMD:
+#endif
+#if defined(FEATURE_HW_INTRINSICS)
+ case GT_HWINTRINSIC:
+#endif
+
+#if defined(FEATURE_SIMD)
+ if (tree->OperIs(GT_SIMD))
+ {
+ printf(" %s %s", varTypeName(tree->AsSIMD()->GetSimdBaseType()),
+ simdIntrinsicNames[tree->AsSIMD()->GetSIMDIntrinsicId()]);
+ }
+#endif // defined(FEATURE_SIMD)
+#if defined(FEATURE_HW_INTRINSICS)
+ if (tree->OperIs(GT_HWINTRINSIC))
+ {
+ printf(" %s %s", tree->AsHWIntrinsic()->GetSimdBaseType() == TYP_UNKNOWN
+ ? ""
+ : varTypeName(tree->AsHWIntrinsic()->GetSimdBaseType()),
+ HWIntrinsicInfo::lookupName(tree->AsHWIntrinsic()->GetHWIntrinsicId()));
+ }
+#endif // defined(FEATURE_HW_INTRINSICS)
+
+ gtDispCommonEndLine(tree);
+
+ if (!topOnly)
+ {
+ size_t index = 0;
+ size_t count = tree->AsMultiOp()->GetOperandCount();
+ for (GenTree* operand : tree->AsMultiOp()->Operands())
+ {
+ gtDispChild(operand, indentStack, ++index < count ? IIArc : IIArcBottom, nullptr, topOnly);
+ }
+ }
+ break;
+#endif // defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+
case GT_ARR_ELEM:
gtDispCommonEndLine(tree);
@@ -12231,10 +11486,7 @@ void Compiler::gtGetLateArgMsg(GenTreeCall* call, GenTree* argx, int lateArgInde
assert(curArgTabEntry);
regNumber argReg = curArgTabEntry->GetRegNum();
-#if !FEATURE_FIXED_OUT_ARGS
- assert(lateArgIndex < call->regArgListCount);
- assert(argReg == call->regArgList[lateArgIndex]);
-#else
+#if FEATURE_FIXED_OUT_ARGS
if (argReg == REG_STK)
{
sprintf_s(bufp, bufLength, "arg%d in out+%02x%c", curArgTabEntry->argNum, curArgTabEntry->GetByteOffset(), 0);
@@ -12298,17 +11550,15 @@ void Compiler::gtGetLateArgMsg(GenTreeCall* call, GenTree* argx, int lateArgInde
// gtDispArgList: Dump the tree for a call arg list
//
// Arguments:
-// call - The call to dump arguments for
-// indentStack - the specification for the current level of indentation & arcs
+// call - the call to dump arguments for
+// lastCallOperand - the call's last operand (to determine the arc types)
+// indentStack - the specification for the current level of indentation & arcs
//
// Return Value:
// None.
//
-void Compiler::gtDispArgList(GenTreeCall* call, IndentStack* indentStack)
+void Compiler::gtDispArgList(GenTreeCall* call, GenTree* lastCallOperand, IndentStack* indentStack)
{
- unsigned numChildren = call->NumChildren();
- GenTree* lastArgNode = call->GetChild(numChildren - 1);
-
unsigned argnum = 0;
if (call->gtCallThisArg != nullptr)
@@ -12323,7 +11573,7 @@ void Compiler::gtDispArgList(GenTreeCall* call, IndentStack* indentStack)
{
char buf[256];
gtGetArgMsg(call, argNode, argnum, buf, sizeof(buf));
- gtDispChild(argNode, indentStack, (argNode == lastArgNode) ? IIArcBottom : IIArc, buf, false);
+ gtDispChild(argNode, indentStack, (argNode == lastCallOperand) ? IIArcBottom : IIArc, buf, false);
}
argnum++;
}
@@ -12344,28 +11594,40 @@ void Compiler::gtDispStmt(Statement* stmt, const char* msg /* = nullptr */)
printf("%s ", msg);
}
printStmtID(stmt);
- IL_OFFSETX firstILOffsx = stmt->GetILOffsetX();
- printf(" (IL ");
- if (firstILOffsx == BAD_IL_OFFSET)
+ printf(" ( ");
+ const DebugInfo& di = stmt->GetDebugInfo();
+ // For statements in the root we display just the location without the
+ // inline context info.
+ if (di.GetInlineContext() == nullptr || di.GetInlineContext()->IsRoot())
{
- printf(" ???");
+ di.GetLocation().Dump();
}
else
{
- printf("0x%03X", jitGetILoffs(firstILOffsx));
+ stmt->GetDebugInfo().Dump(false);
}
- printf("...");
+ printf(" ... ");
IL_OFFSET lastILOffs = stmt->GetLastILOffset();
if (lastILOffs == BAD_IL_OFFSET)
{
- printf(" ???");
+ printf("???");
}
else
{
printf("0x%03X", lastILOffs);
}
- printf(")\n");
+
+ printf(" )");
+
+ DebugInfo par;
+ if (stmt->GetDebugInfo().GetParent(&par))
+ {
+ printf(" <- ");
+ par.Dump(true);
+ }
+
+ printf("\n");
}
gtDispTree(stmt->GetRootNode());
}
@@ -12495,7 +11757,6 @@ void Compiler::gtDispLIRNode(GenTree* node, const char* prefixMsg /* = nullptr *
{
fgArgTabEntry* curArgTabEntry = gtArgEntryByNode(call, operand);
assert(curArgTabEntry);
- assert(operand->OperGet() != GT_LIST);
if (!curArgTabEntry->isLateArg())
{
@@ -12987,7 +12248,7 @@ GenTree* Compiler::gtFoldTypeCompare(GenTree* tree)
GenTree* compare = gtCreateHandleCompare(oper, op1ClassFromHandle, op2ClassFromHandle, inliningKind);
// Drop any now-irrelvant flags
- compare->gtFlags |= tree->gtFlags & (GTF_RELOP_JMP_USED | GTF_RELOP_QMARK | GTF_DONT_CSE);
+ compare->gtFlags |= tree->gtFlags & (GTF_RELOP_JMP_USED | GTF_DONT_CSE);
return compare;
}
@@ -13027,7 +12288,7 @@ GenTree* Compiler::gtFoldTypeCompare(GenTree* tree)
GenTree* compare = gtCreateHandleCompare(oper, arg1, arg2, inliningKind);
// Drop any now-irrelvant flags
- compare->gtFlags |= tree->gtFlags & (GTF_RELOP_JMP_USED | GTF_RELOP_QMARK | GTF_DONT_CSE);
+ compare->gtFlags |= tree->gtFlags & (GTF_RELOP_JMP_USED | GTF_DONT_CSE);
return compare;
}
@@ -13125,7 +12386,7 @@ GenTree* Compiler::gtFoldTypeCompare(GenTree* tree)
GenTree* const compare = gtCreateHandleCompare(oper, objMT, knownMT, typeCheckInliningResult);
// Drop any now irrelevant flags
- compare->gtFlags |= tree->gtFlags & (GTF_RELOP_JMP_USED | GTF_RELOP_QMARK | GTF_DONT_CSE);
+ compare->gtFlags |= tree->gtFlags & (GTF_RELOP_JMP_USED | GTF_DONT_CSE);
// And we're done
return compare;
@@ -14464,11 +13725,6 @@ GenTree* Compiler::gtFoldExprConst(GenTree* tree)
return op2;
}
- if (tree->OperIsAnyList())
- {
- return tree;
- }
-
switchType = op1->TypeGet();
// Normally we will just switch on op1 types, but for the case where
@@ -15249,7 +14505,7 @@ GenTree* Compiler::gtFoldExprConst(GenTree* tree)
// May set compFloatingPointUsed.
GenTree* Compiler::gtNewTempAssign(
- unsigned tmp, GenTree* val, Statement** pAfterStmt, IL_OFFSETX ilOffset, BasicBlock* block)
+ unsigned tmp, GenTree* val, Statement** pAfterStmt, const DebugInfo& di, BasicBlock* block)
{
// Self-assignment is a nop.
if (val->OperGet() == GT_LCL_VAR && val->AsLclVarCommon()->GetLclNum() == tmp)
@@ -15257,7 +14513,7 @@ GenTree* Compiler::gtNewTempAssign(
return gtNewNothingNode();
}
- LclVarDsc* varDsc = lvaTable + tmp;
+ LclVarDsc* varDsc = lvaGetDesc(tmp);
if (varDsc->TypeGet() == TYP_I_IMPL && val->TypeGet() == TYP_BYREF)
{
@@ -15391,7 +14647,7 @@ GenTree* Compiler::gtNewTempAssign(
}
dest->gtFlags |= GTF_DONT_CSE;
valx->gtFlags |= GTF_DONT_CSE;
- asg = impAssignStruct(dest, val, valStructHnd, (unsigned)CHECK_SPILL_NONE, pAfterStmt, ilOffset, block);
+ asg = impAssignStruct(dest, val, valStructHnd, (unsigned)CHECK_SPILL_NONE, pAfterStmt, di, block);
}
else
{
@@ -15567,7 +14823,7 @@ GenTree* Compiler::gtNewRefCOMfield(GenTree* objPtr,
* assignments too.
*/
-bool Compiler::gtNodeHasSideEffects(GenTree* tree, unsigned flags)
+bool Compiler::gtNodeHasSideEffects(GenTree* tree, GenTreeFlags flags)
{
if (flags & GTF_ASG)
{
@@ -15643,10 +14899,10 @@ bool Compiler::gtNodeHasSideEffects(GenTree* tree, unsigned flags)
* Returns true if the expr tree has any side effects.
*/
-bool Compiler::gtTreeHasSideEffects(GenTree* tree, unsigned flags /* = GTF_SIDE_EFFECT*/)
+bool Compiler::gtTreeHasSideEffects(GenTree* tree, GenTreeFlags flags /* = GTF_SIDE_EFFECT*/)
{
// These are the side effect flags that we care about for this tree
- unsigned sideEffectFlags = tree->gtFlags & flags;
+ GenTreeFlags sideEffectFlags = tree->gtFlags & flags;
// Does this tree have any Side-effect flags set that we care about?
if (sideEffectFlags == 0)
@@ -15760,15 +15016,15 @@ GenTree* Compiler::gtBuildCommaList(GenTree* list, GenTree* expr)
// each comma node holds the side effect tree and op2 points to the
// next comma node. The original side effect execution order is preserved.
//
-void Compiler::gtExtractSideEffList(GenTree* expr,
- GenTree** pList,
- unsigned flags /* = GTF_SIDE_EFFECT*/,
- bool ignoreRoot /* = false */)
+void Compiler::gtExtractSideEffList(GenTree* expr,
+ GenTree** pList,
+ GenTreeFlags flags /* = GTF_SIDE_EFFECT*/,
+ bool ignoreRoot /* = false */)
{
class SideEffectExtractor final : public GenTreeVisitor
{
public:
- const unsigned m_flags;
+ const GenTreeFlags m_flags;
ArrayStack m_sideEffects;
enum
@@ -15777,7 +15033,7 @@ void Compiler::gtExtractSideEffList(GenTree* expr,
UseExecutionOrder = true
};
- SideEffectExtractor(Compiler* compiler, unsigned flags)
+ SideEffectExtractor(Compiler* compiler, GenTreeFlags flags)
: GenTreeVisitor(compiler), m_flags(flags), m_sideEffects(compiler->getAllocator(CMK_SideEffects))
{
}
@@ -15921,7 +15177,6 @@ void Compiler::gtExtractSideEffList(GenTree* expr,
*/
#ifdef DEBUG
-
void dispNodeList(GenTree* list, bool verbose)
{
GenTree* last = nullptr;
@@ -15955,21 +15210,7 @@ void dispNodeList(GenTree* list, bool verbose)
}
printf(""); // null string means flush
}
-
-/*****************************************************************************
- * Callback to assert that the nodes of a qmark-colon subtree are marked
- */
-
-/* static */
-Compiler::fgWalkResult Compiler::gtAssertColonCond(GenTree** pTree, fgWalkData* data)
-{
- assert(data->pCallbackData == nullptr);
-
- assert((*pTree)->gtFlags & GTF_COLON_COND);
-
- return WALK_CONTINUE;
-}
-#endif // DEBUG
+#endif
/*****************************************************************************
* Callback to mark the nodes of a qmark-colon subtree that are conditionally
@@ -16522,7 +15763,7 @@ GenTreeLclVarCommon* GenTree::IsLocalAddrExpr()
// Returns true if "this" represents the address of a local, or a field of a local.
//
// Notes:
-// It is mostly used for optimizations but assertion propogation depends on it for correctness.
+// It is mostly used for optimizations but assertion propagation depends on it for correctness.
// So if this function does not recognize a def of a LCL_VAR we can have an incorrect optimization.
//
bool GenTree::IsLocalAddrExpr(Compiler* comp,
@@ -18634,7 +17875,8 @@ GenTreeSIMD* Compiler::gtNewSIMDNode(
assert(op1 != nullptr);
SetOpLclRelatedToSIMDIntrinsic(op1);
- GenTreeSIMD* simdNode = new (this, GT_SIMD) GenTreeSIMD(type, op1, simdIntrinsicID, simdBaseJitType, simdSize);
+ GenTreeSIMD* simdNode = new (this, GT_SIMD)
+ GenTreeSIMD(type, getAllocator(CMK_ASTNode), op1, simdIntrinsicID, simdBaseJitType, simdSize);
return simdNode;
}
@@ -18649,7 +17891,8 @@ GenTreeSIMD* Compiler::gtNewSIMDNode(var_types type,
SetOpLclRelatedToSIMDIntrinsic(op1);
SetOpLclRelatedToSIMDIntrinsic(op2);
- GenTreeSIMD* simdNode = new (this, GT_SIMD) GenTreeSIMD(type, op1, op2, simdIntrinsicID, simdBaseJitType, simdSize);
+ GenTreeSIMD* simdNode = new (this, GT_SIMD)
+ GenTreeSIMD(type, getAllocator(CMK_ASTNode), op1, op2, simdIntrinsicID, simdBaseJitType, simdSize);
return simdNode;
}
@@ -18690,7 +17933,7 @@ void Compiler::SetOpLclRelatedToSIMDIntrinsic(GenTree* op)
bool GenTree::isCommutativeSIMDIntrinsic()
{
assert(gtOper == GT_SIMD);
- switch (AsSIMD()->gtSIMDIntrinsicID)
+ switch (AsSIMD()->GetSIMDIntrinsicId())
{
case SIMDIntrinsicBitwiseAnd:
case SIMDIntrinsicBitwiseOr:
@@ -18701,6 +17944,81 @@ bool GenTree::isCommutativeSIMDIntrinsic()
}
}
+void GenTreeMultiOp::ResetOperandArray(size_t newOperandCount,
+ Compiler* compiler,
+ GenTree** inlineOperands,
+ size_t inlineOperandCount)
+{
+ size_t oldOperandCount = GetOperandCount();
+ GenTree** oldOperands = GetOperandArray();
+
+ if (newOperandCount > oldOperandCount)
+ {
+ if (newOperandCount <= inlineOperandCount)
+ {
+ assert(oldOperandCount <= inlineOperandCount);
+ assert(oldOperands == inlineOperands);
+ }
+ else
+ {
+ // The most difficult case: we need to recreate the dynamic array.
+ assert(compiler != nullptr);
+
+ m_operands = compiler->getAllocator(CMK_ASTNode).allocate(newOperandCount);
+ }
+ }
+ else
+ {
+ // We are shrinking the array and may in process switch to an inline representation.
+ // We choose to do so for simplicity ("if a node has <= InlineOperandCount operands,
+ // then it stores them inline"), but actually it may be more profitable to not do that,
+ // it will save us a copy and a potential cache miss (though the latter seems unlikely).
+
+ if ((newOperandCount <= inlineOperandCount) && (oldOperands != inlineOperands))
+ {
+ m_operands = inlineOperands;
+ }
+ }
+
+#ifdef DEBUG
+ for (size_t i = 0; i < newOperandCount; i++)
+ {
+ m_operands[i] = nullptr;
+ }
+#endif // DEBUG
+
+ SetOperandCount(newOperandCount);
+}
+
+/* static */ bool GenTreeMultiOp::OperandsAreEqual(GenTreeMultiOp* op1, GenTreeMultiOp* op2)
+{
+ if (op1->GetOperandCount() != op2->GetOperandCount())
+ {
+ return false;
+ }
+
+ for (size_t i = 1; i <= op1->GetOperandCount(); i++)
+ {
+ if (!Compare(op1->Op(i), op2->Op(i)))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void GenTreeMultiOp::InitializeOperands(GenTree** operands, size_t operandCount)
+{
+ for (size_t i = 0; i < operandCount; i++)
+ {
+ m_operands[i] = operands[i];
+ gtFlags |= (operands[i]->gtFlags & GTF_ALL_EFFECT);
+ }
+
+ SetOperandCount(operandCount);
+}
+
var_types GenTreeJitIntrinsic::GetAuxiliaryType() const
{
CorInfoType auxiliaryJitType = GetAuxiliaryJitType();
@@ -18726,12 +18044,20 @@ var_types GenTreeJitIntrinsic::GetSimdBaseType() const
// Returns true for the SIMD Intrinsic instructions that have MemoryLoad semantics, false otherwise
bool GenTreeSIMD::OperIsMemoryLoad() const
{
- if (gtSIMDIntrinsicID == SIMDIntrinsicInitArray)
+ if (GetSIMDIntrinsicId() == SIMDIntrinsicInitArray)
{
return true;
}
return false;
}
+
+// TODO-Review: why are layouts not compared here?
+/* static */ bool GenTreeSIMD::Equals(GenTreeSIMD* op1, GenTreeSIMD* op2)
+{
+ return (op1->TypeGet() == op2->TypeGet()) && (op1->GetSIMDIntrinsicId() == op2->GetSIMDIntrinsicId()) &&
+ (op1->GetSimdBaseType() == op2->GetSimdBaseType()) && (op1->GetSimdSize() == op2->GetSimdSize()) &&
+ OperandsAreEqual(op1, op2);
+}
#endif // FEATURE_SIMD
#ifdef FEATURE_HW_INTRINSICS
@@ -18740,7 +18066,7 @@ bool GenTree::isCommutativeHWIntrinsic() const
assert(gtOper == GT_HWINTRINSIC);
#ifdef TARGET_XARCH
- return HWIntrinsicInfo::IsCommutative(AsHWIntrinsic()->gtHWIntrinsicId);
+ return HWIntrinsicInfo::IsCommutative(AsHWIntrinsic()->GetHWIntrinsicId());
#else
return false;
#endif // TARGET_XARCH
@@ -18751,7 +18077,7 @@ bool GenTree::isContainableHWIntrinsic() const
assert(gtOper == GT_HWINTRINSIC);
#ifdef TARGET_XARCH
- switch (AsHWIntrinsic()->gtHWIntrinsicId)
+ switch (AsHWIntrinsic()->GetHWIntrinsicId())
{
case NI_SSE_LoadAlignedVector128:
case NI_SSE_LoadScalarVector128:
@@ -18785,10 +18111,10 @@ bool GenTree::isRMWHWIntrinsic(Compiler* comp)
#if defined(TARGET_XARCH)
if (!comp->canUseVexEncoding())
{
- return HWIntrinsicInfo::HasRMWSemantics(AsHWIntrinsic()->gtHWIntrinsicId);
+ return HWIntrinsicInfo::HasRMWSemantics(AsHWIntrinsic()->GetHWIntrinsicId());
}
- switch (AsHWIntrinsic()->gtHWIntrinsicId)
+ switch (AsHWIntrinsic()->GetHWIntrinsicId())
{
// TODO-XArch-Cleanup: Move this switch block to be table driven.
@@ -18814,7 +18140,7 @@ bool GenTree::isRMWHWIntrinsic(Compiler* comp)
}
}
#elif defined(TARGET_ARM64)
- return HWIntrinsicInfo::HasRMWSemantics(AsHWIntrinsic()->gtHWIntrinsicId);
+ return HWIntrinsicInfo::HasRMWSemantics(AsHWIntrinsic()->GetHWIntrinsicId());
#else
return false;
#endif
@@ -18826,8 +18152,8 @@ GenTreeHWIntrinsic* Compiler::gtNewSimdHWIntrinsicNode(var_types type,
unsigned simdSize,
bool isSimdAsHWIntrinsic)
{
- return new (this, GT_HWINTRINSIC)
- GenTreeHWIntrinsic(type, hwIntrinsicID, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ return new (this, GT_HWINTRINSIC) GenTreeHWIntrinsic(type, getAllocator(CMK_ASTNode), hwIntrinsicID,
+ simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
}
GenTreeHWIntrinsic* Compiler::gtNewSimdHWIntrinsicNode(var_types type,
@@ -18839,8 +18165,8 @@ GenTreeHWIntrinsic* Compiler::gtNewSimdHWIntrinsicNode(var_types type,
{
SetOpLclRelatedToSIMDIntrinsic(op1);
- return new (this, GT_HWINTRINSIC)
- GenTreeHWIntrinsic(type, op1, hwIntrinsicID, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ return new (this, GT_HWINTRINSIC) GenTreeHWIntrinsic(type, getAllocator(CMK_ASTNode), hwIntrinsicID,
+ simdBaseJitType, simdSize, isSimdAsHWIntrinsic, op1);
}
GenTreeHWIntrinsic* Compiler::gtNewSimdHWIntrinsicNode(var_types type,
@@ -18854,8 +18180,8 @@ GenTreeHWIntrinsic* Compiler::gtNewSimdHWIntrinsicNode(var_types type,
SetOpLclRelatedToSIMDIntrinsic(op1);
SetOpLclRelatedToSIMDIntrinsic(op2);
- return new (this, GT_HWINTRINSIC)
- GenTreeHWIntrinsic(type, op1, op2, hwIntrinsicID, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ return new (this, GT_HWINTRINSIC) GenTreeHWIntrinsic(type, getAllocator(CMK_ASTNode), hwIntrinsicID,
+ simdBaseJitType, simdSize, isSimdAsHWIntrinsic, op1, op2);
}
GenTreeHWIntrinsic* Compiler::gtNewSimdHWIntrinsicNode(var_types type,
@@ -18871,8 +18197,8 @@ GenTreeHWIntrinsic* Compiler::gtNewSimdHWIntrinsicNode(var_types type,
SetOpLclRelatedToSIMDIntrinsic(op2);
SetOpLclRelatedToSIMDIntrinsic(op3);
- return new (this, GT_HWINTRINSIC) GenTreeHWIntrinsic(type, gtNewArgList(op1, op2, op3), hwIntrinsicID,
- simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ return new (this, GT_HWINTRINSIC) GenTreeHWIntrinsic(type, getAllocator(CMK_ASTNode), hwIntrinsicID,
+ simdBaseJitType, simdSize, isSimdAsHWIntrinsic, op1, op2, op3);
}
GenTreeHWIntrinsic* Compiler::gtNewSimdHWIntrinsicNode(var_types type,
@@ -18890,8 +18216,44 @@ GenTreeHWIntrinsic* Compiler::gtNewSimdHWIntrinsicNode(var_types type,
SetOpLclRelatedToSIMDIntrinsic(op3);
SetOpLclRelatedToSIMDIntrinsic(op4);
- return new (this, GT_HWINTRINSIC) GenTreeHWIntrinsic(type, gtNewArgList(op1, op2, op3, op4), hwIntrinsicID,
- simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ return new (this, GT_HWINTRINSIC)
+ GenTreeHWIntrinsic(type, getAllocator(CMK_ASTNode), hwIntrinsicID, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic, op1, op2, op3, op4);
+}
+
+GenTreeHWIntrinsic* Compiler::gtNewSimdHWIntrinsicNode(var_types type,
+ GenTree** operands,
+ size_t operandCount,
+ NamedIntrinsic hwIntrinsicID,
+ CorInfoType simdBaseJitType,
+ unsigned simdSize,
+ bool isSimdAsHWIntrinsic)
+{
+ IntrinsicNodeBuilder nodeBuilder(getAllocator(CMK_ASTNode), operandCount);
+ for (size_t i = 0; i < operandCount; i++)
+ {
+ nodeBuilder.AddOperand(i, operands[i]);
+ SetOpLclRelatedToSIMDIntrinsic(operands[i]);
+ }
+
+ return new (this, GT_HWINTRINSIC)
+ GenTreeHWIntrinsic(type, std::move(nodeBuilder), hwIntrinsicID, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+}
+
+GenTreeHWIntrinsic* Compiler::gtNewSimdHWIntrinsicNode(var_types type,
+ IntrinsicNodeBuilder&& nodeBuilder,
+ NamedIntrinsic hwIntrinsicID,
+ CorInfoType simdBaseJitType,
+ unsigned simdSize,
+ bool isSimdAsHWIntrinsic)
+{
+ for (size_t i = 0; i < nodeBuilder.GetOperandCount(); i++)
+ {
+ SetOpLclRelatedToSIMDIntrinsic(nodeBuilder.GetOperand(i));
+ }
+
+ return new (this, GT_HWINTRINSIC)
+ GenTreeHWIntrinsic(type, std::move(nodeBuilder), hwIntrinsicID, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
}
GenTree* Compiler::gtNewSimdAbsNode(
@@ -20312,130 +19674,427 @@ GenTree* Compiler::gtNewSimdCndSelNode(var_types type,
assert(op1 != nullptr);
assert(op1->TypeIs(type));
- assert(op2 != nullptr);
- assert(op2->TypeIs(type));
-
- assert(op3 != nullptr);
- assert(op3->TypeIs(type));
-
+ assert(op2 != nullptr);
+ assert(op2->TypeIs(type));
+
+ assert(op3 != nullptr);
+ assert(op3->TypeIs(type));
+
+ var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType);
+ assert(varTypeIsArithmetic(simdBaseType));
+
+ NamedIntrinsic intrinsic = NI_Illegal;
+
+#if defined(TARGET_XARCH)
+ // TODO-XARCH-CQ: It's likely beneficial to have a dedicated CndSel node so we
+ // can special case when the condition is the result of various compare operations.
+ //
+ // When it is, the condition is AllBitsSet or Zero on a per-element basis and we
+ // could change this to be a Blend operation in lowering as an optimization.
+
+ assert((simdSize != 32) || compIsaSupportedDebugOnly(InstructionSet_AVX));
+ CORINFO_CLASS_HANDLE clsHnd = gtGetStructHandleForSIMD(type, simdBaseJitType);
+
+ GenTree* op1Dup;
+ op1 = impCloneExpr(op1, &op1Dup, clsHnd, (unsigned)CHECK_SPILL_ALL,
+ nullptr DEBUGARG("Clone op1 for vector conditional select"));
+
+ // op2 = op2 & op1
+ op2 = gtNewSimdBinOpNode(GT_AND, type, op2, op1, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+
+ // op3 = op3 & ~op1Dup
+ op3 = gtNewSimdBinOpNode(GT_AND_NOT, type, op3, op1Dup, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+
+ // result = op2 | op3
+ return gtNewSimdBinOpNode(GT_OR, type, op2, op3, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+#elif defined(TARGET_ARM64)
+ return gtNewSimdAsHWIntrinsicNode(type, op1, op2, op3, NI_AdvSimd_BitwiseSelect, simdBaseJitType, simdSize);
+#else
+#error Unsupported platform
+#endif // !TARGET_XARCH && !TARGET_ARM64
+}
+
+GenTree* Compiler::gtNewSimdCreateBroadcastNode(
+ var_types type, GenTree* op1, CorInfoType simdBaseJitType, unsigned simdSize, bool isSimdAsHWIntrinsic)
+{
+ NamedIntrinsic hwIntrinsicID = NI_Vector128_Create;
+ var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType);
+
+#if defined(TARGET_XARCH)
+#if defined(TARGET_X86)
+ if (varTypeIsLong(simdBaseType) && !op1->IsIntegralConst())
+ {
+ // TODO-XARCH-CQ: It may be beneficial to emit the movq
+ // instruction, which takes a 64-bit memory address and
+ // works on 32-bit x86 systems.
+ unreached();
+ }
+#endif // TARGET_X86
+
+ if (simdSize == 32)
+ {
+ hwIntrinsicID = NI_Vector256_Create;
+ }
+#elif defined(TARGET_ARM64)
+ if (simdSize == 8)
+ {
+ hwIntrinsicID = NI_Vector64_Create;
+ }
+#else
+#error Unsupported platform
+#endif // !TARGET_XARCH && !TARGET_ARM64
+
+ return gtNewSimdHWIntrinsicNode(type, op1, hwIntrinsicID, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+}
+
+GenTree* Compiler::gtNewSimdDotProdNode(var_types type,
+ GenTree* op1,
+ GenTree* op2,
+ CorInfoType simdBaseJitType,
+ unsigned simdSize,
+ bool isSimdAsHWIntrinsic)
+{
+ assert(IsBaselineSimdIsaSupportedDebugOnly());
+ assert(varTypeIsArithmetic(type));
+
+ var_types simdType = getSIMDTypeForSize(simdSize);
+ assert(varTypeIsSIMD(simdType));
+
+ assert(op1 != nullptr);
+ assert(op1->TypeIs(simdType));
+
+ assert(op2 != nullptr);
+ assert(op2->TypeIs(simdType));
+
+ var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType);
+ assert(genActualType(simdBaseType) == type);
+
+ NamedIntrinsic intrinsic = NI_Illegal;
+
+#if defined(TARGET_XARCH)
+ assert(!varTypeIsByte(simdBaseType) && !varTypeIsLong(simdBaseType));
+
+ if (simdSize == 32)
+ {
+ assert(varTypeIsFloating(simdBaseType) || compIsaSupportedDebugOnly(InstructionSet_AVX2));
+ intrinsic = NI_Vector256_Dot;
+ }
+ else
+ {
+ assert(((simdBaseType != TYP_INT) && (simdBaseType != TYP_UINT)) ||
+ compIsaSupportedDebugOnly(InstructionSet_SSE41));
+ intrinsic = NI_Vector128_Dot;
+ }
+#elif defined(TARGET_ARM64)
+ assert(!varTypeIsLong(simdBaseType));
+ intrinsic = (simdSize == 8) ? NI_Vector64_Dot : NI_Vector128_Dot;
+#else
+#error Unsupported platform
+#endif // !TARGET_XARCH && !TARGET_ARM64
+
+ assert(intrinsic != NI_Illegal);
+ return gtNewSimdHWIntrinsicNode(type, op1, op2, intrinsic, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+}
+
+GenTree* Compiler::gtNewSimdFloorNode(
+ var_types type, GenTree* op1, CorInfoType simdBaseJitType, unsigned simdSize, bool isSimdAsHWIntrinsic)
+{
+ assert(IsBaselineSimdIsaSupportedDebugOnly());
+
+ assert(varTypeIsSIMD(type));
+ assert(getSIMDTypeForSize(simdSize) == type);
+
+ assert(op1 != nullptr);
+ assert(op1->TypeIs(type));
+
var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType);
- assert(varTypeIsArithmetic(simdBaseType));
+ assert(varTypeIsFloating(simdBaseType));
NamedIntrinsic intrinsic = NI_Illegal;
#if defined(TARGET_XARCH)
- // TODO-XARCH-CQ: It's likely beneficial to have a dedicated CndSel node so we
- // can special case when the condition is the result of various compare operations.
- //
- // When it is, the condition is AllBitsSet or Zero on a per-element basis and we
- // could change this to be a Blend operation in lowering as an optimization.
-
- assert((simdSize != 32) || compIsaSupportedDebugOnly(InstructionSet_AVX));
- CORINFO_CLASS_HANDLE clsHnd = gtGetStructHandleForSIMD(type, simdBaseJitType);
-
- GenTree* op1Dup;
- op1 = impCloneExpr(op1, &op1Dup, clsHnd, (unsigned)CHECK_SPILL_ALL,
- nullptr DEBUGARG("Clone op1 for vector conditional select"));
-
- // op2 = op2 & op1
- op2 = gtNewSimdBinOpNode(GT_AND, type, op2, op1, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
-
- // op3 = op3 & ~op1Dup
- op3 = gtNewSimdBinOpNode(GT_AND_NOT, type, op3, op1Dup, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
-
- // result = op2 | op3
- return gtNewSimdBinOpNode(GT_OR, type, op2, op3, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ if (simdSize == 32)
+ {
+ intrinsic = NI_AVX_Floor;
+ }
+ else
+ {
+ assert(compIsaSupportedDebugOnly(InstructionSet_SSE41));
+ intrinsic = NI_SSE41_Floor;
+ }
#elif defined(TARGET_ARM64)
- return gtNewSimdAsHWIntrinsicNode(type, op1, op2, op3, NI_AdvSimd_BitwiseSelect, simdBaseJitType, simdSize);
+ if (simdBaseType == TYP_DOUBLE)
+ {
+ intrinsic = (simdSize == 8) ? NI_AdvSimd_FloorScalar : NI_AdvSimd_Arm64_Floor;
+ }
+ else
+ {
+ intrinsic = NI_AdvSimd_Floor;
+ }
#else
#error Unsupported platform
#endif // !TARGET_XARCH && !TARGET_ARM64
+
+ assert(intrinsic != NI_Illegal);
+ return gtNewSimdHWIntrinsicNode(type, op1, intrinsic, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
}
-GenTree* Compiler::gtNewSimdCreateBroadcastNode(
- var_types type, GenTree* op1, CorInfoType simdBaseJitType, unsigned simdSize, bool isSimdAsHWIntrinsic)
+GenTree* Compiler::gtNewSimdGetElementNode(var_types type,
+ GenTree* op1,
+ GenTree* op2,
+ CorInfoType simdBaseJitType,
+ unsigned simdSize,
+ bool isSimdAsHWIntrinsic)
{
- NamedIntrinsic hwIntrinsicID = NI_Vector128_Create;
- var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType);
+ NamedIntrinsic intrinsicId = NI_Vector128_GetElement;
+ var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType);
+
+ assert(varTypeIsArithmetic(simdBaseType));
#if defined(TARGET_XARCH)
-#if defined(TARGET_X86)
- if (varTypeIsLong(simdBaseType) && !op1->IsIntegralConst())
+ switch (simdBaseType)
{
- // TODO-XARCH-CQ: It may be beneficial to emit the movq
- // instruction, which takes a 64-bit memory address and
- // works on 32-bit x86 systems.
- unreached();
+ // Using software fallback if simdBaseType is not supported by hardware
+ case TYP_BYTE:
+ case TYP_UBYTE:
+ case TYP_INT:
+ case TYP_UINT:
+ case TYP_LONG:
+ case TYP_ULONG:
+ assert(compIsaSupportedDebugOnly(InstructionSet_SSE41));
+ break;
+
+ case TYP_DOUBLE:
+ case TYP_FLOAT:
+ case TYP_SHORT:
+ case TYP_USHORT:
+ assert(compIsaSupportedDebugOnly(InstructionSet_SSE2));
+ break;
+
+ default:
+ unreached();
}
-#endif // TARGET_X86
if (simdSize == 32)
{
- hwIntrinsicID = NI_Vector256_Create;
+ intrinsicId = NI_Vector256_GetElement;
}
#elif defined(TARGET_ARM64)
if (simdSize == 8)
{
- hwIntrinsicID = NI_Vector64_Create;
+ intrinsicId = NI_Vector64_GetElement;
}
#else
#error Unsupported platform
#endif // !TARGET_XARCH && !TARGET_ARM64
- return gtNewSimdHWIntrinsicNode(type, op1, hwIntrinsicID, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ int immUpperBound = getSIMDVectorLength(simdSize, simdBaseType) - 1;
+ bool rangeCheckNeeded = !op2->OperIsConst();
+
+ if (!rangeCheckNeeded)
+ {
+ ssize_t imm8 = op2->AsIntCon()->IconValue();
+ rangeCheckNeeded = (imm8 < 0) || (imm8 > immUpperBound);
+ }
+
+ if (rangeCheckNeeded)
+ {
+ op2 = addRangeCheckForHWIntrinsic(op2, 0, immUpperBound);
+ }
+
+ return gtNewSimdHWIntrinsicNode(type, op1, op2, intrinsicId, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
}
-GenTree* Compiler::gtNewSimdDotProdNode(var_types type,
- GenTree* op1,
- GenTree* op2,
- CorInfoType simdBaseJitType,
- unsigned simdSize,
- bool isSimdAsHWIntrinsic)
+GenTree* Compiler::gtNewSimdMaxNode(var_types type,
+ GenTree* op1,
+ GenTree* op2,
+ CorInfoType simdBaseJitType,
+ unsigned simdSize,
+ bool isSimdAsHWIntrinsic)
{
assert(IsBaselineSimdIsaSupportedDebugOnly());
- assert(varTypeIsArithmetic(type));
- var_types simdType = getSIMDTypeForSize(simdSize);
- assert(varTypeIsSIMD(simdType));
+ assert(varTypeIsSIMD(type));
+ assert(getSIMDTypeForSize(simdSize) == type);
assert(op1 != nullptr);
- assert(op1->TypeIs(simdType));
+ assert(op1->TypeIs(type));
assert(op2 != nullptr);
- assert(op2->TypeIs(simdType));
+ assert(op2->TypeIs(type));
var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType);
- assert(genActualType(simdBaseType) == type);
+ assert(varTypeIsArithmetic(simdBaseType));
- NamedIntrinsic intrinsic = NI_Illegal;
+ NamedIntrinsic intrinsic = NI_Illegal;
+ CORINFO_CLASS_HANDLE clsHnd = gtGetStructHandleForSIMD(type, simdBaseJitType);
#if defined(TARGET_XARCH)
- assert(!varTypeIsByte(simdBaseType) && !varTypeIsLong(simdBaseType));
-
if (simdSize == 32)
{
- assert(varTypeIsFloating(simdBaseType) || compIsaSupportedDebugOnly(InstructionSet_AVX2));
- intrinsic = NI_Vector256_Dot;
+ assert(compIsaSupportedDebugOnly(InstructionSet_AVX));
+
+ if (varTypeIsFloating(simdBaseType))
+ {
+ intrinsic = NI_AVX_Max;
+ }
+ else
+ {
+ assert(compIsaSupportedDebugOnly(InstructionSet_AVX2));
+
+ if (!varTypeIsLong(simdBaseType))
+ {
+ intrinsic = NI_AVX2_Max;
+ }
+ }
+ }
+ else
+ {
+ switch (simdBaseType)
+ {
+ case TYP_BYTE:
+ case TYP_USHORT:
+ {
+ GenTree* constVal = nullptr;
+ CorInfoType opJitType = simdBaseJitType;
+ var_types opType = simdBaseType;
+ genTreeOps fixupOp1 = GT_NONE;
+ genTreeOps fixupOp2 = GT_NONE;
+
+ switch (simdBaseType)
+ {
+ case TYP_BYTE:
+ {
+ constVal = gtNewIconNode(0x80808080);
+ fixupOp1 = GT_SUB;
+ fixupOp2 = GT_ADD;
+ simdBaseJitType = CORINFO_TYPE_UBYTE;
+ simdBaseType = TYP_UBYTE;
+ break;
+ }
+
+ case TYP_USHORT:
+ {
+ constVal = gtNewIconNode(0x80008000);
+ fixupOp1 = GT_ADD;
+ fixupOp2 = GT_SUB;
+ simdBaseJitType = CORINFO_TYPE_SHORT;
+ simdBaseType = TYP_SHORT;
+ break;
+ }
+
+ default:
+ {
+ unreached();
+ }
+ }
+
+ assert(constVal != nullptr);
+ assert(fixupOp1 != GT_NONE);
+ assert(fixupOp2 != GT_NONE);
+ assert(opJitType != simdBaseJitType);
+ assert(opType != simdBaseType);
+
+ GenTree* constVector =
+ gtNewSimdCreateBroadcastNode(type, constVal, CORINFO_TYPE_INT, simdSize, isSimdAsHWIntrinsic);
+
+ GenTree* constVectorDup1;
+ constVector = impCloneExpr(constVector, &constVectorDup1, clsHnd, (unsigned)CHECK_SPILL_ALL,
+ nullptr DEBUGARG("Clone constVector for vector Max"));
+
+ GenTree* constVectorDup2;
+ constVectorDup1 = impCloneExpr(constVectorDup1, &constVectorDup2, clsHnd, (unsigned)CHECK_SPILL_ALL,
+ nullptr DEBUGARG("Clone constVector for vector Max"));
+
+ // op1 = op1 - constVector
+ // -or-
+ // op1 = op1 + constVector
+ op1 = gtNewSimdBinOpNode(fixupOp1, type, op1, constVector, opJitType, simdSize, isSimdAsHWIntrinsic);
+
+ // op2 = op2 - constVectorDup1
+ // -or-
+ // op2 = op2 + constVectorDup1
+ op2 =
+ gtNewSimdBinOpNode(fixupOp1, type, op2, constVectorDup1, opJitType, simdSize, isSimdAsHWIntrinsic);
+
+ // op1 = Max(op1, op2)
+ op1 = gtNewSimdMaxNode(type, op1, op2, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+
+ // result = op1 + constVectorDup2
+ // -or-
+ // result = op1 - constVectorDup2
+ return gtNewSimdBinOpNode(fixupOp2, type, op1, constVectorDup2, opJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ }
+
+ case TYP_INT:
+ case TYP_UINT:
+ case TYP_LONG:
+ case TYP_ULONG:
+ {
+ break;
+ }
+
+ case TYP_FLOAT:
+ {
+ intrinsic = NI_SSE_Max;
+ break;
+ }
+
+ case TYP_UBYTE:
+ case TYP_SHORT:
+ case TYP_DOUBLE:
+ {
+ intrinsic = NI_SSE2_Max;
+ break;
+ }
+
+ default:
+ {
+ unreached();
+ }
+ }
}
- else
+#elif defined(TARGET_ARM64)
+ if (!varTypeIsLong(simdBaseType))
{
- assert(((simdBaseType != TYP_INT) && (simdBaseType != TYP_UINT)) ||
- compIsaSupportedDebugOnly(InstructionSet_SSE41));
- intrinsic = NI_Vector128_Dot;
+ if (simdBaseType == TYP_DOUBLE)
+ {
+ intrinsic = (simdSize == 8) ? NI_AdvSimd_Arm64_MaxScalar : NI_AdvSimd_Arm64_Max;
+ }
+ else
+ {
+ intrinsic = NI_AdvSimd_Max;
+ }
}
-#elif defined(TARGET_ARM64)
- assert(!varTypeIsLong(simdBaseType));
- intrinsic = (simdSize == 8) ? NI_Vector64_Dot : NI_Vector128_Dot;
#else
#error Unsupported platform
#endif // !TARGET_XARCH && !TARGET_ARM64
- assert(intrinsic != NI_Illegal);
- return gtNewSimdHWIntrinsicNode(type, op1, op2, intrinsic, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ if (intrinsic != NI_Illegal)
+ {
+ return gtNewSimdHWIntrinsicNode(type, op1, op2, intrinsic, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ }
+
+ GenTree* op1Dup;
+ op1 = impCloneExpr(op1, &op1Dup, clsHnd, (unsigned)CHECK_SPILL_ALL, nullptr DEBUGARG("Clone op1 for vector Max"));
+
+ GenTree* op2Dup;
+ op2 = impCloneExpr(op2, &op2Dup, clsHnd, (unsigned)CHECK_SPILL_ALL, nullptr DEBUGARG("Clone op2 for vector Max"));
+
+ // op1 = op1 > op2
+ op1 = gtNewSimdCmpOpNode(GT_GT, type, op1, op2, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+
+ // result = ConditionalSelect(op1, op1Dup, op2Dup)
+ return gtNewSimdCndSelNode(type, op1, op1Dup, op2Dup, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
}
-GenTree* Compiler::gtNewSimdFloorNode(
- var_types type, GenTree* op1, CorInfoType simdBaseJitType, unsigned simdSize, bool isSimdAsHWIntrinsic)
+GenTree* Compiler::gtNewSimdMinNode(var_types type,
+ GenTree* op1,
+ GenTree* op2,
+ CorInfoType simdBaseJitType,
+ unsigned simdSize,
+ bool isSimdAsHWIntrinsic)
{
assert(IsBaselineSimdIsaSupportedDebugOnly());
@@ -20445,110 +20104,181 @@ GenTree* Compiler::gtNewSimdFloorNode(
assert(op1 != nullptr);
assert(op1->TypeIs(type));
+ assert(op2 != nullptr);
+ assert(op2->TypeIs(type));
+
var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType);
- assert(varTypeIsFloating(simdBaseType));
+ assert(varTypeIsArithmetic(simdBaseType));
- NamedIntrinsic intrinsic = NI_Illegal;
+ NamedIntrinsic intrinsic = NI_Illegal;
+ CORINFO_CLASS_HANDLE clsHnd = gtGetStructHandleForSIMD(type, simdBaseJitType);
#if defined(TARGET_XARCH)
if (simdSize == 32)
{
- intrinsic = NI_AVX_Floor;
- }
- else
- {
- assert(compIsaSupportedDebugOnly(InstructionSet_SSE41));
- intrinsic = NI_SSE41_Floor;
- }
-#elif defined(TARGET_ARM64)
- if (simdBaseType == TYP_DOUBLE)
- {
- intrinsic = (simdSize == 8) ? NI_AdvSimd_FloorScalar : NI_AdvSimd_Arm64_Floor;
+ assert(compIsaSupportedDebugOnly(InstructionSet_AVX));
+
+ if (varTypeIsFloating(simdBaseType))
+ {
+ intrinsic = NI_AVX_Min;
+ }
+ else
+ {
+ assert(compIsaSupportedDebugOnly(InstructionSet_AVX2));
+
+ if (!varTypeIsLong(simdBaseType))
+ {
+ intrinsic = NI_AVX2_Min;
+ }
+ }
}
else
{
- intrinsic = NI_AdvSimd_Floor;
- }
-#else
-#error Unsupported platform
-#endif // !TARGET_XARCH && !TARGET_ARM64
+ switch (simdBaseType)
+ {
+ case TYP_BYTE:
+ case TYP_USHORT:
+ {
+ GenTree* constVal = nullptr;
+ CorInfoType opJitType = simdBaseJitType;
+ var_types opType = simdBaseType;
+ genTreeOps fixupOp1 = GT_NONE;
+ genTreeOps fixupOp2 = GT_NONE;
- assert(intrinsic != NI_Illegal);
- return gtNewSimdHWIntrinsicNode(type, op1, intrinsic, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
-}
+ switch (simdBaseType)
+ {
+ case TYP_BYTE:
+ {
+ constVal = gtNewIconNode(0x80808080);
+ fixupOp1 = GT_SUB;
+ fixupOp2 = GT_ADD;
+ simdBaseJitType = CORINFO_TYPE_UBYTE;
+ simdBaseType = TYP_UBYTE;
+ break;
+ }
-GenTree* Compiler::gtNewSimdGetElementNode(var_types type,
- GenTree* op1,
- GenTree* op2,
- CorInfoType simdBaseJitType,
- unsigned simdSize,
- bool isSimdAsHWIntrinsic)
-{
- NamedIntrinsic intrinsicId = NI_Vector128_GetElement;
- var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType);
+ case TYP_USHORT:
+ {
+ constVal = gtNewIconNode(0x80008000);
+ fixupOp1 = GT_ADD;
+ fixupOp2 = GT_SUB;
+ simdBaseJitType = CORINFO_TYPE_SHORT;
+ simdBaseType = TYP_SHORT;
+ break;
+ }
- assert(varTypeIsArithmetic(simdBaseType));
+ default:
+ {
+ unreached();
+ }
+ }
-#if defined(TARGET_XARCH)
- switch (simdBaseType)
- {
- // Using software fallback if simdBaseType is not supported by hardware
- case TYP_BYTE:
- case TYP_UBYTE:
- case TYP_INT:
- case TYP_UINT:
- case TYP_LONG:
- case TYP_ULONG:
- assert(compIsaSupportedDebugOnly(InstructionSet_SSE41));
- break;
+ assert(constVal != nullptr);
+ assert(fixupOp1 != GT_NONE);
+ assert(fixupOp2 != GT_NONE);
+ assert(opJitType != simdBaseJitType);
+ assert(opType != simdBaseType);
- case TYP_DOUBLE:
- case TYP_FLOAT:
- case TYP_SHORT:
- case TYP_USHORT:
- assert(compIsaSupportedDebugOnly(InstructionSet_SSE2));
- break;
+ GenTree* constVector =
+ gtNewSimdCreateBroadcastNode(type, constVal, CORINFO_TYPE_INT, simdSize, isSimdAsHWIntrinsic);
- default:
- unreached();
- }
+ GenTree* constVectorDup1;
+ constVector = impCloneExpr(constVector, &constVectorDup1, clsHnd, (unsigned)CHECK_SPILL_ALL,
+ nullptr DEBUGARG("Clone constVector for vector Min"));
- if (simdSize == 32)
- {
- intrinsicId = NI_Vector256_GetElement;
+ GenTree* constVectorDup2;
+ constVectorDup1 = impCloneExpr(constVectorDup1, &constVectorDup2, clsHnd, (unsigned)CHECK_SPILL_ALL,
+ nullptr DEBUGARG("Clone constVector for vector Min"));
+
+ // op1 = op1 - constVector
+ // -or-
+ // op1 = op1 + constVector
+ op1 = gtNewSimdBinOpNode(fixupOp1, type, op1, constVector, opJitType, simdSize, isSimdAsHWIntrinsic);
+
+ // op2 = op2 - constVectorDup1
+ // -or-
+ // op2 = op2 + constVectorDup1
+ op2 =
+ gtNewSimdBinOpNode(fixupOp1, type, op2, constVectorDup1, opJitType, simdSize, isSimdAsHWIntrinsic);
+
+ // op1 = Min(op1, op2)
+ op1 = gtNewSimdMinNode(type, op1, op2, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+
+ // result = op1 + constVectorDup2
+ // -or-
+ // result = op1 - constVectorDup2
+ return gtNewSimdBinOpNode(fixupOp2, type, op1, constVectorDup2, opJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ }
+
+ case TYP_INT:
+ case TYP_UINT:
+ case TYP_LONG:
+ case TYP_ULONG:
+ {
+ break;
+ }
+
+ case TYP_FLOAT:
+ {
+ intrinsic = NI_SSE_Min;
+ break;
+ }
+
+ case TYP_UBYTE:
+ case TYP_SHORT:
+ case TYP_DOUBLE:
+ {
+ intrinsic = NI_SSE2_Min;
+ break;
+ }
+
+ default:
+ {
+ unreached();
+ }
+ }
}
#elif defined(TARGET_ARM64)
- if (simdSize == 8)
+ if (!varTypeIsLong(simdBaseType))
{
- intrinsicId = NI_Vector64_GetElement;
+ if (simdBaseType == TYP_DOUBLE)
+ {
+ intrinsic = (simdSize == 8) ? NI_AdvSimd_Arm64_MinScalar : NI_AdvSimd_Arm64_Min;
+ }
+ else
+ {
+ intrinsic = NI_AdvSimd_Min;
+ }
}
#else
#error Unsupported platform
#endif // !TARGET_XARCH && !TARGET_ARM64
- int immUpperBound = getSIMDVectorLength(simdSize, simdBaseType) - 1;
- bool rangeCheckNeeded = !op2->OperIsConst();
-
- if (!rangeCheckNeeded)
+ if (intrinsic != NI_Illegal)
{
- ssize_t imm8 = op2->AsIntCon()->IconValue();
- rangeCheckNeeded = (imm8 < 0) || (imm8 > immUpperBound);
+ return gtNewSimdHWIntrinsicNode(type, op1, op2, intrinsic, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
}
- if (rangeCheckNeeded)
- {
- op2 = addRangeCheckForHWIntrinsic(op2, 0, immUpperBound);
- }
+ GenTree* op1Dup;
+ op1 = impCloneExpr(op1, &op1Dup, clsHnd, (unsigned)CHECK_SPILL_ALL, nullptr DEBUGARG("Clone op1 for vector Min"));
- return gtNewSimdHWIntrinsicNode(type, op1, op2, intrinsicId, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ GenTree* op2Dup;
+ op2 = impCloneExpr(op2, &op2Dup, clsHnd, (unsigned)CHECK_SPILL_ALL, nullptr DEBUGARG("Clone op2 for vector Min"));
+
+ // op1 = op1 < op2
+ op1 = gtNewSimdCmpOpNode(GT_LT, type, op1, op2, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+
+ // result = ConditionalSelect(op1, op1Dup, op2Dup)
+ return gtNewSimdCndSelNode(type, op1, op1Dup, op2Dup, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
}
-GenTree* Compiler::gtNewSimdMaxNode(var_types type,
- GenTree* op1,
- GenTree* op2,
- CorInfoType simdBaseJitType,
- unsigned simdSize,
- bool isSimdAsHWIntrinsic)
+GenTree* Compiler::gtNewSimdNarrowNode(var_types type,
+ GenTree* op1,
+ GenTree* op2,
+ CorInfoType simdBaseJitType,
+ unsigned simdSize,
+ bool isSimdAsHWIntrinsic)
{
assert(IsBaselineSimdIsaSupportedDebugOnly());
@@ -20562,27 +20292,159 @@ GenTree* Compiler::gtNewSimdMaxNode(var_types type,
assert(op2->TypeIs(type));
var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType);
- assert(varTypeIsArithmetic(simdBaseType));
+ assert(varTypeIsArithmetic(simdBaseType) && !varTypeIsLong(simdBaseType));
- NamedIntrinsic intrinsic = NI_Illegal;
- CORINFO_CLASS_HANDLE clsHnd = gtGetStructHandleForSIMD(type, simdBaseJitType);
+ GenTree* tmp1;
+ GenTree* tmp2;
#if defined(TARGET_XARCH)
+ GenTree* tmp3;
+ GenTree* tmp4;
+
if (simdSize == 32)
{
assert(compIsaSupportedDebugOnly(InstructionSet_AVX));
- if (varTypeIsFloating(simdBaseType))
- {
- intrinsic = NI_AVX_Max;
- }
- else
+ switch (simdBaseType)
{
- assert(compIsaSupportedDebugOnly(InstructionSet_AVX2));
+ case TYP_BYTE:
+ case TYP_UBYTE:
+ {
+ assert(compIsaSupportedDebugOnly(InstructionSet_AVX2));
- if (!varTypeIsLong(simdBaseType))
+ // This is the same in principle to the other comments below, however due to
+ // code formatting, its too long to reasonably display here.
+
+ CorInfoType opBaseJitType = (simdBaseType == TYP_BYTE) ? CORINFO_TYPE_SHORT : CORINFO_TYPE_USHORT;
+ CORINFO_CLASS_HANDLE clsHnd = gtGetStructHandleForSIMD(type, opBaseJitType);
+
+ tmp1 = gtNewSimdHWIntrinsicNode(type, gtNewIconNode(0x00FF), NI_Vector256_Create, opBaseJitType,
+ simdSize, isSimdAsHWIntrinsic);
+
+ GenTree* tmp1Dup;
+ tmp1 = impCloneExpr(tmp1, &tmp1Dup, clsHnd, (unsigned)CHECK_SPILL_ALL,
+ nullptr DEBUGARG("Clone tmp1 for vector narrow"));
+
+ tmp2 = gtNewSimdHWIntrinsicNode(type, op1, tmp1, NI_SSE2_And, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ tmp3 = gtNewSimdHWIntrinsicNode(type, op2, tmp1Dup, NI_SSE2_And, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ tmp4 = gtNewSimdHWIntrinsicNode(type, tmp2, tmp3, NI_SSE2_PackUnsignedSaturate, CORINFO_TYPE_UBYTE,
+ simdSize, isSimdAsHWIntrinsic);
+
+ CorInfoType permuteBaseJitType = (simdBaseType == TYP_BYTE) ? CORINFO_TYPE_LONG : CORINFO_TYPE_ULONG;
+ return gtNewSimdHWIntrinsicNode(type, tmp4, gtNewIconNode(SHUFFLE_WYZX), NI_AVX2_Permute4x64,
+ permuteBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ }
+
+ case TYP_SHORT:
+ case TYP_USHORT:
{
- intrinsic = NI_AVX2_Max;
+ assert(compIsaSupportedDebugOnly(InstructionSet_AVX2));
+
+ // op1 = Elements 0L, 0U, 1L, 1U, 2L, 2U, 3L, 3U | 4L, 4U, 5L, 5U, 6L, 6U, 7L, 7U
+ // op2 = Elements 8L, 8U, 9L, 9U, AL, AU, BL, BU | CL, CU, DL, DU, EL, EU, FL, FU
+ //
+ // tmp2 = Elements 0L, --, 1L, --, 2L, --, 3L, -- | 4L, --, 5L, --, 6L, --, 7L, --
+ // tmp3 = Elements 8L, --, 9L, --, AL, --, BL, -- | CL, --, DL, --, EL, --, FL, --
+ // tmp4 = Elements 0L, 1L, 2L, 3L, 8L, 9L, AL, BL | 4L, 5L, 6L, 7L, CL, DL, EL, FL
+ // return Elements 0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L | 8L, 9L, AL, BL, CL, DL, EL, FL
+ //
+ // var tmp1 = Vector256.Create(0x0000FFFF).AsInt16();
+ // var tmp2 = Avx2.And(op1.AsInt16(), tmp1);
+ // var tmp3 = Avx2.And(op2.AsInt16(), tmp1);
+ // var tmp4 = Avx2.PackUnsignedSaturate(tmp2, tmp3);
+ // return Avx2.Permute4x64(tmp4.AsUInt64(), SHUFFLE_WYZX).As();
+
+ CorInfoType opBaseJitType = (simdBaseType == TYP_SHORT) ? CORINFO_TYPE_INT : CORINFO_TYPE_UINT;
+ CORINFO_CLASS_HANDLE clsHnd = gtGetStructHandleForSIMD(type, opBaseJitType);
+
+ tmp1 = gtNewSimdHWIntrinsicNode(type, gtNewIconNode(0x0000FFFF), NI_Vector256_Create, opBaseJitType,
+ simdSize, isSimdAsHWIntrinsic);
+
+ GenTree* tmp1Dup;
+ tmp1 = impCloneExpr(tmp1, &tmp1Dup, clsHnd, (unsigned)CHECK_SPILL_ALL,
+ nullptr DEBUGARG("Clone tmp1 for vector narrow"));
+
+ tmp2 = gtNewSimdHWIntrinsicNode(type, op1, tmp1, NI_SSE2_And, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ tmp3 = gtNewSimdHWIntrinsicNode(type, op2, tmp1Dup, NI_SSE2_And, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ tmp4 = gtNewSimdHWIntrinsicNode(type, tmp2, tmp3, NI_SSE41_PackUnsignedSaturate, CORINFO_TYPE_USHORT,
+ simdSize, isSimdAsHWIntrinsic);
+
+ CorInfoType permuteBaseJitType = (simdBaseType == TYP_BYTE) ? CORINFO_TYPE_LONG : CORINFO_TYPE_ULONG;
+ return gtNewSimdHWIntrinsicNode(type, tmp4, gtNewIconNode(SHUFFLE_WYZX), NI_AVX2_Permute4x64,
+ permuteBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ }
+
+ case TYP_INT:
+ case TYP_UINT:
+ {
+ assert(compIsaSupportedDebugOnly(InstructionSet_AVX2));
+
+ // op1 = Elements 0, 1 | 2, 3; 0L, 0U, 1L, 1U | 2L, 2U, 3L, 3U
+ // op2 = Elements 4, 5 | 6, 7; 4L, 4U, 5L, 5U | 6L, 6U, 7L, 7U
+ //
+ // tmp1 = Elements 0L, 4L, 0U, 4U | 2L, 6L, 2U, 6U
+ // tmp2 = Elements 1L, 5L, 1U, 5U | 3L, 7L, 3U, 7U
+ // tmp3 = Elements 0L, 1L, 4L, 5L | 2L, 3L, 6L, 7L
+ // return Elements 0L, 1L, 2L, 3L | 4L, 5L, 6L, 7L
+ //
+ // var tmp1 = Avx2.UnpackLow(op1, op2);
+ // var tmp2 = Avx2.UnpackHigh(op1, op2);
+ // var tmp3 = Avx2.UnpackLow(tmp1, tmp2);
+ // return Avx2.Permute4x64(tmp3.AsUInt64(), SHUFFLE_WYZX).AsUInt32();
+
+ CorInfoType opBaseJitType = (simdBaseType == TYP_INT) ? CORINFO_TYPE_LONG : CORINFO_TYPE_ULONG;
+ CORINFO_CLASS_HANDLE clsHnd = gtGetStructHandleForSIMD(type, opBaseJitType);
+
+ GenTree* op1Dup;
+ op1 = impCloneExpr(op1, &op1Dup, clsHnd, (unsigned)CHECK_SPILL_ALL,
+ nullptr DEBUGARG("Clone op1 for vector narrow"));
+
+ GenTree* op2Dup;
+ op2 = impCloneExpr(op2, &op2Dup, clsHnd, (unsigned)CHECK_SPILL_ALL,
+ nullptr DEBUGARG("Clone op2 for vector narrow"));
+
+ tmp1 = gtNewSimdHWIntrinsicNode(type, op1, op2, NI_AVX2_UnpackLow, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ tmp2 = gtNewSimdHWIntrinsicNode(type, op1Dup, op2Dup, NI_AVX2_UnpackHigh, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ tmp3 = gtNewSimdHWIntrinsicNode(type, tmp1, tmp2, NI_AVX2_UnpackLow, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+
+ return gtNewSimdHWIntrinsicNode(type, tmp3, gtNewIconNode(SHUFFLE_WYZX), NI_AVX2_Permute4x64,
+ opBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ }
+
+ case TYP_FLOAT:
+ {
+ // op1 = Elements 0, 1 | 2, 3
+ // op2 = Elements 4, 5 | 6, 7
+ //
+ // tmp1 = Elements 0, 1, 2, 3 | -, -, -, -
+ // tmp1 = Elements 4, 5, 6, 7
+ // return Elements 0, 1, 2, 3 | 4, 5, 6, 7
+ //
+ // var tmp1 = Avx.ConvertToVector128Single(op1).ToVector256Unsafe();
+ // var tmp2 = Avx.ConvertToVector128Single(op2);
+ // return Avx.InsertVector128(tmp1, tmp2, 1);
+
+ tmp1 = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, NI_AVX_ConvertToVector128Single, simdBaseJitType,
+ simdSize, isSimdAsHWIntrinsic);
+ tmp2 = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op2, NI_AVX_ConvertToVector128Single, simdBaseJitType,
+ simdSize, isSimdAsHWIntrinsic);
+
+ tmp1 = gtNewSimdHWIntrinsicNode(type, tmp1, NI_Vector128_ToVector256Unsafe, simdBaseJitType, 16,
+ isSimdAsHWIntrinsic);
+ return gtNewSimdHWIntrinsicNode(type, tmp1, tmp2, gtNewIconNode(1), NI_AVX_InsertVector128,
+ simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ }
+
+ default:
+ {
+ unreached();
}
}
}
@@ -20591,100 +20453,183 @@ GenTree* Compiler::gtNewSimdMaxNode(var_types type,
switch (simdBaseType)
{
case TYP_BYTE:
+ case TYP_UBYTE:
+ {
+ // op1 = Elements 0, 1, 2, 3, 4, 5, 6, 7; 0L, 0U, 1L, 1U, 2L, 2U, 3L, 3U, 4L, 4U, 5L, 5U, 6L, 6U, 7L, 7U
+ // op2 = Elements 8, 9, A, B, C, D, E, F; 8L, 8U, 9L, 9U, AL, AU, BL, BU, CL, CU, DL, DU, EL, EU, FL, FU
+ //
+ // tmp2 = Elements 0L, --, 1L, --, 2L, --, 3L, --, 4L, --, 5L, --, 6L, --, 7L, --
+ // tmp3 = Elements 8L, --, 9L, --, AL, --, BL, --, CL, --, DL, --, EL, --, FL, --
+ // return Elements 0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, AL, BL, CL, DL, EL, FL
+ //
+ // var tmp1 = Vector128.Create((ushort)(0x00FF)).AsSByte();
+ // var tmp2 = Sse2.And(op1.AsSByte(), tmp1);
+ // var tmp3 = Sse2.And(op2.AsSByte(), tmp1);
+ // return Sse2.PackUnsignedSaturate(tmp1, tmp2).As();
+
+ CorInfoType opBaseJitType = (simdBaseType == TYP_BYTE) ? CORINFO_TYPE_SHORT : CORINFO_TYPE_USHORT;
+ CORINFO_CLASS_HANDLE clsHnd = gtGetStructHandleForSIMD(type, opBaseJitType);
+
+ tmp1 = gtNewSimdHWIntrinsicNode(type, gtNewIconNode(0x00FF), NI_Vector128_Create, opBaseJitType,
+ simdSize, isSimdAsHWIntrinsic);
+
+ GenTree* tmp1Dup;
+ tmp1 = impCloneExpr(tmp1, &tmp1Dup, clsHnd, (unsigned)CHECK_SPILL_ALL,
+ nullptr DEBUGARG("Clone tmp1 for vector narrow"));
+
+ tmp2 = gtNewSimdHWIntrinsicNode(type, op1, tmp1, NI_SSE2_And, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ tmp3 = gtNewSimdHWIntrinsicNode(type, op2, tmp1Dup, NI_SSE2_And, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+
+ return gtNewSimdHWIntrinsicNode(type, tmp2, tmp3, NI_SSE2_PackUnsignedSaturate, CORINFO_TYPE_UBYTE,
+ simdSize, isSimdAsHWIntrinsic);
+ }
+
+ case TYP_SHORT:
case TYP_USHORT:
{
- GenTree* constVal = nullptr;
- CorInfoType opJitType = simdBaseJitType;
- var_types opType = simdBaseType;
- genTreeOps fixupOp1 = GT_NONE;
- genTreeOps fixupOp2 = GT_NONE;
+ // op1 = Elements 0, 1, 2, 3; 0L, 0U, 1L, 1U, 2L, 2U, 3L, 3U
+ // op2 = Elements 4, 5, 6, 7; 4L, 4U, 5L, 5U, 6L, 6U, 7L, 7U
+ //
+ // ...
- switch (simdBaseType)
+ CorInfoType opBaseJitType = (simdBaseType == TYP_SHORT) ? CORINFO_TYPE_INT : CORINFO_TYPE_UINT;
+ CORINFO_CLASS_HANDLE clsHnd = gtGetStructHandleForSIMD(type, opBaseJitType);
+
+ if (compOpportunisticallyDependsOn(InstructionSet_SSE41))
{
- case TYP_BYTE:
- {
- constVal = gtNewIconNode(0x80808080);
- fixupOp1 = GT_SUB;
- fixupOp2 = GT_ADD;
- simdBaseJitType = CORINFO_TYPE_UBYTE;
- simdBaseType = TYP_UBYTE;
- break;
- }
+ // ...
+ //
+ // tmp2 = Elements 0L, --, 1L, --, 2L, --, 3L, --
+ // tmp3 = Elements 4L, --, 5L, --, 6L, --, 7L, --
+ // return Elements 0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L
+ //
+ // var tmp1 = Vector128.Create(0x0000FFFF).AsInt16();
+ // var tmp2 = Sse2.And(op1.AsInt16(), tmp1);
+ // var tmp3 = Sse2.And(op2.AsInt16(), tmp1);
+ // return Sse2.PackUnsignedSaturate(tmp2, tmp3).As();
- case TYP_USHORT:
- {
- constVal = gtNewIconNode(0x80008000);
- fixupOp1 = GT_ADD;
- fixupOp2 = GT_SUB;
- simdBaseJitType = CORINFO_TYPE_SHORT;
- simdBaseType = TYP_SHORT;
- break;
- }
+ tmp1 = gtNewSimdHWIntrinsicNode(type, gtNewIconNode(0x0000FFFF), NI_Vector128_Create, opBaseJitType,
+ simdSize, isSimdAsHWIntrinsic);
- default:
- {
- unreached();
- }
+ GenTree* tmp1Dup;
+ tmp1 = impCloneExpr(tmp1, &tmp1Dup, clsHnd, (unsigned)CHECK_SPILL_ALL,
+ nullptr DEBUGARG("Clone tmp1 for vector narrow"));
+
+ tmp2 = gtNewSimdHWIntrinsicNode(type, op1, tmp1, NI_SSE2_And, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ tmp3 = gtNewSimdHWIntrinsicNode(type, op2, tmp1Dup, NI_SSE2_And, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+
+ return gtNewSimdHWIntrinsicNode(type, tmp2, tmp3, NI_SSE41_PackUnsignedSaturate,
+ CORINFO_TYPE_USHORT, simdSize, isSimdAsHWIntrinsic);
}
+ else
+ {
+ // ...
+ //
+ // tmp1 = Elements 0L, 4L, 0U, 4U, 1L, 5L, 1U, 5U
+ // tmp2 = Elements 2L, 6L, 2U, 6U, 3L, 7L, 3U, 7U
+ // tmp3 = Elements 0L, 2L, 4L, 6L, 0U, 2U, 4U, 6U
+ // tmp4 = Elements 1L, 3L, 5L, 7L, 1U, 3U, 5U, 7U
+ // return Elements 0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L
+ //
+ // var tmp1 = Sse2.UnpackLow(op1.AsUInt16(), op2.AsUInt16());
+ // var tmp2 = Sse2.UnpackHigh(op1.AsUInt16(), op2.AsUInt16());
+ // var tmp3 = Sse2.UnpackLow(tmp1, tmp2);
+ // var tmp4 = Sse2.UnpackHigh(tmp1, tmp2);
+ // return Sse2.UnpackLow(tmp3, tmp4).As();
- assert(constVal != nullptr);
- assert(fixupOp1 != GT_NONE);
- assert(fixupOp2 != GT_NONE);
- assert(opJitType != simdBaseJitType);
- assert(opType != simdBaseType);
+ GenTree* op1Dup;
+ op1 = impCloneExpr(op1, &op1Dup, clsHnd, (unsigned)CHECK_SPILL_ALL,
+ nullptr DEBUGARG("Clone op1 for vector narrow"));
- GenTree* constVector =
- gtNewSimdCreateBroadcastNode(type, constVal, CORINFO_TYPE_INT, simdSize, isSimdAsHWIntrinsic);
+ GenTree* op2Dup;
+ op2 = impCloneExpr(op2, &op2Dup, clsHnd, (unsigned)CHECK_SPILL_ALL,
+ nullptr DEBUGARG("Clone op1 for vector narrow"));
- GenTree* constVectorDup1;
- constVector = impCloneExpr(constVector, &constVectorDup1, clsHnd, (unsigned)CHECK_SPILL_ALL,
- nullptr DEBUGARG("Clone constVector for vector Max"));
+ tmp1 = gtNewSimdHWIntrinsicNode(type, op1, op2, NI_SSE2_UnpackLow, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ tmp2 = gtNewSimdHWIntrinsicNode(type, op1Dup, op2Dup, NI_SSE2_UnpackHigh, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
- GenTree* constVectorDup2;
- constVectorDup1 = impCloneExpr(constVectorDup1, &constVectorDup2, clsHnd, (unsigned)CHECK_SPILL_ALL,
- nullptr DEBUGARG("Clone constVector for vector Max"));
+ clsHnd = gtGetStructHandleForSIMD(type, simdBaseJitType);
- // op1 = op1 - constVector
- // -or-
- // op1 = op1 + constVector
- op1 = gtNewSimdBinOpNode(fixupOp1, type, op1, constVector, opJitType, simdSize, isSimdAsHWIntrinsic);
+ GenTree* tmp1Dup;
+ tmp1 = impCloneExpr(tmp1, &tmp1Dup, clsHnd, (unsigned)CHECK_SPILL_ALL,
+ nullptr DEBUGARG("Clone tmp1 for vector narrow"));
- // op2 = op2 - constVectorDup1
- // -or-
- // op2 = op2 + constVectorDup1
- op2 =
- gtNewSimdBinOpNode(fixupOp1, type, op2, constVectorDup1, opJitType, simdSize, isSimdAsHWIntrinsic);
+ GenTree* tmp2Dup;
+ tmp2 = impCloneExpr(tmp2, &tmp2Dup, clsHnd, (unsigned)CHECK_SPILL_ALL,
+ nullptr DEBUGARG("Clone tmp2 for vector narrow"));
- // op1 = Max(op1, op2)
- op1 = gtNewSimdMaxNode(type, op1, op2, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ tmp3 = gtNewSimdHWIntrinsicNode(type, tmp1, tmp2, NI_SSE2_UnpackLow, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ tmp4 = gtNewSimdHWIntrinsicNode(type, tmp1Dup, tmp2Dup, NI_SSE2_UnpackHigh, simdBaseJitType,
+ simdSize, isSimdAsHWIntrinsic);
- // result = op1 + constVectorDup2
- // -or-
- // result = op1 - constVectorDup2
- return gtNewSimdBinOpNode(fixupOp2, type, op1, constVectorDup2, opJitType, simdSize,
- isSimdAsHWIntrinsic);
+ return gtNewSimdHWIntrinsicNode(type, tmp3, tmp4, NI_SSE2_UnpackLow, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ }
}
case TYP_INT:
case TYP_UINT:
- case TYP_LONG:
- case TYP_ULONG:
{
- break;
+ // op1 = Elements 0, 1; 0L, 0U, 1L, 1U
+ // op2 = Elements 2, 3; 2L, 2U, 3L, 3U
+ //
+ // tmp1 = Elements 0L, 2L, 0U, 2U
+ // tmp2 = Elements 1L, 3L, 1U, 3U
+ // return Elements 0L, 1L, 2L, 3L
+ //
+ // var tmp1 = Sse2.UnpackLow(op1.AsUInt32(), op2.AsUInt32());
+ // var tmp2 = Sse2.UnpackHigh(op1.AsUInt32(), op2.AsUInt32());
+ // return Sse2.UnpackLow(tmp1, tmp2).As();
+
+ CorInfoType opBaseJitType = (simdBaseType == TYP_INT) ? CORINFO_TYPE_LONG : CORINFO_TYPE_ULONG;
+ CORINFO_CLASS_HANDLE clsHnd = gtGetStructHandleForSIMD(type, opBaseJitType);
+
+ GenTree* op1Dup;
+ op1 = impCloneExpr(op1, &op1Dup, clsHnd, (unsigned)CHECK_SPILL_ALL,
+ nullptr DEBUGARG("Clone op1 for vector narrow"));
+
+ GenTree* op2Dup;
+ op2 = impCloneExpr(op2, &op2Dup, clsHnd, (unsigned)CHECK_SPILL_ALL,
+ nullptr DEBUGARG("Clone op2 for vector narrow"));
+
+ tmp1 = gtNewSimdHWIntrinsicNode(type, op1, op2, NI_SSE2_UnpackLow, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ tmp2 = gtNewSimdHWIntrinsicNode(type, op1Dup, op2Dup, NI_SSE2_UnpackHigh, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+
+ return gtNewSimdHWIntrinsicNode(type, tmp1, tmp2, NI_SSE2_UnpackLow, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
}
case TYP_FLOAT:
{
- intrinsic = NI_SSE_Max;
- break;
- }
+ // op1 = Elements 0, 1
+ // op2 = Elements 2, 3
+ //
+ // tmp1 = Elements 0, 1, -, -
+ // tmp1 = Elements 2, 3, -, -
+ // return Elements 0, 1, 2, 3
+ //
+ // var tmp1 = Sse2.ConvertToVector128Single(op1);
+ // var tmp2 = Sse2.ConvertToVector128Single(op2);
+ // return Sse.MoveLowToHigh(tmp1, tmp2);
- case TYP_UBYTE:
- case TYP_SHORT:
- case TYP_DOUBLE:
- {
- intrinsic = NI_SSE2_Max;
- break;
+ CorInfoType opBaseJitType = CORINFO_TYPE_DOUBLE;
+
+ tmp1 = gtNewSimdHWIntrinsicNode(type, op1, NI_SSE2_ConvertToVector128Single, opBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ tmp2 = gtNewSimdHWIntrinsicNode(type, op2, NI_SSE2_ConvertToVector128Single, opBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+
+ return gtNewSimdHWIntrinsicNode(type, tmp1, tmp2, NI_SSE_MoveLowToHigh, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
}
default:
@@ -20694,45 +20639,118 @@ GenTree* Compiler::gtNewSimdMaxNode(var_types type,
}
}
#elif defined(TARGET_ARM64)
- if (!varTypeIsLong(simdBaseType))
+ if (simdSize == 16)
+ {
+ if (varTypeIsFloating(simdBaseType))
+ {
+ // var tmp1 = AdvSimd.Arm64.ConvertToSingleLower(op1);
+ // return AdvSimd.Arm64.ConvertToSingleUpper(tmp1, op2);
+
+ tmp1 = gtNewSimdHWIntrinsicNode(TYP_SIMD8, op1, NI_AdvSimd_Arm64_ConvertToSingleLower, simdBaseJitType, 8,
+ isSimdAsHWIntrinsic);
+ return gtNewSimdHWIntrinsicNode(type, tmp1, op2, NI_AdvSimd_Arm64_ConvertToSingleUpper, simdBaseJitType,
+ simdSize, isSimdAsHWIntrinsic);
+ }
+ else
+ {
+ // var tmp1 = AdvSimd.ExtractNarrowingLower(op1);
+ // return AdvSimd.ExtractNarrowingUpper(tmp1, op2);
+
+ tmp1 = gtNewSimdHWIntrinsicNode(TYP_SIMD8, op1, NI_AdvSimd_ExtractNarrowingLower, simdBaseJitType, 8,
+ isSimdAsHWIntrinsic);
+ return gtNewSimdHWIntrinsicNode(type, tmp1, op2, NI_AdvSimd_ExtractNarrowingUpper, simdBaseJitType,
+ simdSize, isSimdAsHWIntrinsic);
+ }
+ }
+ else if (varTypeIsFloating(simdBaseType))
+ {
+ // var tmp1 = op1.ToVector128Unsafe();
+ // return AdvSimd.Arm64.ConvertToSingleLower(tmp1);
+
+ CorInfoType tmp2BaseJitType = CORINFO_TYPE_DOUBLE;
+
+ tmp1 = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, NI_Vector64_ToVector128Unsafe, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ tmp2 = gtNewSimdHWIntrinsicNode(TYP_SIMD16, tmp1, gtNewIconNode(1), op2, NI_AdvSimd_InsertScalar,
+ tmp2BaseJitType, 16, isSimdAsHWIntrinsic);
+
+ return gtNewSimdHWIntrinsicNode(type, tmp2, NI_AdvSimd_Arm64_ConvertToSingleLower, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ }
+ else
+ {
+ // var tmp1 = op1.ToVector128Unsafe();
+ // var tmp2 = AdvSimd.InsertScalar(tmp1.AsUInt64(), 1, op2.AsUInt64());
+ // return AdvSimd.ExtractNarrowingUpper(tmp2).As();
+
+ CorInfoType tmp2BaseJitType = varTypeIsSigned(simdBaseType) ? CORINFO_TYPE_LONG : CORINFO_TYPE_ULONG;
+
+ tmp1 = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, NI_Vector64_ToVector128Unsafe, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ tmp2 = gtNewSimdHWIntrinsicNode(TYP_SIMD16, tmp1, gtNewIconNode(1), op2, NI_AdvSimd_InsertScalar,
+ tmp2BaseJitType, 16, isSimdAsHWIntrinsic);
+
+ return gtNewSimdHWIntrinsicNode(type, tmp2, NI_AdvSimd_ExtractNarrowingLower, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ }
+#else
+#error Unsupported platform
+#endif // !TARGET_XARCH && !TARGET_ARM64
+}
+
+GenTree* Compiler::gtNewSimdSqrtNode(
+ var_types type, GenTree* op1, CorInfoType simdBaseJitType, unsigned simdSize, bool isSimdAsHWIntrinsic)
+{
+ assert(IsBaselineSimdIsaSupportedDebugOnly());
+
+ assert(varTypeIsSIMD(type));
+ assert(getSIMDTypeForSize(simdSize) == type);
+
+ assert(op1 != nullptr);
+ assert(op1->TypeIs(type));
+
+ var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType);
+ assert(varTypeIsFloating(simdBaseType));
+
+ NamedIntrinsic intrinsic = NI_Illegal;
+
+#if defined(TARGET_XARCH)
+ if (simdSize == 32)
+ {
+ assert(compIsaSupportedDebugOnly(InstructionSet_AVX));
+ intrinsic = NI_AVX_Sqrt;
+ }
+ else if (simdBaseType == TYP_FLOAT)
+ {
+ intrinsic = NI_SSE_Sqrt;
+ }
+ else
+ {
+ intrinsic = NI_SSE2_Sqrt;
+ }
+#elif defined(TARGET_ARM64)
+ if ((simdSize == 8) && (simdBaseType == TYP_DOUBLE))
{
- if (simdBaseType == TYP_DOUBLE)
- {
- intrinsic = (simdSize == 8) ? NI_AdvSimd_Arm64_MaxScalar : NI_AdvSimd_Arm64_Max;
- }
- else
- {
- intrinsic = NI_AdvSimd_Max;
- }
+ intrinsic = NI_AdvSimd_SqrtScalar;
+ }
+ else
+ {
+ intrinsic = NI_AdvSimd_Arm64_Sqrt;
}
#else
#error Unsupported platform
#endif // !TARGET_XARCH && !TARGET_ARM64
- if (intrinsic != NI_Illegal)
- {
- return gtNewSimdHWIntrinsicNode(type, op1, op2, intrinsic, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
- }
-
- GenTree* op1Dup;
- op1 = impCloneExpr(op1, &op1Dup, clsHnd, (unsigned)CHECK_SPILL_ALL, nullptr DEBUGARG("Clone op1 for vector Max"));
-
- GenTree* op2Dup;
- op2 = impCloneExpr(op2, &op2Dup, clsHnd, (unsigned)CHECK_SPILL_ALL, nullptr DEBUGARG("Clone op2 for vector Max"));
-
- // op1 = op1 > op2
- op1 = gtNewSimdCmpOpNode(GT_GT, type, op1, op2, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
-
- // result = ConditionalSelect(op1, op1Dup, op2Dup)
- return gtNewSimdCndSelNode(type, op1, op1Dup, op2Dup, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ assert(intrinsic != NI_Illegal);
+ return gtNewSimdHWIntrinsicNode(type, op1, intrinsic, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
}
-GenTree* Compiler::gtNewSimdMinNode(var_types type,
- GenTree* op1,
- GenTree* op2,
- CorInfoType simdBaseJitType,
- unsigned simdSize,
- bool isSimdAsHWIntrinsic)
+GenTree* Compiler::gtNewSimdUnOpNode(genTreeOps op,
+ var_types type,
+ GenTree* op1,
+ CorInfoType simdBaseJitType,
+ unsigned simdSize,
+ bool isSimdAsHWIntrinsic)
{
assert(IsBaselineSimdIsaSupportedDebugOnly());
@@ -20742,132 +20760,134 @@ GenTree* Compiler::gtNewSimdMinNode(var_types type,
assert(op1 != nullptr);
assert(op1->TypeIs(type));
- assert(op2 != nullptr);
- assert(op2->TypeIs(type));
-
var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType);
assert(varTypeIsArithmetic(simdBaseType));
- NamedIntrinsic intrinsic = NI_Illegal;
- CORINFO_CLASS_HANDLE clsHnd = gtGetStructHandleForSIMD(type, simdBaseJitType);
+ NamedIntrinsic intrinsic = NI_Illegal;
+ GenTree* op2 = nullptr;
-#if defined(TARGET_XARCH)
- if (simdSize == 32)
+ switch (op)
{
- assert(compIsaSupportedDebugOnly(InstructionSet_AVX));
+#if defined(TARGET_XARCH)
+ case GT_NEG:
+ {
+ if (simdSize == 32)
+ {
+ assert(compIsaSupportedDebugOnly(InstructionSet_AVX));
+ assert(varTypeIsFloating(simdBaseType) || compIsaSupportedDebugOnly(InstructionSet_AVX2));
+ }
+ op2 = gtNewSimdZeroNode(type, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
- if (varTypeIsFloating(simdBaseType))
+ // Zero - op1
+ return gtNewSimdBinOpNode(GT_SUB, type, op2, op1, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ }
+
+ case GT_NOT:
{
- intrinsic = NI_AVX_Min;
+ assert((simdSize != 32) || compIsaSupportedDebugOnly(InstructionSet_AVX));
+
+ intrinsic = (simdSize == 32) ? NI_Vector256_get_AllBitsSet : NI_Vector128_get_AllBitsSet;
+ op2 = gtNewSimdHWIntrinsicNode(type, intrinsic, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+
+ // op1 ^ AllBitsSet
+ return gtNewSimdBinOpNode(GT_XOR, type, op1, op2, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
}
- else
+#elif defined(TARGET_ARM64)
+ case GT_NEG:
{
- assert(compIsaSupportedDebugOnly(InstructionSet_AVX2));
+ if (varTypeIsSigned(simdBaseType))
+ {
+ if (simdBaseType == TYP_LONG)
+ {
+ intrinsic = (simdSize == 8) ? NI_AdvSimd_Arm64_NegateScalar : NI_AdvSimd_Arm64_Negate;
+ }
+ else if (simdBaseType == TYP_DOUBLE)
+ {
+ intrinsic = (simdSize == 8) ? NI_AdvSimd_NegateScalar : NI_AdvSimd_Arm64_Negate;
+ }
+ else
+ {
+ intrinsic = NI_AdvSimd_Negate;
+ }
- if (!varTypeIsLong(simdBaseType))
+ return gtNewSimdHWIntrinsicNode(type, op1, intrinsic, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ }
+ else
{
- intrinsic = NI_AVX2_Min;
+ // Zero - op1
+ op2 = gtNewSimdZeroNode(type, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ return gtNewSimdBinOpNode(GT_SUB, type, op2, op1, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
}
}
- }
- else
- {
- switch (simdBaseType)
- {
- case TYP_BYTE:
- case TYP_USHORT:
- {
- GenTree* constVal = nullptr;
- CorInfoType opJitType = simdBaseJitType;
- var_types opType = simdBaseType;
- genTreeOps fixupOp1 = GT_NONE;
- genTreeOps fixupOp2 = GT_NONE;
- switch (simdBaseType)
- {
- case TYP_BYTE:
- {
- constVal = gtNewIconNode(0x80808080);
- fixupOp1 = GT_SUB;
- fixupOp2 = GT_ADD;
- simdBaseJitType = CORINFO_TYPE_UBYTE;
- simdBaseType = TYP_UBYTE;
- break;
- }
+ case GT_NOT:
+ {
+ return gtNewSimdHWIntrinsicNode(type, op1, NI_AdvSimd_Not, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ }
+#else
+#error Unsupported platform
+#endif // !TARGET_XARCH && !TARGET_ARM64
- case TYP_USHORT:
- {
- constVal = gtNewIconNode(0x80008000);
- fixupOp1 = GT_ADD;
- fixupOp2 = GT_SUB;
- simdBaseJitType = CORINFO_TYPE_SHORT;
- simdBaseType = TYP_SHORT;
- break;
- }
+ default:
+ {
+ unreached();
+ }
+ }
+}
- default:
- {
- unreached();
- }
- }
+GenTree* Compiler::gtNewSimdWidenLowerNode(
+ var_types type, GenTree* op1, CorInfoType simdBaseJitType, unsigned simdSize, bool isSimdAsHWIntrinsic)
+{
+ assert(IsBaselineSimdIsaSupportedDebugOnly());
- assert(constVal != nullptr);
- assert(fixupOp1 != GT_NONE);
- assert(fixupOp2 != GT_NONE);
- assert(opJitType != simdBaseJitType);
- assert(opType != simdBaseType);
+ assert(varTypeIsSIMD(type));
+ assert(getSIMDTypeForSize(simdSize) == type);
- GenTree* constVector =
- gtNewSimdCreateBroadcastNode(type, constVal, CORINFO_TYPE_INT, simdSize, isSimdAsHWIntrinsic);
+ assert(op1 != nullptr);
+ assert(op1->TypeIs(type));
- GenTree* constVectorDup1;
- constVector = impCloneExpr(constVector, &constVectorDup1, clsHnd, (unsigned)CHECK_SPILL_ALL,
- nullptr DEBUGARG("Clone constVector for vector Min"));
+ var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType);
+ assert(varTypeIsArithmetic(simdBaseType) && !varTypeIsLong(simdBaseType));
- GenTree* constVectorDup2;
- constVectorDup1 = impCloneExpr(constVectorDup1, &constVectorDup2, clsHnd, (unsigned)CHECK_SPILL_ALL,
- nullptr DEBUGARG("Clone constVector for vector Min"));
+ NamedIntrinsic intrinsic = NI_Illegal;
- // op1 = op1 - constVector
- // -or-
- // op1 = op1 + constVector
- op1 = gtNewSimdBinOpNode(fixupOp1, type, op1, constVector, opJitType, simdSize, isSimdAsHWIntrinsic);
+ GenTree* tmp1;
- // op2 = op2 - constVectorDup1
- // -or-
- // op2 = op2 + constVectorDup1
- op2 =
- gtNewSimdBinOpNode(fixupOp1, type, op2, constVectorDup1, opJitType, simdSize, isSimdAsHWIntrinsic);
+#if defined(TARGET_XARCH)
+ if (simdSize == 32)
+ {
+ assert(compIsaSupportedDebugOnly(InstructionSet_AVX));
+ assert(!varTypeIsIntegral(simdBaseType) || compIsaSupportedDebugOnly(InstructionSet_AVX2));
- // op1 = Min(op1, op2)
- op1 = gtNewSimdMinNode(type, op1, op2, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ tmp1 =
+ gtNewSimdHWIntrinsicNode(type, op1, NI_Vector256_GetLower, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
- // result = op1 + constVectorDup2
- // -or-
- // result = op1 - constVectorDup2
- return gtNewSimdBinOpNode(fixupOp2, type, op1, constVectorDup2, opJitType, simdSize,
- isSimdAsHWIntrinsic);
+ switch (simdBaseType)
+ {
+ case TYP_BYTE:
+ case TYP_UBYTE:
+ {
+ intrinsic = NI_AVX2_ConvertToVector256Int16;
+ break;
}
- case TYP_INT:
- case TYP_UINT:
- case TYP_LONG:
- case TYP_ULONG:
+ case TYP_SHORT:
+ case TYP_USHORT:
{
+ intrinsic = NI_AVX2_ConvertToVector256Int32;
break;
}
- case TYP_FLOAT:
+ case TYP_INT:
+ case TYP_UINT:
{
- intrinsic = NI_SSE_Min;
+ intrinsic = NI_AVX2_ConvertToVector256Int64;
break;
}
- case TYP_UBYTE:
- case TYP_SHORT:
- case TYP_DOUBLE:
+ case TYP_FLOAT:
{
- intrinsic = NI_SSE2_Min;
+ intrinsic = NI_AVX_ConvertToVector256Double;
break;
}
@@ -20876,94 +20896,113 @@ GenTree* Compiler::gtNewSimdMinNode(var_types type,
unreached();
}
}
+
+ assert(intrinsic != NI_Illegal);
+ return gtNewSimdHWIntrinsicNode(type, tmp1, intrinsic, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
}
-#elif defined(TARGET_ARM64)
- if (!varTypeIsLong(simdBaseType))
+ else if ((simdBaseType == TYP_FLOAT) || compOpportunisticallyDependsOn(InstructionSet_SSE41))
{
- if (simdBaseType == TYP_DOUBLE)
- {
- intrinsic = (simdSize == 8) ? NI_AdvSimd_Arm64_MinScalar : NI_AdvSimd_Arm64_Min;
- }
- else
+ switch (simdBaseType)
{
- intrinsic = NI_AdvSimd_Min;
- }
- }
-#else
-#error Unsupported platform
-#endif // !TARGET_XARCH && !TARGET_ARM64
-
- if (intrinsic != NI_Illegal)
- {
- return gtNewSimdHWIntrinsicNode(type, op1, op2, intrinsic, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
- }
+ case TYP_BYTE:
+ case TYP_UBYTE:
+ {
+ intrinsic = NI_SSE41_ConvertToVector128Int16;
+ break;
+ }
- GenTree* op1Dup;
- op1 = impCloneExpr(op1, &op1Dup, clsHnd, (unsigned)CHECK_SPILL_ALL, nullptr DEBUGARG("Clone op1 for vector Min"));
+ case TYP_SHORT:
+ case TYP_USHORT:
+ {
+ intrinsic = NI_SSE41_ConvertToVector128Int32;
+ break;
+ }
- GenTree* op2Dup;
- op2 = impCloneExpr(op2, &op2Dup, clsHnd, (unsigned)CHECK_SPILL_ALL, nullptr DEBUGARG("Clone op2 for vector Min"));
+ case TYP_INT:
+ case TYP_UINT:
+ {
+ intrinsic = NI_SSE41_ConvertToVector128Int64;
+ break;
+ }
- // op1 = op1 < op2
- op1 = gtNewSimdCmpOpNode(GT_LT, type, op1, op2, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ case TYP_FLOAT:
+ {
+ intrinsic = NI_SSE2_ConvertToVector128Double;
+ break;
+ }
- // result = ConditionalSelect(op1, op1Dup, op2Dup)
- return gtNewSimdCndSelNode(type, op1, op1Dup, op2Dup, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
-}
+ default:
+ {
+ unreached();
+ }
+ }
-GenTree* Compiler::gtNewSimdSqrtNode(
- var_types type, GenTree* op1, CorInfoType simdBaseJitType, unsigned simdSize, bool isSimdAsHWIntrinsic)
-{
- assert(IsBaselineSimdIsaSupportedDebugOnly());
+ assert(intrinsic != NI_Illegal);
+ return gtNewSimdHWIntrinsicNode(type, op1, intrinsic, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ }
+ else
+ {
+ tmp1 = gtNewSimdZeroNode(type, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
- assert(varTypeIsSIMD(type));
- assert(getSIMDTypeForSize(simdSize) == type);
+ if (varTypeIsSigned(simdBaseType))
+ {
+ CORINFO_CLASS_HANDLE clsHnd = gtGetStructHandleForSIMD(type, simdBaseJitType);
- assert(op1 != nullptr);
- assert(op1->TypeIs(type));
+ GenTree* op1Dup;
+ op1 = impCloneExpr(op1, &op1Dup, clsHnd, (unsigned)CHECK_SPILL_ALL,
+ nullptr DEBUGARG("Clone op1 for vector widen lower"));
- var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType);
- assert(varTypeIsFloating(simdBaseType));
+ tmp1 = gtNewSimdHWIntrinsicNode(type, op1, tmp1, NI_SSE2_CompareLessThan, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
- NamedIntrinsic intrinsic = NI_Illegal;
+ op1 = op1Dup;
+ }
-#if defined(TARGET_XARCH)
- if (simdSize == 32)
- {
- assert(compIsaSupportedDebugOnly(InstructionSet_AVX));
- intrinsic = NI_AVX_Sqrt;
+ return gtNewSimdHWIntrinsicNode(type, op1, tmp1, NI_SSE2_UnpackLow, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
}
- else if (simdBaseType == TYP_FLOAT)
+#elif defined(TARGET_ARM64)
+ if (simdSize == 16)
{
- intrinsic = NI_SSE_Sqrt;
+ tmp1 = gtNewSimdHWIntrinsicNode(TYP_SIMD8, op1, NI_Vector128_GetLower, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
}
else
{
- intrinsic = NI_SSE2_Sqrt;
+ assert(simdSize == 8);
+ tmp1 = op1;
}
-#elif defined(TARGET_ARM64)
- if ((simdSize == 8) && (simdBaseType == TYP_DOUBLE))
+
+ if (varTypeIsFloating(simdBaseType))
{
- intrinsic = NI_AdvSimd_SqrtScalar;
+ assert(simdBaseType == TYP_FLOAT);
+ intrinsic = NI_AdvSimd_Arm64_ConvertToDouble;
+ }
+ else if (varTypeIsSigned(simdBaseType))
+ {
+ intrinsic = NI_AdvSimd_SignExtendWideningLower;
}
else
{
- intrinsic = NI_AdvSimd_Arm64_Sqrt;
+ intrinsic = NI_AdvSimd_ZeroExtendWideningLower;
+ }
+
+ assert(intrinsic != NI_Illegal);
+ tmp1 = gtNewSimdHWIntrinsicNode(type, tmp1, intrinsic, simdBaseJitType, 8, isSimdAsHWIntrinsic);
+
+ if (simdSize == 8)
+ {
+ tmp1 = gtNewSimdHWIntrinsicNode(type, tmp1, NI_Vector128_GetLower, simdBaseJitType, 16, isSimdAsHWIntrinsic);
}
+
+ return tmp1;
#else
#error Unsupported platform
#endif // !TARGET_XARCH && !TARGET_ARM64
-
- assert(intrinsic != NI_Illegal);
- return gtNewSimdHWIntrinsicNode(type, op1, intrinsic, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
}
-GenTree* Compiler::gtNewSimdUnOpNode(genTreeOps op,
- var_types type,
- GenTree* op1,
- CorInfoType simdBaseJitType,
- unsigned simdSize,
- bool isSimdAsHWIntrinsic)
+GenTree* Compiler::gtNewSimdWidenUpperNode(
+ var_types type, GenTree* op1, CorInfoType simdBaseJitType, unsigned simdSize, bool isSimdAsHWIntrinsic)
{
assert(IsBaselineSimdIsaSupportedDebugOnly());
@@ -20974,78 +21013,184 @@ GenTree* Compiler::gtNewSimdUnOpNode(genTreeOps op,
assert(op1->TypeIs(type));
var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType);
- assert(varTypeIsArithmetic(simdBaseType));
+ assert(varTypeIsArithmetic(simdBaseType) && !varTypeIsLong(simdBaseType));
NamedIntrinsic intrinsic = NI_Illegal;
- GenTree* op2 = nullptr;
- switch (op)
- {
+ GenTree* tmp1;
+
#if defined(TARGET_XARCH)
- case GT_NEG:
+ if (simdSize == 32)
+ {
+ assert(compIsaSupportedDebugOnly(InstructionSet_AVX));
+ assert(!varTypeIsIntegral(simdBaseType) || compIsaSupportedDebugOnly(InstructionSet_AVX2));
+
+ tmp1 = gtNewSimdHWIntrinsicNode(type, op1, gtNewIconNode(1), NI_AVX_ExtractVector128, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+
+ switch (simdBaseType)
{
- if (simdSize == 32)
+ case TYP_BYTE:
+ case TYP_UBYTE:
{
- assert(compIsaSupportedDebugOnly(InstructionSet_AVX));
- assert(varTypeIsFloating(simdBaseType) || compIsaSupportedDebugOnly(InstructionSet_AVX2));
+ intrinsic = NI_AVX2_ConvertToVector256Int16;
+ break;
}
- op2 = gtNewSimdZeroNode(type, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
- // Zero - op1
- return gtNewSimdBinOpNode(GT_SUB, type, op2, op1, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
- }
+ case TYP_SHORT:
+ case TYP_USHORT:
+ {
+ intrinsic = NI_AVX2_ConvertToVector256Int32;
+ break;
+ }
- case GT_NOT:
- {
- assert((simdSize != 32) || compIsaSupportedDebugOnly(InstructionSet_AVX));
+ case TYP_INT:
+ case TYP_UINT:
+ {
+ intrinsic = NI_AVX2_ConvertToVector256Int64;
+ break;
+ }
- intrinsic = (simdSize == 32) ? NI_Vector256_get_AllBitsSet : NI_Vector128_get_AllBitsSet;
- op2 = gtNewSimdHWIntrinsicNode(type, intrinsic, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ case TYP_FLOAT:
+ {
+ intrinsic = NI_AVX_ConvertToVector256Double;
+ break;
+ }
- // op1 ^ AllBitsSet
- return gtNewSimdBinOpNode(GT_XOR, type, op1, op2, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ default:
+ {
+ unreached();
+ }
}
-#elif defined(TARGET_ARM64)
- case GT_NEG:
+
+ assert(intrinsic != NI_Illegal);
+ return gtNewSimdHWIntrinsicNode(type, tmp1, intrinsic, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ }
+ else if (varTypeIsFloating(simdBaseType))
+ {
+ assert(simdBaseType == TYP_FLOAT);
+ CORINFO_CLASS_HANDLE clsHnd = gtGetStructHandleForSIMD(type, simdBaseJitType);
+
+ GenTree* op1Dup;
+ op1 = impCloneExpr(op1, &op1Dup, clsHnd, (unsigned)CHECK_SPILL_ALL,
+ nullptr DEBUGARG("Clone op1 for vector widen upper"));
+
+ tmp1 = gtNewSimdHWIntrinsicNode(type, op1, op1Dup, NI_SSE_MoveHighToLow, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ return gtNewSimdHWIntrinsicNode(type, tmp1, NI_SSE2_ConvertToVector128Double, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ }
+ else if (compOpportunisticallyDependsOn(InstructionSet_SSE41))
+ {
+ tmp1 = gtNewSimdHWIntrinsicNode(type, op1, gtNewIconNode(8), NI_SSE2_ShiftRightLogical128BitLane,
+ simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+
+ switch (simdBaseType)
{
- if (varTypeIsSigned(simdBaseType))
+ case TYP_BYTE:
+ case TYP_UBYTE:
{
- if (simdBaseType == TYP_LONG)
- {
- intrinsic = (simdSize == 8) ? NI_AdvSimd_Arm64_NegateScalar : NI_AdvSimd_Arm64_Negate;
- }
- else if (simdBaseType == TYP_DOUBLE)
- {
- intrinsic = (simdSize == 8) ? NI_AdvSimd_NegateScalar : NI_AdvSimd_Arm64_Negate;
- }
- else
- {
- intrinsic = NI_AdvSimd_Negate;
- }
+ intrinsic = NI_SSE41_ConvertToVector128Int16;
+ break;
+ }
- return gtNewSimdHWIntrinsicNode(type, op1, intrinsic, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ case TYP_SHORT:
+ case TYP_USHORT:
+ {
+ intrinsic = NI_SSE41_ConvertToVector128Int32;
+ break;
}
- else
+
+ case TYP_INT:
+ case TYP_UINT:
{
- // Zero - op1
- op2 = gtNewSimdZeroNode(type, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
- return gtNewSimdBinOpNode(GT_SUB, type, op2, op1, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ intrinsic = NI_SSE41_ConvertToVector128Int64;
+ break;
+ }
+
+ default:
+ {
+ unreached();
}
}
- case GT_NOT:
+ assert(intrinsic != NI_Illegal);
+ return gtNewSimdHWIntrinsicNode(type, tmp1, intrinsic, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ }
+ else
+ {
+ tmp1 = gtNewSimdZeroNode(type, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+
+ if (varTypeIsSigned(simdBaseType))
{
- return gtNewSimdHWIntrinsicNode(type, op1, NI_AdvSimd_Not, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ CORINFO_CLASS_HANDLE clsHnd = gtGetStructHandleForSIMD(type, simdBaseJitType);
+
+ GenTree* op1Dup;
+ op1 = impCloneExpr(op1, &op1Dup, clsHnd, (unsigned)CHECK_SPILL_ALL,
+ nullptr DEBUGARG("Clone op1 for vector widen upper"));
+
+ tmp1 = gtNewSimdHWIntrinsicNode(type, op1, tmp1, NI_SSE2_CompareLessThan, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+
+ op1 = op1Dup;
}
-#else
-#error Unsupported platform
-#endif // !TARGET_XARCH && !TARGET_ARM64
- default:
+ return gtNewSimdHWIntrinsicNode(type, op1, tmp1, NI_SSE2_UnpackHigh, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
+ }
+#elif defined(TARGET_ARM64)
+ GenTree* zero;
+
+ if (simdSize == 16)
+ {
+ if (varTypeIsFloating(simdBaseType))
{
- unreached();
+ assert(simdBaseType == TYP_FLOAT);
+ intrinsic = NI_AdvSimd_Arm64_ConvertToDoubleUpper;
+ }
+ else if (varTypeIsSigned(simdBaseType))
+ {
+ intrinsic = NI_AdvSimd_SignExtendWideningUpper;
+ }
+ else
+ {
+ intrinsic = NI_AdvSimd_ZeroExtendWideningUpper;
+ }
+
+ assert(intrinsic != NI_Illegal);
+ return gtNewSimdHWIntrinsicNode(type, op1, intrinsic, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ }
+ else
+ {
+ assert(simdSize == 8);
+ ssize_t index = 8 / genTypeSize(simdBaseType);
+
+ if (varTypeIsFloating(simdBaseType))
+ {
+ assert(simdBaseType == TYP_FLOAT);
+ intrinsic = NI_AdvSimd_Arm64_ConvertToDouble;
+ }
+ else if (varTypeIsSigned(simdBaseType))
+ {
+ intrinsic = NI_AdvSimd_SignExtendWideningLower;
+ }
+ else
+ {
+ intrinsic = NI_AdvSimd_ZeroExtendWideningLower;
}
+
+ assert(intrinsic != NI_Illegal);
+
+ tmp1 = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, intrinsic, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
+ zero = gtNewSimdZeroNode(TYP_SIMD16, simdBaseJitType, 16, isSimdAsHWIntrinsic);
+ tmp1 = gtNewSimdHWIntrinsicNode(TYP_SIMD16, tmp1, zero, gtNewIconNode(index), NI_AdvSimd_ExtractVector128,
+ simdBaseJitType, 16, isSimdAsHWIntrinsic);
+ return gtNewSimdHWIntrinsicNode(type, tmp1, NI_Vector128_GetLower, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic);
}
+#else
+#error Unsupported platform
+#endif // !TARGET_XARCH && !TARGET_ARM64
}
GenTree* Compiler::gtNewSimdWithElementNode(var_types type,
@@ -21158,12 +21303,18 @@ GenTree* Compiler::gtNewSimdZeroNode(var_types type,
return gtNewSimdHWIntrinsicNode(type, intrinsic, simdBaseJitType, simdSize, isSimdAsHWIntrinsic);
}
+GenTreeHWIntrinsic* Compiler::gtNewScalarHWIntrinsicNode(var_types type, NamedIntrinsic hwIntrinsicID)
+{
+ return new (this, GT_HWINTRINSIC) GenTreeHWIntrinsic(type, getAllocator(CMK_ASTNode), hwIntrinsicID,
+ CORINFO_TYPE_UNDEF, 0, /* isSimdAsHWIntrinsic */ false);
+}
+
GenTreeHWIntrinsic* Compiler::gtNewScalarHWIntrinsicNode(var_types type, GenTree* op1, NamedIntrinsic hwIntrinsicID)
{
SetOpLclRelatedToSIMDIntrinsic(op1);
- return new (this, GT_HWINTRINSIC)
- GenTreeHWIntrinsic(type, op1, hwIntrinsicID, CORINFO_TYPE_UNDEF, 0, /* isSimdAsHWIntrinsic */ false);
+ return new (this, GT_HWINTRINSIC) GenTreeHWIntrinsic(type, getAllocator(CMK_ASTNode), hwIntrinsicID,
+ CORINFO_TYPE_UNDEF, 0, /* isSimdAsHWIntrinsic */ false, op1);
}
GenTreeHWIntrinsic* Compiler::gtNewScalarHWIntrinsicNode(var_types type,
@@ -21175,7 +21326,8 @@ GenTreeHWIntrinsic* Compiler::gtNewScalarHWIntrinsicNode(var_types type,
SetOpLclRelatedToSIMDIntrinsic(op2);
return new (this, GT_HWINTRINSIC)
- GenTreeHWIntrinsic(type, op1, op2, hwIntrinsicID, CORINFO_TYPE_UNDEF, 0, /* isSimdAsHWIntrinsic */ false);
+ GenTreeHWIntrinsic(type, getAllocator(CMK_ASTNode), hwIntrinsicID, CORINFO_TYPE_UNDEF, 0,
+ /* isSimdAsHWIntrinsic */ false, op1, op2);
}
GenTreeHWIntrinsic* Compiler::gtNewScalarHWIntrinsicNode(
@@ -21185,21 +21337,22 @@ GenTreeHWIntrinsic* Compiler::gtNewScalarHWIntrinsicNode(
SetOpLclRelatedToSIMDIntrinsic(op2);
SetOpLclRelatedToSIMDIntrinsic(op3);
- return new (this, GT_HWINTRINSIC) GenTreeHWIntrinsic(type, gtNewArgList(op1, op2, op3), hwIntrinsicID,
- CORINFO_TYPE_UNDEF, 0, /* isSimdAsHWIntrinsic */ false);
+ return new (this, GT_HWINTRINSIC)
+ GenTreeHWIntrinsic(type, getAllocator(CMK_ASTNode), hwIntrinsicID, CORINFO_TYPE_UNDEF, 0,
+ /* isSimdAsHWIntrinsic */ false, op1, op2, op3);
}
// Returns true for the HW Intrinsic instructions that have MemoryLoad semantics, false otherwise
bool GenTreeHWIntrinsic::OperIsMemoryLoad() const
{
#if defined(TARGET_XARCH) || defined(TARGET_ARM64)
- HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(gtHWIntrinsicId);
+ HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(GetHWIntrinsicId());
if (category == HW_Category_MemoryLoad)
{
return true;
}
#ifdef TARGET_XARCH
- else if (HWIntrinsicInfo::MaybeMemoryLoad(gtHWIntrinsicId))
+ else if (HWIntrinsicInfo::MaybeMemoryLoad(GetHWIntrinsicId()))
{
// Some intrinsics (without HW_Category_MemoryLoad) also have MemoryLoad semantics
@@ -21209,19 +21362,19 @@ bool GenTreeHWIntrinsic::OperIsMemoryLoad() const
// Vector128 BroadcastScalarToVector128(Vector128 value)
// Vector128 BroadcastScalarToVector128(byte* source)
// So, we need to check the argument's type is memory-reference or Vector128
- assert(HWIntrinsicInfo::lookupNumArgs(this) == 1);
- return (gtHWIntrinsicId == NI_AVX2_BroadcastScalarToVector128 ||
- gtHWIntrinsicId == NI_AVX2_BroadcastScalarToVector256) &&
- AsOp()->gtOp1->TypeGet() != TYP_SIMD16;
+ assert(GetOperandCount() == 1);
+ return (GetHWIntrinsicId() == NI_AVX2_BroadcastScalarToVector128 ||
+ GetHWIntrinsicId() == NI_AVX2_BroadcastScalarToVector256) &&
+ !Op(1)->TypeIs(TYP_SIMD16);
}
else if (category == HW_Category_IMM)
{
// Do we have less than 3 operands?
- if (HWIntrinsicInfo::lookupNumArgs(this) < 3)
+ if (GetOperandCount() < 3)
{
return false;
}
- else if (HWIntrinsicInfo::isAVX2GatherIntrinsic(gtHWIntrinsicId))
+ else if (HWIntrinsicInfo::isAVX2GatherIntrinsic(GetHWIntrinsicId()))
{
return true;
}
@@ -21236,13 +21389,13 @@ bool GenTreeHWIntrinsic::OperIsMemoryLoad() const
bool GenTreeHWIntrinsic::OperIsMemoryStore() const
{
#if defined(TARGET_XARCH) || defined(TARGET_ARM64)
- HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(gtHWIntrinsicId);
+ HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(GetHWIntrinsicId());
if (category == HW_Category_MemoryStore)
{
return true;
}
#ifdef TARGET_XARCH
- else if (HWIntrinsicInfo::MaybeMemoryStore(gtHWIntrinsicId) &&
+ else if (HWIntrinsicInfo::MaybeMemoryStore(GetHWIntrinsicId()) &&
(category == HW_Category_IMM || category == HW_Category_Scalar))
{
// Some intrinsics (without HW_Category_MemoryStore) also have MemoryStore semantics
@@ -21251,9 +21404,9 @@ bool GenTreeHWIntrinsic::OperIsMemoryStore() const
// unsafe ulong MultiplyNoFlags(ulong left, ulong right, ulong* low)
//
// So, the 3-argument form is MemoryStore
- if (HWIntrinsicInfo::lookupNumArgs(this) == 3)
+ if (GetOperandCount() == 3)
{
- switch (gtHWIntrinsicId)
+ switch (GetHWIntrinsicId())
{
case NI_BMI2_MultiplyNoFlags:
case NI_BMI2_X64_MultiplyNoFlags:
@@ -21278,6 +21431,39 @@ bool GenTreeHWIntrinsic::OperIsMemoryLoadOrStore() const
#endif
}
+NamedIntrinsic GenTreeHWIntrinsic::GetHWIntrinsicId() const
+{
+ NamedIntrinsic id = gtHWIntrinsicId;
+ int numArgs = HWIntrinsicInfo::lookupNumArgs(id);
+ bool numArgsUnknown = numArgs < 0;
+
+ assert((static_cast(numArgs) == GetOperandCount()) || numArgsUnknown);
+
+ return id;
+}
+
+void GenTreeHWIntrinsic::SetHWIntrinsicId(NamedIntrinsic intrinsicId)
+{
+#ifdef DEBUG
+ size_t oldOperandCount = GetOperandCount();
+ int newOperandCount = HWIntrinsicInfo::lookupNumArgs(intrinsicId);
+ bool newCountUnknown = newOperandCount < 0;
+
+ // We'll choose to trust the programmer here.
+ assert((oldOperandCount == static_cast(newOperandCount)) || newCountUnknown);
+#endif // DEBUG
+
+ gtHWIntrinsicId = intrinsicId;
+}
+
+// TODO-Review: why are layouts not compared here?
+/* static */ bool GenTreeHWIntrinsic::Equals(GenTreeHWIntrinsic* op1, GenTreeHWIntrinsic* op2)
+{
+ return (op1->TypeGet() == op2->TypeGet()) && (op1->GetHWIntrinsicId() == op2->GetHWIntrinsicId()) &&
+ (op1->GetSimdBaseType() == op2->GetSimdBaseType()) && (op1->GetSimdSize() == op2->GetSimdSize()) &&
+ (op1->GetAuxiliaryType() == op2->GetAuxiliaryType()) && (op1->GetOtherReg() == op2->GetOtherReg()) &&
+ OperandsAreEqual(op1, op2);
+}
#endif // FEATURE_HW_INTRINSICS
//---------------------------------------------------------------------------------------
@@ -21712,6 +21898,53 @@ uint16_t GenTreeLclVarCommon::GetLclOffs() const
}
}
+#if defined(TARGET_XARCH) && defined(FEATURE_HW_INTRINSICS)
+//------------------------------------------------------------------------
+// GetResultOpNumForFMA: check if the result is written into one of the operands.
+// In the case that none of the operand is overwritten, check if any of them is lastUse.
+//
+// Return Value:
+// The operand number overwritten or lastUse. 0 is the default value, where the result is written into
+// a destination that is not one of the source operands and there is no last use op.
+//
+unsigned GenTreeHWIntrinsic::GetResultOpNumForFMA(GenTree* use, GenTree* op1, GenTree* op2, GenTree* op3)
+{
+ // only FMA intrinsic node should call into this function
+ assert(HWIntrinsicInfo::lookupIsa(gtHWIntrinsicId) == InstructionSet_FMA);
+ if (use != nullptr && use->OperIs(GT_STORE_LCL_VAR))
+ {
+ // For store_lcl_var, check if any op is overwritten
+
+ GenTreeLclVarCommon* overwritten = use->AsLclVarCommon();
+ unsigned overwrittenLclNum = overwritten->GetLclNum();
+ if (op1->IsLocal() && op1->AsLclVarCommon()->GetLclNum() == overwrittenLclNum)
+ {
+ return 1;
+ }
+ else if (op2->IsLocal() && op2->AsLclVarCommon()->GetLclNum() == overwrittenLclNum)
+ {
+ return 2;
+ }
+ else if (op3->IsLocal() && op3->AsLclVarCommon()->GetLclNum() == overwrittenLclNum)
+ {
+ return 3;
+ }
+ }
+
+ // If no overwritten op, check if there is any last use op
+ // https://github.com/dotnet/runtime/issues/62215
+
+ if (op1->OperIs(GT_LCL_VAR) && op1->IsLastUse(0))
+ return 1;
+ else if (op2->OperIs(GT_LCL_VAR) && op2->IsLastUse(0))
+ return 2;
+ else if (op3->OperIs(GT_LCL_VAR) && op3->IsLastUse(0))
+ return 3;
+
+ return 0;
+}
+#endif // TARGET_XARCH && FEATURE_HW_INTRINSICS
+
#ifdef TARGET_ARM
//------------------------------------------------------------------------
// IsOffsetMisaligned: check if the field needs a special handling on arm.
diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h
index 091c2464e69502..1a0865073982dc 100644
--- a/src/coreclr/jit/gentree.h
+++ b/src/coreclr/jit/gentree.h
@@ -21,13 +21,13 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#include "vartype.h" // For "var_types"
#include "target.h" // For "regNumber"
#include "ssaconfig.h" // For "SsaConfig::RESERVED_SSA_NUM"
-#include "reglist.h"
#include "valuenumtype.h"
#include "jitstd.h"
#include "jithashtable.h"
#include "simd.h"
#include "namedintrinsiclist.h"
#include "layout.h"
+#include "debuginfo.h"
// Debugging GenTree is much easier if we add a magic virtual function to make the debugger able to figure out what type
// it's got. This is enabled by default in DEBUG. To enable it in RET builds (temporarily!), you need to change the
@@ -527,9 +527,10 @@ enum GenTreeFlags : unsigned int
GTF_RELOP_NAN_UN = 0x80000000, // GT_ -- Is branch taken if ops are NaN?
GTF_RELOP_JMP_USED = 0x40000000, // GT_ -- result of compare used for jump or ?:
- GTF_RELOP_QMARK = 0x20000000, // GT_ -- the node is the condition for ?:
GTF_RELOP_ZTT = 0x08000000, // GT_ -- Loop test cloned for converting while-loops into do-while
// with explicit "loop test" in the header block.
+ GTF_RELOP_SJUMP_OPT = 0x04000000, // GT_ -- Swap signed jl/jge with js/jns during emitter, reuses flags
+ // from previous instruction.
GTF_JCMP_EQ = 0x80000000, // GTF_JCMP_EQ -- Branch on equal rather than not equal
GTF_JCMP_TST = 0x40000000, // GTF_JCMP_TST -- Use bit test instruction rather than compare against zero instruction
@@ -879,6 +880,10 @@ struct GenTree
// This stores the register assigned to the node. If a register is not assigned, _gtRegNum is set to REG_NA.
regNumberSmall _gtRegNum;
+ // Count of operands. Used *only* by GenTreeMultiOp, exists solely due to padding constraints.
+ friend struct GenTreeMultiOp;
+ uint8_t m_operandCount;
+
public:
// The register number is stored in a small format (8 bits), but the getters return and the setters take
// a full-size (unsigned) format, to localize the casts here.
@@ -1090,11 +1095,6 @@ struct GenTree
// NOPs may only be present in LIR if they do not produce a value.
return IsNothingNode();
- case GT_LIST:
- // LIST nodes may not be present in a block's LIR sequence, but they may
- // be present as children of an LIR node.
- return (gtNext == nullptr) && (gtPrev == nullptr);
-
case GT_ADDR:
{
// ADDR ndoes may only be present in LIR if the location they refer to is not a
@@ -1620,6 +1620,16 @@ struct GenTree
OperIsStoreBlk(gtOper) || OperIsAtomicOp(gtOper));
}
+ static bool OperIsMultiOp(genTreeOps gtOper)
+ {
+ return OperIsSIMD(gtOper) || OperIsHWIntrinsic(gtOper);
+ }
+
+ bool OperIsMultiOp() const
+ {
+ return OperIsMultiOp(OperGet());
+ }
+
// This is here for cleaner FEATURE_SIMD #ifdefs.
static bool OperIsSIMD(genTreeOps gtOper)
{
@@ -1703,16 +1713,13 @@ struct GenTree
#ifdef DEBUG
bool NullOp1Legal() const
{
- assert(OperIsSimple(gtOper));
+ assert(OperIsSimple());
switch (gtOper)
{
case GT_LEA:
case GT_RETFILT:
case GT_NOP:
case GT_FIELD:
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HWINTRINSIC:
-#endif // FEATURE_HW_INTRINSICS
return true;
case GT_RETURN:
return gtType == TYP_VOID;
@@ -1730,17 +1737,8 @@ struct GenTree
}
switch (gtOper)
{
- case GT_LIST:
case GT_INTRINSIC:
case GT_LEA:
-#ifdef FEATURE_SIMD
- case GT_SIMD:
-#endif // !FEATURE_SIMD
-
-#ifdef FEATURE_HW_INTRINSICS
- case GT_HWINTRINSIC:
-#endif // FEATURE_HW_INTRINSICS
-
#if defined(TARGET_ARM)
case GT_PUTARG_REG:
#endif // defined(TARGET_ARM)
@@ -1762,52 +1760,29 @@ struct GenTree
inline bool IsBoxedValue();
- static bool OperIsList(genTreeOps gtOper)
- {
- return gtOper == GT_LIST;
- }
-
- bool OperIsList() const
- {
- return OperIsList(gtOper);
- }
-
- static bool OperIsAnyList(genTreeOps gtOper)
- {
- return OperIsList(gtOper);
- }
-
- bool OperIsAnyList() const
- {
- return OperIsAnyList(gtOper);
- }
-
inline GenTree* gtGetOp1() const;
// Directly return op2. Asserts the node is binary. Might return nullptr if the binary node allows
- // a nullptr op2, such as GT_LIST. This is more efficient than gtGetOp2IfPresent() if you know what
+ // a nullptr op2, such as GT_LEA. This is more efficient than gtGetOp2IfPresent() if you know what
// node type you have.
inline GenTree* gtGetOp2() const;
// The returned pointer might be nullptr if the node is not binary, or if non-null op2 is not required.
inline GenTree* gtGetOp2IfPresent() const;
- // Given a tree node, if this is a child of that node, return the pointer to the child node so that it
- // can be modified; otherwise, return null.
- GenTree** gtGetChildPointer(GenTree* parent) const;
+ bool TryGetUse(GenTree* operand, GenTree*** pUse);
- // Given a tree node, if this node uses that node, return the use as an out parameter and return true.
- // Otherwise, return false.
- bool TryGetUse(GenTree* def, GenTree*** use);
+ bool TryGetUse(GenTree* operand)
+ {
+ GenTree** unusedUse = nullptr;
+ return TryGetUse(operand, &unusedUse);
+ }
private:
- bool TryGetUseList(GenTree* def, GenTree*** use);
-
- bool TryGetUseBinOp(GenTree* def, GenTree*** use);
+ bool TryGetUseBinOp(GenTree* operand, GenTree*** pUse);
public:
- // Get the parent of this node, and optionally capture the pointer to the child so that it can be modified.
- GenTree* gtGetParent(GenTree*** parentChildPtrPtr) const;
+ GenTree* gtGetParent(GenTree*** pUse);
void ReplaceOperand(GenTree** useEdge, GenTree* replacement);
@@ -2104,6 +2079,16 @@ struct GenTree
return (gtFlags & GTF_REVERSE_OPS) ? true : false;
}
+ void SetReverseOp()
+ {
+ gtFlags |= GTF_REVERSE_OPS;
+ }
+
+ void ClearReverseOp()
+ {
+ gtFlags &= ~GTF_REVERSE_OPS;
+ }
+
bool IsUnsigned() const
{
return ((gtFlags & GTF_UNSIGNED) != 0);
@@ -2251,12 +2236,6 @@ struct GenTree
{
}
- // Returns the number of children of the current node.
- unsigned NumChildren();
-
- // Requires "childNum < NumChildren()". Returns the "n"th child of "this."
- GenTree* GetChild(unsigned childNum);
-
// Returns an iterator that will produce the use edge to each operand of this node. Differs
// from the sequence of nodes produced by a loop over `GetChild` in its handling of call, phi,
// and block op nodes.
@@ -2265,13 +2244,11 @@ struct GenTree
IteratorPair UseEdges();
- // Returns an iterator that will produce each operand of this node. Differs from the sequence
- // of nodes produced by a loop over `GetChild` in its handling of call, phi, and block op
- // nodes.
+ // Returns an iterator that will produce each operand of this node, in execution order.
GenTreeOperandIterator OperandsBegin();
GenTreeOperandIterator OperandsEnd();
- // Returns a range that will produce the operands of this node in use order.
+ // Returns a range that will produce the operands of this node in execution order.
IteratorPair Operands();
enum class VisitResult
@@ -2301,9 +2278,6 @@ struct GenTree
void VisitOperands(TVisitor visitor);
private:
- template
- VisitResult VisitListOperands(TVisitor visitor);
-
template
void VisitBinOpOperands(TVisitor visitor);
@@ -2786,10 +2760,6 @@ struct GenTreeFieldList : public GenTree
// GenTreeUseEdgeIterator: an iterator that will produce each use edge of a GenTree node in the order in which
// they are used.
//
-// The use edges of a node may not correspond exactly to the nodes on the other ends of its use edges: in
-// particular, GT_LIST nodes are expanded into their component parts. This differs from the behavior of
-// GenTree::GetChildPointer(), which does not expand lists.
-//
// Operand iteration is common enough in the back end of the compiler that the implementation of this type has
// traded some simplicity for speed:
// - As much work as is reasonable is done in the constructor rather than during operand iteration
@@ -2822,7 +2792,8 @@ class GenTreeUseEdgeIterator final
AdvanceFn m_advance;
GenTree* m_node;
GenTree** m_edge;
- // Pointer sized state storage, GenTreeArgList* or GenTreePhi::Use* or GenTreeCall::Use* currently.
+ // Pointer sized state storage, GenTreePhi::Use* or GenTreeCall::Use*
+ // or the exclusive end/beginning of GenTreeMultiOp's operand array.
void* m_statePtr;
// Integer sized state storage, usually the operand index for non-list based nodes.
int m_state;
@@ -2842,14 +2813,16 @@ class GenTreeUseEdgeIterator final
void AdvanceBinOp();
void SetEntryStateForBinOp();
- // An advance function for list-like nodes (Phi, SIMDIntrinsicInitN, FieldList)
- void AdvanceList();
- void SetEntryStateForList(GenTreeArgList* list);
-
// The advance function for call nodes
template
void AdvanceCall();
+#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+ void AdvanceMultiOp();
+ void AdvanceReversedMultiOp();
+ void SetEntryStateForMultiOp();
+#endif
+
void Terminate();
public:
@@ -2890,8 +2863,7 @@ class GenTreeUseEdgeIterator final
// GenTreeOperandIterator: an iterator that will produce each operand of a
// GenTree node in the order in which they are
// used. This uses `GenTreeUseEdgeIterator` under
-// the covers and comes with the same caveats
-// w.r.t. `GetChild`.
+// the covers.
//
// Note: valid values of this type may be obtained by calling
// `GenTree::OperandsBegin` and `GenTree::OperandsEnd`.
@@ -3027,6 +2999,13 @@ struct GenTreeOp : public GenTreeUnOp
{
}
#endif
+
+ // True if this relop is marked for a transform during the emitter
+ // phase, e.g., jge => jns
+ bool MarkedForSignJumpOpt() const
+ {
+ return (gtFlags & GTF_RELOP_SJUMP_OPT) != 0;
+ }
};
struct GenTreeVal : public GenTree
@@ -3724,54 +3703,6 @@ struct GenTreeField : public GenTreeUnOp
}
};
-// Represents the Argument list of a call node, as a Lisp-style linked list.
-// (Originally I had hoped that this could have *only* the m_arg/m_rest fields, but it turns out
-// that enough of the GenTree mechanism is used that it makes sense just to make it a subtype. But
-// note that in many ways, this is *not* a "real" node of the tree, but rather a mechanism for
-// giving call nodes a flexible number of children. GenTreeArgListNodes never evaluate to registers,
-// for example.)
-
-// Note that while this extends GenTreeOp, it is *not* an EXOP. We don't add any new fields, and one
-// is free to allocate a GenTreeOp of type GT_LIST. If you use this type, you get the convenient Current/Rest
-// method names for the arguments.
-struct GenTreeArgList : public GenTreeOp
-{
- GenTree*& Current()
- {
- return gtOp1;
- }
- GenTreeArgList*& Rest()
- {
- assert(gtOp2 == nullptr || gtOp2->OperIsAnyList());
- return *reinterpret_cast(>Op2);
- }
-
-#if DEBUGGABLE_GENTREE
- GenTreeArgList() : GenTreeOp()
- {
- }
-#endif
-
- GenTreeArgList(GenTree* arg) : GenTreeArgList(arg, nullptr)
- {
- }
-
- GenTreeArgList(GenTree* arg, GenTreeArgList* rest) : GenTreeArgList(GT_LIST, arg, rest)
- {
- }
-
- GenTreeArgList(genTreeOps oper, GenTree* arg, GenTreeArgList* rest) : GenTreeOp(oper, TYP_VOID, arg, rest)
- {
- assert(OperIsAnyList(oper));
- assert((arg != nullptr) && arg->IsValidCallArgument());
- gtFlags |= arg->gtFlags & GTF_ALL_EFFECT;
- if (rest != nullptr)
- {
- gtFlags |= rest->gtFlags & GTF_ALL_EFFECT;
- }
- }
-};
-
// There was quite a bit of confusion in the code base about which of gtOp1 and gtOp2 was the
// 'then' and 'else' clause of a colon node. Adding these accessors, while not enforcing anything,
// at least *allows* the programmer to be obviously correct.
@@ -4244,11 +4175,6 @@ struct GenTreeCall final : public GenTree
return UseList(gtCallLateArgs);
}
-#if !FEATURE_FIXED_OUT_ARGS
- int regArgListCount;
- regList regArgList;
-#endif
-
#ifdef DEBUG
// Used to register callsites with the EE
CORINFO_SIG_INFO* callSig;
@@ -4866,6 +4792,11 @@ struct GenTreeCall final : public GenTree
// IL offset of the call wrt its parent method.
IL_OFFSET gtRawILOffset;
+
+ // In DEBUG we report even non inline candidates in the inline tree in
+ // fgNoteNonInlineCandidate. We need to keep around the inline context for
+ // this as normally it's part of the candidate info.
+ class InlineContext* gtInlineContext;
#endif // defined(DEBUG) || defined(INLINE_DATA)
bool IsHelperCall() const
@@ -5116,18 +5047,243 @@ struct GenTreeIntrinsic : public GenTreeOp
#endif
};
-struct GenTreeJitIntrinsic : public GenTreeOp
+// GenTreeMultiOp - a node with a flexible count of operands stored in an array.
+// The array can be an inline one, or a dynamic one, or both, with switching
+// between them supported. See GenTreeJitIntrinsic for an example of a node
+// utilizing GenTreeMultiOp. GTF_REVERSE_OPS is supported for GenTreeMultiOp's
+// with two operands.
+//
+struct GenTreeMultiOp : public GenTree
{
+public:
+ class Iterator
+ {
+ protected:
+ GenTree** m_use;
+
+ Iterator(GenTree** use) : m_use(use)
+ {
+ }
+
+ public:
+ Iterator& operator++()
+ {
+ m_use++;
+ return *this;
+ }
+
+ bool operator==(const Iterator& other) const
+ {
+ return m_use == other.m_use;
+ }
+
+ bool operator!=(const Iterator& other) const
+ {
+ return m_use != other.m_use;
+ }
+ };
+
+ class OperandsIterator final : public Iterator
+ {
+ public:
+ OperandsIterator(GenTree** use) : Iterator(use)
+ {
+ }
+
+ GenTree* operator*()
+ {
+ return *m_use;
+ }
+ };
+
+ class UseEdgesIterator final : public Iterator
+ {
+ public:
+ UseEdgesIterator(GenTree** use) : Iterator(use)
+ {
+ }
+
+ GenTree** operator*()
+ {
+ return m_use;
+ }
+ };
+
private:
- ClassLayout* gtLayout;
+ GenTree** m_operands;
- unsigned char gtAuxiliaryJitType; // For intrinsics than need another type (e.g. Avx2.Gather* or SIMD (by element))
- regNumberSmall gtOtherReg; // For intrinsics that return 2 registers
+protected:
+ template
+ GenTreeMultiOp(genTreeOps oper,
+ var_types type,
+ CompAllocator allocator,
+ GenTree* (&inlineOperands)[InlineOperandCount] DEBUGARG(bool largeNode),
+ Operands... operands)
+ : GenTree(oper, type DEBUGARG(largeNode))
+ {
+ const size_t OperandCount = sizeof...(Operands);
- unsigned char gtSimdBaseJitType; // SIMD vector base JIT type
- unsigned char gtSimdSize; // SIMD vector size in bytes, use 0 for scalar intrinsics
+ m_operands = (OperandCount <= InlineOperandCount) ? inlineOperands : allocator.allocate(OperandCount);
+
+ // "OperandCount + 1" so that it works well when OperandCount is 0.
+ GenTree* operandsArray[OperandCount + 1]{operands...};
+ InitializeOperands(operandsArray, OperandCount);
+ }
+
+ // Note that this constructor takes the owndership of the "operands" array.
+ template
+ GenTreeMultiOp(genTreeOps oper,
+ var_types type,
+ GenTree** operands,
+ size_t operandCount,
+ GenTree* (&inlineOperands)[InlineOperandCount] DEBUGARG(bool largeNode))
+ : GenTree(oper, type DEBUGARG(largeNode))
+ {
+ m_operands = (operandCount <= InlineOperandCount) ? inlineOperands : operands;
+
+ InitializeOperands(operands, operandCount);
+ }
public:
+#if DEBUGGABLE_GENTREE
+ GenTreeMultiOp() : GenTree()
+ {
+ }
+#endif
+
+ GenTree*& Op(size_t index)
+ {
+ size_t actualIndex = index - 1;
+ assert(actualIndex < m_operandCount);
+ assert(m_operands[actualIndex] != nullptr);
+
+ return m_operands[actualIndex];
+ }
+
+ GenTree* Op(size_t index) const
+ {
+ return const_cast(this)->Op(index);
+ }
+
+ // Note that unlike the general "Operands" iterator, this specialized version does not respect GTF_REVERSE_OPS.
+ IteratorPair Operands()
+ {
+ return MakeIteratorPair(OperandsIterator(GetOperandArray()),
+ OperandsIterator(GetOperandArray() + GetOperandCount()));
+ }
+
+ // Note that unlike the general "UseEdges" iterator, this specialized version does not respect GTF_REVERSE_OPS.
+ IteratorPair UseEdges()
+ {
+ return MakeIteratorPair(UseEdgesIterator(GetOperandArray()),
+ UseEdgesIterator(GetOperandArray() + GetOperandCount()));
+ }
+
+ size_t GetOperandCount() const
+ {
+ return m_operandCount;
+ }
+
+ GenTree** GetOperandArray(size_t startIndex = 0) const
+ {
+ return m_operands + startIndex;
+ }
+
+protected:
+ // Reconfigures the operand array, leaving it in a "dirty" state.
+ void ResetOperandArray(size_t newOperandCount,
+ Compiler* compiler,
+ GenTree** inlineOperands,
+ size_t inlineOperandCount);
+
+ static bool OperandsAreEqual(GenTreeMultiOp* op1, GenTreeMultiOp* op2);
+
+private:
+ void InitializeOperands(GenTree** operands, size_t operandCount);
+
+ void SetOperandCount(size_t newOperandCount)
+ {
+ assert(FitsIn(newOperandCount));
+ m_operandCount = static_cast(newOperandCount);
+ }
+};
+
+// Helper class used to implement the constructor of GenTreeJitIntrinsic which
+// transfers the ownership of the passed-in array to the underlying MultiOp node.
+class IntrinsicNodeBuilder final
+{
+ friend struct GenTreeJitIntrinsic;
+
+ GenTree** m_operands;
+ size_t m_operandCount;
+ GenTree* m_inlineOperands[2];
+
+public:
+ IntrinsicNodeBuilder(CompAllocator allocator, size_t operandCount) : m_operandCount(operandCount)
+ {
+ m_operands =
+ (operandCount <= ArrLen(m_inlineOperands)) ? m_inlineOperands : allocator.allocate(operandCount);
+#ifdef DEBUG
+ for (size_t i = 0; i < operandCount; i++)
+ {
+ m_operands[i] = nullptr;
+ }
+#endif // DEBUG
+ }
+
+ IntrinsicNodeBuilder(CompAllocator allocator, GenTreeMultiOp* source) : m_operandCount(source->GetOperandCount())
+ {
+ m_operands = (m_operandCount <= ArrLen(m_inlineOperands)) ? m_inlineOperands
+ : allocator.allocate(m_operandCount);
+ for (size_t i = 0; i < m_operandCount; i++)
+ {
+ m_operands[i] = source->Op(i + 1);
+ }
+ }
+
+ void AddOperand(size_t index, GenTree* operand)
+ {
+ assert(index < m_operandCount);
+ assert(m_operands[index] == nullptr);
+ m_operands[index] = operand;
+ }
+
+ GenTree* GetOperand(size_t index) const
+ {
+ assert(index < m_operandCount);
+ assert(m_operands[index] != nullptr);
+ return m_operands[index];
+ }
+
+ size_t GetOperandCount() const
+ {
+ return m_operandCount;
+ }
+
+private:
+ GenTree** GetBuiltOperands()
+ {
+#ifdef DEBUG
+ for (size_t i = 0; i < m_operandCount; i++)
+ {
+ assert(m_operands[i] != nullptr);
+ }
+#endif // DEBUG
+
+ return m_operands;
+ }
+};
+
+struct GenTreeJitIntrinsic : public GenTreeMultiOp
+{
+protected:
+ GenTree* gtInlineOperands[2];
+ uint16_t gtLayoutNum;
+ unsigned char gtAuxiliaryJitType; // For intrinsics than need another type (e.g. Avx2.Gather* or SIMD (by element))
+ regNumberSmall gtOtherReg; // For intrinsics that return 2 registers
+ unsigned char gtSimdBaseJitType; // SIMD vector base JIT type
+ unsigned char gtSimdSize; // SIMD vector size in bytes, use 0 for scalar intrinsics
+
#if defined(FEATURE_SIMD)
union {
SIMDIntrinsicID gtSIMDIntrinsicID; // operation Id
@@ -5137,15 +5293,16 @@ struct GenTreeJitIntrinsic : public GenTreeOp
NamedIntrinsic gtHWIntrinsicId;
#endif
- ClassLayout* GetLayout() const
+public:
+ unsigned GetLayoutNum() const
{
- return gtLayout;
+ return gtLayoutNum;
}
- void SetLayout(ClassLayout* layout)
+ void SetLayoutNum(unsigned layoutNum)
{
- assert(layout != nullptr);
- gtLayout = layout;
+ assert(FitsIn(layoutNum));
+ gtLayoutNum = static_cast(layoutNum);
}
regNumber GetOtherReg() const
@@ -5196,10 +5353,15 @@ struct GenTreeJitIntrinsic : public GenTreeOp
assert(gtSimdSize == simdSize);
}
- GenTreeJitIntrinsic(
- genTreeOps oper, var_types type, GenTree* op1, GenTree* op2, CorInfoType simdBaseJitType, unsigned simdSize)
- : GenTreeOp(oper, type, op1, op2)
- , gtLayout(nullptr)
+ template
+ GenTreeJitIntrinsic(genTreeOps oper,
+ var_types type,
+ CompAllocator allocator,
+ CorInfoType simdBaseJitType,
+ unsigned simdSize,
+ Operands... operands)
+ : GenTreeMultiOp(oper, type, allocator, gtInlineOperands DEBUGARG(false), operands...)
+ , gtLayoutNum(0)
, gtAuxiliaryJitType(CORINFO_TYPE_UNDEF)
, gtOtherReg(REG_NA)
, gtSimdBaseJitType((unsigned char)simdBaseJitType)
@@ -5210,16 +5372,39 @@ struct GenTreeJitIntrinsic : public GenTreeOp
assert(gtSimdSize == simdSize);
}
- bool isSIMD() const
+#if DEBUGGABLE_GENTREE
+ GenTreeJitIntrinsic() : GenTreeMultiOp()
{
- return gtSimdSize != 0;
}
+#endif
-#if DEBUGGABLE_GENTREE
- GenTreeJitIntrinsic() : GenTreeOp()
+protected:
+ GenTreeJitIntrinsic(genTreeOps oper,
+ var_types type,
+ IntrinsicNodeBuilder&& nodeBuilder,
+ CorInfoType simdBaseJitType,
+ unsigned simdSize)
+ : GenTreeMultiOp(oper,
+ type,
+ nodeBuilder.GetBuiltOperands(),
+ nodeBuilder.GetOperandCount(),
+ gtInlineOperands DEBUGARG(false))
+ , gtLayoutNum(0)
+ , gtAuxiliaryJitType(CORINFO_TYPE_UNDEF)
+ , gtOtherReg(REG_NA)
+ , gtSimdBaseJitType((unsigned char)simdBaseJitType)
+ , gtSimdSize((unsigned char)simdSize)
+ , gtHWIntrinsicId(NI_Illegal)
{
+ assert(gtSimdBaseJitType == simdBaseJitType);
+ assert(gtSimdSize == simdSize);
+ }
+
+public:
+ bool isSIMD() const
+ {
+ return gtSimdSize != 0;
}
-#endif
};
#ifdef FEATURE_SIMD
@@ -5227,63 +5412,69 @@ struct GenTreeJitIntrinsic : public GenTreeOp
/* gtSIMD -- SIMD intrinsic (possibly-binary op [NULL op2 is allowed] with additional fields) */
struct GenTreeSIMD : public GenTreeJitIntrinsic
{
+ GenTreeSIMD(var_types type,
+ IntrinsicNodeBuilder&& nodeBuilder,
+ SIMDIntrinsicID simdIntrinsicID,
+ CorInfoType simdBaseJitType,
+ unsigned simdSize)
+ : GenTreeJitIntrinsic(GT_SIMD, type, std::move(nodeBuilder), simdBaseJitType, simdSize)
+ {
+ gtSIMDIntrinsicID = simdIntrinsicID;
+ }
- GenTreeSIMD(
- var_types type, GenTree* op1, SIMDIntrinsicID simdIntrinsicID, CorInfoType simdBaseJitType, unsigned simdSize)
- : GenTreeJitIntrinsic(GT_SIMD, type, op1, nullptr, simdBaseJitType, simdSize)
+ GenTreeSIMD(var_types type,
+ CompAllocator allocator,
+ GenTree* op1,
+ SIMDIntrinsicID simdIntrinsicID,
+ CorInfoType simdBaseJitType,
+ unsigned simdSize)
+ : GenTreeJitIntrinsic(GT_SIMD, type, allocator, simdBaseJitType, simdSize, op1)
{
gtSIMDIntrinsicID = simdIntrinsicID;
}
GenTreeSIMD(var_types type,
+ CompAllocator allocator,
GenTree* op1,
GenTree* op2,
SIMDIntrinsicID simdIntrinsicID,
CorInfoType simdBaseJitType,
unsigned simdSize)
- : GenTreeJitIntrinsic(GT_SIMD, type, op1, op2, simdBaseJitType, simdSize)
+ : GenTreeJitIntrinsic(GT_SIMD, type, allocator, simdBaseJitType, simdSize, op1, op2)
{
gtSIMDIntrinsicID = simdIntrinsicID;
}
- bool OperIsMemoryLoad() const; // Returns true for the SIMD Intrinsic instructions that have MemoryLoad semantics,
- // false otherwise
-
#if DEBUGGABLE_GENTREE
GenTreeSIMD() : GenTreeJitIntrinsic()
{
}
#endif
+
+ bool OperIsMemoryLoad() const; // Returns true for the SIMD Intrinsic instructions that have MemoryLoad semantics,
+ // false otherwise
+
+ SIMDIntrinsicID GetSIMDIntrinsicId() const
+ {
+ return gtSIMDIntrinsicID;
+ }
+
+ static bool Equals(GenTreeSIMD* op1, GenTreeSIMD* op2);
};
#endif // FEATURE_SIMD
#ifdef FEATURE_HW_INTRINSICS
struct GenTreeHWIntrinsic : public GenTreeJitIntrinsic
{
- GenTreeHWIntrinsic(var_types type,
- NamedIntrinsic hwIntrinsicID,
- CorInfoType simdBaseJitType,
- unsigned simdSize,
- bool isSimdAsHWIntrinsic)
- : GenTreeJitIntrinsic(GT_HWINTRINSIC, type, nullptr, nullptr, simdBaseJitType, simdSize)
- {
- gtHWIntrinsicId = hwIntrinsicID;
-
- if (isSimdAsHWIntrinsic)
- {
- gtFlags |= GTF_SIMDASHW_OP;
- }
- }
-
- GenTreeHWIntrinsic(var_types type,
- GenTree* op1,
- NamedIntrinsic hwIntrinsicID,
- CorInfoType simdBaseJitType,
- unsigned simdSize,
- bool isSimdAsHWIntrinsic)
- : GenTreeJitIntrinsic(GT_HWINTRINSIC, type, op1, nullptr, simdBaseJitType, simdSize)
+ GenTreeHWIntrinsic(var_types type,
+ IntrinsicNodeBuilder&& nodeBuilder,
+ NamedIntrinsic hwIntrinsicID,
+ CorInfoType simdBaseJitType,
+ unsigned simdSize,
+ bool isSimdAsHWIntrinsic)
+ : GenTreeJitIntrinsic(GT_HWINTRINSIC, type, std::move(nodeBuilder), simdBaseJitType, simdSize)
{
- gtHWIntrinsicId = hwIntrinsicID;
+ SetHWIntrinsicId(hwIntrinsicID);
if (OperIsMemoryStore())
{
@@ -5296,18 +5487,19 @@ struct GenTreeHWIntrinsic : public GenTreeJitIntrinsic
}
}
+ template
GenTreeHWIntrinsic(var_types type,
- GenTree* op1,
- GenTree* op2,
+ CompAllocator allocator,
NamedIntrinsic hwIntrinsicID,
CorInfoType simdBaseJitType,
unsigned simdSize,
- bool isSimdAsHWIntrinsic)
- : GenTreeJitIntrinsic(GT_HWINTRINSIC, type, op1, op2, simdBaseJitType, simdSize)
+ bool isSimdAsHWIntrinsic,
+ Operands... operands)
+ : GenTreeJitIntrinsic(GT_HWINTRINSIC, type, allocator, simdBaseJitType, simdSize, operands...)
{
- gtHWIntrinsicId = hwIntrinsicID;
+ SetHWIntrinsicId(hwIntrinsicID);
- if (OperIsMemoryStore())
+ if ((sizeof...(Operands) > 0) && OperIsMemoryStore())
{
gtFlags |= (GTF_GLOB_REF | GTF_ASG);
}
@@ -5318,9 +5510,11 @@ struct GenTreeHWIntrinsic : public GenTreeJitIntrinsic
}
}
- // Note that HW Intrinsic instructions are a sub class of GenTreeOp which only supports two operands
- // However there are HW Intrinsic instructions that have 3 or even 4 operands and this is
- // supported using a single op1 and using an ArgList for it: gtNewArgList(op1, op2, op3)
+#if DEBUGGABLE_GENTREE
+ GenTreeHWIntrinsic() : GenTreeJitIntrinsic()
+ {
+ }
+#endif
bool OperIsMemoryLoad() const; // Returns true for the HW Intrinsic instructions that have MemoryLoad semantics,
// false otherwise
@@ -5328,17 +5522,100 @@ struct GenTreeHWIntrinsic : public GenTreeJitIntrinsic
// false otherwise
bool OperIsMemoryLoadOrStore() const; // Returns true for the HW Intrinsic instructions that have MemoryLoad or
// MemoryStore semantics, false otherwise
-
bool IsSimdAsHWIntrinsic() const
{
return (gtFlags & GTF_SIMDASHW_OP) != 0;
}
+ unsigned GetResultOpNumForFMA(GenTree* use, GenTree* op1, GenTree* op2, GenTree* op3);
-#if DEBUGGABLE_GENTREE
- GenTreeHWIntrinsic() : GenTreeJitIntrinsic()
+ NamedIntrinsic GetHWIntrinsicId() const;
+
+ //---------------------------------------------------------------------------------------
+ // ChangeHWIntrinsicId: Change the intrinsic id for this node.
+ //
+ // This method just sets the intrinsic id, asserting that the new intrinsic
+ // has the same number of operands as the old one, optionally setting some of
+ // the new operands. Intrinsics with an unknown number of operands are exempt
+ // from the "do I have the same number of operands" check however, so this method must
+ // be used with care. Use "ResetHWIntrinsicId" if you need to fully reconfigure
+ // the node for a different intrinsic, with a possibly different number of operands.
+ //
+ // Arguments:
+ // intrinsicId - the new intrinsic id for the node
+ // operands - optional operands to set while changing the id
+ //
+ // Notes:
+ // It is the caller's responsibility to update side effect flags.
+ //
+ template
+ void ChangeHWIntrinsicId(NamedIntrinsic intrinsicId, Operands... operands)
{
+ const size_t OperandCount = sizeof...(Operands);
+ assert(OperandCount <= GetOperandCount());
+
+ SetHWIntrinsicId(intrinsicId);
+
+ GenTree* operandsArray[OperandCount + 1]{operands...};
+ GenTree** operandsStore = GetOperandArray();
+
+ for (size_t i = 0; i < OperandCount; i++)
+ {
+ operandsStore[i] = operandsArray[i];
+ }
}
-#endif
+
+ //---------------------------------------------------------------------------------------
+ // ResetHWIntrinsicId: Reset the intrinsic id for this node.
+ //
+ // This method resets the intrinsic id, fully reconfiguring the node. It must
+ // be supplied with all the operands the new node needs, and can allocate a
+ // new dynamic array if the operands do not fit into in an inline one, in which
+ // case a compiler argument is used to get the memory allocator.
+ //
+ // This method is similar to "ChangeHWIntrinsicId" but is more versatile and
+ // thus more expensive. Use it when you need to bash to an intrinsic id with
+ // a different number of operands than what the original node had, or, which
+ // is equivalent, when you do not know the original number of operands.
+ //
+ // Arguments:
+ // intrinsicId - the new intrinsic id for the node
+ // compiler - compiler to allocate memory with, can be "nullptr" if the
+ // number of new operands does not exceed the length of the
+ // inline array (so, there are 2 or fewer of them)
+ // operands - *all* operands for the new node
+ //
+ // Notes:
+ // It is the caller's responsibility to update side effect flags.
+ //
+ template
+ void ResetHWIntrinsicId(NamedIntrinsic intrinsicId, Compiler* compiler, Operands... operands)
+ {
+ const size_t NewOperandCount = sizeof...(Operands);
+ assert((compiler != nullptr) || (NewOperandCount <= ArrLen(gtInlineOperands)));
+
+ ResetOperandArray(NewOperandCount, compiler, gtInlineOperands, ArrLen(gtInlineOperands));
+ ChangeHWIntrinsicId(intrinsicId, operands...);
+ }
+
+ void ResetHWIntrinsicId(NamedIntrinsic intrinsicId, GenTree* op1, GenTree* op2)
+ {
+ ResetHWIntrinsicId(intrinsicId, static_cast(nullptr), op1, op2);
+ }
+
+ void ResetHWIntrinsicId(NamedIntrinsic intrinsicId, GenTree* op1)
+ {
+ ResetHWIntrinsicId(intrinsicId, static_cast(nullptr), op1);
+ }
+
+ void ResetHWIntrinsicId(NamedIntrinsic intrinsicId)
+ {
+ ResetHWIntrinsicId(intrinsicId, static_cast(nullptr));
+ }
+
+ static bool Equals(GenTreeHWIntrinsic* op1, GenTreeHWIntrinsic* op2);
+
+private:
+ void SetHWIntrinsicId(NamedIntrinsic intrinsicId);
};
#endif // FEATURE_HW_INTRINSICS
@@ -6132,18 +6409,17 @@ struct GenTreeRetExpr : public GenTree
#endif
};
-class InlineContext;
-
+// In LIR there are no longer statements so debug information is inserted linearly using these nodes.
struct GenTreeILOffset : public GenTree
{
- IL_OFFSETX gtStmtILoffsx; // instr offset (if available)
+ DebugInfo gtStmtDI; // debug info
#ifdef DEBUG
IL_OFFSET gtStmtLastILoffs; // instr offset at end of stmt
#endif
- GenTreeILOffset(IL_OFFSETX offset DEBUGARG(IL_OFFSET lastOffset = BAD_IL_OFFSET))
+ GenTreeILOffset(const DebugInfo& di DEBUGARG(IL_OFFSET lastOffset = BAD_IL_OFFSET))
: GenTree(GT_IL_OFFSET, TYP_VOID)
- , gtStmtILoffsx(offset)
+ , gtStmtDI(di)
#ifdef DEBUG
, gtStmtLastILoffs(lastOffset)
#endif
@@ -6216,13 +6492,11 @@ class GenTreeList
struct Statement
{
public:
- Statement(GenTree* expr, IL_OFFSETX offset DEBUGARG(unsigned stmtID))
+ Statement(GenTree* expr DEBUGARG(unsigned stmtID))
: m_rootNode(expr)
, m_treeList(nullptr)
, m_next(nullptr)
, m_prev(nullptr)
- , m_inlineContext(nullptr)
- , m_ILOffsetX(offset)
#ifdef DEBUG
, m_lastILOffset(BAD_IL_OFFSET)
, m_stmtID(stmtID)
@@ -6265,24 +6539,15 @@ struct Statement
return GenTreeList(GetTreeList());
}
- InlineContext* GetInlineContext() const
- {
- return m_inlineContext;
- }
-
- void SetInlineContext(InlineContext* inlineContext)
+ const DebugInfo& GetDebugInfo() const
{
- m_inlineContext = inlineContext;
+ return m_debugInfo;
}
- IL_OFFSETX GetILOffsetX() const
+ void SetDebugInfo(const DebugInfo& di)
{
- return m_ILOffsetX;
- }
-
- void SetILOffsetX(IL_OFFSETX offsetX)
- {
- m_ILOffsetX = offsetX;
+ m_debugInfo = di;
+ di.Validate();
}
#ifdef DEBUG
@@ -6363,9 +6628,7 @@ struct Statement
Statement* m_next;
Statement* m_prev;
- InlineContext* m_inlineContext; // The inline context for this statement.
-
- IL_OFFSETX m_ILOffsetX; // The instr offset (if available).
+ DebugInfo m_debugInfo;
#ifdef DEBUG
IL_OFFSET m_lastILOffset; // The instr offset at the end of this statement.
@@ -7164,7 +7427,7 @@ struct GenCondition
};
// clang-format on
- assert(m_code < _countof(names));
+ assert(m_code < ArrLen(names));
return names[m_code];
}
@@ -7255,7 +7518,7 @@ struct GenCondition
};
// clang-format on
- assert(condition.m_code < _countof(reverse));
+ assert(condition.m_code < ArrLen(reverse));
return GenCondition(reverse[condition.m_code]);
}
@@ -7272,7 +7535,7 @@ struct GenCondition
};
// clang-format on
- assert(condition.m_code < _countof(swap));
+ assert(condition.m_code < ArrLen(swap));
return GenCondition(swap[condition.m_code]);
}
};
@@ -7402,11 +7665,11 @@ inline bool GenTree::IsIntegralConstVector(ssize_t constVal) const
#ifdef FEATURE_SIMD
// SIMDIntrinsicInit intrinsic with a const value as initializer
// represents a const vector.
- if ((gtOper == GT_SIMD) && (AsSIMD()->gtSIMDIntrinsicID == SIMDIntrinsicInit) &&
- gtGetOp1()->IsIntegralConst(constVal))
+ if ((gtOper == GT_SIMD) && (AsSIMD()->GetSIMDIntrinsicId() == SIMDIntrinsicInit) &&
+ AsSIMD()->Op(1)->IsIntegralConst(constVal))
{
assert(varTypeIsIntegral(AsSIMD()->GetSimdBaseType()));
- assert(gtGetOp2IfPresent() == nullptr);
+ assert(AsSIMD()->GetOperandCount() == 1);
return true;
}
#endif // FEATURE_SIMD
@@ -7422,34 +7685,23 @@ inline bool GenTree::IsIntegralConstVector(ssize_t constVal) const
return false;
}
- GenTree* op1 = gtGetOp1();
- GenTree* op2 = gtGetOp2();
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
-
- if (op1 == nullptr)
+ if ((node->GetOperandCount() == 0) && (constVal == 0))
{
- assert(op2 == nullptr);
-
- if (constVal == 0)
- {
#if defined(TARGET_XARCH)
- return (intrinsicId == NI_Vector128_get_Zero) || (intrinsicId == NI_Vector256_get_Zero);
+ return (intrinsicId == NI_Vector128_get_Zero) || (intrinsicId == NI_Vector256_get_Zero);
#elif defined(TARGET_ARM64)
- return (intrinsicId == NI_Vector64_get_Zero) || (intrinsicId == NI_Vector128_get_Zero);
+ return (intrinsicId == NI_Vector64_get_Zero) || (intrinsicId == NI_Vector128_get_Zero);
#endif // !TARGET_XARCH && !TARGET_ARM64
- }
}
- else if ((op2 == nullptr) && !op1->OperIsList())
+ else if ((node->GetOperandCount() == 1) && node->Op(1)->IsIntegralConst(constVal))
{
- if (op1->IsIntegralConst(constVal))
- {
#if defined(TARGET_XARCH)
- return (intrinsicId == NI_Vector128_Create) || (intrinsicId == NI_Vector256_Create);
+ return (intrinsicId == NI_Vector128_Create) || (intrinsicId == NI_Vector256_Create);
#elif defined(TARGET_ARM64)
- return (intrinsicId == NI_Vector64_Create) || (intrinsicId == NI_Vector128_Create);
+ return (intrinsicId == NI_Vector64_Create) || (intrinsicId == NI_Vector128_Create);
#endif // !TARGET_XARCH && !TARGET_ARM64
- }
}
}
#endif // FEATURE_HW_INTRINSICS
@@ -7467,9 +7719,9 @@ inline bool GenTree::IsIntegralConstVector(ssize_t constVal) const
inline bool GenTree::IsSIMDZero() const
{
#ifdef FEATURE_SIMD
- if ((gtOper == GT_SIMD) && (AsSIMD()->gtSIMDIntrinsicID == SIMDIntrinsicInit))
+ if ((gtOper == GT_SIMD) && (AsSIMD()->GetSIMDIntrinsicId() == SIMDIntrinsicInit))
{
- return (gtGetOp1()->IsIntegralConst(0) || gtGetOp1()->IsFPZero());
+ return (AsSIMD()->Op(1)->IsIntegralConst(0) || AsSIMD()->Op(1)->IsFPZero());
}
#endif
@@ -7508,10 +7760,6 @@ inline bool GenTree::IsBoxedValue()
inline bool GenTree::IsValidCallArgument()
{
- if (OperIsList())
- {
- return false;
- }
if (OperIs(GT_FIELD_LIST))
{
#if !FEATURE_MULTIREG_ARGS && !FEATURE_PUT_STRUCT_ARG_STK
@@ -7581,7 +7829,7 @@ inline GenTree* GenTree::gtGetOp2() const
GenTree* op2 = AsOp()->gtOp2;
- // Only allow null op2 if the node type allows it, e.g. GT_LIST.
+ // Only allow null op2 if the node type allows it, e.g. GT_LEA.
assert((op2 != nullptr) || !RequiresNonNullOp2(gtOper));
return op2;
diff --git a/src/coreclr/jit/gschecks.cpp b/src/coreclr/jit/gschecks.cpp
index a8f03890899168..7765231a87551d 100644
--- a/src/coreclr/jit/gschecks.cpp
+++ b/src/coreclr/jit/gschecks.cpp
@@ -289,7 +289,7 @@ bool Compiler::gsFindVulnerableParams()
for (UINT lclNum = 0; lclNum < lvaCount; lclNum++)
{
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ LclVarDsc* varDsc = lvaGetDesc(lclNum);
ShadowParamVarInfo* shadowInfo = &gsShadowVarInfo[lclNum];
// If there was an indirection or if unsafe buffer, then we'd call it vulnerable.
@@ -367,7 +367,7 @@ void Compiler::gsParamsToShadows()
// Create shadow copy for each param candidate
for (UINT lclNum = 0; lclNum < lvaOldCount; lclNum++)
{
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ LclVarDsc* varDsc = lvaGetDesc(lclNum);
gsShadowVarInfo[lclNum].shadowCopy = NO_SHADOW_COPY;
// Only care about params whose values are on the stack
@@ -383,8 +383,8 @@ void Compiler::gsParamsToShadows()
int shadowVarNum = lvaGrabTemp(false DEBUGARG("shadowVar"));
// reload varDsc as lvaGrabTemp may realloc the lvaTable[]
- varDsc = &lvaTable[lclNum];
- LclVarDsc* shadowVarDsc = &lvaTable[shadowVarNum];
+ varDsc = lvaGetDesc(lclNum);
+ LclVarDsc* shadowVarDsc = lvaGetDesc(shadowVarNum);
// Copy some info
@@ -489,7 +489,7 @@ void Compiler::gsParamsToShadows()
// Now insert code to copy the params to their shadow copy.
for (UINT lclNum = 0; lclNum < lvaOldCount; lclNum++)
{
- const LclVarDsc* varDsc = &lvaTable[lclNum];
+ const LclVarDsc* varDsc = lvaGetDesc(lclNum);
const unsigned shadowVarNum = gsShadowVarInfo[lclNum].shadowCopy;
if (shadowVarNum == NO_SHADOW_COPY)
@@ -497,7 +497,7 @@ void Compiler::gsParamsToShadows()
continue;
}
- const LclVarDsc* shadowVarDsc = &lvaTable[shadowVarNum];
+ const LclVarDsc* shadowVarDsc = lvaGetDesc(shadowVarNum);
var_types type = shadowVarDsc->TypeGet();
GenTree* src = gtNewLclvNode(lclNum, varDsc->TypeGet());
@@ -541,7 +541,7 @@ void Compiler::gsParamsToShadows()
for (UINT lclNum = 0; lclNum < info.compArgsCount; lclNum++)
{
- const LclVarDsc* varDsc = &lvaTable[lclNum];
+ const LclVarDsc* varDsc = lvaGetDesc(lclNum);
const unsigned shadowVarNum = gsShadowVarInfo[lclNum].shadowCopy;
if (shadowVarNum == NO_SHADOW_COPY)
@@ -549,7 +549,7 @@ void Compiler::gsParamsToShadows()
continue;
}
- const LclVarDsc* shadowVarDsc = &lvaTable[shadowVarNum];
+ const LclVarDsc* shadowVarDsc = lvaGetDesc(shadowVarNum);
GenTree* src = gtNewLclvNode(shadowVarNum, shadowVarDsc->TypeGet());
GenTree* dst = gtNewLclvNode(lclNum, varDsc->TypeGet());
diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h
index aa6f990e7e7212..f756a3b7bd4708 100644
--- a/src/coreclr/jit/gtlist.h
+++ b/src/coreclr/jit/gtlist.h
@@ -126,32 +126,12 @@ GTNODE(UMOD , GenTreeOp ,0,GTK_BINOP)
GTNODE(OR , GenTreeOp ,1,(GTK_BINOP|GTK_LOGOP))
GTNODE(XOR , GenTreeOp ,1,(GTK_BINOP|GTK_LOGOP))
GTNODE(AND , GenTreeOp ,1,(GTK_BINOP|GTK_LOGOP))
-GTNODE(AND_NOT , GenTreeOp ,0,GTK_BINOP)
GTNODE(LSH , GenTreeOp ,0,GTK_BINOP)
GTNODE(RSH , GenTreeOp ,0,GTK_BINOP)
GTNODE(RSZ , GenTreeOp ,0,GTK_BINOP)
GTNODE(ROL , GenTreeOp ,0,GTK_BINOP)
GTNODE(ROR , GenTreeOp ,0,GTK_BINOP)
-GTNODE(INC_SATURATE , GenTreeOp ,0,GTK_UNOP) // saturating increment, used in division by a constant (LowerUnsignedDivOrMod)
-
-// Returns high bits (top N bits of the 2N bit result of an NxN multiply)
-// GT_MULHI is used in division by a constant (LowerUnsignedDivOrMod). We turn
-// the div into a MULHI + some adjustments. In codegen, we only use the
-// results of the high register, and we drop the low results.
-GTNODE(MULHI , GenTreeOp ,1,GTK_BINOP)
-
-// A mul that returns the 2N bit result of an NxN multiply. This op is used for
-// multiplies that take two ints and return a long result. For 32 bit targets,
-// all other multiplies with long results are morphed into helper calls.
-// It is similar to GT_MULHI, the difference being that GT_MULHI drops the lo
-// part of the result, whereas GT_MUL_LONG keeps both parts of the result.
-// MUL_LONG is also used on ARM64, where 64 bit multiplication is more expensive.
-#if !defined(TARGET_64BIT)
-GTNODE(MUL_LONG , GenTreeMultiRegOp ,1,GTK_BINOP)
-#elif defined(TARGET_ARM64)
-GTNODE(MUL_LONG , GenTreeOp ,1,GTK_BINOP)
-#endif
GTNODE(ASG , GenTreeOp ,0,(GTK_BINOP|GTK_NOTLIR))
GTNODE(EQ , GenTreeOp ,0,(GTK_BINOP|GTK_RELOP))
@@ -213,13 +193,38 @@ GTNODE(RSH_LO , GenTreeOp ,0,GTK_BINOP)
#endif // !defined(TARGET_64BIT)
#ifdef FEATURE_SIMD
-GTNODE(SIMD , GenTreeSIMD ,0,(GTK_BINOP|GTK_EXOP)) // SIMD functions/operators/intrinsics
+GTNODE(SIMD , GenTreeSIMD ,0,GTK_SPECIAL) // SIMD functions/operators/intrinsics
#endif // FEATURE_SIMD
#ifdef FEATURE_HW_INTRINSICS
-GTNODE(HWINTRINSIC , GenTreeHWIntrinsic ,0,(GTK_BINOP|GTK_EXOP)) // hardware intrinsics
+GTNODE(HWINTRINSIC , GenTreeHWIntrinsic ,0,GTK_SPECIAL) // hardware intrinsics
#endif // FEATURE_HW_INTRINSICS
+//-----------------------------------------------------------------------------
+// Backend-specific arithmetic nodes:
+//-----------------------------------------------------------------------------
+
+GTNODE(INC_SATURATE , GenTreeOp ,0,GTK_UNOP) // saturating increment, used in division by a constant (LowerUnsignedDivOrMod)
+
+// Returns high bits (top N bits of the 2N bit result of an NxN multiply)
+// GT_MULHI is used in division by a constant (LowerUnsignedDivOrMod). We turn
+// the div into a MULHI + some adjustments. In codegen, we only use the
+// results of the high register, and we drop the low results.
+GTNODE(MULHI , GenTreeOp ,1,GTK_BINOP)
+
+// A mul that returns the 2N bit result of an NxN multiply. This op is used for
+// multiplies that take two ints and return a long result. For 32 bit targets,
+// all other multiplies with long results are morphed into helper calls.
+// It is similar to GT_MULHI, the difference being that GT_MULHI drops the lo
+// part of the result, whereas GT_MUL_LONG keeps both parts of the result.
+// MUL_LONG is also used on ARM64, where 64 bit multiplication is more expensive.
+#if !defined(TARGET_64BIT)
+GTNODE(MUL_LONG , GenTreeMultiRegOp ,1,GTK_BINOP)
+#elif defined(TARGET_ARM64)
+GTNODE(MUL_LONG , GenTreeOp ,1,GTK_BINOP)
+#endif
+// AndNot - emitted on ARM/ARM64 as the BIC instruction. Also used for creating AndNot HWINTRINSIC vector nodes in a cross-ISA manner.
+GTNODE(AND_NOT , GenTreeOp ,0,GTK_BINOP)
//-----------------------------------------------------------------------------
// LIR specific compare and conditional branch/set nodes:
//-----------------------------------------------------------------------------
@@ -241,8 +246,6 @@ GTNODE(BT , GenTreeOp ,0,(GTK_BINOP|GTK_NOVALUE)) // The
GTNODE(JTRUE , GenTreeOp ,0,(GTK_UNOP|GTK_NOVALUE))
-GTNODE(LIST , GenTreeArgList ,0,(GTK_BINOP|GTK_NOVALUE))
-
//-----------------------------------------------------------------------------
// Other nodes that have special structure:
//-----------------------------------------------------------------------------
@@ -280,8 +283,16 @@ GTNODE(PHI_ARG , GenTreePhiArg ,0,(GTK_LEAF|GTK_LOCAL)) // phi
// Nodes used by Lower to generate a closer CPU representation of other nodes
//-----------------------------------------------------------------------------
+#ifdef TARGET_ARM64
+GTNODE(MADD , GenTreeOp ,0, GTK_BINOP) // Generates the Multiply-Add instruction (madd/msub)
+ // In future, we might consider enabling it for both armarch and xarch
+ // for floating-point MADD "unsafe" math
+#endif
GTNODE(JMPTABLE , GenTree ,0, (GTK_LEAF|GTK_NOCONTAIN)) // Generates the jump table for switches
GTNODE(SWITCH_TABLE , GenTreeOp ,0, (GTK_BINOP|GTK_NOVALUE)) // Jump Table based switch construct
+#ifdef TARGET_ARM64
+GTNODE(BFIZ , GenTreeOp ,0, GTK_BINOP) // Bitfield Insert in Zero
+#endif
//-----------------------------------------------------------------------------
// Nodes used only within the code generator:
diff --git a/src/coreclr/jit/gtstructs.h b/src/coreclr/jit/gtstructs.h
index b4bad947fd90f7..70a7901b7aca2a 100644
--- a/src/coreclr/jit/gtstructs.h
+++ b/src/coreclr/jit/gtstructs.h
@@ -67,7 +67,6 @@ GTSTRUCT_1(Cast , GT_CAST)
GTSTRUCT_1(Box , GT_BOX)
GTSTRUCT_1(Field , GT_FIELD)
GTSTRUCT_1(Call , GT_CALL)
-GTSTRUCT_1(ArgList , GT_LIST)
GTSTRUCT_1(FieldList , GT_FIELD_LIST)
GTSTRUCT_1(Colon , GT_COLON)
GTSTRUCT_1(FptrVal , GT_FTN_ADDR)
@@ -76,10 +75,13 @@ GTSTRUCT_1(Index , GT_INDEX)
GTSTRUCT_1(IndexAddr , GT_INDEX_ADDR)
#if defined(FEATURE_HW_INTRINSICS) && defined(FEATURE_SIMD)
GTSTRUCT_3(BoundsChk , GT_ARR_BOUNDS_CHECK, GT_SIMD_CHK, GT_HW_INTRINSIC_CHK)
+GTSTRUCT_N(MultiOp , GT_SIMD, GT_HWINTRINSIC)
#elif defined(FEATURE_SIMD)
GTSTRUCT_2(BoundsChk , GT_ARR_BOUNDS_CHECK, GT_SIMD_CHK)
+GTSTRUCT_N(MultiOp , GT_SIMD)
#elif defined(FEATURE_HW_INTRINSICS)
GTSTRUCT_2(BoundsChk , GT_ARR_BOUNDS_CHECK, GT_HW_INTRINSIC_CHK)
+GTSTRUCT_N(MultiOp , GT_HWINTRINSIC)
#else // !FEATURE_SIMD && !FEATURE_HW_INTRINSICS
GTSTRUCT_1(BoundsChk , GT_ARR_BOUNDS_CHECK)
#endif // !FEATURE_SIMD && !FEATURE_HW_INTRINSICS
diff --git a/src/coreclr/jit/hwintrinsic.cpp b/src/coreclr/jit/hwintrinsic.cpp
index 908dac27c60ff6..70ea91dc4032c4 100644
--- a/src/coreclr/jit/hwintrinsic.cpp
+++ b/src/coreclr/jit/hwintrinsic.cpp
@@ -366,92 +366,6 @@ unsigned HWIntrinsicInfo::lookupSimdSize(Compiler* comp, NamedIntrinsic id, CORI
return simdSize;
}
-//------------------------------------------------------------------------
-// lookupNumArgs: Gets the number of args for a given HWIntrinsic node
-//
-// Arguments:
-// node -- The HWIntrinsic node to get the number of args for
-//
-// Return Value:
-// The number of args for the HWIntrinsic associated with node
-int HWIntrinsicInfo::lookupNumArgs(const GenTreeHWIntrinsic* node)
-{
- assert(node != nullptr);
-
- NamedIntrinsic id = node->gtHWIntrinsicId;
- int numArgs = lookupNumArgs(id);
-
- if (numArgs >= 0)
- {
- return numArgs;
- }
-
- assert(numArgs == -1);
-
- GenTree* op1 = node->gtGetOp1();
-
- if (op1 == nullptr)
- {
- return 0;
- }
-
- if (op1->OperIsList())
- {
- GenTreeArgList* list = op1->AsArgList();
- numArgs = 0;
-
- do
- {
- numArgs++;
- list = list->Rest();
- } while (list != nullptr);
-
- return numArgs;
- }
-
- GenTree* op2 = node->gtGetOp2();
-
- return (op2 == nullptr) ? 1 : 2;
-}
-
-//------------------------------------------------------------------------
-// lookupLastOp: Gets the last operand for a given HWIntrinsic node
-//
-// Arguments:
-// node -- The HWIntrinsic node to get the last operand for
-//
-// Return Value:
-// The last operand for node
-GenTree* HWIntrinsicInfo::lookupLastOp(const GenTreeHWIntrinsic* node)
-{
- assert(node != nullptr);
-
- GenTree* op1 = node->gtGetOp1();
-
- if (op1 == nullptr)
- {
- return nullptr;
- }
-
- if (op1->OperIsList())
- {
- GenTreeArgList* list = op1->AsArgList();
- GenTree* last;
-
- do
- {
- last = list->Current();
- list = list->Rest();
- } while (list != nullptr);
-
- return last;
- }
-
- GenTree* op2 = node->gtGetOp2();
-
- return (op2 == nullptr) ? op1 : op2;
-}
-
//------------------------------------------------------------------------
// isImmOp: Checks whether the HWIntrinsic node has an imm operand
//
diff --git a/src/coreclr/jit/hwintrinsic.h b/src/coreclr/jit/hwintrinsic.h
index 0b35ca719b6e2f..43bd543c599f1d 100644
--- a/src/coreclr/jit/hwintrinsic.h
+++ b/src/coreclr/jit/hwintrinsic.h
@@ -309,8 +309,6 @@ struct HWIntrinsicInfo
static CORINFO_InstructionSet lookupIsa(const char* className, const char* enclosingClassName);
static unsigned lookupSimdSize(Compiler* comp, NamedIntrinsic id, CORINFO_SIG_INFO* sig);
- static int lookupNumArgs(const GenTreeHWIntrinsic* node);
- static GenTree* lookupLastOp(const GenTreeHWIntrinsic* node);
#if defined(TARGET_XARCH)
static int lookupImmUpperBound(NamedIntrinsic intrinsic);
@@ -725,7 +723,7 @@ struct HWIntrinsic final
{
assert(node != nullptr);
- id = node->gtHWIntrinsicId;
+ id = node->GetHWIntrinsicId();
category = HWIntrinsicInfo::lookupCategory(id);
assert(HWIntrinsicInfo::RequiresCodegen(id));
@@ -749,53 +747,34 @@ struct HWIntrinsic final
GenTree* op2;
GenTree* op3;
GenTree* op4;
- int numOperands;
+ size_t numOperands;
var_types baseType;
private:
void InitializeOperands(const GenTreeHWIntrinsic* node)
{
- op1 = node->gtGetOp1();
- op2 = node->gtGetOp2();
+ numOperands = node->GetOperandCount();
- if (op1 == nullptr)
+ switch (numOperands)
{
- numOperands = 0;
- }
- else if (op1->OperIsList())
- {
- assert(op2 == nullptr);
-
- GenTreeArgList* list = op1->AsArgList();
- op1 = list->Current();
- list = list->Rest();
- op2 = list->Current();
- list = list->Rest();
- op3 = list->Current();
- list = list->Rest();
-
- if (list != nullptr)
- {
- op4 = list->Current();
- assert(list->Rest() == nullptr);
+ case 4:
+ op4 = node->Op(4);
+ FALLTHROUGH;
+ case 3:
+ op3 = node->Op(3);
+ FALLTHROUGH;
+ case 2:
+ op2 = node->Op(2);
+ FALLTHROUGH;
+ case 1:
+ op1 = node->Op(1);
+ FALLTHROUGH;
+ case 0:
+ break;
- numOperands = 4;
- }
- else
- {
- numOperands = 3;
- }
- }
- else if (op2 != nullptr)
- {
- numOperands = 2;
- }
- else
- {
- numOperands = 1;
+ default:
+ unreached();
}
-
- assert(HWIntrinsicInfo::lookupNumArgs(id) == numOperands);
}
void InitializeBaseType(const GenTreeHWIntrinsic* node)
@@ -804,7 +783,7 @@ struct HWIntrinsic final
if (baseType == TYP_UNKNOWN)
{
- assert(category == HW_Category_Scalar);
+ assert((category == HW_Category_Scalar) || (category == HW_Category_Special));
if (HWIntrinsicInfo::BaseTypeFromFirstArg(id))
{
diff --git a/src/coreclr/jit/hwintrinsicarm64.cpp b/src/coreclr/jit/hwintrinsicarm64.cpp
index 98795c7941f896..7fea81a34e2c12 100644
--- a/src/coreclr/jit/hwintrinsicarm64.cpp
+++ b/src/coreclr/jit/hwintrinsicarm64.cpp
@@ -308,9 +308,8 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic,
var_types retType,
unsigned simdSize)
{
- HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(intrinsic);
- int numArgs = sig->numArgs;
- var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType);
+ HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(intrinsic);
+ int numArgs = sig->numArgs;
if (!featureSIMD || !IsBaselineSimdIsaSupported())
{
@@ -318,7 +317,14 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic,
}
assert(numArgs >= 0);
- assert(varTypeIsArithmetic(simdBaseType));
+
+ var_types simdBaseType = TYP_UNKNOWN;
+
+ if (intrinsic != NI_ArmBase_Yield)
+ {
+ simdBaseType = JitType2PreciseVarType(simdBaseJitType);
+ assert(varTypeIsArithmetic(simdBaseType));
+ }
GenTree* retNode = nullptr;
GenTree* op1 = nullptr;
@@ -327,6 +333,16 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic,
switch (intrinsic)
{
+ case NI_ArmBase_Yield:
+ {
+ assert(sig->numArgs == 0);
+ assert(JITtype2varType(sig->retType) == TYP_VOID);
+ assert(simdSize == 0);
+
+ retNode = gtNewScalarHWIntrinsicNode(TYP_VOID, intrinsic);
+ break;
+ }
+
case NI_Vector64_Abs:
case NI_Vector128_Abs:
{
@@ -482,31 +498,14 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic,
// We shouldn't handle this as an intrinsic if the
// respective ISAs have been disabled by the user.
- if (sig->numArgs == 1)
- {
- op1 = impPopStack().val;
- retNode = gtNewSimdHWIntrinsicNode(retType, op1, intrinsic, simdBaseJitType, simdSize);
- }
- else if (sig->numArgs == 2)
+ IntrinsicNodeBuilder nodeBuilder(getAllocator(CMK_ASTNode), sig->numArgs);
+
+ for (int i = sig->numArgs - 1; i >= 0; i--)
{
- op2 = impPopStack().val;
- op1 = impPopStack().val;
- retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, simdBaseJitType, simdSize);
+ nodeBuilder.AddOperand(i, impPopStack().val);
}
- else
- {
- assert(sig->numArgs >= 3);
-
- GenTreeArgList* tmp = nullptr;
- for (unsigned i = 0; i < sig->numArgs; i++)
- {
- tmp = gtNewListNode(impPopStack().val, tmp);
- }
-
- op1 = tmp;
- retNode = gtNewSimdHWIntrinsicNode(retType, op1, intrinsic, simdBaseJitType, simdSize);
- }
+ retNode = gtNewSimdHWIntrinsicNode(retType, std::move(nodeBuilder), intrinsic, simdBaseJitType, simdSize);
break;
}
@@ -821,7 +820,12 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic,
case NI_Vector128_Narrow:
{
assert(sig->numArgs == 2);
- // TODO-ARM64-CQ: These intrinsics should be accelerated.
+
+ op2 = impSIMDPopStack(retType);
+ op1 = impSIMDPopStack(retType);
+
+ retNode =
+ gtNewSimdNarrowNode(retType, op1, op2, simdBaseJitType, simdSize, /* isSimdAsHWIntrinsic */ false);
break;
}
@@ -899,6 +903,28 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic,
break;
}
+ case NI_Vector64_WidenLower:
+ case NI_Vector128_WidenLower:
+ {
+ assert(sig->numArgs == 1);
+
+ op1 = impSIMDPopStack(retType);
+
+ retNode = gtNewSimdWidenLowerNode(retType, op1, simdBaseJitType, simdSize, /* isSimdAsHWIntrinsic */ false);
+ break;
+ }
+
+ case NI_Vector64_WidenUpper:
+ case NI_Vector128_WidenUpper:
+ {
+ assert(sig->numArgs == 1);
+
+ op1 = impSIMDPopStack(retType);
+
+ retNode = gtNewSimdWidenUpperNode(retType, op1, simdBaseJitType, simdSize, /* isSimdAsHWIntrinsic */ false);
+ break;
+ }
+
case NI_Vector64_WithElement:
case NI_Vector128_WithElement:
{
diff --git a/src/coreclr/jit/hwintrinsiccodegenarm64.cpp b/src/coreclr/jit/hwintrinsiccodegenarm64.cpp
index 3352c9ba595710..706b988f049e51 100644
--- a/src/coreclr/jit/hwintrinsiccodegenarm64.cpp
+++ b/src/coreclr/jit/hwintrinsiccodegenarm64.cpp
@@ -51,7 +51,7 @@ CodeGen::HWIntrinsicImmOpHelper::HWIntrinsicImmOpHelper(CodeGen* codeGen, GenTre
}
else
{
- const HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(intrin->gtHWIntrinsicId);
+ const HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(intrin->GetHWIntrinsicId());
if (category == HW_Category_SIMDByIndexedElement)
{
@@ -71,13 +71,13 @@ CodeGen::HWIntrinsicImmOpHelper::HWIntrinsicImmOpHelper(CodeGen* codeGen, GenTre
assert(varTypeIsSIMD(indexedElementOpType));
const unsigned int indexedElementSimdSize = genTypeSize(indexedElementOpType);
- HWIntrinsicInfo::lookupImmBounds(intrin->gtHWIntrinsicId, indexedElementSimdSize, intrin->GetSimdBaseType(),
- &immLowerBound, &immUpperBound);
+ HWIntrinsicInfo::lookupImmBounds(intrin->GetHWIntrinsicId(), indexedElementSimdSize,
+ intrin->GetSimdBaseType(), &immLowerBound, &immUpperBound);
}
else
{
- HWIntrinsicInfo::lookupImmBounds(intrin->gtHWIntrinsicId, intrin->GetSimdSize(), intrin->GetSimdBaseType(),
- &immLowerBound, &immUpperBound);
+ HWIntrinsicInfo::lookupImmBounds(intrin->GetHWIntrinsicId(), intrin->GetSimdSize(),
+ intrin->GetSimdBaseType(), &immLowerBound, &immUpperBound);
}
nonConstImmReg = immOp->GetRegNum();
@@ -95,7 +95,7 @@ CodeGen::HWIntrinsicImmOpHelper::HWIntrinsicImmOpHelper(CodeGen* codeGen, GenTre
// these by
// using the same approach as in hwintrinsicxarch.cpp - adding an additional indirection level in form of a
// branch table.
- assert(!HWIntrinsicInfo::GeneratesMultipleIns(intrin->gtHWIntrinsicId));
+ assert(!HWIntrinsicInfo::GeneratesMultipleIns(intrin->GetHWIntrinsicId()));
branchTargetReg = intrin->GetSingleTempReg();
}
@@ -255,6 +255,13 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node)
emitSize = emitActualTypeSize(intrin.baseType);
opt = INS_OPTS_NONE;
}
+ else if (intrin.category == HW_Category_Special)
+ {
+ assert(intrin.id == NI_ArmBase_Yield);
+
+ emitSize = EA_UNKNOWN;
+ opt = INS_OPTS_NONE;
+ }
else
{
emitSize = emitActualTypeSize(Compiler::getSIMDTypeForSize(node->GetSimdSize()));
@@ -264,7 +271,7 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node)
const bool isRMW = node->isRMWHWIntrinsic(compiler);
const bool hasImmediateOperand = HWIntrinsicInfo::HasImmediateOperand(intrin.id);
- genConsumeHWIntrinsicOperands(node);
+ genConsumeMultiOpOperands(node);
if (intrin.IsTableDriven())
{
@@ -443,6 +450,12 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node)
}
break;
+ case NI_ArmBase_Yield:
+ {
+ ins = INS_yield;
+ break;
+ }
+
default:
ins = HWIntrinsicInfo::lookupIns(intrin.id, intrin.baseType);
break;
@@ -735,6 +748,12 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node)
}
break;
+ case NI_ArmBase_Yield:
+ {
+ GetEmitter()->emitIns(ins);
+ break;
+ }
+
// mvni doesn't support the range of element types, so hard code the 'opts' value.
case NI_Vector64_get_Zero:
case NI_Vector64_get_AllBitsSet:
diff --git a/src/coreclr/jit/hwintrinsiccodegenxarch.cpp b/src/coreclr/jit/hwintrinsiccodegenxarch.cpp
index 8523b529cbec42..bb6a6daa815d8b 100644
--- a/src/coreclr/jit/hwintrinsiccodegenxarch.cpp
+++ b/src/coreclr/jit/hwintrinsiccodegenxarch.cpp
@@ -78,10 +78,10 @@ static bool genIsTableDrivenHWIntrinsic(NamedIntrinsic intrinsicId, HWIntrinsicC
//
void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node)
{
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
CORINFO_InstructionSet isa = HWIntrinsicInfo::lookupIsa(intrinsicId);
HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(intrinsicId);
- int numArgs = HWIntrinsicInfo::lookupNumArgs(node);
+ size_t numArgs = node->GetOperandCount();
int ival = HWIntrinsicInfo::lookupIval(intrinsicId, compiler->compOpportunisticallyDependsOn(InstructionSet_AVX));
@@ -89,11 +89,13 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node)
if (genIsTableDrivenHWIntrinsic(intrinsicId, category))
{
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = node->gtGetOp2();
regNumber targetReg = node->GetRegNum();
var_types baseType = node->GetSimdBaseType();
+ GenTree* op1 = nullptr;
+ GenTree* op2 = nullptr;
+ GenTree* op3 = nullptr;
+
regNumber op1Reg = REG_NA;
regNumber op2Reg = REG_NA;
emitter* emit = GetEmitter();
@@ -109,6 +111,8 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node)
{
case 1:
{
+ op1 = node->Op(1);
+
if (node->OperIsMemoryLoad())
{
genConsumeAddress(op1);
@@ -150,6 +154,9 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node)
case 2:
{
+ op1 = node->Op(1);
+ op2 = node->Op(2);
+
if (category == HW_Category_MemoryStore)
{
genConsumeAddress(op1);
@@ -158,13 +165,13 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node)
{
GenTreeHWIntrinsic* extract = op2->AsHWIntrinsic();
- assert((extract->gtHWIntrinsicId == NI_AVX_ExtractVector128) ||
- (extract->gtHWIntrinsicId == NI_AVX2_ExtractVector128));
+ assert((extract->GetHWIntrinsicId() == NI_AVX_ExtractVector128) ||
+ (extract->GetHWIntrinsicId() == NI_AVX2_ExtractVector128));
- regNumber regData = genConsumeReg(extract->gtGetOp1());
+ regNumber regData = genConsumeReg(extract->Op(1));
- ins = HWIntrinsicInfo::lookupIns(extract->gtHWIntrinsicId, extract->GetSimdBaseType());
- ival = static_cast(extract->gtGetOp2()->AsIntCon()->IconValue());
+ ins = HWIntrinsicInfo::lookupIns(extract->GetHWIntrinsicId(), extract->GetSimdBaseType());
+ ival = static_cast(extract->Op(2)->AsIntCon()->IconValue());
GenTreeIndir indir = indirForm(TYP_SIMD16, op1);
emit->emitIns_A_R_I(ins, EA_32BYTE, &indir, regData, ival);
@@ -258,18 +265,16 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node)
case 3:
{
- GenTreeArgList* argList = op1->AsArgList();
- op1 = argList->Current();
+ op1 = node->Op(1);
+ op2 = node->Op(2);
+ op3 = node->Op(3);
+
genConsumeRegs(op1);
op1Reg = op1->GetRegNum();
- argList = argList->Rest();
- op2 = argList->Current();
genConsumeRegs(op2);
op2Reg = op2->GetRegNum();
- argList = argList->Rest();
- GenTree* op3 = argList->Current();
genConsumeRegs(op3);
regNumber op3Reg = op3->GetRegNum();
@@ -432,7 +437,7 @@ void CodeGen::genHWIntrinsic_R_RM(
if (rmOp->isContained() || rmOp->isUsedFromSpillTemp())
{
- assert(HWIntrinsicInfo::SupportsContainment(node->gtHWIntrinsicId));
+ assert(HWIntrinsicInfo::SupportsContainment(node->GetHWIntrinsicId()));
assertIsContainableHWIntrinsicOp(compiler->m_pLowering, node, rmOp);
TempDsc* tmpDsc = nullptr;
@@ -462,8 +467,8 @@ void CodeGen::genHWIntrinsic_R_RM(
else
{
assert(rmOp->AsHWIntrinsic()->OperIsMemoryLoad());
- assert(HWIntrinsicInfo::lookupNumArgs(rmOp->AsHWIntrinsic()) == 1);
- addr = rmOp->gtGetOp1();
+ assert(rmOp->AsHWIntrinsic()->GetOperandCount() == 1);
+ addr = rmOp->AsHWIntrinsic()->Op(1);
}
switch (addr->OperGet())
@@ -554,7 +559,7 @@ void CodeGen::genHWIntrinsic_R_RM(
void CodeGen::genHWIntrinsic_R_RM_I(GenTreeHWIntrinsic* node, instruction ins, emitAttr simdSize, int8_t ival)
{
regNumber targetReg = node->GetRegNum();
- GenTree* op1 = node->gtGetOp1();
+ GenTree* op1 = node->Op(1);
// TODO-XArch-CQ: Commutative operations can have op1 be contained
// TODO-XArch-CQ: Non-VEX encoded instructions can have both ops contained
@@ -564,7 +569,7 @@ void CodeGen::genHWIntrinsic_R_RM_I(GenTreeHWIntrinsic* node, instruction ins, e
if (op1->isContained() || op1->isUsedFromSpillTemp())
{
- assert(HWIntrinsicInfo::SupportsContainment(node->gtHWIntrinsicId));
+ assert(HWIntrinsicInfo::SupportsContainment(node->GetHWIntrinsicId()));
assertIsContainableHWIntrinsicOp(compiler->m_pLowering, node, op1);
}
inst_RV_TT_IV(ins, simdSize, targetReg, op1, ival);
@@ -582,8 +587,8 @@ void CodeGen::genHWIntrinsic_R_RM_I(GenTreeHWIntrinsic* node, instruction ins, e
void CodeGen::genHWIntrinsic_R_R_RM(GenTreeHWIntrinsic* node, instruction ins, emitAttr attr)
{
regNumber targetReg = node->GetRegNum();
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = node->gtGetOp2();
+ GenTree* op1 = node->Op(1);
+ GenTree* op2 = node->Op(2);
regNumber op1Reg = op1->GetRegNum();
assert(targetReg != REG_NA);
@@ -612,7 +617,7 @@ void CodeGen::genHWIntrinsic_R_R_RM(
if (op2->isContained() || op2->isUsedFromSpillTemp())
{
- assert(HWIntrinsicInfo::SupportsContainment(node->gtHWIntrinsicId));
+ assert(HWIntrinsicInfo::SupportsContainment(node->GetHWIntrinsicId()));
assertIsContainableHWIntrinsicOp(compiler->m_pLowering, node, op2);
}
@@ -632,29 +637,13 @@ void CodeGen::genHWIntrinsic_R_R_RM(
void CodeGen::genHWIntrinsic_R_R_RM_I(GenTreeHWIntrinsic* node, instruction ins, emitAttr simdSize, int8_t ival)
{
regNumber targetReg = node->GetRegNum();
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = node->gtGetOp2();
+ GenTree* op1 = node->Op(1);
+ GenTree* op2 = node->Op(2);
emitter* emit = GetEmitter();
// TODO-XArch-CQ: Commutative operations can have op1 be contained
// TODO-XArch-CQ: Non-VEX encoded instructions can have both ops contained
- if (op1->OperIsList())
- {
- assert(op2 == nullptr);
-
- GenTreeArgList* argList = op1->AsArgList();
-
- op1 = argList->Current();
- argList = argList->Rest();
-
- op2 = argList->Current();
- argList = argList->Rest();
-
- assert(argList->Current() != nullptr);
- assert(argList->Rest() == nullptr);
- }
-
regNumber op1Reg = op1->GetRegNum();
assert(targetReg != REG_NA);
@@ -662,7 +651,7 @@ void CodeGen::genHWIntrinsic_R_R_RM_I(GenTreeHWIntrinsic* node, instruction ins,
if (op2->isContained() || op2->isUsedFromSpillTemp())
{
- assert(HWIntrinsicInfo::SupportsContainment(node->gtHWIntrinsicId));
+ assert(HWIntrinsicInfo::SupportsContainment(node->GetHWIntrinsicId()));
assertIsContainableHWIntrinsicOp(compiler->m_pLowering, node, op2);
TempDsc* tmpDsc = nullptr;
@@ -692,8 +681,8 @@ void CodeGen::genHWIntrinsic_R_R_RM_I(GenTreeHWIntrinsic* node, instruction ins,
else
{
assert(op2->AsHWIntrinsic()->OperIsMemoryLoad());
- assert(HWIntrinsicInfo::lookupNumArgs(op2->AsHWIntrinsic()) == 1);
- addr = op2->gtGetOp1();
+ assert(op2->AsHWIntrinsic()->GetOperandCount() == 1);
+ addr = op2->AsHWIntrinsic()->Op(1);
}
switch (addr->OperGet())
@@ -795,25 +784,11 @@ void CodeGen::genHWIntrinsic_R_R_RM_I(GenTreeHWIntrinsic* node, instruction ins,
void CodeGen::genHWIntrinsic_R_R_RM_R(GenTreeHWIntrinsic* node, instruction ins, emitAttr simdSize)
{
regNumber targetReg = node->GetRegNum();
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = node->gtGetOp2();
- GenTree* op3 = nullptr;
+ GenTree* op1 = node->Op(1);
+ GenTree* op2 = node->Op(2);
+ GenTree* op3 = node->Op(3);
emitter* emit = GetEmitter();
- assert(op1->OperIsList());
- assert(op2 == nullptr);
-
- GenTreeArgList* argList = op1->AsArgList();
-
- op1 = argList->Current();
- argList = argList->Rest();
-
- op2 = argList->Current();
- argList = argList->Rest();
-
- op3 = argList->Current();
- assert(argList->Rest() == nullptr);
-
regNumber op1Reg = op1->GetRegNum();
regNumber op3Reg = op3->GetRegNum();
@@ -823,7 +798,7 @@ void CodeGen::genHWIntrinsic_R_R_RM_R(GenTreeHWIntrinsic* node, instruction ins,
if (op2->isContained() || op2->isUsedFromSpillTemp())
{
- assert(HWIntrinsicInfo::SupportsContainment(node->gtHWIntrinsicId));
+ assert(HWIntrinsicInfo::SupportsContainment(node->GetHWIntrinsicId()));
assertIsContainableHWIntrinsicOp(compiler->m_pLowering, node, op2);
TempDsc* tmpDsc = nullptr;
@@ -855,8 +830,8 @@ void CodeGen::genHWIntrinsic_R_R_RM_R(GenTreeHWIntrinsic* node, instruction ins,
else
{
assert(op2->AsHWIntrinsic()->OperIsMemoryLoad());
- assert(HWIntrinsicInfo::lookupNumArgs(op2->AsHWIntrinsic()) == 1);
- addr = op2->gtGetOp1();
+ assert(op2->AsHWIntrinsic()->GetOperandCount() == 1);
+ addr = op2->AsHWIntrinsic()->Op(1);
}
switch (addr->OperGet())
@@ -982,8 +957,8 @@ void CodeGen::genHWIntrinsic_R_R_R_RM(
else
{
assert(op3->AsHWIntrinsic()->OperIsMemoryLoad());
- assert(HWIntrinsicInfo::lookupNumArgs(op3->AsHWIntrinsic()) == 1);
- addr = op3->gtGetOp1();
+ assert(op3->AsHWIntrinsic()->GetOperandCount() == 1);
+ addr = op3->AsHWIntrinsic()->Op(1);
}
switch (addr->OperGet())
@@ -1138,17 +1113,17 @@ void CodeGen::genHWIntrinsicJumpTableFallback(NamedIntrinsic intrinsi
//
void CodeGen::genBaseIntrinsic(GenTreeHWIntrinsic* node)
{
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
regNumber targetReg = node->GetRegNum();
var_types baseType = node->GetSimdBaseType();
assert(compiler->compIsaSupportedDebugOnly(InstructionSet_SSE));
assert((baseType >= TYP_BYTE) && (baseType <= TYP_DOUBLE));
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = node->gtGetOp2();
+ GenTree* op1 = (node->GetOperandCount() >= 1) ? node->Op(1) : nullptr;
+ GenTree* op2 = (node->GetOperandCount() >= 2) ? node->Op(2) : nullptr;
- genConsumeHWIntrinsicOperands(node);
+ genConsumeMultiOpOperands(node);
regNumber op1Reg = (op1 == nullptr) ? REG_NA : op1->GetRegNum();
emitter* emit = GetEmitter();
@@ -1395,13 +1370,11 @@ void CodeGen::genBaseIntrinsic(GenTreeHWIntrinsic* node)
case NI_Vector128_get_Zero:
case NI_Vector256_get_Zero:
{
- assert(op1 == nullptr);
emit->emitIns_SIMD_R_R_R(ins, attr, targetReg, targetReg, targetReg);
break;
}
case NI_Vector128_get_AllBitsSet:
- assert(op1 == nullptr);
if (varTypeIsFloating(baseType) && compiler->compOpportunisticallyDependsOn(InstructionSet_AVX))
{
// The following corresponds to vcmptrueps pseudo-op and not available without VEX prefix.
@@ -1414,7 +1387,6 @@ void CodeGen::genBaseIntrinsic(GenTreeHWIntrinsic* node)
break;
case NI_Vector256_get_AllBitsSet:
- assert(op1 == nullptr);
if (varTypeIsIntegral(baseType) && compiler->compOpportunisticallyDependsOn(InstructionSet_AVX2))
{
emit->emitIns_SIMD_R_R_R(ins, attr, targetReg, targetReg, targetReg);
@@ -1445,7 +1417,9 @@ void CodeGen::genBaseIntrinsic(GenTreeHWIntrinsic* node)
//
void CodeGen::genX86BaseIntrinsic(GenTreeHWIntrinsic* node)
{
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
+
+ genConsumeMultiOpOperands(node);
switch (intrinsicId)
{
@@ -1454,14 +1428,19 @@ void CodeGen::genX86BaseIntrinsic(GenTreeHWIntrinsic* node)
case NI_X86Base_X64_BitScanForward:
case NI_X86Base_X64_BitScanReverse:
{
- GenTree* op1 = node->gtGetOp1();
+ GenTree* op1 = node->Op(1);
regNumber targetReg = node->GetRegNum();
var_types targetType = node->TypeGet();
instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, targetType);
- genConsumeOperands(node);
genHWIntrinsic_R_RM(node, ins, emitTypeSize(targetType), targetReg, op1);
- genProduceReg(node);
+ break;
+ }
+
+ case NI_X86Base_Pause:
+ {
+ assert(node->GetSimdBaseType() == TYP_UNKNOWN);
+ GetEmitter()->emitIns(INS_pause);
break;
}
@@ -1469,6 +1448,8 @@ void CodeGen::genX86BaseIntrinsic(GenTreeHWIntrinsic* node)
unreached();
break;
}
+
+ genProduceReg(node);
}
//------------------------------------------------------------------------
@@ -1479,17 +1460,13 @@ void CodeGen::genX86BaseIntrinsic(GenTreeHWIntrinsic* node)
//
void CodeGen::genSSEIntrinsic(GenTreeHWIntrinsic* node)
{
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = node->gtGetOp2();
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
regNumber targetReg = node->GetRegNum();
var_types targetType = node->TypeGet();
var_types baseType = node->GetSimdBaseType();
+ emitter* emit = GetEmitter();
- regNumber op1Reg = REG_NA;
- emitter* emit = GetEmitter();
-
- genConsumeHWIntrinsicOperands(node);
+ genConsumeMultiOpOperands(node);
switch (intrinsicId)
{
@@ -1497,18 +1474,14 @@ void CodeGen::genSSEIntrinsic(GenTreeHWIntrinsic* node)
case NI_SSE_X64_ConvertToInt64WithTruncation:
{
assert(targetType == TYP_LONG);
- assert(op1 != nullptr);
- assert(op2 == nullptr);
instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, baseType);
- genHWIntrinsic_R_RM(node, ins, EA_8BYTE, targetReg, op1);
+ genHWIntrinsic_R_RM(node, ins, EA_8BYTE, targetReg, node->Op(1));
break;
}
case NI_SSE_X64_ConvertScalarToVector128Single:
{
assert(baseType == TYP_LONG);
- assert(op1 != nullptr);
- assert(op2 != nullptr);
instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, baseType);
genHWIntrinsic_R_R_RM(node, ins, EA_8BYTE);
break;
@@ -1520,21 +1493,17 @@ void CodeGen::genSSEIntrinsic(GenTreeHWIntrinsic* node)
case NI_SSE_PrefetchNonTemporal:
{
assert(baseType == TYP_UBYTE);
- assert(op2 == nullptr);
// These do not support containment.
- assert(!op1->isContained());
+ assert(!node->Op(1)->isContained());
instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, node->GetSimdBaseType());
- op1Reg = op1->GetRegNum();
- emit->emitIns_AR(ins, emitTypeSize(baseType), op1Reg, 0);
+ emit->emitIns_AR(ins, emitTypeSize(baseType), node->Op(1)->GetRegNum(), 0);
break;
}
case NI_SSE_StoreFence:
{
- assert(baseType == TYP_VOID);
- assert(op1 == nullptr);
- assert(op2 == nullptr);
+ assert(baseType == TYP_UNKNOWN);
emit->emitIns(INS_sfence);
break;
}
@@ -1555,24 +1524,19 @@ void CodeGen::genSSEIntrinsic(GenTreeHWIntrinsic* node)
//
void CodeGen::genSSE2Intrinsic(GenTreeHWIntrinsic* node)
{
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = node->gtGetOp2();
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
regNumber targetReg = node->GetRegNum();
var_types targetType = node->TypeGet();
var_types baseType = node->GetSimdBaseType();
- regNumber op1Reg = REG_NA;
emitter* emit = GetEmitter();
- genConsumeHWIntrinsicOperands(node);
+ genConsumeMultiOpOperands(node);
switch (intrinsicId)
{
case NI_SSE2_X64_ConvertScalarToVector128Double:
{
assert(baseType == TYP_LONG);
- assert(op1 != nullptr);
- assert(op2 != nullptr);
instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, baseType);
genHWIntrinsic_R_R_RM(node, ins, EA_8BYTE);
break;
@@ -1582,10 +1546,8 @@ void CodeGen::genSSE2Intrinsic(GenTreeHWIntrinsic* node)
case NI_SSE2_X64_ConvertScalarToVector128UInt64:
{
assert(baseType == TYP_LONG || baseType == TYP_ULONG);
- assert(op1 != nullptr);
- assert(op2 == nullptr);
instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, baseType);
- genHWIntrinsic_R_RM(node, ins, emitTypeSize(baseType), targetReg, op1);
+ genHWIntrinsic_R_RM(node, ins, emitTypeSize(baseType), targetReg, node->Op(1));
break;
}
@@ -1596,9 +1558,7 @@ void CodeGen::genSSE2Intrinsic(GenTreeHWIntrinsic* node)
case NI_SSE2_X64_ConvertToInt64WithTruncation:
case NI_SSE2_X64_ConvertToUInt64:
{
- assert(op2 == nullptr);
emitAttr attr;
-
if (varTypeIsIntegral(baseType))
{
assert(baseType == TYP_INT || baseType == TYP_UINT || baseType == TYP_LONG || baseType == TYP_ULONG);
@@ -1611,24 +1571,20 @@ void CodeGen::genSSE2Intrinsic(GenTreeHWIntrinsic* node)
}
instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, baseType);
- genHWIntrinsic_R_RM(node, ins, attr, targetReg, op1);
+ genHWIntrinsic_R_RM(node, ins, attr, targetReg, node->Op(1));
break;
}
case NI_SSE2_LoadFence:
{
- assert(baseType == TYP_VOID);
- assert(op1 == nullptr);
- assert(op2 == nullptr);
+ assert(baseType == TYP_UNKNOWN);
emit->emitIns(INS_lfence);
break;
}
case NI_SSE2_MemoryFence:
{
- assert(baseType == TYP_VOID);
- assert(op1 == nullptr);
- assert(op2 == nullptr);
+ assert(baseType == TYP_UNKNOWN);
emit->emitIns(INS_mfence);
break;
}
@@ -1637,11 +1593,8 @@ void CodeGen::genSSE2Intrinsic(GenTreeHWIntrinsic* node)
case NI_SSE2_X64_StoreNonTemporal:
{
assert(baseType == TYP_INT || baseType == TYP_UINT || baseType == TYP_LONG || baseType == TYP_ULONG);
- assert(op1 != nullptr);
- assert(op2 != nullptr);
-
instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, baseType);
- GenTreeStoreInd store = storeIndirForm(node->TypeGet(), op1, op2);
+ GenTreeStoreInd store = storeIndirForm(node->TypeGet(), node->Op(1), node->Op(2));
emit->emitInsStoreInd(ins, emitTypeSize(baseType), &store);
break;
}
@@ -1662,15 +1615,14 @@ void CodeGen::genSSE2Intrinsic(GenTreeHWIntrinsic* node)
//
void CodeGen::genSSE41Intrinsic(GenTreeHWIntrinsic* node)
{
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = node->gtGetOp2();
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
+ GenTree* op1 = node->Op(1);
regNumber targetReg = node->GetRegNum();
var_types baseType = node->GetSimdBaseType();
emitter* emit = GetEmitter();
- genConsumeHWIntrinsicOperands(node);
+ genConsumeMultiOpOperands(node);
switch (intrinsicId)
{
@@ -1699,6 +1651,7 @@ void CodeGen::genSSE41Intrinsic(GenTreeHWIntrinsic* node)
{
assert(!varTypeIsFloating(baseType));
+ GenTree* op2 = node->Op(2);
instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, baseType);
emitAttr attr = emitActualTypeSize(node->TypeGet());
@@ -1738,20 +1691,19 @@ void CodeGen::genSSE41Intrinsic(GenTreeHWIntrinsic* node)
//
void CodeGen::genSSE42Intrinsic(GenTreeHWIntrinsic* node)
{
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
regNumber targetReg = node->GetRegNum();
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = node->gtGetOp2();
+ GenTree* op1 = node->Op(1);
+ GenTree* op2 = node->Op(2);
var_types baseType = node->GetSimdBaseType();
var_types targetType = node->TypeGet();
emitter* emit = GetEmitter();
- genConsumeHWIntrinsicOperands(node);
+ genConsumeMultiOpOperands(node);
regNumber op1Reg = op1->GetRegNum();
assert(targetReg != REG_NA);
assert(op1Reg != REG_NA);
- assert(op2 != nullptr);
assert(!node->OperIsCommutative());
switch (intrinsicId)
@@ -1795,20 +1747,18 @@ void CodeGen::genSSE42Intrinsic(GenTreeHWIntrinsic* node)
//
void CodeGen::genAvxOrAvx2Intrinsic(GenTreeHWIntrinsic* node)
{
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
var_types baseType = node->GetSimdBaseType();
emitAttr attr = emitActualTypeSize(Compiler::getSIMDTypeForSize(node->GetSimdSize()));
var_types targetType = node->TypeGet();
instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, baseType);
- int numArgs = HWIntrinsicInfo::lookupNumArgs(node);
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = node->gtGetOp2();
+ size_t numArgs = node->GetOperandCount();
+ GenTree* op1 = node->Op(1);
regNumber op1Reg = REG_NA;
- regNumber op2Reg = REG_NA;
regNumber targetReg = node->GetRegNum();
emitter* emit = GetEmitter();
- genConsumeHWIntrinsicOperands(node);
+ genConsumeMultiOpOperands(node);
switch (intrinsicId)
{
@@ -1816,7 +1766,6 @@ void CodeGen::genAvxOrAvx2Intrinsic(GenTreeHWIntrinsic* node)
case NI_AVX2_ConvertToUInt32:
{
op1Reg = op1->GetRegNum();
- assert(numArgs == 1);
assert((baseType == TYP_INT) || (baseType == TYP_UINT));
instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, baseType);
emit->emitIns_Mov(ins, emitActualTypeSize(baseType), targetReg, op1Reg, /* canSkip */ false);
@@ -1848,24 +1797,13 @@ void CodeGen::genAvxOrAvx2Intrinsic(GenTreeHWIntrinsic* node)
case NI_AVX2_GatherMaskVector128:
case NI_AVX2_GatherMaskVector256:
{
- GenTreeArgList* list = op1->AsArgList();
- op1 = list->Current();
- op1Reg = op1->GetRegNum();
-
- list = list->Rest();
- op2 = list->Current();
- op2Reg = op2->GetRegNum();
-
- list = list->Rest();
- GenTree* op3 = list->Current();
-
- list = list->Rest();
- GenTree* op4 = nullptr;
+ GenTree* op2 = node->Op(2);
+ GenTree* op3 = node->Op(3);
GenTree* lastOp = nullptr;
GenTree* indexOp = nullptr;
- regNumber op3Reg = REG_NA;
- regNumber op4Reg = REG_NA;
+ op1Reg = op1->GetRegNum();
+ regNumber op2Reg = op2->GetRegNum();
regNumber addrBaseReg = REG_NA;
regNumber addrIndexReg = REG_NA;
regNumber maskReg = node->ExtractTempReg(RBM_ALLFLOAT);
@@ -1873,11 +1811,13 @@ void CodeGen::genAvxOrAvx2Intrinsic(GenTreeHWIntrinsic* node)
if (numArgs == 5)
{
assert(intrinsicId == NI_AVX2_GatherMaskVector128 || intrinsicId == NI_AVX2_GatherMaskVector256);
- op4 = list->Current();
- list = list->Rest();
- lastOp = list->Current();
- op3Reg = op3->GetRegNum();
- op4Reg = op4->GetRegNum();
+
+ GenTree* op4 = node->Op(4);
+ lastOp = node->Op(5);
+
+ regNumber op3Reg = op3->GetRegNum();
+ regNumber op4Reg = op4->GetRegNum();
+
addrBaseReg = op2Reg;
addrIndexReg = op3Reg;
indexOp = op3;
@@ -1974,18 +1914,15 @@ void CodeGen::genAESIntrinsic(GenTreeHWIntrinsic* node)
//
void CodeGen::genBMI1OrBMI2Intrinsic(GenTreeHWIntrinsic* node)
{
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
regNumber targetReg = node->GetRegNum();
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = node->gtGetOp2();
var_types targetType = node->TypeGet();
instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, targetType);
emitter* emit = GetEmitter();
assert(targetReg != REG_NA);
- assert(op1 != nullptr);
- genConsumeHWIntrinsicOperands(node);
+ genConsumeMultiOpOperands(node);
switch (intrinsicId)
{
@@ -2000,7 +1937,6 @@ void CodeGen::genBMI1OrBMI2Intrinsic(GenTreeHWIntrinsic* node)
case NI_BMI2_ZeroHighBits:
case NI_BMI2_X64_ZeroHighBits:
{
- assert(op2 != nullptr);
assert((targetType == TYP_INT) || (targetType == TYP_LONG));
genHWIntrinsic_R_R_RM(node, ins, emitTypeSize(node->TypeGet()));
break;
@@ -2013,16 +1949,14 @@ void CodeGen::genBMI1OrBMI2Intrinsic(GenTreeHWIntrinsic* node)
case NI_BMI1_X64_GetMaskUpToLowestSetBit:
case NI_BMI1_X64_ResetLowestSetBit:
{
- assert(op2 == nullptr);
assert((targetType == TYP_INT) || (targetType == TYP_LONG));
- genHWIntrinsic_R_RM(node, ins, emitTypeSize(node->TypeGet()), targetReg, op1);
+ genHWIntrinsic_R_RM(node, ins, emitTypeSize(node->TypeGet()), targetReg, node->Op(1));
break;
}
case NI_BMI1_TrailingZeroCount:
case NI_BMI1_X64_TrailingZeroCount:
{
- assert(op2 == nullptr);
assert((targetType == TYP_INT) || (targetType == TYP_LONG));
genXCNTIntrinsic(node, ins);
break;
@@ -2031,32 +1965,26 @@ void CodeGen::genBMI1OrBMI2Intrinsic(GenTreeHWIntrinsic* node)
case NI_BMI2_MultiplyNoFlags:
case NI_BMI2_X64_MultiplyNoFlags:
{
- int numArgs = HWIntrinsicInfo::lookupNumArgs(node);
+ size_t numArgs = node->GetOperandCount();
assert(numArgs == 2 || numArgs == 3);
- regNumber op1Reg = REG_NA;
- regNumber op2Reg = REG_NA;
+ GenTree* op1 = node->Op(1);
+ GenTree* op2 = node->Op(2);
+
+ regNumber op1Reg = op1->GetRegNum();
+ regNumber op2Reg = op2->GetRegNum();
regNumber op3Reg = REG_NA;
regNumber lowReg = REG_NA;
if (numArgs == 2)
{
- op1Reg = op1->GetRegNum();
- op2Reg = op2->GetRegNum();
lowReg = targetReg;
}
else
{
- GenTreeArgList* argList = op1->AsArgList();
- op1 = argList->Current();
- op1Reg = op1->GetRegNum();
- argList = argList->Rest();
- op2 = argList->Current();
- op2Reg = op2->GetRegNum();
- argList = argList->Rest();
- GenTree* op3 = argList->Current();
- op3Reg = op3->GetRegNum();
- assert(!op3->isContained());
+ op3Reg = node->Op(3)->GetRegNum();
+
+ assert(!node->Op(3)->isContained());
assert(op3Reg != op1Reg);
assert(op3Reg != targetReg);
assert(op3Reg != REG_EDX);
@@ -2103,78 +2031,85 @@ void CodeGen::genBMI1OrBMI2Intrinsic(GenTreeHWIntrinsic* node)
//
void CodeGen::genFMAIntrinsic(GenTreeHWIntrinsic* node)
{
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
var_types baseType = node->GetSimdBaseType();
emitAttr attr = emitActualTypeSize(Compiler::getSIMDTypeForSize(node->GetSimdSize()));
- instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, baseType);
- GenTree* op1 = node->gtGetOp1();
- regNumber targetReg = node->GetRegNum();
-
- assert(HWIntrinsicInfo::lookupNumArgs(node) == 3);
+ instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, baseType); // 213 form
+ instruction _132form = (instruction)(ins - 1);
+ instruction _231form = (instruction)(ins + 1);
+ GenTree* op1 = node->Op(1);
+ GenTree* op2 = node->Op(2);
+ GenTree* op3 = node->Op(3);
- genConsumeHWIntrinsicOperands(node);
- GenTreeArgList* argList = op1->AsArgList();
- op1 = argList->Current();
+ regNumber targetReg = node->GetRegNum();
- argList = argList->Rest();
- GenTree* op2 = argList->Current();
+ genConsumeMultiOpOperands(node);
- argList = argList->Rest();
- GenTree* op3 = argList->Current();
+ regNumber op1NodeReg = op1->GetRegNum();
+ regNumber op2NodeReg = op2->GetRegNum();
+ regNumber op3NodeReg = op3->GetRegNum();
- regNumber op1Reg;
- regNumber op2Reg;
+ GenTree* emitOp1 = op1;
+ GenTree* emitOp2 = op2;
+ GenTree* emitOp3 = op3;
- bool isCommutative = false;
const bool copiesUpperBits = HWIntrinsicInfo::CopiesUpperBits(intrinsicId);
// Intrinsics with CopyUpperBits semantics cannot have op1 be contained
assert(!copiesUpperBits || !op1->isContained());
- if (op2->isContained() || op2->isUsedFromSpillTemp())
+ if (op1->isContained() || op1->isUsedFromSpillTemp())
{
- // 132 form: op1 = (op1 * op3) + [op2]
-
- ins = (instruction)(ins - 1);
- op1Reg = op1->GetRegNum();
- op2Reg = op3->GetRegNum();
- op3 = op2;
+ if (targetReg == op2NodeReg)
+ {
+ std::swap(emitOp1, emitOp2);
+ // op2 = ([op1] * op2) + op3
+ // 132 form: XMM1 = (XMM1 * [XMM3]) + XMM2
+ ins = _132form;
+ std::swap(emitOp2, emitOp3);
+ }
+ else
+ {
+ // targetReg == op3NodeReg or targetReg == ?
+ // op3 = ([op1] * op2) + op3
+ // 231 form: XMM1 = (XMM2 * [XMM3]) + XMM1
+ ins = _231form;
+ std::swap(emitOp1, emitOp3);
+ }
}
- else if (op1->isContained() || op1->isUsedFromSpillTemp())
+ else if (op2->isContained() || op2->isUsedFromSpillTemp())
{
- // 231 form: op3 = (op2 * op3) + [op1]
-
- ins = (instruction)(ins + 1);
- op1Reg = op3->GetRegNum();
- op2Reg = op2->GetRegNum();
- op3 = op1;
+ if (!copiesUpperBits && (targetReg == op3NodeReg))
+ {
+ // op3 = (op1 * [op2]) + op3
+ // 231 form: XMM1 = (XMM2 * [XMM3]) + XMM1
+ ins = _231form;
+ std::swap(emitOp1, emitOp3);
+ }
+ else
+ {
+ // targetReg == op1NodeReg or targetReg == ?
+ // op1 = (op1 * [op2]) + op3
+ // 132 form: XMM1 = (XMM1 * [XMM3]) + XMM2
+ ins = _132form;
+ }
+ std::swap(emitOp2, emitOp3);
}
else
{
- // 213 form: op1 = (op2 * op1) + [op3]
-
- op1Reg = op1->GetRegNum();
- op2Reg = op2->GetRegNum();
-
- isCommutative = !copiesUpperBits;
- }
-
- if (isCommutative && (op1Reg != targetReg) && (op2Reg == targetReg))
- {
- assert(node->isRMWHWIntrinsic(compiler));
-
- // We have "reg2 = (reg1 * reg2) +/- op3" where "reg1 != reg2" on a RMW intrinsic.
- //
- // For non-commutative intrinsics, we should have ensured that op2 was marked
- // delay free in order to prevent it from getting assigned the same register
- // as target. However, for commutative intrinsics, we can just swap the operands
- // in order to have "reg2 = reg2 op reg1" which will end up producing the right code.
-
- op2Reg = op1Reg;
- op1Reg = targetReg;
+ // targetReg could be op1NodeReg, op2NodeReg, or not equal to any op
+ // op1 = (op1 * op2) + [op3] or op2 = (op1 * op2) + [op3]
+ // ? = (op1 * op2) + [op3] or ? = (op1 * op2) + op3
+ // 213 form: XMM1 = (XMM2 * XMM1) + [XMM3]
+ if (!copiesUpperBits && (targetReg == op2NodeReg))
+ {
+ // op2 = (op1 * op2) + [op3]
+ // 213 form: XMM1 = (XMM2 * XMM1) + [XMM3]
+ std::swap(emitOp1, emitOp2);
+ }
}
- genHWIntrinsic_R_R_R_RM(ins, attr, targetReg, op1Reg, op2Reg, op3);
+ genHWIntrinsic_R_R_R_RM(ins, attr, targetReg, emitOp1->GetRegNum(), emitOp2->GetRegNum(), emitOp3);
genProduceReg(node);
}
@@ -2186,10 +2121,10 @@ void CodeGen::genFMAIntrinsic(GenTreeHWIntrinsic* node)
//
void CodeGen::genLZCNTIntrinsic(GenTreeHWIntrinsic* node)
{
- assert(node->gtHWIntrinsicId == NI_LZCNT_LeadingZeroCount ||
- node->gtHWIntrinsicId == NI_LZCNT_X64_LeadingZeroCount);
+ assert((node->GetHWIntrinsicId() == NI_LZCNT_LeadingZeroCount) ||
+ (node->GetHWIntrinsicId() == NI_LZCNT_X64_LeadingZeroCount));
- genConsumeOperands(node);
+ genConsumeMultiOpOperands(node);
genXCNTIntrinsic(node, INS_lzcnt);
genProduceReg(node);
}
@@ -2213,9 +2148,9 @@ void CodeGen::genPCLMULQDQIntrinsic(GenTreeHWIntrinsic* node)
//
void CodeGen::genPOPCNTIntrinsic(GenTreeHWIntrinsic* node)
{
- assert(node->gtHWIntrinsicId == NI_POPCNT_PopCount || node->gtHWIntrinsicId == NI_POPCNT_X64_PopCount);
+ assert(node->GetHWIntrinsicId() == NI_POPCNT_PopCount || node->GetHWIntrinsicId() == NI_POPCNT_X64_PopCount);
- genConsumeOperands(node);
+ genConsumeMultiOpOperands(node);
genXCNTIntrinsic(node, INS_popcnt);
genProduceReg(node);
}
@@ -2234,7 +2169,7 @@ void CodeGen::genXCNTIntrinsic(GenTreeHWIntrinsic* node, instruction ins)
// (POPCNT only) processors, so insert a `XOR target, target` to break the dependency via XOR triggering register
// renaming, but only if it's not an actual dependency.
- GenTree* op1 = node->gtGetOp1();
+ GenTree* op1 = node->Op(1);
regNumber sourceReg1 = REG_NA;
regNumber sourceReg2 = REG_NA;
diff --git a/src/coreclr/jit/hwintrinsiclistarm64.h b/src/coreclr/jit/hwintrinsiclistarm64.h
index ba185f3afeb917..c7e49b91a05be8 100644
--- a/src/coreclr/jit/hwintrinsiclistarm64.h
+++ b/src/coreclr/jit/hwintrinsiclistarm64.h
@@ -87,6 +87,8 @@ HARDWARE_INTRINSIC(Vector64, Sqrt,
HARDWARE_INTRINSIC(Vector64, ToScalar, 8, 1, {INS_smov, INS_umov, INS_smov, INS_umov, INS_smov, INS_umov, INS_umov, INS_umov, INS_dup, INS_dup}, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SIMDScalar|HW_Flag_SpecialCodeGen)
HARDWARE_INTRINSIC(Vector64, ToVector128, 8, 1, {INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov}, HW_Category_SIMD, HW_Flag_SpecialCodeGen)
HARDWARE_INTRINSIC(Vector64, ToVector128Unsafe, 8, 1, {INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov, INS_mov}, HW_Category_SIMD, HW_Flag_SpecialCodeGen)
+HARDWARE_INTRINSIC(Vector64, WidenLower, 8, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg)
+HARDWARE_INTRINSIC(Vector64, WidenUpper, 8, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg)
HARDWARE_INTRINSIC(Vector64, WithElement, 8, 3, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialImport)
HARDWARE_INTRINSIC(Vector64, Xor, 8, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen)
@@ -171,6 +173,8 @@ HARDWARE_INTRINSIC(Vector128, op_UnaryPlus,
HARDWARE_INTRINSIC(Vector128, Subtract, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen)
HARDWARE_INTRINSIC(Vector128, Sqrt, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen)
HARDWARE_INTRINSIC(Vector128, ToScalar, 16, 1, {INS_smov, INS_umov, INS_smov, INS_umov, INS_smov, INS_umov, INS_umov, INS_umov, INS_dup, INS_dup}, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SIMDScalar|HW_Flag_SpecialCodeGen)
+HARDWARE_INTRINSIC(Vector128, WidenLower, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg)
+HARDWARE_INTRINSIC(Vector128, WidenUpper, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg)
HARDWARE_INTRINSIC(Vector128, WithElement, 16, 3, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialImport)
HARDWARE_INTRINSIC(Vector128, Xor, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen)
@@ -623,6 +627,7 @@ HARDWARE_INTRINSIC(Aes, PolynomialMultiplyWideningUpper,
// Base Intrinsics
HARDWARE_INTRINSIC(ArmBase, LeadingZeroCount, 0, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_clz, INS_clz, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoFloatingPointUsed)
HARDWARE_INTRINSIC(ArmBase, ReverseElementBits, 0, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_rbit, INS_rbit, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_NoFloatingPointUsed)
+HARDWARE_INTRINSIC(ArmBase, Yield, 0, 0, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Special, HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport)
// ***************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
// ISA Function name SIMD size Number of arguments Instructions Category Flags
diff --git a/src/coreclr/jit/hwintrinsiclistxarch.h b/src/coreclr/jit/hwintrinsiclistxarch.h
index 00a402260fc42a..ce66b6e6906040 100644
--- a/src/coreclr/jit/hwintrinsiclistxarch.h
+++ b/src/coreclr/jit/hwintrinsiclistxarch.h
@@ -104,6 +104,8 @@ HARDWARE_INTRINSIC(Vector128, Sqrt,
HARDWARE_INTRINSIC(Vector128, ToScalar, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movss, INS_movsdsse2}, HW_Category_SimpleSIMD, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoRMWSemantics)
HARDWARE_INTRINSIC(Vector128, ToVector256, 16, 1, {INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movups, INS_movupd}, HW_Category_SimpleSIMD, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoRMWSemantics)
HARDWARE_INTRINSIC(Vector128, ToVector256Unsafe, 16, 1, {INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movups, INS_movupd}, HW_Category_SimpleSIMD, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoRMWSemantics)
+HARDWARE_INTRINSIC(Vector128, WidenLower, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg)
+HARDWARE_INTRINSIC(Vector128, WidenUpper, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg)
HARDWARE_INTRINSIC(Vector128, WithElement, 16, 3, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoContainment|HW_Flag_BaseTypeFromFirstArg)
HARDWARE_INTRINSIC(Vector128, Xor, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen)
@@ -185,6 +187,8 @@ HARDWARE_INTRINSIC(Vector256, op_UnaryPlus,
HARDWARE_INTRINSIC(Vector256, Subtract, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen)
HARDWARE_INTRINSIC(Vector256, Sqrt, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen)
HARDWARE_INTRINSIC(Vector256, ToScalar, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movss, INS_movsdsse2}, HW_Category_SimpleSIMD, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoRMWSemantics)
+HARDWARE_INTRINSIC(Vector256, WidenLower, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg)
+HARDWARE_INTRINSIC(Vector256, WidenUpper, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg)
HARDWARE_INTRINSIC(Vector256, WithElement, 32, 3, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoContainment|HW_Flag_BaseTypeFromFirstArg)
HARDWARE_INTRINSIC(Vector256, Xor, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen)
@@ -195,6 +199,7 @@ HARDWARE_INTRINSIC(Vector256, Xor,
// X86Base Intrinsics
HARDWARE_INTRINSIC(X86Base, BitScanForward, 0, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_bsf, INS_bsf, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_NoFloatingPointUsed|HW_Flag_NoRMWSemantics)
HARDWARE_INTRINSIC(X86Base, BitScanReverse, 0, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_bsr, INS_bsr, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_NoFloatingPointUsed|HW_Flag_NoRMWSemantics)
+HARDWARE_INTRINSIC(X86Base, Pause, 0, 0, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Special, HW_Flag_NoContainment|HW_Flag_NoRMWSemantics)
// ***************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
// ISA Function name SIMD size NumArg Instructions Category Flags
diff --git a/src/coreclr/jit/hwintrinsicxarch.cpp b/src/coreclr/jit/hwintrinsicxarch.cpp
index 46fb8b7c3f86c5..72c9734afc48b7 100644
--- a/src/coreclr/jit/hwintrinsicxarch.cpp
+++ b/src/coreclr/jit/hwintrinsicxarch.cpp
@@ -491,6 +491,7 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic,
{
case InstructionSet_Vector256:
case InstructionSet_Vector128:
+ case InstructionSet_X86Base:
return impBaseIntrinsic(intrinsic, clsHnd, method, sig, simdBaseJitType, retType, simdSize);
case InstructionSet_SSE:
return impSSEIntrinsic(intrinsic, method, sig);
@@ -548,8 +549,13 @@ GenTree* Compiler::impBaseIntrinsic(NamedIntrinsic intrinsic,
return nullptr;
}
- var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType);
- assert(varTypeIsArithmetic(simdBaseType));
+ var_types simdBaseType = TYP_UNKNOWN;
+
+ if (intrinsic != NI_X86Base_Pause)
+ {
+ simdBaseType = JitType2PreciseVarType(simdBaseJitType);
+ assert(varTypeIsArithmetic(simdBaseType));
+ }
switch (intrinsic)
{
@@ -858,31 +864,14 @@ GenTree* Compiler::impBaseIntrinsic(NamedIntrinsic intrinsic,
}
#endif // TARGET_X86
- if (sig->numArgs == 1)
- {
- op1 = impPopStack().val;
- retNode = gtNewSimdHWIntrinsicNode(retType, op1, intrinsic, simdBaseJitType, simdSize);
- }
- else if (sig->numArgs == 2)
+ IntrinsicNodeBuilder nodeBuilder(getAllocator(CMK_ASTNode), sig->numArgs);
+
+ for (int i = sig->numArgs - 1; i >= 0; i--)
{
- op2 = impPopStack().val;
- op1 = impPopStack().val;
- retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, simdBaseJitType, simdSize);
+ nodeBuilder.AddOperand(i, impPopStack().val);
}
- else
- {
- assert(sig->numArgs >= 3);
- GenTreeArgList* tmp = nullptr;
-
- for (unsigned i = 0; i < sig->numArgs; i++)
- {
- tmp = gtNewListNode(impPopStack().val, tmp);
- }
-
- op1 = tmp;
- retNode = gtNewSimdHWIntrinsicNode(retType, op1, intrinsic, simdBaseJitType, simdSize);
- }
+ retNode = gtNewSimdHWIntrinsicNode(retType, std::move(nodeBuilder), intrinsic, simdBaseJitType, simdSize);
break;
}
@@ -1294,7 +1283,15 @@ GenTree* Compiler::impBaseIntrinsic(NamedIntrinsic intrinsic,
case NI_Vector256_Narrow:
{
assert(sig->numArgs == 2);
- // TODO-XARCH-CQ: These intrinsics should be accelerated
+
+ if ((simdSize != 32) || varTypeIsFloating(simdBaseType) || compExactlyDependsOn(InstructionSet_AVX2))
+ {
+ op2 = impSIMDPopStack(retType);
+ op1 = impSIMDPopStack(retType);
+
+ retNode =
+ gtNewSimdNarrowNode(retType, op1, op2, simdBaseJitType, simdSize, /* isSimdAsHWIntrinsic */ false);
+ }
break;
}
@@ -1416,6 +1413,36 @@ GenTree* Compiler::impBaseIntrinsic(NamedIntrinsic intrinsic,
break;
}
+ case NI_Vector128_WidenLower:
+ case NI_Vector256_WidenLower:
+ {
+ assert(sig->numArgs == 1);
+
+ if ((simdSize != 32) || varTypeIsFloating(simdBaseType) || compExactlyDependsOn(InstructionSet_AVX2))
+ {
+ op1 = impSIMDPopStack(retType);
+
+ retNode =
+ gtNewSimdWidenLowerNode(retType, op1, simdBaseJitType, simdSize, /* isSimdAsHWIntrinsic */ false);
+ }
+ break;
+ }
+
+ case NI_Vector128_WidenUpper:
+ case NI_Vector256_WidenUpper:
+ {
+ assert(sig->numArgs == 1);
+
+ if ((simdSize != 32) || varTypeIsFloating(simdBaseType) || compExactlyDependsOn(InstructionSet_AVX2))
+ {
+ op1 = impSIMDPopStack(retType);
+
+ retNode =
+ gtNewSimdWidenUpperNode(retType, op1, simdBaseJitType, simdSize, /* isSimdAsHWIntrinsic */ false);
+ }
+ break;
+ }
+
case NI_Vector128_WithElement:
case NI_Vector256_WithElement:
{
@@ -1494,6 +1521,16 @@ GenTree* Compiler::impBaseIntrinsic(NamedIntrinsic intrinsic,
break;
}
+ case NI_X86Base_Pause:
+ {
+ assert(sig->numArgs == 0);
+ assert(JITtype2varType(sig->retType) == TYP_VOID);
+ assert(simdSize == 0);
+
+ retNode = gtNewScalarHWIntrinsicNode(TYP_VOID, intrinsic);
+ break;
+ }
+
default:
{
return nullptr;
@@ -1566,7 +1603,7 @@ GenTree* Compiler::impSSEIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HAND
case NI_SSE_StoreFence:
assert(sig->numArgs == 0);
assert(JITtype2varType(sig->retType) == TYP_VOID);
- retNode = gtNewSimdHWIntrinsicNode(TYP_VOID, intrinsic, CORINFO_TYPE_VOID, 0);
+ retNode = gtNewScalarHWIntrinsicNode(TYP_VOID, intrinsic);
break;
default:
@@ -1629,7 +1666,7 @@ GenTree* Compiler::impSSE2Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HAN
assert(JITtype2varType(sig->retType) == TYP_VOID);
assert(simdSize == 0);
- retNode = gtNewSimdHWIntrinsicNode(TYP_VOID, intrinsic, CORINFO_TYPE_VOID, simdSize);
+ retNode = gtNewScalarHWIntrinsicNode(TYP_VOID, intrinsic);
break;
}
@@ -1713,9 +1750,11 @@ GenTree* Compiler::impAvxOrAvx2Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHO
op1 = getArgForHWIntrinsic(argType, argClass);
SetOpLclRelatedToSIMDIntrinsic(op1);
- GenTree* opList = new (this, GT_LIST) GenTreeArgList(op1, gtNewArgList(op2, op3, op4, op5));
- retNode = new (this, GT_HWINTRINSIC) GenTreeHWIntrinsic(retType, opList, intrinsic, simdBaseJitType,
- simdSize, /* isSimdAsHWIntrinsic */ false);
+ const bool isSimdAsHWIntrinsic = false;
+
+ retNode = new (this, GT_HWINTRINSIC)
+ GenTreeHWIntrinsic(retType, getAllocator(CMK_ASTNode), intrinsic, simdBaseJitType, simdSize,
+ isSimdAsHWIntrinsic, op1, op2, op3, op4, op5);
retNode->AsHWIntrinsic()->SetAuxiliaryJitType(indexBaseJitType);
break;
}
diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp
index d1c955fddb321a..5aaf145a3323ea 100644
--- a/src/coreclr/jit/importer.cpp
+++ b/src/coreclr/jit/importer.cpp
@@ -527,14 +527,22 @@ inline void Compiler::impAppendStmtCheck(Statement* stmt, unsigned chkLevel)
#endif
}
-/*****************************************************************************
- *
- * Append the given statement to the current block's tree list.
- * [0..chkLevel) is the portion of the stack which we will check for
- * interference with stmt and spill if needed.
- */
-
-inline void Compiler::impAppendStmt(Statement* stmt, unsigned chkLevel)
+//------------------------------------------------------------------------
+// impAppendStmt: Append the given statement to the current block's tree list.
+//
+//
+// Arguments:
+// stmt - The statement to add.
+// chkLevel - [0..chkLevel) is the portion of the stack which we will check
+// for interference with stmt and spill if needed.
+// checkConsumedDebugInfo - Whether to check for consumption of impCurStmtDI. impCurStmtDI
+// marks the debug info of the current boundary and is set when we
+// start importing IL at that boundary. If this parameter is true,
+// then the function checks if 'stmt' has been associated with the
+// current boundary, and if so, clears it so that we do not attach
+// it to more upcoming statements.
+//
+void Compiler::impAppendStmt(Statement* stmt, unsigned chkLevel, bool checkConsumedDebugInfo)
{
if (chkLevel == (unsigned)CHECK_SPILL_ALL)
{
@@ -548,8 +556,8 @@ inline void Compiler::impAppendStmt(Statement* stmt, unsigned chkLevel)
/* If the statement being appended has any side-effects, check the stack
to see if anything needs to be spilled to preserve correct ordering. */
- GenTree* expr = stmt->GetRootNode();
- unsigned flags = expr->gtFlags & GTF_GLOB_EFFECT;
+ GenTree* expr = stmt->GetRootNode();
+ GenTreeFlags flags = expr->gtFlags & GTF_GLOB_EFFECT;
// Assignment to (unaliased) locals don't count as a side-effect as
// we handle them specially using impSpillLclRefs(). Temp locals should
@@ -558,7 +566,7 @@ inline void Compiler::impAppendStmt(Statement* stmt, unsigned chkLevel)
if ((expr->gtOper == GT_ASG) && (expr->AsOp()->gtOp1->gtOper == GT_LCL_VAR) &&
((expr->AsOp()->gtOp1->gtFlags & GTF_GLOB_REF) == 0) && !gtHasLocalsWithAddrOp(expr->AsOp()->gtOp2))
{
- unsigned op2Flags = expr->AsOp()->gtOp2->gtFlags & GTF_GLOB_EFFECT;
+ GenTreeFlags op2Flags = expr->AsOp()->gtOp2->gtFlags & GTF_GLOB_EFFECT;
assert(flags == (op2Flags | GTF_ASG));
flags = op2Flags;
}
@@ -615,10 +623,13 @@ inline void Compiler::impAppendStmt(Statement* stmt, unsigned chkLevel)
impMarkContiguousSIMDFieldAssignments(stmt);
#endif
- /* Once we set impCurStmtOffs in an appended tree, we are ready to
- report the following offsets. So reset impCurStmtOffs */
+ // Once we set the current offset as debug info in an appended tree, we are
+ // ready to report the following offsets. Note that we need to compare
+ // offsets here instead of debug info, since we do not set the "is call"
+ // bit in impCurStmtDI.
- if (impLastStmt->GetILOffsetX() == impCurStmtOffs)
+ if (checkConsumedDebugInfo &&
+ (impLastStmt->GetDebugInfo().GetLocation().GetOffset() == impCurStmtDI.GetLocation().GetOffset()))
{
impCurStmtOffsSet(BAD_IL_OFFSET);
}
@@ -707,23 +718,36 @@ inline void Compiler::impInsertStmtBefore(Statement* stmt, Statement* stmtBefore
stmtBefore->SetPrevStmt(stmt);
}
-/*****************************************************************************
- *
- * Append the given expression tree to the current block's tree list.
- * Return the newly created statement.
- */
-
-Statement* Compiler::impAppendTree(GenTree* tree, unsigned chkLevel, IL_OFFSETX offset)
+//------------------------------------------------------------------------
+// impAppendTree: Append the given expression tree to the current block's tree list.
+//
+//
+// Arguments:
+// tree - The tree that will be the root of the newly created statement.
+// chkLevel - [0..chkLevel) is the portion of the stack which we will check
+// for interference with stmt and spill if needed.
+// di - Debug information to associate with the statement.
+// checkConsumedDebugInfo - Whether to check for consumption of impCurStmtDI. impCurStmtDI
+// marks the debug info of the current boundary and is set when we
+// start importing IL at that boundary. If this parameter is true,
+// then the function checks if 'stmt' has been associated with the
+// current boundary, and if so, clears it so that we do not attach
+// it to more upcoming statements.
+//
+// Return value:
+// The newly created statement.
+//
+Statement* Compiler::impAppendTree(GenTree* tree, unsigned chkLevel, const DebugInfo& di, bool checkConsumedDebugInfo)
{
assert(tree);
/* Allocate an 'expression statement' node */
- Statement* stmt = gtNewStmt(tree, offset);
+ Statement* stmt = gtNewStmt(tree, di);
/* Append the statement to the current block's stmt list */
- impAppendStmt(stmt, chkLevel);
+ impAppendStmt(stmt, chkLevel, checkConsumedDebugInfo);
return stmt;
}
@@ -733,11 +757,11 @@ Statement* Compiler::impAppendTree(GenTree* tree, unsigned chkLevel, IL_OFFSETX
* Insert the given expression tree before "stmtBefore"
*/
-void Compiler::impInsertTreeBefore(GenTree* tree, IL_OFFSETX offset, Statement* stmtBefore)
+void Compiler::impInsertTreeBefore(GenTree* tree, const DebugInfo& di, Statement* stmtBefore)
{
/* Allocate an 'expression statement' node */
- Statement* stmt = gtNewStmt(tree, offset);
+ Statement* stmt = gtNewStmt(tree, di);
/* Append the statement to the current block's stmt list */
@@ -750,12 +774,12 @@ void Compiler::impInsertTreeBefore(GenTree* tree, IL_OFFSETX offset, Statement*
* curLevel is the stack level for which the spill to the temp is being done.
*/
-void Compiler::impAssignTempGen(unsigned tmp,
- GenTree* val,
- unsigned curLevel,
- Statement** pAfterStmt, /* = NULL */
- IL_OFFSETX ilOffset, /* = BAD_IL_OFFSET */
- BasicBlock* block /* = NULL */
+void Compiler::impAssignTempGen(unsigned tmp,
+ GenTree* val,
+ unsigned curLevel,
+ Statement** pAfterStmt, /* = NULL */
+ const DebugInfo& di, /* = DebugInfo() */
+ BasicBlock* block /* = NULL */
)
{
GenTree* asg = gtNewTempAssign(tmp, val);
@@ -764,13 +788,13 @@ void Compiler::impAssignTempGen(unsigned tmp,
{
if (pAfterStmt)
{
- Statement* asgStmt = gtNewStmt(asg, ilOffset);
+ Statement* asgStmt = gtNewStmt(asg, di);
fgInsertStmtAfter(block, *pAfterStmt, asgStmt);
*pAfterStmt = asgStmt;
}
else
{
- impAppendTree(asg, curLevel, impCurStmtOffs);
+ impAppendTree(asg, curLevel, impCurStmtDI);
}
}
}
@@ -784,7 +808,7 @@ void Compiler::impAssignTempGen(unsigned tmpNum,
CORINFO_CLASS_HANDLE structType,
unsigned curLevel,
Statement** pAfterStmt, /* = NULL */
- IL_OFFSETX ilOffset, /* = BAD_IL_OFFSET */
+ const DebugInfo& di, /* = DebugInfo() */
BasicBlock* block /* = NULL */
)
{
@@ -813,7 +837,7 @@ void Compiler::impAssignTempGen(unsigned tmpNum,
// calls that may not actually be required - e.g. if we only access a field of a struct.
GenTree* dst = gtNewLclvNode(tmpNum, varType);
- asg = impAssignStruct(dst, val, structType, curLevel, pAfterStmt, ilOffset, block);
+ asg = impAssignStruct(dst, val, structType, curLevel, pAfterStmt, di, block);
}
else
{
@@ -824,13 +848,13 @@ void Compiler::impAssignTempGen(unsigned tmpNum,
{
if (pAfterStmt)
{
- Statement* asgStmt = gtNewStmt(asg, ilOffset);
+ Statement* asgStmt = gtNewStmt(asg, di);
fgInsertStmtAfter(block, *pAfterStmt, asgStmt);
*pAfterStmt = asgStmt;
}
else
{
- impAppendTree(asg, curLevel, impCurStmtOffs);
+ impAppendTree(asg, curLevel, impCurStmtDI);
}
}
}
@@ -1194,15 +1218,16 @@ GenTree* Compiler::impAssignStruct(GenTree* dest,
CORINFO_CLASS_HANDLE structHnd,
unsigned curLevel,
Statement** pAfterStmt, /* = nullptr */
- IL_OFFSETX ilOffset, /* = BAD_IL_OFFSET */
+ const DebugInfo& di, /* = DebugInfo() */
BasicBlock* block /* = nullptr */
)
{
assert(varTypeIsStruct(dest));
- if (ilOffset == BAD_IL_OFFSET)
+ DebugInfo usedDI = di;
+ if (!usedDI.IsValid())
{
- ilOffset = impCurStmtOffs;
+ usedDI = impCurStmtDI;
}
while (dest->gtOper == GT_COMMA)
@@ -1213,13 +1238,13 @@ GenTree* Compiler::impAssignStruct(GenTree* dest,
// Append all the op1 of GT_COMMA trees before we evaluate op2 of the GT_COMMA tree.
if (pAfterStmt)
{
- Statement* newStmt = gtNewStmt(dest->AsOp()->gtOp1, ilOffset);
+ Statement* newStmt = gtNewStmt(dest->AsOp()->gtOp1, usedDI);
fgInsertStmtAfter(block, *pAfterStmt, newStmt);
*pAfterStmt = newStmt;
}
else
{
- impAppendTree(dest->AsOp()->gtOp1, curLevel, ilOffset); // do the side effect
+ impAppendTree(dest->AsOp()->gtOp1, curLevel, usedDI); // do the side effect
}
// set dest to the second thing
@@ -1249,7 +1274,7 @@ GenTree* Compiler::impAssignStruct(GenTree* dest,
destAddr = gtNewOperNode(GT_ADDR, TYP_BYREF, dest);
}
- return (impAssignStructPtr(destAddr, src, structHnd, curLevel, pAfterStmt, ilOffset, block));
+ return (impAssignStructPtr(destAddr, src, structHnd, curLevel, pAfterStmt, usedDI, block));
}
//------------------------------------------------------------------------
@@ -1261,7 +1286,7 @@ GenTree* Compiler::impAssignStruct(GenTree* dest,
// structHnd - handle representing the struct type
// curLevel - stack level for which a spill may be being done
// pAfterStmt - statement to insert any additional statements after
-// ilOffset - il offset for new statements
+// di - debug info for new statements
// block - block to insert any additional statements in
//
// Return Value:
@@ -1275,16 +1300,17 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr,
CORINFO_CLASS_HANDLE structHnd,
unsigned curLevel,
Statement** pAfterStmt, /* = NULL */
- IL_OFFSETX ilOffset, /* = BAD_IL_OFFSET */
+ const DebugInfo& di, /* = DebugInfo() */
BasicBlock* block /* = NULL */
)
{
GenTree* dest = nullptr;
GenTreeFlags destFlags = GTF_EMPTY;
- if (ilOffset == BAD_IL_OFFSET)
+ DebugInfo usedDI = di;
+ if (!usedDI.IsValid())
{
- ilOffset = impCurStmtOffs;
+ usedDI = impCurStmtDI;
}
assert(src->OperIs(GT_LCL_VAR, GT_LCL_FLD, GT_FIELD, GT_IND, GT_OBJ, GT_CALL, GT_MKREFANY, GT_RET_EXPR, GT_COMMA) ||
@@ -1501,13 +1527,13 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr,
GenTree* asg = gtNewAssignNode(ptrSlot, src->AsOp()->gtOp1);
if (pAfterStmt)
{
- Statement* newStmt = gtNewStmt(asg, ilOffset);
+ Statement* newStmt = gtNewStmt(asg, usedDI);
fgInsertStmtAfter(block, *pAfterStmt, newStmt);
*pAfterStmt = newStmt;
}
else
{
- impAppendTree(asg, curLevel, ilOffset);
+ impAppendTree(asg, curLevel, usedDI);
}
// return the assign of the type value, to be appended
@@ -1520,14 +1546,14 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr,
if (pAfterStmt)
{
// Insert op1 after '*pAfterStmt'
- Statement* newStmt = gtNewStmt(src->AsOp()->gtOp1, ilOffset);
+ Statement* newStmt = gtNewStmt(src->AsOp()->gtOp1, usedDI);
fgInsertStmtAfter(block, *pAfterStmt, newStmt);
*pAfterStmt = newStmt;
}
else if (impLastStmt != nullptr)
{
// Do the side-effect as a separate statement.
- impAppendTree(src->AsOp()->gtOp1, curLevel, ilOffset);
+ impAppendTree(src->AsOp()->gtOp1, curLevel, usedDI);
}
else
{
@@ -1535,12 +1561,12 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr,
// in the importer where we can append the side effect.
// Instead, we're going to sink the assignment below the COMMA.
src->AsOp()->gtOp2 =
- impAssignStructPtr(destAddr, src->AsOp()->gtOp2, structHnd, curLevel, pAfterStmt, ilOffset, block);
+ impAssignStructPtr(destAddr, src->AsOp()->gtOp2, structHnd, curLevel, pAfterStmt, usedDI, block);
return src;
}
// Evaluate the second thing using recursion.
- return impAssignStructPtr(destAddr, src->AsOp()->gtOp2, structHnd, curLevel, pAfterStmt, ilOffset, block);
+ return impAssignStructPtr(destAddr, src->AsOp()->gtOp2, structHnd, curLevel, pAfterStmt, usedDI, block);
}
else if (src->IsLocal())
{
@@ -1689,7 +1715,7 @@ GenTree* Compiler::impGetStructAddr(GenTree* structVal,
beforeStmt = oldLastStmt->GetNextStmt();
}
- impInsertTreeBefore(structVal->AsOp()->gtOp1, impCurStmtOffs, beforeStmt);
+ impInsertTreeBefore(structVal->AsOp()->gtOp1, impCurStmtDI, beforeStmt);
structVal->AsOp()->gtOp1 = gtNewNothingNode();
}
@@ -1729,7 +1755,7 @@ var_types Compiler::impNormStructType(CORINFO_CLASS_HANDLE structHnd, CorInfoTyp
const DWORD structFlags = info.compCompHnd->getClassAttribs(structHnd);
// Don't bother if the struct contains GC references of byrefs, it can't be a SIMD type.
- if ((structFlags & (CORINFO_FLG_CONTAINS_GC_PTR | CORINFO_FLG_CONTAINS_STACK_PTR)) == 0)
+ if ((structFlags & (CORINFO_FLG_CONTAINS_GC_PTR | CORINFO_FLG_BYREF_LIKE)) == 0)
{
unsigned originalSize = info.compCompHnd->getClassSize(structHnd);
@@ -2321,7 +2347,7 @@ GenTree* Compiler::impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken
impSpillSideEffects(true, CHECK_SPILL_ALL DEBUGARG("bubbling QMark0"));
unsigned slotLclNum = lvaGrabTemp(true DEBUGARG("impRuntimeLookup test"));
- impAssignTempGen(slotLclNum, slotPtrTree, NO_CLASS_HANDLE, (unsigned)CHECK_SPILL_ALL, nullptr, impCurStmtOffs);
+ impAssignTempGen(slotLclNum, slotPtrTree, NO_CLASS_HANDLE, (unsigned)CHECK_SPILL_ALL, nullptr, impCurStmtDI);
GenTree* slot = gtNewLclvNode(slotLclNum, TYP_I_IMPL);
// downcast the pointer to a TYP_INT on 64-bit targets
@@ -2341,7 +2367,7 @@ GenTree* Compiler::impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken
GenTree* asg = gtNewAssignNode(slot, indir);
GenTreeColon* colon = new (this, GT_COLON) GenTreeColon(TYP_VOID, gtNewNothingNode(), asg);
GenTreeQmark* qmark = gtNewQmarkNode(TYP_VOID, relop, colon);
- impAppendTree(qmark, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs);
+ impAppendTree(qmark, (unsigned)CHECK_SPILL_NONE, impCurStmtDI);
return gtNewLclvNode(slotLclNum, TYP_I_IMPL);
}
@@ -2617,7 +2643,7 @@ inline void Compiler::impSpillSideEffects(bool spillGlobEffects, unsigned chkLev
assert(chkLevel <= verCurrentState.esStackDepth);
- unsigned spillFlags = spillGlobEffects ? GTF_GLOB_EFFECT : GTF_SIDE_EFFECT;
+ GenTreeFlags spillFlags = spillGlobEffects ? GTF_GLOB_EFFECT : GTF_SIDE_EFFECT;
for (unsigned i = 0; i < chkLevel; i++)
{
@@ -2822,8 +2848,11 @@ BasicBlock* Compiler::impPushCatchArgOnStack(BasicBlock* hndBlk, CORINFO_CLASS_H
{
// Report the debug info. impImportBlockCode won't treat the actual handler as exception block and thus
// won't do it for us.
- impCurStmtOffs = newBlk->bbCodeOffs | IL_OFFSETX_STKBIT;
- argStmt = gtNewStmt(argAsg, impCurStmtOffs);
+ // TODO-DEBUGINFO: Previous code always set stack as non-empty
+ // here. Can we not just use impCurStmtOffsSet? Are we out of sync
+ // here with the stack?
+ impCurStmtDI = DebugInfo(compInlineContext, ILLocation(newBlk->bbCodeOffs, false, false));
+ argStmt = gtNewStmt(argAsg, impCurStmtDI);
}
else
{
@@ -2872,48 +2901,56 @@ GenTree* Compiler::impCloneExpr(GenTree* tree,
// specialized type (e.g. a SIMD type). So we will get the type from
// the lclVar AFTER calling impAssignTempGen().
- impAssignTempGen(temp, tree, structHnd, curLevel, pAfterStmt, impCurStmtOffs);
+ impAssignTempGen(temp, tree, structHnd, curLevel, pAfterStmt, impCurStmtDI);
var_types type = genActualType(lvaTable[temp].TypeGet());
*pClone = gtNewLclvNode(temp, type);
return gtNewLclvNode(temp, type);
}
-/*****************************************************************************
- * Remember the IL offset (including stack-empty info) for the trees we will
- * generate now.
- */
-
-inline void Compiler::impCurStmtOffsSet(IL_OFFSET offs)
+//------------------------------------------------------------------------
+// impCreateDIWithCurrentStackInfo: Create a DebugInfo instance with the
+// specified IL offset and 'is call' bit, using the current stack to determine
+// whether to set the 'stack empty' bit.
+//
+// Arguments:
+// offs - the IL offset for the DebugInfo
+// isCall - whether the created DebugInfo should have the IsCall bit set
+//
+// Return Value:
+// The DebugInfo instance.
+//
+DebugInfo Compiler::impCreateDIWithCurrentStackInfo(IL_OFFSET offs, bool isCall)
{
- if (compIsForInlining())
- {
- Statement* callStmt = impInlineInfo->iciStmt;
- impCurStmtOffs = callStmt->GetILOffsetX();
- }
- else
- {
- assert(offs == BAD_IL_OFFSET || (offs & IL_OFFSETX_BITS) == 0);
- IL_OFFSETX stkBit = (verCurrentState.esStackDepth > 0) ? IL_OFFSETX_STKBIT : 0;
- impCurStmtOffs = offs | stkBit;
- }
+ assert(offs != BAD_IL_OFFSET);
+
+ bool isStackEmpty = verCurrentState.esStackDepth <= 0;
+ return DebugInfo(compInlineContext, ILLocation(offs, isStackEmpty, isCall));
}
-/*****************************************************************************
- * Returns current IL offset with stack-empty and call-instruction info incorporated
- */
-inline IL_OFFSETX Compiler::impCurILOffset(IL_OFFSET offs, bool callInstruction)
+//------------------------------------------------------------------------
+// impCurStmtOffsSet: Set the "current debug info" to attach to statements that
+// we are generating next.
+//
+// Arguments:
+// offs - the IL offset
+//
+// Remarks:
+// This function will be called in the main IL processing loop when it is
+// determined that we have reached a location in the IL stream for which we
+// want to report debug information. This is the main way we determine which
+// statements to report debug info for to the EE: for other statements, they
+// will have no debug information attached.
+//
+inline void Compiler::impCurStmtOffsSet(IL_OFFSET offs)
{
- if (compIsForInlining())
+ if (offs == BAD_IL_OFFSET)
{
- return BAD_IL_OFFSET;
+ impCurStmtDI = DebugInfo(compInlineContext, ILLocation());
}
else
{
- assert(offs == BAD_IL_OFFSET || (offs & IL_OFFSETX_BITS) == 0);
- IL_OFFSETX stkBit = (verCurrentState.esStackDepth > 0) ? IL_OFFSETX_STKBIT : 0;
- IL_OFFSETX callInstructionBit = callInstruction ? IL_OFFSETX_CALLINSTRUCTIONBIT : 0;
- return offs | stkBit | callInstructionBit;
+ impCurStmtDI = impCreateDIWithCurrentStackInfo(offs, false);
}
}
@@ -2978,7 +3015,7 @@ void Compiler::impNoteBranchOffs()
{
if (opts.compDbgCode)
{
- impAppendTree(gtNewNothingNode(), (unsigned)CHECK_SPILL_NONE, impCurStmtOffs);
+ impAppendTree(gtNewNothingNode(), (unsigned)CHECK_SPILL_NONE, impCurStmtDI);
}
}
@@ -2999,11 +3036,6 @@ unsigned Compiler::impInitBlockLineInfo()
impCurStmtOffsSet(BAD_IL_OFFSET);
- if (compIsForInlining())
- {
- return ~0;
- }
-
IL_OFFSET blockOffs = compCurBB->bbCodeOffs;
if ((verCurrentState.esStackDepth == 0) && (info.compStmtOffsetsImplicit & ICorDebugInfo::STACK_EMPTY_BOUNDARIES))
@@ -3011,11 +3043,6 @@ unsigned Compiler::impInitBlockLineInfo()
impCurStmtOffsSet(blockOffs);
}
- if (false && (info.compStmtOffsetsImplicit & ICorDebugInfo::CALL_SITE_BOUNDARIES))
- {
- impCurStmtOffsSet(blockOffs);
- }
-
/* Always report IL offset 0 or some tests get confused.
Probably a good idea anyways */
@@ -3624,7 +3651,6 @@ const char* Compiler::impGetIntrinsicName(CorInfoIntrinsics intrinsicID)
"CORINFO_INTRINSIC_Array_Get",
"CORINFO_INTRINSIC_Array_Address",
"CORINFO_INTRINSIC_Array_Set",
- "CORINFO_INTRINSIC_InitializeArray",
"CORINFO_INTRINSIC_RTH_GetValueInternal",
"CORINFO_INTRINSIC_Object_GetType",
"CORINFO_INTRINSIC_StubHelpers_GetStubContext",
@@ -3647,6 +3673,120 @@ const char* Compiler::impGetIntrinsicName(CorInfoIntrinsics intrinsicID)
#endif // DEBUG
+GenTree* Compiler::impCreateSpanIntrinsic(CORINFO_SIG_INFO* sig)
+{
+ assert(sig->numArgs == 1);
+ assert(sig->sigInst.methInstCount == 1);
+
+ GenTree* fieldTokenNode = impStackTop(0).val;
+
+ //
+ // Verify that the field token is known and valid. Note that it's also
+ // possible for the token to come from reflection, in which case we cannot do
+ // the optimization and must therefore revert to calling the helper. You can
+ // see an example of this in bvt\DynIL\initarray2.exe (in Main).
+ //
+
+ // Check to see if the ldtoken helper call is what we see here.
+ if (fieldTokenNode->gtOper != GT_CALL || (fieldTokenNode->AsCall()->gtCallType != CT_HELPER) ||
+ (fieldTokenNode->AsCall()->gtCallMethHnd != eeFindHelper(CORINFO_HELP_FIELDDESC_TO_STUBRUNTIMEFIELD)))
+ {
+ return nullptr;
+ }
+
+ // Strip helper call away
+ fieldTokenNode = fieldTokenNode->AsCall()->gtCallArgs->GetNode();
+ if (fieldTokenNode->gtOper == GT_IND)
+ {
+ fieldTokenNode = fieldTokenNode->AsOp()->gtOp1;
+ }
+
+ // Check for constant
+ if (fieldTokenNode->gtOper != GT_CNS_INT)
+ {
+ return nullptr;
+ }
+
+ CORINFO_FIELD_HANDLE fieldToken = (CORINFO_FIELD_HANDLE)fieldTokenNode->AsIntCon()->gtCompileTimeHandle;
+ if (!fieldTokenNode->IsIconHandle(GTF_ICON_FIELD_HDL) || (fieldToken == nullptr))
+ {
+ return nullptr;
+ }
+
+ CORINFO_CLASS_HANDLE fieldOwnerHnd = info.compCompHnd->getFieldClass(fieldToken);
+
+ CORINFO_CLASS_HANDLE fieldClsHnd;
+ var_types fieldElementType =
+ JITtype2varType(info.compCompHnd->getFieldType(fieldToken, &fieldClsHnd, fieldOwnerHnd));
+ unsigned totalFieldSize;
+
+ // Most static initialization data fields are of some structure, but it is possible for them to be of various
+ // primitive types as well
+ if (fieldElementType == var_types::TYP_STRUCT)
+ {
+ totalFieldSize = info.compCompHnd->getClassSize(fieldClsHnd);
+ }
+ else
+ {
+ totalFieldSize = genTypeSize(fieldElementType);
+ }
+
+ // Limit to primitive or enum type - see ArrayNative::GetSpanDataFrom()
+ CORINFO_CLASS_HANDLE targetElemHnd = sig->sigInst.methInst[0];
+ if (info.compCompHnd->getTypeForPrimitiveValueClass(targetElemHnd) == CORINFO_TYPE_UNDEF)
+ {
+ return nullptr;
+ }
+
+ const unsigned targetElemSize = info.compCompHnd->getClassSize(targetElemHnd);
+ assert(targetElemSize != 0);
+
+ const unsigned count = totalFieldSize / targetElemSize;
+ if (count == 0)
+ {
+ return nullptr;
+ }
+
+ void* data = info.compCompHnd->getArrayInitializationData(fieldToken, totalFieldSize);
+ if (!data)
+ {
+ return nullptr;
+ }
+
+ //
+ // Ready to commit to the work
+ //
+
+ impPopStack();
+
+ // Turn count and pointer value into constants.
+ GenTree* lengthValue = gtNewIconNode(count, TYP_INT);
+ GenTree* pointerValue = gtNewIconHandleNode((size_t)data, GTF_ICON_CONST_PTR);
+
+ // Construct ReadOnlySpan to return.
+ CORINFO_CLASS_HANDLE spanHnd = sig->retTypeClass;
+ unsigned spanTempNum = lvaGrabTemp(true DEBUGARG("ReadOnlySpan for CreateSpan"));
+ lvaSetStruct(spanTempNum, spanHnd, false);
+
+ CORINFO_FIELD_HANDLE pointerFieldHnd = info.compCompHnd->getFieldInClass(spanHnd, 0);
+ CORINFO_FIELD_HANDLE lengthFieldHnd = info.compCompHnd->getFieldInClass(spanHnd, 1);
+
+ GenTreeLclFld* pointerField = gtNewLclFldNode(spanTempNum, TYP_BYREF, 0);
+ pointerField->SetFieldSeq(GetFieldSeqStore()->CreateSingleton(pointerFieldHnd));
+ GenTree* pointerFieldAsg = gtNewAssignNode(pointerField, pointerValue);
+
+ GenTreeLclFld* lengthField = gtNewLclFldNode(spanTempNum, TYP_INT, TARGET_POINTER_SIZE);
+ lengthField->SetFieldSeq(GetFieldSeqStore()->CreateSingleton(lengthFieldHnd));
+ GenTree* lengthFieldAsg = gtNewAssignNode(lengthField, lengthValue);
+
+ // Now append a few statements the initialize the span
+ impAppendTree(lengthFieldAsg, (unsigned)CHECK_SPILL_NONE, impCurStmtDI);
+ impAppendTree(pointerFieldAsg, (unsigned)CHECK_SPILL_NONE, impCurStmtDI);
+
+ // And finally create a tree that points at the span.
+ return impCreateLocalNode(spanTempNum DEBUGARG(0));
+}
+
//------------------------------------------------------------------------
// impIntrinsic: possibly expand intrinsic call into alternate IR sequence
//
@@ -3806,6 +3946,14 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,
return new (this, GT_LABEL) GenTree(GT_LABEL, TYP_I_IMPL);
}
+ if (((ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_CreateSpan) ||
+ (ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_InitializeArray)) &&
+ IsTargetAbi(CORINFO_CORERT_ABI))
+ {
+ // CreateSpan must be expanded for NativeAOT
+ mustExpand = true;
+ }
+
GenTree* retNode = nullptr;
// Under debug and minopts, only expand what is required.
@@ -3828,10 +3976,6 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,
{
GenTree* op1;
- case CORINFO_INTRINSIC_InitializeArray:
- retNode = impInitializeArrayIntrinsic(sig);
- break;
-
case CORINFO_INTRINSIC_Array_Address:
case CORINFO_INTRINSIC_Array_Get:
case CORINFO_INTRINSIC_Array_Set:
@@ -4074,6 +4218,18 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,
break;
}
+ case NI_System_Runtime_CompilerServices_RuntimeHelpers_CreateSpan:
+ {
+ retNode = impCreateSpanIntrinsic(sig);
+ break;
+ }
+
+ case NI_System_Runtime_CompilerServices_RuntimeHelpers_InitializeArray:
+ {
+ retNode = impInitializeArrayIntrinsic(sig);
+ break;
+ }
+
case NI_System_Span_get_Item:
case NI_System_ReadOnlySpan_get_Item:
{
@@ -5190,6 +5346,18 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method)
result = SimdAsHWIntrinsicInfo::lookupId(&sig, className, methodName, enclosingClassName, sizeOfVectorT);
}
#endif // FEATURE_HW_INTRINSICS
+ else if ((strcmp(namespaceName, "System.Runtime.CompilerServices") == 0) &&
+ (strcmp(className, "RuntimeHelpers") == 0))
+ {
+ if (strcmp(methodName, "CreateSpan") == 0)
+ {
+ result = NI_System_Runtime_CompilerServices_RuntimeHelpers_CreateSpan;
+ }
+ else if (strcmp(methodName, "InitializeArray") == 0)
+ {
+ result = NI_System_Runtime_CompilerServices_RuntimeHelpers_InitializeArray;
+ }
+ }
else if (strncmp(namespaceName, "System.Runtime.Intrinsics", 25) == 0)
{
// We go down this path even when FEATURE_HW_INTRINSICS isn't enabled
@@ -5646,7 +5814,7 @@ void Compiler::verConvertBBToThrowVerificationException(BasicBlock* block DEBUGA
GenTree* op1 =
gtNewHelperCallNode(CORINFO_HELP_VERIFICATION, TYP_VOID, gtNewCallArgs(gtNewIconNode(block->bbCodeOffs)));
// verCurrentState.esStackDepth = 0;
- impAppendTree(op1, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs);
+ impAppendTree(op1, (unsigned)CHECK_SPILL_NONE, impCurStmtDI);
// The inliner is not able to handle methods that require throw block, so
// make sure this methods never gets inlined.
@@ -5871,7 +6039,7 @@ bool Compiler::verIsByRefLike(const typeInfo& ti)
{
return false;
}
- return info.compCompHnd->getClassAttribs(ti.GetClassHandleForValueClass()) & CORINFO_FLG_CONTAINS_STACK_PTR;
+ return info.compCompHnd->getClassAttribs(ti.GetClassHandleForValueClass()) & CORINFO_FLG_BYREF_LIKE;
}
bool Compiler::verIsSafeToReturnByRef(const typeInfo& ti)
@@ -5892,7 +6060,7 @@ bool Compiler::verIsBoxable(const typeInfo& ti)
|| ti.IsUnboxedGenericTypeVar() ||
(ti.IsType(TI_STRUCT) &&
// exclude byreflike structs
- !(info.compCompHnd->getClassAttribs(ti.GetClassHandleForValueClass()) & CORINFO_FLG_CONTAINS_STACK_PTR)));
+ !(info.compCompHnd->getClassAttribs(ti.GetClassHandleForValueClass()) & CORINFO_FLG_BYREF_LIKE)));
}
// Is it a boxed value type?
@@ -7056,7 +7224,7 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken)
// Assign the boxed object to the box temp.
//
GenTree* asg = gtNewTempAssign(impBoxTemp, op1);
- Statement* asgStmt = impAppendTree(asg, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs);
+ Statement* asgStmt = impAppendTree(asg, (unsigned)CHECK_SPILL_NONE, impCurStmtDI);
// If the exprToBox is a call that returns its value via a ret buf arg,
// move the assignment statement(s) before the call (which must be a top level tree).
@@ -7178,7 +7346,7 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken)
impSpillSideEffects(true, (unsigned)CHECK_SPILL_ALL DEBUGARG("impImportAndPushBox"));
// Set up this copy as a second assignment.
- Statement* copyStmt = impAppendTree(op1, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs);
+ Statement* copyStmt = impAppendTree(op1, (unsigned)CHECK_SPILL_NONE, impCurStmtDI);
op1 = gtNewLclvNode(impBoxTemp, TYP_REF);
@@ -7676,7 +7844,7 @@ void Compiler::impCheckForPInvokeCall(
}
}
-GenTreeCall* Compiler::impImportIndirectCall(CORINFO_SIG_INFO* sig, IL_OFFSETX ilOffset)
+GenTreeCall* Compiler::impImportIndirectCall(CORINFO_SIG_INFO* sig, const DebugInfo& di)
{
var_types callRetTyp = JITtype2varType(sig->retType);
@@ -7714,7 +7882,7 @@ GenTreeCall* Compiler::impImportIndirectCall(CORINFO_SIG_INFO* sig, IL_OFFSETX i
/* Create the call node */
- GenTreeCall* call = gtNewIndCallNode(fptr, callRetTyp, nullptr, ilOffset);
+ GenTreeCall* call = gtNewIndCallNode(fptr, callRetTyp, nullptr, di);
call->gtFlags |= GTF_EXCEPT | (fptr->gtFlags & GTF_GLOB_EFFECT);
#ifdef UNIX_X86_ABI
@@ -8065,7 +8233,7 @@ GenTree* Compiler::impImportStaticFieldAccess(CORINFO_RESOLVED_TOKEN* pResolvedT
{
FieldSeqNode* fs = GetFieldSeqStore()->CreateSingleton(pResolvedToken->hField);
op1 = gtNewOperNode(GT_ADD, op1->TypeGet(), op1,
- new (this, GT_CNS_INT) GenTreeIntCon(TYP_INT, pFieldInfo->offset, fs));
+ new (this, GT_CNS_INT) GenTreeIntCon(TYP_I_IMPL, pFieldInfo->offset, fs));
}
break;
}
@@ -8267,7 +8435,7 @@ void Compiler::impInsertHelperCall(CORINFO_HELPER_DESC* helperInfo)
* Also, consider sticking this in the first basic block.
*/
GenTree* callout = gtNewHelperCallNode(helperInfo->helperNum, TYP_VOID, args);
- impAppendTree(callout, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs);
+ impAppendTree(callout, (unsigned)CHECK_SPILL_NONE, impCurStmtDI);
}
//------------------------------------------------------------------------
@@ -8454,7 +8622,7 @@ bool Compiler::impIsImplicitTailCallCandidate(
// newObjThis - tree for this pointer or uninitalized newobj temp (or nullptr)
// prefixFlags - IL prefix flags for the call
// callInfo - EE supplied info for the call
-// rawILOffset - IL offset of the opcode
+// rawILOffset - IL offset of the opcode, used for guarded devirtualization.
//
// Returns:
// Type of the call's return value.
@@ -8483,7 +8651,11 @@ var_types Compiler::impImportCall(OPCODE opcode,
{
assert(opcode == CEE_CALL || opcode == CEE_CALLVIRT || opcode == CEE_NEWOBJ || opcode == CEE_CALLI);
- IL_OFFSETX ilOffset = impCurILOffset(rawILOffset, true);
+ // The current statement DI may not refer to the exact call, but for calls
+ // we wish to be able to attach the exact IL instruction to get "return
+ // value" support in the debugger, so create one with the exact IL offset.
+ DebugInfo di = impCreateDIWithCurrentStackInfo(rawILOffset, true);
+
var_types callRetTyp = TYP_COUNT;
CORINFO_SIG_INFO* sig = nullptr;
CORINFO_METHOD_HANDLE methHnd = nullptr;
@@ -8566,7 +8738,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
callRetTyp = JITtype2varType(calliSig.retType);
- call = impImportIndirectCall(&calliSig, ilOffset);
+ call = impImportIndirectCall(&calliSig, di);
// We don't know the target method, so we have to infer the flags, or
// assume the worst-case.
@@ -8791,7 +8963,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
else
{
// The stub address is known at compile time
- call = gtNewCallNode(CT_USER_FUNC, callInfo->hMethod, callRetTyp, nullptr, ilOffset);
+ call = gtNewCallNode(CT_USER_FUNC, callInfo->hMethod, callRetTyp, nullptr, di);
call->AsCall()->gtStubCallStubAddr = callInfo->stubLookup.constLookup.addr;
call->gtFlags |= GTF_CALL_VIRT_STUB;
assert(callInfo->stubLookup.constLookup.accessType != IAT_PPVALUE &&
@@ -8821,7 +8993,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
{
assert(!(mflags & CORINFO_FLG_STATIC)); // can't call a static method
assert(!(clsFlags & CORINFO_FLG_VALUECLASS));
- call = gtNewCallNode(CT_USER_FUNC, callInfo->hMethod, callRetTyp, nullptr, ilOffset);
+ call = gtNewCallNode(CT_USER_FUNC, callInfo->hMethod, callRetTyp, nullptr, di);
call->gtFlags |= GTF_CALL_VIRT_VTABLE;
// Should we expand virtual call targets early for this method?
@@ -8871,7 +9043,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
// Create the actual call node
- call = gtNewIndCallNode(fptr, callRetTyp, args, ilOffset);
+ call = gtNewIndCallNode(fptr, callRetTyp, args, di);
call->AsCall()->gtCallThisArg = gtNewCallArgs(thisPtrCopy);
call->gtFlags |= GTF_EXCEPT | (fptr->gtFlags & GTF_GLOB_EFFECT);
@@ -8898,7 +9070,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
case CORINFO_CALL:
{
// This is for a non-virtual, non-interface etc. call
- call = gtNewCallNode(CT_USER_FUNC, callInfo->hMethod, callRetTyp, nullptr, ilOffset);
+ call = gtNewCallNode(CT_USER_FUNC, callInfo->hMethod, callRetTyp, nullptr, di);
// We remove the nullcheck for the GetType call intrinsic.
// TODO-CQ: JIT64 does not introduce the null check for many more helper calls
@@ -8943,7 +9115,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
impAssignTempGen(lclNum, fptr, (unsigned)CHECK_SPILL_ALL);
fptr = gtNewLclvNode(lclNum, TYP_I_IMPL);
- call = gtNewIndCallNode(fptr, callRetTyp, nullptr, ilOffset);
+ call = gtNewIndCallNode(fptr, callRetTyp, nullptr, di);
call->gtFlags |= GTF_EXCEPT | (fptr->gtFlags & GTF_GLOB_EFFECT);
if (callInfo->nullInstanceCheck)
{
@@ -9384,6 +9556,8 @@ var_types Compiler::impImportCall(OPCODE opcode,
const bool isLateDevirtualization = false;
impDevirtualizeCall(call->AsCall(), pResolvedToken, &callInfo->hMethod, &callInfo->methodFlags,
&callInfo->contextHandle, &exactContextHnd, isLateDevirtualization, isExplicitTailCall,
+ // Take care to pass raw IL offset here as the 'debug info' might be different for
+ // inlinees.
rawILOffset);
}
@@ -9436,7 +9610,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
}
// append the call node.
- impAppendTree(call, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs);
+ impAppendTree(call, (unsigned)CHECK_SPILL_ALL, impCurStmtDI);
// Now push the value of the 'new onto the stack
@@ -9702,11 +9876,11 @@ var_types Compiler::impImportCall(OPCODE opcode,
{
// we actually did push something, so don't spill the thing we just pushed.
assert(verCurrentState.esStackDepth > 0);
- impAppendTree(call, verCurrentState.esStackDepth - 1, impCurStmtOffs);
+ impAppendTree(call, verCurrentState.esStackDepth - 1, impCurStmtDI);
}
else
{
- impAppendTree(call, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs);
+ impAppendTree(call, (unsigned)CHECK_SPILL_ALL, impCurStmtDI);
}
}
else
@@ -9774,7 +9948,11 @@ var_types Compiler::impImportCall(OPCODE opcode,
assert(!isFatPointerCandidate); // We should not try to inline calli.
// Make the call its own tree (spill the stack if needed).
- impAppendTree(call, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs);
+ // Do not consume the debug info here. This is particularly
+ // important if we give up on the inline, in which case the
+ // call will typically end up in the statement that contains
+ // the GT_RET_EXPR that we leave on the stack.
+ impAppendTree(call, (unsigned)CHECK_SPILL_ALL, impCurStmtDI, false);
// TODO: Still using the widened type.
GenTree* retExpr = gtNewInlineCandidateReturnExpr(call, genActualType(callRetTyp), compCurBB->bbFlags);
@@ -9797,7 +9975,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
if (call->OperGet() != GT_LCL_VAR) // can be already converted by impFixupCallStructReturn.
{
unsigned calliSlot = lvaGrabTemp(true DEBUGARG("calli"));
- LclVarDsc* varDsc = &lvaTable[calliSlot];
+ LclVarDsc* varDsc = lvaGetDesc(calliSlot);
varDsc->lvVerTypeInfo = tiRetVal;
impAssignTempGen(calliSlot, call, tiRetVal.GetClassHandle(), (unsigned)CHECK_SPILL_NONE);
// impAssignTempGen can change src arg list and return type for call that returns struct.
@@ -10299,7 +10477,7 @@ void Compiler::impImportLeave(BasicBlock* block)
callBlock->bbJumpKind = BBJ_CALLFINALLY; // convert the BBJ_LEAVE to BBJ_CALLFINALLY
if (endCatches)
- impAppendTree(endCatches, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs);
+ impAppendTree(endCatches, (unsigned)CHECK_SPILL_NONE, impCurStmtDI);
#ifdef DEBUG
if (verbose)
@@ -10386,7 +10564,7 @@ void Compiler::impImportLeave(BasicBlock* block)
block->bbJumpKind = BBJ_ALWAYS; // convert the BBJ_LEAVE to a BBJ_ALWAYS
if (endCatches)
- impAppendTree(endCatches, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs);
+ impAppendTree(endCatches, (unsigned)CHECK_SPILL_NONE, impCurStmtDI);
#ifdef DEBUG
if (verbose)
@@ -11580,21 +11758,38 @@ void Compiler::impImportBlockCode(BasicBlock* block)
#ifdef FEATURE_ON_STACK_REPLACEMENT
// Are there any places in the method where we might add a patchpoint?
+ //
if (compHasBackwardJump)
{
- // Are patchpoints enabled?
+ // Is OSR enabled?
+ //
if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0) && (JitConfig.TC_OnStackReplacement() > 0))
{
- // We don't inline at Tier0, if we do, we may need rethink our approach.
- // Could probably support inlines that don't introduce flow.
- assert(!compIsForInlining());
-
- // Is the start of this block a suitable patchpoint?
- // Current strategy is blocks that are stack-empty and backwards branch targets
- if (block->bbFlags & BBF_BACKWARD_JUMP_TARGET && (verCurrentState.esStackDepth == 0))
+ // OSR is not yet supported for methods with explicit tail calls.
+ //
+ // But we also may not switch methods to be optimized as we should be
+ // able to avoid getting trapped in Tier0 code by normal call counting.
+ // So instead, just suppress adding patchpoints.
+ //
+ if (!compTailPrefixSeen)
{
- block->bbFlags |= BBF_PATCHPOINT;
- setMethodHasPatchpoint();
+ assert(compCanHavePatchpoints());
+
+ // We don't inline at Tier0, if we do, we may need rethink our approach.
+ // Could probably support inlines that don't introduce flow.
+ assert(!compIsForInlining());
+
+ // Is the start of this block a suitable patchpoint?
+ //
+ if (((block->bbFlags & BBF_BACKWARD_JUMP_TARGET) != 0) && (verCurrentState.esStackDepth == 0))
+ {
+ // We should have noted this earlier and bailed out of OSR.
+ //
+ assert(!block->hasHndIndex());
+
+ block->bbFlags |= BBF_PATCHPOINT;
+ setMethodHasPatchpoint();
+ }
}
}
}
@@ -11614,14 +11809,37 @@ void Compiler::impImportBlockCode(BasicBlock* block)
// propagate rareness back through flow and place the partial compilation patchpoints "earlier"
// so there are fewer overall.
//
+ // Note unlike OSR, it's ok to forgo these.
+ //
// Todo: stress mode...
//
- if ((JitConfig.TC_PartialCompilation() > 0) && opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0) &&
- (block != fgFirstBB) && block->isRunRarely() && (verCurrentState.esStackDepth == 0) &&
- ((block->bbFlags & BBF_PATCHPOINT) == 0))
+ if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0) && (JitConfig.TC_PartialCompilation() > 0) &&
+ compCanHavePatchpoints() && !compTailPrefixSeen)
{
- block->bbFlags |= BBF_PARTIAL_COMPILATION_PATCHPOINT;
- setMethodHasPartialCompilationPatchpoint();
+ // Is this block a good place for partial compilation?
+ //
+ if ((block != fgFirstBB) && block->isRunRarely() && (verCurrentState.esStackDepth == 0) &&
+ ((block->bbFlags & BBF_PATCHPOINT) == 0) && !block->hasHndIndex())
+ {
+ JITDUMP("\nBlock " FMT_BB " will be a partial compilation patchpoint -- not importing\n", block->bbNum);
+ block->bbFlags |= BBF_PARTIAL_COMPILATION_PATCHPOINT;
+ setMethodHasPartialCompilationPatchpoint();
+
+ // Change block to BBJ_THROW so we won't trigger importation of successors.
+ //
+ block->bbJumpKind = BBJ_THROW;
+
+ // If this method has a explicit generic context, the only uses of it may be in
+ // the IL for this block. So assume it's used.
+ //
+ if (info.compMethodInfo->options &
+ (CORINFO_GENERICS_CTXT_FROM_METHODDESC | CORINFO_GENERICS_CTXT_FROM_METHODTABLE))
+ {
+ lvaGenericsContextInUse = true;
+ }
+
+ return;
+ }
}
#endif // FEATURE_ON_STACK_REPLACEMENT
@@ -11705,111 +11923,108 @@ void Compiler::impImportBlockCode(BasicBlock* block)
if (opts.compDbgInfo)
#endif
{
- if (!compIsForInlining())
- {
- nxtStmtOffs =
- (nxtStmtIndex < info.compStmtOffsetsCount) ? info.compStmtOffsets[nxtStmtIndex] : BAD_IL_OFFSET;
+ nxtStmtOffs =
+ (nxtStmtIndex < info.compStmtOffsetsCount) ? info.compStmtOffsets[nxtStmtIndex] : BAD_IL_OFFSET;
+
+ /* Have we reached the next stmt boundary ? */
- /* Have we reached the next stmt boundary ? */
+ if (nxtStmtOffs != BAD_IL_OFFSET && opcodeOffs >= nxtStmtOffs)
+ {
+ assert(nxtStmtOffs == info.compStmtOffsets[nxtStmtIndex]);
- if (nxtStmtOffs != BAD_IL_OFFSET && opcodeOffs >= nxtStmtOffs)
+ if (verCurrentState.esStackDepth != 0 && opts.compDbgCode)
{
- assert(nxtStmtOffs == info.compStmtOffsets[nxtStmtIndex]);
+ /* We need to provide accurate IP-mapping at this point.
+ So spill anything on the stack so that it will form
+ gtStmts with the correct stmt offset noted */
- if (verCurrentState.esStackDepth != 0 && opts.compDbgCode)
- {
- /* We need to provide accurate IP-mapping at this point.
- So spill anything on the stack so that it will form
- gtStmts with the correct stmt offset noted */
+ impSpillStackEnsure(true);
+ }
- impSpillStackEnsure(true);
- }
+ // Have we reported debug info for any tree?
- // Has impCurStmtOffs been reported in any tree?
+ if (impCurStmtDI.IsValid() && opts.compDbgCode)
+ {
+ GenTree* placeHolder = new (this, GT_NO_OP) GenTree(GT_NO_OP, TYP_VOID);
+ impAppendTree(placeHolder, (unsigned)CHECK_SPILL_NONE, impCurStmtDI);
- if (impCurStmtOffs != BAD_IL_OFFSET && opts.compDbgCode)
- {
- GenTree* placeHolder = new (this, GT_NO_OP) GenTree(GT_NO_OP, TYP_VOID);
- impAppendTree(placeHolder, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs);
+ assert(!impCurStmtDI.IsValid());
+ }
- assert(impCurStmtOffs == BAD_IL_OFFSET);
- }
+ if (!impCurStmtDI.IsValid())
+ {
+ /* Make sure that nxtStmtIndex is in sync with opcodeOffs.
+ If opcodeOffs has gone past nxtStmtIndex, catch up */
- if (impCurStmtOffs == BAD_IL_OFFSET)
+ while ((nxtStmtIndex + 1) < info.compStmtOffsetsCount &&
+ info.compStmtOffsets[nxtStmtIndex + 1] <= opcodeOffs)
{
- /* Make sure that nxtStmtIndex is in sync with opcodeOffs.
- If opcodeOffs has gone past nxtStmtIndex, catch up */
-
- while ((nxtStmtIndex + 1) < info.compStmtOffsetsCount &&
- info.compStmtOffsets[nxtStmtIndex + 1] <= opcodeOffs)
- {
- nxtStmtIndex++;
- }
+ nxtStmtIndex++;
+ }
- /* Go to the new stmt */
+ /* Go to the new stmt */
- impCurStmtOffsSet(info.compStmtOffsets[nxtStmtIndex]);
+ impCurStmtOffsSet(info.compStmtOffsets[nxtStmtIndex]);
- /* Update the stmt boundary index */
+ /* Update the stmt boundary index */
- nxtStmtIndex++;
- assert(nxtStmtIndex <= info.compStmtOffsetsCount);
+ nxtStmtIndex++;
+ assert(nxtStmtIndex <= info.compStmtOffsetsCount);
- /* Are there any more line# entries after this one? */
+ /* Are there any more line# entries after this one? */
- if (nxtStmtIndex < info.compStmtOffsetsCount)
- {
- /* Remember where the next line# starts */
+ if (nxtStmtIndex < info.compStmtOffsetsCount)
+ {
+ /* Remember where the next line# starts */
- nxtStmtOffs = info.compStmtOffsets[nxtStmtIndex];
- }
- else
- {
- /* No more line# entries */
+ nxtStmtOffs = info.compStmtOffsets[nxtStmtIndex];
+ }
+ else
+ {
+ /* No more line# entries */
- nxtStmtOffs = BAD_IL_OFFSET;
- }
+ nxtStmtOffs = BAD_IL_OFFSET;
}
}
- else if ((info.compStmtOffsetsImplicit & ICorDebugInfo::STACK_EMPTY_BOUNDARIES) &&
- (verCurrentState.esStackDepth == 0))
- {
- /* At stack-empty locations, we have already added the tree to
- the stmt list with the last offset. We just need to update
- impCurStmtOffs
- */
+ }
+ else if ((info.compStmtOffsetsImplicit & ICorDebugInfo::STACK_EMPTY_BOUNDARIES) &&
+ (verCurrentState.esStackDepth == 0))
+ {
+ /* At stack-empty locations, we have already added the tree to
+ the stmt list with the last offset. We just need to update
+ impCurStmtDI
+ */
+
+ impCurStmtOffsSet(opcodeOffs);
+ }
+ else if ((info.compStmtOffsetsImplicit & ICorDebugInfo::CALL_SITE_BOUNDARIES) &&
+ impOpcodeIsCallSiteBoundary(prevOpcode))
+ {
+ /* Make sure we have a type cached */
+ assert(callTyp != TYP_COUNT);
+ if (callTyp == TYP_VOID)
+ {
impCurStmtOffsSet(opcodeOffs);
}
- else if ((info.compStmtOffsetsImplicit & ICorDebugInfo::CALL_SITE_BOUNDARIES) &&
- impOpcodeIsCallSiteBoundary(prevOpcode))
+ else if (opts.compDbgCode)
{
- /* Make sure we have a type cached */
- assert(callTyp != TYP_COUNT);
-
- if (callTyp == TYP_VOID)
- {
- impCurStmtOffsSet(opcodeOffs);
- }
- else if (opts.compDbgCode)
- {
- impSpillStackEnsure(true);
- impCurStmtOffsSet(opcodeOffs);
- }
+ impSpillStackEnsure(true);
+ impCurStmtOffsSet(opcodeOffs);
}
- else if ((info.compStmtOffsetsImplicit & ICorDebugInfo::NOP_BOUNDARIES) && (prevOpcode == CEE_NOP))
+ }
+ else if ((info.compStmtOffsetsImplicit & ICorDebugInfo::NOP_BOUNDARIES) && (prevOpcode == CEE_NOP))
+ {
+ if (opts.compDbgCode)
{
- if (opts.compDbgCode)
- {
- impSpillStackEnsure(true);
- }
-
- impCurStmtOffsSet(opcodeOffs);
+ impSpillStackEnsure(true);
}
- assert(impCurStmtOffs == BAD_IL_OFFSET || nxtStmtOffs == BAD_IL_OFFSET ||
- jitGetILoffs(impCurStmtOffs) <= nxtStmtOffs);
+ impCurStmtOffsSet(opcodeOffs);
}
+
+ assert(!impCurStmtDI.IsValid() || (nxtStmtOffs == BAD_IL_OFFSET) ||
+ (impCurStmtDI.GetLocation().GetOffset() <= nxtStmtOffs));
}
CORINFO_CLASS_HANDLE clsHnd = DUMMY_INIT(NULL);
@@ -11937,14 +12152,14 @@ void Compiler::impImportBlockCode(BasicBlock* block)
}
/* Append 'op1' to the list of statements */
- impAppendTree(op1, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs);
+ impAppendTree(op1, (unsigned)CHECK_SPILL_ALL, impCurStmtDI);
goto DONE_APPEND;
APPEND:
/* Append 'op1' to the list of statements */
- impAppendTree(op1, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs);
+ impAppendTree(op1, (unsigned)CHECK_SPILL_NONE, impCurStmtDI);
goto DONE_APPEND;
DONE_APPEND:
@@ -12786,8 +13001,6 @@ void Compiler::impImportBlockCode(BasicBlock* block)
}
}
- op1 = impCheckForNullPointer(op1);
-
/* Mark the block as containing an index expression */
if (op1->gtOper == GT_LCL_VAR)
@@ -13001,8 +13214,6 @@ void Compiler::impImportBlockCode(BasicBlock* block)
op2->gtType = TYP_I_IMPL;
}
- op3 = impCheckForNullPointer(op3);
-
// Mark the block as containing an index expression
if (op3->gtOper == GT_LCL_VAR)
@@ -13464,7 +13675,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
/* GT_JTRUE is handled specially for non-empty stacks. See 'addStmt'
in impImportBlock(block). For correct line numbers, spill stack. */
- if (opts.compDbgCode && impCurStmtOffs != BAD_IL_OFFSET)
+ if (opts.compDbgCode && impCurStmtDI.IsValid())
{
impSpillStackEnsure(true);
}
@@ -13634,13 +13845,13 @@ void Compiler::impImportBlockCode(BasicBlock* block)
{
impSpillSideEffects(false, (unsigned)CHECK_SPILL_ALL DEBUGARG(
"Branch to next Optimization, op1 side effect"));
- impAppendTree(gtUnusedValNode(op1), (unsigned)CHECK_SPILL_NONE, impCurStmtOffs);
+ impAppendTree(gtUnusedValNode(op1), (unsigned)CHECK_SPILL_NONE, impCurStmtDI);
}
if (op2->gtFlags & GTF_GLOB_EFFECT)
{
impSpillSideEffects(false, (unsigned)CHECK_SPILL_ALL DEBUGARG(
"Branch to next Optimization, op2 side effect"));
- impAppendTree(gtUnusedValNode(op2), (unsigned)CHECK_SPILL_NONE, impCurStmtOffs);
+ impAppendTree(gtUnusedValNode(op2), (unsigned)CHECK_SPILL_NONE, impCurStmtDI);
}
#ifdef DEBUG
@@ -14525,7 +14736,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
if (op1->gtFlags & GTF_SIDE_EFFECT)
{
op1 = gtUnusedValNode(op1);
- impAppendTree(op1, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs);
+ impAppendTree(op1, (unsigned)CHECK_SPILL_ALL, impCurStmtDI);
}
goto DO_LDFTN;
}
@@ -14535,7 +14746,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
if (op1->gtFlags & GTF_SIDE_EFFECT)
{
op1 = gtUnusedValNode(op1);
- impAppendTree(op1, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs);
+ impAppendTree(op1, (unsigned)CHECK_SPILL_ALL, impCurStmtDI);
}
goto DO_LDFTN;
}
@@ -14668,7 +14879,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
info.compCompHnd->getChildType(resolvedToken.hClass, &elemTypeHnd);
assert(!(elemTypeHnd == nullptr && corType == CORINFO_TYPE_VALUECLASS));
Verify(elemTypeHnd == nullptr ||
- !(info.compCompHnd->getClassAttribs(elemTypeHnd) & CORINFO_FLG_CONTAINS_STACK_PTR),
+ !(info.compCompHnd->getClassAttribs(elemTypeHnd) & CORINFO_FLG_BYREF_LIKE),
"newarr of byref-like objects");
verVerifyCall(opcode, &resolvedToken, nullptr, ((prefixFlags & PREFIX_TAILCALL_EXPLICIT) != 0),
((prefixFlags & PREFIX_READONLY) != 0), delegateCreateStart, codeAddr - 1,
@@ -14787,7 +14998,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
gtNewIconNode(0), // Value
false, // isVolatile
false); // not copyBlock
- impAppendTree(newObjThisPtr, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs);
+ impAppendTree(newObjThisPtr, (unsigned)CHECK_SPILL_NONE, impCurStmtDI);
}
else
{
@@ -15192,7 +15403,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
if (obj->gtFlags & GTF_SIDE_EFFECT)
{
obj = gtUnusedValNode(obj);
- impAppendTree(obj, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs);
+ impAppendTree(obj, (unsigned)CHECK_SPILL_ALL, impCurStmtDI);
}
obj = nullptr;
}
@@ -15212,8 +15423,6 @@ void Compiler::impImportBlockCode(BasicBlock* block)
case CORINFO_FIELD_INSTANCE_WITH_BASE:
#endif
{
- obj = impCheckForNullPointer(obj);
-
// If the object is a struct, what we really want is
// for the field to operate on the address of the struct.
if (!varTypeGCtype(obj->TypeGet()) && impIsValueType(tiObj))
@@ -15524,7 +15733,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
if (obj->gtFlags & GTF_SIDE_EFFECT)
{
obj = gtUnusedValNode(obj);
- impAppendTree(obj, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs);
+ impAppendTree(obj, (unsigned)CHECK_SPILL_ALL, impCurStmtDI);
}
obj = nullptr;
}
@@ -15542,8 +15751,6 @@ void Compiler::impImportBlockCode(BasicBlock* block)
case CORINFO_FIELD_INSTANCE_WITH_BASE:
#endif
{
- obj = impCheckForNullPointer(obj);
-
/* Create the data member node */
op1 = gtNewFieldRef(lclTyp, resolvedToken.hField, obj, fieldInfo.offset);
DWORD typeFlags = info.compCompHnd->getClassAttribs(resolvedToken.hClass);
@@ -15790,7 +15997,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
CORINFO_CLASS_HANDLE elemTypeHnd;
info.compCompHnd->getChildType(resolvedToken.hClass, &elemTypeHnd);
Verify(elemTypeHnd == nullptr ||
- !(info.compCompHnd->getClassAttribs(elemTypeHnd) & CORINFO_FLG_CONTAINS_STACK_PTR),
+ !(info.compCompHnd->getClassAttribs(elemTypeHnd) & CORINFO_FLG_BYREF_LIKE),
"array of byref-like type");
}
@@ -16128,7 +16335,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
// The pointer may have side-effects
if (op1->AsOp()->gtOp1->gtFlags & GTF_SIDE_EFFECT)
{
- impAppendTree(op1->AsOp()->gtOp1, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs);
+ impAppendTree(op1->AsOp()->gtOp1, (unsigned)CHECK_SPILL_ALL, impCurStmtDI);
#ifdef DEBUG
impNoteLastILoffs();
#endif
@@ -16356,7 +16563,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
// may be other trees on the evaluation stack that side-effect the
// sources of the UNBOX operation we must spill the stack.
- impAppendTree(op1, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs);
+ impAppendTree(op1, (unsigned)CHECK_SPILL_ALL, impCurStmtDI);
// Create the address-expression to reference past the object header
// to the beginning of the value-type. Today this means adjusting
@@ -17725,10 +17932,11 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode)
else if (info.compRetBuffArg != BAD_VAR_NUM)
{
// Assign value to return buff (first param)
- GenTree* retBuffAddr = gtNewLclvNode(info.compRetBuffArg, TYP_BYREF DEBUGARG(impCurStmtOffs));
+ GenTree* retBuffAddr =
+ gtNewLclvNode(info.compRetBuffArg, TYP_BYREF DEBUGARG(impCurStmtDI.GetLocation().GetOffset()));
op2 = impAssignStructPtr(retBuffAddr, op2, retClsHnd, (unsigned)CHECK_SPILL_ALL);
- impAppendTree(op2, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs);
+ impAppendTree(op2, (unsigned)CHECK_SPILL_NONE, impCurStmtDI);
// There are cases where the address of the implicit RetBuf should be returned explicitly (in RAX).
CLANG_FORMAT_COMMENT_ANCHOR;
@@ -17800,7 +18008,7 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode)
}
}
- impAppendTree(op1, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs);
+ impAppendTree(op1, (unsigned)CHECK_SPILL_NONE, impCurStmtDI);
#ifdef DEBUG
// Remember at which BC offset the tree was finished
impNoteLastILoffs();
@@ -18799,10 +19007,7 @@ void Compiler::impRetypeEntryStateTemps(BasicBlock* blk)
GenTree* tree = es->esStack[level].val;
if ((tree->gtOper == GT_LCL_VAR) || (tree->gtOper == GT_LCL_FLD))
{
- unsigned lclNum = tree->AsLclVarCommon()->GetLclNum();
- noway_assert(lclNum < lvaCount);
- LclVarDsc* varDsc = lvaTable + lclNum;
- es->esStack[level].val->gtType = varDsc->TypeGet();
+ es->esStack[level].val->gtType = lvaGetDesc(tree->AsLclVarCommon())->TypeGet();
}
}
}
@@ -18828,7 +19033,7 @@ unsigned Compiler::impGetSpillTmpBase(BasicBlock* block)
SetSpillTempsBase callback(baseTmp);
// We do *NOT* need to reset the SpillClique*Members because a block can only be the predecessor
- // to one spill clique, and similarly can only be the sucessor to one spill clique
+ // to one spill clique, and similarly can only be the successor to one spill clique
impWalkSpillCliqueFromPred(block, &callback);
return baseTmp;
@@ -19766,6 +19971,7 @@ void Compiler::impCheckCanInline(GenTreeCall* call,
pInfo->initClassResult = initClassResult;
pInfo->fncRetType = fncRetType;
pInfo->exactContextNeedsRuntimeLookup = false;
+ pInfo->inlinersContext = pParam->pThis->compInlineContext;
// Note exactContextNeedsRuntimeLookup is reset later on,
// over in impMarkInlineCandidate.
@@ -20611,7 +20817,7 @@ GenTree* Compiler::impInlineFetchArg(unsigned lclNum, InlArgInfo* inlArgInfo, In
// Enable for all parameterless (=invariant) hw intrinsics such as
// Vector128<>.Zero and Vector256<>.AllBitSets. We might consider
// doing that for Vector.Create(cns) as well.
- if ((argNode->gtGetOp1() == nullptr) && (argNode->gtGetOp2() == nullptr))
+ if (argNode->AsHWIntrinsic()->GetOperandCount() == 0)
{
substitute = true;
}
@@ -20732,7 +20938,7 @@ bool Compiler::impInlineIsGuaranteedThisDerefBeforeAnySideEffects(GenTree*
for (unsigned level = 0; level < verCurrentState.esStackDepth; level++)
{
- unsigned stackTreeFlags = verCurrentState.esStack[level].val->gtFlags;
+ GenTreeFlags stackTreeFlags = verCurrentState.esStack[level].val->gtFlags;
if (GTF_GLOBALLY_VISIBLE_SIDE_EFFECTS(stackTreeFlags))
{
return false;
@@ -20874,6 +21080,14 @@ void Compiler::impMarkInlineCandidateHelper(GenTreeCall* call,
return;
}
+ // Delegate Invoke method doesn't have a body and gets special cased instead.
+ // Don't even bother trying to inline it.
+ if (call->IsDelegateInvoke())
+ {
+ inlineResult.NoteFatal(InlineObservation::CALLEE_HAS_NO_BODY);
+ return;
+ }
+
// Tail recursion elimination takes precedence over inlining.
// TODO: We may want to do some of the additional checks from fgMorphCall
// here to reduce the chance we don't inline a call that won't be optimized
@@ -21233,7 +21447,7 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
CORINFO_CONTEXT_HANDLE* pExactContextHandle,
bool isLateDevirtualization,
bool isExplicitTailCall,
- IL_OFFSETX ilOffset)
+ IL_OFFSET ilOffset)
{
assert(call != nullptr);
assert(method != nullptr);
@@ -21244,10 +21458,19 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
//
assert(call->IsVirtual());
- // Possibly instrument, if not optimizing.
+ // Possibly instrument. Note for OSR+PGO we will instrument when
+ // optimizing and (currently) won't devirtualize. We may want
+ // to revisit -- if we can devirtualize we should be able to
+ // suppress the probe.
//
- if (opts.OptimizationDisabled())
+ // We strip BBINSTR from inlinees currently, so we'll only
+ // do this for the root method calls.
+ //
+ if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_BBINSTR))
{
+ assert(opts.OptimizationDisabled() || opts.IsOSR());
+ assert(!compIsForInlining());
+
// During importation, optionally flag this block as one that
// contains calls requiring class profiling. Ideally perhaps
// we'd just keep track of the calls themselves, so we don't
@@ -21277,7 +21500,12 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call,
//
compCurBB->bbFlags |= BBF_HAS_CLASS_PROFILE;
}
+ return;
+ }
+ // Bail if optimizations are disabled.
+ if (opts.OptimizationDisabled())
+ {
return;
}
@@ -22123,7 +22351,7 @@ void Compiler::addFatPointerCandidate(GenTreeCall* call)
// Arguments:
//
// call - potential guarded devirtualization candidate
-// ilOffset - IL offset of the call instruction
+// ilOffset - IL ofset of the call instruction
// isInterface - true if this is an interface call
// baseMethod - target method of the call
// baseClass - class that introduced the target method
@@ -22137,7 +22365,7 @@ void Compiler::addFatPointerCandidate(GenTreeCall* call)
//
void Compiler::considerGuardedDevirtualization(
GenTreeCall* call,
- IL_OFFSETX ilOffset,
+ IL_OFFSET ilOffset,
bool isInterface,
CORINFO_METHOD_HANDLE baseMethod,
CORINFO_CLASS_HANDLE baseClass,
@@ -22230,6 +22458,17 @@ void Compiler::considerGuardedDevirtualization(
return;
}
+ uint32_t const likelyClassAttribs = info.compCompHnd->getClassAttribs(likelyClass);
+
+ if ((likelyClassAttribs & CORINFO_FLG_ABSTRACT) != 0)
+ {
+ // We may see an abstract likely class, if we have a stale profile.
+ // No point guessing for this.
+ //
+ JITDUMP("Not guessing for class; abstract (stale profile)\n");
+ return;
+ }
+
// Figure out which method will be called.
//
CORINFO_DEVIRTUALIZATION_INFO dvInfo;
@@ -22253,7 +22492,6 @@ void Compiler::considerGuardedDevirtualization(
// Add this as a potential candidate.
//
uint32_t const likelyMethodAttribs = info.compCompHnd->getMethodAttribs(likelyMethod);
- uint32_t const likelyClassAttribs = info.compCompHnd->getClassAttribs(likelyClass);
addGuardedDevirtualizationCandidate(call, likelyMethod, likelyClass, likelyMethodAttribs, likelyClassAttribs,
likelihood);
}
diff --git a/src/coreclr/jit/indirectcalltransformer.cpp b/src/coreclr/jit/indirectcalltransformer.cpp
index 6be64a86b6ebbb..c1922755647940 100644
--- a/src/coreclr/jit/indirectcalltransformer.cpp
+++ b/src/coreclr/jit/indirectcalltransformer.cpp
@@ -364,7 +364,7 @@ class IndirectCallTransformer
GenTree* zero = new (compiler, GT_CNS_INT) GenTreeIntCon(TYP_I_IMPL, 0);
GenTree* fatPointerCmp = compiler->gtNewOperNode(GT_NE, TYP_INT, fatPointerAnd, zero);
GenTree* jmpTree = compiler->gtNewOperNode(GT_JTRUE, TYP_VOID, fatPointerCmp);
- Statement* jmpStmt = compiler->fgNewStmtFromTree(jmpTree, stmt->GetILOffsetX());
+ Statement* jmpStmt = compiler->fgNewStmtFromTree(jmpTree, stmt->GetDebugInfo());
compiler->fgInsertStmtAtEnd(checkBlock, jmpStmt);
}
@@ -595,7 +595,7 @@ class IndirectCallTransformer
const unsigned thisTempNum = compiler->lvaGrabTemp(true DEBUGARG("guarded devirt this temp"));
// lvaSetClass(thisTempNum, ...);
GenTree* asgTree = compiler->gtNewTempAssign(thisTempNum, thisTree);
- Statement* asgStmt = compiler->fgNewStmtFromTree(asgTree, stmt->GetILOffsetX());
+ Statement* asgStmt = compiler->fgNewStmtFromTree(asgTree, stmt->GetDebugInfo());
compiler->fgInsertStmtAtEnd(checkBlock, asgStmt);
thisTree = compiler->gtNewLclvNode(thisTempNum, TYP_REF);
@@ -624,7 +624,7 @@ class IndirectCallTransformer
//
GenTree* methodTableCompare = compiler->gtNewOperNode(GT_NE, TYP_INT, targetMethodTable, methodTable);
GenTree* jmpTree = compiler->gtNewOperNode(GT_JTRUE, TYP_VOID, methodTableCompare);
- Statement* jmpStmt = compiler->fgNewStmtFromTree(jmpTree, stmt->GetILOffsetX());
+ Statement* jmpStmt = compiler->fgNewStmtFromTree(jmpTree, stmt->GetDebugInfo());
compiler->fgInsertStmtAtEnd(checkBlock, jmpStmt);
}
@@ -781,14 +781,14 @@ class IndirectCallTransformer
}
else
{
- compiler->fgNewStmtAtEnd(thenBlock, call);
+ compiler->fgNewStmtAtEnd(thenBlock, call, stmt->GetDebugInfo());
}
}
else
{
// Add the call.
//
- compiler->fgNewStmtAtEnd(thenBlock, call);
+ compiler->fgNewStmtAtEnd(thenBlock, call, stmt->GetDebugInfo());
// Re-establish this call as an inline candidate.
//
@@ -831,7 +831,7 @@ class IndirectCallTransformer
elseBlock = CreateAndInsertBasicBlock(BBJ_NONE, thenBlock);
elseBlock->bbFlags |= currBlock->bbFlags & BBF_SPLIT_GAINED;
GenTreeCall* call = origCall;
- Statement* newStmt = compiler->gtNewStmt(call);
+ Statement* newStmt = compiler->gtNewStmt(call, stmt->GetDebugInfo());
call->gtFlags &= ~GTF_CALL_INLINE_CANDIDATE;
call->SetIsGuarded();
@@ -1170,13 +1170,13 @@ class IndirectCallTransformer
assert(sizeCheck->OperIs(GT_LE));
GenTree* sizeJmpTree = compiler->gtNewOperNode(GT_JTRUE, TYP_VOID, sizeCheck);
- Statement* sizeJmpStmt = compiler->fgNewStmtFromTree(sizeJmpTree, stmt->GetILOffsetX());
+ Statement* sizeJmpStmt = compiler->fgNewStmtFromTree(sizeJmpTree, stmt->GetDebugInfo());
compiler->fgInsertStmtAtEnd(checkBlock, sizeJmpStmt);
checkBlock2 = CreateAndInsertBasicBlock(BBJ_COND, checkBlock);
assert(nullCheck->OperIs(GT_EQ));
GenTree* nullJmpTree = compiler->gtNewOperNode(GT_JTRUE, TYP_VOID, nullCheck);
- Statement* nullJmpStmt = compiler->fgNewStmtFromTree(nullJmpTree, stmt->GetILOffsetX());
+ Statement* nullJmpStmt = compiler->fgNewStmtFromTree(nullJmpTree, stmt->GetDebugInfo());
compiler->fgInsertStmtAtEnd(checkBlock2, nullJmpStmt);
}
@@ -1195,7 +1195,7 @@ class IndirectCallTransformer
origCall->gtCallArgs = argsIter.GetUse();
GenTree* asg = compiler->gtNewTempAssign(resultLclNum, resultHandle);
- Statement* asgStmt = compiler->gtNewStmt(asg, stmt->GetILOffsetX());
+ Statement* asgStmt = compiler->gtNewStmt(asg, stmt->GetDebugInfo());
compiler->fgInsertStmtAtEnd(thenBlock, asgStmt);
}
@@ -1206,7 +1206,7 @@ class IndirectCallTransformer
{
elseBlock = CreateAndInsertBasicBlock(BBJ_NONE, thenBlock);
GenTree* asg = compiler->gtNewTempAssign(resultLclNum, origCall);
- Statement* asgStmt = compiler->gtNewStmt(asg, stmt->GetILOffsetX());
+ Statement* asgStmt = compiler->gtNewStmt(asg, stmt->GetDebugInfo());
compiler->fgInsertStmtAtEnd(elseBlock, asgStmt);
}
diff --git a/src/coreclr/jit/inline.cpp b/src/coreclr/jit/inline.cpp
index fea132190a56bb..546eb2aa376f9d 100644
--- a/src/coreclr/jit/inline.cpp
+++ b/src/coreclr/jit/inline.cpp
@@ -332,7 +332,6 @@ InlineContext::InlineContext(InlineStrategy* strategy)
, m_Code(nullptr)
, m_ILSize(0)
, m_ImportedILSize(0)
- , m_Offset(BAD_IL_OFFSET)
, m_Observation(InlineObservation::CALLEE_UNUSED_INITIAL)
, m_CodeSizeEstimate(0)
, m_Success(true)
@@ -344,7 +343,11 @@ InlineContext::InlineContext(InlineStrategy* strategy)
, m_Callee(nullptr)
, m_TreeID(0)
, m_Ordinal(0)
+ , m_ActualCallOffset(BAD_IL_OFFSET)
#endif // defined(DEBUG) || defined(INLINE_DATA)
+#ifdef DEBUG
+ , m_ILInstsSet(nullptr)
+#endif
{
// Empty
}
@@ -356,13 +359,14 @@ InlineContext::InlineContext(InlineStrategy* strategy)
//
// Arguments:
// indent - indentation level for this node
+// verbose - more verbose output if true
-void InlineContext::Dump(unsigned indent)
+void InlineContext::Dump(bool verbose, unsigned indent)
{
// Handle fact that siblings are in reverse order.
if (m_Sibling != nullptr)
{
- m_Sibling->Dump(indent);
+ m_Sibling->Dump(verbose, indent);
}
// We may not know callee name in some of the failing cases
@@ -391,35 +395,63 @@ void InlineContext::Dump(unsigned indent)
{
// Root method
InlinePolicy* policy = InlinePolicy::GetPolicy(compiler, true);
- printf("Inlines into %08X [via %s] %s\n", calleeToken, policy->GetName(), calleeName);
+
+ if (verbose)
+ {
+ printf("\nInlines into %08X [via %s] %s:\n", calleeToken, policy->GetName(), calleeName);
+ }
+ else
+ {
+ printf("\nInlines into %s:\n", calleeName);
+ }
}
else
{
// Inline attempt.
const char* inlineTarget = InlGetTargetString(m_Observation);
const char* inlineReason = InlGetObservationString(m_Observation);
- const char* inlineResult = m_Success ? "" : "FAILED: ";
- const char* devirtualized = m_Devirtualized ? " devirt" : "";
- const char* guarded = m_Guarded ? " guarded" : "";
- const char* unboxed = m_Unboxed ? " unboxed" : "";
+ const char* inlineResult = m_Success ? "INLINED: " : "FAILED: ";
+ const char* devirtualized = m_Devirtualized ? " DEVIRT" : "";
+ const char* guarded = m_Guarded ? " GUARDED" : "";
+ const char* unboxed = m_Unboxed ? " UNBOXED" : "";
+
+ IL_OFFSET offs = BAD_IL_OFFSET;
- if (m_Offset == BAD_IL_OFFSET)
+#if defined(DEBUG) || defined(INLINE_DATA)
+ offs = m_ActualCallOffset;
+#endif
+
+ if (offs == BAD_IL_OFFSET && m_Location.IsValid())
{
- printf("%*s[%u IL=???? TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", m_Ordinal, m_TreeID, calleeToken,
- inlineResult, inlineTarget, inlineReason, guarded, devirtualized, unboxed, calleeName);
+ offs = m_Location.GetOffset();
+ }
+
+ if (verbose)
+ {
+ if (offs == BAD_IL_OFFSET)
+ {
+ printf("%*s[" FMT_INL_CTX " IL=???? TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", m_Ordinal,
+ m_TreeID, calleeToken, inlineResult, inlineTarget, inlineReason, guarded, devirtualized, unboxed,
+ calleeName);
+ }
+ else
+ {
+ printf("%*s[" FMT_INL_CTX " IL=%04d TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", m_Ordinal, offs,
+ m_TreeID, calleeToken, inlineResult, inlineTarget, inlineReason, guarded, devirtualized, unboxed,
+ calleeName);
+ }
}
else
{
- IL_OFFSET offset = jitGetILoffs(m_Offset);
- printf("%*s[%u IL=%04d TR=%06u %08X] [%s%s: %s%s%s%s] %s\n", indent, "", m_Ordinal, offset, m_TreeID,
- calleeToken, inlineResult, inlineTarget, inlineReason, guarded, devirtualized, unboxed, calleeName);
+ printf("%*s[%s%s%s%s%s] %s\n", indent, "", inlineResult, inlineReason, guarded, devirtualized, unboxed,
+ calleeName);
}
}
// Recurse to first child
if (m_Child != nullptr)
{
- m_Child->Dump(indent + 2);
+ m_Child->Dump(verbose, indent + 2);
}
}
@@ -455,7 +487,7 @@ void InlineContext::DumpData(unsigned indent)
else if (m_Success)
{
const char* inlineReason = InlGetObservationString(m_Observation);
- printf("%*s%u,\"%s\",\"%s\",", indent, "", m_Ordinal, inlineReason, calleeName);
+ printf("%*s%u,\"%s\",\"%s\",", indent, "", GetOrdinal(), inlineReason, calleeName);
m_Policy->DumpData(jitstdout);
printf("\n");
}
@@ -537,11 +569,7 @@ void InlineContext::DumpXml(FILE* file, unsigned indent)
buf[sizeof(buf) - 1] = 0;
EscapeNameForXml(buf);
- int offset = -1;
- if (m_Offset != BAD_IL_OFFSET)
- {
- offset = (int)jitGetILoffs(m_Offset);
- }
+ int offset = m_Location.IsValid() ? m_Location.GetOffset() : -1;
fprintf(file, "%*s<%s>\n", indent, "", inlineType);
fprintf(file, "%*s%08x\n", indent + 2, "", calleeToken);
@@ -636,13 +664,13 @@ InlineResult::InlineResult(Compiler* compiler, GenTreeCall* call, Statement* stm
// Pass along some optional information to the policy.
if (stmt != nullptr)
{
- m_InlineContext = stmt->GetInlineContext();
+ m_InlineContext = stmt->GetDebugInfo().GetInlineContext();
m_Policy->NoteContext(m_InlineContext);
#if defined(DEBUG) || defined(INLINE_DATA)
m_Policy->NoteOffset(call->gtRawILOffset);
#else
- m_Policy->NoteOffset(stmt->GetILOffsetX());
+ m_Policy->NoteOffset(stmt->GetDebugInfo().GetLocation().GetOffset());
#endif // defined(DEBUG) || defined(INLINE_DATA)
}
@@ -763,21 +791,7 @@ void InlineResult::Report()
if ((m_Callee != nullptr) && (obs != InlineObservation::CALLEE_IS_NOINLINE))
{
-
-#ifdef DEBUG
-
- const char* obsString = InlGetObservationString(obs);
-
- if (VERBOSE)
- {
- JITDUMP("\nINLINER: Marking %s as NOINLINE because of %s\n", callee, obsString);
- }
- else if (m_RootCompiler->fgPrintInlinedMethods)
- {
- printf("Marking %s as NOINLINE because of %s\n", callee, obsString);
- }
-
-#endif // DEBUG
+ JITDUMP("\nINLINER: Marking %s as NOINLINE because of %s\n", callee, InlGetObservationString(obs));
COMP_HANDLE comp = m_RootCompiler->info.compCompHnd;
comp->setMethodAttribs(m_Callee, CORINFO_FLG_BAD_INLINEE);
@@ -1237,137 +1251,101 @@ InlineContext* InlineStrategy::NewRoot()
return rootContext;
}
-//------------------------------------------------------------------------
-// NewSuccess: construct an InlineContext for a successful inline
-// and link it into the context tree
-//
-// Arguments:
-// inlineInfo - information about this inline
-//
-// Return Value:
-// A new InlineContext for statements brought into the method by
-// this inline.
-
-InlineContext* InlineStrategy::NewSuccess(InlineInfo* inlineInfo)
+InlineContext* InlineStrategy::NewContext(InlineContext* parentContext, Statement* stmt, GenTreeCall* call)
{
- InlineContext* calleeContext = new (m_Compiler, CMK_Inlining) InlineContext(this);
- Statement* stmt = inlineInfo->iciStmt;
- BYTE* calleeIL = inlineInfo->inlineCandidateInfo->methInfo.ILCode;
- unsigned calleeILSize = inlineInfo->inlineCandidateInfo->methInfo.ILCodeSize;
- InlineContext* parentContext = stmt->GetInlineContext();
- GenTreeCall* originalCall = inlineInfo->inlineResult->GetCall();
-
- noway_assert(parentContext != nullptr);
-
- calleeContext->m_Code = calleeIL;
- calleeContext->m_ILSize = calleeILSize;
- calleeContext->m_Parent = parentContext;
- // Push on front here will put siblings in reverse lexical
- // order which we undo in the dumper
- calleeContext->m_Sibling = parentContext->m_Child;
- parentContext->m_Child = calleeContext;
- calleeContext->m_Child = nullptr;
- calleeContext->m_Offset = stmt->GetILOffsetX();
- calleeContext->m_Observation = inlineInfo->inlineResult->GetObservation();
- calleeContext->m_Success = true;
- calleeContext->m_Devirtualized = originalCall->IsDevirtualized();
- calleeContext->m_Guarded = originalCall->IsGuarded();
- calleeContext->m_Unboxed = originalCall->IsUnboxed();
- calleeContext->m_ImportedILSize = inlineInfo->inlineResult->GetImportedILSize();
-
-#if defined(DEBUG) || defined(INLINE_DATA)
-
- InlinePolicy* policy = inlineInfo->inlineResult->GetPolicy();
+ InlineContext* context = new (m_Compiler, CMK_Inlining) InlineContext(this);
- calleeContext->m_Policy = policy;
- calleeContext->m_CodeSizeEstimate = policy->CodeSizeEstimate();
- calleeContext->m_Callee = inlineInfo->fncHandle;
- // +1 here since we set this before calling NoteOutcome.
- calleeContext->m_Ordinal = m_InlineCount + 1;
- // Update offset with more accurate info
- calleeContext->m_Offset = originalCall->gtRawILOffset;
-
-#endif // defined(DEBUG) || defined(INLINE_DATA)
-
-#if defined(DEBUG)
+ context->m_InlineStrategy = this;
+ context->m_Parent = parentContext;
+ context->m_Sibling = parentContext->m_Child;
+ parentContext->m_Child = context;
- calleeContext->m_TreeID = originalCall->gtTreeID;
+ // In debug builds we record inline contexts in all produced calls to be
+ // able to show all failed inlines in the inline tree, even non-candidates.
+ // These should always match the parent context we are seeing here.
+ assert(parentContext == call->gtInlineContext);
-#endif // defined(DEBUG)
+ if (call->IsInlineCandidate())
+ {
+ InlineCandidateInfo* info = call->gtInlineCandidateInfo;
+ context->m_Code = info->methInfo.ILCode;
+ context->m_ILSize = info->methInfo.ILCodeSize;
- NoteOutcome(calleeContext);
+#ifdef DEBUG
+ // All inline candidates should get their own statements that have
+ // appropriate debug info (or no debug info).
+ InlineContext* diInlineContext = stmt->GetDebugInfo().GetInlineContext();
+ assert(diInlineContext == nullptr || diInlineContext == parentContext);
+#endif
+ }
- return calleeContext;
-}
+ // TODO-DEBUGINFO: Currently, to keep the same behavior as before, we use
+ // the location of the statement containing the call being inlined. This is
+ // not always the exact IL offset of the call instruction, consider e.g.
+ // ldarg.0
+ // call
+ // which becomes a single statement where the IL location points to the
+ // ldarg instruction. For SPGO purposes we should consider always storing
+ // the exact offset of the call instruction which will be more precise. We
+ // may consider storing the statement itself as well.
+ context->m_Location = stmt->GetDebugInfo().GetLocation();
+ context->m_Devirtualized = call->IsDevirtualized();
+ context->m_Guarded = call->IsGuarded();
+ context->m_Unboxed = call->IsUnboxed();
#if defined(DEBUG) || defined(INLINE_DATA)
+ context->m_TreeID = call->gtTreeID;
+ context->m_Callee = call->gtCallType == CT_INDIRECT ? nullptr : call->gtCallMethHnd;
+ context->m_ActualCallOffset = call->gtRawILOffset;
+#endif
-//------------------------------------------------------------------------
-// NewFailure: construct an InlineContext for a failing inline
-// and link it into the context tree
-//
-// Arguments:
-// stmt - statement containing the attempted inline
-// inlineResult - inlineResult for the attempt
-//
-// Return Value:
-// A new InlineContext for diagnostic purposes
+ return context;
+}
-InlineContext* InlineStrategy::NewFailure(Statement* stmt, InlineResult* inlineResult)
+void InlineContext::SetSucceeded(const InlineInfo* info)
{
- // Check for a parent context first. We should now have a parent
- // context for all statements.
- InlineContext* parentContext = stmt->GetInlineContext();
- assert(parentContext != nullptr);
- InlineContext* failedContext = new (m_Compiler, CMK_Inlining) InlineContext(this);
- GenTreeCall* originalCall = inlineResult->GetCall();
-
- // Pushing the new context on the front of the parent child list
- // will put siblings in reverse lexical order which we undo in the
- // dumper.
- failedContext->m_Parent = parentContext;
- failedContext->m_Sibling = parentContext->m_Child;
- parentContext->m_Child = failedContext;
- failedContext->m_Child = nullptr;
- failedContext->m_Offset = stmt->GetILOffsetX();
- failedContext->m_Observation = inlineResult->GetObservation();
- failedContext->m_Callee = inlineResult->GetCallee();
- failedContext->m_Success = false;
- failedContext->m_Devirtualized = originalCall->IsDevirtualized();
- failedContext->m_Guarded = originalCall->IsGuarded();
- failedContext->m_Unboxed = originalCall->IsUnboxed();
-
- assert(InlIsValidObservation(failedContext->m_Observation));
+ assert(InlIsValidObservation(info->inlineResult->GetObservation()));
+ m_Observation = info->inlineResult->GetObservation();
+ m_ImportedILSize = info->inlineResult->GetImportedILSize();
+ m_Success = true;
#if defined(DEBUG) || defined(INLINE_DATA)
+ m_Policy = info->inlineResult->GetPolicy();
+ m_CodeSizeEstimate = m_Policy->CodeSizeEstimate();
+ m_Ordinal = m_InlineStrategy->m_InlineCount + 1;
+#endif
- // Update offset with more accurate info
- failedContext->m_Offset = originalCall->gtRawILOffset;
-
-#endif // #if defined(DEBUG) || defined(INLINE_DATA)
-
-#if defined(DEBUG)
-
- failedContext->m_TreeID = originalCall->gtTreeID;
+ m_InlineStrategy->NoteOutcome(this);
+}
-#endif // defined(DEBUG)
+void InlineContext::SetFailed(const InlineResult* result)
+{
+ assert(InlIsValidObservation(result->GetObservation()));
+ m_Observation = result->GetObservation();
+ m_ImportedILSize = result->GetImportedILSize();
+ m_Success = false;
- NoteOutcome(failedContext);
+#if defined(DEBUG) || defined(INLINE_DATA)
+ m_Policy = result->GetPolicy();
+ m_CodeSizeEstimate = m_Policy->CodeSizeEstimate();
+#endif
- return failedContext;
+ m_InlineStrategy->NoteOutcome(this);
}
+#if defined(DEBUG) || defined(INLINE_DATA)
+
//------------------------------------------------------------------------
// Dump: dump description of inline behavior
//
// Arguments:
-// showBudget - also dump final budget values
+// verbose - print more details such as final budget values and IL offsets
-void InlineStrategy::Dump(bool showBudget)
+void InlineStrategy::Dump(bool verbose)
{
- m_RootContext->Dump();
+ m_RootContext->Dump(verbose);
- if (!showBudget)
+ if (!verbose)
{
return;
}
diff --git a/src/coreclr/jit/inline.h b/src/coreclr/jit/inline.h
index 6a39b2d9cb5d01..983f9cf1686f4e 100644
--- a/src/coreclr/jit/inline.h
+++ b/src/coreclr/jit/inline.h
@@ -233,7 +233,7 @@ class InlinePolicy
{
(void)context;
}
- virtual void NoteOffset(IL_OFFSETX offset)
+ virtual void NoteOffset(IL_OFFSET offset)
{
(void)offset;
}
@@ -589,6 +589,7 @@ struct InlineCandidateInfo : public GuardedDevirtualizationCandidateInfo
CorInfoInitClassResult initClassResult;
var_types fncRetType;
bool exactContextNeedsRuntimeLookup;
+ InlineContext* inlinersContext;
};
// InlArgInfo describes inline candidate argument properties.
@@ -635,6 +636,7 @@ struct InlineInfo
CORINFO_METHOD_HANDLE fncHandle;
InlineCandidateInfo* inlineCandidateInfo;
+ InlineContext* inlineContext;
InlineResult* inlineResult;
@@ -686,6 +688,8 @@ struct InlineInfo
// This makes it possible to detect recursion and to keep track of the
// depth of each inline attempt.
+#define FMT_INL_CTX "INL%02u"
+
class InlineContext
{
// InlineContexts are created by InlineStrategies
@@ -695,7 +699,7 @@ class InlineContext
#if defined(DEBUG) || defined(INLINE_DATA)
// Dump the full subtree, including failures
- void Dump(unsigned indent = 0);
+ void Dump(bool verbose, unsigned indent = 0);
// Dump only the success subtree, with rich data
void DumpData(unsigned indent = 0);
@@ -709,6 +713,11 @@ class InlineContext
return m_Callee;
}
+ unsigned GetOrdinal() const
+ {
+ return m_Ordinal;
+ }
+
#endif // defined(DEBUG) || defined(INLINE_DATA)
// Get the parent context for this context.
@@ -717,6 +726,18 @@ class InlineContext
return m_Parent;
}
+ // Get the sibling context.
+ InlineContext* GetSibling() const
+ {
+ return m_Sibling;
+ }
+
+ // Get the first child context.
+ InlineContext* GetChild() const
+ {
+ return m_Child;
+ }
+
// Get the code pointer for this context.
const BYTE* GetCode() const
{
@@ -747,10 +768,10 @@ class InlineContext
return m_CodeSizeEstimate;
}
- // Get the offset of the call site
- IL_OFFSETX GetOffset() const
+ // Get the loation of the call site within the parent
+ ILLocation GetLocation() const
{
- return m_Offset;
+ return m_Location;
}
// True if this is the root context
@@ -779,10 +800,24 @@ class InlineContext
return m_ImportedILSize;
}
+ void SetSucceeded(const InlineInfo* info);
+ void SetFailed(const InlineResult* result);
+
+#ifdef DEBUG
+ FixedBitVect* GetILInstsSet() const
+ {
+ return m_ILInstsSet;
+ }
+
+ void SetILInstsSet(FixedBitVect* set)
+ {
+ m_ILInstsSet = set;
+ }
+#endif
+
private:
InlineContext(InlineStrategy* strategy);
-private:
InlineStrategy* m_InlineStrategy; // overall strategy
InlineContext* m_Parent; // logical caller (parent)
InlineContext* m_Child; // first child
@@ -790,8 +825,8 @@ class InlineContext
const BYTE* m_Code; // address of IL buffer for the method
unsigned m_ILSize; // size of IL buffer for the method
unsigned m_ImportedILSize; // estimated size of imported IL
- IL_OFFSETX m_Offset; // call site location within parent
- InlineObservation m_Observation; // what lead to this inline
+ ILLocation m_Location; // inlining statement location within parent
+ InlineObservation m_Observation; // what lead to this inline success or failure
int m_CodeSizeEstimate; // in bytes * 10
bool m_Success : 1; // true if this was a successful inline
bool m_Devirtualized : 1; // true if this was a devirtualized call
@@ -800,12 +835,17 @@ class InlineContext
#if defined(DEBUG) || defined(INLINE_DATA)
- InlinePolicy* m_Policy; // policy that evaluated this inline
- CORINFO_METHOD_HANDLE m_Callee; // handle to the method
- unsigned m_TreeID; // ID of the GenTreeCall
- unsigned m_Ordinal; // Ordinal number of this inline
+ InlinePolicy* m_Policy; // policy that evaluated this inline
+ CORINFO_METHOD_HANDLE m_Callee; // handle to the method
+ unsigned m_TreeID; // ID of the GenTreeCall in the parent
+ unsigned m_Ordinal; // Ordinal number of this inline
+ IL_OFFSET m_ActualCallOffset; // IL offset of actual call instruction leading to the inline
#endif // defined(DEBUG) || defined(INLINE_DATA)
+
+#ifdef DEBUG
+ FixedBitVect* m_ILInstsSet; // Set of offsets where instructions begin
+#endif
};
// The InlineStrategy holds the per-method persistent inline state.
@@ -814,16 +854,14 @@ class InlineContext
class InlineStrategy
{
+ friend class InlineContext;
public:
// Construct a new inline strategy.
InlineStrategy(Compiler* compiler);
- // Create context for a successful inline.
- InlineContext* NewSuccess(InlineInfo* inlineInfo);
-
- // Create context for a failing inline.
- InlineContext* NewFailure(Statement* stmt, InlineResult* inlineResult);
+ // Create context for the specified inline candidate contained in the specified statement.
+ InlineContext* NewContext(InlineContext* parentContext, Statement* stmt, GenTreeCall* call);
// Compiler associated with this strategy
Compiler* GetCompiler() const
@@ -923,7 +961,7 @@ class InlineStrategy
#if defined(DEBUG) || defined(INLINE_DATA)
// Dump textual description of inlines done so far.
- void Dump(bool showBudget);
+ void Dump(bool verbose);
// Dump data-format description of inlines done so far.
void DumpData();
diff --git a/src/coreclr/jit/inlinepolicy.cpp b/src/coreclr/jit/inlinepolicy.cpp
index c69453b3565fbd..1ec47378872d54 100644
--- a/src/coreclr/jit/inlinepolicy.cpp
+++ b/src/coreclr/jit/inlinepolicy.cpp
@@ -3383,7 +3383,7 @@ bool ReplayPolicy::FindContext(InlineContext* context)
// Token and Hash we're looking for.
mdMethodDef contextToken = m_RootCompiler->info.compCompHnd->getMethodDefFromMethod(context->GetCallee());
unsigned contextHash = m_RootCompiler->compMethodHash(context->GetCallee());
- unsigned contextOffset = (unsigned)context->GetOffset();
+ unsigned contextOffset = (unsigned)context->GetLocation().GetOffset();
return FindInline(contextToken, contextHash, contextOffset);
}
@@ -3573,7 +3573,7 @@ bool ReplayPolicy::FindInline(CORINFO_METHOD_HANDLE callee)
int offset = -1;
if (m_Offset != BAD_IL_OFFSET)
{
- offset = (int)jitGetILoffs(m_Offset);
+ offset = m_Offset;
}
unsigned calleeOffset = (unsigned)offset;
diff --git a/src/coreclr/jit/inlinepolicy.h b/src/coreclr/jit/inlinepolicy.h
index e604fd57a36ed5..f5f200e9f5bd71 100644
--- a/src/coreclr/jit/inlinepolicy.h
+++ b/src/coreclr/jit/inlinepolicy.h
@@ -516,7 +516,7 @@ class ReplayPolicy : public DiscretionaryPolicy
m_InlineContext = context;
}
- void NoteOffset(IL_OFFSETX offset) override
+ void NoteOffset(IL_OFFSET offset) override
{
m_Offset = offset;
}
@@ -542,7 +542,7 @@ class ReplayPolicy : public DiscretionaryPolicy
static FILE* s_ReplayFile;
static CritSecObject s_XmlReaderLock;
InlineContext* m_InlineContext;
- IL_OFFSETX m_Offset;
+ IL_OFFSET m_Offset;
bool m_WasForceInline;
};
diff --git a/src/coreclr/jit/instr.cpp b/src/coreclr/jit/instr.cpp
index 06ee07c405b6bd..bbf204c74caa82 100644
--- a/src/coreclr/jit/instr.cpp
+++ b/src/coreclr/jit/instr.cpp
@@ -72,7 +72,7 @@ const char* CodeGen::genInsName(instruction ins)
};
// clang-format on
- assert((unsigned)ins < _countof(insNames));
+ assert((unsigned)ins < ArrLen(insNames));
assert(insNames[ins] != nullptr);
return insNames[ins];
@@ -243,7 +243,7 @@ void CodeGen::instGen(instruction ins)
// static inline
bool CodeGenInterface::instIsFP(instruction ins)
{
- assert((unsigned)ins < _countof(instInfo));
+ assert((unsigned)ins < ArrLen(instInfo));
#ifdef TARGET_XARCH
return (instInfo[ins] & INS_FLAGS_x87Instr) != 0;
@@ -1049,8 +1049,8 @@ void CodeGen::inst_RV_TT_IV(instruction ins, emitAttr attr, regNumber reg1, GenT
{
#if defined(FEATURE_HW_INTRINSICS)
assert(rmOp->AsHWIntrinsic()->OperIsMemoryLoad());
- assert(HWIntrinsicInfo::lookupNumArgs(rmOp->AsHWIntrinsic()) == 1);
- addr = rmOp->gtGetOp1();
+ assert(rmOp->AsHWIntrinsic()->GetOperandCount() == 1);
+ addr = rmOp->AsHWIntrinsic()->Op(1);
#else
unreached();
#endif // FEATURE_HW_INTRINSICS
@@ -1100,8 +1100,7 @@ void CodeGen::inst_RV_TT_IV(instruction ins, emitAttr attr, regNumber reg1, GenT
case GT_LCL_VAR:
{
- assert(rmOp->IsRegOptional() ||
- !compiler->lvaGetDesc(rmOp->AsLclVar()->GetLclNum())->lvIsRegCandidate());
+ assert(rmOp->IsRegOptional() || !compiler->lvaGetDesc(rmOp->AsLclVar())->lvIsRegCandidate());
varNum = rmOp->AsLclVar()->GetLclNum();
offset = 0;
break;
@@ -1178,8 +1177,8 @@ void CodeGen::inst_RV_RV_TT(
{
#if defined(FEATURE_HW_INTRINSICS)
assert(op2->AsHWIntrinsic()->OperIsMemoryLoad());
- assert(HWIntrinsicInfo::lookupNumArgs(op2->AsHWIntrinsic()) == 1);
- addr = op2->gtGetOp1();
+ assert(op2->AsHWIntrinsic()->GetOperandCount() == 1);
+ addr = op2->AsHWIntrinsic()->Op(1);
#else
unreached();
#endif // FEATURE_HW_INTRINSICS
@@ -1231,8 +1230,7 @@ void CodeGen::inst_RV_RV_TT(
case GT_LCL_VAR:
{
- assert(op2->IsRegOptional() ||
- !compiler->lvaGetDesc(op2->AsLclVar()->GetLclNum())->lvIsRegCandidate());
+ assert(op2->IsRegOptional() || !compiler->lvaGetDesc(op2->AsLclVar())->lvIsRegCandidate());
varNum = op2->AsLclVar()->GetLclNum();
offset = 0;
break;
diff --git a/src/coreclr/jit/instrsarm64.h b/src/coreclr/jit/instrsarm64.h
index 548c13f339469f..c89b006a418970 100644
--- a/src/coreclr/jit/instrsarm64.h
+++ b/src/coreclr/jit/instrsarm64.h
@@ -1563,10 +1563,13 @@ INST1(uxth, "uxth", 0, IF_DR_2H, 0x53003C00)
INST1(nop, "nop", 0, IF_SN_0A, 0xD503201F)
// nop SN_0A 1101010100000011 0010000000011111 D503 201F
-INST1(bkpt, "bkpt", 0, IF_SN_0A, 0xD43E0000)
- // brpt SN_0A 1101010000111110 0000000000000000 D43E 0000 0xF000
+INST1(yield, "yield", 0, IF_SN_0A, 0xD503203F)
+ // yield SN_0A 1101010100000011 0010000000111111 D503 203F
-INST1(brk, "brk", 0, IF_SI_0A, 0xD4200000)
+INST1(brk_windows, "brk_windows", 0, IF_SI_0A, 0xD43E0000)
+ // brk (windows) SI_0A 1101010000111110 0000000000000000 D43E 0000 0xF000
+
+INST1(brk_unix, "brk_unix", 0, IF_SI_0A, 0xD4200000)
// brk imm16 SI_0A 11010100001iiiii iiiiiiiiiii00000 D420 0000 imm16
INST1(dsb, "dsb", 0, IF_SI_0B, 0xD503309F)
diff --git a/src/coreclr/jit/instrsxarch.h b/src/coreclr/jit/instrsxarch.h
index 4dc1fffd098d51..17b94fc81d779e 100644
--- a/src/coreclr/jit/instrsxarch.h
+++ b/src/coreclr/jit/instrsxarch.h
@@ -640,11 +640,11 @@ INST2(rcl, "rcl", IUM_RW, 0x0010D2, BAD_CODE,
INST2(rcl_1, "rcl", IUM_RW, 0x0010D0, 0x0010D0, Writes_OF | Writes_CF
| Reads_CF )
INST2(rcl_N, "rcl", IUM_RW, 0x0010C0, 0x0010C0, Undefined_OF | Writes_CF
- | Reads_CF )
+ | Reads_CF )
INST2(rcr, "rcr", IUM_RW, 0x0018D2, BAD_CODE, Undefined_OF | Writes_CF
| Reads_CF )
INST2(rcr_1, "rcr", IUM_RW, 0x0018D0, 0x0018D0, Writes_OF | Writes_CF
- | Reads_CF )
+ | Reads_CF )
INST2(rcr_N, "rcr", IUM_RW, 0x0018C0, 0x0018C0, Undefined_OF | Writes_CF
| Reads_CF )
INST2(shl, "shl", IUM_RW, 0x0020D2, BAD_CODE, Undefined_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Writes_CF )
@@ -683,6 +683,7 @@ INST1(stosq, "stosq", IUM_RD, 0x00AB48,
INST1(int3, "int3", IUM_RD, 0x0000CC, INS_FLAGS_None )
INST1(nop, "nop", IUM_RD, 0x000090, INS_FLAGS_None )
+INST1(pause, "pause", IUM_RD, 0x0090F3, INS_FLAGS_None )
INST1(lock, "lock", IUM_RD, 0x0000F0, INS_FLAGS_None )
INST1(leave, "leave", IUM_RD, 0x0000C9, INS_FLAGS_None )
diff --git a/src/coreclr/jit/jit.h b/src/coreclr/jit/jit.h
index c84bb0b529e4a5..d8cb5cabfb065d 100644
--- a/src/coreclr/jit/jit.h
+++ b/src/coreclr/jit/jit.h
@@ -288,41 +288,9 @@ const CORINFO_CLASS_HANDLE NO_CLASS_HANDLE = (CORINFO_CLASS_HANDLE) nullptr;
/*****************************************************************************/
-// We define two IL offset types, as follows:
-//
-// IL_OFFSET: either a distinguished value, or an IL offset.
-// IL_OFFSETX: either a distinguished value, or the top two bits are a flags, and the remaining bottom
-// bits are a IL offset.
-//
-// In both cases, the set of legal distinguished values is:
-// BAD_IL_OFFSET -- A unique illegal IL offset number. Note that it must be different from
-// the ICorDebugInfo values, below, and must also not be a legal IL offset.
-// ICorDebugInfo::NO_MAPPING -- The IL offset corresponds to no source code (such as EH step blocks).
-// ICorDebugInfo::PROLOG -- The IL offset indicates a prolog
-// ICorDebugInfo::EPILOG -- The IL offset indicates an epilog
-//
-// The IL offset must be in the range [0 .. 0x3fffffff]. This is because we steal
-// the top two bits in IL_OFFSETX for flags, but we want the maximum range to be the same
-// for both types. The IL value can't be larger than the maximum IL offset of the function
-// being compiled.
-//
-// Blocks and statements never store one of the ICorDebugInfo values, even for IL_OFFSETX types. These are
-// only stored in the IPmappingDsc struct, ipmdILoffsx field.
-
typedef unsigned IL_OFFSET;
-const IL_OFFSET BAD_IL_OFFSET = 0x80000000;
-const IL_OFFSET MAX_IL_OFFSET = 0x3fffffff;
-
-typedef unsigned IL_OFFSETX; // IL_OFFSET with stack-empty or call-instruction bit
-const IL_OFFSETX IL_OFFSETX_STKBIT = 0x80000000; // Note: this bit is set when the stack is NOT empty!
-const IL_OFFSETX IL_OFFSETX_CALLINSTRUCTIONBIT = 0x40000000; // Set when the IL offset is for a call instruction.
-const IL_OFFSETX IL_OFFSETX_BITS = IL_OFFSETX_STKBIT | IL_OFFSETX_CALLINSTRUCTIONBIT;
-
-IL_OFFSET jitGetILoffs(IL_OFFSETX offsx);
-IL_OFFSET jitGetILoffsAny(IL_OFFSETX offsx);
-bool jitIsStackEmpty(IL_OFFSETX offsx);
-bool jitIsCallInstruction(IL_OFFSETX offsx);
+const IL_OFFSET BAD_IL_OFFSET = 0xffffffff;
const unsigned BAD_VAR_NUM = UINT_MAX;
diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h
index d9aef33b2c7e1b..06211a597c8ed6 100644
--- a/src/coreclr/jit/jitconfigvalues.h
+++ b/src/coreclr/jit/jitconfigvalues.h
@@ -67,6 +67,11 @@ CONFIG_INTEGER(JitAlignLoopAdaptive,
W("JitAlignLoopAdaptive"),
1) // If set, perform adaptive loop alignment that limits number of padding based on loop size.
+CONFIG_INTEGER(JitHideAlignBehindJmp,
+ W("JitHideAlignBehindJmp"),
+ 1) // If set, try to hide align instruction (if any) behind an unconditional jump instruction (if any)
+ // that is present before the loop start.
+
// Print the alignment boundaries in disassembly.
CONFIG_INTEGER(JitDasmWithAlignmentBoundaries, W("JitDasmWithAlignmentBoundaries"), 0)
@@ -125,7 +130,13 @@ CONFIG_INTEGER(JitQueryCurrentStaticFieldClass, W("JitQueryCurrentStaticFieldCla
CONFIG_INTEGER(JitReportFastTailCallDecisions, W("JitReportFastTailCallDecisions"), 0)
CONFIG_INTEGER(JitPInvokeCheckEnabled, W("JITPInvokeCheckEnabled"), 0)
CONFIG_INTEGER(JitPInvokeEnabled, W("JITPInvokeEnabled"), 1)
+
+// Controls verbosity for JitPrintInlinedMethods. Ignored for JitDump/NgenDump where
+// it's always set.
+CONFIG_INTEGER(JitPrintInlinedMethodsVerbose, W("JitPrintInlinedMethodsVerboseLevel"), 0)
+// Prints a tree of inlinees for a specific method (use '*' for all methods)
CONFIG_METHODSET(JitPrintInlinedMethods, W("JitPrintInlinedMethods"))
+
CONFIG_METHODSET(JitPrintDevirtualizedMethods, W("JitPrintDevirtualizedMethods"))
CONFIG_INTEGER(JitProfileChecks, W("JitProfileChecks"), 0) // 1 enable in dumps, 2 assert if issues found
CONFIG_INTEGER(JitRequired, W("JITRequired"), -1)
@@ -165,12 +176,13 @@ CONFIG_INTEGER(TreesBeforeAfterMorph, W("JitDumpBeforeAfterMorph"), 0) // If 1,
CONFIG_METHODSET(JitBreak, W("JitBreak")) // Stops in the importer when compiling a specified method
CONFIG_METHODSET(JitDebugBreak, W("JitDebugBreak"))
-CONFIG_METHODSET(JitDisasm, W("JitDisasm")) // Dumps disassembly for specified method
-CONFIG_STRING(JitDisasmAssemblies, W("JitDisasmAssemblies")) // Only show JitDisasm and related info for methods
- // from this semicolon-delimited list of assemblies.
-CONFIG_INTEGER(JitDisasmWithGC, W("JitDisasmWithGC"), 0) // Dump interleaved GC Info for any method disassembled.
-CONFIG_METHODSET(JitDump, W("JitDump")) // Dumps trees for specified method
-CONFIG_INTEGER(JitDumpTier0, W("JitDumpTier0"), 1) // Dump tier0 requests
+CONFIG_METHODSET(JitDisasm, W("JitDisasm")) // Dumps disassembly for specified method
+CONFIG_STRING(JitDisasmAssemblies, W("JitDisasmAssemblies")) // Only show JitDisasm and related info for methods
+ // from this semicolon-delimited list of assemblies.
+CONFIG_INTEGER(JitDisasmWithGC, W("JitDisasmWithGC"), 0) // Dump interleaved GC Info for any method disassembled.
+CONFIG_METHODSET(JitDump, W("JitDump")) // Dumps trees for specified method
+CONFIG_INTEGER(JitDumpTier0, W("JitDumpTier0"), 1) // Dump tier0 requests
+CONFIG_INTEGER(JitDumpAtOSROffset, W("JitDumpAtOSROffset"), -1) // Only dump OSR requests for this offset
CONFIG_INTEGER(JitDumpInlinePhases, W("JitDumpInlinePhases"), 1) // Dump inline compiler phases
CONFIG_METHODSET(JitEHDump, W("JitEHDump")) // Dump the EH table for the method, as reported to the VM
CONFIG_METHODSET(JitExclude, W("JitExclude"))
@@ -220,6 +232,9 @@ CONFIG_INTEGER(JitDumpFgConstrained, W("JitDumpFgConstrained"), 1) // 0 == don't
CONFIG_INTEGER(JitDumpFgBlockID, W("JitDumpFgBlockID"), 0) // 0 == display block with bbNum; 1 == display with both
// bbNum and bbID
+CONFIG_STRING(JitDumpPreciseDebugInfoFile, W("JitDumpPreciseDebugInfoFile"))
+CONFIG_INTEGER(JitDisasmWithDebugInfo, W("JitDisasmWithDebugInfo"), 0)
+
CONFIG_STRING(JitLateDisasmTo, W("JITLateDisasmTo"))
CONFIG_STRING(JitRange, W("JitRange"))
CONFIG_STRING(JitStressModeNames, W("JitStressModeNames")) // Internal Jit stress mode: stress using the given set of
diff --git a/src/coreclr/jit/jiteh.cpp b/src/coreclr/jit/jiteh.cpp
index 12e91d077690ba..c0fd2bf9581ce3 100644
--- a/src/coreclr/jit/jiteh.cpp
+++ b/src/coreclr/jit/jiteh.cpp
@@ -1405,7 +1405,12 @@ void Compiler::fgRemoveEHTableEntry(unsigned XTnum)
if (compHndBBtabCount == 0)
{
// No more entries remaining.
- compHndBBtab = nullptr;
+ //
+ // We used to null out compHndBBtab here, but with OSR + Synch method
+ // we may remove all the initial EH entries if not reachable in the
+ // OSR portion, then need to add one for the synchronous exit.
+ //
+ // So now we just leave it be.
}
else
{
diff --git a/src/coreclr/jit/jithashtable.h b/src/coreclr/jit/jithashtable.h
index 5b95afbd899caf..85186dfb508e66 100644
--- a/src/coreclr/jit/jithashtable.h
+++ b/src/coreclr/jit/jithashtable.h
@@ -715,7 +715,7 @@ class JitHashTable
//
static JitPrimeInfo NextPrime(unsigned number)
{
- for (int i = 0; i < (int)(_countof(jitPrimeInfo)); i++)
+ for (int i = 0; i < (int)(ArrLen(jitPrimeInfo)); i++)
{
if (jitPrimeInfo[i].prime >= number)
{
diff --git a/src/coreclr/jit/jitstd/algorithm.h b/src/coreclr/jit/jitstd/algorithm.h
index 9fa6fbb94dd546..7df9c8488d5b7f 100644
--- a/src/coreclr/jit/jitstd/algorithm.h
+++ b/src/coreclr/jit/jitstd/algorithm.h
@@ -1,10 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-
-
#pragma once
+#include
+
namespace jitstd
{
@@ -148,7 +148,7 @@ void quick_sort(RandomAccessIterator first, RandomAccessIterator last, Less less
RandomAccessIterator rightFirst = newLast + 1;
RandomAccessIterator rightLast = last;
- assert(depth < _countof(firstStack));
+ assert(depth < ARRAY_SIZE(firstStack));
// Ideally, the 2 partitions should have the same size, that would guarantee
// log2(n) stack space. If that's not the case then push the larger partition
diff --git a/src/coreclr/jit/layout.cpp b/src/coreclr/jit/layout.cpp
index cbcb631185d9d3..d47d5ae894fc47 100644
--- a/src/coreclr/jit/layout.cpp
+++ b/src/coreclr/jit/layout.cpp
@@ -82,7 +82,7 @@ class ClassLayoutTable
private:
bool HasSmallCapacity() const
{
- return m_layoutCount <= _countof(m_layoutArray);
+ return m_layoutCount <= ArrLen(m_layoutArray);
}
ClassLayout* GetLayoutByIndex(unsigned index) const
@@ -157,7 +157,7 @@ class ClassLayoutTable
unsigned AddBlkLayout(Compiler* compiler, ClassLayout* layout)
{
- if (m_layoutCount < _countof(m_layoutArray))
+ if (m_layoutCount < ArrLen(m_layoutArray))
{
m_layoutArray[m_layoutCount] = layout;
return m_layoutCount++;
@@ -201,7 +201,7 @@ class ClassLayoutTable
unsigned AddObjLayout(Compiler* compiler, ClassLayout* layout)
{
- if (m_layoutCount < _countof(m_layoutArray))
+ if (m_layoutCount < ArrLen(m_layoutArray))
{
m_layoutArray[m_layoutCount] = layout;
return m_layoutCount++;
@@ -220,7 +220,7 @@ class ClassLayoutTable
unsigned newCapacity = m_layoutCount * 2;
ClassLayout** newArray = alloc.allocate(newCapacity);
- if (m_layoutCount <= _countof(m_layoutArray))
+ if (m_layoutCount <= ArrLen(m_layoutArray))
{
BlkLayoutIndexMap* blkLayoutMap = new (alloc) BlkLayoutIndexMap(alloc);
ObjLayoutIndexMap* objLayoutMap = new (alloc) ObjLayoutIndexMap(alloc);
@@ -370,7 +370,7 @@ void ClassLayout::InitializeGCPtrs(Compiler* compiler)
unsigned gcPtrCount = compiler->info.compCompHnd->getClassGClayout(m_classHandle, gcPtrs);
assert((gcPtrCount == 0) || ((compiler->info.compCompHnd->getClassAttribs(m_classHandle) &
- (CORINFO_FLG_CONTAINS_GC_PTR | CORINFO_FLG_CONTAINS_STACK_PTR)) != 0));
+ (CORINFO_FLG_CONTAINS_GC_PTR | CORINFO_FLG_BYREF_LIKE)) != 0));
// Since class size is unsigned there's no way we could have more than 2^30 slots
// so it should be safe to fit this into a 30 bits bit field.
diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp
index 2d2ae92df4203f..debf6bafcfeb5c 100644
--- a/src/coreclr/jit/lclvars.cpp
+++ b/src/coreclr/jit/lclvars.cpp
@@ -56,9 +56,6 @@ void Compiler::lvaInit()
lvaOutgoingArgSpaceVar = BAD_VAR_NUM;
lvaOutgoingArgSpaceSize = PhasedVar();
#endif // FEATURE_FIXED_OUT_ARGS
-#ifdef TARGET_ARM
- lvaPromotedStructAssemblyScratchVar = BAD_VAR_NUM;
-#endif // TARGET_ARM
#ifdef JIT32_GCENCODER
lvaLocAllocSPvar = BAD_VAR_NUM;
#endif // JIT32_GCENCODER
@@ -560,24 +557,8 @@ void Compiler::lvaInitRetBuffArg(InitVarDscInfo* varDscInfo, bool useFixedRetBuf
#endif
varDsc->lvOnFrame = true; // The final home for this incoming register might be our local stack frame
- info.compRetBuffDefStack = 0;
- if (info.compRetType == TYP_STRUCT)
- {
- CORINFO_SIG_INFO sigInfo;
- info.compCompHnd->getMethodSig(info.compMethodHnd, &sigInfo);
- assert(JITtype2varType(sigInfo.retType) == info.compRetType); // Else shouldn't have a ret buff.
-
- info.compRetBuffDefStack = (info.compCompHnd->isStructRequiringStackAllocRetBuf(sigInfo.retTypeClass));
- if (info.compRetBuffDefStack)
- {
- // If we're assured that the ret buff argument points into a callers stack, we will type it as
- // "TYP_I_IMPL"
- // (native int/unmanaged pointer) so that it's not tracked as a GC ref.
- varDsc->lvType = TYP_I_IMPL;
- }
- }
#ifdef FEATURE_SIMD
- else if (supportSIMDTypes() && varTypeIsSIMD(info.compRetType))
+ if (supportSIMDTypes() && varTypeIsSIMD(info.compRetType))
{
varDsc->lvSIMDType = true;
@@ -1310,7 +1291,7 @@ void Compiler::lvaInitVarDsc(LclVarDsc* varDsc,
CORINFO_ARG_LIST_HANDLE varList,
CORINFO_SIG_INFO* varSig)
{
- noway_assert(varDsc == &lvaTable[varNum]);
+ noway_assert(varDsc == lvaGetDesc(varNum));
switch (corInfoType)
{
@@ -1564,9 +1545,7 @@ unsigned Compiler::compMap2ILvarNum(unsigned varNum) const
bool Compiler::lvaVarAddrExposed(unsigned varNum) const
{
- noway_assert(varNum < lvaCount);
- const LclVarDsc* varDsc = &lvaTable[varNum];
-
+ const LclVarDsc* varDsc = lvaGetDesc(varNum);
return varDsc->IsAddressExposed();
}
@@ -1576,9 +1555,7 @@ bool Compiler::lvaVarAddrExposed(unsigned varNum) const
bool Compiler::lvaVarDoNotEnregister(unsigned varNum)
{
- noway_assert(varNum < lvaCount);
- LclVarDsc* varDsc = &lvaTable[varNum];
-
+ LclVarDsc* varDsc = lvaGetDesc(varNum);
return varDsc->lvDoNotEnregister;
}
@@ -1605,9 +1582,7 @@ void Compiler::lvSetMinOptsDoNotEnreg()
CORINFO_CLASS_HANDLE Compiler::lvaGetStruct(unsigned varNum)
{
- noway_assert(varNum < lvaCount);
const LclVarDsc* varDsc = lvaGetDesc(varNum);
-
return varDsc->GetStructHnd();
}
@@ -1838,9 +1813,9 @@ bool Compiler::StructPromotionHelper::CanPromoteStructType(CORINFO_CLASS_HANDLE
}
// If we saw any GC pointer or by-ref fields above then CORINFO_FLG_CONTAINS_GC_PTR or
- // CORINFO_FLG_CONTAINS_STACK_PTR has to be set!
+ // CORINFO_FLG_BYREF_LIKE has to be set!
noway_assert((containsGCpointers == false) ||
- ((typeFlags & (CORINFO_FLG_CONTAINS_GC_PTR | CORINFO_FLG_CONTAINS_STACK_PTR)) != 0));
+ ((typeFlags & (CORINFO_FLG_CONTAINS_GC_PTR | CORINFO_FLG_BYREF_LIKE)) != 0));
// If we have "Custom Layout" then we might have an explicit Size attribute
// Managed C++ uses this for its structs, such C++ types will not contain GC pointers.
@@ -2010,9 +1985,7 @@ bool Compiler::StructPromotionHelper::CanPromoteStructVar(unsigned lclNum)
//
bool Compiler::StructPromotionHelper::ShouldPromoteStructVar(unsigned lclNum)
{
- assert(lclNum < compiler->lvaCount);
-
- LclVarDsc* varDsc = &compiler->lvaTable[lclNum];
+ LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum);
assert(varTypeIsStruct(varDsc));
assert(varDsc->GetStructHnd() == structPromotionInfo.typeHnd);
assert(structPromotionInfo.canPromote);
@@ -2489,9 +2462,7 @@ unsigned Compiler::lvaGetFieldLocal(const LclVarDsc* varDsc, unsigned int fldOff
void Compiler::lvaSetVarAddrExposed(unsigned varNum DEBUGARG(AddressExposedReason reason))
{
- noway_assert(varNum < lvaCount);
-
- LclVarDsc* varDsc = &lvaTable[varNum];
+ LclVarDsc* varDsc = lvaGetDesc(varNum);
varDsc->SetAddressExposed(true DEBUGARG(reason));
@@ -2518,9 +2489,7 @@ void Compiler::lvaSetVarAddrExposed(unsigned varNum DEBUGARG(AddressExposedReaso
//
void Compiler::lvaSetVarLiveInOutOfHandler(unsigned varNum)
{
- noway_assert(varNum < lvaCount);
-
- LclVarDsc* varDsc = &lvaTable[varNum];
+ LclVarDsc* varDsc = lvaGetDesc(varNum);
varDsc->lvLiveInOutOfHndlr = 1;
@@ -2563,8 +2532,7 @@ void Compiler::lvaSetVarLiveInOutOfHandler(unsigned varNum)
void Compiler::lvaSetVarDoNotEnregister(unsigned varNum DEBUGARG(DoNotEnregisterReason reason))
{
- noway_assert(varNum < lvaCount);
- LclVarDsc* varDsc = &lvaTable[varNum];
+ LclVarDsc* varDsc = lvaGetDesc(varNum);
const bool wasAlreadyMarkedDoNotEnreg = (varDsc->lvDoNotEnregister == 1);
varDsc->lvDoNotEnregister = 1;
@@ -2711,9 +2679,7 @@ bool Compiler::lvaIsMultiregStruct(LclVarDsc* varDsc, bool isVarArg)
void Compiler::lvaSetStruct(unsigned varNum, CORINFO_CLASS_HANDLE typeHnd, bool unsafeValueClsCheck, bool setTypeInfo)
{
- noway_assert(varNum < lvaCount);
-
- LclVarDsc* varDsc = &lvaTable[varNum];
+ LclVarDsc* varDsc = lvaGetDesc(varNum);
if (setTypeInfo)
{
varDsc->lvVerTypeInfo = typeInfo(TI_STRUCT, typeHnd);
@@ -2897,7 +2863,7 @@ void Compiler::lvaSetStructUsedAsVarArg(unsigned varNum)
if (GlobalJitOptions::compFeatureHfa && TargetOS::IsWindows)
{
#if defined(TARGET_ARM64)
- LclVarDsc* varDsc = &lvaTable[varNum];
+ LclVarDsc* varDsc = lvaGetDesc(varNum);
// For varargs methods incoming and outgoing arguments should not be treated
// as HFA.
varDsc->SetHfaType(TYP_UNDEF);
@@ -2930,7 +2896,7 @@ void Compiler::lvaSetClass(unsigned varNum, CORINFO_CLASS_HANDLE clsHnd, bool is
// Else we should have a type handle.
assert(clsHnd != nullptr);
- LclVarDsc* varDsc = &lvaTable[varNum];
+ LclVarDsc* varDsc = lvaGetDesc(varNum);
assert(varDsc->lvType == TYP_REF);
// We shoud not have any ref type information for this var.
@@ -3015,7 +2981,7 @@ void Compiler::lvaUpdateClass(unsigned varNum, CORINFO_CLASS_HANDLE clsHnd, bool
// Else we should have a class handle to consider
assert(clsHnd != nullptr);
- LclVarDsc* varDsc = &lvaTable[varNum];
+ LclVarDsc* varDsc = lvaGetDesc(varNum);
assert(varDsc->lvType == TYP_REF);
// We should already have a class
@@ -3920,11 +3886,7 @@ void Compiler::lvaMarkLclRefs(GenTree* tree, BasicBlock* block, Statement* stmt,
if (!opts.ShouldUsePInvokeHelpers())
{
/* Get the special variable descriptor */
-
- unsigned lclNum = info.compLvFrameListRoot;
-
- noway_assert(lclNum <= lvaCount);
- LclVarDsc* varDsc = lvaTable + lclNum;
+ LclVarDsc* varDsc = lvaGetDesc(info.compLvFrameListRoot);
/* Increment the ref counts twice */
varDsc->incRefCnts(weight, this);
@@ -4023,8 +3985,7 @@ void Compiler::lvaMarkLclRefs(GenTree* tree, BasicBlock* block, Statement* stmt,
assert((tree->gtOper == GT_LCL_VAR) || (tree->gtOper == GT_LCL_FLD));
unsigned lclNum = tree->AsLclVarCommon()->GetLclNum();
- noway_assert(lclNum < lvaCount);
- LclVarDsc* varDsc = lvaTable + lclNum;
+ LclVarDsc* varDsc = lvaGetDesc(lclNum);
/* Increment the reference counts */
@@ -4035,7 +3996,7 @@ void Compiler::lvaMarkLclRefs(GenTree* tree, BasicBlock* block, Statement* stmt,
{
// If ref count was increased for struct field, ensure that the
// parent struct is still promoted.
- LclVarDsc* parentStruct = &lvaTable[varDsc->lvParentLcl];
+ LclVarDsc* parentStruct = lvaGetDesc(varDsc->lvParentLcl);
assert(!parentStruct->lvUndoneStructPromotion);
}
#endif
@@ -4318,7 +4279,7 @@ void Compiler::lvaMarkLocalVars()
slotsNeeded++;
lvaShadowSPslotsVar = lvaGrabTempWithImplicitUse(false DEBUGARG("lvaShadowSPslotsVar"));
- LclVarDsc* shadowSPslotsVar = &lvaTable[lvaShadowSPslotsVar];
+ LclVarDsc* shadowSPslotsVar = lvaGetDesc(lvaShadowSPslotsVar);
shadowSPslotsVar->lvType = TYP_BLK;
shadowSPslotsVar->lvExactSize = (slotsNeeded * TARGET_POINTER_SIZE);
}
@@ -4332,7 +4293,7 @@ void Compiler::lvaMarkLocalVars()
if (ehNeedsPSPSym())
{
lvaPSPSym = lvaGrabTempWithImplicitUse(false DEBUGARG("PSPSym"));
- LclVarDsc* lclPSPSym = &lvaTable[lvaPSPSym];
+ LclVarDsc* lclPSPSym = lvaGetDesc(lvaPSPSym);
lclPSPSym->lvType = TYP_I_IMPL;
lvaSetVarDoNotEnregister(lvaPSPSym DEBUGARG(DoNotEnregisterReason::VMNeedsStackAddr));
}
@@ -4355,7 +4316,7 @@ void Compiler::lvaMarkLocalVars()
if (compLocallocUsed)
{
lvaLocAllocSPvar = lvaGrabTempWithImplicitUse(false DEBUGARG("LocAllocSPvar"));
- LclVarDsc* locAllocSPvar = &lvaTable[lvaLocAllocSPvar];
+ LclVarDsc* locAllocSPvar = lvaGetDesc(lvaLocAllocSPvar);
locAllocSPvar->lvType = TYP_I_IMPL;
}
#endif // JIT32_GCENCODER
@@ -5211,7 +5172,7 @@ void Compiler::lvaFixVirtualFrameOffsets()
// We need to fix the offset of the PSPSym so there is no padding between it and the outgoing argument space.
// Without this code, lvaAlignFrame might have put the padding lower than the PSPSym, which would be between
// the PSPSym and the outgoing argument space.
- varDsc = &lvaTable[lvaPSPSym];
+ varDsc = lvaGetDesc(lvaPSPSym);
assert(varDsc->lvFramePointerBased); // We always access it RBP-relative.
assert(!varDsc->lvMustInit); // It is never "must init".
varDsc->SetStackOffset(codeGen->genCallerSPtoInitialSPdelta() + lvaLclSize(lvaOutgoingArgSpaceVar));
@@ -5283,7 +5244,7 @@ void Compiler::lvaFixVirtualFrameOffsets()
//
if (varDsc->lvIsStructField)
{
- LclVarDsc* parentvarDsc = &lvaTable[varDsc->lvParentLcl];
+ LclVarDsc* parentvarDsc = lvaGetDesc(varDsc->lvParentLcl);
lvaPromotionType promotionType = lvaGetPromotionType(parentvarDsc);
#if defined(TARGET_X86)
@@ -5357,7 +5318,7 @@ void Compiler::lvaFixVirtualFrameOffsets()
if (lvaOutgoingArgSpaceVar != BAD_VAR_NUM)
{
- varDsc = &lvaTable[lvaOutgoingArgSpaceVar];
+ varDsc = lvaGetDesc(lvaOutgoingArgSpaceVar);
varDsc->SetStackOffset(0);
varDsc->lvFramePointerBased = false;
varDsc->lvMustInit = false;
@@ -5672,7 +5633,6 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum,
unsigned fieldVarNum = BAD_VAR_NUM;
- noway_assert(lclNum < lvaCount);
LclVarDsc* varDsc = lvaGetDesc(lclNum);
noway_assert(varDsc->lvIsParam);
@@ -5765,7 +5725,6 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum,
unsigned fieldVarNum = BAD_VAR_NUM;
- noway_assert(lclNum < lvaCount);
LclVarDsc* varDsc = lvaGetDesc(lclNum);
noway_assert(varDsc->lvIsParam);
@@ -6262,10 +6221,27 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals()
if (lvaMonAcquired != BAD_VAR_NUM)
{
- // This var must go first, in what is called the 'frame header' for EnC so that it is
- // preserved when remapping occurs. See vm\eetwain.cpp for detailed comment specifying frame
- // layout requirements for EnC to work.
- stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaMonAcquired, lvaLclSize(lvaMonAcquired), stkOffs);
+ // For OSR we use the flag set up by the original method.
+ //
+ if (opts.IsOSR())
+ {
+ assert(info.compPatchpointInfo->HasMonitorAcquired());
+ int originalOffset = info.compPatchpointInfo->MonitorAcquiredOffset();
+ int offset = originalFrameStkOffs + originalOffset;
+
+ JITDUMP("---OSR--- V%02u (on old frame, monitor aquired) old rbp offset %d old frame offset %d new "
+ "virt offset %d\n",
+ lvaMonAcquired, originalOffset, originalFrameStkOffs, offset);
+
+ lvaTable[lvaMonAcquired].SetStackOffset(offset);
+ }
+ else
+ {
+ // This var must go first, in what is called the 'frame header' for EnC so that it is
+ // preserved when remapping occurs. See vm\eetwain.cpp for detailed comment specifying frame
+ // layout requirements for EnC to work.
+ stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaMonAcquired, lvaLclSize(lvaMonAcquired), stkOffs);
+ }
}
#ifdef JIT32_GCENCODER
@@ -6426,7 +6402,7 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals()
alloc_order[cur] = 0;
- noway_assert(cur < _countof(alloc_order));
+ noway_assert(cur < ArrLen(alloc_order));
// Force first pass to happen
UINT assignMore = 0xFFFFFFFF;
@@ -7143,7 +7119,7 @@ void Compiler::lvaAssignFrameOffsetsToPromotedStructs()
#endif // !defined(UNIX_AMD64_ABI) && !defined(TARGET_ARM) && !defined(TARGET_X86)
)
{
- LclVarDsc* parentvarDsc = &lvaTable[varDsc->lvParentLcl];
+ LclVarDsc* parentvarDsc = lvaGetDesc(varDsc->lvParentLcl);
lvaPromotionType promotionType = lvaGetPromotionType(parentvarDsc);
if (promotionType == PROMOTION_TYPE_INDEPENDENT)
@@ -7261,7 +7237,7 @@ int Compiler::lvaAllocateTemps(int stkOffs, bool mustDoubleAlign)
void Compiler::lvaDumpRegLocation(unsigned lclNum)
{
- LclVarDsc* varDsc = lvaTable + lclNum;
+ const LclVarDsc* varDsc = lvaGetDesc(lclNum);
#ifdef TARGET_ARM
if (varDsc->TypeGet() == TYP_DOUBLE)
@@ -7513,7 +7489,7 @@ void Compiler::lvaDumpEntry(unsigned lclNum, FrameLayoutState curState, size_t r
}
if (varDsc->lvIsStructField)
{
- LclVarDsc* parentvarDsc = &lvaTable[varDsc->lvParentLcl];
+ LclVarDsc* parentvarDsc = lvaGetDesc(varDsc->lvParentLcl);
#if !defined(TARGET_64BIT)
if (varTypeIsLong(parentvarDsc))
{
@@ -7738,8 +7714,7 @@ int Compiler::lvaGetSPRelativeOffset(unsigned varNum)
{
assert(!compLocallocUsed);
assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT);
- assert(varNum < lvaCount);
- const LclVarDsc* varDsc = lvaTable + varNum;
+ const LclVarDsc* varDsc = lvaGetDesc(varNum);
assert(varDsc->lvOnFrame);
int spRelativeOffset;
@@ -7767,8 +7742,7 @@ int Compiler::lvaGetSPRelativeOffset(unsigned varNum)
int Compiler::lvaGetCallerSPRelativeOffset(unsigned varNum)
{
assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT);
- assert(varNum < lvaCount);
- LclVarDsc* varDsc = lvaTable + varNum;
+ const LclVarDsc* varDsc = lvaGetDesc(varNum);
assert(varDsc->lvOnFrame);
return lvaToCallerSPRelativeOffset(varDsc->GetStackOffset(), varDsc->lvFramePointerBased);
@@ -7837,8 +7811,7 @@ int Compiler::lvaToCallerSPRelativeOffset(int offset, bool isFpBased, bool forRo
int Compiler::lvaGetInitialSPRelativeOffset(unsigned varNum)
{
assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT);
- assert(varNum < lvaCount);
- LclVarDsc* varDsc = lvaTable + varNum;
+ const LclVarDsc* varDsc = lvaGetDesc(varNum);
assert(varDsc->lvOnFrame);
return lvaToInitialSPRelativeOffset(varDsc->GetStackOffset(), varDsc->lvFramePointerBased);
diff --git a/src/coreclr/jit/likelyclass.cpp b/src/coreclr/jit/likelyclass.cpp
index 6e1c55b4c26747..fc6028c0ac4efe 100644
--- a/src/coreclr/jit/likelyclass.cpp
+++ b/src/coreclr/jit/likelyclass.cpp
@@ -93,7 +93,7 @@ LikelyClassHistogram::LikelyClassHistogram(INT_PTR* histogramEntries, unsigned e
if (!found)
{
- if (countHistogramElements >= _countof(m_histogram))
+ if (countHistogramElements >= ArrLen(m_histogram))
{
continue;
}
diff --git a/src/coreclr/jit/lir.cpp b/src/coreclr/jit/lir.cpp
index e72d834d2c5938..97f9e4f4807d2b 100644
--- a/src/coreclr/jit/lir.cpp
+++ b/src/coreclr/jit/lir.cpp
@@ -1486,7 +1486,7 @@ bool LIR::Range::CheckLIR(Compiler* compiler, bool checkUnusedValues) const
// Some nodes should never be marked unused, as they must be contained in the backend.
// These may be marked as unused during dead code elimination traversal, but they *must* be subsequently
// removed.
- assert(!node->IsUnusedValue() || !node->OperIs(GT_FIELD_LIST, GT_LIST, GT_INIT_VAL));
+ assert(!node->IsUnusedValue() || !node->OperIs(GT_FIELD_LIST, GT_INIT_VAL));
// Verify that the REVERSE_OPS flag is not set. NOTE: if we ever decide to reuse the bit assigned to
// GTF_REVERSE_OPS for an LIR-only flag we will need to move this check to the points at which we
diff --git a/src/coreclr/jit/liveness.cpp b/src/coreclr/jit/liveness.cpp
index bbe75e80b1d008..a0134f214f0f35 100644
--- a/src/coreclr/jit/liveness.cpp
+++ b/src/coreclr/jit/liveness.cpp
@@ -24,10 +24,8 @@ void Compiler::fgMarkUseDef(GenTreeLclVarCommon* tree)
{
assert((tree->OperIsLocal() && (tree->OperGet() != GT_PHI_ARG)) || tree->OperIsLocalAddr());
- const unsigned lclNum = tree->GetLclNum();
- assert(lclNum < lvaCount);
-
- LclVarDsc* const varDsc = &lvaTable[lclNum];
+ const unsigned lclNum = tree->GetLclNum();
+ LclVarDsc* const varDsc = lvaGetDesc(lclNum);
// We should never encounter a reference to a lclVar that has a zero refCnt.
if (varDsc->lvRefCnt() == 0 && (!varTypeIsPromotable(varDsc) || !varDsc->lvPromoted))
@@ -371,9 +369,7 @@ void Compiler::fgPerNodeLocalVarLiveness(GenTree* tree)
{
// Get the FrameRoot local and mark it as used.
- noway_assert(info.compLvFrameListRoot < lvaCount);
-
- LclVarDsc* varDsc = &lvaTable[info.compLvFrameListRoot];
+ LclVarDsc* varDsc = lvaGetDesc(info.compLvFrameListRoot);
if (varDsc->lvTracked)
{
@@ -525,8 +521,6 @@ void Compiler::fgPerBlockLocalVarLiveness()
assert((!opts.ShouldUsePInvokeHelpers()) || (info.compLvFrameListRoot == BAD_VAR_NUM));
if (!opts.ShouldUsePInvokeHelpers())
{
- noway_assert(info.compLvFrameListRoot < lvaCount);
-
// 32-bit targets always pop the frame in the epilog.
// For 64-bit targets, we only do this in the epilog for IL stubs;
// for non-IL stubs the frame is popped after every PInvoke call.
@@ -535,7 +529,7 @@ void Compiler::fgPerBlockLocalVarLiveness()
if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_IL_STUB))
#endif
{
- LclVarDsc* varDsc = &lvaTable[info.compLvFrameListRoot];
+ LclVarDsc* varDsc = lvaGetDesc(info.compLvFrameListRoot);
if (varDsc->lvTracked)
{
@@ -607,7 +601,7 @@ void Compiler::fgBeginScopeLife(VARSET_TP* inScope, VarScopeDsc* var)
{
assert(var);
- LclVarDsc* lclVarDsc1 = &lvaTable[var->vsdVarNum];
+ LclVarDsc* lclVarDsc1 = lvaGetDesc(var->vsdVarNum);
if (lclVarDsc1->lvTracked)
{
@@ -619,7 +613,7 @@ void Compiler::fgEndScopeLife(VARSET_TP* inScope, VarScopeDsc* var)
{
assert(var);
- LclVarDsc* lclVarDsc1 = &lvaTable[var->vsdVarNum];
+ LclVarDsc* lclVarDsc1 = lvaGetDesc(var->vsdVarNum);
if (lclVarDsc1->lvTracked)
{
@@ -869,7 +863,7 @@ void Compiler::fgExtendDbgLifetimes()
for (unsigned argNum = 0; argNum < info.compArgsCount; argNum++)
{
- LclVarDsc* argDsc = lvaTable + argNum;
+ LclVarDsc* argDsc = lvaGetDesc(argNum);
if (argDsc->lvPromoted)
{
lvaPromotionType promotionType = lvaGetPromotionType(argDsc);
@@ -879,7 +873,7 @@ void Compiler::fgExtendDbgLifetimes()
noway_assert(argDsc->lvFieldCnt == 1); // We only handle one field here
unsigned fieldVarNum = argDsc->lvFieldLclStart;
- argDsc = lvaTable + fieldVarNum;
+ argDsc = lvaGetDesc(fieldVarNum);
}
}
noway_assert(argDsc->lvIsParam);
@@ -896,7 +890,7 @@ void Compiler::fgExtendDbgLifetimes()
for (unsigned i = 0; i < lvaCount; i++)
{
- LclVarDsc* varDsc = &lvaTable[i];
+ LclVarDsc* varDsc = lvaGetDesc(i);
if (varTypeIsStruct(varDsc) && varDsc->lvTracked)
{
VarSetOps::AddElemD(this, noUnmarkVars, varDsc->lvVarIndex);
@@ -1460,9 +1454,7 @@ void Compiler::fgComputeLifeCall(VARSET_TP& life, GenTreeCall* call)
{
// Get the FrameListRoot local and make it live.
- noway_assert(info.compLvFrameListRoot < lvaCount);
-
- LclVarDsc* frameVarDsc = &lvaTable[info.compLvFrameListRoot];
+ LclVarDsc* frameVarDsc = lvaGetDesc(info.compLvFrameListRoot);
if (frameVarDsc->lvTracked)
{
@@ -1481,9 +1473,7 @@ void Compiler::fgComputeLifeCall(VARSET_TP& life, GenTreeCall* call)
assert((!opts.ShouldUsePInvokeHelpers()) || (info.compLvFrameListRoot == BAD_VAR_NUM));
if (!opts.ShouldUsePInvokeHelpers() && !call->IsSuppressGCTransition())
{
- noway_assert(info.compLvFrameListRoot < lvaCount);
-
- LclVarDsc* frameVarDsc = &lvaTable[info.compLvFrameListRoot];
+ LclVarDsc* frameVarDsc = lvaGetDesc(info.compLvFrameListRoot);
if (frameVarDsc->lvTracked)
{
@@ -1839,7 +1829,7 @@ void Compiler::fgComputeLife(VARSET_TP& life,
bool isDeadStore = fgComputeLifeLocal(life, keepAliveVars, tree);
if (isDeadStore)
{
- LclVarDsc* varDsc = &lvaTable[tree->AsLclVarCommon()->GetLclNum()];
+ LclVarDsc* varDsc = lvaGetDesc(tree->AsLclVarCommon());
bool doAgain = false;
if (fgRemoveDeadStore(&tree, varDsc, life, &doAgain, pStmtInfoDirty DEBUGARG(treeModf)))
diff --git a/src/coreclr/jit/loopcloning.cpp b/src/coreclr/jit/loopcloning.cpp
index 5bc02e47a50371..a1c71f932f5a65 100644
--- a/src/coreclr/jit/loopcloning.cpp
+++ b/src/coreclr/jit/loopcloning.cpp
@@ -1790,10 +1790,9 @@ void Compiler::optCloneLoop(unsigned loopInd, LoopCloneContext* context)
LoopDsc& loop = optLoopTable[loopInd];
- JITDUMP("\nCloning loop " FMT_LP ": [head: " FMT_BB ", first: " FMT_BB ", top: " FMT_BB ", entry: " FMT_BB
- ", bottom: " FMT_BB ", child: " FMT_LP "].\n",
- loopInd, loop.lpHead->bbNum, loop.lpFirst->bbNum, loop.lpTop->bbNum, loop.lpEntry->bbNum,
- loop.lpBottom->bbNum, loop.lpChild);
+ JITDUMP("\nCloning loop " FMT_LP ": [head: " FMT_BB ", top: " FMT_BB ", entry: " FMT_BB ", bottom: " FMT_BB
+ ", child: " FMT_LP "].\n",
+ loopInd, loop.lpHead->bbNum, loop.lpTop->bbNum, loop.lpEntry->bbNum, loop.lpBottom->bbNum, loop.lpChild);
// Determine the depth of the loop, so we can properly weight blocks added (outside the cloned loop blocks).
unsigned depth = optLoopDepth(loopInd);
diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp
index 2c9ed01a1ebebd..1c9febeb131440 100644
--- a/src/coreclr/jit/lower.cpp
+++ b/src/coreclr/jit/lower.cpp
@@ -139,8 +139,7 @@ GenTree* Lowering::LowerNode(GenTree* node)
case GT_AND:
case GT_OR:
case GT_XOR:
- ContainCheckBinary(node->AsOp());
- break;
+ return LowerBinaryArithmetic(node->AsOp());
case GT_MUL:
case GT_MULHI:
@@ -1332,9 +1331,8 @@ void Lowering::LowerArg(GenTreeCall* call, GenTree** ppArg)
{
if ((arg->OperGet() == GT_LCL_VAR) || (arg->OperGet() == GT_STORE_LCL_VAR))
{
- unsigned varNum = arg->AsLclVarCommon()->GetLclNum();
- LclVarDsc* varDsc = &comp->lvaTable[varNum];
- type = varDsc->lvType;
+ const LclVarDsc* varDsc = comp->lvaGetDesc(arg->AsLclVarCommon());
+ type = varDsc->lvType;
}
else if (arg->OperIs(GT_SIMD, GT_HWINTRINSIC))
{
@@ -3035,7 +3033,7 @@ void Lowering::LowerRet(GenTreeUnOp* ret)
{
ReturnTypeDesc retTypeDesc;
LclVarDsc* varDsc = nullptr;
- varDsc = comp->lvaGetDesc(retVal->AsLclVar()->GetLclNum());
+ varDsc = comp->lvaGetDesc(retVal->AsLclVar());
retTypeDesc.InitializeStructReturnType(comp, varDsc->GetStructHnd(), comp->info.compCallConv);
if (retTypeDesc.GetReturnRegCount() > 1)
{
@@ -3374,9 +3372,7 @@ void Lowering::LowerRetStruct(GenTreeUnOp* ret)
case GT_LCL_FLD:
{
#ifdef DEBUG
- GenTreeLclFld* lclFld = retVal->AsLclFld();
- unsigned lclNum = lclFld->GetLclNum();
- LclVarDsc* varDsc = comp->lvaGetDesc(lclNum);
+ LclVarDsc* varDsc = comp->lvaGetDesc(retVal->AsLclFld());
assert(varDsc->lvDoNotEnregister);
#endif
retVal->ChangeType(nativeReturnType);
@@ -5034,6 +5030,28 @@ bool Lowering::TryCreateAddrMode(GenTree* addr, bool isContainable, var_types ta
}
}
+#ifdef TARGET_ARM64
+ // Check if we can "contain" LEA(BFIZ) in order to extend 32bit index to 64bit as part of load/store.
+ if ((index != nullptr) && index->OperIs(GT_BFIZ) && index->gtGetOp1()->OperIs(GT_CAST) &&
+ index->gtGetOp2()->IsCnsIntOrI() && varTypeIsIntegral(targetType))
+ {
+ // BFIZ node is a binary op where op1 is GT_CAST and op2 is GT_CNS_INT
+ GenTreeCast* cast = index->gtGetOp1()->AsCast();
+ assert(cast->isContained());
+
+ const unsigned shiftBy = (unsigned)index->gtGetOp2()->AsIntCon()->IconValue();
+
+ // 'scale' and 'offset' have to be unset since we're going to use [base + index * SXTW/UXTW scale] form
+ // where there is no room for additional offsets/scales on ARM64. 'shiftBy' has to match target's width.
+ if (cast->CastOp()->TypeIs(TYP_INT) && cast->TypeIs(TYP_LONG) && (genTypeSize(targetType) == (1U << shiftBy)) &&
+ (scale == 1) && (offset == 0))
+ {
+ // TODO: Make sure that genCreateAddrMode marks such BFIZ candidates as GTF_DONT_CSE for better CQ.
+ MakeSrcContained(addrMode, index);
+ }
+ }
+#endif
+
JITDUMP("New addressing mode node:\n ");
DISPNODE(addrMode);
JITDUMP("\n");
@@ -5104,6 +5122,55 @@ GenTree* Lowering::LowerAdd(GenTreeOp* node)
return nullptr;
}
+//------------------------------------------------------------------------
+// LowerBinaryArithmetic: lowers the given binary arithmetic node.
+//
+// Recognizes opportunities for using target-independent "combined" nodes
+// (currently AND_NOT on ARMArch). Performs containment checks.
+//
+// Arguments:
+// node - the arithmetic node to lower
+//
+// Returns:
+// The next node to lower.
+//
+GenTree* Lowering::LowerBinaryArithmetic(GenTreeOp* node)
+{
+ // TODO-CQ-XArch: support BMI2 "andn" in codegen and condition
+ // this logic on the support for the instruction set on XArch.
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#ifdef TARGET_ARMARCH
+ if (comp->opts.OptimizationEnabled() && node->OperIs(GT_AND))
+ {
+ GenTree* opNode = nullptr;
+ GenTree* notNode = nullptr;
+ if (node->gtGetOp1()->OperIs(GT_NOT))
+ {
+ notNode = node->gtGetOp1();
+ opNode = node->gtGetOp2();
+ }
+ else if (node->gtGetOp2()->OperIs(GT_NOT))
+ {
+ notNode = node->gtGetOp2();
+ opNode = node->gtGetOp1();
+ }
+
+ if (notNode != nullptr)
+ {
+ node->gtOp1 = opNode;
+ node->gtOp2 = notNode->AsUnOp()->gtGetOp1();
+ node->ChangeOper(GT_AND_NOT);
+ BlockRange().Remove(notNode);
+ }
+ }
+#endif // TARGET_ARMARCH
+
+ ContainCheckBinary(node);
+
+ return node->gtNext;
+}
+
//------------------------------------------------------------------------
// LowerUnsignedDivOrMod: Lowers a GT_UDIV/GT_UMOD node.
//
@@ -5753,6 +5820,35 @@ void Lowering::LowerShift(GenTreeOp* shift)
shift->gtOp2->ClearContained();
}
ContainCheckShiftRotate(shift);
+
+#ifdef TARGET_ARM64
+ // Try to recognize ubfiz/sbfiz idiom in LSH(CAST(X), CNS) tree
+ if (comp->opts.OptimizationEnabled() && shift->OperIs(GT_LSH) && shift->gtGetOp1()->OperIs(GT_CAST) &&
+ shift->gtGetOp2()->IsCnsIntOrI() && !shift->isContained())
+ {
+ GenTreeIntCon* cns = shift->gtGetOp2()->AsIntCon();
+ GenTreeCast* cast = shift->gtGetOp1()->AsCast();
+
+ if (!cast->isContained() && !cast->IsRegOptional() && !cast->gtOverflow() &&
+ // Smaller CastOp is most likely an IND(X) node which is lowered to a zero-extend load
+ cast->CastOp()->TypeIs(TYP_LONG, TYP_INT))
+ {
+ // Cast is either "TYP_LONG <- TYP_INT" or "TYP_INT <- %SMALL_INT% <- TYP_INT" (signed or unsigned)
+ unsigned dstBits = genTypeSize(cast) * BITS_PER_BYTE;
+ unsigned srcBits = varTypeIsSmall(cast->CastToType()) ? genTypeSize(cast->CastToType()) * BITS_PER_BYTE
+ : genTypeSize(cast->CastOp()) * BITS_PER_BYTE;
+ assert(!cast->CastOp()->isContained());
+
+ // It has to be an upcast and CNS must be in [1..srcBits) range
+ if ((srcBits < dstBits) && (cns->IconValue() > 0) && (cns->IconValue() < srcBits))
+ {
+ JITDUMP("Recognized ubfix/sbfix pattern in LSH(CAST, CNS). Changing op to GT_BFIZ");
+ shift->ChangeOper(GT_BFIZ);
+ MakeSrcContained(shift, cast);
+ }
+ }
+ }
+#endif
}
void Lowering::WidenSIMD12IfNecessary(GenTreeLclVarCommon* node)
@@ -5788,8 +5884,7 @@ void Lowering::WidenSIMD12IfNecessary(GenTreeLclVarCommon* node)
// as a return buffer pointer. The callee doesn't write the high 4 bytes, and we don't need to clear
// it either.
- unsigned varNum = node->AsLclVarCommon()->GetLclNum();
- LclVarDsc* varDsc = &comp->lvaTable[varNum];
+ LclVarDsc* varDsc = comp->lvaGetDesc(node->AsLclVarCommon());
if (comp->lvaMapSimd12ToSimd16(varDsc))
{
@@ -6121,9 +6216,7 @@ void Lowering::CheckNode(Compiler* compiler, GenTree* node)
case GT_LCL_VAR:
case GT_STORE_LCL_VAR:
{
- GenTreeLclVar* lclVar = node->AsLclVar();
- const unsigned lclNum = lclVar->GetLclNum();
- const LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum);
+ const LclVarDsc* varDsc = compiler->lvaGetDesc(node->AsLclVar());
#if defined(FEATURE_SIMD) && defined(TARGET_64BIT)
if (node->TypeIs(TYP_SIMD12))
{
@@ -6167,9 +6260,7 @@ void Lowering::CheckNode(Compiler* compiler, GenTree* node)
case GT_LCL_FLD:
case GT_STORE_LCL_FLD:
{
- GenTreeLclFld* lclFld = node->AsLclFld();
- const unsigned lclNum = lclFld->GetLclNum();
- const LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum);
+ const LclVarDsc* varDsc = compiler->lvaGetDesc(node->AsLclFld());
assert(varDsc->lvDoNotEnregister);
}
break;
@@ -6612,8 +6703,7 @@ void Lowering::ContainCheckRet(GenTreeUnOp* ret)
// op1 must be either a lclvar or a multi-reg returning call
if (op1->OperGet() == GT_LCL_VAR)
{
- GenTreeLclVarCommon* lclVarCommon = op1->AsLclVarCommon();
- LclVarDsc* varDsc = &(comp->lvaTable[lclVarCommon->GetLclNum()]);
+ const LclVarDsc* varDsc = comp->lvaGetDesc(op1->AsLclVarCommon());
// This must be a multi-reg return or an HFA of a single element.
assert(varDsc->lvIsMultiRegRet || (varDsc->lvIsHfa() && varTypeIsValidHfaType(varDsc->lvType)));
@@ -6693,6 +6783,51 @@ void Lowering::LowerStoreIndirCommon(GenTreeStoreInd* ind)
TryCreateAddrMode(ind->Addr(), true, ind->TypeGet());
if (!comp->codeGen->gcInfo.gcIsWriteBarrierStoreIndNode(ind))
{
+ if (varTypeIsFloating(ind) && ind->Data()->IsCnsFltOrDbl())
+ {
+ // Optimize *x = DCON to *x = ICON which can be slightly faster and/or smaller.
+ GenTree* data = ind->Data();
+ double dblCns = data->AsDblCon()->gtDconVal;
+ ssize_t intCns = 0;
+ var_types type = TYP_UNKNOWN;
+ // XARCH: we can always contain the immediates.
+ // ARM64: zero can always be contained, other cases will use immediates from the data
+ // section and it is not a clear win to switch them to inline integers.
+ // ARM: FP constants are assembled from integral ones, so it is always profitable
+ // to directly use the integers as it avoids the int -> float conversion.
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#if defined(TARGET_XARCH) || defined(TARGET_ARM)
+ bool shouldSwitchToInteger = true;
+#else // TARGET_ARM64
+ bool shouldSwitchToInteger = !data->IsCnsNonZeroFltOrDbl();
+#endif
+
+ if (shouldSwitchToInteger)
+ {
+ if (ind->TypeIs(TYP_FLOAT))
+ {
+ float fltCns = static_cast(dblCns); // should be a safe round-trip
+ intCns = static_cast(*reinterpret_cast(&fltCns));
+ type = TYP_INT;
+ }
+#ifdef TARGET_64BIT
+ else
+ {
+ assert(ind->TypeIs(TYP_DOUBLE));
+ intCns = static_cast(*reinterpret_cast(&dblCns));
+ type = TYP_LONG;
+ }
+#endif
+ }
+
+ if (type != TYP_UNKNOWN)
+ {
+ data->BashToConst(intCns, type);
+ ind->ChangeType(type);
+ }
+ }
+
LowerStoreIndir(ind);
}
}
@@ -6762,7 +6897,7 @@ void Lowering::TransformUnusedIndirection(GenTreeIndir* ind, Compiler* comp, Bas
#ifdef TARGET_ARM64
bool useNullCheck = true;
#elif TARGET_ARM
- bool useNullCheck = false;
+ bool useNullCheck = false;
#else // TARGET_XARCH
bool useNullCheck = !ind->Addr()->isContained();
#endif // !TARGET_XARCH
diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h
index b64f4a64109448..ed0ecc56619708 100644
--- a/src/coreclr/jit/lower.h
+++ b/src/coreclr/jit/lower.h
@@ -297,6 +297,7 @@ class Lowering final : public Phase
void LowerStoreIndir(GenTreeStoreInd* node);
GenTree* LowerAdd(GenTreeOp* node);
GenTree* LowerMul(GenTreeOp* mul);
+ GenTree* LowerBinaryArithmetic(GenTreeOp* node);
bool LowerUnsignedDivOrMod(GenTreeOp* divMod);
GenTree* LowerConstIntDivOrMod(GenTree* node);
GenTree* LowerSignedDivOrMod(GenTree* node);
@@ -595,7 +596,7 @@ class Lowering final : public Phase
// This ensures that the local's value is valid on-stack as expected for a *LCL_FLD.
void verifyLclFldDoNotEnregister(unsigned lclNum)
{
- LclVarDsc* varDsc = &(comp->lvaTable[lclNum]);
+ LclVarDsc* varDsc = comp->lvaGetDesc(lclNum);
// Do a couple of simple checks before setting lvDoNotEnregister.
// This may not cover all cases in 'isRegCandidate()' but we don't want to
// do an expensive check here. For non-candidates it is not harmful to set lvDoNotEnregister.
diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp
index 2f1d32e289a5c2..345edc402cc3fb 100644
--- a/src/coreclr/jit/lowerarmarch.cpp
+++ b/src/coreclr/jit/lowerarmarch.cpp
@@ -48,13 +48,22 @@ bool Lowering::IsCallTargetInRange(void* addr)
// True if the immediate can be folded into an instruction,
// for example small enough and non-relocatable.
//
-// TODO-CQ: we can contain a floating point 0.0 constant in a compare instruction
-// (vcmp on arm, fcmp on arm64).
-//
bool Lowering::IsContainableImmed(GenTree* parentNode, GenTree* childNode) const
{
if (!varTypeIsFloating(parentNode->TypeGet()))
{
+#ifdef TARGET_ARM64
+ if (parentNode->OperIsRelop() && childNode->IsFPZero())
+ {
+ // Contain 0.0 constant in fcmp on arm64
+ // TODO: Enable for arm too (vcmp)
+
+ // We currently don't emit these for floating points
+ assert(!parentNode->OperIs(GT_TEST_EQ, GT_TEST_NE));
+ return true;
+ }
+#endif
+
// Make sure we have an actual immediate
if (!childNode->IsCnsIntOrI())
return false;
@@ -600,27 +609,25 @@ void Lowering::LowerSIMD(GenTreeSIMD* simdNode)
//
void Lowering::LowerHWIntrinsicFusedMultiplyAddScalar(GenTreeHWIntrinsic* node)
{
- assert(node->gtHWIntrinsicId == NI_AdvSimd_FusedMultiplyAddScalar);
-
- const HWIntrinsic intrin(node);
+ assert(node->GetHWIntrinsicId() == NI_AdvSimd_FusedMultiplyAddScalar);
- GenTree* op1 = intrin.op1;
- GenTree* op2 = intrin.op2;
- GenTree* op3 = intrin.op3;
+ GenTree* op1 = node->Op(1);
+ GenTree* op2 = node->Op(2);
+ GenTree* op3 = node->Op(3);
auto lowerOperand = [this](GenTree* op) {
bool wasNegated = false;
if (op->OperIsHWIntrinsic() &&
- ((op->AsHWIntrinsic()->gtHWIntrinsicId == NI_AdvSimd_Arm64_DuplicateToVector64) ||
- (op->AsHWIntrinsic()->gtHWIntrinsicId == NI_Vector64_CreateScalarUnsafe)))
+ ((op->AsHWIntrinsic()->GetHWIntrinsicId() == NI_AdvSimd_Arm64_DuplicateToVector64) ||
+ (op->AsHWIntrinsic()->GetHWIntrinsicId() == NI_Vector64_CreateScalarUnsafe)))
{
GenTreeHWIntrinsic* createVector64 = op->AsHWIntrinsic();
- GenTree* valueOp = createVector64->gtGetOp1();
+ GenTree* valueOp = createVector64->Op(1);
if (valueOp->OperIs(GT_NEG))
{
- createVector64->gtOp1 = valueOp->gtGetOp1();
+ createVector64->Op(1) = valueOp->gtGetOp1();
BlockRange().Remove(valueOp);
wasNegated = true;
}
@@ -637,16 +644,16 @@ void Lowering::LowerHWIntrinsicFusedMultiplyAddScalar(GenTreeHWIntrinsic* node)
{
if (op2WasNegated != op3WasNegated)
{
- node->gtHWIntrinsicId = NI_AdvSimd_FusedMultiplyAddNegatedScalar;
+ node->ChangeHWIntrinsicId(NI_AdvSimd_FusedMultiplyAddNegatedScalar);
}
else
{
- node->gtHWIntrinsicId = NI_AdvSimd_FusedMultiplySubtractNegatedScalar;
+ node->ChangeHWIntrinsicId(NI_AdvSimd_FusedMultiplySubtractNegatedScalar);
}
}
else if (op2WasNegated != op3WasNegated)
{
- node->gtHWIntrinsicId = NI_AdvSimd_FusedMultiplySubtractScalar;
+ node->ChangeHWIntrinsicId(NI_AdvSimd_FusedMultiplySubtractScalar);
}
}
@@ -667,7 +674,7 @@ void Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node)
node->gtType = TYP_SIMD16;
}
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
switch (intrinsicId)
{
@@ -680,7 +687,7 @@ void Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node)
// the same intrinsic as when it came in.
LowerHWIntrinsicCreate(node);
- assert(!node->OperIsHWIntrinsic() || (node->gtHWIntrinsicId != intrinsicId));
+ assert(!node->OperIsHWIntrinsic() || (node->GetHWIntrinsicId() != intrinsicId));
LowerNode(node);
return;
}
@@ -730,18 +737,19 @@ void Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node)
// This check may end up modifying node->gtOp1 if it is a cast node that can be removed
bool Lowering::IsValidConstForMovImm(GenTreeHWIntrinsic* node)
{
- assert((node->gtHWIntrinsicId == NI_Vector64_Create) || (node->gtHWIntrinsicId == NI_Vector128_Create) ||
- (node->gtHWIntrinsicId == NI_Vector64_CreateScalarUnsafe) ||
- (node->gtHWIntrinsicId == NI_Vector128_CreateScalarUnsafe) ||
- (node->gtHWIntrinsicId == NI_AdvSimd_DuplicateToVector64) ||
- (node->gtHWIntrinsicId == NI_AdvSimd_DuplicateToVector128) ||
- (node->gtHWIntrinsicId == NI_AdvSimd_Arm64_DuplicateToVector64) ||
- (node->gtHWIntrinsicId == NI_AdvSimd_Arm64_DuplicateToVector128));
- assert(HWIntrinsicInfo::lookupNumArgs(node) == 1);
-
- GenTree* op1 = node->gtOp1;
+ assert((node->GetHWIntrinsicId() == NI_Vector64_Create) || (node->GetHWIntrinsicId() == NI_Vector128_Create) ||
+ (node->GetHWIntrinsicId() == NI_Vector64_CreateScalarUnsafe) ||
+ (node->GetHWIntrinsicId() == NI_Vector128_CreateScalarUnsafe) ||
+ (node->GetHWIntrinsicId() == NI_AdvSimd_DuplicateToVector64) ||
+ (node->GetHWIntrinsicId() == NI_AdvSimd_DuplicateToVector128) ||
+ (node->GetHWIntrinsicId() == NI_AdvSimd_Arm64_DuplicateToVector64) ||
+ (node->GetHWIntrinsicId() == NI_AdvSimd_Arm64_DuplicateToVector128));
+ assert(node->GetOperandCount() == 1);
+
+ GenTree* op1 = node->Op(1);
GenTree* castOp = nullptr;
+ // TODO-Casts: why don't we fold the casts? MinOpts?
if (varTypeIsIntegral(node->GetSimdBaseType()) && op1->OperIs(GT_CAST))
{
// We will sometimes get a cast around a constant value (such as for
@@ -764,8 +772,8 @@ bool Lowering::IsValidConstForMovImm(GenTreeHWIntrinsic* node)
// We found a containable immediate under
// a cast, so remove the cast from the LIR.
- BlockRange().Remove(node->gtOp1);
- node->gtOp1 = op1;
+ BlockRange().Remove(node->Op(1));
+ node->Op(1) = op1;
}
return true;
}
@@ -791,7 +799,7 @@ bool Lowering::IsValidConstForMovImm(GenTreeHWIntrinsic* node)
//
void Lowering::LowerHWIntrinsicCmpOp(GenTreeHWIntrinsic* node, genTreeOps cmpOp)
{
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
CorInfoType simdBaseJitType = node->GetSimdBaseJitType();
var_types simdBaseType = node->GetSimdBaseType();
unsigned simdSize = node->GetSimdSize();
@@ -811,8 +819,8 @@ void Lowering::LowerHWIntrinsicCmpOp(GenTreeHWIntrinsic* node, genTreeOps cmpOp)
// /--* op1 simd
// node = * HWINTRINSIC simd T op_Equality
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = node->gtGetOp2();
+ GenTree* op1 = node->Op(1);
+ GenTree* op2 = node->Op(2);
NamedIntrinsic cmpIntrinsic;
@@ -886,9 +894,9 @@ void Lowering::LowerHWIntrinsicCmpOp(GenTreeHWIntrinsic* node, genTreeOps cmpOp)
node->ChangeOper(cmpOp);
- node->gtType = TYP_INT;
- node->gtOp1 = val;
- node->gtOp2 = zroCns;
+ node->gtType = TYP_INT;
+ node->AsOp()->gtOp1 = val;
+ node->AsOp()->gtOp2 = zroCns;
// The CompareEqual will set (condition is true) or clear (condition is false) all bits of the respective element
// The MinAcross then ensures we get either all bits set (all conditions are true) or clear (any condition is false)
@@ -906,13 +914,20 @@ void Lowering::LowerHWIntrinsicCmpOp(GenTreeHWIntrinsic* node, genTreeOps cmpOp)
//----------------------------------------------------------------------------------------------
// Lowering::LowerHWIntrinsicCreate: Lowers a Vector64 or Vector128 Create call
//
+// Performs the following transformations:
+// 1. If all the arguments are constant (including the broadcast case), the vector
+// will be loaded from the data section, or turned into Zero/AllBitsSet, if possible.
+// 2. Non-constant broadcasts (argCnt == 1) are turned into DuplicateToVector intrinsics.
+// 3. Remaining cases get a chain of "Insert"s, from the second element to the last, where
+// the vector to be inserted into is created with CreateUnsafeScalar from the first element.
+//
// Arguments:
// node - The hardware intrinsic node.
//
void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
{
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
- var_types simdType = node->gtType;
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
+ var_types simdType = node->TypeGet();
CorInfoType simdBaseJitType = node->GetSimdBaseJitType();
var_types simdBaseType = node->GetSimdBaseType();
unsigned simdSize = node->GetSimdSize();
@@ -929,109 +944,47 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
assert(varTypeIsArithmetic(simdBaseType));
assert(simdSize != 0);
- GenTreeArgList* argList = nullptr;
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = node->gtGetOp2();
+ size_t argCnt = node->GetOperandCount();
+ size_t cnsArgCnt = 0;
- // Spare GenTrees to be used for the lowering logic below
- // Defined upfront to avoid naming conflicts, etc...
- GenTree* idx = nullptr;
- GenTree* tmp1 = nullptr;
- GenTree* tmp2 = nullptr;
- GenTree* tmp3 = nullptr;
-
- assert(op1 != nullptr);
-
- unsigned argCnt = 0;
- unsigned cnsArgCnt = 0;
-
- if (op1->OperIsList())
+ // These intrinsics are meant to set the same value to every element.
+ if ((argCnt == 1) && HandleArgForHWIntrinsicCreate(node->Op(1), 0, vecCns, simdBaseType))
{
- assert(op2 == nullptr);
-
- for (argList = op1->AsArgList(); argList != nullptr; argList = argList->Rest())
+ // Now assign the rest of the arguments.
+ for (unsigned i = 1; i < simdSize / genTypeSize(simdBaseType); i++)
{
- if (HandleArgForHWIntrinsicCreate(argList->Current(), argCnt, vecCns, simdBaseType))
- {
- cnsArgCnt += 1;
- }
- argCnt += 1;
+ HandleArgForHWIntrinsicCreate(node->Op(1), i, vecCns, simdBaseType);
}
+
+ cnsArgCnt = 1;
}
else
{
- if (HandleArgForHWIntrinsicCreate(op1, argCnt, vecCns, simdBaseType))
+ for (unsigned i = 1; i <= argCnt; i++)
{
- cnsArgCnt += 1;
- }
- argCnt += 1;
-
- if (op2 != nullptr)
- {
- if (HandleArgForHWIntrinsicCreate(op2, argCnt, vecCns, simdBaseType))
- {
- cnsArgCnt += 1;
- }
- argCnt += 1;
- }
- else if (cnsArgCnt == 1)
- {
- // These intrinsics are meant to set the same value to every element
- // so we'll just specially handle it here and copy it into the remaining
- // indices.
-
- for (unsigned i = 1; i < simdSize / genTypeSize(simdBaseType); i++)
+ if (HandleArgForHWIntrinsicCreate(node->Op(i), i - 1, vecCns, simdBaseType))
{
- HandleArgForHWIntrinsicCreate(op1, i, vecCns, simdBaseType);
+ cnsArgCnt++;
}
}
}
assert((argCnt == 1) || (argCnt == (simdSize / genTypeSize(simdBaseType))));
- if ((argCnt == cnsArgCnt) && (argCnt == 1))
+ // Check if we have a cast that we can remove. Note that "IsValidConstForMovImm"
+ // will reset Op(1) if it finds such a cast, so we do not need to handle it here.
+ // TODO-Casts: why are casts from constants checked for here?
+ if ((argCnt == cnsArgCnt) && (argCnt == 1) && IsValidConstForMovImm(node))
{
- GenTree* castOp = nullptr;
-
- if (varTypeIsIntegral(simdBaseType) && op1->OperIs(GT_CAST))
- {
- // We will sometimes get a cast around a constant value (such as for
- // certain long constants) which would block the below containment.
- // So we will temporarily check what the cast is from instead so we
- // can catch those cases as well.
-
- castOp = op1->AsCast()->CastOp();
- op1 = castOp;
- }
-
- if (IsValidConstForMovImm(node))
- {
- // Set the cnsArgCnt to zero so we get lowered to a DuplicateToVector
- // intrinsic, which will itself mark the node as contained.
- cnsArgCnt = 0;
-
- // Reacquire op1 as the above check may have removed a cast node and
- // changed op1.
- op1 = node->gtOp1;
- }
+ // Set the cnsArgCnt to zero so we get lowered to a DuplicateToVector
+ // intrinsic, which will itself mark the node as contained.
+ cnsArgCnt = 0;
}
if (argCnt == cnsArgCnt)
{
- if (op1->OperIsList())
+ for (GenTree* arg : node->Operands())
{
- for (argList = op1->AsArgList(); argList != nullptr; argList = argList->Rest())
- {
- BlockRange().Remove(argList->Current());
- }
- }
- else
- {
- BlockRange().Remove(op1);
-
- if (op2 != nullptr)
- {
- BlockRange().Remove(op2);
- }
+ BlockRange().Remove(arg);
}
assert((simdSize == 8) || (simdSize == 16));
@@ -1043,16 +996,12 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
if (vecCns.i64[0] == 0)
{
- node->gtOp1 = nullptr;
- node->gtOp2 = nullptr;
- node->gtHWIntrinsicId = (simdSize == 8) ? NI_Vector64_get_Zero : NI_Vector128_get_Zero;
+ node->ResetHWIntrinsicId((simdSize == 8) ? NI_Vector64_get_Zero : NI_Vector128_get_Zero);
return;
}
else if (vecCns.i64[0] == -1)
{
- node->gtOp1 = nullptr;
- node->gtOp2 = nullptr;
- node->gtHWIntrinsicId = (simdSize == 8) ? NI_Vector64_get_AllBitsSet : NI_Vector128_get_AllBitsSet;
+ node->ResetHWIntrinsicId((simdSize == 8) ? NI_Vector64_get_AllBitsSet : NI_Vector128_get_AllBitsSet);
return;
}
}
@@ -1067,7 +1016,7 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
BlockRange().InsertBefore(node, clsVarAddr);
node->ChangeOper(GT_IND);
- node->gtOp1 = clsVarAddr;
+ node->AsOp()->gtOp1 = clsVarAddr;
// TODO-ARM64-CQ: We should be able to modify at least the paths that use Insert to trivially support partial
// vector constants. With this, we can create a constant if say 50% of the inputs are also constant and just
@@ -1090,13 +1039,13 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
if (varTypeIsLong(simdBaseType) || (simdBaseType == TYP_DOUBLE))
{
- node->gtHWIntrinsicId =
- (simdType == TYP_SIMD8) ? NI_AdvSimd_Arm64_DuplicateToVector64 : NI_AdvSimd_Arm64_DuplicateToVector128;
+ node->ChangeHWIntrinsicId((simdType == TYP_SIMD8) ? NI_AdvSimd_Arm64_DuplicateToVector64
+ : NI_AdvSimd_Arm64_DuplicateToVector128);
}
else
{
- node->gtHWIntrinsicId =
- (simdType == TYP_SIMD8) ? NI_AdvSimd_DuplicateToVector64 : NI_AdvSimd_DuplicateToVector128;
+ node->ChangeHWIntrinsicId((simdType == TYP_SIMD8) ? NI_AdvSimd_DuplicateToVector64
+ : NI_AdvSimd_DuplicateToVector128);
}
return;
}
@@ -1107,13 +1056,6 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
// +--* opN T
// node = * HWINTRINSIC simd T Create
- if (op1->OperIsList())
- {
- argList = op1->AsArgList();
- op1 = argList->Current();
- argList = argList->Rest();
- }
-
// We will be constructing the following parts:
// /--* op1 T
// tmp1 = * HWINTRINSIC simd8 T CreateScalarUnsafe
@@ -1123,67 +1065,50 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
// var tmp1 = Vector64.CreateScalarUnsafe(op1);
// ...
- NamedIntrinsic createScalarUnsafe =
+ NamedIntrinsic createScalar =
(simdType == TYP_SIMD8) ? NI_Vector64_CreateScalarUnsafe : NI_Vector128_CreateScalarUnsafe;
- tmp1 = comp->gtNewSimdHWIntrinsicNode(simdType, op1, createScalarUnsafe, simdBaseJitType, simdSize);
- BlockRange().InsertAfter(op1, tmp1);
+ GenTree* tmp1 = comp->gtNewSimdHWIntrinsicNode(simdType, node->Op(1), createScalar, simdBaseJitType, simdSize);
+ BlockRange().InsertAfter(node->Op(1), tmp1);
LowerNode(tmp1);
+ // We will be constructing the following parts:
+ // ...
+ // idx = CNS_INT int N
+ // /--* tmp1 simd
+ // +--* idx int
+ // +--* opN T
+ // tmp1 = * HWINTRINSIC simd T Insert
+ // ...
+
+ // This is roughly the following managed code:
+ // ...
+ // tmp1 = AdvSimd.Insert(tmp1, N, opN);
+ // ...
+
unsigned N = 0;
GenTree* opN = nullptr;
+ GenTree* idx = nullptr;
for (N = 1; N < argCnt - 1; N++)
{
- // We will be constructing the following parts:
- // ...
- // idx = CNS_INT int N
- // /--* tmp1 simd
- // +--* idx int
- // +--* opN T
- // tmp1 = * HWINTRINSIC simd T Insert
- // ...
-
- // This is roughly the following managed code:
- // ...
- // tmp1 = AdvSimd.Insert(tmp1, N, opN);
- // ...
-
- opN = argList->Current();
-
- idx = comp->gtNewIconNode(N, TYP_INT);
+ opN = node->Op(N + 1);
+ idx = comp->gtNewIconNode(N);
BlockRange().InsertBefore(opN, idx);
tmp1 = comp->gtNewSimdHWIntrinsicNode(simdType, tmp1, idx, opN, NI_AdvSimd_Insert, simdBaseJitType, simdSize);
BlockRange().InsertAfter(opN, tmp1);
LowerNode(tmp1);
-
- argList = argList->Rest();
}
assert(N == (argCnt - 1));
- // We will be constructing the following parts:
- // idx = CNS_INT int N
- // /--* tmp1 simd
- // +--* idx int
- // +--* opN T
- // node = * HWINTRINSIC simd T Insert
-
- // This is roughly the following managed code:
- // ...
- // tmp1 = AdvSimd.Insert(tmp1, N, opN);
- // ...
-
- opN = (argCnt == 2) ? op2 : argList->Current();
-
- idx = comp->gtNewIconNode(N, TYP_INT);
+ // For the last insert, we will reuse the existing node and so handle it here, outside the loop.
+ opN = node->Op(argCnt);
+ idx = comp->gtNewIconNode(N);
BlockRange().InsertBefore(opN, idx);
- node->gtOp1 = comp->gtNewArgList(tmp1, idx, opN);
- node->gtOp2 = nullptr;
-
- node->gtHWIntrinsicId = NI_AdvSimd_Insert;
+ node->ResetHWIntrinsicId(NI_AdvSimd_Insert, comp, tmp1, idx, opN);
}
//----------------------------------------------------------------------------------------------
@@ -1194,7 +1119,7 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
//
void Lowering::LowerHWIntrinsicDot(GenTreeHWIntrinsic* node)
{
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
CorInfoType simdBaseJitType = node->GetSimdBaseJitType();
var_types simdBaseType = node->GetSimdBaseType();
unsigned simdSize = node->GetSimdSize();
@@ -1205,12 +1130,8 @@ void Lowering::LowerHWIntrinsicDot(GenTreeHWIntrinsic* node)
assert(varTypeIsArithmetic(simdBaseType));
assert(simdSize != 0);
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = node->gtGetOp2();
-
- assert(op1 != nullptr);
- assert(op2 != nullptr);
- assert(!op1->OperIsList());
+ GenTree* op1 = node->Op(1);
+ GenTree* op2 = node->Op(2);
// Spare GenTrees to be used for the lowering logic below
// Defined upfront to avoid naming conflicts, etc...
@@ -1297,10 +1218,10 @@ void Lowering::LowerHWIntrinsicDot(GenTreeHWIntrinsic* node)
// var tmp2 = tmp1;
// ...
- node->gtOp1 = tmp1;
- LIR::Use tmp1Use(BlockRange(), &node->gtOp1, node);
+ node->Op(1) = tmp1;
+ LIR::Use tmp1Use(BlockRange(), &node->Op(1), node);
ReplaceWithLclVar(tmp1Use);
- tmp1 = node->gtOp1;
+ tmp1 = node->Op(1);
tmp2 = comp->gtClone(tmp1);
BlockRange().InsertAfter(tmp1, tmp2);
@@ -1378,10 +1299,10 @@ void Lowering::LowerHWIntrinsicDot(GenTreeHWIntrinsic* node)
// var tmp1 = AdvSimd.Arm64.AddPairwise(tmp1, tmp2);
// ...
- node->gtOp1 = tmp1;
- LIR::Use tmp1Use(BlockRange(), &node->gtOp1, node);
+ node->Op(1) = tmp1;
+ LIR::Use tmp1Use(BlockRange(), &node->Op(1), node);
ReplaceWithLclVar(tmp1Use);
- tmp1 = node->gtOp1;
+ tmp1 = node->Op(1);
tmp2 = comp->gtClone(tmp1);
BlockRange().InsertAfter(tmp1, tmp2);
@@ -1424,12 +1345,8 @@ void Lowering::LowerHWIntrinsicDot(GenTreeHWIntrinsic* node)
// ...
// return tmp2.ToScalar();
- node->gtOp1 = tmp2;
- node->gtOp2 = nullptr;
-
- node->gtHWIntrinsicId = (simdSize == 8) ? NI_Vector64_ToScalar : NI_Vector128_ToScalar;
+ node->ResetHWIntrinsicId((simdSize == 8) ? NI_Vector64_ToScalar : NI_Vector128_ToScalar, tmp2);
LowerNode(node);
-
return;
}
#endif // FEATURE_HW_INTRINSICS
@@ -1605,6 +1522,7 @@ void Lowering::ContainCheckBinary(GenTreeOp* node)
}
// If both 'a' and 'b' are GT_NEG - MADD will be emitted.
+ node->ChangeOper(GT_MADD);
MakeSrcContained(node, mul);
}
}
@@ -1696,7 +1614,7 @@ void Lowering::ContainCheckStoreLoc(GenTreeLclVarCommon* storeLoc) const
MakeSrcContained(storeLoc, op1);
if (op1->IsSIMDZero())
{
- MakeSrcContained(op1, op1->gtGetOp1());
+ MakeSrcContained(op1, op1->AsSIMD()->Op(1));
}
}
return;
@@ -1780,11 +1698,11 @@ void Lowering::ContainCheckBoundsChk(GenTreeBoundsChk* node)
//
void Lowering::ContainCheckSIMD(GenTreeSIMD* simdNode)
{
- switch (simdNode->gtSIMDIntrinsicID)
+ switch (simdNode->GetSIMDIntrinsicId())
{
case SIMDIntrinsicInit:
{
- GenTree* op1 = simdNode->AsOp()->gtOp1;
+ GenTree* op1 = simdNode->Op(1);
if (op1->IsIntegralConst(0))
{
MakeSrcContained(simdNode, op1);
@@ -1794,7 +1712,7 @@ void Lowering::ContainCheckSIMD(GenTreeSIMD* simdNode)
case SIMDIntrinsicInitArray:
// We have an array and an index, which may be contained.
- CheckImmedAndMakeContained(simdNode, simdNode->gtGetOp2());
+ CheckImmedAndMakeContained(simdNode, simdNode->Op(2));
break;
default:
@@ -1919,10 +1837,7 @@ void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node)
case NI_AdvSimd_Arm64_DuplicateToVector128:
if (IsValidConstForMovImm(node))
{
- // Use node->gtOp1 as the above check may
- // have removed a cast node and changed op1
-
- MakeSrcContained(node, node->gtOp1);
+ MakeSrcContained(node, node->Op(1));
}
break;
diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp
index 8f15e9a04fb8eb..548a6033d872ba 100644
--- a/src/coreclr/jit/lowerxarch.cpp
+++ b/src/coreclr/jit/lowerxarch.cpp
@@ -51,8 +51,7 @@ void Lowering::LowerStoreLoc(GenTreeLclVarCommon* storeLoc)
GenTreeIntCon* con = storeLoc->gtOp1->AsIntCon();
ssize_t ival = con->gtIconVal;
- unsigned varNum = storeLoc->GetLclNum();
- LclVarDsc* varDsc = comp->lvaTable + varNum;
+ LclVarDsc* varDsc = comp->lvaGetDesc(storeLoc);
if (varDsc->lvIsSIMDType())
{
@@ -130,36 +129,6 @@ void Lowering::LowerStoreIndir(GenTreeStoreInd* node)
return;
}
}
- else if (node->Data()->IsCnsFltOrDbl())
- {
- // Optimize *x = DCON to *x = ICON which is slightly faster on xarch
- GenTree* data = node->Data();
- double dblCns = data->AsDblCon()->gtDconVal;
- ssize_t intCns = 0;
- var_types type = TYP_UNKNOWN;
-
- if (node->TypeIs(TYP_FLOAT))
- {
- float fltCns = static_cast(dblCns); // should be a safe round-trip
- intCns = static_cast(*reinterpret_cast(&fltCns));
- type = TYP_INT;
- }
-#ifdef TARGET_AMD64
- else
- {
- assert(node->TypeIs(TYP_DOUBLE));
- intCns = static_cast(*reinterpret_cast(&dblCns));
- type = TYP_LONG;
- }
-#endif
-
- if (type != TYP_UNKNOWN)
- {
- data->SetContained();
- data->BashToConst(intCns, type);
- node->ChangeType(type);
- }
- }
// Optimization: do not unnecessarily zero-extend the result of setcc.
if (varTypeIsByte(node) && (node->Data()->OperIsCompare() || node->Data()->OperIs(GT_SETCC)))
@@ -490,7 +459,7 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk)
{
if (fieldNode->OperGet() == GT_LCL_VAR)
{
- LclVarDsc* varDsc = &(comp->lvaTable[fieldNode->AsLclVarCommon()->GetLclNum()]);
+ const LclVarDsc* varDsc = comp->lvaGetDesc(fieldNode->AsLclVarCommon());
if (!varDsc->lvDoNotEnregister)
{
fieldNode->SetRegOptional();
@@ -750,35 +719,30 @@ void Lowering::LowerSIMD(GenTreeSIMD* simdNode)
simdNode->gtType = TYP_SIMD16;
}
- if (simdNode->gtSIMDIntrinsicID == SIMDIntrinsicInitN)
+ if (simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicInitN)
{
assert(simdNode->GetSimdBaseType() == TYP_FLOAT);
- int argCount = 0;
- int constArgCount = 0;
- float constArgValues[4]{0, 0, 0, 0};
+ size_t argCount = simdNode->GetOperandCount();
+ size_t constArgCount = 0;
+ float constArgValues[4]{0, 0, 0, 0};
- for (GenTreeArgList* list = simdNode->gtGetOp1()->AsArgList(); list != nullptr; list = list->Rest())
+ for (GenTree* arg : simdNode->Operands())
{
- GenTree* arg = list->Current();
-
- assert(arg->TypeGet() == simdNode->GetSimdBaseType());
- assert(argCount < (int)_countof(constArgValues));
+ assert(arg->TypeIs(simdNode->GetSimdBaseType()));
if (arg->IsCnsFltOrDbl())
{
constArgValues[constArgCount] = static_cast(arg->AsDblCon()->gtDconVal);
constArgCount++;
}
-
- argCount++;
}
if (constArgCount == argCount)
{
- for (GenTreeArgList* list = simdNode->gtGetOp1()->AsArgList(); list != nullptr; list = list->Rest())
+ for (GenTree* arg : simdNode->Operands())
{
- BlockRange().Remove(list->Current());
+ BlockRange().Remove(arg);
}
assert(sizeof(constArgValues) == 16);
@@ -791,7 +755,7 @@ void Lowering::LowerSIMD(GenTreeSIMD* simdNode)
GenTree* clsVarAddr = new (comp, GT_CLS_VAR_ADDR) GenTreeClsVar(GT_CLS_VAR_ADDR, TYP_I_IMPL, hnd, nullptr);
BlockRange().InsertBefore(simdNode, clsVarAddr);
simdNode->ChangeOper(GT_IND);
- simdNode->gtOp1 = clsVarAddr;
+ simdNode->AsOp()->gtOp1 = clsVarAddr;
ContainCheckIndir(simdNode->AsIndir());
return;
@@ -817,8 +781,9 @@ void Lowering::LowerHWIntrinsicCC(GenTreeHWIntrinsic* node, NamedIntrinsic newIn
{
GenTreeCC* cc = LowerNodeCC(node, condition);
- node->gtHWIntrinsicId = newIntrinsicId;
- node->gtType = TYP_VOID;
+ assert(HWIntrinsicInfo::lookupNumArgs(newIntrinsicId) == 2);
+ node->ChangeHWIntrinsicId(newIntrinsicId);
+ node->gtType = TYP_VOID;
node->ClearUnusedValue();
bool swapOperands = false;
@@ -867,8 +832,8 @@ void Lowering::LowerHWIntrinsicCC(GenTreeHWIntrinsic* node, NamedIntrinsic newIn
bool op1SupportsRegOptional = false;
bool op2SupportsRegOptional = false;
- if (!IsContainableHWIntrinsicOp(node, node->gtGetOp2(), &op2SupportsRegOptional) &&
- IsContainableHWIntrinsicOp(node, node->gtGetOp1(), &op1SupportsRegOptional))
+ if (!IsContainableHWIntrinsicOp(node, node->Op(2), &op2SupportsRegOptional) &&
+ IsContainableHWIntrinsicOp(node, node->Op(1), &op1SupportsRegOptional))
{
// Swap operands if op2 cannot be contained but op1 can.
swapOperands = true;
@@ -877,7 +842,7 @@ void Lowering::LowerHWIntrinsicCC(GenTreeHWIntrinsic* node, NamedIntrinsic newIn
if (swapOperands)
{
- std::swap(node->gtOp1, node->gtOp2);
+ std::swap(node->Op(1), node->Op(2));
if (cc != nullptr)
{
@@ -908,53 +873,44 @@ void Lowering::LowerHWIntrinsicCC(GenTreeHWIntrinsic* node, NamedIntrinsic newIn
//
void Lowering::LowerFusedMultiplyAdd(GenTreeHWIntrinsic* node)
{
- assert(node->gtHWIntrinsicId == NI_FMA_MultiplyAddScalar);
- GenTreeArgList* argList = node->gtGetOp1()->AsArgList();
+ assert(node->GetHWIntrinsicId() == NI_FMA_MultiplyAddScalar);
GenTreeHWIntrinsic* createScalarOps[3];
- for (GenTreeHWIntrinsic*& createScalarOp : createScalarOps)
+ for (size_t i = 1; i <= 3; i++)
{
- GenTree*& current = argList->Current();
- assert(current != nullptr);
- if (!current->OperIsHWIntrinsic())
- {
- return; // Math(F).FusedMultiplyAdd is expected to emit three NI_Vector128_CreateScalarUnsafe
- // but it's also possible to use NI_FMA_MultiplyAddScalar directly with any operands
- }
- GenTreeHWIntrinsic* hwArg = current->AsHWIntrinsic();
- if (hwArg->gtHWIntrinsicId != NI_Vector128_CreateScalarUnsafe)
+ GenTree* arg = node->Op(i);
+ if (!arg->OperIsHWIntrinsic() || (arg->AsHWIntrinsic()->GetHWIntrinsicId() != NI_Vector128_CreateScalarUnsafe))
{
return;
}
- createScalarOp = hwArg;
- argList = argList->Rest();
+
+ createScalarOps[i - 1] = arg->AsHWIntrinsic();
}
- assert(argList == nullptr);
- GenTree* argX = createScalarOps[0]->gtGetOp1();
- GenTree* argY = createScalarOps[1]->gtGetOp1();
- GenTree* argZ = createScalarOps[2]->gtGetOp1();
+ GenTree* argX = createScalarOps[0]->Op(1);
+ GenTree* argY = createScalarOps[1]->Op(1);
+ GenTree* argZ = createScalarOps[2]->Op(1);
const bool negMul = argX->OperIs(GT_NEG) != argY->OperIs(GT_NEG);
if (argX->OperIs(GT_NEG))
{
- createScalarOps[0]->gtOp1 = argX->gtGetOp1();
+ createScalarOps[0]->Op(1) = argX->gtGetOp1();
BlockRange().Remove(argX);
}
if (argY->OperIs(GT_NEG))
{
- createScalarOps[1]->gtOp1 = argY->gtGetOp1();
+ createScalarOps[1]->Op(1) = argY->gtGetOp1();
BlockRange().Remove(argY);
}
if (argZ->OperIs(GT_NEG))
{
- createScalarOps[2]->gtOp1 = argZ->gtGetOp1();
+ createScalarOps[2]->Op(1) = argZ->gtGetOp1();
BlockRange().Remove(argZ);
- node->gtHWIntrinsicId = negMul ? NI_FMA_MultiplySubtractNegatedScalar : NI_FMA_MultiplySubtractScalar;
+ node->ChangeHWIntrinsicId(negMul ? NI_FMA_MultiplySubtractNegatedScalar : NI_FMA_MultiplySubtractScalar);
}
else
{
- node->gtHWIntrinsicId = negMul ? NI_FMA_MultiplyAddNegatedScalar : NI_FMA_MultiplyAddScalar;
+ node->ChangeHWIntrinsicId(negMul ? NI_FMA_MultiplyAddNegatedScalar : NI_FMA_MultiplyAddScalar);
}
}
@@ -973,7 +929,7 @@ void Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node)
node->gtType = TYP_SIMD16;
}
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
switch (intrinsicId)
{
@@ -988,7 +944,7 @@ void Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node)
// intrinsics that are not Vector*.Create
LowerHWIntrinsicCreate(node);
- assert(!node->OperIsHWIntrinsic() || (node->gtHWIntrinsicId != intrinsicId));
+ assert(!node->OperIsHWIntrinsic() || (node->GetHWIntrinsicId() != intrinsicId));
LowerNode(node);
return;
}
@@ -1005,8 +961,8 @@ void Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node)
{
LowerHWIntrinsicGetElement(node);
- if ((node->gtHWIntrinsicId == NI_Vector128_GetElement) ||
- (node->gtHWIntrinsicId == NI_Vector256_GetElement))
+ if ((node->GetHWIntrinsicId() == NI_Vector128_GetElement) ||
+ (node->GetHWIntrinsicId() == NI_Vector256_GetElement))
{
// Most NI_Vector*_GetElement intrinsics are lowered to
// alternative nodes, such as the Extract intrinsics,
@@ -1053,11 +1009,9 @@ void Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node)
if (varTypeIsFloating(node->GetSimdBaseType()))
{
assert(node->GetSimdBaseType() == TYP_FLOAT);
- assert(node->gtOp1 != nullptr);
- assert(node->gtOp2 != nullptr);
assert(node->GetSimdSize() == 16);
- GenTree* op2 = node->gtGetOp2();
+ GenTree* op2 = node->Op(2);
if (!op2->OperIsConst())
{
@@ -1071,10 +1025,10 @@ void Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node)
BlockRange().InsertAfter(msk, tmp);
LowerNode(tmp);
- node->gtOp2 = tmp;
+ node->Op(2) = tmp;
}
- node->gtHWIntrinsicId = NI_Vector128_GetElement;
+ node->ChangeHWIntrinsicId(NI_Vector128_GetElement);
LowerNode(node);
}
break;
@@ -1084,29 +1038,26 @@ void Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node)
case NI_SSE41_Insert:
case NI_SSE41_X64_Insert:
{
- assert(HWIntrinsicInfo::lookupNumArgs(node) == 3);
-
- GenTreeArgList* argList = node->gtOp1->AsArgList();
+ assert(node->GetOperandCount() == 3);
// Insert takes either a 32-bit register or a memory operand.
// In either case, only SimdBaseType bits are read and so
// widening or narrowing the operand may be unnecessary and it
// can just be used directly.
-
- argList->Rest()->gtOp1 = TryRemoveCastIfPresent(node->GetSimdBaseType(), argList->Rest()->gtOp1);
+ node->Op(2) = TryRemoveCastIfPresent(node->GetSimdBaseType(), node->Op(2));
break;
}
case NI_SSE42_Crc32:
{
- assert(HWIntrinsicInfo::lookupNumArgs(node) == 2);
+ assert(node->GetOperandCount() == 2);
// Crc32 takes either a bit register or a memory operand.
// In either case, only gtType bits are read and so widening
// or narrowing the operand may be unnecessary and it can
// just be used directly.
- node->gtOp2 = TryRemoveCastIfPresent(node->gtType, node->gtOp2);
+ node->Op(2) = TryRemoveCastIfPresent(node->TypeGet(), node->Op(2));
break;
}
@@ -1137,7 +1088,7 @@ void Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node)
}
// pre-AVX doesn't actually support these intrinsics in hardware so we need to swap the operands around
- std::swap(node->gtOp1, node->gtOp2);
+ std::swap(node->Op(1), node->Op(2));
break;
}
@@ -1152,7 +1103,7 @@ void Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node)
assert(varTypeIsIntegral(node->GetSimdBaseType()));
// this isn't actually supported in hardware so we need to swap the operands around
- std::swap(node->gtOp1, node->gtOp2);
+ std::swap(node->Op(1), node->Op(2));
break;
}
@@ -1272,7 +1223,7 @@ void Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node)
//
void Lowering::LowerHWIntrinsicCmpOp(GenTreeHWIntrinsic* node, genTreeOps cmpOp)
{
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
CorInfoType simdBaseJitType = node->GetSimdBaseJitType();
var_types simdBaseType = node->GetSimdBaseType();
unsigned simdSize = node->GetSimdSize();
@@ -1292,8 +1243,8 @@ void Lowering::LowerHWIntrinsicCmpOp(GenTreeHWIntrinsic* node, genTreeOps cmpOp)
// /--* op1 simd
// node = * HWINTRINSIC simd T op_Equality
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = node->gtGetOp2();
+ GenTree* op1 = node->Op(1);
+ GenTree* op2 = node->Op(2);
GenCondition cmpCnd = (cmpOp == GT_EQ) ? GenCondition::EQ : GenCondition::NE;
@@ -1303,34 +1254,35 @@ void Lowering::LowerHWIntrinsicCmpOp(GenTreeHWIntrinsic* node, genTreeOps cmpOp)
// just use PTEST. We can't support it for floating-point, however,
// as it has both +0.0 and -0.0 where +0.0 == -0.0
- node->gtOp1 = op1;
+ node->Op(1) = op1;
BlockRange().Remove(op2);
- op2 = op2->AsOp()->gtGetOp1();
-
- if (op2 != nullptr)
+ if (op2->AsMultiOp()->GetOperandCount() == 1)
{
// Some zero vectors are Create/Initialization nodes with a constant zero operand
// We should also remove this to avoid dead code
- BlockRange().Remove(op2);
+ assert(op2->AsMultiOp()->Op(1)->IsIntegralConst(0));
+ BlockRange().Remove(op2->AsMultiOp()->Op(1));
}
- LIR::Use op1Use(BlockRange(), &node->gtOp1, node);
+ LIR::Use op1Use(BlockRange(), &node->Op(1), node);
ReplaceWithLclVar(op1Use);
- op1 = node->gtOp1;
+ op1 = node->Op(1);
op2 = comp->gtClone(op1);
BlockRange().InsertAfter(op1, op2);
- node->gtOp2 = op2;
+ node->Op(2) = op2;
if (simdSize == 32)
{
- node->gtHWIntrinsicId = NI_AVX_TestZ;
+ // TODO-Review: LowerHWIntrinsicCC resets the id again, so why is this needed?
+ node->ChangeHWIntrinsicId(NI_AVX_TestZ);
LowerHWIntrinsicCC(node, NI_AVX_PTEST, cmpCnd);
}
else
{
- node->gtHWIntrinsicId = NI_SSE41_TestZ;
+ // TODO-Review: LowerHWIntrinsicCC resets the id again, so why is this needed?
+ node->ChangeHWIntrinsicId(NI_SSE41_TestZ);
LowerHWIntrinsicCC(node, NI_SSE41_PTEST, cmpCnd);
}
@@ -1492,10 +1444,9 @@ void Lowering::LowerHWIntrinsicCmpOp(GenTreeHWIntrinsic* node, genTreeOps cmpOp)
}
node->ChangeOper(cmpOp);
-
- node->gtType = TYP_INT;
- node->gtOp1 = msk;
- node->gtOp2 = mskCns;
+ node->ChangeType(TYP_INT);
+ node->AsOp()->gtOp1 = msk;
+ node->AsOp()->gtOp2 = mskCns;
GenTree* cc = LowerNodeCC(node, cmpCnd);
@@ -1513,7 +1464,7 @@ void Lowering::LowerHWIntrinsicCmpOp(GenTreeHWIntrinsic* node, genTreeOps cmpOp)
//
void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
{
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
var_types simdType = node->gtType;
CorInfoType simdBaseJitType = node->GetSimdBaseJitType();
var_types simdBaseType = node->GetSimdBaseType();
@@ -1531,9 +1482,7 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
assert(varTypeIsArithmetic(simdBaseType));
assert(simdSize != 0);
- GenTreeArgList* argList = nullptr;
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = node->gtGetOp2();
+ GenTree* op1 = node->Op(1);
// Spare GenTrees to be used for the lowering logic below
// Defined upfront to avoid naming conflicts, etc...
@@ -1542,49 +1491,27 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
GenTree* tmp2 = nullptr;
GenTree* tmp3 = nullptr;
- assert(op1 != nullptr);
-
- unsigned argCnt = 0;
- unsigned cnsArgCnt = 0;
+ size_t argCnt = node->GetOperandCount();
+ size_t cnsArgCnt = 0;
- if (op1->OperIsList())
+ // These intrinsics are meant to set the same value to every element.
+ if ((argCnt == 1) && HandleArgForHWIntrinsicCreate(node->Op(1), 0, vecCns, simdBaseType))
{
- assert(op2 == nullptr);
-
- for (argList = op1->AsArgList(); argList != nullptr; argList = argList->Rest())
+ // Now assign the rest of the arguments.
+ for (unsigned i = 1; i < simdSize / genTypeSize(simdBaseType); i++)
{
- if (HandleArgForHWIntrinsicCreate(argList->Current(), argCnt, vecCns, simdBaseType))
- {
- cnsArgCnt += 1;
- }
- argCnt += 1;
+ HandleArgForHWIntrinsicCreate(node->Op(1), i, vecCns, simdBaseType);
}
+
+ cnsArgCnt = 1;
}
else
{
- if (HandleArgForHWIntrinsicCreate(op1, argCnt, vecCns, simdBaseType))
- {
- cnsArgCnt += 1;
- }
- argCnt += 1;
-
- if (op2 != nullptr)
- {
- if (HandleArgForHWIntrinsicCreate(op2, argCnt, vecCns, simdBaseType))
- {
- cnsArgCnt += 1;
- }
- argCnt += 1;
- }
- else if (cnsArgCnt == 1)
+ for (unsigned i = 1; i <= argCnt; i++)
{
- // These intrinsics are meant to set the same value to every element
- // so we'll just specially handle it here and copy it into the remaining
- // indices.
-
- for (unsigned i = 1; i < simdSize / genTypeSize(simdBaseType); i++)
+ if (HandleArgForHWIntrinsicCreate(node->Op(i), i - 1, vecCns, simdBaseType))
{
- HandleArgForHWIntrinsicCreate(op1, i, vecCns, simdBaseType);
+ cnsArgCnt++;
}
}
}
@@ -1592,47 +1519,16 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
if (argCnt == cnsArgCnt)
{
- if (op1->OperIsList())
+ for (GenTree* arg : node->Operands())
{
- for (argList = op1->AsArgList(); argList != nullptr; argList = argList->Rest())
- {
- GenTree* arg = argList->Current();
-
#if !defined(TARGET_64BIT)
- if (arg->OperIsLong())
- {
- BlockRange().Remove(arg->AsOp()->gtOp1);
- BlockRange().Remove(arg->AsOp()->gtOp2);
- }
-#endif // !TARGET_64BIT
-
- BlockRange().Remove(arg);
- }
- }
- else
- {
-#if !defined(TARGET_64BIT)
- if (op1->OperIsLong())
+ if (arg->OperIsLong())
{
- BlockRange().Remove(op1->AsOp()->gtOp1);
- BlockRange().Remove(op1->AsOp()->gtOp2);
+ BlockRange().Remove(arg->AsOp()->gtGetOp1());
+ BlockRange().Remove(arg->AsOp()->gtGetOp2());
}
#endif // !TARGET_64BIT
-
- BlockRange().Remove(op1);
-
- if (op2 != nullptr)
- {
-#if defined(TARGET_64BIT)
- if (op2->OperIsLong())
- {
- BlockRange().Remove(op2->AsOp()->gtOp1);
- BlockRange().Remove(op2->AsOp()->gtOp2);
- }
-#endif // !TARGET_64BIT
-
- BlockRange().Remove(op2);
- }
+ BlockRange().Remove(arg);
}
assert((simdSize == 8) || (simdSize == 12) || (simdSize == 16) || (simdSize == 32));
@@ -1644,16 +1540,12 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
if (vecCns.i64[0] == 0)
{
- node->gtOp1 = nullptr;
- node->gtOp2 = nullptr;
- node->gtHWIntrinsicId = (simdSize == 16) ? NI_Vector128_get_Zero : NI_Vector256_get_Zero;
+ node->ResetHWIntrinsicId((simdSize == 16) ? NI_Vector128_get_Zero : NI_Vector256_get_Zero);
return;
}
else if (vecCns.i64[0] == -1)
{
- node->gtOp1 = nullptr;
- node->gtOp2 = nullptr;
- node->gtHWIntrinsicId = (simdSize == 16) ? NI_Vector128_get_AllBitsSet : NI_Vector256_get_AllBitsSet;
+ node->ResetHWIntrinsicId((simdSize == 16) ? NI_Vector128_get_AllBitsSet : NI_Vector256_get_AllBitsSet);
return;
}
}
@@ -1669,7 +1561,7 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
BlockRange().InsertBefore(node, clsVarAddr);
node->ChangeOper(GT_IND);
- node->gtOp1 = clsVarAddr;
+ node->AsOp()->gtOp1 = clsVarAddr;
// TODO-XARCH-CQ: We should be able to modify at least the paths that use Insert to trivially support partial
// vector constants. With this, we can create a constant if say 50% of the inputs are also constant and just
@@ -1702,10 +1594,7 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
BlockRange().InsertAfter(op1, tmp1);
LowerNode(tmp1);
- node->gtOp1 = tmp1;
- node->gtOp2 = nullptr;
-
- node->gtHWIntrinsicId = NI_AVX2_BroadcastScalarToVector256;
+ node->ResetHWIntrinsicId(NI_AVX2_BroadcastScalarToVector256, tmp1);
return;
}
@@ -1736,10 +1625,10 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
BlockRange().InsertAfter(op1, tmp1);
LowerNode(tmp1);
- node->gtOp1 = tmp1;
- LIR::Use tmp1Use(BlockRange(), &node->gtOp1, node);
+ node->Op(1) = tmp1;
+ LIR::Use tmp1Use(BlockRange(), &node->Op(1), node);
ReplaceWithLclVar(tmp1Use);
- tmp1 = node->gtOp1;
+ tmp1 = node->Op(1);
tmp2 = comp->gtClone(tmp1);
BlockRange().InsertAfter(tmp1, tmp2);
@@ -1752,10 +1641,7 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
idx = comp->gtNewIconNode(0x01, TYP_INT);
BlockRange().InsertAfter(tmp3, idx);
- node->gtOp1 = comp->gtNewArgList(tmp3, tmp1, idx);
- node->gtOp2 = nullptr;
-
- node->gtHWIntrinsicId = NI_AVX_InsertVector128;
+ node->ResetHWIntrinsicId(NI_AVX_InsertVector128, comp, tmp3, tmp1, idx);
return;
}
@@ -1783,10 +1669,7 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
// ...
// return Avx2.BroadcastScalarToVector128(tmp1);
- node->gtOp1 = tmp1;
- node->gtOp2 = nullptr;
-
- node->gtHWIntrinsicId = NI_AVX2_BroadcastScalarToVector128;
+ node->ChangeHWIntrinsicId(NI_AVX2_BroadcastScalarToVector128, tmp1);
return;
}
@@ -1814,10 +1697,7 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
BlockRange().InsertAfter(tmp1, tmp2);
LowerNode(tmp2);
- node->gtOp1 = tmp1;
- node->gtOp2 = tmp2;
-
- node->gtHWIntrinsicId = NI_SSSE3_Shuffle;
+ node->ResetHWIntrinsicId(NI_SSSE3_Shuffle, tmp1, tmp2);
break;
}
@@ -1840,10 +1720,10 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
// tmp1 = Sse2.UnpackLow(tmp1, tmp2);
// ...
- node->gtOp1 = tmp1;
- LIR::Use tmp1Use(BlockRange(), &node->gtOp1, node);
+ node->Op(1) = tmp1;
+ LIR::Use tmp1Use(BlockRange(), &node->Op(1), node);
ReplaceWithLclVar(tmp1Use);
- tmp1 = node->gtOp1;
+ tmp1 = node->Op(1);
tmp2 = comp->gtClone(tmp1);
BlockRange().InsertAfter(tmp1, tmp2);
@@ -1878,10 +1758,10 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2));
- node->gtOp1 = tmp1;
- LIR::Use tmp1Use(BlockRange(), &node->gtOp1, node);
+ node->Op(1) = tmp1;
+ LIR::Use tmp1Use(BlockRange(), &node->Op(1), node);
ReplaceWithLclVar(tmp1Use);
- tmp1 = node->gtOp1;
+ tmp1 = node->Op(1);
tmp2 = comp->gtClone(tmp1);
BlockRange().InsertAfter(tmp1, tmp2);
@@ -1913,12 +1793,8 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
idx = comp->gtNewIconNode(0x00, TYP_INT);
BlockRange().InsertAfter(tmp1, idx);
- node->gtOp1 = tmp1;
- node->gtOp2 = idx;
-
- node->gtHWIntrinsicId = NI_SSE2_Shuffle;
+ node->ResetHWIntrinsicId(NI_SSE2_Shuffle, tmp1, idx);
node->SetSimdBaseJitType(CORINFO_TYPE_UINT);
-
break;
}
@@ -1943,18 +1819,15 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2));
- node->gtOp1 = tmp1;
- LIR::Use tmp1Use(BlockRange(), &node->gtOp1, node);
+ node->Op(1) = tmp1;
+ LIR::Use tmp1Use(BlockRange(), &node->Op(1), node);
ReplaceWithLclVar(tmp1Use);
- tmp1 = node->gtOp1;
+ tmp1 = node->Op(1);
tmp2 = comp->gtClone(tmp1);
BlockRange().InsertAfter(tmp1, tmp2);
- node->gtOp1 = tmp1;
- node->gtOp2 = tmp2;
-
- node->gtHWIntrinsicId = NI_SSE2_UnpackLow;
+ node->ResetHWIntrinsicId(NI_SSE2_UnpackLow, tmp1, tmp2);
break;
}
#endif // TARGET_AMD64
@@ -1977,10 +1850,7 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
idx = comp->gtNewIconNode(0x00, TYP_INT);
BlockRange().InsertAfter(tmp1, idx);
- node->gtOp1 = tmp1;
- node->gtOp2 = idx;
-
- node->gtHWIntrinsicId = NI_AVX_Permute;
+ node->ResetHWIntrinsicId(NI_AVX_Permute, tmp1, idx);
break;
}
@@ -2003,10 +1873,10 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE));
- node->gtOp1 = tmp1;
- LIR::Use tmp1Use(BlockRange(), &node->gtOp1, node);
+ node->Op(1) = tmp1;
+ LIR::Use tmp1Use(BlockRange(), &node->Op(1), node);
ReplaceWithLclVar(tmp1Use);
- tmp1 = node->gtOp1;
+ tmp1 = node->Op(1);
tmp2 = comp->gtClone(tmp1);
BlockRange().InsertAfter(tmp1, tmp2);
@@ -2014,10 +1884,7 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
idx = comp->gtNewIconNode(0x00, TYP_INT);
BlockRange().InsertAfter(tmp2, idx);
- node->gtOp1 = comp->gtNewArgList(tmp1, tmp2, idx);
- node->gtOp2 = nullptr;
-
- node->gtHWIntrinsicId = NI_SSE_Shuffle;
+ node->ResetHWIntrinsicId(NI_SSE_Shuffle, comp, tmp1, tmp2, idx);
break;
}
@@ -2034,10 +1901,7 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
// ...
// return Sse3.MoveAndDuplicate(tmp1);
- node->gtOp1 = tmp1;
- node->gtOp2 = nullptr;
-
- node->gtHWIntrinsicId = NI_SSE3_MoveAndDuplicate;
+ node->ChangeHWIntrinsicId(NI_SSE3_MoveAndDuplicate, tmp1);
break;
}
@@ -2058,20 +1922,16 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
// var tmp2 = tmp1;
// return Sse.MoveLowToHigh(tmp1, tmp2);
- node->gtOp1 = tmp1;
- LIR::Use tmp1Use(BlockRange(), &node->gtOp1, node);
+ node->Op(1) = tmp1;
+ LIR::Use tmp1Use(BlockRange(), &node->Op(1), node);
ReplaceWithLclVar(tmp1Use);
- tmp1 = node->gtOp1;
+ tmp1 = node->Op(1);
tmp2 = comp->gtClone(tmp1);
BlockRange().InsertAfter(tmp1, tmp2);
- node->gtOp1 = tmp1;
- node->gtOp2 = tmp2;
-
- node->gtHWIntrinsicId = NI_SSE_MoveLowToHigh;
+ node->ResetHWIntrinsicId(NI_SSE_MoveLowToHigh, tmp1, tmp2);
node->SetSimdBaseJitType(CORINFO_TYPE_FLOAT);
-
break;
}
@@ -2084,6 +1944,8 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
return;
}
+ GenTree* op2 = node->Op(2);
+
// We have the following (where simd is simd16 or simd32):
// /--* op1 T
// +--* ... T
@@ -2117,99 +1979,36 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
// lo = Vector128.Create(op1, op2);
// hi = Vector128.Create(op3, op4);
// -or-
- // lo = Vector128.Create(op1, ..., op3);
- // hi = Vector128.Create(op4, ..., op7);
+ // lo = Vector128.Create(op1, ..., op4);
+ // hi = Vector128.Create(op5, ..., op8);
// -or-
- // lo = Vector128.Create(op1, ..., op7);
- // hi = Vector128.Create(op8, ..., op15);
+ // lo = Vector128.Create(op1, ..., op8);
+ // hi = Vector128.Create(op9, ..., op16);
// -or-
- // lo = Vector128.Create(op1, ..., op15);
- // hi = Vector128.Create(op16, ..., op31);
+ // lo = Vector128.Create(op1, ..., op16);
+ // hi = Vector128.Create(op17, ..., op32);
- unsigned halfArgCnt = argCnt / 2;
+ size_t halfArgCnt = argCnt / 2;
assert((halfArgCnt * 2) == argCnt);
- argList = op1->AsArgList();
-
- for (unsigned i = 0; i < halfArgCnt; i++)
- {
- op2 = argList;
- argList = argList->Rest();
- }
-
- op2->AsArgList()->gtOp2 = nullptr;
- op2 = argList;
-
- // The above for loop splits the operand count into exactly half.
- // Once it exits, op1 will point to op1 and op2 will point to the
- // last operand that will be passed to the first Vector128.Create
- // We will set its op2 to null, terminating the chain and then
- // assign op2 to be argList, which is the first operand that will
- // get passed to the second Vector128.Create
-
- GenTree* lo = nullptr;
- GenTree* hi = nullptr;
-
- if (halfArgCnt == 2)
- {
- // The Vector256.Create calls that take 4 operands are special
- // because the half argument count is 2, which means we can't
- // actually use the GT_LIST anymore and need to pass them as
- // explicit operands instead.
-
- argList = op1->AsArgList();
-
- tmp1 = argList->Current();
- tmp2 = argList->Rest()->Current();
-
- lo = comp->gtNewSimdHWIntrinsicNode(TYP_SIMD16, tmp1, tmp2, NI_Vector128_Create, simdBaseJitType, 16);
- BlockRange().InsertAfter(tmp2, lo);
- LowerNode(lo);
-
- argList = op2->AsArgList();
-
- tmp1 = argList->Current();
- tmp2 = argList->Rest()->Current();
+ GenTree* lo = comp->gtNewSimdHWIntrinsicNode(TYP_SIMD16, node->GetOperandArray(), halfArgCnt,
+ NI_Vector128_Create, simdBaseJitType, 16);
+ BlockRange().InsertAfter(node->Op(halfArgCnt), lo);
+ LowerNode(lo);
- hi = comp->gtNewSimdHWIntrinsicNode(TYP_SIMD16, tmp1, tmp2, NI_Vector128_Create, simdBaseJitType, 16);
- BlockRange().InsertAfter(tmp2, hi);
- LowerNode(hi);
- }
- else
- {
- // The rest of the Vector256.Create calls take at least 8 operands
- // and so the half count is at least 4 and we have to continue
- // passing around GT_LIST nodes in op1 with a null op2
- assert(halfArgCnt >= 4);
-
- tmp1 = op2->AsArgList()->Current();
-
- lo = comp->gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, NI_Vector128_Create, simdBaseJitType, 16);
- BlockRange().InsertBefore(tmp1, lo);
- LowerNode(lo);
-
- hi = comp->gtNewSimdHWIntrinsicNode(TYP_SIMD16, op2, NI_Vector128_Create, simdBaseJitType, 16);
- BlockRange().InsertBefore(node, hi);
- LowerNode(hi);
- }
+ GenTree* hi = comp->gtNewSimdHWIntrinsicNode(TYP_SIMD16, node->GetOperandArray(halfArgCnt), halfArgCnt,
+ NI_Vector128_Create, simdBaseJitType, 16);
+ BlockRange().InsertAfter(node->Op(argCnt), hi);
+ LowerNode(hi);
idx = comp->gtNewIconNode(0x01, TYP_INT);
BlockRange().InsertAfter(hi, idx);
- node->gtOp1 = comp->gtNewArgList(lo, hi, idx);
- node->gtOp2 = nullptr;
-
- node->gtHWIntrinsicId = NI_AVX_InsertVector128;
+ assert(argCnt >= 3);
+ node->ResetHWIntrinsicId(NI_AVX_InsertVector128, comp, lo, hi, idx);
return;
}
- if (op1->OperIsList())
- {
- argList = op1->AsArgList();
- op1 = argList->Current();
- argList = argList->Rest();
- }
-
// We will be constructing the following parts:
// /--* op1 T
// tmp1 = * HWINTRINSIC simd16 T CreateScalarUnsafe
@@ -2264,7 +2063,7 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
// tmp1 = Sse?.Insert(tmp1, opN, N);
// ...
- opN = argList->Current();
+ opN = node->Op(N + 1);
idx = comp->gtNewIconNode(N, TYP_INT);
BlockRange().InsertAfter(opN, idx);
@@ -2273,8 +2072,6 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
simdSize);
BlockRange().InsertAfter(idx, tmp1);
LowerNode(tmp1);
-
- argList = argList->Rest();
}
assert(N == (argCnt - 1));
@@ -2291,15 +2088,12 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
// tmp1 = Sse?.Insert(tmp1, opN, N);
// ...
- opN = argList->Current();
+ opN = node->Op(argCnt);
idx = comp->gtNewIconNode(N, TYP_INT);
BlockRange().InsertAfter(opN, idx);
- node->gtOp1 = comp->gtNewArgList(tmp1, opN, idx);
- node->gtOp2 = nullptr;
-
- node->gtHWIntrinsicId = insIntrinsic;
+ node->ResetHWIntrinsicId(insIntrinsic, comp, tmp1, opN, idx);
break;
}
@@ -2311,16 +2105,13 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
for (N = 1; N < argCnt; N++)
{
- opN = argList->Current();
+ opN = node->Op(N + 1);
op[N] = comp->gtNewSimdHWIntrinsicNode(TYP_SIMD16, opN, NI_Vector128_CreateScalarUnsafe,
simdBaseJitType, 16);
BlockRange().InsertAfter(opN, op[N]);
LowerNode(op[N]);
-
- argList = argList->Rest();
}
- assert(argList == nullptr);
if ((simdBaseType == TYP_BYTE) || (simdBaseType == TYP_UBYTE))
{
@@ -2417,10 +2208,7 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
BlockRange().InsertAfter(op[3], tmp2);
LowerNode(tmp2);
- node->gtOp1 = tmp1;
- node->gtOp2 = tmp2;
-
- node->gtHWIntrinsicId = NI_SSE2_UnpackLow;
+ node->ResetHWIntrinsicId(NI_SSE2_UnpackLow, tmp1, tmp2);
node->SetSimdBaseJitType(CORINFO_TYPE_ULONG);
break;
}
@@ -2446,10 +2234,7 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
idx = comp->gtNewIconNode(0x01, TYP_INT);
BlockRange().InsertBefore(node, idx);
- node->gtOp1 = comp->gtNewArgList(tmp1, op2, idx);
- node->gtOp2 = nullptr;
-
- node->gtHWIntrinsicId = NI_SSE41_X64_Insert;
+ node->ResetHWIntrinsicId(NI_SSE41_X64_Insert, comp, tmp1, op2, idx);
break;
}
@@ -2473,10 +2258,7 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
BlockRange().InsertAfter(op2, tmp2);
LowerNode(tmp2);
- node->gtOp1 = tmp1;
- node->gtOp2 = tmp2;
-
- node->gtHWIntrinsicId = NI_SSE2_UnpackLow;
+ node->ResetHWIntrinsicId(NI_SSE2_UnpackLow, tmp1, tmp2);
break;
}
#endif // TARGET_AMD64
@@ -2508,7 +2290,7 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
// tmp1 = Sse41.Insert(tmp1, tmp2, N << 4);
// ...
- opN = argList->Current();
+ opN = node->Op(N + 1);
tmp2 = comp->gtNewSimdHWIntrinsicNode(TYP_SIMD16, opN, NI_Vector128_CreateScalarUnsafe,
simdBaseJitType, 16);
@@ -2522,8 +2304,6 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
simdSize);
BlockRange().InsertAfter(idx, tmp1);
LowerNode(tmp1);
-
- argList = argList->Rest();
}
// We will be constructing the following parts:
@@ -2542,7 +2322,7 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
// tmp2 = Vector128.CreateScalarUnsafe(opN);
// return Sse41.Insert(tmp1, tmp2, N << 4);
- opN = argList->Current();
+ opN = node->Op(argCnt);
tmp2 = comp->gtNewSimdHWIntrinsicNode(TYP_SIMD16, opN, NI_Vector128_CreateScalarUnsafe, simdBaseJitType,
16);
@@ -2552,10 +2332,7 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
idx = comp->gtNewIconNode((argCnt - 1) << 4, TYP_INT);
BlockRange().InsertAfter(tmp2, idx);
- node->gtOp1 = comp->gtNewArgList(tmp1, tmp2, idx);
- node->gtOp2 = nullptr;
-
- node->gtHWIntrinsicId = NI_SSE41_Insert;
+ node->ResetHWIntrinsicId(NI_SSE41_Insert, comp, tmp1, tmp2, idx);
break;
}
@@ -2592,16 +2369,13 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
for (N = 1; N < argCnt; N++)
{
- opN = argList->Current();
+ opN = node->Op(N + 1);
op[N] = comp->gtNewSimdHWIntrinsicNode(TYP_SIMD16, opN, NI_Vector128_CreateScalarUnsafe,
simdBaseJitType, 16);
BlockRange().InsertAfter(opN, op[N]);
LowerNode(op[N]);
-
- argList = argList->Rest();
}
- assert(argList == nullptr);
tmp1 = comp->gtNewSimdHWIntrinsicNode(simdType, op[0], op[1], NI_SSE_UnpackLow, simdBaseJitType, simdSize);
BlockRange().InsertAfter(op[1], tmp1);
@@ -2611,10 +2385,7 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
BlockRange().InsertAfter(op[3], tmp2);
LowerNode(tmp2);
- node->gtOp1 = tmp1;
- node->gtOp2 = tmp2;
-
- node->gtHWIntrinsicId = NI_SSE_MoveLowToHigh;
+ node->ResetHWIntrinsicId(NI_SSE_MoveLowToHigh, tmp1, tmp2);
break;
}
@@ -2640,12 +2411,8 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
BlockRange().InsertAfter(op2, tmp2);
LowerNode(tmp2);
- node->gtOp1 = tmp1;
- node->gtOp2 = tmp2;
-
- node->gtHWIntrinsicId = NI_SSE_MoveLowToHigh;
+ node->ResetHWIntrinsicId(NI_SSE_MoveLowToHigh, tmp1, tmp2);
node->SetSimdBaseJitType(CORINFO_TYPE_FLOAT);
-
break;
}
@@ -2664,7 +2431,7 @@ void Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
//
void Lowering::LowerHWIntrinsicGetElement(GenTreeHWIntrinsic* node)
{
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
var_types simdType = node->gtType;
CorInfoType simdBaseJitType = node->GetSimdBaseJitType();
var_types simdBaseType = node->GetSimdBaseType();
@@ -2674,11 +2441,8 @@ void Lowering::LowerHWIntrinsicGetElement(GenTreeHWIntrinsic* node)
assert(varTypeIsArithmetic(simdBaseType));
assert(simdSize != 0);
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = node->gtGetOp2();
-
- assert(op1 != nullptr);
- assert(op2 != nullptr);
+ GenTree* op1 = node->Op(1);
+ GenTree* op2 = node->Op(2);
if (op1->OperIs(GT_IND))
{
@@ -2828,7 +2592,7 @@ void Lowering::LowerHWIntrinsicGetElement(GenTreeHWIntrinsic* node)
unreached();
}
- op2 = nullptr;
+ node->ResetHWIntrinsicId(resIntrinsic, op1);
}
else
{
@@ -2871,18 +2635,15 @@ void Lowering::LowerHWIntrinsicGetElement(GenTreeHWIntrinsic* node)
default:
unreached();
}
- }
- assert(resIntrinsic != NI_Illegal);
+ node->ResetHWIntrinsicId(resIntrinsic, op1, op2);
+ }
- node->gtHWIntrinsicId = resIntrinsic;
- node->gtOp1 = op1;
- node->gtOp2 = op2;
node->SetSimdSize(16);
if (!varTypeIsFloating(simdBaseType))
{
- assert(node->gtHWIntrinsicId != intrinsicId);
+ assert(node->GetHWIntrinsicId() != intrinsicId);
LowerNode(node);
}
@@ -2913,8 +2674,8 @@ void Lowering::LowerHWIntrinsicGetElement(GenTreeHWIntrinsic* node)
//
void Lowering::LowerHWIntrinsicWithElement(GenTreeHWIntrinsic* node)
{
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
- var_types simdType = node->gtType;
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
+ var_types simdType = node->TypeGet();
CorInfoType simdBaseJitType = node->GetSimdBaseJitType();
var_types simdBaseType = node->GetSimdBaseType();
unsigned simdSize = node->GetSimdSize();
@@ -2923,30 +2684,11 @@ void Lowering::LowerHWIntrinsicWithElement(GenTreeHWIntrinsic* node)
assert(varTypeIsArithmetic(simdBaseType));
assert(simdSize != 0);
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = nullptr;
- GenTree* op3 = nullptr;
-
- assert(op1->OperIsList());
- assert(node->gtGetOp2() == nullptr);
-
- GenTreeArgList* argList = op1->AsArgList();
-
- op1 = argList->Current();
- argList = argList->Rest();
-
- op2 = argList->Current();
- argList = argList->Rest();
-
- op3 = argList->Current();
- argList = argList->Rest();
-
- assert(op1 != nullptr);
- assert(op2 != nullptr);
- assert(op3 != nullptr);
+ GenTree* op1 = node->Op(1);
+ GenTree* op2 = node->Op(2);
+ GenTree* op3 = node->Op(3);
assert(op2->OperIsConst());
- assert(argList == nullptr);
ssize_t imm8 = op2->AsIntCon()->IconValue();
ssize_t cachedImm8 = imm8;
@@ -2985,29 +2727,36 @@ void Lowering::LowerHWIntrinsicWithElement(GenTreeHWIntrinsic* node)
// Spare GenTrees to be used for the lowering logic below
// Defined upfront to avoid naming conflicts, etc...
- GenTree* idx = nullptr;
- GenTree* tmp1 = nullptr;
- GenTree* tmp2 = nullptr;
- GenTree* tmp3 = nullptr;
- GenTree* tmpv = nullptr;
-
+ GenTree* idx = nullptr;
+ GenTree* tmp1 = nullptr;
+ GenTree* tmp2 = nullptr;
+ GenTreeHWIntrinsic* result = node;
+
+ // If we have a simd32 WithElement, we will spill the original
+ // simd32 source into a local, extract the lower/upper half from
+ // it and then operate on that. At the end, we will insert the simd16
+ // result back into the simd32 local, producing our final value.
if (intrinsicId == NI_Vector256_WithElement)
{
assert(comp->compIsaSupportedDebugOnly(InstructionSet_AVX));
+ // This copy of "node" will have the simd16 value we need.
+ result = comp->gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, op2, op3, intrinsicId, simdBaseJitType, 16);
+ BlockRange().InsertBefore(node, result);
+
// We will be constructing the following parts:
// ...
// /--* op1 simd32
// * STORE_LCL_VAR simd32
- // tmpv = LCL_VAR simd32
- // op1 = LCL_VAR simd32
+ // tmp32 = LCL_VAR simd32
+ // op1 = LCL_VAR simd32
- node->gtOp1 = op1;
- LIR::Use op1Use(BlockRange(), &node->gtOp1, node);
+ // TODO-CQ: move the tmp32 node closer to the final InsertVector128.
+ LIR::Use op1Use(BlockRange(), &node->Op(1), node);
ReplaceWithLclVar(op1Use);
- tmpv = node->gtOp1;
+ GenTree* tmp32 = node->Op(1);
- op1 = comp->gtClone(tmpv);
+ op1 = comp->gtClone(tmp32);
BlockRange().InsertBefore(op3, op1);
if (imm8 >= count / 2)
@@ -3050,9 +2799,12 @@ void Lowering::LowerHWIntrinsicWithElement(GenTreeHWIntrinsic* node)
}
op1 = tmp1;
- }
- NamedIntrinsic resIntrinsic = NI_Illegal;
+ // Now we will insert our "result" into our simd32 temporary.
+ idx = comp->gtNewIconNode((cachedImm8 >= count / 2) ? 1 : 0);
+ BlockRange().InsertBefore(node, idx);
+ node->ChangeHWIntrinsicId(NI_AVX_InsertVector128, tmp32, result, idx);
+ }
switch (simdBaseType)
{
@@ -3060,11 +2812,8 @@ void Lowering::LowerHWIntrinsicWithElement(GenTreeHWIntrinsic* node)
case TYP_ULONG:
{
idx = comp->gtNewIconNode(imm8);
- BlockRange().InsertBefore(node, idx);
-
- op1 = comp->gtNewArgList(op1, op3, idx);
- op2 = nullptr;
- resIntrinsic = NI_SSE41_X64_Insert;
+ BlockRange().InsertBefore(result, idx);
+ result->ChangeHWIntrinsicId(NI_SSE41_X64_Insert, op1, op3, idx);
break;
}
@@ -3081,7 +2830,7 @@ void Lowering::LowerHWIntrinsicWithElement(GenTreeHWIntrinsic* node)
tmp1 = comp->gtNewSimdHWIntrinsicNode(TYP_SIMD16, op3, NI_Vector128_CreateScalarUnsafe, CORINFO_TYPE_FLOAT,
16);
- BlockRange().InsertBefore(node, tmp1);
+ BlockRange().InsertBefore(result, tmp1);
LowerNode(tmp1);
if (!comp->compOpportunisticallyDependsOn(InstructionSet_SSE41))
@@ -3098,8 +2847,7 @@ void Lowering::LowerHWIntrinsicWithElement(GenTreeHWIntrinsic* node)
// ...
// node = Sse.MoveScalar(op1, op2);
- op2 = tmp1;
- resIntrinsic = NI_SSE_MoveScalar;
+ result->ResetHWIntrinsicId(NI_SSE_MoveScalar, op1, tmp1);
}
else
{
@@ -3125,10 +2873,10 @@ void Lowering::LowerHWIntrinsicWithElement(GenTreeHWIntrinsic* node)
// tmp2 = Sse.Shuffle(tmp1, op1, 0 or 48 or 32);
// node = Sse.Shuffle(tmp2, op1, 226 or 132 or 36);
- node->gtOp1 = op1;
- LIR::Use op1Use(BlockRange(), &node->gtOp1, node);
+ result->Op(1) = op1;
+ LIR::Use op1Use(BlockRange(), &result->Op(1), result);
ReplaceWithLclVar(op1Use);
- op2 = node->gtOp1;
+ op2 = result->Op(1);
tmp2 = comp->gtClone(op2);
BlockRange().InsertAfter(tmp1, tmp2);
@@ -3194,9 +2942,7 @@ void Lowering::LowerHWIntrinsicWithElement(GenTreeHWIntrinsic* node)
std::swap(op1, op2);
}
- op1 = comp->gtNewArgList(op1, op2, idx);
- op2 = nullptr;
- resIntrinsic = NI_SSE_Shuffle;
+ result->ChangeHWIntrinsicId(NI_SSE_Shuffle, op1, op2, idx);
}
break;
}
@@ -3214,11 +2960,8 @@ void Lowering::LowerHWIntrinsicWithElement(GenTreeHWIntrinsic* node)
case TYP_UINT:
{
idx = comp->gtNewIconNode(imm8);
- BlockRange().InsertBefore(node, idx);
-
- op1 = comp->gtNewArgList(op1, op3, idx);
- op2 = nullptr;
- resIntrinsic = NI_SSE41_Insert;
+ BlockRange().InsertBefore(result, idx);
+ result->ChangeHWIntrinsicId(NI_SSE41_Insert, op1, op3, idx);
break;
}
@@ -3226,11 +2969,8 @@ void Lowering::LowerHWIntrinsicWithElement(GenTreeHWIntrinsic* node)
case TYP_USHORT:
{
idx = comp->gtNewIconNode(imm8);
- BlockRange().InsertBefore(node, idx);
-
- op1 = comp->gtNewArgList(op1, op3, idx);
- op2 = nullptr;
- resIntrinsic = NI_SSE2_Insert;
+ BlockRange().InsertBefore(result, idx);
+ result->ChangeHWIntrinsicId(NI_SSE2_Insert, op1, op3, idx);
break;
}
@@ -3247,11 +2987,10 @@ void Lowering::LowerHWIntrinsicWithElement(GenTreeHWIntrinsic* node)
tmp1 = comp->gtNewSimdHWIntrinsicNode(TYP_SIMD16, op3, NI_Vector128_CreateScalarUnsafe, CORINFO_TYPE_DOUBLE,
16);
- BlockRange().InsertBefore(node, tmp1);
+ BlockRange().InsertBefore(result, tmp1);
LowerNode(tmp1);
- op2 = tmp1;
- resIntrinsic = (imm8 == 0) ? NI_SSE2_MoveScalar : NI_SSE2_UnpackLow;
+ result->ResetHWIntrinsicId((imm8 == 0) ? NI_SSE2_MoveScalar : NI_SSE2_UnpackLow, op1, tmp1);
break;
}
@@ -3259,28 +2998,16 @@ void Lowering::LowerHWIntrinsicWithElement(GenTreeHWIntrinsic* node)
unreached();
}
- assert(resIntrinsic != NI_Illegal);
+ assert(result->GetHWIntrinsicId() != intrinsicId);
- if (tmpv != nullptr)
+ LowerNode(result);
+ if (intrinsicId == NI_Vector256_WithElement)
{
- tmp1 = comp->gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, op2, resIntrinsic, simdBaseJitType, 16);
- BlockRange().InsertBefore(node, tmp1);
- LowerNode(tmp1);
-
- idx = comp->gtNewIconNode((cachedImm8 >= count / 2) ? 1 : 0);
- BlockRange().InsertAfter(tmp1, idx);
-
- op1 = comp->gtNewArgList(tmpv, tmp1, idx);
- op2 = nullptr;
- resIntrinsic = NI_AVX_InsertVector128;
+ // Now that we have finalized the shape of the tree, lower the insertion node as well.
+ assert(node->GetHWIntrinsicId() == NI_AVX_InsertVector128);
+ assert(node != result);
+ LowerNode(node);
}
-
- node->gtHWIntrinsicId = resIntrinsic;
- node->gtOp1 = op1;
- node->gtOp2 = op2;
-
- assert(node->gtHWIntrinsicId != intrinsicId);
- LowerNode(node);
}
//----------------------------------------------------------------------------------------------
@@ -3291,7 +3018,7 @@ void Lowering::LowerHWIntrinsicWithElement(GenTreeHWIntrinsic* node)
//
void Lowering::LowerHWIntrinsicDot(GenTreeHWIntrinsic* node)
{
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
CorInfoType simdBaseJitType = node->GetSimdBaseJitType();
var_types simdBaseType = node->GetSimdBaseType();
unsigned simdSize = node->GetSimdSize();
@@ -3303,12 +3030,8 @@ void Lowering::LowerHWIntrinsicDot(GenTreeHWIntrinsic* node)
assert(varTypeIsArithmetic(simdBaseType));
assert(simdSize != 0);
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = node->gtGetOp2();
-
- assert(op1 != nullptr);
- assert(op2 != nullptr);
- assert(!op1->OperIsList());
+ GenTree* op1 = node->Op(1);
+ GenTree* op2 = node->Op(2);
// Spare GenTrees to be used for the lowering logic below
// Defined upfront to avoid naming conflicts, etc...
@@ -3375,10 +3098,10 @@ void Lowering::LowerHWIntrinsicDot(GenTreeHWIntrinsic* node)
BlockRange().InsertAfter(idx, tmp1);
LowerNode(tmp1);
- node->gtOp1 = tmp1;
- LIR::Use tmp1Use(BlockRange(), &node->gtOp1, node);
+ node->Op(1) = tmp1;
+ LIR::Use tmp1Use(BlockRange(), &node->Op(1), node);
ReplaceWithLclVar(tmp1Use);
- tmp1 = node->gtOp1;
+ tmp1 = node->Op(1);
tmp2 = comp->gtClone(tmp1);
BlockRange().InsertAfter(tmp1, tmp2);
@@ -3397,12 +3120,8 @@ void Lowering::LowerHWIntrinsicDot(GenTreeHWIntrinsic* node)
node->SetSimdSize(16);
- node->gtOp1 = tmp3;
- node->gtOp2 = nullptr;
-
- node->gtHWIntrinsicId = NI_Vector128_ToScalar;
+ node->ResetHWIntrinsicId(NI_Vector128_ToScalar, tmp3);
LowerNode(node);
-
return;
}
@@ -3488,12 +3207,8 @@ void Lowering::LowerHWIntrinsicDot(GenTreeHWIntrinsic* node)
BlockRange().InsertAfter(idx, tmp3);
LowerNode(tmp3);
- node->gtOp1 = tmp3;
- node->gtOp2 = nullptr;
-
- node->gtHWIntrinsicId = NI_Vector128_ToScalar;
+ node->ResetHWIntrinsicId(NI_Vector128_ToScalar, tmp3);
LowerNode(node);
-
return;
}
@@ -3533,12 +3248,8 @@ void Lowering::LowerHWIntrinsicDot(GenTreeHWIntrinsic* node)
BlockRange().InsertAfter(idx, tmp3);
LowerNode(tmp3);
- node->gtOp1 = tmp3;
- node->gtOp2 = nullptr;
-
- node->gtHWIntrinsicId = NI_Vector128_ToScalar;
+ node->ResetHWIntrinsicId(NI_Vector128_ToScalar, tmp3);
LowerNode(node);
-
return;
}
@@ -3646,10 +3357,10 @@ void Lowering::LowerHWIntrinsicDot(GenTreeHWIntrinsic* node)
// tmp2 = tmp1;
// ...
- node->gtOp1 = tmp1;
- LIR::Use tmp1Use(BlockRange(), &node->gtOp1, node);
+ node->Op(1) = tmp1;
+ LIR::Use tmp1Use(BlockRange(), &node->Op(1), node);
ReplaceWithLclVar(tmp1Use);
- tmp1 = node->gtOp1;
+ tmp1 = node->Op(1);
tmp2 = comp->gtClone(tmp1);
BlockRange().InsertAfter(tmp1, tmp2);
@@ -3748,10 +3459,10 @@ void Lowering::LowerHWIntrinsicDot(GenTreeHWIntrinsic* node)
// tmp2 = Isa.Shuffle(tmp2, tmp3, shuffleConst);
// ...
- node->gtOp1 = tmp2;
- LIR::Use tmp2Use(BlockRange(), &node->gtOp1, node);
+ node->Op(1) = tmp2;
+ LIR::Use tmp2Use(BlockRange(), &node->Op(1), node);
ReplaceWithLclVar(tmp2Use);
- tmp2 = node->gtOp1;
+ tmp2 = node->Op(1);
tmp3 = comp->gtClone(tmp2);
BlockRange().InsertAfter(tmp2, tmp3);
@@ -3860,10 +3571,10 @@ void Lowering::LowerHWIntrinsicDot(GenTreeHWIntrinsic* node)
// var tmp1 = Isa.Add(tmp1, tmp2);
// ...
- node->gtOp1 = tmp1;
- LIR::Use tmp1Use(BlockRange(), &node->gtOp1, node);
+ node->Op(1) = tmp1;
+ LIR::Use tmp1Use(BlockRange(), &node->Op(1), node);
ReplaceWithLclVar(tmp1Use);
- tmp1 = node->gtOp1;
+ tmp1 = node->Op(1);
tmp2 = comp->gtClone(tmp1);
BlockRange().InsertAfter(tmp1, tmp2);
@@ -3892,13 +3603,8 @@ void Lowering::LowerHWIntrinsicDot(GenTreeHWIntrinsic* node)
// ...
// return tmp1.ToScalar();
- node->gtOp1 = tmp1;
- node->gtOp2 = nullptr;
-
- node->gtHWIntrinsicId = NI_Vector128_ToScalar;
+ node->ResetHWIntrinsicId(NI_Vector128_ToScalar, tmp1);
LowerNode(node);
-
- return;
}
//----------------------------------------------------------------------------------------------
@@ -3909,7 +3615,7 @@ void Lowering::LowerHWIntrinsicDot(GenTreeHWIntrinsic* node)
//
void Lowering::LowerHWIntrinsicToScalar(GenTreeHWIntrinsic* node)
{
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
CorInfoType simdBaseJitType = node->GetSimdBaseJitType();
var_types simdBaseType = node->GetSimdBaseType();
unsigned simdSize = node->GetSimdSize();
@@ -3928,7 +3634,7 @@ void Lowering::LowerHWIntrinsicToScalar(GenTreeHWIntrinsic* node)
{
node->gtType = TYP_INT;
node->SetSimdBaseJitType(CORINFO_TYPE_INT);
- node->gtHWIntrinsicId = NI_SSE2_ConvertToInt32;
+ node->ChangeHWIntrinsicId(NI_SSE2_ConvertToInt32);
break;
}
@@ -3938,20 +3644,20 @@ void Lowering::LowerHWIntrinsicToScalar(GenTreeHWIntrinsic* node)
{
node->gtType = TYP_UINT;
node->SetSimdBaseJitType(CORINFO_TYPE_UINT);
- node->gtHWIntrinsicId = NI_SSE2_ConvertToUInt32;
+ node->ChangeHWIntrinsicId(NI_SSE2_ConvertToUInt32);
break;
}
#if defined(TARGET_AMD64)
case TYP_LONG:
{
- node->gtHWIntrinsicId = NI_SSE2_X64_ConvertToInt64;
+ node->ChangeHWIntrinsicId(NI_SSE2_X64_ConvertToInt64);
break;
}
case TYP_ULONG:
{
- node->gtHWIntrinsicId = NI_SSE2_X64_ConvertToUInt64;
+ node->ChangeHWIntrinsicId(NI_SSE2_X64_ConvertToUInt64);
break;
}
#endif // TARGET_AMD64
@@ -3983,6 +3689,7 @@ void Lowering::LowerHWIntrinsicToScalar(GenTreeHWIntrinsic* node)
{
use.ReplaceWith(cast);
}
+
LowerNode(cast);
}
}
@@ -4430,8 +4137,8 @@ GenTree* Lowering::PreferredRegOptionalOperand(GenTree* tree)
// mark op1 as reg optional for the same reason as mentioned in (d) above.
if (op1->OperGet() == GT_LCL_VAR && op2->OperGet() == GT_LCL_VAR)
{
- LclVarDsc* v1 = comp->lvaTable + op1->AsLclVarCommon()->GetLclNum();
- LclVarDsc* v2 = comp->lvaTable + op2->AsLclVarCommon()->GetLclNum();
+ LclVarDsc* v1 = comp->lvaGetDesc(op1->AsLclVarCommon());
+ LclVarDsc* v2 = comp->lvaGetDesc(op2->AsLclVarCommon());
bool v1IsRegCandidate = !v1->lvDoNotEnregister;
bool v2IsRegCandidate = !v2->lvDoNotEnregister;
@@ -5395,11 +5102,11 @@ void Lowering::ContainCheckIntrinsic(GenTreeOp* node)
//
void Lowering::ContainCheckSIMD(GenTreeSIMD* simdNode)
{
- switch (simdNode->gtSIMDIntrinsicID)
+ switch (simdNode->GetSIMDIntrinsicId())
{
case SIMDIntrinsicInit:
{
- GenTree* op1 = simdNode->AsOp()->gtOp1;
+ GenTree* op1 = simdNode->Op(1);
#ifndef TARGET_64BIT
if (op1->OperGet() == GT_LONG)
{
@@ -5435,13 +5142,13 @@ void Lowering::ContainCheckSIMD(GenTreeSIMD* simdNode)
case SIMDIntrinsicInitArray:
// We have an array and an index, which may be contained.
- CheckImmedAndMakeContained(simdNode, simdNode->gtGetOp2());
+ CheckImmedAndMakeContained(simdNode, simdNode->Op(2));
break;
case SIMDIntrinsicShuffleSSE2:
// Second operand is an integer constant and marked as contained.
- assert(simdNode->AsOp()->gtOp2->IsCnsIntOrI());
- MakeSrcContained(simdNode, simdNode->AsOp()->gtOp2);
+ assert(simdNode->Op(2)->IsCnsIntOrI());
+ MakeSrcContained(simdNode, simdNode->Op(2));
break;
default:
@@ -5465,7 +5172,7 @@ void Lowering::ContainCheckSIMD(GenTreeSIMD* simdNode)
//
bool Lowering::IsContainableHWIntrinsicOp(GenTreeHWIntrinsic* containingNode, GenTree* node, bool* supportsRegOptional)
{
- NamedIntrinsic containingIntrinsicId = containingNode->gtHWIntrinsicId;
+ NamedIntrinsic containingIntrinsicId = containingNode->GetHWIntrinsicId();
HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(containingIntrinsicId);
// We shouldn't have called in here if containingNode doesn't support containment
@@ -5632,25 +5339,12 @@ bool Lowering::IsContainableHWIntrinsicOp(GenTreeHWIntrinsic* containingNode, Ge
assert(supportsGeneralLoads == false);
assert(supportsSIMDScalarLoads == false);
- GenTree* op1 = containingNode->gtGetOp1();
- GenTree* op2 = nullptr;
- GenTree* op3 = nullptr;
-
- assert(op1->OperIsList());
- assert(containingNode->gtGetOp2() == nullptr);
-
- GenTreeArgList* argList = op1->AsArgList();
-
- op1 = argList->Current();
- argList = argList->Rest();
-
- op2 = argList->Current();
- argList = argList->Rest();
+ GenTree* op1 = containingNode->Op(1);
+ GenTree* op2 = containingNode->Op(2);
+ GenTree* op3 = containingNode->Op(3);
assert(node == op2);
- op3 = argList->Current();
-
// The upper two bits of the immediate value are ignored if
// op2 comes from memory. In order to support using the upper
// bits, we need to disable containment support if op3 is not
@@ -5825,7 +5519,7 @@ bool Lowering::IsContainableHWIntrinsicOp(GenTreeHWIntrinsic* containingNode, Ge
// TODO-XArch: Update this to be table driven, if possible.
- NamedIntrinsic intrinsicId = node->AsHWIntrinsic()->gtHWIntrinsicId;
+ NamedIntrinsic intrinsicId = node->AsHWIntrinsic()->GetHWIntrinsicId();
switch (intrinsicId)
{
@@ -5891,23 +5585,18 @@ void Lowering::ContainCheckHWIntrinsicAddr(GenTreeHWIntrinsic* node, GenTree* ad
//
void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node)
{
- NamedIntrinsic intrinsicId = node->gtHWIntrinsicId;
+ NamedIntrinsic intrinsicId = node->GetHWIntrinsicId();
HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(intrinsicId);
- int numArgs = HWIntrinsicInfo::lookupNumArgs(node);
+ size_t numArgs = node->GetOperandCount();
CorInfoType simdBaseJitType = node->GetSimdBaseJitType();
var_types simdBaseType = node->GetSimdBaseType();
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = node->gtGetOp2();
- GenTree* op3 = nullptr;
-
if (!HWIntrinsicInfo::SupportsContainment(intrinsicId))
{
// AVX2 gather are not containable and always have constant IMM argument
if (HWIntrinsicInfo::isAVX2GatherIntrinsic(intrinsicId))
{
- GenTree* lastOp = HWIntrinsicInfo::lookupLastOp(node);
- assert(lastOp != nullptr);
+ GenTree* lastOp = node->Op(numArgs);
MakeSrcContained(node, lastOp);
}
// Exit early if containment isn't supported
@@ -5916,8 +5605,7 @@ void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node)
if (HWIntrinsicInfo::lookupCategory(intrinsicId) == HW_Category_IMM)
{
- GenTree* lastOp = HWIntrinsicInfo::lookupLastOp(node);
- assert(lastOp != nullptr);
+ GenTree* lastOp = node->Op(numArgs);
if (HWIntrinsicInfo::isImmOp(intrinsicId, lastOp) && lastOp->IsCnsIntOrI())
{
@@ -5940,18 +5628,21 @@ void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node)
const bool isCommutative = HWIntrinsicInfo::IsCommutative(intrinsicId);
+ GenTree* op1 = nullptr;
+ GenTree* op2 = nullptr;
+ GenTree* op3 = nullptr;
+
if (numArgs == 1)
{
// One argument intrinsics cannot be commutative
assert(!isCommutative);
- assert(!op1->OperIsList());
- assert(op2 == nullptr);
+ op1 = node->Op(1);
switch (category)
{
case HW_Category_MemoryLoad:
- ContainCheckHWIntrinsicAddr(node, node->gtGetOp1());
+ ContainCheckHWIntrinsicAddr(node, op1);
break;
case HW_Category_SimpleSIMD:
@@ -6004,9 +5695,9 @@ void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node)
case NI_AVX2_ConvertToVector256Int16:
case NI_AVX2_ConvertToVector256Int32:
case NI_AVX2_ConvertToVector256Int64:
- if (!varTypeIsSIMD(op1->gtType))
+ if (!varTypeIsSIMD(op1))
{
- ContainCheckHWIntrinsicAddr(node, node->gtGetOp1());
+ ContainCheckHWIntrinsicAddr(node, op1);
return;
}
break;
@@ -6041,29 +5732,28 @@ void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node)
{
if (numArgs == 2)
{
- assert(!op1->OperIsList());
- assert(op2 != nullptr);
- assert(!op2->OperIsList());
+ op1 = node->Op(1);
+ op2 = node->Op(2);
switch (category)
{
case HW_Category_MemoryLoad:
if ((intrinsicId == NI_AVX_MaskLoad) || (intrinsicId == NI_AVX2_MaskLoad))
{
- ContainCheckHWIntrinsicAddr(node, node->gtGetOp1());
+ ContainCheckHWIntrinsicAddr(node, op1);
}
else
{
- ContainCheckHWIntrinsicAddr(node, node->gtGetOp2());
+ ContainCheckHWIntrinsicAddr(node, op2);
}
break;
case HW_Category_MemoryStore:
- ContainCheckHWIntrinsicAddr(node, node->gtGetOp1());
+ ContainCheckHWIntrinsicAddr(node, op1);
if (((intrinsicId == NI_SSE_Store) || (intrinsicId == NI_SSE2_Store)) && op2->OperIsHWIntrinsic() &&
- ((op2->AsHWIntrinsic()->gtHWIntrinsicId == NI_AVX_ExtractVector128) ||
- (op2->AsHWIntrinsic()->gtHWIntrinsicId == NI_AVX2_ExtractVector128)) &&
+ ((op2->AsHWIntrinsic()->GetHWIntrinsicId() == NI_AVX_ExtractVector128) ||
+ (op2->AsHWIntrinsic()->GetHWIntrinsicId() == NI_AVX2_ExtractVector128)) &&
op2->gtGetOp2()->IsIntegralConst())
{
MakeSrcContained(node, op2);
@@ -6087,8 +5777,8 @@ void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node)
MakeSrcContained(node, op1);
// Swap the operands here to make the containment checks in codegen significantly simpler
- node->gtOp1 = op2;
- node->gtOp2 = op1;
+ node->Op(1) = op2;
+ node->Op(2) = op1;
}
else if (supportsRegOptional)
{
@@ -6219,15 +5909,13 @@ void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node)
// These intrinsics should have been marked contained by the general-purpose handling
// earlier in the method.
- GenTree* lastOp = HWIntrinsicInfo::lookupLastOp(node);
- assert(lastOp != nullptr);
+ GenTree* lastOp = node->Op(numArgs);
if (HWIntrinsicInfo::isImmOp(intrinsicId, lastOp) && lastOp->IsCnsIntOrI())
{
assert(lastOp->isContained());
}
#endif
-
break;
}
@@ -6296,25 +5984,14 @@ void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node)
// three argument intrinsics should not be marked commutative
assert(!isCommutative);
- assert(op1->OperIsList());
- assert(op2 == nullptr);
-
- GenTreeArgList* argList = op1->AsArgList();
- GenTreeArgList* originalArgList = argList;
-
- op1 = argList->Current();
- argList = argList->Rest();
-
- op2 = argList->Current();
- argList = argList->Rest();
-
- op3 = argList->Current();
- assert(argList->Rest() == nullptr);
+ op1 = node->Op(1);
+ op2 = node->Op(2);
+ op3 = node->Op(3);
switch (category)
{
case HW_Category_MemoryStore:
- ContainCheckHWIntrinsicAddr(node, node->gtGetOp1()->AsOp()->gtGetOp1());
+ ContainCheckHWIntrinsicAddr(node, op1);
break;
case HW_Category_SimpleSIMD:
@@ -6323,40 +6000,53 @@ void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node)
{
if ((intrinsicId >= NI_FMA_MultiplyAdd) && (intrinsicId <= NI_FMA_MultiplySubtractNegatedScalar))
{
- bool supportsRegOptional = false;
+ bool supportsOp1RegOptional = false;
+ bool supportsOp2RegOptional = false;
+ bool supportsOp3RegOptional = false;
+ unsigned resultOpNum = 0;
+ LIR::Use use;
+ GenTree* user = nullptr;
+
+ if (BlockRange().TryGetUse(node, &use))
+ {
+ user = use.User();
+ }
+ resultOpNum = node->GetResultOpNumForFMA(user, op1, op2, op3);
+
+ // Prioritize Containable op. Check if any one of the op is containable first.
+ // Set op regOptional only if none of them is containable.
- if (IsContainableHWIntrinsicOp(node, op3, &supportsRegOptional))
+ // Prefer to make op3 contained,
+ if (resultOpNum != 3 && IsContainableHWIntrinsicOp(node, op3, &supportsOp3RegOptional))
{
- // 213 form: op1 = (op2 * op1) + [op3]
+ // result = (op1 * op2) + [op3]
MakeSrcContained(node, op3);
}
- else if (IsContainableHWIntrinsicOp(node, op2, &supportsRegOptional))
+ else if (resultOpNum != 2 && IsContainableHWIntrinsicOp(node, op2, &supportsOp2RegOptional))
{
- // 132 form: op1 = (op1 * op3) + [op2]
+ // result = (op1 * [op2]) + op3
MakeSrcContained(node, op2);
}
- else if (IsContainableHWIntrinsicOp(node, op1, &supportsRegOptional))
+ else if (resultOpNum != 1 && !HWIntrinsicInfo::CopiesUpperBits(intrinsicId) &&
+ IsContainableHWIntrinsicOp(node, op1, &supportsOp1RegOptional))
{
- // Intrinsics with CopyUpperBits semantics cannot have op1 be contained
-
- if (!HWIntrinsicInfo::CopiesUpperBits(intrinsicId))
- {
- // 231 form: op3 = (op2 * op3) + [op1]
- MakeSrcContained(node, op1);
- }
+ // result = ([op1] * op2) + op3
+ MakeSrcContained(node, op1);
}
- else
+ else if (supportsOp3RegOptional)
{
- assert(supportsRegOptional);
-
- // TODO-XArch-CQ: Technically any one of the three operands can
- // be reg-optional. With a limitation on op1 where
- // it can only be so if CopyUpperBits is off.
- // https://github.com/dotnet/runtime/issues/6358
-
- // 213 form: op1 = (op2 * op1) + op3
+ assert(resultOpNum != 3);
op3->SetRegOptional();
}
+ else if (supportsOp2RegOptional)
+ {
+ assert(resultOpNum != 2);
+ op2->SetRegOptional();
+ }
+ else if (supportsOp1RegOptional)
+ {
+ op1->SetRegOptional();
+ }
}
else
{
@@ -6403,8 +6093,8 @@ void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node)
MakeSrcContained(node, op1);
// MultiplyNoFlags is a Commutative operation, so swap the first two operands here
// to make the containment checks in codegen significantly simpler
- originalArgList->Current() = op2;
- originalArgList->Rest()->Current() = op1;
+ node->Op(1) = op2;
+ node->Op(2) = op1;
}
else if (supportsRegOptional)
{
diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp
index 9283919ae5ef6e..b64930c5913f14 100644
--- a/src/coreclr/jit/lsra.cpp
+++ b/src/coreclr/jit/lsra.cpp
@@ -190,9 +190,8 @@ weight_t LinearScan::getWeight(RefPosition* refPos)
{
// Tracked locals: use weighted ref cnt as the weight of the
// ref position.
- GenTreeLclVarCommon* lclCommon = treeNode->AsLclVarCommon();
- LclVarDsc* varDsc = &(compiler->lvaTable[lclCommon->GetLclNum()]);
- weight = varDsc->lvRefCntWtd();
+ const LclVarDsc* varDsc = compiler->lvaGetDesc(treeNode->AsLclVarCommon());
+ weight = varDsc->lvRefCntWtd();
if (refPos->getInterval()->isSpilled)
{
// Decrease the weight if the interval has already been spilled.
@@ -1385,7 +1384,7 @@ void LinearScan::recordVarLocationsAtStartOfBB(BasicBlock* bb)
void Interval::setLocalNumber(Compiler* compiler, unsigned lclNum, LinearScan* linScan)
{
- LclVarDsc* varDsc = &compiler->lvaTable[lclNum];
+ const LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum);
assert(varDsc->lvTracked);
assert(varDsc->lvVarIndex < compiler->lvaTrackedCount);
@@ -1501,7 +1500,7 @@ bool LinearScan::isRegCandidate(LclVarDsc* varDsc)
// Pinned variables may not be tracked (a condition of the GCInfo representation)
// 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);
+ unsigned lclNum = compiler->lvaGetLclNum(varDsc);
if (varDsc->IsAddressExposed() || !varDsc->IsEnregisterableType() ||
(!compiler->compEnregStructLocals() && (varDsc->lvType == TYP_STRUCT)))
{
@@ -1918,8 +1917,8 @@ void LinearScan::identifyCandidates()
}
#endif
- JITDUMP("floatVarCount = %d; hasLoops = %d, singleExit = %d\n", floatVarCount, compiler->fgHasLoops,
- (compiler->fgReturnBlocks == nullptr || compiler->fgReturnBlocks->next == nullptr));
+ JITDUMP("floatVarCount = %d; hasLoops = %s, singleExit = %s\n", floatVarCount, dspBool(compiler->fgHasLoops),
+ dspBool(compiler->fgReturnBlocks == nullptr || compiler->fgReturnBlocks->next == nullptr));
// Determine whether to use the 2nd, more aggressive, threshold for fp callee saves.
if (floatVarCount > 6 && compiler->fgHasLoops &&
@@ -2476,7 +2475,7 @@ void LinearScan::dumpVarRefPositions(const char* title)
{
printf("--- V%02u", i);
- LclVarDsc* varDsc = compiler->lvaTable + i;
+ const LclVarDsc* varDsc = compiler->lvaGetDesc(i);
if (varDsc->lvIsRegCandidate())
{
Interval* interval = getIntervalForLocalVar(varDsc->lvVarIndex);
@@ -5766,7 +5765,7 @@ void LinearScan::writeLocalReg(GenTreeLclVar* lclNode, unsigned varNum, regNumbe
else
{
assert(compiler->lvaEnregMultiRegVars);
- LclVarDsc* parentVarDsc = compiler->lvaGetDesc(lclNode->GetLclNum());
+ LclVarDsc* parentVarDsc = compiler->lvaGetDesc(lclNode);
assert(parentVarDsc->lvPromoted);
unsigned regIndex = varNum - parentVarDsc->lvFieldLclStart;
assert(regIndex < MAX_MULTIREG_COUNT);
@@ -6275,7 +6274,7 @@ void LinearScan::insertUpperVectorSave(GenTree* tree,
return;
}
- LclVarDsc* varDsc = compiler->lvaTable + lclVarInterval->varNum;
+ LclVarDsc* varDsc = compiler->lvaGetDesc(lclVarInterval->varNum);
assert(Compiler::varTypeNeedsPartialCalleeSave(varDsc->GetRegisterType()));
// On Arm64, we must always have a register to save the upper half,
@@ -6297,9 +6296,8 @@ void LinearScan::insertUpperVectorSave(GenTree* tree,
saveLcl->SetRegNum(lclVarReg);
SetLsraAdded(saveLcl);
- GenTreeSIMD* simdNode =
- new (compiler, GT_SIMD) GenTreeSIMD(LargeVectorSaveType, saveLcl, nullptr, SIMDIntrinsicUpperSave,
- varDsc->GetSimdBaseJitType(), genTypeSize(varDsc->lvType));
+ GenTreeSIMD* simdNode = compiler->gtNewSIMDNode(LargeVectorSaveType, saveLcl, SIMDIntrinsicUpperSave,
+ varDsc->GetSimdBaseJitType(), genTypeSize(varDsc));
if (simdNode->GetSimdBaseJitType() == CORINFO_TYPE_UNDEF)
{
@@ -6356,7 +6354,7 @@ void LinearScan::insertUpperVectorRestore(GenTree* tree,
// We should not call this method if the lclVar is not in a register (we should have simply marked the entire
// lclVar as spilled).
assert(lclVarReg != REG_NA);
- LclVarDsc* varDsc = compiler->lvaTable + lclVarInterval->varNum;
+ LclVarDsc* varDsc = compiler->lvaGetDesc(lclVarInterval->varNum);
assert(Compiler::varTypeNeedsPartialCalleeSave(varDsc->GetRegisterType()));
GenTree* restoreLcl = nullptr;
@@ -6364,9 +6362,8 @@ void LinearScan::insertUpperVectorRestore(GenTree* tree,
restoreLcl->SetRegNum(lclVarReg);
SetLsraAdded(restoreLcl);
- GenTreeSIMD* simdNode =
- new (compiler, GT_SIMD) GenTreeSIMD(varDsc->lvType, restoreLcl, nullptr, SIMDIntrinsicUpperRestore,
- varDsc->GetSimdBaseJitType(), genTypeSize(varDsc->lvType));
+ GenTreeSIMD* simdNode = compiler->gtNewSIMDNode(varDsc->TypeGet(), restoreLcl, SIMDIntrinsicUpperRestore,
+ varDsc->GetSimdBaseJitType(), genTypeSize(varDsc->lvType));
if (simdNode->GetSimdBaseJitType() == CORINFO_TYPE_UNDEF)
{
@@ -7223,7 +7220,7 @@ void LinearScan::resolveRegisters()
void LinearScan::insertMove(
BasicBlock* block, GenTree* insertionPoint, unsigned lclNum, regNumber fromReg, regNumber toReg)
{
- LclVarDsc* varDsc = compiler->lvaTable + lclNum;
+ LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum);
// the lclVar must be a register candidate
assert(isRegCandidate(varDsc));
// One or both MUST be a register
@@ -7322,8 +7319,8 @@ void LinearScan::insertSwap(
}
#endif // DEBUG
- LclVarDsc* varDsc1 = compiler->lvaTable + lclNum1;
- LclVarDsc* varDsc2 = compiler->lvaTable + lclNum2;
+ LclVarDsc* varDsc1 = compiler->lvaGetDesc(lclNum1);
+ LclVarDsc* varDsc2 = compiler->lvaGetDesc(lclNum2);
assert(reg1 != REG_STK && reg1 != REG_NA && reg2 != REG_STK && reg2 != REG_NA);
GenTree* lcl1 = compiler->gtNewLclvNode(lclNum1, varDsc1->TypeGet());
@@ -9262,9 +9259,8 @@ void LinearScan::lsraGetOperandString(GenTree* tree,
if (tree->IsMultiRegNode())
{
- unsigned regCount = tree->IsMultiRegLclVar()
- ? compiler->lvaGetDesc(tree->AsLclVar()->GetLclNum())->lvFieldCnt
- : tree->GetMultiRegCount();
+ unsigned regCount = tree->IsMultiRegLclVar() ? compiler->lvaGetDesc(tree->AsLclVar())->lvFieldCnt
+ : tree->GetMultiRegCount();
for (unsigned regIndex = 1; regIndex < regCount; regIndex++)
{
charCount = _snprintf_s(operandString, operandStringLength, operandStringLength, ",%s%s",
@@ -9321,7 +9317,7 @@ void LinearScan::lsraDispNode(GenTree* tree, LsraTupleDumpMode mode, bool hasDes
if (tree->IsLocal())
{
varNum = tree->AsLclVarCommon()->GetLclNum();
- varDsc = &(compiler->lvaTable[varNum]);
+ varDsc = compiler->lvaGetDesc(varNum);
if (varDsc->lvLRACandidate)
{
hasDest = false;
@@ -9469,7 +9465,7 @@ void LinearScan::TupleStyleDump(LsraTupleDumpMode mode)
{
reg = currentRefPosition->assignedReg();
}
- LclVarDsc* varDsc = &(compiler->lvaTable[interval->varNum]);
+ const LclVarDsc* varDsc = compiler->lvaGetDesc(interval->varNum);
printf("(");
regNumber assignedReg = varDsc->GetRegNum();
regNumber argReg = (varDsc->lvIsRegArg) ? varDsc->GetArgReg() : REG_STK;
@@ -10838,8 +10834,8 @@ void LinearScan::verifyResolutionMove(GenTree* resolutionMove, LsraLocation curr
GenTreeLclVarCommon* right = dst->gtGetOp2()->AsLclVarCommon();
regNumber leftRegNum = left->GetRegNum();
regNumber rightRegNum = right->GetRegNum();
- LclVarDsc* leftVarDsc = compiler->lvaTable + left->GetLclNum();
- LclVarDsc* rightVarDsc = compiler->lvaTable + right->GetLclNum();
+ LclVarDsc* leftVarDsc = compiler->lvaGetDesc(left);
+ LclVarDsc* rightVarDsc = compiler->lvaGetDesc(right);
Interval* leftInterval = getIntervalForLocalVar(leftVarDsc->lvVarIndex);
Interval* rightInterval = getIntervalForLocalVar(rightVarDsc->lvVarIndex);
assert(leftInterval->physReg == leftRegNum && rightInterval->physReg == rightRegNum);
diff --git a/src/coreclr/jit/lsra.h b/src/coreclr/jit/lsra.h
index ce94501a396712..1b549424f0fc44 100644
--- a/src/coreclr/jit/lsra.h
+++ b/src/coreclr/jit/lsra.h
@@ -577,7 +577,7 @@ inline bool leafAddInRange(GenTree* leaf, int lower, int upper, int multiple = 1
return leafInRange(leaf->gtGetOp2(), lower, upper, multiple);
}
-inline bool isCandidateVar(LclVarDsc* varDsc)
+inline bool isCandidateVar(const LclVarDsc* varDsc)
{
return varDsc->lvLRACandidate;
}
@@ -1015,10 +1015,7 @@ class LinearScan : public LinearScanInterface
{
if (tree->IsLocal())
{
- unsigned int lclNum = tree->AsLclVarCommon()->GetLclNum();
- assert(lclNum < compiler->lvaCount);
- LclVarDsc* varDsc = compiler->lvaTable + tree->AsLclVarCommon()->GetLclNum();
-
+ const LclVarDsc* varDsc = compiler->lvaGetDesc(tree->AsLclVarCommon());
return isCandidateVar(varDsc);
}
return false;
@@ -1097,7 +1094,7 @@ class LinearScan : public LinearScanInterface
Interval* getIntervalForLocalVarNode(GenTreeLclVarCommon* tree)
{
- LclVarDsc* varDsc = &compiler->lvaTable[tree->GetLclNum()];
+ const LclVarDsc* varDsc = compiler->lvaGetDesc(tree);
assert(varDsc->lvTracked);
return getIntervalForLocalVar(varDsc->lvVarIndex);
}
@@ -1804,13 +1801,13 @@ class LinearScan : public LinearScanInterface
void setDelayFree(RefPosition* use);
int BuildBinaryUses(GenTreeOp* node, regMaskTP candidates = RBM_NONE);
#ifdef TARGET_XARCH
- int BuildRMWUses(GenTreeOp* node, regMaskTP candidates = RBM_NONE);
+ int BuildRMWUses(GenTree* node, GenTree* op1, GenTree* op2, regMaskTP candidates = RBM_NONE);
#endif // !TARGET_XARCH
// This is the main entry point for building the RefPositions for a node.
// These methods return the number of sources.
int BuildNode(GenTree* tree);
- void getTgtPrefOperands(GenTreeOp* tree, bool& prefOp1, bool& prefOp2);
+ void getTgtPrefOperands(GenTree* tree, GenTree* op1, GenTree* op2, bool* prefOp1, bool* prefOp2);
bool supportsSpecialPutArg();
int BuildSimple(GenTree* tree);
diff --git a/src/coreclr/jit/lsraarm.cpp b/src/coreclr/jit/lsraarm.cpp
index b4c61c78c222f2..fb5ad16006dad7 100644
--- a/src/coreclr/jit/lsraarm.cpp
+++ b/src/coreclr/jit/lsraarm.cpp
@@ -249,7 +249,7 @@ int LinearScan::BuildNode(GenTree* tree)
case GT_STORE_LCL_VAR:
if (tree->IsMultiRegLclVar() && isCandidateMultiRegLclVar(tree->AsLclVar()))
{
- dstCount = compiler->lvaGetDesc(tree->AsLclVar()->GetLclNum())->lvFieldCnt;
+ dstCount = compiler->lvaGetDesc(tree->AsLclVar())->lvFieldCnt;
}
FALLTHROUGH;
@@ -368,6 +368,7 @@ int LinearScan::BuildNode(GenTree* tree)
FALLTHROUGH;
case GT_AND:
+ case GT_AND_NOT:
case GT_OR:
case GT_XOR:
case GT_LSH:
@@ -432,7 +433,6 @@ int LinearScan::BuildNode(GenTree* tree)
srcCount = 0;
break;
- case GT_LIST:
case GT_ARGPLACE:
case GT_NO_OP:
case GT_START_NONGC:
@@ -818,7 +818,7 @@ int LinearScan::BuildNode(GenTree* tree)
default:
#ifdef DEBUG
char message[256];
- _snprintf_s(message, _countof(message), _TRUNCATE, "NYI: Unimplemented node type %s",
+ _snprintf_s(message, ArrLen(message), _TRUNCATE, "NYI: Unimplemented node type %s",
GenTree::OpName(tree->OperGet()));
NYIRAW(message);
#endif
diff --git a/src/coreclr/jit/lsraarm64.cpp b/src/coreclr/jit/lsraarm64.cpp
index b7acad960d45e3..8a6393e2351958 100644
--- a/src/coreclr/jit/lsraarm64.cpp
+++ b/src/coreclr/jit/lsraarm64.cpp
@@ -106,7 +106,7 @@ int LinearScan::BuildNode(GenTree* tree)
case GT_STORE_LCL_VAR:
if (tree->IsMultiRegLclVar() && isCandidateMultiRegLclVar(tree->AsLclVar()))
{
- dstCount = compiler->lvaGetDesc(tree->AsLclVar()->GetLclNum())->lvFieldCnt;
+ dstCount = compiler->lvaGetDesc(tree->AsLclVar())->lvFieldCnt;
}
FALLTHROUGH;
@@ -121,7 +121,6 @@ int LinearScan::BuildNode(GenTree* tree)
srcCount = 0;
break;
- case GT_LIST:
case GT_ARGPLACE:
case GT_NO_OP:
case GT_START_NONGC:
@@ -268,6 +267,7 @@ int LinearScan::BuildNode(GenTree* tree)
FALLTHROUGH;
case GT_AND:
+ case GT_AND_NOT:
case GT_OR:
case GT_XOR:
case GT_LSH:
@@ -279,6 +279,12 @@ int LinearScan::BuildNode(GenTree* tree)
BuildDef(tree);
break;
+ case GT_BFIZ:
+ assert(tree->gtGetOp1()->OperIs(GT_CAST));
+ srcCount = BuildOperandUses(tree->gtGetOp1()->gtGetOp1());
+ BuildDef(tree);
+ break;
+
case GT_RETURNTRAP:
// this just turns into a compare of its child with an int
// + a conditional call
@@ -682,7 +688,16 @@ int LinearScan::BuildNode(GenTree* tree)
if (index != nullptr)
{
srcCount++;
- BuildUse(index);
+ if (index->OperIs(GT_BFIZ) && index->isContained())
+ {
+ GenTreeCast* cast = index->gtGetOp1()->AsCast();
+ assert(cast->isContained() && (cns == 0));
+ BuildUse(cast->CastOp());
+ }
+ else
+ {
+ BuildUse(index);
+ }
}
assert(dstCount == 1);
@@ -786,17 +801,14 @@ int LinearScan::BuildSIMD(GenTreeSIMD* simdTree)
// Only SIMDIntrinsicInit can be contained
if (simdTree->isContained())
{
- assert(simdTree->gtSIMDIntrinsicID == SIMDIntrinsicInit);
+ assert(simdTree->GetSIMDIntrinsicId() == SIMDIntrinsicInit);
}
int dstCount = simdTree->IsValue() ? 1 : 0;
assert(dstCount == 1);
bool buildUses = true;
- GenTree* op1 = simdTree->gtGetOp1();
- GenTree* op2 = simdTree->gtGetOp2();
-
- switch (simdTree->gtSIMDIntrinsicID)
+ switch (simdTree->GetSIMDIntrinsicId())
{
case SIMDIntrinsicInit:
case SIMDIntrinsicCast:
@@ -804,8 +816,6 @@ int LinearScan::BuildSIMD(GenTreeSIMD* simdTree)
case SIMDIntrinsicConvertToInt32:
case SIMDIntrinsicConvertToDouble:
case SIMDIntrinsicConvertToInt64:
- case SIMDIntrinsicWidenLo:
- case SIMDIntrinsicWidenHi:
// No special handling required.
break;
@@ -816,40 +826,26 @@ int LinearScan::BuildSIMD(GenTreeSIMD* simdTree)
// No special handling required.
break;
- case SIMDIntrinsicNarrow:
- {
- // Op1 will write to dst before Op2 is free
- BuildUse(op1);
- RefPosition* op2Use = BuildUse(op2);
- setDelayFree(op2Use);
- srcCount = 2;
- buildUses = false;
- break;
- }
-
case SIMDIntrinsicInitN:
{
var_types baseType = simdTree->GetSimdBaseType();
srcCount = (short)(simdTree->GetSimdSize() / genTypeSize(baseType));
+ assert(simdTree->GetOperandCount() == static_cast(srcCount));
if (varTypeIsFloating(simdTree->GetSimdBaseType()))
{
// Need an internal register to stitch together all the values into a single vector in a SIMD reg.
buildInternalFloatRegisterDefForNode(simdTree);
}
- int initCount = 0;
- for (GenTree* list = op1; list != nullptr; list = list->gtGetOp2())
+ for (GenTree* operand : simdTree->Operands())
{
- assert(list->OperGet() == GT_LIST);
- GenTree* listItem = list->gtGetOp1();
- assert(listItem->TypeGet() == baseType);
- assert(!listItem->isContained());
- BuildUse(listItem);
- initCount++;
+ assert(operand->TypeIs(baseType));
+ assert(!operand->isContained());
+
+ BuildUse(operand);
}
- assert(initCount == srcCount);
- buildUses = false;
+ buildUses = false;
break;
}
@@ -863,7 +859,6 @@ int LinearScan::BuildSIMD(GenTreeSIMD* simdTree)
case SIMDIntrinsicCopyToArrayX:
case SIMDIntrinsicNone:
case SIMDIntrinsicHWAccel:
- case SIMDIntrinsicWiden:
case SIMDIntrinsicInvalid:
assert(!"These intrinsics should not be seen during register allocation");
FALLTHROUGH;
@@ -874,12 +869,12 @@ int LinearScan::BuildSIMD(GenTreeSIMD* simdTree)
}
if (buildUses)
{
- assert(!op1->OperIs(GT_LIST));
assert(srcCount == 0);
- srcCount = BuildOperandUses(op1);
- if ((op2 != nullptr) && !op2->isContained())
+ srcCount = BuildOperandUses(simdTree->Op(1));
+
+ if ((simdTree->GetOperandCount() == 2) && !simdTree->Op(2)->isContained())
{
- srcCount += BuildOperandUses(op2);
+ srcCount += BuildOperandUses(simdTree->Op(2));
}
}
assert(internalCount <= MaxInternalCount);
diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp
index 35ae7eb3564cd5..097df9b2f0ee9c 100644
--- a/src/coreclr/jit/lsrabuild.cpp
+++ b/src/coreclr/jit/lsrabuild.cpp
@@ -695,7 +695,7 @@ bool LinearScan::isContainableMemoryOp(GenTree* node)
{
return true;
}
- LclVarDsc* varDsc = &compiler->lvaTable[node->AsLclVar()->GetLclNum()];
+ const LclVarDsc* varDsc = compiler->lvaGetDesc(node->AsLclVar());
return varDsc->lvDoNotEnregister;
}
return false;
@@ -712,6 +712,20 @@ bool LinearScan::isContainableMemoryOp(GenTree* node)
//
void LinearScan::addRefsForPhysRegMask(regMaskTP mask, LsraLocation currentLoc, RefType refType, bool isLastUse)
{
+ if (refType == RefTypeKill)
+ {
+ // The mask identifies a set of registers that will be used during
+ // codegen. Mark these as modified here, so when we do final frame
+ // layout, we'll know about all these registers. This is especially
+ // important if mask contains callee-saved registers, which affect the
+ // frame size since we need to save/restore them. In the case where we
+ // have a copyBlk with GC pointers, can need to call the
+ // CORINFO_HELP_ASSIGN_BYREF helper, which kills callee-saved RSI and
+ // RDI, if LSRA doesn't assign RSI/RDI, they wouldn't get marked as
+ // modified until codegen, which is too late.
+ compiler->codeGen->regSet.rsSetRegsModified(mask DEBUGARG(true));
+ }
+
for (regNumber reg = REG_FIRST; mask; reg = REG_NEXT(reg), mask >>= 1)
{
if (mask & 1)
@@ -971,7 +985,7 @@ regMaskTP LinearScan::getKillSetForHWIntrinsic(GenTreeHWIntrinsic* node)
{
regMaskTP killMask = RBM_NONE;
#ifdef TARGET_XARCH
- switch (node->gtHWIntrinsicId)
+ switch (node->GetHWIntrinsicId())
{
case NI_SSE2_MaskMove:
// maskmovdqu uses edi as the implicit address register.
@@ -1137,16 +1151,6 @@ bool LinearScan::buildKillPositionsForNode(GenTree* tree, LsraLocation currentLo
if (killMask != RBM_NONE)
{
- // The killMask identifies a set of registers that will be used during codegen.
- // Mark these as modified here, so when we do final frame layout, we'll know about
- // all these registers. This is especially important if killMask contains
- // callee-saved registers, which affect the frame size since we need to save/restore them.
- // In the case where we have a copyBlk with GC pointers, can need to call the
- // CORINFO_HELP_ASSIGN_BYREF helper, which kills callee-saved RSI and RDI, if
- // LSRA doesn't assign RSI/RDI, they wouldn't get marked as modified until codegen,
- // which is too late.
- compiler->codeGen->regSet.rsSetRegsModified(killMask DEBUGARG(true));
-
addRefsForPhysRegMask(killMask, currentLoc, RefTypeKill, true);
// TODO-CQ: It appears to be valuable for both fp and int registers to avoid killing the callee
@@ -1247,7 +1251,7 @@ bool LinearScan::buildKillPositionsForNode(GenTree* tree, LsraLocation currentLo
bool LinearScan::isCandidateMultiRegLclVar(GenTreeLclVar* lclNode)
{
assert(compiler->lvaEnregMultiRegVars && lclNode->IsMultiReg());
- LclVarDsc* varDsc = compiler->lvaGetDesc(lclNode->GetLclNum());
+ LclVarDsc* varDsc = compiler->lvaGetDesc(lclNode);
assert(varDsc->lvPromoted);
bool isMultiReg = (compiler->lvaGetPromotionType(varDsc) == Compiler::PROMOTION_TYPE_INDEPENDENT);
if (!isMultiReg)
@@ -1679,10 +1683,9 @@ int LinearScan::ComputeAvailableSrcCount(GenTree* node)
//
void LinearScan::buildRefPositionsForNode(GenTree* tree, LsraLocation currentLoc)
{
- // The LIR traversal doesn't visit GT_LIST or GT_ARGPLACE nodes.
+ // The LIR traversal doesn't visit GT_ARGPLACE nodes.
// GT_CLS_VAR nodes should have been eliminated by rationalizer.
assert(tree->OperGet() != GT_ARGPLACE);
- assert(tree->OperGet() != GT_LIST);
assert(tree->OperGet() != GT_CLS_VAR);
// The set of internal temporary registers used by this node are stored in the
@@ -1704,7 +1707,7 @@ void LinearScan::buildRefPositionsForNode(GenTree* tree, LsraLocation currentLoc
// address computation. In this case we need to check whether it is a last use.
if (tree->IsLocal() && ((tree->gtFlags & GTF_VAR_DEATH) != 0))
{
- LclVarDsc* const varDsc = &compiler->lvaTable[tree->AsLclVarCommon()->GetLclNum()];
+ LclVarDsc* const varDsc = compiler->lvaGetDesc(tree->AsLclVarCommon());
if (isCandidateVar(varDsc))
{
assert(varDsc->lvTracked);
@@ -2046,12 +2049,12 @@ void LinearScan::updateRegStateForArg(LclVarDsc* argDsc)
if (isFloat)
{
- JITDUMP("Float arg V%02u in reg %s\n", (argDsc - compiler->lvaTable), getRegName(argDsc->GetArgReg()));
+ JITDUMP("Float arg V%02u in reg %s\n", compiler->lvaGetLclNum(argDsc), getRegName(argDsc->GetArgReg()));
compiler->raUpdateRegStateForArg(floatRegState, argDsc);
}
else
{
- JITDUMP("Int arg V%02u in reg %s\n", (argDsc - compiler->lvaTable), getRegName(argDsc->GetArgReg()));
+ JITDUMP("Int arg V%02u in reg %s\n", compiler->lvaGetLclNum(argDsc), getRegName(argDsc->GetArgReg()));
#if FEATURE_MULTIREG_ARGS
if (argDsc->GetOtherArgReg() != REG_NA)
{
@@ -2183,7 +2186,7 @@ void LinearScan::buildIntervals()
for (unsigned fieldVarNum = argDsc->lvFieldLclStart;
fieldVarNum < argDsc->lvFieldLclStart + argDsc->lvFieldCnt; ++fieldVarNum)
{
- LclVarDsc* fieldVarDsc = &(compiler->lvaTable[fieldVarNum]);
+ const LclVarDsc* fieldVarDsc = compiler->lvaGetDesc(fieldVarNum);
if (fieldVarDsc->lvLRACandidate)
{
assert(fieldVarDsc->lvTracked);
@@ -2356,7 +2359,15 @@ void LinearScan::buildIntervals()
// into the scratch register, so it will be killed here.
if (compiler->compShouldPoisonFrame() && compiler->fgFirstBBisScratch() && block == compiler->fgFirstBB)
{
- addRefsForPhysRegMask(genRegMask(REG_SCRATCH), currentLoc + 1, RefTypeKill, true);
+ regMaskTP killed;
+#if defined(TARGET_XARCH)
+ // Poisoning uses EAX for small vars and rep stosd that kills edi, ecx and eax for large vars.
+ killed = RBM_EDI | RBM_ECX | RBM_EAX;
+#else
+ // Poisoning uses REG_SCRATCH for small vars and memset helper for big vars.
+ killed = genRegMask(REG_SCRATCH) | compiler->compHelperCallKillSet(CORINFO_HELP_MEMSET);
+#endif
+ addRefsForPhysRegMask(killed, currentLoc + 1, RefTypeKill, true);
currentLoc += 2;
}
@@ -2461,8 +2472,8 @@ void LinearScan::buildIntervals()
unsigned varIndex = 0;
while (iter.NextElem(&varIndex))
{
- unsigned varNum = compiler->lvaTrackedToVarNum[varIndex];
- LclVarDsc* varDsc = compiler->lvaTable + varNum;
+ unsigned varNum = compiler->lvaTrackedToVarNum[varIndex];
+ const LclVarDsc* varDsc = compiler->lvaGetDesc(varNum);
assert(isCandidateVar(varDsc));
Interval* interval = getIntervalForLocalVar(varIndex);
RefPosition* pos =
@@ -2480,7 +2491,7 @@ void LinearScan::buildIntervals()
while (iter.NextElem(&varIndex))
{
unsigned varNum = compiler->lvaTrackedToVarNum[varIndex];
- LclVarDsc* const varDsc = &compiler->lvaTable[varNum];
+ LclVarDsc* const varDsc = compiler->lvaGetDesc(varNum);
assert(isCandidateVar(varDsc));
RefPosition* const lastRP = getIntervalForLocalVar(varIndex)->lastRefPosition;
// We should be able to assert that lastRP is non-null if it is live-out, but sometimes liveness
@@ -2515,7 +2526,7 @@ void LinearScan::buildIntervals()
// If we need to KeepAliveAndReportThis, add a dummy exposed use of it at the end
unsigned keepAliveVarNum = compiler->info.compThisArg;
assert(compiler->info.compIsStatic == false);
- LclVarDsc* varDsc = compiler->lvaTable + keepAliveVarNum;
+ const LclVarDsc* varDsc = compiler->lvaGetDesc(keepAliveVarNum);
if (isCandidateVar(varDsc))
{
JITDUMP("Adding exposed use of this, for lvaKeepAliveAndReportThis\n");
@@ -2533,7 +2544,7 @@ void LinearScan::buildIntervals()
while (iter.NextElem(&varIndex))
{
unsigned varNum = compiler->lvaTrackedToVarNum[varIndex];
- LclVarDsc* varDsc = compiler->lvaTable + varNum;
+ LclVarDsc* varDsc = compiler->lvaGetDesc(varNum);
Interval* interval = getIntervalForLocalVar(varIndex);
assert(interval->isWriteThru);
weight_t weight = varDsc->lvRefCntWtd();
@@ -2958,7 +2969,7 @@ RefPosition* LinearScan::BuildUse(GenTree* operand, regMaskTP candidates, int mu
else if (operand->IsMultiRegLclVar())
{
assert(compiler->lvaEnregMultiRegVars);
- LclVarDsc* varDsc = compiler->lvaGetDesc(operand->AsLclVar()->GetLclNum());
+ LclVarDsc* varDsc = compiler->lvaGetDesc(operand->AsLclVar());
LclVarDsc* fieldVarDsc = compiler->lvaGetDesc(varDsc->lvFieldLclStart + multiRegIdx);
interval = getIntervalForLocalVar(fieldVarDsc->lvVarIndex);
if (operand->AsLclVar()->IsLastUse(multiRegIdx))
@@ -3021,10 +3032,22 @@ int LinearScan::BuildAddrUses(GenTree* addr, regMaskTP candidates)
BuildUse(addrMode->Base(), candidates);
srcCount++;
}
- if ((addrMode->Index() != nullptr) && !addrMode->Index()->isContained())
+ if (addrMode->Index() != nullptr)
{
- BuildUse(addrMode->Index(), candidates);
- srcCount++;
+ if (!addrMode->Index()->isContained())
+ {
+ BuildUse(addrMode->Index(), candidates);
+ srcCount++;
+ }
+#ifdef TARGET_ARM64
+ else if (addrMode->Index()->OperIs(GT_BFIZ))
+ {
+ GenTreeCast* cast = addrMode->Index()->gtGetOp1()->AsCast();
+ assert(cast->isContained());
+ BuildUse(cast->CastOp(), candidates);
+ srcCount++;
+ }
+#endif
}
return srcCount;
}
@@ -3065,9 +3088,11 @@ int LinearScan::BuildOperandUses(GenTree* node, regMaskTP candidates)
{
if (node->AsHWIntrinsic()->OperIsMemoryLoad())
{
- return BuildAddrUses(node->gtGetOp1());
+ return BuildAddrUses(node->AsHWIntrinsic()->Op(1));
}
- BuildUse(node->gtGetOp1(), candidates);
+
+ assert(node->AsHWIntrinsic()->GetOperandCount() == 1);
+ BuildUse(node->AsHWIntrinsic()->Op(1), candidates);
return 1;
}
#endif // FEATURE_HW_INTRINSICS
@@ -3129,10 +3154,13 @@ int LinearScan::BuildDelayFreeUses(GenTree* node, GenTree* rmwNode, regMaskTP ca
{
use = BuildUse(node, candidates);
}
+#ifdef FEATURE_HW_INTRINSICS
else if (node->OperIsHWIntrinsic())
{
- use = BuildUse(node->gtGetOp1(), candidates);
+ assert(node->AsHWIntrinsic()->GetOperandCount() == 1);
+ use = BuildUse(node->AsHWIntrinsic()->Op(1), candidates);
}
+#endif
else if (!node->OperIsIndir())
{
return 0;
@@ -3205,15 +3233,17 @@ int LinearScan::BuildDelayFreeUses(GenTree* node, GenTree* rmwNode, regMaskTP ca
//
int LinearScan::BuildBinaryUses(GenTreeOp* node, regMaskTP candidates)
{
+ GenTree* op1 = node->gtGetOp1();
+ GenTree* op2 = node->gtGetOp2IfPresent();
+
#ifdef TARGET_XARCH
if (node->OperIsBinary() && isRMWRegOper(node))
{
- return BuildRMWUses(node, candidates);
+ assert(op2 != nullptr);
+ return BuildRMWUses(node, op1, op2, candidates);
}
#endif // TARGET_XARCH
- int srcCount = 0;
- GenTree* op1 = node->gtOp1;
- GenTree* op2 = node->gtGetOp2IfPresent();
+ int srcCount = 0;
if (op1 != nullptr)
{
srcCount += BuildOperandUses(op1, candidates);
@@ -3279,7 +3309,7 @@ void LinearScan::BuildStoreLocDef(GenTreeLclVarCommon* storeLoc,
defCandidates = allRegs(type);
}
#else
- defCandidates = allRegs(type);
+ defCandidates = allRegs(type);
#endif // TARGET_X86
RefPosition* def = newRefPosition(varDefInterval, currentLoc + 1, RefTypeDef, storeLoc, defCandidates, index);
@@ -3311,7 +3341,7 @@ int LinearScan::BuildMultiRegStoreLoc(GenTreeLclVar* storeLoc)
GenTree* op1 = storeLoc->gtGetOp1();
unsigned int dstCount = storeLoc->GetFieldCount(compiler);
unsigned int srcCount = dstCount;
- LclVarDsc* varDsc = compiler->lvaGetDesc(storeLoc->GetLclNum());
+ LclVarDsc* varDsc = compiler->lvaGetDesc(storeLoc);
assert(compiler->lvaEnregMultiRegVars);
assert(storeLoc->OperGet() == GT_STORE_LCL_VAR);
@@ -3388,7 +3418,7 @@ int LinearScan::BuildStoreLoc(GenTreeLclVarCommon* storeLoc)
GenTree* op1 = storeLoc->gtGetOp1();
int srcCount;
RefPosition* singleUseRef = nullptr;
- LclVarDsc* varDsc = compiler->lvaGetDesc(storeLoc->GetLclNum());
+ LclVarDsc* varDsc = compiler->lvaGetDesc(storeLoc);
if (storeLoc->IsMultiRegLclVar())
{
@@ -3600,12 +3630,11 @@ int LinearScan::BuildReturn(GenTree* tree)
else
{
assert(compiler->lvaEnregMultiRegVars);
- LclVarDsc* varDsc = compiler->lvaGetDesc(op1->AsLclVar()->GetLclNum());
+ LclVarDsc* varDsc = compiler->lvaGetDesc(op1->AsLclVar());
nonCallRetTypeDesc.InitializeStructReturnType(compiler, varDsc->GetStructHnd(),
compiler->info.compCallConv);
pRetTypeDesc = &nonCallRetTypeDesc;
- assert(compiler->lvaGetDesc(op1->AsLclVar()->GetLclNum())->lvFieldCnt ==
- nonCallRetTypeDesc.GetReturnRegCount());
+ assert(compiler->lvaGetDesc(op1->AsLclVar())->lvFieldCnt == nonCallRetTypeDesc.GetReturnRegCount());
}
srcCount = pRetTypeDesc->GetReturnRegCount();
// For any source that's coming from a different register file, we need to ensure that
diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp
index 358630dc9fa30e..ca169600e83f83 100644
--- a/src/coreclr/jit/lsraxarch.cpp
+++ b/src/coreclr/jit/lsraxarch.cpp
@@ -112,7 +112,7 @@ int LinearScan::BuildNode(GenTree* tree)
case GT_STORE_LCL_VAR:
if (tree->IsMultiRegLclVar() && isCandidateMultiRegLclVar(tree->AsLclVar()))
{
- dstCount = compiler->lvaGetDesc(tree->AsLclVar()->GetLclNum())->lvFieldCnt;
+ dstCount = compiler->lvaGetDesc(tree->AsLclVar())->lvFieldCnt;
}
srcCount = BuildStoreLoc(tree->AsLclVarCommon());
break;
@@ -124,7 +124,6 @@ int LinearScan::BuildNode(GenTree* tree)
srcCount = 0;
break;
- case GT_LIST:
case GT_ARGPLACE:
case GT_NO_OP:
case GT_START_NONGC:
@@ -701,6 +700,8 @@ int LinearScan::BuildNode(GenTree* tree)
//
// Arguments:
// tree - the node of interest.
+// op1 - its first operand
+// op2 - its second operand
// prefOp1 - a bool "out" parameter indicating, on return, whether op1 should be preferenced to the target.
// prefOp2 - a bool "out" parameter indicating, on return, whether op2 should be preferenced to the target.
//
@@ -710,27 +711,24 @@ int LinearScan::BuildNode(GenTree* tree)
// Notes:
// The caller is responsible for initializing the two "out" parameters to false.
//
-void LinearScan::getTgtPrefOperands(GenTreeOp* tree, bool& prefOp1, bool& prefOp2)
+void LinearScan::getTgtPrefOperands(GenTree* tree, GenTree* op1, GenTree* op2, bool* prefOp1, bool* prefOp2)
{
// If op2 of a binary-op gets marked as contained, then binary-op srcCount will be 1.
// Even then we would like to set isTgtPref on Op1.
- if (tree->OperIsBinary() && isRMWRegOper(tree))
+ if (isRMWRegOper(tree))
{
- GenTree* op1 = tree->gtGetOp1();
- GenTree* op2 = tree->gtGetOp2();
-
// If we have a read-modify-write operation, we want to preference op1 to the target,
// if it is not contained.
- if (!op1->isContained() && !op1->OperIs(GT_LIST))
+ if (!op1->isContained())
{
- prefOp1 = true;
+ *prefOp1 = true;
}
// Commutative opers like add/mul/and/or/xor could reverse the order of operands if it is safe to do so.
// In that case we will preference both, to increase the chance of getting a match.
- if (tree->OperIsCommutative() && op2 != nullptr && !op2->isContained())
+ if (tree->OperIsCommutative() && (op2 != nullptr) && !op2->isContained())
{
- prefOp2 = true;
+ *prefOp2 = true;
}
}
}
@@ -751,7 +749,13 @@ bool LinearScan::isRMWRegOper(GenTree* tree)
{
// TODO-XArch-CQ: Make this more accurate.
// For now, We assume that most binary operators are of the RMW form.
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#ifdef FEATURE_HW_INTRINSICS
+ assert(tree->OperIsBinary() || (tree->OperIsMultiOp() && (tree->AsMultiOp()->GetOperandCount() <= 2)));
+#else
assert(tree->OperIsBinary());
+#endif
if (tree->OperIsCompare() || tree->OperIs(GT_CMP) || tree->OperIs(GT_BT))
{
@@ -801,11 +805,9 @@ bool LinearScan::isRMWRegOper(GenTree* tree)
}
// Support for building RefPositions for RMW nodes.
-int LinearScan::BuildRMWUses(GenTreeOp* node, regMaskTP candidates)
+int LinearScan::BuildRMWUses(GenTree* node, GenTree* op1, GenTree* op2, regMaskTP candidates)
{
int srcCount = 0;
- GenTree* op1 = node->gtOp1;
- GenTree* op2 = node->gtGetOp2IfPresent();
regMaskTP op1Candidates = candidates;
regMaskTP op2Candidates = candidates;
@@ -828,7 +830,7 @@ int LinearScan::BuildRMWUses(GenTreeOp* node, regMaskTP candidates)
bool prefOp1 = false;
bool prefOp2 = false;
- getTgtPrefOperands(node, prefOp1, prefOp2);
+ getTgtPrefOperands(node, op1, op2, &prefOp1, &prefOp2);
assert(!prefOp2 || node->OperIsCommutative());
// Determine which operand, if any, should be delayRegFree. Normally, this would be op2,
@@ -1873,14 +1875,12 @@ int LinearScan::BuildSIMD(GenTreeSIMD* simdTree)
if (simdTree->isContained())
{
// Only SIMDIntrinsicInit can be contained
- assert(simdTree->gtSIMDIntrinsicID == SIMDIntrinsicInit);
+ assert(simdTree->GetSIMDIntrinsicId() == SIMDIntrinsicInit);
}
SetContainsAVXFlags(simdTree->GetSimdSize());
- GenTree* op1 = simdTree->gtGetOp1();
- GenTree* op2 = simdTree->gtGetOp2();
- int srcCount = 0;
+ int srcCount = 0;
- switch (simdTree->gtSIMDIntrinsicID)
+ switch (simdTree->GetSIMDIntrinsicId())
{
case SIMDIntrinsicInit:
{
@@ -1893,6 +1893,8 @@ int LinearScan::BuildSIMD(GenTreeSIMD* simdTree)
CLANG_FORMAT_COMMENT_ANCHOR;
#if !defined(TARGET_64BIT)
+ GenTree* op1 = simdTree->Op(1);
+
if (op1->OperGet() == GT_LONG)
{
assert(op1->isContained());
@@ -1928,19 +1930,19 @@ int LinearScan::BuildSIMD(GenTreeSIMD* simdTree)
{
var_types baseType = simdTree->GetSimdBaseType();
srcCount = (short)(simdTree->GetSimdSize() / genTypeSize(baseType));
+ assert(simdTree->GetOperandCount() == static_cast(srcCount));
+
// Need an internal register to stitch together all the values into a single vector in a SIMD reg.
buildInternalFloatRegisterDefForNode(simdTree);
- int initCount = 0;
- for (GenTree* list = op1; list != nullptr; list = list->gtGetOp2())
+
+ for (GenTree* operand : simdTree->Operands())
{
- assert(list->OperGet() == GT_LIST);
- GenTree* listItem = list->gtGetOp1();
- assert(listItem->TypeGet() == baseType);
- assert(!listItem->isContained());
- BuildUse(listItem);
- initCount++;
+ assert(operand->TypeIs(baseType));
+ assert(!operand->isContained());
+
+ BuildUse(operand);
}
- assert(initCount == srcCount);
+
buildUses = false;
}
break;
@@ -1975,16 +1977,6 @@ int LinearScan::BuildSIMD(GenTreeSIMD* simdTree)
case SIMDIntrinsicConvertToInt32:
break;
- case SIMDIntrinsicWidenLo:
- case SIMDIntrinsicWidenHi:
- if (varTypeIsIntegral(simdTree->GetSimdBaseType()))
- {
- // We need an internal register different from targetReg.
- setInternalRegsDelayFree = true;
- buildInternalFloatRegisterDefForNode(simdTree);
- }
- break;
-
case SIMDIntrinsicConvertToInt64:
// We need an internal register different from targetReg.
setInternalRegsDelayFree = true;
@@ -2018,19 +2010,9 @@ int LinearScan::BuildSIMD(GenTreeSIMD* simdTree)
buildInternalIntRegisterDefForNode(simdTree);
break;
- case SIMDIntrinsicNarrow:
- // We need an internal register different from targetReg.
- setInternalRegsDelayFree = true;
- buildInternalFloatRegisterDefForNode(simdTree);
- if ((compiler->getSIMDSupportLevel() == SIMD_AVX2_Supported) && (simdTree->GetSimdBaseType() != TYP_DOUBLE))
- {
- buildInternalFloatRegisterDefForNode(simdTree);
- }
- break;
-
case SIMDIntrinsicShuffleSSE2:
// Second operand is an integer constant and marked as contained.
- assert(simdTree->gtGetOp2()->isContainedIntOrIImmed());
+ assert(simdTree->Op(2)->isContainedIntOrIImmed());
break;
default:
@@ -2039,10 +2021,11 @@ int LinearScan::BuildSIMD(GenTreeSIMD* simdTree)
}
if (buildUses)
{
- assert(!op1->OperIs(GT_LIST));
assert(srcCount == 0);
// This is overly conservative, but is here for zero diffs.
- srcCount = BuildRMWUses(simdTree);
+ GenTree* op1 = simdTree->Op(1);
+ GenTree* op2 = (simdTree->GetOperandCount() == 2) ? simdTree->Op(2) : nullptr;
+ srcCount = BuildRMWUses(simdTree, op1, op2);
}
buildInternalRegisterUses();
BuildDef(simdTree, dstCandidates);
@@ -2062,10 +2045,10 @@ int LinearScan::BuildSIMD(GenTreeSIMD* simdTree)
//
int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree)
{
- NamedIntrinsic intrinsicId = intrinsicTree->gtHWIntrinsicId;
+ NamedIntrinsic intrinsicId = intrinsicTree->GetHWIntrinsicId();
var_types baseType = intrinsicTree->GetSimdBaseType();
+ size_t numArgs = intrinsicTree->GetOperandCount();
HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(intrinsicId);
- int numArgs = HWIntrinsicInfo::lookupNumArgs(intrinsicTree);
// Set the AVX Flags if this instruction may use VEX encoding for SIMD operations.
// Note that this may be true even if the ISA is not AVX (e.g. for platform-agnostic intrinsics
@@ -2075,60 +2058,21 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree)
SetContainsAVXFlags(intrinsicTree->GetSimdSize());
}
- GenTree* op1 = intrinsicTree->gtGetOp1();
- GenTree* op2 = intrinsicTree->gtGetOp2();
- GenTree* op3 = nullptr;
- GenTree* lastOp = nullptr;
-
int srcCount = 0;
int dstCount = intrinsicTree->IsValue() ? 1 : 0;
regMaskTP dstCandidates = RBM_NONE;
- if (op1 == nullptr)
+ if (intrinsicTree->GetOperandCount() == 0)
{
- assert(op2 == nullptr);
assert(numArgs == 0);
}
else
{
- if (op1->OperIsList())
- {
- assert(op2 == nullptr);
- assert(numArgs >= 3);
-
- GenTreeArgList* argList = op1->AsArgList();
-
- op1 = argList->Current();
- argList = argList->Rest();
-
- op2 = argList->Current();
- argList = argList->Rest();
-
- op3 = argList->Current();
-
- while (argList->Rest() != nullptr)
- {
- argList = argList->Rest();
- }
-
- lastOp = argList->Current();
- argList = argList->Rest();
-
- assert(argList == nullptr);
- }
- else if (op2 != nullptr)
- {
- assert(numArgs == 2);
- lastOp = op2;
- }
- else
- {
- assert(numArgs == 1);
- lastOp = op1;
- }
-
- assert(lastOp != nullptr);
+ GenTree* op1 = intrinsicTree->Op(1);
+ GenTree* op2 = (numArgs >= 2) ? intrinsicTree->Op(2) : nullptr;
+ GenTree* op3 = (numArgs >= 3) ? intrinsicTree->Op(3) : nullptr;
+ GenTree* lastOp = intrinsicTree->Op(numArgs);
bool buildUses = true;
@@ -2328,48 +2272,93 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree)
const bool copiesUpperBits = HWIntrinsicInfo::CopiesUpperBits(intrinsicId);
- // Intrinsics with CopyUpperBits semantics cannot have op1 be contained
- assert(!copiesUpperBits || !op1->isContained());
+ unsigned resultOpNum = 0;
+ LIR::Use use;
+ GenTree* user = nullptr;
- if (op2->isContained())
+ if (LIR::AsRange(blockSequence[curBBSeqNum]).TryGetUse(intrinsicTree, &use))
{
- // 132 form: op1 = (op1 * op3) + [op2]
+ user = use.User();
+ }
+ resultOpNum = intrinsicTree->GetResultOpNumForFMA(user, op1, op2, op3);
- tgtPrefUse = BuildUse(op1);
+ unsigned containedOpNum = 0;
- srcCount += 1;
- srcCount += BuildOperandUses(op2);
- srcCount += BuildDelayFreeUses(op3, op1);
+ // containedOpNum remains 0 when no operand is contained or regOptional
+ if (op1->isContained() || op1->IsRegOptional())
+ {
+ containedOpNum = 1;
}
- else if (op1->isContained())
+ else if (op2->isContained() || op2->IsRegOptional())
{
- // 231 form: op3 = (op2 * op3) + [op1]
-
- tgtPrefUse = BuildUse(op3);
-
- srcCount += BuildOperandUses(op1);
- srcCount += BuildDelayFreeUses(op2, op1);
- srcCount += 1;
+ containedOpNum = 2;
}
- else
+ else if (op3->isContained() || op3->IsRegOptional())
{
- // 213 form: op1 = (op2 * op1) + [op3]
+ containedOpNum = 3;
+ }
- tgtPrefUse = BuildUse(op1);
- srcCount += 1;
+ GenTree* emitOp1 = op1;
+ GenTree* emitOp2 = op2;
+ GenTree* emitOp3 = op3;
- if (copiesUpperBits)
+ // Intrinsics with CopyUpperBits semantics must have op1 as target
+ assert(containedOpNum != 1 || !copiesUpperBits);
+
+ if (containedOpNum == 1)
+ {
+ // https://github.com/dotnet/runtime/issues/62215
+ // resultOpNum might change between lowering and lsra, comment out assertion for now.
+ // assert(containedOpNum != resultOpNum);
+ // resultOpNum is 3 or 0: op3/? = ([op1] * op2) + op3
+ std::swap(emitOp1, emitOp3);
+
+ if (resultOpNum == 2)
{
- srcCount += BuildDelayFreeUses(op2, op1);
+ // op2 = ([op1] * op2) + op3
+ std::swap(emitOp2, emitOp3);
}
- else
+ }
+ else if (containedOpNum == 3)
+ {
+ // assert(containedOpNum != resultOpNum);
+ if (resultOpNum == 2 && !copiesUpperBits)
{
- tgtPrefUse2 = BuildUse(op2);
- srcCount += 1;
+ // op2 = (op1 * op2) + [op3]
+ std::swap(emitOp1, emitOp2);
}
+ // else: op1/? = (op1 * op2) + [op3]
+ }
+ else if (containedOpNum == 2)
+ {
+ // assert(containedOpNum != resultOpNum);
- srcCount += op3->isContained() ? BuildOperandUses(op3) : BuildDelayFreeUses(op3, op1);
+ // op1/? = (op1 * [op2]) + op3
+ std::swap(emitOp2, emitOp3);
+ if (resultOpNum == 3 && !copiesUpperBits)
+ {
+ // op3 = (op1 * [op2]) + op3
+ std::swap(emitOp1, emitOp2);
+ }
+ }
+ else
+ {
+ // containedOpNum == 0
+ // no extra work when resultOpNum is 0 or 1
+ if (resultOpNum == 2)
+ {
+ std::swap(emitOp1, emitOp2);
+ }
+ else if (resultOpNum == 3)
+ {
+ std::swap(emitOp1, emitOp3);
+ }
}
+ tgtPrefUse = BuildUse(emitOp1);
+
+ srcCount += 1;
+ srcCount += BuildDelayFreeUses(emitOp2, emitOp1);
+ srcCount += emitOp3->isContained() ? BuildOperandUses(emitOp3) : BuildDelayFreeUses(emitOp3, emitOp1);
buildUses = false;
break;
@@ -2413,12 +2402,10 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree)
case NI_AVX2_GatherMaskVector128:
case NI_AVX2_GatherMaskVector256:
{
- assert(numArgs == 5);
assert(!isRMW);
- assert(intrinsicTree->gtGetOp1()->OperIsList());
- GenTreeArgList* argList = intrinsicTree->gtGetOp1()->AsArgList()->Rest()->Rest()->Rest();
- GenTree* op4 = argList->Current();
+ GenTree* op4 = intrinsicTree->Op(4);
+ GenTree* op5 = intrinsicTree->Op(5);
// Any pair of the index, mask, or destination registers should be different
srcCount += BuildOperandUses(op1);
@@ -2427,7 +2414,7 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree)
srcCount += BuildDelayFreeUses(op4);
// op5 should always be contained
- assert(argList->Rest()->Current()->isContained());
+ assert(op5->isContained());
// get a tmp register for mask that will be cleared by gather instructions
buildInternalFloatRegisterDefForNode(intrinsicTree, allSIMDRegs());
@@ -2466,7 +2453,7 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree)
{
if (op2->OperIs(GT_HWINTRINSIC) && op2->AsHWIntrinsic()->OperIsMemoryLoad() && op2->isContained())
{
- srcCount += BuildAddrUses(op2->gtGetOp1());
+ srcCount += BuildAddrUses(op2->AsHWIntrinsic()->Op(1));
}
else if (isRMW)
{
diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp
index 83946626f25d92..0900793a167726 100644
--- a/src/coreclr/jit/morph.cpp
+++ b/src/coreclr/jit/morph.cpp
@@ -1712,27 +1712,6 @@ void fgArgInfo::SortArgs()
assert(begTab == (endTab + 1));
assert(argsRemaining == 0);
-#if !FEATURE_FIXED_OUT_ARGS
- // Finally build the regArgList
- //
- callTree->AsCall()->regArgList = NULL;
- callTree->AsCall()->regArgListCount = regCount;
-
- unsigned regInx = 0;
- for (curInx = 0; curInx < argCount; curInx++)
- {
- fgArgTabEntry* curArgTabEntry = argTable[curInx];
-
- if (curArgTabEntry->GetRegNum() != REG_STK)
- {
- // Encode the argument register in the register mask
- //
- callTree->AsCall()->regArgList[regInx] = curArgTabEntry->GetRegNum();
- regInx++;
- }
- }
-#endif // !FEATURE_FIXED_OUT_ARGS
-
argsSorted = true;
}
@@ -1761,7 +1740,7 @@ void fgArgInfo::Dump(Compiler* compiler) const
GenTree* Compiler::fgMakeTmpArgNode(fgArgTabEntry* curArgTabEntry)
{
unsigned tmpVarNum = curArgTabEntry->tmpNum;
- LclVarDsc* varDsc = &lvaTable[tmpVarNum];
+ LclVarDsc* varDsc = lvaGetDesc(tmpVarNum);
assert(varDsc->lvIsTemp);
var_types type = varDsc->TypeGet();
@@ -1973,7 +1952,7 @@ void fgArgInfo::EvalArgsToTemps()
{
setupArg = compiler->gtNewTempAssign(tmpVarNum, argx);
- LclVarDsc* varDsc = compiler->lvaTable + tmpVarNum;
+ LclVarDsc* varDsc = compiler->lvaGetDesc(tmpVarNum);
var_types lclVarType = genActualType(argx->gtType);
var_types scalarType = TYP_UNKNOWN;
@@ -2725,7 +2704,11 @@ void Compiler::fgInitArgInfo(GenTreeCall* call)
GenTreeCall::Use* nodeArgs = call->gtCallArgs;
// It could include many arguments not included in `sig->numArgs`, for example, `this`, runtime lookup, cookie
// etc.
- unsigned nodeArgsCount = call->NumChildren();
+ unsigned nodeArgsCount = 0;
+ call->VisitOperands([&nodeArgsCount](GenTree* operand) -> GenTree::VisitResult {
+ nodeArgsCount++;
+ return GenTree::VisitResult::Continue;
+ });
if (call->gtCallThisArg != nullptr)
{
@@ -3722,14 +3705,14 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
if (argObj->gtOper == GT_LCL_VAR)
{
unsigned lclNum = argObj->AsLclVarCommon()->GetLclNum();
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ LclVarDsc* varDsc = lvaGetDesc(lclNum);
if (varDsc->lvPromoted)
{
if (varDsc->lvFieldCnt == 1)
{
// get the first and only promoted field
- LclVarDsc* fieldVarDsc = &lvaTable[varDsc->lvFieldLclStart];
+ LclVarDsc* fieldVarDsc = lvaGetDesc(varDsc->lvFieldLclStart);
if (genTypeSize(fieldVarDsc->TypeGet()) >= originalSize)
{
// we will use the first and only promoted field
@@ -3829,7 +3812,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
if (copyBlkClass != NO_CLASS_HANDLE)
{
- fgMakeOutgoingStructArgCopy(call, args, argIndex, copyBlkClass);
+ fgMakeOutgoingStructArgCopy(call, args, copyBlkClass);
}
if (argx->gtOper == GT_MKREFANY)
@@ -4127,7 +4110,7 @@ void Compiler::fgMorphMultiregStructArgs(GenTreeCall* call)
else
{
assert(argx->OperIs(GT_LCL_VAR));
- structSize = lvaGetDesc(argx->AsLclVar()->GetLclNum())->lvExactSize;
+ structSize = lvaGetDesc(argx->AsLclVar())->lvExactSize;
}
assert(structSize > 0);
if (structSize == genTypeSize(hfaType))
@@ -4275,8 +4258,7 @@ GenTree* Compiler::fgMorphMultiregStructArg(GenTree* arg, fgArgTabEntry* fgEntry
// Only update to the same type.
if (underlyingTree->OperIs(GT_LCL_VAR))
{
- const GenTreeLclVar* lclVar = underlyingTree->AsLclVar();
- const LclVarDsc* varDsc = lvaGetDesc(lclVar);
+ const LclVarDsc* varDsc = lvaGetDesc(underlyingTree->AsLclVar());
if (ClassLayout::AreCompatible(varDsc->GetLayout(), objLayout))
{
argValue = underlyingTree;
@@ -4286,12 +4268,8 @@ GenTree* Compiler::fgMorphMultiregStructArg(GenTree* arg, fgArgTabEntry* fgEntry
}
else if (arg->OperGet() == GT_LCL_VAR)
{
- GenTreeLclVarCommon* varNode = arg->AsLclVarCommon();
- unsigned varNum = varNode->GetLclNum();
- assert(varNum < lvaCount);
- LclVarDsc* varDsc = &lvaTable[varNum];
-
- structSize = varDsc->lvExactSize;
+ LclVarDsc* varDsc = lvaGetDesc(arg->AsLclVarCommon());
+ structSize = varDsc->lvExactSize;
assert(structSize == info.compCompHnd->getClassSize(objClass));
}
else
@@ -4398,8 +4376,7 @@ GenTree* Compiler::fgMorphMultiregStructArg(GenTree* arg, fgArgTabEntry* fgEntry
{
GenTreeLclVarCommon* varNode = argValue->AsLclVarCommon();
unsigned varNum = varNode->GetLclNum();
- assert(varNum < lvaCount);
- LclVarDsc* varDsc = &lvaTable[varNum];
+ LclVarDsc* varDsc = lvaGetDesc(varNum);
// At this point any TYP_STRUCT LclVar must be an aligned struct
// or an HFA struct, both which are passed by value.
@@ -4481,8 +4458,8 @@ GenTree* Compiler::fgMorphMultiregStructArg(GenTree* arg, fgArgTabEntry* fgEntry
// Did we find the promoted fields at the necessary offsets?
if ((loVarNum != BAD_VAR_NUM) && (hiVarNum != BAD_VAR_NUM))
{
- LclVarDsc* loVarDsc = &lvaTable[loVarNum];
- LclVarDsc* hiVarDsc = &lvaTable[hiVarNum];
+ LclVarDsc* loVarDsc = lvaGetDesc(loVarNum);
+ LclVarDsc* hiVarDsc = lvaGetDesc(hiVarNum);
var_types loType = loVarDsc->lvType;
var_types hiType = hiVarDsc->lvType;
@@ -4547,7 +4524,7 @@ GenTree* Compiler::fgMorphMultiregStructArg(GenTree* arg, fgArgTabEntry* fgEntry
for (unsigned inx = 0; inx < elemCount; inx++)
{
- varDscs[inx] = &lvaTable[varNums[inx]];
+ varDscs[inx] = lvaGetDesc(varNums[inx]);
varType[inx] = varDscs[inx]->lvType;
if (varTypeIsFloating(varType[inx]))
{
@@ -4598,8 +4575,7 @@ GenTree* Compiler::fgMorphMultiregStructArg(GenTree* arg, fgArgTabEntry* fgEntry
{
GenTreeLclVarCommon* varNode = argValue->AsLclVarCommon();
unsigned varNum = varNode->GetLclNum();
- assert(varNum < lvaCount);
- LclVarDsc* varDsc = &lvaTable[varNum];
+ LclVarDsc* varDsc = lvaGetDesc(varNum);
unsigned baseOffset = varNode->GetLclOffs();
unsigned lastOffset = baseOffset + structSize;
@@ -4764,16 +4740,11 @@ GenTreeFieldList* Compiler::fgMorphLclArgToFieldlist(GenTreeLclVarCommon* lcl)
// Arguments:
// call - call being processed
// args - args for the call
-/// argIndex - arg being processed
// copyBlkClass - class handle for the struct
//
-// Return value:
-// tree that computes address of the outgoing arg
+// The arg is updated if necessary with the copy.
//
-void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call,
- GenTreeCall::Use* args,
- unsigned argIndex,
- CORINFO_CLASS_HANDLE copyBlkClass)
+void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call, GenTreeCall::Use* args, CORINFO_CLASS_HANDLE copyBlkClass)
{
GenTree* argx = args->GetNode();
noway_assert(argx->gtOper != GT_MKREFANY);
@@ -4808,10 +4779,11 @@ void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call,
// * (may not copy) if there is exactly one use of the local in the method,
// and the call is not in loop, this is a last use.
//
- const bool isTailCallLastUse = call->IsTailCall();
- const bool isCallLastUse = (totalAppearances == 1) && !fgMightHaveLoop();
- const bool isNoReturnLastUse = (totalAppearances == 1) && call->IsNoReturn();
- if (isTailCallLastUse || isCallLastUse || isNoReturnLastUse)
+ // fgMightHaveLoop() is expensive; check it last, only if necessary.
+ //
+ if (call->IsTailCall() || //
+ ((totalAppearances == 1) && call->IsNoReturn()) || //
+ ((totalAppearances == 1) && !fgMightHaveLoop()))
{
args->SetNode(lcl);
assert(argEntry->GetNode() == lcl);
@@ -4839,7 +4811,7 @@ void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call,
indexType lclNum;
FOREACH_HBV_BIT_SET(lclNum, fgOutgoingArgTemps)
{
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ LclVarDsc* varDsc = lvaGetDesc((unsigned)lclNum);
if (typeInfo::AreEquivalent(varDsc->lvVerTypeInfo, typeInfo(TI_STRUCT, copyBlkClass)) &&
!fgCurrentlyInUseArgTemps->testBit(lclNum))
{
@@ -4915,8 +4887,6 @@ void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call,
args->SetNode(arg);
call->fgArgInfo->EvalToTmp(argEntry, tmp, arg);
-
- return;
}
#ifdef TARGET_ARM
@@ -4930,7 +4900,7 @@ void Compiler::fgAddSkippedRegsInPromotedStructArg(LclVarDsc* varDsc,
// integer register arguments are consecutive ints. They are on ARM.
// To start, figure out what register contains the last byte of the first argument.
- LclVarDsc* firstFldVarDsc = &lvaTable[varDsc->lvFieldLclStart];
+ LclVarDsc* firstFldVarDsc = lvaGetDesc(varDsc->lvFieldLclStart);
unsigned lastFldRegOfLastByte =
(firstFldVarDsc->lvFldOffset + firstFldVarDsc->lvExactSize - 1) / TARGET_POINTER_SIZE;
;
@@ -4941,7 +4911,7 @@ void Compiler::fgAddSkippedRegsInPromotedStructArg(LclVarDsc* varDsc,
for (unsigned fldVarOffset = 1; fldVarOffset < varDsc->lvFieldCnt; fldVarOffset++)
{
unsigned fldVarNum = varDsc->lvFieldLclStart + fldVarOffset;
- LclVarDsc* fldVarDsc = &lvaTable[fldVarNum];
+ LclVarDsc* fldVarDsc = lvaGetDesc(fldVarNum);
unsigned fldRegOffset = fldVarDsc->lvFldOffset / TARGET_POINTER_SIZE;
assert(fldRegOffset >= lastFldRegOfLastByte); // Assuming sorted fields.
// This loop should enumerate the offsets of any registers skipped.
@@ -5461,15 +5431,43 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree)
// the partial byref will not point within the object, and thus not get updated correctly during a GC.
// This is mostly a risk in fully-interruptible code regions.
- /* Add the first element's offset */
-
- GenTree* cns = gtNewIconNode(elemOffs, TYP_I_IMPL);
+ // We can generate two types of trees for "addr":
+ //
+ // 1) "arrRef + (index + elemOffset)"
+ // 2) "(arrRef + elemOffset) + index"
+ //
+ // XArch has powerful addressing modes such as [base + index*scale + offset] so it's fine with 1),
+ // while for Arm we better try to make an invariant sub-tree as large as possible, which is usually
+ // "(arrRef + elemOffset)" and is CSE/LoopHoisting friendly => produces better codegen.
+ // 2) should still be safe from GC's point of view since both ADD operations are byref and point to
+ // within the object so GC will be able to correctly track and update them.
- addr = gtNewOperNode(GT_ADD, TYP_I_IMPL, addr, cns);
+ bool groupArrayRefWithElemOffset = false;
+#ifdef TARGET_ARMARCH
+ groupArrayRefWithElemOffset = true;
+ // TODO: in some cases even on ARM we better use 1) shape because if "index" is invariant and "arrRef" is not
+ // we at least will be able to hoist/CSE "index + elemOffset" in some cases.
+ // See https://github.com/dotnet/runtime/pull/61293#issuecomment-964146497
- /* Add the object ref to the element's offset */
+ // Use 2) form only for primitive types for now - it significantly reduced number of size regressions
+ if (!varTypeIsIntegral(elemTyp))
+ {
+ groupArrayRefWithElemOffset = false;
+ }
+#endif
- addr = gtNewOperNode(GT_ADD, TYP_BYREF, arrRef, addr);
+ // First element's offset
+ GenTree* elemOffset = gtNewIconNode(elemOffs, TYP_I_IMPL);
+ if (groupArrayRefWithElemOffset)
+ {
+ GenTree* basePlusOffset = gtNewOperNode(GT_ADD, TYP_BYREF, arrRef, elemOffset);
+ addr = gtNewOperNode(GT_ADD, TYP_BYREF, basePlusOffset, addr);
+ }
+ else
+ {
+ addr = gtNewOperNode(GT_ADD, TYP_I_IMPL, addr, elemOffset);
+ addr = gtNewOperNode(GT_ADD, TYP_BYREF, arrRef, addr);
+ }
assert(((tree->gtDebugFlags & GTF_DEBUG_NODE_LARGE) != 0) ||
(GenTree::s_gtNodeSizes[GT_IND] == TREE_NODE_SZ_SMALL));
@@ -5539,16 +5537,16 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree)
tree = gtNewOperNode(GT_COMMA, tree->TypeGet(), arrRefDefn, tree);
}
- JITDUMP("fgMorphArrayIndex (before remorph):\n");
- DISPTREE(tree);
+ JITDUMP("fgMorphArrayIndex (before remorph):\n")
+ DISPTREE(tree)
// Currently we morph the tree to perform some folding operations prior
// to attaching fieldSeq info and labeling constant array index contributions
//
tree = fgMorphTree(tree);
- JITDUMP("fgMorphArrayIndex (after remorph):\n");
- DISPTREE(tree);
+ JITDUMP("fgMorphArrayIndex (after remorph):\n")
+ DISPTREE(tree)
// Ideally we just want to proceed to attaching fieldSeq info and labeling the
// constant array index contributions, but the morphing operation may have changed
@@ -5562,48 +5560,66 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree)
if (fgIsCommaThrow(tree))
{
- if ((arrElem != indTree) || // A new tree node may have been created
- (indTree->OperGet() != GT_IND)) // The GT_IND may have been changed to a GT_CNS_INT
+ if ((arrElem != indTree) || // A new tree node may have been created
+ (!indTree->OperIs(GT_IND))) // The GT_IND may have been changed to a GT_CNS_INT
{
return tree; // Just return the Comma-Throw, don't try to attach the fieldSeq info, etc..
}
}
assert(!fgGlobalMorph || (arrElem->gtDebugFlags & GTF_DEBUG_NODE_MORPHED));
- DBEXEC(fgGlobalMorph && (arrElem == tree), tree->gtDebugFlags &= ~GTF_DEBUG_NODE_MORPHED);
+ DBEXEC(fgGlobalMorph && (arrElem == tree), tree->gtDebugFlags &= ~GTF_DEBUG_NODE_MORPHED)
- addr = arrElem->AsOp()->gtOp1;
-
- assert(addr->TypeGet() == TYP_BYREF);
+ addr = arrElem->gtGetOp1();
GenTree* cnsOff = nullptr;
- if (addr->OperGet() == GT_ADD)
+ if (addr->OperIs(GT_ADD))
{
- assert(addr->TypeGet() == TYP_BYREF);
- assert(addr->AsOp()->gtOp1->TypeGet() == TYP_REF);
-
- addr = addr->AsOp()->gtOp2;
-
- // Look for the constant [#FirstElem] node here, or as the RHS of an ADD.
-
- if (addr->gtOper == GT_CNS_INT)
+ GenTree* addrOp1 = addr->gtGetOp1();
+ if (groupArrayRefWithElemOffset)
{
- cnsOff = addr;
- addr = nullptr;
+ if (addrOp1->OperIs(GT_ADD) && addrOp1->gtGetOp2()->IsCnsIntOrI())
+ {
+ assert(addrOp1->gtGetOp1()->TypeIs(TYP_REF));
+ cnsOff = addrOp1->gtGetOp2();
+ addr = addr->gtGetOp2();
+ // Label any constant array index contributions with #ConstantIndex and any LclVars with
+ // GTF_VAR_ARR_INDEX
+ addr->LabelIndex(this);
+ }
+ else
+ {
+ assert(addr->gtGetOp2()->IsCnsIntOrI());
+ cnsOff = addr->gtGetOp2();
+ addr = nullptr;
+ }
}
else
{
- if ((addr->OperGet() == GT_ADD) && (addr->AsOp()->gtOp2->gtOper == GT_CNS_INT))
+ assert(addr->TypeIs(TYP_BYREF));
+ assert(addr->gtGetOp1()->TypeIs(TYP_REF));
+ addr = addr->gtGetOp2();
+
+ // Look for the constant [#FirstElem] node here, or as the RHS of an ADD.
+ if (addr->IsCnsIntOrI())
{
- cnsOff = addr->AsOp()->gtOp2;
- addr = addr->AsOp()->gtOp1;
+ cnsOff = addr;
+ addr = nullptr;
+ }
+ else
+ {
+ if ((addr->OperIs(GT_ADD)) && addr->gtGetOp2()->IsCnsIntOrI())
+ {
+ cnsOff = addr->gtGetOp2();
+ addr = addr->gtGetOp1();
+ }
+ // Label any constant array index contributions with #ConstantIndex and any LclVars with
+ // GTF_VAR_ARR_INDEX
+ addr->LabelIndex(this);
}
-
- // Label any constant array index contributions with #ConstantIndex and any LclVars with GTF_VAR_ARR_INDEX
- addr->LabelIndex(this);
}
}
- else if (addr->OperGet() == GT_CNS_INT)
+ else if (addr->IsCnsIntOrI())
{
cnsOff = addr;
}
@@ -5653,7 +5669,7 @@ GenTree* Compiler::fgMorphStackArgForVarArgs(unsigned lclNum, var_types varType,
through the varargs cookies to access them, except for the
cookie itself */
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ LclVarDsc* varDsc = lvaGetDesc(lclNum);
if (varDsc->lvIsParam && !varDsc->lvIsRegArg && lclNum != lvaVarargsHandleArg)
{
@@ -5700,7 +5716,7 @@ GenTree* Compiler::fgMorphLocalVar(GenTree* tree, bool forceRemorph)
unsigned lclNum = tree->AsLclVarCommon()->GetLclNum();
var_types varType = lvaGetRealType(lclNum);
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ LclVarDsc* varDsc = lvaGetDesc(lclNum);
if (varDsc->IsAddressExposed())
{
@@ -5845,7 +5861,7 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac)
GenTree* newTree = fgMorphFieldToSimdGetElement(tree);
if (newTree != tree)
{
- newTree = fgMorphSmpOp(newTree);
+ newTree = fgMorphTree(newTree);
return newTree;
}
}
@@ -6364,8 +6380,9 @@ void Compiler::fgMorphCallInline(GenTreeCall* call, InlineResult* inlineResult)
// Is this call an inline candidate?
if (call->IsInlineCandidate())
{
+ InlineContext* createdContext = nullptr;
// Attempt the inline
- fgMorphCallInlineHelper(call, inlineResult);
+ fgMorphCallInlineHelper(call, inlineResult, &createdContext);
// We should have made up our minds one way or another....
assert(inlineResult->IsDecided());
@@ -6374,13 +6391,21 @@ void Compiler::fgMorphCallInline(GenTreeCall* call, InlineResult* inlineResult)
if (inlineResult->IsFailure())
{
+ if (createdContext != nullptr)
+ {
+ // We created a context before we got to the failure, so mark
+ // it as failed in the tree.
+ createdContext->SetFailed(inlineResult);
+ }
+ else
+ {
#ifdef DEBUG
-
- // Before we do any cleanup, create a failing InlineContext to
- // capture details of the inlining attempt.
- m_inlineStrategy->NewFailure(fgMorphStmt, inlineResult);
-
+ // In debug we always put all inline attempts into the inline tree.
+ InlineContext* ctx =
+ m_inlineStrategy->NewContext(call->gtInlineCandidateInfo->inlinersContext, fgMorphStmt, call);
+ ctx->SetFailed(inlineResult);
#endif
+ }
inliningFailed = true;
@@ -6416,14 +6441,28 @@ void Compiler::fgMorphCallInline(GenTreeCall* call, InlineResult* inlineResult)
}
}
-/*****************************************************************************
- * Helper to attempt to inline a call
- * Sets success/failure in inline result
- * If success, modifies current method's IR with inlinee's IR
- * If failed, undoes any speculative modifications to current method
- */
-
-void Compiler::fgMorphCallInlineHelper(GenTreeCall* call, InlineResult* result)
+//------------------------------------------------------------------------------
+// fgMorphCallInlineHelper: Helper to attempt to inline a call
+//
+// Arguments:
+// call - call expression to inline, inline candidate
+// result - result to set to success or failure
+// createdContext - The context that was created if the inline attempt got to the inliner.
+//
+// Notes:
+// Attempts to inline the call.
+//
+// If successful, callee's IR is inserted in place of the call, and
+// is marked with an InlineContext.
+//
+// If unsuccessful, the transformations done in anticipation of a
+// possible inline are undone, and the candidate flag on the call
+// is cleared.
+//
+// If a context was created because we got to the importer then it is output by this function.
+// If the inline succeeded, this context will already be marked as successful. If it failed and
+// a context is returned, then it will not have been marked as success or failed.
+void Compiler::fgMorphCallInlineHelper(GenTreeCall* call, InlineResult* result, InlineContext** createdContext)
{
// Don't expect any surprises here.
assert(result->IsCandidate());
@@ -6483,7 +6522,7 @@ void Compiler::fgMorphCallInlineHelper(GenTreeCall* call, InlineResult* result)
// Invoke the compiler to inline the call.
//
- fgInvokeInlineeCompiler(call, result);
+ fgInvokeInlineeCompiler(call, result, createdContext);
if (result->IsFailure())
{
@@ -7196,7 +7235,8 @@ GenTree* Compiler::fgMorphPotentialTailCall(GenTreeCall* call)
bool hasStructParam = false;
for (unsigned varNum = 0; varNum < lvaCount; varNum++)
{
- LclVarDsc* varDsc = lvaTable + varNum;
+ LclVarDsc* varDsc = lvaGetDesc(varNum);
+
// If the method is marked as an explicit tail call we will skip the
// following three hazard checks.
// We still must check for any struct parameters and set 'hasStructParam'
@@ -8066,7 +8106,7 @@ GenTree* Compiler::fgCreateCallDispatcherAndGetResult(GenTreeCall* orig
CORINFO_METHOD_HANDLE dispatcherHnd)
{
GenTreeCall* callDispatcherNode =
- gtNewCallNode(CT_USER_FUNC, dispatcherHnd, TYP_VOID, nullptr, fgMorphStmt->GetILOffsetX());
+ gtNewCallNode(CT_USER_FUNC, dispatcherHnd, TYP_VOID, nullptr, fgMorphStmt->GetDebugInfo());
// The dispatcher has signature
// void DispatchTailCalls(void* callersRetAddrSlot, void* callTarget, void* retValue)
@@ -8085,37 +8125,29 @@ GenTree* Compiler::fgCreateCallDispatcherAndGetResult(GenTreeCall* orig
assert(retBufArg->OperIsLocal());
assert(retBufArg->AsLclVarCommon()->GetLclNum() == info.compRetBuffArg);
- if (info.compRetBuffDefStack)
- {
- // Use existing retbuf.
- retValArg = retBufArg;
- }
- else
- {
- // Caller return buffer argument retBufArg can point to GC heap while the dispatcher expects
- // the return value argument retValArg to point to the stack.
- // We use a temporary stack allocated return buffer to hold the value during the dispatcher call
- // and copy the value back to the caller return buffer after that.
- unsigned int tmpRetBufNum = lvaGrabTemp(true DEBUGARG("substitute local for return buffer"));
+ // Caller return buffer argument retBufArg can point to GC heap while the dispatcher expects
+ // the return value argument retValArg to point to the stack.
+ // We use a temporary stack allocated return buffer to hold the value during the dispatcher call
+ // and copy the value back to the caller return buffer after that.
+ unsigned int tmpRetBufNum = lvaGrabTemp(true DEBUGARG("substitute local for return buffer"));
- constexpr bool unsafeValueClsCheck = false;
- lvaSetStruct(tmpRetBufNum, origCall->gtRetClsHnd, unsafeValueClsCheck);
- lvaSetVarAddrExposed(tmpRetBufNum DEBUGARG(AddressExposedReason::DISPATCH_RET_BUF));
+ constexpr bool unsafeValueClsCheck = false;
+ lvaSetStruct(tmpRetBufNum, origCall->gtRetClsHnd, unsafeValueClsCheck);
+ lvaSetVarAddrExposed(tmpRetBufNum DEBUGARG(AddressExposedReason::DISPATCH_RET_BUF));
- var_types tmpRetBufType = lvaGetDesc(tmpRetBufNum)->TypeGet();
+ var_types tmpRetBufType = lvaGetDesc(tmpRetBufNum)->TypeGet();
- retValArg = gtNewOperNode(GT_ADDR, TYP_I_IMPL, gtNewLclvNode(tmpRetBufNum, tmpRetBufType));
+ retValArg = gtNewOperNode(GT_ADDR, TYP_I_IMPL, gtNewLclvNode(tmpRetBufNum, tmpRetBufType));
- var_types callerRetBufType = lvaGetDesc(info.compRetBuffArg)->TypeGet();
+ var_types callerRetBufType = lvaGetDesc(info.compRetBuffArg)->TypeGet();
- GenTree* dstAddr = gtNewLclvNode(info.compRetBuffArg, callerRetBufType);
- GenTree* dst = gtNewObjNode(info.compMethodInfo->args.retTypeClass, dstAddr);
- GenTree* src = gtNewLclvNode(tmpRetBufNum, tmpRetBufType);
+ GenTree* dstAddr = gtNewLclvNode(info.compRetBuffArg, callerRetBufType);
+ GenTree* dst = gtNewObjNode(info.compMethodInfo->args.retTypeClass, dstAddr);
+ GenTree* src = gtNewLclvNode(tmpRetBufNum, tmpRetBufType);
- constexpr bool isVolatile = false;
- constexpr bool isCopyBlock = true;
- copyToRetBufNode = gtNewBlkOpNode(dst, src, isVolatile, isCopyBlock);
- }
+ constexpr bool isVolatile = false;
+ constexpr bool isCopyBlock = true;
+ copyToRetBufNode = gtNewBlkOpNode(dst, src, isVolatile, isCopyBlock);
if (origCall->gtType != TYP_VOID)
{
@@ -8679,14 +8711,14 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa
// Transform recursive tail call into a loop.
- Statement* earlyArgInsertionPoint = lastStmt;
- IL_OFFSETX callILOffset = lastStmt->GetILOffsetX();
+ Statement* earlyArgInsertionPoint = lastStmt;
+ const DebugInfo& callDI = lastStmt->GetDebugInfo();
// Hoist arg setup statement for the 'this' argument.
GenTreeCall::Use* thisArg = recursiveTailCall->gtCallThisArg;
if ((thisArg != nullptr) && !thisArg->GetNode()->IsNothingNode() && !thisArg->GetNode()->IsArgPlaceHolderNode())
{
- Statement* thisArgStmt = gtNewStmt(thisArg->GetNode(), callILOffset);
+ Statement* thisArgStmt = gtNewStmt(thisArg->GetNode(), callDI);
fgInsertStmtBefore(block, earlyArgInsertionPoint, thisArgStmt);
}
@@ -8744,7 +8776,7 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa
if ((earlyArg->gtFlags & GTF_LATE_ARG) != 0)
{
// This is a setup node so we need to hoist it.
- Statement* earlyArgStmt = gtNewStmt(earlyArg, callILOffset);
+ Statement* earlyArgStmt = gtNewStmt(earlyArg, callDI);
fgInsertStmtBefore(block, earlyArgInsertionPoint, earlyArgStmt);
}
else
@@ -8758,7 +8790,7 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa
fgAssignRecursiveCallArgToCallerParam(earlyArg, curArgTabEntry,
fgGetArgTabEntryParameterLclNum(recursiveTailCall,
curArgTabEntry),
- block, callILOffset, tmpAssignmentInsertionPoint,
+ block, callDI, tmpAssignmentInsertionPoint,
paramAssignmentInsertionPoint);
if ((tmpAssignmentInsertionPoint == lastStmt) && (paramAssignStmt != nullptr))
{
@@ -8785,7 +8817,7 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa
fgAssignRecursiveCallArgToCallerParam(lateArg, curArgTabEntry,
fgGetArgTabEntryParameterLclNum(recursiveTailCall,
curArgTabEntry),
- block, callILOffset, tmpAssignmentInsertionPoint,
+ block, callDI, tmpAssignmentInsertionPoint,
paramAssignmentInsertionPoint);
if ((tmpAssignmentInsertionPoint == lastStmt) && (paramAssignStmt != nullptr))
@@ -8805,7 +8837,7 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa
var_types thisType = lvaTable[info.compThisArg].TypeGet();
GenTree* arg0 = gtNewLclvNode(lvaArg0Var, thisType);
GenTree* arg0Assignment = gtNewAssignNode(arg0, gtNewLclvNode(info.compThisArg, thisType));
- Statement* arg0AssignmentStmt = gtNewStmt(arg0Assignment, callILOffset);
+ Statement* arg0AssignmentStmt = gtNewStmt(arg0Assignment, callDI);
fgInsertStmtBefore(block, paramAssignmentInsertionPoint, arg0AssignmentStmt);
}
@@ -8847,7 +8879,7 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa
GenTree* zero = gtNewZeroConNode(genActualType(lclType));
init = gtNewAssignNode(lcl, zero);
}
- Statement* initStmt = gtNewStmt(init, callILOffset);
+ Statement* initStmt = gtNewStmt(init, callDI);
fgInsertStmtBefore(block, lastStmt, initStmt);
}
}
@@ -8897,13 +8929,13 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa
// Return Value:
// parameter assignment statement if one was inserted; nullptr otherwise.
-Statement* Compiler::fgAssignRecursiveCallArgToCallerParam(GenTree* arg,
- fgArgTabEntry* argTabEntry,
- unsigned lclParamNum,
- BasicBlock* block,
- IL_OFFSETX callILOffset,
- Statement* tmpAssignmentInsertionPoint,
- Statement* paramAssignmentInsertionPoint)
+Statement* Compiler::fgAssignRecursiveCallArgToCallerParam(GenTree* arg,
+ fgArgTabEntry* argTabEntry,
+ unsigned lclParamNum,
+ BasicBlock* block,
+ const DebugInfo& callDI,
+ Statement* tmpAssignmentInsertionPoint,
+ Statement* paramAssignmentInsertionPoint)
{
// Call arguments should be assigned to temps first and then the temps should be assigned to parameters because
// some argument trees may reference parameters directly.
@@ -8922,7 +8954,7 @@ Statement* Compiler::fgAssignRecursiveCallArgToCallerParam(GenTree* arg,
else if (arg->OperGet() == GT_LCL_VAR)
{
unsigned lclNum = arg->AsLclVar()->GetLclNum();
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ LclVarDsc* varDsc = lvaGetDesc(lclNum);
if (!varDsc->lvIsParam)
{
// The argument is a non-parameter local so it doesn't need to be assigned to a temp.
@@ -8953,17 +8985,17 @@ Statement* Compiler::fgAssignRecursiveCallArgToCallerParam(GenTree* arg,
GenTree* tempSrc = arg;
GenTree* tempDest = gtNewLclvNode(tmpNum, tempSrc->gtType);
GenTree* tmpAssignNode = gtNewAssignNode(tempDest, tempSrc);
- Statement* tmpAssignStmt = gtNewStmt(tmpAssignNode, callILOffset);
+ Statement* tmpAssignStmt = gtNewStmt(tmpAssignNode, callDI);
fgInsertStmtBefore(block, tmpAssignmentInsertionPoint, tmpAssignStmt);
argInTemp = gtNewLclvNode(tmpNum, tempSrc->gtType);
}
// Now assign the temp to the parameter.
- LclVarDsc* paramDsc = lvaTable + lclParamNum;
+ const LclVarDsc* paramDsc = lvaGetDesc(lclParamNum);
assert(paramDsc->lvIsParam);
GenTree* paramDest = gtNewLclvNode(lclParamNum, paramDsc->lvType);
GenTree* paramAssignNode = gtNewAssignNode(paramDest, argInTemp);
- paramAssignStmt = gtNewStmt(paramAssignNode, callILOffset);
+ paramAssignStmt = gtNewStmt(paramAssignNode, callDI);
fgInsertStmtBefore(block, paramAssignmentInsertionPoint, paramAssignStmt);
}
@@ -9015,7 +9047,7 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call)
assg = fgMorphTree(assg);
// Create the assignment statement and insert it before the current statement.
- Statement* assgStmt = gtNewStmt(assg, compCurStmt->GetILOffsetX());
+ Statement* assgStmt = gtNewStmt(assg, compCurStmt->GetDebugInfo());
fgInsertStmtBefore(compCurBB, compCurStmt, assgStmt);
// Return the temp.
@@ -9116,48 +9148,6 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call)
compCurBB->bbFlags |= BBF_HAS_CALL; // This block has a call
- // Make sure that return buffers containing GC pointers that aren't too large are pointers into the stack.
- GenTree* origDest = nullptr; // Will only become non-null if we do the transformation (and thus require
- // copy-back).
- unsigned retValTmpNum = BAD_VAR_NUM;
- CORINFO_CLASS_HANDLE structHnd = nullptr;
- if (call->HasRetBufArg() &&
- call->gtCallLateArgs == nullptr) // Don't do this if we're re-morphing (which will make late args non-null).
- {
- // We're enforcing the invariant that return buffers pointers (at least for
- // struct return types containing GC pointers) are never pointers into the heap.
- // The large majority of cases are address of local variables, which are OK.
- // Otherwise, allocate a local of the given struct type, pass its address,
- // then assign from that into the proper destination. (We don't need to do this
- // if we're passing the caller's ret buff arg to the callee, since the caller's caller
- // will maintain the same invariant.)
-
- GenTree* dest = call->gtCallArgs->GetNode();
- assert(dest->OperGet() != GT_ARGPLACE); // If it was, we'd be in a remorph, which we've already excluded above.
- if (dest->TypeIs(TYP_BYREF) && !dest->IsLocalAddrExpr())
- {
- // We'll exempt helper calls from this, assuming that the helper implementation
- // follows the old convention, and does whatever barrier is required.
- if (call->gtCallType != CT_HELPER)
- {
- structHnd = call->gtRetClsHnd;
- if (info.compCompHnd->isStructRequiringStackAllocRetBuf(structHnd) &&
- !(dest->OperGet() == GT_LCL_VAR && dest->AsLclVar()->GetLclNum() == info.compRetBuffArg))
- {
- // Force re-evaluating the argInfo as the return argument has changed.
- call->fgArgInfo = nullptr;
- origDest = dest;
-
- retValTmpNum = lvaGrabTemp(true DEBUGARG("substitute local for ret buff arg"));
- lvaSetStruct(retValTmpNum, structHnd, true);
- dest = gtNewOperNode(GT_ADDR, TYP_BYREF, gtNewLclvNode(retValTmpNum, TYP_STRUCT));
- }
- }
- }
-
- call->gtCallArgs->SetNode(dest);
- }
-
/* Process the "normal" argument list */
call = fgMorphArgs(call);
noway_assert(call->gtOper == GT_CALL);
@@ -9176,7 +9166,7 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call)
//
call->gtControlExpr = fgMorphTree(call->gtControlExpr);
- // Propogate any gtFlags into the call
+ // Propagate any gtFlags into the call
call->gtFlags |= call->gtControlExpr->gtFlags;
}
@@ -9236,10 +9226,8 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call)
fgWalkTreePost(&value, resetMorphedFlag);
#endif // DEBUG
- GenTree* const nullCheckedArr = impCheckForNullPointer(arr);
- GenTree* const arrIndexNode = gtNewIndexRef(TYP_REF, nullCheckedArr, index);
- GenTree* const arrStore = gtNewAssignNode(arrIndexNode, value);
- arrStore->gtFlags |= GTF_ASG;
+ GenTree* const arrIndexNode = gtNewIndexRef(TYP_REF, arr, index);
+ GenTree* const arrStore = gtNewAssignNode(arrIndexNode, value);
GenTree* result = fgMorphTree(arrStore);
if (argSetup != nullptr)
@@ -9254,31 +9242,6 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call)
}
}
- if (origDest != nullptr)
- {
- GenTree* retValVarAddr = gtNewOperNode(GT_ADDR, TYP_BYREF, gtNewLclvNode(retValTmpNum, TYP_STRUCT));
- // If the origDest expression was an assignment to a variable, it might be to an otherwise-unused
- // var, which would allow the whole assignment to be optimized away to a NOP. So in that case, make the
- // origDest into a comma that uses the var. Note that the var doesn't have to be a temp for this to
- // be correct.
- if (origDest->OperGet() == GT_ASG)
- {
- if (origDest->AsOp()->gtOp1->OperGet() == GT_LCL_VAR)
- {
- GenTree* var = origDest->AsOp()->gtOp1;
- origDest = gtNewOperNode(GT_COMMA, var->TypeGet(), origDest,
- gtNewLclvNode(var->AsLclVar()->GetLclNum(), var->TypeGet()));
- }
- }
- GenTree* copyBlk = gtNewCpObjNode(origDest, retValVarAddr, structHnd, false);
- copyBlk = fgMorphTree(copyBlk);
- GenTree* result = gtNewOperNode(GT_COMMA, TYP_VOID, call, copyBlk);
-#ifdef DEBUG
- result->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED;
-#endif
- return result;
- }
-
if (call->IsNoReturn())
{
//
@@ -9374,14 +9337,15 @@ GenTree* Compiler::fgExpandVirtualVtableCallTarget(GenTreeCall* call)
// [tmp + vtabOffsOfIndirection]
GenTree* tmpTree1 = gtNewOperNode(GT_ADD, TYP_I_IMPL, gtNewLclvNode(varNum1, TYP_I_IMPL),
- gtNewIconNode(vtabOffsOfIndirection, TYP_INT));
+ gtNewIconNode(vtabOffsOfIndirection, TYP_I_IMPL));
tmpTree1 = gtNewOperNode(GT_IND, TYP_I_IMPL, tmpTree1, false);
tmpTree1->gtFlags |= GTF_IND_NONFAULTING;
tmpTree1->gtFlags |= GTF_IND_INVARIANT;
// var1 + vtabOffsOfIndirection + vtabOffsAfterIndirection
- GenTree* tmpTree2 = gtNewOperNode(GT_ADD, TYP_I_IMPL, gtNewLclvNode(varNum1, TYP_I_IMPL),
- gtNewIconNode(vtabOffsOfIndirection + vtabOffsAfterIndirection, TYP_INT));
+ GenTree* tmpTree2 =
+ gtNewOperNode(GT_ADD, TYP_I_IMPL, gtNewLclvNode(varNum1, TYP_I_IMPL),
+ gtNewIconNode(vtabOffsOfIndirection + vtabOffsAfterIndirection, TYP_I_IMPL));
// var1 + vtabOffsOfIndirection + vtabOffsAfterIndirection + [var1 + vtabOffsOfIndirection]
tmpTree2 = gtNewOperNode(GT_ADD, TYP_I_IMPL, tmpTree2, tmpTree1);
@@ -9400,7 +9364,7 @@ GenTree* Compiler::fgExpandVirtualVtableCallTarget(GenTreeCall* call)
else
{
// result = [vtab + vtabOffsOfIndirection]
- result = gtNewOperNode(GT_ADD, TYP_I_IMPL, vtab, gtNewIconNode(vtabOffsOfIndirection, TYP_INT));
+ result = gtNewOperNode(GT_ADD, TYP_I_IMPL, vtab, gtNewIconNode(vtabOffsOfIndirection, TYP_I_IMPL));
result = gtNewOperNode(GT_IND, TYP_I_IMPL, result, false);
result->gtFlags |= GTF_IND_NONFAULTING;
result->gtFlags |= GTF_IND_INVARIANT;
@@ -9416,7 +9380,7 @@ GenTree* Compiler::fgExpandVirtualVtableCallTarget(GenTreeCall* call)
{
// Load the function address
// result = [result + vtabOffsAfterIndirection]
- result = gtNewOperNode(GT_ADD, TYP_I_IMPL, result, gtNewIconNode(vtabOffsAfterIndirection, TYP_INT));
+ result = gtNewOperNode(GT_ADD, TYP_I_IMPL, result, gtNewIconNode(vtabOffsAfterIndirection, TYP_I_IMPL));
// This last indirection is not invariant, but is non-faulting
result = gtNewOperNode(GT_IND, TYP_I_IMPL, result, false);
result->gtFlags |= GTF_IND_NONFAULTING;
@@ -9705,7 +9669,7 @@ GenTree* Compiler::fgMorphOneAsgBlockOp(GenTree* tree)
if (impIsAddressInLocal(lhsBlk->Addr(), &destLclVarTree))
{
destVarNum = destLclVarTree->AsLclVarCommon()->GetLclNum();
- destVarDsc = &(lvaTable[destVarNum]);
+ destVarDsc = lvaGetDesc(destVarNum);
}
if (lhsBlk->OperGet() == GT_OBJ)
{
@@ -9741,7 +9705,7 @@ GenTree* Compiler::fgMorphOneAsgBlockOp(GenTree* tree)
if (destLclVarTree != nullptr)
{
destVarNum = destLclVarTree->AsLclVarCommon()->GetLclNum();
- destVarDsc = &(lvaTable[destVarNum]);
+ destVarDsc = lvaGetDesc(destVarNum);
if (asgType == TYP_STRUCT)
{
clsHnd = destVarDsc->GetStructHnd();
@@ -9833,11 +9797,11 @@ GenTree* Compiler::fgMorphOneAsgBlockOp(GenTree* tree)
if (src->OperGet() == GT_LCL_VAR)
{
srcLclVarTree = src;
- srcVarDsc = &(lvaTable[src->AsLclVarCommon()->GetLclNum()]);
+ srcVarDsc = lvaGetDesc(src->AsLclVarCommon());
}
else if (src->OperIsIndir() && impIsAddressInLocal(src->AsOp()->gtOp1, &srcLclVarTree))
{
- srcVarDsc = &(lvaTable[srcLclVarTree->AsLclVarCommon()->GetLclNum()]);
+ srcVarDsc = lvaGetDesc(srcLclVarTree->AsLclVarCommon());
}
if ((srcVarDsc != nullptr) && varTypeIsStruct(srcLclVarTree) && srcVarDsc->lvPromoted)
{
@@ -10014,15 +9978,14 @@ GenTree* Compiler::fgMorphOneAsgBlockOp(GenTree* tree)
}
else // InitBlk
{
-#if FEATURE_SIMD
+#ifdef FEATURE_SIMD
if (varTypeIsSIMD(asgType))
{
assert(!isCopyBlock); // Else we would have returned the tree above.
noway_assert(src->IsIntegralConst(0));
noway_assert(destVarDsc != nullptr);
- src = new (this, GT_SIMD)
- GenTreeSIMD(asgType, src, SIMDIntrinsicInit, destVarDsc->GetSimdBaseJitType(), size);
+ src = gtNewSIMDNode(asgType, src, SIMDIntrinsicInit, destVarDsc->GetSimdBaseJitType(), size);
}
else
#endif
@@ -10423,7 +10386,7 @@ GenTree* Compiler::fgMorphBlockOperand(GenTree* tree, var_types asgType, unsigne
if (lclNode != nullptr)
{
- LclVarDsc* varDsc = &(lvaTable[lclNode->GetLclNum()]);
+ const LclVarDsc* varDsc = lvaGetDesc(lclNode);
if (varTypeIsStruct(varDsc) && (varDsc->lvExactSize == blockWidth) && (varDsc->lvType == asgType))
{
if (effectiveVal != lclNode)
@@ -10496,7 +10459,7 @@ GenTree* Compiler::fgMorphBlockOperand(GenTree* tree, var_types asgType, unsigne
// false otherwise.
//
// Notes:
-// This check is needed to avoid accesing LCL_VARs with incorrect
+// This check is needed to avoid accessing LCL_VARs with incorrect
// CORINFO_FIELD_HANDLE that would confuse VN optimizations.
//
bool Compiler::fgMorphCanUseLclFldForCopy(unsigned lclNum1, unsigned lclNum2)
@@ -10633,8 +10596,7 @@ GenTree* Compiler::getSIMDStructFromField(GenTree* tree,
if (isSIMDTypeLocal(obj))
{
- unsigned lclNum = obj->AsLclVarCommon()->GetLclNum();
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ LclVarDsc* varDsc = lvaGetDesc(obj->AsLclVarCommon());
if (varDsc->lvIsUsedInSIMDIntrinsic() || ignoreUsedInSIMDIntrinsic)
{
*simdSizeOut = varDsc->lvExactSize;
@@ -10713,9 +10675,6 @@ GenTree* Compiler::fgMorphFieldToSimdGetElement(GenTree* tree)
tree = gtNewSimdGetElementNode(simdBaseType, simdStructNode, op2, simdBaseJitType, simdSize,
/* isSimdAsHWIntrinsic */ true);
-#ifdef DEBUG
- tree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED;
-#endif
}
return tree;
}
@@ -10899,7 +10858,8 @@ GenTree* Compiler::fgMorphCastedBitwiseOp(GenTreeOp* tree)
var_types toType = op1->AsCast()->CastToType();
bool isUnsigned = op1->IsUnsigned();
- if ((op2->CastFromType() != fromType) || (op2->CastToType() != toType) || (op2->IsUnsigned() != isUnsigned))
+ if (varTypeIsFloating(fromType) || (op2->CastFromType() != fromType) || (op2->CastToType() != toType) ||
+ (op2->IsUnsigned() != isUnsigned))
{
return nullptr;
}
@@ -11034,7 +10994,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
if (op1->OperKind() & GTK_RELOP)
{
- noway_assert((oper == GT_JTRUE) || (op1->gtFlags & GTF_RELOP_QMARK));
/* Mark the comparison node with GTF_RELOP_JMP_USED so it knows that it does
not need to materialize the result as a 0 or 1. */
@@ -11531,9 +11490,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
}
break;
#endif
- case GT_LIST:
- // Special handling for the arg list.
- return fgMorphArgList(tree->AsArgList(), mac);
case GT_PUTARG_TYPE:
return fgMorphTree(tree->AsUnOp()->gtGetOp1());
@@ -12007,12 +11963,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
effectiveOp1 = op1->gtEffectiveVal();
- if (effectiveOp1->OperIsConst())
- {
- op1 = gtNewOperNode(GT_IND, tree->TypeGet(), op1);
- tree->AsOp()->gtOp1 = op1;
- }
-
// If we are storing a small type, we might be able to omit a cast.
if (effectiveOp1->OperIs(GT_IND) && varTypeIsSmall(effectiveOp1))
{
@@ -12332,27 +12282,9 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
if (op2->IsCnsIntOrI() && varTypeIsIntegralOrI(typ))
{
- CLANG_FORMAT_COMMENT_ANCHOR;
-
// Fold (x + 0).
-
if ((op2->AsIntConCommon()->IconValue() == 0) && !gtIsActiveCSE_Candidate(tree))
{
-
- // If this addition is adding an offset to a null pointer,
- // avoid the work and yield the null pointer immediately.
- // Dereferencing the pointer in either case will have the
- // same effect.
-
- if (!optValnumCSE_phase && varTypeIsGC(op2->TypeGet()) &&
- ((op1->gtFlags & GTF_ALL_EFFECT) == 0))
- {
- op2->gtType = tree->gtType;
- DEBUG_DESTROY_NODE(op1);
- DEBUG_DESTROY_NODE(tree);
- return op2;
- }
-
// Remove the addition iff it won't change the tree type
// to TYP_REF.
@@ -12706,7 +12638,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
else if (temp->OperIsLocal())
{
unsigned lclNum = temp->AsLclVarCommon()->GetLclNum();
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ LclVarDsc* varDsc = lvaGetDesc(lclNum);
// We will try to optimize when we have a promoted struct promoted with a zero lvFldOffset
if (varDsc->lvPromoted && (varDsc->lvFldOffset == 0))
@@ -12718,7 +12650,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
{
unsigned lclNumFld = varDsc->lvFieldLclStart;
// just grab the promoted field
- LclVarDsc* fieldVarDsc = &lvaTable[lclNumFld];
+ LclVarDsc* fieldVarDsc = lvaGetDesc(lclNumFld);
// Also make sure that the tree type matches the fieldVarType and that it's lvFldOffset
// is zero
@@ -12879,7 +12811,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
assert(temp->OperIsLocal());
const unsigned lclNum = temp->AsLclVarCommon()->GetLclNum();
- LclVarDsc* const varDsc = &lvaTable[lclNum];
+ LclVarDsc* const varDsc = lvaGetDesc(lclNum);
const var_types tempTyp = temp->TypeGet();
const bool useExactSize = varTypeIsStruct(tempTyp) || (tempTyp == TYP_BLK) || (tempTyp == TYP_LCLBLK);
@@ -13090,15 +13022,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
return addr;
}
- else if (op1->gtOper == GT_CAST)
- {
- GenTree* casting = op1->AsCast()->CastOp();
- if (casting->gtOper == GT_LCL_VAR || casting->gtOper == GT_CLS_VAR)
- {
- DEBUG_DESTROY_NODE(op1);
- tree->AsOp()->gtOp1 = op1 = casting;
- }
- }
else if ((op1->gtOper == GT_COMMA) && !optValnumCSE_phase)
{
// Perform the transform ADDR(COMMA(x, ..., z)) == COMMA(x, ..., ADDR(z)).
@@ -13269,7 +13192,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
// Propagate comma throws.
// If we are in the Valuenum CSE phase then don't morph away anything as these
// nodes may have CSE defs/uses in them.
- if (fgGlobalMorph && (oper != GT_ASG) && (oper != GT_COLON) && !tree->OperIsAnyList())
+ if (fgGlobalMorph && (oper != GT_ASG) && (oper != GT_COLON))
{
if ((op1 != nullptr) && fgIsCommaThrow(op1, true))
{
@@ -13509,7 +13432,7 @@ GenTree* Compiler::fgOptimizeEqualityComparisonWithConst(GenTreeOp* cmp)
}
noway_assert((op1->gtFlags & GTF_RELOP_JMP_USED) == 0);
- op1->gtFlags |= cmp->gtFlags & (GTF_RELOP_JMP_USED | GTF_RELOP_QMARK | GTF_DONT_CSE);
+ op1->gtFlags |= cmp->gtFlags & (GTF_RELOP_JMP_USED | GTF_DONT_CSE);
op1->SetVNsFromNode(cmp);
DEBUG_DESTROY_NODE(cmp);
@@ -14190,52 +14113,72 @@ GenTree* Compiler::fgMorphSmpOpOptional(GenTreeOp* tree)
}
break;
+ default:
+ break;
+ }
+ return tree;
+}
+
+#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+//------------------------------------------------------------------------
+// fgMorphMultiOp: Morph a GenTreeMultiOp (SIMD/HWINTRINSIC) tree.
+//
+// Arguments:
+// multiOp - The tree to morph
+//
+// Return Value:
+// The fully morphed tree.
+//
+GenTree* Compiler::fgMorphMultiOp(GenTreeMultiOp* multiOp)
+{
+ gtUpdateNodeOperSideEffects(multiOp);
+ for (GenTree** use : multiOp->UseEdges())
+ {
+ *use = fgMorphTree(*use);
+ multiOp->gtFlags |= ((*use)->gtFlags & GTF_ALL_EFFECT);
+ }
+
#if defined(FEATURE_HW_INTRINSICS) && defined(TARGET_XARCH)
- case GT_HWINTRINSIC:
+ if (opts.OptimizationEnabled() && multiOp->OperIs(GT_HWINTRINSIC))
+ {
+ GenTreeHWIntrinsic* hw = multiOp->AsHWIntrinsic();
+ switch (hw->GetHWIntrinsicId())
{
- GenTreeHWIntrinsic* hw = tree->AsHWIntrinsic();
- switch (hw->gtHWIntrinsicId)
+ case NI_SSE_Xor:
+ case NI_SSE2_Xor:
+ case NI_AVX_Xor:
+ case NI_AVX2_Xor:
{
- case NI_SSE_Xor:
- case NI_SSE2_Xor:
- case NI_AVX_Xor:
- case NI_AVX2_Xor:
+ // Transform XOR(X, 0) to X for vectors
+ GenTree* op1 = hw->Op(1);
+ GenTree* op2 = hw->Op(2);
+ if (!gtIsActiveCSE_Candidate(hw))
{
- // Transform XOR(X, 0) to X for vectors
- GenTree* op1 = hw->gtGetOp1();
- GenTree* op2 = hw->gtGetOp2();
- if (!gtIsActiveCSE_Candidate(tree))
+ if (op1->IsIntegralConstVector(0) && !gtIsActiveCSE_Candidate(op1))
{
- if (op1->IsIntegralConstVector(0) && !gtIsActiveCSE_Candidate(op1))
- {
- DEBUG_DESTROY_NODE(tree);
- DEBUG_DESTROY_NODE(op1);
- INDEBUG(op2->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED);
- return op2;
- }
- if (op2->IsIntegralConstVector(0) && !gtIsActiveCSE_Candidate(op2))
- {
- DEBUG_DESTROY_NODE(tree);
- DEBUG_DESTROY_NODE(op2);
- INDEBUG(op1->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED);
- return op1;
- }
+ DEBUG_DESTROY_NODE(hw);
+ DEBUG_DESTROY_NODE(op1);
+ return op2;
+ }
+ if (op2->IsIntegralConstVector(0) && !gtIsActiveCSE_Candidate(op2))
+ {
+ DEBUG_DESTROY_NODE(hw);
+ DEBUG_DESTROY_NODE(op2);
+ return op1;
}
- break;
}
-
- default:
- break;
+ break;
}
- break;
+
+ default:
+ break;
}
+ }
#endif // defined(FEATURE_HW_INTRINSICS) && defined(TARGET_XARCH)
- default:
- break;
- }
- return tree;
+ return multiOp;
}
+#endif // defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
//------------------------------------------------------------------------
// fgMorphModToSubMulDiv: Transform a % b into the equivalent a - (a / b) * b
@@ -14530,7 +14473,7 @@ GenTree* Compiler::fgRecognizeAndMorphBitwiseRotation(GenTree* tree)
{
noway_assert(GenTree::OperIsRotate(rotateOp));
- unsigned inputTreeEffects = tree->gtFlags & GTF_ALL_EFFECT;
+ GenTreeFlags inputTreeEffects = tree->gtFlags & GTF_ALL_EFFECT;
// We can use the same tree only during global morph; reusing the tree in a later morph
// may invalidate value numbers.
@@ -14806,6 +14749,17 @@ GenTree* Compiler::fgMorphTree(GenTree* tree, MorphAddrContext* mac)
tree = fgMorphCall(tree->AsCall());
break;
+#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+#if defined(FEATURE_SIMD)
+ case GT_SIMD:
+#endif
+#if defined(FEATURE_HW_INTRINSICS)
+ case GT_HWINTRINSIC:
+#endif
+ tree = fgMorphMultiOp(tree->AsMultiOp());
+ break;
+#endif // defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
+
case GT_ARR_ELEM:
tree->AsArrElem()->gtArrObj = fgMorphTree(tree->AsArrElem()->gtArrObj);
@@ -14968,7 +14922,7 @@ void Compiler::fgKillDependentAssertionsSingle(unsigned lclNum DEBUGARG(GenTree*
//
void Compiler::fgKillDependentAssertions(unsigned lclNum DEBUGARG(GenTree* tree))
{
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ LclVarDsc* varDsc = lvaGetDesc(lclNum);
if (varDsc->lvPromoted)
{
@@ -15313,17 +15267,14 @@ bool Compiler::fgFoldConditional(BasicBlock* block)
* Remove the loop from the table */
optLoopTable[loopNum].lpFlags |= LPFLG_REMOVED;
-#if FEATURE_LOOP_ALIGN
- optLoopTable[loopNum].lpFirst->bbFlags &= ~BBF_LOOP_ALIGN;
- JITDUMP("Removing LOOP_ALIGN flag from bogus loop in " FMT_BB "\n",
- optLoopTable[loopNum].lpFirst->bbNum);
-#endif
+
+ optLoopTable[loopNum].lpTop->unmarkLoopAlign(this DEBUG_ARG("Bogus loop"));
#ifdef DEBUG
if (verbose)
{
printf("Removing loop " FMT_LP " (from " FMT_BB " to " FMT_BB ")\n\n", loopNum,
- optLoopTable[loopNum].lpFirst->bbNum, optLoopTable[loopNum].lpBottom->bbNum);
+ optLoopTable[loopNum].lpTop->bbNum, optLoopTable[loopNum].lpBottom->bbNum);
}
#endif
}
@@ -15978,9 +15929,9 @@ void Compiler::fgMergeBlockReturn(BasicBlock* block)
noway_assert(ret->OperGet() == GT_RETURN);
noway_assert(ret->gtGetOp1() != nullptr);
- Statement* pAfterStatement = lastStmt;
- IL_OFFSETX offset = lastStmt->GetILOffsetX();
- GenTree* tree = gtNewTempAssign(genReturnLocal, ret->gtGetOp1(), &pAfterStatement, offset, block);
+ Statement* pAfterStatement = lastStmt;
+ const DebugInfo& di = lastStmt->GetDebugInfo();
+ GenTree* tree = gtNewTempAssign(genReturnLocal, ret->gtGetOp1(), &pAfterStatement, di, block);
if (tree->OperIsCopyBlkOp())
{
tree = fgMorphCopyBlock(tree);
@@ -15998,7 +15949,7 @@ void Compiler::fgMergeBlockReturn(BasicBlock* block)
{
// gtNewTempAssign inserted additional statements after last
fgRemoveStmt(block, lastStmt);
- Statement* newStmt = gtNewStmt(tree, offset);
+ Statement* newStmt = gtNewStmt(tree, di);
fgInsertStmtAfter(block, pAfterStatement, newStmt);
lastStmt = newStmt;
}
@@ -16379,9 +16330,6 @@ void Compiler::fgExpandQmarkForCastInstOf(BasicBlock* block, Statement* stmt)
cond2Expr = nestedQmark->gtGetOp1();
true2Expr = nestedQmark->gtGetOp2()->AsColon()->ThenNode();
false2Expr = nestedQmark->gtGetOp2()->AsColon()->ElseNode();
-
- assert(cond2Expr->gtFlags & GTF_RELOP_QMARK);
- cond2Expr->gtFlags &= ~GTF_RELOP_QMARK;
}
else
{
@@ -16399,10 +16347,6 @@ void Compiler::fgExpandQmarkForCastInstOf(BasicBlock* block, Statement* stmt)
}
assert(false2Expr->OperGet() == trueExpr->OperGet());
- // Clear flags as they are now going to be part of JTRUE.
- assert(condExpr->gtFlags & GTF_RELOP_QMARK);
- condExpr->gtFlags &= ~GTF_RELOP_QMARK;
-
// Create the chain of blocks. See method header comment.
// The order of blocks after this is the following:
// block ... asgBlock ... cond1Block ... cond2Block ... helperBlock ... remainderBlock
@@ -16456,23 +16400,23 @@ void Compiler::fgExpandQmarkForCastInstOf(BasicBlock* block, Statement* stmt)
// Append cond1 as JTRUE to cond1Block
GenTree* jmpTree = gtNewOperNode(GT_JTRUE, TYP_VOID, condExpr);
- Statement* jmpStmt = fgNewStmtFromTree(jmpTree, stmt->GetILOffsetX());
+ Statement* jmpStmt = fgNewStmtFromTree(jmpTree, stmt->GetDebugInfo());
fgInsertStmtAtEnd(cond1Block, jmpStmt);
// Append cond2 as JTRUE to cond2Block
jmpTree = gtNewOperNode(GT_JTRUE, TYP_VOID, cond2Expr);
- jmpStmt = fgNewStmtFromTree(jmpTree, stmt->GetILOffsetX());
+ jmpStmt = fgNewStmtFromTree(jmpTree, stmt->GetDebugInfo());
fgInsertStmtAtEnd(cond2Block, jmpStmt);
// AsgBlock should get tmp = op1 assignment.
trueExpr = gtNewTempAssign(dst->AsLclVarCommon()->GetLclNum(), trueExpr);
- Statement* trueStmt = fgNewStmtFromTree(trueExpr, stmt->GetILOffsetX());
+ Statement* trueStmt = fgNewStmtFromTree(trueExpr, stmt->GetDebugInfo());
fgInsertStmtAtEnd(asgBlock, trueStmt);
// Since we are adding helper in the JTRUE false path, reverse the cond2 and add the helper.
gtReverseCond(cond2Expr);
GenTree* helperExpr = gtNewTempAssign(dst->AsLclVarCommon()->GetLclNum(), true2Expr);
- Statement* helperStmt = fgNewStmtFromTree(helperExpr, stmt->GetILOffsetX());
+ Statement* helperStmt = fgNewStmtFromTree(helperExpr, stmt->GetDebugInfo());
fgInsertStmtAtEnd(helperBlock, helperStmt);
// Finally remove the nested qmark stmt.
@@ -16573,9 +16517,6 @@ void Compiler::fgExpandQmarkStmt(BasicBlock* block, Statement* stmt)
GenTree* trueExpr = qmark->gtGetOp2()->AsColon()->ThenNode();
GenTree* falseExpr = qmark->gtGetOp2()->AsColon()->ElseNode();
- assert(condExpr->gtFlags & GTF_RELOP_QMARK);
- condExpr->gtFlags &= ~GTF_RELOP_QMARK;
-
assert(!varTypeIsFloating(condExpr->TypeGet()));
bool hasTrueExpr = (trueExpr->OperGet() != GT_NOP);
@@ -16674,7 +16615,7 @@ void Compiler::fgExpandQmarkStmt(BasicBlock* block, Statement* stmt)
}
GenTree* jmpTree = gtNewOperNode(GT_JTRUE, TYP_VOID, qmark->gtGetOp1());
- Statement* jmpStmt = fgNewStmtFromTree(jmpTree, stmt->GetILOffsetX());
+ Statement* jmpStmt = fgNewStmtFromTree(jmpTree, stmt->GetDebugInfo());
fgInsertStmtAtEnd(condBlock, jmpStmt);
// Remove the original qmark statement.
@@ -16700,7 +16641,7 @@ void Compiler::fgExpandQmarkStmt(BasicBlock* block, Statement* stmt)
{
trueExpr = gtNewTempAssign(lclNum, trueExpr);
}
- Statement* trueStmt = fgNewStmtFromTree(trueExpr, stmt->GetILOffsetX());
+ Statement* trueStmt = fgNewStmtFromTree(trueExpr, stmt->GetDebugInfo());
fgInsertStmtAtEnd(thenBlock, trueStmt);
}
@@ -16711,7 +16652,7 @@ void Compiler::fgExpandQmarkStmt(BasicBlock* block, Statement* stmt)
{
falseExpr = gtNewTempAssign(lclNum, falseExpr);
}
- Statement* falseStmt = fgNewStmtFromTree(falseExpr, stmt->GetILOffsetX());
+ Statement* falseStmt = fgNewStmtFromTree(falseExpr, stmt->GetDebugInfo());
fgInsertStmtAtEnd(elseBlock, falseStmt);
}
@@ -16859,7 +16800,7 @@ void Compiler::fgPromoteStructs()
{
// Whether this var got promoted
bool promotedVar = false;
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ LclVarDsc* varDsc = lvaGetDesc(lclNum);
// If we have marked this as lvUsedInSIMDIntrinsic, then we do not want to promote
// its fields. Instead, we will attempt to enregister the entire struct.
@@ -16914,7 +16855,7 @@ void Compiler::fgMorphStructField(GenTree* tree, GenTree* parent)
if ((obj != nullptr) && (obj->gtOper == GT_LCL_VAR))
{
unsigned lclNum = obj->AsLclVarCommon()->GetLclNum();
- const LclVarDsc* varDsc = &lvaTable[lclNum];
+ const LclVarDsc* varDsc = lvaGetDesc(lclNum);
if (varTypeIsStruct(obj))
{
@@ -16931,7 +16872,7 @@ void Compiler::fgMorphStructField(GenTree* tree, GenTree* parent)
return;
}
- const LclVarDsc* fieldDsc = &lvaTable[fieldLclIndex];
+ const LclVarDsc* fieldDsc = lvaGetDesc(fieldLclIndex);
var_types fieldType = fieldDsc->TypeGet();
assert(fieldType != TYP_STRUCT); // promoted LCL_VAR can't have a struct type.
@@ -17092,7 +17033,7 @@ void Compiler::fgMorphLocalField(GenTree* tree, GenTree* parent)
noway_assert(tree->OperGet() == GT_LCL_FLD);
unsigned lclNum = tree->AsLclFld()->GetLclNum();
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ LclVarDsc* varDsc = lvaGetDesc(lclNum);
if (varTypeIsStruct(varDsc))
{
@@ -17107,7 +17048,7 @@ void Compiler::fgMorphLocalField(GenTree* tree, GenTree* parent)
{
fieldLclIndex = lvaGetFieldLocal(varDsc, fldOffset);
noway_assert(fieldLclIndex != BAD_VAR_NUM);
- fldVarDsc = &lvaTable[fieldLclIndex];
+ fldVarDsc = lvaGetDesc(fieldLclIndex);
}
var_types treeType = tree->TypeGet();
@@ -17209,7 +17150,7 @@ void Compiler::fgRetypeImplicitByRefArgs()
for (unsigned lclNum = 0; lclNum < info.compArgsCount; lclNum++)
{
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ LclVarDsc* varDsc = lvaGetDesc(lclNum);
if (lvaIsImplicitByRefLocal(lclNum))
{
@@ -17237,10 +17178,10 @@ void Compiler::fgRetypeImplicitByRefArgs()
}
// Update varDsc since lvaGrabTemp might have re-allocated the var dsc array.
- varDsc = &lvaTable[lclNum];
+ varDsc = lvaGetDesc(lclNum);
// Copy the struct promotion annotations to the new temp.
- LclVarDsc* newVarDsc = &lvaTable[newLclNum];
+ LclVarDsc* newVarDsc = lvaGetDesc(newLclNum);
newVarDsc->lvPromoted = true;
newVarDsc->lvFieldLclStart = varDsc->lvFieldLclStart;
newVarDsc->lvFieldCnt = varDsc->lvFieldCnt;
@@ -17316,7 +17257,7 @@ void Compiler::fgRetypeImplicitByRefArgs()
for (unsigned fieldLclNum = fieldLclStart; fieldLclNum < fieldLclStop; ++fieldLclNum)
{
- LclVarDsc* fieldVarDsc = &lvaTable[fieldLclNum];
+ LclVarDsc* fieldVarDsc = lvaGetDesc(fieldLclNum);
if (undoPromotion)
{
@@ -17415,7 +17356,7 @@ void Compiler::fgMarkDemotedImplicitByRefArgs()
for (unsigned lclNum = 0; lclNum < info.compArgsCount; lclNum++)
{
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ LclVarDsc* varDsc = lvaGetDesc(lclNum);
if (lvaIsImplicitByRefLocal(lclNum))
{
@@ -17444,7 +17385,7 @@ void Compiler::fgMarkDemotedImplicitByRefArgs()
// The temp struct is now unused; set flags appropriately so that we
// won't allocate space for it on the stack.
- LclVarDsc* structVarDsc = &lvaTable[structLclNum];
+ LclVarDsc* structVarDsc = lvaGetDesc(structLclNum);
structVarDsc->CleanAddressExposed();
#ifdef DEBUG
structVarDsc->lvUnusedStruct = true;
@@ -17460,7 +17401,7 @@ void Compiler::fgMarkDemotedImplicitByRefArgs()
JITDUMP("Fixing pointer for field V%02d from V%02d to V%02d\n", fieldLclNum, lclNum, structLclNum);
// Fix the pointer to the parent local.
- LclVarDsc* fieldVarDsc = &lvaTable[fieldLclNum];
+ LclVarDsc* fieldVarDsc = lvaGetDesc(fieldLclNum);
assert(fieldVarDsc->lvParentLcl == lclNum);
fieldVarDsc->lvParentLcl = structLclNum;
@@ -17531,7 +17472,7 @@ GenTree* Compiler::fgMorphImplicitByRefArgs(GenTree* tree, bool isAddr)
GenTree* lclVarTree = isAddr ? tree->AsOp()->gtOp1 : tree;
unsigned lclNum = lclVarTree->AsLclVarCommon()->GetLclNum();
- LclVarDsc* lclVarDsc = &lvaTable[lclNum];
+ LclVarDsc* lclVarDsc = lvaGetDesc(lclNum);
CORINFO_FIELD_HANDLE fieldHnd;
unsigned fieldOffset = 0;
@@ -17570,7 +17511,7 @@ GenTree* Compiler::fgMorphImplicitByRefArgs(GenTree* tree, bool isAddr)
assert(fieldHnd != nullptr);
// Update lclNum/lclVarDsc to refer to the parameter
lclNum = lclVarDsc->lvParentLcl;
- lclVarDsc = &lvaTable[lclNum];
+ lclVarDsc = lvaGetDesc(lclNum);
fieldRefType = lclVarTree->TypeGet();
}
else
@@ -18076,64 +18017,3 @@ bool Compiler::fgCanTailCallViaJitHelper()
return true;
#endif
}
-
-static const int numberOfTrackedFlags = 5;
-static const GenTreeFlags trackedFlags[numberOfTrackedFlags] = {GTF_ASG, GTF_CALL, GTF_EXCEPT, GTF_GLOB_REF,
- GTF_ORDER_SIDEEFF};
-
-//------------------------------------------------------------------------
-// fgMorphArgList: morph argument list tree without recursion.
-//
-// Arguments:
-// args - argument list tree to morph;
-// mac - morph address context, used to morph children.
-//
-// Return Value:
-// morphed argument list.
-//
-GenTreeArgList* Compiler::fgMorphArgList(GenTreeArgList* args, MorphAddrContext* mac)
-{
- // Use a non-recursive algorithm that morphs all actual list values,
- // memorizes the last node for each effect flag and resets
- // them during the second iteration.
- assert((trackedFlags[0] | trackedFlags[1] | trackedFlags[2] | trackedFlags[3] | trackedFlags[4]) == GTF_ALL_EFFECT);
-
- GenTree* memorizedLastNodes[numberOfTrackedFlags] = {nullptr};
-
- for (GenTreeArgList* listNode = args; listNode != nullptr; listNode = listNode->Rest())
- {
- // Morph actual list values.
- GenTree*& arg = listNode->Current();
- arg = fgMorphTree(arg, mac);
-
- // Remember the last list node with each flag.
- for (int i = 0; i < numberOfTrackedFlags; ++i)
- {
- if ((arg->gtFlags & trackedFlags[i]) != 0)
- {
- memorizedLastNodes[i] = listNode;
- }
- }
- }
-
- for (GenTreeArgList* listNode = args; listNode != nullptr; listNode = listNode->Rest())
- {
- // Clear all old effects from the list node.
- listNode->gtFlags &= ~GTF_ALL_EFFECT;
-
- // Spread each flag to all list nodes (to the prefix) before the memorized last node.
- for (int i = 0; i < numberOfTrackedFlags; ++i)
- {
- if (memorizedLastNodes[i] != nullptr)
- {
- listNode->gtFlags |= trackedFlags[i];
- }
- if (listNode == memorizedLastNodes[i])
- {
- memorizedLastNodes[i] = nullptr;
- }
- }
- }
-
- return args;
-}
diff --git a/src/coreclr/jit/morphblock.cpp b/src/coreclr/jit/morphblock.cpp
index fdd0e90b24a380..a8482726a5da23 100644
--- a/src/coreclr/jit/morphblock.cpp
+++ b/src/coreclr/jit/morphblock.cpp
@@ -189,7 +189,7 @@ GenTree* MorphInitBlockHelper::Morph()
// with information about it.
//
// Notes:
-// When assertion propogation is enabled this method kills assertions about the dst local,
+// When assertion propagation is enabled this method kills assertions about the dst local,
// so the correctness depends on `IsLocalAddrExpr` recognizing all patterns.
//
void MorphInitBlockHelper::PrepareDst()
diff --git a/src/coreclr/jit/namedintrinsiclist.h b/src/coreclr/jit/namedintrinsiclist.h
index 8a0af00d3ec47b..62ebb80c089994 100644
--- a/src/coreclr/jit/namedintrinsiclist.h
+++ b/src/coreclr/jit/namedintrinsiclist.h
@@ -61,6 +61,9 @@ enum NamedIntrinsic : unsigned short
NI_System_Array_GetUpperBound,
NI_System_Object_MemberwiseClone,
+ NI_System_Runtime_CompilerServices_RuntimeHelpers_CreateSpan,
+ NI_System_Runtime_CompilerServices_RuntimeHelpers_InitializeArray,
+
NI_System_String_get_Chars,
NI_System_String_get_Length,
NI_System_Span_get_Item,
diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp
index fc238be39dfa69..0c66dd39a17736 100644
--- a/src/coreclr/jit/objectalloc.cpp
+++ b/src/coreclr/jit/objectalloc.cpp
@@ -283,7 +283,7 @@ void ObjectAllocator::ComputeStackObjectPointers(BitVecTraits* bitVecTraits)
changed = false;
for (unsigned int lclNum = 0; lclNum < comp->lvaCount; ++lclNum)
{
- LclVarDsc* lclVarDsc = comp->lvaTable + lclNum;
+ LclVarDsc* lclVarDsc = comp->lvaGetDesc(lclNum);
var_types type = lclVarDsc->TypeGet();
if (type == TYP_REF || type == TYP_I_IMPL || type == TYP_BYREF)
@@ -883,7 +883,7 @@ void ObjectAllocator::RewriteUses()
const unsigned int lclNum = tree->AsLclVarCommon()->GetLclNum();
unsigned int newLclNum = BAD_VAR_NUM;
- LclVarDsc* lclVarDsc = m_compiler->lvaTable + lclNum;
+ LclVarDsc* lclVarDsc = m_compiler->lvaGetDesc(lclNum);
if ((lclNum < BitVecTraits::GetSize(&m_allocator->m_bitVecTraits)) &&
m_allocator->MayLclVarPointToStack(lclNum))
diff --git a/src/coreclr/jit/optcse.cpp b/src/coreclr/jit/optcse.cpp
index 9aee78d68efca7..b6dadaac185771 100644
--- a/src/coreclr/jit/optcse.cpp
+++ b/src/coreclr/jit/optcse.cpp
@@ -655,6 +655,7 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, Statement* stmt)
}
}
+ hval = optCSEKeyToHashIndex(key, newOptCSEhashSize);
optCSEhash = newOptCSEhash;
optCSEhashSize = newOptCSEhashSize;
optCSEhashMaxCountBeforeResize = optCSEhashMaxCountBeforeResize * s_optCSEhashGrowthFactor;
@@ -3635,7 +3636,7 @@ bool Compiler::optIsCSEcandidate(GenTree* tree)
{
GenTreeHWIntrinsic* hwIntrinsicNode = tree->AsHWIntrinsic();
assert(hwIntrinsicNode != nullptr);
- HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(hwIntrinsicNode->gtHWIntrinsicId);
+ HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(hwIntrinsicNode->GetHWIntrinsicId());
switch (category)
{
diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp
index 10f14b2b53cf7b..39d2328c2e8d62 100644
--- a/src/coreclr/jit/optimizer.cpp
+++ b/src/coreclr/jit/optimizer.cpp
@@ -20,16 +20,16 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
void Compiler::optInit()
{
- optLoopsMarked = false;
- fgHasLoops = false;
+ optLoopsMarked = false;
+ fgHasLoops = false;
+ loopAlignCandidates = 0;
/* Initialize the # of tracked loops to 0 */
optLoopCount = 0;
optLoopTable = nullptr;
#ifdef DEBUG
- loopAlignCandidates = 0;
- loopsAligned = 0;
+ loopsAligned = 0;
#endif
/* Keep track of the number of calls and indirect calls made by this method */
@@ -78,13 +78,14 @@ void Compiler::optSetBlockWeights()
if (!usingProfileWeights && firstBBDominatesAllReturns)
{
+ // If the weight is already zero (and thus rarely run), there's no point scaling it.
if (block->bbWeight != BB_ZERO_WEIGHT)
{
- // Calculate our bbWeight:
- //
- // o BB_UNITY_WEIGHT if we dominate all BBJ_RETURN blocks
- // o otherwise BB_UNITY_WEIGHT / 2
- //
+ // If the block dominates all return blocks, leave the weight alone. Otherwise,
+ // scale the weight by 0.5 as a heuristic that some other path gets some of the dynamic flow.
+ // Note that `optScaleLoopBlocks` has a similar heuristic for loop blocks that don't dominate
+ // their loop back edge.
+
bool blockDominatesAllReturns = true; // Assume that we will dominate
for (BasicBlockList* retBlocks = fgReturnBlocks; retBlocks != nullptr; retBlocks = retBlocks->next)
@@ -99,6 +100,10 @@ void Compiler::optSetBlockWeights()
if (block == fgFirstBB)
{
firstBBDominatesAllReturns = blockDominatesAllReturns;
+
+ // Don't scale the weight of the first block, since it is guaranteed to execute.
+ // If the first block does not dominate all the returns, we won't scale any of the function's
+ // block weights.
}
else
{
@@ -108,6 +113,12 @@ void Compiler::optSetBlockWeights()
if (!blockDominatesAllReturns)
{
INDEBUG(changed = true);
+
+ // TODO-Cleanup: we should use:
+ // block->scaleBBWeight(0.5);
+ // since we are inheriting "from ourselves", but that leads to asm diffs due to minutely
+ // different floating-point value in the calculation, and some code that compares weights
+ // for equality.
block->inheritWeightPercentage(block, 50);
}
}
@@ -128,29 +139,25 @@ void Compiler::optSetBlockWeights()
#endif
}
-/*****************************************************************************
- *
- * Marks the blocks between 'begBlk' and 'endBlk' as part of a loop.
- */
-
-void Compiler::optMarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk, bool excludeEndBlk)
+//------------------------------------------------------------------------
+// optScaleLoopBlocks: Scale the weight of loop blocks from 'begBlk' to 'endBlk'.
+//
+// Arguments:
+// begBlk - first block of range. Must be marked as a loop head (BBF_LOOP_HEAD).
+// endBlk - last block of range (inclusive). Must be reachable from `begBlk`.
+//
+// Operation:
+// Calculate the 'loop weight'. This is the amount to scale the weight of each block in the loop.
+// Our heuristic is that loops are weighted eight times more than straight-line code
+// (scale factor is BB_LOOP_WEIGHT_SCALE). If the loops are all properly formed this gives us these weights:
+//
+// 1 -- non-loop basic block
+// 8 -- single loop nesting
+// 64 -- double loop nesting
+// 512 -- triple loop nesting
+//
+void Compiler::optScaleLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk)
{
- /* Calculate the 'loopWeight',
- this is the amount to increase each block in the loop
- Our heuristic is that loops are weighted eight times more
- than straight line code.
- Thus we increase each block by 7 times the weight of
- the loop header block,
- if the loops are all properly formed gives us:
- (assuming that BB_LOOP_WEIGHT_SCALE is 8)
-
- 1 -- non loop basic block
- 8 -- single loop nesting
- 64 -- double loop nesting
- 512 -- triple loop nesting
-
- */
-
noway_assert(begBlk->bbNum <= endBlk->bbNum);
noway_assert(begBlk->isLoopHead());
noway_assert(fgReachable(begBlk, endBlk));
@@ -159,17 +166,16 @@ void Compiler::optMarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk, bool ex
#ifdef DEBUG
if (verbose)
{
- printf("\nMarking a loop from " FMT_BB " to " FMT_BB, begBlk->bbNum,
- excludeEndBlk ? endBlk->bbPrev->bbNum : endBlk->bbNum);
+ printf("\nMarking a loop from " FMT_BB " to " FMT_BB, begBlk->bbNum, endBlk->bbNum);
}
#endif
- /* Build list of backedges for block begBlk */
+ // Build list of back edges for block begBlk.
flowList* backedgeList = nullptr;
for (BasicBlock* const predBlock : begBlk->PredBlocks())
{
- /* Is this a backedge? */
+ // Is this a back edge?
if (predBlock->bbNum >= begBlk->bbNum)
{
backedgeList = new (this, CMK_FlowList) flowList(predBlock, backedgeList);
@@ -181,24 +187,41 @@ void Compiler::optMarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk, bool ex
}
}
- /* At least one backedge must have been found (the one from endBlk) */
+ // At least one backedge must have been found (the one from endBlk).
noway_assert(backedgeList);
- BasicBlock* curBlk = begBlk;
+ auto reportBlockWeight = [&](BasicBlock* blk, const char* message) {
+#ifdef DEBUG
+ if (verbose)
+ {
+ printf("\n " FMT_BB "(wt=" FMT_WT ")%s", blk->bbNum, blk->getBBWeight(this), message);
+ }
+#endif // DEBUG
+ };
- while (true)
+ for (BasicBlock* const curBlk : BasicBlockRangeList(begBlk, endBlk))
{
- noway_assert(curBlk);
+ // Don't change the block weight if it came from profile data.
+ if (curBlk->hasProfileWeight())
+ {
+ reportBlockWeight(curBlk, "; unchanged: has profile weight");
+ continue;
+ }
- // For curBlk to be part of a loop that starts at begBlk
- // curBlk must be reachable from begBlk and (since this is a loop)
- // likewise begBlk must be reachable from curBlk.
- //
+ // Don't change the block weight if it's known to be rarely run.
+ if (curBlk->isRunRarely())
+ {
+ reportBlockWeight(curBlk, "; unchanged: run rarely");
+ continue;
+ }
+
+ // For curBlk to be part of a loop that starts at begBlk, curBlk must be reachable from begBlk and
+ // (since this is a loop) begBlk must likewise be reachable from curBlk.
if (fgReachable(curBlk, begBlk) && fgReachable(begBlk, curBlk))
{
- /* If this block reaches any of the backedge blocks we set reachable */
- /* If this block dominates any of the backedge blocks we set dominates */
+ // If `curBlk` reaches any of the back edge blocks we set `reachable`.
+ // If `curBlk` dominates any of the back edge blocks we set `dominates`.
bool reachable = false;
bool dominates = false;
@@ -206,88 +229,75 @@ void Compiler::optMarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk, bool ex
{
BasicBlock* backedge = tmp->getBlock();
- if (!curBlk->isRunRarely())
- {
- reachable |= fgReachable(curBlk, backedge);
- dominates |= fgDominate(curBlk, backedge);
+ reachable |= fgReachable(curBlk, backedge);
+ dominates |= fgDominate(curBlk, backedge);
- if (dominates && reachable)
- {
- break;
- }
+ if (dominates && reachable)
+ {
+ // No need to keep looking; we've already found all the info we need.
+ break;
}
}
if (reachable)
{
+ // If the block has BB_ZERO_WEIGHT, then it should be marked as rarely run, and skipped, above.
noway_assert(curBlk->bbWeight > BB_ZERO_WEIGHT);
- if (!curBlk->hasProfileWeight())
- {
- weight_t scale = BB_LOOP_WEIGHT_SCALE;
-
- if (!dominates)
- {
- scale = scale / 2;
- }
+ weight_t scale = BB_LOOP_WEIGHT_SCALE;
- curBlk->scaleBBWeight(scale);
+ if (!dominates)
+ {
+ // If `curBlk` reaches but doesn't dominate any back edge to `endBlk` then there must be at least
+ // some other path to `endBlk`, so don't give `curBlk` all the execution weight.
+ scale = scale / 2;
}
- JITDUMP("\n " FMT_BB "(wt=" FMT_WT ")", curBlk->bbNum, curBlk->getBBWeight(this));
- }
- }
-
- /* Stop if we've reached the last block in the loop */
+ curBlk->scaleBBWeight(scale);
- if (curBlk == endBlk)
- {
- break;
+ reportBlockWeight(curBlk, "");
+ }
+ else
+ {
+ reportBlockWeight(curBlk, "; unchanged: back edge unreachable");
+ }
}
-
- curBlk = curBlk->bbNext;
-
- /* If we are excluding the endBlk then stop if we've reached endBlk */
-
- if (excludeEndBlk && (curBlk == endBlk))
+ else
{
- break;
+ reportBlockWeight(curBlk, "; unchanged: block not in loop");
}
}
}
-/*****************************************************************************
- *
- * Unmark the blocks between 'begBlk' and 'endBlk' as part of a loop.
- */
-
+//------------------------------------------------------------------------
+// optUnmarkLoopBlocks: Unmark the blocks between 'begBlk' and 'endBlk' as part of a loop.
+//
+// Arguments:
+// begBlk - first block of range. Must be marked as a loop head (BBF_LOOP_HEAD).
+// endBlk - last block of range (inclusive). Must be reachable from `begBlk`.
+//
+// Operation:
+// A set of blocks that were previously marked as a loop are now to be unmarked, since we have decided that
+// for some reason this loop no longer exists. Basically we are just resetting the blocks bbWeight to their
+// previous values.
+//
void Compiler::optUnmarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk)
{
- /* A set of blocks that were previously marked as a loop are now
- to be unmarked, since we have decided that for some reason this
- loop no longer exists.
- Basically we are just reseting the blocks bbWeight to their
- previous values.
- */
-
noway_assert(begBlk->bbNum <= endBlk->bbNum);
noway_assert(begBlk->isLoopHead());
-
noway_assert(!opts.MinOpts());
unsigned backEdgeCount = 0;
for (BasicBlock* const predBlock : begBlk->PredBlocks())
{
- /* is this a backward edge? (from predBlock to begBlk) */
-
+ // Is this a backward edge? (from predBlock to begBlk)
if (begBlk->bbNum > predBlock->bbNum)
{
continue;
}
- /* We only consider back-edges that are BBJ_COND or BBJ_ALWAYS for loops */
-
+ // We only consider back-edges that are BBJ_COND or BBJ_ALWAYS for loops.
if ((predBlock->bbJumpKind != BBJ_COND) && (predBlock->bbJumpKind != BBJ_ALWAYS))
{
continue;
@@ -296,7 +306,7 @@ void Compiler::optUnmarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk)
backEdgeCount++;
}
- /* Only unmark the loop blocks if we have exactly one loop back edge */
+ // Only unmark the loop blocks if we have exactly one loop back edge.
if (backEdgeCount != 1)
{
#ifdef DEBUG
@@ -314,71 +324,62 @@ void Compiler::optUnmarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk)
#endif
return;
}
- noway_assert(backEdgeCount == 1);
noway_assert(fgReachable(begBlk, endBlk));
#ifdef DEBUG
if (verbose)
{
- printf("\nUnmarking loop at " FMT_BB, begBlk->bbNum);
+ printf("\nUnmarking a loop from " FMT_BB " to " FMT_BB, begBlk->bbNum, endBlk->bbNum);
}
#endif
- BasicBlock* curBlk = begBlk;
- while (true)
+ for (BasicBlock* const curBlk : BasicBlockRangeList(begBlk, endBlk))
{
- noway_assert(curBlk);
-
- // For curBlk to be part of a loop that starts at begBlk
- // curBlk must be reachable from begBlk and (since this is a loop)
- // likewise begBlk must be reachable from curBlk.
- //
- if (!curBlk->isRunRarely() && fgReachable(curBlk, begBlk) && fgReachable(begBlk, curBlk))
+ // Stop if we go past the last block in the loop, as it may have been deleted.
+ if (curBlk->bbNum > endBlk->bbNum)
{
- // Don't unmark blocks that are set to BB_MAX_WEIGHT
- // Don't unmark blocks when we are using profile weights
- //
- if (!curBlk->isMaxBBWeight() && !curBlk->hasProfileWeight())
- {
- weight_t scale = 1.0 / BB_LOOP_WEIGHT_SCALE;
-
- if (!fgDominate(curBlk, endBlk))
- {
- scale *= 2;
- }
-
- curBlk->scaleBBWeight(scale);
- }
-
- JITDUMP("\n " FMT_BB "(wt=" FMT_WT ")", curBlk->bbNum, curBlk->getBBWeight(this));
+ break;
}
- /* Stop if we've reached the last block in the loop */
+ // Don't change the block weight if it's known to be rarely run.
+ if (curBlk->isRunRarely())
+ {
+ continue;
+ }
- if (curBlk == endBlk)
+ // Don't change the block weight if it came from profile data.
+ if (curBlk->hasProfileWeight())
{
- break;
+ continue;
}
- curBlk = curBlk->bbNext;
+ // Don't unmark blocks that are maximum weight.
+ if (curBlk->isMaxBBWeight())
+ {
+ continue;
+ }
- /* Stop if we go past the last block in the loop, as it may have been deleted */
- if (curBlk->bbNum > endBlk->bbNum)
+ // For curBlk to be part of a loop that starts at begBlk, curBlk must be reachable from begBlk and
+ // (since this is a loop) begBlk must likewise be reachable from curBlk.
+ //
+ if (fgReachable(curBlk, begBlk) && fgReachable(begBlk, curBlk))
{
- break;
+ weight_t scale = 1.0 / BB_LOOP_WEIGHT_SCALE;
+
+ if (!fgDominate(curBlk, endBlk))
+ {
+ scale *= 2;
+ }
+
+ curBlk->scaleBBWeight(scale);
+
+ JITDUMP("\n " FMT_BB "(wt=" FMT_WT ")", curBlk->bbNum, curBlk->getBBWeight(this));
}
}
JITDUMP("\n");
-#if FEATURE_LOOP_ALIGN
- if (begBlk->isLoopAlign())
- {
- // Clear the loop alignment bit on the head of a loop, since it's no longer a loop.
- begBlk->bbFlags &= ~BBF_LOOP_ALIGN;
- JITDUMP("Removing LOOP_ALIGN flag from removed loop in " FMT_BB "\n", begBlk->bbNum);
- }
-#endif
+ begBlk->unmarkLoopAlign(this DEBUG_ARG("Removed loop"));
}
/*****************************************************************************************************
@@ -550,13 +551,18 @@ void Compiler::optUpdateLoopsBeforeRemoveBlock(BasicBlock* block, bool skipUnmar
{
printf("\nUpdateLoopsBeforeRemoveBlock After: ");
optPrintLoopInfo(loopNum);
+ printf("\n");
}
#endif
}
- if ((skipUnmarkLoop == false) && ((block->bbJumpKind == BBJ_ALWAYS) || (block->bbJumpKind == BBJ_COND)) &&
- (block->bbJumpDest->isLoopHead()) && (block->bbJumpDest->bbNum <= block->bbNum) && fgDomsComputed &&
- (fgCurBBEpochSize == fgDomBBcount + 1) && fgReachable(block->bbJumpDest, block))
+ if ((skipUnmarkLoop == false) && //
+ ((block->bbJumpKind == BBJ_ALWAYS) || (block->bbJumpKind == BBJ_COND)) && //
+ block->bbJumpDest->isLoopHead() && //
+ (block->bbJumpDest->bbNum <= block->bbNum) && //
+ fgDomsComputed && //
+ (fgCurBBEpochSize == fgDomBBcount + 1) && //
+ fgReachable(block->bbJumpDest, block))
{
optUnmarkLoopBlocks(block->bbJumpDest, block);
}
@@ -571,7 +577,6 @@ void Compiler::optUpdateLoopsBeforeRemoveBlock(BasicBlock* block, bool skipUnmar
void Compiler::optPrintLoopInfo(unsigned loopInd,
BasicBlock* lpHead,
- BasicBlock* lpFirst,
BasicBlock* lpTop,
BasicBlock* lpEntry,
BasicBlock* lpBottom,
@@ -579,16 +584,8 @@ void Compiler::optPrintLoopInfo(unsigned loopInd,
BasicBlock* lpExit,
unsigned parentLoop) const
{
- noway_assert(lpHead);
-
- printf(FMT_LP ", from " FMT_BB, loopInd, lpFirst->bbNum);
- if (lpTop != lpFirst)
- {
- printf(" (loop top is " FMT_BB ")", lpTop->bbNum);
- }
-
- printf(" to " FMT_BB " (Head=" FMT_BB ", Entry=" FMT_BB ", ExitCnt=%d", lpBottom->bbNum, lpHead->bbNum,
- lpEntry->bbNum, lpExitCnt);
+ printf(FMT_LP ", from " FMT_BB " to " FMT_BB " (Head=" FMT_BB ", Entry=" FMT_BB ", ExitCnt=%d", loopInd,
+ lpTop->bbNum, lpBottom->bbNum, lpHead->bbNum, lpEntry->bbNum, lpExitCnt);
if (lpExitCnt == 1)
{
@@ -609,15 +606,15 @@ void Compiler::optPrintLoopInfo(unsigned loopInd,
void Compiler::optPrintLoopInfo(unsigned lnum) const
{
- noway_assert(lnum < optLoopCount);
+ assert(lnum < optLoopCount);
- const LoopDsc* ldsc = &optLoopTable[lnum]; // lnum is the INDEX to the loop table.
+ const LoopDsc* ldsc = &optLoopTable[lnum];
- optPrintLoopInfo(lnum, ldsc->lpHead, ldsc->lpFirst, ldsc->lpTop, ldsc->lpEntry, ldsc->lpBottom, ldsc->lpExitCnt,
- ldsc->lpExit, ldsc->lpParent);
+ optPrintLoopInfo(lnum, ldsc->lpHead, ldsc->lpTop, ldsc->lpEntry, ldsc->lpBottom, ldsc->lpExitCnt, ldsc->lpExit,
+ ldsc->lpParent);
}
-#endif
+#endif // DEBUG
//------------------------------------------------------------------------
// optPopulateInitInfo: Populate loop init info in the loop table.
@@ -1035,13 +1032,8 @@ bool Compiler::optExtractInitTestIncr(
* out of entries in loop table.
*/
-bool Compiler::optRecordLoop(BasicBlock* head,
- BasicBlock* first,
- BasicBlock* top,
- BasicBlock* entry,
- BasicBlock* bottom,
- BasicBlock* exit,
- unsigned char exitCnt)
+bool Compiler::optRecordLoop(
+ BasicBlock* head, BasicBlock* top, BasicBlock* entry, BasicBlock* bottom, BasicBlock* exit, unsigned char exitCnt)
{
// Record this loop in the table, if there's room.
@@ -1055,7 +1047,6 @@ bool Compiler::optRecordLoop(BasicBlock* head,
}
// Assumed preconditions on the loop we're adding.
- assert(first->bbNum <= top->bbNum);
assert(top->bbNum <= entry->bbNum);
assert(entry->bbNum <= bottom->bbNum);
assert(head->bbNum < top->bbNum || head->bbNum > bottom->bbNum);
@@ -1073,7 +1064,7 @@ bool Compiler::optRecordLoop(BasicBlock* head,
for (unsigned char prevPlus1 = optLoopCount; prevPlus1 > 0; prevPlus1--)
{
unsigned char prev = prevPlus1 - 1;
- if (optLoopTable[prev].lpContainedBy(first, bottom))
+ if (optLoopTable[prev].lpContainedBy(top, bottom))
{
loopInd = prev;
}
@@ -1091,17 +1082,16 @@ bool Compiler::optRecordLoop(BasicBlock* head,
// The loop is well-formed.
assert(optLoopTable[i].lpWellFormed());
// Check for disjoint.
- if (optLoopTable[i].lpDisjoint(first, bottom))
+ if (optLoopTable[i].lpDisjoint(top, bottom))
{
continue;
}
// Otherwise, assert complete containment (of optLoopTable[i] in new loop).
- assert(optLoopTable[i].lpContainedBy(first, bottom));
+ assert(optLoopTable[i].lpContainedBy(top, bottom));
}
#endif // DEBUG
optLoopTable[loopInd].lpHead = head;
- optLoopTable[loopInd].lpFirst = first;
optLoopTable[loopInd].lpTop = top;
optLoopTable[loopInd].lpBottom = bottom;
optLoopTable[loopInd].lpEntry = entry;
@@ -1257,7 +1247,7 @@ void Compiler::optPrintLoopRecording(unsigned loopInd) const
printf("Recorded loop %s", (loopInd != optLoopCount ? "(extended) " : ""));
optPrintLoopInfo(optLoopCount, // Not necessarily the loop index, but the number of loops that have been added.
- loop.lpHead, loop.lpFirst, loop.lpTop, loop.lpEntry, loop.lpBottom, loop.lpExitCnt, loop.lpExit);
+ loop.lpHead, loop.lpTop, loop.lpEntry, loop.lpBottom, loop.lpExitCnt, loop.lpExit);
// If an iterator loop print the iterator and the initialization.
if (loop.lpFlags & LPFLG_ITER)
@@ -1341,14 +1331,13 @@ namespace
{
//------------------------------------------------------------------------
// LoopSearch: Class that handles scanning a range of blocks to detect a loop,
-// moving blocks to make the loop body contiguous, and recording
-// the loop.
+// moving blocks to make the loop body contiguous, and recording the loop.
//
// We will use the following terminology:
// HEAD - the basic block that flows into the loop ENTRY block (Currently MUST be lexically before entry).
// Not part of the looping of the loop.
-// FIRST - the lexically first basic block (in bbNext order) within this loop.
-// TOP - the target of the backward edge from BOTTOM. In most cases FIRST and TOP are the same.
+// TOP - the target of the backward edge from BOTTOM, and the lexically first basic block (in bbNext order)
+// within this loop.
// BOTTOM - the lexically last block in the loop (i.e. the block from which we jump to the top)
// EXIT - the predecessor of loop's unique exit edge, if it has a unique exit edge; else nullptr
// ENTRY - the entry in the loop (not necessarly the TOP), but there must be only one entry
@@ -1359,14 +1348,14 @@ namespace
// between TOP and BOTTOM as part of the loop even if they aren't part of the SCC.
// Regarding nesting: Since a given block can only have one back-edge (we only detect loops with back-edges
// from BBJ_COND or BBJ_ALWAYS blocks), no two loops will share the same BOTTOM. Two loops may share the
-// same FIRST/TOP/ENTRY as reported by LoopSearch, and optCanonicalizeLoopNest will subsequently re-write
-// the CFG so that no two loops share the same FIRST/TOP/ENTRY anymore.
+// same TOP/ENTRY as reported by LoopSearch, and optCanonicalizeLoopNest will subsequently re-write
+// the CFG so that no two loops share the same TOP/ENTRY anymore.
//
// |
// v
// head
// |
-// | top/first <--+
+// | top <--+
// | | |
// | ... |
// | | |
@@ -1450,7 +1439,10 @@ class LoopSearch
{
return BlockSetOps::IsMember(comp, newBlocksInLoop, blockNum - oldBlockMaxNum);
}
- return BlockSetOps::IsMember(comp, oldBlocksInLoop, blockNum);
+ else
+ {
+ return BlockSetOps::IsMember(comp, oldBlocksInLoop, blockNum);
+ }
}
void Insert(unsigned int blockNum)
@@ -1493,7 +1485,6 @@ class LoopSearch
// See LoopSearch class comment header for a diagram relating these fields:
BasicBlock* head; // Predecessor of unique entry edge
- BasicBlock* first; // Lexically first in-loop block
BasicBlock* top; // Successor of back-edge from BOTTOM
BasicBlock* bottom; // Predecessor of back-edge to TOP, also lexically last in-loop block
BasicBlock* entry; // Successor of unique entry edge
@@ -1524,12 +1515,12 @@ class LoopSearch
//
bool RecordLoop()
{
- /* At this point we have a compact loop - record it in the loop table
- * If we found only one exit, record it in the table too
- * (otherwise an exit = nullptr in the loop table means multiple exits) */
+ // At this point we have a compact loop - record it in the loop table.
+ // If we found only one exit, record it in the table too
+ // (otherwise an exit = nullptr in the loop table means multiple exits).
BasicBlock* onlyExit = (exitCount == 1 ? lastExit : nullptr);
- if (comp->optRecordLoop(head, first, top, entry, bottom, onlyExit, exitCount))
+ if (comp->optRecordLoop(head, top, entry, bottom, onlyExit, exitCount))
{
// Record the BOTTOM block for future reference before returning.
assert(bottom->bbNum <= oldBlockMaxNum);
@@ -1573,12 +1564,11 @@ class LoopSearch
//
bool FindLoop(BasicBlock* head, BasicBlock* top, BasicBlock* bottom)
{
- /* Is this a loop candidate? - We look for "back edges", i.e. an edge from BOTTOM
- * to TOP (note that this is an abuse of notation since this is not necessarily a back edge
- * as the definition says, but merely an indication that we have a loop there).
- * Thus, we have to be very careful and after entry discovery check that it is indeed
- * the only place we enter the loop (especially for non-reducible flow graphs).
- */
+ // Is this a loop candidate? - We look for "back edges", i.e. an edge from BOTTOM
+ // to TOP (note that this is an abuse of notation since this is not necessarily a back edge
+ // as the definition says, but merely an indication that we have a loop there).
+ // Thus, we have to be very careful and after entry discovery check that it is indeed
+ // the only place we enter the loop (especially for non-reducible flow graphs).
if (top->bbNum > bottom->bbNum) // is this a backward edge? (from BOTTOM to TOP)
{
@@ -1597,21 +1587,20 @@ class LoopSearch
(bottom->bbJumpKind == BBJ_EHCATCHRET) || (bottom->bbJumpKind == BBJ_CALLFINALLY) ||
(bottom->bbJumpKind == BBJ_SWITCH))
{
- /* BBJ_EHFINALLYRET, BBJ_EHFILTERRET, BBJ_EHCATCHRET, and BBJ_CALLFINALLY can never form a loop.
- * BBJ_SWITCH that has a backward jump appears only for labeled break. */
+ // BBJ_EHFINALLYRET, BBJ_EHFILTERRET, BBJ_EHCATCHRET, and BBJ_CALLFINALLY can never form a loop.
+ // BBJ_SWITCH that has a backward jump appears only for labeled break.
return false;
}
- /* The presence of a "back edge" is an indication that a loop might be present here
- *
- * LOOP:
- * 1. A collection of STRONGLY CONNECTED nodes i.e. there is a path from any
- * node in the loop to any other node in the loop (wholly within the loop)
- * 2. The loop has a unique ENTRY, i.e. there is only one way to reach a node
- * in the loop from outside the loop, and that is through the ENTRY
- */
+ // The presence of a "back edge" is an indication that a loop might be present here.
+ //
+ // Definition: A loop is:
+ // 1. A collection of STRONGLY CONNECTED nodes i.e. there is a path from any
+ // node in the loop to any other node in the loop (wholly within the loop)
+ // 2. The loop has a unique ENTRY, i.e. there is only one way to reach a node
+ // in the loop from outside the loop, and that is through the ENTRY
- /* Let's find the loop ENTRY */
+ // Let's find the loop ENTRY
BasicBlock* entry = FindEntry(head, top, bottom);
if (entry == nullptr)
@@ -1628,10 +1617,6 @@ class LoopSearch
this->lastExit = nullptr;
this->exitCount = 0;
- // Now we find the "first" block -- the earliest block reachable within the loop.
- // With our current algorithm, this is always the same as "top".
- this->first = top;
-
if (!HasSingleEntryCycle())
{
// There isn't actually a loop between TOP and BOTTOM
@@ -1662,27 +1647,25 @@ class LoopSearch
//
// Here, BB10 is more nested than BB02.
- if (bottom->hasTryIndex() && !comp->bbInTryRegions(bottom->getTryIndex(), first))
+ if (bottom->hasTryIndex() && !comp->bbInTryRegions(bottom->getTryIndex(), top))
{
- JITDUMP("Loop 'first' " FMT_BB " is in an outer EH region compared to loop 'bottom' " FMT_BB ". Rejecting "
+ JITDUMP("Loop 'top' " FMT_BB " is in an outer EH region compared to loop 'bottom' " FMT_BB ". Rejecting "
"loop.\n",
- first->bbNum, bottom->bbNum);
+ top->bbNum, bottom->bbNum);
return false;
}
#if defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
// Disqualify loops where the first block of the loop is a finally target.
- // The main problem is when multiple loops share a 'first' block that is a finally
+ // The main problem is when multiple loops share a 'top' block that is a finally
// target and we canonicalize the loops by adding a new loop head. In that case, we
// need to update the blocks so the finally target bit is moved to the newly created
- // block, and removed from the old 'first' block. This is 'hard', so at this point
- // in the RyuJIT codebase (when we don't expect to keep the "old" ARM32 code generator
- // long-term), it's easier to disallow the loop than to update the flow graph to
- // support this case.
+ // block, and removed from the old 'top' block. This is 'hard', so it's easier to disallow
+ // the loop than to update the flow graph to support this case.
- if ((first->bbFlags & BBF_FINALLY_TARGET) != 0)
+ if ((top->bbFlags & BBF_FINALLY_TARGET) != 0)
{
- JITDUMP("Loop 'first' " FMT_BB " is a finally target. Rejecting loop.\n", first->bbNum);
+ JITDUMP("Loop 'top' " FMT_BB " is a finally target. Rejecting loop.\n", top->bbNum);
return false;
}
#endif // defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
@@ -1729,18 +1712,16 @@ class LoopSearch
{
if (head->bbJumpDest->bbNum <= bottom->bbNum && head->bbJumpDest->bbNum >= top->bbNum)
{
- /* OK - we enter somewhere within the loop */
-
- /* some useful asserts
- * Cannot enter at the top - should have being caught by redundant jumps */
+ // OK - we enter somewhere within the loop.
+ // Cannot enter at the top - should have being caught by redundant jumps
assert((head->bbJumpDest != top) || (head->bbFlags & BBF_KEEP_BBJ_ALWAYS));
return head->bbJumpDest;
}
else
{
- /* special case - don't consider now */
+ // special case - don't consider now
// assert (!"Loop entered in weird way!");
return nullptr;
}
@@ -1748,12 +1729,12 @@ class LoopSearch
// Can we fall through into the loop?
else if (head->bbJumpKind == BBJ_NONE || head->bbJumpKind == BBJ_COND)
{
- /* The ENTRY is at the TOP (a do-while loop) */
+ // The ENTRY is at the TOP (a do-while loop)
return top;
}
else
{
- return nullptr; // head does not flow into the loop bail for now
+ return nullptr; // HEAD does not flow into the loop; bail for now
}
}
@@ -1767,8 +1748,7 @@ class LoopSearch
// false - Did not find a single-entry cycle.
//
// Notes:
- // Will mark (in `loopBlocks`) all blocks found to participate in the
- // cycle.
+ // Will mark (in `loopBlocks`) all blocks found to participate in the cycle.
//
bool HasSingleEntryCycle()
{
@@ -1785,9 +1765,7 @@ class LoopSearch
BasicBlock* block = worklist.back();
worklist.pop_back();
- /* Make sure ENTRY dominates all blocks in the loop
- * This is necessary to ensure condition 2. above
- */
+ // Make sure ENTRY dominates all blocks in the loop.
if (block->bbNum > oldBlockMaxNum)
{
// This is a new block we added to connect fall-through, so the
@@ -2212,7 +2190,7 @@ class LoopSearch
if ((block->bbJumpKind == BBJ_COND) && (block->bbJumpDest == newNext))
{
- /* Reverse the jump condition */
+ // Reverse the jump condition
GenTree* test = block->lastNode();
noway_assert(test->OperIsConditionalJump());
@@ -2280,7 +2258,7 @@ class LoopSearch
if (!loopBlocks.IsMember(exitPoint->bbNum))
{
- /* exit from a block other than BOTTOM */
+ // Exit from a block other than BOTTOM
lastExit = block;
exitCount++;
}
@@ -2291,13 +2269,13 @@ class LoopSearch
case BBJ_EHFINALLYRET:
case BBJ_EHFILTERRET:
- /* The "try" associated with this "finally" must be in the
- * same loop, so the finally block will return control inside the loop */
+ // The "try" associated with this "finally" must be in the same loop, so the
+ // finally block will return control inside the loop.
break;
case BBJ_THROW:
case BBJ_RETURN:
- /* those are exits from the loop */
+ // Those are exits from the loop
lastExit = block;
exitCount++;
break;
@@ -2326,14 +2304,17 @@ class LoopSearch
}
}
};
-}
-
-/*****************************************************************************
- * Find the natural loops, using dominators. Note that the test for
- * a loop is slightly different from the standard one, because we have
- * not done a depth first reordering of the basic blocks.
- */
+} // end (anonymous) namespace
+//------------------------------------------------------------------------
+// optFindNaturalLoops: Find the natural loops, using dominators. Note that the test for
+// a loop is slightly different from the standard one, because we have not done a depth
+// first reordering of the basic blocks.
+//
+// See LoopSearch class comment header for a description of the loops found.
+//
+// We will find and record a maximum of BasicBlock::MAX_LOOP_NUM loops (currently 64).
+//
void Compiler::optFindNaturalLoops()
{
#ifdef DEBUG
@@ -2358,9 +2339,7 @@ void Compiler::optFindNaturalLoops()
{
BasicBlock* top = head->bbNext;
- // Blocks that are rarely run have a zero bbWeight and should
- // never be optimized here
-
+ // Blocks that are rarely run have a zero bbWeight and should never be optimized here.
if (top->bbWeight == BB_ZERO_WEIGHT)
{
continue;
@@ -2378,16 +2357,16 @@ void Compiler::optFindNaturalLoops()
#if COUNT_LOOPS
if (!hasMethodLoops)
{
- /* mark the method as containing natural loops */
+ // Mark the method as containing natural loops
totalLoopMethods++;
hasMethodLoops = true;
}
- /* increment total number of loops found */
+ // Increment total number of loops found
totalLoopCount++;
loopsThisMethod++;
- /* keep track of the number of exits */
+ // Keep track of the number of exits
loopExitCountTable.record(static_cast(search.GetExitCount()));
// Note that we continue to look for loops even if
@@ -2437,13 +2416,13 @@ void Compiler::optFindNaturalLoops()
{
// Need to renumber blocks now since loop canonicalization
// depends on it; can defer the rest of fgUpdateChangedFlowGraph()
- // until after canonicalizing loops. Dominator information is
+ // until after canonicalizing loops. Dominator information is
// recorded in terms of block numbers, so flag it invalid.
fgDomsComputed = false;
fgRenumberBlocks();
}
- // Now the loop indices are stable. We can figure out parent/child relationships
+ // Now the loop indices are stable. We can figure out parent/child relationships
// (using table indices to name loops), and label blocks.
for (unsigned char loopInd = 1; loopInd < optLoopCount; loopInd++)
{
@@ -2460,9 +2439,10 @@ void Compiler::optFindNaturalLoops()
}
}
- // Now label the blocks with the innermost loop to which they belong. Since parents
+ // Now label the blocks with the innermost loop to which they belong. Since parents
// precede children in the table, doing the labeling for each loop in order will achieve
- // this -- the innermost loop labeling will be done last.
+ // this -- the innermost loop labeling will be done last. (Inner loop blocks will be
+ // labeled multiple times before being correct at the end.)
for (unsigned char loopInd = 0; loopInd < optLoopCount; loopInd++)
{
for (BasicBlock* const blk : optLoopTable[loopInd].LoopBlocks())
@@ -2494,7 +2474,7 @@ void Compiler::optFindNaturalLoops()
}
#ifdef DEBUG
- if (verbose && optLoopCount > 0)
+ if (verbose && (optLoopCount > 0))
{
printf("\nFinal natural loop table:\n");
for (unsigned loopInd = 0; loopInd < optLoopCount; loopInd++)
@@ -2506,34 +2486,43 @@ void Compiler::optFindNaturalLoops()
#endif // DEBUG
}
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------
+// optIdentifyLoopsForAlignment: Determine which loops should be considered for alignment.
//
-// All the inner loops that whose block weight meets a threshold are marked
-// as needing alignment.
+// All innermost loops whose block weight meets a threshold are candidates for alignment.
+// The `first` block of the loop is marked with the BBF_LOOP_ALIGN flag to indicate this
+// (the loop table itself is not changed).
+//
+// Depends on the loop table, and on block weights being set.
//
-
void Compiler::optIdentifyLoopsForAlignment()
{
#if FEATURE_LOOP_ALIGN
if (codeGen->ShouldAlignLoops())
{
- for (unsigned char loopInd = 0; loopInd < optLoopCount; loopInd++)
+ for (BasicBlock::loopNumber loopInd = 0; loopInd < optLoopCount; loopInd++)
{
- BasicBlock* first = optLoopTable[loopInd].lpFirst;
-
// An innerloop candidate that might need alignment
if (optLoopTable[loopInd].lpChild == BasicBlock::NOT_IN_LOOP)
{
- if (first->getBBWeight(this) >= (opts.compJitAlignLoopMinBlockWeight * BB_UNITY_WEIGHT))
+ BasicBlock* top = optLoopTable[loopInd].lpTop;
+ weight_t topWeight = top->getBBWeight(this);
+ if (topWeight >= (opts.compJitAlignLoopMinBlockWeight * BB_UNITY_WEIGHT))
{
- first->bbFlags |= BBF_LOOP_ALIGN;
- JITDUMP(FMT_LP " that starts at " FMT_BB " needs alignment, weight=" FMT_WT ".\n", loopInd,
- first->bbNum, first->getBBWeight(this));
+ // Sometimes with JitOptRepeat > 1, we might end up finding the loops twice. In such
+ // cases, make sure to count them just once.
+ if (!top->isLoopAlign())
+ {
+ loopAlignCandidates++;
+ top->bbFlags |= BBF_LOOP_ALIGN;
+ JITDUMP(FMT_LP " that starts at " FMT_BB " needs alignment, weight=" FMT_WT ".\n", loopInd,
+ top->bbNum, top->getBBWeight(this));
+ }
}
else
{
JITDUMP("Skip alignment for " FMT_LP " that starts at " FMT_BB " weight=" FMT_WT ".\n", loopInd,
- first->bbNum, first->getBBWeight(this));
+ top->bbNum, topWeight);
}
}
}
@@ -2649,7 +2638,11 @@ bool Compiler::optIsLoopEntry(BasicBlock* block) const
{
for (unsigned char loopInd = 0; loopInd < optLoopCount; loopInd++)
{
- // Traverse the outermost loops as entries into the loop nest; so skip non-outermost.
+ if ((optLoopTable[loopInd].lpFlags & LPFLG_REMOVED) != 0)
+ {
+ continue;
+ }
+
if (optLoopTable[loopInd].lpEntry == block)
{
return true;
@@ -2664,7 +2657,7 @@ bool Compiler::optCanonicalizeLoopNest(unsigned char loopInd)
{
bool modified = false;
- // Is the top of the current loop not in any nested loop?
+ // Is the top of the current loop in any nested loop?
if (optLoopTable[loopInd].lpTop->bbNatLoopNum != loopInd)
{
if (optCanonicalizeLoop(loopInd))
@@ -2673,8 +2666,9 @@ bool Compiler::optCanonicalizeLoopNest(unsigned char loopInd)
}
}
- for (unsigned char child = optLoopTable[loopInd].lpChild; child != BasicBlock::NOT_IN_LOOP;
- child = optLoopTable[child].lpSibling)
+ for (unsigned char child = optLoopTable[loopInd].lpChild; //
+ child != BasicBlock::NOT_IN_LOOP; //
+ child = optLoopTable[child].lpSibling)
{
if (optCanonicalizeLoopNest(child))
{
@@ -2702,7 +2696,7 @@ bool Compiler::optCanonicalizeLoop(unsigned char loopInd)
// Otherwise, the top of this loop is also part of a nested loop.
//
// Insert a new unique top for this loop. We must be careful to put this new
- // block in the correct EH region. Note that f->bbPrev might be in a different
+ // block in the correct EH region. Note that t->bbPrev might be in a different
// EH region. For example:
//
// try {
@@ -2777,16 +2771,15 @@ bool Compiler::optCanonicalizeLoop(unsigned char loopInd)
// simplify things, we disqualify this type of loop, so we should never see this here.
BasicBlock* h = optLoopTable[loopInd].lpHead;
- BasicBlock* f = optLoopTable[loopInd].lpFirst;
BasicBlock* b = optLoopTable[loopInd].lpBottom;
// The loop must be entirely contained within a single handler region.
- assert(BasicBlock::sameHndRegion(f, b));
+ assert(BasicBlock::sameHndRegion(t, b));
// If the bottom block is in the same "try" region, then we extend the EH
// region. Otherwise, we add the new block outside the "try" region.
- bool extendRegion = BasicBlock::sameTryRegion(f, b);
- BasicBlock* newT = fgNewBBbefore(BBJ_NONE, f, extendRegion);
+ const bool extendRegion = BasicBlock::sameTryRegion(t, b);
+ BasicBlock* newT = fgNewBBbefore(BBJ_NONE, t, extendRegion);
if (!extendRegion)
{
// We need to set the EH region manually. Set it to be the same
@@ -2800,7 +2793,7 @@ bool Compiler::optCanonicalizeLoop(unsigned char loopInd)
// a call to fgUpdateChangedFlowGraph which will recompute the reachability sets anyway.
// Redirect the "bottom" of the current loop to "newT".
- BlockToBlockMap* blockMap = new (getAllocatorLoopHoist()) BlockToBlockMap(getAllocatorLoopHoist());
+ BlockToBlockMap* blockMap = new (getAllocator(CMK_LoopOpt)) BlockToBlockMap(getAllocator(CMK_LoopOpt));
blockMap->Set(t, newT);
optRedirectBlock(b, blockMap);
@@ -2857,14 +2850,7 @@ bool Compiler::optCanonicalizeLoop(unsigned char loopInd)
}
}
- assert(newT->bbNext == f);
- if (f != t)
- {
- newT->bbJumpKind = BBJ_ALWAYS;
- newT->bbJumpDest = t;
- newT->bbStmtList = nullptr;
- fgInsertStmtAtEnd(newT, fgNewStmtFromTree(gtNewOperNode(GT_NOP, TYP_VOID, nullptr)));
- }
+ assert(newT->bbNext == t);
// If it had been a do-while loop (top == entry), update entry, as well.
BasicBlock* origE = optLoopTable[loopInd].lpEntry;
@@ -2872,8 +2858,7 @@ bool Compiler::optCanonicalizeLoop(unsigned char loopInd)
{
optLoopTable[loopInd].lpEntry = newT;
}
- optLoopTable[loopInd].lpTop = newT;
- optLoopTable[loopInd].lpFirst = newT;
+ optLoopTable[loopInd].lpTop = newT;
newT->bbNatLoopNum = loopInd;
@@ -2898,8 +2883,9 @@ bool Compiler::optCanonicalizeLoop(unsigned char loopInd)
// If any loops nested in "loopInd" have the same head and entry as "loopInd",
// it must be the case that they were do-while's (since "h" fell through to the entry).
// The new node "newT" becomes the head of such loops.
- for (unsigned char childLoop = optLoopTable[loopInd].lpChild; childLoop != BasicBlock::NOT_IN_LOOP;
- childLoop = optLoopTable[childLoop].lpSibling)
+ for (unsigned char childLoop = optLoopTable[loopInd].lpChild; //
+ childLoop != BasicBlock::NOT_IN_LOOP; //
+ childLoop = optLoopTable[childLoop].lpSibling)
{
if (optLoopTable[childLoop].lpEntry == origE && optLoopTable[childLoop].lpHead == h &&
newT->bbJumpKind == BBJ_NONE && newT->bbNext == origE)
@@ -3792,11 +3778,7 @@ PhaseStatus Compiler::optUnrollLoops()
#if FEATURE_LOOP_ALIGN
for (block = head->bbNext;; block = block->bbNext)
{
- if (block->isLoopAlign())
- {
- block->bbFlags &= ~BBF_LOOP_ALIGN;
- JITDUMP("Removing LOOP_ALIGN flag from unrolled loop in " FMT_BB "\n", block->bbNum);
- }
+ block->unmarkLoopAlign(this DEBUG_ARG("Unrolled loop"));
if (block == bottom)
{
@@ -4399,7 +4381,7 @@ bool Compiler::optInvertWhileLoop(BasicBlock* block)
if (opts.compDbgInfo)
{
- clonedStmt->SetILOffsetX(stmt->GetILOffsetX());
+ clonedStmt->SetDebugInfo(stmt->GetDebugInfo());
}
clonedStmt->SetCompilerAdded();
@@ -4627,16 +4609,77 @@ PhaseStatus Compiler::optOptimizeLayout()
return madeChanges ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING;
}
+//------------------------------------------------------------------------
+// optMarkLoopHeads: Mark all potential loop heads as BBF_LOOP_HEAD. A potential loop head is a block
+// targeted by a lexical back edge, where the source of the back edge is reachable from the block.
+// Note that if there are no lexical back edges, there can't be any loops.
+//
+// If there are any potential loop heads, set `fgHasLoops` to `true`.
+//
+// Assumptions:
+// The reachability sets must be computed and valid.
+//
+void Compiler::optMarkLoopHeads()
+{
+ assert(!fgCheapPredsValid);
+ assert(fgReachabilitySetsValid);
+
+#ifdef DEBUG
+ if (verbose)
+ {
+ printf("*************** In optMarkLoopHeads()\n");
+ }
+#endif
+
+ bool hasLoops = false;
+
+ for (BasicBlock* const block : Blocks())
+ {
+ // Set BBF_LOOP_HEAD if we have backwards branches to this block.
+
+ unsigned blockNum = block->bbNum;
+ for (BasicBlock* const predBlock : block->PredBlocks())
+ {
+ if (blockNum <= predBlock->bbNum)
+ {
+ if (predBlock->bbJumpKind == BBJ_CALLFINALLY)
+ {
+ // Loops never have BBJ_CALLFINALLY as the source of their "back edge".
+ continue;
+ }
+
+ // If block can reach predBlock then we have a loop head
+ if (BlockSetOps::IsMember(this, predBlock->bbReach, blockNum))
+ {
+ hasLoops = true;
+ block->bbFlags |= BBF_LOOP_HEAD;
+ break; // No need to look at more `block` predecessors
+ }
+ }
+ }
+ }
+
+ fgHasLoops = hasLoops;
+}
+
//-----------------------------------------------------------------------------
-// optFindLoops: find and classify natural loops
+// optFindLoops: find loops in the function.
+//
+// The JIT recognizes two types of loops in a function: natural loops and "general" (or "unnatural") loops.
+// Natural loops are those which get added to the loop table. Most downstream optimizations require
+// using natural loops. See `optFindNaturalLoops` for a definition of the criteria for recognizing a natural loop.
+// A general loop is defined as a lexical (program order) range of blocks where a later block branches to an
+// earlier block (that is, there is a back edge in the flow graph), and the later block is reachable from the earlier
+// block. General loops are used for weighting flow graph blocks (when there is no block profile data), as well as
+// for determining if we require fully interruptible GC information.
//
// Notes:
-// Also (re)sets all non-IBC block weights, and marks loops potentially needing
-// alignment padding.
+// Also (re)sets all non-IBC block weights, and marks loops potentially needing alignment padding.
//
PhaseStatus Compiler::optFindLoops()
{
noway_assert(opts.OptimizationEnabled());
+ assert(fgDomsComputed);
#ifdef DEBUG
if (verbose)
@@ -4645,50 +4688,46 @@ PhaseStatus Compiler::optFindLoops()
}
#endif
+ optMarkLoopHeads();
+
optSetBlockWeights();
/* Were there any loops in the flow graph? */
if (fgHasLoops)
{
- /* now that we have dominator information we can find loops */
-
optFindNaturalLoops();
- unsigned loopNum = 0;
+ // Now find the general loops and scale block weights.
- /* Iterate over the flow graph, marking all loops */
+ unsigned generalLoopCount = 0;
- /* We will use the following terminology:
- * top - the first basic block in the loop (i.e. the head of the backward edge)
- * bottom - the last block in the loop (i.e. the block from which we jump to the top)
- * lastBottom - used when we have multiple back-edges to the same top
- */
+ // We will use the following terminology:
+ // top - the first basic block in the loop (i.e. the head of the backward edge)
+ // bottom - the last block in the loop (i.e. the block from which we jump to the top)
+ // lastBottom - used when we have multiple back edges to the same top
for (BasicBlock* const top : Blocks())
{
+ // Only consider `top` blocks already determined to be potential loop heads.
+ if (!top->isLoopHead())
+ {
+ continue;
+ }
+
BasicBlock* foundBottom = nullptr;
for (BasicBlock* const bottom : top->PredBlocks())
{
- /* Is this a loop candidate? - We look for "back edges" */
-
- /* is this a backward edge? (from BOTTOM to TOP) */
+ // Is this a loop candidate? - We look for "back edges"
+ // Is this a backward edge? (from BOTTOM to TOP)
if (top->bbNum > bottom->bbNum)
{
continue;
}
- /* 'top' also must have the BBF_LOOP_HEAD flag set */
-
- if (top->isLoopHead() == false)
- {
- continue;
- }
-
- /* We only consider back-edges that are BBJ_COND or BBJ_ALWAYS for loops */
-
+ // We only consider back-edges that are BBJ_COND or BBJ_ALWAYS for loops.
if ((bottom->bbJumpKind != BBJ_COND) && (bottom->bbJumpKind != BBJ_ALWAYS))
{
continue;
@@ -4710,15 +4749,15 @@ PhaseStatus Compiler::optFindLoops()
if (foundBottom)
{
- loopNum++;
+ generalLoopCount++;
/* Mark all blocks between 'top' and 'bottom' */
- optMarkLoopBlocks(top, foundBottom, false);
+ optScaleLoopBlocks(top, foundBottom);
}
// We track at most 255 loops
- if (loopNum == 255)
+ if (generalLoopCount == 255)
{
#if COUNT_LOOPS
totalUnnatLoopOverflows++;
@@ -4727,32 +4766,21 @@ PhaseStatus Compiler::optFindLoops()
}
}
- // Check if any of the loops need alignment
-
- JITDUMP("\n");
- optIdentifyLoopsForAlignment();
+ JITDUMP("\nFound a total of %d general loops.\n", generalLoopCount);
#if COUNT_LOOPS
- totalUnnatLoopCount += loopNum;
+ totalUnnatLoopCount += generalLoopCount;
#endif
-#ifdef DEBUG
- if (verbose)
- {
- if (loopNum > 0)
- {
- printf("\nFound a total of %d loops.", loopNum);
- printf("\nAfter loop weight marking:\n");
- fgDispBasicBlocks();
- printf("\n");
- }
- }
+ // Check if any of the loops need alignment
+ optIdentifyLoopsForAlignment();
+ }
- fgDebugCheckLoopTable();
+#ifdef DEBUG
+ fgDebugCheckLoopTable();
#endif
- optLoopsMarked = true;
- }
+ optLoopsMarked = true;
return PhaseStatus::MODIFIED_EVERYTHING;
}
@@ -5482,8 +5510,8 @@ void Compiler::optPerformHoistExpr(GenTree* origExpr, BasicBlock* exprBb, unsign
{
printf("\nHoisting a copy of ");
printTreeID(origExpr);
- printf(" into PreHeader for loop " FMT_LP " <" FMT_BB ".." FMT_BB ">:\n", lnum,
- optLoopTable[lnum].lpFirst->bbNum, optLoopTable[lnum].lpBottom->bbNum);
+ printf(" into PreHeader for loop " FMT_LP " <" FMT_BB ".." FMT_BB ">:\n", lnum, optLoopTable[lnum].lpTop->bbNum,
+ optLoopTable[lnum].lpBottom->bbNum);
gtDispTree(origExpr);
printf("\n");
}
@@ -6752,7 +6780,7 @@ void Compiler::optHoistLoopBlocks(unsigned loopNum, ArrayStack* blo
JITDUMP("\n optHoistLoopBlocks " FMT_BB " (weight=%6s) of loop " FMT_LP " <" FMT_BB ".." FMT_BB
">, firstBlock is %s\n",
- block->bbNum, refCntWtd2str(blockWeight), loopNum, loopDsc->lpFirst->bbNum, loopDsc->lpBottom->bbNum,
+ block->bbNum, refCntWtd2str(blockWeight), loopNum, loopDsc->lpTop->bbNum, loopDsc->lpBottom->bbNum,
dspBool(block == loopDsc->lpEntry));
if (blockWeight < (BB_UNITY_WEIGHT / 10))
@@ -7243,7 +7271,7 @@ void Compiler::optComputeLoopSideEffects()
for (unsigned i = 0; i < lvaCount; i++)
{
- LclVarDsc* varDsc = &lvaTable[i];
+ LclVarDsc* varDsc = lvaGetDesc(i);
if (varDsc->lvTracked)
{
if (varTypeIsFloating(varDsc->lvType))
@@ -7511,6 +7539,15 @@ bool Compiler::optComputeLoopSideEffectsOfBlock(BasicBlock* blk)
}
break;
+#ifdef FEATURE_HW_INTRINSICS
+ case GT_HWINTRINSIC:
+ if (tree->AsHWIntrinsic()->OperIsMemoryStore())
+ {
+ memoryHavoc |= memoryKindSet(GcHeap, ByrefExposed);
+ }
+ break;
+#endif // FEATURE_HW_INTRINSICS
+
case GT_LOCKADD:
case GT_XORR:
case GT_XAND:
@@ -7519,7 +7556,6 @@ bool Compiler::optComputeLoopSideEffectsOfBlock(BasicBlock* blk)
case GT_CMPXCHG:
case GT_MEMORYBARRIER:
{
- assert(!tree->OperIs(GT_LOCKADD) && "LOCKADD should not appear before lowering");
memoryHavoc |= memoryKindSet(GcHeap, ByrefExposed);
}
break;
@@ -7559,6 +7595,7 @@ bool Compiler::optComputeLoopSideEffectsOfBlock(BasicBlock* blk)
default:
// All other gtOper node kinds, leave 'memoryHavoc' unchanged (i.e. false)
+ assert(!tree->OperRequiresAsgFlag());
break;
}
}
@@ -7583,10 +7620,9 @@ void Compiler::AddContainsCallAllContainingLoops(unsigned lnum)
// alignment
if (optLoopTable[lnum].lpChild == BasicBlock::NOT_IN_LOOP)
{
- BasicBlock* first = optLoopTable[lnum].lpFirst;
- first->bbFlags &= ~BBF_LOOP_ALIGN;
- JITDUMP("Removing LOOP_ALIGN flag for " FMT_LP " that starts at " FMT_BB " because loop has a call.\n", lnum,
- first->bbNum);
+ BasicBlock* top = optLoopTable[lnum].lpTop;
+
+ top->unmarkLoopAlign(this DEBUG_ARG("Loop with call"));
}
#endif
diff --git a/src/coreclr/jit/patchpoint.cpp b/src/coreclr/jit/patchpoint.cpp
index 60308075db7801..15a37150326183 100644
--- a/src/coreclr/jit/patchpoint.cpp
+++ b/src/coreclr/jit/patchpoint.cpp
@@ -26,7 +26,6 @@
//
// * no patchpoints in handler regions
// * no patchpoints for localloc methods
-// * no patchpoints for synchronized methods (workaround)
//
class PatchpointTransformer
{
@@ -57,35 +56,33 @@ class PatchpointTransformer
{
if (block->bbFlags & BBF_PATCHPOINT)
{
+ // We can't OSR from funclets.
+ //
+ assert(!block->hasHndIndex());
+
// Clear the patchpoint flag.
//
block->bbFlags &= ~BBF_PATCHPOINT;
- // If block is in a handler region, don't insert a patchpoint.
- // We can't OSR from funclets.
- //
- // TODO: check this earlier, somehow, and fall back to fully
- // optimizing the method (ala QJFL=0).
- if (compiler->ehGetBlockHndDsc(block) != nullptr)
- {
- JITDUMP("Patchpoint: skipping patchpoint for " FMT_BB " as it is in a handler\n", block->bbNum);
- continue;
- }
-
- JITDUMP("Patchpoint: loop patchpoint in " FMT_BB "\n", block->bbNum);
- assert(block != compiler->fgFirstBB);
+ JITDUMP("Patchpoint: regular patchpoint in " FMT_BB "\n", block->bbNum);
TransformBlock(block);
count++;
}
else if (block->bbFlags & BBF_PARTIAL_COMPILATION_PATCHPOINT)
{
- if (compiler->ehGetBlockHndDsc(block) != nullptr)
- {
- JITDUMP("Patchpoint: skipping partial compilation patchpoint for " FMT_BB
- " as it is in a handler\n",
- block->bbNum);
- continue;
- }
+ // We can't OSR from funclets.
+ // Also, we don't import the IL for these blocks.
+ //
+ assert(!block->hasHndIndex());
+
+ // If we're instrumenting, we should not have decided to
+ // put class probes here, as that is driven by looking at IL.
+ //
+ assert((block->bbFlags & BBF_HAS_CLASS_PROFILE) == 0);
+
+ // Clear the partial comp flag.
+ //
+ block->bbFlags &= ~BBF_PARTIAL_COMPILATION_PATCHPOINT;
JITDUMP("Patchpoint: partial compilation patchpoint in " FMT_BB "\n", block->bbNum);
TransformPartialCompilation(block);
@@ -273,34 +270,8 @@ PhaseStatus Compiler::fgTransformPatchpoints()
// We should only be adding patchpoints at Tier0, so should not be in an inlinee
assert(!compIsForInlining());
- // We currently can't do OSR in methods with localloc.
- // Such methods don't have a fixed relationship between frame and stack pointers.
- //
- // This is true whether or not the localloc was executed in the original method.
- //
- // TODO: handle this case, or else check this earlier and fall back to fully
- // optimizing the method (ala QJFL=0).
- if (compLocallocUsed)
- {
- JITDUMP("\n -- unable to handle methods with localloc\n");
- return PhaseStatus::MODIFIED_NOTHING;
- }
-
- // We currently can't do OSR in synchronized methods. We need to alter
- // the logic in fgAddSyncMethodEnterExit for OSR to not try and obtain the
- // monitor (since the original method will have done so) and set the monitor
- // obtained flag to true (or reuse the original method slot value).
- if ((info.compFlags & CORINFO_FLG_SYNCH) != 0)
- {
- JITDUMP("\n -- unable to handle synchronized methods\n");
- return PhaseStatus::MODIFIED_NOTHING;
- }
-
- if (opts.IsReversePInvoke())
- {
- JITDUMP(" -- unable to handle Reverse P/Invoke\n");
- return PhaseStatus::MODIFIED_NOTHING;
- }
+ // We should be allowed to have patchpoints in this method.
+ assert(compCanHavePatchpoints());
PatchpointTransformer ppTransformer(this);
int count = ppTransformer.Run();
diff --git a/src/coreclr/jit/rationalize.cpp b/src/coreclr/jit/rationalize.cpp
index 9887153d54d47e..aae9f1bca07ace 100644
--- a/src/coreclr/jit/rationalize.cpp
+++ b/src/coreclr/jit/rationalize.cpp
@@ -77,8 +77,7 @@ void Rationalizer::RewriteIndir(LIR::Use& use)
addr->gtGetOp2()->IsIntegralConst(0))
{
GenTreeLclVarCommon* lclVarNode = addr->gtGetOp1()->AsLclVarCommon();
- unsigned lclNum = lclVarNode->GetLclNum();
- LclVarDsc* varDsc = comp->lvaTable + lclNum;
+ const LclVarDsc* varDsc = comp->lvaGetDesc(lclVarNode);
if (indir->TypeGet() == varDsc->TypeGet())
{
JITDUMP("Rewriting GT_IND(GT_ADD(LCL_VAR_ADDR,0)) to LCL_VAR\n");
@@ -582,26 +581,9 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, Compiler::Ge
const bool isLateArg = (node->gtFlags & GTF_LATE_ARG) != 0;
#endif
- // First, remove any preceeding list nodes, which are not otherwise visited by the tree walk.
- //
- // NOTE: GT_LIST nodes used by GT_HWINTRINSIC nodes will in fact be visited.
- for (GenTree* prev = node->gtPrev; (prev != nullptr) && prev->OperIs(GT_LIST); prev = node->gtPrev)
- {
- prev->gtFlags &= ~GTF_REVERSE_OPS;
- BlockRange().Remove(prev);
- }
-
- // Now clear the REVERSE_OPS flag on the current node.
+ // Clear the REVERSE_OPS flag on the current node.
node->gtFlags &= ~GTF_REVERSE_OPS;
- // In addition, remove the current node if it is a GT_LIST node that is not an aggregate.
- if (node->OperIs(GT_LIST))
- {
- GenTreeArgList* list = node->AsArgList();
- BlockRange().Remove(list);
- return Compiler::WALK_CONTINUE;
- }
-
LIR::Use use;
if (parentStack.Height() < 2)
{
@@ -754,13 +736,16 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, Compiler::Ge
simdNode->gtType = TYP_SIMD8;
}
// Certain SIMD trees require rationalizing.
- if (simdNode->AsSIMD()->gtSIMDIntrinsicID == SIMDIntrinsicInitArray)
+ if (simdNode->AsSIMD()->GetSIMDIntrinsicId() == SIMDIntrinsicInitArray)
{
// Rewrite this as an explicit load.
JITDUMP("Rewriting GT_SIMD array init as an explicit load:\n");
unsigned int baseTypeSize = genTypeSize(simdNode->GetSimdBaseType());
- GenTree* address = new (comp, GT_LEA) GenTreeAddrMode(TYP_BYREF, simdNode->gtOp1, simdNode->gtOp2,
- baseTypeSize, OFFSETOF__CORINFO_Array__data);
+
+ GenTree* base = simdNode->Op(1);
+ GenTree* index = (simdNode->GetOperandCount() == 2) ? simdNode->Op(2) : nullptr;
+ GenTree* address = new (comp, GT_LEA)
+ GenTreeAddrMode(TYP_BYREF, base, index, baseTypeSize, OFFSETOF__CORINFO_Array__data);
GenTree* ind = comp->gtNewOperNode(GT_IND, simdType, address);
BlockRange().InsertBefore(simdNode, address, ind);
@@ -776,16 +761,15 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, Compiler::Ge
// of a different width. If that assumption changes, we will EITHER have to make these type
// transformations during importation, and plumb the types all the way through the JIT,
// OR add a lot of special handling here.
- GenTree* op1 = simdNode->gtGetOp1();
- if (op1 != nullptr && op1->gtType == TYP_STRUCT)
- {
- op1->gtType = simdType;
- }
- GenTree* op2 = simdNode->gtGetOp2IfPresent();
- if (op2 != nullptr && op2->gtType == TYP_STRUCT)
+ // TODO-Review: the comment above seems outdated. TYP_SIMDs have been "plumbed through" the Jit.
+ // It may be that this code is actually dead.
+ for (GenTree* operand : simdNode->Operands())
{
- op2->gtType = simdType;
+ if (operand->TypeIs(TYP_STRUCT))
+ {
+ operand->ChangeType(simdType);
+ }
}
}
}
@@ -812,8 +796,8 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, Compiler::Ge
#ifdef TARGET_ARM64
// Special case for GetElement/ToScalar because they take Vector64 and return T
// and T can be long or ulong.
- if (!(hwIntrinsicNode->gtHWIntrinsicId == NI_Vector64_GetElement ||
- hwIntrinsicNode->gtHWIntrinsicId == NI_Vector64_ToScalar))
+ if (!((hwIntrinsicNode->GetHWIntrinsicId() == NI_Vector64_GetElement) ||
+ (hwIntrinsicNode->GetHWIntrinsicId() == NI_Vector64_ToScalar)))
#endif
{
// This happens when it is consumed by a GT_RET_EXPR.
@@ -957,13 +941,19 @@ PhaseStatus Rationalizer::DoPhase()
{
BlockRange().InsertAtEnd(LIR::Range(statement->GetTreeList(), statement->GetRootNode()));
- // If this statement has correct offset information, change it into an IL offset
- // node and insert it into the LIR.
- if (statement->GetILOffsetX() != BAD_IL_OFFSET)
+ // If this statement has correct debug information, change it
+ // into a debug info node and insert it into the LIR. Note that
+ // we are currently reporting root info only back to the EE, so
+ // if the leaf debug info is invalid we still attach it.
+ // Note that we would like to have the invariant di.IsValid()
+ // => parent.IsValid() but it is currently not the case for
+ // NEWOBJ IL instructions where the debug info ends up attached
+ // to the allocation instead of the constructor call.
+ DebugInfo di = statement->GetDebugInfo();
+ if (di.IsValid() || di.GetRoot().IsValid())
{
- assert(!statement->IsPhiDefnStmt());
- GenTreeILOffset* ilOffset = new (comp, GT_IL_OFFSET)
- GenTreeILOffset(statement->GetILOffsetX() DEBUGARG(statement->GetLastILOffset()));
+ GenTreeILOffset* ilOffset =
+ new (comp, GT_IL_OFFSET) GenTreeILOffset(di DEBUGARG(statement->GetLastILOffset()));
BlockRange().InsertBefore(statement->GetTreeList(), ilOffset);
}
diff --git a/src/coreclr/jit/redundantbranchopts.cpp b/src/coreclr/jit/redundantbranchopts.cpp
index 97f444943fbeb7..d00de9901da4dc 100644
--- a/src/coreclr/jit/redundantbranchopts.cpp
+++ b/src/coreclr/jit/redundantbranchopts.cpp
@@ -46,6 +46,7 @@ PhaseStatus Compiler::optRedundantBranches()
//
if (block->bbJumpKind == BBJ_COND)
{
+ madeChanges |= m_compiler->optRedundantRelop(block);
madeChanges |= m_compiler->optRedundantBranch(block);
}
}
@@ -98,7 +99,7 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
GenTree* const tree = jumpTree->AsOp()->gtOp1;
- if (!(tree->OperKind() & GTK_RELOP))
+ if (!tree->OperIsCompare())
{
return false;
}
@@ -115,6 +116,11 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
return false;
}
+ const ValueNumStore::VN_RELATION_KIND vnRelations[] = {ValueNumStore::VN_RELATION_KIND::VRK_Same,
+ ValueNumStore::VN_RELATION_KIND::VRK_Reverse,
+ ValueNumStore::VN_RELATION_KIND::VRK_Swap,
+ ValueNumStore::VN_RELATION_KIND::VRK_SwapReverse};
+
while (domBlock != nullptr)
{
// Check the current dominator
@@ -133,21 +139,21 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
//
// Look for an exact match and also try the various swapped/reversed forms.
//
- const ValueNum treeVN = tree->GetVN(VNK_Liberal);
- const ValueNum domCmpVN = domCmpTree->GetVN(VNK_Liberal);
- const ValueNum domCmpSwpVN =
- vnStore->GetRelatedRelop(domCmpVN, ValueNumStore::VN_RELATION_KIND::VRK_Swap);
- const ValueNum domCmpRevVN =
- vnStore->GetRelatedRelop(domCmpVN, ValueNumStore::VN_RELATION_KIND::VRK_Reverse);
- const ValueNum domCmpSwpRevVN =
- vnStore->GetRelatedRelop(domCmpVN, ValueNumStore::VN_RELATION_KIND::VRK_SwapReverse);
-
- const bool matchCmp = ((domCmpVN != ValueNumStore::NoVN) && (domCmpVN == treeVN));
- const bool matchSwp = ((domCmpSwpVN != ValueNumStore::NoVN) && (domCmpSwpVN == treeVN));
- const bool matchRev = ((domCmpRevVN != ValueNumStore::NoVN) && (domCmpRevVN == treeVN));
- const bool matchSwpRev = ((domCmpSwpRevVN != ValueNumStore::NoVN) && (domCmpSwpRevVN == treeVN));
- const bool domIsSameRelop = matchCmp || matchSwp;
- const bool domIsRevRelop = matchRev || matchSwpRev;
+ const ValueNum treeVN = tree->GetVN(VNK_Liberal);
+ const ValueNum domCmpVN = domCmpTree->GetVN(VNK_Liberal);
+ ValueNumStore::VN_RELATION_KIND vnRelationMatch = ValueNumStore::VN_RELATION_KIND::VRK_Same;
+ bool matched = false;
+
+ for (auto vnRelation : vnRelations)
+ {
+ const ValueNum relatedVN = vnStore->GetRelatedRelop(domCmpVN, vnRelation);
+ if ((relatedVN != ValueNumStore::NoVN) && (relatedVN == treeVN))
+ {
+ vnRelationMatch = vnRelation;
+ matched = true;
+ break;
+ }
+ }
// Note we could also infer the tree relop's value from relops higher in the dom tree
// that involve the same operands but are not swaps or reverses.
@@ -156,18 +162,20 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
//
// That is left as a future enhancement.
//
- if (domIsSameRelop || domIsRevRelop)
+ if (matched)
{
// The compare in "tree" is redundant.
// Is there a unique path from the dominating compare?
//
- JITDUMP("\nDominator " FMT_BB " of " FMT_BB " has %srelop with %s liberal VN\n", domBlock->bbNum,
- block->bbNum, matchSwp || matchSwpRev ? "swapped " : "",
- domIsSameRelop ? "the same" : "a reverse");
+ JITDUMP("\nDominator " FMT_BB " of " FMT_BB " has relop with %s liberal VN\n", domBlock->bbNum,
+ block->bbNum, ValueNumStore::VNRelationString(vnRelationMatch));
DISPTREE(domCmpTree);
JITDUMP(" Redundant compare; current relop:\n");
DISPTREE(tree);
+ const bool domIsSameRelop = (vnRelationMatch == ValueNumStore::VN_RELATION_KIND::VRK_Same) ||
+ (vnRelationMatch == ValueNumStore::VN_RELATION_KIND::VRK_Swap);
+
BasicBlock* const trueSuccessor = domBlock->bbJumpDest;
BasicBlock* const falseSuccessor = domBlock->bbNext;
const bool trueReaches = optReachable(trueSuccessor, block, domBlock);
@@ -691,6 +699,447 @@ bool Compiler::optJumpThread(BasicBlock* const block, BasicBlock* const domBlock
return true;
}
+//------------------------------------------------------------------------
+// optRedundantRelop: see if the value of tree is redundant given earlier
+// relops in this block.
+//
+// Arguments:
+// block - block of interest (BBJ_COND)
+//
+// Returns:
+// true, if changes were made.
+//
+// Notes:
+//
+// Here's a walkthrough of how this operates. Given a block like
+//
+// STMT00388 (IL 0x30D... ???)
+// * ASG ref
+// +--* LCL_VAR ref V121 tmp97 d:1
+// \--* IND ref
+// \--* LCL_VAR byref V116 tmp92 u:1 (last use) Zero Fseq[m_task] $18c
+//
+// STMT00390 (IL 0x30D... ???)
+// * ASG bool
+// +--* LCL_VAR int V123 tmp99 d:1
+// \--* NE int
+// +--* LCL_VAR ref V121 tmp97 u:1
+// \--* CNS_INT ref null $VN.Null
+//
+// STMT00391
+// * ASG ref $133
+// +--* LCL_VAR ref V124 tmp100 d:1 $133
+// \--* IND ref $133
+// \--* CNS_INT(h) long 0x31BD3020 [ICON_STR_HDL] $34f
+//
+// STMT00392
+// * JTRUE void
+// \--* NE int
+// +--* LCL_VAR int V123 tmp99 u:1 (last use)
+// \--* CNS_INT int 0 $40
+//
+// We will first consider STMT00391. It is a local assign but the RHS value number
+// isn't related to $8ff. So we continue searching and add V124 to the array
+// of defined locals.
+//
+// Next we consider STMT00390. It is a local assign and the RHS value number is
+// the same, $8ff. So this compare is a fwd-sub candidate. We check if any local
+// on the RHS is in the defined locals array. The answer is no. So the RHS tree
+// can be safely forwarded in place of the compare in STMT00392. We check if V123 is
+// live out of the block. The answer is no. So This RHS tree becomes the candidate tree.
+// We add V123 to the array of defined locals and keep searching.
+//
+// Next we consider STMT00388, It is a local assign but the RHS value number
+// isn't related to $8ff. So we continue searching and add V121 to the array
+// of defined locals.
+//
+// We reach the end of the block and stop searching.
+//
+// Since we found a viable candidate, we clone it and substitute into the jump:
+//
+// STMT00388 (IL 0x30D... ???)
+// * ASG ref
+// +--* LCL_VAR ref V121 tmp97 d:1
+// \--* IND ref
+// \--* LCL_VAR byref V116 tmp92 u:1 (last use) Zero Fseq[m_task] $18c
+//
+// STMT00390 (IL 0x30D... ???)
+// * ASG bool
+// +--* LCL_VAR int V123 tmp99 d:1
+// \--* NE int
+// +--* LCL_VAR ref V121 tmp97 u:1
+// \--* CNS_INT ref null $VN.Null
+//
+// STMT00391
+// * ASG ref $133
+// +--* LCL_VAR ref V124 tmp100 d:1 $133
+// \--* IND ref $133
+// \--* CNS_INT(h) long 0x31BD3020 [ICON_STR_HDL] $34f
+//
+// STMT00392
+// * JTRUE void
+// \--* NE int
+// +--* LCL_VAR ref V121 tmp97 u:1
+// \--* CNS_INT ref null $VN.Null
+//
+// We anticipate that STMT00390 will become dead code, and if and so we've
+// eliminated one of the two compares in the block.
+//
+bool Compiler::optRedundantRelop(BasicBlock* const block)
+{
+ Statement* const stmt = block->lastStmt();
+
+ if (stmt == nullptr)
+ {
+ return false;
+ }
+
+ // If there's just one statement, bail.
+ //
+ if (stmt == block->firstStmt())
+ {
+ JITDUMP(" -- no, no prior stmt\n");
+ return false;
+ }
+
+ GenTree* const jumpTree = stmt->GetRootNode();
+
+ if (!jumpTree->OperIs(GT_JTRUE))
+ {
+ return false;
+ }
+
+ GenTree* const tree = jumpTree->AsOp()->gtOp1;
+
+ if (!tree->OperIsCompare())
+ {
+ return false;
+ }
+
+ // If tree has side effects other than GTF_EXCEPT, bail.
+ //
+ if ((tree->gtFlags & GTF_SIDE_EFFECT) != 0)
+ {
+ if ((tree->gtFlags & GTF_SIDE_EFFECT) != GTF_EXCEPT)
+ {
+ return false;
+ }
+ }
+
+ // If relop's value is known, bail.
+ //
+ const ValueNum treeVN = vnStore->VNNormalValue(tree->GetVN(VNK_Liberal));
+
+ if (vnStore->IsVNConstant(treeVN))
+ {
+ JITDUMP(" -- no, jump tree cond is constant\n");
+ return false;
+ }
+
+ // Save off the jump tree's liberal exceptional VN.
+ //
+ const ValueNum treeExcVN = vnStore->VNExceptionSet(tree->GetVN(VNK_Liberal));
+
+ JITDUMP("\noptRedundantRelop in " FMT_BB "; jump tree is\n", block->bbNum);
+ DISPTREE(jumpTree);
+
+ // We're going to search back to find the earliest tree in block that
+ // * makes the current relop redundant;
+ // * can safely and profitably forward substituted to the jump.
+ //
+ Statement* prevStmt = stmt;
+ GenTree* candidateTree = nullptr;
+ Statement* candidateStmt = nullptr;
+ ValueNumStore::VN_RELATION_KIND candidateVnRelation = ValueNumStore::VN_RELATION_KIND::VRK_Same;
+ bool sideEffect = false;
+
+ const ValueNumStore::VN_RELATION_KIND vnRelations[] = {ValueNumStore::VN_RELATION_KIND::VRK_Same,
+ ValueNumStore::VN_RELATION_KIND::VRK_Reverse,
+ ValueNumStore::VN_RELATION_KIND::VRK_Swap,
+ ValueNumStore::VN_RELATION_KIND::VRK_SwapReverse};
+
+ // We need to keep track of which locals might be killed by
+ // the trees between the expression we want to forward substitute
+ // and the jump.
+ //
+ // We don't use a varset here because we are indexing by local ID,
+ // not by tracked index.
+ //
+ // The table size here also implicitly limits how far back we'll search.
+ //
+ enum
+ {
+ DEFINED_LOCALS_SIZE = 10
+ };
+ unsigned definedLocals[DEFINED_LOCALS_SIZE];
+ unsigned definedLocalsCount = 0;
+
+ while (true)
+ {
+ // If we've run a cross a side effecting pred tree, stop looking.
+ //
+ if (sideEffect)
+ {
+ break;
+ }
+
+ prevStmt = prevStmt->GetPrevStmt();
+
+ // Backwards statement walks wrap around, so if we get
+ // back to stmt we've seen everything there is to see.
+ //
+ if (prevStmt == stmt)
+ {
+ break;
+ }
+
+ // We are looking for ASG(lcl, ...)
+ //
+ GenTree* const prevTree = prevStmt->GetRootNode();
+
+ JITDUMP(" ... checking previous tree\n");
+ DISPTREE(prevTree);
+
+ // Ignore nops.
+ //
+ if (prevTree->OperIs(GT_NOP))
+ {
+ continue;
+ }
+
+ // If prevTree has side effects other than GTF_EXCEPT or GTF_ASG, bail,
+ // unless it is in the immediately preceeding statement.
+ //
+ // (we'll later show that any exception must come from the RHS as the LHS
+ // will be a simple local).
+ //
+ if ((prevTree->gtFlags & GTF_SIDE_EFFECT) != (prevTree->gtFlags & (GTF_EXCEPT | GTF_ASG)))
+ {
+ if (prevStmt->GetNextStmt() != stmt)
+ {
+ JITDUMP(" -- prev tree has side effects and is not next to jumpTree\n");
+ break;
+ }
+
+ JITDUMP(" -- prev tree has side effects, allowing as prev tree is immediately before jumpTree\n");
+ sideEffect = true;
+ }
+
+ if (!prevTree->OperIs(GT_ASG))
+ {
+ JITDUMP(" -- prev tree not ASG\n");
+ break;
+ }
+
+ GenTree* const prevTreeLHS = prevTree->AsOp()->gtOp1;
+ GenTree* const prevTreeRHS = prevTree->AsOp()->gtOp2;
+
+ if (!prevTreeLHS->OperIs(GT_LCL_VAR))
+ {
+ JITDUMP(" -- prev tree not ASG(LCL...)\n");
+ break;
+ }
+
+ // If we are seeing PHIs we have run out of interesting stmts.
+ //
+ if (prevTreeRHS->OperIs(GT_PHI))
+ {
+ JITDUMP(" -- prev tree is a phi\n");
+ break;
+ }
+
+ // Bail if RHS has an embedded assignment. We could handle this
+ // if we generalized the interference check we run below.
+ //
+ if ((prevTreeRHS->gtFlags & GTF_ASG) != 0)
+ {
+ JITDUMP(" -- prev tree RHS has embedded assignment\n");
+ break;
+ }
+
+ // Figure out what local is assigned here.
+ //
+ const unsigned prevTreeLcl = prevTreeLHS->AsLclVarCommon()->GetLclNum();
+ LclVarDsc* const prevTreeLclDsc = lvaGetDesc(prevTreeLcl);
+
+ // If local is not tracked, assume we can't safely reason about interference
+ // or liveness.
+ //
+ if (!prevTreeLclDsc->lvTracked)
+ {
+ JITDUMP(" -- prev tree defs untracked V%02u\n", prevTreeLcl);
+ break;
+ }
+
+ // If we've run out of room to keep track of defined locals, bail.
+ //
+ if (definedLocalsCount >= DEFINED_LOCALS_SIZE)
+ {
+ JITDUMP(" -- ran out of space for tracking kills\n");
+ break;
+ }
+
+ definedLocals[definedLocalsCount++] = prevTreeLcl;
+
+ // If the normal liberal VN of RHS is the normal liberal VN of the current tree, or is "related",
+ // consider forward sub.
+ //
+ const ValueNum domCmpVN = vnStore->VNNormalValue(prevTreeRHS->GetVN(VNK_Liberal));
+ bool matched = false;
+ ValueNumStore::VN_RELATION_KIND vnRelationMatch = ValueNumStore::VN_RELATION_KIND::VRK_Same;
+
+ for (auto vnRelation : vnRelations)
+ {
+ const ValueNum relatedVN = vnStore->GetRelatedRelop(domCmpVN, vnRelation);
+ if ((relatedVN != ValueNumStore::NoVN) && (relatedVN == treeVN))
+ {
+ vnRelationMatch = vnRelation;
+ matched = true;
+ break;
+ }
+ }
+
+ if (!matched)
+ {
+ JITDUMP(" -- prev tree VN is not related\n");
+ continue;
+ }
+
+ JITDUMP(" -- prev tree has relop with %s liberal VN\n", ValueNumStore::VNRelationString(vnRelationMatch));
+
+ // If the jump tree VN has exceptions, verify that the RHS tree has a superset.
+ //
+ if (treeExcVN != vnStore->VNForEmptyExcSet())
+ {
+ const ValueNum prevTreeExcVN = vnStore->VNExceptionSet(prevTreeRHS->GetVN(VNK_Liberal));
+
+ if (!vnStore->VNExcIsSubset(prevTreeExcVN, treeExcVN))
+ {
+ JITDUMP(" -- prev tree does not anticipate all jump tree exceptions\n");
+ break;
+ }
+ }
+
+ // See if we can safely move a copy of prevTreeRHS later, to replace tree.
+ // We can, if none of its lcls are killed.
+ //
+ bool interferes = false;
+
+ for (unsigned int i = 0; i < definedLocalsCount; i++)
+ {
+ if (gtHasRef(prevTreeRHS, definedLocals[i], /*def only*/ false))
+ {
+ JITDUMP(" -- prev tree ref to V%02u interferes\n", definedLocals[i]);
+ interferes = true;
+ break;
+ }
+ }
+
+ if (interferes)
+ {
+ break;
+ }
+
+ // Heuristic: only forward sub a relop
+ //
+ if (!prevTreeRHS->OperIsCompare())
+ {
+ JITDUMP(" -- prev tree is not relop\n");
+ continue;
+ }
+
+ // If the lcl defined here is live out, forward sub is problematic.
+ // We'll either create a redundant tree (as the original won't be dead)
+ // or lose the def (if we actually move the RHS tree).
+ //
+ if (VarSetOps::IsMember(this, block->bbLiveOut, prevTreeLclDsc->lvVarIndex))
+ {
+ JITDUMP(" -- prev tree lcl V%02u is live-out\n", prevTreeLcl);
+ continue;
+ }
+
+ JITDUMP(" -- prev tree is viable candidate for relop fwd sub!\n");
+ candidateTree = prevTreeRHS;
+ candidateStmt = prevStmt;
+ candidateVnRelation = vnRelationMatch;
+ }
+
+ if (candidateTree == nullptr)
+ {
+ return false;
+ }
+
+ GenTree* substituteTree = nullptr;
+ bool usedCopy = false;
+
+ if (candidateStmt->GetNextStmt() == stmt)
+ {
+ // We are going forward-sub candidateTree
+ //
+ substituteTree = candidateTree;
+ }
+ else
+ {
+ // We going to forward-sub a copy of candidateTree
+ //
+ assert(!sideEffect);
+ substituteTree = gtCloneExpr(candidateTree);
+ usedCopy = true;
+ }
+
+ // If we need the reverse compare, make it so.
+ // We also need to set a proper VN.
+ //
+ if ((candidateVnRelation == ValueNumStore::VN_RELATION_KIND::VRK_Reverse) ||
+ (candidateVnRelation == ValueNumStore::VN_RELATION_KIND::VRK_SwapReverse))
+ {
+ // Copy the vn info as it will be trashed when we change the oper.
+ //
+ ValueNumPair origVNP = substituteTree->gtVNPair;
+
+ // Update the tree. Note we don't actually swap operands...?
+ //
+ substituteTree->SetOper(GenTree::ReverseRelop(substituteTree->OperGet()));
+
+ // Compute the right set of VNs for this new tree.
+ //
+ ValueNum origNormConVN = vnStore->VNConservativeNormalValue(origVNP);
+ ValueNum origNormLibVN = vnStore->VNLiberalNormalValue(origVNP);
+ ValueNum newNormConVN = vnStore->GetRelatedRelop(origNormConVN, ValueNumStore::VN_RELATION_KIND::VRK_Reverse);
+ ValueNum newNormLibVN = vnStore->GetRelatedRelop(origNormLibVN, ValueNumStore::VN_RELATION_KIND::VRK_Reverse);
+ ValueNumPair newNormalVNP(newNormLibVN, newNormConVN);
+ ValueNumPair origExcVNP = vnStore->VNPExceptionSet(origVNP);
+ ValueNumPair newVNP = vnStore->VNPWithExc(newNormalVNP, origExcVNP);
+
+ substituteTree->SetVNs(newVNP);
+ }
+
+ // This relop is now a subtree of a jump.
+ //
+ substituteTree->gtFlags |= (GTF_RELOP_JMP_USED | GTF_DONT_CSE);
+
+ // Swap in the new tree.
+ //
+ GenTree** const treeUse = &(jumpTree->AsOp()->gtOp1);
+ jumpTree->ReplaceOperand(treeUse, substituteTree);
+ fgSetStmtSeq(stmt);
+ gtUpdateStmtSideEffects(stmt);
+
+ DEBUG_DESTROY_NODE(tree);
+
+ // If we didn't forward sub a copy, the candidateStmt must be removed.
+ //
+ if (!usedCopy)
+ {
+ fgRemoveStmt(block, candidateStmt);
+ }
+
+ JITDUMP(" -- done! new jump tree is\n");
+ DISPTREE(jumpTree);
+
+ return true;
+}
+
//------------------------------------------------------------------------
// optReachable: see if there's a path from one block to another,
// including paths involving EH flow.
diff --git a/src/coreclr/jit/reglist.h b/src/coreclr/jit/reglist.h
deleted file mode 100644
index c8948b80e1b1ca..00000000000000
--- a/src/coreclr/jit/reglist.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-#ifndef REGLIST_H
-#define REGLIST_H
-
-#include "target.h"
-#include "tinyarray.h"
-
-// The "regList" type is a small set of registerse
-#ifdef TARGET_X86
-typedef TinyArray regList;
-#else
-// The regList is unused for all other targets.
-#endif // TARGET*
-
-#endif // REGLIST_H
diff --git a/src/coreclr/jit/regset.cpp b/src/coreclr/jit/regset.cpp
index 808a443c10a2b6..58439020fd693a 100644
--- a/src/coreclr/jit/regset.cpp
+++ b/src/coreclr/jit/regset.cpp
@@ -320,9 +320,8 @@ void RegSet::rsSpillTree(regNumber reg, GenTree* tree, unsigned regIdx /* =0 */)
#endif // TARGET_ARM
else if (tree->IsMultiRegLclVar())
{
- GenTreeLclVar* lcl = tree->AsLclVar();
- LclVarDsc* varDsc = m_rsCompiler->lvaGetDesc(lcl->GetLclNum());
- treeType = varDsc->TypeGet();
+ LclVarDsc* varDsc = m_rsCompiler->lvaGetDesc(tree->AsLclVar());
+ treeType = varDsc->TypeGet();
}
else
{
@@ -921,7 +920,7 @@ bool RegSet::tmpAllFree() const
return false;
}
- for (unsigned i = 0; i < _countof(tmpUsed); i++)
+ for (unsigned i = 0; i < ArrLen(tmpUsed); i++)
{
if (tmpUsed[i] != nullptr)
{
diff --git a/src/coreclr/jit/scopeinfo.cpp b/src/coreclr/jit/scopeinfo.cpp
index b19df93346912b..69a63e020b46cd 100644
--- a/src/coreclr/jit/scopeinfo.cpp
+++ b/src/coreclr/jit/scopeinfo.cpp
@@ -1093,7 +1093,7 @@ void CodeGen::siOpenScopesForNonTrackedVars(const BasicBlock* block, unsigned in
while ((varScope = compiler->compGetNextEnterScope(beginOffs)) != nullptr)
{
- LclVarDsc* lclVarDsc = &compiler->lvaTable[varScope->vsdVarNum];
+ LclVarDsc* lclVarDsc = compiler->lvaGetDesc(varScope->vsdVarNum);
// Only report locals that were referenced, if we're not doing debug codegen
if (compiler->opts.compDbgCode || (lclVarDsc->lvRefCnt() > 0))
@@ -1179,10 +1179,8 @@ void CodeGen::siEndBlock(BasicBlock* block)
JITDUMP("Scope info: ending scope, LVnum=%u [%03X..%03X)\n", varScope->vsdLVnum, varScope->vsdLifeBeg,
varScope->vsdLifeEnd);
- unsigned varNum = varScope->vsdVarNum;
- LclVarDsc* lclVarDsc1 = &compiler->lvaTable[varNum];
-
- assert(lclVarDsc1);
+ unsigned varNum = varScope->vsdVarNum;
+ const LclVarDsc* lclVarDsc1 = compiler->lvaGetDesc(varNum);
if (lclVarDsc1->lvTracked)
{
@@ -1278,7 +1276,7 @@ void CodeGen::siCheckVarScope(unsigned varNum, IL_OFFSET offs)
}
siScope* scope;
- LclVarDsc* lclVarDsc1 = &compiler->lvaTable[varNum];
+ LclVarDsc* lclVarDsc1 = compiler->lvaGetDesc(varNum);
// If there is an open scope corresponding to varNum, find it
@@ -1525,7 +1523,7 @@ void CodeGen::psiBegProlog()
VarScopeDsc* varScope;
while ((varScope = compiler->compGetNextEnterScope(0)) != nullptr)
{
- LclVarDsc* lclVarDsc = &compiler->lvaTable[varScope->vsdVarNum];
+ LclVarDsc* lclVarDsc = compiler->lvaGetDesc(varScope->vsdVarNum);
if (!lclVarDsc->lvIsParam)
{
diff --git a/src/coreclr/jit/simd.cpp b/src/coreclr/jit/simd.cpp
index a3573b6eeb51e6..cfcc8f0285624b 100644
--- a/src/coreclr/jit/simd.cpp
+++ b/src/coreclr/jit/simd.cpp
@@ -298,7 +298,7 @@ CorInfoType Compiler::getBaseJitTypeAndSizeOfSIMDType(CORINFO_CLASS_HANDLE typeH
// TODO-Throughput: implement product shipping solution to query base type.
WCHAR className[256] = {0};
WCHAR* pbuf = &className[0];
- int len = _countof(className);
+ int len = ArrLen(className);
info.compCompHnd->appendClassName((char16_t**)&pbuf, &len, typeHnd, true, false, false);
noway_assert(pbuf < &className[256]);
JITDUMP("SIMD Candidate Type %S\n", className);
@@ -1204,9 +1204,6 @@ const SIMDIntrinsicInfo* Compiler::getSIMDIntrinsicInfo(CORINFO_CLASS_HANDLE* in
case SIMDIntrinsicConvertToDouble:
case SIMDIntrinsicConvertToInt32:
case SIMDIntrinsicConvertToInt64:
- case SIMDIntrinsicNarrow:
- case SIMDIntrinsicWidenHi:
- case SIMDIntrinsicWidenLo:
return true;
default:
@@ -1280,9 +1277,7 @@ GenTree* Compiler::impSIMDPopStack(var_types type, bool expectAddr, CORINFO_CLAS
if (tree->OperGet() == GT_LCL_VAR)
{
- unsigned lclNum = tree->AsLclVarCommon()->GetLclNum();
- LclVarDsc* lclVarDsc = &lvaTable[lclNum];
- isParam = lclVarDsc->lvIsParam;
+ isParam = lvaGetDesc(tree->AsLclVarCommon())->lvIsParam;
}
// normalize TYP_STRUCT value
@@ -1466,9 +1461,8 @@ SIMDIntrinsicID Compiler::impSIMDRelOp(SIMDIntrinsicID relOpIntrinsicId,
tempBaseJitType = CORINFO_TYPE_INT;
initVal = gtNewIconNode((ssize_t)constVal);
}
- initVal->gtType = JITtype2varType(tempBaseJitType);
- GenTree* constVector =
- gtNewSIMDNode(simdType, initVal, nullptr, SIMDIntrinsicInit, tempBaseJitType, size);
+ initVal->gtType = JITtype2varType(tempBaseJitType);
+ GenTree* constVector = gtNewSIMDNode(simdType, initVal, SIMDIntrinsicInit, tempBaseJitType, size);
// Assign constVector to a temp, since we intend to use it more than once
// TODO-CQ: We have quite a few such constant vectors constructed during
@@ -1536,8 +1530,7 @@ GenTree* Compiler::getOp1ForConstructor(OPCODE opcode, GenTree* newobjThis, CORI
void Compiler::setLclRelatedToSIMDIntrinsic(GenTree* tree)
{
assert(tree->OperIsLocal());
- unsigned lclNum = tree->AsLclVarCommon()->GetLclNum();
- LclVarDsc* lclVarDsc = &lvaTable[lclNum];
+ LclVarDsc* lclVarDsc = lvaGetDesc(tree->AsLclVarCommon());
lclVarDsc->lvUsedInSIMDIntrinsic = true;
}
@@ -1986,10 +1979,13 @@ GenTree* Compiler::impSIMDIntrinsic(OPCODE opcode,
// SIMDIntrinsicInitN
// op2 - list of initializer values stitched into a list
// op1 - byref of vector
- bool initFromFirstArgIndir = false;
+ IntrinsicNodeBuilder nodeBuilder(getAllocator(CMK_ASTNode), argCount - 1);
+ bool initFromFirstArgIndir = false;
+
if (simdIntrinsicID == SIMDIntrinsicInit)
{
op2 = impSIMDPopStack(simdBaseType);
+ nodeBuilder.AddOperand(0, op2);
}
else
{
@@ -2000,21 +1996,19 @@ GenTree* Compiler::impSIMDIntrinsic(OPCODE opcode,
unsigned elementCount = getSIMDVectorLength(size, simdBaseType);
noway_assert(initCount == elementCount);
- // Build a GT_LIST with the N values.
+ // Build an array with the N values.
// We must maintain left-to-right order of the args, but we will pop
// them off in reverse order (the Nth arg was pushed onto the stack last).
- GenTree* list = nullptr;
- GenTree* firstArg = nullptr;
GenTree* prevArg = nullptr;
bool areArgsContiguous = true;
for (unsigned i = 0; i < initCount; i++)
{
- GenTree* nextArg = impSIMDPopStack(simdBaseType);
+ GenTree* arg = impSIMDPopStack(simdBaseType);
+
if (areArgsContiguous)
{
- GenTree* curArg = nextArg;
- firstArg = curArg;
+ GenTree* curArg = arg;
if (prevArg != nullptr)
{
@@ -2024,7 +2018,8 @@ GenTree* Compiler::impSIMDIntrinsic(OPCODE opcode,
prevArg = curArg;
}
- list = new (this, GT_LIST) GenTreeOp(GT_LIST, simdBaseType, nextArg, list);
+ assert(genActualType(arg) == genActualType(simdBaseType));
+ nodeBuilder.AddOperand(initCount - i - 1, arg);
}
if (areArgsContiguous && simdBaseType == TYP_FLOAT)
@@ -2033,20 +2028,15 @@ GenTree* Compiler::impSIMDIntrinsic(OPCODE opcode,
// we intialize the vector from first argument address, only when
// the simdBaseType is TYP_FLOAT and the arguments are located contiguously in memory
initFromFirstArgIndir = true;
- GenTree* op2Address = createAddressNodeForSIMDInit(firstArg, size);
+ GenTree* op2Address = createAddressNodeForSIMDInit(nodeBuilder.GetOperand(0), size);
var_types simdType = getSIMDTypeForSize(size);
op2 = gtNewOperNode(GT_IND, simdType, op2Address);
}
- else
- {
- op2 = list;
- }
}
op1 = getOp1ForConstructor(opcode, newobjThis, clsHnd);
assert(op1->TypeGet() == TYP_BYREF);
- assert(genActualType(op2->TypeGet()) == genActualType(simdBaseType) || initFromFirstArgIndir);
// For integral base types of size less than TYP_INT, expand the initializer
// to fill size of TYP_INT bytes.
@@ -2086,6 +2076,7 @@ GenTree* Compiler::impSIMDIntrinsic(OPCODE opcode,
}
else
{
+ // TODO-Casts: this cast is useless.
assert(simdBaseType == TYP_UBYTE || simdBaseType == TYP_USHORT);
t1 = gtNewCastNode(TYP_INT, op2, false, TYP_INT);
}
@@ -2095,8 +2086,8 @@ GenTree* Compiler::impSIMDIntrinsic(OPCODE opcode,
op2 = gtNewOperNode(GT_MUL, TYP_INT, t1, t2);
// Construct a vector of TYP_INT with the new initializer and cast it back to vector of simdBaseType
- simdTree = gtNewSIMDNode(simdType, op2, nullptr, simdIntrinsicID, CORINFO_TYPE_INT, size);
- simdTree = gtNewSIMDNode(simdType, simdTree, nullptr, SIMDIntrinsicCast, simdBaseJitType, size);
+ simdTree = gtNewSIMDNode(simdType, op2, simdIntrinsicID, CORINFO_TYPE_INT, size);
+ simdTree = gtNewSIMDNode(simdType, simdTree, SIMDIntrinsicCast, simdBaseJitType, size);
}
else
{
@@ -2113,7 +2104,8 @@ GenTree* Compiler::impSIMDIntrinsic(OPCODE opcode,
}
else
{
- simdTree = gtNewSIMDNode(simdType, op2, nullptr, simdIntrinsicID, simdBaseJitType, size);
+ simdTree = new (this, GT_SIMD)
+ GenTreeSIMD(simdType, std::move(nodeBuilder), simdIntrinsicID, simdBaseJitType, size);
}
}
@@ -2230,8 +2222,10 @@ GenTree* Compiler::impSIMDIntrinsic(OPCODE opcode,
if (simdIntrinsicID == SIMDIntrinsicInitArray || simdIntrinsicID == SIMDIntrinsicInitArrayX)
{
- op1 = getOp1ForConstructor(opcode, newobjThis, clsHnd);
- simdTree = gtNewSIMDNode(simdType, op2, op3, SIMDIntrinsicInitArray, simdBaseJitType, size);
+ op1 = getOp1ForConstructor(opcode, newobjThis, clsHnd);
+ simdTree = (op3 != nullptr)
+ ? gtNewSIMDNode(simdType, op2, op3, SIMDIntrinsicInitArray, simdBaseJitType, size)
+ : gtNewSIMDNode(simdType, op2, SIMDIntrinsicInitArray, simdBaseJitType, size);
copyBlkDst = op1;
doCopyBlk = true;
}
@@ -2340,7 +2334,7 @@ GenTree* Compiler::impSIMDIntrinsic(OPCODE opcode,
{
op1 = impSIMDPopStack(simdType, instMethod);
- simdTree = gtNewSIMDNode(simdType, op1, nullptr, simdIntrinsicID, simdBaseJitType, size);
+ simdTree = gtNewSIMDNode(simdType, op1, simdIntrinsicID, simdBaseJitType, size);
retVal = simdTree;
}
break;
@@ -2350,7 +2344,7 @@ GenTree* Compiler::impSIMDIntrinsic(OPCODE opcode,
#ifdef TARGET_64BIT
op1 = impSIMDPopStack(simdType, instMethod);
- simdTree = gtNewSIMDNode(simdType, op1, nullptr, simdIntrinsicID, simdBaseJitType, size);
+ simdTree = gtNewSIMDNode(simdType, op1, simdIntrinsicID, simdBaseJitType, size);
retVal = simdTree;
#else
JITDUMP("SIMD Conversion to Int64 is not supported on this platform\n");
@@ -2359,50 +2353,6 @@ GenTree* Compiler::impSIMDIntrinsic(OPCODE opcode,
}
break;
- case SIMDIntrinsicNarrow:
- {
- assert(!instMethod);
- op2 = impSIMDPopStack(simdType);
- op1 = impSIMDPopStack(simdType);
- // op1 and op2 are two input Vector.
- simdTree = gtNewSIMDNode(simdType, op1, op2, simdIntrinsicID, simdBaseJitType, size);
- retVal = simdTree;
- }
- break;
-
- case SIMDIntrinsicWiden:
- {
- GenTree* dstAddrHi = impSIMDPopStack(TYP_BYREF);
- GenTree* dstAddrLo = impSIMDPopStack(TYP_BYREF);
- op1 = impSIMDPopStack(simdType);
- // op1 must have a valid class handle; the following method will assert it.
- CORINFO_CLASS_HANDLE op1Handle = gtGetStructHandle(op1);
- GenTree* dupOp1 = fgInsertCommaFormTemp(&op1, op1Handle);
-
- // Widen the lower half and assign it to dstAddrLo.
- simdTree = gtNewSIMDNode(simdType, op1, nullptr, SIMDIntrinsicWidenLo, simdBaseJitType, size);
- // TODO-1stClassStructs: With the introduction of ClassLayout it would be preferrable to use
- // GT_OBJ instead of GT_BLK nodes to avoid losing information about the actual vector type.
- GenTree* loDest = new (this, GT_BLK)
- GenTreeBlk(GT_BLK, simdType, dstAddrLo, typGetBlkLayout(getSIMDTypeSizeInBytes(clsHnd)));
- GenTree* loAsg = gtNewBlkOpNode(loDest, simdTree,
- false, // not volatile
- true); // copyBlock
- loAsg->gtFlags |= ((simdTree->gtFlags | dstAddrLo->gtFlags) & GTF_ALL_EFFECT);
-
- // Widen the upper half and assign it to dstAddrHi.
- simdTree = gtNewSIMDNode(simdType, dupOp1, nullptr, SIMDIntrinsicWidenHi, simdBaseJitType, size);
- GenTree* hiDest = new (this, GT_BLK)
- GenTreeBlk(GT_BLK, simdType, dstAddrHi, typGetBlkLayout(getSIMDTypeSizeInBytes(clsHnd)));
- GenTree* hiAsg = gtNewBlkOpNode(hiDest, simdTree,
- false, // not volatile
- true); // copyBlock
- hiAsg->gtFlags |= ((simdTree->gtFlags | dstAddrHi->gtFlags) & GTF_ALL_EFFECT);
-
- retVal = gtNewOperNode(GT_COMMA, simdType, loAsg, hiAsg);
- }
- break;
-
case SIMDIntrinsicHWAccel:
{
GenTreeIntCon* intConstTree = new (this, GT_CNS_INT) GenTreeIntCon(TYP_INT, 1);
diff --git a/src/coreclr/jit/simd.h b/src/coreclr/jit/simd.h
index 07d70e20d503d1..8388f1ef3bc617 100644
--- a/src/coreclr/jit/simd.h
+++ b/src/coreclr/jit/simd.h
@@ -42,7 +42,7 @@ enum SIMDLevel
extern const char* const simdIntrinsicNames[];
#endif
-enum SIMDIntrinsicID
+enum SIMDIntrinsicID : uint16_t
{
#define SIMD_INTRINSIC(m, i, id, n, r, ac, arg1, arg2, arg3, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) SIMDIntrinsic##id,
#include "simdintrinsiclist.h"
diff --git a/src/coreclr/jit/simdashwintrinsic.cpp b/src/coreclr/jit/simdashwintrinsic.cpp
index 3aa47a6b50d98b..7f704cd5672926 100644
--- a/src/coreclr/jit/simdashwintrinsic.cpp
+++ b/src/coreclr/jit/simdashwintrinsic.cpp
@@ -229,7 +229,8 @@ GenTree* Compiler::impSimdAsHWIntrinsic(NamedIntrinsic intrinsic,
simdBaseJitType = getBaseJitTypeAndSizeOfSIMDType(clsHnd, &simdSize);
}
}
- else if ((clsHnd == m_simdHandleCache->SIMDVectorHandle) && (numArgs != 0))
+ else if ((clsHnd == m_simdHandleCache->SIMDVectorHandle) && (numArgs != 0) &&
+ !SimdAsHWIntrinsicInfo::KeepBaseTypeFromRet(intrinsic))
{
// We need to fixup the clsHnd in the case we are an intrinsic on Vector
// The first argument will be the appropriate Vector handle to use
@@ -674,6 +675,7 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic,
{
return gtNewSimdAbsNode(retType, op1, simdBaseJitType, simdSize, /* isSimdAsHWIntrinsic */ true);
}
+
case NI_VectorT128_Sum:
{
@@ -693,6 +695,7 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic,
return gtNewSimdAsHWIntrinsicNode(retType, op1, NI_Vector128_ToScalar, simdBaseJitType, simdSize);
}
+
case NI_VectorT256_Sum:
{
// HorizontalAdd combines pairs so we need log2(vectorLength) passes to sum all elements together.
@@ -730,11 +733,26 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic,
return gtNewSimdAsHWIntrinsicNode(retType, op1, NI_Vector128_ToScalar, simdBaseJitType, 16);
}
+
+ case NI_VectorT128_WidenLower:
+ case NI_VectorT256_WidenLower:
+ {
+ return gtNewSimdWidenLowerNode(retType, op1, simdBaseJitType, simdSize,
+ /* isSimdAsHWIntrinsic */ true);
+ }
+
+ case NI_VectorT128_WidenUpper:
+ case NI_VectorT256_WidenUpper:
+ {
+ return gtNewSimdWidenUpperNode(retType, op1, simdBaseJitType, simdSize,
+ /* isSimdAsHWIntrinsic */ true);
+ }
#elif defined(TARGET_ARM64)
case NI_VectorT128_Abs:
{
return gtNewSimdAbsNode(retType, op1, simdBaseJitType, simdSize, /* isSimdAsHWIntrinsic */ true);
}
+
case NI_VectorT128_Sum:
{
GenTree* tmp;
@@ -782,6 +800,18 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic,
}
}
}
+
+ case NI_VectorT128_WidenLower:
+ {
+ return gtNewSimdWidenLowerNode(retType, op1, simdBaseJitType, simdSize,
+ /* isSimdAsHWIntrinsic */ true);
+ }
+
+ case NI_VectorT128_WidenUpper:
+ {
+ return gtNewSimdWidenUpperNode(retType, op1, simdBaseJitType, simdSize,
+ /* isSimdAsHWIntrinsic */ true);
+ }
#else
#error Unsupported platform
#endif // !TARGET_XARCH && !TARGET_ARM64
@@ -915,6 +945,13 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic,
/* isSimdAsHWIntrinsic */ true);
}
+ case NI_VectorT128_Narrow:
+ case NI_VectorT256_Narrow:
+ {
+ return gtNewSimdNarrowNode(retType, op1, op2, simdBaseJitType, simdSize,
+ /* isSimdAsHWIntrinsic */ true);
+ }
+
case NI_VectorT128_op_Multiply:
case NI_VectorT256_op_Multiply:
{
@@ -954,6 +991,12 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic,
/* isSimdAsHWIntrinsic */ true);
}
+ case NI_VectorT128_Narrow:
+ {
+ return gtNewSimdNarrowNode(retType, op1, op2, simdBaseJitType, simdSize,
+ /* isSimdAsHWIntrinsic */ true);
+ }
+
case NI_VectorT128_op_Multiply:
{
return gtNewSimdBinOpNode(GT_MUL, retType, op1, op2, simdBaseJitType, simdSize,
diff --git a/src/coreclr/jit/simdashwintrinsic.h b/src/coreclr/jit/simdashwintrinsic.h
index 176507d0b66531..a48729412b954e 100644
--- a/src/coreclr/jit/simdashwintrinsic.h
+++ b/src/coreclr/jit/simdashwintrinsic.h
@@ -29,6 +29,9 @@ enum class SimdAsHWIntrinsicFlag : unsigned int
// Base type should come from the this argument
BaseTypeFromThisArg = 0x08,
+
+ // For SIMDVectorHandle, keep the base type from the result type
+ KeepBaseTypeFromRet = 0x10,
};
inline SimdAsHWIntrinsicFlag operator~(SimdAsHWIntrinsicFlag value)
@@ -133,6 +136,12 @@ struct SimdAsHWIntrinsicInfo
SimdAsHWIntrinsicFlag flags = lookupFlags(id);
return (flags & SimdAsHWIntrinsicFlag::BaseTypeFromThisArg) == SimdAsHWIntrinsicFlag::BaseTypeFromThisArg;
}
+
+ static bool KeepBaseTypeFromRet(NamedIntrinsic id)
+ {
+ SimdAsHWIntrinsicFlag flags = lookupFlags(id);
+ return (flags & SimdAsHWIntrinsicFlag::KeepBaseTypeFromRet) == SimdAsHWIntrinsicFlag::KeepBaseTypeFromRet;
+ }
};
#endif // _SIMD_AS_HWINTRINSIC_H_
diff --git a/src/coreclr/jit/simdashwintrinsiclistarm64.h b/src/coreclr/jit/simdashwintrinsiclistarm64.h
index 229222882f7202..2810a0e6ecfb1c 100644
--- a/src/coreclr/jit/simdashwintrinsiclistarm64.h
+++ b/src/coreclr/jit/simdashwintrinsiclistarm64.h
@@ -121,6 +121,7 @@ SIMD_AS_HWINTRINSIC_ID(VectorT128, LessThan,
SIMD_AS_HWINTRINSIC_ID(VectorT128, LessThanOrEqual, 2, {NI_AdvSimd_CompareLessThanOrEqual, NI_AdvSimd_CompareLessThanOrEqual, NI_AdvSimd_CompareLessThanOrEqual, NI_AdvSimd_CompareLessThanOrEqual, NI_AdvSimd_CompareLessThanOrEqual, NI_AdvSimd_CompareLessThanOrEqual, NI_AdvSimd_Arm64_CompareLessThanOrEqual, NI_AdvSimd_Arm64_CompareLessThanOrEqual, NI_AdvSimd_CompareLessThanOrEqual, NI_AdvSimd_Arm64_CompareLessThanOrEqual}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT128, Max, 2, {NI_AdvSimd_Max, NI_AdvSimd_Max, NI_AdvSimd_Max, NI_AdvSimd_Max, NI_AdvSimd_Max, NI_AdvSimd_Max, NI_VectorT128_Max, NI_VectorT128_Max, NI_AdvSimd_Max, NI_AdvSimd_Arm64_Max}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT128, Min, 2, {NI_AdvSimd_Min, NI_AdvSimd_Min, NI_AdvSimd_Min, NI_AdvSimd_Min, NI_AdvSimd_Min, NI_AdvSimd_Min, NI_VectorT128_Min, NI_VectorT128_Min, NI_AdvSimd_Min, NI_AdvSimd_Arm64_Min}, SimdAsHWIntrinsicFlag::None)
+SIMD_AS_HWINTRINSIC_ID(VectorT128, Narrow, 2, {NI_VectorT128_Narrow, NI_VectorT128_Narrow, NI_VectorT128_Narrow, NI_VectorT128_Narrow, NI_VectorT128_Narrow, NI_VectorT128_Narrow, NI_VectorT128_Narrow, NI_VectorT128_Narrow, NI_VectorT128_Narrow, NI_VectorT128_Narrow}, SimdAsHWIntrinsicFlag::KeepBaseTypeFromRet)
SIMD_AS_HWINTRINSIC_ID(VectorT128, op_Addition, 2, {NI_AdvSimd_Add, NI_AdvSimd_Add, NI_AdvSimd_Add, NI_AdvSimd_Add, NI_AdvSimd_Add, NI_AdvSimd_Add, NI_AdvSimd_Add, NI_AdvSimd_Add, NI_AdvSimd_Add, NI_AdvSimd_Arm64_Add}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT128, op_BitwiseAnd, 2, {NI_AdvSimd_And, NI_AdvSimd_And, NI_AdvSimd_And, NI_AdvSimd_And, NI_AdvSimd_And, NI_AdvSimd_And, NI_AdvSimd_And, NI_AdvSimd_And, NI_AdvSimd_And, NI_AdvSimd_And}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT128, op_BitwiseOr, 2, {NI_AdvSimd_Or, NI_AdvSimd_Or, NI_AdvSimd_Or, NI_AdvSimd_Or, NI_AdvSimd_Or, NI_AdvSimd_Or, NI_AdvSimd_Or, NI_AdvSimd_Or, NI_AdvSimd_Or, NI_AdvSimd_Or}, SimdAsHWIntrinsicFlag::None)
@@ -133,6 +134,8 @@ SIMD_AS_HWINTRINSIC_ID(VectorT128, op_Multiply,
SIMD_AS_HWINTRINSIC_ID(VectorT128, op_Subtraction, 2, {NI_AdvSimd_Subtract, NI_AdvSimd_Subtract, NI_AdvSimd_Subtract, NI_AdvSimd_Subtract, NI_AdvSimd_Subtract, NI_AdvSimd_Subtract, NI_AdvSimd_Subtract, NI_AdvSimd_Subtract, NI_AdvSimd_Subtract, NI_AdvSimd_Arm64_Subtract}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT128, SquareRoot, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_AdvSimd_Arm64_Sqrt, NI_AdvSimd_Arm64_Sqrt}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT128, Sum, 1, {NI_VectorT128_Sum, NI_VectorT128_Sum, NI_VectorT128_Sum, NI_VectorT128_Sum, NI_VectorT128_Sum, NI_VectorT128_Sum, NI_VectorT128_Sum, NI_VectorT128_Sum, NI_VectorT128_Sum, NI_VectorT128_Sum}, SimdAsHWIntrinsicFlag::None)
+SIMD_AS_HWINTRINSIC_ID(VectorT128, WidenLower, 1, {NI_VectorT128_WidenLower, NI_VectorT128_WidenLower, NI_VectorT128_WidenLower, NI_VectorT128_WidenLower, NI_VectorT128_WidenLower, NI_VectorT128_WidenLower, NI_VectorT128_WidenLower, NI_VectorT128_WidenLower, NI_VectorT128_WidenLower, NI_VectorT128_WidenLower}, SimdAsHWIntrinsicFlag::None)
+SIMD_AS_HWINTRINSIC_ID(VectorT128, WidenUpper, 1, {NI_VectorT128_WidenUpper, NI_VectorT128_WidenUpper, NI_VectorT128_WidenUpper, NI_VectorT128_WidenUpper, NI_VectorT128_WidenUpper, NI_VectorT128_WidenUpper, NI_VectorT128_WidenUpper, NI_VectorT128_WidenUpper, NI_VectorT128_WidenUpper, NI_VectorT128_WidenUpper}, SimdAsHWIntrinsicFlag::None)
#undef SIMD_AS_HWINTRINSIC_NM
#undef SIMD_AS_HWINTRINSIC_ID
diff --git a/src/coreclr/jit/simdashwintrinsiclistxarch.h b/src/coreclr/jit/simdashwintrinsiclistxarch.h
index 92d665c2de8a71..08cf5178282832 100644
--- a/src/coreclr/jit/simdashwintrinsiclistxarch.h
+++ b/src/coreclr/jit/simdashwintrinsiclistxarch.h
@@ -121,6 +121,7 @@ SIMD_AS_HWINTRINSIC_ID(VectorT128, LessThan,
SIMD_AS_HWINTRINSIC_ID(VectorT128, LessThanOrEqual, 2, {NI_VectorT128_LessThanOrEqual, NI_VectorT128_LessThanOrEqual, NI_VectorT128_LessThanOrEqual, NI_VectorT128_LessThanOrEqual, NI_VectorT128_LessThanOrEqual, NI_VectorT128_LessThanOrEqual, NI_VectorT128_LessThanOrEqual, NI_VectorT128_LessThanOrEqual, NI_SSE_CompareLessThanOrEqual, NI_SSE2_CompareLessThanOrEqual}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT128, Max, 2, {NI_VectorT128_Max, NI_SSE2_Max, NI_SSE2_Max, NI_VectorT128_Max, NI_VectorT128_Max, NI_VectorT128_Max, NI_VectorT128_Max, NI_VectorT128_Max, NI_SSE_Max, NI_SSE2_Max}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT128, Min, 2, {NI_VectorT128_Min, NI_SSE2_Min, NI_SSE2_Min, NI_VectorT128_Min, NI_VectorT128_Min, NI_VectorT128_Min, NI_VectorT128_Min, NI_VectorT128_Min, NI_SSE_Min, NI_SSE2_Min}, SimdAsHWIntrinsicFlag::None)
+SIMD_AS_HWINTRINSIC_ID(VectorT128, Narrow, 2, {NI_VectorT128_Narrow, NI_VectorT128_Narrow, NI_VectorT128_Narrow, NI_VectorT128_Narrow, NI_VectorT128_Narrow, NI_VectorT128_Narrow, NI_VectorT128_Narrow, NI_VectorT128_Narrow, NI_VectorT128_Narrow, NI_VectorT128_Narrow}, SimdAsHWIntrinsicFlag::KeepBaseTypeFromRet)
SIMD_AS_HWINTRINSIC_ID(VectorT128, op_Addition, 2, {NI_SSE2_Add, NI_SSE2_Add, NI_SSE2_Add, NI_SSE2_Add, NI_SSE2_Add, NI_SSE2_Add, NI_SSE2_Add, NI_SSE2_Add, NI_SSE_Add, NI_SSE2_Add}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT128, op_BitwiseAnd, 2, {NI_SSE2_And, NI_SSE2_And, NI_SSE2_And, NI_SSE2_And, NI_SSE2_And, NI_SSE2_And, NI_SSE2_And, NI_SSE2_And, NI_SSE_And, NI_SSE2_And}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT128, op_BitwiseOr, 2, {NI_SSE2_Or, NI_SSE2_Or, NI_SSE2_Or, NI_SSE2_Or, NI_SSE2_Or, NI_SSE2_Or, NI_SSE2_Or, NI_SSE2_Or, NI_SSE_Or, NI_SSE2_Or}, SimdAsHWIntrinsicFlag::None)
@@ -133,6 +134,8 @@ SIMD_AS_HWINTRINSIC_ID(VectorT128, op_Multiply,
SIMD_AS_HWINTRINSIC_ID(VectorT128, op_Subtraction, 2, {NI_SSE2_Subtract, NI_SSE2_Subtract, NI_SSE2_Subtract, NI_SSE2_Subtract, NI_SSE2_Subtract, NI_SSE2_Subtract, NI_SSE2_Subtract, NI_SSE2_Subtract, NI_SSE_Subtract, NI_SSE2_Subtract}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT128, SquareRoot, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_SSE_Sqrt, NI_SSE2_Sqrt}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT128, Sum, 1, {NI_Illegal, NI_Illegal, NI_VectorT128_Sum, NI_VectorT128_Sum, NI_VectorT128_Sum, NI_VectorT128_Sum, NI_Illegal, NI_Illegal, NI_VectorT128_Sum, NI_VectorT128_Sum}, SimdAsHWIntrinsicFlag::None)
+SIMD_AS_HWINTRINSIC_ID(VectorT128, WidenLower, 1, {NI_VectorT128_WidenLower, NI_VectorT128_WidenLower, NI_VectorT128_WidenLower, NI_VectorT128_WidenLower, NI_VectorT128_WidenLower, NI_VectorT128_WidenLower, NI_VectorT128_WidenLower, NI_VectorT128_WidenLower, NI_VectorT128_WidenLower, NI_VectorT128_WidenLower}, SimdAsHWIntrinsicFlag::None)
+SIMD_AS_HWINTRINSIC_ID(VectorT128, WidenUpper, 1, {NI_VectorT128_WidenUpper, NI_VectorT128_WidenUpper, NI_VectorT128_WidenUpper, NI_VectorT128_WidenUpper, NI_VectorT128_WidenUpper, NI_VectorT128_WidenUpper, NI_VectorT128_WidenUpper, NI_VectorT128_WidenUpper, NI_VectorT128_WidenUpper, NI_VectorT128_WidenUpper}, SimdAsHWIntrinsicFlag::None)
// *************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
// ISA ID Name NumArg Instructions Flags
@@ -160,6 +163,7 @@ SIMD_AS_HWINTRINSIC_ID(VectorT256, LessThan,
SIMD_AS_HWINTRINSIC_ID(VectorT256, LessThanOrEqual, 2, {NI_VectorT256_LessThanOrEqual, NI_VectorT256_LessThanOrEqual, NI_VectorT256_LessThanOrEqual, NI_VectorT256_LessThanOrEqual, NI_VectorT256_LessThanOrEqual, NI_VectorT256_LessThanOrEqual, NI_VectorT256_LessThanOrEqual, NI_VectorT256_LessThanOrEqual, NI_AVX_CompareLessThanOrEqual, NI_AVX_CompareLessThanOrEqual}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT256, Max, 2, {NI_AVX2_Max, NI_AVX2_Max, NI_AVX2_Max, NI_AVX2_Max, NI_AVX2_Max, NI_AVX2_Max, NI_VectorT256_Max, NI_VectorT256_Max, NI_AVX_Max, NI_AVX_Max}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT256, Min, 2, {NI_AVX2_Min, NI_AVX2_Min, NI_AVX2_Min, NI_AVX2_Min, NI_AVX2_Min, NI_AVX2_Min, NI_VectorT256_Min, NI_VectorT256_Min, NI_AVX_Min, NI_AVX_Min}, SimdAsHWIntrinsicFlag::None)
+SIMD_AS_HWINTRINSIC_ID(VectorT256, Narrow, 2, {NI_VectorT256_Narrow, NI_VectorT256_Narrow, NI_VectorT256_Narrow, NI_VectorT256_Narrow, NI_VectorT256_Narrow, NI_VectorT256_Narrow, NI_VectorT256_Narrow, NI_VectorT256_Narrow, NI_VectorT256_Narrow, NI_VectorT256_Narrow}, SimdAsHWIntrinsicFlag::KeepBaseTypeFromRet)
SIMD_AS_HWINTRINSIC_ID(VectorT256, op_Addition, 2, {NI_AVX2_Add, NI_AVX2_Add, NI_AVX2_Add, NI_AVX2_Add, NI_AVX2_Add, NI_AVX2_Add, NI_AVX2_Add, NI_AVX2_Add, NI_AVX_Add, NI_AVX_Add}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT256, op_BitwiseAnd, 2, {NI_AVX2_And, NI_AVX2_And, NI_AVX2_And, NI_AVX2_And, NI_AVX2_And, NI_AVX2_And, NI_AVX2_And, NI_AVX2_And, NI_AVX_And, NI_AVX_And}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT256, op_BitwiseOr, 2, {NI_AVX2_Or, NI_AVX2_Or, NI_AVX2_Or, NI_AVX2_Or, NI_AVX2_Or, NI_AVX2_Or, NI_AVX2_Or, NI_AVX2_Or, NI_AVX_Or, NI_AVX_Or}, SimdAsHWIntrinsicFlag::None)
@@ -172,6 +176,8 @@ SIMD_AS_HWINTRINSIC_ID(VectorT256, op_Multiply,
SIMD_AS_HWINTRINSIC_ID(VectorT256, op_Subtraction, 2, {NI_AVX2_Subtract, NI_AVX2_Subtract, NI_AVX2_Subtract, NI_AVX2_Subtract, NI_AVX2_Subtract, NI_AVX2_Subtract, NI_AVX2_Subtract, NI_AVX2_Subtract, NI_AVX_Subtract, NI_AVX_Subtract}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT256, SquareRoot, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_AVX_Sqrt, NI_AVX_Sqrt}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT256, Sum, 1, {NI_Illegal, NI_Illegal, NI_VectorT256_Sum, NI_VectorT256_Sum, NI_VectorT256_Sum, NI_VectorT256_Sum, NI_Illegal, NI_Illegal, NI_VectorT256_Sum, NI_VectorT256_Sum}, SimdAsHWIntrinsicFlag::None)
+SIMD_AS_HWINTRINSIC_ID(VectorT256, WidenLower, 1, {NI_VectorT256_WidenLower, NI_VectorT256_WidenLower, NI_VectorT256_WidenLower, NI_VectorT256_WidenLower, NI_VectorT256_WidenLower, NI_VectorT256_WidenLower, NI_VectorT256_WidenLower, NI_VectorT256_WidenLower, NI_VectorT256_WidenLower, NI_VectorT256_WidenLower}, SimdAsHWIntrinsicFlag::None)
+SIMD_AS_HWINTRINSIC_ID(VectorT256, WidenUpper, 1, {NI_VectorT256_WidenUpper, NI_VectorT256_WidenUpper, NI_VectorT256_WidenUpper, NI_VectorT256_WidenUpper, NI_VectorT256_WidenUpper, NI_VectorT256_WidenUpper, NI_VectorT256_WidenUpper, NI_VectorT256_WidenUpper, NI_VectorT256_WidenUpper, NI_VectorT256_WidenUpper}, SimdAsHWIntrinsicFlag::None)
#undef SIMD_AS_HWINTRINSIC_NM
#undef SIMD_AS_HWINTRINSIC_ID
diff --git a/src/coreclr/jit/simdcodegenxarch.cpp b/src/coreclr/jit/simdcodegenxarch.cpp
index 4523fe48a896e4..9362aa05f521a4 100644
--- a/src/coreclr/jit/simdcodegenxarch.cpp
+++ b/src/coreclr/jit/simdcodegenxarch.cpp
@@ -241,86 +241,6 @@ instruction CodeGen::getOpForSIMDIntrinsic(SIMDIntrinsicID intrinsicId, var_type
result = INS_cvttsd2si;
break;
- case SIMDIntrinsicNarrow:
- // Note that for the integer types the caller must zero the upper bits of
- // each source element, since the instructions saturate.
- switch (baseType)
- {
- case TYP_INT:
- case TYP_UINT:
- if (compiler->getSIMDSupportLevel() >= SIMD_SSE4_Supported)
- {
- result = INS_packusdw;
- }
- else
- {
- result = INS_packssdw;
- }
- break;
- case TYP_SHORT:
- case TYP_USHORT:
- result = INS_packuswb;
- break;
- default:
- assert(!"Invalid baseType for SIMDIntrinsicNarrow");
- result = INS_invalid;
- break;
- }
- break;
-
- case SIMDIntrinsicWidenLo:
- // Some of these have multiple instruction implementations, with one instruction to widen the lo half,
- // and another to widen the hi half.
- switch (baseType)
- {
- case TYP_FLOAT:
- result = INS_cvtps2pd;
- break;
- case TYP_INT:
- case TYP_UINT:
- result = INS_punpckldq;
- break;
- case TYP_SHORT:
- case TYP_USHORT:
- result = INS_punpcklwd;
- break;
- case TYP_BYTE:
- case TYP_UBYTE:
- result = INS_punpcklbw;
- break;
- default:
- assert(!"Invalid baseType for SIMDIntrinsicWidenLo");
- result = INS_invalid;
- break;
- }
- break;
-
- case SIMDIntrinsicWidenHi:
- switch (baseType)
- {
- case TYP_FLOAT:
- // For this case, we actually use the same instruction.
- result = INS_cvtps2pd;
- break;
- case TYP_INT:
- case TYP_UINT:
- result = INS_punpckhdq;
- break;
- case TYP_SHORT:
- case TYP_USHORT:
- result = INS_punpckhwd;
- break;
- case TYP_BYTE:
- case TYP_UBYTE:
- result = INS_punpckhbw;
- break;
- default:
- assert(!"Invalid baseType for SIMDIntrinsicWidenHi");
- result = INS_invalid;
- break;
- }
- break;
-
case SIMDIntrinsicShiftLeftInternal:
switch (baseType)
{
@@ -467,9 +387,9 @@ void CodeGen::genSIMDZero(var_types targetType, var_types baseType, regNumber ta
//
void CodeGen::genSIMDIntrinsicInit(GenTreeSIMD* simdNode)
{
- assert(simdNode->gtSIMDIntrinsicID == SIMDIntrinsicInit);
+ assert(simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicInit);
- GenTree* op1 = simdNode->gtGetOp1();
+ GenTree* op1 = simdNode->Op(1);
var_types baseType = simdNode->GetSimdBaseType();
regNumber targetReg = simdNode->GetRegNum();
assert(targetReg != REG_NA);
@@ -581,7 +501,7 @@ void CodeGen::genSIMDIntrinsicInit(GenTreeSIMD* simdNode)
srcReg = targetReg;
}
- ins = getOpForSIMDIntrinsic(simdNode->gtSIMDIntrinsicID, baseType);
+ ins = getOpForSIMDIntrinsic(simdNode->GetSIMDIntrinsicId(), baseType);
GetEmitter()->emitIns_R_R(ins, emitActualTypeSize(targetType), targetReg, srcReg);
}
else
@@ -649,7 +569,7 @@ void CodeGen::genSIMDIntrinsicInit(GenTreeSIMD* simdNode)
//
void CodeGen::genSIMDIntrinsicInitN(GenTreeSIMD* simdNode)
{
- assert(simdNode->gtSIMDIntrinsicID == SIMDIntrinsicInitN);
+ assert(simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicInitN);
// Right now this intrinsic is supported only on TYP_FLOAT vectors
var_types baseType = simdNode->GetSimdBaseType();
@@ -678,19 +598,17 @@ void CodeGen::genSIMDIntrinsicInitN(GenTreeSIMD* simdNode)
// We will first consume the list items in execution (left to right) order,
// and record the registers.
regNumber operandRegs[SIMD_INTRINSIC_MAX_PARAM_COUNT];
- unsigned initCount = 0;
- for (GenTree* list = simdNode->gtGetOp1(); list != nullptr; list = list->gtGetOp2())
+ size_t initCount = simdNode->GetOperandCount();
+ for (size_t i = 1; i <= initCount; i++)
{
- assert(list->OperGet() == GT_LIST);
- GenTree* listItem = list->gtGetOp1();
- assert(listItem->TypeGet() == baseType);
- assert(!listItem->isContained());
- regNumber operandReg = genConsumeReg(listItem);
- operandRegs[initCount] = operandReg;
- initCount++;
+ GenTree* operand = simdNode->Op(i);
+ assert(operand->TypeIs(baseType));
+ assert(!operand->isContained());
+
+ operandRegs[i - 1] = genConsumeReg(operand);
}
- unsigned int offset = 0;
+ unsigned offset = 0;
for (unsigned i = 0; i < initCount; i++)
{
// We will now construct the vector from the list items in reverse order.
@@ -729,17 +647,17 @@ void CodeGen::genSIMDIntrinsicInitN(GenTreeSIMD* simdNode)
//
void CodeGen::genSIMDIntrinsicUnOp(GenTreeSIMD* simdNode)
{
- assert(simdNode->gtSIMDIntrinsicID == SIMDIntrinsicCast);
+ assert(simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicCast);
- GenTree* op1 = simdNode->gtGetOp1();
+ GenTree* op1 = simdNode->Op(1);
var_types baseType = simdNode->GetSimdBaseType();
regNumber targetReg = simdNode->GetRegNum();
assert(targetReg != REG_NA);
var_types targetType = simdNode->TypeGet();
regNumber op1Reg = genConsumeReg(op1);
- instruction ins = getOpForSIMDIntrinsic(simdNode->gtSIMDIntrinsicID, baseType);
- if (simdNode->gtSIMDIntrinsicID != SIMDIntrinsicCast)
+ instruction ins = getOpForSIMDIntrinsic(simdNode->GetSIMDIntrinsicId(), baseType);
+ if (simdNode->GetSIMDIntrinsicId() != SIMDIntrinsicCast)
{
inst_RV_RV(ins, targetReg, op1Reg, targetType, emitActualTypeSize(targetType));
}
@@ -761,17 +679,17 @@ void CodeGen::genSIMDIntrinsicUnOp(GenTreeSIMD* simdNode)
//
void CodeGen::genSIMDIntrinsic32BitConvert(GenTreeSIMD* simdNode)
{
- SIMDIntrinsicID intrinsicID = simdNode->gtSIMDIntrinsicID;
+ SIMDIntrinsicID intrinsicID = simdNode->GetSIMDIntrinsicId();
assert((intrinsicID == SIMDIntrinsicConvertToSingle) || (intrinsicID == SIMDIntrinsicConvertToInt32));
- GenTree* op1 = simdNode->gtGetOp1();
+ GenTree* op1 = simdNode->Op(1);
var_types baseType = simdNode->GetSimdBaseType();
regNumber targetReg = simdNode->GetRegNum();
assert(targetReg != REG_NA);
var_types targetType = simdNode->TypeGet();
regNumber op1Reg = genConsumeReg(op1);
- instruction ins = getOpForSIMDIntrinsic(simdNode->gtSIMDIntrinsicID, baseType);
+ instruction ins = getOpForSIMDIntrinsic(simdNode->GetSIMDIntrinsicId(), baseType);
if (intrinsicID == SIMDIntrinsicConvertToSingle && baseType == TYP_UINT)
{
regNumber tmpIntReg = simdNode->GetSingleTempReg(RBM_ALLINT);
@@ -892,10 +810,10 @@ void CodeGen::genSIMDLo64BitConvert(SIMDIntrinsicID intrinsicID,
//
void CodeGen::genSIMDIntrinsic64BitConvert(GenTreeSIMD* simdNode)
{
- SIMDIntrinsicID intrinsicID = simdNode->gtSIMDIntrinsicID;
+ SIMDIntrinsicID intrinsicID = simdNode->GetSIMDIntrinsicId();
assert((intrinsicID == SIMDIntrinsicConvertToDouble) || (intrinsicID == SIMDIntrinsicConvertToInt64));
- GenTree* op1 = simdNode->gtGetOp1();
+ GenTree* op1 = simdNode->Op(1);
var_types baseType = simdNode->GetSimdBaseType();
regNumber targetReg = simdNode->GetRegNum();
assert(targetReg != REG_NA);
@@ -1193,245 +1111,6 @@ void CodeGen::genSIMDExtractUpperHalf(GenTreeSIMD* simdNode, regNumber srcReg, r
}
}
-//--------------------------------------------------------------------------------
-// genSIMDIntrinsicWiden: Generate code for SIMD Intrinsic Widen operations
-//
-// Arguments:
-// simdNode - The GT_SIMD node
-//
-// Notes:
-// The Widen intrinsics are broken into separate intrinsics for the two results.
-//
-void CodeGen::genSIMDIntrinsicWiden(GenTreeSIMD* simdNode)
-{
- assert((simdNode->gtSIMDIntrinsicID == SIMDIntrinsicWidenLo) ||
- (simdNode->gtSIMDIntrinsicID == SIMDIntrinsicWidenHi));
-
- GenTree* op1 = simdNode->gtGetOp1();
- var_types baseType = simdNode->GetSimdBaseType();
- regNumber targetReg = simdNode->GetRegNum();
- assert(targetReg != REG_NA);
- var_types simdType = simdNode->TypeGet();
- SIMDLevel level = compiler->getSIMDSupportLevel();
-
- genConsumeOperands(simdNode);
- regNumber op1Reg = op1->GetRegNum();
- regNumber srcReg = op1Reg;
- emitAttr emitSize = emitActualTypeSize(simdType);
- instruction widenIns = getOpForSIMDIntrinsic(simdNode->gtSIMDIntrinsicID, baseType);
-
- if (baseType == TYP_FLOAT)
- {
- if (simdNode->gtSIMDIntrinsicID == SIMDIntrinsicWidenHi)
- {
- genSIMDExtractUpperHalf(simdNode, srcReg, targetReg);
- srcReg = targetReg;
- }
- inst_RV_RV(widenIns, targetReg, srcReg, simdType);
- }
- else
- {
- // We will generate the following on AVX:
- // vpermq targetReg, op1Reg, 0xd4|0xe8
- // vpxor tmpReg, tmpReg
- // vpcmpgt[b|w|d] tmpReg, targetReg (if basetype is signed)
- // vpunpck[l|h][bw|wd|dq] targetReg, tmpReg
- regNumber tmpReg = simdNode->GetSingleTempReg(RBM_ALLFLOAT);
- assert(tmpReg != op1Reg);
-
- if (level == SIMD_AVX2_Supported)
- {
- // permute op1Reg and put it into targetReg
- unsigned ival = 0xd4;
- if (simdNode->gtSIMDIntrinsicID == SIMDIntrinsicWidenHi)
- {
- ival = 0xe8;
- }
- assert((ival >= 0) && (ival <= 255));
- GetEmitter()->emitIns_R_R_I(INS_vpermq, emitSize, targetReg, op1Reg, (int8_t)ival);
- }
- else
- {
- inst_Mov(simdType, targetReg, op1Reg, /* canSkip */ true);
- }
-
- genSIMDZero(simdType, baseType, tmpReg);
- if (!varTypeIsUnsigned(baseType))
- {
- instruction compareIns = INS_invalid;
-
- if (baseType == TYP_INT)
- {
- compareIns = INS_pcmpgtd;
- }
- else if (baseType == TYP_SHORT)
- {
- compareIns = INS_pcmpgtw;
- }
- else if (baseType == TYP_BYTE)
- {
- compareIns = INS_pcmpgtb;
- }
- else if ((baseType == TYP_LONG) && (compiler->getSIMDSupportLevel() >= SIMD_SSE4_Supported))
- {
- compareIns = INS_pcmpgtq;
- }
-
- assert(compareIns != INS_invalid);
- inst_RV_RV(compareIns, tmpReg, targetReg, simdType, emitSize);
- }
- inst_RV_RV(widenIns, targetReg, tmpReg, simdType);
- }
- genProduceReg(simdNode);
-}
-
-//--------------------------------------------------------------------------------
-// genSIMDIntrinsicNarrow: Generate code for SIMD Intrinsic Narrow operations
-//
-// Arguments:
-// simdNode - The GT_SIMD node
-//
-// Notes:
-// This intrinsic takes two arguments. The first operand is narrowed to produce the
-// lower elements of the results, and the second operand produces the high elements.
-//
-void CodeGen::genSIMDIntrinsicNarrow(GenTreeSIMD* simdNode)
-{
- assert(simdNode->gtSIMDIntrinsicID == SIMDIntrinsicNarrow);
-
- GenTree* op1 = simdNode->gtGetOp1();
- GenTree* op2 = simdNode->gtGetOp2();
- var_types baseType = simdNode->GetSimdBaseType();
- regNumber targetReg = simdNode->GetRegNum();
- assert(targetReg != REG_NA);
- var_types simdType = simdNode->TypeGet();
- emitAttr emitSize = emitTypeSize(simdType);
- SIMDLevel level = compiler->getSIMDSupportLevel();
-
- genConsumeOperands(simdNode);
- regNumber op1Reg = op1->GetRegNum();
- regNumber op2Reg = op2->GetRegNum();
- if (baseType == TYP_DOUBLE)
- {
- regNumber tmpReg = simdNode->GetSingleTempReg(RBM_ALLFLOAT);
-
- inst_RV_RV(INS_cvtpd2ps, targetReg, op1Reg, simdType);
- inst_RV_RV(INS_cvtpd2ps, tmpReg, op2Reg, simdType);
- // Now insert the high-order result (in tmpReg) into the upper half of targetReg.
- if (level == SIMD_AVX2_Supported)
- {
- GetEmitter()->emitIns_R_R_I(INS_vinsertf128, EA_32BYTE, targetReg, tmpReg, 0x01);
- }
- else
- {
- inst_RV_RV_IV(INS_shufps, EA_16BYTE, targetReg, tmpReg, (int8_t)SHUFFLE_YXYX);
- }
- }
- else if (varTypeIsLong(baseType))
- {
- if (level == SIMD_AVX2_Supported)
- {
- // We have 8 long elements, 0-3 in op1Reg, 4-7 in op2Reg.
- // We will generate the following:
- // vextracti128 tmpReg, op1Reg, 1 (extract elements 2 and 3 into tmpReg)
- // vextracti128 tmpReg2, op2Reg, 1 (extract elements 6 and 7 into tmpReg2)
- // vinserti128 tmpReg, tmpReg2, 1 (insert elements 6 and 7 into the high half of tmpReg)
- // mov tmpReg2, op1Reg
- // vinserti128 tmpReg2, op2Reg, 1 (insert elements 4 and 5 into the high half of tmpReg2)
- // pshufd tmpReg, tmpReg, XXZX ( - - 7L 6L - - 3L 2L) in tmpReg
- // pshufd tgtReg, tmpReg2, XXZX ( - - 5L 4L - - 1L 0L) in tgtReg
- // punpcklqdq tgtReg, tmpReg
- regNumber tmpReg = simdNode->ExtractTempReg(RBM_ALLFLOAT);
- regNumber tmpReg2 = simdNode->GetSingleTempReg(RBM_ALLFLOAT);
- GetEmitter()->emitIns_R_R_I(INS_vextracti128, EA_32BYTE, tmpReg, op1Reg, 0x01);
- GetEmitter()->emitIns_R_R_I(INS_vextracti128, EA_32BYTE, tmpReg2, op2Reg, 0x01);
- GetEmitter()->emitIns_R_R_I(INS_vinserti128, EA_32BYTE, tmpReg, tmpReg2, 0x01);
- inst_Mov(simdType, tmpReg2, op1Reg, /* canSkip */ false, emitSize);
- GetEmitter()->emitIns_R_R_I(INS_vinserti128, EA_32BYTE, tmpReg2, op2Reg, 0x01);
- GetEmitter()->emitIns_R_R_I(INS_pshufd, emitSize, tmpReg, tmpReg, (int8_t)SHUFFLE_XXZX);
- GetEmitter()->emitIns_R_R_I(INS_pshufd, emitSize, targetReg, tmpReg2, (int8_t)SHUFFLE_XXZX);
- inst_RV_RV_RV(INS_punpcklqdq, targetReg, targetReg, tmpReg, emitSize);
- }
- else
- {
- // We will generate the following:
- // pshufd targetReg, op1Reg, ZXXX (extract the low 32-bits into the upper two 32-bit elements)
- // psrldq targetReg, 8 (shift them right to get zeros in the high elements)
- // pshufd tmpReg, op2Reg, XXZX (same as above, but extract into the lower two 32-bit elements)
- // pslldq tmpReg, 8 (now shift these left to get zeros in the low elements)
- // por targetReg, tmpReg
- regNumber tmpReg = simdNode->GetSingleTempReg(RBM_ALLFLOAT);
- instruction shiftLeftIns = getOpForSIMDIntrinsic(SIMDIntrinsicShiftLeftInternal, TYP_SIMD16);
- instruction shiftRightIns = getOpForSIMDIntrinsic(SIMDIntrinsicShiftRightInternal, TYP_SIMD16);
- emitAttr emitSize = emitTypeSize(simdType);
-
- GetEmitter()->emitIns_R_R_I(INS_pshufd, emitSize, targetReg, op1Reg, (int8_t)SHUFFLE_ZXXX);
- GetEmitter()->emitIns_R_I(shiftRightIns, emitSize, targetReg, 8);
- GetEmitter()->emitIns_R_R_I(INS_pshufd, emitSize, tmpReg, op2Reg, (int8_t)SHUFFLE_XXZX);
- GetEmitter()->emitIns_R_I(shiftLeftIns, emitSize, tmpReg, 8);
- inst_RV_RV(INS_por, targetReg, tmpReg, simdType);
- }
- }
- else
- {
- // We will generate the following:
- // mov targetReg, op1Reg
- // mov tmpReg, op2Reg
- // psll? targetReg, shiftCount
- // pslr? targetReg, shiftCount
- // psll? tmpReg, shiftCount
- // pslr? tmpReg, shiftCount
- // targetReg, tmpReg
- // Where shiftCount is the size of the target baseType (i.e. half the size of the source baseType),
- // and is the appropriate instruction to pack the result (note that we have to truncate to
- // get CLR type semantics; otherwise it will saturate).
- //
- int shiftCount = genTypeSize(baseType) * (BITS_IN_BYTE / 2);
- instruction ins = getOpForSIMDIntrinsic(simdNode->gtSIMDIntrinsicID, baseType);
- instruction shiftLeftIns = getOpForSIMDIntrinsic(SIMDIntrinsicShiftLeftInternal, baseType);
- instruction shiftRightIns = getOpForSIMDIntrinsic(SIMDIntrinsicShiftRightInternal, baseType);
-
- assert((shiftCount >= 0) && (shiftCount <= 127));
-
- if (level == SIMD_AVX2_Supported)
- {
- regNumber tmpReg = simdNode->ExtractTempReg(RBM_ALLFLOAT);
- regNumber tmpReg2 = simdNode->GetSingleTempReg(RBM_ALLFLOAT);
-
- // The AVX instructions generally operate on "lanes", so we have to permute the
- // inputs so that the destination register has the low 128-bit halves of the two
- // inputs, and 'tmpReg' has the high 128-bit halves of the two inputs.
- GetEmitter()->emitIns_R_R_R_I(INS_vperm2i128, emitSize, tmpReg2, op1Reg, op2Reg, 0x20);
- GetEmitter()->emitIns_R_R_R_I(INS_vperm2i128, emitSize, tmpReg, op1Reg, op2Reg, 0x31);
- GetEmitter()->emitIns_R_I(shiftLeftIns, emitSize, tmpReg2, shiftCount);
- GetEmitter()->emitIns_R_I(shiftRightIns, emitSize, tmpReg2, shiftCount);
- GetEmitter()->emitIns_R_I(shiftLeftIns, emitSize, tmpReg, shiftCount);
- GetEmitter()->emitIns_R_I(shiftRightIns, emitSize, tmpReg, shiftCount);
- inst_RV_RV_RV(ins, targetReg, tmpReg2, tmpReg, emitActualTypeSize(simdType));
- }
- else
- {
- regNumber tmpReg = simdNode->GetSingleTempReg(RBM_ALLFLOAT);
-
- inst_Mov(simdType, targetReg, op1Reg, /* canSkip */ false, emitSize);
- inst_Mov(simdType, tmpReg, op2Reg, /* canSkip */ false, emitSize);
-
- instruction tmpShiftRight = shiftRightIns;
- if ((baseType == TYP_INT || baseType == TYP_UINT) && level == SIMD_SSE2_Supported)
- {
- tmpShiftRight = INS_psrad;
- }
-
- GetEmitter()->emitIns_R_I(shiftLeftIns, emitSize, targetReg, shiftCount);
- GetEmitter()->emitIns_R_I(tmpShiftRight, emitSize, targetReg, shiftCount);
- GetEmitter()->emitIns_R_I(shiftLeftIns, emitSize, tmpReg, shiftCount);
- GetEmitter()->emitIns_R_I(tmpShiftRight, emitSize, tmpReg, shiftCount);
- inst_RV_RV(ins, targetReg, tmpReg, simdType);
- }
- }
- genProduceReg(simdNode);
-}
-
//--------------------------------------------------------------------------------
// genSIMDIntrinsicBinOp: Generate code for SIMD Intrinsic binary operations
// add, sub, mul, bit-wise And, AndNot and Or.
@@ -1444,22 +1123,23 @@ void CodeGen::genSIMDIntrinsicNarrow(GenTreeSIMD* simdNode)
//
void CodeGen::genSIMDIntrinsicBinOp(GenTreeSIMD* simdNode)
{
- assert(simdNode->gtSIMDIntrinsicID == SIMDIntrinsicSub || simdNode->gtSIMDIntrinsicID == SIMDIntrinsicBitwiseAnd ||
- simdNode->gtSIMDIntrinsicID == SIMDIntrinsicBitwiseOr);
+ assert((simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicSub) ||
+ (simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicBitwiseAnd) ||
+ (simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicBitwiseOr));
- GenTree* op1 = simdNode->gtGetOp1();
- GenTree* op2 = simdNode->gtGetOp2();
+ GenTree* op1 = simdNode->Op(1);
+ GenTree* op2 = simdNode->Op(2);
var_types baseType = simdNode->GetSimdBaseType();
regNumber targetReg = simdNode->GetRegNum();
assert(targetReg != REG_NA);
var_types targetType = simdNode->TypeGet();
- genConsumeOperands(simdNode);
+ genConsumeMultiOpOperands(simdNode);
regNumber op1Reg = op1->GetRegNum();
regNumber op2Reg = op2->GetRegNum();
regNumber otherReg = op2Reg;
- instruction ins = getOpForSIMDIntrinsic(simdNode->gtSIMDIntrinsicID, baseType);
+ instruction ins = getOpForSIMDIntrinsic(simdNode->GetSIMDIntrinsicId(), baseType);
// Currently AVX doesn't support integer.
// if the ins is INS_cvtsi2ss or INS_cvtsi2sd, we won't use AVX.
@@ -1497,19 +1177,19 @@ void CodeGen::genSIMDIntrinsicBinOp(GenTreeSIMD* simdNode)
//
void CodeGen::genSIMDIntrinsicRelOp(GenTreeSIMD* simdNode)
{
- GenTree* op1 = simdNode->gtGetOp1();
- GenTree* op2 = simdNode->gtGetOp2();
+ GenTree* op1 = simdNode->Op(1);
+ GenTree* op2 = simdNode->Op(2);
var_types baseType = simdNode->GetSimdBaseType();
regNumber targetReg = simdNode->GetRegNum();
var_types targetType = simdNode->TypeGet();
SIMDLevel level = compiler->getSIMDSupportLevel();
- genConsumeOperands(simdNode);
+ genConsumeMultiOpOperands(simdNode);
regNumber op1Reg = op1->GetRegNum();
regNumber op2Reg = op2->GetRegNum();
regNumber otherReg = op2Reg;
- switch (simdNode->gtSIMDIntrinsicID)
+ switch (simdNode->GetSIMDIntrinsicId())
{
case SIMDIntrinsicEqual:
{
@@ -1525,7 +1205,7 @@ void CodeGen::genSIMDIntrinsicRelOp(GenTreeSIMD* simdNode)
#endif
unsigned ival = 0;
- instruction ins = getOpForSIMDIntrinsic(simdNode->gtSIMDIntrinsicID, baseType, &ival);
+ instruction ins = getOpForSIMDIntrinsic(simdNode->GetSIMDIntrinsicId(), baseType, &ival);
// targetReg = op1reg > op2reg
// Therefore, we can optimize if op1Reg == targetReg
@@ -1534,7 +1214,7 @@ void CodeGen::genSIMDIntrinsicRelOp(GenTreeSIMD* simdNode)
{
if (op2Reg == targetReg)
{
- assert(simdNode->gtSIMDIntrinsicID == SIMDIntrinsicEqual);
+ assert(simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicEqual);
otherReg = op1Reg;
}
else
@@ -1574,11 +1254,11 @@ void CodeGen::genSIMDIntrinsicRelOp(GenTreeSIMD* simdNode)
//
void CodeGen::genSIMDIntrinsicShuffleSSE2(GenTreeSIMD* simdNode)
{
- assert(simdNode->gtSIMDIntrinsicID == SIMDIntrinsicShuffleSSE2);
+ assert(simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicShuffleSSE2);
noway_assert(compiler->getSIMDSupportLevel() == SIMD_SSE2_Supported);
- GenTree* op1 = simdNode->gtGetOp1();
- GenTree* op2 = simdNode->gtGetOp2();
+ GenTree* op1 = simdNode->Op(1);
+ GenTree* op2 = simdNode->Op(2);
assert(op2->isContained());
assert(op2->IsCnsIntOrI());
ssize_t shuffleControl = op2->AsIntConCommon()->IconValue();
@@ -1590,7 +1270,7 @@ void CodeGen::genSIMDIntrinsicShuffleSSE2(GenTreeSIMD* simdNode)
regNumber op1Reg = genConsumeReg(op1);
inst_Mov(targetType, targetReg, op1Reg, /* canSkip */ true);
- instruction ins = getOpForSIMDIntrinsic(simdNode->gtSIMDIntrinsicID, baseType);
+ instruction ins = getOpForSIMDIntrinsic(simdNode->GetSIMDIntrinsicId(), baseType);
assert((shuffleControl >= 0) && (shuffleControl <= 255));
GetEmitter()->emitIns_R_R_I(ins, emitTypeSize(baseType), targetReg, targetReg, (int8_t)shuffleControl);
genProduceReg(simdNode);
@@ -1844,9 +1524,9 @@ void CodeGen::genPutArgStkSIMD12(GenTree* treeNode)
//
void CodeGen::genSIMDIntrinsicUpperSave(GenTreeSIMD* simdNode)
{
- assert(simdNode->gtSIMDIntrinsicID == SIMDIntrinsicUpperSave);
+ assert(simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicUpperSave);
- GenTree* op1 = simdNode->gtGetOp1();
+ GenTree* op1 = simdNode->Op(1);
assert(op1->IsLocal() && op1->TypeGet() == TYP_SIMD32);
regNumber targetReg = simdNode->GetRegNum();
regNumber op1Reg = genConsumeReg(op1);
@@ -1886,9 +1566,9 @@ void CodeGen::genSIMDIntrinsicUpperSave(GenTreeSIMD* simdNode)
//
void CodeGen::genSIMDIntrinsicUpperRestore(GenTreeSIMD* simdNode)
{
- assert(simdNode->gtSIMDIntrinsicID == SIMDIntrinsicUpperRestore);
+ assert(simdNode->GetSIMDIntrinsicId() == SIMDIntrinsicUpperRestore);
- GenTree* op1 = simdNode->gtGetOp1();
+ GenTree* op1 = simdNode->Op(1);
assert(op1->IsLocal() && op1->TypeGet() == TYP_SIMD32);
regNumber srcReg = simdNode->GetRegNum();
regNumber lclVarReg = genConsumeReg(op1);
@@ -1931,7 +1611,7 @@ void CodeGen::genSIMDIntrinsic(GenTreeSIMD* simdNode)
noway_assert(!"SIMD intrinsic with unsupported base type.");
}
- switch (simdNode->gtSIMDIntrinsicID)
+ switch (simdNode->GetSIMDIntrinsicId())
{
case SIMDIntrinsicInit:
genSIMDIntrinsicInit(simdNode);
@@ -1955,15 +1635,6 @@ void CodeGen::genSIMDIntrinsic(GenTreeSIMD* simdNode)
genSIMDIntrinsic64BitConvert(simdNode);
break;
- case SIMDIntrinsicWidenLo:
- case SIMDIntrinsicWidenHi:
- genSIMDIntrinsicWiden(simdNode);
- break;
-
- case SIMDIntrinsicNarrow:
- genSIMDIntrinsicNarrow(simdNode);
- break;
-
case SIMDIntrinsicSub:
case SIMDIntrinsicBitwiseAnd:
case SIMDIntrinsicBitwiseOr:
diff --git a/src/coreclr/jit/simdintrinsiclist.h b/src/coreclr/jit/simdintrinsiclist.h
index 258fecfdd65783..0b354b533702c3 100644
--- a/src/coreclr/jit/simdintrinsiclist.h
+++ b/src/coreclr/jit/simdintrinsiclist.h
@@ -78,10 +78,6 @@ SIMD_INTRINSIC("ConvertToDouble", false, ConvertToDouble,
SIMD_INTRINSIC("ConvertToInt32", false, ConvertToInt32, "ConvertToInt32", TYP_STRUCT, 1, {TYP_STRUCT, TYP_UNDEF, TYP_UNDEF}, {TYP_FLOAT, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF})
// Convert double to long
SIMD_INTRINSIC("ConvertToInt64", false, ConvertToInt64, "ConvertToInt64", TYP_STRUCT, 1, {TYP_STRUCT, TYP_UNDEF, TYP_UNDEF}, {TYP_DOUBLE, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF})
-// Narrow two input Vectors to a single Vector. The return value's lower elements are the elements from src1, and the upper elements are from src2.
-SIMD_INTRINSIC("Narrow", false, Narrow, "Narrow", TYP_STRUCT, 2, {TYP_STRUCT, TYP_STRUCT, TYP_UNDEF}, {TYP_INT, TYP_DOUBLE, TYP_LONG, TYP_USHORT, TYP_SHORT, TYP_UINT, TYP_ULONG, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF})
-// Widen one input Vector to two Vectors: dest1 contains the lower half of elements in src, and dest2 contains the upper half of elements in src.
-SIMD_INTRINSIC("Widen", false, Widen, "Widen", TYP_VOID, 3, {TYP_STRUCT, TYP_BYREF, TYP_BYREF}, {TYP_INT, TYP_FLOAT, TYP_USHORT, TYP_UBYTE, TYP_BYTE, TYP_SHORT, TYP_UINT, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF})
// Miscellaneous
SIMD_INTRINSIC("get_IsHardwareAccelerated", false, HWAccel, "HWAccel", TYP_BOOL, 0, {TYP_UNDEF, TYP_UNDEF, TYP_UNDEF}, {TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF})
@@ -100,10 +96,6 @@ SIMD_INTRINSIC("ShiftRightInternal", false, ShiftRightInternal,
SIMD_INTRINSIC("UpperSave", false, UpperSave, "UpperSave Internal", TYP_STRUCT, 2, {TYP_UNDEF, TYP_UNDEF, TYP_UNDEF}, {TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF})
SIMD_INTRINSIC("UpperRestore", false, UpperRestore, "UpperRestore Internal", TYP_STRUCT, 2, {TYP_UNDEF, TYP_UNDEF, TYP_UNDEF}, {TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF})
-// Internal intrinsics for Widen
-SIMD_INTRINSIC("WidenHi", false, WidenHi, "WidenHi", TYP_VOID, 2, {TYP_UNDEF, TYP_UNDEF, TYP_UNDEF}, {TYP_INT, TYP_FLOAT, TYP_USHORT, TYP_UBYTE, TYP_BYTE, TYP_SHORT, TYP_UINT, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF})
-SIMD_INTRINSIC("WidenLo", false, WidenLo, "WidenLo", TYP_VOID, 2, {TYP_UNDEF, TYP_UNDEF, TYP_UNDEF}, {TYP_INT, TYP_FLOAT, TYP_USHORT, TYP_UBYTE, TYP_BYTE, TYP_SHORT, TYP_UINT, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF})
-
SIMD_INTRINSIC(nullptr, false, Invalid, "Invalid", TYP_UNDEF, 0, {TYP_UNDEF, TYP_UNDEF, TYP_UNDEF}, {TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF})
#undef SIMD_INTRINSIC
#else // !defined(TARGET_XARCH) && !defined(TARGET_ARM64)
diff --git a/src/coreclr/jit/smdata.cpp b/src/coreclr/jit/smdata.cpp
index 30b0a7b81d7bec..0d6ec896552bb7 100644
--- a/src/coreclr/jit/smdata.cpp
+++ b/src/coreclr/jit/smdata.cpp
@@ -269,7 +269,7 @@ const SMState g_SMStates[] =
};
// clang-format on
-static_assert_no_msg(NUM_SM_STATES == _countof(g_SMStates));
+static_assert_no_msg(NUM_SM_STATES == ArrLen(g_SMStates));
const SMState* gp_SMStates = g_SMStates;
diff --git a/src/coreclr/jit/smweights.cpp b/src/coreclr/jit/smweights.cpp
index ec55072436eb2c..02aa5d647f7db7 100644
--- a/src/coreclr/jit/smweights.cpp
+++ b/src/coreclr/jit/smweights.cpp
@@ -268,6 +268,6 @@ const short g_StateWeights[] = {
DEFAULT_WEIGHT_VALUE, // state 249 [ldarga.s -> ldfld -> ldarga.s -> ldfld -> sub]
};
-static_assert_no_msg(NUM_SM_STATES == _countof(g_StateWeights));
+static_assert_no_msg(NUM_SM_STATES == ArrLen(g_StateWeights));
const short* gp_StateWeights = g_StateWeights;
diff --git a/src/coreclr/jit/ssabuilder.cpp b/src/coreclr/jit/ssabuilder.cpp
index c1f056c7d2aba9..b9c506e29400a8 100644
--- a/src/coreclr/jit/ssabuilder.cpp
+++ b/src/coreclr/jit/ssabuilder.cpp
@@ -1268,7 +1268,7 @@ void SsaBuilder::AddPhiArgsToSuccessors(BasicBlock* block)
// If the variable is live-out of "blk", and is therefore live on entry to the try-block-start
// "succ", then we make sure the current SSA name for the
// var is one of the args of the phi node. If not, go on.
- LclVarDsc* lclVarDsc = &m_pCompiler->lvaTable[lclNum];
+ const LclVarDsc* lclVarDsc = m_pCompiler->lvaGetDesc(lclNum);
if (!lclVarDsc->lvTracked ||
!VarSetOps::IsMember(m_pCompiler, block->bbLiveOut, lclVarDsc->lvVarIndex))
{
@@ -1361,7 +1361,7 @@ void SsaBuilder::RenameVariables()
continue;
}
- LclVarDsc* varDsc = &m_pCompiler->lvaTable[lclNum];
+ LclVarDsc* varDsc = m_pCompiler->lvaGetDesc(lclNum);
assert(varDsc->lvTracked);
if (varDsc->lvIsParam || m_pCompiler->info.compInitMem || varDsc->lvMustInit ||
@@ -1628,7 +1628,7 @@ void SsaBuilder::SetupBBRoot()
//
bool SsaBuilder::IncludeInSsa(unsigned lclNum)
{
- LclVarDsc* varDsc = &m_pCompiler->lvaTable[lclNum];
+ LclVarDsc* varDsc = m_pCompiler->lvaGetDesc(lclNum);
if (varDsc->IsAddressExposed())
{
diff --git a/src/coreclr/jit/ssabuilder.h b/src/coreclr/jit/ssabuilder.h
index 6d1a9fbd5542f2..68ebb084f08dbc 100644
--- a/src/coreclr/jit/ssabuilder.h
+++ b/src/coreclr/jit/ssabuilder.h
@@ -29,8 +29,9 @@ class SsaBuilder
// Requires stmt nodes to be already sequenced in evaluation order. Analyzes the graph
// for introduction of phi-nodes as GT_PHI tree nodes at the beginning of each block.
// Each GT_LCL_VAR is given its ssa number through its GetSsaNum() field in the node.
- // Each GT_PHI node will have gtOp1 set to lhs of the phi node and the gtOp2 to be a
- // GT_LIST of GT_PHI_ARG. Each use or def is denoted by the corresponding GT_LCL_VAR
+ // Each GT_PHI node will be under a GT_ASG node with the LHS set to the local node and
+ // the RHS to the GT_PHI itself. The inputs to the PHI are represented as a linked list
+ // of GT_PHI_ARG nodes. Each use or def is denoted by the corresponding GT_LCL_VAR
// tree. For example, to get all uses of a particular variable fully defined by its
// lclNum and ssaNum, one would use m_uses and look up all the uses. Similarly, a single
// def of an SSA variable can be looked up similarly using m_defs member.
@@ -70,7 +71,7 @@ class SsaBuilder
// Requires "postOrder" to hold the blocks of the flowgraph in topologically sorted order. Requires
// count to be the valid entries in the "postOrder" array. Inserts GT_PHI nodes at the beginning
// of basic blocks that require them like so:
- // GT_ASG(GT_LCL_VAR, GT_PHI(GT_PHI_ARG(GT_LCL_VAR, Block*), GT_LIST(GT_PHI_ARG(GT_LCL_VAR, Block*), NULL));
+ // GT_ASG(GT_LCL_VAR, GT_PHI(GT_PHI_ARG(ssaNum, Block*), GT_PHI_ARG(ssaNum, Block*), ...));
void InsertPhiFunctions(BasicBlock** postOrder, int count);
// Rename all definitions and uses within the compiled method.
diff --git a/src/coreclr/jit/static/CMakeLists.txt b/src/coreclr/jit/static/CMakeLists.txt
index 8a9c1ab0cb07a9..99ae15963b506c 100644
--- a/src/coreclr/jit/static/CMakeLists.txt
+++ b/src/coreclr/jit/static/CMakeLists.txt
@@ -4,7 +4,7 @@ set_source_files_properties(${JIT_EXPORTS_FILE} PROPERTIES GENERATED TRUE)
add_library_clr(clrjit_obj
OBJECT
- ${JIT_CORE_SOURCES}
+ ${JIT_SOURCES}
${JIT_ARCH_SOURCES}
)
diff --git a/src/coreclr/jit/treelifeupdater.cpp b/src/coreclr/jit/treelifeupdater.cpp
index 15c32596cc4223..526a3c4f72ac36 100644
--- a/src/coreclr/jit/treelifeupdater.cpp
+++ b/src/coreclr/jit/treelifeupdater.cpp
@@ -182,7 +182,7 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree)
lclVarTree = tree;
}
unsigned int lclNum = lclVarTree->AsLclVarCommon()->GetLclNum();
- LclVarDsc* varDsc = compiler->lvaTable + lclNum;
+ LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum);
#ifdef DEBUG
#if !defined(TARGET_AMD64)
@@ -277,7 +277,7 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree)
for (unsigned i = 0; i < varDsc->lvFieldCnt; ++i)
{
bool fieldIsSpilled = spill && ((lclVarTree->GetRegSpillFlagByIdx(i) & GTF_SPILL) != 0);
- LclVarDsc* fldVarDsc = &(compiler->lvaTable[firstFieldVarNum + i]);
+ LclVarDsc* fldVarDsc = compiler->lvaGetDesc(firstFieldVarNum + i);
noway_assert(fldVarDsc->lvIsStructField);
assert(fldVarDsc->lvTracked);
unsigned fldVarIndex = fldVarDsc->lvVarIndex;
@@ -451,7 +451,7 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree)
#ifdef DEBUG
if (compiler->verbose)
{
- printf("\t\t\t\t\t\t\tVar V%02u becoming live\n", varDsc - compiler->lvaTable);
+ printf("\t\t\t\t\t\t\tVar V%02u becoming live\n", compiler->lvaGetLclNum(varDsc));
}
#endif // DEBUG
}
diff --git a/src/coreclr/jit/unwindarm.cpp b/src/coreclr/jit/unwindarm.cpp
index 2b25ed82942cc5..54c6a011cb0a70 100644
--- a/src/coreclr/jit/unwindarm.cpp
+++ b/src/coreclr/jit/unwindarm.cpp
@@ -2270,7 +2270,7 @@ void DumpUnwindInfo(Compiler* comp,
else
{
printf(" --- One epilog, unwind codes at %u\n", epilogCount);
- assert(epilogCount < _countof(epilogStartAt));
+ assert(epilogCount < ArrLen(epilogStartAt));
epilogStartAt[epilogCount] = true; // the one and only epilog starts its unwind codes at this offset
}
diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp
index e454eb338682bc..d6f9df4ce6b1e4 100644
--- a/src/coreclr/jit/utils.cpp
+++ b/src/coreclr/jit/utils.cpp
@@ -110,7 +110,7 @@ const char* varTypeName(var_types vt)
#undef DEF_TP
};
- assert((unsigned)vt < _countof(varTypeNames));
+ assert((unsigned)vt < ArrLen(varTypeNames));
return varTypeNames[vt];
}
@@ -601,7 +601,7 @@ void dumpILRange(const BYTE* const codeAddr, unsigned codeSize) // in bytes
for (IL_OFFSET offs = 0; offs < codeSize;)
{
char prefix[100];
- sprintf_s(prefix, _countof(prefix), "IL_%04x ", offs);
+ sprintf_s(prefix, ArrLen(prefix), "IL_%04x ", offs);
unsigned codeBytesDumped = dumpSingleInstr(codeAddr, offs, prefix);
offs += codeBytesDumped;
}
@@ -1377,7 +1377,6 @@ void HelperCallProperties::init()
case CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS:
case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS:
case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_DYNAMICCLASS:
- case CORINFO_HELP_GETSTATICFIELDADDR_CONTEXT:
case CORINFO_HELP_GETSTATICFIELDADDR_TLS:
case CORINFO_HELP_GETGENERICS_GCSTATIC_BASE:
case CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE:
diff --git a/src/coreclr/jit/utils.h b/src/coreclr/jit/utils.h
index 44521010288426..4e98bc5369df71 100644
--- a/src/coreclr/jit/utils.h
+++ b/src/coreclr/jit/utils.h
@@ -32,7 +32,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#endif
template
-unsigned ArrLen(T (&)[size])
+inline constexpr unsigned ArrLen(T (&)[size])
{
return size;
}
diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp
index 6e31f385d5558e..e4d18a9a0c9f91 100644
--- a/src/coreclr/jit/valuenum.cpp
+++ b/src/coreclr/jit/valuenum.cpp
@@ -343,11 +343,11 @@ VNFunc GetVNFuncForNode(GenTree* node)
#ifdef FEATURE_SIMD
case GT_SIMD:
- return VNFunc(VNF_SIMD_FIRST + node->AsSIMD()->gtSIMDIntrinsicID);
+ return VNFunc(VNF_SIMD_FIRST + node->AsSIMD()->GetSIMDIntrinsicId());
#endif // FEATURE_SIMD
#ifdef FEATURE_HW_INTRINSICS
case GT_HWINTRINSIC:
- return VNFunc(VNF_HWI_FIRST + (node->AsHWIntrinsic()->gtHWIntrinsicId - NI_HW_INTRINSIC_START - 1));
+ return VNFunc(VNF_HWI_FIRST + (node->AsHWIntrinsic()->GetHWIntrinsicId() - NI_HW_INTRINSIC_START - 1));
#endif // FEATURE_HW_INTRINSICS
case GT_CAST:
@@ -464,11 +464,9 @@ ValueNumStore::ValueNumStore(Compiler* comp, CompAllocator alloc)
{
m_VNsForSmallIntConsts[i] = NoVN;
}
- // We will reserve chunk 0 to hold some special constants, like the constant NULL, the "exception" value, and the
- // "zero map."
+ // We will reserve chunk 0 to hold some special constants.
Chunk* specialConstChunk = new (m_alloc) Chunk(m_alloc, &m_nextChunkBase, TYP_REF, CEA_Const);
- specialConstChunk->m_numUsed +=
- SRC_NumSpecialRefConsts; // Implicitly allocate 0 ==> NULL, and 1 ==> Exception, 2 ==> ZeroMap.
+ specialConstChunk->m_numUsed += SRC_NumSpecialRefConsts;
ChunkNum cn = m_chunks.Push(specialConstChunk);
assert(cn == 0);
@@ -1787,8 +1785,6 @@ ValueNum ValueNumStore::VNForHandle(ssize_t cnsVal, GenTreeFlags handleFlags)
}
}
-// Returns the value number for zero of the given "typ".
-// It has an unreached() for a "typ" that has no zero value, such as TYP_VOID.
ValueNum ValueNumStore::VNZeroForType(var_types typ)
{
switch (typ)
@@ -1812,15 +1808,18 @@ ValueNum ValueNumStore::VNZeroForType(var_types typ)
return VNForNull();
case TYP_BYREF:
return VNForByrefCon(0);
- case TYP_STRUCT:
- return VNForZeroMap(); // Recursion!
#ifdef FEATURE_SIMD
case TYP_SIMD8:
case TYP_SIMD12:
case TYP_SIMD16:
case TYP_SIMD32:
- return VNForLongCon(0);
+ // We do not have the base type - a "fake" one will have to do. Note that we cannot
+ // use the HWIntrinsic "get_Zero" VNFunc here. This is because they only represent
+ // "fully zeroed" vectors, and here we may be loading one from memory, leaving upper
+ // bits undefined. So using "SIMD_Init" is "the next best thing", so to speak, and
+ // TYP_FLOAT is one of the more popular base types, so that's why we use it here.
+ return VNForFunc(typ, VNF_SIMD_Init, VNForFloatCon(0), VNForSimdType(genTypeSize(typ), TYP_FLOAT));
#endif // FEATURE_SIMD
// These should be unreached.
@@ -1829,6 +1828,16 @@ ValueNum ValueNumStore::VNZeroForType(var_types typ)
}
}
+ValueNum ValueNumStore::VNForZeroObj(CORINFO_CLASS_HANDLE structHnd)
+{
+ assert(structHnd != NO_CLASS_HANDLE);
+
+ ValueNum structHndVN = VNForHandle(ssize_t(structHnd), GTF_ICON_CLASS_HDL);
+ ValueNum zeroObjVN = VNForFunc(TYP_STRUCT, VNF_ZeroObj, structHndVN);
+
+ return zeroObjVN;
+}
+
// Returns the value number for one of the given "typ".
// It returns NoVN for a "typ" that has no one value, such as TYP_REF.
ValueNum ValueNumStore::VNOneForType(var_types typ)
@@ -1856,6 +1865,17 @@ ValueNum ValueNumStore::VNOneForType(var_types typ)
}
}
+#ifdef FEATURE_SIMD
+ValueNum ValueNumStore::VNForSimdType(unsigned simdSize, var_types simdBaseType)
+{
+ ValueNum baseTypeVN = VNForIntCon(INT32(simdBaseType));
+ ValueNum sizeVN = VNForIntCon(simdSize);
+ ValueNum simdTypeVN = VNForFunc(TYP_REF, VNF_SimdType, sizeVN, baseTypeVN);
+
+ return simdTypeVN;
+}
+#endif // FEATURE_SIMD
+
class Object* ValueNumStore::s_specialRefConsts[] = {nullptr, nullptr, nullptr};
//----------------------------------------------------------------------------------------
@@ -2180,7 +2200,6 @@ ValueNum ValueNumStore::VNForFunc(
//
//
// Arguments:
-// type - Type for the new map
// map - Map value number
// index - Index value number
// value - New value for map[index]
@@ -2188,17 +2207,17 @@ ValueNum ValueNumStore::VNForFunc(
// Return Value:
// Value number for "map" with "map[index]" set to "value".
//
-ValueNum ValueNumStore::VNForMapStore(var_types type, ValueNum map, ValueNum index, ValueNum value)
+ValueNum ValueNumStore::VNForMapStore(ValueNum map, ValueNum index, ValueNum value)
{
BasicBlock* const bb = m_pComp->compCurBB;
BasicBlock::loopNumber const loopNum = bb->bbNatLoopNum;
- ValueNum const result = VNForFunc(type, VNF_MapStore, map, index, value, loopNum);
+ ValueNum const result = VNForFunc(TypeOfVN(map), VNF_MapStore, map, index, value, loopNum);
#ifdef DEBUG
if (m_pComp->verbose)
{
printf(" VNForMapStore(" FMT_VN ", " FMT_VN ", " FMT_VN "):%s in " FMT_BB " returns ", map, index, value,
- varTypeName(type), bb->bbNum);
+ VNMapTypeName(TypeOfVN(result)), bb->bbNum);
m_pComp->vnPrint(result, 1);
printf("\n");
}
@@ -2236,7 +2255,7 @@ ValueNum ValueNumStore::VNForMapSelect(ValueNumKind vnk, var_types type, ValueNu
#ifdef DEBUG
if (m_pComp->verbose)
{
- printf(" VNForMapSelect(" FMT_VN ", " FMT_VN "):%s returns ", map, index, varTypeName(type));
+ printf(" VNForMapSelect(" FMT_VN ", " FMT_VN "):%s returns ", map, index, VNMapTypeName(type));
m_pComp->vnPrint(result, 1);
printf("\n");
}
@@ -2317,14 +2336,9 @@ ValueNum ValueNumStore::VNForMapSelectWork(
return RecursiveVN;
}
- if (map == VNForZeroMap())
- {
- return VNZeroForType(type);
- }
- else if (IsVNFunc(map))
+ VNFuncApp funcApp;
+ if (GetVNFunc(map, &funcApp))
{
- VNFuncApp funcApp;
- GetVNFunc(map, &funcApp);
if (funcApp.m_func == VNF_MapStore)
{
// select(store(m, i, v), i) == v
@@ -2476,6 +2490,22 @@ ValueNum ValueNumStore::VNForMapSelectWork(
m_fixedPointMapSels.Pop();
}
}
+ else if (funcApp.m_func == VNF_ZeroObj)
+ {
+ // For structs, we need to extract the handle from the selector.
+ if (type == TYP_STRUCT)
+ {
+ // We only expect field selectors here.
+ assert(GetHandleFlags(index) == GTF_ICON_FIELD_HDL);
+ CORINFO_FIELD_HANDLE fieldHnd = CORINFO_FIELD_HANDLE(ConstantValue(index));
+ CORINFO_CLASS_HANDLE structHnd = NO_CLASS_HANDLE;
+ m_pComp->eeGetFieldType(fieldHnd, &structHnd);
+
+ return VNForZeroObj(structHnd);
+ }
+
+ return VNZeroForType(type);
+ }
}
// We may have run out of budget and already assigned a result
@@ -2495,6 +2525,61 @@ ValueNum ValueNumStore::VNForMapSelectWork(
}
}
+//------------------------------------------------------------------------
+// VNForFieldSelector: A specialized version (with logging) of VNForHandle
+// that is used for field handle selectors.
+//
+// Arguments:
+// fieldHnd - handle of the field in question
+// pFieldType - [out] parameter for the field's type
+// pStructSize - optional [out] parameter for the size of the struct,
+// populated if the field in question is of a struct type,
+// otherwise set to zero
+//
+// Return Value:
+// Value number corresponding to the given field handle.
+//
+ValueNum ValueNumStore::VNForFieldSelector(CORINFO_FIELD_HANDLE fieldHnd, var_types* pFieldType, size_t* pStructSize)
+{
+ CORINFO_CLASS_HANDLE structHnd = NO_CLASS_HANDLE;
+ ValueNum fldHndVN = VNForHandle(ssize_t(fieldHnd), GTF_ICON_FIELD_HDL);
+ var_types fieldType = m_pComp->eeGetFieldType(fieldHnd, &structHnd);
+ size_t structSize = 0;
+
+ if (fieldType == TYP_STRUCT)
+ {
+ structSize = m_pComp->info.compCompHnd->getClassSize(structHnd);
+
+ // We have to normalize here since there is no CorInfoType for vectors...
+ if (m_pComp->structSizeMightRepresentSIMDType(structSize))
+ {
+ fieldType = m_pComp->impNormStructType(structHnd);
+ }
+ }
+
+#ifdef DEBUG
+ if (m_pComp->verbose)
+ {
+ const char* modName;
+ const char* fldName = m_pComp->eeGetFieldName(fieldHnd, &modName);
+ printf(" VNForHandle(%s) is " FMT_VN ", fieldType is %s", fldName, fldHndVN, varTypeName(fieldType));
+ if (structSize != 0)
+ {
+ printf(", size = %u", structSize);
+ }
+ printf("\n");
+ }
+#endif
+
+ if (pStructSize != nullptr)
+ {
+ *pStructSize = structSize;
+ }
+ *pFieldType = fieldType;
+
+ return fldHndVN;
+}
+
ValueNum ValueNumStore::EvalFuncForConstantArgs(var_types typ, VNFunc func, ValueNum arg0VN)
{
assert(CanEvalForConstantArgs(func));
@@ -3812,44 +3897,16 @@ ValueNum ValueNumStore::VNApplySelectors(ValueNumKind vnk,
assert(field != FieldSeqStore::NotAField());
- CORINFO_FIELD_HANDLE fldHnd = field->m_fieldHnd;
- CORINFO_CLASS_HANDLE structHnd = NO_CLASS_HANDLE;
- ValueNum fldHndVN = VNForHandle(ssize_t(fldHnd), GTF_ICON_FIELD_HDL);
- noway_assert(fldHnd != nullptr);
- CorInfoType fieldCit = m_pComp->info.compCompHnd->getFieldType(fldHnd, &structHnd);
- var_types fieldType = JITtype2varType(fieldCit);
+ JITDUMP(" VNApplySelectors:\n");
+ var_types fieldType;
+ size_t structSize;
+ ValueNum fldHndVN = VNForFieldSelector(field->GetFieldHandle(), &fieldType, &structSize);
- size_t structSize = 0;
- if (varTypeIsStruct(fieldType))
- {
- structSize = m_pComp->info.compCompHnd->getClassSize(structHnd);
- // We do not normalize the type field accesses during importation unless they
- // are used in a call, return or assignment.
- if ((fieldType == TYP_STRUCT) && m_pComp->structSizeMightRepresentSIMDType(structSize))
- {
- fieldType = m_pComp->impNormStructType(structHnd);
- }
- }
if (wbFinalStructSize != nullptr)
{
*wbFinalStructSize = structSize;
}
-#ifdef DEBUG
- if (m_pComp->verbose)
- {
- printf(" VNApplySelectors:\n");
- const char* modName;
- const char* fldName = m_pComp->eeGetFieldName(fldHnd, &modName);
- printf(" VNForHandle(%s) is " FMT_VN ", fieldType is %s", fldName, fldHndVN, varTypeName(fieldType));
- if (varTypeIsStruct(fieldType))
- {
- printf(", size = %d", structSize);
- }
- printf("\n");
- }
-#endif
-
map = VNForMapSelect(vnk, fieldType, map, fldHndVN);
}
@@ -4005,48 +4062,27 @@ ValueNum ValueNumStore::VNApplySelectorsAssign(
return VNApplySelectorsAssign(vnk, map, fieldSeq->m_next, value, dstIndType);
}
+ if (fieldSeq->m_next == nullptr)
+ {
+ JITDUMP(" VNApplySelectorsAssign:\n");
+ }
+
// Otherwise, fldHnd is a real field handle.
- CORINFO_FIELD_HANDLE fldHnd = fieldSeq->m_fieldHnd;
- ValueNum fldHndVN = VNForHandle(ssize_t(fldHnd), GTF_ICON_FIELD_HDL);
- noway_assert(fldHnd != nullptr);
- CorInfoType fieldCit = m_pComp->info.compCompHnd->getFieldType(fldHnd);
- var_types fieldType = JITtype2varType(fieldCit);
+ var_types fieldType;
+ ValueNum fldHndVN = VNForFieldSelector(fieldSeq->GetFieldHandle(), &fieldType);
ValueNum valueAfter;
- if (fieldSeq->m_next)
+ if (fieldSeq->m_next != nullptr)
{
-#ifdef DEBUG
- if (m_pComp->verbose)
- {
- const char* modName;
- const char* fldName = m_pComp->eeGetFieldName(fldHnd, &modName);
- printf(" VNForHandle(%s) is " FMT_VN ", fieldType is %s\n", fldName, fldHndVN,
- varTypeName(fieldType));
- }
-#endif
ValueNum fseqMap = VNForMapSelect(vnk, fieldType, map, fldHndVN);
valueAfter = VNApplySelectorsAssign(vnk, fseqMap, fieldSeq->m_next, value, dstIndType);
}
else
{
-#ifdef DEBUG
- if (m_pComp->verbose)
- {
- if (fieldSeq->m_next == nullptr)
- {
- printf(" VNApplySelectorsAssign:\n");
- }
- const char* modName;
- const char* fldName = m_pComp->eeGetFieldName(fldHnd, &modName);
- printf(" VNForHandle(%s) is " FMT_VN ", fieldType is %s\n", fldName, fldHndVN,
- varTypeName(fieldType));
- }
-#endif
valueAfter = VNApplySelectorsAssignTypeCoerce(value, dstIndType);
}
- ValueNum newMap = VNForMapStore(fieldType, map, fldHndVN, valueAfter);
- return newMap;
+ return VNForMapStore(map, fldHndVN, valueAfter);
}
}
@@ -4229,8 +4265,8 @@ ValueNum Compiler::fgValueNumberArrIndexAssign(CORINFO_CLASS_HANDLE elemTypeEq,
bool invalidateArray = false;
ValueNum elemTypeEqVN = vnStore->VNForHandle(ssize_t(elemTypeEq), GTF_ICON_CLASS_HDL);
var_types arrElemType = DecodeElemType(elemTypeEq);
- ValueNum hAtArrType = vnStore->VNForMapSelect(VNK_Liberal, TYP_REF, fgCurMemoryVN[GcHeap], elemTypeEqVN);
- ValueNum hAtArrTypeAtArr = vnStore->VNForMapSelect(VNK_Liberal, TYP_REF, hAtArrType, arrVN);
+ ValueNum hAtArrType = vnStore->VNForMapSelect(VNK_Liberal, TYP_MEM, fgCurMemoryVN[GcHeap], elemTypeEqVN);
+ ValueNum hAtArrTypeAtArr = vnStore->VNForMapSelect(VNK_Liberal, TYP_MEM, hAtArrType, arrVN);
ValueNum hAtArrTypeAtArrAtInx = vnStore->VNForMapSelect(VNK_Liberal, arrElemType, hAtArrTypeAtArr, inxVN);
ValueNum newValAtInx = ValueNumStore::NoVN;
@@ -4243,7 +4279,7 @@ ValueNum Compiler::fgValueNumberArrIndexAssign(CORINFO_CLASS_HANDLE elemTypeEq,
JITDUMP(" *** NotAField sequence encountered in fgValueNumberArrIndexAssign\n");
// Store a new unique value for newValAtArrType
- newValAtArrType = vnStore->VNForExpr(compCurBB, TYP_REF);
+ newValAtArrType = vnStore->VNForExpr(compCurBB, TYP_MEM);
invalidateArray = true;
}
else
@@ -4252,16 +4288,10 @@ ValueNum Compiler::fgValueNumberArrIndexAssign(CORINFO_CLASS_HANDLE elemTypeEq,
// This is the value that should be stored at "arr[inx]".
newValAtInx = vnStore->VNApplySelectorsAssign(VNK_Liberal, hAtArrTypeAtArrAtInx, fldSeq, rhsVN, indType);
- var_types arrElemFldType = arrElemType; // Uses arrElemType unless we has a non-null fldSeq
- if (vnStore->IsVNFunc(newValAtInx))
- {
- VNFuncApp funcApp;
- vnStore->GetVNFunc(newValAtInx, &funcApp);
- if (funcApp.m_func == VNF_MapStore)
- {
- arrElemFldType = vnStore->TypeOfVN(newValAtInx);
- }
- }
+ // TODO-VNTypes: the validation below is a workaround for logic in ApplySelectorsAssignTypeCoerce
+ // not handling some cases correctly. Remove once ApplySelectorsAssignTypeCoerce has been fixed.
+ var_types arrElemFldType =
+ (fldSeq != nullptr) ? eeGetFieldType(fldSeq->GetTail()->GetFieldHandle()) : arrElemType;
if (indType != arrElemFldType)
{
@@ -4271,15 +4301,15 @@ ValueNum Compiler::fgValueNumberArrIndexAssign(CORINFO_CLASS_HANDLE elemTypeEq,
JITDUMP(" *** Mismatched types in fgValueNumberArrIndexAssign\n");
// Store a new unique value for newValAtArrType
- newValAtArrType = vnStore->VNForExpr(compCurBB, TYP_REF);
+ newValAtArrType = vnStore->VNForExpr(compCurBB, TYP_MEM);
invalidateArray = true;
}
}
if (!invalidateArray)
{
- newValAtArr = vnStore->VNForMapStore(indType, hAtArrTypeAtArr, inxVN, newValAtInx);
- newValAtArrType = vnStore->VNForMapStore(TYP_REF, hAtArrType, arrVN, newValAtArr);
+ newValAtArr = vnStore->VNForMapStore(hAtArrTypeAtArr, inxVN, newValAtInx);
+ newValAtArrType = vnStore->VNForMapStore(hAtArrType, arrVN, newValAtArr);
}
#ifdef DEBUG
@@ -4298,7 +4328,7 @@ ValueNum Compiler::fgValueNumberArrIndexAssign(CORINFO_CLASS_HANDLE elemTypeEq,
printf(" hAtArrTypeAtArr " FMT_VN " is MapSelect(hAtArrType(" FMT_VN "), arr=" FMT_VN ")\n", hAtArrTypeAtArr,
hAtArrType, arrVN);
printf(" hAtArrTypeAtArrAtInx " FMT_VN " is MapSelect(hAtArrTypeAtArr(" FMT_VN "), inx=" FMT_VN "):%s\n",
- hAtArrTypeAtArrAtInx, hAtArrTypeAtArr, inxVN, varTypeName(arrElemType));
+ hAtArrTypeAtArrAtInx, hAtArrTypeAtArr, inxVN, ValueNumStore::VNMapTypeName(arrElemType));
if (!invalidateArray)
{
@@ -4317,7 +4347,7 @@ ValueNum Compiler::fgValueNumberArrIndexAssign(CORINFO_CLASS_HANDLE elemTypeEq,
}
#endif // DEBUG
- return vnStore->VNForMapStore(TYP_REF, fgCurMemoryVN[GcHeap], elemTypeEqVN, newValAtArrType);
+ return vnStore->VNForMapStore(fgCurMemoryVN[GcHeap], elemTypeEqVN, newValAtArrType);
}
ValueNum Compiler::fgValueNumberArrIndexVal(GenTree* tree, VNFuncApp* pFuncApp, ValueNum addrXvn)
@@ -4371,8 +4401,8 @@ ValueNum Compiler::fgValueNumberArrIndexVal(GenTree* tree,
else
{
ValueNum elemTypeEqVN = vnStore->VNForHandle(ssize_t(elemTypeEq), GTF_ICON_CLASS_HDL);
- ValueNum hAtArrType = vnStore->VNForMapSelect(VNK_Liberal, TYP_REF, fgCurMemoryVN[GcHeap], elemTypeEqVN);
- ValueNum hAtArrTypeAtArr = vnStore->VNForMapSelect(VNK_Liberal, TYP_REF, hAtArrType, arrVN);
+ ValueNum hAtArrType = vnStore->VNForMapSelect(VNK_Liberal, TYP_MEM, fgCurMemoryVN[GcHeap], elemTypeEqVN);
+ ValueNum hAtArrTypeAtArr = vnStore->VNForMapSelect(VNK_Liberal, TYP_MEM, hAtArrType, arrVN);
ValueNum wholeElem = vnStore->VNForMapSelect(VNK_Liberal, elemTyp, hAtArrTypeAtArr, inxVN);
#ifdef DEBUG
@@ -4548,18 +4578,25 @@ bool ValueNumStore::IsVNHandle(ValueNum vn)
// vrk - whether the new vn should swap, reverse, or both
//
// Returns:
-// vn for reversed/swapped comparsion, or NoVN.
+// vn for related comparsion, or NoVN.
//
// Note:
// If "vn" corresponds to (x > y), the resulting VN corresponds to
+// VRK_Same (x > y)
// VRK_Swap (y < x)
// VRK_Reverse (x <= y)
// VRK_SwapReverse (y >= x)
//
-// Will return NoVN for all float comparisons.
+// VRK_Same will always return the VN passed in.
+// For other relations, this method will return NoVN for all float comparisons.
//
ValueNum ValueNumStore::GetRelatedRelop(ValueNum vn, VN_RELATION_KIND vrk)
{
+ if (vrk == VN_RELATION_KIND::VRK_Same)
+ {
+ return vn;
+ }
+
if (vn == NoVN)
{
return NoVN;
@@ -4677,23 +4714,27 @@ ValueNum ValueNumStore::GetRelatedRelop(ValueNum vn, VN_RELATION_KIND vrk)
ValueNum newVN = VNForFunc(TYP_INT, newFunc, funcAttr.m_args[swap ? 1 : 0], funcAttr.m_args[swap ? 0 : 1]);
ValueNum result = VNWithExc(newVN, excepVN);
+ return result;
+}
+
#ifdef DEBUG
- if (m_pComp->verbose)
- {
- printf("%s of ", swap ? (reverse ? "swap-reverse" : "swap") : "reverse");
- m_pComp->vnPrint(vn, 1);
- printf(" => ");
- m_pComp->vnPrint(newVN, 1);
- if (result != newVN)
- {
- m_pComp->vnPrint(result, 1);
- }
- printf("\n");
+const char* ValueNumStore::VNRelationString(VN_RELATION_KIND vrk)
+{
+ switch (vrk)
+ {
+ case VN_RELATION_KIND::VRK_Same:
+ return "same";
+ case VN_RELATION_KIND::VRK_Reverse:
+ return "reversed";
+ case VN_RELATION_KIND::VRK_Swap:
+ return "swapped";
+ case VN_RELATION_KIND::VRK_SwapReverse:
+ return "swapped and reversed";
+ default:
+ return "unknown vn relation";
}
-#endif
-
- return result;
}
+#endif
bool ValueNumStore::IsVNRelop(ValueNum vn)
{
@@ -5807,11 +5848,6 @@ void ValueNumStore::vnDump(Compiler* comp, ValueNum vn, bool isPtr)
{
printf("void");
}
- else
- {
- assert(vn == VNForZeroMap());
- printf("zeroMap");
- }
break;
case TYP_BYREF:
printf("byrefVal");
@@ -5883,6 +5919,9 @@ void ValueNumStore::vnDump(Compiler* comp, ValueNum vn, bool isPtr)
case VNF_CastOvf:
vnDumpCast(comp, vn);
break;
+ case VNF_ZeroObj:
+ vnDumpZeroObj(comp, &funcApp);
+ break;
default:
printf("%s(", VNFuncName(funcApp.m_func));
@@ -6104,6 +6143,12 @@ void ValueNumStore::vnDumpCast(Compiler* comp, ValueNum castVN)
}
}
+void ValueNumStore::vnDumpZeroObj(Compiler* comp, VNFuncApp* zeroObj)
+{
+ printf("ZeroObj(");
+ comp->vnPrint(zeroObj->m_args[0], 0);
+ printf(": %s)", comp->eeGetClassName(CORINFO_CLASS_HANDLE(ConstantValue(zeroObj->m_args[0]))));
+}
#endif // DEBUG
// Static fields, methods.
@@ -6186,10 +6231,6 @@ void ValueNumStore::InitValueNumStoreStatics()
// SIMDIntrinsicInit has an entry of 2 for numArgs, but it only has one normal arg
ValueNumFuncSetArity(VNF_SIMD_Init, 1);
- // SIMDIntrinsicWidenHi has an entry of 2 for numArgs, but it only has one normal arg
- ValueNumFuncSetArity(VNF_SIMD_WidenHi, 1);
- // SIMDIntrinsicWidenLo has an entry of 2 for numArgs, but it only has one normal arg
- ValueNumFuncSetArity(VNF_SIMD_WidenLo, 1);
// Some SIMD intrinsic nodes have an extra VNF_SimdType arg
//
@@ -6235,7 +6276,7 @@ void ValueNumStore::InitValueNumStoreStatics()
#undef ValueNumFuncSetArity
- for (unsigned i = 0; i < _countof(genTreeOpsIllegalAsVNFunc); i++)
+ for (unsigned i = 0; i < ArrLen(genTreeOpsIllegalAsVNFunc); i++)
{
vnfOpAttribs[genTreeOpsIllegalAsVNFunc[i]] |= VNFOA_IllegalGenTreeOp;
}
@@ -6250,8 +6291,7 @@ const char* ValueNumStore::VNFuncNameArr[] = {
#undef ValueNumFuncDef
};
-// static
-const char* ValueNumStore::VNFuncName(VNFunc vnf)
+/* static */ const char* ValueNumStore::VNFuncName(VNFunc vnf)
{
if (vnf < VNF_Boundary)
{
@@ -6263,14 +6303,26 @@ const char* ValueNumStore::VNFuncName(VNFunc vnf)
}
}
+/* static */ const char* ValueNumStore::VNMapTypeName(var_types type)
+{
+ switch (type)
+ {
+ case TYP_HEAP:
+ return "heap";
+ case TYP_MEM:
+ return "mem";
+ default:
+ return varTypeName(type);
+ }
+}
+
static const char* s_reservedNameArr[] = {
"$VN.Recursive", // -2 RecursiveVN
"$VN.No", // -1 NoVN
"$VN.Null", // 0 VNForNull()
- "$VN.ZeroMap", // 1 VNForZeroMap()
- "$VN.ReadOnlyHeap", // 2 VNForROH()
- "$VN.Void", // 3 VNForVoid()
- "$VN.EmptyExcSet" // 4 VNForEmptyExcSet()
+ "$VN.ReadOnlyHeap", // 1 VNForROH()
+ "$VN.Void", // 2 VNForVoid()
+ "$VN.EmptyExcSet" // 3 VNForEmptyExcSet()
};
// Returns the string name of "vn" when it is a reserved value number, nullptr otherwise
@@ -6631,7 +6683,7 @@ void Compiler::fgValueNumber()
continue;
}
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ LclVarDsc* varDsc = lvaGetDesc(lclNum);
assert(varDsc->lvTracked);
if (varDsc->lvIsParam)
@@ -6695,7 +6747,8 @@ void Compiler::fgValueNumber()
if (isZeroed)
{
// By default we will zero init these LclVars
- initVal = vnStore->VNZeroForType(typ);
+ initVal = (typ == TYP_STRUCT) ? vnStore->VNForZeroObj(varDsc->GetStructHnd())
+ : vnStore->VNZeroForType(typ);
}
else
{
@@ -6716,7 +6769,7 @@ void Compiler::fgValueNumber()
}
}
// Give memory an initial value number (about which we know nothing).
- ValueNum memoryInitVal = vnStore->VNForFunc(TYP_REF, VNF_InitVal, vnStore->VNForIntCon(-1)); // Use -1 for memory.
+ ValueNum memoryInitVal = vnStore->VNForFunc(TYP_HEAP, VNF_InitVal, vnStore->VNForIntCon(-1)); // Use -1 for memory.
GetMemoryPerSsaData(SsaConfig::FIRST_SSA_NUM)->m_vnPair.SetBoth(memoryInitVal);
#ifdef DEBUG
if (verbose)
@@ -6853,8 +6906,8 @@ void Compiler::fgValueNumberBlock(BasicBlock* blk)
// Is there a phi for this block?
if (blk->bbMemorySsaPhiFunc[memoryKind] == nullptr)
{
- fgCurMemoryVN[memoryKind] = GetMemoryPerSsaData(blk->bbMemorySsaNumIn[memoryKind])->m_vnPair.GetLiberal();
- assert(fgCurMemoryVN[memoryKind] != ValueNumStore::NoVN);
+ ValueNum newMemoryVN = GetMemoryPerSsaData(blk->bbMemorySsaNumIn[memoryKind])->m_vnPair.GetLiberal();
+ fgSetCurrentMemoryVN(memoryKind, newMemoryVN);
}
else
{
@@ -6902,7 +6955,7 @@ void Compiler::fgValueNumberBlock(BasicBlock* blk)
unsigned phiArgSSANum = phiArgs->GetSsaNum();
ValueNum phiArgSSANumVN = vnStore->VNForIntCon(phiArgSSANum);
JITDUMP(" Building phi application: $%x = SSA# %d.\n", phiArgSSANumVN, phiArgSSANum);
- phiAppVN = vnStore->VNForFunc(TYP_REF, VNF_Phi, phiArgSSANumVN, phiAppVN);
+ phiAppVN = vnStore->VNForFunc(TYP_HEAP, VNF_Phi, phiArgSSANumVN, phiAppVN);
JITDUMP(" Building phi application: $%x = phi($%x, $%x).\n", phiAppVN, phiArgSSANumVN,
oldPhiAppVN);
phiArgs = phiArgs->m_nextArg;
@@ -6913,16 +6966,16 @@ void Compiler::fgValueNumberBlock(BasicBlock* blk)
}
else
{
- newMemoryVN = vnStore->VNForFunc(TYP_REF, VNF_PhiMemoryDef,
+ newMemoryVN = vnStore->VNForFunc(TYP_HEAP, VNF_PhiMemoryDef,
vnStore->VNForHandle(ssize_t(blk), GTF_EMPTY), phiAppVN);
}
}
GetMemoryPerSsaData(blk->bbMemorySsaNumIn[memoryKind])->m_vnPair.SetLiberal(newMemoryVN);
- fgCurMemoryVN[memoryKind] = newMemoryVN;
+ fgSetCurrentMemoryVN(memoryKind, newMemoryVN);
if ((memoryKind == GcHeap) && byrefStatesMatchGcHeapStates)
{
// Keep the CurMemoryVNs in sync
- fgCurMemoryVN[ByrefExposed] = newMemoryVN;
+ fgSetCurrentMemoryVN(ByrefExposed, newMemoryVN);
}
}
#ifdef DEBUG
@@ -7020,7 +7073,7 @@ ValueNum Compiler::fgMemoryVNForLoopSideEffects(MemoryKind memoryKind,
// If this loop has memory havoc effects, just use a new, unique VN.
if (optLoopTable[loopNum].lpLoopHasMemoryHavoc[memoryKind])
{
- ValueNum res = vnStore->VNForExpr(entryBlock, TYP_REF);
+ ValueNum res = vnStore->VNForExpr(entryBlock, TYP_HEAP);
#ifdef DEBUG
if (verbose)
{
@@ -7060,7 +7113,7 @@ ValueNum Compiler::fgMemoryVNForLoopSideEffects(MemoryKind memoryKind,
}
if (multipleNonLoopPreds)
{
- ValueNum res = vnStore->VNForExpr(entryBlock, TYP_REF);
+ ValueNum res = vnStore->VNForExpr(entryBlock, TYP_HEAP);
#ifdef DEBUG
if (verbose)
{
@@ -7105,12 +7158,11 @@ ValueNum Compiler::fgMemoryVNForLoopSideEffects(MemoryKind memoryKind,
}
#endif // DEBUG
- // Instance field maps get the placeholder TYP_REF - they do not represent "singular"
+ // Instance field maps get a placeholder type - they do not represent "singular"
// values. Static field maps, on the other hand, do, and so must be given proper types.
- var_types fldMapType = eeIsFieldStatic(fldHnd) ? eeGetFieldType(fldHnd) : TYP_REF;
+ var_types fldMapType = eeIsFieldStatic(fldHnd) ? eeGetFieldType(fldHnd) : TYP_MEM;
- newMemoryVN =
- vnStore->VNForMapStore(TYP_REF, newMemoryVN, fldHndVN, vnStore->VNForExpr(entryBlock, fldMapType));
+ newMemoryVN = vnStore->VNForMapStore(newMemoryVN, fldHndVN, vnStore->VNForExpr(entryBlock, fldMapType));
}
}
// Now do the array maps.
@@ -7141,8 +7193,8 @@ ValueNum Compiler::fgMemoryVNForLoopSideEffects(MemoryKind memoryKind,
#endif // DEBUG
ValueNum elemTypeVN = vnStore->VNForHandle(ssize_t(elemClsHnd), GTF_ICON_CLASS_HDL);
- ValueNum uniqueVN = vnStore->VNForExpr(entryBlock, TYP_REF);
- newMemoryVN = vnStore->VNForMapStore(TYP_REF, newMemoryVN, elemTypeVN, uniqueVN);
+ ValueNum uniqueVN = vnStore->VNForExpr(entryBlock, TYP_MEM);
+ newMemoryVN = vnStore->VNForMapStore(newMemoryVN, elemTypeVN, uniqueVN);
}
}
}
@@ -7169,13 +7221,13 @@ ValueNum Compiler::fgMemoryVNForLoopSideEffects(MemoryKind memoryKind,
void Compiler::fgMutateGcHeap(GenTree* tree DEBUGARG(const char* msg))
{
// Update the current memory VN, and if we're tracking the heap SSA # caused by this node, record it.
- recordGcHeapStore(tree, vnStore->VNForExpr(compCurBB, TYP_REF) DEBUGARG(msg));
+ recordGcHeapStore(tree, vnStore->VNForExpr(compCurBB, TYP_HEAP) DEBUGARG(msg));
}
void Compiler::fgMutateAddressExposedLocal(GenTree* tree DEBUGARG(const char* msg))
{
// Update the current ByrefExposed VN, and if we're tracking the heap SSA # caused by this node, record it.
- recordAddressExposedLocalStore(tree, vnStore->VNForExpr(compCurBB) DEBUGARG(msg));
+ recordAddressExposedLocalStore(tree, vnStore->VNForExpr(compCurBB, TYP_HEAP) DEBUGARG(msg));
}
void Compiler::recordGcHeapStore(GenTree* curTree, ValueNum gcHeapVN DEBUGARG(const char* msg))
@@ -7183,13 +7235,13 @@ void Compiler::recordGcHeapStore(GenTree* curTree, ValueNum gcHeapVN DEBUGARG(co
// bbMemoryDef must include GcHeap for any block that mutates the GC Heap
// and GC Heap mutations are also ByrefExposed mutations
assert((compCurBB->bbMemoryDef & memoryKindSet(GcHeap, ByrefExposed)) == memoryKindSet(GcHeap, ByrefExposed));
- fgCurMemoryVN[GcHeap] = gcHeapVN;
+ fgSetCurrentMemoryVN(GcHeap, gcHeapVN);
if (byrefStatesMatchGcHeapStates)
{
// Since GcHeap and ByrefExposed share SSA nodes, they need to share
// value numbers too.
- fgCurMemoryVN[ByrefExposed] = gcHeapVN;
+ fgSetCurrentMemoryVN(ByrefExposed, gcHeapVN);
}
else
{
@@ -7197,7 +7249,7 @@ void Compiler::recordGcHeapStore(GenTree* curTree, ValueNum gcHeapVN DEBUGARG(co
// assume that this GcHeap store may alias any byref load/store, so don't
// bother trying to record the map/select stuff, and instead just an opaque VN
// for ByrefExposed
- fgCurMemoryVN[ByrefExposed] = vnStore->VNForExpr(compCurBB);
+ fgSetCurrentMemoryVN(ByrefExposed, vnStore->VNForExpr(compCurBB, TYP_HEAP));
}
#ifdef DEBUG
@@ -7222,7 +7274,7 @@ void Compiler::recordAddressExposedLocalStore(GenTree* curTree, ValueNum memoryV
// bbMemoryDef must include ByrefExposed for any block that mutates an address-exposed local
assert((compCurBB->bbMemoryDef & memoryKindSet(ByrefExposed)) != 0);
- fgCurMemoryVN[ByrefExposed] = memoryVN;
+ fgSetCurrentMemoryVN(ByrefExposed, memoryVN);
#ifdef DEBUG
if (verbose)
@@ -7236,6 +7288,13 @@ void Compiler::recordAddressExposedLocalStore(GenTree* curTree, ValueNum memoryV
fgValueNumberRecordMemorySsa(ByrefExposed, curTree);
}
+void Compiler::fgSetCurrentMemoryVN(MemoryKind memoryKind, ValueNum newMemoryVN)
+{
+ assert(vnStore->VNIsValid(newMemoryVN));
+ assert(vnStore->TypeOfVN(newMemoryVN) == TYP_HEAP);
+ fgCurMemoryVN[memoryKind] = newMemoryVN;
+}
+
void Compiler::fgValueNumberRecordMemorySsa(MemoryKind memoryKind, GenTree* tree)
{
unsigned ssaNum;
@@ -7476,7 +7535,7 @@ void Compiler::fgValueNumberAssignment(GenTreeOp* tree)
// through address-taken locals in regions of code with no calls or byref
// writes.
// For now, just use a new opaque VN.
- ValueNum heapVN = vnStore->VNForExpr(compCurBB);
+ ValueNum heapVN = vnStore->VNForExpr(compCurBB, TYP_HEAP);
recordAddressExposedLocalStore(tree, heapVN DEBUGARG("local assign"));
}
#ifdef DEBUG
@@ -7559,7 +7618,7 @@ void Compiler::fgValueNumberAssignment(GenTreeOp* tree)
// As with GT_LCL_VAR, we could probably use MapStore here and MapSelect at corresponding
// loads, but to do so would have to identify the subset of address-exposed locals
// whose fields can be disambiguated.
- ValueNum heapVN = vnStore->VNForExpr(compCurBB);
+ ValueNum heapVN = vnStore->VNForExpr(compCurBB, TYP_HEAP);
recordAddressExposedLocalStore(tree, heapVN DEBUGARG("local field assign"));
}
}
@@ -7685,7 +7744,7 @@ void Compiler::fgValueNumberAssignment(GenTreeOp* tree)
// through address-taken locals in regions of code with no calls or byref
// writes.
// For now, just use a new opaque VN.
- ValueNum heapVN = vnStore->VNForExpr(compCurBB);
+ ValueNum heapVN = vnStore->VNForExpr(compCurBB, TYP_HEAP);
recordAddressExposedLocalStore(tree, heapVN DEBUGARG("PtrToLoc indir"));
}
}
@@ -7783,82 +7842,79 @@ void Compiler::fgValueNumberAssignment(GenTreeOp* tree)
assert(fldSeq != nullptr);
}
- // Get a field sequence for just the first field in the sequence
- //
- FieldSeqNode* firstFieldOnly = GetFieldSeqStore()->CreateSingleton(fldSeq->m_fieldHnd);
+ // The value number from the rhs of the assignment
+ ValueNum storeVal = rhsVNPair.GetLiberal();
+ ValueNum newHeapVN = ValueNumStore::NoVN;
- // The final field in the sequence will need to match the 'indType'
+ // We will check that the final field in the sequence matches 'indType'.
var_types indType = lhs->TypeGet();
- ValueNum fldMapVN =
- vnStore->VNApplySelectors(VNK_Liberal, fgCurMemoryVN[GcHeap], firstFieldOnly);
-
- // The type of the field is "struct" if there are more fields in the sequence,
- // otherwise it is the type returned from VNApplySelectors above.
- var_types firstFieldType = vnStore->TypeOfVN(fldMapVN);
-
- // The value number from the rhs of the assignment
- ValueNum storeVal = rhsVNPair.GetLiberal();
- ValueNum newFldMapVN = ValueNumStore::NoVN;
// when (obj != nullptr) we have an instance field, otherwise a static field
// when (staticOffset != nullptr) it represents a offset into a static or the call to
// Shared Static Base
if ((obj != nullptr) || (staticOffset != nullptr))
{
- ValueNum valAtAddr = fldMapVN;
- ValueNum normVal = ValueNumStore::NoVN;
+ var_types firstFieldType;
+ ValueNum firstFieldSelectorVN =
+ vnStore->VNForFieldSelector(fldSeq->GetFieldHandle(), &firstFieldType);
+ // Construct the "field map" VN. It represents memory state of the first field
+ // of all objects on the heap. This is our primary map.
+ ValueNum fldMapVN = vnStore->VNForMapSelect(VNK_Liberal, TYP_MEM, fgCurMemoryVN[GcHeap],
+ firstFieldSelectorVN);
+
+ ValueNum firstFieldValueSelectorVN = ValueNumStore::NoVN;
if (obj != nullptr)
{
- // Unpack, Norm,Exc for 'obj'
- ValueNum vnObjExcSet;
- vnStore->VNUnpackExc(obj->gtVNPair.GetLiberal(), &normVal, &vnObjExcSet);
- vnExcSet = vnStore->VNExcSetUnion(vnExcSet, vnObjExcSet);
-
- // construct the ValueNumber for 'fldMap at obj'
- valAtAddr = vnStore->VNForMapSelect(VNK_Liberal, firstFieldType, fldMapVN, normVal);
+ firstFieldValueSelectorVN = vnStore->VNLiberalNormalValue(obj->gtVNPair);
}
else // (staticOffset != nullptr)
{
- // construct the ValueNumber for 'fldMap at staticOffset'
- normVal = vnStore->VNLiberalNormalValue(staticOffset->gtVNPair);
- valAtAddr = vnStore->VNForMapSelect(VNK_Liberal, firstFieldType, fldMapVN, normVal);
+ firstFieldValueSelectorVN = vnStore->VNLiberalNormalValue(staticOffset->gtVNPair);
+ }
+
+ ValueNum newFirstFieldValueVN = ValueNumStore::NoVN;
+ // Optimization: avoid traversting the maps for the value of the first field if
+ // we do not need it, which is the case if the rest of the field sequence is empty.
+ if (fldSeq->m_next == nullptr)
+ {
+ newFirstFieldValueVN = vnStore->VNApplySelectorsAssignTypeCoerce(storeVal, indType);
}
- // Now get rid of any remaining struct field dereferences. (if they exist)
- if (fldSeq->m_next)
+ else
{
- storeVal = vnStore->VNApplySelectorsAssign(VNK_Liberal, valAtAddr, fldSeq->m_next,
- storeVal, indType);
+ // Construct the ValueNumber for fldMap[obj/offset]. This (struct)
+ // map represents the specific field we're looking to store to.
+ ValueNum firstFieldValueVN =
+ vnStore->VNForMapSelect(VNK_Liberal, firstFieldType, fldMapVN,
+ firstFieldValueSelectorVN);
+
+ // Construct the maps updating the rest of the fields in the sequence.
+ newFirstFieldValueVN =
+ vnStore->VNApplySelectorsAssign(VNK_Liberal, firstFieldValueVN, fldSeq->m_next,
+ storeVal, indType);
}
- // From which we can construct the new ValueNumber for 'fldMap at normVal'
- newFldMapVN =
- vnStore->VNForMapStore(vnStore->TypeOfVN(fldMapVN), fldMapVN, normVal, storeVal);
+ // Finally, construct the new field map...
+ ValueNum newFldMapVN =
+ vnStore->VNForMapStore(fldMapVN, firstFieldValueSelectorVN, newFirstFieldValueVN);
+
+ // ...and a new value for the heap.
+ newHeapVN =
+ vnStore->VNForMapStore(fgCurMemoryVN[GcHeap], firstFieldSelectorVN, newFldMapVN);
}
else
{
- // plain static field
-
- // Now get rid of any remaining struct field dereferences. (if they exist)
- if (fldSeq->m_next)
- {
- storeVal = vnStore->VNApplySelectorsAssign(VNK_Liberal, fldMapVN, fldSeq->m_next,
- storeVal, indType);
- }
-
- newFldMapVN = vnStore->VNApplySelectorsAssign(VNK_Liberal, fgCurMemoryVN[GcHeap], fldSeq,
- storeVal, indType);
+ // Plain static field.
+ newHeapVN = vnStore->VNApplySelectorsAssign(VNK_Liberal, fgCurMemoryVN[GcHeap], fldSeq,
+ storeVal, indType);
}
// It is not strictly necessary to set the lhs value number,
// but the dumps read better with it set to the 'storeVal' that we just computed
lhs->gtVNPair.SetBoth(storeVal);
- // Update the field map for firstField in GcHeap to this new value.
- ValueNum heapVN = vnStore->VNApplySelectorsAssign(VNK_Liberal, fgCurMemoryVN[GcHeap],
- firstFieldOnly, newFldMapVN, indType);
-
- recordGcHeapStore(tree, heapVN DEBUGARG("StoreField"));
+ // Update the GcHeap value.
+ recordGcHeapStore(tree, newHeapVN DEBUGARG("StoreField"));
}
}
else
@@ -7874,7 +7930,7 @@ void Compiler::fgValueNumberAssignment(GenTreeOp* tree)
// through address-taken locals in regions of code with no calls or byref
// writes.
// For now, just use a new opaque VN.
- ValueNum memoryVN = vnStore->VNForExpr(compCurBB);
+ ValueNum memoryVN = vnStore->VNForExpr(compCurBB, TYP_HEAP);
recordAddressExposedLocalStore(tree, memoryVN DEBUGARG("PtrToLoc indir"));
}
else if (!isLocal)
@@ -7957,39 +8013,38 @@ void Compiler::fgValueNumberBlockAssignment(GenTree* tree)
// Should not have been recorded as updating the GC heap.
assert(!GetMemorySsaMap(GcHeap)->Lookup(tree));
- unsigned lclNum = lclVarTree->GetLclNum();
-
unsigned lclDefSsaNum = GetSsaNumForLocalVarDef(lclVarTree);
+
// Ignore vars that we excluded from SSA (for example, because they're address-exposed). They don't have
// SSA names in which to store VN's on defs. We'll yield unique VN's when we read from them.
- if (lvaInSsa(lclNum) && lclDefSsaNum != SsaConfig::RESERVED_SSA_NUM)
+ if (lclDefSsaNum != SsaConfig::RESERVED_SSA_NUM)
{
+ LclVarDsc* lclVarDsc = lvaGetDesc(lclVarTree);
+
// Should not have been recorded as updating ByrefExposed.
assert(!GetMemorySsaMap(ByrefExposed)->Lookup(tree));
- ValueNum initBlkVN = ValueNumStore::NoVN;
- GenTree* initConst = rhs;
- if (isEntire && initConst->OperGet() == GT_CNS_INT)
+ ValueNum lclVarVN = ValueNumStore::NoVN;
+ if (isEntire && rhs->IsIntegralConst(0))
{
- unsigned initVal = 0xFF & (unsigned)initConst->AsIntConCommon()->IconValue();
- if (initVal == 0)
- {
- initBlkVN = vnStore->VNZeroForType(lclVarTree->TypeGet());
- }
+ // Note that it is possible to see pretty much any kind of type for the local
+ // (not just TYP_STRUCT) here because of the ASG(BLK(ADDR(LCL_VAR/FLD)), 0) form.
+ lclVarVN = (lclVarDsc->TypeGet() == TYP_STRUCT) ? vnStore->VNForZeroObj(lclVarDsc->GetStructHnd())
+ : vnStore->VNZeroForType(lclVarDsc->TypeGet());
+ }
+ else
+ {
+ // Non-zero block init is very rare so we'll use a simple, unique VN here.
+ lclVarVN = vnStore->VNForExpr(compCurBB, lclVarDsc->TypeGet());
}
- ValueNum lclVarVN = (initBlkVN != ValueNumStore::NoVN)
- ? initBlkVN
- : vnStore->VNForExpr(compCurBB, var_types(lvaTable[lclNum].lvType));
- lvaTable[lclNum].GetPerSsaData(lclDefSsaNum)->m_vnPair.SetBoth(lclVarVN);
+ lclVarDsc->GetPerSsaData(lclDefSsaNum)->m_vnPair.SetBoth(lclVarVN);
#ifdef DEBUG
if (verbose)
{
- printf("N%03u ", tree->gtSeqNum);
+ printf("Tree ");
Compiler::printTreeID(tree);
- printf(" ");
- gtDispNodeName(tree);
- printf(" V%02u/%d => ", lclNum, lclDefSsaNum);
+ printf(" assigned VN to local var V%02u/%d: ", lclVarTree->GetLclNum(), lclDefSsaNum);
vnPrint(lclVarVN, 1);
printf("\n");
}
@@ -8080,7 +8135,7 @@ void Compiler::fgValueNumberBlockAssignment(GenTree* tree)
if (rhs->IsLocalExpr(this, &rhsLclVarTree, &rhsFldSeq))
{
unsigned rhsLclNum = rhsLclVarTree->GetLclNum();
- rhsVarDsc = &lvaTable[rhsLclNum];
+ rhsVarDsc = lvaGetDesc(rhsLclNum);
if (!lvaInSsa(rhsLclNum) || !rhsLclVarTree->HasSsaName() ||
rhsFldSeq == FieldSeqStore::NotAField())
{
@@ -8107,7 +8162,7 @@ void Compiler::fgValueNumberBlockAssignment(GenTree* tree)
if (srcAddr->IsLocalAddrExpr(this, &rhsLclVarTree, &rhsFldSeq))
{
unsigned rhsLclNum = rhsLclVarTree->GetLclNum();
- rhsVarDsc = &lvaTable[rhsLclNum];
+ rhsVarDsc = lvaGetDesc(rhsLclNum);
if (!lvaInSsa(rhsLclNum) || !rhsLclVarTree->HasSsaName() ||
rhsFldSeq == FieldSeqStore::NotAField())
{
@@ -8374,12 +8429,12 @@ void Compiler::fgValueNumberTree(GenTree* tree)
{
GenTreeLclVarCommon* lcl = tree->AsLclVarCommon();
unsigned lclNum = lcl->GetLclNum();
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ LclVarDsc* varDsc = lvaGetDesc(lclNum);
if (varDsc->CanBeReplacedWithItsField(this))
{
lclNum = varDsc->lvFieldLclStart;
- varDsc = &lvaTable[lclNum];
+ varDsc = lvaGetDesc(lclNum);
}
// Do we have a Use (read) of the LclVar?
@@ -8544,9 +8599,8 @@ void Compiler::fgValueNumberTree(GenTree* tree)
// forms (assignment, or initBlk or copyBlk).
if (((lclFld->gtFlags & GTF_VAR_DEF) == 0) || (lclFld->gtFlags & GTF_VAR_USEASG))
{
- unsigned lclNum = lclFld->GetLclNum();
unsigned ssaNum = lclFld->GetSsaNum();
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ LclVarDsc* varDsc = lvaGetDesc(lclFld);
var_types indType = tree->TypeGet();
if ((lclFld->GetFieldSeq() == FieldSeqStore::NotAField()) || !lvaInSsa(lclFld->GetLclNum()) ||
@@ -8914,9 +8968,8 @@ void Compiler::fgValueNumberTree(GenTree* tree)
if (addr->IsLocalAddrExpr(this, &lclVarTree, &localFldSeq) && lvaInSsa(lclVarTree->GetLclNum()) &&
lclVarTree->HasSsaName())
{
- unsigned lclNum = lclVarTree->GetLclNum();
unsigned ssaNum = lclVarTree->GetSsaNum();
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ LclVarDsc* varDsc = lvaGetDesc(lclVarTree);
if ((localFldSeq == FieldSeqStore::NotAField()) || (localFldSeq == nullptr))
{
@@ -8968,10 +9021,11 @@ void Compiler::fgValueNumberTree(GenTree* tree)
}
else if (fldSeq2 != nullptr)
{
- // Get the first (instance or static) field from field seq. GcHeap[field] will yield the "field
- // map".
- CLANG_FORMAT_COMMENT_ANCHOR;
-
+ if (fldSeq2->IsFirstElemFieldSeq())
+ {
+ fldSeq2 = fldSeq2->m_next;
+ assert(fldSeq2 != nullptr);
+ }
#ifdef DEBUG
CORINFO_CLASS_HANDLE fldCls = info.compCompHnd->getFieldClass(fldSeq2->m_fieldHnd);
if (obj != nullptr)
@@ -8983,42 +9037,38 @@ void Compiler::fgValueNumberTree(GenTree* tree)
}
#endif // DEBUG
- // Get a field sequence for just the first field in the sequence
- //
- FieldSeqNode* firstFieldOnly = GetFieldSeqStore()->CreateSingleton(fldSeq2->m_fieldHnd);
- size_t structSize = 0;
- ValueNum fldMapVN =
- vnStore->VNApplySelectors(VNK_Liberal, fgCurMemoryVN[GcHeap], firstFieldOnly, &structSize);
+ // The size of the ultimate value we will select, if it is of a struct type.
+ size_t structSize = 0;
- // The final field in the sequence will need to match the 'indType'
- var_types indType = tree->TypeGet();
+ // Get the selector for the first field.
+ var_types firstFieldType;
+ ValueNum firstFieldSelectorVN =
+ vnStore->VNForFieldSelector(fldSeq2->GetFieldHandle(), &firstFieldType, &structSize);
- // The type of the field is "struct" if there are more fields in the sequence,
- // otherwise it is the type returned from VNApplySelectors above.
- var_types firstFieldType = vnStore->TypeOfVN(fldMapVN);
+ ValueNum fldMapVN =
+ vnStore->VNForMapSelect(VNK_Liberal, TYP_MEM, fgCurMemoryVN[GcHeap], firstFieldSelectorVN);
- ValueNum valAtAddr = fldMapVN;
+ ValueNum firstFieldValueSelectorVN;
if (obj != nullptr)
{
- // construct the ValueNumber for 'fldMap at obj'
- ValueNum objNormVal = vnStore->VNLiberalNormalValue(obj->gtVNPair);
- valAtAddr = vnStore->VNForMapSelect(VNK_Liberal, firstFieldType, fldMapVN, objNormVal);
+ firstFieldValueSelectorVN = vnStore->VNLiberalNormalValue(obj->gtVNPair);
}
- else if (staticOffset != nullptr)
+ else
{
- // construct the ValueNumber for 'fldMap at staticOffset'
- ValueNum offsetNormVal = vnStore->VNLiberalNormalValue(staticOffset->gtVNPair);
- valAtAddr = vnStore->VNForMapSelect(VNK_Liberal, firstFieldType, fldMapVN, offsetNormVal);
+ assert(staticOffset != nullptr);
+ firstFieldValueSelectorVN = vnStore->VNLiberalNormalValue(staticOffset->gtVNPair);
}
- // Now get rid of any remaining struct field dereferences.
- if (fldSeq2->m_next)
- {
- valAtAddr = vnStore->VNApplySelectors(VNK_Liberal, valAtAddr, fldSeq2->m_next, &structSize);
- }
- valAtAddr = vnStore->VNApplySelectorsTypeCheck(valAtAddr, indType, structSize);
+ // Construct the value number for fldMap[obj/offset].
+ ValueNum firstFieldValueVN =
+ vnStore->VNForMapSelect(VNK_Liberal, firstFieldType, fldMapVN, firstFieldValueSelectorVN);
+
+ // Finally, account for the rest of the fields in the sequence.
+ ValueNum valueVN =
+ vnStore->VNApplySelectors(VNK_Liberal, firstFieldValueVN, fldSeq2->m_next, &structSize);
- tree->gtVNPair.SetLiberal(valAtAddr);
+ valueVN = vnStore->VNApplySelectorsTypeCheck(valueVN, tree->TypeGet(), structSize);
+ tree->gtVNPair.SetLiberal(valueVN);
// The conservative value is a new, unique VN.
tree->gtVNPair.SetConservative(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
@@ -9050,21 +9100,6 @@ void Compiler::fgValueNumberTree(GenTree* tree)
{
fgValueNumberIntrinsic(tree);
}
-
-#ifdef FEATURE_SIMD
- else if (tree->OperGet() == GT_SIMD)
- {
- fgValueNumberSimd(tree);
- }
-#endif // FEATURE_SIMD
-
-#ifdef FEATURE_HW_INTRINSICS
- else if (tree->OperGet() == GT_HWINTRINSIC)
- {
- fgValueNumberHWIntrinsic(tree);
- }
-#endif // FEATURE_HW_INTRINSICS
-
else // Look up the VNFunc for the node
{
VNFunc vnf = GetVNFuncForNode(tree);
@@ -9119,7 +9154,7 @@ void Compiler::fgValueNumberTree(GenTree* tree)
ValueNumPair op2VNPair;
if (tree->AsOp()->gtOp2 == nullptr)
{
- // Handle any GT_LIST nodes as they can have a nullptr for op2.
+ // Handle any GT_LEA nodes as they can have a nullptr for op2.
op2VNPair.SetBoth(ValueNumStore::VNForNull());
}
else
@@ -9277,7 +9312,6 @@ void Compiler::fgValueNumberTree(GenTree* tree)
}
case GT_JTRUE:
- case GT_LIST:
// These nodes never need to have a ValueNumber
tree->gtVNPair.SetBoth(ValueNumStore::NoVN);
break;
@@ -9311,6 +9345,18 @@ void Compiler::fgValueNumberTree(GenTree* tree)
fgValueNumberCall(tree->AsCall());
break;
+#ifdef FEATURE_SIMD
+ case GT_SIMD:
+ fgValueNumberSimd(tree->AsSIMD());
+ break;
+#endif // FEATURE_SIMD
+
+#ifdef FEATURE_HW_INTRINSICS
+ case GT_HWINTRINSIC:
+ fgValueNumberHWIntrinsic(tree->AsHWIntrinsic());
+ break;
+#endif // FEATURE_HW_INTRINSICS
+
case GT_CMPXCHG: // Specialop
{
// For CMPXCHG and other intrinsics add an arbitrary side effect on GcHeap/ByrefExposed.
@@ -9396,17 +9442,13 @@ void Compiler::fgValueNumberIntrinsic(GenTree* tree)
vnStore->VNPWithExc(vnStore->EvalMathFuncUnary(tree->TypeGet(), intrinsic->gtIntrinsicName, arg0VNP),
arg0VNPx);
}
- else if (!intrinsic->AsOp()->gtOp1->OperIsList())
+ else
{
ValueNumPair newVNP =
vnStore->EvalMathFuncBinary(tree->TypeGet(), intrinsic->gtIntrinsicName, arg0VNP, arg1VNP);
ValueNumPair excSet = vnStore->VNPExcSetUnion(arg0VNPx, arg1VNPx);
intrinsic->gtVNPair = vnStore->VNPWithExc(newVNP, excSet);
}
- else
- {
- unreached();
- }
}
else
{
@@ -9418,30 +9460,24 @@ void Compiler::fgValueNumberIntrinsic(GenTree* tree)
#ifdef FEATURE_SIMD
// Does value-numbering for a GT_SIMD node.
-void Compiler::fgValueNumberSimd(GenTree* tree)
+void Compiler::fgValueNumberSimd(GenTreeSIMD* tree)
{
- assert(tree->OperGet() == GT_SIMD);
- GenTreeSIMD* simdNode = tree->AsSIMD();
- assert(simdNode != nullptr);
-
VNFunc simdFunc = GetVNFuncForNode(tree);
ValueNumPair excSetPair;
ValueNumPair normalPair;
// There are some SIMD operations that have zero args, i.e. NI_Vector128_Zero
- if (tree->AsOp()->gtOp1 == nullptr)
+ if (tree->GetOperandCount() == 0)
{
excSetPair = ValueNumStore::VNPForEmptyExcSet();
normalPair = vnStore->VNPairForFunc(tree->TypeGet(), simdFunc);
}
- else if (tree->AsOp()->gtOp1->OperIs(GT_LIST))
+ // TODO-List-Cleanup: the "tree->GetSIMDIntrinsicId() == SIMDIntrinsicInitN" case is a quirk
+ // to get zero diffs - Vector2(float, float) was imported with lists - remove it.
+ else if ((tree->GetOperandCount() > 2) || (tree->GetSIMDIntrinsicId() == SIMDIntrinsicInitN))
{
- assert(tree->AsOp()->gtOp2 == nullptr);
-
- // We have a SIMD node in the GT_LIST form with 3 or more args
- // For now we will generate a unique value number for this case.
-
- // Generate a unique VN
+ // We have a SIMD node with 3 or more args. To retain the
+ // previous behavior, we will generate a unique VN for this case.
tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
return;
}
@@ -9450,25 +9486,25 @@ void Compiler::fgValueNumberSimd(GenTree* tree)
ValueNumPair resvnp = ValueNumPair();
ValueNumPair op1vnp;
ValueNumPair op1Xvnp;
- vnStore->VNPUnpackExc(tree->AsOp()->gtOp1->gtVNPair, &op1vnp, &op1Xvnp);
+ vnStore->VNPUnpackExc(tree->Op(1)->gtVNPair, &op1vnp, &op1Xvnp);
ValueNum addrVN = ValueNumStore::NoVN;
- bool isMemoryLoad = simdNode->OperIsMemoryLoad();
+ bool isMemoryLoad = tree->OperIsMemoryLoad();
if (isMemoryLoad)
{
// Currently the only SIMD operation with MemoryLoad sematics is SIMDIntrinsicInitArray
// and it has to be handled specially since it has an optional op2
//
- assert(simdNode->gtSIMDIntrinsicID == SIMDIntrinsicInitArray);
+ assert(tree->GetSIMDIntrinsicId() == SIMDIntrinsicInitArray);
// rationalize rewrites this as an explicit load with op1 as the base address
assert(tree->OperIsImplicitIndir());
ValueNumPair op2vnp;
- if (tree->AsOp()->gtOp2 == nullptr)
+ if (tree->GetOperandCount() != 2)
{
- // a nullptr for op2 means that we have an impicit index of zero
+ // No op2 means that we have an impicit index of zero
op2vnp = ValueNumPair(vnStore->VNZeroForType(TYP_INT), vnStore->VNZeroForType(TYP_INT));
excSetPair = op1Xvnp;
@@ -9476,7 +9512,7 @@ void Compiler::fgValueNumberSimd(GenTree* tree)
else // We have an explicit index in op2
{
ValueNumPair op2Xvnp;
- vnStore->VNPUnpackExc(tree->AsOp()->gtOp2->gtVNPair, &op2vnp, &op2Xvnp);
+ vnStore->VNPUnpackExc(tree->Op(2)->gtVNPair, &op2vnp, &op2Xvnp);
excSetPair = vnStore->VNPExcSetUnion(op1Xvnp, op2Xvnp);
}
@@ -9488,7 +9524,7 @@ void Compiler::fgValueNumberSimd(GenTree* tree)
if (verbose)
{
printf("Treating GT_SIMD %s as a ByrefExposed load , addrVN is ",
- simdIntrinsicNames[simdNode->gtSIMDIntrinsicID]);
+ simdIntrinsicNames[tree->GetSIMDIntrinsicId()]);
vnPrint(addrVN, 0);
}
#endif // DEBUG
@@ -9499,17 +9535,15 @@ void Compiler::fgValueNumberSimd(GenTree* tree)
tree->gtVNPair.SetLiberal(loadVN);
tree->gtVNPair.SetConservative(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
tree->gtVNPair = vnStore->VNPWithExc(tree->gtVNPair, excSetPair);
- fgValueNumberAddExceptionSetForIndirection(tree, tree->AsOp()->gtOp1);
+ fgValueNumberAddExceptionSetForIndirection(tree, tree->Op(1));
return;
}
- bool encodeResultType = vnEncodesResultTypeForSIMDIntrinsic(simdNode->gtSIMDIntrinsicID);
+ bool encodeResultType = vnEncodesResultTypeForSIMDIntrinsic(tree->GetSIMDIntrinsicId());
if (encodeResultType)
{
- ValueNum vnSize = vnStore->VNForIntCon(simdNode->GetSimdSize());
- ValueNum vnBaseType = vnStore->VNForIntCon(INT32(simdNode->GetSimdBaseType()));
- ValueNum simdTypeVN = vnStore->VNForFunc(TYP_REF, VNF_SimdType, vnSize, vnBaseType);
+ ValueNum simdTypeVN = vnStore->VNForSimdType(tree->GetSimdSize(), tree->GetSimdBaseType());
resvnp.SetBoth(simdTypeVN);
#ifdef DEBUG
@@ -9522,9 +9556,9 @@ void Compiler::fgValueNumberSimd(GenTree* tree)
#endif
}
- if (tree->AsOp()->gtOp2 == nullptr)
+ if (tree->GetOperandCount() == 1)
{
- // Unary SIMD nodes have a nullptr for op2.
+ // A unary SIMD node.
excSetPair = op1Xvnp;
if (encodeResultType)
{
@@ -9541,7 +9575,7 @@ void Compiler::fgValueNumberSimd(GenTree* tree)
{
ValueNumPair op2vnp;
ValueNumPair op2Xvnp;
- vnStore->VNPUnpackExc(tree->AsOp()->gtOp2->gtVNPair, &op2vnp, &op2Xvnp);
+ vnStore->VNPUnpackExc(tree->Op(2)->gtVNPair, &op2vnp, &op2Xvnp);
excSetPair = vnStore->VNPExcSetUnion(op1Xvnp, op2Xvnp);
if (encodeResultType)
@@ -9562,33 +9596,26 @@ void Compiler::fgValueNumberSimd(GenTree* tree)
#ifdef FEATURE_HW_INTRINSICS
// Does value-numbering for a GT_HWINTRINSIC node
-void Compiler::fgValueNumberHWIntrinsic(GenTree* tree)
+void Compiler::fgValueNumberHWIntrinsic(GenTreeHWIntrinsic* tree)
{
- assert(tree->OperGet() == GT_HWINTRINSIC);
- GenTreeHWIntrinsic* hwIntrinsicNode = tree->AsHWIntrinsic();
- assert(hwIntrinsicNode != nullptr);
-
// For safety/correctness we must mutate the global heap valuenumber
// for any HW intrinsic that performs a memory store operation
- if (hwIntrinsicNode->OperIsMemoryStore())
+ if (tree->OperIsMemoryStore())
{
fgMutateGcHeap(tree DEBUGARG("HWIntrinsic - MemoryStore"));
}
- if ((tree->AsOp()->gtOp1 != nullptr) && tree->gtGetOp1()->OperIs(GT_LIST))
+ if (tree->GetOperandCount() > 2)
{
- // TODO-CQ: allow intrinsics with GT_LIST to be properly VN'ed, it will
+ // TODO-CQ: allow intrinsics with > 2 operands to be properly VN'ed, it will
// allow use to process things like Vector128.Create(1,2,3,4) etc.
- // Generate unique VN for now.
+ // Generate unique VN for now to retaing previois behavior.
tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
return;
}
- // We don't expect GT_LIST to be in the second op
- assert((tree->AsOp()->gtOp2 == nullptr) || !tree->gtGetOp2()->OperIs(GT_LIST));
-
VNFunc func = GetVNFuncForNode(tree);
- bool isMemoryLoad = hwIntrinsicNode->OperIsMemoryLoad();
+ bool isMemoryLoad = tree->OperIsMemoryLoad();
// If we have a MemoryLoad operation we will use the fgValueNumberByrefExposedLoad
// method to assign a value number that depends upon fgCurMemoryVN[ByrefExposed] ValueNumber
@@ -9597,7 +9624,7 @@ void Compiler::fgValueNumberHWIntrinsic(GenTree* tree)
{
ValueNumPair op1vnp;
ValueNumPair op1Xvnp;
- vnStore->VNPUnpackExc(tree->AsOp()->gtOp1->gtVNPair, &op1vnp, &op1Xvnp);
+ vnStore->VNPUnpackExc(tree->Op(1)->gtVNPair, &op1vnp, &op1Xvnp);
// The addrVN incorporates both op1's ValueNumber and the func operation
// The func is used because operations such as LoadLow and LoadHigh perform
@@ -9612,11 +9639,11 @@ void Compiler::fgValueNumberHWIntrinsic(GenTree* tree)
tree->gtVNPair.SetLiberal(loadVN);
tree->gtVNPair.SetConservative(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
tree->gtVNPair = vnStore->VNPWithExc(tree->gtVNPair, op1Xvnp);
- fgValueNumberAddExceptionSetForIndirection(tree, tree->AsOp()->gtOp1);
+ fgValueNumberAddExceptionSetForIndirection(tree, tree->Op(1));
return;
}
- bool encodeResultType = vnEncodesResultTypeForHWIntrinsic(hwIntrinsicNode->gtHWIntrinsicId);
+ bool encodeResultType = vnEncodesResultTypeForHWIntrinsic(tree->GetHWIntrinsicId());
ValueNumPair excSetPair = ValueNumStore::VNPForEmptyExcSet();
ValueNumPair normalPair;
@@ -9624,9 +9651,7 @@ void Compiler::fgValueNumberHWIntrinsic(GenTree* tree)
if (encodeResultType)
{
- ValueNum vnSize = vnStore->VNForIntCon(hwIntrinsicNode->GetSimdSize());
- ValueNum vnBaseType = vnStore->VNForIntCon(INT32(hwIntrinsicNode->GetSimdBaseType()));
- ValueNum simdTypeVN = vnStore->VNForFunc(TYP_REF, VNF_SimdType, vnSize, vnBaseType);
+ ValueNum simdTypeVN = vnStore->VNForSimdType(tree->GetSimdSize(), tree->GetSimdBaseType());
resvnp.SetBoth(simdTypeVN);
#ifdef DEBUG
@@ -9639,10 +9664,10 @@ void Compiler::fgValueNumberHWIntrinsic(GenTree* tree)
#endif
}
- const bool isVariableNumArgs = HWIntrinsicInfo::lookupNumArgs(hwIntrinsicNode->gtHWIntrinsicId) == -1;
+ const bool isVariableNumArgs = HWIntrinsicInfo::lookupNumArgs(tree->GetHWIntrinsicId()) == -1;
// There are some HWINTRINSICS operations that have zero args, i.e. NI_Vector128_Zero
- if (tree->AsOp()->gtOp1 == nullptr)
+ if (tree->GetOperandCount() == 0)
{
// Currently we don't have intrinsics with variable number of args with a parameter-less option.
assert(!isVariableNumArgs);
@@ -9663,9 +9688,9 @@ void Compiler::fgValueNumberHWIntrinsic(GenTree* tree)
{
ValueNumPair op1vnp;
ValueNumPair op1Xvnp;
- vnStore->VNPUnpackExc(tree->AsOp()->gtOp1->gtVNPair, &op1vnp, &op1Xvnp);
+ vnStore->VNPUnpackExc(tree->Op(1)->gtVNPair, &op1vnp, &op1Xvnp);
- if (tree->AsOp()->gtOp2 == nullptr)
+ if (tree->GetOperandCount() == 1)
{
excSetPair = op1Xvnp;
@@ -9684,7 +9709,7 @@ void Compiler::fgValueNumberHWIntrinsic(GenTree* tree)
{
ValueNumPair op2vnp;
ValueNumPair op2Xvnp;
- vnStore->VNPUnpackExc(tree->AsOp()->gtOp2->gtVNPair, &op2vnp, &op2Xvnp);
+ vnStore->VNPUnpackExc(tree->Op(2)->gtVNPair, &op2vnp, &op2Xvnp);
excSetPair = vnStore->VNPExcSetUnion(op1Xvnp, op2Xvnp);
if (encodeResultType)
@@ -10265,9 +10290,6 @@ VNFunc Compiler::fgValueNumberJitHelperMethodVNFunc(CorInfoHelpFunc helpFunc)
case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_DYNAMICCLASS:
vnf = VNF_GetsharedNongcthreadstaticBaseDynamicclass;
break;
- case CORINFO_HELP_GETSTATICFIELDADDR_CONTEXT:
- vnf = VNF_GetStaticAddrContext;
- break;
case CORINFO_HELP_GETSTATICFIELDADDR_TLS:
vnf = VNF_GetStaticAddrTLS;
break;
diff --git a/src/coreclr/jit/valuenum.h b/src/coreclr/jit/valuenum.h
index afb9510090e39a..a45044b6620357 100644
--- a/src/coreclr/jit/valuenum.h
+++ b/src/coreclr/jit/valuenum.h
@@ -57,17 +57,9 @@
// taken from another map at a given index. As such, it must have a type that corresponds
// to the "index/selector", in practical terms - the field's type.
//
-// Note that the above constraints on the types for maps are not followed currently to the
-// letter by the implementation - "opaque" maps can and do have TYP_REF assigned to them
-// in various situations such as when initializing the "primary" VNs for loop entries.
-//
-// Note as well that the meaning of "the type" for a map is overloaded, because maps are used
-// both to represent memory "of all fields B of all objects that have this field in the heap"
-// and "the field B of this particular object on the heap". Only the latter maps can be used
-// as VNs for actual nodes, while the former are used for "the first field" maps and "array
-// equivalence type" maps, and, of course, for the heap VNs, which always have the placeholder
-// types of TYP_REF or TYP_UNKNOWN. In principle, placeholder types could be given to all the
-// maps of the former type.
+// Note that we give "placeholder" types (TYP_UNDEF and TYP_UNKNOWN as TYP_MEM and TYP_HEAP)
+// to maps that do not represent values found in IR. This is just to avoid confusion and
+// facilitate more precise validating checks.
//
// Let's review the following snippet to demonstrate how the MapSelect/MapStore machinery works
// together to deliver the results that it does. Say we have this snippet of (C#) code:
@@ -196,6 +188,12 @@ struct VNFuncApp
// This define is used with string concatenation to put this in printf format strings
#define FMT_VN "$%x"
+// We will use this placeholder type for memory maps that do not represent IR values ("field maps", etc).
+static const var_types TYP_MEM = TYP_UNDEF;
+
+// We will use this placeholder type for memory maps representing "the heap" (GcHeap/ByrefExposed).
+static const var_types TYP_HEAP = TYP_UNKNOWN;
+
class ValueNumStore
{
@@ -420,13 +418,6 @@ class ValueNumStore
return ValueNum(SRC_Null);
}
- // The zero map is the map that returns a zero "for the appropriate type" when indexed at any index.
- static ValueNum VNForZeroMap()
- {
- // We reserve Chunk 0 for "special" VNs. Let SRC_ZeroMap (== 1) be the zero map.
- return ValueNum(SRC_ZeroMap);
- }
-
// The ROH map is the map for the "read-only heap". We assume that this is never mutated, and always
// has the same value number.
static ValueNum VNForROH()
@@ -435,8 +426,8 @@ class ValueNumStore
return ValueNum(SRC_ReadOnlyHeap);
}
- // A special value number for "void" -- sometimes a type-void thing is an argument to a
- // GT_LIST, and we want the args to be non-NoVN.
+ // A special value number for "void" -- sometimes a type-void thing is an argument,
+ // and we want the args to be non-NoVN.
static ValueNum VNForVoid()
{
// We reserve Chunk 0 for "special" VNs. Let SRC_Void (== 4) be the value for "void".
@@ -463,10 +454,18 @@ class ValueNumStore
// It has an unreached() for a "typ" that has no zero value, such as TYP_VOID.
ValueNum VNZeroForType(var_types typ);
+ // Returns the value number for a zero-initialized struct.
+ ValueNum VNForZeroObj(CORINFO_CLASS_HANDLE structHnd);
+
// Returns the value number for one of the given "typ".
// It returns NoVN for a "typ" that has no one value, such as TYP_REF.
ValueNum VNOneForType(var_types typ);
+#ifdef FEATURE_SIMD
+ // A helper function for constructing VNF_SimdType VNs.
+ ValueNum VNForSimdType(unsigned simdSize, var_types simdBaseType);
+#endif // FEATURE_SIMD
+
// Create or return the existimg value number representing a singleton exception set
// for the the exception value "x".
ValueNum VNExcSetSingleton(ValueNum x);
@@ -588,7 +587,9 @@ class ValueNumStore
ValueNumKind vnk, var_types type, ValueNum map, ValueNum index, int* pBudget, bool* pUsedRecursiveVN);
// A specialized version of VNForFunc that is used for VNF_MapStore and provides some logging when verbose is set
- ValueNum VNForMapStore(var_types type, ValueNum map, ValueNum index, ValueNum value);
+ ValueNum VNForMapStore(ValueNum map, ValueNum index, ValueNum value);
+
+ ValueNum VNForFieldSelector(CORINFO_FIELD_HANDLE fieldHnd, var_types* pFieldType, size_t* pStructSize = nullptr);
// These functions parallel the ones above, except that they take liberal/conservative VN pairs
// as arguments, and return such a pair (the pair of the function applied to the liberal args, and
@@ -837,11 +838,16 @@ class ValueNumStore
//
enum class VN_RELATION_KIND
{
+ VRK_Same, // (x > y)
VRK_Swap, // (y > x)
VRK_Reverse, // (x <= y)
VRK_SwapReverse // (y >= x)
};
+#ifdef DEBUG
+ static const char* VNRelationString(VN_RELATION_KIND vrk);
+#endif
+
ValueNum GetRelatedRelop(ValueNum vn, VN_RELATION_KIND vrk);
// Convert a vartype_t to the value number's storage type for that vartype_t.
@@ -1021,14 +1027,19 @@ class ValueNumStore
// Prints the cast's representation mirroring GT_CAST's dump format.
void vnDumpCast(Compiler* comp, ValueNum castVN);
+ // Requires "zeroObj" to be a VNF_ZeroObj. Prints its representation.
+ void vnDumpZeroObj(Compiler* comp, VNFuncApp* zeroObj);
+
// Returns the string name of "vnf".
static const char* VNFuncName(VNFunc vnf);
// Used in the implementation of the above.
static const char* VNFuncNameArr[];
+ // Returns a type name used for "maps", i. e. displays TYP_UNDEF and TYP_UNKNOWN as TYP_MEM and TYP_HEAP.
+ static const char* VNMapTypeName(var_types type);
+
// Returns the string name of "vn" when it is a reserved value number, nullptr otherwise
static const char* reservedName(ValueNum vn);
-
#endif // DEBUG
// Returns true if "vn" is a reserved value number
@@ -1449,7 +1460,6 @@ class ValueNumStore
enum SpecialRefConsts
{
SRC_Null,
- SRC_ZeroMap,
SRC_ReadOnlyHeap,
SRC_Void,
SRC_EmptyExcSet,
diff --git a/src/coreclr/jit/valuenumfuncs.h b/src/coreclr/jit/valuenumfuncs.h
index 7665770881bcd9..d5a81d03ca5022 100644
--- a/src/coreclr/jit/valuenumfuncs.h
+++ b/src/coreclr/jit/valuenumfuncs.h
@@ -12,7 +12,6 @@ ValueNumFuncDef(MapSelect, 2, false, false, false) // Args: 0: map, 1: key.
ValueNumFuncDef(FieldSeq, 2, false, false, false) // Sequence (VN of null == empty) of (VN's of) field handles.
ValueNumFuncDef(NotAField, 0, false, false, false) // Value number function for FieldSeqStore::NotAField.
-ValueNumFuncDef(ZeroMap, 0, false, false, false) // The "ZeroMap": indexing at any index yields "zero of the desired type".
ValueNumFuncDef(PtrToLoc, 2, false, false, false) // Pointer (byref) to a local variable. Args: VN's of: 0: var num, 1: FieldSeq.
ValueNumFuncDef(PtrToArrElem, 4, false, false, false) // Pointer (byref) to an array element. Args: 0: array elem type eq class var_types value, VN's of: 1: array, 2: index, 3: FieldSeq.
@@ -22,6 +21,7 @@ ValueNumFuncDef(Phi, 2, false, false, false) // A phi function. Only occ
ValueNumFuncDef(PhiDef, 3, false, false, false) // Args: 0: local var # (or -1 for memory), 1: SSA #, 2: VN of definition.
// Wouldn't need this if I'd made memory a regular local variable...
ValueNumFuncDef(PhiMemoryDef, 2, false, false, false) // Args: 0: VN for basic block pointer, 1: VN of definition
+ValueNumFuncDef(ZeroObj, 1, false, false, false) // Zero-initialized struct. Args: 0: VN of the class handle.
ValueNumFuncDef(InitVal, 1, false, false, false) // An input arg, or init val of a local Args: 0: a constant VN.
@@ -128,7 +128,6 @@ ValueNumFuncDef(RuntimeHandleMethod, 2, false, true, false)
ValueNumFuncDef(RuntimeHandleClass, 2, false, true, false)
ValueNumFuncDef(ReadyToRunGenericHandle, 2, false, true, false)
-ValueNumFuncDef(GetStaticAddrContext, 1, false, true, false)
ValueNumFuncDef(GetStaticAddrTLS, 1, false, true, false)
ValueNumFuncDef(JitNew, 2, false, true, false)
diff --git a/src/coreclr/jit/vartype.h b/src/coreclr/jit/vartype.h
index d43e02a0a30737..33c411032d3308 100644
--- a/src/coreclr/jit/vartype.h
+++ b/src/coreclr/jit/vartype.h
@@ -247,6 +247,12 @@ inline bool varTypeIsLong(T vt)
return (TypeGet(vt) >= TYP_LONG) && (TypeGet(vt) <= TYP_ULONG);
}
+template
+inline bool varTypeIsInt(T vt)
+{
+ return (TypeGet(vt) >= TYP_INT) && (TypeGet(vt) <= TYP_UINT);
+}
+
template
inline bool varTypeIsMultiReg(T vt)
{
diff --git a/src/coreclr/md/compiler/custattr_emit.cpp b/src/coreclr/md/compiler/custattr_emit.cpp
index 47589f93a24208..02d3f8e9e3a4a1 100644
--- a/src/coreclr/md/compiler/custattr_emit.cpp
+++ b/src/coreclr/md/compiler/custattr_emit.cpp
@@ -198,8 +198,8 @@ DEFINE_CA_NAMED_ARGS(DllImportAttribute)
DEFINE_CA_NAMED_ARGS_END()
const KnownCaProp DllImportAttributeProps = {"System.Runtime.InteropServices", "DllImportAttribute", DllImportTargets, bDONTKEEPCA,
- rDllImportAttributeArgs, lengthof(rDllImportAttributeArgs),
- rDllImportAttributeNamedArgs, lengthof(rDllImportAttributeNamedArgs)};
+ rDllImportAttributeArgs, ARRAY_SIZE(rDllImportAttributeArgs),
+ rDllImportAttributeNamedArgs, ARRAY_SIZE(rDllImportAttributeNamedArgs)};
//-----------------------------------------------------------------------------
// GUID args, named args (none), and known attribute properties.
@@ -208,7 +208,7 @@ DEFINE_CA_CTOR_ARGS(GuidAttribute)
DEFINE_CA_CTOR_ARGS_END()
const KnownCaProp GuidAttributeProps = {"System.Runtime.InteropServices", "GuidAttribute", GuidTargets, bKEEPCA,
- rGuidAttributeArgs, lengthof(rGuidAttributeArgs)};
+ rGuidAttributeArgs, ARRAY_SIZE(rGuidAttributeArgs)};
//-----------------------------------------------------------------------------
// ComImport args (none), named args (none), and known attribute properties.
@@ -221,7 +221,7 @@ DEFINE_CA_CTOR_ARGS(InterfaceTypeAttribute)
DEFINE_CA_CTOR_ARGS_END()
const KnownCaProp InterfaceTypeAttributeProps = {"System.Runtime.InteropServices", "InterfaceTypeAttribute", InterfaceTypeTargets, bKEEPCA,
- rInterfaceTypeAttributeArgs, lengthof(rInterfaceTypeAttributeArgs)};
+ rInterfaceTypeAttributeArgs, ARRAY_SIZE(rInterfaceTypeAttributeArgs)};
//-----------------------------------------------------------------------------
// Class interface type args, named args (none), and known attribute properties.
@@ -230,7 +230,7 @@ DEFINE_CA_CTOR_ARGS(ClassInterfaceAttribute)
DEFINE_CA_CTOR_ARGS_END()
const KnownCaProp ClassInterfaceAttributeProps = {"System.Runtime.InteropServices", "ClassInterfaceAttribute", ClassInterfaceTargets, bKEEPCA,
- rClassInterfaceAttributeArgs, lengthof(rClassInterfaceAttributeArgs)};
+ rClassInterfaceAttributeArgs, ARRAY_SIZE(rClassInterfaceAttributeArgs)};
//-----------------------------------------------------------------------------
// Serializable args (none), named args (none), and known attribute properties.
@@ -275,15 +275,15 @@ DEFINE_CA_NAMED_ARGS_END()
const KnownCaProp MethodImplAttribute1Props = {"System.Runtime.CompilerServices", "MethodImplAttribute", MethodImplTargets, bDONTKEEPCA,
0, 0,
- rMethodImplAttributeNamedArgs, lengthof(rMethodImplAttributeNamedArgs),
+ rMethodImplAttributeNamedArgs, ARRAY_SIZE(rMethodImplAttributeNamedArgs),
bMATCHBYSIG};
const KnownCaProp MethodImplAttribute2Props = {"System.Runtime.CompilerServices", "MethodImplAttribute", MethodImplTargets, bDONTKEEPCA,
- rMethodImplAttribute2Args, lengthof(rMethodImplAttribute2Args),
- rMethodImplAttributeNamedArgs, lengthof(rMethodImplAttributeNamedArgs),
+ rMethodImplAttribute2Args, ARRAY_SIZE(rMethodImplAttribute2Args),
+ rMethodImplAttributeNamedArgs, ARRAY_SIZE(rMethodImplAttributeNamedArgs),
bMATCHBYSIG};
const KnownCaProp MethodImplAttribute3Props = {"System.Runtime.CompilerServices", "MethodImplAttribute", MethodImplTargets, bDONTKEEPCA,
- rMethodImplAttribute3Args, lengthof(rMethodImplAttribute3Args),
- rMethodImplAttributeNamedArgs, lengthof(rMethodImplAttributeNamedArgs),
+ rMethodImplAttribute3Args, ARRAY_SIZE(rMethodImplAttribute3Args),
+ rMethodImplAttributeNamedArgs, ARRAY_SIZE(rMethodImplAttributeNamedArgs),
bMATCHBYNAME};
//-----------------------------------------------------------------------------
@@ -324,13 +324,13 @@ DEFINE_CA_NAMED_ARGS(MarshalAsAttribute)
DEFINE_CA_NAMED_ARGS_END()
const KnownCaProp MarshalAsAttribute1Props = {"System.Runtime.InteropServices", "MarshalAsAttribute", MarshalTargets, bDONTKEEPCA,
- rMarshalAsAttribute1Args, lengthof(rMarshalAsAttribute1Args),
- rMarshalAsAttributeNamedArgs, lengthof(rMarshalAsAttributeNamedArgs),
+ rMarshalAsAttribute1Args, ARRAY_SIZE(rMarshalAsAttribute1Args),
+ rMarshalAsAttributeNamedArgs, ARRAY_SIZE(rMarshalAsAttributeNamedArgs),
bMATCHBYSIG};
const KnownCaProp MarshalAsAttribute2Props = {"System.Runtime.InteropServices", "MarshalAsAttribute", MarshalTargets, bDONTKEEPCA,
- rMarshalAsAttribute2Args, lengthof(rMarshalAsAttribute2Args),
- rMarshalAsAttributeNamedArgs, lengthof(rMarshalAsAttributeNamedArgs),
+ rMarshalAsAttribute2Args, ARRAY_SIZE(rMarshalAsAttribute2Args),
+ rMarshalAsAttributeNamedArgs, ARRAY_SIZE(rMarshalAsAttributeNamedArgs),
bMATCHBYNAME};
//-----------------------------------------------------------------------------
@@ -375,12 +375,12 @@ DEFINE_CA_NAMED_ARGS(StructLayoutAttribute)
DEFINE_CA_NAMED_ARGS_END()
const KnownCaProp StructLayoutAttribute1Props = {"System.Runtime.InteropServices", "StructLayoutAttribute", StructLayoutTargets, bDONTKEEPCA,
- rStructLayoutAttribute1Args, lengthof(rStructLayoutAttribute1Args),
- rStructLayoutAttributeNamedArgs, lengthof(rStructLayoutAttributeNamedArgs),
+ rStructLayoutAttribute1Args, ARRAY_SIZE(rStructLayoutAttribute1Args),
+ rStructLayoutAttributeNamedArgs, ARRAY_SIZE(rStructLayoutAttributeNamedArgs),
bMATCHBYSIG};
const KnownCaProp StructLayoutAttribute2Props = {"System.Runtime.InteropServices", "StructLayoutAttribute", StructLayoutTargets, bDONTKEEPCA,
- rStructLayoutAttribute2Args, lengthof(rStructLayoutAttribute2Args),
- rStructLayoutAttributeNamedArgs, lengthof(rStructLayoutAttributeNamedArgs),
+ rStructLayoutAttribute2Args, ARRAY_SIZE(rStructLayoutAttribute2Args),
+ rStructLayoutAttributeNamedArgs, ARRAY_SIZE(rStructLayoutAttributeNamedArgs),
bMATCHBYNAME};
//-----------------------------------------------------------------------------
@@ -390,7 +390,7 @@ DEFINE_CA_CTOR_ARGS(FieldOffsetAttribute)
DEFINE_CA_CTOR_ARGS_END()
const KnownCaProp FieldOffsetAttributeProps = {"System.Runtime.InteropServices", "FieldOffsetAttribute", FieldOffsetTargets, bDONTKEEPCA,
- rFieldOffsetAttributeArgs, lengthof(rFieldOffsetAttributeArgs)};
+ rFieldOffsetAttributeArgs, ARRAY_SIZE(rFieldOffsetAttributeArgs)};
DEFINE_CA_CTOR_ARGS(TypeLibVersionAttribute)
DEFINE_CA_CTOR_ARG(SERIALIZATION_TYPE_I4)
@@ -398,7 +398,7 @@ DEFINE_CA_CTOR_ARGS(TypeLibVersionAttribute)
DEFINE_CA_CTOR_ARGS_END()
const KnownCaProp TypeLibVersionAttributeProps = {"System.Runtime.InteropServices", "TypeLibVersionAttribute", TypeLibVersionTargets, bKEEPCA,
- rTypeLibVersionAttributeArgs, lengthof(rTypeLibVersionAttributeArgs)};
+ rTypeLibVersionAttributeArgs, ARRAY_SIZE(rTypeLibVersionAttributeArgs)};
DEFINE_CA_CTOR_ARGS(ComCompatibleVersionAttribute)
@@ -409,7 +409,7 @@ DEFINE_CA_CTOR_ARGS(ComCompatibleVersionAttribute)
DEFINE_CA_CTOR_ARGS_END()
const KnownCaProp ComCompatibleVersionAttributeProps = {"System.Runtime.InteropServices", "ComCompatibleVersionAttribute", ComCompatibleVersionTargets, bKEEPCA,
- rComCompatibleVersionAttributeArgs, lengthof(rComCompatibleVersionAttributeArgs)};
+ rComCompatibleVersionAttributeArgs, ARRAY_SIZE(rComCompatibleVersionAttributeArgs)};
//-----------------------------------------------------------------------------
diff --git a/src/coreclr/md/compiler/regmeta.cpp b/src/coreclr/md/compiler/regmeta.cpp
index ffe28774334698..44acdf6f410574 100644
--- a/src/coreclr/md/compiler/regmeta.cpp
+++ b/src/coreclr/md/compiler/regmeta.cpp
@@ -910,7 +910,7 @@ const char *DumpMD_DumpRawNameOfType(RegMeta *pMD, ULONG iType)
}
// default:
static char buf[30];
- sprintf_s(buf, NumItems(buf), "unknown type 0x%02x", iType);
+ sprintf_s(buf, ARRAY_SIZE(buf), "unknown type 0x%02x", iType);
return buf;
} // const char *DumpMD_DumpRawNameOfType()
@@ -1127,7 +1127,7 @@ void DumpMD_DisplayUserStrings(
bool bUnprint = false; // Is an unprintable character found?
HRESULT hr; // A result.
while (SUCCEEDED(hr = pMD->EnumUserStrings( &stringEnum,
- Strings, NumItems(Strings), &count)) &&
+ Strings, ARRAY_SIZE(Strings), &count)) &&
count > 0)
{
if (totalCount == 1)
@@ -1225,7 +1225,7 @@ void DumpMD_DumpRawHeaps(
do
{
pMD->GetBlob(oData, &cbData, (const void**)&pData);
- sprintf_s(rcPrefix, NumItems(rcPrefix), "%5x,%-2x", oData, cbData);
+ sprintf_s(rcPrefix, ARRAY_SIZE(rcPrefix), "%5x,%-2x", oData, cbData);
DumpMD_DumpHex(rcPrefix, pData, cbData);
hr = pMD->GetNextBlob(oData, &oData);
}
@@ -1239,7 +1239,7 @@ void DumpMD_DumpRawHeaps(
do
{
pMD->GetString(oData, &pString);
- sprintf_s(rcPrefix, NumItems(rcPrefix), "%08x", oData);
+ sprintf_s(rcPrefix, ARRAY_SIZE(rcPrefix), "%08x", oData);
DumpMD_DumpHex(rcPrefix, pString, (ULONG)strlen(pString)+1);
if (*pString != 0)
DumpMD_VWrite("%08x: %s\n", oData, pString);
diff --git a/src/coreclr/md/compiler/regmeta_vm.cpp b/src/coreclr/md/compiler/regmeta_vm.cpp
index 04b69c7d5cc3a1..80746137bfbaf1 100644
--- a/src/coreclr/md/compiler/regmeta_vm.cpp
+++ b/src/coreclr/md/compiler/regmeta_vm.cpp
@@ -27,9 +27,6 @@
#include
-
-
-
#define DEFINE_CUSTOM_NODUPCHECK 1
#define DEFINE_CUSTOM_DUPCHECK 2
#define SET_CUSTOM 3
@@ -210,12 +207,12 @@ RegMeta::ResolveTypeRef(
_ASSERTE(TypeFromToken(tr) == mdtTypeRef);
IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tr), &pTypeRefRec));
- IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, wzNameSpace, lengthof(wzNameSpace), NULL));
+ IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, wzNameSpace, ARRAY_SIZE(wzNameSpace), NULL));
if (hr != NOERROR)
{
_ASSERTE(hr == CLDB_S_TRUNCATION);
// Truncate the namespace string
- wzNameSpace[lengthof(wzNameSpace) - 1] = 0;
+ wzNameSpace[STRING_LENGTH(wzNameSpace)] = 0;
}
//***********************
diff --git a/src/coreclr/md/enc/metamodelrw.cpp b/src/coreclr/md/enc/metamodelrw.cpp
index bc15f4f6ffcb16..82142503783b11 100644
--- a/src/coreclr/md/enc/metamodelrw.cpp
+++ b/src/coreclr/md/enc/metamodelrw.cpp
@@ -5219,7 +5219,7 @@ CMiniMdRW::FindGenericParamHelper(
if (IsSorted(TBL_GenericParam))
{
mdToken tk;
- tk = encodeToken(RidFromToken(tkOwner), TypeFromToken(tkOwner), mdtTypeOrMethodDef, lengthof(mdtTypeOrMethodDef));
+ tk = encodeToken(RidFromToken(tkOwner), TypeFromToken(tkOwner), mdtTypeOrMethodDef, ARRAY_SIZE(mdtTypeOrMethodDef));
IfFailGo(SearchTableForMultipleRows(TBL_GenericParam,
_COLDEF(GenericParam,Owner),
tk,
diff --git a/src/coreclr/md/inc/metamodel.h b/src/coreclr/md/inc/metamodel.h
index cc368146807665..56845d20ff6ceb 100644
--- a/src/coreclr/md/inc/metamodel.h
+++ b/src/coreclr/md/inc/metamodel.h
@@ -21,6 +21,8 @@
#include "../datablob.h"
#include "../debug_metadata.h"
+#include
+
#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB
#include "portablepdbmdds.h"
#include "portablepdbmdi.h"
@@ -1622,12 +1624,12 @@ template class CMiniMdTemplate : public CMiniMdBase
// Return RID to Constant table.
__checkReturn
HRESULT FindConstantFor(RID rid, mdToken typ, RID *pFoundRid)
- { return doSearchTable(TBL_Constant, _COLPAIR(Constant,Parent), encodeToken(rid,typ,mdtHasConstant,lengthof(mdtHasConstant)), pFoundRid); }
+ { return doSearchTable(TBL_Constant, _COLPAIR(Constant,Parent), encodeToken(rid,typ,mdtHasConstant, ARRAY_SIZE(mdtHasConstant)), pFoundRid); }
// Return RID to FieldMarshal table.
__checkReturn
HRESULT FindFieldMarshalFor(RID rid, mdToken typ, RID *pFoundRid)
- { return doSearchTable(TBL_FieldMarshal, _COLPAIR(FieldMarshal,Parent), encodeToken(rid,typ,mdtHasFieldMarshal,lengthof(mdtHasFieldMarshal)), pFoundRid); }
+ { return doSearchTable(TBL_FieldMarshal, _COLPAIR(FieldMarshal,Parent), encodeToken(rid,typ,mdtHasFieldMarshal, ARRAY_SIZE(mdtHasFieldMarshal)), pFoundRid); }
// Return RID to ClassLayout table, given the rid to a TypeDef.
__checkReturn
@@ -1695,7 +1697,7 @@ template class CMiniMdTemplate : public CMiniMdBase
// Return RID to Constant table.
__checkReturn
HRESULT FindImplMapFor(RID rid, mdToken typ, RID *pFoundRid)
- { return doSearchTable(TBL_ImplMap, _COLPAIR(ImplMap,MemberForwarded), encodeToken(rid,typ,mdtMemberForwarded,lengthof(mdtMemberForwarded)), pFoundRid); }
+ { return doSearchTable(TBL_ImplMap, _COLPAIR(ImplMap,MemberForwarded), encodeToken(rid,typ,mdtMemberForwarded, ARRAY_SIZE(mdtMemberForwarded)), pFoundRid); }
// Return RID to FieldRVA table.
__checkReturn
@@ -1735,7 +1737,7 @@ template class CMiniMdTemplate : public CMiniMdBase
{
return SearchTableForMultipleRows(TBL_GenericParam,
_COLDEF(GenericParam,Owner),
- encodeToken(rid, mdtTypeDef, mdtTypeOrMethodDef, lengthof(mdtTypeOrMethodDef)),
+ encodeToken(rid, mdtTypeDef, mdtTypeOrMethodDef, ARRAY_SIZE(mdtTypeOrMethodDef)),
pEnd,
pFoundRid);
}
@@ -1744,7 +1746,7 @@ template class CMiniMdTemplate : public CMiniMdBase
{
return SearchTableForMultipleRows(TBL_GenericParam,
_COLDEF(GenericParam,Owner),
- encodeToken(rid, mdtMethodDef, mdtTypeOrMethodDef, lengthof(mdtTypeOrMethodDef)),
+ encodeToken(rid, mdtMethodDef, mdtTypeOrMethodDef, ARRAY_SIZE(mdtTypeOrMethodDef)),
pEnd,
pFoundRid);
}
@@ -1753,7 +1755,7 @@ template class CMiniMdTemplate : public CMiniMdBase
{
return SearchTableForMultipleRows(TBL_MethodSpec,
_COLDEF(MethodSpec,Method),
- encodeToken(rid, mdtMethodDef, mdtMethodDefOrRef, lengthof(mdtMethodDefOrRef)),
+ encodeToken(rid, mdtMethodDef, mdtMethodDefOrRef, ARRAY_SIZE(mdtMethodDefOrRef)),
pEnd,
pFoundRid);
}
@@ -1762,7 +1764,7 @@ template class CMiniMdTemplate : public CMiniMdBase
{
return SearchTableForMultipleRows(TBL_MethodSpec,
_COLDEF(MethodSpec,Method),
- encodeToken(rid, mdtMemberRef, mdtMethodDefOrRef, lengthof(mdtMethodDefOrRef)),
+ encodeToken(rid, mdtMemberRef, mdtMethodDefOrRef, ARRAY_SIZE(mdtMethodDefOrRef)),
pEnd,
pFoundRid);
}
@@ -1823,7 +1825,7 @@ template class CMiniMdTemplate : public CMiniMdBase
{
return SearchTableForMultipleRows(TBL_CustomAttribute,
_COLDEF(CustomAttribute,Parent),
- encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtHasCustomAttribute, lengthof(mdtHasCustomAttribute)),
+ encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtHasCustomAttribute, ARRAY_SIZE(mdtHasCustomAttribute)),
pEnd,
pFoundRid);
}
@@ -1842,7 +1844,7 @@ template class CMiniMdTemplate : public CMiniMdBase
{
return SearchTableForMultipleRows(TBL_DeclSecurity,
_COLDEF(DeclSecurity,Parent),
- encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtHasDeclSecurity, lengthof(mdtHasDeclSecurity)),
+ encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtHasDeclSecurity, ARRAY_SIZE(mdtHasDeclSecurity)),
pEnd,
pFoundRid);
}
@@ -1893,7 +1895,7 @@ template class CMiniMdTemplate : public CMiniMdBase
{
return SearchTableForMultipleRows(TBL_MethodSemantics,
_COLDEF(MethodSemantics,Association),
- encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtHasSemantic, lengthof(mdtHasSemantic)),
+ encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtHasSemantic, ARRAY_SIZE(mdtHasSemantic)),
pEnd,
pFoundRid);
}
diff --git a/src/coreclr/md/inc/metamodelrw.h b/src/coreclr/md/inc/metamodelrw.h
index 90ac20948269ef..a8ccf8ae2effb5 100644
--- a/src/coreclr/md/inc/metamodelrw.h
+++ b/src/coreclr/md/inc/metamodelrw.h
@@ -24,6 +24,8 @@
#include "../heaps/export.h"
#include "../tables/export.h"
+#include
+
struct HENUMInternal;
#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
struct IMDCustomDataSource;
@@ -950,7 +952,7 @@ class CMiniMdRW : public CMiniMdTemplate
HRESULT GetGenericParamsForToken(mdToken tk, RID *pRidStart, RID *pRidEnd = 0)
{
return LookUpTableByCol(
- encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtTypeOrMethodDef, lengthof(mdtTypeOrMethodDef)),
+ encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtTypeOrMethodDef, ARRAY_SIZE(mdtTypeOrMethodDef)),
m_pVS[TBL_GenericParam], pRidStart, pRidEnd);
}
@@ -965,7 +967,7 @@ class CMiniMdRW : public CMiniMdTemplate
HRESULT GetMethodSpecsForToken(mdToken tk, RID *pRidStart, RID *pRidEnd = 0)
{
return LookUpTableByCol(
- encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtMethodDefOrRef, lengthof(mdtMethodDefOrRef)),
+ encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtMethodDefOrRef, ARRAY_SIZE(mdtMethodDefOrRef)),
m_pVS[TBL_MethodSpec], pRidStart, pRidEnd);
}
@@ -973,7 +975,7 @@ class CMiniMdRW : public CMiniMdTemplate
HRESULT GetDeclSecurityForToken(mdToken tk, RID *pRidStart, RID *pRidEnd = 0)
{
return LookUpTableByCol(
- encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtHasDeclSecurity, lengthof(mdtHasDeclSecurity)),
+ encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtHasDeclSecurity, ARRAY_SIZE(mdtHasDeclSecurity)),
m_pVS[TBL_DeclSecurity],
pRidStart,
pRidEnd);
@@ -983,7 +985,7 @@ class CMiniMdRW : public CMiniMdTemplate
HRESULT GetCustomAttributeForToken(mdToken tk, RID *pRidStart, RID *pRidEnd = 0)
{
return LookUpTableByCol(
- encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtHasCustomAttribute, lengthof(mdtHasCustomAttribute)),
+ encodeToken(RidFromToken(tk), TypeFromToken(tk), mdtHasCustomAttribute, ARRAY_SIZE(mdtHasCustomAttribute)),
m_pVS[TBL_CustomAttribute],
pRidStart,
pRidEnd);
diff --git a/src/coreclr/md/runtime/metamodel.cpp b/src/coreclr/md/runtime/metamodel.cpp
index cd1ccde9cc36af..b13ee6a736aaf4 100644
--- a/src/coreclr/md/runtime/metamodel.cpp
+++ b/src/coreclr/md/runtime/metamodel.cpp
@@ -90,7 +90,7 @@ static const char* rDummy3ColNames[] = { "" };
//-----------------------------------------------------------------------------
// Define the array of Coded Token Definitions.
-#define MiniMdCodedToken(x) {lengthof(CMiniMdBase::mdt##x), CMiniMdBase::mdt##x, #x},
+#define MiniMdCodedToken(x) {ARRAY_SIZE(CMiniMdBase::mdt##x), CMiniMdBase::mdt##x, #x},
const CCodedTokenDef g_CodedTokens [] = {
MiniMdCodedTokens()
};
@@ -98,7 +98,7 @@ const CCodedTokenDef g_CodedTokens [] = {
// Define the array of Table Definitions.
#undef MiniMdTable
-#define MiniMdTable(x) { { r##x##Cols, lengthof(r##x##Cols), x##Rec::COL_KEY, 0 }, r##x##ColNames, #x},
+#define MiniMdTable(x) { { r##x##Cols, ARRAY_SIZE(r##x##Cols), x##Rec::COL_KEY, 0 }, r##x##ColNames, #x},
const CMiniTableDefEx g_Tables[TBL_COUNT] = {
MiniMdTables()
#ifdef FEATURE_METADATA_EMIT_PORTABLE_PDB
@@ -107,7 +107,7 @@ const CMiniTableDefEx g_Tables[TBL_COUNT] = {
};
// Define a table descriptor for the obsolete v1.0 GenericParam table definition.
-const CMiniTableDefEx g_Table_GenericParamV1_1 = { { rGenericParamV1_1Cols, lengthof(rGenericParamV1_1Cols), GenericParamV1_1Rec::COL_KEY, 0 }, rGenericParamV1_1ColNames, "GenericParamV1_"};
+const CMiniTableDefEx g_Table_GenericParamV1_1 = { { rGenericParamV1_1Cols, ARRAY_SIZE(rGenericParamV1_1Cols), GenericParamV1_1Rec::COL_KEY, 0 }, rGenericParamV1_1ColNames, "GenericParamV1_"};
@@ -444,7 +444,7 @@ CMiniMdBase::encodeToken(
//*****************************************************************************
inline BYTE cbRID(ULONG ixMax) { return ixMax > USHRT_MAX ? (BYTE) sizeof(ULONG) : (BYTE) sizeof(USHORT); }
-#define _CBTKN(cRecs,tkns) cbRID((cRecs) << m_cb[lengthof(tkns)])
+#define _CBTKN(cRecs,tkns) cbRID((cRecs) << m_cb[ARRAY_SIZE(tkns)])
//*****************************************************************************
// Constructor.
@@ -723,7 +723,7 @@ CMiniMdBase::InitColsForTable(
HRESULT hr = S_OK;
_ASSERTE((bExtra == 0) || (bExtra == 1));
- _ASSERTE(NumItems(pCols) >= pTable->m_cCols);
+ _ASSERTE(ARRAY_SIZE(pCols) >= pTable->m_cCols);
bExtra = 0;//@FUTURE: save in schema header. until then use 0.
@@ -751,7 +751,7 @@ CMiniMdBase::InitColsForTable(
ULONG iCdTkn = pCols[ixCol].m_Type - iCodedToken;
ULONG cRecs = 0;
- _ASSERTE(iCdTkn < lengthof(g_CodedTokens));
+ _ASSERTE(iCdTkn < ARRAY_SIZE(g_CodedTokens));
CCodedTokenDef const *pCTD = &g_CodedTokens[iCdTkn];
// Iterate the token list of this coded token.
@@ -1084,7 +1084,7 @@ CMiniMdBase::FindCustomAttributeFor(
{
HRESULT hr;
int ixFound; // index of some custom value row.
- ULONG ulTarget = encodeToken(rid,tkObj,mdtHasCustomAttribute,lengthof(mdtHasCustomAttribute)); // encoded token representing target.
+ ULONG ulTarget = encodeToken(rid,tkObj,mdtHasCustomAttribute, ARRAY_SIZE(mdtHasCustomAttribute)); // encoded token representing target.
ULONG ixCur; // Current row being examined.
mdToken tkFound; // Type of some custom value row.
void *pCur; // A custom value entry.
@@ -1107,7 +1107,7 @@ CMiniMdBase::FindCustomAttributeFor(
{
// Test the type of the current row.
tkFound = getIX(pCur, _COLDEF(CustomAttribute,Type));
- tkFound = decodeToken(tkFound, mdtCustomAttributeType, lengthof(mdtCustomAttributeType));
+ tkFound = decodeToken(tkFound, mdtCustomAttributeType, ARRAY_SIZE(mdtCustomAttributeType));
if (tkFound == tkType)
{
*pFoundRid = ixCur;
@@ -1137,7 +1137,7 @@ CMiniMdBase::FindCustomAttributeFor(
break;
// Test the type of the current row.
tkFound = getIX(pCur, _COLDEF(CustomAttribute,Type));
- tkFound = decodeToken(tkFound, mdtCustomAttributeType, lengthof(mdtCustomAttributeType));
+ tkFound = decodeToken(tkFound, mdtCustomAttributeType, ARRAY_SIZE(mdtCustomAttributeType));
if (tkFound == tkType)
{
*pFoundRid = ixCur;
diff --git a/src/coreclr/pal/src/debug/debug.cpp b/src/coreclr/pal/src/debug/debug.cpp
index 5f6f17a25cf9cd..3e20cb7c1c1534 100644
--- a/src/coreclr/pal/src/debug/debug.cpp
+++ b/src/coreclr/pal/src/debug/debug.cpp
@@ -742,6 +742,7 @@ PAL_ProbeMemory(
BOOL fWriteAccess)
{
int fds[2];
+ int flags;
if (pipe(fds) != 0)
{
@@ -749,8 +750,11 @@ PAL_ProbeMemory(
return FALSE;
}
- fcntl(fds[0], O_NONBLOCK);
- fcntl(fds[1], O_NONBLOCK);
+ flags = fcntl(fds[0], F_GETFL, 0);
+ fcntl(fds[0], F_SETFL, flags | O_NONBLOCK);
+
+ flags = fcntl(fds[1], F_GETFL, 0);
+ fcntl(fds[1], F_SETFL, flags | O_NONBLOCK);
PVOID pEnd = (PBYTE)pBuffer + cbBuffer;
BOOL result = TRUE;
diff --git a/src/coreclr/pal/src/exception/machmessage.h b/src/coreclr/pal/src/exception/machmessage.h
index c24a979f52089d..01e2bb691d2a41 100644
--- a/src/coreclr/pal/src/exception/machmessage.h
+++ b/src/coreclr/pal/src/exception/machmessage.h
@@ -35,7 +35,7 @@ using namespace CorUnix;
if (machret != KERN_SUCCESS) \
{ \
char _szError[1024]; \
- snprintf(_szError, _countof(_szError), "%s: %u: %s", __FUNCTION__, __LINE__, _msg); \
+ snprintf(_szError, ARRAY_SIZE(_szError), "%s: %u: %s", __FUNCTION__, __LINE__, _msg); \
mach_error(_szError, machret); \
abort(); \
} \
diff --git a/src/coreclr/pal/src/file/filetime.cpp b/src/coreclr/pal/src/file/filetime.cpp
index 7cdde900d2117f..768ee426c4dbfa 100644
--- a/src/coreclr/pal/src/file/filetime.cpp
+++ b/src/coreclr/pal/src/file/filetime.cpp
@@ -251,7 +251,7 @@ Function
FileTimeToSystemTime()
Helper function for FileTimeToDosTime.
- Converts the necessary file time attibutes to system time, for
+ Converts the necessary file time attributes to system time, for
easier manipulation in FileTimeToDosTime.
--*/
diff --git a/src/coreclr/pal/src/include/pal/palinternal.h b/src/coreclr/pal/src/include/pal/palinternal.h
index b0abe2aa9be0ac..3a12d789103647 100644
--- a/src/coreclr/pal/src/include/pal/palinternal.h
+++ b/src/coreclr/pal/src/include/pal/palinternal.h
@@ -617,8 +617,6 @@ function_name() to call the system's implementation
#undef assert
#define assert (Use__ASSERTE_instead_of_assert) assert
-#define string_countof(a) (sizeof(a) / sizeof(a[0]) - 1)
-
#ifndef __ANDROID__
#define TEMP_DIRECTORY_PATH "/tmp/"
#else
diff --git a/src/coreclr/pal/src/include/pal/sharedmemory.h b/src/coreclr/pal/src/include/pal/sharedmemory.h
index 1ded94e12fcc59..c9cecc750859c0 100644
--- a/src/coreclr/pal/src/include/pal/sharedmemory.h
+++ b/src/coreclr/pal/src/include/pal/sharedmemory.h
@@ -5,15 +5,12 @@
#define _PAL_SHARED_MEMORY_H_
#include "corunix.hpp"
+#include
#ifndef static_assert_no_msg
#define static_assert_no_msg( cond ) static_assert( cond, #cond )
#endif // !static_assert_no_msg
-#ifndef _countof
-#define _countof(a) (sizeof(a) / sizeof(a[0]))
-#endif // !_countof
-
// The folder used for storing shared memory files and their lock files is defined in
// the gSharedFilesPath global variable. The value of the variable depends on which
// OS is being used, and if the application is running in a sandbox in Mac.
@@ -28,16 +25,16 @@
// {gSharedFilesPath}/.dotnet/lockfiles/session/
#define SHARED_MEMORY_MAX_FILE_NAME_CHAR_COUNT (_MAX_FNAME - 1)
-#define SHARED_MEMORY_MAX_NAME_CHAR_COUNT (string_countof("Global\\") + SHARED_MEMORY_MAX_FILE_NAME_CHAR_COUNT)
+#define SHARED_MEMORY_MAX_NAME_CHAR_COUNT (STRING_LENGTH("Global\\") + SHARED_MEMORY_MAX_FILE_NAME_CHAR_COUNT)
#define SHARED_MEMORY_RUNTIME_TEMP_DIRECTORY_NAME ".dotnet"
#define SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_NAME ".dotnet/shm"
#define SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME ".dotnet/lockfiles"
-static_assert_no_msg(_countof(SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME) >= _countof(SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_NAME));
+static_assert_no_msg(ARRAY_SIZE(SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME) >= ARRAY_SIZE(SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_NAME));
#define SHARED_MEMORY_GLOBAL_DIRECTORY_NAME "global"
#define SHARED_MEMORY_SESSION_DIRECTORY_NAME_PREFIX "session"
-static_assert_no_msg(_countof(SHARED_MEMORY_SESSION_DIRECTORY_NAME_PREFIX) >= _countof(SHARED_MEMORY_GLOBAL_DIRECTORY_NAME));
+static_assert_no_msg(ARRAY_SIZE(SHARED_MEMORY_SESSION_DIRECTORY_NAME_PREFIX) >= ARRAY_SIZE(SHARED_MEMORY_GLOBAL_DIRECTORY_NAME));
#define SHARED_MEMORY_UNIQUE_TEMP_NAME_TEMPLATE ".coreclr.XXXXXX"
@@ -46,9 +43,9 @@ static_assert_no_msg(_countof(SHARED_MEMORY_SESSION_DIRECTORY_NAME_PREFIX) >= _c
// Note that this Max size does not include the prefix folder path size which is unknown (in the case of sandbox) until runtime
#define SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT \
( \
- string_countof(SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME) + \
+ STRING_LENGTH(SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME) + \
1 /* path separator */ + \
- string_countof(SHARED_MEMORY_SESSION_DIRECTORY_NAME_PREFIX) + \
+ STRING_LENGTH(SHARED_MEMORY_SESSION_DIRECTORY_NAME_PREFIX) + \
SHARED_MEMORY_MAX_SESSION_ID_CHAR_COUNT + \
1 /* path separator */ + \
SHARED_MEMORY_MAX_FILE_NAME_CHAR_COUNT \
diff --git a/src/coreclr/pal/src/init/pal.cpp b/src/coreclr/pal/src/init/pal.cpp
index e9dffec3dbb2ae..5469b4bebd483a 100644
--- a/src/coreclr/pal/src/init/pal.cpp
+++ b/src/coreclr/pal/src/init/pal.cpp
@@ -39,7 +39,7 @@ SET_DEFAULT_DEBUG_CHANNEL(PAL); // some headers have code with asserts, so do th
#include "pal/numa.h"
#include "pal/stackstring.hpp"
#include "pal/cgroup.h"
-#include
+#include
#if HAVE_MACH_EXCEPTIONS
#include "../exception/machexception.h"
@@ -1371,5 +1371,5 @@ static BOOL INIT_SharedFilesPath(void)
return gSharedFilesPath->Set(TEMP_DIRECTORY_PATH);
// We can verify statically the non sandboxed case, since the size is known during compile time
- static_assert_no_msg(string_countof(TEMP_DIRECTORY_PATH) + SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1 /* null terminator */ <= MAX_LONGPATH);
+ static_assert_no_msg(STRING_LENGTH(TEMP_DIRECTORY_PATH) + SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1 /* null terminator */ <= MAX_LONGPATH);
}
diff --git a/src/coreclr/pal/src/locale/utf8.cpp b/src/coreclr/pal/src/locale/utf8.cpp
index 96a633b165d9a2..8ff3229fcbabad 100644
--- a/src/coreclr/pal/src/locale/utf8.cpp
+++ b/src/coreclr/pal/src/locale/utf8.cpp
@@ -3,8 +3,6 @@
/*++
-
-
Module Name:
unicode/utf8.c
@@ -14,8 +12,6 @@ Module Name:
Revision History:
-
-
--*/
#include "pal/utf8.h"
@@ -25,10 +21,6 @@ using namespace CorUnix;
#define FASTLOOP
-#ifndef COUNTOF
-#define COUNTOF(x) (sizeof(x) / sizeof((x)[0]))
-#endif
-
struct CharUnicodeInfo
{
static const WCHAR HIGH_SURROGATE_START = 0xd800;
@@ -232,7 +224,7 @@ class DecoderReplacementFallback : public DecoderFallback
if (bFoundHigh)
throw ArgumentException("String 'replacement' contains invalid Unicode code points.", "replacement");
- wcscpy_s(strDefault, COUNTOF(strDefault), replacement);
+ wcscpy_s(strDefault, ARRAY_SIZE(strDefault), replacement);
strDefaultLength = replacementLength;
}
@@ -429,7 +421,7 @@ class DecoderReplacementFallbackBuffer : public DecoderFallbackBuffer
// Construction
DecoderReplacementFallbackBuffer(DecoderReplacementFallback* fallback)
{
- wcscpy_s(strDefault, COUNTOF(strDefault), fallback->GetDefaultString());
+ wcscpy_s(strDefault, ARRAY_SIZE(strDefault), fallback->GetDefaultString());
strDefaultLength = PAL_wcslen((const WCHAR *)fallback->GetDefaultString());
}
@@ -709,7 +701,7 @@ class EncoderReplacementFallback : public EncoderFallback
if (bFoundHigh)
throw ArgumentException("String 'replacement' contains invalid Unicode code points.", "replacement");
- wcscpy_s(strDefault, COUNTOF(strDefault), replacement);
+ wcscpy_s(strDefault, ARRAY_SIZE(strDefault), replacement);
strDefaultLength = replacementLength;
}
@@ -881,8 +873,8 @@ class EncoderReplacementFallbackBuffer : public EncoderFallbackBuffer
EncoderReplacementFallbackBuffer(EncoderReplacementFallback* fallback)
{
// 2X in case we're a surrogate pair
- wcscpy_s(strDefault, COUNTOF(strDefault), fallback->GetDefaultString());
- wcscat_s(strDefault, COUNTOF(strDefault), fallback->GetDefaultString());
+ wcscpy_s(strDefault, ARRAY_SIZE(strDefault), fallback->GetDefaultString());
+ wcscat_s(strDefault, ARRAY_SIZE(strDefault), fallback->GetDefaultString());
strDefaultLength = 2 * PAL_wcslen((const WCHAR *)fallback->GetDefaultString());
}
diff --git a/src/coreclr/pal/src/misc/dbgmsg.cpp b/src/coreclr/pal/src/misc/dbgmsg.cpp
index a41d2d1036b47d..a5f98a0b0d3231 100644
--- a/src/coreclr/pal/src/misc/dbgmsg.cpp
+++ b/src/coreclr/pal/src/misc/dbgmsg.cpp
@@ -3,8 +3,6 @@
/*++
-
-
Module Name:
misc/dbgmsg.cpp
@@ -13,8 +11,6 @@ Module Name:
Implementation of Debug Message utilies. Relay channel information,
output functions, etc.
-
-
--*/
/* PAL headers */
@@ -102,7 +98,7 @@ static const char *dbg_channel_names[]=
};
// Verify the number of elements in dbg_channel_names
-static_assert_no_msg(_countof(dbg_channel_names) == DCI_LAST);
+static_assert_no_msg(ARRAY_SIZE(dbg_channel_names) == DCI_LAST);
static const char *dbg_level_names[]=
{
diff --git a/src/coreclr/pal/src/misc/jitsupport.cpp b/src/coreclr/pal/src/misc/jitsupport.cpp
index 973de4033e3d4b..2addd1526e64c8 100644
--- a/src/coreclr/pal/src/misc/jitsupport.cpp
+++ b/src/coreclr/pal/src/misc/jitsupport.cpp
@@ -82,7 +82,7 @@ static const CpuCapability CpuCapabilities[] = {
// If the capability name is not recognized or unused at present, zero is returned.
static unsigned long LookupCpuCapabilityFlag(const char* start, size_t length)
{
- for (size_t i = 0; i < _countof(CpuCapabilities); i++)
+ for (size_t i = 0; i < ARRAY_SIZE(CpuCapabilities); i++)
{
const char* capabilityName = CpuCapabilities[i].name;
if ((length == strlen(capabilityName)) && (memcmp(start, capabilityName, length) == 0))
diff --git a/src/coreclr/pal/src/sharedmemory/sharedmemory.cpp b/src/coreclr/pal/src/sharedmemory/sharedmemory.cpp
index 4c946cc5257b74..f82b4c01a47a91 100644
--- a/src/coreclr/pal/src/sharedmemory/sharedmemory.cpp
+++ b/src/coreclr/pal/src/sharedmemory/sharedmemory.cpp
@@ -444,13 +444,13 @@ SharedMemoryId::SharedMemoryId(LPCSTR name)
if (strncmp(name, "Global\\", 7) == 0)
{
m_isSessionScope = false;
- name += _countof("Global\\") - 1;
+ name += STRING_LENGTH("Global\\");
}
else
{
if (strncmp(name, "Local\\", 6) == 0)
{
- name += _countof("Local\\") - 1;
+ name += STRING_LENGTH("Local\\");
}
m_isSessionScope = true;
}
diff --git a/src/coreclr/pal/src/synchmgr/wait.cpp b/src/coreclr/pal/src/synchmgr/wait.cpp
index 273b9617a9fb2e..bce09fe91c795e 100644
--- a/src/coreclr/pal/src/synchmgr/wait.cpp
+++ b/src/coreclr/pal/src/synchmgr/wait.cpp
@@ -56,7 +56,7 @@ static PalObjectTypeId sg_rgSignalableObjectIds[] =
otiNamedMutex,
otiSemaphore
};
-static CAllowedObjectTypes sg_aotSignalableObject(sg_rgSignalableObjectIds, _countof(sg_rgSignalableObjectIds));
+static CAllowedObjectTypes sg_aotSignalableObject(sg_rgSignalableObjectIds, ARRAY_SIZE(sg_rgSignalableObjectIds));
/*++
Function:
diff --git a/src/coreclr/pal/src/synchobj/mutex.cpp b/src/coreclr/pal/src/synchobj/mutex.cpp
index 85bf3da1e79a64..267176b15118aa 100644
--- a/src/coreclr/pal/src/synchobj/mutex.cpp
+++ b/src/coreclr/pal/src/synchobj/mutex.cpp
@@ -3,8 +3,6 @@
/*++
-
-
Module Name:
mutex.ccpp
@@ -16,8 +14,6 @@ Module Name:
Revision History:
-
-
--*/
#include "pal/dbgmsg.h"
@@ -90,7 +86,7 @@ CObjectType CorUnix::otNamedMutex(
static CAllowedObjectTypes aotNamedMutex(otiNamedMutex);
static PalObjectTypeId anyMutexTypeIds[] = {otiMutex, otiNamedMutex};
-static CAllowedObjectTypes aotAnyMutex(anyMutexTypeIds, _countof(anyMutexTypeIds));
+static CAllowedObjectTypes aotAnyMutex(anyMutexTypeIds, ARRAY_SIZE(anyMutexTypeIds));
/*++
Function:
@@ -125,7 +121,7 @@ CreateMutexW(
if (lpName != nullptr)
{
- int bytesWritten = WideCharToMultiByte(CP_ACP, 0, lpName, -1, utf8Name, _countof(utf8Name), nullptr, nullptr);
+ int bytesWritten = WideCharToMultiByte(CP_ACP, 0, lpName, -1, utf8Name, ARRAY_SIZE(utf8Name), nullptr, nullptr);
if (bytesWritten == 0)
{
DWORD errorCode = GetLastError();
@@ -569,7 +565,7 @@ OpenMutexW(
}
{
- int bytesWritten = WideCharToMultiByte(CP_ACP, 0, lpName, -1, utf8Name, _countof(utf8Name), nullptr, nullptr);
+ int bytesWritten = WideCharToMultiByte(CP_ACP, 0, lpName, -1, utf8Name, ARRAY_SIZE(utf8Name), nullptr, nullptr);
if (bytesWritten == 0)
{
DWORD errorCode = GetLastError();
diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp
index 7fe41410386dd5..f9d591bb849478 100644
--- a/src/coreclr/pal/src/thread/process.cpp
+++ b/src/coreclr/pal/src/thread/process.cpp
@@ -3,8 +3,6 @@
/*++
-
-
Module Name:
process.cpp
@@ -13,8 +11,6 @@ Module Name:
Implementation of process object and functions related to processes.
-
-
--*/
#include "pal/dbgmsg.h"
@@ -99,7 +95,7 @@ extern "C"
if (machret != KERN_SUCCESS) \
{ \
char _szError[1024]; \
- snprintf(_szError, _countof(_szError), "%s: %u: %s", __FUNCTION__, __LINE__, _msg); \
+ snprintf(_szError, ARRAY_SIZE(_szError), "%s: %u: %s", __FUNCTION__, __LINE__, _msg); \
mach_error(_szError, machret); \
abort(); \
} \
@@ -2993,7 +2989,7 @@ CreateProcessModules(
int devHi, devLo, inode;
char moduleName[PATH_MAX];
- if (sscanf_s(line, "%p-%p %*[-rwxsp] %p %x:%x %d %s\n", &startAddress, &endAddress, &offset, &devHi, &devLo, &inode, moduleName, _countof(moduleName)) == 7)
+ if (sscanf_s(line, "%p-%p %*[-rwxsp] %p %x:%x %d %s\n", &startAddress, &endAddress, &offset, &devHi, &devLo, &inode, moduleName, ARRAY_SIZE(moduleName)) == 7)
{
if (inode != 0)
{
diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/malloc/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/malloc/test1/test1.cpp
index 2f713a0ee96faa..067791fe866dfd 100644
--- a/src/coreclr/pal/tests/palsuite/c_runtime/malloc/test1/test1.cpp
+++ b/src/coreclr/pal/tests/palsuite/c_runtime/malloc/test1/test1.cpp
@@ -5,7 +5,7 @@
**
** Source: test1.c
**
-** Purpose: Test that malloc returns useable memory
+** Purpose: Test that malloc returns usable memory
**
**
**==========================================================================*/
diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/sprintf_s/sprintf_s.h b/src/coreclr/pal/tests/palsuite/c_runtime/sprintf_s/sprintf_s.h
index 8cfe4507ac1b87..1feb23ecec935b 100644
--- a/src/coreclr/pal/tests/palsuite/c_runtime/sprintf_s/sprintf_s.h
+++ b/src/coreclr/pal/tests/palsuite/c_runtime/sprintf_s/sprintf_s.h
@@ -17,13 +17,13 @@ inline void DoStrTest_sprintf_s(const char *formatstr, char* param, const char *
{
char buf[256] = { 0 };
- sprintf_s(buf, _countof(buf), formatstr, param);
+ sprintf_s(buf, ARRAY_SIZE(buf), formatstr, param);
if (memcmp(buf, checkstr, strlen(checkstr) + 1) != 0)
{
Fail("ERROR: failed to insert string \"%s\" into \"%s\"\n"
"Expected \"%s\" got \"%s\".\n",
param, formatstr, checkstr, buf);
- }
+ }
}
#define DoStrTest DoStrTest_sprintf_s
@@ -31,22 +31,22 @@ inline void DoWStrTest_sprintf_s(const char *formatstr, WCHAR* param, const char
{
char buf[256] = { 0 };
- sprintf_s(buf, _countof(buf), formatstr, param);
+ sprintf_s(buf, ARRAY_SIZE(buf), formatstr, param);
if (memcmp(buf, checkstr, strlen(checkstr) + 1) != 0)
{
Fail("ERROR: failed to insert wide string \"%s\" into \"%s\"\n"
"Expected \"%s\" got \"%s\".\n",
convertC(param), formatstr, checkstr, buf);
- }
+ }
}
#define DoWStrTest DoWStrTest_sprintf_s
-inline void DoPointerTest_sprintf_s(const char *formatstr, void* param, char* paramstr,
+inline void DoPointerTest_sprintf_s(const char *formatstr, void* param, char* paramstr,
const char *checkstr1)
{
char buf[256] = { 0 };
- sprintf_s(buf, _countof(buf), formatstr, param);
+ sprintf_s(buf, ARRAY_SIZE(buf), formatstr, param);
if (memcmp(buf, checkstr1, strlen(checkstr1) + 1) != 0)
{
Fail("ERROR: failed to insert %s into \"%s\"\n"
@@ -60,8 +60,8 @@ inline void DoCountTest_sprintf_s(const char *formatstr, int param, const char *
{
char buf[512] = { 0 };
int n = -1;
-
- sprintf_s(buf, _countof(buf), formatstr, &n);
+
+ sprintf_s(buf, ARRAY_SIZE(buf), formatstr, &n);
if (n != param)
{
@@ -80,7 +80,7 @@ inline void DoShortCountTest_sprintf_s(const char *formatstr, int param, const c
char buf[256] = { 0 };
short int n = -1;
- sprintf_s(buf, _countof(buf), formatstr, &n);
+ sprintf_s(buf, ARRAY_SIZE(buf), formatstr, &n);
if (n != param)
{
@@ -98,7 +98,7 @@ inline void DoCharTest_sprintf_s(const char *formatstr, char param, const char *
{
char buf[256] = { 0 };
- sprintf_s(buf, _countof(buf), formatstr, param);
+ sprintf_s(buf, ARRAY_SIZE(buf), formatstr, param);
if (memcmp(buf, checkstr, strlen(checkstr) + 1) != 0)
{
Fail("ERROR: failed to insert char \'%c\' (%d) into \"%s\"\n"
@@ -112,7 +112,7 @@ inline void DoWCharTest_sprintf_s(const char *formatstr, WCHAR param, const char
{
char buf[256] = { 0 };
- sprintf_s(buf, _countof(buf), formatstr, param);
+ sprintf_s(buf, ARRAY_SIZE(buf), formatstr, param);
if (memcmp(buf, checkstr, strlen(checkstr) + 1) != 0)
{
Fail("ERROR: failed to insert wide char \'%c\' (%d) into \"%s\"\n"
@@ -126,7 +126,7 @@ inline void DoNumTest_sprintf_s(const char *formatstr, int value, const char *ch
{
char buf[256] = { 0 };
- sprintf_s(buf, _countof(buf), formatstr, value);
+ sprintf_s(buf, ARRAY_SIZE(buf), formatstr, value);
if (memcmp(buf, checkstr, strlen(checkstr) + 1) != 0)
{
Fail("ERROR: failed to insert %#x into \"%s\"\n"
@@ -140,13 +140,13 @@ inline void DoI64Test_sprintf_s(const char *formatstr, INT64 value, char *values
{
char buf[256] = { 0 };
- sprintf_s(buf, _countof(buf), formatstr, value);
+ sprintf_s(buf, ARRAY_SIZE(buf), formatstr, value);
if (memcmp(buf, checkstr1, strlen(checkstr1) + 1) != 0)
{
Fail("ERROR: failed to insert %s into \"%s\"\n"
"Expected \"%s\", got \"%s\".\n",
valuestr, formatstr, checkstr1, buf);
- }
+ }
}
#define DoI64Test DoI64Test_sprintf_s
@@ -155,12 +155,12 @@ inline void DoDoubleTest_sprintf_s(const char *formatstr, double value, const ch
{
char buf[256] = { 0 };
- sprintf_s(buf, _countof(buf), formatstr, value);
+ sprintf_s(buf, ARRAY_SIZE(buf), formatstr, value);
if (memcmp(buf, checkstr1, strlen(checkstr1) + 1) != 0 &&
memcmp(buf, checkstr2, strlen(checkstr2) + 1) != 0)
{
Fail("ERROR: failed to insert %f into \"%s\"\n"
- "Expected \"%s\" or \"%s\", got \"%s\".\n",
+ "Expected \"%s\" or \"%s\", got \"%s\".\n",
value, formatstr, checkstr1, checkstr2, buf);
}
}
@@ -171,7 +171,7 @@ inline void DoArgumentPrecTest_sprintf_s(const char *formatstr, int precision, v
{
char buf[256];
- sprintf_s(buf, _countof(buf), formatstr, precision, param);
+ sprintf_s(buf, ARRAY_SIZE(buf), formatstr, precision, param);
if (memcmp(buf, checkstr1, strlen(checkstr1) + 1) != 0 &&
memcmp(buf, checkstr2, strlen(checkstr2) + 1) != 0)
{
@@ -188,7 +188,7 @@ inline void DoArgumentPrecDoubleTest_sprintf_s(const char *formatstr, int precis
{
char buf[256];
- sprintf_s(buf, _countof(buf), formatstr, precision, param);
+ sprintf_s(buf, ARRAY_SIZE(buf), formatstr, precision, param);
if (memcmp(buf, checkstr1, strlen(checkstr1) + 1) != 0 &&
memcmp(buf, checkstr2, strlen(checkstr2) + 1) != 0)
{
diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/sprintf_s/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/sprintf_s/test1/test1.cpp
index 12342858aff78b..6cc7fe46846006 100644
--- a/src/coreclr/pal/tests/palsuite/c_runtime/sprintf_s/test1/test1.cpp
+++ b/src/coreclr/pal/tests/palsuite/c_runtime/sprintf_s/test1/test1.cpp
@@ -16,7 +16,7 @@
#include
#include "../sprintf_s.h"
-/*
+/*
* Depends on memcmp and strlen
*/
@@ -31,7 +31,7 @@ PALTEST(c_runtime_sprintf_s_test1_paltest_sprintf_test1, "c_runtime/sprintf_s/te
}
- sprintf_s(buf, _countof(buf), "hello world");
+ sprintf_s(buf, ARRAY_SIZE(buf), "hello world");
if (memcmp(checkstr, buf, strlen(checkstr)+1) != 0)
{
diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/sscanf_s/sscanf_s.h b/src/coreclr/pal/tests/palsuite/c_runtime/sscanf_s/sscanf_s.h
index 8c66c98fcb0c6f..f4422866781c70 100644
--- a/src/coreclr/pal/tests/palsuite/c_runtime/sscanf_s/sscanf_s.h
+++ b/src/coreclr/pal/tests/palsuite/c_runtime/sscanf_s/sscanf_s.h
@@ -43,7 +43,7 @@ inline void DoStrTest_scanf_s(char *inputstr, const char *formatstr, const char
char buf[256] = { 0 };
int ret;
- ret = sscanf_s(inputstr, formatstr, buf, _countof(buf));
+ ret = sscanf_s(inputstr, formatstr, buf, ARRAY_SIZE(buf));
if (ret != 1)
{
Fail("ERROR: Expected sscanf_s to return 1, got %d.\n"
@@ -53,7 +53,7 @@ inline void DoStrTest_scanf_s(char *inputstr, const char *formatstr, const char
if (memcmp(checkstr, buf, strlen(checkstr) + 1) != 0)
{
Fail("ERROR: scanned string incorrectly from \"%s\" using \"%s\".\n"
- "Expected \"%s\", got \"%s\".\n", inputstr, formatstr, checkstr,
+ "Expected \"%s\", got \"%s\".\n", inputstr, formatstr, checkstr,
buf);
}
@@ -65,7 +65,7 @@ inline void DoWStrTest_scanf_s(char *inputstr, const char *formatstr, const WCHA
WCHAR buf[256] = { 0 };
int ret;
- ret = sscanf_s(inputstr, formatstr, buf, _countof(buf));
+ ret = sscanf_s(inputstr, formatstr, buf, ARRAY_SIZE(buf));
if (ret != 1)
{
Fail("ERROR: Expected sscanf_s to return 1, got %d.\n"
@@ -75,7 +75,7 @@ inline void DoWStrTest_scanf_s(char *inputstr, const char *formatstr, const WCHA
if (memcmp(checkstr, buf, wcslen(checkstr)*2 + 2) != 0)
{
Fail("ERROR: scanned wide string incorrectly from \"%s\" using \"%s\".\n"
- "Expected \"%s\", got \"%s\".\n", inputstr, formatstr,
+ "Expected \"%s\", got \"%s\".\n", inputstr, formatstr,
convertC(checkstr), convertC(buf));
}
@@ -138,8 +138,8 @@ inline void DoI64NumTest_scanf_s(char *inputstr, const char *formatstr, INT64 ch
if (checknum != num)
{
- sprintf_s(buf, _countof(buf), "%I64d", num);
- sprintf_s(check, _countof(check), "%I64d", checknum);
+ sprintf_s(buf, ARRAY_SIZE(buf), "%I64d", num);
+ sprintf_s(check, ARRAY_SIZE(check), "%I64d", checknum);
Fail("ERROR: scanned I64 number incorrectly from \"%s\" using \"%s\".\n"
"Expected %s, got %s.\n", inputstr, formatstr, check, buf);
}
@@ -155,7 +155,7 @@ inline void DoCharTest_scanf_s(char *inputstr, const char *formatstr, char* chec
for (i=0; i<256; i++)
buf[i] = (char)-1;
- ret = sscanf_s(inputstr, formatstr, buf, _countof(buf));
+ ret = sscanf_s(inputstr, formatstr, buf, ARRAY_SIZE(buf));
if (ret != 1)
{
Fail("ERROR: Expected sscanf_s to return 1, got %d.\n"
@@ -167,14 +167,14 @@ inline void DoCharTest_scanf_s(char *inputstr, const char *formatstr, char* chec
buf[numchars] = 0;
Fail("ERROR: scanned character(s) incorrectly from \"%s\" using \"%s\".\n"
- "Expected %s, got %s.\n", inputstr, formatstr, checkchars,
+ "Expected %s, got %s.\n", inputstr, formatstr, checkchars,
buf);
}
if (buf[numchars] != (char)-1)
{
Fail("ERROR: overflow occurred in scanning character(s) from \"%s\" "
- "using \"%s\".\nExpected %d character(s)\n", inputstr, formatstr,
+ "using \"%s\".\nExpected %d character(s)\n", inputstr, formatstr,
numchars);
}
}
@@ -189,7 +189,7 @@ inline void DoWCharTest_scanf_s(char *inputstr, const char *formatstr, WCHAR* ch
for (i=0; i<256; i++)
buf[i] = (WCHAR)-1;
- ret = sscanf_s(inputstr, formatstr, buf, _countof(buf));
+ ret = sscanf_s(inputstr, formatstr, buf, ARRAY_SIZE(buf));
if (ret != 1)
{
Fail("ERROR: Expected sscanf_s to return 1, got %d.\n"
@@ -201,14 +201,14 @@ inline void DoWCharTest_scanf_s(char *inputstr, const char *formatstr, WCHAR* ch
buf[numchars] = 0;
Fail("ERROR: scanned wide character(s) incorrectly from \"%s\" using \"%s\".\n"
- "Expected %s, got %s.\n", inputstr, formatstr, convertC(checkchars),
+ "Expected %s, got %s.\n", inputstr, formatstr, convertC(checkchars),
convertC(buf));
}
if (buf[numchars] != (WCHAR)-1)
{
Fail("ERROR: overflow occurred in scanning wide character(s) from \"%s\" "
- "using \"%s\".\nExpected %d character(s)\n", inputstr, formatstr,
+ "using \"%s\".\nExpected %d character(s)\n", inputstr, formatstr,
numchars);
}
}
@@ -236,7 +236,7 @@ inline void DoFloatTest_scanf_s(char *inputstr, const char *formatstr, float che
if (val != checkval)
{
Fail("ERROR: scanned float incorrectly from \"%s\" using \"%s\".\n"
- "Expected \"%f\", got \"%f\".\n", inputstr, formatstr, checkval,
+ "Expected \"%f\", got \"%f\".\n", inputstr, formatstr, checkval,
val);
}
diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/swprintf/swprintf.h b/src/coreclr/pal/tests/palsuite/c_runtime/swprintf/swprintf.h
index 5ca39606e8b2e1..b00ca81d8002c1 100644
--- a/src/coreclr/pal/tests/palsuite/c_runtime/swprintf/swprintf.h
+++ b/src/coreclr/pal/tests/palsuite/c_runtime/swprintf/swprintf.h
@@ -17,13 +17,13 @@ inline void DoWStrTest_swprintf_s(const WCHAR *formatstr, WCHAR *param, const WC
{
WCHAR buf[256] = { 0 };
- swprintf_s(buf, _countof(buf), formatstr, param);
+ swprintf_s(buf, ARRAY_SIZE(buf), formatstr, param);
if (memcmp(buf, checkstr, wcslen(checkstr) * 2 + 2) != 0)
{
Fail("ERROR: failed to insert wide string \"%s\" into \"%s\".\n"
- "Expected \"%s\", got \"%s\".\n",
- convertC(param), convertC(formatstr),
+ "Expected \"%s\", got \"%s\".\n",
+ convertC(param), convertC(formatstr),
convertC(checkstr), convertC(buf));
}
}
@@ -33,13 +33,13 @@ inline void DoStrTest_swprintf_s(const WCHAR *formatstr, char *param, const WCHA
{
WCHAR buf[256] = { 0 };
- swprintf_s(buf, _countof(buf), formatstr, param);
+ swprintf_s(buf, ARRAY_SIZE(buf), formatstr, param);
if (memcmp(buf, checkstr, wcslen(checkstr) * 2 + 2) != 0)
{
Fail("ERROR: failed to insert wide string \"%s\" into \"%s\".\n"
- "Expected \"%s\", got \"%s\".\n",
- param, convertC(formatstr), convertC(checkstr),
+ "Expected \"%s\", got \"%s\".\n",
+ param, convertC(formatstr), convertC(checkstr),
convertC(buf));
}
}
@@ -49,13 +49,13 @@ inline void DoPointerTest_swprintf_s(const WCHAR *formatstr, void* param, const
{
WCHAR buf[256] = { 0 };
- swprintf_s(buf, _countof(buf), formatstr, param);
+ swprintf_s(buf, ARRAY_SIZE(buf), formatstr, param);
if (memcmp(buf, checkstr1, wcslen(checkstr1)*2 + 2) != 0)
{
Fail("ERROR: failed to insert pointer to %#p into \"%s\"\n"
"Expected \"%s\", got \"%s\".\n", param,
convertC(formatstr), convertC(checkstr1), convertC(buf));
- }
+ }
}
#define DoPointerTest DoPointerTest_swprintf_s
@@ -63,7 +63,7 @@ inline void DoCharTest_swprintf_s(const WCHAR *formatstr, char param, const WCHA
{
WCHAR buf[256] = { 0 };
- swprintf_s(buf, _countof(buf), formatstr, param);
+ swprintf_s(buf, ARRAY_SIZE(buf), formatstr, param);
if (memcmp(buf, checkstr, wcslen(checkstr)*2 + 2) != 0)
{
Fail("ERROR: failed to insert char \'%c\' (%d) into \"%s\"\n"
@@ -77,7 +77,7 @@ inline void DoWCharTest_swprintf_s(const WCHAR *formatstr, WCHAR param, const WC
{
WCHAR buf[256] = { 0 };
- swprintf_s(buf, _countof(buf), formatstr, param);
+ swprintf_s(buf, ARRAY_SIZE(buf), formatstr, param);
if (memcmp(buf, checkstr, wcslen(checkstr)*2 + 2) != 0)
{
Fail("ERROR: failed to insert wide char \'%c\' (%d) into \"%s\"\n"
@@ -91,7 +91,7 @@ inline void DoNumTest_swprintf_s(const WCHAR *formatstr, int value, const WCHAR
{
WCHAR buf[256] = { 0 };
- swprintf_s(buf, _countof(buf), formatstr, value);
+ swprintf_s(buf, ARRAY_SIZE(buf), formatstr, value);
if (memcmp(buf, checkstr, wcslen(checkstr)* 2 + 2) != 0)
{
Fail("ERROR: failed to insert %#x into \"%s\"\n"
@@ -106,7 +106,7 @@ inline void DoI64Test_swprintf_s(const WCHAR *formatstr, INT64 param, char *para
{
WCHAR buf[256] = { 0 };
- swprintf_s(buf, _countof(buf), formatstr, param);
+ swprintf_s(buf, ARRAY_SIZE(buf), formatstr, param);
if (memcmp(buf, checkstr1, wcslen(checkstr1)*2 + 2) != 0)
{
Fail("ERROR: failed to insert %s into \"%s\"\n"
@@ -121,7 +121,7 @@ inline void DoDoubleTest_swprintf_s(const WCHAR *formatstr, double value, const
{
WCHAR buf[256] = { 0 };
- swprintf_s(buf, _countof(buf), formatstr, value);
+ swprintf_s(buf, ARRAY_SIZE(buf), formatstr, value);
if (memcmp(buf, checkstr1, wcslen(checkstr1)*2 + 2) != 0 &&
memcmp(buf, checkstr2, wcslen(checkstr2)*2 + 2) != 0)
{
@@ -138,7 +138,7 @@ inline void DoArgumentPrecTest_swprintf_s(const WCHAR *formatstr, int precision,
{
WCHAR buf[256];
- swprintf_s(buf, _countof(buf), formatstr, precision, param);
+ swprintf_s(buf, ARRAY_SIZE(buf), formatstr, precision, param);
if (memcmp(buf, checkstr1, wcslen(checkstr1) + 2) != 0 &&
memcmp(buf, checkstr2, wcslen(checkstr2) + 2) != 0)
{
@@ -155,7 +155,7 @@ inline void DoArgumentPrecDoubleTest_swprintf_s(const WCHAR *formatstr, int prec
{
WCHAR buf[256];
- swprintf_s(buf, _countof(buf), formatstr, precision, param);
+ swprintf_s(buf, ARRAY_SIZE(buf), formatstr, precision, param);
if (memcmp(buf, checkstr1, wcslen(checkstr1) + 2) != 0 &&
memcmp(buf, checkstr2, wcslen(checkstr2) + 2) != 0)
{
diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/swprintf/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/swprintf/test1/test1.cpp
index 62ec881dfca328..eb1574bbe2e2f0 100644
--- a/src/coreclr/pal/tests/palsuite/c_runtime/swprintf/test1/test1.cpp
+++ b/src/coreclr/pal/tests/palsuite/c_runtime/swprintf/test1/test1.cpp
@@ -30,7 +30,7 @@ PALTEST(c_runtime_swprintf_test1_paltest_swprintf_test1, "c_runtime/swprintf/tes
}
checkstr = convert("hello world");
- swprintf_s(buf, _countof(buf), convert("hello world"));
+ swprintf_s(buf, ARRAY_SIZE(buf), convert("hello world"));
if (memcmp(checkstr, buf, wcslen(checkstr)*2+2) != 0)
{
diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/swscanf/swscanf.h b/src/coreclr/pal/tests/palsuite/c_runtime/swscanf/swscanf.h
index a5b0228bc01f36..0950263bfae63d 100644
--- a/src/coreclr/pal/tests/palsuite/c_runtime/swscanf/swscanf.h
+++ b/src/coreclr/pal/tests/palsuite/c_runtime/swscanf/swscanf.h
@@ -23,7 +23,7 @@ inline void DoVoidTest_swscanf(WCHAR *inputstr, const WCHAR *formatstr)
if (ret != 0)
{
Fail("ERROR: Expected sscanf to return 0, got %d.\n"
- "Using \"%s\" in \"%s\".\n", ret, convertC(inputstr),
+ "Using \"%s\" in \"%s\".\n", ret, convertC(inputstr),
convertC(formatstr));
}
@@ -32,7 +32,7 @@ inline void DoVoidTest_swscanf(WCHAR *inputstr, const WCHAR *formatstr)
if (buf[i] != 0)
{
Fail("ERROR: Parameter unexpectedly modified scanning \"%s\" "
- "using \"%s\".\n", convertC(inputstr),
+ "using \"%s\".\n", convertC(inputstr),
convertC(formatstr));
}
}
@@ -49,15 +49,15 @@ inline void DoStrTest_swscanf(WCHAR *inputstr, const WCHAR *formatstr, const cha
if (ret != 1)
{
Fail("ERROR: Expected swscanf to return 1, got %d.\n"
- "Using \"%s\" in \"%s\".\n", ret, convertC(inputstr),
+ "Using \"%s\" in \"%s\".\n", ret, convertC(inputstr),
convertC(formatstr));
}
if (memcmp(checkstr, buf, strlen(checkstr) + 1) != 0)
{
Fail("ERROR: scanned string incorrectly from \"%s\" using \"%s\".\n"
- "Expected \"%s\", got \"%s\".\n", convertC(inputstr),
- convertC(formatstr), checkstr,
+ "Expected \"%s\", got \"%s\".\n", convertC(inputstr),
+ convertC(formatstr), checkstr,
buf);
}
@@ -80,8 +80,8 @@ inline void DoWStrTest_swscanf(WCHAR *inputstr, const WCHAR *formatstr, const WC
if (memcmp(checkstr, buf, wcslen(checkstr)*2 + 2) != 0)
{
Fail("ERROR: scanned wide string incorrectly from \"%s\" using \"%s\".\n"
- "Expected \"%s\", got \"%s\".\n", convertC(inputstr),
- convertC(formatstr), convertC(checkstr),
+ "Expected \"%s\", got \"%s\".\n", convertC(inputstr),
+ convertC(formatstr), convertC(checkstr),
convertC(buf));
}
@@ -97,14 +97,14 @@ inline void DoNumTest_swscanf(WCHAR *inputstr, const WCHAR *formatstr, int check
if (ret != 1)
{
Fail("ERROR: Expected swscanf to return 1, got %d.\n"
- "Using \"%s\" in \"%s\".\n", ret, convertC(inputstr),
+ "Using \"%s\" in \"%s\".\n", ret, convertC(inputstr),
convertC(formatstr));
}
if (checknum != num)
{
Fail("ERROR: scanned number incorrectly from \"%s\" using \"%s\".\n"
- "Expected %d, got %d.\n", convertC(inputstr),
+ "Expected %d, got %d.\n", convertC(inputstr),
convertC(formatstr), checknum, num);
}
}
@@ -119,14 +119,14 @@ inline void DoShortNumTest_swscanf(WCHAR *inputstr, const WCHAR *formatstr, shor
if (ret != 1)
{
Fail("ERROR: Expected swscanf to return 1, got %d.\n"
- "Using \"%s\" in \"%s\".\n", ret, convertC(inputstr),
+ "Using \"%s\" in \"%s\".\n", ret, convertC(inputstr),
convertC(formatstr));
}
if (checknum != num)
{
Fail("ERROR: scanned number incorrectly from \"%s\" using \"%s\".\n"
- "Expected %hd, got %hd.\n", convertC(inputstr),
+ "Expected %hd, got %hd.\n", convertC(inputstr),
convertC(formatstr), checknum, num);
}
}
@@ -143,16 +143,16 @@ inline void DoI64NumTest_swscanf(WCHAR *inputstr, const WCHAR *formatstr, INT64
if (ret != 1)
{
Fail("ERROR: Expected swscanf to return 1, got %d.\n"
- "Using \"%s\" in \"%s\".\n", ret, convertC(inputstr),
+ "Using \"%s\" in \"%s\".\n", ret, convertC(inputstr),
convertC(formatstr));
}
if (checknum != num)
{
- sprintf_s(buf, _countof(buf), "%I64d", num);
- sprintf_s(check, _countof(check), "%I64d", checknum);
+ sprintf_s(buf, ARRAY_SIZE(buf), "%I64d", num);
+ sprintf_s(check, ARRAY_SIZE(check), "%I64d", checknum);
Fail("ERROR: scanned I64 number incorrectly from \"%s\" using \"%s\".\n"
- "Expected %s, got %s.\n", convertC(inputstr),
+ "Expected %s, got %s.\n", convertC(inputstr),
convertC(formatstr), check, buf);
}
}
@@ -171,7 +171,7 @@ inline void DoCharTest_swscanf(WCHAR *inputstr, const WCHAR *formatstr, char* ch
if (ret != 1)
{
Fail("ERROR: Expected swscanf to return 1, got %d.\n"
- "Using \"%s\" in \"%s\".\n", ret, convertC(inputstr),
+ "Using \"%s\" in \"%s\".\n", ret, convertC(inputstr),
convertC(formatstr));
}
@@ -180,14 +180,14 @@ inline void DoCharTest_swscanf(WCHAR *inputstr, const WCHAR *formatstr, char* ch
buf[numchars] = 0;
Fail("ERROR: scanned character(s) incorrectly from \"%s\" using \"%s\".\n"
- "Expected %s, got %s.\n", convertC(inputstr),
+ "Expected %s, got %s.\n", convertC(inputstr),
convertC(formatstr), checkchars, buf);
}
if (buf[numchars] != (char)-1)
{
Fail("ERROR: overflow occurred in scanning character(s) from \"%s\" "
- "using \"%s\".\nExpected %d character(s)\n",
+ "using \"%s\".\nExpected %d character(s)\n",
convertC(inputstr), convertC(formatstr), numchars);
}
}
@@ -206,7 +206,7 @@ inline void DoWCharTest_swscanf(WCHAR *inputstr, const WCHAR *formatstr, const W
if (ret != 1)
{
Fail("ERROR: Expected swscanf to return 1, got %d.\n"
- "Using \"%s\" in \"%s\".\n", ret, convertC(inputstr),
+ "Using \"%s\" in \"%s\".\n", ret, convertC(inputstr),
convertC(formatstr));
}
@@ -215,15 +215,15 @@ inline void DoWCharTest_swscanf(WCHAR *inputstr, const WCHAR *formatstr, const W
buf[numchars] = 0;
Fail("ERROR: scanned wide character(s) incorrectly from \"%s\" using \"%s\".\n"
- "Expected %s, got %s.\n", convertC(inputstr),
- convertC(formatstr), convertC(checkchars),
+ "Expected %s, got %s.\n", convertC(inputstr),
+ convertC(formatstr), convertC(checkchars),
convertC(buf));
}
if (buf[numchars] != (WCHAR)-1)
{
Fail("ERROR: overflow occurred in scanning wide character(s) from \"%s\" "
- "using \"%s\".\nExpected %d character(s)\n",
+ "using \"%s\".\nExpected %d character(s)\n",
convertC(inputstr), convertC(formatstr), numchars);
}
}
@@ -245,14 +245,14 @@ inline void DoFloatTest_swscanf(WCHAR *inputstr, const WCHAR *formatstr, float c
if (ret != 1)
{
Fail("ERROR: Expected swscanf to return 1, got %d.\n"
- "Using \"%s\" in \"%s\".\n", ret, convertC(inputstr),
+ "Using \"%s\" in \"%s\".\n", ret, convertC(inputstr),
convertC(formatstr));
}
if (val != checkval)
{
Fail("ERROR: scanned float incorrectly from \"%s\" using \"%s\".\n"
- "Expected \"%f\", got \"%f\".\n", convertC(inputstr),
+ "Expected \"%f\", got \"%f\".\n", convertC(inputstr),
convertC(formatstr), checkval, val);
}
diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/vsprintf/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/vsprintf/test1/test1.cpp
index 09b0b9f011148c..74e4b0c85083d3 100644
--- a/src/coreclr/pal/tests/palsuite/c_runtime/vsprintf/test1/test1.cpp
+++ b/src/coreclr/pal/tests/palsuite/c_runtime/vsprintf/test1/test1.cpp
@@ -22,13 +22,13 @@ PALTEST(c_runtime_vsprintf_test1_paltest_vsprintf_test1, "c_runtime/vsprintf/tes
char checkstr[] = "hello world";
char buf[256] = { 0 };
int ret;
-
+
if (PAL_Initialize(argc, argv) != 0)
{
return(FAIL);
}
- testvsp(buf, _countof(buf), "hello world");
+ testvsp(buf, ARRAY_SIZE(buf), "hello world");
if (memcmp(checkstr, buf, strlen(checkstr)+1) != 0)
{
@@ -36,8 +36,8 @@ PALTEST(c_runtime_vsprintf_test1_paltest_vsprintf_test1, "c_runtime/vsprintf/tes
checkstr, 256, buf);
}
- testvsp(buf, _countof(buf), "xxxxxxxxxxxxxxxxx");
- ret = testvsp(buf, _countof(buf), "hello world");
+ testvsp(buf, ARRAY_SIZE(buf), "xxxxxxxxxxxxxxxxx");
+ ret = testvsp(buf, ARRAY_SIZE(buf), "hello world");
if (ret != strlen(checkstr))
{
diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/vsprintf/vsprintf.h b/src/coreclr/pal/tests/palsuite/c_runtime/vsprintf/vsprintf.h
index 00e459b3db0c79..638f90490b70d4 100644
--- a/src/coreclr/pal/tests/palsuite/c_runtime/vsprintf/vsprintf.h
+++ b/src/coreclr/pal/tests/palsuite/c_runtime/vsprintf/vsprintf.h
@@ -29,7 +29,7 @@ inline void DoStrTest_vsprintf(const char *formatstr, char* param, const char *c
{
char buf[256] = { 0 };
- testvsp(buf, _countof(buf), formatstr, param);
+ testvsp(buf, ARRAY_SIZE(buf), formatstr, param);
if (memcmp(buf, checkstr, strlen(buf) + 1) != 0)
{
Fail("ERROR: failed to insert string \"%s\" into \"%s\"\n"
@@ -43,7 +43,7 @@ inline void DoWStrTest_vsprintf(const char *formatstr, WCHAR* param, const char
{
char buf[256] = { 0 };
- testvsp(buf, _countof(buf), formatstr, param);
+ testvsp(buf, ARRAY_SIZE(buf), formatstr, param);
if (memcmp(buf, checkstr, strlen(buf) + 1) != 0)
{
Fail("ERROR: failed to insert wide string \"%s\" into \"%s\"\n"
@@ -57,7 +57,7 @@ inline void DoCharTest_vsprintf(const char *formatstr, char param, const char *c
{
char buf[256] = { 0 };
- testvsp(buf, _countof(buf), formatstr, param);
+ testvsp(buf, ARRAY_SIZE(buf), formatstr, param);
if (memcmp(buf, checkstr, strlen(buf) + 1) != 0)
{
Fail("ERROR: failed to insert char \'%c\' (%d) into \"%s\"\n"
@@ -71,7 +71,7 @@ inline void DoWCharTest_vsprintf(const char *formatstr, WCHAR param, const char
{
char buf[256] = { 0 };
- testvsp(buf, _countof(buf), formatstr, param);
+ testvsp(buf, ARRAY_SIZE(buf), formatstr, param);
if (memcmp(buf, checkstr, strlen(buf) + 1) != 0)
{
Fail("ERROR: failed to insert wide char \'%c\' (%d) into \"%s\"\n"
@@ -85,7 +85,7 @@ inline void DoNumTest_vsprintf(const char *formatstr, int value, const char *che
{
char buf[256] = { 0 };
- testvsp(buf, _countof(buf), formatstr, value);
+ testvsp(buf, ARRAY_SIZE(buf), formatstr, value);
if (memcmp(buf, checkstr, strlen(buf) + 1) != 0)
{
Fail("ERROR: failed to insert %#x into \"%s\"\n"
@@ -99,7 +99,7 @@ inline void DoI64Test_vsprintf(const char *formatstr, INT64 value, char *valuest
{
char buf[256] = { 0 };
- testvsp(buf, _countof(buf), formatstr, value);
+ testvsp(buf, ARRAY_SIZE(buf), formatstr, value);
if (memcmp(buf, checkstr, strlen(buf) + 1) != 0)
{
Fail("ERROR: failed to insert %s into \"%s\"\n"
@@ -114,7 +114,7 @@ inline void DoDoubleTest_vsprintf(const char *formatstr, double value, const cha
{
char buf[256] = { 0 };
- testvsp(buf, _countof(buf), formatstr, value);
+ testvsp(buf, ARRAY_SIZE(buf), formatstr, value);
if (memcmp(buf, checkstr1, strlen(checkstr1) + 1) != 0 &&
memcmp(buf, checkstr2, strlen(checkstr2) + 1) != 0)
{
@@ -131,7 +131,7 @@ inline void DoArgumentPrecTest_vsprintf(const char *formatstr, int precision, vo
{
char buf[256];
- testvsp(buf, _countof(buf), formatstr, precision, param);
+ testvsp(buf, ARRAY_SIZE(buf), formatstr, precision, param);
if (memcmp(buf, checkstr1, strlen(checkstr1) + 1) != 0 &&
memcmp(buf, checkstr2, strlen(checkstr2) + 1) != 0)
{
@@ -148,7 +148,7 @@ inline void DoArgumentPrecDoubleTest_vsprintf(const char *formatstr, int precisi
{
char buf[256];
- testvsp(buf, _countof(buf), formatstr, precision, param);
+ testvsp(buf, ARRAY_SIZE(buf), formatstr, precision, param);
if (memcmp(buf, checkstr1, strlen(checkstr1) + 1) != 0 &&
memcmp(buf, checkstr2, strlen(checkstr2) + 1) != 0)
{
@@ -165,7 +165,7 @@ inline void DoPointerTest_vsprintf(const char *formatstr, void* param, char* par
{
char buf[256] = { 0 };
- testvsp(buf, _countof(buf), formatstr, param);
+ testvsp(buf, ARRAY_SIZE(buf), formatstr, param);
if (memcmp(buf, checkstr1, strlen(checkstr1) + 1))
{
Fail("ERROR: failed to insert %s into \"%s\"\n"
@@ -180,7 +180,7 @@ inline void DoI64DoubleTest_vsprintf(const char *formatstr, INT64 value, char *v
{
char buf[256] = { 0 };
- testvsp(buf, _countof(buf), formatstr, value);
+ testvsp(buf, ARRAY_SIZE(buf), formatstr, value);
if (memcmp(buf, checkstr1, strlen(checkstr1) + 1) != 0)
{
Fail("ERROR: failed to insert %s into \"%s\"\n"
@@ -195,7 +195,7 @@ inline void DoTest_vsprintf(const char *formatstr, int param, const char *checks
char buf[256] = { 0 };
int n = -1;
- testvsp(buf, _countof(buf), formatstr, &n);
+ testvsp(buf, ARRAY_SIZE(buf), formatstr, &n);
if (n != param)
{
@@ -214,7 +214,7 @@ inline void DoShortTest_vsprintf(const char *formatstr, int param, const char *c
char buf[256] = { 0 };
short int n = -1;
- testvsp(buf, _countof(buf), formatstr, &n);
+ testvsp(buf, ARRAY_SIZE(buf), formatstr, &n);
if (n != param)
{
diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/vswprintf/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/vswprintf/test1/test1.cpp
index 954f8f99f13e13..41e59d6b3a56a5 100644
--- a/src/coreclr/pal/tests/palsuite/c_runtime/vswprintf/test1/test1.cpp
+++ b/src/coreclr/pal/tests/palsuite/c_runtime/vswprintf/test1/test1.cpp
@@ -25,11 +25,11 @@ PALTEST(c_runtime_vswprintf_test1_paltest_vswprintf_test1, "c_runtime/vswprintf/
return(FAIL);
checkstr = convert("hello world");
- testvswp(buf, _countof(buf), checkstr);
+ testvswp(buf, ARRAY_SIZE(buf), checkstr);
if (memcmp(checkstr, buf, wcslen(checkstr)*2+2) != 0)
{
- Fail("ERROR: Expected \"%s\", got \"%s\"\n",
+ Fail("ERROR: Expected \"%s\", got \"%s\"\n",
convertC(checkstr), convertC(buf));
}
diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/vswprintf/test19/test19.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/vswprintf/test19/test19.cpp
index 482c442f03a2ce..eed7f216d255bb 100644
--- a/src/coreclr/pal/tests/palsuite/c_runtime/vswprintf/test19/test19.cpp
+++ b/src/coreclr/pal/tests/palsuite/c_runtime/vswprintf/test19/test19.cpp
@@ -9,7 +9,7 @@
**
**
**===================================================================*/
-
+
#include
#include "../vswprintf.h"
@@ -22,38 +22,38 @@ void DoArgumentPrecTest_vswprintf(WCHAR *formatstr, int precision, void *param,
WCHAR *paramstr, WCHAR *checkstr1, WCHAR *checkstr2)
{
WCHAR buf[256];
-
- testvswp(buf, _countof(buf), formatstr, precision, param);
+
+ testvswp(buf, ARRAY_SIZE(buf), formatstr, precision, param);
if (memcmp(buf, checkstr1, wcslen(checkstr1) + 2) != 0 &&
memcmp(buf, checkstr2, wcslen(checkstr2) + 2) != 0)
{
Fail("ERROR: failed to insert %s into \"%s\" with precision %d\n"
- "Expected \"%s\" or \"%s\", got \"%s\".\n",
+ "Expected \"%s\" or \"%s\", got \"%s\".\n",
paramstr,
convertC(formatstr),
precision,
convertC(checkstr1),
convertC(checkstr2),
convertC(buf));
- }
+ }
}
void DoArgumentPrecDoubleTest_vswprintf(WCHAR *formatstr, int precision, double param,
WCHAR *checkstr1, WCHAR *checkstr2)
{
WCHAR buf[256];
- testvswp(buf, _countof(buf), formatstr, precision, param);
+ testvswp(buf, ARRAY_SIZE(buf), formatstr, precision, param);
if (memcmp(buf, checkstr1, wcslen(checkstr1) + 2) != 0 &&
memcmp(buf, checkstr2, wcslen(checkstr2) + 2) != 0)
{
Fail("ERROR: failed to insert %f into \"%s\" with precision %d\n"
- "Expected \"%s\" or \"%s\", got \"%s\".\n",
+ "Expected \"%s\" or \"%s\", got \"%s\".\n",
param, convertC(formatstr),
precision,
convertC(checkstr1),
convertC(checkstr2),
convertC(buf));
- }
+ }
}
/*
@@ -66,15 +66,15 @@ PALTEST(c_runtime_vswprintf_test19_paltest_vswprintf_test19, "c_runtime/vswprint
if (PAL_Initialize(argc, argv) != 0)
return(FAIL);
- DoArgumentPrecTest_vswprintf(convert("%.*s"), 2, (void*)convert("bar"), convert("bar"),
+ DoArgumentPrecTest_vswprintf(convert("%.*s"), 2, (void*)convert("bar"), convert("bar"),
convert("ba"), convert("ba"));
- DoArgumentPrecTest_vswprintf(convert("%.*c"), 0, (void*)'a', convert("a"),
+ DoArgumentPrecTest_vswprintf(convert("%.*c"), 0, (void*)'a', convert("a"),
convert("a"), convert("a"));
- DoArgumentPrecTest_vswprintf(convert("%.*c"), 4, (void*)'a', convert("a"),
+ DoArgumentPrecTest_vswprintf(convert("%.*c"), 4, (void*)'a', convert("a"),
convert("a"), convert("a"));
- DoArgumentPrecTest_vswprintf(convert("%.*C"), 0, (void*)'a', convert("a"),
+ DoArgumentPrecTest_vswprintf(convert("%.*C"), 0, (void*)'a', convert("a"),
convert("a"), convert("a"));
- DoArgumentPrecTest_vswprintf(convert("%.*C"), 4, (void*)'a', convert("a"),
+ DoArgumentPrecTest_vswprintf(convert("%.*C"), 4, (void*)'a', convert("a"),
convert("a"), convert("a"));
DoArgumentPrecTest_vswprintf(convert("%.*d"), 1, (void*)42, convert("42"),
convert("42"), convert("42"));
@@ -96,9 +96,9 @@ PALTEST(c_runtime_vswprintf_test19_paltest_vswprintf_test19, "c_runtime/vswprint
convert("42"), convert("42"));
DoArgumentPrecTest_vswprintf(convert("%.*x"), 3, (void*)0x42, convert("0x42"),
convert("042"), convert("042"));
- DoArgumentPrecTest_vswprintf(convert("%.*X"), 1, (void*)0x42, convert("0x42"),
+ DoArgumentPrecTest_vswprintf(convert("%.*X"), 1, (void*)0x42, convert("0x42"),
convert("42"), convert("42"));
- DoArgumentPrecTest_vswprintf(convert("%.*X"), 3, (void*)0x42, convert("0x42"),
+ DoArgumentPrecTest_vswprintf(convert("%.*X"), 3, (void*)0x42, convert("0x42"),
convert("042"), convert("042"));
@@ -118,7 +118,7 @@ PALTEST(c_runtime_vswprintf_test19_paltest_vswprintf_test19, "c_runtime/vswprint
convert("3e+02"));
DoArgumentPrecDoubleTest_vswprintf(convert("%.*g"), 3, 256.01, convert("256"),
convert("256"));
- DoArgumentPrecDoubleTest_vswprintf(convert("%.*g"), 4, 256.01, convert("256"),
+ DoArgumentPrecDoubleTest_vswprintf(convert("%.*g"), 4, 256.01, convert("256"),
convert("256"));
DoArgumentPrecDoubleTest_vswprintf(convert("%.*g"), 6, 256.01, convert("256.01"),
convert("256.01"));
@@ -126,7 +126,7 @@ PALTEST(c_runtime_vswprintf_test19_paltest_vswprintf_test19, "c_runtime/vswprint
convert("3E+02"));
DoArgumentPrecDoubleTest_vswprintf(convert("%.*G"), 3, 256.01, convert("256"),
convert("256"));
- DoArgumentPrecDoubleTest_vswprintf(convert("%.*G"), 4, 256.01, convert("256"),
+ DoArgumentPrecDoubleTest_vswprintf(convert("%.*G"), 4, 256.01, convert("256"),
convert("256"));
DoArgumentPrecDoubleTest_vswprintf(convert("%.*G"), 6, 256.01, convert("256.01"),
convert("256.01"));
diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/vswprintf/test4/test4.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/vswprintf/test4/test4.cpp
index 8e72f735224a2d..4d933e1dcfbb37 100644
--- a/src/coreclr/pal/tests/palsuite/c_runtime/vswprintf/test4/test4.cpp
+++ b/src/coreclr/pal/tests/palsuite/c_runtime/vswprintf/test4/test4.cpp
@@ -20,25 +20,25 @@ static void DoPointerTest(WCHAR *formatstr, void* param, WCHAR* paramstr,
{
WCHAR buf[256] = { 0 };
- testvswp(buf, _countof(buf), formatstr, param);
+ testvswp(buf, ARRAY_SIZE(buf), formatstr, param);
if (memcmp(buf, checkstr1, wcslen(checkstr1) + 2) != 0)
{
Fail("ERROR: failed to insert pointer to %#p into \"%s\"\n"
"Expected \"%s\" got \"%s\".\n",
paramstr,
- convertC(formatstr),
- convertC(checkstr1),
+ convertC(formatstr),
+ convertC(checkstr1),
convertC(buf));
- }
+ }
}
static void DoI64DoubleTest(WCHAR *formatstr, INT64 value, WCHAR *valuestr,
WCHAR *checkstr1)
{
WCHAR buf[256] = { 0 };
-
- testvswp(buf, _countof(buf), formatstr, value);
+
+ testvswp(buf, ARRAY_SIZE(buf), formatstr, value);
if (memcmp(buf, checkstr1, wcslen(checkstr1) + 2) != 0)
{
Fail("ERROR: failed to insert %s into \"%s\"\n"
@@ -54,63 +54,63 @@ PALTEST(c_runtime_vswprintf_test4_paltest_vswprintf_test4, "c_runtime/vswprintf/
{
void *ptr = (void*) 0x123456;
INT64 lptr = I64(0x1234567887654321);
-
+
if (PAL_Initialize(argc, argv) != 0)
return(FAIL);
-
+
/*
** Run only on 64 bit platforms
*/
#if defined(HOST_64BIT)
Trace("Testing for 64 Bit Platforms \n");
DoPointerTest(convert("%p"), NULL, convert("NULL"), convert("0000000000000000"));
- DoPointerTest(convert("%p"), ptr, convert("pointer to 0x123456"),
+ DoPointerTest(convert("%p"), ptr, convert("pointer to 0x123456"),
convert("0000000000123456"));
- DoPointerTest(convert("%17p"), ptr, convert("pointer to 0x123456"),
+ DoPointerTest(convert("%17p"), ptr, convert("pointer to 0x123456"),
convert(" 0000000000123456"));
- DoPointerTest(convert("%17p"), ptr, convert("pointer to 0x123456"),
+ DoPointerTest(convert("%17p"), ptr, convert("pointer to 0x123456"),
convert(" 0000000000123456"));
- DoPointerTest(convert("%-17p"), ptr, convert("pointer to 0x123456"),
+ DoPointerTest(convert("%-17p"), ptr, convert("pointer to 0x123456"),
convert("0000000000123456 "));
- DoPointerTest(convert("%+p"), ptr, convert("pointer to 0x123456"),
+ DoPointerTest(convert("%+p"), ptr, convert("pointer to 0x123456"),
convert("0000000000123456"));
- DoPointerTest(convert("%#p"), ptr, convert("pointer to 0x123456"),
+ DoPointerTest(convert("%#p"), ptr, convert("pointer to 0x123456"),
convert("0X0000000000123456"));
- DoPointerTest(convert("%lp"), ptr, convert("pointer to 0x123456"),
+ DoPointerTest(convert("%lp"), ptr, convert("pointer to 0x123456"),
convert("00123456"));
- DoPointerTest(convert("%hp"), ptr, convert("pointer to 0x123456"),
+ DoPointerTest(convert("%hp"), ptr, convert("pointer to 0x123456"),
convert("00003456"));
- DoPointerTest(convert("%Lp"), ptr, convert("pointer to 0x123456"),
+ DoPointerTest(convert("%Lp"), ptr, convert("pointer to 0x123456"),
convert("00123456"));
- DoI64DoubleTest(convert("%I64p"), lptr,
+ DoI64DoubleTest(convert("%I64p"), lptr,
convert("pointer to 0x1234567887654321"), convert("1234567887654321"));
#else
Trace("Testing for Non 64 Bit Platforms \n");
DoPointerTest(convert("%p"), NULL, convert("NULL"), convert("00000000"));
- DoPointerTest(convert("%p"), ptr, convert("pointer to 0x123456"),
+ DoPointerTest(convert("%p"), ptr, convert("pointer to 0x123456"),
convert("00123456"));
- DoPointerTest(convert("%9p"), ptr, convert("pointer to 0x123456"),
+ DoPointerTest(convert("%9p"), ptr, convert("pointer to 0x123456"),
convert(" 00123456"));
- DoPointerTest(convert("%09p"), ptr, convert("pointer to 0x123456"),
+ DoPointerTest(convert("%09p"), ptr, convert("pointer to 0x123456"),
convert(" 00123456"));
- DoPointerTest(convert("%-9p"), ptr, convert("pointer to 0x123456"),
+ DoPointerTest(convert("%-9p"), ptr, convert("pointer to 0x123456"),
convert("00123456 "));
- DoPointerTest(convert("%+p"), ptr, convert("pointer to 0x123456"),
+ DoPointerTest(convert("%+p"), ptr, convert("pointer to 0x123456"),
convert("00123456"));
- DoPointerTest(convert("%#p"), ptr, convert("pointer to 0x123456"),
+ DoPointerTest(convert("%#p"), ptr, convert("pointer to 0x123456"),
convert("0X00123456"));
- DoPointerTest(convert("%lp"), ptr, convert("pointer to 0x123456"),
+ DoPointerTest(convert("%lp"), ptr, convert("pointer to 0x123456"),
convert("00123456"));
- DoPointerTest(convert("%hp"), ptr, convert("pointer to 0x123456"),
+ DoPointerTest(convert("%hp"), ptr, convert("pointer to 0x123456"),
convert("00003456"));
- DoPointerTest(convert("%Lp"), ptr, convert("pointer to 0x123456"),
+ DoPointerTest(convert("%Lp"), ptr, convert("pointer to 0x123456"),
convert("00123456"));
- DoI64DoubleTest(convert("%I64p"), lptr,
+ DoI64DoubleTest(convert("%I64p"), lptr,
convert("pointer to 0x1234567887654321"), convert("1234567887654321"));
#endif
-
+
PAL_Terminate();
return PASS;
}
diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/vswprintf/vswprintf.h b/src/coreclr/pal/tests/palsuite/c_runtime/vswprintf/vswprintf.h
index c360c00db4e04d..70cee85e7f6170 100644
--- a/src/coreclr/pal/tests/palsuite/c_runtime/vswprintf/vswprintf.h
+++ b/src/coreclr/pal/tests/palsuite/c_runtime/vswprintf/vswprintf.h
@@ -30,13 +30,13 @@ inline void DoWStrTest_vswprintf_s(const WCHAR *formatstr, WCHAR *param, const W
{
WCHAR buf[256] = { 0 };
- testvswp(buf, _countof(buf), formatstr, param);
+ testvswp(buf, ARRAY_SIZE(buf), formatstr, param);
if (memcmp(buf, checkstr, wcslen(buf) * 2 + 2) != 0)
{
Fail("ERROR: failed to insert wide string \"%s\" into \"%s\".\n"
- "Expected \"%s\", got \"%s\".\n",
- convertC(param), convertC(formatstr),
+ "Expected \"%s\", got \"%s\".\n",
+ convertC(param), convertC(formatstr),
convertC(checkstr), convertC(buf));
}
}
@@ -46,13 +46,13 @@ inline void DoStrTest_vswprintf_s(const WCHAR *formatstr, char *param, const WCH
{
WCHAR buf[256] = { 0 };
- testvswp(buf, _countof(buf), formatstr, param);
+ testvswp(buf, ARRAY_SIZE(buf), formatstr, param);
if (memcmp(buf, checkstr, wcslen(buf) * 2 + 2) != 0)
{
Fail("ERROR: failed to insert wide string \"%s\" into \"%s\".\n"
- "Expected \"%s\", got \"%s\".\n",
- param, convertC(formatstr), convertC(checkstr),
+ "Expected \"%s\", got \"%s\".\n",
+ param, convertC(formatstr), convertC(checkstr),
convertC(buf));
}
}
@@ -62,14 +62,14 @@ inline void DoCharTest_vswprintf_s(const WCHAR *formatstr, char param, const WCH
{
WCHAR buf[256] = { 0 };
- testvswp(buf, _countof(buf), formatstr, param);
+ testvswp(buf, ARRAY_SIZE(buf), formatstr, param);
if (memcmp(buf, checkstr, wcslen(buf)*2 + 2) != 0)
{
Fail("ERROR: failed to insert char \'%c\' (%d) into \"%s\"\n"
- "Expected \"%s\" got \"%s\".\n",
- param, param, convertC(formatstr), convertC(checkstr),
+ "Expected \"%s\" got \"%s\".\n",
+ param, param, convertC(formatstr), convertC(checkstr),
convertC(buf));
- }
+ }
}
#define DoCharTest DoCharTest_vswprintf_s
@@ -77,14 +77,14 @@ inline void DoWCharTest_vswprintf_s(const WCHAR *formatstr, WCHAR param, const W
{
WCHAR buf[256] = { 0 };
- testvswp(buf, _countof(buf), formatstr, param);
+ testvswp(buf, ARRAY_SIZE(buf), formatstr, param);
if (memcmp(buf, checkstr, wcslen(buf)*2 + 2) != 0)
{
Fail("ERROR: failed to insert wide char \'%c\' (%d) into \"%s\"\n"
- "Expected \"%s\" got \"%s\".\n",
- (char) param, param, convertC(formatstr), convertC(checkstr),
+ "Expected \"%s\" got \"%s\".\n",
+ (char) param, param, convertC(formatstr), convertC(checkstr),
convertC(buf));
- }
+ }
}
#define DoWCharTest DoWCharTest_vswprintf_s
@@ -92,13 +92,13 @@ inline void DoNumTest_vswprintf_s(const WCHAR *formatstr, int value, const WCHAR
{
WCHAR buf[256] = { 0 };
- testvswp(buf, _countof(buf), formatstr, value);
+ testvswp(buf, ARRAY_SIZE(buf), formatstr, value);
if (memcmp(buf, checkstr, wcslen(buf)* 2 + 2) != 0)
{
Fail("ERROR: failed to insert %#x into \"%s\"\n"
- "Expected \"%s\" got \"%s\".\n", value, convertC(formatstr),
+ "Expected \"%s\" got \"%s\".\n", value, convertC(formatstr),
convertC(checkstr), convertC(buf));
- }
+ }
}
#define DoNumTest DoNumTest_vswprintf_s
@@ -106,13 +106,13 @@ inline void DoI64NumTest_vswprintf_s(const WCHAR *formatstr, INT64 value, char *
{
WCHAR buf[256] = { 0 };
- testvswp(buf, _countof(buf), formatstr, value);
+ testvswp(buf, ARRAY_SIZE(buf), formatstr, value);
if (memcmp(buf, checkstr, wcslen(buf)* 2 + 2) != 0)
{
Fail("ERROR: failed to insert %s into \"%s\"\n"
- "Expected \"%s\" got \"%s\".\n", valuestr, convertC(formatstr),
+ "Expected \"%s\" got \"%s\".\n", valuestr, convertC(formatstr),
convertC(checkstr), convertC(buf));
- }
+ }
}
#define DoI64NumTest DoI64NumTest_vswprintf_s
@@ -121,7 +121,7 @@ inline void DoDoubleTest_vswprintf_s(const WCHAR *formatstr, double value, const
{
WCHAR buf[256] = { 0 };
- testvswp(buf, _countof(buf), formatstr, value);
+ testvswp(buf, ARRAY_SIZE(buf), formatstr, value);
if (memcmp(buf, checkstr1, wcslen(checkstr1) + 2) != 0 &&
memcmp(buf, checkstr2, wcslen(checkstr2) + 2) != 0)
{
diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/wcscat/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/wcscat/test1/test1.cpp
index da8f581554b5d5..d60d483ac50b86 100644
--- a/src/coreclr/pal/tests/palsuite/c_runtime/wcscat/test1/test1.cpp
+++ b/src/coreclr/pal/tests/palsuite/c_runtime/wcscat/test1/test1.cpp
@@ -5,8 +5,8 @@
**
** Source: test1.c
**
-** Purpose:
-** Test to that wcscat correctly concatanates wide strings, including placing
+** Purpose:
+** Test to that wcscat correctly concatanates wide strings, including placing
** null pointers.
**
**
@@ -30,7 +30,7 @@ PALTEST(c_runtime_wcscat_test1_paltest_wcscat_test1, "c_runtime/wcscat/test1/pal
WCHAR *ptr;
char buffer[256];
-
+
if (PAL_Initialize(argc, argv))
{
return FAIL;
@@ -59,8 +59,8 @@ PALTEST(c_runtime_wcscat_test1_paltest_wcscat_test1, "c_runtime/wcscat/test1/pal
if (memcmp(dest, test, sizeof(test)) != 0)
{
- sprintf_s(buffer, _countof(buffer), "%S", dest);
- Fail("ERROR: Expected wcscat to give \"%s\", got \"%s\"\n",
+ sprintf_s(buffer, ARRAY_SIZE(buffer), "%S", dest);
+ Fail("ERROR: Expected wcscat to give \"%s\", got \"%s\"\n",
"foo bar baz", buffer);
}
diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/wcscpy/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/wcscpy/test1/test1.cpp
index 2c0c9afa8749cf..ba8336fa423013 100644
--- a/src/coreclr/pal/tests/palsuite/c_runtime/wcscpy/test1/test1.cpp
+++ b/src/coreclr/pal/tests/palsuite/c_runtime/wcscpy/test1/test1.cpp
@@ -25,7 +25,7 @@ PALTEST(c_runtime_wcscpy_test1_paltest_wcscpy_test1, "c_runtime/wcscpy/test1/pal
WCHAR *ret;
char buffer[256];
-
+
if (PAL_Initialize(argc, argv))
{
return FAIL;
@@ -33,10 +33,10 @@ PALTEST(c_runtime_wcscpy_test1_paltest_wcscpy_test1, "c_runtime/wcscpy/test1/pal
ret = wcscpy(dest, str);
-
+
if (ret != dest || memcmp(dest, result, sizeof(result)) != 0)
{
- sprintf_s(buffer, _countof(buffer), "%S", dest);
+ sprintf_s(buffer, ARRAY_SIZE(buffer), "%S", dest);
Fail("Expected wcscpy to give \"%s\" with a return value of %p, got \"%s\" "
"with a return value of %p.\n", "foo", dest, buffer, ret);
}
diff --git a/src/coreclr/pal/tests/palsuite/common/palsuite.h b/src/coreclr/pal/tests/palsuite/common/palsuite.h
index d5e5a921ef1453..57a56772065be0 100644
--- a/src/coreclr/pal/tests/palsuite/common/palsuite.h
+++ b/src/coreclr/pal/tests/palsuite/common/palsuite.h
@@ -23,6 +23,7 @@ typedef unsigned short char16_t;
#include
#include
#include
+#include
#define PALTEST(testfunc, testname) \
int __cdecl testfunc(int argc, char* argv[]); \
@@ -38,7 +39,7 @@ enum
inline void Trace(const char *format, ...)
{
va_list arglist;
-
+
va_start(arglist, format);
vprintf(format, arglist);
@@ -49,10 +50,10 @@ inline void Trace(const char *format, ...)
inline void Fail(const char *format, ...)
{
va_list arglist;
-
+
va_start(arglist, format);
- vprintf(format, arglist);
+ vprintf(format, arglist);
va_end(arglist);
printf("\n");
@@ -102,7 +103,7 @@ int __cdecl main(int argc, char **argv)
// time between PAL_Initialize and PAL_Terminate. However, getenv in PAL
// can be run only after PAL_Initialize.
szPerfLoopEnv = getenv(PALTEST_LOOP_ENV);
- if (szPerfLoopEnv != NULL)
+ if (szPerfLoopEnv != NULL)
{
loopCount = atoi(szPerfLoopEnv);
if (loopCount <= 0) loopCount = 1;
@@ -146,9 +147,9 @@ void Bogus_PAL_Terminate()
#ifdef BIGENDIAN
inline ULONG VAL32(ULONG x)
{
- return( ((x & 0xFF000000L) >> 24) |
- ((x & 0x00FF0000L) >> 8) |
- ((x & 0x0000FF00L) << 8) |
+ return( ((x & 0xFF000000L) >> 24) |
+ ((x & 0x00FF0000L) >> 8) |
+ ((x & 0x0000FF00L) << 8) |
((x & 0x000000FFL) << 24) );
}
#define th_htons(w) (w)
@@ -157,15 +158,12 @@ inline ULONG VAL32(ULONG x)
#define th_htons(w) (((w) >> 8) | ((w) << 8))
#endif // BIGENDIAN
-#define _countof(_array) (sizeof(_array)/sizeof(_array[0]))
-
WCHAR* convert(const char * aString);
char* convertC(const WCHAR * wString);
UINT64 GetHighPrecisionTimeStamp(LARGE_INTEGER performanceFrequency);
extern const char* szTextFile;
-
int
mkAbsoluteFilename( LPSTR dirName,
DWORD dwDirLength,
@@ -177,10 +175,10 @@ BOOL CleanupHelper (HANDLE *hArray, DWORD dwIndex);
BOOL Cleanup(HANDLE *hArray, DWORD dwIndex);
-/*
+/*
* Tokens 0 and 1 are events. Token 2 is the thread.
*/
-#define NUM_TOKENS 3
+#define NUM_TOKENS 3
extern HANDLE hToken[NUM_TOKENS];
extern CRITICAL_SECTION CriticalSection;
@@ -194,11 +192,11 @@ extern CRITICAL_SECTION CriticalSection;
* Returns: The number of wide characters in the resulting string.
* 0 is returned on Error.
*/
-int
-mkAbsoluteFilenameW (
- LPWSTR dirName,
- DWORD dwDirLength,
- LPCWSTR fileName,
+int
+mkAbsoluteFilenameW (
+ LPWSTR dirName,
+ DWORD dwDirLength,
+ LPCWSTR fileName,
DWORD dwFileLength,
LPWSTR absPathName );
@@ -211,11 +209,11 @@ mkAbsoluteFilenameW (
* Returns: The number of wide characters in the resulting string.
* 0 is returned on Error.
*/
-int
-mkAbsoluteFilenameA (
- LPSTR dirName,
- DWORD dwDirLength,
- LPCSTR fileName,
+int
+mkAbsoluteFilenameA (
+ LPSTR dirName,
+ DWORD dwDirLength,
+ LPCSTR fileName,
DWORD dwFileLength,
LPSTR absPathName );
diff --git a/src/coreclr/pal/tests/palsuite/composite/synchronization/criticalsection/readme.txt b/src/coreclr/pal/tests/palsuite/composite/synchronization/criticalsection/readme.txt
index af6ef5d230bdc7..974497cfaff94e 100644
--- a/src/coreclr/pal/tests/palsuite/composite/synchronization/criticalsection/readme.txt
+++ b/src/coreclr/pal/tests/palsuite/composite/synchronization/criticalsection/readme.txt
@@ -1,11 +1,11 @@
-To compile:
+To compile:
1) create a dat file (say criticalsection.dat) with contents:
-PAL,Composite,palsuite\composite\syncronization\criticalsection,criticalsection=mainWrapper.c,criticalsection.c,,,
+PAL,Composite,palsuite\composite\synchronization\criticalsection,criticalsection=mainWrapper.c,criticalsection.c,,,
2) perl rrunmod.pl -r criticalsection.dat
To execute:
-mainwrapper [PROCESS_COUNT] [WORKER_THREAD_MULTIPLIER_COUNT] [REPEAT_COUNT]
+mainwrapper [PROCESS_COUNT] [WORKER_THREAD_MULTIPLIER_COUNT] [REPEAT_COUNT]
diff --git a/src/coreclr/pal/tests/palsuite/debug_api/WriteProcessMemory/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/debug_api/WriteProcessMemory/test1/test1.cpp
index 41f4f825ca2508..f64291b7ec8584 100644
--- a/src/coreclr/pal/tests/palsuite/debug_api/WriteProcessMemory/test1/test1.cpp
+++ b/src/coreclr/pal/tests/palsuite/debug_api/WriteProcessMemory/test1/test1.cpp
@@ -5,9 +5,9 @@
**
** Source: test1.c
**
-** Purpose: Create a child process and some events for communications with it.
+** Purpose: Create a child process and some events for communications with it.
** When the child gets back to us with a memory location and a length,
-** Call WriteProcessMemory on this location and check to see that it
+** Call WriteProcessMemory on this location and check to see that it
** writes successfully.
**
**
@@ -27,7 +27,7 @@ PALTEST(debug_api_WriteProcessMemory_test1_paltest_writeprocessmemory_test1, "de
HANDLE hEvToHelper;
HANDLE hEvFromHelper;
DWORD dwExitCode;
-
+
DWORD dwRet;
char cmdComposeBuf[MAX_PATH];
@@ -40,33 +40,33 @@ PALTEST(debug_api_WriteProcessMemory_test1_paltest_writeprocessmemory_test1, "de
/* Create the signals we need for cross process communication */
hEvToHelper = CreateEvent(NULL, TRUE, FALSE, szcToHelperEvName);
- if (!hEvToHelper)
+ if (!hEvToHelper)
{
Fail("WriteProcessMemory: CreateEvent of '%S' failed. "
- "GetLastError() returned %d.\n", szcToHelperEvName,
+ "GetLastError() returned %d.\n", szcToHelperEvName,
GetLastError());
}
- if (GetLastError() == ERROR_ALREADY_EXISTS)
+ if (GetLastError() == ERROR_ALREADY_EXISTS)
{
Fail("WriteProcessMemory: CreateEvent of '%S' failed. "
"(already exists!)\n", szcToHelperEvName);
}
hEvFromHelper = CreateEvent(NULL, TRUE, FALSE, szcFromHelperEvName);
- if (!hEvToHelper)
+ if (!hEvToHelper)
{
Fail("WriteProcessMemory: CreateEvent of '%S' failed. "
- "GetLastError() returned %d.\n", szcFromHelperEvName,
+ "GetLastError() returned %d.\n", szcFromHelperEvName,
GetLastError());
}
- if (GetLastError() == ERROR_ALREADY_EXISTS)
+ if (GetLastError() == ERROR_ALREADY_EXISTS)
{
Fail("WriteProcessMemory: CreateEvent of '%S' failed. "
"(already exists!)\n", szcFromHelperEvName);
}
ResetEvent(hEvFromHelper);
ResetEvent(hEvToHelper);
-
- if (!sprintf_s(cmdComposeBuf, _countof(cmdComposeBuf), "helper %s", commsFileName))
+
+ if (!sprintf_s(cmdComposeBuf, ARRAY_SIZE(cmdComposeBuf), "helper %s", commsFileName))
{
Fail("Could not convert command line\n");
}
@@ -75,11 +75,11 @@ PALTEST(debug_api_WriteProcessMemory_test1_paltest_writeprocessmemory_test1, "de
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
-
+
/* Create a new process. This is the process that will ask for
* memory munging */
- if(!CreateProcess( NULL, uniString, NULL, NULL,
- FALSE, 0, NULL, NULL, &si, &pi))
+ if(!CreateProcess( NULL, uniString, NULL, NULL,
+ FALSE, 0, NULL, NULL, &si, &pi))
{
Trace("ERROR: CreateProcess failed to load executable '%S'. "
"GetLastError() returned %u.\n",
@@ -89,7 +89,7 @@ PALTEST(debug_api_WriteProcessMemory_test1_paltest_writeprocessmemory_test1, "de
}
free(uniString);
- while(1)
+ while(1)
{
FILE *commsFile;
char* pSrcMemory;
@@ -122,9 +122,9 @@ PALTEST(debug_api_WriteProcessMemory_test1_paltest_writeprocessmemory_test1, "de
}
PEDANTIC1(fclose,(commsFile));
sscanf(incomingCMDBuffer, "%u %u", &pDestMemory, &Count);
- if (argc > 1)
+ if (argc > 1)
{
- Trace("Preparing to write to %u bytes @ %u ('%s')\n",
+ Trace("Preparing to write to %u bytes @ %u ('%s')\n",
Count, pDestMemory, incomingCMDBuffer);
}
@@ -139,32 +139,32 @@ PALTEST(debug_api_WriteProcessMemory_test1_paltest_writeprocessmemory_test1, "de
memset(pSrcMemory, nextValue, Count);
/* do the work */
- dwRet = WriteProcessMemory(pi.hProcess,
+ dwRet = WriteProcessMemory(pi.hProcess,
pDestMemory,
pSrcMemory,
Count,
&wpmCount);
if (!dwRet)
{
- Trace("%s: Problem: on a write to %u bytes @ %u ('%s')\n",
+ Trace("%s: Problem: on a write to %u bytes @ %u ('%s')\n",
argv[0], Count, pDestMemory, incomingCMDBuffer);
- Trace("test1 WriteProcessMemory returned a%u(!=0) (GLE=%u)\n",
+ Trace("test1 WriteProcessMemory returned a%u(!=0) (GLE=%u)\n",
GetLastError());
}
if(Count != wpmCount)
{
- Trace("%s: Problem: on a write to %u bytes @ %u ('%s')\n",
+ Trace("%s: Problem: on a write to %u bytes @ %u ('%s')\n",
argv[0], Count, pDestMemory, incomingCMDBuffer);
Trace("The number of bytes written should have been "
"%u, but was reported as %u.\n", Count, wpmCount);
}
free(pSrcMemory);
- doneIteration:
+ doneIteration:
PEDANTIC(ResetEvent, (hEvFromHelper));
PEDANTIC(SetEvent, (hEvToHelper));
}
-
+
/* wait for the child process to complete */
WaitForSingleObject ( pi.hProcess, TIMEOUT );
/* this may return a failure code on a success path */
@@ -172,8 +172,8 @@ PALTEST(debug_api_WriteProcessMemory_test1_paltest_writeprocessmemory_test1, "de
/* check the exit code from the process */
if( ! GetExitCodeProcess( pi.hProcess, &dwExitCode ) )
{
- Trace( "GetExitCodeProcess call failed with error code %u\n",
- GetLastError() );
+ Trace( "GetExitCodeProcess call failed with error code %u\n",
+ GetLastError() );
dwExitCode = FAIL;
}
diff --git a/src/coreclr/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/test3.cpp b/src/coreclr/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/test3.cpp
index 196efb1b2abfe1..1d6cb240dfda90 100644
--- a/src/coreclr/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/test3.cpp
+++ b/src/coreclr/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/test3.cpp
@@ -7,8 +7,8 @@
**
** Purpose: Create a child process and debug it. When the child
** raises an exception, it sends back a memory location. Call
-** WriteProcessMemory on the memory location, but attempt to write
-** more than the memory allows. This should cause an error and the
+** WriteProcessMemory on the memory location, but attempt to write
+** more than the memory allows. This should cause an error and the
** data should be unchanged.
**
**
@@ -28,7 +28,7 @@ PALTEST(debug_api_WriteProcessMemory_test3_paltest_writeprocessmemory_test3, "de
HANDLE hEvToHelper;
HANDLE hEvFromHelper;
DWORD dwExitCode;
-
+
DWORD dwRet;
BOOL success = TRUE; /* assume success */
@@ -42,31 +42,31 @@ PALTEST(debug_api_WriteProcessMemory_test3_paltest_writeprocessmemory_test3, "de
/* Create the signals we need for cross process communication */
hEvToHelper = CreateEvent(NULL, TRUE, FALSE, szcToHelperEvName);
- if (!hEvToHelper)
+ if (!hEvToHelper)
{
Fail("WriteProcessMemory: CreateEvent of '%S' failed. "
- "GetLastError() returned %u.\n", szcToHelperEvName,
+ "GetLastError() returned %u.\n", szcToHelperEvName,
GetLastError());
}
- if (GetLastError() == ERROR_ALREADY_EXISTS)
+ if (GetLastError() == ERROR_ALREADY_EXISTS)
{
Fail("WriteProcessMemory: CreateEvent of '%S' failed. "
"(already exists!)\n", szcToHelperEvName);
}
hEvFromHelper = CreateEvent(NULL, TRUE, FALSE, szcFromHelperEvName);
- if (!hEvToHelper)
+ if (!hEvToHelper)
{
Fail("WriteProcessMemory: CreateEvent of '%S' failed. "
- "GetLastError() returned %u.\n", szcFromHelperEvName,
+ "GetLastError() returned %u.\n", szcFromHelperEvName,
GetLastError());
}
- if (GetLastError() == ERROR_ALREADY_EXISTS)
+ if (GetLastError() == ERROR_ALREADY_EXISTS)
{
Fail("WriteProcessMemory: CreateEvent of '%S' failed. "
"(already exists!)\n", szcFromHelperEvName);
}
-
- if (!sprintf_s(cmdComposeBuf, _countof(cmdComposeBuf), "helper %s", commsFileName))
+
+ if (!sprintf_s(cmdComposeBuf, ARRAY_SIZE(cmdComposeBuf), "helper %s", commsFileName))
{
Fail("Could not convert command line\n");
}
@@ -75,11 +75,11 @@ PALTEST(debug_api_WriteProcessMemory_test3_paltest_writeprocessmemory_test3, "de
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
-
+
/* Create a new process. This is the process that will ask for
* memory munging */
- if(!CreateProcess( NULL, uniString, NULL, NULL,
- FALSE, 0, NULL, NULL, &si, &pi))
+ if(!CreateProcess( NULL, uniString, NULL, NULL,
+ FALSE, 0, NULL, NULL, &si, &pi))
{
Trace("ERROR: CreateProcess failed to load executable '%S'. "
"GetLastError() returned %u.\n",
@@ -127,11 +127,11 @@ PALTEST(debug_api_WriteProcessMemory_test3_paltest_writeprocessmemory_test3, "de
goto doneIteration;
}
PEDANTIC1(fclose,(commsFile));
- sscanf(incomingCMDBuffer, "%u %u %u",
+ sscanf(incomingCMDBuffer, "%u %u %u",
&pDestMemory, &Count, &dwExpectedErrorCode);
- if (argc > 1)
+ if (argc > 1)
{
- Trace("Preparing to write to %u bytes @ %u ('%s')\n",
+ Trace("Preparing to write to %u bytes @ %u ('%s')\n",
Count, pDestMemory, incomingCMDBuffer);
}
@@ -147,12 +147,12 @@ PALTEST(debug_api_WriteProcessMemory_test3_paltest_writeprocessmemory_test3, "de
memset(pSrcMemory, nextValue, Count);
/* do the work */
- dwRet = WriteProcessMemory(pi.hProcess,
+ dwRet = WriteProcessMemory(pi.hProcess,
pDestMemory,
pSrcMemory,
Count,
&wpmCount);
-
+
if(dwRet != 0)
{
Trace("ERROR: Situation: '%s', return code: %u, bytes 'written': %u\n",
@@ -162,7 +162,7 @@ PALTEST(debug_api_WriteProcessMemory_test3_paltest_writeprocessmemory_test3, "de
"not completely accessible.\n");
success = FALSE;
}
-
+
if(GetLastError() != dwExpectedErrorCode)
{
Trace("ERROR: GetLastError() should have returned "
@@ -172,12 +172,12 @@ PALTEST(debug_api_WriteProcessMemory_test3_paltest_writeprocessmemory_test3, "de
}
free(pSrcMemory);
- doneIteration:
+ doneIteration:
PEDANTIC(ResetEvent, (hEvFromHelper));
PEDANTIC(SetEvent, (hEvToHelper));
}
-
+
/* wait for the child process to complete */
WaitForSingleObject ( pi.hProcess, TIMEOUT );
/* this may return a failure code on a success path */
@@ -185,8 +185,8 @@ PALTEST(debug_api_WriteProcessMemory_test3_paltest_writeprocessmemory_test3, "de
/* check the exit code from the process */
if( ! GetExitCodeProcess( pi.hProcess, &dwExitCode ) )
{
- Trace( "GetExitCodeProcess call failed with error code %u\n",
- GetLastError() );
+ Trace( "GetExitCodeProcess call failed with error code %u\n",
+ GetLastError() );
dwExitCode = FAIL;
}
if(!success)
diff --git a/src/coreclr/pal/tests/palsuite/debug_api/WriteProcessMemory/test4/test4.cpp b/src/coreclr/pal/tests/palsuite/debug_api/WriteProcessMemory/test4/test4.cpp
index 577869b78717ee..255d96c832bfec 100644
--- a/src/coreclr/pal/tests/palsuite/debug_api/WriteProcessMemory/test4/test4.cpp
+++ b/src/coreclr/pal/tests/palsuite/debug_api/WriteProcessMemory/test4/test4.cpp
@@ -18,11 +18,11 @@ const int MY_EXCEPTION=999;
PALTEST(debug_api_WriteProcessMemory_test4_paltest_writeprocessmemory_test4, "debug_api/WriteProcessMemory/test4/paltest_writeprocessmemory_test4")
{
-
+
PROCESS_INFORMATION pi;
STARTUPINFO si;
DEBUG_EVENT DebugEv;
- DWORD dwContinueStatus = DBG_CONTINUE;
+ DWORD dwContinueStatus = DBG_CONTINUE;
int Count, ret;
char* DataBuffer[4096];
char* Memory;
@@ -31,22 +31,22 @@ PALTEST(debug_api_WriteProcessMemory_test4_paltest_writeprocessmemory_test4, "de
{
return FAIL;
}
-
+
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
-
+
memset(DataBuffer, 'z', 4096);
/* Create a new process. This is the process to be Debugged */
- if(!CreateProcess( NULL, "helper", NULL, NULL,
- FALSE, 0, NULL, NULL, &si, &pi))
+ if(!CreateProcess( NULL, "helper", NULL, NULL,
+ FALSE, 0, NULL, NULL, &si, &pi))
{
Fail("ERROR: CreateProcess failed to load executable 'helper'. "
"GetLastError() returned %d.\n",GetLastError());
}
- /* Call DebugActiveProcess, because the process wasn't created as a
+ /* Call DebugActiveProcess, because the process wasn't created as a
debug process.
*/
if(DebugActiveProcess(pi.dwProcessId) == 0)
@@ -56,11 +56,11 @@ PALTEST(debug_api_WriteProcessMemory_test4_paltest_writeprocessmemory_test4, "de
GetLastError());
}
-
+
/* Call WaitForDebugEvent, which will wait until the helper process
raises an exception.
*/
-
+
while(1)
{
if(WaitForDebugEvent(&DebugEv, INFINITE) == 0)
@@ -68,53 +68,53 @@ PALTEST(debug_api_WriteProcessMemory_test4_paltest_writeprocessmemory_test4, "de
Fail("ERROR: WaitForDebugEvent returned 0, indicating failure. "
"GetLastError() returned %d.\n",GetLastError());
}
-
+
/* We're waiting for the helper process to send this exception.
When it does, we call WriteProcess. If it gets called more than
once, it is ignored.
*/
-
+
if(DebugEv.u.Exception.ExceptionRecord.ExceptionCode == MY_EXCEPTION)
{
Memory = (LPVOID)
DebugEv.u.Exception.ExceptionRecord.ExceptionInformation[0];
-
+
/* Write to this memory which we have no access to. */
- ret = WriteProcessMemory(pi.hProcess,
+ ret = WriteProcessMemory(pi.hProcess,
Memory,
DataBuffer,
4096,
&Count);
-
+
if(ret != 0)
{
Fail("ERROR: WriteProcessMemory should have failed, as "
"it attempted to write to a range of memory which was "
"not accessible.\n");
}
-
+
if(GetLastError() != ERROR_NOACCESS)
{
Fail("ERROR: GetLastError() should have returned "
- "ERROR_NOACCESS , but intead it returned "
+ "ERROR_NOACCESS , but instead it returned "
"%d.\n",GetLastError());
}
}
-
+
if(DebugEv.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
{
break;
}
-
- if(ContinueDebugEvent(DebugEv.dwProcessId,
+
+ if(ContinueDebugEvent(DebugEv.dwProcessId,
DebugEv.dwThreadId, dwContinueStatus) == 0)
{
Fail("ERROR: ContinueDebugEvent failed to continue the thread "
"which had a debug event. GetLastError() returned %d.\n",
GetLastError());
- }
+ }
}
diff --git a/src/coreclr/pal/tests/palsuite/file_io/CreateFileA/test1/CreateFileA.cpp b/src/coreclr/pal/tests/palsuite/file_io/CreateFileA/test1/CreateFileA.cpp
index 06af62f9f3a29d..6723f19db31621 100644
--- a/src/coreclr/pal/tests/palsuite/file_io/CreateFileA/test1/CreateFileA.cpp
+++ b/src/coreclr/pal/tests/palsuite/file_io/CreateFileA/test1/CreateFileA.cpp
@@ -20,7 +20,7 @@ BOOL Cleanup_CreateFileA_test1(void)
// loop through all accesses, modes, dispositions and flags
for (i=0; i<4*8*4*5; ++i) {
- sprintf_s(FileName, _countof(FileName), "test%03d.txt", i);
+ sprintf_s(FileName, ARRAY_SIZE(FileName), "test%03d.txt", i);
if (DeleteFileA(FileName) == FALSE) {
if (GetLastError() != ERROR_FILE_NOT_FOUND) {
bRet = FALSE;
@@ -97,7 +97,7 @@ PALTEST(file_io_CreateFileA_test1_paltest_createfilea_test1, "file_io/CreateFile
// creation disp loop
for (l = 0; l < 5; l++)
{
- sprintf_s(lpFileName, _countof(lpFileName), "test%03d.txt", nCounter);
+ sprintf_s(lpFileName, ARRAY_SIZE(lpFileName), "test%03d.txt", nCounter);
hFile = CreateFile(lpFileName,
dwDesiredAccess[i],
dwShareMode[j],
diff --git a/src/coreclr/pal/tests/palsuite/file_io/CreateFileW/test1/CreateFileW.cpp b/src/coreclr/pal/tests/palsuite/file_io/CreateFileW/test1/CreateFileW.cpp
index 38d7f5fc9c5664..f256a84c56c8a0 100644
--- a/src/coreclr/pal/tests/palsuite/file_io/CreateFileW/test1/CreateFileW.cpp
+++ b/src/coreclr/pal/tests/palsuite/file_io/CreateFileW/test1/CreateFileW.cpp
@@ -20,7 +20,7 @@ BOOL Cleanup_CreateFileW_test1(void)
// loop through all accesses, modes, dispositions and flags
for (i=0; i<4*8*4*5; ++i) {
- sprintf_s(FileName, _countof(FileName), "test%03d.txt", i);
+ sprintf_s(FileName, ARRAY_SIZE(FileName), "test%03d.txt", i);
if (DeleteFileA(FileName) == FALSE) {
if (GetLastError() != ERROR_FILE_NOT_FOUND) {
bRet = FALSE;
@@ -98,7 +98,7 @@ PALTEST(file_io_CreateFileW_test1_paltest_createfilew_test1, "file_io/CreateFile
// creation disp loop
for (l = 0; l < 5; l++)
{
- sprintf_s(string, _countof(string), "test%03d.txt", nCounter);
+ sprintf_s(string, ARRAY_SIZE(string), "test%03d.txt", nCounter);
lpFileName = convert(string);
hFile = CreateFileW(lpFileName,
dwDesiredAccess[i],
diff --git a/src/coreclr/pal/tests/palsuite/file_io/GetTempFileNameA/test1/GetTempFileNameA.cpp b/src/coreclr/pal/tests/palsuite/file_io/GetTempFileNameA/test1/GetTempFileNameA.cpp
index e93d2d9840cbd2..96d45cd90ccfab 100644
--- a/src/coreclr/pal/tests/palsuite/file_io/GetTempFileNameA/test1/GetTempFileNameA.cpp
+++ b/src/coreclr/pal/tests/palsuite/file_io/GetTempFileNameA/test1/GetTempFileNameA.cpp
@@ -46,7 +46,7 @@ PALTEST(file_io_GetTempFileNameA_test1_paltest_gettempfilenamea_test1, "file_io/
if (GetFileAttributesA(szReturnedName) == -1)
{
Fail("GetTempFileNameA: ERROR -> GetFileAttributes failed on the "
- "returned temp file \"%s\" with error code: %ld.\n",
+ "returned temp file \"%s\" with error code: %ld.\n",
szReturnedName,
GetLastError());
}
@@ -71,14 +71,14 @@ PALTEST(file_io_GetTempFileNameA_test1_paltest_gettempfilenamea_test1, "file_io/
if (GetFileAttributesA(szReturnedName) == -1)
{
Fail("GetTempFileNameA: ERROR -> GetFileAttributes failed on the "
- "returned temp file \"%s\" with error code: %ld.\n",
+ "returned temp file \"%s\" with error code: %ld.\n",
szReturnedName,
GetLastError());
}
if (DeleteFileA(szReturnedName) != TRUE)
{
Fail("GetTempFileNameA: ERROR -> DeleteFileW failed to delete"
- "the created temp \"%s\" file with error code: %ld.\n",
+ "the created temp \"%s\" file with error code: %ld.\n",
szReturnedName,
GetLastError());
}
@@ -97,13 +97,13 @@ PALTEST(file_io_GetTempFileNameA_test1_paltest_gettempfilenamea_test1, "file_io/
if (GetFileAttributesA(szReturnedName) == -1)
{
Fail("GetTempFileNameA: ERROR -> GetFileAttributes failed on the "
- "returned temp file \"%s\" with error code: %ld.\n",
+ "returned temp file \"%s\" with error code: %ld.\n",
szReturnedName,
GetLastError());
}
/* now verify that it only used the first 3 characters of the prefix */
- sprintf_s(szTempString, _countof(szTempString), "%s\\%s", szDot, szLongValidPrefix);
+ sprintf_s(szTempString, ARRAY_SIZE(szTempString), "%s\\%s", szDot, szLongValidPrefix);
if (strncmp(szTempString, szReturnedName, 6) == 0)
{
Fail("GetTempFileNameA: ERROR -> It appears that an improper prefix "
@@ -113,7 +113,7 @@ PALTEST(file_io_GetTempFileNameA_test1_paltest_gettempfilenamea_test1, "file_io/
if (DeleteFileA(szReturnedName) != TRUE)
{
Fail("GetTempFileNameA: ERROR -> DeleteFileW failed to delete"
- "the created temp file \"%s\" with error code: %ld.\n",
+ "the created temp file \"%s\" with error code: %ld.\n",
szReturnedName,
GetLastError());
}
diff --git a/src/coreclr/pal/tests/palsuite/file_io/GetTempFileNameW/test1/GetTempFileNameW.cpp b/src/coreclr/pal/tests/palsuite/file_io/GetTempFileNameW/test1/GetTempFileNameW.cpp
index 0d6371cd859740..5653609154ecc6 100644
--- a/src/coreclr/pal/tests/palsuite/file_io/GetTempFileNameW/test1/GetTempFileNameW.cpp
+++ b/src/coreclr/pal/tests/palsuite/file_io/GetTempFileNameW/test1/GetTempFileNameW.cpp
@@ -103,7 +103,7 @@ PALTEST(file_io_GetTempFileNameW_test1_paltest_gettempfilenamew_test1, "file_io/
}
// now verify that it only used the first 3 characters of the prefix
- swprintf_s(wTempString, _countof(wTempString), convert("%s\\%s"), wPath, wPrefix);
+ swprintf_s(wTempString, ARRAY_SIZE(wTempString), convert("%s\\%s"), wPath, wPrefix);
if (memcmp(wTempString, wReturnedName, wcslen(wTempString)*sizeof(WCHAR)) == 0)
{
free (wPath);
diff --git a/src/coreclr/pal/tests/palsuite/file_io/SearchPathW/test1/SearchPathW.cpp b/src/coreclr/pal/tests/palsuite/file_io/SearchPathW/test1/SearchPathW.cpp
index aa9e3bee6baa9a..a14cd04115f975 100644
--- a/src/coreclr/pal/tests/palsuite/file_io/SearchPathW/test1/SearchPathW.cpp
+++ b/src/coreclr/pal/tests/palsuite/file_io/SearchPathW/test1/SearchPathW.cpp
@@ -133,7 +133,7 @@ PALTEST(file_io_SearchPathW_test1_paltest_searchpathw_test1, "file_io/SearchPath
}
memset(fileloc_SearchPathW_test1, 0, _MAX_PATH);
- sprintf_s(fileloc_SearchPathW_test1, _countof(fileloc_SearchPathW_test1), "%s%s", fullPath, szFileNameExistsWithExt);
+ sprintf_s(fileloc_SearchPathW_test1, ARRAY_SIZE(fileloc_SearchPathW_test1), "%s%s", fullPath, szFileNameExistsWithExt);
RemoveAll_SearchPathW_test1();
diff --git a/src/coreclr/pal/tests/palsuite/filemapping_memmgt/CreateFileMappingW/test2/CreateFileMappingW.cpp b/src/coreclr/pal/tests/palsuite/filemapping_memmgt/CreateFileMappingW/test2/CreateFileMappingW.cpp
index bacdc92365cddb..ff18c512c4b926 100644
--- a/src/coreclr/pal/tests/palsuite/filemapping_memmgt/CreateFileMappingW/test2/CreateFileMappingW.cpp
+++ b/src/coreclr/pal/tests/palsuite/filemapping_memmgt/CreateFileMappingW/test2/CreateFileMappingW.cpp
@@ -33,9 +33,9 @@ PALTEST(filemapping_memmgt_CreateFileMappingW_test2_paltest_createfilemappingw_t
}
#if WIN32
- sprintf_s(executableFileName, _countof(executableFileName),"%s","executable.exe");
+ sprintf_s(executableFileName, ARRAY_SIZE(executableFileName),"%s","executable.exe");
#else
- sprintf_s(executableFileName, _countof(executableFileName),"%s","executable");
+ sprintf_s(executableFileName, ARRAY_SIZE(executableFileName),"%s","executable");
#endif
//conver string to a unicode one
@@ -53,7 +53,7 @@ PALTEST(filemapping_memmgt_CreateFileMappingW_test2_paltest_createfilemappingw_t
//free this memory
free(wpFileName);
-
+
if(INVALID_HANDLE_VALUE == FileHandle)
{
Fail("Failed to call CreateFile to create a file\n");
@@ -70,7 +70,7 @@ PALTEST(filemapping_memmgt_CreateFileMappingW_test2_paltest_createfilemappingw_t
NULL); //unnamed object
- if(NULL == FileMappingHandle)
+ if(NULL == FileMappingHandle)
{
Trace("\nFailed to call CreateFileMapping to create a mapping object!\n");
err = CloseHandle(FileHandle);
diff --git a/src/coreclr/pal/tests/palsuite/loader/LoadLibraryA/test5/loadlibrarya.cpp b/src/coreclr/pal/tests/palsuite/loader/LoadLibraryA/test5/loadlibrarya.cpp
index 690f1ba071c9ac..beceabf82fdb69 100644
--- a/src/coreclr/pal/tests/palsuite/loader/LoadLibraryA/test5/loadlibrarya.cpp
+++ b/src/coreclr/pal/tests/palsuite/loader/LoadLibraryA/test5/loadlibrarya.cpp
@@ -6,7 +6,7 @@
** Source: loadlibrarya.c
**
** Purpose: Negative test the LoadLibraryA API.
-** Call LoadLibraryA by passing a module name
+** Call LoadLibraryA by passing a module name
** without extension but with a trailing dot.
**
**
@@ -30,13 +30,13 @@ PALTEST(loader_LoadLibraryA_test5_paltest_loadlibrarya_test5, "loader/LoadLibrar
/*Module name without extension but with a trailing dot*/
#if WIN32
- sprintf_s(ModuleName, _countof(ModuleName), "%s", "rotor_pal.");
+ sprintf_s(ModuleName, ARRAY_SIZE(ModuleName), "%s", "rotor_pal.");
#else
/* Under FreeBSD */
- sprintf_s(ModuleName, _countof(ModuleName), "%s", "librotor_pal.");
+ sprintf_s(ModuleName, ARRAY_SIZE(ModuleName), "%s", "librotor_pal.");
#endif
- /* load a module which does not have the file extension,
+ /* load a module which does not have the file extension,
* but has a trailing dot
*/
ModuleHandle = LoadLibraryA(ModuleName);
diff --git a/src/coreclr/pal/tests/palsuite/loader/LoadLibraryW/test5/loadlibraryw.cpp b/src/coreclr/pal/tests/palsuite/loader/LoadLibraryW/test5/loadlibraryw.cpp
index 33b46fb0a4064c..e6c97ad555df7f 100644
--- a/src/coreclr/pal/tests/palsuite/loader/LoadLibraryW/test5/loadlibraryw.cpp
+++ b/src/coreclr/pal/tests/palsuite/loader/LoadLibraryW/test5/loadlibraryw.cpp
@@ -6,7 +6,7 @@
** Source: loadlibraryw.c
**
** Purpose: Negative test the LoadLibraryW API.
-** Call LoadLibraryW by passing a module name
+** Call LoadLibraryW by passing a module name
** without extension but with a trailing dot.
**
**
@@ -32,9 +32,9 @@ PALTEST(loader_LoadLibraryW_test5_paltest_loadlibraryw_test5, "loader/LoadLibrar
/*Module name without extension but with a trailing dot*/
#if WIN32
- sprintf_s(ModuleName, _countof(ModuleName),"%s","rotor_pal.");
+ sprintf_s(ModuleName, ARRAY_SIZE(ModuleName),"%s","rotor_pal.");
#else
- sprintf_s(ModuleName, _countof(ModuleName),"%s","librotor_pal.");
+ sprintf_s(ModuleName, ARRAY_SIZE(ModuleName),"%s","librotor_pal.");
#endif
/* convert a normal string to a wide one */
@@ -52,7 +52,7 @@ PALTEST(loader_LoadLibraryW_test5_paltest_loadlibraryw_test5, "loader/LoadLibrar
"call LoadLibraryW with module name which does not have "
"extension except a trailing dot, a NULL module handle is"
"expected, but no NULL module handle is returned, "
- "error code = %u\n", GetLastError());
+ "error code = %u\n", GetLastError());
/* decrement the reference count of the loaded dll */
err = FreeLibrary(ModuleHandle);
diff --git a/src/coreclr/pal/tests/palsuite/locale_info/GetLocaleInfoW/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/locale_info/GetLocaleInfoW/test1/test1.cpp
index 13f8176f3e6469..ea9cbb331c101b 100644
--- a/src/coreclr/pal/tests/palsuite/locale_info/GetLocaleInfoW/test1/test1.cpp
+++ b/src/coreclr/pal/tests/palsuite/locale_info/GetLocaleInfoW/test1/test1.cpp
@@ -5,7 +5,7 @@
**
** Source: test1.c
**
-** Purpose: Tests that GetLocaleInfoW gives the correction information for
+** Purpose: Tests that GetLocaleInfoW gives the correction information for
** LOCALE_NEUTRAL.
**
**
@@ -14,15 +14,13 @@
#include
-int Types[] = { LOCALE_SDECIMAL, LOCALE_STHOUSAND, LOCALE_ILZERO,
+int Types[] = { LOCALE_SDECIMAL, LOCALE_STHOUSAND, LOCALE_ILZERO,
LOCALE_SCURRENCY, LOCALE_SMONDECIMALSEP, LOCALE_SMONTHOUSANDSEP };
char *TypeStrings[] = { "LOCALE_SDECIMAL", "LOCALE_STHOUSAND", "LOCALE_ILZERO",
"LOCALE_SCURRENCY", "LOCALE_SMONDECIMALSEP", "LOCALE_SMONTHOUSANDSEP" };
-#define NUM_TYPES (sizeof(Types) / sizeof(Types[0]))
-
-typedef WCHAR InfoStrings[NUM_TYPES][4];
+typedef WCHAR InfoStrings[ARRAY_SIZE(Types)][4];
typedef struct
{
@@ -32,7 +30,7 @@ typedef struct
LocalInfoType Locales[] =
{
- {LOCALE_NEUTRAL,
+ {LOCALE_NEUTRAL,
{{'.',0}, {',',0}, {'1',0}, {'$',0}, {'.',0}, {',',0}}},
};
@@ -40,7 +38,7 @@ int NumLocales = sizeof(Locales) / sizeof(Locales[0]);
PALTEST(locale_info_GetLocaleInfoW_test1_paltest_getlocaleinfow_test1, "locale_info/GetLocaleInfoW/test1/paltest_getlocaleinfow_test1")
-{
+{
WCHAR buffer[256] = { 0 };
int ret;
int i,j;
@@ -52,10 +50,10 @@ PALTEST(locale_info_GetLocaleInfoW_test1_paltest_getlocaleinfow_test1, "locale_i
for (i=0; i/dev/null
-
+
TEST_XUNIT_NAME=$(dirname $TEST_NAME)
TEST_XUNIT_CLASSNAME=$(dirname $TEST_XUNIT_NAME)
TEST_XUNIT_NAME=${TEST_XUNIT_NAME#*/}
TEST_XUNIT_NAME=${TEST_XUNIT_NAME#*/}
-
+
TEST_XUNIT_NAME=$(echo $TEST_XUNIT_NAME | tr / .)
TEST_XUNIT_CLASSNAME=$(echo $TEST_XUNIT_CLASSNAME | tr / .)
-
+
echo -n "> $PAL_XUNIT_TEST_LIST_TMP
# If the exit code is 0 then the test passed, otherwise record a failure.
@@ -175,10 +175,10 @@ do
echo "Pass\" />" >> $PAL_XUNIT_TEST_LIST_TMP
else
echo "Fail\" >" >> $PAL_XUNIT_TEST_LIST_TMP
- echo "" >> $PAL_XUNIT_TEST_LIST_TMP
- echo "" >> $PAL_XUNIT_TEST_LIST_TMP
- echo "" >> $PAL_XUNIT_TEST_LIST_TMP
- echo "" >> $PAL_XUNIT_TEST_LIST_TMP
+ echo "" >> $PAL_XUNIT_TEST_LIST_TMP
+ echo "" >> $PAL_XUNIT_TEST_LIST_TMP
+ echo "" >> $PAL_XUNIT_TEST_LIST_TMP
+ echo "" >> $PAL_XUNIT_TEST_LIST_TMP
echo "" >> $PAL_XUNIT_TEST_LIST_TMP
FAILED_TEST="$TEST_NAME. Exit code: $TEST_EXIT_CODE"
echo
diff --git a/src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test2/parentprocess.cpp b/src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test2/parentprocess.cpp
index 5d69ee224d51ec..19bfc74ba6afde 100644
--- a/src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test2/parentprocess.cpp
+++ b/src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test2/parentprocess.cpp
@@ -6,7 +6,7 @@
** Source: createprocessw/test2/parentprocess.c
**
** Purpose: Test the following features of CreateProcessW:
-** - Check to see if hProcess & hThread are set in
+** - Check to see if hProcess & hThread are set in
** return PROCESS_INFORMATION structure
** - Check to see if stdin, stdout, & stderr handles
** are used when STARTF_USESTDHANDLES is specified
@@ -19,7 +19,7 @@
** WaitForSingleObject
** WriteFile, ReadFile
** GetExitCodeProcess
-**
+**
**
**=========================================================*/
@@ -68,11 +68,11 @@ PALTEST(threading_CreateProcessW_test2_paltest_createprocessw_test2, "threading/
}
/*Setup SECURITY_ATTRIBUTES structure for CreatePipe*/
- pipeAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
- pipeAttributes.lpSecurityDescriptor = NULL;
- pipeAttributes.bInheritHandle = TRUE;
-
-
+ pipeAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
+ pipeAttributes.lpSecurityDescriptor = NULL;
+ pipeAttributes.bInheritHandle = TRUE;
+
+
/*Create a StdIn pipe for child*/
bRetVal = CreatePipe(&hTestStdInR, /* read handle*/
&hTestStdInW, /* write handle */
@@ -138,7 +138,7 @@ PALTEST(threading_CreateProcessW_test2_paltest_createprocessw_test2, "threading/
/* Launch the child */
if ( !CreateProcess (NULL, szFullPathNameW, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi ))
{
- Fail("ERROR: CreateProcess call failed. GetLastError returned %d\n",
+ Fail("ERROR: CreateProcess call failed. GetLastError returned %d\n",
GetLastError() );
}
@@ -157,7 +157,7 @@ PALTEST(threading_CreateProcessW_test2_paltest_createprocessw_test2, "threading/
Fail("ERROR: %ld :unable to write to write pipe handle "
"hTestStdInW=0x%lx\n", GetLastError(), hTestStdInW);
}
-
+
/* Wait for the child to finish, Max 20 seconds */
dwExitCode = WaitForSingleObject(pi.hProcess, 20000);
@@ -222,7 +222,7 @@ PALTEST(threading_CreateProcessW_test2_paltest_createprocessw_test2, "threading/
NULL); /* overlapped buffer*/
- /* Confirm that we recieved the same string that we originally */
+ /* Confirm that we received the same string that we originally */
/* wrote to the child and was received on both stdout & stderr.*/
if (strncmp(szTestString, szStdOutBuf, strlen(szTestString)) != 0
|| strncmp(szTestString, szStdErrBuf, strlen(szTestString)) != 0)
diff --git a/src/coreclr/pal/tests/palsuite/threading/NamedMutex/test1/namedmutex.cpp b/src/coreclr/pal/tests/palsuite/threading/NamedMutex/test1/namedmutex.cpp
index 7956a955455c7d..b3a73807ed77af 100644
--- a/src/coreclr/pal/tests/palsuite/threading/NamedMutex/test1/namedmutex.cpp
+++ b/src/coreclr/pal/tests/palsuite/threading/NamedMutex/test1/namedmutex.cpp
@@ -7,10 +7,6 @@
#include
-#ifndef _countof
-#define _countof(a) (sizeof(a) / sizeof(a[0]))
-#endif // !_countof
-
const char *const SessionPrefix = "Local\\";
const char *const GlobalPrefix = "Global\\";
@@ -207,7 +203,7 @@ bool StartProcess(const char *funcName)
if (g_isStress)
{
test_strcpy(&g_processCommandLinePath[processCommandLinePathLength], " stress");
- processCommandLinePathLength += _countof("stress") - 1;
+ processCommandLinePathLength += STRING_LENGTH("stress");
}
STARTUPINFO si;
@@ -416,8 +412,8 @@ bool NameTests()
// Name too long. The maximum allowed length depends on the file system, so we're not checking for that.
{
char name[257];
- memset(name, 'a', _countof(name) - 1);
- name[_countof(name) - 1] = '\0';
+ memset(name, 'a', STRING_LENGTH(name));
+ name[STRING_LENGTH(name)] = '\0';
TestCreateMutex(m, name);
TestAssert(m == nullptr);
TestAssert(GetLastError() == ERROR_FILENAME_EXCED_RANGE);
@@ -579,7 +575,7 @@ bool MutualExclusionTests()
HANDLE waitHandles[] = {m2.GetHandle(), m.GetHandle()};
TestAssert(
WaitForMultipleObjects(
- _countof(waitHandles),
+ ARRAY_SIZE(waitHandles),
waitHandles,
false /* waitAll */,
FailTimeoutMilliseconds) ==
@@ -587,7 +583,7 @@ bool MutualExclusionTests()
TestAssert(GetLastError() == ERROR_NOT_SUPPORTED);
TestAssert(
WaitForMultipleObjects(
- _countof(waitHandles),
+ ARRAY_SIZE(waitHandles),
waitHandles,
true /* waitAll */,
FailTimeoutMilliseconds) ==
@@ -1114,7 +1110,7 @@ bool (*const TestList[])() =
bool RunTests()
{
bool allPassed = true;
- for (SIZE_T i = 0; i < _countof(TestList); ++i)
+ for (SIZE_T i = 0; i < ARRAY_SIZE(TestList); ++i)
{
if (!TestList[i]())
{
@@ -1125,7 +1121,7 @@ bool RunTests()
}
DWORD g_stressDurationMilliseconds = 0;
-LONG g_stressTestCounts[_countof(TestList)] = {0};
+LONG g_stressTestCounts[ARRAY_SIZE(TestList)] = {0};
LONG g_stressResult = true;
DWORD PALAPI StressTest(void *arg)
@@ -1154,8 +1150,8 @@ bool StressTests(DWORD durationMinutes)
g_stressDurationMilliseconds = durationMinutes * (60 * 1000);
// Start a thread for each test
- HANDLE threadHandles[_countof(TestList)];
- for (SIZE_T i = 0; i < _countof(threadHandles); ++i)
+ HANDLE threadHandles[ARRAY_SIZE(TestList)];
+ for (SIZE_T i = 0; i < ARRAY_SIZE(threadHandles); ++i)
{
TestAssert(StartThread(StressTest, reinterpret_cast(i), &threadHandles[i]));
}
@@ -1163,7 +1159,7 @@ bool StressTests(DWORD durationMinutes)
while (true)
{
DWORD waitResult =
- WaitForMultipleObjects(_countof(threadHandles), threadHandles, true /* bWaitAll */, 10 * 1000 /* dwMilliseconds */);
+ WaitForMultipleObjects(ARRAY_SIZE(threadHandles), threadHandles, true /* bWaitAll */, 10 * 1000 /* dwMilliseconds */);
TestAssert(waitResult == WAIT_OBJECT_0 || waitResult == WAIT_TIMEOUT);
if (waitResult == WAIT_OBJECT_0)
{
@@ -1171,7 +1167,7 @@ bool StressTests(DWORD durationMinutes)
}
Trace("'paltest_namedmutex_test1' stress test counts: ");
- for (SIZE_T i = 0; i < _countof(g_stressTestCounts); ++i)
+ for (SIZE_T i = 0; i < ARRAY_SIZE(g_stressTestCounts); ++i)
{
if (i != 0)
{
@@ -1183,7 +1179,7 @@ bool StressTests(DWORD durationMinutes)
fflush(stdout);
}
- for (SIZE_T i = 0; i < _countof(threadHandles); ++i)
+ for (SIZE_T i = 0; i < ARRAY_SIZE(threadHandles); ++i)
{
CloseHandle(threadHandles[i]);
}
diff --git a/src/coreclr/pal/tests/palsuite/threading/NamedMutex/test1/nopal.cpp b/src/coreclr/pal/tests/palsuite/threading/NamedMutex/test1/nopal.cpp
index 3c18e67d021c8e..d226ff5cf8d82d 100644
--- a/src/coreclr/pal/tests/palsuite/threading/NamedMutex/test1/nopal.cpp
+++ b/src/coreclr/pal/tests/palsuite/threading/NamedMutex/test1/nopal.cpp
@@ -12,8 +12,7 @@
#include
#include
#include
-
-#define _countof(a) (sizeof(a) / sizeof(a[0]))
+#include
#undef PAGE_SIZE
#define PAGE_SIZE (4096)
@@ -58,7 +57,7 @@ bool WriteHeaderInfo(const char *path, char sharedMemoryType, char version, int
// See SharedMemorySharedDataHeader for format
char buffer[] = {sharedMemoryType, version};
- if (write(fd, buffer, _countof(buffer)) != _countof(buffer))
+ if (write(fd, buffer, ARRAY_SIZE(buffer)) != ARRAY_SIZE(buffer))
return false;
return flock(fd, LOCK_SH | LOCK_NB) == 0;
diff --git a/src/coreclr/pal/tools/smarty.sh b/src/coreclr/pal/tools/smarty.sh
index d8b407e72c619c..6ab63d94939261 100755
--- a/src/coreclr/pal/tools/smarty.sh
+++ b/src/coreclr/pal/tools/smarty.sh
@@ -15,8 +15,8 @@ then
export BVT_ROOT=$PWD
fi
-if [ -n "$PERL5LIB" ] ; then
- if [ -z "`expr $PERL5LIB : ".*\($BVT_ROOT/Common/Smarty\)"`" ] ; then
+if [ -n "$PERL5LIB" ]; then
+ if [ -z "`expr $PERL5LIB : ".*\($BVT_ROOT/Common/Smarty\)"`" ]; then
export PERL5LIB="$PERL5LIB:$BVT_ROOT/Common/Smarty"
fi
else
diff --git a/src/coreclr/palrt/common.h b/src/coreclr/palrt/common.h
index 684c134abede95..9946ed67e8d967 100644
--- a/src/coreclr/palrt/common.h
+++ b/src/coreclr/palrt/common.h
@@ -14,4 +14,5 @@
#include
#include
#include "shlwapip.h"
+#include
#endif // _COMMON_H_
diff --git a/src/coreclr/palrt/path.cpp b/src/coreclr/palrt/path.cpp
index 7e0d2279f4a9fe..51eb1514769e70 100644
--- a/src/coreclr/palrt/path.cpp
+++ b/src/coreclr/palrt/path.cpp
@@ -430,15 +430,15 @@ STDAPI_(LPWSTR) PathCombineW(LPWSTR lpszDest, LPCWSTR lpszDir, LPCWSTR lpszFile)
if (!lpszFile || *lpszFile==W('\0'))
{
// lpszFile is empty
- StringCchCopyNW(szTemp, ARRAYSIZE(szTemp), lpszDir, ARRAYSIZE(szTemp));
+ StringCchCopyNW(szTemp, ARRAY_SIZE(szTemp), lpszDir, ARRAY_SIZE(szTemp));
}
else if (PathIsRelativeW(lpszFile))
{
- StringCchCopyNW(szTemp, ARRAYSIZE(szTemp), lpszDir, ARRAYSIZE(szTemp));
+ StringCchCopyNW(szTemp, ARRAY_SIZE(szTemp), lpszDir, ARRAY_SIZE(szTemp));
pszT = PathAddBackslashW(szTemp);
if (pszT)
{
- size_t iRemaining = ARRAYSIZE(szTemp) - (pszT - szTemp);
+ size_t iRemaining = ARRAY_SIZE(szTemp) - (pszT - szTemp);
if (wcslen(lpszFile) < iRemaining)
{
@@ -456,7 +456,7 @@ STDAPI_(LPWSTR) PathCombineW(LPWSTR lpszDest, LPCWSTR lpszDir, LPCWSTR lpszFile)
}
else if (IsPathSeparator(*lpszFile) && !PathIsUNCW(lpszFile))
{
- StringCchCopyNW(szTemp, ARRAYSIZE(szTemp), lpszDir, ARRAYSIZE(szTemp));
+ StringCchCopyNW(szTemp, ARRAY_SIZE(szTemp), lpszDir, ARRAY_SIZE(szTemp));
// FEATURE: Note that we do not check that an actual root is returned;
// it is assumed that we are given valid parameters
PathStripToRootW(szTemp);
@@ -467,7 +467,7 @@ STDAPI_(LPWSTR) PathCombineW(LPWSTR lpszDest, LPCWSTR lpszDir, LPCWSTR lpszFile)
// Skip the backslash when copying
// Note: We don't support strings longer than 4GB, but that's
// okay because we already fail at MAX_PATH
- int iRemaining = (int)(ARRAYSIZE(szTemp) - (pszT - szTemp));
+ int iRemaining = (int)(ARRAY_SIZE(szTemp) - (pszT - szTemp));
StringCchCopyNW(pszT, iRemaining, lpszFile+1, iRemaining);
}
else
@@ -478,13 +478,13 @@ STDAPI_(LPWSTR) PathCombineW(LPWSTR lpszDest, LPCWSTR lpszDir, LPCWSTR lpszFile)
else
{
// already fully qualified file part
- StringCchCopyNW(szTemp, ARRAYSIZE(szTemp), lpszFile, ARRAYSIZE(szTemp));
+ StringCchCopyNW(szTemp, ARRAY_SIZE(szTemp), lpszFile, ARRAY_SIZE(szTemp));
}
}
else if (lpszFile && *lpszFile)
{
// no dir just use file.
- StringCchCopyNW(szTemp, ARRAYSIZE(szTemp), lpszFile, ARRAYSIZE(szTemp));
+ StringCchCopyNW(szTemp, ARRAY_SIZE(szTemp), lpszFile, ARRAY_SIZE(szTemp));
}
//
diff --git a/src/coreclr/palrt/shlwapip.h b/src/coreclr/palrt/shlwapip.h
index a34c923c346b51..490d73f36a9d95 100644
--- a/src/coreclr/palrt/shlwapip.h
+++ b/src/coreclr/palrt/shlwapip.h
@@ -12,9 +12,6 @@
#ifndef SHLWAPIP_H_INCLUDED
#define SHLWAPIP_H_INCLUDED
-#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
-#define SIZECHARS(sz) (sizeof(sz)/sizeof(sz[0]))
-
#define SIZEOF(x) sizeof(x)
#define PRIVATE
#define PUBLIC
diff --git a/src/coreclr/scripts/antigen_run.py b/src/coreclr/scripts/antigen_run.py
index 96146005906d9a..240e1353f75b83 100644
--- a/src/coreclr/scripts/antigen_run.py
+++ b/src/coreclr/scripts/antigen_run.py
@@ -20,7 +20,7 @@
from os.path import getsize
import os
from coreclr_arguments import *
-from azdo_pipelines_util import run_command, TempDir
+from jitutil import run_command, TempDir
parser = argparse.ArgumentParser(description="description")
diff --git a/src/coreclr/scripts/azdo_pipelines_util.py b/src/coreclr/scripts/azdo_pipelines_util.py
deleted file mode 100644
index 83f1d083ee6ad0..00000000000000
--- a/src/coreclr/scripts/azdo_pipelines_util.py
+++ /dev/null
@@ -1,178 +0,0 @@
-#!/usr/bin/env python3
-#
-# Licensed to the .NET Foundation under one or more agreements.
-# The .NET Foundation licenses this file to you under the MIT license.
-#
-# Title : azdo_pipelines_util.py
-#
-# Notes:
-#
-# Utility functions used by Python scripts involved with Azure DevOps Pipelines
-# setup.
-#
-################################################################################
-################################################################################
-
-import os
-import shutil
-import subprocess
-import sys
-import tempfile
-
-
-def run_command(command_to_run, _cwd=None, _exit_on_fail=False, _output_file=None):
- """ Runs the command.
-
- Args:
- command_to_run ([string]): Command to run along with arguments.
- _cwd (string): Current working directory.
- _exit_on_fail (bool): If it should exit on failure.
- Returns:
- (string, string, int): Returns a tuple of stdout, stderr, and command return code if _output_file= None
- Otherwise stdout, stderr are empty.
- """
- print("Running: " + " ".join(command_to_run))
- command_stdout = ""
- command_stderr = ""
- return_code = 1
-
- output_type = subprocess.STDOUT if _output_file else subprocess.PIPE
- with subprocess.Popen(command_to_run, stdout=subprocess.PIPE, stderr=output_type, cwd=_cwd) as proc:
-
- # For long running command, continuously print the output
- if _output_file:
- while True:
- with open(_output_file, 'a') as of:
- output = proc.stdout.readline()
- if proc.poll() is not None:
- break
- if output:
- output_str = output.strip().decode("utf-8")
- print(output_str)
- of.write(output_str + "\n")
- else:
- command_stdout, command_stderr = proc.communicate()
- if len(command_stdout) > 0:
- print(command_stdout.decode("utf-8"))
- if len(command_stderr) > 0:
- print(command_stderr.decode("utf-8"))
-
- return_code = proc.returncode
- if _exit_on_fail and return_code != 0:
- print("Command failed. Exiting.")
- sys.exit(1)
- return command_stdout, command_stderr, return_code
-
-
-def copy_directory(src_path, dst_path, verbose_output=True, match_func=lambda path: True):
- """Copies directory in 'src_path' to 'dst_path' maintaining the directory
- structure. https://docs.python.org/3.5/library/shutil.html#shutil.copytree can't
- be used in this case because it expects the destination directory should not
- exist, however we do call copy_directory() to copy files to same destination directory.
-
- Args:
- src_path (string): Path of source directory that need to be copied.
- dst_path (string): Path where directory should be copied.
- verbose_output (bool): True to print every copy or skipped file.
- match_func (str -> bool) : Criteria function determining if a file is copied.
- """
- if not os.path.exists(dst_path):
- os.makedirs(dst_path)
- for item in os.listdir(src_path):
- src_item = os.path.join(src_path, item)
- dst_item = os.path.join(dst_path, item)
- if os.path.isdir(src_item):
- copy_directory(src_item, dst_item, verbose_output, match_func)
- else:
- try:
- if match_func(src_item):
- if verbose_output:
- print("> copy {0} => {1}".format(src_item, dst_item))
- try:
- shutil.copy2(src_item, dst_item)
- except PermissionError as pe_error:
- print('Ignoring PermissionError: {0}'.format(pe_error))
- else:
- if verbose_output:
- print("> skipping {0}".format(src_item))
- except UnicodeEncodeError:
- if verbose_output:
- print("> Got UnicodeEncodeError")
-
-
-def copy_files(src_path, dst_path, file_names):
- """Copy files from 'file_names' list from 'src_path' to 'dst_path'.
- It retains the original directory structure of src_path.
-
- Args:
- src_path (string): Source directory from where files are copied.
- dst_path (string): Destination directory where files to be copied.
- file_names ([string]): List of full path file names to be copied.
- """
-
- print('### Copying below files from {0} to {1}:'.format(src_path, dst_path))
- print('')
- print(os.linesep.join(file_names))
- for f in file_names:
- # Create same structure in dst so we don't clobber same files names present in different directories
- dst_path_of_file = f.replace(src_path, dst_path)
-
- dst_directory = os.path.dirname(dst_path_of_file)
- if not os.path.exists(dst_directory):
- os.makedirs(dst_directory)
- try:
- shutil.copy2(f, dst_path_of_file)
- except PermissionError as pe_error:
- print('Ignoring PermissionError: {0}'.format(pe_error))
-
-
-def set_pipeline_variable(name, value):
- """ This method sets pipeline variable.
-
- Args:
- name (string): Name of the variable.
- value (string): Value of the variable.
- """
- define_variable_format = "##vso[task.setvariable variable={0}]{1}"
- print("{0} -> {1}".format(name, value)) # logging
- print(define_variable_format.format(name, value)) # set variable
-
-
-class TempDir:
- """ Class to create a temporary working directory, or use one that is passed as an argument.
-
- Use with: "with TempDir() as temp_dir" to change to that directory and then automatically
- change back to the original working directory afterwards and remove the temporary
- directory and its contents (if skip_cleanup is False).
- """
-
- def __init__(self, path=None, skip_cleanup=False):
- self.mydir = tempfile.mkdtemp() if path is None else path
- self.cwd = None
- self._skip_cleanup = skip_cleanup
-
- def __enter__(self):
- self.cwd = os.getcwd()
- os.chdir(self.mydir)
- return self.mydir
-
- def __exit__(self, exc_type, exc_val, exc_tb):
- os.chdir(self.cwd)
- if not self._skip_cleanup:
- shutil.rmtree(self.mydir)
-
-
-class ChangeDir:
- """ Class to temporarily change to a given directory. Use with "with".
- """
-
- def __init__(self, mydir):
- self.mydir = mydir
- self.cwd = None
-
- def __enter__(self):
- self.cwd = os.getcwd()
- os.chdir(self.mydir)
-
- def __exit__(self, exc_type, exc_val, exc_tb):
- os.chdir(self.cwd)
\ No newline at end of file
diff --git a/src/coreclr/scripts/fuzzer_setup.py b/src/coreclr/scripts/fuzzer_setup.py
index 54ce2f37734fc1..7bc60acc71c4ff 100644
--- a/src/coreclr/scripts/fuzzer_setup.py
+++ b/src/coreclr/scripts/fuzzer_setup.py
@@ -18,7 +18,7 @@
import os
from coreclr_arguments import *
from os import path
-from azdo_pipelines_util import run_command, copy_directory, set_pipeline_variable, ChangeDir, TempDir
+from jitutil import run_command, copy_directory, set_pipeline_variable, ChangeDir, TempDir
parser = argparse.ArgumentParser(description="description")
@@ -103,7 +103,7 @@ def main(main_args):
# create exploratory directory
print('Copying {} -> {}'.format(scripts_src_directory, coreroot_directory))
- copy_directory(scripts_src_directory, coreroot_directory, match_func=lambda path: any(path.endswith(extension) for extension in [".py"]))
+ copy_directory(scripts_src_directory, coreroot_directory, verbose_output=True, match_func=lambda path: any(path.endswith(extension) for extension in [".py"]))
if is_windows:
acceptable_copy = lambda path: any(path.endswith(extension) for extension in [".py", ".dll", ".exe", ".json"])
@@ -113,7 +113,7 @@ def main(main_args):
# copy CORE_ROOT
print('Copying {} -> {}'.format(coreclr_args.core_root_directory, coreroot_directory))
- copy_directory(coreclr_args.core_root_directory, coreroot_directory, match_func=acceptable_copy)
+ copy_directory(coreclr_args.core_root_directory, coreroot_directory, verbose_output=True, match_func=acceptable_copy)
try:
with TempDir() as tool_code_directory:
@@ -136,7 +136,7 @@ def main(main_args):
# copy tool
print('Copying {} -> {}'.format(publish_dir, dst_directory))
- copy_directory(publish_dir, dst_directory, match_func=acceptable_copy)
+ copy_directory(publish_dir, dst_directory, verbose_output=True, match_func=acceptable_copy)
except PermissionError as pe:
print("Skipping file. Got error: %s", pe)
diff --git a/src/coreclr/scripts/fuzzlyn_run.py b/src/coreclr/scripts/fuzzlyn_run.py
index 7d99b1f504fffb..fa37b6eb4f655c 100644
--- a/src/coreclr/scripts/fuzzlyn_run.py
+++ b/src/coreclr/scripts/fuzzlyn_run.py
@@ -20,7 +20,7 @@
import re
import shutil
import threading
-from azdo_pipelines_util import run_command, TempDir
+from jitutil import run_command, TempDir
from coreclr_arguments import *
from os import path
diff --git a/src/coreclr/scripts/genDummyProvider.py b/src/coreclr/scripts/genDummyProvider.py
index 908c09d174db15..b8786c47a7a4f1 100644
--- a/src/coreclr/scripts/genDummyProvider.py
+++ b/src/coreclr/scripts/genDummyProvider.py
@@ -6,7 +6,7 @@
## interface from a manifest file
##
## The intended use if for platforms which support event pipe
-## but do not have a an eventing platform to recieve report events
+## but do not have a an eventing platform to receive report events
import os
from genEventing import *
diff --git a/src/coreclr/scripts/jitrollingbuild.py b/src/coreclr/scripts/jitrollingbuild.py
index 0003805b49960d..100aff85c35226 100644
--- a/src/coreclr/scripts/jitrollingbuild.py
+++ b/src/coreclr/scripts/jitrollingbuild.py
@@ -32,10 +32,10 @@
################################################################################
az_account_name = "clrjit2"
-az_container_name = "jitrollingbuild"
+az_jitrollingbuild_container_name = "jitrollingbuild"
az_builds_root_folder = "builds"
az_blob_storage_account_uri = "https://" + az_account_name + ".blob.core.windows.net/"
-az_blob_storage_container_uri = az_blob_storage_account_uri + az_container_name
+az_blob_storage_jitrollingbuild_container_uri = az_blob_storage_account_uri + az_jitrollingbuild_container_name
################################################################################
# Argument Parser
@@ -50,19 +50,28 @@
"""
download_description = """\
-Download clrjit from SuperPMI Azure storage.
+Download clrjit from SuperPMI Azure storage. If -git_hash is given, download exactly
+that JIT. If -git_hash is not given, find the latest git hash from the main branch that
+corresponds to the current tree, and download that JIT. That is, find an appropriate
+"baseline" JIT for doing asm diffs.
"""
list_description = """\
List clrjit in SuperPMI Azure storage.
"""
-host_os_help = "OS (windows, OSX, Linux). Default: current OS."
-
arch_help = "Architecture (x64, x86, arm, arm64). Default: current architecture."
build_type_help = "Build type (Debug, Checked, Release). Default: Checked."
+host_os_help = "OS (windows, OSX, Linux). Default: current OS."
+
+spmi_location_help = """\
+Directory in which to put SuperPMI files, such as downloaded MCH files, asm diffs, and repro .MC files.
+Optional. Default is 'spmi' within the repo 'artifacts' directory.
+If 'SUPERPMI_CACHE_DIRECTORY' environment variable is set to a path, it will use that directory.
+"""
+
git_hash_help = "git hash"
target_dir_help = "Directory to put the downloaded JIT."
@@ -82,6 +91,7 @@
common_parser.add_argument("-arch", help=arch_help)
common_parser.add_argument("-build_type", default="Checked", help=build_type_help)
common_parser.add_argument("-host_os", help=host_os_help)
+common_parser.add_argument("-spmi_location", help=spmi_location_help)
# subparser for upload
upload_parser = subparsers.add_parser("upload", description=upload_description, parents=[common_parser])
@@ -93,8 +103,8 @@
# subparser for download
download_parser = subparsers.add_parser("download", description=download_description, parents=[common_parser])
-download_parser.add_argument("-git_hash", required=True, help=git_hash_help)
-download_parser.add_argument("-target_dir", required=True, help=target_dir_help)
+download_parser.add_argument("-git_hash", help=git_hash_help)
+download_parser.add_argument("-target_dir", help=target_dir_help)
download_parser.add_argument("--skip_cleanup", action="store_true", help=skip_cleanup_help)
# subparser for list
@@ -172,6 +182,111 @@ def determine_jit_name(coreclr_args):
raise RuntimeError("Unknown OS.")
+def process_git_hash_arg(coreclr_args):
+ """ Process the -git_hash argument.
+
+ If the argument is present, use that to download a JIT.
+ If not present, try to find and download a JIT based on the current environment:
+ 1. Determine the current directory git hash using:
+ git rev-parse HEAD
+ Call the result `current_git_hash`.
+ 2. Determine the baseline: where does this hash meet `main` using:
+ git merge-base `current_git_hash` main
+ Call the result `base_git_hash`.
+ 3. Figure out the latest hash, starting with `base_git_hash`, that contains any changes to
+ the src/coreclr/jit directory. (We do this because the JIT rolling build only includes
+ builds for changes to this directory. So, this logic needs to stay in sync with the logic
+ that determines what causes the JIT rolling build to run. E.g., it should also get
+ rebuilt if the JIT-EE interface GUID changes. Alternatively, we can take the entire list
+ of changes, and probe the rolling build drop for all of them.)
+ 4. Starting with `base_git_hash`, and possibly walking to older changes, look for matching builds
+ in the JIT rolling build drops.
+ 5. If a JIT directory in Azure Storage is found, set coreclr_args.git_hash to that git hash to use
+ for downloading.
+
+ Args:
+ coreclr_args (CoreclrArguments) : parsed args
+
+ Returns:
+ Nothing
+
+ coreclr_args.git_hash is set to the git hash to use
+
+ An exception is thrown if the `-git_hash` argument is unspecified, and we don't find an appropriate
+ JIT to download.
+ """
+
+ if coreclr_args.git_hash is not None:
+ return
+
+ # Do all the remaining commands, including a number of 'git' commands including relative paths,
+ # from the root of the runtime repo.
+
+ with ChangeDir(coreclr_args.runtime_repo_location):
+ command = [ "git", "rev-parse", "HEAD" ]
+ print("Invoking: {}".format(" ".join(command)))
+ proc = subprocess.Popen(command, stdout=subprocess.PIPE)
+ stdout_git_rev_parse, _ = proc.communicate()
+ return_code = proc.returncode
+ if return_code == 0:
+ current_git_hash = stdout_git_rev_parse.decode('utf-8').strip()
+ print("Current hash: {}".format(current_git_hash))
+ else:
+ raise RuntimeError("Couldn't determine current git hash")
+
+ # We've got the current hash; figure out the baseline hash.
+ command = [ "git", "merge-base", current_git_hash, "origin/main" ]
+ print("Invoking: {}".format(" ".join(command)))
+ proc = subprocess.Popen(command, stdout=subprocess.PIPE)
+ stdout_git_merge_base, _ = proc.communicate()
+ return_code = proc.returncode
+ if return_code == 0:
+ base_git_hash = stdout_git_merge_base.decode('utf-8').strip()
+ print("Baseline hash: {}".format(base_git_hash))
+ else:
+ raise RuntimeError("Couldn't determine baseline git hash")
+
+ # Enumerate the last 20 changes, starting with the baseline, that included JIT changes.
+ command = [ "git", "log", "--pretty=format:%H", base_git_hash, "-20", "--", "src/coreclr/jit/*" ]
+ print("Invoking: {}".format(" ".join(command)))
+ proc = subprocess.Popen(command, stdout=subprocess.PIPE)
+ stdout_change_list, _ = proc.communicate()
+ return_code = proc.returncode
+ change_list_hashes = []
+ if return_code == 0:
+ change_list_hashes = stdout_change_list.decode('utf-8').strip().splitlines()
+ else:
+ raise RuntimeError("Couldn't determine list of JIT changes starting with baseline hash")
+
+ if len(change_list_hashes) == 0:
+ raise RuntimeError("No JIT changes found starting with baseline hash")
+
+ # For each hash, see if the rolling build contains the JIT.
+
+ hashnum = 1
+ for git_hash in change_list_hashes:
+ print("try {}: {}".format(hashnum, git_hash))
+
+ # Set the git hash to look for
+ # Note: there's a slight inefficiency here because this code searches for a JIT at this hash value, and
+ # then when we go to download, we do the same search again because we don't cache the result and pass it
+ # directly on to the downloader.
+ coreclr_args.git_hash = git_hash
+ urls = get_jit_urls(coreclr_args, find_all=False)
+ if len(urls) > 1:
+ if hashnum > 1:
+ print("Warning: the baseline found is not built with the first git hash with JIT code changes; there may be extraneous diffs")
+ return
+
+ # We didn't find a baseline; keep looking
+ hashnum += 1
+
+ # We ran out of hashes of JIT changes, and didn't find a baseline. Give up.
+ print("Error: no baseline JIT found")
+
+ raise RuntimeError("No baseline JIT found")
+
+
def list_az_jits(filter_func=lambda unused: True, prefix_string = None):
""" List the JITs in Azure Storage using REST api
@@ -198,7 +313,7 @@ def list_az_jits(filter_func=lambda unused: True, prefix_string = None):
urls = []
- list_az_container_uri_root = az_blob_storage_container_uri + "?restype=container&comp=list&prefix=" + az_builds_root_folder + "/"
+ list_az_container_uri_root = az_blob_storage_jitrollingbuild_container_uri + "?restype=container&comp=list&prefix=" + az_builds_root_folder + "/"
if prefix_string:
list_az_container_uri_root += prefix_string
@@ -268,7 +383,7 @@ def upload_command(coreclr_args):
print("JIT upload")
def upload_blob(file, blob_name):
- blob_client = blob_service_client.get_blob_client(container=az_container_name, blob=blob_name)
+ blob_client = blob_service_client.get_blob_client(container=az_jitrollingbuild_container_name, blob=blob_name)
# Check if the blob already exists, and delete it if it does, before uploading / replacing it.
try:
@@ -382,14 +497,14 @@ def upload_blob(file, blob_name):
total_bytes_uploaded += zip_stat_result.st_size
blob_name = "{}/{}".format(blob_folder_name, zip_name)
- print("Uploading: {} ({}) -> {}".format(file, zip_path, az_blob_storage_container_uri + "/" + blob_name))
+ print("Uploading: {} ({}) -> {}".format(file, zip_path, az_blob_storage_jitrollingbuild_container_uri + "/" + blob_name))
upload_blob(zip_path, blob_name)
else:
file_stat_result = os.stat(file)
total_bytes_uploaded += file_stat_result.st_size
file_name = os.path.basename(file)
blob_name = "{}/{}".format(blob_folder_name, file_name)
- print("Uploading: {} -> {}".format(file, az_blob_storage_container_uri + "/" + blob_name))
+ print("Uploading: {} -> {}".format(file, az_blob_storage_jitrollingbuild_container_uri + "/" + blob_name))
upload_blob(file, blob_name)
print("Uploaded {:n} bytes".format(total_bytes_uploaded))
@@ -466,7 +581,7 @@ def get_jit_urls(coreclr_args, find_all=False):
"""
blob_filter_string = "{}/{}/{}/{}".format(coreclr_args.git_hash, coreclr_args.host_os, coreclr_args.arch, coreclr_args.build_type)
- blob_prefix_filter = "{}/{}/{}".format(az_blob_storage_container_uri, az_builds_root_folder, blob_filter_string).lower()
+ blob_prefix_filter = "{}/{}/{}".format(az_blob_storage_jitrollingbuild_container_uri, az_builds_root_folder, blob_filter_string).lower()
# Determine if a URL in Azure Storage should be allowed. The URL looks like:
# https://clrjit.blob.core.windows.net/jitrollingbuild/builds/git_hash/Linux/x64/Checked/clrjit.dll
@@ -480,17 +595,27 @@ def filter_jits(url):
def download_command(coreclr_args):
- """ Download the JIT
+ """ Download the JITs
Args:
coreclr_args (CoreclrArguments): parsed args
"""
urls = get_jit_urls(coreclr_args, find_all=False)
- if urls is None:
+ if len(urls) == 0:
+ print("Nothing to download")
return
- download_urls(urls, coreclr_args.target_dir)
+ if coreclr_args.target_dir is None:
+ # Use the same default download location for the JIT as superpmi.py uses for downloading a baseline JIT.
+ default_basejit_root_dir = os.path.join(coreclr_args.spmi_location, "basejit")
+ target_dir = os.path.join(default_basejit_root_dir, "{}.{}.{}.{}".format(coreclr_args.git_hash, coreclr_args.host_os, coreclr_args.arch, coreclr_args.build_type))
+ if not os.path.isdir(target_dir):
+ os.makedirs(target_dir)
+ else:
+ target_dir = coreclr_args.target_dir
+
+ download_urls(urls, target_dir)
def list_command(coreclr_args):
@@ -501,7 +626,8 @@ def list_command(coreclr_args):
"""
urls = get_jit_urls(coreclr_args, find_all=coreclr_args.all)
- if urls is None:
+ if len(urls) == 0:
+ print("No JITs found")
return
count = len(urls)
@@ -536,6 +662,21 @@ def setup_args(args):
lambda unused: True,
"Unable to set mode")
+ def setup_spmi_location_arg(spmi_location):
+ if spmi_location is None:
+ if "SUPERPMI_CACHE_DIRECTORY" in os.environ:
+ spmi_location = os.environ["SUPERPMI_CACHE_DIRECTORY"]
+ spmi_location = os.path.abspath(spmi_location)
+ else:
+ spmi_location = os.path.abspath(os.path.join(coreclr_args.artifacts_location, "spmi"))
+ return spmi_location
+
+ coreclr_args.verify(args,
+ "spmi_location",
+ lambda unused: True,
+ "Unable to set spmi_location",
+ modify_arg=setup_spmi_location_arg)
+
if coreclr_args.mode == "upload":
coreclr_args.verify(args,
@@ -575,10 +716,12 @@ def setup_args(args):
lambda unused: True,
"Unable to set skip_cleanup")
- if not os.path.isdir(coreclr_args.target_dir):
+ if coreclr_args.target_dir is not None and not os.path.isdir(coreclr_args.target_dir):
print("--target_dir directory does not exist")
raise RuntimeError("Error")
+ process_git_hash_arg(coreclr_args)
+
elif coreclr_args.mode == "list":
coreclr_args.verify(args,
@@ -607,7 +750,6 @@ def main(args):
return 1
coreclr_args = setup_args(args)
- success = True
if coreclr_args.mode == "upload":
upload_command(coreclr_args)
@@ -621,7 +763,8 @@ def main(args):
else:
raise NotImplementedError(coreclr_args.mode)
- return 0 if success else 1
+ # Note that if there is any failure, an exception is raised and the process exit code is then `1`
+ return 0
################################################################################
# __main__
diff --git a/src/coreclr/scripts/jitutil.py b/src/coreclr/scripts/jitutil.py
new file mode 100644
index 00000000000000..c7906124b45d4b
--- /dev/null
+++ b/src/coreclr/scripts/jitutil.py
@@ -0,0 +1,673 @@
+#!/usr/bin/env python3
+#
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+#
+# Title : jitutil.py
+#
+# Notes:
+#
+# Utility functions used by Python scripts used by the CLR JIT team.
+#
+################################################################################
+################################################################################
+
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+import logging
+import urllib
+import urllib.request
+import zipfile
+
+################################################################################
+##
+## Helper classes
+##
+################################################################################
+
+class TempDir:
+ """ Class to create a temporary working directory, or use one that is passed as an argument.
+
+ Use with: "with TempDir() as temp_dir" to change to that directory and then automatically
+ change back to the original working directory afterwards and remove the temporary
+ directory and its contents (if skip_cleanup is False).
+ """
+
+ def __init__(self, path=None, skip_cleanup=False):
+ self.mydir = tempfile.mkdtemp() if path is None else path
+ self.cwd = None
+ self._skip_cleanup = skip_cleanup
+
+ def __enter__(self):
+ self.cwd = os.getcwd()
+ os.chdir(self.mydir)
+ return self.mydir
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ os.chdir(self.cwd)
+ if not self._skip_cleanup:
+ shutil.rmtree(self.mydir)
+
+class ChangeDir:
+ """ Class to temporarily change to a given directory. Use with "with".
+ """
+
+ def __init__(self, mydir):
+ self.mydir = mydir
+ self.cwd = None
+
+ def __enter__(self):
+ self.cwd = os.getcwd()
+ os.chdir(self.mydir)
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ os.chdir(self.cwd)
+
+
+################################################################################
+##
+## Azure DevOps pipelines helper functions
+##
+################################################################################
+
+def set_pipeline_variable(name, value):
+ """ This method sets pipeline variable.
+
+ Args:
+ name (string): Name of the variable.
+ value (string): Value of the variable.
+ """
+ define_variable_format = "##vso[task.setvariable variable={0}]{1}"
+ print("{0} -> {1}".format(name, value)) # logging
+ print(define_variable_format.format(name, value)) # set variable
+
+
+################################################################################
+##
+## Helper functions
+##
+################################################################################
+
+def run_command(command_to_run, _cwd=None, _exit_on_fail=False, _output_file=None):
+ """ Runs the command.
+
+ Args:
+ command_to_run ([string]): Command to run along with arguments.
+ _cwd (string): Current working directory.
+ _exit_on_fail (bool): If it should exit on failure.
+ _output_file ():
+ Returns:
+ (string, string, int): Returns a tuple of stdout, stderr, and command return code if _output_file= None
+ Otherwise stdout, stderr are empty.
+ """
+ print("Running: " + " ".join(command_to_run))
+ command_stdout = ""
+ command_stderr = ""
+ return_code = 1
+
+ output_type = subprocess.STDOUT if _output_file else subprocess.PIPE
+ with subprocess.Popen(command_to_run, stdout=subprocess.PIPE, stderr=output_type, cwd=_cwd) as proc:
+
+ # For long running command, continuously print the output
+ if _output_file:
+ while True:
+ with open(_output_file, 'a') as of:
+ output = proc.stdout.readline()
+ if proc.poll() is not None:
+ break
+ if output:
+ output_str = output.strip().decode("utf-8")
+ print(output_str)
+ of.write(output_str + "\n")
+ else:
+ command_stdout, command_stderr = proc.communicate()
+ if len(command_stdout) > 0:
+ print(command_stdout.decode("utf-8"))
+ if len(command_stderr) > 0:
+ print(command_stderr.decode("utf-8"))
+
+ return_code = proc.returncode
+ if _exit_on_fail and return_code != 0:
+ print("Command failed. Exiting.")
+ sys.exit(1)
+ return command_stdout, command_stderr, return_code
+
+
+def copy_directory(src_path, dst_path, verbose_output=False, verbose_copy=False, verbose_skip=False, match_func=lambda path: True):
+ """Copies directory in 'src_path' to 'dst_path' maintaining the directory
+ structure. https://docs.python.org/3.5/library/shutil.html#shutil.copytree can't
+ be used in this case because it expects the destination directory should not
+ exist, however we do call copy_directory() to copy files to same destination directory.
+
+ Args:
+ src_path (string): Path of source directory that need to be copied.
+ dst_path (string): Path where directory should be copied.
+ verbose_output (bool): True to print every copied or skipped file or error.
+ verbose_copy (bool): True to print every copied file
+ verbose_skip (bool): True to print every skipped file.
+ match_func (str -> bool) : Criteria function determining if a file is copied.
+ """
+ display_copy = verbose_output or verbose_copy
+ display_skip = verbose_output or verbose_skip
+ for item in os.listdir(src_path):
+ src_item = os.path.join(src_path, item)
+ dst_item = os.path.join(dst_path, item)
+ if os.path.isdir(src_item):
+ copy_directory(src_item, dst_item, verbose_output, verbose_copy, verbose_skip, match_func)
+ else:
+ try:
+ if match_func(src_item):
+ if display_copy:
+ print("> copy {0} => {1}".format(src_item, dst_item))
+ try:
+ if not os.path.exists(dst_path):
+ os.makedirs(dst_path)
+ shutil.copy2(src_item, dst_item)
+ except PermissionError as pe_error:
+ print('Ignoring PermissionError: {0}'.format(pe_error))
+ else:
+ if display_skip:
+ print("> skipping {0}".format(src_item))
+ except UnicodeEncodeError:
+ # Should this always be displayed? Or is it too verbose somehow?
+ if verbose_output:
+ print("> Got UnicodeEncodeError")
+
+
+def copy_files(src_path, dst_path, file_names):
+ """Copy files from 'file_names' list from 'src_path' to 'dst_path'.
+ It retains the original directory structure of src_path.
+
+ Args:
+ src_path (string): Source directory from where files are copied.
+ dst_path (string): Destination directory where files to be copied.
+ file_names ([string]): List of full path file names to be copied.
+ """
+
+ print('### Copying below files from {0} to {1}:'.format(src_path, dst_path))
+ print('')
+ print(os.linesep.join(file_names))
+ for f in file_names:
+ # Create same structure in dst so we don't clobber same files names present in different directories
+ dst_path_of_file = f.replace(src_path, dst_path)
+
+ dst_directory = os.path.dirname(dst_path_of_file)
+ if not os.path.exists(dst_directory):
+ os.makedirs(dst_directory)
+ try:
+ shutil.copy2(f, dst_path_of_file)
+ except PermissionError as pe_error:
+ print('Ignoring PermissionError: {0}'.format(pe_error))
+
+
+def remove_prefix(text, prefix):
+ """ Helper function to remove a prefix `prefix` from a string `text`
+ """
+ if text.startswith(prefix):
+ return text[len(prefix):]
+ return text
+
+
+def is_zero_length_file(fpath):
+ """ Determine if a file system path refers to an existing file that is zero length
+
+ Args:
+ fpath (str) : file system path to test
+
+ Returns:
+ bool : true if the path is an existing file that is zero length
+ """
+ return os.path.isfile(fpath) and os.stat(fpath).st_size == 0
+
+
+def is_nonzero_length_file(fpath):
+ """ Determine if a file system path refers to an existing file that is non-zero length
+
+ Args:
+ fpath (str) : file system path to test
+
+ Returns:
+ bool : true if the path is an existing file that is non-zero length
+ """
+ return os.path.isfile(fpath) and os.stat(fpath).st_size != 0
+
+
+def make_safe_filename(s):
+ """ Turn a string into a string usable as a single file name component; replace illegal characters with underscores.
+
+ Args:
+ s (str) : string to convert to a file name
+
+ Returns:
+ (str) : The converted string
+ """
+ def safe_char(c):
+ if c.isalnum():
+ return c
+ else:
+ return "_"
+ return "".join(safe_char(c) for c in s)
+
+
+def find_in_path(name, pathlist, match_func=os.path.isfile):
+ """ Find a name (e.g., directory name or file name) in the file system by searching the directories
+ in a `pathlist` (e.g., PATH environment variable that has been semi-colon
+ split into a list).
+
+ Args:
+ name (str) : name to search for
+ pathlist (list) : list of directory names to search
+ match_func (str -> bool) : determines if the name is a match
+
+ Returns:
+ (str) The pathname of the object, or None if not found.
+ """
+ for dirname in pathlist:
+ candidate = os.path.join(dirname, name)
+ if match_func(candidate):
+ return candidate
+ return None
+
+
+def find_file(filename, pathlist):
+ """ Find a filename in the file system by searching the directories
+ in a `pathlist` (e.g., PATH environment variable that has been semi-colon
+ split into a list).
+
+ Args:
+ filename (str) : name to search for
+ pathlist (list) : list of directory names to search
+
+ Returns:
+ (str) The pathname of the object, or None if not found.
+ """
+ return find_in_path(filename, pathlist)
+
+
+def find_dir(dirname, pathlist):
+ """ Find a directory name in the file system by searching the directories
+ in a `pathlist` (e.g., PATH environment variable that has been semi-colon
+ split into a list).
+
+ Args:
+ dirname (str) : name to search for
+ pathlist (list) : list of directory names to search
+
+ Returns:
+ (str) The pathname of the object, or None if not found.
+ """
+ return find_in_path(dirname, pathlist, match_func=os.path.isdir)
+
+
+def create_unique_directory_name(root_directory, base_name):
+ """ Create a unique directory name by joining `root_directory` and `base_name`.
+ If this name already exists, append ".1", ".2", ".3", etc., to the final
+ name component until the full directory name is not found.
+
+ Args:
+ root_directory (str) : root directory in which a new directory will be created
+ base_name (str) : the base name of the new directory name component to be added
+
+ Returns:
+ (str) The full absolute path of the new directory. The directory has been created.
+ """
+ root_directory = os.path.abspath(root_directory)
+ full_path = os.path.join(root_directory, base_name)
+
+ count = 1
+ while os.path.isdir(full_path):
+ new_full_path = os.path.join(root_directory, base_name + "." + str(count))
+ count += 1
+ full_path = new_full_path
+
+ os.makedirs(full_path)
+ return full_path
+
+
+def create_unique_file_name(directory, base_name, extension):
+ """ Create a unique file name in the specified directory by joining `base_name` and `extension`.
+ If this name already exists, append ".1", ".2", ".3", etc., to the `base_name`
+ name component until the full file name is not found.
+
+ Args:
+ directory (str) : directory in which a new file will be created
+ base_name (str) : the base name of the new filename to be added
+ extension (str) : the filename extension of the new filename to be added
+
+ Returns:
+ (str) The full absolute path of the new filename.
+ """
+ directory = os.path.abspath(directory)
+ if not os.path.isdir(directory):
+ try:
+ os.makedirs(directory)
+ except Exception as exception:
+ logging.critical(exception)
+ raise exception
+
+ full_path = os.path.join(directory, base_name + "." + extension)
+
+ count = 1
+ while os.path.isfile(full_path):
+ new_full_path = os.path.join(directory, base_name + "." + str(count) + "." + extension)
+ count += 1
+ full_path = new_full_path
+
+ return full_path
+
+
+def get_files_from_path(path, match_func=lambda path: True):
+ """ Return all files in a directory tree matching a criteria.
+
+ Args:
+ path (str) : Either a single file to include, or a directory to traverse looking for matching
+ files.
+ match_func (str -> bool) : Criteria function determining if a file is added to the list
+
+ Returns:
+ Array of absolute paths of matching files
+ """
+ if not(os.path.isdir(path) or os.path.isfile(path)):
+ logging.warning("Warning: \"%s\" is not a file or directory", path)
+ return []
+
+ path = os.path.abspath(path)
+
+ files = []
+
+ if os.path.isdir(path):
+ for item in os.listdir(path):
+ files += get_files_from_path(os.path.join(path, item), match_func)
+ else:
+ if match_func(path):
+ files.append(path)
+
+ return files
+
+
+def is_url(path):
+ """ Return True if this looks like a URL
+
+ Args:
+ path (str) : name to check
+
+ Returns:
+ True it it looks like an URL, False otherwise.
+ """
+ # Probably could use urllib.parse to be more precise.
+ # If it doesn't look like an URL, treat it like a file, possibly a UNC file.
+ return path.lower().startswith("http:") or path.lower().startswith("https:")
+
+################################################################################
+##
+## Azure Storage functions
+##
+################################################################################
+
+# Decide if we're going to download and enumerate Azure Storage using anonymous
+# read access and urllib functions (False), or Azure APIs including authentication (True).
+authenticate_using_azure = False
+
+# Have we checked whether we have the Azure Storage libraries yet?
+azure_storage_libraries_check = False
+
+
+def require_azure_storage_libraries(need_azure_storage_blob=True, need_azure_identity=True):
+ """ Check for and import the Azure libraries.
+ We do this lazily, only when we decide we're actually going to need them.
+ Once we've done it once, we don't do it again.
+
+ For this to work for cross-module usage, after you call this function, you need to add a line like:
+ from jitutil import BlobClient, AzureCliCredential
+ naming all the types you want to use.
+
+ The full set of types this function loads:
+ BlobServiceClient, BlobClient, ContainerClient, AzureCliCredential
+ """
+ global azure_storage_libraries_check, BlobServiceClient, BlobClient, ContainerClient, AzureCliCredential
+
+ if azure_storage_libraries_check:
+ return
+
+ azure_storage_libraries_check = True
+
+ azure_storage_blob_import_ok = True
+ if need_azure_storage_blob:
+ try:
+ from azure.storage.blob import BlobServiceClient, BlobClient, ContainerClient
+ except:
+ azure_storage_blob_import_ok = False
+
+ azure_identity_import_ok = True
+ if need_azure_identity:
+ try:
+ from azure.identity import AzureCliCredential
+ except:
+ azure_identity_import_ok = False
+
+ if not azure_storage_blob_import_ok or not azure_identity_import_ok:
+ logging.error("One or more required Azure Storage packages is missing.")
+ logging.error("")
+ logging.error("Please install:")
+ logging.error(" pip install azure-storage-blob azure-identity")
+ logging.error("or (Windows):")
+ logging.error(" py -3 -m pip install azure-storage-blob azure-identity")
+ logging.error("See also https://docs.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-python")
+ raise RuntimeError("Missing Azure Storage package.")
+
+ # The Azure packages spam all kinds of output to the logging channels.
+ # Restrict this to only ERROR and CRITICAL.
+ for name in logging.Logger.manager.loggerDict.keys():
+ if 'azure' in name:
+ logging.getLogger(name).setLevel(logging.ERROR)
+
+
+def report_azure_error():
+ """ Report an Azure error
+ """
+ logging.error("A problem occurred accessing Azure. Are you properly authenticated using the Azure CLI?")
+ logging.error("Install the Azure CLI from https://docs.microsoft.com/en-us/cli/azure/install-azure-cli.")
+ logging.error("Then log in to Azure using `az login`.")
+
+
+def download_with_azure(uri, target_location, fail_if_not_found=True):
+ """ Do an URI download using Azure blob storage API. Compared to urlretrieve,
+ there is no progress hook. Maybe if converted to use the async APIs we
+ could have some kind of progress?
+
+ Args:
+ uri (string) : URI to download
+ target_location (string) : local path to put the downloaded object
+ fail_if_not_found (bool) : if True, fail if a download fails due to file not found (HTTP error 404).
+ Otherwise, ignore the failure.
+
+ Returns True if successful, False on failure
+ """
+
+ require_azure_storage_libraries()
+
+ logging.info("Download: %s -> %s", uri, target_location)
+
+ ok = True
+ az_credential = AzureCliCredential()
+ blob = BlobClient.from_blob_url(uri, credential=az_credential)
+ with open(target_location, "wb") as my_blob:
+ try:
+ download_stream = blob.download_blob(retry_total=0)
+ try:
+ my_blob.write(download_stream.readall())
+ except Exception as ex1:
+ logging.error("Error writing data to %s", target_location)
+ report_azure_error()
+ ok = False
+ except Exception as ex2:
+ logging.error("Azure error downloading %s", uri)
+ report_azure_error()
+ ok = False
+
+ if not ok and fail_if_not_found:
+ raise RuntimeError("Azure failed to download")
+ return ok
+
+################################################################################
+##
+## File downloading functions
+##
+################################################################################
+
+
+def download_progress_hook(count, block_size, total_size):
+ """ A hook for urlretrieve to report download progress
+
+ Args:
+ count (int) : current block index
+ block_size (int) : size of a block
+ total_size (int) : total size of a payload
+ """
+ sys.stdout.write("\rDownloading {0:.1f}/{1:.1f} MB...".format(min(count * block_size, total_size) / 1024 / 1024, total_size / 1024 / 1024))
+ sys.stdout.flush()
+
+
+def download_with_progress_urlretrieve(uri, target_location, fail_if_not_found=True, display_progress=True):
+ """ Do an URI download using urllib.request.urlretrieve with a progress hook.
+
+ Outputs messages using the `logging` package.
+
+ Args:
+ uri (string) : URI to download
+ target_location (string) : local path to put the downloaded object
+ fail_if_not_found (bool) : if True, fail if a download fails due to file not found (HTTP error 404).
+ Otherwise, ignore the failure.
+ display_progress (bool) : if True, display download progress (for URL downloads). Otherwise, do not.
+
+ Returns True if successful, False on failure
+ """
+ logging.info("Download: %s -> %s", uri, target_location)
+
+ ok = True
+ try:
+ progress_display_method = download_progress_hook if display_progress else None
+ urllib.request.urlretrieve(uri, target_location, reporthook=progress_display_method)
+ except urllib.error.HTTPError as httperror:
+ if (httperror == 404) and fail_if_not_found:
+ logging.error("HTTP 404 error")
+ raise httperror
+ ok = False
+
+ if display_progress:
+ sys.stdout.write("\n") # Add newline after progress hook
+
+ return ok
+
+
+def download_one_url(uri, target_location, fail_if_not_found=True, is_azure_storage=False, display_progress=True):
+ """ Do an URI download using urllib.request.urlretrieve or Azure Storage APIs.
+
+ Args:
+ uri (string) : URI to download
+ target_location (string) : local path to put the downloaded object
+ fail_if_not_found (bool) : if True, fail if a download fails due to file not found (HTTP error 404).
+ Otherwise, ignore the failure.
+ display_progress (bool) : if True, display download progress (for URL downloads). Otherwise, do not.
+
+ Returns True if successful, False on failure
+ """
+ if is_azure_storage and authenticate_using_azure:
+ return download_with_azure(uri, target_location, fail_if_not_found)
+ else:
+ return download_with_progress_urlretrieve(uri, target_location, fail_if_not_found, display_progress)
+
+
+def download_files(paths, target_dir, verbose=True, fail_if_not_found=True, is_azure_storage=False, display_progress=True):
+ """ Download a set of files, specified as URLs or paths (such as Windows UNC paths),
+ to a target directory. If a file is a .ZIP file, then uncompress the file and
+ copy all its contents to the target directory.
+
+ Args:
+ paths (list): the URLs and paths to download
+ target_dir (str): target directory where files are copied. It will be created if it doesn't already exist.
+ verbose (bool): if True, do verbose logging.
+ fail_if_not_found (bool): if True, fail if a download fails due to file not found (HTTP error 404).
+ Otherwise, ignore the failure.
+ is_azure_storage (bool): if True, treat any URL as an Azure Storage URL
+ display_progress (bool): if True, display download progress (for URL downloads). Otherwise, do not.
+
+ Returns:
+ list of full paths of local filenames of downloaded files in the target directory
+ """
+
+ if len(paths) == 0:
+ logging.warning("No files specified to download")
+ return None
+
+ if verbose:
+ logging.info("Downloading:")
+ for item_path in paths:
+ logging.info(" %s", item_path)
+
+ # Create the target directory now, if it doesn't already exist.
+ target_dir = os.path.abspath(target_dir)
+ if not os.path.isdir(target_dir):
+ os.makedirs(target_dir)
+
+ local_paths = []
+
+ # In case we'll need a temp directory for ZIP file processing, create it first.
+ with TempDir() as temp_location:
+ for item_path in paths:
+ is_item_url = is_url(item_path)
+ item_name = item_path.split("/")[-1] if is_item_url else os.path.basename(item_path)
+
+ if item_path.lower().endswith(".zip"):
+ # Delete everything in the temp_location (from previous iterations of this loop, so previous URL downloads).
+ temp_location_items = [os.path.join(temp_location, item) for item in os.listdir(temp_location)]
+ for item in temp_location_items:
+ if os.path.isdir(item):
+ shutil.rmtree(item)
+ else:
+ os.remove(item)
+
+ download_path = os.path.join(temp_location, item_name)
+ if is_item_url:
+ ok = download_one_url(item_path, download_path, fail_if_not_found=fail_if_not_found, is_azure_storage=is_azure_storage, display_progress=display_progress)
+ if not ok:
+ continue
+ else:
+ if fail_if_not_found or os.path.isfile(item_path):
+ if verbose:
+ logging.info("Download: %s -> %s", item_path, download_path)
+ shutil.copy2(item_path, download_path)
+
+ if verbose:
+ logging.info("Uncompress %s", download_path)
+ with zipfile.ZipFile(download_path, "r") as file_handle:
+ file_handle.extractall(temp_location)
+
+ # Copy everything that was extracted to the target directory.
+ copy_directory(temp_location, target_dir, verbose_copy=verbose, match_func=lambda path: not path.endswith(".zip"))
+
+ # The caller wants to know where all the files ended up, so compute that.
+ for dirpath, _, files in os.walk(temp_location, topdown=True):
+ for file_name in files:
+ if not file_name.endswith(".zip"):
+ full_file_path = os.path.join(dirpath, file_name)
+ target_path = full_file_path.replace(temp_location, target_dir)
+ local_paths.append(target_path)
+ else:
+ # Not a zip file; download directory to target directory
+ download_path = os.path.join(target_dir, item_name)
+ if is_item_url:
+ ok = download_one_url(item_path, download_path, fail_if_not_found=fail_if_not_found, is_azure_storage=is_azure_storage, display_progress=display_progress)
+ if not ok:
+ continue
+ else:
+ if fail_if_not_found or os.path.isfile(item_path):
+ if verbose:
+ logging.info("Download: %s -> %s", item_path, download_path)
+ shutil.copy2(item_path, download_path)
+ local_paths.append(download_path)
+
+ return local_paths
diff --git a/src/coreclr/scripts/superpmi-asmdiffs.proj b/src/coreclr/scripts/superpmi-asmdiffs.proj
new file mode 100644
index 00000000000000..aef782d769cb9e
--- /dev/null
+++ b/src/coreclr/scripts/superpmi-asmdiffs.proj
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+ %HELIX_PYTHONPATH%
+ %HELIX_CORRELATION_PAYLOAD%
+ %HELIX_WORKITEM_UPLOAD_ROOT%
+
+ $(BUILD_SOURCESDIRECTORY)\artifacts\helixresults
+ $(Python) $(ProductDirectory)\superpmi_asmdiffs.py -base_jit_directory $(ProductDirectory)\base -diff_jit_directory $(ProductDirectory)\diff -log_directory $(SuperpmiLogsLocation)
+ 1:00
+
+
+
+ false
+ false
+ $(_Creator)
+ $(_HelixAccessToken)
+ $(_HelixBuild)
+ $(_HelixSource)
+ $(_HelixTargetQueues)
+ $(_HelixType)
+
+
+
+
+ %(Identity)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(WorkItemCommand) -arch %(HelixWorkItem.Architecture) -platform %(HelixWorkItem.Platform)
+ $(WorkItemTimeout)
+ superpmi_%(HelixWorkItem.Platform)_%(HelixWorkItem.Architecture).log;superpmi_download_%(HelixWorkItem.Platform)_%(HelixWorkItem.Architecture).log;superpmi_diff_summary_%(HelixWorkItem.Platform)_%(HelixWorkItem.Architecture).md;Asmdiffs_%(HelixWorkItem.Platform)_%(HelixWorkItem.Architecture).zip
+
+
+
diff --git a/src/coreclr/scripts/superpmi.py b/src/coreclr/scripts/superpmi.py
index e919117d8c39ad..267879c5a9eb54 100755
--- a/src/coreclr/scripts/superpmi.py
+++ b/src/coreclr/scripts/superpmi.py
@@ -36,13 +36,13 @@
import zipfile
from coreclr_arguments import *
+from jitutil import TempDir, ChangeDir, remove_prefix, is_zero_length_file, is_nonzero_length_file, \
+ make_safe_filename, find_file, download_one_url, download_files, report_azure_error, \
+ require_azure_storage_libraries, authenticate_using_azure, \
+ create_unique_directory_name, create_unique_file_name, get_files_from_path
locale.setlocale(locale.LC_ALL, '') # Use '' for auto, or force e.g. to 'en_US.UTF-8'
-# Decide if we're going to download and enumerate Azure Storage using anonymous
-# read access and urllib functions (False), or Azure APIs including authentication (True).
-authenticate_using_azure = False
-
################################################################################
# Azure Storage information
################################################################################
@@ -237,8 +237,9 @@
core_root_parser.add_argument("-log_level", help=log_level_help)
core_root_parser.add_argument("-log_file", help=log_file_help)
core_root_parser.add_argument("-spmi_location", help=spmi_location_help)
+core_root_parser.add_argument("--no_progress", action="store_true", help=download_no_progress_help)
-# Create a set of arguments common to target specification. Used for replay, upload, upload-private, download, list-collections.
+# Create a set of arguments common to target specification. Used for collect, replay, asmdiffs, upload, upload-private, download, list-collections.
target_parser = argparse.ArgumentParser(add_help=False)
@@ -311,13 +312,13 @@
asm_diff_parser.add_argument("-git_hash", help="Use this git hash as the current hash for use to find a baseline JIT. Defaults to current git hash of source tree.")
asm_diff_parser.add_argument("-base_git_hash", help="Use this git hash as the baseline JIT hash. Default: search for the baseline hash.")
asm_diff_parser.add_argument("--diff_jit_dump", action="store_true", help="Generate JitDump output for diffs. Default: only generate asm, not JitDump.")
-asm_diff_parser.add_argument("-temp_dir", help="Specify a temporary directory used for a previous ASM diffs run (for which --skip_cleanup was used) to view the results. The replay command is skipped.")
asm_diff_parser.add_argument("--gcinfo", action="store_true", help="Include GC info in disassembly (sets COMPlus_JitGCDump/COMPlus_NgenGCDump; requires instructions to be prefixed by offsets).")
asm_diff_parser.add_argument("--debuginfo", action="store_true", help="Include debug info after disassembly (sets COMPlus_JitDebugDump/COMPlus_NgenDebugDump).")
asm_diff_parser.add_argument("-base_jit_option", action="append", help="Option to pass to the baseline JIT. Format is key=value, where key is the option name without leading COMPlus_...")
asm_diff_parser.add_argument("-diff_jit_option", action="append", help="Option to pass to the diff JIT. Format is key=value, where key is the option name without leading COMPlus_...")
asm_diff_parser.add_argument("-tag", help="Specify a word to add to the directory name where the asm diffs will be placed")
asm_diff_parser.add_argument("-metrics", action="append", help="Metrics option to pass to jit-analyze. Can be specified multiple times, or pass comma-separated values.")
+asm_diff_parser.add_argument("-retainOnlyTopFiles", action="store_true", help="Retain only top .dasm files with largest improvements or regressions and delete remaining files.")
# subparser for upload
upload_parser = subparsers.add_parser("upload", description=upload_description, parents=[core_root_parser, target_parser])
@@ -342,7 +343,6 @@
download_parser.add_argument("-jit_ee_version", help=jit_ee_version_help)
download_parser.add_argument("--skip_cleanup", action="store_true", help=skip_cleanup_help)
download_parser.add_argument("--force_download", action="store_true", help=force_download_help)
-download_parser.add_argument("--no_progress", action="store_true", help=download_no_progress_help)
download_parser.add_argument("-mch_files", metavar="MCH_FILE", nargs='+', help=replay_mch_files_help)
download_parser.add_argument("-private_store", action="append", help=private_store_help)
@@ -363,345 +363,6 @@
################################################################################
# Helper functions
################################################################################
-
-def remove_prefix(text, prefix):
- """ Helper method to remove a prefix `prefix` from a string `text`
- """
- if text.startswith(prefix):
- return text[len(prefix):]
- return text
-
-# Have we checked whether we have the Azure Storage libraries yet?
-azure_storage_libraries_check = False
-
-
-def require_azure_storage_libraries(need_azure_storage_blob=True, need_azure_identity=True):
- """ Check for and import the Azure libraries.
- We do this lazily, only when we decide we're actually going to need them.
- Once we've done it once, we don't do it again.
- """
- global azure_storage_libraries_check, BlobServiceClient, BlobClient, ContainerClient, AzureCliCredential
-
- if azure_storage_libraries_check:
- return
-
- azure_storage_libraries_check = True
-
- azure_storage_blob_import_ok = True
- if need_azure_storage_blob:
- try:
- from azure.storage.blob import BlobServiceClient, BlobClient, ContainerClient
- except:
- azure_storage_blob_import_ok = False
-
- azure_identity_import_ok = True
- if need_azure_identity:
- try:
- from azure.identity import AzureCliCredential
- except:
- azure_identity_import_ok = False
-
- if not azure_storage_blob_import_ok or not azure_identity_import_ok:
- logging.error("One or more required Azure Storage packages is missing.")
- logging.error("")
- logging.error("Please install:")
- logging.error(" pip install azure-storage-blob azure-identity")
- logging.error("or (Windows):")
- logging.error(" py -3 -m pip install azure-storage-blob azure-identity")
- logging.error("See also https://docs.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-python")
- raise RuntimeError("Missing Azure Storage package.")
-
- # The Azure packages spam all kinds of output to the logging channels.
- # Restrict this to only ERROR and CRITICAL.
- for name in logging.Logger.manager.loggerDict.keys():
- if 'azure' in name:
- logging.getLogger(name).setLevel(logging.ERROR)
-
-
-def download_progress_hook(count, block_size, total_size):
- """ A hook for urlretrieve to report download progress
-
- Args:
- count (int) : current block index
- block_size (int) : size of a block
- total_size (int) : total size of a payload
- """
- sys.stdout.write("\rDownloading {0:.1f}/{1:.1f} MB...".format(min(count * block_size, total_size) / 1024 / 1024, total_size / 1024 / 1024))
- sys.stdout.flush()
-
-
-def download_with_progress_urlretrieve(uri, target_location, fail_if_not_found=True, display_progress=True):
- """ Do an URI download using urllib.request.urlretrieve with a progress hook.
-
- Args:
- uri (string) : URI to download
- target_location (string) : local path to put the downloaded object
- fail_if_not_found (bool) : if True, fail if a download fails due to file not found (HTTP error 404).
- Otherwise, ignore the failure.
-
- Returns True if successful, False on failure
- """
- logging.info("Download: %s -> %s", uri, target_location)
-
- ok = True
- try:
- progress_display_method = download_progress_hook if display_progress else None
- urllib.request.urlretrieve(uri, target_location, reporthook=progress_display_method)
- except urllib.error.HTTPError as httperror:
- if (httperror == 404) and fail_if_not_found:
- logging.error("HTTP 404 error")
- raise httperror
- ok = False
-
- sys.stdout.write("\n") # Add newline after progress hook
- return ok
-
-
-def report_azure_error():
- """ Report an Azure error
- """
- logging.error("A problem occurred accessing Azure. Are you properly authenticated using the Azure CLI?")
- logging.error("Install the Azure CLI from https://docs.microsoft.com/en-us/cli/azure/install-azure-cli.")
- logging.error("Then log in to Azure using `az login`.")
-
-
-def download_with_azure(uri, target_location, fail_if_not_found=True):
- """ Do an URI download using Azure blob storage API. Compared to urlretrieve,
- there is no progress hook. Maybe if converted to use the async APIs we
- could have some kind of progress?
-
- Args:
- uri (string) : URI to download
- target_location (string) : local path to put the downloaded object
- fail_if_not_found (bool) : if True, fail if a download fails due to file not found (HTTP error 404).
- Otherwise, ignore the failure.
-
- Returns True if successful, False on failure
- """
-
- require_azure_storage_libraries()
-
- logging.info("Download: %s -> %s", uri, target_location)
-
- ok = True
- az_credential = AzureCliCredential()
- blob = BlobClient.from_blob_url(uri, credential=az_credential)
- with open(target_location, "wb") as my_blob:
- try:
- download_stream = blob.download_blob(retry_total=0)
- try:
- my_blob.write(download_stream.readall())
- except Exception as ex1:
- logging.error("Error writing data to %s", target_location)
- report_azure_error()
- ok = False
- except Exception as ex2:
- logging.error("Azure error downloading %s", uri)
- report_azure_error()
- ok = False
-
- if not ok and fail_if_not_found:
- raise RuntimeError("Azure failed to download")
- return ok
-
-
-def download_one_url(uri, target_location, fail_if_not_found=True, display_progress=True):
- """ Do an URI download using urllib.request.urlretrieve or Azure Storage APIs.
-
- Args:
- uri (string) : URI to download
- target_location (string) : local path to put the downloaded object
- fail_if_not_found (bool) : if True, fail if a download fails due to file not found (HTTP error 404).
- Otherwise, ignore the failure.
-
- Returns True if successful, False on failure
- """
- if authenticate_using_azure:
- return download_with_azure(uri, target_location, fail_if_not_found)
- else:
- return download_with_progress_urlretrieve(uri, target_location, fail_if_not_found, display_progress)
-
-
-def is_zero_length_file(fpath):
- """ Determine if a file system path refers to an existing file that is zero length
-
- Args:
- fpath (str) : file system path to test
-
- Returns:
- bool : true if the path is an existing file that is zero length
- """
- return os.path.isfile(fpath) and os.stat(fpath).st_size == 0
-
-
-def is_nonzero_length_file(fpath):
- """ Determine if a file system path refers to an existing file that is non-zero length
-
- Args:
- fpath (str) : file system path to test
-
- Returns:
- bool : true if the path is an existing file that is non-zero length
- """
- return os.path.isfile(fpath) and os.stat(fpath).st_size != 0
-
-
-def make_safe_filename(s):
- """ Turn a string into a string usable as a single file name component; replace illegal characters with underscores.
-
- Args:
- s (str) : string to convert to a file name
-
- Returns:
- (str) : The converted string
- """
- def safe_char(c):
- if c.isalnum():
- return c
- else:
- return "_"
- return "".join(safe_char(c) for c in s)
-
-
-def find_in_path(name, pathlist, match_func=os.path.isfile):
- """ Find a name (e.g., directory name or file name) in the file system by searching the directories
- in a `pathlist` (e.g., PATH environment variable that has been semi-colon
- split into a list).
-
- Args:
- name (str) : name to search for
- pathlist (list) : list of directory names to search
- match_func (str -> bool) : determines if the name is a match
-
- Returns:
- (str) The pathname of the object, or None if not found.
- """
- for dirname in pathlist:
- candidate = os.path.join(dirname, name)
- if match_func(candidate):
- return candidate
- return None
-
-
-def find_file(filename, pathlist):
- """ Find a filename in the file system by searching the directories
- in a `pathlist` (e.g., PATH environment variable that has been semi-colon
- split into a list).
-
- Args:
- filename (str) : name to search for
- pathlist (list) : list of directory names to search
-
- Returns:
- (str) The pathname of the object, or None if not found.
- """
- return find_in_path(filename, pathlist)
-
-
-def find_dir(dirname, pathlist):
- """ Find a directory name in the file system by searching the directories
- in a `pathlist` (e.g., PATH environment variable that has been semi-colon
- split into a list).
-
- Args:
- dirname (str) : name to search for
- pathlist (list) : list of directory names to search
-
- Returns:
- (str) The pathname of the object, or None if not found.
- """
- return find_in_path(dirname, pathlist, match_func=os.path.isdir)
-
-
-def create_unique_directory_name(root_directory, base_name):
- """ Create a unique directory name by joining `root_directory` and `base_name`.
- If this name already exists, append ".1", ".2", ".3", etc., to the final
- name component until the full directory name is not found.
-
- Args:
- root_directory (str) : root directory in which a new directory will be created
- base_name (str) : the base name of the new directory name component to be added
-
- Returns:
- (str) The full absolute path of the new directory. The directory has been created.
- """
-
- root_directory = os.path.abspath(root_directory)
- full_path = os.path.join(root_directory, base_name)
-
- count = 1
- while os.path.isdir(full_path):
- new_full_path = os.path.join(root_directory, base_name + "." + str(count))
- count += 1
- full_path = new_full_path
-
- os.makedirs(full_path)
- return full_path
-
-
-def create_unique_file_name(directory, base_name, extension):
- """ Create a unique file name in the specified directory by joining `base_name` and `extension`.
- If this name already exists, append ".1", ".2", ".3", etc., to the `base_name`
- name component until the full file name is not found.
-
- Args:
- directory (str) : directory in which a new file will be created
- base_name (str) : the base name of the new filename to be added
- extension (str) : the filename extension of the new filename to be added
-
- Returns:
- (str) The full absolute path of the new filename.
- """
-
- directory = os.path.abspath(directory)
- if not os.path.isdir(directory):
- try:
- os.makedirs(directory)
- except Exception as exception:
- logging.critical(exception)
- raise exception
-
- full_path = os.path.join(directory, base_name + "." + extension)
-
- count = 1
- while os.path.isfile(full_path):
- new_full_path = os.path.join(directory, base_name + "." + str(count) + "." + extension)
- count += 1
- full_path = new_full_path
-
- return full_path
-
-
-def get_files_from_path(path, match_func=lambda path: True):
- """ Return all files in a directory tree matching a criteria.
-
- Args:
- path (str) : Either a single file to include, or a directory to traverse looking for matching
- files.
- match_func (str -> bool) : Criteria function determining if a file is added to the list
-
- Returns:
- Array of absolute paths of matching files
- """
-
- if not(os.path.isdir(path) or os.path.isfile(path)):
- logging.warning("Warning: \"%s\" is not a file or directory", path)
- return []
-
- path = os.path.abspath(path)
-
- files = []
-
- if os.path.isdir(path):
- for item in os.listdir(path):
- files += get_files_from_path(os.path.join(path, item), match_func)
- else:
- if match_func(path):
- files.append(path)
-
- return files
-
-
def run_and_log(command, log_level=logging.DEBUG):
""" Return a command and log its output to the debug logger
@@ -788,19 +449,6 @@ def create_artifacts_base_name(coreclr_args, mch_file):
return artifacts_base_name
-def is_url(path):
- """ Return True if this looks like a URL
-
- Args:
- path (str) : name to check
-
- Returns:
- True it it looks like an URL, False otherwise.
- """
- # Probably could use urllib.parse to be more precise.
- # If it doesn't look like an URL, treat it like a file, possibly a UNC file.
- return path.lower().startswith("http:") or path.lower().startswith("https:")
-
def read_csv_metrics(path):
""" Read a metrics summary file produced by superpmi, and return the single row containing the information as a dictionary.
@@ -822,47 +470,6 @@ def read_csv_metrics(path):
# Helper classes
################################################################################
-
-class TempDir:
- """ Class to create a temporary working directory, or use one that is passed as an argument.
-
- Use with: "with TempDir() as temp_dir" to change to that directory and then automatically
- change back to the original working directory afterwards and remove the temporary
- directory and its contents (if skip_cleanup is False).
- """
-
- def __init__(self, path=None, skip_cleanup=False):
- self.mydir = tempfile.mkdtemp() if path is None else path
- self.cwd = None
- self._skip_cleanup = skip_cleanup
-
- def __enter__(self):
- self.cwd = os.getcwd()
- os.chdir(self.mydir)
- return self.mydir
-
- def __exit__(self, exc_type, exc_val, exc_tb):
- os.chdir(self.cwd)
- if not self._skip_cleanup:
- shutil.rmtree(self.mydir)
-
-
-class ChangeDir:
- """ Class to temporarily change to a given directory. Use with "with".
- """
-
- def __init__(self, mydir):
- self.mydir = mydir
- self.cwd = None
-
- def __enter__(self):
- self.cwd = os.getcwd()
- os.chdir(self.mydir)
-
- def __exit__(self, exc_type, exc_val, exc_tb):
- os.chdir(self.cwd)
-
-
class AsyncSubprocessHelper:
""" Class to help with async multiprocessing tasks.
"""
@@ -1471,7 +1078,7 @@ def __verify_final_mch__(self):
################################################################################
-def print_superpmi_failure_code(return_code, coreclr_args):
+def print_superpmi_result(return_code, coreclr_args, base_metrics, diff_metrics):
""" Print a description of a superpmi return (error) code. If the return code is
zero, meaning success, don't print anything.
Note that Python treats process return codes (at least on Windows) as
@@ -1479,8 +1086,7 @@ def print_superpmi_failure_code(return_code, coreclr_args):
those return codes.
"""
if return_code == 0:
- # Don't print anything if the code is zero, which is success.
- pass
+ logging.info("Clean SuperPMI {} ({} contexts processed)".format("replay" if diff_metrics is None else "diff", base_metrics["Successful compiles"]))
elif return_code == -1 or return_code == 4294967295:
logging.error("General fatal error")
elif return_code == -2 or return_code == 4294967294:
@@ -1490,7 +1096,15 @@ def print_superpmi_failure_code(return_code, coreclr_args):
elif return_code == 2:
logging.warning("Asm diffs found")
elif return_code == 3:
- logging.warning("SuperPMI missing data encountered")
+ missing_base = int(base_metrics["Missing compiles"])
+ total_contexts = int(base_metrics["Successful compiles"]) + int(base_metrics["Failing compiles"])
+
+ if diff_metrics is None:
+ logging.warning("SuperPMI encountered missing data for {} out of {} contexts".format(missing_base, total_contexts))
+ else:
+ missing_diff = int(diff_metrics["Missing compiles"])
+ logging.warning("SuperPMI encountered missing data. Missing with base JIT: {}. Missing with diff JIT: {}. Total contexts: {}.".format(missing_base, missing_diff, total_contexts))
+
elif return_code == 139 and coreclr_args.host_os != "windows":
logging.error("Fatal error, SuperPMI has returned SIGSEGV (segmentation fault)")
else:
@@ -1636,31 +1250,35 @@ def replay(self):
flags = common_flags.copy()
fail_mcl_file = os.path.join(temp_location, os.path.basename(mch_file) + "_fail.mcl")
+ metrics_summary_file = os.path.join(temp_location, os.path.basename(mch_file) + "_metrics.csv")
+
flags += [
- "-f", fail_mcl_file # Failing mc List
+ "-f", fail_mcl_file, # Failing mc List
+ "-metricsSummary", metrics_summary_file
]
command = [self.superpmi_path] + flags + [self.jit_path, mch_file]
return_code = run_and_log(command)
- print_superpmi_failure_code(return_code, self.coreclr_args)
- if return_code == 0:
- logging.info("Clean SuperPMI replay")
- else:
- result = False
+
+ metrics = read_csv_metrics(metrics_summary_file)
+
+ print_superpmi_result(return_code, self.coreclr_args, metrics, None)
+ if return_code != 0:
# Don't report as replay failure missing data (return code 3).
# Anything else, such as compilation failure (return code 1, typically a JIT assert) will be
# reported as a replay failure.
if return_code != 3:
+ result = False
files_with_replay_failures.append(mch_file)
- if is_nonzero_length_file(fail_mcl_file):
- # Unclean replay. Examine the contents of the fail.mcl file to dig into failures.
- if return_code == 0:
- logging.warning("Warning: SuperPMI returned a zero exit code, but generated a non-zero-sized mcl file")
- print_fail_mcl_file_method_numbers(fail_mcl_file)
- repro_base_command_line = "{} {} {}".format(self.superpmi_path, " ".join(repro_flags), self.jit_path)
- artifacts_base_name = create_artifacts_base_name(self.coreclr_args, mch_file)
- save_repro_mc_files(temp_location, self.coreclr_args, artifacts_base_name, repro_base_command_line)
+ if is_nonzero_length_file(fail_mcl_file):
+ # Unclean replay. Examine the contents of the fail.mcl file to dig into failures.
+ if return_code == 0:
+ logging.warning("Warning: SuperPMI returned a zero exit code, but generated a non-zero-sized mcl file")
+ print_fail_mcl_file_method_numbers(fail_mcl_file)
+ repro_base_command_line = "{} {} {}".format(self.superpmi_path, " ".join(repro_flags), self.jit_path)
+ artifacts_base_name = create_artifacts_base_name(self.coreclr_args, mch_file)
+ save_repro_mc_files(temp_location, self.coreclr_args, artifacts_base_name, repro_base_command_line)
if not self.coreclr_args.skip_cleanup:
if os.path.isfile(fail_mcl_file):
@@ -1745,7 +1363,8 @@ def replay_with_asm_diffs(self):
if self.coreclr_args.debuginfo:
asm_complus_vars.update({
"COMPlus_JitDebugDump": "*",
- "COMPlus_NgenDebugDump": "*" })
+ "COMPlus_NgenDebugDump": "*",
+ "COMPlus_JitDisasmWithDebugInfo": "1" })
jit_dump_complus_vars = asm_complus_vars.copy()
jit_dump_complus_vars.update({
@@ -1798,7 +1417,7 @@ def replay_with_asm_diffs(self):
# List of all Markdown summary files
all_md_summary_files = []
- with TempDir(self.coreclr_args.temp_dir, self.coreclr_args.skip_cleanup) as temp_location:
+ with TempDir(None, self.coreclr_args.skip_cleanup) as temp_location:
logging.debug("")
logging.debug("Temp Location: %s", temp_location)
logging.debug("")
@@ -1820,65 +1439,63 @@ def replay_with_asm_diffs(self):
base_metrics_summary_file = os.path.join(temp_location, os.path.basename(mch_file) + "_base_metrics.csv")
diff_metrics_summary_file = os.path.join(temp_location, os.path.basename(mch_file) + "_diff_metrics.csv")
- # If the user passed -temp_dir, we skip the SuperPMI replay process,
- # and rely on what we find from a previous run.
- if self.coreclr_args.temp_dir is not None:
- return_code = 1
- else:
- flags = [
- "-a", # Asm diffs
- "-v", "ewmi", # display errors, warnings, missing, jit info
- "-f", fail_mcl_file, # Failing mc List
- "-diffMCList", diff_mcl_file, # Create all of the diffs in an mcl file
- "-r", os.path.join(temp_location, "repro"), # Repro name, create .mc repro files
- "-baseMetricsSummary", base_metrics_summary_file, # Create summary of metrics we can use to get total code size impact
- "-diffMetricsSummary", diff_metrics_summary_file,
- ]
- flags += altjit_asm_diffs_flags
- flags += base_option_flags
- flags += diff_option_flags
-
- if not self.coreclr_args.sequential:
- flags += [ "-p" ]
-
- if self.coreclr_args.break_on_assert:
- flags += [ "-boa" ]
-
- if self.coreclr_args.break_on_error:
- flags += [ "-boe" ]
-
- if self.coreclr_args.spmi_log_file is not None:
- flags += [ "-w", self.coreclr_args.spmi_log_file ]
-
- if self.coreclr_args.error_limit is not None:
- flags += ["-failureLimit", self.coreclr_args.error_limit]
-
- # Change the working directory to the Core_Root we will call SuperPMI from.
- # This is done to allow libcoredistools to be loaded correctly on unix
- # as the loadlibrary path will be relative to the current directory.
- with ChangeDir(self.coreclr_args.core_root):
- command = [self.superpmi_path] + flags + [self.base_jit_path, self.diff_jit_path, mch_file]
- return_code = run_and_log(command)
- print_superpmi_failure_code(return_code, self.coreclr_args)
- if return_code == 0:
- logging.info("Clean SuperPMI diff")
- else:
- result = False
- # Don't report as replay failure asm diffs (return code 2) or missing data (return code 3).
- # Anything else, such as compilation failure (return code 1, typically a JIT assert) will be
- # reported as a replay failure.
- if return_code != 2 and return_code != 3:
- files_with_replay_failures.append(mch_file)
+ flags = [
+ "-a", # Asm diffs
+ "-v", "ewmi", # display errors, warnings, missing, jit info
+ "-f", fail_mcl_file, # Failing mc List
+ "-diffMCList", diff_mcl_file, # Create all of the diffs in an mcl file
+ "-r", os.path.join(temp_location, "repro"), # Repro name, create .mc repro files
+ "-baseMetricsSummary", base_metrics_summary_file, # Create summary of metrics we can use to get total code size impact
+ "-diffMetricsSummary", diff_metrics_summary_file,
+ ]
+ flags += altjit_asm_diffs_flags
+ flags += base_option_flags
+ flags += diff_option_flags
+ if not self.coreclr_args.sequential:
+ flags += [ "-p" ]
+
+ if self.coreclr_args.break_on_assert:
+ flags += [ "-boa" ]
+
+ if self.coreclr_args.break_on_error:
+ flags += [ "-boe" ]
+
+ if self.coreclr_args.spmi_log_file is not None:
+ flags += [ "-w", self.coreclr_args.spmi_log_file ]
+
+ if self.coreclr_args.error_limit is not None:
+ flags += ["-failureLimit", self.coreclr_args.error_limit]
+
+ # Change the working directory to the Core_Root we will call SuperPMI from.
+ # This is done to allow libcoredistools to be loaded correctly on unix
+ # as the loadlibrary path will be relative to the current directory.
+ with ChangeDir(self.coreclr_args.core_root):
+ command = [self.superpmi_path] + flags + [self.base_jit_path, self.diff_jit_path, mch_file]
+ return_code = run_and_log(command)
+
+ base_metrics = read_csv_metrics(base_metrics_summary_file)
+ diff_metrics = read_csv_metrics(diff_metrics_summary_file)
+
+ print_superpmi_result(return_code, self.coreclr_args, base_metrics, diff_metrics)
artifacts_base_name = create_artifacts_base_name(self.coreclr_args, mch_file)
- if is_nonzero_length_file(fail_mcl_file):
- # Unclean replay. Examine the contents of the fail.mcl file to dig into failures.
- if return_code == 0:
- logging.warning("Warning: SuperPMI returned a zero exit code, but generated a non-zero-sized mcl file")
- print_fail_mcl_file_method_numbers(fail_mcl_file)
- repro_base_command_line = "{} {} {}".format(self.superpmi_path, " ".join(altjit_asm_diffs_flags), self.diff_jit_path)
- save_repro_mc_files(temp_location, self.coreclr_args, artifacts_base_name, repro_base_command_line)
+ if return_code != 0:
+
+ # Don't report as replay failure asm diffs (return code 2) or missing data (return code 3).
+ # Anything else, such as compilation failure (return code 1, typically a JIT assert) will be
+ # reported as a replay failure.
+ if return_code != 2 and return_code != 3:
+ result = False
+ files_with_replay_failures.append(mch_file)
+
+ if is_nonzero_length_file(fail_mcl_file):
+ # Unclean replay. Examine the contents of the fail.mcl file to dig into failures.
+ if return_code == 0:
+ logging.warning("Warning: SuperPMI returned a zero exit code, but generated a non-zero-sized mcl file")
+ print_fail_mcl_file_method_numbers(fail_mcl_file)
+ repro_base_command_line = "{} {} {}".format(self.superpmi_path, " ".join(altjit_asm_diffs_flags), self.diff_jit_path)
+ save_repro_mc_files(temp_location, self.coreclr_args, artifacts_base_name, repro_base_command_line)
# There were diffs. Go through each method that created diffs and
# create a base/diff asm file with diffable asm. In addition, create
@@ -1983,16 +1600,13 @@ async def create_one_artifact(jit_path: str, location: str, flags) -> str:
logging.debug(item)
logging.debug("")
- base_metrics = read_csv_metrics(base_metrics_summary_file)
- diff_metrics = read_csv_metrics(diff_metrics_summary_file)
-
- if base_metrics is not None and "Code bytes" in base_metrics and diff_metrics is not None and "Code bytes" in diff_metrics:
- base_bytes = int(base_metrics["Code bytes"])
- diff_bytes = int(diff_metrics["Code bytes"])
- logging.info("Total Code bytes of base: {}".format(base_bytes))
- logging.info("Total Code bytes of diff: {}".format(diff_bytes))
+ if base_metrics is not None and diff_metrics is not None:
+ base_bytes = int(base_metrics["Diffed code bytes"])
+ diff_bytes = int(diff_metrics["Diffed code bytes"])
+ logging.info("Total bytes of base: {}".format(base_bytes))
+ logging.info("Total bytes of diff: {}".format(diff_bytes))
delta_bytes = diff_bytes - base_bytes
- logging.info("Total Code bytes of delta: {} ({:.2%} of base)".format(delta_bytes, delta_bytes / base_bytes))
+ logging.info("Total bytes of delta: {} ({:.2%} of base)".format(delta_bytes, delta_bytes / base_bytes))
try:
current_text_diff = text_differences.get_nowait()
@@ -2004,11 +1618,11 @@ async def create_one_artifact(jit_path: str, location: str, flags) -> str:
if current_text_diff is not None:
logging.info("Textual differences found in generated asm.")
- # Find jit-analyze.bat/sh on PATH, if it exists, then invoke it.
+ # Find jit-analyze on PATH, if it exists, then invoke it.
ran_jit_analyze = False
path_var = os.environ.get("PATH")
if path_var is not None:
- jit_analyze_file = "jit-analyze.bat" if platform.system() == "Windows" else "jit-analyze.sh"
+ jit_analyze_file = "jit-analyze.exe" if platform.system() == "Windows" else "jit-analyze"
jit_analyze_path = find_file(jit_analyze_file, path_var.split(os.pathsep))
if jit_analyze_path is not None:
# It appears we have a built jit-analyze on the path, so try to run it.
@@ -2016,6 +1630,8 @@ async def create_one_artifact(jit_path: str, location: str, flags) -> str:
summary_file_info = ( mch_file, md_summary_file )
all_md_summary_files.append(summary_file_info)
command = [ jit_analyze_path, "--md", md_summary_file, "-r", "--base", base_asm_location, "--diff", diff_asm_location ]
+ if self.coreclr_args.retainOnlyTopFiles:
+ command += [ "--retainOnlyTopFiles" ]
if self.coreclr_args.metrics:
command += [ "--metrics", ",".join(self.coreclr_args.metrics) ]
elif base_bytes is not None and diff_bytes is not None:
@@ -2044,6 +1660,15 @@ async def create_one_artifact(jit_path: str, location: str, flags) -> str:
else:
logging.warning("No textual differences found in generated JitDump. Is this an issue with coredistools?")
+ if base_metrics is not None and diff_metrics is not None:
+ missing_base = int(base_metrics["Missing compiles"])
+ missing_diff = int(diff_metrics["Missing compiles"])
+ total_contexts = int(base_metrics["Successful compiles"]) + int(base_metrics["Failing compiles"])
+
+ if missing_base > 0 or missing_diff > 0:
+ logging.warning("Warning: SuperPMI encountered missing data during the diff. The diff summary printed above may be misleading.")
+ logging.warning("Missing with base JIT: {}. Missing with diff JIT: {}. Total contexts: {}.".format(missing_base, missing_diff, total_contexts))
+
################################################################################################ end of processing asm diffs (if is_nonzero_length_file(diff_mcl_file)...
if not self.coreclr_args.skip_cleanup:
@@ -2147,7 +1772,8 @@ def determine_coredis_tools(coreclr_args):
logging.warning("Warning: Core_Root does not exist at \"%s\"; creating it now", coreclr_args.core_root)
os.makedirs(coreclr_args.core_root)
coredistools_uri = az_blob_storage_superpmi_container_uri + "/libcoredistools/{}-{}/{}".format(coreclr_args.host_os.lower(), coreclr_args.arch.lower(), coredistools_dll_name)
- download_one_url(coredistools_uri, coredistools_location)
+ skip_progress = hasattr(coreclr_args, 'no_progress') and coreclr_args.no_progress
+ download_one_url(coredistools_uri, coredistools_location, is_azure_storage=True, display_progress=not skip_progress)
assert os.path.isfile(coredistools_location)
return coredistools_location
@@ -2183,7 +1809,8 @@ def determine_pmi_location(coreclr_args):
logging.info("Using PMI found at %s", pmi_location)
else:
pmi_uri = az_blob_storage_superpmi_container_uri + "/pmi/pmi.dll"
- download_one_url(pmi_uri, pmi_location)
+ skip_progress = hasattr(coreclr_args, 'no_progress') and coreclr_args.no_progress
+ download_one_url(pmi_uri, pmi_location, is_azure_storage=True, display_progress=not skip_progress)
assert os.path.isfile(pmi_location)
return pmi_location
@@ -2516,6 +2143,7 @@ def list_superpmi_collections_container_via_azure_api(path_filter=lambda unused:
"""
require_azure_storage_libraries()
+ from jitutil import ContainerClient, AzureCliCredential
superpmi_container_url = az_blob_storage_superpmi_container_uri
@@ -2609,7 +2237,7 @@ def filter_local_path(path):
# Download all the urls at once, and add the local cache filenames to our accumulated list of local file names.
skip_progress = hasattr(coreclr_args, 'no_progress') and coreclr_args.no_progress
if len(urls) != 0:
- local_mch_files += download_files(urls, mch_cache_dir, display_progress=not skip_progress)
+ local_mch_files += download_files(urls, mch_cache_dir, is_azure_storage=True, display_progress=not skip_progress)
# Special case: walk the URLs list and for every ".mch" or ".mch.zip" file, check to see that either the associated ".mct" file is already
# in the list, or add it to a new list to attempt to download (but don't fail the download if it doesn't exist).
@@ -2620,7 +2248,7 @@ def filter_local_path(path):
if mct_url not in urls:
mct_urls.append(mct_url)
if len(mct_urls) != 0:
- local_mch_files += download_files(mct_urls, mch_cache_dir, fail_if_not_found=False, display_progress=not skip_progress)
+ local_mch_files += download_files(mct_urls, mch_cache_dir, fail_if_not_found=False, is_azure_storage=True, display_progress=not skip_progress)
# Even though we might have downloaded MCT files, only return the set of MCH files.
local_mch_files = [file for file in local_mch_files if any(file.lower().endswith(extension) for extension in [".mch"])]
@@ -2716,94 +2344,7 @@ def filter_superpmi_collections(path):
urls = [blob_url_prefix + path for path in paths]
skip_progress = hasattr(coreclr_args, 'no_progress') and coreclr_args.no_progress
- return download_files(urls, target_dir, display_progress=not skip_progress)
-
-def download_files(paths, target_dir, verbose=True, fail_if_not_found=True, display_progress=True):
- """ Download a set of files, specified as URLs or paths (such as Windows UNC paths),
- to a target directory. If a file is a .ZIP file, then uncompress the file and
- copy all its contents to the target directory.
-
- Args:
- paths (list): the URLs and paths to download
- target_dir (str): target directory where files are copied.
- verbse (bool): if True, do verbose logging.
- fail_if_not_found (bool): if True, fail if a download fails due to file not found (HTTP error 404).
- Otherwise, ignore the failure.
-
- Returns:
- list of full paths of local filenames of downloaded files in the target directory
- """
-
- if len(paths) == 0:
- logging.warning("No files specified to download")
- return None
-
- if verbose:
- logging.info("Downloading:")
- for item_path in paths:
- logging.info(" %s", item_path)
-
- # Create the target directory now, if it doesn't already exist.
- target_dir = os.path.abspath(target_dir)
- if not os.path.isdir(target_dir):
- os.makedirs(target_dir)
-
- local_paths = []
-
- # In case we'll need a temp directory for ZIP file processing, create it first.
- with TempDir() as temp_location:
- for item_path in paths:
- is_item_url = is_url(item_path)
- item_name = item_path.split("/")[-1] if is_item_url else os.path.basename(item_path)
-
- if item_path.lower().endswith(".zip"):
- # Delete everything in the temp_location (from previous iterations of this loop, so previous URL downloads).
- temp_location_items = [os.path.join(temp_location, item) for item in os.listdir(temp_location)]
- for item in temp_location_items:
- if os.path.isdir(item):
- shutil.rmtree(item)
- else:
- os.remove(item)
-
- download_path = os.path.join(temp_location, item_name)
- if is_item_url:
- ok = download_one_url(item_path, download_path, fail_if_not_found, display_progress)
- if not ok:
- continue
- else:
- if fail_if_not_found or os.path.isfile(item_path):
- if verbose:
- logging.info("Download: %s -> %s", item_path, download_path)
- shutil.copy2(item_path, download_path)
-
- if verbose:
- logging.info("Uncompress %s", download_path)
- with zipfile.ZipFile(download_path, "r") as file_handle:
- file_handle.extractall(temp_location)
-
- # Copy everything that was extracted to the target directory.
- items = [ os.path.join(temp_location, item) for item in os.listdir(temp_location) if not item.endswith(".zip") ]
- for item in items:
- target_path = os.path.join(target_dir, os.path.basename(item))
- if verbose:
- logging.info("Copy %s -> %s", item, target_path)
- shutil.copy2(item, target_dir)
- local_paths.append(target_path)
- else:
- # Not a zip file; download directory to target directory
- download_path = os.path.join(target_dir, item_name)
- if is_item_url:
- ok = download_one_url(item_path, download_path, fail_if_not_found, display_progress)
- if not ok:
- continue
- else:
- if fail_if_not_found or os.path.isfile(item_path):
- if verbose:
- logging.info("Download: %s -> %s", item_path, download_path)
- shutil.copy2(item_path, download_path)
- local_paths.append(download_path)
-
- return local_paths
+ return download_files(urls, target_dir, is_azure_storage=True, display_progress=not skip_progress)
def upload_mch(coreclr_args):
@@ -2814,6 +2355,7 @@ def upload_mch(coreclr_args):
"""
require_azure_storage_libraries(need_azure_identity=False)
+ from jitutil import BlobServiceClient
def upload_blob(file, blob_name):
blob_client = blob_service_client.get_blob_client(container=az_superpmi_container_name, blob=blob_name)
@@ -3198,7 +2740,7 @@ def process_base_jit_path_arg(coreclr_args):
blob_folder_name = "{}/{}/{}/{}/{}/{}".format(az_builds_root_folder, git_hash, coreclr_args.host_os, coreclr_args.arch, coreclr_args.build_type, jit_name)
blob_uri = "{}/{}".format(az_blob_storage_jitrollingbuild_container_uri, blob_folder_name)
urls = [ blob_uri ]
- local_files = download_files(urls, basejit_dir, verbose=False, fail_if_not_found=False)
+ local_files = download_files(urls, basejit_dir, verbose=False, is_azure_storage=True, fail_if_not_found=False)
if len(local_files) > 0:
if hashnum > 1:
@@ -3278,6 +2820,11 @@ def setup_spmi_location_arg(spmi_location):
"Unable to set spmi_location",
modify_arg=setup_spmi_location_arg)
+ coreclr_args.verify(args,
+ "no_progress",
+ lambda unused: True,
+ "Unable to set no_progress")
+
# Finish setting up logging.
# The spmi_location is the root directory where we put the log file.
# Log everything to the log file and only the specified verbosity to the console logger.
@@ -3682,11 +3229,6 @@ def verify_replay_common_args():
lambda unused: True,
"Unable to set base_git_hash")
- coreclr_args.verify(args,
- "temp_dir",
- lambda unused: True,
- "Unable to set temp_dir.")
-
coreclr_args.verify(args,
"gcinfo",
lambda unused: True,
@@ -3723,6 +3265,11 @@ def verify_replay_common_args():
lambda unused: True,
"Unable to set metrics.")
+ coreclr_args.verify(args,
+ "retainOnlyTopFiles",
+ lambda unused: True,
+ "Unable to set retainOnlyTopFiles.")
+
process_base_jit_path_arg(coreclr_args)
jit_in_product_location = False
@@ -3770,10 +3317,6 @@ def verify_replay_common_args():
os.path.isfile,
"Unable to find coredistools.")
- if coreclr_args.temp_dir is not None:
- coreclr_args.temp_dir = os.path.abspath(coreclr_args.temp_dir)
- logging.debug("Using temp_dir %s", coreclr_args.temp_dir)
-
elif coreclr_args.mode == "upload":
verify_target_args()
@@ -3825,11 +3368,6 @@ def verify_replay_common_args():
lambda unused: True,
"Unable to set force_download")
- coreclr_args.verify(args,
- "no_progress",
- lambda unused: True,
- "Unable to set no_progress")
-
coreclr_args.verify(args,
"filter",
lambda unused: True,
diff --git a/src/coreclr/scripts/superpmi_asmdiffs.py b/src/coreclr/scripts/superpmi_asmdiffs.py
new file mode 100644
index 00000000000000..8a30850e7282f5
--- /dev/null
+++ b/src/coreclr/scripts/superpmi_asmdiffs.py
@@ -0,0 +1,251 @@
+#!/usr/bin/env python3
+#
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+#
+# Title : superpmi_asmdiffs.py
+#
+# Notes:
+#
+# Script to run "superpmi asmdiffs" for various collections on the Helix machines.
+#
+################################################################################
+################################################################################
+
+import argparse
+from os import walk, path
+import shutil
+from coreclr_arguments import *
+from jitutil import run_command, TempDir
+
+parser = argparse.ArgumentParser(description="description")
+
+parser.add_argument("-arch", help="Architecture")
+parser.add_argument("-platform", help="OS platform")
+parser.add_argument("-base_jit_directory", help="path to the directory containing base clrjit binaries")
+parser.add_argument("-diff_jit_directory", help="path to the directory containing diff clrjit binaries")
+parser.add_argument("-log_directory", help="path to the directory containing superpmi log files")
+
+def setup_args(args):
+ """ Setup the args for SuperPMI to use.
+
+ Args:
+ args (ArgParse): args parsed by arg parser
+
+ Returns:
+ args (CoreclrArguments)
+
+ """
+ coreclr_args = CoreclrArguments(args, require_built_core_root=False, require_built_product_dir=False,
+ require_built_test_dir=False, default_build_type="Checked")
+
+ coreclr_args.verify(args,
+ "arch",
+ lambda unused: True,
+ "Unable to set arch")
+
+ coreclr_args.verify(args,
+ "platform",
+ lambda unused: True,
+ "Unable to set platform")
+
+ coreclr_args.verify(args,
+ "base_jit_directory",
+ lambda jit_directory: os.path.isdir(jit_directory),
+ "base_jit_directory doesn't exist")
+
+ coreclr_args.verify(args,
+ "diff_jit_directory",
+ lambda jit_directory: os.path.isdir(jit_directory),
+ "diff_jit_directory doesn't exist")
+
+ coreclr_args.verify(args,
+ "log_directory",
+ lambda log_directory: True,
+ "log_directory doesn't exist")
+
+ return coreclr_args
+
+def copy_dasm_files(spmi_location, upload_directory, tag_name):
+ """Copies .dasm files to a tempDirectory, zip it, and copy the compressed file to the upload directory.
+
+ Args:
+ spmi_location (string): Location where .dasm files are present
+ upload_directory (string): Upload directory
+ tag_name (string): tag_name used in zip file name.
+ """
+
+ print("Copy .dasm files")
+
+ # Create upload_directory
+ if not os.path.isdir(upload_directory):
+ os.makedirs(upload_directory)
+
+ dasm_file_present = False
+ # Create temp directory to copy all issues to upload. We don't want to create a sub-folder
+ # inside issues_directory because that will also get included twice.
+ with TempDir() as prep_directory:
+ for file_path, dirs, files in walk(spmi_location, topdown=True):
+ # Credit: https://stackoverflow.com/a/19859907
+ dirs[:] = [d for d in dirs]
+ for name in files:
+ if not name.lower().endswith(".dasm"):
+ continue
+
+ dasm_src_file = path.join(file_path, name)
+ dasm_dst_file = dasm_src_file.replace(spmi_location, prep_directory)
+ dst_directory = path.dirname(dasm_dst_file)
+ if not os.path.exists(dst_directory):
+ os.makedirs(dst_directory)
+ try:
+ shutil.copy2(dasm_src_file, dasm_dst_file)
+ dasm_file_present = True
+ except PermissionError as pe_error:
+ print('Ignoring PermissionError: {0}'.format(pe_error))
+
+ # If there are no diffs, create an zip file with single file in it.
+ # Otherwise, Azdo considers it as failed job.
+ # See https://github.com/dotnet/arcade/issues/8200
+ if not dasm_file_present:
+ no_diff = os.path.join(prep_directory, "nodiff.txt")
+ with open(no_diff, "w") as no_diff_file:
+ no_diff_file.write("No diffs found!")
+
+ # Zip compress the files we will upload
+ zip_path = os.path.join(prep_directory, "Asmdiffs_" + tag_name)
+ print("Creating archive: " + zip_path)
+ shutil.make_archive(zip_path, 'zip', prep_directory)
+
+ zip_path += ".zip"
+ dst_zip_path = os.path.join(upload_directory, "Asmdiffs_" + tag_name + ".zip")
+ print("Copying {} to {}".format(zip_path, dst_zip_path))
+ try:
+ shutil.copy2(zip_path, dst_zip_path)
+ except PermissionError as pe_error:
+ print('Ignoring PermissionError: {0}'.format(pe_error))
+
+
+def main(main_args):
+ """ Run superpmi asmdiffs process on the Helix machines.
+
+ See superpmi_asmdiffs_setup.py for how the directory structure is set up in the
+ correlation payload. This script lives in the root of that directory tree.
+
+ Args:
+ main_args ([type]): Arguments to the script
+ """
+
+ python_path = sys.executable
+ script_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
+ coreclr_args = setup_args(main_args)
+
+ # It doesn't really matter where we put the downloaded SPMI artifacts.
+ # Here, they are put in /artifacts/spmi.
+ spmi_location = os.path.join(script_dir, "artifacts", "spmi")
+
+ log_directory = coreclr_args.log_directory
+ platform_name = coreclr_args.platform
+
+ # Find the built jit-analyze and put its directory on the PATH
+ jit_analyze_dir = os.path.join(script_dir, "jit-analyze")
+ if not os.path.isdir(jit_analyze_dir):
+ print("Error: jit-analyze not found in {} (continuing)".format(jit_analyze_dir))
+ else:
+ # Put the jit-analyze directory on the PATH so superpmi.py can find it.
+ print("Adding {} to PATH".format(jit_analyze_dir))
+ os.environ["PATH"] = jit_analyze_dir + os.pathsep + os.environ["PATH"]
+
+ # Find the portable `git` installation, and put `git.exe` on the PATH, for use by `jit-analyze`.
+ git_directory = os.path.join(script_dir, "git", "cmd")
+ git_exe_tool = os.path.join(git_directory, "git.exe")
+ if not os.path.isfile(git_exe_tool):
+ print("Error: `git` not found at {} (continuing)".format(git_exe_tool))
+ else:
+ # Put the git/cmd directory on the PATH so jit-analyze can find it.
+ print("Adding {} to PATH".format(git_directory))
+ os.environ["PATH"] = git_directory + os.pathsep + os.environ["PATH"]
+
+ # Figure out which JITs to use
+ os_name = "win" if platform_name.lower() == "windows" else "unix"
+ arch_name = coreclr_args.arch
+ host_arch_name = "x64" if arch_name.endswith("64") else "x86"
+ os_name = "universal" if arch_name.startswith("arm") else os_name
+ base_jit_path = os.path.join(coreclr_args.base_jit_directory, 'clrjit_{}_{}_{}.dll'.format(os_name, arch_name, host_arch_name))
+ diff_jit_path = os.path.join(coreclr_args.diff_jit_directory, 'clrjit_{}_{}_{}.dll'.format(os_name, arch_name, host_arch_name))
+
+ # Core_Root is where the superpmi tools (superpmi.exe, mcs.exe) are expected to be found.
+ # We pass the full path of the JITs to use as arguments.
+ core_root_dir = script_dir
+
+ print("Running superpmi.py download to get MCH files")
+
+ log_file = os.path.join(log_directory, "superpmi_download_{}_{}.log".format(platform_name, arch_name))
+ run_command([
+ python_path,
+ os.path.join(script_dir, "superpmi.py"),
+ "download",
+ "--no_progress",
+ "-core_root", core_root_dir,
+ "-target_os", platform_name,
+ "-target_arch", arch_name,
+ "-spmi_location", spmi_location,
+ "-log_level", "debug",
+ "-log_file", log_file
+ ], _exit_on_fail=True)
+
+ print("Running superpmi.py asmdiffs")
+ log_file = os.path.join(log_directory, "superpmi_{}_{}.log".format(platform_name, arch_name))
+
+ overall_md_summary_file = os.path.join(spmi_location, "diff_summary.md")
+ if os.path.isfile(overall_md_summary_file):
+ os.remove(overall_md_summary_file)
+
+ _, _, return_code = run_command([
+ python_path,
+ os.path.join(script_dir, "superpmi.py"),
+ "asmdiffs",
+ "--no_progress",
+ "-core_root", core_root_dir,
+ "-target_os", platform_name,
+ "-target_arch", arch_name,
+ "-arch", host_arch_name,
+ "-base_jit_path", base_jit_path,
+ "-diff_jit_path", diff_jit_path,
+ "-spmi_location", spmi_location,
+ "-error_limit", "100",
+ "-log_level", "debug",
+ "-log_file", log_file,
+ "-retainOnlyTopFiles"])
+
+ # If there are asm diffs, and jit-analyze ran, we'll get a diff_summary.md file in the spmi_location directory.
+ # We make sure the file doesn't exist before we run diffs, so we don't need to worry about superpmi.py creating
+ # a unique, numbered file. If there are no diffs, we still want to create this file and indicate there were no diffs.
+
+ overall_md_summary_file_target = os.path.join(log_directory, "superpmi_diff_summary_{}_{}.md".format(platform_name, arch_name))
+ if os.path.isfile(overall_md_summary_file):
+ try:
+ print("Copying summary file {} -> {}".format(overall_md_summary_file, overall_md_summary_file_target))
+ shutil.copy2(overall_md_summary_file, overall_md_summary_file_target)
+ except PermissionError as pe_error:
+ print('Ignoring PermissionError: {0}'.format(pe_error))
+
+ copy_dasm_files(spmi_location, log_directory, "{}_{}".format(platform_name, arch_name))
+ else:
+ # Write a basic summary file. Ideally, we should not generate a summary.md file. However, currently I'm seeing
+ # errors where the Helix work item fails to upload this specified file if it doesn't exist. We should change the
+ # upload to be conditional, or otherwise not error.
+ with open(overall_md_summary_file_target, "a") as f:
+ f.write("""\
+No diffs found
+""")
+
+ if return_code != 0:
+ print("Failure in {}".format(log_file))
+ return 1
+
+ return 0
+
+
+if __name__ == "__main__":
+ args = parser.parse_args()
+ sys.exit(main(args))
diff --git a/src/coreclr/scripts/superpmi_asmdiffs_setup.py b/src/coreclr/scripts/superpmi_asmdiffs_setup.py
new file mode 100644
index 00000000000000..0342db7b56a0da
--- /dev/null
+++ b/src/coreclr/scripts/superpmi_asmdiffs_setup.py
@@ -0,0 +1,270 @@
+#!/usr/bin/env python3
+#
+# Licensed to the .NET Foundation under one or more agreements.
+# The .NET Foundation licenses this file to you under the MIT license.
+#
+# Title : superpmi_asmdiffs_setup.py
+#
+# Notes:
+#
+# Script to setup the directory structure required to perform SuperPMI asmdiffs in CI.
+# It creates `correlation_payload_directory` with `base` and `diff` directories
+# that contain clrjit*.dll. It figures out the baseline commit hash to use for
+# a particular GitHub pull request, and downloads the JIT rolling build for that
+# commit hash. It downloads the jitutils repo and builds the jit-analyze tool. It
+# downloads a version of `git` to be used by jit-analyze.
+#
+################################################################################
+################################################################################
+
+import argparse
+import logging
+import os
+
+from coreclr_arguments import *
+from jitutil import copy_directory, set_pipeline_variable, run_command, TempDir, download_files
+
+parser = argparse.ArgumentParser(description="description")
+
+parser.add_argument("-arch", help="Architecture")
+parser.add_argument("-source_directory", help="Path to the root directory of the dotnet/runtime source tree")
+parser.add_argument("-product_directory", help="Path to the directory containing built binaries (e.g., /artifacts/bin/coreclr/windows.x64.Checked)")
+
+is_windows = platform.system() == "Windows"
+
+
+def setup_args(args):
+ """ Setup the args for SuperPMI to use.
+
+ Args:
+ args (ArgParse): args parsed by arg parser
+
+ Returns:
+ args (CoreclrArguments)
+
+ """
+ coreclr_args = CoreclrArguments(args, require_built_core_root=False, require_built_product_dir=False,
+ require_built_test_dir=False, default_build_type="Checked")
+
+ coreclr_args.verify(args,
+ "arch",
+ lambda unused: True,
+ "Unable to set arch")
+
+ coreclr_args.verify(args,
+ "source_directory",
+ lambda source_directory: os.path.isdir(source_directory),
+ "source_directory doesn't exist")
+
+ coreclr_args.verify(args,
+ "product_directory",
+ lambda product_directory: os.path.isdir(product_directory),
+ "product_directory doesn't exist")
+
+ return coreclr_args
+
+
+def match_jit_files(full_path):
+ """ Match all the JIT files that we want to copy and use.
+ Note that we currently only match Windows files, and not osx cross-compile files.
+ We also don't copy the "default" clrjit.dll, since we always use the fully specified
+ JITs, e.g., clrjit_win_x86_x86.dll.
+ """
+ file_name = os.path.basename(full_path)
+
+ if file_name.startswith("clrjit_") and file_name.endswith(".dll") and file_name.find("osx") == -1:
+ return True
+
+ return False
+
+
+def match_superpmi_tool_files(full_path):
+ """ Match all the SuperPMI tool files that we want to copy and use.
+ Note that we currently only match Windows files.
+ """
+ file_name = os.path.basename(full_path)
+
+ if file_name == "superpmi.exe" or file_name == "mcs.exe":
+ return True
+
+ return False
+
+
+def main(main_args):
+ """ Prepare the Helix data for SuperPMI asmdiffs Azure DevOps pipeline.
+
+ The Helix correlation payload directory is created and populated as follows:
+
+ \payload -- the correlation payload directory
+ -- contains the *.py scripts from \src\coreclr\scripts
+ -- contains superpmi.exe, mcs.exe from the target-specific build
+ \payload\base
+ -- contains the baseline JITs
+ \payload\diff
+ -- contains the diff JITs
+ \payload\jit-analyze
+ -- contains the self-contained jit-analyze build (from dotnet/jitutils)
+ \payload\git
+ -- contains a Portable ("xcopy installable") `git` tool, downloaded from:
+ https://netcorenativeassets.blob.core.windows.net/resource-packages/external/windows/git/Git-2.32.0-64-bit.zip
+ This is needed by jit-analyze to do `git diff` on the generated asm. The `\payload\git\cmd`
+ directory is added to the PATH.
+ NOTE: this only runs on Windows.
+
+ Then, AzDO pipeline variables are set.
+
+ Args:
+ main_args ([type]): Arguments to the script
+
+ Returns:
+ 0 on success, otherwise a failure code
+ """
+
+ # Set up logging.
+ logger = logging.getLogger()
+ logger.setLevel(logging.INFO)
+ stream_handler = logging.StreamHandler(sys.stdout)
+ stream_handler.setLevel(logging.INFO)
+ logger.addHandler(stream_handler)
+
+ coreclr_args = setup_args(main_args)
+
+ arch = coreclr_args.arch
+ source_directory = coreclr_args.source_directory
+ product_directory = coreclr_args.product_directory
+
+ python_path = sys.executable
+
+ # CorrelationPayload directories
+ correlation_payload_directory = os.path.join(source_directory, "payload")
+ superpmi_scripts_directory = os.path.join(source_directory, 'src', 'coreclr', 'scripts')
+ base_jit_directory = os.path.join(correlation_payload_directory, "base")
+ diff_jit_directory = os.path.join(correlation_payload_directory, "diff")
+ jit_analyze_build_directory = os.path.join(correlation_payload_directory, "jit-analyze")
+ git_directory = os.path.join(correlation_payload_directory, "git")
+
+ ######## Get the portable `git` package
+
+ git_url = "https://netcorenativeassets.blob.core.windows.net/resource-packages/external/windows/git/Git-2.32.0-64-bit.zip"
+
+ print('Downloading {} -> {}'.format(git_url, git_directory))
+
+ urls = [ git_url ]
+ # There are too many files to be verbose in the download and copy.
+ download_files(urls, git_directory, verbose=False, display_progress=False)
+ git_exe_tool = os.path.join(git_directory, "cmd", "git.exe")
+ if not os.path.isfile(git_exe_tool):
+ print('Error: `git` not found at {}'.format(git_exe_tool))
+ return 1
+
+ ######## Get SuperPMI python scripts
+
+ # Copy *.py to CorrelationPayload
+ print('Copying {} -> {}'.format(superpmi_scripts_directory, correlation_payload_directory))
+ copy_directory(superpmi_scripts_directory, correlation_payload_directory, verbose_copy=True,
+ match_func=lambda path: any(path.endswith(extension) for extension in [".py"]))
+
+ ######## Get baseline JIT
+
+ # Figure out which baseline JIT to use, and download it.
+ if not os.path.exists(base_jit_directory):
+ os.makedirs(base_jit_directory)
+
+ print("Fetching history of `main` branch so we can find the baseline JIT")
+ run_command(["git", "fetch", "--depth=500", "origin", "main"], source_directory, _exit_on_fail=True)
+
+ # Note: we only support downloading Windows versions of the JIT currently. To support downloading
+ # non-Windows JITs on a Windows machine, pass `-host_os ` to jitrollingbuild.py.
+ print("Running jitrollingbuild.py download to get baseline JIT")
+ jit_rolling_build_script = os.path.join(superpmi_scripts_directory, "jitrollingbuild.py")
+ _, _, return_code = run_command([
+ python_path,
+ jit_rolling_build_script,
+ "download",
+ "-arch", arch,
+ "-target_dir", base_jit_directory],
+ source_directory)
+ if return_code != 0:
+ print('{} failed with {}'.format(jit_rolling_build_script, return_code))
+ return return_code
+
+ ######## Get diff JIT
+
+ print('Copying diff binaries {} -> {}'.format(product_directory, diff_jit_directory))
+ copy_directory(product_directory, diff_jit_directory, verbose_copy=True, match_func=match_jit_files)
+
+ ######## Get SuperPMI tools
+
+ # Put the SuperPMI tools directly in the root of the correlation payload directory.
+ print('Copying SuperPMI tools {} -> {}'.format(product_directory, correlation_payload_directory))
+ copy_directory(product_directory, correlation_payload_directory, verbose_copy=True, match_func=match_superpmi_tool_files)
+
+ ######## Clone and build jitutils: we only need jit-analyze
+
+ try:
+ with TempDir() as jitutils_directory:
+ run_command(
+ ["git", "clone", "--quiet", "--depth", "1", "https://github.com/dotnet/jitutils", jitutils_directory])
+
+ # Make sure ".dotnet" directory exists, by running the script at least once
+ dotnet_script_name = "dotnet.cmd" if is_windows else "dotnet.sh"
+ dotnet_script_path = os.path.join(source_directory, dotnet_script_name)
+ run_command([dotnet_script_path, "--info"], jitutils_directory)
+
+ # Build jit-analyze only, and build it as a self-contained app (not framework-dependent).
+ # What target RID are we building? It depends on where we're going to run this code.
+ # The RID catalog is here: https://docs.microsoft.com/en-us/dotnet/core/rid-catalog.
+ # Windows x64 => win-x64
+ # Windows x86 => win-x86
+ # Windows arm32 => win-arm
+ # Windows arm64 => win-arm64
+ # Linux x64 => linux-x64
+ # Linux arm32 => linux-arm
+ # Linux arm64 => linux-arm64
+ # macOS x64 => osx-x64
+
+ # NOTE: we currently only support running on Windows x86/x64 (we don't pass the target OS)
+ RID = None
+ if arch == "x86":
+ RID = "win-x86"
+ if arch == "x64":
+ RID = "win-x64"
+
+ # Set dotnet path to run build
+ os.environ["PATH"] = os.path.join(source_directory, ".dotnet") + os.pathsep + os.environ["PATH"]
+
+ run_command([
+ "dotnet",
+ "publish",
+ "-c", "Release",
+ "--runtime", RID,
+ "--self-contained",
+ "--output", jit_analyze_build_directory,
+ os.path.join(jitutils_directory, "src", "jit-analyze", "jit-analyze.csproj")],
+ jitutils_directory)
+ except PermissionError as pe_error:
+ # Details: https://bugs.python.org/issue26660
+ print('Ignoring PermissionError: {0}'.format(pe_error))
+
+ jit_analyze_tool = os.path.join(jit_analyze_build_directory, "jit-analyze.exe")
+ if not os.path.isfile(jit_analyze_tool):
+ print('Error: {} not found'.format(jit_analyze_tool))
+ return 1
+
+ ######## Set pipeline variables
+
+ helix_source_prefix = "official"
+ creator = ""
+
+ print('Setting pipeline variables:')
+ set_pipeline_variable("CorrelationPayloadDirectory", correlation_payload_directory)
+ set_pipeline_variable("Architecture", arch)
+ set_pipeline_variable("Creator", creator)
+ set_pipeline_variable("HelixSourcePrefix", helix_source_prefix)
+
+ return 0
+
+
+if __name__ == "__main__":
+ args = parser.parse_args()
+ sys.exit(main(args))
diff --git a/src/coreclr/scripts/superpmi_asmdiffs_summarize.py b/src/coreclr/scripts/superpmi_asmdiffs_summarize.py
new file mode 100644
index 00000000000000..9601d91e56b60b
--- /dev/null
+++ b/src/coreclr/scripts/superpmi_asmdiffs_summarize.py
@@ -0,0 +1,176 @@
+#!/usr/bin/env python3
+#
+## Licensed to the .NET Foundation under one or more agreements.
+## The .NET Foundation licenses this file to you under the MIT license.
+#
+##
+# Title: superpmi_asmdiffs_summarize.py
+#
+# Notes:
+#
+# Script to summarize issues found from all partitions and print them on console.
+#
+################################################################################
+################################################################################
+
+import argparse
+import os
+import re
+from coreclr_arguments import *
+
+parser = argparse.ArgumentParser(description="description")
+
+parser.add_argument("-diff_summary_dir", help="Path to diff summary directory")
+parser.add_argument("-arch", help="Architecture")
+
+def setup_args(args):
+ """ Setup the args.
+
+ Args:
+ args (ArgParse): args parsed by arg parser
+
+ Returns:
+ args (CoreclrArguments)
+
+ """
+ coreclr_args = CoreclrArguments(args, require_built_core_root=False, require_built_product_dir=False,
+ require_built_test_dir=False, default_build_type="Checked")
+
+ coreclr_args.verify(args,
+ "diff_summary_dir",
+ lambda diff_summary_dir: os.path.isdir(diff_summary_dir),
+ "diff_summary_dir doesn't exist")
+
+ coreclr_args.verify(args,
+ "arch",
+ lambda unused: True,
+ "Unable to set arch")
+
+ return coreclr_args
+
+
+def append_diff_file(f, arch, file_name, full_file_path):
+ """ Append a single summary file to the consolidated diff file.
+
+ Args:
+ f : File we are appending to
+ arch (string): architecture we ran on
+ file_name (string): base file name of file to append (not including path components)
+ full_file_path (string): full path to file to append
+
+ Returns:
+ True if diffs were found in the file, False otherwise
+ """
+
+ diffs_found = False
+ print("Appending {}".format(full_file_path))
+
+ # What platform is this file summarizing? We parse the filename itself, which is of the form:
+ # superpmi_diff_summary__.md
+
+ diff_os = "unknown"
+ diff_arch = "unknown"
+ match_obj = re.search(r'^superpmi_diff_summary_(.*)_(.*).md', file_name)
+ if match_obj is not None:
+ diff_os = match_obj.group(1)
+ diff_arch = match_obj.group(2)
+
+ with open(full_file_path, "r") as current_superpmi_md:
+ contents = current_superpmi_md.read()
+
+ # Were there actually any diffs? We currently look to see if the file contains the text "No diffs found",
+ # inserted by `superpmi_asmdiffs.py`, instead of just not having a diff summary .md file.
+ # (A missing file has the same effect.)
+ match_obj = re.search(r'^No diffs found', contents)
+ if match_obj is not None:
+ # There were no diffs in this file; don't add it to the result
+ pass
+ else:
+ diffs_found = True
+ # Write a header for this summary, and create a ... disclosure
+ # section around the file.
+ f.write("""\
+
+## {0} {1}
+
+
+
+{0} {1} details
+
+Summary file: `{2}`
+
+To reproduce these diffs on Windows {3}:
+```
+superpmi.py asmdiffs -target_os {0} -target_arch {1} -arch {3}
+```
+
+""".format(diff_os, diff_arch, file_name, arch))
+
+ # Now write the contents
+ f.write(contents)
+
+ # Write the footer (close the section)
+ f.write("""\
+
+
+
+""")
+
+ return diffs_found
+
+
+def main(main_args):
+ """Main entrypoint
+
+ Args:
+ main_args ([type]): Arguments to the script
+ """
+
+ coreclr_args = setup_args(main_args)
+
+ diff_summary_dir = coreclr_args.diff_summary_dir
+ arch = coreclr_args.arch
+
+ # Consolidate all superpmi_diff_summary_*.md in overall_diff_summary__.md
+ # (Don't name it "superpmi_xxx.md" or we might consolidate it into itself.)
+ # If there are no summary files found, add a "No diffs found" text to be explicit about that.
+ #
+ # Note that we currently do this summarizing in an architecture-specific job. That means that diffs run
+ # in a Windows x64 job and those run in a Windows x86 job will be summarized in two separate files.
+ # We should create a job that depends on all the diff jobs, downloads all the .md file artifacts,
+ # and consolidates everything together in one file.
+
+ any_diffs_found = False
+
+ final_md_path = os.path.join(diff_summary_dir, "overall_diff_summary_windows_{}.md".format(arch))
+ print("Consolidating final {}".format(final_md_path))
+ with open(final_md_path, "a") as f:
+
+ f.write("""\
+# ASM diffs generated on Windows {}
+""".format(arch))
+
+ for dirpath, _, files in os.walk(diff_summary_dir):
+ for file_name in files:
+ if file_name.startswith("superpmi_") and file_name.endswith(".md"):
+ full_file_path = os.path.join(dirpath, file_name)
+ if append_diff_file(f, arch, file_name, full_file_path):
+ any_diffs_found = True
+
+ if not any_diffs_found:
+ f.write("""\
+
+No diffs found
+""")
+
+ print("##vso[task.uploadsummary]{}".format(final_md_path))
+
+ with open(final_md_path, "r") as f:
+ print(f.read())
+
+ return 0
+
+
+if __name__ == "__main__":
+ args = parser.parse_args()
+ sys.exit(main(args))
diff --git a/src/coreclr/scripts/superpmi_aspnet.py b/src/coreclr/scripts/superpmi_aspnet.py
index 2a0225d1e43bcc..f2e01ad6e2ae5e 100644
--- a/src/coreclr/scripts/superpmi_aspnet.py
+++ b/src/coreclr/scripts/superpmi_aspnet.py
@@ -20,7 +20,7 @@
from os import path
from coreclr_arguments import *
from superpmi import TempDir, determine_mcs_tool_path, determine_superpmi_tool_path, is_nonzero_length_file
-from azdo_pipelines_util import run_command
+from jitutil import run_command
# Start of parser object creation.
is_windows = platform.system() == "Windows"
diff --git a/src/coreclr/scripts/superpmi_benchmarks.py b/src/coreclr/scripts/superpmi_benchmarks.py
index 28ebf50c92cd82..1c15b9ae803bf9 100644
--- a/src/coreclr/scripts/superpmi_benchmarks.py
+++ b/src/coreclr/scripts/superpmi_benchmarks.py
@@ -20,7 +20,7 @@
from os.path import isfile
from shutil import copyfile
from coreclr_arguments import *
-from azdo_pipelines_util import run_command, ChangeDir, TempDir
+from jitutil import run_command, ChangeDir, TempDir
# Start of parser object creation.
is_windows = platform.system() == "Windows"
diff --git a/src/coreclr/scripts/superpmi_collect_setup.py b/src/coreclr/scripts/superpmi_collect_setup.py
index a6a9b84e7e8ba8..ce102ea0910261 100644
--- a/src/coreclr/scripts/superpmi_collect_setup.py
+++ b/src/coreclr/scripts/superpmi_collect_setup.py
@@ -37,7 +37,7 @@
import stat
from coreclr_arguments import *
-from azdo_pipelines_util import run_command, copy_directory, copy_files, set_pipeline_variable, ChangeDir, TempDir
+from jitutil import run_command, copy_directory, copy_files, set_pipeline_variable, ChangeDir, TempDir
# Start of parser object creation.
@@ -398,7 +398,7 @@ def main(main_args):
# create superpmi directory
print('Copying {} -> {}'.format(superpmi_src_directory, superpmi_dst_directory))
- copy_directory(superpmi_src_directory, superpmi_dst_directory, match_func=lambda path: any(path.endswith(extension) for extension in [".py"]))
+ copy_directory(superpmi_src_directory, superpmi_dst_directory, verbose_output=True, match_func=lambda path: any(path.endswith(extension) for extension in [".py"]))
if is_windows:
acceptable_copy = lambda path: any(path.endswith(extension) for extension in [".py", ".dll", ".exe", ".json"])
@@ -407,7 +407,7 @@ def main(main_args):
acceptable_copy = lambda path: (os.path.basename(path).find(".") == -1) or any(path.endswith(extension) for extension in [".py", ".dll", ".so", ".json"])
print('Copying {} -> {}'.format(coreclr_args.core_root_directory, superpmi_dst_directory))
- copy_directory(coreclr_args.core_root_directory, superpmi_dst_directory, match_func=acceptable_copy)
+ copy_directory(coreclr_args.core_root_directory, superpmi_dst_directory, verbose_output=True, match_func=acceptable_copy)
# Copy all the test files to CORE_ROOT
# The reason is there are lot of dependencies with *.Tests.dll and to ensure we do not get
@@ -448,7 +448,7 @@ def make_readable(folder_name):
run_command(["ls", "-l", folder_name])
make_readable(coreclr_args.input_directory)
- copy_directory(coreclr_args.input_directory, superpmi_dst_directory, match_func=acceptable_copy)
+ copy_directory(coreclr_args.input_directory, superpmi_dst_directory, verbose_output=True, match_func=acceptable_copy)
# Workitem directories
workitem_directory = os.path.join(source_directory, "workitem")
diff --git a/src/coreclr/scripts/superpmi_replay.py b/src/coreclr/scripts/superpmi_replay.py
index a00b80753b2a98..198b0f28ff9403 100644
--- a/src/coreclr/scripts/superpmi_replay.py
+++ b/src/coreclr/scripts/superpmi_replay.py
@@ -15,7 +15,7 @@
import argparse
import os
from coreclr_arguments import *
-from azdo_pipelines_util import run_command
+from jitutil import run_command
parser = argparse.ArgumentParser(description="description")
diff --git a/src/coreclr/scripts/superpmi_replay_setup.py b/src/coreclr/scripts/superpmi_replay_setup.py
index 73c89eb3335568..b7717da8efaf24 100644
--- a/src/coreclr/scripts/superpmi_replay_setup.py
+++ b/src/coreclr/scripts/superpmi_replay_setup.py
@@ -17,7 +17,7 @@
import os
from coreclr_arguments import *
-from azdo_pipelines_util import copy_directory, copy_files, set_pipeline_variable
+from jitutil import copy_directory, copy_files, set_pipeline_variable
parser = argparse.ArgumentParser(description="description")
@@ -90,12 +90,12 @@ def main(main_args):
# Copy *.py to CorrelationPayload
print('Copying {} -> {}'.format(superpmi_src_directory, correlation_payload_directory))
- copy_directory(superpmi_src_directory, correlation_payload_directory,
+ copy_directory(superpmi_src_directory, correlation_payload_directory, verbose_output=True,
match_func=lambda path: any(path.endswith(extension) for extension in [".py"]))
# Copy clrjit*_arch.dll binaries to CorrelationPayload
print('Copying binaries {} -> {}'.format(product_directory, correlation_payload_directory))
- copy_directory(product_directory, correlation_payload_directory, match_func=match_correlation_files)
+ copy_directory(product_directory, correlation_payload_directory, verbose_output=True, match_func=match_correlation_files)
# Set variables
print('Setting pipeline variables:')
diff --git a/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.cs b/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.cs
index 1dc0fd8d91d999..fa0828fc034e86 100644
--- a/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.cs
+++ b/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.cs
@@ -193,6 +193,12 @@ private EcmaModule AddModule(string filePath, string expectedSimpleName, bool us
if (oldModuleData == null)
{
peReader = OpenPEFile(filePath, out mappedViewAccessor);
+
+#if !READYTORUN
+ if (peReader.HasMetadata && (peReader.PEHeaders.CorHeader.Flags & (CorFlags.ILLibrary | CorFlags.ILOnly)) == 0)
+ throw new NotSupportedException($"Error: C++/CLI is not supported: '{filePath}'");
+#endif
+
pdbReader = PortablePdbSymbolReader.TryOpenEmbedded(peReader, GetMetadataStringDecoder()) ?? OpenAssociatedSymbolFile(filePath, peReader);
}
else
diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs
index 5b50701a2b97eb..eda9378acc36d4 100644
--- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs
+++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs
@@ -30,7 +30,7 @@ public enum RelocType
// Relocations for R2R image production
//
IMAGE_REL_SYMBOL_SIZE = 0x1000, // The size of data in the image represented by the target symbol node
- IMAGE_REL_FILE_ABSOLUTE = 0x1001, // 32 bit offset from begining of image
+ IMAGE_REL_FILE_ABSOLUTE = 0x1001, // 32 bit offset from beginning of image
}
public struct Relocation
diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_X64/X64Emitter.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_X64/X64Emitter.cs
index 455f6b446795b3..17135f6c4e405a 100644
--- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_X64/X64Emitter.cs
+++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_X64/X64Emitter.cs
@@ -174,6 +174,12 @@ public void EmitRETIfEqual()
Builder.EmitByte(0xC3);
}
+ public void EmitCompareToZero(Register reg)
+ {
+ AddrMode rexAddrMode = new AddrMode(Register.RegDirect | reg, null, 0, 0, AddrModeSize.Int64);
+ EmitIndirInstructionSize(0x84, reg, ref rexAddrMode);
+ }
+
public void EmitZeroReg(Register reg)
{
// High 32 bits get cleared automatically when using 32bit registers
diff --git a/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs b/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs
index aba41341a27095..f7bd609553fc87 100644
--- a/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs
+++ b/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs
@@ -201,5 +201,16 @@ protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType
return impl;
}
+
+#if !READYTORUN
+ ///
+ /// Gets a value indicating whether it might be possible to obtain a constructed type data structure for the given type.
+ ///
+ ///
+ /// This is a bit of a hack, but devirtualization manager has a global view of all allocated types
+ /// so it can answer this question.
+ ///
+ public virtual bool CanConstructType(TypeDesc type) => true;
+#endif
}
}
diff --git a/src/coreclr/tools/Common/Compiler/DisplayNameHelpers.cs b/src/coreclr/tools/Common/Compiler/DisplayNameHelpers.cs
index 674d54a95e0597..0ee508d160c652 100644
--- a/src/coreclr/tools/Common/Compiler/DisplayNameHelpers.cs
+++ b/src/coreclr/tools/Common/Compiler/DisplayNameHelpers.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
using System.Text;
using Internal.TypeSystem;
@@ -11,6 +12,21 @@ namespace ILCompiler
{
internal static class DisplayNameHelpers
{
+ public static string GetDisplayName(this TypeSystemEntity entity)
+ {
+ return entity switch
+ {
+ MethodDesc method => method.GetDisplayName(),
+ FieldDesc field => field.GetDisplayName(),
+ TypeDesc type => type.GetDisplayName(),
+#if !READYTORUN
+ PropertyPseudoDesc property => property.GetDisplayName(),
+ EventPseudoDesc @event => @event.GetDisplayName(),
+#endif
+ _ => throw new InvalidOperationException(),
+ };
+ }
+
public static string GetDisplayName(this MethodDesc method)
{
var sb = new StringBuilder();
@@ -66,6 +82,13 @@ public static string GetDisplayName(this PropertyPseudoDesc property)
.Append('.')
.Append(property.Name).ToString();
}
+
+ public static string GetDisplayName(this EventPseudoDesc @event)
+ {
+ return new StringBuilder(@event.OwningType.GetDisplayName())
+ .Append('.')
+ .Append(@event.Name).ToString();
+ }
#endif
public static string GetDisplayName(this TypeDesc type)
diff --git a/src/coreclr/tools/Common/Compiler/Logger.cs b/src/coreclr/tools/Common/Compiler/Logger.cs
index b75435c4df0f84..af84d21ab3b4bb 100644
--- a/src/coreclr/tools/Common/Compiler/Logger.cs
+++ b/src/coreclr/tools/Common/Compiler/Logger.cs
@@ -94,13 +94,23 @@ public void LogWarning(string text, int code, string origin, string subcategory
internal bool IsWarningSuppressed(int code, MessageOrigin origin)
{
+ // This is causing too much noise
+ // https://github.com/dotnet/runtimelab/issues/1591
+ if (code == 2110 || code == 2111 || code == 2113 || code == 2115)
+ return true;
+
if (_suppressedWarnings.Contains(code))
return true;
IEnumerable> suppressions = null;
// TODO: Suppressions with different scopes
-
+
+ if (origin.MemberDefinition is TypeDesc type)
+ {
+ var ecmaType = type.GetTypeDefinition() as EcmaType;
+ suppressions = ecmaType?.GetDecodedCustomAttributes("System.Diagnostics.CodeAnalysis", "UnconditionalSuppressMessageAttribute");
+ }
if (origin.MemberDefinition is MethodDesc method)
{
diff --git a/src/coreclr/tools/Common/Compiler/Logging/ReferenceSource/MessageContainer.cs b/src/coreclr/tools/Common/Compiler/Logging/ReferenceSource/MessageContainer.cs
index 7977343d17d5a3..f0a4afd09b1875 100644
--- a/src/coreclr/tools/Common/Compiler/Logging/ReferenceSource/MessageContainer.cs
+++ b/src/coreclr/tools/Common/Compiler/Logging/ReferenceSource/MessageContainer.cs
@@ -1,8 +1,9 @@
-// Licensed to the .NET Foundation under one or more agreements.
+// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Text;
using Mono.Cecil;
@@ -40,7 +41,7 @@ namespace Mono.Linker
/// Create an error message.
///
/// Humanly readable message describing the error
- /// Unique error ID. Please see https://github.com/mono/linker/blob/main/docs/error-codes.md
+ /// Unique error ID. Please see https://github.com/dotnet/linker/blob/main/docs/error-codes.md
/// for the list of errors and possibly add a new one
/// Optionally, further categorize this error
/// Filename, line, and column where the error was found
@@ -80,7 +81,7 @@ public static MessageContainer CreateCustomErrorMessage (string text, int code,
///
/// Context with the relevant warning suppression info.
/// Humanly readable message describing the warning
- /// Unique warning ID. Please see https://github.com/mono/linker/blob/main/docs/error-codes.md
+ /// Unique warning ID. Please see https://github.com/dotnet/linker/blob/main/docs/error-codes.md
/// for the list of warnings and possibly add a new one
/// /// Filename or member where the warning is coming from
/// Optionally, further categorize this warning
@@ -140,14 +141,31 @@ private static MessageContainer CreateWarningMessageContainer (LinkContext conte
return new MessageContainer (MessageCategory.Warning, text, code, subcategory, origin);
}
+ public bool IsWarningMessage ([NotNullWhen (true)] out int? code)
+ {
+ code = null;
+
+ if (Category is MessageCategory.Warning or MessageCategory.WarningAsError) {
+ // Warning messages always have a code.
+ code = Code!;
+ return true;
+ }
+
+ return false;
+ }
+
static bool TryLogSingleWarning (LinkContext context, int code, MessageOrigin origin, string subcategory)
{
if (subcategory != MessageSubCategory.TrimAnalysis)
return false;
- Debug.Assert (origin.MemberDefinition != null);
- var declaringType = origin.MemberDefinition?.DeclaringType ?? (origin.MemberDefinition as TypeDefinition);
- var assembly = declaringType.Module.Assembly;
+ Debug.Assert (origin.Provider != null);
+ var assembly = origin.Provider switch {
+ AssemblyDefinition asm => asm,
+ TypeDefinition type => type.Module.Assembly,
+ IMemberDefinition member => member.DeclaringType.Module.Assembly,
+ _ => throw new NotSupportedException ()
+ };
Debug.Assert (assembly != null);
if (assembly == null)
@@ -228,17 +246,22 @@ public string ToMSBuildString ()
sb.Append (" ")
.Append (cat)
.Append (" IL")
- .Append (Code.Value.ToString ("D4"))
+ // Warning and error messages always have a code.
+ .Append (Code!.Value.ToString ("D4"))
.Append (": ");
} else {
sb.Append (" ");
}
- if (Origin?.MemberDefinition != null) {
- if (Origin?.MemberDefinition is MethodDefinition method)
+ if (Origin?.Provider != null) {
+ if (Origin?.Provider is MethodDefinition method)
sb.Append (method.GetDisplayName ());
+ else if (Origin?.Provider is IMemberDefinition member)
+ sb.Append (member.FullName);
+ else if (Origin?.Provider is AssemblyDefinition assembly)
+ sb.Append (assembly.Name.Name);
else
- sb.Append (Origin?.MemberDefinition.FullName);
+ throw new NotSupportedException ();
sb.Append (": ");
}
diff --git a/src/coreclr/tools/Common/Compiler/Logging/ReferenceSource/MessageOrigin.cs b/src/coreclr/tools/Common/Compiler/Logging/ReferenceSource/MessageOrigin.cs
index 364216788ba61f..84357b26ae221d 100644
--- a/src/coreclr/tools/Common/Compiler/Logging/ReferenceSource/MessageOrigin.cs
+++ b/src/coreclr/tools/Common/Compiler/Logging/ReferenceSource/MessageOrigin.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Diagnostics;
using System.Linq;
using System.Text;
using Mono.Cecil;
@@ -13,10 +14,14 @@ namespace Mono.Linker
{
#nullable enable
public string? FileName { get; }
- public IMemberDefinition? MemberDefinition { get; }
-
- readonly IMemberDefinition _suppressionContextMember;
- public IMemberDefinition? SuppressionContextMember { get => _suppressionContextMember ?? MemberDefinition; }
+ public ICustomAttributeProvider? Provider { get; }
+ readonly ICustomAttributeProvider _suppressionContextMember;
+ public ICustomAttributeProvider? SuppressionContextMember {
+ get {
+ Debug.Assert (_suppressionContextMember == null || _suppressionContextMember is IMemberDefinition || _suppressionContextMember is AssemblyDefinition);
+ return _suppressionContextMember ?? Provider;
+ }
+ }
#nullable disable
public int SourceLine { get; }
public int SourceColumn { get; }
@@ -24,8 +29,13 @@ namespace Mono.Linker
const int HiddenLineNumber = 0xfeefee;
- public MessageOrigin (IMemberDefinition memberDefinition)
- : this (memberDefinition, null)
+ public MessageOrigin (IMemberDefinition memberDefinition, int? ilOffset = null)
+ : this (memberDefinition as ICustomAttributeProvider, ilOffset)
+ {
+ }
+
+ public MessageOrigin (ICustomAttributeProvider provider)
+ : this (provider, null)
{
}
@@ -34,31 +44,43 @@ public MessageOrigin (string fileName, int sourceLine = 0, int sourceColumn = 0)
FileName = fileName;
SourceLine = sourceLine;
SourceColumn = sourceColumn;
- MemberDefinition = null;
+ Provider = null;
_suppressionContextMember = null;
ILOffset = null;
}
- public MessageOrigin (IMemberDefinition memberDefinition, int? ilOffset)
- : this (memberDefinition, ilOffset, null)
+ public MessageOrigin (ICustomAttributeProvider provider, int? ilOffset)
+ : this (provider, ilOffset, null)
{
}
- public MessageOrigin (IMemberDefinition memberDefinition, int? ilOffset, IMemberDefinition suppressionContextMember)
+ public MessageOrigin (ICustomAttributeProvider provider, int? ilOffset, ICustomAttributeProvider suppressionContextMember)
{
+ Debug.Assert (provider == null || provider is IMemberDefinition || provider is AssemblyDefinition);
+ Debug.Assert (suppressionContextMember == null || suppressionContextMember is IMemberDefinition || provider is AssemblyDefinition);
FileName = null;
- MemberDefinition = memberDefinition;
+ Provider = provider;
_suppressionContextMember = suppressionContextMember;
SourceLine = 0;
SourceColumn = 0;
ILOffset = ilOffset;
}
+ public MessageOrigin (MessageOrigin other, IMemberDefinition suppressionContextMember)
+ {
+ FileName = other.FileName;
+ Provider = other.Provider;
+ _suppressionContextMember = suppressionContextMember;
+ SourceLine = other.SourceLine;
+ SourceColumn = other.SourceColumn;
+ ILOffset = other.ILOffset;
+ }
+
public override string ToString ()
{
int sourceLine = SourceLine, sourceColumn = SourceColumn;
string fileName = FileName;
- if (MemberDefinition is MethodDefinition method &&
+ if (Provider is MethodDefinition method &&
method.DebugInformation.HasSequencePoints) {
var offset = ILOffset ?? 0;
SequencePoint correspondingSequencePoint = method.DebugInformation.SequencePoints
@@ -94,20 +116,24 @@ public override string ToString ()
}
public bool Equals (MessageOrigin other) =>
- (FileName, MemberDefinition, SourceLine, SourceColumn, ILOffset) == (other.FileName, other.MemberDefinition, other.SourceLine, other.SourceColumn, other.ILOffset);
+ (FileName, Provider, SourceLine, SourceColumn, ILOffset) == (other.FileName, other.Provider, other.SourceLine, other.SourceColumn, other.ILOffset);
public override bool Equals (object obj) => obj is MessageOrigin messageOrigin && Equals (messageOrigin);
- public override int GetHashCode () => (FileName, MemberDefinition, SourceLine, SourceColumn).GetHashCode ();
+ public override int GetHashCode () => (FileName, Provider, SourceLine, SourceColumn).GetHashCode ();
public static bool operator == (MessageOrigin lhs, MessageOrigin rhs) => lhs.Equals (rhs);
public static bool operator != (MessageOrigin lhs, MessageOrigin rhs) => !lhs.Equals (rhs);
public int CompareTo (MessageOrigin other)
{
- if (MemberDefinition != null && other.MemberDefinition != null) {
- TypeDefinition thisTypeDef = (MemberDefinition as TypeDefinition) ?? MemberDefinition.DeclaringType;
- TypeDefinition otherTypeDef = (other.MemberDefinition as TypeDefinition) ?? other.MemberDefinition.DeclaringType;
- int result = (thisTypeDef?.Module?.Assembly?.Name?.Name, thisTypeDef?.Name, MemberDefinition?.Name).CompareTo
- ((otherTypeDef?.Module?.Assembly?.Name?.Name, otherTypeDef?.Name, other.MemberDefinition?.Name));
+ if (Provider != null && other.Provider != null) {
+ var thisMember = Provider as IMemberDefinition;
+ var otherMember = other.Provider as IMemberDefinition;
+ TypeDefinition thisTypeDef = (Provider as TypeDefinition) ?? (Provider as IMemberDefinition)?.DeclaringType;
+ TypeDefinition otherTypeDef = (other.Provider as TypeDefinition) ?? (other.Provider as IMemberDefinition)?.DeclaringType;
+ var thisAssembly = thisTypeDef?.Module.Assembly ?? Provider as AssemblyDefinition;
+ var otherAssembly = otherTypeDef?.Module.Assembly ?? other.Provider as AssemblyDefinition;
+ int result = (thisAssembly.Name.Name, thisTypeDef?.Name, thisMember?.Name).CompareTo
+ ((otherAssembly.Name.Name, otherTypeDef?.Name, otherMember?.Name));
if (result != 0)
return result;
@@ -115,7 +141,7 @@ public int CompareTo (MessageOrigin other)
return ILOffset.Value.CompareTo (other.ILOffset);
return ILOffset == null ? (other.ILOffset == null ? 0 : 1) : -1;
- } else if (MemberDefinition == null && other.MemberDefinition == null) {
+ } else if (Provider == null && other.Provider == null) {
if (FileName != null && other.FileName != null) {
return string.Compare (FileName, other.FileName);
} else if (FileName == null && other.FileName == null) {
@@ -125,7 +151,7 @@ public int CompareTo (MessageOrigin other)
return (FileName == null) ? 1 : -1;
}
- return (MemberDefinition == null) ? 1 : -1;
+ return (Provider == null) ? 1 : -1;
}
}
}
diff --git a/src/coreclr/tools/Common/Compiler/Logging/ReferenceSource/README.md b/src/coreclr/tools/Common/Compiler/Logging/ReferenceSource/README.md
index b894c9dc1f1c81..883332600e88d0 100644
--- a/src/coreclr/tools/Common/Compiler/Logging/ReferenceSource/README.md
+++ b/src/coreclr/tools/Common/Compiler/Logging/ReferenceSource/README.md
@@ -1 +1 @@
-Sources from the mono/linker repo at commit 012efef292663aa38f9047896942cdcc8765b8e0.
\ No newline at end of file
+Sources from the dotnet/linker repo at commit c0567db0b9088e2ad4144cd0fe2a985611ec28f0.
diff --git a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs
index eee208cfb2de0f..434f9a9ee1c888 100644
--- a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs
+++ b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs
@@ -84,6 +84,7 @@ public enum ReadyToRunSectionType
ThreadStaticIndex = 210,
LoopHijackFlag = 211,
ImportAddressTables = 212,
+ ModuleInitializerList = 213,
// Sections 300 - 399 are reserved for RhFindBlob backwards compatibility
ReadonlyBlobRegionStart = 300,
diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs
index 5e5c89774b7ca2..8c552704c7080f 100644
--- a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs
+++ b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs
@@ -674,21 +674,6 @@ static uint _getClassAttribs(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLA
}
}
- [UnmanagedCallersOnly]
- static byte _isStructRequiringStackAllocRetBuf(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* cls)
- {
- var _this = GetThis(thisHandle);
- try
- {
- return _this.isStructRequiringStackAllocRetBuf(cls) ? (byte)1 : (byte)0;
- }
- catch (Exception ex)
- {
- *ppException = _this.AllocException(ex);
- return default;
- }
- }
-
[UnmanagedCallersOnly]
static CORINFO_MODULE_STRUCT_* _getClassModule(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* cls)
{
@@ -2581,7 +2566,7 @@ static byte _doesFieldBelongToClass(IntPtr thisHandle, IntPtr* ppException, CORI
static IntPtr GetUnmanagedCallbacks()
{
- void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 174);
+ void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 173);
callbacks[0] = (delegate* unmanaged)&_isJitIntrinsic;
callbacks[1] = (delegate* unmanaged)&_getMethodAttribs;
@@ -2628,135 +2613,134 @@ static IntPtr GetUnmanagedCallbacks()
callbacks[42] = (delegate* unmanaged)&_isValueClass;
callbacks[43] = (delegate* unmanaged)&_canInlineTypeCheck;
callbacks[44] = (delegate* unmanaged)&_getClassAttribs;
- callbacks[45] = (delegate* unmanaged)&_isStructRequiringStackAllocRetBuf;
- callbacks[46] = (delegate* unmanaged)&_getClassModule;
- callbacks[47] = (delegate* unmanaged)&_getModuleAssembly;
- callbacks[48] = (delegate* unmanaged)&_getAssemblyName;
- callbacks[49] = (delegate* unmanaged)&_LongLifetimeMalloc;
- callbacks[50] = (delegate* unmanaged)&_LongLifetimeFree;
- callbacks[51] = (delegate* unmanaged)&_getClassModuleIdForStatics;
- callbacks[52] = (delegate* unmanaged)&_getClassSize;
- callbacks[53] = (delegate* unmanaged)&_getHeapClassSize;
- callbacks[54] = (delegate* unmanaged)&_canAllocateOnStack;
- callbacks[55] = (delegate* unmanaged)&_getClassAlignmentRequirement;
- callbacks[56] = (delegate* unmanaged)&_getClassGClayout;
- callbacks[57] = (delegate* unmanaged)&_getClassNumInstanceFields;
- callbacks[58] = (delegate* unmanaged)&_getFieldInClass;
- callbacks[59] = (delegate* unmanaged)&_checkMethodModifier;
- callbacks[60] = (delegate* unmanaged)&_getNewHelper;
- callbacks[61] = (delegate* unmanaged)&_getNewArrHelper;
- callbacks[62] = (delegate* unmanaged)&_getCastingHelper;
- callbacks[63] = (delegate* unmanaged