diff --git a/README.md b/README.md index b115294b7ad1..9652429fb132 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # .NET Runtime - Native AOT -This branch contains experimental fork of CoreCLR [.NET runtime](http://github.com/dotnet/runtime) optimized for the [Native AOT Form factor](https://github.com/dotnet/designs/blob/main/accepted/2020/form-factors.md#native-aot-form-factors). The ahead-of-time (AOT) toolchain can compile .NET application into a native (architecture specific) single-file executable. It can also produce standalone dynamic or static libraries that can be consumed by applications written in other programming languages. +This branch contains experimental fork of CoreCLR [.NET runtime](http://github.com/dotnet/runtime) optimized for the [Native AOT Form factor](https://github.com/dotnet/designs/blob/main/accepted/2020/form-factors.md#native-aot-form-factors). The ahead-of-time (AOT) toolchain can compile .NET application into a native (architecture specific) single-file executable. It can also produce standalone dynamic or static libraries that can be consumed by applications written in other programming languages. This branch contains the experimental feature of compiling to LLVM to target Web Assembly at present and other LLVM targets in the future. ## Samples diff --git a/docs/workflow/building/coreclr/nativeaot.md b/docs/workflow/building/coreclr/nativeaot.md index 7cba07269c39..3e8f53ae1322 100644 --- a/docs/workflow/building/coreclr/nativeaot.md +++ b/docs/workflow/building/coreclr/nativeaot.md @@ -10,6 +10,12 @@ The Native AOT toolchain can be currently built for Linux, macOS and Windows x64 - Add the package directory to your `nuget.config` file. For example, replace `dotnet-experimental` line in `samples\HelloWorld\nuget.config` with `` - 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 +- Currently only tested on Windows +- Run `build nativeaot+libs+nativeaot.packages -rc [Debug|Release] -lc [Debug|Release] -a wasm -os Browser -runtimeFlavor CoreCLR` +- Add the package directory to your `nuget.config` as above. +- Run `dotnet publish -r browser-wasm -c [Debug|Release] /p:Platform=wasm` to publish. + ## 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. diff --git a/eng/Configurations.props b/eng/Configurations.props index 09814ae0c05e..23b85837df79 100644 --- a/eng/Configurations.props +++ b/eng/Configurations.props @@ -123,9 +123,9 @@ <_toolRuntimeRID Condition="'$(_runtimeOS)' == 'linux-musl' and $(TargetArchitecture.StartsWith('arm')) and !$(_hostArch.StartsWith('arm'))">linux-x64 - <_toolRuntimeRID Condition="'$(_runtimeOS)' == 'browser' and '$(TargetOS)' == 'windows'">win-x64 - <_toolRuntimeRID Condition="'$(_runtimeOS)' == 'browser' and '$(TargetOS)' != 'windows' and $(_buildingInOSX)">osx-x64 - <_toolRuntimeRID Condition="'$(_runtimeOS)' == 'browser' and '$(TargetOS)' != 'windows' and !$(_buildingInOSX)">linux-x64 + <_toolRuntimeRID Condition="'$(_runtimeOS)' == 'browser'">linux-x64 + <_toolRuntimeRID Condition="'$(_runtimeOS)' == 'browser' and $([MSBuild]::IsOSPlatform('WINDOWS'))">win-x64 + <_toolRuntimeRID Condition="'$(_runtimeOS)' == 'browser' and $([MSBuild]::IsOSPlatform('OSX'))">osx-x64 <_toolRuntimeRID Condition="'$(_runtimeOS)' == 'android' and '$(TargetOS)' == 'windows'">win-x64 diff --git a/eng/Subsets.props b/eng/Subsets.props index 6423a04c0938..0fc111ff877d 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -237,7 +237,7 @@ - + diff --git a/eng/build.ps1 b/eng/build.ps1 index 4faea7ff30c3..658f35ac48bc 100644 --- a/eng/build.ps1 +++ b/eng/build.ps1 @@ -243,7 +243,11 @@ foreach ($config in $configuration) { $argumentsWithConfig = $arguments + " -configuration $((Get-Culture).TextInfo.ToTitleCase($config))"; foreach ($singleArch in $arch) { $argumentsWithArch = "/p:TargetArchitecture=$singleArch " + $argumentsWithConfig - $env:__DistroRid="win-$singleArch" + if ($singleArch -eq "wasm") { + $env:__DistroRid="browser-wasm" + } else { + $env:__DistroRid="win-$singleArch" + } Invoke-Expression "& `"$PSScriptRoot/common/build.ps1`" $argumentsWithArch" if ($lastExitCode -ne 0) { $failedBuilds += "Configuration: $config, Architecture: $singleArch" diff --git a/eng/liveBuilds.targets b/eng/liveBuilds.targets index 5cb3c220feea..214d68d51c75 100644 --- a/eng/liveBuilds.targets +++ b/eng/liveBuilds.targets @@ -159,14 +159,14 @@ $(LibrariesNativeArtifactsPath)*.pdb" IsNative="true" Exclude="@(ExcludeNativeLibrariesRuntimeFiles)" /> - - - >>:TARGET_X86>) +elseif (CLR_CMAKE_TARGET_ARCH_WASM) + set(ARCH_TARGET_NAME wasm) + set(ARCH_SOURCES_DIR wasm) + add_compile_definitions($<$>>:TARGET_WASM>) else () clr_unknown_arch() endif () @@ -294,15 +302,26 @@ endif () #-------------------------------------- # Compile Options #-------------------------------------- -if (CLR_CMAKE_HOST_UNIX) - # Disable frame pointer optimizations so profilers can get better call stacks - add_compile_options(-fno-omit-frame-pointer) - +if (NOT(MSVC)) # The -fms-extensions enable the stuff like __if_exists, __declspec(uuid()), etc. add_compile_options(-fms-extensions) #-fms-compatibility Enable full Microsoft Visual C++ compatibility #-fms-extensions Accept some non-standard constructs supported by the Microsoft compiler + # Disabled common warnings + add_compile_options(-Wno-unused-variable) + add_compile_options(-Wno-unused-value) + add_compile_options(-Wno-unused-function) + add_compile_options(-Wno-tautological-compare) + + #These seem to indicate real issues + add_compile_options($<$:-Wno-invalid-offsetof>) +endif (NOT(MSVC)) + +if (CLR_CMAKE_HOST_UNIX) + # Disable frame pointer optimizations so profilers can get better call stacks + add_compile_options(-fno-omit-frame-pointer) + # Make signed arithmetic overflow of addition, subtraction, and multiplication wrap around # using twos-complement representation (this is normally undefined according to the C++ spec). add_compile_options(-fwrapv) @@ -322,20 +341,11 @@ if (CLR_CMAKE_HOST_UNIX) add_compile_options(-Werror) endif(PRERELEASE) - # Disabled common warnings - add_compile_options(-Wno-unused-variable) - add_compile_options(-Wno-unused-value) - add_compile_options(-Wno-unused-function) - add_compile_options(-Wno-tautological-compare) - check_cxx_compiler_flag(-Wimplicit-fallthrough COMPILER_SUPPORTS_W_IMPLICIT_FALLTHROUGH) if (COMPILER_SUPPORTS_W_IMPLICIT_FALLTHROUGH) add_compile_options(-Wimplicit-fallthrough) endif() - #These seem to indicate real issues - add_compile_options($<$:-Wno-invalid-offsetof>) - if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") # The -ferror-limit is helpful during the porting, it makes sure the compiler doesn't stop # after hitting just about 20 errors. @@ -639,6 +649,8 @@ if (CLR_CMAKE_HOST_WIN32) message(FATAL_ERROR "MC not found") endif() +elseif(CLR_CMAKE_TARGET_ARCH_WASM) + # No other tools required, emcmake is already running this script else (CLR_CMAKE_HOST_WIN32) enable_language(ASM) diff --git a/eng/native/configureplatform.cmake b/eng/native/configureplatform.cmake index f8b1a10c0e8f..c659c0c6594c 100644 --- a/eng/native/configureplatform.cmake +++ b/eng/native/configureplatform.cmake @@ -183,7 +183,8 @@ if(CLR_CMAKE_HOST_OS STREQUAL Windows) endif(CLR_CMAKE_HOST_OS STREQUAL Windows) if(CLR_CMAKE_HOST_OS STREQUAL Emscripten) - #set(CLR_CMAKE_HOST_UNIX 1) # TODO: this should be reenabled but it activates a bunch of additional compiler flags in configurecompiler.cmake + set(CLR_CMAKE_HOST_WIN32 0) + set(CLR_CMAKE_HOST_UNIX 0) set(CLR_CMAKE_HOST_UNIX_WASM 1) set(CLR_CMAKE_HOST_BROWSER 1) endif(CLR_CMAKE_HOST_OS STREQUAL Emscripten) @@ -337,8 +338,10 @@ endif(CLR_CMAKE_TARGET_OS STREQUAL SunOS) if(CLR_CMAKE_TARGET_OS STREQUAL Emscripten) set(CLR_CMAKE_TARGET_UNIX 1) - set(CLR_CMAKE_TARGET_LINUX 1) set(CLR_CMAKE_TARGET_BROWSER 1) + if(NOT(RUNTIME_FLAVOR STREQUAL CoreClr)) + set(CLR_CMAKE_TARGET_LINUX 1) + endif(NOT(RUNTIME_FLAVOR STREQUAL CoreClr)) endif(CLR_CMAKE_TARGET_OS STREQUAL Emscripten) if(CLR_CMAKE_TARGET_UNIX) @@ -364,7 +367,7 @@ endif(CLR_CMAKE_TARGET_UNIX) # check if host & target os/arch combination are valid if (CLR_CMAKE_TARGET_OS STREQUAL CLR_CMAKE_HOST_OS) if(NOT(CLR_CMAKE_TARGET_ARCH STREQUAL CLR_CMAKE_HOST_ARCH)) - if(NOT((CLR_CMAKE_HOST_ARCH_AMD64 AND CLR_CMAKE_TARGET_ARCH_ARM64) OR (CLR_CMAKE_HOST_ARCH_I386 AND CLR_CMAKE_TARGET_ARCH_ARM) OR (CLR_CMAKE_HOST_ARCH_AMD64 AND CLR_CMAKE_TARGET_ARCH_ARM) OR (CLR_CMAKE_HOST_ARCH_AMD64 AND CLR_CMAKE_TARGET_ARCH_I386))) + if(NOT((CLR_CMAKE_HOST_ARCH_AMD64 AND CLR_CMAKE_TARGET_ARCH_ARM64) OR (CLR_CMAKE_HOST_ARCH_I386 AND CLR_CMAKE_TARGET_ARCH_ARM) OR (CLR_CMAKE_HOST_ARCH_AMD64 AND CLR_CMAKE_TARGET_ARCH_ARM) OR (CLR_CMAKE_HOST_ARCH_AMD64 AND CLR_CMAKE_TARGET_ARCH_I386) OR (CLR_CMAKE_TARGET_UNIX_WASM AND CLR_CMAKE_HOST_ARCH STREQUAL Windows_NT))) message(FATAL_ERROR "Invalid platform and target arch combination TARGET_ARCH=${CLR_CMAKE_TARGET_ARCH} HOST_ARCH=${CLR_CMAKE_HOST_ARCH}") endif() endif() diff --git a/eng/native/gen-buildsys.cmd b/eng/native/gen-buildsys.cmd index c340ec2f9fad..52674a3c73ba 100644 --- a/eng/native/gen-buildsys.cmd +++ b/eng/native/gen-buildsys.cmd @@ -29,7 +29,7 @@ if /i "%__Ninja%" == "1" ( if /i NOT "%__Arch%" == "wasm" ( if /i "%__VSVersion%" == "vs2019" (set __CmakeGenerator=%__CmakeGenerator% 16 2019) if /i "%__VSVersion%" == "vs2017" (set __CmakeGenerator=%__CmakeGenerator% 15 2017) - + if /i "%__Arch%" == "x64" (set __ExtraCmakeParams=%__ExtraCmakeParams% -A x64) if /i "%__Arch%" == "arm" (set __ExtraCmakeParams=%__ExtraCmakeParams% -A ARM) if /i "%__Arch%" == "arm64" (set __ExtraCmakeParams=%__ExtraCmakeParams% -A ARM64) @@ -38,17 +38,17 @@ if /i "%__Ninja%" == "1" ( set __CmakeGenerator=NMake Makefiles ) ) - +echo gen-buildsys %__Arch% if /i "%__Arch%" == "wasm" ( - if "%EMSDK_PATH%" == "" ( - echo Error: Should set EMSDK_PATH environment variable pointing to emsdk root. + if "%EMSDK%" == "" ( + echo Error: Should set EMSDK environment variable pointing to emsdk root. exit /B 1 ) - if "%EMSCRIPTEN_ROOT%" == "" ( - set EMSCRIPTEN_ROOT="%EMSDK_PATH/upstream/emscripten%" + if /i "%CMAKE_BUILD_TYPE%" == "debug" ( + set __ExtraCmakeParams=%__ExtraCmakeParams% -g -O0 ) - set __ExtraCmakeParams=%__ExtraCmakeParams% "-DEMSCRIPTEN_GENERATE_BITCODE_STATIC_LIBRARIES=1" "-DCMAKE_TOOLCHAIN_FILE=%EMSCRIPTEN%/cmake/Modules/Platform/Emscripten.cmake" + set __ExtraCmakeParams=%__ExtraCmakeParams% "-DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%" "-DCMAKE_TOOLCHAIN_FILE=%EMSDK%/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake" -DCLR_CMAKE_TARGET_ARCH=wasm -DCLR_CMAKE_TARGET_ARCH_WASM=1 -DCLR_CMAKE_HOST_ARCH=Windows_NT -DCLR_CMAKE_HOST_OS=Emscripten -DRUNTIME_FLAVOR=CoreClr -DCLR_CMAKE_HOST_UNIX_WASM=1 "-DCLR_ENG_NATIVE_DIR=%__repoRoot%\eng\native" "-DCMAKE_REPO_ROOT=%__repoRoot%" -DCLR_CMAKE_KEEP_NATIVE_SYMBOLS=1 set __UseEmcmake=1 ) else ( set __ExtraCmakeParams=%__ExtraCmakeParams% "-DCMAKE_SYSTEM_VERSION=10.0" diff --git a/samples/HelloWorld/HelloWorld.csproj b/samples/HelloWorld/HelloWorld.csproj index 0dd37c7c03c4..fc110f34ca6e 100644 --- a/samples/HelloWorld/HelloWorld.csproj +++ b/samples/HelloWorld/HelloWorld.csproj @@ -6,7 +6,7 @@ - + diff --git a/samples/HelloWorld/README.md b/samples/HelloWorld/README.md index ad2bca14daf6..f97770d08ff0 100644 --- a/samples/HelloWorld/README.md +++ b/samples/HelloWorld/README.md @@ -33,7 +33,7 @@ This will add a nuget.config file to your application. Open the file and in the Once you've added the package source, add a reference to the compiler by running the following command: ```bash -> dotnet add package Microsoft.DotNet.ILCompiler -v 6.0.0-* +> dotnet add package Microsoft.DotNet.ILCompiler.LLVM -v 6.0.0-* ``` ## Restore and Publish your app diff --git a/samples/HelloWorld/nuget.config b/samples/HelloWorld/nuget.config index 689c575ce22e..2c2fd2f313ed 100644 --- a/samples/HelloWorld/nuget.config +++ b/samples/HelloWorld/nuget.config @@ -3,7 +3,7 @@ - + diff --git a/samples/NativeLibrary/NativeLibrary.csproj b/samples/NativeLibrary/NativeLibrary.csproj index c13e259a1bec..e06e6ac40238 100644 --- a/samples/NativeLibrary/NativeLibrary.csproj +++ b/samples/NativeLibrary/NativeLibrary.csproj @@ -5,7 +5,7 @@ - + diff --git a/src/coreclr/CMakeLists.txt b/src/coreclr/CMakeLists.txt index 98a9aa8ea507..11c9485ae293 100644 --- a/src/coreclr/CMakeLists.txt +++ b/src/coreclr/CMakeLists.txt @@ -75,21 +75,23 @@ if(CLR_CMAKE_HOST_UNIX) add_subdirectory(src/pal) add_subdirectory(src/hosts) else(CLR_CMAKE_HOST_UNIX) - if(CLR_CMAKE_TARGET_UNIX) + if(CLR_CMAKE_TARGET_UNIX AND NOT CLR_CMAKE_HOST_UNIX_WASM) add_subdirectory(src/pal/src/libunwind) - endif(CLR_CMAKE_TARGET_UNIX) + endif(CLR_CMAKE_TARGET_UNIX AND NOT CLR_CMAKE_HOST_UNIX_WASM) endif(CLR_CMAKE_HOST_UNIX) # Add this subdir. We install the headers for the jit. add_subdirectory(src/pal/prebuilt/inc) -add_subdirectory(src/debug/debug-pal) +if(NOT CLR_CMAKE_HOST_UNIX_WASM) + add_subdirectory(src/debug/debug-pal) + add_subdirectory(src/tools/aot/jitinterface) +endif(NOT CLR_CMAKE_HOST_UNIX_WASM) if(CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_BUILD_SUBSET_RUNTIME) add_subdirectory(src/gc/sample) endif() -add_subdirectory(src/tools/aot/jitinterface) if(NOT CLR_CROSS_COMPONENTS_BUILD) add_subdirectory(src/nativeaot) diff --git a/src/coreclr/build-runtime.cmd b/src/coreclr/build-runtime.cmd index ca32712554db..72f983cf169e 100644 --- a/src/coreclr/build-runtime.cmd +++ b/src/coreclr/build-runtime.cmd @@ -58,6 +58,7 @@ set __BuildArchX64=0 set __BuildArchX86=0 set __BuildArchArm=0 set __BuildArchArm64=0 +set __BuildArchWasm=0 set __BuildTypeDebug=0 set __BuildTypeChecked=0 @@ -117,6 +118,7 @@ if /i "%1" == "-x64" (set __BuildArchX64=1&set processedArgs=!pr if /i "%1" == "-x86" (set __BuildArchX86=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "-arm" (set __BuildArchArm=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "-arm64" (set __BuildArchArm64=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) +if /i "%1" == "-wasm" (set __BuildArchWasm=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "-debug" (set __BuildTypeDebug=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "-checked" (set __BuildTypeChecked=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) @@ -233,6 +235,11 @@ if %__BuildArchArm64%==1 ( set __BuildArch=arm64 set __CrossArch=x64 ) +if %__BuildArchWasm%==1 ( + set __TargetOS=browser + set __BuildArch=wasm + set __BuildJit=0 +) set /A __TotalSpecifiedBuildType=__BuildTypeDebug + __BuildTypeChecked + __BuildTypeRelease if %__TotalSpecifiedBuildType% GTR 1 ( @@ -622,6 +629,9 @@ if %__BuildNative% EQU 1 ( if %__Ninja% EQU 1 ( set __ExtraCmakeArgs="-DCMAKE_BUILD_TYPE=!__BuildType!" ) + if "%__BuildArch%" == "wasm" ( + set __ExtraCmakeArgs="-DCMAKE_BUILD_TYPE=!__BuildType!" + ) set __ExtraCmakeArgs=!__ExtraCmakeArgs! !___CrossBuildDefine! %__CMakeClrBuildSubsetArgs% "-DCLR_CMAKE_PGO_INSTRUMENT=%__PgoInstrument%" "-DCLR_CMAKE_OPTDATA_PATH=%__PgoOptDataPath%" "-DCLR_CMAKE_PGO_OPTIMIZE=%__PgoOptimize%" "-DCLR_ENG_NATIVE_DIR=%__RepoRootDir%/eng/native" "-DCLR_REPO_ROOT_DIR=%__RepoRootDir%" %__CMakeArgs% call "%__RepoRootDir%\eng\native\gen-buildsys.cmd" "%__ProjectDir%" "%__IntermediatesDir%" %__VSVersion% %__BuildArch% !__ExtraCmakeArgs! @@ -656,7 +666,10 @@ if %__BuildNative% EQU 1 ( set __CmakeBuildToolArgs= ) else ( REM We pass the /m flag directly to MSBuild so that we can get both MSBuild and CL parallelism, which is fastest for our builds. - set __CmakeBuildToolArgs=/nologo /m !__Logging! + REM wasm uses nmake which does not support /m + if not "%__BuildArch%" == "wasm" ( + set __CmakeBuildToolArgs=/nologo /m !__Logging! + ) ) "%CMakePath%" --build %__IntermediatesDir% --target install --config %__BuildType% -- !__CmakeBuildToolArgs! @@ -671,6 +684,7 @@ if %__BuildNative% EQU 1 ( ) if /i "%__BuildArch%" == "arm64" goto SkipCopyUcrt + if /i "%__BuildArch%" == "wasm" goto SkipCopyUcrt if not defined UCRTVersion ( echo %__ErrMsgPrefix%%__MsgPrefix%Error: Please install Windows 10 SDK. diff --git a/src/coreclr/clrfeatures.cmake b/src/coreclr/clrfeatures.cmake index 1687bc50568f..ab91230e3700 100644 --- a/src/coreclr/clrfeatures.cmake +++ b/src/coreclr/clrfeatures.cmake @@ -2,6 +2,10 @@ if(CLR_CMAKE_TARGET_TIZEN_LINUX) set(FEATURE_GDBJIT_LANGID_CS 1) endif() +if(CLR_CMAKE_TARGET_UNIX_WASM) + set(FEATURE_EVENT_TRACE 0) +endif() + if(NOT DEFINED FEATURE_EVENT_TRACE) set(FEATURE_EVENT_TRACE 1) endif(NOT DEFINED FEATURE_EVENT_TRACE) diff --git a/src/coreclr/src/CMakeLists.txt b/src/coreclr/src/CMakeLists.txt index 5f74a587d849..46be64cf4ba9 100644 --- a/src/coreclr/src/CMakeLists.txt +++ b/src/coreclr/src/CMakeLists.txt @@ -12,7 +12,9 @@ if(CLR_CMAKE_TARGET_WIN32 AND FEATURE_EVENT_TRACE) include_directories("${GENERATED_INCLUDE_DIR}/etw") endif(CLR_CMAKE_TARGET_WIN32 AND FEATURE_EVENT_TRACE) -add_subdirectory(debug/dbgutil) +if (NOT CLR_CMAKE_TARGET_ARCH_WASM) + add_subdirectory(debug/dbgutil) +endif(NOT CLR_CMAKE_TARGET_ARCH_WASM) if(CLR_CMAKE_HOST_UNIX) if(CLR_CMAKE_BUILD_SUBSET_RUNTIME) @@ -64,28 +66,34 @@ if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSI add_compile_options(-Wno-error=stringop-overflow=) endif() -add_subdirectory(utilcode) -add_subdirectory(gcinfo) -add_subdirectory(jit) -add_subdirectory(inc) +if (NOT CLR_CMAKE_TARGET_ARCH_WASM) + add_subdirectory(utilcode) + add_subdirectory(gcinfo) + add_subdirectory(jit) + add_subdirectory(inc) +endif(NOT CLR_CMAKE_TARGET_ARCH_WASM) if(CLR_CMAKE_HOST_UNIX) add_subdirectory(palrt) endif(CLR_CMAKE_HOST_UNIX) -add_subdirectory(vm) +if (NOT CLR_CMAKE_TARGET_ARCH_WASM) + add_subdirectory(vm) +endif(NOT CLR_CMAKE_TARGET_ARCH_WASM) if (CLR_CMAKE_BUILD_SUBSET_RUNTIME) - add_subdirectory(md) - add_subdirectory(debug) - add_subdirectory(binder) - add_subdirectory(classlibnative) - add_subdirectory(dlls) - add_subdirectory(ToolBox) - add_subdirectory(tools) - add_subdirectory(unwinder) - add_subdirectory(ildasm) - add_subdirectory(ilasm) - add_subdirectory(interop) + if (NOT CLR_CMAKE_TARGET_ARCH_WASM) + add_subdirectory(md) + add_subdirectory(debug) + add_subdirectory(binder) + add_subdirectory(classlibnative) + add_subdirectory(dlls) + add_subdirectory(ToolBox) + add_subdirectory(tools) + add_subdirectory(unwinder) + add_subdirectory(ildasm) + add_subdirectory(ilasm) + add_subdirectory(interop) + endif(NOT CLR_CMAKE_TARGET_ARCH_WASM) if(CLR_CMAKE_HOST_WIN32) add_subdirectory(hosts) diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj index 3073cec10bdc..9a1f2f25d5b6 100644 --- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -132,12 +132,14 @@ + + diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Exception.CoreCLR.LLVM.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Exception.CoreCLR.LLVM.cs new file mode 100644 index 000000000000..345c91a44e55 --- /dev/null +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Exception.CoreCLR.LLVM.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System +{ + public partial class Exception + { + [DllImport("*")] + internal static extern void RhpThrowEx(object exception); + + private static void DispatchExLLVM(object exception) + { + AppendExceptionStackFrameLLVM(exception, new StackTrace(1).ToString()); + //RhpThrowEx(exception); can't as not handling the transition unmanaged->managed in the landing pads. + } + + private static void AppendExceptionStackFrameLLVM(object exceptionObj, string stackTraceString) + { + // This method is called by the runtime's EH dispatch code and is not allowed to leak exceptions + // back into the dispatcher. + try + { + Exception ex = exceptionObj as Exception; + if (ex == null) + Environment.FailFast("Exceptions must derive from the System.Exception class"); + + if (!RuntimeExceptionHelpers.SafeToPerformRichExceptionSupport) + return; + + ex._stackTraceString = stackTraceString.Replace("__", ".").Replace("_", "."); + } + catch + { + // We may end up with a confusing stack trace or a confusing ETW trace log, but at least we + // can continue to dispatch this exception. + } + } + } +} diff --git a/src/coreclr/src/gc/CMakeLists.txt b/src/coreclr/src/gc/CMakeLists.txt index c68bbcefc347..64cc19ba66b7 100644 --- a/src/coreclr/src/gc/CMakeLists.txt +++ b/src/coreclr/src/gc/CMakeLists.txt @@ -26,7 +26,7 @@ set( GC_SOURCES gcload.cpp handletablecache.cpp) -if(CLR_CMAKE_HOST_UNIX) +if(CLR_CMAKE_HOST_UNIX OR CLR_CMAKE_TARGET_ARCH_WASM) include(unix/configure.cmake) set ( GC_SOURCES ${GC_SOURCES} @@ -37,7 +37,7 @@ else() set ( GC_SOURCES ${GC_SOURCES} windows/gcenv.windows.cpp) -endif(CLR_CMAKE_HOST_UNIX) +endif(CLR_CMAKE_HOST_UNIX OR CLR_CMAKE_TARGET_ARCH_WASM) if (CLR_CMAKE_TARGET_ARCH_AMD64 AND CLR_CMAKE_TARGET_WIN32) set ( GC_SOURCES diff --git a/src/coreclr/src/gc/env/gcenv.base.h b/src/coreclr/src/gc/env/gcenv.base.h index 7132efae407c..e1b3fbd6b523 100644 --- a/src/coreclr/src/gc/env/gcenv.base.h +++ b/src/coreclr/src/gc/env/gcenv.base.h @@ -231,10 +231,10 @@ typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(void* lpThreadParameter); #define MemoryBarrier __sync_synchronize #endif // __aarch64__ -#ifdef __arm__ +#if defined(__arm__) || TARGET_WASM #define YieldProcessor() #define MemoryBarrier __sync_synchronize -#endif // __arm__ +#endif // __arm__ || TARGET_WASM #endif // _MSC_VER diff --git a/src/coreclr/src/gc/unix/config.gc.h.in b/src/coreclr/src/gc/unix/config.gc.h.in index dbcf506f91d2..9a12a23b24f2 100644 --- a/src/coreclr/src/gc/unix/config.gc.h.in +++ b/src/coreclr/src/gc/unix/config.gc.h.in @@ -30,6 +30,7 @@ #cmakedefine01 HAVE_SYSINFO_WITH_MEM_UNIT #cmakedefine01 HAVE_XSW_USAGE #cmakedefine01 HAVE_XSWDEV -#cmakedefine01 HAVE_NON_LEGACY_STATFS - +#if !TARGET_WASM // emscripten configure test passes for this, but the f_type on the statfs is not recognized and fails https://github.com/dotnet/runtimelab/blob/e5c4d674afdf758adf12052d64c120ce9030048b/src/coreclr/src/gc/unix/cgroup.cpp#L152 +#cmakedefine01 HAVE_NON_LEGACY_STATFS +#endif #endif // __CONFIG_H__ diff --git a/src/coreclr/src/gc/unix/configure.cmake b/src/coreclr/src/gc/unix/configure.cmake index 02c4725e538b..e7f7ee1f1201 100644 --- a/src/coreclr/src/gc/unix/configure.cmake +++ b/src/coreclr/src/gc/unix/configure.cmake @@ -1,3 +1,10 @@ +# Emscripten cmake requires these includes +include(CheckCXXSourceRuns) +include(CheckCXXSymbolExists) +include(CheckStructHasMember) +include(CheckFunctionExists) +include(CheckPrototypeDefinition) + check_include_files(sys/time.h HAVE_SYS_TIME_H) check_include_files(sys/mman.h HAVE_SYS_MMAN_H) check_include_files(numa.h HAVE_NUMA_H) diff --git a/src/coreclr/src/nativeaot/Bootstrap/main.cpp b/src/coreclr/src/nativeaot/Bootstrap/main.cpp index 7fda9a47b622..9cd13ed33d19 100644 --- a/src/coreclr/src/nativeaot/Bootstrap/main.cpp +++ b/src/coreclr/src/nativeaot/Bootstrap/main.cpp @@ -3,6 +3,10 @@ #include +#if defined HOST_WASM +#include +#endif // HOST_WASM + // // This is the mechanism whereby multiple linked modules contribute their global data for initialization at // startup of the application. @@ -143,13 +147,58 @@ extern "C" int __managed__Main(int argc, char* argv[]); extern "C" void __managed__Startup(); #endif // !CORERT_DLL +#if defined HOST_WASM +// Exception wrapper type that allows us to differentiate managed and native exceptions +class ManagedExceptionWrapper : std::exception +{ +public: + ManagedExceptionWrapper(void* pManagedException) + { + m_pManagedException = pManagedException; + } + +public: + void* m_pManagedException; +}; + +extern "C" void RhpThrowEx(void * pEx) +{ + throw ManagedExceptionWrapper(pEx); +} + +extern "C" void RhpThrowHwEx() +{ + throw "RhpThrowHwEx"; +} + +// returns the Leave target +extern "C" uint32_t LlvmCatchFunclet(void* pHandlerIP, void* pvRegDisplay); +extern "C" uint32_t RhpCallCatchFunclet(void * exceptionObj, void* pHandlerIP, void* pvRegDisplay, void *exInfo) +{ + return LlvmCatchFunclet(pHandlerIP, pvRegDisplay); +} + +extern "C" uint32_t LlvmFilterFunclet(void* pHandlerIP, void* pvRegDisplay); +extern "C" uint32_t RhpCallFilterFunclet(void* exceptionObj, void * pHandlerIP, void* shadowStack) +{ + return LlvmFilterFunclet(pHandlerIP, shadowStack); +} +extern "C" void LlvmFinallyFunclet(void *finallyHandler, void *shadowStack); +extern "C" void RhpCallFinallyFunclet(void *finallyHandler, void *shadowStack) +{ + LlvmFinallyFunclet(finallyHandler, shadowStack); +} +extern "C" void* RtRHeaderWrapper(); +#endif // HOST_WASM + static int InitializeRuntime() { if (!RhInitialize()) return -1; - // RhpEnableConservativeStackReporting(); - +#if defined HOST_WASM + RhpEnableConservativeStackReporting(); +#else void * osModule = PalGetModuleHandleFromPointer((void*)&CORERT_ENTRYPOINT); // TODO: pass struct with parameters instead of the large signature of RhRegisterOSModule @@ -161,8 +210,13 @@ static int InitializeRuntime() { return -1; } +#endif // HOST_WASM +#if defined HOST_WASM + InitializeModules(nullptr, (void**)RtRHeaderWrapper(), 1, (void **)&c_classlibFunctions, _countof(c_classlibFunctions)); +#else // !HOST_WASM InitializeModules(osModule, __modules_a, (int)((__modules_z - __modules_a)), (void **)&c_classlibFunctions, _countof(c_classlibFunctions)); +#endif #ifdef CORERT_DLL // Run startup method immediately for a native library diff --git a/src/coreclr/src/nativeaot/BuildIntegration/BuildFrameworkNativeObjects.proj b/src/coreclr/src/nativeaot/BuildIntegration/BuildFrameworkNativeObjects.proj index 49c4b2f75751..dd71595be86b 100644 --- a/src/coreclr/src/nativeaot/BuildIntegration/BuildFrameworkNativeObjects.proj +++ b/src/coreclr/src/nativeaot/BuildIntegration/BuildFrameworkNativeObjects.proj @@ -11,7 +11,7 @@ - + win osx linux-musl + browser-wasm linux - $(PortableRuntimeIdentifier)-$(PlatformTarget) - runtime.$(PortableRuntimeIdentifier).Microsoft.DotNet.ILCompiler + $(PortableRuntimeIdentifier)-$(PlatformTarget) + runtime.$(PortableRuntimeIdentifier).Microsoft.DotNet.ILCompiler.LLVM true diff --git a/src/coreclr/src/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Publish.targets b/src/coreclr/src/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Publish.targets index e520ffa4352d..9cf6b66744ad 100644 --- a/src/coreclr/src/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Publish.targets +++ b/src/coreclr/src/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Publish.targets @@ -53,9 +53,9 @@ - - - + + + @@ -74,6 +74,13 @@ + + + + + + + diff --git a/src/coreclr/src/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets b/src/coreclr/src/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets index da2ea40a4605..59c37a337873 100644 --- a/src/coreclr/src/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets +++ b/src/coreclr/src/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets @@ -14,14 +14,20 @@ The .NET Foundation licenses this file to you under the MIT license. --> + + + browser + wasm + + $(IntermediateOutputPath)native\ $(OutputPath)native\ true $(MSBuildThisFileDirectory)..\tools\netstandard\ILCompiler.Build.Tasks.dll - windows - OSX + windows + OSX $(OS) true @@ -55,6 +61,7 @@ The .NET Foundation licenses this file to you under the MIT license. .so .lib .a + .html .def .exports @@ -73,6 +80,7 @@ The .NET Foundation licenses this file to you under the MIT license. $(FrameworkLibPath)\Framework$(LibFileExt) $(FrameworkLibPath)\libframework$(LibFileExt) SetupProperties + true @@ -172,7 +180,7 @@ The .NET Foundation licenses this file to you under the MIT license. --> - + IntermediateOutputPath=$(IntermediateOutputPath); @@ -204,6 +212,8 @@ The .NET Foundation licenses this file to you under the MIT license. + + @@ -294,15 +304,34 @@ 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 + $(EmccArgs) "$(IlcPath)/sdk/libPortableRuntime.a" "$(IlcPath)/sdk/libbootstrapper.a" "$(IlcPath)/framework/libSystem.Native.a" $(EmccExtraArgs) + $(EmccArgs) -O2 -flto + $(EmccArgs) -g3 + + + + + + + + = _tryStartOffset) && + (idxTryLandingStart < _tryEndOffset)); + } + } + + // TODO: temporary to try things out, when working look to see how to refactor with FindFirstPassHandler + private static bool FindFirstPassHandlerWasm(object exception, uint idxStart, uint idxCurrentBlockStart /* the start IL idx of the current block for the landing pad, will use in place of PC */, + void* shadowStack, ref EHClauseIterator clauseIter, out uint tryRegionIdx, out byte* pHandler) + { + pHandler = (byte*)0; + tryRegionIdx = MaxTryRegionIdx; + uint lastTryStart = 0, lastTryEnd = 0; + RhEHClauseWasm ehClause = new RhEHClauseWasm(); + for (uint curIdx = 0; clauseIter.Next(ref ehClause); curIdx++) + { + // + // Skip to the starting try region. This is used by collided unwinds and rethrows to pickup where + // the previous dispatch left off. + // + if (idxStart != MaxTryRegionIdx) + { + if (curIdx <= idxStart) + { + lastTryStart = ehClause._tryStartOffset; + lastTryEnd = ehClause._tryEndOffset; + continue; + } + + // Now, we continue skipping while the try region is identical to the one that invoked the + // previous dispatch. + if ((ehClause._tryStartOffset == lastTryStart) && (ehClause._tryEndOffset == lastTryEnd)) + { + continue; + } + + // We are done skipping. This is required to handle empty finally block markers that are used + // to separate runs of different try blocks with same native code offsets. + idxStart = MaxTryRegionIdx; + } + + EHClauseIterator.RhEHClauseKindWasm clauseKind = ehClause._clauseKind; + if (((clauseKind != EHClauseIterator.RhEHClauseKindWasm.RH_EH_CLAUSE_TYPED) && + (clauseKind != EHClauseIterator.RhEHClauseKindWasm.RH_EH_CLAUSE_FILTER)) + || !ehClause.ContainsCodeOffset(idxCurrentBlockStart)) + { + continue; + } + + // Found a containing clause. Because of the order of the clauses, we know this is the + // most containing. + if (clauseKind == EHClauseIterator.RhEHClauseKindWasm.RH_EH_CLAUSE_TYPED) + { + if (ShouldTypedClauseCatchThisException(exception, (EEType*)ehClause._typeSymbol)) + { + pHandler = ehClause._handlerAddress; + tryRegionIdx = curIdx; + return true; + } + } + else + { + tryRegionIdx = 0; + bool shouldInvokeHandler = InternalCalls.RhpCallFilterFunclet(exception, ehClause._filterAddress, shadowStack); + if (shouldInvokeHandler) + { + pHandler = ehClause._handlerAddress; + tryRegionIdx = curIdx; + return true; + } + } + } + + return false; + } + + private static void InvokeSecondPassWasm(uint idxStart, uint idxTryLandingStart, ref EHClauseIterator clauseIter, uint idxLimit, void* shadowStack) + { + uint lastTryStart = 0, lastTryEnd = 0; + // Search the clauses for one that contains the current offset. + RhEHClauseWasm ehClause = new RhEHClauseWasm(); + for (uint curIdx = 0; clauseIter.Next(ref ehClause) && curIdx < idxLimit; curIdx++) + { + // + // Skip to the starting try region. This is used by collided unwinds and rethrows to pickup where + // the previous dispatch left off. + // + if (idxStart != MaxTryRegionIdx) + { + if (curIdx <= idxStart) + { + lastTryStart = ehClause._tryStartOffset; + lastTryEnd = ehClause._tryEndOffset; + continue; + } + + // Now, we continue skipping while the try region is identical to the one that invoked the + // previous dispatch. + if ((ehClause._tryStartOffset == lastTryStart) && (ehClause._tryEndOffset == lastTryEnd)) + continue; + + // We are done skipping. This is required to handle empty finally block markers that are used + // to separate runs of different try blocks with same native code offsets. + idxStart = MaxTryRegionIdx; + } + + EHClauseIterator.RhEHClauseKindWasm clauseKind = ehClause._clauseKind; + + if ((clauseKind != EHClauseIterator.RhEHClauseKindWasm.RH_EH_CLAUSE_FAULT) + || !ehClause.TryStartsAt(idxTryLandingStart)) + { + continue; + } + + // Found a containing clause. Because of the order of the clauses, we know this is the + // most containing. + + // N.B. -- We need to suppress GC "in-between" calls to finallys in this loop because we do + // not have the correct next-execution point live on the stack and, therefore, may cause a GC + // hole if we allow a GC between invocation of finally funclets (i.e. after one has returned + // here to the dispatcher, but before the next one is invoked). Once they are running, it's + // fine for them to trigger a GC, obviously. + // + // As a result, RhpCallFinallyFunclet will set this state in the runtime upon return from the + // funclet, and we need to reset it if/when we fall out of the loop and we know that the + // method will no longer get any more GC callbacks. + + byte* pFinallyHandler = ehClause._handlerAddress; + + InternalCalls.RhpCallFinallyFunclet(pFinallyHandler, shadowStack); + } + } + } // static class EH +} diff --git a/src/coreclr/src/nativeaot/Runtime.Base/src/System/Runtime/StackFrameIterator.wasm.cs b/src/coreclr/src/nativeaot/Runtime.Base/src/System/Runtime/StackFrameIterator.wasm.cs new file mode 100644 index 000000000000..96e4fffdcdb5 --- /dev/null +++ b/src/coreclr/src/nativeaot/Runtime.Base/src/System/Runtime/StackFrameIterator.wasm.cs @@ -0,0 +1,120 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Runtime +{ + internal unsafe struct EHClauseIterator + { + private uint _totalClauses; + private byte *_currentPtr; + private int _currentClause; + + private static uint DecodeUnsigned(ref byte* stream) + { + uint value = 0; + + uint val = *stream; + if ((val & 1) == 0) + { + value = (val >> 1); + stream += 1; + } + else if ((val & 2) == 0) + { + value = (val >> 2) | + (((uint)*(stream + 1)) << 6); + stream += 2; + } + else if ((val & 4) == 0) + { + value = (val >> 3) | + (((uint)*(stream + 1)) << 5) | + (((uint)*(stream + 2)) << 13); + stream += 3; + } + else if ((val & 8) == 0) + { + value = (val >> 4) | + (((uint)*(stream + 1)) << 4) | + (((uint)*(stream + 2)) << 12) | + (((uint)*(stream + 3)) << 20); + stream += 4; + } + else if ((val & 16) == 0) + { + stream += 1; + value = ReadUInt32(ref stream); + } + + // TODO : deleted all the error handling + return value; + } + + private static uint ReadUInt32(ref byte* stream) + { + uint result = *(uint*)(stream); // Assumes little endian and unaligned access + stream += 4; + return result; + } + + uint GetUnsigned() + { + uint value; + value = DecodeUnsigned(ref _currentPtr); + return value; + } + + internal void InitFromEhInfo(byte* ehInfoStart, byte* ehInfoEnd, int idxStart) + { + _currentPtr = ehInfoStart; + _currentClause = 0; + _totalClauses = GetUnsigned(); + } + + // TODO : copied from EH + internal enum RhEHClauseKindWasm + { + RH_EH_CLAUSE_TYPED = 0, + RH_EH_CLAUSE_FAULT = 1, + RH_EH_CLAUSE_FILTER = 2, + RH_EH_CLAUSE_UNUSED = 3, + } + + internal bool Next(ref EH.RhEHClauseWasm pEHClause) + { + if (_currentClause >= _totalClauses) return false; + + _currentClause++; + pEHClause._tryStartOffset = GetUnsigned(); + uint tryLengthAndKind = GetUnsigned(); + pEHClause._clauseKind = (RhEHClauseKindWasm)(tryLengthAndKind & 3); + pEHClause._tryEndOffset = (tryLengthAndKind >> 2) + pEHClause._tryStartOffset; + switch (pEHClause._clauseKind) + { + case RhEHClauseKindWasm.RH_EH_CLAUSE_TYPED: + + AlignToSymbol(); + pEHClause._typeSymbol = ReadUInt32(ref _currentPtr); + pEHClause._handlerAddress = (byte *)ReadUInt32(ref _currentPtr); + break; + case RhEHClauseKindWasm.RH_EH_CLAUSE_FAULT: + AlignToSymbol(); + pEHClause._handlerAddress = (byte*)ReadUInt32(ref _currentPtr); + break; + case RhEHClauseKindWasm.RH_EH_CLAUSE_FILTER: + AlignToSymbol(); + pEHClause._handlerAddress = (byte*)ReadUInt32(ref _currentPtr); + pEHClause._filterAddress = (byte*)ReadUInt32(ref _currentPtr); + break; + } + return true; + } + + private void AlignToSymbol() + { + _currentPtr = (byte *)(((uint)_currentPtr + 3) & ~(3)); + } + } +} diff --git a/src/coreclr/src/nativeaot/Runtime/Portable/CMakeLists.txt b/src/coreclr/src/nativeaot/Runtime/Portable/CMakeLists.txt index e1f09ed4127c..d7883ff4b961 100644 --- a/src/coreclr/src/nativeaot/Runtime/Portable/CMakeLists.txt +++ b/src/coreclr/src/nativeaot/Runtime/Portable/CMakeLists.txt @@ -17,7 +17,7 @@ if(WIN32) set(ASM_OFFSETS_CPP ${RUNTIME_DIR}/windows/AsmOffsets.cpp) else() set(COMPILER_LANGUAGE -x c++) - set(PREPROCESSOR_FLAGS -E -P) + set(PREPROCESSOR_FLAGS -E -P -C) # include license for code cop set(ASM_OFFSETS_CPP ${RUNTIME_DIR}/unix/AsmOffsets.cpp) endif() diff --git a/src/coreclr/src/nativeaot/Runtime/wasm/AsmOffsetsCpu.h b/src/coreclr/src/nativeaot/Runtime/wasm/AsmOffsetsCpu.h new file mode 100644 index 000000000000..23976f026fed --- /dev/null +++ b/src/coreclr/src/nativeaot/Runtime/wasm/AsmOffsetsCpu.h @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// This file is used by AsmOffsets.h to validate that our +// assembly-code offsets always match their C++ counterparts. +// +// NOTE: the offsets MUST be in hex notation WITHOUT the 0x prefix + +PLAT_ASM_SIZEOF(a4, ExInfo) +PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo) +PLAT_ASM_OFFSET(4, ExInfo, m_pExContext) +PLAT_ASM_OFFSET(8, ExInfo, m_exception) +PLAT_ASM_OFFSET(0c, ExInfo, m_kind) +PLAT_ASM_OFFSET(0d, ExInfo, m_passNumber) +PLAT_ASM_OFFSET(10, ExInfo, m_idxCurClause) +PLAT_ASM_OFFSET(14, ExInfo, m_frameIter) +PLAT_ASM_OFFSET(a0, ExInfo, m_notifyDebuggerSP) + +PLAT_ASM_SIZEOF(8c, StackFrameIterator) +PLAT_ASM_OFFSET(08, StackFrameIterator, m_FramePointer) +PLAT_ASM_OFFSET(0c, StackFrameIterator, m_ControlPC) +PLAT_ASM_OFFSET(10, StackFrameIterator, m_RegDisplay) +PLAT_ASM_OFFSET(88, StackFrameIterator, m_OriginalControlPC) + +PLAT_ASM_SIZEOF(4, PAL_LIMITED_CONTEXT) +PLAT_ASM_OFFSET(0, PAL_LIMITED_CONTEXT, IP) + +PLAT_ASM_SIZEOF(0c, REGDISPLAY) +PLAT_ASM_OFFSET(0, REGDISPLAY, SP) diff --git a/src/coreclr/src/nativeaot/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/nativeaot/System.Private.CoreLib/System.Private.CoreLib.csproj index 42024ce857b1..14379be924c6 100644 --- a/src/coreclr/src/nativeaot/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/src/nativeaot/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -26,16 +26,20 @@ false - true + true false - true + true true true + + TARGET_UNIX;TARGET_WASM;TARGET_32BIT;FEATURE_64BIT_ALIGNMENT;$(DefineConstants) + llvm + @@ -168,7 +172,7 @@ - + @@ -181,6 +185,7 @@ + @@ -224,6 +229,7 @@ + @@ -323,7 +329,7 @@ Interop\Windows\Kernel32\Interop.GetLastError.cs - + @@ -469,9 +475,17 @@ Common\TransitionBlock.cs + + Runtime.Base\src\System\Runtime\ExceptionHandling.wasm.cs + + + Runtime.Base\src\System\Runtime\StackFrameIterator.wasm.cs + - + + + diff --git a/src/coreclr/src/nativeaot/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreRT.Wasm.cs b/src/coreclr/src/nativeaot/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreRT.Wasm.cs new file mode 100644 index 000000000000..1537709b6d91 --- /dev/null +++ b/src/coreclr/src/nativeaot/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreRT.Wasm.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text; +using System.Runtime.InteropServices; + +namespace System.Diagnostics +{ + public partial class StackTrace + { + private readonly StringBuilder _builder = new StringBuilder(); + + [DllImport("*")] + static unsafe extern int emscripten_get_callstack(int flags, byte* outBuf, int maxBytes); + + private unsafe void InitializeForCurrentThread(int skipFrames, bool needFileInfo) + { + var backtraceBuffer = new byte[8192]; + int callstackLen; + // skip these 2: + // at S_P_CoreLib_System_Diagnostics_StackTrace__InitializeForCurrentThread (wasm-function[12314]:275) + // at S_P_CoreLib_System_Diagnostics_StackTrace___ctor_0(wasm-function[12724]:118) + skipFrames += 2; // METHODS_TO_SKIP is a constant so just change here + + fixed (byte* curChar = backtraceBuffer) + { + callstackLen = emscripten_get_callstack(8 /* original source stack if source maps available, not tested */, curChar, backtraceBuffer.Length); + } + int _numOfFrames = 1; + int lineStartIx = 0; + int ix = 0; + for (; ix < callstackLen; ix++) + { + if (backtraceBuffer[ix] == '\n') + { + if (_numOfFrames > skipFrames) + { + _builder.Append(Encoding.Default.GetString(backtraceBuffer, lineStartIx, ix - lineStartIx + 1)); + } + _numOfFrames++; + lineStartIx = ix + 1; + } + } + if (lineStartIx < ix) + { + _builder.AppendLine(Encoding.Default.GetString(backtraceBuffer, lineStartIx, ix - lineStartIx)); + } + _methodsToSkip = 0; + } + + + internal string ToString(TraceFormat traceFormat) + { + var stackTraceString = _builder.ToString(); + if (traceFormat == TraceFormat.Normal && stackTraceString.EndsWith(Environment.NewLine)) + return stackTraceString.Substring(0, stackTraceString.Length - Environment.NewLine.Length); + + return stackTraceString; + } + } +} diff --git a/src/coreclr/src/nativeaot/System.Private.CoreLib/src/System/Exception.CoreRT.LLVM.cs b/src/coreclr/src/nativeaot/System.Private.CoreLib/src/System/Exception.CoreRT.LLVM.cs new file mode 100644 index 000000000000..345c91a44e55 --- /dev/null +++ b/src/coreclr/src/nativeaot/System.Private.CoreLib/src/System/Exception.CoreRT.LLVM.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System +{ + public partial class Exception + { + [DllImport("*")] + internal static extern void RhpThrowEx(object exception); + + private static void DispatchExLLVM(object exception) + { + AppendExceptionStackFrameLLVM(exception, new StackTrace(1).ToString()); + //RhpThrowEx(exception); can't as not handling the transition unmanaged->managed in the landing pads. + } + + private static void AppendExceptionStackFrameLLVM(object exceptionObj, string stackTraceString) + { + // This method is called by the runtime's EH dispatch code and is not allowed to leak exceptions + // back into the dispatcher. + try + { + Exception ex = exceptionObj as Exception; + if (ex == null) + Environment.FailFast("Exceptions must derive from the System.Exception class"); + + if (!RuntimeExceptionHelpers.SafeToPerformRichExceptionSupport) + return; + + ex._stackTraceString = stackTraceString.Replace("__", ".").Replace("_", "."); + } + catch + { + // We may end up with a confusing stack trace or a confusing ETW trace log, but at least we + // can continue to dispatch this exception. + } + } + } +} diff --git a/src/coreclr/src/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.cs b/src/coreclr/src/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.cs index 093c764aaf2f..8817da165012 100644 --- a/src/coreclr/src/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.cs +++ b/src/coreclr/src/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.cs @@ -102,7 +102,9 @@ private static extern unsafe void CallingConventionConverter_GetStubs(out IntPtr out IntPtr commonStub #if CALLDESCR_FPARGREGSARERETURNREGS #else +#pragma warning disable SA1001,SA1115,SA1113 , out IntPtr returnFloatingPointReturn4Thunk, +#pragma warning restore SA10011,SA1115,SA1113 out IntPtr returnFloatingPointReturn8Thunk #endif ); diff --git a/src/coreclr/src/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj b/src/coreclr/src/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj index c8badf6365bd..7324dfcd6d69 100644 --- a/src/coreclr/src/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj +++ b/src/coreclr/src/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj @@ -19,6 +19,9 @@ $(CompilerCommonPath)\Internal\NativeFormat + + TARGET_UNIX;TARGET_WASM;TARGET_32BIT;$(DefineConstants) + diff --git a/src/coreclr/src/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj b/src/coreclr/src/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj index ffb31c08312b..6e315e602c6c 100644 --- a/src/coreclr/src/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj +++ b/src/coreclr/src/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj @@ -12,9 +12,11 @@ FEATURE_64BIT_ALIGNMENT;$(DefineConstants) - - FEATURE_64BIT_ALIGNMENT;$(DefineConstants) + + TARGET_WASM;TARGET_32BIT;FEATURE_64BIT_ALIGNMENT;$(DefineConstants) + llvm + true @@ -72,7 +74,8 @@ - + + diff --git a/src/coreclr/src/tools/aot/ILCompiler.LLVM/CodeGen/DebugMetadata.cs b/src/coreclr/src/tools/aot/ILCompiler.LLVM/CodeGen/DebugMetadata.cs new file mode 100644 index 000000000000..29b66e8a0484 --- /dev/null +++ b/src/coreclr/src/tools/aot/ILCompiler.LLVM/CodeGen/DebugMetadata.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using LLVMSharp.Interop; + +namespace ILCompiler.LLVM +{ + class DebugMetadata + { + public DebugMetadata(LLVMMetadataRef file, LLVMMetadataRef compileUnit) + { + File = file; + CompileUnit = compileUnit; + } + + public LLVMMetadataRef CompileUnit { get; } + public LLVMMetadataRef File { get; } + } +} diff --git a/src/coreclr/src/tools/aot/ILCompiler.LLVM/CodeGen/EvaluationStack.cs b/src/coreclr/src/tools/aot/ILCompiler.LLVM/CodeGen/EvaluationStack.cs new file mode 100644 index 000000000000..685622d2fcb7 --- /dev/null +++ b/src/coreclr/src/tools/aot/ILCompiler.LLVM/CodeGen/EvaluationStack.cs @@ -0,0 +1,596 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using Internal.TypeSystem; +using LLVMSharp.Interop; + +namespace Internal.IL +{ + /// + /// Abstraction of a variable size last-in-first-out (LIFO) collection of instances of the same specified type + /// implemented via an array. + /// + /// Type of elements in the stack. + internal class EvaluationStack + { + /// + /// Initializes a new instance of the stack that is empty and has the specified initial capacity . + /// + /// Initial number of elements that the stack can contain. + public EvaluationStack(int n) + { + Debug.Assert(n >= 0, "Count should be non-negative"); + + _stack = n > 0 ? new T[n] : s_emptyStack; + _top = 0; + + Debug.Assert(n == _stack.Length, "Stack length does not match requested capacity"); + Debug.Assert(_top == 0, "Top of stack is at bottom"); + } + + /// + /// Value for all stacks of length 0. + /// + private static readonly T[] s_emptyStack = new T[0]; + + /// + /// Storage for current stack. + /// + private T[] _stack; + + /// + /// Position in where next element will be pushed. + /// + private int _top; + + /// + /// Position in stack where next element will be pushed. + /// + public int Top + { + get { return _top; } + } + + /// + /// Number of elements contained in the stack. + /// + public int Length + { + get { return _top; } + } + + /// + /// Push at the top of the stack. + /// + /// Element to push onto the stack. + public void Push(T value) + { + if (_top >= _stack.Length) + { + Array.Resize(ref _stack, 2 * _top + 3); + } + _stack[_top++] = value; + } + + /// + /// Insert at position in current stack, shifting all + /// elements after or at by one. + /// + /// Element to insert + /// Position where to insert + public void InsertAt(T v, int pos) + { + Debug.Assert(pos <= _top, "Invalid insertion point"); + + if (_top >= _stack.Length) + { + Array.Resize(ref _stack, 2 * _top + 3); + } + for (int i = _top - 1; i >= pos; i--) + { + _stack[i + 1] = _stack[i]; + } + _top++; + _stack[pos] = v; + } + + /// + /// Access and set + /// + /// + /// + public T this[int index] + { + get + { + Debug.Assert(index >= 0 && index < _top, "Index not in range"); + return _stack[index]; + } + set + { + Debug.Assert(index >= 0 && index < _top, "Index not in range"); + _stack[index] = value; + } + } + + /// + /// Return element of the top of the stack. + /// + /// Element at the top of the stack + public T Peek() + { + if (_top <= 0) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + return _stack[_top - 1]; + } + + /// + /// Remove top element from stack and return it. + /// + /// Element formerly at the top of the stack + public T Pop() + { + if (_top <= 0) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + return _stack[--_top]; + } + + /// + /// Remove elements from the stack. + /// + /// Number of elements to remove from the stack + public void PopN(int n) + { + Debug.Assert(n <= _top, "Too many elements to remove"); + _top -= n; + } + + /// + /// Remove all elements from the stack. + /// + public void Clear() + { + _top = 0; + } + } + + /// + /// Abstract representation of a stack entry + /// + internal abstract class StackEntry + { + /// + /// Evaluation stack kind of the entry. + /// + public StackValueKind Kind { get; } + + /// + /// Managed type if any of the entry. + /// + public TypeDesc Type { get; } + + public LLVMValueRef ValueAsType(LLVMTypeRef type, LLVMBuilderRef builder) + { + return ValueAsTypeInternal(type, builder, Type != null && (Type.IsWellKnownType(WellKnownType.SByte) || Type.IsWellKnownType(WellKnownType.Int16))); + } + + public LLVMValueRef ValueAsType(TypeDesc type, LLVMBuilderRef builder) + { + return ValueAsType(ILImporter.GetLLVMTypeForTypeDesc(type), builder); + } + + public LLVMValueRef ValueForStackKind(StackValueKind kind, LLVMBuilderRef builder, bool signExtend) + { + if (kind == StackValueKind.Int32) + return ValueAsInt32(builder, signExtend); + else if (kind == StackValueKind.Int64) + return ValueAsInt64(builder, signExtend); + else if (kind == StackValueKind.Float) + return ValueAsType(Type.IsWellKnownType(WellKnownType.Single) ? ILImporter.Context.FloatType : ILImporter.Context.DoubleType, builder); + else if (kind == StackValueKind.NativeInt || kind == StackValueKind.ByRef || kind == StackValueKind.ObjRef) + return ValueAsInt32(builder, false); + else + throw new NotImplementedException(); + } + + public LLVMValueRef ValueAsInt32(LLVMBuilderRef builder, bool signExtend) + { + return ValueAsTypeInternal(ILImporter.Context.Int32Type, builder, signExtend); + } + + public LLVMValueRef ValueAsInt64(LLVMBuilderRef builder, bool signExtend) + { + return ValueAsTypeInternal(ILImporter.Context.Int64Type, builder, signExtend); + } + + protected abstract LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend); + + /// + /// Initializes a new instance of StackEntry. + /// + /// Kind of entry. + /// Type if any of entry. + protected StackEntry(StackValueKind kind, TypeDesc type = null) + { + Kind = kind; + Type = type; + } + + /// + /// Add representation of current entry in . + /// + /// Generation buffer used for appending new content. + //public abstract void Append(CppGenerationBuffer builder); + + /// + /// Create a new copy of current entry. + /// + /// A new instance of the same type as the current entry. + public abstract StackEntry Duplicate(LLVMBuilderRef builder); + } + + /// + /// Abstract entry for all constant values. + /// + internal abstract class ConstantEntry : StackEntry + { + protected ConstantEntry(StackValueKind kind, TypeDesc type = null) : base(kind, type) + { + } + + /// + /// Does current entry require a cast to be assigned to ? + /// + /// Type of destination + /// True if a cast is required + public virtual bool IsCastNecessary(TypeDesc destType) + { + return false; + } + } + + internal abstract class ConstantEntry : ConstantEntry where T : IConvertible + { + public T Value { get; } + + protected ConstantEntry(StackValueKind kind, T value, TypeDesc type = null) : base(kind, type) + { + Value = value; + } + } + + internal class Int32ConstantEntry : ConstantEntry + { + public Int32ConstantEntry(int value, TypeDesc type = null) : base(StackValueKind.Int32, value, type) + { + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + if (type.Kind == LLVMTypeKind.LLVMPointerTypeKind && Value == 0) + { + return LLVMValueRef.CreateConstPointerNull(type); + } + else if (type.Kind == LLVMTypeKind.LLVMPointerTypeKind && Value != 0) + { + return LLVMValueRef.CreateConstIntToPtr(LLVMValueRef.CreateConstInt(ILImporter.Context.Int32Type, (ulong)Value), type); + } + else if (type.Kind != LLVMTypeKind.LLVMIntegerTypeKind) + { + throw new NotImplementedException(); + } + else + { + return LLVMValueRef.CreateConstInt(type, (ulong)Value); + } + } + + public override StackEntry Duplicate(LLVMBuilderRef builder) + { + return new Int32ConstantEntry(Value, Type); + } + + public override bool IsCastNecessary(TypeDesc destType) + { + switch (destType.UnderlyingType.Category) + { + case TypeFlags.SByte: + return Value >= sbyte.MaxValue || Value <= sbyte.MinValue; + case TypeFlags.Byte: + case TypeFlags.Boolean: + return Value >= byte.MaxValue || Value < 0; + case TypeFlags.Int16: + return Value >= short.MaxValue || Value <= short.MinValue; + case TypeFlags.UInt16: + case TypeFlags.Char: + return Value >= ushort.MaxValue || Value < 0; + case TypeFlags.Int32: + return false; + case TypeFlags.UInt32: + return Value < 0; + default: + return true; + } + } + } + + internal class Int64ConstantEntry : ConstantEntry + { + public Int64ConstantEntry(long value, TypeDesc type = null) : base(StackValueKind.Int64, value, type) + { + } + + public override StackEntry Duplicate(LLVMBuilderRef builder) + { + return new Int64ConstantEntry(Value, Type); + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + if (type.Kind == LLVMTypeKind.LLVMPointerTypeKind && Value == 0) + { + return LLVMValueRef.CreateConstPointerNull(type); + } + else if (type.Kind == LLVMTypeKind.LLVMPointerTypeKind && Value != 0) + { + return LLVMValueRef.CreateConstIntToPtr(LLVMValueRef.CreateConstInt(ILImporter.Context.Int64Type, (ulong)Value), type); + } + else if (type.Kind != LLVMTypeKind.LLVMIntegerTypeKind) + { + throw new NotImplementedException(); + } + else + { + return LLVMValueRef.CreateConstInt(type, (ulong)Value); + } + } + + public override bool IsCastNecessary(TypeDesc destType) + { + switch (destType.UnderlyingType.Category) + { + case TypeFlags.SByte: + return Value >= sbyte.MaxValue || Value <= sbyte.MinValue; + case TypeFlags.Byte: + case TypeFlags.Boolean: + return Value >= byte.MaxValue || Value < 0; + case TypeFlags.Int16: + return Value >= short.MaxValue || Value <= short.MinValue; + case TypeFlags.UInt16: + case TypeFlags.Char: + return Value >= ushort.MaxValue || Value < 0; + case TypeFlags.Int32: + return Value >= int.MaxValue || Value <= int.MinValue; + case TypeFlags.UInt32: + return Value >= uint.MaxValue || Value < 0; + case TypeFlags.Int64: + return false; + case TypeFlags.UInt64: + return Value < 0; + default: + return true; + } + } + } + + internal class FloatConstantEntry : ConstantEntry + { + public FloatConstantEntry(double value, TypeDesc type = null) : base(StackValueKind.Float, value, type) + { + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + return LLVMValueRef.CreateConstReal(type, Value); + } + + public override StackEntry Duplicate(LLVMBuilderRef builder) + { + return new FloatConstantEntry(Value, Type); + } + } + + /// + /// Entry representing some expression + /// + internal class ExpressionEntry : StackEntry + { + /// + /// String representation of current expression + /// + public string Name { get; set; } + public LLVMValueRef RawLLVMValue { get; set; } + /// + /// Initializes new instance of ExpressionEntry + /// + /// Kind of entry + /// String representation of entry + /// Type if any of entry + public ExpressionEntry(StackValueKind kind, string name, LLVMValueRef llvmValue, TypeDesc type = null) : base(kind, type) + { + Name = name; + RawLLVMValue = llvmValue; + } + + public override StackEntry Duplicate(LLVMBuilderRef builder) + { + return new ExpressionEntry(Kind, Name, RawLLVMValue, Type); + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + if (type.IsPackedStruct && type.Handle != RawLLVMValue.TypeOf.Handle) + { + var destStruct = type.Undef; + for (uint elemNo = 0; elemNo < RawLLVMValue.TypeOf.StructElementTypesCount; elemNo++) + { + var elemValRef = builder.BuildExtractValue(RawLLVMValue, 0, "ex" + elemNo); + destStruct = builder.BuildInsertValue(destStruct, elemValRef, elemNo, "st" + elemNo); + } + return destStruct; + } + return ILImporter.CastIfNecessary(builder, RawLLVMValue, type, Name, !signExtend); + } + } + + internal class LoadExpressionEntry : ExpressionEntry + { + /// + /// Initializes new instance of ExpressionEntry + /// + /// Kind of entry + /// String representation of entry + /// Type if any of entry + public LoadExpressionEntry(StackValueKind kind, string name, LLVMValueRef llvmValue, TypeDesc type = null) : base(kind, name, llvmValue, type) + { + } + + public override StackEntry Duplicate(LLVMBuilderRef builder) + { + return new ExpressionEntry(Kind, "duplicate_" + Name, ILImporter.LoadValue(builder, RawLLVMValue, Type, ILImporter.GetLLVMTypeForTypeDesc(Type), false, "load_duplicate_" + Name), Type); + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + return ILImporter.LoadValue(builder, RawLLVMValue, Type, type, signExtend, $"Load{Name}"); + } + } + + internal class AddressExpressionEntry : ExpressionEntry + { + /// + /// Initializes new instance of ExpressionEntry + /// + /// Kind of entry + /// String representation of entry + /// Type if any of entry + public AddressExpressionEntry(StackValueKind kind, string name, LLVMValueRef llvmValue, TypeDesc type = null) : base(kind, name, llvmValue, type) + { + } + + public override StackEntry Duplicate(LLVMBuilderRef builder) + { + return new AddressExpressionEntry(Kind, Name, RawLLVMValue, Type); + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + return ILImporter.CastIfNecessary(builder, RawLLVMValue, type, Name); + } + } + + /// + /// Represents the result of a ldftn or ldvirtftn + /// + internal class FunctionPointerEntry : ExpressionEntry + { + /// + /// True if the function pointer was loaded as a virtual function pointer + /// + public bool IsVirtual { get; } + + public MethodDesc Method { get; } + + public FunctionPointerEntry(string name, MethodDesc method, LLVMValueRef llvmValue, TypeDesc type, bool isVirtual) : base(StackValueKind.NativeInt, name, llvmValue, type) + { + Method = method; + IsVirtual = isVirtual; + } + + public override StackEntry Duplicate(LLVMBuilderRef builder) + { + return new FunctionPointerEntry(Name, Method, RawLLVMValue, Type, IsVirtual); + } + } + + /// + /// Entry representing some token (either of TypeDesc, MethodDesc or FieldDesc) along with its string representation + /// + internal class LdTokenEntry : ExpressionEntry + { + public T LdToken { get; } + + public LdTokenEntry(StackValueKind kind, string name, T token, LLVMValueRef llvmValue, TypeDesc type = null) : base(kind, name, llvmValue, type) + { + LdToken = token; + } + + public override StackEntry Duplicate(LLVMBuilderRef builder) + { + return new LdTokenEntry(Kind, Name, LdToken, RawLLVMValue, Type); + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + if (RawLLVMValue.Handle == IntPtr.Zero) + throw new NullReferenceException(); + + return ILImporter.CastIfNecessary(builder, RawLLVMValue, type, Name); + } + } + + internal class InvalidEntry : StackEntry + { + /// + /// Entry to use to get an instance of InvalidEntry. + /// + public static InvalidEntry Entry = new InvalidEntry(); + + protected InvalidEntry() : base(StackValueKind.Unknown, null) + { + } + + public override StackEntry Duplicate(LLVMBuilderRef builder) + { + return this; + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + throw new InvalidOperationException(); + } + } + + /// + /// Entry representing a writable sharable stack entry that can survive from one basic block to another + /// + internal class SpilledExpressionEntry : ExpressionEntry + { + public int LocalIndex; + private ILImporter _importer; + public SpilledExpressionEntry(StackValueKind kind, string name, TypeDesc type, int localIndex, ILImporter importer) : base(kind, name, new LLVMValueRef(IntPtr.Zero), type) + { + LocalIndex = localIndex; + _importer = importer; + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + LLVMTypeRef origLLVMType = ILImporter.GetLLVMTypeForTypeDesc(Type); + LLVMValueRef value = _importer.LoadTemp(LocalIndex, origLLVMType); + + return ILImporter.CastIfNecessary(builder, value, type, unsigned: !signExtend); + } + + public override StackEntry Duplicate(LLVMBuilderRef builder) + { + return new SpilledExpressionEntry(Kind, Name, Type, LocalIndex, _importer); + } + } + + internal static class StackEntryExtensions + { + public static string Name(this StackEntry entry) + { + return (entry as ExpressionEntry)?.Name; + } + } +} diff --git a/src/coreclr/src/tools/aot/ILCompiler.LLVM/CodeGen/ILToLLVMImporter.cs b/src/coreclr/src/tools/aot/ILCompiler.LLVM/CodeGen/ILToLLVMImporter.cs new file mode 100644 index 000000000000..816b800622b3 --- /dev/null +++ b/src/coreclr/src/tools/aot/ILCompiler.LLVM/CodeGen/ILToLLVMImporter.cs @@ -0,0 +1,5648 @@ +// 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.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Linq; +using Internal.TypeSystem; +using ILCompiler; +using LLVMSharp.Interop; +using ILCompiler.Compiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysis; +using ILCompiler.LLVM; +using Internal.IL.Stubs; +using Internal.TypeSystem.Ecma; + +namespace Internal.IL +{ + // Implements an IL scanner that scans method bodies to be compiled by the code generation + // backend before the actual compilation happens to gain insights into the code. + partial class ILImporter + { + public enum LocalVarKind + { + Argument, + Local, + Temp + } + + private class ExceptionRegion + { + public ILExceptionRegion ILRegion; + } + + ArrayBuilder _dependencies = new ArrayBuilder(); + public IEnumerable GetDependencies() + { + return _dependencies.ToArray(); + } + + public LLVMModuleRef Module { get; } + public static LLVMContextRef Context { get; private set; } + private static Dictionary LlvmStructs { get; } = new Dictionary(); + private readonly MethodDesc _method; + private readonly MethodIL _methodIL; + private readonly MethodIL _canonMethodIL; + private readonly MethodSignature _signature; + private readonly TypeDesc _thisType; + private readonly LLVMCodegenCompilation _compilation; + private readonly string _mangledName; + private LLVMValueRef _llvmFunction; + private LLVMValueRef _currentFunclet; + private bool _isUnboxingThunk; + private LLVMBasicBlockRef _curBasicBlock; + private LLVMBuilderRef _builder; + private readonly LocalVariableDefinition[] _locals; + private readonly LLVMValueRef[] _localSlots; + private readonly LLVMValueRef[] _argSlots; + private List _spilledExpressions = new List(); + private readonly int _pointerSize; + private readonly byte[] _ilBytes; + private MethodDebugInformation _debugInformation; + private LLVMMetadataRef _debugFunction; + private TypeDesc _constrainedType = null; + private List _exceptionFunclets; + private List _leaveTargets; + private readonly Dictionary, LLVMBasicBlockRef> _landingPads; + private readonly Dictionary _funcletUnreachableBlocks = new Dictionary(); + private readonly Dictionary _funcletResumeBlocks = new Dictionary(); + private readonly EHInfoNode _ehInfoNode; + private AddressCacheContext _funcletAddrCacheCtx; + + /// + /// Stack of values pushed onto the IL stack: locals, arguments, values, function pointer, ... + /// + private EvaluationStack _stack = new EvaluationStack(0); + + private class BasicBlock + { + // Common fields + public enum ImportState : byte + { + Unmarked, + IsPending + } + + public BasicBlock Next; + + public int StartOffset; + public ImportState State = ImportState.Unmarked; + + public EvaluationStack EntryStack; + + public bool TryStart; + public bool FilterStart; + public bool HandlerStart; + + public LLVMBasicBlockRef Block; + public LLVMBasicBlockRef LastInternalBlock; + public readonly List LLVMBlocks = new List(1); + public LLVMBasicBlockRef LastBlock => LastInternalBlock.Handle == IntPtr.Zero ? Block : LastInternalBlock; + } + + private ExceptionRegion[] _exceptionRegions; + public ILImporter(LLVMCodegenCompilation compilation, MethodDesc method, MethodIL methodIL, string mangledName, bool isUnboxingThunk) + { + Module = compilation.Module; + _compilation = compilation; + _method = method; + _isUnboxingThunk = isUnboxingThunk; + // stubs for Unix calls which are not available to this target yet + if ((method.OwningType as EcmaType)?.Name == "Interop" && method.Name == "GetRandomBytes") + { + // this would normally fill the buffer parameter, but we'll just leave the buffer as is and that will be our "random" data for now + methodIL = new ILStubMethodIL(method, new byte[] { (byte)ILOpcode.ret }, Array.Empty(), null); + } + else if ((method.OwningType as EcmaType)?.Name == "CalendarData" && method.Name == "EnumCalendarInfo") + { + // just return false + methodIL = new ILStubMethodIL(method, new byte[] { (byte)ILOpcode.ldc_i4_0, (byte)ILOpcode.ret }, Array.Empty(), null); + } + + _canonMethodIL = methodIL; + // Get the runtime determined method IL so that this works right in shared code + // and tokens in shared code resolve to runtime determined types. + MethodIL uninstantiatiedMethodIL = methodIL.GetMethodILDefinition(); + if (methodIL != uninstantiatiedMethodIL) + { + MethodDesc sharedMethod = method.GetSharedRuntimeFormMethodTarget(); + _methodIL = new InstantiatedMethodIL(sharedMethod, uninstantiatiedMethodIL); + } + else + { + _methodIL = methodIL; + } + + _mangledName = mangledName; + _ilBytes = methodIL.GetILBytes(); + _locals = methodIL.GetLocals(); + _localSlots = new LLVMValueRef[_locals.Length]; + _argSlots = new LLVMValueRef[method.Signature.Length]; + _signature = method.Signature; + _thisType = method.OwningType; + var ilExceptionRegions = methodIL.GetExceptionRegions(); + _exceptionRegions = new ExceptionRegion[ilExceptionRegions.Length]; + if (ilExceptionRegions.Length != 0) + { + _exceptionFunclets = new List(_exceptionRegions.Length); + _landingPads = new Dictionary, LLVMBasicBlockRef>(); + _ehInfoNode = new EHInfoNode(_mangledName); + } + int curRegion = 0; + foreach (ILExceptionRegion region in ilExceptionRegions.OrderBy(region => region.TryOffset) + .ThenByDescending(region => region.TryLength) // outer regions with the same try offset as inner region first - they will have longer lengths, // WASMTODO, except maybe an inner of try {} catch {} which could still be a problem + .ThenBy(region => region.HandlerOffset)) + { + _exceptionRegions[curRegion++] = new ExceptionRegion + { + ILRegion = region + }; + } + + _llvmFunction = GetOrCreateLLVMFunction(mangledName, method.Signature, method.RequiresInstArg()); + _currentFunclet = _llvmFunction; + _pointerSize = compilation.NodeFactory.Target.PointerSize; + + _debugInformation = _compilation.GetDebugInfo(_methodIL); + + Context = Module.Context; + _builder = Context.CreateBuilder(); + } + + public void Import() + { + FindBasicBlocks(); + + GenerateProlog(); + + try + { + ImportBasicBlocks(); + } + catch + { + LLVMBasicBlockRef trapBlock = _llvmFunction.AppendBasicBlock("Trap"); + + // Change the function body to trap + foreach (BasicBlock block in _basicBlocks) + { + if (block != null) + { + foreach (LLVMBasicBlockRef llvmBlock in block.LLVMBlocks) + { + if (llvmBlock.Handle != IntPtr.Zero) + { + llvmBlock.AsValue().ReplaceAllUsesWith(trapBlock.AsValue()); + llvmBlock.RemoveFromParent(); + } + } + } + } + + if (_exceptionFunclets != null) + { + foreach (LLVMValueRef funclet in _exceptionFunclets) + { + funclet.DeleteFunction(); + } + } + + _builder.PositionAtEnd(trapBlock); + EmitTrapCall(); + throw; + } + finally + { + // Generate thunk for runtime exports + if ((_method.IsRuntimeExport || _method.IsUnmanagedCallersOnly) && _method is EcmaMethod) // TODO: Reverse delegate invokes probably need something here, but what would be the export name? + { + EcmaMethod ecmaMethod = ((EcmaMethod)_method); + string exportName = ecmaMethod.IsRuntimeExport ? ecmaMethod.GetRuntimeExportName() : ecmaMethod.GetUnmanagedCallersOnlyExportName(); + if (exportName == null) + { + exportName = ecmaMethod.Name; + } + + EmitNativeToManagedThunk(_compilation, _method, exportName, _llvmFunction); + } + } + } + + private void GenerateProlog() + { + // Avoid appearing to be in any exception regions + _currentOffset = -1; + + LLVMBuilderRef prologBuilder = Context.CreateBuilder(); + LLVMBasicBlockRef prologBlock = _llvmFunction.AppendBasicBlock("Prolog"); + prologBuilder.PositionAtEnd(prologBlock); + // Copy arguments onto the stack to allow + // them to be referenced by address + int thisOffset = 0; + if (!_signature.IsStatic) + { + thisOffset = 1; + } + _funcletAddrCacheCtx = new AddressCacheContext + { + // sparsely populated, args on LLVM stack not in here + ArgAddresses = new LLVMValueRef[thisOffset + _signature.Length], + LocalAddresses = new LLVMValueRef[_locals.Length], + TempAddresses = new List(), + PrologBuilder = prologBuilder + }; + // Allocate slots to store exception being dispatched and generic context if present + if (_exceptionRegions.Length > 0) + { + _spilledExpressions.Add(new SpilledExpressionEntry(StackValueKind.ObjRef, "ExceptionSlot", GetWellKnownType(WellKnownType.Object), 0, this)); + // clear any uncovered object references for GC.Collect + ImportCallMemset(LoadVarAddress(0, LocalVarKind.Temp, out TypeDesc _, prologBuilder), 0, LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)_pointerSize), prologBuilder); + // and a slot for the generic context if present + if (FuncletsRequireHiddenContext()) + { + var genCtx = new SpilledExpressionEntry(StackValueKind.ObjRef, "GenericCtxSlot", GetWellKnownType(WellKnownType.IntPtr), 1, this); + _spilledExpressions.Add(genCtx); + // put the generic context in the slot for reference by funclets + var addressValue = CastIfNecessary(prologBuilder, LoadVarAddress(genCtx.LocalIndex, LocalVarKind.Temp, _funcletAddrCacheCtx, out TypeDesc unused, prologBuilder), + LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), 0)); + prologBuilder.BuildStore(_llvmFunction.GetParam(GetHiddenContextParamNo()), addressValue); + } + } + + // 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)) + { + signatureIndex++; + } + if (_method.RequiresInstArg()) // hidden param after shadow stack pointer and return slot if present + { + signatureIndex++; + } + + IList argNames = null; + if (_debugInformation != null) + { + argNames = GetParameterNamesForMethod(_method); + } + + for (int i = 0; i < _signature.Length; i++) + { + if (CanStoreTypeOnStack(_signature[i])) + { + LLVMValueRef storageAddr; + Debug.Assert(signatureIndex < _llvmFunction.ParamsCount); + LLVMValueRef argValue = _llvmFunction.GetParam((uint)signatureIndex); + + // The caller will always pass the argument on the stack. If this function doesn't have + // EH, we can put it in an alloca for efficiency and better debugging. Otherwise, + // copy it to the shadow stack so funclets can find it + int argOffset = i + thisOffset; + if (_exceptionRegions.Length == 0) + { + string argName = String.Empty; + if (argNames != null && argNames[argOffset] != null) + { + argName = argNames[argOffset] + "_"; + } + argName += $"arg{argOffset}_"; + + storageAddr = prologBuilder.BuildAlloca(GetLLVMTypeForTypeDesc(_signature[i]), argName); + _argSlots[i] = storageAddr; + } + else + { + storageAddr = CastIfNecessary(prologBuilder, LoadVarAddress(argOffset, LocalVarKind.Argument, _funcletAddrCacheCtx, out _, prologBuilder), LLVMTypeRef.CreatePointer(argValue.TypeOf, 0)); + } + + prologBuilder.BuildStore(argValue, storageAddr); + signatureIndex++; + } + } + + string[] localNames = new string[_locals.Length]; + if (_debugInformation != null) + { + foreach (ILLocalVariable localDebugInfo in _debugInformation.GetLocalVariables() ?? Enumerable.Empty()) + { + // Check whether the slot still exists as the compiler may remove it for intrinsics + int slot = localDebugInfo.Slot; + if (slot < localNames.Length) + { + localNames[localDebugInfo.Slot] = localDebugInfo.Name; + } + } + } + + for (int i = 0; i < _locals.Length; i++) + { + if (CanStoreVariableOnStack(_locals[i].Type)) + { + string localName = String.Empty; + if (localNames[i] != null) + { + localName = localNames[i] + "_"; + } + + localName += $"local{i}_"; + + LLVMValueRef localStackSlot = prologBuilder.BuildAlloca(GetLLVMTypeForTypeDesc(_locals[i].Type), localName); + _localSlots[i] = localStackSlot; + } + } + + if (_methodIL.IsInitLocals) + { + for (int i = 0; i < _locals.Length; i++) + { + LLVMValueRef localAddr = LoadVarAddress(i, LocalVarKind.Local, _funcletAddrCacheCtx, out TypeDesc localType, prologBuilder); + if (CanStoreVariableOnStack(localType)) + { + LLVMTypeRef llvmType = GetLLVMTypeForTypeDesc(localType); + LLVMTypeKind typeKind = llvmType.Kind; + switch (typeKind) + { + case LLVMTypeKind.LLVMIntegerTypeKind: + if (llvmType.Equals(LLVMTypeRef.Int1)) + { + prologBuilder.BuildStore(BuildConstInt1(0), localAddr); + } + else if (llvmType.Equals(LLVMTypeRef.Int8)) + { + prologBuilder.BuildStore(BuildConstInt8(0), localAddr); + } + else if (llvmType.Equals(LLVMTypeRef.Int16)) + { + prologBuilder.BuildStore(BuildConstInt16(0), localAddr); + } + else if (llvmType.Equals(LLVMTypeRef.Int32)) + { + prologBuilder.BuildStore(BuildConstInt32(0), localAddr); + } + else if (llvmType.Equals(LLVMTypeRef.Int64)) + { + prologBuilder.BuildStore(BuildConstInt64(0), localAddr); + } + else + { + throw new Exception("Unexpected LLVM int type"); + } + break; + + case LLVMTypeKind.LLVMPointerTypeKind: + prologBuilder.BuildStore(LLVMValueRef.CreateConstPointerNull(llvmType), localAddr); + break; + + default: + LLVMValueRef castAddr = prologBuilder.BuildPointerCast(localAddr, LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), $"cast_local{i}_"); + ImportCallMemset(castAddr, 0, localType.GetElementSize().AsInt, prologBuilder); + break; + } + } + else + { + LLVMValueRef castAddr = prologBuilder.BuildPointerCast(localAddr, LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), $"cast_local{i}_"); + ImportCallMemset(castAddr, 0, localType.GetElementSize().AsInt, prologBuilder); + } + } + } + + if (_thisType is MetadataType metadataType && !metadataType.IsBeforeFieldInit + && (!_method.IsStaticConstructor && _method.Signature.IsStatic || _method.IsConstructor || (_thisType.IsValueType && !_method.Signature.IsStatic)) + && _compilation.HasLazyStaticConstructor(metadataType)) + { + if(_debugInformation != null) + { + // set the location for the call to EnsureClassConstructorRun + // LLVM can't process empty string file names + var curSequencePoint = GetSequencePoint(0 /* offset for the prolog? */); + if (!string.IsNullOrWhiteSpace(curSequencePoint.Document)) + { + DebugMetadata debugMetadata = GetOrCreateDebugMetadata(curSequencePoint); + LLVMMetadataRef currentLine = CreateDebugFunctionAndDiLocation(debugMetadata, curSequencePoint); + prologBuilder.CurrentDebugLocation = Context.MetadataAsValue(currentLine); + } + } + TriggerCctor(metadataType, prologBuilder); + } + + LLVMBasicBlockRef block0 = GetLLVMBasicBlockForBlock(_basicBlocks[0]); + prologBuilder.PositionBefore(prologBuilder.BuildBr(block0)); + _builder.PositionAtEnd(block0); + } + + private LLVMValueRef CreateLLVMFunction(string mangledName, MethodSignature signature, bool hasHiddenParameter) + { + return Module.AddFunction(mangledName, GetLLVMSignatureForMethod(signature, hasHiddenParameter)); + } + + private LLVMValueRef GetOrCreateLLVMFunction(string mangledName, MethodSignature signature, bool hasHiddenParam) + { + LLVMValueRef llvmFunction = Module.GetNamedFunction(mangledName); + + if(llvmFunction.Handle == IntPtr.Zero) + { + return CreateLLVMFunction(mangledName, signature, hasHiddenParam); + } + return llvmFunction; + } + + private LLVMValueRef GetOrCreateLLVMFunction(string mangledName, LLVMTypeRef functionType) + { + LLVMValueRef llvmFunction = Module.GetNamedFunction(mangledName); + + if (llvmFunction.Handle == IntPtr.Zero) + { + return Module.AddFunction(mangledName, functionType); + } + return llvmFunction; + } + + /// + /// Gets or creates an LLVM function for an exception handling funclet + /// + private LLVMValueRef GetOrCreateFunclet(ILExceptionRegionKind kind, int handlerOffset) + { + string funcletName = _mangledName + "$" + kind.ToString() + handlerOffset.ToString("X"); + LLVMValueRef funclet = Module.GetNamedFunction(funcletName); + if (funclet.Handle == IntPtr.Zero) + { + LLVMTypeRef returnType; + if (kind == ILExceptionRegionKind.Filter || kind == ILExceptionRegionKind.Catch) + { + returnType = LLVMTypeRef.Int32; + } + else + { + returnType = LLVMTypeRef.Void; + } + var funcletArgs = new LLVMTypeRef[] { LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0) }; + LLVMTypeRef universalFuncletSignature = LLVMTypeRef.CreateFunction(returnType, funcletArgs, false); + funclet = Module.AddFunction(funcletName, universalFuncletSignature); + + _exceptionFunclets.Add(funclet); + } + + return funclet; + } + + private void ImportCallMemset(LLVMValueRef targetPointer, byte value, int length, LLVMBuilderRef builder) + { + LLVMValueRef objectSizeValue = BuildConstInt32(length); + ImportCallMemset(targetPointer, value, objectSizeValue, builder); + } + + private void ImportCallMemset(LLVMValueRef targetPointer, byte value, LLVMValueRef length, LLVMBuilderRef builder = default(LLVMBuilderRef)) + { + if (builder.Handle == IntPtr.Zero) builder = _builder; + var memsetSignature = LLVMTypeRef.CreateFunction(LLVMTypeRef.Void, new LLVMTypeRef[] { LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), LLVMTypeRef.Int8, LLVMTypeRef.Int32, LLVMTypeRef.Int1 }, false); + builder.BuildCall(GetOrCreateLLVMFunction("llvm.memset.p0i8.i32", memsetSignature), new LLVMValueRef[] { targetPointer, BuildConstInt8(value), length, BuildConstInt1(0) }, String.Empty); + } + + private void PushLoadExpression(StackValueKind kind, string name, LLVMValueRef rawLLVMValue, TypeDesc type) + { + Debug.Assert(kind != StackValueKind.Unknown, "Unknown stack kind"); + _stack.Push(new LoadExpressionEntry(kind, name, rawLLVMValue, type)); + } + + /// + /// Push an expression named of kind . + /// + /// Kind of entry in stack + /// Variable to be pushed + /// Type if any of + private void PushExpression(StackValueKind kind, string name, LLVMValueRef llvmValue, TypeDesc type = null) + { + Debug.Assert(kind != StackValueKind.Unknown, "Unknown stack kind"); + + switch (kind) + { + case StackValueKind.Int32: + { + if (!type.IsWellKnownType(WellKnownType.Int32) + && !type.IsWellKnownType(WellKnownType.IntPtr) + && !type.IsWellKnownType(WellKnownType.UInt32) + && !type.IsWellKnownType(WellKnownType.UIntPtr)) + { + llvmValue = _builder.BuildIntCast(llvmValue, LLVMTypeRef.Int32, ""); + } + } + break; + + case StackValueKind.Int64: + { + if (!type.IsWellKnownType(WellKnownType.Int64) + && !(type.IsWellKnownType(WellKnownType.UInt64))) + { + llvmValue = _builder.BuildIntCast(llvmValue, LLVMTypeRef.Int64, ""); + } + } + break; + + case StackValueKind.NativeInt: + break; + } + + _stack.Push(new ExpressionEntry(kind, name, llvmValue, type)); + } + + private void MarkInstructionBoundary() + { + } + + private LLVMBasicBlockRef GetLLVMBasicBlockForBlock(BasicBlock block) + { + if (block.Block.Handle == IntPtr.Zero) + { + LLVMValueRef blockFunclet = GetFuncletForBlock(block); + + block.Block = blockFunclet.AppendBasicBlock("Block" + block.StartOffset.ToString("X")); + block.LLVMBlocks.Add(block.Block); + } + return block.Block; + } + + /// + /// Gets or creates the LLVM function or funclet the basic block is part of + /// + private LLVMValueRef GetFuncletForBlock(BasicBlock block) + { + LLVMValueRef blockFunclet; + + // Find the matching funclet for this block + ExceptionRegion ehRegion = GetHandlerRegion(block.StartOffset); + + if (ehRegion != null) + { + ILExceptionRegionKind kind; + int funcletOffset; + if (ehRegion.ILRegion.Kind == ILExceptionRegionKind.Filter && + IsOffsetContained(block.StartOffset, ehRegion.ILRegion.FilterOffset, ehRegion.ILRegion.HandlerOffset - ehRegion.ILRegion.FilterOffset)) + { + kind = ILExceptionRegionKind.Filter; + funcletOffset = ehRegion.ILRegion.FilterOffset; + } + else + { + kind = ehRegion.ILRegion.Kind; + if (kind == ILExceptionRegionKind.Filter) + { + kind = ILExceptionRegionKind.Catch; + } + funcletOffset = ehRegion.ILRegion.HandlerOffset; + } + + blockFunclet = GetOrCreateFunclet(kind, funcletOffset); + } + else + { + blockFunclet = _llvmFunction; + } + + return blockFunclet; + } + + /// + /// Returns the most nested try region the current offset is in + /// + private ExceptionRegion GetCurrentTryRegion() + { + return GetTryRegion(_currentOffset); + } + + private ExceptionRegion GetTryRegion(int offset) + { + // Iterate backwards to find the most nested region + for (int i = _exceptionRegions.Length - 1; i >= 0; i--) + { + ILExceptionRegion region = _exceptionRegions[i].ILRegion; + if (IsOffsetContained(offset - 1, region.TryOffset, region.TryLength)) + { + return _exceptionRegions[i]; + } + } + + return null; + } + + /// + /// Returns the most nested exception handler region the offset is in + /// + /// An exception region or null if it is not in an exception region + private ExceptionRegion GetHandlerRegion(int offset) + { + // Iterate backwards to find the most nested region + for (int i = _exceptionRegions.Length - 1; i >= 0; i--) + { + ExceptionRegion exceptionRegion = _exceptionRegions[i]; + if (IsOffsetContained(offset, exceptionRegion.ILRegion.HandlerOffset, exceptionRegion.ILRegion.HandlerLength) || + (exceptionRegion.ILRegion.Kind == ILExceptionRegionKind.Filter && IsOffsetContained(offset, exceptionRegion.ILRegion.FilterOffset, exceptionRegion.ILRegion.HandlerOffset - exceptionRegion.ILRegion.FilterOffset))) + { + return _exceptionRegions[i]; + } + } + + return null; + } + + private ILExceptionRegionKind? GetHandlerRegionKind(BasicBlock block) + { + ExceptionRegion ehRegion = GetHandlerRegion(block.StartOffset); + return ehRegion?.ILRegion.Kind; + } + + private void StartImportingBasicBlock(BasicBlock basicBlock) + { + _stack.Clear(); + + EvaluationStack entryStack = basicBlock.EntryStack; + if (entryStack != null) + { + int n = entryStack.Length; + for (int i = 0; i < n; i++) + { + _stack.Push(entryStack[i].Duplicate(_builder)); + } + } + ILExceptionRegionKind? handlerKind = GetHandlerRegionKind(basicBlock); + if (basicBlock.FilterStart || handlerKind == ILExceptionRegionKind.Finally) + { + // need to pad out the spilled slots in case the filter or finally tries to allocate some temp variables on the shadow stack. As some spaces are used in the call to FindFirstPassHandlerWasm/InvokeSecondPassWasm + // and inside FindFirstPassHandlerWasm/InvokeSecondPassWasm we need to leave space for them. + // An alternative approach could be to calculate this space in RhpCallFilterFunclet/RhpCallFinallyFunclet and pass it through + NewSpillSlot(new ExpressionEntry(StackValueKind.Int32, "infoIteratorEntry", LLVMValueRef.CreateConstNull(LLVMTypeRef.Int32))); //3 ref and out params + NewSpillSlot(new ExpressionEntry(StackValueKind.Int32, "tryRegionIdxEntry", LLVMValueRef.CreateConstNull(LLVMTypeRef.Int32))); + NewSpillSlot(new ExpressionEntry(StackValueKind.Int32, "handlerFuncPtrEntry", LLVMValueRef.CreateConstNull(LLVMTypeRef.Int32))); + NewSpillSlot(new ExpressionEntry(StackValueKind.Int32, "padding", LLVMValueRef.CreateConstNull(LLVMTypeRef.Int32))); // 8 bytes enough to cover the temps used in FindFirstPassHandlerWasm or InvokeSecondPassWasm + NewSpillSlot(new ExpressionEntry(StackValueKind.Int32, "padding", LLVMValueRef.CreateConstNull(LLVMTypeRef.Int32))); + } + + _curBasicBlock = GetLLVMBasicBlockForBlock(basicBlock); + _currentFunclet = GetFuncletForBlock(basicBlock); + + // Push an exception object for catch and filter + if (basicBlock.HandlerStart || basicBlock.FilterStart) + { + _funcletAddrCacheCtx = null; + foreach (ExceptionRegion ehRegion in _exceptionRegions) + { + if (ehRegion.ILRegion.HandlerOffset == basicBlock.StartOffset || + ehRegion.ILRegion.FilterOffset == basicBlock.StartOffset) + { + if (ehRegion.ILRegion.Kind != ILExceptionRegionKind.Finally) + { + // todo: should this be converted to the exception's type? + _stack.Push(_spilledExpressions[0]); + } + break; + } + } + } + + if (basicBlock.TryStart) + { + foreach (ExceptionRegion ehRegion in _exceptionRegions) + { + if(ehRegion.ILRegion.TryOffset == basicBlock.StartOffset) + { + MarkBasicBlock(_basicBlocks[ehRegion.ILRegion.HandlerOffset]); + if (ehRegion.ILRegion.Kind == ILExceptionRegionKind.Filter) + { + MarkBasicBlock(_basicBlocks[ehRegion.ILRegion.FilterOffset]); + } + } + } + } + + _builder.PositionAtEnd(_curBasicBlock); + } + + private void EndImportingBasicBlock(BasicBlock basicBlock) + { + var terminator = basicBlock.LastBlock.Terminator; + if (terminator.Handle == IntPtr.Zero) + { + if (_basicBlocks.Length > _currentOffset) + { + if (_basicBlocks[_currentOffset].StartOffset == 0) + throw new InvalidProgramException(); + MarkBasicBlock(_basicBlocks[_currentOffset]); + + _builder.BuildBr(GetLLVMBasicBlockForBlock(_basicBlocks[_currentOffset])); + } + } + } + + private void StartImportingInstruction() + { + if (_debugInformation != null) + { + ILSequencePoint curSequencePoint = GetSequencePoint(_currentOffset); + + // LLVM can't process empty string file names + if (string.IsNullOrWhiteSpace(curSequencePoint.Document)) + { + return; + } + + DebugMetadata debugMetadata = GetOrCreateDebugMetadata(curSequencePoint); + + LLVMMetadataRef currentLine = CreateDebugFunctionAndDiLocation(debugMetadata, curSequencePoint); + _builder.CurrentDebugLocation = Context.MetadataAsValue(currentLine); + } + } + + LLVMMetadataRef CreateDebugFunctionAndDiLocation(DebugMetadata debugMetadata, ILSequencePoint sequencePoint) + { + if (_debugFunction.Handle == IntPtr.Zero) + { + LLVMMetadataRef functionMetaType = _compilation.DIBuilder.CreateSubroutineType(debugMetadata.File, + ReadOnlySpan.Empty, LLVMDIFlags.LLVMDIFlagZero); + + uint lineNumber = (uint) _debugInformation.GetSequencePoints().FirstOrDefault().LineNumber; + _debugFunction = _compilation.DIBuilder.CreateFunction(debugMetadata.File, _method.Name, _method.Name, + debugMetadata.File, + lineNumber, functionMetaType, 1, 1, lineNumber, 0, 0); + LLVMSharpInterop.DISetSubProgram(_llvmFunction, _debugFunction); + } + return Context.CreateDebugLocation((uint)sequencePoint.LineNumber, 0, _debugFunction, default(LLVMMetadataRef)); + } + + ILSequencePoint GetSequencePoint(int offset) + { + ILSequencePoint curSequencePoint = default; + foreach (var sequencePoint in _debugInformation.GetSequencePoints() ?? Enumerable.Empty()) + { + if (offset <= sequencePoint.Offset) // take the first sequence point in case we need to make a call to RhNewObject before the first matching sequence point + { + curSequencePoint = sequencePoint; + break; + } + if (sequencePoint.Offset < offset) + { + curSequencePoint = sequencePoint; + } + } + return curSequencePoint; + } + + DebugMetadata GetOrCreateDebugMetadata(ILSequencePoint curSequencePoint) + { + DebugMetadata debugMetadata; + if (!_compilation.DebugMetadataMap.TryGetValue(curSequencePoint.Document, out debugMetadata)) + { + string fullPath = curSequencePoint.Document; + string fileName = Path.GetFileName(fullPath); + string directory = Path.GetDirectoryName(fullPath) ?? String.Empty; + LLVMMetadataRef fileMetadata = _compilation.DIBuilder.CreateFile(fileName, directory); + + // todo: get the right value for isOptimized + LLVMMetadataRef compileUnitMetadata = _compilation.DIBuilder.CreateCompileUnit( + LLVMDWARFSourceLanguage.LLVMDWARFSourceLanguageC, + fileMetadata, "ILC", 0 /* Optimized */, String.Empty, 1, String.Empty, + LLVMDWARFEmissionKind.LLVMDWARFEmissionFull, 0, 0, 0); + Module.AddNamedMetadataOperand("llvm.dbg.cu", compileUnitMetadata); + + debugMetadata = new DebugMetadata(fileMetadata, compileUnitMetadata); + _compilation.DebugMetadataMap[fileName] = debugMetadata; + } + return debugMetadata; + } + + private void EndImportingInstruction() + { + // If this was constrained used in a call, it's already been cleared, + // but if it was on some other instruction, it shoudln't carry forward + _constrainedType = null; + + // Reset the debug position so it doesn't end up applying to the wrong instructions + _builder.CurrentDebugLocation = default(LLVMValueRef); + } + + private void ImportNop() + { + EmitDoNothingCall(); + } + + private void ImportBreak() + { + if (DebugtrapFunction.Handle == IntPtr.Zero) + { + DebugtrapFunction = Module.AddFunction("llvm.debugtrap", LLVMTypeRef.CreateFunction(LLVMTypeRef.Void, Array.Empty(), false)); + } + _builder.BuildCall(DebugtrapFunction, Array.Empty(), string.Empty); + } + + private void ImportLoadVar(int index, bool argument) + { + LLVMValueRef typedLoadLocation = LoadVarAddress(index, argument ? LocalVarKind.Argument : LocalVarKind.Local, out TypeDesc type); + PushLoadExpression(GetStackValueKind(type), (argument ? "arg" : "loc") + index + "_", typedLoadLocation, type); + } + + internal LLVMValueRef LoadTemp(int index, LLVMTypeRef asType) + { + LLVMValueRef address = LoadVarAddress(index, LocalVarKind.Temp, out TypeDesc type); + return _builder.BuildLoad(CastIfNecessary(address, LLVMTypeRef.CreatePointer(asType, 0), $"Temp{index}_"), $"LdTemp{index}_"); + } + + private LLVMValueRef StoreTemp(int index, LLVMValueRef value, string name = null) + { + LLVMValueRef address = LoadVarAddress(index, LocalVarKind.Temp, out TypeDesc type); + _builder.BuildStore(CastToTypeDesc(value, type, name), CastToPointerToTypeDesc(address, type, $"Temp{index}_")); + return address; + } + + internal static LLVMValueRef LoadValue(LLVMBuilderRef builder, LLVMValueRef address, TypeDesc sourceType, LLVMTypeRef targetType, bool signExtend, string loadName = null) + { + var underlyingSourceType = sourceType.UnderlyingType; + if (targetType.Kind == LLVMTypeKind.LLVMIntegerTypeKind && underlyingSourceType.IsPrimitive && !underlyingSourceType.IsPointer) + { + LLVMValueRef loadValueRef = CastIfNecessaryAndLoad(builder, address, underlyingSourceType, loadName); + return CastIntValue(builder, loadValueRef, targetType, signExtend); + } + else if (targetType.Kind == LLVMTypeKind.LLVMDoubleTypeKind) + { + LLVMValueRef loadValueRef = CastIfNecessaryAndLoad(builder, address, underlyingSourceType, loadName); + return CastDoubleValue(builder, loadValueRef, targetType); + } + else + { + var typedAddress = CastIfNecessary(builder, address, LLVMTypeRef.CreatePointer(targetType, 0)); + return builder.BuildLoad(typedAddress, loadName ?? "ldvalue"); + } + } + + private static LLVMValueRef CastIfNecessaryAndLoad(LLVMBuilderRef builder, LLVMValueRef address, TypeDesc sourceTypeDesc, string loadName) + { + LLVMTypeRef sourceLLVMType = ILImporter.GetLLVMTypeForTypeDesc(sourceTypeDesc); + LLVMValueRef typedAddress = CastIfNecessary(builder, address, LLVMTypeRef.CreatePointer(sourceLLVMType, 0)); + return builder.BuildLoad(typedAddress, loadName ?? "ldvalue"); + } + + private static LLVMValueRef CastIntValue(LLVMBuilderRef builder, LLVMValueRef value, LLVMTypeRef type, bool signExtend) + { + LLVMTypeKind typeKind = value.TypeOf.Kind; + if (value.TypeOf.Handle == type.Handle) + { + return value; + } + else if (typeKind == LLVMTypeKind.LLVMPointerTypeKind) + { + return builder.BuildPtrToInt(value, type, "intcast"); + } + else if (typeKind == LLVMTypeKind.LLVMFloatTypeKind || typeKind == LLVMTypeKind.LLVMDoubleTypeKind) + { + if (signExtend) + { + return builder.BuildFPToSI(value, type, "fptosi"); + } + else + { + return builder.BuildFPToUI(value, type, "fptoui"); + } + } + else if (signExtend && type.IntWidth > value.TypeOf.IntWidth) + { + return builder.BuildSExtOrBitCast(value, type, "SExtOrBitCast"); + } + else if (type.IntWidth > value.TypeOf.IntWidth) + { + return builder.BuildZExtOrBitCast(value, type, "ZExtOrBitCast"); + } + else + { + Debug.Assert(typeKind == LLVMTypeKind.LLVMIntegerTypeKind); + return builder.BuildIntCast(value, type, "intcast"); + } + } + + private static LLVMValueRef CastDoubleValue(LLVMBuilderRef builder, LLVMValueRef value, LLVMTypeRef type) + { + if (value.TypeOf.Handle == type.Handle) + { + return value; + } + Debug.Assert(value.TypeOf.Kind == LLVMTypeKind.LLVMFloatTypeKind); + return builder.BuildFPExt(value, type, "fpext"); + } + + private LLVMValueRef LoadVarAddress(int index, LocalVarKind kind, out TypeDesc type, LLVMBuilderRef builder = default(LLVMBuilderRef)) + { + return LoadVarAddress(index, kind, _funcletAddrCacheCtx, out type, builder); + } + + private LLVMValueRef LoadVarAddress(int index, LocalVarKind kind, AddressCacheContext addressCacheContext, out TypeDesc type, LLVMBuilderRef builder = default(LLVMBuilderRef)) + { + if (builder.Handle == IntPtr.Zero) builder = _builder; + + int varOffset; + if (kind == LocalVarKind.Argument) + { + int varCountBase = 0; + if (!_signature.IsStatic) + { + varCountBase = 1; + } + if (addressCacheContext != null && addressCacheContext.ArgAddresses[index] != null) + { + if (!_signature.IsStatic && index == 0) + { + type = _thisType; + if (type.IsValueType) + { + type = type.MakeByRefType(); + } + } + else + { + type = _signature[index - varCountBase]; + } + return addressCacheContext.ArgAddresses[index]; + } + varOffset = GetArgOffsetAtIndex(index, out int realArgIndex); + + if (!_signature.IsStatic && index == 0) + { + type = _thisType; + if (type.IsValueType) + { + type = type.MakeByRefType(); + } + } + else + { + type = _signature[index - varCountBase]; + } + + // If the argument can be passed as a real argument rather than on the shadow stack, + // get its address here + if (realArgIndex != -1) + { + return _argSlots[realArgIndex]; + } + } + else if (kind == LocalVarKind.Local) + { + type = _locals[index].Type; + if (addressCacheContext != null && addressCacheContext.LocalAddresses[index] != null) + { + return addressCacheContext.LocalAddresses[index]; + } + varOffset = GetLocalOffsetAtIndex(index); + if (varOffset == -1) + { + Debug.Assert(_localSlots[index].Handle != IntPtr.Zero); + return _localSlots[index]; + } + varOffset = varOffset + GetTotalParameterOffset(); + } + else + { + type = _spilledExpressions[index].Type; + if (addressCacheContext != null && addressCacheContext.TempAddresses.Count > index && addressCacheContext.TempAddresses[index] != null) + { + return addressCacheContext.TempAddresses[index]; + } + varOffset = GetSpillOffsetAtIndex(index, GetTotalRealLocalOffset()) + GetTotalParameterOffset(); + } + + LLVMValueRef addr; + if (addressCacheContext != null) + { + addr = addressCacheContext.PrologBuilder.BuildGEP(_currentFunclet.GetParam(0), + new LLVMValueRef[] { LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (uint)(varOffset), false) }, + $"{kind}{index}_"); + if (kind == LocalVarKind.Argument) + { + addressCacheContext.ArgAddresses[index] = addr; + } + else if (kind == LocalVarKind.Local) + { + addressCacheContext.LocalAddresses[index] = addr; + } + else if (kind == LocalVarKind.Temp) + { + if (addressCacheContext.TempAddresses.Count <= index) + { + var toAdd = index - addressCacheContext.TempAddresses.Count + 1; + for (var i = 0; i < toAdd; i++) addressCacheContext.TempAddresses.Add(null); + } + addressCacheContext.TempAddresses[index] = addr; + } + } + else + { + addr = builder.BuildGEP(_currentFunclet.GetParam(0), + new LLVMValueRef[] { LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (uint)(varOffset), false) }, + $"{kind}{index}_"); + } + return addr; + } + + private StackValueKind GetStackValueKind(TypeDesc type) + { + switch (type.Category) + { + case TypeFlags.Boolean: + case TypeFlags.Char: + case TypeFlags.SByte: + case TypeFlags.Byte: + case TypeFlags.Int16: + case TypeFlags.UInt16: + case TypeFlags.Int32: + case TypeFlags.UInt32: + return StackValueKind.Int32; + case TypeFlags.Int64: + case TypeFlags.UInt64: + return StackValueKind.Int64; + case TypeFlags.Single: + case TypeFlags.Double: + return StackValueKind.Float; + case TypeFlags.IntPtr: + case TypeFlags.UIntPtr: + return StackValueKind.NativeInt; + case TypeFlags.ValueType: + case TypeFlags.Nullable: + return StackValueKind.ValueType; + case TypeFlags.Enum: + return GetStackValueKind(type.UnderlyingType); + case TypeFlags.Class: + case TypeFlags.Interface: + case TypeFlags.Array: + case TypeFlags.SzArray: + return StackValueKind.ObjRef; + case TypeFlags.ByRef: + return StackValueKind.ByRef; + case TypeFlags.Pointer: + case TypeFlags.FunctionPointer: + return StackValueKind.NativeInt; + default: + return StackValueKind.Unknown; + } + } + + private void ImportStoreVar(int index, bool argument) + { + TypeDesc varType; + StackEntry toStore = _stack.Pop(); + LLVMValueRef varAddress = LoadVarAddress(index, argument ? LocalVarKind.Argument : LocalVarKind.Local, out varType); + CastingStore(varAddress, toStore, varType, false, $"Variable{index}_"); + } + + private void ImportStoreHelper(LLVMValueRef toStore, LLVMTypeRef valueType, LLVMValueRef basePtr, uint offset, string name = null, LLVMBuilderRef builder = default(LLVMBuilderRef)) + { + if (builder.Handle == IntPtr.Zero) + builder = _builder; + + LLVMValueRef typedToStore = CastIfNecessary(builder, toStore, valueType, name); + + var storeLocation = builder.BuildGEP(basePtr, + new LLVMValueRef[] { LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, offset, false) }, + String.Empty); + var typedStoreLocation = CastIfNecessary(builder, storeLocation, LLVMTypeRef.CreatePointer(valueType, 0), "TypedStore" + (name ?? "")); + builder.BuildStore(typedToStore, typedStoreLocation); + } + + private LLVMValueRef CastToRawPointer(LLVMValueRef source, string name = null) + { + return CastIfNecessary(source, LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), name); + } + + private LLVMValueRef CastToTypeDesc(LLVMValueRef source, TypeDesc type, string name = null) + { + return CastIfNecessary(source, GetLLVMTypeForTypeDesc(type), (name ?? "") + type.ToString()); + } + + private LLVMValueRef CastToPointerToTypeDesc(LLVMValueRef source, TypeDesc type, string name = null) + { + return CastIfNecessary(source, LLVMTypeRef.CreatePointer(GetLLVMTypeForTypeDesc(type), 0), (name ?? "") + type.ToString()); + } + + private void CastingStore(LLVMValueRef address, StackEntry value, TypeDesc targetType, bool withGCBarrier, string targetName = null) + { + if (withGCBarrier && targetType.IsGCPointer) + { + CallRuntime(_method.Context, "InternalCalls", "RhpAssignRef", new StackEntry[] + { + new ExpressionEntry(StackValueKind.Int32, "address", address), value + }); + } + else + { + var typedStoreLocation = CastToPointerToTypeDesc(address, targetType, targetName); + var llvmValue = value.ValueAsType(targetType, _builder); + if (withGCBarrier && IsStruct(targetType)) + { + StoreStruct(address, llvmValue, targetType, typedStoreLocation); + } + else + { + _builder.BuildStore(llvmValue, typedStoreLocation); + } + } + } + + private static bool IsStruct(TypeDesc typeDesc) + { + return typeDesc.IsValueType && !typeDesc.IsPrimitive && !typeDesc.IsEnum; + } + + private void StoreStruct(LLVMValueRef address, LLVMValueRef llvmValue, TypeDesc targetType, LLVMValueRef typedStoreLocation, bool childStruct = false) + { + // TODO: if this is used for anything multithreaded, this foreach and the subsequent BuildStore are susceptible to a race condition + foreach (FieldDesc f in targetType.GetFields()) + { + if (f.IsStatic) continue; + if (IsStruct(f.FieldType) && llvmValue.TypeOf.IsPackedStruct) + { + LLVMValueRef targetAddress = _builder.BuildGEP(address, new[] { BuildConstInt32(f.Offset.AsInt) }); + uint index = LLVMSharpInterop.ElementAtOffset(_compilation.TargetData, llvmValue.TypeOf, (ulong)f.Offset.AsInt); + LLVMValueRef fieldValue = _builder.BuildExtractValue(llvmValue, index); + //recurse into struct + StoreStruct(targetAddress, fieldValue, f.FieldType, CastToPointerToTypeDesc(targetAddress, f.FieldType), true); + } + else if (f.FieldType.IsGCPointer) + { + LLVMValueRef targetAddress = _builder.BuildGEP(address, new[] {BuildConstInt32(f.Offset.AsInt)}); + LLVMValueRef fieldValue; + if (llvmValue.TypeOf.IsPackedStruct) + { + uint index = LLVMSharpInterop.ElementAtOffset(_compilation.TargetData, llvmValue.TypeOf, (ulong) f.Offset.AsInt); + fieldValue = _builder.BuildExtractValue(llvmValue, index); + Debug.Assert(fieldValue.TypeOf.Kind == LLVMTypeKind.LLVMPointerTypeKind, "expected an LLVM pointer type"); + } + else + { + // single field IL structs are not LLVM structs + fieldValue = llvmValue; + } + CallRuntime(_method.Context, "InternalCalls", "RhpAssignRef", + new StackEntry[] + { + new ExpressionEntry(StackValueKind.Int32, "targetAddress", targetAddress), + new ExpressionEntry(StackValueKind.ObjRef, "sourceAddress", fieldValue) + }); + } + } + if (!childStruct) + { + _builder.BuildStore(llvmValue, typedStoreLocation); // just copy all the fields again for simplicity, if all the fields were set using RhpAssignRef then a possible optimisation would be to skip this line + } + } + + private LLVMValueRef CastIfNecessary(LLVMValueRef source, LLVMTypeRef valueType, string name = null, bool unsigned = false) + { + return CastIfNecessary(_builder, source, valueType, name, unsigned); + } + + internal static LLVMValueRef CastIfNecessary(LLVMBuilderRef builder, LLVMValueRef source, LLVMTypeRef valueType, string name = null, bool unsigned = false) + { + LLVMTypeRef sourceType = source.TypeOf; + if (sourceType.Handle == valueType.Handle) + return source; + + LLVMTypeKind toStoreKind = sourceType.Kind; + LLVMTypeKind valueTypeKind = valueType.Kind; + + LLVMValueRef typedToStore = source; + if (toStoreKind == LLVMTypeKind.LLVMPointerTypeKind && valueTypeKind == LLVMTypeKind.LLVMPointerTypeKind) + { + typedToStore = builder.BuildPointerCast(source, valueType, "CastPtr" + (name ?? "")); + } + else if (toStoreKind == LLVMTypeKind.LLVMPointerTypeKind && valueTypeKind == LLVMTypeKind.LLVMIntegerTypeKind) + { + typedToStore = builder.BuildPtrToInt(source, valueType, "CastInt" + (name ?? "")); + } + else if (toStoreKind == LLVMTypeKind.LLVMIntegerTypeKind && valueTypeKind == LLVMTypeKind.LLVMArrayTypeKind) + { + typedToStore = builder.BuildLoad(CastIfNecessary(builder, source, LLVMTypeRef.CreatePointer(valueType, 0), name), "CastArrayLoad" + (name ?? "")); + } + else if (toStoreKind == LLVMTypeKind.LLVMPointerTypeKind && valueTypeKind == LLVMTypeKind.LLVMArrayTypeKind) + { + typedToStore = builder.BuildLoad(CastIfNecessary(builder, source, LLVMTypeRef.CreatePointer(valueType, 0), name), "CastArrayLoad" + (name ?? "")); + } + else if (toStoreKind == LLVMTypeKind.LLVMPointerTypeKind && valueTypeKind != LLVMTypeKind.LLVMIntegerTypeKind) + { + throw new NotImplementedException($"trying to cast {toStoreKind} to {valueTypeKind}"); + } + else if (toStoreKind == LLVMTypeKind.LLVMIntegerTypeKind && valueTypeKind == LLVMTypeKind.LLVMPointerTypeKind) + { + typedToStore = builder.BuildIntToPtr(source, valueType, "CastPtr" + (name ?? "")); + } + else if (toStoreKind != LLVMTypeKind.LLVMIntegerTypeKind && valueTypeKind == LLVMTypeKind.LLVMPointerTypeKind) + { + throw new NotImplementedException($"trying to cast {toStoreKind} to {valueTypeKind}"); + } + else if (toStoreKind == LLVMTypeKind.LLVMFloatTypeKind && valueTypeKind == LLVMTypeKind.LLVMDoubleTypeKind) + { + typedToStore = builder.BuildFPExt(source, valueType, "CastFloatToDouble" + (name ?? "")); + } + + else if (toStoreKind == LLVMTypeKind.LLVMDoubleTypeKind && valueTypeKind == LLVMTypeKind.LLVMFloatTypeKind) + { + typedToStore = builder.BuildFPTrunc(source, valueType, "CastDoubleToFloat" + (name ?? "")); + } + else if (toStoreKind != valueTypeKind && toStoreKind != LLVMTypeKind.LLVMIntegerTypeKind && valueTypeKind != LLVMTypeKind.LLVMIntegerTypeKind) + { + throw new NotImplementedException($"trying to cast {toStoreKind} to {valueTypeKind}"); + } + else if (toStoreKind == valueTypeKind && toStoreKind == LLVMTypeKind.LLVMIntegerTypeKind) + { + Debug.Assert(toStoreKind != LLVMTypeKind.LLVMPointerTypeKind && valueTypeKind != LLVMTypeKind.LLVMPointerTypeKind); + // when extending unsigned ints do fill left with 0s, zext + typedToStore = unsigned && sourceType.IntWidth < valueType.IntWidth + ? builder.BuildZExt(source, valueType, "CastZInt" + (name ?? "")) + : builder.BuildIntCast(source, valueType, "CastInt" + (name ?? "")); + } + else if (toStoreKind == LLVMTypeKind.LLVMIntegerTypeKind && (valueTypeKind == LLVMTypeKind.LLVMDoubleTypeKind || valueTypeKind == LLVMTypeKind.LLVMFloatTypeKind)) + { + //TODO: keep track of the TypeDesc so we can call BuildUIToFP when the integer is unsigned + typedToStore = builder.BuildSIToFP(source, valueType, "CastSIToFloat" + (name ?? "")); + } + else if ((toStoreKind == LLVMTypeKind.LLVMDoubleTypeKind || toStoreKind == LLVMTypeKind.LLVMFloatTypeKind) && + valueTypeKind == LLVMTypeKind.LLVMIntegerTypeKind) + { + //TODO: keep track of the TypeDesc so we can call BuildFPToUI when the integer is unsigned + typedToStore = builder.BuildFPToSI(source, valueType, "CastFloatSI" + (name ?? "")); + } + + return typedToStore; + } + + internal static LLVMTypeRef GetLLVMTypeForTypeDesc(TypeDesc type) + { + switch (type.Category) + { + case TypeFlags.Boolean: + return LLVMTypeRef.Int1; + + case TypeFlags.SByte: + case TypeFlags.Byte: + return LLVMTypeRef.Int8; + + case TypeFlags.Int16: + case TypeFlags.UInt16: + case TypeFlags.Char: + return LLVMTypeRef.Int16; + + case TypeFlags.Int32: + case TypeFlags.UInt32: + return LLVMTypeRef.Int32; + case TypeFlags.IntPtr: + case TypeFlags.UIntPtr: + case TypeFlags.Array: + case TypeFlags.SzArray: + case TypeFlags.ByRef: + case TypeFlags.Class: + case TypeFlags.Interface: + return LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0); + + case TypeFlags.Pointer: + return LLVMTypeRef.CreatePointer(type.GetParameterType().IsVoid ? LLVMTypeRef.Int8 : GetLLVMTypeForTypeDesc(type.GetParameterType()), 0); + case TypeFlags.FunctionPointer: + return LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0); + + case TypeFlags.Int64: + case TypeFlags.UInt64: + return LLVMTypeRef.Int64; + + case TypeFlags.Single: + return LLVMTypeRef.Float; + + case TypeFlags.Double: + return LLVMTypeRef.Double; + + case TypeFlags.ValueType: + case TypeFlags.Nullable: + { + if (!LlvmStructs.TryGetValue(type, out LLVMTypeRef llvmStructType)) + { + // LLVM thinks certain sizes of struct have a different calling convention than Clang does. + // Treating them as ints fixes that and is more efficient in general + int structSize = type.GetElementSize().AsInt; + int structAlignment = ((DefType)type).InstanceFieldAlignment.AsInt; + switch (structSize) + { + case 1: + llvmStructType = LLVMTypeRef.Int8; + break; + case 2: + if (structAlignment == 2) + { + llvmStructType = LLVMTypeRef.Int16; + } + else + { + goto default; + } + break; + case 4: + if (structAlignment == 4) + { + if (StructIsWrappedPrimitive(type, type.Context.GetWellKnownType(WellKnownType.Single))) + { + llvmStructType = LLVMTypeRef.Float; + } + else + { + llvmStructType = LLVMTypeRef.Int32; + } + } + else + { + goto default; + } + break; + case 8: + if (structAlignment == 8) + { + if (StructIsWrappedPrimitive(type, type.Context.GetWellKnownType(WellKnownType.Double))) + { + llvmStructType = LLVMTypeRef.Double; + } + else + { + llvmStructType = LLVMTypeRef.Int64; + } + } + else + { + goto default; + } + break; + + default: + // Forward-declare the struct in case there's a reference to it in the fields. + // This must be a named struct or LLVM hits a stack overflow + llvmStructType = Context.CreateNamedStruct(type.ToString()); + LlvmStructs[type] = llvmStructType; + + FieldDesc[] instanceFields = type.GetFields().Where(field => !field.IsStatic).ToArray(); + FieldAndOffset[] fieldLayout = new FieldAndOffset[instanceFields.Length]; + for (int i = 0; i < instanceFields.Length; i++) + { + fieldLayout[i] = new FieldAndOffset(instanceFields[i], instanceFields[i].Offset); + } + + // Sort fields by offset and size in order to handle generating unions + FieldAndOffset[] sortedFields = fieldLayout.OrderBy(fieldAndOffset => fieldAndOffset.Offset.AsInt). + ThenByDescending(fieldAndOffset => fieldAndOffset.Field.FieldType.GetElementSize().AsInt).ToArray(); + + List llvmFields = new List(sortedFields.Length); + int lastOffset = -1; + int nextNewOffset = -1; + TypeDesc prevType = null; + int totalSize = 0; + + foreach (FieldAndOffset fieldAndOffset in sortedFields) + { + int curOffset = fieldAndOffset.Offset.AsInt; + + if (prevType == null || (curOffset != lastOffset && curOffset >= nextNewOffset)) + { + // The layout should be in order + Debug.Assert(curOffset > lastOffset); + + int prevElementSize; + if (prevType == null) + { + lastOffset = 0; + prevElementSize = 0; + } + else + { + prevElementSize = prevType.GetElementSize().AsInt; + } + + // Pad to this field if necessary + int paddingSize = curOffset - lastOffset - prevElementSize; + if (paddingSize > 0) + { + AddPaddingFields(paddingSize, llvmFields); + totalSize += paddingSize; + } + + TypeDesc fieldType = fieldAndOffset.Field.FieldType; + int fieldSize = fieldType.GetElementSize().AsInt; + + llvmFields.Add(GetLLVMTypeForTypeDesc(fieldType)); + + totalSize += fieldSize; + lastOffset = curOffset; + prevType = fieldType; + nextNewOffset = curOffset + fieldSize; + } + } + + // If explicit layout is greater than the sum of fields, add padding + if (totalSize < structSize) + { + AddPaddingFields(structSize - totalSize, llvmFields); + } + + llvmStructType.StructSetBody(llvmFields.ToArray(), true); + break; + } + + LlvmStructs[type] = llvmStructType; + } + return llvmStructType; + } + + case TypeFlags.Enum: + return GetLLVMTypeForTypeDesc(type.UnderlyingType); + + case TypeFlags.Void: + return LLVMTypeRef.Void; + + default: + throw new NotImplementedException(type.Category.ToString()); + } + } + + /// + /// Returns true if a type is a struct that just wraps a given primitive + /// or another struct that does so and can thus be treated as that primitive + /// + /// The struct to evaluate + /// The primitive to check for + /// True if the struct is a wrapper of the primitive + private static bool StructIsWrappedPrimitive(TypeDesc type, TypeDesc primitiveType) + { + Debug.Assert(type.IsValueType); + Debug.Assert(primitiveType.IsPrimitive); + + if (type.GetElementSize().AsInt != primitiveType.GetElementSize().AsInt) + { + return false; + } + + FieldDesc[] fields = type.GetFields().ToArray(); + int instanceFieldCount = 0; + bool foundPrimitive = false; + + foreach (FieldDesc field in fields) + { + if (field.IsStatic) + { + continue; + } + + instanceFieldCount++; + + // If there's more than one field, figuring out whether this is a primitive gets complicated, so assume it's not + if (instanceFieldCount > 1) + { + break; + } + + TypeDesc fieldType = field.FieldType; + if (fieldType == primitiveType) + { + foundPrimitive = true; + } + else if (fieldType.IsValueType && !fieldType.IsPrimitive && StructIsWrappedPrimitive(fieldType, primitiveType)) + { + foundPrimitive = true; + } + } + + if (instanceFieldCount == 1 && foundPrimitive) + { + return true; + } + + return false; + } + + /// + /// Pad out a struct at the current location + /// + /// Number of bytes of padding to add + /// The set of llvm fields in the struct so far + private static void AddPaddingFields(int paddingSize, List llvmFields) + { + int numInts = paddingSize / 4; + int numBytes = paddingSize - numInts * 4; + for (int i = 0; i < numInts; i++) + { + llvmFields.Add(LLVMTypeRef.Int32); + } + for (int i = 0; i < numBytes; i++) + { + llvmFields.Add(LLVMTypeRef.Int8); + } + } + + private int GetTotalLocalOffset() + { + int offset = GetTotalRealLocalOffset(); + for (int i = 0; i < _spilledExpressions.Count; i++) + { + offset = PadNextOffset(_spilledExpressions[i].Type, offset); + } + return offset.AlignUp(_pointerSize); + } + + private int GetTotalRealLocalOffset() + { + int offset = 0; + for (int i = 0; i < _locals.Length; i++) + { + TypeDesc localType = _locals[i].Type; + if (!CanStoreVariableOnStack(localType)) + { + offset = PadNextOffset(localType, offset); + } + } + return offset.AlignUp(_pointerSize); + } + + private bool CanStoreVariableOnStack(TypeDesc variableType) + { + // Keep all variables on the shadow stack if there is exception + // handling so funclets can access them + if (_exceptionRegions.Length == 0) + { + return CanStoreTypeOnStack(variableType); + } + return false; + } + + /// + /// 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) + { + if (type is DefType defType) + { + if (!defType.IsGCPointer && !defType.ContainsGCPointers) + { + return true; + } + } + else if (type is PointerType) + { + return true; + } + + 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; + for (int i = 0; i < _signature.Length; i++) + { + if (!CanStoreVariableOnStack(_signature[i])) + { + offset = PadNextOffset(_signature[i], offset); + } + } + if (!_signature.IsStatic) + { + // If this is a struct, then it's a pointer on the stack + if (_thisType.IsValueType) + { + offset = PadNextOffset(_thisType.MakeByRefType(), offset); + } + else + { + offset = PadNextOffset(_thisType, offset); + } + } + + return offset.AlignUp(_pointerSize); + } + + private int GetArgOffsetAtIndex(int index, out int realArgIndex) + { + realArgIndex = -1; + int offset; + int thisSize = 0; + if (!_signature.IsStatic) + { + thisSize = _thisType.IsValueType ? _thisType.Context.Target.PointerSize : _thisType.GetElementSize().AsInt.AlignUp(_pointerSize); + if (index == 0) + { + return 0; + } + else + { + index--; + } + } + + var argType = _signature[index]; + int potentialRealArgIndex = 0; + + offset = thisSize; + + if (!CanStoreVariableOnStack(argType) && CanStoreTypeOnStack(argType)) + { + // this is an arg that was passed on the stack and is now copied to the shadow stack: move past args that are passed on shadow stack + for (int i = 0; i < _signature.Length; i++) + { + if (!CanStoreTypeOnStack(_signature[i])) + { + offset = PadNextOffset(_signature[i], offset); + } + } + } + + for (int i = 0; i < index; i++) + { + // We could compact the set of argSlots to only those that we'd keep on the stack, but currently don't + potentialRealArgIndex++; + + if (CanStoreTypeOnStack(_signature[index])) + { + if (CanStoreTypeOnStack(_signature[i]) && !CanStoreVariableOnStack(_signature[index]) && !CanStoreVariableOnStack(_signature[i])) + { + offset = PadNextOffset(_signature[i], offset); + } + } + // if this is a shadow stack arg, then only count other shadow stack args as stack args come later + else if (!CanStoreVariableOnStack(_signature[i]) && !CanStoreTypeOnStack(_signature[i])) + { + offset = PadNextOffset(_signature[i], offset); + } + } + + if (CanStoreVariableOnStack(argType)) + { + realArgIndex = potentialRealArgIndex; + offset = -1; + } + else + { + offset = PadOffset(argType, offset); + } + return offset; + } + + private int GetLocalOffsetAtIndex(int index) + { + LocalVariableDefinition local = _locals[index]; + int offset; + if (CanStoreVariableOnStack(local.Type)) + { + offset = -1; + } + else + { + offset = 0; + for (int i = 0; i < index; i++) + { + if (!CanStoreVariableOnStack(_locals[i].Type)) + { + offset = PadNextOffset(_locals[i].Type, offset); + } + } + offset = PadOffset(local.Type, offset); + } + return offset; + } + + private int GetSpillOffsetAtIndex(int index, int offset) + { + SpilledExpressionEntry spill = _spilledExpressions[index]; + + for (int i = 0; i < index; i++) + { + offset = PadNextOffset(_spilledExpressions[i].Type, offset); + } + offset = PadOffset(spill.Type, offset); + return offset; + } + + public int PadNextOffset(TypeDesc type, int atOffset) + { + var size = type is DefType && type.IsValueType ? ((DefType)type).InstanceFieldSize : type.Context.Target.LayoutPointerSize; + return PadOffset(type, atOffset) + size.AsInt; + } + + public int PadOffset(TypeDesc type, int atOffset) + { + var fieldAlignment = type is DefType && type.IsValueType ? ((DefType)type).InstanceFieldAlignment : type.Context.Target.LayoutPointerSize; + var alignment = LayoutInt.Min(fieldAlignment, new LayoutInt(ComputePackingSize(type))).AsInt; + var padding = (atOffset + (alignment - 1)) & ~(alignment - 1); + return padding; + } + + private static int ComputePackingSize(TypeDesc type) + { + if (type is MetadataType) + { + var metaType = type as MetadataType; + var layoutMetadata = metaType.GetClassLayout(); + + // If a type contains pointers then the metadata specified packing size is ignored (On desktop this is disqualification from ManagedSequential) + if (layoutMetadata.PackingSize == 0 || metaType.ContainsGCPointers) + return type.Context.Target.DefaultPackingSize; + else + return layoutMetadata.PackingSize; + } + else + return type.Context.Target.DefaultPackingSize; + } + + private void ImportAddressOfVar(int index, bool argument) + { + TypeDesc type; + LLVMValueRef typedLoadLocation = LoadVarAddress(index, argument ? LocalVarKind.Argument : LocalVarKind.Local, out type); + _stack.Push(new AddressExpressionEntry(StackValueKind.ByRef, "ldloca", typedLoadLocation, type.MakeByRefType())); + } + + private void ImportDup() + { + var entry = _stack.Pop(); + _stack.Push(entry.Duplicate(_builder)); + _stack.Push(entry.Duplicate(_builder)); + } + + private void ImportPop() + { + _stack.Pop(); + } + + private void ImportJmp(int token) + { + throw new NotImplementedException("jmp"); + } + + private void ImportCasting(ILOpcode opcode, int token) + { + TypeDesc type = (TypeDesc)_methodIL.GetObject(token); + + //TODO: call GetCastingHelperNameForType from JitHelper.cs (needs refactoring) + string function; + bool throwing = opcode == ILOpcode.castclass; + if (type.IsArray) + function = throwing ? "CheckCastArray" : "IsInstanceOfArray"; + else if (type.IsInterface) + function = throwing ? "CheckCastInterface" : "IsInstanceOfInterface"; + else + function = throwing ? "CheckCastClass" : "IsInstanceOfClass"; + + LLVMValueRef typeRef; + if (type.IsRuntimeDeterminedSubtype) + { + typeRef = CallGenericHelper(ReadyToRunHelperId.TypeHandleForCasting, type); + } + else + { + ISymbolNode lookup = _compilation.ComputeConstantLookup(ReadyToRunHelperId.TypeHandleForCasting, type); + _dependencies.Add(lookup); + typeRef = LoadAddressOfSymbolNode(lookup); + } + + _stack.Push(CallRuntime(_compilation.TypeSystemContext, TypeCast, function, + new StackEntry[] + { + new ExpressionEntry(StackValueKind.ValueType, "eeType", typeRef, GetWellKnownType(WellKnownType.IntPtr)), + _stack.Pop() + }, GetWellKnownType(WellKnownType.Object))); + } + + LLVMValueRef CallGenericHelper(ReadyToRunHelperId helperId, object helperArg) + { + _dependencies.Add(GetGenericLookupHelperAndAddReference(helperId, helperArg, out LLVMValueRef helper)); + return _builder.BuildCall(helper, new LLVMValueRef[] + { + GetShadowStack(), + GetGenericContext() + }, "getHelper"); + } + + private void ImportLoadNull() + { + _stack.Push(new ExpressionEntry(StackValueKind.ObjRef, "null", LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 0, false))); + } + + private void ImportReturn() + { + if (_signature.ReturnType.IsVoid) + { + _builder.BuildRetVoid(); + return; + } + + StackEntry retVal = _stack.Pop(); + LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(_signature.ReturnType); + LLVMValueRef castValue = retVal.ValueAsType(valueType, _builder); + + if (NeedsReturnStackSlot(_signature)) + { + var retParam = _llvmFunction.GetParam(1); + ImportStoreHelper(castValue, valueType, retParam, 0); + _builder.BuildRetVoid(); + } + else + { + _builder.BuildRet(castValue); + } + } + + private void ImportCall(ILOpcode opcode, int token) + { + MethodDesc runtimeDeterminedMethod = (MethodDesc)_methodIL.GetObject(token); + MethodDesc callee = (MethodDesc)_canonMethodIL.GetObject(token); + if (callee.IsIntrinsic) + { + if (ImportIntrinsicCall(callee, runtimeDeterminedMethod)) + { + return; + } + } + + // Hard coded InternalCall mappings for mono interoperability + if (callee.IsInternalCall) + { + var metadataType = callee.OwningType as MetadataType; + // See https://github.com/dotnet/runtime/blob/9ba9a300a08170c8170ea52981810f41fad68cf0/src/mono/wasm/runtime/driver.c#L400-L407 + // Mono have these InternalCall methods in different namespaces but just mapping them to System.Private.WebAssembly. + if (metadataType != null && (metadataType.Namespace == "WebAssembly.JSInterop" && metadataType.Name == "InternalCalls" || metadataType.Namespace == "WebAssembly" && metadataType.Name == "Runtime")) + { + var coreRtJsInternalCallsType = _compilation.TypeSystemContext + .GetModuleForSimpleName("System.Private.WebAssembly") + .GetKnownType("System.Private.WebAssembly", "InternalCalls"); + callee = coreRtJsInternalCallsType.GetMethod(callee.Name, callee.Signature); + } + } + + if (callee.IsRawPInvoke() || (callee.IsInternalCall && callee.HasCustomAttribute("System.Runtime", "RuntimeImportAttribute"))) + { + ImportRawPInvoke(callee); + return; + } + + TypeDesc localConstrainedType = _constrainedType; + _constrainedType = null; + + if (opcode == ILOpcode.newobj) + { + TypeDesc newType = callee.OwningType; + if (newType.IsArray) + { + var paramCnt = callee.Signature.Length; + var eeTypeDesc = _compilation.TypeSystemContext.SystemModule.GetKnownType("Internal.Runtime", "EEType").MakePointerType(); + LLVMValueRef dimensions = _builder.BuildArrayAlloca(LLVMTypeRef.Int32, BuildConstInt32(paramCnt), "newobj_array_pdims_" + _currentOffset); + for (int i = paramCnt - 1; i >= 0; --i) + { + _builder.BuildStore(_stack.Pop().ValueAsInt32(_builder, true), + _builder.BuildGEP(dimensions, new LLVMValueRef[] { BuildConstInt32(i) }, "pdims_ptr")); + } + var arguments = new StackEntry[] + { + null, + new Int32ConstantEntry(paramCnt), + new AddressExpressionEntry(StackValueKind.ValueType, "newobj_array_pdims", dimensions) + }; + if (!runtimeDeterminedMethod.OwningType.IsRuntimeDeterminedSubtype) + { + arguments[0] = new LoadExpressionEntry(StackValueKind.ValueType, "eeType", GetEETypePointerForTypeDesc(newType, true), eeTypeDesc); + } + else + { + var typeRef = CallGenericHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.OwningType); + arguments[0] = new ExpressionEntry(StackValueKind.ValueType, "eeType", typeRef, eeTypeDesc); + } + MetadataType helperType = _compilation.TypeSystemContext.SystemModule.GetKnownType("Internal.Runtime.CompilerHelpers", "ArrayHelpers"); + MethodDesc helperMethod = helperType.GetKnownMethod("NewObjArray", null); + PushNonNull(HandleCall(helperMethod, helperMethod.Signature, helperMethod, arguments, runtimeDeterminedMethod, forcedReturnType: newType).Item1); + return; + } + else if (newType.IsString) + { + // String constructors actually look like regular method calls + IMethodNode node = _compilation.NodeFactory.StringAllocator(callee); + _dependencies.Add(node); + callee = node.Method; + opcode = ILOpcode.call; + } + else + { + if (callee.Signature.Length > _stack.Length) + throw new InvalidProgramException(); + + if (newType.IsValueType) + { + // Allocate a slot on the shadow stack for the value type + int spillIndex = _spilledExpressions.Count; + SpilledExpressionEntry spillEntry = new SpilledExpressionEntry(GetStackValueKind(newType), "newobj" + _currentOffset, newType, spillIndex, this); + _spilledExpressions.Add(spillEntry); + LLVMValueRef addrOfValueType = LoadVarAddress(spillIndex, LocalVarKind.Temp, out TypeDesc unused); + AddressExpressionEntry valueTypeByRef = new AddressExpressionEntry(StackValueKind.ByRef, "newobj_slot" + _currentOffset, addrOfValueType, newType.MakeByRefType()); + + // The ctor needs a reference to the spill slot, but the + // actual value ends up on the stack after the ctor is done + _stack.InsertAt(spillEntry, _stack.Top - callee.Signature.Length); + _stack.InsertAt(valueTypeByRef, _stack.Top - callee.Signature.Length); + } + else + { + StackEntry newObjResult; + TypeDesc typeToAlloc; + var runtimeDeterminedRetType = runtimeDeterminedMethod.OwningType; + + if (runtimeDeterminedRetType.IsRuntimeDeterminedSubtype) + { + typeToAlloc = _compilation.ConvertToCanonFormIfNecessary(runtimeDeterminedRetType, CanonicalFormKind.Specific); + var typeRef = CallGenericHelper(ReadyToRunHelperId.TypeHandle, typeToAlloc); + newObjResult = AllocateObject(new ExpressionEntry(StackValueKind.ValueType, "eeType", typeRef, GetWellKnownType(WellKnownType.IntPtr))); + } + else + { + typeToAlloc = callee.OwningType; + MetadataType metadataType = (MetadataType)typeToAlloc; + newObjResult = AllocateObject(new LoadExpressionEntry(StackValueKind.ValueType, "eeType", GetEETypePointerForTypeDesc(metadataType, true), GetWellKnownType(WellKnownType.IntPtr)), typeToAlloc); + } + + //one for the real result and one to be consumed by ctor + _stack.InsertAt(newObjResult, _stack.Top - callee.Signature.Length); + _stack.InsertAt(newObjResult, _stack.Top - callee.Signature.Length); + } + } + } + + if (opcode == ILOpcode.newobj && callee.OwningType.IsDelegate) + { + FunctionPointerEntry functionPointer = ((FunctionPointerEntry)_stack.Peek()); + TypeDesc canonDelegateType = callee.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific); + DelegateCreationInfo delegateInfo = _compilation.GetDelegateCtor(canonDelegateType, functionPointer.Method, followVirtualDispatch: false); + MethodDesc delegateTargetMethod = delegateInfo.TargetMethod; + callee = delegateInfo.Constructor.Method; + if (delegateInfo.NeedsRuntimeLookup && !functionPointer.IsVirtual) + { + LLVMValueRef helper; + List additionalTypes = new List(); + var shadowStack = GetShadowStack(); + if (delegateInfo.Thunk != null) + { + MethodDesc thunkMethod = delegateInfo.Thunk.Method; + AddMethodReference(thunkMethod); + PushExpression(StackValueKind.NativeInt, "invokeThunk", + GetOrCreateLLVMFunction( + _compilation.NameMangler.GetMangledMethodName(thunkMethod).ToString(), + thunkMethod.Signature, + false)); + } + var sigLength = callee.Signature.Length; + var stackCopy = new StackEntry[sigLength]; + for (var i = 0; i < sigLength; i++) + { + stackCopy[i] = _stack.Pop(); + } + var thisEntry = _stack.Pop(); // the extra newObjResult which we dont want as we are not going through HandleCall + // by convention(?) the delegate initialize methods take this as the first parameter which is not in the ctor + // method sig, so add that here + int curOffset = 0; + + // pass this (delegate obj) as first param + LLVMTypeRef llvmTypeRefForThis = GetLLVMTypeForTypeDesc(thisEntry.Type); + curOffset = PadOffset(thisEntry.Type, curOffset); + LLVMValueRef thisAddr = _builder.BuildGEP(shadowStack, new LLVMValueRef[] { LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)curOffset, false) }, "thisLoc"); + LLVMValueRef llvmValueRefForThis = thisEntry.ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), _builder); + _builder.BuildStore(llvmValueRefForThis, CastIfNecessary(_builder, thisAddr, LLVMTypeRef.CreatePointer(llvmTypeRefForThis, 0), "thisCast")); + curOffset = PadNextOffset(GetWellKnownType(WellKnownType.Object), curOffset); + + List helperParams = new List + { + shadowStack, + GetGenericContext() + }; + + for (var i = 0; i < sigLength; i++) + { + TypeDesc argTypeDesc = callee.Signature[i]; + LLVMTypeRef llvmTypeRefForArg = GetLLVMTypeForTypeDesc(argTypeDesc); + StackEntry argStackEntry = stackCopy[sigLength - i - 1]; + if (CanStoreTypeOnStack(callee.Signature[i])) + { + LLVMValueRef llvmValueRefForArg = argStackEntry.ValueAsType(llvmTypeRefForArg, _builder); + additionalTypes.Add(llvmTypeRefForArg); + helperParams.Add(llvmValueRefForArg); + } + else + { + LLVMValueRef llvmValueRefForArg = argStackEntry.ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), _builder); + curOffset = PadOffset(argTypeDesc, curOffset); + LLVMValueRef argAddr = _builder.BuildGEP(shadowStack, new LLVMValueRef[] { LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)curOffset, false) }, "arg" + i); + _builder.BuildStore(llvmValueRefForArg, CastIfNecessary(_builder, argAddr, LLVMTypeRef.CreatePointer(llvmTypeRefForArg, 0), $"parameter{i}_")); + curOffset = PadNextOffset(argTypeDesc, curOffset); + } + } + + GetGenericLookupHelperAndAddReference(ReadyToRunHelperId.DelegateCtor, delegateInfo, out helper, + additionalTypes); + _builder.BuildCall(helper, helperParams.ToArray(), string.Empty); + return; + } + if (!functionPointer.IsVirtual && delegateTargetMethod.OwningType.IsValueType && + !delegateTargetMethod.Signature.IsStatic) + { + _stack.Pop(); // remove the target + + MethodDesc canonDelegateTargetMethod = delegateTargetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + ISymbolNode targetNode = delegateInfo.GetTargetNode(_compilation.NodeFactory); + _dependencies.Add(targetNode); + if (delegateTargetMethod != canonDelegateTargetMethod) + { + var funcRef = LoadAddressOfSymbolNode(targetNode); + var toInt = _builder.BuildPtrToInt(funcRef, LLVMTypeRef.Int32, "toInt"); + var withOffset = _builder.BuildOr(toInt, BuildConstUInt32((uint)_compilation.TypeSystemContext.Target.FatFunctionPointerOffset), "withOffset"); + PushExpression(StackValueKind.NativeInt, "fatthunk", withOffset); + } + else + { + PushExpression(StackValueKind.NativeInt, "thunk", GetOrCreateLLVMFunction(targetNode.GetMangledName(_compilation.NodeFactory.NameMangler), delegateTargetMethod.Signature, false)); + } + } + else if (callee.Signature.Length == 3) + { + // These are the invoke thunks e.g. {[S.P.CoreLib]System.Func`1.InvokeOpenStaticThunk()} that are passed to e.g. {[S.P.CoreLib]System.Delegate.InitializeOpenStaticThunk(object,native int,native int)} + // only push this if there is the third argument, i.e. not {[S.P.CoreLib]System.Delegate.InitializeClosedInstance(object,native int)} + PushExpression(StackValueKind.NativeInt, "thunk", GetOrCreateLLVMFunction(_compilation.NodeFactory.NameMangler.GetMangledMethodName(delegateInfo.Thunk.Method).ToString(), delegateInfo.Thunk.Method.Signature, false)); + } + } + + HandleCall(callee, callee.Signature, runtimeDeterminedMethod, opcode, localConstrainedType); + } + + private LLVMValueRef LLVMFunctionForMethod(MethodDesc callee, MethodDesc canonMethod, StackEntry thisPointer, bool isCallVirt, + TypeDesc constrainedType, MethodDesc runtimeDeterminedMethod, out bool hasHiddenParam, + out LLVMValueRef dictPtrPtrStore, + out LLVMValueRef fatFunctionPtr) + { + hasHiddenParam = false; + dictPtrPtrStore = default(LLVMValueRef); + fatFunctionPtr = default(LLVMValueRef); + + TypeDesc owningType = callee.OwningType; + bool delegateInvoke = owningType.IsDelegate && callee.Name == "Invoke"; + // Sealed methods must not be called virtually due to sealed vTables, so call them directly, but not delegate Invoke + if ((canonMethod.IsFinal || canonMethod.OwningType.IsSealed()) && !delegateInvoke) + { + if (!_compilation.NodeFactory.TypeSystemContext.IsSpecialUnboxingThunkTargetMethod(canonMethod)) + { + hasHiddenParam = canonMethod.RequiresInstArg() || canonMethod.IsArrayAddressMethod(); + } + AddMethodReference(canonMethod); + string physicalName = _compilation.NodeFactory.MethodEntrypoint(canonMethod).GetMangledName(_compilation.NameMangler); + return GetOrCreateLLVMFunction(physicalName, canonMethod.Signature, hasHiddenParam); + } + + LLVMValueRef thisRef = default; + if (thisPointer != null) + { + thisRef = thisPointer.ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), _builder); + ThrowIfNull(thisRef); + } + + if (canonMethod.IsVirtual && isCallVirt) + { + // TODO: Full resolution of virtual methods + if (!canonMethod.IsNewSlot) + throw new NotImplementedException(); + + bool isValueTypeCall = false; + TypeDesc thisType = thisPointer.Type; + TypeFlags category = thisType.Category; + MethodDesc targetMethod = null; + TypeDesc parameterType = null; + + if (category == TypeFlags.ByRef) + { + parameterType = ((ByRefType)thisType).ParameterType; + if (parameterType.IsValueType) + { + isValueTypeCall = true; + } + } + + if (constrainedType != null && constrainedType.IsValueType) + { + isValueTypeCall = true; + } + + if (isValueTypeCall) + { + if (constrainedType != null) + { + if (constrainedType.IsRuntimeDeterminedType) + { + constrainedType = constrainedType.ConvertToCanonForm(CanonicalFormKind.Specific); + } + targetMethod = constrainedType.TryResolveConstraintMethodApprox(canonMethod.OwningType, canonMethod, out _); + } + else if (canonMethod.OwningType.IsInterface) + { + targetMethod = parameterType.ResolveInterfaceMethodTarget(canonMethod); + } + else + { + targetMethod = parameterType.FindVirtualFunctionTargetMethodOnObjectType(canonMethod); + } + } + + hasHiddenParam = callee.RequiresInstArg(); + if (targetMethod != null) + { + AddMethodReference(targetMethod); + return GetOrCreateLLVMFunction(_compilation.NameMangler.GetMangledMethodName(targetMethod).ToString(), canonMethod.Signature, hasHiddenParam); + } + if (canonMethod.HasInstantiation && !canonMethod.IsFinal && !canonMethod.OwningType.IsSealed()) + { + return GetCallableGenericVirtualMethod(thisPointer, canonMethod, callee, runtimeDeterminedMethod, out dictPtrPtrStore, out fatFunctionPtr); + } + return GetCallableVirtualMethod(thisRef, callee, runtimeDeterminedMethod); + } + + hasHiddenParam = canonMethod.RequiresInstArg(); + AddMethodReference(canonMethod); + string canonMethodName = _compilation.NodeFactory.MethodEntrypoint(canonMethod).GetMangledName(_compilation.NameMangler); + return GetOrCreateLLVMFunction(canonMethodName, canonMethod.Signature, hasHiddenParam); + } + + private ISymbolNode GetMethodGenericDictionaryNode(MethodDesc method) + { + ISymbolNode node = _compilation.NodeFactory.MethodGenericDictionary(method); + _dependencies.Add(node); + + return node; + } + + private LLVMValueRef GetOrCreateMethodSlot(MethodDesc canonMethod, MethodDesc callee) + { + var vtableSlotSymbol = _compilation.NodeFactory.VTableSlot(callee); + _dependencies.Add(vtableSlotSymbol); + LLVMValueRef slot = LoadAddressOfSymbolNode(vtableSlotSymbol); + return _builder.BuildLoad(slot, $"{callee.Name}_slot"); + } + + private LLVMValueRef GetCallableVirtualMethod(LLVMValueRef thisPointer, MethodDesc callee, MethodDesc runtimeDeterminedMethod) + { + Debug.Assert(runtimeDeterminedMethod.IsVirtual); + + LLVMValueRef slot = GetOrCreateMethodSlot(runtimeDeterminedMethod, callee); + + LLVMTypeRef llvmSignature = GetLLVMSignatureForMethod(runtimeDeterminedMethod.Signature, false); + LLVMValueRef functionPtr; + ThrowIfNull(thisPointer); + if (runtimeDeterminedMethod.OwningType.IsInterface) + { + ExpressionEntry interfaceEEType; + ExpressionEntry eeTypeExpression; + if (runtimeDeterminedMethod.OwningType.IsRuntimeDeterminedSubtype) + { + //TODO interfaceEEType can be refactored out + eeTypeExpression = CallRuntime("System", _compilation.TypeSystemContext, "Object", "get_EEType", + new[] { new ExpressionEntry(StackValueKind.ObjRef, "thisPointer", thisPointer) }); + interfaceEEType = new ExpressionEntry(StackValueKind.ValueType, "interfaceEEType", CallGenericHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.OwningType), GetWellKnownType(WellKnownType.IntPtr)); + } + else + { + interfaceEEType = new LoadExpressionEntry(StackValueKind.ValueType, "interfaceEEType", GetEETypePointerForTypeDesc(runtimeDeterminedMethod.OwningType, true), GetWellKnownType(WellKnownType.IntPtr)); + eeTypeExpression = new LoadExpressionEntry(StackValueKind.ValueType, "eeType", thisPointer, GetWellKnownType(WellKnownType.IntPtr)); + } + + var targetEntry = CallRuntime(_compilation.TypeSystemContext, DispatchResolve, "FindInterfaceMethodImplementationTarget", new StackEntry[] { eeTypeExpression, interfaceEEType, new ExpressionEntry(StackValueKind.Int32, "slot", slot, GetWellKnownType(WellKnownType.UInt16)) }); + functionPtr = targetEntry.ValueAsType(LLVMTypeRef.CreatePointer(llvmSignature, 0), _builder); + } + else + { + var rawObjectPtr = CastIfNecessary(thisPointer, LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(llvmSignature, 0), 0), 0), "this"); + var eeType = _builder.BuildLoad(rawObjectPtr, "ldEEType"); + var slotPtr = _builder.BuildGEP(eeType, new LLVMValueRef[] { slot }, "__getslot__"); + functionPtr = _builder.BuildLoad(slotPtr, "ld__getslot__"); + } + + return functionPtr; + } + + private LLVMValueRef GetCallableGenericVirtualMethod(StackEntry objectPtr, MethodDesc canonMethod, MethodDesc callee, MethodDesc runtimeDeterminedMethod, out LLVMValueRef dictPtrPtrStore, + out LLVMValueRef slotRef) + { + // this will only have a non-zero pointer the the GVM ptr is fat. + dictPtrPtrStore = _builder.BuildAlloca(LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), 0), 0), + "dictPtrPtrStore"); + + _dependencies.Add(_compilation.NodeFactory.GVMDependencies(canonMethod)); + bool exactContextNeedsRuntimeLookup; + if (canonMethod.HasInstantiation) + { + exactContextNeedsRuntimeLookup = callee.IsSharedByGenericInstantiations; + } + else + { + exactContextNeedsRuntimeLookup = canonMethod.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any); + } + LLVMValueRef runtimeMethodHandle; + if (exactContextNeedsRuntimeLookup) + { + LLVMValueRef helper; + var node = GetGenericLookupHelperAndAddReference(ReadyToRunHelperId.MethodHandle, runtimeDeterminedMethod, out helper); + _dependencies.Add(node); + runtimeMethodHandle = _builder.BuildCall(helper, new LLVMValueRef[] + { + GetShadowStack(), + GetGenericContext() + }, "getHelper"); + } + else + { + var runtimeMethodHandleNode = _compilation.NodeFactory.RuntimeMethodHandle(runtimeDeterminedMethod); + _dependencies.Add(runtimeMethodHandleNode); + runtimeMethodHandle = LoadAddressOfSymbolNode(runtimeMethodHandleNode); + } + + var lookupSlotArgs = new StackEntry[] + { + objectPtr, + new ExpressionEntry(StackValueKind.ObjRef, "rmh", runtimeMethodHandle, GetWellKnownType(WellKnownType.Object)) + }; + var gvmPtr = CallRuntime(_compilation.TypeSystemContext, "TypeLoaderExports", "GVMLookupForSlot", lookupSlotArgs); + slotRef = gvmPtr.ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), _builder); + + var fatBranch = _currentFunclet.AppendBasicBlock("then"); + var notFatBranch = _currentFunclet.AppendBasicBlock("else"); + var endifBlock = _currentFunclet.AppendBasicBlock("endif"); + // if + var andResRef = _builder.BuildAnd(CastIfNecessary(_builder, slotRef, LLVMTypeRef.Int32), LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)_compilation.TypeSystemContext.Target.FatFunctionPointerOffset, false), "andPtrOffset"); + var eqz = _builder.BuildICmp(LLVMIntPredicate.LLVMIntEQ, andResRef, BuildConstInt32(0), "eqz"); + _builder.BuildCondBr(eqz, notFatBranch, fatBranch); + + // fat + _builder.PositionAtEnd(fatBranch); + var gep = RemoveFatOffset(_builder, slotRef); + var loadFuncPtr = _builder.BuildLoad(CastIfNecessary(_builder, gep, LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), 0)), + "loadFuncPtr"); + var dictPtrPtr = _builder.BuildGEP(CastIfNecessary(_builder, gep, + LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), 0), 0), "castDictPtrPtr"), + new [] {BuildConstInt32(1)}, "dictPtrPtr"); + _builder.BuildStore(dictPtrPtr, dictPtrPtrStore); + _builder.BuildBr(endifBlock); + + // not fat + _builder.PositionAtEnd(notFatBranch); + // store null to indicate the GVM call needs no hidden param at run time + _builder.BuildStore(LLVMValueRef.CreateConstPointerNull(LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), 0), 0)), dictPtrPtrStore); + _builder.BuildBr(endifBlock); + + // end if + _builder.PositionAtEnd(endifBlock); + var loadPtr = _builder.BuildPhi(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), "fatNotFatPhi"); + loadPtr.AddIncoming(new LLVMValueRef[] { loadFuncPtr, slotRef }, + 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"); + return asFunc; + } + + private 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 && returnType != GetWellKnownType(WellKnownType.Void)) + { + 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) + return CallRuntime(_compilation.TypeSystemContext, RuntimeExport, "RhNewObject", new StackEntry[] { eeType }, forcedReturnType); + } + + private static LLVMValueRef BuildConstInt1(int number) + { + Debug.Assert(number == 0 || number == 1, "Non-boolean int1"); + return LLVMValueRef.CreateConstInt(LLVMTypeRef.Int1, (ulong)number, false); + } + + private static LLVMValueRef BuildConstInt8(byte number) + { + return LLVMValueRef.CreateConstInt(LLVMTypeRef.Int8, number, false); + } + + private static LLVMValueRef BuildConstInt16(byte number) + { + return LLVMValueRef.CreateConstInt(LLVMTypeRef.Int16, number, false); + } + + private static LLVMValueRef BuildConstInt32(int number) + { + return LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)number, false); + } + + private static LLVMValueRef BuildConstUInt32(uint number) + { + return LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, number, false); + } + + private static LLVMValueRef BuildConstInt64(long number) + { + return LLVMValueRef.CreateConstInt(LLVMTypeRef.Int64, (ulong)number, false); + } + + private static LLVMValueRef BuildConstUInt64(ulong number) + { + return LLVMValueRef.CreateConstInt(LLVMTypeRef.Int64, number, false); + } + + private LLVMValueRef GetEETypeForTypeDesc(TypeDesc target, bool constructed) + { + var eeTypePointer = GetEETypePointerForTypeDesc(target, constructed); + return _builder.BuildLoad(eeTypePointer, "eeTypePtrLoad"); + } + + private LLVMValueRef GetEETypePointerForTypeDesc(TypeDesc target, bool constructed) + { + ISymbolNode node; + if (constructed) + { + node = _compilation.NodeFactory.MaximallyConstructableType(target); + } + else + { + node = _compilation.NodeFactory.NecessaryTypeSymbol(target); + } + LLVMValueRef eeTypePointer = LLVMObjectWriter.GetSymbolValuePointer(Module, node, _compilation.NameMangler, false); + _dependencies.Add(node); + + return eeTypePointer; + } + + /// + /// Implements intrinsic methods instread of calling them + /// + /// True if the method was implemented + private bool ImportIntrinsicCall(MethodDesc method, MethodDesc runtimeDeterminedMethod) + { + Debug.Assert(method.IsIntrinsic); + + if (!(method.OwningType is MetadataType metadataType)) + { + return false; + } + + switch (method.Name) + { + case "InitializeArray": + if (metadataType.Namespace == "System.Runtime.CompilerServices" && metadataType.Name == "RuntimeHelpers") + { + StackEntry fieldSlot = _stack.Pop(); + StackEntry arraySlot = _stack.Pop(); + + // TODO: Does fldHandle always come from ldtoken? If not, what to do with other cases? + if (!(fieldSlot is LdTokenEntry checkedFieldSlot) || + !(_compilation.GetFieldRvaData(checkedFieldSlot.LdToken) is BlobNode fieldNode)) + throw new InvalidProgramException("Provided field handle is invalid."); + + LLVMValueRef src = LoadAddressOfSymbolNode(fieldNode); + _dependencies.Add(fieldNode); + var fieldData = fieldNode.GetData(_compilation.NodeFactory, false).Data; + int srcLength = fieldData.Length; + + if (arraySlot.Type.IsArray) + { + // Handle single dimensional arrays (vectors) and multidimensional. + LLVMValueRef arrayObjPtr = arraySlot.ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), _builder); + + var argsType = new LLVMTypeRef[] + { + LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), + LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), + LLVMTypeRef.Int32, + LLVMTypeRef.Int1 + }; + LLVMValueRef memcpyFunction = GetOrCreateLLVMFunction("llvm.memcpy.p0i8.p0i8.i32", LLVMTypeRef.CreateFunction(LLVMTypeRef.Void, argsType, false)); + + LLVMValueRef offset; + if (arraySlot.Type.IsSzArray) + { + offset = ArrayBaseSizeRef(); + } + else + { + ArrayType arrayType = (ArrayType)arraySlot.Type; + offset = BuildConstInt32(ArrayBaseSize() + + 2 * sizeof(int) * arrayType.Rank); + } + var args = new LLVMValueRef[] + { + _builder.BuildGEP(arrayObjPtr, new LLVMValueRef[] { offset }, string.Empty), + _builder.BuildBitCast(src, LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), string.Empty), + BuildConstInt32(srcLength), // TODO: Handle destination array length to avoid runtime overflow. + BuildConstInt1(0) + }; + _builder.BuildCall(memcpyFunction, args, string.Empty); + } + else + { + // Handle object-typed first argument. This include System.Array typed array, and any ill-typed argument. + // TODO: Emit runtime type check code on array argument and further memcpy. + // TODO: Maybe a new runtime interface for this is better than hand-written code emission? + throw new NotImplementedException(); + } + + return true; + } + break; + case "get_Value": + if (metadataType.IsByReferenceOfT) + { + StackEntry byRefHolder = _stack.Pop(); + + TypeDesc byRefType = metadataType.Instantiation[0].MakeByRefType(); + PushLoadExpression(StackValueKind.ByRef, "byref", byRefHolder.ValueForStackKind(StackValueKind.ByRef, _builder, false), byRefType); + return true; + } + break; + case ".ctor": + if (metadataType.IsByReferenceOfT) + { + StackEntry byRefValueParamHolder = _stack.Pop(); + + // Allocate a slot on the shadow stack for the ByReference type + int spillIndex = _spilledExpressions.Count; + SpilledExpressionEntry spillEntry = new SpilledExpressionEntry(StackValueKind.ByRef, "byref" + _currentOffset, metadataType, spillIndex, this); + _spilledExpressions.Add(spillEntry); + LLVMValueRef addrOfValueType = LoadVarAddress(spillIndex, LocalVarKind.Temp, out TypeDesc unused); + var typedAddress = CastIfNecessary(_builder, addrOfValueType, LLVMTypeRef.CreatePointer(LLVMTypeRef.Int32, 0)); + _builder.BuildStore(byRefValueParamHolder.ValueForStackKind(StackValueKind.ByRef, _builder, false), typedAddress); + + _stack.Push(spillEntry); + return true; + } + break; + case "GetValueInternal": + if (metadataType.Namespace == "System" && metadataType.Name == "RuntimeTypeHandle") + { + var typeHandleSlot = (LdTokenEntry)_stack.Pop(); + TypeDesc typeOfEEType = typeHandleSlot.LdToken; + + if (typeOfEEType.IsRuntimeDeterminedSubtype) + { + var typeHandlerRef = CallGenericHelper(ReadyToRunHelperId.TypeHandle, typeOfEEType); + PushExpression(StackValueKind.Int32, "eeType", typeHandlerRef, GetWellKnownType(WellKnownType.IntPtr)); + } + else + { + PushLoadExpression(StackValueKind.Int32, "eeType", GetEETypePointerForTypeDesc(typeOfEEType, true), GetWellKnownType(WellKnownType.IntPtr)); + } + return true; + } + break; + case "DefaultConstructorOf": + if (metadataType.Namespace == "System" && metadataType.Name == "Activator" && method.Instantiation.Length == 1) + { + if (runtimeDeterminedMethod.IsRuntimeDeterminedExactMethod) + { + var ctorRef = CallGenericHelper(ReadyToRunHelperId.DefaultConstructor, runtimeDeterminedMethod.Instantiation[0]); + PushExpression(StackValueKind.Int32, "ctor", ctorRef, GetWellKnownType(WellKnownType.IntPtr)); + } + else + { + IMethodNode methodNode = (IMethodNode)_compilation.ComputeConstantLookup(ReadyToRunHelperId.DefaultConstructor, method.Instantiation[0]); + _dependencies.Add(methodNode); + + MethodDesc ctor = methodNode.Method; + PushExpression(StackValueKind.Int32, "ctor", LLVMFunctionForMethod(ctor, ctor, null, false, null, ctor, out bool _, out LLVMValueRef _, out LLVMValueRef _), GetWellKnownType(WellKnownType.IntPtr)); + } + + return true; + } + break; + } + + return false; + } + + // if the call is done via `invoke` then we need the try/then block passed back in case the calling code takes the result from a phi. + private LLVMBasicBlockRef HandleCall(MethodDesc callee, MethodSignature signature, MethodDesc runtimeDeterminedMethod, ILOpcode opcode = ILOpcode.call, TypeDesc constrainedType = null, LLVMValueRef calliTarget = default(LLVMValueRef), LLVMValueRef hiddenRef = default(LLVMValueRef)) + { + bool resolvedConstraint = false; + + var parameterCount = signature.Length + (signature.IsStatic ? 0 : 1); + // The last argument is the top of the stack. We need to reverse them and store starting at the first argument + StackEntry[] argumentValues = new StackEntry[parameterCount]; + for (int i = 0; i < argumentValues.Length; i++) + { + argumentValues[argumentValues.Length - i - 1] = _stack.Pop(); + } + if (constrainedType != null) + { + if (signature.IsStatic) + { + // Constrained call on static method + ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramSpecific, _method); + } + StackEntry thisByRef = argumentValues[0]; + if (thisByRef.Kind != StackValueKind.ByRef) + { + // Constrained call without byref + ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramSpecific, _method); + } + + // If this is a constrained call and the 'this' pointer is a reference type, it's a byref, + // dereference it before calling. + if (!constrainedType.IsValueType) + { + TypeDesc objectType = thisByRef.Type.GetParameterType(); + argumentValues[0] = new LoadExpressionEntry(StackValueKind.ObjRef, "thisPtr", thisByRef.ValueAsType(objectType, _builder), objectType); + } + else if (opcode == ILOpcode.callvirt) + { + var canonConstrainedType = constrainedType; + if (constrainedType.IsRuntimeDeterminedSubtype) + canonConstrainedType = constrainedType.ConvertToCanonForm(CanonicalFormKind.Specific); + + bool forceUseRuntimeLookup; + var constrainedClosestDefType = canonConstrainedType.GetClosestDefType(); + MethodDesc directMethod = constrainedClosestDefType.TryResolveConstraintMethodApprox(callee.OwningType, callee, out forceUseRuntimeLookup); + + if (directMethod == null) + { + StackEntry eeTypeEntry; + if (constrainedType.IsRuntimeDeterminedSubtype) + { + eeTypeEntry = new ExpressionEntry(StackValueKind.ValueType, "eeType", CallGenericHelper(ReadyToRunHelperId.TypeHandle, constrainedType), GetWellKnownType(WellKnownType.IntPtr)); + } + else + { + eeTypeEntry = new LoadExpressionEntry(StackValueKind.ValueType, "eeType", GetEETypePointerForTypeDesc(constrainedType, true), GetWellKnownType(WellKnownType.IntPtr)); + } + + argumentValues[0] = CallRuntime(_compilation.TypeSystemContext, RuntimeExport, "RhBox", + new StackEntry[] + { + eeTypeEntry, + argumentValues[0], + }); + } + else + { + callee = directMethod; + opcode = ILOpcode.call; + resolvedConstraint = true; + } + } + } + MethodDesc canonMethod = callee?.GetCanonMethodTarget(CanonicalFormKind.Specific); + (ExpressionEntry, LLVMBasicBlockRef) returnDetails = HandleCall(callee, signature, canonMethod, argumentValues, runtimeDeterminedMethod, opcode, constrainedType, calliTarget, hiddenRef, resolvedConstraint); + PushNonNull(returnDetails.Item1); + return returnDetails.Item2; + } + + private (ExpressionEntry, LLVMBasicBlockRef) HandleCall(MethodDesc callee, MethodSignature signature, MethodDesc canonMethod, StackEntry[] argumentValues, MethodDesc runtimeDeterminedMethod, ILOpcode opcode = ILOpcode.call, + TypeDesc constrainedType = null, LLVMValueRef calliTarget = default(LLVMValueRef), LLVMValueRef hiddenParamRef = default(LLVMValueRef), bool resolvedConstraint = false, TypeDesc forcedReturnType = null, bool fromLandingPad = false, LLVMBuilderRef builder = default(LLVMBuilderRef)) + { + if (builder.Handle == IntPtr.Zero) + builder = _builder; + + LLVMValueRef fn; + bool hasHiddenParam = false; + LLVMValueRef hiddenParam = default; + LLVMValueRef dictPtrPtrStore = default; + LLVMValueRef fatFunctionPtr = default; + if (opcode == ILOpcode.calli) + { + fn = calliTarget; + hiddenParam = hiddenParamRef; + } + else + { + fn = LLVMFunctionForMethod(callee, canonMethod, signature.IsStatic ? null : argumentValues[0], opcode == ILOpcode.callvirt, constrainedType, runtimeDeterminedMethod, out hasHiddenParam, out dictPtrPtrStore, out fatFunctionPtr); + } + + int offset = GetTotalParameterOffset() + GetTotalLocalOffset(); + LLVMValueRef shadowStack = builder.BuildGEP(_currentFunclet.GetParam(0), + new LLVMValueRef[] { LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (uint)offset, false) }, + String.Empty); + var castShadowStack = builder.BuildPointerCast(shadowStack, LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), "castshadowstack"); + List llvmArgs = new List + { + castShadowStack + }; + + TypeDesc returnType = signature.ReturnType; + + bool needsReturnSlot = NeedsReturnStackSlot(signature); + SpilledExpressionEntry returnSlot = null; + var actualReturnType = forcedReturnType ?? returnType; + if (needsReturnSlot) + { + int returnIndex = _spilledExpressions.Count; + returnSlot = new SpilledExpressionEntry(GetStackValueKind(actualReturnType), callee?.Name + "_return", actualReturnType, returnIndex, this); + _spilledExpressions.Add(returnSlot); + LLVMValueRef returnAddress = LoadVarAddress(returnIndex, LocalVarKind.Temp, out TypeDesc unused); + LLVMValueRef castReturnAddress = builder.BuildPointerCast(returnAddress, LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), callee?.Name + "_castreturn"); + llvmArgs.Add(castReturnAddress); + } + + // for GVM, the hidden param is added conditionally at runtime. + if (opcode != ILOpcode.calli && fatFunctionPtr.Handle == IntPtr.Zero) + { + bool exactContextNeedsRuntimeLookup; + if (callee.HasInstantiation) + { + exactContextNeedsRuntimeLookup = callee.IsSharedByGenericInstantiations && !_isUnboxingThunk; + } + else + { + exactContextNeedsRuntimeLookup = callee.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any); + } + + if (hasHiddenParam) + { + if (exactContextNeedsRuntimeLookup) + { + if (!resolvedConstraint) + { + if (callee.RequiresInstMethodDescArg()) + { + hiddenParam = CallGenericHelper(ReadyToRunHelperId.MethodDictionary, runtimeDeterminedMethod); + } + else + { + hiddenParam = CallGenericHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedMethod.OwningType); + } + } + else + { + Debug.Assert(canonMethod.RequiresInstMethodTableArg() && constrainedType != null); + if (constrainedType.IsRuntimeDeterminedSubtype) + { + hiddenParam = CallGenericHelper(ReadyToRunHelperId.TypeHandle, constrainedType); + } + else + { + var constrainedTypeSymbol = _compilation.NodeFactory.ConstructedTypeSymbol(constrainedType); + _dependencies.Add(constrainedTypeSymbol); + hiddenParam = LoadAddressOfSymbolNode(constrainedTypeSymbol); + } + } + } + else + { + if (_isUnboxingThunk && _method.RequiresInstArg()) + { + hiddenParam = _currentFunclet.GetParam((uint)(1 + (NeedsReturnStackSlot(_signature) ? 1 : 0))); + } + else if (canonMethod.RequiresInstMethodDescArg()) + { + hiddenParam = LoadAddressOfSymbolNode(GetMethodGenericDictionaryNode(callee)); + } + else + { + var owningTypeSymbol = _compilation.NodeFactory.ConstructedTypeSymbol(callee.OwningType); + _dependencies.Add(owningTypeSymbol); + hiddenParam = LoadAddressOfSymbolNode(owningTypeSymbol); + } + } + } + } + + if (hiddenParam.Handle != IntPtr.Zero) + { + llvmArgs.Add(CastIfNecessary(hiddenParam, LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0))); + } + + // argument offset on the shadow stack + int argOffset = 0; + var instanceAdjustment = signature.IsStatic ? 0 : 1; + for (int index = 0; index < argumentValues.Length; index++) + { + StackEntry toStore = argumentValues[index]; + + bool isThisParameter = false; + TypeDesc argType; + if (index == 0 && !signature.IsStatic) + { + isThisParameter = true; + if (opcode == ILOpcode.calli) + argType = toStore.Type; + else if (callee.OwningType.IsValueType) + argType = callee.OwningType.MakeByRefType(); + else + argType = callee.OwningType; + } + else + { + argType = signature[index - instanceAdjustment]; + if (canonMethod != null && CanStoreTypeOnStack(argType)) + { + argType = canonMethod.Signature[index - instanceAdjustment]; + } + } + + LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(argType); + LLVMValueRef argValue = toStore.ValueAsType(valueType, builder); + + // Pass arguments as parameters if possible + if (!isThisParameter && CanStoreTypeOnStack(argType)) + { + llvmArgs.Add(argValue); + } + // Otherwise store them on the shadow stack + else + { + // The previous argument might have left this type unaligned, so pad if necessary + argOffset = PadOffset(argType, argOffset); + + ImportStoreHelper(argValue, valueType, castShadowStack, (uint)argOffset, builder: builder); + + argOffset += argType.GetElementSize().AsInt; + } + } + LLVMValueRef llvmReturn = default; + LLVMBasicBlockRef nextInstrBlock = default(LLVMBasicBlockRef); + if (fatFunctionPtr.Handle != IntPtr.Zero) // indicates GVM + { + // conditional call depending on if the function was fat/the dict hidden param is needed + // TODO: not sure this is always conditional, maybe there is some optimisation that can be done to not inject this conditional logic depending on the caller/callee + LLVMValueRef dict = builder.BuildLoad( dictPtrPtrStore, "dictPtrPtr"); + LLVMValueRef dictAsInt = builder.BuildPtrToInt(dict, LLVMTypeRef.Int32, "toInt"); + LLVMValueRef eqZ = builder.BuildICmp(LLVMIntPredicate.LLVMIntEQ, dictAsInt, BuildConstInt32(0), "eqz"); + var notFatBranch = _currentFunclet.AppendBasicBlock("notFat"); + var fatBranch = _currentFunclet.AppendBasicBlock("fat"); + var endifBlock = _currentFunclet.AppendBasicBlock("endif"); + builder.BuildCondBr(eqZ, notFatBranch, fatBranch); + + // then + builder.PositionAtEnd(notFatBranch); + ExceptionRegion currentTryRegion = GetCurrentTryRegion(); + LLVMValueRef notFatReturn = CallOrInvoke(fromLandingPad, builder, currentTryRegion, fn, llvmArgs.ToArray(), ref nextInstrBlock); + builder.BuildBr(endifBlock); + + // else + builder.PositionAtEnd(fatBranch); + var fnWithDict = builder.BuildCast(LLVMOpcode.LLVMBitCast, fn, LLVMTypeRef.CreatePointer(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); + builder.BuildBr(endifBlock); + + // endif + builder.PositionAtEnd(endifBlock); + if (!returnType.IsVoid && !needsReturnSlot) + + { + llvmReturn = builder.BuildPhi(GetLLVMTypeForTypeDesc(returnType), "callReturnPhi"); + llvmReturn.AddIncoming(new LLVMValueRef[] { notFatReturn, fatReturn }, + new LLVMBasicBlockRef[] { notFatBranch, fatBranch }, 2); + } + _currentBasicBlock.LastInternalBlock = endifBlock; + } + else + { + llvmReturn = CallOrInvoke(fromLandingPad, builder, GetCurrentTryRegion(), fn, llvmArgs.ToArray(), ref nextInstrBlock); + } + + if (!returnType.IsVoid) + { + return ( + needsReturnSlot + ? returnSlot + : ( + canonMethod != null && canonMethod.Signature.ReturnType != actualReturnType + ? CreateGenericReturnExpression(GetStackValueKind(actualReturnType), + callee?.Name + "_return", llvmReturn, actualReturnType) + : new ExpressionEntry(GetStackValueKind(actualReturnType), callee?.Name + "_return", + llvmReturn, actualReturnType)), + nextInstrBlock); + } + else + { + return (null, default(LLVMBasicBlockRef)); + } + } + + LLVMValueRef CallOrInvoke(bool fromLandingPad, LLVMBuilderRef builder, ExceptionRegion currentTryRegion, + LLVMValueRef fn, LLVMValueRef[] llvmArgs, ref LLVMBasicBlockRef nextInstrBlock) + { + LLVMValueRef retVal; + if (currentTryRegion == null || fromLandingPad) // not handling exceptions that occur in the LLVM landing pad determining the EH handler + { + retVal = builder.BuildCall(fn, llvmArgs, string.Empty); + } + else + { + nextInstrBlock = _currentFunclet.AppendBasicBlock(String.Format("Try{0:X}", _currentOffset)); + + retVal = builder.BuildInvoke(fn, llvmArgs, + nextInstrBlock, GetOrCreateLandingPad(currentTryRegion), string.Empty); + + AddInternalBasicBlock(nextInstrBlock); + builder.PositionAtEnd(_curBasicBlock); + } + return retVal; + } + + // generic structs need to be cast to the actualReturnType + private ExpressionEntry CreateGenericReturnExpression(StackValueKind stackValueKind, string calleeName, LLVMValueRef llvmReturn, TypeDesc actualReturnType) + { + Debug.Assert(llvmReturn.TypeOf.IsPackedStruct); + var destStruct = GetLLVMTypeForTypeDesc(actualReturnType).Undef; + for (uint elemNo = 0; elemNo < llvmReturn.TypeOf.StructElementTypesCount; elemNo++) + { + var elemValRef = _builder.BuildExtractValue(llvmReturn, elemNo, "ex" + elemNo); + destStruct = _builder.BuildInsertValue(destStruct, elemValRef, elemNo, "st" + elemNo); + } + return new ExpressionEntry(stackValueKind, calleeName, destStruct, actualReturnType); + } + + private LLVMBasicBlockRef GetOrCreateLandingPad(ExceptionRegion tryRegion) + { + Tuple landingPadKey = Tuple.Create(tryRegion.ILRegion.TryOffset, _currentFunclet.Handle); + if (_landingPads.TryGetValue(landingPadKey, out LLVMBasicBlockRef landingPad)) + { + return landingPad; + } + + if (GxxPersonality.Handle.Equals(IntPtr.Zero)) + { + GxxPersonalityType = LLVMTypeRef.CreateStruct(new[] {LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), LLVMTypeRef.Int32 }, false); + GxxPersonality = Module.AddFunction("__gxx_personality_v0", LLVMTypeRef.CreateFunction(GxxPersonalityType, new [] + { + LLVMTypeRef.Int32, + LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), + LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), + }, true)); + } + + landingPad = _currentFunclet.AppendBasicBlock("LandingPad" + tryRegion.ILRegion.TryOffset.ToString("X")); + _landingPads[landingPadKey] = landingPad; + + LLVMBuilderRef landingPadBuilder = Context.CreateBuilder(); + if (_debugFunction.Handle != IntPtr.Zero) + { + // we need a location if going to call something, e.g. InitFromEhInfo and the call could be inlined, this is an LLVM requirement + landingPadBuilder.CurrentDebugLocation = _builder.CurrentDebugLocation; + } + landingPadBuilder.PositionAtEnd(landingPad); + LLVMValueRef pad = landingPadBuilder.BuildLandingPad(GxxPersonalityType, GxxPersonality, 1, ""); + pad.AddClause(LLVMValueRef.CreateConstPointerNull(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0))); + pad.IsCleanup = true; // always enter this clause regardless of exception type - do our own exception type matching + if (RhpCallCatchFunclet.Handle.Equals(IntPtr.Zero)) + { + RhpCallCatchFunclet = GetOrCreateLLVMFunction("RhpCallCatchFunclet", LLVMTypeRef.CreateFunction(LLVMTypeRef.Int32, new [] + { + LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), + LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), + LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), + LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), + }, false)); + BuildCatchFunclet(Module, + new LLVMTypeRef[] + { + LLVMTypeRef.CreatePointer(LLVMTypeRef.CreateFunction(LLVMTypeRef.Int32, new LLVMTypeRef[] + { + LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0) + }, false), 0), // pHandlerIP - catch funcletAddress + LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), // shadow stack + }); + BuildFilterFunclet(Module, + new LLVMTypeRef[] + { + LLVMTypeRef.CreatePointer(LLVMTypeRef.CreateFunction(LLVMTypeRef.Int32, new LLVMTypeRef[] + { + LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), // shadow stack + }, false), 0), // pHandlerIP - catch funcletAddress + LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), // shadow stack + }); + BuildFinallyFunclet(Module); + } + + // __cxa_begin catch to get the c++ exception object, must be paired with __cxa_end_catch (http://libcxxabi.llvm.org/spec.html) + var exPtr = landingPadBuilder.BuildCall(GetCxaBeginCatchFunction(), new LLVMValueRef[] { landingPadBuilder.BuildExtractValue(pad, 0, "ex") }); + + // unwrap managed, cast to 32bit pointer from 8bit personality signature pointer + var ex32Ptr = landingPadBuilder.BuildPointerCast(exPtr, LLVMTypeRef.CreatePointer(LLVMTypeRef.Int32, 0)); + var plus4 = landingPadBuilder.BuildGEP(ex32Ptr, new LLVMValueRef[] {BuildConstInt32(1)}, "offset"); + + var managedPtr = landingPadBuilder.BuildLoad(plus4, "managedEx"); + + // WASMTODO: should this really be a newobj call? + LLVMTypeRef ehInfoIteratorType = LLVMTypeRef.CreateStruct(new LLVMTypeRef[] { LLVMTypeRef.Int32, LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0) }, false); + var ehInfoIterator = landingPadBuilder.BuildAlloca(ehInfoIteratorType, "ehInfoIterPtr"); + + var iteratorInitArgs = new StackEntry[] { + new ExpressionEntry(StackValueKind.ObjRef, "ehInfoIter", ehInfoIterator), + new ExpressionEntry(StackValueKind.ByRef, "ehInfoStart", LoadAddressOfSymbolNode(_ehInfoNode, landingPadBuilder)), + new ExpressionEntry(StackValueKind.ByRef, "ehInfoEnd", LoadAddressOfSymbolNode(_ehInfoNode.EndSymbol, landingPadBuilder)), + new ExpressionEntry(StackValueKind.Int32, "idxStart", LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 0, false)) }; + var res = CallRuntime(_compilation.TypeSystemContext, "EHClauseIterator", "InitFromEhInfo", iteratorInitArgs, null, fromLandingPad: true, builder: landingPadBuilder); + + // params are: + // object exception, uint idxStart, + // ref StackFrameIterator frameIter, out uint tryRegionIdx, out byte* pHandler + var tryRegionIdx = landingPadBuilder.BuildAlloca(LLVMTypeRef.Int32, "tryRegionIdx"); + var handlerFuncPtr = landingPadBuilder.BuildAlloca(LLVMTypeRef.Int32, "handlerFuncPtr"); + + // put the exception in the spilled slot, exception slot is at 0 + var addressValue = CastIfNecessary(landingPadBuilder, LoadVarAddress(_spilledExpressions[0].LocalIndex, + LocalVarKind.Temp, out TypeDesc unused, builder:landingPadBuilder), + LLVMTypeRef.CreatePointer(LLVMTypeRef.Int32, 0)); + landingPadBuilder.BuildStore(managedPtr, addressValue); + + var arguments = new StackEntry[] { new ExpressionEntry(StackValueKind.ObjRef, "managedPtr", managedPtr), + new ExpressionEntry(StackValueKind.Int32, "idxStart", LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 0xFFFFFFFFu, false)), + new ExpressionEntry(StackValueKind.Int32, "idxCurrentBlockStart", LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)_currentBasicBlock.StartOffset, false)), + new ExpressionEntry(StackValueKind.NativeInt, "shadowStack", _currentFunclet.GetParam(0)), + new ExpressionEntry(StackValueKind.ByRef, "refFrameIter", ehInfoIterator), + new ExpressionEntry(StackValueKind.ByRef, "tryRegionIdx", tryRegionIdx), + new ExpressionEntry(StackValueKind.ByRef, "pHandler", handlerFuncPtr) + }; + var handler = CallRuntime(_compilation.TypeSystemContext, "EH", "FindFirstPassHandlerWasm", arguments, null, fromLandingPad: true, builder: landingPadBuilder); + var handlerFunc = landingPadBuilder.BuildLoad(handlerFuncPtr, "handlerFunc"); + + var leaveDestination = landingPadBuilder.BuildAlloca(LLVMTypeRef.Int32, "leaveDest"); // create a variable to store the operand of the leave as we can't use the result of the call directly due to domination/branches + landingPadBuilder.BuildStore(LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 0, false), leaveDestination); + var foundCatchBlock = _currentFunclet.AppendBasicBlock("LPFoundCatch"); + // If it didn't find a catch block, we can rethrow (resume in LLVM) the C++ exception to continue the stack walk. + var noCatch = landingPadBuilder.BuildICmp(LLVMIntPredicate.LLVMIntEQ, LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 0, false), + handler.ValueAsInt32(landingPadBuilder, false), "testCatch"); + var secondPassBlock = _currentFunclet.AppendBasicBlock("SecondPass"); + landingPadBuilder.BuildCondBr(noCatch, secondPassBlock, foundCatchBlock); + + landingPadBuilder.PositionAtEnd(foundCatchBlock); + // finished with the c++ exception + landingPadBuilder.BuildCall(GetCxaEndCatchFunction(), new LLVMValueRef[] { }); + + LLVMValueRef[] callCatchArgs = new LLVMValueRef[] + { + LLVMValueRef.CreateConstPointerNull(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0)), + CastIfNecessary(landingPadBuilder, handlerFunc, LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0)), /* catch funclet address */ + _currentFunclet.GetParam(0), + LLVMValueRef.CreateConstPointerNull(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0)) + }; + LLVMValueRef leaveReturnValue = landingPadBuilder.BuildCall(RhpCallCatchFunclet, callCatchArgs, ""); + + landingPadBuilder.BuildStore(leaveReturnValue, leaveDestination); + landingPadBuilder.BuildBr(secondPassBlock); + + landingPadBuilder.PositionAtEnd(secondPassBlock); + + // reinitialise the iterator + CallRuntime(_compilation.TypeSystemContext, "EHClauseIterator", "InitFromEhInfo", iteratorInitArgs, null, fromLandingPad: true, builder: landingPadBuilder); + + var secondPassArgs = new StackEntry[] { new ExpressionEntry(StackValueKind.Int32, "idxStart", LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 0xFFFFFFFFu, false)), + new ExpressionEntry(StackValueKind.Int32, "idxTryLandingStart", LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)tryRegion.ILRegion.TryOffset, false)), + new ExpressionEntry(StackValueKind.ByRef, "refFrameIter", ehInfoIterator), + new ExpressionEntry(StackValueKind.Int32, "idxLimit", LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 0xFFFFFFFFu, false)), + new ExpressionEntry(StackValueKind.NativeInt, "shadowStack", _currentFunclet.GetParam(0)) + }; + CallRuntime(_compilation.TypeSystemContext, "EH", "InvokeSecondPassWasm", secondPassArgs, null, true, builder: landingPadBuilder); + + var catchLeaveBlock = _currentFunclet.AppendBasicBlock("CatchLeave"); + landingPadBuilder.BuildCondBr(noCatch, GetOrCreateResumeBlock(pad, tryRegion.ILRegion.TryOffset.ToString()), catchLeaveBlock); + landingPadBuilder.PositionAtEnd(catchLeaveBlock); + + // Use the else as the path for no exception handler found for this exception + LLVMValueRef @switch = landingPadBuilder.BuildSwitch(landingPadBuilder.BuildLoad(leaveDestination, "loadLeaveDest"), GetOrCreateUnreachableBlock(), 1 /* number of cases, but fortunately this doesn't seem to make much difference */); + + if (_leaveTargets != null) + { + LLVMBasicBlockRef switchReturnBlock = default; + foreach (var leaveTarget in _leaveTargets) + { + var targetBlock = _basicBlocks[leaveTarget]; + var funcletForBlock = GetFuncletForBlock(targetBlock); + if (funcletForBlock.Handle.Equals(_currentFunclet.Handle)) + { + @switch.AddCase(BuildConstInt32(targetBlock.StartOffset), GetLLVMBasicBlockForBlock(targetBlock)); + } + else + { + + // leave destination is in a different funclet, this happens when an exception is thrown/rethrown from inside a catch handler and the throw is not directly in a try handler + // In this case we need to return out of this funclet to get back to the containing funclet. Logic checks we are actually in a catch funclet as opposed to a finally or the main function funclet + ExceptionRegion currentRegion = GetTryRegion(_currentBasicBlock.StartOffset); + if (currentRegion != null && _currentBasicBlock.StartOffset >= currentRegion.ILRegion.HandlerOffset && _currentBasicBlock.StartOffset < currentRegion.ILRegion.HandlerOffset + currentRegion.ILRegion.HandlerLength + && currentRegion.ILRegion.Kind == ILExceptionRegionKind.Catch) + { + if (switchReturnBlock == default) + { + switchReturnBlock = _currentFunclet.AppendBasicBlock("SwitchReturn"); + } + @switch.AddCase(BuildConstInt32(targetBlock.StartOffset), switchReturnBlock); + } + } + } + if (switchReturnBlock != default) + { + landingPadBuilder.PositionAtEnd(switchReturnBlock); + landingPadBuilder.BuildRet(landingPadBuilder.BuildLoad(leaveDestination, "loadLeaveDest")); + } + } + + landingPadBuilder.Dispose(); + + return landingPad; + } + + LLVMValueRef GetCxaBeginCatchFunction() + { + if (CxaBeginCatchFunction == default) + { + // takes the exception structure and returns the c++ exception, defined by emscripten + CxaBeginCatchFunction = Module.AddFunction("__cxa_begin_catch", LLVMTypeRef.CreateFunction(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), + new LLVMTypeRef[] { LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0)}, false)); + } + return CxaBeginCatchFunction; + } + + LLVMValueRef GetCxaEndCatchFunction() + { + if (CxaEndCatchFunction == default) + { + CxaEndCatchFunction = Module.AddFunction("__cxa_end_catch", LLVMTypeRef.CreateFunction(LLVMTypeRef.Void, new LLVMTypeRef[] { }, false)); + } + return CxaEndCatchFunction; + } + + private void AddMethodReference(MethodDesc method) + { + _dependencies.Add(_compilation.NodeFactory.MethodEntrypoint(method)); + } + + static Dictionary _pinvokeMap = new Dictionary(); + private void ImportRawPInvoke(MethodDesc method) + { + var arguments = new StackEntry[method.Signature.Length]; + for (int i = 0; i < arguments.Length; i++) + { + // Arguments are reversed on the stack + // Coerce pointers to the native type + arguments[arguments.Length - i - 1] = _stack.Pop(); + } + + + PushNonNull(ImportRawPInvoke(method, arguments, _builder)); + } + + private ExpressionEntry ImportRawPInvoke(MethodDesc method, StackEntry[] arguments, LLVMBuilderRef builder, TypeDesc forcedReturnType = null) + { + string realMethodName = method.Name; + + if (method.IsPInvoke) + { + string entrypointName = method.GetPInvokeMethodMetadata().Name; + if (!String.IsNullOrEmpty(entrypointName)) + { + realMethodName = entrypointName; + } + } + else if (!method.IsPInvoke && method is TypeSystem.Ecma.EcmaMethod) + { + realMethodName = ((TypeSystem.Ecma.EcmaMethod)method).GetRuntimeImportName() ?? method.Name; + } + MethodDesc existantDesc; + LLVMValueRef nativeFunc; + LLVMValueRef realNativeFunc = Module.GetNamedFunction(realMethodName); + if (_pinvokeMap.TryGetValue(realMethodName, out existantDesc)) + { + if (existantDesc != method) + { + // Set up native parameter types + nativeFunc = MakeExternFunction(method, realMethodName, realNativeFunc); + } + else + { + nativeFunc = realNativeFunc; + } + } + else + { + _pinvokeMap.Add(realMethodName, method); + nativeFunc = realNativeFunc; + } + + // Create an import if we haven't already + if (nativeFunc.Handle == IntPtr.Zero) + { + // Set up native parameter types + nativeFunc = MakeExternFunction(method, realMethodName); + } + + LLVMValueRef[] llvmArguments = new LLVMValueRef[method.Signature.Length]; + for (int i = 0; i < arguments.Length; i++) + { + TypeDesc signatureType = method.Signature[i]; + llvmArguments[i] = arguments[i].ValueAsType(GetLLVMTypeForTypeDesc(signatureType), _builder); + } + + // Save the top of the shadow stack in case the callee reverse P/Invokes + LLVMValueRef stackFrameSize = BuildConstInt32(GetTotalParameterOffset() + GetTotalLocalOffset()); + _builder.BuildStore(_builder.BuildGEP(_currentFunclet.GetParam(0), new LLVMValueRef[] {stackFrameSize}, "shadowStackTop"), ShadowStackTop); + + LLVMValueRef pInvokeTransitionFrame = default; + LLVMTypeRef pInvokeFunctionType = default; + if (method.IsPInvoke) + { + // add call to go to preemptive mode + LLVMTypeRef pInvokeTransitionFrameType = + LLVMTypeRef.CreateStruct(new LLVMTypeRef[] { LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0) }, false); + pInvokeFunctionType = LLVMTypeRef.CreateFunction(LLVMTypeRef.Void, new LLVMTypeRef[] { LLVMTypeRef.CreatePointer(pInvokeTransitionFrameType, 0) }, false); + pInvokeTransitionFrame = _builder.BuildAlloca(pInvokeTransitionFrameType, "PInvokeTransitionFrame"); + LLVMValueRef RhpPInvoke2 = GetOrCreateLLVMFunction("RhpPInvoke2", pInvokeFunctionType); + _builder.BuildCall(RhpPInvoke2, new LLVMValueRef[] { pInvokeTransitionFrame }, ""); + } + // Don't name the return value if the function returns void, it's invalid + var returnValue = _builder.BuildCall(nativeFunc, llvmArguments, !method.Signature.ReturnType.IsVoid ? "call" : string.Empty); + + if (method.IsPInvoke) + { + // add call to go to cooperative mode + LLVMValueRef RhpPInvokeReturn2 = GetOrCreateLLVMFunction("RhpPInvokeReturn2", pInvokeFunctionType); + _builder.BuildCall(RhpPInvokeReturn2, new LLVMValueRef[] { pInvokeTransitionFrame }, ""); + } + + if (!method.Signature.ReturnType.IsVoid) + return new ExpressionEntry(GetStackValueKind(method.Signature.ReturnType), "retval", returnValue, forcedReturnType ?? method.Signature.ReturnType); + else + return null; + } + + private LLVMValueRef MakeExternFunction(MethodDesc method, string realMethodName, LLVMValueRef realFunction = default(LLVMValueRef)) + { + LLVMValueRef nativeFunc; + LLVMTypeRef[] paramTypes = new LLVMTypeRef[method.Signature.Length]; + for (int i = 0; i < paramTypes.Length; i++) + { + paramTypes[i] = GetLLVMTypeForTypeDesc(method.Signature[i]); + } + + // Define the full signature + LLVMTypeRef nativeFuncType = LLVMTypeRef.CreateFunction(GetLLVMTypeForTypeDesc(method.Signature.ReturnType), paramTypes, false); + + if (realFunction.Handle == IntPtr.Zero) + { + nativeFunc = Module.AddFunction(realMethodName, nativeFuncType); + nativeFunc.Linkage = LLVMLinkage.LLVMDLLImportLinkage; + } + else + { + nativeFunc = _builder.BuildPointerCast(realFunction, LLVMTypeRef.CreatePointer(nativeFuncType, 0), realMethodName + "__slot__"); + } + return nativeFunc; + } + + static LLVMValueRef s_shadowStackTop = default(LLVMValueRef); + + LLVMValueRef ShadowStackTop + { + get + { + if (s_shadowStackTop.Handle.Equals(IntPtr.Zero)) + { + s_shadowStackTop = Module.AddGlobal(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), "t_pShadowStackTop"); + s_shadowStackTop.Linkage = LLVMLinkage.LLVMExternalLinkage; + s_shadowStackTop.Initializer = LLVMValueRef.CreateConstPointerNull(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0)); + s_shadowStackTop.ThreadLocalMode = LLVMThreadLocalMode.LLVMLocalDynamicTLSModel; + } + return s_shadowStackTop; + } + } + + private void EmitNativeToManagedThunk(LLVMCodegenCompilation compilation, MethodDesc method, string nativeName, LLVMValueRef managedFunction) + { + if (_pinvokeMap.TryGetValue(nativeName, out MethodDesc existing)) + { + if (existing != method) + throw new InvalidProgramException("export and import function were mismatched"); + } + else + { + _pinvokeMap.Add(nativeName, method); + } + + LLVMTypeRef[] llvmParams = new LLVMTypeRef[method.Signature.Length]; + for (int i = 0; i < llvmParams.Length; i++) + { + llvmParams[i] = GetLLVMTypeForTypeDesc(method.Signature[i]); + } + + LLVMTypeRef thunkSig = LLVMTypeRef.CreateFunction(GetLLVMTypeForTypeDesc(method.Signature.ReturnType), llvmParams, false); + LLVMValueRef thunkFunc = GetOrCreateLLVMFunction(nativeName, thunkSig); + + LLVMBasicBlockRef shadowStackSetupBlock = thunkFunc.AppendBasicBlock("ShadowStackSetupBlock"); + LLVMBasicBlockRef allocateShadowStackBlock = thunkFunc.AppendBasicBlock("allocateShadowStackBlock"); + LLVMBasicBlockRef managedCallBlock = thunkFunc.AppendBasicBlock("ManagedCallBlock"); + + LLVMBuilderRef builder = Context.CreateBuilder(); + builder.PositionAtEnd(shadowStackSetupBlock); + + // Allocate shadow stack if it's null + LLVMValueRef shadowStackPtr = builder.BuildAlloca(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), "ShadowStackPtr"); + LLVMValueRef savedShadowStack = builder.BuildLoad(ShadowStackTop, "SavedShadowStack"); + builder.BuildStore(savedShadowStack, shadowStackPtr); + LLVMValueRef shadowStackNull = builder.BuildICmp(LLVMIntPredicate.LLVMIntEQ, savedShadowStack, LLVMValueRef.CreateConstPointerNull(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0)), "ShadowStackNull"); + builder.BuildCondBr(shadowStackNull, allocateShadowStackBlock, managedCallBlock); + + builder.PositionAtEnd(allocateShadowStackBlock); + + LLVMValueRef newShadowStack = builder.BuildArrayMalloc(LLVMTypeRef.Int8, BuildConstInt32(1000000), "NewShadowStack"); + builder.BuildStore(newShadowStack, shadowStackPtr); + builder.BuildBr(managedCallBlock); + + builder.PositionAtEnd(managedCallBlock); + LLVMTypeRef reversePInvokeFrameType = LLVMTypeRef.CreateStruct(new LLVMTypeRef[] { LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0) }, false); + LLVMValueRef reversePInvokeFrame = default(LLVMValueRef); + LLVMTypeRef reversePInvokeFunctionType = LLVMTypeRef.CreateFunction(LLVMTypeRef.Void, new LLVMTypeRef[] { LLVMTypeRef.CreatePointer(reversePInvokeFrameType, 0) }, false); + if (method.IsUnmanagedCallersOnly) + { + reversePInvokeFrame = builder.BuildAlloca(reversePInvokeFrameType, "ReversePInvokeFrame"); + LLVMValueRef RhpReversePInvoke2 = GetOrCreateLLVMFunction("RhpReversePInvoke2", reversePInvokeFunctionType); + builder.BuildCall(RhpReversePInvoke2, new LLVMValueRef[] { reversePInvokeFrame }, ""); + } + + LLVMValueRef shadowStack = builder.BuildLoad(shadowStackPtr, "ShadowStack"); + int curOffset = 0; + curOffset = PadNextOffset(method.Signature.ReturnType, curOffset); + ImportCallMemset(shadowStack, 0, curOffset, builder); // clear any uncovered object references for GC.Collect + LLVMValueRef calleeFrame = builder.BuildGEP(shadowStack, new LLVMValueRef[] { BuildConstInt32(curOffset) }, "calleeFrame"); + + List llvmArgs = new List(); + llvmArgs.Add(calleeFrame); + + bool needsReturnSlot = NeedsReturnStackSlot(method.Signature); + + if (needsReturnSlot) + { + // Slot for return value if necessary + llvmArgs.Add(shadowStack); + } + + for (int i = 0; i < llvmParams.Length; i++) + { + LLVMValueRef argValue = thunkFunc.GetParam((uint)i); + + if (CanStoreTypeOnStack(method.Signature[i])) + { + llvmArgs.Add(argValue); + } + else + { + curOffset = PadOffset(method.Signature[i], curOffset); + LLVMValueRef argAddr = builder.BuildGEP(shadowStack, new LLVMValueRef[] { LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)curOffset, false) }, "arg" + i); + builder.BuildStore(argValue, CastIfNecessary(builder, argAddr, LLVMTypeRef.CreatePointer(llvmParams[i], 0), $"parameter{i}_")); + curOffset = PadNextOffset(method.Signature[i], curOffset); + } + } + + LLVMValueRef llvmReturnValue = builder.BuildCall(managedFunction, llvmArgs.ToArray(), ""); + + if (method.IsUnmanagedCallersOnly) + { + LLVMValueRef RhpReversePInvokeReturn2 = GetOrCreateLLVMFunction("RhpReversePInvokeReturn2", reversePInvokeFunctionType); + builder.BuildCall(RhpReversePInvokeReturn2, new LLVMValueRef[] { reversePInvokeFrame }, ""); + } + + if (!method.Signature.ReturnType.IsVoid) + { + if (needsReturnSlot) + { + builder.BuildRet(builder.BuildLoad(CastIfNecessary(builder, shadowStack, LLVMTypeRef.CreatePointer(GetLLVMTypeForTypeDesc(method.Signature.ReturnType), 0)), "returnValue")); + } + else + { + builder.BuildRet(llvmReturnValue); + } + } + else + { + builder.BuildRetVoid(); + } + } + + private void ImportCalli(int token) + { + MethodSignature methodSignature = (MethodSignature)_canonMethodIL.GetObject(token); + + var noHiddenParamSig = GetLLVMSignatureForMethod(methodSignature, false); + var hddenParamSig = GetLLVMSignatureForMethod(methodSignature, true); + var target = ((ExpressionEntry)_stack.Pop()).ValueAsType(LLVMTypeRef.CreatePointer(noHiddenParamSig, 0), _builder); + + var functionPtrAsInt = _builder.BuildPtrToInt(target, LLVMTypeRef.Int32, "ptrToInt"); + var andResRef = _builder.BuildBinOp(LLVMOpcode.LLVMAnd, functionPtrAsInt, LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)_compilation.TypeSystemContext.Target.FatFunctionPointerOffset, false), "andFatCheck"); + var boolConv = _builder.BuildICmp(LLVMIntPredicate.LLVMIntEQ, andResRef, BuildConstInt32(0), "bitConv"); + var fatBranch = _currentFunclet.AppendBasicBlock("fat"); + var notFatBranch = _currentFunclet.AppendBasicBlock("notFat"); + var endif = _currentFunclet.AppendBasicBlock("endif"); + _builder.BuildCondBr(boolConv, notFatBranch, fatBranch); + _builder.PositionAtEnd(notFatBranch); + + // non fat branch + var parameterCount = methodSignature.Length + (methodSignature.IsStatic ? 0 : 1); + StackEntry[] stackCopy = new StackEntry[parameterCount]; + for (int i = 0; i < stackCopy.Length; i++) + { + stackCopy[i] = _stack.Pop(); + } + for (int i = 0; i < stackCopy.Length; i++) + { + _stack.Push(stackCopy[stackCopy.Length - i - 1]); + } + var nonFatCallThenBlock = HandleCall(null, methodSignature, null, ILOpcode.calli, calliTarget: target); + LLVMValueRef fatResRef = default; + LLVMValueRef nonFatResRef = default; + bool hasRes = !methodSignature.ReturnType.IsVoid; + if (hasRes) + { + StackEntry nonFatRes = _stack.Pop(); + nonFatResRef = nonFatRes.ValueAsType(methodSignature.ReturnType, _builder); + } + _builder.BuildBr(endif); + _builder.PositionAtEnd(fatBranch); + + // fat branch + var minusOffset = RemoveFatOffset(_builder, target); + var minusOffsetPtr = _builder.BuildIntToPtr(minusOffset, + LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), "ptr"); + var hiddenRefAddr = _builder.BuildGEP(minusOffsetPtr, new[] { BuildConstInt32(_pointerSize) }, "fatArgPtr"); + var hiddenRefPtrPtr = _builder.BuildPointerCast(hiddenRefAddr, LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), 0), 0), "hiddenRefPtr"); + var hiddenRef = _builder.BuildLoad(_builder.BuildLoad(hiddenRefPtrPtr, "hiddenRefPtr"), "hiddenRef"); + + for (int i = 0; i < stackCopy.Length; i++) + { + _stack.Push(stackCopy[stackCopy.Length - i - 1]); + } + var funcPtrPtrWithHidden = _builder.BuildPointerCast(minusOffsetPtr, LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(hddenParamSig, 0), 0), "hiddenFuncPtr"); + var funcWithHidden = _builder.BuildLoad(funcPtrPtrWithHidden, "funcPtr"); + var fatCallThenBlock = HandleCall(null, methodSignature, null, ILOpcode.calli, calliTarget: funcWithHidden, hiddenRef: hiddenRef); + StackEntry fatRes = null; + if (hasRes) + { + fatRes = _stack.Pop(); + fatResRef = fatRes.ValueAsType(methodSignature.ReturnType, _builder); + } + _builder.BuildBr(endif); + _builder.PositionAtEnd(endif); + + // choose the right return value + if (hasRes) + { + var phi = _builder.BuildPhi(GetLLVMTypeForTypeDesc(methodSignature.ReturnType), "phi"); + phi.AddIncoming(new LLVMValueRef[] {fatResRef, nonFatResRef}, + new LLVMBasicBlockRef[] + { + fatCallThenBlock.Handle == IntPtr.Zero + ? fatBranch + : fatCallThenBlock, // phi requires the preceding blocks, which in the case of an `invoke` (when in a try block) will be the `then` block of the `invoke` + nonFatCallThenBlock.Handle == IntPtr.Zero + ? notFatBranch + : nonFatCallThenBlock // phi requires the preceding blocks, which in the case of an `invoke` (when in a try block) will be the `then` block of the `invoke` + }, 2); + PushExpression(fatRes.Kind, "phi", phi, fatRes.Type); + } + _currentBasicBlock.LastInternalBlock = endif; + } + + private void ImportLdFtn(int token, ILOpcode opCode) + { + MethodDesc runtimeDeterminedMethod = (MethodDesc)_methodIL.GetObject(token); + MethodDesc method = ((MethodDesc)_canonMethodIL.GetObject(token)); + MethodDesc canonMethod = method.GetCanonMethodTarget(CanonicalFormKind.Specific); + LLVMValueRef targetLLVMFunction = default; + bool hasHiddenParam = false; + + if (opCode == ILOpcode.ldvirtftn) + { + StackEntry thisPointer = _stack.Pop(); + if (runtimeDeterminedMethod.IsVirtual) + { + // we want the fat function ptr here + LLVMValueRef fatFunctionPtr; + targetLLVMFunction = LLVMFunctionForMethod(method, canonMethod, thisPointer, true, null, runtimeDeterminedMethod, out hasHiddenParam, out LLVMValueRef dictPtrPtrStore, out fatFunctionPtr); + if (fatFunctionPtr.Handle != IntPtr.Zero) + { + targetLLVMFunction = fatFunctionPtr; + } + } + else + { + AddMethodReference(runtimeDeterminedMethod); + } + } + else + { + if (canonMethod.IsSharedByGenericInstantiations && (canonMethod.HasInstantiation || canonMethod.Signature.IsStatic)) + { + var exactContextNeedsRuntimeLookup = method.HasInstantiation + ? method.IsSharedByGenericInstantiations + : method.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any); + if (exactContextNeedsRuntimeLookup) + { + targetLLVMFunction = CallGenericHelper(ReadyToRunHelperId.MethodEntry, runtimeDeterminedMethod); + if (!(canonMethod.IsVirtual && !canonMethod.IsFinal && !canonMethod.OwningType.IsSealed())) + { + // fat function pointer + targetLLVMFunction = MakeFatPointer(_builder, targetLLVMFunction, _compilation); + } + } + else + { + var fatFunctionSymbol = GetAndAddFatFunctionPointer(runtimeDeterminedMethod); + targetLLVMFunction = MakeFatPointer(_builder, LoadAddressOfSymbolNode(fatFunctionSymbol), _compilation); + } + } + else AddMethodReference(canonMethod); + } + + if (targetLLVMFunction.Handle.Equals(IntPtr.Zero)) + { + if (runtimeDeterminedMethod.IsUnmanagedCallersOnly) + { + EcmaMethod ecmaMethod = ((EcmaMethod)runtimeDeterminedMethod); + string mangledName = ecmaMethod.GetUnmanagedCallersOnlyExportName(); + if (mangledName == null) + { + mangledName = ecmaMethod.Name; + } + LLVMTypeRef[] llvmParams = new LLVMTypeRef[runtimeDeterminedMethod.Signature.Length]; + for (int i = 0; i < llvmParams.Length; i++) + { + llvmParams[i] = GetLLVMTypeForTypeDesc(runtimeDeterminedMethod.Signature[i]); + } + LLVMTypeRef thunkSig = LLVMTypeRef.CreateFunction(GetLLVMTypeForTypeDesc(runtimeDeterminedMethod.Signature.ReturnType), llvmParams, false); + + targetLLVMFunction = GetOrCreateLLVMFunction(mangledName, thunkSig); + } + else + { + hasHiddenParam = canonMethod.RequiresInstArg(); + targetLLVMFunction = GetOrCreateLLVMFunction(_compilation.NameMangler.GetMangledMethodName(canonMethod).ToString(), runtimeDeterminedMethod.Signature, hasHiddenParam); + } + } + + var entry = new FunctionPointerEntry("ldftn", runtimeDeterminedMethod, targetLLVMFunction, GetWellKnownType(WellKnownType.IntPtr), opCode == ILOpcode.ldvirtftn); + _stack.Push(entry); + } + + ISymbolNode GetAndAddFatFunctionPointer(MethodDesc method, bool isUnboxingStub = false) + { + ISymbolNode node = _compilation.NodeFactory.FatFunctionPointer(method, isUnboxingStub); + _dependencies.Add(node); + return node; + } + + private void ImportLoadInt(long value, StackValueKind kind) + { + switch (kind) + { + case StackValueKind.Int32: + case StackValueKind.NativeInt: + _stack.Push(new Int32ConstantEntry((int)value, _method.Context.GetWellKnownType(WellKnownType.Int32))); + break; + + case StackValueKind.Int64: + _stack.Push(new Int64ConstantEntry(value, _method.Context.GetWellKnownType(WellKnownType.Int64))); + break; + + default: + throw new InvalidOperationException(kind.ToString()); + } + + } + + private void ImportLoadFloat(double value) + { + _stack.Push(new FloatConstantEntry(value, _method.Context.GetWellKnownType(WellKnownType.Double))); + } + + private void ImportBranch(ILOpcode opcode, BasicBlock target, BasicBlock fallthrough) + { + if (opcode == ILOpcode.br) + { + ImportFallthrough(target); + _builder.BuildBr(GetLLVMBasicBlockForBlock(target)); + } + else + { + LLVMValueRef condition; + + if (opcode == ILOpcode.brfalse || opcode == ILOpcode.brtrue) + { + var op = _stack.Pop(); + LLVMValueRef value = op.ValueAsInt32(_builder, false); + + if (value.TypeOf.Kind != LLVMTypeKind.LLVMIntegerTypeKind) + throw new InvalidProgramException("branch on non integer"); + + if (opcode == ILOpcode.brfalse) + { + condition = _builder.BuildICmp(LLVMIntPredicate.LLVMIntEQ, value, LLVMValueRef.CreateConstInt(value.TypeOf, 0, false), "brfalse"); + } + else + { + condition = _builder.BuildICmp(LLVMIntPredicate.LLVMIntNE, value, LLVMValueRef.CreateConstInt(value.TypeOf, 0, false), "brtrue"); + } + } + else + { + var op1 = _stack.Pop(); + var op2 = _stack.Pop(); + + // StackValueKind is carefully ordered to make this work (assuming the IL is valid) + StackValueKind kind; + + if (op1.Kind > op2.Kind) + { + kind = op1.Kind; + } + else + { + kind = op2.Kind; + } + + LLVMValueRef right = op1.ValueForStackKind(kind, _builder, TypeNeedsSignExtension(op1.Type)); + LLVMValueRef left = op2.ValueForStackKind(kind, _builder, TypeNeedsSignExtension(op2.Type)); + + if (kind != StackValueKind.Float) + { + switch (opcode) + { + case ILOpcode.beq: + condition = _builder.BuildICmp(LLVMIntPredicate.LLVMIntEQ, left, right, "beq"); + break; + case ILOpcode.bge: + condition = _builder.BuildICmp(LLVMIntPredicate.LLVMIntSGE, left, right, "bge"); + break; + case ILOpcode.bgt: + condition = _builder.BuildICmp(LLVMIntPredicate.LLVMIntSGT, left, right, "bgt"); + break; + case ILOpcode.ble: + condition = _builder.BuildICmp(LLVMIntPredicate.LLVMIntSLE, left, right, "ble"); + break; + case ILOpcode.blt: + condition = _builder.BuildICmp(LLVMIntPredicate.LLVMIntSLT, left, right, "blt"); + break; + case ILOpcode.bne_un: + condition = _builder.BuildICmp(LLVMIntPredicate.LLVMIntNE, left, right, "bne_un"); + break; + case ILOpcode.bge_un: + condition = _builder.BuildICmp(LLVMIntPredicate.LLVMIntUGE, left, right, "bge_un"); + break; + case ILOpcode.bgt_un: + condition = _builder.BuildICmp(LLVMIntPredicate.LLVMIntUGT, left, right, "bgt_un"); + break; + case ILOpcode.ble_un: + condition = _builder.BuildICmp(LLVMIntPredicate.LLVMIntULE, left, right, "ble_un"); + break; + case ILOpcode.blt_un: + condition = _builder.BuildICmp(LLVMIntPredicate.LLVMIntULT, left, right, "blt_un"); + break; + default: + throw new NotSupportedException(); // unreachable + } + } + else + { + if (op1.Type.IsWellKnownType(WellKnownType.Double) && op2.Type.IsWellKnownType(WellKnownType.Single)) + { + left = _builder.BuildFPExt(left, LLVMTypeRef.Double, "fpextop2"); + } + else if (op2.Type.IsWellKnownType(WellKnownType.Double) && op1.Type.IsWellKnownType(WellKnownType.Single)) + { + right = _builder.BuildFPExt(right, LLVMTypeRef.Double, "fpextop1"); + } + switch (opcode) + { + case ILOpcode.beq: + condition = _builder.BuildFCmp(LLVMRealPredicate.LLVMRealOEQ, left, right, "beq"); + break; + case ILOpcode.bge: + condition = _builder.BuildFCmp(LLVMRealPredicate.LLVMRealOGE, left, right, "bge"); + break; + case ILOpcode.bgt: + condition = _builder.BuildFCmp(LLVMRealPredicate.LLVMRealOGT, left, right, "bgt"); + break; + case ILOpcode.ble: + condition = _builder.BuildFCmp(LLVMRealPredicate.LLVMRealOLE, left, right, "ble"); + break; + case ILOpcode.blt: + condition = _builder.BuildFCmp(LLVMRealPredicate.LLVMRealOLT, left, right, "blt"); + break; + case ILOpcode.bne_un: + condition = _builder.BuildFCmp(LLVMRealPredicate.LLVMRealONE, left, right, "bne_un"); + break; + case ILOpcode.bge_un: + condition = _builder.BuildFCmp(LLVMRealPredicate.LLVMRealUGE, left, right, "bge_un"); + break; + case ILOpcode.bgt_un: + condition = _builder.BuildFCmp(LLVMRealPredicate.LLVMRealUGT, left, right, "bgt_un"); + break; + case ILOpcode.ble_un: + condition = _builder.BuildFCmp(LLVMRealPredicate.LLVMRealULE, left, right, "ble_un"); + break; + case ILOpcode.blt_un: + condition = _builder.BuildFCmp(LLVMRealPredicate.LLVMRealULT, left, right, "blt_un"); + break; + default: + throw new NotSupportedException(); // unreachable + } + } + } + + ImportFallthrough(target); + ImportFallthrough(fallthrough); + _builder.BuildCondBr(condition, GetLLVMBasicBlockForBlock(target), GetLLVMBasicBlockForBlock(fallthrough)); + } + } + + private void ImportSwitchJump(int jmpBase, int[] jmpDelta, BasicBlock fallthrough) + { + var operand = _stack.Pop(); + + var @switch = _builder.BuildSwitch(operand.ValueAsInt32(_builder, false), GetLLVMBasicBlockForBlock(fallthrough), (uint)jmpDelta.Length); + for (var i = 0; i < jmpDelta.Length; i++) + { + var target = _basicBlocks[_currentOffset + jmpDelta[i]]; + @switch.AddCase(LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)i, false), GetLLVMBasicBlockForBlock(target)); + ImportFallthrough(target); + } + + ImportFallthrough(fallthrough); + } + + private void ImportLoadIndirect(int token) + { + ImportLoadIndirect(ResolveTypeToken(token)); + } + + private void ImportLoadIndirect(TypeDesc type) + { + var pointer = _stack.Pop(); + Debug.Assert(pointer is ExpressionEntry || pointer is ConstantEntry); + if (type == null) + { + type = GetWellKnownType(WellKnownType.Object); + } + + LLVMValueRef pointerElementType = pointer.ValueAsType(type.MakePointerType(), _builder); + _stack.Push(new LoadExpressionEntry(type != null ? GetStackValueKind(type) : StackValueKind.ByRef, $"Indirect{pointer.Name()}", + pointerElementType, type)); + } + + private void ImportStoreIndirect(int token) + { + ImportStoreIndirect(ResolveTypeToken(token)); + } + + private void ImportStoreIndirect(TypeDesc type) + { + StackEntry value = _stack.Pop(); + StackEntry destinationPointer = _stack.Pop(); + LLVMValueRef typedValue; + LLVMValueRef typedPointer; + bool requireWriteBarrier; + + if (type != null) + { + typedPointer = destinationPointer.ValueAsType(type.MakePointerType(), _builder); + typedValue = value.ValueAsType(type, _builder); + if (IsStruct(type)) + { + StoreStruct(typedPointer, typedValue, type, typedPointer); + return; + } + requireWriteBarrier = type.IsGCPointer; + } + else + { + typedPointer = destinationPointer.ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int32, 0), _builder); + typedValue = value.ValueAsInt32(_builder, false); + requireWriteBarrier = (value is ExpressionEntry) && !((ExpressionEntry)value).RawLLVMValue.IsNull && value.Type.IsGCPointer; + } + if (requireWriteBarrier) + { + CallRuntime(_method.Context, "InternalCalls", "RhpAssignRef", new StackEntry[] + { + new ExpressionEntry(StackValueKind.Int32, "typedPointer", typedPointer), value + }); + } + else + { + _builder.BuildStore(typedValue, typedPointer); + } + } + + private void ImportBinaryOperation(ILOpcode opcode) + { + StackEntry op1 = _stack.Pop(); + StackEntry op2 = _stack.Pop(); + + // StackValueKind is carefully ordered to make this work (assuming the IL is valid) + StackValueKind kind; + TypeDesc type; + + if (op1.Kind > op2.Kind) + { + kind = op1.Kind; + type = op1.Type; + } + else + { + kind = op2.Kind; + type = op2.Type; + } + + // The one exception from the above rule + if (kind == StackValueKind.ByRef) + { + kind = StackValueKind.NativeInt; + type = type.MakePointerType(); + } + + LLVMValueRef result; + LLVMValueRef left = op2.ValueForStackKind(kind, _builder, TypeNeedsSignExtension(op2.Type)); + LLVMValueRef right = op1.ValueForStackKind(kind, _builder, TypeNeedsSignExtension(op1.Type)); + if (kind == StackValueKind.Float) + { + if (op1.Type.IsWellKnownType(WellKnownType.Double) && op2.Type.IsWellKnownType(WellKnownType.Single)) + { + left = _builder.BuildFPExt(left, LLVMTypeRef.Double, "fpextop2"); + } + else if (op2.Type.IsWellKnownType(WellKnownType.Double) && op1.Type.IsWellKnownType(WellKnownType.Single)) + { + right = _builder.BuildFPExt(right, LLVMTypeRef.Double, "fpextop1"); + } + switch (opcode) + { + case ILOpcode.add: + result = _builder.BuildFAdd(left, right, "fadd"); + break; + case ILOpcode.sub: + result = _builder.BuildFSub(left, right, "fsub"); + break; + case ILOpcode.mul: + result = _builder.BuildFMul(left, right, "fmul"); + break; + case ILOpcode.div: + result = _builder.BuildFDiv(left, right, "fdiv"); + break; + case ILOpcode.rem: + result = _builder.BuildFRem(left, right, "frem"); + break; + default: + throw new InvalidOperationException(); // Should be unreachable + } + } + else + { + // these ops return an int32 for these. + type = WidenBytesAndShorts(type); + switch (opcode) + { + case ILOpcode.add: + result = _builder.BuildAdd(left, right, "add"); + break; + case ILOpcode.sub: + result = _builder.BuildSub(left, right, "sub"); + break; + case ILOpcode.mul: + result = _builder.BuildMul(left, right, "mul"); + break; + case ILOpcode.div: + result = _builder.BuildSDiv(left, right, "sdiv"); + break; + case ILOpcode.div_un: + result = _builder.BuildUDiv(left, right, "udiv"); + break; + case ILOpcode.rem: + result = _builder.BuildSRem(left, right, "srem"); + break; + case ILOpcode.rem_un: + result = _builder.BuildURem(left, right, "urem"); + break; + case ILOpcode.and: + result = _builder.BuildAnd(left, right, "and"); + break; + case ILOpcode.or: + result = _builder.BuildOr(left, right, "or"); + break; + case ILOpcode.xor: + result = _builder.BuildXor(left, right, "xor"); + break; + + case ILOpcode.add_ovf: + Debug.Assert(CanPerformSignedOverflowOperations(op1.Kind)); + if (Is32BitStackValue(op1.Kind)) + { + result = BuildArithmeticOperationWithOverflowCheck(left, right, "sadd", LLVMTypeRef.Int32); + } + else + { + result = BuildArithmeticOperationWithOverflowCheck(left, right, "sadd", LLVMTypeRef.Int64); + } + break; + case ILOpcode.add_ovf_un: + Debug.Assert(CanPerformUnsignedOverflowOperations(op1.Kind)); + if (Is32BitStackValue(op1.Kind)) + { + result = BuildArithmeticOperationWithOverflowCheck(left, right, "uadd", LLVMTypeRef.Int32); + } + else + { + result = BuildArithmeticOperationWithOverflowCheck(left, right, "uadd", LLVMTypeRef.Int64); + } + break; + case ILOpcode.sub_ovf: + Debug.Assert(CanPerformSignedOverflowOperations(op1.Kind)); + if (Is32BitStackValue(op1.Kind)) + { + result = BuildArithmeticOperationWithOverflowCheck(left, right, "ssub", LLVMTypeRef.Int32); + } + else + { + result = BuildArithmeticOperationWithOverflowCheck(left, right, "ssub", LLVMTypeRef.Int64); + } + break; + case ILOpcode.sub_ovf_un: + Debug.Assert(CanPerformUnsignedOverflowOperations(op1.Kind)); + if (Is32BitStackValue(op1.Kind)) + { + result = BuildArithmeticOperationWithOverflowCheck(left, right, "usub", LLVMTypeRef.Int32); + } + else + { + result = BuildArithmeticOperationWithOverflowCheck(left, right, "usub", LLVMTypeRef.Int64); + } + break; + case ILOpcode.mul_ovf_un: + Debug.Assert(CanPerformUnsignedOverflowOperations(op1.Kind)); + if (Is32BitStackValue(op1.Kind)) + { + result = BuildArithmeticOperationWithOverflowCheck(left, right, "umul", LLVMTypeRef.Int32); + } + else + { + result = BuildArithmeticOperationWithOverflowCheck(left, right, "umul", LLVMTypeRef.Int64); + } + break; + case ILOpcode.mul_ovf: + if (Is32BitStackValue(op1.Kind)) + { + result = BuildArithmeticOperationWithOverflowCheck(left, right, "smul", LLVMTypeRef.Int32); + } + else + { + result = BuildArithmeticOperationWithOverflowCheck(left, right, "smul", LLVMTypeRef.Int64); + } + break; + + default: + throw new InvalidOperationException(); // Should be unreachable + } + } + + + if (kind == StackValueKind.NativeInt || kind == StackValueKind.ByRef || kind == StackValueKind.ObjRef) + { + //we need to put the type back if we changed it because it started out a pointer + result = CastToTypeDesc(result, type); + } + PushExpression(kind, "binop", result, type); + } + + LLVMValueRef BuildArithmeticOperationWithOverflowCheck(LLVMValueRef left, LLVMValueRef right, string arithmeticOp, LLVMTypeRef intType) + { + LLVMValueRef mulFunction = GetOrCreateLLVMFunction("llvm." + arithmeticOp + ".with.overflow." + (intType == LLVMTypeRef.Int32 ? "i32" : "i64"), LLVMTypeRef.CreateFunction( + LLVMTypeRef.CreateStruct(new[] { intType, LLVMTypeRef.Int1}, false), new[] { intType, intType })); + LLVMValueRef mulRes = _builder.BuildCall(mulFunction, new[] {left, right}); + var overflow = _builder.BuildExtractValue(mulRes, 1); + LLVMBasicBlockRef overflowBlock = _currentFunclet.AppendBasicBlock("ovf"); + LLVMBasicBlockRef noOverflowBlock = _currentFunclet.AppendBasicBlock("no_ovf"); + _builder.BuildCondBr(overflow, overflowBlock, noOverflowBlock); + + _builder.PositionAtEnd(overflowBlock); + CallOrInvokeThrowException(_builder, "ThrowHelpers", "ThrowOverflowException"); + + _builder.PositionAtEnd(noOverflowBlock); + LLVMValueRef result = _builder.BuildExtractValue(mulRes, 0); + AddInternalBasicBlock(noOverflowBlock); + return result; + } + + void AddInternalBasicBlock(LLVMBasicBlockRef basicBlock) + { + _curBasicBlock = basicBlock; + _currentBasicBlock.LLVMBlocks.Add(_curBasicBlock); + _currentBasicBlock.LastInternalBlock = _curBasicBlock; + } + + bool CanPerformSignedOverflowOperations(StackValueKind kind) + { + return kind == StackValueKind.Int32 || kind == StackValueKind.Int64; + } + + bool CanPerformUnsignedOverflowOperations(StackValueKind kind) + { + return CanPerformSignedOverflowOperations(kind) || kind == StackValueKind.ByRef || + kind == StackValueKind.ObjRef || kind == StackValueKind.NativeInt; + } + + bool Is32BitStackValue(StackValueKind kind) + { + return kind == StackValueKind.Int32 || kind == StackValueKind.ByRef || kind == StackValueKind.ObjRef || kind == StackValueKind.NativeInt; + } + + private TypeDesc WidenBytesAndShorts(TypeDesc type) + { + switch (type.Category) + { + case TypeFlags.Byte: + case TypeFlags.SByte: + case TypeFlags.Int16: + case TypeFlags.UInt16: + return GetWellKnownType(WellKnownType.Int32); + default: + return type; + } + } + + private void ImportShiftOperation(ILOpcode opcode) + { + LLVMValueRef result; + StackEntry numBitsToShift = _stack.Pop(); + StackEntry valueToShift = _stack.Pop(); + + LLVMValueRef valueToShiftValue = valueToShift.ValueForStackKind(valueToShift.Kind, _builder, TypeNeedsSignExtension(valueToShift.Type)); + + // while it seems excessive that the bits to shift should need to be 64 bits, the LLVM docs say that both operands must be the same type and a compilation failure results if this is not the case. + LLVMValueRef rhs; + if (valueToShiftValue.TypeOf.Equals(LLVMTypeRef.Int64)) + { + rhs = numBitsToShift.ValueAsInt64(_builder, false); + } + else + { + rhs = numBitsToShift.ValueAsInt32(_builder, false); + } + switch (opcode) + { + case ILOpcode.shl: + result = _builder.BuildShl(valueToShiftValue, rhs, "shl"); + break; + case ILOpcode.shr: + result = _builder.BuildAShr(valueToShiftValue, rhs, "shr"); + break; + case ILOpcode.shr_un: + result = _builder.BuildLShr(valueToShiftValue, rhs, "shr"); + break; + default: + throw new InvalidOperationException(); // Should be unreachable + } + //TODO: do we need this if we sign extend above? + PushExpression(valueToShift.Kind, "shiftop", result, WidenBytesAndShorts(valueToShift.Type)); + } + + bool TypeNeedsSignExtension(TypeDesc targetType) + { + var enumCleanTargetType = targetType?.UnderlyingType; + if (enumCleanTargetType != null && targetType.IsPrimitive) + { + if (enumCleanTargetType.IsWellKnownType(WellKnownType.Byte) || + enumCleanTargetType.IsWellKnownType(WellKnownType.Boolean) || + enumCleanTargetType.IsWellKnownType(WellKnownType.Char) || + enumCleanTargetType.IsWellKnownType(WellKnownType.UInt16) || + enumCleanTargetType.IsWellKnownType(WellKnownType.UInt32) || + enumCleanTargetType.IsWellKnownType(WellKnownType.UInt64) || + enumCleanTargetType.IsWellKnownType(WellKnownType.UIntPtr)) + { + return false; + } + else + { + return true; + } + } + return false; + } + private void ImportCompareOperation(ILOpcode opcode) + { + var op1 = _stack.Pop(); + var op2 = _stack.Pop(); + + // StackValueKind is carefully ordered to make this work (assuming the IL is valid) + StackValueKind kind; + + if (op1.Kind > op2.Kind) + { + kind = op1.Kind; + } + else + { + kind = op2.Kind; + } + + LLVMValueRef result; + LLVMValueRef typeSaneOp1 = op1.ValueForStackKind(kind, _builder, TypeNeedsSignExtension(op1.Type)); + LLVMValueRef typeSaneOp2 = op2.ValueForStackKind(kind, _builder, TypeNeedsSignExtension(op2.Type)); + + if (kind != StackValueKind.Float) + { + switch (opcode) + { + case ILOpcode.ceq: + result = _builder.BuildICmp(LLVMIntPredicate.LLVMIntEQ, typeSaneOp2, typeSaneOp1, "ceq"); + break; + case ILOpcode.cgt: + result = _builder.BuildICmp(LLVMIntPredicate.LLVMIntSGT, typeSaneOp2, typeSaneOp1, "cgt"); + break; + case ILOpcode.clt: + result = _builder.BuildICmp(LLVMIntPredicate.LLVMIntSLT, typeSaneOp2, typeSaneOp1, "clt"); + break; + case ILOpcode.cgt_un: + result = _builder.BuildICmp(LLVMIntPredicate.LLVMIntUGT, typeSaneOp2, typeSaneOp1, "cgt_un"); + break; + case ILOpcode.clt_un: + result = _builder.BuildICmp(LLVMIntPredicate.LLVMIntULT, typeSaneOp2, typeSaneOp1, "clt_un"); + break; + default: + throw new NotSupportedException(); // unreachable + } + } + else + { + if (op1.Type.IsWellKnownType(WellKnownType.Double) && op2.Type.IsWellKnownType(WellKnownType.Single)) + { + typeSaneOp2 = _builder.BuildFPExt(typeSaneOp2, LLVMTypeRef.Double, "fpextop2"); + } + else if (op2.Type.IsWellKnownType(WellKnownType.Double) && op1.Type.IsWellKnownType(WellKnownType.Single)) + { + typeSaneOp1 = _builder.BuildFPExt(typeSaneOp1, LLVMTypeRef.Double, "fpextop1"); + } + switch (opcode) + { + case ILOpcode.ceq: + result = _builder.BuildFCmp(LLVMRealPredicate.LLVMRealOEQ, typeSaneOp2, typeSaneOp1, "ceq"); + break; + case ILOpcode.cgt: + result = _builder.BuildFCmp(LLVMRealPredicate.LLVMRealOGT, typeSaneOp2, typeSaneOp1, "cgt"); + break; + case ILOpcode.clt: + result = _builder.BuildFCmp(LLVMRealPredicate.LLVMRealOLT, typeSaneOp2, typeSaneOp1, "clt"); + break; + case ILOpcode.cgt_un: + result = _builder.BuildFCmp(LLVMRealPredicate.LLVMRealUGT, typeSaneOp2, typeSaneOp1, "cgt_un"); + break; + case ILOpcode.clt_un: + result = _builder.BuildFCmp(LLVMRealPredicate.LLVMRealULT, typeSaneOp2, typeSaneOp1, "clt_un"); + break; + default: + throw new NotSupportedException(); // unreachable + } + } + + PushExpression(StackValueKind.Int32, "cmpop", result, GetWellKnownType(WellKnownType.UInt32)); + } + + private void ImportConvert(WellKnownType wellKnownType, bool checkOverflow, bool unsigned) + { + //TODO checkOverflow - r_un & r_4, i & i_un + StackEntry value = _stack.Pop(); + TypeDesc destType = GetWellKnownType(wellKnownType); + + // Load the value and then convert it instead of using ValueAsType to avoid loading the incorrect size + LLVMValueRef loadedValue = value.ValueAsType(value.Type, _builder); + + ExpressionEntry expressionEntry; + if (checkOverflow) + { + Debug.Assert(destType is EcmaType); + if (IsLlvmReal(loadedValue.TypeOf)) + { + expressionEntry = BuildConvOverflowFromReal(value, loadedValue, (EcmaType)destType, wellKnownType, unsigned, value.Type); + } + else + { + expressionEntry = BuildConvOverflow(value.Name(), loadedValue, (EcmaType)destType, wellKnownType, unsigned, value.Type); + } + } + else + { + LLVMValueRef converted = CastIfNecessary(loadedValue, GetLLVMTypeForTypeDesc(destType), value.Name(), wellKnownType == WellKnownType.UInt64 /* unsigned is always false, so check for the type explicitly */); + expressionEntry = new ExpressionEntry(GetStackValueKind(destType), "conv", converted, destType); + } + _stack.Push(expressionEntry); + } + + private bool IsLlvmReal(LLVMTypeRef llvmTypeRef) + { + return llvmTypeRef == LLVMTypeRef.Float || llvmTypeRef == LLVMTypeRef.Double; + } + + ExpressionEntry BuildConvOverflowFromReal(StackEntry value, LLVMValueRef loadedValue, EcmaType destType, WellKnownType destWellKnownType, bool unsigned, TypeDesc sourceType) + { + //TODO: single overflow checks extend to doubles - this could be more efficient + if (value.Type == GetWellKnownType(WellKnownType.Single)) + { + value = new ExpressionEntry(StackValueKind.Float, "dbl", _builder.BuildFPExt(loadedValue, LLVMTypeRef.Double), GetWellKnownType(WellKnownType.Double)); + } + switch (destWellKnownType) + { + case WellKnownType.Byte: + case WellKnownType.SByte: + case WellKnownType.Int16: + case WellKnownType.UInt16: + var intExpression = CallRuntime("Internal.Runtime.CompilerHelpers", _method.Context, "MathHelpers", "Dbl2IntOvf", new[] {value}); + return BuildConvOverflow(value.Name(), intExpression.ValueForStackKind(StackValueKind.Int32, _builder, false), destType, destWellKnownType, unsigned, sourceType); + case WellKnownType.Int32: + return CallRuntime("Internal.Runtime.CompilerHelpers", _method.Context, "MathHelpers", "Dbl2IntOvf", new[] { value }); + case WellKnownType.UInt32: + case WellKnownType.UIntPtr: // TODO : 64bit. + return CallRuntime("Internal.Runtime.CompilerHelpers", _method.Context, "MathHelpers", "Dbl2UIntOvf", new[] { value }); + case WellKnownType.Int64: + return CallRuntime("Internal.Runtime.CompilerHelpers", _method.Context, "MathHelpers", "Dbl2LngOvf", new[] { value }); + case WellKnownType.UInt64: + return CallRuntime("Internal.Runtime.CompilerHelpers", _method.Context, "MathHelpers", "Dbl2ULngOvf", new[] { value }); + default: + throw new InvalidProgramException("Unsupported destination for singled/double overflow check"); + } + } + + ExpressionEntry BuildConvOverflow(string name, LLVMValueRef loadedValue, EcmaType destType, WellKnownType destWellKnownType, bool unsigned, TypeDesc sourceType) + { + ulong maxValue = 0; + long minValue = 0; + switch (destWellKnownType) + { + case WellKnownType.Byte: + maxValue = byte.MaxValue; + break; + case WellKnownType.SByte: + maxValue = (ulong)sbyte.MaxValue; + minValue = sbyte.MinValue; + break; + case WellKnownType.UInt16: + maxValue = ushort.MaxValue; + break; + case WellKnownType.Int16: + maxValue = (ulong)short.MaxValue; + minValue = short.MinValue; + break; + case WellKnownType.UInt32: + case WellKnownType.UIntPtr: // TODO : 64bit. + maxValue = uint.MaxValue; + break; + case WellKnownType.Int32: + maxValue = int.MaxValue; + minValue = int.MinValue; + break; + case WellKnownType.UInt64: + maxValue = ulong.MaxValue; + break; + case WellKnownType.Int64: + maxValue = long.MaxValue; + minValue = long.MinValue; + break; + } + BuildConvOverflowCheck(loadedValue, unsigned, maxValue, minValue, sourceType, destType); + LLVMValueRef converted = CastIfNecessary(loadedValue, GetLLVMTypeForTypeDesc(destType), name, unsigned); + return new ExpressionEntry(GetStackValueKind(destType), "conv", converted, destType); + } + + private void BuildConvOverflowCheck(LLVMValueRef loadedValue, bool unsigned, ulong maxValue, long minValue, TypeDesc sourceType, EcmaType destType) + { + var maxDiff = LLVMValueRef.CreateConstInt(loadedValue.TypeOf, maxValue - (ulong)minValue); + + LLVMBasicBlockRef overflowBlock = _currentFunclet.AppendBasicBlock("ovf"); + LLVMBasicBlockRef noOverflowBlock = _currentFunclet.AppendBasicBlock("no_ovf"); + LLVMValueRef cmp; + //special case same width signed -> unsigned, can just check for negative values + if (unsigned && (loadedValue.TypeOf.IntWidth >> 3) == destType.InstanceFieldSize.AsInt && + (sourceType == GetWellKnownType(WellKnownType.Int16) + || sourceType == GetWellKnownType(WellKnownType.Int32) + || sourceType == GetWellKnownType(WellKnownType.Int64))) + { + cmp = _builder.BuildICmp(LLVMIntPredicate.LLVMIntSLT, loadedValue, LLVMValueRef.CreateConstInt(loadedValue.TypeOf, 0)); + } + else + { + var valueDiff = _builder.BuildSub(loadedValue, LLVMValueRef.CreateConstInt(loadedValue.TypeOf, (ulong)minValue)); + cmp = _builder.BuildICmp(LLVMIntPredicate.LLVMIntUGT, valueDiff, maxDiff); + } + _builder.BuildCondBr(cmp, overflowBlock, noOverflowBlock); + + _builder.PositionAtEnd(overflowBlock); + CallOrInvokeThrowException(_builder, "ThrowHelpers", "ThrowOverflowException"); + _builder.PositionAtEnd(noOverflowBlock); + AddInternalBasicBlock(noOverflowBlock); + } + + private void ImportUnaryOperation(ILOpcode opCode) + { + var argument = _stack.Pop(); + + LLVMValueRef result; + switch (opCode) + { + case ILOpcode.neg: + if (argument.Kind == StackValueKind.Float) + { + result = _builder.BuildFNeg(argument.ValueForStackKind(argument.Kind, _builder, false), "neg"); + } + else + { + result = _builder.BuildNeg(argument.ValueForStackKind(argument.Kind, _builder, true), "neg"); + } + break; + case ILOpcode.not: + result = _builder.BuildNot(argument.ValueForStackKind(argument.Kind, _builder, true), "not"); + break; + default: + throw new NotSupportedException(); // unreachable + } + + PushExpression(argument.Kind, "unaryop", result, argument.Type); + } + + private void ImportCpOpj(int token) + { + var type = ResolveTypeToken(token); + + if (!type.IsValueType) + { + throw new InvalidOperationException(); + } + + var src = _stack.Pop(); + + if (src.Kind != StackValueKind.NativeInt && src.Kind != StackValueKind.ByRef && src.Kind != StackValueKind.ObjRef) + { + throw new InvalidOperationException(); + } + + var dest = _stack.Pop(); + + if (dest.Kind != StackValueKind.NativeInt && dest.Kind != StackValueKind.ByRef && dest.Kind != StackValueKind.ObjRef) + { + throw new InvalidOperationException(); + } + + var pointerType = GetLLVMTypeForTypeDesc(type.MakePointerType()); + + var value = _builder.BuildLoad(src.ValueAsType(pointerType, _builder), "cpobj.load"); + + _builder.BuildStore(value, dest.ValueAsType(pointerType, _builder)); + } + + private void ImportUnbox(int token, ILOpcode opCode) + { + TypeDesc type = ResolveTypeToken(token); + TypeDesc methodType = (TypeDesc)_methodIL.GetObject(token); + LLVMValueRef eeType; + ExpressionEntry eeTypeExp; + if (methodType.IsRuntimeDeterminedSubtype) + { + eeType = CallGenericHelper(ReadyToRunHelperId.TypeHandle, methodType); + eeTypeExp = new ExpressionEntry(StackValueKind.ByRef, "eeType", eeType, GetWellKnownType(WellKnownType.IntPtr)); + } + else + { + eeType = GetEETypePointerForTypeDesc(methodType, true); + eeTypeExp = new LoadExpressionEntry(StackValueKind.ByRef, "eeType", eeType, GetWellKnownType(WellKnownType.IntPtr)); + } + StackEntry boxedObject = _stack.Pop(); + if (opCode == ILOpcode.unbox) + { + if (methodType.IsNullable) + throw new NotImplementedException(); + + var arguments = new StackEntry[] { eeTypeExp, boxedObject }; + PushNonNull(CallRuntime(_compilation.TypeSystemContext, RuntimeExport, "RhUnbox2", arguments)); + } + else //unbox_any + { + Debug.Assert(opCode == ILOpcode.unbox_any); + LLVMValueRef untypedObjectValue = _builder.BuildAlloca(GetLLVMTypeForTypeDesc(methodType), "objptr"); + var arguments = new StackEntry[] + { + boxedObject, + new ExpressionEntry(StackValueKind.ByRef, "objPtr", untypedObjectValue, methodType.MakePointerType()), + eeTypeExp + }; + CallRuntime(_compilation.TypeSystemContext, RuntimeExport, "RhUnboxAny", arguments); + PushLoadExpression(GetStackValueKind(methodType), "unboxed", untypedObjectValue, methodType); + } + } + + LLVMValueRef GetShadowStack() + { + int offset = GetTotalParameterOffset() + GetTotalLocalOffset(); + return _builder.BuildGEP(_currentFunclet.GetParam(0), + new LLVMValueRef[] { LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (uint)offset, false) }, + String.Empty); + } + + private void ImportRefAnyVal(int token) + { + } + + private void ImportCkFinite() + { + StackEntry value = _stack.Pop(); + if (value.Type == GetWellKnownType(WellKnownType.Single)) + { + ThrowCkFinite(value.ValueForStackKind(value.Kind, _builder, false), 32, ref CkFinite32Function); + } + else + { + ThrowCkFinite(value.ValueForStackKind(value.Kind, _builder, false), 64, ref CkFinite64Function); + } + + _stack.Push(value); + } + + private void ImportMkRefAny(int token) + { + } + + private void ImportLdToken(int token) + { + var ldtokenValue = _methodIL.GetObject(token); + if (ldtokenValue is TypeDesc) + { + TypeDesc runtimeTypeHandleTypeDesc = GetWellKnownType(WellKnownType.RuntimeTypeHandle); + var typeDesc = (TypeDesc)ldtokenValue; + MethodDesc helper = _compilation.TypeSystemContext.GetHelperEntryPoint("LdTokenHelpers", "GetRuntimeTypeHandle"); + AddMethodReference(helper); + var fn = LLVMFunctionForMethod(helper, helper, null/* static method */, false /* not virt */, _constrainedType, helper, out bool hasHiddenParam, out LLVMValueRef dictPtrPtrStore, out LLVMValueRef fatFunctionPtr); + + if (typeDesc.IsRuntimeDeterminedSubtype) + { + var hiddenParam = CallGenericHelper(ReadyToRunHelperId.TypeHandle, typeDesc); + var handleRef = _builder.BuildCall( fn, new LLVMValueRef[] + { + GetShadowStack(), + hiddenParam + }, "getHelper"); + _stack.Push(new LdTokenEntry(StackValueKind.ValueType, "ldtoken", typeDesc, handleRef, runtimeTypeHandleTypeDesc)); + } + else + { + PushLoadExpression(StackValueKind.ByRef, "ldtoken", GetEETypePointerForTypeDesc(typeDesc, true), GetWellKnownType(WellKnownType.IntPtr)); + HandleCall(helper, helper.Signature, helper); + var callExp = _stack.Pop(); + _stack.Push(new LdTokenEntry(StackValueKind.ValueType, "ldtoken", typeDesc, callExp.ValueAsInt32(_builder, false), runtimeTypeHandleTypeDesc)); + } + } + else if (ldtokenValue is FieldDesc) + { + LLVMValueRef fieldHandle = LLVMValueRef.CreateConstStruct(new LLVMValueRef[] { BuildConstInt32(0) }, true); + StackEntry value = new LdTokenEntry(StackValueKind.ValueType, null, (FieldDesc)ldtokenValue, fieldHandle, GetWellKnownType(WellKnownType.RuntimeFieldHandle)); + _stack.Push(value); + } + else if (ldtokenValue is MethodDesc) + { + throw new NotImplementedException(); + } + else + { + throw new InvalidOperationException(); + } + } + + private void ImportLocalAlloc() + { + StackEntry allocSizeEntry = _stack.Pop(); + LLVMValueRef allocSize = allocSizeEntry.ValueAsInt32(_builder, false); + LLVMValueRef allocatedMemory = _builder.BuildArrayAlloca(LLVMTypeRef.Int8, allocSize, "localloc" + _currentOffset); + allocatedMemory.Alignment = (uint)_pointerSize; + if (_methodIL.IsInitLocals) + { + ImportCallMemset(allocatedMemory, 0, allocSize); + } + + PushExpression(StackValueKind.NativeInt, "localloc" + _currentOffset, allocatedMemory, _compilation.TypeSystemContext.GetPointerType(GetWellKnownType(WellKnownType.Void))); + } + + private void ImportEndFilter() + { + _builder.BuildRet(_stack.Pop().ValueAsInt32(_builder, false)); + } + + private void ImportCpBlk() + { + } + + private void ImportInitBlk() + { + } + + private void ImportRethrow() + { + // rethrows can only occur from a catch handler in which case there should be an exception slot + Debug.Assert(_spilledExpressions.Count > 0 && _spilledExpressions[0].Name == "ExceptionSlot"); + + ThrowOrRethrow(_spilledExpressions[0]); + } + + private void ImportSizeOf(int token) + { + TypeDesc type = (TypeDesc)_methodIL.GetObject(token); + int size = type.GetElementSize().AsInt; + PushExpression(StackValueKind.Int32, "sizeof", LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)size, false), GetWellKnownType(WellKnownType.Int32)); + } + + private void ImportRefAnyType() + { + } + + private void ImportArgList() + { + } + + private void ImportUnalignedPrefix(byte alignment) + { + } + + private void ImportVolatilePrefix() + { + } + + private void ImportTailPrefix() + { + } + + private void ImportConstrainedPrefix(int token) + { + _constrainedType = (TypeDesc)_methodIL.GetObject(token); + } + + private void ImportNoPrefix(byte mask) + { + } + + private void ImportReadOnlyPrefix() + { + } + + private void ImportThrow() + { + var exceptionObject = _stack.Pop(); + + ThrowOrRethrow(exceptionObject); + } + + void ThrowOrRethrow(StackEntry exceptionObject) + { + int offset = GetTotalParameterOffset() + GetTotalLocalOffset(); + LLVMValueRef shadowStack = _builder.BuildGEP(_currentFunclet.GetParam(0), + new LLVMValueRef[] { LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (uint)offset, false) }, + String.Empty); + LLVMValueRef exSlot = _builder.BuildBitCast(shadowStack, LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), 0)); + _builder.BuildStore(exceptionObject.ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), _builder), exSlot); + LLVMValueRef[] llvmArgs = new LLVMValueRef[] { shadowStack }; + MetadataType helperType = _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "Exception"); + MethodDesc helperMethod = helperType.GetKnownMethod("DispatchExLLVM", null); + LLVMValueRef fn = LLVMFunctionForMethod(helperMethod, helperMethod, null, false, null, null, out bool hasHiddenParam, out LLVMValueRef dictPtrPtrStore, out LLVMValueRef fatFunctionPtr); + ExceptionRegion currentExceptionRegion = GetCurrentTryRegion(); + _builder.BuildCall(fn, llvmArgs, string.Empty); + + if (RhpThrowEx.Handle.Equals(IntPtr.Zero)) + { + RhpThrowEx = Module.AddFunction("RhpThrowEx", LLVMTypeRef.CreateFunction(LLVMTypeRef.Void, new LLVMTypeRef[] { LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0) }, false)); + } + + LLVMValueRef[] args = new LLVMValueRef[] { exceptionObject.ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), _builder) }; + if (currentExceptionRegion == null) + { + _builder.BuildCall(RhpThrowEx, args, ""); + _builder.BuildUnreachable(); + } + else + { + _builder.BuildInvoke(RhpThrowEx, args, GetOrCreateUnreachableBlock(), GetOrCreateLandingPad(currentExceptionRegion), ""); + } + + for (int i = 0; i < _exceptionRegions.Length; i++) + { + var r = _exceptionRegions[i]; + + if (IsOffsetContained(_currentOffset - 1, r.ILRegion.TryOffset, r.ILRegion.TryLength)) + { + MarkBasicBlock(_basicBlocks[r.ILRegion.HandlerOffset]); + } + } + } + + private LLVMBasicBlockRef GetOrCreateUnreachableBlock() + { + if (_funcletUnreachableBlocks.TryGetValue(_currentFunclet.Handle, out LLVMBasicBlockRef unreachableBlock)) + { + return unreachableBlock; + } + + unreachableBlock = _currentFunclet.AppendBasicBlock("Unreachable"); + LLVMBuilderRef unreachableBuilder = Context.CreateBuilder(); + unreachableBuilder.PositionAtEnd(unreachableBlock); + unreachableBuilder.BuildUnreachable(); + unreachableBuilder.Dispose(); + _funcletUnreachableBlocks[_currentFunclet.Handle] = unreachableBlock; + + return unreachableBlock; + } + + private LLVMBasicBlockRef GetOrCreateResumeBlock(LLVMValueRef exceptionValueRef, string offset) + { + if (_funcletResumeBlocks.TryGetValue(exceptionValueRef.Handle, out LLVMBasicBlockRef resumeBlock)) + { + return resumeBlock; + } + + resumeBlock = _currentFunclet.AppendBasicBlock("Resume" + offset); + LLVMBuilderRef resumeBuilder = Context.CreateBuilder(); + resumeBuilder.PositionAtEnd(resumeBlock); + resumeBuilder.BuildResume(exceptionValueRef); + resumeBuilder.Dispose(); + _funcletResumeBlocks[_currentFunclet.Handle] = resumeBlock; + + return resumeBlock; + } + + private void ThrowIfNull(LLVMValueRef entry) + { + if (NullRefFunction.Handle == IntPtr.Zero) + { + NullRefFunction = Module.AddFunction("corert.throwifnull", LLVMTypeRef.CreateFunction(LLVMTypeRef.Void, new LLVMTypeRef[] { LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0) }, false)); + var builder = Context.CreateBuilder(); + var block = NullRefFunction.AppendBasicBlock("Block"); + var throwBlock = NullRefFunction.AppendBasicBlock("ThrowBlock"); + var retBlock = NullRefFunction.AppendBasicBlock("RetBlock"); + builder.PositionAtEnd(block); + builder.BuildCondBr(builder.BuildICmp(LLVMIntPredicate.LLVMIntEQ, NullRefFunction.GetParam(1), LLVMValueRef.CreateConstPointerNull(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0)), "nullCheck"), + throwBlock, retBlock); + builder.PositionAtEnd(throwBlock); + + ThrowException(builder, "ThrowHelpers", "ThrowNullReferenceException", NullRefFunction); + + builder.PositionAtEnd(retBlock); + builder.BuildRetVoid(); + } + + LLVMBasicBlockRef nextInstrBlock = default; + CallOrInvoke(false, _builder, GetCurrentTryRegion(), NullRefFunction, new LLVMValueRef[] { GetShadowStack(), entry }, ref nextInstrBlock); + } + + private void ThrowCkFinite(LLVMValueRef value, int size, ref LLVMValueRef llvmCheckFunction) + { + if (llvmCheckFunction.Handle == IntPtr.Zero) + { + llvmCheckFunction = Module.AddFunction("corert.throwckfinite" + size, LLVMTypeRef.CreateFunction(LLVMTypeRef.Void, new LLVMTypeRef[] { LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), size == 32 ? LLVMTypeRef.Float : LLVMTypeRef.Double }, false)); + LLVMValueRef exponentMask; + LLVMTypeRef intTypeRef; + var builder = Context.CreateBuilder(); + var block = llvmCheckFunction.AppendBasicBlock("Block"); + builder.PositionAtEnd(block); + + if (size == 32) + { + intTypeRef = LLVMTypeRef.Int32; + exponentMask = LLVMValueRef.CreateConstInt(intTypeRef, 0x7F800000, false); + } + else + { + intTypeRef = LLVMTypeRef.Int64; + exponentMask = LLVMValueRef.CreateConstInt(intTypeRef, 0x7FF0000000000000, false); + } + + var valRef = builder.BuildBitCast(llvmCheckFunction.GetParam(1), intTypeRef); + LLVMValueRef exponentBits = builder.BuildAnd(valRef, exponentMask, "and"); + LLVMValueRef isFinite = builder.BuildICmp(LLVMIntPredicate.LLVMIntEQ, exponentBits, exponentMask, "isfinite"); + + LLVMBasicBlockRef throwBlock = llvmCheckFunction.AppendBasicBlock("Throw"); + LLVMBasicBlockRef afterIf = llvmCheckFunction.AppendBasicBlock("AfterIf"); + builder.BuildCondBr(isFinite, throwBlock, afterIf); + + builder.PositionAtEnd(throwBlock); + + ThrowException(builder, "ThrowHelpers", "ThrowOverflowException", llvmCheckFunction); + + afterIf.MoveAfter(llvmCheckFunction.LastBasicBlock); + builder.PositionAtEnd(afterIf); + builder.BuildRetVoid(); + } + + LLVMBasicBlockRef nextInstrBlock = default; + CallOrInvoke(false, _builder, GetCurrentTryRegion(), llvmCheckFunction, new LLVMValueRef[] { GetShadowStack(), value }, ref nextInstrBlock); + } + + private void ThrowException(LLVMBuilderRef builder, string helperClass, string helperMethodName, LLVMValueRef throwingFunction) + { + LLVMValueRef fn = GetHelperLlvmMethod(helperClass, helperMethodName); + builder.BuildCall(fn, new LLVMValueRef[] {throwingFunction.GetParam(0) }, string.Empty); + builder.BuildUnreachable(); + } + + /// + /// Calls or invokes the call to throwing the exception so it can be caught in the caller + /// + private void CallOrInvokeThrowException(LLVMBuilderRef builder, string helperClass, string helperMethodName) + { + LLVMValueRef fn = GetHelperLlvmMethod(helperClass, helperMethodName); + LLVMBasicBlockRef nextInstrBlock = default; + CallOrInvoke(false, builder, GetCurrentTryRegion(), fn, new LLVMValueRef[] {GetShadowStack()}, ref nextInstrBlock); + builder.BuildUnreachable(); + } + + LLVMValueRef GetHelperLlvmMethod(string helperClass, string helperMethodName) + { + MetadataType helperType = _compilation.TypeSystemContext.SystemModule.GetKnownType("Internal.Runtime.CompilerHelpers", helperClass); + MethodDesc helperMethod = helperType.GetKnownMethod(helperMethodName, null); + LLVMValueRef fn = LLVMFunctionForMethod(helperMethod, helperMethod, null, false, null, null, out bool hasHiddenParam, out LLVMValueRef dictPtrPtrStore, out LLVMValueRef fatFunctionPtr); + return fn; + } + + private LLVMValueRef GetInstanceFieldAddress(StackEntry objectEntry, FieldDesc field) + { + var objectType = objectEntry.Type ?? field.OwningType; + LLVMValueRef untypedObjectValue; + LLVMTypeRef llvmObjectType = GetLLVMTypeForTypeDesc(objectType); + if (objectType.IsValueType && !objectType.IsPointer && objectEntry.Kind != StackValueKind.NativeInt && objectEntry.Kind != StackValueKind.ByRef) + { + if (objectEntry is LoadExpressionEntry) + { + untypedObjectValue = CastToRawPointer(((LoadExpressionEntry)objectEntry).RawLLVMValue); + } + else + { + untypedObjectValue = _builder.BuildAlloca(llvmObjectType, "objptr"); + _builder.BuildStore(objectEntry.ValueAsType(llvmObjectType, _builder), untypedObjectValue); + untypedObjectValue = _builder.BuildPointerCast(untypedObjectValue, LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), "objptrcast"); + } + } + else + { + untypedObjectValue = objectEntry.ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), _builder); + } + ThrowIfNull(untypedObjectValue); + if (field.Offset.AsInt == 0) + { + return untypedObjectValue; + } + else + { + var loadLocation = _builder.BuildGEP(untypedObjectValue, + new LLVMValueRef[] { LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)field.Offset.AsInt, false) }, String.Empty); + return loadLocation; + } + } + + private LLVMValueRef GetFieldAddress(FieldDesc runtimeDeterminedField, FieldDesc field, bool isStatic) + { + if (field.IsStatic) + { + //pop unused value + if (!isStatic) + _stack.Pop(); + + ISymbolNode node = null; + MetadataType owningType = (MetadataType)_compilation.ConvertToCanonFormIfNecessary(field.OwningType, CanonicalFormKind.Specific); + LLVMValueRef staticBase; + int fieldOffset; + // If the type is non-BeforeFieldInit, this is handled before calling any methods on it + //TODO : this seems to call into the cctor if the cctor itself accesses static fields. e.g. SR. Try a test with an ++ in the cctor + bool needsCctorCheck = (owningType.IsBeforeFieldInit || (!owningType.IsBeforeFieldInit && owningType != _thisType)) && _compilation.HasLazyStaticConstructor(owningType); + + if (field.HasRva) + { + node = _compilation.GetFieldRvaData(field); + staticBase = LoadAddressOfSymbolNode(node); + fieldOffset = 0; + // Run static constructor if necessary + if (needsCctorCheck) + { + TriggerCctor(owningType); + } + } + else + { + fieldOffset = field.Offset.AsInt; + TypeDesc runtimeDeterminedOwningType = runtimeDeterminedField.OwningType; + if (field.IsThreadStatic) + { + if (runtimeDeterminedOwningType.IsRuntimeDeterminedSubtype) + { + staticBase = CallGenericHelper(ReadyToRunHelperId.GetThreadStaticBase, runtimeDeterminedOwningType); + } + else + { + ExpressionEntry returnExp; + node = TriggerCctorWithThreadStaticStorage((MetadataType)runtimeDeterminedOwningType, needsCctorCheck, out returnExp); + staticBase = returnExp.ValueAsType(returnExp.Type, _builder); + } + } + else + { + if (field.HasGCStaticBase) + { + if (runtimeDeterminedOwningType.IsRuntimeDeterminedSubtype) + { + needsCctorCheck = false; // no cctor for canonical types + staticBase = CallGenericHelper(ReadyToRunHelperId.GetGCStaticBase, runtimeDeterminedOwningType); + } + else + { + node = _compilation.NodeFactory.TypeGCStaticsSymbol(owningType); + LLVMValueRef basePtrPtr = LoadAddressOfSymbolNode(node); + staticBase = _builder.BuildLoad(_builder.BuildLoad(_builder.BuildPointerCast(basePtrPtr, LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), 0), 0), "castBasePtrPtr"), "basePtr"), "base"); + } + } + else + { + if (runtimeDeterminedOwningType.IsRuntimeDeterminedSubtype) + { + needsCctorCheck = false; // no cctor for canonical types + staticBase = CallGenericHelper(ReadyToRunHelperId.GetNonGCStaticBase, runtimeDeterminedOwningType); + } + else + { + node = _compilation.NodeFactory.TypeNonGCStaticsSymbol(owningType); + staticBase = LoadAddressOfSymbolNode(node); + } + } + // Run static constructor if necessary + if (needsCctorCheck) + { + TriggerCctor(owningType); + } + } + } + + if (node != null) _dependencies.Add(node); + + LLVMValueRef castStaticBase = _builder.BuildPointerCast(staticBase, LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), owningType.Name + "_statics"); + LLVMValueRef fieldAddr = _builder.BuildGEP(castStaticBase, new LLVMValueRef[] { BuildConstInt32(fieldOffset) }, field.Name + "_addr"); + + + return fieldAddr; + } + else + { + return GetInstanceFieldAddress(_stack.Pop(), field); + } + } + + ISymbolNode GetGenericLookupHelperAndAddReference(ReadyToRunHelperId helperId, object helperArg, out LLVMValueRef helper, IEnumerable additionalArgs = null) + { + ISymbolNode node; + GenericDictionaryLookup lookup = _compilation.ComputeGenericLookup(_method, helperId, helperArg); + + var retType = helperId == ReadyToRunHelperId.DelegateCtor + ? LLVMTypeRef.Void + : LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0); + var helperArgs = new List + { + LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), + LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), + }; + if (additionalArgs != null) helperArgs.AddRange(additionalArgs); + if (_method.RequiresInstMethodDescArg()) + { + node = _compilation.NodeFactory.ReadyToRunHelperFromDictionaryLookup(lookup.HelperId, lookup.HelperObject, _method); + helper = GetOrCreateLLVMFunction(node.GetMangledName(_compilation.NameMangler), + LLVMTypeRef.CreateFunction(retType, helperArgs.ToArray(), false)); + } + else + { + Debug.Assert(_method.RequiresInstMethodTableArg() || _method.AcquiresInstMethodTableFromThis()); + node = _compilation.NodeFactory.ReadyToRunHelperFromTypeLookup(lookup.HelperId, lookup.HelperObject, _method.OwningType); + helper = GetOrCreateLLVMFunction(node.GetMangledName(_compilation.NameMangler), + LLVMTypeRef.CreateFunction(retType, helperArgs.ToArray(), false)); + } + // cpp backend relies on a lazy static constructor to get this node added during the dependency generation. + // If left to when the code is written that uses the helper then its too late. + IMethodNode helperNode = (IMethodNode)_compilation.NodeFactory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase); + + _dependencies.Add(node); + _dependencies.Add(helperNode); + return node; + } + + /// + /// Triggers a static constructor check and call for types that have them + /// + private void TriggerCctor(MetadataType type, LLVMBuilderRef builder = default) + { + if (builder.Handle == IntPtr.Zero) builder = _builder; + if (type.IsCanonicalSubtype(CanonicalFormKind.Specific)) return; // TODO - what to do here? + ISymbolNode classConstructionContextSymbol = _compilation.NodeFactory.TypeNonGCStaticsSymbol(type); + _dependencies.Add(classConstructionContextSymbol); + LLVMValueRef firstNonGcStatic = LoadAddressOfSymbolNode(classConstructionContextSymbol, builder); + + // TODO: Codegen could check whether it has already run rather than calling into EnsureClassConstructorRun + // but we'd have to figure out how to manage the additional basic blocks + LLVMValueRef classConstructionContextPtr = builder.BuildGEP(firstNonGcStatic, new LLVMValueRef[] { BuildConstInt32(-2) }, "classConstructionContext"); + StackEntry classConstructionContext = new AddressExpressionEntry(StackValueKind.NativeInt, "classConstructionContext", classConstructionContextPtr, GetWellKnownType(WellKnownType.IntPtr)); + CallRuntime("System.Runtime.CompilerServices", _compilation.TypeSystemContext, ClassConstructorRunner, "EnsureClassConstructorRun", new StackEntry[] { classConstructionContext }, builder: builder); + } + + /// + /// Triggers creation of thread static storage and the static constructor if present + /// + private ISymbolNode TriggerCctorWithThreadStaticStorage(MetadataType type, bool needsCctorCheck, out ExpressionEntry returnExp) + { + ISymbolNode threadStaticIndexSymbol = _compilation.NodeFactory.TypeThreadStaticIndex(type); + LLVMValueRef threadStaticIndex = LoadAddressOfSymbolNode(threadStaticIndexSymbol); + + StackEntry typeManagerSlotEntry = new LoadExpressionEntry(StackValueKind.ValueType, "typeManagerSlot", threadStaticIndex, GetWellKnownType(WellKnownType.Int32)); + LLVMValueRef typeTlsIndexPtr = + _builder.BuildGEP(threadStaticIndex, new LLVMValueRef[] { BuildConstInt32(1) }, "typeTlsIndexPtr"); // index is the second field after the ptr. + StackEntry tlsIndexExpressionEntry = new LoadExpressionEntry(StackValueKind.ValueType, "typeTlsIndex", typeTlsIndexPtr, GetWellKnownType(WellKnownType.Int32)); + + if (needsCctorCheck) + { + ISymbolNode classConstructionContextSymbol = _compilation.NodeFactory.TypeNonGCStaticsSymbol(type); + _dependencies.Add(classConstructionContextSymbol); + LLVMValueRef firstNonGcStatic = LoadAddressOfSymbolNode(classConstructionContextSymbol); + + // TODO: Codegen could check whether it has already run rather than calling into EnsureClassConstructorRun + // but we'd have to figure out how to manage the additional basic blocks + LLVMValueRef classConstructionContextPtr = _builder.BuildGEP(firstNonGcStatic, new LLVMValueRef[] { BuildConstInt32(-2) }, "classConstructionContext"); + StackEntry classConstructionContext = new AddressExpressionEntry(StackValueKind.NativeInt, "classConstructionContext", classConstructionContextPtr, + GetWellKnownType(WellKnownType.IntPtr)); + + returnExp = CallRuntime("System.Runtime.CompilerServices", _compilation.TypeSystemContext, ClassConstructorRunner, "CheckStaticClassConstructionReturnThreadStaticBase", new StackEntry[] + { + typeManagerSlotEntry, + tlsIndexExpressionEntry, + classConstructionContext + }); + return threadStaticIndexSymbol; + } + else + { + returnExp = CallRuntime("Internal.Runtime", _compilation.TypeSystemContext, ThreadStatics, "GetThreadStaticBaseForType", new StackEntry[] + { + typeManagerSlotEntry, + tlsIndexExpressionEntry + }); + return threadStaticIndexSymbol; + } + } + + private void TriggerCctor(MetadataType type, LLVMValueRef staticBaseValueRef, string runnerMethodName) + { + var classConstCtx = _builder.BuildGEP( + _builder.BuildBitCast(staticBaseValueRef, LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), + "ptr8"), new LLVMValueRef[] { BuildConstInt32(-8) }, "backToClassCtx"); + StackEntry classConstructionContext = new AddressExpressionEntry(StackValueKind.NativeInt, "classConstructionContext", classConstCtx, + GetWellKnownType(WellKnownType.IntPtr)); + StackEntry staticBaseEntry = new AddressExpressionEntry(StackValueKind.NativeInt, "staticBase", staticBaseValueRef, + GetWellKnownType(WellKnownType.IntPtr)); + + CallRuntime("System.Runtime.CompilerServices", _compilation.TypeSystemContext, ClassConstructorRunner, runnerMethodName, new StackEntry[] + { + classConstructionContext, + staticBaseEntry + }); + } + + private void ImportLoadField(int token, bool isStatic) + { + FieldDesc field = (FieldDesc)_methodIL.GetObject(token); + FieldDesc canonFieldDesc = (FieldDesc)_canonMethodIL.GetObject(token); + LLVMValueRef fieldAddress = GetFieldAddress(field, canonFieldDesc, isStatic); + + PushLoadExpression(GetStackValueKind(canonFieldDesc.FieldType), $"Field_{field.Name}", fieldAddress, canonFieldDesc.FieldType); + } + + private void ImportAddressOfField(int token, bool isStatic) + { + FieldDesc field = (FieldDesc)_methodIL.GetObject(token); + LLVMValueRef fieldAddress = GetFieldAddress(field, (FieldDesc)_canonMethodIL.GetObject(token), isStatic); + _stack.Push(new AddressExpressionEntry(StackValueKind.ByRef, $"FieldAddress_{field.Name}", fieldAddress, field.FieldType.MakeByRefType())); + } + + private void ImportStoreField(int token, bool isStatic) + { + FieldDesc runtimeDeterminedField = (FieldDesc)_methodIL.GetObject(token); + FieldDesc field = (FieldDesc)_canonMethodIL.GetObject(token); + StackEntry valueEntry = _stack.Pop(); + + LLVMValueRef fieldAddress = GetFieldAddress(runtimeDeterminedField, field, isStatic); + CastingStore(fieldAddress, valueEntry, field.FieldType, true); + } + + // Loads symbol address. Address is represented as a i32* + private LLVMValueRef LoadAddressOfSymbolNode(ISymbolNode node, LLVMBuilderRef builder = default(LLVMBuilderRef)) + { + if (builder.Handle == IntPtr.Zero) + builder = _builder; + + LLVMValueRef addressOfAddress = LLVMObjectWriter.GetSymbolValuePointer(Module, node, _compilation.NameMangler, false); + //return addressOfAddress; + return builder.BuildLoad(addressOfAddress, "LoadAddressOfSymbolNode"); + } + + private void ImportLoadString(int token) + { + TypeDesc stringType = this._compilation.TypeSystemContext.GetWellKnownType(WellKnownType.String); + + string str = (string)_methodIL.GetObject(token); + ISymbolNode node = _compilation.NodeFactory.SerializedStringObject(str); + LLVMValueRef stringDataPointer = LoadAddressOfSymbolNode(node); + _dependencies.Add(node); + _stack.Push(new ExpressionEntry(GetStackValueKind(stringType), String.Empty, stringDataPointer, stringType)); + } + + private void ImportInitObj(int token) + { + TypeDesc type = ResolveTypeToken(token); + var valueEntry = _stack.Pop(); + var llvmType = GetLLVMTypeForTypeDesc(type); + if (llvmType.Kind == LLVMTypeKind.LLVMStructTypeKind) + { + ImportCallMemset(valueEntry.ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), _builder), 0, type.GetElementSize().AsInt, _builder); + } + else if (llvmType.Kind == LLVMTypeKind.LLVMIntegerTypeKind) + _builder.BuildStore(LLVMValueRef.CreateConstInt(llvmType, 0, false), valueEntry.ValueAsType(LLVMTypeRef.CreatePointer(llvmType, 0), _builder)); + else if (llvmType.Kind == LLVMTypeKind.LLVMPointerTypeKind) + _builder.BuildStore(LLVMValueRef.CreateConstNull(llvmType), valueEntry.ValueAsType(LLVMTypeRef.CreatePointer(llvmType, 0), _builder)); + else if (llvmType.Kind == LLVMTypeKind.LLVMFloatTypeKind || llvmType.Kind == LLVMTypeKind.LLVMDoubleTypeKind) + _builder.BuildStore(LLVMValueRef.CreateConstReal(llvmType, 0.0), valueEntry.ValueAsType(LLVMTypeRef.CreatePointer(llvmType, 0), _builder)); + else + throw new NotImplementedException(); + } + + private void ImportBox(int token) + { + LLVMValueRef eeType; + TypeDesc type = (TypeDesc)_methodIL.GetObject(token); + + StackEntry eeTypeEntry; + bool truncDouble = type.Equals(GetWellKnownType(WellKnownType.Single)); + if (type.IsRuntimeDeterminedSubtype) + { + eeType = CallGenericHelper(ReadyToRunHelperId.TypeHandle, type); + eeTypeEntry = new ExpressionEntry(StackValueKind.ValueType, "eeType", eeType, GetWellKnownType(WellKnownType.IntPtr).MakePointerType()); + type = type.ConvertToCanonForm(CanonicalFormKind.Specific); + } + else + { + eeType = GetEETypePointerForTypeDesc(type, true); + eeTypeEntry = new LoadExpressionEntry(StackValueKind.ValueType, "eeType", eeType, GetWellKnownType(WellKnownType.IntPtr).MakePointerType()); + } + var toBoxValue = _stack.Pop(); + StackEntry valueAddress; + if (truncDouble) + { + var doubleToBox = toBoxValue.ValueAsType(LLVMTypeRef.Double, _builder); + var singleToBox = _builder.BuildFPTrunc(doubleToBox, LLVMTypeRef.Float, "trunc"); + toBoxValue = new ExpressionEntry(StackValueKind.Float, "singleToBox", singleToBox, + GetWellKnownType(WellKnownType.Single)); + } + valueAddress = TakeAddressOf(toBoxValue); + if (type.IsValueType) + { + var arguments = new StackEntry[] { eeTypeEntry, valueAddress }; + PushNonNull(CallRuntime(_compilation.TypeSystemContext, RuntimeExport, "RhBox", arguments)); + } + else + { + var arguments = new StackEntry[] { valueAddress, eeTypeEntry }; + PushNonNull(CallRuntime(_compilation.TypeSystemContext, RuntimeExport, "RhBoxAny", arguments)); + } + } + + private void ImportLeave(BasicBlock target) + { + for (int i = _exceptionRegions.Length - 1; i >= 0; i--) + { + var r = _exceptionRegions[i]; + + if (r.ILRegion.Kind == ILExceptionRegionKind.Finally && + IsOffsetContained(_currentOffset - 1, r.ILRegion.TryOffset, r.ILRegion.TryLength) && + !IsOffsetContained(target.StartOffset, r.ILRegion.TryOffset, r.ILRegion.TryLength)) + { + // Work backwards through containing finally blocks to call them in the right order + BasicBlock finallyBlock = _basicBlocks[r.ILRegion.HandlerOffset]; + MarkBasicBlock(finallyBlock); + var funcletParams = new LLVMValueRef[] {_currentFunclet.GetParam(0)}; + + // todo morganb: this should use invoke if the finally is inside of an outer try block + _builder.BuildCall(GetFuncletForBlock(finallyBlock), funcletParams, String.Empty); + } + } + + MarkBasicBlock(target); + + // If the target is in the current funclet, jump to it. If it's not, we're in a catch + // block and need to return the offset to the calling funclet to jump to the block + LLVMValueRef targetFunclet = GetFuncletForBlock(target); + if (_currentFunclet.Handle.Equals(targetFunclet.Handle)) + { + _builder.BuildBr(GetLLVMBasicBlockForBlock(target)); + } + else + { + _builder.BuildRet(BuildConstInt32(target.StartOffset)); + } + } + + private static bool IsOffsetContained(int offset, int start, int length) + { + return start <= offset && offset < start + length; + } + + private void ImportNewArray(int token) + { + TypeDesc runtimeDeterminedType = (TypeDesc)_methodIL.GetObject(token); + TypeDesc runtimeDeterminedArrayType = runtimeDeterminedType.MakeArrayType(); + var sizeOfArray = _stack.Pop(); + StackEntry[] arguments; + var eeTypeDesc = _compilation.TypeSystemContext.SystemModule.GetKnownType("Internal.Runtime", "EEType").MakePointerType(); + if (runtimeDeterminedArrayType.IsRuntimeDeterminedSubtype) + { + var lookedUpType = CallGenericHelper(ReadyToRunHelperId.TypeHandle, runtimeDeterminedArrayType); + arguments = new StackEntry[] { new ExpressionEntry(StackValueKind.ValueType, "eeType", lookedUpType, eeTypeDesc), sizeOfArray }; + } + else + { + arguments = new StackEntry[] { new LoadExpressionEntry(StackValueKind.ValueType, "eeType", GetEETypePointerForTypeDesc(runtimeDeterminedArrayType, true), eeTypeDesc), sizeOfArray }; + } + var helper = GetNewArrayHelperForType(runtimeDeterminedArrayType); + var res = CallRuntime(_compilation.TypeSystemContext, InternalCalls, helper, arguments, runtimeDeterminedArrayType); + int spillIndex = _spilledExpressions.Count; + SpilledExpressionEntry spillEntry = new SpilledExpressionEntry(StackValueKind.ObjRef, "newarray" + _currentOffset, runtimeDeterminedArrayType, spillIndex, this); + _spilledExpressions.Add(spillEntry); + LLVMValueRef addrOfValueType = LoadVarAddress(spillIndex, LocalVarKind.Temp, out TypeDesc unused); + var typedAddress = CastIfNecessary(_builder, addrOfValueType, LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), 0)); + _builder.BuildStore(res.ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), _builder), typedAddress); + + PushNonNull(spillEntry); + } + + //TODO: copy of the same method in JitHelper.cs but that is internal + public static string GetNewArrayHelperForType(TypeDesc type) + { + if (type.RequiresAlign8()) + return "RhpNewArrayAlign8"; + + return "RhpNewArray"; + } + + LLVMValueRef GetGenericContext() + { + Debug.Assert(_method.IsSharedByGenericInstantiations); + if (_method.AcquiresInstMethodTableFromThis()) + { + LLVMValueRef typedAddress; + LLVMValueRef thisPtr; + + typedAddress = CastIfNecessary(_builder, _currentFunclet.GetParam(0), + LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), 0), 0)); + thisPtr = _builder.BuildLoad( typedAddress, "loadThis"); + + return _builder.BuildLoad( thisPtr, "methodTablePtrRef"); + } + // if the function has exception regions, the generic context is stored in a local, otherwise get it from the parameters + return _exceptionRegions.Length > 0 + ? _builder.BuildLoad(CastIfNecessary(_builder, LoadVarAddress(1, LocalVarKind.Temp, out TypeDesc unused), LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), 0), "ctx")) + : CastIfNecessary(_builder, _currentFunclet.GetParam(GetHiddenContextParamNo() /* hidden param after shadow stack and return slot if present */), LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), "HiddenArg"); + } + + uint GetHiddenContextParamNo() + { + return 1 + (NeedsReturnStackSlot(_method.Signature) ? (uint)1 : 0); + } + + bool FuncletsRequireHiddenContext() + { + return _method.RequiresInstArg(); + } + + LLVMValueRef GetGenericContextParamForFunclet() + { + return FuncletsRequireHiddenContext() + ? GetGenericContext() + : LLVMValueRef.CreateConstPointerNull(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0)); + } + + private LLVMValueRef ArrayBaseSizeRef() + { + return BuildConstInt32(ArrayBaseSize()); + } + + private int ArrayBaseSize() + { + return 2 * _compilation.NodeFactory.Target.PointerSize; + } + + private void ImportLoadElement(int token) + { + ImportLoadElement(ResolveTypeToken(token)); + } + + private void ImportLoadElement(TypeDesc elementType) + { + StackEntry index = _stack.Pop(); + StackEntry arrayReference = _stack.Pop(); + var nullSafeElementType = elementType ?? GetWellKnownType(WellKnownType.Object); + PushLoadExpression(GetStackValueKind(nullSafeElementType), $"{arrayReference.Name()}Element", GetElementAddress(index.ValueAsInt32(_builder, true), arrayReference.ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), _builder), nullSafeElementType), nullSafeElementType); + } + + private void ImportStoreElement(int token) + { + ImportStoreElement(ResolveTypeToken(token)); + } + + private void ImportStoreElement(TypeDesc elementType) + { + StackEntry value = _stack.Pop(); + StackEntry index = _stack.Pop(); + StackEntry arrayReference = _stack.Pop(); + var nullSafeElementType = elementType ?? GetWellKnownType(WellKnownType.Object); + LLVMValueRef elementAddress = GetElementAddress(index.ValueAsInt32(_builder, true), arrayReference.ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), _builder), nullSafeElementType); + CastingStore(elementAddress, value, nullSafeElementType, true); + } + + private void ImportLoadLength() + { + StackEntry arrayReference = _stack.Pop(); + var arrayReferenceValue = arrayReference.ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), _builder); + ThrowIfNull(arrayReferenceValue); + LLVMValueRef lengthPtr = _builder.BuildGEP(arrayReferenceValue, new LLVMValueRef[] { BuildConstInt32(_compilation.NodeFactory.Target.PointerSize) }, "arrayLength"); + LLVMValueRef castLengthPtr = _builder.BuildPointerCast(lengthPtr, LLVMTypeRef.CreatePointer(LLVMTypeRef.Int32, 0), "castArrayLength"); + PushLoadExpression(StackValueKind.Int32, "arrayLength", castLengthPtr, GetWellKnownType(WellKnownType.Int32)); + } + + private void ImportAddressOfElement(int token) + { + TypeDesc elementType = ResolveTypeToken(token); + var byRefElement = elementType.MakeByRefType(); + StackEntry index = _stack.Pop(); + StackEntry arrayReference = _stack.Pop(); + + PushExpression(GetStackValueKind(byRefElement), $"{arrayReference.Name()}ElementAddress", GetElementAddress(index.ValueAsInt32(_builder, true), arrayReference.ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), _builder), elementType), byRefElement); + } + + private LLVMValueRef GetElementAddress(LLVMValueRef elementPosition, LLVMValueRef arrayReference, TypeDesc arrayElementType) + { + ThrowIfNull(arrayReference); + var elementSize = arrayElementType.GetElementSize(); + LLVMValueRef elementOffset = _builder.BuildMul(elementPosition, BuildConstInt32(elementSize.AsInt), "elementOffset"); + LLVMValueRef arrayOffset = _builder.BuildAdd(elementOffset, ArrayBaseSizeRef(), "arrayOffset"); + return _builder.BuildGEP(arrayReference, new LLVMValueRef[] { arrayOffset }, "elementPointer"); + } + + LLVMValueRef EmitRuntimeHelperCall(string name, TypeDesc returnType, LLVMValueRef[] parameters) + { + var runtimeHelperSig = LLVMTypeRef.CreateFunction(GetLLVMTypeForTypeDesc(returnType), parameters.Select(valRef => valRef.TypeOf).ToArray(), false); + var runtimeHelper = GetOrCreateLLVMFunction(name, runtimeHelperSig); + return _builder.BuildCall(runtimeHelper, parameters, "call_" + name); + } + + private void ImportEndFinally() + { + _builder.BuildRetVoid(); + } + + private void ImportFallthrough(BasicBlock next) + { + EvaluationStack entryStack = next.EntryStack; + + if (entryStack != null) + { + if (entryStack.Length != _stack.Length) + throw new InvalidProgramException(); + + for (int i = 0; i < entryStack.Length; i++) + { + // TODO: Do we need to allow conversions? + if (entryStack[i].Kind != _stack[i].Kind) + throw new InvalidProgramException(); + + if (entryStack[i].Kind == StackValueKind.ValueType) + { + if (entryStack[i].Type != _stack[i].Type) + throw new InvalidProgramException(); + } + } + } + else + { + if (_stack.Length > 0) + { + entryStack = new EvaluationStack(_stack.Length); + for (int i = 0; i < _stack.Length; i++) + { + entryStack.Push(NewSpillSlot(_stack[i])); + } + } + next.EntryStack = entryStack; + } + + if (entryStack != null) + { + for (int i = 0; i < entryStack.Length; i++) + { + var currentEntry = _stack[i]; + var entry = entryStack[i] as SpilledExpressionEntry; + if (entry == null) + throw new InvalidProgramException(); + + StoreTemp(entry.LocalIndex, currentEntry.ValueAsType(entry.Type, _builder)); + } + } + + MarkBasicBlock(next); + + } + + private const string RuntimeExport = "RuntimeExports"; + private const string InternalCalls = "InternalCalls"; + private const string TypeCast = "TypeCast"; + private const string DispatchResolve = "DispatchResolve"; + private const string ThreadStatics = "ThreadStatics"; + private const string ClassConstructorRunner = "ClassConstructorRunner"; + + private ExpressionEntry CallRuntime(TypeSystemContext context, string className, string methodName, StackEntry[] arguments, TypeDesc forcedReturnType = null, bool fromLandingPad = false, LLVMBuilderRef builder = default(LLVMBuilderRef)) + { + return CallRuntime("System.Runtime", context, className, methodName, arguments, forcedReturnType, fromLandingPad, builder); + } + + private ExpressionEntry CallRuntime(string @namespace, TypeSystemContext context, string className, string methodName, StackEntry[] arguments, TypeDesc forcedReturnType = null, bool fromLandingPad = false, LLVMBuilderRef builder = default(LLVMBuilderRef)) + { + if (builder.Handle == IntPtr.Zero) + builder = _builder; + + MetadataType helperType = context.SystemModule.GetKnownType(@namespace, className); + MethodDesc helperMethod = helperType.GetKnownMethod(methodName, null); + if ((helperMethod.IsInternalCall && helperMethod.HasCustomAttribute("System.Runtime", "RuntimeImportAttribute"))) + return ImportRawPInvoke(helperMethod, arguments, forcedReturnType: forcedReturnType, builder: builder); + else + return HandleCall(helperMethod, helperMethod.Signature, helperMethod, arguments, helperMethod, fromLandingPad: fromLandingPad, builder: builder).Item1; + } + + private void PushNonNull(StackEntry entry) + { + if (entry != null) + { + _stack.Push(entry); + } + } + + private StackEntry NewSpillSlot(StackEntry entry) + { + var entryType = entry.Type ?? GetWellKnownType(WellKnownType.Object); //type is required here, currently the only time entry.Type is null is if someone has pushed a null literal + var entryIndex = _spilledExpressions.Count; + var newEntry = new SpilledExpressionEntry(entry.Kind, entry is ExpressionEntry ? ((ExpressionEntry)entry).Name : "spilled" + entryIndex, entryType, entryIndex, this); + _spilledExpressions.Add(newEntry); + return newEntry; + } + + private StackEntry TakeAddressOf(StackEntry entry) + { + var entryType = entry.Type ?? GetWellKnownType(WellKnownType.Object); //type is required here, currently the only time entry.Type is null is if someone has pushed a null literal + + LLVMValueRef addressValue; + if (entry is LoadExpressionEntry) + { + addressValue = ((LoadExpressionEntry)entry).RawLLVMValue; + } + else if (entry is SpilledExpressionEntry) + { + int spillIndex = ((SpilledExpressionEntry)entry).LocalIndex; + addressValue = LoadVarAddress(spillIndex, LocalVarKind.Temp, out TypeDesc unused); + } + else + { + //This path should only ever be taken for constants and the results of a primitive cast (not writable) + //all other cases should be operating on a LoadExpressionEntry + var entryIndex = _spilledExpressions.Count; + var newEntry = new SpilledExpressionEntry(entry.Kind, entry is ExpressionEntry ? ((ExpressionEntry)entry).Name : "address_of_temp" + entryIndex, entryType, entryIndex, this); + _spilledExpressions.Add(newEntry); + + if (entry is ExpressionEntry) + addressValue = StoreTemp(entryIndex, ((ExpressionEntry)entry).RawLLVMValue); + else + addressValue = StoreTemp(entryIndex, entry.ValueForStackKind(entry.Kind, _builder, false)); + } + + return new AddressExpressionEntry(StackValueKind.NativeInt, "address_of", addressValue, entry.Type.MakePointerType()); + } + + private TypeDesc ResolveTypeToken(int token) + { + return (TypeDesc)_canonMethodIL.GetObject(token); + } + + private TypeDesc GetWellKnownType(WellKnownType wellKnownType) + { + return _compilation.TypeSystemContext.GetWellKnownType(wellKnownType); + } + + private void ReportInvalidBranchTarget(int targetOffset) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private void ReportFallthroughAtEndOfMethod() + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private void ReportMethodEndInsideInstruction() + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private void ReportInvalidInstruction(ILOpcode opcode) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + private void EmitTrapCall(LLVMBuilderRef builder = default(LLVMBuilderRef)) + { + if (builder.Handle == IntPtr.Zero) + builder = _builder; + + if (TrapFunction.Handle == IntPtr.Zero) + { + TrapFunction = Module.AddFunction("llvm.trap", LLVMTypeRef.CreateFunction(LLVMTypeRef.Void, Array.Empty(), false)); + } + builder.BuildCall(TrapFunction, Array.Empty(), string.Empty); + builder.BuildUnreachable(); + } + + private void EmitDoNothingCall() + { + if (DoNothingFunction.Handle == IntPtr.Zero) + { + DoNothingFunction = Module.AddFunction("llvm.donothing", LLVMTypeRef.CreateFunction(LLVMTypeRef.Void, Array.Empty(), false)); + } + _builder.BuildCall(DoNothingFunction, Array.Empty(), string.Empty); + } + + public override string ToString() + { + return _method.ToString(); + } + + //TOOD refactor with cctor + public ExpressionEntry OutputCodeForGetThreadStaticBaseForType(LLVMValueRef threadStaticIndex) + { + var threadStaticIndexPtr = _builder.BuildPointerCast(threadStaticIndex, + LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int32, 0), 0), "tsiPtr"); + LLVMValueRef typeTlsIndexPtr = + _builder.BuildGEP(threadStaticIndexPtr, new LLVMValueRef[] { BuildConstInt32(1) }, "typeTlsIndexPtr"); // index is the second field after the ptr. + + StackEntry typeManagerSlotEntry = new LoadExpressionEntry(StackValueKind.ValueType, "typeManagerSlot", threadStaticIndexPtr, GetWellKnownType(WellKnownType.Int32)); + StackEntry tlsIndexExpressionEntry = new LoadExpressionEntry(StackValueKind.ValueType, "typeTlsIndex", typeTlsIndexPtr, GetWellKnownType(WellKnownType.Int32)); + + var expressionEntry = CallRuntime("Internal.Runtime", _compilation.TypeSystemContext, ThreadStatics, + "GetThreadStaticBaseForType", new StackEntry[] + { + typeManagerSlotEntry, + tlsIndexExpressionEntry + }); + return expressionEntry; + } + + private LLVMValueRef RemoveFatOffset(LLVMBuilderRef builder, LLVMValueRef fatFunctionRef) + { + return builder.BuildAnd(CastIfNecessary(builder, fatFunctionRef, LLVMTypeRef.Int32), + BuildConstUInt32(~(uint)_compilation.TypeSystemContext.Target.FatFunctionPointerOffset), "minusFatOffset"); + } + + private void CreateEHData(LLVMMethodCodeNode methodCodeNodeNeedingCode) + { + ObjectNode.ObjectData ehInfo = _exceptionRegions.Length > 0 ? EncodeEHInfo() : null; + //WASMTODO: is this just for debugging, what happens if we dont have it + // DebugEHClauseInfo[] debugEHClauseInfos = null; + // if (_ehClauses != null) + // { + // debugEHClauseInfos = new DebugEHClauseInfo[_ehClauses.Length]; + // for (int i = 0; i < _ehClauses.Length; i++) + // { + // var clause = _ehClauses[i]; + // debugEHClauseInfos[i] = new DebugEHClauseInfo(clause.TryOffset, clause.TryLength, + // clause.HandlerOffset, clause.HandlerLength); + // } + // } + + // WASMTODO: guessing we dont need this + // _methodCodeNode.SetCode(objectData); + + // WASMTODO: guessing we dont need these yet + // _methodCodeNode.InitializeFrameInfos(_frameInfos); + // _methodCodeNode.InitializeDebugEHClauseInfos(debugEHClauseInfos); + // _methodCodeNode.InitializeGCInfo(_gcInfo); + // + // _methodCodeNode.InitializeDebugLocInfos(_debugLocInfos); + // _methodCodeNode.InitializeDebugVarInfos(_debugVarInfos); + + if (ehInfo != null) + { + _ehInfoNode.AddEHInfo(ehInfo); + _dependencies.Add(_ehInfoNode); + } + } + + private ObjectNode.ObjectData EncodeEHInfo() + { + var builder = new ObjectDataBuilder(); + builder.RequireInitialAlignment(1); + int totalClauses = _exceptionRegions.Length; + + // Count the number of special markers that will be needed +// for (int i = 1; i < _exceptionRegions.Length; i++) +// { +// ExceptionRegion clause = _exceptionRegions[i]; +// ExceptionRegion previousClause = _exceptionRegions[i - 1]; + + // WASMTODO : do we need these special markers and if so how do we detect and set CORINFO_EH_CLAUSE_SAMETRY? +// if ((previousClause.ILRegion.TryOffset == clause.ILRegion.TryOffset) && +// (previousClause.ILRegion.TryLength == clause.ILRegion.TryLength) && +// ((clause.Flags & CORINFO_EH_CLAUSE_FLAGS.CORINFO_EH_CLAUSE_SAMETRY) == 0)) +// { +// totalClauses++; +// } +// } + + builder.EmitCompressedUInt((uint)totalClauses); + // Iterate backwards to emit the innermost first, but within a try region go forwards to get the first matching catch type + int i = _exceptionRegions.Length - 1; + while (i >= 0) + { + int tryStart = _exceptionRegions[i].ILRegion.TryOffset; + int tryLength = _exceptionRegions[i].ILRegion.TryLength; + for (var j = 0; j < _exceptionRegions.Length; j++) + { + ExceptionRegion exceptionRegion = _exceptionRegions[j]; + if (exceptionRegion.ILRegion.TryOffset != tryStart || exceptionRegion.ILRegion.TryLength != tryLength) continue; + // if (i > 0) + // { + // ExceptionRegion previousClause = _exceptionRegions[i - 1]; + + // If the previous clause has same try offset and length as the current clause, + // but belongs to a different try block (CORINFO_EH_CLAUSE_SAMETRY is not set), + // emit a special marker to allow runtime distinguish this case. + //WASMTODO: see above - do we need these + // if ((previousClause.TryOffset == clause.TryOffset) && + // (previousClause.TryLength == clause.TryLength) && + // ((clause.Flags & CORINFO_EH_CLAUSE_FLAGS.CORINFO_EH_CLAUSE_SAMETRY) == 0)) + // { + // builder.EmitCompressedUInt(0); + // builder.EmitCompressedUInt((uint)RhEHClauseKind.RH_EH_CLAUSE_FAULT); + // builder.EmitCompressedUInt(0); + // } + // } + + RhEHClauseKind clauseKind; + + if (exceptionRegion.ILRegion.Kind == ILExceptionRegionKind.Fault || + exceptionRegion.ILRegion.Kind == ILExceptionRegionKind.Finally) + { + clauseKind = RhEHClauseKind.RH_EH_CLAUSE_FAULT; + } + else if (exceptionRegion.ILRegion.Kind == ILExceptionRegionKind.Filter) + { + clauseKind = RhEHClauseKind.RH_EH_CLAUSE_FILTER; + } + else + { + clauseKind = RhEHClauseKind.RH_EH_CLAUSE_TYPED; + } + + builder.EmitCompressedUInt((uint)exceptionRegion.ILRegion.TryOffset); + + builder.EmitCompressedUInt(((uint)tryLength << 2) | (uint)clauseKind); + + RelocType rel = (_compilation.NodeFactory.Target.IsWindows) + ? RelocType.IMAGE_REL_BASED_ABSOLUTE + : RelocType.IMAGE_REL_BASED_REL32; + + if (_compilation.NodeFactory.Target.Abi == TargetAbi.Jit) + rel = RelocType.IMAGE_REL_BASED_REL32; + + switch (clauseKind) + { + case RhEHClauseKind.RH_EH_CLAUSE_TYPED: + var type = (TypeDesc)_methodIL.GetObject((int)exceptionRegion.ILRegion.ClassToken); + Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Any)); + AlignForSymbol(ref builder); + var typeSymbol = _compilation.NodeFactory.NecessaryTypeSymbol(type); + builder.EmitReloc(typeSymbol, rel); + string catchFuncletName = GetFuncletName(exceptionRegion, + exceptionRegion.ILRegion.HandlerOffset, exceptionRegion.ILRegion.Kind); + builder.EmitReloc(new LLVMBlockRefNode(catchFuncletName), rel); + break; + case RhEHClauseKind.RH_EH_CLAUSE_FAULT: + AlignForSymbol(ref builder); + string finallyFuncletName = GetFuncletName(exceptionRegion, + exceptionRegion.ILRegion.HandlerOffset, exceptionRegion.ILRegion.Kind); + builder.EmitReloc(new LLVMBlockRefNode(finallyFuncletName), rel); + break; + case RhEHClauseKind.RH_EH_CLAUSE_FILTER: + AlignForSymbol(ref builder); + string clauseFuncletName = GetFuncletName(exceptionRegion, + exceptionRegion.ILRegion.HandlerOffset, ILExceptionRegionKind.Catch); + builder.EmitReloc(new LLVMBlockRefNode(clauseFuncletName), rel); + string filterFuncletName = GetFuncletName(exceptionRegion, + exceptionRegion.ILRegion.FilterOffset, exceptionRegion.ILRegion.Kind); + builder.EmitReloc(new LLVMBlockRefNode(filterFuncletName), rel); + break; + } + i--; + } + } + + return builder.ToObjectData(); + } + + private string GetFuncletName(ExceptionRegion exceptionRegion, int regionOffset, ILExceptionRegionKind ilExceptionRegionKind) + { + return _mangledName + "$" + ilExceptionRegionKind.ToString() + regionOffset.ToString("X"); + } + + void AlignForSymbol(ref ObjectDataBuilder builder) + { + if ((builder.CountBytes & 3) == 0) return; + var padding = (4 - (builder.CountBytes & 3)); + + for (var pad = 0; pad < padding; pad++) + { + builder.EmitByte(0); + } + } + + enum RhEHClauseKind + { + RH_EH_CLAUSE_TYPED = 0, // catch + RH_EH_CLAUSE_FAULT = 1, // fault and finally + RH_EH_CLAUSE_FILTER = 2 + } + + partial void OnLeaveTargetCreated(int target) + { + if (_leaveTargets == null) + { + _leaveTargets = new List(); + } + if(!_leaveTargets.Contains(target)) _leaveTargets.Add(target); + } + + class AddressCacheContext + { + internal LLVMBuilderRef PrologBuilder; + internal LLVMValueRef[] ArgAddresses; + internal LLVMValueRef[] LocalAddresses; + internal List TempAddresses; + } + } +} diff --git a/src/coreclr/src/tools/aot/ILCompiler.LLVM/CodeGen/ILToLLVMImporter_Statics.cs b/src/coreclr/src/tools/aot/ILCompiler.LLVM/CodeGen/ILToLLVMImporter_Statics.cs new file mode 100644 index 000000000000..c869f363a9d7 --- /dev/null +++ b/src/coreclr/src/tools/aot/ILCompiler.LLVM/CodeGen/ILToLLVMImporter_Statics.cs @@ -0,0 +1,206 @@ +// 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 Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using ILCompiler; + +using ILCompiler.DependencyAnalysis; +using LLVMSharp.Interop; + +namespace Internal.IL +{ + internal partial class ILImporter + { + public static void CompileMethod(LLVMCodegenCompilation compilation, LLVMMethodCodeNode methodCodeNodeNeedingCode) + { + MethodDesc method = methodCodeNodeNeedingCode.Method; + + if (compilation.Logger.IsVerbose) + { + string methodName = method.ToString(); + compilation.Logger.Writer.WriteLine("Compiling " + methodName); + } + + if (method.HasCustomAttribute("System.Runtime", "RuntimeImportAttribute")) + { + methodCodeNodeNeedingCode.CompilationCompleted = true; + //throw new NotImplementedException(); + //CompileExternMethod(methodCodeNodeNeedingCode, ((EcmaMethod)method).GetRuntimeImportName()); + //return; + } + + if (method.IsRawPInvoke()) + { + //CompileExternMethod(methodCodeNodeNeedingCode, method.GetPInvokeMethodMetadata().Name ?? method.Name); + //return; + } + var methodIL = compilation.GetMethodIL(method); + if (methodIL == null) + return; + + ILImporter ilImporter = null; + try + { + string mangledName; + + // TODO: Better detection of the StartupCodeMain method + if (methodCodeNodeNeedingCode.Method.Signature.IsStatic && methodCodeNodeNeedingCode.Method.Name == "StartupCodeMain") + { + mangledName = "StartupCodeMain"; + } + else + { + mangledName = compilation.NameMangler.GetMangledMethodName(methodCodeNodeNeedingCode.Method).ToString(); + } + + ilImporter = new ILImporter(compilation, method, methodIL, mangledName, methodCodeNodeNeedingCode is LlvmUnboxingThunkNode); + + CompilerTypeSystemContext typeSystemContext = compilation.TypeSystemContext; + + //MethodDebugInformation debugInfo = compilation.GetDebugInfo(methodIL); + + /* if (!compilation.Options.HasOption(CppCodegenConfigProvider.NoLineNumbersString))*/ + { + //IEnumerable sequencePoints = debugInfo.GetSequencePoints(); + /*if (sequencePoints != null) + ilImporter.SetSequencePoints(sequencePoints);*/ + } + + //IEnumerable localVariables = debugInfo.GetLocalVariables(); + /*if (localVariables != null) + ilImporter.SetLocalVariables(localVariables);*/ + + IEnumerable parameters = GetParameterNamesForMethod(method); + /*if (parameters != null) + ilImporter.SetParameterNames(parameters);*/ + + ilImporter.Import(); + ilImporter.CreateEHData(methodCodeNodeNeedingCode); + methodCodeNodeNeedingCode.CompilationCompleted = true; + } + catch (Exception e) + { + compilation.Logger.Writer.WriteLine(e.Message + " (" + method + ")"); + + methodCodeNodeNeedingCode.CompilationCompleted = true; +// methodCodeNodeNeedingCode.SetDependencies(ilImporter.GetDependencies()); + //throw new NotImplementedException(); + //methodCodeNodeNeedingCode.SetCode(sb.ToString(), Array.Empty()); + } + + // Uncomment the block below to get specific method failures when LLVM fails for cryptic reasons +#if false + LLVMBool result = LLVM.VerifyFunction(ilImporter._llvmFunction, LLVMVerifierFailureAction.LLVMPrintMessageAction); + if (result.Value != 0) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine($"Error compliling {method.OwningType}.{method}"); + Console.ResetColor(); + } +#endif // false + + // Ensure dependencies show up regardless of exceptions to avoid breaking LLVM + methodCodeNodeNeedingCode.SetDependencies(ilImporter.GetDependencies()); + } + + static LLVMValueRef DebugtrapFunction = default(LLVMValueRef); + static LLVMValueRef TrapFunction = default(LLVMValueRef); + static LLVMValueRef DoNothingFunction = default(LLVMValueRef); + static LLVMValueRef CxaBeginCatchFunction = default(LLVMValueRef); + static LLVMValueRef CxaEndCatchFunction = default(LLVMValueRef); + static LLVMValueRef RhpThrowEx = default(LLVMValueRef); + static LLVMValueRef RhpCallCatchFunclet = default(LLVMValueRef); + static LLVMValueRef LlvmCatchFunclet = default(LLVMValueRef); + static LLVMValueRef LlvmFilterFunclet = default(LLVMValueRef); + static LLVMValueRef LlvmFinallyFunclet = default(LLVMValueRef); + static LLVMValueRef NullRefFunction = default(LLVMValueRef); + static LLVMValueRef CkFinite32Function = default(LLVMValueRef); + static LLVMValueRef CkFinite64Function = default(LLVMValueRef); + public static LLVMValueRef GxxPersonality = default(LLVMValueRef); + public static LLVMTypeRef GxxPersonalityType = default(LLVMTypeRef); + + internal static LLVMValueRef MakeFatPointer(LLVMBuilderRef builder, LLVMValueRef targetLlvmFunction, LLVMCodegenCompilation compilation) + { + var asInt = builder.BuildPtrToInt(targetLlvmFunction, LLVMTypeRef.Int32, "toInt"); + return builder.BuildBinOp(LLVMOpcode.LLVMOr, asInt, LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)compilation.TypeSystemContext.Target.FatFunctionPointerOffset, false), "makeFat"); + } + + private static IList GetParameterNamesForMethod(MethodDesc method) + { + // TODO: The uses of this method need revision. The right way to get to this info is from + // a MethodIL. For declarations, we don't need names. + method = method.GetTypicalMethodDefinition(); + var ecmaMethod = method as EcmaMethod; + if (ecmaMethod != null && ecmaMethod.Module.PdbReader != null) + { + List parameterNames = new List(new EcmaMethodDebugInformation(ecmaMethod).GetParameterNames()); + + // Return the parameter names only if they match the method signature + if (parameterNames.Count != 0) + { + var methodSignature = method.Signature; + int argCount = methodSignature.Length; + if (!methodSignature.IsStatic) + argCount++; + + if (parameterNames.Count == argCount) + return parameterNames; + } + } + + return null; + } + + static void BuildCatchFunclet(LLVMModuleRef module, LLVMTypeRef[] funcletArgTypes) + { + LlvmCatchFunclet = module.AddFunction("LlvmCatchFunclet", LLVMTypeRef.CreateFunction(LLVMTypeRef.Int32, funcletArgTypes, false)); + var block = LlvmCatchFunclet.AppendBasicBlock("Catch"); + LLVMBuilderRef funcletBuilder = Context.CreateBuilder(); + funcletBuilder.PositionAtEnd( block); + + LLVMValueRef leaveToILOffset = funcletBuilder.BuildCall(LlvmCatchFunclet.GetParam(0), new LLVMValueRef[] { LlvmCatchFunclet.GetParam(1) }, "callCatch"); + funcletBuilder.BuildRet(leaveToILOffset); + funcletBuilder.Dispose(); + } + + static void BuildFilterFunclet(LLVMModuleRef module, LLVMTypeRef[] funcletArgTypes) + { + LlvmFilterFunclet = module.AddFunction("LlvmFilterFunclet", LLVMTypeRef.CreateFunction(LLVMTypeRef.Int32, funcletArgTypes, false)); + var block = LlvmFilterFunclet.AppendBasicBlock("Filter"); + LLVMBuilderRef funcletBuilder = Context.CreateBuilder(); + funcletBuilder.PositionAtEnd(block); + + LLVMValueRef filterResult = funcletBuilder.BuildCall(LlvmFilterFunclet.GetParam(0), new LLVMValueRef[] { LlvmFilterFunclet.GetParam(1) }, "callFilter"); + funcletBuilder.BuildRet(filterResult); + funcletBuilder.Dispose(); + } + + static void BuildFinallyFunclet(LLVMModuleRef module) + { + LlvmFinallyFunclet = module.AddFunction("LlvmFinallyFunclet", LLVMTypeRef.CreateFunction(LLVMTypeRef.Void, + new LLVMTypeRef[] + { + LLVMTypeRef.CreatePointer(LLVMTypeRef.CreateFunction(LLVMTypeRef.Void, new LLVMTypeRef[] { LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0)}, false), 0), // finallyHandler + LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), // shadow stack + }, false)); + var block = LlvmFinallyFunclet.AppendBasicBlock("Finally"); + LLVMBuilderRef funcletBuilder = Context.CreateBuilder(); + funcletBuilder.PositionAtEnd(block); + + var finallyFunclet = LlvmFinallyFunclet.GetParam(0); + var castShadowStack = LlvmFinallyFunclet.GetParam(1); + + List llvmArgs = new List(); + llvmArgs.Add(castShadowStack); + + funcletBuilder.BuildCall(finallyFunclet, llvmArgs.ToArray(), string.Empty); + funcletBuilder.BuildRetVoid(); + funcletBuilder.Dispose(); + } + } +} diff --git a/src/coreclr/src/tools/aot/ILCompiler.LLVM/CodeGen/LLVMObjectWriter.cs b/src/coreclr/src/tools/aot/ILCompiler.LLVM/CodeGen/LLVMObjectWriter.cs new file mode 100644 index 000000000000..54401a6eaa60 --- /dev/null +++ b/src/coreclr/src/tools/aot/ILCompiler.LLVM/CodeGen/LLVMObjectWriter.cs @@ -0,0 +1,1143 @@ +// 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.IO; +using System.Diagnostics; +using ILCompiler; +using ILCompiler.DependencyAnalysisFramework; +using Internal.Text; +using Internal.TypeSystem; +using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData; + +using LLVMSharp.Interop; +using ILCompiler.DependencyAnalysis; +using Internal.IL; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Object writer using https://github.com/dotnet/llilc + /// + internal class LLVMObjectWriter : IDisposable + { + private string GetBaseSymbolName(ISymbolNode symbol, NameMangler nameMangler, bool objectWriterUse = false) + { + if (symbol is LLVMMethodCodeNode || symbol is LLVMBlockRefNode) + { + return symbol.GetMangledName(nameMangler); + } + + if (symbol is ObjectNode) + { + ISymbolDefinitionNode symbolDefNode = (ISymbolDefinitionNode)symbol; + var symbolName = _nodeFactory.GetSymbolAlternateName(symbolDefNode) ?? symbol.GetMangledName(nameMangler); + if (symbolDefNode.Offset == 0) + { + return symbolName; + } + else + { + return symbolName + "___REALBASE"; + } + } + else if (symbol is ObjectAndOffsetSymbolNode) + { + ObjectAndOffsetSymbolNode objAndOffset = (ObjectAndOffsetSymbolNode)symbol; + if (objAndOffset.Target is IHasStartSymbol) + { + ISymbolNode startSymbol = ((IHasStartSymbol)objAndOffset.Target).StartSymbol; + if (startSymbol == symbol) + { + Debug.Assert(startSymbol.Offset == 0); + return symbol.GetMangledName(nameMangler); + } + return GetBaseSymbolName(startSymbol, nameMangler, objectWriterUse); + } + return GetBaseSymbolName((ISymbolNode)objAndOffset.Target, nameMangler, objectWriterUse); + } + else if (symbol is EmbeddedObjectNode) + { + EmbeddedObjectNode embeddedNode = (EmbeddedObjectNode)symbol; + return GetBaseSymbolName(embeddedNode.ContainingNode.StartSymbol, nameMangler, objectWriterUse); + } + else + { + return null; + } + } + + public static Dictionary s_symbolValues = new Dictionary(); + private static Dictionary s_staticFieldMapping = new Dictionary(); + + public static LLVMValueRef GetSymbolValuePointer(LLVMModuleRef module, ISymbolNode symbol, NameMangler nameMangler, bool objectWriterUse = false) + { + if (symbol is LLVMMethodCodeNode) + { + ThrowHelper.ThrowInvalidProgramException(); + } + + string symbolAddressGlobalName = symbol.GetMangledName(nameMangler) + "___SYMBOL"; + LLVMValueRef symbolAddress; + if (s_symbolValues.TryGetValue(symbolAddressGlobalName, out symbolAddress)) + { + return symbolAddress; + } + var intPtrType = LLVMTypeRef.CreatePointer(LLVMTypeRef.Int32, 0); + var myGlobal = module.AddGlobalInAddressSpace(intPtrType, symbolAddressGlobalName, 0); + myGlobal.IsGlobalConstant = true; + myGlobal.Linkage = LLVMLinkage.LLVMInternalLinkage; + s_symbolValues.Add(symbolAddressGlobalName, myGlobal); + return myGlobal; + } + + private static int GetNumericOffsetFromBaseSymbolValue(ISymbolNode symbol) + { + if (symbol is LLVMMethodCodeNode || symbol is LLVMBlockRefNode) + { + return 0; + } + + if (symbol is ObjectNode) + { + ISymbolDefinitionNode symbolDefNode = (ISymbolDefinitionNode)symbol; + return symbolDefNode.Offset; + } + else if (symbol is ObjectAndOffsetSymbolNode) + { + ObjectAndOffsetSymbolNode objAndOffset = (ObjectAndOffsetSymbolNode)symbol; + ISymbolDefinitionNode symbolDefNode = (ISymbolDefinitionNode)symbol; + if (objAndOffset.Target is IHasStartSymbol) + { + ISymbolNode startSymbol = ((IHasStartSymbol)objAndOffset.Target).StartSymbol; + + if (startSymbol == symbol) + { + Debug.Assert(symbolDefNode.Offset == 0); + return symbolDefNode.Offset; + } + return symbolDefNode.Offset; + } + int baseOffset = GetNumericOffsetFromBaseSymbolValue((ISymbolNode)objAndOffset.Target); + return baseOffset + symbolDefNode.Offset; + } + else if (symbol is EmbeddedObjectNode) + { + EmbeddedObjectNode embeddedNode = (EmbeddedObjectNode)symbol; + int baseOffset = GetNumericOffsetFromBaseSymbolValue(embeddedNode.ContainingNode.StartSymbol); + return baseOffset + ((ISymbolDefinitionNode)embeddedNode).Offset; + } + else + { + ThrowHelper.ThrowInvalidProgramException(); + return 0; + } + } + + // this is the llvm instance. + public LLVMModuleRef Module { get; } + + public LLVMDIBuilderRef DIBuilder { get; } + + // This is used to build mangled names + private Utf8StringBuilder _sb = new Utf8StringBuilder(); + + // Track offsets in node data that prevent writing all bytes in one single blob. This includes + // relocs, symbol definitions, debug data that must be streamed out using the existing LLVM API + private SortedSet _byteInterruptionOffsets = new SortedSet(); + + // Code offset to defined names + private Dictionary> _offsetToDefName = new Dictionary>(); + + // The section for the current node being processed. + private ObjectNodeSection _currentSection; + + // The first defined symbol name of the current node being processed. + private Utf8String _currentNodeZeroTerminatedName; + + // Nodefactory for which ObjectWriter is instantiated for. + private NodeFactory _nodeFactory; + +#if DEBUG + static Dictionary _previouslyWrittenNodeNames = new Dictionary(); +#endif + + public void SetSection(ObjectNodeSection section) + { + _currentSection = section; + } + + public void FinishObjWriter(LLVMContextRef context) + { + // Since emission to llvm is delayed until after all nodes are emitted... emit now. + foreach (var nodeData in _dataToFill) + { + nodeData.Fill(Module, _nodeFactory); + } + + EmitNativeMain(context); + + EmitDebugMetadata(context); + +#if DEBUG + Module.PrintToFile(Path.ChangeExtension(_objectFilePath, ".txt")); +#endif //DEBUG + Module.Verify(LLVMVerifierFailureAction.LLVMAbortProcessAction); + + Module.WriteBitcodeToFile(_objectFilePath); + + //throw new NotImplementedException(); // This function isn't complete + } + + private void EmitDebugMetadata(LLVMContextRef context) + { + var dwarfVersion = LLVMValueRef.CreateMDNode(new[] + { + LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 2, false), + context.GetMDString("Dwarf Version", 13), + LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 4, false) + }); + var dwarfSchemaVersion = LLVMValueRef.CreateMDNode(new[] + { + LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 2, false), + context.GetMDString("Debug Info Version", 18), + LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 3, false) + }); + Module.AddNamedMetadataOperand("llvm.module.flags", dwarfVersion); + Module.AddNamedMetadataOperand("llvm.module.flags", dwarfSchemaVersion); + DIBuilder.DIBuilderFinalize(); + } + + private void EmitReadyToRunHeaderCallback(LLVMContextRef context) + { + LLVMTypeRef intPtr = LLVMTypeRef.CreatePointer(LLVMTypeRef.Int32, 0); + LLVMTypeRef intPtrPtr = LLVMTypeRef.CreatePointer(intPtr, 0); + var callback = Module.AddFunction("RtRHeaderWrapper", LLVMTypeRef.CreateFunction(intPtrPtr, new LLVMTypeRef[0], false)); + var builder = context.CreateBuilder(); + var block = callback.AppendBasicBlock("Block"); + builder.PositionAtEnd(block); + + LLVMValueRef rtrHeaderPtr = GetSymbolValuePointer(Module, _nodeFactory.ReadyToRunHeader, _nodeFactory.NameMangler, false); + LLVMValueRef castRtrHeaderPtr = builder.BuildPointerCast(rtrHeaderPtr, intPtrPtr, "castRtrHeaderPtr"); + builder.BuildRet(castRtrHeaderPtr); + } + + private void EmitNativeMain(LLVMContextRef context) + { + LLVMValueRef shadowStackTop = Module.GetNamedGlobal("t_pShadowStackTop"); + + LLVMBuilderRef builder = context.CreateBuilder(); + var mainSignature = LLVMTypeRef.CreateFunction(LLVMTypeRef.Int32, new LLVMTypeRef[] { LLVMTypeRef.Int32, LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0) }, false); + var mainFunc = Module.AddFunction("__managed__Main", mainSignature); + var mainEntryBlock = mainFunc.AppendBasicBlock("entry"); + builder.PositionAtEnd(mainEntryBlock); + LLVMValueRef managedMain = Module.GetNamedFunction("StartupCodeMain"); + if (managedMain.Handle == IntPtr.Zero) + { + throw new Exception("Main not found"); + } + + LLVMTypeRef reversePInvokeFrameType = LLVMTypeRef.CreateStruct(new LLVMTypeRef[] { LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0) }, false); + LLVMValueRef reversePinvokeFrame = builder.BuildAlloca(reversePInvokeFrameType, "ReversePInvokeFrame"); + LLVMValueRef RhpReversePInvoke2 = Module.GetNamedFunction("RhpReversePInvoke2"); + + if (RhpReversePInvoke2.Handle == IntPtr.Zero) + { + RhpReversePInvoke2 = Module.AddFunction("RhpReversePInvoke2", LLVMTypeRef.CreateFunction(LLVMTypeRef.Void, new LLVMTypeRef[] { LLVMTypeRef.CreatePointer(reversePInvokeFrameType, 0) }, false)); + } + + builder.BuildCall(RhpReversePInvoke2, new LLVMValueRef[] { reversePinvokeFrame }, ""); + + var shadowStack = builder.BuildMalloc(LLVMTypeRef.CreateArray(LLVMTypeRef.Int8, 1000000), String.Empty); + var castShadowStack = builder.BuildPointerCast(shadowStack, LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), String.Empty); + builder.BuildStore(castShadowStack, shadowStackTop); + + var shadowStackBottom = Module.AddGlobal(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), "t_pShadowStackBottom"); + shadowStackBottom.Linkage = LLVMLinkage.LLVMExternalLinkage; + shadowStackBottom.Initializer = LLVMValueRef.CreateConstPointerNull(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0)); + shadowStackBottom.ThreadLocalMode = LLVMThreadLocalMode.LLVMLocalDynamicTLSModel; + builder.BuildStore(castShadowStack, shadowStackBottom); + + // Pass on main arguments + LLVMValueRef argc = mainFunc.GetParam(0); + LLVMValueRef argv = mainFunc.GetParam(1); + + LLVMValueRef mainReturn = builder.BuildCall(managedMain, new LLVMValueRef[] + { + castShadowStack, + argc, + argv, + }, + "returnValue"); + + LLVMValueRef RhpReversePInvokeReturn2 = Module.GetNamedFunction("RhpReversePInvokeReturn2"); + LLVMTypeRef reversePInvokeFunctionType = LLVMTypeRef.CreateFunction(LLVMTypeRef.Void, new LLVMTypeRef[] { LLVMTypeRef.CreatePointer(reversePInvokeFrameType, 0) }, false); + if (RhpReversePInvoke2.Handle == IntPtr.Zero) + { + RhpReversePInvokeReturn2 = Module.AddFunction("RhpReversePInvokeReturn2", reversePInvokeFunctionType); + } + builder.BuildCall(RhpReversePInvokeReturn2, new LLVMValueRef[] { reversePinvokeFrame }, ""); + + builder.BuildRet(mainReturn); + mainFunc.Linkage = LLVMLinkage.LLVMExternalLinkage; + } + + public void SetCodeSectionAttribute(ObjectNodeSection section) + { + //throw new NotImplementedException(); // This function isn't complete + } + + public void EnsureCurrentSection() + { + } + + ArrayBuilder _currentObjectData = new ArrayBuilder(); + struct SymbolRefData + { + public SymbolRefData(bool isFunction, string symbolName, uint offset) + { + IsFunction = isFunction; + SymbolName = symbolName; + Offset = offset; + } + + readonly bool IsFunction; + readonly string SymbolName; + readonly uint Offset; + + public LLVMValueRef ToLLVMValueRef(LLVMModuleRef module) + { + LLVMValueRef valRef = IsFunction ? module.GetNamedFunction(SymbolName) : module.GetNamedGlobal(SymbolName); + + if (Offset != 0 && valRef.Handle != IntPtr.Zero) + { + var CreatePointer = LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0); + var bitCast = LLVMValueRef.CreateConstBitCast(valRef, CreatePointer); + LLVMValueRef[] index = new LLVMValueRef[] {LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, Offset, false)}; + valRef = LLVMValueRef.CreateConstGEP(bitCast, index); + } + + return valRef; + } + } + + Dictionary _currentObjectSymbolRefs = new Dictionary(); + ObjectNode _currentObjectNode; + + List _dataToFill = new List(); + + List> _symbolDefs = new List>(); + + struct ObjectNodeDataEmission + { + public ObjectNodeDataEmission(LLVMValueRef node, byte[] data, Dictionary objectSymbolRefs) + { + Node = node; + Data = data; + ObjectSymbolRefs = objectSymbolRefs; + } + LLVMValueRef Node; + readonly byte[] Data; + readonly Dictionary ObjectSymbolRefs; + + public void Fill(LLVMModuleRef module, NodeFactory nodeFactory) + { + List entries = new List(); + int pointerSize = nodeFactory.Target.PointerSize; + + int countOfPointerSizedElements = Data.Length / pointerSize; + + byte[] currentObjectData = Data; + var intPtrType = LLVMTypeRef.CreatePointer(LLVMTypeRef.Int32, 0); + + for (int i = 0; i < countOfPointerSizedElements; i++) + { + int curOffset = (i * pointerSize); + SymbolRefData symbolRef; + if (ObjectSymbolRefs.TryGetValue(curOffset, out symbolRef)) + { + LLVMValueRef pointedAtValue = symbolRef.ToLLVMValueRef(module); + var ptrValue = LLVMValueRef.CreateConstBitCast(pointedAtValue, intPtrType); + entries.Add(ptrValue); + } + else + { + int value = BitConverter.ToInt32(currentObjectData, curOffset); + entries.Add(LLVMValueRef.CreateConstIntToPtr(LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (uint)value, false), LLVMTypeRef.CreatePointer(LLVMTypeRef.Int32, 0))); + } + } + + var funcptrarray = LLVMValueRef.CreateConstArray(intPtrType, entries.ToArray()); + Node.Initializer = funcptrarray; + } + } + + public void StartObjectNode(ObjectNode node) + { + Debug.Assert(_currentObjectNode == null); + _currentObjectNode = node; + Debug.Assert(_currentObjectData.Count == 0); + } + + public void DoneObjectNode() + { + EmitAlignment(_nodeFactory.Target.PointerSize); + Debug.Assert(_nodeFactory.Target.PointerSize == 4); + int countOfPointerSizedElements = _currentObjectData.Count / _nodeFactory.Target.PointerSize; + + ISymbolNode symNode = _currentObjectNode as ISymbolNode; + if (symNode == null) + symNode = ((IHasStartSymbol)_currentObjectNode).StartSymbol; + string realName = GetBaseSymbolName(symNode, _nodeFactory.NameMangler, true); + + var intPtrType = LLVMTypeRef.CreatePointer(LLVMTypeRef.Int32, 0); + var arrayglobal = Module.AddGlobalInAddressSpace(LLVMTypeRef.CreateArray(intPtrType, (uint)countOfPointerSizedElements), realName, 0); + arrayglobal.Linkage = LLVMLinkage.LLVMExternalLinkage; + + + _dataToFill.Add(new ObjectNodeDataEmission(arrayglobal, _currentObjectData.ToArray(), _currentObjectSymbolRefs)); + + foreach (var symbolIdInfo in _symbolDefs) + { + EmitSymbolDef(arrayglobal, symbolIdInfo.Key, symbolIdInfo.Value); + } + + _currentObjectNode = null; + _currentObjectSymbolRefs = new Dictionary(); + _currentObjectData = new ArrayBuilder(); + _symbolDefs.Clear(); + } + + public void EmitAlignment(int byteAlignment) + { + while ((_currentObjectData.Count % byteAlignment) != 0) + _currentObjectData.Add(0); + } + + public void EmitBlob(byte[] blob) + { + _currentObjectData.Append(blob); + } + + public void EmitIntValue(ulong value, int size) + { + switch (size) + { + case 1: + _currentObjectData.Append(BitConverter.GetBytes((byte)value)); + break; + case 2: + _currentObjectData.Append(BitConverter.GetBytes((ushort)value)); + break; + case 4: + _currentObjectData.Append(BitConverter.GetBytes((uint)value)); + break; + case 8: + _currentObjectData.Append(BitConverter.GetBytes(value)); + break; + default: + ThrowHelper.ThrowInvalidProgramException(); + break; + } + } + + public void EmitBytes(IntPtr pArray, int length) + { + unsafe + { + byte* pBytes = (byte*)pArray; + for (int i = 0; i < length; i++) + _currentObjectData.Add(pBytes[i]); + } + } + + public void EmitSymbolDef(LLVMValueRef realSymbol, string symbolIdentifier, int offsetFromSymbolName) + { + string symbolAddressGlobalName = symbolIdentifier + "___SYMBOL"; + LLVMValueRef symbolAddress; + var intType = LLVMTypeRef.Int32; + if (s_symbolValues.TryGetValue(symbolAddressGlobalName, out symbolAddress)) + { + var int8PtrType = LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0); + var intPtrType = LLVMTypeRef.CreatePointer(LLVMTypeRef.Int32, 0); + var pointerToRealSymbol = LLVMValueRef.CreateConstBitCast(realSymbol, int8PtrType); + var offsetValue = LLVMValueRef.CreateConstInt(intType, (uint)offsetFromSymbolName, false); + var symbolPointerData = LLVMValueRef.CreateConstGEP(pointerToRealSymbol, new LLVMValueRef[] { offsetValue }); + var symbolPointerDataAsInt32Ptr = LLVMValueRef.CreateConstBitCast(symbolPointerData, intPtrType); + symbolAddress.Initializer = symbolPointerDataAsInt32Ptr; + } + } + + public int EmitSymbolRef(string realSymbolName, int offsetFromSymbolName, bool isFunction, RelocType relocType, int delta = 0) + { + int symbolStartOffset = _currentObjectData.Count; + + // Workaround for ObjectWriter's lack of support for IMAGE_REL_BASED_RELPTR32 + // https://github.com/dotnet/corert/issues/3278 + if (relocType == RelocType.IMAGE_REL_BASED_RELPTR32) + { + relocType = RelocType.IMAGE_REL_BASED_REL32; + delta = checked(delta + sizeof(int)); + } + uint totalOffset = checked((uint)delta + unchecked((uint)offsetFromSymbolName)); + + EmitBlob(new byte[this._nodeFactory.Target.PointerSize]); + if (relocType == RelocType.IMAGE_REL_BASED_REL32) + { + return this._nodeFactory.Target.PointerSize; + } + _currentObjectSymbolRefs.Add(symbolStartOffset, new SymbolRefData(isFunction, realSymbolName, totalOffset)); + return _nodeFactory.Target.PointerSize; + } + + public string GetMangledName(TypeDesc type) + { + return _nodeFactory.NameMangler.GetMangledTypeName(type); + } + + public void BuildSymbolDefinitionMap(ObjectNode node, ISymbolDefinitionNode[] definedSymbols) + { + _offsetToDefName.Clear(); + foreach (ISymbolDefinitionNode n in definedSymbols) + { + if (!_offsetToDefName.ContainsKey(n.Offset)) + { + _offsetToDefName[n.Offset] = new List(); + } + + _offsetToDefName[n.Offset].Add(n); + _byteInterruptionOffsets.Add(n.Offset); + } + + var symbolNode = node as ISymbolDefinitionNode; + if (symbolNode != null) + { + _sb.Clear(); + AppendExternCPrefix(_sb); + symbolNode.AppendMangledName(_nodeFactory.NameMangler, _sb); + _currentNodeZeroTerminatedName = _sb.Append('\0').ToUtf8String(); + } + else + { + _currentNodeZeroTerminatedName = default(Utf8String); + } + } + + private void AppendExternCPrefix(Utf8StringBuilder sb) + { + } + + // Returns size of the emitted symbol reference + public int EmitSymbolReference(ISymbolNode target, int delta, RelocType relocType) + { + string realSymbolName = GetBaseSymbolName(target, _nodeFactory.NameMangler, true); + + if (realSymbolName == null) + { + Console.WriteLine("Unable to generate symbolRef to " + target.GetMangledName(_nodeFactory.NameMangler)); + + int pointerSize = _nodeFactory.Target.PointerSize; + EmitBlob(new byte[pointerSize]); + return pointerSize; + } + int offsetFromBase = GetNumericOffsetFromBaseSymbolValue(target) + target.Offset; + return EmitSymbolRef(realSymbolName, offsetFromBase, target is LLVMMethodCodeNode || target is LLVMBlockRefNode, relocType, delta); + } + + public void EmitBlobWithRelocs(byte[] blob, Relocation[] relocs) + { + int nextRelocOffset = -1; + int nextRelocIndex = -1; + if (relocs.Length > 0) + { + nextRelocOffset = relocs[0].Offset; + nextRelocIndex = 0; + } + + int i = 0; + while (i < blob.Length) + { + if (i == nextRelocOffset) + { + Relocation reloc = relocs[nextRelocIndex]; + + // Make sure we've gotten the correct size for the reloc + Debug.Assert(reloc.RelocType == RelocType.IMAGE_REL_BASED_DIR64 || + reloc.RelocType == RelocType.IMAGE_REL_BASED_HIGHLOW); + + long delta; + unsafe + { + fixed (void* location = &blob[i]) + { + delta = Relocation.ReadValue(reloc.RelocType, location); + } + } + int size = EmitSymbolReference(reloc.Target, (int)delta, reloc.RelocType); + + // Update nextRelocIndex/Offset + if (++nextRelocIndex < relocs.Length) + { + nextRelocOffset = relocs[nextRelocIndex].Offset; + } + i += size; + } + else + { + EmitIntValue(blob[i], 1); + i++; + } + } + } + + public void EmitSymbolDefinition(int currentOffset) + { + List nodes; + if (_offsetToDefName.TryGetValue(currentOffset, out nodes)) + { + foreach (var name in nodes) + { + _sb.Clear(); + AppendExternCPrefix(_sb); + name.AppendMangledName(_nodeFactory.NameMangler, _sb); + + string symbolId = name.GetMangledName(_nodeFactory.NameMangler); + int offsetFromBase = GetNumericOffsetFromBaseSymbolValue(name); + Debug.Assert(offsetFromBase == currentOffset); + + _symbolDefs.Add(new KeyValuePair(symbolId, offsetFromBase)); + /* + string alternateName = _nodeFactory.GetSymbolAlternateName(name); + if (alternateName != null) + { + _sb.Clear(); + //AppendExternCPrefix(_sb); + _sb.Append(alternateName); + + EmitSymbolDef(_sb); + }*/ + } + } + } + + //System.IO.FileStream _file; + string _objectFilePath; + public LLVMObjectWriter(string objectFilePath, NodeFactory factory, LLVMCodegenCompilation compilation) + { + _nodeFactory = factory; + _objectFilePath = objectFilePath; + Module = compilation.Module; + DIBuilder = compilation.DIBuilder; + } + + public void Dispose() + { + Dispose(true); + } + + public virtual void Dispose(bool bDisposing) + { + FinishObjWriter(Module.Context); + //if (_file != null) + //{ + // // Finalize object emission. + // FinishObjWriter(); + // _file.Flush(); + // _file.Dispose(); + // _file = null; + //} + + _nodeFactory = null; + + if (bDisposing) + { + GC.SuppressFinalize(this); + } + } + + ~LLVMObjectWriter() + { + Dispose(false); + } + + private bool ShouldShareSymbol(ObjectNode node) + { + if (_nodeFactory.CompilationModuleGroup.IsSingleFileCompilation) + return false; + + if (!(node is ISymbolNode)) + return false; + + // These intentionally clash with one another, but are merged with linker directives so should not be Comdat folded + if (node is ModulesSectionNode) + return false; + + return true; + } + + private ObjectNodeSection GetSharedSection(ObjectNodeSection section, string key) + { + string standardSectionPrefix = ""; + if (section.IsStandardSection) + standardSectionPrefix = "."; + + return new ObjectNodeSection(standardSectionPrefix + section.Name, section.Type, key); + } + + public void ResetByteRunInterruptionOffsets(Relocation[] relocs) + { + _byteInterruptionOffsets.Clear(); + + for (int i = 0; i < relocs.Length; ++i) + { + _byteInterruptionOffsets.Add(relocs[i].Offset); + } + } + + private static int GetVTableSlotsCount(NodeFactory factory, TypeDesc type) + { + if (type == null) + return 0; + int slotsOnCurrentType = factory.VTable(type).Slots.Count; + return slotsOnCurrentType + GetVTableSlotsCount(factory, type.BaseType); + } + + public static void EmitObject(string objectFilePath, IEnumerable nodes, NodeFactory factory, LLVMCodegenCompilation compilation, IObjectDumper dumper) + { + LLVMObjectWriter objectWriter = new LLVMObjectWriter(objectFilePath, factory, compilation); + bool succeeded = false; + + try + { + objectWriter.EmitReadyToRunHeaderCallback(compilation.Module.Context); + //ObjectNodeSection managedCodeSection = null; + + var listOfOffsets = new List(); + foreach (DependencyNode depNode in nodes) + { + ObjectNode node = depNode as ObjectNode; + if (node == null) + continue; + + if (node.ShouldSkipEmittingObjectNode(factory)) + continue; + + if (node is ReadyToRunGenericHelperNode readyToRunGenericHelperNode) + { + objectWriter.GetCodeForReadyToRunGenericHelper(compilation, readyToRunGenericHelperNode, factory); + continue; + } + + objectWriter.StartObjectNode(node); + ObjectData nodeContents = node.GetData(factory); + + if (dumper != null) + dumper.DumpObjectNode(factory.NameMangler, node, nodeContents); + +#if DEBUG + foreach (ISymbolNode definedSymbol in nodeContents.DefinedSymbols) + { + try + { + _previouslyWrittenNodeNames.Add(definedSymbol.GetMangledName(factory.NameMangler), definedSymbol); + } + catch (ArgumentException) + { + ISymbolNode alreadyWrittenSymbol = _previouslyWrittenNodeNames[definedSymbol.GetMangledName(factory.NameMangler)]; + Debug.Fail("Duplicate node name emitted to file", + $"Symbol {definedSymbol.GetMangledName(factory.NameMangler)} has already been written to the output object file {objectFilePath} with symbol {alreadyWrittenSymbol}"); + } + } +#endif + + ObjectNodeSection section = node.Section; + if (objectWriter.ShouldShareSymbol(node)) + { + section = objectWriter.GetSharedSection(section, ((ISymbolNode)node).GetMangledName(factory.NameMangler)); + } + + // Ensure section and alignment for the node. + objectWriter.SetSection(section); + objectWriter.EmitAlignment(nodeContents.Alignment); + + objectWriter.ResetByteRunInterruptionOffsets(nodeContents.Relocs); + + // Build symbol definition map. + objectWriter.BuildSymbolDefinitionMap(node, nodeContents.DefinedSymbols); + + Relocation[] relocs = nodeContents.Relocs; + int nextRelocOffset = -1; + int nextRelocIndex = -1; + if (relocs.Length > 0) + { + nextRelocOffset = relocs[0].Offset; + nextRelocIndex = 0; + } + + int i = 0; + + listOfOffsets.Clear(); + listOfOffsets.AddRange(objectWriter._byteInterruptionOffsets); + + int offsetIndex = 0; + while (i < nodeContents.Data.Length) + { + // Emit symbol definitions if necessary + objectWriter.EmitSymbolDefinition(i); + if (i == nextRelocOffset) + { + Relocation reloc = relocs[nextRelocIndex]; + + long delta; + unsafe + { + fixed (void* location = &nodeContents.Data[i]) + { + delta = Relocation.ReadValue(reloc.RelocType, location); + } + } + ISymbolNode symbolToWrite = reloc.Target; + var eeTypeNode = reloc.Target as EETypeNode; + if (eeTypeNode != null) + { + if (eeTypeNode.ShouldSkipEmittingObjectNode(factory)) + { + symbolToWrite = factory.ConstructedTypeSymbol(eeTypeNode.Type); + } + } + int size = objectWriter.EmitSymbolReference(symbolToWrite, (int)delta, reloc.RelocType); + + /* + WebAssembly has no thumb + // Emit a copy of original Thumb2 instruction that came from RyuJIT + if (reloc.RelocType == RelocType.IMAGE_REL_BASED_THUMB_MOV32 || + reloc.RelocType == RelocType.IMAGE_REL_BASED_THUMB_BRANCH24) + { + unsafe + { + fixed (void* location = &nodeContents.Data[i]) + { + objectWriter.EmitBytes((IntPtr)location, size); + } + } + }*/ + + // Update nextRelocIndex/Offset + if (++nextRelocIndex < relocs.Length) + { + nextRelocOffset = relocs[nextRelocIndex].Offset; + } + else + { + // This is the last reloc. Set the next reloc offset to -1 in case the last reloc has a zero size, + // which means the reloc does not have vacant bytes corresponding to in the data buffer. E.g, + // IMAGE_REL_THUMB_BRANCH24 is a kind of 24-bit reloc whose bits scatte over the instruction that + // references it. We do not vacate extra bytes in the data buffer for this kind of reloc. + nextRelocOffset = -1; + } + i += size; + } + else + { + while (offsetIndex < listOfOffsets.Count && listOfOffsets[offsetIndex] <= i) + { + offsetIndex++; + } + + int nextOffset = offsetIndex == listOfOffsets.Count ? nodeContents.Data.Length : listOfOffsets[offsetIndex]; + + unsafe + { + // Todo: Use Span instead once it's available to us in this repo + fixed (byte* pContents = &nodeContents.Data[i]) + { + objectWriter.EmitBytes((IntPtr)(pContents), nextOffset - i); + i += nextOffset - i; + } + } + + } + } + Debug.Assert(i == nodeContents.Data.Length); + + // It is possible to have a symbol just after all of the data. + objectWriter.EmitSymbolDefinition(nodeContents.Data.Length); + objectWriter.DoneObjectNode(); + } + + succeeded = true; + } + finally + { + objectWriter.Dispose(); + + if (!succeeded) + { + // If there was an exception while generating the OBJ file, make sure we don't leave the unfinished + // object file around. + try + { + File.Delete(objectFilePath); + } + catch + { + } + } + } + } + + private void GetCodeForReadyToRunGenericHelper(LLVMCodegenCompilation compilation, ReadyToRunGenericHelperNode node, NodeFactory factory) + { + LLVMBuilderRef builder = compilation.Module.Context.CreateBuilder(); + var args = new List(); + MethodDesc delegateCtor = null; + if (node.Id == ReadyToRunHelperId.DelegateCtor) + { + DelegateCreationInfo target = (DelegateCreationInfo)node.Target; + delegateCtor = target.Constructor.Method; + bool isStatic = delegateCtor.Signature.IsStatic; + int argCount = delegateCtor.Signature.Length; + if (!isStatic) argCount++; + for (int i = 0; i < argCount; i++) + { + TypeDesc argType; + if (i == 0 && !isStatic) + { + argType = delegateCtor.OwningType; + } + else + { + argType = delegateCtor.Signature[i - (isStatic ? 0 : 1)]; + } + args.Add(ILImporter.GetLLVMTypeForTypeDesc(argType)); + } + } + + LLVMValueRef helperFunc = Module.GetNamedFunction(node.GetMangledName(factory.NameMangler)); + + if (helperFunc.Handle == IntPtr.Zero) + { + throw new Exception("if the function is requested here, it should have been created earlier"); + } + var helperBlock = helperFunc.AppendBasicBlock("genericHelper"); + builder.PositionAtEnd(helperBlock); + var importer = new ILImporter(builder, compilation, Module, helperFunc, delegateCtor); + LLVMValueRef ctx; + string gepName; + if (node is ReadyToRunGenericLookupFromTypeNode) + { + // Locate the VTable slot that points to the dictionary + int vtableSlot = VirtualMethodSlotHelper.GetGenericDictionarySlot(factory, (TypeDesc)node.DictionaryOwner); + + int pointerSize = factory.Target.PointerSize; + // Load the dictionary pointer from the VTable + int slotOffset = EETypeNode.GetVTableOffset(pointerSize) + (vtableSlot * pointerSize); + var slotGep = builder.BuildGEP(helperFunc.GetParam(1), new[] {LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)slotOffset, false)}, "slotGep"); + var slotGepPtrPtr = builder.BuildPointerCast(slotGep, + LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), 0), "slotGepPtrPtr"); + ctx = builder.BuildLoad(slotGepPtrPtr, "dictGep"); + gepName = "typeNodeGep"; + } + else + { + ctx = helperFunc.GetParam(1); + gepName = "paramGep"; + } + + LLVMValueRef resVar = OutputCodeForDictionaryLookup(builder, factory, node, node.LookupSignature, ctx, gepName); + + switch (node.Id) + { + case ReadyToRunHelperId.GetNonGCStaticBase: + { + MetadataType target = (MetadataType)node.Target; + + if (compilation.HasLazyStaticConstructor(target)) + { + importer.OutputCodeForTriggerCctor(target, resVar); + } + } + break; + + case ReadyToRunHelperId.GetGCStaticBase: + { + MetadataType target = (MetadataType)node.Target; + + var ptrPtrPtr = builder.BuildBitCast(resVar, LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), 0), 0), "ptrPtrPtr"); + + resVar = builder.BuildLoad(builder.BuildLoad(ptrPtrPtr, "ind1"), "ind2"); + + if (compilation.HasLazyStaticConstructor(target)) + { + GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); + var nonGcStaticsBase = OutputCodeForDictionaryLookup(builder, factory, node, nonGcRegionLookup, ctx, "lazyGep"); + importer.OutputCodeForTriggerCctor(target, nonGcStaticsBase); + } + } + break; + + case ReadyToRunHelperId.GetThreadStaticBase: + { + MetadataType target = (MetadataType)node.Target; + + if (compilation.HasLazyStaticConstructor(target)) + { + GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); + var threadStaticBase = OutputCodeForDictionaryLookup(builder, factory, node, nonGcRegionLookup, ctx, "tsGep"); + importer.OutputCodeForTriggerCctor(target, threadStaticBase); + } + resVar = importer.OutputCodeForGetThreadStaticBaseForType(resVar).ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), builder); + } + break; + + case ReadyToRunHelperId.DelegateCtor: + { + DelegateCreationInfo target = (DelegateCreationInfo)node.Target; + MethodDesc constructor = target.Constructor.Method; + var fatPtr = ILImporter.MakeFatPointer(builder, resVar, compilation); + importer.OutputCodeForDelegateCtorInit(builder, helperFunc, constructor, fatPtr); + } + break; + + // These are all simple: just get the thing from the dictionary and we're done + case ReadyToRunHelperId.TypeHandle: + case ReadyToRunHelperId.TypeHandleForCasting: + case ReadyToRunHelperId.MethodHandle: + case ReadyToRunHelperId.FieldHandle: + case ReadyToRunHelperId.MethodDictionary: + case ReadyToRunHelperId.MethodEntry: + case ReadyToRunHelperId.VirtualDispatchCell: + case ReadyToRunHelperId.DefaultConstructor: + break; + default: + throw new NotImplementedException(); + } + + if (node.Id != ReadyToRunHelperId.DelegateCtor) + { + builder.BuildRet(resVar); + } + else + { + builder.BuildRetVoid(); + } + } + + private LLVMValueRef OutputCodeForDictionaryLookup(LLVMBuilderRef builder, NodeFactory factory, + ReadyToRunGenericHelperNode node, GenericLookupResult lookup, LLVMValueRef ctx, string gepName) + { + // Find the generic dictionary slot + int dictionarySlot = factory.GenericDictionaryLayout(node.DictionaryOwner).GetSlotForEntry(lookup); + int offset = dictionarySlot * factory.Target.PointerSize; + + // Load the generic dictionary cell + LLVMValueRef retGep = builder.BuildGEP(ctx, new[] {LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)offset, false)}, "retGep"); + LLVMValueRef castGep = builder.BuildBitCast(retGep, LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), 0), "ptrPtr"); + LLVMValueRef retRef = builder.BuildLoad(castGep, gepName); + + switch (lookup.LookupResultReferenceType(factory)) + { + case GenericLookupResultReferenceType.Indirect: + var ptrPtr = builder.BuildBitCast(retRef, LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), 0), "ptrPtr"); + retRef = builder.BuildLoad(ptrPtr, "indLoad"); + break; + + case GenericLookupResultReferenceType.ConditionalIndirect: + throw new NotImplementedException(); + + default: + break; + } + + return retRef; + } + + } +} + +namespace Internal.IL +{ + partial class ILImporter + { + public ILImporter(LLVMBuilderRef builder, LLVMCodegenCompilation compilation, LLVMModuleRef module, LLVMValueRef helperFunc, MethodDesc delegateCtor) + { + this._builder = builder; + this._compilation = compilation; + this.Module = module; + this._currentFunclet = helperFunc; + _locals = new LocalVariableDefinition[0]; + if (delegateCtor == null) + { + _signature = new MethodSignature(MethodSignatureFlags.None, 0, GetWellKnownType(WellKnownType.Void), + new TypeDesc[0]); + } + else + { + _signature = delegateCtor.Signature; + _argSlots = new LLVMValueRef[_signature.Length]; + int signatureIndex = 2; // past hidden param + int thisOffset = 0; + if (!_signature.IsStatic) + { + thisOffset = 1; + } + for (int i = 0; i < _signature.Length; i++) + { + if (CanStoreTypeOnStack(_signature[i])) + { + LLVMValueRef storageAddr; + LLVMValueRef argValue = helperFunc.GetParam((uint)signatureIndex); + + // The caller will always pass the argument on the stack. If this function doesn't have + // EH, we can put it in an alloca for efficiency and better debugging. Otherwise, + // copy it to the shadow stack so funclets can find it + int argOffset = i + thisOffset; + string argName = $"arg{argOffset}_"; + storageAddr = _builder.BuildAlloca(GetLLVMTypeForTypeDesc(_signature[i]), argName); + _argSlots[i] = storageAddr; + _builder.BuildStore(argValue, storageAddr); + signatureIndex++; + } + } + } + _thisType = GetWellKnownType(WellKnownType.Void); + _pointerSize = compilation.NodeFactory.Target.PointerSize; + _exceptionRegions = new ExceptionRegion[0]; + } + + internal void OutputCodeForTriggerCctor(TypeDesc type, LLVMValueRef staticBaseValueRef) + { + IMethodNode helperNode = (IMethodNode)_compilation.NodeFactory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase); + TriggerCctor((MetadataType)helperNode.Method.OwningType, staticBaseValueRef, helperNode.Method.Name); + } + + public void OutputCodeForDelegateCtorInit(LLVMBuilderRef builder, LLVMValueRef helperFunc, + MethodDesc constructor, + LLVMValueRef fatFunction) + { + StackEntry[] argValues = new StackEntry [constructor.Signature.Length + 1]; // for delegate this + var shadowStack = helperFunc.GetParam(0); + argValues[0] = new LoadExpressionEntry(StackValueKind.ObjRef, "this", shadowStack, GetWellKnownType(WellKnownType.Object)); + for (var i = 0; i < constructor.Signature.Length; i++) + { + if (i == 1) + { + argValues[i + 1] = new ExpressionEntry(StackValueKind.Int32, "arg" + (i + 1), + builder.BuildIntToPtr(fatFunction, LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), "toPtr"), + GetWellKnownType(WellKnownType.IntPtr)); + } + else + { + var argRef = LoadVarAddress(i + 1, LocalVarKind.Argument, out TypeDesc type); + var ptrPtr = builder.BuildPointerCast(argRef, + LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), 0), "ptrPtr"); + var loadArg = builder.BuildLoad(ptrPtr, "arg" + (i + 1)); + argValues[i + 1] = new ExpressionEntry(GetStackValueKind(constructor.Signature[i]), "arg" + i + 1, loadArg, + constructor.Signature[i]); + } + } + HandleCall(constructor, constructor.Signature, constructor, argValues, null); + } + } +} diff --git a/src/coreclr/src/tools/aot/ILCompiler.LLVM/CodeGen/LLVMSharpInterop.cs b/src/coreclr/src/tools/aot/ILCompiler.LLVM/CodeGen/LLVMSharpInterop.cs new file mode 100644 index 000000000000..7bed30163341 --- /dev/null +++ b/src/coreclr/src/tools/aot/ILCompiler.LLVM/CodeGen/LLVMSharpInterop.cs @@ -0,0 +1,23 @@ +using LLVMSharp.Interop; + +namespace Internal.IL +{ + internal class LLVMSharpInterop + { + /// + /// Workaround while waiting for https://github.com/microsoft/LLVMSharp/pull/141 + /// + internal static unsafe uint ElementAtOffset(LLVMTargetDataRef targetDataRef, LLVMTypeRef structTypeRef, ulong offset) + { + return LLVM.ElementAtOffset(targetDataRef, structTypeRef, offset); + } + + /// + /// Wrapper while waiting for https://github.com/microsoft/LLVMSharp/pull/144 + /// + internal static unsafe void DISetSubProgram(LLVMValueRef function, LLVMMetadataRef diFunction) + { + LLVM.SetSubprogram(function, diFunction); + } + } +} diff --git a/src/coreclr/src/tools/aot/ILCompiler.LLVM/CodeGen/NodeDataSection.cs b/src/coreclr/src/tools/aot/ILCompiler.LLVM/CodeGen/NodeDataSection.cs new file mode 100644 index 000000000000..b7ce23d30878 --- /dev/null +++ b/src/coreclr/src/tools/aot/ILCompiler.LLVM/CodeGen/NodeDataSection.cs @@ -0,0 +1,29 @@ +// 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.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ILCompiler.Compiler.CppCodeGen +{ + internal enum NodeDataSectionType + { + Relocation, + ByteData + } + + internal struct NodeDataSection + { + public readonly NodeDataSectionType SectionType; + public readonly int SectionSize; + + public NodeDataSection(NodeDataSectionType sectionType, int sectionSize) + { + SectionType = sectionType; + SectionSize = sectionSize; + } + } +} diff --git a/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/EHInfoNode.cs b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/EHInfoNode.cs new file mode 100644 index 000000000000..ded7cb7591d2 --- /dev/null +++ b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/EHInfoNode.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using ILCompiler.DependencyAnalysis; +using Internal.Text; + +namespace ILCompiler.Compiler.DependencyAnalysis +{ + public class EHInfoNode : ObjectNode, ISymbolDefinitionNode + { + private readonly string _name; + private ArrayBuilder _ehInfoBuilder; + private readonly ObjectAndOffsetSymbolNode _endSymbol; + private Relocation[] _relocs; + + public override ObjectNodeSection Section => ObjectNodeSection.ReadOnlyDataSection; + + public override bool IsShareable => false; + + public override int ClassCode => 354769872; + + public override bool StaticDependenciesAreComputed => true; + + public int Offset => 0; + + public EHInfoNode(string mangledName) + { + _name = mangledName + "___EHInfo"; + _ehInfoBuilder = new ArrayBuilder(); + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, mangledName + "___EHInfo_End", true); + } + public int AddEHInfo(ObjectData ehInfo) + { + int offset = _ehInfoBuilder.Count; + _ehInfoBuilder.Append(ehInfo.Data); + _relocs = ehInfo.Relocs; + return offset; + } + + public int Count => _ehInfoBuilder.Count; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + // EH info node is a singleton in the R2R PE file + sb.Append(_name); + } + + public ISymbolDefinitionNode EndSymbol => _endSymbol; + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + var byteArray = _ehInfoBuilder.ToArray(); + _endSymbol.SetSymbolOffset(byteArray.Length); + return new ObjectData(byteArray, _relocs, alignment: 1, definedSymbols: new ISymbolDefinitionNode[] { this, _endSymbol }); + } + + protected override string GetName(NodeFactory context) + { + Utf8StringBuilder sb = new Utf8StringBuilder(); + AppendMangledName(context.NameMangler, sb); + return sb.ToString(); + } + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return _name.CompareTo(((EHInfoNode)other)._name); + } + } +} diff --git a/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMBlockRefNode.cs b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMBlockRefNode.cs new file mode 100644 index 000000000000..2726bde2f8f6 --- /dev/null +++ b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMBlockRefNode.cs @@ -0,0 +1,53 @@ +// 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.Linq; +using ILCompiler.DependencyAnalysisFramework; +using Internal.Text; + +namespace ILCompiler.DependencyAnalysis +{ + internal class LLVMBlockRefNode : DependencyNodeCore, ISymbolNode + { + readonly string mangledName; + + public LLVMBlockRefNode(string mangledName) + { + this.mangledName = mangledName; + } + + public override bool HasConditionalStaticDependencies => false; + public override bool HasDynamicDependencies => false; + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool StaticDependenciesAreComputed => true; + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + return Enumerable.Empty(); + } + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) + { + throw new NotImplementedException(); + } + + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) + { + throw new NotImplementedException(); + } + + protected override string GetName(NodeFactory context) + { + throw new NotImplementedException(); + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(mangledName); + } + + public int Offset { get; } + public bool RepresentsIndirectionCell { get; } + } +} diff --git a/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMCodegenNodeFactory.cs b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMCodegenNodeFactory.cs new file mode 100644 index 000000000000..f47f27c3ea21 --- /dev/null +++ b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMCodegenNodeFactory.cs @@ -0,0 +1,95 @@ +// 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 ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public sealed class LLVMCodegenNodeFactory : NodeFactory + { + private NodeCache _vTableSlotNodes; + + public LLVMCodegenNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager, + InteropStubManager interopStubManager, NameMangler nameMangler, VTableSliceProvider vtableSliceProvider, DictionaryLayoutProvider dictionaryLayoutProvider, PreinitializationManager preinitializationManager) + : base(context, + compilationModuleGroup, + metadataManager, + interopStubManager, + nameMangler, + new LazyGenericsDisabledPolicy(), + vtableSliceProvider, + dictionaryLayoutProvider, + new ImportedNodeProviderThrowing(), + preinitializationManager) + { + _vTableSlotNodes = new NodeCache(methodKey => + { + return new LLVMVTableSlotNode(methodKey); + }); + } + + public override bool IsCppCodegenTemporaryWorkaround => true; + + protected override IMethodNode CreateMethodEntrypointNode(MethodDesc method) + { + if (method.IsInternalCall) + { + if (TypeSystemContext.IsSpecialUnboxingThunkTargetMethod(method)) + { + return MethodEntrypoint(TypeSystemContext.GetRealSpecialUnboxingThunkTargetMethod(method)); + } + if (method.IsArrayAddressMethod()) + { + return new LlvmMethodBodyNode(((ArrayType)method.OwningType).GetArrayMethod(ArrayMethodKind.AddressWithHiddenArg)); + } + } + if (CompilationModuleGroup.ContainsMethodBody(method, false)) + { + return new LlvmMethodBodyNode(method); + } + else + { + return new ExternMethodSymbolNode(this, method); + } + } + + public LLVMVTableSlotNode VTableSlot(MethodDesc method) + { + return _vTableSlotNodes.GetOrAdd(method); + } + + protected override IMethodNode CreateUnboxingStubNode(MethodDesc method) + { + if (method.IsCanonicalMethod(CanonicalFormKind.Specific) && !method.HasInstantiation) + { + // Unboxing stubs to canonical instance methods need a special unboxing stub that unboxes + // 'this' and also provides an instantiation argument (we do a calling convention conversion). + // We don't do this for generic instance methods though because they don't use the EEType + // for the generic context anyway. + return new LlvmMethodBodyNode(TypeSystemContext.GetSpecialUnboxingThunk(method, TypeSystemContext.GeneratedAssembly)); + } + else + { + // Otherwise we just unbox 'this' and don't touch anything else. + return new LlvmUnboxingThunkNode(TypeSystemContext.GetUnboxingThunk(method, TypeSystemContext.GeneratedAssembly)); + } + } + + protected override ISymbolNode CreateReadyToRunHelperNode(ReadyToRunHelperKey helperCall) + { + throw new NotSupportedException(); + } + + protected override ISymbolNode CreateGenericLookupFromDictionaryNode(ReadyToRunGenericHelperKey helperKey) + { + return new LLVMReadyToRunGenericLookupFromDictionaryNode(this, helperKey.HelperId, helperKey.Target, helperKey.DictionaryOwner); + } + + protected override ISymbolNode CreateGenericLookupFromTypeNode(ReadyToRunGenericHelperKey helperKey) + { + return new LLVMReadyToRunGenericLookupFromTypeNode(this, helperKey.HelperId, helperKey.Target, helperKey.DictionaryOwner); + } + } +} diff --git a/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMMethodCodeNode.cs b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMMethodCodeNode.cs new file mode 100644 index 000000000000..997176532dc4 --- /dev/null +++ b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMMethodCodeNode.cs @@ -0,0 +1,87 @@ +// 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.Diagnostics; +using System.Linq; +using ILCompiler.DependencyAnalysisFramework; + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + internal abstract class LLVMMethodCodeNode : DependencyNodeCore + { + protected readonly MethodDesc _method; + protected IEnumerable _dependencies = Enumerable.Empty(); + + protected LLVMMethodCodeNode(MethodDesc method) + { + Debug.Assert(!method.IsAbstract); + _method = method; + } + + public void SetDependencies(IEnumerable dependencies) + { + Debug.Assert(dependencies != null); + _dependencies = dependencies; + } + + public MethodDesc Method + { + get + { + return _method; + } + } + + public override bool StaticDependenciesAreComputed => CompilationCompleted; + + public bool CompilationCompleted { get; set; } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.GetMangledMethodName(_method)); + } + public int Offset => 0; + public bool RepresentsIndirectionCell => false; + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + } + + internal class LlvmMethodBodyNode : LLVMMethodCodeNode, IMethodBodyNode + { + public LlvmMethodBodyNode(MethodDesc method) + : base(method) + { + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + var dependencies = new DependencyList(); + + foreach (Object node in _dependencies) + dependencies.Add(node, "Wasm code "); + + CodeBasedDependencyAlgorithm.AddDependenciesDueToMethodCodePresence(ref dependencies, factory, _method); + + return dependencies; + } + + int ISortableNode.ClassCode => -1502960727; + + int ISortableNode.CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_method, ((LlvmMethodBodyNode)other)._method); + } + } +} diff --git a/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMReadyToRunGenericLookupFromDictionaryNode.cs b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMReadyToRunGenericLookupFromDictionaryNode.cs new file mode 100644 index 000000000000..e74a1a3b9556 --- /dev/null +++ b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMReadyToRunGenericLookupFromDictionaryNode.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + internal class LLVMReadyToRunGenericLookupFromDictionaryNode : ReadyToRunGenericLookupFromDictionaryNode + { + public LLVMReadyToRunGenericLookupFromDictionaryNode(NodeFactory factory, ReadyToRunHelperId helperId, object target, TypeSystemEntity dictionaryOwner) + : base(factory, helperId, target, dictionaryOwner) + { + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly) + { + // this code for this node is written out in LLVMObjectWriter.GetCodeForReadyToRunGenericHelper + return new ObjectData(new byte[0], new Relocation[0], 1, new ISymbolDefinitionNode[0]); + } + } +} diff --git a/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMReadyToRunGenericLookupFromTypeNode.cs b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMReadyToRunGenericLookupFromTypeNode.cs new file mode 100644 index 000000000000..13884c13246f --- /dev/null +++ b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMReadyToRunGenericLookupFromTypeNode.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + internal class LLVMReadyToRunGenericLookupFromTypeNode : ReadyToRunGenericLookupFromTypeNode + { + public LLVMReadyToRunGenericLookupFromTypeNode(NodeFactory factory, ReadyToRunHelperId helperId, object target, TypeSystemEntity dictionaryOwner) + : base(factory, helperId, target, dictionaryOwner) + { + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly) + { + // this code for this node is written out in LLVMObjectWriter.GetCodeForReadyToRunGenericHelper + return new ObjectData(new byte[0], new Relocation[0], 1, new ISymbolDefinitionNode[0]); + } + } +} diff --git a/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMVTableSlotNode.cs b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMVTableSlotNode.cs new file mode 100644 index 000000000000..b40d934c43f0 --- /dev/null +++ b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMVTableSlotNode.cs @@ -0,0 +1,85 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public class LLVMVTableSlotNode : ObjectNode, ISymbolDefinitionNode + { + private readonly MethodDesc _targetMethod; + + public LLVMVTableSlotNode(MethodDesc targetMethod) + { + Debug.Assert(targetMethod.IsVirtual); + Debug.Assert(!targetMethod.HasInstantiation); + Debug.Assert(!targetMethod.IsRuntimeDeterminedExactMethod); + _targetMethod = targetMethod; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(GetMangledName(nameMangler, _targetMethod)); + } + public int Offset => 0; + + public override bool IsShareable => false; + + public static string GetMangledName(NameMangler nameMangler, MethodDesc method) + { + return "__getslot__" + nameMangler.GetMangledMethodName(method); + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectNodeSection Section => ObjectNodeSection.DataSection; + + public override bool StaticDependenciesAreComputed => true; + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList result = new DependencyList(); + + if (!factory.VTable(_targetMethod.OwningType).HasFixedSlots) + { + result.Add(factory.VirtualMethodUse(_targetMethod), "VTable method use"); + } + + return result; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + Debug.Assert((EETypeNode.GetVTableOffset(factory.Target.PointerSize) % factory.Target.PointerSize) == 0, "vtable offset must be aligned"); + ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); + + objData.AddSymbol(this); + + if (!relocsOnly) + { + int tableOffset; + if (_targetMethod.OwningType.IsInterface) + { + tableOffset = 0; + } + else + { + tableOffset = EETypeNode.GetVTableOffset(factory.Target.PointerSize) / factory.Target.PointerSize; + } + + objData.EmitInt(tableOffset + VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, _targetMethod, _targetMethod.OwningType)); + } + return objData.ToObjectData(); + } + + public override int ClassCode => 0; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_targetMethod, ((LLVMVTableSlotNode)other)._targetMethod); + } + } +} diff --git a/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LlvmUnboxingThunkNode.cs b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LlvmUnboxingThunkNode.cs new file mode 100644 index 000000000000..a087738e2901 --- /dev/null +++ b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LlvmUnboxingThunkNode.cs @@ -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. + +using System; +using System.Collections.Generic; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + internal class LlvmUnboxingThunkNode : LLVMMethodCodeNode, IMethodNode + { + public LlvmUnboxingThunkNode(MethodDesc method) + : base(method) + { + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + var dependencies = new DependencyList(); + + foreach (Object node in _dependencies) + dependencies.Add(node, "Wasm code "); + + return dependencies; + } + + int ISortableNode.ClassCode => -18942467; + + int ISortableNode.CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return comparer.Compare(_method, ((LlvmUnboxingThunkNode)other)._method); + } + } +} diff --git a/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/RawMainMethodRootProvider.cs b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/RawMainMethodRootProvider.cs new file mode 100644 index 000000000000..9b37f79d6f73 --- /dev/null +++ b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/RawMainMethodRootProvider.cs @@ -0,0 +1,28 @@ +// 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 Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler +{ + public class RawMainMethodRootProvider : ICompilationRootProvider + { + private EcmaModule _module; + + public RawMainMethodRootProvider(EcmaModule module) + { + _module = module; + } + + public void AddCompilationRoots(IRootingServiceProvider rootProvider) + { + MethodDesc mainMethod = _module.EntryPoint; + if (mainMethod == null) + throw new Exception("No managed entrypoint defined for executable module"); + + rootProvider.AddCompilationRoot(mainMethod, "Managed Main Method"); + } + } +} diff --git a/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/LLVMCodegenCompilation.cs b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/LLVMCodegenCompilation.cs new file mode 100644 index 000000000000..457e8ad5c59c --- /dev/null +++ b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/LLVMCodegenCompilation.cs @@ -0,0 +1,103 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using Internal.TypeSystem; +using Internal.IL; + +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; +using LLVMSharp.Interop; +using ILCompiler.LLVM; + +namespace ILCompiler +{ + public sealed class LLVMCodegenCompilation : Compilation + { + 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, + LLVMCodegenNodeFactory nodeFactory, + IEnumerable roots, + ILProvider ilProvider, + DebugInformationProvider debugInformationProvider, + Logger logger, + LLVMCodegenConfigProvider options) + : base(dependencyGraph, nodeFactory, GetCompilationRoots(roots, nodeFactory), ilProvider, debugInformationProvider, null, logger) + { + NodeFactory = nodeFactory; + LLVMModuleRef m = LLVMModuleRef.CreateWithName(options.ModuleName); + m.Target = options.Target; + m.DataLayout = options.DataLayout; + Module = m; + TargetData = m.CreateExecutionEngine().TargetData; + Options = options; + DIBuilder = Module.CreateDIBuilder(); + DebugMetadataMap = new Dictionary(); + } + + private static IEnumerable GetCompilationRoots(IEnumerable existingRoots, NodeFactory factory) + { + foreach (var existingRoot in existingRoots) + yield return existingRoot; + } + + protected override void CompileInternal(string outputFile, ObjectDumper dumper) + { + _dependencyGraph.ComputeMarkedNodes(); + + var nodes = _dependencyGraph.MarkedNodeList; + + LLVMObjectWriter.EmitObject(outputFile, nodes, NodeFactory, this, dumper); + } + + protected override void ComputeDependencyNodeDependencies(List> obj) + { + foreach (var dependency in obj) + { + var methodCodeNodeNeedingCode = dependency as LLVMMethodCodeNode; + if (methodCodeNodeNeedingCode == null) + { + // To compute dependencies of the shadow method that tracks dictionary + // dependencies we need to ensure there is code for the canonical method body. + var dependencyMethod = (ShadowConcreteMethodNode)dependency; + methodCodeNodeNeedingCode = (LLVMMethodCodeNode)dependencyMethod.CanonicalMethodNode; + } + + // We might have already compiled this method. + if (methodCodeNodeNeedingCode.StaticDependenciesAreComputed) + continue; + + ILImporter.CompileMethod(this, methodCodeNodeNeedingCode); + } + } + + public TypeDesc ConvertToCanonFormIfNecessary(TypeDesc type, CanonicalFormKind policy) + { + if (!type.IsCanonicalSubtype(CanonicalFormKind.Any)) + return type; + + if (type.IsPointer || type.IsByRef) + { + ParameterizedType parameterizedType = (ParameterizedType)type; + TypeDesc paramTypeConverted = ConvertToCanonFormIfNecessary(parameterizedType.ParameterType, policy); + if (paramTypeConverted == parameterizedType.ParameterType) + return type; + + if (type.IsPointer) + return TypeSystemContext.GetPointerType(paramTypeConverted); + + if (type.IsByRef) + return TypeSystemContext.GetByRefType(paramTypeConverted); + } + + return type.ConvertToCanonForm(policy); + } + } +} diff --git a/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/LLVMCodegenCompilationBuilder.cs b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/LLVMCodegenCompilationBuilder.cs new file mode 100644 index 000000000000..54455c93cdfd --- /dev/null +++ b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/LLVMCodegenCompilationBuilder.cs @@ -0,0 +1,89 @@ +// 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 ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; + +using Internal.IL; + +namespace ILCompiler +{ + public sealed class LLVMCodegenCompilationBuilder : CompilationBuilder + { + // These need to provide reasonable defaults so that the user can optionally skip + // calling the Use/Configure methods and still get something reasonable back. + LLVMCodegenConfigProvider _config = new LLVMCodegenConfigProvider(Array.Empty()); + private ILProvider _ilProvider = new CoreRTILProvider(); + + public LLVMCodegenCompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup group) + : base(context, group, new CoreRTNameMangler(new LLVMNodeMangler(), false)) + { + } + + public override CompilationBuilder UseBackendOptions(IEnumerable options) + { + _config = new LLVMCodegenConfigProvider(options); + return this; + } + + public override CompilationBuilder UseILProvider(ILProvider ilProvider) + { + _ilProvider = ilProvider; + return this; + } + + protected override ILProvider GetILProvider() + { + return _ilProvider; + } + + public override ICompilation ToCompilation() + { + LLVMCodegenNodeFactory factory = new LLVMCodegenNodeFactory(_context, _compilationGroup, _metadataManager, _interopStubManager, _nameMangler, _vtableSliceProvider, _dictionaryLayoutProvider, GetPreinitializationManager()); + DependencyAnalyzerBase graph = CreateDependencyGraph(factory, new ObjectNode.ObjectNodeComparer(new CompilerComparer())); + return new LLVMCodegenCompilation(graph, factory, _compilationRoots, _ilProvider, _debugInformationProvider, _logger, _config); + } + } + + internal class LLVMCodegenConfigProvider + { + private readonly HashSet _options; + + public const string NoLineNumbersString = "NoLineNumbers"; + + public LLVMCodegenConfigProvider(IEnumerable options) + { + _options = new HashSet(options, StringComparer.OrdinalIgnoreCase); + } + + public string Target => ValueOrDefault("Target", "wasm32-unknown-emscripten"); + public string ModuleName => ValueOrDefault("ModuleName", "netscripten"); + + // https://llvm.org/docs/LangRef.html#langref-datalayout + // e litte endian, mangled names + // m:e ELF mangling + // p:32:32 pointer size 32, abi 32 + // i64:64 64 ints aligned 64 + // n:32:64 native widths + // S128 natural alignment of stack + public string DataLayout => ValueOrDefault("DataLayout", "e-m:e-p:32:32-i64:64-n32:64-S128"); + + private string ValueOrDefault(string optionName, string defaultValue) + { + if (_options.TryGetValue(optionName, out string value)) + { + return value; + } + + return defaultValue; + } + + public bool HasOption(string optionName) + { + return _options.Contains(optionName); + } + } +} diff --git a/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/LLVMNodeMangler.cs b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/LLVMNodeMangler.cs new file mode 100644 index 000000000000..fadc98db2c41 --- /dev/null +++ b/src/coreclr/src/tools/aot/ILCompiler.LLVM/Compiler/LLVMNodeMangler.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; +using System.Diagnostics; + +namespace ILCompiler +{ + // + // The naming format of these names is known to the debugger + // + public sealed class LLVMNodeMangler : NodeMangler + { + // Mangled name of boxed version of a type + public sealed override string MangledBoxedTypeName(TypeDesc type) + { + Debug.Assert(type.IsValueType); + return "Boxed_" + NameMangler.GetMangledTypeName(type); + } + + public sealed override string EEType(TypeDesc type) + { + return "__EEType_" + NameMangler.GetMangledTypeName(type); + } + + public sealed override string GCStatics(TypeDesc type) + { + return "__GCStaticBase_" + NameMangler.GetMangledTypeName(type); + } + + public sealed override string NonGCStatics(TypeDesc type) + { + return "__NonGCStaticBase_" + NameMangler.GetMangledTypeName(type); + } + + public sealed override string ThreadStatics(TypeDesc type) + { + return "__ThreadStaticBase_" + NameMangler.GetMangledTypeName(type); + } + + public sealed override string TypeGenericDictionary(TypeDesc type) + { + return GenericDictionaryNamePrefix + NameMangler.GetMangledTypeName(type); + } + + public sealed override string MethodGenericDictionary(MethodDesc method) + { + return GenericDictionaryNamePrefix + NameMangler.GetMangledMethodName(method); + } + } +} diff --git a/src/coreclr/src/tools/aot/ILCompiler.LLVM/ILCompiler.LLVM.csproj b/src/coreclr/src/tools/aot/ILCompiler.LLVM/ILCompiler.LLVM.csproj new file mode 100644 index 000000000000..ee94f2f77a9a --- /dev/null +++ b/src/coreclr/src/tools/aot/ILCompiler.LLVM/ILCompiler.LLVM.csproj @@ -0,0 +1,65 @@ + + + + Library + ILCompiler + ILCompiler.LLVM + true + net5.0 + 8002 + false + + + + + 9.0.0-beta + + + + + + + + + + + + IL\ILImporter.cs + + + IL\StackValueKind.cs + + + Common\ArrayBuilder.cs + + + Common\FormattingHelpers.cs + + + IL\HelperExtensions.cs + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/coreclr/src/tools/aot/ILCompiler.LLVM/libLLVMdep.depproj b/src/coreclr/src/tools/aot/ILCompiler.LLVM/libLLVMdep.depproj new file mode 100644 index 000000000000..d4279311774b --- /dev/null +++ b/src/coreclr/src/tools/aot/ILCompiler.LLVM/libLLVMdep.depproj @@ -0,0 +1,26 @@ + + + + + $(BaseOutputPath)$(OSPlatformConfig)/tools + + + + netstandard2.0 + $(NuPkgRid) + true +true + + + + + 9.0.0-beta + + + 9.0.0 + + + + + + diff --git a/src/coreclr/src/tools/aot/ILCompiler/ILCompiler.csproj b/src/coreclr/src/tools/aot/ILCompiler/ILCompiler.csproj index 31113f8015b4..75b8e4743b3b 100644 --- a/src/coreclr/src/tools/aot/ILCompiler/ILCompiler.csproj +++ b/src/coreclr/src/tools/aot/ILCompiler/ILCompiler.csproj @@ -24,9 +24,11 @@ .dll .so .dylib - win-$(TargetArchitecture) - linux-$(TargetArchitecture) - osx-$(TargetArchitecture) + win + linux + osx + $(TargetPlatform)-$(TargetArchitecture) + $(TargetPlatform)-x64 @@ -65,13 +67,13 @@ false - + PreserveNewest false false - + $(LibraryNamePrefix)clrjitilc$(LibraryNameExtension) PreserveNewest false @@ -81,6 +83,7 @@ + diff --git a/src/coreclr/src/tools/aot/ILCompiler/Program.cs b/src/coreclr/src/tools/aot/ILCompiler/Program.cs index 0604fa6c70e7..da6b4f8985e9 100644 --- a/src/coreclr/src/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/src/tools/aot/ILCompiler/Program.cs @@ -26,6 +26,7 @@ internal class Program private string _outputFilePath; private bool _isVerbose; + private bool _isLlvmCodegen; private string _dgmlLogFileName; private bool _generateFullDgmlLog; @@ -158,6 +159,8 @@ private ArgumentSyntax ParseCommandLine(string[] args) syntax.DefineOption("Os", ref optimizeSpace, "Enable optimizations, favor code space"); syntax.DefineOption("Ot", ref optimizeTime, "Enable optimizations, favor code speed"); syntax.DefineOption("g", ref _enableDebugInfo, "Emit debugging information"); + syntax.DefineOption("wasm", ref _isLlvmCodegen, "Compile for Web Assembly code-generation"); + syntax.DefineOption("llvm", ref _isLlvmCodegen, "Compile for LLVM code-generation"); syntax.DefineOption("nativelib", ref _nativeLib, "Compile as static or shared library"); syntax.DefineOption("exportsfile", ref _exportsFile, "File to write exported method definitions"); syntax.DefineOption("dgmllog", ref _dgmlLogFileName, "Save result of dependency analysis as DGML"); @@ -284,6 +287,11 @@ private int Run(string[] args) _targetArchitecture = TargetArchitecture.ARM; else if (_targetArchitectureStr.Equals("arm64", StringComparison.OrdinalIgnoreCase)) _targetArchitecture = TargetArchitecture.ARM64; + else if (_targetArchitectureStr.Equals("wasm", StringComparison.OrdinalIgnoreCase) || _targetArchitectureStr.Equals("llvm", StringComparison.OrdinalIgnoreCase)) // handle both so old projects still work + { + _targetArchitecture = TargetArchitecture.Wasm32; // TODO: handle generic LLVM and Wasm64 + _isLlvmCodegen = true; + } else throw new CommandLineException("Target architecture is not supported"); } @@ -295,6 +303,8 @@ private int Run(string[] args) _targetOS = TargetOS.Linux; else if (_targetOSStr.Equals("osx", StringComparison.OrdinalIgnoreCase)) _targetOS = TargetOS.OSX; + else if (_targetOSStr.Equals("wasm", StringComparison.OrdinalIgnoreCase)) + _targetOS = TargetOS.WebAssembly; else throw new CommandLineException("Target OS is not supported"); } @@ -421,7 +431,7 @@ private int Run(string[] args) SharedGenericsMode genericsMode = SharedGenericsMode.CanonicalReferenceTypes; - var simdVectorLength = instructionSetSupport.GetVectorTSimdVector(); + var simdVectorLength = (_isLlvmCodegen) ? SimdVectorLength.None : instructionSetSupport.GetVectorTSimdVector(); var targetAbi = TargetAbi.CoreRT; var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, targetAbi, simdVectorLength); CompilerTypeSystemContext typeSystemContext = @@ -554,12 +564,20 @@ private int Run(string[] args) // Compile // - CompilationBuilder builder = new RyuJitCompilationBuilder(typeSystemContext, compilationGroup); + CompilationBuilder builder; + if (_isLlvmCodegen) + builder = new LLVMCodegenCompilationBuilder(typeSystemContext, compilationGroup); + else + builder = new RyuJitCompilationBuilder(typeSystemContext, compilationGroup); string compilationUnitPrefix = _multiFile ? System.IO.Path.GetFileNameWithoutExtension(_outputFilePath) : ""; builder.UseCompilationUnitPrefix(compilationUnitPrefix); - PInvokeILEmitterConfiguration pinvokePolicy = new ConfigurablePInvokePolicy(typeSystemContext.Target); + PInvokeILEmitterConfiguration pinvokePolicy; + if (_isLlvmCodegen) + pinvokePolicy = new DirectPInvokePolicy(); + else + pinvokePolicy = new ConfigurablePInvokePolicy(typeSystemContext.Target); RemovedFeature removedFeatures = 0; foreach (string feature in _removedFeatures) @@ -637,7 +655,7 @@ private int Run(string[] args) // fixable by using a CompilationGroup for the scanner that has a bigger worldview, but // let's cross that bridge when we get there). bool useScanner = _useScanner || - (_optimizationMode != OptimizationMode.None && !_multiFile); + (_optimizationMode != OptimizationMode.None && !_isLlvmCodegen && !_multiFile); useScanner &= !_noScanner; diff --git a/src/coreclr/src/tools/aot/ilc.sln b/src/coreclr/src/tools/aot/ilc.sln index 25fc1745ba8f..6f6dedb4d475 100644 --- a/src/coreclr/src/tools/aot/ilc.sln +++ b/src/coreclr/src/tools/aot/ilc.sln @@ -14,6 +14,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.TypeSystem", "IL EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.Compiler", "ILCompiler.Compiler\ILCompiler.Compiler.csproj", "{FFBD9619-DE6F-4A98-8732-8A14EC3C1A18}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.LLVM", "ILCompiler.LLVM\ILCompiler.LLVM.csproj", "{8487612B-4DB2-4EA5-BBB3-2303659809A9}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "repro", "ILCompiler\repro\repro.csproj", "{CBDE0470-E0C9-4693-9A11-ACC117522F3F}" EndProject Global @@ -119,6 +121,24 @@ 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 + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Checked|Any CPU.Build.0 = Debug|Any CPU + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Checked|x64.ActiveCfg = Debug|Any CPU + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Checked|x64.Build.0 = Debug|Any CPU + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Checked|x86.ActiveCfg = Debug|Any CPU + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Checked|x86.Build.0 = Debug|Any CPU + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Debug|x64.ActiveCfg = Debug|Any CPU + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Debug|x64.Build.0 = Debug|Any CPU + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Debug|x86.ActiveCfg = Debug|Any CPU + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Debug|x86.Build.0 = Debug|Any CPU + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Release|Any CPU.Build.0 = Release|Any CPU + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Release|x64.ActiveCfg = Release|Any CPU + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Release|x64.Build.0 = Release|Any CPU + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Release|x86.ActiveCfg = Release|Any CPU + {8487612B-4DB2-4EA5-BBB3-2303659809A9}.Release|x86.Build.0 = Release|Any CPU {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 diff --git a/src/installer/pkg/projects/Microsoft.DotNet.ILCompiler/Microsoft.DotNet.ILCompiler.pkgproj b/src/installer/pkg/projects/Microsoft.DotNet.ILCompiler.LLVM/Microsoft.DotNet.ILCompiler.LLVM.pkgproj similarity index 100% rename from src/installer/pkg/projects/Microsoft.DotNet.ILCompiler/Microsoft.DotNet.ILCompiler.pkgproj rename to src/installer/pkg/projects/Microsoft.DotNet.ILCompiler.LLVM/Microsoft.DotNet.ILCompiler.LLVM.pkgproj diff --git a/src/installer/pkg/projects/descriptions.json b/src/installer/pkg/projects/descriptions.json index 50fd1569a48c..8365cb9cf5e0 100644 --- a/src/installer/pkg/projects/descriptions.json +++ b/src/installer/pkg/projects/descriptions.json @@ -10,7 +10,7 @@ "CommonTypes": [ ] }, { - "Name": "Microsoft.DotNet.ILCompiler", + "Name": "Microsoft.DotNet.ILCompiler.LLVM", "Description": "Provides a native AOT compiler and runtime for .NET", "CommonTypes": [ ] }, diff --git a/src/installer/pkg/projects/nativeaot-packages.proj b/src/installer/pkg/projects/nativeaot-packages.proj index e823aee736ad..23850f589e5d 100644 --- a/src/installer/pkg/projects/nativeaot-packages.proj +++ b/src/installer/pkg/projects/nativeaot-packages.proj @@ -2,7 +2,7 @@ - + diff --git a/src/libraries/Directory.Build.props b/src/libraries/Directory.Build.props index 9d600a0efd95..f51d8f6b8d2a 100644 --- a/src/libraries/Directory.Build.props +++ b/src/libraries/Directory.Build.props @@ -255,5 +255,4 @@ false true - diff --git a/src/libraries/Native/Unix/System.Globalization.Native/CMakeLists.txt b/src/libraries/Native/Unix/System.Globalization.Native/CMakeLists.txt index 834a86ec1cca..b124f49f7975 100644 --- a/src/libraries/Native/Unix/System.Globalization.Native/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Globalization.Native/CMakeLists.txt @@ -13,7 +13,11 @@ if(CLR_CMAKE_TARGET_UNIX) add_compile_options(-Wno-extra-semi-stmt) add_compile_options(-Wno-unknown-warning-option) - if (NOT CLR_CMAKE_TARGET_ANDROID) + if (CLR_CMAKE_TARGET_UNIX_WASM) + message("including ICU") + # add ICU support to emscripten and workaround https://github.com/emscripten-core/emscripten/issues/9302 + add_compile_options(-s USE_ICU=1 -I$ENV{EMSDK}/upstream/emscripten/cache/ports/icu/icu/source/i18n) + elseif (NOT CLR_CMAKE_TARGET_ANDROID) set(ICU_HOMEBREW_INC_PATH "/usr/local/opt/icu4c/include") find_path(UTYPES_H "unicode/utypes.h" PATHS ${ICU_HOMEBREW_INC_PATH}) diff --git a/src/libraries/Native/build-native.cmd b/src/libraries/Native/build-native.cmd index e96af68eb5a1..8568899162a3 100644 --- a/src/libraries/Native/build-native.cmd +++ b/src/libraries/Native/build-native.cmd @@ -97,7 +97,7 @@ goto :SetupDirs echo Commencing build of native components echo. -if /i "%__BuildArch%" == "wasm" set __sourceDir=%~dp0..\Unix +if /i "%__BuildArch%" == "wasm" set __sourceDir=%~dp0\Unix if [%__outConfig%] == [] set __outConfig=%__TargetOS%-%__BuildArch%-%CMAKE_BUILD_TYPE% @@ -158,7 +158,7 @@ set __generatorArgs= if [%__Ninja%] == [1] ( set __generatorArgs= ) else if [%__BuildArch%] == [wasm] ( - set __generatorArgs=-j + set __generatorArgs= ) else ( set __generatorArgs=/p:Platform=%__BuildArch% /p:PlatformToolset="%__PlatformToolset%" -noWarn:MSB8065 ) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 5d9e31260a20..8b7c80c27910 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -52,7 +52,6 @@ -