diff --git a/.github/workflows/basic-ci.yml b/.github/workflows/basic-ci.yml index 87d5805a..255030ec 100644 --- a/.github/workflows/basic-ci.yml +++ b/.github/workflows/basic-ci.yml @@ -7,26 +7,23 @@ on: jobs: build-and-run-test: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 if: "!contains(github.event.head_commit.message, '[ci skip]')" steps: - uses: actions/checkout@v2 - - name: Setup LLVM repository - run: | - wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key 2>/dev/null | sudo apt-key add - - sudo add-apt-repository 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main' -y - sudo apt-get update -q - - name: Install LLVM run: sudo apt-get install libllvm10 llvm-10 llvm-10-dev - name: Install Clang - run: sudo apt-get install clang-10 + run: sudo apt-get install clang-10 clang-tidy-10 - name: Install OpenMPI run: sudo apt-get install libopenmpi-dev openmpi-bin + - name: Install lcov + run: sudo apt-get install lcov + - name: Setup env run: | sudo ln -f -s /usr/bin/clang-10 /usr/bin/clang @@ -34,37 +31,50 @@ jobs: sudo ln -f -s /usr/bin/opt-10 /usr/bin/opt sudo ln -f -s /usr/bin/FileCheck-10 /usr/bin/FileCheck sudo ln -f -s /usr/bin/llc-10 /usr/bin/llc - echo "::set-env name=CC::clang-10" - echo "::set-env name=CXX::clang++-10" - echo "::set-env name=EXTERNAL_LIT::/usr/lib/llvm-10/build/utils/lit/lit.py" + sudo ln -f -s /usr/bin/clang-tidy-10 /usr/bin/clang-tidy + echo "CC=clang-10" >> $GITHUB_ENV + echo "CXX=clang++-10" >> $GITHUB_ENV + echo "EXTERNAL_LIT=/usr/lib/llvm-10/build/utils/lit/lit.py" >> $GITHUB_ENV - name: Build TypeART run: | - cmake -B build -DTEST_CONFIG=ON -DLLVM_EXTERNAL_LIT=${EXTERNAL_LIT} + cmake -B build -DTEST_CONFIG=ON -DENABLE_CODE_COVERAGE=ON -DLLVM_EXTERNAL_LIT=${EXTERNAL_LIT} cmake --build build --parallel - - name: Test TypeART - run: cmake --build build --target test -- ARGS=-VV + - name: Test TypeART with coverage (exludes lulesh) + run: | + cmake --build build --target lcov-clean + cmake --build build --target test -- ARGS=-VV + + - name: Build coverage report + run: cmake --build build --target lcov-html - - name: Build TypeART Release + - name: Build TypeART release run: | cmake -B build_lulesh -DCMAKE_BUILD_TYPE=Release -DMPI_INTERCEPT_LIB=ON -DSHOW_STATS=ON cmake --build build_lulesh --parallel - - name: Test TypeART on lulesh + - name: Test TypeART release on lulesh working-directory: build_lulesh run: ctest -V -R lulesh -O lulesh2.0_build.log - - - name: Prepare lulesh artifact + + - name: Prepare artifact run: | - mkdir artifact_lulesh - mv build_lulesh/lulesh2.0_build.log artifact_lulesh/ - mv test/lulesh/lulesh2.0_out.log artifact_lulesh/ - mv test/lulesh/types.yaml artifact_lulesh/lulesh2.0_types.yaml + mkdir -p artifact/lulesh + mkdir -p artifact/coverage + mv build_lulesh/lulesh2.0_build.log artifact/lulesh/ + mv test/lulesh/lulesh2.0_out.log artifact/lulesh/ + mv test/lulesh/types.yaml artifact/lulesh/lulesh2.0_types.yaml + mv build/profiles/ artifact/coverage - name: Upload lulesh test artifact - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v2 + with: + name: typeart-ci + path: artifact + + - name: Coveralls + uses: coverallsapp/github-action@master with: - name: typeart-lulesh-archive - path: artifact_lulesh - + github-token: ${{ secrets.GITHUB_TOKEN }} + path-to-lcov: build/typeart.coverage diff --git a/.github/workflows/ext-ci.yml b/.github/workflows/ext-ci.yml index 61bf8c15..a8fe977f 100644 --- a/.github/workflows/ext-ci.yml +++ b/.github/workflows/ext-ci.yml @@ -2,13 +2,12 @@ name: TypeART-CI-ext on: push: - branches: - - master + branches: [ master, devel ] pull_request: jobs: build-and-run-testbench: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 if: "!contains(github.event.head_commit.message, '[ci skip]') || !contains(github.event.head_commit.message, '[ci ext skip]')" steps: - uses: actions/checkout@v2 @@ -20,17 +19,19 @@ jobs: ssh-key: ${{ secrets.AUTH_SSH_CI_EXT }} path: test-bench - - name: Setup LLVM repository - run: | - wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key 2>/dev/null | sudo apt-key add - - sudo add-apt-repository 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main' -y - sudo apt-get update -q + - name: Checkout AD test-bench + uses: actions/checkout@v2 + with: + repository: ahueck/typeart-ad-benchmarks + ssh-key: ${{ secrets.AUTH_SSH_CI_EXT_AD }} + ref: feat/ci + path: ad-test-bench - name: Install LLVM run: sudo apt-get install libllvm10 llvm-10 llvm-10-dev - name: Install Clang - run: sudo apt-get install clang-10 + run: sudo apt-get install clang-10 clang-tidy-10 - name: Install OpenMPI run: sudo apt-get install libopenmpi-dev openmpi-bin @@ -41,14 +42,16 @@ jobs: sudo ln -f -s /usr/bin/clang++-10 /usr/bin/clang++ sudo ln -f -s /usr/bin/opt-10 /usr/bin/opt sudo ln -f -s /usr/bin/llc-10 /usr/bin/llc - echo "::set-env name=CC::clang-10" - echo "::set-env name=CXX::clang++-10" + sudo ln -f -s /usr/bin/clang-tidy-10 /usr/bin/clang-tidy + echo "CC=clang-10" >> $GITHUB_ENV + echo "CXX=clang++-10" >> $GITHUB_ENV + echo "EXTERNAL_LIT=/usr/lib/llvm-10/build/utils/lit/lit.py" >> $GITHUB_ENV - name: Build & install TypeART run: | cmake -B build -DCMAKE_BUILD_TYPE=Release -DMPI_INTERCEPT_LIB=ON -DSHOW_STATS=ON cmake --build build --parallel --target install - echo "::set-env name=TYPEART_PATH::${GITHUB_WORKSPACE}/install/typeart" + echo "TYPEART_PATH=${GITHUB_WORKSPACE}/install/typeart" >> $GITHUB_ENV - name: Setup tests working-directory: test-bench @@ -70,16 +73,27 @@ jobs: working-directory: test-bench/build run: ctest -V -R amg2013 -O amg2013_build.log - - name: Prepare test-bench artifact - working-directory: test-bench + - name: Setup AD tests + working-directory: ad-test-bench + run: cmake -B build -DLOG_PATH=${GITHUB_WORKSPACE}/ad-test-bench/artifact + + - name: Run AD lulesh + working-directory: ad-test-bench/build + run: ctest -V -R lulesh -O ad-lulesh2.0_build.log + + - name: Prepare artifact run: | - mkdir -p artifact - mv build/*_build.log artifact + mkdir -p artifact/bench + mkdir -p artifact/ad-bench + mv test-bench/build/*_build.log artifact/bench + mv test-bench/artifact/* artifact/bench + mv ad-test-bench/build/*_build.log artifact/ad-bench + mv ad-test-bench/artifact/* artifact/ad-bench - name: Upload test-bench artifact - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v2 with: - name: typeart-bench-archive - path: test-bench/artifact + name: typeart-ci-ext + path: artifact diff --git a/CMakeLists.txt b/CMakeLists.txt index e3c9ad8e..7a5cf920 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,11 @@ -cmake_minimum_required(VERSION 3.12) +cmake_minimum_required(VERSION 3.14) project(typeart - VERSION 2.0 + VERSION 1.5.0 ) +set(TYPEART_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_VERBOSE_MAKEFILE ON) @@ -20,18 +22,19 @@ include(CMakePackageConfigHelpers) add_format_target(format-sources "Formats project source files" - TARGETS lib/*.cpp - lib/*.h - runtime/*.cpp - runtime/*.h - typelib/*.cpp - typelib/*.h -) + TARGETS + lib/passes/*.cpp + lib/passes/*.h + lib/runtime/*.cpp + lib/runtime/*.h + lib/typelib/*.cpp + lib/typelib/*.h + lib/support/*.cpp + lib/support/*.h + ) add_subdirectory(externals) add_subdirectory(lib) -add_subdirectory(typelib) -add_subdirectory(runtime) add_subdirectory(scripts) enable_testing() add_subdirectory(test) diff --git a/README.md b/README.md index 9badca51..85151ac0 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,19 @@ -# TypeART   [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)   ![](https://github.com/tudasc/TypeART/workflows/TypeART-CI/badge.svg?branch=master)   ![](https://github.com/tudasc/TypeART/workflows/TypeART-CI-ext/badge.svg?branch=master) +# TypeART · [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) ![](https://github.com/tudasc/TypeART/workflows/TypeART-CI/badge.svg?branch=master) ![](https://github.com/tudasc/TypeART/workflows/TypeART-CI-ext/badge.svg?branch=master) [![Coverage Status](https://coveralls.io/repos/github/tudasc/TypeART/badge.svg?branch=master)](https://coveralls.io/github/tudasc/TypeART) -TypeART \[[TA18](#ref-typeart-2018)\] is a type and memory allocation tracking sanitizer. -It consists of an LLVM compiler pass and a corresponding runtime to track relevant memory allocation information during the execution of a target program. -It instruments heap, stack and global variable allocations with a callback to our runtime. -The callback consists of the runtime memory pointer value, what type (built-ins, user-defined structs etc.) and extent of the value. -This allows users of our runtime to query detailed type information behind arbritary memory locations, as long as they are mapped. +TypeART \[[TA18](#ref-typeart-2018); [TA20](#ref-typeart-2020)\] is a type and memory allocation tracking sanitizer. It +consists of an LLVM compiler pass and a corresponding runtime to track relevant memory allocation information during the +execution of a target program. It instruments heap, stack and global variable allocations with a callback to our +runtime. The callback consists of the runtime memory pointer value, what type (built-ins, user-defined structs etc.) and +extent of the value. This allows users of our runtime to query detailed type information behind arbitrary memory +locations, as long as they are mapped. ### Use Case: MUST - A dynamic MPI correctness checker -TypeART is used in conjunction with MUST \[[MU13](#ref-must-2013)\] to track memory (de-)allocation relevant to MPI communication. -Thus, MUST can check for type compatibility between the type-less communication buffer and the declared MPI datatype at all phases of the MPI communication, namely message assembly, message transfer and message disassembly into the receiving buffer. -A brief summary is given in a subsequent section and more information can be found in our publication: +TypeART is used in conjunction with MUST \[[MU13](#ref-must-2013)\] to track memory (de-)allocation relevant to MPI +communication. Thus, MUST can check for type compatibility between the type-less communication buffer and the declared +MPI datatype at all phases of the MPI communication, namely message assembly, message transfer and message disassembly +into the receiving buffer. A brief summary is given in a subsequent section and more information can be found in our +publication: #### References @@ -23,6 +26,14 @@ A brief summary is given in a subsequent section and more information can be fou In 2nd International Workshop on Software Correctness for HPC Applications (Correctness), pages 51–58. IEEE, 2018. + + [TA20] + Hück, Alexander and Protze, Joachim and Lehr, Jan-Patrick and Terboven, Christian and Bischof, Christian and Müller, Matthias S. + + Towards compiler-aided correctness checking of adjoint MPI applications. + In 4th International Workshop on Software Correctness for HPC Applications (Correctness), + pages 40–48. IEEE/ACM, 2020. + [MU13] Hilbrich, Tobias and Protze, Joachim and Schulz, Martin and de Supinski, Bronis R. and Müller, Matthias S. @@ -35,12 +46,14 @@ A brief summary is given in a subsequent section and more information can be fou ## Software dependencies -TypeART requires [LLVM](https://llvm.org) version 10 and CMake version >= 3.12. +TypeART requires [LLVM](https://llvm.org) version 10 and CMake version >= 3.14. #### Building TypeART TypeART uses CMake to build, cf. [GitHub CI build file](.github/workflows/basic-ci.yml) for a complete recipe to build. -Example build recipe (debug build, installs to default prefix) +Example build recipe (debug build, installs to default prefix +`${typeart_SOURCE_DIR}/install/typeart`) + ```{.sh} $> git clone https://github.com/tudasc/TypeART $> cd TypeART @@ -50,35 +63,58 @@ $> cmake --build build --target install --parallel #### CMake Configuration: Options for users +##### Runtime + - `SOFTCOUNTERS` (default: **off**) : Enable runtime tracking of #tracked addrs. / #distinct checks / etc. -- `USE_BTREE` (default: **on**) : Enable usage of btree-backed map instead of std::map for the runtime, typically resulting in higher performance. +- `USE_ABSL` (default: **on**) : Enable usage of btree-backed map of the abseil project instead of std::map for the + runtime. +- `USE_BTREE` (default: **off**) : Enable usage of a btree-backed map (alternative to abseil) instead of std::map for + the runtime. + +##### Logging and Passes + +- `SHOW_STATS` (default: **on**) : Passes show the statistics w.r.t. allocations etc. +- `MPI_LOGGER` (default: **on**) : Enable better logging support in MPI execution context +- `MPI_INTERCEPT_LIB` (default: **on**) : Library can be used by preloading to intercept MPI calls and check whether + TypeART tracks the buffer pointer +- `LOG_LEVEL_` and `LOG_LEVEL_RT` (default **0**) : Granularity of logger. 3 ist most verbose, 0 is least. + +##### Testing + +- `TEST_CONFIG` (default: **off**) : Set (force) logging levels to appropriate levels for test runner to succeed +- `ENABLE_CODE_COVERAGE` (default: **off**) : Enable code coverage statistics using LCOV 1.14 and genhtml (gcovr + optional) +- `ENABLE_LLVM_CODE_COVERAGE` (default: **off**) : Enable llvm-cov code coverage statistics (llvm-cov and llvm-profdata + required) ## Using TypeART -Making use of TypeART consists of two phases: +Making use of TypeART consists of two phases: - 1. Compile and instrument the target code with our LLVM passes, and, - 2. execute the target program with a runtime library (based on the TypeART runtime) to accept the callbacks from the instrumented code and actually do some useful analysis. +1. Compile and instrument the target code with our LLVM passes, and, +2. execute the target program with a runtime library (based on the TypeART runtime) to accept the callbacks from the + instrumented code and actually do some useful analysis. -To that end, the interface [RuntimeInterface.h](runtime/RuntimeInterface.h) can be used to query type information during the target code execution. +To that end, the interface [RuntimeInterface.h](runtime/RuntimeInterface.h) can be used to query type information during +the target code execution. #### Example: MPI Demo -The folder [demo](demo) contains an example of MPI related type errors that can be detected using TypeART. -The code is compiled with our instrumentation, and executed by preloading the MPI related check library implemented in [tool.c](demo/tool.c), which is linked against the TypeART runtime and uses the aforementioned query interface. -It overloads the required MPI calls and checks that the passed `void* buffer` is correct. +The folder [demo](demo) contains an example of MPI related type errors that can be detected using TypeART. The code is +compiled with our instrumentation, and executed by preloading the MPI related check library implemented +in [tool.c](demo/tool.c), which is linked against the TypeART runtime and uses the aforementioned query interface. It +overloads the required MPI calls and checks that the passed `void* buffer` is correct. ## LLVM pass -The necessary allocation sites and type information are extracted in LLVM passes. -TypeART analyzes: +The necessary allocation sites and type information are extracted in LLVM passes. TypeART analyzes: - Calls to ```malloc``` and ```free``` to keep track of active pointers referring to objects allocated on the heap, -- relevant stack space allocations, i.e., allocations that cannot be proven to never lead to ```MPI``` functions, +- relevant stack space allocations, i.e., allocations that cannot be proven to never lead to ```MPI``` functions, - built-in as well as user-defined types to retrieve type size and the size of the allocation, e.g., for arrays. -The type information is necessary to correlate the type of the buffer passed to an MPI call with the MPI datatype the user declared. -In this prototype we restrict ourselves to: +The type information is necessary to correlate the type of the buffer passed to an MPI call with the MPI datatype the +user declared. In this prototype we restrict ourselves to: + primitive types (int, float, long, double, char, unsigned int, unsigned long) + arrays of primitive types @@ -86,8 +122,9 @@ In this prototype we restrict ourselves to: ### Example of Instrumentation: Handling malloc -To instrument relevant allocations and extract the necessary type information, the LLVM pass searches for specific patterns, e.g., how calls to ```malloc``` look like in LLVM IR. -Calls to the ```malloc``` function are typically call instructions followed by a ```bitcast``` instruction to cast the returned pointer to the desired type. +To instrument relevant allocations and extract the necessary type information, the LLVM pass searches for specific +patterns, e.g., how calls to ```malloc``` look like in LLVM IR. Calls to the ```malloc``` function are typically call +instructions followed by a ```bitcast``` instruction to cast the returned pointer to the desired type. ~~~{.ll} ; %0 == n * sizeof(float) @@ -95,9 +132,9 @@ Calls to the ```malloc``` function are typically call instructions followed by a %2 = bitcast i8* %1 to float * ~~~ -The patterns has all the information we require for our instrumentation. -Our transformation first detects the type that the returned pointer is casted to, then it computes the extent of the allocation. -The information is passed to our instrumentation function. +The patterns has all the information we require for our instrumentation. Our transformation first detects the type that +the returned pointer is casted to, then it computes the extent of the allocation. The information is passed to our +instrumentation function. ~~~{.ll} ; %0 == n * sizeof(float) diff --git a/cmake/ToolchainOptions.cmake b/cmake/ToolchainOptions.cmake index c0652766..9a34592e 100644 --- a/cmake/ToolchainOptions.cmake +++ b/cmake/ToolchainOptions.cmake @@ -1,79 +1,82 @@ -find_package(LLVM REQUIRED CONFIG) +find_package(LLVM 10 REQUIRED CONFIG) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") +set(LOG_LEVEL 0 CACHE STRING "Granularity of LLVM pass logger. 3 ist most verbose, 0 is least.") +set(LOG_LEVEL_RT 0 CACHE STRING "Granularity of runtime logger. 3 ist most verbose, 0 is least.") +option(SHOW_STATS "Passes show the statistics vars." ON) +option(MPI_LOGGER "Whether the logger should use MPI." ON) +option(MPI_INTERCEPT_LIB "Build MPI interceptor library for prototyping and testing." ON) +option(SOFTCOUNTERS "Enable software tracking of #tracked addrs. / #distinct checks / etc." OFF) +option(TEST_CONFIG "Set logging levels to appropriate levels for test runner to succeed" OFF) +option(ENABLE_CODE_COVERAGE "Enable code coverage statistics" OFF) +option(ENABLE_LLVM_CODE_COVERAGE "Enable llvm-cov code coverage statistics" OFF) + include(AddLLVM) include(llvm-lit) include(clang-tidy) include(clang-format) include(llvm-util) include(log-util) +include(coverage) -set(LOG_LEVEL 0 CACHE STRING "Granularity of LLVM pass logger. 3 ist most verbose, 0 is least.") -set(LOG_LEVEL_RT 0 CACHE STRING "Granularity of runtime logger. 3 ist most verbose, 0 is least.") -option(SHOW_STATS "Passes show the statistics vars." OFF) -option(MPI_LOGGER "Whether the logger should use MPI." OFF) -option(MPI_INTERCEPT_LIB "Build MPI interceptor library, requires wrap.py generator file." OFF) -option(SOFTCOUNTERS "Enable software tracking of #tracked addrs. / #distinct checks / etc." OFF) -option(TEST_CONFIG "Set logging levels to appropriate levels for test runner to succeed" OFF) - -if(TEST_CONFIG) +if (TEST_CONFIG) set(LOG_LEVEL 2 CACHE STRING "" FORCE) set(LOG_LEVEL_RT 3 CACHE STRING "" FORCE) -endif() +endif () -if(MPI_LOGGER) +if (MPI_LOGGER) find_package(MPI REQUIRED) -endif() +endif () -if(NOT CMAKE_BUILD_TYPE) +if (NOT CMAKE_BUILD_TYPE) # set default build type set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE) message(STATUS "Building as debug (default)") -endif() +endif () -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) +if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) # set default install path set(CMAKE_INSTALL_PREFIX "${typeart_SOURCE_DIR}/install/typeart" CACHE PATH "Default install path" FORCE) message(STATUS "Installing to (default): ${CMAKE_INSTALL_PREFIX}") -endif() +endif () function(target_project_compile_options target) cmake_parse_arguments(ARG "" "" "PRIVATE_FLAGS;PUBLIC_FLAGS" ${ARGN}) target_compile_options(${target} PRIVATE - -Wall -Wextra -pedantic - -Wunreachable-code -Wwrite-strings - -Wpointer-arith -Wcast-align - -Wcast-qual -Wno-unused-parameter - ) + -Wall -Wextra -pedantic + -Wunreachable-code -Wwrite-strings + -Wpointer-arith -Wcast-align + -Wcast-qual -Wno-unused-parameter + ) - if(ARG_PRIVATE_FLAGS) + if (ARG_PRIVATE_FLAGS) target_compile_options(${target} PRIVATE - "${ARG_PRIVATE_FLAGS}" - ) - endif() + "${ARG_PRIVATE_FLAGS}" + ) + endif () - if(ARG_PUBLIC_FLAGS) + if (ARG_PUBLIC_FLAGS) target_compile_options(${target} PUBLIC - "${ARG_PUBLIC_FLAGS}" - ) - endif() + "${ARG_PUBLIC_FLAGS}" + ) + endif () endfunction() function(target_project_compile_definitions target) cmake_parse_arguments(ARG "" "" "PRIVATE_DEFS;PUBLIC_DEFS" ${ARGN}) - if(ARG_PRIVATE_DEFS) + if (ARG_PRIVATE_DEFS) target_compile_definitions(${target} PRIVATE - "${ARG_PRIVATE_DEFS}" - ) - endif() + "${ARG_PRIVATE_DEFS}" + ) + endif () - if(ARG_PUBLIC_DEFS) + if (ARG_PUBLIC_DEFS) target_compile_definitions(${target} PUBLIC - "${ARG_PUBLIC_DEFS}" - ) - endif() + "${ARG_PUBLIC_DEFS}" + ) + endif () endfunction() diff --git a/cmake/modules/coverage-gcovr.cmake b/cmake/modules/coverage-gcovr.cmake new file mode 100644 index 00000000..566953bb --- /dev/null +++ b/cmake/modules/coverage-gcovr.cmake @@ -0,0 +1,14 @@ +if (ENABLE_CODE_COVERAGE AND CMAKE_CXX_COMPILER_ID MATCHES "GNU") + find_program(GCOVR_COMMAND gcovr) + if(GCOVR_COMMAND) + add_custom_target(gcovr-report + COMMAND ${GCOVR_COMMAND} -r ${PROJECT_SOURCE_DIR} -e ${PROJECT_BINARY_DIR} -s -j 4 + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + COMMENT "Make coverage report" + USES_TERMINAL + ) + else() + add_custom_target(gcovr-report + COMMAND ${CMAKE_COMMAND} -E echo "gcovr-report does nothing, no gcovr executable found.") + endif() +endif() \ No newline at end of file diff --git a/cmake/modules/coverage-lcov.cmake b/cmake/modules/coverage-lcov.cmake new file mode 100644 index 00000000..c6e05294 --- /dev/null +++ b/cmake/modules/coverage-lcov.cmake @@ -0,0 +1,58 @@ +find_program(LCOV_COMMAND lcov) +find_program(GENHTML_COMMAND genhtml) + +if(LCOV_COMMAND-NOTFOUND OR GENHTML_COMMAND-NOTFOUND) + message(WARNING "lcov and genhtml command needed for coverage.") +endif() + +add_custom_target( + lcov-clean + COMMAND ${LCOV_COMMAND} -d ${CMAKE_BINARY_DIR} -z +) + +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + find_program(LLVMCOV_COMMAND REQUIRED + NAMES llvm-cov-10 llvm-cov + ) + # workaround lcov and clang --coverage have a version mismatch + set(GCOV_TOOL --gcov-tool ${CMAKE_BINARY_DIR}/scripts/llvm-gcov.sh) + # this autogenerated file in build/* causes error when using llvm-cov gcov (should be fine with gcc) + set(GCOV_WORKAROUND --exclude *mpi_interceptor_rt.c) +endif() + +add_custom_target( + lcov-make + COMMAND ${LCOV_COMMAND} ${GCOV_TOOL} ${GCOV_WORKAROUND} --no-external -c -d ${CMAKE_BINARY_DIR} -b ${CMAKE_SOURCE_DIR} -o typeart.coverage + COMMAND ${LCOV_COMMAND} --remove typeart.coverage '${CMAKE_BINARY_DIR}/*' -o typeart.coverage +) + +add_custom_target( + lcov-html + COMMAND ${GENHTML_COMMAND} -o ${TYPEART_PROFILE_DIR} typeart.coverage + DEPENDS lcov-make +) + + +function(make_lcov_target target) +# add_custom_target( +# lcov-clean-${target} +# COMMAND lcov -d ${CMAKE_BINARY_DIR} -z +# WORKING_DIRECTORY ${PROJECT_BINARY_DIR} +# ) + + get_target_property(LCOV_TARGET_SOURCE_DIR ${target} SOURCE_DIR) + + add_custom_target( + lcov-make-${target} + COMMAND ${LCOV_COMMAND} ${GCOV_TOOL} ${GCOV_WORKAROUND} --no-external -c -d ${CMAKE_BINARY_DIR} -b ${LCOV_TARGET_SOURCE_DIR} -o counter-${target}.pro + COMMAND ${LCOV_COMMAND} --remove counter-${target}.pro '${CMAKE_BINARY_DIR}/*' -o counter-${target}.pro + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) + + add_custom_target( + lcov-html-${target} + COMMAND ${GENHTML_COMMAND} -o ${TYPEART_PROFILE_DIR} counter-${target}.pro + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + DEPENDS lcov-make-${target} + ) +endfunction() diff --git a/cmake/modules/coverage-llvm-cov.cmake b/cmake/modules/coverage-llvm-cov.cmake new file mode 100644 index 00000000..512059f3 --- /dev/null +++ b/cmake/modules/coverage-llvm-cov.cmake @@ -0,0 +1,65 @@ +find_program(LLVM_PROFDATA_COMMAND + NAMES llvm-profdata-10 llvm-profdata +) +find_program(LLVMCOV_COMMAND + NAMES llvm-cov-10 llvm-cov +) + +if(LLVM_PROFDATA_COMMAND-NOTFOUND OR LLVMCOV_COMMAND-NOTFOUND) + message(WARNING "llvm-cov stack needed for coverage.") +endif() + + +add_custom_target( + cov-merge + COMMAND ${LLVM_PROFDATA_COMMAND} merge -sparse -o code.pro *.profraw + DEPENDS ${target} + WORKING_DIRECTORY ${TYPEART_PROFILE_DIR} + DEPENDS ${target} +) + +add_custom_target( + cov-all-report + COMMAND ${LLVMCOV_COMMAND} report `cat -s ta-binaries.txt` --instr-profile=code.pro + WORKING_DIRECTORY ${TYPEART_PROFILE_DIR} + DEPENDS cov-merge +) + +add_custom_target( + cov-clean + COMMAND rm ta-binaries.txt + COMMAND rm *.pro + WORKING_DIRECTORY ${TYPEART_PROFILE_DIR} +) + +add_custom_target( + cov-all-clean + COMMAND rm * + WORKING_DIRECTORY ${TYPEART_PROFILE_DIR} +) + +function(make_llvm_cov_target target) + add_custom_target( + cov-binary-list-${target} + COMMAND ${CMAKE_COMMAND} -E echo "-object $" >> ta-binaries.txt + WORKING_DIRECTORY ${TYPEART_PROFILE_DIR} + DEPENDS ${target} + ) + + add_custom_target( + cov-merge-${target} + COMMAND ${LLVM_PROFDATA_COMMAND} merge -sparse -o code-${target}.pro *.profraw + DEPENDS ${target} + WORKING_DIRECTORY ${TYPEART_PROFILE_DIR} + DEPENDS ${target} + ) + + add_custom_target( + cov-report-${target} + COMMAND ${LLVMCOV_COMMAND} report -object $ --instr-profile=code-${target}.pro + WORKING_DIRECTORY ${TYPEART_PROFILE_DIR} + DEPENDS ${target} cov-merge-${target} + ) + + add_dependencies(cov-all-report cov-binary-list-${target}) +endfunction() \ No newline at end of file diff --git a/cmake/modules/coverage.cmake b/cmake/modules/coverage.cmake new file mode 100644 index 00000000..6ab7b8a6 --- /dev/null +++ b/cmake/modules/coverage.cmake @@ -0,0 +1,40 @@ +set(TYPEART_PROFILE_DIR ${CMAKE_BINARY_DIR}/profiles) +file(MAKE_DIRECTORY ${TYPEART_PROFILE_DIR}) + +if(NOT ENABLE_LLVM_CODE_COVERAGE AND ENABLE_CODE_COVERAGE) + include(coverage-gcovr) + include(coverage-lcov) +endif() + +if(ENABLE_LLVM_CODE_COVERAGE) + include(coverage-llvm-cov) +endif() + +function(target_project_coverage_options target) + if (NOT ENABLE_LLVM_CODE_COVERAGE AND ENABLE_CODE_COVERAGE AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options(${target} PUBLIC + -O0 + -g + --coverage + ) + target_link_options(${target} PUBLIC + --coverage + ) + make_lcov_target(${target}) + endif () + if (ENABLE_LLVM_CODE_COVERAGE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options(${target} PUBLIC + -O0 + -g + -fprofile-instr-generate + -fcoverage-mapping + ) + target_link_options(${target} PUBLIC + -fprofile-instr-generate + ) + make_llvm_cov_target(${target}) + endif () +endfunction() + + + diff --git a/cmake/modules/llvm-lit.cmake b/cmake/modules/llvm-lit.cmake index 546233e0..33c4ce66 100644 --- a/cmake/modules/llvm-lit.cmake +++ b/cmake/modules/llvm-lit.cmake @@ -30,4 +30,6 @@ if(NOT LIT_COMMAND_I) endif() endif() +mark_as_advanced(path_to_llvm_lit) + message(STATUS "llvm lit command is set to ${LIT_COMMAND_I}") \ No newline at end of file diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 40f733b6..b1496cfe 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -1,10 +1,16 @@ include(CMakeDependentOption) -set(EXTERNAL_DIR ${CMAKE_CURRENT_BINARY_DIR}) -# TODO may remove later set(FETCHCONTENT_UPDATES_DISCONNECTED ON CACHE STRING "" FORCE) -option(USE_BTREE "Enable usage of btree-backed map instead of std::map for the runtime." ON) -CMAKE_DEPENDENT_OPTION(USE_ABSL "Enable usage of abseil's btree-backed map instead of std::map for the runtime." ON "NOT USE_BTREE" OFF) +include(FetchContent) + +mark_as_advanced( + FETCHCONTENT_BASE_DIR + FETCHCONTENT_QUIET + FETCHCONTENT_FULLY_DISCONNECTED +) + +option(USE_ABSL "Enable usage of abseil's btree-backed map instead of std::map for the runtime." ON ) +CMAKE_DEPENDENT_OPTION(USE_BTREE "Enable usage of btree-backed map instead of std::map for the runtime." ON "NOT USE_ABSL" ON) if(USE_BTREE) add_subdirectory(btree) diff --git a/externals/abseil/CMakeLists.txt b/externals/abseil/CMakeLists.txt index 7000d141..0b0b2248 100644 --- a/externals/abseil/CMakeLists.txt +++ b/externals/abseil/CMakeLists.txt @@ -1,12 +1,20 @@ -include(FetchContent) FetchContent_Declare( cpp-abseil GIT_REPOSITORY https://github.com/abseil/abseil-cpp.git ) -FetchContent_GetProperties(cpp-abseil) -if(NOT cpp-abseil_POPULATED) - FetchContent_Populate(cpp-abseil) - add_subdirectory(${cpp-abseil_SOURCE_DIR} ${cpp-abseil_BINARY_DIR}) -# set_target_properties(absl::btree PROPERTIES IMPORTED_GLOBAL TRUE) -endif() +FetchContent_MakeAvailable(cpp-abseil) + +mark_as_advanced( + ABSL_ENABLE_INSTALL + ABSL_GOOGLETEST_DOWNLOAD_URL + ABSL_LOCAL_GOOGLETEST_DIR + ABSL_RUN_TESTS + ABSL_USE_EXTERNAL_GOOGLETEST + ABSL_USE_GOOGLETEST_HEAD +) + +mark_as_advanced( + FETCHCONTENT_SOURCE_DIR_CPP-ABSEIL + FETCHCONTENT_UPDATES_DISCONNECTED_CPP-ABSEIL +) diff --git a/externals/btree/CMakeLists.txt b/externals/btree/CMakeLists.txt index 15b8dc7a..ec73ce12 100644 --- a/externals/btree/CMakeLists.txt +++ b/externals/btree/CMakeLists.txt @@ -1,15 +1,20 @@ -include(FetchContent) FetchContent_Declare( cpp-btree GIT_REPOSITORY https://github.com/ahueck/cpp-btree.git ) FetchContent_GetProperties(cpp-btree) + if(NOT cpp-btree_POPULATED) FetchContent_Populate(cpp-btree) - + add_library(cppbtree INTERFACE IMPORTED GLOBAL) target_include_directories(cppbtree SYSTEM INTERFACE ${cpp-btree_SOURCE_DIR}) #target_compile_features(cppbtree INTERFACE cxx_std_11 ) add_library(google::btree ALIAS cppbtree) endif() + +mark_as_advanced( + FETCHCONTENT_SOURCE_DIR_CPP-BTREE + FETCHCONTENT_UPDATES_DISCONNECTED_CPP-BTREE +) \ No newline at end of file diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index a7461810..568774f4 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,61 +1,4 @@ -set(PROJECT_NAME typeart_llvm) -set(TARGETS_EXPORT_NAME ${PROJECT_NAME}-targets) - -add_subdirectory(analysis) - -set(PASS_SOURCES - TypeARTPass.cpp - support/TypeUtil.cpp - support/InstrumentationHelper.cpp - TypeManager.cpp -) - -make_llvm_module(typeartpass - "${PASS_SOURCES}" - LINK_LIBS - typelib - DEPENDS - meminstfinderpass - INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR} - ${PROJECT_SOURCE_DIR}/typelib - ${PROJECT_SOURCE_DIR}/runtime -) - -target_project_compile_options(typeartpass) -target_project_compile_definitions(typeartpass - PRIVATE_DEFS - LOG_LEVEL=${LOG_LEVEL} -) - -if(SHOW_STATS) - target_project_compile_definitions(typeartpass - PRIVATE_DEFS - LLVM_ENABLE_STATS - ) -endif() - -install( - TARGETS typeartpass - EXPORT ${TARGETS_EXPORT_NAME} - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib -) - -# also handles subdir meminstfinder -install( - EXPORT ${TARGETS_EXPORT_NAME} - NAMESPACE typeart:: - DESTINATION lib/cmake -) - -configure_package_config_file( - ${PROJECT_SOURCE_DIR}/cmake/Config.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake - INSTALL_DESTINATION lib/cmake -) - -install(FILES - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake - DESTINATION lib/cmake -) +add_subdirectory(passes) +add_subdirectory(typelib) +add_subdirectory(runtime) +add_subdirectory(mpi_interceptor) \ No newline at end of file diff --git a/lib/TypeARTPass.cpp b/lib/TypeARTPass.cpp deleted file mode 100644 index 3e8e37d3..00000000 --- a/lib/TypeARTPass.cpp +++ /dev/null @@ -1,403 +0,0 @@ -#include "TypeARTPass.h" - -#include "RuntimeInterface.h" -#include "TypeIO.h" -#include "TypeInterface.h" -#include "analysis/MemInstFinderPass.h" -#include "support/Logger.h" -#include "support/TypeUtil.h" -#include "support/Util.h" - -#include "llvm/ADT/Statistic.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Format.h" -#include "llvm/Transforms/Utils/BasicBlockUtils.h" -#include "llvm/Transforms/Utils/CtorUtils.h" -#include "llvm/Transforms/Utils/EscapeEnumerator.h" -#include "llvm/Transforms/Utils/ModuleUtils.h" - -#include -#include -#include - -using namespace llvm; - -#define DEBUG_TYPE "typeart" - -namespace { -static llvm::RegisterPass msp("typeart", "TypeArt type information", false, false); -} // namespace - -static cl::opt ClTypeArtStats("typeart-stats", cl::desc("Show statistics for TypeArt type pass."), cl::Hidden, - cl::init(false)); -static cl::opt ClIgnoreHeap("typeart-no-heap", cl::desc("Ignore heap allocation/free instruction."), cl::Hidden, - cl::init(false)); -static cl::opt ClTypeArtAlloca("typeart-alloca", cl::desc("Track alloca instructions."), cl::Hidden, - cl::init(false)); - -static cl::opt ClTypeFile("typeart-outfile", cl::desc("Location of the generated type file."), cl::Hidden, - cl::init("types.yaml")); - -STATISTIC(NumInstrumentedMallocs, "Number of instrumented mallocs"); -STATISTIC(NumInstrumentedFrees, "Number of instrumented frees"); -STATISTIC(NumInstrumentedAlloca, "Number of instrumented (stack) allocas"); -STATISTIC(NumInstrumentedGlobal, "Number of instrumented globals"); - -namespace tu = typeart::util::type; - -namespace typeart { -namespace pass { - -// Used by LLVM pass manager to identify passes in memory -char TypeArtPass::ID = 0; - -// std::unique_ptr TypeArtPass::typeMapping = std::make_unique(); - -TypeArtPass::TypeArtPass() : llvm::ModulePass(ID), typeManager(ClTypeFile.getValue()) { - assert(!ClTypeFile.empty() && "Default type file not set"); - EnableStatistics(); -} - -void TypeArtPass::getAnalysisUsage(llvm::AnalysisUsage& info) const { - info.addRequired(); -} - -bool TypeArtPass::doInitialization(Module& m) { - instr.setModule(m); - - LOG_DEBUG("Propagating type infos."); - if (typeManager.load()) { - LOG_DEBUG("Existing type configuration successfully loaded from " << ClTypeFile.getValue()); - } else { - LOG_DEBUG("No valid existing type configuration found: " << ClTypeFile.getValue()); - } - - return true; -} - -bool TypeArtPass::runOnModule(Module& m) { - bool globalInstro{false}; - if (ClIgnoreHeap) { - declareInstrumentationFunctions(m); - - DataLayout dl(&m); - - auto& c = m.getContext(); - - const auto instrumentGlobal = [&](auto* global, auto& IRB) { - auto type = global->getValueType(); - - unsigned numElements = 1; - if (type->isArrayTy()) { - numElements = tu::getArrayLengthFlattened(type); - type = tu::getArrayElementType(type); - } - - int typeId = typeManager.getOrRegisterType(type, dl); - auto* typeIdConst = instr.getConstantFor(IType::type_id, typeId); - auto* numElementsConst = instr.getConstantFor(IType::extent, numElements); - auto globalPtr = IRB.CreateBitOrPointerCast(global, instr.getTypeFor(IType::ptr)); - - LOG_DEBUG("Instrumenting global variable: " << util::dump(*global)); - - IRB.CreateCall(typeart_alloc_global.f, ArrayRef{globalPtr, typeIdConst, numElementsConst}); - return true; - }; - - const auto makeCtorFunc = [&]() -> IRBuilder<> { - auto ctorFunctionName = "__typeart_init_module_" + m.getSourceFileName(); - - FunctionType* ctorType = FunctionType::get(llvm::Type::getVoidTy(c), false); - Function* ctorFunction = Function::Create(ctorType, Function::PrivateLinkage, ctorFunctionName, &m); - - BasicBlock* entry = BasicBlock::Create(c, "entry", ctorFunction); - - llvm::appendToGlobalCtors(m, ctorFunction, 0, nullptr); - - IRBuilder<> IRB(entry); - return IRB; - }; - - const auto& globalsList = getAnalysis().getModuleGlobals(); - if (!globalsList.empty()) { - auto IRB = makeCtorFunc(); - auto instrGlobalCount = llvm::count_if(globalsList, [&](auto g) { return instrumentGlobal(g, IRB); }); - NumInstrumentedGlobal += instrGlobalCount; - globalInstro = instrGlobalCount > 0; - IRB.CreateRetVoid(); - } - } - const auto instrumentedF = llvm::count_if(m.functions(), [&](auto& f) { return runOnFunc(f); }) > 0; - return instrumentedF || globalInstro; -} - -bool TypeArtPass::runOnFunc(Function& f) { - using namespace typeart; - - if (f.isDeclaration() || f.getName().startswith("__typeart")) { - return false; - } - - if (!getAnalysis().hasFunctionData(&f)) { - LOG_WARNING("No allocation data could be retrieved for function: " << f.getName()); - return false; - } - - LOG_DEBUG("Running on function: " << f.getName()) - - // FIXME this is required when "PassManagerBuilder::EP_OptimizerLast" is used as the function (constant) pointer are - // nullpointer/invalidated - declareInstrumentationFunctions(*f.getParent()); - - bool mod{false}; - // auto& c = f.getContext(); - DataLayout dl(f.getParent()); - - llvm::SmallDenseMap allocCounts; - - const auto& fData = getAnalysis().getFunctionData(&f); - const auto& listMalloc = fData.listMalloc; - const auto& listAlloca = fData.listAlloca; - const auto& listFree = fData.listFree; - - const auto instrumentMalloc = [&](const auto& malloc) -> bool { - const auto mallocInst = malloc.call; - BitCastInst* primaryBitcast = malloc.primary; - - // Number of bytes allocated - auto mallocArg = mallocInst->getOperand(0); - int typeId = typeManager.getOrRegisterType(mallocInst->getType()->getPointerElementType(), - dl); // retrieveTypeID(tu::getVoidType(c)); - if (typeId == TA_UNKNOWN_TYPE) { - LOG_ERROR("Unknown allocated type. Not instrumenting. " << util::dump(*mallocInst)); - return false; - } - - // Number of bytes per element, 1 for void* - unsigned typeSize = tu::getTypeSizeInBytes(mallocInst->getType()->getPointerElementType(), dl); - auto insertBefore = mallocInst->getNextNode(); - - // Use the first cast as the determining type (if there is any) - if (primaryBitcast) { - auto* dstPtrType = primaryBitcast->getDestTy()->getPointerElementType(); - - typeSize = tu::getTypeSizeInBytes(dstPtrType, dl); - - // Resolve arrays - // TODO: Write tests for this case - if (dstPtrType->isArrayTy()) { - dstPtrType = tu::getArrayElementType(dstPtrType); - } - - typeId = typeManager.getOrRegisterType(dstPtrType, dl); - if (typeId == TA_UNKNOWN_TYPE) { - LOG_ERROR("Target type of casted allocation is unknown. Not instrumenting. " << util::dump(*mallocInst)); - LOG_ERROR("Cast: " << util::dump(*primaryBitcast)); - LOG_ERROR("Target type: " << util::dump(*dstPtrType)); - return false; - } - } - - IRBuilder<> IRB(insertBefore); - auto* typeIdConst = instr.getConstantFor(IType::type_id, typeId); - auto* typeSizeConst = instr.getConstantFor(IType::extent, typeSize); - // Compute element count: count = numBytes / typeSize - Value* elementCount = nullptr; - if (malloc.kind == MemOpKind::MALLOC) { - elementCount = IRB.CreateUDiv(mallocArg, typeSizeConst); - } else if (malloc.kind == MemOpKind::CALLOC) { - elementCount = mallocInst->getOperand(0); // get the element count in calloc call - } else if (malloc.kind == MemOpKind::REALLOC) { - auto mArg = mallocInst->getOperand(1); - elementCount = IRB.CreateUDiv(mArg, typeSizeConst); - - IRBuilder<> FreeB(mallocInst); - auto addrOp = mallocInst->getOperand(0); - FreeB.CreateCall(typeart_free.f, ArrayRef{addrOp}); - - } else { - LOG_ERROR("Unknown malloc kind. Not instrumenting. " << util::dump(*mallocInst)); - return false; - } - - IRB.CreateCall(typeart_alloc.f, ArrayRef{mallocInst, typeIdConst, elementCount}); - - return true; - }; - - const auto instrumentFree = [&](const auto& free_inst) -> bool { - // Pointer address: - auto freeArg = free_inst->getOperand(0); - IRBuilder<> IRB(free_inst->getNextNode()); - IRB.CreateCall(typeart_free.f, ArrayRef{freeArg}); - - return true; - }; - - const auto instrumentAlloca = [&](const auto& allocaData) -> bool { - auto alloca = allocaData.alloca; - Type* elementType = alloca->getAllocatedType(); - Value* numElementsVal = nullptr; - // The length can be specified statically through the array type or as a separate argument. - // Both cases are handled here. - if (allocaData.is_vla) { - numElementsVal = alloca->getArraySize(); - // This should not happen in generated IR code - assert(!elementType->isArrayTy() && "VLAs of array types are currently not supported."); - } else { - size_t arraySize = allocaData.arraySize; - if (elementType->isArrayTy()) { - arraySize = arraySize * tu::getArrayLengthFlattened(elementType); - elementType = tu::getArrayElementType(elementType); - } - numElementsVal = instr.getConstantFor(IType::extent, arraySize); - } - - IRBuilder<> IRB(alloca->getNextNode()); - - // unsigned typeSize = tu::getTypeSizeInBytes(elementType, dl); - int typeId = typeManager.getOrRegisterType(elementType, dl); - - if (typeId == TA_UNKNOWN_TYPE) { - LOG_ERROR("Type is not supported: " << util::dump(*elementType)); - } - - auto* typeIdConst = instr.getConstantFor(IType::type_id, typeId); - auto arrayPtr = IRB.CreateBitOrPointerCast(alloca, instr.getTypeFor(IType::ptr)); - - IRB.CreateCall(typeart_alloc_stack.f, ArrayRef{arrayPtr, typeIdConst, numElementsVal}); - - allocCounts[alloca->getParent()]++; - - ++NumInstrumentedAlloca; - return true; - }; - - if (!ClIgnoreHeap) { - // instrument collected calls of bb: - for (const auto& malloc : listMalloc) { - ++NumInstrumentedMallocs; - mod |= instrumentMalloc(malloc); - } - - for (auto* free : listFree) { - ++NumInstrumentedFrees; - mod |= instrumentFree(free); - } - } - if (ClTypeArtAlloca) { - const bool instrumented_alloca = std::count_if(listAlloca.begin(), listAlloca.end(), instrumentAlloca) > 0; - mod |= instrumented_alloca; - - if (instrumented_alloca) { - // LOG_DEBUG("Add alloca counter") - // counter = 0 at beginning of function - IRBuilder<> CBuilder(f.getEntryBlock().getFirstNonPHI()); - auto* counter = CBuilder.CreateAlloca(instr.getTypeFor(IType::stack_count), nullptr, "__ta_alloca_counter"); - CBuilder.CreateStore(instr.getConstantFor(IType::stack_count), counter); - - // In each basic block: counter =+ num_alloca (in BB) - for (auto data : allocCounts) { - IRBuilder<> IRB(data.first->getTerminator()); - auto* load_counter = IRB.CreateLoad(counter); - Value* increment_counter = IRB.CreateAdd(instr.getConstantFor(IType::stack_count, data.second), load_counter); - IRB.CreateStore(increment_counter, counter); - } - - // Find return instructions: - // if(counter > 0) call runtime for stack cleanup - EscapeEnumerator ee(f); - while (IRBuilder<>* irb = ee.Next()) { - auto* I = &(*irb->GetInsertPoint()); - - auto* counter_load = irb->CreateLoad(counter, "__ta_counter_load"); - auto* cond = irb->CreateICmpNE(counter_load, instr.getConstantFor(IType::stack_count), "__ta_cond"); - auto* then_term = SplitBlockAndInsertIfThen(cond, I, false); - irb->SetInsertPoint(then_term); - irb->CreateCall(typeart_leave_scope.f, ArrayRef{counter_load}); - } - } - } else { - NumInstrumentedAlloca += listAlloca.size(); - } - - return mod; -} // namespace pass - -bool TypeArtPass::doFinalization(Module&) { - /* - * Persist the accumulated type definition information for this module. - */ - LOG_DEBUG("Writing type file to " << ClTypeFile.getValue()); - - if (typeManager.store()) { - LOG_DEBUG("Success!"); - } else { - LOG_FATAL("Failed writing type config to " << ClTypeFile.getValue()); - } - if (ClTypeArtStats && AreStatisticsEnabled()) { - auto& out = llvm::errs(); - printStats(out); - } - return false; -} - -void TypeArtPass::declareInstrumentationFunctions(Module& m) { - // Remove this return if problems come up during compilation - if (typeart_alloc_global.f != nullptr && typeart_alloc_stack.f != nullptr && typeart_alloc.f != nullptr && - typeart_free.f != nullptr && typeart_leave_scope.f != nullptr) { - return; - } - - auto alloc_arg_types = instr.make_parameters(IType::ptr, IType::type_id, IType::extent); - auto free_arg_types = instr.make_parameters(IType::ptr); - auto leavescope_arg_types = instr.make_parameters(IType::stack_count); - - typeart_alloc.f = instr.make_function(typeart_alloc.name, alloc_arg_types); - typeart_alloc_stack.f = instr.make_function(typeart_alloc_stack.name, alloc_arg_types); - typeart_alloc_global.f = instr.make_function(typeart_alloc_global.name, alloc_arg_types); - typeart_free.f = instr.make_function(typeart_free.name, free_arg_types); - typeart_leave_scope.f = instr.make_function(typeart_leave_scope.name, leavescope_arg_types); -} - -void TypeArtPass::printStats(llvm::raw_ostream& out) { - const unsigned max_string{12u}; - const unsigned max_val{5u}; - std::string line(22, '-'); - line += "\n"; - const auto make_format = [&](const char* desc, const auto val) { - return format("%-*s: %*u\n", max_string, desc, max_val, val); - }; - - out << line; - out << " TypeArtPass\n"; - out << line; - out << "Heap Memory\n"; - out << line; - out << make_format("Malloc", NumInstrumentedMallocs.getValue()); - out << make_format("Free", NumInstrumentedFrees.getValue()); - out << line; - out << "Stack Memory\n"; - out << line; - out << make_format("Alloca", NumInstrumentedAlloca.getValue()); - out << line; - out << "Global Memory\n"; - out << line; - out << make_format("Global", NumInstrumentedGlobal.getValue()); - out << line; - out.flush(); -} - -} // namespace pass -} // namespace typeart - -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" - -static void registerClangPass(const llvm::PassManagerBuilder&, llvm::legacy::PassManagerBase& PM) { - PM.add(new typeart::pass::TypeArtPass()); -} -static RegisterStandardPasses RegisterClangPass(PassManagerBuilder::EP_OptimizerLast, registerClangPass); diff --git a/lib/analysis/MemInstFinderPass.cpp b/lib/analysis/MemInstFinderPass.cpp deleted file mode 100644 index 386b9cb4..00000000 --- a/lib/analysis/MemInstFinderPass.cpp +++ /dev/null @@ -1,581 +0,0 @@ -/* - * MemInstFinderPass.cpp - * - * Created on: Jun 3, 2018 - * Author: ahueck - */ - -#include "MemInstFinderPass.h" - -#include "MemOpVisitor.h" -#include "support/Logger.h" -#include "support/TypeUtil.h" -#include "support/Util.h" - -#include "llvm/ADT/Statistic.h" -#include "llvm/IR/Instructions.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Format.h" - -using namespace llvm; - -#define DEBUG_TYPE "meminstanalysis" - -namespace { -static RegisterPass X("mem-inst-finder", // pass option - "Find heap and stack allocations in a program.", // pass description - true, // does not modify the CFG - true // and it's an analysis -); -} // namespace - -static cl::opt ClFilterNonArrayAlloca("alloca-array-only", cl::desc("Only use alloca instructions of arrays."), - cl::Hidden, cl::init(true)); - -static cl::opt ClFilterMallocAllocPair("malloc-store-filter", - cl::desc("Filter allocs that get a store from a heap alloc."), cl::Hidden, - cl::init(false)); - -static cl::opt ClCallFilter("call-filter", - cl::desc("Filter alloca instructions that are passed to specific calls."), cl::Hidden, - cl::init(false)); - -static cl::opt ClCallFilterDeep("call-filter-deep", - cl::desc("If the CallFilter matches, we look if the value is passed as a void*."), - cl::Hidden, cl::init(false)); - -static cl::opt ClCallFilterGlob("call-filter-str", cl::desc("Filter alloca instructions based on string."), - cl::Hidden, cl::init("MPI_*")); - -static cl::opt ClFilterGlobal("filter-globals", cl::desc("Filter globals of a module."), cl::Hidden, - cl::init(true)); - -STATISTIC(NumDetectedHeap, "Number of detected heap allocs"); -STATISTIC(NumFilteredDetectedHeap, "Number of filtered heap allocs"); -STATISTIC(NumDetectedAllocs, "Number of detected allocs"); -STATISTIC(NumCallFilteredAllocs, "Number of call filtered allocs"); -STATISTIC(NumFilteredMallocAllocs, "Number of filtered malloc-related allocs"); -STATISTIC(NumFilteredNonArrayAllocs, "Number of call filtered allocs"); -STATISTIC(NumDetectedGlobals, "Number of detected globals"); -STATISTIC(NumFilteredGlobals, "Number of filtered globals"); -STATISTIC(NumCallFilteredGlobals, "Number of filtered globals"); - -namespace typeart { - -using namespace finder; - -namespace filter { - -class CallFilter::FilterImpl { - const std::string call_regex; - bool malloc_mode{false}; - llvm::Function* start_f{nullptr}; - int depth{0}; - - public: - explicit FilterImpl(const std::string& glob) : call_regex(util::glob2regex(glob)) { - } - - void setMode(bool search_malloc) { - malloc_mode = search_malloc; - } - - void setStartingFunction(llvm::Function* start) { - start_f = start; - depth = 0; - } - - bool filter(Value* in) { - if (in == nullptr) { - LOG_DEBUG("Called with nullptr"); - return false; - } - - if (depth == 15) { - return false; - } - - const auto match = [&](auto callee) -> bool { - const auto name = FilterImpl::getName(callee); - return util::regex_matches(call_regex, name); - }; - - llvm::SmallPtrSet visited_set; - llvm::SmallVector working_set; - llvm::SmallVector working_set_calls; - - const auto addToWork = [&visited_set, &working_set](auto vals) { - for (auto v : vals) { - if (visited_set.find(v) == visited_set.end()) { - working_set.push_back(v); - visited_set.insert(v); - } - } - }; - - const auto peek = [&working_set]() -> Value* { - auto user_iter = working_set.end() - 1; - working_set.erase(user_iter); - return *user_iter; - }; - - // Seed working set with users of value (e.g., our AllocaInst) - addToWork(in->users()); - - // Search through all users of users of .... (e.g., our AllocaInst) - while (!working_set.empty()) { - auto val = peek(); - - // If we encounter a callsite, we want to analyze later, or quit in case we have a regex match - CallSite c(val); - if (c.isCall()) { - const auto callee = c.getCalledFunction(); - const bool indirect_call = callee == nullptr; - - if (indirect_call) { - LOG_DEBUG("Found an indirect call, not filtering alloca: " << util::dump(*val)); - return false; // Indirect calls might contain critical function calls. - } - - const bool is_decl = callee->isDeclaration(); - // FIXME the MPI calls are all hitting this branch (obviously) - if (is_decl) { - LOG_DEBUG("Found call with declaration only. Call: " << util::dump(*c.getInstruction())); - if (c.getIntrinsicID() == Intrinsic::not_intrinsic /*Intrinsic::ID::not_intrinsic*/) { - if (ClCallFilterDeep && match(callee) && shouldContinue(c, in)) { - continue; - } - return false; - } else { - LOG_DEBUG("Call is an intrinsic. Continue analyzing...") - continue; - } - } - - if (match(callee)) { - LOG_DEBUG("Found a call. Call: " << util::dump(*c.getInstruction())); - if (ClCallFilterDeep && shouldContinue(c, in)) { - continue; - } - return false; - } - - working_set_calls.push_back(c); - // Caveat: below at the end of the loop, we add users of the function call to the search even though it might be - // a simple "sink" for the alloca we analyse - } else if (auto store = llvm::dyn_cast(val)) { - // If we encounter a store, we follow the store target pointer. - // More inclusive than strictly necessary in some cases. - LOG_DEBUG("Store found: " << util::dump(*store) - << " Store target has users: " << util::dump(store->getPointerOperand()->users())); - auto store_target = store->getPointerOperand(); - // FIXME here we check store operand, if target is another alloca, we already track that?: - // Note: if we apply this to malloc filtering, this might become problematic? - if (!malloc_mode && llvm::isa(store_target)) { - LOG_DEBUG("Target is alloca, skipping!"); - } else { - addToWork(store_target->users()); - } - continue; - } - // cont. our search - addToWork(val->users()); - } - ++depth; - return std::all_of(working_set_calls.begin(), working_set_calls.end(), [&](CallSite c) { return filter(c, in); }); - } - - private: - bool filter(CallSite& csite, Value* in) { - const auto analyse_arg = [&](auto& csite, auto argNum) -> bool { - Argument& the_arg = *(csite.getCalledFunction()->arg_begin() + argNum); - LOG_DEBUG("Calling filter with inst of argument: " << util::dump(the_arg)); - const bool filter_arg = filter(&the_arg); - LOG_DEBUG("Should filter? : " << filter_arg); - return filter_arg; - }; - - LOG_DEBUG("Analyzing function call " << csite.getCalledFunction()->getName()); - - if (csite.getCalledFunction() == start_f) { - return true; - } - - // this only works if we can correlate alloca with argument: - const auto pos = std::find_if(csite.arg_begin(), csite.arg_end(), - [&in](const Use& arg_use) -> bool { return arg_use.get() == in; }); - // auto pos = csite.arg_end(); - if (pos != csite.arg_end()) { - const auto argNum = std::distance(csite.arg_begin(), pos); - LOG_DEBUG("Found exact position: " << argNum); - return analyse_arg(csite, argNum); - } else { - LOG_DEBUG("Analyze all args, cannot correlate alloca with arg."); - return std::all_of(csite.arg_begin(), csite.arg_end(), [&csite, &analyse_arg](const Use& arg_use) { - auto argNum = csite.getArgumentNo(&arg_use); - return analyse_arg(csite, argNum); - }); - } - - return true; - } - - bool filter(Argument* arg) { - for (auto* user : arg->users()) { - LOG_DEBUG("Looking at arg user " << util::dump(*user)); - // This code is for non mem2reg code (i.e., where the argument is stored to a local alloca): - if (auto store = llvm::dyn_cast(user)) { - // if (auto* alloca = llvm::dyn_cast(store->getPointerOperand())) { - // LOG_DEBUG("Argument is a store inst and the operand is alloca"); - return filter(store->getPointerOperand()); - // } - } - } - return filter(llvm::dyn_cast(arg)); - } - - bool shouldContinue(CallSite c, Value* in) const { - LOG_DEBUG("Found a name match, analyzing closer..."); - const auto is_void_ptr = [](Type* type) { - return type->isPointerTy() && type->getPointerElementType()->isIntegerTy(8); - }; - const auto arg_pos = llvm::find_if(c.args(), [&in](const Use& arg_use) -> bool { return arg_use.get() == in; }); - if (arg_pos == c.arg_end()) { - // we had no direct correlation for the arg position - // Now checking if void* is passed, if not we can potentially filter! - auto count_void_ptr = llvm::count_if(c.args(), [&is_void_ptr](const auto& arg) { - const auto type = arg->getType(); - return is_void_ptr(type); - }); - if (count_void_ptr > 0) { - LOG_DEBUG("Call takes a void*, filtering."); - return false; - } - LOG_DEBUG("Call has no void* argument"); - } else { - // We have an arg_pos match - const auto argNum = std::distance(c.arg_begin(), arg_pos); - Argument& the_arg = *(c.getCalledFunction()->arg_begin() + argNum); - auto type = the_arg.getType(); - if (is_void_ptr(type)) { - LOG_DEBUG("Call arg is a void*, filtering."); - return false; - } - LOG_DEBUG("Value* in is not passed as void ptr"); - } - LOG_DEBUG("No filter necessary for this call, continue."); - return true; - } - - static inline std::string getName(const Function* f) { - auto name = f->getName(); - // FIXME figure out if we need to demangle, i.e., source is .c or .cpp - const auto f_name = util::demangle(name); - if (f_name != "") { - name = f_name; - } - - return name; - } -}; - -CallFilter::CallFilter(const std::string& glob) : fImpl{std::make_unique(glob)} { -} - -bool CallFilter::operator()(AllocaInst* in) { - LOG_DEBUG("Analyzing value: " << util::dump(*in)); - fImpl->setMode(/*search mallocs = */ false); - fImpl->setStartingFunction(in->getParent()->getParent()); - const auto filter_ = fImpl->filter(in); - if (filter_) { - LOG_DEBUG("Filtering value: " << util::dump(*in) << "\n"); - } else { - LOG_DEBUG("Keeping value: " << util::dump(*in) << "\n"); - } - return filter_; -} - -bool CallFilter::operator()(GlobalValue* g) { - LOG_DEBUG("Analyzing value: " << util::dump(*g)); - fImpl->setMode(/*search mallocs = */ false); - fImpl->setStartingFunction(nullptr); - const auto filter_ = fImpl->filter(g); - if (filter_) { - LOG_DEBUG("Filtering value: " << util::dump(*g) << "\n"); - } else { - LOG_DEBUG("Keeping value: " << util::dump(*g) << "\n"); - } - return filter_; -} - -CallFilter& CallFilter::operator=(CallFilter&&) noexcept = default; - -CallFilter::~CallFilter() = default; - -} // namespace filter - -char MemInstFinderPass::ID = 0; - -MemInstFinderPass::MemInstFinderPass() : llvm::ModulePass(ID), mOpsCollector(), filter(ClCallFilterGlob.getValue()) { -} - -void MemInstFinderPass::getAnalysisUsage(llvm::AnalysisUsage& info) const { - info.setPreservesAll(); -} - -bool MemInstFinderPass::runOnModule(Module& m) { - mOpsCollector.visitModuleGlobals(m); - auto& globals = mOpsCollector.listGlobals; - NumDetectedGlobals += globals.size(); - if (ClFilterGlobal && !ClFilterNonArrayAlloca) { - globals.erase( - llvm::remove_if( - globals, - [&](const auto g) { - const auto name = g->getName(); - if (name.startswith("llvm.") || name.startswith("__llvm_gcov") || name.startswith("__llvm_gcda")) { - // 2nd and 3rd check: Check if the global is private gcov data. - return true; - } - - if (g->hasInitializer()) { - auto* ini = g->getInitializer(); - StringRef ini_name = util::dump(*ini); - - if (ini_name.contains("std::ios_base::Init")) { - return true; - } - } - // if (!g->hasInitializer()) { - // return true; - // } - - if (g->hasSection()) { - StringRef Section = g->getSection(); - - // Globals from llvm.metadata aren't emitted, do not instrument them. - if (Section == "llvm.metadata") { - return true; - } - // Do not instrument globals from special LLVM sections. - if (Section.find("__llvm") != StringRef::npos || Section.find("__LLVM") != StringRef::npos) { - return true; - } - // Check if the global is in the PGO counters section. - // auto OF = Triple(m.getTargetTriple()).getObjectFormat(); - // if (Section.endswith(getInstrProfSectionName(IPSK_cnts, OF, - // /*AddSegmentInfo=*/false))) { - // return true; - // } - } - - if (g->getLinkage() == GlobalValue::ExternalLinkage || g->getLinkage() == GlobalValue::PrivateLinkage) { - return true; - } - - Type* t = g->getValueType(); - if (!t->isSized()) { - return true; - } - - if (t->isArrayTy()) { - t = t->getArrayElementType(); - } - if (auto structType = dyn_cast(t)) { - if (structType->isOpaque()) { - LOG_DEBUG("Encountered opaque struct " << t->getStructName() << " - skipping..."); - return true; - } - } - return false; - }), - globals.end()); - - const auto beforeCallFilter = globals.size(); - NumFilteredGlobals = NumDetectedGlobals - beforeCallFilter; - - globals.erase(llvm::remove_if(globals, [&](const auto g) { return filter(g); }), globals.end()); - - NumCallFilteredGlobals = beforeCallFilter - globals.size(); - NumFilteredGlobals += NumCallFilteredGlobals; - } - - return llvm::count_if(m.functions(), [&](auto& f) { return runOnFunc(f); }) > 0; -} // namespace typeart - -bool MemInstFinderPass::runOnFunc(llvm::Function& f) { - if (f.isDeclaration() || f.getName().startswith("__typeart")) { - return false; - } - - mOpsCollector.visit(f); - - LOG_DEBUG("Running on function: " << f.getName()) - - const auto checkAmbigiousMalloc = [](const MallocData& mallocData) { - auto primaryBitcast = mallocData.primary; - if (primaryBitcast) { - const auto& bitcasts = mallocData.bitcasts; - std::for_each(bitcasts.begin(), bitcasts.end(), [&](auto bitcastInst) { - if (bitcastInst != primaryBitcast && (!typeart::util::type::isVoidPtr(bitcastInst->getDestTy()) && - primaryBitcast->getDestTy() != bitcastInst->getDestTy())) { - // Second non-void* bitcast detected - semantics unclear - LOG_WARNING("Encountered ambiguous pointer type in allocation: " << util::dump(*(mallocData.call))); - LOG_WARNING(" Primary cast: " << util::dump(*primaryBitcast)); - LOG_WARNING(" Secondary cast: " << util::dump(*bitcastInst)); - } - }); - } - }; - - NumDetectedAllocs += mOpsCollector.listAlloca.size(); - - if (ClFilterNonArrayAlloca) { - auto& allocs = mOpsCollector.listAlloca; - allocs.erase(llvm::remove_if(allocs, - [&](const auto& data) { - if (!data.alloca->getAllocatedType()->isArrayTy() && data.arraySize == 1) { - ++NumFilteredNonArrayAllocs; - return true; - } - return false; - }), - allocs.end()); - } - - if (ClFilterMallocAllocPair) { - auto& allocs = mOpsCollector.listAlloca; - auto& mlist = mOpsCollector.listMalloc; - - const auto filterMallocAllocPairing = [&mlist](const auto alloc) { - // Only look for the direct users of the alloc: - // TODO is a deeper analysis required? - for (auto inst : alloc->users()) { - if (StoreInst* store = dyn_cast(inst)) { - const auto source = store->getValueOperand(); - if (isa(source)) { - for (auto& mdata : mlist) { - // is it a bitcast we already collected? if yes, we can filter the alloc - return std::any_of(mdata.bitcasts.begin(), mdata.bitcasts.end(), - [&source](const auto bcast) { return bcast == source; }); - } - } else if (isa(source)) { - return std::any_of(mlist.begin(), mlist.end(), - [&source](const auto& mdata) { return mdata.call == source; }); - } - } - } - return false; - }; - - allocs.erase(llvm::remove_if(allocs, - [&](const auto& data) { - if (filterMallocAllocPairing(data.alloca)) { - ++NumFilteredMallocAllocs; - return true; - } - return false; - }), - allocs.end()); - } - - if (ClCallFilter) { - auto& allocs = mOpsCollector.listAlloca; - allocs.erase(llvm::remove_if(allocs, - [&](const auto& data) { - if (filter(data.alloca)) { - ++NumCallFilteredAllocs; - return true; - } - return false; - }), - allocs.end()); - // LOG_DEBUG(allocs.size() << " allocas to instrument : " << util::dump(allocs)); - } - - auto& mallocs = mOpsCollector.listMalloc; - NumDetectedHeap += mallocs.size(); - - for (const auto& mallocData : mallocs) { - checkAmbigiousMalloc(mallocData); - } - - FunctionData d{mOpsCollector.listMalloc, mOpsCollector.listFree, mOpsCollector.listAlloca}; - functionMap[&f] = d; - - mOpsCollector.clear(); - - return false; -} // namespace typeart - -bool MemInstFinderPass::doFinalization(llvm::Module&) { - if (AreStatisticsEnabled()) { - auto& out = llvm::errs(); - printStats(out); - } - return false; -} - -void MemInstFinderPass::printStats(llvm::raw_ostream& out) { - const unsigned max_string{28u}; - const unsigned max_val{5u}; - std::string line(42, '-'); - line += "\n"; - const auto make_format = [&](const char* desc, const auto val) { - return format("%-*s: %*.1f\n", max_string, desc, max_val, val); - }; - - auto all_stack = double(NumDetectedAllocs.getValue()); - auto nonarray_stack = double(NumFilteredNonArrayAllocs.getValue()); - auto malloc_alloc_stack = double(NumFilteredMallocAllocs.getValue()); - auto call_filter_stack = double(NumCallFilteredAllocs.getValue()); - - out << line; - out << " MemInstFinderPass\n"; - out << line; - out << "Heap Memory\n"; - out << line; - out << make_format("Heap alloc", double(NumDetectedHeap.getValue())); - out << make_format( - "% call filtered", - (double(NumFilteredDetectedHeap.getValue()) / std::max(1.0, double(NumDetectedHeap.getValue()))) * 100.0); - out << line; - out << "Stack Memory\n"; - out << line; - out << make_format("Alloca", all_stack); - out << make_format("% non array filtered", (nonarray_stack / std::max(1.0, all_stack)) * 100.0); - out << make_format("% malloc-alloc filtered", - (malloc_alloc_stack / std::max(1.0, all_stack - nonarray_stack)) * 100.0); - out << make_format("% call filtered", - (call_filter_stack / std::max(1.0, all_stack - nonarray_stack - malloc_alloc_stack)) * 100.0); - out << line; - out << "Global Memory\n"; - out << line; - out << make_format("Global", double(NumDetectedGlobals.getValue())); - out << make_format("Global total filtered", double(NumFilteredGlobals.getValue())); - out << make_format("Global Call Filtered", double(NumCallFilteredGlobals.getValue())); - out << make_format( - "% global call filtered", - (double(NumCallFilteredGlobals.getValue()) / std::max(1.0, double(NumDetectedGlobals.getValue()))) * 100.0); - out << make_format( - "% global filtered", - (double(NumFilteredGlobals.getValue()) / std::max(1.0, double(NumDetectedGlobals.getValue()))) * 100.0); - out << line; - out.flush(); -} - -bool MemInstFinderPass::hasFunctionData(Function* f) const { - auto iter = functionMap.find(f); - return iter != functionMap.end(); -} - -const FunctionData& MemInstFinderPass::getFunctionData(Function* f) const { - auto iter = functionMap.find(f); - return iter->second; -} - -const llvm::SmallVector& MemInstFinderPass::getModuleGlobals() const { - return mOpsCollector.listGlobals; -} - -} // namespace typeart diff --git a/lib/analysis/MemOpVisitor.cpp b/lib/analysis/MemOpVisitor.cpp deleted file mode 100644 index 89779e57..00000000 --- a/lib/analysis/MemOpVisitor.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * MemOpVisitor.cpp - * - * Created on: Jan 3, 2018 - * Author: ahueck - */ - -#include "MemOpVisitor.h" - -#include "support/Logger.h" -#include "support/TypeUtil.h" -#include "support/Util.h" - -#include - -namespace typeart { -namespace finder { -using namespace llvm; - -MemOpVisitor::MemOpVisitor() = default; - -void MemOpVisitor::visitModuleGlobals(Module& m) { - for (auto& g : m.globals()) { - listGlobals.push_back(&g); - } -} - -void MemOpVisitor::visitCallInst(llvm::CallInst& ci) { - const auto isInSet = [&](const auto& fMap) -> llvm::Optional { - const auto* f = ci.getCalledFunction(); - if (!f) { - // TODO handle calls through, e.g., function pointers? - seems infeasible - LOG_INFO("Encountered indirect call, skipping."); - return None; - } - const auto name = f->getName().str(); - const auto res = fMap.find(name); - if (res != fMap.end()) { - return {(*res).second}; - } - return None; - }; - - if (auto val = isInSet(allocMap)) { - visitMallocLike(ci, val.getValue()); - } else if (auto val = isInSet(deallocMap)) { - visitFreeLike(ci, val.getValue()); - } -} - -void MemOpVisitor::visitMallocLike(llvm::CallInst& ci, MemOpKind k) { - // LOG_DEBUG("Found malloc-like: " << ci.getCalledFunction()->getName()); - - SmallPtrSet bcasts; - - for (auto user : ci.users()) { - // Simple case: Pointer is immediately casted - if (auto inst = dyn_cast(user)) { - bcasts.insert(inst); - } - // Pointer is first stored, then loaded and subsequently casted - if (auto storeInst = dyn_cast(user)) { - auto storeAddr = storeInst->getPointerOperand(); - for (auto storeUser : storeAddr->users()) { // TODO: Ensure that load occurs ofter store? - if (auto loadInst = dyn_cast(storeUser)) { - for (auto loadUser : loadInst->users()) { - if (auto bcastInst = dyn_cast(loadUser)) { - bcasts.insert(bcastInst); - } - } - } - } - } - } - - BitCastInst* primaryBitcast{nullptr}; - std::for_each(bcasts.begin(), bcasts.end(), [&](auto bcast) { - if (!util::type::isVoidPtr(bcast->getDestTy())) { - primaryBitcast = bcast; - } - }); - - // LOG_DEBUG(" >> number of bitcasts found: " << bcasts.size()); - - listMalloc.push_back(MallocData{&ci, primaryBitcast, bcasts, k}); -} - -void MemOpVisitor::visitFreeLike(llvm::CallInst& ci, MemOpKind) { - // LOG_DEBUG(ci.getCalledFunction()->getName()); - - listFree.insert(&ci); -} - -// void MemOpVisitor::visitIntrinsicInst(llvm::IntrinsicInst& ii) { -// -//} - -void MemOpVisitor::visitAllocaInst(llvm::AllocaInst& ai) { - // LOG_DEBUG("Found alloca " << ai); - Value* arraySizeOperand = ai.getArraySize(); - size_t arraySize{0}; // FIXME avoid using int ( - bool is_vla{false}; - if (auto arraySizeConst = llvm::dyn_cast(arraySizeOperand)) { - arraySize = arraySizeConst->getZExtValue(); - } else { - is_vla = true; - } - - listAlloca.push_back({&ai, arraySize, is_vla}); - // LOG_DEBUG("Alloca: " << util::dump(ai) << " -> lifetime marker: " << util::dump(lifetimes)); -} // namespace typeart - -void MemOpVisitor::clear() { - listAlloca.clear(); - listMalloc.clear(); - listFree.clear(); -} - -MemOpVisitor::~MemOpVisitor() = default; - -} // namespace finder -} // namespace typeart diff --git a/lib/analysis/MemOpVisitor.h b/lib/analysis/MemOpVisitor.h deleted file mode 100644 index 9801a46f..00000000 --- a/lib/analysis/MemOpVisitor.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * MemOpVisitor.h - * - * Created on: Jan 3, 2018 - * Author: ahueck - */ - -#ifndef LIB_MEMOPVISITOR_H_ -#define LIB_MEMOPVISITOR_H_ - -#include "llvm/IR/InstVisitor.h" - -#include - -namespace typeart { - -enum class MemOpKind { MALLOC, CALLOC, REALLOC, FREE }; -struct MallocData { - llvm::CallInst* call{nullptr}; - llvm::BitCastInst* primary{nullptr}; // Non-null if non (void*) cast exists - llvm::SmallPtrSet bitcasts; - MemOpKind kind; -}; - -struct AllocaData { - llvm::AllocaInst* alloca{nullptr}; - size_t arraySize; - bool is_vla{false}; -}; - -namespace finder { - -struct MemOpVisitor : public llvm::InstVisitor { - MemOpVisitor(); - void clear(); - void visitModuleGlobals(llvm::Module& m); - void visitCallInst(llvm::CallInst& ci); - void visitMallocLike(llvm::CallInst& ci, MemOpKind k); - void visitFreeLike(llvm::CallInst& ci, MemOpKind k); - // void visitIntrinsicInst(llvm::IntrinsicInst& ii); - void visitAllocaInst(llvm::AllocaInst& ai); - virtual ~MemOpVisitor(); - - llvm::SmallVector listGlobals; - llvm::SmallVector listMalloc; - llvm::SmallPtrSet listFree; - llvm::SmallVector listAlloca; - - private: - // clang-format off - const std::map allocMap{{"malloc", MemOpKind::MALLOC}, - {"_Znwm", MemOpKind::MALLOC}, /*new*/ - {"_Znam", MemOpKind::MALLOC}, /*new[]*/ - {"calloc", MemOpKind::CALLOC}, - {"realloc", MemOpKind::REALLOC} - }; - const std::map deallocMap{{"free", MemOpKind::FREE}, - {"_ZdlPv", MemOpKind::FREE}, /*delete*/ - {"_ZdaPv", MemOpKind::FREE} /*delete[]*/ - }; - // clang-format on -}; - -} // namespace finder -} // namespace typeart - -#endif /* LIB_MEMOPVISITOR_H_ */ diff --git a/test/mpi_interceptor/CMakeLists.txt b/lib/mpi_interceptor/CMakeLists.txt similarity index 96% rename from test/mpi_interceptor/CMakeLists.txt rename to lib/mpi_interceptor/CMakeLists.txt index 145a8025..514cb887 100644 --- a/test/mpi_interceptor/CMakeLists.txt +++ b/lib/mpi_interceptor/CMakeLists.txt @@ -23,7 +23,7 @@ if(${MPI_C_FOUND} AND ${PYTHONINTERP_FOUND}) target_include_directories(interceptor-rt PUBLIC - ${PROJECT_SOURCE_DIR}/runtime + ${PROJECT_SOURCE_DIR}/lib ${CMAKE_CURRENT_SOURCE_DIR} ) diff --git a/test/mpi_interceptor/InterceptorFunctions.h b/lib/mpi_interceptor/InterceptorFunctions.h similarity index 91% rename from test/mpi_interceptor/InterceptorFunctions.h rename to lib/mpi_interceptor/InterceptorFunctions.h index 1a5d8a1d..78a21b56 100644 --- a/test/mpi_interceptor/InterceptorFunctions.h +++ b/lib/mpi_interceptor/InterceptorFunctions.h @@ -8,7 +8,7 @@ #ifndef TEST_MPI_INTERCEPTOR_INTERCEPTORFUNCTIONS_H_ #define TEST_MPI_INTERCEPTOR_INTERCEPTORFUNCTIONS_H_ -#include "RuntimeInterface.h" +#include "runtime/RuntimeInterface.h" #include #include @@ -93,7 +93,7 @@ int ta_check_buffer(const char* mpi_name, const void* called_from, const void* b return -1; } int typeId; - size_t count = 0; + size_t count = 0; typeart_status typeart_status_v = typeart_get_type(buf, &typeId, &count); if (typeart_status_v != TA_OK) { ++mcounter.error; @@ -137,9 +137,9 @@ void ta_exit() { MPI_Comm_rank(MPI_COMM_WORLD, &rank); struct rusage end; getrusage(RUSAGE_SELF, &end); - printf("CCounter (%i) { Send: %i Recv: %i Send_Recv: %i Unsupported: %i MAX RSS[KBytes]: %ld }\n", rank, counter.send, - counter.recv, counter.send_recv, counter.unsupported, end.ru_maxrss); - printf("MCounter (%i) { Error: %i Null_Buf: %i Null_Count: %i }\n", rank, mcounter.error, mcounter.null_buff, + printf("CCounter (%i) { Send: %zu Recv: %zu Send_Recv: %zu Unsupported: %zu MAX RSS[KBytes]: %ld }\n", rank, + counter.send, counter.recv, counter.send_recv, counter.unsupported, end.ru_maxrss); + printf("MCounter (%i) { Error: %zu Null_Buf: %zu Null_Count: %zu }\n", rank, mcounter.error, mcounter.null_buff, mcounter.null_count); } diff --git a/test/mpi_interceptor/mpi_interceptor_tmpl.impl b/lib/mpi_interceptor/mpi_interceptor_tmpl.impl similarity index 100% rename from test/mpi_interceptor/mpi_interceptor_tmpl.impl rename to lib/mpi_interceptor/mpi_interceptor_tmpl.impl diff --git a/test/mpi_interceptor/wrap.py b/lib/mpi_interceptor/wrap.py similarity index 100% rename from test/mpi_interceptor/wrap.py rename to lib/mpi_interceptor/wrap.py diff --git a/lib/passes/CMakeLists.txt b/lib/passes/CMakeLists.txt new file mode 100644 index 00000000..6a33bb66 --- /dev/null +++ b/lib/passes/CMakeLists.txt @@ -0,0 +1,68 @@ +set(PROJECT_NAME typeart_llvm) +set(TARGETS_EXPORT_NAME ${PROJECT_NAME}-targets) + +add_subdirectory(filter) +add_subdirectory(analysis) + +set(PASS_SOURCES + TypeARTPass.cpp + support/TypeUtil.cpp + instrumentation/InstrumentationHelper.cpp + instrumentation/TypeARTFunctions.cpp + TypeManager.cpp + instrumentation/MemOpArgCollector.cpp + instrumentation/MemOpInstrumentation.cpp + instrumentation/Instrumentation.cpp +) + +make_llvm_module(typeartpass + "${PASS_SOURCES}" + LINK_LIBS + typelib + DEPENDS + meminstfinderpass + INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/lib/ + ${PROJECT_SOURCE_DIR}/lib/passes/ +) + +mark_as_advanced(LLVM_TYPEARTPASS_LINK_INTO_TOOLS) + +target_project_compile_options(typeartpass) +target_project_compile_definitions(typeartpass + PRIVATE_DEFS + LOG_LEVEL=${LOG_LEVEL} +) + +if(SHOW_STATS) + target_project_compile_definitions(typeartpass + PRIVATE_DEFS + LLVM_ENABLE_STATS + ) +endif() + +install( + TARGETS typeartpass + EXPORT ${TARGETS_EXPORT_NAME} + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) + +# also handles subdir meminstfinder +install( + EXPORT ${TARGETS_EXPORT_NAME} + NAMESPACE typeart:: + DESTINATION lib/cmake +) + +configure_package_config_file( + ${PROJECT_SOURCE_DIR}/cmake/Config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake + INSTALL_DESTINATION lib/cmake +) + +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake + DESTINATION lib/cmake +) diff --git a/lib/passes/TypeARTPass.cpp b/lib/passes/TypeARTPass.cpp new file mode 100644 index 00000000..842c3907 --- /dev/null +++ b/lib/passes/TypeARTPass.cpp @@ -0,0 +1,215 @@ +#include "TypeARTPass.h" + +//#include "RuntimeInterface.h" +#include "analysis/MemInstFinderPass.h" +#include "instrumentation/MemOpArgCollector.h" +#include "instrumentation/MemOpInstrumentation.h" +#include "instrumentation/TypeARTFunctions.h" +#include "support/Logger.h" +#include "support/Table.h" +#include "typelib/TypeIO.h" + +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Format.h" +#include "llvm/Transforms/Utils/CtorUtils.h" + +#include + +using namespace llvm; + +#define DEBUG_TYPE "typeart" + +namespace { +static llvm::RegisterPass msp("typeart", "TypeArt type information", false, false); +} // namespace + +static cl::opt ClTypeArtStats("typeart-stats", cl::desc("Show statistics for TypeArt type pass."), cl::Hidden, + cl::init(false)); +static cl::opt ClIgnoreHeap("typeart-no-heap", cl::desc("Ignore heap allocation/free instruction."), cl::Hidden, + cl::init(false)); +static cl::opt ClTypeArtAlloca("typeart-alloca", cl::desc("Track alloca instructions."), cl::Hidden, + cl::init(false)); + +static cl::opt ClTypeFile("typeart-outfile", cl::desc("Location of the generated type file."), cl::Hidden, + cl::init("types.yaml")); + +STATISTIC(NumInstrumentedMallocs, "Number of instrumented mallocs"); +STATISTIC(NumInstrumentedFrees, "Number of instrumented frees"); +STATISTIC(NumInstrumentedAlloca, "Number of instrumented (stack) allocas"); +STATISTIC(NumInstrumentedGlobal, "Number of instrumented globals"); + +namespace typeart::pass { + +// Used by LLVM pass manager to identify passes in memory +char TypeArtPass::ID = 0; + +// std::unique_ptr TypeArtPass::typeMapping = std::make_unique(); + +TypeArtPass::TypeArtPass() : llvm::ModulePass(ID), typeManager(ClTypeFile.getValue()) { + assert(!ClTypeFile.empty() && "Default type file not set"); + EnableStatistics(); +} + +void TypeArtPass::getAnalysisUsage(llvm::AnalysisUsage& info) const { + info.addRequired(); +} + +bool TypeArtPass::doInitialization(Module& m) { + instrumentation_helper.setModule(m); + + LOG_DEBUG("Propagating type infos."); + if (typeManager.load()) { + LOG_DEBUG("Existing type configuration successfully loaded from " << ClTypeFile.getValue()); + } else { + LOG_DEBUG("No valid existing type configuration found: " << ClTypeFile.getValue()); + } + + auto arg_collector = std::make_unique(typeManager, instrumentation_helper); + auto mem_instrument = std::make_unique(functions, instrumentation_helper); + instrumentation_context = + std::make_unique(std::move(arg_collector), std::move(mem_instrument)); + + return true; +} + +bool TypeArtPass::runOnModule(Module& m) { + bool instrumented_global{false}; + if (ClTypeArtAlloca) { + declareInstrumentationFunctions(m); + + const auto& globalsList = getAnalysis().getModuleGlobals(); + if (!globalsList.empty()) { + const auto global_count = instrumentation_context->handleGlobal(globalsList); + NumInstrumentedGlobal += global_count; + instrumented_global = global_count > 0; + } + } + + const auto instrumented_function = llvm::count_if(m.functions(), [&](auto& f) { return runOnFunc(f); }) > 0; + return instrumented_function || instrumented_global; +} + +bool TypeArtPass::runOnFunc(Function& f) { + using namespace typeart; + + if (f.isDeclaration() || f.getName().startswith("__typeart")) { + return false; + } + + if (!getAnalysis().hasFunctionData(&f)) { + LOG_WARNING("No allocation data could be retrieved for function: " << f.getName()); + return false; + } + + LOG_DEBUG("Running on function: " << f.getName()) + + // FIXME this is required when "PassManagerBuilder::EP_OptimizerLast" is used as the function (constant) pointer are + // nullpointer/invalidated + declareInstrumentationFunctions(*f.getParent()); + + bool mod{false}; + // auto& c = f.getContext(); + DataLayout dl(f.getParent()); + + llvm::SmallDenseMap allocCounts; + + const auto& fData = getAnalysis().getFunctionData(&f); + const auto& mallocs = fData.mallocs; + const auto& allocas = fData.allocas; + const auto& frees = fData.frees; + + if (!ClIgnoreHeap) { + // instrument collected calls of bb: + const auto heap_count = instrumentation_context->handleHeap(mallocs); + const auto free_count = instrumentation_context->handleFree(frees); + + NumInstrumentedMallocs += heap_count; + NumInstrumentedFrees += free_count; + + mod |= heap_count > 0 || free_count > 0; + } + if (ClTypeArtAlloca) { + const auto stack_count = instrumentation_context->handleStack(allocas); + NumInstrumentedAlloca += stack_count; + mod |= stack_count > 0; + } + + return mod; +} // namespace pass + +bool TypeArtPass::doFinalization(Module&) { + /* + * Persist the accumulated type definition information for this module. + */ + LOG_DEBUG("Writing type file to " << ClTypeFile.getValue()); + + if (typeManager.store()) { + LOG_DEBUG("Success!"); + } else { + LOG_FATAL("Failed writing type config to " << ClTypeFile.getValue()); + } + if (ClTypeArtStats && AreStatisticsEnabled()) { + auto& out = llvm::errs(); + printStats(out); + } + return false; +} + +void TypeArtPass::declareInstrumentationFunctions(Module& m) { + // Remove this return if problems come up during compilation + if (typeart_alloc_global.f != nullptr && typeart_alloc_stack.f != nullptr && typeart_alloc.f != nullptr && + typeart_free.f != nullptr && typeart_leave_scope.f != nullptr) { + return; + } + + TAFunctionDeclarator decl(m, instrumentation_helper, functions); + + auto alloc_arg_types = instrumentation_helper.make_parameters(IType::ptr, IType::type_id, IType::extent); + auto free_arg_types = instrumentation_helper.make_parameters(IType::ptr); + auto leavescope_arg_types = instrumentation_helper.make_parameters(IType::stack_count); + + typeart_alloc.f = decl.make_function(IFunc::heap, typeart_alloc.name, alloc_arg_types); + typeart_alloc_stack.f = decl.make_function(IFunc::stack, typeart_alloc_stack.name, alloc_arg_types); + typeart_alloc_global.f = decl.make_function(IFunc::global, typeart_alloc_global.name, alloc_arg_types); + typeart_free.f = decl.make_function(IFunc::free, typeart_free.name, free_arg_types); + typeart_leave_scope.f = decl.make_function(IFunc::scope, typeart_leave_scope.name, leavescope_arg_types); +} + +void TypeArtPass::printStats(llvm::raw_ostream& out) { + const auto get_ta_mode = [&]() { + const bool heap = !ClIgnoreHeap.getValue(); + const bool stack = ClTypeArtAlloca.getValue(); + if (heap) { + if (stack) { + return " [Heap & Stack]"; + } + return " [Heap]"; + } else if (stack) { + return " [Stack]"; + } + return " [Unknown]"; + }; + + Table stats("TypeArtPass"); + stats.wrap_header = true; + stats.title += get_ta_mode(); + stats.put(Row::make("Malloc", NumInstrumentedMallocs.getValue())); + stats.put(Row::make("Free", NumInstrumentedFrees.getValue())); + stats.put(Row::make("Alloca", NumInstrumentedAlloca.getValue())); + stats.put(Row::make("Global", NumInstrumentedGlobal.getValue())); + + stats.print(out); +} + +} // namespace typeart::pass + +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" + +static void registerClangPass(const llvm::PassManagerBuilder&, llvm::legacy::PassManagerBase& PM) { + PM.add(new typeart::pass::TypeArtPass()); +} +static RegisterStandardPasses RegisterClangPass(PassManagerBuilder::EP_OptimizerLast, registerClangPass); diff --git a/lib/TypeARTPass.h b/lib/passes/TypeARTPass.h similarity index 73% rename from lib/TypeARTPass.h rename to lib/passes/TypeARTPass.h index 47709724..9096bede 100644 --- a/lib/TypeARTPass.h +++ b/lib/passes/TypeARTPass.h @@ -1,9 +1,11 @@ #ifndef _LIB_MUSTSUPPORTPASS_H #define _LIB_MUSTSUPPORTPASS_H -#include "TypeDB.h" #include "TypeManager.h" -#include "support/InstrumentationHelper.h" +#include "instrumentation/Instrumentation.h" +#include "instrumentation/InstrumentationHelper.h" +#include "instrumentation/TypeARTFunctions.h" +#include "typelib/TypeDB.h" #include "llvm/Pass.h" @@ -16,13 +18,12 @@ class Function; class AnalysisUsage; } // namespace llvm -namespace typeart { -namespace pass { +namespace typeart::pass { class TypeArtPass : public llvm::ModulePass { private: struct TypeArtFunc { - const std::string name{""}; + const std::string name; llvm::Value* f{nullptr}; }; @@ -33,7 +34,9 @@ class TypeArtPass : public llvm::ModulePass { TypeArtFunc typeart_leave_scope{"__typeart_leave_scope"}; TypeManager typeManager; - InstrumentationHelper instr; + InstrumentationHelper instrumentation_helper; + TAFunctions functions; + std::unique_ptr instrumentation_context; public: static char ID; // used to identify pass @@ -50,7 +53,6 @@ class TypeArtPass : public llvm::ModulePass { void printStats(llvm::raw_ostream&); }; -} // namespace pass -} // namespace typeart +} // namespace typeart::pass #endif diff --git a/lib/TypeManager.cpp b/lib/passes/TypeManager.cpp similarity index 94% rename from lib/TypeManager.cpp rename to lib/passes/TypeManager.cpp index 3fcf5ac5..43dff118 100644 --- a/lib/TypeManager.cpp +++ b/lib/passes/TypeManager.cpp @@ -4,19 +4,20 @@ #include "TypeManager.h" -#include "TypeIO.h" -#include "TypeInterface.h" #include "support/Logger.h" #include "support/TypeUtil.h" #include "support/Util.h" +#include "typelib/TypeIO.h" +#include "typelib/TypeInterface.h" #include +#include namespace typeart { using namespace llvm; -TypeManager::TypeManager(std::string file) : file(file), structCount(0) { +TypeManager::TypeManager(std::string file) : file(std::move(file)), structCount(0) { } bool TypeManager::load() { @@ -99,8 +100,7 @@ int TypeManager::getOrRegisterVector(llvm::VectorType* type, const llvm::DataLay // arrays. They are given special names and are marked with a TA_VEC flag to avoid confusion. // Look up name - auto it = structMap.find(name); - if (it != structMap.end()) { + if (auto it = structMap.find(name); it != structMap.end()) { if (!typeDB.isVectorType(it->second)) { LOG_ERROR("Expected vector type: " << name); return TA_UNKNOWN_TYPE; @@ -142,9 +142,9 @@ int TypeManager::getOrRegisterStruct(llvm::StructType* type, const llvm::DataLay return type->getStructName(); }; - auto name = getName(type); - auto it = structMap.find(name); - if (it != structMap.end()) { + const auto name = getName(type); + + if (auto it = structMap.find(name); it != structMap.end()) { if (!typeDB.isUserDefinedType(it->second)) { LOG_ERROR("Expected user defined struct type: " << name); return TA_UNKNOWN_TYPE; diff --git a/lib/TypeManager.h b/lib/passes/TypeManager.h similarity index 87% rename from lib/TypeManager.h rename to lib/passes/TypeManager.h index b4a9cc58..cbd02d68 100644 --- a/lib/TypeManager.h +++ b/lib/passes/TypeManager.h @@ -1,36 +1,32 @@ #ifndef LLVM_MUST_SUPPORT_TYPEMANAGER_H #define LLVM_MUST_SUPPORT_TYPEMANAGER_H -#include "TypeDB.h" +#include "typelib/TypeDB.h" +#include #include #include -#include namespace typeart { class TypeManager { + std::string file; + TypeDB typeDB; + llvm::StringMap structMap; + size_t structCount; + public: explicit TypeManager(std::string file); - bool load(); - bool store(); - int getOrRegisterType(llvm::Type* type, const llvm::DataLayout& dl); private: int getOrRegisterStruct(llvm::StructType* type, const llvm::DataLayout& dl); - int getOrRegisterVector(llvm::VectorType* type, const llvm::DataLayout& dl); - int reserveNextId(); - - std::string file; - TypeDB typeDB; - std::map structMap; - size_t structCount; }; + } // namespace typeart #endif // LLVM_MUST_SUPPORT_TYPEMANAGER_H diff --git a/lib/analysis/CMakeLists.txt b/lib/passes/analysis/CMakeLists.txt similarity index 74% rename from lib/analysis/CMakeLists.txt rename to lib/passes/analysis/CMakeLists.txt index 8d5c04dc..b0f66b57 100644 --- a/lib/analysis/CMakeLists.txt +++ b/lib/passes/analysis/CMakeLists.txt @@ -6,16 +6,24 @@ set(MEM_PASS_SOURCES make_llvm_module(meminstfinderpass "${MEM_PASS_SOURCES}" + LINK_LIBS + memfilter INCLUDE_DIRS + ${PROJECT_SOURCE_DIR}/lib ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_CURRENT_SOURCE_DIR}/filter ) +mark_as_advanced(LLVM_MEMINSTFINDERPASS_LINK_INTO_TOOLS) + target_project_compile_options(meminstfinderpass) target_project_compile_definitions(meminstfinderpass PRIVATE_DEFS LOG_LEVEL=${LOG_LEVEL} ) +target_project_coverage_options(meminstfinderpass) + if(SHOW_STATS) target_project_compile_definitions(meminstfinderpass PRIVATE_DEFS diff --git a/lib/passes/analysis/MemInstFinderPass.cpp b/lib/passes/analysis/MemInstFinderPass.cpp new file mode 100644 index 00000000..9f3ca770 --- /dev/null +++ b/lib/passes/analysis/MemInstFinderPass.cpp @@ -0,0 +1,407 @@ +/* + * MemInstFinderPass.cpp + * + * Created on: Jun 3, 2018 + * Author: ahueck + */ + +#include "MemInstFinderPass.h" + +#include "MemOpVisitor.h" +#include "filter/CGForwardFilter.h" +#include "filter/CGInterface.h" +#include "filter/Filter.h" +#include "filter/StandardFilter.h" +#include "filter/StdForwardFilter.h" +#include "support/Logger.h" +#include "support/Table.h" +#include "support/TypeUtil.h" +#include "support/Util.h" + +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/Instructions.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Format.h" + +using namespace llvm; + +#define DEBUG_TYPE "meminstanalysis" + +namespace { +static RegisterPass X("mem-inst-finder", // pass option + "Find heap and stack allocations in a program.", // pass description + true, // does not modify the CFG + true // and it's an analysis +); +} // namespace + +static cl::opt ClFilterNonArrayAlloca("alloca-array-only", cl::desc("Only use alloca instructions of arrays."), + cl::Hidden, cl::init(false)); + +static cl::opt ClFilterMallocAllocPair("malloc-store-filter", + cl::desc("Filter allocs that get a store from a heap alloc."), cl::Hidden, + cl::init(false)); + +static cl::opt ClFilterGlobal("filter-globals", cl::desc("Filter globals of a module."), cl::Hidden, + cl::init(true)); + +static cl::opt ClUseCallFilter("call-filter", + cl::desc("Filter alloca instructions that are passed to specific calls."), + cl::Hidden, cl::init(false)); + +static cl::opt ClCallFilterImpl("call-filter-impl", cl::desc("Select the filter implementation."), + cl::Hidden, cl::init("default")); + +static cl::opt ClCallFilterGlob("call-filter-str", cl::desc("Filter values based on string."), cl::Hidden, + cl::init("*MPI_*")); + +static cl::opt ClCallFilterDeepGlob("call-filter-deep-str", + cl::desc("Filter values based on API, i.e., passed as void*."), + cl::Hidden, cl::init("MPI_*")); + +static cl::opt ClCallFilterCGFile("call-filter-cg-file", cl::desc("Location of CG to use."), cl::Hidden, + cl::init("")); + +// Deprecated, only used with the old std filter: +static cl::opt ClCallFilterDeep("call-filter-deep", + cl::desc("If the CallFilter matches, we look if the value is passed as a void*."), + cl::Hidden, cl::init(false)); + +STATISTIC(NumDetectedHeap, "Number of detected heap allocs"); +STATISTIC(NumFilteredDetectedHeap, "Number of filtered heap allocs"); +STATISTIC(NumDetectedAllocs, "Number of detected allocs"); +STATISTIC(NumCallFilteredAllocs, "Number of call filtered allocs"); +STATISTIC(NumFilteredMallocAllocs, "Number of filtered malloc-related allocs"); +STATISTIC(NumFilteredNonArrayAllocs, "Number of call filtered allocs"); +STATISTIC(NumDetectedGlobals, "Number of detected globals"); +STATISTIC(NumFilteredGlobals, "Number of filtered globals"); +STATISTIC(NumCallFilteredGlobals, "Number of filtered globals"); + +namespace typeart { + +using namespace finder; + +namespace filter { + +static std::unique_ptr make_filter(std::string id, std::string glob) { + const bool deep = ClCallFilterDeep.getValue(); + if (id == "empty" || !ClUseCallFilter.getValue()) { + LOG_DEBUG("Return no-op filter") + return std::make_unique(); + } else if (id == "deprecated::default") { + // default + LOG_DEBUG("Return deprecated default filter") + return std::make_unique(glob, deep); + } else if (id == "cg" || id == "experimental::cg") { + if (ClCallFilterCGFile.empty()) { + LOG_FATAL("CG File not set!"); + std::exit(1); + } + LOG_DEBUG("Return CG filter with CG file @ " << ClCallFilterCGFile.getValue()) + auto json_cg = JSONCG::getJSON(ClCallFilterCGFile.getValue()); + auto matcher = std::make_unique(util::glob2regex(glob)); + return std::make_unique(glob, std::move(json_cg), std::move(matcher)); + } else { + LOG_DEBUG("Return default filter") + auto matcher = std::make_unique(util::glob2regex(glob)); + const auto deep_glob = ClCallFilterDeepGlob.getValue(); + auto deep_matcher = std::make_unique(util::glob2regex(deep_glob)); + return std::make_unique(std::move(matcher), std::move(deep_matcher)); + } +} + +CallFilter::CallFilter(const std::string& glob) : fImpl{make_filter(ClCallFilterImpl.getValue(), glob)} { +} + +bool CallFilter::operator()(AllocaInst* in) { + LOG_DEBUG("Analyzing value: " << util::dump(*in)); + fImpl->setMode(/*search mallocs = */ false); + fImpl->setStartingFunction(in->getParent()->getParent()); + const auto filter_ = fImpl->filter(in); + if (filter_) { + LOG_DEBUG("Filtering value: " << util::dump(*in) << "\n"); + } else { + LOG_DEBUG("Keeping value: " << util::dump(*in) << "\n"); + } + return filter_; +} + +bool CallFilter::operator()(GlobalValue* g) { + LOG_DEBUG("Analyzing value: " << util::dump(*g)); + fImpl->setMode(/*search mallocs = */ false); + fImpl->setStartingFunction(nullptr); + const auto filter_ = fImpl->filter(g); + if (filter_) { + LOG_DEBUG("Filtering value: " << util::dump(*g) << "\n"); + } else { + LOG_DEBUG("Keeping value: " << util::dump(*g) << "\n"); + } + return filter_; +} + +CallFilter& CallFilter::operator=(CallFilter&&) noexcept = default; + +CallFilter::~CallFilter() = default; + +} // namespace filter + +char MemInstFinderPass::ID = 0; + +MemInstFinderPass::MemInstFinderPass() : llvm::ModulePass(ID), mOpsCollector(), filter(ClCallFilterGlob.getValue()) { +} + +void MemInstFinderPass::getAnalysisUsage(llvm::AnalysisUsage& info) const { + info.setPreservesAll(); +} + +bool MemInstFinderPass::runOnModule(Module& m) { + mOpsCollector.visitModuleGlobals(m); + auto& globals = mOpsCollector.globals; + NumDetectedGlobals += globals.size(); + if (ClFilterGlobal) { + globals.erase( + llvm::remove_if( + globals, + [&](const auto gdata) { + auto global = gdata.global; + const auto name = global->getName(); + if (name.startswith("llvm.") || name.startswith("__llvm_gcov") || name.startswith("__llvm_gcda")) { + // 2nd and 3rd check: Check if the global is private gcov data. + return true; + } + + if (global->hasInitializer()) { + auto* ini = global->getInitializer(); + StringRef ini_name = util::dump(*ini); + + if (ini_name.contains("std::ios_base::Init")) { + return true; + } + } + // if (!g->hasInitializer()) { + // return true; + // } + + if (global->hasSection()) { + StringRef Section = global->getSection(); + + // Globals from llvm.metadata aren't emitted, do not instrument them. + if (Section == "llvm.metadata") { + return true; + } + // Do not instrument globals from special LLVM sections. + if (Section.find("__llvm") != StringRef::npos || Section.find("__LLVM") != StringRef::npos) { + return true; + } + // Check if the global is in the PGO counters section. + // auto OF = Triple(m.getTargetTriple()).getObjectFormat(); + // if (Section.endswith(getInstrProfSectionName(IPSK_cnts, OF, + // /*AddSegmentInfo=*/false))) { + // return true; + // } + } + + if (global->getLinkage() == GlobalValue::ExternalLinkage || + global->getLinkage() == GlobalValue::PrivateLinkage) { + return true; + } + + Type* t = global->getValueType(); + if (!t->isSized()) { + return true; + } + + if (t->isArrayTy()) { + t = t->getArrayElementType(); + } + if (auto structType = dyn_cast(t)) { + if (structType->isOpaque()) { + LOG_DEBUG("Encountered opaque struct " << t->getStructName() << " - skipping..."); + return true; + } + } + return false; + }), + globals.end()); + + const auto beforeCallFilter = globals.size(); + NumFilteredGlobals = NumDetectedGlobals - beforeCallFilter; + + globals.erase(llvm::remove_if(globals, [&](const auto g) { return filter(g.global); }), globals.end()); + + NumCallFilteredGlobals = beforeCallFilter - globals.size(); + NumFilteredGlobals += NumCallFilteredGlobals; + } + + return llvm::count_if(m.functions(), [&](auto& f) { return runOnFunc(f); }) > 0; +} // namespace typeart + +bool MemInstFinderPass::runOnFunc(llvm::Function& f) { + if (f.isDeclaration() || f.getName().startswith("__typeart")) { + return false; + } + + LOG_DEBUG("Running on function: " << f.getName()) + + mOpsCollector.visit(f); + + const auto checkAmbigiousMalloc = [&f](const MallocData& mallocData) { + using namespace typeart::util::type; + auto primaryBitcast = mallocData.primary; + if (primaryBitcast != nullptr) { + const auto& bitcasts = mallocData.bitcasts; + std::for_each(bitcasts.begin(), bitcasts.end(), [&](auto bitcastInst) { + auto dest = bitcastInst->getDestTy(); + if (bitcastInst != primaryBitcast && + (!isVoidPtr(dest) && !isi64Ptr(dest) && + primaryBitcast->getDestTy() != dest)) { // void* and i64* are used by LLVM + // Second non-void* bitcast detected - semantics unclear + LOG_WARNING("Encountered ambiguous pointer type in function: " << util::try_demangle(f)); + LOG_WARNING(" Allocation" << util::dump(*(mallocData.call))); + LOG_WARNING(" Primary cast: " << util::dump(*primaryBitcast)); + LOG_WARNING(" Secondary cast: " << util::dump(*bitcastInst)); + } + }); + } + }; + + NumDetectedAllocs += mOpsCollector.allocas.size(); + + if (ClFilterNonArrayAlloca) { + auto& allocs = mOpsCollector.allocas; + allocs.erase(llvm::remove_if(allocs, + [&](const auto& data) { + if (!data.alloca->getAllocatedType()->isArrayTy() && data.array_size == 1) { + ++NumFilteredNonArrayAllocs; + return true; + } + return false; + }), + allocs.end()); + } + + if (ClFilterMallocAllocPair) { + auto& allocs = mOpsCollector.allocas; + auto& mallocs = mOpsCollector.mallocs; + + const auto filterMallocAllocPairing = [&mallocs](const auto alloc) { + // Only look for the direct users of the alloc: + // TODO is a deeper analysis required? + for (auto inst : alloc->users()) { + if (StoreInst* store = dyn_cast(inst)) { + const auto source = store->getValueOperand(); + if (isa(source)) { + for (auto& mdata : mallocs) { + // is it a bitcast we already collected? if yes, we can filter the alloc + return std::any_of(mdata.bitcasts.begin(), mdata.bitcasts.end(), + [&source](const auto bcast) { return bcast == source; }); + } + } else if (isa(source)) { + return std::any_of(mallocs.begin(), mallocs.end(), + [&source](const auto& mdata) { return mdata.call == source; }); + } + } + } + return false; + }; + + allocs.erase(llvm::remove_if(allocs, + [&](const auto& data) { + if (filterMallocAllocPairing(data.alloca)) { + ++NumFilteredMallocAllocs; + return true; + } + return false; + }), + allocs.end()); + } + + if (ClUseCallFilter) { + auto& allocs = mOpsCollector.allocas; + allocs.erase(llvm::remove_if(allocs, + [&](const auto& data) { + if (filter(data.alloca)) { + ++NumCallFilteredAllocs; + return true; + } + return false; + }), + allocs.end()); + // LOG_DEBUG(allocs.size() << " allocas to instrument : " << util::dump(allocs)); + } + + auto& mallocs = mOpsCollector.mallocs; + NumDetectedHeap += mallocs.size(); + + for (const auto& mallocData : mallocs) { + checkAmbigiousMalloc(mallocData); + } + + FunctionData d{mOpsCollector.mallocs, mOpsCollector.frees, mOpsCollector.allocas}; + functionMap[&f] = d; + + mOpsCollector.clear(); + + return false; +} // namespace typeart + +bool MemInstFinderPass::doFinalization(llvm::Module&) { + if (AreStatisticsEnabled()) { + auto& out = llvm::errs(); + printStats(out); + } + return false; +} + +void MemInstFinderPass::printStats(llvm::raw_ostream& out) { + auto all_stack = double(NumDetectedAllocs.getValue()); + auto nonarray_stack = double(NumFilteredNonArrayAllocs.getValue()); + auto malloc_alloc_stack = double(NumFilteredMallocAllocs.getValue()); + auto call_filter_stack = double(NumCallFilteredAllocs.getValue()); + + const auto call_filter_stack_p = + (call_filter_stack / std::max(1.0, all_stack - nonarray_stack - malloc_alloc_stack)) * 100.0; + + const auto call_filter_heap_p = + (double(NumFilteredDetectedHeap.getValue()) / std::max(1.0, double(NumDetectedHeap.getValue()))) * 100.0; + + const auto call_filter_global_p = + (double(NumCallFilteredGlobals.getValue()) / std::max(1.0, double(NumDetectedGlobals.getValue()))) * 100.0; + + const auto call_filter_global_nocallfilter_p = + (double(NumFilteredGlobals.getValue()) / std::max(1.0, double(NumDetectedGlobals.getValue()))) * 100.0; + + Table stats("MemInstFinderPass"); + stats.wrap_header = true; + stats.wrap_length = true; + stats.put(Row::make("Filter string", ClCallFilterGlob.getValue())); + stats.put(Row::make_row("> Heap Memory")); + stats.put(Row::make("Heap alloc", NumDetectedHeap.getValue())); + stats.put(Row::make("Heap call filtered %", call_filter_heap_p)); + stats.put(Row::make_row("> Stack Memory")); + stats.put(Row::make("Alloca", all_stack)); + stats.put(Row::make("Stack call filtered %", call_filter_stack_p)); + stats.put(Row::make_row("> Global Memory")); + stats.put(Row::make("Global", NumDetectedGlobals.getValue())); + stats.put(Row::make("Global filter total", NumFilteredGlobals.getValue())); + stats.put(Row::make("Global call filtered %", call_filter_global_p)); + stats.put(Row::make("Global filtered %", call_filter_global_nocallfilter_p)); + + stats.print(out); +} + +bool MemInstFinderPass::hasFunctionData(Function* f) const { + auto iter = functionMap.find(f); + return iter != functionMap.end(); +} + +const FunctionData& MemInstFinderPass::getFunctionData(Function* f) const { + auto iter = functionMap.find(f); + return iter->second; +} + +const GlobalDataList& MemInstFinderPass::getModuleGlobals() const { + return mOpsCollector.globals; +} + +} // namespace typeart diff --git a/lib/analysis/MemInstFinderPass.h b/lib/passes/analysis/MemInstFinderPass.h similarity index 84% rename from lib/analysis/MemInstFinderPass.h rename to lib/passes/analysis/MemInstFinderPass.h index cfa0e4c5..14b998ab 100644 --- a/lib/analysis/MemInstFinderPass.h +++ b/lib/passes/analysis/MemInstFinderPass.h @@ -8,6 +8,7 @@ #ifndef LIB_ANALYSIS_MEMINSTFINDERPASS_H_ #define LIB_ANALYSIS_MEMINSTFINDERPASS_H_ +#include "MemOpData.h" #include "MemOpVisitor.h" #include "llvm/Pass.h" @@ -25,10 +26,10 @@ class GlobalValue; namespace typeart { namespace filter { +class Filter; class CallFilter { - class FilterImpl; - std::unique_ptr fImpl; + std::unique_ptr fImpl; public: explicit CallFilter(const std::string& glob); @@ -44,9 +45,9 @@ class CallFilter { } // namespace filter struct FunctionData { - llvm::SmallVector listMalloc; - llvm::SmallPtrSet listFree; - llvm::SmallVector listAlloca; + MallocDataList mallocs; + FreeDataList frees; + AllocaDataList allocas; }; class MemInstFinderPass : public llvm::ModulePass { @@ -65,7 +66,7 @@ class MemInstFinderPass : public llvm::ModulePass { bool doFinalization(llvm::Module&) override; bool hasFunctionData(llvm::Function*) const; const FunctionData& getFunctionData(llvm::Function*) const; - const llvm::SmallVector& getModuleGlobals() const; + const GlobalDataList& getModuleGlobals() const; private: void printStats(llvm::raw_ostream&); diff --git a/lib/passes/analysis/MemOpData.h b/lib/passes/analysis/MemOpData.h new file mode 100644 index 00000000..7ae430af --- /dev/null +++ b/lib/passes/analysis/MemOpData.h @@ -0,0 +1,62 @@ +// +// Created by ahueck on 08.10.20. +// + +#ifndef TYPEART_MEMOPDATA_H +#define TYPEART_MEMOPDATA_H + +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" + +namespace llvm { +class CallBase; +class BitCastInst; +class AllocaInst; +class GlobalVariable; +} // namespace llvm + +namespace typeart { +enum class MemOpKind : uint8_t { + NewLike = 1 << 0, // allocates, never null + MallocLike = 1 << 1 | NewLike, // allocates, maybe null + AlignedAllocLike = 1 << 2, // allocates aligned, maybe null + CallocLike = 1 << 3, // allocates zeroed + ReallocLike = 1 << 4, // re-allocated (existing) memory + FreeLike = 1 << 5, // free memory + DeleteLike = 1 << 6, // delete (cpp) memory + MallocOrCallocLike = MallocLike | CallocLike | AlignedAllocLike, + AllocLike = MallocOrCallocLike, + AnyAlloc = AllocLike | ReallocLike, + AnyFree = FreeLike | DeleteLike +}; +struct MallocData { + llvm::CallBase* call{nullptr}; + llvm::BitCastInst* primary{nullptr}; // Non-null if non (void*) cast exists + llvm::SmallPtrSet bitcasts; + MemOpKind kind; + bool is_invoke{false}; +}; + +struct FreeData { + llvm::CallBase* call{nullptr}; + MemOpKind kind; + bool is_invoke{false}; +}; + +struct AllocaData { + llvm::AllocaInst* alloca{nullptr}; + size_t array_size; + bool is_vla{false}; +}; + +struct GlobalData { + llvm::GlobalVariable* global{nullptr}; +}; + +using GlobalDataList = llvm::SmallVector; +using MallocDataList = llvm::SmallVector; +using FreeDataList = llvm::SmallVector; +using AllocaDataList = llvm::SmallVector; + +} // namespace typeart +#endif // TYPEART_MEMOPDATA_H diff --git a/lib/passes/analysis/MemOpVisitor.cpp b/lib/passes/analysis/MemOpVisitor.cpp new file mode 100644 index 00000000..1fcb474f --- /dev/null +++ b/lib/passes/analysis/MemOpVisitor.cpp @@ -0,0 +1,164 @@ +/* + * MemOpVisitor.cpp + * + * Created on: Jan 3, 2018 + * Author: ahueck + */ + +#include "MemOpVisitor.h" + +#include "support/Logger.h" +#include "support/TypeUtil.h" +#include "support/Util.h" + +#include + +namespace typeart::finder { + +using namespace llvm; + +void MemOpVisitor::visitModuleGlobals(Module& m) { + for (auto& g : m.globals()) { + globals.emplace_back(GlobalData{&g}); + } +} + +void MemOpVisitor::visitCallBase(llvm::CallBase& cb) { + const auto isInSet = [&](const auto& fMap) -> llvm::Optional { + const auto* f = cb.getCalledFunction(); + if (!f) { + // TODO handle calls through, e.g., function pointers? - seems infeasible + // LOG_INFO("Encountered indirect call, skipping."); + return None; + } + const auto name = f->getName().str(); + const auto res = fMap.find(name); + if (res != fMap.end()) { + return {(*res).second}; + } + return None; + }; + + if (auto val = isInSet(alloc_map)) { + visitMallocLike(cb, val.getValue()); + } else if (auto val = isInSet(dealloc_map)) { + visitFreeLike(cb, val.getValue()); + } +} + +void MemOpVisitor::visitMallocLike(llvm::CallBase& ci, MemOpKind k) { + // LOG_DEBUG("Found malloc-like: " << ci.getCalledFunction()->getName()); + + SmallPtrSet bcasts; + + for (auto user : ci.users()) { + // Simple case: Pointer is immediately casted + if (auto inst = dyn_cast(user)) { + bcasts.insert(inst); + } + // Pointer is first stored, then loaded and subsequently casted + if (auto storeInst = dyn_cast(user)) { + auto storeAddr = storeInst->getPointerOperand(); + for (auto storeUser : storeAddr->users()) { // TODO: Ensure that load occurs ofter store? + if (auto loadInst = dyn_cast(storeUser)) { + for (auto loadUser : loadInst->users()) { + if (auto bcastInst = dyn_cast(loadUser)) { + LOG_MSG(*bcastInst) + bcasts.insert(bcastInst); + } + } + } + } + } + // FIXME this is a try to fix issue #13 ; may require more sophisticated dataflow tracking + if (auto gep = dyn_cast(user)) { + // if (gep->getPointerOperand() == ci) { + for (auto gep_user : gep->users()) { + if (auto bcastInst = dyn_cast(gep_user)) { + bcasts.insert(bcastInst); + } + } + //} + } + } + + BitCastInst* primary_cast{nullptr}; + if (!bcasts.empty()) { + primary_cast = *bcasts.begin(); + } + + using namespace util::type; + // const auto is_i64 = [](auto* type) { return type->isPointerTy() && type->getPointerElementType()->isIntegerTy(64); + // }; + + const bool has_specific = llvm::count_if(bcasts, [&](auto bcast) { + auto dest = bcast->getDestTy(); + return !isVoidPtr(dest) && !isi64Ptr(dest); + }) > 0; + + for (auto bcast : bcasts) { + auto dest = bcast->getDestTy(); + if (!isVoidPtr(dest)) { + auto cast = bcast; + if (isi64Ptr(dest) && has_specific) { // LLVM likes to treat mallocs with i64 ptr type, skip if we have sth else + continue; + } + primary_cast = cast; + } + } + + if (primary_cast == nullptr) { + LOG_DEBUG("Primay bitcast null: " << ci) + } + /* + std::for_each(bcasts.begin(), bcasts.end(), [&](auto bcast) { + using namespace util::type; + auto dest = bcast->getDestTy(); + if (!isVoidPtr(dest)) { + primary_cast = bcast; + } + }); + */ + // LOG_DEBUG(" >> number of bitcasts found: " << bcasts.size()); + + mallocs.push_back(MallocData{&ci, primary_cast, bcasts, k, isa(ci)}); +} + +void MemOpVisitor::visitFreeLike(llvm::CallBase& ci, MemOpKind k) { + // LOG_DEBUG(ci.getCalledFunction()->getName()); + MemOpKind kind = k; + if (auto f = ci.getCalledFunction()) { + if (auto elem = dealloc_map.find(f->getName()); elem != std::end(dealloc_map)) { + kind = elem->getValue(); + } + } + + frees.emplace_back(FreeData{&ci, kind, isa(ci)}); +} + +// void MemOpVisitor::visitIntrinsicInst(llvm::IntrinsicInst& ii) { +// +//} + +void MemOpVisitor::visitAllocaInst(llvm::AllocaInst& ai) { + // LOG_DEBUG("Found alloca " << ai); + Value* arraySizeOperand = ai.getArraySize(); + size_t arraySize{0}; + bool is_vla{false}; + if (auto arraySizeConst = llvm::dyn_cast(arraySizeOperand)) { + arraySize = arraySizeConst->getZExtValue(); + } else { + is_vla = true; + } + + allocas.push_back({&ai, arraySize, is_vla}); + // LOG_DEBUG("Alloca: " << util::dump(ai) << " -> lifetime marker: " << util::dump(lifetimes)); +} // namespace typeart + +void MemOpVisitor::clear() { + allocas.clear(); + mallocs.clear(); + frees.clear(); +} + +} // namespace typeart::finder diff --git a/lib/passes/analysis/MemOpVisitor.h b/lib/passes/analysis/MemOpVisitor.h new file mode 100644 index 00000000..de91b490 --- /dev/null +++ b/lib/passes/analysis/MemOpVisitor.h @@ -0,0 +1,79 @@ +/* + * MemOpVisitor.h + * + * Created on: Jan 3, 2018 + * Author: ahueck + */ + +#ifndef LIB_MEMOPVISITOR_H_ +#define LIB_MEMOPVISITOR_H_ + +#include "MemOpData.h" + +#include "llvm/ADT/StringMap.h" +#include "llvm/IR/InstVisitor.h" + +namespace typeart::finder { + +struct MemOpVisitor : public llvm::InstVisitor { + GlobalDataList globals; + MallocDataList mallocs; + FreeDataList frees; + AllocaDataList allocas; + + private: + //clang-format off + const llvm::StringMap alloc_map{ + {"malloc", MemOpKind::MallocLike}, + {"calloc", MemOpKind::CallocLike}, + {"realloc", MemOpKind::ReallocLike}, + {"aligned_alloc", MemOpKind::AlignedAllocLike}, + {"_Znwm", MemOpKind::NewLike}, /*new(unsigned long)*/ + {"_Znwj", MemOpKind::NewLike}, /*new(unsigned int)*/ + {"_Znam", MemOpKind::NewLike}, /*new[](unsigned long)*/ + {"_Znaj", MemOpKind::NewLike}, /*new[](unsigned int)*/ + {"_ZnwjRKSt9nothrow_t", MemOpKind::MallocLike}, /*new(unsigned int, nothrow)*/ + {"_ZnwmRKSt9nothrow_t", MemOpKind::MallocLike}, /*new(unsigned long, nothrow)*/ + {"_ZnajRKSt9nothrow_t", MemOpKind::MallocLike}, /*new[](unsigned int, nothrow)*/ + {"_ZnamRKSt9nothrow_t", MemOpKind::MallocLike}, /*new[](unsigned long, nothrow)*/ + {"_ZnwjSt11align_val_t", MemOpKind::NewLike}, /*new(unsigned int, align_val_t)*/ + {"_ZnwjSt11align_val_tRKSt9nothrow_t", MemOpKind::MallocLike}, /*new(unsigned int, align_val_t, nothrow)*/ + {"_ZnwmSt11align_val_t", MemOpKind::NewLike}, /*new(unsigned long, align_val_t)*/ + {"_ZnwmSt11align_val_tRKSt9nothrow_t", MemOpKind::MallocLike}, /*new(unsigned long, align_val_t, nothrow)*/ + {"_ZnajSt11align_val_t", MemOpKind::NewLike}, /*new[](unsigned int, align_val_t)*/ + {"_ZnajSt11align_val_tRKSt9nothrow_t", MemOpKind::MallocLike}, /*new[](unsigned int, align_val_t, nothrow)*/ + {"_ZnamSt11align_val_t", MemOpKind::NewLike}, /*new[](unsigned long, align_val_t)*/ + {"_ZnamSt11align_val_tRKSt9nothrow_t", MemOpKind::MallocLike}, /*new[](unsigned long, align_val_t, nothrow)*/ + }; + + const llvm::StringMap dealloc_map{ + {"free", MemOpKind::FreeLike}, + {"_ZdlPv", MemOpKind::DeleteLike}, /*delete(void*)*/ + {"_ZdaPv", MemOpKind::DeleteLike}, /*delete[](void*)*/ + {"_ZdlPvj", MemOpKind::DeleteLike}, /*delete(void*, uint)*/ + {"_ZdlPvm", MemOpKind::DeleteLike}, /*delete(void*, ulong)*/ + {"_ZdlPvRKSt9nothrow_t", MemOpKind::DeleteLike}, /*delete(void*, nothrow)*/ + {"_ZdaPvj", MemOpKind::DeleteLike}, /*delete[](void*, uint)*/ + {"_ZdaPvm", MemOpKind::DeleteLike}, /*delete[](void*, ulong)*/ + {"_ZdaPvRKSt9nothrow_t", MemOpKind::DeleteLike}, /*delete[](void*, nothrow)*/ + {"_ZdaPvSt11align_val_tRKSt9nothrow_t", MemOpKind::DeleteLike}, /* delete(void*, align_val_t, nothrow) */ + {"_ZdlPvSt11align_val_tRKSt9nothrow_t", MemOpKind::DeleteLike}, /* delete[](void*, align_val_t, nothrow) */ + {"_ZdlPvjSt11align_val_t", MemOpKind::DeleteLike}, /* delete(void*, unsigned long, align_val_t) */ + {"_ZdlPvmSt11align_val_t", MemOpKind::DeleteLike}, /* delete(void*, unsigned long, align_val_t) */ + {"_ZdaPvjSt11align_val_t", MemOpKind::DeleteLike}, /* delete[](void*, unsigned int, align_val_t) */ + {"_ZdaPvmSt11align_val_t", MemOpKind::DeleteLike}, /* delete[](void*, unsigned long, align_val_t) */ + }; + //clang-format off + + public: + void clear(); + void visitModuleGlobals(llvm::Module& m); + void visitCallBase(llvm::CallBase& cb); + void visitMallocLike(llvm::CallBase& ci, MemOpKind k); + void visitFreeLike(llvm::CallBase& ci, MemOpKind k); + void visitAllocaInst(llvm::AllocaInst& ai); +}; + +} // namespace typeart::finder + +#endif /* LIB_MEMOPVISITOR_H_ */ diff --git a/lib/passes/filter/CGForwardFilter.cpp b/lib/passes/filter/CGForwardFilter.cpp new file mode 100644 index 00000000..8b59f074 --- /dev/null +++ b/lib/passes/filter/CGForwardFilter.cpp @@ -0,0 +1,71 @@ +// +// Created by ahueck on 26.10.20. +// + +#include "CGForwardFilter.h" + +#include "CGInterface.h" +#include "Matcher.h" + +namespace typeart::filter { + +CGFilterImpl::CGFilterImpl(const std::string& filter_str, std::unique_ptr&& cgraph) + : CGFilterImpl(filter_str, std::move(cgraph), nullptr) { +} + +CGFilterImpl::CGFilterImpl(const std::string& filter_str, std::unique_ptr&& cgraph, + std::unique_ptr&& matcher) + : filter(util::glob2regex(filter_str)), call_graph(std::move(cgraph)), deep_matcher(std::move(matcher)) { +} + +FilterAnalysis CGFilterImpl::precheck(Value* /*in*/, Function* start) { + if (start != nullptr) { + FunctionAnalysis analysis; + analysis.analyze(start); + if (analysis.empty()) { + return FilterAnalysis::Filter; + } + } + return FilterAnalysis::Continue; +} + +FilterAnalysis CGFilterImpl::decl(CallSite current, const Path& p) { + if (deep_matcher && deep_matcher->match(current) == Matcher::MatchResult::Match) { + auto result = correlate2void(current, p); + switch (result) { + case ArgCorrelation::GlobalMismatch: + [[fallthrough]]; + case ArgCorrelation::ExactMismatch: + LOG_DEBUG("Correlated, continue search"); + return FilterAnalysis::Continue; + default: + return FilterAnalysis::Keep; + } + } + + const auto searchCG = [&](auto from) { + if (call_graph) { + return call_graph->reachable(from->getName(), filter); + } + return CGInterface::ReachabilityResult::unknown; + }; + + const auto reached = searchCG(current.getCalledFunction()); + + switch (reached) { + case CGInterface::ReachabilityResult::reaches: + return FilterAnalysis::Keep; + case CGInterface::ReachabilityResult::never_reaches: + return FilterAnalysis::Skip; + case CGInterface::ReachabilityResult::maybe_reaches: + return FilterAnalysis::Filter; + default: + return FilterAnalysis::Continue; + } +} + +FilterAnalysis CGFilterImpl::def(CallSite current, const Path& p) { + return decl(current, p); +} + +} // namespace typeart::filter \ No newline at end of file diff --git a/lib/passes/filter/CGForwardFilter.h b/lib/passes/filter/CGForwardFilter.h new file mode 100644 index 00000000..0f804c92 --- /dev/null +++ b/lib/passes/filter/CGForwardFilter.h @@ -0,0 +1,46 @@ +// +// Created by ahueck on 26.10.20. +// + +#ifndef TYPEART_CGFORWARDFILTER_H +#define TYPEART_CGFORWARDFILTER_H + +#include "FilterBase.h" +#include "Matcher.h" + +namespace typeart::filter { + +struct CGFilterTrait { + constexpr static bool Indirect = false; + constexpr static bool Intrinsic = false; + constexpr static bool Declaration = true; + constexpr static bool Definition = true; + constexpr static bool PreCheck = true; +}; + +class CGInterface; + +struct CGFilterImpl { + using Support = CGFilterTrait; + + std::string filter; + std::unique_ptr call_graph; + std::unique_ptr deep_matcher; + + CGFilterImpl(const std::string& filter_str, std::unique_ptr&& cgraph); + + CGFilterImpl(const std::string& filter_str, std::unique_ptr&& cgraph, + std::unique_ptr&& matcher); + + FilterAnalysis precheck(Value* in, Function* start); + + FilterAnalysis decl(CallSite current, const Path& p); + + FilterAnalysis def(CallSite current, const Path& p); +}; + +using CGForwardFilter = BaseFilter; + +} // namespace typeart::filter + +#endif // TYPEART_CGFORWARDFILTER_H \ No newline at end of file diff --git a/lib/passes/filter/CGInterface.cpp b/lib/passes/filter/CGInterface.cpp new file mode 100644 index 00000000..a2562548 --- /dev/null +++ b/lib/passes/filter/CGInterface.cpp @@ -0,0 +1,174 @@ +#include "CGInterface.h" + +#include "support/Logger.h" +#include "support/Util.h" + +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/MemoryBuffer.h" + +namespace typeart::filter { + +CGInterface::ReachabilityResult JSONCG::reachable(const std::string& source, const std::string& target, + bool case_sensitive, bool short_circuit) { + const auto reachables = get_reachable_functions(source); + bool matches = false; + bool allBodies = true; + + // bool what{false}; + if (!hasBodyMap[source]) { + ++no_call_chain; + } + + if (!reachables.empty()) { + ++call_chain; + f.push_back(source); + } + + for (const auto& f : reachables) { + matches |= util::regex_matches(target, f, case_sensitive); + allBodies = allBodies && hasBodyMap[target]; + + if (matches && short_circuit) { + // we have found a match -> whether all functions have bodies is irrelevant + return ReachabilityResult::reaches; + } + } + + if (matches) { + // matches but no short circuit + return ReachabilityResult::reaches; + } + + if (!matches && (!allBodies || !hasBodyMap[source])) { + // We did not find a match, but not all functions had bodies, we don't know + return ReachabilityResult::maybe_reaches; + } + + // No match and all functions had bodies -> never reaches (all call targets found) + return ReachabilityResult::never_reaches; +} + +std::vector JSONCG::get_decl_only() { + std::vector list; + list.reserve(hasBodyMap.size()); + for (const auto& [func, has_body] : hasBodyMap) { + if (!has_body) { + list.push_back(func); + } + } + return list; +} + +std::unordered_set JSONCG::get_reachable_functions(const std::string& caller, + bool considerOverrides) const { + std::unordered_set ret; + std::unordered_set worklist; + + worklist = get_directly_called_function_names(caller, considerOverrides); + while (!worklist.empty()) { + const std::string func_name = *worklist.begin(); + // Check if we did not already handled it + if (ret.find(func_name) == ret.end()) { + worklist.merge(get_directly_called_function_names(func_name)); + ret.insert(func_name); + } + worklist.erase(worklist.find(func_name)); // Iterators get invalidated by merge, so we need to search again + } + return ret; +} + +std::unordered_set JSONCG::get_directly_called_function_names(const std::string& caller, + bool considerOverrides) const { + auto ref = directly_called_functions.find(caller); + if (ref != std::end(directly_called_functions)) { + // If the caller is virtual and overridden, add the overriding functions + if (considerOverrides && (virtualTargets.find(caller) != std::end(virtualTargets))) { + auto targets = ref->second; + auto vTargets = virtualTargets.find(caller)->second; + targets.merge(vTargets); + return targets; + } + return ref->second; + } + return std::unordered_set(); +} + +JSONCG::JSONCG(const llvm::json::Value& cg) { + // Expected json format is the following: + // A top level object/map Key is the function name, value is a object/map with informations + // We only care about "callees" + // callees itself ist an array with function names (as strings) + assert(cg.kind() == llvm::json::Value::Kind::Object && "Top level json must be an Object"); + const llvm::json::Object* tlobj = cg.getAsObject(); + if (tlobj != nullptr) { + for (const auto& entry : *tlobj) { + // std::cout << "Building call site info for " << entry.first.str() << std::endl; + construct_call_information(entry.first.str(), *tlobj); + } + } +} + +void JSONCG::construct_call_information(const std::string& entry_caller, const llvm::json::Object& j) { + if (directly_called_functions.find(entry_caller) == directly_called_functions.end()) { + // We did not handle this function yet + directly_called_functions[entry_caller] = std::unordered_set(); + const auto caller = j.getObject(entry_caller); + if (caller != nullptr) { + const auto hasBody = caller->get("hasBody"); + if (hasBody != nullptr) { + assert(hasBody->kind() == llvm::json::Value::Kind::Boolean && "hasBody must be boolean"); + hasBodyMap[entry_caller] = hasBody->getAsBoolean().getValue(); + } + const auto calles = caller->getArray("callees"); + assert(calles != nullptr && "Json callee information is missing"); + if (calles != nullptr) { + // Now iterate over them + for (const auto& callee : *calles) { + assert(callee.kind() == llvm::json::Value::Kind::String && "Callees must be strings"); + const auto callee_json_string = callee.getAsString(); + assert(callee_json_string.hasValue() && "Could not get callee as string"); + if (callee_json_string.hasValue()) { + const std::string callee_string = callee_json_string.getValue(); + directly_called_functions[entry_caller].insert(callee_string); + } + } + } + // if the function is virtual, overriding functions are _potential_ call targets + const auto overridingFunctions = caller->getArray("overriddenBy"); + if (overridingFunctions != nullptr) { + for (const auto& function : *overridingFunctions) { + assert(function.kind() == llvm::json::Value::Kind::String && "Function names are always strings"); + const auto functionStr = function.getAsString(); + assert(functionStr.hasValue() && "Retrieving overriding function as String failed"); + if (functionStr.hasValue()) { + const std::string functionName = functionStr.getValue(); + virtualTargets[entry_caller].insert(functionName); + } + } + } + } + } +} + +std::unique_ptr JSONCG::getJSON(const std::string& fileName) { + using namespace llvm; + auto memBuffer = MemoryBuffer::getFile(fileName); + + if (memBuffer) { + auto json = llvm::json::parse(memBuffer.get()->getBuffer()); + + if (!json) { + std::string str; + llvm::raw_string_ostream ostr(str); + ostr << json.takeError(); + LOG_FATAL(ostr.str()); + exit(-1); + } + + return std::make_unique(json.get()); + } + LOG_FATAL("No CG file provided / file cannot be found: " << fileName); + return nullptr; +} + +} // namespace typeart::filter \ No newline at end of file diff --git a/lib/passes/filter/CGInterface.h b/lib/passes/filter/CGInterface.h new file mode 100644 index 00000000..a463f013 --- /dev/null +++ b/lib/passes/filter/CGInterface.h @@ -0,0 +1,68 @@ +#ifndef _LIB_TYPEART_CGINTERFACE_H +#define _LIB_TYPEART_CGINTERFACE_H + +#include "llvm/Support/JSON.h" + +#include +#include +#include + +namespace typeart::filter { + +class CGInterface { + public: + enum class ReachabilityResult { reaches, maybe_reaches, never_reaches, unknown }; + + CGInterface() = default; + CGInterface(const CGInterface&) = default; + CGInterface(CGInterface&&) = default; + CGInterface& operator=(const CGInterface&) = default; + CGInterface& operator=(CGInterface&&) = default; + + /** + * \brief Checks if a path exists from source to target + */ + virtual ReachabilityResult reachable(const std::string& source, const std::string& target, + bool case_sensitive = false, bool short_circuit = true) = 0; + + /** + * \brief Returns all reachable functions starting from source + */ + virtual std::unordered_set get_reachable_functions(const std::string& source, + bool considerOverrides) const = 0; + + virtual std::vector get_decl_only() = 0; + + virtual ~CGInterface() = default; +}; + +class JSONCG final : public CGInterface { + std::unordered_map> directly_called_functions; + std::unordered_map hasBodyMap; + // in case a function is virtual, this map holds all potential overrides. + std::unordered_map> virtualTargets; + size_t no_call_chain{0}; + size_t call_chain{0}; + std::vector f; + + public: + explicit JSONCG(const llvm::json::Value& cg); + CGInterface::ReachabilityResult reachable(const std::string& source, const std::string& target, + bool case_sensitive = false, bool short_circuit = true) override; + + std::unordered_set get_reachable_functions(const std::string& source, + bool considerOverrides = true) const override; + + std::unordered_set get_directly_called_function_names(const std::string& entry_caller, + bool considerOverrides = true) const; + + std::vector get_decl_only() override; + + static std::unique_ptr getJSON(const std::string& fileName); + + private: + void construct_call_information(const std::string& entry_caller, const llvm::json::Object& j); +}; + +} // namespace typeart::filter +#endif \ No newline at end of file diff --git a/lib/passes/filter/CMakeLists.txt b/lib/passes/filter/CMakeLists.txt new file mode 100644 index 00000000..ae01694b --- /dev/null +++ b/lib/passes/filter/CMakeLists.txt @@ -0,0 +1,69 @@ +set(FILTER_SOURCES + StandardFilter.cpp + StandardFilter.h + CGInterface.cpp + CGInterface.h + Filter.h + FilterBase.h + CGForwardFilter.h + CGForwardFilter.cpp + StdForwardFilter.h + StdForwardFilter.cpp + FilterUtil.cpp + FilterUtil.h IRSearch.h) + +add_library(memfilter STATIC + ${FILTER_SOURCES} + ) + +# https://lists.llvm.org/pipermail/llvm-dev/2018-July/124534.html +#target_link_libraries(memfilter PUBLIC +# LLVMCore +# LLVMSupport +#) + +target_project_compile_options(memfilter) + +target_define_file_basename(memfilter) + +target_project_compile_definitions(memfilter + PRIVATE_DEFS + LOG_LEVEL=${LOG_LEVEL} + ) + +set_target_properties(memfilter + PROPERTIES + POSITION_INDEPENDENT_CODE ON + ) + + +target_include_directories(memfilter + PUBLIC + ${PROJECT_SOURCE_DIR}/lib + ${PROJECT_SOURCE_DIR}/lib/passes + ) + +target_include_directories(memfilter + SYSTEM + PRIVATE + ${LLVM_INCLUDE_DIRS} + ) + +make_tidy_check(memfilter + "${FILTER_SOURCES}" + ) + +target_project_coverage_options(memfilter) + +# For now, non-consumable filter +#install( +# FILES Filter.h StandardFilter.h CGFilter.h +# DESTINATION include +#) + +#install( +# TARGETS memfilter +# EXPORT ${TARGETS_EXPORT_NAME} +# LIBRARY DESTINATION lib +# ARCHIVE DESTINATION lib +#) \ No newline at end of file diff --git a/lib/passes/filter/Filter.h b/lib/passes/filter/Filter.h new file mode 100644 index 00000000..805785f0 --- /dev/null +++ b/lib/passes/filter/Filter.h @@ -0,0 +1,43 @@ +// +// Created by ahueck on 19.10.20. +// + +#ifndef TYPEART_FILTER_H +#define TYPEART_FILTER_H + +namespace llvm { +class Value; +class Function; +} // namespace llvm + +namespace typeart::filter { + +class Filter { + public: + Filter() = default; + Filter(const Filter&) = default; + Filter(Filter&&) = default; + Filter& operator=(const Filter&) = default; + Filter& operator=(Filter&&) = default; + + virtual bool filter(llvm::Value*) = 0; + virtual void setStartingFunction(llvm::Function*) = 0; + virtual void setMode(bool) = 0; + + virtual ~Filter() = default; +}; + +class NoOpFilter final : public Filter { + public: + bool filter(llvm::Value*) override { + return false; + } + void setMode(bool) override { + } + void setStartingFunction(llvm::Function*) override { + } +}; + +} // namespace typeart::filter + +#endif // TYPEART_FILTER_H diff --git a/lib/passes/filter/FilterBase.h b/lib/passes/filter/FilterBase.h new file mode 100644 index 00000000..59f4b651 --- /dev/null +++ b/lib/passes/filter/FilterBase.h @@ -0,0 +1,246 @@ +// +// Created by ahueck on 21.10.20. +// + +#ifndef TYPEART_FILTERBASE_H +#define TYPEART_FILTERBASE_H + +#include "Filter.h" +#include "FilterUtil.h" +#include "IRPath.h" +#include "IRSearch.h" +#include "support/Logger.h" +#include "support/Util.h" + +#include "llvm/IR/CallSite.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" + +#include +#include + +namespace typeart::filter { + +enum class FilterAnalysis { + Skip = 0, // Do not follow users of current decl/def etc. + Continue, // Continue searching users of decl/def etc. + Keep, // Keep the value (return false) + Filter, // Filter the value (return true) + FollowDef, // Want analysis of the called function def +}; + +template +class BaseFilter : public Filter { + CallSiteHandler handler; + Search search_dir; + bool malloc_mode{false}; + llvm::Function* start_f{nullptr}; + + public: + explicit BaseFilter(const std::string& glob) : handler(glob), search_dir() { + } + + explicit BaseFilter(const CallSiteHandler& handler) : handler(handler), search_dir() { + } + + template + explicit BaseFilter(Args&&... args) : handler(std::forward(args)...) { + } + + bool filter(llvm::Value* in) override { + if (in == nullptr) { + LOG_WARNING("Called with nullptr"); + return false; + } + + FPath fpath(start_f); + const auto filter = DFSFuncFilter(in, fpath); + if (!filter) { + LOG_DEBUG(fpath); + } + return filter; + } + + void setStartingFunction(llvm::Function* f) override { + start_f = f; + }; + + void setMode(bool m) override { + malloc_mode = m; + }; + + private: + bool DFSFuncFilter(llvm::Value* current, FPath& fpath) { + /* do a pre-flow tracking check of value in */ + if constexpr (CallSiteHandler::Support::PreCheck) { + // is null in case of global: + llvm::Function* currentF = fpath.getCurrentFunc(); + if (currentF != nullptr) { + auto status = handler.precheck(current, currentF); + switch (status) { + case FilterAnalysis::Filter: + fpath.pop(); + LOG_DEBUG("Pre-check, filter") + return true; + case FilterAnalysis::Keep: + LOG_DEBUG("Pre-check, keep") + return false; + case FilterAnalysis::Skip: + [[fallthrough]]; + case FilterAnalysis::Continue: + [[fallthrough]]; + default: + break; + } + } + } else { + } + + PathList defPath; // paths that reach a definition in currentF + Path p; + const auto filter = DFSfilter(current, p, defPath); + + if (!filter) { + // for diagnostic output, store the last path + fpath.pushFinal(p); + return false; + } + + for (auto& path2def : defPath) { + auto csite = path2def.getEnd(); + if (!csite) { + continue; + } + + llvm::CallSite c(csite.getValue()); + if (fpath.contains(c)) { + // Avoid recursion: + // TODO a continue may be wrong, if the function itself eventually calls "MPI"? + continue; + } + + fpath.push(path2def); + + auto argv = args(c, path2def); + if (argv.size() > 1) { + LOG_DEBUG("All args are looked at.") + } + + for (auto* arg : argv) { + const auto dfs_filter = DFSFuncFilter(arg, fpath); + if (!dfs_filter) { + return false; + } + } + } + + fpath.pop(); + return true; + } + + bool DFSfilter(llvm::Value* current, Path& path, PathList& plist) { + if (current == nullptr) { + LOG_FATAL("Called with nullptr: " << path); + return false; + } + + path.push(current); + + bool skip{false}; + // In-order analysis + const auto status = callsite(current, path); + switch (status) { + case FilterAnalysis::Keep: + return false; + case FilterAnalysis::Skip: + skip = true; + break; + case FilterAnalysis::FollowDef: + LOG_DEBUG("Analyze definition in path"); + // store path (with the callsite) for a function recursive check later + plist.emplace_back(path); + break; + default: + break; + } + + if (!skip) { + const auto successors = search_dir.search(current, path); + for (auto* successor : successors) { + if (path.contains(successor)) { + // Avoid recursion (e.g., with store inst pointer operands pointing to an allocation) + continue; + } + const auto filter = DFSfilter(successor, path, plist); + if (!filter) { + return false; + } + } + } + + path.pop(); + return true; + } + + FilterAnalysis callsite(llvm::Value* val, const Path& path) { + CallSite site(val); + if (site.isCall() || site.isInvoke()) { + const auto callee = site.getCalledFunction(); + const bool indirect_call = callee == nullptr; + + // Indirect calls (sth. like function pointers) + if (indirect_call) { + if constexpr (CallSiteHandler::Support::Indirect) { + auto status = handler.indirect(site, path); + LOG_DEBUG("Indirect call: " << util::try_demangle(site)) + return status; + } else { + LOG_DEBUG("Indirect call, keep: " << util::try_demangle(site)) + return FilterAnalysis::Keep; + } + } + + const bool is_decl = callee->isDeclaration(); + const bool is_intrinsic = site.getIntrinsicID() != Intrinsic::not_intrinsic; + + // Handle decl + if (is_decl) { + if (is_intrinsic) { + if constexpr (CallSiteHandler::Support::Intrinsic) { + auto status = handler.intrinsic(site, path); + LOG_DEBUG("Intrinsic call: " << util::try_demangle(site)) + return status; + } else { + LOG_DEBUG("Skip intrinsic: " << util::try_demangle(site)) + return FilterAnalysis::Skip; + } + } + + // Handle decl (like MPI calls) + if constexpr (CallSiteHandler::Support::Declaration) { + auto status = handler.decl(site, path); + LOG_DEBUG("Decl call: " << util::try_demangle(site)) + return status; + } else { + LOG_DEBUG("Declaration, keep: " << util::try_demangle(site)) + return FilterAnalysis::Keep; + } + } else { + // Handle definitions + if constexpr (CallSiteHandler::Support::Definition) { + auto status = handler.def(site, path); + LOG_DEBUG("Defined call: " << util::try_demangle(site)) + return status; + } else { + LOG_DEBUG("Definition, keep: " << util::try_demangle(site)) + return FilterAnalysis::Keep; + } + } + } + return FilterAnalysis::Continue; + } +}; + +} // namespace typeart::filter + +#endif // TYPEART_FILTERBASE_H diff --git a/lib/passes/filter/FilterUtil.cpp b/lib/passes/filter/FilterUtil.cpp new file mode 100644 index 00000000..17573bda --- /dev/null +++ b/lib/passes/filter/FilterUtil.cpp @@ -0,0 +1,66 @@ +// +// Created by ahueck on 26.10.20. +// + +#include "FilterUtil.h" + +namespace typeart::filter { + +void FunctionAnalysis::clear() { + calls.indirect.clear(); + calls.def.clear(); + calls.decl.clear(); + calls.intrinsic.clear(); +} + +bool FunctionAnalysis::empty() const { + return calls.def.empty() && calls.decl.empty() && calls.indirect.empty() && calls.intrinsic.empty(); +} + +FunctionAnalysis::FunctionCounts FunctionAnalysis::analyze(Function* f) { + FunctionCounts count{0, 0, 0, 0}; + + for (auto& BB : *f) { + for (auto& I : BB) { + CallSite site(&I); + if (site.isCall() || site.isInvoke()) { + const auto callee = site.getCalledFunction(); + const bool indirect_call = callee == nullptr; + + if (indirect_call) { + ++count.indirect; + calls.indirect.push_back(site); + continue; + } + + const bool is_decl = callee->isDeclaration(); + const bool is_intrinsic = site.getIntrinsicID() != Intrinsic::not_intrinsic; + + if (is_intrinsic) { + ++count.intrinsic; + calls.intrinsic.push_back(site); + continue; + } + + if (is_decl) { + ++count.decl; + calls.decl.push_back(site); + continue; + } + + ++count.def; + calls.def.push_back(site); + continue; + } + } + } + + return count; +} + +raw_ostream& operator<<(raw_ostream& os, const FunctionAnalysis::FunctionCounts& counts) { + os << "[ decl:" << counts.decl << ";def:" << counts.def << ";intr:" << counts.intrinsic + << ";indir:" << counts.indirect << " ]"; + return os; +} +} // namespace typeart::filter \ No newline at end of file diff --git a/lib/passes/filter/FilterUtil.h b/lib/passes/filter/FilterUtil.h new file mode 100644 index 00000000..c59c86e9 --- /dev/null +++ b/lib/passes/filter/FilterUtil.h @@ -0,0 +1,112 @@ +// +// Created by ahueck on 26.10.20. +// + +#ifndef TYPEART_FILTERUTIL_H +#define TYPEART_FILTERUTIL_H + +#include "IRPath.h" +#include "support/Logger.h" + +#include "llvm/IR/CallSite.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +namespace typeart::filter { + +struct FunctionAnalysis { + using FunctionCounts = struct { int decl, def, intrinsic, indirect; }; + using FunctionCalls = struct { llvm::SmallVector decl, def, intrinsic, indirect; }; + + FunctionCalls calls; + + void clear(); + + bool empty() const; + + FunctionCounts analyze(Function* f); +}; + +raw_ostream& operator<<(raw_ostream& os, const FunctionAnalysis::FunctionCounts& counts); + +enum class ArgCorrelation { + NoMatch, + Exact, + ExactMismatch, + Global, + GlobalMismatch, +}; + +inline std::pair findArg(CallSite c, const Path& p) { + auto arg = p.getEndPrev(); + if (!arg) { + return {nullptr, -1}; + } + + Value* in = arg.getValue(); + const auto arg_pos = llvm::find_if(c.args(), [&in](const Use& arg_use) -> bool { return arg_use.get() == in; }); + if (arg_pos == c.arg_end()) { + return {nullptr, -1}; + } + const auto argNum = std::distance(c.arg_begin(), arg_pos); + Argument& argument = *(c.getCalledFunction()->arg_begin() + argNum); + + return {&argument, argNum}; +} + +inline std::vector args(CallSite c, const Path& p) { + if (c.isIndirectCall()) { + return {}; + } + + auto [arg, _] = findArg(c, p); + if (arg != nullptr) { + return {arg}; + } + + std::vector args; + llvm::for_each(c.getCalledFunction()->args(), [&](llvm::Argument& a) { args.emplace_back(&a); }); + return args; +} + +namespace detail { +template +ArgCorrelation correlate(CallSite c, const Path& p, TypeID&& isType) { + auto [arg, _] = findArg(c, p); + + if (!arg) { + const auto count_type_ptr = llvm::count_if(c.args(), [&](const auto& arg) { + const auto type = arg->getType(); + return isType(type); + }); + if (count_type_ptr > 0) { + return ArgCorrelation::Global; + } + return ArgCorrelation::GlobalMismatch; + } + + auto type = arg->getType(); + + if (isType(type)) { + return ArgCorrelation::Exact; + } + return ArgCorrelation::ExactMismatch; +} +} // namespace detail + +inline ArgCorrelation correlate2void(CallSite c, const Path& p) { + return detail::correlate( + c, p, [](llvm::Type* type) { return type->isPointerTy() && type->getPointerElementType()->isIntegerTy(8); }); +} + +inline ArgCorrelation correlate2pointer(CallSite c, const Path& p) { + // weaker predicate than void pointer, but more generally applicable + return detail::correlate(c, p, [](llvm::Type* type) { return type->isPointerTy(); }); +} + +} // namespace typeart::filter + +#endif // TYPEART_FILTERUTIL_H diff --git a/lib/passes/filter/IRPath.h b/lib/passes/filter/IRPath.h new file mode 100644 index 00000000..39a07fbb --- /dev/null +++ b/lib/passes/filter/IRPath.h @@ -0,0 +1,188 @@ +// +// Created by ahueck on 03.11.20. +// + +#ifndef TYPEART_IRPATH_H +#define TYPEART_IRPATH_H + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/IR/CallSite.h" +#include "llvm/IR/Value.h" +#include "llvm/Support/raw_ostream.h" + +#include +namespace typeart::filter { + +struct IRPath { + using Node = llvm::Value*; + std::vector path; + + llvm::Optional getStart() const { + if (path.empty()) { + return llvm::None; + } + return *path.begin(); + } + + llvm::Optional getEnd() const { + return getNodeFromEnd<1>(); + } + + llvm::Optional getEndPrev() const { + return getNodeFromEnd<2>(); + } + + template + llvm::Optional getNodeFromEnd() const { + if (path.empty() || path.size() < n) { + return llvm::None; + } + return *std::prev(path.end(), n); + } + + void pop() { + if (!path.empty()) { + path.pop_back(); + } + } + + void push(Node n) { + path.push_back(n); + } + + bool contains(Node n) const { + return llvm::find_if(path, [&n](const auto* node) { return node == n; }) != std::end(path); + } +}; + +inline llvm::raw_ostream& operator<<(llvm::raw_ostream& os, const IRPath& p) { + const auto& vec = p.path; + if (vec.empty()) { + os << "path = [ ]"; + return os; + } + auto begin = std::begin(vec); + os << "path = [" << **begin; + std::for_each(std::next(begin), std::end(vec), [&](const auto* v) { os << " ->" << *v; }); + os << "]"; + return os; +} + +struct CallsitePath { + // Node: IRPath leads to function: + using Node = std::pair; + + // Structure: [Function (start) or null for global]{1} -> [Path -> Function]* -> (Path)? + llvm::Optional start; + IRPath terminatingPath{}; + std::vector intermediatePath; + + explicit CallsitePath(llvm::Function* root) { + if (root == nullptr) { + start = llvm::None; + } else { + start = root; + } + } + + // Can return nullptr + llvm::Function* getCurrentFunc() { + if (intermediatePath.empty()) { + if (start) { + return start.getValue(); + } + return nullptr; + } + auto end = getEnd(); + if (end) { + return end.getValue().first; + } + return nullptr; + } + + llvm::Optional getStart() const { + if (intermediatePath.empty()) { + return llvm::None; + } + return *intermediatePath.begin(); + } + + llvm::Optional getEnd() const { + return getNodeFromEnd<1>(); + } + + template + llvm::Optional getNodeFromEnd() const { + if (intermediatePath.empty() || intermediatePath.size() < n) { + return llvm::None; + } + return *std::prev(intermediatePath.end(), n); + } + + void push(const IRPath& p) { + auto csite = p.getEnd(); + if (csite) { + llvm::CallSite c(csite.getValue()); + intermediatePath.emplace_back(c.getCalledFunction(), p); + } + } + + void pushFinal(const IRPath& p) { + terminatingPath = p; + } + + void pop() { + if (!intermediatePath.empty()) { + intermediatePath.pop_back(); + } + } + + bool contains(llvm::CallSite c) { + llvm::Function* f = c.getCalledFunction(); + if (f && f == start) { + return true; + } + return llvm::find_if(intermediatePath, [&f](const auto& node) { return node.first == f; }) != + std::end(intermediatePath); + } +}; + +inline llvm::raw_ostream& operator<<(llvm::raw_ostream& os, const CallsitePath::Node& n) { + auto f = n.first; + if (f) { + os << f->getName(); + } else { + os << "--"; + } + os << ":" << n.second; + return os; +} + +inline llvm::raw_ostream& operator<<(llvm::raw_ostream& os, const CallsitePath& p) { + const auto& vec = p.intermediatePath; + if (vec.empty()) { + os << "func_path = ["; + if (p.start) { + os << p.start.getValue()->getName(); + } else { + os << "Module"; + } + os << " -> " << p.terminatingPath << "]"; + return os; + } + auto begin = std::begin(vec); + os << "func_path = [" << *begin; + std::for_each(std::next(begin), std::end(vec), [&](const auto& v) { os << " -> " << v; }); + os << "]"; + return os; +} + +using Path = IRPath; +using FPath = CallsitePath; + +using PathList = std::vector; + +} // namespace typeart::filter + +#endif // TYPEART_IRPATH_H diff --git a/lib/passes/filter/IRSearch.h b/lib/passes/filter/IRSearch.h new file mode 100644 index 00000000..2b90a720 --- /dev/null +++ b/lib/passes/filter/IRSearch.h @@ -0,0 +1,50 @@ +// +// Created by ahueck on 06.11.20. +// + +#ifndef TYPEART_IRSEARCH_H +#define TYPEART_IRSEARCH_H + +#include "llvm/IR/CallSite.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" + +namespace typeart::filter { + +struct DefaultSearch { + auto search(llvm::Value* val, const Path& p) -> std::vector { + using namespace llvm; + + std::vector out; + + if (auto store = llvm::dyn_cast(val)) { + val = store->getPointerOperand(); + if (llvm::isa(val) && !store->getValueOperand()->getType()->isPointerTy()) { + // 1. if we store to an alloca, and the value is not a pointer (i.e., a value) there is no connection to follow + // w.r.t. dataflow. (TODO exceptions could be some pointer arithm.) + return out; + } + if (p.contains(val)) { + // If the pointer operand is already in the path, we do not want to continue. + // Encountered: amg: box_algebra.c: hypre_MinUnionBoxes endless recursion + return out; + } + + out.push_back(val); + // The following return is needed to fix endless recursions (see test/pass/filter/ -> 08 and 09) + return out; // this is what we need in case of following store target + + // 2. TODO if we store to a pointer, analysis is required to filter simple aliasing pointer (filter opportunity, + // see test 01_alloca.llin variable a and c -- c points to a, then c gets passed to MPI) + // 2.1 care has to be taken for argument store to aliasing local (implicit) alloc, i.e., see same test variable %x + // passed to func foo_bar + } + + llvm::transform(val->users(), std::back_inserter(out), [](User* u) { return dyn_cast(u); }); + return out; + } +}; + +} // namespace typeart::filter + +#endif // TYPEART_IRSEARCH_H diff --git a/lib/passes/filter/Matcher.h b/lib/passes/filter/Matcher.h new file mode 100644 index 00000000..11ddc0ac --- /dev/null +++ b/lib/passes/filter/Matcher.h @@ -0,0 +1,79 @@ +// +// Created by ahueck on 18.11.20. +// + +#ifndef TYPEART_MATCHER_H +#define TYPEART_MATCHER_H + +#include "../support/Util.h" + +#include "llvm/ADT/StringSet.h" +#include "llvm/IR/CallSite.h" + +namespace typeart::filter { + +class Matcher { + public: + enum class MatchResult : int { Match, NoMatch, ShouldSkip, ShouldContinue }; + Matcher() = default; + Matcher(const Matcher&) = default; + Matcher(Matcher&&) = default; + Matcher& operator=(const Matcher&) = default; + Matcher& operator=(Matcher&&) = default; + + virtual MatchResult match(llvm::CallSite) const = 0; + + virtual ~Matcher() = default; +}; + +class NoMatcher final : public Matcher { + public: + MatchResult match(llvm::CallSite) const { + return MatchResult::NoMatch; + }; +}; + +class DefaultStringMatcher final : public Matcher { + Regex matcher; + + public: + explicit DefaultStringMatcher(const std::string& regex) : matcher(regex, Regex::NoFlags) { + } + + MatchResult match(llvm::CallSite c) const override { + const auto f = c.getCalledFunction(); + if (f != nullptr) { + const auto f_name = util::demangle(f->getName()); + const bool matched = matcher.match(f_name); + if (matched) { + return MatchResult::Match; + } + } + return MatchResult::NoMatch; + } +}; + +class FunctionOracleMatcher final : public Matcher { + llvm::SmallDenseSet continue_set{{"sqrt"}, {"cos"}, {"sin"}, {"pow"}, {"fabs"}, {"abs"}, {"log"}}; + llvm::SmallDenseSet skip_set{{"printf"}, {"sprintf"}, {"snprintf"}, {"fprintf"}, {"puts"}, + {"__cxa_atexit"}, {"fopen"}, {"fclose"}, {"scanf"}, {"strcmp"}}; + + public: + MatchResult match(llvm::CallSite c) const override { + const auto f = c.getCalledFunction(); + if (f != nullptr) { + const auto f_name = util::demangle(f->getName()); + if (continue_set.count(f_name) > 0) { + return MatchResult::ShouldContinue; + } + if (skip_set.count(f_name) > 0) { + return MatchResult::ShouldSkip; + } + } + return MatchResult::NoMatch; + } +}; + +} // namespace typeart::filter + +#endif // TYPEART_MATCHER_H diff --git a/lib/passes/filter/StandardFilter.cpp b/lib/passes/filter/StandardFilter.cpp new file mode 100644 index 00000000..e8ae2962 --- /dev/null +++ b/lib/passes/filter/StandardFilter.cpp @@ -0,0 +1,217 @@ +// +// Created by ahueck on 19.10.20. +// + +#include "StandardFilter.h" + +namespace typeart::filter::deprecated { + +StandardFilter::StandardFilter(const std::string& glob, bool CallFilterDeep) + : call_regex(util::glob2regex(glob)), ClCallFilterDeep(CallFilterDeep) { +} + +void StandardFilter::setMode(bool search_malloc) { + malloc_mode = search_malloc; +} + +void StandardFilter::setStartingFunction(llvm::Function* start) { + start_f = start; + depth = 0; +} + +bool StandardFilter::filter(Value* in) { + if (in == nullptr) { + LOG_DEBUG("Called with nullptr"); + return false; + } + + if (depth == 15) { + return false; + } + + const auto match = [&](auto callee) -> bool { + const auto name = StandardFilter::getName(callee); + return util::regex_matches(call_regex, name); + }; + + llvm::SmallPtrSet visited_set; + llvm::SmallVector working_set; + llvm::SmallVector working_set_calls; + + const auto addToWork = [&visited_set, &working_set](auto vals) { + for (auto v : vals) { + if (visited_set.find(v) == visited_set.end()) { + working_set.push_back(v); + visited_set.insert(v); + } + } + }; + + const auto peek = [&working_set]() -> Value* { + auto user_iter = working_set.end() - 1; + working_set.erase(user_iter); + return *user_iter; + }; + + // Seed working set with users of value (e.g., our AllocaInst) + addToWork(in->users()); + + // Search through all users of users of .... (e.g., our AllocaInst) + while (!working_set.empty()) { + auto val = peek(); + + // If we encounter a callsite, we want to analyze later, or quit in case we have a regex match + CallSite c(val); + if (c.isCall() || c.isInvoke()) { + const auto callee = c.getCalledFunction(); + const bool indirect_call = callee == nullptr; + + if (indirect_call) { + LOG_DEBUG("Found an indirect call, not filtering alloca: " << util::dump(*val)); + return false; // Indirect calls might contain critical function calls. + } + + const bool is_decl = callee->isDeclaration(); + // FIXME the MPI calls are all hitting this branch (obviously) + if (is_decl) { + LOG_DEBUG("Found call with declaration only. Call: " << util::dump(*c.getInstruction())); + if (c.getIntrinsicID() == Intrinsic::not_intrinsic /*Intrinsic::ID::not_intrinsic*/) { + LOG_DEBUG("Not intrinsic " << ClCallFilterDeep); + if (ClCallFilterDeep && match(callee)) { + const bool can_cont = shouldContinue(c, in); + LOG_DEBUG("Matched, void* analysis " << can_cont); + if (can_cont) { + continue; + } + } + return false; + } else { + LOG_DEBUG("Call is an intrinsic. Continue analyzing...") + continue; + } + } + + if (match(callee)) { + LOG_DEBUG("Found a call. Call: " << util::dump(*c.getInstruction())); + if (ClCallFilterDeep && shouldContinue(c, in)) { + continue; + } + return false; + } + + working_set_calls.push_back(c); + // Caveat: below at the end of the loop, we add users of the function call to the search even though it might be + // a simple "sink" for the alloca we analyse + } else if (auto store = llvm::dyn_cast(val)) { + // If we encounter a store, we follow the store target pointer. + // More inclusive than strictly necessary in some cases. + LOG_DEBUG("Store found: " << util::dump(*store) + << " Store target has users: " << util::dump(store->getPointerOperand()->users())); + auto store_target = store->getPointerOperand(); + // FIXME here we check store operand, if target is another alloca, we already track that?: + // Note: if we apply this to malloc filtering, this might become problematic? + if (!malloc_mode && llvm::isa(store_target)) { + LOG_DEBUG("Target is alloca, skipping!"); + } else { + addToWork(store_target->users()); + } + continue; + } + // cont. our search + addToWork(val->users()); + } + ++depth; + return std::all_of(working_set_calls.begin(), working_set_calls.end(), [&](CallSite c) { return filter(c, in); }); +} + +bool StandardFilter::filter(CallSite& csite, Value* in) { + const auto analyse_arg = [&](auto& csite, auto argNum) -> bool { + Argument& the_arg = *(csite.getCalledFunction()->arg_begin() + argNum); + LOG_DEBUG("Calling filter with inst of argument: " << util::dump(the_arg)); + const bool filter_arg = filter(&the_arg); + LOG_DEBUG("Should filter? : " << filter_arg); + return filter_arg; + }; + + LOG_DEBUG("Analyzing function call " << csite.getCalledFunction()->getName()); + + if (csite.getCalledFunction() == start_f) { + return true; + } + + // this only works if we can correlate alloca with argument: + const auto pos = std::find_if(csite.arg_begin(), csite.arg_end(), + [&in](const Use& arg_use) -> bool { return arg_use.get() == in; }); + // auto pos = csite.arg_end(); + if (pos != csite.arg_end()) { + const auto argNum = std::distance(csite.arg_begin(), pos); + LOG_DEBUG("Found exact position: " << argNum); + return analyse_arg(csite, argNum); + } else { + LOG_DEBUG("Analyze all args, cannot correlate alloca with arg."); + return std::all_of(csite.arg_begin(), csite.arg_end(), [&csite, &analyse_arg](const Use& arg_use) { + auto argNum = csite.getArgumentNo(&arg_use); + return analyse_arg(csite, argNum); + }); + } + + return true; +} + +bool StandardFilter::filter(Argument* arg) { + for (auto* user : arg->users()) { + LOG_DEBUG("Looking at arg user " << util::dump(*user)); + // This code is for non mem2reg code (i.e., where the argument is stored to a local alloca): + if (auto store = llvm::dyn_cast(user)) { + // if (auto* alloca = llvm::dyn_cast(store->getPointerOperand())) { + // LOG_DEBUG("Argument is a store inst and the operand is alloca"); + return filter(store->getPointerOperand()); + // } + } + } + return filter(llvm::dyn_cast(arg)); +} + +bool StandardFilter::shouldContinue(CallSite c, Value* in) const { + LOG_DEBUG("Found a name match, analyzing closer..."); + const auto is_void_ptr = [](Type* type) { + return type->isPointerTy() && type->getPointerElementType()->isIntegerTy(8); + }; + const auto arg_pos = llvm::find_if(c.args(), [&in](const Use& arg_use) -> bool { return arg_use.get() == in; }); + if (arg_pos == c.arg_end()) { + // we had no direct correlation for the arg position + // Now checking if void* is passed, if not we can potentially filter! + auto count_void_ptr = llvm::count_if(c.args(), [&is_void_ptr](const auto& arg) { + const auto type = arg->getType(); + return is_void_ptr(type); + }); + if (count_void_ptr > 0) { + LOG_DEBUG("Call takes a void*, filtering."); + return false; + } + LOG_DEBUG("Call has no void* argument"); + } else { + // We have an arg_pos match + const auto argNum = std::distance(c.arg_begin(), arg_pos); + Argument& the_arg = *(c.getCalledFunction()->arg_begin() + argNum); + auto type = the_arg.getType(); + if (is_void_ptr(type)) { + LOG_DEBUG("Call arg is a void*, filtering."); + return false; + } + LOG_DEBUG("Value* in is not passed as void ptr"); + } + LOG_DEBUG("No filter necessary for this call, continue."); + return true; +} +std::string StandardFilter::getName(const Function* f) { + auto name = f->getName(); + // FIXME figure out if we need to demangle, i.e., source is .c or .cpp + const auto f_name = util::demangle(name); + if (f_name != "") { + name = f_name; + } + + return name; +} +} // namespace typeart::filter::deprecated diff --git a/lib/passes/filter/StandardFilter.h b/lib/passes/filter/StandardFilter.h new file mode 100644 index 00000000..b64c481f --- /dev/null +++ b/lib/passes/filter/StandardFilter.h @@ -0,0 +1,51 @@ +// +// Created by ahueck on 19.10.20. +// + +#ifndef TYPEART_STANDARDFILTER_H +#define TYPEART_STANDARDFILTER_H + +#include "Filter.h" +#include "support/Logger.h" +#include "support/Util.h" + +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/CallSite.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Format.h" + +namespace typeart::filter::deprecated { + +using namespace llvm; + +class StandardFilter final : public Filter { + const std::string call_regex; + bool malloc_mode{false}; + llvm::Function* start_f{nullptr}; + int depth{0}; + bool ClCallFilterDeep{true}; + + public: + explicit StandardFilter(const std::string& glob, bool CallFilterDeep); + + void setMode(bool search_malloc) override; + + void setStartingFunction(llvm::Function* start) override; + + bool filter(Value* in) override; + + private: + bool filter(CallSite& csite, Value* in); + + bool filter(Argument* arg); + + bool shouldContinue(CallSite c, Value* in) const; + + static inline std::string getName(const Function* f); +}; + +} // namespace typeart::filter::deprecated + +#endif // TYPEART_STANDARDFILTER_H diff --git a/lib/passes/filter/StdForwardFilter.cpp b/lib/passes/filter/StdForwardFilter.cpp new file mode 100644 index 00000000..4c4a7bc6 --- /dev/null +++ b/lib/passes/filter/StdForwardFilter.cpp @@ -0,0 +1,83 @@ +// +// Created by ahueck on 26.10.20. +// + +#include "StdForwardFilter.h" + +namespace typeart::filter { + +ForwardFilterImpl::ForwardFilterImpl(std::unique_ptr&& m) : ForwardFilterImpl(std::move(m), nullptr) { +} + +ForwardFilterImpl::ForwardFilterImpl(std::unique_ptr&& m, std::unique_ptr&& deep) + : matcher(std::move(m)), deep_matcher(std::move(deep)) { +} + +FilterAnalysis filter::ForwardFilterImpl::precheck(Value* /*in*/, Function* start) { + if (start != nullptr) { + FunctionAnalysis analysis; + analysis.analyze(start); + if (analysis.empty()) { + return FilterAnalysis::Filter; + } + } + return FilterAnalysis::Continue; +} + +FilterAnalysis filter::ForwardFilterImpl::decl(CallSite current, const Path& p) const { + const bool match_sig = matcher->match(current) == Matcher::MatchResult::Match; + if (match_sig) { + // if we have a deep_matcher it needs to trigger, otherwise analyze + if (!deep_matcher || deep_matcher->match(current) == Matcher::MatchResult::Match) { + auto result = correlate2void(current, p); + switch (result) { + case ArgCorrelation::GlobalMismatch: + [[fallthrough]]; + case ArgCorrelation::ExactMismatch: + LOG_DEBUG("Correlated, continue search"); + return FilterAnalysis::Continue; + default: + return FilterAnalysis::Keep; + } + } + } + // Not a relevant name (e.g. MPI), ask oracle if we have + // some benign (C) function name + const auto oracle_match = oracle.match(current); + switch (oracle_match) { + case Matcher::MatchResult::ShouldSkip: { + return FilterAnalysis::Skip; + } + case Matcher::MatchResult::ShouldContinue: { + return FilterAnalysis::Continue; + } + default: + break; + } + + return FilterAnalysis::Keep; +} + +FilterAnalysis filter::ForwardFilterImpl::def(CallSite current, const Path& p) const { + const bool match_sig = matcher->match(current) == Matcher::MatchResult::Match; + if (match_sig) { + if (!deep_matcher || deep_matcher->match(current) == Matcher::MatchResult::Match) { + auto result = correlate2void(current, p); + switch (result) { + case ArgCorrelation::GlobalMismatch: + [[fallthrough]]; + case ArgCorrelation::ExactMismatch: + LOG_DEBUG("Correlated definition args, continue search"); + return FilterAnalysis::Continue; + default: + return FilterAnalysis::Keep; + } + } else { + return FilterAnalysis::Keep; + } + } + + return FilterAnalysis::FollowDef; +} + +} // namespace typeart::filter \ No newline at end of file diff --git a/lib/passes/filter/StdForwardFilter.h b/lib/passes/filter/StdForwardFilter.h new file mode 100644 index 00000000..7416d983 --- /dev/null +++ b/lib/passes/filter/StdForwardFilter.h @@ -0,0 +1,42 @@ +// +// Created by ahueck on 26.10.20. +// + +#ifndef TYPEART_STDFORWARDFILTER_H +#define TYPEART_STDFORWARDFILTER_H + +#include "FilterBase.h" +#include "Matcher.h" + +namespace typeart::filter { + +struct StdFilterTrait { + constexpr static bool Indirect = false; + constexpr static bool Intrinsic = false; + constexpr static bool Declaration = true; + constexpr static bool Definition = true; + constexpr static bool PreCheck = true; +}; + +struct ForwardFilterImpl { + using Support = StdFilterTrait; + std::unique_ptr matcher; + std::unique_ptr deep_matcher; + FunctionOracleMatcher oracle; // TODO make set flexible + + explicit ForwardFilterImpl(std::unique_ptr&& m); + + ForwardFilterImpl(std::unique_ptr&& m, std::unique_ptr&& deep); + + FilterAnalysis precheck(Value* in, Function* start); + + FilterAnalysis decl(CallSite current, const Path& p) const; + + FilterAnalysis def(CallSite current, const Path& p) const; +}; + +using StandardForwardFilter = BaseFilter; + +} // namespace typeart::filter + +#endif // TYPEART_STDFORWARDFILTER_H diff --git a/lib/passes/instrumentation/Instrumentation.cpp b/lib/passes/instrumentation/Instrumentation.cpp new file mode 100644 index 00000000..e628abff --- /dev/null +++ b/lib/passes/instrumentation/Instrumentation.cpp @@ -0,0 +1,49 @@ +// +// Created by ahueck on 18.11.20. +// +#include "Instrumentation.h" + +namespace typeart { + +InstrumentationContext::InstrumentationContext(std::unique_ptr col, + std::unique_ptr instr) + : collector(std::move(col)), instrumenter(std::move(instr)) { +} + +InstrCount InstrumentationContext::handleHeap(const MallocDataList& mallocs) { + if (mallocs.empty()) { + return 0; + } + auto heap_args = collector->collectHeap(mallocs); + const auto heap_count = instrumenter->instrumentHeap(heap_args); + return heap_count; +} + +InstrCount InstrumentationContext::handleFree(const FreeDataList& frees) { + if (frees.empty()) { + return 0; + } + auto free_args = collector->collectFree(frees); + const auto free_count = instrumenter->instrumentFree(free_args); + return free_count; +} + +InstrCount InstrumentationContext::handleStack(const AllocaDataList& allocas) { + if (allocas.empty()) { + return 0; + } + auto alloca_args = collector->collectStack(allocas); + const auto stack_count = instrumenter->instrumentStack(alloca_args); + return stack_count; +} + +InstrCount InstrumentationContext::handleGlobal(const GlobalDataList& globals) { + if (globals.empty()) { + return 0; + } + auto global_args = collector->collectGlobal(globals); + const auto global_count = instrumenter->instrumentGlobal(global_args); + return global_count; +} + +} // namespace typeart \ No newline at end of file diff --git a/lib/passes/instrumentation/Instrumentation.h b/lib/passes/instrumentation/Instrumentation.h new file mode 100644 index 00000000..84b24e51 --- /dev/null +++ b/lib/passes/instrumentation/Instrumentation.h @@ -0,0 +1,116 @@ +// +// Created by ahueck on 08.10.20. +// + +#ifndef TYPEART_INSTRUMENTATION_H +#define TYPEART_INSTRUMENTATION_H + +#include "../analysis/MemOpData.h" + +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Casting.h" + +namespace llvm { +class Value; +} + +namespace typeart { + +struct ArgStrId { + static constexpr char pointer[] = "pointer"; + static constexpr char type_id[] = "type_id"; + static constexpr char type_size[] = "type_size"; + static constexpr char byte_count[] = "byte_count"; + static constexpr char element_count[] = "element_count"; + static constexpr char realloc_ptr[] = "realloc_ptr"; +}; + +struct ArgMap { + using ID = ArgStrId; + using ArgsContainer = llvm::StringMap; + using Key = ArgsContainer::key_type; + ArgsContainer args; + + ArgsContainer::mapped_type& operator[](Key key) { + return args[key]; + } + + /* + const llvm::Optional operator[](ArgsContainer::key_type key) const { + auto it = args.find(key); + if (it != args.end()) { + return {it->second}; + } + return llvm::None; + }*/ + + template + T* get_as(Key key) const { + T* elem{nullptr}; + if (auto it = args.find(key); it != args.end()) { + elem = llvm::dyn_cast(it->second); + } + return elem; + } + + llvm::Value* get_value(Key key) const { + return get_as(key); + } +}; + +namespace detail { +template +struct MemContainer { + Data mem_data; + ArgMap args; +}; +} // namespace detail + +using HeapContainer = detail::MemContainer; +using FreeContainer = detail::MemContainer; +using StackContainer = detail::MemContainer; +using GlobalContainer = detail::MemContainer; + +using HeapArgList = llvm::SmallVector; +using FreeArgList = llvm::SmallVector; +using StackArgList = llvm::SmallVector; +using GlobalArgList = llvm::SmallVector; + +using InstrCount = size_t; + +class ArgumentCollector { + public: + virtual HeapArgList collectHeap(const MallocDataList& mallocs) = 0; + virtual FreeArgList collectFree(const FreeDataList& frees) = 0; + virtual StackArgList collectStack(const AllocaDataList& frees) = 0; + virtual GlobalArgList collectGlobal(const GlobalDataList& globals) = 0; + virtual ~ArgumentCollector() = default; +}; + +class MemoryInstrument { + public: + virtual InstrCount instrumentHeap(const HeapArgList& heap) = 0; + virtual InstrCount instrumentFree(const FreeArgList& frees) = 0; + virtual InstrCount instrumentStack(const StackArgList& frees) = 0; + virtual InstrCount instrumentGlobal(const GlobalArgList& globals) = 0; + virtual ~MemoryInstrument() = default; +}; + +class InstrumentationContext { + private: + std::unique_ptr collector; + std::unique_ptr instrumenter; + + public: + InstrumentationContext(std::unique_ptr col, std::unique_ptr instr); + + InstrCount handleHeap(const MallocDataList& mallocs); + InstrCount handleFree(const FreeDataList& frees); + InstrCount handleStack(const AllocaDataList& frees); + InstrCount handleGlobal(const GlobalDataList& globals); +}; + +} // namespace typeart + +#endif // TYPEART_INSTRUMENTATION_H diff --git a/lib/support/InstrumentationHelper.cpp b/lib/passes/instrumentation/InstrumentationHelper.cpp similarity index 51% rename from lib/support/InstrumentationHelper.cpp rename to lib/passes/instrumentation/InstrumentationHelper.cpp index 9039bb63..585d3d38 100644 --- a/lib/support/InstrumentationHelper.cpp +++ b/lib/passes/instrumentation/InstrumentationHelper.cpp @@ -6,7 +6,6 @@ #include "llvm/IR/CFG.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalValue.h" -#include "llvm/IR/IRBuilder.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" @@ -63,55 +62,8 @@ void InstrumentationHelper::setModule(llvm::Module& m) { module = &m; } -llvm::Function* InstrumentationHelper::make_function(llvm::StringRef basename, llvm::ArrayRef args, - bool fixed_name) { - const auto make_fname = [&fixed_name](llvm::StringRef name, llvm::ArrayRef args) { - if (fixed_name) { - return std::string(name.str()); - } - return std::string((name + "_" + std::to_string(args.size())).str()); - }; - - const auto name = make_fname(basename, args); - if (auto it = f_map.find(name); it != f_map.end()) { - return it->second; - } - - auto& m = *module; - auto& c = m.getContext(); - const auto addOptimizerAttributes = [&](llvm::Function* f) { - for (Argument& arg : f->args()) { - if (arg.getType()->isPointerTy()) { - arg.addAttr(Attribute::NoCapture); - arg.addAttr(Attribute::ReadOnly); - } - } - }; - const auto setFunctionLinkageExternal = [](llvm::Function* f) { - f->setLinkage(GlobalValue::ExternalLinkage); - // f->setLinkage(GlobalValue::ExternalWeakLinkage); - }; - const auto do_make = [&](auto& name, auto f_type) { - auto fc = m.getOrInsertFunction(name, f_type); -#if LLVM_VERSION >= 10 - auto f = dyn_cast(fc.getCallee()); -#else - auto f = dyn_cast(fc); -#endif - setFunctionLinkageExternal(f); - addOptimizerAttributes(f); - return f; - }; - - auto f = do_make(name, FunctionType::get(Type::getVoidTy(c), args, false)); - - f_map[name] = f; - - return f; -} - -const std::map& InstrumentationHelper::getFunctionMap() const { - return f_map; +llvm::Module* InstrumentationHelper::getModule() const { + return module; } } // namespace typeart diff --git a/lib/support/InstrumentationHelper.h b/lib/passes/instrumentation/InstrumentationHelper.h similarity index 86% rename from lib/support/InstrumentationHelper.h rename to lib/passes/instrumentation/InstrumentationHelper.h index b9bd99ad..75b3e7e4 100644 --- a/lib/support/InstrumentationHelper.h +++ b/lib/passes/instrumentation/InstrumentationHelper.h @@ -8,8 +8,6 @@ #ifndef LIB_INSTRUMENTATIONHELPER_H_ #define LIB_INSTRUMENTATIONHELPER_H_ -// #include "llvm/IR/LLVMContext.h" - #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" @@ -37,18 +35,17 @@ enum class IType { function_id, // Type for identifying a function type_id, // Type for identifying a type extent, // Type for identifying an array length - alloca_id, // Type for identifying a memory allocaion + alloca_id, // Type for identifying a memory allocation stack_count, // Type for identifying a count of stack alloca instructions }; class InstrumentationHelper { llvm::Module* module{nullptr}; - std::map f_map; public: InstrumentationHelper(); void setModule(llvm::Module& m); - llvm::Function* make_function(llvm::StringRef name, llvm::ArrayRef args, bool fixed_name = true); + llvm::Module* getModule() const; static llvm::SmallVector make_signature(const llvm::ArrayRef& args); template diff --git a/lib/passes/instrumentation/MemOpArgCollector.cpp b/lib/passes/instrumentation/MemOpArgCollector.cpp new file mode 100644 index 00000000..331b7dd2 --- /dev/null +++ b/lib/passes/instrumentation/MemOpArgCollector.cpp @@ -0,0 +1,218 @@ +// +// Created by ahueck on 08.10.20. +// + +#include "MemOpArgCollector.h" + +#include "../TypeManager.h" +#include "InstrumentationHelper.h" +#include "support/Logger.h" +#include "support/TypeUtil.h" +#include "support/Util.h" + +#include "llvm/IR/Instructions.h" + +namespace tu = typeart::util::type; +using namespace llvm; + +namespace typeart { + +MemOpArgCollector::MemOpArgCollector(TypeManager& tm, InstrumentationHelper& instr) + : ArgumentCollector(), type_m(&tm), instr_helper(&instr) { +} + +HeapArgList MemOpArgCollector::collectHeap(const MallocDataList& mallocs) { + HeapArgList list; + list.reserve(mallocs.size()); + const llvm::DataLayout& dl = instr_helper->getModule()->getDataLayout(); + for (const MallocData& mdata : mallocs) { + ArgMap arg_map; + const auto malloc_call = mdata.call; + BitCastInst* primaryBitcast = mdata.primary; + auto kind = mdata.kind; + + // Number of bytes allocated + auto mallocArg = malloc_call->getOperand(0); + int typeId = type_m->getOrRegisterType(malloc_call->getType()->getPointerElementType(), + dl); // retrieveTypeID(tu::getVoidType(c)); + if (typeId == TA_UNKNOWN_TYPE) { + LOG_ERROR("Unknown allocated type. Not instrumenting. " << util::dump(*malloc_call)); + // TODO notify caller that we skipped: via lambda callback function + continue; + } + + // Number of bytes per element, 1 for void* + unsigned typeSize = tu::getTypeSizeInBytes(malloc_call->getType()->getPointerElementType(), dl); + + // Use the first cast as the determining type (if there is any) + if (primaryBitcast != nullptr) { + auto* dstPtrType = primaryBitcast->getDestTy()->getPointerElementType(); + + typeSize = tu::getTypeSizeInBytes(dstPtrType, dl); + + // Resolve arrays + // TODO: Write tests for this case + if (dstPtrType->isArrayTy()) { + dstPtrType = tu::getArrayElementType(dstPtrType); + } + + typeId = type_m->getOrRegisterType(dstPtrType, dl); + if (typeId == TA_UNKNOWN_TYPE) { + LOG_ERROR("Target type of casted allocation is unknown. Not instrumenting. " << util::dump(*malloc_call)); + LOG_ERROR("Cast: " << util::dump(*primaryBitcast)); + LOG_ERROR("Target type: " << util::dump(*dstPtrType)); + // TODO notify caller that we skipped: via lambda callback function + continue; + } + } else { + LOG_ERROR("Primary bitcast is null. malloc: " << util::dump(*malloc_call)) + } + + auto* typeIdConst = instr_helper->getConstantFor(IType::type_id, typeId); + Value* typeSizeConst = instr_helper->getConstantFor(IType::extent, typeSize); + + Value* elementCount{nullptr}; + Value* byte_count{nullptr}; + Value* realloc_ptr{nullptr}; + switch (kind) { + case MemOpKind::NewLike: + [[fallthrough]]; + case MemOpKind::MallocLike: + byte_count = mallocArg; + break; + case MemOpKind::CallocLike: { + if (mdata.primary == nullptr) { + // we need the second arg when the calloc type is identified as void* to calculcate total bytes allocated + typeSizeConst = malloc_call->getOperand(1); + } + elementCount = malloc_call->getOperand(0); + break; + } + case MemOpKind::ReallocLike: + realloc_ptr = malloc_call->getOperand(0); + byte_count = malloc_call->getOperand(1); + break; + case MemOpKind::AlignedAllocLike: + byte_count = malloc_call->getArgOperand(1); + break; + default: + LOG_ERROR("Unknown malloc kind. Not instrumenting. " << util::dump(*malloc_call)); + // TODO see above continues + continue; + } + + arg_map[ArgMap::ID::pointer] = malloc_call; + arg_map[ArgMap::ID::type_id] = typeIdConst; + arg_map[ArgMap::ID::type_size] = typeSizeConst; + arg_map[ArgMap::ID::byte_count] = byte_count; + arg_map[ArgMap::ID::element_count] = elementCount; + arg_map[ArgMap::ID::realloc_ptr] = realloc_ptr; + list.emplace_back(HeapArgList::value_type{mdata, arg_map}); + } + + return list; +} + +FreeArgList MemOpArgCollector::collectFree(const FreeDataList& frees) { + FreeArgList list; + list.reserve(frees.size()); + for (const FreeData& fdata : frees) { + ArgMap arg_map; + auto free_call = fdata.call; + + Value* freeArg{nullptr}; + switch (fdata.kind) { + case MemOpKind::DeleteLike: + [[fallthrough]]; + case MemOpKind::FreeLike: + freeArg = free_call->getOperand(0); + break; + default: + LOG_ERROR("Unknown free kind. Not instrumenting. " << util::dump(*free_call)); + continue; + } + + arg_map[ArgMap::ID::pointer] = freeArg; + list.emplace_back(FreeArgList::value_type{fdata, arg_map}); + } + + return list; +} + +StackArgList MemOpArgCollector::collectStack(const AllocaDataList& allocs) { + using namespace llvm; + StackArgList list; + list.reserve(allocs.size()); + const llvm::DataLayout& dl = instr_helper->getModule()->getDataLayout(); + + for (const AllocaData& adata : allocs) { + ArgMap arg_map; + auto alloca = adata.alloca; + Type* elementType = alloca->getAllocatedType(); + Value* numElementsVal = nullptr; + // The length can be specified statically through the array type or as a separate argument. + // Both cases are handled here. + if (adata.is_vla) { + numElementsVal = alloca->getArraySize(); + // This should not happen in generated IR code + assert(!elementType->isArrayTy() && "VLAs of array types are currently not supported."); + } else { + size_t arraySize = adata.array_size; + if (elementType->isArrayTy()) { + arraySize = arraySize * tu::getArrayLengthFlattened(elementType); + elementType = tu::getArrayElementType(elementType); + } + numElementsVal = instr_helper->getConstantFor(IType::extent, arraySize); + } + + // unsigned typeSize = tu::getTypeSizeInBytes(elementType, dl); + int typeId = type_m->getOrRegisterType(elementType, dl); + + if (typeId == TA_UNKNOWN_TYPE) { + LOG_ERROR("Type is not supported: " << util::dump(*elementType)); + } + + auto* typeIdConst = instr_helper->getConstantFor(IType::type_id, typeId); + + arg_map[ArgMap::ID::pointer] = alloca; + arg_map[ArgMap::ID::type_id] = typeIdConst; + arg_map[ArgMap::ID::element_count] = numElementsVal; + + list.emplace_back(StackArgList::value_type{adata, arg_map}); + } + + return list; +} + +GlobalArgList MemOpArgCollector::collectGlobal(const GlobalDataList& globals) { + GlobalArgList list; + list.reserve(globals.size()); + const llvm::DataLayout& dl = instr_helper->getModule()->getDataLayout(); + + for (const GlobalData& gdata : globals) { + ArgMap arg_map; + auto global = gdata.global; + auto type = global->getValueType(); + + unsigned numElements = 1; + if (type->isArrayTy()) { + numElements = tu::getArrayLengthFlattened(type); + type = tu::getArrayElementType(type); + } + + int typeId = type_m->getOrRegisterType(type, dl); + auto* typeIdConst = instr_helper->getConstantFor(IType::type_id, typeId); + auto* numElementsConst = instr_helper->getConstantFor(IType::extent, numElements); + // auto globalPtr = IRB.CreateBitOrPointerCast(global, instr.getTypeFor(IType::ptr)); + + arg_map[ArgMap::ID::pointer] = global; + arg_map[ArgMap::ID::type_id] = typeIdConst; + arg_map[ArgMap::ID::element_count] = numElementsConst; + + list.emplace_back(GlobalArgList::value_type{gdata, arg_map}); + } + + return list; +} + +} // namespace typeart \ No newline at end of file diff --git a/lib/passes/instrumentation/MemOpArgCollector.h b/lib/passes/instrumentation/MemOpArgCollector.h new file mode 100644 index 00000000..1a2d127f --- /dev/null +++ b/lib/passes/instrumentation/MemOpArgCollector.h @@ -0,0 +1,27 @@ +// +// Created by ahueck on 08.10.20. +// + +#ifndef TYPEART_MEMOPARGCOLLECTOR_H +#define TYPEART_MEMOPARGCOLLECTOR_H + +#include "Instrumentation.h" + +namespace typeart { +class TypeManager; +class InstrumentationHelper; + +class MemOpArgCollector final : public ArgumentCollector { + TypeManager* type_m; + InstrumentationHelper* instr_helper; + + public: + MemOpArgCollector(TypeManager&, InstrumentationHelper&); + HeapArgList collectHeap(const MallocDataList& mallocs) override; + FreeArgList collectFree(const FreeDataList& frees) override; + StackArgList collectStack(const AllocaDataList& allocs) override; + GlobalArgList collectGlobal(const GlobalDataList& globals) override; +}; +} // namespace typeart + +#endif // TYPEART_MEMOPARGCOLLECTOR_H diff --git a/lib/passes/instrumentation/MemOpInstrumentation.cpp b/lib/passes/instrumentation/MemOpInstrumentation.cpp new file mode 100644 index 00000000..87f67d6d --- /dev/null +++ b/lib/passes/instrumentation/MemOpInstrumentation.cpp @@ -0,0 +1,195 @@ +// +// Created by ahueck on 09.10.20. +// + +#include "MemOpInstrumentation.h" + +#include "../TypeManager.h" +#include "InstrumentationHelper.h" +#include "TransformUtil.h" +#include "TypeARTFunctions.h" +#include "support/Logger.h" +#include "support/Util.h" + +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/Transforms/Utils/CtorUtils.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" + +using namespace llvm; + +namespace typeart { + +MemOpInstrumentation::MemOpInstrumentation(TAFunctionQuery& fquery, InstrumentationHelper& instr) + : MemoryInstrument(), fquery(&fquery), instr_helper(&instr) { +} + +InstrCount MemOpInstrumentation::instrumentHeap(const HeapArgList& heap) { + InstrCount counter{0}; + for (const auto& [malloc, args] : heap) { + auto kind = malloc.kind; + Instruction* malloc_call = args.get_as(ArgMap::ID::pointer); + + Instruction* insertBefore = malloc_call->getNextNode(); + if (malloc.is_invoke) { + const InvokeInst* inv = dyn_cast(malloc_call); + insertBefore = &(*inv->getNormalDest()->getFirstInsertionPt()); + } + + IRBuilder<> IRB(insertBefore); + + auto typeIdConst = args.get_value(ArgMap::ID::type_id); + auto typeSizeConst = args.get_value(ArgMap::ID::type_size); + + bool single_byte_type{false}; + if (auto* const_int = llvm::dyn_cast(typeSizeConst)) { + single_byte_type = const_int->equalsInt(1); + } + + Value* elementCount{nullptr}; + + switch (kind) { + case MemOpKind::AlignedAllocLike: + [[fallthrough]]; + case MemOpKind::NewLike: + [[fallthrough]]; + case MemOpKind::MallocLike: { + auto bytes = args.get_value(ArgMap::ID::byte_count); // can be null (for calloc, realloc) + elementCount = single_byte_type ? bytes : IRB.CreateUDiv(bytes, typeSizeConst); + break; + } + case MemOpKind::CallocLike: { + if (malloc.primary == nullptr) { + auto elems = args.get_value(ArgMap::ID::element_count); + auto type_size = args.get_value(ArgMap::ID::type_size); + elementCount = IRB.CreateMul(elems, type_size); + } else { + elementCount = args.get_value(ArgMap::ID::element_count); + } + break; + } + case MemOpKind::ReallocLike: { + auto mArg = args.get_value(ArgMap::ID::byte_count); + auto addrOp = args.get_value(ArgMap::ID::realloc_ptr); + + elementCount = single_byte_type ? mArg : IRB.CreateUDiv(mArg, typeSizeConst); + IRBuilder<> FreeB(malloc_call); + FreeB.CreateCall(fquery->getFunctionFor(IFunc::free), ArrayRef{addrOp}); + break; + } + default: + LOG_ERROR("Unknown malloc kind. Not instrumenting. " << util::dump(*malloc_call)); + continue; + } + + IRB.CreateCall(fquery->getFunctionFor(IFunc::heap), ArrayRef{malloc_call, typeIdConst, elementCount}); + ++counter; + } + + return counter; +} + +InstrCount MemOpInstrumentation::instrumentFree(const FreeArgList& frees) { + InstrCount counter{0}; + for (const auto& [fdata, args] : frees) { + auto free_call = fdata.call; + const bool is_invoke = fdata.is_invoke; + + Instruction* insertBefore = free_call->getNextNode(); + if (is_invoke) { + auto* inv = dyn_cast(free_call); + insertBefore = &(*inv->getNormalDest()->getFirstInsertionPt()); + } + + Value* free_arg{nullptr}; + switch (fdata.kind) { + case MemOpKind::DeleteLike: + [[fallthrough]]; + case MemOpKind::FreeLike: + free_arg = args.get_value(ArgMap::ID::pointer); + break; + default: + LOG_ERROR("Unknown free kind. Not instrumenting. " << util::dump(*free_call)); + continue; + } + + IRBuilder<> IRB(insertBefore); + IRB.CreateCall(fquery->getFunctionFor(IFunc::free), ArrayRef{free_arg}); + ++counter; + } + + return counter; +} + +InstrCount MemOpInstrumentation::instrumentStack(const StackArgList& stack) { + using namespace transform; + InstrCount counter{0}; + StackCounter::StackOpCounter allocCounts; + Function* f{nullptr}; + for (const auto& [sdata, args] : stack) { + // auto alloca = sdata.alloca; + auto* alloca = args.get_as(ArgMap::ID::pointer); + + IRBuilder<> IRB(alloca->getNextNode()); + + auto typeIdConst = args.get_value(ArgMap::ID::type_id); + auto numElementsVal = args.get_value(ArgMap::ID::element_count); + auto arrayPtr = IRB.CreateBitOrPointerCast(alloca, instr_helper->getTypeFor(IType::ptr)); + + IRB.CreateCall(fquery->getFunctionFor(IFunc::stack), ArrayRef{arrayPtr, typeIdConst, numElementsVal}); + ++counter; + + auto bb = alloca->getParent(); + allocCounts[bb]++; + if (!f) { + f = bb->getParent(); + } + } + + if (f) { + StackCounter scount(f, instr_helper, fquery); + scount.addStackHandling(allocCounts); + } + + return counter; +} + +InstrCount MemOpInstrumentation::instrumentGlobal(const GlobalArgList& globals) { + InstrCount counter{0}; + + const auto instrumentGlobalsInCtor = [&](auto& IRB) { + for (const auto& [gdata, args] : globals) { + // Instruction* global = args.get_as("pointer"); + auto global = gdata.global; + auto typeIdConst = args.get_value(ArgMap::ID::type_id); + auto numElementsVal = args.get_value(ArgMap::ID::element_count); + auto globalPtr = IRB.CreateBitOrPointerCast(global, instr_helper->getTypeFor(IType::ptr)); + IRB.CreateCall(fquery->getFunctionFor(IFunc::global), ArrayRef{globalPtr, typeIdConst, numElementsVal}); + ++counter; + } + }; + + const auto makeCtorFuncBody = [&]() -> IRBuilder<> { + auto m = instr_helper->getModule(); + auto& c = m->getContext(); + auto ctorFunctionName = "__typeart_init_module_" + m->getSourceFileName(); // needed -- will not work with piping? + + FunctionType* ctorType = FunctionType::get(llvm::Type::getVoidTy(c), false); + Function* ctorFunction = Function::Create(ctorType, Function::PrivateLinkage, ctorFunctionName, m); + + BasicBlock* entry = BasicBlock::Create(c, "entry", ctorFunction); + + llvm::appendToGlobalCtors(*m, ctorFunction, 0, nullptr); + + IRBuilder<> IRB(entry); + return IRB; + }; + + auto IRB = makeCtorFuncBody(); + instrumentGlobalsInCtor(IRB); + IRB.CreateRetVoid(); + + return counter; +} + +} // namespace typeart \ No newline at end of file diff --git a/lib/passes/instrumentation/MemOpInstrumentation.h b/lib/passes/instrumentation/MemOpInstrumentation.h new file mode 100644 index 00000000..af9530ca --- /dev/null +++ b/lib/passes/instrumentation/MemOpInstrumentation.h @@ -0,0 +1,28 @@ +// +// Created by ahueck on 09.10.20. +// + +#ifndef TYPEART_MEMOPINSTRUMENTATION_H +#define TYPEART_MEMOPINSTRUMENTATION_H + +#include "Instrumentation.h" + +namespace typeart { + +class TAFunctionQuery; +class InstrumentationHelper; + +class MemOpInstrumentation final : public MemoryInstrument { + TAFunctionQuery* fquery; + InstrumentationHelper* instr_helper; + + public: + MemOpInstrumentation(TAFunctionQuery& fquery, InstrumentationHelper& instr); + InstrCount instrumentHeap(const HeapArgList& heap) override; + InstrCount instrumentFree(const FreeArgList& frees) override; + InstrCount instrumentStack(const StackArgList& stack) override; + InstrCount instrumentGlobal(const GlobalArgList& globals) override; +}; + +} // namespace typeart +#endif // TYPEART_MEMOPINSTRUMENTATION_H diff --git a/lib/passes/instrumentation/TransformUtil.h b/lib/passes/instrumentation/TransformUtil.h new file mode 100644 index 00000000..1321c94d --- /dev/null +++ b/lib/passes/instrumentation/TransformUtil.h @@ -0,0 +1,62 @@ +// +// Created by ahueck on 09.10.20. +// + +#ifndef TYPEART_TRANSFORMUTIL_H +#define TYPEART_TRANSFORMUTIL_H + +#include "InstrumentationHelper.h" +#include "TypeARTFunctions.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/EscapeEnumerator.h" + +namespace typeart::transform { + +struct StackCounter { + using StackOpCounter = llvm::SmallDenseMap; + llvm::Function* f; + InstrumentationHelper* instr_helper; + TAFunctionQuery* fquery; + + StackCounter(llvm::Function* f, InstrumentationHelper* instr, TAFunctionQuery* query) + : f(f), instr_helper(instr), fquery(query) { + } + + void addStackHandling(StackOpCounter& allocCounts) const { + using namespace llvm; + // LOG_DEBUG("Add alloca counter") + // counter = 0 at beginning of function + IRBuilder<> CBuilder(f->getEntryBlock().getFirstNonPHI()); + auto* counter = CBuilder.CreateAlloca(instr_helper->getTypeFor(IType::stack_count), nullptr, "__ta_alloca_counter"); + CBuilder.CreateStore(instr_helper->getConstantFor(IType::stack_count), counter); + + // In each basic block: counter =+ num_alloca (in BB) + for (auto data : allocCounts) { + IRBuilder<> IRB(data.first->getTerminator()); + auto* load_counter = IRB.CreateLoad(counter); + Value* increment_counter = + IRB.CreateAdd(instr_helper->getConstantFor(IType::stack_count, data.second), load_counter); + IRB.CreateStore(increment_counter, counter); + } + + // Find return instructions: + // if(counter > 0) call runtime for stack cleanup + EscapeEnumerator ee(*f); + while (IRBuilder<>* irb = ee.Next()) { + auto* I = &(*irb->GetInsertPoint()); + + auto* counter_load = irb->CreateLoad(counter, "__ta_counter_load"); + auto* cond = irb->CreateICmpNE(counter_load, instr_helper->getConstantFor(IType::stack_count), "__ta_cond"); + auto* then_term = SplitBlockAndInsertIfThen(cond, I, false); + irb->SetInsertPoint(then_term); + irb->CreateCall(fquery->getFunctionFor(IFunc::scope), ArrayRef{counter_load}); + } + } +}; + +} // namespace typeart::transform + +#endif // TYPEART_TRANSFORMUTIL_H diff --git a/lib/passes/instrumentation/TypeARTFunctions.cpp b/lib/passes/instrumentation/TypeARTFunctions.cpp new file mode 100644 index 00000000..68fa06b3 --- /dev/null +++ b/lib/passes/instrumentation/TypeARTFunctions.cpp @@ -0,0 +1,86 @@ +// +// Created by ahueck on 08.10.20. +// + +#include "TypeARTFunctions.h" + +#include "llvm/IR/Argument.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" + +using namespace llvm; + +namespace typeart { + +TAFunctionDeclarator::TAFunctionDeclarator(Module& m, InstrumentationHelper& instr, TAFunctions& tafunc) + : m(m), instr(instr), tafunc(tafunc) { +} + +llvm::Function* TAFunctionDeclarator::make_function(IFunc id, llvm::StringRef basename, + llvm::ArrayRef args, bool fixed_name) { + const auto make_fname = [&fixed_name](llvm::StringRef name, llvm::ArrayRef args) { + if (fixed_name) { + return std::string(name.str()); + } + return std::string((name + "_" + std::to_string(args.size())).str()); + }; + + const auto name = make_fname(basename, args); + if (auto it = f_map.find(name); it != f_map.end()) { + return it->second; + } + + auto& c = m.getContext(); + const auto addOptimizerAttributes = [&](llvm::Function* f) { + for (Argument& arg : f->args()) { + if (arg.getType()->isPointerTy()) { + arg.addAttr(Attribute::NoCapture); + arg.addAttr(Attribute::ReadOnly); + } + } + }; + const auto setFunctionLinkageExternal = [](llvm::Function* f) { + f->setLinkage(GlobalValue::ExternalLinkage); + // f->setLinkage(GlobalValue::ExternalWeakLinkage); + }; + const auto do_make = [&](auto& name, auto f_type) { + auto fc = m.getOrInsertFunction(name, f_type); +#if LLVM_VERSION >= 10 + auto f = dyn_cast(fc.getCallee()); +#else + auto f = dyn_cast(fc); +#endif + setFunctionLinkageExternal(f); + addOptimizerAttributes(f); + return f; + }; + + auto f = do_make(name, FunctionType::get(Type::getVoidTy(c), args, false)); + + f_map[name] = f; + + tafunc.putFunctionFor(id, f); + + return f; +} + +const llvm::StringMap& TAFunctionDeclarator::getFunctionMap() const { + return f_map; +} + +TAFunctions::TAFunctions() = default; + +Function* TAFunctions::getFunctionFor(IFunc id) { + return typeart_callbacks[id]; +} + +void TAFunctions::putFunctionFor(IFunc id, llvm::Function* f) { + typeart_callbacks[id] = f; +} + +} // namespace typeart diff --git a/lib/passes/instrumentation/TypeARTFunctions.h b/lib/passes/instrumentation/TypeARTFunctions.h new file mode 100644 index 00000000..cc5f1c52 --- /dev/null +++ b/lib/passes/instrumentation/TypeARTFunctions.h @@ -0,0 +1,64 @@ +// +// Created by ahueck on 08.10.20. +// + +#ifndef TYPEART_TYPEARTFUNCTIONS_H +#define TYPEART_TYPEARTFUNCTIONS_H + +#include "InstrumentationHelper.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" + +namespace llvm { +class Function; +class Type; +class Module; +} // namespace llvm + +namespace typeart { + +enum class IFunc : unsigned { + heap, + stack, + global, + free, + scope, +}; + +class TAFunctionQuery { + public: + virtual llvm::Function* getFunctionFor(IFunc id) = 0; + virtual ~TAFunctionQuery() = default; +}; + +class TAFunctions : public TAFunctionQuery { + // densemap has problems with IFunc + using FMap = std::unordered_map; + FMap typeart_callbacks; + + public: + TAFunctions(); + + llvm::Function* getFunctionFor(IFunc id) override; + void putFunctionFor(IFunc id, llvm::Function* f); +}; + +class TAFunctionDeclarator { + llvm::Module& m; + [[maybe_unused]] InstrumentationHelper& instr; + TAFunctions& tafunc; + llvm::StringMap f_map; + + public: + TAFunctionDeclarator(llvm::Module& m, InstrumentationHelper& instr, TAFunctions& tafunc); + llvm::Function* make_function(IFunc id, llvm::StringRef basename, llvm::ArrayRef args, + bool fixed_name = true); + const llvm::StringMap& getFunctionMap() const; + virtual ~TAFunctionDeclarator() = default; +}; + +} // namespace typeart + +#endif // TYPEART_TYPEARTFUNCTIONS_H diff --git a/lib/support/TypeUtil.cpp b/lib/passes/support/TypeUtil.cpp similarity index 92% rename from lib/support/TypeUtil.cpp rename to lib/passes/support/TypeUtil.cpp index 3059d7fd..5732672b 100644 --- a/lib/support/TypeUtil.cpp +++ b/lib/passes/support/TypeUtil.cpp @@ -1,6 +1,6 @@ #include "TypeUtil.h" -#include "Logger.h" +#include "support/Logger.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DataLayout.h" @@ -9,9 +9,11 @@ using namespace llvm; -namespace typeart { -namespace util { -namespace type { +namespace typeart::util::type { + +bool isi64Ptr(llvm::Type* type) { + return type->isPointerTy() && type->getPointerElementType()->isIntegerTy(64); +} bool isVoidPtr(llvm::Type* type) { return type->isPointerTy() && type->getPointerElementType()->isIntegerTy(8); @@ -81,7 +83,7 @@ unsigned getStructSizeInBytes(llvm::Type* structT, const llvm::DataLayout& dl) { return layout->getSizeInBytes(); } -unsigned getPointerSizeInBytes(llvm::Type* ptrT, const llvm::DataLayout& dl) { +unsigned getPointerSizeInBytes(llvm::Type* /*ptrT*/, const llvm::DataLayout& dl) { return dl.getPointerSizeInBits() / 8; } @@ -113,6 +115,4 @@ unsigned getTypeSizeForArrayAlloc(llvm::AllocaInst* ai, const llvm::DataLayout& return bytes; } -} // namespace type -} // namespace util -} // namespace typeart +} // namespace typeart::util::type diff --git a/lib/support/TypeUtil.h b/lib/passes/support/TypeUtil.h similarity index 88% rename from lib/support/TypeUtil.h rename to lib/passes/support/TypeUtil.h index d689201d..5d0e8509 100644 --- a/lib/support/TypeUtil.h +++ b/lib/passes/support/TypeUtil.h @@ -8,9 +8,9 @@ class AllocaInst; class LLVMContext; } // namespace llvm -namespace typeart { -namespace util { -namespace type { +namespace typeart::util::type { + +bool isi64Ptr(llvm::Type* type); bool isVoidPtr(llvm::Type* type); @@ -34,8 +34,6 @@ unsigned getTypeSizeForArrayAlloc(llvm::AllocaInst* ai, const llvm::DataLayout& // bool compareTypes(llvm::Type* t1, llvm::Type* t2); -} // namespace type -} // namespace util -} // namespace typeart +} // namespace typeart::util::type #endif diff --git a/lib/support/Util.h b/lib/passes/support/Util.h similarity index 83% rename from lib/support/Util.h rename to lib/passes/support/Util.h index f94bb42c..887d3f32 100644 --- a/lib/support/Util.h +++ b/lib/passes/support/Util.h @@ -11,11 +11,11 @@ //#include "Logger.h" #include "llvm/Demangle/Demangle.h" +#include "llvm/IR/CallSite.h" #include "llvm/Support/Regex.h" #include "llvm/Support/raw_ostream.h" -namespace typeart { -namespace util { +namespace typeart::util { namespace detail { // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf : @@ -80,12 +80,28 @@ template inline std::string demangle(String&& s) { std::string name = s; auto demangle = llvm::itaniumDemangle(name.data(), nullptr, nullptr, nullptr); - if (demangle && std::string(demangle) != "") { + if (demangle && !std::string(demangle).empty()) { return std::string(demangle); } return name; } +template +inline std::string try_demangle(const T& site) { + if constexpr (std::is_same_v) { + if (site.isIndirectCall()) { + return ""; + } + return demangle(site.getCalledFunction()->getName()); + } else { + if constexpr (std::is_same_v) { + return demangle(site.getName()); + } else { + return demangle(site); + } + } +} + inline bool regex_matches(const std::string& regex, const std::string& in, bool case_sensitive = false) { using namespace llvm; Regex r(regex, !case_sensitive ? Regex::IgnoreCase : Regex::NoFlags); @@ -124,7 +140,7 @@ inline std::string glob2regex(const std::string& glob) { glob_reg += (in_curly > 0 ? "|" : ","); break; default: - if (strchr("()^$|*+.\\", c)) { + if (strchr("()^$|*+.\\", c) != nullptr) { glob_reg += '\\'; } glob_reg += c; @@ -135,7 +151,6 @@ inline std::string glob2regex(const std::string& glob) { return glob_reg; } -} // namespace util -} // namespace typeart +} // namespace typeart::util #endif /* LIB_UTIL_H_ */ diff --git a/lib/runtime/AccessCounter.h b/lib/runtime/AccessCounter.h new file mode 100644 index 00000000..68dca194 --- /dev/null +++ b/lib/runtime/AccessCounter.h @@ -0,0 +1,302 @@ +// +// Created by ahueck on 12.10.20. +// + +#ifndef TYPEART_ACCESSCOUNTER_H +#define TYPEART_ACCESSCOUNTER_H + +#include "RuntimeData.h" +#include "RuntimeInterface.h" +#include "support/Logger.h" +#include "support/Table.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace typeart { + +namespace softcounter { + +using Counter = long long int; + +class AccessRecorder { + public: + using TypeCountMap = std::unordered_map; + ~AccessRecorder() = default; + + inline void incHeapAlloc(int typeId, size_t count) { + ++curHeapAllocs; + ++heapAllocs; + if (count > 1) { + ++heapArray; + } + ++heapAlloc[typeId]; + } + + inline void incStackAlloc(int typeId, size_t count) { + ++curStackAllocs; + ++stackAllocs; + if (count > 1) { + ++stackArray; + } + ++stackAlloc[typeId]; + } + + inline void incGlobalAlloc(int typeId, size_t count) { + ++globalAllocs; + if (count > 1) { + ++globalArray; + } + ++globalAlloc[typeId]; + } + + inline void incStackFree(int typeId, size_t count) { + ++stackAllocsFree; + if (count > 1) { + ++stackArrayFree; + } + ++stackFree[typeId]; + } + + inline void incHeapFree(int typeId, size_t count) { + ++heapAllocsFree; + if (count > 1) { + ++heapArrayFree; + } + ++heapFree[typeId]; + } + + inline void decHeapAlloc() { + if (curHeapAllocs > maxHeapAllocs) { + maxHeapAllocs = curHeapAllocs; + } + --curHeapAllocs; + } + + inline void decStackAlloc(size_t amount) { + if (curStackAllocs > maxStackAllocs) { + maxStackAllocs = curStackAllocs; + } + curStackAllocs -= amount; + } + + inline void incUsedInRequest(MemAddr addr) { + ++addrChecked; + seen.insert(addr); + } + + inline void incAddrReuse() { + ++addrReuses; + } + + inline void incAddrMissing(MemAddr addr) { + ++addrMissing; + missing.insert(addr); + } + + inline void incNullAddr() { + ++nullAlloc; + } + + inline void incZeroLengthAddr() { + ++zeroAlloc; + } + + inline void incZeroLengthAndNullAddr() { + ++nullAndZeroAlloc; + } + + inline void incUDefTypes(size_t count) { + numUDefTypes += count; + } + + [[nodiscard]] static AccessRecorder& get() { + static AccessRecorder instance; + return instance; + } + + private: + AccessRecorder() = default; + AccessRecorder(AccessRecorder& other) = default; + AccessRecorder(AccessRecorder&& other) = default; + + Counter heapAllocs = 0; + Counter stackAllocs = 0; + Counter globalAllocs = 0; + Counter maxHeapAllocs = 0; + Counter maxStackAllocs = 0; + Counter curHeapAllocs = 0; + Counter curStackAllocs = 0; + Counter addrReuses = 0; + Counter addrMissing = 0; + Counter addrChecked = 0; + Counter stackArray = 0; + Counter heapArray = 0; + Counter globalArray = 0; + Counter stackAllocsFree = 0; + Counter stackArrayFree = 0; + Counter heapAllocsFree = 0; + Counter heapArrayFree = 0; + Counter nullAlloc = 0; + Counter zeroAlloc = 0; + Counter nullAndZeroAlloc = 0; + Counter numUDefTypes = 0; + std::unordered_set missing; + std::unordered_set seen; + TypeCountMap stackAlloc; + TypeCountMap heapAlloc; + TypeCountMap globalAlloc; + TypeCountMap stackFree; + TypeCountMap heapFree; + + template + friend void serialise(const Recorder& r, llvm::raw_ostream& buf); +}; + +/** + * Used for no-operations in counter methods when not using softcounters. + */ +class NoneRecorder { + public: + [[maybe_unused]] inline void incHeapAlloc(int, size_t) { + } + [[maybe_unused]] inline void incStackAlloc(int, size_t) { + } + [[maybe_unused]] inline void incGlobalAlloc(int, size_t) { + } + [[maybe_unused]] inline void incUsedInRequest(MemAddr) { + } + [[maybe_unused]] inline void decHeapAlloc() { + } + [[maybe_unused]] inline void decStackAlloc(size_t) { + } + [[maybe_unused]] inline void incAddrReuse() { + } + [[maybe_unused]] inline void incAddrMissing(MemAddr) { + } + [[maybe_unused]] inline void incStackFree(int, size_t) { + } + [[maybe_unused]] inline void incHeapFree(int, size_t) { + } + [[maybe_unused]] inline void incNullAddr() { + } + [[maybe_unused]] inline void incZeroLengthAddr() { + } + [[maybe_unused]] inline void incZeroLengthAndNullAddr() { + } + [[maybe_unused]] inline void incUDefTypes(size_t count) { + } + + static NoneRecorder& get() { + static NoneRecorder instance; + return instance; + } +}; + +namespace memory { +struct MemOverhead { + static constexpr auto pointerMapSize = sizeof(RuntimeT::PointerMap); // Map overhead + static constexpr auto perNodeSizeMap = + sizeof(std::remove_pointer::iterator::_Link_type>::type) + + sizeof(RuntimeT::MapEntry); // not applicable to btree + static constexpr auto stackVectorSize = sizeof(RuntimeT::Stack); // Stack overhead + static constexpr auto perNodeSizeStack = sizeof(RuntimeT::StackEntry); // Stack allocs + double stack{0}; + double map{0}; +}; +inline MemOverhead estimate(Counter stack_max, Counter heap_max, Counter global_max, const double scale = 1024.0) { + MemOverhead mem; + mem.stack = double(MemOverhead::stackVectorSize + + MemOverhead::perNodeSizeStack * std::max(RuntimeT::StackReserve, stack_max)) / + scale; + mem.map = + double(MemOverhead::pointerMapSize + MemOverhead::perNodeSizeMap * (stack_max + heap_max + global_max)) / scale; + return mem; +} +} // namespace memory + +template +void serialise(const Recorder& r, llvm::raw_ostream& buf) { + if constexpr (std::is_same_v) { + return; + } else { + const auto memory_use = memory::estimate(r.maxStackAllocs, r.maxHeapAllocs, r.globalAllocs); + + Table t("Alloc Stats from softcounters"); + t.wrap_length = true; + t.put(Row::make("Total heap", r.heapAllocs, r.heapArray)); + t.put(Row::make("Total stack", r.stackAllocs, r.stackArray)); + t.put(Row::make("Total global", r.globalAllocs, r.globalArray)); + t.put(Row::make("Max. Heap Allocs", r.maxHeapAllocs)); + t.put(Row::make("Max. Stack Allocs", r.maxStackAllocs)); + t.put(Row::make("Addresses checked", r.addrChecked)); + t.put(Row::make("Distinct Addresses checked", r.seen.size())); + t.put(Row::make("Addresses re-used", r.addrReuses)); + t.put(Row::make("Addresses missed", r.addrMissing)); + t.put(Row::make("Distinct Addresses missed", r.missing.size())); + t.put(Row::make("Total free heap", r.heapAllocsFree, r.heapArrayFree)); + t.put(Row::make("Total free stack", r.stackAllocsFree, r.stackArrayFree)); + t.put(Row::make("Null/Zero/NullZero Addr", r.nullAlloc, r.zeroAlloc, r.nullAndZeroAlloc)); + t.put(Row::make("User-def. types", r.numUDefTypes)); + t.put(Row::make("Estimated memory use (KiB)", size_t(std::round(memory_use.map + memory_use.stack)))); + t.put(Row::make("Bytes per node map/stack", memory::MemOverhead::perNodeSizeMap, + memory::MemOverhead::perNodeSizeStack)); + + t.print(buf); + + std::set type_id_set; + const auto fill_set = [&type_id_set](const auto& map) { + for (const auto& [key, val] : map) { + type_id_set.insert(key); + } + }; + fill_set(r.heapAlloc); + fill_set(r.globalAlloc); + fill_set(r.stackAlloc); + fill_set(r.heapFree); + fill_set(r.stackFree); + + const auto count = [](const auto& map, auto id) { + auto it = map.find(id); + if (it != map.end()) { + return it->second; + } + return 0ll; + }; + + Table type_table("Allocation type detail (heap, stack, global)"); + type_table.table_header = '#'; + for (auto type_id : type_id_set) { + type_table.put(Row::make(std::to_string(type_id), count(r.heapAlloc, type_id), count(r.stackAlloc, type_id), + count(r.globalAlloc, type_id), typeart_get_type_name(type_id))); + } + + type_table.print(buf); + + Table type_table_free("Free allocation type detail (heap, stack)"); + type_table_free.table_header = '#'; + for (auto type_id : type_id_set) { + type_table_free.put(Row::make(std::to_string(type_id), count(r.heapFree, type_id), count(r.stackFree, type_id), + typeart_get_type_name(type_id))); + } + + type_table_free.print(buf); + } +} + +} // namespace softcounter + +#if ENABLE_SOFTCOUNTER == 1 +using Recorder = softcounter::AccessRecorder; +#else +using Recorder = softcounter::NoneRecorder; +#endif + +} // namespace typeart + +#endif // TYPEART_ACCESSCOUNTER_H diff --git a/runtime/CMakeLists.txt b/lib/runtime/CMakeLists.txt similarity index 82% rename from runtime/CMakeLists.txt rename to lib/runtime/CMakeLists.txt index d42697e9..7a2b376f 100644 --- a/runtime/CMakeLists.txt +++ b/lib/runtime/CMakeLists.txt @@ -3,10 +3,14 @@ set(TARGETS_EXPORT_NAME ${PROJECT_NAME}-targets) set(RUNTIME_LIB_SOURCES Runtime.cpp + AccessCounter.h + CallbackInterface.h + RuntimeData.h + RuntimeInterface.h ) if(MPI_LOGGER) - list(APPEND RUNTIME_LIB_SOURCES MPILogger.cpp) + list(APPEND RUNTIME_LIB_SOURCES ../support/MPILogger.cpp) endif() add_library(typeart-rt SHARED @@ -56,11 +60,17 @@ target_link_libraries(typeart-rt PUBLIC target_include_directories(typeart-rt PUBLIC - $ - $ + $ + $ + $ $ ) +target_include_directories(typeart-rt + PRIVATE + $ + ) + target_include_directories(typeart-rt SYSTEM PRIVATE @@ -75,6 +85,8 @@ target_project_compile_definitions(typeart-rt target_project_compile_definitions(typeart-rt) target_define_file_basename(typeart-rt) +target_project_coverage_options(typeart-rt) + target_link_libraries(typeart-rt PUBLIC LLVMCore LLVMSupport diff --git a/lib/runtime/CallbackInterface.h b/lib/runtime/CallbackInterface.h new file mode 100644 index 00000000..44f3a545 --- /dev/null +++ b/lib/runtime/CallbackInterface.h @@ -0,0 +1,30 @@ +// +// Created by ahueck on 16.10.20. +// + +#ifndef TYPEART_CALLBACKINTERFACE_H +#define TYPEART_CALLBACKINTERFACE_H + +#ifdef __cplusplus +#include +#else +#include +#endif + +// Callback function signatures invoked by the LLVM pass +#ifdef __cplusplus +extern "C" { +#endif +void __typeart_alloc(const void* addr, int typeId, size_t count); + +void __typeart_alloc_global(const void* addr, int typeId, size_t count); +void __typeart_free(const void* addr); + +void __typeart_alloc_stack(const void* addr, int typeId, size_t count); +void __typeart_leave_scope(size_t alloca_count); + +#ifdef __cplusplus +} +#endif + +#endif // TYPEART_CALLBACKINTERFACE_H diff --git a/runtime/Runtime.cpp b/lib/runtime/Runtime.cpp similarity index 65% rename from runtime/Runtime.cpp rename to lib/runtime/Runtime.cpp index f3988e28..5b853981 100644 --- a/runtime/Runtime.cpp +++ b/lib/runtime/Runtime.cpp @@ -1,23 +1,25 @@ #include "Runtime.h" -#include "Logger.h" +#include "AccessCounter.h" #include "RuntimeInterface.h" -#include "TypeIO.h" +#include "support/Logger.h" +#include "typelib/TypeIO.h" #include "llvm/ADT/Optional.h" #include #include #include -#include -#include +#include #include -#include #ifdef USE_BTREE using namespace btree; #endif +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + namespace typeart { /** * Ensures that memory tracking functions do not come from within the runtime. @@ -34,186 +36,90 @@ static bool typeart_rt_scope{false}; #define RUNTIME_GUARD_END typeart::typeart_rt_scope = false namespace typeart { -namespace softcounter { -/** - * Very basic implementation of some couting infrastructure. - * This implementation counts: - * - the number of objects hold maximally in the datastructures for stack and heap. - * - the total number of tracked allocations (counting multiple insertions of the same address as multiple tracked - * values) for both stack and heap. - * - the number of distinct addresses queried for information - * - the number of addresses re-used (according to our type map) - * In addition it estimates (lower-bound) the consumed memory for tracking the type information. - * - * It prints the information during object de-construction. - */ -class AccessRecorder { - public: - ~AccessRecorder() { - printStats(); - } - - inline void incHeapAlloc() { - ++curHeapAllocs; - ++heapAllocs; - } - inline void incStackAlloc() { - ++curStackAllocs; - ++stackAllocs; - } +template +inline const void* addByteOffset(const void* addr, T offset) { + return static_cast(static_cast(addr) + offset); +} - inline void incGlobalAlloc() { - ++globalAllocs; - } +namespace debug { +inline static std::string toString(const void* memAddr, int typeId, size_t count, size_t typeSize, + const void* calledFrom) { + std::string buf; + llvm::raw_string_ostream s(buf); + const auto name = TypeArtRT::get().getTypeName(typeId); + s << memAddr << " " << typeId << " " << name << " " << typeSize << " " << count << " (" << calledFrom << ")"; + return s.str(); +} - inline void decHeapAlloc() { - if (curHeapAllocs > maxHeapAllocs) { - maxHeapAllocs = curHeapAllocs; - } - --curHeapAllocs; - } +inline static std::string toString(const void* memAddr, int typeId, size_t count, const void* calledFrom) { + const auto typeSize = TypeArtRT::get().getTypeSize(typeId); + return toString(memAddr, typeId, count, typeSize, calledFrom); +} - inline void decStackAlloc(size_t amount) { - if (curStackAllocs > maxStackAllocs) { - maxStackAllocs = curStackAllocs; - } - curStackAllocs -= amount; - } - - inline void incUsedInRequest(const void* addr) { - ++addrChecked; - seen.insert(addr); - } - - inline void incAddrReuse() { - ++addrReuses; - } - - inline void incAddrMissing(const void* addr) { - ++addrMissing; - missing.insert(addr); - } - - void printStats() const { - std::string s; - llvm::raw_string_ostream buf(s); - auto estMemConsumption = (maxHeapAllocs + maxStackAllocs) * memPerEntry; - estMemConsumption += (maxStackAllocs * memInStack); - estMemConsumption += (vectorSize + mapSize); - auto estMemConsumptionKByte = estMemConsumption / 1024.0; - - const auto getStr = [&](const auto memConsKB) { - auto memStr = std::to_string(memConsKB); - return memStr.substr(0, memStr.find('.') + 2); - }; - - buf << "------------\nAlloc Stats from softcounters\n" - << "Total Calls .onAlloc [heap]:\t" << heapAllocs << "\n" - << "Total Calls .onAlloc [stack]:\t" << stackAllocs << "\n" - << "Total Calls .onAlloc [global]:\t" << globalAllocs << "\n" - << "Max. Heap Allocs:\t\t" << maxHeapAllocs << "\n" - << "Max. Stack Allocs:\t\t" << maxStackAllocs << "\n" - << "Addresses re-used:\t\t" << addrReuses << "\n" - << "Addresses missed:\t\t" << addrMissing << "\n" - << "Distinct Addresses checked:\t" << seen.size() << "\n" - << "Addresses checked:\t\t" << addrChecked << "\n" - << "Distinct Addresses missed:\t" << missing.size() << "\n" - << "Estimated mem consumption:\t" << estMemConsumption << " bytes = " << getStr(estMemConsumptionKByte) - << " kiB\n" - << "vector overhead: " << vectorSize << " bytes\tmap overhead: " << mapSize << " bytes\n"; - LOG_MSG(buf.str()); - } - - static AccessRecorder& get() { - static AccessRecorder instance; - return instance; - } - - private: - AccessRecorder() = default; - AccessRecorder(AccessRecorder& other) = default; - AccessRecorder(AccessRecorder&& other) = default; - - const int memPerEntry = sizeof(PointerInfo) + sizeof(void*); // Type-map key + value - const int memInStack = sizeof(void*); // Stack allocs - const int vectorSize = sizeof(TypeArtRT::Stack); // Stack overhead - const int mapSize = sizeof(TypeArtRT::PointerMap); // Map overhead - long long heapAllocs = 0; - long long stackAllocs = 0; - long long globalAllocs = 0; - long long maxHeapAllocs = 0; - long long maxStackAllocs = 0; - long long curHeapAllocs = 0; - long long curStackAllocs = 0; - long long addrReuses = 0; - long long addrMissing = 0; - long long addrChecked = 0; - std::unordered_set missing; - std::unordered_set seen; -}; +inline static std::string toString(const void* addr, const PointerInfo& info) { + return toString(addr, info.typeId, info.count, info.debug); +} -/** - * Used for no-operations in counter methods when not using softcounters. - */ -class NoneRecorder { - public: - inline void incHeapAlloc() { - } - inline void incStackAlloc() { - } - inline void incGlobalAlloc() { - } - inline void incUsedInRequest(const void*) { - } - inline void decHeapAlloc() { - } - inline void decStackAlloc(size_t) { - } - inline void incAddrReuse() { - } - inline void incAddrMissing(const void*) { +void printTraceStart() { + LOG_TRACE("TypeART Runtime Trace"); + LOG_TRACE("*********************"); + LOG_TRACE("Operation Address Type Size Count (CallAddr) Stack/Heap/Global"); + LOG_TRACE("-------------------------------------------------------------------------"); +} +} // namespace debug + +namespace detail { +template +constexpr std::false_type always_false{}; +} // namespace detail + +template +inline Enum operator|(Enum lhs, Enum rhs) { + if constexpr (std::is_enum_v && + (std::is_same_v || std::is_same_v)) { + using enum_t = typename std::underlying_type::type; + return static_cast(static_cast(lhs) | static_cast(rhs)); + } else { + static_assert(detail::always_false); } - inline void printStats() const { +} +template +inline void operator|=(Enum& lhs, Enum rhs) { + if constexpr (std::is_enum_v && + (std::is_same_v || std::is_same_v)) { + lhs = lhs | rhs; + } else { + static_assert(detail::always_false); } +} - static NoneRecorder& get() { - static NoneRecorder instance; - return instance; +template +inline Enum operator&(Enum lhs, Enum rhs) { + if constexpr (std::is_enum_v && std::is_same_v) { + using enum_t = typename std::underlying_type::type; + return static_cast(static_cast(lhs) & static_cast(rhs)); + } else { + static_assert(detail::always_false); } -}; -} // namespace softcounter - -#if ENABLE_SOFTCOUNTER == 1 -using Recorder = softcounter::AccessRecorder; -#else -using Recorder = softcounter::NoneRecorder; -#endif - -std::string TypeArtRT::defaultTypeFileName{"types.yaml"}; - -template -inline const void* addByteOffset(const void* addr, T offset) { - return static_cast(static_cast(addr) + offset); } -inline static std::string toString(const void* addr, int typeId, size_t count, size_t typeSize) { - std::stringstream s; - // clang-format off - s << addr - << ". typeId: " << typeId << " (" << TypeArtRT::get().getTypeName(typeId) << ")" - << ". count: " << count - << ". typeSize " << typeSize; - // clang-format on - return s.str(); +template +inline typename std::underlying_type::type operator==(Enum lhs, Enum rhs) { + if constexpr (std::is_enum_v && std::is_same_v) { + using enum_t = typename std::underlying_type::type; + return static_cast(lhs) & static_cast(rhs); + } else { + static_assert(detail::always_false); + } } -inline static std::string toString(const void* addr, const PointerInfo& info) { - auto typeSize = TypeArtRT::get().getTypeSize(info.typeId); - return toString(addr, info.typeId, info.count, typeSize); -} +using namespace debug; TypeArtRT::TypeArtRT() { + // This keeps the destruction order: 1. RT 2. Recorder + auto& recorder = Recorder::get(); + // Try to load types from specified file first. // Then look at default location. const char* typeFile = std::getenv("TA_TYPE_FILE"); @@ -232,14 +138,26 @@ TypeArtRT::TypeArtRT() { } std::stringstream ss; - for (const auto& structInfo : typeDB.getStructList()) { + const auto& typeList = typeDB.getStructList(); + for (const auto& structInfo : typeList) { ss << structInfo.name << ", "; } + recorder.incUDefTypes(typeList.size()); LOG_INFO("Recorded types: " << ss.str()); - stackVars.reserve(1024); + stackVars.reserve(RuntimeT::StackReserve); - printTraceStart(); + debug::printTraceStart(); +} + +TypeArtRT::~TypeArtRT() { + std::string stats; + llvm::raw_string_ostream stream(stats); + softcounter::serialise(Recorder::get(), stream); + if (!stream.str().empty()) { + // llvm::errs/LOG will crash with virtual call error + std::cerr << stream.str(); + } } bool TypeArtRT::loadTypes(const std::string& file) { @@ -247,14 +165,7 @@ bool TypeArtRT::loadTypes(const std::string& file) { return cio.load(file); } -void TypeArtRT::printTraceStart() const { - LOG_TRACE("TypeART Runtime Trace"); - LOG_TRACE("**************************"); - LOG_TRACE("Operation Address Type Size Count Stack/Heap/Global"); - LOG_TRACE("-----------------------------------------------------------"); -} - -llvm::Optional TypeArtRT::findBaseAddress(const void* addr) const { +llvm::Optional TypeArtRT::findBaseAddress(const void* addr) const { if (typeMap.empty() || addr < typeMap.begin()->first) { return llvm::None; } @@ -518,77 +429,113 @@ void TypeArtRT::getReturnAddress(const void* addr, const void** retAddr) const { } } -void TypeArtRT::doAlloc(const void* addr, int typeId, size_t count, const void* retAddr, const char reg) { - if (!typeDB.isValid(typeId)) { - LOG_ERROR("Allocation of unknown type (id=" << typeId << ") recorded at " << addr << " [" << reg - << "], called from " << retAddr); +TypeArtRT::AllocState TypeArtRT::doAlloc(const void* addr, int typeId, size_t count, const void* retAddr) { + AllocState status = AllocState::NO_INIT; + if (unlikely(!typeDB.isValid(typeId))) { + status |= AllocState::UNKNOWN_ID; + LOG_ERROR("Allocation of unknown type " << toString(addr, typeId, count, retAddr)); } // Calling malloc with size 0 may return a nullptr or some address that can not be written to. // In the second case, the allocation is tracked anyway so that onFree() does not report an error. // On the other hand, an allocation on address 0x0 with size > 0 is an actual error. - if (count == 0) { - LOG_WARNING("Zero-size allocation (id=" << typeId << ") recorded at " << addr << " [" << reg << "], called from " - << retAddr); - + if (unlikely(count == 0)) { + Recorder::get().incZeroLengthAddr(); + status |= AllocState::ZERO_COUNT; + LOG_WARNING("Zero-size allocation " << toString(addr, typeId, count, retAddr)); if (addr == nullptr) { - return; + Recorder::get().incZeroLengthAndNullAddr(); + LOG_ERROR("Zero-size and nullptr allocation " << toString(addr, typeId, count, retAddr)); + return status | AllocState::NULL_ZERO | AllocState::ADDR_SKIPPED; } - } else if (addr == nullptr) { - LOG_ERROR("Nullptr allocation (id=" << typeId << ") recorded at " << addr << " [" << reg << "], called from " - << retAddr); - return; + } else if (unlikely(addr == nullptr)) { + Recorder::get().incNullAddr(); + LOG_ERROR("Zero-size allocation " << toString(addr, typeId, count, retAddr)); + return status | AllocState::NULL_PTR | AllocState::ADDR_SKIPPED; } auto& def = typeMap[addr]; - if (def.typeId == -1) { - LOG_TRACE("Alloc " << addr << " " << typeDB.getTypeName(typeId) << " " << typeDB.getTypeSize(typeId) << " " << count - << " " << reg); - } else { + if (unlikely(def.typeId != -1)) { typeart::Recorder::get().incAddrReuse(); - if (reg == 'G' || reg == 'H') { - LOG_ERROR("Already exists (" << retAddr << ", prev=" << def.debug - << "): " << toString(addr, typeId, count, typeDB.getTypeSize(typeId))); - LOG_ERROR("Data in map is: " << toString(addr, def)); - } + status |= AllocState::ADDR_REUSE; + LOG_WARNING("Pointer already in map " << toString(addr, typeId, count, retAddr)); + LOG_WARNING("Overriden data in map " << toString(addr, def)); } def.typeId = typeId; def.count = count; def.debug = retAddr; + + return status | AllocState::OK; } void TypeArtRT::onAlloc(const void* addr, int typeId, size_t count, const void* retAddr) { - doAlloc(addr, typeId, count, retAddr); + const auto status = doAlloc(addr, typeId, count, retAddr); + if (status != AllocState::ADDR_SKIPPED) { + typeart::Recorder::get().incHeapAlloc(typeId, count); + } + LOG_TRACE("Alloc " << toString(addr, typeId, count, retAddr) << " " << 'H'); } void TypeArtRT::onAllocStack(const void* addr, int typeId, size_t count, const void* retAddr) { - doAlloc(addr, typeId, count, retAddr, 'S'); - stackVars.push_back(addr); + const auto status = doAlloc(addr, typeId, count, retAddr); + if (status != AllocState::ADDR_SKIPPED) { + stackVars.push_back(addr); + typeart::Recorder::get().incStackAlloc(typeId, count); + } + LOG_TRACE("Alloc " << toString(addr, typeId, count, retAddr) << " " << 'S'); } void TypeArtRT::onAllocGlobal(const void* addr, int typeId, size_t count, const void* retAddr) { - doAlloc(addr, typeId, count, retAddr, 'G'); + const auto status = doAlloc(addr, typeId, count, retAddr); + if (status != AllocState::ADDR_SKIPPED) { + typeart::Recorder::get().incGlobalAlloc(typeId, count); + } + LOG_TRACE("Alloc " << toString(addr, typeId, count, retAddr) << " " << 'G'); } -template -void TypeArtRT::onFree(const void* addr, const void* retAddr) { - if (!isStack && addr == nullptr) { - LOG_INFO("Recorded free on nullptr, called from " << retAddr); - return; +template +TypeArtRT::FreeState TypeArtRT::doFree(const void* addr, const void* retAddr) { + if (unlikely(addr == nullptr)) { + LOG_ERROR("Free on nullptr " + << "(" << retAddr << ")"); + return FreeState::ADDR_SKIPPED | FreeState::NULL_PTR; } - auto it = typeMap.find(addr); - if (it != typeMap.end()) { + + const auto it = typeMap.find(addr); + + if (likely(it != typeMap.end())) { LOG_TRACE("Free " << toString((*it).first, (*it).second)); + + if constexpr (!std::is_same_v) { + const auto typeId = it->second.typeId; + const auto count = it->second.count; + if (stack) { + Recorder::get().incStackFree(typeId, count); + } else { + Recorder::get().incHeapFree(typeId, count); + } + } + typeMap.erase(it); - } else if (!isStack) { - LOG_ERROR("Free recorded on unregistered address " << addr << ", called from " << retAddr); + } else { + LOG_ERROR("Free on unregistered address " << addr << " (" << retAddr << ")"); + return FreeState::ADDR_SKIPPED | FreeState::UNREG_ADDR; + } + + return FreeState::OK; +} + +void TypeArtRT::onFreeHeap(const void* addr, const void* retAddr) { + const auto status = doFree(addr, retAddr); + if (FreeState::OK == status) { + typeart::Recorder::get().decHeapAlloc(); } } void TypeArtRT::onLeaveScope(size_t alloca_count, const void* retAddr) { - if (alloca_count > stackVars.size()) { + if (unlikely(alloca_count > stackVars.size())) { LOG_ERROR("Stack is smaller than requested de-allocation count. alloca_count: " << alloca_count << ". size: " << stackVars.size()); alloca_count = stackVars.size(); @@ -597,39 +544,39 @@ void TypeArtRT::onLeaveScope(size_t alloca_count, const void* retAddr) { const auto cend = stackVars.cend(); const auto start_pos = (cend - alloca_count); LOG_TRACE("Freeing stack (" << alloca_count << ") " << std::distance(start_pos, stackVars.cend())) - std::for_each(start_pos, cend, [&](const void* addr) { onFree(addr, retAddr); }); + std::for_each(start_pos, cend, [this, &retAddr](const void* addr) { doFree(addr, retAddr); }); stackVars.erase(start_pos, cend); + typeart::Recorder::get().decStackAlloc(alloca_count); LOG_TRACE("Stack after free: " << stackVars.size()); } } // namespace typeart -void __typeart_alloc(void* addr, int typeId, size_t count) { +void __typeart_alloc(const void* addr, int typeId, size_t count) { RUNTIME_GUARD_BEGIN; const void* retAddr = __builtin_return_address(0); typeart::TypeArtRT::get().onAlloc(addr, typeId, count, retAddr); RUNTIME_GUARD_END; } -void __typeart_alloc_stack(void* addr, int typeId, size_t count) { +void __typeart_alloc_stack(const void* addr, int typeId, size_t count) { RUNTIME_GUARD_BEGIN; const void* retAddr = __builtin_return_address(0); typeart::TypeArtRT::get().onAllocStack(addr, typeId, count, retAddr); RUNTIME_GUARD_END; } -void __typeart_alloc_global(void* addr, int typeId, size_t count) { +void __typeart_alloc_global(const void* addr, int typeId, size_t count) { RUNTIME_GUARD_BEGIN; const void* retAddr = __builtin_return_address(0); typeart::TypeArtRT::get().onAllocGlobal(addr, typeId, count, retAddr); RUNTIME_GUARD_END; } -void __typeart_free(void* addr) { +void __typeart_free(const void* addr) { RUNTIME_GUARD_BEGIN; const void* retAddr = __builtin_return_address(0); - typeart::TypeArtRT::get().onFree(addr, retAddr); - typeart::Recorder::get().decHeapAlloc(); + typeart::TypeArtRT::get().onFreeHeap(addr, retAddr); RUNTIME_GUARD_END; } @@ -637,7 +584,6 @@ void __typeart_leave_scope(size_t alloca_count) { RUNTIME_GUARD_BEGIN; const void* retAddr = __builtin_return_address(0); typeart::TypeArtRT::get().onLeaveScope(alloca_count, retAddr); - typeart::Recorder::get().decStackAlloc(alloca_count); RUNTIME_GUARD_END; } @@ -646,8 +592,9 @@ typeart_status typeart_get_builtin_type(const void* addr, typeart::BuiltinType* } typeart_status typeart_get_type(const void* addr, int* type, size_t* count) { + typeart_status_t status = typeart::TypeArtRT::get().getTypeInfo(addr, type, count); typeart::Recorder::get().incUsedInRequest(addr); - return typeart::TypeArtRT::get().getTypeInfo(addr, type, count); + return status; } typeart_status typeart_get_containing_type(const void* addr, int* type, size_t* count, const void** base_address, diff --git a/runtime/Runtime.h b/lib/runtime/Runtime.h similarity index 81% rename from runtime/Runtime.h rename to lib/runtime/Runtime.h index 210f1f1d..6fa4444c 100644 --- a/runtime/Runtime.h +++ b/lib/runtime/Runtime.h @@ -1,37 +1,13 @@ #ifndef RUNTIME_RUNTIME_H_ #define RUNTIME_RUNTIME_H_ +#include "CallbackInterface.h" +#include "RuntimeData.h" #include "RuntimeInterface.h" -#include "TypeDB.h" - -#ifdef USE_BTREE -#ifdef USE_ABSL -#error TypeART-RT: Set BTREE and ABSL, mutually exclusive. -#endif -#include "btree_map.h" -#endif - -#ifdef USE_ABSL -#ifdef USE_BTREE -#error TypeART-RT: Set ABSL and BTREE, mutually exclusive. -#endif -#include "absl/container/btree_map.h" -#endif - -#if !defined(USE_BTREE) && !defined(USE_ABSL) -#include -#endif +#include "typelib/TypeDB.h" #include -extern "C" { -void __typeart_alloc(void* addr, int typeId, size_t count); -void __typeart_alloc_stack(void* addr, int typeId, size_t count); -void __typeart_alloc_global(void* addr, int typeId, size_t count); -void __typeart_free(void* addr); -void __typeart_leave_scope(size_t alloca_count); -} - namespace llvm { template class Optional; @@ -39,26 +15,34 @@ class Optional; namespace typeart { -struct PointerInfo final { - int typeId{-1}; - size_t count{0}; - const void* debug{nullptr}; -}; - class TypeArtRT final { + RuntimeT::PointerMap typeMap; + RuntimeT::Stack stackVars; + TypeDB typeDB; + + static constexpr const char* defaultTypeFileName = "types.yaml"; + public: + enum class AllocState : unsigned { + NO_INIT = 1 << 0, + OK = 1 << 1, + ADDR_SKIPPED = 1 << 2, + NULL_PTR = 1 << 3, + ZERO_COUNT = 1 << 4, + NULL_ZERO = 1 << 5, + ADDR_REUSE = 1 << 6, + UNKNOWN_ID = 1 << 7 + }; + + enum class FreeState : unsigned { + NO_INIT = 1 << 0, + OK = 1 << 1, + ADDR_SKIPPED = 1 << 2, + NULL_PTR = 1 << 3, + UNREG_ADDR = 1 << 4, + }; + using TypeArtStatus = typeart_status; - using Stack = std::vector; -#ifdef USE_BTREE - using PointerMap = btree::btree_map; -#endif -#ifdef USE_ABSL - using PointerMap = absl::btree_map; -#endif -#if !defined(USE_BTREE) && !defined(USE_ABSL) - using PointerMap = std::map; -#endif - using MapEntry = PointerMap::value_type; static TypeArtRT& get() { static TypeArtRT instance; @@ -176,13 +160,13 @@ class TypeArtRT final { void onAllocGlobal(const void* addr, int typeID, size_t count, const void* retAddr); - template - void onFree(const void* addr, const void* retAddr); + void onFreeHeap(const void* addr, const void* retAddr); void onLeaveScope(size_t alloca_count, const void* retAddr); private: TypeArtRT(); + ~TypeArtRT(); /** * If a given address points inside a known struct, this method is used to recursively resolve the exact member type. @@ -197,27 +181,21 @@ class TypeArtRT final { */ size_t getMemberIndex(typeart_struct_layout structInfo, size_t offset) const; - void printTraceStart() const; - /** * Loads the type file created by the LLVM pass. */ bool loadTypes(const std::string& file); - inline void doAlloc(const void* addr, int typeID, size_t count, const void* retAddr, const char reg = 'H'); + inline AllocState doAlloc(const void* addr, int typeID, size_t count, const void* retAddr); + + template + inline FreeState doFree(const void* addr, const void* retAddr); /** * Given an address, this method searches for the pointer that corresponds to the start of the allocated block. * Returns null if the memory location is not registered as allocated. */ - llvm::Optional findBaseAddress(const void* addr) const; - - // Class members - PointerMap typeMap; - Stack stackVars; - TypeDB typeDB; - - static std::string defaultTypeFileName; + llvm::Optional findBaseAddress(const void* addr) const; }; } // namespace typeart diff --git a/lib/runtime/RuntimeData.h b/lib/runtime/RuntimeData.h new file mode 100644 index 00000000..e59fd482 --- /dev/null +++ b/lib/runtime/RuntimeData.h @@ -0,0 +1,59 @@ +// +// Created by ahueck on 12.10.20. +// + +#ifndef TYPEART_RUNTIMEDATA_H +#define TYPEART_RUNTIMEDATA_H + +#ifdef USE_BTREE +#ifdef USE_ABSL +#error TypeART-RT: Set BTREE and ABSL, mutually exclusive. +#endif +#include "btree_map.h" +#endif + +#ifdef USE_ABSL +#ifdef USE_BTREE +#error TypeART-RT: Set ABSL and BTREE, mutually exclusive. +#endif +#include "absl/container/btree_map.h" +#endif + +#if !defined(USE_BTREE) && !defined(USE_ABSL) +#include +#endif + +namespace typeart { + +using MemAddr = const void*; + +struct PointerInfo final { + int typeId{-1}; + size_t count{0}; + MemAddr debug{nullptr}; +}; + +struct RuntimeT { + using Stack = std::vector; + static constexpr auto StackReserve{512U}; + static constexpr char StackName[] = "std::vector"; +#ifdef USE_BTREE + using PointerMap = btree::btree_map; + static constexpr char MapName[] = "btree::btree_map"; +#endif +#ifdef USE_ABSL + using PointerMap = absl::btree_map; + static constexpr char MapName[] = "absl::btree_map"; +#endif +#if !defined(USE_BTREE) && !defined(USE_ABSL) + using PointerMap = std::map; + static constexpr char MapName[] = "std::map"; +#endif + using MapEntry = PointerMap::value_type; + using MapKey = PointerMap::key_type; + using StackEntry = Stack::value_type; +}; + +} // namespace typeart + +#endif // TYPEART_RUNTIMEDATA_H diff --git a/runtime/RuntimeInterface.h b/lib/runtime/RuntimeInterface.h similarity index 100% rename from runtime/RuntimeInterface.h rename to lib/runtime/RuntimeInterface.h diff --git a/lib/support/Logger.h b/lib/support/Logger.h index 9c82c822..2668dffe 100644 --- a/lib/support/Logger.h +++ b/lib/support/Logger.h @@ -22,7 +22,29 @@ #define LOG_BASENAME_FILE __FILE__ #endif +#ifndef MPI_LOGGER +#define MPI_LOGGER 0 +#endif + // clang-format off +#if MPI_LOGGER +void mpi_log(const std::string& msg); +#define OO_LOG_LEVEL_MSG(LEVEL_NUM, LEVEL, MSG) \ + if ((LEVEL_NUM) <= LOG_LEVEL) { \ + std::string s; \ + llvm::raw_string_ostream rso(s); \ + rso << (LEVEL) << LOG_BASENAME_FILE << ":" << __func__ << ":" << __LINE__ << ":" << MSG << "\n"; /* NOLINT */ \ + mpi_log(rso.str()); \ + } + +#define OO_LOG_LEVEL_MSG_BARE(LEVEL_NUM, LEVEL, MSG) \ + if ((LEVEL_NUM) <= LOG_LEVEL) { \ + std::string s; \ + llvm::raw_string_ostream rso(s); \ + rso << (LEVEL) << " " << MSG << "\n"; /* NOLINT */ \ + mpi_log(rso.str()); \ + } +#else #define OO_LOG_LEVEL_MSG(LEVEL_NUM, LEVEL, MSG) \ if ((LEVEL_NUM) <= LOG_LEVEL) { \ llvm::errs() << (LEVEL) << " " << LOG_BASENAME_FILE << ":" << __func__ << ":" << __LINE__ << ": " << MSG << "\n"; /* NOLINT */ \ @@ -32,7 +54,9 @@ if ((LEVEL_NUM) <= LOG_LEVEL) { \ llvm::errs() << (LEVEL) << " " << MSG << "\n"; /* NOLINT */ \ } +#endif // clang-format on + #define LOG_TRACE(MSG) OO_LOG_LEVEL_MSG_BARE(3, "[Trace]", MSG) #define LOG_DEBUG(MSG) OO_LOG_LEVEL_MSG(3, "[Debug]", MSG) #define LOG_INFO(MSG) OO_LOG_LEVEL_MSG(2, "[Info]", MSG) diff --git a/runtime/MPILogger.cpp b/lib/support/MPILogger.cpp similarity index 94% rename from runtime/MPILogger.cpp rename to lib/support/MPILogger.cpp index 4d65bc40..708e090e 100644 --- a/runtime/MPILogger.cpp +++ b/lib/support/MPILogger.cpp @@ -12,7 +12,7 @@ inline std::vector getRanks() { ranks.push_back(0); return ranks; } else if (strncmp(rStr, "all", 3) == 0) { - return {}; + return ranks; } ranks.push_back(std::atoi(rStr)); @@ -25,7 +25,7 @@ void mpi_log(const std::string& msg) { MPI_Initialized(&initFlag); MPI_Finalized(&finiFlag); - if (initFlag && !finiFlag) { + if (initFlag != 0 && finiFlag == 0) { int mRank; MPI_Comm_rank(MPI_COMM_WORLD, &mRank); const auto outputRanks = getRanks(); diff --git a/lib/support/Table.h b/lib/support/Table.h new file mode 100644 index 00000000..f4f6d3fd --- /dev/null +++ b/lib/support/Table.h @@ -0,0 +1,185 @@ +// +// Created by ahueck on 14.10.20. +// + +#ifndef TYPEART_TABLE_H +#define TYPEART_TABLE_H + +#include "llvm/Support/Format.h" +#include "llvm/Support/FormatProviders.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include +#include + +namespace typeart { + +namespace detail { +template +inline std::string num2str(Number num) { + if constexpr (std::is_floating_point_v) { + std::string buf; + llvm::raw_string_ostream os(buf); + llvm::format_provider::format(num, os, llvm::StringRef("f")); + return os.str(); + } else { + return std::to_string(num); + } +} +} // namespace detail + +struct Cell { + enum align_as { left, right }; + + std::string c; + int w; + align_as align; + + explicit Cell(std::string v) : c(std::move(v)), w(c.size()), align(left) { + w = c.size(); + align = left; + } + + explicit Cell(const char* v) : Cell(std::string(v)) { + } + + template + explicit Cell(number v) : Cell(detail::num2str(v)) { + align = right; + } +}; + +struct Row { + Cell label; + std::vector cells; + + explicit Row(std::string name) : label(std::move(name)) { + } + + Row& put(Cell&& c) { + cells.emplace_back(c); + return *this; + } + + static Row make_row(std::string name) { + auto r = Row(std::move(name)); + return r; + } + + template + static Row make(std::string name, Cells&&... c) { + auto r = make_row(std::move(name)); + (r.put(Cell(c)), ...); + return r; + } +}; + +struct Table { + std::string title{"Table"}; + std::vector row_vec; + int columns{0}; + int rows{0}; + int max_row_label_width{1}; + std::string cell_sep{" , "}; + std::string empty_cell{"-"}; + char table_header{'-'}; + bool wrap_header{false}; + bool wrap_length{false}; + bool colon_empty{false}; + + explicit Table(std::string title) : title(std::move(title)) { + } + + void put(Row&& r) { + row_vec.emplace_back(r); + columns = std::max(columns, r.cells.size()); + ++rows; + max_row_label_width = std::max(max_row_label_width, r.label.w); + } + + template + static Table make(std::string title, Rows&&... r) { + Table t(std::move(title)); + (t.put(std::forward(r)), ...); + return t; + } + + void print(llvm::raw_ostream& s) const { + const auto max_row_id = max_row_label_width + 1; + + // determine per column width + std::vector col_width(columns, 4); + for (const auto& row : row_vec) { + int col_num{0}; + for (const auto& col : row.cells) { + col_width[col_num] = std::max(col_width[col_num], col.w + 1); + ++col_num; + } + } + + // how long is the header separation supposed to be + const auto head_rep_count = [&]() -> int { + if (wrap_length) { + const int sum_col_width = std::accumulate(std::begin(col_width), std::end(col_width), 0); + const int title_width = std::max(max_row_id, title.size()); + + return title_width + sum_col_width + columns * cell_sep.size(); + } + return std::max(max_row_id, title.size()); + }; + + s << std::string(head_rep_count(), table_header) << "\n"; + s << title << "\n"; + for (const auto& row : row_vec) { + s << llvm::left_justify(row.label.c, max_row_id); + + if (!row.cells.empty() || colon_empty) { + s << ":"; + } + + int col_num{0}; + auto num_beg = std::begin(row.cells); + const auto has_cells = num_beg != std::end(row.cells); + + if (has_cells) { + // print first row cell, then subsequent cells with *cell_sep* as prefix + s << llvm::right_justify(num_beg->c, col_width[col_num]); + std::for_each(std::next(num_beg), std::end(row.cells), [&](const auto& v) { + const auto width = col_width[++col_num]; + const auto aligned_cell = + v.align == Cell::right ? llvm::right_justify(v.c, width) : llvm::left_justify(v.c, width); + s << cell_sep << aligned_cell; + }); + } + + // fill up empty columns with empty_cell + const int padding = columns - col_num - 1; + if (padding > 0 && !empty_cell.empty()) { + const auto iterate_w = [&]() -> int { + const auto width = col_width[++col_num]; + return width; + }; + + // print first empty padding, then subsequent padding with *cell_sep* as prefix + if (!has_cells) { + s << llvm::right_justify(empty_cell, iterate_w()); + } + for (int pad = 0; pad < padding; ++pad) { + s << cell_sep << llvm::right_justify(empty_cell, iterate_w()); + } + } + s << "\n"; + } + + if (wrap_header) { + // Put header separation at bottom too + s << std::string(head_rep_count(), table_header) << "\n"; + } + + s.flush(); + } +}; +} // namespace typeart +#endif // TYPEART_TABLE_H diff --git a/typelib/CMakeLists.txt b/lib/typelib/CMakeLists.txt similarity index 90% rename from typelib/CMakeLists.txt rename to lib/typelib/CMakeLists.txt index af57fb8c..78d7ef52 100644 --- a/typelib/CMakeLists.txt +++ b/lib/typelib/CMakeLists.txt @@ -25,6 +25,13 @@ target_include_directories(typelib ${LLVM_INCLUDE_DIRS} ) +target_include_directories(typelib + PRIVATE + ${PROJECT_SOURCE_DIR}/lib +) + +target_project_coverage_options(typelib) + target_project_compile_options(typelib) target_project_compile_definitions(typelib ARG_PRIVATE_DEFS diff --git a/typelib/TypeDB.cpp b/lib/typelib/TypeDB.cpp similarity index 92% rename from typelib/TypeDB.cpp rename to lib/typelib/TypeDB.cpp index 42bf1154..b65a1549 100644 --- a/typelib/TypeDB.cpp +++ b/lib/typelib/TypeDB.cpp @@ -46,12 +46,12 @@ bool TypeDB::isStructType(int id) const { bool TypeDB::isUserDefinedType(int id) const { auto structInfo = getStructInfo(id); - return structInfo && (structInfo->flags & static_cast(TA_USER_DEF)); + return (structInfo != nullptr) && (structInfo->flags & static_cast(TA_USER_DEF)); } bool TypeDB::isVectorType(int id) const { auto structInfo = getStructInfo(id); - return structInfo && (structInfo->flags & static_cast(TA_VEC)); + return (structInfo != nullptr) && (structInfo->flags & static_cast(TA_VEC)); } bool TypeDB::isValid(int id) const { @@ -83,7 +83,7 @@ const std::string& TypeDB::getTypeName(int id) const { } if (isStructType(id)) { const auto* structInfo = getStructInfo(id); - if (structInfo) { + if (structInfo != nullptr) { return structInfo->name; } } @@ -99,7 +99,7 @@ size_t TypeDB::getTypeSize(int id) const { } const auto* structInfo = getStructInfo(id); - if (structInfo) { + if (structInfo != nullptr) { return structInfo->extent; } return 0; diff --git a/typelib/TypeDB.h b/lib/typelib/TypeDB.h similarity index 99% rename from typelib/TypeDB.h rename to lib/typelib/TypeDB.h index 9a8298d8..5f8f6220 100644 --- a/typelib/TypeDB.h +++ b/lib/typelib/TypeDB.h @@ -63,6 +63,7 @@ class TypeDB { std::vector structInfoList; std::unordered_map id2Idx; }; + } // namespace typeart #endif // LLVM_MUST_SUPPORT_TYPECONFIG_H diff --git a/typelib/TypeIO.cpp b/lib/typelib/TypeIO.cpp similarity index 93% rename from typelib/TypeIO.cpp rename to lib/typelib/TypeIO.cpp index cbf9d6d6..7579ce56 100644 --- a/typelib/TypeIO.cpp +++ b/lib/typelib/TypeIO.cpp @@ -6,6 +6,7 @@ #include "TypeDB.h" #include "TypeInterface.h" +#include "support/Logger.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" @@ -77,12 +78,7 @@ bool TypeIO::load(const std::string& file) { typeDB.registerStruct(typeInfo); } - if (in.error()) { - // FIXME we really need meaningful errors for IO - return false; - } - - return true; + return !in.error(); } bool TypeIO::store(const std::string& file) const { @@ -92,12 +88,13 @@ bool TypeIO::store(const std::string& file) const { raw_fd_ostream oss(StringRef(file), ec, sys::fs::OpenFlags::F_Text); if (oss.has_error()) { - llvm::errs() << "Error\n"; + LOG_FATAL("Error while storing type file to " << file); return false; } + auto types = typeDB.getStructList(); yaml::Output out(oss); - if (types.size() > 0) { + if (!types.empty()) { out << types; } else { out.beginDocuments(); diff --git a/typelib/TypeIO.h b/lib/typelib/TypeIO.h similarity index 99% rename from typelib/TypeIO.h rename to lib/typelib/TypeIO.h index 4289418e..60dd35f9 100644 --- a/typelib/TypeIO.h +++ b/lib/typelib/TypeIO.h @@ -21,6 +21,7 @@ class TypeIO { bool load(const std::string& file); bool store(const std::string& file) const; }; + } // namespace typeart #endif // LLVM_MUST_SUPPORT_CONFIGIO_H diff --git a/typelib/TypeInterface.h b/lib/typelib/TypeInterface.h similarity index 100% rename from typelib/TypeInterface.h rename to lib/typelib/TypeInterface.h diff --git a/runtime/Logger.h b/runtime/Logger.h deleted file mode 100644 index 114c0cd2..00000000 --- a/runtime/Logger.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Logger.h - * - * Created on: Jan 3, 2018 - * Author: ahueck - */ - -#ifndef LIB_LOGGER_H_ -#define LIB_LOGGER_H_ - -#include - -#ifndef LOG_LEVEL -/* - * Usually set at compile time: -DLOG_LEVEL=, N in [0, 3] for output - * 3 being most verbose - */ -#define LOG_LEVEL 3 -#endif - -#ifndef LOG_BASENAME_FILE -#define LOG_BASENAME_FILE __FILE__ -#endif - -#ifndef MPI_LOGGER -#define MPI_LOGGER 0 -#endif - -#if MPI_LOGGER - -void mpi_log(const std::string& msg); -#define OO_LOG_LEVEL_MSG(LEVEL_NUM, LEVEL, MSG) \ - if ((LEVEL_NUM) <= LOG_LEVEL) { \ - std::string s; \ - llvm::raw_string_ostream rso(s); \ - rso << (LEVEL) << LOG_BASENAME_FILE << ":" << __func__ << ":" << __LINE__ << ":" << MSG << "\n"; /* NOLINT */ \ - mpi_log(rso.str()); \ - } - -#define OO_LOG_LEVEL_MSG_BARE(LEVEL_NUM, LEVEL, MSG) \ - if ((LEVEL_NUM) <= LOG_LEVEL) { \ - std::string s; \ - llvm::raw_string_ostream rso(s); \ - rso << (LEVEL) << " " << MSG << "\n"; /* NOLINT */ \ - mpi_log(rso.str()); \ - } - -#else - -#define OO_LOG_LEVEL_MSG(LEVEL_NUM, LEVEL, MSG) \ - if ((LEVEL_NUM) <= LOG_LEVEL) { \ - llvm::errs() << (LEVEL) << " " << LOG_BASENAME_FILE << ":" << __func__ << ":" << __LINE__ << ": " << MSG \ - << "\n"; /* NOLINT */ \ - } - -#define OO_LOG_LEVEL_MSG_BARE(LEVEL_NUM, LEVEL, MSG) \ - if ((LEVEL_NUM) <= LOG_LEVEL) { \ - llvm::errs() << (LEVEL) << " " << MSG << "\n"; /* NOLINT */ \ - } - -#endif // MPI_LOGGER - -#define LOG_TRACE(MSG) OO_LOG_LEVEL_MSG_BARE(3, "[Trace]", MSG) -#define LOG_DEBUG(MSG) OO_LOG_LEVEL_MSG(3, "[Debug]", MSG) -#define LOG_INFO(MSG) OO_LOG_LEVEL_MSG(2, "[Info]", MSG) -#define LOG_WARNING(MSG) OO_LOG_LEVEL_MSG(1, "[Warning]", MSG) -#define LOG_ERROR(MSG) OO_LOG_LEVEL_MSG(1, "[Error]", MSG) -#define LOG_FATAL(MSG) OO_LOG_LEVEL_MSG(0, "[Fatal]", MSG) -#define LOG_MSG(MSG) llvm::errs() << MSG << "\n"; /* NOLINT */ - -#endif /* LIB_LOGGER_H_ */ diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index b06316e5..db7b29c5 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -1,9 +1,52 @@ -configure_file(clang-typeart.in clang-typeart) -configure_file(clang-typeart++.in clang-typeart++) - -install( - PROGRAMS - ${CMAKE_CURRENT_BINARY_DIR}/clang-typeart - ${CMAKE_CURRENT_BINARY_DIR}/clang-typeart++ - DESTINATION bin -) +macro(make_executable input output) + # Executable permission: In CMAKE 3.19 we can simply use file(CHMOD) instead of file(copy) workaround + configure_file(${input} ${CMAKE_CURRENT_BINARY_DIR}/tmp/${output} @ONLY) + file( + COPY ${CMAKE_CURRENT_BINARY_DIR}/tmp/${output} + DESTINATION ${CMAKE_CURRENT_BINARY_DIR} + FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE + ) + file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/tmp) +endmacro() + +function(configure_typeart_script input output) + set(TYPEART_PROJECT_DIR ${PROJECT_SOURCE_DIR}) + set(TYPEART_BINARY_DIR ${CMAKE_BINARY_DIR}) + set(TYPEART_SCRIPT_DIR ${PROJECT_SOURCE_DIR}/scripts) + set(TYPEART_PASS_DIR ${CMAKE_BINARY_DIR}/lib/passes) + set(TYPEART_ANALYSIS_PASS_DIR ${TYPEART_PASS_DIR}/analysis) + set(TYPEART_RT_DIR ${CMAKE_BINARY_DIR}/lib/runtime) + set(TYPEART_ANALYSIS_PLUGIN meminstfinderpass.so) + set(TYPEART_PLUGIN typeartpass.so) + set(TYPEART_INCLUDE_DIRS -I${PROJECT_SOURCE_DIR}/lib/typelib) + + # To get execute permission: create run.sh in the binary dir, and copy it to scripts folder with permission + make_executable(${input} ${output}) +endfunction() + +function(configure_typeart_coverage_script input output) + find_program(LLVMCOV_COMMAND + NAMES llvm-cov-10 llvm-cov + ) + if(LLVMCOV_COMMAND) + set(LLVM_COV_COMMAND ${LLVMCOV_COMMAND}) + make_executable(${input} ${output}) + else() + message(WARNING "Need llvm-cov for coverage script") + endif() +endfunction() + +configure_typeart_script(run.sh.in run.sh) +configure_typeart_script(apply.sh.in apply.sh) + +configure_typeart_coverage_script(llvm-gcov.sh.in llvm-gcov.sh) + +#configure_file(clang-typeart.in clang-typeart) +#configure_file(clang-typeart++.in clang-typeart++) + +#install( +# PROGRAMS +# ${CMAKE_CURRENT_BINARY_DIR}/clang-typeart +# ${CMAKE_CURRENT_BINARY_DIR}/clang-typeart++ +# DESTINATION bin +#) diff --git a/scripts/apply.sh.in b/scripts/apply.sh.in new file mode 100644 index 00000000..db0be282 --- /dev/null +++ b/scripts/apply.sh.in @@ -0,0 +1,39 @@ +#!/bin/bash + +target=$1 +ta_more_args=${2:-""} +optimize=${3:-" "} + +extension="${target##*.}" +tmpfile=${target##*/} + +if [ $extension == "c" ]; then + compiler=clang +else + compiler=clang++ +fi + +if [ -e "types.yaml" ]; then + rm "types.yaml" +fi + +function make_no_optim() { + # Order: heap and stack/global together, no optimization pass + $compiler @TYPEART_INCLUDE_DIRS@ -O1 -Xclang -disable-llvm-passes -S -emit-llvm "$target" -o - | opt $typeart_plugin -typeart-alloca -typeart-stats $ta_more_args -S 2>&1 +} + +function make_with_optim() { + # Order: heap, optimize, alloca with additional args.. + $compiler @TYPEART_INCLUDE_DIRS@ -O1 -Xclang -disable-llvm-passes -S -emit-llvm "$target" -o - | opt $typeart_plugin -typeart-stats | opt $optimize -S | opt $typeart_plugin -typeart-alloca -typeart-stats $ta_more_args -S 2>&1 +} + +function compile() { + local typeart_plugin="-load @TYPEART_ANALYSIS_PASS_DIR@/@TYPEART_ANALYSIS_PLUGIN@ -load @TYPEART_PASS_DIR@/@TYPEART_PLUGIN@ -typeart" + if [ "$optimize" == " " ]; then + make_no_optim + else + make_with_optim + fi +} + +compile \ No newline at end of file diff --git a/scripts/applyAndRun.sh b/scripts/applyAndRun.sh deleted file mode 100755 index 1f9c4132..00000000 --- a/scripts/applyAndRun.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -target=$1 -pathToPlugin=${2:-build/lib} -pluginArgs=${3:-""} -pathToRT=${4:-build/runtime} -scriptDir=$(dirname "$0") - -tmpDir="./" -tmpfile="$tmpDir"/"${target##*/}" -extension="${target##*.}" - -rtDir="$( cd "$pathToRT" && pwd )" - -echo -e Running on "$target" using plugin: "$plugin" - -echo $pathToPlugin -echo $pathToRT - -if [ $extension == "c" ]; then - compiler=clang -else - compiler=clang++ -fi - -if [ -e "${tmpDir}/types.yaml" ]; then - rm "${tmpDir}/types.yaml" -fi - -$compiler -S -emit-llvm "$target" -o "$tmpfile".ll -I${scriptDir}/../typelib -opt -load ${pathToPlugin}/analysis/meminstfinderpass.so -load ${pathToPlugin}/typeartpass.so -typeart $pluginArgs < "$tmpfile".ll -o "$tmpfile".ll > /dev/null -llc "$tmpfile".ll -o "$tmpfile".s -$compiler "$tmpfile".s -L"$pathToRT" -ltypeart-rt -o "$tmpfile".o -echo -e Executing with runtime lib -LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$rtDir" "$tmpfile".o diff --git a/scripts/applyPass.sh b/scripts/applyPass.sh deleted file mode 100755 index d869bf62..00000000 --- a/scripts/applyPass.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -target=$1 -pathToPlugin=${2:-build/lib} -tmpDir=/tmp -tmpfile="$tmpDir"/"${target##*/}" -extension="${target##*.}" - -if [ -e "types.yaml" ]; then - rm "types.yaml" -fi - -echo -e Running on "$target" using plugin: "$plugin" - -if [ $extension == "c" ]; then - compiler=clang - flags="-Xclang -disable-O0-optnone " -else - compiler=clang++ - flags="-std=c++14 -Xclang -disable-O0-optnone " -fi - -function show_ir() { -# FIXME -OX as argument for opt causes passed to run twice.. -echo "$compiler $flags -S -emit-llvm "$target" -o - | opt -load "$pathToPlugin"/analysis/meminstfinderpass.so -load "$pathToPlugin"/typeartpass.so -typeart -typeart-alloca -typeart-stats -alloca-array-only=false -call-filter -S 2>&1" - $compiler $flags -S -emit-llvm "$target" -o - | opt -load "$pathToPlugin"/analysis/meminstfinderpass.so -load "$pathToPlugin"/typeartpass.so -typeart -typeart-alloca -typeart-stats -alloca-array-only=false -call-filter -S 2>&1 -} - -function show_ir_mem() { -# FIXME -OX as argument for opt causes passed to run twice.. - echo $compiler $flags -S -emit-llvm "$target" -o - | opt -load "$pathToPlugin"/analysis/meminstfinderpass.so -mem-inst-finder - - $compiler $flags -S -emit-llvm "$target" -o - | opt -load "$pathToPlugin"/analysis/meminstfinderpass.so -mem-inst-finder -alloca-array-only=false -call-filter -S 2>&1 -} - -show_ir diff --git a/scripts/llvm-gcov.sh.in b/scripts/llvm-gcov.sh.in new file mode 100644 index 00000000..e4de5064 --- /dev/null +++ b/scripts/llvm-gcov.sh.in @@ -0,0 +1,4 @@ +#!/bin/bash + +# Workaround for lcov and clang --coverage incompatibility +exec @LLVM_COV_COMMAND@ gcov "$@" \ No newline at end of file diff --git a/scripts/run.sh.in b/scripts/run.sh.in new file mode 100644 index 00000000..718e5a8a --- /dev/null +++ b/scripts/run.sh.in @@ -0,0 +1,42 @@ +#!/bin/bash + +target=$1 +ta_more_args=${2:-""} +optimize=${3:-" "} + +extension="${target##*.}" +tmpfile=${target##*/} + +if [ $extension == "c" ]; then + compiler=clang +else + compiler=clang++ +fi + +if [ -e "types.yaml" ]; then + rm "types.yaml" +fi + +function make_no_optim() { + # Order: heap and stack/global together, no optimization pass + $compiler @TYPEART_INCLUDE_DIRS@ -O1 -Xclang -disable-llvm-passes -S -emit-llvm "$target" -o - | opt $typeart_plugin -typeart-alloca -typeart-stats $ta_more_args | llc -x=ir -filetype=obj -o "$tmpfile".o +} + +function make_with_optim() { + # Order: heap, optimize, alloca with additional args.. + $compiler @TYPEART_INCLUDE_DIRS@ -O1 -Xclang -disable-llvm-passes -S -emit-llvm "$target" -o - | opt $typeart_plugin -typeart-stats | opt $optimize -S | opt $typeart_plugin -typeart-alloca -typeart-stats $ta_more_args | llc -x=ir -filetype=obj -o "$tmpfile".o +} + +function compile() { + local typeart_plugin="-load @TYPEART_ANALYSIS_PASS_DIR@/@TYPEART_ANALYSIS_PLUGIN@ -load @TYPEART_PASS_DIR@/@TYPEART_PLUGIN@ -typeart" + if [ "$optimize" == " " ]; then + make_no_optim + else + make_with_optim + fi + $compiler "$tmpfile".o -L@TYPEART_RT_DIR@ -ltypeart-rt -o "$tmpfile".exe +} + +compile +echo -e Executing with runtime lib +LD_LIBRARY_PATH="$LD_LIBRARY_PATH:@TYPEART_RT_DIR@" ./"$tmpfile".exe \ No newline at end of file diff --git a/scripts/show-source-ll.sh b/scripts/show-source-ll.sh index 58c39f2b..c460cbbd 100755 --- a/scripts/show-source-ll.sh +++ b/scripts/show-source-ll.sh @@ -16,4 +16,4 @@ cat "$target" echo "|----------------------------------------------------------|" echo "|--------------------------LL------------------------------|" echo "|----------------------------------------------------------|" -$compiler -S -emit-llvm "$target" -o - +$compiler -S -emit-llvm -fno-discard-value-names "$target" -o - diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 278ecb9c..07a7e825 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -7,9 +7,10 @@ function(configure_typeart_lit_site_cfg input output) set(TYPEARTPASS_PROJECT_DIR ${PROJECT_SOURCE_DIR}) set(TYPEARTPASS_BINARY_DIR ${CMAKE_BINARY_DIR}) - set(TYPEARTPASS_SCRIPT_DIR ${PROJECT_SOURCE_DIR}/scripts) - set(TYPEARTPASS_LIBRARY_DIR ${CMAKE_BINARY_DIR}/lib) - set(TYPEARTPASS_RT_DIR ${CMAKE_BINARY_DIR}/runtime) + set(TYPEARTPASS_SCRIPT_DIR ${CMAKE_BINARY_DIR}/scripts) + set(TYPEARTPASS_LIBRARY_DIR ${CMAKE_BINARY_DIR}/lib/passes) + set(TYPEARTPASS_RT_DIR ${CMAKE_BINARY_DIR}/lib/runtime) + set(TYPEARTPASS_PROFILE_FILE ${TYPEART_PROFILE_DIR}/lit-code-%p.profraw) set(LIT_SITE_CFG_IN_HEADER "## Autogenerated from ${input}\n## Do not edit!") configure_file(${input} ${output} @ONLY) @@ -17,7 +18,7 @@ endfunction() add_format_target(format-tests "Formats project test source files" - TARGETS *.c + TARGETS *.c *.cpp *.h ) configure_typeart_lit_site_cfg( @@ -36,11 +37,10 @@ add_lit_testsuite(lit-pass-test ) add_test( - NAME ctest_lit_suite - COMMAND ${LIT_COMMAND_I} -j 1 --param typeartpass_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg ${CMAKE_CURRENT_SOURCE_DIR} + NAME ctest_lit_suite COMMAND + ${LIT_COMMAND_I} -j 1 --param typeartpass_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg ${CMAKE_CURRENT_SOURCE_DIR} ) if(MPI_INTERCEPT_LIB) - add_subdirectory(mpi_interceptor) add_subdirectory(lulesh) endif() diff --git a/test/lit.cfg b/test/lit.cfg index bea58e39..e57d8875 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -1,40 +1,60 @@ import os -import sys -import re -import platform -import subprocess - import lit.util import lit.formats -config.name = "TypeArtPassTest" +# From libcxx lit: Tell pylint that we know config and lit_config exist somewhere. +if 'PYLINT_IMPORT' in os.environ: + config = object() + lit_config = object() -typeart_lib_root = getattr(config, 'typeartpass_lib_dir', None) -typeart_rt_root = getattr(config, 'typeartpass_rt_dir', None) -typeart_script_dir = getattr(config, 'typeartpass_script_dir', None) +config.name = "TypeArtPassTest" -if typeart_lib_root is None or typeart_rt_root is None or typeart_script_dir is None: +loaded_site_cfg = getattr(config, 'loaded_site_config', False) +if not loaded_site_cfg: # Check for 'typeartpass_site_config' user parameter, and use that if available. site_cfg = lit_config.params.get('typeartpass_site_config', None) if site_cfg and os.path.exists(site_cfg): lit_config.load_config(config, site_cfg) raise SystemExit -typeart_script_dir = typeart_script_dir -typeart_bin_dir = typeart_lib_root -typeart_rt_dir = typeart_rt_root -plugin_name = 'typeartpass.so' -plugin_args ='-typeart -typeart-stats' - -execute_external = (True) -config.test_format = lit.formats.ShTest(execute_external) - +config.test_format = lit.formats.ShTest(execute_external=True) config.suffixes = ['.c','.cpp', '.llin'] - -config.excludes = ['Inputs', 'mpi_interceptor', 'lulesh', 'amg', 'amg_make'] - -config.substitutions.append(('%scriptpath', typeart_script_dir)) -config.substitutions.append(('%pluginpath', typeart_bin_dir)) -config.substitutions.append(('%rtpath', typeart_rt_dir)) -config.substitutions.append(('%pluginname', plugin_name)) -config.substitutions.append(('%pluginargs', plugin_args)) +config.excludes = ['Inputs', 'mpi_interceptor', 'lulesh'] + +profile_files = getattr(config, 'profile_file', None) +typeart_lib_root = getattr(config, 'typeartpass_lib_dir', None) +typeart_rt_root = getattr(config, 'typeartpass_rt_dir', None) +typeart_script_dir = getattr(config, 'typeartpass_script_dir', None) +transform_name = 'typeartpass.so' +transform_pass = '{}/{}'.format(typeart_lib_root, transform_name) +analysis_name = 'meminstfinderpass.so' +analysis_pass = '{}/analysis/{}'.format(typeart_lib_root, analysis_name) +std_plugin_args = '-typeart -typeart-stats' +to_llvm_args = '-O1 -Xclang -disable-llvm-passes -S -emit-llvm -o -' +type_file = 'types.yaml' + +config.environment['LLVM_PROFILE_FILE'] = profile_files + +# Substitutions: executables use "-" separator, variables use underscore +config.substitutions.append(('%script_path', typeart_script_dir)) +config.substitutions.append(('%plugin_path', typeart_lib_root)) +config.substitutions.append(('%runtime_path', typeart_rt_root)) + +config.substitutions.append(('%type_file', type_file)) + +config.substitutions.append(('%transform_plugin', transform_name)) +config.substitutions.append(('%analysis_plugin', analysis_name)) +config.substitutions.append(('%transform_pass', transform_pass)) +config.substitutions.append(('%analysis_pass', analysis_pass)) + +config.substitutions.append(('%arg_std', std_plugin_args)) + +# TODO refactor typeart arguments (and add args for enabling heap/stack/globals etc.) +# config.substitutions.append(('%arg_stack', '-typeart-alloca')) +# config.substitutions.append(('%arg_heap', '-typeart-no-heap')) + +config.substitutions.append(('%apply-typeart', 'opt -load {} -load {} {}'.format(analysis_pass, transform_pass, std_plugin_args))) +config.substitutions.append(('%c-to-llvm', 'clang {}'.format(to_llvm_args))) +config.substitutions.append(('%cpp-to-llvm', 'clang++ {}'.format(to_llvm_args))) +config.substitutions.append(('%run', '{}/run.sh'.format(typeart_script_dir))) +config.substitutions.append(('%apply', '{}/apply.sh'.format(typeart_script_dir))) diff --git a/test/lit.site.cfg.in b/test/lit.site.cfg.in index 94c5d06a..76ce0757 100644 --- a/test/lit.site.cfg.in +++ b/test/lit.site.cfg.in @@ -6,6 +6,8 @@ config.typeartpass_obj_root = "@TYPEARTPASS_BINARY_DIR@" config.typeartpass_lib_dir = "@TYPEARTPASS_LIBRARY_DIR@" config.typeartpass_rt_dir = "@TYPEARTPASS_RT_DIR@" config.typeartpass_script_dir = "@TYPEARTPASS_SCRIPT_DIR@" +config.profile_file = "@TYPEARTPASS_PROFILE_FILE@" # Let the main config do the real work. +config.loaded_site_config = True lit_config.load_config(config, "@TYPEARTPASS_PROJECT_DIR@/test/lit.cfg") diff --git a/test/lulesh/CMakeLists.txt b/test/lulesh/CMakeLists.txt index 8038fa16..c3382ccf 100644 --- a/test/lulesh/CMakeLists.txt +++ b/test/lulesh/CMakeLists.txt @@ -5,6 +5,7 @@ project(typeart-lulesh-integration-tests) set(WORK_DIR ${PROJECT_SOURCE_DIR}) set(TEST_SCRIPT ${WORK_DIR}/run_integration_test.sh) set(LULESH_NP 8 CACHE STRING "Number of MPI processes for lulesh") +mark_as_advanced(LULESH_NP) function(add_integration_test name dir exe_dir np args) message(STATUS "Configuring benchmark ${name}") @@ -38,11 +39,12 @@ function(add_integration_test name dir exe_dir np args) #set_tests_properties(test_clean_${name} PROPERTIES FIXTURES_CLEANUP lulesh_b) add_test(NAME ${name} - COMMAND "${TEST_SCRIPT}" ${name} ${np} ${args} ${CMAKE_BINARY_DIR}/runtime ${CMAKE_BINARY_DIR}/test/mpi_interceptor + COMMAND "${TEST_SCRIPT}" ${name} ${np} ${args} ${CMAKE_BINARY_DIR}/lib/runtime ${CMAKE_BINARY_DIR}/lib/mpi_interceptor WORKING_DIRECTORY "${exe_dir}" ) set_tests_properties(${name} PROPERTIES FIXTURES_REQUIRED lulesh_b) + set_tests_properties(${name} PROPERTIES ENVIRONMENT LLVM_PROFILE_FILE=${TYPEART_PROFILE_DIR}/code-%p-lulesh.profraw) endfunction() add_integration_test("lulesh2.0" "${WORK_DIR}" "${WORK_DIR}" ${LULESH_NP} "-s 12 -i 200") diff --git a/test/lulesh/Makefile b/test/lulesh/Makefile index 0d90a8e4..3697b575 100644 --- a/test/lulesh/Makefile +++ b/test/lulesh/Makefile @@ -9,8 +9,8 @@ SERCXX = clang++ -DUSE_MPI=0 MPICXX = OMPI_CXX=clang++ mpic++ -DUSE_MPI=1 CXX = $(MPICXX) -TYPEART_RTPATH = ${TYPEART_PATH}/lib -LTYPEART_RT= -L$(TYPEART_PATH)/runtime -ltypeart-rt +TYPEART_LIBPATH = ${TYPEART_PATH}/lib +LTYPEART_RT= -L$(TYPEART_LIBPATH)/runtime -ltypeart-rt SOURCES2.0 = \ lulesh.cc \ @@ -24,12 +24,12 @@ OBJECTS2.0 = $(SOURCES2.0:.cc=.o) CXXFLAGS = -g -I. -Wall LDFLAGS = -g -O3 -PASSES=-load ${TYPEART_PATH}/lib/analysis/meminstfinderpass.so -load ${TYPEART_PATH}/lib/typeartpass.so -CALL_FILTER=-call-filter -call-filter-deep +PASSES=-load ${TYPEART_LIBPATH}/passes/analysis/meminstfinderpass.so -load ${TYPEART_LIBPATH}/passes/typeartpass.so -typeart-stats +CALL_FILTER=-call-filter -call-filter-impl=default .cc.o: lulesh.h ifeq ($(TYPEART), yes) - $(CXX) ${CXXFLAGS} -O1 -Xclang -disable-llvm-passes -S -emit-llvm $< -o - | opt $(PASSES) -typeart -S | opt -O3 -S | opt $(PASSES) -typeart -typeart-alloca -alloca-array-only=false $(CALL_FILTER) -typeart-no-heap=true | llc -x=ir -filetype=obj -o $@ + $(CXX) ${CXXFLAGS} -O1 -Xclang -disable-llvm-passes -S -emit-llvm $< -o - | opt $(PASSES) -typeart -S | opt -O3 -S | opt $(PASSES) -typeart -typeart-alloca $(CALL_FILTER) -typeart-no-heap=true | llc -x=ir -filetype=obj -o $@ else $(CXX) $(CXXFLAGS) -O3 -c -o $@ $< endif diff --git a/test/lulesh/lulesh.h b/test/lulesh/lulesh.h index b6afd5c4..25a2edc3 100644 --- a/test/lulesh/lulesh.h +++ b/test/lulesh/lulesh.h @@ -1,8 +1,7 @@ #if !defined(USE_MPI) -# error "You should specify USE_MPI=0 or USE_MPI=1 on the compile line" +#error "You should specify USE_MPI=0 or USE_MPI=1 on the compile line" #endif - // OpenMP will be compiled in if this flag is set to 1 AND the compiler beging // used supports it (i.e. the _OPENMP symbol is defined) #define USE_OMP 1 @@ -25,72 +24,88 @@ #include //************************************************** -// Allow flexibility for arithmetic representations +// Allow flexibility for arithmetic representations //************************************************** -#define MAX(a, b) ( ((a) > (b)) ? (a) : (b)) - +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) // Precision specification -typedef float real4 ; -typedef double real8 ; -typedef long double real10 ; // 10 bytes on x86 - -typedef int Index_t ; // array subscript and loop index -typedef real8 Real_t ; // floating point representation -typedef int Int_t ; // integer representation - -enum { VolumeError = -1, QStopError = -2 } ; - -inline real4 SQRT(real4 arg) { return sqrtf(arg) ; } -inline real8 SQRT(real8 arg) { return sqrt(arg) ; } -inline real10 SQRT(real10 arg) { return sqrtl(arg) ; } - -inline real4 CBRT(real4 arg) { return cbrtf(arg) ; } -inline real8 CBRT(real8 arg) { return cbrt(arg) ; } -inline real10 CBRT(real10 arg) { return cbrtl(arg) ; } - -inline real4 FABS(real4 arg) { return fabsf(arg) ; } -inline real8 FABS(real8 arg) { return fabs(arg) ; } -inline real10 FABS(real10 arg) { return fabsl(arg) ; } - +typedef float real4; +typedef double real8; +typedef long double real10; // 10 bytes on x86 + +typedef int Index_t; // array subscript and loop index +typedef real8 Real_t; // floating point representation +typedef int Int_t; // integer representation + +enum { VolumeError = -1, QStopError = -2 }; + +inline real4 SQRT(real4 arg) { + return sqrtf(arg); +} +inline real8 SQRT(real8 arg) { + return sqrt(arg); +} +inline real10 SQRT(real10 arg) { + return sqrtl(arg); +} + +inline real4 CBRT(real4 arg) { + return cbrtf(arg); +} +inline real8 CBRT(real8 arg) { + return cbrt(arg); +} +inline real10 CBRT(real10 arg) { + return cbrtl(arg); +} + +inline real4 FABS(real4 arg) { + return fabsf(arg); +} +inline real8 FABS(real8 arg) { + return fabs(arg); +} +inline real10 FABS(real10 arg) { + return fabsl(arg); +} // Stuff needed for boundary conditions // 2 BCs on each of 6 hexahedral faces (12 bits) -#define XI_M 0x00007 -#define XI_M_SYMM 0x00001 -#define XI_M_FREE 0x00002 -#define XI_M_COMM 0x00004 - -#define XI_P 0x00038 -#define XI_P_SYMM 0x00008 -#define XI_P_FREE 0x00010 -#define XI_P_COMM 0x00020 - -#define ETA_M 0x001c0 -#define ETA_M_SYMM 0x00040 -#define ETA_M_FREE 0x00080 -#define ETA_M_COMM 0x00100 - -#define ETA_P 0x00e00 -#define ETA_P_SYMM 0x00200 -#define ETA_P_FREE 0x00400 -#define ETA_P_COMM 0x00800 - -#define ZETA_M 0x07000 +#define XI_M 0x00007 +#define XI_M_SYMM 0x00001 +#define XI_M_FREE 0x00002 +#define XI_M_COMM 0x00004 + +#define XI_P 0x00038 +#define XI_P_SYMM 0x00008 +#define XI_P_FREE 0x00010 +#define XI_P_COMM 0x00020 + +#define ETA_M 0x001c0 +#define ETA_M_SYMM 0x00040 +#define ETA_M_FREE 0x00080 +#define ETA_M_COMM 0x00100 + +#define ETA_P 0x00e00 +#define ETA_P_SYMM 0x00200 +#define ETA_P_FREE 0x00400 +#define ETA_P_COMM 0x00800 + +#define ZETA_M 0x07000 #define ZETA_M_SYMM 0x01000 #define ZETA_M_FREE 0x02000 #define ZETA_M_COMM 0x04000 -#define ZETA_P 0x38000 +#define ZETA_P 0x38000 #define ZETA_P_SYMM 0x08000 #define ZETA_P_FREE 0x10000 #define ZETA_P_COMM 0x20000 // MPI Message Tags -#define MSG_COMM_SBN 1024 -#define MSG_SYNC_POS_VEL 2048 -#define MSG_MONOQ 3072 +#define MSG_COMM_SBN 1024 +#define MSG_SYNC_POS_VEL 2048 +#define MSG_MONOQ 3072 #define MAX_FIELDS_PER_MPI_COMM 6 @@ -98,8 +113,7 @@ inline real10 FABS(real10 arg) { return fabsl(arg) ; } // Assume Real_t is an "integral power of 2" bytes wide #define CACHE_COHERENCE_PAD_REAL (128 / sizeof(Real_t)) -#define CACHE_ALIGN_REAL(n) \ - (((n) + (CACHE_COHERENCE_PAD_REAL - 1)) & ~(CACHE_COHERENCE_PAD_REAL-1)) +#define CACHE_ALIGN_REAL(n) (((n) + (CACHE_COHERENCE_PAD_REAL - 1)) & ~(CACHE_COHERENCE_PAD_REAL - 1)) ////////////////////////////////////////////////////// // Primary data structure @@ -125,492 +139,664 @@ inline real10 FABS(real10 arg) { return fabsl(arg) ; } */ class Domain { + public: + // Constructor + Domain(Int_t numRanks, Index_t colLoc, Index_t rowLoc, Index_t planeLoc, Index_t nx, Int_t tp, Int_t nr, + Int_t balance, Int_t cost); + + // + // ALLOCATION + // + + void AllocateNodePersistent(Int_t numNode) // Node-centered + { + m_x.resize(numNode); // coordinates + m_y.resize(numNode); + m_z.resize(numNode); + + m_xd.resize(numNode); // velocities + m_yd.resize(numNode); + m_zd.resize(numNode); + + m_xdd.resize(numNode); // accelerations + m_ydd.resize(numNode); + m_zdd.resize(numNode); + + m_fx.resize(numNode); // forces + m_fy.resize(numNode); + m_fz.resize(numNode); + + m_nodalMass.resize(numNode); // mass + } + + void AllocateElemPersistent(Int_t numElem) // Elem-centered + { + m_nodelist.resize(8 * numElem); + + // elem connectivities through face + m_lxim.resize(numElem); + m_lxip.resize(numElem); + m_letam.resize(numElem); + m_letap.resize(numElem); + m_lzetam.resize(numElem); + m_lzetap.resize(numElem); + + m_elemBC.resize(numElem); + + m_e.resize(numElem); + m_p.resize(numElem); + + m_q.resize(numElem); + m_ql.resize(numElem); + m_qq.resize(numElem); + + m_v.resize(numElem); + + m_volo.resize(numElem); + m_delv.resize(numElem); + m_vdov.resize(numElem); + + m_arealg.resize(numElem); + + m_ss.resize(numElem); + + m_elemMass.resize(numElem); + } + + void AllocateGradients(Int_t numElem, Int_t allElem) { + // Position gradients + m_delx_xi.resize(numElem); + m_delx_eta.resize(numElem); + m_delx_zeta.resize(numElem); + + // Velocity gradients + m_delv_xi.resize(allElem); + m_delv_eta.resize(allElem); + m_delv_zeta.resize(allElem); + } + + void DeallocateGradients() { + m_delx_zeta.clear(); + m_delx_eta.clear(); + m_delx_xi.clear(); + + m_delv_zeta.clear(); + m_delv_eta.clear(); + m_delv_xi.clear(); + } + + void AllocateStrains(Int_t numElem) { + m_dxx.resize(numElem); + m_dyy.resize(numElem); + m_dzz.resize(numElem); + } + + void DeallocateStrains() { + m_dzz.clear(); + m_dyy.clear(); + m_dxx.clear(); + } + + // + // ACCESSORS + // + + // Node-centered + + // Nodal coordinates + Real_t& x(Index_t idx) { + return m_x[idx]; + } + Real_t& y(Index_t idx) { + return m_y[idx]; + } + Real_t& z(Index_t idx) { + return m_z[idx]; + } + + // Nodal velocities + Real_t& xd(Index_t idx) { + return m_xd[idx]; + } + Real_t& yd(Index_t idx) { + return m_yd[idx]; + } + Real_t& zd(Index_t idx) { + return m_zd[idx]; + } + + // Nodal accelerations + Real_t& xdd(Index_t idx) { + return m_xdd[idx]; + } + Real_t& ydd(Index_t idx) { + return m_ydd[idx]; + } + Real_t& zdd(Index_t idx) { + return m_zdd[idx]; + } + + // Nodal forces + Real_t& fx(Index_t idx) { + return m_fx[idx]; + } + Real_t& fy(Index_t idx) { + return m_fy[idx]; + } + Real_t& fz(Index_t idx) { + return m_fz[idx]; + } + + // Nodal mass + Real_t& nodalMass(Index_t idx) { + return m_nodalMass[idx]; + } + + // Nodes on symmertry planes + Index_t symmX(Index_t idx) { + return m_symmX[idx]; + } + Index_t symmY(Index_t idx) { + return m_symmY[idx]; + } + Index_t symmZ(Index_t idx) { + return m_symmZ[idx]; + } + bool symmXempty() { + return m_symmX.empty(); + } + bool symmYempty() { + return m_symmY.empty(); + } + bool symmZempty() { + return m_symmZ.empty(); + } + + // + // Element-centered + // + Index_t& regElemSize(Index_t idx) { + return m_regElemSize[idx]; + } + Index_t& regNumList(Index_t idx) { + return m_regNumList[idx]; + } + Index_t* regNumList() { + return &m_regNumList[0]; + } + Index_t* regElemlist(Int_t r) { + return m_regElemlist[r]; + } + Index_t& regElemlist(Int_t r, Index_t idx) { + return m_regElemlist[r][idx]; + } + + Index_t* nodelist(Index_t idx) { + return &m_nodelist[Index_t(8) * idx]; + } + + // elem connectivities through face + Index_t& lxim(Index_t idx) { + return m_lxim[idx]; + } + Index_t& lxip(Index_t idx) { + return m_lxip[idx]; + } + Index_t& letam(Index_t idx) { + return m_letam[idx]; + } + Index_t& letap(Index_t idx) { + return m_letap[idx]; + } + Index_t& lzetam(Index_t idx) { + return m_lzetam[idx]; + } + Index_t& lzetap(Index_t idx) { + return m_lzetap[idx]; + } + + // elem face symm/free-surface flag + Int_t& elemBC(Index_t idx) { + return m_elemBC[idx]; + } + + // Principal strains - temporary + Real_t& dxx(Index_t idx) { + return m_dxx[idx]; + } + Real_t& dyy(Index_t idx) { + return m_dyy[idx]; + } + Real_t& dzz(Index_t idx) { + return m_dzz[idx]; + } + + // Velocity gradient - temporary + Real_t& delv_xi(Index_t idx) { + return m_delv_xi[idx]; + } + Real_t& delv_eta(Index_t idx) { + return m_delv_eta[idx]; + } + Real_t& delv_zeta(Index_t idx) { + return m_delv_zeta[idx]; + } + + // Position gradient - temporary + Real_t& delx_xi(Index_t idx) { + return m_delx_xi[idx]; + } + Real_t& delx_eta(Index_t idx) { + return m_delx_eta[idx]; + } + Real_t& delx_zeta(Index_t idx) { + return m_delx_zeta[idx]; + } + + // Energy + Real_t& e(Index_t idx) { + return m_e[idx]; + } + + // Pressure + Real_t& p(Index_t idx) { + return m_p[idx]; + } + + // Artificial viscosity + Real_t& q(Index_t idx) { + return m_q[idx]; + } + + // Linear term for q + Real_t& ql(Index_t idx) { + return m_ql[idx]; + } + // Quadratic term for q + Real_t& qq(Index_t idx) { + return m_qq[idx]; + } + + // Relative volume + Real_t& v(Index_t idx) { + return m_v[idx]; + } + Real_t& delv(Index_t idx) { + return m_delv[idx]; + } + + // Reference volume + Real_t& volo(Index_t idx) { + return m_volo[idx]; + } + + // volume derivative over volume + Real_t& vdov(Index_t idx) { + return m_vdov[idx]; + } + + // Element characteristic length + Real_t& arealg(Index_t idx) { + return m_arealg[idx]; + } + + // Sound speed + Real_t& ss(Index_t idx) { + return m_ss[idx]; + } + + // Element mass + Real_t& elemMass(Index_t idx) { + return m_elemMass[idx]; + } + + Index_t nodeElemCount(Index_t idx) { + return m_nodeElemStart[idx + 1] - m_nodeElemStart[idx]; + } + + Index_t* nodeElemCornerList(Index_t idx) { + return &m_nodeElemCornerList[m_nodeElemStart[idx]]; + } + + // Parameters + + // Cutoffs + Real_t u_cut() const { + return m_u_cut; + } + Real_t e_cut() const { + return m_e_cut; + } + Real_t p_cut() const { + return m_p_cut; + } + Real_t q_cut() const { + return m_q_cut; + } + Real_t v_cut() const { + return m_v_cut; + } + + // Other constants (usually are settable via input file in real codes) + Real_t hgcoef() const { + return m_hgcoef; + } + Real_t qstop() const { + return m_qstop; + } + Real_t monoq_max_slope() const { + return m_monoq_max_slope; + } + Real_t monoq_limiter_mult() const { + return m_monoq_limiter_mult; + } + Real_t ss4o3() const { + return m_ss4o3; + } + Real_t qlc_monoq() const { + return m_qlc_monoq; + } + Real_t qqc_monoq() const { + return m_qqc_monoq; + } + Real_t qqc() const { + return m_qqc; + } + + Real_t eosvmax() const { + return m_eosvmax; + } + Real_t eosvmin() const { + return m_eosvmin; + } + Real_t pmin() const { + return m_pmin; + } + Real_t emin() const { + return m_emin; + } + Real_t dvovmax() const { + return m_dvovmax; + } + Real_t refdens() const { + return m_refdens; + } + + // Timestep controls, etc... + Real_t& time() { + return m_time; + } + Real_t& deltatime() { + return m_deltatime; + } + Real_t& deltatimemultlb() { + return m_deltatimemultlb; + } + Real_t& deltatimemultub() { + return m_deltatimemultub; + } + Real_t& stoptime() { + return m_stoptime; + } + Real_t& dtcourant() { + return m_dtcourant; + } + Real_t& dthydro() { + return m_dthydro; + } + Real_t& dtmax() { + return m_dtmax; + } + Real_t& dtfixed() { + return m_dtfixed; + } + + Int_t& cycle() { + return m_cycle; + } + Index_t& numRanks() { + return m_numRanks; + } + + Index_t& colLoc() { + return m_colLoc; + } + Index_t& rowLoc() { + return m_rowLoc; + } + Index_t& planeLoc() { + return m_planeLoc; + } + Index_t& tp() { + return m_tp; + } + + Index_t& sizeX() { + return m_sizeX; + } + Index_t& sizeY() { + return m_sizeY; + } + Index_t& sizeZ() { + return m_sizeZ; + } + Index_t& numReg() { + return m_numReg; + } + Int_t& cost() { + return m_cost; + } + Index_t& numElem() { + return m_numElem; + } + Index_t& numNode() { + return m_numNode; + } + + Index_t& maxPlaneSize() { + return m_maxPlaneSize; + } + Index_t& maxEdgeSize() { + return m_maxEdgeSize; + } + + // + // MPI-Related additional data + // - public: - - // Constructor - Domain(Int_t numRanks, Index_t colLoc, - Index_t rowLoc, Index_t planeLoc, - Index_t nx, Int_t tp, Int_t nr, Int_t balance, Int_t cost); - - // - // ALLOCATION - // - - void AllocateNodePersistent(Int_t numNode) // Node-centered - { - m_x.resize(numNode); // coordinates - m_y.resize(numNode); - m_z.resize(numNode); - - m_xd.resize(numNode); // velocities - m_yd.resize(numNode); - m_zd.resize(numNode); - - m_xdd.resize(numNode); // accelerations - m_ydd.resize(numNode); - m_zdd.resize(numNode); - - m_fx.resize(numNode); // forces - m_fy.resize(numNode); - m_fz.resize(numNode); - - m_nodalMass.resize(numNode); // mass - } - - void AllocateElemPersistent(Int_t numElem) // Elem-centered - { - m_nodelist.resize(8*numElem); - - // elem connectivities through face - m_lxim.resize(numElem); - m_lxip.resize(numElem); - m_letam.resize(numElem); - m_letap.resize(numElem); - m_lzetam.resize(numElem); - m_lzetap.resize(numElem); - - m_elemBC.resize(numElem); - - m_e.resize(numElem); - m_p.resize(numElem); - - m_q.resize(numElem); - m_ql.resize(numElem); - m_qq.resize(numElem); - - m_v.resize(numElem); - - m_volo.resize(numElem); - m_delv.resize(numElem); - m_vdov.resize(numElem); - - m_arealg.resize(numElem); - - m_ss.resize(numElem); - - m_elemMass.resize(numElem); - } - - void AllocateGradients(Int_t numElem, Int_t allElem) - { - // Position gradients - m_delx_xi.resize(numElem) ; - m_delx_eta.resize(numElem) ; - m_delx_zeta.resize(numElem) ; - - // Velocity gradients - m_delv_xi.resize(allElem) ; - m_delv_eta.resize(allElem); - m_delv_zeta.resize(allElem) ; - } - - void DeallocateGradients() - { - m_delx_zeta.clear() ; - m_delx_eta.clear() ; - m_delx_xi.clear() ; - - m_delv_zeta.clear() ; - m_delv_eta.clear() ; - m_delv_xi.clear() ; - } - - void AllocateStrains(Int_t numElem) - { - m_dxx.resize(numElem) ; - m_dyy.resize(numElem) ; - m_dzz.resize(numElem) ; - } - - void DeallocateStrains() - { - m_dzz.clear() ; - m_dyy.clear() ; - m_dxx.clear() ; - } - - // - // ACCESSORS - // - - // Node-centered - - // Nodal coordinates - Real_t& x(Index_t idx) { return m_x[idx] ; } - Real_t& y(Index_t idx) { return m_y[idx] ; } - Real_t& z(Index_t idx) { return m_z[idx] ; } - - // Nodal velocities - Real_t& xd(Index_t idx) { return m_xd[idx] ; } - Real_t& yd(Index_t idx) { return m_yd[idx] ; } - Real_t& zd(Index_t idx) { return m_zd[idx] ; } - - // Nodal accelerations - Real_t& xdd(Index_t idx) { return m_xdd[idx] ; } - Real_t& ydd(Index_t idx) { return m_ydd[idx] ; } - Real_t& zdd(Index_t idx) { return m_zdd[idx] ; } - - // Nodal forces - Real_t& fx(Index_t idx) { return m_fx[idx] ; } - Real_t& fy(Index_t idx) { return m_fy[idx] ; } - Real_t& fz(Index_t idx) { return m_fz[idx] ; } - - // Nodal mass - Real_t& nodalMass(Index_t idx) { return m_nodalMass[idx] ; } - - // Nodes on symmertry planes - Index_t symmX(Index_t idx) { return m_symmX[idx] ; } - Index_t symmY(Index_t idx) { return m_symmY[idx] ; } - Index_t symmZ(Index_t idx) { return m_symmZ[idx] ; } - bool symmXempty() { return m_symmX.empty(); } - bool symmYempty() { return m_symmY.empty(); } - bool symmZempty() { return m_symmZ.empty(); } - - // - // Element-centered - // - Index_t& regElemSize(Index_t idx) { return m_regElemSize[idx] ; } - Index_t& regNumList(Index_t idx) { return m_regNumList[idx] ; } - Index_t* regNumList() { return &m_regNumList[0] ; } - Index_t* regElemlist(Int_t r) { return m_regElemlist[r] ; } - Index_t& regElemlist(Int_t r, Index_t idx) { return m_regElemlist[r][idx] ; } - - Index_t* nodelist(Index_t idx) { return &m_nodelist[Index_t(8)*idx] ; } - - // elem connectivities through face - Index_t& lxim(Index_t idx) { return m_lxim[idx] ; } - Index_t& lxip(Index_t idx) { return m_lxip[idx] ; } - Index_t& letam(Index_t idx) { return m_letam[idx] ; } - Index_t& letap(Index_t idx) { return m_letap[idx] ; } - Index_t& lzetam(Index_t idx) { return m_lzetam[idx] ; } - Index_t& lzetap(Index_t idx) { return m_lzetap[idx] ; } - - // elem face symm/free-surface flag - Int_t& elemBC(Index_t idx) { return m_elemBC[idx] ; } - - // Principal strains - temporary - Real_t& dxx(Index_t idx) { return m_dxx[idx] ; } - Real_t& dyy(Index_t idx) { return m_dyy[idx] ; } - Real_t& dzz(Index_t idx) { return m_dzz[idx] ; } - - // Velocity gradient - temporary - Real_t& delv_xi(Index_t idx) { return m_delv_xi[idx] ; } - Real_t& delv_eta(Index_t idx) { return m_delv_eta[idx] ; } - Real_t& delv_zeta(Index_t idx) { return m_delv_zeta[idx] ; } - - // Position gradient - temporary - Real_t& delx_xi(Index_t idx) { return m_delx_xi[idx] ; } - Real_t& delx_eta(Index_t idx) { return m_delx_eta[idx] ; } - Real_t& delx_zeta(Index_t idx) { return m_delx_zeta[idx] ; } - - // Energy - Real_t& e(Index_t idx) { return m_e[idx] ; } - - // Pressure - Real_t& p(Index_t idx) { return m_p[idx] ; } - - // Artificial viscosity - Real_t& q(Index_t idx) { return m_q[idx] ; } - - // Linear term for q - Real_t& ql(Index_t idx) { return m_ql[idx] ; } - // Quadratic term for q - Real_t& qq(Index_t idx) { return m_qq[idx] ; } - - // Relative volume - Real_t& v(Index_t idx) { return m_v[idx] ; } - Real_t& delv(Index_t idx) { return m_delv[idx] ; } - - // Reference volume - Real_t& volo(Index_t idx) { return m_volo[idx] ; } - - // volume derivative over volume - Real_t& vdov(Index_t idx) { return m_vdov[idx] ; } - - // Element characteristic length - Real_t& arealg(Index_t idx) { return m_arealg[idx] ; } - - // Sound speed - Real_t& ss(Index_t idx) { return m_ss[idx] ; } - - // Element mass - Real_t& elemMass(Index_t idx) { return m_elemMass[idx] ; } - - Index_t nodeElemCount(Index_t idx) - { return m_nodeElemStart[idx+1] - m_nodeElemStart[idx] ; } - - Index_t *nodeElemCornerList(Index_t idx) - { return &m_nodeElemCornerList[m_nodeElemStart[idx]] ; } - - // Parameters - - // Cutoffs - Real_t u_cut() const { return m_u_cut ; } - Real_t e_cut() const { return m_e_cut ; } - Real_t p_cut() const { return m_p_cut ; } - Real_t q_cut() const { return m_q_cut ; } - Real_t v_cut() const { return m_v_cut ; } - - // Other constants (usually are settable via input file in real codes) - Real_t hgcoef() const { return m_hgcoef ; } - Real_t qstop() const { return m_qstop ; } - Real_t monoq_max_slope() const { return m_monoq_max_slope ; } - Real_t monoq_limiter_mult() const { return m_monoq_limiter_mult ; } - Real_t ss4o3() const { return m_ss4o3 ; } - Real_t qlc_monoq() const { return m_qlc_monoq ; } - Real_t qqc_monoq() const { return m_qqc_monoq ; } - Real_t qqc() const { return m_qqc ; } - - Real_t eosvmax() const { return m_eosvmax ; } - Real_t eosvmin() const { return m_eosvmin ; } - Real_t pmin() const { return m_pmin ; } - Real_t emin() const { return m_emin ; } - Real_t dvovmax() const { return m_dvovmax ; } - Real_t refdens() const { return m_refdens ; } - - // Timestep controls, etc... - Real_t& time() { return m_time ; } - Real_t& deltatime() { return m_deltatime ; } - Real_t& deltatimemultlb() { return m_deltatimemultlb ; } - Real_t& deltatimemultub() { return m_deltatimemultub ; } - Real_t& stoptime() { return m_stoptime ; } - Real_t& dtcourant() { return m_dtcourant ; } - Real_t& dthydro() { return m_dthydro ; } - Real_t& dtmax() { return m_dtmax ; } - Real_t& dtfixed() { return m_dtfixed ; } - - Int_t& cycle() { return m_cycle ; } - Index_t& numRanks() { return m_numRanks ; } - - Index_t& colLoc() { return m_colLoc ; } - Index_t& rowLoc() { return m_rowLoc ; } - Index_t& planeLoc() { return m_planeLoc ; } - Index_t& tp() { return m_tp ; } - - Index_t& sizeX() { return m_sizeX ; } - Index_t& sizeY() { return m_sizeY ; } - Index_t& sizeZ() { return m_sizeZ ; } - Index_t& numReg() { return m_numReg ; } - Int_t& cost() { return m_cost ; } - Index_t& numElem() { return m_numElem ; } - Index_t& numNode() { return m_numNode ; } - - Index_t& maxPlaneSize() { return m_maxPlaneSize ; } - Index_t& maxEdgeSize() { return m_maxEdgeSize ; } - - // - // MPI-Related additional data - // - -#if USE_MPI - // Communication Work space - Real_t *commDataSend ; - Real_t *commDataRecv ; - - // Maximum number of block neighbors - MPI_Request recvRequest[26] ; // 6 faces + 12 edges + 8 corners - MPI_Request sendRequest[26] ; // 6 faces + 12 edges + 8 corners -#endif +#if USE_MPI + // Communication Work space + Real_t* commDataSend; + Real_t* commDataRecv; - private: - - void BuildMesh(Int_t nx, Int_t edgeNodes, Int_t edgeElems); - void SetupThreadSupportStructures(); - void CreateRegionIndexSets(Int_t nreg, Int_t balance); - void SetupCommBuffers(Int_t edgeNodes); - void SetupSymmetryPlanes(Int_t edgeNodes); - void SetupElementConnectivities(Int_t edgeElems); - void SetupBoundaryConditions(Int_t edgeElems); - - // - // IMPLEMENTATION - // - - /* Node-centered */ - std::vector m_x ; /* coordinates */ - std::vector m_y ; - std::vector m_z ; - - std::vector m_xd ; /* velocities */ - std::vector m_yd ; - std::vector m_zd ; - - std::vector m_xdd ; /* accelerations */ - std::vector m_ydd ; - std::vector m_zdd ; - - std::vector m_fx ; /* forces */ - std::vector m_fy ; - std::vector m_fz ; - - std::vector m_nodalMass ; /* mass */ - - std::vector m_symmX ; /* symmetry plane nodesets */ - std::vector m_symmY ; - std::vector m_symmZ ; - - // Element-centered - - // Region information - Int_t m_numReg ; - Int_t m_cost; //imbalance cost - Index_t *m_regElemSize ; // Size of region sets - Index_t *m_regNumList ; // Region number per domain element - Index_t **m_regElemlist ; // region indexset - - std::vector m_nodelist ; /* elemToNode connectivity */ - - std::vector m_lxim ; /* element connectivity across each face */ - std::vector m_lxip ; - std::vector m_letam ; - std::vector m_letap ; - std::vector m_lzetam ; - std::vector m_lzetap ; - - std::vector m_elemBC ; /* symmetry/free-surface flags for each elem face */ - - std::vector m_dxx ; /* principal strains -- temporary */ - std::vector m_dyy ; - std::vector m_dzz ; - - std::vector m_delv_xi ; /* velocity gradient -- temporary */ - std::vector m_delv_eta ; - std::vector m_delv_zeta ; - - std::vector m_delx_xi ; /* coordinate gradient -- temporary */ - std::vector m_delx_eta ; - std::vector m_delx_zeta ; - - std::vector m_e ; /* energy */ - - std::vector m_p ; /* pressure */ - std::vector m_q ; /* q */ - std::vector m_ql ; /* linear term for q */ - std::vector m_qq ; /* quadratic term for q */ - - std::vector m_v ; /* relative volume */ - std::vector m_volo ; /* reference volume */ - std::vector m_vnew ; /* new relative volume -- temporary */ - std::vector m_delv ; /* m_vnew - m_v */ - std::vector m_vdov ; /* volume derivative over volume */ - - std::vector m_arealg ; /* characteristic length of an element */ - - std::vector m_ss ; /* "sound speed" */ - - std::vector m_elemMass ; /* mass */ - - // Cutoffs (treat as constants) - const Real_t m_e_cut ; // energy tolerance - const Real_t m_p_cut ; // pressure tolerance - const Real_t m_q_cut ; // q tolerance - const Real_t m_v_cut ; // relative volume tolerance - const Real_t m_u_cut ; // velocity tolerance - - // Other constants (usually setable, but hardcoded in this proxy app) - - const Real_t m_hgcoef ; // hourglass control - const Real_t m_ss4o3 ; - const Real_t m_qstop ; // excessive q indicator - const Real_t m_monoq_max_slope ; - const Real_t m_monoq_limiter_mult ; - const Real_t m_qlc_monoq ; // linear term coef for q - const Real_t m_qqc_monoq ; // quadratic term coef for q - const Real_t m_qqc ; - const Real_t m_eosvmax ; - const Real_t m_eosvmin ; - const Real_t m_pmin ; // pressure floor - const Real_t m_emin ; // energy floor - const Real_t m_dvovmax ; // maximum allowable volume change - const Real_t m_refdens ; // reference density - - // Variables to keep track of timestep, simulation time, and cycle - Real_t m_dtcourant ; // courant constraint - Real_t m_dthydro ; // volume change constraint - Int_t m_cycle ; // iteration count for simulation - Real_t m_dtfixed ; // fixed time increment - Real_t m_time ; // current time - Real_t m_deltatime ; // variable time increment - Real_t m_deltatimemultlb ; - Real_t m_deltatimemultub ; - Real_t m_dtmax ; // maximum allowable time increment - Real_t m_stoptime ; // end time for simulation - - - Int_t m_numRanks ; - - Index_t m_colLoc ; - Index_t m_rowLoc ; - Index_t m_planeLoc ; - Index_t m_tp ; - - Index_t m_sizeX ; - Index_t m_sizeY ; - Index_t m_sizeZ ; - Index_t m_numElem ; - Index_t m_numNode ; - - Index_t m_maxPlaneSize ; - Index_t m_maxEdgeSize ; - - // OMP hack - Index_t *m_nodeElemStart ; - Index_t *m_nodeElemCornerList ; - - // Used in setup - Index_t m_rowMin, m_rowMax; - Index_t m_colMin, m_colMax; - Index_t m_planeMin, m_planeMax ; - -} ; - -typedef Real_t &(Domain::* Domain_member )(Index_t) ; + // Maximum number of block neighbors + MPI_Request recvRequest[26]; // 6 faces + 12 edges + 8 corners + MPI_Request sendRequest[26]; // 6 faces + 12 edges + 8 corners +#endif -struct cmdLineOpts { - Int_t its; // -i - Int_t nx; // -s - Int_t numReg; // -r - Int_t numFiles; // -f - Int_t showProg; // -p - Int_t quiet; // -q - Int_t viz; // -v - Int_t cost; // -c - Int_t balance; // -b + private: + void BuildMesh(Int_t nx, Int_t edgeNodes, Int_t edgeElems); + void SetupThreadSupportStructures(); + void CreateRegionIndexSets(Int_t nreg, Int_t balance); + void SetupCommBuffers(Int_t edgeNodes); + void SetupSymmetryPlanes(Int_t edgeNodes); + void SetupElementConnectivities(Int_t edgeElems); + void SetupBoundaryConditions(Int_t edgeElems); + + // + // IMPLEMENTATION + // + + /* Node-centered */ + std::vector m_x; /* coordinates */ + std::vector m_y; + std::vector m_z; + + std::vector m_xd; /* velocities */ + std::vector m_yd; + std::vector m_zd; + + std::vector m_xdd; /* accelerations */ + std::vector m_ydd; + std::vector m_zdd; + + std::vector m_fx; /* forces */ + std::vector m_fy; + std::vector m_fz; + + std::vector m_nodalMass; /* mass */ + + std::vector m_symmX; /* symmetry plane nodesets */ + std::vector m_symmY; + std::vector m_symmZ; + + // Element-centered + + // Region information + Int_t m_numReg; + Int_t m_cost; // imbalance cost + Index_t* m_regElemSize; // Size of region sets + Index_t* m_regNumList; // Region number per domain element + Index_t** m_regElemlist; // region indexset + + std::vector m_nodelist; /* elemToNode connectivity */ + + std::vector m_lxim; /* element connectivity across each face */ + std::vector m_lxip; + std::vector m_letam; + std::vector m_letap; + std::vector m_lzetam; + std::vector m_lzetap; + + std::vector m_elemBC; /* symmetry/free-surface flags for each elem face */ + + std::vector m_dxx; /* principal strains -- temporary */ + std::vector m_dyy; + std::vector m_dzz; + + std::vector m_delv_xi; /* velocity gradient -- temporary */ + std::vector m_delv_eta; + std::vector m_delv_zeta; + + std::vector m_delx_xi; /* coordinate gradient -- temporary */ + std::vector m_delx_eta; + std::vector m_delx_zeta; + + std::vector m_e; /* energy */ + + std::vector m_p; /* pressure */ + std::vector m_q; /* q */ + std::vector m_ql; /* linear term for q */ + std::vector m_qq; /* quadratic term for q */ + + std::vector m_v; /* relative volume */ + std::vector m_volo; /* reference volume */ + std::vector m_vnew; /* new relative volume -- temporary */ + std::vector m_delv; /* m_vnew - m_v */ + std::vector m_vdov; /* volume derivative over volume */ + + std::vector m_arealg; /* characteristic length of an element */ + + std::vector m_ss; /* "sound speed" */ + + std::vector m_elemMass; /* mass */ + + // Cutoffs (treat as constants) + const Real_t m_e_cut; // energy tolerance + const Real_t m_p_cut; // pressure tolerance + const Real_t m_q_cut; // q tolerance + const Real_t m_v_cut; // relative volume tolerance + const Real_t m_u_cut; // velocity tolerance + + // Other constants (usually setable, but hardcoded in this proxy app) + + const Real_t m_hgcoef; // hourglass control + const Real_t m_ss4o3; + const Real_t m_qstop; // excessive q indicator + const Real_t m_monoq_max_slope; + const Real_t m_monoq_limiter_mult; + const Real_t m_qlc_monoq; // linear term coef for q + const Real_t m_qqc_monoq; // quadratic term coef for q + const Real_t m_qqc; + const Real_t m_eosvmax; + const Real_t m_eosvmin; + const Real_t m_pmin; // pressure floor + const Real_t m_emin; // energy floor + const Real_t m_dvovmax; // maximum allowable volume change + const Real_t m_refdens; // reference density + + // Variables to keep track of timestep, simulation time, and cycle + Real_t m_dtcourant; // courant constraint + Real_t m_dthydro; // volume change constraint + Int_t m_cycle; // iteration count for simulation + Real_t m_dtfixed; // fixed time increment + Real_t m_time; // current time + Real_t m_deltatime; // variable time increment + Real_t m_deltatimemultlb; + Real_t m_deltatimemultub; + Real_t m_dtmax; // maximum allowable time increment + Real_t m_stoptime; // end time for simulation + + Int_t m_numRanks; + + Index_t m_colLoc; + Index_t m_rowLoc; + Index_t m_planeLoc; + Index_t m_tp; + + Index_t m_sizeX; + Index_t m_sizeY; + Index_t m_sizeZ; + Index_t m_numElem; + Index_t m_numNode; + + Index_t m_maxPlaneSize; + Index_t m_maxEdgeSize; + + // OMP hack + Index_t* m_nodeElemStart; + Index_t* m_nodeElemCornerList; + + // Used in setup + Index_t m_rowMin, m_rowMax; + Index_t m_colMin, m_colMax; + Index_t m_planeMin, m_planeMax; }; +typedef Real_t& (Domain::*Domain_member)(Index_t); +struct cmdLineOpts { + Int_t its; // -i + Int_t nx; // -s + Int_t numReg; // -r + Int_t numFiles; // -f + Int_t showProg; // -p + Int_t quiet; // -q + Int_t viz; // -v + Int_t cost; // -c + Int_t balance; // -b +}; // Function Prototypes // lulesh-par -Real_t CalcElemVolume( const Real_t x[8], - const Real_t y[8], - const Real_t z[8]); +Real_t CalcElemVolume(const Real_t x[8], const Real_t y[8], const Real_t z[8]); // lulesh-util -void ParseCommandLineOptions(int argc, char *argv[], - Int_t myRank, struct cmdLineOpts *opts); -void VerifyAndWriteFinalOutput(Real_t elapsed_time, - Domain& locDom, - Int_t nx, - Int_t numRanks); +void ParseCommandLineOptions(int argc, char* argv[], Int_t myRank, struct cmdLineOpts* opts); +void VerifyAndWriteFinalOutput(Real_t elapsed_time, Domain& locDom, Int_t nx, Int_t numRanks); // lulesh-viz void DumpToVisit(Domain& domain, int numFiles, int myRank, int numRanks); // lulesh-comm -void CommRecv(Domain& domain, Int_t msgType, Index_t xferFields, - Index_t dx, Index_t dy, Index_t dz, - bool doRecv, bool planeOnly); -void CommSend(Domain& domain, Int_t msgType, - Index_t xferFields, Domain_member *fieldData, - Index_t dx, Index_t dy, Index_t dz, - bool doSend, bool planeOnly); -void CommSBN(Domain& domain, Int_t xferFields, Domain_member *fieldData); +void CommRecv(Domain& domain, Int_t msgType, Index_t xferFields, Index_t dx, Index_t dy, Index_t dz, bool doRecv, + bool planeOnly); +void CommSend(Domain& domain, Int_t msgType, Index_t xferFields, Domain_member* fieldData, Index_t dx, Index_t dy, + Index_t dz, bool doSend, bool planeOnly); +void CommSBN(Domain& domain, Int_t xferFields, Domain_member* fieldData); void CommSyncPosVel(Domain& domain); void CommMonoQ(Domain& domain); // lulesh-init -void InitMeshDecomp(Int_t numRanks, Int_t myRank, - Int_t *col, Int_t *row, Int_t *plane, Int_t *side); +void InitMeshDecomp(Int_t numRanks, Int_t myRank, Int_t* col, Int_t* row, Int_t* plane, Int_t* side); diff --git a/test/lulesh/lulesh_tuple.h b/test/lulesh/lulesh_tuple.h deleted file mode 100644 index 9ad7412f..00000000 --- a/test/lulesh/lulesh_tuple.h +++ /dev/null @@ -1,610 +0,0 @@ -#if !defined(USE_MPI) -# error "You should specify USE_MPI=0 or USE_MPI=1 on the compile line" -#endif - - -// OpenMP will be compiled in if this flag is set to 1 AND the compiler beging -// used supports it (i.e. the _OPENMP symbol is defined) -#define USE_OMP 1 - -#if USE_MPI -#include -#endif - -#include - -/* - define one of these three symbols: - - SEDOV_SYNC_POS_VEL_NONE - SEDOV_SYNC_POS_VEL_EARLY - SEDOV_SYNC_POS_VEL_LATE -*/ - -#define SEDOV_SYNC_POS_VEL_EARLY 1 - -#include -#include - -//************************************************** -// Allow flexibility for arithmetic representations -//************************************************** - -#define MAX(a, b) ( ((a) > (b)) ? (a) : (b)) - - -// Precision specification -typedef float real4 ; -typedef double real8 ; -typedef long double real10 ; // 10 bytes on x86 - -typedef int Index_t ; // array subscript and loop index -typedef real8 Real_t ; // floating point representation -typedef int Int_t ; // integer representation - -enum { VolumeError = -1, QStopError = -2 } ; - -inline real4 SQRT(real4 arg) { return sqrtf(arg) ; } -inline real8 SQRT(real8 arg) { return sqrt(arg) ; } -inline real10 SQRT(real10 arg) { return sqrtl(arg) ; } - -inline real4 CBRT(real4 arg) { return cbrtf(arg) ; } -inline real8 CBRT(real8 arg) { return cbrt(arg) ; } -inline real10 CBRT(real10 arg) { return cbrtl(arg) ; } - -inline real4 FABS(real4 arg) { return fabsf(arg) ; } -inline real8 FABS(real8 arg) { return fabs(arg) ; } -inline real10 FABS(real10 arg) { return fabsl(arg) ; } - - -// Stuff needed for boundary conditions -// 2 BCs on each of 6 hexahedral faces (12 bits) -#define XI_M 0x00007 -#define XI_M_SYMM 0x00001 -#define XI_M_FREE 0x00002 -#define XI_M_COMM 0x00004 - -#define XI_P 0x00038 -#define XI_P_SYMM 0x00008 -#define XI_P_FREE 0x00010 -#define XI_P_COMM 0x00020 - -#define ETA_M 0x001c0 -#define ETA_M_SYMM 0x00040 -#define ETA_M_FREE 0x00080 -#define ETA_M_COMM 0x00100 - -#define ETA_P 0x00e00 -#define ETA_P_SYMM 0x00200 -#define ETA_P_FREE 0x00400 -#define ETA_P_COMM 0x00800 - -#define ZETA_M 0x07000 -#define ZETA_M_SYMM 0x01000 -#define ZETA_M_FREE 0x02000 -#define ZETA_M_COMM 0x04000 - -#define ZETA_P 0x38000 -#define ZETA_P_SYMM 0x08000 -#define ZETA_P_FREE 0x10000 -#define ZETA_P_COMM 0x20000 - -// MPI Message Tags -#define MSG_COMM_SBN 1024 -#define MSG_SYNC_POS_VEL 2048 -#define MSG_MONOQ 3072 - -#define MAX_FIELDS_PER_MPI_COMM 6 - -// Assume 128 byte coherence -// Assume Real_t is an "integral power of 2" bytes wide -#define CACHE_COHERENCE_PAD_REAL (128 / sizeof(Real_t)) - -#define CACHE_ALIGN_REAL(n) \ - (((n) + (CACHE_COHERENCE_PAD_REAL - 1)) & ~(CACHE_COHERENCE_PAD_REAL-1)) - -////////////////////////////////////////////////////// -// Primary data structure -////////////////////////////////////////////////////// - -/* - * The implementation of the data abstraction used for lulesh - * resides entirely in the Domain class below. You can change - * grouping and interleaving of fields here to maximize data layout - * efficiency for your underlying architecture or compiler. - * - * For example, fields can be implemented as STL objects or - * raw array pointers. As another example, individual fields - * m_x, m_y, m_z could be budled into - * - * struct { Real_t x, y, z ; } *m_coord ; - * - * allowing accessor functions such as - * - * "Real_t &x(Index_t idx) { return m_coord[idx].x ; }" - * "Real_t &y(Index_t idx) { return m_coord[idx].y ; }" - * "Real_t &z(Index_t idx) { return m_coord[idx].z ; }" - */ - -class Domain { - - public: - - // Constructor - Domain(Int_t numRanks, Index_t colLoc, - Index_t rowLoc, Index_t planeLoc, - Index_t nx, Int_t tp, Int_t nr, Int_t balance, Int_t cost); - - // - // ALLOCATION - // - - void AllocateNodePersistent(Int_t numNode) // Node-centered - { - m_coord.resize(numNode); // coordinates - - m_vel.resize(numNode); // velocities - - m_acc.resize(numNode); // accelerations - - m_force.resize(numNode); // forces - - m_nodalMass.resize(numNode); // mass - } - - void AllocateElemPersistent(Int_t numElem) // Elem-centered - { - m_nodelist.resize(8*numElem); - - // elem connectivities through face - m_faceToElem.resize(numElem); - - m_elemBC.resize(numElem); - - m_e.resize(numElem); - - m_pq.resize(numElem); - - m_qlqq.resize(numElem); - - m_vol.resize(numElem); - - m_delv.resize(numElem); - m_vdov.resize(numElem); - - m_arealg.resize(numElem); - - m_ss.resize(numElem); - - m_elemMass.resize(numElem); - } - - void AllocateGradients(Int_t numElem, Int_t allElem) - { - // Position gradients - m_delx_xi.resize(numElem) ; - m_delx_eta.resize(numElem) ; - m_delx_zeta.resize(numElem) ; - - // Velocity gradients - m_delv_xi.resize(allElem) ; - m_delv_eta.resize(allElem); - m_delv_zeta.resize(allElem) ; - } - - void DeallocateGradients() - { - m_delx_zeta.clear() ; - m_delx_eta.clear() ; - m_delx_xi.clear() ; - - m_delv_zeta.clear() ; - m_delv_eta.clear() ; - m_delv_xi.clear() ; - } - - void AllocateStrains(Int_t numElem) - { - m_dxx.resize(numElem) ; - m_dyy.resize(numElem) ; - m_dzz.resize(numElem) ; - } - - void DeallocateStrains() - { - m_dzz.clear() ; - m_dyy.clear() ; - m_dxx.clear() ; - } - - // - // ACCESSORS - // - - // Node-centered - - // Nodal coordinates - Real_t& x(Index_t idx) { return m_coord[idx].x ; } - Real_t& y(Index_t idx) { return m_coord[idx].y ; } - Real_t& z(Index_t idx) { return m_coord[idx].z ; } - - // Nodal velocities - Real_t& xd(Index_t idx) { return m_vel[idx].x ; } - Real_t& yd(Index_t idx) { return m_vel[idx].y ; } - Real_t& zd(Index_t idx) { return m_vel[idx].z ; } - - // Nodal accelerations - Real_t& xdd(Index_t idx) { return m_acc[idx].x ; } - Real_t& ydd(Index_t idx) { return m_acc[idx].y ; } - Real_t& zdd(Index_t idx) { return m_acc[idx].z ; } - - // Nodal forces - Real_t& fx(Index_t idx) { return m_force[idx].x ; } - Real_t& fy(Index_t idx) { return m_force[idx].y ; } - Real_t& fz(Index_t idx) { return m_force[idx].z ; } - - // Nodal mass - Real_t& nodalMass(Index_t idx) { return m_nodalMass[idx] ; } - - // Nodes on symmertry planes - Index_t symmX(Index_t idx) { return m_symmX[idx] ; } - Index_t symmY(Index_t idx) { return m_symmY[idx] ; } - Index_t symmZ(Index_t idx) { return m_symmZ[idx] ; } - bool symmXempty() { return m_symmX.empty(); } - bool symmYempty() { return m_symmY.empty(); } - bool symmZempty() { return m_symmZ.empty(); } - - // - // Element-centered - // - Index_t& regElemSize(Index_t idx) { return m_regElemSize[idx] ; } - Index_t& regNumList(Index_t idx) { return m_regNumList[idx] ; } - Index_t* regNumList() { return &m_regNumList[0] ; } - Index_t* regElemlist(Int_t r) { return m_regElemlist[r] ; } - Index_t& regElemlist(Int_t r, Index_t idx) { return m_regElemlist[r][idx] ; } - - Index_t* nodelist(Index_t idx) { return &m_nodelist[Index_t(8)*idx] ; } - - // elem connectivities through face - Index_t& lxim(Index_t idx) { return m_faceToElem[idx].lxim ; } - Index_t& lxip(Index_t idx) { return m_faceToElem[idx].lxip ; } - Index_t& letam(Index_t idx) { return m_faceToElem[idx].letam ; } - Index_t& letap(Index_t idx) { return m_faceToElem[idx].letap ; } - Index_t& lzetam(Index_t idx) { return m_faceToElem[idx].lzetam ; } - Index_t& lzetap(Index_t idx) { return m_faceToElem[idx].lzetap ; } - - // elem face symm/free-surface flag - Int_t& elemBC(Index_t idx) { return m_elemBC[idx] ; } - - // Principal strains - temporary - Real_t& dxx(Index_t idx) { return m_dxx[idx] ; } - Real_t& dyy(Index_t idx) { return m_dyy[idx] ; } - Real_t& dzz(Index_t idx) { return m_dzz[idx] ; } - - // Velocity gradient - temporary - Real_t& delv_xi(Index_t idx) { return m_delv_xi[idx] ; } - Real_t& delv_eta(Index_t idx) { return m_delv_eta[idx] ; } - Real_t& delv_zeta(Index_t idx) { return m_delv_zeta[idx] ; } - - // Position gradient - temporary - Real_t& delx_xi(Index_t idx) { return m_delx_xi[idx] ; } - Real_t& delx_eta(Index_t idx) { return m_delx_eta[idx] ; } - Real_t& delx_zeta(Index_t idx) { return m_delx_zeta[idx] ; } - - // Energy - Real_t& e(Index_t idx) { return m_e[idx] ; } - - // Pressure - Real_t& p(Index_t idx) { return m_pq[idx].p ; } - - // Artificial viscosity - Real_t& q(Index_t idx) { return m_pq[idx].q ; } - - // Linear term for q - Real_t& ql(Index_t idx) { return m_qlqq[idx].ql ; } - // Quadratic term for q - Real_t& qq(Index_t idx) { return m_qlqq[idx].qq ; } - - Real_t& delv(Index_t idx) { return m_delv[idx] ; } - - // Relative volume - Real_t& v(Index_t idx) { return m_vol[idx].v ; } - // Reference volume - Real_t& volo(Index_t idx) { return m_vol[idx].volo ; } - - // volume derivative over volume - Real_t& vdov(Index_t idx) { return m_vdov[idx] ; } - - // Element characteristic length - Real_t& arealg(Index_t idx) { return m_arealg[idx] ; } - - // Sound speed - Real_t& ss(Index_t idx) { return m_ss[idx] ; } - - // Element mass - Real_t& elemMass(Index_t idx) { return m_elemMass[idx] ; } - - Index_t nodeElemCount(Index_t idx) - { return m_nodeElemStart[idx+1] - m_nodeElemStart[idx] ; } - - Index_t *nodeElemCornerList(Index_t idx) - { return &m_nodeElemCornerList[m_nodeElemStart[idx]] ; } - - // Parameters - - // Cutoffs - Real_t u_cut() const { return m_u_cut ; } - Real_t e_cut() const { return m_e_cut ; } - Real_t p_cut() const { return m_p_cut ; } - Real_t q_cut() const { return m_q_cut ; } - Real_t v_cut() const { return m_v_cut ; } - - // Other constants (usually are settable via input file in real codes) - Real_t hgcoef() const { return m_hgcoef ; } - Real_t qstop() const { return m_qstop ; } - Real_t monoq_max_slope() const { return m_monoq_max_slope ; } - Real_t monoq_limiter_mult() const { return m_monoq_limiter_mult ; } - Real_t ss4o3() const { return m_ss4o3 ; } - Real_t qlc_monoq() const { return m_qlc_monoq ; } - Real_t qqc_monoq() const { return m_qqc_monoq ; } - Real_t qqc() const { return m_qqc ; } - - Real_t eosvmax() const { return m_eosvmax ; } - Real_t eosvmin() const { return m_eosvmin ; } - Real_t pmin() const { return m_pmin ; } - Real_t emin() const { return m_emin ; } - Real_t dvovmax() const { return m_dvovmax ; } - Real_t refdens() const { return m_refdens ; } - - // Timestep controls, etc... - Real_t& time() { return m_time ; } - Real_t& deltatime() { return m_deltatime ; } - Real_t& deltatimemultlb() { return m_deltatimemultlb ; } - Real_t& deltatimemultub() { return m_deltatimemultub ; } - Real_t& stoptime() { return m_stoptime ; } - Real_t& dtcourant() { return m_dtcourant ; } - Real_t& dthydro() { return m_dthydro ; } - Real_t& dtmax() { return m_dtmax ; } - Real_t& dtfixed() { return m_dtfixed ; } - - Int_t& cycle() { return m_cycle ; } - Index_t& numRanks() { return m_numRanks ; } - - Index_t& colLoc() { return m_colLoc ; } - Index_t& rowLoc() { return m_rowLoc ; } - Index_t& planeLoc() { return m_planeLoc ; } - Index_t& tp() { return m_tp ; } - - Index_t& sizeX() { return m_sizeX ; } - Index_t& sizeY() { return m_sizeY ; } - Index_t& sizeZ() { return m_sizeZ ; } - Index_t& numReg() { return m_numReg ; } - Int_t& cost() { return m_cost ; } - Index_t& numElem() { return m_numElem ; } - Index_t& numNode() { return m_numNode ; } - - Index_t& maxPlaneSize() { return m_maxPlaneSize ; } - Index_t& maxEdgeSize() { return m_maxEdgeSize ; } - - // - // MPI-Related additional data - // - -#if USE_MPI - // Communication Work space - Real_t *commDataSend ; - Real_t *commDataRecv ; - - // Maximum number of block neighbors - MPI_Request recvRequest[26] ; // 6 faces + 12 edges + 8 corners - MPI_Request sendRequest[26] ; // 6 faces + 12 edges + 8 corners -#endif - - private: - - void BuildMesh(Int_t nx, Int_t edgeNodes, Int_t edgeElems); - void SetupThreadSupportStructures(); - void CreateRegionIndexSets(Int_t nreg, Int_t balance); - void SetupCommBuffers(Int_t edgeNodes); - void SetupSymmetryPlanes(Int_t edgeNodes); - void SetupElementConnectivities(Int_t edgeElems); - void SetupBoundaryConditions(Int_t edgeElems); - - // - // IMPLEMENTATION - // - - /* Node-centered */ - - struct Tuple3 { - Real_t x, y, z ; - } ; - - std::vector m_coord ; /* coordinates */ - - std::vector m_vel ; /* velocities */ - - std::vector m_acc ; /* accelerations */ - - std::vector m_force ; /* forces */ - - std::vector m_nodalMass ; /* mass */ - - std::vector m_symmX ; /* symmetry plane nodesets */ - std::vector m_symmY ; - std::vector m_symmZ ; - - // Element-centered - - // Region information - Int_t m_numReg ; - Int_t m_cost; //imbalance cost - Index_t *m_regElemSize ; // Size of region sets - Index_t *m_regNumList ; // Region number per domain element - Index_t **m_regElemlist ; // region indexset - - std::vector m_nodelist ; /* elemToNode connectivity */ - - struct FaceElemConn { - Index_t lxim, lxip, letam, letap, lzetam, lzetap ; - } ; - - std::vector m_faceToElem ; /* element conn across faces */ - - std::vector m_elemBC ; /* symmetry/free-surface flags for each elem face */ - - std::vector m_dxx ; /* principal strains -- temporary */ - std::vector m_dyy ; - std::vector m_dzz ; - - std::vector m_delv_xi ; /* velocity gradient -- temporary */ - std::vector m_delv_eta ; - std::vector m_delv_zeta ; - - std::vector m_delx_xi ; /* coordinate gradient -- temporary */ - std::vector m_delx_eta ; - std::vector m_delx_zeta ; - - std::vector m_e ; /* energy */ - - struct Pcomponents { - Real_t p, q ; - } ; - - std::vector m_pq ; /* pressure and artificial viscosity */ - - struct Qcomponents { - Real_t ql, qq ; - } ; - - std::vector m_qlqq ; /* linear and quadratic terms for q */ - - struct Volume { - Real_t v, volo ; - } ; - - std::vector m_vol ; /* relative and reference volume */ - - std::vector m_vnew ; /* new relative volume -- temporary */ - std::vector m_delv ; /* m_vnew - m_v */ - std::vector m_vdov ; /* volume derivative over volume */ - - std::vector m_arealg ; /* characteristic length of an element */ - - std::vector m_ss ; /* "sound speed" */ - - std::vector m_elemMass ; /* mass */ - - // Cutoffs (treat as constants) - const Real_t m_e_cut ; // energy tolerance - const Real_t m_p_cut ; // pressure tolerance - const Real_t m_q_cut ; // q tolerance - const Real_t m_v_cut ; // relative volume tolerance - const Real_t m_u_cut ; // velocity tolerance - - // Other constants (usually setable, but hardcoded in this proxy app) - - const Real_t m_hgcoef ; // hourglass control - const Real_t m_ss4o3 ; - const Real_t m_qstop ; // excessive q indicator - const Real_t m_monoq_max_slope ; - const Real_t m_monoq_limiter_mult ; - const Real_t m_qlc_monoq ; // linear term coef for q - const Real_t m_qqc_monoq ; // quadratic term coef for q - const Real_t m_qqc ; - const Real_t m_eosvmax ; - const Real_t m_eosvmin ; - const Real_t m_pmin ; // pressure floor - const Real_t m_emin ; // energy floor - const Real_t m_dvovmax ; // maximum allowable volume change - const Real_t m_refdens ; // reference density - - // Variables to keep track of timestep, simulation time, and cycle - Real_t m_dtcourant ; // courant constraint - Real_t m_dthydro ; // volume change constraint - Int_t m_cycle ; // iteration count for simulation - Real_t m_dtfixed ; // fixed time increment - Real_t m_time ; // current time - Real_t m_deltatime ; // variable time increment - Real_t m_deltatimemultlb ; - Real_t m_deltatimemultub ; - Real_t m_dtmax ; // maximum allowable time increment - Real_t m_stoptime ; // end time for simulation - - - Int_t m_numRanks ; - - Index_t m_colLoc ; - Index_t m_rowLoc ; - Index_t m_planeLoc ; - Index_t m_tp ; - - Index_t m_sizeX ; - Index_t m_sizeY ; - Index_t m_sizeZ ; - Index_t m_numElem ; - Index_t m_numNode ; - - Index_t m_maxPlaneSize ; - Index_t m_maxEdgeSize ; - - // OMP hack - Index_t *m_nodeElemStart ; - Index_t *m_nodeElemCornerList ; - - // Used in setup - Index_t m_rowMin, m_rowMax; - Index_t m_colMin, m_colMax; - Index_t m_planeMin, m_planeMax ; - -} ; - -typedef Real_t &(Domain::* Domain_member )(Index_t) ; - -struct cmdLineOpts { - Int_t its; // -i - Int_t nx; // -s - Int_t numReg; // -r - Int_t numFiles; // -f - Int_t showProg; // -p - Int_t quiet; // -q - Int_t viz; // -v - Int_t cost; // -c - Int_t balance; // -b -}; - - - -// Function Prototypes - -// lulesh-par -Real_t CalcElemVolume( const Real_t x[8], - const Real_t y[8], - const Real_t z[8]); - -// lulesh-util -void ParseCommandLineOptions(int argc, char *argv[], - Int_t myRank, struct cmdLineOpts *opts); -void VerifyAndWriteFinalOutput(Real_t elapsed_time, - Domain& locDom, - Int_t nx, - Int_t numRanks); - -// lulesh-viz -void DumpToVisit(Domain& domain, int numFiles, int myRank, int numRanks); - -// lulesh-comm -void CommRecv(Domain& domain, Int_t msgType, Index_t xferFields, - Index_t dx, Index_t dy, Index_t dz, - bool doRecv, bool planeOnly); -void CommSend(Domain& domain, Int_t msgType, - Index_t xferFields, Domain_member *fieldData, - Index_t dx, Index_t dy, Index_t dz, - bool doSend, bool planeOnly); -void CommSBN(Domain& domain, Int_t xferFields, Domain_member *fieldData); -void CommSyncPosVel(Domain& domain); -void CommMonoQ(Domain& domain); - -// lulesh-init -void InitMeshDecomp(Int_t numRanks, Int_t myRank, - Int_t *col, Int_t *row, Int_t *plane, Int_t *side); diff --git a/test/pass/arrays/01_simple_array.c b/test/pass/arrays/01_simple_array.c index 7b6b43b6..b4e8010b 100644 --- a/test/pass/arrays/01_simple_array.c +++ b/test/pass/arrays/01_simple_array.c @@ -1,10 +1,20 @@ // clang-format off -// RUN: clang -S -emit-llvm %s -o - | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -typeart-alloca -S 2>&1 | FileCheck %s // clang-format on void test() { int a[100]; } +// CHECK: @test() +// CHECK-NEXT: %__ta_alloca_counter = alloca i32 +// CHECK-NEXT: store i32 0, i32* %__ta_alloca_counter + +// CHECK: [[POINTER:%[0-9]+]] = alloca [100 x i32] +// CHECK-NEXT: [[POINTER2:%[0-9]+]] = bitcast [100 x i32]* [[POINTER]] to i8* +// CHECK-NEXT: call void @__typeart_alloc_stack(i8* [[POINTER2]], i32 2, i64 100) + +// CHECK: call void @__typeart_leave_scope(i32 %__ta_counter_load) + // CHECK: Malloc{{[ ]*}}:{{[ ]*}}0 // CHECK: Free{{[ ]*}}:{{[ ]*}}0 // CHECK: Alloca{{[ ]*}}:{{[ ]*}}1 diff --git a/test/pass/arrays/02_array_to_pointer.c b/test/pass/arrays/02_array_to_pointer.c index aef6d69a..79a59545 100644 --- a/test/pass/arrays/02_array_to_pointer.c +++ b/test/pass/arrays/02_array_to_pointer.c @@ -1,11 +1,21 @@ // clang-format off -// RUN: clang -S -emit-llvm %s -o - | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -typeart-alloca -S 2>&1 | FileCheck %s // clang-format on void test() { int a[100]; - int* pa = a; + int* pa = a; // TODO: Tracking this value should not be necessary? } +// CHECK: @test() +// CHECK-NEXT: %__ta_alloca_counter = alloca i32 +// CHECK-NEXT: store i32 0, i32* %__ta_alloca_counter + +// CHECK: [[POINTER:%[0-9]+]] = alloca [100 x i32] +// CHECK-NEXT: [[POINTER2:%[0-9]+]] = bitcast [100 x i32]* [[POINTER]] to i8* +// CHECK-NEXT: call void @__typeart_alloc_stack(i8* [[POINTER2]], i32 2, i64 100) + +// CHECK: call void @__typeart_leave_scope(i32 %__ta_counter_load) + // CHECK: Malloc{{[ ]*}}:{{[ ]*}}0 // CHECK: Free{{[ ]*}}:{{[ ]*}}0 -// CHECK: Alloca{{[ ]*}}:{{[ ]*}}1 +// CHECK: Alloca{{[ ]*}}:{{[ ]*}}2 diff --git a/test/pass/arrays/03_multidim_array.c b/test/pass/arrays/03_multidim_array.c index 47ca04c6..908bd764 100644 --- a/test/pass/arrays/03_multidim_array.c +++ b/test/pass/arrays/03_multidim_array.c @@ -1,5 +1,5 @@ // clang-format off -// RUN: clang -S -emit-llvm %s -o - | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -typeart-alloca -S 2>&1 | FileCheck %s // clang-format on void test() { const int n = 64; @@ -10,7 +10,12 @@ void test() { int d[n][m][n]; } +// CHECK: call void @__typeart_alloc_stack(i8* %{{[0-9]+}}, i32 2, i64 64) +// CHECK: call void @__typeart_alloc_stack(i8* %{{[0-9]+}}, i32 2, i64 4096) +// CHECK: call void @__typeart_alloc_stack(i8* %{{[0-9]+}}, i32 2, i64 8192) +// CHECK: call void @__typeart_alloc_stack(i8* %{{[0-9]+}}, i32 2, i64 524288) + // CHECK-NOT: Encountered unhandled type // CHECK: Malloc{{[ ]*}}:{{[ ]*}}0 // CHECK: Free{{[ ]*}}:{{[ ]*}}0 -// CHECK: Alloca{{[ ]*}}:{{[ ]*}}4 +// CHECK: Alloca{{[ ]*}}:{{[ ]*}}6 diff --git a/test/pass/arrays/04_vla.c b/test/pass/arrays/04_vla.c index 83d910af..b5845283 100644 --- a/test/pass/arrays/04_vla.c +++ b/test/pass/arrays/04_vla.c @@ -1,5 +1,5 @@ // clang-format off -// RUN: clang -S -emit-llvm %s -o - | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -typeart-alloca -alloca-array-only=true -S 2>&1 | FileCheck %s // clang-format on void test(int n) { int a[n]; @@ -8,6 +8,7 @@ void test(int n) { } // CHECK-NOT: Encountered unhandled type -// CHECK: Malloc{{[ ]*}}:{{[ ]*}}0 -// CHECK: Free{{[ ]*}}:{{[ ]*}}0 -// CHECK: Alloca{{[ ]*}}:{{[ ]*}}3 +// CHECK: TypeArtPass [Heap & Stack] +// CHECK-NEXT: Malloc{{[ ]*}}:{{[ ]*}}0 +// CHECK-NEXT: Free{{[ ]*}}:{{[ ]*}}0 +// CHECK-NEXT: Alloca{{[ ]*}}:{{[ ]*}}3 diff --git a/test/pass/arrays/05_vector.c b/test/pass/arrays/05_vector.c index aab64f98..b0e2796d 100644 --- a/test/pass/arrays/05_vector.c +++ b/test/pass/arrays/05_vector.c @@ -1,5 +1,5 @@ // clang-format off -// RUN: clang -S -emit-llvm %s -o - | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -typeart-alloca -S 2>&1 | FileCheck %s // clang-format on typedef float float2 __attribute__((ext_vector_type(2))); @@ -10,6 +10,8 @@ void test() { // CHECK-NOT Type is not supported: <2 x float> // CHECK: alloca <2 x float>, align 8 +// CHECK: call void @__typeart_alloc_stack(i8* %2, i32 25{{[0-9]}}, i64 1) + // CHECK: Malloc{{[ ]*}}:{{[ ]*}}0 // CHECK: Free{{[ ]*}}:{{[ ]*}}0 // CHECK: Alloca{{[ ]*}}:{{[ ]*}}1 diff --git a/test/pass/filter/01_alloca.c b/test/pass/filter/01_alloca.c index aec5c932..6388daae 100644 --- a/test/pass/filter/01_alloca.c +++ b/test/pass/filter/01_alloca.c @@ -7,7 +7,7 @@ extern void bar(int x); extern void ebar(int* x); -void MPI_send(int* a) { +void MPI_send(void* a) { (void)a; return; } diff --git a/test/pass/filter/01_alloca.llin b/test/pass/filter/01_alloca.llin index 05777a36..f0c84608 100644 --- a/test/pass/filter/01_alloca.llin +++ b/test/pass/filter/01_alloca.llin @@ -1,123 +1,155 @@ -; RUN: cat %s | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -typeart-alloca -alloca-array-only=false -S 2>&1 | FileCheck %s -; RUN: cat %s | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -typeart-alloca -alloca-array-only=false -call-filter -S 2>&1 | FileCheck %s -check-prefix=CHECK-FILTER - -; ModuleID = '' -source_filename = "test/pass/filter/01_alloca.c" -target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" -target triple = "x86_64-pc-linux-gnu" - -; Function Attrs: noinline nounwind uwtable -define void @MPI_send(i32*) #0 { - %2 = alloca i32*, align 8 - store i32* %0, i32** %2, align 8 - %3 = load i32*, i32** %2, align 8 +; RUN: cat %s | %apply-typeart -typeart-alloca -S 2>&1 | FileCheck %s +; RUN: cat %s | %apply-typeart -typeart-alloca -call-filter -call-filter-impl=deprecated::default -S 2>&1 | FileCheck %s -check-prefix=CHECK-FILTER +; RUN: cat %s | %apply-typeart -typeart-alloca -call-filter -S 2>&1 | FileCheck %s -check-prefix=CHECK-FILTER-EXP + +; ModuleID = '01_alloca.c' +source_filename = "01_alloca.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @MPI_send(i8* %a) { +entry: + %a.addr = alloca i8*, align 8 + store i8* %a, i8** %a.addr, align 8 + %0 = load i8*, i8** %a.addr, align 8 ret void } -; Function Attrs: noinline nounwind uwtable -define void @foo_bar(i32*) #0 { - %2 = alloca i32*, align 8 - store i32* %0, i32** %2, align 8 - %3 = load i32*, i32** %2, align 8 - store i32 1, i32* %3, align 4 - %4 = load i32*, i32** %2, align 8 +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @foo_bar(i32* %a) #0 { +entry: + %a.addr = alloca i32*, align 8 + store i32* %a, i32** %a.addr, align 8 + %0 = load i32*, i32** %a.addr, align 8 + store i32 1, i32* %0, align 4 + %1 = load i32*, i32** %a.addr, align 8 ret void } -; Function Attrs: noinline nounwind uwtable -define void @foo_bar2(i32*, i32*) #0 { - %3 = alloca i32*, align 8 - %4 = alloca i32*, align 8 - store i32* %0, i32** %3, align 8 - store i32* %1, i32** %4, align 8 - %5 = load i32*, i32** %3, align 8 - store i32 1, i32* %5, align 4 - %6 = load i32*, i32** %3, align 8 - %7 = load i32*, i32** %4, align 8 - call void @MPI_send(i32* %7) +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @foo_bar2(i32* %a, i32* %b) #0 { +entry: + %a.addr = alloca i32*, align 8 + %b.addr = alloca i32*, align 8 + store i32* %a, i32** %a.addr, align 8 + store i32* %b, i32** %b.addr, align 8 + %0 = load i32*, i32** %a.addr, align 8 + store i32 1, i32* %0, align 4 + %1 = load i32*, i32** %a.addr, align 8 + %2 = load i32*, i32** %b.addr, align 8 + %3 = bitcast i32* %2 to i8* + call void @MPI_send(i8* %3) ret void } -; Function Attrs: noinline nounwind uwtable -define void @foo_bar3() #0 { - %1 = alloca i32*, align 8 - %2 = call noalias i8* @malloc(i64 40) #3 - %3 = bitcast i8* %2 to i32* - store i32* %3, i32** %1, align 8 - %4 = load i32*, i32** %1, align 8 - %5 = load i32*, i32** %1, align 8 - call void @MPI_send(i32* %5) + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @foo_bar3() #0 { +entry: + %a = alloca i32*, align 8 + %call = call noalias i8* @malloc(i64 40) #3 + %0 = bitcast i8* %call to i32* + store i32* %0, i32** %a, align 8 + %1 = load i32*, i32** %a, align 8 + %2 = load i32*, i32** %a, align 8 + %3 = bitcast i32* %2 to i8* + call void @MPI_send(i8* %3) ret void } ; Function Attrs: nounwind -declare noalias i8* @malloc(i64) #1 +declare dso_local noalias i8* @malloc(i64) #1 -; CHECK-FILTER: define void @foo() +; Deprecated standard filter: +; CHECK-FILTER: define dso_local void @foo() ; CHECK-FILTER: %__ta_alloca_counter = alloca i{{(64|32)}} ; CHECK-FILTER-NEXT: store i{{(64|32)}} 0, i{{(64|32)}}* %__ta_alloca_counter -; CHECK-FILTER: %4 = bitcast i32** %3 to i8* -; CHECK-FILTER-NEXT call void @__typeart_alloc_stack(i8* %4, i32 10, i64 1) -; CHECK-FILTER: %6 = bitcast i32* %5 to i8* -; CHECK-FILTER-NEXT call void @__typeart_alloc_stack(i8* %6, i32 2, i64 1) -; CHECK-FILTER: %8 = bitcast i32* %7 to i8* -; CHECK-FILTER-NEXT call void @__typeart_alloc_stack(i8* %8, i32 2, i64 1) -; CHECK-FILTER: %14 = load i{{(64|32)}}, i{{(64|32)}}* %__ta_alloca_counter -; CHECK-FILTER-NEXT: %15 = add i{{(64|32)}} 3, %14 +; CHECK-FILTER: %0 = bitcast i32** %c to i8* +; CHECK-FILTER-NEXT call void @__typeart_alloc_stack(i8* %0, i32 10, i64 1) +; CHECK-FILTER: %1 = bitcast i32* %d to i8* +; CHECK-FILTER-NEXT call void @__typeart_alloc_stack(i8* %1, i32 2, i64 1) +; CHECK-FILTER: %2 = bitcast i32* %x to i8* +; CHECK-FILTER-NEXT call void @__typeart_alloc_stack(i8* %2, i32 2, i64 1) +; CHECK-FILTER: %9 = load i{{(64|32)}}, i{{(64|32)}}* %__ta_alloca_counter +; CHECK-FILTER-NEXT: %10 = add i{{(64|32)}} 3, %9 ; CHECK-FILTER: call void @__typeart_leave_scope(i{{(64|32)}} %__ta_counter_load) -; CHECK: define void @foo() + +; New filter, TODO: %0 = bitcast should be filterable with proper data-flow tracking +; CHECK-FILTER-EXP: define dso_local void @foo() +; CHECK-FILTER-EXP: %__ta_alloca_counter = alloca i{{(64|32)}} +; CHECK-FILTER-EXP-NEXT: store i{{(64|32)}} 0, i{{(64|32)}}* %__ta_alloca_counter +; CHECK-FILTER-EXP: %0 = bitcast i32* %a to i8* +; CHECK-FILTER-EXP-NEXT: call void @__typeart_alloc_stack(i8* %0, i32 2, i64 1) +; CHECK-FILTER-EXP: %1 = bitcast i32** %c to i8* +; CHECK-FILTER-EXP-NEXT call void @__typeart_alloc_stack(i8* %1, i32 10, i64 1) +; CHECK-FILTER-EXP: %2 = bitcast i32* %d to i8* +; CHECK-FILTER-EXP-NEXT call void @__typeart_alloc_stack(i8* %2, i32 2, i64 1) +; CHECK-FILTER-EXP: %3 = bitcast i32* %x to i8* +; CHECK-FILTER-EXP-NEXT call void @__typeart_alloc_stack(i8* %3, i32 2, i64 1) +; CHECK-FILTER-EXP: %10 = load i{{(64|32)}}, i{{(64|32)}}* %__ta_alloca_counter +; CHECK-FILTER-EXP-NEXT: %11 = add i{{(64|32)}} 4, %10 +; CHECK-FILTER-EXP: call void @__typeart_leave_scope(i{{(64|32)}} %__ta_counter_load) + +; No filter: +; CHECK: define dso_local void @foo() ; CHECK: %__ta_alloca_counter = alloca i{{(64|32)}} ; CHECK-NEXT: store i{{(64|32)}} 0, i{{(64|32)}}* %__ta_alloca_counter -; CHECK: %2 = bitcast i32* %1 to i8* -; CHECK-NEXT: call void @__typeart_alloc_stack(i8* %2, i32 2, i64 1) -; CHECK: %4 = bitcast i32* %3 to i8* +; CHECK: %0 = bitcast i32* %a to i8* +; CHECK-NEXT: call void @__typeart_alloc_stack(i8* %0, i32 2, i64 1) +; CHECK: %1 = bitcast i32* %b to i8* +; CHECK-NEXT: call void @__typeart_alloc_stack(i8* %1, i32 2, i64 1) +; CHECK: %2 = bitcast i32** %c to i8* +; CHECK-NEXT: call void @__typeart_alloc_stack(i8* %2, i32 10, i64 1) +; CHECK: %3 = bitcast i32* %d to i8* +; CHECK-NEXT: call void @__typeart_alloc_stack(i8* %3, i32 2, i64 1) +; CHECK: %4 = bitcast i32* %x to i8* ; CHECK-NEXT: call void @__typeart_alloc_stack(i8* %4, i32 2, i64 1) -; CHECK: %6 = bitcast i32** %5 to i8* -; CHECK-NEXT: call void @__typeart_alloc_stack(i8* %6, i32 10, i64 1) -; CHECK: %8 = bitcast i32* %7 to i8* -; CHECK-NEXT: call void @__typeart_alloc_stack(i8* %8, i32 2, i64 1) -; CHECK: %10 = bitcast i32* %9 to i8* -; CHECK-NEXT: call void @__typeart_alloc_stack(i8* %10, i32 2, i64 1) -; CHECK: %16 = load i{{(64|32)}}, i{{(64|32)}}* %__ta_alloca_counter -; CHECK-NEXT: %17 = add i{{(64|32)}} 5, %16 +; CHECK: %11 = load i{{(64|32)}}, i{{(64|32)}}* %__ta_alloca_counter +; CHECK-NEXT: %12 = add i{{(64|32)}} 5, %11 ; CHECK: call void @__typeart_leave_scope(i{{(64|32)}} %__ta_counter_load) -; Function Attrs: noinline nounwind uwtable -define void @foo() #0 { - %1 = alloca i32, align 4 - %2 = alloca i32, align 4 - %3 = alloca i32*, align 8 - %4 = alloca i32, align 4 - %5 = alloca i32, align 4 - store i32 1, i32* %1, align 4 - %6 = load i32, i32* %1, align 4 - store i32 %6, i32* %2, align 4 - store i32* %1, i32** %3, align 8 - %7 = load i32, i32* %1, align 4 - store i32 %7, i32* %4, align 4 - %8 = load i32, i32* %1, align 4 - store i32 %8, i32* %5, align 4 - %9 = load i32*, i32** %3, align 8 - call void @MPI_send(i32* %9) - %10 = load i32, i32* %4, align 4 - call void @bar(i32 %10) - call void @ebar(i32* %4) - call void @foo_bar(i32* %1) - call void @foo_bar2(i32* %1, i32* %5) + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @foo() #0 { +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %c = alloca i32*, align 8 + %d = alloca i32, align 4 + %x = alloca i32, align 4 + store i32 1, i32* %a, align 4 + %0 = load i32, i32* %a, align 4 + store i32 %0, i32* %b, align 4 + store i32* %a, i32** %c, align 8 + %1 = load i32, i32* %a, align 4 + store i32 %1, i32* %d, align 4 + %2 = load i32, i32* %a, align 4 + store i32 %2, i32* %x, align 4 + %3 = load i32*, i32** %c, align 8 + %4 = bitcast i32* %3 to i8* + call void @MPI_send(i8* %4) + %5 = load i32, i32* %d, align 4 + call void @bar(i32 %5) + call void @ebar(i32* %d) + call void @foo_bar(i32* %a) + call void @foo_bar2(i32* %a, i32* %x) ret void } -declare void @bar(i32) #2 -declare void @ebar(i32*) #2 +declare dso_local void @bar(i32) #2 -attributes #0 = { noinline nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +declare dso_local void @ebar(i32*) #2 + +attributes #0 = { noinline nounwind optnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } attributes #3 = { nounwind } !llvm.module.flags = !{!0} !llvm.ident = !{!1} !0 = !{i32 1, !"wchar_size", i32 4} -!1 = !{!"clang version 6.0.0 (tags/RELEASE_600/final 332839)"} +!1 = !{!"clang version 10.0.0 "} + diff --git a/test/pass/filter/02_recursion.c b/test/pass/filter/02_recursion.c index ff92b1b3..b2c1b257 100644 --- a/test/pass/filter/02_recursion.c +++ b/test/pass/filter/02_recursion.c @@ -1,6 +1,7 @@ // Template for recursion.ll.in // clang-format off -// RUN: clang -S -emit-llvm %s -o - | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -typeart-alloca -alloca-array-only=false -call-filter -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -typeart-alloca -call-filter -call-filter-impl=deprecated::default -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -typeart-alloca -call-filter -S 2>&1 | FileCheck %s // clang-format on void bar(int* x) { } @@ -12,4 +13,5 @@ void foo(int x) { } } -// CHECK: % call filtered : 100.0 +// CHECK: MemInstFinderPass +// CHECK: Stack call filtered %{{[ :]+}}100.0 diff --git a/test/pass/filter/03_globals.c b/test/pass/filter/03_globals.c index d86b0b4b..48f974ab 100644 --- a/test/pass/filter/03_globals.c +++ b/test/pass/filter/03_globals.c @@ -1,5 +1,6 @@ // clang-format off -// RUN: clang -S -emit-llvm %s -o - | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -typeart-alloca -alloca-array-only=false -call-filter -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -typeart-alloca -call-filter -call-filter-impl=deprecated::default -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -typeart-alloca -call-filter -S 2>&1 | FileCheck %s // clang-format on int a; @@ -10,8 +11,8 @@ void foo() { bar(&a); } -// CHECK: Global : 2.0 -// CHECK: Global total filtered : 1.0 -// CHECK: Global Call Filtered : 1.0 -// CHECK: % global call filtered : 50.0 -// CHECK: % global filtered : 50.0 +// CHECK: MemInstFinderPass +// Global : 2 +// Global filter total : 1 +// Global call filtered % : 50.00 +// Global filtered % : 50.00 \ No newline at end of file diff --git a/test/pass/filter/04_cg.c b/test/pass/filter/04_cg.c new file mode 100644 index 00000000..8a41f4ab --- /dev/null +++ b/test/pass/filter/04_cg.c @@ -0,0 +1,23 @@ +// clang-format off +// RUN: %c-to-llvm %s | %apply-typeart -typeart-alloca -call-filter -call-filter-impl=cg -call-filter-cg-file=%p/04_cg.ipcg -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -typeart-alloca -call-filter -call-filter-impl=deprecated::default -S 2>&1 | FileCheck %s --check-prefix=CHECK-default +// RUN: %c-to-llvm %s | %apply-typeart -typeart-alloca -call-filter -S 2>&1 | FileCheck %s --check-prefix=CHECK-default +// clang-format on + +extern void bar(int* ptr); // reaches MPI, see 04_cg.ipcg +extern void aar(int* ptr); // does not reach MPI + +void foo() { + int a, b; + bar(&a); + aar(&b); +} +// CG: +// CHECK: > Stack Memory +// CHECK-NEXT: Alloca : 2.00 +// CHECK-NEXT: Stack call filtered % : 50.00 + +// Standard filter +// CHECK-default: > Stack Memory +// CHECK-default-NEXT: Alloca : 2.00 +// CHECK-default-NEXT: Stack call filtered % : 0.00 \ No newline at end of file diff --git a/test/pass/filter/04_cg.ipcg b/test/pass/filter/04_cg.ipcg new file mode 100644 index 00000000..ba0371ab --- /dev/null +++ b/test/pass/filter/04_cg.ipcg @@ -0,0 +1,38 @@ +{ + "bar": { + "callees": ["MPI_Send"], + "doesOverride": false, + "hasBody": false, + "isVirtual": false, + "numStatements": 0, + "overriddenBy": [], + "overriddenFunctions": [], + "parents": [ + "foo" + ] + }, + "aar": { + "callees": [], + "doesOverride": false, + "hasBody": false, + "isVirtual": false, + "numStatements": 0, + "overriddenBy": [], + "overriddenFunctions": [], + "parents": [ + "foo" + ] + }, + "foo": { + "callees": [ + "bar" + ], + "doesOverride": false, + "hasBody": true, + "isVirtual": false, + "numStatements": 1, + "overriddenBy": [], + "overriddenFunctions": [], + "parents": [] + } +} \ No newline at end of file diff --git a/test/pass/filter/05_cg.ipcg b/test/pass/filter/05_cg.ipcg new file mode 100644 index 00000000..b3f54cd8 --- /dev/null +++ b/test/pass/filter/05_cg.ipcg @@ -0,0 +1,13 @@ +{ + "foo": { + "callees": [ + "MPI_Mock", "MPI_Send" + ], + "doesOverride": false, + "hasBody": true, + "isVirtual": false, + "overriddenBy": [], + "overriddenFunctions": [], + "parents": [] + } +} \ No newline at end of file diff --git a/test/pass/filter/05_correlate_sig.c b/test/pass/filter/05_correlate_sig.c new file mode 100644 index 00000000..a05f0e53 --- /dev/null +++ b/test/pass/filter/05_correlate_sig.c @@ -0,0 +1,34 @@ +// clang-format off +// RUN: %c-to-llvm %s | %apply-typeart -typeart-alloca -call-filter -call-filter-impl=deprecated::default -call-filter-deep=true -S 2>&1 | FileCheck %s --check-prefix=CHECK-default +// RUN: %c-to-llvm %s | %apply-typeart -typeart-alloca -call-filter -S 2>&1 | FileCheck %s --check-prefix=CHECK-exp-default +// RUN: %c-to-llvm %s | %apply-typeart -typeart-alloca -call-filter -call-filter-impl=cg -call-filter-cg-file=%p/05_cg.ipcg -S 2>&1 | FileCheck %s --check-prefix=CHECK-exp-cg +// clang-format on + +extern void MPI_Mock(int, int, int); +extern void MPI_Send(void*, int); +void foo() { + int a = 0; + int b = 1; + int c = 2; + int d = 3; + int e = 4; + // no (void*), so we assume benign (with deep analysis) + MPI_Mock(a, b, c); + // Analysis should filter d, but not e... + MPI_Send((void*)d, e); +} + +// Standard filter +// CHECK-default: > Stack Memory +// CHECK-default-NEXT: Alloca : 5.00 +// CHECK-default-NEXT: Stack call filtered % : 60.00 + +// Standard experimental filter +// CHECK-exp-default: > Stack Memory +// CHECK-exp-default-NEXT: Alloca : 5.00 +// CHECK-exp-default-NEXT: Stack call filtered % : 80.00 + +// CG experimental filter +// CHECK-exp-cg: > Stack Memory +// CHECK-exp-cg-NEXT: Alloca : 5.00 +// CHECK-exp-cg-NEXT: Stack call filtered % : 80.00 \ No newline at end of file diff --git a/test/pass/filter/06_empty.c b/test/pass/filter/06_empty.c new file mode 100644 index 00000000..2e5bdca2 --- /dev/null +++ b/test/pass/filter/06_empty.c @@ -0,0 +1,23 @@ +// clang-format off +// RUN: %c-to-llvm %s | %apply-typeart -typeart-alloca -call-filter -call-filter-impl=deprecated::default -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -typeart-alloca -call-filter -S 2>&1 | FileCheck %s +// clang-format on + +extern int d; + +void empty() { + int a = 1; + int b = 2; + int c = 3; + + if (d > c) { + b = a * c; + } else { + b = c * c; + } +} + +// Standard filter +// CHECK: > Stack Memory +// CHECK-NEXT: Alloca : 3.00 +// CHECK-NEXT: Stack call filtered % : 100.00 \ No newline at end of file diff --git a/test/pass/filter/07_lulesh_mock.cpp b/test/pass/filter/07_lulesh_mock.cpp new file mode 100644 index 00000000..8d6c1eba --- /dev/null +++ b/test/pass/filter/07_lulesh_mock.cpp @@ -0,0 +1,103 @@ +// clang-format off +// RUN: %c-to-llvm -fno-discard-value-names %s | %apply-typeart -typeart-alloca -call-filter -call-filter-impl=deprecated::default -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm -fno-discard-value-names %s | opt -O3 -S | %apply-typeart -typeart-alloca -call-filter -call-filter-impl=deprecated::default -S 2>&1 | FileCheck %s --check-prefix=CHECK-opt +// RUN: %c-to-llvm -fno-discard-value-names %s | %apply-typeart -typeart-alloca -call-filter -S 2>&1 | FileCheck %s --check-prefix=CHECK-exp-default +// RUN: %c-to-llvm -fno-discard-value-names %s | opt -O3 -S | %apply-typeart -typeart-alloca -call-filter -S 2>&1 | FileCheck %s --check-prefix=CHECK-exp-default-opt +// clang-format on + +using Real_t = double; +using Int_t = int; + +#define real_f(name) \ + Real_t _##name; \ + Real_t& name() { \ + return _##name; \ + } + +#define int_f(name) \ + Int_t _##name; \ + Int_t& name() { \ + return _##name; \ + } + +struct Domain { + real_f(stoptime); + real_f(time); + real_f(dtfixed); + real_f(deltatime); + real_f(dtcourant); + real_f(dthydro); + real_f(deltatimemultub); + real_f(deltatimemultlb); + real_f(dtmax); + int_f(cycle); +}; + +extern void MPI_Allreduce(void*, void*, int); + +void TimeIncrement(Domain& domain) { + Real_t targetdt = domain.stoptime() - domain.time(); + + if ((domain.dtfixed() <= Real_t(0.0)) && (domain.cycle() != Int_t(0))) { + Real_t ratio; + Real_t olddt = domain.deltatime(); + + /* This will require a reduction in parallel */ + Real_t gnewdt = Real_t(1.0e+20); + Real_t newdt; + if (domain.dtcourant() < gnewdt) { + gnewdt = domain.dtcourant() / Real_t(2.0); + } + if (domain.dthydro() < gnewdt) { + gnewdt = domain.dthydro() * Real_t(2.0) / Real_t(3.0); + } + + MPI_Allreduce(&gnewdt, &newdt, 1); // Need to keep gnewdt and newdt + + ratio = newdt / olddt; + if (ratio >= Real_t(1.0)) { + if (ratio < domain.deltatimemultlb()) { + newdt = olddt; + } else if (ratio > domain.deltatimemultub()) { + newdt = olddt * domain.deltatimemultub(); + } + } + + if (newdt > domain.dtmax()) { + newdt = domain.dtmax(); + } + domain.deltatime() = newdt; + } + + if ((targetdt > domain.deltatime()) && (targetdt < (Real_t(4.0) * domain.deltatime() / Real_t(3.0)))) { + targetdt = Real_t(2.0) * domain.deltatime() / Real_t(3.0); + } + + if (targetdt < domain.deltatime()) { + domain.deltatime() = targetdt; + } + + domain.time() += domain.deltatime(); + + ++domain.cycle(); +} + +// Standard filter +// CHECK: > Stack Memory +// CHECK-NEXT: Alloca : 16.00 +// CHECK-NEXT: Stack call filtered % : 81.25 + +// Standard filter with -O3 +// CHECK-opt: > Stack Memory +// CHECK-opt-NEXT: Alloca : 2.00 +// CHECK-opt-NEXT: Stack call filtered % : 0.00 + +// Standard experimental filter +// CHECK-exp-default: > Stack Memory +// CHECK-exp-default-NEXT: Alloca : 16.00 +// CHECK-exp-default-NEXT: Stack call filtered % : 87.50 + +// Standard experimental filter with -O3 +// CHECK-exp-default-opt: > Stack Memory +// CHECK-exp-default-opt-NEXT: Alloca : 2.00 +// CHECK-exp-default-opt-NEXT: Stack call filtered % : 0.00 \ No newline at end of file diff --git a/test/pass/filter/08_amg_box_algebra_mock.c b/test/pass/filter/08_amg_box_algebra_mock.c new file mode 100644 index 00000000..98015528 --- /dev/null +++ b/test/pass/filter/08_amg_box_algebra_mock.c @@ -0,0 +1,240 @@ +// This file tests for an specific endless recursion in the filter implementations w.r.t. following store targets +// RUN: %c-to-llvm -fno-discard-value-names %s | opt -O3 -S | %apply-typeart -typeart-alloca -call-filter -S 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-exp-default-opt + +// CHECK-exp-default-opt: TypeArtPass [Heap & Stack] +// CHECK-exp-default-opt-next: Malloc : 1 +// CHECK-exp-default-opt-next: Free : 1 +// CHECK-exp-default-opt-next: Alloca : 2 +// CHECK-exp-default-opt-next: Global : 0 + +#include + +#define hypre_TAlloc(type, count) \ + ((unsigned int)(count) * sizeof(type)) > 0 ? ((type*)malloc((unsigned int)(sizeof(type) * (count)))) : (type*)NULL + +#define hypre_CTAlloc(type, count) \ + ((unsigned int)(count) * sizeof(type)) > 0 ? ((type*)calloc((unsigned int)(count), (unsigned int)sizeof(type))) \ + : (type*)NULL + +#define hypre_TFree(ptr) (free((char*)ptr), ptr = NULL) + +typedef int hypre_Index[3]; +typedef int* hypre_IndexRef; + +typedef struct hypre_Box_struct { + hypre_Index imin; /* min bounding indices */ + hypre_Index imax; /* max bounding indices */ + +} hypre_Box; + +typedef struct hypre_BoxArray_struct { + hypre_Box* boxes; /* Array of boxes */ + int size; /* Size of box array */ + int alloc_size; /* Size of currently alloced space */ + +} hypre_BoxArray; + +#define hypre_BoxArrayExcess 10 + +typedef struct hypre_BoxArrayArray_struct { + hypre_BoxArray** box_arrays; /* Array of pointers to box arrays */ + int size; /* Size of box array array */ + +} hypre_BoxArrayArray; + +#define hypre_IndexD(index, d) (index[d]) + +#define hypre_IndexX(index) hypre_IndexD(index, 0) +#define hypre_IndexY(index) hypre_IndexD(index, 1) +#define hypre_IndexZ(index) hypre_IndexD(index, 2) + +#define hypre_SetIndex(index, ix, iy, iz) (hypre_IndexX(index) = ix, hypre_IndexY(index) = iy, hypre_IndexZ(index) = iz) + +#define hypre_ClearIndex(index) hypre_SetIndex(index, 0, 0, 0) + +#define hypre_BoxIMin(box) ((box)->imin) +#define hypre_BoxIMax(box) ((box)->imax) + +#define hypre_BoxOffsetDistance(box, index) \ + (hypre_IndexX(index) + (hypre_IndexY(index) + (hypre_IndexZ(index) * hypre_BoxSizeY(box))) * hypre_BoxSizeX(box)) + +#define hypre_CCBoxOffsetDistance(box, index) 0 + +#define hypre_BoxArrayBoxes(box_array) ((box_array)->boxes) +#define hypre_BoxArrayBox(box_array, i) &((box_array)->boxes[(i)]) +#define hypre_BoxArraySize(box_array) ((box_array)->size) +#define hypre_BoxArrayAllocSize(box_array) ((box_array)->alloc_size) + +#define hypre_BoxArrayArrayBoxArrays(box_array_array) ((box_array_array)->box_arrays) +#define hypre_BoxArrayArrayBoxArray(box_array_array, i) ((box_array_array)->box_arrays[(i)]) +#define hypre_BoxArrayArraySize(box_array_array) ((box_array_array)->size) + +extern int hypre_UnionBoxes(hypre_BoxArray* boxes); +extern hypre_BoxArrayArray* hypre_BoxArrayArrayCreate(int); +extern void hypre_AppendBox(hypre_Box*, hypre_BoxArray*); +extern void hypre_BoxSetExtents(hypre_Box*, hypre_Index, hypre_Index); +extern void hypre_BoxArrayArrayDestroy(hypre_BoxArrayArray*); + +int hypre_MinUnionBoxes(hypre_BoxArray* boxes) { + int ierr = 0; + + hypre_BoxArrayArray* rotated_array; + hypre_BoxArray* rotated_boxes; + hypre_Box *box, *rotated_box; + hypre_Index lower, upper; + + int i, j, size, min_size, array; + + size = hypre_BoxArraySize(boxes); + rotated_box = hypre_CTAlloc(hypre_Box, 1); + rotated_array = hypre_BoxArrayArrayCreate(5); + + for (i = 0; i < 5; i++) { + rotated_boxes = hypre_BoxArrayArrayBoxArray(rotated_array, i); + switch (i) { + case 0: + for (j = 0; j < size; j++) { + box = hypre_BoxArrayBox(boxes, j); + hypre_SetIndex(lower, hypre_BoxIMin(box)[0], hypre_BoxIMin(box)[2], hypre_BoxIMin(box)[1]); + hypre_SetIndex(upper, hypre_BoxIMax(box)[0], hypre_BoxIMax(box)[2], hypre_BoxIMax(box)[1]); + hypre_BoxSetExtents(rotated_box, lower, upper); + hypre_AppendBox(rotated_box, rotated_boxes); + } + hypre_UnionBoxes(rotated_boxes); + break; + + case 1: + for (j = 0; j < size; j++) { + box = hypre_BoxArrayBox(boxes, j); + hypre_SetIndex(lower, hypre_BoxIMin(box)[1], hypre_BoxIMin(box)[2], hypre_BoxIMin(box)[0]); + hypre_SetIndex(upper, hypre_BoxIMax(box)[1], hypre_BoxIMax(box)[2], hypre_BoxIMax(box)[0]); + hypre_BoxSetExtents(rotated_box, lower, upper); + hypre_AppendBox(rotated_box, rotated_boxes); + } + hypre_UnionBoxes(rotated_boxes); + break; + + case 2: + for (j = 0; j < size; j++) { + box = hypre_BoxArrayBox(boxes, j); + hypre_SetIndex(lower, hypre_BoxIMin(box)[1], hypre_BoxIMin(box)[0], hypre_BoxIMin(box)[2]); + hypre_SetIndex(upper, hypre_BoxIMax(box)[1], hypre_BoxIMax(box)[0], hypre_BoxIMax(box)[2]); + hypre_BoxSetExtents(rotated_box, lower, upper); + hypre_AppendBox(rotated_box, rotated_boxes); + } + hypre_UnionBoxes(rotated_boxes); + break; + + case 3: + for (j = 0; j < size; j++) { + box = hypre_BoxArrayBox(boxes, j); + hypre_SetIndex(lower, hypre_BoxIMin(box)[2], hypre_BoxIMin(box)[0], hypre_BoxIMin(box)[1]); + hypre_SetIndex(upper, hypre_BoxIMax(box)[2], hypre_BoxIMax(box)[0], hypre_BoxIMax(box)[1]); + hypre_BoxSetExtents(rotated_box, lower, upper); + hypre_AppendBox(rotated_box, rotated_boxes); + } + hypre_UnionBoxes(rotated_boxes); + break; + + case 4: + for (j = 0; j < size; j++) { + box = hypre_BoxArrayBox(boxes, j); + hypre_SetIndex(lower, hypre_BoxIMin(box)[2], hypre_BoxIMin(box)[1], hypre_BoxIMin(box)[0]); + hypre_SetIndex(upper, hypre_BoxIMax(box)[2], hypre_BoxIMax(box)[1], hypre_BoxIMax(box)[0]); + hypre_BoxSetExtents(rotated_box, lower, upper); + hypre_AppendBox(rotated_box, rotated_boxes); + } + hypre_UnionBoxes(rotated_boxes); + break; + + } /*switch(i) */ + } /* for (i= 0; i< 5; i++) */ + hypre_TFree(rotated_box); + + hypre_UnionBoxes(boxes); + + array = 5; + min_size = hypre_BoxArraySize(boxes); + + for (i = 0; i < 5; i++) { + rotated_boxes = hypre_BoxArrayArrayBoxArray(rotated_array, i); + if (hypre_BoxArraySize(rotated_boxes) < min_size) { + min_size = hypre_BoxArraySize(rotated_boxes); + array = i; + } + } + + /* copy the box_array with the minimum number of boxes to boxes */ + if (array != 5) { + rotated_boxes = hypre_BoxArrayArrayBoxArray(rotated_array, array); + hypre_BoxArraySize(boxes) = min_size; + + switch (array) { + case 0: + for (j = 0; j < min_size; j++) { + rotated_box = hypre_BoxArrayBox(rotated_boxes, j); + hypre_SetIndex(lower, hypre_BoxIMin(rotated_box)[0], hypre_BoxIMin(rotated_box)[2], + hypre_BoxIMin(rotated_box)[1]); + hypre_SetIndex(upper, hypre_BoxIMax(rotated_box)[0], hypre_BoxIMax(rotated_box)[2], + hypre_BoxIMax(rotated_box)[1]); + + hypre_BoxSetExtents(hypre_BoxArrayBox(boxes, j), lower, upper); + } + break; + + case 1: + for (j = 0; j < min_size; j++) { + rotated_box = hypre_BoxArrayBox(rotated_boxes, j); + hypre_SetIndex(lower, hypre_BoxIMin(rotated_box)[2], hypre_BoxIMin(rotated_box)[0], + hypre_BoxIMin(rotated_box)[1]); + hypre_SetIndex(upper, hypre_BoxIMax(rotated_box)[2], hypre_BoxIMax(rotated_box)[0], + hypre_BoxIMax(rotated_box)[1]); + + hypre_BoxSetExtents(hypre_BoxArrayBox(boxes, j), lower, upper); + } + break; + + case 2: + for (j = 0; j < min_size; j++) { + rotated_box = hypre_BoxArrayBox(rotated_boxes, j); + hypre_SetIndex(lower, hypre_BoxIMin(rotated_box)[1], hypre_BoxIMin(rotated_box)[0], + hypre_BoxIMin(rotated_box)[2]); + hypre_SetIndex(upper, hypre_BoxIMax(rotated_box)[1], hypre_BoxIMax(rotated_box)[0], + hypre_BoxIMax(rotated_box)[2]); + + hypre_BoxSetExtents(hypre_BoxArrayBox(boxes, j), lower, upper); + } + break; + + case 3: + for (j = 0; j < min_size; j++) { + rotated_box = hypre_BoxArrayBox(rotated_boxes, j); + hypre_SetIndex(lower, hypre_BoxIMin(rotated_box)[1], hypre_BoxIMin(rotated_box)[2], + hypre_BoxIMin(rotated_box)[0]); + hypre_SetIndex(upper, hypre_BoxIMax(rotated_box)[1], hypre_BoxIMax(rotated_box)[2], + hypre_BoxIMax(rotated_box)[0]); + + hypre_BoxSetExtents(hypre_BoxArrayBox(boxes, j), lower, upper); + } + break; + + case 4: + for (j = 0; j < min_size; j++) { + rotated_box = hypre_BoxArrayBox(rotated_boxes, j); + hypre_SetIndex(lower, hypre_BoxIMin(rotated_box)[2], hypre_BoxIMin(rotated_box)[1], + hypre_BoxIMin(rotated_box)[0]); + hypre_SetIndex(upper, hypre_BoxIMax(rotated_box)[2], hypre_BoxIMax(rotated_box)[1], + hypre_BoxIMax(rotated_box)[0]); + + hypre_BoxSetExtents(hypre_BoxArrayBox(boxes, j), lower, upper); + } + break; + + } /* switch(array) */ + } /* if (array != 5) */ + + hypre_BoxArrayArrayDestroy(rotated_array); + + return ierr; +} \ No newline at end of file diff --git a/test/pass/filter/09_milc_gauge_stuff_mock.c b/test/pass/filter/09_milc_gauge_stuff_mock.c new file mode 100644 index 00000000..ed82e600 --- /dev/null +++ b/test/pass/filter/09_milc_gauge_stuff_mock.c @@ -0,0 +1,185 @@ +// This file tests for an specific endless recursion in the filter implementations w.r.t. following store targets +// RUN: %c-to-llvm -fno-discard-value-names %s | opt -O3 -S | %apply-typeart -typeart-alloca -call-filter -S 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-exp-default-opt + +// CHECK-exp-default-opt: TypeArtPass [Heap & Stack] +// CHECK-exp-default-opt-next: Malloc : 0 +// CHECK-exp-default-opt-next: Free : 0 +// CHECK-exp-default-opt-next: Alloca : 0 +// CHECK-exp-default-opt-next: Global : 3 + +#include +#include +#include +#include + +#define XUP 0 +#define YUP 1 +#define ZUP 2 +#define TUP 3 +#define TDOWN 4 +#define ZDOWN 5 +#define YDOWN 6 +#define XDOWN 7 + +#define NODIR -1 + +#define OPP_DIR(dir) (7 - (dir)) +#define NDIRS 8 + +#define NREPS 1 +#define NLOOP 3 +#define MAX_LENGTH 6 +#define MAX_NUM 16 + +extern char gauge_action_description[128]; +int gauge_action_nloops = NLOOP; +int gauge_action_nreps = NREPS; +int loop_length[NLOOP]; +int loop_num[NLOOP]; // TypeART: kept due to printf + +int loop_ind[NLOOP][MAX_LENGTH]; +int loop_table[NLOOP][MAX_NUM][MAX_LENGTH]; +float loop_coeff[NLOOP][NREPS]; // TypeART: kept due to printf +int loop_char[MAX_NUM]; +double loop_expect[NLOOP][NREPS][MAX_NUM]; + +float beta, mass, u0; // TypeART: u0 kept, used by log function + +extern char* strcpy(char* destination, const char* source); + +/* Make table of loops in action */ +void make_loop_table() { + int perm[8], pp[8], ir[4]; + int length, iloop, i, j, chr; + int vec[MAX_LENGTH]; + int count, flag; + void char_num(int* dig, int* chr, int length); + + /* defines all loops and their coefficients */ + static int loop_ind[NLOOP][MAX_LENGTH] = { + {XUP, YUP, XDOWN, YDOWN, NODIR, NODIR}, + {XUP, XUP, YUP, XDOWN, XDOWN, YDOWN}, + {XUP, YUP, ZUP, XDOWN, YDOWN, ZDOWN}, + }; + static int loop_length_in[NLOOP] = {4, 6, 6}; + + for (j = 0; j < NLOOP; j++) { + loop_num[j] = 0; + loop_length[j] = loop_length_in[j]; + for (i = 0; i < NREPS; i++) { + loop_coeff[j][i] = 0.0; + } + } + + /* Loop coefficients from Urs */ + loop_coeff[0][0] = 1.0; + loop_coeff[1][0] = -1.00 / (20.0 * u0 * u0) * (1.00 - 0.6264 * log(u0)); + loop_coeff[2][0] = 1.00 / (u0 * u0) * 0.04335 * log(u0); + // TypeART: this is transformed to intrinsic IR memcpy!: + strcpy(gauge_action_description, "\"Symanzik 1x1 + 1x2 + 1x1x1 action\""); + printf("Symanzik 1x1 + 1x2 + 1x1x1 action\n"); + + for (iloop = 0; iloop < NLOOP; iloop++) { + length = loop_length[iloop]; + count = 0; + /* permutations */ + for (perm[0] = 0; perm[0] < 4; perm[0]++) + for (perm[1] = 0; perm[1] < 4; perm[1]++) + for (perm[2] = 0; perm[2] < 4; perm[2]++) + for (perm[3] = 0; perm[3] < 4; perm[3]++) { + if (perm[0] != perm[1] && perm[0] != perm[2] && perm[0] != perm[3] && perm[1] != perm[2] && + perm[1] != perm[3] && perm[2] != perm[3]) { + /* reflections*/ + for (ir[0] = 0; ir[0] < 2; ir[0]++) + for (ir[1] = 0; ir[1] < 2; ir[1]++) + for (ir[2] = 0; ir[2] < 2; ir[2]++) + for (ir[3] = 0; ir[3] < 2; ir[3]++) { + for (j = 0; j < 4; j++) { + pp[j] = perm[j]; + + if (ir[j] == 1) + pp[j] = 7 - pp[j]; + pp[7 - j] = 7 - pp[j]; + } + /* create new vector*/ + for (j = 0; j < length; j++) + vec[j] = pp[loop_ind[iloop][j]]; + + char_num(vec, &chr, length); + flag = 0; + /* check if it's a new set: */ + for (j = 0; j < count; j++) + if (chr == loop_char[j]) + flag = 1; + if (flag == 0) { + loop_char[count] = chr; + for (j = 0; j < length; j++) + loop_table[iloop][count][j] = vec[j]; + count++; + /**node0_printf("ADD LOOP: "); printpath( vec, length );**/ + } + if (count > MAX_NUM) { + printf("OOPS: MAX_NUM too small\n"); + exit(0); + } + loop_num[iloop] = count; + + } /* end reflection*/ + } /* end permutation if block */ + } /* end permutation */ + } /* end iloop */ + + /* print out the loop coefficients */ + printf("loop coefficients: nloop rep loop_coeff multiplicity\n"); + for (i = 0; i < NREPS; i++) + for (j = 0; j < NLOOP; j++) { + printf(" %d %d %e %d\n", j, i, loop_coeff[j][i], loop_num[j]); + } + +} /* make_loop_table */ + +/* find a number uniquely identifying the cyclic permutation of a path, + or the starting point on the path. Backwards paths are considered + equivalent here, so scan those too. */ +void char_num(int* dig, int* chr, int length) { + int j; + int bdig[MAX_LENGTH], tenl, newv, old; + /* "dig" is array of directions. "bdig" is array of directions for +backwards path. */ + + tenl = 1; + for (j = 0; j < length - 1; j++) + tenl = tenl * 10; + + *chr = dig[length - 1]; + for (j = length - 2; j >= 0; j--) + *chr = *chr * 10 + dig[j]; + + /* forward*/ + old = *chr; + for (j = length - 1; j >= 1; j--) { + newv = old - tenl * dig[j]; + newv = newv * 10 + dig[j]; + if (newv < *chr) + *chr = newv; + old = newv; + } + + /* backward*/ + for (j = 0; j < length; j++) + bdig[j] = 7 - dig[length - j - 1]; + old = bdig[length - 1]; + for (j = length - 2; j >= 0; j--) + old = old * 10 + bdig[j]; + if (old < *chr) + *chr = old; + for (j = length - 1; j >= 1; j--) { + newv = old - tenl * bdig[j]; + newv = newv * 10 + bdig[j]; + if (newv < *chr) + *chr = newv; + old = newv; + } + +} /* char_num */ diff --git a/test/pass/malloc_free/01_simple_malloc_int.c b/test/pass/malloc_free/01_simple_malloc_int.c index 712284a2..4a8c5149 100644 --- a/test/pass/malloc_free/01_simple_malloc_int.c +++ b/test/pass/malloc_free/01_simple_malloc_int.c @@ -1,9 +1,16 @@ // clang-format off -// RUN: clang -S -emit-llvm %s -o - | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s // clang-format on #include void test() { int* p = (int*)malloc(42 * sizeof(int)); } -// CHECK: Malloc{{[ ]*}}:{{[ ]*}}1 -// CHECK: Alloca{{[ ]*}}:{{[ ]*}}0 + +// CHECK: [[POINTER:%[0-9]+]] = call noalias i8* @malloc +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER]], i32 2, i64 42) +// CHECK-NEXT: bitcast i8* [[POINTER]] to i32* + +// CHECK: TypeArtPass [Heap] +// CHECK-NEXT: Malloc{{[ ]*}}:{{[ ]*}}1 +// CHECK-NEXT: Free{{[ ]*}}:{{[ ]*}}0 +// CHECK-NEXT: Alloca{{[ ]*}}:{{[ ]*}}0 diff --git a/test/pass/malloc_free/02_simple_malloc_free_int.c b/test/pass/malloc_free/02_simple_malloc_free_int.c index 9a18ba9c..49004711 100644 --- a/test/pass/malloc_free/02_simple_malloc_free_int.c +++ b/test/pass/malloc_free/02_simple_malloc_free_int.c @@ -1,5 +1,5 @@ // clang-format off -// RUN: clang -S -emit-llvm %s -o - | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s // clang-format on #include @@ -8,6 +8,14 @@ void test() { free(p); } -// CHECK: Malloc{{[ ]*}}:{{[ ]*}}1 -// CHECK: Free{{[ ]*}}:{{[ ]*}}1 -// CHECK: Alloca{{[ ]*}}:{{[ ]*}}0 +// CHECK: [[POINTER:%[0-9]+]] = call noalias i8* @malloc +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER]], +// CHECK-NEXT: bitcast i8* [[POINTER]] to i32* + +// CHECK: call void @free(i8* [[POINTER:%[0-9]+]]) +// CHECK-NEXT: call void @__typeart_free(i8* [[POINTER]]) + +// CHECK: TypeArtPass [Heap] +// CHECK-NEXT: Malloc{{[ ]*}}:{{[ ]*}}1 +// CHECK-NEXT: Free{{[ ]*}}:{{[ ]*}}1 +// CHECK-NEXT: Alloca{{[ ]*}}:{{[ ]*}}0 diff --git a/test/pass/malloc_free/03_simple_malloc_free_double.c b/test/pass/malloc_free/03_simple_malloc_free_double.c index 97d2b196..471e336e 100644 --- a/test/pass/malloc_free/03_simple_malloc_free_double.c +++ b/test/pass/malloc_free/03_simple_malloc_free_double.c @@ -1,5 +1,5 @@ // clang-format off -// RUN: clang -S -emit-llvm %s -o - | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s // clang-format on #include void test() { @@ -7,6 +7,14 @@ void test() { free(p); } -// CHECK: Malloc{{[ ]*}}:{{[ ]*}}1 -// CHECK: Free{{[ ]*}}:{{[ ]*}}1 -// CHECK: Alloca{{[ ]*}}:{{[ ]*}}0 +// CHECK: [[POINTER:%[0-9]+]] = call noalias i8* @malloc +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER]], i32 6, i64 42) +// CHECK-NEXT: bitcast i8* [[POINTER]] to double* + +// CHECK: call void @free(i8* [[POINTER:%[0-9]+]]) +// CHECK-NEXT: call void @__typeart_free(i8* [[POINTER]]) + +// CHECK: TypeArtPass [Heap] +// CHECK-NEXT: Malloc{{[ ]*}}:{{[ ]*}}1 +// CHECK-NEXT: Free{{[ ]*}}:{{[ ]*}}1 +// CHECK-NEXT: Alloca{{[ ]*}}:{{[ ]*}}0 diff --git a/test/pass/malloc_free/04_simple_malloc_int_double.c b/test/pass/malloc_free/04_simple_malloc_int_double.c index 8a696bcf..f283dded 100644 --- a/test/pass/malloc_free/04_simple_malloc_int_double.c +++ b/test/pass/malloc_free/04_simple_malloc_int_double.c @@ -1,5 +1,5 @@ // clang-format off -// RUN: clang -S -emit-llvm %s -o - | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s // clang-format on #include void test() { @@ -7,5 +7,15 @@ void test() { double* pd = (double*)malloc(42 * sizeof(double)); } -// CHECK: Malloc{{[ ]*}}:{{[ ]*}}2 -// CHECK: Alloca{{[ ]*}}:{{[ ]*}}0 +// CHECK: [[POINTER:%[0-9]+]] = call noalias i8* @malloc +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER]], +// CHECK-NEXT: bitcast i8* [[POINTER]] to i32* + +// CHECK: [[POINTER:%[0-9]+]] = call noalias i8* @malloc +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER]], +// CHECK-NEXT: bitcast i8* [[POINTER]] to double* + +// CHECK: TypeArtPass [Heap] +// CHECK-NEXT: Malloc{{[ ]*}}:{{[ ]*}}2 +// CHECK-NEXT: Free{{[ ]*}}:{{[ ]*}}0 +// CHECK-NEXT: Alloca{{[ ]*}}:{{[ ]*}}0 diff --git a/test/pass/malloc_free/05_simple_malloc_struct.c b/test/pass/malloc_free/05_simple_malloc_struct.c index 96a1c17b..a758efc0 100644 --- a/test/pass/malloc_free/05_simple_malloc_struct.c +++ b/test/pass/malloc_free/05_simple_malloc_struct.c @@ -1,5 +1,5 @@ // clang-format off -// RUN: clang -S -emit-llvm %s -o - | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s // clang-format on #include typedef struct ms { @@ -12,6 +12,14 @@ void test() { free(m); } -// CHECK: Malloc{{[ ]*}}:{{[ ]*}}1 -// CHECK: Free{{[ ]*}}:{{[ ]*}}1 -// CHECK: Alloca{{[ ]*}}:{{[ ]*}}0 +// CHECK: [[POINTER:%[0-9]+]] = call noalias i8* @malloc +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER]], i32 256, i64 1) +// CHECK-NEXT: bitcast i8* [[POINTER]] to %struct.ms* + +// CHECK: call void @free(i8* [[POINTER:%[0-9]+]]) +// CHECK-NEXT: call void @__typeart_free(i8* [[POINTER]]) + +// CHECK: TypeArtPass [Heap] +// CHECK-NEXT: Malloc{{[ ]*}}:{{[ ]*}}1 +// CHECK-NEXT: Free{{[ ]*}}:{{[ ]*}}1 +// CHECK-NEXT: Alloca{{[ ]*}}:{{[ ]*}}0 diff --git a/test/pass/malloc_free/06_simple_malloc_void_type.c b/test/pass/malloc_free/06_simple_malloc_void_type.c index b246f9df..3b9134da 100644 --- a/test/pass/malloc_free/06_simple_malloc_void_type.c +++ b/test/pass/malloc_free/06_simple_malloc_void_type.c @@ -1,9 +1,17 @@ // clang-format off -// RUN: clang -S -emit-llvm %s -o - | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -S | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s --check-prefix=PASS-OUT // clang-format on #include void test() { void* p = malloc(42 * sizeof(int)); // LLVM-IR: lacks a bitcast } -// CHECK: Malloc{{[ ]*}}:{{[ ]*}}1 -// CHECK: Alloca{{[ ]*}}:{{[ ]*}}0 + +// CHECK: [[POINTER:%[0-9]+]] = call noalias i8* @malloc +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER]], i32 0, i64 168) +// CHECK-NOT: bitcast i8* [[POINTER]] to i32* + +// PASS-OUT: TypeArtPass [Heap] +// PASS-OUT-NEXT: Malloc{{[ ]*}}:{{[ ]*}}1 +// PASS-OUT-NEXT: Free{{[ ]*}}:{{[ ]*}}0 +// PASS-OUT-NEXT: Alloca{{[ ]*}}:{{[ ]*}}0 \ No newline at end of file diff --git a/test/pass/malloc_free/07_simple_malloc_free_void_type.c b/test/pass/malloc_free/07_simple_malloc_free_void_type.c index 234dfd3b..ad0b2371 100644 --- a/test/pass/malloc_free/07_simple_malloc_free_void_type.c +++ b/test/pass/malloc_free/07_simple_malloc_free_void_type.c @@ -1,10 +1,21 @@ // clang-format off -// RUN: clang -S -emit-llvm %s -o - | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -S | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s --check-prefix=PASS-OUT // clang-format on #include void test() { void* p = malloc(42 * sizeof(int)); // LLVM-IR: lacks a bitcast free(p); } -// CHECK: Malloc{{[ ]*}}:{{[ ]*}}1 -// CHECK: Alloca{{[ ]*}}:{{[ ]*}}0 + +// CHECK: [[POINTER:%[0-9]+]] = call noalias i8* @malloc +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER]], +// CHECK-NOT: bitcast i8* [[POINTER]] to i32* + +// CHECK: call void @free(i8* [[POINTER:%[0-9]+]]) +// CHECK-NEXT: call void @__typeart_free(i8* [[POINTER]]) + +// PASS-OUT: TypeArtPass [Heap] +// PASS-OUT-NEXT: Malloc{{[ ]*}}:{{[ ]*}}1 +// PASS-OUT-NEXT: Free{{[ ]*}}:{{[ ]*}}1 +// PASS-OUT-NEXT: Alloca{{[ ]*}}:{{[ ]*}}0 diff --git a/test/pass/malloc_free/08_simple_malloc_struct_void.c b/test/pass/malloc_free/08_simple_malloc_struct_void.c index e1be4906..d13a6583 100644 --- a/test/pass/malloc_free/08_simple_malloc_struct_void.c +++ b/test/pass/malloc_free/08_simple_malloc_struct_void.c @@ -1,5 +1,6 @@ // clang-format off -// RUN: clang -S -emit-llvm %s -o - | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -S | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s --check-prefix=PASS-OUT // clang-format on #include typedef struct ms { @@ -12,6 +13,14 @@ void test() { free(m); } -// CHECK: Malloc{{[ ]*}}:{{[ ]*}}1 -// CHECK: Free{{[ ]*}}:{{[ ]*}}1 -// CHECK: Alloca{{[ ]*}}:{{[ ]*}}0 +// CHECK: [[POINTER:%[0-9]+]] = call noalias i8* @malloc +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER]], i32 0, i64 16) +// CHECK-NOT: bitcast i8* [[POINTER]] to i32* + +// CHECK: call void @free(i8* [[POINTER:%[0-9]+]]) +// CHECK-NEXT: call void @__typeart_free(i8* [[POINTER]]) + +// PASS-OUT: TypeArtPass [Heap] +// PASS-OUT-NEXT: Malloc{{[ ]*}}:{{[ ]*}}1 +// PASS-OUT-NEXT: Free{{[ ]*}}:{{[ ]*}}1 +// PASS-OUT-NEXT: Alloca{{[ ]*}}:{{[ ]*}}0 diff --git a/test/pass/malloc_free/09_malloc_with_disjunct_free.c b/test/pass/malloc_free/09_malloc_with_disjunct_free.c index a21fddfd..adf7f844 100644 --- a/test/pass/malloc_free/09_malloc_with_disjunct_free.c +++ b/test/pass/malloc_free/09_malloc_with_disjunct_free.c @@ -1,5 +1,5 @@ // clang-format off -// RUN: clang -S -emit-llvm %s -o - | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s // clang-format on #include @@ -15,6 +15,14 @@ void foo(double* ptr) { ptr = NULL; } -// CHECK: Malloc{{[ ]*}}:{{[ ]*}}1 -// CHECK: Free{{[ ]*}}:{{[ ]*}}1 -// CHECK: Alloca{{[ ]*}}:{{[ ]*}}0 +// CHECK: [[POINTER:%[0-9]+]] = call noalias i8* @malloc +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER]], +// CHECK-NEXT: bitcast i8* [[POINTER]] to double* + +// CHECK: call void @free(i8* [[POINTER:%[0-9]+]]) +// CHECK-NEXT: call void @__typeart_free(i8* [[POINTER]]) + +// CHECK: TypeArtPass [Heap] +// CHECK-NEXT: Malloc{{[ ]*}}:{{[ ]*}}1 +// CHECK-NEXT: Free{{[ ]*}}:{{[ ]*}}1 +// CHECK-NEXT: Alloca{{[ ]*}}:{{[ ]*}}0 diff --git a/test/pass/malloc_free/10_malloc_multiple_casts.c b/test/pass/malloc_free/10_malloc_multiple_casts.c index a458e803..34a7ddb8 100644 --- a/test/pass/malloc_free/10_malloc_multiple_casts.c +++ b/test/pass/malloc_free/10_malloc_multiple_casts.c @@ -1,5 +1,6 @@ // clang-format off -// RUN: clang -S -emit-llvm %s -o - | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -S | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s --check-prefix=PASS-OUT // clang-format on #include void test() { @@ -8,6 +9,12 @@ void test() { short* ps = (short*)p; } -// CHECK: [Warning] -// CHECK: Malloc{{[ ]*}}:{{[ ]*}}1 -// CHECK: Alloca{{[ ]*}}:{{[ ]*}}0 +// CHECK: [[POINTER:%[0-9]+]] = call noalias i8* @malloc +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER]], + +// PASS-OUT: [Warning] {{.*}} Encountered ambiguous pointer type in function + +// PASS-OUT: TypeArtPass [Heap] +// PASS-OUT-NEXT: Malloc{{[ ]*}}:{{[ ]*}}1 +// PASS-OUT-NEXT: Free{{[ ]*}}:{{[ ]*}}0 +// PASS-OUT-NEXT: Alloca{{[ ]*}}:{{[ ]*}}0 diff --git a/test/pass/malloc_free/11_calloc_realloc.c b/test/pass/malloc_free/11_calloc_realloc.c index 322db63b..2f0180b8 100644 --- a/test/pass/malloc_free/11_calloc_realloc.c +++ b/test/pass/malloc_free/11_calloc_realloc.c @@ -1,5 +1,6 @@ // clang-format off -// RUN: clang -S -emit-llvm %s -o - | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -S 2>&1 | FileCheck %s +// RUN: %c-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s --check-prefix=REALLOC +// RUN: %c-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s // clang-format on #include @@ -11,5 +12,15 @@ int main() { return 0; } -// CHECK: Malloc{{[ ]*}}:{{[ ]*}}2 -// CHECK: Alloca{{[ ]*}}:{{[ ]*}}0 +// CHECK: [[POINTER:%[0-9]+]] = call noalias i8* @calloc(i64 [[SIZE:[0-9]+]], i64 8) +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER]], i32 6, i64 [[SIZE]]) +// CHECK-NEXT: bitcast i8* [[POINTER]] to double* + +// REALLOC: __typeart_free(i8* [[POINTER:%[0-9]+]]) +// REALLOC-NEXT: [[POINTER2:%[0-9]+]] = call i8* @realloc(i8* [[POINTER]], i64 160) +// REALLOC-NEXT: __typeart_alloc(i8* [[POINTER2]], i32 6, i64 20) + +// CHECK: TypeArtPass [Heap] +// CHECK-NEXT: Malloc{{[ ]*}}:{{[ ]*}}2 +// CHECK-NEXT: Free{{[ ]*}}:{{[ ]*}}0 +// CHECK-NEXT: Alloca{{[ ]*}}:{{[ ]*}}0 diff --git a/test/pass/malloc_free/11_calloc_realloc.llin b/test/pass/malloc_free/11_calloc_realloc.llin deleted file mode 100644 index eab51c20..00000000 --- a/test/pass/malloc_free/11_calloc_realloc.llin +++ /dev/null @@ -1,46 +0,0 @@ -; RUN: cat %s | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -typeart-alloca -alloca-array-only=false -S 2>&1 | FileCheck %s - -; ModuleID = '11_calloc_realloc.c' -source_filename = "11_calloc_realloc.c" -target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" -target triple = "x86_64-pc-linux-gnu" - -; CHECK:define i32 @main() -; CHECK: %5 = call noalias i8* @calloc(i64 10, i64 8) #2 -; CHECK-NEXT: call void @__typeart_alloc(i8* %5, i32 6, i64 10) -; CHECK-NEXT: %6 = bitcast i8* %5 to double* -; CHECK: call void @__typeart_free(i8* %8) -; CHECK-NEXT: %9 = call i8* @realloc(i8* %8, i64 160) #2 -; CHECK-NEXT: call void @__typeart_alloc(i8* %9, i32 6, i64 20) -; CHECK-NEXT: %10 = bitcast i8* %9 to double* -; Function Attrs: noinline nounwind optnone uwtable -define i32 @main() #0 { - %1 = alloca i32, align 4 - %2 = alloca double*, align 8 - store i32 0, i32* %1, align 4 - %3 = call noalias i8* @calloc(i64 10, i64 8) #2 - %4 = bitcast i8* %3 to double* - store double* %4, double** %2, align 8 - %5 = load double*, double** %2, align 8 - %6 = bitcast double* %5 to i8* - %7 = call i8* @realloc(i8* %6, i64 160) #2 - %8 = bitcast i8* %7 to double* - store double* %8, double** %2, align 8 - ret i32 0 -} - -; Function Attrs: nounwind -declare noalias i8* @calloc(i64, i64) #1 - -; Function Attrs: nounwind -declare i8* @realloc(i8*, i64) #1 - -attributes #0 = { noinline nounwind optnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { nounwind } - -!llvm.module.flags = !{!0} -!llvm.ident = !{!1} - -!0 = !{i32 1, !"wchar_size", i32 4} -!1 = !{!"clang version 6.0.0 (tags/RELEASE_600/final 332839)"} diff --git a/test/pass/malloc_free/12_malloc_memcpy.c b/test/pass/malloc_free/12_malloc_memcpy.c index 4121cabb..cd736b18 100644 --- a/test/pass/malloc_free/12_malloc_memcpy.c +++ b/test/pass/malloc_free/12_malloc_memcpy.c @@ -1,5 +1,5 @@ // clang-format off -// RUN: clang -O2 -S -emit-llvm %s -o - | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -S 2>&1 | FileCheck %s --check-prefix CHECK-OPT +// RUN: clang -O2 -S -emit-llvm %s -o - | %apply-typeart -S 2>&1 | FileCheck %s --check-prefix CHECK-OPT // clang-format on // This is a dummy test illustrating problems with -Xclang approach and higher optimizations, losing infos about the diff --git a/test/pass/malloc_free/13_calloc_void_type.c b/test/pass/malloc_free/13_calloc_void_type.c new file mode 100644 index 00000000..f66b0dea --- /dev/null +++ b/test/pass/malloc_free/13_calloc_void_type.c @@ -0,0 +1,18 @@ +// clang-format off +// RUN: %c-to-llvm %s | %apply-typeart -S | FileCheck %s +// clang-format on +#include + +void foo(int n) { + void* pvd = calloc(10, sizeof(double)); + + void* pvd2 = calloc(n, sizeof(double)); +} + +// CHECK: [[POINTER:%[0-9]+]] = call noalias i8* @calloc(i64 [[SIZE:[0-9]+]], i64 8) +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER]], i32 0, i64 80) +// CHECK-NOT: bitcast i8* [[POINTER]] to double* + +// CHECK: [[POINTER2:%[0-9]+]] = call noalias i8* @calloc(i64 [[SIZE2:%[0-9]+]], i64 8) +// CHECK-NOT: call void @__typeart_alloc(i8* [[POINTER2]], i32 0, i64 [[SIZE2]]) +// CHECK-NOT: bitcast i8* [[POINTER]] to double* \ No newline at end of file diff --git a/test/pass/malloc_free/14_aligned_alloc.c b/test/pass/malloc_free/14_aligned_alloc.c new file mode 100644 index 00000000..b0b665db --- /dev/null +++ b/test/pass/malloc_free/14_aligned_alloc.c @@ -0,0 +1,19 @@ +// clang-format off +// RUN: %c-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s +// clang-format on +#include + +void foo(int n) { + int* pi = aligned_alloc(64, 20); + + int* pi2 = (int*)aligned_alloc(128, n); +} + +// CHECK: [[POINTER:%[0-9]+]] = call noalias i8* @aligned_alloc(i64 64, i64 20) +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER]], i32 2, i64 5) +// CHECK-NEXT: bitcast i8* [[POINTER]] to i32* + +// CHECK: [[POINTER2:%[0-9]+]] = call noalias i8* @aligned_alloc(i64 128, i64 [[SIZE:%[0-9]+]]) +// CHECK-NOT: call void @__typeart_alloc(i8* [[POINTER2]], i32 2, i64 [[SIZE]]) +// CHECK: call void @__typeart_alloc(i8* [[POINTER2]], i32 2, i64 %{{[0-9]+}}) +// CHECK-NEXT: bitcast i8* [[POINTER2]] to i32* \ No newline at end of file diff --git a/test/pass/malloc_free/15_aligned_alloc_void_type.c b/test/pass/malloc_free/15_aligned_alloc_void_type.c new file mode 100644 index 00000000..d951fd3d --- /dev/null +++ b/test/pass/malloc_free/15_aligned_alloc_void_type.c @@ -0,0 +1,18 @@ +// clang-format off +// RUN: %c-to-llvm %s | %apply-typeart -S | FileCheck %s +// clang-format on +#include + +void foo(int n) { + void* pi = aligned_alloc(64, 20); + + void* pi2 = aligned_alloc(128, n); +} + +// CHECK: [[POINTER:%[0-9]+]] = call noalias i8* @aligned_alloc(i64 64, i64 20) +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER]], i32 0, i64 20) +// CHECK-NOT: bitcast i8* [[POINTER]] to {{.*}}* + +// CHECK: [[POINTER2:%[0-9]+]] = call noalias i8* @aligned_alloc(i64 128, i64 [[SIZE:%[0-9]+]]) +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER2]], i32 0, i64 [[SIZE]]) +// CHECK-NOT: bitcast i8* [[POINTER]] to {{.*}}* \ No newline at end of file diff --git a/test/pass/misc/01_MemInstFinder_Args.c b/test/pass/misc/01_MemInstFinder_Args.c new file mode 100644 index 00000000..e6f20eb5 --- /dev/null +++ b/test/pass/misc/01_MemInstFinder_Args.c @@ -0,0 +1,18 @@ +// clang-format off +// Sanity check for arg names +// RUN: %c-to-llvm %s | opt -load %analysis_pass -mem-inst-finder \ +// RUN: -call-filter \ +// RUN: -call-filter-impl=empty \ +// RUN: -call-filter-str=MPI1 \ +// RUN: -call-filter-deep-str=MPI2 \ +// RUN: -call-filter-cg-file=/foo/file.cg \ +// RUN: -call-filter-deep=true \ +// RUN: -alloca-array-only=true \ +// RUN: -malloc-store-filter=true \ +// RUN: -filter-globals=true +// clang-format on + +void foo() { + int a = 1; + double b = 2.0; +} \ No newline at end of file diff --git a/test/pass/misc/02_TypeArtPass_Args.c b/test/pass/misc/02_TypeArtPass_Args.c new file mode 100644 index 00000000..ff2fcea2 --- /dev/null +++ b/test/pass/misc/02_TypeArtPass_Args.c @@ -0,0 +1,13 @@ +// clang-format off +// Sanity check for arg names +// RUN: %c-to-llvm %s | opt -load %analysis_pass -load %transform_pass -typeart \ +// RUN: -typeart-stats \ +// RUN: -typeart-no-heap=false \ +// RUN: -typeart-alloca=true \ +// RUN: -typeart-outfile=typeart_types.yaml +// clang-format on + +void foo() { + int a = 1; + double b = 2.0; +} \ No newline at end of file diff --git a/test/pass/new_delete/01_inv_simple_new_int.cpp b/test/pass/new_delete/01_inv_simple_new_int.cpp new file mode 100644 index 00000000..3bb1b591 --- /dev/null +++ b/test/pass/new_delete/01_inv_simple_new_int.cpp @@ -0,0 +1,21 @@ +// clang-format off +// RUN: %cpp-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s +// clang-format on + +#include +// CHECK: invoke i8* @_Znwm(i64 4) +// CHECK: call void @__typeart_alloc(i8* [[POINTER:%[0-9]+]], i32 2, i64 1) +// CHECK-NEXT: bitcast i8* {{.*}}[[POINTER]] to i32* +int main() { + try { + auto s = new int; + } catch (...) { + } + + return 0; +} + +// CHECK: TypeArtPass [Heap] +// CHECK-NEXT: Malloc{{[ ]*}}:{{[ ]*}}1 +// CHECK-NEXT: Free +// CHECK-NEXT: Alloca{{[ ]*}}:{{[ ]*}}0 diff --git a/test/pass/new_delete/02_inv_simple_int_array.cpp b/test/pass/new_delete/02_inv_simple_int_array.cpp new file mode 100644 index 00000000..20ab459f --- /dev/null +++ b/test/pass/new_delete/02_inv_simple_int_array.cpp @@ -0,0 +1,21 @@ +// clang-format off +// RUN: %cpp-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s +// clang-format on + +#include +// CHECK: invoke i8* @_Znam(i64 8) +// CHECK: call void @__typeart_alloc(i8* [[POINTER:%[0-9]+]], i32 2, i64 2) +// CHECK-NEXT: bitcast i8* {{.*}}[[POINTER]] to i32* +int main() { + try { + auto s = new int[2]; + } catch (...) { + } + + return 0; +} + +// CHECK: TypeArtPass [Heap] +// CHECK-NEXT: Malloc{{[ ]*}}:{{[ ]*}}1 +// CHECK-NEXT: Free +// CHECK-NEXT: Alloca{{[ ]*}}:{{[ ]*}}0 diff --git a/test/pass/new_delete/03_inv_struct.cpp b/test/pass/new_delete/03_inv_struct.cpp new file mode 100644 index 00000000..c8c01c2a --- /dev/null +++ b/test/pass/new_delete/03_inv_struct.cpp @@ -0,0 +1,27 @@ +// clang-format off +// RUN: %cpp-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s +// clang-format on + +#include + +struct S1 { + int x; + virtual ~S1() = default; +}; + +// CHECK: invoke i8* @_Znwm(i64 16) +// CHECK: call void @__typeart_alloc(i8* [[POINTER:%[0-9]+]], i32 {{2[0-9]+}}, i64 1) +// CHECK: bitcast i8* [[POINTER]] to %struct.S1* +int main() { + try { + S1* ss = new S1; + } catch (...) { + } + + return 0; +} + +// CHECK: TypeArtPass [Heap] +// CHECK-NEXT: Malloc{{[ ]*}}:{{[ ]*}}1 +// CHECK-NEXT: Free +// CHECK-NEXT: Alloca{{[ ]*}}:{{[ ]*}}0 diff --git a/test/pass/new_delete/04_inv_struct_array.cpp b/test/pass/new_delete/04_inv_struct_array.cpp new file mode 100644 index 00000000..88d08be2 --- /dev/null +++ b/test/pass/new_delete/04_inv_struct_array.cpp @@ -0,0 +1,29 @@ +// clang-format off +// RUN: %cpp-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s +// XFAIL: * +// Fix: size is calculated based on new call with array cookie; address is array cookie +// clang-format on + +#include + +struct S1 { + int x; + virtual ~S1() = default; +}; + +// CHECK: invoke i8* @_Znam(i64 40) +// CHECK: call void @__typeart_alloc(i8* [[POINTER:%[0-9]+]], i32 {{2[5-9][0-9]}}, i64 2) +// CHECK: bitcast i8* [[POINTER]] to %struct.S1* +int main() { + try { + S1* ss = new S1[2]; + } catch (...) { + } + + return 0; +} + +// CHECK: TypeArtPass [Heap] +// CHECK-NEXT: Malloc{{[ ]*}}:{{[ ]*}}1 +// CHECK-NEXT: Free +// CHECK-NEXT: Alloca{{[ ]*}}:{{[ ]*}}0 diff --git a/test/pass/new_delete/05_struct_array.cpp b/test/pass/new_delete/05_struct_array.cpp new file mode 100644 index 00000000..2ddc2d9a --- /dev/null +++ b/test/pass/new_delete/05_struct_array.cpp @@ -0,0 +1,20 @@ +// clang-format off +// RUN: %cpp-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s +// clang-format on + +struct S1 { + int x; +}; + +// CHECK: call i8* @_Znam(i64 8) +// CHECK: call void @__typeart_alloc(i8* [[POINTER:%[0-9]+]], i32 {{2[0-9]+}}, i64 2) +// CHECK: bitcast i8* [[POINTER]] to %struct.S1* +int main() { + S1* ss = new S1[2]; + return 0; +} + +// CHECK: TypeArtPass [Heap] +// CHECK-NEXT: Malloc{{[ ]*}}:{{[ ]*}}1 +// CHECK-NEXT: Free +// CHECK-NEXT: Alloca{{[ ]*}}:{{[ ]*}}0 diff --git a/test/pass/new_delete/06_struct_array_def_dest.cpp b/test/pass/new_delete/06_struct_array_def_dest.cpp new file mode 100644 index 00000000..649d5d8b --- /dev/null +++ b/test/pass/new_delete/06_struct_array_def_dest.cpp @@ -0,0 +1,21 @@ +// clang-format off +// RUN: %cpp-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s +// clang-format on + +struct S1 { + int x; + ~S1() = default; +}; + +// CHECK: call i8* @_Znam(i64 8) +// CHECK: call void @__typeart_alloc(i8* [[POINTER:%[0-9]+]], i32 {{2[0-9]+}}, i64 2) +// CHECK: bitcast i8* [[POINTER]] to %struct.S1* +int main() { + S1* ss = new S1[2]; + return 0; +} + +// CHECK: TypeArtPass [Heap] +// CHECK-NEXT: Malloc{{[ ]*}}:{{[ ]*}}1 +// CHECK-NEXT: Free +// CHECK-NEXT: Alloca{{[ ]*}}:{{[ ]*}}0 diff --git a/test/pass/new_delete/07_struct_array_userdef_dest.cpp b/test/pass/new_delete/07_struct_array_userdef_dest.cpp new file mode 100644 index 00000000..ebacf5f8 --- /dev/null +++ b/test/pass/new_delete/07_struct_array_userdef_dest.cpp @@ -0,0 +1,23 @@ +// clang-format off +// RUN: %cpp-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s +// XFAIL: * +// Wrong size is calculated due to using Znam call, instead of bitcast to struct.S1* +// clang-format on + +struct S1 { + int x; + ~S1(){}; +}; + +// CHECK: call i8* @_Znam(i64 8) +// CHECK: call void @__typeart_alloc(i8* [[POINTER:%[0-9]+]], i32 {{2[0-9]+}}, i64 2) +// CHECK: bitcast i8* [[POINTER]] to %struct.S1* +int main() { + S1* ss = new S1[2]; + return 0; +} + +// CHECK: TypeArtPass [Heap] +// CHECK-NEXT: Malloc{{[ ]*}}:{{[ ]*}}1 +// CHECK-NEXT: Free +// CHECK-NEXT: Alloca{{[ ]*}}:{{[ ]*}}0 diff --git a/test/pass/new_delete/08_inv_double_array_delete.cpp b/test/pass/new_delete/08_inv_double_array_delete.cpp new file mode 100644 index 00000000..c3ce9a1e --- /dev/null +++ b/test/pass/new_delete/08_inv_double_array_delete.cpp @@ -0,0 +1,40 @@ +// clang-format off +// RUN: %cpp-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s +// clang-format on + +#include +// CHECK: invoke i8* @_Znam(i64 16) +// CHECK: call void @__typeart_alloc(i8* [[POINTER:%[0-9]+]], i32 6, i64 2) +// CHECK-NEXT: bitcast i8* {{.*}}[[POINTER]] to double* +// CHECK: call void @_ZdaPv(i8* [[POINTER2:%[0-9]+]]) +// CHECK-NEXT: call void @__typeart_free(i8* {{.*}}[[POINTER2]]) +int main() { + try { + auto s = new double[2]; + delete[] s; + } catch (...) { + } + + return 0; +} + +// CHECK: invoke i8* @_Znam(i64 16) +// CHECK: call void @__typeart_alloc(i8* [[POINTER:%[0-9]+]], i32 6, i64 2) +// CHECK-NEXT: bitcast i8* {{.*}}[[POINTER]] to double* +// CHECK: call void @_ZdaPv(i8* [[POINTER2:%[0-9]+]]) +// CHECK-NEXT: call void @__typeart_free(i8* {{.*}}[[POINTER2]]) +void foo() { + double* b{nullptr}; + try { + b = new double[2]; + } catch (...) { + } + if (b != nullptr) { + delete[] b; + } +} + +// CHECK: TypeArtPass [Heap] +// CHECK-NEXT: Malloc{{[ ]*}}:{{[ ]*}}2 +// CHECK-NEXT: Free{{[ ]*}}:{{[ ]*}}2 +// CHECK-NEXT: Alloca{{[ ]*}}:{{[ ]*}}0 diff --git a/test/pass/new_delete/09_inv_struct_delete.cpp b/test/pass/new_delete/09_inv_struct_delete.cpp new file mode 100644 index 00000000..694ebf5a --- /dev/null +++ b/test/pass/new_delete/09_inv_struct_delete.cpp @@ -0,0 +1,50 @@ +// clang-format off +// RUN: %cpp-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s +// clang-format on + +#include + +struct S1 { + int x; + virtual ~S1() = default; +}; + +// CHECK: invoke i8* @_Znwm(i64 16) +// CHECK: call void @__typeart_alloc(i8* [[POINTER:%[0-9]+]], i32 {{2[0-9]+}}, i64 1) +// CHECK: bitcast i8* [[POINTER]] to %struct.S1* +// CHECK-NOT: call void @_ZdlPv(i8* [[POINTER2:%[0-9]+]]) +// CHECK-NOT: call void @__typeart_free(i8* {{.*}}[[POINTER2]]) +void foo() { + S1* b{nullptr}; + try { + b = new S1; + } catch (...) { + } + if (b != nullptr) { + delete b; + } +} + +// CHECK: invoke i8* @_Znwm(i64 16) +// CHECK: call void @__typeart_alloc(i8* [[POINTER:%[0-9]+]], i32 {{2[0-9]+}}, i64 1) +// CHECK: bitcast i8* [[POINTER]] to %struct.S1* +// CHECK-NOT: call void @_ZdaPv(i8* [[POINTER2:%[0-9]+]]) +// CHECK-NOT: call void @__typeart_free(i8* {{.*}}[[POINTER2]]) +int main() { + try { + S1* ss = new S1; + delete ss; // TODO LLVM does not call _ZdaPv here, but in destructor @_ZN2S1D0Ev + } catch (...) { + } + + return 0; +} + +// CHECK: @_ZN2S1D0Ev +// CHECK: call void @_ZdlPv(i8* [[POINTER2:%[0-9]+]]) +// CHECK-NEXT: call void @__typeart_free(i8* {{.*}}[[POINTER2]]) + +// CHECK: TypeArtPass [Heap] +// CHECK-NEXT: Malloc{{[ ]*}}:{{[ ]*}}2 +// CHECK-NEXT: Free{{[ ]*}}:{{[ ]*}}1 +// CHECK-NEXT: Alloca{{[ ]*}}:{{[ ]*}}0 diff --git a/test/pass/new_delete/10_inv_struct_array_delete.cpp b/test/pass/new_delete/10_inv_struct_array_delete.cpp new file mode 100644 index 00000000..8e7db2bf --- /dev/null +++ b/test/pass/new_delete/10_inv_struct_array_delete.cpp @@ -0,0 +1,51 @@ +// clang-format off +// RUN: %cpp-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s +// FIXME revisit for array cookie handling +// clang-format on + +#include + +struct S1 { + int x; + virtual ~S1() = default; +}; + +// CHECK: invoke i8* @_Znam(i64 56) +// CHECK: call void @__typeart_alloc(i8* [[POINTER:%[0-9]+]], i32 {{2[0-9]+}}, i64 3) +// CHECK: [[MEMORYBLOB:%[0-9]+]] = getelementptr inbounds i8, i8* %{{[0-9]+}}, i64 -8 +// CHECK: call void @_ZdaPv(i8* [[MEMORYBLOB]]) +// CHECK-NEXT: call void @__typeart_free(i8* [[MEMORYBLOB]]) +void foo() { + S1* b{nullptr}; + try { + b = new S1[3]; + } catch (...) { + } + if (b != nullptr) { + delete[] b; + } +} + +// CHECK: invoke i8* @_Znam(i64 40) +// CHECK: call void @__typeart_alloc(i8* [[POINTER:%[0-9]+]], i32 {{2[0-9]+}}, i64 2) +// CHECK: [[MEMORYBLOB:%[0-9]+]] = getelementptr inbounds i8, i8* %{{[0-9]+}}, i64 -8 +// CHECK: call void @_ZdaPv(i8* [[MEMORYBLOB]]) +// CHECK-NEXT: call void @__typeart_free(i8* [[MEMORYBLOB]]) +int main() { + try { + S1* ss = new S1[2]; + delete[] ss; // TODO LLVM does not call _ZdaPv here, but in destructor @_ZN2S1D0Ev + } catch (...) { + } + + return 0; +} + +// CHECK: @_ZN2S1D0Ev +// CHECK: call void @_ZdlPv(i8* [[POINTER2:%[0-9]+]]) +// CHECK-NEXT: call void @__typeart_free(i8* [[POINTER2]]) + +// CHECK: TypeArtPass [Heap] +// CHECK-NEXT: Malloc{{[ ]*}}:{{[ ]*}}2 +// CHECK-NEXT: Free{{[ ]*}}:{{[ ]*}}3 +// CHECK-NEXT: Alloca{{[ ]*}}:{{[ ]*}}0 diff --git a/test/pass/new_delete/11_new_nothrow.cpp b/test/pass/new_delete/11_new_nothrow.cpp new file mode 100644 index 00000000..e1ea9c36 --- /dev/null +++ b/test/pass/new_delete/11_new_nothrow.cpp @@ -0,0 +1,23 @@ +// clang-format off +// RUN: %cpp-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s +// clang-format on + +#include + +struct C { + int x; +}; + +C* foo() { + return new (std::nothrow) C[10]; +} + +C* bar() { + return new (std::nothrow) C; +} + +// CHECK: [[POINTER:%[0-9]+]] = call noalias i8* @_ZnamRKSt9nothrow_t(i64 40, +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER]], i32 [[ID:2[5-9][0-9]]], i64 10) + +// CHECK: [[POINTER2:%[0-9]+]] = call noalias i8* @_ZnwmRKSt9nothrow_t(i64 4, +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER2]], i32 [[ID]], i64 1) \ No newline at end of file diff --git a/test/pass/new_delete/12_new_aligned.cpp b/test/pass/new_delete/12_new_aligned.cpp new file mode 100644 index 00000000..48558a10 --- /dev/null +++ b/test/pass/new_delete/12_new_aligned.cpp @@ -0,0 +1,24 @@ +// clang-format off +// RUN: %cpp-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s +// clang-format on + +#define __cpp_aligned_new 1 +#include + +struct C { + int x; +}; + +C* foo() { + return new (std::align_val_t(64)) C[10]; +} + +C* bar() { + return new (std::align_val_t(128)) C; +} + +// CHECK: [[POINTER:%[0-9]+]] = call i8* @_ZnamSt11align_val_t(i64 40, i64 64) +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER]], i32 [[ID:2[5-9][0-9]]], i64 10) + +// CHECK: [[POINTER2:%[0-9]+]] = call i8* @_ZnwmSt11align_val_t(i64 4, i64 128) +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER2]], i32 [[ID]], i64 1) \ No newline at end of file diff --git a/test/pass/new_delete/13_new_aligned_nothrow.cpp b/test/pass/new_delete/13_new_aligned_nothrow.cpp new file mode 100644 index 00000000..b2af82ec --- /dev/null +++ b/test/pass/new_delete/13_new_aligned_nothrow.cpp @@ -0,0 +1,24 @@ +// clang-format off +// RUN: %cpp-to-llvm %s | %apply-typeart -S 2>&1 | FileCheck %s +// clang-format on + +#define __cpp_aligned_new 1 +#include + +struct C { + int x; +}; + +C* foo() { + return new (std::align_val_t(64), std::nothrow) C[10]; +} + +C* bar() { + return new (std::align_val_t(128), std::nothrow) C; +} + +// CHECK: [[POINTER:%[0-9]+]] = call noalias i8* @_ZnamSt11align_val_tRKSt9nothrow_t(i64 40, +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER]], i32 [[ID:2[5-9][0-9]]], i64 10) + +// CHECK: [[POINTER2:%[0-9]+]] = call noalias i8* @_ZnwmSt11align_val_tRKSt9nothrow_t(i64 4, +// CHECK-NEXT: call void @__typeart_alloc(i8* [[POINTER2]], i32 [[ID]], i64 1) \ No newline at end of file diff --git a/test/runtime/01_malloc_trace.c b/test/runtime/01_malloc_trace.c index 7e54e294..20cf9530 100644 --- a/test/runtime/01_malloc_trace.c +++ b/test/runtime/01_malloc_trace.c @@ -1,4 +1,4 @@ -// RUN: %scriptpath/applyAndRun.sh %s %pluginpath "-typeart-alloca" %rtpath 2>&1 | FileCheck %s +// RUN: %run %s 2>&1 | FileCheck %s #include diff --git a/test/runtime/02_malloc_unsigned_trace.c b/test/runtime/02_malloc_unsigned_trace.c index 59ecbd3f..23dcbc2c 100644 --- a/test/runtime/02_malloc_unsigned_trace.c +++ b/test/runtime/02_malloc_unsigned_trace.c @@ -1,4 +1,4 @@ -// RUN: %scriptpath/applyAndRun.sh %s %pluginpath "-typeart-alloca" %rtpath 2>&1 | FileCheck %s +// RUN: %run %s 2>&1 | FileCheck %s // XFAIL: * #include diff --git a/test/runtime/03_new_trace.cpp b/test/runtime/03_new_trace.cpp index ad972e2c..d6b70892 100644 --- a/test/runtime/03_new_trace.cpp +++ b/test/runtime/03_new_trace.cpp @@ -1,82 +1,77 @@ -// RUN: %scriptpath/applyAndRun.sh %s %pluginpath "-typeart-alloca" %rtpath 2>&1 | FileCheck %s +// RUN: %run %s 2>&1 | FileCheck %s - -template +template void new_delete() { - T* t = new T; - delete t; + T* t = new T; + delete t; } -template +template void new_delete(int n) { - T* t = new T[n]; - delete[] t; + T* t = new T[n]; + delete[] t; } - - int main(int argc, char** argv) { - const int n = 42; - - // CHECK: [Trace] TypeART Runtime Trace - - // CHECK: [Trace] Alloc 0x{{.*}} int8 1 1 - // CHECK: [Trace] Free 0x{{.*}} - new_delete(); + const int n = 42; - // CHECK: [Trace] Alloc 0x{{.*}} int16 2 1 - // CHECK: [Trace] Free 0x{{.*}} - new_delete(); + // CHECK: [Trace] TypeART Runtime Trace - // CHECK: [Trace] Alloc 0x{{.*}} int32 4 1 - // CHECK: [Trace] Free 0x{{.*}} - new_delete(); + // CHECK: [Trace] Alloc 0x{{.*}} int8 1 1 + // CHECK: [Trace] Free 0x{{.*}} + new_delete(); - // CHECK: [Trace] Alloc 0x{{.*}} int64 8 1 - // CHECK: [Trace] Free 0x{{.*}} - new_delete(); + // CHECK: [Trace] Alloc 0x{{.*}} int16 2 1 + // CHECK: [Trace] Free 0x{{.*}} + new_delete(); - // CHECK: [Trace] Alloc 0x{{.*}} float 4 1 - // CHECK: [Trace] Free 0x{{.*}} - new_delete(); + // CHECK: [Trace] Alloc 0x{{.*}} int32 4 1 + // CHECK: [Trace] Free 0x{{.*}} + new_delete(); - // CHECK: [Trace] Alloc 0x{{.*}} double 8 1 - // CHECK: [Trace] Free 0x{{.*}} - new_delete(); + // CHECK: [Trace] Alloc 0x{{.*}} int64 8 1 + // CHECK: [Trace] Free 0x{{.*}} + new_delete(); - // CHECK: [Trace] Alloc 0x{{.*}} pointer 8 1 - // CHECK: [Trace] Free 0x{{.*}} - new_delete(); + // CHECK: [Trace] Alloc 0x{{.*}} float 4 1 + // CHECK: [Trace] Free 0x{{.*}} + new_delete(); + // CHECK: [Trace] Alloc 0x{{.*}} double 8 1 + // CHECK: [Trace] Free 0x{{.*}} + new_delete(); - // CHECK: [Trace] Alloc 0x{{.*}} int8 1 42 - // CHECK: [Trace] Free 0x{{.*}} - new_delete(n); + // CHECK: [Trace] Alloc 0x{{.*}} pointer 8 1 + // CHECK: [Trace] Free 0x{{.*}} + new_delete(); - // CHECK: [Trace] Alloc 0x{{.*}} int16 2 42 - // CHECK: [Trace] Free 0x{{.*}} - new_delete(n); + // CHECK: [Trace] Alloc 0x{{.*}} int8 1 42 + // CHECK: [Trace] Free 0x{{.*}} + new_delete(n); - // CHECK: [Trace] Alloc 0x{{.*}} int32 4 42 - // CHECK: [Trace] Free 0x{{.*}} - new_delete(n); + // CHECK: [Trace] Alloc 0x{{.*}} int16 2 42 + // CHECK: [Trace] Free 0x{{.*}} + new_delete(n); - // CHECK: [Trace] Alloc 0x{{.*}} int64 8 42 - // CHECK: [Trace] Free 0x{{.*}} - new_delete(n); + // CHECK: [Trace] Alloc 0x{{.*}} int32 4 42 + // CHECK: [Trace] Free 0x{{.*}} + new_delete(n); - // CHECK: [Trace] Alloc 0x{{.*}} float 4 42 - // CHECK: [Trace] Free 0x{{.*}} - new_delete(n); + // CHECK: [Trace] Alloc 0x{{.*}} int64 8 42 + // CHECK: [Trace] Free 0x{{.*}} + new_delete(n); - // CHECK: [Trace] Alloc 0x{{.*}} double 8 42 - // CHECK: [Trace] Free 0x{{.*}} - new_delete(n); + // CHECK: [Trace] Alloc 0x{{.*}} float 4 42 + // CHECK: [Trace] Free 0x{{.*}} + new_delete(n); - // CHECK: [Trace] Alloc 0x{{.*}} pointer 8 42 - // CHECK: [Trace] Free 0x{{.*}} - new_delete(n); + // CHECK: [Trace] Alloc 0x{{.*}} double 8 42 + // CHECK: [Trace] Free 0x{{.*}} + new_delete(n); + // CHECK: [Trace] Alloc 0x{{.*}} pointer 8 42 + // CHECK: [Trace] Free 0x{{.*}} + new_delete(n); - return 0; + return 0; } diff --git a/test/runtime/04_alloca_trace.c b/test/runtime/04_alloca_trace.c index cb4735be..5759f66f 100644 --- a/test/runtime/04_alloca_trace.c +++ b/test/runtime/04_alloca_trace.c @@ -1,4 +1,4 @@ -// RUN: %scriptpath/applyAndRun.sh %s %pluginpath "-typeart-alloca" %rtpath 2>&1 | FileCheck %s +// RUN: %run %s 2>&1 | FileCheck %s #include diff --git a/test/runtime/05_alloca_unsigned_trace.c b/test/runtime/05_alloca_unsigned_trace.c index f73f668e..12385b75 100644 --- a/test/runtime/05_alloca_unsigned_trace.c +++ b/test/runtime/05_alloca_unsigned_trace.c @@ -1,4 +1,4 @@ -// RUN: %scriptpath/applyAndRun.sh %s %pluginpath "-typeart-alloca" %rtpath 2>&1 | FileCheck %s +// RUN: %run %s 2>&1 | FileCheck %s // XFAIL: * #include diff --git a/test/runtime/06_simple_array_type_check.cpp b/test/runtime/06_simple_array_type_check.cpp index 4149f139..34fa3d6c 100644 --- a/test/runtime/06_simple_array_type_check.cpp +++ b/test/runtime/06_simple_array_type_check.cpp @@ -1,29 +1,28 @@ -// RUN: %scriptpath/applyAndRun.sh %s %pluginpath "-typeart-alloca" %rtpath 2>&1 | FileCheck %s +// RUN: %run %s 2>&1 | FileCheck %s -#include -#include +#include "../../lib/typelib/TypeInterface.h" #include "util.h" -#include "../../typelib/TypeInterface.h" -template +#include +#include + +template void performTypeChecks(int n, typeart_builtin_type typeId) { - T* p = (T*) malloc(n * sizeof(T)); - check(p - 1, typeId, 1, 1); // Unknown address - check(p, typeId, n, 1); // Ok - check(p + n/2, typeId, n - n/2, 1); // Ok - check(p + n-1, typeId, 1, 1); // Ok - check(p + n, typeId, 1, 1); // Error: Unknown address - check(((uint8_t*)p) + 1, typeId, n-1, 1); // Fails for sizeof(T) > 1 - check(((uint8_t*) (p + n/2)) + 1, typeId, n - n/2 - 1, 1); // Fails for sizeof(T) > 1 - check(((uint8_t*)p) + 2, typeId, n-2/sizeof(T), 1); // Fails for sizeof(T) > 2 - check(((uint8_t*)p) + 4, typeId, n-4/sizeof(T), 1); // Fails for sizeof(T) > 4 - check(((uint8_t*)p) + 8, typeId, n-8/sizeof(T), 1); // Fails for sizeof(T) > 8 + T* p = (T*)malloc(n * sizeof(T)); + check(p - 1, typeId, 1, 1); // Unknown address + check(p, typeId, n, 1); // Ok + check(p + n / 2, typeId, n - n / 2, 1); // Ok + check(p + n - 1, typeId, 1, 1); // Ok + check(p + n, typeId, 1, 1); // Error: Unknown address + check(((uint8_t*)p) + 1, typeId, n - 1, 1); // Fails for sizeof(T) > 1 + check(((uint8_t*)(p + n / 2)) + 1, typeId, n - n / 2 - 1, 1); // Fails for sizeof(T) > 1 + check(((uint8_t*)p) + 2, typeId, n - 2 / sizeof(T), 1); // Fails for sizeof(T) > 2 + check(((uint8_t*)p) + 4, typeId, n - 4 / sizeof(T), 1); // Fails for sizeof(T) > 4 + check(((uint8_t*)p) + 8, typeId, n - 8 / sizeof(T), 1); // Fails for sizeof(T) > 8 free(p); } - int main(int argc, char** argv) { - const int n = 42; // CHECK: [Trace] Alloc 0x{{.*}} int8 1 42 diff --git a/test/runtime/07_simple_struct_type_check.c b/test/runtime/07_simple_struct_type_check.c index 6ed62bc1..53f077dd 100644 --- a/test/runtime/07_simple_struct_type_check.c +++ b/test/runtime/07_simple_struct_type_check.c @@ -1,4 +1,4 @@ -// RUN: %scriptpath/applyAndRun.sh %s %pluginpath "-typeart-alloca" %rtpath 2>&1 | FileCheck %s +// RUN: %run %s -alloca-array-only=true 2>&1 | FileCheck %s #include "../struct_defs.h" #include "util.h" diff --git a/test/runtime/08_recursive_struct_type_check.c b/test/runtime/08_recursive_struct_type_check.c index af36495c..02bc623e 100644 --- a/test/runtime/08_recursive_struct_type_check.c +++ b/test/runtime/08_recursive_struct_type_check.c @@ -1,4 +1,4 @@ -// RUN: %scriptpath/applyAndRun.sh %s %pluginpath "-typeart-alloca" %rtpath 2>&1 | FileCheck %s +// RUN: %run %s -alloca-array-only=true 2>&1 | FileCheck %s #include "../struct_defs.h" #include "util.h" diff --git a/test/runtime/10_vector_fill_demo.c b/test/runtime/10_vector_fill_demo.c index 4f05c9e4..83846489 100644 --- a/test/runtime/10_vector_fill_demo.c +++ b/test/runtime/10_vector_fill_demo.c @@ -1,6 +1,6 @@ -// RUN: %scriptpath/applyAndRun.sh %s %pluginpath "-typeart-alloca" %rtpath 2>&1 | FileCheck %s +// RUN: %run %s 2>&1 | FileCheck %s -#include "../../runtime/RuntimeInterface.h" +#include "../../lib/runtime/RuntimeInterface.h" #include #include diff --git a/test/runtime/11_matrix_mult_demo.c b/test/runtime/11_matrix_mult_demo.c index 29a8b5c9..3fdd2637 100644 --- a/test/runtime/11_matrix_mult_demo.c +++ b/test/runtime/11_matrix_mult_demo.c @@ -1,4 +1,4 @@ -// RUN: %scriptpath/applyAndRun.sh %s %pluginpath "-typeart-alloca" %rtpath 2>&1 | FileCheck %s +// RUN: %run %s 2>&1 | FileCheck %s #include #include diff --git a/test/runtime/12_alloc_multi.c b/test/runtime/12_alloc_multi.c index 61ab2313..d1ea1fe6 100644 --- a/test/runtime/12_alloc_multi.c +++ b/test/runtime/12_alloc_multi.c @@ -1,6 +1,6 @@ // clang-format off -// RUN: %scriptpath/applyAndRun.sh %s %pluginpath "-typeart-alloca" %rtpath 2>&1 | FileCheck %s -// RUN: %scriptpath/applyAndRun.sh %s %pluginpath "-typeart-alloca -call-filter" %rtpath 2>&1 | FileCheck %s --check-prefix CHECK-FILTER +// RUN: %run %s 2>&1 | FileCheck %s +// RUN: %run %s "-call-filter" 2>&1 | FileCheck %s --check-prefix CHECK-FILTER // clang-format on #include diff --git a/test/runtime/13_alloca_vla.c b/test/runtime/13_alloca_vla.c index 9a166435..822e014e 100644 --- a/test/runtime/13_alloca_vla.c +++ b/test/runtime/13_alloca_vla.c @@ -1,4 +1,4 @@ -// RUN: %scriptpath/applyAndRun.sh %s %pluginpath "-typeart-alloca" %rtpath 2>&1 | FileCheck %s +// RUN: %run %s 2>&1 | FileCheck %s #include diff --git a/test/runtime/14_alloca_vector.c b/test/runtime/14_alloca_vector.c index 6f500ccc..8a270072 100644 --- a/test/runtime/14_alloca_vector.c +++ b/test/runtime/14_alloca_vector.c @@ -1,4 +1,4 @@ -// RUN: %scriptpath/applyAndRun.sh %s %pluginpath "-typeart-alloca" %rtpath 2>&1 | FileCheck %s +// RUN: %run %s 2>&1 | FileCheck %s #include diff --git a/test/runtime/15_malloc_zero.c b/test/runtime/15_malloc_zero.c index 100772a0..4877637a 100644 --- a/test/runtime/15_malloc_zero.c +++ b/test/runtime/15_malloc_zero.c @@ -1,4 +1,4 @@ -// RUN: %scriptpath/applyAndRun.sh %s %pluginpath "-typeart-alloca" %rtpath 2>&1 | FileCheck %s +// RUN: %run %s 2>&1 | FileCheck %s #include diff --git a/test/runtime/16_inv_struct.cpp b/test/runtime/16_inv_struct.cpp new file mode 100644 index 00000000..62b4c599 --- /dev/null +++ b/test/runtime/16_inv_struct.cpp @@ -0,0 +1,39 @@ +// clang-format off +// RUN: %run %s 2>&1 | FileCheck %s +// clang-format on + +#include + +struct S1 { + int x; + virtual ~S1() = default; +}; + +void foo() { + S1* b{nullptr}; + try { + b = new S1; + } catch (...) { + } + if (b != nullptr) { + delete b; + } +} + +int main() { + try { + S1* ss = new S1; + delete ss; // LLVM does not call _ZdaPv here, but in destructor @_ZN2S1D0Ev + } catch (...) { + } + + foo(); + + return 0; +} +// main() +// CHECK: [Trace] Alloc [[POINTER:0x[0-9a-f]+]] [[typeid:2[5-9][0-9]]] struct.S1 16 1 ([[ALLOC_FROM:0x[0-9a-f]+]]) H +// CHECK: [Trace] Free [[POINTER]] [[typeid]] struct.S1 16 1 ([[ALLOC_FROM]]) +// foo() +// CHECK: [Trace] Alloc [[POINTER:0x[0-9a-f]+]] [[typeid:2[5-9][0-9]]] struct.S1 16 1 ([[ALLOC_FROM:0x[0-9a-f]+]]) H +// CHECK: [Trace] Free [[POINTER]] [[typeid]] struct.S1 16 1 ([[ALLOC_FROM]]) diff --git a/test/runtime/17_inv_array.cpp b/test/runtime/17_inv_array.cpp new file mode 100644 index 00000000..3e70d0b4 --- /dev/null +++ b/test/runtime/17_inv_array.cpp @@ -0,0 +1,40 @@ +// clang-format off +// RUN: %run %s 2>&1 | FileCheck %s +// XFAIL: * +// clang-format on + +#include + +struct S1 { + int x; + virtual ~S1() = default; +}; + +void foo() { + S1* b{nullptr}; + try { + b = new S1[6]; // FIXME this works because size computation rounds down + } catch (...) { + } + if (b != nullptr) { + delete[] b; + } +} + +int main() { + try { + S1* ss = new S1[3]; // FIXME this works because size computation rounds down + delete[] ss; // LLVM does not call _ZdaPv here, but in destructor @_ZN2S1D0Ev + } catch (...) { + } + + foo(); + + return 0; +} +// main() +// CHECK: [Trace] Alloc [[POINTER:0x[0-9a-f]+]] struct.S1 16 6 H +// CHECK: [Trace] Free [[POINTER]]. typeId: [[typeid:2[5-9][0-9]]] (struct.S1). +// foo() +// CHECK: [Trace] Alloc [[POINTER:0x[0-9a-f]+]] struct.S1 16 3 H +// CHECK: [Trace] Free [[POINTER]]. typeId: [[typeid]] (struct.S1). diff --git a/test/runtime/util.h b/test/runtime/util.h index 9658d370..d6884a38 100644 --- a/test/runtime/util.h +++ b/test/runtime/util.h @@ -1,87 +1,87 @@ #ifndef UTIL_H #define UTIL_H -#include +#include "../../lib/runtime/RuntimeInterface.h" + #include +#include #include -#include "../../runtime/RuntimeInterface.h" int get_struct_id(int index) { - return TA_NUM_RESERVED_IDS + index; + return TA_NUM_RESERVED_IDS + index; } void check(void* addr, int id, int expected_count, int resolveStructs) { - int id_result; - size_t count_check; - typeart_status status = typeart_get_type(addr, &id_result, &count_check); - if (status == TA_OK) { - if (resolveStructs) { - // If the address corresponds to a struct, fetch the type of the first member - while (id_result >= TA_NUM_RESERVED_IDS) { - - typeart_struct_layout struct_layout; - typeart_resolve_type(id_result, &struct_layout); - id_result = struct_layout.member_types[0]; - count_check = struct_layout.count[0]; - } - } - - if (id_result == id) { - if (count_check != expected_count) { - fprintf(stderr, "Error: Count mismatch (%zu)\n", count_check); - } else { - fprintf(stderr, "Ok\n"); - } - } else { - fprintf(stderr, "Error: Type mismatch\n"); - } + int id_result; + size_t count_check; + typeart_status status = typeart_get_type(addr, &id_result, &count_check); + if (status == TA_OK) { + if (resolveStructs) { + // If the address corresponds to a struct, fetch the type of the first member + while (id_result >= TA_NUM_RESERVED_IDS) { + typeart_struct_layout struct_layout; + typeart_resolve_type(id_result, &struct_layout); + id_result = struct_layout.member_types[0]; + count_check = struct_layout.count[0]; + } + } + if (id_result == id) { + if (count_check != expected_count) { + fprintf(stderr, "Error: Count mismatch (%zu)\n", count_check); + } else { + fprintf(stderr, "Ok\n"); + } } else { - switch (status) { - case TA_UNKNOWN_ADDRESS: - fprintf(stderr, "Error: Unknown address\n"); - break; - case TA_BAD_ALIGNMENT: - fprintf(stderr, "Error: Bad alignment\n"); - break; - default: - fprintf(stderr, "Error: Unexpected status: %d\n", status); - break; - } + fprintf(stderr, "Error: Type mismatch\n"); + } + + } else { + switch (status) { + case TA_UNKNOWN_ADDRESS: + fprintf(stderr, "Error: Unknown address\n"); + break; + case TA_BAD_ALIGNMENT: + fprintf(stderr, "Error: Bad alignment\n"); + break; + default: + fprintf(stderr, "Error: Unexpected status: %d\n", status); + break; } + } } void check_struct(void* addr, const char* name, int expected_count) { - int id; - size_t count_check; - typeart_status status = typeart_get_type(addr, &id, &count_check); - if (status == TA_OK) { - if (id >= TA_NUM_RESERVED_IDS) { - typeart_struct_layout struct_layout; - typeart_resolve_type(id, &struct_layout); - if (strcmp(typeart_get_type_name(id), struct_layout.name) != 0) { - fprintf(stderr, "Error: Name mismatch\n"); - } else if (expected_count != count_check) { - fprintf(stderr, "Error: Count mismatch (%zu)\n", count_check); - } else { - fprintf(stderr, "Ok\n"); - } - } else { - fprintf(stderr, "Error: Not a struct\n"); - } + int id; + size_t count_check; + typeart_status status = typeart_get_type(addr, &id, &count_check); + if (status == TA_OK) { + if (id >= TA_NUM_RESERVED_IDS) { + typeart_struct_layout struct_layout; + typeart_resolve_type(id, &struct_layout); + if (strcmp(typeart_get_type_name(id), struct_layout.name) != 0) { + fprintf(stderr, "Error: Name mismatch\n"); + } else if (expected_count != count_check) { + fprintf(stderr, "Error: Count mismatch (%zu)\n", count_check); + } else { + fprintf(stderr, "Ok\n"); + } } else { - switch (status) { - case TA_UNKNOWN_ADDRESS: - fprintf(stderr, "Error: Unknown address\n"); - break; - case TA_BAD_ALIGNMENT: - fprintf(stderr, "Error: Bad alignment\n"); - break; - default: - fprintf(stderr, "Error: Unexpected status: %d\n", status); - break; - } + fprintf(stderr, "Error: Not a struct\n"); + } + } else { + switch (status) { + case TA_UNKNOWN_ADDRESS: + fprintf(stderr, "Error: Unknown address\n"); + break; + case TA_BAD_ALIGNMENT: + fprintf(stderr, "Error: Bad alignment\n"); + break; + default: + fprintf(stderr, "Error: Unexpected status: %d\n", status); + break; } + } } #endif \ No newline at end of file diff --git a/test/struct_defs.h b/test/struct_defs.h index 5313eb54..750e8ab0 100644 --- a/test/struct_defs.h +++ b/test/struct_defs.h @@ -5,56 +5,55 @@ // The number behind each member indicates the corresponding offset. // The comment after the struct definition gives the extent with added padding. - typedef struct s_int_t { - int a; // 0 -} s_int; // 4 + int a; // 0 +} s_int; // 4 typedef struct s_builtins_t { - int a; // 0 - char b; // 4 - long c; // 8 -} s_builtins; // 16 + int a; // 0 + char b; // 4 + long c; // 8 +} s_builtins; // 16 typedef struct s_arrays_t { - int a[3]; // 0 - long b[2]; // 16 - char c; // 32 - int d[3]; // 36 - char e[5]; // 48 - long f; // 56 - char g; -} s_arrays; // 72 + int a[3]; // 0 + long b[2]; // 16 + char c; // 32 + int d[3]; // 36 + char e[5]; // 48 + long f; // 56 + char g; +} s_arrays; // 72 typedef struct s_ptrs_t { - char a; // 0 - int* b; // 8 - int c; // 16 - double* d; // 20 -} s_ptrs; // 32 + char a; // 0 + int* b; // 8 + int c; // 16 + double* d; // 20 +} s_ptrs; // 32 typedef struct s_mixed_simple_t { - int a; // 0 - double b[3]; // 8 - char* c; // 32 - char d[5]; // 40 + int a; // 0 + double b[3]; // 8 + char* c; // 32 + char d[5]; // 40 } s_mixed_simple; // 48 typedef struct s_ptr_to_self_t { - struct s_ptr_to_self_t* a; // 0 - struct s_ptr_to_self_t** b; // 8 -} s_ptr_to_self; // 16 + struct s_ptr_to_self_t* a; // 0 + struct s_ptr_to_self_t** b; // 8 +} s_ptr_to_self; // 16 typedef struct s_struct_member_t { - int a; // 0 - s_ptr_to_self b; // 4 - struct s_struct_member_t* c; // 20 -} s_struct_member; // 32 + int a; // 0 + s_ptr_to_self b; // 4 + struct s_struct_member_t* c; // 20 +} s_struct_member; // 32 typedef struct s_aos_t { - int a; // 0 - s_struct_member b[2]; // 8 - s_struct_member* c[3]; // 72 -} s_aos; // 96 + int a; // 0 + s_struct_member b[2]; // 8 + s_struct_member* c[3]; // 72 +} s_aos; // 96 #endif diff --git a/test/typemapping/01_simple_struct.c b/test/typemapping/01_simple_struct.c index a5796035..7328bf27 100644 --- a/test/typemapping/01_simple_struct.c +++ b/test/typemapping/01_simple_struct.c @@ -1,5 +1,5 @@ // clang-format off -// RUN: rm types.yaml | clang -S -emit-llvm %s -o - | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -S 2>&1; cat types.yaml | FileCheck %s +// RUN: rm %type_file | %c-to-llvm %s | %apply-typeart -S 2>&1; cat %type_file | FileCheck %s // clang-format on // Note: This test assumes standard alignment on a 64bit system. Non-standard alignment may lead to failure. diff --git a/test/typemapping/02_recursive_struct.c b/test/typemapping/02_recursive_struct.c index 79d6b4f1..fe3e451c 100644 --- a/test/typemapping/02_recursive_struct.c +++ b/test/typemapping/02_recursive_struct.c @@ -1,5 +1,5 @@ // clang-format off -// RUN: rm types.yaml | clang -S -emit-llvm %s -o - | opt -load %pluginpath/analysis/meminstfinderpass.so -load %pluginpath/%pluginname %pluginargs -S 2>&1; cat types.yaml | FileCheck %s +// RUN: rm %type_file | %c-to-llvm %s | %apply-typeart -S 2>&1; cat %type_file | FileCheck %s // clang-format on // Note: This test assumes standard alignment on a 64bit system. Non-standard alignment may lead to failure.