├── .clang-format ├── .github └── workflows │ ├── apple-silicon-llvm-from-sources.yml │ ├── apple-silicon.yml │ ├── docker-archlinux.yml │ ├── docker-fedora.yml │ ├── docker-ubuntu.yml │ ├── docker-ubunut-apt.yml │ ├── x86-ubuntu-llvm-from-sources-static.yml │ ├── x86-ubuntu-llvm-from-sources.yml │ └── x86-ubuntu.yml ├── .gitignore ├── CMakeLists.txt ├── Dockerfile_archlinux ├── Dockerfile_fedora ├── Dockerfile_ubuntu ├── Dockerfile_ubuntu_apt ├── HelloWorld ├── CMakeLists.txt └── HelloWorld.cpp ├── LICENSE ├── README.md ├── include ├── ConvertFCmpEq.h ├── DuplicateBB.h ├── DynamicCallCounter.h ├── FindFCmpEq.h ├── InjectFuncCall.h ├── MBAAdd.h ├── MBASub.h ├── MergeBB.h ├── OpcodeCounter.h ├── RIV.h └── StaticCallCounter.h ├── inputs ├── input_for_cc.c ├── input_for_duplicate_bb.c ├── input_for_fcmp_eq.c ├── input_for_hello.c ├── input_for_mba.c ├── input_for_mba_sub.c └── input_for_riv.c ├── lib ├── CMakeLists.txt ├── ConvertFCmpEq.cpp ├── DuplicateBB.cpp ├── DynamicCallCounter.cpp ├── FindFCmpEq.cpp ├── InjectFuncCall.cpp ├── MBAAdd.cpp ├── MBASub.cpp ├── MergeBB.cpp ├── OpcodeCounter.cpp ├── RIV.cpp └── StaticCallCounter.cpp ├── test ├── CMakeLists.txt ├── ConvertFCmpEqPreds.ll ├── DuplicateBB.ll ├── DuplicateBB_MergeBB_exec.ll ├── DuplicateBB_add.ll ├── DuplicateBB_exec.ll ├── DuplicateBB_float.ll ├── DuplicateBB_global.ll ├── DuplicateBB_switch.ll ├── DynamicCallCounterTest1.ll ├── DynamicCallCounterTest2.ll ├── DynamicCallCounterTest3.ll ├── FindFCmpEq_float.ll ├── FindFCmpEq_float_compare.ll ├── FindFCmpEq_float_non_compare.ll ├── FindFCmpEq_int_compare.ll ├── Inputs │ ├── CallCounterInput.ll │ └── FCmpEqInput.ll ├── MBA_add_32bit.ll ├── MBA_add_8bit.ll ├── MBA_add_exec.ll ├── MBA_sub.ll ├── MBA_sub_exec.ll ├── MergeBB_conditional_branch_merged.ll ├── MergeBB_conditional_branch_not_merged.ll ├── MergeBB_exec.ll ├── MergeBB_no_merge.ll ├── MergeBB_switch_merged.ll ├── MergeBB_switch_not_merged.ll ├── OpcodeCounter.ll ├── OpcodeCounter_OptimisationPipeline.ll ├── StaticCallCounter_Basic.ll ├── StaticCallCounter_Loop.ll ├── StaticCallCounter_NestedCalls.ll ├── StaticCallCounter_Ptr.ll ├── auto_registration_new_pm.ll ├── hello.ll ├── inject_func_call.ll ├── inject_func_call_exec.ll ├── lit.cfg.py ├── lit.site.cfg.py.in ├── llvm │ ├── always-inline.ll │ ├── dce.ll │ ├── licm.ll │ ├── loop-deletion.ll │ ├── memcpyopt.ll │ ├── reassociate.ll │ ├── slp_aarch64.ll │ └── slp_x86.ll ├── riv_float.ll ├── riv_global.ll ├── riv_integer.ll └── static-test1.ll ├── tools ├── CMakeLists.txt └── StaticMain.cpp └── utils └── static_registration.sh /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | 3 | -------------------------------------------------------------------------------- /.github/workflows/apple-silicon-llvm-from-sources.yml: -------------------------------------------------------------------------------- 1 | name: Apple Silicon (from sources) 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | runs-on: macos-latest 11 | steps: 12 | - uses: actions/checkout@v1 13 | - name: Download Ninja 14 | run: | 15 | git clone https://github.com/ninja-build/ninja.git 16 | pushd ninja 17 | ./configure.py --bootstrap 18 | popd 19 | - name: Add Ninja to $PATH 20 | run: | 21 | echo "${GITHUB_WORKSPACE}/ninja" >> $GITHUB_PATH 22 | - name: Clone llvm-project 23 | run: | 24 | git clone --depth 1 --single-branch --branch release/19.x https://github.com/llvm/llvm-project 25 | - name: Build LLVM 26 | run: | 27 | cd llvm-project 28 | mkdir build && cd build 29 | cmake -G Ninja \ 30 | -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang" \ 31 | -DLLVM_TARGETS_TO_BUILD="host;X86" -DLLVM_OPTIMIZED_TABLEGEN=ON \ 32 | ../llvm 33 | # Note that only the required tools are built 34 | ninja clang opt lli not FileCheck 35 | - name: Install lit 36 | run: | 37 | brew install lit 38 | - name: Build HelloWorld 39 | run: | 40 | cd HelloWorld 41 | mkdir build && cd build 42 | cmake -DLT_LLVM_INSTALL_DIR="$GITHUB_WORKSPACE/llvm-project/build" ../ 43 | make -j2 44 | - name: Build llvm-tutor + run tests 45 | run: | 46 | mkdir build && cd build 47 | cmake -DLT_LLVM_INSTALL_DIR="$GITHUB_WORKSPACE/llvm-project/build" ../ 48 | make -j2 49 | lit -va test/ 50 | -------------------------------------------------------------------------------- /.github/workflows/apple-silicon.yml: -------------------------------------------------------------------------------- 1 | name: Apple Silicon 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: [ main ] 7 | schedule: 8 | - cron: '0 0 * * *' 9 | workflow_dispatch: 10 | 11 | jobs: 12 | build: 13 | runs-on: macos-14 14 | strategy: 15 | matrix: 16 | type: [Debug, Release] 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Unbreak Python in Github Actions 20 | # See: 21 | # * https://github.com/actions/runner-images/issues/2322 22 | # * https://github.com/libui-ng/libui-ng/commit/97d9601e74fadea1f8d1869c77acbe24be2886e2 23 | run: | 24 | find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete 25 | sudo rm -rf /Library/Frameworks/Python.framework/ 26 | brew install --force python3 && brew unlink python3 && brew unlink python3 && brew link --overwrite python3 27 | - name: Install Dependencies 28 | run: | 29 | brew update 30 | brew install llvm@19 31 | brew install lit 32 | - name: Build HelloWorld 33 | run: | 34 | cd HelloWorld 35 | mkdir build && cd build 36 | cmake -DLT_LLVM_INSTALL_DIR="/opt/homebrew/opt/llvm@19/" -DCMAKE_BUILD_TYPE=${{ matrix.type }} ../ 37 | make -j2 38 | - name: Build llvm-tutor + run tests 39 | run: | 40 | cd $GITHUB_WORKSPACE 41 | mkdir build && cd build 42 | cmake -DLT_LLVM_INSTALL_DIR="/opt/homebrew/opt/llvm@19/" -DCMAKE_BUILD_TYPE=${{ matrix.type }} ../ 43 | make -j2 44 | lit test/ 45 | -------------------------------------------------------------------------------- /.github/workflows/docker-archlinux.yml: -------------------------------------------------------------------------------- 1 | name: docker-archlinux 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Build the Docker image 16 | run: docker build . --file Dockerfile_archlinux --tag llvm-tutor:llvm-19 17 | -------------------------------------------------------------------------------- /.github/workflows/docker-fedora.yml: -------------------------------------------------------------------------------- 1 | name: docker-fedora 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Build the Docker image 16 | run: docker build . --file Dockerfile_fedora --tag llvm-tutor:llvm-19 17 | -------------------------------------------------------------------------------- /.github/workflows/docker-ubuntu.yml: -------------------------------------------------------------------------------- 1 | name: docker-ubuntu 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Build the Docker image 16 | run: docker build . --file Dockerfile_ubuntu --tag llvm-tutor:llvm-19 17 | -------------------------------------------------------------------------------- /.github/workflows/docker-ubunut-apt.yml: -------------------------------------------------------------------------------- 1 | name: docker-ubuntu-apt 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Build the Docker image 16 | run: docker build . --file Dockerfile_ubuntu_apt --tag llvm-tutor:llvm-19 17 | -------------------------------------------------------------------------------- /.github/workflows/x86-ubuntu-llvm-from-sources-static.yml: -------------------------------------------------------------------------------- 1 | name: x86-Ubuntu (LLVM from sources - static linking) 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-22.04 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Download Ninja 14 | run: | 15 | git clone https://github.com/ninja-build/ninja.git 16 | pushd ninja 17 | ./configure.py --bootstrap 18 | popd 19 | - name: Add Ninja to $PATH 20 | run: | 21 | echo "${GITHUB_WORKSPACE}/ninja" >> $GITHUB_PATH 22 | - name: Clone llvm-project 23 | run: | 24 | git clone --depth 1 --single-branch --branch release/19.x https://github.com/llvm/llvm-project 25 | cd llvm-project 26 | - name: Register MBAAdd statically 27 | run: | 28 | bash utils/static_registration.sh --llvm_project_dir "$GITHUB_WORKSPACE/llvm-project" 29 | - name: Build LLVM 30 | run: | 31 | cd llvm-project 32 | mkdir build && cd build 33 | ln -s /usr/bin/x86_64-linux-gnu-ld.gold ld 34 | cmake -G Ninja \ 35 | -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang" \ 36 | -DLLVM_TARGETS_TO_BUILD="host" -DLLVM_OPTIMIZED_TABLEGEN=ON \ 37 | -DLLVM_BUILD_EXAMPLES=On -DLLVM_MBASUB_LINK_INTO_TOOLS=On \ 38 | ../llvm 39 | # Note that only the required tools are built 40 | ninja 41 | - name: Install lit 42 | run: | 43 | sudo apt-get install python3-setuptools 44 | sudo pip3 install lit 45 | - name: Run MBASub - sanity check 46 | run: | 47 | $GITHUB_WORKSPACE/llvm-project/build/bin/opt -passes=mba-sub -S $GITHUB_WORKSPACE/test/MBA_sub.ll 48 | - name: Run MBASub tests 49 | run: | 50 | $GITHUB_WORKSPACE/llvm-project/build/bin/llvm-lit $GITHUB_WORKSPACE/llvm-project/llvm/test/Examples/MBASub/ 51 | -------------------------------------------------------------------------------- /.github/workflows/x86-ubuntu-llvm-from-sources.yml: -------------------------------------------------------------------------------- 1 | name: x86-Ubuntu-llvm-from-sources 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-22.04 11 | steps: 12 | - uses: actions/checkout@v1 13 | - name: Download Ninja 14 | run: | 15 | git clone https://github.com/ninja-build/ninja.git 16 | pushd ninja 17 | ./configure.py --bootstrap 18 | # chmod +x ninja 19 | ./ninja --version 20 | popd 21 | - name: Add Ninja to $PATH 22 | run: | 23 | echo "${GITHUB_WORKSPACE}/ninja" >> $GITHUB_PATH 24 | - name: Clone llvm-project 25 | run: | 26 | git clone --depth 10 --single-branch --branch release/19.x https://github.com/llvm/llvm-project 27 | - name: Build LLVM 28 | run: | 29 | cd llvm-project 30 | mkdir build && cd build 31 | ln -s /usr/bin/x86_64-linux-gnu-ld.gold ld 32 | cmake -G Ninja \ 33 | -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang" \ 34 | -DLLVM_TARGETS_TO_BUILD="X86;AArch64" -DLLVM_OPTIMIZED_TABLEGEN=ON \ 35 | ../llvm 36 | # Note that only the required tools are built 37 | ninja clang opt lli not FileCheck 38 | - name: Install lit 39 | run: | 40 | sudo apt-get install python3-setuptools 41 | sudo pip3 install lit 42 | - name: Build HelloWorld 43 | run: | 44 | cd HelloWorld 45 | mkdir build && cd build 46 | cmake -DLT_LLVM_INSTALL_DIR="$GITHUB_WORKSPACE/llvm-project/build" ../ 47 | make -j2 48 | - name: Build llvm-tutor + run tests 49 | run: | 50 | mkdir build && cd build 51 | cmake -DLT_LLVM_INSTALL_DIR="$GITHUB_WORKSPACE/llvm-project/build" ../ 52 | make -j2 53 | lit test/ 54 | -------------------------------------------------------------------------------- /.github/workflows/x86-ubuntu.yml: -------------------------------------------------------------------------------- 1 | name: x86-Ubuntu 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: [ master ] 7 | schedule: 8 | - cron: '0 0 * * *' 9 | workflow_dispatch: 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-22.04 14 | strategy: 15 | matrix: 16 | compiler: 17 | - { compiler: GNU, CC: gcc, CXX: g++ } 18 | - { compiler: LLVM, CC: clang, CXX: clang++ } 19 | type: [Debug, Release] 20 | steps: 21 | - uses: actions/checkout@v1 22 | - name: Install Dependencies 23 | env: 24 | CC: ${{ matrix.compiler.CC }} 25 | CXX: ${{ matrix.compiler.CXX }} 26 | run: | 27 | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - 28 | sudo apt-add-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-19 main" 29 | sudo apt-get update 30 | sudo apt-get install -y llvm-19 llvm-19-dev llvm-19-tools clang-19 31 | sudo apt-get install python3-setuptools 32 | sudo pip3 install lit 33 | - name: Build HelloWorld 34 | env: 35 | CC: ${{ matrix.compiler.CC }} 36 | CXX: ${{ matrix.compiler.CXX }} 37 | run: | 38 | cd HelloWorld 39 | mkdir build && cd build 40 | cmake -DLT_LLVM_INSTALL_DIR="/usr/lib/llvm-19/" -DCMAKE_BUILD_TYPE=${{ matrix.type }} ../ 41 | make -j2 42 | - name: Build llvm-tutor + run tests 43 | run: | 44 | cd $GITHUB_WORKSPACE 45 | mkdir build && cd build 46 | cmake -DLT_LLVM_INSTALL_DIR="/usr/lib/llvm-19/" -DCMAKE_BUILD_TYPE=${{ matrix.type }} ../ 47 | make -j2 48 | lit -va test/ 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | project(llvm-tutor) 3 | 4 | #=============================================================================== 5 | # 1. VERIFY LLVM INSTALLATION DIR 6 | # This is just a bit of a sanity checking. 7 | #=============================================================================== 8 | set(LT_LLVM_INSTALL_DIR "" CACHE PATH "LLVM installation directory") 9 | 10 | # 1.1 Check the "include" directory 11 | set(LT_LLVM_INCLUDE_DIR "${LT_LLVM_INSTALL_DIR}/include/llvm") 12 | if(NOT EXISTS "${LT_LLVM_INCLUDE_DIR}") 13 | message(FATAL_ERROR 14 | " LT_LLVM_INSTALL_DIR (${LT_LLVM_INCLUDE_DIR}) is invalid.") 15 | endif() 16 | 17 | # 1.2 Check that the LLVMConfig.cmake file exists (the location depends on the 18 | # OS) 19 | set(LT_VALID_INSTALLATION FALSE) 20 | 21 | # Ubuntu + Darwin 22 | if(EXISTS "${LT_LLVM_INSTALL_DIR}/lib/cmake/llvm/LLVMConfig.cmake") 23 | set(LT_VALID_INSTALLATION TRUE) 24 | endif() 25 | 26 | # Fedora 27 | if(EXISTS "${LT_LLVM_INSTALL_DIR}/lib64/cmake/llvm/LLVMConfig.cmake") 28 | set(LT_VALID_INSTALLATION TRUE) 29 | endif() 30 | 31 | if(NOT ${LT_VALID_INSTALLATION}) 32 | message(FATAL_ERROR 33 | "LLVM installation directory, (${LT_LLVM_INSTALL_DIR}), is invalid. Couldn't 34 | find LLVMConfig.cmake.") 35 | endif() 36 | 37 | #=============================================================================== 38 | # 2. LOAD LLVM CONFIGURATION 39 | # For more: http://llvm.org/docs/CMake.html#embedding-llvm-in-your-project 40 | #=============================================================================== 41 | # Add the location of LLVMConfig.cmake to CMake search paths (so that 42 | # find_package can locate it) 43 | # Note: On Fedora, when using the pre-compiled binaries installed with `dnf`, 44 | # LLVMConfig.cmake is located in "/usr/lib64/cmake/llvm". But this path is 45 | # among other paths that will be checked by default when using 46 | # `find_package(llvm)`. So there's no need to add it here. 47 | list(APPEND CMAKE_PREFIX_PATH "${LT_LLVM_INSTALL_DIR}/lib/cmake/llvm/") 48 | 49 | # The way LLVMConfigVersion.cmake is set up, it will only match MAJOR.MINOR 50 | # exactly, even if we do not specify "REQUIRED" in the statement below. 51 | # So we accept any version and do the proper ranged check below. 52 | find_package(LLVM CONFIG) 53 | 54 | # We defer the version checking to this statement 55 | if("${LLVM_VERSION_MAJOR}" VERSION_LESS 19) 56 | message(FATAL_ERROR "Found LLVM ${LLVM_VERSION_MAJOR}, but need LLVM 19 or above") 57 | endif() 58 | 59 | message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") 60 | message(STATUS "Using LLVMConfig.cmake in: ${LT_LLVM_INSTALL_DIR}") 61 | 62 | message("LLVM STATUS: 63 | Definitions ${LLVM_DEFINITIONS} 64 | Includes ${LLVM_INCLUDE_DIRS} 65 | Libraries ${LLVM_LIBRARY_DIRS} 66 | Targets ${LLVM_TARGETS_TO_BUILD}" 67 | ) 68 | 69 | # Set the LLVM header and library paths 70 | include_directories(SYSTEM ${LLVM_INCLUDE_DIRS}) 71 | link_directories(${LLVM_LIBRARY_DIRS}) 72 | add_definitions(${LLVM_DEFINITIONS}) 73 | 74 | #=============================================================================== 75 | # 3. LLVM-TUTOR BUILD CONFIGURATION 76 | #=============================================================================== 77 | # Use the same C++ standard as LLVM does 78 | set(CMAKE_CXX_STANDARD 17 CACHE STRING "") 79 | 80 | # Build type 81 | if (NOT CMAKE_BUILD_TYPE) 82 | set(CMAKE_BUILD_TYPE Debug CACHE 83 | STRING "Build type (default Debug):" FORCE) 84 | endif() 85 | 86 | # Compiler flags 87 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall\ 88 | -fdiagnostics-color=always") 89 | 90 | # LLVM is normally built without RTTI. Be consistent with that. 91 | if(NOT LLVM_ENABLE_RTTI) 92 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") 93 | endif() 94 | 95 | # -fvisibility-inlines-hidden is set when building LLVM and on Darwin warnings 96 | # are triggered if llvm-tutor is built without this flag (though otherwise it 97 | # builds fine). For consistency, add it here too. 98 | include(CheckCXXCompilerFlag) 99 | check_cxx_compiler_flag("-fvisibility-inlines-hidden" SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG) 100 | if (${SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG} EQUAL "1") 101 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden") 102 | endif() 103 | 104 | # Set the build directories 105 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") 106 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") 107 | 108 | #=============================================================================== 109 | # 4. ADD SUB-TARGETS 110 | # Doing this at the end so that all definitions and link/include paths are 111 | # available for the sub-projects. 112 | #=============================================================================== 113 | add_subdirectory(lib) 114 | add_subdirectory(tools) 115 | add_subdirectory(test) 116 | add_subdirectory(HelloWorld) 117 | -------------------------------------------------------------------------------- /Dockerfile_archlinux: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # An Arch Linux docker file for llvm-tutor. Clones and builds llvm-tutor, runs 3 | # all tests. It uses the precompiled LLVM packages from Fedora. 4 | # 5 | # USAGE: 6 | # ```bash 7 | # wget https://raw.githubusercontent.com/banach-space/llvm-tutor/main/Dockerfile_archlinux 8 | # docker build -t=llvm-tutor:llvm-19 . 9 | # docker run --rm -it --hostname=llvm-tutor llvm-tutor:llvm-19 /bin/bash 10 | # ``` 11 | # ============================================================================= 12 | 13 | FROM archlinux 14 | 15 | ENV LLVM_DIR /usr/ 16 | ENV TUTOR_DIR /llvm-tutor 17 | ENV VENV_DIR /venv 18 | 19 | # 1. INSTALL DEPENDENCIES 20 | RUN pacman -Syu --noconfirm \ 21 | git \ 22 | zstd \ 23 | cmake \ 24 | ninja \ 25 | gcc \ 26 | llvm \ 27 | clang \ 28 | python-pip 29 | 30 | # 2. INSTALL LIT 31 | RUN python3 -m venv $VENV_DIR 32 | RUN $VENV_DIR/bin/pip3 install lit 33 | 34 | # 3. CLONE LLVM-TUTOR 35 | RUN git clone https://github.com/banach-space/llvm-tutor $TUTOR_DIR 36 | 37 | # 4. BUILD AND RUN HELLO-WORLD 38 | RUN mkdir -p $TUTOR_DIR/hello-world-build \ 39 | && cd $TUTOR_DIR/hello-world-build \ 40 | && cmake -G Ninja -DLT_LLVM_INSTALL_DIR=$LLVM_DIR ../HelloWorld \ 41 | && ninja 42 | RUN cd $TUTOR_DIR/hello-world-build && $LLVM_DIR/bin/clang -S -O1 -emit-llvm ../inputs/input_for_hello.c -o input_for_hello.ll 43 | RUN cd $TUTOR_DIR/hello-world-build && $LLVM_DIR/bin/opt -load-pass-plugin ./libHelloWorld.so -passes=hello-world -disable-output input_for_hello.ll 2>&1 | grep "(llvm-tutor) Hello from: foo" 44 | 45 | # 5. BUILD AND RUN LLVM-TUTOR 46 | RUN mkdir -p $TUTOR_DIR/build \ 47 | && cd $TUTOR_DIR/build \ 48 | && cmake -G Ninja -DLT_LLVM_INSTALL_DIR=$LLVM_DIR ../ \ 49 | && ninja \ 50 | && $VENV_DIR/bin/lit test/ 51 | -------------------------------------------------------------------------------- /Dockerfile_fedora: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # A Fedora docker file for llvm-tutor. Clones and builds llvm-tutor, runs all 3 | # tests. It uses the precompiled LLVM packages from Fedora. 4 | # 5 | # USAGE: 6 | # ```bash 7 | # wget https://raw.githubusercontent.com/banach-space/llvm-tutor/main/Dockerfile_fedora 8 | # docker build -t=llvm-tutor:llvm-19 . 9 | # docker run --rm -it --hostname=llvm-tutor llvm-tutor:llvm-19 /bin/bash 10 | # ``` 11 | # ============================================================================= 12 | 13 | FROM fedora:41 14 | 15 | ENV LLVM_DIR /usr/ 16 | ENV TUTOR_DIR /llvm-tutor 17 | 18 | # 1. INSTALL DEPENDENCIES 19 | RUN dnf -y install \ 20 | git \ 21 | cmake \ 22 | ninja-build \ 23 | gcc \ 24 | gcc-c++ \ 25 | llvm-devel \ 26 | clang \ 27 | zlib-devel \ 28 | libzstd-devel \ 29 | python3-pip 30 | 31 | # 2. INSTALL LIT 32 | RUN pip3 install lit 33 | 34 | # 3. CLONE LLVM-TUTOR 35 | RUN git clone https://github.com/banach-space/llvm-tutor $TUTOR_DIR 36 | 37 | # 4. BUILD AND RUN HELLO-WORLD 38 | RUN mkdir -p $TUTOR_DIR/hello-world-build \ 39 | && cd $TUTOR_DIR/hello-world-build \ 40 | && cmake -G Ninja -DLT_LLVM_INSTALL_DIR=$LLVM_DIR ../HelloWorld \ 41 | && ninja 42 | RUN cd $TUTOR_DIR/hello-world-build && $LLVM_DIR/bin/clang -S -O1 -emit-llvm ../inputs/input_for_hello.c -o input_for_hello.ll 43 | RUN cd $TUTOR_DIR/hello-world-build && $LLVM_DIR/bin/opt -load-pass-plugin ./libHelloWorld.so -passes=hello-world -disable-output input_for_hello.ll 2>&1 | grep "(llvm-tutor) Hello from: foo" 44 | 45 | # 5. BUILD AND RUN LLVM-TUTOR 46 | RUN mkdir -p $TUTOR_DIR/build \ 47 | && cd $TUTOR_DIR/build \ 48 | && cmake -DLT_LLVM_INSTALL_DIR=$LLVM_DIR ../ \ 49 | && make -j $(nproc --all) \ 50 | && lit test/ 51 | -------------------------------------------------------------------------------- /Dockerfile_ubuntu: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # An Ubuntu docker file for llvm-tutor that builds LLVM from sources. Clones 3 | # and builds llvm-tutor, runs all tests. It uses the precompiled LLVM packages 4 | # from Ubuntu. 5 | # 6 | # USAGE: 7 | # ```bash 8 | # wget https://raw.githubusercontent.com/banach-space/llvm-tutor/main/Dockerfile_ubuntu 9 | # docker build -t=llvm-tutor:llvm-19 . 10 | # docker run --rm -it --hostname=llvm-tutor llvm-tutor:llvm-19 /bin/bash 11 | # ``` 12 | # ============================================================================= 13 | 14 | FROM ubuntu:22.04 15 | 16 | # 1. INSTALL DEPENDENCIES 17 | RUN apt-get update && apt-get install -y \ 18 | git \ 19 | cmake \ 20 | libzstd-dev \ 21 | ninja-build \ 22 | build-essential \ 23 | python3-minimal python3-pip\ 24 | && rm -rf /var/lib/apt/lists/* 25 | 26 | # 2. INSTALL LIT 27 | RUN pip3 install lit 28 | 29 | # 2. BUILD LLVM + Clang from sources 30 | # Note that LIT tests depend on 'not' and 'FileCheck', LLVM utilities. For this 31 | # reason, we need to add `-DLLVM_INSTALL_UTILS=ON` CMake flag when building 32 | # LLVM. 33 | ENV LLVM_DIR /opt/llvm 34 | RUN git clone --branch release/19.x --depth 1 https://github.com/llvm/llvm-project \ 35 | && mkdir -p $LLVM_DIR \ 36 | && mkdir -p llvm-project/build \ 37 | && cd llvm-project/build \ 38 | && cmake -G Ninja \ 39 | -DLLVM_ENABLE_PROJECTS=clang \ 40 | -DLLVM_TARGETS_TO_BUILD=host \ 41 | -DCMAKE_BUILD_TYPE=Release \ 42 | -DCMAKE_INSTALL_PREFIX=$LLVM_DIR \ 43 | -DLLVM_INSTALL_UTILS=ON \ 44 | ../llvm \ 45 | && cmake --build . --target install \ 46 | && rm -r /llvm-project 47 | 48 | # 5. BUILD AND RUN LLVM-TUTOR 49 | ENV TUTOR_DIR /llvm-tutor 50 | RUN git clone https://github.com/banach-space/llvm-tutor $TUTOR_DIR \ 51 | && mkdir -p $TUTOR_DIR/build \ 52 | && cd $TUTOR_DIR/build \ 53 | && cmake -DLT_LLVM_INSTALL_DIR=$LLVM_DIR ../ \ 54 | && make -j $(nproc --all) \ 55 | && lit test/ 56 | -------------------------------------------------------------------------------- /Dockerfile_ubuntu_apt: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # An Ubuntu docker file for llvm-tutor that installs LLVM via apt. Clones and 3 | # builds llvm-tutor, runs all tests. It uses the precompiled LLVM packages from 4 | # Ubuntu. 5 | # 6 | # USAGE: 7 | # ```bash 8 | # wget https://raw.githubusercontent.com/banach-space/llvm-tutor/main/Dockerfile_ubuntu_apt 9 | # docker build -t=llvm-tutor:llvm-19 . 10 | # docker run --rm -it --hostname=llvm-tutor llvm-tutor:llvm-19 /bin/bash 11 | # ``` 12 | # ============================================================================= 13 | 14 | FROM ubuntu:22.04 15 | 16 | ENV LLVM_DIR /usr/lib/llvm-19/ 17 | ENV TUTOR_DIR /llvm-tutor 18 | 19 | # 1. INSTALL DEPENDENCIES 20 | # As `tzdata` gets installed automatically (AFAIK, Python depends on it), it 21 | # will interrupt the set-up with a question about the time-zone to use. However, 22 | # this is a non-interactive sessions and that won't work. Work around it by 23 | # setting the time-zone here. 24 | ENV TZ=Europe/London 25 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 26 | RUN apt-get update && apt-get install -y \ 27 | git \ 28 | cmake \ 29 | ninja-build \ 30 | build-essential \ 31 | python3-minimal python3-pip\ 32 | wget \ 33 | libzstd-dev \ 34 | software-properties-common \ 35 | && rm -rf /var/lib/apt/lists/* 36 | 37 | RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - 38 | RUN apt-add-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-19 main" 39 | RUN apt-get update 40 | RUN apt-get install -y llvm-19 llvm-19-dev llvm-19-tools clang-19 41 | RUN apt-get install -y python3-setuptools 42 | 43 | # 2. INSTALL LIT 44 | RUN pip3 install lit 45 | 46 | # 3. CLONE LLVM-TUTOR 47 | RUN git clone https://github.com/banach-space/llvm-tutor $TUTOR_DIR 48 | 49 | # 4. BUILD AND RUN HELLO-WORLD 50 | RUN mkdir -p $TUTOR_DIR/hello-world-build \ 51 | && cd $TUTOR_DIR/hello-world-build \ 52 | && cmake -G Ninja -DLT_LLVM_INSTALL_DIR=$LLVM_DIR ../HelloWorld \ 53 | && ninja 54 | RUN cd $TUTOR_DIR/hello-world-build && /usr/bin/clang-19 -S -O1 -emit-llvm ../inputs/input_for_hello.c -o input_for_hello.ll 55 | RUN cd $TUTOR_DIR/hello-world-build && /usr/bin/opt-19 -load-pass-plugin ./libHelloWorld.so -passes=hello-world -disable-output input_for_hello.ll 2>&1 | grep "(llvm-tutor) Hello from: foo" 56 | 57 | # 5. BUILD AND RUN LLVM-TUTOR 58 | RUN mkdir -p $TUTOR_DIR/build \ 59 | && cd $TUTOR_DIR/build \ 60 | && cmake -DLT_LLVM_INSTALL_DIR=$LLVM_DIR ../ \ 61 | && make -j $(nproc --all) \ 62 | && lit test/ 63 | -------------------------------------------------------------------------------- /HelloWorld/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | project(llvm-tutor-hello-world) 3 | 4 | #=============================================================================== 5 | # 1. LOAD LLVM CONFIGURATION 6 | #=============================================================================== 7 | # Set this to a valid LLVM installation dir 8 | set(LT_LLVM_INSTALL_DIR "" CACHE PATH "LLVM installation directory") 9 | 10 | # Add the location of LLVMConfig.cmake to CMake search paths (so that 11 | # find_package can locate it) 12 | list(APPEND CMAKE_PREFIX_PATH "${LT_LLVM_INSTALL_DIR}/lib/cmake/llvm/") 13 | 14 | find_package(LLVM CONFIG) 15 | if("${LLVM_VERSION_MAJOR}" VERSION_LESS 19) 16 | message(FATAL_ERROR "Found LLVM ${LLVM_VERSION_MAJOR}, but need LLVM 19 or above") 17 | endif() 18 | 19 | # HelloWorld includes headers from LLVM - update the include paths accordingly 20 | include_directories(SYSTEM ${LLVM_INCLUDE_DIRS}) 21 | 22 | #=============================================================================== 23 | # 2. LLVM-TUTOR BUILD CONFIGURATION 24 | #=============================================================================== 25 | # Use the same C++ standard as LLVM does 26 | set(CMAKE_CXX_STANDARD 17 CACHE STRING "") 27 | 28 | # LLVM is normally built without RTTI. Be consistent with that. 29 | if(NOT LLVM_ENABLE_RTTI) 30 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") 31 | endif() 32 | 33 | #=============================================================================== 34 | # 3. ADD THE TARGET 35 | #=============================================================================== 36 | add_library(HelloWorld SHARED HelloWorld.cpp) 37 | 38 | # Allow undefined symbols in shared objects on Darwin (this is the default 39 | # behaviour on Linux) 40 | target_link_libraries(HelloWorld 41 | "$<$:-undefined dynamic_lookup>") 42 | -------------------------------------------------------------------------------- /HelloWorld/HelloWorld.cpp: -------------------------------------------------------------------------------- 1 | //============================================================================= 2 | // FILE: 3 | // HelloWorld.cpp 4 | // 5 | // DESCRIPTION: 6 | // Visits all functions in a module, prints their names and the number of 7 | // arguments via stderr. Strictly speaking, this is an analysis pass (i.e. 8 | // the functions are not modified). However, in order to keep things simple 9 | // there's no 'print' method here (every analysis pass should implement it). 10 | // 11 | // USAGE: 12 | // New PM 13 | // opt -load-pass-plugin=libHelloWorld.dylib -passes="hello-world" `\` 14 | // -disable-output 15 | // 16 | // 17 | // License: MIT 18 | //============================================================================= 19 | #include "llvm/IR/LegacyPassManager.h" 20 | #include "llvm/Passes/PassBuilder.h" 21 | #include "llvm/Passes/PassPlugin.h" 22 | #include "llvm/Support/raw_ostream.h" 23 | 24 | using namespace llvm; 25 | 26 | //----------------------------------------------------------------------------- 27 | // HelloWorld implementation 28 | //----------------------------------------------------------------------------- 29 | // No need to expose the internals of the pass to the outside world - keep 30 | // everything in an anonymous namespace. 31 | namespace { 32 | 33 | // This method implements what the pass does 34 | void visitor(Function &F) { 35 | errs() << "(llvm-tutor) Hello from: "<< F.getName() << "\n"; 36 | errs() << "(llvm-tutor) number of arguments: " << F.arg_size() << "\n"; 37 | } 38 | 39 | // New PM implementation 40 | struct HelloWorld : PassInfoMixin { 41 | // Main entry point, takes IR unit to run the pass on (&F) and the 42 | // corresponding pass manager (to be queried if need be) 43 | PreservedAnalyses run(Function &F, FunctionAnalysisManager &) { 44 | visitor(F); 45 | return PreservedAnalyses::all(); 46 | } 47 | 48 | // Without isRequired returning true, this pass will be skipped for functions 49 | // decorated with the optnone LLVM attribute. Note that clang -O0 decorates 50 | // all functions with optnone. 51 | static bool isRequired() { return true; } 52 | }; 53 | } // namespace 54 | 55 | //----------------------------------------------------------------------------- 56 | // New PM Registration 57 | //----------------------------------------------------------------------------- 58 | llvm::PassPluginLibraryInfo getHelloWorldPluginInfo() { 59 | return {LLVM_PLUGIN_API_VERSION, "HelloWorld", LLVM_VERSION_STRING, 60 | [](PassBuilder &PB) { 61 | PB.registerPipelineParsingCallback( 62 | [](StringRef Name, FunctionPassManager &FPM, 63 | ArrayRef) { 64 | if (Name == "hello-world") { 65 | FPM.addPass(HelloWorld()); 66 | return true; 67 | } 68 | return false; 69 | }); 70 | }}; 71 | } 72 | 73 | // This is the core interface for pass plugins. It guarantees that 'opt' will 74 | // be able to recognize HelloWorld when added to the pass pipeline on the 75 | // command line, i.e. via '-passes=hello-world' 76 | extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo 77 | llvmGetPassPluginInfo() { 78 | return getHelloWorldPluginInfo(); 79 | } 80 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Andrzej Warzyński 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /include/ConvertFCmpEq.h: -------------------------------------------------------------------------------- 1 | //============================================================================== 2 | // FILE: 3 | // ConvertFCmpEq.h 4 | // 5 | // DESCRIPTION: 6 | // Declares the ConvertFCmpEq pass for the new and the legacy pass managers. 7 | // 8 | // License: MIT 9 | //============================================================================== 10 | 11 | #ifndef LLVM_TUTOR_CONVERT_FCMP_EQ_H 12 | #define LLVM_TUTOR_CONVERT_FCMP_EQ_H 13 | 14 | #include "FindFCmpEq.h" 15 | #include "llvm/IR/PassManager.h" 16 | #include "llvm/Pass.h" 17 | 18 | // Forward declarations 19 | namespace llvm { 20 | 21 | class Function; 22 | 23 | } // namespace llvm 24 | 25 | //------------------------------------------------------------------------------ 26 | // New PM interface 27 | //------------------------------------------------------------------------------ 28 | 29 | struct ConvertFCmpEq : llvm::PassInfoMixin { 30 | // This is one of the standard run() member functions expected by 31 | // PassInfoMixin. When the pass is executed by the new PM, this is the 32 | // function that will be called. 33 | llvm::PreservedAnalyses run(llvm::Function &Func, 34 | llvm::FunctionAnalysisManager &FAM); 35 | // This is a helper run() member function overload which can be called by the 36 | // legacy pass (or any other code) without having to supply a 37 | // FunctionAnalysisManager argument. 38 | bool run(llvm::Function &Func, const FindFCmpEq::Result &Comparisons); 39 | 40 | // Without isRequired returning true, this pass will be skipped for functions 41 | // decorated with the optnone LLVM attribute. Note that clang -O0 decorates 42 | // all functions with optnone. 43 | static bool isRequired() { return true; } 44 | }; 45 | 46 | #endif // !LLVM_TUTOR_CONVERT_FCMP_EQ_H 47 | -------------------------------------------------------------------------------- /include/DuplicateBB.h: -------------------------------------------------------------------------------- 1 | //============================================================================== 2 | // FILE: 3 | // DuplicateBB.h 4 | // 5 | // DESCRIPTION: 6 | // Declares the DuplicateBB pass 7 | // 8 | // License: MIT 9 | //============================================================================== 10 | #ifndef LLVM_TUTOR_DUPLICATE_BB_H 11 | #define LLVM_TUTOR_DUPLICATE_BB_H 12 | 13 | #include "RIV.h" 14 | #include "llvm/IR/BasicBlock.h" 15 | #include "llvm/IR/PassManager.h" 16 | #include "llvm/IR/ValueMap.h" 17 | #include "llvm/Pass.h" 18 | 19 | #include 20 | #include 21 | 22 | namespace llvm { 23 | class RandomNumberGenerator; 24 | } // namespace llvm 25 | 26 | //------------------------------------------------------------------------------ 27 | // New PM interface 28 | //------------------------------------------------------------------------------ 29 | struct DuplicateBB : public llvm::PassInfoMixin { 30 | llvm::PreservedAnalyses run(llvm::Function &F, 31 | llvm::FunctionAnalysisManager &); 32 | 33 | // Maps BB, a BasicBlock, to one integer value (defined in a different 34 | // BasicBlock) that's reachable in BB. The Value that BB is mapped to is used 35 | // in the `if-then-else` construct when cloning BB. 36 | using BBToSingleRIVMap = 37 | std::vector>; 38 | // Maps a Value before duplication to a Phi node that merges the 39 | // corresponding values after duplication/cloning. 40 | using ValueToPhiMap = std::map; 41 | 42 | // Creates a BBToSingleRIVMap of BasicBlocks that are suitable for cloning. 43 | BBToSingleRIVMap findBBsToDuplicate(llvm::Function &F, 44 | const RIV::Result &RIVResult); 45 | 46 | // Clones the input basic block: 47 | // * injects an `if-then-else` construct using ContextValue 48 | // * duplicates BB 49 | // * adds PHI nodes as required 50 | void cloneBB(llvm::BasicBlock &BB, llvm::Value *ContextValue, 51 | ValueToPhiMap &ReMapper); 52 | 53 | unsigned DuplicateBBCount = 0; 54 | 55 | // Without isRequired returning true, this pass will be skipped for functions 56 | // decorated with the optnone LLVM attribute. Note that clang -O0 decorates 57 | // all functions with optnone. 58 | static bool isRequired() { return true; } 59 | 60 | std::unique_ptr pRNG; 61 | }; 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /include/DynamicCallCounter.h: -------------------------------------------------------------------------------- 1 | //============================================================================== 2 | // FILE: 3 | // DynamicCallCounter.h 4 | // 5 | // DESCRIPTION: 6 | // Declares the DynamicCallCounter pass for the new and the legacy pass 7 | // managers. 8 | // 9 | // License: MIT 10 | //============================================================================== 11 | #ifndef LLVM_TUTOR_INSTRUMENT_BASIC_H 12 | #define LLVM_TUTOR_INSTRUMENT_BASIC_H 13 | 14 | #include "llvm/IR/Module.h" 15 | #include "llvm/IR/PassManager.h" 16 | #include "llvm/Pass.h" 17 | 18 | //------------------------------------------------------------------------------ 19 | // New PM interface 20 | //------------------------------------------------------------------------------ 21 | struct DynamicCallCounter : public llvm::PassInfoMixin { 22 | llvm::PreservedAnalyses run(llvm::Module &M, 23 | llvm::ModuleAnalysisManager &); 24 | bool runOnModule(llvm::Module &M); 25 | 26 | // Without isRequired returning true, this pass will be skipped for functions 27 | // decorated with the optnone LLVM attribute. Note that clang -O0 decorates 28 | // all functions with optnone. 29 | static bool isRequired() { return true; } 30 | }; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /include/FindFCmpEq.h: -------------------------------------------------------------------------------- 1 | //============================================================================== 2 | // FILE: 3 | // FindFCmpEq.h 4 | // 5 | // DESCRIPTION: 6 | // Declares the FindFCmpEq pass for the new and the legacy pass managers as 7 | // well as a printing pass for the new pass manager. 8 | // 9 | // License: MIT 10 | //============================================================================== 11 | 12 | #ifndef LLVM_TUTOR_FIND_FCMP_EQ_H 13 | #define LLVM_TUTOR_FIND_FCMP_EQ_H 14 | 15 | #include "llvm/IR/PassManager.h" 16 | #include "llvm/Pass.h" 17 | #include 18 | 19 | // Forward declarations 20 | namespace llvm { 21 | 22 | class FCmpInst; 23 | class Function; 24 | class Module; 25 | class raw_ostream; 26 | 27 | } // namespace llvm 28 | 29 | //------------------------------------------------------------------------------ 30 | // New PM interface 31 | //------------------------------------------------------------------------------ 32 | class FindFCmpEq : public llvm::AnalysisInfoMixin { 33 | public: 34 | using Result = std::vector; 35 | // This is one of the standard run() member functions expected by 36 | // PassInfoMixin. When the pass is executed by the new PM, this is the 37 | // function that will be called. 38 | Result run(llvm::Function &Func, llvm::FunctionAnalysisManager &FAM); 39 | // This is a helper run() member function overload which can be called by the 40 | // legacy pass (or any other code) without having to supply a 41 | // FunctionAnalysisManager argument. 42 | Result run(llvm::Function &Func); 43 | 44 | private: 45 | friend struct llvm::AnalysisInfoMixin; 46 | static llvm::AnalysisKey Key; 47 | }; 48 | 49 | //------------------------------------------------------------------------------ 50 | // New PM interface for the printer pass 51 | //------------------------------------------------------------------------------ 52 | class FindFCmpEqPrinter : public llvm::PassInfoMixin { 53 | public: 54 | explicit FindFCmpEqPrinter(llvm::raw_ostream &OutStream) : OS(OutStream){}; 55 | 56 | llvm::PreservedAnalyses run(llvm::Function &Func, 57 | llvm::FunctionAnalysisManager &FAM); 58 | 59 | private: 60 | llvm::raw_ostream &OS; 61 | }; 62 | 63 | #endif // !LLVM_TUTOR_FIND_FCMP_EQ_H 64 | -------------------------------------------------------------------------------- /include/InjectFuncCall.h: -------------------------------------------------------------------------------- 1 | //============================================================================== 2 | // FILE: 3 | // InjectFuncCall.h 4 | // 5 | // DESCRIPTION: 6 | // Declares the InjectFuncCall pass for the new and the legacy pass managers. 7 | // 8 | // License: MIT 9 | //============================================================================== 10 | #ifndef LLVM_TUTOR_INSTRUMENT_BASIC_H 11 | #define LLVM_TUTOR_INSTRUMENT_BASIC_H 12 | 13 | #include "llvm/IR/Module.h" 14 | #include "llvm/IR/PassManager.h" 15 | #include "llvm/Pass.h" 16 | 17 | //------------------------------------------------------------------------------ 18 | // New PM interface 19 | //------------------------------------------------------------------------------ 20 | struct InjectFuncCall : public llvm::PassInfoMixin { 21 | llvm::PreservedAnalyses run(llvm::Module &M, 22 | llvm::ModuleAnalysisManager &); 23 | bool runOnModule(llvm::Module &M); 24 | 25 | // Without isRequired returning true, this pass will be skipped for functions 26 | // decorated with the optnone LLVM attribute. Note that clang -O0 decorates 27 | // all functions with optnone. 28 | static bool isRequired() { return true; } 29 | }; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /include/MBAAdd.h: -------------------------------------------------------------------------------- 1 | //============================================================================== 2 | // FILE: 3 | // MBAAdd.h 4 | // 5 | // DESCRIPTION: 6 | // Declares the MBAAdd pass for the new and the legacy pass managers. 7 | // 8 | // License: MIT 9 | //============================================================================== 10 | #ifndef LLVM_TUTOR_MBA_ADD_H 11 | #define LLVM_TUTOR_MBA_ADD_H 12 | 13 | #include "llvm/IR/BasicBlock.h" 14 | #include "llvm/IR/PassManager.h" 15 | #include "llvm/Pass.h" 16 | 17 | //------------------------------------------------------------------------------ 18 | // New PM interface 19 | //------------------------------------------------------------------------------ 20 | struct MBAAdd : public llvm::PassInfoMixin { 21 | llvm::PreservedAnalyses run(llvm::Function &F, 22 | llvm::FunctionAnalysisManager &); 23 | bool runOnBasicBlock(llvm::BasicBlock &B); 24 | 25 | // Without isRequired returning true, this pass will be skipped for functions 26 | // decorated with the optnone LLVM attribute. Note that clang -O0 decorates 27 | // all functions with optnone. 28 | static bool isRequired() { return true; } 29 | }; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /include/MBASub.h: -------------------------------------------------------------------------------- 1 | //======================================================================== 2 | // FILE: 3 | // MBASub.h 4 | // 5 | // DESCRIPTION: 6 | // Declares the MBASub pass for the new and the legacy pass managers. 7 | // 8 | // License: MIT 9 | //======================================================================== 10 | #ifndef LLVM_TUTOR_MBA_SUB_H 11 | #define LLVM_TUTOR_MBA_SUB_H 12 | 13 | #include "llvm/IR/BasicBlock.h" 14 | #include "llvm/IR/PassManager.h" 15 | #include "llvm/Pass.h" 16 | 17 | // PassInfoMixIn is a CRTP mix-in to automatically provide informational APIs 18 | // needed for passes. Currently it provides only the 'name' method. 19 | struct MBASub : public llvm::PassInfoMixin { 20 | llvm::PreservedAnalyses run(llvm::Function &F, 21 | llvm::FunctionAnalysisManager &); 22 | bool runOnBasicBlock(llvm::BasicBlock &B); 23 | 24 | // Without isRequired returning true, this pass will be skipped for functions 25 | // decorated with the optnone LLVM attribute. Note that clang -O0 decorates 26 | // all functions with optnone. 27 | static bool isRequired() { return true; } 28 | }; 29 | #endif 30 | -------------------------------------------------------------------------------- /include/MergeBB.h: -------------------------------------------------------------------------------- 1 | //======================================================================== 2 | // FILE: 3 | // MergeBB.h 4 | // 5 | // DESCRIPTION: 6 | // Declares the MergeBB Pass 7 | // 8 | // License: MIT 9 | //======================================================================== 10 | #ifndef LLVM_TUTOR_MERGEBBS_H 11 | #define LLVM_TUTOR_MERGEBBS_H 12 | 13 | #include "llvm/IR/Instruction.h" 14 | #include "llvm/IR/PassManager.h" 15 | #include "llvm/Pass.h" 16 | 17 | using ResultMergeBB = llvm::StringMap; 18 | 19 | //------------------------------------------------------------------------------ 20 | // New PM interface 21 | //------------------------------------------------------------------------------ 22 | struct MergeBB : public llvm::PassInfoMixin { 23 | using Result = ResultMergeBB; 24 | llvm::PreservedAnalyses run(llvm::Function &F, 25 | llvm::FunctionAnalysisManager &); 26 | 27 | // Checks whether the input instruction Inst (that has exactly one use) can be 28 | // removed. This is the case when its only user is either: 29 | // 1) a PHI (it can be easily updated if Inst is removed), or 30 | // 2) located in the same block as Inst (if that block is removed then the 31 | // user will also be removed) 32 | bool canRemoveInst(const llvm::Instruction *Inst); 33 | 34 | // Instructions in Insts belong to different blocks that unconditionally 35 | // branch to a common successor. Analyze them and return true if it would be 36 | // possible to merge them, i.e. replace Inst1 with Inst2 (or vice-versa). 37 | bool canMergeInstructions(llvm::ArrayRef Insts); 38 | 39 | // Replace the destination of incoming edges of BBToErase by BBToRetain 40 | unsigned updateBranchTargets(llvm::BasicBlock *BBToErase, 41 | llvm::BasicBlock *BBToRetain); 42 | 43 | // If BB is duplicated, then merges BB with its duplicate and adds BB to 44 | // DeleteList. DeleteList contains the list of blocks to be deleted. 45 | bool 46 | mergeDuplicatedBlock(llvm::BasicBlock *BB, 47 | llvm::SmallPtrSet &DeleteList); 48 | 49 | // Without isRequired returning true, this pass will be skipped for functions 50 | // decorated with the optnone LLVM attribute. Note that clang -O0 decorates 51 | // all functions with optnone. 52 | static bool isRequired() { return true; } 53 | }; 54 | 55 | //------------------------------------------------------------------------------ 56 | // Helper data structures 57 | //------------------------------------------------------------------------------ 58 | // Iterates through instructions in BB1 and BB2 in reverse order from the first 59 | // non-debug instruction. For example (assume all blocks have size n): 60 | // LockstepReverseIterator I(BB1, BB2); 61 | // *I-- = [BB1[n], BB2[n]]; 62 | // *I-- = [BB1[n-1], BB2[n-1]]; 63 | // *I-- = [BB1[n-2], BB2[n-2]]; 64 | // ... 65 | class LockstepReverseIterator { 66 | llvm::BasicBlock *BB1; 67 | llvm::BasicBlock *BB2; 68 | 69 | llvm::SmallVector Insts; 70 | bool Fail; 71 | 72 | public: 73 | LockstepReverseIterator(llvm::BasicBlock *BB1In, llvm::BasicBlock *BB2In); 74 | 75 | llvm::Instruction *getLastNonDbgInst(llvm::BasicBlock *BB); 76 | bool isValid() const { return !Fail; } 77 | 78 | void operator--(); 79 | 80 | llvm::ArrayRef operator*() const { return Insts; } 81 | }; 82 | #endif 83 | -------------------------------------------------------------------------------- /include/OpcodeCounter.h: -------------------------------------------------------------------------------- 1 | //============================================================================== 2 | // FILE: 3 | // OpcodeCounter.h 4 | // 5 | // DESCRIPTION: 6 | // Declares the OpcodeCounter Passes: 7 | // * new pass manager interface 8 | // * printer pass for the new pass manager 9 | // 10 | // License: MIT 11 | //============================================================================== 12 | #ifndef LLVM_TUTOR_OPCODECOUNTER_H 13 | #define LLVM_TUTOR_OPCODECOUNTER_H 14 | 15 | #include "llvm/ADT/StringMap.h" 16 | #include "llvm/IR/Function.h" 17 | #include "llvm/IR/PassManager.h" 18 | #include "llvm/Pass.h" 19 | #include "llvm/Support/raw_ostream.h" 20 | 21 | //------------------------------------------------------------------------------ 22 | // New PM interface 23 | //------------------------------------------------------------------------------ 24 | using ResultOpcodeCounter = llvm::StringMap; 25 | 26 | struct OpcodeCounter : public llvm::AnalysisInfoMixin { 27 | using Result = ResultOpcodeCounter; 28 | Result run(llvm::Function &F, 29 | llvm::FunctionAnalysisManager &); 30 | 31 | OpcodeCounter::Result generateOpcodeMap(llvm::Function &F); 32 | // Part of the official API: 33 | // https://llvm.org/docs/WritingAnLLVMNewPMPass.html#required-passes 34 | static bool isRequired() { return true; } 35 | 36 | private: 37 | // A special type used by analysis passes to provide an address that 38 | // identifies that particular analysis pass type. 39 | static llvm::AnalysisKey Key; 40 | friend struct llvm::AnalysisInfoMixin; 41 | }; 42 | 43 | //------------------------------------------------------------------------------ 44 | // New PM interface for the printer pass 45 | //------------------------------------------------------------------------------ 46 | class OpcodeCounterPrinter : public llvm::PassInfoMixin { 47 | public: 48 | explicit OpcodeCounterPrinter(llvm::raw_ostream &OutS) : OS(OutS) {} 49 | llvm::PreservedAnalyses run(llvm::Function &Func, 50 | llvm::FunctionAnalysisManager &FAM); 51 | // Part of the official API: 52 | // https://llvm.org/docs/WritingAnLLVMNewPMPass.html#required-passes 53 | static bool isRequired() { return true; } 54 | 55 | private: 56 | llvm::raw_ostream &OS; 57 | }; 58 | #endif 59 | -------------------------------------------------------------------------------- /include/RIV.h: -------------------------------------------------------------------------------- 1 | //======================================================================== 2 | // FILE: 3 | // RIV.h 4 | // 5 | // DESCRIPTION: 6 | // Declares the RIV passes: 7 | // * new pass manager interface 8 | // * legacy pass manager interface 9 | // * printer pass for the new pass manager 10 | // 11 | // License: MIT 12 | //======================================================================== 13 | #ifndef LLVM_TUTOR_RIV_H 14 | #define LLVM_TUTOR_RIV_H 15 | 16 | #include "llvm/ADT/MapVector.h" 17 | #include "llvm/ADT/SmallPtrSet.h" 18 | #include "llvm/IR/BasicBlock.h" 19 | #include "llvm/IR/Dominators.h" 20 | #include "llvm/Pass.h" 21 | 22 | //------------------------------------------------------------------------------ 23 | // New PM interface 24 | //------------------------------------------------------------------------------ 25 | struct RIV : public llvm::AnalysisInfoMixin { 26 | // A map that for every basic block holds a set of pointers to reachable 27 | // integer values for that block. 28 | using Result = llvm::MapVector>; 30 | Result run(llvm::Function &F, llvm::FunctionAnalysisManager &); 31 | Result buildRIV(llvm::Function &F, 32 | llvm::DomTreeNodeBase *CFGRoot); 33 | 34 | private: 35 | // A special type used by analysis passes to provide an address that 36 | // identifies that particular analysis pass type. 37 | static llvm::AnalysisKey Key; 38 | friend struct llvm::AnalysisInfoMixin; 39 | }; 40 | 41 | //------------------------------------------------------------------------------ 42 | // New PM interface for the printer pass 43 | //------------------------------------------------------------------------------ 44 | class RIVPrinter : public llvm::PassInfoMixin { 45 | public: 46 | explicit RIVPrinter(llvm::raw_ostream &OutS) : OS(OutS) {} 47 | llvm::PreservedAnalyses run(llvm::Function &F, 48 | llvm::FunctionAnalysisManager &FAM); 49 | 50 | private: 51 | llvm::raw_ostream &OS; 52 | }; 53 | 54 | #endif // LLVM_TUTOR_RIV_H 55 | -------------------------------------------------------------------------------- /include/StaticCallCounter.h: -------------------------------------------------------------------------------- 1 | //======================================================================== 2 | // FILE: 3 | // StaticCallCounter.h 4 | // 5 | // DESCRIPTION: 6 | // Declares the StaticCallCounter Passes 7 | // * new pass manager interface 8 | // * legacy pass manager interface 9 | // * printer pass for the new pass manager 10 | // 11 | // License: MIT 12 | //======================================================================== 13 | #ifndef LLVM_TUTOR_STATICCALLCOUNTER_H 14 | #define LLVM_TUTOR_STATICCALLCOUNTER_H 15 | 16 | #include "llvm/ADT/MapVector.h" 17 | #include "llvm/IR/AbstractCallSite.h" 18 | #include "llvm/IR/Module.h" 19 | #include "llvm/IR/PassManager.h" 20 | #include "llvm/Pass.h" 21 | #include "llvm/Support/raw_ostream.h" 22 | 23 | //------------------------------------------------------------------------------ 24 | // New PM interface 25 | //------------------------------------------------------------------------------ 26 | using ResultStaticCC = llvm::MapVector; 27 | 28 | struct StaticCallCounter : public llvm::AnalysisInfoMixin { 29 | using Result = ResultStaticCC; 30 | Result run(llvm::Module &M, llvm::ModuleAnalysisManager &); 31 | Result runOnModule(llvm::Module &M); 32 | // Part of the official API: 33 | // https://llvm.org/docs/WritingAnLLVMNewPMPass.html#required-passes 34 | static bool isRequired() { return true; } 35 | 36 | private: 37 | // A special type used by analysis passes to provide an address that 38 | // identifies that particular analysis pass type. 39 | static llvm::AnalysisKey Key; 40 | friend struct llvm::AnalysisInfoMixin; 41 | }; 42 | 43 | //------------------------------------------------------------------------------ 44 | // New PM interface for the printer pass 45 | //------------------------------------------------------------------------------ 46 | class StaticCallCounterPrinter 47 | : public llvm::PassInfoMixin { 48 | public: 49 | explicit StaticCallCounterPrinter(llvm::raw_ostream &OutS) : OS(OutS) {} 50 | llvm::PreservedAnalyses run(llvm::Module &M, 51 | llvm::ModuleAnalysisManager &MAM); 52 | // Part of the official API: 53 | // https://llvm.org/docs/WritingAnLLVMNewPMPass.html#required-passes 54 | static bool isRequired() { return true; } 55 | 56 | private: 57 | llvm::raw_ostream &OS; 58 | }; 59 | 60 | #endif // LLVM_TUTOR_STATICCALLCOUNTER_H 61 | -------------------------------------------------------------------------------- /inputs/input_for_cc.c: -------------------------------------------------------------------------------- 1 | //============================================================================= 2 | // FILE: 3 | // input_for_cc.c 4 | // 5 | // DESCRIPTION: 6 | // Sample input file for CallCounter analysis. 7 | // 8 | // License: MIT 9 | //============================================================================= 10 | void foo() { } 11 | void bar() {foo(); } 12 | void fez() {bar(); } 13 | 14 | int main() { 15 | foo(); 16 | bar(); 17 | fez(); 18 | 19 | int ii = 0; 20 | for (ii = 0; ii < 10; ii++) 21 | foo(); 22 | 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /inputs/input_for_duplicate_bb.c: -------------------------------------------------------------------------------- 1 | //============================================================================= 2 | // FILE: 3 | // input_for_duplicate_bb.c 4 | // 5 | // DESCRIPTION: 6 | // Sample input file for the DuplicateBB pass. 7 | // 8 | // License: MIT 9 | //============================================================================= 10 | int foo(int arg_1) { return 1; } 11 | -------------------------------------------------------------------------------- /inputs/input_for_fcmp_eq.c: -------------------------------------------------------------------------------- 1 | //============================================================================= 2 | // FILE: 3 | // input_for_fcmp_eq.c 4 | // 5 | // DESCRIPTION: 6 | // Sample input file for the FindFCmpEq and ConvertFCmpEq passes. 7 | // 8 | // License: MIT 9 | //============================================================================= 10 | 11 | // sqrt_impl uses the Newton-Raphson method for approximating square roots. 12 | double sqrt_impl(double x, double hi, double lo) { 13 | // First direct floating-point equality comparison 14 | if (hi == lo) { 15 | return lo; 16 | } 17 | 18 | double midpoint = (lo + hi + 1.0) / 2.0; 19 | if (x / midpoint < midpoint) { 20 | return sqrt_impl(x, lo, midpoint - 1.0); 21 | } 22 | 23 | return sqrt_impl(x, midpoint, hi); 24 | } 25 | 26 | double sqrt(double x) { return sqrt_impl(x, 0, x / 2.0 + 1.0); } 27 | 28 | int main() { 29 | double a = 0.2; 30 | double b = 1.0 / sqrt(5.0) / sqrt(5.0); 31 | // Second direct floating-point equality comparison 32 | if (b == 1.0) 33 | return a == b ? 1 : 0; 34 | else 35 | return a == b ? 0 : 1; 36 | } 37 | -------------------------------------------------------------------------------- /inputs/input_for_hello.c: -------------------------------------------------------------------------------- 1 | //============================================================================= 2 | // FILE: 3 | // input_for_hello.c 4 | // 5 | // DESCRIPTION: 6 | // Sample input file for HelloWorld and InjectFuncCall 7 | // 8 | // License: MIT 9 | //============================================================================= 10 | int foo(int a) { 11 | return a * 2; 12 | } 13 | 14 | int bar(int a, int b) { 15 | return (a + foo(b) * 2); 16 | } 17 | 18 | int fez(int a, int b, int c) { 19 | return (a + bar(a, b) * 2 + c * 3); 20 | } 21 | 22 | int main(int argc, char *argv[]) { 23 | int a = 123; 24 | int ret = 0; 25 | 26 | ret += foo(a); 27 | ret += bar(a, ret); 28 | ret += fez(a, ret, 123); 29 | 30 | return ret; 31 | } 32 | -------------------------------------------------------------------------------- /inputs/input_for_mba.c: -------------------------------------------------------------------------------- 1 | //============================================================================= 2 | // FILE: 3 | // input_for_mba_add.c 4 | // 5 | // DESCRIPTION: 6 | // Sample input file for the MBAAdd pass. 7 | // 8 | // License: MIT 9 | //============================================================================= 10 | #include 11 | #include 12 | 13 | int8_t foo(int8_t a, int8_t b, int8_t c, int8_t d) { 14 | int8_t e = c + d; 15 | int8_t f = a + b; 16 | 17 | return e + f; 18 | } 19 | 20 | int main(int argc, char *argv[]) { 21 | int a = atoi(argv[1]), b = atoi(argv[2]), c = atoi(argv[3]), 22 | d = atoi(argv[4]); 23 | 24 | int8_t ret = foo(a, b, c, d); 25 | return ret; 26 | } 27 | -------------------------------------------------------------------------------- /inputs/input_for_mba_sub.c: -------------------------------------------------------------------------------- 1 | //============================================================================= 2 | // FILE: 3 | // input_for_mba_sub.c 4 | // 5 | // DESCRIPTION: 6 | // Sample input file for the MBASub pass. 7 | // 8 | // License: MIT 9 | //============================================================================= 10 | #include 11 | #include 12 | 13 | int main(int argc, char *argv[]) { 14 | int a = atoi(argv[1]), b = atoi(argv[2]), c = atoi(argv[3]), 15 | d = atoi(argv[4]); 16 | 17 | int e = a - b; 18 | int f = c - d; 19 | 20 | return e - f; 21 | } 22 | -------------------------------------------------------------------------------- /inputs/input_for_riv.c: -------------------------------------------------------------------------------- 1 | //============================================================================= 2 | // FILE: 3 | // input_for_riv.c 4 | // 5 | // DESCRIPTION: 6 | // Sample input file for RIV analysis. 7 | // 8 | // License: MIT 9 | //============================================================================= 10 | int foo(int a, int b, int c) { 11 | int result = 123 + a; 12 | 13 | if (a > 0) { 14 | int d = a * b; 15 | int e = b / c; 16 | if (d == e) { 17 | int f = d * e; 18 | result = result - 2*f; 19 | } else { 20 | int g = 987; 21 | result = g * c * e; 22 | } 23 | } else { 24 | result = 321; 25 | } 26 | 27 | return result; 28 | } 29 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # THE LIST OF PLUGINS AND THE CORRESPONDING SOURCE FILES 2 | # ====================================================== 3 | set(LLVM_TUTOR_PLUGINS 4 | StaticCallCounter 5 | DynamicCallCounter 6 | FindFCmpEq 7 | ConvertFCmpEq 8 | InjectFuncCall 9 | MBAAdd 10 | MBASub 11 | RIV 12 | DuplicateBB 13 | OpcodeCounter 14 | MergeBB 15 | ) 16 | 17 | set(StaticCallCounter_SOURCES 18 | StaticCallCounter.cpp) 19 | set(DynamicCallCounter_SOURCES 20 | DynamicCallCounter.cpp) 21 | set(FindFCmpEq_SOURCES 22 | FindFCmpEq.cpp) 23 | set(ConvertFCmpEq_SOURCES 24 | ConvertFCmpEq.cpp) 25 | set(InjectFuncCall_SOURCES 26 | InjectFuncCall.cpp) 27 | set(MBAAdd_SOURCES 28 | MBAAdd.cpp) 29 | set(MBASub_SOURCES 30 | MBASub.cpp) 31 | set(RIV_SOURCES 32 | RIV.cpp) 33 | set(DuplicateBB_SOURCES 34 | DuplicateBB.cpp) 35 | set(OpcodeCounter_SOURCES 36 | OpcodeCounter.cpp) 37 | set(MergeBB_SOURCES 38 | MergeBB.cpp) 39 | 40 | # CONFIGURE THE PLUGIN LIBRARIES 41 | # ============================== 42 | foreach( plugin ${LLVM_TUTOR_PLUGINS} ) 43 | # Create a library corresponding to 'plugin' 44 | add_library( 45 | ${plugin} 46 | SHARED 47 | ${${plugin}_SOURCES} 48 | ) 49 | 50 | # Configure include directories for 'plugin' 51 | target_include_directories( 52 | ${plugin} 53 | PRIVATE 54 | "${CMAKE_CURRENT_SOURCE_DIR}/../include" 55 | ) 56 | 57 | # On Darwin (unlike on Linux), undefined symbols in shared objects are not 58 | # allowed at the end of the link-edit. The plugins defined here: 59 | # - _are_ shared objects 60 | # - reference symbols from LLVM shared libraries, i.e. symbols which are 61 | # undefined until those shared objects are loaded in memory (and hence 62 | # _undefined_ during static linking) 63 | # The build will fail with errors like this: 64 | # "Undefined symbols for architecture x86_64" 65 | # with various LLVM symbols being undefined. Since those symbols are later 66 | # loaded and resolved at runtime, these errors are false positives. 67 | # This behaviour can be modified via the '-undefined' OS X linker flag as 68 | # follows. 69 | target_link_libraries( 70 | ${plugin} 71 | "$<$:-undefined dynamic_lookup>" 72 | ) 73 | endforeach() 74 | -------------------------------------------------------------------------------- /lib/ConvertFCmpEq.cpp: -------------------------------------------------------------------------------- 1 | //============================================================================= 2 | // FILE: 3 | // ConvertFCmpEq.cpp 4 | // 5 | // DESCRIPTION: 6 | // Transformation pass which uses the results of the FindFCmpEq analysis pass 7 | // to convert all equality-based floating point comparison instructions in a 8 | // function to indirect, difference-based comparisons. 9 | // 10 | // This example demonstrates how to couple an analysis pass with a 11 | // transformation pass, the use of statistics (the STATISTIC macro), and LLVM 12 | // debugging operations (the LLVM_DEBUG macro and the llvm::dbgs() output 13 | // stream). It also demonstrates how instructions can be modified without 14 | // having to completely replace them. 15 | // 16 | // Originally developed for [1]. 17 | // 18 | // [1] "Writing an LLVM Optimization" by Jonathan Smith 19 | // 20 | // USAGE: 21 | // opt --load-pass-plugin libConvertFCmpEq.dylib [--stats] `\` 22 | // --passes='convert-fcmp-eq' --disable-output 23 | // 24 | // License: MIT 25 | //============================================================================= 26 | #include "ConvertFCmpEq.h" 27 | #include "llvm/ADT/APFloat.h" 28 | #include "llvm/ADT/APInt.h" 29 | #include "llvm/ADT/Statistic.h" 30 | #include "llvm/IR/Attributes.h" 31 | #include "llvm/IR/BasicBlock.h" 32 | #include "llvm/IR/Constants.h" 33 | #include "llvm/IR/Function.h" 34 | #include "llvm/IR/IRBuilder.h" 35 | #include "llvm/IR/Instructions.h" 36 | #include "llvm/IR/LLVMContext.h" 37 | #include "llvm/IR/Module.h" 38 | #include "llvm/IR/Type.h" 39 | #include "llvm/IR/Value.h" 40 | #include "llvm/Passes/PassBuilder.h" 41 | #include "llvm/Passes/PassPlugin.h" 42 | #include "llvm/Support/Casting.h" 43 | #include "llvm/Support/Debug.h" 44 | #include "llvm/Support/ErrorHandling.h" 45 | #include 46 | 47 | using namespace llvm; 48 | 49 | // Unnamed namespace for private functions 50 | static FCmpInst *convertFCmpEqInstruction(FCmpInst *FCmp) noexcept { 51 | assert(FCmp && "The given fcmp instruction is null"); 52 | 53 | if (!FCmp->isEquality()) { 54 | // We're only interested in equality-based comparisons, so return null if 55 | // this comparison isn't equality-based. 56 | return nullptr; 57 | } 58 | 59 | Value *LHS = FCmp->getOperand(0); 60 | Value *RHS = FCmp->getOperand(1); 61 | // Determine the new floating-point comparison predicate based on the current 62 | // one. 63 | CmpInst::Predicate CmpPred = [FCmp] { 64 | switch (FCmp->getPredicate()) { 65 | case CmpInst::Predicate::FCMP_OEQ: 66 | return CmpInst::Predicate::FCMP_OLT; 67 | case CmpInst::Predicate::FCMP_UEQ: 68 | return CmpInst::Predicate::FCMP_ULT; 69 | case CmpInst::Predicate::FCMP_ONE: 70 | return CmpInst::Predicate::FCMP_OGE; 71 | case CmpInst::Predicate::FCMP_UNE: 72 | return CmpInst::Predicate::FCMP_UGE; 73 | default: 74 | llvm_unreachable("Unsupported fcmp predicate"); 75 | } 76 | }(); 77 | 78 | // Create the objects and values needed to perform the equality comparison 79 | // conversion. 80 | Module *M = FCmp->getModule(); 81 | assert(M && "The given fcmp instruction does not belong to a module"); 82 | LLVMContext &Ctx = M->getContext(); 83 | IntegerType *I64Ty = IntegerType::get(Ctx, 64); 84 | Type *DoubleTy = Type::getDoubleTy(Ctx); 85 | 86 | // Define the sign-mask and double-precision machine epsilon constants. 87 | ConstantInt *SignMask = ConstantInt::get(I64Ty, ~(1L << 63)); 88 | // The machine epsilon value for IEEE 754 double-precision values is 2 ^ -52 89 | // or (b / 2) * b ^ -(p - 1) where b (base) = 2 and p (precision) = 53. 90 | APInt EpsilonBits(64, 0x3CB0000000000000); 91 | Constant *EpsilonValue = 92 | ConstantFP::get(DoubleTy, EpsilonBits.bitsToDouble()); 93 | 94 | // Create an IRBuilder with an insertion point set to the given fcmp 95 | // instruction. 96 | IRBuilder<> Builder(FCmp); 97 | // Create the subtraction, casting, absolute value, and new comparison 98 | // instructions one at a time. 99 | // %0 = fsub double %a, %b 100 | auto *FSubInst = Builder.CreateFSub(LHS, RHS); 101 | // %1 = bitcast double %0 to i64 102 | auto *CastToI64 = Builder.CreateBitCast(FSubInst, I64Ty); 103 | // %2 = and i64 %1, 0x7fffffffffffffff 104 | auto *AbsValue = Builder.CreateAnd(CastToI64, SignMask); 105 | // %3 = bitcast i64 %2 to double 106 | auto *CastToDouble = Builder.CreateBitCast(AbsValue, DoubleTy); 107 | // %4 = fcmp double %3, 0x3cb0000000000000 108 | // Rather than creating a new instruction, we'll just change the predicate and 109 | // operands of the existing fcmp instruction to match what we want. 110 | FCmp->setPredicate(CmpPred); 111 | FCmp->setOperand(0, CastToDouble); 112 | FCmp->setOperand(1, EpsilonValue); 113 | return FCmp; 114 | } 115 | 116 | static constexpr char PassArg[] = "convert-fcmp-eq"; 117 | static constexpr char PluginName[] = "ConvertFCmpEq"; 118 | 119 | #define DEBUG_TYPE ::PassArg 120 | STATISTIC(FCmpEqConversionCount, 121 | "Number of direct floating-point equality comparisons converted"); 122 | 123 | //------------------------------------------------------------------------------ 124 | // ConvertFCmpEq implementation 125 | //------------------------------------------------------------------------------ 126 | PreservedAnalyses ConvertFCmpEq::run(Function &Func, 127 | FunctionAnalysisManager &FAM) { 128 | auto &Comparisons = FAM.getResult(Func); 129 | bool Modified = run(Func, Comparisons); 130 | return Modified ? PreservedAnalyses::none() : PreservedAnalyses::all(); 131 | } 132 | 133 | bool ConvertFCmpEq::run(Function &Func, 134 | const FindFCmpEq::Result &Comparisons) { 135 | bool Modified = false; 136 | // Functions marked explicitly 'optnone' should be ignored since we shouldn't 137 | // be changing anything in them anyway. 138 | if (Func.hasFnAttribute(Attribute::OptimizeNone)) { 139 | LLVM_DEBUG(dbgs() << "Ignoring optnone-marked function \"" << Func.getName() 140 | << "\"\n"); 141 | Modified = false; 142 | } else { 143 | for (FCmpInst *FCmp : Comparisons) { 144 | if (convertFCmpEqInstruction(FCmp)) { 145 | ++FCmpEqConversionCount; 146 | Modified = true; 147 | } 148 | } 149 | } 150 | 151 | return Modified; 152 | } 153 | 154 | //----------------------------------------------------------------------------- 155 | // New PM Registration 156 | //----------------------------------------------------------------------------- 157 | PassPluginLibraryInfo getConvertFCmpEqPluginInfo() { 158 | return {LLVM_PLUGIN_API_VERSION, PluginName, LLVM_VERSION_STRING, 159 | [](PassBuilder &PB) { 160 | PB.registerPipelineParsingCallback( 161 | [&](StringRef Name, FunctionPassManager &FPM, 162 | ArrayRef) { 163 | if (!Name.compare(PassArg)) { 164 | FPM.addPass(ConvertFCmpEq()); 165 | return true; 166 | } 167 | 168 | return false; 169 | }); 170 | }}; 171 | } 172 | 173 | extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo 174 | llvmGetPassPluginInfo() { 175 | return getConvertFCmpEqPluginInfo(); 176 | } 177 | -------------------------------------------------------------------------------- /lib/DynamicCallCounter.cpp: -------------------------------------------------------------------------------- 1 | //======================================================================== 2 | // FILE: 3 | // DynamicCallCounter.cpp 4 | // 5 | // DESCRIPTION: 6 | // Counts dynamic function calls in a module. `Dynamic` in this context means 7 | // runtime function calls (as opposed to static, i.e. compile time). Note 8 | // that runtime calls can only be analysed while the underlying module is 9 | // executing. In order to count them one has to instrument the input 10 | // module. 11 | // 12 | // This pass adds/injects code that will count function calls at 13 | // runtime and prints the results when the module exits. More specifically: 14 | // 1. For every function F _defined_ in M: 15 | // * defines a global variable, `i32 CounterFor_F`, initialised with 0 16 | // * adds instructions at the beginning of F that increment `CounterFor_F` 17 | // every time F executes 18 | // 2. At the end of the module (after `main`), calls `printf_wrapper` that 19 | // prints the global call counters injected by this pass (e.g. 20 | // `CounterFor_F`). The definition of `printf_wrapper` is also inserted by 21 | // DynamicCallCounter. 22 | // 23 | // To illustrate, the following code will be injected at the beginning of 24 | // function F (defined in the input module): 25 | // ```IR 26 | // %1 = load i32, i32* @CounterFor_F 27 | // %2 = add i32 1, %1 28 | // store i32 %2, i32* @CounterFor_F 29 | // ``` 30 | // The following definition of `CounterFor_F` is also added: 31 | // ```IR 32 | // @CounterFor_foo = common global i32 0, align 4 33 | // ``` 34 | // 35 | // This pass will only count calls to functions _defined_ in the input 36 | // module. Functions that are only _declared_ (and defined elsewhere) are not 37 | // counted. 38 | // 39 | // USAGE: 40 | // $ opt -load-pass-plugin /lib/libDynamicCallCounter.so `\` 41 | // -passes=-"dynamic-cc" -o instrumentend.bin 42 | // $ lli instrumented.bin 43 | // 44 | // License: MIT 45 | //======================================================================== 46 | #include "DynamicCallCounter.h" 47 | 48 | #include "llvm/IR/IRBuilder.h" 49 | #include "llvm/Passes/PassBuilder.h" 50 | #include "llvm/Passes/PassPlugin.h" 51 | #include "llvm/Transforms/Utils/ModuleUtils.h" 52 | 53 | using namespace llvm; 54 | 55 | #define DEBUG_TYPE "dynamic-cc" 56 | 57 | Constant *CreateGlobalCounter(Module &M, StringRef GlobalVarName) { 58 | auto &CTX = M.getContext(); 59 | 60 | // This will insert a declaration into M 61 | Constant *NewGlobalVar = 62 | M.getOrInsertGlobal(GlobalVarName, IntegerType::getInt32Ty(CTX)); 63 | 64 | // This will change the declaration into definition (and initialise to 0) 65 | GlobalVariable *NewGV = M.getNamedGlobal(GlobalVarName); 66 | NewGV->setLinkage(GlobalValue::CommonLinkage); 67 | NewGV->setAlignment(MaybeAlign(4)); 68 | NewGV->setInitializer(llvm::ConstantInt::get(CTX, APInt(32, 0))); 69 | 70 | return NewGlobalVar; 71 | } 72 | 73 | //----------------------------------------------------------------------------- 74 | // DynamicCallCounter implementation 75 | //----------------------------------------------------------------------------- 76 | bool DynamicCallCounter::runOnModule(Module &M) { 77 | bool Instrumented = false; 78 | 79 | // Function name <--> IR variable that holds the call counter 80 | llvm::StringMap CallCounterMap; 81 | // Function name <--> IR variable that holds the function name 82 | llvm::StringMap FuncNameMap; 83 | 84 | auto &CTX = M.getContext(); 85 | 86 | // STEP 1: For each function in the module, inject a call-counting code 87 | // -------------------------------------------------------------------- 88 | for (auto &F : M) { 89 | if (F.isDeclaration()) 90 | continue; 91 | 92 | // Get an IR builder. Sets the insertion point to the top of the function 93 | IRBuilder<> Builder(&*F.getEntryBlock().getFirstInsertionPt()); 94 | 95 | // Create a global variable to count the calls to this function 96 | std::string CounterName = "CounterFor_" + std::string(F.getName()); 97 | Constant *Var = CreateGlobalCounter(M, CounterName); 98 | CallCounterMap[F.getName()] = Var; 99 | 100 | // Create a global variable to hold the name of this function 101 | auto FuncName = Builder.CreateGlobalStringPtr(F.getName()); 102 | FuncNameMap[F.getName()] = FuncName; 103 | 104 | // Inject instruction to increment the call count each time this function 105 | // executes 106 | LoadInst *Load2 = Builder.CreateLoad(IntegerType::getInt32Ty(CTX), Var); 107 | Value *Inc2 = Builder.CreateAdd(Builder.getInt32(1), Load2); 108 | Builder.CreateStore(Inc2, Var); 109 | 110 | // The following is visible only if you pass -debug on the command line 111 | // *and* you have an assert build. 112 | LLVM_DEBUG(dbgs() << " Instrumented: " << F.getName() << "\n"); 113 | 114 | Instrumented = true; 115 | } 116 | 117 | // Stop here if there are no function definitions in this module 118 | if (false == Instrumented) 119 | return Instrumented; 120 | 121 | // STEP 2: Inject the declaration of printf 122 | // ---------------------------------------- 123 | // Create (or _get_ in cases where it's already available) the following 124 | // declaration in the IR module: 125 | // declare i32 @printf(i8*, ...) 126 | // It corresponds to the following C declaration: 127 | // int printf(char *, ...) 128 | PointerType *PrintfArgTy = PointerType::getUnqual(Type::getInt8Ty(CTX)); 129 | FunctionType *PrintfTy = 130 | FunctionType::get(IntegerType::getInt32Ty(CTX), PrintfArgTy, 131 | /*IsVarArgs=*/true); 132 | 133 | FunctionCallee Printf = M.getOrInsertFunction("printf", PrintfTy); 134 | 135 | // Set attributes as per inferLibFuncAttributes in BuildLibCalls.cpp 136 | Function *PrintfF = dyn_cast(Printf.getCallee()); 137 | PrintfF->setDoesNotThrow(); 138 | PrintfF->addParamAttr(0, Attribute::NoCapture); 139 | PrintfF->addParamAttr(0, Attribute::ReadOnly); 140 | 141 | // STEP 3: Inject a global variable that will hold the printf format string 142 | // ------------------------------------------------------------------------ 143 | llvm::Constant *ResultFormatStr = 144 | llvm::ConstantDataArray::getString(CTX, "%-20s %-10lu\n"); 145 | 146 | Constant *ResultFormatStrVar = 147 | M.getOrInsertGlobal("ResultFormatStrIR", ResultFormatStr->getType()); 148 | dyn_cast(ResultFormatStrVar)->setInitializer(ResultFormatStr); 149 | 150 | std::string out = ""; 151 | out += "=================================================\n"; 152 | out += "LLVM-TUTOR: dynamic analysis results\n"; 153 | out += "=================================================\n"; 154 | out += "NAME #N DIRECT CALLS\n"; 155 | out += "-------------------------------------------------\n"; 156 | 157 | llvm::Constant *ResultHeaderStr = 158 | llvm::ConstantDataArray::getString(CTX, out.c_str()); 159 | 160 | Constant *ResultHeaderStrVar = 161 | M.getOrInsertGlobal("ResultHeaderStrIR", ResultHeaderStr->getType()); 162 | dyn_cast(ResultHeaderStrVar)->setInitializer(ResultHeaderStr); 163 | 164 | // STEP 4: Define a printf wrapper that will print the results 165 | // ----------------------------------------------------------- 166 | // Define `printf_wrapper` that will print the results stored in FuncNameMap 167 | // and CallCounterMap. It is equivalent to the following C++ function: 168 | // ``` 169 | // void printf_wrapper() { 170 | // for (auto &item : Functions) 171 | // printf("llvm-tutor): Function %s was called %d times. \n", 172 | // item.name, item.count); 173 | // } 174 | // ``` 175 | // (item.name comes from FuncNameMap, item.count comes from 176 | // CallCounterMap) 177 | FunctionType *PrintfWrapperTy = 178 | FunctionType::get(llvm::Type::getVoidTy(CTX), {}, 179 | /*IsVarArgs=*/false); 180 | Function *PrintfWrapperF = dyn_cast( 181 | M.getOrInsertFunction("printf_wrapper", PrintfWrapperTy).getCallee()); 182 | 183 | // Create the entry basic block for printf_wrapper ... 184 | llvm::BasicBlock *RetBlock = 185 | llvm::BasicBlock::Create(CTX, "enter", PrintfWrapperF); 186 | IRBuilder<> Builder(RetBlock); 187 | 188 | // ... and start inserting calls to printf 189 | // (printf requires i8*, so cast the input strings accordingly) 190 | llvm::Value *ResultHeaderStrPtr = 191 | Builder.CreatePointerCast(ResultHeaderStrVar, PrintfArgTy); 192 | llvm::Value *ResultFormatStrPtr = 193 | Builder.CreatePointerCast(ResultFormatStrVar, PrintfArgTy); 194 | 195 | Builder.CreateCall(Printf, {ResultHeaderStrPtr}); 196 | 197 | LoadInst *LoadCounter; 198 | for (auto &item : CallCounterMap) { 199 | LoadCounter = Builder.CreateLoad(IntegerType::getInt32Ty(CTX), item.second); 200 | // LoadCounter = Builder.CreateLoad(item.second); 201 | Builder.CreateCall( 202 | Printf, {ResultFormatStrPtr, FuncNameMap[item.first()], LoadCounter}); 203 | } 204 | 205 | // Finally, insert return instruction 206 | Builder.CreateRetVoid(); 207 | 208 | // STEP 5: Call `printf_wrapper` at the very end of this module 209 | // ------------------------------------------------------------ 210 | appendToGlobalDtors(M, PrintfWrapperF, /*Priority=*/0); 211 | 212 | return true; 213 | } 214 | 215 | PreservedAnalyses DynamicCallCounter::run(llvm::Module &M, 216 | llvm::ModuleAnalysisManager &) { 217 | bool Changed = runOnModule(M); 218 | 219 | return (Changed ? llvm::PreservedAnalyses::none() 220 | : llvm::PreservedAnalyses::all()); 221 | } 222 | 223 | //----------------------------------------------------------------------------- 224 | // New PM Registration 225 | //----------------------------------------------------------------------------- 226 | llvm::PassPluginLibraryInfo getDynamicCallCounterPluginInfo() { 227 | return {LLVM_PLUGIN_API_VERSION, "dynamic-cc", LLVM_VERSION_STRING, 228 | [](PassBuilder &PB) { 229 | PB.registerPipelineParsingCallback( 230 | [](StringRef Name, ModulePassManager &MPM, 231 | ArrayRef) { 232 | if (Name == "dynamic-cc") { 233 | MPM.addPass(DynamicCallCounter()); 234 | return true; 235 | } 236 | return false; 237 | }); 238 | }}; 239 | } 240 | 241 | extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo 242 | llvmGetPassPluginInfo() { 243 | return getDynamicCallCounterPluginInfo(); 244 | } 245 | -------------------------------------------------------------------------------- /lib/FindFCmpEq.cpp: -------------------------------------------------------------------------------- 1 | //============================================================================= 2 | // FILE: 3 | // FindFCmpEq.cpp 4 | // 5 | // DESCRIPTION: 6 | // Visits all instructions in a function and returns all equality-based 7 | // floating point comparisons. The results can be printed through the use of 8 | // a printing pass. 9 | // 10 | // This example demonstrates how to separate printing logic into a separate 11 | // printing pass, how to register it along with an analysis pass at the same 12 | // time, and how to parse pass pipeline elements to conditionally register a 13 | // pass. This is achieved using a combination of llvm::formatv() (not 14 | // strictly required), 15 | // llvm::PassBuilder::registerAnalysisRegistrationCallback(), and 16 | // llvm::PassBuilder::registerPipelineParsingCallback(). 17 | // 18 | // Originally developed for [1]. 19 | // 20 | // [1] "Writing an LLVM Optimization" by Jonathan Smith 21 | // 22 | // USAGE: 23 | // opt --load-pass-plugin libFindFCmpEq.dylib `\` 24 | // --passes='print' --disable-output 25 | // 26 | // License: MIT 27 | //============================================================================= 28 | #include "FindFCmpEq.h" 29 | 30 | #include "llvm/ADT/ArrayRef.h" 31 | #include "llvm/ADT/StringRef.h" 32 | #include "llvm/IR/Function.h" 33 | #include "llvm/IR/InstIterator.h" 34 | #include "llvm/IR/Instruction.h" 35 | #include "llvm/IR/Instructions.h" 36 | #include "llvm/IR/ModuleSlotTracker.h" 37 | #include "llvm/Passes/PassBuilder.h" 38 | #include "llvm/Passes/PassPlugin.h" 39 | #include "llvm/Support/Casting.h" 40 | #include "llvm/Support/Compiler.h" 41 | #include "llvm/Support/FormatVariadic.h" 42 | #include "llvm/Support/raw_ostream.h" 43 | 44 | #include 45 | 46 | using namespace llvm; 47 | 48 | static void 49 | printFCmpEqInstructions(raw_ostream &OS, Function &Func, 50 | const FindFCmpEq::Result &FCmpEqInsts) noexcept { 51 | if (FCmpEqInsts.empty()) 52 | return; 53 | 54 | OS << "Floating-point equality comparisons in \"" << Func.getName() 55 | << "\":\n"; 56 | 57 | // Using a ModuleSlotTracker for printing makes it so full function analysis 58 | // for slot numbering only occurs once instead of every time an instruction 59 | // is printed. 60 | ModuleSlotTracker Tracker(Func.getParent()); 61 | 62 | for (FCmpInst *FCmpEq : FCmpEqInsts) { 63 | FCmpEq->print(OS, Tracker); 64 | OS << '\n'; 65 | } 66 | } 67 | 68 | static constexpr char PassArg[] = "find-fcmp-eq"; 69 | static constexpr char PassName[] = 70 | "Floating-point equality comparisons locator"; 71 | static constexpr char PluginName[] = "FindFCmpEq"; 72 | 73 | //------------------------------------------------------------------------------ 74 | // FindFCmpEq implementation 75 | //------------------------------------------------------------------------------ 76 | FindFCmpEq::Result FindFCmpEq::run(Function &Func, 77 | FunctionAnalysisManager &FAM) { 78 | return run(Func); 79 | } 80 | 81 | FindFCmpEq::Result FindFCmpEq::run(Function &Func) { 82 | Result Comparisons; 83 | for (Instruction &Inst : instructions(Func)) { 84 | // We're only looking for 'fcmp' instructions here. 85 | if (auto *FCmp = dyn_cast(&Inst)) { 86 | // We've found an 'fcmp' instruction; we need to make sure it's an 87 | // equality comparison. 88 | if (FCmp->isEquality()) { 89 | Comparisons.push_back(FCmp); 90 | } 91 | } 92 | } 93 | 94 | return Comparisons; 95 | } 96 | 97 | PreservedAnalyses FindFCmpEqPrinter::run(Function &Func, 98 | FunctionAnalysisManager &FAM) { 99 | auto &Comparisons = FAM.getResult(Func); 100 | printFCmpEqInstructions(OS, Func, Comparisons); 101 | return PreservedAnalyses::all(); 102 | } 103 | 104 | //----------------------------------------------------------------------------- 105 | // New PM Registration 106 | //----------------------------------------------------------------------------- 107 | llvm::AnalysisKey FindFCmpEq::Key; 108 | 109 | PassPluginLibraryInfo getFindFCmpEqPluginInfo() { 110 | return {LLVM_PLUGIN_API_VERSION, PluginName, LLVM_VERSION_STRING, 111 | [](PassBuilder &PB) { 112 | // #1 REGISTRATION FOR "FAM.getResult(Function)" 113 | PB.registerAnalysisRegistrationCallback( 114 | [](FunctionAnalysisManager &FAM) { 115 | FAM.registerPass([&] { return FindFCmpEq(); }); 116 | }); 117 | // #2 REGISTRATION FOR "opt -passes=print" 118 | // Printing passes format their pipeline element argument to the 119 | // pattern `print`. This is the pattern we're checking 120 | // for here. 121 | PB.registerPipelineParsingCallback( 122 | [&](StringRef Name, FunctionPassManager &FPM, 123 | ArrayRef) { 124 | std::string PrinterPassElement = 125 | formatv("print<{0}>", PassArg); 126 | if (!Name.compare(PrinterPassElement)) { 127 | FPM.addPass(FindFCmpEqPrinter(llvm::outs())); 128 | return true; 129 | } 130 | 131 | return false; 132 | }); 133 | }}; 134 | } 135 | 136 | extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo 137 | llvmGetPassPluginInfo() { 138 | return getFindFCmpEqPluginInfo(); 139 | } 140 | -------------------------------------------------------------------------------- /lib/InjectFuncCall.cpp: -------------------------------------------------------------------------------- 1 | //======================================================================== 2 | // FILE: 3 | // InjectFuncCall.cpp 4 | // 5 | // DESCRIPTION: 6 | // For each function defined in the input IR module, InjectFuncCall inserts 7 | // a call to printf (from the C standard I/O library). The injected IR code 8 | // corresponds to the following function call in ANSI C: 9 | // ```C 10 | // printf("(llvm-tutor) Hello from: %s\n(llvm-tutor) number of arguments: %d\n", 11 | // FuncName, FuncNumArgs); 12 | // ``` 13 | // This code is inserted at the beginning of each function, i.e. before any 14 | // other instruction is executed. 15 | // 16 | // To illustrate, for `void foo(int a, int b, int c)`, the code added by InjectFuncCall 17 | // will generated the following output at runtime: 18 | // ``` 19 | // (llvm-tutor) Hello World from: foo 20 | // (llvm-tutor) number of arguments: 3 21 | // ``` 22 | // 23 | // USAGE: 24 | // $ opt -load-pass-plugin /lib/libInjectFunctCall.so `\` 25 | // -passes=-"inject-func-call" 26 | // 27 | // License: MIT 28 | //======================================================================== 29 | #include "InjectFuncCall.h" 30 | 31 | #include "llvm/IR/IRBuilder.h" 32 | #include "llvm/Passes/PassPlugin.h" 33 | #include "llvm/Passes/PassBuilder.h" 34 | 35 | using namespace llvm; 36 | 37 | #define DEBUG_TYPE "inject-func-call" 38 | 39 | //----------------------------------------------------------------------------- 40 | // InjectFuncCall implementation 41 | //----------------------------------------------------------------------------- 42 | bool InjectFuncCall::runOnModule(Module &M) { 43 | bool InsertedAtLeastOnePrintf = false; 44 | 45 | auto &CTX = M.getContext(); 46 | PointerType *PrintfArgTy = PointerType::getUnqual(Type::getInt8Ty(CTX)); 47 | 48 | // STEP 1: Inject the declaration of printf 49 | // ---------------------------------------- 50 | // Create (or _get_ in cases where it's already available) the following 51 | // declaration in the IR module: 52 | // declare i32 @printf(i8*, ...) 53 | // It corresponds to the following C declaration: 54 | // int printf(char *, ...) 55 | FunctionType *PrintfTy = FunctionType::get( 56 | IntegerType::getInt32Ty(CTX), 57 | PrintfArgTy, 58 | /*IsVarArgs=*/true); 59 | 60 | FunctionCallee Printf = M.getOrInsertFunction("printf", PrintfTy); 61 | 62 | // Set attributes as per inferLibFuncAttributes in BuildLibCalls.cpp 63 | Function *PrintfF = dyn_cast(Printf.getCallee()); 64 | PrintfF->setDoesNotThrow(); 65 | PrintfF->addParamAttr(0, Attribute::NoCapture); 66 | PrintfF->addParamAttr(0, Attribute::ReadOnly); 67 | 68 | 69 | // STEP 2: Inject a global variable that will hold the printf format string 70 | // ------------------------------------------------------------------------ 71 | llvm::Constant *PrintfFormatStr = llvm::ConstantDataArray::getString( 72 | CTX, "(llvm-tutor) Hello from: %s\n(llvm-tutor) number of arguments: %d\n"); 73 | 74 | Constant *PrintfFormatStrVar = 75 | M.getOrInsertGlobal("PrintfFormatStr", PrintfFormatStr->getType()); 76 | dyn_cast(PrintfFormatStrVar)->setInitializer(PrintfFormatStr); 77 | 78 | // STEP 3: For each function in the module, inject a call to printf 79 | // ---------------------------------------------------------------- 80 | for (auto &F : M) { 81 | if (F.isDeclaration()) 82 | continue; 83 | 84 | // Get an IR builder. Sets the insertion point to the top of the function 85 | IRBuilder<> Builder(&*F.getEntryBlock().getFirstInsertionPt()); 86 | 87 | // Inject a global variable that contains the function name 88 | auto FuncName = Builder.CreateGlobalStringPtr(F.getName()); 89 | 90 | // Printf requires i8*, but PrintfFormatStrVar is an array: [n x i8]. Add 91 | // a cast: [n x i8] -> i8* 92 | llvm::Value *FormatStrPtr = 93 | Builder.CreatePointerCast(PrintfFormatStrVar, PrintfArgTy, "formatStr"); 94 | 95 | // The following is visible only if you pass -debug on the command line 96 | // *and* you have an assert build. 97 | LLVM_DEBUG(dbgs() << " Injecting call to printf inside " << F.getName() 98 | << "\n"); 99 | 100 | // Finally, inject a call to printf 101 | Builder.CreateCall( 102 | Printf, {FormatStrPtr, FuncName, Builder.getInt32(F.arg_size())}); 103 | 104 | InsertedAtLeastOnePrintf = true; 105 | } 106 | 107 | return InsertedAtLeastOnePrintf; 108 | } 109 | 110 | PreservedAnalyses InjectFuncCall::run(llvm::Module &M, 111 | llvm::ModuleAnalysisManager &) { 112 | bool Changed = runOnModule(M); 113 | 114 | return (Changed ? llvm::PreservedAnalyses::none() 115 | : llvm::PreservedAnalyses::all()); 116 | } 117 | 118 | 119 | //----------------------------------------------------------------------------- 120 | // New PM Registration 121 | //----------------------------------------------------------------------------- 122 | llvm::PassPluginLibraryInfo getInjectFuncCallPluginInfo() { 123 | return {LLVM_PLUGIN_API_VERSION, "inject-func-call", LLVM_VERSION_STRING, 124 | [](PassBuilder &PB) { 125 | PB.registerPipelineParsingCallback( 126 | [](StringRef Name, ModulePassManager &MPM, 127 | ArrayRef) { 128 | if (Name == "inject-func-call") { 129 | MPM.addPass(InjectFuncCall()); 130 | return true; 131 | } 132 | return false; 133 | }); 134 | }}; 135 | } 136 | 137 | extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo 138 | llvmGetPassPluginInfo() { 139 | return getInjectFuncCallPluginInfo(); 140 | } 141 | -------------------------------------------------------------------------------- /lib/MBAAdd.cpp: -------------------------------------------------------------------------------- 1 | //============================================================================== 2 | // FILE: 3 | // MBAAdd.cpp 4 | // 5 | // DESCRIPTION: 6 | // This pass performs a substitution for 8-bit integer add 7 | // instruction based on this Mixed Boolean-Airthmetic expression: 8 | // a + b == (((a ^ b) + 2 * (a & b)) * 39 + 23) * 151 + 111 9 | // See formula (3) in [1]. 10 | // 11 | // USAGE: 12 | // $ opt -load-pass-plugin /lib/libMBAAdd.so `\` 13 | // -passes=-"mba-add" 14 | // The command line option is not available for the new PM 15 | // 16 | // 17 | // [1] "Defeating MBA-based Obfuscation" Ninon Eyrolles, Louis Goubin, Marion 18 | // Videau 19 | // 20 | // License: MIT 21 | //============================================================================== 22 | #include "MBAAdd.h" 23 | 24 | #include "llvm/ADT/Statistic.h" 25 | #include "llvm/IR/IRBuilder.h" 26 | #include "llvm/Passes/PassBuilder.h" 27 | #include "llvm/Passes/PassPlugin.h" 28 | #include "llvm/Transforms/Utils/BasicBlockUtils.h" 29 | 30 | #include 31 | 32 | using namespace llvm; 33 | 34 | #define DEBUG_TYPE "mba-add" 35 | 36 | STATISTIC(SubstCount, "The # of substituted instructions"); 37 | 38 | //----------------------------------------------------------------------------- 39 | // MBAAdd Implementation 40 | //----------------------------------------------------------------------------- 41 | bool MBAAdd::runOnBasicBlock(BasicBlock &BB) { 42 | bool Changed = false; 43 | 44 | // Loop over all instructions in the block. Replacing instructions requires 45 | // iterators, hence a for-range loop wouldn't be suitable 46 | for (auto Inst = BB.begin(), IE = BB.end(); Inst != IE; ++Inst) { 47 | // Skip non-binary (e.g. unary or compare) instructions 48 | auto *BinOp = dyn_cast(Inst); 49 | if (!BinOp) 50 | continue; 51 | 52 | // Skip instructions other than add 53 | if (BinOp->getOpcode() != Instruction::Add) 54 | continue; 55 | 56 | // Skip if the result is not 8-bit wide (this implies that the operands are 57 | // also 8-bit wide) 58 | if (!BinOp->getType()->isIntegerTy() || 59 | !(BinOp->getType()->getIntegerBitWidth() == 8)) 60 | continue; 61 | 62 | // A uniform API for creating instructions and inserting 63 | // them into basic blocks 64 | IRBuilder<> Builder(BinOp); 65 | 66 | // Constants used in building the instruction for substitution 67 | auto Val39 = ConstantInt::get(BinOp->getType(), 39); 68 | auto Val151 = ConstantInt::get(BinOp->getType(), 151); 69 | auto Val23 = ConstantInt::get(BinOp->getType(), 23); 70 | auto Val2 = ConstantInt::get(BinOp->getType(), 2); 71 | auto Val111 = ConstantInt::get(BinOp->getType(), 111); 72 | 73 | // Build an instruction representing `(((a ^ b) + 2 * (a & b)) * 39 + 23) * 74 | // 151 + 111` 75 | Instruction *NewInst = 76 | // E = e5 + 111 77 | BinaryOperator::CreateAdd( 78 | Val111, 79 | // e5 = e4 * 151 80 | Builder.CreateMul( 81 | Val151, 82 | // e4 = e2 + 23 83 | Builder.CreateAdd( 84 | Val23, 85 | // e3 = e2 * 39 86 | Builder.CreateMul( 87 | Val39, 88 | // e2 = e0 + e1 89 | Builder.CreateAdd( 90 | // e0 = a ^ b 91 | Builder.CreateXor(BinOp->getOperand(0), 92 | BinOp->getOperand(1)), 93 | // e1 = 2 * (a & b) 94 | Builder.CreateMul( 95 | Val2, Builder.CreateAnd(BinOp->getOperand(0), 96 | BinOp->getOperand(1)))) 97 | ) // e3 = e2 * 39 98 | ) // e4 = e2 + 23 99 | ) // e5 = e4 * 151 100 | ); // E = e5 + 111 101 | 102 | // The following is visible only if you pass -debug on the command line 103 | // *and* you have an assert build. 104 | LLVM_DEBUG(dbgs() << *BinOp << " -> " << *NewInst << "\n"); 105 | 106 | // Replace `(a + b)` (original instructions) with `(((a ^ b) + 2 * (a & b)) 107 | // * 39 + 23) * 151 + 111` (the new instruction) 108 | ReplaceInstWithInst(&BB, Inst, NewInst); 109 | Changed = true; 110 | 111 | // Update the statistics 112 | ++SubstCount; 113 | } 114 | return Changed; 115 | } 116 | 117 | PreservedAnalyses MBAAdd::run(llvm::Function &F, 118 | llvm::FunctionAnalysisManager &) { 119 | bool Changed = false; 120 | 121 | for (auto &BB : F) { 122 | Changed |= runOnBasicBlock(BB); 123 | } 124 | return (Changed ? llvm::PreservedAnalyses::none() 125 | : llvm::PreservedAnalyses::all()); 126 | } 127 | 128 | //----------------------------------------------------------------------------- 129 | // New PM Registration 130 | //----------------------------------------------------------------------------- 131 | llvm::PassPluginLibraryInfo getMBAAddPluginInfo() { 132 | return {LLVM_PLUGIN_API_VERSION, "mba-add", LLVM_VERSION_STRING, 133 | [](PassBuilder &PB) { 134 | PB.registerPipelineParsingCallback( 135 | [](StringRef Name, FunctionPassManager &FPM, 136 | ArrayRef) { 137 | if (Name == "mba-add") { 138 | FPM.addPass(MBAAdd()); 139 | return true; 140 | } 141 | return false; 142 | }); 143 | }}; 144 | } 145 | 146 | extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo 147 | llvmGetPassPluginInfo() { 148 | return getMBAAddPluginInfo(); 149 | } 150 | -------------------------------------------------------------------------------- /lib/MBASub.cpp: -------------------------------------------------------------------------------- 1 | //============================================================================== 2 | // FILE: 3 | // MBASub.cpp 4 | // 5 | // DESCRIPTION: 6 | // Obfuscation for integer sub instructions through Mixed Boolean Arithmetic 7 | // (MBA). This pass performs an instruction substitution based on this 8 | // equality: 9 | // a - b == (a + ~b) + 1 10 | // See formula 2.2 (j) in [1]. 11 | // 12 | // USAGE: 13 | // $ opt -load-pass-plugin /lib/libMBASub.so `\` 14 | // -passes=-"mba-sub" 15 | // 16 | // [1] "Hacker's Delight" by Henry S. Warren, Jr. 17 | // 18 | // License: MIT 19 | //============================================================================== 20 | #include "MBASub.h" 21 | 22 | #include "llvm/ADT/Statistic.h" 23 | #include "llvm/IR/IRBuilder.h" 24 | #include "llvm/Passes/PassBuilder.h" 25 | #include "llvm/Passes/PassPlugin.h" 26 | #include "llvm/Transforms/Utils/BasicBlockUtils.h" 27 | 28 | #include 29 | 30 | using namespace llvm; 31 | 32 | #define DEBUG_TYPE "mba-sub" 33 | 34 | STATISTIC(SubstCount, "The # of substituted instructions"); 35 | 36 | //----------------------------------------------------------------------------- 37 | // MBASub Implementaion 38 | //----------------------------------------------------------------------------- 39 | bool MBASub::runOnBasicBlock(BasicBlock &BB) { 40 | bool Changed = false; 41 | 42 | // Loop over all instructions in the block. Replacing instructions requires 43 | // iterators, hence a for-range loop wouldn't be suitable. 44 | for (auto Inst = BB.begin(), IE = BB.end(); Inst != IE; ++Inst) { 45 | 46 | // Skip non-binary (e.g. unary or compare) instruction. 47 | auto *BinOp = dyn_cast(Inst); 48 | if (!BinOp) 49 | continue; 50 | 51 | /// Skip instructions other than integer sub. 52 | unsigned Opcode = BinOp->getOpcode(); 53 | if (Opcode != Instruction::Sub || !BinOp->getType()->isIntegerTy()) 54 | continue; 55 | 56 | // A uniform API for creating instructions and inserting 57 | // them into basic blocks. 58 | IRBuilder<> Builder(BinOp); 59 | 60 | // Create an instruction representing (a + ~b) + 1 61 | Instruction *NewValue = BinaryOperator::CreateAdd( 62 | Builder.CreateAdd(BinOp->getOperand(0), 63 | Builder.CreateNot(BinOp->getOperand(1))), 64 | ConstantInt::get(BinOp->getType(), 1)); 65 | 66 | // The following is visible only if you pass -debug on the command line 67 | // *and* you have an assert build. 68 | LLVM_DEBUG(dbgs() << *BinOp << " -> " << *NewValue << "\n"); 69 | 70 | // Replace `(a - b)` (original instructions) with `(a + ~b) + 1` 71 | // (the new instruction) 72 | ReplaceInstWithInst(&BB, Inst, NewValue); 73 | Changed = true; 74 | 75 | // Update the statistics 76 | ++SubstCount; 77 | } 78 | return Changed; 79 | } 80 | 81 | PreservedAnalyses MBASub::run(llvm::Function &F, 82 | llvm::FunctionAnalysisManager &) { 83 | bool Changed = false; 84 | 85 | for (auto &BB : F) { 86 | Changed |= runOnBasicBlock(BB); 87 | } 88 | return (Changed ? llvm::PreservedAnalyses::none() 89 | : llvm::PreservedAnalyses::all()); 90 | } 91 | 92 | //----------------------------------------------------------------------------- 93 | // New PM Registration 94 | //----------------------------------------------------------------------------- 95 | llvm::PassPluginLibraryInfo getMBASubPluginInfo() { 96 | return {LLVM_PLUGIN_API_VERSION, "mba-sub", LLVM_VERSION_STRING, 97 | [](PassBuilder &PB) { 98 | PB.registerPipelineParsingCallback( 99 | [](StringRef Name, FunctionPassManager &FPM, 100 | ArrayRef) { 101 | if (Name == "mba-sub") { 102 | FPM.addPass(MBASub()); 103 | return true; 104 | } 105 | return false; 106 | }); 107 | }}; 108 | } 109 | 110 | extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo 111 | llvmGetPassPluginInfo() { 112 | return getMBASubPluginInfo(); 113 | } 114 | -------------------------------------------------------------------------------- /lib/MergeBB.cpp: -------------------------------------------------------------------------------- 1 | //============================================================================= 2 | // FILE: 3 | // MergeBB.cpp 4 | // 5 | // DESCRIPTION: 6 | // Merges identical basic blocks into one. As an example, consider the CFG 7 | // below. After the transformation, BB1 is merged into BB2. 8 | // ---------------------------------------------------------------------------- 9 | // CFG BEFORE: CFG AFTER: 10 | // ---------------------------------------------------------------------------- 11 | // [BB0] [other BBs] [BB0] [other BBs] | 12 | // \ | \ | | 13 | // [BB1][BB2] [other BBs] ---[BB2] [other BBs] | 14 | // \ | / | / | 15 | // [ BBsucc ] [ BBsucc ] | 16 | // / | \ / | \ V 17 | // ---------------------------------------------------------------------------- 18 | // Only qualifying basic blocks are merged. The edge(s) from (potentially 19 | // multiple) BB0 to BB1, must be one of the following instructions: 20 | // * conditional branch 21 | // * unconditional branch, and 22 | // * switch 23 | // For the edges from BB1 to BBsucc and BB2 to BBsucc, only unconditional 24 | // branch instructions are allowed. Finally, BB1 is identical to BB2 iff all 25 | // instructions in BB1 are identical to the instructions in BB2. For finer 26 | // details please consult the implementation. 27 | // 28 | // This pass will to some extent revert the modifications introduced by 29 | // DuplicateBB. The qualifying clones (lt-clone-1-BBId and lt-clone-2-BBid) 30 | // *will indeed* be merged, but the lt-if-then-else and lt-tail blocks (also 31 | // introduced by DuplicateBB) will be updated, but not removed. Please keep 32 | // this in mind when running the passes in a chain. 33 | // 34 | // USAGE: 35 | // $ opt -load-pass-plugin /lib/libMergeBB.so `\` 36 | // -passes=merge-bb -S 37 | // 38 | // License: MIT 39 | //============================================================================= 40 | #include "MergeBB.h" 41 | 42 | #include "llvm/IR/IntrinsicInst.h" 43 | #include "llvm/Passes/PassBuilder.h" 44 | #include "llvm/Passes/PassPlugin.h" 45 | 46 | #include "llvm/Transforms/Utils/BasicBlockUtils.h" 47 | 48 | #include "llvm/ADT/Statistic.h" 49 | #include "llvm/Support/Debug.h" 50 | 51 | using namespace llvm; 52 | 53 | #define DEBUG_TYPE "MergeBB" 54 | 55 | STATISTIC(NumDedupBBs, "Number of basic blocks merged"); 56 | STATISTIC(OverallNumOfUpdatedBranchTargets, "Number of updated branch targets"); 57 | 58 | //----------------------------------------------------------------------------- 59 | // MergeBB Implementation 60 | //----------------------------------------------------------------------------- 61 | bool MergeBB::canRemoveInst(const Instruction *Inst) { 62 | assert(Inst->hasOneUse() && "Inst needs to have exactly one use"); 63 | 64 | auto *PNUse = dyn_cast(*Inst->user_begin()); 65 | auto *Succ = Inst->getParent()->getTerminator()->getSuccessor(0); 66 | auto *User = cast(*Inst->user_begin()); 67 | 68 | bool SameParentBB = (User->getParent() == Inst->getParent()); 69 | bool UsedInPhi = (PNUse && PNUse->getParent() == Succ && 70 | PNUse->getIncomingValueForBlock(Inst->getParent()) == Inst); 71 | 72 | return UsedInPhi || SameParentBB; 73 | } 74 | 75 | bool MergeBB::canMergeInstructions(ArrayRef Insts) { 76 | const Instruction *Inst1 = Insts[0]; 77 | const Instruction *Inst2 = Insts[1]; 78 | 79 | if (!Inst1->isSameOperationAs(Inst2)) 80 | return false; 81 | 82 | // Each instruction must have exactly zero or one use. 83 | bool HasUse = !Inst1->user_empty(); 84 | for (auto *I : Insts) { 85 | if (HasUse && !I->hasOneUse()) 86 | return false; 87 | if (!HasUse && !I->user_empty()) 88 | return false; 89 | } 90 | 91 | // Not all instructions that have one use can be merged. Make sure that 92 | // instructions that have one use can be safely deleted. 93 | if (HasUse) { 94 | if (!canRemoveInst(Inst1) || !canRemoveInst(Inst2)) 95 | return false; 96 | } 97 | 98 | // Make sure that Inst1 and Inst2 have identical operands. 99 | assert(Inst2->getNumOperands() == Inst1->getNumOperands()); 100 | auto NumOpnds = Inst1->getNumOperands(); 101 | for (unsigned OpndIdx = 0; OpndIdx != NumOpnds; ++OpndIdx) { 102 | if (Inst2->getOperand(OpndIdx) != Inst1->getOperand(OpndIdx)) 103 | return false; 104 | } 105 | 106 | return true; 107 | } 108 | 109 | // Get the number of non-debug instructions in BB 110 | static unsigned getNumNonDbgInstrInBB(BasicBlock *BB) { 111 | unsigned Count = 0; 112 | for (Instruction &Instr : *BB) 113 | if (!isa(Instr)) 114 | Count++; 115 | return Count; 116 | } 117 | 118 | unsigned MergeBB::updateBranchTargets(BasicBlock *BBToErase, BasicBlock *BBToRetain) { 119 | SmallVector BBToUpdate(predecessors(BBToErase)); 120 | 121 | LLVM_DEBUG(dbgs() << "DEDUP BB: merging duplicated blocks (" 122 | << BBToErase->getName() << " into " << BBToRetain->getName() 123 | << ")\n"); 124 | 125 | unsigned UpdatedTargetsCount = 0; 126 | for (BasicBlock *BB0 : BBToUpdate) { 127 | // The terminator is either a branch (conditional or unconditional) or a 128 | // switch statement. One of its targets should be BBToErase. Replace 129 | // that target with BBToRetain. 130 | Instruction *Term = BB0->getTerminator(); 131 | for (unsigned OpIdx = 0, NumOpnds = Term->getNumOperands(); 132 | OpIdx != NumOpnds; ++OpIdx) { 133 | if (Term->getOperand(OpIdx) == BBToErase) { 134 | Term->setOperand(OpIdx, BBToRetain); 135 | UpdatedTargetsCount++; 136 | } 137 | } 138 | } 139 | 140 | return UpdatedTargetsCount; 141 | } 142 | 143 | bool MergeBB::mergeDuplicatedBlock(BasicBlock *BB1, 144 | SmallPtrSet &DeleteList) { 145 | // Do not optimize the entry block 146 | if (BB1 == &BB1->getParent()->getEntryBlock()) 147 | return false; 148 | 149 | // Only merge CFG edges of unconditional branch 150 | BranchInst *BB1Term = dyn_cast(BB1->getTerminator()); 151 | if (!(BB1Term && BB1Term->isUnconditional())) 152 | return false; 153 | 154 | // Do not optimize non-branch and non-switch CFG edges (to keep things 155 | // relatively simple) 156 | for (auto *B : predecessors(BB1)) 157 | if (!(isa(B->getTerminator()) || 158 | isa(B->getTerminator()))) 159 | return false; 160 | 161 | BasicBlock *BBSucc = BB1Term->getSuccessor(0); 162 | 163 | BasicBlock::iterator II = BBSucc->begin(); 164 | const PHINode *PN = dyn_cast(II); 165 | Value *InValBB1 = nullptr; 166 | Instruction *InInstBB1 = nullptr; 167 | BBSucc->getFirstNonPHI(); 168 | if (nullptr != PN) { 169 | // Do not optimize if multiple PHI instructions exist in the successor (to 170 | // keep things relatively simple) 171 | if (++II != BBSucc->end() && isa(II)) 172 | return false; 173 | 174 | InValBB1 = PN->getIncomingValueForBlock(BB1); 175 | InInstBB1 = dyn_cast(InValBB1); 176 | } 177 | 178 | unsigned BB1NumInst = getNumNonDbgInstrInBB(BB1); 179 | for (auto *BB2 : predecessors(BBSucc)) { 180 | // Do not optimize the entry block 181 | if (BB2 == &BB2->getParent()->getEntryBlock()) 182 | continue; 183 | 184 | // Only merge CFG edges of unconditional branch 185 | BranchInst *BB2Term = dyn_cast(BB2->getTerminator()); 186 | if (!(BB2Term && BB2Term->isUnconditional())) 187 | continue; 188 | 189 | // Do not optimize non-branch and non-switch CFG edges (to keep things 190 | // relatively simple) 191 | for (auto *B : predecessors(BB2)) 192 | if (!(isa(B->getTerminator()) || 193 | isa(B->getTerminator()))) 194 | continue; 195 | 196 | // Skip basic blocks that have already been marked for merging 197 | if (DeleteList.end() != DeleteList.find(BB2)) 198 | continue; 199 | 200 | // Make sure that BB2 != BB1 201 | if (BB2 == BB1) 202 | continue; 203 | 204 | // BB1 and BB2 are definitely different if the number of instructions is 205 | // not identical 206 | if (BB1NumInst != getNumNonDbgInstrInBB(BB2)) 207 | continue; 208 | 209 | // Control flow can be merged if incoming values to the PHI node 210 | // at the successor are same values or both defined in the BBs to merge. 211 | // For the latter case, canMergeInstructions executes further analysis. 212 | if (nullptr != PN) { 213 | Value *InValBB2 = PN->getIncomingValueForBlock(BB2); 214 | Instruction *InInstBB2 = dyn_cast(InValBB2); 215 | 216 | bool areValuesSimilar = (InValBB1 == InValBB2); 217 | bool bothValuesDefinedInParent = 218 | ((InInstBB1 && InInstBB1->getParent() == BB1) && 219 | (InInstBB2 && InInstBB2->getParent() == BB2)); 220 | if (!areValuesSimilar && !bothValuesDefinedInParent) 221 | continue; 222 | } 223 | 224 | // Finally, check that all instructions in BB1 and BB2 are identical 225 | LockstepReverseIterator LRI(BB1, BB2); 226 | while (LRI.isValid() && canMergeInstructions(*LRI)) { 227 | --LRI; 228 | } 229 | 230 | // Valid iterator means that a mismatch was found in middle of BB 231 | if (LRI.isValid()) 232 | continue; 233 | 234 | // It is safe to de-duplicate - do so. 235 | unsigned UpdatedTargets = updateBranchTargets(BB1, BB2); 236 | assert(UpdatedTargets && "No branch target was updated"); 237 | OverallNumOfUpdatedBranchTargets += UpdatedTargets; 238 | DeleteList.insert(BB1); 239 | NumDedupBBs++; 240 | 241 | return true; 242 | } 243 | 244 | return false; 245 | } 246 | 247 | PreservedAnalyses MergeBB::run(llvm::Function &Func, 248 | llvm::FunctionAnalysisManager &) { 249 | bool Changed = false; 250 | SmallPtrSet DeleteList; 251 | for (auto &BB : Func) { 252 | Changed |= mergeDuplicatedBlock(&BB, DeleteList); 253 | } 254 | 255 | for (BasicBlock *BB : DeleteList) { 256 | DeleteDeadBlock(BB); 257 | } 258 | 259 | return (Changed ? llvm::PreservedAnalyses::none() 260 | : llvm::PreservedAnalyses::all()); 261 | } 262 | 263 | //----------------------------------------------------------------------------- 264 | // New PM Registration 265 | //----------------------------------------------------------------------------- 266 | llvm::PassPluginLibraryInfo getMergeBBPluginInfo() { 267 | return {LLVM_PLUGIN_API_VERSION, "MergeBB", LLVM_VERSION_STRING, 268 | [](PassBuilder &PB) { 269 | PB.registerPipelineParsingCallback( 270 | [](StringRef Name, FunctionPassManager &FPM, 271 | ArrayRef) { 272 | if (Name == "merge-bb") { 273 | FPM.addPass(MergeBB()); 274 | return true; 275 | } 276 | return false; 277 | }); 278 | }}; 279 | } 280 | 281 | extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo 282 | llvmGetPassPluginInfo() { 283 | return getMergeBBPluginInfo(); 284 | } 285 | 286 | //------------------------------------------------------------------------------ 287 | // Helper data structures 288 | //------------------------------------------------------------------------------ 289 | LockstepReverseIterator::LockstepReverseIterator(BasicBlock *BB1In, 290 | BasicBlock *BB2In) 291 | : BB1(BB1In), BB2(BB2In), Fail(false) { 292 | Insts.clear(); 293 | 294 | Instruction *InstBB1 = getLastNonDbgInst(BB1); 295 | if (nullptr == InstBB1) 296 | Fail = true; 297 | 298 | Instruction *InstBB2 = getLastNonDbgInst(BB2); 299 | if (nullptr == InstBB2) 300 | Fail = true; 301 | 302 | Insts.push_back(InstBB1); 303 | Insts.push_back(InstBB2); 304 | } 305 | 306 | Instruction *LockstepReverseIterator::getLastNonDbgInst(BasicBlock *BB) { 307 | Instruction *Inst = BB->getTerminator(); 308 | 309 | do { 310 | Inst = Inst->getPrevNode(); 311 | } while (Inst && isa(Inst)); 312 | 313 | return Inst; 314 | } 315 | 316 | void LockstepReverseIterator::operator--() { 317 | if (Fail) 318 | return; 319 | 320 | for (auto *&Inst : Insts) { 321 | do { 322 | Inst = Inst->getPrevNode(); 323 | } while (Inst && isa(Inst)); 324 | 325 | if (!Inst) { 326 | // Already at the beginning of BB 327 | Fail = true; 328 | return; 329 | } 330 | } 331 | } 332 | -------------------------------------------------------------------------------- /lib/OpcodeCounter.cpp: -------------------------------------------------------------------------------- 1 | //============================================================================= 2 | // FILE: 3 | // OpcodeCounter.cpp 4 | // 5 | // DESCRIPTION: 6 | // Visits all instructions in a function and counts how many times every 7 | // LLVM IR opcode was used. Prints the output to stderr. 8 | // 9 | // This example demonstrates how to insert your pass at one of the 10 | // predefined extension points, e.g. whenever the vectoriser is run (i.e. via 11 | // `registerVectorizerStartEPCallback` for the new PM). 12 | // 13 | // USAGE: 14 | // 1. New PM 15 | // opt -load-pass-plugin libOpcodeCounter.dylib `\` 16 | // -passes="print" `\` 17 | // -disable-output 18 | // 2. Automatically through an optimisation pipeline - new PM 19 | // opt -load-pass-plugin libOpcodeCounter.dylib --passes='default' `\` 20 | // -disable-output 21 | // 22 | // License: MIT 23 | //============================================================================= 24 | #include "OpcodeCounter.h" 25 | 26 | #include "llvm/Passes/PassBuilder.h" 27 | #include "llvm/Passes/PassPlugin.h" 28 | 29 | using namespace llvm; 30 | 31 | // Pretty-prints the result of this analysis 32 | static void printOpcodeCounterResult(llvm::raw_ostream &, 33 | const ResultOpcodeCounter &OC); 34 | 35 | //----------------------------------------------------------------------------- 36 | // OpcodeCounter implementation 37 | //----------------------------------------------------------------------------- 38 | llvm::AnalysisKey OpcodeCounter::Key; 39 | 40 | OpcodeCounter::Result OpcodeCounter::generateOpcodeMap(llvm::Function &Func) { 41 | OpcodeCounter::Result OpcodeMap; 42 | 43 | for (auto &BB : Func) { 44 | for (auto &Inst : BB) { 45 | StringRef Name = Inst.getOpcodeName(); 46 | 47 | if (OpcodeMap.find(Name) == OpcodeMap.end()) { 48 | OpcodeMap[Inst.getOpcodeName()] = 1; 49 | } else { 50 | OpcodeMap[Inst.getOpcodeName()]++; 51 | } 52 | } 53 | } 54 | 55 | return OpcodeMap; 56 | } 57 | 58 | OpcodeCounter::Result OpcodeCounter::run(llvm::Function &Func, 59 | llvm::FunctionAnalysisManager &) { 60 | return generateOpcodeMap(Func); 61 | } 62 | 63 | PreservedAnalyses OpcodeCounterPrinter::run(Function &Func, 64 | FunctionAnalysisManager &FAM) { 65 | auto &OpcodeMap = FAM.getResult(Func); 66 | 67 | // In the legacy PM, the following string is printed automatically by the 68 | // pass manager. For the sake of consistency, we're adding this here so that 69 | // it's also printed when using the new PM. 70 | OS << "Printing analysis 'OpcodeCounter Pass' for function '" 71 | << Func.getName() << "':\n"; 72 | 73 | printOpcodeCounterResult(OS, OpcodeMap); 74 | return PreservedAnalyses::all(); 75 | } 76 | 77 | //----------------------------------------------------------------------------- 78 | // New PM Registration 79 | //----------------------------------------------------------------------------- 80 | llvm::PassPluginLibraryInfo getOpcodeCounterPluginInfo() { 81 | return { 82 | LLVM_PLUGIN_API_VERSION, "OpcodeCounter", LLVM_VERSION_STRING, 83 | [](PassBuilder &PB) { 84 | // #1 REGISTRATION FOR "opt -passes=print" 85 | // Register OpcodeCounterPrinter so that it can be used when 86 | // specifying pass pipelines with `-passes=`. 87 | PB.registerPipelineParsingCallback( 88 | [&](StringRef Name, FunctionPassManager &FPM, 89 | ArrayRef) { 90 | if (Name == "print") { 91 | FPM.addPass(OpcodeCounterPrinter(llvm::errs())); 92 | return true; 93 | } 94 | return false; 95 | }); 96 | // #2 REGISTRATION FOR "-O{1|2|3|s}" 97 | // Register OpcodeCounterPrinter as a step of an existing pipeline. 98 | // The insertion point is specified by using the 99 | // 'registerVectorizerStartEPCallback' callback. To be more precise, 100 | // using this callback means that OpcodeCounterPrinter will be called 101 | // whenever the vectoriser is used (i.e. when using '-O{1|2|3|s}'. 102 | PB.registerVectorizerStartEPCallback( 103 | [](llvm::FunctionPassManager &PM, 104 | llvm::OptimizationLevel Level) { 105 | PM.addPass(OpcodeCounterPrinter(llvm::errs())); 106 | }); 107 | // #3 REGISTRATION FOR "FAM.getResult(Func)" 108 | // Register OpcodeCounter as an analysis pass. This is required so that 109 | // OpcodeCounterPrinter (or any other pass) can request the results 110 | // of OpcodeCounter. 111 | PB.registerAnalysisRegistrationCallback( 112 | [](FunctionAnalysisManager &FAM) { 113 | FAM.registerPass([&] { return OpcodeCounter(); }); 114 | }); 115 | } 116 | }; 117 | } 118 | 119 | extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo 120 | llvmGetPassPluginInfo() { 121 | return getOpcodeCounterPluginInfo(); 122 | } 123 | 124 | //------------------------------------------------------------------------------ 125 | // Helper functions - implementation 126 | //------------------------------------------------------------------------------ 127 | static void printOpcodeCounterResult(raw_ostream &OutS, 128 | const ResultOpcodeCounter &OpcodeMap) { 129 | OutS << "=================================================" 130 | << "\n"; 131 | OutS << "LLVM-TUTOR: OpcodeCounter results\n"; 132 | OutS << "=================================================\n"; 133 | const char *str1 = "OPCODE"; 134 | const char *str2 = "#TIMES USED"; 135 | OutS << format("%-20s %-10s\n", str1, str2); 136 | OutS << "-------------------------------------------------" 137 | << "\n"; 138 | for (auto &Inst : OpcodeMap) { 139 | OutS << format("%-20s %-10lu\n", Inst.first().str().c_str(), 140 | Inst.second); 141 | } 142 | OutS << "-------------------------------------------------" 143 | << "\n\n"; 144 | } 145 | -------------------------------------------------------------------------------- /lib/RIV.cpp: -------------------------------------------------------------------------------- 1 | //============================================================================= 2 | // FILE: 3 | // RIV.cpp 4 | // 5 | // DESCRIPTION: 6 | // For every basic block in the input function, this pass creates a list of 7 | // integer values reachable from that block. It uses the results of the 8 | // DominatorTree pass. 9 | // 10 | // ALGORITHM: 11 | // ------------------------------------------------------------------------- 12 | // v_N = set of integer values defined in basic block N (BB_N) 13 | // RIV_N = set of reachable integer values for basic block N (BB_N) 14 | // ------------------------------------------------------------------------- 15 | // STEP 1: 16 | // For every BB_N in F: 17 | // compute v_N and store it in DefinedValuesMap 18 | // ------------------------------------------------------------------------- 19 | // STEP 2: 20 | // Compute the RIVs for the entry block (BB_0): 21 | // RIV_0 = {input args, global vars} 22 | // ------------------------------------------------------------------------- 23 | // STEP 3: Traverse the CFG and for every BB_M that BB_N dominates, 24 | // calculate RIV_M as follows: 25 | // RIV_M = {RIV_N, v_N} 26 | // ------------------------------------------------------------------------- 27 | // 28 | // REFERENCES: 29 | // Based on examples from: 30 | // "Building, Testing and Debugging a Simple out-of-tree LLVM Pass", Serge 31 | // Guelton and Adrien Guinet, LLVM Dev Meeting 2015 32 | // 33 | // License: MIT 34 | //============================================================================= 35 | #include "RIV.h" 36 | 37 | #include "llvm/IR/Module.h" 38 | #include "llvm/Passes/PassBuilder.h" 39 | #include "llvm/Passes/PassPlugin.h" 40 | #include "llvm/Support/Format.h" 41 | 42 | #include 43 | 44 | using namespace llvm; 45 | 46 | // DominatorTree node types used in RIV. One could use auto instead, but IMO 47 | // being verbose makes it easier to follow. 48 | using NodeTy = DomTreeNodeBase *; 49 | // A map that a basic block BB holds a set of pointers to values defined in BB. 50 | using DefValMapTy = RIV::Result; 51 | 52 | // Pretty-prints the result of this analysis 53 | static void printRIVResult(llvm::raw_ostream &OutS, const RIV::Result &RIVMap); 54 | 55 | //----------------------------------------------------------------------------- 56 | // RIV Implementation 57 | //----------------------------------------------------------------------------- 58 | RIV::Result RIV::buildRIV(Function &F, NodeTy CFGRoot) { 59 | Result ResultMap; 60 | 61 | // Initialise a double-ended queue that will be used to traverse all BBs in F 62 | std::deque BBsToProcess; 63 | BBsToProcess.push_back(CFGRoot); 64 | 65 | // STEP 1: For every basic block BB compute the set of integer values defined 66 | // in BB 67 | DefValMapTy DefinedValuesMap; 68 | for (BasicBlock &BB : F) { 69 | auto &Values = DefinedValuesMap[&BB]; 70 | for (Instruction &Inst : BB) 71 | if (Inst.getType()->isIntegerTy()) 72 | Values.insert(&Inst); 73 | } 74 | 75 | // STEP 2: Compute the RIVs for the entry BB. This will include global 76 | // variables and input arguments. 77 | auto &EntryBBValues = ResultMap[&F.getEntryBlock()]; 78 | 79 | for (auto &Global : F.getParent()->globals()) 80 | if (Global.getValueType()->isIntegerTy()) 81 | EntryBBValues.insert(&Global); 82 | 83 | for (Argument &Arg : F.args()) 84 | if (Arg.getType()->isIntegerTy()) 85 | EntryBBValues.insert(&Arg); 86 | 87 | // STEP 3: Traverse the CFG for every BB in F calculate its RIVs 88 | while (!BBsToProcess.empty()) { 89 | auto *Parent = BBsToProcess.back(); 90 | BBsToProcess.pop_back(); 91 | 92 | // Get the values defined in Parent 93 | auto &ParentDefs = DefinedValuesMap[Parent->getBlock()]; 94 | // Get the RIV set of for Parent 95 | // (Since RIVMap is updated on every iteration, its contents are likely to 96 | // be moved around when resizing. This means that we need a copy of it 97 | // (i.e. a reference is not sufficient). 98 | llvm::SmallPtrSet ParentRIVs = 99 | ResultMap[Parent->getBlock()]; 100 | 101 | // Loop over all BBs that Parent dominates and update their RIV sets 102 | for (NodeTy Child : *Parent) { 103 | BBsToProcess.push_back(Child); 104 | auto ChildBB = Child->getBlock(); 105 | 106 | // Add values defined in Parent to the current child's set of RIV 107 | ResultMap[ChildBB].insert(ParentDefs.begin(), ParentDefs.end()); 108 | 109 | // Add Parent's set of RIVs to the current child's RIV 110 | ResultMap[ChildBB].insert(ParentRIVs.begin(), ParentRIVs.end()); 111 | } 112 | } 113 | 114 | return ResultMap; 115 | } 116 | 117 | RIV::Result RIV::run(llvm::Function &F, llvm::FunctionAnalysisManager &FAM) { 118 | DominatorTree *DT = &FAM.getResult(F); 119 | Result Res = buildRIV(F, DT->getRootNode()); 120 | 121 | return Res; 122 | } 123 | 124 | PreservedAnalyses RIVPrinter::run(Function &Func, 125 | FunctionAnalysisManager &FAM) { 126 | 127 | auto RIVMap = FAM.getResult(Func); 128 | 129 | printRIVResult(OS, RIVMap); 130 | return PreservedAnalyses::all(); 131 | } 132 | 133 | //----------------------------------------------------------------------------- 134 | // New PM Registration 135 | //----------------------------------------------------------------------------- 136 | AnalysisKey RIV::Key; 137 | 138 | llvm::PassPluginLibraryInfo getRIVPluginInfo() { 139 | return {LLVM_PLUGIN_API_VERSION, "riv", LLVM_VERSION_STRING, 140 | [](PassBuilder &PB) { 141 | // #1 REGISTRATION FOR "opt -passes=print" 142 | PB.registerPipelineParsingCallback( 143 | [&](StringRef Name, FunctionPassManager &FPM, 144 | ArrayRef) { 145 | if (Name == "print") { 146 | FPM.addPass(RIVPrinter(llvm::errs())); 147 | return true; 148 | } 149 | return false; 150 | }); 151 | // #2 REGISTRATION FOR "FAM.getResult(Function)" 152 | PB.registerAnalysisRegistrationCallback( 153 | [](FunctionAnalysisManager &FAM) { 154 | FAM.registerPass([&] { return RIV(); }); 155 | }); 156 | }}; 157 | }; 158 | 159 | extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo 160 | llvmGetPassPluginInfo() { 161 | return getRIVPluginInfo(); 162 | } 163 | 164 | //------------------------------------------------------------------------------ 165 | // Helper functions 166 | //------------------------------------------------------------------------------ 167 | static void printRIVResult(raw_ostream &OutS, const RIV::Result &RIVMap) { 168 | OutS << "=================================================\n"; 169 | OutS << "LLVM-TUTOR: RIV analysis results\n"; 170 | OutS << "=================================================\n"; 171 | 172 | const char *Str1 = "BB id"; 173 | const char *Str2 = "Reachable Integer Values"; 174 | OutS << format("%-10s %-30s\n", Str1, Str2); 175 | OutS << "-------------------------------------------------\n"; 176 | 177 | const char *EmptyStr = ""; 178 | 179 | for (auto const &KV : RIVMap) { 180 | std::string DummyStr; 181 | raw_string_ostream BBIdStream(DummyStr); 182 | KV.first->printAsOperand(BBIdStream, false); 183 | OutS << format("BB %-12s %-30s\n", BBIdStream.str().c_str(), EmptyStr); 184 | for (auto const *IntegerValue : KV.second) { 185 | std::string DummyStr; 186 | raw_string_ostream InstrStr(DummyStr); 187 | IntegerValue->print(InstrStr); 188 | OutS << format("%-12s %-30s\n", EmptyStr, InstrStr.str().c_str()); 189 | } 190 | } 191 | 192 | OutS << "\n\n"; 193 | } 194 | -------------------------------------------------------------------------------- /lib/StaticCallCounter.cpp: -------------------------------------------------------------------------------- 1 | //============================================================================== 2 | // FILE: 3 | // StaticCallCounter.cpp 4 | // 5 | // DESCRIPTION: 6 | // Counts the number of static function calls in the input module. `Static` 7 | // refers to the fact that the analysed functions calls are compile-time 8 | // calls (as opposed to `dynamic`, i.e. run-time). Only direct function 9 | // calls are considered. Calls via functions pointers are not taken into 10 | // account. 11 | // 12 | // This pass is used in `static`, a tool implemented in tools/StaticMain.cpp 13 | // that is a wrapper around StaticCallCounter. `static` allows you to run 14 | // StaticCallCounter without `opt`. 15 | // 16 | // USAGE: 17 | // opt -load-pass-plugin libStaticCallCounter.dylib `\` 18 | // -passes="print" `\` 19 | // -disable-output 20 | // 21 | // License: MIT 22 | //============================================================================== 23 | #include "StaticCallCounter.h" 24 | 25 | #include "llvm/Passes/PassBuilder.h" 26 | #include "llvm/Passes/PassPlugin.h" 27 | 28 | using namespace llvm; 29 | 30 | // Pretty-prints the result of this analysis 31 | static void printStaticCCResult(llvm::raw_ostream &OutS, 32 | const ResultStaticCC &DirectCalls); 33 | 34 | //------------------------------------------------------------------------------ 35 | // StaticCallCounter Implementation 36 | //------------------------------------------------------------------------------ 37 | StaticCallCounter::Result StaticCallCounter::runOnModule(Module &M) { 38 | llvm::MapVector Res; 39 | 40 | for (auto &Func : M) { 41 | for (auto &BB : Func) { 42 | for (auto &Ins : BB) { 43 | 44 | // If this is a call instruction then CB will be not null. 45 | auto *CB = dyn_cast(&Ins); 46 | if (nullptr == CB) { 47 | continue; 48 | } 49 | 50 | // If CB is a direct function call then DirectInvoc will be not null. 51 | auto DirectInvoc = CB->getCalledFunction(); 52 | if (nullptr == DirectInvoc) { 53 | continue; 54 | } 55 | 56 | // We have a direct function call - update the count for the function 57 | // being called. 58 | auto CallCount = Res.find(DirectInvoc); 59 | if (Res.end() == CallCount) { 60 | CallCount = Res.insert(std::make_pair(DirectInvoc, 0)).first; 61 | } 62 | ++CallCount->second; 63 | } 64 | } 65 | } 66 | 67 | return Res; 68 | } 69 | 70 | PreservedAnalyses 71 | StaticCallCounterPrinter::run(Module &M, 72 | ModuleAnalysisManager &MAM) { 73 | 74 | auto DirectCalls = MAM.getResult(M); 75 | 76 | printStaticCCResult(OS, DirectCalls); 77 | return PreservedAnalyses::all(); 78 | } 79 | 80 | StaticCallCounter::Result 81 | StaticCallCounter::run(llvm::Module &M, llvm::ModuleAnalysisManager &) { 82 | return runOnModule(M); 83 | } 84 | 85 | //------------------------------------------------------------------------------ 86 | // New PM Registration 87 | //------------------------------------------------------------------------------ 88 | AnalysisKey StaticCallCounter::Key; 89 | 90 | llvm::PassPluginLibraryInfo getStaticCallCounterPluginInfo() { 91 | return {LLVM_PLUGIN_API_VERSION, "static-cc", LLVM_VERSION_STRING, 92 | [](PassBuilder &PB) { 93 | // #1 REGISTRATION FOR "opt -passes=print" 94 | PB.registerPipelineParsingCallback( 95 | [&](StringRef Name, ModulePassManager &MPM, 96 | ArrayRef) { 97 | if (Name == "print") { 98 | MPM.addPass(StaticCallCounterPrinter(llvm::errs())); 99 | return true; 100 | } 101 | return false; 102 | }); 103 | // #2 REGISTRATION FOR "MAM.getResult(Module)" 104 | PB.registerAnalysisRegistrationCallback( 105 | [](ModuleAnalysisManager &MAM) { 106 | MAM.registerPass([&] { return StaticCallCounter(); }); 107 | }); 108 | }}; 109 | }; 110 | 111 | extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo 112 | llvmGetPassPluginInfo() { 113 | return getStaticCallCounterPluginInfo(); 114 | } 115 | 116 | //------------------------------------------------------------------------------ 117 | // Helper functions 118 | //------------------------------------------------------------------------------ 119 | static void printStaticCCResult(raw_ostream &OutS, 120 | const ResultStaticCC &DirectCalls) { 121 | OutS << "=================================================" 122 | << "\n"; 123 | OutS << "LLVM-TUTOR: static analysis results\n"; 124 | OutS << "=================================================\n"; 125 | const char *str1 = "NAME"; 126 | const char *str2 = "#N DIRECT CALLS"; 127 | OutS << format("%-20s %-10s\n", str1, str2); 128 | OutS << "-------------------------------------------------" 129 | << "\n"; 130 | 131 | for (auto &CallCount : DirectCalls) { 132 | OutS << format("%-20s %-10lu\n", CallCount.first->getName().str().c_str(), 133 | CallCount.second); 134 | } 135 | 136 | OutS << "-------------------------------------------------" 137 | << "\n\n"; 138 | } 139 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(LT_TEST_SHLIBEXT "${CMAKE_SHARED_LIBRARY_SUFFIX}") 2 | 3 | set(LT_TEST_SITE_CFG_INPUT "${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in") 4 | set(LT_TEST_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}") 5 | 6 | set(LIT_SITE_CFG_IN_HEADER "## Autogenerated from ${LT_TEST_SITE_CFG_INPUT}\n## Do not edit!") 7 | 8 | configure_file("${LT_TEST_SITE_CFG_INPUT}" 9 | "${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg.py" @ONLY 10 | ) 11 | -------------------------------------------------------------------------------- /test/ConvertFCmpEqPreds.ll: -------------------------------------------------------------------------------- 1 | ; RUN: opt -load-pass-plugin=%shlibdir/libFindFCmpEq%shlibext -load-pass-plugin=%shlibdir/libConvertFCmpEq%shlibext --passes=convert-fcmp-eq -S %s \ 2 | ; RUN: | FileCheck %s 3 | 4 | define i32 @fcmp_oeq(double %a, double %b) { 5 | ; CHECK-LABEL: @fcmp_oeq 6 | ; CHECK-DAG: %1 = fsub double %a, %b 7 | ; CHECK-NEXT: %2 = bitcast double %1 to i64 8 | ; CHECK-NEXT: %3 = and i64 %2, 9223372036854775807 9 | ; CHECK-NEXT: %4 = bitcast i64 %3 to double 10 | ; CHECK-NEXT: %cmp = fcmp olt double %4, 0x3CB0000000000000 11 | ; CHECK-NEXT: %conv = zext i1 %cmp to i32 12 | ; CHECK-NOT: fcmp oeq 13 | ; CHECK-DAG: ret i32 %conv 14 | 15 | ; a == b 16 | %cmp = fcmp oeq double %a, %b 17 | %conv = zext i1 %cmp to i32 18 | ret i32 %conv 19 | } 20 | 21 | define i32 @fcmp_une(double %a, double %b) { 22 | ; CHECK-LABEL: @fcmp_une 23 | ; CHECK-DAG: %1 = fsub double %a, %b 24 | ; CHECK-NEXT: %2 = bitcast double %1 to i64 25 | ; CHECK-NEXT: %3 = and i64 %2, 9223372036854775807 26 | ; CHECK-NEXT: %4 = bitcast i64 %3 to double 27 | ; CHECK-NEXT: %cmp = fcmp uge double %4, 0x3CB0000000000000 28 | ; CHECK-NEXT: %conv = zext i1 %cmp to i32 29 | ; CHECK-NOT: fcmp une 30 | ; CHECK-DAG: ret i32 %conv 31 | 32 | ; a != b 33 | %cmp = fcmp une double %a, %b 34 | %conv = zext i1 %cmp to i32 35 | ret i32 %conv 36 | } 37 | 38 | define i32 @fcmp_neg_oeq(double %a, double %b) { 39 | ; CHECK-DAG: @fcmp_neg_oeq 40 | ; CHECK-NEXT: %fneg = fneg double %a 41 | ; CHECK-NEXT: %1 = fsub double %fneg, %b 42 | ; CHECK-NEXT: %2 = bitcast double %1 to i64 43 | ; CHECK-NEXT: %3 = and i64 %2, 9223372036854775807 44 | ; CHECK-NEXT: %4 = bitcast i64 %3 to double 45 | ; CHECK-NEXT: %cmp = fcmp olt double %4, 0x3CB0000000000000 46 | ; CHECK-NEXT: %conv = zext i1 %cmp to i32 47 | ; CHECK-NOT: fcmp oeq 48 | ; CHECK-DAG: ret i32 %conv 49 | 50 | ; -a == b 51 | %fneg = fneg double %a 52 | %cmp = fcmp oeq double %fneg, %b 53 | %conv = zext i1 %cmp to i32 54 | ret i32 %conv 55 | } 56 | 57 | define i32 @fcmp_neg_une(double %a, double %b) { 58 | ; CHECK-LABEL: @fcmp_neg_une 59 | ; CHECK-DAG %fneg = fneg double %a 60 | ; CHECK-NEXT %1 = fsub double %fneg, %b 61 | ; CHECK-NEXT %2 = bitcast double %1 to i64 62 | ; CHECK-NEXT %3 = and i64 %2, 9223372036854775807 63 | ; CHECK-NEXT %4 = bitcast i64 %3 to double 64 | ; CHECK-NEXT %cmp = fcmp uge double %4, 0x3CB0000000000000 65 | ; CHECK-NEXT %conv = zext i1 %cmp to i32 66 | ; CHECK-NOT: fcmp une 67 | ; CHECK-DAG ret i32 %conv 68 | 69 | ; -a != b 70 | %fneg = fneg double %a 71 | %cmp = fcmp une double %fneg, %b 72 | %conv = zext i1 %cmp to i32 73 | ret i32 %conv 74 | } 75 | -------------------------------------------------------------------------------- /test/DuplicateBB.ll: -------------------------------------------------------------------------------- 1 | ; RUN: opt -load-pass-plugin %shlibdir/libRIV%shlibext -load-pass-plugin %shlibdir/libDuplicateBB%shlibext -passes=duplicate-bb -S %s | FileCheck %s 2 | 3 | ; Verify that indeed the only BasicBlock in foo is duplcated. It's a trivial 4 | ; BasicBlock with only one terminator instruction (terminator instructions are 5 | ; not duplicated). In total, 1 BasicBlock is replaced with 4. 6 | 7 | define i32 @foo(i32) { 8 | ret i32 1 9 | } 10 | 11 | ; CHECK-LABEL: foo 12 | ; CHECK-NEXT: lt-if-then-else-0: 13 | ; CHECK-NEXT: %1 = icmp eq i32 %0, 0 14 | ; CHECK: br i1 %1, label %lt-clone-1-0, label %lt-clone-2-0 15 | 16 | ; CHECK: lt-clone-1-0: 17 | ; CHECK-NEXT: br label %lt-tail-0 18 | 19 | ; CHECK: lt-clone-2-0: 20 | ; CHECK-NEXT: br label %lt-tail-0 21 | 22 | ; CHECK: lt-tail-0: 23 | ; CHECK-NEXT: ret i32 1 24 | -------------------------------------------------------------------------------- /test/DuplicateBB_MergeBB_exec.ll: -------------------------------------------------------------------------------- 1 | ; RUN: %clang -S -emit-llvm %S/../inputs/input_for_mba.c -o - \ 2 | ; RUN: | opt -load-pass-plugin %shlibdir/libRIV%shlibext -load-pass-plugin %shlibdir/libDuplicateBB%shlibext -passes=duplicate-bb -S -o - \ 3 | ; RUN: | opt -load-pass-plugin %shlibdir/libMergeBB%shlibext -passes=merge-bb -S -o %t.ll 4 | ; RUN: %clang %t.ll -o %t.bin 5 | 6 | ; Verify that after applying DuplicaateBB + MergeBB the output generated by the 7 | ; input module (input_for_mba.c) doesn't change. 8 | 9 | ; The program implemented in input_for_mba.c takes for inputs and adds them up, 10 | ; and returns the result. So if they add up to 0, then the binary returns `0` 11 | ; (aka success). Verify that the obfuscation didn't violate this invariant. 12 | ; RUN: %t.bin 0 0 0 0 13 | ; RUN: %t.bin 1 2 3 -6 14 | ; RUN: %t.bin -13 13 -13 13 15 | ; RUN: %t.bin -11100 100 1000 10000 16 | 17 | ; If the input values don't add up to 0, then the result shouldn't add to `0`. 18 | ; Use `not` to negate the result so that we still test for `success`. 19 | ; RUN: not %t.bin 0 0 0 1 20 | ; RUN: not %t.bin 1 2 3 -7 21 | ; RUN: not %t.bin 13 13 -13 13 22 | ; RUN: not %t.bin -11101 100 1000 10000 23 | -------------------------------------------------------------------------------- /test/DuplicateBB_add.ll: -------------------------------------------------------------------------------- 1 | ; RUN: opt -load-pass-plugin %shlibdir/libRIV%shlibext -load-pass-plugin %shlibdir/libDuplicateBB%shlibext -passes=duplicate-bb -S %s | FileCheck %s 2 | 3 | ; Verify that the output from DuplicateBB is correct, i.e. 4 | ; * every addition was duplicated 5 | ; * the required PHI nodes to merge the values were created. 6 | ; This test is a very illustrative example of how DuplicateBB modifies the 7 | ; input code in the presence of meaningful instructions (as opposed to empty 8 | ; BasicBlocks). 9 | 10 | define i32 @foo(i32, i32, i32, i32) { 11 | %5 = add i32 %1, %0 12 | %6 = add i32 %5, %2 13 | %7 = add i32 %6, %3 14 | ret i32 %7 15 | } 16 | 17 | ; CHECK-LABEL: @foo 18 | ; CHECK-NEXT: lt-if-then-else-0: 19 | ; CHECK-NEXT: %4 = icmp eq i32 %{{[0-3]}}, 0 20 | ; CHECK-NEXT: br i1 %4, label %lt-clone-1-0, label %lt-clone-2-0 21 | 22 | ; CHECK-LABEL: lt-clone-1-0: 23 | ; CHECK-NEXT: %5 = add i32 %1, %0 24 | ; CHECK-NEXT: %6 = add i32 %5, %2 25 | ; CHECK-NEXT: %7 = add i32 %6, %3 26 | ; CHECK-NEXT: br label %lt-tail-0 27 | 28 | ; CHECK-LABEL: lt-clone-2-0: 29 | ; CHECK-NEXT: %8 = add i32 %1, %0 30 | ; CHECK-NEXT: %9 = add i32 %8, %2 31 | ; CHECK-NEXT: %10 = add i32 %9, %3 32 | ; CHECK-NEXT: br label %lt-tail-0 33 | 34 | ; CHECK-LABEL: lt-tail-0: 35 | ; CHECK-NEXT: %11 = phi i32 [ %5, %lt-clone-1-0 ], [ %8, %lt-clone-2-0 ] 36 | ; CHECK-NEXT: %12 = phi i32 [ %6, %lt-clone-1-0 ], [ %9, %lt-clone-2-0 ] 37 | ; CHECK-NEXT: %13 = phi i32 [ %7, %lt-clone-1-0 ], [ %10, %lt-clone-2-0 ] 38 | ; CHECK-NEXT: ret i32 %13 39 | -------------------------------------------------------------------------------- /test/DuplicateBB_exec.ll: -------------------------------------------------------------------------------- 1 | ; RUN: %clang -S -emit-llvm %S/../inputs/input_for_mba.c -o - \ 2 | ; RUN: | opt -load-pass-plugin %shlibdir/libRIV%shlibext -load-pass-plugin %shlibdir/libDuplicateBB%shlibext -passes=duplicate-bb -S -o %t.ll 3 | ; RUN: %clang %t.ll -o %t.bin 4 | 5 | ; Verify that after applying DuplicateBB the output generated by the input 6 | ; module (input_for_mba.c) doesn't change. 7 | 8 | ; The program implemented in input_for_mba.c takes for inputs and adds them up, 9 | ; and returns the result. So if they add up to 0, then the binary returns `0` 10 | ; (aka success). Verify that the obfuscation didn't violate this invariant. 11 | ; RUN: %t.bin 0 0 0 0 12 | ; RUN: %t.bin 1 2 3 -6 13 | ; RUN: %t.bin -13 13 -13 13 14 | ; RUN: %t.bin -11100 100 1000 10000 15 | 16 | ; If the input values don't add up to 0, then the result shouldn't add to `0`. 17 | ; Use `not` to negate the result so that we still test for `success`. 18 | ; RUN: not %t.bin 0 0 0 1 19 | ; RUN: not %t.bin 1 2 3 -7 20 | ; RUN: not %t.bin 13 13 -13 13 21 | ; RUN: not %t.bin -11101 100 1000 10000 22 | -------------------------------------------------------------------------------- /test/DuplicateBB_float.ll: -------------------------------------------------------------------------------- 1 | ; RUN: opt -load-pass-plugin %shlibdir/libRIV%shlibext -load-pass-plugin %shlibdir/libDuplicateBB%shlibext -passes=duplicate-bb -S %s | FileCheck %s 2 | 3 | ; No integer reachable values (only floats), hence the only BasicBlock in foo 4 | ; is *not* duplicated 5 | 6 | @var = global float 1.25 7 | 8 | define i32 @foo(float %in) { 9 | ret i32 1 10 | } 11 | 12 | ; CHECK-LABEL: foo 13 | ; CHECK-NEXT: ret i32 1 14 | -------------------------------------------------------------------------------- /test/DuplicateBB_global.ll: -------------------------------------------------------------------------------- 1 | ; RUN: opt -load-pass-plugin %shlibdir/libRIV%shlibext -load-pass-plugin %shlibdir/libDuplicateBB%shlibext -passes=duplicate-bb -S %s | FileCheck %s 2 | 3 | ; No local integer reachable values (only 1 global), hence the only BasicBlock 4 | ; in foo is *not* duplicated 5 | 6 | @var = global i32 123 7 | 8 | define i32 @foo() { 9 | ret i32 1 10 | } 11 | 12 | ; CHECK-LABEL: foo 13 | ; CHECK-NEXT: ret i32 1 14 | -------------------------------------------------------------------------------- /test/DuplicateBB_switch.ll: -------------------------------------------------------------------------------- 1 | ; RUN: opt -load-pass-plugin %shlibdir/libRIV%shlibext -load-pass-plugin %shlibdir/libDuplicateBB%shlibext -passes=duplicate-bb -S %s | FileCheck %s 2 | 3 | ; Below is a rather simple function with one switch statement. This test 4 | ; verifies that the output from DuplicateBB is correct. As expected, for every 5 | ; BasicBlock 4 new BasicBlocks are created. The switch instruction is not 6 | ; duplicated - it's a terminator instruction, so that's expected. 7 | 8 | define i32 @foo(i32) { 9 | switch i32 %0, label %3 [ 10 | i32 1, label %2 11 | ] 12 | 13 | ;