From 37651e03c08fd3ce235d16b024fc8dd21dfb33e8 Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Mon, 8 Sep 2025 14:49:15 +0200 Subject: [PATCH] [interop] Upgrade cppinterop to latest --- interpreter/CppInterOp/.codecov.yml | 7 +- interpreter/CppInterOp/.markdownlint.json | 3 + interpreter/CppInterOp/.readthedocs.yaml | 8 +- interpreter/CppInterOp/CMakeLists.txt | 20 +- .../Emscripten-build-instructions.md | 71 +- interpreter/CppInterOp/README.md | 434 ++-- .../CppInterOp/cmake/modules/GoogleTest.cmake | 17 +- interpreter/CppInterOp/cppinterop-version.tag | 2 +- .../docs/DevelopersDocumentation.rst | 17 - .../docs/Emscripten-build-instructions.rst | 71 +- .../CppInterOp/docs/InstallationAndUsage.rst | 17 - .../include/CppInterOp/CppInterOp.h | 27 + .../CppInterOp/lib/CppInterOp/CMakeLists.txt | 9 + .../lib/CppInterOp/CXCppInterOp.cpp | 14 +- .../CppInterOp/lib/CppInterOp/Compatibility.h | 44 +- .../CppInterOp/lib/CppInterOp/CppInterOp.cpp | 323 ++- .../lib/CppInterOp/DynamicLibraryManager.cpp | 5 +- .../DynamicLibraryManagerSymbol.cpp | 18 +- .../CppInterOp/lib/CppInterOp/Paths.cpp | 3 +- .../CppInterOp/lib/CppInterOp/exports.ld | 3 +- ...ws-emscripten-clang19-1-CrossCompile.patch | 20 - ...ws-emscripten-clang20-1-CrossCompile.patch | 20 - .../patches/llvm/clang16-1-Value.patch | 2084 ----------------- .../patches/llvm/clang16-2-CUDA.patch | 969 -------- .../patches/llvm/clang16-3-WeakRef.patch | 33 - .../patches/llvm/clang17-1-NewOperator.patch | 205 -- ...-clang19-4-enable_exception_handling.patch | 67 + ...-clang20-3-enable_exception_handling.patch | 67 + .../scripts/browser_tests_safari.py | 9 + .../unittests/CppInterOp/CMakeLists.txt | 2 +- .../CppInterOp/FunctionReflectionTest.cpp | 252 +- .../unittests/CppInterOp/InterpreterTest.cpp | 5 +- .../CppInterOp/ScopeReflectionTest.cpp | 22 +- .../CppInterOp/TypeReflectionTest.cpp | 66 + .../CppInterOp/VariableReflectionTest.cpp | 28 +- 35 files changed, 1186 insertions(+), 3776 deletions(-) create mode 100644 interpreter/CppInterOp/.markdownlint.json delete mode 100644 interpreter/CppInterOp/patches/llvm/Windows-emscripten-clang19-1-CrossCompile.patch delete mode 100644 interpreter/CppInterOp/patches/llvm/Windows-emscripten-clang20-1-CrossCompile.patch delete mode 100644 interpreter/CppInterOp/patches/llvm/clang16-1-Value.patch delete mode 100644 interpreter/CppInterOp/patches/llvm/clang16-2-CUDA.patch delete mode 100644 interpreter/CppInterOp/patches/llvm/clang16-3-WeakRef.patch delete mode 100644 interpreter/CppInterOp/patches/llvm/clang17-1-NewOperator.patch create mode 100644 interpreter/CppInterOp/patches/llvm/emscripten-clang19-4-enable_exception_handling.patch create mode 100644 interpreter/CppInterOp/patches/llvm/emscripten-clang20-3-enable_exception_handling.patch create mode 100644 interpreter/CppInterOp/scripts/browser_tests_safari.py diff --git a/interpreter/CppInterOp/.codecov.yml b/interpreter/CppInterOp/.codecov.yml index 3216e14e28bc4..2f7378fea9739 100644 --- a/interpreter/CppInterOp/.codecov.yml +++ b/interpreter/CppInterOp/.codecov.yml @@ -4,7 +4,7 @@ codecov: coverage: precision: 2 round: down - range: "70...100" + range: "75...100" status: project: yes @@ -22,4 +22,7 @@ parsers: comment: layout: "reach, diff, flags, tree, files" behavior: default - require_changes: no \ No newline at end of file + require_changes: no + +github_checks: + annotations: true # Deprecated but very useful diff --git a/interpreter/CppInterOp/.markdownlint.json b/interpreter/CppInterOp/.markdownlint.json new file mode 100644 index 0000000000000..c40ca334bd8de --- /dev/null +++ b/interpreter/CppInterOp/.markdownlint.json @@ -0,0 +1,3 @@ +{ + "no-duplicate-heading": false +} diff --git a/interpreter/CppInterOp/.readthedocs.yaml b/interpreter/CppInterOp/.readthedocs.yaml index 8d42b0c76009c..5dae8b1275b94 100644 --- a/interpreter/CppInterOp/.readthedocs.yaml +++ b/interpreter/CppInterOp/.readthedocs.yaml @@ -9,8 +9,8 @@ build: tools: python: "3.11" apt_packages: - - clang-16 + - clang-18 - cmake - - libclang-16-dev - - llvm-16-dev - - llvm-16-tools + - libclang-18-dev + - llvm-18-dev + - llvm-18-tools diff --git a/interpreter/CppInterOp/CMakeLists.txt b/interpreter/CppInterOp/CMakeLists.txt index f7cf109ee2c1d..1a167fe1de3e8 100644 --- a/interpreter/CppInterOp/CMakeLists.txt +++ b/interpreter/CppInterOp/CMakeLists.txt @@ -67,25 +67,13 @@ if( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR ) include(GNUInstallDirs) ## Define supported version of clang and llvm -if (CPPINTEROP_USE_CLING) set(CLANG_MIN_SUPPORTED 18.0) -else() - set(CLANG_MIN_SUPPORTED 16.0) -endif(CPPINTEROP_USE_CLING) set(CLANG_MAX_SUPPORTED "20.1.x") set(CLANG_VERSION_UPPER_BOUND 21.0.0) -if (CPPINTEROP_USE_CLING) set(LLD_MIN_SUPPORTED 18.0) -else() - set(LLD_MIN_SUPPORTED 16.0) -endif(CPPINTEROP_USE_CLING) set(LLD_MAX_SUPPORTED "20.1.x") set(LLD_VERSION_UPPER_BOUND 21.0.0) -if (CPPINTEROP_USE_CLING) set(LLVM_MIN_SUPPORTED 18.0) -else() - set(LLVM_MIN_SUPPORTED 16.0) -endif(CPPINTEROP_USE_CLING) set(LLVM_MAX_SUPPORTED "20.1.x") set(LLVM_VERSION_UPPER_BOUND 21.0.0) @@ -242,7 +230,7 @@ endif() message(STATUS "Found supported version: Clang ${CLANG_PACKAGE_VERSION}") message(STATUS "Using ClangConfig.cmake in: ${Clang_DIR}") - ## Clang 16 require c++17 or later. + ## Clang 18 require c++17 or later. if (NOT CMAKE_CXX_STANDARD) set (CMAKE_CXX_STANDARD 17) endif() @@ -286,10 +274,10 @@ endif() set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX}) set( CPPINTEROP_BUILT_STANDALONE 1 ) -endif() -include(AddLLVM) -include(HandleLLVMOptions) + include(AddLLVM) + include(HandleLLVMOptions) +endif() set(CMAKE_INCLUDE_CURRENT_DIR ON) diff --git a/interpreter/CppInterOp/Emscripten-build-instructions.md b/interpreter/CppInterOp/Emscripten-build-instructions.md index e41912bcc1c58..0854fed2dc6b0 100644 --- a/interpreter/CppInterOp/Emscripten-build-instructions.md +++ b/interpreter/CppInterOp/Emscripten-build-instructions.md @@ -63,9 +63,8 @@ On Windows execute the following ```powershell cd .\llvm-project\ cp -r ..\patches\llvm\emscripten-clang20* -cp -r ..\patches\llvm\Windows-emscripten-clang20* -git apply -v Windows-emscripten-clang20-1-CrossCompile.patch git apply -v emscripten-clang20-2-shift-temporary-files-to-tmp-dir.patch +git apply -v emscripten-clang20-3-enable_exception_handling.patch ``` We are now in a position to build an emscripten build of llvm by executing the following on Linux @@ -81,7 +80,6 @@ mkdir build cd build emcmake cmake -DCMAKE_BUILD_TYPE=Release \ -DLLVM_HOST_TRIPLE=wasm32-unknown-emscripten \ - -DLLVM_ENABLE_ASSERTIONS=ON \ -DLLVM_TARGETS_TO_BUILD="WebAssembly" \ -DLLVM_ENABLE_LIBEDIT=OFF \ -DLLVM_ENABLE_PROJECTS="clang;lld" \ @@ -99,6 +97,9 @@ emcmake cmake -DCMAKE_BUILD_TYPE=Release \ -DLLVM_ENABLE_LIBPFM=OFF \ -DCLANG_BUILD_TOOLS=OFF \ -DLLVM_NATIVE_TOOL_DIR=$NATIVE_DIR \ + -DCMAKE_C_FLAGS_RELEASE="-Oz -g0 -DNDEBUG" \ + -DCMAKE_CXX_FLAGS_RELEASE="-Oz -g0 -DNDEBUG" \ + -DLLVM_ENABLE_LTO=Full \ ../llvm emmake make libclang -j $(nproc --all) emmake make clangInterpreter clangStaticAnalyzerCore -j $(nproc --all) @@ -108,11 +109,17 @@ emmake make lldWasm -j $(nproc --all) or executing ```powershell +mkdir native_build +cd native_build +cmake -DLLVM_ENABLE_PROJECTS=clang -DLLVM_TARGETS_TO_BUILD=host -DCMAKE_BUILD_TYPE=Release -G Ninja ../llvm/ +cmake --build . --target llvm-tblgen clang-tblgen --parallel $(nproc --all) +$env:PWD_DIR= $PWD.Path +$env:NATIVE_DIR="$env:PWD_DIR/bin/" +cd .. mkdir build cd build emcmake cmake -DCMAKE_BUILD_TYPE=Release ` -DLLVM_HOST_TRIPLE=wasm32-unknown-emscripten ` - -DLLVM_ENABLE_ASSERTIONS=ON ` -DLLVM_TARGETS_TO_BUILD="WebAssembly" ` -DLLVM_ENABLE_LIBEDIT=OFF ` -DLLVM_ENABLE_PROJECTS="clang;lld" ` @@ -129,7 +136,11 @@ emcmake cmake -DCMAKE_BUILD_TYPE=Release ` -DLLVM_BUILD_TOOLS=OFF ` -DLLVM_ENABLE_LIBPFM=OFF ` -DCLANG_BUILD_TOOLS=OFF ` + -DLLVM_NATIVE_TOOL_DIR="$env:NATIVE_DIR" ` -G Ninja ` + -DCMAKE_C_FLAGS_RELEASE="-Oz -g0 -DNDEBUG" ` + -DCMAKE_CXX_FLAGS_RELEASE="-Oz -g0 -DNDEBUG" ` + -DLLVM_ENABLE_LTO=Full ` ..\llvm emmake ninja libclang clangInterpreter clangStaticAnalyzerCore lldWasm ``` @@ -208,14 +219,14 @@ emcmake cmake -DCMAKE_BUILD_TYPE=Release ` emmake make -j $(nproc --all) check-cppinterop ``` -It is possible to run the Emscripten tests in a headless browser on Linux and osx (in future we plan to include instructions on how to run the tests in a browser on Windows too). To do this we will first move to the tests directory +It is possible to run the Emscripten tests in a headless browser. To do this we will first move to the tests directory ```bash cd ./unittests/CppInterOp/ ``` -We will run our tests in a fresh installed browser. Installing the browsers, and running the tests within the installed browsers will be platform dependent. To do this on MacOS execute the following +We will run our tests in a fresh installed browser. Installing the browsers, and running the tests within the installed browsers will be platform dependent. To do this for Chrome and Firefox on MacOS execute the following ```bash wget "https://download.mozilla.org/?product=firefox-latest&os=osx&lang=en-US" -O Firefox-latest.dmg @@ -242,6 +253,19 @@ echo "Running DynamicLibraryManagerTests in Google Chrome" emrun --browser="Google Chrome" --kill_exit --timeout 60 --browser-args="--headless --no-sandbox" DynamicLibraryManagerTests.html ``` +To run tests in Safari you can make use of safaridriver. How to enable this will depend on +your MacOS operating system, and is best to consult [safaridriver](https://developer.apple.com/documentation/webkit/testing-with-webdriver-in-safari). You will also need to install the Selenium +python package. This only needs to be enable once, and then you can execute the following to run the tests in Safari + +```bash +echo "Running CppInterOpTests in Safari" +emrun --no_browser --kill_exit --timeout 60 --browser-args="--headless --no-sandbox" CppInterOpTests.html & +python ../../../scripts/browser_tests_safari.py CppInterOpTests.html +echo "Running DynamicLibraryManagerTests in Safari" +emrun --no_browser --kill_exit --timeout 60 --browser-args="--headless --no-sandbox" DynamicLibraryManagerTests.html & +python ../../../scripts/browser_tests_safari.py DynamicLibraryManagerTests.html +``` + To do this on Ubuntu x86 execute the following ```bash @@ -283,6 +307,26 @@ echo "Running DynamicLibraryManagerTests in Firefox" emrun --browser="firefox" --kill_exit --timeout 60 --browser-args="--headless" DynamicLibraryManagerTests.html ``` +To do this on Windows x86 execute the following + +```powershell +Invoke-WebRequest -Uri "https://commondatastorage.googleapis.com/chromium-browser-snapshots/Win/1411573/chrome-win.zip" -OutFile "$PWD\chrome-win.zip" -Verbose +Expand-Archive -Path "$PWD\chrome-win.zip" -DestinationPath "$PWD" -Force -Verbose +Invoke-WebRequest -Uri "https://download.mozilla.org/?product=firefox-latest-ssl&os=win64&lang=en-US" -OutFile "firefox-setup.exe" -Verbose +& "C:\Program Files\7-Zip\7z.exe" x "firefox-setup.exe" +$env:PATH="$PWD\core;$PWD\chrome-win;$env:PATH" +echo "PATH=$env:PATH" +echo "PATH=$env:PATH" >> $env:GITHUB_ENV +echo "Running CppInterOpTests in Firefox" +emrun.bat --browser="firefox.exe" --kill_exit --timeout 60 --browser-args="--headless" CppInterOpTests.html +echo "Running DynamicLibraryManagerTests in Firefox" +emrun.bat --browser="firefox.exe" --kill_exit --timeout 60 --browser-args="--headless" DynamicLibraryManagerTests.html +echo "Running CppInterOpTests in Chromium" +emrun.bat --browser="chrome.exe" --kill_exit --timeout 60 --browser-args="--headless --no-sandbox" CppInterOpTests.html +echo "Running DynamicLibraryManagerTests in Chromium" +emrun.bat --browser="chrome.exe" --kill_exit --timeout 60 --browser-args="--headless --no-sandbox" DynamicLibraryManagerTests.html +``` + Assuming it passes all test you can install by executing the following ```bash @@ -293,12 +337,13 @@ emmake make -j $(nproc --all) install ## Xeus-cpp-lite Wasm Build Instructions A project which makes use of the wasm build of CppInterOp is xeus-cpp. xeus-cpp is a C++ Jupyter kernel. Assuming you are in -the CppInterOp build folder, you can build the wasm version of xeus-cpp by executing (replace $LLVM_VERSION with the version +the CppInterOp build folder, you can build the wasm version of xeus-cpp by executing (replace LLVM_VERSION with the version of llvm you are building against) ```bash cd ../.. git clone --depth=1 https://github.com/compiler-research/xeus-cpp.git +export LLVM_VERSION=20 cd ./xeus-cpp mkdir build cd build @@ -308,19 +353,19 @@ emcmake cmake \ -DCMAKE_INSTALL_PREFIX=$PREFIX \ -DXEUS_CPP_EMSCRIPTEN_WASM_BUILD=ON \ -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ON \ - -DXEUS_CPP_RESOURCE_DIR=$LLVM_BUILD_DIR/lib/clang/$LLVM_VERSION \ + -DXEUS_CPP_RESOURCE_DIR="$LLVM_BUILD_DIR/lib/clang/$LLVM_VERSION" \ -DSYSROOT_PATH=$SYSROOT_PATH \ .. emmake make -j $(nproc --all) install ``` -To build Jupyter Lite website with this kernel locally that you can use for testing execute the following +To build and test Jupyter Lite with this kernel locally you can execute the following ```bash cd ../.. micromamba create -n xeus-lite-host jupyterlite-core=0.6 jupyterlite-xeus jupyter_server jupyterlab notebook python-libarchive-c -c conda-forge micromamba activate xeus-lite-host -jupyter lite build --XeusAddon.prefix=$PREFIX \ +jupyter lite serve --XeusAddon.prefix=$PREFIX \ --contents xeus-cpp/notebooks/xeus-cpp-lite-demo.ipynb \ --contents xeus-cpp/notebooks/smallpt.ipynb \ --contents xeus-cpp/notebooks/images/marie.png \ @@ -328,9 +373,3 @@ jupyter lite build --XeusAddon.prefix=$PREFIX \ --XeusAddon.mounts="$PREFIX/share/xeus-cpp/tagfiles:/share/xeus-cpp/tagfiles" \ --XeusAddon.mounts="$PREFIX/etc/xeus-cpp/tags.d:/etc/xeus-cpp/tags.d" ``` - -Once the Jupyter Lite site has built you can test the website locally by executing - -```bash -jupyter lite serve --XeusAddon.prefix=$PREFIX -``` diff --git a/interpreter/CppInterOp/README.md b/interpreter/CppInterOp/README.md index 534fc191e9175..e14a94d94eff2 100644 --- a/interpreter/CppInterOp/README.md +++ b/interpreter/CppInterOp/README.md @@ -82,9 +82,15 @@ adding speed and resilience with it. Build instructions for CppInterOp and its dependencies are as follows. CppInterOP can be built with either Cling and Clang-REPL, so instructions will differ slightly depending on which option you would like to build, but should be clear from the section title which instructions to follow. +
+Linux/MacOS + +
+Clang-REPL based CppInterOp + #### Clone CppInterOp and cppyy-backend -First clone the CppInterOp repository, as this contains patches that need to be applied to the subsequently cloned llvm-project repo (these patches are only applied if building CppInterOp with Clang-REPL) +First clone the CppInterOp repository, as this may contain patches that need to be applied to the subsequently cloned llvm-project repo ```bash git clone --depth=1 https://github.com/compiler-research/CppInterOp.git @@ -105,25 +111,10 @@ git clone --depth=1 --branch release/20.x https://github.com/llvm/llvm-project.g cd llvm-project ``` -For Clang 16 & 17, the following patches required for development work. To apply these patches on Linux and MacOS execute the following command(substitute `{version}` with your clang version): - -```bash -git apply -v ../CppInterOp/patches/llvm/clang{version}-*.patch -``` - -and - -```powershell -cp -r ..\CppInterOp\patches\llvm\clang{version}* . -git apply -v clang{version}-*.patch -``` - -on Windows. - ##### Build Clang-REPL Clang-REPL is an interpreter that CppInterOp works alongside. Build Clang (and -Clang-REPL along with it). On Linux and MacOS you do this by executing the following +Clang-REPL along with it) by executing command ```bash @@ -141,25 +132,7 @@ cmake -DLLVM_ENABLE_PROJECTS=clang \ cmake --build . --target clang clang-repl --parallel $(nproc --all) ``` -On Windows you would do this by executing the following - -```powershell -$env:ncpus = $([Environment]::ProcessorCount) -mkdir build -cd build -cmake -DLLVM_ENABLE_PROJECTS=clang ` - -DLLVM_TARGETS_TO_BUILD="host;NVPTX" ` - -DCMAKE_BUILD_TYPE=Release ` - -DLLVM_ENABLE_ASSERTIONS=ON ` - -DCLANG_ENABLE_STATIC_ANALYZER=OFF ` - -DCLANG_ENABLE_ARCMT=OFF ` - -DCLANG_ENABLE_FORMAT=OFF ` - -DCLANG_ENABLE_BOOTSTRAP=OFF ` - ..\llvm - cmake --build . --target clang clang-repl --parallel $env:ncpus -``` - -Note the 'llvm-project' directory location. On linux and MacOS you execute the following +Note the 'llvm-project' directory location by executing ```bash cd ../ @@ -167,20 +140,73 @@ export LLVM_DIR=$PWD cd ../ ``` -On Windows you execute the following +#### Environment variables -```powershell -cd ..\ -$env:LLVM_DIR= $PWD.Path -cd ..\ +You will need to define the following environment variables for the build of CppInterOp and cppyy (as they clear for a new session, it is recommended that you also add these to your .bashrc in linux, .bash_profile if on MacOS). On Linux and MacOS you define as follows + +```bash +export CB_PYTHON_DIR="$PWD/cppyy-backend/python" +export CPPINTEROP_DIR="$CB_PYTHON_DIR/cppyy_backend" +export CPLUS_INCLUDE_PATH="${CPLUS_INCLUDE_PATH}:${LLVM_DIR}/llvm/include:${LLVM_DIR}/clang/include:${LLVM_DIR}/build/include:${LLVM_DIR}/build/tools/clang/include" +``` + +If on MacOS you will also need the following environment variable defined + +```bash +export SDKROOT=`xcrun --show-sdk-path` +``` + +#### Build CppInterOp + +Now CppInterOp can be built. This can be done by executing + +```bash +mkdir CppInterOp/build/ +cd CppInterOp/build/ +cmake -DBUILD_SHARED_LIBS=ON -DCPPINTEROP_USE_CLING=ON -DCPPINTEROP_USE_REPL=Off -DCling_DIR=$LLVM_DIR/build/tools/cling -DLLVM_DIR=$LLVM_DIR/build/lib/cmake/llvm -DClang_DIR=$LLVM_DIR/build/lib/cmake/clang -DCMAKE_INSTALL_PREFIX=$CPPINTEROP_DIR .. +cmake --build . --target install --parallel $(nproc --all) +``` + +#### Testing CppInterOp + +To test the built CppInterOp execute the following command in the CppInterOP build folder on Linux and MacOS + +```bash +cmake --build . --target check-cppinterop --parallel $(nproc --all) +``` + +Now go back to the top level directory in which your building CppInterOP + +```bash +cd ../.. +``` + +Now you are in a position to install cppyy following the instructions below. + +
+ +
+Cling based CppInterOp + +#### Clone CppInterOp and cppyy-backend + +First clone the CppInterOp repository, as this may contain patches that need to be applied to the subsequently cloned llvm-project repo + +```bash +git clone --depth=1 https://github.com/compiler-research/CppInterOp.git +``` + +and clone cppyy-backend repository where we will be installing the CppInterOp library + +```bash +git clone --depth=1 https://github.com/compiler-research/cppyy-backend.git ``` #### Build Cling and related dependencies -Besides the Clang-REPL interpreter, CppInterOp also works alongside the Cling -interpreter. Cling depends on its own customised version of `llvm-project`, +The Cling interpreter and depends on its own customised version of `llvm-project`, hosted under the `root-project` (see the git path below). -Use the following build instructions to build on Linux and MacOS +Use the following build instructions to build ```bash git clone https://github.com/root-project/cling.git @@ -194,7 +220,7 @@ cd llvm-project/build cmake -DLLVM_ENABLE_PROJECTS=clang \ -DLLVM_EXTERNAL_PROJECTS=cling \ -DLLVM_EXTERNAL_CLING_SOURCE_DIR=../../cling \ - -DLLVM_TARGETS_TO_BUILD="host;NVPTX" \ + -DLLVM_TARGETS_TO_BUILD="host;NVPTX" \ -DCMAKE_BUILD_TYPE=Release \ -DLLVM_ENABLE_ASSERTIONS=ON \ -DCLANG_ENABLE_STATIC_ANALYZER=OFF \ @@ -206,36 +232,7 @@ cmake --build . --target clang --parallel $(nproc --all) cmake --build . --target cling --parallel $(nproc --all) ``` -Use the following build instructions to build on Windows - -```powershell -git clone https://github.com/root-project/cling.git -cd .\cling\ -git checkout tags/v1.2 -git apply -v ..\CppInterOp\patches\llvm\cling1.2-LookupHelper.patch -cd .. -git clone --depth=1 -b cling-llvm18 https://github.com/root-project/llvm-project.git -$env:ncpus = $([Environment]::ProcessorCount) -$env:PWD_DIR= $PWD.Path -$env:CLING_DIR="$env:PWD_DIR\cling" -mkdir llvm-project\build -cd llvm-project\build -cmake -DLLVM_ENABLE_PROJECTS=clang ` - -DLLVM_EXTERNAL_PROJECTS=cling ` - -DLLVM_EXTERNAL_CLING_SOURCE_DIR="$env:CLING_DIR" ` - -DLLVM_TARGETS_TO_BUILD="host;NVPTX" ` - -DCMAKE_BUILD_TYPE=Release ` - -DLLVM_ENABLE_ASSERTIONS=ON ` - -DCLANG_ENABLE_STATIC_ANALYZER=OFF ` - -DCLANG_ENABLE_ARCMT=OFF ` - -DCLANG_ENABLE_FORMAT=OFF ` - -DCLANG_ENABLE_BOOTSTRAP=OFF ` - ../llvm -cmake --build . --target clang --parallel $env:ncpus -cmake --build . --target cling --parallel $env:ncpus -``` - -Note the 'llvm-project' directory location. On linux and MacOS you execute the following +Note the 'llvm-project' directory location by executing the following ```bash cd ../ @@ -243,32 +240,13 @@ export LLVM_DIR=$PWD cd ../ ``` -On Windows you execute the following - -```powershell -cd ..\ -$env:LLVM_DIR= $PWD.Path -cd ..\ -``` - #### Environment variables -Regardless of whether you are building CppInterOP with Cling or Clang-REPL you will need to define the following environment variables (as they clear for a new session, it is recommended that you also add these to your .bashrc in linux, .bash_profile if on MacOS, or profile.ps1 on Windows). On Linux and MacOS you define as follows +You will need to define the following environment variables for the build of CppInterOp and cppyy (as they clear for a new session, it is recommended that you also add these to your .bashrc in linux, .bash_profile if on MacOS). On Linux and MacOS you define as follows ```bash export CB_PYTHON_DIR="$PWD/cppyy-backend/python" export CPPINTEROP_DIR="$CB_PYTHON_DIR/cppyy_backend" -``` - -If building CppInterOp against clang-repl you will need to define the following - -```bash -export CPLUS_INCLUDE_PATH="${CPLUS_INCLUDE_PATH}:${LLVM_DIR}/llvm/include:${LLVM_DIR}/clang/include:${LLVM_DIR}/build/include:${LLVM_DIR}/build/tools/clang/include" -``` - -and if building against cling you will need to define the following - -```bash export CLING_DIR="$(pwd)/cling" export CLING_BUILD_DIR="$(pwd)/cling/build" export CPLUS_INCLUDE_PATH="${CLING_DIR}/tools/cling/include:${CLING_BUILD_DIR}/include:${LLVM_DIR}/llvm/include:${LLVM_DIR}/clang/include:${LLVM_BUILD_DIR}/include:${LLVM_BUILD_DIR}/tools/clang/include:$PWD/include" @@ -280,73 +258,17 @@ If on MacOS you will also need the following environment variable defined export SDKROOT=`xcrun --show-sdk-path` ``` -On Windows you define as follows (assumes you have defined $env:PWD_DIR= $PWD.Path ) - -```powershell -$env:CB_PYTHON_DIR="$env:PWD_DIR\cppyy-backend\python" -$env:CPPINTEROP_DIR="$env:CB_PYTHON_DIR\cppyy_backend" -``` - -If building against clang-repl you will have the following defined - -```powershell -$env:CPLUS_INCLUDE_PATH="$env:CPLUS_INCLUDE_PATH;$env:LLVM_DIR\llvm\include;$env:LLVM_DIR\clang\include;$env:LLVM_DIR\build\include;$env:LLVM_DIR\build\tools\clang\include" -``` - -and if building against cling - -```powershell -$env:CLING_DIR="$env:PWD_DIR\cling" -$env:CLING_BUILD_DIR="$env:PWD_DIR\cling\build" -$env:CPLUS_INCLUDE_PATH="$env:CLING_DIR\tools\cling\include;$env:CLING_BUILD_DIR\include;$env:LLVM_DIR\llvm\include;$env:LLVM_DIR\clang\include;$env:LLVM_BUILD_DIR\include;$env:LLVM_BUILD_DIR\tools\clang\include;$env:PWD_DIR\include;" -``` - #### Build CppInterOp -Now CppInterOp can be installed. On Linux and MacOS execute +Now CppInterOp can be built. This can be done by executing ```bash mkdir CppInterOp/build/ cd CppInterOp/build/ -``` - -On Windows execute - -```powershell -mkdir CppInterOp\build\ -cd CppInterOp\build\ -``` - -Now if you want to build CppInterOp with Clang-REPL then execute the following commands on Linux and MacOS - -```bash -cmake -DBUILD_SHARED_LIBS=ON -DLLVM_DIR=$LLVM_DIR/build/lib/cmake/llvm -DClang_DIR=$LLVM_DIR/build/lib/cmake/clang -DCMAKE_INSTALL_PREFIX=$CPPINTEROP_DIR .. -cmake --build . --target install --parallel $(nproc --all) -``` - -and - -```powershell -cmake -DLLVM_DIR=$env:LLVM_DIR\build\lib\cmake\llvm -DClang_DIR=$env:LLVM_DIR\build\lib\cmake\clang -DCMAKE_INSTALL_PREFIX=$env:CPPINTEROP_DIR .. -cmake --build . --target install --parallel $env:ncpus -``` - -on Windows. If alternatively you would like to install CppInterOp with Cling then execute the following commands on Linux and MacOS - -```bash cmake -DBUILD_SHARED_LIBS=ON -DCPPINTEROP_USE_CLING=ON -DCPPINTEROP_USE_REPL=Off -DCling_DIR=$LLVM_DIR/build/tools/cling -DLLVM_DIR=$LLVM_DIR/build/lib/cmake/llvm -DClang_DIR=$LLVM_DIR/build/lib/cmake/clang -DCMAKE_INSTALL_PREFIX=$CPPINTEROP_DIR .. cmake --build . --target install --parallel $(nproc --all) ``` -and - -```powershell -cmake -DCPPINTEROP_USE_CLING=ON -DCPPINTEROP_USE_REPL=Off -DCling_DIR=$env:LLVM_DIR\build\tools\cling -DLLVM_DIR=$env:LLVM_DIR\build\lib\cmake\llvm -DClang_DIR=$env:LLVM_DIR\build\lib\cmake\clang -DCMAKE_INSTALL_PREFIX=$env:CPPINTEROP_DIR .. -cmake --build . --target install --parallel $env:ncpus -``` - -on Windows. - #### Testing CppInterOp To test the built CppInterOp execute the following command in the CppInterOP build folder on Linux and MacOS @@ -355,25 +277,15 @@ To test the built CppInterOp execute the following command in the CppInterOP bui cmake --build . --target check-cppinterop --parallel $(nproc --all) ``` -and - -```powershell -cmake --build . --target check-cppinterop --parallel $env:ncpus -``` - -on Windows. Now go back to the top level directory in which your building CppInterOP. On Linux and MacOS you do this by executing +Now go back to the top level directory in which your building CppInterOP ```bash cd ../.. ``` -and +Now you are in a position to install cppyy following the instructions below. -```powershell -cd ..\.. -``` - -on Windows. Now you are in a position to install cppyy following the instructions below. +
#### Building and Install cppyy-backend @@ -435,12 +347,6 @@ Export the `libcppyy` path to python: export PYTHONPATH=$PYTHONPATH:$CPYCPPYY_DIR:$CB_PYTHON_DIR ``` -and on Windows: - -```powershell -$env:PYTHONPATH="$env:PYTHONPATH;$env:CPYCPPYY_DIR;$env:CB_PYTHON_DIR" -``` - #### Install cppyy ```bash @@ -476,6 +382,192 @@ python -m pip install pytest python -m pytest -sv ``` +
+ +
+Windows + +
+Clang-REPL based CppInterOp + +#### Clone CppInterOp and cppyy-backend + +First clone the CppInterOp repository, as this may contain patches that need to be applied to the subsequently cloned llvm-project repo + +```bash +git clone --depth=1 https://github.com/compiler-research/CppInterOp.git +``` + +and clone cppyy-backend repository where we will be installing the CppInterOp library + +```bash +git clone --depth=1 https://github.com/compiler-research/cppyy-backend.git +``` + +#### Setup Clang-REPL + +Clone the 20.x release of the LLVM project repository. + +```bash +git clone --depth=1 --branch release/20.x https://github.com/llvm/llvm-project.git +cd llvm-project +``` + +##### Build Clang-REPL + +Clang-REPL is an interpreter that CppInterOp works alongside. Build Clang (and +Clang-REPL along with it) by executing the following + +```powershell +$env:ncpus = $([Environment]::ProcessorCount) +mkdir build +cd build +cmake -DLLVM_ENABLE_PROJECTS=clang ` + -DLLVM_TARGETS_TO_BUILD="host;NVPTX" ` + -DCMAKE_BUILD_TYPE=Release ` + -DLLVM_ENABLE_ASSERTIONS=ON ` + -DCLANG_ENABLE_STATIC_ANALYZER=OFF ` + -DCLANG_ENABLE_ARCMT=OFF ` + -DCLANG_ENABLE_FORMAT=OFF ` + -DCLANG_ENABLE_BOOTSTRAP=OFF ` + ..\llvm +cmake --build . --target clang clang-repl --parallel $env:ncpus +``` + +Note the 'llvm-project' directory location by executing the following + +```powershell +cd ..\ +$env:LLVM_DIR= $PWD.Path +cd ..\ +``` + +#### Environment variables + +You will need to define the following environment variables for the build of CppInterOp (as they clear for a new session, it is recommended that you also add these to your profile.ps1). You define as follows (assumes you have defined $env:PWD_DIR= $PWD.Path ) + +```powershell +$env:CB_PYTHON_DIR="$env:PWD_DIR\cppyy-backend\python" +$env:CPPINTEROP_DIR="$env:CB_PYTHON_DIR\cppyy_backend" +$env:CPLUS_INCLUDE_PATH="$env:CPLUS_INCLUDE_PATH;$env:LLVM_DIR\llvm\include;$env:LLVM_DIR\clang\include;$env:LLVM_DIR\build\include;$env:LLVM_DIR\build\tools\clang\include" +``` + +#### Build CppInterOp + +Now CppInterOp can be built. This can be done by executing + +```powershell +mkdir CppInterOp\build\ +cd CppInterOp\build\ +cmake -DLLVM_DIR=$env:LLVM_DIR\build\lib\cmake\llvm -DClang_DIR=$env:LLVM_DIR\build\lib\cmake\clang -DCMAKE_INSTALL_PREFIX=$env:CPPINTEROP_DIR .. +cmake --build . --target install --parallel $env:ncpus +``` + +#### Testing CppInterOp + +To test the built CppInterOp execute the following command in the CppInterOP build folder +and + +```powershell +cmake --build . --target check-cppinterop --parallel $env:ncpus +``` + +
+ +
+Cling based CppInterOp + +#### Clone CppInterOp and cppyy-backend + +First clone the CppInterOp repository, as this may contain patches that need to be applied to the subsequently cloned llvm-project repo + +```bash +git clone --depth=1 https://github.com/compiler-research/CppInterOp.git +``` + +and clone cppyy-backend repository where we will be installing the CppInterOp library + +```bash +git clone --depth=1 https://github.com/compiler-research/cppyy-backend.git +``` + +#### Build Cling and related dependencies + +The Cling interpreter and depends on its own customised version of `llvm-project`, +hosted under the `root-project` (see the git path below). +Use the following build instructions to build + +```powershell +git clone https://github.com/root-project/cling.git +cd .\cling\ +git checkout tags/v1.2 +git apply -v ..\CppInterOp\patches\llvm\cling1.2-LookupHelper.patch +cd .. +git clone --depth=1 -b cling-llvm18 https://github.com/root-project/llvm-project.git +$env:ncpus = $([Environment]::ProcessorCount) +$env:PWD_DIR= $PWD.Path +$env:CLING_DIR="$env:PWD_DIR\cling" +mkdir llvm-project\build +cd llvm-project\build +cmake -DLLVM_ENABLE_PROJECTS=clang ` + -DLLVM_EXTERNAL_PROJECTS=cling ` + -DLLVM_EXTERNAL_CLING_SOURCE_DIR="$env:CLING_DIR" ` + -DLLVM_TARGETS_TO_BUILD="host;NVPTX" ` + -DCMAKE_BUILD_TYPE=Release ` + -DLLVM_ENABLE_ASSERTIONS=ON ` + -DCLANG_ENABLE_STATIC_ANALYZER=OFF ` + -DCLANG_ENABLE_ARCMT=OFF ` + -DCLANG_ENABLE_FORMAT=OFF ` + -DCLANG_ENABLE_BOOTSTRAP=OFF ` + ../llvm +cmake --build . --target clang --parallel $env:ncpus +cmake --build . --target cling --parallel $env:ncpus +``` + +Note the 'llvm-project' directory location by executing the following + +```powershell +cd ..\ +$env:LLVM_DIR= $PWD.Path +cd ..\ +``` + +#### Environment variables + +You will need to define the following environment variables for the build of CppInterOp (as they clear for a new session, it is recommended that you also add these to your profile.ps1). You define as follows (assumes you have defined $env:PWD_DIR= $PWD.Path ) + +```powershell +$env:CB_PYTHON_DIR="$env:PWD_DIR\cppyy-backend\python" +$env:CPPINTEROP_DIR="$env:CB_PYTHON_DIR\cppyy_backend" +$env:CLING_DIR="$env:PWD_DIR\cling" +$env:CLING_BUILD_DIR="$env:PWD_DIR\cling\build" +$env:CPLUS_INCLUDE_PATH="$env:CLING_DIR\tools\cling\include;$env:CLING_BUILD_DIR\include;$env:LLVM_DIR\llvm\include;$env:LLVM_DIR\clang\include;$env:LLVM_BUILD_DIR\include;$env:LLVM_BUILD_DIR\tools\clang\include;$env:PWD_DIR\include;" +``` + +#### Build CppInterOp + +Now CppInterOp can be built. This can be done by executing + +```powershell +mkdir CppInterOp\build\ +cd CppInterOp\build\ +cmake -DCPPINTEROP_USE_CLING=ON -DCPPINTEROP_USE_REPL=Off -DCling_DIR=$env:LLVM_DIR\build\tools\cling -DLLVM_DIR=$env:LLVM_DIR\build\lib\cmake\llvm -DClang_DIR=$env:LLVM_DIR\build\lib\cmake\clang -DCMAKE_INSTALL_PREFIX=$env:CPPINTEROP_DIR .. +cmake --build . --target install --parallel $env:ncpus +``` + +#### Testing CppInterOp + +To test the built CppInterOp execute the following command in the CppInterOP build folder +and + +```powershell +cmake --build . --target check-cppinterop --parallel $env:ncpus +``` + +
+ +
+ ______________________________________________________________________ Further Reading: [C++ Language Interoperability Layer](https://compiler-research.org/libinterop/) diff --git a/interpreter/CppInterOp/cmake/modules/GoogleTest.cmake b/interpreter/CppInterOp/cmake/modules/GoogleTest.cmake index 4df2ff0474017..e6753c30c9787 100644 --- a/interpreter/CppInterOp/cmake/modules/GoogleTest.cmake +++ b/interpreter/CppInterOp/cmake/modules/GoogleTest.cmake @@ -20,20 +20,11 @@ endif() include(ExternalProject) if (EMSCRIPTEN) - if (CMAKE_C_COMPILER MATCHES ".bat") - set(config_cmd emcmake.bat cmake) - if(CMAKE_GENERATOR STREQUAL "Ninja") - set(build_cmd emmake.bat ninja) - else() - set(build_cmd emmake.bat make) - endif() + set(config_cmd emcmake${EMCC_SUFFIX} cmake) + if(CMAKE_GENERATOR STREQUAL "Ninja") + set(build_cmd emmake${EMCC_SUFFIX} ninja) else() - set(config_cmd emcmake cmake) - if(CMAKE_GENERATOR STREQUAL "Ninja") - set(build_cmd emmake ninja) - else() - set(build_cmd emmake make) - endif() + set(build_cmd emmake${EMCC_SUFFIX} make) endif() else() set(config_cmd ${CMAKE_COMMAND}) diff --git a/interpreter/CppInterOp/cppinterop-version.tag b/interpreter/CppInterOp/cppinterop-version.tag index f1be46f3d4668..bb4a3565c8603 100644 --- a/interpreter/CppInterOp/cppinterop-version.tag +++ b/interpreter/CppInterOp/cppinterop-version.tag @@ -1 +1 @@ -2585d0d2506d462ae85cb4afaf2e89c0e6a83ffb +33a3e7e7629c0818bd92420568671325b016d6b2 \ No newline at end of file diff --git a/interpreter/CppInterOp/docs/DevelopersDocumentation.rst b/interpreter/CppInterOp/docs/DevelopersDocumentation.rst index 2e997b5632315..0be22db9010f9 100644 --- a/interpreter/CppInterOp/docs/DevelopersDocumentation.rst +++ b/interpreter/CppInterOp/docs/DevelopersDocumentation.rst @@ -41,23 +41,6 @@ Clone the 20.x release of the LLVM project repository. git clone --depth=1 --branch release/20.x https://github.com/llvm/llvm-project.git cd llvm-project -For Clang 16 & 17, the following patches required for development work. To apply -these patches on Linux and MacOS execute the following command(substitute -`{version}` with your clang version): - -.. code:: bash - - git apply -v ../CppInterOp/patches/llvm/clang{version}-*.patch - -and - -.. code:: powershell - - cp -r ..\CppInterOp\patches\llvm\clang17* . - git apply -v clang{version}-*.patch - -on Windows. - ****************** Build Clang-REPL ****************** diff --git a/interpreter/CppInterOp/docs/Emscripten-build-instructions.rst b/interpreter/CppInterOp/docs/Emscripten-build-instructions.rst index 57362738221ba..89e9be0d2c1bb 100644 --- a/interpreter/CppInterOp/docs/Emscripten-build-instructions.rst +++ b/interpreter/CppInterOp/docs/Emscripten-build-instructions.rst @@ -82,9 +82,8 @@ On Windows execute the following cd .\llvm-project\ cp -r ..\patches\llvm\emscripten-clang20* - cp -r ..\patches\llvm\Windows-emscripten-clang20* - git apply -v Windows-emscripten-clang20-1-CrossCompile.patch git apply -v emscripten-clang20-2-shift-temporary-files-to-tmp-dir.patch + git apply -v emscripten-clang20-3-enable_exception_handling.patch We are now in a position to build an emscripten build of llvm by executing the following on Linux and osx @@ -101,7 +100,6 @@ and osx cd build emcmake cmake -DCMAKE_BUILD_TYPE=Release \ -DLLVM_HOST_TRIPLE=wasm32-unknown-emscripten \ - -DLLVM_ENABLE_ASSERTIONS=ON \ -DLLVM_TARGETS_TO_BUILD="WebAssembly" \ -DLLVM_ENABLE_LIBEDIT=OFF \ -DLLVM_ENABLE_PROJECTS="clang;lld" \ @@ -119,6 +117,9 @@ and osx -DLLVM_ENABLE_LIBPFM=OFF \ -DCLANG_BUILD_TOOLS=OFF \ -DLLVM_NATIVE_TOOL_DIR=$NATIVE_DIR \ + -DCMAKE_C_FLAGS_RELEASE="-Oz -g0 -DNDEBUG" \ + -DCMAKE_CXX_FLAGS_RELEASE="-Oz -g0 -DNDEBUG" \ + -DLLVM_ENABLE_LTO=Full \ ../llvm emmake make libclang -j $(nproc --all) emmake make clangInterpreter clangStaticAnalyzerCore -j $(nproc --all) @@ -128,11 +129,17 @@ or executing .. code:: powershell + mkdir native_build + cd native_build + cmake -DLLVM_ENABLE_PROJECTS=clang -DLLVM_TARGETS_TO_BUILD=host -DCMAKE_BUILD_TYPE=Release -G Ninja ../llvm/ + cmake --build . --target llvm-tblgen clang-tblgen --parallel $(nproc --all) + $env:PWD_DIR= $PWD.Path + $env:NATIVE_DIR="$env:PWD_DIR/bin/" + cd .. mkdir build cd build emcmake cmake -DCMAKE_BUILD_TYPE=Release ` -DLLVM_HOST_TRIPLE=wasm32-unknown-emscripten ` - -DLLVM_ENABLE_ASSERTIONS=ON ` -DLLVM_TARGETS_TO_BUILD="WebAssembly" ` -DLLVM_ENABLE_LIBEDIT=OFF ` -DLLVM_ENABLE_PROJECTS="clang;lld" ` @@ -149,7 +156,11 @@ or executing -DLLVM_BUILD_TOOLS=OFF ` -DLLVM_ENABLE_LIBPFM=OFF ` -DCLANG_BUILD_TOOLS=OFF ` + -DLLVM_NATIVE_TOOL_DIR="$env:NATIVE_DIR" ` -G Ninja ` + -DCMAKE_C_FLAGS_RELEASE="-Oz -g0 -DNDEBUG" ` + -DCMAKE_CXX_FLAGS_RELEASE="-Oz -g0 -DNDEBUG" ` + -DLLVM_ENABLE_LTO=Full ` ..\llvm emmake ninja libclang clangInterpreter clangStaticAnalyzerCore lldWasm @@ -232,13 +243,13 @@ To build and test your Emscripten build of CppInterOp on Windows execute the fol ..\ emmake make -j $(nproc --all) check-cppinterop -It is possible to run the Emscripten tests in a headless browser on Linux and osx (in future we plan to include instructions on how to run the tests in a browser on Windows too). To do this we will first move to the tests directory +It is possible to run the Emscripten tests in a headless browser. To do this we will first move to the tests directory .. code:: bash cd ./unittests/CppInterOp/ -We will run our tests in a fresh installed browser. Installing the browsers, and running the tests within the installed browsers will be platform dependent. To do this on MacOS execute the following +We will run our tests in a fresh installed browser. Installing the browsers, and running the tests within the installed browsers will be platform dependent. To do this for Chrome and Firefox on MacOS execute the following .. code:: bash @@ -265,6 +276,19 @@ We will run our tests in a fresh installed browser. Installing the browsers, and echo "Running DynamicLibraryManagerTests in Google Chrome" emrun --browser="Google Chrome" --kill_exit --timeout 60 --browser-args="--headless --no-sandbox" DynamicLibraryManagerTests.html +To run tests in Safari you can make use of safaridriver. How to enable this will depend on +your MacOS operating system, and is best to consult `safaridriver `_. You will also need to install the Selenium +python package. This only needs to be enable once, and then you can execute the following to run the tests in Safari + +.. code:: bash + + echo "Running CppInterOpTests in Safari" + emrun --no_browser --kill_exit --timeout 60 --browser-args="--headless --no-sandbox" CppInterOpTests.html & + python ../../../scripts/browser_tests_safari.py CppInterOpTests.html + echo "Running DynamicLibraryManagerTests in Safari" + emrun --no_browser --kill_exit --timeout 60 --browser-args="--headless --no-sandbox" DynamicLibraryManagerTests.html & + python ../../../scripts/browser_tests_safari.py DynamicLibraryManagerTests.html + To do this on Ubuntu x86 execute the following .. code:: bash @@ -306,6 +330,24 @@ so we currently only run the tests using Firefox on this platform, unlike other echo "Running DynamicLibraryManagerTests in Firefox" emrun --browser="firefox" --kill_exit --timeout 60 --browser-args="--headless" DynamicLibraryManagerTests.html +To do this on Windows x86 execute the following + +.. code:: powershell + + Invoke-WebRequest -Uri "https://commondatastorage.googleapis.com/chromium-browser-snapshots/Win/1411573/chrome-win.zip" -OutFile "$PWD\chrome-win.zip" -Verbose + Expand-Archive -Path "$PWD\chrome-win.zip" -DestinationPath "$PWD" -Force -Verbose + Invoke-WebRequest -Uri "https://download.mozilla.org/?product=firefox-latest-ssl&os=win64&lang=en-US" -OutFile "firefox-setup.exe" -Verbose + & "C:\Program Files\7-Zip\7z.exe" x "firefox-setup.exe" + $env:PATH="$PWD\core;$PWD\chrome-win;$env:PATH" + echo "Running CppInterOpTests in Firefox" + emrun.bat --browser="firefox.exe" --kill_exit --timeout 60 --browser-args="--headless" CppInterOpTests.html + echo "Running DynamicLibraryManagerTests in Firefox" + emrun.bat --browser="firefox.exe" --kill_exit --timeout 60 --browser-args="--headless" DynamicLibraryManagerTests.html + echo "Running CppInterOpTests in Chromium" + emrun.bat --browser="chrome.exe" --kill_exit --timeout 60 --browser-args="--headless --no-sandbox" CppInterOpTests.html + echo "Running DynamicLibraryManagerTests in Chromium" + emrun.bat --browser="chrome.exe" --kill_exit --timeout 60 --browser-args="--headless --no-sandbox" DynamicLibraryManagerTests.html + Assuming it passes all test you can install by executing the following. .. code:: bash @@ -317,12 +359,13 @@ Assuming it passes all test you can install by executing the following. A project which makes use of the wasm build of CppInterOp is xeus-cpp. xeus-cpp is a C++ Jupyter kernel. Assuming you are in the CppInterOp build folder, you can build the wasm version of xeus-cpp by executing -(replace $LLVM_VERSION with the version of llvm you are building against) +(replace LLVM_VERSION with the version of llvm you are building against) .. code:: bash cd ../.. git clone --depth=1 https://github.com/compiler-research/xeus-cpp.git + export LLVM_VERSION=20 cd ./xeus-cpp mkdir build cd build @@ -332,30 +375,22 @@ build folder, you can build the wasm version of xeus-cpp by executing -DCMAKE_INSTALL_PREFIX=$PREFIX \ -DXEUS_CPP_EMSCRIPTEN_WASM_BUILD=ON \ -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ON \ - -DXEUS_CPP_RESOURCE_DIR=$LLVM_BUILD_DIR/lib/clang/$LLVM_VERSION \ + -DXEUS_CPP_RESOURCE_DIR="$LLVM_BUILD_DIR/lib/clang/$LLVM_VERSION" \ -DSYSROOT_PATH=$SYSROOT_PATH \ .. emmake make -j $(nproc --all) install -To build Jupyter Lite website with this kernel locally that you can use -for testing execute the following +To build and test Jupyter Lite with this kernel locally you can execute the following .. code:: bash cd ../.. micromamba create -n xeus-lite-host jupyterlite-core=0.6 jupyterlite-xeus jupyter_server jupyterlab notebook python-libarchive-c -c conda-forge micromamba activate xeus-lite-host - jupyter lite build --XeusAddon.prefix=$PREFIX \ + jupyter lite serve --XeusAddon.prefix=$PREFIX \ --contents xeus-cpp/notebooks/xeus-cpp-lite-demo.ipynb \ --contents xeus-cpp/notebooks/smallpt.ipynb \ --contents xeus-cpp/notebooks/images/marie.png \ --contents xeus-cpp/notebooks/audio/audio.wav \ --XeusAddon.mounts="$PREFIX/share/xeus-cpp/tagfiles:/share/xeus-cpp/tagfiles" \ --XeusAddon.mounts="$PREFIX/etc/xeus-cpp/tags.d:/etc/xeus-cpp/tags.d" - -Once the Jupyter Lite site has built you can test the website locally by -executing - -.. code:: bash - - jupyter lite serve --XeusAddon.prefix=$PREFIX diff --git a/interpreter/CppInterOp/docs/InstallationAndUsage.rst b/interpreter/CppInterOp/docs/InstallationAndUsage.rst index 8063d9c6180db..3ec6470b44361 100644 --- a/interpreter/CppInterOp/docs/InstallationAndUsage.rst +++ b/interpreter/CppInterOp/docs/InstallationAndUsage.rst @@ -41,23 +41,6 @@ Clone the 20.x release of the LLVM project repository. git clone --depth=1 --branch release/20.x https://github.com/llvm/llvm-project.git cd llvm-project -For Clang 16 & 17, the following patches required for development work. To apply -these patches on Linux and MacOS execute the following command(substitute -`{version}` with your clang version): - -.. code:: bash - - git apply -v ../CppInterOp/patches/llvm/clang{version}-*.patch - -and - -.. code:: powershell - - cp -r ..\CppInterOp\patches\llvm\clang17* . - git apply -v clang{version}-*.patch - -on Windows. - ****************** Build Clang-REPL ****************** diff --git a/interpreter/CppInterOp/include/CppInterOp/CppInterOp.h b/interpreter/CppInterOp/include/CppInterOp/CppInterOp.h index 534a8f45ced51..bcbe3e5e1f9fe 100644 --- a/interpreter/CppInterOp/include/CppInterOp/CppInterOp.h +++ b/interpreter/CppInterOp/include/CppInterOp/CppInterOp.h @@ -94,6 +94,18 @@ enum Operator : unsigned char { enum OperatorArity : unsigned char { kUnary = 1, kBinary, kBoth }; +/// Enum modelling CVR qualifiers. +enum QualKind : unsigned char { + Const = 1 << 0, + Volatile = 1 << 1, + Restrict = 1 << 2 +}; + +inline QualKind operator|(QualKind a, QualKind b) { + return static_cast(static_cast(a) | + static_cast(b)); +} + /// A class modeling function calls for functions produced by the interpreter /// in compiled code. It provides an information if we are calling a standard /// function, constructor or destructor. @@ -309,6 +321,18 @@ CPPINTEROP_API bool IsEnumConstant(TCppScope_t handle); /// Checks if the passed value is an enum type or not. CPPINTEROP_API bool IsEnumType(TCppType_t type); +/// Checks if the passed type has qual Qualifiers +/// qual can be ORed value of enum QualKind +CPPINTEROP_API bool HasTypeQualifier(TCppType_t type, QualKind qual); + +/// Returns type with the qual Qualifiers removed +/// qual can be ORed value of enum QualKind +CPPINTEROP_API TCppType_t RemoveTypeQualifier(TCppType_t type, QualKind qual); + +/// Returns type with the qual Qualifiers added +/// qual can be ORed value of enum QualKind +CPPINTEROP_API TCppType_t AddTypeQualifier(TCppType_t type, QualKind qual); + /// Extracts enum declarations from a specified scope and stores them in /// vector CPPINTEROP_API void GetEnums(TCppScope_t scope, @@ -549,6 +573,9 @@ void GetEnumConstantDatamembers(TCppScope_t scope, CPPINTEROP_API TCppScope_t LookupDatamember(const std::string& name, TCppScope_t parent); +/// Check if the given type is a lamda class +CPPINTEROP_API bool IsLambdaClass(TCppType_t type); + /// Gets the type of the variable that is passed as a parameter. CPPINTEROP_API TCppType_t GetVariableType(TCppScope_t var); diff --git a/interpreter/CppInterOp/lib/CppInterOp/CMakeLists.txt b/interpreter/CppInterOp/lib/CppInterOp/CMakeLists.txt index a277b2b4941b9..d5670bca170a3 100644 --- a/interpreter/CppInterOp/lib/CppInterOp/CMakeLists.txt +++ b/interpreter/CppInterOp/lib/CppInterOp/CMakeLists.txt @@ -69,6 +69,9 @@ endif() continue() endif() foreach(transitive_lib ${transitive_libs}) + if(NOT TARGET ${transitive_lib}) + continue() + endif() get_target_property(lib_type ${transitive_lib} TYPE) if("${lib_type}" STREQUAL "STATIC_LIBRARY") list(APPEND static_transitive_libs ${transitive_lib}) @@ -127,10 +130,16 @@ if(EMSCRIPTEN) set_target_properties(clangCppInterOp PROPERTIES NO_SONAME 1 ) + target_compile_options(clangCppInterOp + PRIVATE "SHELL: -Oz" + PRIVATE "SHELL: -flto" + ) target_link_options(clangCppInterOp PRIVATE "SHELL: -s WASM_BIGINT" PRIVATE "SHELL: -s SIDE_MODULE=1" PRIVATE "SHELL: ${SYMBOLS_LIST}" + PRIVATE "SHELL: -Oz" + PRIVATE "SHELL: -flto" ) else() target_link_options(clangCppInterOp diff --git a/interpreter/CppInterOp/lib/CppInterOp/CXCppInterOp.cpp b/interpreter/CppInterOp/lib/CppInterOp/CXCppInterOp.cpp index bd9043d5b91a4..00ecdf4392a87 100644 --- a/interpreter/CppInterOp/lib/CppInterOp/CXCppInterOp.cpp +++ b/interpreter/CppInterOp/lib/CppInterOp/CXCppInterOp.cpp @@ -90,7 +90,6 @@ CXCursorKind cxcursor_getCursorKindForDecl(const Decl* D) { default: if (const auto* TD = dyn_cast(D)) { switch (TD->getTagKind()) { -#if CLANG_VERSION_MAJOR >= 18 case TagTypeKind::Interface: // fall through case TagTypeKind::Struct: return CXCursor_StructDecl; @@ -100,17 +99,6 @@ CXCursorKind cxcursor_getCursorKindForDecl(const Decl* D) { return CXCursor_UnionDecl; case TagTypeKind::Enum: return CXCursor_EnumDecl; -#else - case TagTypeKind::TTK_Interface: // fall through - case TagTypeKind::TTK_Struct: - return CXCursor_StructDecl; - case TagTypeKind::TTK_Class: - return CXCursor_ClassDecl; - case TagTypeKind::TTK_Union: - return CXCursor_UnionDecl; - case TagTypeKind::TTK_Enum: - return CXCursor_EnumDecl; -#endif } } } @@ -449,7 +437,7 @@ CXString clang_getTypeAsString(CXQualType type) { clang::PrintingPolicy Policy = C.getPrintingPolicy(); Policy.Bool = true; // Print bool instead of _Bool. Policy.SuppressTagKeyword = true; // Do not print `class std::string`. - return makeCXString(compat::FixTypeName(QT.getAsString(Policy))); + return makeCXString(QT.getAsString(Policy)); } CXQualType clang_getComplexType(CXQualType eltype) { diff --git a/interpreter/CppInterOp/lib/CppInterOp/Compatibility.h b/interpreter/CppInterOp/lib/CppInterOp/Compatibility.h index 0c7c66f5bf334..24c822decd0f9 100644 --- a/interpreter/CppInterOp/lib/CppInterOp/Compatibility.h +++ b/interpreter/CppInterOp/lib/CppInterOp/Compatibility.h @@ -5,9 +5,13 @@ #ifndef CPPINTEROP_COMPATIBILITY_H #define CPPINTEROP_COMPATIBILITY_H +#include "clang/AST/DeclTemplate.h" #include "clang/AST/GlobalDecl.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/Specifiers.h" #include "clang/Basic/Version.h" #include "clang/Config/config.h" +#include "clang/Sema/Sema.h" #ifdef _MSC_VER #define dup _dup @@ -59,14 +63,7 @@ static inline char* GetEnv(const char* Var_Name) { CXXSpecialMemberKind::MoveConstructor #endif -#if LLVM_VERSION_MAJOR < 18 -#define starts_with startswith -#define ends_with endswith -#endif - -#if CLANG_VERSION_MAJOR >= 18 #include "clang/Interpreter/CodeCompletion.h" -#endif #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" @@ -342,7 +339,6 @@ inline void codeComplete(std::vector& Results, clang::Interpreter& I, const char* code, unsigned complete_line = 1U, unsigned complete_column = 1U) { -#if CLANG_VERSION_MAJOR >= 18 // FIXME: We should match the invocation arguments of the main interpreter. // That can affect the returned completion results. auto CB = clang::IncrementalCompilerBuilder(); @@ -358,7 +354,6 @@ inline void codeComplete(std::vector& Results, } std::vector results; - std::vector Comps; clang::CompilerInstance* MainCI = (*Interp)->getCompilerInstance(); auto CC = clang::ReplCodeCompleter(); CC.codeComplete(MainCI, code, complete_line, complete_column, @@ -366,9 +361,6 @@ inline void codeComplete(std::vector& Results, for (llvm::StringRef r : results) if (r.find(CC.Prefix) == 0) Results.push_back(r.str()); -#else - assert(false && "CodeCompletion API only available in Clang >= 18."); -#endif } } // namespace compat @@ -401,18 +393,6 @@ class SynthesizingCodeRAII { namespace compat { -// Clang >= 14 change type name to string (spaces formatting problem) -inline std::string FixTypeName(const std::string type_name) { - return type_name; -} - -inline std::string MakeResourceDir(llvm::StringRef Dir) { - llvm::SmallString<128> P(Dir); - llvm::sys::path::append(P, CLANG_INSTALL_LIBDIR_BASENAME, "clang", - CLANG_VERSION_MAJOR_STRING); - return std::string(P.str()); -} - // Clang >= 16 (=16 with Value patch) change castAs to convertTo #ifdef CPPINTEROP_USE_CLING template inline T convertTo(cling::Value V) { @@ -424,6 +404,22 @@ template inline T convertTo(clang::Value V) { } #endif // CPPINTEROP_USE_CLING +inline void InstantiateClassTemplateSpecialization( + Interpreter& interp, clang::ClassTemplateSpecializationDecl* CTSD) { +#ifdef CPPINTEROP_USE_CLING + cling::Interpreter::PushTransactionRAII RAII(&interp); +#endif +#if CLANG_VERSION_MAJOR < 20 + interp.getSema().InstantiateClassTemplateSpecialization( + clang::SourceLocation::getFromRawEncoding(1), CTSD, + clang::TemplateSpecializationKind::TSK_Undeclared, /*Complain=*/true); +#else + interp.getSema().InstantiateClassTemplateSpecialization( + clang::SourceLocation::getFromRawEncoding(1), CTSD, + clang::TemplateSpecializationKind::TSK_Undeclared, /*Complain=*/true, + /*PrimaryHasMatchedPackOnParmToNonPackOnArg=*/false); +#endif +} } // namespace compat #endif // CPPINTEROP_COMPATIBILITY_H diff --git a/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp b/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp index b4dd7ad74a411..b6cf555e8d524 100644 --- a/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp +++ b/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp @@ -11,6 +11,7 @@ #include "Compatibility.h" +#include "clang/AST/Attrs.inc" #include "clang/AST/CXXInheritance.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclAccessPair.h" @@ -43,14 +44,19 @@ #endif #include "clang/Sema/TemplateDeduction.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Demangle/Demangle.h" +#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" +#include "llvm/IR/GlobalValue.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/Error.h" #include "llvm/Support/ManagedStatic.h" -#include "llvm/Support/raw_os_ostream.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" #include #include @@ -63,6 +69,7 @@ #include #include #include +#include // Stream redirect. #ifdef _WIN32 @@ -133,6 +140,42 @@ static compat::Interpreter& getInterp() { static clang::Sema& getSema() { return getInterp().getCI()->getSema(); } static clang::ASTContext& getASTContext() { return getSema().getASTContext(); } +static void ForceCodeGen(Decl* D, compat::Interpreter& I) { + // The decl was deferred by CodeGen. Force its emission. + // FIXME: In ASTContext::DeclMustBeEmitted we should check if the + // Decl::isUsed is set or we should be able to access CodeGen's + // addCompilerUsedGlobal. + ASTContext& C = I.getSema().getASTContext(); + + D->addAttr(UsedAttr::CreateImplicit(C)); +#ifdef CPPINTEROP_USE_CLING + cling::Interpreter::PushTransactionRAII RAII(&I); + I.getCI()->getASTConsumer().HandleTopLevelDecl(DeclGroupRef(D)); +#else // CLANG_REPL + I.getCI()->getASTConsumer().HandleTopLevelDecl(DeclGroupRef(D)); + // Take the newest llvm::Module produced by CodeGen and send it to JIT. + auto GeneratedPTU = I.Parse(""); + if (!GeneratedPTU) + llvm::logAllUnhandledErrors(GeneratedPTU.takeError(), llvm::errs(), + "[ForceCodeGen] Failed to generate PTU:"); + + // From cling's BackendPasses.cpp + // FIXME: We need to upstream this code in IncrementalExecutor::addModule + for (auto& GV : GeneratedPTU->TheModule->globals()) { + llvm::GlobalValue::LinkageTypes LT = GV.getLinkage(); + if (GV.isDeclaration() || !GV.hasName() || + GV.getName().starts_with(".str") || + !llvm::GlobalVariable::isDiscardableIfUnused(LT) || + LT != llvm::GlobalValue::InternalLinkage) + continue; // nothing to do + GV.setLinkage(llvm::GlobalValue::WeakAnyLinkage); + } + if (auto Err = I.Execute(*GeneratedPTU)) + llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), + "[ForceCodeGen] Failed to execute PTU:"); +#endif +} + #define DEBUG_TYPE "jitcall" bool JitCall::AreArgumentsValid(void* result, ArgList args, void* self, size_t nary) const { @@ -215,20 +258,10 @@ std::string GetVersion() { } std::string Demangle(const std::string& mangled_name) { -#if CLANG_VERSION_MAJOR > 16 #ifdef _WIN32 std::string demangle = microsoftDemangle(mangled_name, nullptr, nullptr); #else std::string demangle = itaniumDemangle(mangled_name); -#endif -#else -#ifdef _WIN32 - std::string demangle = microsoftDemangle(mangled_name.c_str(), nullptr, - nullptr, nullptr, nullptr); -#else - std::string demangle = - itaniumDemangle(mangled_name.c_str(), nullptr, nullptr, nullptr); -#endif #endif return demangle; } @@ -526,18 +559,28 @@ std::string GetCompleteName(TCppType_t klass) { auto& C = getSema().getASTContext(); auto* D = (Decl*)klass; + PrintingPolicy Policy = C.getPrintingPolicy(); + Policy.SuppressUnwrittenScope = true; + Policy.SuppressScope = true; + Policy.AnonymousTagLocations = false; + Policy.SuppressTemplateArgsInCXXConstructors = false; + Policy.SuppressDefaultTemplateArgs = false; + Policy.AlwaysIncludeTypeForTemplateArgument = true; + if (auto* ND = llvm::dyn_cast_or_null(D)) { if (auto* TD = llvm::dyn_cast(ND)) { std::string type_name; QualType QT = C.getTagDeclType(TD); - PrintingPolicy Policy = C.getPrintingPolicy(); - Policy.SuppressUnwrittenScope = true; - Policy.SuppressScope = true; - Policy.AnonymousTagLocations = false; QT.getAsStringInternal(type_name, Policy); - return type_name; } + if (auto* FD = llvm::dyn_cast(ND)) { + std::string func_name; + llvm::raw_string_ostream name_stream(func_name); + FD->getNameForDiagnostic(name_stream, Policy, false); + name_stream.flush(); + return func_name; + } return ND->getNameAsString(); } @@ -574,9 +617,7 @@ std::string GetQualifiedCompleteName(TCppType_t klass) { PrintingPolicy PP = C.getPrintingPolicy(); PP.FullyQualifiedName = true; PP.SuppressUnwrittenScope = true; -#if CLANG_VERSION_MAJOR > 16 PP.SuppressElaboration = true; -#endif QT.getAsStringInternal(type_name, PP); return type_name; @@ -616,7 +657,9 @@ static Decl* GetScopeFromType(QualType QT) { Type = Type->getUnqualifiedDesugaredType(); if (auto* ET = llvm::dyn_cast(Type)) return ET->getDecl(); - return Type->getAsCXXRecordDecl(); + CXXRecordDecl* CXXRD = Type->getAsCXXRecordDecl(); + if (CXXRD) + return CXXRD->getCanonicalDecl(); } return 0; } @@ -635,7 +678,7 @@ static clang::Decl* GetUnderlyingScope(clang::Decl* D) { D = Scope; } - return D; + return D->getCanonicalDecl(); } TCppScope_t GetUnderlyingScope(TCppScope_t scope) { @@ -717,6 +760,9 @@ TCppScope_t GetParentScope(TCppScope_t scope) { TCppIndex_t GetNumBases(TCppScope_t klass) { auto* D = (Decl*)klass; + if (auto* CTSD = llvm::dyn_cast_or_null(D)) + if (!CTSD->hasDefinition()) + compat::InstantiateClassTemplateSpecialization(getInterp(), CTSD); if (auto* CXXRD = llvm::dyn_cast_or_null(D)) { if (CXXRD->hasDefinition()) return CXXRD->getNumBases(); @@ -844,13 +890,36 @@ static void GetClassDecls(TCppScope_t klass, #ifdef CPPINTEROP_USE_CLING cling::Interpreter::PushTransactionRAII RAII(&getInterp()); #endif // CPPINTEROP_USE_CLING + if (CXXRD->hasDefinition()) + CXXRD = CXXRD->getDefinition(); getSema().ForceDeclarationOfImplicitMembers(CXXRD); for (Decl* DI : CXXRD->decls()) { if (auto* MD = dyn_cast(DI)) methods.push_back(MD); - else if (auto* USD = dyn_cast(DI)) - if (auto* MD = dyn_cast(USD->getTargetDecl())) + else if (auto* USD = dyn_cast(DI)) { + auto* MD = dyn_cast(USD->getTargetDecl()); + if (!MD) + continue; + + auto* CUSD = dyn_cast(DI); + if (!CUSD) { methods.push_back(MD); + continue; + } + + auto* CXXCD = dyn_cast_or_null(CUSD->getTargetDecl()); + if (!CXXCD) { + methods.push_back(MD); + continue; + } + if (CXXCD->isDeleted()) + continue; + + // Result is appended to the decls, i.e. CXXRD, iterator + // non-shadowed decl will be push_back later + // methods.push_back(Result); + getSema().findInheritingConstructor(SourceLocation(), CXXCD, CUSD); + } } } @@ -1306,8 +1375,16 @@ static TCppFuncAddr_t GetFunctionAddress(const FunctionDecl* FD) { TCppFuncAddr_t GetFunctionAddress(TCppFunction_t method) { auto* D = static_cast(method); - if (auto* FD = llvm::dyn_cast_or_null(D)) + if (auto* FD = llvm::dyn_cast_or_null(D)) { + if ((IsTemplateInstantiationOrSpecialization(FD) || + FD->getTemplatedKind() == FunctionDecl::TK_MemberSpecialization) && + !FD->getDefinition()) + InstantiateFunctionDefinition(D); + ASTContext& C = getASTContext(); + if (isDiscardableGVALinkage(C.GetGVALinkageForFunction(FD))) + ForceCodeGen(FD, getInterp()); return GetFunctionAddress(FD); + } return nullptr; } @@ -1400,6 +1477,14 @@ TCppScope_t LookupDatamember(const std::string& name, TCppScope_t parent) { return 0; } +bool IsLambdaClass(TCppType_t type) { + QualType QT = QualType::getFromOpaquePtr(type); + if (auto* CXXRD = QT->getAsCXXRecordDecl()) { + return CXXRD->isLambda(); + } + return false; +} + TCppType_t GetVariableType(TCppScope_t var) { auto* D = static_cast(var); @@ -1504,6 +1589,7 @@ intptr_t GetVariableOffset(compat::Interpreter& I, Decl* D, cling::Interpreter::PushTransactionRAII RAII(&getInterp()); #endif // CPPINTEROP_USE_CLING getSema().InstantiateVariableDefinition(SourceLocation(), VD); + VD = VD->getDefinition(); } if (VD->hasInit() && (VD->isConstexpr() || VD->getType().isConstQualified())) { @@ -1516,39 +1602,8 @@ intptr_t GetVariableOffset(compat::Interpreter& I, Decl* D, } if (!address) { auto Linkage = C.GetGVALinkageForVariable(VD); - // The decl was deferred by CodeGen. Force its emission. - // FIXME: In ASTContext::DeclMustBeEmitted we should check if the - // Decl::isUsed is set or we should be able to access CodeGen's - // addCompilerUsedGlobal. if (isDiscardableGVALinkage(Linkage)) - VD->addAttr(UsedAttr::CreateImplicit(C)); -#ifdef CPPINTEROP_USE_CLING - cling::Interpreter::PushTransactionRAII RAII(&I); - I.getCI()->getASTConsumer().HandleTopLevelDecl(DeclGroupRef(VD)); -#else // CLANG_REPL - I.getCI()->getASTConsumer().HandleTopLevelDecl(DeclGroupRef(VD)); - // Take the newest llvm::Module produced by CodeGen and send it to JIT. - auto GeneratedPTU = I.Parse(""); - if (!GeneratedPTU) - llvm::logAllUnhandledErrors( - GeneratedPTU.takeError(), llvm::errs(), - "[GetVariableOffset] Failed to generate PTU:"); - - // From cling's BackendPasses.cpp - // FIXME: We need to upstream this code in IncrementalExecutor::addModule - for (auto& GV : GeneratedPTU->TheModule->globals()) { - llvm::GlobalValue::LinkageTypes LT = GV.getLinkage(); - if (GV.isDeclaration() || !GV.hasName() || - GV.getName().starts_with(".str") || !GV.isDiscardableIfUnused(LT) || - LT != llvm::GlobalValue::InternalLinkage) - continue; // nothing to do - GV.setLinkage(llvm::GlobalValue::WeakAnyLinkage); - } - if (auto Err = I.Execute(*GeneratedPTU)) - llvm::logAllUnhandledErrors( - std::move(Err), llvm::errs(), - "[GetVariableOffset] Failed to execute PTU:"); -#endif + ForceCodeGen(VD, I); } auto VDAorErr = compat::getSymbolAddress(I, StringRef(mangledName)); if (!VDAorErr) { @@ -1689,11 +1744,9 @@ std::string GetTypeAsString(TCppType_t var) { PrintingPolicy Policy((LangOptions())); Policy.Bool = true; // Print bool instead of _Bool. Policy.SuppressTagKeyword = true; // Do not print `class std::string`. -#if CLANG_VERSION_MAJOR > 16 Policy.SuppressElaboration = true; -#endif Policy.FullyQualifiedName = true; - return compat::FixTypeName(QT.getAsString(Policy)); + return QT.getAsString(Policy); } TCppType_t GetCanonicalType(TCppType_t type) { @@ -1703,6 +1756,60 @@ TCppType_t GetCanonicalType(TCppType_t type) { return QT.getCanonicalType().getAsOpaquePtr(); } +bool HasTypeQualifier(TCppType_t type, QualKind qual) { + if (!type) + return false; + + QualType QT = QualType::getFromOpaquePtr(type); + if (qual & QualKind::Const) { + if (!QT.isConstQualified()) + return false; + } + if (qual & QualKind::Volatile) { + if (!QT.isVolatileQualified()) + return false; + } + if (qual & QualKind::Restrict) { + if (!QT.isRestrictQualified()) + return false; + } + return true; +} + +TCppType_t RemoveTypeQualifier(TCppType_t type, QualKind qual) { + if (!type) + return type; + + auto QT = QualType(QualType::getFromOpaquePtr(type)); + if (qual & QualKind::Const) + QT.removeLocalConst(); + if (qual & QualKind::Volatile) + QT.removeLocalVolatile(); + if (qual & QualKind::Restrict) + QT.removeLocalRestrict(); + return QT.getAsOpaquePtr(); +} + +TCppType_t AddTypeQualifier(TCppType_t type, QualKind qual) { + if (!type) + return type; + + auto QT = QualType(QualType::getFromOpaquePtr(type)); + if (qual & QualKind::Const) { + if (!QT.isConstQualified()) + QT.addConst(); + } + if (qual & QualKind::Volatile) { + if (!QT.isVolatileQualified()) + QT.addVolatile(); + } + if (qual & QualKind::Restrict) { + if (!QT.isRestrictQualified()) + QT.addRestrict(); + } + return QT.getAsOpaquePtr(); +} + // Internal functions that are not needed outside the library are // encompassed in an anonymous namespace as follows. This function converts // from a string to the actual type. It is used in the GetType() function. @@ -1834,11 +1941,10 @@ void get_type_as_string(QualType QT, std::string& type_name, ASTContext& C, // cling::utils::Transform::GetPartiallyDesugaredType() if (!QT->isTypedefNameType() || QT->isBuiltinType()) QT = QT.getDesugaredType(C); -#if CLANG_VERSION_MAJOR > 16 Policy.SuppressElaboration = true; -#endif Policy.SuppressTagKeyword = !QT->isEnumeralType(); Policy.FullyQualifiedName = true; + Policy.UsePreferredNames = false; QT.getAsStringInternal(type_name, Policy); } @@ -1848,6 +1954,7 @@ static void GetDeclName(const clang::Decl* D, ASTContext& Context, PrintingPolicy Policy(Context.getPrintingPolicy()); Policy.SuppressTagKeyword = true; Policy.SuppressUnwrittenScope = true; + Policy.PrintCanonicalTypes = true; if (const TypeDecl* TD = dyn_cast(D)) { // This is a class, struct, or union member. QualType QT; @@ -1876,13 +1983,26 @@ void collect_type_info(const FunctionDecl* FD, QualType& QT, // ASTContext& C = FD->getASTContext(); PrintingPolicy Policy(C.getPrintingPolicy()); -#if CLANG_VERSION_MAJOR > 16 Policy.SuppressElaboration = true; -#endif refType = kNotReference; - if (QT->isRecordType() && forArgument) { - get_type_as_string(QT, type_name, C, Policy); - return; + if (QT->isRecordType()) { + if (forArgument) { + get_type_as_string(QT, type_name, C, Policy); + return; + } + if (auto* CXXRD = QT->getAsCXXRecordDecl()) { + if (CXXRD->isLambda()) { + std::string fn_name; + llvm::raw_string_ostream stream(fn_name); + Policy.FullyQualifiedName = true; + Policy.SuppressUnwrittenScope = true; + FD->getNameForDiagnostic(stream, Policy, + /*Qualified=*/false); + type_name = "__internal_CppInterOp::function::result_type"; + return; + } + } } if (QT->isFunctionPointerType()) { std::string fp_typedef_name; @@ -2092,28 +2212,17 @@ void make_narg_call(const FunctionDecl* FD, const std::string& return_type, PrintingPolicy PP = FD->getASTContext().getPrintingPolicy(); PP.FullyQualifiedName = true; PP.SuppressUnwrittenScope = true; -#if CLANG_VERSION_MAJOR > 16 PP.SuppressElaboration = true; -#endif FD->getNameForDiagnostic(stream, PP, /*Qualified=*/false); - - // insert space between template argument list and the function name - // this is require if the function is `operator<` - // `operator<` is invalid syntax - // whereas `operator< ` is valid - std::string simple_name = FD->getNameAsString(); - size_t idx = complete_name.find(simple_name, 0) + simple_name.size(); - std::string name_without_template_args = complete_name.substr(0, idx); - std::string template_args = complete_name.substr(idx); - name = name_without_template_args + - (template_args.empty() ? "" : " " + template_args); + name = complete_name; // If a template has consecutive parameter packs, then it is impossible to // use the explicit name in the wrapper, since the type deduction is what // determines the split of the packs. Instead, we'll revert to the // non-templated function name and hope that the type casts in the wrapper // will suffice. + std::string simple_name = FD->getNameAsString(); if (FD->isTemplateInstantiation() && FD->getPrimaryTemplate()) { const FunctionTemplateDecl* FTDecl = llvm::dyn_cast(FD->getPrimaryTemplate()); @@ -2128,10 +2237,12 @@ void make_narg_call(const FunctionDecl* FD, const std::string& return_type, numPacks = 0; } if (numPacks > 1) { - name = name_without_template_args; + name = simple_name; } } } + if (FD->isOverloadedOperator()) + name = simple_name; } if (op_flag || N <= 1) callbuf << name; @@ -2160,12 +2271,33 @@ void make_narg_call(const FunctionDecl* FD, const std::string& return_type, } } + CXXRecordDecl* rtdecl = QT->getAsCXXRecordDecl(); if (refType != kNotReference) { callbuf << "(" << type_name.c_str() << (refType == kLValueReference ? "&" : "&&") << ")*(" << type_name.c_str() << "*)args[" << i << "]"; } else if (isPointer) { callbuf << "*(" << type_name.c_str() << "**)args[" << i << "]"; + } else if (rtdecl && + (rtdecl->hasTrivialCopyConstructor() && + !rtdecl->hasSimpleCopyConstructor()) && + rtdecl->hasMoveConstructor()) { + // By-value construction; this may either copy or move, but there is no + // information here in terms of intent. Thus, simply assume that the + // intent is to move if there is no viable copy constructor (ie. if the + // code would otherwise fail to even compile). There does not appear to be + // a simple way of determining whether a viable copy constructor exists, + // so check for the most common case: the trivial one, but not uniquely + // available, while there is a move constructor. + + // include utility header if not already included for std::move + DeclarationName DMove = &getASTContext().Idents.get("move"); + auto result = getSema().getStdNamespace()->lookup(DMove); + if (result.empty()) + Cpp::Declare("#include "); + + // move construction as needed for classes (note that this is implicit) + callbuf << "std::move(*(" << type_name.c_str() << "*)args[" << i << "])"; } else { // pointer falls back to non-pointer case; the argument preserves // the "pointerness" (i.e. doesn't reference the value). @@ -3055,7 +3187,10 @@ static std::string MakeResourcesPath() { Dir = sys::path::parent_path(Dir); // Dir = sys::path::parent_path(Dir); #endif // LLVM_BINARY_DIR - return compat::MakeResourceDir(Dir); + llvm::SmallString<128> P(Dir); + llvm::sys::path::append(P, CLANG_INSTALL_LIBDIR_BASENAME, "clang", + CLANG_VERSION_MAJOR_STRING); + return std::string(P.str()); } } // namespace @@ -3125,6 +3260,17 @@ TInterp_t CreateInterpreter(const std::vector& Args /*={}*/, llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get()); } + I->declare(R"( + namespace __internal_CppInterOp { + template + struct function; + template + struct function { + typedef Res result_type; + }; + } // namespace __internal_CppInterOp + )"); + sInterpreters->emplace_back(I, /*Owned=*/true); return I; @@ -3353,11 +3499,7 @@ bool InsertOrReplaceJitSymbol(compat::Interpreter& I, auto Symbol = compat::getSymbolAddress(I, linker_mangled_name); llvm::orc::LLJIT& Jit = *compat::getExecutionEngine(I); llvm::orc::ExecutionSession& ES = Jit.getExecutionSession(); -#if CLANG_VERSION_MAJOR < 17 - JITDylib& DyLib = Jit.getMainJITDylib(); -#else JITDylib& DyLib = *Jit.getProcessSymbolsJITDylib().get(); -#endif // CLANG_VERSION_MAJOR if (Error Err = Symbol.takeError()) { logAllUnhandledErrors(std::move(Err), errs(), @@ -3385,12 +3527,7 @@ bool InsertOrReplaceJitSymbol(compat::Interpreter& I, } auto Name = ES.intern(tmp); InjectedSymbols[Name] = -#if CLANG_VERSION_MAJOR < 17 - JITEvaluatedSymbol(address, -#else - ExecutorSymbolDef(ExecutorAddr(address), -#endif // CLANG_VERSION_MAJOR < 17 - JITSymbolFlags::Exported); + ExecutorSymbolDef(ExecutorAddr(address), JITSymbolFlags::Exported); // We want to replace a symbol with a custom provided one. if (Symbol && address) @@ -3663,7 +3800,11 @@ std::string GetFunctionArgDefault(TCppFunction_t func, if (PI->hasDefaultArg()) { std::string Result; llvm::raw_string_ostream OS(Result); - Expr* DefaultArgExpr = const_cast(PI->getDefaultArg()); + Expr* DefaultArgExpr = nullptr; + if (PI->hasUninstantiatedDefaultArg()) + DefaultArgExpr = PI->getUninstantiatedDefaultArg(); + else + DefaultArgExpr = PI->getDefaultArg(); DefaultArgExpr->printPretty(OS, nullptr, PrintingPolicy(LangOptions())); // FIXME: Floats are printed in clang with the precision of their underlying diff --git a/interpreter/CppInterOp/lib/CppInterOp/DynamicLibraryManager.cpp b/interpreter/CppInterOp/lib/CppInterOp/DynamicLibraryManager.cpp index 2a90300e42bbb..df579aa2cccf8 100644 --- a/interpreter/CppInterOp/lib/CppInterOp/DynamicLibraryManager.cpp +++ b/interpreter/CppInterOp/lib/CppInterOp/DynamicLibraryManager.cpp @@ -145,19 +145,22 @@ std::string DynamicLibraryManager::lookupLibInPaths( LLVM_DEBUG(dbgs() << "Dyld::lookupLibInPaths: \n"); LLVM_DEBUG(dbgs() << ":: RPATH\n"); +#ifndef NDEBUG for (auto Info : RPath) { LLVM_DEBUG(dbgs() << ":::: " << Info.str() << "\n"); } +#endif LLVM_DEBUG(dbgs() << ":: SearchPaths (LD_LIBRARY_PATH, etc...)\n"); for (auto Info : getSearchPaths()) { LLVM_DEBUG(dbgs() << ":::: " << Info.Path << ", user=" << (Info.IsUser ? "true" : "false") << "\n"); } LLVM_DEBUG(dbgs() << ":: RUNPATH\n"); +#ifndef NDEBUG for (auto Info : RunPath) { LLVM_DEBUG(dbgs() << ":::: " << Info.str() << "\n"); } - +#endif SmallString<512> ThisPath; // RPATH for (auto Info : RPath) { diff --git a/interpreter/CppInterOp/lib/CppInterOp/DynamicLibraryManagerSymbol.cpp b/interpreter/CppInterOp/lib/CppInterOp/DynamicLibraryManagerSymbol.cpp index 44661f90f213c..09d65be89b57f 100644 --- a/interpreter/CppInterOp/lib/CppInterOp/DynamicLibraryManagerSymbol.cpp +++ b/interpreter/CppInterOp/lib/CppInterOp/DynamicLibraryManagerSymbol.cpp @@ -620,10 +620,11 @@ void Dyld::ScanForLibraries(bool searchSystemLibraries /* = false*/) { LLVM_DEBUG(dbgs() << "Dyld::ScanForLibraries: system=" << (searchSystemLibraries ? "true" : "false") << "\n"); +#ifndef NDEBUG for (const DynamicLibraryManager::SearchPathInfo& Info : searchPaths) LLVM_DEBUG(dbgs() << ">>>" << Info.Path << ", " << (Info.IsUser ? "user\n" : "system\n")); - +#endif llvm::SmallSet ScannedPaths; for (const DynamicLibraryManager::SearchPathInfo& Info : searchPaths) { @@ -769,11 +770,12 @@ void Dyld::ScanForLibraries(bool searchSystemLibraries /* = false*/) { << RPathToStr(RPath) << "\n"); LLVM_DEBUG(dbgs() << "Dyld::ScanForLibraries: RUNPATH=" << RPathToStr(RunPath) << "\n"); +#ifndef NDEBUG int x = 0; for (StringRef dep : Deps) LLVM_DEBUG(dbgs() << "Dyld::ScanForLibraries: Deps[" << x++ << "]=" << dep.str() << "\n"); - +#endif // Heuristics for workaround performance problems: // (H1) If RPATH and RUNPATH == "" -> skip handling Deps if (RPath.empty() && RunPath.empty()) { @@ -965,10 +967,11 @@ void Dyld::BuildBloomFilter(LibraryPath* Lib, } LLVM_DEBUG(dbgs() << "Dyld::BuildBloomFilter: Symbols:\n"); +#ifndef NDEBUG for (auto it : symbols) LLVM_DEBUG(dbgs() << "Dyld::BuildBloomFilter" << "- " << it << "\n"); - +#endif // Generate BloomFilter for (const auto& S : symbols) { if (m_UseHashTable) @@ -1036,7 +1039,11 @@ bool Dyld::ContainsSymbol(const LibraryPath* Lib, StringRef mangledName, } auto ForeachSymbol = +#ifndef NDEBUG [&library_filename]( +#else + []( +#endif llvm::iterator_range range, unsigned IgnoreSymbolFlags, llvm::StringRef mangledName) -> bool { for (const llvm::object::SymbolRef& S : range) { @@ -1157,6 +1164,7 @@ bool Dyld::ShouldPermanentlyIgnore(StringRef FileName) const { void Dyld::dumpDebugInfo() const { #define DEBUG_TYPE "Dyld:" LLVM_DEBUG(dbgs() << "---\n"); +#ifndef NDEBUG size_t x = 0; for (auto const& item : m_BasePaths.m_Paths) { LLVM_DEBUG(dbgs() << "Dyld: - m_BasePaths[" << x++ << "]:" << &item << ": " @@ -1174,6 +1182,7 @@ void Dyld::dumpDebugInfo() const { << ": " << item->m_Path << ", " << item->m_LibName << "\n"); } +#endif #undef DEBUG_TYPE } @@ -1213,12 +1222,13 @@ std::string Dyld::searchLibrariesForSymbol(StringRef mangledName, // from our lists of not-yet-loaded libs. LLVM_DEBUG(dbgs() << "Dyld::ResolveSymbol: m_QueriedLibraries:\n"); +#ifndef NDEBUG size_t x = 0; for (auto item : m_QueriedLibraries.GetLibraries()) { LLVM_DEBUG(dbgs() << "Dyld::ResolveSymbol - [" << x++ << "]:" << &item << ": " << item->GetFullName() << "\n"); } - +#endif for (const LibraryPath* P : m_QueriedLibraries.GetLibraries()) { const std::string LibName = P->GetFullName(); if (!m_DynamicLibraryManager.isLibraryLoaded(LibName)) diff --git a/interpreter/CppInterOp/lib/CppInterOp/Paths.cpp b/interpreter/CppInterOp/lib/CppInterOp/Paths.cpp index 78732e7a7b0ac..424d5f90955a9 100644 --- a/interpreter/CppInterOp/lib/CppInterOp/Paths.cpp +++ b/interpreter/CppInterOp/lib/CppInterOp/Paths.cpp @@ -390,10 +390,11 @@ void AddIncludePaths( if (HOpts.Verbose) { LLVM_DEBUG(dbgs() << "Added include paths:\n"); +#ifndef NDEBUG for (llvm::StringRef Path : PathsChecked) LLVM_DEBUG(dbgs() << " " << Path << "\n"); +#endif } - #undef DEBUG_TYPE } diff --git a/interpreter/CppInterOp/lib/CppInterOp/exports.ld b/interpreter/CppInterOp/lib/CppInterOp/exports.ld index 49b7b0d83a6fb..e22490f2a00e9 100644 --- a/interpreter/CppInterOp/lib/CppInterOp/exports.ld +++ b/interpreter/CppInterOp/lib/CppInterOp/exports.ld @@ -50,4 +50,5 @@ -Wl,--export=_ZNK5clang4Type14isFloatingTypeEv -Wl,--export=_ZNK5clang12FunctionDecl12getNumParamsEv -Wl,--export=__clang_Interpreter_SetValueNoAlloc --Wl,--export=__clang_Interpreter_SetValueWithAlloc \ No newline at end of file +-Wl,--export=__clang_Interpreter_SetValueWithAlloc +-Wl,--export=_ZN4llvm15SmallVectorBaseIjE8set_sizeEm \ No newline at end of file diff --git a/interpreter/CppInterOp/patches/llvm/Windows-emscripten-clang19-1-CrossCompile.patch b/interpreter/CppInterOp/patches/llvm/Windows-emscripten-clang19-1-CrossCompile.patch deleted file mode 100644 index a1ffeccc832b4..0000000000000 --- a/interpreter/CppInterOp/patches/llvm/Windows-emscripten-clang19-1-CrossCompile.patch +++ /dev/null @@ -1,20 +0,0 @@ -diff --git a/llvm/cmake/modules/CrossCompile.cmake b/llvm/cmake/modules/CrossCompile.cmake -index 39b4abaa0..474ceddbb 100644 ---- a/llvm/cmake/modules/CrossCompile.cmake -+++ b/llvm/cmake/modules/CrossCompile.cmake -@@ -74,10 +74,12 @@ function(llvm_create_cross_target project_name target_name toolchain buildtype) - endif() - - add_custom_command(OUTPUT ${${project_name}_${target_name}_BUILD}/CMakeCache.txt -- COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" -+ COMMAND ${CMAKE_COMMAND} -G Ninja - -DCMAKE_MAKE_PROGRAM="${CMAKE_MAKE_PROGRAM}" -- -DCMAKE_C_COMPILER_LAUNCHER="${CMAKE_C_COMPILER_LAUNCHER}" -- -DCMAKE_CXX_COMPILER_LAUNCHER="${CMAKE_CXX_COMPILER_LAUNCHER}" -+ -DCMAKE_C_COMPILER="clang-cl" -+ -DCMAKE_CXX_COMPILER="clang-cl" -+ -DCMAKE_ASM_MASM_COMPILER=llvm-ml -+ -DCMAKE_ASM_MASM_FLAGS="-m64" - ${CROSS_TOOLCHAIN_FLAGS_${target_name}} ${CMAKE_CURRENT_SOURCE_DIR} - ${CROSS_TOOLCHAIN_FLAGS_${project_name}_${target_name}} - -DLLVM_TARGET_IS_CROSSCOMPILE_HOST=TRUE diff --git a/interpreter/CppInterOp/patches/llvm/Windows-emscripten-clang20-1-CrossCompile.patch b/interpreter/CppInterOp/patches/llvm/Windows-emscripten-clang20-1-CrossCompile.patch deleted file mode 100644 index a1ffeccc832b4..0000000000000 --- a/interpreter/CppInterOp/patches/llvm/Windows-emscripten-clang20-1-CrossCompile.patch +++ /dev/null @@ -1,20 +0,0 @@ -diff --git a/llvm/cmake/modules/CrossCompile.cmake b/llvm/cmake/modules/CrossCompile.cmake -index 39b4abaa0..474ceddbb 100644 ---- a/llvm/cmake/modules/CrossCompile.cmake -+++ b/llvm/cmake/modules/CrossCompile.cmake -@@ -74,10 +74,12 @@ function(llvm_create_cross_target project_name target_name toolchain buildtype) - endif() - - add_custom_command(OUTPUT ${${project_name}_${target_name}_BUILD}/CMakeCache.txt -- COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" -+ COMMAND ${CMAKE_COMMAND} -G Ninja - -DCMAKE_MAKE_PROGRAM="${CMAKE_MAKE_PROGRAM}" -- -DCMAKE_C_COMPILER_LAUNCHER="${CMAKE_C_COMPILER_LAUNCHER}" -- -DCMAKE_CXX_COMPILER_LAUNCHER="${CMAKE_CXX_COMPILER_LAUNCHER}" -+ -DCMAKE_C_COMPILER="clang-cl" -+ -DCMAKE_CXX_COMPILER="clang-cl" -+ -DCMAKE_ASM_MASM_COMPILER=llvm-ml -+ -DCMAKE_ASM_MASM_FLAGS="-m64" - ${CROSS_TOOLCHAIN_FLAGS_${target_name}} ${CMAKE_CURRENT_SOURCE_DIR} - ${CROSS_TOOLCHAIN_FLAGS_${project_name}_${target_name}} - -DLLVM_TARGET_IS_CROSSCOMPILE_HOST=TRUE diff --git a/interpreter/CppInterOp/patches/llvm/clang16-1-Value.patch b/interpreter/CppInterOp/patches/llvm/clang16-1-Value.patch deleted file mode 100644 index 854077c7a5da4..0000000000000 --- a/interpreter/CppInterOp/patches/llvm/clang16-1-Value.patch +++ /dev/null @@ -1,2084 +0,0 @@ -diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h -index 863f6ac57..feb6db113 100644 ---- a/clang/include/clang/AST/Decl.h -+++ b/clang/include/clang/AST/Decl.h -@@ -4308,6 +4308,7 @@ class TopLevelStmtDecl : public Decl { - friend class ASTDeclWriter; - - Stmt *Statement = nullptr; -+ bool IsSemiMissing = false; - - TopLevelStmtDecl(DeclContext *DC, SourceLocation L, Stmt *S) - : Decl(TopLevelStmt, DC, L), Statement(S) {} -@@ -4321,6 +4322,12 @@ public: - SourceRange getSourceRange() const override LLVM_READONLY; - Stmt *getStmt() { return Statement; } - const Stmt *getStmt() const { return Statement; } -+ void setStmt(Stmt *S) { -+ assert(IsSemiMissing && "Operation supported for printing values only!"); -+ Statement = S; -+ } -+ bool isSemiMissing() const { return IsSemiMissing; } -+ void setSemiMissing(bool Missing = true) { IsSemiMissing = Missing; } - - static bool classof(const Decl *D) { return classofKind(D->getKind()); } - static bool classofKind(Kind K) { return K == TopLevelStmt; } -diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def -index 96feae991..752629855 100644 ---- a/clang/include/clang/Basic/TokenKinds.def -+++ b/clang/include/clang/Basic/TokenKinds.def -@@ -936,6 +936,9 @@ ANNOTATION(module_end) - // into the name of a header unit. - ANNOTATION(header_unit) - -+// Annotation for end of input in clang-repl. -+ANNOTATION(repl_input_end) -+ - #undef PRAGMA_ANNOTATION - #undef ANNOTATION - #undef TESTING_KEYWORD -diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h -index fd22af976..e68021845 100644 ---- a/clang/include/clang/Interpreter/Interpreter.h -+++ b/clang/include/clang/Interpreter/Interpreter.h -@@ -14,13 +14,15 @@ - #ifndef LLVM_CLANG_INTERPRETER_INTERPRETER_H - #define LLVM_CLANG_INTERPRETER_INTERPRETER_H - --#include "clang/Interpreter/PartialTranslationUnit.h" -- -+#include "clang/AST/Decl.h" - #include "clang/AST/GlobalDecl.h" -+#include "clang/Interpreter/PartialTranslationUnit.h" -+#include "clang/Interpreter/Value.h" - -+#include "llvm/ADT/DenseMap.h" - #include "llvm/ExecutionEngine/JITSymbol.h" -+#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" - #include "llvm/Support/Error.h" -- - #include - #include - -@@ -28,7 +30,7 @@ namespace llvm { - namespace orc { - class LLJIT; - class ThreadSafeContext; --} -+} // namespace orc - } // namespace llvm - - namespace clang { -@@ -52,39 +54,64 @@ class Interpreter { - - Interpreter(std::unique_ptr CI, llvm::Error &Err); - -+ llvm::Error CreateExecutor(); -+ unsigned InitPTUSize = 0; -+ -+ // This member holds the last result of the value printing. It's a class -+ // member because we might want to access it after more inputs. If no value -+ // printing happens, it's in an invalid state. -+ Value LastValue; -+ - public: - ~Interpreter(); - static llvm::Expected> - create(std::unique_ptr CI); -+ const ASTContext &getASTContext() const; -+ ASTContext &getASTContext(); - const CompilerInstance *getCompilerInstance() const; -- const llvm::orc::LLJIT *getExecutionEngine() const; -+ llvm::Expected getExecutionEngine(); -+ - llvm::Expected Parse(llvm::StringRef Code); - llvm::Error Execute(PartialTranslationUnit &T); -- llvm::Error ParseAndExecute(llvm::StringRef Code) { -- auto PTU = Parse(Code); -- if (!PTU) -- return PTU.takeError(); -- if (PTU->TheModule) -- return Execute(*PTU); -- return llvm::Error::success(); -- } -+ llvm::Error ParseAndExecute(llvm::StringRef Code, Value *V = nullptr); -+ llvm::Expected CompileDtorCall(CXXRecordDecl *CXXRD); - - /// Undo N previous incremental inputs. - llvm::Error Undo(unsigned N = 1); - -- /// \returns the \c JITTargetAddress of a \c GlobalDecl. This interface uses -+ /// Link a dynamic library -+ llvm::Error LoadDynamicLibrary(const char *name); -+ -+ /// \returns the \c ExecutorAddr of a \c GlobalDecl. This interface uses - /// the CodeGenModule's internal mangling cache to avoid recomputing the - /// mangled name. -- llvm::Expected getSymbolAddress(GlobalDecl GD) const; -+ llvm::Expected getSymbolAddress(GlobalDecl GD) const; - -- /// \returns the \c JITTargetAddress of a given name as written in the IR. -- llvm::Expected -+ /// \returns the \c ExecutorAddr of a given name as written in the IR. -+ llvm::Expected - getSymbolAddress(llvm::StringRef IRName) const; - -- /// \returns the \c JITTargetAddress of a given name as written in the object -+ /// \returns the \c ExecutorAddr of a given name as written in the object - /// file. -- llvm::Expected -+ llvm::Expected - getSymbolAddressFromLinkerName(llvm::StringRef LinkerName) const; -+ -+ enum InterfaceKind { NoAlloc, WithAlloc, CopyArray }; -+ -+ const llvm::SmallVectorImpl &getValuePrintingInfo() const { -+ return ValuePrintingInfo; -+ } -+ -+ Expr *SynthesizeExpr(Expr *E); -+ -+private: -+ size_t getEffectivePTUSize() const; -+ -+ bool FindRuntimeInterface(); -+ -+ llvm::DenseMap Dtors; -+ -+ llvm::SmallVector ValuePrintingInfo; - }; - } // namespace clang - -diff --git a/clang/include/clang/Interpreter/Value.h b/clang/include/clang/Interpreter/Value.h -new file mode 100644 -index 000000000..4df436703 ---- /dev/null -+++ b/clang/include/clang/Interpreter/Value.h -@@ -0,0 +1,202 @@ -+//===--- Value.h - Definition of interpreter value --------------*- C++ -*-===// -+// -+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -+// See https://llvm.org/LICENSE.txt for license information. -+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -+// -+//===----------------------------------------------------------------------===// -+// -+// Value is a lightweight struct that is used for carrying execution results in -+// clang-repl. It's a special runtime that acts like a messager between compiled -+// code and interpreted code. This makes it possible to exchange interesting -+// information between the compiled & interpreted world. -+// -+// A typical usage is like the below: -+// -+// Value V; -+// Interp.ParseAndExecute("int x = 42;"); -+// Interp.ParseAndExecute("x", &V); -+// V.getType(); // <-- Yields a clang::QualType. -+// V.getInt(); // <-- Yields 42. -+// -+// The current design is still highly experimental and nobody should rely on the -+// API being stable because we're hopefully going to make significant changes to -+// it in the relatively near future. For example, Value also intends to be used -+// as an exchange token for JIT support enabling remote execution on the embed -+// devices where the JIT infrastructure cannot fit. To support that we will need -+// to split the memory storage in a different place and perhaps add a resource -+// header is similar to intrinsics headers which have stricter performance -+// constraints. -+// -+//===----------------------------------------------------------------------===// -+ -+#ifndef LLVM_CLANG_INTERPRETER_VALUE_H -+#define LLVM_CLANG_INTERPRETER_VALUE_H -+ -+#include "llvm/Support/Compiler.h" -+#include -+ -+// NOTE: Since the REPL itself could also include this runtime, extreme caution -+// should be taken when MAKING CHANGES to this file, especially when INCLUDE NEW -+// HEADERS, like , and etc. (That pulls a large number of -+// tokens and will impact the runtime performance of the REPL) -+ -+namespace llvm { -+class raw_ostream; -+ -+} // namespace llvm -+ -+namespace clang { -+ -+class ASTContext; -+class Interpreter; -+class QualType; -+ -+#if __has_attribute(visibility) && \ -+ (!(defined(_WIN32) || defined(__CYGWIN__)) || \ -+ (defined(__MINGW32__) && defined(__clang__))) -+#if defined(LLVM_BUILD_LLVM_DYLIB) || defined(LLVM_BUILD_SHARED_LIBS) -+#define REPL_EXTERNAL_VISIBILITY __attribute__((visibility("default"))) -+#else -+#define REPL_EXTERNAL_VISIBILITY -+#endif -+#else -+#if defined(_WIN32) -+#define REPL_EXTERNAL_VISIBILITY __declspec(dllexport) -+#endif -+#endif -+ -+#define REPL_BUILTIN_TYPES \ -+ X(bool, Bool) \ -+ X(char, Char_S) \ -+ X(signed char, SChar) \ -+ X(unsigned char, UChar) \ -+ X(short, Short) \ -+ X(unsigned short, UShort) \ -+ X(int, Int) \ -+ X(unsigned int, UInt) \ -+ X(long, Long) \ -+ X(unsigned long, ULong) \ -+ X(long long, LongLong) \ -+ X(unsigned long long, ULongLong) \ -+ X(float, Float) \ -+ X(double, Double) \ -+ X(long double, LongDouble) -+ -+class REPL_EXTERNAL_VISIBILITY Value { -+ union Storage { -+#define X(type, name) type m_##name; -+ REPL_BUILTIN_TYPES -+#undef X -+ void *m_Ptr; -+ }; -+ -+public: -+ enum Kind { -+#define X(type, name) K_##name, -+ REPL_BUILTIN_TYPES -+#undef X -+ -+ K_Void, -+ K_PtrOrObj, -+ K_Unspecified -+ }; -+ -+ Value() = default; -+ Value(Interpreter *In, void *Ty); -+ Value(const Value &RHS); -+ Value(Value &&RHS) noexcept; -+ Value &operator=(const Value &RHS); -+ Value &operator=(Value &&RHS) noexcept; -+ ~Value(); -+ -+ void printType(llvm::raw_ostream &Out) const; -+ void printData(llvm::raw_ostream &Out) const; -+ void print(llvm::raw_ostream &Out) const; -+ void dump() const; -+ void clear(); -+ -+ ASTContext &getASTContext(); -+ const ASTContext &getASTContext() const; -+ Interpreter &getInterpreter(); -+ const Interpreter &getInterpreter() const; -+ QualType getType() const; -+ -+ bool isValid() const { return ValueKind != K_Unspecified; } -+ bool isVoid() const { return ValueKind == K_Void; } -+ bool hasValue() const { return isValid() && !isVoid(); } -+ bool isManuallyAlloc() const { return IsManuallyAlloc; } -+ Kind getKind() const { return ValueKind; } -+ void setKind(Kind K) { ValueKind = K; } -+ void setOpaqueType(void *Ty) { OpaqueType = Ty; } -+ -+ void *getPtr() const; -+ void setPtr(void *Ptr) { Data.m_Ptr = Ptr; } -+ -+#define X(type, name) \ -+ void set##name(type Val) { Data.m_##name = Val; } \ -+ type get##name() const { return Data.m_##name; } -+ REPL_BUILTIN_TYPES -+#undef X -+ -+ /// \brief Get the value with cast. -+ // -+ /// Get the value cast to T. This is similar to reinterpret_cast(value), -+ /// casting the value of builtins (except void), enums and pointers. -+ /// Values referencing an object are treated as pointers to the object. -+ template T convertTo() const { -+ return convertFwd::cast(*this); -+ } -+ -+protected: -+ bool isPointerOrObjectType() const { return ValueKind == K_PtrOrObj; } -+ -+ /// \brief Get to the value with type checking casting the underlying -+ /// stored value to T. -+ template T as() const { -+ switch (ValueKind) { -+ default: -+ return T(); -+#define X(type, name) \ -+ case Value::K_##name: \ -+ return (T)Data.m_##name; -+ REPL_BUILTIN_TYPES -+#undef X -+ } -+ } -+ -+ // Allow convertTo to be partially specialized. -+ template struct convertFwd { -+ static T cast(const Value &V) { -+ if (V.isPointerOrObjectType()) -+ return (T)(uintptr_t)V.as(); -+ if (!V.isValid() || V.isVoid()) { -+ return T(); -+ } -+ return V.as(); -+ } -+ }; -+ -+ template struct convertFwd { -+ static T *cast(const Value &V) { -+ if (V.isPointerOrObjectType()) -+ return (T *)(uintptr_t)V.as(); -+ return nullptr; -+ } -+ }; -+ -+ Interpreter *Interp = nullptr; -+ void *OpaqueType = nullptr; -+ Storage Data; -+ Kind ValueKind = K_Unspecified; -+ bool IsManuallyAlloc = false; -+}; -+ -+template <> inline void *Value::as() const { -+ if (isPointerOrObjectType()) -+ return Data.m_Ptr; -+ return (void *)as(); -+} -+ -+} // namespace clang -+#endif -diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h -index 6f9581b9e..6b73f43a1 100644 ---- a/clang/include/clang/Parse/Parser.h -+++ b/clang/include/clang/Parse/Parser.h -@@ -18,6 +18,7 @@ - #include "clang/Basic/OpenMPKinds.h" - #include "clang/Basic/OperatorPrecedence.h" - #include "clang/Basic/Specifiers.h" -+#include "clang/Basic/TokenKinds.h" - #include "clang/Lex/CodeCompletionHandler.h" - #include "clang/Lex/Preprocessor.h" - #include "clang/Sema/DeclSpec.h" -@@ -692,7 +693,8 @@ private: - bool isEofOrEom() { - tok::TokenKind Kind = Tok.getKind(); - return Kind == tok::eof || Kind == tok::annot_module_begin || -- Kind == tok::annot_module_end || Kind == tok::annot_module_include; -+ Kind == tok::annot_module_end || Kind == tok::annot_module_include || -+ Kind == tok::annot_repl_input_end; - } - - /// Checks if the \p Level is valid for use in a fold expression. -diff --git a/clang/lib/Frontend/PrintPreprocessedOutput.cpp b/clang/lib/Frontend/PrintPreprocessedOutput.cpp -index ffa85e523..1b262d9e6 100644 ---- a/clang/lib/Frontend/PrintPreprocessedOutput.cpp -+++ b/clang/lib/Frontend/PrintPreprocessedOutput.cpp -@@ -663,7 +663,8 @@ void PrintPPOutputPPCallbacks::HandleWhitespaceBeforeTok(const Token &Tok, - // them. - if (Tok.is(tok::eof) || - (Tok.isAnnotation() && !Tok.is(tok::annot_header_unit) && -- !Tok.is(tok::annot_module_begin) && !Tok.is(tok::annot_module_end))) -+ !Tok.is(tok::annot_module_begin) && !Tok.is(tok::annot_module_end) && -+ !Tok.is(tok::annot_repl_input_end))) - return; - - // EmittedDirectiveOnThisLine takes priority over RequireSameLine. -@@ -819,6 +820,9 @@ static void PrintPreprocessedTokens(Preprocessor &PP, Token &Tok, - // -traditional-cpp the lexer keeps /all/ whitespace, including comments. - PP.Lex(Tok); - continue; -+ } else if (Tok.is(tok::annot_repl_input_end)) { -+ PP.Lex(Tok); -+ continue; - } else if (Tok.is(tok::eod)) { - // Don't print end of directive tokens, since they are typically newlines - // that mess up our line tracking. These come from unknown pre-processor -diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt -index c49f22fdd..565e824bf 100644 ---- a/clang/lib/Interpreter/CMakeLists.txt -+++ b/clang/lib/Interpreter/CMakeLists.txt -@@ -12,6 +12,8 @@ add_clang_library(clangInterpreter - IncrementalExecutor.cpp - IncrementalParser.cpp - Interpreter.cpp -+ InterpreterUtils.cpp -+ Value.cpp - - DEPENDS - intrinsics_gen -diff --git a/clang/lib/Interpreter/IncrementalExecutor.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp -index 37d230b61..489ea48e0 100644 ---- a/clang/lib/Interpreter/IncrementalExecutor.cpp -+++ b/clang/lib/Interpreter/IncrementalExecutor.cpp -@@ -86,7 +86,7 @@ llvm::Error IncrementalExecutor::runCtors() const { - return Jit->initialize(Jit->getMainJITDylib()); - } - --llvm::Expected -+llvm::Expected - IncrementalExecutor::getSymbolAddress(llvm::StringRef Name, - SymbolNameKind NameKind) const { - auto Sym = (NameKind == LinkerName) ? Jit->lookupLinkerMangled(Name) -@@ -94,7 +94,7 @@ IncrementalExecutor::getSymbolAddress(llvm::StringRef Name, - - if (!Sym) - return Sym.takeError(); -- return Sym->getValue(); -+ return Sym; - } - - } // end namespace clang -diff --git a/clang/lib/Interpreter/IncrementalExecutor.h b/clang/lib/Interpreter/IncrementalExecutor.h -index 54d37c763..dd0a210a0 100644 ---- a/clang/lib/Interpreter/IncrementalExecutor.h -+++ b/clang/lib/Interpreter/IncrementalExecutor.h -@@ -16,6 +16,7 @@ - #include "llvm/ADT/DenseMap.h" - #include "llvm/ADT/StringRef.h" - #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" -+#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" - - #include - -@@ -51,9 +52,10 @@ public: - llvm::Error removeModule(PartialTranslationUnit &PTU); - llvm::Error runCtors() const; - llvm::Error cleanUp(); -- llvm::Expected -+ llvm::Expected - getSymbolAddress(llvm::StringRef Name, SymbolNameKind NameKind) const; -- llvm::orc::LLJIT *getExecutionEngine() const { return Jit.get(); } -+ -+ llvm::orc::LLJIT &GetExecutionEngine() { return *Jit; } - }; - - } // end namespace clang -diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp -index 373e2844b..e43189071 100644 ---- a/clang/lib/Interpreter/IncrementalParser.cpp -+++ b/clang/lib/Interpreter/IncrementalParser.cpp -@@ -11,7 +11,6 @@ - //===----------------------------------------------------------------------===// - - #include "IncrementalParser.h" -- - #include "clang/AST/DeclContextInternals.h" - #include "clang/CodeGen/BackendUtil.h" - #include "clang/CodeGen/CodeGenAction.h" -@@ -19,9 +18,9 @@ - #include "clang/Frontend/CompilerInstance.h" - #include "clang/Frontend/FrontendAction.h" - #include "clang/FrontendTool/Utils.h" -+#include "clang/Interpreter/Interpreter.h" - #include "clang/Parse/Parser.h" - #include "clang/Sema/Sema.h" -- - #include "llvm/Option/ArgList.h" - #include "llvm/Support/CrashRecoveryContext.h" - #include "llvm/Support/Error.h" -@@ -31,6 +30,79 @@ - - namespace clang { - -+class IncrementalASTConsumer final : public ASTConsumer { -+ Interpreter &Interp; -+ std::unique_ptr Consumer; -+ -+public: -+ IncrementalASTConsumer(Interpreter &InterpRef, std::unique_ptr C) -+ : Interp(InterpRef), Consumer(std::move(C)) {} -+ -+ bool HandleTopLevelDecl(DeclGroupRef DGR) override final { -+ if (DGR.isNull()) -+ return true; -+ if (!Consumer) -+ return true; -+ -+ for (Decl *D : DGR) -+ if (auto *TSD = llvm::dyn_cast(D); -+ TSD && TSD->isSemiMissing()) -+ TSD->setStmt(Interp.SynthesizeExpr(cast(TSD->getStmt()))); -+ -+ return Consumer->HandleTopLevelDecl(DGR); -+ } -+ void HandleTranslationUnit(ASTContext &Ctx) override final { -+ Consumer->HandleTranslationUnit(Ctx); -+ } -+ void HandleInlineFunctionDefinition(FunctionDecl *D) override final { -+ Consumer->HandleInlineFunctionDefinition(D); -+ } -+ void HandleInterestingDecl(DeclGroupRef D) override final { -+ Consumer->HandleInterestingDecl(D); -+ } -+ void HandleTagDeclDefinition(TagDecl *D) override final { -+ Consumer->HandleTagDeclDefinition(D); -+ } -+ void HandleTagDeclRequiredDefinition(const TagDecl *D) override final { -+ Consumer->HandleTagDeclRequiredDefinition(D); -+ } -+ void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) override final { -+ Consumer->HandleCXXImplicitFunctionInstantiation(D); -+ } -+ void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override final { -+ Consumer->HandleTopLevelDeclInObjCContainer(D); -+ } -+ void HandleImplicitImportDecl(ImportDecl *D) override final { -+ Consumer->HandleImplicitImportDecl(D); -+ } -+ void CompleteTentativeDefinition(VarDecl *D) override final { -+ Consumer->CompleteTentativeDefinition(D); -+ } -+ void CompleteExternalDeclaration(VarDecl *D) override final { -+ Consumer->CompleteExternalDeclaration(D); -+ } -+ void AssignInheritanceModel(CXXRecordDecl *RD) override final { -+ Consumer->AssignInheritanceModel(RD); -+ } -+ void HandleCXXStaticMemberVarInstantiation(VarDecl *D) override final { -+ Consumer->HandleCXXStaticMemberVarInstantiation(D); -+ } -+ void HandleVTable(CXXRecordDecl *RD) override final { -+ Consumer->HandleVTable(RD); -+ } -+ ASTMutationListener *GetASTMutationListener() override final { -+ return Consumer->GetASTMutationListener(); -+ } -+ ASTDeserializationListener *GetASTDeserializationListener() override final { -+ return Consumer->GetASTDeserializationListener(); -+ } -+ void PrintStats() override final { Consumer->PrintStats(); } -+ bool shouldSkipFunctionBody(Decl *D) override final { -+ return Consumer->shouldSkipFunctionBody(D); -+ } -+ static bool classof(const clang::ASTConsumer *) { return true; } -+}; -+ - /// A custom action enabling the incremental processing functionality. - /// - /// The usual \p FrontendAction expects one call to ExecuteAction and once it -@@ -122,7 +194,8 @@ public: - } - }; - --IncrementalParser::IncrementalParser(std::unique_ptr Instance, -+IncrementalParser::IncrementalParser(Interpreter &Interp, -+ std::unique_ptr Instance, - llvm::LLVMContext &LLVMCtx, - llvm::Error &Err) - : CI(std::move(Instance)) { -@@ -131,6 +204,9 @@ IncrementalParser::IncrementalParser(std::unique_ptr Instance, - if (Err) - return; - CI->ExecuteAction(*Act); -+ std::unique_ptr IncrConsumer = -+ std::make_unique(Interp, CI->takeASTConsumer()); -+ CI->setASTConsumer(std::move(IncrConsumer)); - Consumer = &CI->getASTConsumer(); - P.reset( - new Parser(CI->getPreprocessor(), CI->getSema(), /*SkipBodies=*/false)); -@@ -158,8 +234,8 @@ IncrementalParser::ParseOrWrapTopLevelDecl() { - LastPTU.TUPart = C.getTranslationUnitDecl(); - - // Skip previous eof due to last incremental input. -- if (P->getCurToken().is(tok::eof)) { -- P->ConsumeToken(); -+ if (P->getCurToken().is(tok::annot_repl_input_end)) { -+ P->ConsumeAnyToken(); - // FIXME: Clang does not call ExitScope on finalizing the regular TU, we - // might want to do that around HandleEndOfTranslationUnit. - P->ExitScope(); -@@ -259,23 +335,28 @@ IncrementalParser::Parse(llvm::StringRef input) { - Token Tok; - do { - PP.Lex(Tok); -- } while (Tok.isNot(tok::eof)); -+ } while (Tok.isNot(tok::annot_repl_input_end)); -+ } else { -+ Token AssertTok; -+ PP.Lex(AssertTok); -+ assert(AssertTok.is(tok::annot_repl_input_end) && -+ "Lexer must be EOF when starting incremental parse!"); - } - -- Token AssertTok; -- PP.Lex(AssertTok); -- assert(AssertTok.is(tok::eof) && -- "Lexer must be EOF when starting incremental parse!"); -+ if (std::unique_ptr M = GenModule()) -+ PTU->TheModule = std::move(M); -+ -+ return PTU; -+} - -+std::unique_ptr IncrementalParser::GenModule() { -+ static unsigned ID = 0; - if (CodeGenerator *CG = getCodeGen(Act.get())) { - std::unique_ptr M(CG->ReleaseModule()); -- CG->StartModule("incr_module_" + std::to_string(PTUs.size()), -- M->getContext()); -- -- PTU->TheModule = std::move(M); -+ CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext()); -+ return M; - } -- -- return PTU; -+ return nullptr; - } - - void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) { -diff --git a/clang/lib/Interpreter/IncrementalParser.h b/clang/lib/Interpreter/IncrementalParser.h -index 8e45d6b59..99e37588d 100644 ---- a/clang/lib/Interpreter/IncrementalParser.h -+++ b/clang/lib/Interpreter/IncrementalParser.h -@@ -16,7 +16,6 @@ - #include "clang/Interpreter/PartialTranslationUnit.h" - - #include "clang/AST/GlobalDecl.h" -- - #include "llvm/ADT/ArrayRef.h" - #include "llvm/ADT/StringRef.h" - #include "llvm/Support/Error.h" -@@ -31,8 +30,8 @@ namespace clang { - class ASTConsumer; - class CompilerInstance; - class IncrementalAction; -+class Interpreter; - class Parser; -- - /// Provides support for incremental compilation. Keeps track of the state - /// changes between the subsequent incremental input. - /// -@@ -57,7 +56,8 @@ class IncrementalParser { - std::list PTUs; - - public: -- IncrementalParser(std::unique_ptr Instance, -+ IncrementalParser(Interpreter &Interp, -+ std::unique_ptr Instance, - llvm::LLVMContext &LLVMCtx, llvm::Error &Err); - ~IncrementalParser(); - -@@ -76,6 +76,8 @@ public: - - std::list &getPTUs() { return PTUs; } - -+ std::unique_ptr GenModule(); -+ - private: - llvm::Expected ParseOrWrapTopLevelDecl(); - }; -diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp -index a6f5fdc6e..4391bd008 100644 ---- a/clang/lib/Interpreter/Interpreter.cpp -+++ b/clang/lib/Interpreter/Interpreter.cpp -@@ -16,7 +16,11 @@ - #include "IncrementalExecutor.h" - #include "IncrementalParser.h" - -+#include "InterpreterUtils.h" - #include "clang/AST/ASTContext.h" -+#include "clang/AST/Mangle.h" -+#include "clang/AST/TypeVisitor.h" -+#include "clang/Basic/DiagnosticSema.h" - #include "clang/Basic/TargetInfo.h" - #include "clang/CodeGen/ModuleBuilder.h" - #include "clang/CodeGen/ObjectFilePCHContainerOperations.h" -@@ -27,12 +31,16 @@ - #include "clang/Driver/Tool.h" - #include "clang/Frontend/CompilerInstance.h" - #include "clang/Frontend/TextDiagnosticBuffer.h" -+#include "clang/Interpreter/Value.h" - #include "clang/Lex/PreprocessorOptions.h" -- -+#include "clang/Sema/Lookup.h" -+#include "llvm/ExecutionEngine/JITSymbol.h" -+#include "llvm/ExecutionEngine/Orc/LLJIT.h" - #include "llvm/IR/Module.h" - #include "llvm/Support/Errc.h" -+#include "llvm/Support/ErrorHandling.h" -+#include "llvm/Support/raw_ostream.h" - #include "llvm/Support/Host.h" -- - using namespace clang; - - // FIXME: Figure out how to unify with namespace init_convenience from -@@ -176,7 +184,7 @@ Interpreter::Interpreter(std::unique_ptr CI, - llvm::ErrorAsOutParameter EAO(&Err); - auto LLVMCtx = std::make_unique(); - TSCtx = std::make_unique(std::move(LLVMCtx)); -- IncrParser = std::make_unique(std::move(CI), -+ IncrParser = std::make_unique(*this, std::move(CI), - *TSCtx->getContext(), Err); - } - -@@ -189,6 +197,29 @@ Interpreter::~Interpreter() { - } - } - -+// These better to put in a runtime header but we can't. This is because we -+// can't find the precise resource directory in unittests so we have to hard -+// code them. -+const char *const Runtimes = R"( -+ void* operator new(__SIZE_TYPE__, void* __p) noexcept; -+ void *__clang_Interpreter_SetValueWithAlloc(void*, void*, void*); -+ void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*); -+ void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, void*); -+ void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, float); -+ void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, double); -+ void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, long double); -+ void __clang_Interpreter_SetValueNoAlloc(void*,void*,void*,unsigned long long); -+ template -+ void __clang_Interpreter_SetValueCopyArr(T* Src, void* Placement, unsigned long Size) { -+ for (auto Idx = 0; Idx < Size; ++Idx) -+ new ((void*)(((T*)Placement) + Idx)) T(Src[Idx]); -+ } -+ template -+ void __clang_Interpreter_SetValueCopyArr(const T (*Src)[N], void* Placement, unsigned long Size) { -+ __clang_Interpreter_SetValueCopyArr(Src[0], Placement, Size); -+ } -+)"; -+ - llvm::Expected> - Interpreter::create(std::unique_ptr CI) { - llvm::Error Err = llvm::Error::success(); -@@ -196,6 +227,15 @@ Interpreter::create(std::unique_ptr CI) { - std::unique_ptr(new Interpreter(std::move(CI), Err)); - if (Err) - return std::move(Err); -+ auto PTU = Interp->Parse(Runtimes); -+ if (!PTU) -+ return PTU.takeError(); -+ -+ Interp->ValuePrintingInfo.resize(3); -+ // FIXME: This is a ugly hack. Undo command checks its availability by looking -+ // at the size of the PTU list. However we have parsed something in the -+ // beginning of the REPL so we have to mark them as 'Irrevocable'. -+ Interp->InitPTUSize = Interp->IncrParser->getPTUs().size(); - return std::move(Interp); - } - -@@ -203,25 +243,53 @@ const CompilerInstance *Interpreter::getCompilerInstance() const { - return IncrParser->getCI(); - } - --const llvm::orc::LLJIT *Interpreter::getExecutionEngine() const { -- if (IncrExecutor) -- return IncrExecutor->getExecutionEngine(); -- return nullptr; -+llvm::Expected Interpreter::getExecutionEngine() { -+ if (!IncrExecutor) { -+ if (auto Err = CreateExecutor()) -+ return std::move(Err); -+ } -+ -+ return IncrExecutor->GetExecutionEngine(); -+} -+ -+ASTContext &Interpreter::getASTContext() { -+ return getCompilerInstance()->getASTContext(); -+} -+ -+const ASTContext &Interpreter::getASTContext() const { -+ return getCompilerInstance()->getASTContext(); -+} -+ -+size_t Interpreter::getEffectivePTUSize() const { -+ std::list &PTUs = IncrParser->getPTUs(); -+ assert(PTUs.size() >= InitPTUSize && "empty PTU list?"); -+ return PTUs.size() - InitPTUSize; - } - - llvm::Expected - Interpreter::Parse(llvm::StringRef Code) { -+ // Tell the interpreter sliently ignore unused expressions since value -+ // printing could cause it. -+ getCompilerInstance()->getDiagnostics().setSeverity( -+ clang::diag::warn_unused_expr, diag::Severity::Ignored, SourceLocation()); - return IncrParser->Parse(Code); - } - -+llvm::Error Interpreter::CreateExecutor() { -+ const clang::TargetInfo &TI = -+ getCompilerInstance()->getASTContext().getTargetInfo(); -+ llvm::Error Err = llvm::Error::success(); -+ auto Executor = std::make_unique(*TSCtx, Err, TI); -+ if (!Err) -+ IncrExecutor = std::move(Executor); -+ -+ return Err; -+} -+ - llvm::Error Interpreter::Execute(PartialTranslationUnit &T) { - assert(T.TheModule); - if (!IncrExecutor) { -- const clang::TargetInfo &TI = -- getCompilerInstance()->getASTContext().getTargetInfo(); -- llvm::Error Err = llvm::Error::success(); -- IncrExecutor = std::make_unique(*TSCtx, Err, TI); -- -+ auto Err = CreateExecutor(); - if (Err) - return Err; - } -@@ -235,7 +303,26 @@ llvm::Error Interpreter::Execute(PartialTranslationUnit &T) { - return llvm::Error::success(); - } - --llvm::Expected -+llvm::Error Interpreter::ParseAndExecute(llvm::StringRef Code, Value *V) { -+ -+ auto PTU = Parse(Code); -+ if (!PTU) -+ return PTU.takeError(); -+ if (PTU->TheModule) -+ if (llvm::Error Err = Execute(*PTU)) -+ return Err; -+ -+ if (LastValue.isValid()) { -+ if (!V) { -+ LastValue.dump(); -+ LastValue.clear(); -+ } else -+ *V = std::move(LastValue); -+ } -+ return llvm::Error::success(); -+} -+ -+llvm::Expected - Interpreter::getSymbolAddress(GlobalDecl GD) const { - if (!IncrExecutor) - return llvm::make_error("Operation failed. " -@@ -245,7 +332,7 @@ Interpreter::getSymbolAddress(GlobalDecl GD) const { - return getSymbolAddress(MangledName); - } - --llvm::Expected -+llvm::Expected - Interpreter::getSymbolAddress(llvm::StringRef IRName) const { - if (!IncrExecutor) - return llvm::make_error("Operation failed. " -@@ -255,7 +342,7 @@ Interpreter::getSymbolAddress(llvm::StringRef IRName) const { - return IncrExecutor->getSymbolAddress(IRName, IncrementalExecutor::IRName); - } - --llvm::Expected -+llvm::Expected - Interpreter::getSymbolAddressFromLinkerName(llvm::StringRef Name) const { - if (!IncrExecutor) - return llvm::make_error("Operation failed. " -@@ -268,7 +355,7 @@ Interpreter::getSymbolAddressFromLinkerName(llvm::StringRef Name) const { - llvm::Error Interpreter::Undo(unsigned N) { - - std::list &PTUs = IncrParser->getPTUs(); -- if (N > PTUs.size()) -+ if (N > getEffectivePTUSize()) - return llvm::make_error("Operation failed. " - "Too many undos", - std::error_code()); -@@ -283,3 +370,359 @@ llvm::Error Interpreter::Undo(unsigned N) { - } - return llvm::Error::success(); - } -+ -+llvm::Error Interpreter::LoadDynamicLibrary(const char *name) { -+ auto EE = getExecutionEngine(); -+ if (!EE) -+ return EE.takeError(); -+ -+ auto &DL = EE->getDataLayout(); -+ -+ if (auto DLSG = llvm::orc::DynamicLibrarySearchGenerator::Load( -+ name, DL.getGlobalPrefix())) -+ EE->getMainJITDylib().addGenerator(std::move(*DLSG)); -+ else -+ return DLSG.takeError(); -+ -+ return llvm::Error::success(); -+} -+ -+llvm::Expected -+Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) { -+ assert(CXXRD && "Cannot compile a destructor for a nullptr"); -+ if (auto Dtor = Dtors.find(CXXRD); Dtor != Dtors.end()) -+ return Dtor->getSecond(); -+ -+ if (CXXRD->hasIrrelevantDestructor()) -+ return llvm::orc::ExecutorAddr{}; -+ -+ CXXDestructorDecl *DtorRD = -+ getCompilerInstance()->getSema().LookupDestructor(CXXRD); -+ -+ llvm::StringRef Name = -+ IncrParser->GetMangledName(GlobalDecl(DtorRD, Dtor_Base)); -+ auto AddrOrErr = getSymbolAddress(Name); -+ if (!AddrOrErr) -+ return AddrOrErr.takeError(); -+ -+ Dtors[CXXRD] = *AddrOrErr; -+ return AddrOrErr; -+} -+ -+static constexpr llvm::StringRef MagicRuntimeInterface[] = { -+ "__clang_Interpreter_SetValueNoAlloc", -+ "__clang_Interpreter_SetValueWithAlloc", -+ "__clang_Interpreter_SetValueCopyArr"}; -+ -+bool Interpreter::FindRuntimeInterface() { -+ if (llvm::all_of(ValuePrintingInfo, [](Expr *E) { return E != nullptr; })) -+ return true; -+ -+ Sema &S = getCompilerInstance()->getSema(); -+ ASTContext &Ctx = S.getASTContext(); -+ -+ auto LookupInterface = [&](Expr *&Interface, llvm::StringRef Name) { -+ LookupResult R(S, &Ctx.Idents.get(Name), SourceLocation(), -+ Sema::LookupOrdinaryName, Sema::ForVisibleRedeclaration); -+ S.LookupQualifiedName(R, Ctx.getTranslationUnitDecl()); -+ if (R.empty()) -+ return false; -+ -+ CXXScopeSpec CSS; -+ Interface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get(); -+ return true; -+ }; -+ -+ if (!LookupInterface(ValuePrintingInfo[NoAlloc], -+ MagicRuntimeInterface[NoAlloc])) -+ return false; -+ if (!LookupInterface(ValuePrintingInfo[WithAlloc], -+ MagicRuntimeInterface[WithAlloc])) -+ return false; -+ if (!LookupInterface(ValuePrintingInfo[CopyArray], -+ MagicRuntimeInterface[CopyArray])) -+ return false; -+ return true; -+} -+ -+namespace { -+ -+class RuntimeInterfaceBuilder -+ : public TypeVisitor { -+ clang::Interpreter &Interp; -+ ASTContext &Ctx; -+ Sema &S; -+ Expr *E; -+ llvm::SmallVector Args; -+ -+public: -+ RuntimeInterfaceBuilder(clang::Interpreter &In, ASTContext &C, Sema &SemaRef, -+ Expr *VE, ArrayRef FixedArgs) -+ : Interp(In), Ctx(C), S(SemaRef), E(VE) { -+ // The Interpreter* parameter and the out parameter `OutVal`. -+ for (Expr *E : FixedArgs) -+ Args.push_back(E); -+ -+ // Get rid of ExprWithCleanups. -+ if (auto *EWC = llvm::dyn_cast_if_present(E)) -+ E = EWC->getSubExpr(); -+ } -+ -+ ExprResult getCall() { -+ QualType Ty = E->getType(); -+ QualType DesugaredTy = Ty.getDesugaredType(Ctx); -+ -+ // For lvalue struct, we treat it as a reference. -+ if (DesugaredTy->isRecordType() && E->isLValue()) { -+ DesugaredTy = Ctx.getLValueReferenceType(DesugaredTy); -+ Ty = Ctx.getLValueReferenceType(Ty); -+ } -+ -+ Expr *TypeArg = -+ CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)Ty.getAsOpaquePtr()); -+ // The QualType parameter `OpaqueType`, represented as `void*`. -+ Args.push_back(TypeArg); -+ -+ // We push the last parameter based on the type of the Expr. Note we need -+ // special care for rvalue struct. -+ Interpreter::InterfaceKind Kind = Visit(&*DesugaredTy); -+ switch (Kind) { -+ case Interpreter::InterfaceKind::WithAlloc: -+ case Interpreter::InterfaceKind::CopyArray: { -+ // __clang_Interpreter_SetValueWithAlloc. -+ ExprResult AllocCall = S.ActOnCallExpr( -+ /*Scope=*/nullptr, -+ Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::WithAlloc], -+ E->getBeginLoc(), Args, E->getEndLoc()); -+ assert(!AllocCall.isInvalid() && "Can't create runtime interface call!"); -+ -+ TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation()); -+ -+ // Force CodeGen to emit destructor. -+ if (auto *RD = Ty->getAsCXXRecordDecl()) { -+ auto *Dtor = S.LookupDestructor(RD); -+ Dtor->addAttr(UsedAttr::CreateImplicit(Ctx)); -+ Interp.getCompilerInstance()->getASTConsumer().HandleTopLevelDecl( -+ DeclGroupRef(Dtor)); -+ } -+ -+ // __clang_Interpreter_SetValueCopyArr. -+ if (Kind == Interpreter::InterfaceKind::CopyArray) { -+ const auto *ConstantArrTy = -+ cast(DesugaredTy.getTypePtr()); -+ size_t ArrSize = Ctx.getConstantArrayElementCount(ConstantArrTy); -+ Expr *ArrSizeExpr = IntegerLiteralExpr(Ctx, ArrSize); -+ Expr *Args[] = {E, AllocCall.get(), ArrSizeExpr}; -+ return S.ActOnCallExpr( -+ /*Scope *=*/nullptr, -+ Interp -+ .getValuePrintingInfo()[Interpreter::InterfaceKind::CopyArray], -+ SourceLocation(), Args, SourceLocation()); -+ } -+ Expr *Args[] = {AllocCall.get()}; -+ ExprResult CXXNewCall = S.BuildCXXNew( -+ E->getSourceRange(), -+ /*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args, -+ /*PlacementRParen=*/SourceLocation(), -+ /*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt, -+ E->getSourceRange(), E); -+ -+ assert(!CXXNewCall.isInvalid() && -+ "Can't create runtime placement new call!"); -+ -+ return S.ActOnFinishFullExpr(CXXNewCall.get(), -+ /*DiscardedValue=*/false); -+ } -+ // __clang_Interpreter_SetValueNoAlloc. -+ case Interpreter::InterfaceKind::NoAlloc: { -+ return S.ActOnCallExpr( -+ /*Scope=*/nullptr, -+ Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NoAlloc], -+ E->getBeginLoc(), Args, E->getEndLoc()); -+ } -+ } -+ llvm_unreachable("Unhandled Interpreter::InterfaceKind"); -+ } -+ -+ Interpreter::InterfaceKind VisitRecordType(const RecordType *Ty) { -+ return Interpreter::InterfaceKind::WithAlloc; -+ } -+ -+ Interpreter::InterfaceKind -+ VisitMemberPointerType(const MemberPointerType *Ty) { -+ return Interpreter::InterfaceKind::WithAlloc; -+ } -+ -+ Interpreter::InterfaceKind -+ VisitConstantArrayType(const ConstantArrayType *Ty) { -+ return Interpreter::InterfaceKind::CopyArray; -+ } -+ -+ Interpreter::InterfaceKind -+ VisitFunctionProtoType(const FunctionProtoType *Ty) { -+ HandlePtrType(Ty); -+ return Interpreter::InterfaceKind::NoAlloc; -+ } -+ -+ Interpreter::InterfaceKind VisitPointerType(const PointerType *Ty) { -+ HandlePtrType(Ty); -+ return Interpreter::InterfaceKind::NoAlloc; -+ } -+ -+ Interpreter::InterfaceKind VisitReferenceType(const ReferenceType *Ty) { -+ ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E); -+ assert(!AddrOfE.isInvalid() && "Can not create unary expression"); -+ Args.push_back(AddrOfE.get()); -+ return Interpreter::InterfaceKind::NoAlloc; -+ } -+ -+ Interpreter::InterfaceKind VisitBuiltinType(const BuiltinType *Ty) { -+ if (Ty->isNullPtrType()) -+ Args.push_back(E); -+ else if (Ty->isFloatingType()) -+ Args.push_back(E); -+ else if (Ty->isIntegralOrEnumerationType()) -+ HandleIntegralOrEnumType(Ty); -+ else if (Ty->isVoidType()) { -+ // Do we need to still run `E`? -+ } -+ -+ return Interpreter::InterfaceKind::NoAlloc; -+ } -+ -+ Interpreter::InterfaceKind VisitEnumType(const EnumType *Ty) { -+ HandleIntegralOrEnumType(Ty); -+ return Interpreter::InterfaceKind::NoAlloc; -+ } -+ -+private: -+ // Force cast these types to uint64 to reduce the number of overloads of -+ // `__clang_Interpreter_SetValueNoAlloc`. -+ void HandleIntegralOrEnumType(const Type *Ty) { -+ TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.UnsignedLongLongTy); -+ ExprResult CastedExpr = -+ S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E); -+ assert(!CastedExpr.isInvalid() && "Cannot create cstyle cast expr"); -+ Args.push_back(CastedExpr.get()); -+ } -+ -+ void HandlePtrType(const Type *Ty) { -+ TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.VoidPtrTy); -+ ExprResult CastedExpr = -+ S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E); -+ assert(!CastedExpr.isInvalid() && "Can not create cstyle cast expression"); -+ Args.push_back(CastedExpr.get()); -+ } -+}; -+} // namespace -+ -+// This synthesizes a call expression to a speciall -+// function that is responsible for generating the Value. -+// In general, we transform: -+// clang-repl> x -+// To: -+// // 1. If x is a built-in type like int, float. -+// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, x); -+// // 2. If x is a struct, and a lvalue. -+// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, -+// &x); -+// // 3. If x is a struct, but a rvalue. -+// new (__clang_Interpreter_SetValueWithAlloc(ThisInterp, OpaqueValue, -+// xQualType)) (x); -+ -+Expr *Interpreter::SynthesizeExpr(Expr *E) { -+ Sema &S = getCompilerInstance()->getSema(); -+ ASTContext &Ctx = S.getASTContext(); -+ -+ if (!FindRuntimeInterface()) -+ llvm_unreachable("We can't find the runtime iterface for pretty print!"); -+ -+ // Create parameter `ThisInterp`. -+ auto *ThisInterp = CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)this); -+ -+ // Create parameter `OutVal`. -+ auto *OutValue = CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)&LastValue); -+ -+ // Build `__clang_Interpreter_SetValue*` call. -+ RuntimeInterfaceBuilder Builder(*this, Ctx, S, E, {ThisInterp, OutValue}); -+ -+ ExprResult Result = Builder.getCall(); -+ // It could fail, like printing an array type in C. (not supported) -+ if (Result.isInvalid()) -+ return E; -+ return Result.get(); -+} -+ -+// Temporary rvalue struct that need special care. -+REPL_EXTERNAL_VISIBILITY void * -+__clang_Interpreter_SetValueWithAlloc(void *This, void *OutVal, -+ void *OpaqueType) { -+ Value &VRef = *(Value *)OutVal; -+ VRef = Value(static_cast(This), OpaqueType); -+ return VRef.getPtr(); -+} -+ -+// Pointers, lvalue struct that can take as a reference. -+REPL_EXTERNAL_VISIBILITY void -+__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType, -+ void *Val) { -+ Value &VRef = *(Value *)OutVal; -+ VRef = Value(static_cast(This), OpaqueType); -+ VRef.setPtr(Val); -+} -+ -+REPL_EXTERNAL_VISIBILITY void -+__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, -+ void *OpaqueType) { -+ Value &VRef = *(Value *)OutVal; -+ VRef = Value(static_cast(This), OpaqueType); -+} -+ -+static void SetValueDataBasedOnQualType(Value &V, unsigned long long Data) { -+ QualType QT = V.getType(); -+ if (const auto *ET = QT->getAs()) -+ QT = ET->getDecl()->getIntegerType(); -+ -+ switch (QT->getAs()->getKind()) { -+ default: -+ llvm_unreachable("unknown type kind!"); -+#define X(type, name) \ -+ case BuiltinType::name: \ -+ V.set##name(Data); \ -+ break; -+ REPL_BUILTIN_TYPES -+#undef X -+ } -+} -+ -+REPL_EXTERNAL_VISIBILITY void -+__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType, -+ unsigned long long Val) { -+ Value &VRef = *(Value *)OutVal; -+ VRef = Value(static_cast(This), OpaqueType); -+ SetValueDataBasedOnQualType(VRef, Val); -+} -+ -+REPL_EXTERNAL_VISIBILITY void -+__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType, -+ float Val) { -+ Value &VRef = *(Value *)OutVal; -+ VRef = Value(static_cast(This), OpaqueType); -+ VRef.setFloat(Val); -+} -+ -+REPL_EXTERNAL_VISIBILITY void -+__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType, -+ double Val) { -+ Value &VRef = *(Value *)OutVal; -+ VRef = Value(static_cast(This), OpaqueType); -+ VRef.setDouble(Val); -+} -+ -+REPL_EXTERNAL_VISIBILITY void -+__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType, -+ long double Val) { -+ Value &VRef = *(Value *)OutVal; -+ VRef = Value(static_cast(This), OpaqueType); -+ VRef.setLongDouble(Val); -+} -diff --git a/clang/lib/Interpreter/InterpreterUtils.cpp b/clang/lib/Interpreter/InterpreterUtils.cpp -new file mode 100644 -index 000000000..c19cf6aa3 ---- /dev/null -+++ b/clang/lib/Interpreter/InterpreterUtils.cpp -@@ -0,0 +1,111 @@ -+//===--- InterpreterUtils.cpp - Incremental Utils --------*- C++ -*-===// -+// -+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -+// See https://llvm.org/LICENSE.txt for license information. -+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -+// -+//===----------------------------------------------------------------------===// -+// -+// This file implements some common utils used in the incremental library. -+// -+//===----------------------------------------------------------------------===// -+ -+#include "InterpreterUtils.h" -+ -+namespace clang { -+ -+IntegerLiteral *IntegerLiteralExpr(ASTContext &C, uint64_t Val) { -+ return IntegerLiteral::Create(C, llvm::APSInt::getUnsigned(Val), -+ C.UnsignedLongLongTy, SourceLocation()); -+} -+ -+Expr *CStyleCastPtrExpr(Sema &S, QualType Ty, Expr *E) { -+ ASTContext &Ctx = S.getASTContext(); -+ if (!Ty->isPointerType()) -+ Ty = Ctx.getPointerType(Ty); -+ -+ TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation()); -+ Expr *Result = -+ S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E).get(); -+ assert(Result && "Cannot create CStyleCastPtrExpr"); -+ return Result; -+} -+ -+Expr *CStyleCastPtrExpr(Sema &S, QualType Ty, uintptr_t Ptr) { -+ ASTContext &Ctx = S.getASTContext(); -+ return CStyleCastPtrExpr(S, Ty, IntegerLiteralExpr(Ctx, (uint64_t)Ptr)); -+} -+ -+Sema::DeclGroupPtrTy CreateDGPtrFrom(Sema &S, Decl *D) { -+ SmallVector DeclsInGroup; -+ DeclsInGroup.push_back(D); -+ Sema::DeclGroupPtrTy DeclGroupPtr = S.BuildDeclaratorGroup(DeclsInGroup); -+ return DeclGroupPtr; -+} -+ -+NamespaceDecl *LookupNamespace(Sema &S, llvm::StringRef Name, -+ const DeclContext *Within) { -+ DeclarationName DName = &S.Context.Idents.get(Name); -+ LookupResult R(S, DName, SourceLocation(), -+ Sema::LookupNestedNameSpecifierName); -+ R.suppressDiagnostics(); -+ if (!Within) -+ S.LookupName(R, S.TUScope); -+ else { -+ if (const auto *TD = dyn_cast(Within); -+ TD && !TD->getDefinition()) -+ // No definition, no lookup result. -+ return nullptr; -+ -+ S.LookupQualifiedName(R, const_cast(Within)); -+ } -+ -+ if (R.empty()) -+ return nullptr; -+ -+ R.resolveKind(); -+ -+ return dyn_cast(R.getFoundDecl()); -+} -+ -+NamedDecl *LookupNamed(Sema &S, llvm::StringRef Name, -+ const DeclContext *Within) { -+ DeclarationName DName = &S.Context.Idents.get(Name); -+ LookupResult R(S, DName, SourceLocation(), Sema::LookupOrdinaryName, -+ Sema::ForVisibleRedeclaration); -+ -+ R.suppressDiagnostics(); -+ -+ if (!Within) -+ S.LookupName(R, S.TUScope); -+ else { -+ const DeclContext *PrimaryWithin = nullptr; -+ if (const auto *TD = dyn_cast(Within)) -+ PrimaryWithin = llvm::dyn_cast_or_null(TD->getDefinition()); -+ else -+ PrimaryWithin = Within->getPrimaryContext(); -+ -+ // No definition, no lookup result. -+ if (!PrimaryWithin) -+ return nullptr; -+ -+ S.LookupQualifiedName(R, const_cast(PrimaryWithin)); -+ } -+ -+ if (R.empty()) -+ return nullptr; -+ R.resolveKind(); -+ -+ if (R.isSingleResult()) -+ return llvm::dyn_cast(R.getFoundDecl()); -+ -+ return nullptr; -+} -+ -+std::string GetFullTypeName(ASTContext &Ctx, QualType QT) { -+ PrintingPolicy Policy(Ctx.getPrintingPolicy()); -+ Policy.SuppressScope = false; -+ Policy.AnonymousTagLocations = false; -+ return QT.getAsString(Policy); -+} -+} // namespace clang -diff --git a/clang/lib/Interpreter/InterpreterUtils.h b/clang/lib/Interpreter/InterpreterUtils.h -new file mode 100644 -index 000000000..8df158c17 ---- /dev/null -+++ b/clang/lib/Interpreter/InterpreterUtils.h -@@ -0,0 +1,54 @@ -+//===--- InterpreterUtils.h - Incremental Utils --------*- C++ -*-===// -+// -+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -+// See https://llvm.org/LICENSE.txt for license information. -+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -+// -+//===----------------------------------------------------------------------===// -+// -+// This file implements some common utils used in the incremental library. -+// -+//===----------------------------------------------------------------------===// -+ -+#ifndef LLVM_CLANG_INTERPRETER_UTILS_H -+#define LLVM_CLANG_INTERPRETER_UTILS_H -+ -+#include "clang/AST/ASTContext.h" -+#include "clang/AST/Mangle.h" -+#include "clang/AST/TypeVisitor.h" -+#include "clang/Basic/TargetInfo.h" -+#include "clang/CodeGen/ModuleBuilder.h" -+#include "clang/CodeGen/ObjectFilePCHContainerOperations.h" -+#include "clang/Driver/Compilation.h" -+#include "clang/Driver/Driver.h" -+#include "clang/Driver/Job.h" -+#include "clang/Driver/Options.h" -+#include "clang/Driver/Tool.h" -+#include "clang/Frontend/CompilerInstance.h" -+#include "clang/Frontend/TextDiagnosticBuffer.h" -+#include "clang/Lex/PreprocessorOptions.h" -+ -+#include "clang/Sema/Lookup.h" -+#include "llvm/IR/Module.h" -+#include "llvm/Support/Errc.h" -+#include "llvm/TargetParser/Host.h" -+ -+namespace clang { -+IntegerLiteral *IntegerLiteralExpr(ASTContext &C, uint64_t Val); -+ -+Expr *CStyleCastPtrExpr(Sema &S, QualType Ty, Expr *E); -+ -+Expr *CStyleCastPtrExpr(Sema &S, QualType Ty, uintptr_t Ptr); -+ -+Sema::DeclGroupPtrTy CreateDGPtrFrom(Sema &S, Decl *D); -+ -+NamespaceDecl *LookupNamespace(Sema &S, llvm::StringRef Name, -+ const DeclContext *Within = nullptr); -+ -+NamedDecl *LookupNamed(Sema &S, llvm::StringRef Name, -+ const DeclContext *Within); -+ -+std::string GetFullTypeName(ASTContext &Ctx, QualType QT); -+} // namespace clang -+ -+#endif -diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp -new file mode 100644 -index 000000000..fe37eebac ---- /dev/null -+++ b/clang/lib/Interpreter/Value.cpp -@@ -0,0 +1,266 @@ -+//===--- Interpreter.h - Incremental Compiation and Execution---*- C++ -*-===// -+// -+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -+// See https://llvm.org/LICENSE.txt for license information. -+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -+// -+//===----------------------------------------------------------------------===// -+// -+// This file defines the class that used to represent a value in incremental -+// C++. -+// -+//===----------------------------------------------------------------------===// -+ -+#include "clang/Interpreter/Value.h" -+#include "clang/AST/ASTContext.h" -+#include "clang/AST/Type.h" -+#include "clang/Interpreter/Interpreter.h" -+#include "llvm/ADT/StringExtras.h" -+#include "llvm/Support/ErrorHandling.h" -+#include "llvm/Support/raw_os_ostream.h" -+#include -+#include -+#include -+ -+using namespace clang; -+ -+namespace { -+ -+// This is internal buffer maintained by Value, used to hold temporaries. -+class ValueStorage { -+public: -+ using DtorFunc = void (*)(void *); -+ -+ static unsigned char *CreatePayload(void *DtorF, size_t AllocSize, -+ size_t ElementsSize) { -+ if (AllocSize < sizeof(Canary)) -+ AllocSize = sizeof(Canary); -+ unsigned char *Buf = -+ new unsigned char[ValueStorage::getPayloadOffset() + AllocSize]; -+ ValueStorage *VS = new (Buf) ValueStorage(DtorF, AllocSize, ElementsSize); -+ std::memcpy(VS->getPayload(), Canary, sizeof(Canary)); -+ return VS->getPayload(); -+ } -+ -+ unsigned char *getPayload() { return Storage; } -+ const unsigned char *getPayload() const { return Storage; } -+ -+ static unsigned getPayloadOffset() { -+ static ValueStorage Dummy(nullptr, 0, 0); -+ return Dummy.getPayload() - reinterpret_cast(&Dummy); -+ } -+ -+ static ValueStorage *getFromPayload(void *Payload) { -+ ValueStorage *R = reinterpret_cast( -+ (unsigned char *)Payload - getPayloadOffset()); -+ return R; -+ } -+ -+ void Retain() { ++RefCnt; } -+ -+ void Release() { -+ assert(RefCnt > 0 && "Can't release if reference count is already zero"); -+ if (--RefCnt == 0) { -+ // We hace a non-trivial dtor. -+ if (Dtor && IsAlive()) { -+ assert(Elements && "We at least should have 1 element in Value"); -+ size_t Stride = AllocSize / Elements; -+ for (size_t Idx = 0; Idx < Elements; ++Idx) -+ (*Dtor)(getPayload() + Idx * Stride); -+ } -+ delete[] reinterpret_cast(this); -+ } -+ } -+ -+ // Check whether the storage is valid by validating the canary bits. -+ // If someone accidentally write some invalid bits in the storage, the canary -+ // will be changed first, and `IsAlive` will return false then. -+ bool IsAlive() const { -+ return std::memcmp(getPayload(), Canary, sizeof(Canary)) != 0; -+ } -+ -+private: -+ ValueStorage(void *DtorF, size_t AllocSize, size_t ElementsNum) -+ : RefCnt(1), Dtor(reinterpret_cast(DtorF)), -+ AllocSize(AllocSize), Elements(ElementsNum) {} -+ -+ mutable unsigned RefCnt; -+ DtorFunc Dtor = nullptr; -+ size_t AllocSize = 0; -+ size_t Elements = 0; -+ unsigned char Storage[1]; -+ -+ // These are some canary bits that are used for protecting the storage been -+ // damaged. -+ static constexpr unsigned char Canary[8] = {0x4c, 0x37, 0xad, 0x8f, -+ 0x2d, 0x23, 0x95, 0x91}; -+}; -+} // namespace -+ -+static Value::Kind ConvertQualTypeToKind(const ASTContext &Ctx, QualType QT) { -+ if (Ctx.hasSameType(QT, Ctx.VoidTy)) -+ return Value::K_Void; -+ -+ if (const auto *ET = QT->getAs()) -+ QT = ET->getDecl()->getIntegerType(); -+ -+ const auto *BT = QT->getAs(); -+ if (!BT || BT->isNullPtrType()) -+ return Value::K_PtrOrObj; -+ -+ switch (QT->getAs()->getKind()) { -+ default: -+ assert(false && "Type not supported"); -+ return Value::K_Unspecified; -+#define X(type, name) \ -+ case BuiltinType::name: \ -+ return Value::K_##name; -+ REPL_BUILTIN_TYPES -+#undef X -+ } -+} -+ -+Value::Value(Interpreter *In, void *Ty) : Interp(In), OpaqueType(Ty) { -+ setKind(ConvertQualTypeToKind(getASTContext(), getType())); -+ if (ValueKind == K_PtrOrObj) { -+ QualType Canon = getType().getCanonicalType(); -+ if ((Canon->isPointerType() || Canon->isObjectType() || -+ Canon->isReferenceType()) && -+ (Canon->isRecordType() || Canon->isConstantArrayType() || -+ Canon->isMemberPointerType())) { -+ IsManuallyAlloc = true; -+ // Compile dtor function. -+ Interpreter &Interp = getInterpreter(); -+ void *DtorF = nullptr; -+ size_t ElementsSize = 1; -+ QualType DtorTy = getType(); -+ -+ if (const auto *ArrTy = -+ llvm::dyn_cast(DtorTy.getTypePtr())) { -+ DtorTy = ArrTy->getElementType(); -+ llvm::APInt ArrSize(sizeof(size_t) * 8, 1); -+ do { -+ ArrSize *= ArrTy->getSize(); -+ ArrTy = llvm::dyn_cast( -+ ArrTy->getElementType().getTypePtr()); -+ } while (ArrTy); -+ ElementsSize = static_cast(ArrSize.getZExtValue()); -+ } -+ if (const auto *RT = DtorTy->getAs()) { -+ if (CXXRecordDecl *CXXRD = -+ llvm::dyn_cast(RT->getDecl())) { -+ if (llvm::Expected Addr = -+ Interp.CompileDtorCall(CXXRD)) -+ DtorF = reinterpret_cast(Addr->getValue()); -+ else -+ llvm::logAllUnhandledErrors(Addr.takeError(), llvm::errs()); -+ } -+ } -+ -+ size_t AllocSize = -+ getASTContext().getTypeSizeInChars(getType()).getQuantity(); -+ unsigned char *Payload = -+ ValueStorage::CreatePayload(DtorF, AllocSize, ElementsSize); -+ setPtr((void *)Payload); -+ } -+ } -+} -+ -+Value::Value(const Value &RHS) -+ : Interp(RHS.Interp), OpaqueType(RHS.OpaqueType), Data(RHS.Data), -+ ValueKind(RHS.ValueKind), IsManuallyAlloc(RHS.IsManuallyAlloc) { -+ if (IsManuallyAlloc) -+ ValueStorage::getFromPayload(getPtr())->Retain(); -+} -+ -+Value::Value(Value &&RHS) noexcept { -+ Interp = std::exchange(RHS.Interp, nullptr); -+ OpaqueType = std::exchange(RHS.OpaqueType, nullptr); -+ Data = RHS.Data; -+ ValueKind = std::exchange(RHS.ValueKind, K_Unspecified); -+ IsManuallyAlloc = std::exchange(RHS.IsManuallyAlloc, false); -+ -+ if (IsManuallyAlloc) -+ ValueStorage::getFromPayload(getPtr())->Release(); -+} -+ -+Value &Value::operator=(const Value &RHS) { -+ if (IsManuallyAlloc) -+ ValueStorage::getFromPayload(getPtr())->Release(); -+ -+ Interp = RHS.Interp; -+ OpaqueType = RHS.OpaqueType; -+ Data = RHS.Data; -+ ValueKind = RHS.ValueKind; -+ IsManuallyAlloc = RHS.IsManuallyAlloc; -+ -+ if (IsManuallyAlloc) -+ ValueStorage::getFromPayload(getPtr())->Retain(); -+ -+ return *this; -+} -+ -+Value &Value::operator=(Value &&RHS) noexcept { -+ if (IsManuallyAlloc) -+ ValueStorage::getFromPayload(getPtr())->Release(); -+ -+ Interp = std::exchange(RHS.Interp, nullptr); -+ OpaqueType = std::exchange(RHS.OpaqueType, nullptr); -+ ValueKind = std::exchange(RHS.ValueKind, K_Unspecified); -+ IsManuallyAlloc = std::exchange(RHS.IsManuallyAlloc, false); -+ -+ Data = RHS.Data; -+ -+ return *this; -+} -+ -+void Value::clear() { -+ if (IsManuallyAlloc) -+ ValueStorage::getFromPayload(getPtr())->Release(); -+ ValueKind = K_Unspecified; -+ OpaqueType = nullptr; -+ Interp = nullptr; -+ IsManuallyAlloc = false; -+} -+ -+Value::~Value() { clear(); } -+ -+void *Value::getPtr() const { -+ assert(ValueKind == K_PtrOrObj); -+ return Data.m_Ptr; -+} -+ -+QualType Value::getType() const { -+ return QualType::getFromOpaquePtr(OpaqueType); -+} -+ -+Interpreter &Value::getInterpreter() { -+ assert(Interp != nullptr && -+ "Can't get interpreter from a default constructed value"); -+ return *Interp; -+} -+ -+const Interpreter &Value::getInterpreter() const { -+ assert(Interp != nullptr && -+ "Can't get interpreter from a default constructed value"); -+ return *Interp; -+} -+ -+ASTContext &Value::getASTContext() { return getInterpreter().getASTContext(); } -+ -+const ASTContext &Value::getASTContext() const { -+ return getInterpreter().getASTContext(); -+} -+ -+void Value::dump() const { print(llvm::outs()); } -+ -+void Value::printType(llvm::raw_ostream &Out) const { -+ Out << "Not implement yet.\n"; -+} -+void Value::printData(llvm::raw_ostream &Out) const { -+ Out << "Not implement yet.\n"; -+} -+void Value::print(llvm::raw_ostream &Out) const { -+ assert(OpaqueType != nullptr && "Can't print default Value"); -+ Out << "Not implement yet.\n"; -+} -diff --git a/clang/lib/Lex/PPLexerChange.cpp b/clang/lib/Lex/PPLexerChange.cpp -index 66168467e..0822f83b5 100644 ---- a/clang/lib/Lex/PPLexerChange.cpp -+++ b/clang/lib/Lex/PPLexerChange.cpp -@@ -526,13 +526,19 @@ bool Preprocessor::HandleEndOfFile(Token &Result, bool isEndOfMacro) { - return LeavingSubmodule; - } - } -- - // If this is the end of the main file, form an EOF token. - assert(CurLexer && "Got EOF but no current lexer set!"); - const char *EndPos = getCurLexerEndPos(); - Result.startToken(); - CurLexer->BufferPtr = EndPos; -- CurLexer->FormTokenWithChars(Result, EndPos, tok::eof); -+ -+ if (isIncrementalProcessingEnabled()) { -+ CurLexer->FormTokenWithChars(Result, EndPos, tok::annot_repl_input_end); -+ Result.setAnnotationEndLoc(Result.getLocation()); -+ Result.setAnnotationValue(nullptr); -+ } else { -+ CurLexer->FormTokenWithChars(Result, EndPos, tok::eof); -+ } - - if (isCodeCompletionEnabled()) { - // Inserting the code-completion point increases the source buffer by 1, -diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp -index 3a7f5426d..57a3dfba4 100644 ---- a/clang/lib/Parse/ParseCXXInlineMethods.cpp -+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp -@@ -836,6 +836,7 @@ bool Parser::ConsumeAndStoreUntil(tok::TokenKind T1, tok::TokenKind T2, - case tok::annot_module_begin: - case tok::annot_module_end: - case tok::annot_module_include: -+ case tok::annot_repl_input_end: - // Ran out of tokens. - return false; - -@@ -1242,6 +1243,7 @@ bool Parser::ConsumeAndStoreInitializer(CachedTokens &Toks, - case tok::annot_module_begin: - case tok::annot_module_end: - case tok::annot_module_include: -+ case tok::annot_repl_input_end: - // Ran out of tokens. - return false; - -diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp -index e6812ac72..2f193a3b4 100644 ---- a/clang/lib/Parse/ParseDecl.cpp -+++ b/clang/lib/Parse/ParseDecl.cpp -@@ -2030,6 +2030,7 @@ void Parser::SkipMalformedDecl() { - case tok::annot_module_begin: - case tok::annot_module_end: - case tok::annot_module_include: -+ case tok::annot_repl_input_end: - return; - - default: -@@ -5394,6 +5395,13 @@ Parser::DeclGroupPtrTy Parser::ParseTopLevelStmtDecl() { - - SmallVector DeclsInGroup; - DeclsInGroup.push_back(Actions.ActOnTopLevelStmtDecl(R.get())); -+ -+ if (Tok.is(tok::annot_repl_input_end) && -+ Tok.getAnnotationValue() != nullptr) { -+ ConsumeAnnotationToken(); -+ cast(DeclsInGroup.back())->setSemiMissing(); -+ } -+ - // Currently happens for things like -fms-extensions and use `__if_exists`. - for (Stmt *S : Stmts) - DeclsInGroup.push_back(Actions.ActOnTopLevelStmtDecl(S)); -diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp -index 1c8441faf..d22e1d440 100644 ---- a/clang/lib/Parse/ParseStmt.cpp -+++ b/clang/lib/Parse/ParseStmt.cpp -@@ -543,9 +543,22 @@ StmtResult Parser::ParseExprStatement(ParsedStmtContext StmtCtx) { - return ParseCaseStatement(StmtCtx, /*MissingCase=*/true, Expr); - } - -- // Otherwise, eat the semicolon. -- ExpectAndConsumeSemi(diag::err_expected_semi_after_expr); -- return handleExprStmt(Expr, StmtCtx); -+ Token *CurTok = nullptr; -+ // If the semicolon is missing at the end of REPL input, consider if -+ // we want to do value printing. Note this is only enabled in C++ mode -+ // since part of the implementation requires C++ language features. -+ // Note we shouldn't eat the token since the callback needs it. -+ if (Tok.is(tok::annot_repl_input_end) && Actions.getLangOpts().CPlusPlus) -+ CurTok = &Tok; -+ else -+ // Otherwise, eat the semicolon. -+ ExpectAndConsumeSemi(diag::err_expected_semi_after_expr); -+ -+ StmtResult R = handleExprStmt(Expr, StmtCtx); -+ if (CurTok && !R.isInvalid()) -+ CurTok->setAnnotationValue(R.get()); -+ -+ return R; - } - - /// ParseSEHTryBlockCommon -diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp -index 6db3dc315..7fbb27057 100644 ---- a/clang/lib/Parse/Parser.cpp -+++ b/clang/lib/Parse/Parser.cpp -@@ -319,6 +319,7 @@ bool Parser::SkipUntil(ArrayRef Toks, SkipUntilFlags Flags) { - case tok::annot_module_begin: - case tok::annot_module_end: - case tok::annot_module_include: -+ case tok::annot_repl_input_end: - // Stop before we change submodules. They generally indicate a "good" - // place to pick up parsing again (except in the special case where - // we're trying to skip to EOF). -@@ -612,11 +613,6 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result, - Sema::ModuleImportState &ImportState) { - DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(*this); - -- // Skip over the EOF token, flagging end of previous input for incremental -- // processing -- if (PP.isIncrementalProcessingEnabled() && Tok.is(tok::eof)) -- ConsumeToken(); -- - Result = nullptr; - switch (Tok.getKind()) { - case tok::annot_pragma_unused: -@@ -695,6 +691,7 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result, - return false; - - case tok::eof: -+ case tok::annot_repl_input_end: - // Check whether -fmax-tokens= was reached. - if (PP.getMaxTokens() != 0 && PP.getTokenCount() > PP.getMaxTokens()) { - PP.Diag(Tok.getLocation(), diag::warn_max_tokens_total) -diff --git a/clang/test/Interpreter/Inputs/dynamic-library-test.cpp b/clang/test/Interpreter/Inputs/dynamic-library-test.cpp -new file mode 100644 -index 000000000..1f143ba04 ---- /dev/null -+++ b/clang/test/Interpreter/Inputs/dynamic-library-test.cpp -@@ -0,0 +1,6 @@ -+int ultimate_answer = 0; -+ -+int calculate_answer() { -+ ultimate_answer = 42; -+ return 5; -+} -diff --git a/clang/test/Interpreter/dynamic-library.cpp b/clang/test/Interpreter/dynamic-library.cpp -new file mode 100644 -index 000000000..794ccccf7 ---- /dev/null -+++ b/clang/test/Interpreter/dynamic-library.cpp -@@ -0,0 +1,19 @@ -+// REQUIRES: host-supports-jit, system-linux -+ -+// RUN: %clang -xc++ -o %T/libdynamic-library-test.so -fPIC -shared -DLIBRARY %S/Inputs/dynamic-library-test.cpp -+// RUN: cat %s | env LD_LIBRARY_PATH=%T:$LD_LIBRARY_PATH clang-repl | FileCheck %s -+ -+#include -+ -+extern int ultimate_answer; -+int calculate_answer(); -+ -+%lib libdynamic-library-test.so -+ -+printf("Return value: %d\n", calculate_answer()); -+// CHECK: Return value: 5 -+ -+printf("Variable: %d\n", ultimate_answer); -+// CHECK-NEXT: Variable: 42 -+ -+%quit -diff --git a/clang/tools/clang-repl/CMakeLists.txt b/clang/tools/clang-repl/CMakeLists.txt -index b51a18c10..15d7f9439 100644 ---- a/clang/tools/clang-repl/CMakeLists.txt -+++ b/clang/tools/clang-repl/CMakeLists.txt -@@ -12,6 +12,7 @@ add_clang_tool(clang-repl - ) - - clang_target_link_libraries(clang-repl PRIVATE -+ clangAST - clangBasic - clangFrontend - clangInterpreter -diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp -index 401a31d34..33faf3fab 100644 ---- a/clang/tools/clang-repl/ClangRepl.cpp -+++ b/clang/tools/clang-repl/ClangRepl.cpp -@@ -123,6 +123,13 @@ int main(int argc, const char **argv) { - } - continue; - } -+ if (Line->rfind("%lib ", 0) == 0) { -+ if (auto Err = Interp->LoadDynamicLibrary(Line->data() + 5)) { -+ llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); -+ HasError = true; -+ } -+ continue; -+ } - - if (auto Err = Interp->ParseAndExecute(*Line)) { - llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); -diff --git a/clang/unittests/Interpreter/CMakeLists.txt b/clang/unittests/Interpreter/CMakeLists.txt -index 1a099dbbf..698494b98 100644 ---- a/clang/unittests/Interpreter/CMakeLists.txt -+++ b/clang/unittests/Interpreter/CMakeLists.txt -@@ -22,3 +22,5 @@ target_link_libraries(ClangReplInterpreterTests PUBLIC - if(NOT WIN32) - add_subdirectory(ExceptionTests) - endif() -+ -+export_executable_symbols(ClangReplInterpreterTests) -diff --git a/clang/unittests/Interpreter/ExceptionTests/InterpreterExceptionTest.cpp b/clang/unittests/Interpreter/ExceptionTests/InterpreterExceptionTest.cpp -index f54c65568..6d0433a98 100644 ---- a/clang/unittests/Interpreter/ExceptionTests/InterpreterExceptionTest.cpp -+++ b/clang/unittests/Interpreter/ExceptionTests/InterpreterExceptionTest.cpp -@@ -25,7 +25,6 @@ - #include "llvm/ExecutionEngine/Orc/LLJIT.h" - #include "llvm/Support/ManagedStatic.h" - #include "llvm/Support/TargetSelect.h" --#include "llvm-c/Error.h" - - #include "gmock/gmock.h" - #include "gtest/gtest.h" -@@ -116,7 +115,8 @@ extern "C" int throw_exception() { - llvm::cantFail(Interp->ParseAndExecute(ExceptionCode)); - testing::internal::CaptureStdout(); - auto ThrowException = -- (int (*)())llvm::cantFail(Interp->getSymbolAddress("throw_exception")); -+ llvm::cantFail(Interp->getSymbolAddress("throw_exception")) -+ .toPtr(); - EXPECT_ANY_THROW(ThrowException()); - std::string CapturedStdOut = testing::internal::GetCapturedStdout(); - EXPECT_EQ(CapturedStdOut, "Caught: 'To be caught in JIT'\n"); -diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp b/clang/unittests/Interpreter/InterpreterTest.cpp -index d4900a0e4..330fd18ab 100644 ---- a/clang/unittests/Interpreter/InterpreterTest.cpp -+++ b/clang/unittests/Interpreter/InterpreterTest.cpp -@@ -17,6 +17,7 @@ - #include "clang/AST/Mangle.h" - #include "clang/Frontend/CompilerInstance.h" - #include "clang/Frontend/TextDiagnosticPrinter.h" -+#include "clang/Interpreter/Value.h" - #include "clang/Sema/Lookup.h" - #include "clang/Sema/Sema.h" - -@@ -33,6 +34,11 @@ using namespace clang; - #define CLANG_INTERPRETER_NO_SUPPORT_EXEC - #endif - -+int Global = 42; -+// JIT reports symbol not found on Windows without the visibility attribute. -+REPL_EXTERNAL_VISIBILITY int getGlobal() { return Global; } -+REPL_EXTERNAL_VISIBILITY void setGlobal(int val) { Global = val; } -+ - namespace { - using Args = std::vector; - static std::unique_ptr -@@ -225,7 +231,7 @@ TEST(IncrementalProcessing, FindMangledNameSymbol) { - - std::string MangledName = MangleName(FD); - auto Addr = cantFail(Interp->getSymbolAddress(MangledName)); -- EXPECT_NE(0U, Addr); -+ EXPECT_NE(0U, Addr.getValue()); - GlobalDecl GD(FD); - EXPECT_EQ(Addr, cantFail(Interp->getSymbolAddress(GD))); - } -@@ -276,8 +282,7 @@ TEST(IncrementalProcessing, InstantiateTemplate) { - std::vector Args = {"-fno-delayed-template-parsing"}; - std::unique_ptr Interp = createInterpreter(Args); - -- llvm::cantFail(Interp->Parse("void* operator new(__SIZE_TYPE__, void* __p);" -- "extern \"C\" int printf(const char*,...);" -+ llvm::cantFail(Interp->Parse("extern \"C\" int printf(const char*,...);" - "class A {};" - "struct B {" - " template" -@@ -309,9 +314,109 @@ TEST(IncrementalProcessing, InstantiateTemplate) { - - std::string MangledName = MangleName(TmpltSpec); - typedef int (*TemplateSpecFn)(void *); -- auto fn = (TemplateSpecFn)cantFail(Interp->getSymbolAddress(MangledName)); -+ auto fn = -+ cantFail(Interp->getSymbolAddress(MangledName)).toPtr(); - EXPECT_EQ(42, fn(NewA)); - free(NewA); - } - -+#ifdef CLANG_INTERPRETER_NO_SUPPORT_EXEC -+TEST(InterpreterTest, DISABLED_Value) { -+#else -+TEST(InterpreterTest, Value) { -+#endif -+ // We cannot execute on the platform. -+ if (!HostSupportsJit()) -+ return; -+ -+ std::unique_ptr Interp = createInterpreter(); -+ -+ Value V1; -+ llvm::cantFail(Interp->ParseAndExecute("int x = 42;")); -+ llvm::cantFail(Interp->ParseAndExecute("x", &V1)); -+ EXPECT_TRUE(V1.isValid()); -+ EXPECT_TRUE(V1.hasValue()); -+ EXPECT_EQ(V1.getInt(), 42); -+ EXPECT_EQ(V1.convertTo(), 42); -+ EXPECT_TRUE(V1.getType()->isIntegerType()); -+ EXPECT_EQ(V1.getKind(), Value::K_Int); -+ EXPECT_FALSE(V1.isManuallyAlloc()); -+ -+ Value V2; -+ llvm::cantFail(Interp->ParseAndExecute("double y = 3.14;")); -+ llvm::cantFail(Interp->ParseAndExecute("y", &V2)); -+ EXPECT_TRUE(V2.isValid()); -+ EXPECT_TRUE(V2.hasValue()); -+ EXPECT_EQ(V2.getDouble(), 3.14); -+ EXPECT_EQ(V2.convertTo(), 3.14); -+ EXPECT_TRUE(V2.getType()->isFloatingType()); -+ EXPECT_EQ(V2.getKind(), Value::K_Double); -+ EXPECT_FALSE(V2.isManuallyAlloc()); -+ -+ Value V3; -+ llvm::cantFail(Interp->ParseAndExecute( -+ "struct S { int* p; S() { p = new int(42); } ~S() { delete p; }};")); -+ llvm::cantFail(Interp->ParseAndExecute("S{}", &V3)); -+ EXPECT_TRUE(V3.isValid()); -+ EXPECT_TRUE(V3.hasValue()); -+ EXPECT_TRUE(V3.getType()->isRecordType()); -+ EXPECT_EQ(V3.getKind(), Value::K_PtrOrObj); -+ EXPECT_TRUE(V3.isManuallyAlloc()); -+ -+ Value V4; -+ llvm::cantFail(Interp->ParseAndExecute("int getGlobal();")); -+ llvm::cantFail(Interp->ParseAndExecute("void setGlobal(int);")); -+ llvm::cantFail(Interp->ParseAndExecute("getGlobal()", &V4)); -+ EXPECT_EQ(V4.getInt(), 42); -+ EXPECT_TRUE(V4.getType()->isIntegerType()); -+ -+ Value V5; -+ // Change the global from the compiled code. -+ setGlobal(43); -+ llvm::cantFail(Interp->ParseAndExecute("getGlobal()", &V5)); -+ EXPECT_EQ(V5.getInt(), 43); -+ EXPECT_TRUE(V5.getType()->isIntegerType()); -+ -+ // Change the global from the interpreted code. -+ llvm::cantFail(Interp->ParseAndExecute("setGlobal(44);")); -+ EXPECT_EQ(getGlobal(), 44); -+ -+ Value V6; -+ llvm::cantFail(Interp->ParseAndExecute("void foo() {}")); -+ llvm::cantFail(Interp->ParseAndExecute("foo()", &V6)); -+ EXPECT_TRUE(V6.isValid()); -+ EXPECT_FALSE(V6.hasValue()); -+ EXPECT_TRUE(V6.getType()->isVoidType()); -+ EXPECT_EQ(V6.getKind(), Value::K_Void); -+ EXPECT_FALSE(V2.isManuallyAlloc()); -+ -+ Value V7; -+ llvm::cantFail(Interp->ParseAndExecute("foo", &V7)); -+ EXPECT_TRUE(V7.isValid()); -+ EXPECT_TRUE(V7.hasValue()); -+ EXPECT_TRUE(V7.getType()->isFunctionProtoType()); -+ EXPECT_EQ(V7.getKind(), Value::K_PtrOrObj); -+ EXPECT_FALSE(V7.isManuallyAlloc()); -+ -+ Value V8; -+ llvm::cantFail(Interp->ParseAndExecute("struct SS{ void f() {} };")); -+ llvm::cantFail(Interp->ParseAndExecute("&SS::f", &V8)); -+ EXPECT_TRUE(V8.isValid()); -+ EXPECT_TRUE(V8.hasValue()); -+ EXPECT_TRUE(V8.getType()->isMemberFunctionPointerType()); -+ EXPECT_EQ(V8.getKind(), Value::K_PtrOrObj); -+ EXPECT_TRUE(V8.isManuallyAlloc()); -+ -+ Value V9; -+ llvm::cantFail(Interp->ParseAndExecute("struct A { virtual int f(); };")); -+ llvm::cantFail( -+ Interp->ParseAndExecute("struct B : A { int f() { return 42; }};")); -+ llvm::cantFail(Interp->ParseAndExecute("int (B::*ptr)() = &B::f;")); -+ llvm::cantFail(Interp->ParseAndExecute("ptr", &V9)); -+ EXPECT_TRUE(V9.isValid()); -+ EXPECT_TRUE(V9.hasValue()); -+ EXPECT_TRUE(V9.getType()->isMemberFunctionPointerType()); -+ EXPECT_EQ(V9.getKind(), Value::K_PtrOrObj); -+ EXPECT_TRUE(V9.isManuallyAlloc()); -+} - } // end anonymous namespace diff --git a/interpreter/CppInterOp/patches/llvm/clang16-2-CUDA.patch b/interpreter/CppInterOp/patches/llvm/clang16-2-CUDA.patch deleted file mode 100644 index ffaed5b974d7d..0000000000000 --- a/interpreter/CppInterOp/patches/llvm/clang16-2-CUDA.patch +++ /dev/null @@ -1,969 +0,0 @@ -diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h -index e68021845..43573fb1a 100644 ---- a/clang/include/clang/Interpreter/Interpreter.h -+++ b/clang/include/clang/Interpreter/Interpreter.h -@@ -42,8 +42,34 @@ class IncrementalParser; - /// Create a pre-configured \c CompilerInstance for incremental processing. - class IncrementalCompilerBuilder { - public: -+ IncrementalCompilerBuilder() {} -+ -+ void SetCompilerArgs(const std::vector &Args) { -+ UserArgs = Args; -+ } -+ -+ // General C++ -+ llvm::Expected> CreateCpp(); -+ -+ // Offload options -+ void SetOffloadArch(llvm::StringRef Arch) { OffloadArch = Arch; }; -+ -+ // CUDA specific -+ void SetCudaSDK(llvm::StringRef path) { CudaSDKPath = path; }; -+ -+ llvm::Expected> CreateCudaHost(); -+ llvm::Expected> CreateCudaDevice(); -+ -+private: - static llvm::Expected> - create(std::vector &ClangArgv); -+ -+ llvm::Expected> createCuda(bool device); -+ -+ std::vector UserArgs; -+ -+ llvm::StringRef OffloadArch; -+ llvm::StringRef CudaSDKPath; - }; - - /// Provides top-level interfaces for incremental compilation and execution. -@@ -52,6 +78,9 @@ class Interpreter { - std::unique_ptr IncrParser; - std::unique_ptr IncrExecutor; - -+ // An optional parser for CUDA offloading -+ std::unique_ptr DeviceParser; -+ - Interpreter(std::unique_ptr CI, llvm::Error &Err); - - llvm::Error CreateExecutor(); -@@ -66,6 +95,9 @@ public: - ~Interpreter(); - static llvm::Expected> - create(std::unique_ptr CI); -+ static llvm::Expected> -+ createWithCUDA(std::unique_ptr CI, -+ std::unique_ptr DCI); - const ASTContext &getASTContext() const; - ASTContext &getASTContext(); - const CompilerInstance *getCompilerInstance() const; -diff --git a/clang/lib/CodeGen/CGCUDANV.cpp b/clang/lib/CodeGen/CGCUDANV.cpp -index bb887df3e..9cb8ae33b 100644 ---- a/clang/lib/CodeGen/CGCUDANV.cpp -+++ b/clang/lib/CodeGen/CGCUDANV.cpp -@@ -24,6 +24,7 @@ - #include "llvm/IR/DerivedTypes.h" - #include "llvm/IR/ReplaceConstant.h" - #include "llvm/Support/Format.h" -+#include "llvm/Support/VirtualFileSystem.h" - - using namespace clang; - using namespace CodeGen; -@@ -721,8 +722,9 @@ llvm::Function *CGNVCUDARuntime::makeModuleCtorFunction() { - // handle so CUDA runtime can figure out what to call on the GPU side. - std::unique_ptr CudaGpuBinary = nullptr; - if (!CudaGpuBinaryFileName.empty()) { -- llvm::ErrorOr> CudaGpuBinaryOrErr = -- llvm::MemoryBuffer::getFileOrSTDIN(CudaGpuBinaryFileName); -+ auto VFS = CGM.getFileSystem(); -+ auto CudaGpuBinaryOrErr = -+ VFS->getBufferForFile(CudaGpuBinaryFileName, -1, false); - if (std::error_code EC = CudaGpuBinaryOrErr.getError()) { - CGM.getDiags().Report(diag::err_cannot_open_file) - << CudaGpuBinaryFileName << EC.message(); -diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp -index 2b2192678..8ea66845e 100644 ---- a/clang/lib/CodeGen/CodeGenAction.cpp -+++ b/clang/lib/CodeGen/CodeGenAction.cpp -@@ -263,6 +263,7 @@ namespace clang { - // Links each entry in LinkModules into our module. Returns true on error. - bool LinkInModules() { - for (auto &LM : LinkModules) { -+ assert(LM.Module && "LinkModule does not actually have a module"); - if (LM.PropagateAttrs) - for (Function &F : *LM.Module) { - // Skip intrinsics. Keep consistent with how intrinsics are created -@@ -291,6 +292,7 @@ namespace clang { - if (Err) - return true; - } -+ LinkModules.clear(); - return false; // success - } - -diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp -index 12d602fed..978e4d404 100644 ---- a/clang/lib/CodeGen/CodeGenModule.cpp -+++ b/clang/lib/CodeGen/CodeGenModule.cpp -@@ -6228,6 +6228,10 @@ void CodeGenModule::EmitLinkageSpec(const LinkageSpecDecl *LSD) { - } - - void CodeGenModule::EmitTopLevelStmt(const TopLevelStmtDecl *D) { -+ // Device code should not be at top level. -+ if (LangOpts.CUDA && LangOpts.CUDAIsDevice) -+ return; -+ - std::unique_ptr &CurCGF = - GlobalTopLevelStmtBlockInFlight.first; - -diff --git a/clang/lib/CodeGen/ModuleBuilder.cpp b/clang/lib/CodeGen/ModuleBuilder.cpp -index e3e953c34..3594f4c66 100644 ---- a/clang/lib/CodeGen/ModuleBuilder.cpp -+++ b/clang/lib/CodeGen/ModuleBuilder.cpp -@@ -36,7 +36,7 @@ namespace { - IntrusiveRefCntPtr FS; // Only used for debug info. - const HeaderSearchOptions &HeaderSearchOpts; // Only used for debug info. - const PreprocessorOptions &PreprocessorOpts; // Only used for debug info. -- const CodeGenOptions CodeGenOpts; // Intentionally copied in. -+ const CodeGenOptions &CodeGenOpts; - - unsigned HandlingTopLevelDecls; - -diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt -index 565e824bf..32f1b7c37 100644 ---- a/clang/lib/Interpreter/CMakeLists.txt -+++ b/clang/lib/Interpreter/CMakeLists.txt -@@ -1,6 +1,7 @@ - set(LLVM_LINK_COMPONENTS - core - native -+ MC - Option - OrcJit - Support -@@ -9,6 +10,7 @@ set(LLVM_LINK_COMPONENTS - ) - - add_clang_library(clangInterpreter -+ DeviceOffload.cpp - IncrementalExecutor.cpp - IncrementalParser.cpp - Interpreter.cpp -diff --git a/clang/lib/Interpreter/DeviceOffload.cpp b/clang/lib/Interpreter/DeviceOffload.cpp -new file mode 100644 -index 000000000..8e39af6ab ---- /dev/null -+++ b/clang/lib/Interpreter/DeviceOffload.cpp -@@ -0,0 +1,176 @@ -+//===---------- DeviceOffload.cpp - Device Offloading------------*- C++ -*-===// -+// -+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -+// See https://llvm.org/LICENSE.txt for license information. -+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -+// -+//===----------------------------------------------------------------------===// -+// -+// This file implements offloading to CUDA devices. -+// -+//===----------------------------------------------------------------------===// -+ -+#include "DeviceOffload.h" -+ -+#include "clang/Basic/TargetOptions.h" -+#include "clang/CodeGen/ModuleBuilder.h" -+#include "clang/Frontend/CompilerInstance.h" -+ -+#include "llvm/IR/LegacyPassManager.h" -+#include "llvm/MC/TargetRegistry.h" -+#include "llvm/Target/TargetMachine.h" -+ -+namespace clang { -+ -+IncrementalCUDADeviceParser::IncrementalCUDADeviceParser( -+ Interpreter &Interp, std::unique_ptr Instance, -+ IncrementalParser &HostParser, llvm::LLVMContext &LLVMCtx, -+ llvm::IntrusiveRefCntPtr FS, -+ llvm::Error &Err) -+ : IncrementalParser(Interp, std::move(Instance), LLVMCtx, Err), -+ HostParser(HostParser), VFS(FS) { -+ if (Err) -+ return; -+ StringRef Arch = CI->getTargetOpts().CPU; -+ if (!Arch.starts_with("sm_") || Arch.substr(3).getAsInteger(10, SMVersion)) { -+ Err = llvm::joinErrors(std::move(Err), llvm::make_error( -+ "Invalid CUDA architecture", -+ llvm::inconvertibleErrorCode())); -+ return; -+ } -+} -+ -+llvm::Expected -+IncrementalCUDADeviceParser::Parse(llvm::StringRef Input) { -+ auto PTU = IncrementalParser::Parse(Input); -+ if (!PTU) -+ return PTU.takeError(); -+ -+ auto PTX = GeneratePTX(); -+ if (!PTX) -+ return PTX.takeError(); -+ -+ auto Err = GenerateFatbinary(); -+ if (Err) -+ return std::move(Err); -+ -+ std::string FatbinFileName = -+ "/incr_module_" + std::to_string(PTUs.size()) + ".fatbin"; -+ VFS->addFile(FatbinFileName, 0, -+ llvm::MemoryBuffer::getMemBuffer( -+ llvm::StringRef(FatbinContent.data(), FatbinContent.size()), -+ "", false)); -+ -+ HostParser.getCI()->getCodeGenOpts().CudaGpuBinaryFileName = FatbinFileName; -+ -+ FatbinContent.clear(); -+ -+ return PTU; -+} -+ -+llvm::Expected IncrementalCUDADeviceParser::GeneratePTX() { -+ auto &PTU = PTUs.back(); -+ std::string Error; -+ -+ const llvm::Target *Target = llvm::TargetRegistry::lookupTarget( -+ PTU.TheModule->getTargetTriple(), Error); -+ if (!Target) -+ return llvm::make_error(std::move(Error), -+ std::error_code()); -+ llvm::TargetOptions TO = llvm::TargetOptions(); -+ llvm::TargetMachine *TargetMachine = Target->createTargetMachine( -+ PTU.TheModule->getTargetTriple(), getCI()->getTargetOpts().CPU, "", TO, -+ llvm::Reloc::Model::PIC_); -+ PTU.TheModule->setDataLayout(TargetMachine->createDataLayout()); -+ -+ PTXCode.clear(); -+ llvm::raw_svector_ostream dest(PTXCode); -+ -+ llvm::legacy::PassManager PM; -+ if (TargetMachine->addPassesToEmitFile(PM, dest, nullptr, -+ llvm::CGFT_AssemblyFile)) { -+ return llvm::make_error( -+ "NVPTX backend cannot produce PTX code.", -+ llvm::inconvertibleErrorCode()); -+ } -+ -+ if (!PM.run(*PTU.TheModule)) -+ return llvm::make_error("Failed to emit PTX code.", -+ llvm::inconvertibleErrorCode()); -+ -+ PTXCode += '\0'; -+ while (PTXCode.size() % 8) -+ PTXCode += '\0'; -+ return PTXCode.str(); -+} -+ -+llvm::Error IncrementalCUDADeviceParser::GenerateFatbinary() { -+ enum FatBinFlags { -+ AddressSize64 = 0x01, -+ HasDebugInfo = 0x02, -+ ProducerCuda = 0x04, -+ HostLinux = 0x10, -+ HostMac = 0x20, -+ HostWindows = 0x40 -+ }; -+ -+ struct FatBinInnerHeader { -+ uint16_t Kind; // 0x00 -+ uint16_t unknown02; // 0x02 -+ uint32_t HeaderSize; // 0x04 -+ uint32_t DataSize; // 0x08 -+ uint32_t unknown0c; // 0x0c -+ uint32_t CompressedSize; // 0x10 -+ uint32_t SubHeaderSize; // 0x14 -+ uint16_t VersionMinor; // 0x18 -+ uint16_t VersionMajor; // 0x1a -+ uint32_t CudaArch; // 0x1c -+ uint32_t unknown20; // 0x20 -+ uint32_t unknown24; // 0x24 -+ uint32_t Flags; // 0x28 -+ uint32_t unknown2c; // 0x2c -+ uint32_t unknown30; // 0x30 -+ uint32_t unknown34; // 0x34 -+ uint32_t UncompressedSize; // 0x38 -+ uint32_t unknown3c; // 0x3c -+ uint32_t unknown40; // 0x40 -+ uint32_t unknown44; // 0x44 -+ FatBinInnerHeader(uint32_t DataSize, uint32_t CudaArch, uint32_t Flags) -+ : Kind(1 /*PTX*/), unknown02(0x0101), HeaderSize(sizeof(*this)), -+ DataSize(DataSize), unknown0c(0), CompressedSize(0), -+ SubHeaderSize(HeaderSize - 8), VersionMinor(2), VersionMajor(4), -+ CudaArch(CudaArch), unknown20(0), unknown24(0), Flags(Flags), -+ unknown2c(0), unknown30(0), unknown34(0), UncompressedSize(0), -+ unknown3c(0), unknown40(0), unknown44(0) {} -+ }; -+ -+ struct FatBinHeader { -+ uint32_t Magic; // 0x00 -+ uint16_t Version; // 0x04 -+ uint16_t HeaderSize; // 0x06 -+ uint32_t DataSize; // 0x08 -+ uint32_t unknown0c; // 0x0c -+ public: -+ FatBinHeader(uint32_t DataSize) -+ : Magic(0xba55ed50), Version(1), HeaderSize(sizeof(*this)), -+ DataSize(DataSize), unknown0c(0) {} -+ }; -+ -+ FatBinHeader OuterHeader(sizeof(FatBinInnerHeader) + PTXCode.size()); -+ FatbinContent.append((char *)&OuterHeader, -+ ((char *)&OuterHeader) + OuterHeader.HeaderSize); -+ -+ FatBinInnerHeader InnerHeader(PTXCode.size(), SMVersion, -+ FatBinFlags::AddressSize64 | -+ FatBinFlags::HostLinux); -+ FatbinContent.append((char *)&InnerHeader, -+ ((char *)&InnerHeader) + InnerHeader.HeaderSize); -+ -+ FatbinContent.append(PTXCode.begin(), PTXCode.end()); -+ -+ return llvm::Error::success(); -+} -+ -+IncrementalCUDADeviceParser::~IncrementalCUDADeviceParser() {} -+ -+} // namespace clang -diff --git a/clang/lib/Interpreter/DeviceOffload.h b/clang/lib/Interpreter/DeviceOffload.h -new file mode 100644 -index 000000000..ce4f218c9 ---- /dev/null -+++ b/clang/lib/Interpreter/DeviceOffload.h -@@ -0,0 +1,51 @@ -+//===----------- DeviceOffload.h - Device Offloading ------------*- C++ -*-===// -+// -+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -+// See https://llvm.org/LICENSE.txt for license information. -+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -+// -+//===----------------------------------------------------------------------===// -+// -+// This file implements classes required for offloading to CUDA devices. -+// -+//===----------------------------------------------------------------------===// -+ -+#ifndef LLVM_CLANG_LIB_INTERPRETER_DEVICE_OFFLOAD_H -+#define LLVM_CLANG_LIB_INTERPRETER_DEVICE_OFFLOAD_H -+ -+#include "IncrementalParser.h" -+#include "llvm/Support/FileSystem.h" -+#include "llvm/Support/VirtualFileSystem.h" -+ -+namespace clang { -+ -+class IncrementalCUDADeviceParser : public IncrementalParser { -+public: -+ IncrementalCUDADeviceParser( -+ Interpreter &Interp, std::unique_ptr Instance, -+ IncrementalParser &HostParser, llvm::LLVMContext &LLVMCtx, -+ llvm::IntrusiveRefCntPtr VFS, -+ llvm::Error &Err); -+ -+ llvm::Expected -+ Parse(llvm::StringRef Input) override; -+ -+ // Generate PTX for the last PTU -+ llvm::Expected GeneratePTX(); -+ -+ // Generate fatbinary contents in memory -+ llvm::Error GenerateFatbinary(); -+ -+ ~IncrementalCUDADeviceParser(); -+ -+protected: -+ IncrementalParser &HostParser; -+ int SMVersion; -+ llvm::SmallString<1024> PTXCode; -+ llvm::SmallVector FatbinContent; -+ llvm::IntrusiveRefCntPtr VFS; -+}; -+ -+} // namespace clang -+ -+#endif // LLVM_CLANG_LIB_INTERPRETER_DEVICE_OFFLOAD_H -diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp -index f892eeb81..a3ae1aa8a 100644 ---- a/clang/lib/Interpreter/IncrementalParser.cpp -+++ b/clang/lib/Interpreter/IncrementalParser.cpp -@@ -194,6 +194,15 @@ public: - } - }; - -+CodeGenerator *IncrementalParser::getCodeGen() const { -+ FrontendAction *WrappedAct = Act->getWrapped(); -+ if (!WrappedAct->hasIRSupport()) -+ return nullptr; -+ return static_cast(WrappedAct)->getCodeGenerator(); -+} -+ -+IncrementalParser::IncrementalParser() {} -+ - IncrementalParser::IncrementalParser(Interpreter &Interp, - std::unique_ptr Instance, - llvm::LLVMContext &LLVMCtx, -@@ -211,6 +220,21 @@ IncrementalParser::IncrementalParser(Interpreter &Interp, - P.reset( - new Parser(CI->getPreprocessor(), CI->getSema(), /*SkipBodies=*/false)); - P->Initialize(); -+ -+ // An initial PTU is needed as CUDA includes some headers automatically -+ auto PTU = ParseOrWrapTopLevelDecl(); -+ if (auto E = PTU.takeError()) { -+ consumeError(std::move(E)); // FIXME -+ return; // PTU.takeError(); -+ } -+ -+ if (CodeGenerator *CG = getCodeGen()) { -+ std::unique_ptr M(CG->ReleaseModule()); -+ CG->StartModule("incr_module_" + std::to_string(PTUs.size()), -+ M->getContext()); -+ PTU->TheModule = std::move(M); -+ assert(PTU->TheModule && "Failed to create initial PTU"); -+ } - } - - IncrementalParser::~IncrementalParser() { -@@ -281,14 +305,6 @@ IncrementalParser::ParseOrWrapTopLevelDecl() { - return LastPTU; - } - --static CodeGenerator *getCodeGen(FrontendAction *Act) { -- IncrementalAction *IncrAct = static_cast(Act); -- FrontendAction *WrappedAct = IncrAct->getWrapped(); -- if (!WrappedAct->hasIRSupport()) -- return nullptr; -- return static_cast(WrappedAct)->getCodeGenerator(); --} -- - llvm::Expected - IncrementalParser::Parse(llvm::StringRef input) { - Preprocessor &PP = CI->getPreprocessor(); -@@ -351,7 +367,7 @@ IncrementalParser::Parse(llvm::StringRef input) { - - std::unique_ptr IncrementalParser::GenModule() { - static unsigned ID = 0; -- if (CodeGenerator *CG = getCodeGen(Act.get())) { -+ if (CodeGenerator *CG = getCodeGen()) { - std::unique_ptr M(CG->ReleaseModule()); - CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext()); - return M; -@@ -378,7 +394,7 @@ void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) { - } - - llvm::StringRef IncrementalParser::GetMangledName(GlobalDecl GD) const { -- CodeGenerator *CG = getCodeGen(Act.get()); -+ CodeGenerator *CG = getCodeGen(); - assert(CG); - return CG->GetMangledName(GD); - } -diff --git a/clang/lib/Interpreter/IncrementalParser.h b/clang/lib/Interpreter/IncrementalParser.h -index 99e37588d..def5750d1 100644 ---- a/clang/lib/Interpreter/IncrementalParser.h -+++ b/clang/lib/Interpreter/IncrementalParser.h -@@ -28,6 +28,7 @@ class LLVMContext; - - namespace clang { - class ASTConsumer; -+class CodeGenerator; - class CompilerInstance; - class IncrementalAction; - class Interpreter; -@@ -36,6 +37,7 @@ class Parser; - /// changes between the subsequent incremental input. - /// - class IncrementalParser { -+protected: - /// Long-lived, incremental parsing action. - std::unique_ptr Act; - -@@ -55,18 +57,21 @@ class IncrementalParser { - /// of code. - std::list PTUs; - -+ IncrementalParser(); -+ - public: - IncrementalParser(Interpreter &Interp, - std::unique_ptr Instance, - llvm::LLVMContext &LLVMCtx, llvm::Error &Err); -- ~IncrementalParser(); -+ virtual ~IncrementalParser(); - -- const CompilerInstance *getCI() const { return CI.get(); } -+ CompilerInstance *getCI() { return CI.get(); } -+ CodeGenerator *getCodeGen() const; - - /// Parses incremental input by creating an in-memory file. - ///\returns a \c PartialTranslationUnit which holds information about the - /// \c TranslationUnitDecl and \c llvm::Module corresponding to the input. -- llvm::Expected Parse(llvm::StringRef Input); -+ virtual llvm::Expected Parse(llvm::StringRef Input); - - /// Uses the CodeGenModule mangled name cache and avoids recomputing. - ///\returns the mangled name of a \c GD. -diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp -index 4391bd008..74d428a86 100644 ---- a/clang/lib/Interpreter/Interpreter.cpp -+++ b/clang/lib/Interpreter/Interpreter.cpp -@@ -13,6 +13,7 @@ - - #include "clang/Interpreter/Interpreter.h" - -+#include "DeviceOffload.h" - #include "IncrementalExecutor.h" - #include "IncrementalParser.h" - -@@ -22,6 +23,7 @@ - #include "clang/AST/TypeVisitor.h" - #include "clang/Basic/DiagnosticSema.h" - #include "clang/Basic/TargetInfo.h" -+#include "clang/CodeGen/CodeGenAction.h" - #include "clang/CodeGen/ModuleBuilder.h" - #include "clang/CodeGen/ObjectFilePCHContainerOperations.h" - #include "clang/Driver/Compilation.h" -@@ -146,7 +148,6 @@ IncrementalCompilerBuilder::create(std::vector &ClangArgv) { - // action and use other actions in incremental mode. - // FIXME: Print proper driver diagnostics if the driver flags are wrong. - // We do C++ by default; append right after argv[0] if no "-x" given -- ClangArgv.insert(ClangArgv.end(), "-xc++"); - ClangArgv.insert(ClangArgv.end(), "-Xclang"); - ClangArgv.insert(ClangArgv.end(), "-fincremental-extensions"); - ClangArgv.insert(ClangArgv.end(), "-c"); -@@ -179,6 +180,54 @@ IncrementalCompilerBuilder::create(std::vector &ClangArgv) { - return CreateCI(**ErrOrCC1Args); - } - -+llvm::Expected> -+IncrementalCompilerBuilder::CreateCpp() { -+ std::vector Argv; -+ Argv.reserve(5 + 1 + UserArgs.size()); -+ Argv.push_back("-xc++"); -+ Argv.insert(Argv.end(), UserArgs.begin(), UserArgs.end()); -+ -+ return IncrementalCompilerBuilder::create(Argv); -+} -+ -+llvm::Expected> -+IncrementalCompilerBuilder::createCuda(bool device) { -+ std::vector Argv; -+ Argv.reserve(5 + 4 + UserArgs.size()); -+ -+ Argv.push_back("-xcuda"); -+ if (device) -+ Argv.push_back("--cuda-device-only"); -+ else -+ Argv.push_back("--cuda-host-only"); -+ -+ std::string SDKPathArg = "--cuda-path="; -+ if (!CudaSDKPath.empty()) { -+ SDKPathArg += CudaSDKPath; -+ Argv.push_back(SDKPathArg.c_str()); -+ } -+ -+ std::string ArchArg = "--offload-arch="; -+ if (!OffloadArch.empty()) { -+ ArchArg += OffloadArch; -+ Argv.push_back(ArchArg.c_str()); -+ } -+ -+ Argv.insert(Argv.end(), UserArgs.begin(), UserArgs.end()); -+ -+ return IncrementalCompilerBuilder::create(Argv); -+} -+ -+llvm::Expected> -+IncrementalCompilerBuilder::CreateCudaDevice() { -+ return IncrementalCompilerBuilder::createCuda(true); -+} -+ -+llvm::Expected> -+IncrementalCompilerBuilder::CreateCudaHost() { -+ return IncrementalCompilerBuilder::createCuda(false); -+} -+ - Interpreter::Interpreter(std::unique_ptr CI, - llvm::Error &Err) { - llvm::ErrorAsOutParameter EAO(&Err); -@@ -239,6 +288,34 @@ Interpreter::create(std::unique_ptr CI) { - return std::move(Interp); - } - -+llvm::Expected> -+Interpreter::createWithCUDA(std::unique_ptr CI, -+ std::unique_ptr DCI) { -+ // avoid writing fat binary to disk using an in-memory virtual file system -+ llvm::IntrusiveRefCntPtr IMVFS = -+ std::make_unique(); -+ llvm::IntrusiveRefCntPtr OverlayVFS = -+ std::make_unique( -+ llvm::vfs::getRealFileSystem()); -+ OverlayVFS->pushOverlay(IMVFS); -+ CI->createFileManager(OverlayVFS); -+ -+ auto Interp = Interpreter::create(std::move(CI)); -+ if (auto E = Interp.takeError()) -+ return std::move(E); -+ -+ llvm::Error Err = llvm::Error::success(); -+ auto DeviceParser = std::make_unique( -+ **Interp, std::move(DCI), *(*Interp)->IncrParser.get(), -+ *(*Interp)->TSCtx->getContext(), IMVFS, Err); -+ if (Err) -+ return std::move(Err); -+ -+ (*Interp)->DeviceParser = std::move(DeviceParser); -+ -+ return Interp; -+} -+ - const CompilerInstance *Interpreter::getCompilerInstance() const { - return IncrParser->getCI(); - } -@@ -268,6 +345,14 @@ size_t Interpreter::getEffectivePTUSize() const { - - llvm::Expected - Interpreter::Parse(llvm::StringRef Code) { -+ // If we have a device parser, parse it first. -+ // The generated code will be included in the host compilation -+ if (DeviceParser) { -+ auto DevicePTU = DeviceParser->Parse(Code); -+ if (auto E = DevicePTU.takeError()) -+ return std::move(E); -+ } -+ - // Tell the interpreter sliently ignore unused expressions since value - // printing could cause it. - getCompilerInstance()->getDiagnostics().setSeverity( -diff --git a/clang/test/Interpreter/CUDA/device-function-template.cu b/clang/test/Interpreter/CUDA/device-function-template.cu -new file mode 100644 -index 000000000..f0077a2c5 ---- /dev/null -+++ b/clang/test/Interpreter/CUDA/device-function-template.cu -@@ -0,0 +1,24 @@ -+// Tests device function templates -+// RUN: cat %s | clang-repl --cuda | FileCheck %s -+ -+extern "C" int printf(const char*, ...); -+ -+template __device__ inline T sum(T a, T b) { return a + b; } -+__global__ void test_kernel(int* value) { *value = sum(40, 2); } -+ -+int var; -+int* devptr = nullptr; -+printf("cudaMalloc: %d\n", cudaMalloc((void **) &devptr, sizeof(int))); -+// CHECK: cudaMalloc: 0 -+ -+test_kernel<<<1,1>>>(devptr); -+printf("CUDA Error: %d\n", cudaGetLastError()); -+// CHECK-NEXT: CUDA Error: 0 -+ -+printf("cudaMemcpy: %d\n", cudaMemcpy(&var, devptr, sizeof(int), cudaMemcpyDeviceToHost)); -+// CHECK-NEXT: cudaMemcpy: 0 -+ -+printf("Value: %d\n", var); -+// CHECK-NEXT: Value: 42 -+ -+%quit -diff --git a/clang/test/Interpreter/CUDA/device-function.cu b/clang/test/Interpreter/CUDA/device-function.cu -new file mode 100644 -index 000000000..396f8f0f9 ---- /dev/null -+++ b/clang/test/Interpreter/CUDA/device-function.cu -@@ -0,0 +1,24 @@ -+// Tests __device__ function calls -+// RUN: cat %s | clang-repl --cuda | FileCheck %s -+ -+extern "C" int printf(const char*, ...); -+ -+__device__ inline void test_device(int* value) { *value = 42; } -+__global__ void test_kernel(int* value) { test_device(value); } -+ -+int var; -+int* devptr = nullptr; -+printf("cudaMalloc: %d\n", cudaMalloc((void **) &devptr, sizeof(int))); -+// CHECK: cudaMalloc: 0 -+ -+test_kernel<<<1,1>>>(devptr); -+printf("CUDA Error: %d\n", cudaGetLastError()); -+// CHECK-NEXT: CUDA Error: 0 -+ -+printf("cudaMemcpy: %d\n", cudaMemcpy(&var, devptr, sizeof(int), cudaMemcpyDeviceToHost)); -+// CHECK-NEXT: cudaMemcpy: 0 -+ -+printf("Value: %d\n", var); -+// CHECK-NEXT: Value: 42 -+ -+%quit -diff --git a/clang/test/Interpreter/CUDA/host-and-device.cu b/clang/test/Interpreter/CUDA/host-and-device.cu -new file mode 100644 -index 000000000..8e44e3403 ---- /dev/null -+++ b/clang/test/Interpreter/CUDA/host-and-device.cu -@@ -0,0 +1,27 @@ -+// Checks that a function is available in both __host__ and __device__ -+// RUN: cat %s | clang-repl --cuda | FileCheck %s -+ -+extern "C" int printf(const char*, ...); -+ -+__host__ __device__ inline int sum(int a, int b){ return a + b; } -+__global__ void kernel(int * output){ *output = sum(40,2); } -+ -+printf("Host sum: %d\n", sum(41,1)); -+// CHECK: Host sum: 42 -+ -+int var = 0; -+int * deviceVar; -+printf("cudaMalloc: %d\n", cudaMalloc((void **) &deviceVar, sizeof(int))); -+// CHECK-NEXT: cudaMalloc: 0 -+ -+kernel<<<1,1>>>(deviceVar); -+printf("CUDA Error: %d\n", cudaGetLastError()); -+// CHECK-NEXT: CUDA Error: 0 -+ -+printf("cudaMemcpy: %d\n", cudaMemcpy(&var, deviceVar, sizeof(int), cudaMemcpyDeviceToHost)); -+// CHECK-NEXT: cudaMemcpy: 0 -+ -+printf("var: %d\n", var); -+// CHECK-NEXT: var: 42 -+ -+%quit -diff --git a/clang/test/Interpreter/CUDA/lit.local.cfg b/clang/test/Interpreter/CUDA/lit.local.cfg -new file mode 100644 -index 000000000..999157246 ---- /dev/null -+++ b/clang/test/Interpreter/CUDA/lit.local.cfg -@@ -0,0 +1,2 @@ -+if 'host-supports-cuda' not in config.available_features: -+ config.unsupported = True -diff --git a/clang/test/Interpreter/CUDA/memory.cu b/clang/test/Interpreter/CUDA/memory.cu -new file mode 100644 -index 000000000..852cc04f6 ---- /dev/null -+++ b/clang/test/Interpreter/CUDA/memory.cu -@@ -0,0 +1,23 @@ -+// Tests cudaMemcpy and writes from kernel -+// RUN: cat %s | clang-repl --cuda | FileCheck %s -+ -+extern "C" int printf(const char*, ...); -+ -+__global__ void test_func(int* value) { *value = 42; } -+ -+int var; -+int* devptr = nullptr; -+printf("cudaMalloc: %d\n", cudaMalloc((void **) &devptr, sizeof(int))); -+// CHECK: cudaMalloc: 0 -+ -+test_func<<<1,1>>>(devptr); -+printf("CUDA Error: %d\n", cudaGetLastError()); -+// CHECK-NEXT: CUDA Error: 0 -+ -+printf("cudaMemcpy: %d\n", cudaMemcpy(&var, devptr, sizeof(int), cudaMemcpyDeviceToHost)); -+// CHECK-NEXT: cudaMemcpy: 0 -+ -+printf("Value: %d\n", var); -+// CHECK-NEXT: Value: 42 -+ -+%quit -diff --git a/clang/test/Interpreter/CUDA/sanity.cu b/clang/test/Interpreter/CUDA/sanity.cu -new file mode 100644 -index 000000000..ef9d68df4 ---- /dev/null -+++ b/clang/test/Interpreter/CUDA/sanity.cu -@@ -0,0 +1,11 @@ -+// RUN: cat %s | clang-repl --cuda | FileCheck %s -+ -+extern "C" int printf(const char*, ...); -+ -+__global__ void test_func() {} -+ -+test_func<<<1,1>>>(); -+printf("CUDA Error: %d", cudaGetLastError()); -+// CHECK: CUDA Error: 0 -+ -+%quit -diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py -index cc55c3c44..fe237fadc 100644 ---- a/clang/test/lit.cfg.py -+++ b/clang/test/lit.cfg.py -@@ -86,9 +86,39 @@ def have_host_jit_feature_support(feature_name): - - return 'true' in clang_repl_out - -+def have_host_clang_repl_cuda(): -+ clang_repl_exe = lit.util.which('clang-repl', config.clang_tools_dir) -+ -+ if not clang_repl_exe: -+ return False -+ -+ testcode = b'\n'.join([ -+ b"__global__ void test_func() {}", -+ b"test_func<<<1,1>>>();", -+ b"extern \"C\" int puts(const char *s);", -+ b"puts(cudaGetLastError() ? \"failure\" : \"success\");", -+ b"%quit" -+ ]) -+ try: -+ clang_repl_cmd = subprocess.run([clang_repl_exe, '--cuda'], -+ stdout=subprocess.PIPE, -+ stderr=subprocess.PIPE, -+ input=testcode) -+ except OSError: -+ return False -+ -+ if clang_repl_cmd.returncode == 0: -+ if clang_repl_cmd.stdout.find(b"success") != -1: -+ return True -+ -+ return False -+ - if have_host_jit_feature_support('jit'): - config.available_features.add('host-supports-jit') - -+ if have_host_clang_repl_cuda(): -+ config.available_features.add('host-supports-cuda') -+ - if config.clang_staticanalyzer: - config.available_features.add('staticanalyzer') - tools.append('clang-check') -diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp -index 33faf3fab..19733e193 100644 ---- a/clang/tools/clang-repl/ClangRepl.cpp -+++ b/clang/tools/clang-repl/ClangRepl.cpp -@@ -20,9 +20,13 @@ - #include "llvm/Support/CommandLine.h" - #include "llvm/Support/ManagedStatic.h" // llvm_shutdown - #include "llvm/Support/Signals.h" --#include "llvm/Support/TargetSelect.h" // llvm::Initialize* -+#include "llvm/Support/TargetSelect.h" - #include - -+static llvm::cl::opt CudaEnabled("cuda", llvm::cl::Hidden); -+static llvm::cl::opt CudaPath("cuda-path", llvm::cl::Hidden); -+static llvm::cl::opt OffloadArch("offload-arch", llvm::cl::Hidden); -+ - static llvm::cl::list - ClangArgs("Xcc", - llvm::cl::desc("Argument to pass to the CompilerInvocation"), -@@ -76,8 +80,11 @@ int main(int argc, const char **argv) { - std::vector ClangArgv(ClangArgs.size()); - std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(), - [](const std::string &s) -> const char * { return s.data(); }); -- llvm::InitializeNativeTarget(); -- llvm::InitializeNativeTargetAsmPrinter(); -+ // Initialize all targets (required for device offloading) -+ llvm::InitializeAllTargetInfos(); -+ llvm::InitializeAllTargets(); -+ llvm::InitializeAllTargetMCs(); -+ llvm::InitializeAllAsmPrinters(); - - if (OptHostSupportsJit) { - auto J = llvm::orc::LLJITBuilder().create(); -@@ -90,9 +97,30 @@ int main(int argc, const char **argv) { - return 0; - } - -+ clang::IncrementalCompilerBuilder CB; -+ CB.SetCompilerArgs(ClangArgv); -+ -+ std::unique_ptr DeviceCI; -+ if (CudaEnabled) { -+ if (!CudaPath.empty()) -+ CB.SetCudaSDK(CudaPath); -+ -+ if (OffloadArch.empty()) { -+ OffloadArch = "sm_35"; -+ } -+ CB.SetOffloadArch(OffloadArch); -+ -+ DeviceCI = ExitOnErr(CB.CreateCudaDevice()); -+ } -+ - // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It - // can replace the boilerplate code for creation of the compiler instance. -- auto CI = ExitOnErr(clang::IncrementalCompilerBuilder::create(ClangArgv)); -+ std::unique_ptr CI; -+ if (CudaEnabled) { -+ CI = ExitOnErr(CB.CreateCudaHost()); -+ } else { -+ CI = ExitOnErr(CB.CreateCpp()); -+ } - - // Set an error handler, so that any LLVM backend diagnostics go through our - // error handler. -@@ -101,8 +129,23 @@ int main(int argc, const char **argv) { - - // Load any requested plugins. - CI->LoadRequestedPlugins(); -+ if (CudaEnabled) -+ DeviceCI->LoadRequestedPlugins(); -+ -+ std::unique_ptr Interp; -+ if (CudaEnabled) { -+ Interp = ExitOnErr( -+ clang::Interpreter::createWithCUDA(std::move(CI), std::move(DeviceCI))); -+ -+ if (CudaPath.empty()) { -+ ExitOnErr(Interp->LoadDynamicLibrary("libcudart.so")); -+ } else { -+ auto CudaRuntimeLibPath = CudaPath + "/lib/libcudart.so"; -+ ExitOnErr(Interp->LoadDynamicLibrary(CudaRuntimeLibPath.c_str())); -+ } -+ } else -+ Interp = ExitOnErr(clang::Interpreter::create(std::move(CI))); - -- auto Interp = ExitOnErr(clang::Interpreter::create(std::move(CI))); - for (const std::string &input : OptInputs) { - if (auto Err = Interp->ParseAndExecute(input)) - llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); -diff --git a/clang/unittests/Interpreter/ExceptionTests/InterpreterExceptionTest.cpp b/clang/unittests/Interpreter/ExceptionTests/InterpreterExceptionTest.cpp -index 6d0433a98..63bb69038 100644 ---- a/clang/unittests/Interpreter/ExceptionTests/InterpreterExceptionTest.cpp -+++ b/clang/unittests/Interpreter/ExceptionTests/InterpreterExceptionTest.cpp -@@ -38,7 +38,9 @@ createInterpreter(const Args &ExtraArgs = {}, - DiagnosticConsumer *Client = nullptr) { - Args ClangArgs = {"-Xclang", "-emit-llvm-only"}; - ClangArgs.insert(ClangArgs.end(), ExtraArgs.begin(), ExtraArgs.end()); -- auto CI = cantFail(clang::IncrementalCompilerBuilder::create(ClangArgs)); -+ auto CB = clang::IncrementalCompilerBuilder(); -+ CB.SetCompilerArgs(ClangArgs); -+ auto CI = cantFail(CB.CreateCpp()); - if (Client) - CI->getDiagnostics().setClient(Client, /*ShouldOwnClient=*/false); - return cantFail(clang::Interpreter::create(std::move(CI))); -diff --git a/clang/unittests/Interpreter/IncrementalProcessingTest.cpp b/clang/unittests/Interpreter/IncrementalProcessingTest.cpp -index b7ad468e1..6d477c9ab 100644 ---- a/clang/unittests/Interpreter/IncrementalProcessingTest.cpp -+++ b/clang/unittests/Interpreter/IncrementalProcessingTest.cpp -@@ -52,7 +52,9 @@ const Function *getGlobalInit(llvm::Module *M) { - - TEST(IncrementalProcessing, EmitCXXGlobalInitFunc) { - std::vector ClangArgv = {"-Xclang", "-emit-llvm-only"}; -- auto CI = llvm::cantFail(IncrementalCompilerBuilder::create(ClangArgv)); -+ auto CB = clang::IncrementalCompilerBuilder(); -+ CB.SetCompilerArgs(ClangArgv); -+ auto CI = cantFail(CB.CreateCpp()); - auto Interp = llvm::cantFail(Interpreter::create(std::move(CI))); - - std::array PTUs; -diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp b/clang/unittests/Interpreter/InterpreterTest.cpp -index 330fd18ab..338003cd9 100644 ---- a/clang/unittests/Interpreter/InterpreterTest.cpp -+++ b/clang/unittests/Interpreter/InterpreterTest.cpp -@@ -46,7 +46,9 @@ createInterpreter(const Args &ExtraArgs = {}, - DiagnosticConsumer *Client = nullptr) { - Args ClangArgs = {"-Xclang", "-emit-llvm-only"}; - ClangArgs.insert(ClangArgs.end(), ExtraArgs.begin(), ExtraArgs.end()); -- auto CI = cantFail(clang::IncrementalCompilerBuilder::create(ClangArgs)); -+ auto CB = clang::IncrementalCompilerBuilder(); -+ CB.SetCompilerArgs(ClangArgs); -+ auto CI = cantFail(CB.CreateCpp()); - if (Client) - CI->getDiagnostics().setClient(Client, /*ShouldOwnClient=*/false); - return cantFail(clang::Interpreter::create(std::move(CI))); diff --git a/interpreter/CppInterOp/patches/llvm/clang16-3-WeakRef.patch b/interpreter/CppInterOp/patches/llvm/clang16-3-WeakRef.patch deleted file mode 100644 index 26172ef4b22f8..0000000000000 --- a/interpreter/CppInterOp/patches/llvm/clang16-3-WeakRef.patch +++ /dev/null @@ -1,33 +0,0 @@ -diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp -index 978e4d404..683449958 100644 ---- a/clang/lib/CodeGen/CodeGenModule.cpp -+++ b/clang/lib/CodeGen/CodeGenModule.cpp -@@ -7206,7 +7206,6 @@ void CodeGenModule::moveLazyEmissionStates(CodeGenModule *NewBuilder) { - "Newly created module should not have manglings"); - NewBuilder->Manglings = std::move(Manglings); - -- assert(WeakRefReferences.empty() && "Not all WeakRefRefs have been applied"); - NewBuilder->WeakRefReferences = std::move(WeakRefReferences); - - NewBuilder->TBAA = std::move(TBAA); -diff --git a/clang/test/Interpreter/execute-weak.cpp b/clang/test/Interpreter/execute-weak.cpp -index 5b343512c..3c6978165 100644 ---- a/clang/test/Interpreter/execute-weak.cpp -+++ b/clang/test/Interpreter/execute-weak.cpp -@@ -2,11 +2,16 @@ - // RUN: clang-repl "int i = 10;" 'extern "C" int printf(const char*,...);' \ - // RUN: 'auto r1 = printf("i = %d\n", i);' | FileCheck --check-prefix=CHECK-DRIVER %s - // CHECK-DRIVER: i = 10 -+// - // UNSUPPORTED: system-aix, system-windows - // RUN: cat %s | clang-repl | FileCheck %s -+ - extern "C" int printf(const char *, ...); - int __attribute__((weak)) bar() { return 42; } - auto r4 = printf("bar() = %d\n", bar()); - // CHECK: bar() = 42 - -+int a = 12; -+static __typeof(a) b __attribute__((__weakref__("a"))); -+int c = b; - %quit diff --git a/interpreter/CppInterOp/patches/llvm/clang17-1-NewOperator.patch b/interpreter/CppInterOp/patches/llvm/clang17-1-NewOperator.patch deleted file mode 100644 index fd32d792c5326..0000000000000 --- a/interpreter/CppInterOp/patches/llvm/clang17-1-NewOperator.patch +++ /dev/null @@ -1,205 +0,0 @@ -From a3f213ef4a7e293152c272cce78ad5d10a3ede52 Mon Sep 17 00:00:00 2001 -From: Vassil Vassilev -Date: Fri, 22 Dec 2023 08:38:23 +0000 -Subject: [PATCH] [clang-repl] Add a interpreter-specific overload of operator - new for C++. - -This patch brings back the basic support for C by inserting the required for -value printing runtime only when we are in C++ mode. Additionally, it defines -a new overload of operator placement new because we can't really forward declare -it in a library-agnostic way. - -Fixes the issue described in llvm/llvm-project#69072. ---- - clang/include/clang/Interpreter/Interpreter.h | 4 +-- - clang/lib/Interpreter/Interpreter.cpp | 33 +++++++++++++++---- - clang/test/Interpreter/incremental-mode.cpp | 3 +- - .../unittests/Interpreter/InterpreterTest.cpp | 29 +++------------- - 4 files changed, 36 insertions(+), 33 deletions(-) - -diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h -index 01858dfcc90ac5..292fa566ae7037 100644 ---- a/clang/include/clang/Interpreter/Interpreter.h -+++ b/clang/include/clang/Interpreter/Interpreter.h -@@ -129,7 +129,7 @@ class Interpreter { - llvm::Expected - getSymbolAddressFromLinkerName(llvm::StringRef LinkerName) const; - -- enum InterfaceKind { NoAlloc, WithAlloc, CopyArray }; -+ enum InterfaceKind { NoAlloc, WithAlloc, CopyArray, NewTag }; - - const llvm::SmallVectorImpl &getValuePrintingInfo() const { - return ValuePrintingInfo; -@@ -144,7 +144,7 @@ class Interpreter { - - llvm::DenseMap Dtors; - -- llvm::SmallVector ValuePrintingInfo; -+ llvm::SmallVector ValuePrintingInfo; - }; - } // namespace clang - -diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp -index c9fcef5b5b5af1..9f97a3c6b0be9e 100644 ---- a/clang/lib/Interpreter/Interpreter.cpp -+++ b/clang/lib/Interpreter/Interpreter.cpp -@@ -248,7 +248,7 @@ Interpreter::~Interpreter() { - // can't find the precise resource directory in unittests so we have to hard - // code them. - const char *const Runtimes = R"( -- void* operator new(__SIZE_TYPE__, void* __p) noexcept; -+#ifdef __cplusplus - void *__clang_Interpreter_SetValueWithAlloc(void*, void*, void*); - void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*); - void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, void*); -@@ -256,15 +256,18 @@ const char *const Runtimes = R"( - void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, double); - void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, long double); - void __clang_Interpreter_SetValueNoAlloc(void*,void*,void*,unsigned long long); -+ struct __clang_Interpreter_NewTag{} __ci_newtag; -+ void* operator new(__SIZE_TYPE__, void* __p, __clang_Interpreter_NewTag) noexcept; - template - void __clang_Interpreter_SetValueCopyArr(T* Src, void* Placement, unsigned long Size) { - for (auto Idx = 0; Idx < Size; ++Idx) -- new ((void*)(((T*)Placement) + Idx)) T(Src[Idx]); -+ new ((void*)(((T*)Placement) + Idx), __ci_newtag) T(Src[Idx]); - } - template - void __clang_Interpreter_SetValueCopyArr(const T (*Src)[N], void* Placement, unsigned long Size) { - __clang_Interpreter_SetValueCopyArr(Src[0], Placement, Size); - } -+#endif // __cplusplus - )"; - - llvm::Expected> -@@ -279,7 +282,7 @@ Interpreter::create(std::unique_ptr CI) { - if (!PTU) - return PTU.takeError(); - -- Interp->ValuePrintingInfo.resize(3); -+ Interp->ValuePrintingInfo.resize(4); - // FIXME: This is a ugly hack. Undo command checks its availability by looking - // at the size of the PTU list. However we have parsed something in the - // beginning of the REPL so we have to mark them as 'Irrevocable'. -@@ -500,7 +503,7 @@ Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) { - static constexpr llvm::StringRef MagicRuntimeInterface[] = { - "__clang_Interpreter_SetValueNoAlloc", - "__clang_Interpreter_SetValueWithAlloc", -- "__clang_Interpreter_SetValueCopyArr"}; -+ "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"}; - - bool Interpreter::FindRuntimeInterface() { - if (llvm::all_of(ValuePrintingInfo, [](Expr *E) { return E != nullptr; })) -@@ -530,6 +533,9 @@ bool Interpreter::FindRuntimeInterface() { - if (!LookupInterface(ValuePrintingInfo[CopyArray], - MagicRuntimeInterface[CopyArray])) - return false; -+ if (!LookupInterface(ValuePrintingInfo[NewTag], -+ MagicRuntimeInterface[NewTag])) -+ return false; - return true; - } - -@@ -607,7 +613,9 @@ class RuntimeInterfaceBuilder - .getValuePrintingInfo()[Interpreter::InterfaceKind::CopyArray], - SourceLocation(), Args, SourceLocation()); - } -- Expr *Args[] = {AllocCall.get()}; -+ Expr *Args[] = { -+ AllocCall.get(), -+ Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NewTag]}; - ExprResult CXXNewCall = S.BuildCXXNew( - E->getSourceRange(), - /*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args, -@@ -628,8 +636,9 @@ class RuntimeInterfaceBuilder - Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NoAlloc], - E->getBeginLoc(), Args, E->getEndLoc()); - } -+ default: -+ llvm_unreachable("Unhandled Interpreter::InterfaceKind"); - } -- llvm_unreachable("Unhandled Interpreter::InterfaceKind"); - } - - Interpreter::InterfaceKind VisitRecordType(const RecordType *Ty) { -@@ -814,3 +823,15 @@ __clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType, - VRef = Value(static_cast(This), OpaqueType); - VRef.setLongDouble(Val); - } -+ -+// A trampoline to work around the fact that operator placement new cannot -+// really be forward declared due to libc++ and libstdc++ declaration mismatch. -+// FIXME: __clang_Interpreter_NewTag is ODR violation because we get the same -+// definition in the interpreter runtime. We should move it in a runtime header -+// which gets included by the interpreter and here. -+struct __clang_Interpreter_NewTag {}; -+REPL_EXTERNAL_VISIBILITY void * -+operator new(size_t __sz, void *__p, __clang_Interpreter_NewTag) noexcept { -+ // Just forward to the standard operator placement new. -+ return operator new(__sz, __p); -+} -diff --git a/clang/test/Interpreter/incremental-mode.cpp b/clang/test/Interpreter/incremental-mode.cpp -index e6350d237ef578..d63cee0dd6d15f 100644 ---- a/clang/test/Interpreter/incremental-mode.cpp -+++ b/clang/test/Interpreter/incremental-mode.cpp -@@ -1,3 +1,4 @@ - // RUN: clang-repl -Xcc -E --// RUN: clang-repl -Xcc -emit-llvm -+// RUN: clang-repl -Xcc -emit-llvm -+// RUN: clang-repl -Xcc -xc - // expected-no-diagnostics -diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp b/clang/unittests/Interpreter/InterpreterTest.cpp -index 5f2911e9a7adad..1e0854b3c4af46 100644 ---- a/clang/unittests/Interpreter/InterpreterTest.cpp -+++ b/clang/unittests/Interpreter/InterpreterTest.cpp -@@ -248,28 +248,10 @@ TEST(IncrementalProcessing, FindMangledNameSymbol) { - #endif // _WIN32 - } - --static void *AllocateObject(TypeDecl *TD, Interpreter &Interp) { -+static Value AllocateObject(TypeDecl *TD, Interpreter &Interp) { - std::string Name = TD->getQualifiedNameAsString(); -- const clang::Type *RDTy = TD->getTypeForDecl(); -- clang::ASTContext &C = Interp.getCompilerInstance()->getASTContext(); -- size_t Size = C.getTypeSize(RDTy); -- void *Addr = malloc(Size); -- -- // Tell the interpreter to call the default ctor with this memory. Synthesize: -- // new (loc) ClassName; -- static unsigned Counter = 0; -- std::stringstream SS; -- SS << "auto _v" << Counter++ << " = " -- << "new ((void*)" -- // Windows needs us to prefix the hexadecimal value of a pointer with '0x'. -- << std::hex << std::showbase << (size_t)Addr << ")" << Name << "();"; -- -- auto R = Interp.ParseAndExecute(SS.str()); -- if (!R) { -- free(Addr); -- return nullptr; -- } -- -+ Value Addr; -+ cantFail(Interp.ParseAndExecute("new " + Name + "()", &Addr)); - return Addr; - } - -@@ -317,7 +299,7 @@ TEST(IncrementalProcessing, InstantiateTemplate) { - } - - TypeDecl *TD = cast(LookupSingleName(*Interp, "A")); -- void *NewA = AllocateObject(TD, *Interp); -+ Value NewA = AllocateObject(TD, *Interp); - - // Find back the template specialization - VarDecl *VD = static_cast(*PTUDeclRange.begin()); -@@ -328,8 +310,7 @@ TEST(IncrementalProcessing, InstantiateTemplate) { - typedef int (*TemplateSpecFn)(void *); - auto fn = - cantFail(Interp->getSymbolAddress(MangledName)).toPtr(); -- EXPECT_EQ(42, fn(NewA)); -- free(NewA); -+ EXPECT_EQ(42, fn(NewA.getPtr())); - } - - #ifdef CLANG_INTERPRETER_NO_SUPPORT_EXEC diff --git a/interpreter/CppInterOp/patches/llvm/emscripten-clang19-4-enable_exception_handling.patch b/interpreter/CppInterOp/patches/llvm/emscripten-clang19-4-enable_exception_handling.patch new file mode 100644 index 0000000000000..36b36ba47ce02 --- /dev/null +++ b/interpreter/CppInterOp/patches/llvm/emscripten-clang19-4-enable_exception_handling.patch @@ -0,0 +1,67 @@ +diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp +index 985d0b7c0..20a727893 100644 +--- a/clang/lib/Interpreter/Interpreter.cpp ++++ b/clang/lib/Interpreter/Interpreter.cpp +@@ -135,6 +135,48 @@ CreateCI(const llvm::opt::ArgStringList &Argv) { + return std::move(Clang); + } + ++static llvm::Error HandleFrontendOptions(const CompilerInstance &CI) { ++ const auto &FrontendOpts = CI.getFrontendOpts(); ++ ++ if (FrontendOpts.ShowHelp) { ++ driver::getDriverOptTable().printHelp( ++ llvm::outs(), "clang -cc1 [options] file...", ++ "LLVM 'Clang' Compiler: http://clang.llvm.org", ++ /*ShowHidden=*/false, /*ShowAllAliases=*/false, ++ llvm::opt::Visibility(driver::options::CC1Option)); ++ return llvm::createStringError(llvm::errc::not_supported, "Help displayed"); ++ } ++ ++ if (FrontendOpts.ShowVersion) { ++ llvm::cl::PrintVersionMessage(); ++ return llvm::createStringError(llvm::errc::not_supported, ++ "Version displayed"); ++ } ++ ++ if (!FrontendOpts.LLVMArgs.empty()) { ++ unsigned NumArgs = FrontendOpts.LLVMArgs.size(); ++ auto Args = std::make_unique(NumArgs + 2); ++ Args[0] = "clang-repl (LLVM option parsing)"; ++ for (unsigned i = 0; i != NumArgs; ++i) { ++ Args[i + 1] = FrontendOpts.LLVMArgs[i].c_str(); ++ // remove the leading '-' from the option name ++ if (Args[i + 1][0] == '-') { ++ auto *option = static_cast *>( ++ llvm::cl::getRegisteredOptions()[Args[i + 1] + 1]); ++ if (option) { ++ option->setInitialValue(true); ++ } else { ++ llvm::errs() << "Unknown LLVM option: " << Args[i + 1] << "\n"; ++ } ++ } ++ } ++ Args[NumArgs + 1] = nullptr; ++ llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get()); ++ } ++ ++ return llvm::Error::success(); ++} ++ + } // anonymous namespace + + llvm::Expected> +@@ -306,7 +348,12 @@ const char *const Runtimes = R"( + + llvm::Expected> + Interpreter::create(std::unique_ptr CI) { +- llvm::Error Err = llvm::Error::success(); ++ ++ llvm::Error Err = HandleFrontendOptions(*CI); ++ if (Err) { ++ return std::move(Err); ++ } ++ + auto Interp = + std::unique_ptr(new Interpreter(std::move(CI), Err)); + if (Err) diff --git a/interpreter/CppInterOp/patches/llvm/emscripten-clang20-3-enable_exception_handling.patch b/interpreter/CppInterOp/patches/llvm/emscripten-clang20-3-enable_exception_handling.patch new file mode 100644 index 0000000000000..85e369c2cd369 --- /dev/null +++ b/interpreter/CppInterOp/patches/llvm/emscripten-clang20-3-enable_exception_handling.patch @@ -0,0 +1,67 @@ +diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp +index 3b81f9d70..2704edb8a 100644 +--- a/clang/lib/Interpreter/Interpreter.cpp ++++ b/clang/lib/Interpreter/Interpreter.cpp +@@ -142,6 +142,48 @@ CreateCI(const llvm::opt::ArgStringList &Argv) { + return std::move(Clang); + } + ++static llvm::Error HandleFrontendOptions(const CompilerInstance &CI) { ++ const auto &FrontendOpts = CI.getFrontendOpts(); ++ ++ if (FrontendOpts.ShowHelp) { ++ driver::getDriverOptTable().printHelp( ++ llvm::outs(), "clang -cc1 [options] file...", ++ "LLVM 'Clang' Compiler: http://clang.llvm.org", ++ /*ShowHidden=*/false, /*ShowAllAliases=*/false, ++ llvm::opt::Visibility(driver::options::CC1Option)); ++ return llvm::createStringError(llvm::errc::not_supported, "Help displayed"); ++ } ++ ++ if (FrontendOpts.ShowVersion) { ++ llvm::cl::PrintVersionMessage(); ++ return llvm::createStringError(llvm::errc::not_supported, ++ "Version displayed"); ++ } ++ ++ if (!FrontendOpts.LLVMArgs.empty()) { ++ unsigned NumArgs = FrontendOpts.LLVMArgs.size(); ++ auto Args = std::make_unique(NumArgs + 2); ++ Args[0] = "clang-repl (LLVM option parsing)"; ++ for (unsigned i = 0; i != NumArgs; ++i) { ++ Args[i + 1] = FrontendOpts.LLVMArgs[i].c_str(); ++ // remove the leading '-' from the option name ++ if (Args[i + 1][0] == '-') { ++ auto *option = static_cast *>( ++ llvm::cl::getRegisteredOptions()[Args[i + 1] + 1]); ++ if (option) { ++ option->setInitialValue(true); ++ } else { ++ llvm::errs() << "Unknown LLVM option: " << Args[i + 1] << "\n"; ++ } ++ } ++ } ++ Args[NumArgs + 1] = nullptr; ++ llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get()); ++ } ++ ++ return llvm::Error::success(); ++} ++ + } // anonymous namespace + + namespace clang { +@@ -456,7 +498,12 @@ const char *const Runtimes = R"( + + llvm::Expected> + Interpreter::create(std::unique_ptr CI) { +- llvm::Error Err = llvm::Error::success(); ++ ++ llvm::Error Err = HandleFrontendOptions(*CI); ++ if (Err) { ++ return std::move(Err); ++ } ++ + auto Interp = + std::unique_ptr(new Interpreter(std::move(CI), Err)); + if (Err) diff --git a/interpreter/CppInterOp/scripts/browser_tests_safari.py b/interpreter/CppInterOp/scripts/browser_tests_safari.py new file mode 100644 index 0000000000000..4210c22a5636a --- /dev/null +++ b/interpreter/CppInterOp/scripts/browser_tests_safari.py @@ -0,0 +1,9 @@ +import sys +import time +from selenium import webdriver + +driver = webdriver.Safari() +driver.get("http://localhost:6931/" + sys.argv[1]) + +time.sleep(60) +driver.quit() diff --git a/interpreter/CppInterOp/unittests/CppInterOp/CMakeLists.txt b/interpreter/CppInterOp/unittests/CppInterOp/CMakeLists.txt index 8cc63038acf0a..4b4b43bdd2936 100644 --- a/interpreter/CppInterOp/unittests/CppInterOp/CMakeLists.txt +++ b/interpreter/CppInterOp/unittests/CppInterOp/CMakeLists.txt @@ -97,7 +97,7 @@ target_link_libraries(DynamicLibraryManagerTests ) if(EMSCRIPTEN) - set(TEST_SHARED_LIBRARY_PATH "${CMAKE_CURRENT_BINARY_DIR}/TestSharedLib/unittests/bin/Release/") + set(TEST_SHARED_LIBRARY_PATH "${CMAKE_CURRENT_BINARY_DIR}/TestSharedLib/unittests/bin/$/") string(REPLACE "@" "@@" ESCAPED_TEST_SHARED_LIBRARY_PATH "${TEST_SHARED_LIBRARY_PATH}") # Check explanation of Emscripten-specific link flags for CppInterOpTests above for DynamicLibraryManagerTests as well. target_link_options(DynamicLibraryManagerTests diff --git a/interpreter/CppInterOp/unittests/CppInterOp/FunctionReflectionTest.cpp b/interpreter/CppInterOp/unittests/CppInterOp/FunctionReflectionTest.cpp index d5cc81e78b1f2..b056c77cef048 100644 --- a/interpreter/CppInterOp/unittests/CppInterOp/FunctionReflectionTest.cpp +++ b/interpreter/CppInterOp/unittests/CppInterOp/FunctionReflectionTest.cpp @@ -22,6 +22,8 @@ using namespace clang; TEST(FunctionReflectionTest, GetClassMethods) { std::vector Decls; std::string code = R"( + class A; + class A { public: int f1(int a, int b) { return a + b; } @@ -70,7 +72,7 @@ TEST(FunctionReflectionTest, GetClassMethods) { EXPECT_EQ(get_method_name(methods0[10]), "inline A::~A()"); std::vector methods1; - Cpp::GetClassMethods(Decls[1], methods1); + Cpp::GetClassMethods(Decls[2], methods1); EXPECT_EQ(methods0.size(), methods1.size()); EXPECT_EQ(methods0[0], methods1[0]); EXPECT_EQ(methods0[1], methods1[1]); @@ -79,7 +81,7 @@ TEST(FunctionReflectionTest, GetClassMethods) { EXPECT_EQ(methods0[4], methods1[4]); std::vector methods2; - Cpp::GetClassMethods(Decls[2], methods2); + Cpp::GetClassMethods(Decls[3], methods2); EXPECT_EQ(methods2.size(), 6); EXPECT_EQ(get_method_name(methods2[0]), "B::B(int n)"); @@ -90,27 +92,70 @@ TEST(FunctionReflectionTest, GetClassMethods) { EXPECT_EQ(get_method_name(methods2[5]), "inline B &B::operator=(B &&)"); std::vector methods3; - Cpp::GetClassMethods(Decls[3], methods3); + Cpp::GetClassMethods(Decls[4], methods3); EXPECT_EQ(methods3.size(), 9); - EXPECT_EQ(get_method_name(methods3[0]), "B::B(int n)"); - EXPECT_EQ(get_method_name(methods3[1]), "inline constexpr B::B(const B &)"); - EXPECT_EQ(get_method_name(methods3[3]), "inline C::C()"); - EXPECT_EQ(get_method_name(methods3[4]), "inline constexpr C::C(const C &)"); - EXPECT_EQ(get_method_name(methods3[5]), "inline constexpr C::C(C &&)"); - EXPECT_EQ(get_method_name(methods3[6]), "inline C &C::operator=(const C &)"); - EXPECT_EQ(get_method_name(methods3[7]), "inline C &C::operator=(C &&)"); - EXPECT_EQ(get_method_name(methods3[8]), "inline C::~C()"); + EXPECT_EQ(get_method_name(methods3[0]), "inline C::C()"); + EXPECT_EQ(get_method_name(methods3[1]), "inline constexpr C::C(const C &)"); + EXPECT_EQ(get_method_name(methods3[2]), "inline constexpr C::C(C &&)"); + EXPECT_EQ(get_method_name(methods3[3]), "inline C &C::operator=(const C &)"); + EXPECT_EQ(get_method_name(methods3[4]), "inline C &C::operator=(C &&)"); + EXPECT_EQ(get_method_name(methods3[5]), "inline C::~C()"); + EXPECT_EQ(get_method_name(methods3[6]), "inline C::B(int)"); + EXPECT_EQ(get_method_name(methods3[7]), "inline constexpr C::B(const B &)"); // Should not crash. std::vector methods4; - Cpp::GetClassMethods(Decls[4], methods4); + Cpp::GetClassMethods(Decls[5], methods4); EXPECT_EQ(methods4.size(), 0); std::vector methods5; Cpp::GetClassMethods(nullptr, methods5); EXPECT_EQ(methods5.size(), 0); + Decls.clear(); + code = R"( + struct T { + template + T(Tmpl x) : n(x) {} + T(const T&) = delete; + T(T&&) = delete; + void fn() {} + int n; + }; + + struct TT: public T { + using T::T; + using T::fn; + }; + )"; + + GetAllTopLevelDecls(code, Decls); + EXPECT_EQ(Decls.size(), 2); + + std::vector templ_methods1; + Cpp::GetClassMethods(Decls[0], templ_methods1); + EXPECT_EQ(templ_methods1.size(), 5); + EXPECT_EQ(get_method_name(templ_methods1[0]), "T::T(const T &) = delete"); + EXPECT_EQ(get_method_name(templ_methods1[1]), "T::T(T &&) = delete"); + EXPECT_EQ(get_method_name(templ_methods1[2]), "void T::fn()"); + EXPECT_EQ(get_method_name(templ_methods1[3]), + "inline T &T::operator=(const T &)"); + EXPECT_EQ(get_method_name(templ_methods1[4]), "inline T::~T()"); + + std::vector templ_methods2; + Cpp::GetClassMethods(Decls[1], templ_methods2); + EXPECT_EQ(templ_methods2.size(), 7); + EXPECT_EQ(get_method_name(templ_methods2[0]), "void T::fn()"); + EXPECT_EQ(get_method_name(templ_methods2[1]), "inline TT::TT()"); + EXPECT_EQ(get_method_name(templ_methods2[2]), "inline TT::TT(const TT &)"); + EXPECT_EQ(get_method_name(templ_methods2[3]), "inline TT::TT(TT &&)"); + EXPECT_EQ(get_method_name(templ_methods2[4]), + "inline TT &TT::operator=(const TT &)"); + EXPECT_EQ(get_method_name(templ_methods2[5]), + "inline TT &TT::operator=(TT &&)"); + EXPECT_EQ(get_method_name(templ_methods2[6]), "inline TT::~TT()"); + // C API auto* I = clang_createInterpreterFromRawPtr(Cpp::GetInterpreter()); auto C_API_SHIM = [&](Cpp::TCppFunction_t method) { @@ -517,7 +562,7 @@ TEST(FunctionReflectionTest, GetFunctionRequiredArgs) { } TEST(FunctionReflectionTest, GetFunctionArgType) { - std::vector Decls, SubDecls; + std::vector Decls; std::string code = R"( void f1(int i, double d, long l, char ch) {} void f2(const int i, double d[], long *l, char ch[4]) {} @@ -537,7 +582,7 @@ TEST(FunctionReflectionTest, GetFunctionArgType) { } TEST(FunctionReflectionTest, GetFunctionSignature) { - std::vector Decls, SubDecls; + std::vector Decls; std::string code = R"( class C { void f(int i, double d, long l = 0, char ch = 'a') {} @@ -581,7 +626,8 @@ TEST(FunctionReflectionTest, GetFunctionSignature) { } TEST(FunctionReflectionTest, IsTemplatedFunction) { - std::vector Decls, SubDeclsC1, SubDeclsC2; + std::vector Decls; + std::vector SubDeclsC1; std::string code = R"( void f1(int a) {} @@ -1403,7 +1449,7 @@ TEST(FunctionReflectionTest, GetFunctionAddress) { #ifdef _WIN32 GTEST_SKIP() << "Disabled on Windows. Needs fixing."; #endif - std::vector Decls, SubDecls; + std::vector Decls; std::string code = "int f1(int i) { return i * i; }"; std::vector interpreter_args = {"-include", "new"}; @@ -1423,6 +1469,23 @@ TEST(FunctionReflectionTest, GetFunctionAddress) { EXPECT_EQ(address.str(), output); EXPECT_FALSE(Cpp::GetFunctionAddress(Cpp::GetGlobalScope())); + + Interp->declare(R"( + template + T add1(T t) { return t + 1; } + )"); + + std::vector funcs; + Cpp::GetClassTemplatedMethods("add1", Cpp::GetGlobalScope(), funcs); + EXPECT_EQ(funcs.size(), 1); + + ASTContext& C = Interp->getCI()->getASTContext(); + std::vector argument = {C.DoubleTy.getAsOpaquePtr()}; + Cpp::TCppScope_t add1_double = + Cpp::InstantiateTemplate(funcs[0], argument.data(), argument.size()); + EXPECT_TRUE(add1_double); + + EXPECT_TRUE(Cpp::GetFunctionAddress(add1_double)); } TEST(FunctionReflectionTest, IsVirtualMethod) { @@ -1653,7 +1716,6 @@ TEST(FunctionReflectionTest, GetFunctionCallWrapper) { FCI4.Invoke(&ret4); EXPECT_EQ(ret4, 4); -#if CLANG_VERSION_MAJOR > 16 Cpp::JitCall FCI5 = Cpp::MakeFunctionCallable(Cpp::GetNamed("f5", Cpp::GetNamed("NS"))); EXPECT_TRUE(FCI5.getKind() == Cpp::JitCall::kGenericCall); @@ -1663,7 +1725,6 @@ TEST(FunctionReflectionTest, GetFunctionCallWrapper) { FCI5.Invoke((void*)&callback); EXPECT_TRUE(callback); EXPECT_EQ(callback(), 3); -#endif // FIXME: Do we need to support private ctors? Interp->process(R"( @@ -1740,9 +1801,6 @@ TEST(FunctionReflectionTest, GetFunctionCallWrapper) { set_5_f.Invoke(nullptr, {set_5_args, 1}); EXPECT_EQ(b, 5); -#if CLANG_VERSION_MAJOR > 16 - // typedef resolution testing - // supported for clang version >16 only Interp->process(R"( class TypedefToPrivateClass { private: @@ -1770,7 +1828,6 @@ TEST(FunctionReflectionTest, GetFunctionCallWrapper) { void* res = nullptr; FCI_f.Invoke(&res, {nullptr, 0}); EXPECT_TRUE(res); -#endif // templated operators Interp->process(R"( @@ -2009,6 +2066,124 @@ TEST(FunctionReflectionTest, GetFunctionCallWrapper) { auto bar_callable = Cpp::MakeFunctionCallable(bar); EXPECT_EQ(bar_callable.getKind(), Cpp::JitCall::kGenericCall); + + Cpp::Declare(R"( + struct A { + A() {} + A(A&& other) {}; + }; + + A consumable; + + template + void consume(T t) {} + )"); + + unresolved_candidate_methods.clear(); + Cpp::GetClassTemplatedMethods("consume", Cpp::GetGlobalScope(), + unresolved_candidate_methods); + EXPECT_EQ(unresolved_candidate_methods.size(), 1); + + Cpp::TCppScope_t consume = Cpp::BestOverloadFunctionMatch( + unresolved_candidate_methods, {}, + {Cpp::GetVariableType(Cpp::GetNamed("consumable"))}); + EXPECT_TRUE(consume); + + auto consume_callable = Cpp::MakeFunctionCallable(consume); + EXPECT_EQ(consume_callable.getKind(), Cpp::JitCall::kGenericCall); + + Cpp::Declare(R"( + template + struct Product {}; + + template + struct KlassProduct { + template + const Product + operator*(const KlassProduct &other) const { return Product(); } + + template + const Product + operator*(const KlassProduct &other) const { return Product(); } + }; + )"); + + Cpp::TCppScope_t KlassProduct = Cpp::GetNamed("KlassProduct"); + EXPECT_TRUE(KlassProduct); + + Cpp::TCppScope_t KlassProduct_int = + Cpp::InstantiateTemplate(KlassProduct, &TAI, 1); + EXPECT_TRUE(KlassProduct_int); + TAI = Cpp::TemplateArgInfo(Cpp::GetType("float")); + Cpp::TCppScope_t KlassProduct_float = + Cpp::InstantiateTemplate(KlassProduct, &TAI, 1); + EXPECT_TRUE(KlassProduct_float); + + operators.clear(); + Cpp::GetOperator(KlassProduct_int, Cpp::OP_Star, operators); + EXPECT_EQ(operators.size(), 2); + + op = Cpp::BestOverloadFunctionMatch( + operators, {}, {{Cpp::GetTypeFromScope(KlassProduct_float)}}); + EXPECT_TRUE(op); + + auto op_callable = Cpp::MakeFunctionCallable(op); + EXPECT_EQ(op_callable.getKind(), Cpp::JitCall::kGenericCall); + + Cpp::Declare(R"( + enum class MyEnum { A, B, C }; + template + class TemplatedEnum {}; + + namespace MyNameSpace { + enum class MyEnum { A, B, C }; + template + class TemplatedEnum {}; + } + )"); + + Cpp::TCppScope_t TemplatedEnum = Cpp::GetScope("TemplatedEnum"); + EXPECT_TRUE(TemplatedEnum); + + auto TAI_enum = + Cpp::TemplateArgInfo(Cpp::GetTypeFromScope(Cpp::GetNamed("MyEnum")), "1"); + Cpp::TCppScope_t TemplatedEnum_instantiated = + Cpp::InstantiateTemplate(TemplatedEnum, &TAI_enum, 1); + EXPECT_TRUE(TemplatedEnum_instantiated); + + Cpp::TCppObject_t obj = Cpp::Construct(TemplatedEnum_instantiated); + EXPECT_TRUE(obj); + Cpp::Destruct(obj, TemplatedEnum_instantiated); + obj = nullptr; + + Cpp::TCppScope_t MyNameSpace_TemplatedEnum = + Cpp::GetScope("TemplatedEnum", Cpp::GetScope("MyNameSpace")); + EXPECT_TRUE(TemplatedEnum); + + TAI_enum = Cpp::TemplateArgInfo(Cpp::GetTypeFromScope(Cpp::GetNamed( + "MyEnum", Cpp::GetScope("MyNameSpace"))), + "1"); + Cpp::TCppScope_t MyNameSpace_TemplatedEnum_instantiated = + Cpp::InstantiateTemplate(MyNameSpace_TemplatedEnum, &TAI_enum, 1); + EXPECT_TRUE(TemplatedEnum_instantiated); + + obj = Cpp::Construct(MyNameSpace_TemplatedEnum_instantiated); + EXPECT_TRUE(obj); + Cpp::Destruct(obj, MyNameSpace_TemplatedEnum_instantiated); + obj = nullptr; + + Cpp::Declare(R"( + auto get_fn(int x) { return [x](int y){ return x + y; }; } + )"); + + Cpp::TCppScope_t get_fn = Cpp::GetNamed("get_fn"); + EXPECT_TRUE(get_fn); + + auto get_fn_callable = Cpp::MakeFunctionCallable(get_fn); + EXPECT_EQ(get_fn_callable.getKind(), Cpp::JitCall::kGenericCall); + + EXPECT_TRUE(Cpp::IsLambdaClass(Cpp::GetFunctionReturnType(get_fn))); + EXPECT_FALSE(Cpp::IsLambdaClass(Cpp::GetFunctionReturnType(bar))); } TEST(FunctionReflectionTest, IsConstMethod) { @@ -2030,7 +2205,7 @@ TEST(FunctionReflectionTest, IsConstMethod) { } TEST(FunctionReflectionTest, GetFunctionArgName) { - std::vector Decls, SubDecls; + std::vector Decls; std::string code = R"( void f1(int i, double d, long l, char ch) {} void f2(const int i, double d[], long *l, char ch[4]) {} @@ -2070,7 +2245,7 @@ TEST(FunctionReflectionTest, GetFunctionArgName) { } TEST(FunctionReflectionTest, GetFunctionArgDefault) { - std::vector Decls, SubDecls; + std::vector Decls; std::string code = R"( void f1(int i, double d = 4.0, const char *s = "default", char ch = 'c') {} void f2(float i = 0.0, double d = 3.123, long m = 34126) {} @@ -2084,6 +2259,15 @@ TEST(FunctionReflectionTest, GetFunctionArgDefault) { template void get_size(long k, A, char ch = 'a', double l = 0.0) {} + template + struct Other {}; + + template > + struct MyStruct { + T t; + S s; + void fn(T t, S s = S()) {} + }; )"; GetAllTopLevelDecls(code, Decls); @@ -2108,6 +2292,20 @@ TEST(FunctionReflectionTest, GetFunctionArgDefault) { EXPECT_EQ(Cpp::GetFunctionArgDefault(Decls[4], 1), ""); EXPECT_EQ(Cpp::GetFunctionArgDefault(Decls[4], 2), "\'a\'"); EXPECT_EQ(Cpp::GetFunctionArgDefault(Decls[4], 3), "0."); + + ASTContext& C = Interp->getCI()->getASTContext(); + Cpp::TemplateArgInfo template_args[1] = {C.IntTy.getAsOpaquePtr()}; + Cpp::TCppScope_t my_struct = + Cpp::InstantiateTemplate(Decls[6], template_args, 1); + EXPECT_TRUE(my_struct); + + std::vector fns = + Cpp::GetFunctionsUsingName(my_struct, "fn"); + EXPECT_EQ(fns.size(), 1); + + Cpp::TCppScope_t fn = fns[0]; + EXPECT_EQ(Cpp::GetFunctionArgDefault(fn, 0), ""); + EXPECT_EQ(Cpp::GetFunctionArgDefault(fn, 1), "S()"); } TEST(FunctionReflectionTest, Construct) { @@ -2305,9 +2503,6 @@ TEST(FunctionReflectionTest, ConstructArray) { #ifdef _WIN32 GTEST_SKIP() << "Disabled on Windows. Needs fixing."; #endif -#if defined(__APPLE__) && (CLANG_VERSION_MAJOR == 16) - GTEST_SKIP() << "Test fails on Clang16 OS X"; -#endif Cpp::CreateInterpreter(); @@ -2432,9 +2627,6 @@ TEST(FunctionReflectionTest, DestructArray) { #ifdef _WIN32 GTEST_SKIP() << "Disabled on Windows. Needs fixing."; #endif -#if defined(__APPLE__) && (CLANG_VERSION_MAJOR == 16) - GTEST_SKIP() << "Test fails on Clang16 OS X"; -#endif std::vector interpreter_args = {"-include", "new"}; Cpp::CreateInterpreter(interpreter_args); diff --git a/interpreter/CppInterOp/unittests/CppInterOp/InterpreterTest.cpp b/interpreter/CppInterOp/unittests/CppInterOp/InterpreterTest.cpp index 4ef046f92d0b7..e9b82ea1def54 100644 --- a/interpreter/CppInterOp/unittests/CppInterOp/InterpreterTest.cpp +++ b/interpreter/CppInterOp/unittests/CppInterOp/InterpreterTest.cpp @@ -334,7 +334,10 @@ if (llvm::sys::RunningOnValgrind()) #ifdef CPPINTEROP_USE_CLING std::string MainExecutableName = sys::fs::getMainExecutable(nullptr, nullptr); - std::string ResourceDir = compat::MakeResourceDir(LLVM_BINARY_DIR); + llvm::SmallString<128> P(LLVM_BINARY_DIR); + llvm::sys::path::append(P, CLANG_INSTALL_LIBDIR_BASENAME, "clang", + CLANG_VERSION_MAJOR_STRING); + std::string ResourceDir = std::string(P.str()); std::vector ClingArgv = {"-resource-dir", ResourceDir.c_str(), "-std=c++14"}; ClingArgv.insert(ClingArgv.begin(), MainExecutableName.c_str()); diff --git a/interpreter/CppInterOp/unittests/CppInterOp/ScopeReflectionTest.cpp b/interpreter/CppInterOp/unittests/CppInterOp/ScopeReflectionTest.cpp index 4ecdf706e47ad..53b2ee8948ced 100644 --- a/interpreter/CppInterOp/unittests/CppInterOp/ScopeReflectionTest.cpp +++ b/interpreter/CppInterOp/unittests/CppInterOp/ScopeReflectionTest.cpp @@ -191,7 +191,6 @@ TEST(ScopeReflectionTest, IsBuiltin) { EXPECT_TRUE(Cpp::IsBuiltin(C.getComplexType(C.Float128Ty).getAsOpaquePtr())); // std::complex - std::vector Decls; Interp->declare("#include "); Sema &S = Interp->getCI()->getSema(); auto lookup = S.getStdNamespace()->lookup(&C.Idents.get("complex")); @@ -334,6 +333,9 @@ TEST(ScopeReflectionTest, GetCompleteName) { A a; enum { enum1 }; + + template + void fn(T1 t1, T2 t2) {} )"; GetAllTopLevelDecls(code, Decls); @@ -350,7 +352,15 @@ TEST(ScopeReflectionTest, GetCompleteName) { Cpp::GetVariableType( Decls[9]))), "A"); EXPECT_EQ(Cpp::GetCompleteName(Decls[10]), "(unnamed)"); + EXPECT_EQ(Cpp::GetCompleteName(Decls[11]), "fn"); EXPECT_EQ(Cpp::GetCompleteName(nullptr), ""); + + ASTContext& C = Interp->getCI()->getASTContext(); + Cpp::TemplateArgInfo template_args[2] = {C.IntTy.getAsOpaquePtr(), + C.DoubleTy.getAsOpaquePtr()}; + Cpp::TCppScope_t fn = Cpp::InstantiateTemplate(Decls[11], template_args, 2); + EXPECT_TRUE(fn); + EXPECT_EQ(Cpp::GetCompleteName(fn), "fn"); } TEST(ScopeReflectionTest, GetQualifiedName) { @@ -630,6 +640,13 @@ TEST(ScopeReflectionTest, GetNumBases) { class D : public B, public C {}; class E : public D {}; class NoDef; + + template + struct Klass : public A { + T t{N}; + }; + + typedef Klass TKlass; )"; GetAllTopLevelDecls(code, Decls); @@ -642,6 +659,7 @@ TEST(ScopeReflectionTest, GetNumBases) { // FIXME: Perhaps we should have a special number or error out as this // operation is not well defined if a class has no definition. EXPECT_EQ(Cpp::GetNumBases(Decls[5]), 0); + EXPECT_EQ(Cpp::GetNumBases(Cpp::GetUnderlyingScope(Decls[7])), 1); } TEST(ScopeReflectionTest, GetBaseClass) { @@ -863,7 +881,7 @@ template constexpr T pi = T(3.1415926535897932385L); auto* VD = cast((Decl*)Instance1); VarTemplateDecl* VDTD1 = VD->getSpecializedTemplate(); EXPECT_TRUE(VDTD1->isThisDeclarationADefinition()); -#if CLANG_VERSION_MAJOR <= 18 +#if CLANG_VERSION_MAJOR == 18 TemplateArgument TA1 = (*VD->getTemplateArgsInfo())[0].getArgument(); #else TemplateArgument TA1 = (*VD->getTemplateArgsAsWritten())[0].getArgument(); diff --git a/interpreter/CppInterOp/unittests/CppInterOp/TypeReflectionTest.cpp b/interpreter/CppInterOp/unittests/CppInterOp/TypeReflectionTest.cpp index 26eb2d6217c26..215d30f104394 100644 --- a/interpreter/CppInterOp/unittests/CppInterOp/TypeReflectionTest.cpp +++ b/interpreter/CppInterOp/unittests/CppInterOp/TypeReflectionTest.cpp @@ -620,3 +620,69 @@ TEST(TypeReflectionTest, OperatorSpelling) { EXPECT_EQ(Cpp::GetOperatorFromSpelling("()"), Cpp::OP_Call); EXPECT_EQ(Cpp::GetOperatorFromSpelling("invalid"), Cpp::OP_None); } + +TEST(TypeReflectionTest, TypeQualifiers) { + Cpp::CreateInterpreter(); + Cpp::Declare(R"( + int *a; + int *__restrict__ b; + int *const c = 0; + int *volatile d; + int *const volatile e = nullptr; + int *__restrict__ const f = nullptr; + int *__restrict__ volatile g; + int *__restrict__ const volatile h = nullptr; + )"); + + Cpp::TCppType_t a = Cpp::GetVariableType(Cpp::GetNamed("a")); + Cpp::TCppType_t b = Cpp::GetVariableType(Cpp::GetNamed("b")); + Cpp::TCppType_t c = Cpp::GetVariableType(Cpp::GetNamed("c")); + Cpp::TCppType_t d = Cpp::GetVariableType(Cpp::GetNamed("d")); + Cpp::TCppType_t e = Cpp::GetVariableType(Cpp::GetNamed("e")); + Cpp::TCppType_t f = Cpp::GetVariableType(Cpp::GetNamed("f")); + Cpp::TCppType_t g = Cpp::GetVariableType(Cpp::GetNamed("g")); + Cpp::TCppType_t h = Cpp::GetVariableType(Cpp::GetNamed("h")); + + EXPECT_FALSE(Cpp::HasTypeQualifier(nullptr, Cpp::QualKind::Const)); + EXPECT_FALSE(Cpp::RemoveTypeQualifier(nullptr, Cpp::QualKind::Const)); + EXPECT_FALSE(Cpp::AddTypeQualifier(nullptr, Cpp::QualKind::Const)); + + EXPECT_FALSE(Cpp::HasTypeQualifier(a, Cpp::QualKind::Const)); + EXPECT_FALSE(Cpp::HasTypeQualifier(a, Cpp::QualKind::Volatile)); + EXPECT_FALSE(Cpp::HasTypeQualifier(a, Cpp::QualKind::Restrict)); + EXPECT_TRUE(Cpp::HasTypeQualifier(b, Cpp::QualKind::Restrict)); + EXPECT_TRUE(Cpp::HasTypeQualifier(c, Cpp::QualKind::Const)); + EXPECT_TRUE(Cpp::HasTypeQualifier(d, Cpp::QualKind::Volatile)); + EXPECT_TRUE( + Cpp::HasTypeQualifier(e, Cpp::QualKind::Const | Cpp::QualKind::Volatile)); + EXPECT_TRUE( + Cpp::HasTypeQualifier(f, Cpp::QualKind::Const | Cpp::QualKind::Restrict)); + EXPECT_TRUE(Cpp::HasTypeQualifier(g, Cpp::QualKind::Volatile | + Cpp::QualKind::Restrict)); + EXPECT_TRUE(Cpp::HasTypeQualifier(h, Cpp::QualKind::Const | + Cpp::QualKind::Volatile | + Cpp::QualKind::Restrict)); + + EXPECT_EQ(a, Cpp::RemoveTypeQualifier(b, Cpp::QualKind::Restrict)); + EXPECT_EQ(a, Cpp::RemoveTypeQualifier(c, Cpp::QualKind::Const)); + EXPECT_EQ(a, Cpp::RemoveTypeQualifier(d, Cpp::QualKind::Volatile)); + EXPECT_EQ(a, Cpp::RemoveTypeQualifier(e, Cpp::QualKind::Const | + Cpp::QualKind::Volatile)); + EXPECT_EQ(a, Cpp::RemoveTypeQualifier(f, Cpp::QualKind::Const | + Cpp::QualKind::Restrict)); + EXPECT_EQ(a, Cpp::RemoveTypeQualifier(g, Cpp::QualKind::Volatile | + Cpp::QualKind::Restrict)); + EXPECT_EQ(a, Cpp::RemoveTypeQualifier(h, Cpp::QualKind::Const | + Cpp::QualKind::Volatile | + Cpp::QualKind::Restrict)); + EXPECT_EQ(e, Cpp::RemoveTypeQualifier(h, Cpp::QualKind::Restrict)); + EXPECT_EQ(b, Cpp::RemoveTypeQualifier(h, Cpp::QualKind::Const | + Cpp::QualKind::Volatile)); + + EXPECT_EQ(c, Cpp::AddTypeQualifier(a, Cpp::QualKind::Const)); + EXPECT_EQ(d, Cpp::AddTypeQualifier(a, Cpp::QualKind::Volatile)); + EXPECT_EQ(b, Cpp::AddTypeQualifier(a, Cpp::QualKind::Restrict)); + EXPECT_EQ(h, Cpp::AddTypeQualifier(a, Cpp::QualKind::Const | + Cpp::QualKind::Volatile | + Cpp::QualKind::Restrict)); +} diff --git a/interpreter/CppInterOp/unittests/CppInterOp/VariableReflectionTest.cpp b/interpreter/CppInterOp/unittests/CppInterOp/VariableReflectionTest.cpp index f75d1839a032a..e6bde0e8d5b72 100644 --- a/interpreter/CppInterOp/unittests/CppInterOp/VariableReflectionTest.cpp +++ b/interpreter/CppInterOp/unittests/CppInterOp/VariableReflectionTest.cpp @@ -234,6 +234,7 @@ TEST(VariableReflectionTest, GetVariableType) { E e; E *f; int g[4]; + auto fn = []() { return 1; }; )"; GetAllTopLevelDecls(code, Decls); @@ -245,6 +246,9 @@ TEST(VariableReflectionTest, GetVariableType) { EXPECT_EQ(Cpp::GetTypeAsString(Cpp::GetVariableType(Decls[6])), "E"); EXPECT_EQ(Cpp::GetTypeAsString(Cpp::GetVariableType(Decls[7])), "E *"); EXPECT_EQ(Cpp::GetTypeAsString(Cpp::GetVariableType(Decls[8])), "int[4]"); + + EXPECT_FALSE(Cpp::IsLambdaClass(Cpp::GetVariableType(Decls[8]))); + EXPECT_TRUE(Cpp::IsLambdaClass(Cpp::GetVariableType(Decls[9]))); } #define CODE \ @@ -318,6 +322,28 @@ TEST(VariableReflectionTest, GetVariableOffset) { EXPECT_EQ(Cpp::GetVariableOffset(datamembers[0]), offsetof(K, x)); EXPECT_EQ(Cpp::GetVariableOffset(datamembers[1]), offsetof(K, y)); EXPECT_EQ(Cpp::GetVariableOffset(datamembers[2]), offsetof(K, z)); + + Cpp::Declare(R"( + template struct ClassWithStatic { + static T const ref_value; + }; + template T constexpr ClassWithStatic::ref_value = 42; + )"); + + Cpp::TCppScope_t klass = Cpp::GetNamed("ClassWithStatic"); + EXPECT_TRUE(klass); + + ASTContext& C = Interp->getCI()->getASTContext(); + std::vector template_args = { + {C.IntTy.getAsOpaquePtr()}}; + Cpp::TCppScope_t klass_instantiated = Cpp::InstantiateTemplate( + klass, template_args.data(), template_args.size()); + EXPECT_TRUE(klass_instantiated); + + Cpp::TCppScope_t var = Cpp::GetNamed("ref_value", klass_instantiated); + EXPECT_TRUE(var); + + EXPECT_TRUE(Cpp::GetVariableOffset(var)); } #define CODE \ @@ -508,7 +534,7 @@ TEST(VariableReflectionTest, IsConstVariable) { } TEST(VariableReflectionTest, DISABLED_GetArrayDimensions) { - std::vector Decls, SubDecls; + std::vector Decls; std::string code = R"( int a; int b[1];