diff --git a/docs/workflow/building/coreclr/nativeaot.md b/docs/workflow/building/coreclr/nativeaot.md index 8a7ef8c8ff4f..fb39bd707de7 100644 --- a/docs/workflow/building/coreclr/nativeaot.md +++ b/docs/workflow/building/coreclr/nativeaot.md @@ -11,11 +11,27 @@ The Native AOT toolchain can be currently built for Linux, macOS and Windows x64 - Run `dotnet publish --packages pkg -r [win-x64|linux-x64|osx-64] -c [Debug|Release]` to publish your project. `--packages pkg` option restores the package into a local directory that is easy to cleanup once you are done. It avoids polluting the global nuget cache with your locally built dev package. ## Building for Web Assembly +- This branch contains a version of the WebAssembly compiler that creates LLVM from the clrjit to take advantage of RyuJits optimisations. It goes from RyuJIT IR -> LLVM instead of the NativeAOT-LLVM branch way of CIL -> LLVM. +- It does not work, yet or maybe never. - Currently only tested on Windows +- Download the LLVM 11.0.0 source from https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.0/llvm-11.0.0.src.tar.xz +- Extract and create a subdirectory in the llvm-11.0.0.src folder called build. cd to this build folder +- Configure the LLVM source to use the same runtime as clrjit `cmake -G "Visual Studio 16 2019" -DCMAKE_BUILD_TYPE=Debug -D LLVM_USE_CRT_DEBUG=MTd ..` +- Build LLVM either from the command line (`build`) or from VS 2019. You only really need to build the LLVMCore and LLVMBitWriter projects which takes less time than the 400 odd projects when building all. This will save some time. +- set the enviroment variable LLVM_CMAKE_CONFIG to locate the LLVM config, e.g. `set LLVM_CMAKE_CONFIG=E:/llvm11/llvm-11.0.0.src/build/lib/cmake/llvm` . This location should contain the file `LLVMConfig.cmake` +- Build the x64 libraries and compiler as per the Building section. - Run `build nativeaot+libs+nativeaot.packages -rc [Debug|Release] -lc [Debug|Release] -a wasm -os Browser -runtimeFlavor CoreCLR` +- The compiler can now be debugged with the Wasm clrjit. Load the clrjit_browser_wasm32_x64.vcxproj which can be found in artifacts\obj\coreclr\windows.x64.Debug\jit +- Run Ilc with a .rsp file as normal for Web assembly, e.g. if you build the WebAssembly tests you can use artifacts\tests\coreclr\Browser.wasm.Debug\nativeaot\SmokeTests\HelloWasm\HelloWasm\native\HelloWasm.ilc.rsp - Add the package directory to your `nuget.config` as above. - Run `dotnet publish -r browser-wasm -c [Debug|Release] /p:Platform=wasm` to publish. +- To work on the clr jit for LLVM: +- Open the Ilc solution and add the clr jit project `clrjit_browser_wasm32_x64.vcxproj` from `artifacts\obj\coreclr\windows.x64.Debug\jit` +- In the project properties General section, change the output folder to the full path for `artifacts\bin\coreclr\windows.x64.Debug\ilc\net5.0` e.g. `E:\GitHub\runtimelab\artifacts\bin\coreclr\windows.x64.Debug\ilc\net5.0` +- Build `clrjit_browser_wasm32_x64` project and you should now be able to change and put breakpoints in the c++ code. + + ## Visual Studio Solutions The repository has a number of Visual Studio Solutions files (`*.sln`) that are useful for editing parts of the repository. Build the repo from command line first before building using the solution files. Remember to select the appropriate configuration that you built. By default, `build.cmd` builds Debug x64 and so `Debug` and `x64` must be selected in the solution build configuration drop downs. @@ -40,6 +56,7 @@ The workflow looks like this: - Open the ilc.sln solution described above. This solution contains the compiler, but also an unrelated project named "repro". This repro project is a small Hello World. You can place any piece of C# you would like to compile in it. Building the project will compile the source code into IL, but also generate a response file that is suitable to pass to the AOT compiler. - Make sure you set the solution configuration in VS to the configuration you just built (e.g. x64 Debug). - In the ILCompiler project properties, on the Debug tab, set the "Application arguments" to the generated response file. This will be a file such as "C:\runtimelab\artifacts\bin\repro\x64\Debug\compile-with-Release-libs.rsp". Prefix the path to the file with "@" to indicate this is a response file so that the "Application arguments" field looks like "@some\path\to\file.rsp". +- For WebAssembly, edit the .rsp file and - Build & run ILCompiler using **F5**. This will compile the repro project into an `.obj` file. You can debug the compiler and set breakpoints in it at this point. - The last step is linking the file into an executable so that we can launch the result of the AOT compilation. - Open the src\coreclr\tools\aot\ILCompiler\reproNative\reproNative.vcxproj project in Visual Studio. This project is configured to pick up the `.obj` file we just compiled and link it with the rest of the runtime. diff --git a/eng/common/build.ps1 b/eng/common/build.ps1 index d9b6b3adc038..0d7628b8da70 100644 --- a/eng/common/build.ps1 +++ b/eng/common/build.ps1 @@ -143,6 +143,7 @@ try { } if ($properties.Contains('/p:TargetArchitecture=wasm') -and $runtimeFlavor -eq "CoreCLR") { . $PSScriptRoot\..\..\wasm-tools\emsdk\emsdk_env.ps1 + $Env:LLVM_CMAKE_CONFIG = "$PSScriptRoot\..\..\wasm-tools\llvm-11.0.0.src\build\lib\cmake\llvm" } $nodeReuse = $false } diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake index 81208c46ba21..5245fc90e62c 100644 --- a/eng/native/configurecompiler.cmake +++ b/eng/native/configurecompiler.cmake @@ -530,6 +530,14 @@ if (MSVC) add_compile_options($<$:/wd4291>) add_compile_options($<$:/wd5105>) + # TODO: if for LLVM + add_compile_options($<$:/wd4244>) + add_compile_options($<$:/wd4267>) + add_compile_options($<$:/wd4141>) + add_compile_options($<$:/wd4310>) + add_compile_options($<$:/wd4624>) # destructor was implicitly defined as deleted + add_compile_options($<$:/wd4324>) # structure was padded due to alignment specifier + # Treat Warnings as Errors: # 4007: 'main' : must be __cdecl. # 4013: 'function' undefined - assuming extern returning int. diff --git a/eng/pipelines/common/global-build-job.yml b/eng/pipelines/common/global-build-job.yml index 0bce85ed875d..fe6d51b57e23 100644 --- a/eng/pipelines/common/global-build-job.yml +++ b/eng/pipelines/common/global-build-job.yml @@ -122,9 +122,11 @@ jobs: displayName: Disk Usage before Build - ${{ if and(eq(parameters.runtimeFlavor, 'coreclr'), and(eq(parameters.osGroup, 'windows'), eq(parameters.platform, 'Browser_wasm'))) }}: - # Install Wasm dependencies: emscripten + # Install Wasm dependencies: emscripten, LLVM - script: call $(Build.SourcesDirectory)/eng/pipelines/runtimelab/install-emscripten.cmd $(Build.SourcesDirectory)\wasm-tools displayName: Install/activate emscripten + - script: call $(Build.SourcesDirectory)/eng/pipelines/runtimelab/install-llvm.cmd $(Build.SourcesDirectory)\wasm-tools $(Build.SourcesDirectory) + displayName: Install/build LLVM # Build - script: $(Build.SourcesDirectory)$(dir)build$(scriptExt) -ci -arch ${{ parameters.archType }} $(_osParameter) ${{ parameters.buildArgs }} $(_officialBuildParameter) $(_crossBuildPropertyArg) $(_cxx11Parameter) $(_richCodeNavigationParam) diff --git a/eng/pipelines/runtimelab/install-llvm.cmd b/eng/pipelines/runtimelab/install-llvm.cmd new file mode 100644 index 000000000000..aa0d3c299dcc --- /dev/null +++ b/eng/pipelines/runtimelab/install-llvm.cmd @@ -0,0 +1,24 @@ +mkdir "%1" 2>nul +cd /D "%1" + +set RepoRoot=%2\ + +set +:: Set CMakePath by evaluating the output from set-cmake-path.ps1 +call "%RepoRoot%src\coreclr\setup_vs_tools.cmd" || exit /b 1 +for /f "delims=" %%a in ('powershell -NoProfile -ExecutionPolicy ByPass "& ""%RepoRoot%eng\native\set-cmake-path.ps1"""') do %%a +echo Using CMake at "%CMakePath%" + +set + +powershell -NoProfile -NoLogo -ExecutionPolicy ByPass -command "& """%~dp0install-llvm.ps1""" %*" +if %errorlevel% NEQ 0 goto fail + +echo setting LLVM_CMAKE_CONFIG to %1\llvm-11.0.0.src\build +echo "##vso[task.setvariable variable=LLVM_CMAKE_CONFIG]%1\llvm-11.0.0.src\build" + +exit /b 0 + +fail: +echo "Failed to install llvm" +exit /b 1 diff --git a/eng/pipelines/runtimelab/install-llvm.ps1 b/eng/pipelines/runtimelab/install-llvm.ps1 new file mode 100644 index 000000000000..bfd98b4378a1 --- /dev/null +++ b/eng/pipelines/runtimelab/install-llvm.ps1 @@ -0,0 +1,31 @@ +# LLVM is supplied in a gz file which Windows doesn't native understand, so we need gz to unpack it - TODO this is liable to fail randomly when a new version comes out and the version number changes +Invoke-WebRequest -Uri https://tukaani.org/xz/xz-5.2.5-windows.zip -OutFile xz.zip +Expand-Archive -LiteralPath xz.zip -DestinationPath . +copy bin_i686\xz.exe . # get it in the path for tar + +Invoke-WebRequest -Uri https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.0/llvm-11.0.0.src.tar.xz -OutFile llvm-11.0.0.src.tar.xz + +dir + +./xz -d --force llvm-11.0.0.src.tar.xz + +tar -xf llvm-11.0.0.src.tar + + +cd llvm-11.0.0.src +mkdir build +dir +cd build + + +# TODO Release build +& "$env:CMakePath" -G "Visual Studio 16 2019" -DCMAKE_BUILD_TYPE=Debug -DLLVM_USE_CRT_DEBUG=MTd -Thost=x64 .. + +& "$env:CMakePath" --build . --target LLVMCore +& "$env:CMakePath" --build . --target LLVMBitWriter +#& "$env:CMakePath" --build . --target LLVMDebugInfoDwarf + +dir + + + diff --git a/src/coreclr/inc/clr_std/utility b/src/coreclr/inc/clr_std/utility index 1b6b5a7b72c1..4f72ab46e32a 100644 --- a/src/coreclr/inc/clr_std/utility +++ b/src/coreclr/inc/clr_std/utility @@ -49,6 +49,22 @@ namespace std { // forward _Arg, given explicitly specified type parameter return ((T&&)_Arg); } + + template + constexpr const T& + (min)(const T& _Left, const T& _Right) + { + // return smaller of _Left and _Right + return _Right < _Left ? _Right : _Left; + } + + template + constexpr const T& + (max)(const T& _Left, const T& _Right) + { + // return larger of _Left and _Right + return _Left < _Right ? _Right : _Left; + } } namespace std diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index 4de783aae984..512ab78e74f3 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -145,6 +145,8 @@ class ICorDebugInfo REGNUM_R13, REGNUM_R14, REGNUM_R15, +#elif TARGET_WASM +// TODO???? #else PORTABILITY_WARNING("Register numbers not defined on this platform") #endif diff --git a/src/coreclr/inc/corjit.h b/src/coreclr/inc/corjit.h index 85cb80bc3700..f516aafa3a26 100644 --- a/src/coreclr/inc/corjit.h +++ b/src/coreclr/inc/corjit.h @@ -145,6 +145,9 @@ enum CheckedWriteBarrierKinds { #include "corjithost.h" extern "C" void jitStartup(ICorJitHost* host); +#if TARGET_WASM +extern "C" void jitShutdown(bool processIsTerminating); +#endif class ICorJitCompiler; class ICorJitInfo; diff --git a/src/coreclr/inc/crosscomp.h b/src/coreclr/inc/crosscomp.h index d5d8cc8bd70b..fd2c04c2bc06 100644 --- a/src/coreclr/inc/crosscomp.h +++ b/src/coreclr/inc/crosscomp.h @@ -26,6 +26,10 @@ #endif #endif // TARGET_WINDOWS +#ifdef TARGET_WASM +#include "wasm.h" +#endif + #ifdef UNICODE #define MAKE_TARGET_DLLNAME(name) MAKE_TARGET_DLLNAME_W(name) #else diff --git a/src/coreclr/inc/daccess.h b/src/coreclr/inc/daccess.h index b0269592c9ef..d7b410113a6b 100644 --- a/src/coreclr/inc/daccess.h +++ b/src/coreclr/inc/daccess.h @@ -2371,6 +2371,11 @@ typedef S8PTR(const char) PTR_CUTF8; typedef S16PTR(WCHAR) PTR_WSTR; typedef S16PTR(const WCHAR) PTR_CWSTR; +#if TARGET_WASM +#define T_CONTEXT CONTEXT +#define PT_RUNTIME_FUNCTION PRUNTIME_FUNCTION +#endif + typedef DPTR(T_CONTEXT) PTR_CONTEXT; typedef DPTR(PTR_CONTEXT) PTR_PTR_CONTEXT; typedef DPTR(struct _EXCEPTION_POINTERS) PTR_EXCEPTION_POINTERS; diff --git a/src/coreclr/inc/switches.h b/src/coreclr/inc/switches.h index 8fb65335116b..12e63753ed55 100644 --- a/src/coreclr/inc/switches.h +++ b/src/coreclr/inc/switches.h @@ -65,6 +65,8 @@ #define USE_UPPER_ADDRESS 0 #endif // !HOST_UNIX +#elif defined(TARGET_WASM) + #define USE_UPPER_ADDRESS 0 // not used but is required to be defined #else #error Please add a new #elif clause and define all portability macros for the new platform #endif diff --git a/src/coreclr/jit/CMakeLists.txt b/src/coreclr/jit/CMakeLists.txt index b22f6e8de102..ead9b73e12a0 100644 --- a/src/coreclr/jit/CMakeLists.txt +++ b/src/coreclr/jit/CMakeLists.txt @@ -3,6 +3,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) include_directories("./jitstd") include_directories("../inc") + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") add_compile_options($<$:-fpermissive>) add_compile_options(-Wno-error) @@ -23,7 +24,11 @@ function(create_standalone_jit) endif() set(JIT_ARCH_LINK_LIBRARIES gcinfo_unix_arm64) else() - set(JIT_ARCH_LINK_LIBRARIES gcinfo_${TARGETDETAILS_OS}_${TARGETDETAILS_ARCH}) + if(TARGETDETAILS_OS STREQUAL "browser") + set(JIT_ARCH_LINK_LIBRARIES gcinfo_win_x64) # TODO: Wasm + else() + set(JIT_ARCH_LINK_LIBRARIES gcinfo_${TARGETDETAILS_OS}_${TARGETDETAILS_ARCH}) + endif() endif() if(TARGETDETAILS_ARCH STREQUAL "x64") @@ -34,6 +39,10 @@ function(create_standalone_jit) set(JIT_ARCH_SOURCES ${JIT_I386_SOURCES}) elseif(TARGETDETAILS_ARCH STREQUAL "arm64") set(JIT_ARCH_SOURCES ${JIT_ARM64_SOURCES}) + elseif(TARGETDETAILS_ARCH STREQUAL "wasm64") + set(JIT_ARCH_SOURCES ${JIT_WASM64_SOURCES}) + elseif(TARGETDETAILS_ARCH STREQUAL "wasm32") + set(JIT_ARCH_SOURCES ${JIT_WASM32_SOURCES}) else() clr_unknown_arch() endif() @@ -57,6 +66,25 @@ function(create_standalone_jit) target_compile_definitions(${TARGETDETAILS_TARGET} PRIVATE FEATURE_SIMD) target_compile_definitions(${TARGETDETAILS_TARGET} PRIVATE FEATURE_HW_INTRINSICS) endif () + + if (TARGETDETAILS_ARCH STREQUAL "wasm64" OR TARGETDETAILS_ARCH STREQUAL "wasm32") + set(CLR_CMAKE_TARGET_ARCH_WASM 1) + target_compile_definitions(${TARGETDETAILS_TARGET} PRIVATE TARGET_WASM) + if (TARGETDETAILS_ARCH STREQUAL "wasm32") + target_compile_definitions(${TARGETDETAILS_TARGET} PRIVATE TARGET_WASM32) + elseif (TARGETDETAILS_ARCH STREQUAL "wasm64") + target_compile_definitions(${TARGETDETAILS_TARGET} PRIVATE TARGET_WASM64) + endif() + target_compile_definitions(${TARGETDETAILS_TARGET} PRIVATE USE_STL) + target_compile_definitions(${TARGETDETAILS_TARGET} PRIVATE PAL_STDCPP_COMPAT) + + find_package(LLVM REQUIRED CONFIG PATHS $ENV{LLVM_CMAKE_CONFIG}) + include_directories(${LLVM_INCLUDE_DIRS}) + add_definitions(${LLVM_DEFINITIONS}) + llvm_map_components_to_libnames(llvm_libs core bitwriter) + target_link_libraries(${TARGETDETAILS_TARGET} ${llvm_libs}) + endif () + endfunction() if (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64 OR (CLR_CMAKE_TARGET_ARCH_I386 AND NOT CLR_CMAKE_HOST_UNIX)) @@ -211,6 +239,7 @@ if (CLR_CMAKE_TARGET_WIN32) jitstd.h jittelemetry.h lir.h + llvm.h loopcloning.h loopcloningopts.h lower.h @@ -251,6 +280,7 @@ if (CLR_CMAKE_TARGET_WIN32) valuenumtype.h varset.h vartype.h + wasm.h ) if (CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_ARM) @@ -293,6 +323,26 @@ set( JIT_AMD64_SOURCES hwintrinsiccodegenxarch.cpp ) +# TODO this is just a copy of AMD64_SOURCES to get started - e.g. simd,hwintrinsics doesn't make sense for wasm +set( JIT_WASM64_SOURCES + simd.cpp + simdashwintrinsic.cpp + simdcodegenxarch.cpp + targetwasm.cpp + hwintrinsicxarch.cpp + hwintrinsiccodegenxarch.cpp + llvm.cpp +) +set( JIT_WASM32_SOURCES + simd.cpp + simdashwintrinsic.cpp + simdcodegenxarch.cpp + targetwasm.cpp + hwintrinsicxarch.cpp + hwintrinsiccodegenxarch.cpp + llvm.cpp +) + set( JIT_ARM_SOURCES codegenarmarch.cpp codegenarm.cpp @@ -363,6 +413,8 @@ convert_to_absolute_path(JIT_AMD64_SOURCES ${JIT_AMD64_SOURCES}) convert_to_absolute_path(JIT_ARM_SOURCES ${JIT_ARM_SOURCES}) convert_to_absolute_path(JIT_I386_SOURCES ${JIT_I386_SOURCES}) convert_to_absolute_path(JIT_ARM64_SOURCES ${JIT_ARM64_SOURCES}) +convert_to_absolute_path(JIT_WASM64_SOURCES ${JIT_WASM64_SOURCES}) +convert_to_absolute_path(JIT_WASM32_SOURCES ${JIT_WASM32_SOURCES}) set(JIT_DLL_MAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/dllmain.cpp) @@ -493,17 +545,41 @@ if (CLR_CMAKE_BUILD_SUBSET_ALLJITS AND NOT CLR_CROSS_COMPONENTS_BUILD) create_standalone_jit(TARGET clrjit_unix_arm_${ARCH_HOST_NAME} OS unix ARCH arm) create_standalone_jit(TARGET clrjit_win_arm_${ARCH_HOST_NAME} OS win ARCH arm) create_standalone_jit(TARGET clrjit_win_x86_${ARCH_HOST_NAME} OS win ARCH x86) + if (NOT CLR_CMAKE_HOST_UNIX AND BUILD_WASM_JIT) + # the LLVM clrjit needs to be the last clrjit to use create_standalone_jit as it modifies some cmake variables + # LLVM clrjit has an extra export - registerLlvmCallbacks + set(CLRJIT_EXPORTS ${CMAKE_CURRENT_LIST_DIR}/ClrJit.Llvm.exports) + set(JIT_EXPORTS_FILE ${CMAKE_CURRENT_BINARY_DIR}/ClrJit.Llvm.exports.def) + preprocess_file (${CLRJIT_EXPORTS} ${JIT_EXPORTS_FILE}) + set(JIT_DEF_FILE ${JIT_EXPORTS_FILE}) + + # exclude cpp files that are not required when not processing beyond rationalized LIR + # use REGEX as this list conatins the absolute paths + list(FILTER JIT_CORE_SOURCES EXCLUDE REGEX stacklevelsetter\.cpp) + list(FILTER JIT_CORE_SOURCES EXCLUDE REGEX codegencommon\.cpp) + list(FILTER JIT_CORE_SOURCES EXCLUDE REGEX codegenlinear\.cpp) + list(FILTER JIT_CORE_SOURCES EXCLUDE REGEX emit\.cpp) + list(FILTER JIT_CORE_SOURCES EXCLUDE REGEX gcencode\.cpp) + list(FILTER JIT_CORE_SOURCES EXCLUDE REGEX instr\.cpp) + list(FILTER JIT_CORE_SOURCES EXCLUDE REGEX lower\.cpp) + list(FILTER JIT_CORE_SOURCES EXCLUDE REGEX lsra\.cpp) + list(FILTER JIT_CORE_SOURCES EXCLUDE REGEX lsrabuild\.cpp) + list(FILTER JIT_CORE_SOURCES EXCLUDE REGEX regalloc\.cpp) + + create_standalone_jit(TARGET clrjit_browser_wasm32_${ARCH_HOST_NAME} OS browser ARCH wasm32) + # uncomment to enable 8 byte pointer size version of the wasm clrjit.dll + #create_standalone_jit(TARGET clrjit_browser_wasm64_${ARCH_HOST_NAME} OS browser ARCH wasm64) + endif (NOT CLR_CMAKE_HOST_UNIX AND BUILD_WASM_JIT) else() if (CLR_CMAKE_TARGET_UNIX) create_standalone_jit(TARGET clrjit_unix_${ARCH_TARGET_NAME}_${ARCH_HOST_NAME} OS unix ARCH ${ARCH_TARGET_NAME}) endif(CLR_CMAKE_TARGET_UNIX) endif (CLR_CMAKE_BUILD_SUBSET_ALLJITS AND NOT CLR_CROSS_COMPONENTS_BUILD) - if (CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_PGO_INSTRUMENT) # Copy PGO dependency to target dir set(PGORT_DLL "pgort140.dll") find_path(PGORT_DIR ${PGORT_DLL} REQUIRED) _install(FILES "${PGORT_DIR}/${PGORT_DLL}" DESTINATION ${CMAKE_INSTALL_PREFIX}) _install(FILES "${PGORT_DIR}/${PGORT_DLL}" DESTINATION ${CMAKE_INSTALL_PREFIX}/sharedFramework) -endif () \ No newline at end of file +endif () diff --git a/src/coreclr/jit/ClrJit.Llvm.exports b/src/coreclr/jit/ClrJit.Llvm.exports new file mode 100644 index 000000000000..3adee7c3d36a --- /dev/null +++ b/src/coreclr/jit/ClrJit.Llvm.exports @@ -0,0 +1,9 @@ +; Licensed to the .NET Foundation under one or more agreements. +; The .NET Foundation licenses this file to you under the MIT license. + +EXPORTS + getJit + jitStartup + jitShutdown + registerLlvmCallbacks + diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 1770947d6402..8f076423a957 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -6,7 +6,7 @@ // of a method, except for the target-specific elements, which are // primarily in the Target class. // - +#ifndef TARGET_WASM #ifndef _CODEGEN_H_ #define _CODEGEN_H_ #include "codegeninterface.h" @@ -1608,3 +1608,4 @@ inline void DoPhase(CodeGen* _codeGen, Phases _phase, void (CodeGen::*_action)() } #endif // _CODEGEN_H_ +#endif // TARGET_WASM diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 66ad2f340951..418ccaa9385c 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -31,30 +31,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX /*****************************************************************************/ -const BYTE genTypeSizes[] = { -#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, tf, howUsed) sz, -#include "typelist.h" -#undef DEF_TP -}; - -const BYTE genTypeAlignments[] = { -#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, tf, howUsed) al, -#include "typelist.h" -#undef DEF_TP -}; - -const BYTE genTypeStSzs[] = { -#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, tf, howUsed) st, -#include "typelist.h" -#undef DEF_TP -}; - -const BYTE genActualTypes[] = { -#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, tf, howUsed) jitType, -#include "typelist.h" -#undef DEF_TP -}; - void CodeGenInterface::setFramePointerRequiredEH(bool value) { m_cgFramePointerRequired = value; diff --git a/src/coreclr/jit/codegeninterface.h b/src/coreclr/jit/codegeninterface.h index 6a590352753f..cae33f2136bd 100644 --- a/src/coreclr/jit/codegeninterface.h +++ b/src/coreclr/jit/codegeninterface.h @@ -17,6 +17,7 @@ // accessed from members of Compiler. // +#ifndef TARGET_WASM #ifndef _CODEGEN_INTERFACE_H_ #define _CODEGEN_INTERFACE_H_ @@ -775,3 +776,4 @@ class CodeGenInterface }; #endif // _CODEGEN_INTERFACE_H_ +#endif // TARGET_WASM diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 17b86c8c74e6..b281d22fb9d7 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -24,6 +24,13 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "patchpointinfo.h" #include "jitstd/algorithm.h" +#undef min +#undef max + +#if defined(TARGET_WASM) +#include "llvm.h" +#endif + #if defined(DEBUG) // Column settings for COMPlus_JitDumpIR. We could(should) make these programmable. #define COLUMN_OPCODE 30 @@ -118,6 +125,30 @@ inline unsigned getCurTime() return (((tim.wHour * 60) + tim.wMinute) * 60 + tim.wSecond) * 1000 + tim.wMilliseconds; } +const BYTE genTypeSizes[] = { +#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, tf, howUsed) sz, +#include "typelist.h" +#undef DEF_TP +}; + +const BYTE genTypeAlignments[] = { +#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, tf, howUsed) al, +#include "typelist.h" +#undef DEF_TP +}; + +const BYTE genTypeStSzs[] = { +#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, tf, howUsed) st, +#include "typelist.h" +#undef DEF_TP +}; + +const BYTE genActualTypes[] = { +#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, tf, howUsed) jitType, +#include "typelist.h" +#undef DEF_TP +}; + /*****************************************************************************/ #ifdef DEBUG /*****************************************************************************/ @@ -204,7 +235,7 @@ void Compiler::compDspSrcLinesByLineNum(unsigned line, bool seek) } /*****************************************************************************/ - +#ifndef TARGET_WASM void Compiler::compDspSrcLinesByNativeIP(UNATIVE_OFFSET curIP) { static IPmappingDsc* nextMappingDsc; @@ -262,6 +293,7 @@ void Compiler::compDspSrcLinesByNativeIP(UNATIVE_OFFSET curIP) } } } +#endif // TARGET_WASM /*****************************************************************************/ #endif // DEBUG @@ -830,7 +862,7 @@ var_types Compiler::getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, // Arm64 Windows VarArg methods arguments will not classify HFA/HVA types, they will need to be treated // as if they are not HFA/HVA types. var_types hfaType; -#if defined(TARGET_WINDOWS) && defined(TARGET_ARM64) +#if defined(TARGET_WINDOWS) && defined(TARGET_ARM64) || defined(TARGET_WASM) if (isVarArg) { hfaType = TYP_UNDEF; @@ -923,7 +955,7 @@ var_types Compiler::getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, howToPassStruct = SPK_ByValue; useType = TYP_STRUCT; -#elif defined(TARGET_AMD64) || defined(TARGET_ARM64) +#elif defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_WASM) // TODO: WASM can in theory pass any size struct as an arg. // Otherwise we pass this struct by reference to a copy // setup wbPassType and useType indicate that this is passed using one register (by reference to a copy) @@ -948,6 +980,61 @@ var_types Compiler::getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, return useType; } +#ifdef TARGET_WASM +bool Compiler::IsHfa(CORINFO_CLASS_HANDLE hClass) +{ + return false; // TODO WASM +} +var_types Compiler::GetHfaType(GenTree* tree) +{ + return TYP_UNDEF; // TODO WASM +} +var_types Compiler::GetHfaType(CORINFO_CLASS_HANDLE hClass) +{ + return TYP_UNDEF; +} +//------------------------------------------------------------------------ +// GetHfaCount: Given a class handle for an HFA struct +// return the number of registers needed to hold the HFA +// +// Note that on ARM32 the single precision registers overlap with +// the double precision registers and for that reason each +// double register is considered to be two single registers. +// Thus for ARM32 an HFA of 4 doubles this function will return 8. +// On ARM64 given an HFA of 4 singles or 4 doubles this function will +// will return 4 for both. +// Arguments: +// hClass: the class handle of a HFA struct +// +unsigned Compiler::GetHfaCount(CORINFO_CLASS_HANDLE hClass) +{ + assert(false); // TODO + //assert(IsHfa(hClass)); + //var_types hfaType = GetHfaType(hClass); + //unsigned classSize = info.compCompHnd->getClassSize(hClass); + //// Note that the retail build issues a warning about a potential divsion by zero without the Max function + //unsigned elemSize = Max((unsigned)1, (unsigned)EA_SIZE_IN_BYTES(emitActualTypeSize(hfaType))); + //return classSize / elemSize; + return 1; +} + +IL_OFFSET jitGetILoffs(IL_OFFSETX offsx) +{ + assert(offsx != BAD_IL_OFFSET); + + switch ((int)offsx) // Need the cast since offs is unsigned and the case statements are comparing to signed. + { + case ICorDebugInfo::NO_MAPPING: + case ICorDebugInfo::PROLOG: + case ICorDebugInfo::EPILOG: + unreached(); + + default: + return IL_OFFSET(offsx & ~IL_OFFSETX_BITS); + } +} +#endif //TARGET_WASM + //----------------------------------------------------------------------------- // getReturnTypeForStruct: // Get the type that is used to return values of the given struct type. @@ -1445,8 +1532,11 @@ void Compiler::compStartup() #endif /* Initialize the emitter */ - +#ifdef TARGET_WASM + Llvm::Init(); +#else emitter::emitInit(); +#endif // !TARGET_WASM // Static vars of ValueNumStore ValueNumStore::InitValueNumStoreStatics(); @@ -1480,9 +1570,13 @@ void Compiler::compShutdown() DisplayNowayAssertMap(); #endif // MEASURE_NOWAY +#ifdef TARGET_WASM + Llvm::llvmShutdown(); +#else /* Shut down the emitter */ emitter::emitDone(); +#endif // !TARGET_WASM #if defined(DEBUG) || defined(INLINE_DATA) // Finish reading and/or writing inline xml @@ -1928,7 +2022,9 @@ void Compiler::compInit(ArenaAllocator* pAlloc, if (!compIsForInlining()) { +#ifndef TARGET_WASM codeGen = getCodeGenerator(this); +#endif // !TARGET_WASM optInit(); hashBv::Init(this); @@ -2686,7 +2782,9 @@ void Compiler::compInitOptions(JitFlags* jitFlags) else { verbose = false; +#ifndef TARGET_WASM codeGen->setVerbose(false); +#endif // !TARGET_WASM } verboseTrees = verbose && shouldUseVerboseTrees(); verboseSsa = verbose && shouldUseVerboseSsa(); @@ -3123,7 +3221,9 @@ void Compiler::compInitOptions(JitFlags* jitFlags) verbose = true; verboseTrees = shouldUseVerboseTrees(); verboseSsa = shouldUseVerboseSsa(); +#ifndef TARGET_WASM codeGen->setVerbose(true); +#endif // !TARGET_WASM } treesBeforeAfterMorph = (JitConfig.TreesBeforeAfterMorph() == 1); @@ -3183,7 +3283,9 @@ void Compiler::compInitOptions(JitFlags* jitFlags) //------------------------------------------------------------------------- #ifdef DEBUG +#ifndef TARGET_WASM assert(!codeGen->isGCTypeFixed()); +#endif // !TARGET_WASM opts.compGcChecks = (JitConfig.JitGCChecks() != 0) || compStressCompile(STRESS_GENERIC_VARN, 5); #endif @@ -3946,6 +4048,7 @@ void Compiler::compSetOptimizationLevel() opts.compFlags |= CLFLG_MINOPT; } +#ifndef TARGET_WASM if (!compIsForInlining()) { codeGen->setFramePointerRequired(false); @@ -3978,6 +4081,7 @@ void Compiler::compSetOptimizationLevel() codeGen->SetAlignLoops(JitConfig.JitAlignLoops() == 1); } } +#endif // !TARGET_WASM #if TARGET_ARM // A single JitStress=1 Linux ARM32 test fails when we expand virtual calls early @@ -4360,6 +4464,15 @@ void Compiler::EndPhase(Phases phase) mostRecentlyActivePhase = phase; } +#if defined(TARGET_WASM) +inline void DoLlvmPhase(Compiler* pCompiler) +{ + Llvm* llvm = new Llvm(); + llvm->Compile(pCompiler); + delete llvm; +} +#endif + //------------------------------------------------------------------------ // compCompile: run phases needed for compilation // @@ -4510,6 +4623,7 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl // Note that requiring a EBP Frame disallows double alignment. Thus if we change this // we either have to disallow double alignment for E&C some other way or handle it in EETwain. +#ifndef TARGET_WASM if (opts.compDbgEnC) { codeGen->setFramePointerRequired(true); @@ -4520,6 +4634,7 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl // // compLocallocUsed = true; } +#endif // !TARGET_WASM // Start phases that are broadly called morphing, and includes // global morph, as well as other phases that massage the trees so @@ -4778,8 +4893,10 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl } #endif // defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM) +#ifndef TARGET_WASM // Decide the kind of code we want to generate fgSetOptions(); +#endif // !TARGET_WASM fgExpandQmarkNodes(); @@ -5056,6 +5173,10 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl Rationalizer rat(this); // PHASE_RATIONALIZE rat.Run(); +#if defined(TARGET_WASM) + DoLlvmPhase(this); +#else + // Here we do "simple lowering". When the RyuJIT backend works for all // platforms, this will be part of the more general lowering phase. For now, though, we do a separate // pass of "final lowering." We must do this before (final) liveness analysis, because this creates @@ -5168,8 +5289,11 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl fprintf(compJitFuncInfoFile, ""); // in our logic this causes a flush } #endif // FUNC_INFO_LOGGING +#endif // TARGET_WASM + } +#ifndef TARGET_WASM //------------------------------------------------------------------------ // generatePatchpointInfo: allocate and fill in patchpoint info data, // and report it to the VM @@ -5249,6 +5373,7 @@ void Compiler::generatePatchpointInfo() // Register this with the runtime. info.compCompHnd->setPatchpointInfo(patchpointInfo); } +#endif // !TARGET_WASM //------------------------------------------------------------------------ // ResetOptAnnotations: Clear annotations produced during global optimizations. @@ -5525,6 +5650,8 @@ int Compiler::compCompile(CORINFO_MODULE_HANDLE classPtr, CORINFO_EE_INFO* eeInfo = eeGetEEInfo(); #ifdef TARGET_UNIX info.compMatchedVM = info.compMatchedVM && (eeInfo->osType == CORINFO_UNIX); +#elif TARGET_WASM + // TODO: do we need a CORINFO_WASM (or CORINFO_LLVM/CORINFO_BROWSER even though wasm can run outside the browser) #else info.compMatchedVM = info.compMatchedVM && (eeInfo->osType == CORINFO_WINNT); #endif @@ -5661,16 +5788,18 @@ int Compiler::compCompile(CORINFO_MODULE_HANDLE classPtr, goto DoneCleanUp; } +#ifndef TARGET_WASM /* Tell the emitter that we're done with this function */ GetEmitter()->emitEndCG(); +#endif // !TARGET_WASM DoneCleanUp: compDone(); } endErrorTrap() // ERROR TRAP: End - return param.result; + return param.result; } #if defined(DEBUG) || defined(INLINE_DATA) @@ -6177,12 +6306,14 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr, compBasicBlockID = 0; #endif +#ifndef TARGET_WASM /* Initialize emitter */ if (!compIsForInlining()) { codeGen->GetEmitter()->emitBegCG(this, compHnd); } +#endif // !TARGET_WASM info.compIsStatic = (info.compFlags & CORINFO_FLG_STATIC) != 0; @@ -6545,7 +6676,7 @@ void Compiler::compInitVarScopeMap() compVarScopeMap = new (getAllocator()) VarNumToScopeDscMap(getAllocator()); // 599 prime to limit huge allocations; for ex: duplicated scopes on single var. - compVarScopeMap->Reallocate(min(info.compVarScopesCount, 599)); + compVarScopeMap->Reallocate(std::min(info.compVarScopesCount, 599U)); for (unsigned i = 0; i < info.compVarScopesCount; ++i) { @@ -7670,16 +7801,16 @@ void CompTimeSummaryInfo::AddInfo(CompTimeInfo& info, bool includePhases) // Update the totals and maxima. m_total.m_byteCodeBytes += info.m_byteCodeBytes; - m_maximum.m_byteCodeBytes = max(m_maximum.m_byteCodeBytes, info.m_byteCodeBytes); + m_maximum.m_byteCodeBytes = std::max(m_maximum.m_byteCodeBytes, info.m_byteCodeBytes); m_total.m_totalCycles += info.m_totalCycles; - m_maximum.m_totalCycles = max(m_maximum.m_totalCycles, info.m_totalCycles); + m_maximum.m_totalCycles = std::max(m_maximum.m_totalCycles, info.m_totalCycles); #if MEASURE_CLRAPI_CALLS // Update the CLR-API values. m_total.m_allClrAPIcalls += info.m_allClrAPIcalls; - m_maximum.m_allClrAPIcalls = max(m_maximum.m_allClrAPIcalls, info.m_allClrAPIcalls); + m_maximum.m_allClrAPIcalls = std::max(m_maximum.m_allClrAPIcalls, info.m_allClrAPIcalls); m_total.m_allClrAPIcycles += info.m_allClrAPIcycles; - m_maximum.m_allClrAPIcycles = max(m_maximum.m_allClrAPIcycles, info.m_allClrAPIcycles); + m_maximum.m_allClrAPIcycles = std::max(m_maximum.m_allClrAPIcycles, info.m_allClrAPIcycles); #endif if (includeInFiltered) @@ -7709,14 +7840,14 @@ void CompTimeSummaryInfo::AddInfo(CompTimeInfo& info, bool includePhases) m_filtered.m_CLRcyclesByPhase[i] += info.m_CLRcyclesByPhase[i]; #endif } - m_maximum.m_cyclesByPhase[i] = max(m_maximum.m_cyclesByPhase[i], info.m_cyclesByPhase[i]); + m_maximum.m_cyclesByPhase[i] = std::max(m_maximum.m_cyclesByPhase[i], info.m_cyclesByPhase[i]); #if MEASURE_CLRAPI_CALLS m_maximum.m_CLRcyclesByPhase[i] = max(m_maximum.m_CLRcyclesByPhase[i], info.m_CLRcyclesByPhase[i]); #endif } m_total.m_parentPhaseEndSlop += info.m_parentPhaseEndSlop; - m_maximum.m_parentPhaseEndSlop = max(m_maximum.m_parentPhaseEndSlop, info.m_parentPhaseEndSlop); + m_maximum.m_parentPhaseEndSlop = std::max(m_maximum.m_parentPhaseEndSlop, info.m_parentPhaseEndSlop); } #if MEASURE_CLRAPI_CALLS else @@ -8542,6 +8673,7 @@ void cEH(Compiler* comp) comp->fgDispHandlerTab(); } +#ifndef TARGET_WASM void cVar(Compiler* comp, unsigned lclNum) { static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called @@ -8570,6 +8702,7 @@ void cVarsFinal(Compiler* comp) printf("===================================================================== *Vars %u\n", sequenceNumber++); comp->lvaTableDump(Compiler::FINAL_FRAME_LAYOUT); } +#endif // !TARGET_WASM void cBlockCheapPreds(Compiler* comp, BasicBlock* block) { @@ -8678,6 +8811,7 @@ void dEH() cEH(JitTls::GetCompiler()); } +#ifndef TARGET_WASM void dVar(unsigned lclNum) { cVar(JitTls::GetCompiler(), lclNum); @@ -8697,6 +8831,7 @@ void dVarsFinal() { cVarsFinal(JitTls::GetCompiler()); } +#endif // !TARGET_WASM void dBlockPreds(BasicBlock* block) { diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index b69c0c1a27e0..16cbfb93a643 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -1817,7 +1817,7 @@ struct fgArgTabEntry unsigned roundedByteSize = roundUp(byteSize, TARGET_POINTER_SIZE); #endif // OSX_ARM64_ABI -#if !defined(TARGET_ARM) +#if !defined(TARGET_ARM) && !defined(TARGET_WASM32) // Arm32 could have a struct with 8 byte alignment // which rounded size % 8 is not 0. assert(m_byteAlignment != 0); @@ -2570,7 +2570,9 @@ class Compiler } void* ehEmitCookie(BasicBlock* block); +#ifndef TARGET_WASM UNATIVE_OFFSET ehCodeOffset(BasicBlock* block); +#endif // !TARGET_WASM EHblkDsc* ehInitHndRange(BasicBlock* src, IL_OFFSET* hndBeg, IL_OFFSET* hndEnd, bool* inFilter); @@ -3422,8 +3424,10 @@ class Compiler void lvaDumpRegLocation(unsigned lclNum); void lvaDumpFrameLocation(unsigned lclNum); void lvaDumpEntry(unsigned lclNum, FrameLayoutState curState, size_t refCntWtdWidth = 6); +#ifndef TARGET_WASM void lvaTableDump(FrameLayoutState curState = NO_FRAME_LAYOUT); // NO_FRAME_LAYOUT means use the current frame // layout state defined by lvaDoneFrameLayout +#endif //!TARGET_WASM #endif // Limit frames size to 1GB. The maximum is 2GB in theory - make it intentionally smaller @@ -4739,7 +4743,9 @@ class Compiler bool fgMorphBlockStmt(BasicBlock* block, Statement* stmt DEBUGARG(const char* msg)); +#ifndef TARGET_WASM void fgSetOptions(); +#endif // !TARGET_WASM #ifdef DEBUG static fgWalkPreFn fgAssertNoQmark; @@ -5775,7 +5781,9 @@ class Compiler bool fgCheckStmtAfterTailCall(); GenTree* fgMorphTailCallViaHelpers(GenTreeCall* call, CORINFO_TAILCALL_HELPERS& help); bool fgCanTailCallViaJitHelper(); +#ifdef TARGET_X86 void fgMorphTailCallViaJitHelper(GenTreeCall* call); +#endif GenTree* fgCreateCallDispatcherAndGetResult(GenTreeCall* origCall, CORINFO_METHOD_HANDLE callTargetStubHnd, CORINFO_METHOD_HANDLE dispatcherHnd); @@ -7348,8 +7356,9 @@ class Compiler */ public: +#ifndef TARGET_WASM regNumber raUpdateRegStateForArg(RegState* regState, LclVarDsc* argDsc); - +#endif void raMarkStkVars(); protected: @@ -7553,6 +7562,17 @@ class Compiler #elif defined(TARGET_ARM64) reg = REG_R11; regMask = RBM_R11; +#elif defined(TARGET_WASM) //TODO: empty better? + if (isCoreRTABI) + { + reg = REG_R10; + regMask = RBM_R10; + } + else + { + reg = REG_R11; + regMask = RBM_R11; + } #else #error Unsupported or unset target architecture #endif @@ -7617,6 +7637,7 @@ class Compiler unsigned eeVarsCount; +#ifndef TARGET_WASM struct VarResultInfo { UNATIVE_OFFSET startOffset; @@ -7636,6 +7657,7 @@ class Compiler void eeDispVar(ICorDebugInfo::NativeVarInfo* var); void eeDispVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::NativeVarInfo* vars); #endif // DEBUG +#endif // ICorJitInfo wrappers @@ -7717,7 +7739,7 @@ class Compiler CodeGenInterface* codeGen; // The following holds information about instr offsets in terms of generated code. - +#ifndef TARGET_WASM struct IPmappingDsc { IPmappingDsc* ipmdNext; // next line# record @@ -7725,11 +7747,11 @@ class Compiler IL_OFFSETX ipmdILoffsx; // the instr offset bool ipmdIsLabel; // Can this code be a branch label? }; - // Record the instr offset mapping to the generated code IPmappingDsc* genIPmappingList; IPmappingDsc* genIPmappingLast; +#endif // Managed RetVal - A side hash table meant to record the mapping from a // GT_CALL node to its IL offset. This info is used to emit sequence points @@ -7752,6 +7774,7 @@ class Compiler // convenience and backward compatibility, but the properties can only be set by invoking // the setter on CodeGenContext directly. +#ifndef TARGET_WASM emitter* GetEmitter() const { return codeGen->GetEmitter(); @@ -7761,14 +7784,21 @@ class Compiler { return codeGen->isFramePointerUsed(); } - +#endif bool GetInterruptible() { +#ifdef TARGET_WASM + return false; +#else return codeGen->GetInterruptible(); +#endif } void SetInterruptible(bool value) { +#ifndef TARGET_WASM codeGen->SetInterruptible(value); +#else +#endif // !TARGET_WASM } #ifdef TARGET_ARMARCH @@ -7798,11 +7828,18 @@ class Compiler bool IsFullPtrRegMapRequired() { +#ifndef TARGET_WASM return codeGen->IsFullPtrRegMapRequired(); +#else + return false; // For GCInfo TODO: sensible default? +#endif // TARGET_WASM } void SetFullPtrRegMapRequired(bool value) { +#ifndef TARGET_WASM codeGen->SetFullPtrRegMapRequired(value); +#else +#endif // TARGET_WASM } // Things that MAY belong either in CodeGen or CodeGenContext @@ -8000,7 +8037,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX UNATIVE_OFFSET unwindGetCurrentOffset(FuncInfoDsc* func); -#if defined(TARGET_AMD64) +#if defined(TARGET_AMD64) || defined(TARGET_WASM) // TODO: delete? void unwindBegPrologWindows(); void unwindPushWindows(regNumber reg); @@ -8070,6 +8107,9 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // min bar is SSE2 return SIMD_SSE2_Supported; +#elif defined(TARGET_WASM) + assert(!"WASM supports SIMD so what to do here?"); + return SIMD_Not_Supported; #else assert(!"Available instruction set(s) for SIMD codegen is not defined for target arch"); unreached(); @@ -9651,7 +9691,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // In case of Amd64 this doesn't include float regs saved on stack. unsigned compCalleeRegsPushed; -#if defined(TARGET_XARCH) +#if defined(TARGET_XARCH) || defined(TARGET_WASM) // TODO Wasm // Mask of callee saved float regs on stack. regMaskTP compCalleeFPRegsSavedMask; #endif diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index d7576d6b4d60..c7f4f97dfb89 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -2034,6 +2034,7 @@ inline int Compiler::lvaCachedGenericContextArgOffset() return lvaCachedGenericContextArgOffs; } +#ifndef TARGET_WASM //------------------------------------------------------------------------ // lvaFrameAddress: Determine the stack frame offset of the given variable, // and how to generate an address to that stack frame. @@ -2244,6 +2245,7 @@ inline return varOffset; } +#endif inline bool Compiler::lvaIsParameter(unsigned varNum) { diff --git a/src/coreclr/jit/ee_il_dll.cpp b/src/coreclr/jit/ee_il_dll.cpp index 1062fd3c0e17..3cd02b1f0068 100644 --- a/src/coreclr/jit/ee_il_dll.cpp +++ b/src/coreclr/jit/ee_il_dll.cpp @@ -123,6 +123,9 @@ extern "C" DLLEXPORT void jitStartup(ICorJitHost* jitHost) g_jitInitialized = true; } +#if TARGET_WASM +extern "C" DLLEXPORT +#endif void jitShutdown(bool processIsTerminating) { if (!g_jitInitialized) @@ -574,6 +577,7 @@ void Compiler::eeGetStmtOffsets() info.compCompHnd->freeArray(offsets); } +#ifndef TARGET_WASM /***************************************************************************** * * Debugging support - Local var info @@ -635,6 +639,7 @@ void Compiler::eeSetLVdone() eeVars = nullptr; // We give up ownership after setVars() } +#endif // !TARGET_WASM void Compiler::eeGetVars() { @@ -763,6 +768,7 @@ void Compiler::eeGetVars() } #ifdef DEBUG +#ifndef TARGET_WASM void Compiler::eeDispVar(ICorDebugInfo::NativeVarInfo* var) { const char* name = nullptr; @@ -878,6 +884,7 @@ void Compiler::eeDispVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInf eeDispVar(&vars[i]); } } +#endif // !TARGET_WASM #endif // DEBUG /***************************************************************************** @@ -1111,6 +1118,7 @@ WORD Compiler::eeGetRelocTypeHint(void* target) } } +#ifndef TARGET_WASM CORINFO_FIELD_HANDLE Compiler::eeFindJitDataOffs(unsigned dataOffs) { // Data offsets are marked by the fact that the low two bits are 0b01 0x1 @@ -1148,6 +1156,7 @@ int Compiler::eeGetJitDataOffs(CORINFO_FIELD_HANDLE field) return -1; } } +#endif // !TARGET_WASM /***************************************************************************** * diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 50311c555c68..e072bfdbfd35 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. /*****************************************************************************/ - +#ifndef TARGET_WASM #ifndef _EMIT_H_ #define _EMIT_H_ @@ -2888,3 +2888,4 @@ inline void emitter::emitEnableGC() /*****************************************************************************/ #endif // _EMIT_H_ /*****************************************************************************/ +#endif // TARGET_WASM diff --git a/src/coreclr/jit/emitfmts.h b/src/coreclr/jit/emitfmts.h index c252c0b1237d..f4a8af429ca7 100644 --- a/src/coreclr/jit/emitfmts.h +++ b/src/coreclr/jit/emitfmts.h @@ -8,6 +8,7 @@ #include "emitfmtsarm.h" #elif defined(TARGET_ARM64) #include "emitfmtsarm64.h" +#elif defined(TARGET_WASM) // this file included in CMakeList.txt unconditionally #else #error Unsupported or unset target architecture #endif // target type diff --git a/src/coreclr/jit/emitjmps.h b/src/coreclr/jit/emitjmps.h index 4ed340302119..3d0f35ef1df7 100644 --- a/src/coreclr/jit/emitjmps.h +++ b/src/coreclr/jit/emitjmps.h @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // clang-format off +#ifndef TARGET_WASM #ifndef JMP_SMALL #error Must define JMP_SMALL macro before including this file #endif @@ -53,5 +54,6 @@ JMP_SMALL(le , gt , ble ) // LE /*****************************************************************************/ #undef JMP_SMALL /*****************************************************************************/ +#endif // TARGET_WASM // clang-format on diff --git a/src/coreclr/jit/error.h b/src/coreclr/jit/error.h index a63643c0ee5b..7856b4a974b3 100644 --- a/src/coreclr/jit/error.h +++ b/src/coreclr/jit/error.h @@ -174,6 +174,8 @@ extern void notYetImplemented(const char* msg, const char* file, unsigned line); #define NYI_X86(msg) do { } while (0) #define NYI_ARM(msg) do { } while (0) #define NYI_ARM64(msg) do { } while (0) +#define NYI_WASM32(msg) do { } while (0) +#define NYI_WASM64(msg) do { } while (0) #elif defined(TARGET_X86) @@ -181,6 +183,8 @@ extern void notYetImplemented(const char* msg, const char* file, unsigned line); #define NYI_X86(msg) NYIRAW("NYI_X86: " msg) #define NYI_ARM(msg) do { } while (0) #define NYI_ARM64(msg) do { } while (0) +#define NYI_WASM32(msg) do { } while (0) +#define NYI_WASM64(msg) do { } while (0) #elif defined(TARGET_ARM) @@ -188,6 +192,8 @@ extern void notYetImplemented(const char* msg, const char* file, unsigned line); #define NYI_X86(msg) do { } while (0) #define NYI_ARM(msg) NYIRAW("NYI_ARM: " msg) #define NYI_ARM64(msg) do { } while (0) +#define NYI_WASM32(msg) do { } while (0) +#define NYI_WASM64(msg) do { } while (0) #elif defined(TARGET_ARM64) @@ -195,6 +201,26 @@ extern void notYetImplemented(const char* msg, const char* file, unsigned line); #define NYI_X86(msg) do { } while (0) #define NYI_ARM(msg) do { } while (0) #define NYI_ARM64(msg) NYIRAW("NYI_ARM64: " msg) +#define NYI_WASM32(msg) do { } while (0) +#define NYI_WASM64(msg) do { } while (0) + +#elif defined(TARGET_WASM64) + +#define NYI_AMD64(msg) do { } while (0) +#define NYI_X86(msg) do { } while (0) +#define NYI_ARM(msg) do { } while (0) +#define NYI_ARM64(msg) do { } while (0) +#define NYI_WASM32(msg) do { } while (0) +#define NYI_WASM64(msg) NYIRAW("NYI_WASM64: " msg) + +#elif defined(TARGET_WASM32) + +#define NYI_AMD64(msg) do { } while (0) +#define NYI_X86(msg) do { } while (0) +#define NYI_ARM(msg) do { } while (0) +#define NYI_ARM64(msg) do { } while (0) +#define NYI_WASM32(msg) NYIRAW("NYI_WASM32: " msg) +#define NYI_WASM64(msg) do { } while (0) #else diff --git a/src/coreclr/jit/fgopt.cpp b/src/coreclr/jit/fgopt.cpp index 5fb125525229..37af697d5a42 100644 --- a/src/coreclr/jit/fgopt.cpp +++ b/src/coreclr/jit/fgopt.cpp @@ -2478,7 +2478,9 @@ bool Compiler::fgOptimizeEmptyBlock(BasicBlock* block) { LIR::AsRange(block).InsertAtEnd(nop); LIR::ReadOnlyRange range(nop, nop); +#ifndef TARGET_WASM m_pLowering->LowerRange(block, range); +#endif // TARGET_WASM } else { @@ -2821,7 +2823,9 @@ bool Compiler::fgOptimizeSwitchBranches(BasicBlock* block) { blockRange->InsertAfter(switchVal, zeroConstNode, condNode); LIR::ReadOnlyRange range(zeroConstNode, switchTree); +#ifndef TARGET_WASM m_pLowering->LowerRange(block, range); +#endif // TARGET_WASM } else if (fgStmtListThreaded) { diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index d6a9ed7642b1..d6580da1b2c3 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -1519,6 +1519,7 @@ inline void Compiler::fgMarkLoopHead(BasicBlock* block) } } +#ifndef TARGET_WASM /* * We have to make this method fully interruptible since we can not * ensure that this loop will execute a call every time it loops. @@ -1527,6 +1528,7 @@ inline void Compiler::fgMarkLoopHead(BasicBlock* block) */ assert(!codeGen->isGCTypeFixed()); +#endif // !TARGET_WASM if (!compCanEncodePtrArgCntMax()) { diff --git a/src/coreclr/jit/gcinfo.cpp b/src/coreclr/jit/gcinfo.cpp index b75ddb1d86fb..13ed5a615ba0 100644 --- a/src/coreclr/jit/gcinfo.cpp +++ b/src/coreclr/jit/gcinfo.cpp @@ -76,6 +76,7 @@ void GCInfo::gcResetForBB() VarSetOps::AssignNoCopy(compiler, gcVarPtrSetCur, VarSetOps::MakeEmpty(compiler)); } +#ifndef TARGET_WASM #ifdef DEBUG /***************************************************************************** @@ -220,6 +221,7 @@ void GCInfo::gcMarkRegPtrVal(regNumber reg, var_types type) break; } } +#endif // !TARGET_WASM /*****************************************************************************/ diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index c359bef46712..3a3a075de6cd 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -2855,6 +2855,466 @@ bool Compiler::gtCanSwapOrder(GenTree* firstNode, GenTree* secondNode) return canSwap; } +#ifdef TARGET_WASM +bool genCreateAddrMode(Compiler* compiler, GenTree* addr, + bool fold, + bool* revPtr, + GenTree** rv1Ptr, + GenTree** rv2Ptr, +#if SCALED_ADDR_MODES + unsigned* mulPtr, +#endif // SCALED_ADDR_MODES + ssize_t* cnsPtr) +{ + /* + The following indirections are valid address modes on x86/x64: + + [ icon] * not handled here + [reg ] + [reg + icon] + [reg1 + reg2 ] + [reg1 + reg2 + icon] + [reg1 + 2 * reg2 ] + [reg1 + 4 * reg2 ] + [reg1 + 8 * reg2 ] + [ 2 * reg2 + icon] + [ 4 * reg2 + icon] + [ 8 * reg2 + icon] + [reg1 + 2 * reg2 + icon] + [reg1 + 4 * reg2 + icon] + [reg1 + 8 * reg2 + icon] + + The following indirections are valid address modes on arm64: + + [reg] + [reg + icon] + [reg1 + reg2] + [reg1 + reg2 * natural-scale] + + */ + + /* All indirect address modes require the address to be an addition */ + + if (addr->gtOper != GT_ADD) + { + return false; + } + + // Can't use indirect addressing mode as we need to check for overflow. + // Also, can't use 'lea' as it doesn't set the flags. + + if (addr->gtOverflow()) + { + return false; + } + + GenTree* rv1 = nullptr; + GenTree* rv2 = nullptr; + + GenTree* op1; + GenTree* op2; + + ssize_t cns; +#if SCALED_ADDR_MODES + unsigned mul; +#endif // SCALED_ADDR_MODES + + GenTree* tmp; + + /* What order are the sub-operands to be evaluated */ + + if (addr->gtFlags & GTF_REVERSE_OPS) + { + op1 = addr->AsOp()->gtOp2; + op2 = addr->AsOp()->gtOp1; + } + else + { + op1 = addr->AsOp()->gtOp1; + op2 = addr->AsOp()->gtOp2; + } + + bool rev = false; // Is op2 first in the evaluation order? + + /* + A complex address mode can combine the following operands: + + op1 ... base address + op2 ... optional scaled index +#if SCALED_ADDR_MODES + mul ... optional multiplier (2/4/8) for op2 +#endif + cns ... optional displacement + + Here we try to find such a set of operands and arrange for these + to sit in registers. + */ + + cns = 0; +#if SCALED_ADDR_MODES + mul = 0; +#endif // SCALED_ADDR_MODES + +AGAIN: + /* We come back to 'AGAIN' if we have an add of a constant, and we are folding that + constant, or we have gone through a GT_NOP or GT_COMMA node. We never come back + here if we find a scaled index. + */ + CLANG_FORMAT_COMMENT_ANCHOR; + +#if SCALED_ADDR_MODES + assert(mul == 0); +#endif // SCALED_ADDR_MODES + + /* Special case: keep constants as 'op2' */ + + if (op1->IsCnsIntOrI()) + { + // Presumably op2 is assumed to not be a constant (shouldn't happen if we've done constant folding)? + tmp = op1; + op1 = op2; + op2 = tmp; + } + + /* Check for an addition of a constant */ + + if (op2->IsIntCnsFitsInI32() && (op2->gtType != TYP_REF) && FitsIn(cns + op2->AsIntConCommon()->IconValue())) + { + // We should not be building address modes out of non-foldable constants + assert(op2->AsIntConCommon()->ImmedValCanBeFolded(compiler, addr->OperGet())); + + /* We're adding a constant */ + + cns += op2->AsIntConCommon()->IconValue(); + +#if defined(TARGET_ARMARCH) + if (cns == 0) +#endif + { + /* Inspect the operand the constant is being added to */ + + switch (op1->gtOper) + { + case GT_ADD: + + if (op1->gtOverflow()) + { + break; + } + + op2 = op1->AsOp()->gtOp2; + op1 = op1->AsOp()->gtOp1; + + goto AGAIN; + +#if SCALED_ADDR_MODES && !defined(TARGET_ARMARCH) + // TODO-ARM64-CQ, TODO-ARM-CQ: For now we don't try to create a scaled index. + case GT_MUL: + if (op1->gtOverflow()) + { + return false; // Need overflow check + } + + FALLTHROUGH; + + case GT_LSH: + + mul = op1->GetScaledIndex(); + if (mul) + { + /* We can use "[mul*rv2 + icon]" */ + + rv1 = nullptr; + rv2 = op1->AsOp()->gtOp1; + + goto FOUND_AM; + } + break; +#endif // SCALED_ADDR_MODES && !defined(TARGET_ARMARCH) + + default: + break; + } + } + + /* The best we can do is "[rv1 + icon]" */ + + rv1 = op1; + rv2 = nullptr; + + goto FOUND_AM; + } + + // op2 is not a constant. So keep on trying. + + /* Neither op1 nor op2 are sitting in a register right now */ + + switch (op1->gtOper) + { +#if !defined(TARGET_ARMARCH) + // TODO-ARM64-CQ, TODO-ARM-CQ: For now we don't try to create a scaled index. + case GT_ADD: + + if (op1->gtOverflow()) + { + break; + } + + if (op1->AsOp()->gtOp2->IsIntCnsFitsInI32() && + FitsIn(cns + op1->AsOp()->gtOp2->AsIntCon()->gtIconVal)) + { + cns += op1->AsOp()->gtOp2->AsIntCon()->gtIconVal; + op1 = op1->AsOp()->gtOp1; + + goto AGAIN; + } + + break; + +#if SCALED_ADDR_MODES + + case GT_MUL: + + if (op1->gtOverflow()) + { + break; + } + + FALLTHROUGH; + + case GT_LSH: + + mul = op1->GetScaledIndex(); + if (mul) + { + /* 'op1' is a scaled value */ + + rv1 = op2; + rv2 = op1->AsOp()->gtOp1; + + int argScale; + while ((rv2->gtOper == GT_MUL || rv2->gtOper == GT_LSH) && (argScale = rv2->GetScaledIndex()) != 0) + { + if (jitIsScaleIndexMul(argScale * mul)) + { + mul = mul * argScale; + rv2 = rv2->AsOp()->gtOp1; + } + else + { + break; + } + } + + noway_assert(rev == false); + rev = true; + + goto FOUND_AM; + } + break; + +#endif // SCALED_ADDR_MODES +#endif // !TARGET_ARMARCH + + case GT_NOP: + + op1 = op1->AsOp()->gtOp1; + goto AGAIN; + + case GT_COMMA: + + op1 = op1->AsOp()->gtOp2; + goto AGAIN; + + default: + break; + } + + noway_assert(op2); + switch (op2->gtOper) + { +#if !defined(TARGET_ARMARCH) + // TODO-ARM64-CQ, TODO-ARM-CQ: For now we don't try to create a scaled index. + case GT_ADD: + + if (op2->gtOverflow()) + { + break; + } + + if (op2->AsOp()->gtOp2->IsIntCnsFitsInI32() && + FitsIn(cns + op2->AsOp()->gtOp2->AsIntCon()->gtIconVal)) + { + cns += op2->AsOp()->gtOp2->AsIntCon()->gtIconVal; + op2 = op2->AsOp()->gtOp1; + + goto AGAIN; + } + + break; + +#if SCALED_ADDR_MODES + + case GT_MUL: + + if (op2->gtOverflow()) + { + break; + } + + FALLTHROUGH; + + case GT_LSH: + + mul = op2->GetScaledIndex(); + if (mul) + { + // 'op2' is a scaled value...is it's argument also scaled? + int argScale; + rv2 = op2->AsOp()->gtOp1; + while ((rv2->gtOper == GT_MUL || rv2->gtOper == GT_LSH) && (argScale = rv2->GetScaledIndex()) != 0) + { + if (jitIsScaleIndexMul(argScale * mul)) + { + mul = mul * argScale; + rv2 = rv2->AsOp()->gtOp1; + } + else + { + break; + } + } + + rv1 = op1; + + goto FOUND_AM; + } + break; + +#endif // SCALED_ADDR_MODES +#endif // !TARGET_ARMARCH + + case GT_NOP: + + op2 = op2->AsOp()->gtOp1; + goto AGAIN; + + case GT_COMMA: + + op2 = op2->AsOp()->gtOp2; + goto AGAIN; + + default: + break; + } + + /* The best we can do "[rv1 + rv2]" or "[rv1 + rv2 + cns]" */ + + rv1 = op1; + rv2 = op2; +#ifdef TARGET_ARM64 + assert(cns == 0); +#endif + +FOUND_AM: + + if (rv2) + { + /* Make sure a GC address doesn't end up in 'rv2' */ + + if (varTypeIsGC(rv2->TypeGet())) + { + noway_assert(rv1 && !varTypeIsGC(rv1->TypeGet())); + + tmp = rv1; + rv1 = rv2; + rv2 = tmp; + + rev = !rev; + } + + /* Special case: constant array index (that is range-checked) */ + + if (fold) + { + ssize_t tmpMul; + GenTree* index; + + if ((rv2->gtOper == GT_MUL || rv2->gtOper == GT_LSH) && (rv2->AsOp()->gtOp2->IsCnsIntOrI())) + { + /* For valuetype arrays where we can't use the scaled address + mode, rv2 will point to the scaled index. So we have to do + more work */ + assert(false); //TODO: this method is copied from Compiler, but not properly refactored so this call is not available, put off for now and investigate when it gets hit + index = NULL; + tmpMul = 1; // compiler->optGetArrayRefScaleAndIndex(rv2, &index DEBUGARG(false)); + if (mul) + { + tmpMul *= mul; + } + } + else + { + /* May be a simple array. rv2 will points to the actual index */ + + index = rv2; + tmpMul = mul; + } + + /* Get hold of the array index and see if it's a constant */ + if (index->IsIntCnsFitsInI32()) + { + /* Get hold of the index value */ + ssize_t ixv = index->AsIntConCommon()->IconValue(); + +#if SCALED_ADDR_MODES + /* Scale the index if necessary */ + if (tmpMul) + { + ixv *= tmpMul; + } +#endif + + if (FitsIn(cns + ixv)) + { + /* Add the scaled index to the offset value */ + + cns += ixv; + +#if SCALED_ADDR_MODES + /* There is no scaled operand any more */ + mul = 0; +#endif + rv2 = nullptr; + } + } + } + } + + // We shouldn't have [rv2*1 + cns] - this is equivalent to [rv1 + cns] + noway_assert(rv1 || mul != 1); + + noway_assert(FitsIn(cns)); + + if (rv1 == nullptr && rv2 == nullptr) + { + return false; + } + + /* Success - return the various components to the caller */ + + *revPtr = rev; + *rv1Ptr = rv1; + *rv2Ptr = rv2; +#if SCALED_ADDR_MODES + * mulPtr = mul; +#endif + * cnsPtr = cns; + + return true; +} +#endif // TARGET_WASM + //------------------------------------------------------------------------ // Given an address expression, compute its costs and addressing mode opportunities, // and mark addressing mode candidates as GTF_DONT_CSE. @@ -2884,7 +3344,11 @@ bool Compiler::gtMarkAddrMode(GenTree* addr, int* pCostEx, int* pCostSz, var_typ GenTree* base; // This is the base of the address. GenTree* idx; // This is the index. +#ifdef TARGET_WASM + if (genCreateAddrMode(this, addr, false /*fold*/, &rev, &base, &idx, +#else if (codeGen->genCreateAddrMode(addr, false /*fold*/, &rev, &base, &idx, +#endif #if SCALED_ADDR_MODES &mul, #endif // SCALED_ADDR_MODES @@ -2894,7 +3358,7 @@ bool Compiler::gtMarkAddrMode(GenTree* addr, int* pCostEx, int* pCostSz, var_typ // nodes with GTF_ADDRMODE_NO_CSE and calculate a more accurate cost. addr->gtFlags |= GTF_ADDRMODE_NO_CSE; -#ifdef TARGET_XARCH +#if defined(TARGET_XARCH) || defined(TARGET_WASM) // TODO Wasm // addrmodeCount is the count of items that we used to form // an addressing mode. The maximum value is 4 when we have // all of these: { base, idx, cns, mul } @@ -3025,7 +3489,7 @@ bool Compiler::gtMarkAddrMode(GenTree* addr, int* pCostEx, int* pCostSz, var_typ } } } -#elif defined TARGET_ARM64 +#elif defined TARGET_ARM64 if (base) { *pCostEx += base->GetCostEx(); @@ -3088,7 +3552,7 @@ bool Compiler::gtMarkAddrMode(GenTree* addr, int* pCostEx, int* pCostSz, var_typ // we have already found either a non-ADD op1 or a non-constant op2. gtWalkOp(&op1, &op2, nullptr, true); -#if defined(TARGET_XARCH) +#if defined(TARGET_XARCH) || defined(TARGET_WASM) // For XARCH we will fold GT_ADDs in the op2 position into the addressing mode, so we call // gtWalkOp on both operands of the original GT_ADD. // This is not done for ARMARCH. Though the stated reason is that we don't try to create a @@ -3098,7 +3562,7 @@ bool Compiler::gtMarkAddrMode(GenTree* addr, int* pCostEx, int* pCostSz, var_typ // into the addressing mode. // Walk op2 looking for non-overflow GT_ADDs of constants. gtWalkOp(&op2, &op1, nullptr, true); -#endif // defined(TARGET_XARCH) +#endif // defined(TARGET_XARCH) || defined(TARGET_WASM) // OK we are done walking the tree // Now assert that op1 and op2 correspond with base and idx @@ -3331,7 +3795,7 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) goto COMMON_CNS; } -#elif defined TARGET_XARCH +#elif defined(TARGET_XARCH) || defined(TARGET_WASM) // TODO Wasm case GT_CNS_STR: #ifdef TARGET_AMD64 @@ -3660,6 +4124,16 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) costEx = 1; costSz = 2; + if (isflt || varTypeIsFloating(op1->TypeGet())) + { + /* cast involving floats always go through memory */ + costEx = IND_COST_EX * 2; + costSz = 6; + } +#elif defined(TARGET_WASM) + costEx = 1; + costSz = 2; + if (isflt || varTypeIsFloating(op1->TypeGet())) { /* cast involving floats always go through memory */ diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index d29db8b41ca6..8415d364922e 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -7390,7 +7390,7 @@ inline unsigned GenTree::GetMultiRegCount() } #endif -#if !defined(TARGET_64BIT) +#if !defined(TARGET_64BIT) && !defined(TARGET_WASM32) && !defined(TARGET_WASM64) if (OperIsMultiRegOp()) { return AsMultiRegOp()->GetRegCount(); @@ -7459,7 +7459,7 @@ inline regNumber GenTree::GetRegByIndex(int regIndex) return AsPutArgSplit()->GetRegNumByIdx(regIndex); } #endif -#if !defined(TARGET_64BIT) +#if !defined(TARGET_64BIT) && !defined(TARGET_WASM32) && !defined(TARGET_WASM64) if (OperIsMultiRegOp()) { return AsMultiRegOp()->GetRegNumByIdx(regIndex); @@ -7519,7 +7519,7 @@ inline var_types GenTree::GetRegTypeByIndex(int regIndex) return AsPutArgSplit()->GetRegType(regIndex); } #endif -#if !defined(TARGET_64BIT) +#if !defined(TARGET_64BIT) && !defined(TARGET_WASM32) && !defined(TARGET_WASM64) if (OperIsMultiRegOp()) { return AsMultiRegOp()->GetRegType(regIndex); @@ -7583,7 +7583,7 @@ inline unsigned int GenTree::GetRegSpillFlagByIdx(int regIndex) const return AsPutArgSplit()->GetRegSpillFlagByIdx(regIndex); } #endif -#if !defined(TARGET_64BIT) +#if !defined(TARGET_64BIT) && !defined(TARGET_WASM32) && !defined(TARGET_WASM64) if (OperIsMultiRegOp()) { return AsMultiRegOp()->GetRegSpillFlagByIdx(regIndex); diff --git a/src/coreclr/jit/gtstructs.h b/src/coreclr/jit/gtstructs.h index b4bad947fd90..2947e36d5f76 100644 --- a/src/coreclr/jit/gtstructs.h +++ b/src/coreclr/jit/gtstructs.h @@ -122,6 +122,8 @@ GTSTRUCT_2(CC , GT_JCC, GT_SETCC) GTSTRUCT_1(MultiRegOp , GT_MUL_LONG) #elif defined (TARGET_ARM) GTSTRUCT_3(MultiRegOp , GT_MUL_LONG, GT_PUTARG_REG, GT_BITCAST) +#elif defined (TARGET_WASM32) +GTSTRUCT_3(MultiRegOp, GT_MUL_LONG, GT_PUTARG_REG, GT_BITCAST) #endif /*****************************************************************************/ #undef GTSTRUCT_0 diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 63c4c3727f17..a725b363cb32 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -3783,7 +3783,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, GenTree* op1; GenTree* op2; -#if defined(TARGET_XARCH) || defined(TARGET_ARM64) +#if defined(TARGET_XARCH) || defined(TARGET_ARM64) || defined(TARGET_WASM) // TODO Wasm // TODO-ARM-CQ: reenable treating Interlocked operation as intrinsic // Note that CORINFO_INTRINSIC_InterlockedAdd32/64 are not actually used. @@ -19774,7 +19774,7 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo) return; } } - else if (genTypeSize(sigType) < EA_PTRSIZE) + else if (genTypeSize(sigType) < TARGET_POINTER_SIZE) { // Narrowing cast. if (inlArgNode->OperIs(GT_LCL_VAR)) diff --git a/src/coreclr/jit/instr.cpp b/src/coreclr/jit/instr.cpp index a1135a347f1b..d6e2951fa894 100644 --- a/src/coreclr/jit/instr.cpp +++ b/src/coreclr/jit/instr.cpp @@ -1810,6 +1810,20 @@ instruction CodeGen::ins_Copy(var_types dstType) { return INS_mov; } +#elif defined(TARGET_WASM) + if (varTypeIsSIMD(dstType)) + { + return INS_movaps; + } + else if (varTypeIsFloating(dstType)) + { + // Both float and double copy can use movaps + return INS_movaps; + } + else + { + return INS_mov; + } #else // TARGET_* #error "Unknown TARGET_" #endif @@ -1983,7 +1997,7 @@ instruction CodeGenInterface::ins_StoreFromSrc(regNumber srcReg, var_types dstTy return ins; } -#if defined(TARGET_XARCH) +#if defined(TARGET_XARCH) || defined(TARGET_WASM) bool CodeGen::isMoveIns(instruction ins) { @@ -2323,6 +2337,8 @@ void CodeGen::instGen_Set_Reg_To_Zero(emitAttr size, regNumber reg, insFlags fla GetEmitter()->emitIns_R_R(INS_xor, size, reg, reg); #elif defined(TARGET_ARMARCH) GetEmitter()->emitIns_R_I(INS_mov, size, reg, 0 ARM_ARG(flags)); +#elif defined(TARGET_WASM) + GetEmitter()->emitIns_R_R(INS_xor, size, reg, reg); #else #error "Unknown TARGET" #endif @@ -2340,6 +2356,8 @@ void CodeGen::instGen_Compare_Reg_To_Zero(emitAttr size, regNumber reg) GetEmitter()->emitIns_R_R(INS_test, size, reg, reg); #elif defined(TARGET_ARMARCH) GetEmitter()->emitIns_R_I(INS_cmp, size, reg, 0); +#elif defined(TARGET_WASM) + GetEmitter()->emitIns_R_R(INS_test, size, reg, reg); #else #error "Unknown TARGET" #endif diff --git a/src/coreclr/jit/instr.h b/src/coreclr/jit/instr.h index ed001fdc1bc7..f8e31e6bcc40 100644 --- a/src/coreclr/jit/instr.h +++ b/src/coreclr/jit/instr.h @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. /*****************************************************************************/ +#ifndef TARGET_WASM #ifndef _INSTR_H_ #define _INSTR_H_ /*****************************************************************************/ @@ -296,3 +297,4 @@ enum emitAttr : unsigned /*****************************************************************************/ #endif //_INSTR_H_ /*****************************************************************************/ +#endif // !TARGET_WASM diff --git a/src/coreclr/jit/instrs.h b/src/coreclr/jit/instrs.h index b543f781645f..770c5a61e4f7 100644 --- a/src/coreclr/jit/instrs.h +++ b/src/coreclr/jit/instrs.h @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#ifndef TARGET_WASM #if defined(TARGET_XARCH) #include "instrsxarch.h" #elif defined(TARGET_ARM) @@ -10,3 +11,4 @@ #else #error Unsupported or unset target architecture #endif // target type +#endif // !TARGET_WASM diff --git a/src/coreclr/jit/jit.h b/src/coreclr/jit/jit.h index c5d9774dfa63..5438697ad0ca 100644 --- a/src/coreclr/jit/jit.h +++ b/src/coreclr/jit/jit.h @@ -147,6 +147,7 @@ #if !defined(HOST_ARM64) #define _CROSS_COMPILER_ #endif +#elif defined(TARGET_WASM) #else #error Unsupported or unset target architecture #endif @@ -194,6 +195,10 @@ #define IMAGE_FILE_MACHINE_TARGET IMAGE_FILE_MACHINE_ARMNT #elif defined(TARGET_ARM64) #define IMAGE_FILE_MACHINE_TARGET IMAGE_FILE_MACHINE_ARM64 // 0xAA64 +#elif defined(TARGET_WASM32) +#define IMAGE_FILE_MACHINE_TARGET IMAGE_FILE_MACHINE_WASM32 +#elif defined(TARGET_WASM64) +#define IMAGE_FILE_MACHINE_TARGET IMAGE_FILE_MACHINE_WASM64 #else #error Unsupported or unset target architecture #endif @@ -239,10 +244,11 @@ #define UNIX_AMD64_ABI_ONLY(x) #endif // defined(UNIX_AMD64_ABI) -#if defined(DEBUG) && !defined(OSX_ARM64_ABI) +#if defined(DEBUG) && !defined(OSX_ARM64_ABI) && !defined(TARGET_WASM32) // On all platforms except Arm64 OSX arguments on the stack are taking // register size slots. On these platforms we could check that stack slots count // matchs out new byte size calculations. +// For Wasm32 doubles are 8 bytes so can't be asserted against the size of a "register" #define DEBUG_ARG_SLOTS #endif diff --git a/src/coreclr/jit/jiteh.cpp b/src/coreclr/jit/jiteh.cpp index 8f511a2913d1..129cb61e06b1 100644 --- a/src/coreclr/jit/jiteh.cpp +++ b/src/coreclr/jit/jiteh.cpp @@ -891,7 +891,7 @@ unsigned Compiler::ehGetCallFinallyRegionIndex(unsigned finallyIndex, bool* inTr assert(finallyIndex != EHblkDsc::NO_ENCLOSING_INDEX); assert(ehGetDsc(finallyIndex)->HasFinallyHandler()); -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_WASM) return ehGetDsc(finallyIndex)->ebdGetEnclosingRegionIndex(inTryRegion); #else *inTryRegion = true; @@ -1092,6 +1092,7 @@ void* Compiler::ehEmitCookie(BasicBlock* block) return cookie; } +#ifndef TARGET_WASM /***************************************************************************** * Determine the emitter code offset for a block. If the block is a finally * target, choose the offset of the NOP padding that precedes the block. @@ -1101,6 +1102,7 @@ UNATIVE_OFFSET Compiler::ehCodeOffset(BasicBlock* block) { return GetEmitter()->emitCodeOffset(ehEmitCookie(block), 0); } +#endif // !TARGET_WASM /****************************************************************************/ @@ -2979,10 +2981,12 @@ void Compiler::dispOutgoingEHClause(unsigned num, const CORINFO_EH_CLAUSE& claus { if (opts.dspDiffable) { +#ifndef TARGET_WASM /* (( brace matching editor workaround to compensate for the following line */ printf("EH#%u: try [%s..%s) handled by [%s..%s) ", num, GetEmitter()->emitOffsetToLabel(clause.TryOffset), GetEmitter()->emitOffsetToLabel(clause.TryLength), GetEmitter()->emitOffsetToLabel(clause.HandlerOffset), GetEmitter()->emitOffsetToLabel(clause.HandlerLength)); +#endif // !TARGET_WASM } else { @@ -3005,9 +3009,11 @@ void Compiler::dispOutgoingEHClause(unsigned num, const CORINFO_EH_CLAUSE& claus case CORINFO_EH_CLAUSE_FILTER: if (opts.dspDiffable) { +#ifndef TARGET_WASM /* ( brace matching editor workaround to compensate for the following line */ printf("filter at [%s..%s)", GetEmitter()->emitOffsetToLabel(clause.ClassToken), GetEmitter()->emitOffsetToLabel(clause.HandlerOffset)); +#endif // !TARGET_WASM } else { diff --git a/src/coreclr/jit/jitgcinfo.h b/src/coreclr/jit/jitgcinfo.h index 83393bda9bbe..1274d9a2a162 100644 --- a/src/coreclr/jit/jitgcinfo.h +++ b/src/coreclr/jit/jitgcinfo.h @@ -170,10 +170,12 @@ class GCInfo } unsigned short rpdGCtype : 2; // is this a pointer, after all? +#ifndef TARGET_WASM GCtype rpdGCtypeGet() { return (GCtype)rpdGCtype; } +#endif // !TARGET_WASM unsigned short rpdIsThis : 1; // is it the 'this' pointer unsigned short rpdCall : 1; // is this a true call site? diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index ea51815de2e0..4fd1cdeec3ec 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -344,12 +344,14 @@ void Compiler::lvaInitTypeRef() // emitter when the varNum is greater that 32767 (see emitLclVarAddr::initLclVarAddr) lvaAllocOutgoingArgSpaceVar(); +#ifndef TARGET_WASM #ifdef DEBUG if (verbose) { lvaTableDump(INITIAL_FRAME_LAYOUT); } #endif +#endif //!TARGET_WASM } /*****************************************************************************/ @@ -423,8 +425,10 @@ void Compiler::lvaInitArgs(InitVarDscInfo* varDscInfo) noway_assert(varDscInfo->varNum == info.compArgsCount); assert(varDscInfo->intRegArgNum <= MAX_REG_ARG); +#ifndef TARGET_WASM codeGen->intRegState.rsCalleeRegArgCount = varDscInfo->intRegArgNum; codeGen->floatRegState.rsCalleeRegArgCount = varDscInfo->floatRegArgNum; +#endif // !TARGET_WASM #if FEATURE_FASTTAILCALL // Save the stack usage information @@ -2524,6 +2528,7 @@ void Compiler::lvaPromoteLongVars() } } +#ifndef TARGET_WASM #ifdef DEBUG if (verbose) { @@ -2531,6 +2536,7 @@ void Compiler::lvaPromoteLongVars() lvaTableDump(); } #endif // DEBUG +#endif //!TARGET_WASM } #endif // !defined(TARGET_64BIT) @@ -4587,6 +4593,7 @@ inline void Compiler::lvaIncrementFrameSize(unsigned size) compLclFrameSize += size; } +#ifndef TARGET_WASM /**************************************************************************** * * Return true if absolute offsets of temps are larger than vars, or in other @@ -5705,7 +5712,7 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, #if defined(TARGET_X86) argOffs += TARGET_POINTER_SIZE; -#elif defined(TARGET_AMD64) +#elif defined(TARGET_AMD64) || defined(TARGET_WASM) // TODO Wasm // Register arguments on AMD64 also takes stack space. (in the backing store) varDsc->SetStackOffset(argOffs); argOffs += TARGET_POINTER_SIZE; @@ -7718,6 +7725,7 @@ int Compiler::lvaGetInitialSPRelativeOffset(unsigned varNum) return lvaToInitialSPRelativeOffset(varDsc->GetStackOffset(), varDsc->lvFramePointerBased); } +#endif // !TARGET_WASM // Given a local variable offset, and whether that offset is frame-pointer based, return its offset from Initial-SP. // This is used, for example, to figure out the offset of the frame pointer from Initial-SP. diff --git a/src/coreclr/jit/liveness.cpp b/src/coreclr/jit/liveness.cpp index cae25c9d5935..4396c7565c07 100644 --- a/src/coreclr/jit/liveness.cpp +++ b/src/coreclr/jit/liveness.cpp @@ -131,10 +131,12 @@ void Compiler::fgLocalVarLiveness() { printf("*************** In fgLocalVarLiveness()\n"); +#ifndef TARGET_WASM if (compRationalIRForm) { lvaTableDump(); } +#endif //!TARGET_WASM } #endif // DEBUG @@ -1029,10 +1031,12 @@ void Compiler::fgExtendDbgLifetimes() LIR::Range initRange = LIR::EmptyRange(); initRange.InsertBefore(nullptr, zero, store); -#if !defined(TARGET_64BIT) +#if !defined(TARGET_64BIT) && !defined(TARGET_WASM32) && !defined(TARGET_WASM64) DecomposeLongs::DecomposeRange(this, initRange); #endif // !defined(TARGET_64BIT) +#ifndef TARGET_WASM m_pLowering->LowerRange(block, initRange); +#endif // !TARGET_WASM // Naively inserting the initializer at the end of the block may add code after the block's // terminator, in which case the inserted code will never be executed (and the IR for the @@ -1995,10 +1999,12 @@ void Compiler::fgComputeLifeLIR(VARSET_TP& life, BasicBlock* block, VARSET_VALAR store->OperIs(GT_STOREIND) ? store->AsStoreInd()->Data() : store->AsBlk()->Data(); data->SetUnusedValue(); +#ifndef TARGET_WASM if (data->isIndir()) { Lowering::TransformUnusedIndirection(data->AsIndir(), this, block); } +#endif // !TARGET_WASM fgRemoveDeadStoreLIR(store, block); } @@ -2119,12 +2125,14 @@ void Compiler::fgComputeLifeLIR(VARSET_TP& life, BasicBlock* block, VARSET_VALAR case GT_DYN_BLK: { bool removed = fgTryRemoveNonLocal(node, &blockRange); +#ifndef TARGET_WASM if (!removed && node->IsUnusedValue()) { // IR doesn't expect dummy uses of `GT_OBJ/BLK/DYN_BLK`. JITDUMP("Transform an unused OBJ/BLK node [%06d]\n", dspTreeID(node)); Lowering::TransformUnusedIndirection(node->AsIndir(), this, block); } +#endif // !TARGET_WASM } break; diff --git a/src/coreclr/jit/llvm.cpp b/src/coreclr/jit/llvm.cpp new file mode 100644 index 000000000000..e85f153a6339 --- /dev/null +++ b/src/coreclr/jit/llvm.cpp @@ -0,0 +1,145 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifdef TARGET_WASM +#include +#include "compiler.h" +#include "block.h" +#include "gentree.h" +#include "llvm.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/Bitcode/BitcodeWriter.h" + +using llvm::Function; +using llvm::FunctionType; +using llvm::Type; +using llvm::LLVMContext; +using llvm::ArrayRef; +using llvm::Module; + +static Module* _module = nullptr; +static LLVMContext _llvmContext; +static void* _thisPtr; // TODO: workaround for not changing the JIT/EE interface. As this is static, it will probably fail if multithreaded compilation is attempted +static const char* (*_getMangledMethodName)(void*, CORINFO_METHOD_STRUCT_*); +static char* _outputFileName; +static Function* _doNothingFunction; + +Compiler::Info _info; + +extern "C" DLLEXPORT void registerLlvmCallbacks(void* thisPtr, const char* outputFileName, const char* triple, const char* dataLayout, const char* (*getMangledMethodNamePtr)(void*, CORINFO_METHOD_STRUCT_*)) +{ + _thisPtr = thisPtr; + _getMangledMethodName = getMangledMethodNamePtr; + if (_module == nullptr) // registerLlvmCallbacks is called for each method to compile, but must only created the module once. Better perhaps to split this into 2 calls. + { + _module = new Module(llvm::StringRef("netscripten-clrjit"), _llvmContext); + _module->setTargetTriple(triple); + _module->setDataLayout(dataLayout); + _outputFileName = (char*)malloc(strlen(outputFileName) + 7); + strcpy(_outputFileName, "1.txt"); // ??? without this _outputFileName is corrupted + strcpy(_outputFileName, outputFileName); + strcpy(_outputFileName + strlen(_outputFileName) - 3, "clrjit"); // use different module output name for now, TODO: delete if old LLVM gen does not create a module + strcat(_outputFileName, ".bc"); + } +} + +void Llvm::Init() +{ +} + +void Llvm::llvmShutdown() +{ +#if DEBUG + std::error_code ec; + char* txtFileName = (char *)malloc(strlen(_outputFileName) + 2); // .txt is longer than .bc + strcpy(txtFileName, _outputFileName); + strcpy(txtFileName + strlen(_outputFileName) - 2, "txt"); + llvm::raw_fd_ostream textOutputStream(txtFileName, ec); + _module->print(textOutputStream, (llvm::AssemblyAnnotationWriter*)NULL); + free(txtFileName); +#endif //DEBUG + llvm::raw_fd_ostream OS(_outputFileName, ec); + llvm::WriteBitcodeToFile(*_module, OS); + delete _module; +// Module.Verify(LLVMVerifierFailureAction.LLVMAbortProcessAction); +} + +FunctionType* GetFunctionTypeForMethod(Compiler::Info info) +{ + if (info.compArgsCount != 0 || info.compRetType != TYP_VOID) + { + fatal(CORJIT_SKIPPED); + } + // all functions have shadow stack as first arg (i8*) + return FunctionType::get(Type::getVoidTy(_llvmContext), ArrayRef(Type::getInt8PtrTy(_llvmContext)), false); +} + +void EmitDoNothingCall(llvm::IRBuilder<>& builder) +{ + if (_doNothingFunction == nullptr) + { + _doNothingFunction = Function::Create(FunctionType::get(Type::getVoidTy(_llvmContext), ArrayRef(), false), Function::ExternalLinkage, 0U, "llvm.donothing", _module); + } + builder.CreateCall(_doNothingFunction); +} + +bool visitNode(llvm::IRBuilder<> &builder, GenTree* node) +{ + switch (node->OperGet()) + { + case GT_IL_OFFSET: + break; + case GT_NO_OP: + EmitDoNothingCall(builder); + break; + case GT_RETURN: + builder.CreateRetVoid(); + break; + default: + return false; + } + return true; +} + +//------------------------------------------------------------------------ +// Compile: Compile IR to LLVM, adding to the LLVM Module +// +void Llvm::Compile(Compiler* pCompiler) +{ + _info = pCompiler->info; + + const char* mangledName = (*_getMangledMethodName)(_thisPtr, _info.compMethodHnd); + Function* function = Function::Create(GetFunctionTypeForMethod(_info), Function::ExternalLinkage, 0U, mangledName, _module); // TODO: ExternalLinkage forced as linked from old module + + BasicBlock* firstBb = pCompiler->fgFirstBB; + llvm::IRBuilder<> builder(_llvmContext); + for (BasicBlock* block = firstBb; block; block = block->bbNext) + { + if (block->hasTryIndex()) + { + function->dropAllReferences(); + function->eraseFromParent(); + fatal(CORJIT_SKIPPED); // TODO: skip anything with a try block for now + } + + llvm::BasicBlock* entry = llvm::BasicBlock::Create(_llvmContext, "", function); + builder.SetInsertPoint(entry); + // GenTree* firstGt = block->GetFirstLIRNode(); +// firstGt->VisitOperands(); + for (GenTree* node = block->GetFirstLIRNode(); node; node = node->gtNext) + { + if (!visitNode(builder, node)) + { + // delete created function , dont want duplicate symbols + function->dropAllReferences(); + function->eraseFromParent(); + fatal(CORJIT_SKIPPED); // visitNode incomplete + } + } + } +} +#endif diff --git a/src/coreclr/jit/llvm.h b/src/coreclr/jit/llvm.h new file mode 100644 index 000000000000..907273192130 --- /dev/null +++ b/src/coreclr/jit/llvm.h @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/*****************************************************************************/ +#ifndef _LLVM_H_ +#define _LLVM_H_ +#undef __PLACEMENT_NEW_INLINE + +#include "alloc.h" +#include "jitpch.h" +#include + +// these break std::min/max in LLVM's headers +#undef min +#undef max +// this breaks StringMap.h +#undef NumItems +#ifdef TARGET_WASM + +#define IMAGE_FILE_MACHINE_WASM32 0xFFFF +#define IMAGE_FILE_MACHINE_WASM64 0xFFFE // TODO: appropriate values for this? Used to check compilation is for intended target + +extern "C" void registerLlvmCallbacks(void* thisPtr, const char* outputFileName, const char* triple, const char* dataLayout, const char* (*getMangledMethodNamePtr)(void*, CORINFO_METHOD_STRUCT_*)); + +class Llvm +{ +public: + static void Init(); + static void llvmShutdown(); + + void Compile(Compiler* pCompiler); +}; + +#endif + +#endif /* End of _LLVM_H_ */ diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 8a4f96028761..4a5468cb3c58 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -1371,7 +1371,9 @@ void Lowering::LowerArg(GenTreeCall* call, GenTree** ppArg) // For longs, we will replace the GT_LONG with a GT_FIELD_LIST, and put that under a PUTARG_STK. // Although the hi argument needs to be pushed first, that will be handled by the general case, // in which the fields will be reversed. +#ifdef DEBUG_ARG_SLOTS assert(info->numSlots == 2); +#endif newArg->SetRegNum(REG_STK); BlockRange().InsertBefore(arg, fieldList, newArg); } @@ -5354,7 +5356,7 @@ GenTree* Lowering::LowerConstIntDivOrMod(GenTree* node) return nullptr; } -#if defined(TARGET_XARCH) || defined(TARGET_ARM64) +#if defined(TARGET_XARCH) || defined(TARGET_ARM64) || defined(TARGET_WASM) // TODO Wasm ssize_t magic; int shift; @@ -5818,7 +5820,7 @@ PhaseStatus Lowering::DoPhase() InsertPInvokeMethodProlog(); } -#if !defined(TARGET_64BIT) +#if !defined(TARGET_64BIT) && !defined(TARGET_WASM32) DecomposeLongs decomp(comp); // Initialize the long decomposition class. if (comp->compLongUsed) { @@ -5831,7 +5833,7 @@ PhaseStatus Lowering::DoPhase() /* Make the block publicly available */ comp->compCurBB = block; -#if !defined(TARGET_64BIT) +#if !defined(TARGET_64BIT) && !defined(TARGET_WASM32) if (comp->compLongUsed) { decomp.DecomposeBlock(block); diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h index 83ce67d2685d..c2f7333b888a 100644 --- a/src/coreclr/jit/lower.h +++ b/src/coreclr/jit/lower.h @@ -10,7 +10,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ - +#ifndef TARGET_WASM #ifndef _LOWER_H_ #define _LOWER_H_ @@ -97,7 +97,7 @@ class Lowering final : public Phase void ContainCheckCompare(GenTreeOp* node); void ContainCheckBinary(GenTreeOp* node); void ContainCheckBoundsChk(GenTreeBoundsChk* node); -#ifdef TARGET_XARCH +#if defined(TARGET_XARCH) || defined(TARGET_WASM) void ContainCheckFloatBinary(GenTreeOp* node); void ContainCheckIntrinsic(GenTreeOp* node); #endif // TARGET_XARCH @@ -228,7 +228,7 @@ class Lowering final : public Phase // return true if this call target is within range of a pc-rel call on the machine bool IsCallTargetInRange(void* addr); -#if defined(TARGET_XARCH) +#if defined(TARGET_XARCH) || defined(TARGET_WASM) GenTree* PreferredRegOptionalOperand(GenTree* tree); // ------------------------------------------------------------------ @@ -601,3 +601,4 @@ class Lowering final : public Phase }; #endif // _LOWER_H_ +#endif // TARGET_WASM diff --git a/src/coreclr/jit/lsra.h b/src/coreclr/jit/lsra.h index 345cbb451f78..7df658560985 100644 --- a/src/coreclr/jit/lsra.h +++ b/src/coreclr/jit/lsra.h @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. /*****************************************************************************/ - +#ifndef TARGET_WASM #ifndef _LSRA_H_ #define _LSRA_H_ @@ -2375,3 +2375,4 @@ void dumpRegMask(regMaskTP regs); /*****************************************************************************/ #endif //_LSRA_H_ /*****************************************************************************/ +#endif // TARGET_WASM diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 94f98634dff6..28591171a776 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -3065,6 +3065,9 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) #elif defined(TARGET_X86) passUsingFloatRegs = false; +#elif defined(TARGET_WASM) + + passUsingFloatRegs = varTypeIsFloating(argx); #else #error Unsupported or unset target architecture @@ -3115,7 +3118,7 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) assert(structSize == info.compCompHnd->getClassSize(objClass)); } } -#if defined(TARGET_AMD64) +#if defined(TARGET_AMD64) || defined(TARGET_WASM) // TODO Wasm #ifdef UNIX_AMD64_ABI if (!isStructArg) { @@ -7497,11 +7500,13 @@ GenTree* Compiler::fgMorphPotentialTailCall(GenTreeCall* call) // On x86 we have a faster mechanism than the general one which we use // in almost all cases. See fgCanTailCallViaJitHelper for more information. +#ifdef TARGET_X86 if (fgCanTailCallViaJitHelper()) { tailCallViaJitHelper = true; } - else +#endif + if (!tailCallViaJitHelper) { // Make sure we can get the helpers. We do this last as the runtime // will likely be required to generate these. @@ -7741,6 +7746,7 @@ GenTree* Compiler::fgMorphPotentialTailCall(GenTreeCall* call) // Do some target-specific transformations (before we process the args, // etc.) for the JIT helper case. +#ifdef TARGET_X86 if (tailCallViaJitHelper) { fgMorphTailCallViaJitHelper(call); @@ -7749,7 +7755,7 @@ GenTree* Compiler::fgMorphPotentialTailCall(GenTreeCall* call) // argument list, invalidating the argInfo. call->fgArgInfo = nullptr; } - +#endif // Tail call via JIT helper: The VM can't use return address hijacking // if we're not going to return and the helper doesn't have enough info // to safely poll, so we poll before the tail call, if the block isn't @@ -8457,6 +8463,7 @@ GenTree* Compiler::getTokenHandleTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, bo return result; } +#ifdef TARGET_X86 /***************************************************************************** * * Transform the given GT_CALL tree for tail call via JIT helper. @@ -8654,6 +8661,7 @@ void Compiler::fgMorphTailCallViaJitHelper(GenTreeCall* call) JITDUMP("fgMorphTailCallViaJitHelper (after):\n"); DISPTREE(call); } +#endif //TARGET_X86 //------------------------------------------------------------------------ // fgGetStubAddrArg: Return the virtual stub address for the given call. @@ -17086,6 +17094,7 @@ void Compiler::fgMergeBlockReturn(BasicBlock* block) } } +#ifndef TARGET_WASM /***************************************************************************** * * Make some decisions about the kind of code to generate. @@ -17196,6 +17205,7 @@ void Compiler::fgSetOptions() // printf("method will %s be fully interruptible\n", GetInterruptible() ? " " : "not"); } +#endif // !TARGET_WASM /*****************************************************************************/ @@ -17886,7 +17896,9 @@ void Compiler::fgPromoteStructs() if (verbose) { printf("\nlvaTable before fgPromoteStructs\n"); +#ifndef TARGET_WASM lvaTableDump(); +#endif //!TARGET_WASM } #endif // DEBUG @@ -17958,7 +17970,9 @@ void Compiler::fgPromoteStructs() if (verbose) { printf("\nlvaTable after fgPromoteStructs\n"); +#ifndef TARGET_WASM lvaTableDump(); +#endif //!TARGET_WASM } #endif // DEBUG } @@ -19106,6 +19120,7 @@ bool Compiler::fgCheckStmtAfterTailCall() return nextMorphStmt == nullptr; } +#ifdef TARGET_X86 //------------------------------------------------------------------------ // fgCanTailCallViaJitHelper: check whether we can use the faster tailcall // JIT helper on x86. @@ -19115,17 +19130,13 @@ bool Compiler::fgCheckStmtAfterTailCall() // bool Compiler::fgCanTailCallViaJitHelper() { -#ifndef TARGET_X86 - // On anything except X86 we have no faster mechanism available. - return false; -#else // The JIT helper does not properly handle the case where localloc was used. if (compLocallocUsed) return false; return true; -#endif } +#endif static const int numberOfTrackedFlags = 5; static const unsigned trackedFlags[numberOfTrackedFlags] = {GTF_ASG, GTF_CALL, GTF_EXCEPT, GTF_GLOB_REF, diff --git a/src/coreclr/jit/register.h b/src/coreclr/jit/register.h index d06bef0cea1d..d6ce4be200e7 100644 --- a/src/coreclr/jit/register.h +++ b/src/coreclr/jit/register.h @@ -12,7 +12,8 @@ #define REGALIAS(alias, realname) #endif -#if defined(TARGET_XARCH) +// TODO: WASM doesn't have these but can't compile without them +#if defined(TARGET_XARCH) || defined(TARGET_WASM) #if defined(TARGET_X86) /* @@ -69,6 +70,9 @@ REGALIAS(EDI, RDI) #ifdef TARGET_AMD64 #define XMMBASE 16 #define XMMMASK(x) (__int64(1) << ((x)+XMMBASE)) +#elif defined(TARGET_WASM) +#define XMMBASE 16 +#define XMMMASK(x) (__int64(1) << ((x)+XMMBASE)) #else // !TARGET_AMD64 #define XMMBASE 8 #define XMMMASK(x) (__int32(1) << ((x)+XMMBASE)) @@ -103,6 +107,7 @@ REGDEF(STK, 16+XMMBASE, 0x0000, "STK" ) #elif defined(TARGET_ARM64) #include "registerarm64.h" +#elif defined(TARGET_WASM) #else #error Unsupported or unset target architecture #endif // target type diff --git a/src/coreclr/jit/regset.cpp b/src/coreclr/jit/regset.cpp index 1a7816d6ca25..f18206dc0499 100644 --- a/src/coreclr/jit/regset.cpp +++ b/src/coreclr/jit/regset.cpp @@ -189,6 +189,7 @@ void RegSet::rsRemoveRegsModified(regMaskTP mask) void RegSet::SetMaskVars(regMaskTP newMaskVars) { +#ifndef TARGET_WASM #ifdef DEBUG if (m_rsCompiler->verbose) { @@ -208,6 +209,7 @@ void RegSet::SetMaskVars(regMaskTP newMaskVars) printf("\n"); } #endif // DEBUG +#endif // !TARGET_WASM _rsMaskVars = newMaskVars; } @@ -431,7 +433,11 @@ void RegSet::rsSpillTree(regNumber reg, GenTree* tree, unsigned regIdx /* =0 */) // Generate the code to spill the register var_types storeType = floatSpill ? treeType : tempType; +#ifndef TARGET_WASM m_rsCompiler->codeGen->spillReg(storeType, temp, reg); +#else + assert(false); // TODO +#endif // !TARGET_WASM // Mark the tree node as having been spilled rsMarkSpill(tree, reg); diff --git a/src/coreclr/jit/scopeinfo.cpp b/src/coreclr/jit/scopeinfo.cpp index f667dde97fd6..b88504b43d70 100644 --- a/src/coreclr/jit/scopeinfo.cpp +++ b/src/coreclr/jit/scopeinfo.cpp @@ -48,6 +48,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * ****************************************************************************** */ +#ifndef TARGET_WASM #include "jitpch.h" #ifdef _MSC_VER @@ -1876,3 +1877,4 @@ void CodeGen::psiMoveToStack(unsigned varNum) #endif // ACCURATE_PROLOG_DEBUG_INFO } #endif // USING_SCOPE_INFO +#endif // !TARGET_WASM diff --git a/src/coreclr/jit/simd.h b/src/coreclr/jit/simd.h index 07d70e20d503..4cd84e8d0347 100644 --- a/src/coreclr/jit/simd.h +++ b/src/coreclr/jit/simd.h @@ -29,6 +29,21 @@ enum SIMDLevel // Floating-point instructions are legacy SSE encoded. SIMD_SSE4_Supported = 2, + // AVX2 - Hardware has AVX and AVX2 instruction set. + // Vector length is 256-bit and SIMD instructions are VEX-256 encoded. + // Floating-point instructions are VEX-128 encoded. + SIMD_AVX2_Supported = 3 +#elif defined(TARGET_WASM) + // SSE2 - The min bar of SIMD ISA on x86/x64. + // Vector length is 128-bit. + // Floating-point instructions are legacy SSE encoded. + SIMD_SSE2_Supported = 1, + + // SSE4 - RyuJIT may generate SSE3, SSSE3, SSE4.1 and SSE4.2 instructions for certain intrinsics. + // Vector length is 128-bit. + // Floating-point instructions are legacy SSE encoded. + SIMD_SSE4_Supported = 2, + // AVX2 - Hardware has AVX and AVX2 instruction set. // Vector length is 256-bit and SIMD instructions are VEX-256 encoded. // Floating-point instructions are VEX-128 encoded. diff --git a/src/coreclr/jit/stacklevelsetter.h b/src/coreclr/jit/stacklevelsetter.h index f43558f09769..48208cda198a 100644 --- a/src/coreclr/jit/stacklevelsetter.h +++ b/src/coreclr/jit/stacklevelsetter.h @@ -3,6 +3,8 @@ #pragma once +#ifndef TARGET_WASM + #include "compiler.h" #include "phase.h" @@ -42,3 +44,5 @@ class StackLevelSetter final : public Phase bool throwHelperBlocksUsed; // Were any throw helper blocks created for this method. #endif // !FEATURE_FIXED_OUT_ARGS }; + +#endif // !TARGET_WASM diff --git a/src/coreclr/jit/target.h b/src/coreclr/jit/target.h index 3c6b4970fb14..1e36cf59f273 100644 --- a/src/coreclr/jit/target.h +++ b/src/coreclr/jit/target.h @@ -22,6 +22,10 @@ #define TARGET_READABLE_NAME "ARM" #elif defined(TARGET_ARM64) #define TARGET_READABLE_NAME "ARM64" +#elif defined(TARGET_WASM64) +#define TARGET_READABLE_NAME "WASM64" +#elif defined(TARGET_WASM32) +#define TARGET_READABLE_NAME "WASM32" #else #error Unsupported or unset target architecture #endif @@ -41,6 +45,9 @@ #define REGMASK_BITS 64 #define CSE_CONST_SHARED_LOW_BITS 12 +#elif defined(TARGET_WASM) +#define REGMASK_BITS 32 +#define CSE_CONST_SHARED_LOW_BITS 16 #else #error Unsupported or unset target architecture #endif @@ -141,6 +148,26 @@ enum _regMask_enum : unsigned #include "register.h" }; +#elif defined(TARGET_WASM) +enum _regNumber_enum : unsigned +{ +#define REGDEF(name, rnum, mask, sname) REG_##name = rnum, +#define REGALIAS(alias, realname) REG_##alias = REG_##realname, +#include "register.h" + + REG_COUNT, + REG_NA = REG_COUNT, + ACTUAL_REG_COUNT = REG_COUNT - 1 // everything but REG_STK (only real regs) +}; + +enum _regMask_enum : unsigned +{ + RBM_NONE = 0, + +#define REGDEF(name, rnum, mask, sname) RBM_##name = mask, +#define REGALIAS(alias, realname) RBM_##alias = RBM_##realname, +#include "register.h" +}; #else #error Unsupported target architecture #endif @@ -1556,6 +1583,751 @@ typedef unsigned char regNumberSmall; #define REG_ZERO_INIT_FRAME_REG2 REG_R10 #define REG_ZERO_INIT_FRAME_SIMD REG_V16 +#elif defined(TARGET_WASM) // TODO: a copy of X64 +#define RBM_LNGRET_LO RBM_EAX +#define REG_LNGRET_HI REG_EDX +#define RBM_LNGRET_HI RBM_EDX + + + +#if defined(TARGET_WASM32) +#define TARGET_POINTER_SIZE 4 // equal to sizeof(void*) and the managed pointer size in bytes for this +// target +#else +#define TARGET_POINTER_SIZE 8 +#endif + + +// TODO-AMD64-CQ: Fine tune the following xxBlk threshold values: + +#define CPU_LOAD_STORE_ARCH 0 +#define CPU_HAS_FP_SUPPORT 1 +#define ROUND_FLOAT 0 // Do not round intermed float expression results +#define CPU_HAS_BYTE_REGS 0 + +#define CPBLK_UNROLL_LIMIT 64 // Upper bound to let the code generator to loop unroll CpBlk. +#define INITBLK_UNROLL_LIMIT 128 // Upper bound to let the code generator to loop unroll InitBlk. +#define CPOBJ_NONGC_SLOTS_LIMIT 4 // For CpObj code generation, this is the the threshold of the number + // of contiguous non-gc slots that trigger generating rep movsq instead of + // sequences of movsq instructions + +#ifdef FEATURE_SIMD +#define ALIGN_SIMD_TYPES 1 // whether SIMD type locals are to be aligned +#if defined(UNIX_AMD64_ABI) +#define FEATURE_PARTIAL_SIMD_CALLEE_SAVE 0 // Whether SIMD registers are partially saved at calls +#else // !UNIX_AMD64_ABI +#define FEATURE_PARTIAL_SIMD_CALLEE_SAVE 1 // Whether SIMD registers are partially saved at calls +#endif // !UNIX_AMD64_ABI +#endif +#define FEATURE_FIXED_OUT_ARGS 1 // Preallocate the outgoing arg area in the prolog +#define FEATURE_STRUCTPROMOTE 1 // JIT Optimization to promote fields of structs into registers +#define FEATURE_FASTTAILCALL 1 // Tail calls made as epilog+jmp +#define FEATURE_TAILCALL_OPT 1 // opportunistic Tail calls (i.e. without ".tail" prefix) made as fast tail calls. +#define FEATURE_SET_FLAGS 0 // Set to true to force the JIT to mark the trees with GTF_SET_FLAGS when the flags need to be set +#define MAX_PASS_SINGLEREG_BYTES 8 // Maximum size of a struct passed in a single register (double). +#ifdef UNIX_AMD64_ABI +#define FEATURE_MULTIREG_ARGS_OR_RET 1 // Support for passing and/or returning single values in more than one register +#define FEATURE_MULTIREG_ARGS 1 // Support for passing a single argument in more than one register +#define FEATURE_MULTIREG_RET 1 // Support for returning a single value in more than one register +#define FEATURE_MULTIREG_STRUCT_PROMOTE 1 // True when we want to promote fields of a multireg struct into registers +#define FEATURE_STRUCT_CLASSIFIER 1 // Uses a classifier function to determine if structs are passed/returned in more than one register +#define MAX_PASS_MULTIREG_BYTES 32 // Maximum size of a struct that could be passed in more than one register (Max is two SIMD16s) +#define MAX_RET_MULTIREG_BYTES 32 // Maximum size of a struct that could be returned in more than one register (Max is two SIMD16s) +#define MAX_ARG_REG_COUNT 2 // Maximum registers used to pass a single argument in multiple registers. +#define MAX_RET_REG_COUNT 2 // Maximum registers used to return a value. + +#define MAX_MULTIREG_COUNT 2 // Maxiumum number of registers defined by a single instruction (including calls). + // This is also the maximum number of registers for a MultiReg node. +#else // !UNIX_AMD64_ABI +#define WINDOWS_AMD64_ABI // Uses the Windows ABI for AMD64 +#define FEATURE_MULTIREG_ARGS_OR_RET 0 // Support for passing and/or returning single values in more than one register +#define FEATURE_MULTIREG_ARGS 0 // Support for passing a single argument in more than one register +#define FEATURE_MULTIREG_RET 0 // Support for returning a single value in more than one register +#define FEATURE_MULTIREG_STRUCT_PROMOTE 0 // True when we want to promote fields of a multireg struct into registers +#define MAX_PASS_MULTIREG_BYTES 0 // No multireg arguments +#define MAX_RET_MULTIREG_BYTES 0 // No multireg return values +#define MAX_ARG_REG_COUNT 1 // Maximum registers used to pass a single argument (no arguments are passed using multiple registers) +#define MAX_RET_REG_COUNT 1 // Maximum registers used to return a value. + +#define MAX_MULTIREG_COUNT 2 // Maxiumum number of registers defined by a single instruction (including calls). + // This is also the maximum number of registers for a MultiReg node. + // Note that this must be greater than 1 so that GenTreeLclVar can have an array of + // MAX_MULTIREG_COUNT - 1. +#endif // !UNIX_AMD64_ABI + +#define NOGC_WRITE_BARRIERS 0 // We DO-NOT have specialized WriteBarrier JIT Helpers that DO-NOT trash the RBM_CALLEE_TRASH registers +#define USER_ARGS_COME_LAST 1 +#define EMIT_TRACK_STACK_DEPTH 1 + +#if defined(TARGET_WASM32) +#define TARGET_POINTER_SIZE 4 // equal to sizeof(void*) and the managed pointer size in bytes for this target +#else +#define TARGET_POINTER_SIZE 8 +#endif + +#define FEATURE_EH 1 // To aid platform bring-up, eliminate exceptional EH clauses (catch, filter, filter-handler, fault) and directly execute 'finally' clauses. +#define FEATURE_EH_CALLFINALLY_THUNKS 1 // Generate call-to-finally code in "thunks" in the enclosing EH region, protected by "cloned finally" clauses. +#ifdef UNIX_AMD64_ABI +#define ETW_EBP_FRAMED 1 // if 1 we cannot use EBP as a scratch register and must create EBP based frames for most methods +#else // !UNIX_AMD64_ABI +#define ETW_EBP_FRAMED 0 // if 1 we cannot use EBP as a scratch register and must create EBP based frames for most methods +#endif // !UNIX_AMD64_ABI +#define CSE_CONSTS 1 // Enable if we want to CSE constants + +#define RBM_ALLFLOAT (RBM_XMM0 | RBM_XMM1 | RBM_XMM2 | RBM_XMM3 | RBM_XMM4 | RBM_XMM5 | RBM_XMM6 | RBM_XMM7 | RBM_XMM8 | RBM_XMM9 | RBM_XMM10 | RBM_XMM11 | RBM_XMM12 | RBM_XMM13 | RBM_XMM14 | RBM_XMM15) +#define RBM_ALLDOUBLE RBM_ALLFLOAT +#define REG_FP_FIRST REG_XMM0 +#define REG_FP_LAST REG_XMM15 +#define FIRST_FP_ARGREG REG_XMM0 + +#ifdef UNIX_AMD64_ABI +#define LAST_FP_ARGREG REG_XMM7 +#else // !UNIX_AMD64_ABI +#define LAST_FP_ARGREG REG_XMM3 +#endif // !UNIX_AMD64_ABI + +#define REGNUM_BITS 6 // number of bits in a REG_* +#define REGMASK_BITS 32 // number of bits in a REGNUM_MASK +#if defined(TARGET_WASM32) // morph phase uses this +#define REGSIZE_BYTES 4 // number of bytes in one register +#else +#define REGSIZE_BYTES 8 // number of bytes in one register +#endif +#define XMM_REGSIZE_BYTES 16 // XMM register size in bytes +#define YMM_REGSIZE_BYTES 32 // YMM register size in bytes + +#define CODE_ALIGN 1 // code alignment requirement +#define STACK_ALIGN 16 // stack alignment requirement +#define STACK_ALIGN_SHIFT 4 // Shift-right amount to convert size in bytes to size in STACK_ALIGN units == log2(STACK_ALIGN) + +#if ETW_EBP_FRAMED +#define RBM_ETW_FRAMED_EBP RBM_NONE +#define RBM_ETW_FRAMED_EBP_LIST +#define REG_ETW_FRAMED_EBP_LIST +#define REG_ETW_FRAMED_EBP_COUNT 0 +#else // !ETW_EBP_FRAMED +#define RBM_ETW_FRAMED_EBP RBM_EBP +#define RBM_ETW_FRAMED_EBP_LIST RBM_EBP, +#define REG_ETW_FRAMED_EBP_LIST REG_EBP, +#define REG_ETW_FRAMED_EBP_COUNT 1 +#endif // !ETW_EBP_FRAMED + +//#ifdef UNIX_AMD64_ABI +// WASM take these as they seem more liberal +#define MIN_ARG_AREA_FOR_CALL 0 // Minimum required outgoing argument space for a call. + +#define RBM_INT_CALLEE_SAVED (RBM_EBX|RBM_ETW_FRAMED_EBP|RBM_R12|RBM_R13|RBM_R14|RBM_R15) +#define RBM_INT_CALLEE_TRASH (RBM_EAX|RBM_RDI|RBM_RSI|RBM_EDX|RBM_ECX|RBM_R8|RBM_R9|RBM_R10|RBM_R11) +#define RBM_FLT_CALLEE_SAVED (0) +#define RBM_FLT_CALLEE_TRASH (RBM_XMM0|RBM_XMM1|RBM_XMM2|RBM_XMM3|RBM_XMM4|RBM_XMM5|RBM_XMM6|RBM_XMM7| \ + RBM_XMM8|RBM_XMM9|RBM_XMM10|RBM_XMM11|RBM_XMM12|RBM_XMM13|RBM_XMM14|RBM_XMM15) +#define REG_PROFILER_ENTER_ARG_0 REG_R14 +#define RBM_PROFILER_ENTER_ARG_0 RBM_R14 +#define REG_PROFILER_ENTER_ARG_1 REG_R15 +#define RBM_PROFILER_ENTER_ARG_1 RBM_R15 + +#define REG_DEFAULT_PROFILER_CALL_TARGET REG_R11 + +//#else // !UNIX_AMD64_ABI +//#define MIN_ARG_AREA_FOR_CALL (4 * REGSIZE_BYTES) // Minimum required outgoing argument space for a call. +// +//#define RBM_INT_CALLEE_SAVED (RBM_EBX|RBM_ESI|RBM_EDI|RBM_ETW_FRAMED_EBP|RBM_R12|RBM_R13|RBM_R14|RBM_R15) +//#define RBM_INT_CALLEE_TRASH (RBM_EAX|RBM_ECX|RBM_EDX|RBM_R8|RBM_R9|RBM_R10|RBM_R11) +//#define RBM_FLT_CALLEE_SAVED (RBM_XMM6|RBM_XMM7|RBM_XMM8|RBM_XMM9|RBM_XMM10|RBM_XMM11|RBM_XMM12|RBM_XMM13|RBM_XMM14|RBM_XMM15) +//#define RBM_FLT_CALLEE_TRASH (RBM_XMM0|RBM_XMM1|RBM_XMM2|RBM_XMM3|RBM_XMM4|RBM_XMM5) +//#endif // !UNIX_AMD64_ABI + +#define REG_FLT_CALLEE_SAVED_FIRST REG_XMM6 +#define REG_FLT_CALLEE_SAVED_LAST REG_XMM15 + +#define RBM_CALLEE_TRASH (RBM_INT_CALLEE_TRASH | RBM_FLT_CALLEE_TRASH) +#define RBM_CALLEE_SAVED (RBM_INT_CALLEE_SAVED | RBM_FLT_CALLEE_SAVED) + +#define RBM_CALLEE_TRASH_NOGC RBM_CALLEE_TRASH + +#define RBM_ALLINT (RBM_INT_CALLEE_SAVED | RBM_INT_CALLEE_TRASH) + +#if 0 +#define REG_VAR_ORDER REG_EAX,REG_EDX,REG_ECX,REG_ESI,REG_EDI,REG_EBX,REG_ETW_FRAMED_EBP_LIST \ + REG_R8,REG_R9,REG_R10,REG_R11,REG_R14,REG_R15,REG_R12,REG_R13 +#else + // TEMPORARY ORDER TO AVOID CALLEE-SAVES + // TODO-CQ: Review this and set appropriately +#ifdef UNIX_AMD64_ABI +#define REG_VAR_ORDER REG_EAX,REG_EDI,REG_ESI, \ + REG_EDX,REG_ECX,REG_R8,REG_R9, \ + REG_R10,REG_R11,REG_EBX,REG_ETW_FRAMED_EBP_LIST \ + REG_R14,REG_R15,REG_R12,REG_R13 +#else // !UNIX_AMD64_ABI +#define REG_VAR_ORDER REG_EAX,REG_EDX,REG_ECX, \ + REG_R8,REG_R9,REG_R10,REG_R11, \ + REG_ESI,REG_EDI,REG_EBX,REG_ETW_FRAMED_EBP_LIST \ + REG_R14,REG_R15,REG_R12,REG_R13 +#endif // !UNIX_AMD64_ABI +#endif + +#define REG_VAR_ORDER_FLT REG_XMM0,REG_XMM1,REG_XMM2,REG_XMM3,REG_XMM4,REG_XMM5,REG_XMM6,REG_XMM7,REG_XMM8,REG_XMM9,REG_XMM10,REG_XMM11,REG_XMM12,REG_XMM13,REG_XMM14,REG_XMM15 + +#ifdef UNIX_AMD64_ABI +#define CNT_CALLEE_SAVED (5 + REG_ETW_FRAMED_EBP_COUNT) +#define CNT_CALLEE_TRASH (9) +#define CNT_CALLEE_ENREG (CNT_CALLEE_SAVED) + +#define CNT_CALLEE_SAVED_FLOAT (0) +#define CNT_CALLEE_TRASH_FLOAT (16) + +#define REG_CALLEE_SAVED_ORDER REG_EBX,REG_ETW_FRAMED_EBP_LIST REG_R12,REG_R13,REG_R14,REG_R15 +#define RBM_CALLEE_SAVED_ORDER RBM_EBX,RBM_ETW_FRAMED_EBP_LIST RBM_R12,RBM_R13,RBM_R14,RBM_R15 +#else // !UNIX_AMD64_ABI +#define CNT_CALLEE_SAVED (7 + REG_ETW_FRAMED_EBP_COUNT) +#define CNT_CALLEE_TRASH (7) +#define CNT_CALLEE_ENREG (CNT_CALLEE_SAVED) + +#define CNT_CALLEE_SAVED_FLOAT (10) +#define CNT_CALLEE_TRASH_FLOAT (6) + +#define REG_CALLEE_SAVED_ORDER REG_EBX,REG_ESI,REG_EDI,REG_ETW_FRAMED_EBP_LIST REG_R12,REG_R13,REG_R14,REG_R15 +#define RBM_CALLEE_SAVED_ORDER RBM_EBX,RBM_ESI,RBM_EDI,RBM_ETW_FRAMED_EBP_LIST RBM_R12,RBM_R13,RBM_R14,RBM_R15 +#endif // !UNIX_AMD64_ABI + +#define CALLEE_SAVED_REG_MAXSZ (CNT_CALLEE_SAVED*REGSIZE_BYTES) +#define CALLEE_SAVED_FLOAT_MAXSZ (CNT_CALLEE_SAVED_FLOAT*16) + +// register to hold shift amount +#define REG_SHIFT REG_ECX +#define RBM_SHIFT RBM_ECX + +// This is a general scratch register that does not conflict with the argument registers +#define REG_SCRATCH REG_EAX + +// Where is the exception object on entry to the handler block? +#ifdef UNIX_AMD64_ABI +#define REG_EXCEPTION_OBJECT REG_ESI +#define RBM_EXCEPTION_OBJECT RBM_ESI +#else // !UNIX_AMD64_ABI +#define REG_EXCEPTION_OBJECT REG_EDX +#define RBM_EXCEPTION_OBJECT RBM_EDX +#endif // !UNIX_AMD64_ABI + +#define REG_JUMP_THUNK_PARAM REG_EAX +#define RBM_JUMP_THUNK_PARAM RBM_EAX + +// Register to be used for emitting helper calls whose call target is an indir of an +// absolute memory address in case of Rel32 overflow i.e. a data address could not be +// encoded as PC-relative 32-bit offset. +// +// Notes: +// 1) that RAX is callee trash register that is not used for passing parameter and +// also results in smaller instruction encoding. +// 2) Profiler Leave callback requires the return value to be preserved +// in some form. We can use custom calling convention for Leave callback. +// For e.g return value could be preserved in rcx so that it is available for +// profiler. +#define REG_DEFAULT_HELPER_CALL_TARGET REG_RAX +#define RBM_DEFAULT_HELPER_CALL_TARGET RBM_RAX + +// GenericPInvokeCalliHelper VASigCookie Parameter +#define REG_PINVOKE_COOKIE_PARAM REG_R11 +#define RBM_PINVOKE_COOKIE_PARAM RBM_R11 + +// GenericPInvokeCalliHelper unmanaged target Parameter +#define REG_PINVOKE_TARGET_PARAM REG_R10 +#define RBM_PINVOKE_TARGET_PARAM RBM_R10 + +// IL stub's secret MethodDesc parameter (JitFlags::JIT_FLAG_PUBLISH_SECRET_PARAM) +#define REG_SECRET_STUB_PARAM REG_R10 +#define RBM_SECRET_STUB_PARAM RBM_R10 + +// Registers used by PInvoke frame setup +#define REG_PINVOKE_FRAME REG_EDI +#define RBM_PINVOKE_FRAME RBM_EDI +#define REG_PINVOKE_TCB REG_EAX +#define RBM_PINVOKE_TCB RBM_EAX +#define REG_PINVOKE_SCRATCH REG_EAX +#define RBM_PINVOKE_SCRATCH RBM_EAX + +// The following defines are useful for iterating a regNumber +#define REG_FIRST REG_EAX +#define REG_INT_FIRST REG_EAX +#define REG_INT_LAST REG_R15 +#define REG_INT_COUNT (REG_INT_LAST - REG_INT_FIRST + 1) +#define REG_NEXT(reg) ((regNumber)((unsigned)(reg) + 1)) +#define REG_PREV(reg) ((regNumber)((unsigned)(reg) - 1)) + +// Which register are int and long values returned in ? +#define REG_INTRET REG_EAX +#define RBM_INTRET RBM_EAX + +#define RBM_LNGRET RBM_EAX + +#ifdef UNIX_AMD64_ABI +#define REG_INTRET_1 REG_RDX +#define RBM_INTRET_1 RBM_RDX + +#define REG_LNGRET_1 REG_RDX +#define RBM_LNGRET_1 RBM_RDX +#endif // UNIX_AMD64_ABI + + +#define REG_FLOATRET REG_XMM0 +#define RBM_FLOATRET RBM_XMM0 +#define REG_DOUBLERET REG_XMM0 +#define RBM_DOUBLERET RBM_XMM0 + +#ifdef UNIX_AMD64_ABI +#define REG_FLOATRET_1 REG_XMM1 +#define RBM_FLOATRET_1 RBM_XMM1 + +#define REG_DOUBLERET_1 REG_XMM1 +#define RBM_DOUBLERET_1 RBM_XMM1 +#endif // UNIX_AMD64_ABI + +#define REG_FPBASE REG_EBP +#define RBM_FPBASE RBM_EBP +#define STR_FPBASE "rbp" +#define REG_SPBASE REG_ESP +#define RBM_SPBASE RBM_ESP +#define STR_SPBASE "rsp" + +#define FIRST_ARG_STACK_OFFS (REGSIZE_BYTES) // return address + +#ifdef UNIX_AMD64_ABI +#define MAX_REG_ARG 6 +#define MAX_FLOAT_REG_ARG 8 +#define REG_ARG_FIRST REG_EDI +#define REG_ARG_LAST REG_R9 +#define INIT_ARG_STACK_SLOT 0 // No outgoing reserved stack slots + +#define REG_ARG_0 REG_EDI +#define REG_ARG_1 REG_ESI +#define REG_ARG_2 REG_EDX +#define REG_ARG_3 REG_ECX +#define REG_ARG_4 REG_R8 +#define REG_ARG_5 REG_R9 + +extern const regNumber intArgRegs[MAX_REG_ARG]; +extern const regMaskTP intArgMasks[MAX_REG_ARG]; +extern const regNumber fltArgRegs[MAX_FLOAT_REG_ARG]; +extern const regMaskTP fltArgMasks[MAX_FLOAT_REG_ARG]; + +#define RBM_ARG_0 RBM_RDI +#define RBM_ARG_1 RBM_RSI +#define RBM_ARG_2 RBM_EDX +#define RBM_ARG_3 RBM_ECX +#define RBM_ARG_4 RBM_R8 +#define RBM_ARG_5 RBM_R9 +#else // !UNIX_AMD64_ABI +#define MAX_REG_ARG 4 +#define MAX_FLOAT_REG_ARG 4 +#define REG_ARG_FIRST REG_ECX +#define REG_ARG_LAST REG_R9 +#define INIT_ARG_STACK_SLOT 4 // 4 outgoing reserved stack slots + +#define REG_ARG_0 REG_ECX +#define REG_ARG_1 REG_EDX +#define REG_ARG_2 REG_R8 +#define REG_ARG_3 REG_R9 + +extern const regNumber intArgRegs[MAX_REG_ARG]; +extern const regMaskTP intArgMasks[MAX_REG_ARG]; +extern const regNumber fltArgRegs[MAX_FLOAT_REG_ARG]; +extern const regMaskTP fltArgMasks[MAX_FLOAT_REG_ARG]; + +#define RBM_ARG_0 RBM_ECX +#define RBM_ARG_1 RBM_EDX +#define RBM_ARG_2 RBM_R8 +#define RBM_ARG_3 RBM_R9 +#endif // !UNIX_AMD64_ABI + +#define REG_FLTARG_0 REG_XMM0 +#define REG_FLTARG_1 REG_XMM1 +#define REG_FLTARG_2 REG_XMM2 +#define REG_FLTARG_3 REG_XMM3 + +#define RBM_FLTARG_0 RBM_XMM0 +#define RBM_FLTARG_1 RBM_XMM1 +#define RBM_FLTARG_2 RBM_XMM2 +#define RBM_FLTARG_3 RBM_XMM3 + +#ifdef UNIX_AMD64_ABI +#define REG_FLTARG_4 REG_XMM4 +#define REG_FLTARG_5 REG_XMM5 +#define REG_FLTARG_6 REG_XMM6 +#define REG_FLTARG_7 REG_XMM7 + +#define RBM_FLTARG_4 RBM_XMM4 +#define RBM_FLTARG_5 RBM_XMM5 +#define RBM_FLTARG_6 RBM_XMM6 +#define RBM_FLTARG_7 RBM_XMM7 + +#define RBM_ARG_REGS (RBM_ARG_0|RBM_ARG_1|RBM_ARG_2|RBM_ARG_3|RBM_ARG_4|RBM_ARG_5) +#define RBM_FLTARG_REGS (RBM_FLTARG_0|RBM_FLTARG_1|RBM_FLTARG_2|RBM_FLTARG_3|RBM_FLTARG_4|RBM_FLTARG_5|RBM_FLTARG_6|RBM_FLTARG_7) +#else // !UNIX_AMD64_ABI +#define RBM_ARG_REGS (RBM_ARG_0|RBM_ARG_1|RBM_ARG_2|RBM_ARG_3) +#define RBM_FLTARG_REGS (RBM_FLTARG_0|RBM_FLTARG_1|RBM_FLTARG_2|RBM_FLTARG_3) +#endif // !UNIX_AMD64_ABI + +// The registers trashed by profiler enter/leave/tailcall hook +// See vm\amd64\asmhelpers.asm for more details. +#define RBM_PROFILER_ENTER_TRASH RBM_CALLEE_TRASH +#define RBM_PROFILER_TAILCALL_TRASH RBM_PROFILER_LEAVE_TRASH + +// The registers trashed by the CORINFO_HELP_STOP_FOR_GC helper. +#ifdef UNIX_AMD64_ABI + // See vm\amd64\unixasmhelpers.S for more details. + // + // On Unix a struct of size >=9 and <=16 bytes in size is returned in two return registers. + // The return registers could be any two from the set { RAX, RDX, XMM0, XMM1 }. + // STOP_FOR_GC helper preserves all the 4 possible return registers. +#define RBM_STOP_FOR_GC_TRASH (RBM_CALLEE_TRASH & ~(RBM_FLOATRET | RBM_INTRET | RBM_FLOATRET_1 | RBM_INTRET_1)) +#define RBM_PROFILER_LEAVE_TRASH (RBM_CALLEE_TRASH & ~(RBM_FLOATRET | RBM_INTRET | RBM_FLOATRET_1 | RBM_INTRET_1)) +#else + // See vm\amd64\asmhelpers.asm for more details. +#define RBM_STOP_FOR_GC_TRASH (RBM_CALLEE_TRASH & ~(RBM_FLOATRET | RBM_INTRET)) +#define RBM_PROFILER_LEAVE_TRASH (RBM_CALLEE_TRASH & ~(RBM_FLOATRET | RBM_INTRET)) +#endif + + // The registers trashed by the CORINFO_HELP_INIT_PINVOKE_FRAME helper. +#define RBM_INIT_PINVOKE_FRAME_TRASH RBM_CALLEE_TRASH + +// What sort of reloc do we use for [disp32] address mode +#define IMAGE_REL_BASED_DISP32 IMAGE_REL_BASED_REL32 + +// What sort of reloc to we use for 'moffset' address mode (for 'mov eax, moffset' or 'mov moffset, eax') +#define IMAGE_REL_BASED_MOFFSET IMAGE_REL_BASED_DIR64 + +// Pointer-sized string move instructions +#define INS_movsp INS_movsq +#define INS_r_movsp INS_r_movsq +#define INS_stosp INS_stosq +#define INS_r_stosp INS_r_stosq + +// AMD64 uses FEATURE_FIXED_OUT_ARGS so this can be zero. +#define STACK_PROBE_BOUNDARY_THRESHOLD_BYTES 0 + +#define REG_STACK_PROBE_HELPER_ARG REG_R11 +#define RBM_STACK_PROBE_HELPER_ARG RBM_R11 + +#ifdef TARGET_UNIX +#define RBM_STACK_PROBE_HELPER_TRASH RBM_NONE +#else // !TARGET_UNIX +#define RBM_STACK_PROBE_HELPER_TRASH RBM_RAX +#endif // !TARGET_UNIX + +#elif defined(TARGET_ARM) + +// TODO-ARM-CQ: Use shift for division by power of 2 +// TODO-ARM-CQ: Check for sdiv/udiv at runtime and generate it if available +#define USE_HELPERS_FOR_INT_DIV 1 // BeagleBoard (ARMv7A) doesn't support SDIV/UDIV +#define CPU_LOAD_STORE_ARCH 1 +#define CPU_HAS_FP_SUPPORT 1 +#define ROUND_FLOAT 0 // Do not round intermed float expression results +#define CPU_HAS_BYTE_REGS 0 + +#define CPBLK_UNROLL_LIMIT 32 // Upper bound to let the code generator to loop unroll CpBlk. +#define INITBLK_UNROLL_LIMIT 16 // Upper bound to let the code generator to loop unroll InitBlk. + +#define FEATURE_FIXED_OUT_ARGS 1 // Preallocate the outgoing arg area in the prolog +#define FEATURE_STRUCTPROMOTE 1 // JIT Optimization to promote fields of structs into registers +#define FEATURE_MULTIREG_STRUCT_PROMOTE 0 // True when we want to promote fields of a multireg struct into registers +#define FEATURE_FASTTAILCALL 0 // Tail calls made as epilog+jmp +#define FEATURE_TAILCALL_OPT 0 // opportunistic Tail calls (i.e. without ".tail" prefix) made as fast tail calls. +#define FEATURE_SET_FLAGS 1 // Set to true to force the JIT to mark the trees with GTF_SET_FLAGS when the flags need to be set +#define FEATURE_MULTIREG_ARGS_OR_RET 1 // Support for passing and/or returning single values in more than one register (including HFA support) +#define FEATURE_MULTIREG_ARGS 1 // Support for passing a single argument in more than one register (including passing HFAs) +#define FEATURE_MULTIREG_RET 1 // Support for returning a single value in more than one register (including HFA returns) +#define FEATURE_STRUCT_CLASSIFIER 0 // Uses a classifier function to determine is structs are passed/returned in more than one register +#define MAX_PASS_SINGLEREG_BYTES 8 // Maximum size of a struct passed in a single register (double). +#define MAX_PASS_MULTIREG_BYTES 32 // Maximum size of a struct that could be passed in more than one register (Max is an HFA of 4 doubles) +#define MAX_RET_MULTIREG_BYTES 32 // Maximum size of a struct that could be returned in more than one register (Max is an HFA of 4 doubles) +#define MAX_ARG_REG_COUNT 4 // Maximum registers used to pass a single argument in multiple registers. (max is 4 floats or doubles using an HFA) +#define MAX_RET_REG_COUNT 4 // Maximum registers used to return a value. + +#define MAX_MULTIREG_COUNT 4 // Maxiumum number of registers defined by a single instruction (including calls). + // This is also the maximum number of registers for a MultiReg node. + +#define NOGC_WRITE_BARRIERS 0 // We DO-NOT have specialized WriteBarrier JIT Helpers that DO-NOT trash the RBM_CALLEE_TRASH registers +#define USER_ARGS_COME_LAST 1 +#define EMIT_TRACK_STACK_DEPTH 1 // This is something of a workaround. For both ARM and AMD64, the frame size is fixed, so we don't really + // need to track stack depth, but this is currently necessary to get GC information reported at call sites. +#define TARGET_POINTER_SIZE 4 // equal to sizeof(void*) and the managed pointer size in bytes for this target +#define FEATURE_EH 1 // To aid platform bring-up, eliminate exceptional EH clauses (catch, filter, filter-handler, fault) and directly execute 'finally' clauses. +#define FEATURE_EH_CALLFINALLY_THUNKS 0 // Generate call-to-finally code in "thunks" in the enclosing EH region, protected by "cloned finally" clauses. +#define ETW_EBP_FRAMED 1 // if 1 we cannot use REG_FP as a scratch register and must setup the frame pointer for most methods +#define CSE_CONSTS 1 // Enable if we want to CSE constants + +#define REG_FP_FIRST REG_F0 +#define REG_FP_LAST REG_F31 +#define FIRST_FP_ARGREG REG_F0 +#define LAST_FP_ARGREG REG_F15 + +#define REGNUM_BITS 6 // number of bits in a REG_* +#define REGMASK_BITS 64 // number of bits in a REGNUM_MASK +#define REGSIZE_BYTES 4 // number of bytes in one register +#define MIN_ARG_AREA_FOR_CALL 0 // Minimum required outgoing argument space for a call. + +#define CODE_ALIGN 2 // code alignment requirement +#define STACK_ALIGN 8 // stack alignment requirement + +#define RBM_INT_CALLEE_SAVED (RBM_R4|RBM_R5|RBM_R6|RBM_R7|RBM_R8|RBM_R9|RBM_R10) +#define RBM_INT_CALLEE_TRASH (RBM_R0|RBM_R1|RBM_R2|RBM_R3|RBM_R12|RBM_LR) +#define RBM_FLT_CALLEE_SAVED (RBM_F16|RBM_F17|RBM_F18|RBM_F19|RBM_F20|RBM_F21|RBM_F22|RBM_F23|RBM_F24|RBM_F25|RBM_F26|RBM_F27|RBM_F28|RBM_F29|RBM_F30|RBM_F31) +#define RBM_FLT_CALLEE_TRASH (RBM_F0|RBM_F1|RBM_F2|RBM_F3|RBM_F4|RBM_F5|RBM_F6|RBM_F7|RBM_F8|RBM_F9|RBM_F10|RBM_F11|RBM_F12|RBM_F13|RBM_F14|RBM_F15) + +#define RBM_CALLEE_SAVED (RBM_INT_CALLEE_SAVED | RBM_FLT_CALLEE_SAVED) +#define RBM_CALLEE_TRASH (RBM_INT_CALLEE_TRASH | RBM_FLT_CALLEE_TRASH) + +#define REG_DEFAULT_HELPER_CALL_TARGET REG_R12 +#define RBM_DEFAULT_HELPER_CALL_TARGET RBM_R12 + +#define REG_FASTTAILCALL_TARGET REG_R12 // Target register for fast tail call +#define RBM_FASTTAILCALL_TARGET RBM_R12 + +#define RBM_ALLINT (RBM_INT_CALLEE_SAVED | RBM_INT_CALLEE_TRASH) +#define RBM_ALLFLOAT (RBM_FLT_CALLEE_SAVED | RBM_FLT_CALLEE_TRASH) +#define RBM_ALLDOUBLE (RBM_F0|RBM_F2|RBM_F4|RBM_F6|RBM_F8|RBM_F10|RBM_F12|RBM_F14|RBM_F16|RBM_F18|RBM_F20|RBM_F22|RBM_F24|RBM_F26|RBM_F28|RBM_F30) + +#define REG_VAR_ORDER REG_R3,REG_R2,REG_R1,REG_R0,REG_R4,REG_LR,REG_R12,\ + REG_R5,REG_R6,REG_R7,REG_R8,REG_R9,REG_R10 + +#define REG_VAR_ORDER_FLT REG_F8, REG_F9, REG_F10, REG_F11, \ + REG_F12, REG_F13, REG_F14, REG_F15, \ + REG_F6, REG_F7, REG_F4, REG_F5, \ + REG_F2, REG_F3, REG_F0, REG_F1, \ + REG_F16, REG_F17, REG_F18, REG_F19, \ + REG_F20, REG_F21, REG_F22, REG_F23, \ + REG_F24, REG_F25, REG_F26, REG_F27, \ + REG_F28, REG_F29, REG_F30, REG_F31, + +#define RBM_LOW_REGS (RBM_R0|RBM_R1|RBM_R2|RBM_R3|RBM_R4|RBM_R5|RBM_R6|RBM_R7) +#define RBM_HIGH_REGS (RBM_R8|RBM_R9|RBM_R10|RBM_R11|RBM_R12|RBM_SP|RBM_LR|RBM_PC) + +#define REG_CALLEE_SAVED_ORDER REG_R4,REG_R5,REG_R6,REG_R7,REG_R8,REG_R9,REG_R10,REG_R11 +#define RBM_CALLEE_SAVED_ORDER RBM_R4,RBM_R5,RBM_R6,RBM_R7,RBM_R8,RBM_R9,RBM_R10,RBM_R11 + +#define CNT_CALLEE_SAVED (8) +#define CNT_CALLEE_TRASH (6) +#define CNT_CALLEE_ENREG (CNT_CALLEE_SAVED-1) + +#define CNT_CALLEE_SAVED_FLOAT (16) +#define CNT_CALLEE_TRASH_FLOAT (16) + +#define CALLEE_SAVED_REG_MAXSZ (CNT_CALLEE_SAVED*REGSIZE_BYTES) +#define CALLEE_SAVED_FLOAT_MAXSZ (CNT_CALLEE_SAVED_FLOAT*sizeof(float)) + +// Temporary registers used for the GS cookie check. +#define REG_GSCOOKIE_TMP_0 REG_R12 +#define REG_GSCOOKIE_TMP_1 REG_LR + +// register to hold shift amount; no special register is required on the ARM +#define REG_SHIFT REG_NA +#define RBM_SHIFT RBM_ALLINT + +// register to hold shift amount when shifting 64-bit values (this uses a helper call) +#define REG_SHIFT_LNG REG_R2 // REG_ARG_2 +#define RBM_SHIFT_LNG RBM_R2 // RBM_ARG_2 + +// This is a general scratch register that does not conflict with the argument registers +#define REG_SCRATCH REG_LR + +// This is a general register that can be optionally reserved for other purposes during codegen +#define REG_OPT_RSVD REG_R10 +#define RBM_OPT_RSVD RBM_R10 + +// We reserve R9 to store SP on entry for stack unwinding when localloc is used +// This needs to stay in sync with the ARM version of InlinedCallFrame::UpdateRegDisplay code. +#define REG_SAVED_LOCALLOC_SP REG_R9 +#define RBM_SAVED_LOCALLOC_SP RBM_R9 + +// Where is the exception object on entry to the handler block? +#define REG_EXCEPTION_OBJECT REG_R0 +#define RBM_EXCEPTION_OBJECT RBM_R0 + +#define REG_JUMP_THUNK_PARAM REG_R12 +#define RBM_JUMP_THUNK_PARAM RBM_R12 + +// ARM write barrier ABI (see vm\arm\asmhelpers.asm, vm\arm\asmhelpers.S): +// CORINFO_HELP_ASSIGN_REF (JIT_WriteBarrier), CORINFO_HELP_CHECKED_ASSIGN_REF (JIT_CheckedWriteBarrier): +// On entry: +// r0: the destination address (LHS of the assignment) +// r1: the object reference (RHS of the assignment) +// On exit: +// r0: trashed +// r3: trashed +// CORINFO_HELP_ASSIGN_BYREF (JIT_ByRefWriteBarrier): +// On entry: +// r0: the destination address (object reference written here) +// r1: the source address (points to object reference to write) +// On exit: +// r0: incremented by 4 +// r1: incremented by 4 +// r2: trashed +// r3: trashed + +#define REG_WRITE_BARRIER_DST_BYREF REG_ARG_0 +#define RBM_WRITE_BARRIER_DST_BYREF RBM_ARG_0 + +#define REG_WRITE_BARRIER_SRC_BYREF REG_ARG_1 +#define RBM_WRITE_BARRIER_SRC_BYREF RBM_ARG_1 + +#define RBM_CALLEE_TRASH_NOGC (RBM_R2|RBM_R3|RBM_LR|RBM_DEFAULT_HELPER_CALL_TARGET) + +// Registers killed by CORINFO_HELP_ASSIGN_REF and CORINFO_HELP_CHECKED_ASSIGN_REF. +#define RBM_CALLEE_TRASH_WRITEBARRIER (RBM_R0|RBM_R3|RBM_LR|RBM_DEFAULT_HELPER_CALL_TARGET) + +// Registers no longer containing GC pointers after CORINFO_HELP_ASSIGN_REF and CORINFO_HELP_CHECKED_ASSIGN_REF. +#define RBM_CALLEE_GCTRASH_WRITEBARRIER RBM_CALLEE_TRASH_WRITEBARRIER + +// Registers killed by CORINFO_HELP_ASSIGN_BYREF. +#define RBM_CALLEE_TRASH_WRITEBARRIER_BYREF (RBM_WRITE_BARRIER_DST_BYREF | RBM_WRITE_BARRIER_SRC_BYREF | RBM_CALLEE_TRASH_NOGC) + +// Registers no longer containing GC pointers after CORINFO_HELP_ASSIGN_BYREF. +// Note that r0 and r1 are still valid byref pointers after this helper call, despite their value being changed. +#define RBM_CALLEE_GCTRASH_WRITEBARRIER_BYREF RBM_CALLEE_TRASH_NOGC + +// GenericPInvokeCalliHelper VASigCookie Parameter +#define REG_PINVOKE_COOKIE_PARAM REG_R4 +#define RBM_PINVOKE_COOKIE_PARAM RBM_R4 + +// GenericPInvokeCalliHelper unmanaged target Parameter +#define REG_PINVOKE_TARGET_PARAM REG_R12 +#define RBM_PINVOKE_TARGET_PARAM RBM_R12 + +// IL stub's secret MethodDesc parameter (JitFlags::JIT_FLAG_PUBLISH_SECRET_PARAM) +#define REG_SECRET_STUB_PARAM REG_R12 +#define RBM_SECRET_STUB_PARAM RBM_R12 + +// R2R indirect call. Use the same registers as VSD +#define REG_R2R_INDIRECT_PARAM REG_R4 +#define RBM_R2R_INDIRECT_PARAM RBM_R4 + +// JMP Indirect call register +#define REG_INDIRECT_CALL_TARGET_REG REG_R12 + +// Registers used by PInvoke frame setup +#define REG_PINVOKE_FRAME REG_R4 +#define RBM_PINVOKE_FRAME RBM_R4 +#define REG_PINVOKE_TCB REG_R5 +#define RBM_PINVOKE_TCB RBM_R5 +#define REG_PINVOKE_SCRATCH REG_R6 +#define RBM_PINVOKE_SCRATCH RBM_R6 + +// The following defines are useful for iterating a regNumber +#define REG_FIRST REG_R0 +#define REG_INT_FIRST REG_R0 +#define REG_INT_LAST REG_LR +#define REG_INT_COUNT (REG_INT_LAST - REG_INT_FIRST + 1) +#define REG_NEXT(reg) ((regNumber)((unsigned)(reg) + 1)) +#define REG_PREV(reg) ((regNumber)((unsigned)(reg) - 1)) + +// The following registers are used in emitting Enter/Leave/Tailcall profiler callbacks +#define REG_PROFILER_ENTER_ARG REG_R0 +#define RBM_PROFILER_ENTER_ARG RBM_R0 +#define REG_PROFILER_RET_SCRATCH REG_R2 +#define RBM_PROFILER_RET_SCRATCH RBM_R2 + +// The registers trashed by profiler enter/leave/tailcall hook +// See vm\arm\asmhelpers.asm for more details. +#define RBM_PROFILER_ENTER_TRASH RBM_NONE +// While REG_PROFILER_RET_SCRATCH is not trashed by the method, the register allocator must +// consider it killed by the return. +#define RBM_PROFILER_LEAVE_TRASH RBM_PROFILER_RET_SCRATCH +#define RBM_PROFILER_TAILCALL_TRASH RBM_NONE + +// Which register are int and long values returned in ? +#define REG_INTRET REG_R0 +#define RBM_INTRET RBM_R0 +#define RBM_LNGRET (RBM_R1|RBM_R0) +#define REG_LNGRET_LO REG_R0 +#define REG_LNGRET_HI REG_R1 +#define RBM_LNGRET_LO RBM_R0 +#define RBM_LNGRET_HI RBM_R1 + +#define REG_FLOATRET REG_F0 +#define RBM_FLOATRET RBM_F0 +#define RBM_DOUBLERET (RBM_F0|RBM_F1) + +// The registers trashed by the CORINFO_HELP_STOP_FOR_GC helper (JIT_RareDisableHelper). +// See vm\arm\amshelpers.asm for more details. +#define RBM_STOP_FOR_GC_TRASH (RBM_CALLEE_TRASH & ~(RBM_LNGRET|RBM_R7|RBM_R8|RBM_R11|RBM_DOUBLERET|RBM_F2|RBM_F3|RBM_F4|RBM_F5|RBM_F6|RBM_F7)) + +// The registers trashed by the CORINFO_HELP_INIT_PINVOKE_FRAME helper. +#define RBM_INIT_PINVOKE_FRAME_TRASH (RBM_CALLEE_TRASH | RBM_PINVOKE_TCB | RBM_PINVOKE_SCRATCH) + +#define REG_FPBASE REG_R11 +#define RBM_FPBASE RBM_R11 +#define STR_FPBASE "r11" +#define REG_SPBASE REG_SP +#define RBM_SPBASE RBM_SP +#define STR_SPBASE "sp" + +#define FIRST_ARG_STACK_OFFS (2*REGSIZE_BYTES) // Caller's saved FP and return address + +#define MAX_REG_ARG 4 +#define MAX_FLOAT_REG_ARG 16 +#define MAX_HFA_RET_SLOTS 8 + +#define REG_ARG_FIRST REG_R0 +#define REG_ARG_LAST REG_R3 +#define REG_ARG_FP_FIRST REG_F0 +#define REG_ARG_FP_LAST REG_F7 +#define INIT_ARG_STACK_SLOT 0 // No outgoing reserved stack slots + +#define REG_ARG_0 REG_R0 +#define REG_ARG_1 REG_R1 +#define REG_ARG_2 REG_R2 +#define REG_ARG_3 REG_R3 + +extern const regNumber intArgRegs[MAX_REG_ARG]; +extern const regMaskTP intArgMasks[MAX_REG_ARG]; + +#define RBM_ARG_0 RBM_R0 +#define RBM_ARG_1 RBM_R1 +#define RBM_ARG_2 RBM_R2 +#define RBM_ARG_3 RBM_R3 + +#define RBM_ARG_REGS (RBM_ARG_0|RBM_ARG_1|RBM_ARG_2|RBM_ARG_3) +#define RBM_FLTARG_REGS (RBM_F0|RBM_F1|RBM_F2|RBM_F3|RBM_F4|RBM_F5|RBM_F6|RBM_F7|RBM_F8|RBM_F9|RBM_F10|RBM_F11|RBM_F12|RBM_F13|RBM_F14|RBM_F15) +#define RBM_DBL_REGS RBM_ALLDOUBLE + +extern const regNumber fltArgRegs[MAX_FLOAT_REG_ARG]; +extern const regMaskTP fltArgMasks[MAX_FLOAT_REG_ARG]; + +#define LBL_DIST_SMALL_MAX_NEG (0) +#define LBL_DIST_SMALL_MAX_POS (+1020) +#define LBL_DIST_MED_MAX_NEG (-4095) +#define LBL_DIST_MED_MAX_POS (+4096) + +#define JMP_DIST_SMALL_MAX_NEG (-2048) +#define JMP_DIST_SMALL_MAX_POS (+2046) + +#define CALL_DIST_MAX_NEG (-16777216) +#define CALL_DIST_MAX_POS (+16777214) + +#define JCC_DIST_SMALL_MAX_NEG (-256) +#define JCC_DIST_SMALL_MAX_POS (+254) + +#define JCC_DIST_MEDIUM_MAX_NEG (-1048576) +#define JCC_DIST_MEDIUM_MAX_POS (+1048574) + +#define LBL_SIZE_SMALL (2) + +#define JMP_SIZE_SMALL (2) +#define JMP_SIZE_LARGE (4) + +#define JCC_SIZE_SMALL (2) +#define JCC_SIZE_MEDIUM (4) +#define JCC_SIZE_LARGE (6) + +// The first thing in an ARM32 prolog pushes LR to the stack, so this can be 0. +#define STACK_PROBE_BOUNDARY_THRESHOLD_BYTES 0 + +#define REG_STACK_PROBE_HELPER_ARG REG_R4 +#define RBM_STACK_PROBE_HELPER_ARG RBM_R4 +#define REG_STACK_PROBE_HELPER_CALL_TARGET REG_R5 +#define RBM_STACK_PROBE_HELPER_CALL_TARGET RBM_R5 +#define RBM_STACK_PROBE_HELPER_TRASH (RBM_R5 | RBM_LR) + #else #error Unsupported or unset target architecture #endif @@ -1579,9 +2351,11 @@ typedef unsigned char regNumberSmall; #endif // TARGET_XARCH +#if !defined(TARGET_WASM32) && !defined(TARGET_WASM64) // has no registers C_ASSERT(REG_FIRST == 0); C_ASSERT(REG_INT_FIRST < REG_INT_LAST); C_ASSERT(REG_FP_FIRST < REG_FP_LAST); +#endif // Opportunistic tail call feature converts non-tail prefixed calls into // tail calls where possible. It requires fast tail calling mechanism for @@ -1654,8 +2428,12 @@ inline regMaskTP genRegMaskFloat(regNumber reg, var_types type = TYP_DOUBLE); */ inline bool genIsValidReg(regNumber reg) { +#if defined(TARGET_WASM) // infinite "registers" + return true; +#else /* It's safest to perform an unsigned comparison in case reg is negative */ return ((unsigned)reg < (unsigned)REG_COUNT); +#endif } /***************************************************************************** @@ -1765,7 +2543,11 @@ inline regMaskTP fullIntArgRegMask() // inline bool isValidIntArgReg(regNumber reg) { +#if defined(TARGET_WASM) + return true; +#else return (genRegMask(reg) & fullIntArgRegMask()) != 0; +#endif } //------------------------------------------------------------------------------------------- @@ -1842,6 +2624,10 @@ inline regMaskTP genRegMask(regNumber reg) regMaskTP result = 1 << reg; assert(result == regMasks[reg]); return result; +#elif defined(TARGET_WASM) + regMaskTP result = 1 << reg; + assert(result == regMasks[reg]); + return result; #else return regMasks[reg]; #endif @@ -1854,7 +2640,7 @@ inline regMaskTP genRegMask(regNumber reg) inline regMaskTP genRegMaskFloat(regNumber reg, var_types type /* = TYP_DOUBLE */) { -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_X86) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_X86) || defined(TARGET_WASM) assert(genIsValidFloatReg(reg)); assert((unsigned)reg < ArrLen(regMasks)); return regMasks[reg]; @@ -1979,11 +2765,12 @@ inline bool isFloatRegType(var_types type) // If the WINDOWS_AMD64_ABI is defined make sure that TARGET_AMD64 is also defined. #if defined(WINDOWS_AMD64_ABI) -#if !defined(TARGET_AMD64) +#if !defined(TARGET_AMD64) && !defined(TARGET_WASM32) && !defined(TARGET_WASM64) #error When WINDOWS_AMD64_ABI is defined you must define TARGET_AMD64 defined as well. #endif #endif +#if !defined(TARGET_WASM32) && !defined(TARGET_WASM64) /*****************************************************************************/ // Some sanity checks on some of the register masks // Stack pointer is never part of RBM_ALLINT @@ -1996,6 +2783,7 @@ C_ASSERT((RBM_ALLINT & RBM_FPBASE) == RBM_NONE); C_ASSERT((RBM_INT_CALLEE_SAVED & RBM_FPBASE) == RBM_NONE); #endif /*****************************************************************************/ +#endif #ifdef TARGET_64BIT typedef unsigned __int64 target_size_t; diff --git a/src/coreclr/jit/targetwasm.cpp b/src/coreclr/jit/targetwasm.cpp new file mode 100644 index 000000000000..501a480b2edd --- /dev/null +++ b/src/coreclr/jit/targetwasm.cpp @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/*****************************************************************************/ + +#include "jitpch.h" +#ifdef _MSC_VER +#pragma hdrstop +#endif + +#if defined(TARGET_WASM) + +#include "target.h" + +const char* Target::g_tgtCPUName = "wasm"; +const Target::ArgOrder Target::g_tgtArgOrder = ARG_ORDER_R2L; +const Target::ArgOrder Target::g_tgtUnmanagedArgOrder = ARG_ORDER_R2L; + +// clang-format off +#ifdef UNIX_AMD64_ABI +const regNumber intArgRegs [] = { REG_EDI, REG_ESI, REG_EDX, REG_ECX, REG_R8, REG_R9 }; +const regMaskTP intArgMasks[] = { RBM_EDI, RBM_ESI, RBM_EDX, RBM_ECX, RBM_R8, RBM_R9 }; +const regNumber fltArgRegs [] = { REG_XMM0, REG_XMM1, REG_XMM2, REG_XMM3, REG_XMM4, REG_XMM5, REG_XMM6, REG_XMM7 }; +const regMaskTP fltArgMasks[] = { RBM_XMM0, RBM_XMM1, RBM_XMM2, RBM_XMM3, RBM_XMM4, RBM_XMM5, RBM_XMM6, RBM_XMM7 }; +#else // !UNIX_AMD64_ABI +const regNumber intArgRegs [] = { REG_ECX, REG_EDX, REG_R8, REG_R9 }; +const regMaskTP intArgMasks[] = { RBM_ECX, RBM_EDX, RBM_R8, RBM_R9 }; +const regNumber fltArgRegs [] = { REG_XMM0, REG_XMM1, REG_XMM2, REG_XMM3 }; +const regMaskTP fltArgMasks[] = { RBM_XMM0, RBM_XMM1, RBM_XMM2, RBM_XMM3 }; +#endif // !UNIX_AMD64_ABI +// clang-format on + +#endif // TARGET_AMD64 diff --git a/src/coreclr/jit/treelifeupdater.cpp b/src/coreclr/jit/treelifeupdater.cpp index d81ce887218d..a170a8eb53f3 100644 --- a/src/coreclr/jit/treelifeupdater.cpp +++ b/src/coreclr/jit/treelifeupdater.cpp @@ -56,6 +56,7 @@ bool TreeLifeUpdater::UpdateLifeFieldVar(GenTreeLclVar* lclNode, uns if (isBorn || isDying) { +#ifndef TARGET_WASM if (ForCodeGen) { regNumber reg = lclNode->GetRegNumByIdx(multiRegIndex); @@ -70,6 +71,8 @@ bool TreeLifeUpdater::UpdateLifeFieldVar(GenTreeLclVar* lclNode, uns compiler->codeGen->genUpdateRegLife(fldVarDsc, isBorn, isDying DEBUGARG(lclNode)); } } +#endif // !TARGET_WASM + // First, update the live set if (isDying) { @@ -96,6 +99,7 @@ bool TreeLifeUpdater::UpdateLifeFieldVar(GenTreeLclVar* lclNode, uns VarSetOps::Assign(compiler, compiler->compCurLife, newLife); +#ifndef TARGET_WASM if (ForCodeGen) { // Only add vars to the gcInfo.gcVarPtrSetCur if they are currently on stack, since the @@ -122,6 +126,7 @@ bool TreeLifeUpdater::UpdateLifeFieldVar(GenTreeLclVar* lclNode, uns VarSetOps::RemoveElemD(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur, fldVarIndex); } +#ifndef TARGET_WASM #ifdef DEBUG if (compiler->verbose) { @@ -129,6 +134,7 @@ bool TreeLifeUpdater::UpdateLifeFieldVar(GenTreeLclVar* lclNode, uns printf("\n"); } #endif // DEBUG +#endif // !TARGET_WASM } #ifdef USING_VARIABLE_LIVE_RANGE @@ -141,8 +147,10 @@ bool TreeLifeUpdater::UpdateLifeFieldVar(GenTreeLclVar* lclNode, uns compiler->codeGen->siUpdate(); #endif // USING_SCOPE_INFO } +#endif // !TARGET_WASM } +#ifndef TARGET_WASM if (ForCodeGen && spill) { if (VarSetOps::IsMember(compiler, compiler->codeGen->gcInfo.gcTrkStkPtrLcls, fldVarIndex)) @@ -160,6 +168,7 @@ bool TreeLifeUpdater::UpdateLifeFieldVar(GenTreeLclVar* lclNode, uns } return true; } +#endif // !TARGET_WASM return false; } @@ -252,6 +261,7 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree) if (varDsc->lvTracked) { VarSetOps::AddElemD(compiler, varDeltaSet, varDsc->lvVarIndex); +#ifndef TARGET_WASM if (ForCodeGen) { if (isBorn && varDsc->lvIsRegCandidate() && tree->gtHasReg()) @@ -269,7 +279,9 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree) VarSetOps::AddElemD(compiler, stackVarDeltaSet, varDsc->lvVarIndex); } } +#endif // !TARGET_WASM } +#ifndef TARGET_WASM else if (ForCodeGen && lclVarTree->IsMultiRegLclVar()) { assert(varDsc->lvPromoted && compiler->lvaEnregMultiRegVars); @@ -306,6 +318,7 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree) } spill = false; } +#endif // !TARGET_WASM else if (varDsc->lvPromoted) { // If hasDeadTrackedFieldVars is true, then, for a LDOBJ(ADDR()), @@ -392,6 +405,7 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree) VarSetOps::Assign(compiler, compiler->compCurLife, newLife); +#ifndef TARGET_WASM if (ForCodeGen) { // Only add vars to the gcInfo.gcVarPtrSetCur if they are currently on stack, since the @@ -437,8 +451,10 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree) compiler->codeGen->siUpdate(); #endif // USING_SCOPE_INFO } +#endif // !TARGET_WASM } +#ifndef TARGET_WASM if (ForCodeGen && spill) { assert(!varDsc->lvPromoted); @@ -457,6 +473,7 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree) } } } +#endif // !TARGET_WASM } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/typelist.h b/src/coreclr/jit/typelist.h index 5f129106fcaf..21032081d5f2 100644 --- a/src/coreclr/jit/typelist.h +++ b/src/coreclr/jit/typelist.h @@ -3,7 +3,7 @@ #define GCS EA_GCREF #define BRS EA_BYREF -#define PS EA_PTRSIZE +#define PS TARGET_POINTER_SIZE #define PST (TARGET_POINTER_SIZE / sizeof(int)) #ifdef TARGET_64BIT diff --git a/src/coreclr/jit/unwind.cpp b/src/coreclr/jit/unwind.cpp index 7bbccd789d6c..f8ace4acfd40 100644 --- a/src/coreclr/jit/unwind.cpp +++ b/src/coreclr/jit/unwind.cpp @@ -15,6 +15,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #pragma hdrstop #endif +#ifndef TARGET_WASM #if defined(FEATURE_EH_FUNCLETS) //------------------------------------------------------------------------ @@ -116,6 +117,7 @@ void Compiler::unwindGetFuncLocations(FuncInfoDsc* func, } #endif // FEATURE_EH_FUNCLETS +#endif // !TARGET_WASM #if defined(TARGET_UNIX) @@ -378,6 +380,7 @@ void Compiler::DumpCfiInfo(bool isHotCode, #endif // TARGET_UNIX +#ifndef TARGET_WASM //------------------------------------------------------------------------ // Compiler::unwindGetCurrentOffset: Calculate the current byte offset of the // prolog being generated. @@ -408,6 +411,7 @@ UNATIVE_OFFSET Compiler::unwindGetCurrentOffset(FuncInfoDsc* func) return offset; } +#endif // !TARGET_WASM #if defined(TARGET_AMD64) @@ -425,6 +429,8 @@ UNATIVE_OFFSET Compiler::unwindGetCurrentOffset(FuncInfoDsc* func) // See unwindX86.cpp +#elif defined(TARGET_WASM) // TODO + #else // TARGET* #error Unsupported or unset target architecture diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp index c973f6f63c87..768611b2dda5 100644 --- a/src/coreclr/jit/utils.cpp +++ b/src/coreclr/jit/utils.cpp @@ -333,8 +333,16 @@ void dspRegMask(regMaskTP regMask, size_t minSiz) inRegRange = true; sep = "-"; } -#elif defined(TARGET_X86) +#elif defined(TARGET_X86) // No register ranges +#elif defined(TARGET_WASM) // TODO Wasm + // For AMD64, create ranges for int registers R8 through R15, but not the "old" registers. + if (regNum >= REG_R8) + { + regHead = regNum; + inRegRange = true; + sep = "-"; + } #else // TARGET* #error Unsupported or unset target architecture #endif // TARGET* diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 11825b08952d..eb2323cdad65 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -57,6 +57,8 @@ struct FloatTraits unsigned bits = 0xFFC00000u; #elif defined(TARGET_ARMARCH) unsigned bits = 0x7FC00000u; +#elif defined(TARGET_WASM) + unsigned bits = 0x7FC00000u; #else #error Unsupported or unset target architecture #endif @@ -83,6 +85,8 @@ struct DoubleTraits unsigned long long bits = 0xFFF8000000000000ull; #elif defined(TARGET_ARMARCH) unsigned long long bits = 0x7FF8000000000000ull; +#elif defined(TARGET_WASM) + unsigned long long bits = 0xFFF8000000000000ull; #else #error Unsupported or unset target architecture #endif diff --git a/src/coreclr/jit/valuenumfuncs.h b/src/coreclr/jit/valuenumfuncs.h index 1d3906d92dde..d5941877a3d4 100644 --- a/src/coreclr/jit/valuenumfuncs.h +++ b/src/coreclr/jit/valuenumfuncs.h @@ -184,6 +184,8 @@ ValueNumFuncDef(HWI_##isa##_##name, argCount, false, false, false) // All of t #elif defined (TARGET_ARM) // No Hardware Intrinsics on ARM32 +#elif defined(TARGET_WASM) +// No Hardware Intrinsics on WebAssembly #else #error Unsupported platform #endif diff --git a/src/coreclr/jit/wasm.h b/src/coreclr/jit/wasm.h new file mode 100644 index 000000000000..dfee7df9877d --- /dev/null +++ b/src/coreclr/jit/wasm.h @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef WASM_H_ +#define WASM_H_ + +#ifdef TARGET_WASM +#endif + +#endif // WASM_H_ diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets index 06f5914eff20..0720e6838824 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets @@ -75,8 +75,11 @@ The .NET Foundation licenses this file to you under the MIT license. .exports $(NativeIntermediateOutputPath)$(TargetName)$(LlvmObjectExt) + + $(NativeIntermediateOutputPath)$(TargetName)clrjit$(LlvmObjectExt) $(NativeIntermediateOutputPath)$(TargetName)$(NativeObjectExt) + $(NativeIntermediateOutputPath)$(TargetName)clrjit$(NativeObjectExt) $(NativeOutputPath)$(TargetName)$(NativeBinaryExt) $(NativeIntermediateOutputPath)$(TargetName)$(ExportsFileExt) @@ -85,6 +88,9 @@ The .NET Foundation licenses this file to you under the MIT license. IlcCompile WasmObject + LinkNativeSingle + LinkNativeLlvm + $(NativeOutputPath) $(NativeIntermediateOutputPath) @@ -291,8 +297,8 @@ The .NET Foundation licenses this file to you under the MIT license. @@ -306,11 +312,22 @@ The .NET Foundation licenses this file to you under the MIT license. + + + + "$(LlvmClrjitObject)" -c -o "$(NativeClrjitObject)" -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s DISABLE_EXCEPTION_CATCHING=0 -s ABORTING_MALLOC=0 + $(EmccArgs) -O2 + $(EmccArgs) -g3 + + + + + - @@ -339,20 +356,30 @@ The .NET Foundation licenses this file to you under the MIT license. - - + + - + - + + + + + + + + - "$(NativeObject)" -o "$(NativeBinary)" -s ALLOW_MEMORY_GROWTH=1 -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s DISABLE_EXCEPTION_CATCHING=0 --emrun + "$(NativeObject)" "$(NativeClrjitObject)" -o "$(NativeBinary)" -s ALLOW_MEMORY_GROWTH=1 -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s DISABLE_EXCEPTION_CATCHING=0 --emrun $(EmccArgs) "$(IlcPath)/sdk/libPortableRuntime.a" "$(IlcPath)/sdk/libbootstrapper.a" "$(IlcPath)/framework/libSystem.Native.a" "$(IlcPath)/framework/libSystem.Globalization.Native.a" $(EmccExtraArgs) $(EmccArgs) -O2 -flto $(EmccArgs) -g3 @@ -363,6 +390,10 @@ The .NET Foundation licenses this file to you under the MIT license. + + + + + + + $(CMakeArgs) -DBUILD_WASM_JIT=1 + -DBUILD_WASM_JIT=1 + + + <_CoreClrBuildArg Remove="@(_CoreClrBuildArg)"/> + + <_CoreClrBuildArg Include="-x64" /> + <_CoreClrBuildArg Condition="!$([MSBuild]::IsOsPlatform(Windows)) and '$(CMakeArgs)' != ''" Include="$(CMakeArgs)" /> + <_CoreClrBuildArg Condition="$([MSBuild]::IsOsPlatform(Windows)) and '$(CMakeArgs)' != ''" Include="-cmakeargs "$(CMakeArgs)"" /> + <_CoreClrBuildArg Include="-$(Configuration.ToLower())" /> + <_CoreClrBuildArg Include="$(Compiler)" /> + <_CoreClrBuildArg Condition="'$(ContinuousIntegrationBuild)' == 'true'" Include="-ci" /> + <_CoreClrBuildArg Condition="'$(CrossBuild)' == 'true'" Include="-cross" /> + <_CoreClrBuildArg Condition="'$(PortableBuild)' != 'true'" Include="-portablebuild=false" /> + <_CoreClrBuildArg Condition="'$(KeepNativeSymbols)' != 'false'" Include="-keepnativesymbols" /> + <_CoreClrBuildArg Condition="!$([MSBuild]::IsOsPlatform(Windows))" Include="-os $(TargetOS)" /> + + <_CoreClrBuildArg Condition="$([MSBuild]::IsOsPlatform(Windows)) and + ('$(TargetArchitecture)' == 'x86' or '$(TargetArchitecture)' == 'x64') and + '$(Configuration)' == 'Release' and + '$(ClrRuntimeSubset)' == 'true' and + '$(NoPgoOptimize)' != 'true' and + '$(PgoInstrument)' != 'true'" + Include="-enforcepgo" /> + <_CoreClrBuildArg Condition="$([MSBuild]::IsOsPlatform(Windows)) and '$(CrossDac)' != ''" Include="-$(CrossDac)dac" /> + <_CoreClrBuildArg Condition="'$(Ninja)' == 'true'" Include="-ninja" /> + <_CoreClrBuildArg Condition="'$(ClrRuntimeSubset)' != 'true'" Include="-skipruntime" /> + <_CoreClrBuildArg Condition="'$(ClrJitSubset)' != 'true'" Include="-skipjit" /> + <_CoreClrBuildArg Condition="'$(ClrPalTestsSubset)' == 'true'" Include="-paltests" /> + <_CoreClrBuildArg Condition="'$(ClrAllJitsSubset)' != 'true'" Include="-skipalljits" /> + <_CoreClrBuildArg Condition="'$(PgoInstrument)' == 'true'" Include="-pgoinstrument" /> + <_CoreClrBuildArg Condition="'$(NoPgoOptimize)' == 'true' or '$(PgoInstrument)' == 'true'" Include="-nopgooptimize" /> + <_CoreClrBuildArg Condition="'$(OfficialBuildId)' != ''" Include="/p:OfficialBuildId=$(OfficialBuildId)" /> + + + + <_CoreClrBuildScript Condition="$([MSBuild]::IsOsPlatform(Windows))">build-runtime.cmd + <_CoreClrBuildScript Condition="!$([MSBuild]::IsOsPlatform(Windows))">build-runtime.sh + + + + + + + + + + + + diff --git a/src/coreclr/tools/Common/Compiler/InstructionSetSupport.cs b/src/coreclr/tools/Common/Compiler/InstructionSetSupport.cs index 5a8e04f0d4a3..2e7b60f2978b 100644 --- a/src/coreclr/tools/Common/Compiler/InstructionSetSupport.cs +++ b/src/coreclr/tools/Common/Compiler/InstructionSetSupport.cs @@ -81,6 +81,10 @@ public static string GetHardwareIntrinsicId(TargetArchitecture architecture, Typ if (potentialType.Namespace != "System.Runtime.Intrinsics.Arm") return ""; } + else if (architecture == TargetArchitecture.Wasm32 || architecture == TargetArchitecture.Wasm64) + { + return ""; // No "hardware" for Wasm at all. + } else { throw new InternalCompilerErrorException("Unknown architecture"); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.Intrinsics.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.Intrinsics.cs index bd6aaca2d7f4..7797c26b372c 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.Intrinsics.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.Intrinsics.cs @@ -8,7 +8,7 @@ namespace Internal.JitInterface { - internal unsafe partial class CorInfoImpl + public unsafe partial class CorInfoImpl { private struct IntrinsicKey { diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 39a2571102ae..b16b89b1f24d 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -33,7 +33,7 @@ namespace Internal.JitInterface { - internal unsafe sealed partial class CorInfoImpl + public unsafe sealed partial class CorInfoImpl { // // Global initialization and state @@ -45,6 +45,8 @@ private enum ImageFileMachine AMD64 = 0x8664, ARM = 0x01c4, ARM64 = 0xaa64, + WASM32 = 0xffff, // matches llvm.h - TODO better to just #if out this check in compiler.cpp? + WASM64 = 0xfffe, } private enum CFI_OPCODE { @@ -71,6 +73,9 @@ private enum CFI_OPCODE [DllImport(JitLibrary)] private extern static IntPtr jitStartup(IntPtr host); + [DllImport(JitLibrary)] + private extern static void jitShutdown([MarshalAs(UnmanagedType.I1)] bool processIsTerminating); + [DllImport(JitLibrary)] private extern static IntPtr getJit(); @@ -88,7 +93,7 @@ private static CorInfoImpl GetThis(IntPtr thisHandle) } [DllImport(JitSupportLibrary)] - private extern static CorJitResult JitCompileMethod(out IntPtr exception, + internal extern static CorJitResult JitCompileMethod(out IntPtr exception, IntPtr jit, IntPtr thisHandle, IntPtr callbacks, ref CORINFO_METHOD_INFO info, uint flags, out IntPtr nativeEntry, out uint codeSize); @@ -123,6 +128,11 @@ public static void Startup() jitStartup(GetJitHost(JitConfigProvider.Instance.UnmanagedInstance)); } + public static void Shutdown() + { + jitShutdown(true); + } + public CorInfoImpl() { _jit = getJit(); @@ -3284,7 +3294,7 @@ private static RelocType GetRelocType(TargetArchitecture targetArchitecture, ush default: Debug.Fail("Invalid RelocType: " + fRelocType); return 0; - }; + } } private void recordRelocation(void* location, void* target, ushort fRelocType, ushort slotNum, int addlDelta) @@ -3379,6 +3389,10 @@ private uint getExpectedTargetArchitecture() return (uint)ImageFileMachine.ARM; case TargetArchitecture.ARM64: return (uint)ImageFileMachine.ARM64; + case TargetArchitecture.Wasm32: + return (uint)ImageFileMachine.WASM32; + case TargetArchitecture.Wasm64: + return (uint)ImageFileMachine.WASM64; default: throw new NotImplementedException("Expected target architecture is not supported"); } @@ -3469,7 +3483,6 @@ private uint getJitFlags(ref CORJIT_FLAGS flags, uint sizeInBytes) return (uint)sizeof(CORJIT_FLAGS); } - #if READYTORUN InstructionSetFlags _actualInstructionSetSupported; InstructionSetFlags _actualInstructionSetUnsupported; diff --git a/src/coreclr/tools/Common/JitInterface/JitConfigProvider.cs b/src/coreclr/tools/Common/JitInterface/JitConfigProvider.cs index b9a806007828..3b706364f6a7 100644 --- a/src/coreclr/tools/Common/JitInterface/JitConfigProvider.cs +++ b/src/coreclr/tools/Common/JitInterface/JitConfigProvider.cs @@ -131,13 +131,15 @@ public string GetStringConfigValue(string name) private static string GetTargetSpec(TargetDetails target) { - string targetOSComponent = (target.OperatingSystem == TargetOS.Windows ? "win" : "unix"); + string targetOSComponent = (target.OperatingSystem == TargetOS.Windows ? "win" : (target.OperatingSystem == TargetOS.WebAssembly ? "browser" : "unix")); string targetArchComponent = target.Architecture switch { TargetArchitecture.X86 => "x86", TargetArchitecture.X64 => "x64", TargetArchitecture.ARM => "arm", TargetArchitecture.ARM64 => "arm64", + TargetArchitecture.Wasm32 => "wasm32", + TargetArchitecture.Wasm64 => "wasm64", _ => throw new NotImplementedException(target.Architecture.ToString()) }; diff --git a/src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs b/src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs index b3b587d1432f..f942cf695362 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/TargetArchitecture.cs @@ -17,5 +17,6 @@ public enum TargetArchitecture X64, X86, Wasm32, + Wasm64 } } diff --git a/src/coreclr/tools/aot/ILCompiler.LLVM/CodeGen/ILToLLVMImporter.cs b/src/coreclr/tools/aot/ILCompiler.LLVM/CodeGen/ILToLLVMImporter.cs index 9064086a609f..7e50166e1a2e 100644 --- a/src/coreclr/tools/aot/ILCompiler.LLVM/CodeGen/ILToLLVMImporter.cs +++ b/src/coreclr/tools/aot/ILCompiler.LLVM/CodeGen/ILToLLVMImporter.cs @@ -7,6 +7,8 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Internal.TypeSystem; using ILCompiler; using LLVMSharp.Interop; @@ -278,7 +280,7 @@ private void GenerateProlog() // Keep track of where we are in the llvm signature, starting after the // shadow stack pointer and return address int signatureIndex = 1; - if (NeedsReturnStackSlot(_signature)) + if (LLVMCodegenCompilation.NeedsReturnStackSlot(_signature)) { signatureIndex++; } @@ -440,7 +442,7 @@ private void GenerateProlog() private LLVMValueRef CreateLLVMFunction(string mangledName, MethodSignature signature, bool hasHiddenParameter) { - return Module.AddFunction(mangledName, GetLLVMSignatureForMethod(signature, hasHiddenParameter)); + return Module.AddFunction(mangledName, LLVMCodegenCompilation.GetLLVMSignatureForMethod(signature, hasHiddenParameter)); } private LLVMValueRef GetOrCreateLLVMFunction(string mangledName, MethodSignature signature, bool hasHiddenParam) @@ -764,8 +766,11 @@ LLVMMetadataRef CreateDebugFunctionAndDiLocation(DebugMetadata debugMetadata, IL if (_debugFunction.Handle == IntPtr.Zero) { LLVMMetadataRef functionMetaType = _compilation.DIBuilder.CreateSubroutineType(debugMetadata.File, - ReadOnlySpan.Empty, LLVMDIFlags.LLVMDIFlagZero); + ReadOnlySpan.Empty /* TODO */, LLVMDIFlags.LLVMDIFlagZero); + if (_method.Name == "Resize") + { + } uint lineNumber = (uint) _debugInformation.GetSequencePoints().FirstOrDefault().LineNumber; _debugFunction = _compilation.DIBuilder.CreateFunction(debugMetadata.File, _method.Name, _method.Name, debugMetadata.File, @@ -1580,7 +1585,7 @@ private bool CanStoreVariableOnStack(TypeDesc variableType) /// Returns true if the type can be stored on the local stack /// instead of the shadow stack in this method. /// - private static bool CanStoreTypeOnStack(TypeDesc type) + internal static bool CanStoreTypeOnStack(TypeDesc type) { if (type is DefType defType) { @@ -1597,15 +1602,6 @@ private static bool CanStoreTypeOnStack(TypeDesc type) return false; } - /// - /// Returns true if the method returns a type that must be kept - /// on the shadow stack - /// - private static bool NeedsReturnStackSlot(MethodSignature signature) - { - return !signature.ReturnType.IsVoid && !CanStoreTypeOnStack(signature.ReturnType); - } - private int GetTotalParameterOffset() { int offset = 0; @@ -1849,7 +1845,7 @@ private void ImportReturn() LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(_signature.ReturnType); LLVMValueRef castValue = retVal.ValueAsType(valueType, _builder); - if (NeedsReturnStackSlot(_signature)) + if (LLVMCodegenCompilation.NeedsReturnStackSlot(_signature)) { var retParam = _llvmFunction.GetParam(1); ImportStoreHelper(castValue, valueType, retParam, 0); @@ -2204,7 +2200,7 @@ private LLVMValueRef GetCallableVirtualMethod(LLVMValueRef thisPointer, MethodDe LLVMValueRef slot = GetOrCreateMethodSlot(runtimeDeterminedMethod, callee); - LLVMTypeRef llvmSignature = GetLLVMSignatureForMethod(runtimeDeterminedMethod.Signature, false); + LLVMTypeRef llvmSignature = LLVMCodegenCompilation.GetLLVMSignatureForMethod(runtimeDeterminedMethod.Signature, false); LLVMValueRef functionPtr; ThrowIfNull(thisPointer); if (runtimeDeterminedMethod.OwningType.IsInterface) @@ -2314,51 +2310,10 @@ private LLVMValueRef GetCallableGenericVirtualMethod(StackEntry objectPtr, Metho new LLVMBasicBlockRef[] { fatBranch, notFatBranch }, 2); // dont know the type for sure, but will generate for no hidden dict param and change if necessary before calling. - var asFunc = CastIfNecessary(_builder, loadPtr, LLVMTypeRef.CreatePointer(GetLLVMSignatureForMethod(runtimeDeterminedMethod.Signature, false), 0) , "castToFunc"); + var asFunc = CastIfNecessary(_builder, loadPtr, LLVMTypeRef.CreatePointer(LLVMCodegenCompilation.GetLLVMSignatureForMethod(runtimeDeterminedMethod.Signature, false), 0) , "castToFunc"); return asFunc; } - internal static LLVMTypeRef GetLLVMSignatureForMethod(MethodSignature signature, bool hasHiddenParam) - { - TypeDesc returnType = signature.ReturnType; - LLVMTypeRef llvmReturnType; - bool returnOnStack = false; - if (!NeedsReturnStackSlot(signature)) - { - returnOnStack = true; - llvmReturnType = GetLLVMTypeForTypeDesc(returnType); - } - else - { - llvmReturnType = LLVMTypeRef.Void; - } - - List signatureTypes = new List(); - signatureTypes.Add(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0)); // Shadow stack pointer - - if (!returnOnStack && !signature.ReturnType.IsVoid) - { - signatureTypes.Add(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0)); - } - - if (hasHiddenParam) - { - signatureTypes.Add(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0)); // *EEType - } - - // Intentionally skipping the 'this' pointer since it could always be a GC reference - // and thus must be on the shadow stack - foreach (TypeDesc type in signature) - { - if (CanStoreTypeOnStack(type)) - { - signatureTypes.Add(GetLLVMTypeForTypeDesc(type)); - } - } - - return LLVMTypeRef.CreateFunction(llvmReturnType, signatureTypes.ToArray(), false); - } - private ExpressionEntry AllocateObject(StackEntry eeType, TypeDesc forcedReturnType = null) { //TODO: call GetNewObjectHelperForType from JitHelper.cs (needs refactoring) @@ -2698,7 +2653,7 @@ private bool ImportIntrinsicCall(MethodDesc method, MethodDesc runtimeDetermined TypeDesc returnType = signature.ReturnType; - bool needsReturnSlot = NeedsReturnStackSlot(signature); + bool needsReturnSlot = LLVMCodegenCompilation.NeedsReturnStackSlot(signature); SpilledExpressionEntry returnSlot = null; var actualReturnType = forcedReturnType ?? returnType; if (needsReturnSlot) @@ -2758,7 +2713,7 @@ private bool ImportIntrinsicCall(MethodDesc method, MethodDesc runtimeDetermined { if (_isUnboxingThunk && _method.RequiresInstArg()) { - hiddenParam = _currentFunclet.GetParam((uint)(1 + (NeedsReturnStackSlot(_signature) ? 1 : 0))); + hiddenParam = _currentFunclet.GetParam((uint)(1 + (LLVMCodegenCompilation.NeedsReturnStackSlot(_signature) ? 1 : 0))); } else if (canonMethod.RequiresInstMethodDescArg()) { @@ -2848,7 +2803,7 @@ private bool ImportIntrinsicCall(MethodDesc method, MethodDesc runtimeDetermined // else builder.PositionAtEnd(fatBranch); - var fnWithDict = builder.BuildCast(LLVMOpcode.LLVMBitCast, fn, LLVMTypeRef.CreatePointer(GetLLVMSignatureForMethod(runtimeDeterminedMethod.Signature, true), 0), "fnWithDict"); + var fnWithDict = builder.BuildCast(LLVMOpcode.LLVMBitCast, fn, LLVMTypeRef.CreatePointer(LLVMCodegenCompilation.GetLLVMSignatureForMethod(runtimeDeterminedMethod.Signature, true), 0), "fnWithDict"); var dictDereffed = builder.BuildLoad(builder.BuildLoad( dict, "l1"), "l2"); llvmArgs.Insert(needsReturnSlot ? 2 : 1, dictDereffed); LLVMValueRef fatReturn = CallOrInvoke(fromLandingPad, builder, currentTryRegion, fnWithDict, llvmArgs.ToArray(), ref nextInstrBlock); @@ -3338,7 +3293,7 @@ private void EmitNativeToManagedThunk(LLVMCodegenCompilation compilation, Method List llvmArgs = new List(); llvmArgs.Add(calleeFrame); - bool needsReturnSlot = NeedsReturnStackSlot(method.Signature); + bool needsReturnSlot = LLVMCodegenCompilation.NeedsReturnStackSlot(method.Signature); if (needsReturnSlot) { @@ -3392,8 +3347,8 @@ private void ImportCalli(int token) { MethodSignature methodSignature = (MethodSignature)_canonMethodIL.GetObject(token); - var noHiddenParamSig = GetLLVMSignatureForMethod(methodSignature, false); - var hddenParamSig = GetLLVMSignatureForMethod(methodSignature, true); + var noHiddenParamSig = LLVMCodegenCompilation.GetLLVMSignatureForMethod(methodSignature, false); + var hddenParamSig = LLVMCodegenCompilation.GetLLVMSignatureForMethod(methodSignature, true); var target = ((ExpressionEntry)_stack.Pop()).ValueAsType(LLVMTypeRef.CreatePointer(noHiddenParamSig, 0), _builder); var functionPtrAsInt = _builder.BuildPtrToInt(target, LLVMTypeRef.Int32, "ptrToInt"); @@ -5179,7 +5134,7 @@ LLVMValueRef GetGenericContext() uint GetHiddenContextParamNo() { - return 1 + (NeedsReturnStackSlot(_method.Signature) ? (uint)1 : 0); + return 1 + (LLVMCodegenCompilation.NeedsReturnStackSlot(_method.Signature) ? (uint)1 : 0); } bool FuncletsRequireHiddenContext() @@ -5405,7 +5360,7 @@ private TypeDesc ResolveTypeToken(int token) private TypeDesc GetWellKnownType(WellKnownType wellKnownType) { - return _compilation.TypeSystemContext.GetWellKnownType(wellKnownType); + return _compilation.GetWellKnownType(wellKnownType); } private void ReportInvalidBranchTarget(int targetOffset) diff --git a/src/coreclr/tools/aot/ILCompiler.LLVM/CodeGen/LLVMObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.LLVM/CodeGen/LLVMObjectWriter.cs index e2b7aa5c5a1b..97c4ba213b8a 100644 --- a/src/coreclr/tools/aot/ILCompiler.LLVM/CodeGen/LLVMObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.LLVM/CodeGen/LLVMObjectWriter.cs @@ -1034,7 +1034,7 @@ private void GetCodeForTentativeMethod(LLVMCodegenCompilation compilation, Tenta { LLVMBuilderRef builder = compilation.Module.Context.CreateBuilder(); MethodDesc method = node.Method; - LLVMValueRef tentativeStub = Module.AddFunction(node.GetMangledName(factory.NameMangler), ILImporter.GetLLVMSignatureForMethod(method.Signature, method.RequiresInstArg())); + LLVMValueRef tentativeStub = Module.AddFunction(node.GetMangledName(factory.NameMangler), LLVMCodegenCompilation.GetLLVMSignatureForMethod(method.Signature, method.RequiresInstArg())); LLVMBasicBlockRef block = tentativeStub.AppendBasicBlock("tentativeStub"); builder.PositionAtEnd(block); MethodDesc helperMethod = factory.TypeSystemContext.GetOptionalHelperEntryPoint("ThrowHelpers", "ThrowBodyRemoved"); diff --git a/src/coreclr/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMCodegenNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMCodegenNodeFactory.cs index 1edb484f81b0..20ff4e3b8933 100644 --- a/src/coreclr/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMCodegenNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMCodegenNodeFactory.cs @@ -83,7 +83,7 @@ protected override IMethodNode CreateUnboxingStubNode(MethodDesc method) protected override ISymbolNode CreateReadyToRunHelperNode(ReadyToRunHelperKey helperCall) { - throw new NotSupportedException(); + return new ReadyToRunHelperNode(helperCall.HelperId, helperCall.Target); } protected override ISymbolNode CreateGenericLookupFromDictionaryNode(ReadyToRunGenericHelperKey helperKey) diff --git a/src/coreclr/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMMethodCodeNode.cs b/src/coreclr/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMMethodCodeNode.cs index c0972f02b745..05843dfdc431 100644 --- a/src/coreclr/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMMethodCodeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMMethodCodeNode.cs @@ -10,8 +10,10 @@ namespace ILCompiler.DependencyAnalysis { - internal abstract class LLVMMethodCodeNode : DependencyNodeCore + internal abstract class LLVMMethodCodeNode : DependencyNodeCore, IMethodCodeNode { + private bool _isStateMachineMoveNextMethod; + protected readonly MethodDesc _method; protected DependencyList _dependencies; @@ -52,6 +54,49 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + public int ClassCode { get; } + + public int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_method, ((LLVMMethodCodeNode)other)._method); + } + + public void SetCode(ObjectNode.ObjectData data, bool isFoldable) + { + } + + public void InitializeFrameInfos(FrameInfo[] frameInfos) + { + } + + public void InitializeDebugEHClauseInfos(DebugEHClauseInfo[] debugEhClauseInfos) + { + } + + public void InitializeGCInfo(byte[] gcInfo) + { + } + + public void InitializeEHInfo(ObjectNode.ObjectData ehInfo) + { + } + + public void InitializeDebugLocInfos(DebugLocInfo[] debugLocInfos) + { + } + + public void InitializeDebugVarInfos(DebugVarInfo[] debugVarInfos) + { + } + + public void InitializeNonRelocationDependencies(DependencyList additionalDependencies) + { + } + + public void InitializeIsStateMachineMoveNextMethod(bool value) + { + _isStateMachineMoveNextMethod = value; + } } internal class LlvmMethodBodyNode : LLVMMethodCodeNode, IMethodBodyNode diff --git a/src/coreclr/tools/aot/ILCompiler.LLVM/Compiler/LLVMCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.LLVM/Compiler/LLVMCodegenCompilation.cs index f62105255cec..7b62d6c7560c 100644 --- a/src/coreclr/tools/aot/ILCompiler.LLVM/Compiler/LLVMCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.LLVM/Compiler/LLVMCodegenCompilation.cs @@ -1,8 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; - +using System.Runtime.CompilerServices; +using System.Threading; using Internal.TypeSystem; using Internal.IL; @@ -10,27 +12,33 @@ using ILCompiler.DependencyAnalysisFramework; using LLVMSharp.Interop; using ILCompiler.LLVM; +using Internal.JitInterface; +using Internal.IL.Stubs; namespace ILCompiler { - public sealed class LLVMCodegenCompilation : Compilation + public sealed class LLVMCodegenCompilation : RyuJitCompilation { + private readonly ConditionalWeakTable _corinfos = new ConditionalWeakTable(); + private string _outputFile; + internal LLVMCodegenConfigProvider Options { get; } internal LLVMModuleRef Module { get; } internal LLVMTargetDataRef TargetData { get; } public new LLVMCodegenNodeFactory NodeFactory { get; } internal LLVMDIBuilderRef DIBuilder { get; } internal Dictionary DebugMetadataMap { get; } - internal LLVMCodegenCompilation( - DependencyAnalyzerBase dependencyGraph, + internal LLVMCodegenCompilation(DependencyAnalyzerBase dependencyGraph, LLVMCodegenNodeFactory nodeFactory, IEnumerable roots, ILProvider ilProvider, DebugInformationProvider debugInformationProvider, Logger logger, + LLVMCodegenConfigProvider options, IInliningPolicy inliningPolicy, - LLVMCodegenConfigProvider options) - : base(dependencyGraph, nodeFactory, GetCompilationRoots(roots, nodeFactory), ilProvider, debugInformationProvider, null, inliningPolicy, logger) + DevirtualizationManager devirtualizationManager, + InstructionSetSupport instructionSetSupport) + : base(dependencyGraph, nodeFactory, GetCompilationRoots(roots, nodeFactory), ilProvider, debugInformationProvider, logger, devirtualizationManager, inliningPolicy, instructionSetSupport, 0) { NodeFactory = nodeFactory; LLVMModuleRef m = LLVMModuleRef.CreateWithName(options.ModuleName); @@ -51,15 +59,22 @@ private static IEnumerable GetCompilationRoots(IEnumer protected override void CompileInternal(string outputFile, ObjectDumper dumper) { + _outputFile = outputFile; _dependencyGraph.ComputeMarkedNodes(); var nodes = _dependencyGraph.MarkedNodeList; LLVMObjectWriter.EmitObject(outputFile, nodes, NodeFactory, this, dumper); + + CorInfoImpl.Shutdown(); // writes the LLVM bitcode + + Console.WriteLine($"RyuJIT compilation results, total methods {totalMethodCount} RyuJit Methods {ryuJitMethodCount} % {((decimal)ryuJitMethodCount * 100 / totalMethodCount):n4}"); } protected override void ComputeDependencyNodeDependencies(List> obj) { + // Determine the list of method we actually need to compile + var methodsToCompile = new List(); foreach (var dependency in obj) { var methodCodeNodeNeedingCode = dependency as LLVMMethodCodeNode; @@ -75,8 +90,75 @@ protected override void ComputeDependencyNodeDependencies(List methodsToCompile) + { + CorInfoImpl corInfo = _corinfos.GetValue(Thread.CurrentThread, thread => new CorInfoImpl(this)); + + foreach (LLVMMethodCodeNode methodCodeNodeNeedingCode in methodsToCompile) + { + if (methodCodeNodeNeedingCode.StaticDependenciesAreComputed) + continue; + + if (Logger.IsVerbose) + { + Logger.Writer.WriteLine($"Compiling {methodCodeNodeNeedingCode.Method}..."); + } + + CompileSingleMethod(corInfo, methodCodeNodeNeedingCode); + } + } + + static int totalMethodCount; + static int ryuJitMethodCount; + private unsafe void CompileSingleMethod(CorInfoImpl corInfo, LLVMMethodCodeNode methodCodeNodeNeedingCode) + { + MethodDesc method = methodCodeNodeNeedingCode.Method; + + try + { + var sig = method.Signature; + if (sig.Length == 0 && sig.ReturnType == TypeSystemContext.GetWellKnownType(WellKnownType.Void) && + sig.IsStatic) // speed up + { + corInfo.RegisterLlvmCallbacks((IntPtr)Unsafe.AsPointer(ref corInfo), _outputFile, Module.Target, Module.DataLayout); + corInfo.CompileMethod(methodCodeNodeNeedingCode); + methodCodeNodeNeedingCode.CompilationCompleted = true; + methodCodeNodeNeedingCode.SetDependencies(new DependencyNodeCore.DependencyList()); // TODO: how to track - check RyuJITCompilation + // TODO: delete this external function when old module is gone + LLVMValueRef externFunc = Module.AddFunction(NodeFactory.NameMangler.GetMangledMethodName(method).ToString(), GetLLVMSignatureForMethod(sig, method.RequiresInstArg())); + externFunc.Linkage = LLVMLinkage.LLVMExternalLinkage; + ryuJitMethodCount++; + } + else ILImporter.CompileMethod(this, methodCodeNodeNeedingCode); + } + catch (CodeGenerationFailedException) + { ILImporter.CompileMethod(this, methodCodeNodeNeedingCode); } + catch (TypeSystemException ex) + { + // TODO: fail compilation if a switch was passed + + // Try to compile the method again, but with a throwing method body this time. + MethodIL throwingIL = TypeSystemThrowingILEmitter.EmitIL(method, ex); + corInfo.CompileMethod(methodCodeNodeNeedingCode, throwingIL); + + // TODO: Log as a warning. For now, just log to the logger; but this needs to + // have an error code, be supressible, the method name/sig needs to be properly formatted, etc. + // https://github.com/dotnet/corert/issues/72 + Logger.Writer.WriteLine($"Warning: Method `{method}` will always throw because: {ex.Message}"); + } + finally + { + totalMethodCount++; + // if (_compilationCountdown != null) + // _compilationCountdown.Signal(); + } } public TypeDesc ConvertToCanonFormIfNecessary(TypeDesc type, CanonicalFormKind policy) @@ -100,5 +182,60 @@ public TypeDesc ConvertToCanonFormIfNecessary(TypeDesc type, CanonicalFormKind p return type.ConvertToCanonForm(policy); } + + internal static LLVMTypeRef GetLLVMSignatureForMethod(MethodSignature signature, bool hasHiddenParam) + { + TypeDesc returnType = signature.ReturnType; + LLVMTypeRef llvmReturnType; + bool returnOnStack = false; + if (!NeedsReturnStackSlot(signature)) + { + returnOnStack = true; + llvmReturnType = ILImporter.GetLLVMTypeForTypeDesc(returnType); + } + else + { + llvmReturnType = LLVMTypeRef.Void; + } + + List signatureTypes = new List(); + signatureTypes.Add(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0)); // Shadow stack pointer + + if (!returnOnStack && !signature.ReturnType.IsVoid) + { + signatureTypes.Add(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0)); + } + + if (hasHiddenParam) + { + signatureTypes.Add(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0)); // *EEType + } + + // Intentionally skipping the 'this' pointer since it could always be a GC reference + // and thus must be on the shadow stack + foreach (TypeDesc type in signature) + { + if (ILImporter.CanStoreTypeOnStack(type)) + { + signatureTypes.Add(ILImporter.GetLLVMTypeForTypeDesc(type)); + } + } + + return LLVMTypeRef.CreateFunction(llvmReturnType, signatureTypes.ToArray(), false); + } + + /// + /// Returns true if the method returns a type that must be kept + /// on the shadow stack + /// + internal static bool NeedsReturnStackSlot(MethodSignature signature) + { + return !signature.ReturnType.IsVoid && !ILImporter.CanStoreTypeOnStack(signature.ReturnType); + } + + public TypeDesc GetWellKnownType(WellKnownType wellKnownType) + { + return TypeSystemContext.GetWellKnownType(wellKnownType); + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.LLVM/Compiler/LLVMCodegenCompilationBuilder.cs b/src/coreclr/tools/aot/ILCompiler.LLVM/Compiler/LLVMCodegenCompilationBuilder.cs index d83c5b1a268a..92ca57d853b2 100644 --- a/src/coreclr/tools/aot/ILCompiler.LLVM/Compiler/LLVMCodegenCompilationBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.LLVM/Compiler/LLVMCodegenCompilationBuilder.cs @@ -8,6 +8,7 @@ using ILCompiler.DependencyAnalysisFramework; using Internal.IL; +using Internal.JitInterface; namespace ILCompiler { @@ -17,6 +18,7 @@ public sealed class LLVMCodegenCompilationBuilder : CompilationBuilder // calling the Use/Configure methods and still get something reasonable back. LLVMCodegenConfigProvider _config = new LLVMCodegenConfigProvider(Array.Empty()); private ILProvider _ilProvider = new CoreRTILProvider(); + private KeyValuePair[] _ryujitOptions = Array.Empty>(); public LLVMCodegenCompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup group) : base(context, group, new CoreRTNameMangler(new LLVMNodeMangler(), false)) @@ -26,6 +28,26 @@ public LLVMCodegenCompilationBuilder(CompilerTypeSystemContext context, Compilat public override CompilationBuilder UseBackendOptions(IEnumerable options) { _config = new LLVMCodegenConfigProvider(options); + var builder = new ArrayBuilder>(); + + foreach (string param in options) + { + int indexOfEquals = param.IndexOf('='); + + // We're skipping bad parameters without reporting. + // This is not a mainstream feature that would need to be friendly. + // Besides, to really validate this, we would also need to check that the config name is known. + if (indexOfEquals < 1) + continue; + + string name = param.Substring(0, indexOfEquals); + string value = param.Substring(indexOfEquals + 1); + + builder.Add(new KeyValuePair(name, value)); + } + + _ryujitOptions = builder.ToArray(); + return this; } @@ -42,9 +64,31 @@ protected override ILProvider GetILProvider() public override ICompilation ToCompilation() { + ArrayBuilder jitFlagBuilder = new ArrayBuilder(); + + switch (_optimizationMode) + { + case OptimizationMode.None: + jitFlagBuilder.Add(CorJitFlag.CORJIT_FLAG_DEBUG_CODE); + break; + + case OptimizationMode.PreferSize: + jitFlagBuilder.Add(CorJitFlag.CORJIT_FLAG_SIZE_OPT); + break; + + case OptimizationMode.PreferSpeed: + jitFlagBuilder.Add(CorJitFlag.CORJIT_FLAG_SPEED_OPT); + break; + + default: + // Not setting a flag results in BLENDED_CODE. + break; + } + LLVMCodegenNodeFactory factory = new LLVMCodegenNodeFactory(_context, _compilationGroup, _metadataManager, _interopStubManager, _nameMangler, _vtableSliceProvider, _dictionaryLayoutProvider, GetPreinitializationManager()); + JitConfigProvider.Initialize(_context.Target, jitFlagBuilder.ToArray(), _ryujitOptions); DependencyAnalyzerBase graph = CreateDependencyGraph(factory, new ObjectNode.ObjectNodeComparer(new CompilerComparer())); - return new LLVMCodegenCompilation(graph, factory, _compilationRoots, _ilProvider, _debugInformationProvider, _logger, _inliningPolicy ?? _compilationGroup, _config); + return new LLVMCodegenCompilation(graph, factory, _compilationRoots, _ilProvider, _debugInformationProvider, _logger, _config, _inliningPolicy, _devirtualizationManager, _instructionSetSupport); } } diff --git a/src/coreclr/tools/aot/ILCompiler.LLVM/ILCompiler.LLVM.csproj b/src/coreclr/tools/aot/ILCompiler.LLVM/ILCompiler.LLVM.csproj index 347fff9bad00..b1e41e99bb00 100644 --- a/src/coreclr/tools/aot/ILCompiler.LLVM/ILCompiler.LLVM.csproj +++ b/src/coreclr/tools/aot/ILCompiler.LLVM/ILCompiler.LLVM.csproj @@ -21,6 +21,7 @@ + @@ -40,6 +41,9 @@ IL\HelperExtensions.cs + + IL\Stubs\TypeSystemThrowingILEmitter.cs + diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs index e33f2a673bd0..bc4394bded5d 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs @@ -3,13 +3,27 @@ using System.Diagnostics; using System.Text; +using ILCompiler.DependencyAnalysisFramework; using Internal.Text; using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis { + public interface IMethodCodeNode : IMethodNode, ISymbolDefinitionNode + { + void SetCode(ObjectNode.ObjectData data, bool isFoldable); + void InitializeFrameInfos(FrameInfo[] frameInfos); + void InitializeDebugEHClauseInfos(DebugEHClauseInfo[] debugEhClauseInfos); + void InitializeGCInfo(byte[] gcInfo); + void InitializeEHInfo(ObjectNode.ObjectData ehInfo); + void InitializeDebugLocInfos(DebugLocInfo[] debugLocInfos); + void InitializeDebugVarInfos(DebugVarInfo[] debugVarInfos); + void InitializeNonRelocationDependencies(DependencyNodeCore.DependencyList additionalDependencies); + void InitializeIsStateMachineMoveNextMethod(bool debugInfoIsStateMachineMoveNextMethod); + } + [DebuggerTypeProxy(typeof(MethodCodeNodeDebugView))] - public class MethodCodeNode : ObjectNode, IMethodBodyNode, INodeWithCodeInfo, INodeWithDebugInfo, ISymbolDefinitionNode, ISpecialUnboxThunkNode + public class MethodCodeNode : ObjectNode, IMethodBodyNode, INodeWithCodeInfo, INodeWithDebugInfo, ISymbolDefinitionNode, ISpecialUnboxThunkNode, IMethodCodeNode { private MethodDesc _method; private ObjectData _methodCode; diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs index d57f7844eab4..87af5a604a52 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs @@ -16,7 +16,7 @@ namespace ILCompiler { - public sealed class RyuJitCompilation : Compilation + public class RyuJitCompilation : Compilation { private readonly ConditionalWeakTable _corinfos = new ConditionalWeakTable(); internal readonly RyuJitCompilationOptions _compilationOptions; @@ -26,7 +26,7 @@ public sealed class RyuJitCompilation : Compilation public InstructionSetSupport InstructionSetSupport { get; } - internal RyuJitCompilation( + public RyuJitCompilation( DependencyAnalyzerBase dependencyGraph, NodeFactory nodeFactory, IEnumerable roots, diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj b/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj index 865fcb71046e..60d8ef9d41cd 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj @@ -94,4 +94,8 @@ Pgo\TypeSystemEntityOrUnknown.cs + + + + diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.Llvm.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.Llvm.cs new file mode 100644 index 000000000000..d266de7ca997 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.Llvm.cs @@ -0,0 +1,30 @@ +using System; +using System.Runtime.InteropServices; +using Internal.TypeSystem; + +namespace Internal.JitInterface +{ + public unsafe sealed partial class CorInfoImpl + { + [UnmanagedCallersOnly] + public static byte* getMangledMethodName(IntPtr thisHandle, CORINFO_METHOD_STRUCT_* ftn) + { + var _this = GetThis(thisHandle); + + MethodDesc method = _this.HandleToObject(ftn); + + return (byte*)_this.GetPin(_this._compilation.NameMangler.GetMangledMethodName(method).UnderlyingArray); + } + + [DllImport(JitLibrary)] + private extern static void registerLlvmCallbacks(IntPtr thisHandle, byte* outputFileName, byte* triple, byte* dataLayout, delegate* unmanaged getMangedMethodNamePtr); + + public void RegisterLlvmCallbacks(IntPtr corInfoPtr, string outputFileName, string triple, string dataLayout) + { + registerLlvmCallbacks(corInfoPtr, (byte*)GetPin(StringToUTF8(outputFileName)), + (byte*)GetPin(StringToUTF8(triple)), + (byte*)GetPin(StringToUTF8(dataLayout)), + (delegate* unmanaged) &getMangledMethodName); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index 19bc8342d8d5..8c5a2ece1e58 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -22,7 +22,7 @@ namespace Internal.JitInterface { - unsafe partial class CorInfoImpl + unsafe public partial class CorInfoImpl { private struct SequencePoint { @@ -37,7 +37,7 @@ private struct SequencePoint private RyuJitCompilation _compilation; private MethodDebugInformation _debugInfo; - private MethodCodeNode _methodCodeNode; + private IMethodCodeNode _methodCodeNode; private DebugLocInfo[] _debugLocInfos; private DebugVarInfo[] _debugVarInfos; private Dictionary _sequencePoints; @@ -59,7 +59,7 @@ private MethodDesc getUnboxingThunk(MethodDesc method) return _unboxingThunkFactory.GetUnboxingMethod(method); } - public void CompileMethod(MethodCodeNode methodCodeNodeNeedingCode, MethodIL methodIL = null) + public void CompileMethod(IMethodCodeNode methodCodeNodeNeedingCode, MethodIL methodIL = null) { _methodCodeNode = methodCodeNodeNeedingCode; _isFallbackBodyCompilation = methodIL != null; diff --git a/src/coreclr/tools/aot/ilc.sln b/src/coreclr/tools/aot/ilc.sln index 3c1d83fdf890..5c12affa7263 100644 --- a/src/coreclr/tools/aot/ilc.sln +++ b/src/coreclr/tools/aot/ilc.sln @@ -18,6 +18,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.LLVM", "ILCompil EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "repro", "ILCompiler\repro\repro.csproj", "{CBDE0470-E0C9-4693-9A11-ACC117522F3F}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "clrjit_browser_wasm32_x64", "..\..\..\..\artifacts\obj\coreclr\windows.x64.Debug\jit\clrjit_browser_wasm32_x64.vcxproj", "{A88F0FA2-0F52-3EC8-BBFA-7FC6F4E93B56}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "clrjit_win_x64_x64", "..\..\..\..\artifacts\obj\coreclr\windows.x64.Debug\jit\clrjit_win_x64_x64.vcxproj", "{A75E7596-C53A-3C6F-8FD7-AC56E41F3783}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Checked|Any CPU = Checked|Any CPU @@ -29,6 +33,9 @@ Global Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 + RelWithDebInfo|Any CPU = RelWithDebInfo|Any CPU + RelWithDebInfo|x64 = RelWithDebInfo|x64 + RelWithDebInfo|x86 = RelWithDebInfo|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.Checked|Any CPU.ActiveCfg = Checked|x86 @@ -46,6 +53,12 @@ Global {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.Release|x64.Build.0 = Release|x64 {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.Release|x86.ActiveCfg = Release|x86 {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.Release|x86.Build.0 = Release|x86 + {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.RelWithDebInfo|Any CPU.ActiveCfg = Release|x64 + {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.RelWithDebInfo|Any CPU.Build.0 = Release|x64 + {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.RelWithDebInfo|x64.ActiveCfg = Release|x64 + {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.RelWithDebInfo|x64.Build.0 = Release|x64 + {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.RelWithDebInfo|x86.ActiveCfg = Release|x86 + {6856F5F6-E568-493F-AF8A-7F624B5A02A5}.RelWithDebInfo|x86.Build.0 = Release|x86 {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.Checked|Any CPU.ActiveCfg = Checked|x86 {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.Checked|x64.ActiveCfg = Checked|x64 {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.Checked|x64.Build.0 = Checked|x64 @@ -61,6 +74,12 @@ Global {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.Release|x64.Build.0 = Release|x64 {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.Release|x86.ActiveCfg = Release|x86 {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.Release|x86.Build.0 = Release|x86 + {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.RelWithDebInfo|Any CPU.ActiveCfg = Release|x64 + {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.RelWithDebInfo|Any CPU.Build.0 = Release|x64 + {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.RelWithDebInfo|x64.ActiveCfg = Release|x64 + {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.RelWithDebInfo|x64.Build.0 = Release|x64 + {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.RelWithDebInfo|x86.ActiveCfg = Release|x86 + {FB2D45F2-FA4C-42B2-8E53-3E1F30CF8046}.RelWithDebInfo|x86.Build.0 = Release|x86 {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.Checked|Any CPU.ActiveCfg = Checked|x86 {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.Checked|x64.ActiveCfg = Checked|x64 {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.Checked|x64.Build.0 = Checked|x64 @@ -76,6 +95,12 @@ Global {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.Release|x64.Build.0 = Release|x64 {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.Release|x86.ActiveCfg = Release|x86 {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.Release|x86.Build.0 = Release|x86 + {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.RelWithDebInfo|Any CPU.ActiveCfg = Release|x64 + {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.RelWithDebInfo|Any CPU.Build.0 = Release|x64 + {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.RelWithDebInfo|x64.ActiveCfg = Release|x64 + {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.RelWithDebInfo|x64.Build.0 = Release|x64 + {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.RelWithDebInfo|x86.ActiveCfg = Release|x86 + {B68D5B9E-405B-4E44-B2CC-418EC78AE070}.RelWithDebInfo|x86.Build.0 = Release|x86 {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.Checked|Any CPU.ActiveCfg = Checked|x86 {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.Checked|x64.ActiveCfg = Checked|x64 {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.Checked|x64.Build.0 = Checked|x64 @@ -91,6 +116,12 @@ Global {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.Release|x64.Build.0 = Release|x64 {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.Release|x86.ActiveCfg = Release|x86 {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.Release|x86.Build.0 = Release|x86 + {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.RelWithDebInfo|Any CPU.ActiveCfg = Release|x64 + {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.RelWithDebInfo|Any CPU.Build.0 = Release|x64 + {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.RelWithDebInfo|x64.ActiveCfg = Release|x64 + {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.RelWithDebInfo|x64.Build.0 = Release|x64 + {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.RelWithDebInfo|x86.ActiveCfg = Release|x86 + {05E020F4-6FA1-4DEE-8B9D-4F4B79840231}.RelWithDebInfo|x86.Build.0 = Release|x86 {07221944-6A68-4B82-8461-82C7754B1B1F}.Checked|Any CPU.ActiveCfg = Checked|x86 {07221944-6A68-4B82-8461-82C7754B1B1F}.Checked|x64.ActiveCfg = Checked|x64 {07221944-6A68-4B82-8461-82C7754B1B1F}.Checked|x64.Build.0 = Checked|x64 @@ -106,6 +137,12 @@ Global {07221944-6A68-4B82-8461-82C7754B1B1F}.Release|x64.Build.0 = Release|x64 {07221944-6A68-4B82-8461-82C7754B1B1F}.Release|x86.ActiveCfg = Release|x86 {07221944-6A68-4B82-8461-82C7754B1B1F}.Release|x86.Build.0 = Release|x86 + {07221944-6A68-4B82-8461-82C7754B1B1F}.RelWithDebInfo|Any CPU.ActiveCfg = Release|x64 + {07221944-6A68-4B82-8461-82C7754B1B1F}.RelWithDebInfo|Any CPU.Build.0 = Release|x64 + {07221944-6A68-4B82-8461-82C7754B1B1F}.RelWithDebInfo|x64.ActiveCfg = Release|x64 + {07221944-6A68-4B82-8461-82C7754B1B1F}.RelWithDebInfo|x64.Build.0 = Release|x64 + {07221944-6A68-4B82-8461-82C7754B1B1F}.RelWithDebInfo|x86.ActiveCfg = Release|x86 + {07221944-6A68-4B82-8461-82C7754B1B1F}.RelWithDebInfo|x86.Build.0 = Release|x86 {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.Checked|Any CPU.ActiveCfg = Checked|x86 {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.Checked|x64.ActiveCfg = Checked|x64 {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.Checked|x64.Build.0 = Checked|x64 @@ -121,6 +158,12 @@ Global {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.Release|x64.Build.0 = Release|x64 {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.Release|x86.ActiveCfg = Release|x86 {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.Release|x86.Build.0 = Release|x86 + {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.RelWithDebInfo|Any CPU.ActiveCfg = Release|x64 + {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.RelWithDebInfo|Any CPU.Build.0 = Release|x64 + {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.RelWithDebInfo|x64.ActiveCfg = Release|x64 + {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.RelWithDebInfo|x64.Build.0 = Release|x64 + {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.RelWithDebInfo|x86.ActiveCfg = Release|x86 + {FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}.RelWithDebInfo|x86.Build.0 = Release|x86 {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Checked|Any CPU.ActiveCfg = Checked|x86 {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Checked|x64.ActiveCfg = Checked|x64 {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Checked|x64.Build.0 = Checked|x64 @@ -130,12 +173,18 @@ Global {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Debug|x64.ActiveCfg = Debug|x64 {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Debug|x64.Build.0 = Debug|x64 {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Debug|x86.ActiveCfg = Debug|x86 - {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Debug|x86.Build.0 = Debug|x64 + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Debug|x86.Build.0 = Debug|x86 {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Release|Any CPU.ActiveCfg = Release|Any CPU {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Release|x64.ActiveCfg = Release|x64 {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Release|x64.Build.0 = Release|x64 {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Release|x86.ActiveCfg = Release|x86 {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Release|x86.Build.0 = Release|x86 + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.RelWithDebInfo|x64.ActiveCfg = Release|x64 + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.RelWithDebInfo|x64.Build.0 = Release|x64 + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.RelWithDebInfo|x86.ActiveCfg = Release|x86 + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.RelWithDebInfo|x86.Build.0 = Release|x86 {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Checked|Any CPU.ActiveCfg = Checked|x86 {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Checked|x64.ActiveCfg = Checked|x64 {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Checked|x64.Build.0 = Checked|x64 @@ -151,6 +200,44 @@ Global {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Release|x64.Build.0 = Release|x64 {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Release|x86.ActiveCfg = Release|x86 {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.Release|x86.Build.0 = Release|x86 + {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.RelWithDebInfo|Any CPU.ActiveCfg = Release|x64 + {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.RelWithDebInfo|Any CPU.Build.0 = Release|x64 + {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.RelWithDebInfo|x64.ActiveCfg = Release|x64 + {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.RelWithDebInfo|x64.Build.0 = Release|x64 + {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.RelWithDebInfo|x86.ActiveCfg = Release|x86 + {CBDE0470-E0C9-4693-9A11-ACC117522F3F}.RelWithDebInfo|x86.Build.0 = Release|x86 + {A88F0FA2-0F52-3EC8-BBFA-7FC6F4E93B56}.Checked|Any CPU.ActiveCfg = Checked|x64 + {A88F0FA2-0F52-3EC8-BBFA-7FC6F4E93B56}.Checked|x64.ActiveCfg = Checked|x64 + {A88F0FA2-0F52-3EC8-BBFA-7FC6F4E93B56}.Checked|x64.Build.0 = Checked|x64 + {A88F0FA2-0F52-3EC8-BBFA-7FC6F4E93B56}.Checked|x86.ActiveCfg = Checked|x64 + {A88F0FA2-0F52-3EC8-BBFA-7FC6F4E93B56}.Debug|Any CPU.ActiveCfg = Debug|x64 + {A88F0FA2-0F52-3EC8-BBFA-7FC6F4E93B56}.Debug|x64.ActiveCfg = Debug|x64 + {A88F0FA2-0F52-3EC8-BBFA-7FC6F4E93B56}.Debug|x64.Build.0 = Debug|x64 + {A88F0FA2-0F52-3EC8-BBFA-7FC6F4E93B56}.Debug|x86.ActiveCfg = Debug|x64 + {A88F0FA2-0F52-3EC8-BBFA-7FC6F4E93B56}.Release|Any CPU.ActiveCfg = Release|x64 + {A88F0FA2-0F52-3EC8-BBFA-7FC6F4E93B56}.Release|x64.ActiveCfg = Release|x64 + {A88F0FA2-0F52-3EC8-BBFA-7FC6F4E93B56}.Release|x64.Build.0 = Release|x64 + {A88F0FA2-0F52-3EC8-BBFA-7FC6F4E93B56}.Release|x86.ActiveCfg = Release|x64 + {A88F0FA2-0F52-3EC8-BBFA-7FC6F4E93B56}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64 + {A88F0FA2-0F52-3EC8-BBFA-7FC6F4E93B56}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64 + {A88F0FA2-0F52-3EC8-BBFA-7FC6F4E93B56}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64 + {A88F0FA2-0F52-3EC8-BBFA-7FC6F4E93B56}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64 + {A75E7596-C53A-3C6F-8FD7-AC56E41F3783}.Checked|Any CPU.ActiveCfg = Checked|x64 + {A75E7596-C53A-3C6F-8FD7-AC56E41F3783}.Checked|x64.ActiveCfg = Checked|x64 + {A75E7596-C53A-3C6F-8FD7-AC56E41F3783}.Checked|x64.Build.0 = Checked|x64 + {A75E7596-C53A-3C6F-8FD7-AC56E41F3783}.Checked|x86.ActiveCfg = Checked|x64 + {A75E7596-C53A-3C6F-8FD7-AC56E41F3783}.Debug|Any CPU.ActiveCfg = Debug|x64 + {A75E7596-C53A-3C6F-8FD7-AC56E41F3783}.Debug|x64.ActiveCfg = Debug|x64 + {A75E7596-C53A-3C6F-8FD7-AC56E41F3783}.Debug|x64.Build.0 = Debug|x64 + {A75E7596-C53A-3C6F-8FD7-AC56E41F3783}.Debug|x86.ActiveCfg = Debug|x64 + {A75E7596-C53A-3C6F-8FD7-AC56E41F3783}.Release|Any CPU.ActiveCfg = Release|x64 + {A75E7596-C53A-3C6F-8FD7-AC56E41F3783}.Release|x64.ActiveCfg = Release|x64 + {A75E7596-C53A-3C6F-8FD7-AC56E41F3783}.Release|x64.Build.0 = Release|x64 + {A75E7596-C53A-3C6F-8FD7-AC56E41F3783}.Release|x86.ActiveCfg = Release|x64 + {A75E7596-C53A-3C6F-8FD7-AC56E41F3783}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64 + {A75E7596-C53A-3C6F-8FD7-AC56E41F3783}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64 + {A75E7596-C53A-3C6F-8FD7-AC56E41F3783}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64 + {A75E7596-C53A-3C6F-8FD7-AC56E41F3783}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE