diff --git a/CMakeLists.txt b/CMakeLists.txt index a40d7f284df8..a25742172110 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -486,6 +486,11 @@ if(CLR_CROSS_COMPONENTS_BUILD) include(crosscomponents.cmake) endif(CLR_CROSS_COMPONENTS_BUILD) +#------------------- +# Enable PGO support +#------------------- +include(pgosupport.cmake) + #----------------------------------------- # Add Projects # - project which require platform header not clr's diff --git a/build.cmd b/build.cmd index 0974b000add3..2dc479c2c319 100644 --- a/build.cmd +++ b/build.cmd @@ -52,6 +52,8 @@ set __BuildTypeChecked=0 set __BuildTypeRelease=0 set __BuildJit32="-DBUILD_JIT32=0" +set __PgoInstrument=0 + REM __PassThroughArgs is a set of things that will be passed through to nested calls to build.cmd REM when using "all". set __PassThroughArgs= @@ -103,6 +105,7 @@ if /i "%1" == "skiptests" (set __BuildTests=0&set processedArgs=!proce if /i "%1" == "skipbuildpackages" (set __BuildPackages=0&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "usenmakemakefiles" (set __NMakeMakefiles=1&set __ConfigureOnly=1&set __BuildNative=1&set __BuildNativeCoreLib=0&set __BuildCoreLib=0&set __BuildTests=0&set __BuildPackages=0&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "buildjit32" (set __BuildJit32="-DBUILD_JIT32=1"&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) +if /i "%1" == "pgoinstrument" (set __PgoInstrument=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "toolset_dir" (set __ToolsetDir=%2&set __PassThroughArgs=%__PassThroughArgs% %2&set processedArgs=!processedArgs! %1 %2&shift&shift&goto Arg_Loop) if [!processedArgs!]==[] ( @@ -186,6 +189,15 @@ call "%__VSToolsRoot%\VsDevCmd.bat" @call %__ProjectDir%\run.cmd build -Project=%__ProjectDir%\build.proj -generateHeaderWindows -NativeVersionHeaderFile="%__RootBinDir%\obj\_version.h" %__RunArgs% %__UnprocessedBuildArgs% +REM ========================================================================================= +REM === +REM === Restore optimization profile data +REM === +REM ========================================================================================= + +echo %__MsgPrefix%Restoring the OptimizationData Package +@call %__ProjectDir%\run.cmd sync -optdata + REM ========================================================================================= REM === REM === Build the CLR VM @@ -228,7 +240,8 @@ if %__BuildNative% EQU 1 ( echo %__MsgPrefix%Regenerating the Visual Studio solution pushd "%__IntermediatesDir%" - call "%__SourceDir%\pal\tools\gen-buildsys-win.bat" "%__ProjectDir%" %__VSVersion% %__BuildArch% %__BuildJit32% + set __ExtraCmakeArgs="-DCLR_CMAKE_TARGET_OS=%__BuildOs%" "-DCLR_CMAKE_PACKAGES_DIR=%__PackagesDir%" "-DCLR_CMAKE_PGO_INSTRUMENT=%__PgoInstrument%" + call "%__SourceDir%\pal\tools\gen-buildsys-win.bat" "%__ProjectDir%" %__VSVersion% %__BuildArch% %__BuildJit32% !__ExtraCmakeArgs! @if defined __echo @echo on popd :SkipConfigure @@ -281,7 +294,7 @@ if /i "%__DoCrossArchBuild%"=="1" ( pushd "%__CrossCompIntermediatesDir%" set __CMakeBinDir=%__CrossComponentBinDir% set "__CMakeBinDir=!__CMakeBinDir:\=/!" - set __ExtraCmakeArgs="-DCLR_CROSS_COMPONENTS_BUILD=1" "-DCLR_CMAKE_TARGET_ARCH=%__BuildArch%" + set __ExtraCmakeArgs="-DCLR_CROSS_COMPONENTS_BUILD=1" "-DCLR_CMAKE_TARGET_ARCH=%__BuildArch%" "-DCLR_CMAKE_TARGET_OS=%__BuildOs%" "-DCLR_CMAKE_PACKAGES_DIR=%__PackagesDir%" "-DCLR_CMAKE_PGO_INSTRUMENT=%__PgoInstrument%" call "%__SourceDir%\pal\tools\gen-buildsys-win.bat" "%__ProjectDir%" %__VSVersion% %__CrossArch% !__ExtraCmakeArgs! @if defined __echo @echo on popd @@ -522,6 +535,7 @@ echo for the specified platform ^(FreeBSD, Linux, NetBSD, OS X or Windows, echo respectively^). echo add nativemscorlib to go further and build the native image for designated mscorlib. echo toolset_dir ^ : set the toolset directory -- Arm64 use only. Required for Arm64 builds. +echo pgoinstrument: generate instrumented code for profile guided optimization enabled binaries. echo configureonly: skip all builds; only run CMake ^(default: CMake and builds are run^) echo skipconfigure: skip CMake ^(default: CMake is run^) echo skipmscorlib: skip building System.Private.CoreLib ^(default: System.Private.CoreLib is built^). diff --git a/build.proj b/build.proj index 7df2904e3690..c8d7a5d76bfe 100644 --- a/build.proj +++ b/build.proj @@ -24,6 +24,10 @@ + + + + diff --git a/build.sh b/build.sh index 5959abdfa996..e639ca0197ba 100755 --- a/build.sh +++ b/build.sh @@ -35,6 +35,7 @@ usage() echo "clangx.y - optional argument to build using clang version x.y." echo "cross - optional argument to signify cross compilation," echo " - will use ROOTFS_DIR environment variable if set." + echo "pgoinstrument - generate instrumented code for profile guided optimization enabled binaries." echo "configureonly - do not perform any builds; just configure the build." echo "skipconfigure - skip build configuration." echo "skipnative - do not build native components." @@ -175,10 +176,14 @@ build_coreclr() echo $__versionSourceLine > $__versionSourceFile fi + echo "Restoring the OptimizationData package" + "$__ProjectRoot/run.sh" sync -optdata + pushd "$__IntermediatesDir" # Regenerate the CMake solution - echo "Invoking \"$__ProjectRoot/src/pal/tools/gen-buildsys-clang.sh\" \"$__ProjectRoot\" $__ClangMajorVersion $__ClangMinorVersion $__BuildArch $__BuildType $__CodeCoverage $__IncludeTests $generator $__cmakeargs" - "$__ProjectRoot/src/pal/tools/gen-buildsys-clang.sh" "$__ProjectRoot" $__ClangMajorVersion $__ClangMinorVersion $__BuildArch $__BuildType $__CodeCoverage $__IncludeTests $generator "$__cmakeargs" + __ExtraCmakeArgs="-DCLR_CMAKE_TARGET_OS=$__BuildOS -DCLR_CMAKE_PACKAGES_DIR=$__PackagesDir -DCLR_CMAKE_PGO_INSTRUMENT=$__PgoInstrument" + echo "Invoking \"$__ProjectRoot/src/pal/tools/gen-buildsys-clang.sh\" \"$__ProjectRoot\" $__ClangMajorVersion $__ClangMinorVersion $__BuildArch $__BuildType $__CodeCoverage $__IncludeTests $generator $__ExtraCmakeArgs $__cmakeargs" + "$__ProjectRoot/src/pal/tools/gen-buildsys-clang.sh" "$__ProjectRoot" $__ClangMajorVersion $__ClangMinorVersion $__BuildArch $__BuildType $__CodeCoverage $__IncludeTests $generator "$__ExtraCmakeArgs" "$__cmakeargs" popd fi @@ -459,6 +464,7 @@ __RunArgs= __MSBCleanBuildArgs= __UseNinja=0 __VerboseBuild=0 +__PgoInstrument=0 __ConfigureOnly=0 __SkipConfigure=0 __SkipRestore="" @@ -558,6 +564,10 @@ while :; do __UseNinja=1 ;; + pgoinstrument) + __PgoInstrument=1 + ;; + configureonly) __ConfigureOnly=1 __SkipMSCorLib=1 diff --git a/config.json b/config.json index 1ddb2f94f508..d25fdba043d2 100644 --- a/config.json +++ b/config.json @@ -48,6 +48,12 @@ "values": [], "defaultValue": "" }, + "RestoreOptData": { + "description": "MsBuild target that restores optimization profile data.", + "valueType": "target", + "values": [], + "defaultValue": "" + }, "RestoreDuringBuild": { "description": "Enables/disables package restore.", "valueType": "property", @@ -362,6 +368,14 @@ "RestoreNETCorePlatforms": "default" } }, + "optdata": { + "description": "Restores optimization profile data for the repository.", + "settings": { + "Project": "./build.proj", + "RestoreDuringBuild": true, + "RestoreOptData": "default" + } + }, "ab": { "description": "Downloads the latests product packages from Azure. The values for '-AzureAccount' and '-AzureToken' are required", "settings": { diff --git a/extract-from-json.py b/extract-from-json.py new file mode 100755 index 000000000000..e432b2b067f0 --- /dev/null +++ b/extract-from-json.py @@ -0,0 +1,56 @@ +#!/usr/bin/python + +import argparse +import json +import sys + +def parse_args(): + parser = argparse.ArgumentParser( + description="""Extracts information from a json file by navigating the JSON object using a + sequence of property accessors and returning the JSON subtree, or the raw data, found + at that location.""" + ) + + parser.add_argument( + '-f', '--file', + metavar='', + help="Path to project.json file to parse", + required=True, + ) + + parser.add_argument( + 'property', + metavar='property_name', + help="""Name of property to extract using object notation. + Pass multiple values to drill down into nested objects (in order).""", + nargs='*', + ) + + parser.add_argument( + '-r', '--raw', + help="""Dumps the raw object found at the requested location. + If omitted, returns a JSON formatted object instead.""", + action='store_true', + default=False + ) + + return parser.parse_args() + +def main(): + args = parse_args() + + with open(args.file) as json_file: + selected_property = json.load(json_file) + + for prop in args.property: + selected_property = selected_property[prop] + + if args.raw: + print(selected_property) + else: + print(json.dumps(selected_property)) + + return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/functions.cmake b/functions.cmake index df2216c8854b..f8a2eeae048d 100644 --- a/functions.cmake +++ b/functions.cmake @@ -179,6 +179,11 @@ function(install_clr targetName) else() install(FILES ${strip_destination_file} DESTINATION .) endif() + if(CLR_CMAKE_PGO_INSTRUMENT) + if(WIN32) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/$/${targetName}.pgd DESTINATION PGD OPTIONAL) + endif() + endif() endif() endfunction() diff --git a/pgosupport.cmake b/pgosupport.cmake new file mode 100644 index 000000000000..27fe13df6d00 --- /dev/null +++ b/pgosupport.cmake @@ -0,0 +1,80 @@ +function(clr_pgo_unknown_arch) + if (WIN32) + message(FATAL_ERROR "Only AMD64, ARM and I386 are supported for PGO") + else() + message(FATAL_ERROR "PGO not currently supported on the current platform") + endif() +endfunction(clr_pgo_unknown_arch) + +# Adds Profile Guided Optimization (PGO) flags to the current target +function(add_pgo TargetName) + if(WIN32) + set(ProfileFileName "${TargetName}.pgd") + endif(WIN32) + + file(TO_NATIVE_PATH + "${CLR_CMAKE_PACKAGES_DIR}/${CLR_CMAKE_OPTDATA_PACKAGEWITHRID}/${CLR_CMAKE_OPTDATA_VERSION}/data/${ProfileFileName}" + ProfilePath + ) + + # Enable PGO only for optimized configs + set(ConfigTypeList RELEASE RELWITHDEBINFO) + + foreach(ConfigType IN LISTS ConfigTypeList) + set(LinkFlagsProperty "LINK_FLAGS_${ConfigType}") + if(CLR_CMAKE_PGO_INSTRUMENT) + if(WIN32) + set_property(TARGET ${TargetName} APPEND_STRING PROPERTY ${LinkFlagsProperty} "/LTCG /GENPROFILE") + endif(WIN32) + else(CLR_CMAKE_PGO_INSTRUMENT) + # If we don't have profile data availble, gracefully fall back to a non-PGO opt build + if(EXISTS ${ProfilePath}) + if(WIN32) + set_property(TARGET ${TargetName} APPEND_STRING PROPERTY ${LinkFlagsProperty} "/LTCG /USEPROFILE:PGD=${ProfilePath}") + endif(WIN32) + endif(EXISTS ${ProfilePath}) + endif(CLR_CMAKE_PGO_INSTRUMENT) + endforeach(ConfigType) +endfunction(add_pgo) + +set(CLR_CMAKE_OPTDATA_PACKAGEID "optimization.PGO.CoreCLR") +set(CLR_CMAKE_OPTDATA_PACKAGEWITHRID "optimization.${CLR_CMAKE_TARGET_OS}-${CLR_CMAKE_TARGET_ARCH}.PGO.CoreCLR") + +# Parse optdata package version from project.json +file(TO_NATIVE_PATH "${CMAKE_SOURCE_DIR}/extract-from-json.py" ExtractFromJsonScript) +file(TO_NATIVE_PATH "${CMAKE_SOURCE_DIR}/src/.nuget/optdata/project.json" OptDataProjectJsonPath) +execute_process( + COMMAND python "${ExtractFromJsonScript}" -rf "${OptDataProjectJsonPath}" dependencies "${CLR_CMAKE_OPTDATA_PACKAGEID}" + OUTPUT_VARIABLE CLR_CMAKE_OPTDATA_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +if(WIN32) + if(CLR_CMAKE_PGO_INSTRUMENT) + # Instrumented PGO binaries on Windows introduce an additional runtime dependency, pgort.dll. + # Make sure we copy it next to the installed product to make it easier to redistribute the package. + + string(SUBSTRING ${CMAKE_VS_PLATFORM_TOOLSET} 1 -1 VS_PLATFORM_VERSION_NUMBER) + set(PGORT_FILENAME "pgort${VS_PLATFORM_VERSION_NUMBER}.dll") + + get_filename_component(PATH_CXX_ROOTDIR ${CMAKE_CXX_COMPILER} DIRECTORY) + + if(CLR_CMAKE_PLATFORM_ARCH_I386) + set(PATH_VS_PGORT_DLL "${PATH_CXX_ROOTDIR}/${PGORT_FILENAME}") + elseif(CLR_CMAKE_PLATFORM_ARCH_AMD64) + set(PATH_VS_PGORT_DLL "${PATH_CXX_ROOTDIR}/../amd64/${PGORT_FILENAME}") + elseif(CLR_CMAKE_PLATFORM_ARCH_ARM) + set(PATH_VS_PGORT_DLL "${PATH_CXX_ROOTDIR}/../arm/${PGORT_FILENAME}") + else() + clr_pgo_unknown_arch() + endif() + + if (EXISTS ${PATH_VS_PGORT_DLL}) + message(STATUS "Found PGO runtime: ${PATH_VS_PGORT_DLL}") + install(PROGRAMS ${PATH_VS_PGORT_DLL} DESTINATION .) + else() + message(FATAL_ERROR "file not found: ${PATH_VS_PGORT_DLL}") + endif() + + endif(CLR_CMAKE_PGO_INSTRUMENT) +endif(WIN32) diff --git a/src/.nuget/optdata/project.json b/src/.nuget/optdata/project.json new file mode 100644 index 000000000000..e8b45c85c0c7 --- /dev/null +++ b/src/.nuget/optdata/project.json @@ -0,0 +1,11 @@ +{ + "dependencies": { + "optimization.PGO.CoreCLR": "1.1.0-release-20161025" + }, + "frameworks": { + "netstandard": {} + }, + "runtimes": { + "win7-x64": {} + } +} diff --git a/src/dlls/mscoree/coreclr/CMakeLists.txt b/src/dlls/mscoree/coreclr/CMakeLists.txt index cc14f9cbdca5..aa7bb0d9b98b 100644 --- a/src/dlls/mscoree/coreclr/CMakeLists.txt +++ b/src/dlls/mscoree/coreclr/CMakeLists.txt @@ -174,3 +174,6 @@ endif(WIN32) # add the install targets install_clr(coreclr) + +# Enable profile guided optimization +add_pgo(coreclr) diff --git a/src/jit/standalone/CMakeLists.txt b/src/jit/standalone/CMakeLists.txt index b4efc30a8e09..2e6317098ee2 100644 --- a/src/jit/standalone/CMakeLists.txt +++ b/src/jit/standalone/CMakeLists.txt @@ -53,3 +53,6 @@ target_link_libraries(${JIT_BASE_NAME} # add the install targets install_clr(${JIT_BASE_NAME}) + +# Enable profile guided optimization +add_pgo(${JIT_BASE_NAME})