├── .gitattributes ├── .gitignore ├── CODE-OF-CONDUCT.md ├── LICENSE.TXT ├── NuGet.Config ├── README.md ├── azure-pipelines.yml ├── bootstrap.cmd ├── bootstrap.sh ├── build-coredistools.cmd ├── build-coredistools.sh ├── build-tblgen.cmd ├── build-tblgen.sh ├── build.cmd ├── build.sh ├── coredistools.yml ├── doc ├── building-coredistools.md ├── cijobs.md ├── config.md ├── diffs.md ├── formatting.md └── tools.md ├── eng ├── build.yml └── download-checkout-llvm.yml ├── jitutils.sln ├── pack-coredistools.cmd ├── sample_config.json ├── src ├── AnalyzeAsm │ ├── AnalyzeAsm.csproj │ ├── OccurenceInfo.cs │ ├── Program.cs │ └── README.md ├── cijobs │ ├── CIClient.cs │ ├── CIJobsRootCommand.cs │ ├── Models.cs │ ├── Program.cs │ ├── README.md │ └── cijobs.csproj ├── coreclr_config_download │ ├── README.md │ └── coreclr_config_download.py ├── coredistools │ ├── .nuget │ │ └── Microsoft.NETCore.CoreDisTools.nuspec │ ├── CMakeLists.txt │ ├── coredistools.cpp │ ├── coredistools.exports │ └── coredistools.h ├── instructions-retired-explorer │ ├── README.md │ ├── instructions-retired-explorer.cs │ └── instructions-retired-explorer.csproj ├── jit-analyze │ ├── IEnumerableExtensions.cs │ ├── JitAnalyzeRootCommand.cs │ ├── MetricCollection.cs │ ├── Metrics.cs │ ├── Program.cs │ ├── README.md │ └── jit-analyze.csproj ├── jit-dasm-pmi │ ├── JitDasmPmiRootCommand.cs │ ├── Program.cs │ ├── README.md │ └── jit-dasm-pmi.csproj ├── jit-dasm │ ├── JitDasmRootCommand.cs │ ├── Program.cs │ ├── README.md │ └── jit-dasm.csproj ├── jit-decisions-analyze │ ├── README.md │ ├── jit-decisions-analyze.cs │ └── jit-decisions-analyze.csproj ├── jit-diff │ ├── README.md │ ├── diff.cs │ ├── install.cs │ ├── jit-diff.cs │ ├── jit-diff.csproj │ └── uninstall.cs ├── jit-format │ ├── README.md │ ├── jit-format.cs │ └── jit-format.csproj ├── jit-include.props ├── jit-pintool │ ├── README.md │ └── clrjit_inscount.cpp ├── jit-rl-cse │ ├── MLCSE.cs │ ├── MLCSE.csproj │ ├── MLCSECommands.cs │ ├── MarkovChain96689.png │ ├── MarkovChain96689PG100.png │ └── README.md ├── jit-tp-analyze │ ├── Program.cs │ ├── README.md │ └── jit-tp-analyze.csproj ├── mutate-test │ ├── MutateTestRootCommand.cs │ ├── Program.cs │ └── mutate-test.csproj ├── performance-explorer │ ├── README.md │ ├── benchmark-info.cs │ ├── benchmark-json.cs │ ├── cse-experiment.cs │ ├── hot-function.cs │ ├── performance-explorer.cs │ └── performance-explorer.csproj ├── pmi │ ├── JITDecisionEventListener.cs │ ├── PMIDriver.cs │ ├── pmi.cs │ ├── pmi.csproj │ └── readme.md ├── superpmi │ ├── superpmicollect.cs │ └── superpmicollect.csproj ├── target-framework.props └── util │ ├── CommandLineHelpers.cs │ └── util.cs └── test ├── jit-analyze ├── base │ ├── test1.dasm │ ├── test2.dasm │ └── test3.dasm ├── baseline1.out ├── baseline2.out ├── baseline3.out ├── diff │ ├── test1.dasm │ ├── test2.dasm │ └── test3.dasm └── runtest.sh ├── jit-dasm └── runtest.sh └── jit-format ├── .clang-format ├── runtest.cmd ├── test-fixed.cpp └── test.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behavior to automatically normalize line endings. 2 | * text=auto 3 | 4 | *.doc diff=astextplain 5 | *.DOC diff=astextplain 6 | *.docx diff=astextplain 7 | *.DOCX diff=astextplain 8 | *.dot diff=astextplain 9 | *.DOT diff=astextplain 10 | *.pdf diff=astextplain 11 | *.PDF diff=astextplain 12 | *.rtf diff=astextplain 13 | *.RTF diff=astextplain 14 | 15 | *.jpg binary 16 | *.png binary 17 | *.gif binary 18 | 19 | # Force bash scripts to always use lf line endings so that if a repo is accessed 20 | # in Unix via a file share from Windows, the scripts will work. 21 | *.in text eol=lf 22 | *.sh text eol=lf 23 | 24 | # Likewise, force cmd and batch scripts to always use crlf 25 | *.cmd text eol=crlf 26 | *.bat text eol=crlf 27 | 28 | *.cs text=auto diff=csharp 29 | *.vb text=auto 30 | *.resx text=auto 31 | *.c text=auto 32 | *.cpp text=auto 33 | *.cxx text=auto 34 | *.h text=auto 35 | *.hxx text=auto 36 | *.py text=auto 37 | *.rb text=auto 38 | *.java text=auto 39 | *.html text=auto 40 | *.htm text=auto 41 | *.css text=auto 42 | *.scss text=auto 43 | *.sass text=auto 44 | *.less text=auto 45 | *.js text=auto 46 | *.lisp text=auto 47 | *.clj text=auto 48 | *.sql text=auto 49 | *.php text=auto 50 | *.lua text=auto 51 | *.m text=auto 52 | *.asm text=auto 53 | *.erl text=auto 54 | *.fs text=auto 55 | *.fsx text=auto 56 | *.hs text=auto 57 | 58 | *.csproj text=auto 59 | *.vbproj text=auto 60 | *.fsproj text=auto 61 | *.dbproj text=auto 62 | *.sln text=auto eol=crlf 63 | 64 | # Set linguist language for .h files explicitly based on 65 | # https://github.com/github/linguist/issues/1626#issuecomment-401442069 66 | # this only affects the repo's language statistics 67 | *.h linguist-language=C 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Hides dotnet cli build artifacts as well as project system cookies. 2 | ## 3 | 4 | # ignore default binary/obj locations 5 | **/bin 6 | **/obj 7 | .vscode 8 | 9 | # project system lock files 10 | project.lock.json 11 | 12 | # vs files 13 | .vs 14 | browse.VC.db 15 | 16 | # vi 17 | 18 | *~ 19 | -------------------------------------------------------------------------------- /CODE-OF-CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | This project has adopted the code of conduct defined by the Contributor Covenant 4 | to clarify expected behavior in our community. 5 | 6 | For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct). 7 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) .NET Foundation and Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dotnet JIT code gen utilities - jitutils 2 | 3 | This repo holds a collection of utilities used by RyuJIT developers to 4 | automate tasks when working on CoreCLR. 5 | 6 | ## Summary 7 | 8 | Current tools include: 9 | 10 | 1. [Assembly diffs](doc/diffs.md): jit-diff, jit-dasm, jit-dasm-pmi, jit-analyze, jit-tp-analyze. 11 | 2. [CI jobs information](doc/cijobs.md): cijobs. 12 | 3. [JIT source code formatting](doc/formatting.md): jit-format. 13 | 4. [General tools](doc/tools.md): pmi 14 | 5. [Experimental tools](src/performance-explorer/README.md): performance-explorer 15 | 6. [BenchmarkDotNet Analysis](src/instructions-retired-explorer/README.md) 16 | 17 | 18 | ## Getting started 19 | 20 | 1. Clone the jitutils repo: 21 | ``` 22 | git clone https://github.com/dotnet/jitutils 23 | ``` 24 | 25 | 2. Install a recent .NET Core SDK (including the `dotnet` command-line interface, or CLI) from [here](https://dot.net). 26 | 27 | 3. Build the tools: 28 | ``` 29 | cd jitutils 30 | bootstrap.cmd 31 | ``` 32 | (on non-Windows, run bootstrap.sh. NOTE: On Mac, you need to first use `ulimit -n 2048` or the `dotnet restore` part of the build will fail.) 33 | 34 | 4. Optionally, add the built tools directory to your path, e.g.: 35 | ``` 36 | set PATH=%PATH%;\jitutils\bin 37 | ``` 38 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | - main 3 | 4 | pr: 5 | - main 6 | 7 | # Schedule a twice monthly build to ensure the pipeline isn't marked as inactive. 8 | schedules: 9 | - cron: '0 0 1,15 * *' 10 | displayName: Twice monthly build 11 | branches: 12 | include: 13 | - main 14 | always: true 15 | 16 | jobs: 17 | - template: /eng/build.yml 18 | parameters: 19 | agentOs: Windows_NT 20 | pool: 21 | vmImage: windows-latest 22 | 23 | - template: /eng/build.yml 24 | parameters: 25 | agentOs: Linux 26 | pool: 27 | vmImage: ubuntu-latest 28 | 29 | - template: /eng/build.yml 30 | parameters: 31 | agentOs: macOS 32 | pool: 33 | vmImage: macOS-latest 34 | -------------------------------------------------------------------------------- /bootstrap.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM Bootstrap the jitutils tools: 4 | REM 1. If this script is run not from within the jitutils directory (e.g., you've downloaded 5 | REM a copy of this file directly), then "git clone" the jitutils project first. Otherwise, 6 | REM if we can tell we're being run from within an existing "git clone", don't do that. 7 | REM 2. Build the jitutils tools. 8 | REM 3. Download (if necessary) clang-format.exe and clang-tidy.exe (used by the jit-format tool). 9 | 10 | set __ExitCode=0 11 | 12 | where /q dotnet.exe 13 | if %errorlevel% NEQ 0 echo Can't find dotnet.exe! Please install this ^(e.g., from https://www.microsoft.com/net/core^) and add dotnet.exe to PATH&set __ExitCode=1&goto :script_exit 14 | 15 | where /q git.exe 16 | if %errorlevel% NEQ 0 echo Can't find git.exe! Please add to PATH&set __ExitCode=1&goto :script_exit 17 | 18 | set __root=%~dp0 19 | setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION 20 | 21 | REM Are we already in the dotnet/jitutils repo? Or do we need to clone it? We look for build.cmd 22 | REM in the current directory (which is the directory this script was invoked from). 23 | 24 | if not exist %__root%build.cmd goto clone_jitutils 25 | 26 | pushd %__root% 27 | 28 | REM Check if build.cmd is in the root of the repo. 29 | set __tempfile=%TEMP%\gittemp-%RANDOM%.txt 30 | git rev-parse --show-toplevel >%__tempfile% 2>&1 31 | if errorlevel 1 ( 32 | echo Error: git failure: 33 | type %__tempfile% 34 | echo Cloning jitutils repo. 35 | del %__tempfile% 36 | popd 37 | goto clone_jitutils 38 | ) 39 | set /P gitroot=<%__tempfile% 40 | del %__tempfile% 41 | set gitroot=%gitroot:/=\% 42 | if not %gitroot:~-1%==\ set gitroot=%gitroot%\ 43 | if /I not %__root%==%gitroot% ( 44 | echo It doesn't looks like bootstrap.cmd is at the root of the repo. 45 | echo Cloning jitutils repo. 46 | popd 47 | goto clone_jitutils 48 | ) 49 | 50 | REM Is this actually the jitutils repo? 51 | git remote -v | findstr /i /c:"/jitutils" >nul 52 | if errorlevel 1 ( 53 | echo It doesn't looks like we're in the jitutils repo. 54 | echo Cloning jitutils repo. 55 | popd 56 | goto clone_jitutils 57 | ) 58 | 59 | REM Now go ahead and build it. 60 | call :build_jitutils 61 | 62 | popd 63 | 64 | REM If the build failed, we need to check it here, before the "endlocal". 65 | if %__ExitCode% NEQ 0 goto :script_exit 66 | 67 | :: Add utilites to the current path, but only if not already there 68 | endlocal 69 | call :AddToPath %__root%bin 70 | set __root= 71 | goto :script_exit 72 | 73 | REM =================================================================== 74 | REM == This is top-level, not a "call". 75 | REM == 76 | :clone_jitutils 77 | 78 | pushd %__root% 79 | 80 | :: Clone the jitutils repo 81 | 82 | git clone https://github.com/dotnet/jitutils.git 83 | if errorlevel 1 echo ERROR: clone failed.&set __ExitCode=1&goto :script_exit 84 | if not exist .\jitutils echo ERROR: can't find jitutils directory.&set __ExitCode=1goto :script_exit 85 | 86 | pushd .\jitutils 87 | 88 | call :build_jitutils 89 | 90 | popd 91 | 92 | popd 93 | 94 | REM If the build failed, we need to check it here, before the "endlocal". 95 | if %__ExitCode% NEQ 0 goto :script_exit 96 | 97 | :: Add utilites to the current path 98 | endlocal 99 | call :AddToPath %__root%jitutils\bin 100 | set __root= 101 | goto :script_exit 102 | 103 | REM =================================================================== 104 | :build_jitutils 105 | 106 | if not exist .\build.cmd echo Can't find build.cmd.&set __ExitCode=1&goto :eof 107 | 108 | :: Pull in needed packages. This works globally (due to jitutils.sln). 109 | 110 | dotnet restore 111 | if errorlevel 1 echo ERROR: dotnet restore failed.&set __ExitCode=1&goto :eof 112 | 113 | :: Build and publish all the utilities 114 | 115 | call .\build.cmd -p 116 | if errorlevel 1 echo ERROR: build failed.&set __ExitCode=1&goto :eof 117 | 118 | :: Check to see if clang-format and clang-tidy are available. Since we're going 119 | :: to add the 'bin' directory to the path, and they most likely already live there 120 | :: if you're running bootstrap.cmd not for the first time, add 'bin' to the path 121 | :: here (within the setlocal scope for now) before checking for them. Add 'bin' 122 | :: at the end of the path, to prefer other user downloaded versions, if any. 123 | 124 | set _clang_version=17.0.6 125 | 126 | set PATH=%PATH%;.\bin 127 | 128 | where /Q clang-format 129 | IF %errorlevel% NEQ 0 GOTO DownloadTools 130 | 131 | where /Q clang-tidy 132 | IF %errorlevel% NEQ 0 GOTO DownloadTools 133 | 134 | REM We found the tools on the path; now make sure the versions are good. 135 | 136 | clang-format --version | findstr /c:"version %_clang_version%" > NUL 137 | IF %errorlevel% EQU 0 GOTO build_done 138 | 139 | echo jit-format requires clang-format and clang-tidy version %_clang_version%. Currently installed: 140 | clang-format --version 141 | clang-tidy --version 142 | echo Please install version %_clang_version% and put the tools on the PATH to use jit-format. 143 | echo Tools can be found at https://github.com/llvm/llvm-project/releases/tag/llvmorg-%_clang_version% 144 | 145 | :DownloadTools 146 | 147 | echo Downloading formatting tools 148 | 149 | set _azdo_root=https://clrjit2.blob.core.windows.net/clang-tools 150 | set _platform=windows-x64 151 | set _azdo_dir=%_azdo_root%/%_clang_version%/%_platform% 152 | 153 | call :download_url clang-format "%_azdo_dir%/clang-format.exe" bin\clang-format.exe 154 | if %__ExitCode% NEQ 0 goto :eof 155 | 156 | call :download_url clang-tidy "%_azdo_dir%/clang-tidy.exe" bin\clang-tidy.exe 157 | if %__ExitCode% NEQ 0 goto :eof 158 | 159 | :build_done 160 | goto :eof 161 | 162 | REM =================================================================== 163 | REM Download helper, with retry on failure. Args: 164 | REM %1 - friendly name of download 165 | REM %2 - URL to download 166 | REM %3 - target of download 167 | REM 168 | REM We'd like to use Invoke-WebRequest `-MaximumRetryCount 8 -RetryIntervalSec 15`, but those aren't 169 | REM available on all versions of PowerShell. 170 | :download_url 171 | set _friendly_name=%1 172 | set _url=%2 173 | set _target_file=%3 174 | set _retry_count=1 175 | :start_download 176 | echo call powershell Invoke-WebRequest -Uri %_url% -OutFile %_target_file% 177 | call powershell Invoke-WebRequest -Uri %_url% -OutFile %_target_file% 178 | if %errorlevel% EQU 0 goto :eof 179 | echo ERROR: attempt #%_retry_count% failed to download %_friendly_name%. 180 | if %_retry_count% GEQ 4 echo ERROR: giving up attempting to download %_friendly_name%.&set __ExitCode=1&goto :eof 181 | echo Waiting 15 seconds to retry... 182 | powershell -command "Start-Sleep -Seconds 15" 183 | set /A _retry_count=_retry_count + 1 184 | goto start_download 185 | 186 | REM =================================================================== 187 | :script_exit 188 | REM Note, this entire line gets variable expanded before execution, so the "exit" use of %__ExitCode% gets expanded before it is cleared during execution. 189 | set __ExitCode=&exit /b %__ExitCode% 190 | 191 | REM =================================================================== 192 | :AddToPath 193 | set PATH | findstr /i %1 >nul 194 | if %errorlevel% NEQ 0 set PATH=%PATH%;%1 195 | goto :eof 196 | -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Bootstrap the jitutils tools: 4 | # 1. If this script is run not from within the jitutils directory (e.g., you've downloaded 5 | # a copy of this file directly), then "git clone" the jitutils project first. Otherwise, 6 | # if we can tell we're being run from within an existing "git clone", don't do that. 7 | # 2. Build the jitutils tools. 8 | # 3. Download (if necessary) clang-format and clang-tidy (used by the jit-format tool). 9 | 10 | function get_host_os { 11 | # Use uname to determine what the OS is. 12 | OSName=$(uname -s) 13 | case $OSName in 14 | Linux) 15 | __HostOS=Linux 16 | ;; 17 | 18 | Darwin) 19 | __HostOS=OSX 20 | ;; 21 | 22 | FreeBSD) 23 | __HostOS=FreeBSD 24 | ;; 25 | 26 | OpenBSD) 27 | __HostOS=OpenBSD 28 | ;; 29 | 30 | NetBSD) 31 | __HostOS=NetBSD 32 | ;; 33 | 34 | SunOS) 35 | __HostOS=SunOS 36 | ;; 37 | 38 | *) 39 | echo "Unsupported OS $OSName detected, configuring as if for Linux" 40 | __HostOS=Linux 41 | ;; 42 | esac 43 | } 44 | 45 | _machineHasCurl= 46 | 47 | function validate_url { 48 | if (( _machineHasCurl == 1 )); then 49 | status="$(curl -sLIo /dev/null "$1" -w '%{http_code}\n')" 50 | else 51 | response=($(wget -S --spider "$1" 2>&1 | grep "HTTP/")) 52 | status="${response[1]}" 53 | fi 54 | 55 | if (( status >= 200 || status < 400 )); then 56 | return 0; 57 | else 58 | return 1; 59 | fi 60 | } 61 | 62 | function download_tools { 63 | 64 | echo "Installing clang ${clangVersion} tools" 65 | 66 | # Do we have wget or curl? 67 | 68 | if command -v curl 2>/dev/null; then 69 | _machineHasCurl=1 70 | elif ! command -v wget 2>/dev/null; then 71 | echo "Error: curl or wget not found; not downloading clang-format and clang-tidy." 72 | return 1 73 | fi 74 | 75 | # Figure out which version to download. Use the "RID:" value from "dotnet --info". 76 | # It looks like one of these: 77 | # RID: osx-arm64 78 | # RID: linux-x64 79 | # RID: osx.10.12-x64 80 | # RID: ubuntu.22.04-x64 81 | # 82 | # If the RID doesn't work directly, it could be used to pick the appropriate platform. 83 | # Note that we currently don't support osx-x64 clang tools. 84 | 85 | clangToolsRootUrl="https://clrjit2.blob.core.windows.net/clang-tools" 86 | 87 | clangPlatform="$(dotnet --info | grep 'RID:')" 88 | clangPlatform="${clangPlatform##*RID:* }" 89 | 90 | # override common RIDs with compatible version so we don't need to upload binaries for each RID 91 | case $clangPlatform in 92 | osx*-x64) 93 | echo "clang-tidy/clang-format are not supported on osx-x64." 94 | return 0 95 | ;; 96 | ubuntu.*-x64) 97 | clangPlatform=linux-x64 98 | ;; 99 | esac 100 | 101 | clangFormatUrl="${clangToolsRootUrl}/${clangVersion}/${clangPlatform}/clang-format" 102 | 103 | if validate_url "$clangFormatUrl" > /dev/null; then 104 | echo "Downloading clang-format from ${clangFormatUrl} to bin directory" 105 | if (( _machineHasCurl == 1 )); then 106 | curl --retry 4 --progress-bar --location --fail "$clangFormatUrl" -o bin/clang-format 107 | else 108 | wget --tries 4 --progress=dot:giga "$clangFormatUrl" -O bin/clang-format 109 | fi 110 | chmod 751 bin/clang-format 111 | else 112 | echo "clang-format not found here: $clangFormatUrl" 113 | fi 114 | 115 | clangTidyUrl="${clangToolsRootUrl}/${clangVersion}/${clangPlatform}/clang-tidy" 116 | 117 | if validate_url "$clangTidyUrl" > /dev/null; then 118 | echo "Downloading clang-tidy from ${clangTidyUrl} to bin directory" 119 | if (( _machineHasCurl == 1 )); then 120 | curl --retry 4 --progress-bar --location --fail "$clangTidyUrl" -o bin/clang-tidy 121 | else 122 | wget --tries 4 --progress=dot:giga "$clangTidyUrl" -O bin/clang-tidy 123 | fi 124 | chmod 751 bin/clang-tidy 125 | else 126 | echo "clang-tidy not found here: $clangTidyUrl" 127 | fi 128 | 129 | if [ ! -f bin/clang-format -o ! -f bin/clang-tidy ]; then 130 | echo "Either clang-tidy or clang-format was not installed. Please install and put them on the PATH to use jit-format." 131 | echo "Tools can be found at https://github.com/llvm/llvm-project/releases/tag/llvmorg-${clangVersion}" 132 | return 1 133 | fi 134 | 135 | return 0 136 | } 137 | 138 | # Start the non-functions. 139 | 140 | __ErrMsgPrefix="ERROR: " 141 | 142 | get_host_os 143 | 144 | # Check if our required tools exist. 145 | 146 | if ! hash dotnet 2>/dev/null; then 147 | echo "${__ErrMsgPrefix}Can't find dotnet! Please install from https://dot.net and add to PATH." 148 | exit 1 149 | fi 150 | 151 | if ! hash git 2>/dev/null; then 152 | echo "${__ErrMsgPrefix}Can't find git! Please add to PATH." 153 | exit 1 154 | fi 155 | 156 | # Are we already in the dotnet/jitutils repo? Or do we need to clone it? We look for build.cmd 157 | # in the directory this script was invoked from. 158 | 159 | # Obtain the location of the bash script. 160 | __root="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 161 | 162 | # Check if the bootstrap script is in the root of the jitutils repo. 163 | # By default, we're going to clone the repo. 164 | __clone_repo=1 165 | if [ -e ${__root}/build.cmd ]; then 166 | # We found build.cmd. But make sure it's the root of the repo. 167 | __root="$( cd ${__root} && git rev-parse --show-toplevel )" 168 | pushd ${__root} >/dev/null 169 | git remote -v | grep "/jitutils" >/dev/null 170 | if [ $? == 0 ]; then 171 | # We've proven that we're at the root of the jitutils repo. 172 | __clone_repo=0 173 | fi 174 | popd >/dev/null 175 | fi 176 | 177 | if [ ${__clone_repo} == 1 ]; then 178 | git clone https://github.com/dotnet/jitutils.git 179 | exit_code=$? 180 | if [ $exit_code != 0 ]; then 181 | echo "${__ErrMsgPrefix}git clone failed." 182 | exit $exit_code 183 | fi 184 | if [ ! -d ${__root}/jitutils ]; then 185 | echo "${__ErrMsgPrefix}can't find ${__root}/jitutils." 186 | exit 1 187 | fi 188 | pushd ${__root}/jitutils >/dev/null 189 | else 190 | pushd . >/dev/null 191 | fi 192 | 193 | # Pull in needed packages. This works globally (due to jitutils.sln). 194 | 195 | dotnet restore 196 | exit_code=$? 197 | if [ $exit_code != 0 ]; then 198 | echo "${__ErrMsgPrefix}Failed to restore packages." 199 | exit $exit_code 200 | fi 201 | 202 | # Build and publish all the utilities 203 | 204 | ./build.sh -p 205 | exit_code=$? 206 | if [ $exit_code != 0 ]; then 207 | echo "${__ErrMsgPrefix}Build failed." 208 | exit $exit_code 209 | fi 210 | 211 | # Check for the formatting tools, and download them if necessary. 212 | 213 | clangVersion="17.0.6" 214 | 215 | exit_code=0 216 | if ! hash clang-format 2>/dev/null || ! hash clang-tidy 2>/dev/null; then 217 | download_tools 218 | exit_code=$? 219 | else 220 | if ! clang-format --version | grep -q "version ${clangVersion}" || ! clang-tidy --version | grep -q "version ${clangVersion}"; then 221 | echo "jit-format requires clang-format and clang-tidy version ${clangVersion}. Currently installed: " 222 | clang-format --version 223 | clang-tidy --version 224 | 225 | download_tools 226 | exit_code=$? 227 | fi 228 | fi 229 | if [ $exit_code != 0 ]; then 230 | echo "${__ErrMsgPrefix}Failed to download clang-format and clang-tidy." 231 | exit $exit_code 232 | fi 233 | 234 | popd >/dev/null 235 | 236 | echo "Adding ${__root}/jitutils/bin to PATH" 237 | export PATH=$PATH:${__root}/jitutils/bin 238 | 239 | echo "Done setting up!" 240 | exit 0 241 | -------------------------------------------------------------------------------- /build-coredistools.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal EnableDelayedExpansion EnableExtensions 3 | 4 | set TargetOSArchitecture=%1 5 | set LLVMTargetsToBuild=AArch64;ARM;X86;LoongArch;RISCV 6 | 7 | if /i "%TargetOSArchitecture%" == "win-arm64" ( 8 | set GeneratorPlatform=ARM64 9 | set LLVMHostTriple=aarch64-pc-windows-msvc 10 | ) else if /i "%TargetOSArchitecture%" == "win-x64" ( 11 | set GeneratorPlatform=x64 12 | set LLVMHostTriple=x86_64-pc-windows-msvc 13 | ) else if /i "%TargetOSArchitecture%" == "win-x86" ( 14 | set GeneratorPlatform=Win32 15 | set LLVMHostTriple=i686-pc-windows-msvc 16 | set LLVMTargetsToBuild=ARM;X86 17 | ) else ( 18 | echo ERROR: Unknown target OS and architecture: %TargetOSArchitecture% 19 | echo Use one of win-arm64, win-x64, win-x86. 20 | exit /b 1 21 | ) 22 | 23 | set BuildFlavor=%2 24 | if "%BuildFlavor%"=="" set BuildFlavor=Release 25 | 26 | if /i "%BuildFlavor%" == "Release" ( 27 | @REM ok 28 | ) else if /i "%BuildFlavor%" == "Debug" ( 29 | @REM ok 30 | ) else ( 31 | echo ERROR: Unknown build flavor: %BuildFlavor% 32 | exit /b 1 33 | ) 34 | 35 | if not defined LLVMDefaultTargetTriple ( 36 | set LLVMDefaultTargetTriple=%LLVMHostTriple% 37 | ) 38 | 39 | set RootDirectory=%~dp0 40 | set SourcesDirectory=%RootDirectory%src 41 | set BinariesDirectory=%RootDirectory%obj\%TargetOSArchitecture% 42 | set StagingDirectory=%RootDirectory%artifacts\%TargetOSArchitecture% 43 | 44 | where /q cmake.exe 45 | 46 | if %ERRORLEVEL% neq 0 ( 47 | echo ERROR: cmake.exe is not found in the PATH 48 | exit /b 1 49 | ) 50 | 51 | where /q llvm-tblgen.exe 52 | 53 | if %ERRORLEVEL% equ 0 goto found_llvm_tblgen 54 | 55 | @REM We expect it to be in the `bin` directory, so add that to the PATH if it's there. 56 | if not exist %RootDirectory%bin\llvm-tblgen.exe ( 57 | echo ERROR: llvm-tblgen.exe is not found in the PATH 58 | exit /b 1 59 | ) 60 | 61 | echo Found llvm-tblgen.exe in %RootDirectory%bin; adding that directory to PATH. 62 | set PATH=%RootDirectory%bin;%PATH% 63 | 64 | :found_llvm_tblgen 65 | 66 | for /f %%I in ('where llvm-tblgen.exe') do ( 67 | set LLVMTableGen=%%~I 68 | ) 69 | 70 | if not exist "%BinariesDirectory%" ( 71 | mkdir "%BinariesDirectory%" 72 | ) 73 | 74 | pushd "%BinariesDirectory%" 75 | 76 | @REM To use the Debug CRT, use: 77 | @REM -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebug 78 | @REM 79 | @REM To build a Debug version (asserts, debug info): 80 | @REM -DCMAKE_BUILD_TYPE=Debug 81 | @REM To build a Release version (no asserts, no debug info): 82 | @REM -DCMAKE_BUILD_TYPE=Release 83 | @REM 84 | @REM Misc. LLVM CMake documentation: https://llvm.org/docs/CMake.html 85 | 86 | cmake.exe ^ 87 | -G "Visual Studio 17 2022" ^ 88 | -A %GeneratorPlatform% ^ 89 | -DCMAKE_INSTALL_PREFIX="%StagingDirectory%" ^ 90 | -DCMAKE_BUILD_TYPE=%BuildFlavor% ^ 91 | -DLLVM_DEFAULT_TARGET_TRIPLE=%LLVMDefaultTargetTriple% ^ 92 | -DLLVM_EXTERNAL_PROJECTS=coredistools ^ 93 | -DLLVM_EXTERNAL_COREDISTOOLS_SOURCE_DIR="%SourcesDirectory%\coredistools" ^ 94 | -DLLVM_HOST_TRIPLE=%LLVMHostTriple% ^ 95 | -DLLVM_INCLUDE_TESTS=OFF ^ 96 | -DLLVM_TABLEGEN="%LLVMTableGen%" ^ 97 | -DLLVM_TARGETS_TO_BUILD=%LLVMTargetsToBuild% ^ 98 | -DLLVM_TOOL_COREDISTOOLS_BUILD=ON ^ 99 | -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded ^ 100 | "%SourcesDirectory%\llvm-project\llvm" 101 | 102 | popd 103 | 104 | if %ERRORLEVEL% neq 0 goto :CMakeNonZeroExitStatus 105 | 106 | @REM Use `--config Release` for release build, `--config Debug` for debug build 107 | 108 | cmake.exe ^ 109 | --build "%BinariesDirectory%" ^ 110 | --target coredistools ^ 111 | --config %BuildFlavor% 112 | 113 | if %ERRORLEVEL% neq 0 goto :CMakeNonZeroExitStatus 114 | 115 | cmake.exe ^ 116 | --install "%BinariesDirectory%" ^ 117 | --component coredistools 118 | 119 | if %ERRORLEVEL% neq 0 goto :CMakeNonZeroExitStatus 120 | 121 | exit /b 0 122 | 123 | :CMakeNonZeroExitStatus 124 | 125 | echo ERROR: cmake exited with code %ERRORLEVEL% 126 | exit /b 1 127 | -------------------------------------------------------------------------------- /build-coredistools.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | TargetOSArchitecture=$1 4 | CrossRootfsDirectory=$2 5 | 6 | # Set this to 1 to build using Azure Linux 3 7 | BuildUsingAzureLinux=1 8 | 9 | EnsureCrossRootfsDirectoryExists () { 10 | if [ ! -d "$CrossRootfsDirectory" ]; then 11 | echo "Invalid or unspecified CrossRootfsDirectory: $CrossRootfsDirectory" 12 | exit 1 13 | fi 14 | } 15 | 16 | CMakeOSXArchitectures= 17 | LLVMTargetsToBuild="AArch64;ARM;X86;LoongArch;RISCV" 18 | 19 | # Figure out which `strip` to use. Prefer `llvm-strip` if it is available. 20 | # `llvm-strip` is available in Azure Linux 3 container, 21 | # `llvm-strip-` is available on standard cross build Ubuntu container, 22 | # `strip` is available on macOS. 23 | StripTool=$(command -v llvm-strip{,-{20..15}} strip | head -n 1) 24 | if [ -z "$StripTool" ]; then 25 | echo "Strip tool not found" 26 | exit 1 27 | fi 28 | 29 | TblGenTool=$(command -v llvm-tblgen) 30 | if [ -z "$TblGenTool" ]; then 31 | echo "llvm-tblgen tool not found" 32 | exit 1 33 | fi 34 | 35 | # Take first match from: clang clang-20 clang-19 .. clang-15 36 | C_COMPILER=$(command -v clang{,-{20..15}} | head -n 1) 37 | if [ -z "$C_COMPILER" ]; then 38 | echo "C compiler not found" 39 | # Keep going in case cmake can find one? 40 | fi 41 | 42 | CXX_COMPILER=$(command -v clang++{,-{20..15}} | head -n 1) 43 | if [ -z "$CXX_COMPILER" ]; then 44 | echo "C++ compiler not found" 45 | # Keep going in case cmake can find one? 46 | fi 47 | 48 | echo "Using C compiler: $C_COMPILER" 49 | echo "Using C++ compiler: $CXX_COMPILER" 50 | echo "Using llvm-tablegen: $TblGenTool" 51 | 52 | case "$TargetOSArchitecture" in 53 | linux-arm) 54 | CMakeCrossCompiling=ON 55 | LLVMDefaultTargetTriple=thumbv7-linux-gnueabihf 56 | LLVMHostTriple=arm-linux-gnueabihf 57 | LLVMTargetsToBuild="ARM" 58 | EnsureCrossRootfsDirectoryExists 59 | ;; 60 | 61 | linux-arm64) 62 | CMakeCrossCompiling=ON 63 | LLVMHostTriple=aarch64-linux-gnu 64 | EnsureCrossRootfsDirectoryExists 65 | ;; 66 | 67 | linux-x64) 68 | LLVMHostTriple=x86_64-linux-gnu 69 | if [ $BuildUsingAzureLinux -eq 1 ]; then 70 | CMakeCrossCompiling=ON 71 | EnsureCrossRootfsDirectoryExists 72 | else 73 | CMakeCrossCompiling=OFF 74 | fi 75 | ;; 76 | 77 | linux-loongarch64) 78 | CMakeCrossCompiling=OFF 79 | LLVMHostTriple=loongarch64-linux-gnu 80 | LLVMTargetsToBuild="LoongArch" 81 | ;; 82 | 83 | linux-riscv64) 84 | CMakeCrossCompiling=ON 85 | LLVMHostTriple=riscv64-linux-gnu 86 | LLVMTargetsToBuild="RISCV" 87 | EnsureCrossRootfsDirectoryExists 88 | ;; 89 | 90 | osx-arm64) 91 | CMakeCrossCompiling=ON 92 | CMakeOSXArchitectures=arm64 93 | LLVMHostTriple=arm64-apple-macos 94 | BuildUsingAzureLinux=1 95 | ;; 96 | 97 | osx-x64) 98 | CMakeCrossCompiling=OFF 99 | CMakeOSXArchitectures=x86_64 100 | LLVMHostTriple=x86_64-apple-darwin 101 | ;; 102 | 103 | *) 104 | echo "Unknown target OS and architecture: $TargetOSArchitecture" 105 | exit 1 106 | esac 107 | 108 | LLVMDefaultTargetTriple=${LLVMDefaultTargetTriple:-$LLVMHostTriple} 109 | 110 | RootDirectory="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 111 | SourcesDirectory=$RootDirectory/src 112 | BinariesDirectory=$RootDirectory/obj/$TargetOSArchitecture 113 | StagingDirectory=$RootDirectory/artifacts/$TargetOSArchitecture 114 | 115 | command -v cmake >/dev/null 2>&1 116 | 117 | if [ "$?" -ne 0 ]; then 118 | echo "ERROR: cmake is not found in the PATH" 119 | exit 1 120 | fi 121 | 122 | if [ ! -d $BinariesDirectory ]; then 123 | mkdir -p $BinariesDirectory 124 | fi 125 | 126 | pushd "$BinariesDirectory" 127 | 128 | if [ -z "$CrossRootfsDirectory" ]; then 129 | C_BUILD_FLAGS="-target $LLVMHostTriple" 130 | CXX_BUILD_FLAGS="-target $LLVMHostTriple" 131 | cmake \ 132 | -G "Unix Makefiles" \ 133 | -DCMAKE_BUILD_TYPE=Release \ 134 | -DCMAKE_CROSSCOMPILING=$CMakeCrossCompiling \ 135 | -DCMAKE_C_COMPILER=${C_COMPILER} \ 136 | -DCMAKE_CXX_COMPILER=${CXX_COMPILER} \ 137 | -DCMAKE_C_FLAGS="${C_BUILD_FLAGS}" \ 138 | -DCMAKE_CXX_FLAGS="${CXX_BUILD_FLAGS}" \ 139 | -DCMAKE_INSTALL_PREFIX=$StagingDirectory \ 140 | -DCMAKE_OSX_ARCHITECTURES=$CMakeOSXArchitectures \ 141 | -DCMAKE_STRIP=$StripTool \ 142 | -DLLVM_DEFAULT_TARGET_TRIPLE=$LLVMDefaultTargetTriple \ 143 | -DLLVM_ENABLE_TERMINFO=OFF \ 144 | -DLLVM_EXTERNAL_PROJECTS=coredistools \ 145 | -DLLVM_EXTERNAL_COREDISTOOLS_SOURCE_DIR=$SourcesDirectory/coredistools \ 146 | -DLLVM_HOST_TRIPLE=$LLVMHostTriple \ 147 | -DLLVM_INCLUDE_TESTS=OFF \ 148 | -DLLVM_TABLEGEN=$TblGenTool \ 149 | -DLLVM_TARGETS_TO_BUILD=$LLVMTargetsToBuild \ 150 | -DLLVM_TOOL_COREDISTOOLS_BUILD=ON \ 151 | $SourcesDirectory/llvm-project/llvm 152 | elif [ $BuildUsingAzureLinux -eq 1 ]; then 153 | C_BUILD_FLAGS="--sysroot=$CrossRootfsDirectory -target $LLVMHostTriple" 154 | CXX_BUILD_FLAGS="--sysroot=$CrossRootfsDirectory -target $LLVMHostTriple" 155 | # Azure Linux 3 doesn't have `ld` so need to tell clang to use `lld` with "-fuse-ld=lld" 156 | cmake \ 157 | -G "Unix Makefiles" \ 158 | -DCMAKE_BUILD_TYPE=Release \ 159 | -DCMAKE_SYSTEM_NAME=Linux \ 160 | -DCMAKE_C_COMPILER=${C_COMPILER} \ 161 | -DCMAKE_CXX_COMPILER=${CXX_COMPILER} \ 162 | -DCMAKE_C_FLAGS="${C_BUILD_FLAGS}" \ 163 | -DCMAKE_CXX_FLAGS="${CXX_BUILD_FLAGS}" \ 164 | -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld" \ 165 | -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=lld" \ 166 | -DCMAKE_INCLUDE_PATH=$CrossRootfsDirectory/usr/include \ 167 | -DCMAKE_INSTALL_PREFIX=$StagingDirectory \ 168 | -DCMAKE_LIBRARY_PATH=$CrossRootfsDirectory/usr/lib/$LLVMHostTriple \ 169 | -DCMAKE_STRIP=$StripTool \ 170 | -DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER \ 171 | -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY \ 172 | -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY \ 173 | -DLLVM_DEFAULT_TARGET_TRIPLE=$LLVMDefaultTargetTriple \ 174 | -DLLVM_TABLEGEN=$TblGenTool \ 175 | -DLLVM_ENABLE_TERMINFO=OFF \ 176 | -DLLVM_EXTERNAL_PROJECTS=coredistools \ 177 | -DLLVM_EXTERNAL_COREDISTOOLS_SOURCE_DIR=$SourcesDirectory/coredistools \ 178 | -DLLVM_HOST_TRIPLE=$LLVMHostTriple \ 179 | -DLLVM_INCLUDE_TESTS=OFF \ 180 | -DLLVM_TARGETS_TO_BUILD=$LLVMTargetsToBuild \ 181 | -DLLVM_TOOL_COREDISTOOLS_BUILD=ON \ 182 | $SourcesDirectory/llvm-project/llvm 183 | else 184 | C_BUILD_FLAGS="--sysroot=$CrossRootfsDirectory -target $LLVMHostTriple" 185 | CXX_BUILD_FLAGS="--sysroot=$CrossRootfsDirectory -target $LLVMHostTriple" 186 | cmake \ 187 | -G "Unix Makefiles" \ 188 | -DCMAKE_BUILD_TYPE=Release \ 189 | -DCMAKE_CROSSCOMPILING=$CMakeCrossCompiling \ 190 | -DCMAKE_C_COMPILER=${C_COMPILER} \ 191 | -DCMAKE_CXX_COMPILER=${CXX_COMPILER} \ 192 | -DCMAKE_C_FLAGS="${C_BUILD_FLAGS}" \ 193 | -DCMAKE_CXX_FLAGS="${CXX_BUILD_FLAGS}" \ 194 | -DCMAKE_INCLUDE_PATH=$CrossRootfsDirectory/usr/include \ 195 | -DCMAKE_INSTALL_PREFIX=$StagingDirectory \ 196 | -DCMAKE_LIBRARY_PATH=$CrossRootfsDirectory/usr/lib/$LLVMHostTriple \ 197 | -DCMAKE_STRIP=$StripTool \ 198 | -DLLVM_DEFAULT_TARGET_TRIPLE=$LLVMDefaultTargetTriple \ 199 | -DLLVM_ENABLE_TERMINFO=OFF \ 200 | -DLLVM_EXTERNAL_PROJECTS=coredistools \ 201 | -DLLVM_EXTERNAL_COREDISTOOLS_SOURCE_DIR=$SourcesDirectory/coredistools \ 202 | -DLLVM_HOST_TRIPLE=$LLVMHostTriple \ 203 | -DLLVM_INCLUDE_TESTS=OFF \ 204 | -DLLVM_TABLEGEN=$TblGenTool \ 205 | -DLLVM_TARGETS_TO_BUILD=$LLVMTargetsToBuild \ 206 | -DLLVM_TOOL_COREDISTOOLS_BUILD=ON \ 207 | $SourcesDirectory/llvm-project/llvm 208 | fi 209 | 210 | popd 211 | 212 | if [ "$?" -ne 0 ]; then 213 | echo "ERROR: cmake exited with code $?" 214 | exit 1 215 | fi 216 | 217 | cmake \ 218 | --build $BinariesDirectory \ 219 | --parallel 4 \ 220 | --target install-coredistools-stripped 221 | 222 | if [ "$?" -ne 0 ]; then 223 | echo "ERROR: cmake exited with code $?" 224 | exit 1 225 | fi 226 | 227 | exit 0 228 | -------------------------------------------------------------------------------- /build-tblgen.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal EnableDelayedExpansion EnableExtensions 3 | 4 | set RootDirectory=%~dp0 5 | set SourcesDirectory=%RootDirectory%src 6 | set BinariesDirectory=%RootDirectory%obj 7 | set StagingDirectory=%RootDirectory%bin 8 | 9 | where /q cmake.exe 10 | 11 | if %ERRORLEVEL% neq 0 ( 12 | echo ERROR: cmake.exe is not found in the PATH 13 | exit /b 1 14 | ) 15 | 16 | if not exist "%BinariesDirectory%" ( 17 | mkdir "%BinariesDirectory%" 18 | ) 19 | 20 | pushd "%BinariesDirectory%" 21 | 22 | cmake.exe ^ 23 | -G "Visual Studio 17 2022" ^ 24 | -DCMAKE_INSTALL_PREFIX="%RootDirectory%\" ^ 25 | -DLLVM_TARGETS_TO_BUILD=AArch64;ARM;X86;LoongArch;RISCV ^ 26 | "%SourcesDirectory%\llvm-project\llvm" 27 | 28 | popd 29 | 30 | cmake.exe ^ 31 | --build %BinariesDirectory% ^ 32 | --target llvm-tblgen ^ 33 | --config Release 34 | 35 | if %ERRORLEVEL% neq 0 ( 36 | echo llvm-tblgen compilation has failed 37 | exit /b 1 38 | ) 39 | 40 | if not exist "%StagingDirectory%" ( 41 | mkdir "%StagingDirectory%" 42 | ) 43 | 44 | for /r "%BinariesDirectory%" %%I in (llvm-tblgen.ex?) do ( 45 | if "%%~nxI" == "llvm-tblgen.exe" ( 46 | xcopy "%%~I" "%StagingDirectory%" 47 | exit /b 0 48 | ) 49 | ) 50 | 51 | echo llvm-tblgen.exe is not found in "%BinariesDirectory%" 52 | exit /b 1 53 | -------------------------------------------------------------------------------- /build-tblgen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Build the Linux/Mac llvm-tblgen tool. Note that this will be run during the 4 | # Linux/Mac coredistools build. So, we only build the versions that we'll use 5 | # during those builds. Thus, we need a linux-x64 version (used for 6 | # linux-x64, linux-arm, linux-arm64 builds) and osx-x64 version (used for 7 | # osx-x64 and osx-arm64 builds). 8 | 9 | set -x 10 | 11 | TargetOSArchitecture=$1 12 | CrossRootfsDirectory=$2 13 | 14 | # Set this to 1 to build using Azure Linux 15 | BuildUsingAzureLinux=1 16 | 17 | CMakeOSXArchitectures= 18 | LLVMTargetsToBuild="AArch64;ARM;X86;LoongArch;RISCV" 19 | 20 | case "$TargetOSArchitecture" in 21 | linux-x64) 22 | LLVMHostTriple=x86_64-linux-gnu 23 | ;; 24 | 25 | linux-loongarch64) 26 | LLVMHostTriple=loongarch64-linux-gnu 27 | LLVMTargetsToBuild="LoongArch" 28 | ;; 29 | 30 | osx-x64) 31 | CMakeOSXArchitectures=x86_64 32 | LLVMHostTriple=x86_64-apple-darwin 33 | BuildUsingAzureLinux=0 34 | ;; 35 | 36 | *) 37 | echo "Unknown target OS and architecture: $TargetOSArchitecture" 38 | exit 1 39 | esac 40 | 41 | RootDirectory="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 42 | SourcesDirectory=$RootDirectory/src 43 | BinariesDirectory=$RootDirectory/obj 44 | StagingDirectory=$RootDirectory/bin 45 | 46 | command -v cmake >/dev/null 2>&1 47 | 48 | if [ "$?" -ne 0 ]; then 49 | echo "ERROR: cmake is not found in the PATH" 50 | exit 1 51 | fi 52 | 53 | if [ ! -d $BinariesDirectory ]; then 54 | mkdir -p $BinariesDirectory 55 | fi 56 | 57 | pushd "$BinariesDirectory" 58 | 59 | # Take first match from: clang clang-20 clang-19 .. clang-15 60 | C_COMPILER=$(command -v clang{,-{20..15}} | head -n 1) 61 | CXX_COMPILER=$(command -v clang++{,-{20..15}} | head -n 1) 62 | 63 | echo "============== Configuring build" 64 | if [ $BuildUsingAzureLinux -eq 1 ]; then 65 | C_BUILD_FLAGS="" 66 | CXX_BUILD_FLAGS="" 67 | # Azure Linux doesn't have `ld` so need to tell clang to use `lld` with "-fuse-ld=lld" 68 | cmake \ 69 | -G "Unix Makefiles" \ 70 | -DCMAKE_BUILD_TYPE=Release \ 71 | -DCMAKE_ASM_COMPILER=$C_COMPILER \ 72 | -DCMAKE_C_COMPILER=$C_COMPILER \ 73 | -DCMAKE_CXX_COMPILER=$CXX_COMPILER \ 74 | -DCMAKE_ASM_COMPILER_TARGET="$LLVMHostTriple" \ 75 | -DCMAKE_C_COMPILER_TARGET="$LLVMHostTriple" \ 76 | -DCMAKE_CXX_COMPILER_TARGET="$LLVMHostTriple" \ 77 | -DCMAKE_C_FLAGS="${C_BUILD_FLAGS}" \ 78 | -DCMAKE_CXX_FLAGS="${CXX_BUILD_FLAGS}" \ 79 | -DCMAKE_INSTALL_PREFIX=$RootDirectory \ 80 | -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ 81 | -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld" \ 82 | -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=lld" \ 83 | -DLLVM_USE_LINKER=lld \ 84 | -DLLVM_ENABLE_RUNTIMES="libcxx" \ 85 | -DLIBCXX_ENABLE_SHARED=OFF \ 86 | -DLIBCXX_CXX_ABI=libstdc++ \ 87 | -DLIBCXX_CXX_ABI_INCLUDE_PATHS="$CPP_INCLUDES;$TRIPLET_INCLUDES" \ 88 | -DLLVM_TARGETS_TO_BUILD=$LLVMTargetsToBuild \ 89 | $SourcesDirectory/llvm-project/llvm 90 | elif [ -z "$CrossRootfsDirectory" ]; then 91 | BUILD_FLAGS="" 92 | cmake \ 93 | -G "Unix Makefiles" \ 94 | -DCMAKE_BUILD_TYPE=Release \ 95 | -DCMAKE_SYSTEM_NAME=$CMakeSystemName \ 96 | -DCMAKE_C_COMPILER=$C_COMPILER \ 97 | -DCMAKE_CXX_COMPILER=$CXX_COMPILER \ 98 | -DCMAKE_C_FLAGS="${BUILD_FLAGS}" \ 99 | -DCMAKE_CXX_FLAGS="${BUILD_FLAGS}" \ 100 | -DCMAKE_INSTALL_PREFIX=$RootDirectory \ 101 | -DCMAKE_OSX_ARCHITECTURES=$CMakeOSXArchitectures \ 102 | -DLLVM_TARGETS_TO_BUILD=$LLVMTargetsToBuild \ 103 | $SourcesDirectory/llvm-project/llvm 104 | else 105 | BUILD_FLAGS="--sysroot=$CrossRootfsDirectory" 106 | cmake \ 107 | -G "Unix Makefiles" \ 108 | -DCMAKE_BUILD_TYPE=Release \ 109 | -DCMAKE_INSTALL_PREFIX=$RootDirectory \ 110 | -DCMAKE_SYSTEM_NAME=$CMakeSystemName \ 111 | -DCMAKE_C_COMPILER=$C_COMPILER \ 112 | -DCMAKE_CXX_COMPILER=$CXX_COMPILER \ 113 | -DCMAKE_C_FLAGS="${BUILD_FLAGS}" \ 114 | -DCMAKE_CXX_FLAGS="${BUILD_FLAGS}" \ 115 | -DCMAKE_INCLUDE_PATH=$CrossRootfsDirectory/usr/include \ 116 | -DCMAKE_LIBRARY_PATH=$CrossRootfsDirectory/usr/lib/$LLVMHostTriple \ 117 | -DLLVM_TARGETS_TO_BUILD=$LLVMTargetsToBuild \ 118 | $SourcesDirectory/llvm-project/llvm 119 | fi 120 | 121 | popd 122 | 123 | if [ "$?" -ne 0 ]; then 124 | echo "ERROR: cmake exited with code $?" 125 | exit 1 126 | fi 127 | 128 | echo "============== Building llvm-tblgen" 129 | cmake \ 130 | --build $BinariesDirectory \ 131 | --parallel 4 \ 132 | --target llvm-tblgen \ 133 | --config Release 134 | 135 | if [ "$?" -ne 0 ]; then 136 | echo "ERROR: cmake exited with code $?" 137 | exit 1 138 | fi 139 | 140 | if [ ! -d $StagingDirectory ]; then 141 | mkdir -p $StagingDirectory 142 | fi 143 | 144 | # Copy llvm-tblgen from BinariesDirectory to StagingDirectory 145 | find $BinariesDirectory -name llvm-tblgen -type f -exec cp -v {} $StagingDirectory \; 146 | 147 | exit 0 148 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal EnableDelayedExpansion 3 | 4 | REM Build and optionally publish sub projects 5 | REM 6 | REM This script will by default build release versions of the tools. 7 | REM If publish (-p) is requested it will create standalone versions of the 8 | REM tools in /src//bin///netcoreapp. 9 | 10 | set scriptDir=%~dp0 11 | set appInstallDir=%scriptDir%bin 12 | set buildType=Release 13 | set publish=false 14 | 15 | for /f "usebackq tokens=1,2" %%a in (`dotnet --info`) do ( 16 | if "%%a"=="RID:" set rid=%%b 17 | ) 18 | 19 | :argLoop 20 | if "%1"=="" goto :build 21 | 22 | if /i "%1"=="-b" ( 23 | set buildType=%2 24 | shift 25 | goto :nextArg 26 | ) 27 | if /i "%1"=="-p" ( 28 | set publish=true 29 | goto :nextArg 30 | ) 31 | if /i "%1" == "-h" ( 32 | goto :usage 33 | ) 34 | echo ERROR: unknown argument %1 35 | goto :usage 36 | 37 | :nextArg 38 | shift 39 | goto :argLoop 40 | 41 | :build 42 | 43 | REM Do as many builds as possible; don't stop on first failure (if any). 44 | set __ExitCode=0 45 | 46 | REM Declare the list of projects 47 | set projects=jit-diff jit-dasm jit-analyze jit-tp-analyze jit-format pmi jit-dasm-pmi jit-decisions-analyze performance-explorer instructions-retired-explorer 48 | 49 | REM Build each project 50 | for %%p in (%projects%) do ( 51 | if %publish%==true ( 52 | REM Publish src/pmi project without single-file, so it can be executed with a custom build of the runtime/JIT 53 | if "%%p"=="pmi" ( 54 | dotnet publish -c %buildType% -o %appInstallDir% .\src\%%p 55 | ) else ( 56 | dotnet publish -c %buildType% -o %appInstallDir% .\src\%%p --self-contained -r:%rid% -p:PublishSingleFile=true 57 | ) 58 | if errorlevel 1 echo ERROR: dotnet publish failed for .\src\%%p.&set __ExitCode=1 59 | ) else ( 60 | dotnet build -c %buildType% .\src\%%p 61 | if errorlevel 1 echo ERROR: dotnet build failed for .\src\%%p.&set __ExitCode=1 62 | ) 63 | ) 64 | 65 | REM Done 66 | exit /b %__ExitCode% 67 | 68 | :usage 69 | echo. 70 | echo build.cmd [-b ^] [-h] [-p] 71 | echo. 72 | echo -b ^ : Build type, can be Debug or Release. 73 | echo -h : Show this message. 74 | echo -p : Publish utilities. 75 | echo. 76 | exit /b 1 77 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## Build and optionally publish sub projects 4 | ## 5 | ## This script will by default build release versions of the tools. 6 | ## If publish (-p) is requested it will create standalone versions of the 7 | ## tools in /bin. 8 | 9 | function usage 10 | { 11 | echo "" 12 | echo "build.sh [-b ] [-h] [-p]" 13 | echo "" 14 | echo " -b : Build type, can be Debug or Release." 15 | echo " -h : Show this message." 16 | echo " -p : Publish utilities." 17 | echo "" 18 | } 19 | 20 | # defaults 21 | __ErrMsgPrefix="ERROR: " 22 | final_exit_code=0 23 | buildType="Release" 24 | publish=0 25 | scriptDir="$(cd "$(dirname "$0")" || exit; pwd -P)" 26 | # default install in 'bin' dir at script location 27 | appInstallDir="$scriptDir/bin" 28 | rid=$(dotnet --info | grep RID:) 29 | rid=${rid##*RID:* } 30 | 31 | # process for '-h', '-p', '-b ' 32 | while getopts "hpb:" opt; do 33 | case "$opt" in 34 | h) 35 | usage 36 | exit 0 37 | ;; 38 | b) 39 | buildType=$OPTARG 40 | ;; 41 | p) 42 | publish=1 43 | ;; 44 | *) echo "ERROR: unknown argument $opt" 45 | exit 1 46 | ;; 47 | esac 48 | done 49 | 50 | # declare the array of projects 51 | declare -a projects=(jit-dasm jit-diff jit-analyze jit-tp-analyze jit-format pmi jit-dasm-pmi jit-decisions-analyze performance-explorer instructions-retired-explorer) 52 | 53 | # for each project either build or publish 54 | for proj in "${projects[@]}" 55 | do 56 | if [ "$publish" = 1 ]; then 57 | case "$proj" in 58 | # Publish src/pmi project without single-file, so it can be executed with a custom build of the runtime/JIT 59 | pmi) dotnet publish -c "$buildType" -o "$appInstallDir" ./src/"$proj" ;; 60 | *) dotnet publish -c "$buildType" -o "$appInstallDir" ./src/"$proj" --self-contained -r $rid -p:PublishSingleFile=true ;; 61 | esac 62 | exit_code=$? 63 | if [ $exit_code != 0 ]; then 64 | echo "${__ErrMsgPrefix}dotnet publish of ./src/${proj} failed." 65 | final_exit_code=1 66 | fi 67 | else 68 | dotnet build -c "$buildType" ./src/"$proj" 69 | exit_code=$? 70 | if [ $exit_code != 0 ]; then 71 | echo "${__ErrMsgPrefix}dotnet build of ./src/${proj} failed." 72 | final_exit_code=1 73 | fi 74 | fi 75 | done 76 | 77 | exit "$final_exit_code" 78 | -------------------------------------------------------------------------------- /doc/building-coredistools.md: -------------------------------------------------------------------------------- 1 | # Building coredistools 2 | 3 | ## Building on Windows with Visual Studio 2022 4 | 5 | 1. Checkout the jitutils repository: 6 | ``` 7 | git clone https://github.com/dotnet/jitutils.git 8 | cd jitutils 9 | ``` 10 | 11 | 2. Checkout the LLVM project repository into a subdirectory named src/llvm-project: 12 | ``` 13 | git clone --depth 1 --branch llvmorg-20.1.0 https://github.com/llvm/llvm-project.git src\llvm-project 14 | ``` 15 | 16 | 3. Build `llvm-tblgen.exe`: 17 | ``` 18 | build-tblgen.cmd 19 | ``` 20 | 21 | This builds llvm-tblgen.exe and puts it in the `bin` subdirectory. 22 | 23 | 4. Add the `bin` subdirectory to the `PATH`: 24 | ``` 25 | set "PATH=%cd%\bin;%PATH%" 26 | ``` 27 | 28 | This puts the just built lldb-tblgen.exe on the `PATH`. 29 | 30 | 5. Build `coredistools.dll` for a combination of target OS and architecture. 31 | Build Windows x64, Windows x86, and Windows ARM64 binaries: 32 | ``` 33 | build-coredistools.cmd win-x64 34 | build-coredistools.cmd win-x86 35 | build-coredistools.cmd win-arm64 36 | ``` 37 | 38 | The file will be copied to subdirectory `artifacts` after the command finishes. E.g., for win-x64: 39 | ``` 40 | dir /A:-D /B /S artifacts\win-x64 41 | F:\echesako\git\jitutils\artifacts\win-x64\bin\coredistools.dll 42 | F:\echesako\git\jitutils\artifacts\win-x64\lib\coredistools.lib 43 | ``` 44 | 45 | ### Building Debug binaries 46 | 47 | The `build-coredistools.cmd` script is set up to build a Release build. To create a Debug build with a PDB file 48 | for debugging, change the `--config Release` line to `--config Debug`. 49 | 50 | ## Building on Linux / Mac 51 | 52 | 1. Checkout the jitutils repository: 53 | ``` 54 | git clone https://github.com/dotnet/jitutils.git 55 | cd jitutils 56 | ``` 57 | 58 | 2. Checkout the LLVM project repository: 59 | ``` 60 | git clone --depth 1 --branch llvmorg-20.1.0 https://github.com/llvm/llvm-project.git src/llvm-project 61 | ``` 62 | 63 | 3. Start the Docker container in which the build will be run: 64 | ``` 65 | docker run -it --rm --entrypoint /bin/bash -v ~/git/jitutils:/opt/code -w /opt/code mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-cross-amd64 66 | ``` 67 | 68 | 4. Install necessary dependencies (in Docker): 69 | You need to install the `ncurses-compat` package because the Azure Linux container we use doesn't have libtinfo.so.5, which the built 70 | llvm-tblgen needs to be able to run. (Note that we build in the Azure Linux container, but we also run some built binaries, namely llvm-tblgen, 71 | as part of the build process.) 72 | ``` 73 | sudo tdnf install -y ncurses-compat 74 | ``` 75 | 76 | 5. Build `llvm-tblgen` (in Docker): 77 | ``` 78 | ./build-tblgen.sh linux-x64 /crossrootfs/x64 79 | ``` 80 | 81 | This builds llvm-tblgen and puts it in the `bin` subdirectory. 82 | 83 | 6. Add `llvm-tblgen` to the PATH (in Docker): 84 | ``` 85 | export PATH=$(pwd)/bin:$PATH 86 | ``` 87 | 88 | 7. Build `libcoredistools.so` for Linux x64 (in Docker): 89 | ``` 90 | ./build-coredistools.sh linux-x64 /crossrootfs/x64 91 | ``` 92 | 93 | The file will be copied to subdirectory `artifacts` after the command finishes: 94 | ``` 95 | find ./artifacts -name libcoredistools.so 96 | ./artifacts/linux-x64/bin/libcoredistools.so 97 | ./artifacts/linux-x64/lib/libcoredistools.so 98 | ``` 99 | 100 | 8. Build `libcoredistools.so` for Linux arm64 under Docker: 101 | ``` 102 | docker run -it --rm --entrypoint /bin/bash -v ~/git/jitutils:/opt/code -w /opt/code mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-cross-arm64 103 | sudo tdnf install -y ncurses-compat 104 | export PATH=$(pwd)/bin:$PATH 105 | ./build-coredistools.sh linux-arm64 /crossrootfs/arm64 106 | ``` 107 | 108 | 9. Build `libcoredistools.so` for Linux arm under Docker: 109 | ``` 110 | docker run -it --rm --entrypoint /bin/bash -v ~/git/jitutils:/opt/code -w /opt/code mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-cross-arm 111 | sudo tdnf install -y ncurses-compat 112 | export PATH=$(pwd)/bin:$PATH 113 | ./build-coredistools.sh linux-arm /crossrootfs/arm 114 | ``` 115 | 116 | 10. Build `libcoredistools.so` for Linux riscv64 under Docker: 117 | There is no Azure Linux container for RISC-V so use the standard Ubuntu cross build container used for e.g. dotnet/runtime. 118 | ``` 119 | docker run -it --rm --entrypoint /bin/bash -v ~/git/jitutils:/opt/code -w /opt/code mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-22.04-cross-riscv64 120 | apt install libtinfo5 121 | 122 | # If you haven't built llvm-tblgen in step 5, you can do so in the same docker (pass "/" as crossrootfs). 123 | ./build-tblgen.sh linux-x64 / 124 | 125 | # Now, the main course 126 | export PATH=$(pwd)/bin:$PATH 127 | ./build-coredistools.sh linux-riscv64 /crossrootfs/riscv64 128 | ``` 129 | -------------------------------------------------------------------------------- /doc/cijobs.md: -------------------------------------------------------------------------------- 1 | # CI jobs information 2 | 3 | The .NET team maintains a "continuous integration" (CI) system for testing .NET, using Jenkins. 4 | In particular, each source code change submitted for consideration is tested. 5 | The "cijobs" command-line tool allows for querying the CI for per-job information, 6 | and for downloading archived per-job artifacts. These artifacts can be used for 7 | generating assembly code output, instead of building your own baseline JIT, for example. 8 | 9 | ## cijobs 10 | 11 | cijobs has two commands: (1) list, and (2) copy. 12 | 13 | cijobs help message: 14 | ``` 15 | $ cijobs --help 16 | usage: cijobs [] 17 | 18 | list List jobs on ci.dot.net for the repo. 19 | copy Copies job artifacts from ci.dot.net. This 20 | command copies a zip of artifacts from a repo (defaulted to 21 | dotnet_coreclr). The default location of the zips is the 22 | Product sub-directory, though that can be changed using the 23 | ContentPath(p) parameter 24 | ``` 25 | 26 | The "cijobs list" command has the following help message: 27 | ``` 28 | $ cijobs list --help 29 | usage: cijobs list [-j ] [-b ] [-r ] [-m ] 30 | [-n ] [-l] [-c ] [-a] 31 | 32 | -s, --server Url of the server. Defaults to 33 | https://ci.dot.net/ 34 | -j, --job Name of the job. 35 | -b, --branch Name of the branch (default is master). 36 | -r, --repo Name of the repo (e.g. dotnet_corefx or 37 | dotnet_coreclr). Default is dotnet_coreclr 38 | -m, --match Regex pattern used to select jobs output. 39 | -n, --number Job number. 40 | -l, --last_successful List last successful build. 41 | -c, --commit List build at this commit. 42 | -a, --artifacts List job artifacts on server. 43 | ``` 44 | 45 | The "cijobs copy" command has the following help message: 46 | ``` 47 | usage: cijobs copy [-j ] [-n ] [-l] [-c ] [-b ] 48 | [-r ] [-o ] [-u] [-p ] 49 | 50 | -s, --server Url of the server. Defaults to 51 | https://ci.dot.net/ 52 | -j, --job Name of the job. 53 | -n, --number Job number. 54 | -l, --last_successful Copy last successful build. 55 | -c, --commit Copy this commit. 56 | -b, --branch Name of the branch (default is master). 57 | -r, --repo Name of the repo (e.g. dotnet_corefx or 58 | dotnet_coreclr). Default is 59 | dotnet_coreclr 60 | -o, --output Output path. 61 | -u, --unzip Unzip copied artifacts 62 | -p, --ContentPath Relative product zip path. Default is 63 | artifact/bin/Product/*zip*/Product.zip 64 | ``` 65 | 66 | ## Using cijobs 67 | 68 | A common question for a developer might be "what is the last successful OSX checked build?" 69 | 70 | ``` 71 | $ cijobs list --match "osx" 72 | 73 | job checked_osx 74 | job checked_osx_flow 75 | job checked_osx_flow_prtest 76 | job checked_osx_prtest 77 | job checked_osx_tst 78 | job checked_osx_tst_prtest 79 | job debug_osx 80 | job debug_osx_flow 81 | job debug_osx_flow_prtest 82 | job debug_osx_prtest 83 | job debug_osx_tst 84 | job debug_osx_tst_prtest 85 | job release_osx 86 | job release_osx_flow 87 | ... 88 | ``` 89 | 90 | The previous example shows searching for job names that match "osx". The checked_osx jobs is the 91 | one the developer wants. (Some familiarity with the jobs running on the server is helpful. 92 | Visit https://ci.dot.net to familarize yourself with what's available.) 93 | 94 | Further querying the `checked_osx` job for the last successful build can be done with this command 95 | line. 96 | 97 | ``` 98 | $ cijobs list --job checked_osx --last_successful 99 | 100 | Last successful build: 101 | build 1609 - SUCCESS : commit 74798b5b95aca1b27050038202034448a523c9f9 102 | ``` 103 | 104 | With this in hand two things can be accomplished. First, new development for a feature could be 105 | started based on the commit hash returned. Second, the tools generated by this job can be downloaded 106 | for use locally. 107 | 108 | ``` 109 | $ cijobs copy --job checked_osx --last_successful --output ../output/mytools --unzip 110 | 111 | Downloading: job/dotnet_coreclr/job/master/job/checked_osx/1609/artifact/bin/Product/*zip*/Product.zip 112 | ``` 113 | 114 | Results are unzipped in the output after they are downloaded. 115 | 116 | ``` 117 | $ ls ../output/mytools/ 118 | Product Product.zip 119 | 120 | ``` 121 | 122 | One comment on the underlying Jenkins feature. The artifacts are kept and managed by a Jenkins plug-in 123 | used by the system. This plug-in will ZIP compress on demand at any point in the defined artifacts output tree. 124 | Today we only use the Product sub-directory but this could be extended in the future. 125 | -------------------------------------------------------------------------------- /doc/config.md: -------------------------------------------------------------------------------- 1 | # Configuring defaults 2 | 3 | Several jitutils tools support specifying default command line arguments via a configuration 4 | file. This file is described here. 5 | 6 | The tools which currently support this configuration mechanism are: jit-diff, jit-format. 7 | 8 | ## Overview 9 | 10 | When the environment variable `JIT_UTILS_ROOT` is defined, it specifies the directory where a 11 | config.json file can be found that specifies various configuration data that will be used 12 | while running the jitutils tools. It also specifies the root directory where the tools can 13 | create subdirectories to copy various things. 14 | 15 | ``` 16 | $ export JIT_UTILS_ROOT=~/Work/output 17 | $ ls -1 $JIT_UTILS_ROOT 18 | config.json 19 | dasmset_1 20 | dasmset_2 21 | dasmset_3 22 | dasmset_4 23 | dasmset_5 24 | tools 25 | ``` 26 | 27 | The above example shows a populated `JIT_UTILS_ROOT` directory. The config.json file contains defaults, 28 | the `dasmset_(x)` contain multiple iterations of output from jit-diff, and the tools directory 29 | contains installed tools. 30 | 31 | ### config.json 32 | 33 | A sample config.json file is included in the jitutils repo as an example that can be modified 34 | for a developer's own use. We will go through the different elements here for added detail. 35 | This file supplies the configuration options for both jit-diff and jit-format. The most interesting 36 | section of the file is the `"default"` section. Each sub element of `"default"` maps directly to a jit-diff 37 | or jit-format option name. Setting a default value here will cause the tools to 38 | use the given value on start up and then only override that value if new options are passed 39 | on the command line. 40 | 41 | In the jit-diff section, the `"base"` and `"diff"` entries are worth going into 42 | in more detail. The `"base"` is set to `"checked_osx-1526"`. Looking down in the `"tools"` section 43 | shows that the tool is installed in the `tools` sub-directory of the directory specified by 44 | `JIT_UTILS_ROOT`. Any of the tools listed like this can be used in the default section 45 | as a value, but they can also be passed on the command line 46 | as the value for `--base` or `--diff`. 47 | 48 | Sample config.json: 49 | ``` 50 | { 51 | "format": { 52 | "default": { 53 | "arch": "x64", 54 | "build": "Checked", 55 | "os": "windows", 56 | "coreclr": "C:\\michelm\\coreclr", 57 | "verbose": "true", 58 | "fix": "true" 59 | } 60 | }, 61 | "asmdiff": { 62 | "default": { 63 | "base": "checked_osx-1526", 64 | "diff": "/Users/russellhadley/Work/dotnet/coreclr/bin/Product/OSX.x64.Checked", 65 | "frameworks": "true", 66 | "output": "/Users/russellhadley/Work/dotnet/output", 67 | "core_root": "/Users/russellhadley/Work/dotnet/runtime/artifacts/tests/coreclr/OSX.x64.Checked/Tests/Core_Root" 68 | }, 69 | "tools": [ 70 | { 71 | "tag": "checked_osx-1439", 72 | "path": "/Users/russellhadley/Work/dotnet/output/tools/checked_osx-1439/Product/OSX.x64.Checked" 73 | }, 74 | { 75 | "tag": "checked_osx-1442", 76 | "path": "/Users/russellhadley/Work/dotnet/output/tools/checked_osx-1442/Product/OSX.x64.Checked" 77 | }, 78 | { 79 | "tag": "checked_osx-1443", 80 | "path": "/Users/russellhadley/Work/dotnet/output/tools/checked_osx-1443/Product/OSX.x64.Checked" 81 | }, 82 | { 83 | "tag": "checked_osx-1526", 84 | "path": "/Users/russellhadley/Work/dotnet/output/tools/checked_osx-1526/Product/OSX.x64.Checked" 85 | } 86 | ] 87 | } 88 | } 89 | ``` 90 | 91 | ### Listing current defaults 92 | 93 | The jit-diff command `list` will read the config.json file in the specified `JIT_UTILS_ROOT` path, and list 94 | the results. Adding `--verbose` will show the associated file system paths for installed tools as well. 95 | 96 | For example: 97 | ``` 98 | $ jit-diff list 99 | 100 | Defaults: 101 | base: checked_osx-1526 102 | diff: /Users/russellhadley/Work/dotnet/coreclr/bin/Product/OSX.x64.Checked 103 | output: /Users/russellhadley/Work/dotnet/output 104 | core_root: /Users/russellhadley/Work/dotnet/runtime/artifacts/tests/coreclr/OSX.x64.Checked/Tests/Core_Root 105 | frameworks: true 106 | 107 | Installed tools: 108 | checked_osx-1439 109 | checked_osx-1442 110 | checked_osx-1443 111 | checked_osx-1526 112 | ``` 113 | 114 | ### Installing new tools 115 | 116 | The jit-diff command `install` will download and install a new tool to the default location 117 | and update the config.json so it can be found. 118 | 119 | The options to `install` are the same as you would use for the cijobs copy command since jit-diff 120 | uses cijobs to download the appropriate tools. I.e. the `install` command is just a wrapper over 121 | cijobs to simplify getting tools into the default location correctly. 122 | -------------------------------------------------------------------------------- /doc/formatting.md: -------------------------------------------------------------------------------- 1 | # JIT source code formatting 2 | 3 | JIT source code is automatically formatted by the jit-format tool. 4 | The idea is to automatically enforce the 5 | [CLR JIT Coding Conventions](https://github.com/dotnet/coreclr/blob/master/Documentation/coding-guidelines/clr-jit-coding-conventions.md) 6 | where possible, although the tool by its nature ends up defining the 7 | coding conventions by the formatting it enforces. The tool invokes clang-format and clang-tidy 8 | to do its work. 9 | 10 | ## jit-format 11 | 12 | The jit-format tool runs over all jit source, or specific files if they are specified. 13 | It can either tell the user that code needs to be reformatted or it 14 | can fix the source itself. 15 | 16 | Sample help command line 17 | ``` 18 | $ jit-format --help 19 | usage: jit-format [-a ] [-o ] [-b ] [-c ] 20 | [--compile-commands ] [-v] [--untidy] 21 | [--noformat] [-f] [-i] [--projects ...] [--] 22 | ... 23 | 24 | -a, --arch The architecture of the build (options: 25 | x64, x86) 26 | -o, --os The operating system of the build 27 | (options: Windows, OSX, Ubuntu, Fedora, 28 | etc.) 29 | -b, --build The build type of the build (options: 30 | Release, Checked, Debug) 31 | -c, --coreclr Full path to base coreclr directory 32 | --compile-commands Full path to compile_commands.json 33 | -v, --verbose Enable verbose output. 34 | --untidy Do not run clang-tidy 35 | --noformat Do not run clang-format 36 | -f, --fix Fix formatting errors discovered by 37 | clang-format and clang-tidy. 38 | -i, --ignore-errors Ignore clang-tidy errors 39 | --projects ... List of build projects clang-tidy should 40 | consider (e.g. dll, standalone, 41 | protojit, etc.). Default: dll 42 | ... Optional list of files that should be 43 | formatted. 44 | ``` 45 | 46 | ## Using jit-format 47 | 48 | A common task for developers is to format their changes before submitting a GitHub pull request. 49 | A developer can run the tool using the tests/scripts/format.py script in the coreclr repo: 50 | 51 | ``` 52 | python src\coreclr\scripts\jitformat.py --coreclr C:\gh\runtime\src\coreclr --arch x64 --os windows 53 | ``` 54 | 55 | This will run all build flavors and all projects for the user. This should be done on both 56 | Windows and Linux. 57 | 58 | A developer can also run the tool manually: 59 | 60 | ``` 61 | jit-format -c c:\gh\coreclr -f 62 | ``` 63 | 64 | or, specifically passing architecture, build type, and operating system: 65 | 66 | ``` 67 | jit-format -a x64 -b Debug -o Windows -c C:\gh\coreclr -f 68 | ``` 69 | 70 | This will run both clang-tidy and clang-format on all of the jit code and fix all the 71 | formatting to match the rules. 72 | 73 | Often a developer is only interested in a few files. For example, 74 | you can invoke jit-format as follows to just examine lower.cpp and codegenxarch.cpp: 75 | 76 | ``` 77 | jit-format -a x64 -b Debug -o Windows -c C:\gh\coreclr -v lower.cpp codegenxarch.cpp 78 | 79 | Formatting jit directory. 80 | Formatting dll project. 81 | Building compile_commands.json. 82 | Using compile_commands.json found at C:\gh\coreclr\bin\obj\windows.x64.Checked\compile_commands.json 83 | Running clang-tidy. 84 | Running: 85 | clang-tidy -checks=-*,readability-braces*,modernize-use-nullptr -header-filter=.* -p C:\gh\coreclr\bin\obj\windows.x64.Checked\compile_commands.json C:\gh\coreclr\src\jit\codegenxarch.cpp 86 | 87 | Running: 88 | clang-tidy -checks=-*,readability-braces*,modernize-use-nullptr -header-filter=.* -p C:\gh\coreclr\bin\obj\windows.x64.Checked\compile_commands.json C:\gh\coreclr\src\jit\lower.cpp 89 | 90 | Running: clang-format C:\gh\coreclr\src\jit\codegenxarch.cpp 91 | Running: clang-format C:\gh\coreclr\src\jit\lower.cpp 92 | ``` 93 | 94 | jit-format runs over only those files. The verbose flag shows the user what 95 | clang-tidy and clang-format invocations are being executed. 96 | 97 | When the developer is ready to check in, they will want to make sure they fix any formatting 98 | errors that clang-tidy and clang-format identified. This can be done with the --fix flag: 99 | 100 | ``` 101 | jit-format -a x64 -b Debug -o Windows -c C:\gh\coreclr -v --fix lower.cpp codegenxarch.cpp 102 | 103 | Formatting jit directory. 104 | Formatting dll project. 105 | Building compile_commands.json. 106 | Using compile_commands.json found at C:\gh\coreclr\bin\obj\windows.x64.Checked\compile_commands.json 107 | Running clang-tidy. 108 | Running: 109 | clang-tidy -fix -checks=-*,readability-braces*,modernize-use-nullptr -header-filter=.* -p C:\gh\coreclr\bin\obj\windows.x64.Checked\compile_commands.json C:\gh\coreclr\src\jit\codegenxarch.cpp 110 | 111 | Running: 112 | clang-tidy -fix -checks=-*,readability-braces*,modernize-use-nullptr -header-filter=.* -p C:\gh\coreclr\bin\obj\windows.x64.Checked\compile_commands.json C:\gh\coreclr\src\jit\lower.cpp 113 | 114 | Running: clang-format -i C:\gh\coreclr\src\jit\codegenxarch.cpp 115 | Running: clang-format -i C:\gh\coreclr\src\jit\lower.cpp 116 | ``` 117 | 118 | The developer may also only be interested in only running clang-tidy or clang-format without 119 | running the other tool. This can be done by using the --noformat (no clang-format) or 120 | --untidy (no clang-tidy) flags. 121 | 122 | Finally, the developer can pass their own compile_commands.json database to jit-format 123 | if they already have one built: 124 | 125 | ``` 126 | jit-format.cmd --fix --noformat --compile-commands C:\gh\jitutils\test\jit-format\compile_commands.json --coreclr C:\gh\jitutils\test\jit-format C:\gh\jitutils\test\jit-format\test.cpp 127 | Formatting jit directory. 128 | Formatting dll project. 129 | Using compile_commands.json found at C:\gh\jitutils\test\jit-format\compile_commands.json 130 | Running clang-tidy. 131 | Running: 132 | clang-tidy -fix -checks=-*,readability-braces*,modernize-use-nullptr -header-filter=.* -p C:\gh\jitutils\test\jit-format\compile_commands.json C:\gh\jitutils\test\jit-format\test.cpp 133 | ``` 134 | 135 | ## clang-format and clang-tidy 136 | 137 | jit-format uses clang-format and clang-tidy to do its work. 138 | 139 | Currently, clang-tidy will run the `modernize-use-nullptr` and `readability-braces` 140 | checks. Clang-format will use the `.clang-format` specification found in the jit directory. 141 | A summary of what each of the options does can be found 142 | [here](https://llvm.org/releases/3.8.0/tools/clang/docs/ClangFormatStyleOptions.html). 143 | 144 | Because jit-format will build a `compile_commands.json` database from the build log on Windows, 145 | developers must do a full build of coreclr before running jit-format. 146 | 147 | ## Limitations 148 | 149 | The clang-format and clang-tidy tools cannot enforce all the rules specified in the CLR JIT Coding Conventions. 150 | Developers still need to be aware of these conventions and follow the parts that can't be automatically 151 | enforced. 152 | 153 | Clang-format and clang-tidy have bugs and limitations to their enforcement. One particular example: a comment 154 | line followed by an `#ifdef`, typically placed at the leftmost column, will cause the comment line to be also 155 | aligned to the leftmost column. To avoid this, the JIT sources introduced the `CLANG_FORMAT_COMMENT_ANCHOR` 156 | macro to prevent this from happening, used as follows: 157 | 158 | ``` 159 | // Some kind of comment 160 | CLANG_FORMAT_COMMENT_ANCHOR; 161 | 162 | #ifdef SOME_KIND_OF_IFDEF 163 | ``` 164 | 165 | ## Configuring defaults 166 | 167 | See the document [configuring defaults](config.md) for details on setting up a set of default configurations. 168 | -------------------------------------------------------------------------------- /doc/tools.md: -------------------------------------------------------------------------------- 1 | # Other Tools 2 | 3 | ## pmi 4 | 5 | `pmi` is a low-level tool for running the jit across the methods in an assembly. 6 | It can be used as a component to create diffs or to simply test whether the jit 7 | encounters any internal issues when jitting methods. 8 | ``` 9 | $pmi --help 10 | 11 | Usage: 12 | 13 | pmi Count PATH_TO_ASSEMBLY 14 | Count the number of types and methods in an assembly. 15 | 16 | pmi PrepOne PATH_TO_ASSEMBLY INDEX_OF_TARGET_METHOD 17 | JIT a single method, specified by a method number. 18 | 19 | pmi PrepAll PATH_TO_ASSEMBLY [INDEX_OF_FIRST_METHOD_TO_PROCESS] 20 | JIT all the methods in an assembly. If INDEX_OF_FIRST_METHOD_TO_PROCESS 21 | is specified, it is the first method compiled, followed by all subsequent 22 | methods. 23 | 24 | pmi DriveAll PATH_TO_ASSEMBLY 25 | The same as PrepAll, but is more robust. While PrepAll will stop at the 26 | first JIT assert, DriveAll will continue by skipping that method. 27 | 28 | Environment variable PMIPATH is a semicolon-separated list of paths used to find 29 | dependent assemblies. 30 | 31 | Use PrepAll-Quiet and PrepOne-Quiet if less verbose output is desired. 32 | ``` 33 | -------------------------------------------------------------------------------- /eng/build.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | agentOs: '' 3 | pool: '' 4 | 5 | jobs: 6 | - job: ${{ parameters.agentOs }} 7 | pool: ${{ parameters.pool }} 8 | steps: 9 | - checkout: self 10 | clean: true 11 | 12 | - task: UseDotNet@2 13 | displayName: 'Install .NET Core SDK' 14 | inputs: 15 | packageType: sdk 16 | version: 8.x 17 | installationPath: $(Agent.ToolsDirectory)/dotnet 18 | 19 | - ${{ if eq(parameters.agentOs, 'Windows_NT') }}: 20 | - script: bootstrap.cmd 21 | displayName: Build the tools 22 | - ${{ if ne(parameters.agentOs, 'Windows_NT') }}: 23 | - script: ./bootstrap.sh 24 | displayName: Build the tools 25 | -------------------------------------------------------------------------------- /eng/download-checkout-llvm.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - download: current 3 | artifact: $(LLVMSourceBundle) 4 | displayName: Download LLVM bundle 5 | 6 | - script: git clone $(Pipeline.Workspace)\$(LLVMSourceBundle)\$(LLVMSourceBundle) $(Build.SourcesDirectory)\src\llvm-project 7 | displayName: Checkout LLVM from bundle (Windows) 8 | condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) 9 | 10 | - script: git clone $(Pipeline.Workspace)/$(LLVMSourceBundle)/$(LLVMSourceBundle) $(Build.SourcesDirectory)/src/llvm-project 11 | displayName: Checkout LLVM from bundle (Linux or macOS) 12 | condition: and(succeeded(), ne(variables['Agent.OS'], 'Windows_NT')) 13 | -------------------------------------------------------------------------------- /pack-coredistools.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal EnableDelayedExpansion EnableExtensions 3 | 4 | set RootDirectory=%~dp0 5 | set SourcesDirectory=%RootDirectory%src 6 | set PackagesDirectory=%RootDirectory%artifacts\pkg 7 | 8 | set BinariesDirectory=%~f1 9 | 10 | if "%BinariesDirectory%"=="" ( 11 | echo ERROR: Binaries directory is not specified 12 | exit /b 1 13 | ) else if not exist "%BinariesDirectory%" ( 14 | echo ERROR: Binaries directory does not exist: %BinariesDirectory% 15 | exit /b 1 16 | ) 17 | 18 | where /q nuget.exe 19 | 20 | if %ERRORLEVEL% neq 0 ( 21 | echo ERROR: nuget.exe is not found in the PATH 22 | exit /b 1 23 | ) 24 | 25 | nuget.exe pack ^ 26 | "%SourcesDirectory%\coredistools\.nuget\Microsoft.NETCore.CoreDisTools.nuspec" ^ 27 | -OutputDirectory "%PackagesDirectory%" ^ 28 | -BasePath %RootDirectory% ^ 29 | -Properties BinariesDirectory="%BinariesDirectory%" ^ 30 | -NonInteractive 31 | 32 | if %ERRORLEVEL% neq 0 ( 33 | echo ERROR: nuget pack exited with code %ERRORLEVEL% 34 | exit /b 1 35 | ) 36 | 37 | exit /b 0 38 | -------------------------------------------------------------------------------- /sample_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "asmdiff" : { 3 | "default": { 4 | "base": "e:\\gh\\runtime2\\artifacts\\bin\\coreclr\\windows.x64.checked", 5 | "diff": "c:\\gh\\runtime\\artifacts\\bin\\coreclr\\windows.x64.checked", 6 | "frameworks": "true", 7 | "output": "c:\\diffs", 8 | "core_root": "c:\\gh\\runtime\\artifacts\\tests\\coreclr\\windows.x64.checked\\Tests\\Core_Root", 9 | "test_root": "c:\\gh\\runtime\\artifacts\\tests\\coreclr\\windows.x64.Release" 10 | }, 11 | "tools": [ 12 | { 13 | "tag": "checked_osx-1439", 14 | "path": "/Users/russellhadley/Work/glue/output/tools/checked_osx-1439/Product/OSX.x64.Checked" 15 | }, 16 | { 17 | "tag": "checked_osx-1442", 18 | "path": "/Users/russellhadley/Work/glue/output/tools/checked_osx-1442/Product/OSX.x64.Checked" 19 | }, 20 | { 21 | "tag": "checked_osx-1443", 22 | "path": "/Users/russellhadley/Work/glue/output/tools/checked_osx-1443/Product/OSX.x64.Checked" 23 | }, 24 | { 25 | "tag": "checked_osx-1526", 26 | "path": "/Users/russellhadley/Work/glue/output/tools/checked_osx-1526/Product/OSX.x64.Checked" 27 | } 28 | ] 29 | }, 30 | "format" : { 31 | "default": { 32 | "arch": "x64", 33 | "build": "Checked", 34 | "os": "windows", 35 | "coreclr": "C:\\gh\\runtime", 36 | "verbose": "true", 37 | "fix": "true", 38 | "untidy": "false" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/AnalyzeAsm/AnalyzeAsm.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net6.0 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/AnalyzeAsm/README.md: -------------------------------------------------------------------------------- 1 | Notes: 2 | 3 | * The code in `Program.cs` has **lot of redundant code**. Most of the methods are copied from previous methods with little tweak. 4 | * All the methods rely on `ngen_arm64.txt` / `ngen_amd64.txt` file that are produced by doing the following: 5 | * `set DOTNET_NGenDisasm=1` 6 | * Running `build-test.cmd crossgen > ngen_arm64.txt` 7 | * The path locations of these files are hardcoded too. 8 | 9 | 10 | `FindLdrGroups_1()` finds patterns: 11 | 12 | ```asm 13 | ldr x1, [x0] 14 | ldr x2, [x0, #8] 15 | ; becomes 16 | ldp x1, x2 [x0] 17 | ``` 18 | 19 | `FindLdrGroups_2()` finds patterns: 20 | ```asm 21 | ldr x1, [fp] 22 | ldr x2, [fp, #8] 23 | ; becomes 24 | ldp x1, x2 [fp] 25 | ``` 26 | 27 | `FindStrGroups_1()` finds patterns: 28 | ```asm 29 | str x1, [x0] 30 | str x2, [x0, #8] 31 | ; becomes 32 | stp x1, x2, [x0] 33 | ``` 34 | 35 | `FindStrGroups_2()` finds patterns: 36 | ```asm 37 | str x1, [fp] 38 | str x2, [fp, #8] 39 | ; becomes 40 | stp x1, x2, [fp] 41 | ``` 42 | 43 | `FindStrGroups_wzr()` finds patterns: 44 | ```asm 45 | str wzr, [x1] 46 | str wzr, [x1, #8] 47 | ; becomes 48 | str xzr, [x1] 49 | ``` 50 | 51 | `FindLdrLdrToMovGroups()` finds patterns: 52 | ```asm 53 | add x0, x0, x1 54 | ldr x1, [x0] 55 | ; becomes 56 | ldr x1, [x0, x1] 57 | ``` 58 | 59 | `FindPostIndexAddrMode1()` finds patterns: 60 | ```asm 61 | ldr x0, [x2] 62 | add x2, x2, #4 63 | ; becomes 64 | ldr x0, [x2], #4 65 | ``` 66 | 67 | `FindPreIndexAddrMode1()` finds patterns: 68 | ```asm 69 | ldr x0, [x2, #4] 70 | add x2, x2, #4 71 | ;becomes 72 | ldr x0, [x2, #4]! 73 | ``` 74 | 75 | `FindPreIndexAddrMode2()` finds patterns: 76 | ```asm 77 | add x2, x2, #4 78 | ldr x0, [x2, #4] 79 | ;becomes 80 | ldr x0, [x2, #4]! 81 | ``` 82 | 83 | `RedundantMovs1()` finds patterns: 84 | ```asm 85 | mov x0, x0 86 | ``` 87 | 88 | `RedundantMovs2()` finds patterns: 89 | ```asm 90 | mov x0, x20 91 | mov x20, x0 92 | ``` 93 | 94 | `RedundantMovs3()` finds patterns: 95 | ```asm 96 | ldr w0, [x0] ; <-- this should zero extend the register so next mov is not needed. 97 | mov w0, w0 98 | ``` 99 | 100 | `AdrpAddPairs()` finds patterns: 101 | ```asm 102 | adrp x11, [RELOC #0x1f40fb92b00] 103 | add x11, x11, #0 104 | adrp x0, [RELOC #0x1f40fb92b00] 105 | add x0, x0, #0 106 | ldr x0, [x0] 107 | ``` 108 | 109 | `ArrayAccess()` finds patterns: 110 | ```asm 111 | sxtw x4, x4 112 | lsl x4, x4, #3 113 | add x4, x4, #16 114 | ``` 115 | 116 | `PrologEpilogInx64()` finds methods that don't have prolog/epilog in x64 117 | 118 | `BasePlusRegisterOffset()` finds patterns: 119 | ```asm 120 | ldr w11, [x11, x1, LSL #2] 121 | ``` 122 | 123 | `FindMovZMovKGroups()` finds groups of `movz/movk` instructions. 124 | 125 | `OptimizeDmbs` finds patterns to remove unnecessary `dmb` instructions. Motivation from clang's [ARMOptimizeBarriersPass](https://github.com/llvm/llvm-project/blob/2946cd701067404b99c39fb29dc9c74bd7193eb3/llvm/lib/Target/ARM/ARMOptimizeBarriersPass.cpp). -------------------------------------------------------------------------------- /src/cijobs/CIClient.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Net.Http; 10 | using System.Net.Http.Headers; 11 | using System.Text.Json; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | 15 | namespace ManagedCodeGen 16 | { 17 | // Wrap CI httpClient with focused APIs for product, job, and build. 18 | // This logic is seperate from listing/copying and just extracts data. 19 | internal sealed class CIClient 20 | { 21 | private HttpClient _client; 22 | 23 | public CIClient(string server) 24 | { 25 | _client = new HttpClient(); 26 | _client.BaseAddress = new Uri(server); 27 | _client.DefaultRequestHeaders.Accept.Clear(); 28 | _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 29 | _client.Timeout = Timeout.InfiniteTimeSpan; 30 | } 31 | 32 | public async Task DownloadProduct(string messageString, string outputPath, string contentPath) 33 | { 34 | Console.WriteLine("Downloading: {0}", messageString); 35 | 36 | HttpResponseMessage response = await _client.GetAsync(messageString); 37 | 38 | bool downloaded = false; 39 | 40 | if (response.IsSuccessStatusCode) 41 | { 42 | var zipPath = Path.Combine(outputPath, Path.GetFileName(contentPath)); 43 | using (var outputStream = System.IO.File.Create(zipPath)) 44 | { 45 | Stream inputStream = await response.Content.ReadAsStreamAsync(); 46 | inputStream.CopyTo(outputStream); 47 | } 48 | downloaded = true; 49 | } 50 | else 51 | { 52 | Console.Error.WriteLine("Zip not found!"); 53 | } 54 | 55 | return downloaded; 56 | } 57 | 58 | public async Task> GetProductJobs(string productName, string branchName) 59 | { 60 | string productString = $"job/{productName}/job/{branchName}/api/json?&tree=jobs[name,url]"; 61 | 62 | try 63 | { 64 | using HttpResponseMessage response = await _client.GetAsync(productString); 65 | 66 | if (response.IsSuccessStatusCode) 67 | { 68 | var json = await response.Content.ReadAsStringAsync(); 69 | var productJobs = JsonSerializer.Deserialize(json); 70 | return productJobs.jobs; 71 | } 72 | } 73 | catch (Exception ex) 74 | { 75 | Console.Error.WriteLine("Error enumerating jobs: {0} {1}", ex.Message, ex.InnerException.Message); 76 | } 77 | 78 | return Enumerable.Empty(); 79 | } 80 | 81 | public async Task> GetJobBuilds(string productName, string branchName, 82 | string jobName, bool lastSuccessfulBuild, int number, string commit) 83 | { 84 | var jobString 85 | = String.Format(@"job/{0}/job/{1}/job/{2}", productName, branchName, jobName); 86 | var messageString 87 | = String.Format("{0}/api/json?&tree=builds[number,url],lastSuccessfulBuild[number,url]", 88 | jobString); 89 | HttpResponseMessage response = await _client.GetAsync(messageString); 90 | 91 | if (response.IsSuccessStatusCode) 92 | { 93 | var json = await response.Content.ReadAsStringAsync(); 94 | var jobBuilds = JsonSerializer.Deserialize(json); 95 | 96 | if (lastSuccessfulBuild) 97 | { 98 | var lastSuccessfulNumber = jobBuilds.lastSuccessfulBuild.number; 99 | jobBuilds.lastSuccessfulBuild.info = await GetJobBuildInfo(productName, branchName, jobName, lastSuccessfulNumber); 100 | return Enumerable.Repeat(jobBuilds.lastSuccessfulBuild, 1); 101 | } 102 | else if (number != 0) 103 | { 104 | var builds = jobBuilds.builds; 105 | 106 | var count = builds.Count(); 107 | for (int i = 0; i < count; i++) 108 | { 109 | var build = builds[i]; 110 | if (build.number == number) 111 | { 112 | build.info = await GetJobBuildInfo(productName, branchName, jobName, build.number); 113 | return Enumerable.Repeat(build, 1); 114 | } 115 | } 116 | return Enumerable.Empty(); 117 | } 118 | else if (commit != null) 119 | { 120 | var builds = jobBuilds.builds; 121 | 122 | var count = builds.Count(); 123 | for (int i = 0; i < count; i++) 124 | { 125 | var build = builds[i]; 126 | build.info = await GetJobBuildInfo(productName, branchName, jobName, build.number); 127 | var actions = build.info.actions.Where(x => x.lastBuiltRevision.SHA1 != null); 128 | foreach (var action in actions) 129 | { 130 | if (action.lastBuiltRevision.SHA1.Equals(commit, StringComparison.OrdinalIgnoreCase)) 131 | { 132 | return Enumerable.Repeat(build, 1); 133 | } 134 | } 135 | } 136 | return Enumerable.Empty(); 137 | } 138 | else 139 | { 140 | var builds = jobBuilds.builds; 141 | 142 | var count = builds.Count(); 143 | for (int i = 0; i < count; i++) 144 | { 145 | var build = builds[i]; 146 | // fill in build info 147 | build.info = await GetJobBuildInfo(productName, branchName, jobName, build.number); 148 | builds[i] = build; 149 | } 150 | 151 | return jobBuilds.builds; 152 | } 153 | } 154 | else 155 | { 156 | return Enumerable.Empty(); 157 | } 158 | } 159 | 160 | public async Task GetJobBuildInfo(string repoName, string branchName, string jobName, int number) 161 | { 162 | string buildString = String.Format("job/{0}/job/{1}/job/{2}/{3}", 163 | repoName, branchName, jobName, number); 164 | string buildMessage = String.Format("{0}/{1}", buildString, 165 | "api/json?&tree=actions[lastBuiltRevision[SHA1]],artifacts[fileName,relativePath],result"); 166 | HttpResponseMessage response = await _client.GetAsync(buildMessage); 167 | 168 | if (response.IsSuccessStatusCode) 169 | { 170 | var buildInfoJson = await response.Content.ReadAsStringAsync(); 171 | var info = JsonSerializer.Deserialize(buildInfoJson); 172 | return info; 173 | } 174 | else 175 | { 176 | return null; 177 | } 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/cijobs/Models.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Collections.Generic; 6 | 7 | namespace ManagedCodeGen 8 | { 9 | internal class Artifact 10 | { 11 | public string fileName { get; set; } 12 | public string relativePath { get; set; } 13 | } 14 | 15 | internal class Revision 16 | { 17 | public string SHA1 { get; set; } 18 | } 19 | 20 | internal class Action 21 | { 22 | public Revision lastBuiltRevision { get; set; } 23 | } 24 | 25 | internal class BuildInfo 26 | { 27 | public List actions { get; set; } 28 | public List artifacts { get; set; } 29 | public string result { get; set; } 30 | } 31 | 32 | internal class Job 33 | { 34 | public string name { get; set; } 35 | public string url { get; set; } 36 | } 37 | 38 | internal class ProductJobs 39 | { 40 | public List jobs { get; set; } 41 | } 42 | 43 | internal class Build 44 | { 45 | public int number { get; set; } 46 | public string url { get; set; } 47 | public BuildInfo info { get; set; } 48 | } 49 | 50 | internal class JobBuilds 51 | { 52 | public List builds { get; set; } 53 | public Build lastSuccessfulBuild { get; set; } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/cijobs/README.md: -------------------------------------------------------------------------------- 1 | # cijobs - List or copy job info/artifacts from the CI to a local machine 2 | 3 | Continuous integration build jobs tool enables the listing of 4 | jobs built in the CI system as well as downloading their artifacts. This 5 | functionality allows for the speed up of some common dev tasks but taking 6 | advantage of work being done in the cloud. 7 | 8 | ###Scenario 1: Start new work. 9 | When beginning a new set of changes, listing 10 | job status can help you find a commit to start your work from. The tool 11 | answers questions like "are the CentOS build jobs passing?" and "what was 12 | the commit hash for the last successful tizen arm32 build?" 13 | 14 | ###Scenario 2: Copy artifacts to speed up development flow. The tool enables 15 | developers to download builds from the cloud so that developers can avoid 16 | rebuilding baseline tools on a local machine. Need the crossgen tool for 17 | the baseline commit for OSX diffs? Cijobs makes this easy to copy to your 18 | system. 19 | 20 | To build/setup: 21 | 22 | * Download dotnet cli. Follow install instructions and get dotnet on your 23 | your path. 24 | * Do 'dotnet restore' to create lock file and 25 | pull down required packages. 26 | * Issue a 'dotnet build' command. Build artifacts will be generated under 27 | a local 'bin' dir. 28 | * cijobs is included in the `build.{cmd|sh}` in the root. Building the whole 29 | repo will install the tool in addition to the other diff utilities in a 30 | bin directory. 31 | -------------------------------------------------------------------------------- /src/cijobs/cijobs.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Exe 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/coreclr_config_download/README.md: -------------------------------------------------------------------------------- 1 | **CoreClr Config Download** 2 | 3 | Download all of the config.xml files for each coreclr job. This will will download into two different folders /base and . 4 | 5 | You can then diff the two folders with whatever your favorite diff tool is. An example on unix would be: 6 | 7 | >$diff -rq /base /diff 8 | 9 | **Requirements** 10 | 11 | In order to grab the config.xml files you will need to authenticate with jenkins and Github. This requires an OAuth token. 12 | 13 | Note that you are able to do this both from the command line (see [these](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/) instructions) and using the web api [here](https://github.com/settings/tokens). 14 | 15 | The minimum access you need is the user information. 16 | 17 | **Usage** 18 | 19 | Please use -h to see the full help. 20 | 21 | ``` 22 | usage: coreclr_config_download.py [-h] [-api_token [API_TOKEN]] 23 | [-branch [BRANCH]] 24 | [-output_location [OUTPUT_LOCATION]] 25 | [-sim_connections [SIM_CONNECTIONS]] 26 | [-username [USERNAME]] [--baseline_only] 27 | [--diff_only] 28 | 29 | Simple script to grab all of the config.xml files for jobs. Note that the 30 | api_token can either be the token or the path to a file containing the token. 31 | If you are having a problem with connecting try passing the, download basline 32 | only then download the diff only after waiting a little. Github will 33 | unforuntately rate limit OAuth connections. 34 | 35 | optional arguments: 36 | -h, --help show this help message and exit 37 | -api_token [API_TOKEN] 38 | Github API Token. Can be the path to a file or string. 39 | -branch [BRANCH] 40 | -output_location [OUTPUT_LOCATION] 41 | -sim_connections [SIM_CONNECTIONS] 42 | The amount of parrallel conntections to launch. 43 | -username [USERNAME] Github username for the API Token. 44 | --baseline_only Download the baseline config files only. 45 | --diff_only Download the diff config files only. 46 | ``` 47 | 48 | **Example usage** 49 | 50 | >$./coreclr_config_download.py -branch master -output_location ~/output -username jashook -api_token ~/jenkins_api_token 51 | 52 | >$./coreclr_config_download.py -branch master -output_location ~/output -username jashook -api_token ~/jenkins_api_token --baseline_only 53 | 54 | >$./coreclr_config_download.py -branch master -output_location ~/output -username jashook -api_token ~/jenkins_api_token --diff_only 55 | 56 | **Caveats** 57 | 58 | Each GET Request to download the config.xml must authenticate. Therefore, after around 2k GET Requests Github will start rate limiting the requests. From what it looks like it will require you to wait about an hour before being able to authenticate you from jenkins. 59 | 60 | To work around this annoying problem the script has a --baseline_only and --diff_only option. Which will allow you to download the --baseline_only, wait then download --diff_only. -------------------------------------------------------------------------------- /src/coredistools/.nuget/Microsoft.NETCore.CoreDisTools.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Microsoft.NETCore.CoreDisTools 5 | 1.5.0 6 | Microsoft.NETCore Instruction-wise Disassembler 7 | Microsoft 8 | Microsoft 9 | LICENSE.TXT 10 | https://github.com/dotnet/jitutils 11 | true 12 | Disassembly Tools for dotnet/runtime 13 | Initial release 14 | Copyright © Microsoft Corporation 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/coredistools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(coredistools) 2 | 3 | set(LLVM_LINK_COMPONENTS 4 | AllTargetsAsmParsers 5 | AllTargetsDescs 6 | AllTargetsDisassemblers 7 | AllTargetsInfos 8 | MC 9 | Support) 10 | 11 | set(SOURCES coredistools.cpp) 12 | 13 | set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/coredistools.exports) 14 | 15 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 16 | 17 | add_llvm_library(coredistools SHARED ${SOURCES} DEPENDS intrinsics_gen) 18 | 19 | install(TARGETS coredistools LIBRARY DESTINATION bin COMPONENT coredistools 20 | ARCHIVE DESTINATION lib COMPONENT coredistools) 21 | -------------------------------------------------------------------------------- /src/coredistools/coredistools.exports: -------------------------------------------------------------------------------- 1 | InitDisasm 2 | InitBufferedDisasm 3 | NewDisasm 4 | FinishDisasm 5 | InitBufferedDiffer 6 | NewDiffer 7 | NewDiffer2 8 | FinishDiff 9 | DisasmInstruction 10 | DumpInstruction 11 | NearDiffCodeBlocks 12 | DumpCodeBlock 13 | DumpDiffBlocks 14 | GetOutputBuffer 15 | ClearOutputBuffer 16 | -------------------------------------------------------------------------------- /src/coredistools/coredistools.h: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | //===--------- coredistools.h - Disassembly tools for CoreClr ------------=== 5 | // Core Disassembly Tools API 6 | // Disassembly tools required by CoreCLR for utilities like 7 | // GCStress, SuperPMI, and R2RDump. 8 | //===---------------------------------------------------------------------=== 9 | 10 | #if !defined(_COREDISTOOLS_H_) 11 | #define _COREDISTOOLS_H_ 12 | 13 | #include 14 | 15 | #if defined(__cplusplus) 16 | #define EXTERN_C extern "C" 17 | #else 18 | #define EXTERN_C 19 | #endif // defined(__cplusplus) 20 | 21 | #if defined(_MSC_VER) 22 | #if defined(DllInterfaceExporter) 23 | #define DllIface EXTERN_C __declspec(dllexport) 24 | #else 25 | #define DllIface EXTERN_C __declspec(dllimport) 26 | #endif // defined(DllInterfaceExporter) 27 | #else 28 | #if !defined(__cdecl) 29 | #if defined(__i386__) 30 | #define __cdecl __attribute__((cdecl)) 31 | #else 32 | #define __cdecl 33 | #endif 34 | #endif 35 | #define DllIface EXTERN_C 36 | #endif // defined(_MSC_VER) 37 | 38 | enum TargetArch { 39 | Target_Host, // Target is the same as host architecture 40 | Target_X86, 41 | Target_X64, 42 | Target_Thumb, 43 | Target_Arm64, 44 | Target_LoongArch64, 45 | Target_RiscV64, 46 | }; 47 | 48 | struct CorDisasm; 49 | struct CorAsmDiff; 50 | 51 | // The custom print functionality to be provide by the 52 | // users of this Library 53 | typedef void(__cdecl *Printer)(const char *msg, ...); 54 | struct PrintControl { 55 | const Printer Error; 56 | const Printer Warning; 57 | const Printer Log; 58 | const Printer Dump; 59 | }; 60 | 61 | // The type of a custom function provided by the user to determine 62 | // if two offsets are considered equivalent wrt diffing code blocks. 63 | // Offset1 and Offset2 are the two offsets to be compared. 64 | // BlockOffset is the offset of the instructions (that contain Offset1 65 | // and Offset2) from the beginning of their respective code blocks. 66 | // InstructionLength is the length of the current instruction being 67 | // compared for equivalency. 68 | typedef bool(__cdecl *OffsetComparator)(const void *UserData, size_t BlockOffset, 69 | size_t InstructionLength, uint64_t Offset1, 70 | uint64_t Offset2); 71 | 72 | // If an OffsetMunger function is defined, it is called before the OffsetComparator. 73 | // If it returns `true` then: 74 | // 1. the instructions are considered equivalent 75 | // 2. the offsets have been decoded and "munged" (changed), and 76 | // *Offset1 and *Offset2 are set to the values to use. 77 | // 3. *SkipInstructions1 instructions in code stream 1 are skipped 78 | // 4. *SkipInstructions2 instructions in code stream 2 are skipped 79 | // 80 | // This is typically used on arm32 to treat "movw/movt" as a single instruction 81 | // generating a single constant. Similarly, for arm64 mov/movk/movk/movk sequences. 82 | typedef bool(__cdecl *OffsetMunger)(const void *UserData, size_t BlockOffset, 83 | size_t InstructionLength, uint64_t* Offset1, uint64_t* Offset2, 84 | uint32_t* SkipInstructions1, uint32_t* SkipInstructions2); 85 | 86 | // The Export/Import definitions for CoreDistools library are defined below. 87 | // A typedef for each interface function's type is defined in order to aid 88 | // the importer. 89 | 90 | // Initialize the disassembler, using default print controls 91 | typedef CorDisasm * __cdecl InitDisasm_t(enum TargetArch Target); 92 | DllIface InitDisasm_t InitDisasm; 93 | 94 | // Initialize the disassembler, using buffered print controls 95 | typedef CorDisasm * __cdecl InitBufferedDisasm_t(enum TargetArch Target); 96 | DllIface InitBufferedDisasm_t InitBufferedDisasm; 97 | 98 | // Initialize the disassembler using custom print controls 99 | typedef CorDisasm * __cdecl NewDisasm_t(enum TargetArch Target, 100 | const PrintControl *PControl); 101 | DllIface NewDisasm_t NewDisasm; 102 | 103 | // Delete the disassembler 104 | typedef void __cdecl FinishDisasm_t(const CorDisasm *Disasm); 105 | DllIface FinishDisasm_t FinishDisasm; 106 | 107 | // Initialize a code differ using buffered output. 108 | typedef CorDisasm * __cdecl InitBufferedDiffer_t(enum TargetArch Target, 109 | const OffsetComparator Comparator); 110 | DllIface InitBufferedDiffer_t InitBufferedDiffer; 111 | 112 | // Initialize the Code Differ 113 | typedef CorAsmDiff * __cdecl NewDiffer_t(enum TargetArch Target, 114 | const PrintControl *PControl, 115 | const OffsetComparator Comparator); 116 | DllIface NewDiffer_t NewDiffer; 117 | 118 | // Initialize the Code Differ, with an offset munger. 119 | typedef CorAsmDiff * __cdecl NewDiffer2_t(enum TargetArch Target, 120 | const PrintControl *PControl, 121 | const OffsetComparator Comparator, 122 | const OffsetMunger Munger); 123 | DllIface NewDiffer2_t NewDiffer2; 124 | 125 | // Delete the Code Differ 126 | typedef void __cdecl FinishDiff_t(const CorAsmDiff *AsmDiff); 127 | DllIface FinishDiff_t FinishDiff; 128 | 129 | // DisasmInstruction -- Disassemble one instruction 130 | // Arguments: 131 | // Disasm -- The Disassembler 132 | // Address -- The address at which the bytes of the instruction 133 | // are intended to execute 134 | // Bytes -- Pointer to the actual bytes which need to be disassembled 135 | // MaxLength -- Number of bytes available in Bytes buffer 136 | // Returns: 137 | // -- The Size of the disassembled instruction 138 | // -- Zero on failure 139 | typedef size_t __cdecl DisasmInstruction_t(const CorDisasm *Disasm, 140 | const uint8_t *Address, 141 | const uint8_t *Bytes, size_t Maxlength); 142 | DllIface DisasmInstruction_t DisasmInstruction; 143 | 144 | // DumpInstruction -- Disassemble one instruction and output it 145 | // Arguments: 146 | // Disasm -- The Disassembler 147 | // Address -- The address at which the bytes of the instruction 148 | // are intended to execute 149 | // Bytes -- Pointer to the actual bytes which need to be disassembled 150 | // MaxLength -- Number of bytes available in Bytes buffer 151 | // Returns: 152 | // -- The Size of the disassembled instruction 153 | // -- Zero on failure 154 | typedef size_t __cdecl DumpInstruction_t(const CorDisasm *Disasm, 155 | const uint8_t *Address, const uint8_t *Bytes, 156 | size_t Maxlength); 157 | DllIface DumpInstruction_t DumpInstruction; 158 | 159 | // NearDiffCodeBlocks -- Compare two code blocks for semantic 160 | // equivalence 161 | // Arguments: 162 | // AsmDiff -- The Asm-differ 163 | // UserData -- Any data the user wishes to pass through into 164 | // the OffsetComparator/OffsetMunger 165 | // Address1 -- Address at which first block will execute 166 | // Bytes1 -- Pointer to the actual bytes of the first block 167 | // Size1 -- The size of the first block 168 | // Address2 -- Address at which second block will execute 169 | // Bytes2 -- Pointer to the actual bytes of the second block 170 | // Size2 -- The size of the second block 171 | // Returns: 172 | // -- true if the two blocks are equivalent, false if not. 173 | typedef bool __cdecl NearDiffCodeBlocks_t(const CorAsmDiff *AsmDiff, 174 | const void *UserData, 175 | const uint8_t *Address1, 176 | const uint8_t *Bytes1, size_t Size1, 177 | const uint8_t *Address2, 178 | const uint8_t *Bytes2, size_t Size2); 179 | DllIface NearDiffCodeBlocks_t NearDiffCodeBlocks; 180 | 181 | // Print a code block according to the Disassembler's Print Controls 182 | typedef void __cdecl DumpCodeBlock_t(const CorDisasm *Disasm, const uint8_t *Address, 183 | const uint8_t *Bytes, size_t Size); 184 | DllIface DumpCodeBlock_t DumpCodeBlock; 185 | 186 | // Print the two code blocks being diffed, according to 187 | // AsmDiff's PrintControls. 188 | typedef void __cdecl DumpDiffBlocks_t(const CorAsmDiff *AsmDiff, 189 | const uint8_t *Address1, const uint8_t *Bytes1, 190 | size_t Size1, const uint8_t *Address2, 191 | const uint8_t *Bytes2, size_t Size2); 192 | DllIface DumpDiffBlocks_t DumpDiffBlocks; 193 | 194 | // Get a pointer to the buffered output buffer. 195 | typedef const char* __cdecl GetOutputBuffer_t(); 196 | DllIface GetOutputBuffer_t GetOutputBuffer; 197 | 198 | // Clear the buffered output buffer. 199 | typedef void __cdecl ClearOutputBuffer_t(); 200 | DllIface ClearOutputBuffer_t ClearOutputBuffer; 201 | 202 | #endif // !defined(_COREDISTOOLS_H_) 203 | -------------------------------------------------------------------------------- /src/instructions-retired-explorer/README.md: -------------------------------------------------------------------------------- 1 | ### Instructions Retired Explorer 2 | 3 | Instructions Retired Explorer is a tool to parse ETW files like those produced by BenchmarkDotNet (aka BDN) (via `-p ETW`) or PerfView. 4 | 5 | It understands profile, jit, and method events, and can attribute profile or PMU 6 | samples to jitted code. 7 | 8 | It also understands BDN's profiling events, and can filter profiles to just those taken when BDN is actively measuring performance (that is, it will ignore the various warmup and overhead phases, as well as time spent within BDN itself). 9 | 10 | ### Usage 11 | 12 | ``` 13 | dotnet run -- file.etl [-process process-name] [-pid pid] [-show-events] [-show-jit-times] [-benchmark] [-instructions-retired] 14 | 15 | -process: defaults to corerun 16 | -pid: choose process to summarize via ID 17 | -benchmark: only count samples made during BechmarkDotNet intervals. Changes default process to dotnet 18 | -show-events: show counts of raw ETL events 19 | -show-jit-times: summarize data on time spent jitting 20 | -show-samples : show raw method-relative hits for some methods 21 | -instructions-retired: if ETL has instructions retired events, summarize those instead of profile samples 22 | ``` 23 | 24 | ### Sample Output 25 | 26 | This shows some output from analyzing a BDN produced file, with `-benchmark`: 27 | 28 | ``` 29 | Samples for corerun: 6830 events for Benchmark Intervals 30 | Jitting : 01.66% 6.4E+05 samples 1507 methods 31 | JitInterface : 00.78% 3E+05 samples 32 | Jit-generated code: 83.95% 3.23E+07 samples 33 | Jitted code : 83.95% 3.23E+07 samples 34 | MinOpts code : 00.00% 0 samples 35 | FullOpts code : 00.81% 3.1E+05 samples 36 | Tier-0 code : 00.00% 0 samples 37 | Tier-1 code : 83.14% 3.2E+07 samples 38 | R2R code : 00.00% 0 samples 39 | 40 | 02.13% 8.2E+05 ? Unknown 41 | 42.38% 1.629E+07 Tier-1 [System.Private.CoreLib]DateTimeFormat.FormatCustomized(value class System.DateTime,value class System.ReadOnlySpan`1,class System.Globalization.DateTimeFormatInfo,value class System.TimeSpan,value class System.Collections.Generic.ValueListBuilder`1&) 42 | 19.30% 7.42E+06 Tier-1 [System.Private.CoreLib]DateTimeFormat.FormatDigits(value class System.Collections.Generic.ValueListBuilder`1&,int32,int32) 43 | 11.81% 4.54E+06 native coreclr.dll 44 | 09.26% 3.56E+06 Tier-1 [System.Private.CoreLib]DateTimeFormat.Format(value class System.DateTime,class System.String,class System.IFormatProvider,value class System.TimeSpan) 45 | 04.37% 1.68E+06 Tier-1 [System.Private.CoreLib]System.Collections.Generic.ValueListBuilder`1[System.Char].AppendMultiChar(value class System.ReadOnlySpan`1) 46 | 03.23% 1.24E+06 Tier-1 [System.Private.CoreLib]Buffer.Memmove(unsigned int8&,unsigned int8&,unsigned int) 47 | 01.61% 6.2E+05 Tier-1 [System.Private.CoreLib]System.ReadOnlySpan`1[System.Char].ToString() 48 | 01.27% 4.9E+05 Tier-1 [System.Private.CoreLib]String.Ctor(value class System.ReadOnlySpan`1) 49 | 01.17% 4.5E+05 Tier-1 [System.Private.CoreLib]DateTimeFormat.ExpandStandardFormatToCustomPattern(wchar,class System.Globalization.DateTimeFormatInfo) 50 | 00.88% 3.4E+05 native clrjit.dll 51 | 00.81% 3.1E+05 FullOpt [d0c2a6e2-c859-4adf-aa32-e1950c899716]Runnable_0.WorkloadActionUnroll(int64) 52 | 00.62% 2.4E+05 native ntoskrnl.exe 53 | 00.55% 2.1E+05 Tier-1 [MicroBenchmarks]Perf_DateTime.ToString(class System.String) 54 | 00.39% 1.5E+05 native ntdll.dll 55 | 00.13% 5E+04 native intelppm.sys 56 | 00.05% 2E+04 native KernelBase.dll 57 | 58 | Benchmark: found 15 intervals; mean interval 252.122ms 59 | 000 3243.972 -- 3506.304 : 262.332 60 | 001 3508.081 -- 3766.636 : 258.554 61 | 002 3768.304 -- 4027.688 : 259.384 62 | 003 4029.104 -- 4275.982 : 246.878 63 | 004 4277.706 -- 4529.997 : 252.291 64 | 005 4531.510 -- 4781.650 : 250.140 65 | 006 4783.191 -- 5032.090 : 248.899 66 | 007 5033.857 -- 5283.478 : 249.621 67 | 008 5285.356 -- 5538.937 : 253.581 68 | 009 5540.676 -- 5791.375 : 250.699 69 | 010 5792.768 -- 6044.684 : 251.916 70 | 011 6046.395 -- 6295.090 : 248.694 71 | 012 6296.746 -- 6547.423 : 250.677 72 | 013 6549.081 -- 6796.750 : 247.669 73 | 014 6798.383 -- 7048.879 : 250.496 74 | ``` 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/instructions-retired-explorer/instructions-retired-explorer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Exe 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/jit-analyze/IEnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | 9 | namespace ManagedCodeGen 10 | { 11 | // Allow Linq to be able to sum up MetricCollections 12 | public static class IEnumerableExtensions 13 | { 14 | public static MetricCollection Sum(this IEnumerable source) 15 | { 16 | MetricCollection result = new MetricCollection(); 17 | 18 | foreach (MetricCollection s in source) 19 | { 20 | result.Add(s); 21 | } 22 | 23 | return result; 24 | } 25 | 26 | public static MetricCollection Sum(this IEnumerable source, Func selector) 27 | { 28 | return source.Select(x => selector(x)).Sum(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/jit-analyze/JitAnalyzeRootCommand.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.CommandLine; 8 | using System.Globalization; 9 | 10 | namespace ManagedCodeGen 11 | { 12 | internal sealed class JitAnalyzeRootCommand : RootCommand 13 | { 14 | public Option BasePath { get; } = 15 | new("--base", "-b") { Description = "Base file or directory" }; 16 | public Option DiffPath { get; } = 17 | new("--diff", "-d") { Description = "Diff file or directory" }; 18 | public Option Recursive { get; } = 19 | new("--recursive", "-r") { Description = "Search directories recursively" }; 20 | public Option FileExtension { get; } = 21 | new("--file-extension") { DefaultValueFactory = _ => ".dasm", Description = "File extension to look for" }; 22 | public Option Count { get; } = 23 | new("--count", "-c") { DefaultValueFactory = _ => 20, Description = "Count of files and methods (at most) to output in the summary. (count) improvements and (count) regressions of each will be included" }; 24 | public Option Warn { get; } = 25 | new("--warn", "-w") { Description = "Generate warning output for files/methods that only exists in one dataset or the other (only in base or only in diff)" }; 26 | public Option> Metrics { get; } = 27 | new("--metrics", "-m") { DefaultValueFactory = _ => new List { "CodeSize" }, Description = $"Metrics to use for diff computations. Available metrics: {MetricCollection.ListMetrics()}" }; 28 | public Option Note { get; } = 29 | new("--note") { Description = "Descriptive note to add to summary output" }; 30 | public Option NoReconcile { get; } = 31 | new("--no-reconcile") { Description = "Do not reconcile unique methods in base/diff" }; 32 | public Option Json { get; } = 33 | new("--json") { Description = "Dump analysis data to specified file in JSON format" }; 34 | public Option Tsv { get; } = 35 | new("--tsv") { Description = "Dump analysis data to specified file in tab-separated format" }; 36 | public Option MD { get; } = 37 | new("--md") { Description = "Dump analysis data to specified file in markdown format" }; 38 | public Option Filter { get; } = 39 | new("--filter") { Description = "Only consider assembly files whose names match the filter" }; 40 | public Option SkipTextDiff { get; } = 41 | new("--skip-text-diff") { Description = "Skip analysis that checks for files that have textual diffs but no metric diffs" }; 42 | public Option RetainOnlyTopFiles { get; } = 43 | new("--retain-only-top-files") { Description = "Retain only the top 'count' improvements/regressions .dasm files. Delete other files. Useful in CI scenario to reduce the upload size" }; 44 | public Option OverrideTotalBaseMetric { get; } = 45 | new("--override-total-base-metric") { CustomParser = result => 46 | { 47 | string optionValue = result.Tokens[0].Value; 48 | if (double.TryParse(optionValue, NumberStyles.Any, CultureInfo.InvariantCulture, out var parsedValue)) 49 | return parsedValue; 50 | 51 | result.AddError($"Cannot parse argument '{optionValue}' for option '--override-total-base-metric' as expected type '{typeof(double).FullName}'."); 52 | return 0; 53 | }, Description = "Override the total base metric shown in the output with this value. Useful when only changed .dasm files are present and these values are known" }; 54 | public Option OverrideTotalDiffMetric { get; } = 55 | new("--override-total-diff-metric") { CustomParser = result => 56 | { 57 | string optionValue = result.Tokens[0].Value; 58 | if (double.TryParse(optionValue, NumberStyles.Any, CultureInfo.InvariantCulture, out var parsedValue)) 59 | return parsedValue; 60 | 61 | result.AddError($"Cannot parse argument '{optionValue}' for option '--override-total-diff-metric' as expected type '{typeof(double).FullName}'."); 62 | return 0; 63 | }, Description = "Override the total diff metric shown in the output with this value. Useful when only changed .dasm files are present and these values are known" }; 64 | public Option IsDiffsOnly { get; } = 65 | new("--is-diffs-only") { Description = "Specify that the disassembly files are only produced for contexts with diffs, so avoid producing output making assumptions about the number of contexts" }; 66 | public Option IsSubsetOfDiffs { get; } = 67 | new("--is-subset-of-diffs") { Description = "Specify that the disassembly files are only a subset of the contexts with diffs, so avoid producing output making assumptions about the remaining diffs" }; 68 | public Option ConcatFiles { get; } = 69 | new("--concat-files") { Description = "Consider all files in the base and diff to be part of the same logical unit of functions" }; 70 | 71 | public ParseResult Result; 72 | 73 | public JitAnalyzeRootCommand(string[] args) : base("Compare and analyze `*.dasm` files from baseline/diff") 74 | { 75 | Options.Add(BasePath); 76 | Options.Add(DiffPath); 77 | Options.Add(Recursive); 78 | Options.Add(FileExtension); 79 | Options.Add(Count); 80 | Options.Add(Warn); 81 | Options.Add(Metrics); 82 | Options.Add(Note); 83 | Options.Add(NoReconcile); 84 | Options.Add(Json); 85 | Options.Add(Tsv); 86 | Options.Add(MD); 87 | Options.Add(Filter); 88 | Options.Add(SkipTextDiff); 89 | Options.Add(RetainOnlyTopFiles); 90 | Options.Add(OverrideTotalBaseMetric); 91 | Options.Add(OverrideTotalDiffMetric); 92 | Options.Add(IsDiffsOnly); 93 | Options.Add(IsSubsetOfDiffs); 94 | Options.Add(ConcatFiles); 95 | 96 | SetAction(result => 97 | { 98 | Result = result; 99 | 100 | try 101 | { 102 | List errors = new(); 103 | if (Result.GetValue(BasePath) == null) 104 | { 105 | errors.Add("Base path (--base) is required"); 106 | } 107 | 108 | if (Result.GetValue(DiffPath) == null) 109 | { 110 | errors.Add("Diff path (--diff) is required"); 111 | } 112 | 113 | foreach (string metricName in Result.GetValue(Metrics)) 114 | { 115 | if (!MetricCollection.ValidateMetric(metricName)) 116 | { 117 | errors.Add($"Unknown metric '{metricName}'. Available metrics: {MetricCollection.ListMetrics()}"); 118 | } 119 | } 120 | 121 | if ((Result.GetResult(OverrideTotalBaseMetric) == null) != (Result.GetResult(OverrideTotalDiffMetric) == null)) 122 | { 123 | errors.Add("override-total-base-metric and override-total-diff-metric must either both be specified or both not be specified"); 124 | } 125 | 126 | if (errors.Count > 0) 127 | { 128 | throw new Exception(string.Join(Environment.NewLine, errors)); 129 | } 130 | 131 | return new Program(this).Run(); 132 | } 133 | catch (Exception e) 134 | { 135 | Console.ResetColor(); 136 | Console.ForegroundColor = ConsoleColor.Red; 137 | 138 | Console.Error.WriteLine("Error: " + e.Message); 139 | Console.Error.WriteLine(e.ToString()); 140 | 141 | Console.ResetColor(); 142 | 143 | return 1; 144 | } 145 | }); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/jit-analyze/MetricCollection.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Text; 9 | using System.Text.Json.Serialization; 10 | 11 | namespace ManagedCodeGen 12 | { 13 | public class MetricCollection 14 | { 15 | private static Dictionary s_metricNameToIndex; 16 | private static Metric[] s_metrics; 17 | 18 | static MetricCollection() 19 | { 20 | var derivedType = typeof(Metric); 21 | var currentAssembly = Assembly.GetAssembly(derivedType); 22 | s_metrics = currentAssembly.GetTypes() 23 | .Where(t => t != derivedType && derivedType.IsAssignableFrom(t)) 24 | .Select(t => currentAssembly.CreateInstance(t.FullName)).Cast().ToArray(); 25 | 26 | s_metricNameToIndex = new Dictionary(s_metrics.Length); 27 | 28 | for (int i = 0; i < s_metrics.Length; i++) 29 | { 30 | Metric m = s_metrics[i]; 31 | s_metricNameToIndex[m.Name] = i; 32 | } 33 | } 34 | 35 | [JsonInclude] 36 | private Metric[] metrics; 37 | 38 | public MetricCollection() 39 | { 40 | metrics = new Metric[s_metrics.Length]; 41 | for (int i = 0; i < s_metrics.Length; i++) 42 | { 43 | metrics[i] = s_metrics[i].Clone(); 44 | } 45 | } 46 | 47 | public MetricCollection(MetricCollection other) : this() 48 | { 49 | this.SetValueFrom(other); 50 | } 51 | 52 | public static IEnumerable AllMetrics => s_metrics; 53 | 54 | public Metric GetMetric(string metricName) 55 | { 56 | int index; 57 | if (s_metricNameToIndex.TryGetValue(metricName, out index)) 58 | { 59 | return metrics[index]; 60 | } 61 | return null; 62 | } 63 | 64 | public static bool ValidateMetric(string name) 65 | { 66 | return s_metricNameToIndex.TryGetValue(name, out _); 67 | } 68 | 69 | public static string DisplayName(string metricName) 70 | { 71 | int index; 72 | if (s_metricNameToIndex.TryGetValue(metricName, out index)) 73 | { 74 | return s_metrics[index].DisplayName; 75 | } 76 | return "Unknown metric"; 77 | } 78 | 79 | public static string ListMetrics() 80 | { 81 | StringBuilder sb = new StringBuilder(); 82 | bool isFirst = true; 83 | foreach (string s in s_metricNameToIndex.Keys) 84 | { 85 | if (!isFirst) sb.Append(", "); 86 | sb.Append(s); 87 | isFirst = false; 88 | } 89 | return sb.ToString(); 90 | } 91 | 92 | public override string ToString() 93 | { 94 | StringBuilder sb = new StringBuilder(); 95 | bool isFirst = true; 96 | foreach (Metric m in metrics) 97 | { 98 | if (!isFirst) sb.Append(", "); 99 | sb.Append($"{m.Name} {m.Unit} {m.ValueString}"); 100 | isFirst = false; 101 | } 102 | return sb.ToString(); 103 | } 104 | 105 | public void Add(MetricCollection other) 106 | { 107 | for (int i = 0; i < metrics.Length; i++) 108 | { 109 | metrics[i].Add(other.metrics[i]); 110 | } 111 | } 112 | 113 | public void Add(string metricName, double value) 114 | { 115 | Metric m = GetMetric(metricName); 116 | m.Value += value; 117 | } 118 | 119 | public void Sub(MetricCollection other) 120 | { 121 | for (int i = 0; i < metrics.Length; i++) 122 | { 123 | metrics[i].Sub(other.metrics[i]); 124 | } 125 | } 126 | 127 | public void Rel(MetricCollection other) 128 | { 129 | for (int i = 0; i < metrics.Length; i++) 130 | { 131 | metrics[i].Rel(other.metrics[i]); 132 | } 133 | } 134 | 135 | public void SetValueFrom(MetricCollection other) 136 | { 137 | for (int i = 0; i < metrics.Length; i++) 138 | { 139 | metrics[i].SetValueFrom(other.metrics[i]); 140 | } 141 | } 142 | 143 | public bool IsZero() 144 | { 145 | for (int i = 0; i < metrics.Length; i++) 146 | { 147 | if (metrics[i].Value != 0) return false; 148 | } 149 | return true; 150 | } 151 | } 152 | } -------------------------------------------------------------------------------- /src/jit-analyze/Metrics.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace ManagedCodeGen 6 | { 7 | public abstract class Metric 8 | { 9 | public virtual string Name { get; } 10 | public virtual string DisplayName { get; } 11 | public virtual string Unit { get; } 12 | public virtual bool LowerIsBetter { get; } 13 | public abstract Metric Clone(); 14 | public abstract string ValueString { get; } 15 | public double Value { get; set; } 16 | 17 | public void Add(Metric m) 18 | { 19 | Value += m.Value; 20 | } 21 | 22 | public void Sub(Metric m) 23 | { 24 | Value -= m.Value; 25 | } 26 | 27 | public void Rel(Metric m) 28 | { 29 | Value = (Value - m.Value) / m.Value; 30 | } 31 | 32 | public void SetValueFrom(Metric m) 33 | { 34 | Value = m.Value; 35 | } 36 | 37 | public override string ToString() 38 | { 39 | return Name; 40 | } 41 | } 42 | 43 | public class CodeSizeMetric : Metric 44 | { 45 | public override string Name => "CodeSize"; 46 | public override string DisplayName => "Code Size"; 47 | public override string Unit => "byte"; 48 | public override bool LowerIsBetter => true; 49 | public override Metric Clone() => new CodeSizeMetric(); 50 | public override string ValueString => $"{Value}"; 51 | } 52 | 53 | public class PrologSizeMetric : Metric 54 | { 55 | public override string Name => "PrologSize"; 56 | public override string DisplayName => "Prolog Size"; 57 | public override string Unit => "byte"; 58 | public override bool LowerIsBetter => true; 59 | public override Metric Clone() => new PrologSizeMetric(); 60 | public override string ValueString => $"{Value}"; 61 | } 62 | 63 | public class PerfScoreMetric : Metric 64 | { 65 | public override string Name => "PerfScore"; 66 | public override string DisplayName => "Perf Score"; 67 | public override string Unit => "PerfScoreUnit"; 68 | public override bool LowerIsBetter => true; 69 | public override Metric Clone() => new PerfScoreMetric(); 70 | public override string ValueString => $"{Value:F2}"; 71 | } 72 | 73 | public class InstrCountMetric : Metric 74 | { 75 | public override string Name => "InstrCount"; 76 | public override string DisplayName => "Instruction Count"; 77 | public override string Unit => "Instruction"; 78 | public override bool LowerIsBetter => true; 79 | public override Metric Clone() => new InstrCountMetric(); 80 | public override string ValueString => $"{Value}"; 81 | } 82 | 83 | public class AllocSizeMetric : Metric 84 | { 85 | public override string Name => "AllocSize"; 86 | public override string DisplayName => "Allocation Size"; 87 | public override string Unit => "byte"; 88 | public override bool LowerIsBetter => true; 89 | public override Metric Clone() => new AllocSizeMetric(); 90 | public override string ValueString => $"{Value}"; 91 | } 92 | 93 | public class ExtraAllocBytesMetric : Metric 94 | { 95 | public override string Name => "ExtraAllocBytes"; 96 | public override string DisplayName => "Extra Allocation Size"; 97 | public override string Unit => "byte"; 98 | public override bool LowerIsBetter => true; 99 | public override Metric Clone() => new ExtraAllocBytesMetric(); 100 | public override string ValueString => $"{Value}"; 101 | } 102 | public class DebugClauseMetric : Metric 103 | { 104 | public override string Name => "DebugClauseCount"; 105 | public override string DisplayName => "Debug Clause Count"; 106 | public override string Unit => "Clause"; 107 | public override bool LowerIsBetter => true; 108 | public override Metric Clone() => new DebugClauseMetric(); 109 | public override string ValueString => $"{Value}"; 110 | } 111 | 112 | public class DebugVarMetric : Metric 113 | { 114 | public override string Name => "DebugVarCount"; 115 | public override string DisplayName => "Debug Variable Count"; 116 | public override string Unit => "Variable"; 117 | public override bool LowerIsBetter => true; 118 | public override Metric Clone() => new DebugVarMetric(); 119 | public override string ValueString => $"{Value}"; 120 | } 121 | 122 | /* LSRA specific */ 123 | public class SpillCountMetric : Metric 124 | { 125 | public override string Name => "SpillCount"; 126 | public override string DisplayName => "Spill Count"; 127 | public override string Unit => "Count"; 128 | public override bool LowerIsBetter => true; 129 | public override Metric Clone() => new SpillCountMetric(); 130 | public override string ValueString => $"{Value}"; 131 | } 132 | 133 | public class SpillWeightMetric : Metric 134 | { 135 | public override string Name => "SpillWeight"; 136 | public override string DisplayName => "Spill Weighted"; 137 | public override string Unit => "Count"; 138 | public override bool LowerIsBetter => true; 139 | public override Metric Clone() => new SpillWeightMetric(); 140 | public override string ValueString => $"{Value}"; 141 | } 142 | 143 | public class ResolutionCountMetric : Metric 144 | { 145 | public override string Name => "ResolutionCount"; 146 | public override string DisplayName => "Resolution Count"; 147 | public override string Unit => "Count"; 148 | public override bool LowerIsBetter => true; 149 | public override Metric Clone() => new ResolutionCountMetric(); 150 | public override string ValueString => $"{Value}"; 151 | } 152 | 153 | public class ResolutionWeightMetric : Metric 154 | { 155 | public override string Name => "ResolutionWeight"; 156 | public override string DisplayName => "Resolution Weighted"; 157 | public override string Unit => "Count"; 158 | public override bool LowerIsBetter => true; 159 | public override Metric Clone() => new ResolutionWeightMetric(); 160 | public override string ValueString => $"{Value}"; 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/jit-analyze/README.md: -------------------------------------------------------------------------------- 1 | # jit-analyze - Managed CodeGen difference analysis tool 2 | 3 | jit-analyze is a utility to provide feedback on generated disassembly. 4 | The tool will produce the total bytes of difference and list of files 5 | and methods sorted by contribution (size in bytes of regression/improvement) 6 | 7 | To build/setup: 8 | 9 | * Download dotnet cli. Follow install instructions and get dotnet on your 10 | your path. 11 | * Follow publish directions for the jitutils repo in the root. This will 12 | put the tools on your path. 13 | * Generate corediff disasm run. See [Getting Started](../../doc/getstarted.md) 14 | for directions how. 15 | * Run analyze --base `` --diff `` to produce a summary of the 16 | differences. 17 | 18 | The output of analyze looks like the following: 19 | ``` 20 | $ jit-analyze --base ~/Work/output/base --diff ~/Work/output/diff 21 | 22 | (Note: Lower is better) 23 | 24 | Total bytes of diff: -4124 25 | diff is an improvement. 26 | 27 | Top file regressions by size (bytes): 28 | 193 : Microsoft.CodeAnalysis.dasm 29 | 154 : System.Dynamic.Runtime.dasm 30 | 60 : System.IO.Compression.dasm 31 | 43 : System.Net.Security.dasm 32 | 43 : System.Xml.ReaderWriter.dasm 33 | 34 | Top file improvements by size (bytes): 35 | -1804 : mscorlib.dasm 36 | -1532 : Microsoft.CodeAnalysis.CSharp.dasm 37 | -726 : System.Xml.XmlDocument.dasm 38 | -284 : System.Linq.Expressions.dasm 39 | -239 : System.Net.Http.dasm 40 | 41 | 21 total files with diffs. 42 | 43 | Top method regressions by size (bytes): 44 | 328 : Microsoft.CodeAnalysis.CSharp.dasm - Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.DocumentationCommentXmlTokens:.cctor() 45 | 266 : Microsoft.CodeAnalysis.CSharp.dasm - Microsoft.CodeAnalysis.CSharp.MethodTypeInferrer:Fix(int,byref):bool:this 46 | 194 : mscorlib.dasm - System.DefaultBinder:BindToMethod(int,ref,byref,ref,ref,ref,byref):ref:this 47 | 187 : Microsoft.CodeAnalysis.CSharp.dasm - Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.LanguageParser:ParseModifiers(ref):this 48 | 163 : Microsoft.CodeAnalysis.CSharp.dasm - Microsoft.CodeAnalysis.CSharp.Symbols.SourceAssemblySymbol:DecodeWellKnownAttribute(byref,int,bool):this 49 | 50 | Top method improvements by size (bytes): 51 | -160 : System.Xml.XmlDocument.dasm - System.Xml.XmlTextWriter:AutoComplete(int):this 52 | -124 : System.Xml.XmlDocument.dasm - System.Xml.XmlTextWriter:WriteEndStartTag(bool):this 53 | -110 : Microsoft.CodeAnalysis.CSharp.dasm - Microsoft.CodeAnalysis.CSharp.MemberSemanticModel:GetEnclosingBinder(ref,int):ref:this 54 | -95 : Microsoft.CodeAnalysis.CSharp.dasm - Microsoft.CodeAnalysis.CSharp.CSharpDataFlowAnalysis:AnalyzeReadWrite():this 55 | -85 : Microsoft.CodeAnalysis.CSharp.dasm - Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.LanguageParser:ParseForStatement():ref:this 56 | 57 | 3762 total methods with diffs 58 | ``` -------------------------------------------------------------------------------- /src/jit-analyze/jit-analyze.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Exe 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/jit-dasm-pmi/JitDasmPmiRootCommand.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.CommandLine; 8 | using System.IO; 9 | 10 | namespace ManagedCodeGen 11 | { 12 | internal sealed class JitDasmPmiRootCommand : RootCommand 13 | { 14 | public Option AltJit { get; } = 15 | new("--altjit") { Description = "If set, the name of the altjit to use (e.g., clrjit_win_arm64_x64.dll)" }; 16 | public Option CorerunPath { get; } = 17 | new("--corerun", "-c") { CustomParser = Helpers.GetResolvedPath, DefaultValueFactory = Helpers.GetResolvedPath, Description = "The corerun compiler exe" }; 18 | public Option JitPath { get; } = 19 | new("--jit", "-j") { CustomParser = Helpers.GetResolvedPath, DefaultValueFactory = Helpers.GetResolvedPath, Description = "The full path to the jit library" }; 20 | public Option OutputPath { get; } = 21 | new("--output", "-o") { Description = "The output path" }; 22 | public Option Filename { get; } = 23 | new("--file", "-f") { Description = "Name of file to take list of assemblies from. Both a file and assembly list can be used" }; 24 | public Option DumpGCInfo { get; } = 25 | new("--gcinfo") { Description = "Add GC info to the disasm output" }; 26 | public Option DumpDebugInfo { get; } = 27 | new("--debuginfo") { Description = "Add Debug info to the disasm output" }; 28 | public Option Verbose { get; } = 29 | new("--verbose") { Description = "Enable verbose output" }; 30 | public Option NoDiffable { get; } = 31 | new("--nodiffable") { Description = "Generate non-diffable asm (pointer values will be left in output)" }; 32 | public Option Tier0 { get; } = 33 | new("--tier0") { Description = "Generate tier0 code" }; 34 | public Option Cctors { get; } = 35 | new("--cctors") { Description = "Jit and run cctors before jitting other methods" }; 36 | public Option Recursive { get; } = 37 | new("--recursive", "-r") { Description = "Search directories recursively" }; 38 | public Option> PlatformPaths { get; } = 39 | new("--platform", "-p") { Description = "Path to platform assemblies" }; 40 | public Option> Methods { get; } = 41 | new("--methods", "-m") { Description = "List of methods to disasm" }; 42 | public Argument> AssemblyList { get; } = 43 | new("--assembly") { Description = "The list of assemblies or directories to scan for assemblies" }; 44 | public Option WaitForDebugger { get; } = 45 | new("--wait", "-w") { Description = "Wait for debugger to attach" }; 46 | public Option NoCopyJit { get; } = 47 | new("--nocopy") { Description = "Correct jit has already been copied into the corerun directory" }; 48 | 49 | public ParseResult Result; 50 | 51 | public JitDasmPmiRootCommand(string[] args) : base("Managed code gen diff tool") 52 | { 53 | Options.Add(AltJit); 54 | Options.Add(CorerunPath); 55 | Options.Add(JitPath); 56 | Options.Add(OutputPath); 57 | Options.Add(Filename); 58 | Options.Add(DumpGCInfo); 59 | Options.Add(DumpDebugInfo); 60 | Options.Add(Verbose); 61 | Options.Add(NoDiffable); 62 | Options.Add(Tier0); 63 | Options.Add(Cctors); 64 | Options.Add(Recursive); 65 | Options.Add(PlatformPaths); 66 | Options.Add(Methods); 67 | Options.Add(WaitForDebugger); 68 | Options.Add(NoCopyJit); 69 | 70 | Arguments.Add(AssemblyList); 71 | 72 | SetAction(result => 73 | { 74 | Result = result; 75 | 76 | try 77 | { 78 | List errors = new(); 79 | string corerun = Result.GetValue(CorerunPath); 80 | if (corerun == null || !File.Exists(corerun)) 81 | { 82 | errors.Add("Can't find --corerun tool."); 83 | } 84 | 85 | if (Result.GetResult(Filename) == null && Result.GetValue(AssemblyList).Count == 0) 86 | { 87 | errors.Add("No input: Specify --file or list input assemblies."); 88 | } 89 | 90 | string jitPath = Result.GetValue(JitPath); 91 | if (jitPath != null && !File.Exists(jitPath)) 92 | { 93 | errors.Add("Can't find --jit library."); 94 | } 95 | 96 | string filename = Result.GetValue(Filename); 97 | if (filename != null && !File.Exists(filename)) 98 | { 99 | errors.Add($"Error reading input file {filename}, file not found."); 100 | } 101 | 102 | if (errors.Count > 0) 103 | { 104 | throw new Exception(string.Join(Environment.NewLine, errors)); 105 | } 106 | 107 | return new Program(this).Run(); 108 | } 109 | catch (Exception e) 110 | { 111 | Console.ResetColor(); 112 | Console.ForegroundColor = ConsoleColor.Red; 113 | 114 | Console.Error.WriteLine("Error: " + e.Message); 115 | Console.Error.WriteLine(e.ToString()); 116 | 117 | Console.ResetColor(); 118 | 119 | return 1; 120 | } 121 | }); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/jit-dasm-pmi/README.md: -------------------------------------------------------------------------------- 1 | # jit-dasm-pmi - Managed CodeGen Dasm Generator 2 | 3 | jit-dasm-pmi is a utility to drive the dotnet runtime to produce 4 | binary disassembly from the JIT compiler. This can be used to create 5 | diffs to check ongoing development. 6 | 7 | To build/setup: 8 | 9 | * Download the 2.1 dotnet cli. Follow install instructions and get 10 | dotnet on your path. 11 | * Do 'dotnet restore' to create lock file and pull down required packages. 12 | * Issue a 'dotnet build' command. This will create a jit-dasm-pmi in the bin 13 | directory that you can use to drive creation of diffs. 14 | * jit-dasm-pmi can be installed by running the project build script in the root 15 | of this repo via 16 | 17 | ``` 18 | $ ./build.{cmd|sh} -p 19 | ``` -------------------------------------------------------------------------------- /src/jit-dasm-pmi/jit-dasm-pmi.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Exe 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/jit-dasm/JitDasmRootCommand.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.CommandLine; 8 | using System.IO; 9 | 10 | namespace ManagedCodeGen 11 | { 12 | internal sealed class JitDasmRootCommand : RootCommand 13 | { 14 | public Option AltJit { get; } = 15 | new("--altjit") { Description = "If set, the name of the altjit to use (e.g., clrjit_win_arm64_x64.dll)" }; 16 | public Option CrossgenPath { get; } = 17 | new("--crossgen", "-c") { CustomParser = Helpers.GetResolvedPath, DefaultValueFactory = Helpers.GetResolvedPath, Description = "The crossgen or crossgen2 compiler exe." }; 18 | public Option JitPath { get; } = 19 | new("--jit", "-j") { CustomParser = Helpers.GetResolvedPath, DefaultValueFactory = Helpers.GetResolvedPath, Description = "The full path to the jit library" }; 20 | public Option OutputPath { get; } = 21 | new("--output", "-o") { Description = "The output path" }; 22 | public Option Filename { get; } = 23 | new("--file", "-f") { Description = "Name of file to take list of assemblies from. Both a file and assembly list can be used" }; 24 | public Option DumpGCInfo { get; } = 25 | new("--gcinfo") { Description = "Add GC info to the disasm output" }; 26 | public Option DumpDebugInfo { get; } = 27 | new("--debuginfo") { Description = "Add Debug info to the disasm output" }; 28 | public Option Verbose { get; } = 29 | new("--verbose") { Description = "Enable verbose output" }; 30 | public Option NoDiffable { get; } = 31 | new("--nodiffable") { Description = "Generate non-diffable asm (pointer values will be left in output)" }; 32 | public Option Recursive { get; } = 33 | new("--recursive", "-r") { Description = "Search directories recursively" }; 34 | public Option> PlatformPaths { get; } = 35 | new("--platform", "-p") { Description = "Path to platform assemblies" }; 36 | public Option> Methods { get; } = 37 | new("--methods", "-m") { Description = "List of methods to disasm" }; 38 | public Argument> AssemblyList { get; } = 39 | new("--assembly") { Description = "The list of assemblies or directories to scan for assemblies" }; 40 | public Option WaitForDebugger { get; } = 41 | new("--wait", "-w") { Description = "Wait for debugger to attach" }; 42 | 43 | public ParseResult Result; 44 | public bool CodeGeneratorV1 { get; private set; } 45 | 46 | public JitDasmRootCommand(string[] args) : base("Managed codegen diff tool (crossgen/AOT)") 47 | { 48 | Options.Add(AltJit); 49 | Options.Add(CrossgenPath); 50 | Options.Add(JitPath); 51 | Options.Add(OutputPath); 52 | Options.Add(Filename); 53 | Options.Add(DumpGCInfo); 54 | Options.Add(DumpDebugInfo); 55 | Options.Add(Verbose); 56 | Options.Add(NoDiffable); 57 | Options.Add(Recursive); 58 | Options.Add(PlatformPaths); 59 | Options.Add(Methods); 60 | Options.Add(WaitForDebugger); 61 | 62 | Arguments.Add(AssemblyList); 63 | 64 | SetAction(result => 65 | { 66 | Result = result; 67 | 68 | try 69 | { 70 | List errors = new(); 71 | string crossgen = Result.GetValue(CrossgenPath); 72 | if (crossgen == null || !File.Exists(crossgen)) 73 | { 74 | errors.Add("Can't find --crossgen tool."); 75 | } 76 | else 77 | { 78 | string crossgenFilename = Path.GetFileNameWithoutExtension(crossgen).ToLower(); 79 | if (crossgenFilename == "crossgen") 80 | { 81 | CodeGeneratorV1 = true; 82 | } 83 | else if (crossgenFilename != "crossgen2") 84 | { 85 | errors.Add("--crossgen tool should be crossgen or crossgen2."); 86 | } 87 | } 88 | 89 | if (Result.GetResult(Filename) == null && Result.GetValue(AssemblyList).Count == 0) 90 | { 91 | errors.Add("No input: Specify --file or list input assemblies."); 92 | } 93 | 94 | string jitPath = Result.GetValue(JitPath); 95 | if (jitPath != null && !File.Exists(jitPath)) 96 | { 97 | errors.Add("Can't find --jit library."); 98 | } 99 | 100 | string filename = Result.GetValue(Filename); 101 | if (filename != null && !File.Exists(filename)) 102 | { 103 | errors.Add($"Error reading input file {filename}, file not found."); 104 | } 105 | 106 | if (errors.Count > 0) 107 | { 108 | throw new Exception(string.Join(Environment.NewLine, errors)); 109 | } 110 | 111 | return new Program(this).Run(); 112 | } 113 | catch (Exception e) 114 | { 115 | Console.ResetColor(); 116 | Console.ForegroundColor = ConsoleColor.Red; 117 | 118 | Console.Error.WriteLine("Error: " + e.Message); 119 | Console.Error.WriteLine(e.ToString()); 120 | 121 | Console.ResetColor(); 122 | 123 | return 1; 124 | } 125 | }); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/jit-dasm/README.md: -------------------------------------------------------------------------------- 1 | # jit-dasm - Managed CodeGen Dasm Generator 2 | 3 | jit-dasm is a utility to drive the dotnet crossgen tool to produce 4 | binary disassembly from the JIT compiler. This can be used to create 5 | diffs to check ongoing development. 6 | 7 | To build/setup: 8 | 9 | * Download dotnet cli. Follow install instructions and get dotnet on your 10 | your path. 11 | * Do 'dotnet restore' to create lock file and 12 | pull down required packages. 13 | * Issue a 'dotnet build' command. This will create a jit-dasm in the bin 14 | directory that you can use to drive creation of diffs. 15 | * jit-dasm can be installed by running the project build script in the root of this repo 16 | via 17 | 18 | ``` 19 | $ ./build.{cmd|sh} -p 20 | ``` -------------------------------------------------------------------------------- /src/jit-dasm/jit-dasm.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Exe 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/jit-decisions-analyze/README.md: -------------------------------------------------------------------------------- 1 | # jit-decisions-analyze - Managed JIT decisions analysis tool 2 | -------------------------------------------------------------------------------- /src/jit-decisions-analyze/jit-decisions-analyze.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | 6 | namespace jit_decisions_analyze 7 | { 8 | internal class Program 9 | { 10 | private static void Main(string[] args) 11 | { 12 | List events = new List(); 13 | int malformed = 0; 14 | static void WriteProgress(double pct) 15 | { 16 | Console.CursorLeft = 0; 17 | Console.Write("{0:F2}% done", pct); 18 | } 19 | 20 | using (var sr = new StreamReader(File.OpenRead(args[0]))) 21 | { 22 | int lines = 0; 23 | string line; 24 | while ((line = sr.ReadLine()) != null) 25 | { 26 | if (!line.StartsWith("JITTracing: ")) 27 | continue; 28 | 29 | line = line.Substring("JITTracing: ".Length); 30 | Event evt = ToEvent(line); 31 | if (evt != null) 32 | events.Add(evt); 33 | else 34 | malformed++; 35 | 36 | if (lines++ % 10000 == 0) 37 | WriteProgress(sr.BaseStream.Position / (double)sr.BaseStream.Length * 100); 38 | } 39 | } 40 | 41 | WriteProgress(100); 42 | Console.WriteLine(); 43 | 44 | Console.WriteLine("{0} total well-formed events ({1} filtered away because they were malformed)", events.Count, malformed); 45 | List tailCalls = events.OfType().ToList(); 46 | WriteInfo("Implicit", tailCalls.Where(t => !t.TailPrefix)); 47 | WriteInfo("Explicit", tailCalls.Where(t => t.TailPrefix)); 48 | WriteInfo("Inlining", events.OfType()); 49 | } 50 | 51 | private static Event ToEvent(string l) 52 | { 53 | string[] data = l.Split("@!@!@"); 54 | if (data.Length % 2 == 0) 55 | return null; 56 | 57 | Dictionary payload = new Dictionary(); 58 | for (int i = 1; i < data.Length; i += 2) 59 | payload.Add(data[i], data[i + 1]); 60 | 61 | string tailPrefix; 62 | string failReason; 63 | switch (data[0]) 64 | { 65 | case "MethodJitTailCallSucceeded": 66 | tailPrefix = payload.GetValueOrDefault("TailPrefix"); 67 | if (tailPrefix == null) 68 | return null; 69 | 70 | return new TailCallSucceededEvent { TailPrefix = tailPrefix == "True" }; 71 | case "MethodJitTailCallFailed": 72 | tailPrefix = payload.GetValueOrDefault("TailPrefix"); 73 | failReason = payload.GetValueOrDefault("FailReason"); 74 | if (failReason == null || tailPrefix == null) 75 | return null; 76 | 77 | return new TailCallFailedEvent { FailReason = failReason, TailPrefix = tailPrefix == "True" }; 78 | case "MethodJitInliningSucceeded": 79 | return new InliningSucceededEvent(); 80 | case "MethodJitInliningFailed": 81 | failReason = payload.GetValueOrDefault("FailReason"); 82 | if (failReason == null) 83 | return null; 84 | 85 | return new InliningFailedEvent { FailReason = failReason }; 86 | default: 87 | return null; 88 | } 89 | } 90 | 91 | private static void WriteInfo(string name, IEnumerable events) 92 | { 93 | List list = events.ToList(); 94 | int sites = list.Count; 95 | int sitesSuccessful = list.Count(IsSuccessEvent); 96 | Console.WriteLine("{0} call sites: {1}/{2} converted", name, sitesSuccessful, sites); 97 | if (sites == 0) 98 | return; 99 | 100 | string GetInfoString(Event e) 101 | { 102 | switch (e) 103 | { 104 | case TailCallSucceededEvent f: return "Successfully converted"; 105 | case InliningSucceededEvent f: return "Successfully converted"; 106 | case TailCallFailedEvent f: return f.FailReason; 107 | case InliningFailedEvent f: return f.FailReason; 108 | default: throw new ArgumentException("No fail reason on event"); 109 | } 110 | } 111 | 112 | var groupedFailures = list.GroupBy(GetInfoString).OrderByDescending(g => g.Count()); 113 | foreach (var g in groupedFailures) 114 | Console.WriteLine("[{0:00.00}%] {1}", g.Count() / (double)sites * 100, g.Key); 115 | 116 | Console.WriteLine(); 117 | } 118 | 119 | private static bool IsSuccessEvent(Event e) => e is TailCallSucceededEvent || e is InliningSucceededEvent; 120 | } 121 | 122 | internal abstract class Event 123 | { 124 | } 125 | 126 | internal abstract class TailCallEvent : Event 127 | { 128 | public bool TailPrefix { get; set; } 129 | } 130 | 131 | internal class TailCallSucceededEvent : TailCallEvent 132 | { 133 | } 134 | 135 | internal class TailCallFailedEvent : TailCallEvent 136 | { 137 | public string FailReason { get; set; } 138 | } 139 | 140 | internal abstract class InliningEvent : Event 141 | { 142 | } 143 | 144 | internal class InliningSucceededEvent : InliningEvent 145 | { 146 | } 147 | 148 | internal class InliningFailedEvent : InliningEvent 149 | { 150 | public string FailReason { get; set; } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/jit-decisions-analyze/jit-decisions-analyze.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Exe 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/jit-diff/README.md: -------------------------------------------------------------------------------- 1 | # jit-diff - Diff CoreCLR tree 2 | 3 | jit-diff is a utility to produce diffs from a CoreCLR test layout via 4 | the jit-dasm tool. 5 | 6 | To build/setup: 7 | 8 | * Download dotnet cli. Follow install instructions and get dotnet on your 9 | your path. 10 | * Do 'dotnet restore' to create lock file and 11 | pull down required packages. 12 | * Issue a 'dotnet build' command. This will create a jit-diff in the bin 13 | directory that you can use to drive creation of diffs. 14 | * Ensure that jit-dasm is on your path. (See jit-dasm README.md for details 15 | on how to build) 16 | * invoke jit-diff --frameworks --base `` --diff `` 17 | --coreroot `` --testroot `` 18 | * jit-diff can be installed by running the project build script in the root of this repo 19 | via 20 | 21 | ``` 22 | $ ./build.{cmd|sh} -p 23 | ``` 24 | -------------------------------------------------------------------------------- /src/jit-diff/install.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.CommandLine; 8 | using System.Diagnostics; 9 | using System.IO; 10 | using System.Linq; 11 | using System.Text; 12 | using System.Text.Json; 13 | using System.Text.Json.Nodes; 14 | using System.Text.RegularExpressions; 15 | using System.Threading; 16 | using System.Threading.Tasks; 17 | using System.Xml; 18 | 19 | namespace ManagedCodeGen 20 | { 21 | public partial class jitdiff 22 | { 23 | public static int InstallCommand(Config config) 24 | { 25 | var configFilePath = Path.Combine(config.JitUtilsRoot, s_configFileName); 26 | string configJson = File.ReadAllText(configFilePath); 27 | var jObj = JsonObject.Parse(configJson); 28 | 29 | if ((jObj[s_configFileRootKey] == null) || (jObj[s_configFileRootKey]["tools"] == null)) 30 | { 31 | Console.Error.WriteLine("\"install\" doesn't know how to add the \"" + s_configFileRootKey + "\":\"tools\" section to the config file"); 32 | return -1; 33 | } 34 | 35 | if ((config.PlatformMoniker == null) || (GetOSArtifactDirComponent(config.PlatformMoniker) == null)) 36 | { 37 | return -1; 38 | } 39 | 40 | var tools = (JsonArray)jObj[s_configFileRootKey]["tools"]; 41 | 42 | // Early out if the tool is already installed. We can only do this if we're not doing 43 | // "--last_successful", in which case we don't know what the build number (and hence 44 | // tag) is. 45 | string tag = null; 46 | if (!config.DoLastSucessful) 47 | { 48 | tag = String.Format("{0}-{1}", config.JobName, config.Number); 49 | if (tools.Where(x => (string)x["tag"] == tag).Any()) 50 | { 51 | Console.Error.WriteLine("{0} is already installed in the " + s_configFileName + ". Remove before re-install.", tag); 52 | return -1; 53 | } 54 | } 55 | 56 | string toolPath = Path.Combine(config.JitUtilsRoot, "tools"); 57 | 58 | // Issue cijobs command to download bits 59 | List cijobsArgs = new List(); 60 | 61 | cijobsArgs.Add("copy"); 62 | 63 | cijobsArgs.Add("--job"); 64 | cijobsArgs.Add(config.JobName); 65 | 66 | if (config.BranchName != null) 67 | { 68 | cijobsArgs.Add("--branch"); 69 | cijobsArgs.Add(config.BranchName); 70 | } 71 | 72 | if (config.DoLastSucessful) 73 | { 74 | cijobsArgs.Add("--last_successful"); 75 | } 76 | else 77 | { 78 | cijobsArgs.Add("--number"); 79 | cijobsArgs.Add(config.Number); 80 | } 81 | 82 | cijobsArgs.Add("--unzip"); 83 | 84 | cijobsArgs.Add("--output_root"); 85 | cijobsArgs.Add(toolPath); 86 | 87 | if (config.Verbose) 88 | { 89 | Console.WriteLine("Command: {0} {1}", "cijobs", String.Join(" ", cijobsArgs)); 90 | } 91 | 92 | ProcessResult result = Utility.ExecuteProcess("cijobs", cijobsArgs); 93 | 94 | if (result.ExitCode != 0) 95 | { 96 | Console.Error.WriteLine("cijobs command returned with {0} failures", result.ExitCode); 97 | return result.ExitCode; 98 | } 99 | 100 | // There is a convention that cijobs creates a directory to store the job within 101 | // the toolPath named: 102 | // - 103 | // for example: 104 | // checked_windows_nt-1234 105 | // 106 | // However, if we passed "--last_successful", we don't know that number! So, figure it out. 107 | 108 | if (config.DoLastSucessful) 109 | { 110 | // Find the largest numbered build with this job name. 111 | int maxBuildNum = -1; 112 | foreach (var dir in Directory.EnumerateDirectories(toolPath)) 113 | { 114 | var lastComponent = Path.GetFileName(dir); 115 | Regex dirPattern = new Regex(@"(.*)-(.*)"); 116 | Match dirMatch = dirPattern.Match(lastComponent); 117 | if (dirMatch.Success) 118 | { 119 | var value = dirMatch.Groups[2].Value; 120 | if (int.TryParse(value, out int thisBuildNum)) 121 | { 122 | if (thisBuildNum > maxBuildNum) 123 | { 124 | maxBuildNum = thisBuildNum; 125 | } 126 | } 127 | } 128 | } 129 | 130 | if (maxBuildNum == -1) 131 | { 132 | Console.Error.WriteLine("Error: couldn't determine last successful build directory in {0}", toolPath); 133 | return -1; 134 | } 135 | 136 | string buildNum = maxBuildNum.ToString(); 137 | tag = String.Format("{0}-{1}", config.JobName, buildNum); 138 | } 139 | 140 | toolPath = Path.Combine(toolPath, tag); 141 | 142 | string platformPath = Path.Combine(toolPath, "Product"); 143 | if (!Directory.Exists(platformPath)) 144 | { 145 | Console.Error.WriteLine("cijobs didn't create or populate directory {0}", platformPath); 146 | return 1; 147 | } 148 | 149 | string buildOS = GetOSArtifactDirComponent(config.PlatformMoniker).ToUpper(); 150 | foreach (var dir in Directory.EnumerateDirectories(platformPath)) 151 | { 152 | if (Path.GetFileName(dir).ToUpper().Contains(buildOS)) 153 | { 154 | tools.Add(new JsonObject 155 | { 156 | ["tag"] = tag, 157 | ["path"] = Path.GetFullPath(dir) 158 | }); 159 | break; 160 | } 161 | } 162 | 163 | // Overwrite current config.json with new data. 164 | using (var sw = File.CreateText(configFilePath)) 165 | { 166 | var json = JsonSerializer.Serialize (jObj, new JsonSerializerOptions { WriteIndented = true }); 167 | sw.Write(json); 168 | } 169 | 170 | return 0; 171 | } 172 | } 173 | } 174 | 175 | -------------------------------------------------------------------------------- /src/jit-diff/jit-diff.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Exe 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/jit-diff/uninstall.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System; 6 | using System.CommandLine; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Text.Json; 10 | using System.Text.Json.Nodes; 11 | 12 | namespace ManagedCodeGen 13 | { 14 | public partial class jitdiff 15 | { 16 | public static int UninstallCommand(Config config) 17 | { 18 | var configFilePath = Path.Combine(config.JitUtilsRoot, s_configFileName); 19 | string configJson = File.ReadAllText(configFilePath); 20 | var jObj = JsonObject.Parse(configJson); 21 | 22 | if ((jObj[s_configFileRootKey] == null) || (jObj[s_configFileRootKey]["tools"] == null)) 23 | { 24 | Console.Error.WriteLine("Error: no \"" + s_configFileRootKey + "\":\"tools\" section in the config file"); 25 | return -1; 26 | } 27 | 28 | var tools = (JsonArray)jObj[s_configFileRootKey]["tools"]; 29 | var elem = tools.Where(x => (string)x["tag"] == config.Tag); 30 | if (!elem.Any()) 31 | { 32 | Console.WriteLine("{0} is not installed in {1}.", config.Tag, s_configFileName); 33 | return -1; 34 | } 35 | 36 | var jobj = elem.First(); 37 | string path = (string)jobj["path"]; 38 | if (path != null) 39 | { 40 | Console.WriteLine("Warning: you should remove install directory {0}.", path); 41 | 42 | // We could do this: 43 | // Directory.Delete(path, true); 44 | // However, the "install" command copies down a lot more than just this directory, 45 | // so removing this directory still leaves around a lot of stuff. 46 | } 47 | 48 | Console.WriteLine("Removing tag {0} from config file.", config.Tag); 49 | tools.Remove(jobj); 50 | 51 | // Overwrite current config.json with new data. 52 | using (var sw = File.CreateText(configFilePath)) 53 | { 54 | var json = JsonSerializer.Serialize (jObj, new JsonSerializerOptions { WriteIndented = true }); 55 | sw.Write(json); 56 | } 57 | 58 | return 0; 59 | } 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /src/jit-format/README.md: -------------------------------------------------------------------------------- 1 | # jit-format - Code Formatting Tool for JIT Source 2 | 3 | jit-format is a utility to maintain formatting standards in jit source. 4 | The tool will analyze the code for formatting errors using clang-tidy 5 | and clang-format, and potentially fix errors. 6 | 7 | To build/setup: 8 | 9 | * Download dotnet cli. Follow install instructions and get dotnet on your 10 | your path. 11 | * Do 'dotnet restore' to create lock file and 12 | pull down required packages. 13 | * Issue a 'dotnet build' command. This will create a jit-format in the bin 14 | directory that you can use to check the formatting of your changes. 15 | * Invoke jit-format -a `` -b `` -p `` 16 | --runtime `` 17 | * jit-format can be installed by running the project build script in the root of this repo 18 | via 19 | 20 | ``` 21 | $ ./build.{cmd|sh} -p 22 | ``` 23 | 24 | -------------------------------------------------------------------------------- /src/jit-format/jit-format.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Exe 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/jit-include.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/jit-pintool/README.md: -------------------------------------------------------------------------------- 1 | # clrjit pintool 2 | 3 | This directory contains the source code of a pintool that can be used with 4 | [PIN](https://www.intel.com/content/www/us/en/developer/articles/tool/pin-a-dynamic-binary-instrumentation-tool.html) 5 | to measure throughput of the JIT on x64 and x86. The pintool counts the number 6 | of instructions executed inside the JIT only. Furthermore it has some special 7 | support to integrate with SuperPMI's metric collection to allow support for 8 | diffing throughput. 9 | 10 | ## Building 11 | The easiest way to build it is to follow PIN's manual and adding the pintool 12 | here as another example. See the "Building the Example Tools" and "Building Your 13 | Own Tool" sections in the manual. 14 | Note that this requires cygwin on Windows. 15 | -------------------------------------------------------------------------------- /src/jit-rl-cse/MLCSE.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Exe 8 | enable 9 | enable 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/jit-rl-cse/MarkovChain96689.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotnet/jitutils/b19db6c8a30fc1717a554f5ef0f7f8dc14778188/src/jit-rl-cse/MarkovChain96689.png -------------------------------------------------------------------------------- /src/jit-rl-cse/MarkovChain96689PG100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotnet/jitutils/b19db6c8a30fc1717a554f5ef0f7f8dc14778188/src/jit-rl-cse/MarkovChain96689PG100.png -------------------------------------------------------------------------------- /src/jit-tp-analyze/Program.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.CommandLine; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Runtime.InteropServices; 11 | using System.Text.RegularExpressions; 12 | 13 | namespace ManagedCodeGen; 14 | 15 | internal class Program 16 | { 17 | private static readonly Regex _traceLineRegex = new("(\\d+) +: (.*)", RegexOptions.Compiled | RegexOptions.CultureInvariant); 18 | 19 | private readonly string _baseTracePath; 20 | private readonly string _diffTracePath; 21 | private readonly double _noise; 22 | 23 | public Program(JitTpAnalyzeRootCommand command, ParseResult result) 24 | { 25 | _baseTracePath = result.GetValue(command.BasePath); 26 | _diffTracePath = result.GetValue(command.DiffPath); 27 | _noise = result.GetValue(command.Noise); 28 | } 29 | 30 | public void Run() 31 | { 32 | TextWriter output = Console.Out; 33 | Dictionary baseTrace = ParseTrace(_baseTracePath); 34 | Dictionary diffTrace = ParseTrace(_diffTracePath); 35 | HashSet allRecordedFunctions = new(); 36 | foreach (var function in baseTrace) 37 | { 38 | allRecordedFunctions.Add(function.Key); 39 | } 40 | foreach (var function in diffTrace) 41 | { 42 | allRecordedFunctions.Add(function.Key); 43 | } 44 | 45 | long baseTotalInsCount = baseTrace.Sum(x => x.Value); 46 | long diffTotalInsCount = diffTrace.Sum(x => x.Value); 47 | double totalPercentageDiff = GetPercentageDiff(baseTotalInsCount, diffTotalInsCount); 48 | output.WriteLine($"Base: {baseTotalInsCount}, Diff: {diffTotalInsCount}, {FormatPercentageDiff(totalPercentageDiff, "0000")}"); 49 | output.WriteLine(); 50 | 51 | // Now create a list of functions which contributed to the difference. 52 | long totalAbsInsCountDiff = 0; 53 | List diffs = new(); 54 | foreach (string functionName in allRecordedFunctions) 55 | { 56 | long diffInsCount = diffTrace.GetValueOrDefault(functionName); 57 | long baseInsCount = baseTrace.GetValueOrDefault(functionName); 58 | long insCountDiff = diffInsCount - baseInsCount; 59 | if (insCountDiff == 0) 60 | { 61 | continue; 62 | } 63 | 64 | diffs.Add(new() 65 | { 66 | Name = functionName, 67 | InsCountDiff = insCountDiff, 68 | InsPercentageDiff = GetPercentageDiff(baseInsCount, diffInsCount), 69 | TotalInsPercentageDiff = (double)insCountDiff / baseTotalInsCount * 100 70 | }); 71 | 72 | totalAbsInsCountDiff += Math.Abs(insCountDiff); 73 | } 74 | 75 | foreach (ref FunctionDiff diff in CollectionsMarshal.AsSpan(diffs)) 76 | { 77 | diff.ContributionPercentage = (double)Math.Abs(diff.InsCountDiff) / totalAbsInsCountDiff * 100; 78 | } 79 | 80 | // Filter out functions below the noise level. 81 | diffs = diffs.Where(d => d.ContributionPercentage > _noise).ToList(); 82 | diffs.Sort((x, y) => y.InsCountDiff.CompareTo(x.InsCountDiff)); 83 | 84 | int maxNameLength = 0; 85 | int maxInsCountDiffLength = 0; 86 | int maxInsPercentageDiffLength = 0; 87 | foreach (ref FunctionDiff diff in CollectionsMarshal.AsSpan(diffs)) 88 | { 89 | maxNameLength = Math.Max(maxNameLength, diff.Name.Length); 90 | maxInsCountDiffLength = Math.Max(maxInsCountDiffLength, $"{diff.InsCountDiff}".Length); 91 | maxInsPercentageDiffLength = Math.Max(maxInsPercentageDiffLength, FormatPercentageDiff(diff.InsPercentageDiff).Length); 92 | } 93 | 94 | foreach (ref FunctionDiff diff in CollectionsMarshal.AsSpan(diffs)) 95 | { 96 | output.WriteLine( 97 | $"{{0,-{maxNameLength}}} : {{1,-{maxInsCountDiffLength}}} : {{2,-{maxInsPercentageDiffLength}}} : {{3,-6:P2}} : {{4}}", 98 | diff.Name, 99 | diff.InsCountDiff, 100 | double.IsInfinity(diff.InsPercentageDiff) ? "NA" : FormatPercentageDiff(diff.InsPercentageDiff), 101 | diff.ContributionPercentage / 100, 102 | FormatPercentageDiff(diff.TotalInsPercentageDiff, "0000")); 103 | } 104 | } 105 | 106 | private Dictionary ParseTrace(string path) 107 | { 108 | Dictionary trace = new(); 109 | foreach (string line in File.ReadLines(path)) 110 | { 111 | Match match = _traceLineRegex.Match(line); 112 | if (match.Success) 113 | { 114 | trace.Add(match.Groups[2].Value, long.Parse(match.Groups[1].Value)); 115 | } 116 | } 117 | 118 | return trace; 119 | } 120 | 121 | private static double GetPercentageDiff(double baseValue, double diffValue) => 122 | (diffValue - baseValue) / baseValue * 100; 123 | 124 | private static string FormatPercentageDiff(double value, string precision = "00") => 125 | (value > 0 ? "+" : "") + value.ToString($"0.{precision}") + "%"; 126 | 127 | private static void Main(string[] args) => 128 | new CommandLineConfiguration(new JitTpAnalyzeRootCommand().UseVersion()).Invoke(args); 129 | 130 | private struct FunctionDiff 131 | { 132 | public string Name; 133 | public long InsCountDiff; 134 | public double InsPercentageDiff; 135 | public double ContributionPercentage; 136 | public double TotalInsPercentageDiff; 137 | } 138 | } 139 | 140 | internal class JitTpAnalyzeRootCommand : RootCommand 141 | { 142 | public JitTpAnalyzeRootCommand() : base("Compare PIN-based throughput traces") 143 | { 144 | Options.Add(BasePath); 145 | Options.Add(DiffPath); 146 | Options.Add(Noise); 147 | 148 | SetAction(result => 149 | { 150 | try 151 | { 152 | Program jitTpDiff = new(this, result); 153 | jitTpDiff.Run(); 154 | } 155 | catch (Exception e) 156 | { 157 | Console.ResetColor(); 158 | Console.ForegroundColor = ConsoleColor.Red; 159 | Console.Error.WriteLine("Error: " + e.Message); 160 | Console.Error.WriteLine(e.ToString()); 161 | Console.ResetColor(); 162 | return 1; 163 | } 164 | 165 | return 0; 166 | }); 167 | } 168 | 169 | public Option BasePath { get; } = 170 | new("--base", "-b") { Description = "Base trace file", DefaultValueFactory = (_) => "basetp.txt" }; 171 | public Option DiffPath { get; } = 172 | new("--diff", "-d") { Description = "Diff trace file", DefaultValueFactory = (_) => "difftp.txt" }; 173 | public Option Noise { get; } = 174 | new("--noise", "-n") { Description = "Minimal contribution percentage for inclusion into the summary", DefaultValueFactory = (_) => 0.1 }; 175 | } 176 | -------------------------------------------------------------------------------- /src/jit-tp-analyze/README.md: -------------------------------------------------------------------------------- 1 | # jit-tp-analyze - throughput difference analysis tool 2 | 3 | jit-tp-analyze is a utility to parse traces generated by PIN-based 4 | instrumentation over runs of the JIT. The tool reads all lines in 5 | the following format from the two input files: 6 | ``` 7 | : 8 | ``` 9 | The tool ignores all lines that do not match this pattern and so can be 10 | run directly against superpmi.exe's usual output. 11 | 12 | The tool produces the following summary: 13 | ``` 14 | Base: 1039322782, Diff: 1040078986, +0.0728% 15 | 16 | `Compiler::optCopyPropPushDef'::`2'::::operator() : 1073512 : NA : 18.17% : +0.1033% 17 | SsaBuilder::RenamePushDef : 911022 : NA : 15.42% : +0.0877% 18 | `Compiler::fgValueNumberLocalStore'::`2'::::operator() : 584435 : NA : 9.89% : +0.0562% 19 | Compiler::lvaLclExactSize : 244692 : +60.09% : 4.14% : +0.0235% 20 | ValueNumStore::VNForMapSelectWork : 87006 : +2.78% : 1.47% : +0.0084% 21 | GenTree::DefinesLocal : 82633 : +1.63% : 1.40% : +0.0080% 22 | Rationalizer::DoPhase : -91104 : -6.36% : 1.54% : -0.0088% 23 | Compiler::gtCallGetDefinedRetBufLclAddr : -115926 : -98.78% : 1.96% : -0.0112% 24 | Compiler::optBlockCopyProp : -272450 : -5.75% : 4.61% : -0.0262% 25 | Compiler::fgValueNumberLocalStore : -313540 : -50.82% : 5.31% : -0.0302% 26 | Compiler::GetSsaNumForLocalVarDef : -322826 : -100.00% : 5.46% : -0.0311% 27 | SsaBuilder::RenameDef : -478441 : -28.33% : 8.10% : -0.0460% 28 | Compiler::optCopyPropPushDef : -711380 : -55.34% : 12.04% : -0.0684% 29 | ``` 30 | The columns, in order: 31 | 1. Method name. 32 | 2. The instruction count difference for the given function. 33 | 3. Same as `1`, but relative. May be `NA`, indicating the base didn't contain the given function, or `-100%` indicating the diff didn't. 34 | 4. Relative contribution to the diff. Calculated as `abs(instruction diff count) / sum-over-all-functions(abs(instruction diff count))`. 35 | 5. Relative difference, calculated as `instruction diff count / total base instruction count`. 36 | 37 | To use: 38 | 1. Obtain the base and diff traces, by compiling and running a PIN tool that counts instructions retired for each function. 39 | 2. Invoke `./jit-tp-analyze --base base-trace.txt --diff diff-trace.txt`. 40 | 41 | For convenience, both arguments have default values: `basetp.txt` for `--base`, `difftp.txt` for `--diff`, and so can be omitted. 42 | 43 | By default, the tool will hide functions that contributed less than `0.1%` to the difference. You can change this value with the `--noise` argument. 44 | -------------------------------------------------------------------------------- /src/jit-tp-analyze/jit-tp-analyze.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Exe 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/mutate-test/MutateTestRootCommand.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System; 6 | using System.CommandLine; 7 | 8 | namespace MutateTest 9 | { 10 | internal sealed class MutateTestRootCommand : CliRootCommand 11 | { 12 | public CliArgument InputFilePath { get; } = 13 | new("input-test-case") { Description = "Input test case file or directory (for --recursive)", Arity = ArgumentArity.OneOrMore }; 14 | public CliOption EHStress { get; } = 15 | new("--ehStress") { Description = "Add EH to methods" }; 16 | public CliOption StructStress { get; } = 17 | new("--structStress") { Description = "Replace locals with structs" }; 18 | public CliOption ShowResults { get; } = 19 | new("--showResults") { Description = "Add EH to methods" }; 20 | public CliOption Verbose { get; } = 21 | new("--verbose") { Description = "Describe each transformation" }; 22 | public CliOption Quiet { get; } = 23 | new("--quiet") { Description = "Produce minimal output" }; 24 | public CliOption Recursive { get; } = 25 | new("--recursive") { Description = "Process each file recursively" }; 26 | public CliOption Seed { get; } = 27 | new("--seed") { DefaultValueFactory = _ => 42, Description = "Random seed" }; 28 | public CliOption StopAtFirstFailure { get; } = 29 | new("--stopAtFirstFailure") { Description = "Stop each test at first failure" }; 30 | public CliOption EmptyBlocks { get; } = 31 | new("--emptyBlocks") { Description = "Transform empty blocks" }; 32 | public CliOption SizeLimit { get; } = 33 | new("--sizeLimit") { DefaultValueFactory = _ => 10000, Description = "Don't process programs larger than this size" }; 34 | public CliOption TimeLimit { get; } = 35 | new("--timeLimit") { DefaultValueFactory = _ => 10000, Description = "Don't stress programs where compile + run takes more than this many milliseconds" }; 36 | public CliOption Projects { get; } = 37 | new("--projects") { Description = "Look for .csproj files instead of .cs files when doing recursive exploration" }; 38 | public CliOption OnlyFailures { get; } = 39 | new("--onlyFailures") { Description = "Only emit output for cases that fail at runtime" }; 40 | 41 | public ParseResult Result { get; private set; } 42 | 43 | public MutateTestRootCommand(string[] args) : base(".NET JIT mutate test utility") 44 | { 45 | Arguments.Add(InputFilePath); 46 | Options.Add(EHStress); 47 | Options.Add(StructStress); 48 | Options.Add(ShowResults); 49 | Options.Add(Verbose); 50 | Options.Add(Quiet); 51 | Options.Add(Recursive); 52 | Options.Add(Seed); 53 | Options.Add(StopAtFirstFailure); 54 | Options.Add(EmptyBlocks); 55 | Options.Add(SizeLimit); 56 | Options.Add(TimeLimit); 57 | Options.Add(Projects); 58 | Options.Add(OnlyFailures); 59 | 60 | SetAction(result => 61 | { 62 | Result = result; 63 | 64 | try 65 | { 66 | return new Program(this).Run(); 67 | } 68 | catch (Exception e) 69 | { 70 | Console.ResetColor(); 71 | Console.ForegroundColor = ConsoleColor.Red; 72 | 73 | Console.Error.WriteLine("Error: " + e.Message); 74 | Console.Error.WriteLine(e.ToString()); 75 | 76 | Console.ResetColor(); 77 | 78 | return 1; 79 | } 80 | }); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/mutate-test/mutate-test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Exe 7 | net6.0 8 | Mutate 9 | Mutate 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/performance-explorer/README.md: -------------------------------------------------------------------------------- 1 | ### Performance Explorer 2 | 3 | Performance Explorer is a tool to examine the impact of changing JIT behavior on key methods in a benchmark. 4 | It is currently specialized to explore varying the CSEs in the most dominant Tier-1 method of a benchmark. 5 | 6 | ### Setup 7 | 8 | This tool currently only works on Windows. 9 | 10 | To run Performance Explorer, you must have local enlistments of: 11 | * [the runtime repo](https://github.com/dotnet/runtime) 12 | * [the performance repo](https://github.com/dotnet/performance) 13 | * [instructions retired explorer](https://github.com/AndyAyersMS/InstructionsRetiredExplorer) 14 | 15 | You will need to do both release and checked builds of the runtime repo, and create the associated 16 | test directories (aka Core_Roots). 17 | 18 | You will need to build the instructions retired explorer. 19 | 20 | You will need to modify file paths in the performance explorer code to refer to the locations 21 | of the above repos and builds, an to specify a results directory. 22 | 23 | Finally, you will likely want to customize the list of benchmarks to explore; the names of these 24 | are the names used in the performance repo. Note the names often contain quotes or other special 25 | characters so you will likely need to read up on how to handle these when they appear in C# literal strings. 26 | 27 | Once you have made these modifications, you can then build the performance explorer. 28 | 29 | The tool must be run as admin, in order to perform the necessary profiling. 30 | 31 | ### How It Works 32 | 33 | For each benchmark in the list, performance explorer will: 34 | * run the benchmark from the perf directory, with `-p ETW` so that profile data is collected 35 | * parse the profile data using instructions retired explorer to find the hot methods 36 | * also parse the BenchmarkDotNet json to determine the performance of the benchmark 37 | * determine if there's a hot method that would be a good candidate for exploration. Currently we look for a Tier-1 method that accounts for at least 20% of the benchmark time. 38 | * if there is a suitable hot method: 39 | * run an SPMI collection for that benchmark 40 | * use that SPMI to get an assembly listing for the hot method 41 | * determine from that listing how many CSEs were performed (the "default set" of N CSEs) 42 | * if there were any CSEs, start the experimentation process: 43 | * run the benchmark with all CSEs disabled (0 CSEs), and measure perf. Add to the exploration queue. 44 | * then, repeatedly, until we have run out of experiment to try, or hit some predetermined limit 45 | * pick the best performing experiment from the queue 46 | * Determine which CSEs in the default set were not done in the experiment. Say there are M (<=N) of these 47 | * Run M more experiments, each adding one of the missing CSEs 48 | 49 | Each benchmark's data is stored in a subfolder in the results directory; we also create disassembly for all the 50 | experiments tried, and copies of all the intermediate files. 51 | 52 | There is also a master results.csv that has data from all experiments in all benchmarks, suitable for use 53 | in excel or as input to a machine learning algorithm. 54 | 55 | If you re-run the tool with the same benchmark list and results directory, it will use the cached copies of 56 | data and won't re-run the experiments. 57 | 58 | If along the way anything goes wrong then an "error.txt" file is added to the results subdirectory for 59 | that benchmark, and future runs will skip that benchmark. 60 | 61 | So say there are 2 CSEs by default. The explorer will run: 62 | * one experiment with 0 CSEs 63 | * two experiments each with 1 CSE 64 | * one experiment with 2 CSEs 65 | and then stop as all possibilities have been explored. 66 | 67 | For larger values of N the number of possible experiments 2^N grows rapidly and we cannot hope to explore 68 | the full space. The exploration process is intended to prioritize for those experiments that likely have 69 | the largest impact on performance. 70 | 71 | ### Future Enhancements 72 | 73 | * add option to offload benchmark runs to the perf lab 74 | * capture more details about CSEs so we can use the data to develop better CSE heuristics 75 | * generalize the experiment processing to allow other kinds of experiments 76 | * parameterize the config settings so we don't need to modify the sources 77 | * add options to characterize the noise level of benchmarks and (perhaps) do more runs if noisy 78 | * leverage SPMI instead of perf runs, if we can trust perf scores 79 | 80 | -------------------------------------------------------------------------------- /src/performance-explorer/benchmark-info.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.IO; 6 | using System; 7 | 8 | public class BenchmarkInfo 9 | { 10 | public string Name { get; init; } 11 | public double Ratio { get; set; } 12 | 13 | public string CleanName 14 | { 15 | get 16 | { 17 | string cleanName = Name; 18 | if (cleanName.Length > 100) 19 | { 20 | int parensIndex = cleanName.IndexOf('('); 21 | static string Last(string s, int num) => s.Length < num ? s : s[^num..]; 22 | if (parensIndex == -1) 23 | { 24 | cleanName = Last(cleanName, 100); 25 | } 26 | else 27 | { 28 | string benchmarkName = cleanName[..parensIndex]; 29 | string paramsStr = cleanName[(parensIndex + 1)..^1]; 30 | cleanName = Last(benchmarkName, Math.Max(50, 100 - paramsStr.Length)) + "(" + Last(paramsStr, Math.Max(50, 100 - benchmarkName.Length)) + ")"; 31 | } 32 | } 33 | 34 | foreach (char illegalChar in Path.GetInvalidFileNameChars()) 35 | { 36 | cleanName = cleanName.Replace(illegalChar, '_'); 37 | } 38 | 39 | cleanName = cleanName.Replace(' ', '_'); 40 | 41 | return cleanName; 42 | } 43 | } 44 | 45 | public string CsvName => CleanName.Replace(',', '_'); 46 | 47 | } -------------------------------------------------------------------------------- /src/performance-explorer/benchmark-json.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | // Classes for deserializing BenchmarkDotNet .json result files 6 | 7 | using System; 8 | using System.Collections.Generic; 9 | using System.IO; 10 | using System.Text.Json; 11 | 12 | public class ChronometerFrequency 13 | { 14 | public int Hertz { get; set; } 15 | } 16 | 17 | public class HostEnvironmentInfo 18 | { 19 | public string BenchmarkDotNetCaption { get; set; } 20 | public string BenchmarkDotNetVersion { get; set; } 21 | public string OsVersion { get; set; } 22 | public string ProcessorName { get; set; } 23 | public int? PhysicalProcessorCount { get; set; } 24 | public int? PhysicalCoreCount { get; set; } 25 | public int? LogicalCoreCount { get; set; } 26 | public string RuntimeVersion { get; set; } 27 | public string Architecture { get; set; } 28 | public bool? HasAttachedDebugger { get; set; } 29 | public bool? HasRyuJit { get; set; } 30 | public string Configuration { get; set; } 31 | public string JitModules { get; set; } 32 | public string DotNetCliVersion { get; set; } 33 | public ChronometerFrequency ChronometerFrequency { get; set; } 34 | public string HardwareTimerKind { get; set; } 35 | } 36 | 37 | public class ConfidenceInterval 38 | { 39 | public int N { get; set; } 40 | public double Mean { get; set; } 41 | public double StandardError { get; set; } 42 | public int Level { get; set; } 43 | public double Margin { get; set; } 44 | public double Lower { get; set; } 45 | public double Upper { get; set; } 46 | } 47 | 48 | public class Percentiles 49 | { 50 | public double P0 { get; set; } 51 | public double P25 { get; set; } 52 | public double P50 { get; set; } 53 | public double P67 { get; set; } 54 | public double P80 { get; set; } 55 | public double P85 { get; set; } 56 | public double P90 { get; set; } 57 | public double P95 { get; set; } 58 | public double P100 { get; set; } 59 | } 60 | 61 | public class Statistics 62 | { 63 | public double[] OriginalValues { get; set; } 64 | public int N { get; set; } 65 | public double Min { get; set; } 66 | public double LowerFence { get; set; } 67 | public double Q1 { get; set; } 68 | public double Median { get; set; } 69 | public double Mean { get; set; } 70 | public double Q3 { get; set; } 71 | public double UpperFence { get; set; } 72 | public double Max { get; set; } 73 | public double InterquartileRange { get; set; } 74 | public List LowerOutliers { get; set; } 75 | public List UpperOutliers { get; set; } 76 | public List AllOutliers { get; set; } 77 | public double StandardError { get; set; } 78 | public double Variance { get; set; } 79 | public double StandardDeviation { get; set; } 80 | public double? Skewness { get; set; } 81 | public double? Kurtosis { get; set; } 82 | public ConfidenceInterval ConfidenceInterval { get; set; } 83 | public Percentiles Percentiles { get; set; } 84 | } 85 | 86 | public class Memory 87 | { 88 | public int Gen0Collections { get; set; } 89 | public int Gen1Collections { get; set; } 90 | public int Gen2Collections { get; set; } 91 | public long TotalOperations { get; set; } 92 | public long BytesAllocatedPerOperation { get; set; } 93 | } 94 | 95 | public class Measurement 96 | { 97 | public string IterationStage { get; set; } 98 | public int LaunchIndex { get; set; } 99 | public int IterationIndex { get; set; } 100 | public long Operations { get; set; } 101 | public double Nanoseconds { get; set; } 102 | } 103 | 104 | public class Metric 105 | { 106 | public double Value { get; set; } 107 | public MetricDescriptor Descriptor { get; set; } 108 | } 109 | 110 | public class MetricDescriptor 111 | { 112 | public string Id { get; set; } 113 | public string DisplayName { get; set; } 114 | public string Legend { get; set; } 115 | public string NumberFormat { get; set; } 116 | public int UnitType { get; set; } 117 | public string Unit { get; set; } 118 | public bool TheGreaterTheBetter { get; set; } 119 | public int PriorityInCategory { get; set; } 120 | } 121 | 122 | public class Benchmark 123 | { 124 | public string DisplayInfo { get; set; } 125 | public string Namespace { get; set; } 126 | public string Type { get; set; } 127 | public string Method { get; set; } 128 | public string MethodTitle { get; set; } 129 | public string Parameters { get; set; } 130 | public string FullName { get; set; } 131 | public Statistics Statistics { get; set; } 132 | public Memory Memory { get; set; } 133 | public List Measurements { get; set; } 134 | public List Metrics { get; set; } 135 | } 136 | 137 | public class BdnResult 138 | { 139 | public string Title { get; set; } 140 | public HostEnvironmentInfo HostEnvironmentInfo { get; set; } 141 | public List Benchmarks { get; set; } 142 | } 143 | 144 | public class BdnParser 145 | { 146 | // Return performance of this benchmark (in microseconds) 147 | public static double GetPerf(string bdnJsonFile) 148 | { 149 | double perf = 0; 150 | string bdnJsonLines = File.ReadAllText(bdnJsonFile); 151 | BdnResult bdnResult = JsonSerializer.Deserialize(bdnJsonLines)!; 152 | 153 | // Assume all runs are for the same benchmark 154 | // Handle possibility of multiple runs (via --LaunchCount) 155 | // 156 | foreach (Benchmark b in bdnResult.Benchmarks) 157 | { 158 | double sum = 0; 159 | long ops = 0; 160 | 161 | foreach (Measurement m in b.Measurements) 162 | { 163 | if (!m.IterationStage.Equals("Result")) 164 | { 165 | continue; 166 | } 167 | 168 | sum += m.Nanoseconds; 169 | ops += m.Operations; 170 | } 171 | 172 | perf = (sum / ops) / 1000; 173 | } 174 | 175 | return perf; 176 | } 177 | } -------------------------------------------------------------------------------- /src/performance-explorer/cse-experiment.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | 6 | using PerformanceExplorer; 7 | using System.Globalization; 8 | using System.IO; 9 | using System.Text.Json; 10 | using System; 11 | using System.Xml.Serialization; 12 | 13 | public class CseExperiment 14 | { 15 | public BenchmarkInfo Benchmark { get; set; } 16 | public CseExperiment Baseline { get; set; } 17 | public HotFunction Method { get; set; } 18 | public uint Mask { get; set; } 19 | public uint NumCse { get; set; } 20 | public uint CodeSize { get; set; } 21 | public double PerfScore { get; set; } 22 | public double Perf { get; set; } 23 | public bool Explored { get; set; } 24 | 25 | public string Hash { get; set; } 26 | 27 | public uint Index { get; set; } 28 | 29 | public bool IsImprovement { get { return Perf < Baseline.Perf; } } 30 | 31 | public static string Schema 32 | { 33 | get 34 | { 35 | return "Benchmark,Index,Mask,NumCse,CodeSize,PerfScore,PerfScoreRatio,Perf,PerfRatio"; 36 | } 37 | } 38 | 39 | public string Info 40 | { 41 | get 42 | { 43 | double perfRatio = (Baseline == null) ? 1.0 : Perf / Baseline.Perf; 44 | double perfScoreRatio = (Baseline == null) ? 1.0 : PerfScore / Baseline.PerfScore; 45 | return $"{Benchmark.CsvName},{Index},{Mask:x8},{NumCse},{CodeSize},{PerfScore:F2},{perfScoreRatio:F3},{Perf:F4},{perfRatio:F3}"; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/performance-explorer/hot-function.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System; 6 | using System.IO; 7 | using System.Text; 8 | 9 | public class HotFunction 10 | { 11 | public double Fraction { get; set; } 12 | public string CodeType { get; set; } 13 | public string Name { get; set; } 14 | 15 | string disasmName; 16 | string friendlyName; 17 | 18 | public string DisasmName { get { SetNames(); return disasmName; } } 19 | public string FriendlyName { get { SetNames(); return friendlyName; } } 20 | 21 | void SetNames() 22 | { 23 | int classNameStart = Name.IndexOf(']') + 1; 24 | int classInstantiationStart = Name.IndexOf('[', classNameStart); // skip module name 25 | // Remove all instantiations, module name, and parameters 26 | string name = RemoveMatched(Name, '(', ')'); 27 | name = RemoveMatched(name, '[', ']'); 28 | 29 | int lastDot = name.LastIndexOf('.'); 30 | while (name[lastDot - 1] == '.') 31 | { 32 | lastDot--; 33 | } 34 | string methodName = name[(lastDot + 1)..]; 35 | string className = name[..lastDot]; 36 | if (classInstantiationStart != -1 && classInstantiationStart < lastDot) 37 | { 38 | className = Name[classNameStart..classInstantiationStart]; 39 | } 40 | disasmName = $"*{className}:*{methodName}"; 41 | friendlyName = $"{className}.{methodName}"; 42 | } 43 | 44 | private static string RemoveMatched(string text, char open, char close) 45 | { 46 | StringBuilder newString = new StringBuilder(text.Length); 47 | int nest = 0; 48 | for (int i = 0; i < text.Length; i++) 49 | { 50 | if (text[i] == open) 51 | { 52 | nest++; 53 | } 54 | else if (nest > 0 && text[i] == close) 55 | { 56 | nest--; 57 | } 58 | else if (nest == 0) 59 | { 60 | newString.Append(text[i]); 61 | } 62 | } 63 | 64 | return newString.ToString(); 65 | } 66 | 67 | public string DasmFileName 68 | { 69 | get 70 | { 71 | string fileName = FriendlyName + ".dasm"; 72 | foreach (char illegalChar in Path.GetInvalidFileNameChars()) 73 | fileName = fileName.Replace(illegalChar, '_'); 74 | fileName = fileName.Replace(' ', '_'); 75 | return fileName; 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /src/performance-explorer/performance-explorer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Exe 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/pmi/JITDecisionEventListener.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.Tracing; 4 | 5 | namespace pmi 6 | { 7 | internal class JITDecisionEventListener : EventListener 8 | { 9 | // We cannot use a parameter to this event listener because 10 | // EventListener constructor calls OnEventWritten, which will happen 11 | // before we have been able to run our own constructor. 12 | internal static readonly HashSet s_enabledEvents = new HashSet(); 13 | 14 | protected override void OnEventSourceCreated(EventSource eventSource) 15 | { 16 | if (eventSource.Name != "Microsoft-Windows-DotNETRuntime") 17 | return; 18 | 19 | EventKeywords jitTracing = (EventKeywords)0x1000; // JITTracing 20 | EnableEvents(eventSource, EventLevel.Verbose, jitTracing); 21 | } 22 | 23 | protected override void OnEventWritten(EventWrittenEventArgs data) 24 | { 25 | if (!s_enabledEvents.Contains(data.EventName)) 26 | return; 27 | 28 | List dataStrings = new List { data.EventName }; 29 | 30 | for (int i = 0; i < data.Payload.Count; i++) 31 | { 32 | dataStrings.Add(data.PayloadNames[i]); 33 | dataStrings.Add(data.Payload[i] != null ? data.Payload[i].ToString() : ""); 34 | } 35 | 36 | // Log payload separated by @!@!@. This is somewhat ugly, but easy enough to parse 37 | // and avoids pulling in a dependency here. 38 | Console.WriteLine("JITTracing: {0}", string.Join("@!@!@", dataStrings)); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/pmi/pmi.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/pmi/readme.md: -------------------------------------------------------------------------------- 1 | # PMI - use Prepare Method (Instantiation) to jit all methods in an assembly 2 | 3 | PMI uses reflection to locate all the types in an assembly and all methods 4 | each type. Then it calls `PrepareMethod` on each method in turn. 5 | 6 | This gives us the ability to look at the code the jit will generate for a large 7 | number of methods without needing to have test cases that call the methods. So 8 | it is very useful for doing widespread jit-time testing and analysis of jit 9 | codegen. 10 | 11 | The methods jitted are not called, so PMI is not as useful for finding bugs in 12 | the jit-generated code. 13 | 14 | This initial commit is a preliminary port of the PMI tool we have developed for 15 | .Net Framework testing. Over time we'll improve it and adapt it better for use 16 | with .Net Core. 17 | 18 | Improvements to come: 19 | * proper subprocess launching for core 20 | * integrated support for alt jits and/or alternate codegen modes 21 | * jitting of methods in generic types 22 | * jitting of generic methods 23 | * support for corelib 24 | * (possibly) the ability to fetch code via nuget 25 | * integration into jit-diffs 26 | 27 | -------------------------------------------------------------------------------- /src/superpmi/superpmicollect.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/target-framework.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | net8.0 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/util/CommandLineHelpers.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.CommandLine; 6 | using System.CommandLine.Parsing; 7 | using System.IO; 8 | 9 | public static class Helpers 10 | { 11 | public static RootCommand UseVersion(this RootCommand command) 12 | { 13 | for (int i = 0; i < command.Options.Count; i++) 14 | { 15 | if (command.Options[i] is VersionOption) 16 | { 17 | command.Options[i] = new VersionOption("--version", "-v"); 18 | break; 19 | } 20 | } 21 | 22 | return command; 23 | } 24 | 25 | #nullable enable 26 | public static string? GetResolvedPath(ArgumentResult result) => 27 | result.Tokens.Count > 0 ? Path.GetFullPath(result.Tokens[0].Value) : null; 28 | #nullable disable 29 | } 30 | -------------------------------------------------------------------------------- /test/jit-analyze/baseline1.out: -------------------------------------------------------------------------------- 1 | No diffs found. 2 | -------------------------------------------------------------------------------- /test/jit-analyze/baseline2.out: -------------------------------------------------------------------------------- 1 | Found files with textual diffs. 2 | 3 | Summary: 4 | (Note: Lower is better) 5 | 6 | Total bytes of diff: 0 7 | 8 | 0 total files with size differences. 9 | 10 | Top method regessions by size (bytes): 11 | 2 : test2.dasm - DomainNeutralILStubClass:IL_STUB_ReversePInvoke(long,int,ubyte,long,long,long,long) 12 | 13 | Top method improvements by size (bytes): 14 | -2 : test2.dasm - System.ArraySegment`1[__Canon][System.__Canon]:System.Collections.Generic.IReadOnlyList.get_Item(int):ref:this 15 | 16 | 2 total methods with size differences. 17 | -------------------------------------------------------------------------------- /test/jit-analyze/baseline3.out: -------------------------------------------------------------------------------- 1 | Found files with textual diffs. 2 | 3 | Summary: 4 | (Note: Lower is better) 5 | 6 | Total bytes of diff: -26 7 | diff is an improvement. 8 | 9 | Top file improvements by size (bytes): 10 | -26 : test3.dasm 11 | 12 | 1 total files with size differences. 13 | 14 | Top method regessions by size (bytes): 15 | 6 : test3.dasm - System.ArraySegment`1[__Canon][System.__Canon]:System.Collections.Generic.IReadOnlyList.get_Item(int):ref:this 16 | 2 : test2.dasm - DomainNeutralILStubClass:IL_STUB_ReversePInvoke(long,int,ubyte,long,long,long,long) 17 | 18 | Top method improvements by size (bytes): 19 | -18 : test3.dasm - DomainNeutralILStubClass:IL_STUB_PInvoke(int):int 20 | -6 : test3.dasm - DomainNeutralILStubClass:IL_STUB_PInvoke(ref,ref):bool 21 | -3 : test3.dasm - Microsoft.Win32.SafeHandles.SafeFileHandle:.ctor():this 22 | -2 : test3.dasm - Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid:get_IsInvalid():bool:this 23 | -2 : test3.dasm - DomainNeutralILStubClass:IL_STUB_PInvoke(long,int,long,long):int 24 | 25 | 9 total methods with size differences. 26 | -------------------------------------------------------------------------------- /test/jit-analyze/runtest.sh: -------------------------------------------------------------------------------- 1 | # Run analysis tool with indentical input and ensure that we get no diffs. 2 | 3 | if jit-analyze --base ./base/test1.dasm --diff ./diff/test1.dasm > test1.out; then 4 | echo "Test1: Passed null diff test" 5 | else 6 | echo "Test1: Failed" 7 | fi 8 | 9 | if diff ./test1.out ./baseline1.out; then 10 | echo "Test1: Passed baseline check" 11 | else 12 | echo "Test1: Failed baseline check" 13 | fi 14 | 15 | jit-analyze --base ./base/test2.dasm --diff ./diff/test2.dasm > test2.out 16 | RESULT=$? 17 | #echo $RESULT 18 | if [ $RESULT == 0 ]; then 19 | echo "Test2: Passed diff command" 20 | else 21 | echo "Test2: Failed" 22 | fi 23 | 24 | if diff ./test2.out ./baseline2.out; then 25 | echo "Test2: Passed baseline check" 26 | else 27 | echo "Test2: Failed baseline check" 28 | fi 29 | 30 | jit-analyze --base ./base --diff ./diff > test3.out 31 | RESULT=$? 32 | #echo $RESULT 33 | if [ $RESULT == 26 ]; then 34 | echo "Test3: Passed diff command" 35 | else 36 | echo "Test3: Failed" 37 | fi 38 | 39 | if diff ./test3.out ./baseline3.out; then 40 | echo "Test3: Passed baseline check" 41 | else 42 | echo "Test3: Failed baseline check" 43 | fi -------------------------------------------------------------------------------- /test/jit-dasm/runtest.sh: -------------------------------------------------------------------------------- 1 | # Runs a set of simple tests to validate that JITDASM is working 2 | # 3 | # Required input is a path to a single built CoreCLR repo as well as 4 | # the built JITDASM executable. 5 | # 6 | # Tests will run through the simple scenarios to ensure that the flags work, 7 | # as well as output structure being laid out as expected. 8 | # 9 | 10 | # Test 1: Run JITDASM with the same crossgen to verify that --base, --diff 11 | # work with --frameworks and that the output is generated with the correct 12 | # 'base' and 'diff' tags. 13 | 14 | #set -x #echo on 15 | 16 | # Process the incoming arguments and extract the location info needed. 17 | 18 | while getopts :d:c:o: opt; do 19 | case $opt in 20 | d) 21 | JITDASM=$OPTARG 22 | ;; 23 | o) 24 | OUTPUT=$OPTARG 25 | ;; 26 | c) 27 | CROSSGEN=$OPTARG 28 | ;; 29 | :) 30 | echo "-$OPTARG requires an argument" 31 | exit -1 32 | ;; 33 | esac 34 | done 35 | 36 | # Test that we have the needed info to run the test. 37 | 38 | if [ -z "$JITDASM" ]; then 39 | echo "Missing JITDASM path." 40 | exit -1 41 | fi 42 | 43 | if [ -z "$CROSSGEN" ]; then 44 | echo "Missing crossgen path." 45 | exit -1 46 | fi 47 | 48 | if [ -z "$OUTPUT" ]; then 49 | echo "Missing output." 50 | exit -1 51 | fi 52 | 53 | # Create disasm of mscorlib in base/diff form. 54 | 55 | echo Running: $JITDASM --base $CROSSGEN --diff $CROSSGEN --output $OUTPUT ${CROSSGEN%/*}/mscorlib.dll 56 | 57 | if ! $JITDASM --base $CROSSGEN --diff $CROSSGEN --output $OUTPUT ${CROSSGEN%/*}/mscorlib.dll; then 58 | echo "Error! Managed code gen diff failed to generate disasm." 59 | exit -1 60 | fi 61 | 62 | # test that output has 'base' and 'diff' and 63 | # that mscorlib.dasm appears. 64 | 65 | if ! ls $OUTPUT/base/mscorlib.dasm; then 66 | echo "missing base disasm!" 67 | exit -1 68 | fi 69 | 70 | if ! ls $OUTPUT/diff/mscorlib.dasm; then 71 | echo "missing diff disasm!" 72 | exit -1 73 | fi 74 | 75 | # verify that mscorlib.dasm is nodiff. 76 | 77 | if ! diff $OUTPUT/diff/mscorlib.dasm $OUTPUT/base/mscorlib.dasm; then 78 | echo "Error! Found differences." 79 | exit -1 80 | fi 81 | 82 | echo $JITDASM passed validation. -------------------------------------------------------------------------------- /test/jit-format/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AccessModifierOffset: -4 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveAssignments: true 6 | AlignConsecutiveDeclarations: true 7 | AlignEscapedNewlinesLeft: false 8 | AlignOperands: true 9 | AlignTrailingComments: true 10 | AllowAllParametersOfDeclarationOnNextLine: true 11 | AllowShortBlocksOnASingleLine: false 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortFunctionsOnASingleLine: Empty 14 | AllowShortIfStatementsOnASingleLine: false 15 | AllowShortLoopsOnASingleLine: false 16 | AlwaysBreakAfterDefinitionReturnType: None 17 | AlwaysBreakBeforeMultilineStrings: false 18 | AlwaysBreakTemplateDeclarations: true 19 | BinPackArguments: true 20 | BinPackParameters: false 21 | BraceWrapping: 22 | AfterClass: true 23 | AfterControlStatement: true 24 | AfterEnum: false 25 | AfterFunction: true 26 | AfterNamespace: false 27 | AfterObjCDeclaration: false 28 | AfterStruct: true 29 | AfterUnion: true 30 | BeforeCatch: true 31 | BeforeElse: true 32 | IndentBraces: false 33 | BreakBeforeBinaryOperators: None 34 | BreakBeforeBraces: Allman 35 | BreakBeforeTernaryOperators: true 36 | BreakConstructorInitializersBeforeComma: false 37 | ColumnLimit: 120 38 | CommentPragmas: '^ IWYU pragma:' 39 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 40 | ConstructorInitializerIndentWidth: 4 41 | ContinuationIndentWidth: 4 42 | Cpp11BracedListStyle: true 43 | DerivePointerAlignment: false 44 | DisableFormat: false 45 | ExperimentalAutoDetectBinPacking: false 46 | ForEachMacros: [ ] 47 | IndentCaseLabels: true 48 | IndentWidth: 4 49 | IndentWrappedFunctionNames: false 50 | KeepEmptyLinesAtTheStartOfBlocks: true 51 | MacroBlockBegin: '' 52 | MacroBlockEnd: '' 53 | MaxEmptyLinesToKeep: 1 54 | NamespaceIndentation: None 55 | ObjCBlockIndentWidth: 2 56 | ObjCSpaceAfterProperty: false 57 | ObjCSpaceBeforeProtocolList: true 58 | PenaltyBreakBeforeFirstCallParameter: 400 59 | PenaltyBreakComment: 50 60 | PenaltyBreakFirstLessLess: 500 61 | PenaltyBreakString: 1000 62 | PenaltyExcessCharacter: 1000000 63 | PenaltyReturnTypeOnItsOwnLine: 100000 64 | PointerAlignment: Left 65 | ReflowComments: true 66 | SortIncludes: false 67 | SpaceAfterCStyleCast: false 68 | SpaceBeforeAssignmentOperators: true 69 | SpaceBeforeParens: ControlStatements 70 | SpaceInEmptyParentheses: false 71 | SpacesBeforeTrailingComments: 1 72 | SpacesInAngles: false 73 | SpacesInContainerLiterals: true 74 | SpacesInCStyleCastParentheses: false 75 | SpacesInParentheses: false 76 | SpacesInSquareBrackets: false 77 | Standard: Cpp11 78 | TabWidth: 4 79 | UseTab: Never 80 | ... 81 | -------------------------------------------------------------------------------- /test/jit-format/runtest.cmd: -------------------------------------------------------------------------------- 1 | rem Run jit-format tool on test.cpp and compare the output to test-fixed.cpp 2 | 3 | setlocal 4 | 5 | set testroot=%~dp0 6 | set testrootReplace=%testroot:\=/% 7 | set jitUtilsBin=%testroot%\..\..\bin 8 | 9 | copy test.cpp test-pre.cpp 10 | 11 | echo [> %testroot%\compile_commands.json 12 | echo {>> %testroot%\compile_commands.json 13 | echo "directory": "%testrootReplace%",>> %testroot%\compile_commands.json 14 | echo "command": "cl.exe %testrootReplace%test.cpp",>> %testroot%\compile_commands.json 15 | echo "file": "%testrootReplace%test.cpp">> %testroot%\compile_commands.json 16 | echo }>> %testroot%\compile_commands.json 17 | echo ]>> %testroot%\compile_commands.json 18 | 19 | rem Because we specified full paths, we will ignore the --coreclr argument, but it is required 20 | call %jitUtilsBin%\jit-format.cmd --fix --compile-commands %testroot%\compile_commands.json --coreclr %testroot% %testroot%\test.cpp 21 | 22 | timeout 1 > NUL 23 | 24 | fc test.cpp test-fixed.cpp 25 | 26 | if "%errorlevel%" == "0" ( 27 | echo Test Passed 28 | ) else ( 29 | echo Test Failed 30 | ) 31 | 32 | move /Y test-pre.cpp test.cpp 33 | -------------------------------------------------------------------------------- /test/jit-format/test-fixed.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // This is a really long comment that needs to be reflowed. Clang-format should take care of this for us. This function 5 | // is just a test for the code below. 6 | static void testFunction(int* ptr) 7 | { 8 | if (ptr) 9 | { 10 | printf("You passed a pointer!\n"); 11 | } 12 | else 13 | { 14 | printf("You passed a nullptr!\n"); 15 | } 16 | } 17 | 18 | int main(void) 19 | { 20 | int i; 21 | 22 | // clang-tidy should insert braces in the below for loop. clang-format should removed the spaces above, and move the 23 | // braces clang-tidy adds below to the following line and re-indent them. Clang-formatting should fix all of the 24 | // indentation in this function. 25 | for (i = 0; i < 3; i++) 26 | { 27 | printf("Hello World!\n"); 28 | } 29 | 30 | // clang-tidy should replace both of these instances with nullptr. 31 | testFunction(nullptr); 32 | testFunction(nullptr); 33 | 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /test/jit-format/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // This is a really long comment that needs to be reflowed. Clang-format should take care of this for us. This function is just a test for the code below. 5 | static void testFunction(int* ptr) 6 | { 7 | if (ptr) 8 | printf("You passed a pointer!\n"); 9 | else 10 | printf("You passed a nullptr!\n"); 11 | } 12 | 13 | int main(void) 14 | { 15 | int i; 16 | 17 | // clang-tidy should insert braces in the below for loop. clang-format should removed the spaces above, and move the 18 | // braces clang-tidy adds below to the following line and re-indent them. Clang-formatting should fix all of the 19 | // indentation in this function. 20 | for (i = 0; i < 3; i++) 21 | printf("Hello World!\n"); 22 | 23 | // clang-tidy should replace both of these instances with nullptr. 24 | testFunction(NULL); 25 | testFunction(0); 26 | 27 | return 0; 28 | } 29 | --------------------------------------------------------------------------------