diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..fe7950bd7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "sea-dsa"] + path = sea-dsa + url = https://github.com/seahorn/sea-dsa.git + branch = llvm-8.0 diff --git a/.travis.yml b/.travis.yml index 70f6ce234..f8ff1c6ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,18 @@ language: generic -dist: xenial -sudo: required -compiler: clang +os: linux +dist: bionic addons: apt: packages: - git - cmake - - python-yaml - - python-psutil + - python3-yaml + - python3-psutil - unzip - libz-dev - libedit-dev + - libboost-all-dev cache: directories: @@ -24,30 +24,37 @@ cache: env: global: - COMPILER_NAME=clang COMPILER=clang++ CXX=clang++ CC=clang - matrix: - - TRAVIS_ENV="--exhaustive --folder=basic" - - TRAVIS_ENV="--exhaustive --folder=data" - - TRAVIS_ENV="--exhaustive --folder=ntdrivers-simplified" - - TRAVIS_ENV="--exhaustive --folder=bits" - - TRAVIS_ENV="--exhaustive --folder=float" - - TRAVIS_ENV="--exhaustive --folder=locks" - - TRAVIS_ENV="--exhaustive --folder=contracts" - - TRAVIS_ENV="--exhaustive --folder=simd" - - TRAVIS_ENV="--exhaustive --folder=memory-safety" - - TRAVIS_ENV="--exhaustive --folder=pthread" - - TRAVIS_ENV="--exhaustive --folder=strings" + jobs: + - TRAVIS_ENV="--exhaustive --folder=c/basic" + - TRAVIS_ENV="--exhaustive --folder=c/data" + - TRAVIS_ENV="--exhaustive --folder=c/ntdrivers-simplified" + - TRAVIS_ENV="--exhaustive --folder=c/bits" + - TRAVIS_ENV="--exhaustive --folder=c/float" + - TRAVIS_ENV="--exhaustive --folder=c/locks" + - TRAVIS_ENV="--exhaustive --folder=c/contracts" + - TRAVIS_ENV="--exhaustive --folder=c/simd" + - TRAVIS_ENV="--exhaustive --folder=c/memory-safety" + - TRAVIS_ENV="--exhaustive --folder=c/pthread" + - TRAVIS_ENV="--exhaustive --folder=c/strings" + - TRAVIS_ENV="--exhaustive --folder=c/special" + - TRAVIS_ENV="--exhaustive --folder=rust/basic --languages=rust" + - TRAVIS_ENV="--exhaustive --folder=rust/functions --languages=rust" + - TRAVIS_ENV="--exhaustive --folder=rust/generics --languages=rust" + - TRAVIS_ENV="--exhaustive --folder=rust/loops --languages=rust" + - TRAVIS_ENV="--exhaustive --folder=rust/recursion --languages=rust" + - TRAVIS_ENV="--exhaustive --folder=rust/structures --languages=rust" + - TRAVIS_ENV="--exhaustive --folder=rust/vector --languages=rust" before_install: - - sudo rm -rf /usr/local/clang-7.0.0 - - sudo add-apt-repository "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-8 main" - wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - + - sudo add-apt-repository "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-8 main" - sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF - sudo apt-get install -y apt-transport-https - - echo "deb https://download.mono-project.com/repo/ubuntu stable-xenial main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list + - echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list - sudo apt-get update install: - - sudo apt-get install -y clang-8 clang-format-8 llvm-8-dev mono-complete ninja-build + - sudo apt-get install -y clang-8 clang-format-8 llvm-8-dev mono-complete ca-certificates-mono ninja-build - sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-8 20 - sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-8 20 - sudo update-alternatives --install /usr/bin/llvm-config llvm-config /usr/bin/llvm-config-8 20 @@ -58,11 +65,12 @@ install: script: - python --version + - python3 --version - $CXX --version - $CC --version - clang --version - clang++ --version - llvm-link --version - llvm-config --version - - ./format/run-clang-format.py -e test/basic/transform-out.c -r lib/smack include/smack share/smack/include share/smack/lib test examples - - ./bin/build.sh + - ./format/run-clang-format.py -e test/c/basic/transform-out.c -r lib/smack include/smack share/smack/include share/smack/lib test examples + - INSTALL_RUST=1 ./bin/build.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 40f5078cc..c73d88a73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ # This file is distributed under the MIT License. See LICENSE for details. # -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.4.3) project(smack) if (NOT WIN32 OR MSYS OR CYGWIN) @@ -18,6 +18,7 @@ if (NOT WIN32 OR MSYS OR CYGWIN) OUTPUT_STRIP_TRAILING_WHITESPACE ) + # TODO: explain why these are required. string(REPLACE "-DNDEBUG" "" LLVM_CXXFLAGS "${LLVM_CXXFLAGS}") string(REPLACE "-Wno-maybe-uninitialized" "" LLVM_CXXFLAGS "${LLVM_CXXFLAGS}") string(REPLACE "-fuse-ld=gold" "" LLVM_CXXFLAGS "${LLVM_CXXFLAGS}") @@ -26,9 +27,27 @@ if (NOT WIN32 OR MSYS OR CYGWIN) string(REPLACE "-Wl," "" LLVM_CXXFLAGS "${LLVM_CXXFLAGS}") string(REPLACE "-gsplit-dwarf" "" LLVM_CXXFLAGS "${LLVM_CXXFLAGS}") string(REGEX REPLACE "-O[0-9]" "" CMAKE_CXX_FLAGS "${LLVM_CXXFLAGS}") + + # TODO: append these one at a time; give rationale. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti -Wno-undefined-var-template") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0") - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0") + + # Apparently avoids a problem with inconsistent visibility settings from LLVM: + # + # > ld: warning: direct access in function 'llvm::_' + # > from file '/usr/local/opt/llvm@8/lib/_' + # > to global weak symbol 'llvm::_' + # > from file 'libsmackTranslator.a(_.cpp.o)' + # > means the weak symbol cannot be overridden at runtime. + # > This was likely caused by different translation units being compiled + # > with different visibility settings. + # + # Solution found on Stack Overflow: + # https://stackoverflow.com/questions/8685045/xcode-with-boost-linkerid-warning-about-visibility-settings + string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fvisibility=hidden") + + # TODO: explain why these are required. + string(APPEND CMAKE_CXX_FLAGS_DEBUG " -O0") + string(APPEND CMAKE_C_FLAGS_DEBUG " -O0") execute_process( COMMAND ${LLVM_CONFIG_EXECUTABLE} --libs @@ -74,87 +93,17 @@ else() endif() include_directories(include) - -add_library(assistDS STATIC - include/assistDS/ArgCast.h - include/assistDS/FuncSimplify.h - include/assistDS/Int2PtrCmp.h - include/assistDS/SimplifyExtractValue.h - include/assistDS/StructReturnToPointer.h - include/assistDS/DSNodeEquivs.h - include/assistDS/FuncSpec.h - include/assistDS/SimplifyGEP.h - include/assistDS/TypeChecks.h - include/assistDS/DataStructureCallGraph.h - include/assistDS/GEPExprArgs.h - include/assistDS/LoadArgs.h - include/assistDS/SimplifyInsertValue.h - include/assistDS/TypeChecksOpt.h - include/assistDS/Devirt.h - include/assistDS/IndCloner.h - include/assistDS/MergeGEP.h - include/assistDS/SimplifyLoad.h - lib/AssistDS/ArgCast.cpp - lib/AssistDS/Devirt.cpp - lib/AssistDS/GEPExprArgs.cpp - lib/AssistDS/LoadArgs.cpp - lib/AssistDS/SimplifyExtractValue.cpp - lib/AssistDS/StructReturnToPointer.cpp - lib/AssistDS/ArgSimplify.cpp - lib/AssistDS/DynCount.cpp - lib/AssistDS/IndCloner.cpp - lib/AssistDS/SimplifyGEP.cpp - lib/AssistDS/TypeChecks.cpp - lib/AssistDS/DSNodeEquivs.cpp - lib/AssistDS/FuncSimplify.cpp - lib/AssistDS/Int2PtrCmp.cpp - lib/AssistDS/MergeGEP.cpp - lib/AssistDS/SimplifyInsertValue.cpp - lib/AssistDS/TypeChecksOpt.cpp - lib/AssistDS/DataStructureCallGraph.cpp - lib/AssistDS/FuncSpec.cpp - lib/AssistDS/SVADevirt.cpp - lib/AssistDS/SimplifyLoad.cpp -) - -add_library(dsa STATIC - include/dsa/AddressTakenAnalysis.h - include/dsa/DSCallGraph.h - include/dsa/DSNode.h - include/dsa/EntryPointAnalysis.h - include/dsa/keyiterator.h - include/dsa/svset.h - include/dsa/AllocatorIdentification.h - include/dsa/DSGraph.h - include/dsa/DSSupport.h - include/dsa/stl_util.h - include/dsa/CallTargets.h - include/dsa/DSGraphTraits.h - include/dsa/DataStructure.h - include/dsa/TypeSafety.h - include/dsa/super_set.h - include/dsa/DSMonitor.h - lib/DSA/AddressTakenAnalysis.cpp - lib/DSA/CallTargets.cpp - lib/DSA/DSTest.cpp - lib/DSA/EquivClassGraphs.cpp - lib/DSA/StdLibPass.cpp - lib/DSA/AllocatorIdentification.cpp - lib/DSA/CompleteBottomUp.cpp - lib/DSA/DataStructure.cpp - lib/DSA/GraphChecker.cpp - lib/DSA/Printer.cpp - lib/DSA/TopDownClosure.cpp - lib/DSA/Basic.cpp - lib/DSA/DSCallGraph.cpp - lib/DSA/DataStructureStats.cpp - lib/DSA/TypeSafety.cpp - lib/DSA/BottomUpClosure.cpp - lib/DSA/DSGraph.cpp - lib/DSA/EntryPointAnalysis.cpp - lib/DSA/DSMonitor.cpp - lib/DSA/Local.cpp - lib/DSA/SanityCheck.cpp +include_directories(sea-dsa/include) + +add_library(utils STATIC + include/utils/Devirt.h + include/utils/MergeGEP.h + include/utils/SimplifyInsertValue.h + include/utils/SimplifyExtractValue.h + lib/utils/Devirt.cpp + lib/utils/MergeGEP.cpp + lib/utils/SimplifyInsertValue.cpp + lib/utils/SimplifyExtractValue.cpp ) add_library(smackTranslator STATIC @@ -211,18 +160,33 @@ add_executable(llvm2bpl tools/llvm2bpl/llvm2bpl.cpp ) +# We need to include Boost header files at least for macOS +find_package (Boost 1.55) +if (Boost_FOUND) + include_directories (${Boost_INCLUDE_DIRS}) +endif () +# We have to import LLVM's cmake definitions to build sea-dsa +# Borrowed from sea-dsa's CMakeLists.txt +find_package (LLVM 8.0.1 EXACT CONFIG) +list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") +include(AddLLVM) +include(HandleLLVMOptions) +# We need the release build of sea-dsa otherwise we'll see a lot of crashes in +# sv-comp benchmarks. +# The following solution is from: https://stackoverflow.com/questions/30985215/passing-variables-down-to-subdirectory-only +set(SMACK_BUILD_TYPE "${CMAKE_BUILD_TYPE}") +set(CMAKE_BUILD_TYPE "Release") +add_subdirectory(sea-dsa/src) +set(CMAKE_BUILD_TYPE ${SMACK_BUILD_TYPE}) + target_link_libraries(smackTranslator ${LLVM_LIBS} ${LLVM_SYSTEM_LIBS} ${LLVM_LDFLAGS}) -target_link_libraries(llvm2bpl smackTranslator assistDS dsa) +target_link_libraries(llvm2bpl smackTranslator utils SeaDsaAnalysis) INSTALL(TARGETS llvm2bpl RUNTIME DESTINATION bin ) INSTALL(FILES - ${CMAKE_CURRENT_SOURCE_DIR}/bin/boogie - ${CMAKE_CURRENT_SOURCE_DIR}/bin/corral - ${CMAKE_CURRENT_SOURCE_DIR}/bin/symbooglix - ${CMAKE_CURRENT_SOURCE_DIR}/bin/lockpwn ${CMAKE_CURRENT_SOURCE_DIR}/bin/smack ${CMAKE_CURRENT_SOURCE_DIR}/bin/smack-doctor ${CMAKE_CURRENT_SOURCE_DIR}/bin/smack-svcomp-wrapper.sh diff --git a/Doxyfile b/Doxyfile index f77267f29..8f750c35f 100644 --- a/Doxyfile +++ b/Doxyfile @@ -5,7 +5,7 @@ #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = smack -PROJECT_NUMBER = 2.4.0 +PROJECT_NUMBER = 2.4.1 PROJECT_BRIEF = "A bounded software verifier." PROJECT_LOGO = OUTPUT_DIRECTORY = docs diff --git a/README.md b/README.md index 0367ec47d..912394b6d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -| **master** | [![Build Status](https://travis-ci.org/smackers/smack.svg?branch=master)](https://travis-ci.org/smackers/smack) | **develop** | [![Build Status](https://travis-ci.org/smackers/smack.svg?branch=develop)](https://travis-ci.org/smackers/smack) | +| **master** | [![Build Status](https://travis-ci.com/smackers/smack.svg?branch=master)](https://travis-ci.com/smackers/smack) | **develop** | [![Build Status](https://travis-ci.com/smackers/smack.svg?branch=develop)](https://travis-ci.com/smackers/smack) | | --- | --- | --- | --- | SMACK Logo diff --git a/bin/boogie b/bin/boogie deleted file mode 100755 index 1ea15b4fb..000000000 --- a/bin/boogie +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -if [ -z "$BOOGIE" ] ; then - echo "The required BOOGIE environment variable is not set. See SMACK documentation for details." - exit 1 -fi -$BOOGIE "$@" diff --git a/bin/build.sh b/bin/build.sh index c20615efd..1fc78fc7a 100755 --- a/bin/build.sh +++ b/bin/build.sh @@ -23,6 +23,7 @@ # Set these flags to control various installation options INSTALL_DEPENDENCIES=1 INSTALL_Z3=1 +INSTALL_CVC4=1 BUILD_BOOGIE=1 BUILD_CORRAL=1 BUILD_SYMBOOGLIX=1 @@ -34,12 +35,13 @@ BUILD_MONO=0 # mono is typically installed from packages (see below) # Support for more programming languages INSTALL_OBJECTIVEC=0 -INSTALL_RUST=0 +INSTALL_RUST=${INSTALL_RUST:-0} # PATHS SMACK_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd )" ROOT="$( cd "${SMACK_DIR}" && cd .. && pwd )" Z3_DIR="${ROOT}/z3" +CVC4_DIR="${ROOT}/cvc4" BOOGIE_DIR="${ROOT}/boogie" CORRAL_DIR="${ROOT}/corral" SYMBOOGLIX_DIR="${ROOT}/symbooglix" @@ -58,7 +60,7 @@ CONFIGURE_INSTALL_PREFIX= CMAKE_INSTALL_PREFIX= # Partial list of dependencies; the rest are added depending on the platform -DEPENDENCIES="git cmake python-yaml python-psutil unzip wget ninja-build" +DEPENDENCIES="git cmake python3-yaml python3-psutil unzip wget ninja-build libboost-all-dev" shopt -s extglob @@ -182,23 +184,23 @@ puts "Detected distribution: $distro" case "$distro" in linux-opensuse*) Z3_DOWNLOAD_LINK="https://github.com/Z3Prover/z3/releases/download/Z3-${Z3_SHORT_VERSION}/z3-${Z3_FULL_VERSION}-x64-debian-8.10.zip" - DEPENDENCIES+=" llvm-clang llvm-devel gcc-c++ mono-complete make" + DEPENDENCIES+=" llvm-clang llvm-devel gcc-c++ mono-complete ca-certificates-mono make" DEPENDENCIES+=" ncurses-devel zlib-devel" ;; linux-@(ubuntu|neon)-14*) Z3_DOWNLOAD_LINK="https://github.com/Z3Prover/z3/releases/download/Z3-${Z3_SHORT_VERSION}/z3-${Z3_FULL_VERSION}-x64-ubuntu-14.04.zip" - DEPENDENCIES+=" clang-${LLVM_SHORT_VERSION} llvm-${LLVM_SHORT_VERSION}-dev mono-complete libz-dev libedit-dev" + DEPENDENCIES+=" clang-${LLVM_SHORT_VERSION} llvm-${LLVM_SHORT_VERSION}-dev mono-complete ca-certificates-mono libz-dev libedit-dev" ;; linux-@(ubuntu|neon)-16*) Z3_DOWNLOAD_LINK="https://github.com/Z3Prover/z3/releases/download/Z3-${Z3_SHORT_VERSION}/z3-${Z3_FULL_VERSION}-x64-ubuntu-16.04.zip" - DEPENDENCIES+=" clang-${LLVM_SHORT_VERSION} llvm-${LLVM_SHORT_VERSION}-dev mono-complete libz-dev libedit-dev" + DEPENDENCIES+=" clang-${LLVM_SHORT_VERSION} llvm-${LLVM_SHORT_VERSION}-dev mono-complete ca-certificates-mono libz-dev libedit-dev" ;; linux-@(ubuntu|neon)-18*) Z3_DOWNLOAD_LINK="https://github.com/Z3Prover/z3/releases/download/Z3-${Z3_SHORT_VERSION}/z3-${Z3_FULL_VERSION}-x64-ubuntu-16.04.zip" - DEPENDENCIES+=" clang-${LLVM_SHORT_VERSION} llvm-${LLVM_SHORT_VERSION}-dev mono-complete libz-dev libedit-dev" + DEPENDENCIES+=" clang-${LLVM_SHORT_VERSION} llvm-${LLVM_SHORT_VERSION}-dev mono-complete ca-certificates-mono libz-dev libedit-dev" ;; linux-ubuntu-12*) @@ -377,7 +379,7 @@ if [ ${INSTALL_OBJECTIVEC} -eq 1 ] ; then echo ". /usr/share/GNUstep/Makefiles/GNUstep.sh" >> ${SMACKENV} puts "Installed Objective-C" -fi +fi if [ ${INSTALL_RUST} -eq 1 ] ; then puts "Installing Rust" @@ -385,9 +387,10 @@ if [ ${INSTALL_RUST} -eq 1 ] ; then ${WGET} https://static.rust-lang.org/dist/${RUST_VERSION}/rust-nightly-x86_64-unknown-linux-gnu.tar.gz -O rust.tar.gz tar xf rust.tar.gz cd rust-nightly-x86_64-unknown-linux-gnu - sudo ./install.sh + sudo ./install.sh --without=rust-docs cd .. - + rm -r rust-nightly-x86_64-unknown-linux-gnu rust.tar.gz + puts "Installed Rust" fi @@ -405,6 +408,17 @@ if [ ${INSTALL_Z3} -eq 1 ] ; then fi fi +if [ ${INSTALL_CVC4} -eq 1 ] ; then + if [ ! -d "$CVC4_DIR" ] ; then + puts "Installing CVC4" + mkdir -p ${CVC4_DIR} + ${WGET} https://github.com/CVC4/CVC4/releases/download/${CVC4_VERSION}/cvc4-${CVC4_VERSION}-x86_64-linux-opt -O ${CVC4_DIR}/cvc4 + chmod +x ${CVC4_DIR}/cvc4 + puts "Installed CVC4" + else + puts "CVC4 already installed" + fi +fi if [ ${BUILD_BOOGIE} -eq 1 ] ; then if ! upToDate $BOOGIE_DIR $BOOGIE_COMMIT ; then @@ -420,10 +434,12 @@ if [ ${BUILD_BOOGIE} -eq 1 ] ; then rm -rf /tmp/nuget/ msbuild Boogie.sln /p:Configuration=Release ln -sf ${Z3_DIR}/bin/z3 ${BOOGIE_DIR}/Binaries/z3.exe + ln -sf ${CVC4_DIR}/cvc4 ${BOOGIE_DIR}/Binaries/cvc4.exe puts "Built Boogie" else puts "Boogie already built" fi + echo export PATH=\"${BOOGIE_DIR}/Binaries:\$PATH\" >> ${SMACKENV} fi @@ -439,10 +455,13 @@ if [ ${BUILD_CORRAL} -eq 1 ] ; then git submodule update msbuild cba.sln /p:Configuration=Release ln -sf ${Z3_DIR}/bin/z3 ${CORRAL_DIR}/bin/Release/z3.exe + ln -sf ${CVC4_DIR}/cvc4 ${CORRAL_DIR}/bin/Release/cvc4.exe + sed -i.debug -e's/Debug/Release/' ${CORRAL_DIR}/bin/corral puts "Built Corral" else puts "Corral already built" fi + echo export PATH=\"${CORRAL_DIR}/bin:\$PATH\" >> ${SMACKENV} fi if [ ${BUILD_SYMBOOGLIX} -eq 1 ] ; then @@ -459,10 +478,12 @@ if [ ${BUILD_SYMBOOGLIX} -eq 1 ] ; then xbuild Symbooglix.sln /p:Configuration=Release ln -s ${Z3_DIR}/bin/z3 ${SYMBOOGLIX_DIR}/src/SymbooglixDriver/bin/Release/z3.exe ln -s ${Z3_DIR}/bin/z3 ${SYMBOOGLIX_DIR}/src/Symbooglix/bin/Release/z3.exe + sed -i.debug -e's/Debug/Release/' ${SYMBOOGLIX_DIR}/bin/symbooglix puts "Built Symbooglix" else puts "Symbooglix already built" fi + echo export PATH=\"${SYMBOOGLIX_DIR}/bin:\$PATH\" >> ${SMACKENV} fi if [ ${BUILD_LOCKPWN} -eq 1 ] ; then @@ -479,11 +500,16 @@ if [ ${BUILD_LOCKPWN} -eq 1 ] ; then else puts "Lockpwn already built" fi + echo export PATH=\"${LOCKPWN_DIR}/Binaries:\$PATH\" >> ${SMACKENV} fi if [ ${BUILD_SMACK} -eq 1 ] ; then puts "Building SMACK" + cd ${SMACK_DIR} + git submodule init + git submodule update + mkdir -p ${SMACK_DIR}/build cd ${SMACK_DIR}/build cmake ${CMAKE_INSTALL_PREFIX} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Debug .. -G Ninja @@ -491,10 +517,6 @@ if [ ${BUILD_SMACK} -eq 1 ] ; then sudo ninja install puts "Configuring shell environment" - echo export BOOGIE=\"mono ${BOOGIE_DIR}/Binaries/Boogie.exe\" >> ${SMACKENV} - echo export CORRAL=\"mono ${CORRAL_DIR}/bin/Release/corral.exe\" >> ${SMACKENV} - echo export SYMBOOGLIX=\"mono ${SYMBOOGLIX_DIR}/src/SymbooglixDriver/bin/Release/sbx.exe\" >> ${SMACKENV} - echo export LOCKPWN=\"mono ${LOCKPWN_DIR}/Binaries/lockpwn.exe\" >> ${SMACKENV} source ${SMACKENV} puts "The required environment variables have been set in ${SMACKENV}" puts "You should source ${SMACKENV} in your .bashrc" diff --git a/bin/corral b/bin/corral deleted file mode 100755 index b26192e81..000000000 --- a/bin/corral +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -if [ -z "$CORRAL" ] ; then - echo "The required CORRAL environment variable is not set. See SMACK documentation for details." - exit 1 -fi -$CORRAL "$@" diff --git a/bin/lockpwn b/bin/lockpwn deleted file mode 100755 index c8cdfec33..000000000 --- a/bin/lockpwn +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -if [ -z "$LOCKPWN" ] ; then - echo "The required LOCKPWN environment variable is not set. See SMACK documentation for details." - exit 1 -fi -$LOCKPWN "$@" diff --git a/bin/smack b/bin/smack index bbc4e0601..e8efa74db 100755 --- a/bin/smack +++ b/bin/smack @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # # This file is distributed under the MIT License. See LICENSE for details. # diff --git a/bin/smack-doctor b/bin/smack-doctor index c206adfd9..10610c495 100755 --- a/bin/smack-doctor +++ b/bin/smack-doctor @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # # This file is distributed under the MIT License. See LICENSE for details. # diff --git a/bin/symbooglix b/bin/symbooglix deleted file mode 100755 index 79c20a3cb..000000000 --- a/bin/symbooglix +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -if [ -z "$SYMBOOGLIX" ] ; then - echo "The required SYMBOOGLIX environment variable is not set. See SMACK documentation for details." - exit 1 -fi -$SYMBOOGLIX "$@" diff --git a/bin/versions b/bin/versions index d9e780e1d..7436fd763 100644 --- a/bin/versions +++ b/bin/versions @@ -1,10 +1,11 @@ MONO_VERSION=5.0.0.100 Z3_SHORT_VERSION=4.8.5 Z3_FULL_VERSION=4.8.5 +CVC4_VERSION=1.7 BOOGIE_COMMIT=5c829b6340 -CORRAL_COMMIT=c446f5e827 -SYMBOOGLIX_COMMIT=7210e5d09b -LOCKPWN_COMMIT=73eddf97bd +CORRAL_COMMIT=6437c41d34 +SYMBOOGLIX_COMMIT=ccb2e7f2b3 +LOCKPWN_COMMIT=12ba58f1ec LLVM_SHORT_VERSION=8 LLVM_FULL_VERSION=8.0.1 -RUST_VERSION=2016-12-16 +RUST_VERSION=2019-07-16 diff --git a/bin/vsmack b/bin/vsmack index 8cbbdf037..253c3ad73 100755 --- a/bin/vsmack +++ b/bin/vsmack @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 import argparse import os.path @@ -84,7 +84,7 @@ if __name__ == '__main__': raise Exception('failed to halt the box') except Exception as e: - print 'error:', e + print('error:', e) finally: pass diff --git a/docs/installation.md b/docs/installation.md index e129dd08b..9af8d42bf 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -82,11 +82,13 @@ SMACK depends on the following projects: * [LLVM][] version [8.0.1][LLVM-8.0.1] * [Clang][] version [8.0.1][Clang-8.0.1] -* [Python][] version 2.7 or greater +* [Boost][] version 1.55 or greater +* [Python][] version 3.6.8 or greater * [Ninja][] version 1.5.1 or greater * [Mono][] version 5.0.0 or greater (except on Windows) * [Z3][] or compatible SMT-format theorem prover * [Boogie][] or [Corral][] or compatible Boogie-format verifier +* [sea-dsa][] See [here](https://github.com/smackers/smack/blob/master/bin/versions) for compatible version numbers of [Boogie][] and [Corral][]. See the appropriate @@ -153,9 +155,14 @@ installed via the Microsoft Store. ### Installing SMACK Itself -SMACK is built using [CMake][] via the following sequence of shell commands -from SMACK's root directory: -````Shell +The prerequisite step for building SMACK is to fetch its submodule +(i.e., [sea-dsa][]). Then, it is built using [CMake][]. Both steps can be done +via the following sequence of shell commands from SMACK's root directory: +```Shell +# fetch the submodule +git submodule init +git submodule update +# build SMACK mkdir build cd build cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Debug .. -G Ninja @@ -169,17 +176,17 @@ prefix, add the additional flag: ```` substituting the string `PREFIX` for the desired location. -Actually running SMACK relies on the environment variables `BOOGIE` and -`CORRAL` targeting the `Boogie.exe` and `corral.exe` executables, for instance -residing in paths prefixed by `XXX` and `YYY`: -````Shell -export BOOGIE="mono /XXX/Boogie/Binaries/Boogie.exe" -export CORRAL="mono /YYY/Corral/bin/Release/corral.exe" +Actually running SMACK relies on `boogie` and `corral` being in the executable +path. For instance, if you have built Boogie and Corral from source at paths +at `$BOOGIE_SOURCE` and `$CORRAL_SOURCE`: +````bash +export PATH="$BOOGIE_SOURCE/Binaries:$PATH" +export PATH="$CORRAL_SOURCE/bin:$PATH" ```` Source the preceding lines in your shell's `.profile`, and ensure they invoke Boogie/Corral correctly. For example, running -````Shell -BOOGIE +````console +$ boogie ```` should result in ```` @@ -215,6 +222,8 @@ shell in the `test` directory by executing [build.sh]: https://github.com/smackers/smack/blob/master/bin/build.sh [Xcode]: https://developer.apple.com/xcode/ [Homebrew]: http://brew.sh/ -[Homebrew Cask]: http://caskroom.io +[Homebrew Cask]: https://formulae.brew.sh/cask/ [Docker]: https://www.docker.com [Ninja]: https://ninja-build.org +[sea-dsa]: https://github.com/seahorn/sea-dsa +[Boost]: http://boost.org/ diff --git a/format/run-clang-format.py b/format/run-clang-format.py index 3d0cca0a9..2923ee31a 100755 --- a/format/run-clang-format.py +++ b/format/run-clang-format.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """A wrapper script around clang-format, suitable for linting multiple files and to use for continuous integration. @@ -126,7 +126,7 @@ def run_clang_format_diff(args, file): # # It's not pretty, due to Python 2 & 3 compatibility. encoding_py3 = {} - if sys.version_info[0] >= 3: + if sys.version_info[0] >= 3 and sys.version_info[1] >= 6: encoding_py3['encoding'] = 'utf-8' try: diff --git a/include/assistDS/ArgCast.h b/include/assistDS/ArgCast.h deleted file mode 100644 index 4c0bf8765..000000000 --- a/include/assistDS/ArgCast.h +++ /dev/null @@ -1,35 +0,0 @@ -//===-------- ArgCast.cpp - Cast Arguments to Calls -----------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// Convert -// call(bitcast (.., T1 arg, ...)F to(..., T2 arg, ...))(..., T2 val, ...) -// to -// val1 = bitcast T2 val to T1 -// call F (..., T1 val1, ...) -//===----------------------------------------------------------------------===// - -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/Module.h" -#include "llvm/Pass.h" - -namespace llvm { - // - // Class: ArgCast - // - // Description: - // Implement an LLVM pass that cleans up call sites that take casted args - // - class ArgCast : public ModulePass { - public: - static char ID; - ArgCast() : ModulePass(ID) {} - virtual bool runOnModule(Module& M); - }; -} - diff --git a/include/assistDS/DSNodeEquivs.h b/include/assistDS/DSNodeEquivs.h deleted file mode 100644 index e05a542c7..000000000 --- a/include/assistDS/DSNodeEquivs.h +++ /dev/null @@ -1,68 +0,0 @@ -//===- DSNodeEquivs.h - Build DSNode equivalence classes ------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This pass computes equivalence classes of DSNodes across DSGraphs. -// -//===----------------------------------------------------------------------===// - -#ifndef DSNODEEQUIVS_H -#define DSNODEEQUIVS_H - -#include "dsa/DataStructure.h" -#include "dsa/DSGraph.h" -#include "dsa/DSNode.h" - -#include "llvm/ADT/EquivalenceClasses.h" - -#include - -namespace llvm { - -typedef std::vector FunctionList; -typedef FunctionList::iterator FunctionList_it; - -class DSNodeEquivs : public ModulePass { -private: - EquivalenceClasses Classes; - - void buildDSNodeEquivs(Module &M); - - void addNodesFromGraph(DSGraph *G); - FunctionList getCallees(CallSite &CS); - void equivNodesThroughCallsite(CallInst *CI); - void equivNodesToGlobals(DSGraph *G); - void equivNodeMapping(DSGraph::NodeMapTy & NM); - -public: - static char ID; - - DSNodeEquivs() : ModulePass(ID) {} - - void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequiredTransitive(); - AU.setPreservesAll(); - } - - bool runOnModule(Module &M); - - // Returns the computed equivalence classes. Two DSNodes in the same - // equivalence class may alias. DSNodes may also alias if they have the - // Incomplete, Unknown, or External flags set (even if they are in different - // equivalence classes). - const EquivalenceClasses &getEquivalenceClasses(); - - // Returns a DSNode for the specified value. Note that two nodes may alias - // even if they have different DSNodes (because the DSNodes may belong to - // different DSGraphs). - const DSNode *getMemberForValue(const Value *V); -}; - -} - -#endif // DSNODEEQUIVS_H diff --git a/include/assistDS/DataStructureCallGraph.h b/include/assistDS/DataStructureCallGraph.h deleted file mode 100644 index ed5dcf9bf..000000000 --- a/include/assistDS/DataStructureCallGraph.h +++ /dev/null @@ -1,107 +0,0 @@ -//===- DataStructureCallGraph.h - Provide a CallGraph using DSA -----------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file declares the DataStructureCallGraph implementation of the -// CallGraph analysis. Based on llvm/lib/Analysis/IPA/CallGraph.cpp. -// -//===----------------------------------------------------------------------===// - -#ifndef _DATA_STRUCTURE_CALLGRAPH_H -#define _DATA_STRUCTURE_CALLGRAPH_H - -#include "dsa/CallTargets.h" -#include "dsa/DataStructure.h" - -#include "llvm/IR/Module.h" -#include "llvm/Analysis/CallGraph.h" -#include "llvm/Support/raw_ostream.h" - -namespace llvm { - -class DataStructureCallGraph : public CallGraphWrapperPass { - // Root is root of the call graph, or the external node if a 'main' function - // couldn't be found. - CallGraphNode *Root; - - // ExternalCallingNode - This node has edges to all external functions and - // those internal functions that have their address taken. - CallGraphNode *ExternalCallingNode; - - // CallsExternalNode - This node has edges to it from all functions making - // indirect calls or calling an external function. - CallGraphNode *CallsExternalNode; - - typedef dsa::CallTargetFinder CallTargetFinderTy; - -public: - static char ID; - DataStructureCallGraph() : - Root(0), ExternalCallingNode(0), CallsExternalNode(0) { } - - virtual bool runOnModule(Module &M); - - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired(); - AU.addRequired(); - AU.setPreservesAll(); - } - - virtual void print(raw_ostream &OS, const Module *) const { - OS << "CallGraph Root is: "; - if (Function *F = getRoot()->getFunction()) - OS << F->getName() << "\n"; - else { - OS << "<>\n"; - } - - CallGraphWrapperPass::print(OS, 0); - } - - virtual void releaseMemory() { - destroy(); - } - - // getAdjustedAnalysisPointer - This method is used when a pass implements an - // analysis interface through multiple inheritance. If needed, it should - // override this to adjust the this pointer as needed for the specified pass - // info. - virtual void *getAdjustedAnalysisPointer(AnalysisID PI) { - if (PI == CallGraphAnalysis::ID()) - return (CallGraph*)this; - return this; - } - - CallGraphNode* getExternalCallingNode() const { return ExternalCallingNode; } - CallGraphNode* getCallsExternalNode() const { return CallsExternalNode; } - - // getRoot - Return the root of the call graph, which is either main, or if - // main cannot be found, the external node. - CallGraphNode *getRoot() { return Root; } - const CallGraphNode *getRoot() const { return Root; } - -private: - // addToCallGraph - Add a function to the call graph, and link the node to all - // of the functions that it calls. - void addToCallGraph(Function *F); - - // destroy - Release memory for the call graph - virtual void destroy() { - // CallsExternalNode is not in the function map, delete it explicitly. - if (CallsExternalNode) { - // CallsExternalNode->allReferencesDropped(); FIXME FIXME FIXME FIXME - delete CallsExternalNode; - CallsExternalNode = 0; - } - CallGraphWrapperPass::releaseMemory(); - } -}; - -} - -#endif // _DATA_STRUCTURE_CALLGRAPH_H diff --git a/include/assistDS/FuncSimplify.h b/include/assistDS/FuncSimplify.h deleted file mode 100644 index ad4a93648..000000000 --- a/include/assistDS/FuncSimplify.h +++ /dev/null @@ -1,30 +0,0 @@ -//===-------- ArgCast.cpp - Cast Arguments to Calls -----------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" -#include "llvm/Pass.h" - -namespace llvm { - // - // Class: FuncSimplify - // - // Description: - // Replace all internal aliases with the - // aliasee value - // - class FuncSimplify : public ModulePass { - public: - static char ID; - FuncSimplify() : ModulePass(ID) {} - virtual bool runOnModule(Module& M); - }; -} - diff --git a/include/assistDS/FuncSpec.h b/include/assistDS/FuncSpec.h deleted file mode 100644 index 433d98c96..000000000 --- a/include/assistDS/FuncSpec.h +++ /dev/null @@ -1,35 +0,0 @@ -//===-- FuncSpec.cpp - Clone Functions With Constant Function Ptr Args ----===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This pass clones functions that take constant function pointers as arguments -// from some call sites. It changes those call sites to call cloned functions. -// -//===----------------------------------------------------------------------===// - -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" -#include "llvm/Pass.h" - -namespace llvm { - // - // Class: FuncSpec - // - // Description: - // Implement an LLVM pass that clones functions which are passed - // as an argument - // - // - class FuncSpec : public ModulePass { - public: - static char ID; - FuncSpec() : ModulePass(ID) {} - virtual bool runOnModule(Module& M); - }; -} - diff --git a/include/assistDS/GEPExprArgs.h b/include/assistDS/GEPExprArgs.h deleted file mode 100644 index 201097bd5..000000000 --- a/include/assistDS/GEPExprArgs.h +++ /dev/null @@ -1,34 +0,0 @@ - -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Identify GEPs used as arguments to call sites. -// -//===----------------------------------------------------------------------===// - -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" -#include "llvm/Pass.h" - -namespace llvm { - // - // Class: GEPExprArgs - // - // Description: - // Implement an LLVM pass that clones functions which are passed GEPs - // as an argument - // - // - class GEPExprArgs : public ModulePass { - public: - static char ID; - GEPExprArgs() : ModulePass(ID) {} - virtual bool runOnModule(Module& M); - }; -} - diff --git a/include/assistDS/IndCloner.h b/include/assistDS/IndCloner.h deleted file mode 100644 index d99200ad6..000000000 --- a/include/assistDS/IndCloner.h +++ /dev/null @@ -1,34 +0,0 @@ -//===-- IndCloner.h - Clone Indirectly Called Functions -------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This code defines a pass which clones functions which could potentially be -// used in indirect function calls. -// -//===----------------------------------------------------------------------===// - -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" -#include "llvm/Pass.h" - -namespace llvm { - // - // Class: IndClone - // - // Description: - // Implement an LLVM pass that clones functions which could be used for - // indirect function calls. - // - class IndClone : public ModulePass { - public: - static char ID; - IndClone() : ModulePass(ID) {} - virtual bool runOnModule(Module& M); - }; -} - diff --git a/include/assistDS/Int2PtrCmp.h b/include/assistDS/Int2PtrCmp.h deleted file mode 100644 index e9356a97c..000000000 --- a/include/assistDS/Int2PtrCmp.h +++ /dev/null @@ -1,36 +0,0 @@ -//===-- Int2PtrCmp.cpp - Merge inttoptr/ptrtoint --------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Remove unnecessary inttoptr casts -// Specially ones used in just compares -// Most cases derived from InstCombine -// -//===----------------------------------------------------------------------===// - -#include "llvm/IR/DataLayout.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" -#include "llvm/Pass.h" - - -namespace llvm { - // - // Class: Int2PtrCmp - // - // - class Int2PtrCmp : public ModulePass { - private: - const DataLayout * TD; - public: - static char ID; - Int2PtrCmp() : ModulePass(ID) {} - virtual bool runOnModule(Module& M); - }; -} - diff --git a/include/assistDS/LoadArgs.h b/include/assistDS/LoadArgs.h deleted file mode 100644 index 760ce5c5b..000000000 --- a/include/assistDS/LoadArgs.h +++ /dev/null @@ -1,36 +0,0 @@ -//===-- LoadArgs.cpp - Promote args if they came from loads ---------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Identify calls, that are passed arguemtns that are LoadInsts. -// Pass the original pointer instead. Helps improve some -// context sensitivity. -// -//===----------------------------------------------------------------------===// - -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" -#include "llvm/Pass.h" - -namespace llvm { - // - // Class: LoadArgs - // - // Description: - // Implement an LLVM pass that clones functions which are passed loads - // as an argument - // - // - class LoadArgs : public ModulePass { - public: - static char ID; - LoadArgs() : ModulePass(ID) {} - virtual bool runOnModule(Module& M); - }; -} - diff --git a/include/assistDS/SimplifyGEP.h b/include/assistDS/SimplifyGEP.h deleted file mode 100644 index e8a48749d..000000000 --- a/include/assistDS/SimplifyGEP.h +++ /dev/null @@ -1,32 +0,0 @@ -//===--------------- SimplifyGEP.cpp - Simplify GEPs types ---------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Simplify GEPs with bitcasts (mostly cloned from InstCombine) -// -//===----------------------------------------------------------------------===// - -#include "llvm/IR/DataLayout.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" -#include "llvm/Pass.h" - -namespace llvm { - // - // Class: SimplifyGEP - // - class SimplifyGEP : public ModulePass { - private: - const DataLayout * TD; - public: - static char ID; - SimplifyGEP() : ModulePass(ID) {} - virtual bool runOnModule(Module& M); - }; -} - diff --git a/include/assistDS/SimplifyLoad.h b/include/assistDS/SimplifyLoad.h deleted file mode 100644 index 75c03f852..000000000 --- a/include/assistDS/SimplifyLoad.h +++ /dev/null @@ -1,29 +0,0 @@ -//===--------------- SimplifyLoad.cpp - Simplify load insts ---------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Derived from InstCombine -// -//===----------------------------------------------------------------------===// - -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" -#include "llvm/Pass.h" - -namespace llvm { - // - // Class: SimplifyLoad - // - class SimplifyLoad : public ModulePass { - public: - static char ID; - SimplifyLoad() : ModulePass(ID) {} - virtual bool runOnModule(Module& M); - }; -} - diff --git a/include/assistDS/StructReturnToPointer.h b/include/assistDS/StructReturnToPointer.h deleted file mode 100644 index 9cbd01a36..000000000 --- a/include/assistDS/StructReturnToPointer.h +++ /dev/null @@ -1,30 +0,0 @@ -//===-------- StructReturnToPointer.cpp ------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// For functions that return structures, -// transform them to return a pointer to the structure instead. -// -//===----------------------------------------------------------------------===// - -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" -#include "llvm/Pass.h" - -namespace llvm { - // - // Class: StructRet - // - class StructRet : public ModulePass { - public: - static char ID; - StructRet() : ModulePass(ID) {} - virtual bool runOnModule(Module& M); - }; -} - diff --git a/include/assistDS/TypeChecks.h b/include/assistDS/TypeChecks.h deleted file mode 100644 index cf1cf2fea..000000000 --- a/include/assistDS/TypeChecks.h +++ /dev/null @@ -1,116 +0,0 @@ -//===------------- TypeChecks.h - Insert runtime type checks --------------===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This pass inserts checks to enforce type safety during runtime. -// -//===----------------------------------------------------------------------===// - -#ifndef TYPE_CHECKS_H -#define TYPE_CHECKS_H - -#include "dsa/AddressTakenAnalysis.h" - -#include "llvm/IR/DataLayout.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Function.h" -#include "llvm/Pass.h" -#include "llvm/IR/CallSite.h" -#include "llvm/IR/Dominators.h" -#include "llvm/Analysis/LoopInfo.h" - -#include -#include -#include - -namespace llvm { - -class Type; -class Value; - -class TypeChecks : public ModulePass { -private: - std::map UsedTypes; - std::map IndFunctionsMap; - std::list VAArgFunctions; - std::list ByValFunctions; - std::list AddressTakenFunctions; - std::set IndCalls; - std::set InsertedPHINodes; - std::map SelectInst_MD_Map; - std::map SelectInst_BasePtr_Map; - std::map PHINode_MD_Map; - std::map PHINode_BasePtr_Map; - std::map BitCast_MD_Map; - - // Analysis from other passes. - const DataLayout *TD; - AddressTakenAnalysis* addrAnalysis; - - unsigned int getTypeMarker(Type*); - unsigned int getTypeMarker(Value*); - Constant *getTypeMarkerConstant(Value * V); - Constant *getTypeMarkerConstant(Type* T); - unsigned int getSize(Type*); - Constant* getSizeConstant(Type*); - - bool initShadow(Module &M); - void addTypeMap(Module &M) ; - void optimizeChecks(Module &M); - void initRuntimeCheckPrototypes(Module &M); - - bool visitMain(Module &M, Function &F); - - bool visitCallInst(Module &M, CallInst &CI); - bool visitInvokeInst(Module &M, InvokeInst &CI); - bool visitCallSite(Module &M, CallSite CS); - bool visitIndirectCallSite(Module &M, Instruction *I); - - bool visitLoadInst(Module &M, LoadInst &LI); - bool visitStoreInst(Module &M, StoreInst &SI); - bool visitAllocaInst(Module &M, AllocaInst &AI); - bool visitVAArgInst(Module &M, VAArgInst &VI); - - bool visitUses(Instruction *I, Instruction *AI, Value *BCI); - - bool visitGlobal(Module &M, GlobalVariable &GV, - Constant *C, Instruction &I, SmallVector); - - bool visitInternalByValFunction(Module &M, Function &F); - bool visitExternalByValFunction(Module &M, Function &F); - bool visitByValFunction(Module &M, Function &F); - - bool visitAddressTakenFunction(Module &M, Function &F); - - - bool visitVarArgFunction(Module &M, Function &F); - bool visitInternalVarArgFunction(Module &M, Function &F); - - bool visitInputFunctionValue(Module &M, Value *V, Instruction *CI); - -public: - static char ID; - TypeChecks() : ModulePass(ID) {} - virtual bool runOnModule(Module &M); - virtual void print(raw_ostream &OS, const Module *M) const; - - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired(); - AU.addRequired(); - AU.addRequired(); - } - - // Return the map containing all of the types used in the module. - const std::map &getTypes() const { - return UsedTypes; - } -}; - -} // End llvm namespace - -#endif diff --git a/include/assistDS/TypeChecksOpt.h b/include/assistDS/TypeChecksOpt.h deleted file mode 100644 index 0bf0e5dda..000000000 --- a/include/assistDS/TypeChecksOpt.h +++ /dev/null @@ -1,52 +0,0 @@ -//===---------- TypeChecksOpt.h - Remove safe runtime type checks ---------===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This pass removes type checks that are statically proven safe -// -//===----------------------------------------------------------------------===// - -#ifndef TYPE_CHECKS_OPT_H -#define TYPE_CHECKS_OPT_H - -#include "dsa/TypeSafety.h" - -#include "llvm/Pass.h" -#include "llvm/IR/DataLayout.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/CallSite.h" - -#include - -namespace llvm { - -class Type; -class Value; - -class TypeChecksOpt : public ModulePass { - -private: - - // Analysis from other passes. - dsa::TypeSafety *TS; - std::list toDelete; - -public: - static char ID; - TypeChecksOpt() : ModulePass(ID) {} - virtual bool runOnModule(Module &M); - - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired >(); - } - -}; - -} // End llvm namespace - -#endif diff --git a/include/dsa/AddressTakenAnalysis.h b/include/dsa/AddressTakenAnalysis.h deleted file mode 100644 index 4be834087..000000000 --- a/include/dsa/AddressTakenAnalysis.h +++ /dev/null @@ -1,47 +0,0 @@ -//===-- AddressTakenAnalysis.h - Identify address Taken Values-------------===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This pass helps find which functions are address taken in a module. -// Functions are considered to be address taken if they are either stored, -// or passed as arguments to functions. -// -//===----------------------------------------------------------------------===// - -#ifndef _ADDRESSTAKENANALYSIS_H -#define _ADDRESSTAKENANALYSIS_H - -#include "llvm/Pass.h" - -#include -#include - -namespace llvm { -class Function; -class Module; -class Instruction; - -class AddressTakenAnalysis : public llvm::ModulePass { - std::set addressTakenFunctions; -public: - static char ID; - AddressTakenAnalysis() : ModulePass (ID) {} - virtual ~AddressTakenAnalysis(); - - bool runOnModule(llvm::Module&); - - virtual void getAnalysisUsage(llvm::AnalysisUsage &Info) const; - - bool hasAddressTaken(llvm::Function *); - -}; - -} - -#endif /* _ADDRESSTAKENANALYSIS_H */ - diff --git a/include/dsa/AllocatorIdentification.h b/include/dsa/AllocatorIdentification.h deleted file mode 100644 index 2a35c00c9..000000000 --- a/include/dsa/AllocatorIdentification.h +++ /dev/null @@ -1,56 +0,0 @@ -//===-- AllocatorIdentification.h - Identify alloc wrappers --------------===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// Identify malloc/free wrappers. -//===----------------------------------------------------------------------===// - -#ifndef _ALLOCATORIDENTIFICATION_H -#define _ALLOCATORIDENTIFICATION_H - -#include -#include "llvm/Pass.h" -#include "llvm/IR/Value.h" - -namespace llvm { - class Function; - class Module; - class Instruction; - - class AllocIdentify : public llvm::ModulePass { - protected: - std::set allocators; - std::set deallocators; - bool flowsFrom(Value *Dest,Value *Src); - - public: - std::set::iterator alloc_begin() { - return allocators.begin(); - } - std::set::iterator alloc_end() { - return allocators.end(); - } - std::set::iterator dealloc_begin() { - return deallocators.begin(); - } - std::set::iterator dealloc_end() { - return deallocators.end(); - } - static char ID; - AllocIdentify(); - virtual ~AllocIdentify(); - bool runOnModule(llvm::Module&); - virtual void getAnalysisUsage(llvm::AnalysisUsage &Info) const; - virtual StringRef getPassName() const { - return "Allocator Identification Analysis (find malloc/free wrappers)"; - } - }; - -} - -#endif /* _ALLOCATORIDENTIFICATION_H */ - diff --git a/include/dsa/CallTargets.h b/include/dsa/CallTargets.h deleted file mode 100644 index 7130340d7..000000000 --- a/include/dsa/CallTargets.h +++ /dev/null @@ -1,74 +0,0 @@ -//=- llvm/Analysis/CallTargets.h - Resolve Indirect Call Targets --*- C++ -*-=// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This pass uses DSA to map targets of all calls, and reports on if it -// thinks it knows all targets of a given call. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_ANALYSIS_CALLTARGETS_H -#define LLVM_ANALYSIS_CALLTARGETS_H - -#include "llvm/Pass.h" -#include "llvm/IR/CallSite.h" -#include "dsa/DataStructure.h" - -#include -#include -#include - -using namespace llvm; -namespace dsa{ - - template - class CallTargetFinder : public ModulePass { - std::map > IndMap; - std::set CompleteSites; - std::list AllSites; - - void findIndTargets(Module &M); - public: - static char ID; - CallTargetFinder() : ModulePass(ID) {} - - virtual bool runOnModule(Module &M); - - virtual void getAnalysisUsage(AnalysisUsage &AU) const; - - virtual void print(llvm::raw_ostream &O, const Module *M) const; - - // Given a CallSite, get an iterator of callees - std::vector::iterator begin(CallSite cs){ - return IndMap[cs].begin(); - } - std::vector::iterator end(CallSite cs){ - return IndMap[cs].end(); - } - unsigned size(CallSite cs){ - return IndMap[cs].size(); - } - - // Iterate over CallSites in program - std::list::iterator cs_begin(){ - return AllSites.begin(); - } - std::list::iterator cs_end(){ - return AllSites.end(); - } - - // Do we think we have complete knowledge of this site? - // That is, do we think there are no missing callees - bool isComplete(CallSite cs) const { - return CompleteSites.find(cs) != CompleteSites.end(); - } - }; - -} - -#endif diff --git a/include/dsa/DSCallGraph.h b/include/dsa/DSCallGraph.h deleted file mode 100644 index cc4d3572a..000000000 --- a/include/dsa/DSCallGraph.h +++ /dev/null @@ -1,210 +0,0 @@ -//===- DSCallGaph.h - Build call graphs -------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Implement a detailed call graph for DSA. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_DSCALLGRAPH_H -#define LLVM_DSCALLGRAPH_H - -#include "dsa/svset.h" -#include "dsa/keyiterator.h" - -#include -#include "llvm/ADT/EquivalenceClasses.h" -#include "llvm/IR/CallSite.h" - -#include -#include - -class DSCallGraph { -public: - typedef svset FuncSet; - typedef std::map ActualCalleesTy; - typedef std::map SimpleCalleesTy; - -private: - // ActualCallees contains CallSite -> set of Function mappings - ActualCalleesTy ActualCallees; - - // SimpleCallees contains Function -> set of Functions mappings - SimpleCalleesTy SimpleCallees; - - // These are used for returning empty sets when the caller has no callees - FuncSet EmptyActual; - FuncSet EmptySimple; - - // An equivalence class is exactly an SCC - llvm::EquivalenceClasses SCCs; - - // Functions we know about that aren't called - svset knownRoots; - - // Functions that might be called from an incomplete - // unresolved call site. - svset IncompleteCalleeSet; - - svset completeCS; - - // Types for SCC construction - typedef std::map TFMap; - typedef std::vector TFStack; - - // Tarjan's SCC algorithm - unsigned tarjan_rec(const llvm::Function* F, TFStack& Stack, unsigned &NextID, - TFMap& ValMap); - - void removeECFunctions(); - -public: - - DSCallGraph() {} - - typedef ActualCalleesTy::mapped_type::const_iterator callee_iterator; - typedef KeyIterator callee_key_iterator; - typedef SimpleCalleesTy::mapped_type::const_iterator flat_iterator; - typedef KeyIterator flat_key_iterator; - typedef FuncSet::const_iterator root_iterator; - typedef llvm::EquivalenceClasses::member_iterator scc_iterator; - - void insert(llvm::CallSite CS, const llvm::Function* F); - - void insureEntry(const llvm::Function* F); - - template - void insert(llvm::CallSite CS, Iterator _begin, Iterator _end) { - for (; _begin != _end; ++_begin) - insert(CS, *_begin); - } - - callee_iterator callee_begin(llvm::CallSite CS) const { - ActualCalleesTy::const_iterator ii = ActualCallees.find(CS); - if (ii == ActualCallees.end()) - return EmptyActual.end(); - return ii->second.begin(); - } - - callee_iterator callee_end(llvm::CallSite CS) const { - ActualCalleesTy::const_iterator ii = ActualCallees.find(CS); - if (ii == ActualCallees.end()) - return EmptyActual.end(); - return ii->second.end(); - } - - callee_key_iterator key_begin() const { - return callee_key_iterator(ActualCallees.begin()); - } - - callee_key_iterator key_end() const { - return callee_key_iterator(ActualCallees.end()); - } - - flat_iterator flat_callee_begin(const llvm::Function* F) const { - SimpleCalleesTy::const_iterator ii = SimpleCallees.find(F); - if (ii == SimpleCallees.end()) - return EmptySimple.end(); - return ii->second.begin(); - } - - flat_iterator flat_callee_end(const llvm::Function* F) const { - SimpleCalleesTy::const_iterator ii = SimpleCallees.find(F); - if (ii == SimpleCallees.end()) - return EmptySimple.end(); - return ii->second.end(); - } - - flat_key_iterator flat_key_begin() const { - return flat_key_iterator(SimpleCallees.begin()); - } - - flat_key_iterator flat_key_end() const { - return flat_key_iterator(SimpleCallees.end()); - } - - root_iterator root_begin() const { - return knownRoots.begin(); - } - - root_iterator root_end() const { - return knownRoots.end(); - } - - scc_iterator scc_begin(const llvm::Function* F) const { - assert(F == SCCs.getLeaderValue(F) && "Requested non-leader"); - return SCCs.member_begin(SCCs.findValue(F)); - } - - scc_iterator scc_end(const llvm::Function* F) const { - assert(F == SCCs.getLeaderValue(F) && "Requested non-leader"); - return SCCs.member_end(); - } - - const llvm::Function* sccLeader(const llvm::Function*F) const { - return SCCs.getLeaderValue(F); - } - unsigned callee_size(llvm::CallSite CS) const { - ActualCalleesTy::const_iterator ii = ActualCallees.find(CS); - if (ii == ActualCallees.end()) - return 0; - return ii->second.size(); - } - - bool called_from_incomplete_site(const llvm::Function *F) const { - return !(IncompleteCalleeSet.find(F) - == IncompleteCalleeSet.end()); - } - void callee_mark_complete(llvm::CallSite CS) { - completeCS.insert(CS); - } - - bool callee_is_complete(llvm::CallSite CS) const { - return completeCS.find(CS) != completeCS.end(); - } - - unsigned size() const { - unsigned sum = 0; - for (ActualCalleesTy::const_iterator ii = ActualCallees.begin(), - ee = ActualCallees.end(); ii != ee; ++ii) - sum += ii->second.size(); - return sum; - } - - unsigned flat_size() const { - return SimpleCallees.size(); - } - - void buildSCCs(); - - void buildRoots(); - - void buildIncompleteCalleeSet(svset callees); - - void addFullFunctionSet(llvm::CallSite CS, svset &Set) const; - // Temporary compat wrapper - void addFullFunctionList(llvm::CallSite CS, std::vector &List) const { - svset Set; - addFullFunctionSet(CS, Set); - List.insert(List.end(), Set.begin(), Set.end()); - } - - void dump() const; - - void assertSCCRoot(const llvm::Function* F) { - assert(F == SCCs.getLeaderValue(F) && "Not Leader?"); - } - - // common helper; no good reason for it to be here rather than elsewhere - static bool hasPointers(const llvm::Function* F); - static bool hasPointers(llvm::CallSite& CS); - -}; - -#endif /* LLVM_DSCALLGRAPH_H */ - diff --git a/include/dsa/DSGraph.h b/include/dsa/DSGraph.h deleted file mode 100644 index 6b0a47e87..000000000 --- a/include/dsa/DSGraph.h +++ /dev/null @@ -1,714 +0,0 @@ -//===- DSGraph.h - Represent a collection of data structures ----*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This header defines the data structure graph (DSGraph) and the -// ReachabilityCloner class. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_ANALYSIS_DSGRAPH_H -#define LLVM_ANALYSIS_DSGRAPH_H - -#include "dsa/DSNode.h" -#include "dsa/DSCallGraph.h" -#include "llvm/ADT/EquivalenceClasses.h" -#include "llvm/IR/Function.h" - -#include -#include -#include - -namespace llvm { - -class DataLayout; -class GlobalValue; - -//===----------------------------------------------------------------------===// -/// DSScalarMap - An instance of this class is used to keep track of all of -/// which DSNode each scalar in a function points to. This is specialized to -/// keep track of globals with nodes in the function, and to keep track of the -/// unique DSNodeHandle being used by the scalar map. -/// -/// This class is crucial to the efficiency of DSA with some large SCC's. In -/// these cases, the cost of iterating over the scalar map dominates the cost -/// of DSA. In all of these cases, the DSA phase is really trying to identify -/// globals or unique node handles active in the function. -/// -class DSScalarMap { - typedef std::map ValueMapTy; - ValueMapTy ValueMap; - - typedef std::set GlobalSetTy; - GlobalSetTy GlobalSet; - - EquivalenceClasses &GlobalECs; -public: - DSScalarMap(EquivalenceClasses &ECs) : GlobalECs(ECs) {} - - EquivalenceClasses &getGlobalECs() const { return GlobalECs; } - - // Compatibility methods: provide an interface compatible with a map of - // Value* to DSNodeHandle's. - typedef ValueMapTy::const_iterator const_iterator; - typedef ValueMapTy::iterator iterator; - iterator begin() { return ValueMap.begin(); } - iterator end() { return ValueMap.end(); } - const_iterator begin() const { return ValueMap.begin(); } - const_iterator end() const { return ValueMap.end(); } - - const GlobalValue *getLeaderForGlobal(const GlobalValue *GV) const { - EquivalenceClasses::iterator ECI = GlobalECs.findValue(GV); - if (ECI == GlobalECs.end()) return GV; - return *GlobalECs.findLeader(ECI); - } - - - iterator find(const Value *V) { - assert(V); - iterator I = ValueMap.find(V); - if (I != ValueMap.end()) return I; - - if (const GlobalValue *GV = dyn_cast(V)) { - // If this is a global, check to see if it is equivalenced to something - // in the map. - const GlobalValue *Leader = getLeaderForGlobal(GV); - if (Leader != GV) - I = ValueMap.find((const Value*)Leader); - } - return I; - } - const_iterator find(const Value *V) const { - assert(V); - const_iterator I = ValueMap.find(V); - if (I != ValueMap.end()) return I; - - if (const GlobalValue *GV = dyn_cast(V)) { - // If this is a global, check to see if it is equivalenced to something - // in the map. - const GlobalValue *Leader = getLeaderForGlobal(GV); - if (Leader != GV) - I = ValueMap.find((const Value*)Leader); - } - return I; - } - - /// getRawEntryRef - This method can be used by clients that are aware of the - /// global value equivalence class in effect. - DSNodeHandle &getRawEntryRef(const Value *V) { - std::pair IP = - ValueMap.insert(std::make_pair(V, DSNodeHandle())); - if (IP.second) // Inserted the new entry into the map. - if (const GlobalValue *GV = dyn_cast(V)) - GlobalSet.insert(GV); - return IP.first->second; - } - - unsigned count(const Value *V) const { return ValueMap.find(V) != ValueMap.end(); } - - void erase(const Value *V) { erase(ValueMap.find(V)); } - - void eraseIfExists(const Value *V) { - iterator I = find(V); - if (I != end()) erase(I); - } - - /// replaceScalar - When an instruction needs to be modified, this method can - /// be used to update the scalar map to remove the old and insert the new. - /// - void replaceScalar(const Value *Old, const Value *New) { - iterator I = find(Old); - assert(I != end() && "Old value is not in the map!"); - ValueMap.insert(std::make_pair(New, I->second)); - erase(I); - } - - /// copyScalarIfExists - If Old exists in the scalar map, make New point to - /// whatever Old did. - void copyScalarIfExists(const Value *Old, const Value *New) { - iterator I = find(Old); - if (I != end()) - ValueMap.insert(std::make_pair(New, I->second)); - } - - /// operator[] - Return the DSNodeHandle for the specified value, creating a - /// new null handle if there is no entry yet. - DSNodeHandle &operator[](const Value *V) { - assert(V); - iterator I = ValueMap.find(V); - if (I != ValueMap.end()) - return I->second; // Return value if already exists. - - if (const GlobalValue *GV = dyn_cast(V)) - return AddGlobal(GV); - - return ValueMap.insert(std::make_pair(V, DSNodeHandle())).first->second; - } - - void erase(iterator I) { - assert(I != ValueMap.end() && "Cannot erase end!"); - if (const GlobalValue *GV = dyn_cast(I->first)) - GlobalSet.erase(GV); - ValueMap.erase(I); - } - - void clear_scalars() { - for(iterator ii = begin(); ii != end(); ) - if (isa(ii->first)) - ++ii; - else { - iterator next = ii; - ++ii; - erase(next); - } - } - - void clear() { - ValueMap.clear(); - GlobalSet.clear(); - } - - /// spliceFrom - Copy all entries from RHS, then clear RHS. - /// - void spliceFrom(DSScalarMap &RHS); - - // Access to the global set: the set of all globals currently in the - // scalar map. - typedef GlobalSetTy::const_iterator global_iterator; - global_iterator global_begin() const { return GlobalSet.begin(); } - global_iterator global_end() const { return GlobalSet.end(); } - unsigned global_size() const { return GlobalSet.size(); } - unsigned global_count(const GlobalValue *GV) const { return GlobalSet.count(GV); } -private: - DSNodeHandle &AddGlobal(const GlobalValue *GV); -}; - -//===----------------------------------------------------------------------===// -/// DSGraph - The graph that represents a function. -/// -class DSGraph { -public: - // Public data-type declarations... - typedef DSScalarMap ScalarMapTy; - typedef std::map ReturnNodesTy; - typedef std::map VANodesTy; - typedef ilist NodeListTy; - - /// NodeMapTy - This data type is used when cloning one graph into another to - /// keep track of the correspondence between the nodes in the old and new - /// graphs. - typedef std::map NodeMapTy; - - // InvNodeMapTy - This data type is used to represent the inverse of a node - // map. - typedef std::multimap InvNodeMapTy; - typedef std::list FunctionListTy; -private: - DSGraph *GlobalsGraph; // Pointer to the common graph of global objects - - // This is use to differentiate between Local and the rest of the passes. - // Local should use the FunctionCalls vector that has all the DSCallSites. - // All other passes, shoud use the Aux calls vector, as they process and - // subsequently might remove some DSCall Sites. Once those call sites - // have been resolved, we must not revisit them again. - // UseAuxCalls is false for Local. And true for the other passes. - - bool UseAuxCalls; // Should this pass use the Aux calls vector? - - NodeListTy Nodes; - ScalarMapTy ScalarMap; - - // ReturnNodes - A return value for every function merged into this graph. - // Each DSGraph may have multiple functions merged into it at any time, which - // is used for representing SCCs. - // - ReturnNodesTy ReturnNodes; - - // VANodes - Special "Variable Argument" Node for each function - // - VANodesTy VANodes; - - // FunctionCalls - This list maintains a single entry for each call - // instruction in the current graph. The first entry in the vector is the - // scalar that holds the return value for the call, the second is the function - // scalar being invoked, and the rest are pointer arguments to the function. - // This vector is built by the Local graph and is never modified after that. - // - FunctionListTy FunctionCalls; - - // AuxFunctionCalls - This vector contains call sites that have been processed - // by some mechanism. In pratice, the BU Analysis uses this vector to hold - // the _unresolved_ call sites, because it cannot modify FunctionCalls. - // - FunctionListTy AuxFunctionCalls; - - /// TD - This is the target data object for the machine this graph is - /// constructed for. - const DataLayout &TD; - - SuperSet& TypeSS; - - void operator=(const DSGraph &); // DO NOT IMPLEMENT - DSGraph(const DSGraph&); // DO NOT IMPLEMENT -public: - // Create a new, empty, DSGraph. - DSGraph(EquivalenceClasses &ECs, const DataLayout &td, - SuperSet& tss, - DSGraph *GG = 0) - :GlobalsGraph(GG), UseAuxCalls(false), - ScalarMap(ECs), TD(td), TypeSS(tss) - { } - - // Copy ctor - If you want to capture the node mapping between the source and - // destination graph, you may optionally do this by specifying a map to record - // this into. - // - // Note that a copied graph does not retain the GlobalsGraph pointer of the - // source. You need to set a new GlobalsGraph with the setGlobalsGraph - // method. - // - DSGraph( DSGraph* DSG, EquivalenceClasses &ECs, - SuperSet& tss, - unsigned CloneFlags = 0); - ~DSGraph(); - - DSGraph *getGlobalsGraph() const { return GlobalsGraph; } - void setGlobalsGraph(DSGraph *G) { GlobalsGraph = G; } - - /// getGlobalECs - Return the set of equivalence classes that the global - /// variables in the program form. - EquivalenceClasses &getGlobalECs() const { - return ScalarMap.getGlobalECs(); - } - - SuperSet& getTypeSS() const { - return TypeSS; - } - - /// getDataLayout - Return the DataLayout object for the current target. - /// - const DataLayout &getDataLayout() const { return TD; } - - /// setUseAuxCalls - If you call this method, the auxillary call vector will - /// be used instead of the standard call vector to the dot file. - /// - void setUseAuxCalls() { UseAuxCalls = true; } - bool shouldUseAuxCalls() const { return UseAuxCalls; } - - /// node_iterator/begin/end - Iterate over all of the nodes in the graph. Be - /// extremely careful with these methods because any merging of nodes could - /// cause the node to be removed from this list. This means that if you are - /// iterating over nodes and doing something that could cause _any_ node to - /// merge, your node_iterators into this graph can be invalidated. - typedef NodeListTy::iterator node_iterator; - node_iterator node_begin() { return Nodes.begin(); } - node_iterator node_end() { return Nodes.end(); } - - typedef NodeListTy::const_iterator node_const_iterator; - node_const_iterator node_begin() const { return Nodes.begin(); } - node_const_iterator node_end() const { return Nodes.end(); } - - /// getFunctionNames - Return a space separated list of the name of the - /// functions in this graph (if any) - /// - std::string getFunctionNames() const; - - /// addNode - Add a new node to the graph. - /// - void addNode(DSNode *N) { Nodes.push_back(N); } - void unlinkNode(DSNode *N) { Nodes.remove(N); } - - /// getScalarMap - Get a map that describes what the nodes the scalars in this - /// function point to... - /// - ScalarMapTy &getScalarMap() { return ScalarMap; } - const ScalarMapTy &getScalarMap() const { return ScalarMap; } - - /// getFunctionCalls - Return the list of call sites in the original local - /// graph... - /// - const FunctionListTy &getFunctionCalls() const { return FunctionCalls;} - FunctionListTy &getFunctionCalls() { return FunctionCalls;} - - /// getAuxFunctionCalls - Get the call sites as modified by whatever passes - /// have been run. - /// - FunctionListTy &getAuxFunctionCalls() { return AuxFunctionCalls; } - const FunctionListTy &getAuxFunctionCalls() const { - return AuxFunctionCalls; - } - - // addAuxFunctionCall - Add a call site to the AuxFunctionCallList - void addAuxFunctionCall(DSCallSite D) { AuxFunctionCalls.push_back(D); } - - void buildCallGraph(DSCallGraph& DCG, std::vector &GlobalFunctionList, bool filter) const; - void buildCompleteCallGraph(DSCallGraph& DCG, std::vector &GlobalFunctionList, bool filter) const; - - /// removeFunction - Specify that all call sites to the function have been - /// fully specified by a pass such as StdLibPass. - void removeFunctionCalls(Function& F); - - // Function Call iteration - typedef FunctionListTy::const_iterator fc_iterator; - fc_iterator fc_begin() const { return FunctionCalls.begin(); } - fc_iterator fc_end() const { return FunctionCalls.end(); } - - - // Aux Function Call iteration - typedef FunctionListTy::iterator afc_iterator; - afc_iterator afc_begin() { return AuxFunctionCalls.begin(); } - afc_iterator afc_end() { return AuxFunctionCalls.end(); } - typedef FunctionListTy::const_iterator afc_const_iterator; - afc_const_iterator afc_begin() const { return AuxFunctionCalls.begin(); } - afc_const_iterator afc_end() const { return AuxFunctionCalls.end(); } - - /// getNodeForValue - Given a value that is used or defined in the body of the - /// current function, return the DSNode that it points to. - /// - DSNodeHandle &getNodeForValue(const Value *V) { return ScalarMap[V]; } - - const DSNodeHandle &getNodeForValue(const Value *V) const { - ScalarMapTy::const_iterator I = ScalarMap.find(V); - assert(I != ScalarMap.end() && - "Use non-const lookup function if node may not be in the map"); - return I->second; - } - - bool hasNodeForValue(const Value* V) const { - ScalarMapTy::const_iterator I = ScalarMap.find(V); - return I != ScalarMap.end(); - } - - void eraseNodeForValue(const Value* V) { - ScalarMap.erase(V); - } - - /// retnodes_* iterator methods: expose iteration over return nodes in the - /// graph, which are also the set of functions incorporated in this graph. - typedef ReturnNodesTy::const_iterator retnodes_iterator; - retnodes_iterator retnodes_begin() const { return ReturnNodes.begin(); } - retnodes_iterator retnodes_end() const { return ReturnNodes.end(); } - - typedef VANodesTy::const_iterator vanodes_iterator; - vanodes_iterator vanodes_begin() const { return VANodes.begin(); } - vanodes_iterator vanodes_end() const { return VANodes.end(); } - - - /// getReturnNodes - Return the mapping of functions to their return nodes for - /// this graph. - /// - const ReturnNodesTy &getReturnNodes() const { return ReturnNodes; } - ReturnNodesTy &getReturnNodes() { return ReturnNodes; } - - /// getReturnNodeFor - Return the return node for the specified function. - /// - DSNodeHandle &getReturnNodeFor(const Function &F) { - ReturnNodesTy::iterator I = ReturnNodes.find(&F); - assert(I != ReturnNodes.end() && "F not in this DSGraph!"); - return I->second; - } - - const DSNodeHandle &getReturnNodeFor(const Function &F) const { - ReturnNodesTy::const_iterator I = ReturnNodes.find(&F); - assert(I != ReturnNodes.end() && "F not in this DSGraph!"); - return I->second; - } - - /// getVANodes - Return the mapping of functions to their var-arg nodes for - /// this graph. - /// - const VANodesTy &getVANodes() const { return VANodes; } - VANodesTy &getVANodes() { return VANodes; } - - /// getVANodeFor - Return the var-arg node for the specified function. - /// - DSNodeHandle &getVANodeFor(const Function &F) { - VANodesTy::iterator I = VANodes.find(&F); - assert(I != VANodes.end() && "Var-arg info for F not in this graph!"); - return I->second; - } - - const DSNodeHandle &getVANodeFor(const Function &F) const { - VANodesTy::const_iterator I = VANodes.find(&F); - assert(I != VANodes.end() && "Var-arg info for F not in this graph!"); - return I->second; - } - - DSNodeHandle& getOrCreateReturnNodeFor(const Function& F) { - return ReturnNodes[&F]; - } - - DSNodeHandle& getOrCreateVANodeFor(const Function& F) { - return VANodes[&F]; - } - - /// containsFunction - Return true if this DSGraph contains information for - /// the specified function. - bool containsFunction(const Function *F) const { - return ReturnNodes.count(F); - } - - /// getGraphSize - Return the number of nodes in this graph. - /// - unsigned getGraphSize() const { - return Nodes.size(); - } - - /// addObjectToGraph - This method can be used to add global, stack, and heap - /// objects to the graph. This can be used when updating DSGraphs due to the - /// introduction of new temporary objects. The new object is not pointed to - /// and does not point to any other objects in the graph. Note that this - /// method initializes the type of the DSNode to the declared type of the - /// object if UseDeclaredType is true, otherwise it leaves the node type as - /// void. - DSNode *addObjectToGraph(Value *Ptr, bool UseDeclaredType = true); - - - /// print - Print a dot graph to the specified ostream... - /// - void print(llvm::raw_ostream &O) const; - - /// dump - call print(cerr), for use from the debugger... - /// - void dump() const; - - /// viewGraph - Emit a dot graph, run 'dot', run gv on the postscript file, - /// then cleanup. For use from the debugger. - /// - void viewGraph() const; - - void writeGraphToFile(llvm::raw_ostream &O, const std::string &GraphName) const; - - /// maskNodeTypes - Apply a mask to all of the node types in the graph. This - /// is useful for clearing out markers like Incomplete. - /// - void maskNodeTypes(unsigned Mask) { - for (node_iterator I = node_begin(), E = node_end(); I != E; ++I) - I->maskNodeTypes(Mask); - } - void maskIncompleteMarkers() { maskNodeTypes(~DSNode::IncompleteNode); } - - // markIncompleteNodes - Traverse the graph, identifying nodes that may be - // modified by other functions that have not been resolved yet. This marks - // nodes that are reachable through three sources of "unknownness": - // Global Variables, Function Calls, and Incoming Arguments - // - // For any node that may have unknown components (because something outside - // the scope of current analysis may have modified it), the 'Incomplete' flag - // is added to the NodeType. - // - enum MarkIncompleteFlags { - MarkFormalArgs = 1, IgnoreFormalArgs = 0, - IgnoreGlobals = 2, MarkGlobalsIncomplete = 0, - MarkVAStart = 4 - }; - void markIncompleteNodes(unsigned Flags); - - // Mark nodes that have overlapping Int and Pointer types. - void computeIntPtrFlags(); - - // Mark all reachable from external as external. - enum ComputeExternalFlags { - MarkFormalsExternal = 1, DontMarkFormalsExternal = 0, - ProcessCallSites = 2, IgnoreCallSites = 0, - ResetExternal = 4, DontResetExternal = 0 - }; - void computeExternalFlags(unsigned Flags); - - // removeDeadNodes - Use a reachability analysis to eliminate subgraphs that - // are unreachable. This often occurs because the data structure doesn't - // "escape" into it's caller, and thus should be eliminated from the caller's - // graph entirely. This is only appropriate to use when inlining graphs. - // - enum RemoveDeadNodesFlags { - RemoveUnreachableGlobals = 1, KeepUnreachableGlobals = 0 - }; - void removeDeadNodes(unsigned Flags); - - /// CloneFlags enum - Bits that may be passed into the cloneInto method to - /// specify how to clone the function graph. - enum CloneFlags { - StripAllocaBit = 1 << 0, KeepAllocaBit = 0, - DontCloneCallNodes = 1 << 1, CloneCallNodes = 0, - DontCloneAuxCallNodes = 1 << 2, CloneAuxCallNodes = 0, - StripModRefBits = 1 << 3, KeepModRefBits = 0, - StripIncompleteBit = 1 << 4, KeepIncompleteBit = 0 - }; - - void updateFromGlobalGraph(); - - /// computeNodeMapping - Given roots in two different DSGraphs, traverse the - /// nodes reachable from the two graphs, computing the mapping of nodes from - /// the first to the second graph. - /// - static void computeNodeMapping(const DSNodeHandle &NH1, - const DSNodeHandle &NH2, NodeMapTy &NodeMap, - bool StrictChecking = true); - - /// computeGToGGMapping - Compute the mapping of nodes in the graph to nodes - /// in the globals graph. - void computeGToGGMapping(NodeMapTy &NodeMap); - - /// computeGGToGMapping - Compute the mapping of nodes in the global - /// graph to nodes in this graph. - void computeGGToGMapping(InvNodeMapTy &InvNodeMap); - - /// computeCalleeCallerMapping - Given a call from a function in the current - /// graph to the 'Callee' function (which lives in 'CalleeGraph'), compute the - /// mapping of nodes from the callee to nodes in the caller. - void computeCalleeCallerMapping(DSCallSite CS, const Function &Callee, - DSGraph &CalleeGraph, NodeMapTy &NodeMap); - - /// spliceFrom - Logically perform the operation of cloning the RHS graph into - /// this graph, then clearing the RHS graph. Instead of performing this as - /// two seperate operations, do it as a single, much faster, one. - /// - void spliceFrom(DSGraph* RHS); - - /// cloneInto - Clone the specified DSGraph into the current graph. - /// - /// The CloneFlags member controls various aspects of the cloning process. - /// - void cloneInto(DSGraph* G, unsigned CloneFlags = 0); - - /// getFunctionArgumentsForCall - Given a function that is currently in this - /// graph, return the DSNodeHandles that correspond to the pointer-compatible - /// function arguments. The vector is filled in with the return value (or - /// null if it is not pointer compatible), followed by all of the - /// pointer-compatible arguments. - void getFunctionArgumentsForCall(const Function *F, - std::vector &Args) const; - - /// mergeInGraph - This graph merges in the minimal number of - /// nodes from G2 into 'this' graph, merging the bindings specified by the - /// call site (in this graph) with the bindings specified by the vector in G2. - /// If the StripAlloca's argument is 'StripAllocaBit' then Alloca markers are - /// removed from nodes. - /// - void mergeInGraph(const DSCallSite &CS, std::vector &Args, - const DSGraph &G2, unsigned CloneFlags); - - /// mergeInGraph - This method is the same as the above method, but the - /// argument bindings are provided by using the formal arguments of F. - /// - void mergeInGraph(const DSCallSite &CS, const Function &F, - const DSGraph &Graph, unsigned CloneFlags); - - /// getCallSiteForArguments - Get the arguments and return value bindings for - /// the specified function in the current graph. - /// - DSCallSite getCallSiteForArguments(const Function &F) const; - - /// getDSCallSiteForCallSite - Given an LLVM CallSite object that is live in - /// the context of this graph, return the DSCallSite for it. - DSCallSite getDSCallSiteForCallSite(CallSite CS) const; - - // Methods for checking to make sure graphs are well formed... - void AssertNodeInGraph(const DSNode *N) const { - assert((!N || N->getParentGraph() == this) && - "AssertNodeInGraph: Node is not in graph!"); - } - void AssertNodeContainsGlobal(const DSNode *N, const GlobalValue *GV) const; - - void AssertCallSiteInGraph(const DSCallSite &CS) const; - void AssertCallNodesInGraph() const; - void AssertAuxCallNodesInGraph() const; - - void AssertGraphOK() const; - - /// removeTriviallyDeadNodes - After the graph has been constructed, this - /// method removes all unreachable nodes that are created because they got - /// merged with other nodes in the graph. This is used as the first step of - /// removeDeadNodes. - /// - void removeTriviallyDeadNodes(); -}; - - -/// ReachabilityCloner - This class is used to incrementally clone and merge -/// nodes from a non-changing source graph into a potentially mutating -/// destination graph. Nodes are only cloned over on demand, either in -/// responds to a merge() or getClonedNH() call. When a node is cloned over, -/// all of the nodes reachable from it are automatically brought over as well. -/// -class ReachabilityCloner { - DSGraph* Dest; - const DSGraph* Src; - - /// BitsToKeep - These bits are retained from the source node when the - /// source nodes are merged into the destination graph. - unsigned BitsToKeep; - unsigned CloneFlags; - - bool createDest; - - // NodeMap - A mapping from nodes in the source graph to the nodes that - // represent them in the destination graph. - // We cannot use a densemap here as references into it are not stable across - // insertion - typedef std::map RCNodeMap; - RCNodeMap NodeMap; - -public: - ReachabilityCloner(DSGraph* dest, const DSGraph* src, unsigned cloneFlags, - bool _createDest = true) - : Dest(dest), Src(src), CloneFlags(cloneFlags), createDest(_createDest) { - assert(Dest != Src && "Cannot clone from graph to same graph!"); - BitsToKeep = ~DSNode::DeadNode; - if (CloneFlags & DSGraph::StripAllocaBit) - BitsToKeep &= ~DSNode::AllocaNode; - if (CloneFlags & DSGraph::StripModRefBits) - BitsToKeep &= ~(DSNode::ModifiedNode | DSNode::ReadNode); - if (CloneFlags & DSGraph::StripIncompleteBit) - BitsToKeep &= ~DSNode::IncompleteNode; - } - - DSNodeHandle getClonedNH(const DSNodeHandle &SrcNH); - - void merge(const DSNodeHandle &NH, const DSNodeHandle &SrcNH); - - /// mergeCallSite - Merge the nodes reachable from the specified src call - /// site into the nodes reachable from DestCS. - /// - void mergeCallSite(DSCallSite &DestCS, const DSCallSite &SrcCS); - - DSCallSite cloneCallSite(const DSCallSite& SrcCS); - - bool clonedAnyNodes() const { return !NodeMap.empty(); } - - /// hasClonedNode - Return true if the specified node has been cloned from - /// the source graph into the destination graph. - bool hasClonedNode(const DSNode *N) { - return NodeMap.count(N); - } - - void destroy() { NodeMap.clear(); } -}; - -// -// Function: functionIsCallable() -// -// Description: -// Determine whether the specified function can be a target of the specified -// call site. We allow the user to configure what we consider to be -// uncallable at an indirect function call site. -// -// Inputs: -// CS - The call site which calls the function. -// F - The function that is potentially called by CS. -// -// Return value: -// true - The function F can be called by the call site. -// false - The function F cannot be called by the call site. -// -bool -functionIsCallable (ImmutableCallSite CS, const Function* F); - -} // End llvm namespace - -#endif diff --git a/include/dsa/DSGraphTraits.h b/include/dsa/DSGraphTraits.h deleted file mode 100644 index 050284a73..000000000 --- a/include/dsa/DSGraphTraits.h +++ /dev/null @@ -1,145 +0,0 @@ -//===- DSGraphTraits.h - Provide generic graph interface --------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file provides GraphTraits specializations for the DataStructure graph -// nodes, allowing datastructure graphs to be processed by generic graph -// algorithms. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_ANALYSIS_DSGRAPHTRAITS_H -#define LLVM_ANALYSIS_DSGRAPHTRAITS_H - -#include "dsa/DSGraph.h" -#include "llvm/ADT/GraphTraits.h" -#include "llvm/ADT/STLExtras.h" - -#include - -namespace llvm { - -namespace bugfix { - -// XXX: There's a bug in llvm::pointer_iterator. The iterator_category -// parameter to iterator_adaptor_base is omitted. We can remove this once it is fixed. -// http://llvm.org/doxygen/iterator_8h_source.html#l00311 -template ())> -class pointer_iterator - : public iterator_adaptor_base, - WrappedIteratorT, - typename std::iterator_traits::iterator_category, - T> { - mutable T Ptr; - -public: - pointer_iterator() = default; - - explicit pointer_iterator(WrappedIteratorT u) - : pointer_iterator::iterator_adaptor_base(std::move(u)) {} - - T &operator*() { return Ptr = &*this->I; } - const T &operator*() const { return Ptr = &*this->I; } -}; - -} // End bugfix namespace - -template -class DSNodeIterator : public std::iterator { - friend class DSNode; - - DSNode::const_edge_iterator NH; - - typedef DSNodeIterator _Self; - - DSNodeIterator(NodeTy *N) : NH(N->edge_begin()) {} // begin iterator - DSNodeIterator(NodeTy *N, bool) : NH(N->edge_end()) {} // Create end iterator - -public: -// DSNodeIterator(const DSNodeHandle &NH) -// : Node(NH.getNode()), Offset(NH.getOffset()) {} - - bool operator==(const _Self& x) const { - return NH == x.NH; - } - bool operator!=(const _Self& x) const { return !operator==(x); } - - const _Self &operator=(const _Self &I) { - NH = I.NH; - return *this; - } - - pointer operator*() const { - return NH->second.getNode(); - } - pointer operator->() const { return operator*(); } - - _Self& operator++() { // Preincrement - ++NH; - return *this; - } - _Self operator++(int) { // Postincrement - _Self tmp = *this; ++*this; return tmp; - } - - unsigned getOffset() const { return NH->first; } -}; - -// Provide iterators for DSNode... -inline DSNode::iterator DSNode::begin() { - return DSNode::iterator(this); -} -inline DSNode::iterator DSNode::end() { - return DSNode::iterator(this, false); -} -inline DSNode::const_iterator DSNode::begin() const { - return DSNode::const_iterator(this); -} -inline DSNode::const_iterator DSNode::end() const { - return DSNode::const_iterator(this, false); -} - -template <> struct GraphTraits { - typedef DSNode* NodeRef; - typedef DSNode::iterator ChildIteratorType; - - static NodeRef getEntryNode(NodeRef N) { return N; } - static ChildIteratorType child_begin(NodeRef N) { return N->begin(); } - static ChildIteratorType child_end(NodeRef N) { return N->end(); } -}; - -template <> struct GraphTraits { - typedef const DSNode* NodeRef; - typedef DSNode::const_iterator ChildIteratorType; - - static NodeRef getEntryNode(NodeRef N) { return N; } - static ChildIteratorType child_begin(NodeRef N) { return N->begin(); } - static ChildIteratorType child_end(NodeRef N) { return N->end(); } -}; - -template <> struct GraphTraits { - typedef const DSNode* NodeRef; - typedef DSNode::const_iterator ChildIteratorType; - - // nodes_iterator/begin/end - Allow iteration over all nodes in the graph - typedef bugfix::pointer_iterator nodes_iterator; - static nodes_iterator nodes_begin(const DSGraph *G) { - return nodes_iterator(G->node_begin()); - } - static nodes_iterator nodes_end(const DSGraph *G) { - return nodes_iterator(G->node_end()); - } - - static ChildIteratorType child_begin(NodeRef N) { return N->begin(); } - static ChildIteratorType child_end(NodeRef N) { return N->end(); } -}; - -} // End llvm namespace - -#endif diff --git a/include/dsa/DSMonitor.h b/include/dsa/DSMonitor.h deleted file mode 100644 index 8519f95f4..000000000 --- a/include/dsa/DSMonitor.h +++ /dev/null @@ -1,29 +0,0 @@ - -#ifndef LLVM_ANALYSIS_DSMONITOR_H -#define LLVM_ANALYSIS_DSMONITOR_H - -#include "dsa/DataStructure.h" -#include "dsa/DSSupport.h" - -namespace llvm { - -class DSMonitor { - DSNodeHandle N; - std::string caption; - std::vector VS; - std::string message; - std::string location; - - void unwatch(); - -public: - DSMonitor() { } - void watch(DSNodeHandle N, std::vector VS, std::string M = ""); - void warn(); - void witness(DSNodeHandle N, std::vector VS, std::string M = ""); - void check(); -}; - -} - -#endif diff --git a/include/dsa/DSNode.h b/include/dsa/DSNode.h deleted file mode 100644 index 180cd1c20..000000000 --- a/include/dsa/DSNode.h +++ /dev/null @@ -1,540 +0,0 @@ -//===- DSNode.h - Node definition for datastructure graphs ------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Data structure graph nodes and some implementation of DSNodeHandle. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_ANALYSIS_DSNODE_H -#define LLVM_ANALYSIS_DSNODE_H - -#include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/ilist_node.h" -#include "llvm/ADT/ilist.h" -#include "llvm/Support/raw_ostream.h" -#include "dsa/svset.h" -#include "dsa/super_set.h" -#include "dsa/keyiterator.h" -#include "dsa/DSSupport.h" - -#include -#include - -namespace llvm { - -template -class DSNodeIterator; // Data structure graph traversal iterator - -//===----------------------------------------------------------------------===// -/// DSNode - Data structure node class -/// -/// This class represents an untyped memory object of Size bytes. It keeps -/// track of any pointers that have been stored into the object as well as the -/// different types represented in this object. -/// -class DSNode : public ilist_node { -public: - typedef std::map::setPtr> TyMapTy; - typedef std::map LinkMapTy; - -private: - //Sentinel - DSNode() : NumReferrers(0), Size(0), NodeType(0) {} - - /// NumReferrers - The number of DSNodeHandles pointing to this node... if - /// this is a forwarding node, then this is the number of node handles which - /// are still forwarding over us. - /// - unsigned NumReferrers; - - /// ForwardNH - This NodeHandle contain the node (and offset into the node) - /// that this node really is. When nodes get folded together, the node to be - /// eliminated has these fields filled in, otherwise ForwardNH.getNode() is - /// null. - /// - DSNodeHandle ForwardNH; - - /// Size - The current size of the node. This should be equal to the size of - /// the current type record. - /// - unsigned Size; - - /// ParentGraph - The graph this node is currently embedded into. - /// - DSGraph *ParentGraph; - - /// TyMap - Keep track of the loadable types and offsets those types are seen - // at. - TyMapTy TyMap; - - /// Links - Contains one entry for every byte in this memory - /// object. - /// - LinkMapTy Links; - - /// Globals - The list of global values that are merged into this node. - /// - svset Globals; - - void operator=(const DSNode &); // DO NOT IMPLEMENT - DSNode(const DSNode &); // DO NOT IMPLEMENT -public: - enum NodeTy { - ShadowNode = 0, // Nothing is known about this node... - AllocaNode = 1 << 0, // This node was allocated with alloca - HeapNode = 1 << 1, // This node was allocated with malloc - GlobalNode = 1 << 2, // This node was allocated by a global var decl - ExternFuncNode = 1 << 3, // This node contains external functions - ExternGlobalNode = 1 << 4, // This node contains external globals - UnknownNode = 1 << 5, // This node points to unknown allocated memory - IncompleteNode = 1 << 6, // This node may not be complete - - ModifiedNode = 1 << 7, // This node is modified in this context - ReadNode = 1 << 8, // This node is read in this context - - ArrayNode = 1 << 9, // This node is treated like an array - CollapsedNode = 1 << 10, // This node is collapsed - ExternalNode = 1 << 11, // This node comes from an external source - IntToPtrNode = 1 << 12, // This node comes from an int cast - // and is used in pointer operations - // like geps, loads, stores - PtrToIntNode = 1 << 13, // This node escapes to an int cast - // and DSA does not track it further. - VAStartNode = 1 << 14, // This node is from a vastart call - - //#ifndef NDEBUG - DeadNode = 1 << 15, // This node is dead and should not be pointed to - //#endif - - Composition = AllocaNode | HeapNode | GlobalNode | UnknownNode - }; - - /// NodeType - A union of the above bits. "Shadow" nodes do not add any flags - /// to the nodes in the data structure graph, so it is possible to have nodes - /// with a value of 0 for their NodeType. - /// -private: - unsigned short NodeType; -public: - - /// DSNode ctor - Create a node of the specified type, inserting it into the - /// specified graph. - /// - explicit DSNode(DSGraph *G); - - /// DSNode "copy ctor" - Copy the specified node, inserting it into the - /// specified graph. If NullLinks is true, then null out all of the links, - /// but keep the same number of them. This can be used for efficiency if the - /// links are just going to be clobbered anyway. - /// - DSNode(const DSNode &, DSGraph *G, bool NullLinks = false); - ~DSNode(); - - // Iterator for graph interface... Defined in DSGraphTraits.h - typedef DSNodeIterator iterator; - typedef DSNodeIterator const_iterator; - inline iterator begin(); - inline iterator end(); - inline const_iterator begin() const; - inline const_iterator end() const; - - /// type_* - Provide iterators for accessing types. Some types may be null - typedef TyMapTy::iterator type_iterator; - typedef TyMapTy::const_iterator const_type_iterator; - type_iterator type_begin() { return TyMap.begin(); } - type_iterator type_end() { return TyMap.end(); } - const_type_iterator type_begin() const { return TyMap.begin(); } - const_type_iterator type_end() const { return TyMap.end(); } - - /// edge_* - Provide iterators for accessing outgoing edges. Some outgoing - /// edges may be null. - typedef LinkMapTy::iterator edge_iterator; - typedef LinkMapTy::const_iterator const_edge_iterator; - edge_iterator edge_begin() { return Links.begin(); } - edge_iterator edge_end() { return Links.end(); } - const_edge_iterator edge_begin() const { return Links.begin(); } - const_edge_iterator edge_end() const { return Links.end(); } - - //===-------------------------------------------------- - // Accessors - - /// getSize - Return the maximum number of bytes occupied by this object... - /// - unsigned getSize() const { return Size; } - - /// hasNoReferrers - Return true if nothing is pointing to this node at all. - /// - bool hasNoReferrers() const { return getNumReferrers() == 0; } - - /// getNumReferrers - This method returns the number of referrers to the - /// current node. Note that if this node is a forwarding node, this will - /// return the number of nodes forwarding over the node! - unsigned getNumReferrers() const { return NumReferrers; } - - - DSGraph *getParentGraph() const { return ParentGraph; } - void setParentGraph(DSGraph *G) { ParentGraph = G; } - - /// getForwardNode - This method returns the node that this node is forwarded - /// to, if any. - /// - DSNode *getForwardNode() const { return ForwardNH.getNode(); } - - /// isForwarding - Return true if this node is forwarding to another. - /// - bool isForwarding() const { return !ForwardNH.isNull(); } - - /// stopForwarding - When the last reference to this forwarding node has been - /// dropped, delete the node. - /// - void stopForwarding() { - assert(isForwarding() && - "Node isn't forwarding, cannot stopForwarding()!"); - ForwardNH.setTo(0, 0); - assert(ParentGraph == 0 && - "Forwarding nodes must have been removed from graph!"); - delete this; - } - - void growSize(unsigned NSize) { - assert(NSize >= Size && "Cannot shrink"); - assert(!isCollapsedNode() && "Growing a collapsed node"); - Size = NSize; - } - - void growSizeForType(Type *Ty, unsigned Offset); - - /// hasLink - Return true if this memory object has a link in slot LinkNo - /// - bool hasLink(unsigned Offset) const { - assert(Offset < getSize() && "Link index is out of range!"); - return Links.find(Offset) != Links.end(); - } - - /// getLink - Return the link at the specified offset. - /// - DSNodeHandle &getLink(unsigned Offset) { - assert(Offset < getSize() && "Link index is out of range!"); - assert(!isForwarding() && "Link on a forwarding node"); - return Links[Offset]; - } - const DSNodeHandle &getLink(unsigned Offset) const { - assert(hasLink(Offset) && "No Link"); - assert(!isForwarding() && "Link on a forwarding node"); - return Links.find(Offset)->second; - } - - //unsigned getNumLinks() const { -// assert(!isForwarding() && "Link on a forwarding node"); -// return Links.size(); -// } - - /// mergeTypeInfo - This method merges the specified type into the current - /// node at the specified offset. This may update the current node's type - /// record if this gives more information to the node, it may do nothing to - /// the node if this information is already known, or it may merge the node - /// completely (and return true) if the information is incompatible with what - /// is already known. - /// - void mergeTypeInfo(Type *Ty, unsigned Offset); - void mergeTypeInfo(const TyMapTy::mapped_type TyIt, unsigned Offset); - void mergeTypeInfo(const DSNode* D, unsigned Offset); - - // Types records might exist without types in them - bool hasNoType() { - type_iterator ii = type_begin(), ee = type_end(); - while (ii != ee) { - if (ii->second) return false; - ++ii; - } - return true; - } - - /// markIntPtrFlags - If the node at any offset has overlapping int/ptr types - /// mark the P2I flags. - /// - void markIntPtrFlags(); - - /// foldNodeCompletely - If we determine that this node has some funny - /// behavior happening to it that we cannot represent, we fold it down to a - /// single, completely pessimistic, node. This node is represented as a - /// single byte with a single TypeEntry of "void" with isArray = true. - /// - void foldNodeCompletely(); - - /// isNodeCompletelyFolded - Return true if this node has been completely - /// folded down to something that can never be expanded, effectively losing - /// all of the field sensitivity that may be present in the node. - /// - bool isNodeCompletelyFolded() const; - - /// setLink - Set the link at the specified offset to the specified - /// NodeHandle, replacing what was there. It is uncommon to use this method, - /// instead one of the higher level methods should be used, below. - /// - void setLink(unsigned Offset, const DSNodeHandle &NH) { - assert(Offset < getSize() && "Link index is out of range!"); - Links[Offset] = NH; - } - - /// addEdgeTo - Add an edge from the current node to the specified node. This - /// can cause merging of nodes in the graph. - /// - void addEdgeTo(unsigned Offset, const DSNodeHandle &NH); - - /// mergeWith - Merge this node and the specified node, moving all links to - /// and from the argument node into the current node, deleting the node - /// argument. Offset indicates what offset the specified node is to be merged - /// into the current node. - /// - /// The specified node may be a null pointer (in which case, nothing happens). - /// - void mergeWith(const DSNodeHandle &NH, unsigned Offset); - - /// addGlobal - Add an entry for a global value to the Globals list. This - /// also marks the node with the 'G' flag if it does not already have it. - /// - void addGlobal(const GlobalValue *GV); - - void addFunction(const Function* F); - - /// removeGlobal - Remove the specified global that is explicitly in the - /// globals list. - void removeGlobal(const GlobalValue *GV); - - void mergeGlobals(const DSNode& RHS); - void clearGlobals() { Globals.clear(); } - - bool isEmptyGlobals() const { return Globals.empty(); } - unsigned numGlobals() const { return Globals.size(); } - - /// addFullGlobalSet - Compute the full set of global values that are - /// represented by this node. Unlike getGlobalsList(), this requires fair - /// amount of work to compute, so don't treat this method call as free. - void addFullGlobalsSet(svset &Set) const; - /// Same as above, keeping for compat reasons - void addFullGlobalsList(std::vector &List) const { - svset Set; - addFullGlobalsSet(Set); - List.insert(List.end(), Set.begin(), Set.end()); - } - - /// addFullFunctionSet - Identical to addFullGlobalsSet, but only return the - /// functions in the full list. - void addFullFunctionSet(svset &Set) const; - /// Same as above, keeping for compat reasons - void addFullFunctionList(std::vector &List) const { - svset Set; - addFullFunctionSet(Set); - List.insert(List.end(), Set.begin(), Set.end()); - } - /// Same as above, only doesn't include duplicates - /// (keeping both for compat with existing clients) - - /// globals_iterator/begin/end - Provide iteration methods over the global - /// value leaders set that is merged into this node. Like the getGlobalsList - /// method, these iterators do not return globals that are part of the - /// equivalence classes for globals in this node, but aren't leaders. - typedef svset::const_iterator globals_iterator; - globals_iterator globals_begin() const { return Globals.begin(); } - globals_iterator globals_end() const { return Globals.end(); } - - /// addValueList - Compute a full set of values that are represented by - /// this node. High overhead method. - void addValueList(std::vector &List) const; - - /// maskNodeTypes - Apply a mask to the node types bitfield. - /// - void maskNodeTypes(unsigned Mask) { - NodeType &= Mask; - } - - void mergeNodeFlags(unsigned RHS) { - NodeType |= RHS; - } - - /// getNodeFlags - Return all of the flags set on the node. If the DEAD flag - /// is set, hide it from the caller. - /// - unsigned getNodeFlags() const { return NodeType & ~DeadNode; } - - /// clearNodeFlags - Useful for completely resetting a node, - /// used in external recognizers - DSNode* clearNodeFlags() { NodeType = 0; return this; } - - bool isAllocaNode() const { return NodeType & AllocaNode; } - bool isHeapNode() const { return NodeType & HeapNode; } - bool isGlobalNode() const { return NodeType & GlobalNode; } - bool isExternFuncNode() const { return NodeType & ExternFuncNode; } - bool isUnknownNode() const { return NodeType & UnknownNode; } - bool isModifiedNode() const { return NodeType & ModifiedNode; } - bool isReadNode() const { return NodeType & ReadNode; } - bool isArrayNode() const { return NodeType & ArrayNode; } - bool isCollapsedNode() const { return NodeType & CollapsedNode; } - bool isIncompleteNode() const { return NodeType & IncompleteNode;} - bool isCompleteNode() const { return !isIncompleteNode(); } - bool isDeadNode() const { return NodeType & DeadNode; } - bool isExternalNode() const { return NodeType & ExternalNode; } - bool isIntToPtrNode() const { return NodeType & IntToPtrNode; } - bool isPtrToIntNode() const { return NodeType & PtrToIntNode; } - bool isVAStartNode() const { return NodeType & VAStartNode; } - - DSNode* setAllocaMarker() { NodeType |= AllocaNode; return this; } - DSNode* setHeapMarker() { NodeType |= HeapNode; return this; } - DSNode* setGlobalMarker() { NodeType |= GlobalNode; return this; } - DSNode* setExternFuncMarker() { NodeType |= ExternFuncNode; return this; } - DSNode* setExternGlobalMarker() { NodeType |= ExternGlobalNode; return this; } - DSNode* setUnknownMarker() { NodeType |= UnknownNode; return this; } - DSNode* setModifiedMarker() { NodeType |= ModifiedNode; return this; } - DSNode* setReadMarker() { NodeType |= ReadNode; return this; } - DSNode* setArrayMarker() { NodeType |= ArrayNode; return this; } - DSNode* setCollapsedMarker() { NodeType |= CollapsedNode; return this; } - DSNode* setIncompleteMarker() { NodeType |= IncompleteNode; return this; } - DSNode* setExternalMarker() { NodeType |= ExternalNode; return this; } - DSNode* setIntToPtrMarker() { NodeType |= IntToPtrNode; return this; } - DSNode* setPtrToIntMarker() { NodeType |= PtrToIntNode; return this; } - DSNode* setVAStartMarker() { NodeType |= VAStartNode; return this; } - - void makeNodeDead() { - Globals.clear(); - assert(hasNoReferrers() && "Dead node shouldn't have refs!"); - NodeType = DeadNode; - } - - /// forwardNode - Mark this node as being obsolete, and all references to it - /// should be forwarded to the specified node and offset. - /// - void forwardNode(DSNode *To, unsigned Offset); - - void cleanEdges(); - - void print(llvm::raw_ostream &O, const DSGraph *G) const; - void dump() const; - void dumpParentGraph() const; - void dumpFuncs(); - - void assertOK() const; - - void dropAllReferences() { - Links.clear(); - TyMap.clear(); - if (isForwarding()) - ForwardNH.setTo(0, 0); - } - - /// remapLinks - Change all of the Links in the current node according to the - /// specified mapping. - /// - void remapLinks(std::map &OldNodeMap); - - /// markReachableNodes - This method recursively traverses the specified - /// DSNodes, marking any nodes which are reachable. All reachable nodes it - /// adds to the set, which allows it to only traverse visited nodes once. - /// - void markReachableNodes(llvm::DenseSet &ReachableNodes) const; - - - /// checkOffsetFoldIfNeeded - Fold DSNode if the specified offset is - /// larger than its size, and the node isn't an array or forwarding. - void checkOffsetFoldIfNeeded(int Offset); -private: - friend class DSNodeHandle; - - // static mergeNodes - Helper for mergeWith() - static void MergeNodes(DSNodeHandle& CurNodeH, DSNodeHandle& NH); -}; - -//===----------------------------------------------------------------------===// -// Define inline DSNodeHandle functions that depend on the definition of DSNode -// -inline DSNode *DSNodeHandle::getNode() const { - // Disabling this assertion because it is failing on a "magic" struct - // in named (from bind). The fourth field is an array of length 0, - // presumably used to create struct instances of different sizes. - // In a variable length struct, Offset could exceed Size when getNode() - // is called before such a node is folded. In this case, the DS Analysis now - // correctly folds this node after calling getNode. - /* assert((!N || - N->isNodeCompletelyFolded() || - (N->Size == 0 && Offset == 0) || - (int(Offset) >= 0 && Offset < N->Size) || - (int(Offset) < 0 && -int(Offset) < int(N->Size)) || - N->isForwarding()) && "Node handle offset out of range!"); - */ - if (N == 0 || !N->isForwarding()) - return N; - - return HandleForwarding(); -} - -inline void DSNodeHandle::setTo(DSNode *n, unsigned NewOffset) const { - assert((!n || !n->isForwarding()) && "Cannot set node to a forwarded node!"); - if (N) getNode()->NumReferrers--; - N = n; - Offset = NewOffset; - if (N) { - N->NumReferrers++; - if (Offset >= N->Size) { - assert((Offset == 0 || N->Size == 1) && - "Pointer to non-collapsed node with invalid offset!"); - Offset = 0; - } - } - assert(!N || ((N->NodeType & DSNode::DeadNode) == 0)); - assert((!N || Offset < N->Size || (N->Size == 0 && Offset == 0) || - N->isForwarding()) && "Node handle offset out of range!"); -} - -inline bool DSNodeHandle::hasLink(unsigned Num) const { - assert(N && "DSNodeHandle does not point to a node yet!"); - return getNode()->hasLink(Num+Offset); -} - - -/// getLink - Treat this current node pointer as a pointer to a structure of -/// some sort. This method will return the pointer a mem[this+Num] -/// -inline const DSNodeHandle &DSNodeHandle::getLink(unsigned Off) const { - assert(N && "DSNodeHandle does not point to a node yet!"); - return getNode()->getLink(Offset+Off); -} -inline DSNodeHandle &DSNodeHandle::getLink(unsigned Off) { - assert(N && "DSNodeHandle does not point to a node yet!"); - return getNode()->getLink(Off+Offset); -} - -inline void DSNodeHandle::setLink(unsigned Off, const DSNodeHandle &NH) { - assert(N && "DSNodeHandle does not point to a node yet!"); - getNode()->setLink(Off+Offset, NH); -} - -/// addEdgeTo - Add an edge from the current node to the specified node. This -/// can cause merging of nodes in the graph. -/// -inline void DSNodeHandle::addEdgeTo(unsigned Off, const DSNodeHandle &NH) { - assert(N && "DSNodeHandle does not point to a node yet!"); - getNode()->addEdgeTo(Off+Offset, NH); -} - -/// mergeWith - Merge the logical node pointed to by 'this' with the node -/// pointed to by 'N'. -/// -inline void DSNodeHandle::mergeWith(const DSNodeHandle &NH) const { - if (!isNull()) - getNode()->mergeWith(NH, Offset); - else { // No node to merge with, so just point to Node - Offset = 0; - DSNode *N = NH.getNode(); - setTo(N, NH.getOffset()); - } -} - -} // End llvm namespace - -#endif diff --git a/include/dsa/DSSupport.h b/include/dsa/DSSupport.h deleted file mode 100644 index 8d58bb434..000000000 --- a/include/dsa/DSSupport.h +++ /dev/null @@ -1,380 +0,0 @@ -//===- DSSupport.h - Support for datastructure graphs -----------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Support for graph nodes, call sites, and types. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_ANALYSIS_DSSUPPORT_H -#define LLVM_ANALYSIS_DSSUPPORT_H - -#include -#include -#include -#include - -#include "llvm/ADT/DenseSet.h" -#include "llvm/IR/CallSite.h" - -namespace llvm { - -class Function; -class FunctionType; -class CallInst; -class Value; -class GlobalValue; -class Type; - -class DSNode; // Each node in the graph -class DSGraph; // A graph for a function -class ReachabilityCloner; - - /// isPointerType - Return true if this first class type is big enough to hold - /// a pointer. - /// - bool isPointerType(Type *Ty); - -//===----------------------------------------------------------------------===// -/// DSNodeHandle - Implement a "handle" to a data structure node that takes care -/// of all of the add/un'refing of the node to prevent the backpointers in the -/// graph from getting out of date. This class represents a "pointer" in the -/// graph, whose destination is an indexed offset into a node. -/// -/// Note: some functions that are marked as inline in DSNodeHandle are actually -/// defined in DSNode.h because they need knowledge of DSNode operation. Putting -/// them in a CPP file wouldn't help making them inlined and keeping DSNode and -/// DSNodeHandle (and friends) in one file complicates things. -/// -class DSNodeHandle { - mutable DSNode *N; - mutable unsigned Offset; - void operator==(const DSNode *N); // DISALLOW, use to promote N to nodehandle -public: - - DSNodeHandle() : N(0), Offset(0) {} - - // Allow construction, destruction, and assignment... - DSNodeHandle(DSNode *n, unsigned offs = 0) : N(0), Offset(0) { - setTo(n, offs); - } - DSNodeHandle(const DSNodeHandle &H) : N(0), Offset(0) { - DSNode *NN = H.getNode(); - setTo(NN, H.Offset); // Must read offset AFTER the getNode() - } - ~DSNodeHandle() { setTo(0, 0); } - DSNodeHandle &operator=(const DSNodeHandle &H) { - if (&H == this) return *this; // Don't set offset to 0 if self assigning. - DSNode *NN = H.getNode(); // Call getNode() before .Offset - setTo(NN, H.Offset); - return *this; - } - - bool operator<(const DSNodeHandle &H) const { // Allow sorting - return getNode() < H.getNode() || (N == H.N && Offset < H.Offset); - } - bool operator>(const DSNodeHandle &H) const { return H < *this; } - bool operator==(const DSNodeHandle &H) const { // Allow comparison - // getNode can change the offset, so we must call getNode() first. - return getNode() == H.getNode() && Offset == H.Offset; - } - bool operator!=(const DSNodeHandle &H) const { return !operator==(H); } - - inline void swap(DSNodeHandle &NH) { - std::swap(Offset, NH.Offset); - std::swap(N, NH.N); - } - - /// isNull - Check to see if getNode() == 0, without going through the trouble - /// of checking to see if we are forwarding... - /// - bool isNull() const { return N == 0; } - - // Allow explicit conversion to DSNode... - DSNode *getNode() const; // Defined inline in DSNode.h - unsigned getOffset() const { - getNode(); - assert(!isForwarding() && "This is a forwarding NH, call getNode() first!"); - return Offset; - } - - void setOffset(unsigned O) { - assert(!isForwarding() && "This is a forwarding NH, call getNode() first!"); - //assert((!N || Offset < N->Size || (N->Size == 0 && Offset == 0) || - // !N->ForwardNH.isNull()) && "Node handle offset out of range!"); - //assert((!N || O < N->Size || (N->Size == 0 && O == 0) || - // !N->ForwardNH.isNull()) && "Node handle offset out of range!"); - Offset = O; - } - - void setTo(DSNode *N, unsigned O) const; // Defined inline in DSNode.h - - void addEdgeTo(unsigned LinkNo, const DSNodeHandle &N); - void addEdgeTo(const DSNodeHandle &N) { addEdgeTo(0, N); } - - /// mergeWith - Merge the logical node pointed to by 'this' with the node - /// pointed to by 'N'. - /// - void mergeWith(const DSNodeHandle &N) const; - - /// hasLink - Return true if there is a link at the specified offset... - /// - inline bool hasLink(unsigned Num) const; - - /// getLink - Treat this current node pointer as a pointer to a structure of - /// some sort. This method will return the pointer a mem[this+Num] - /// - inline const DSNodeHandle &getLink(unsigned Num) const; - inline DSNodeHandle &getLink(unsigned Num); - - inline void setLink(unsigned Num, const DSNodeHandle &NH); -private: - DSNode *HandleForwarding() const; - - /// isForwarding - Return true if this NodeHandle is forwarding to another - /// one. - bool isForwarding() const; -}; - -} // End llvm namespace - -namespace std { - template<> - inline void swap(llvm::DSNodeHandle &NH1, llvm::DSNodeHandle &NH2) { NH1.swap(NH2); } -} - -namespace llvm { - -//===----------------------------------------------------------------------===// -/// DSCallSite - Representation of a call site via its call instruction, -/// the DSNode handle for the callee function (or function pointer), and -/// the DSNode handles for the function arguments. -/// -class DSCallSite { -public: - typedef std::set MappedSites_t; -private: - CallSite Site; // Actual call site - const Function *CalleeF; // The function called (direct call) - DSNodeHandle CalleeN; // The function node called (indirect call) - DSNodeHandle RetVal; // Returned value - DSNodeHandle VarArgVal; // Merged var-arg val - std::vector CallArgs; // The pointer arguments - MappedSites_t MappedSites; // The merged callsites - - static void InitNH(DSNodeHandle &NH, const DSNodeHandle &Src, - const std::map &NodeMap) { - if (DSNode *N = Src.getNode()) { - std::map::const_iterator I = NodeMap.find(N); - assert(I != NodeMap.end() && "Node not in mapping!"); - NH.setTo(I->second, Src.getOffset()); - } - } - - static void InitNH(DSNodeHandle &NH, const DSNodeHandle &Src, - const std::map &NodeMap) { - if (DSNode *N = Src.getNode()) { - std::map::const_iterator I = NodeMap.find(N); - assert(I != NodeMap.end() && "Node not in mapping!"); - - DSNode *NN = I->second.getNode(); // Call getNode before getOffset() - NH.setTo(NN, Src.getOffset()+I->second.getOffset()); - } - } - - static void InitNH(DSNodeHandle &NH, const DSNodeHandle &Src, - ReachabilityCloner &RC); - - - DSCallSite(); // DO NOT IMPLEMENT -public: - /// Constructor. Note - This ctor destroys the argument vector passed in. On - /// exit, the argument vector is empty. - /// - DSCallSite(CallSite CS, const DSNodeHandle &rv, const DSNodeHandle &va, - DSNode *Callee, std::vector &Args) - : Site(CS), CalleeF(0), CalleeN(Callee), RetVal(rv), VarArgVal(va) { - assert(Callee && "Null callee node specified for call site!"); - Args.swap(CallArgs); - } - DSCallSite(CallSite CS, const DSNodeHandle &rv, const DSNodeHandle &va, - const Function *Callee, std::vector &Args) - : Site(CS), CalleeF(Callee), RetVal(rv), VarArgVal(va) { - assert(Callee && "Null callee function specified for call site!"); - Args.swap(CallArgs); - } - - DSCallSite(const DSCallSite &DSCS) // Simple copy ctor - : Site(DSCS.Site), CalleeF(DSCS.CalleeF), CalleeN(DSCS.CalleeN), - RetVal(DSCS.RetVal), VarArgVal(DSCS.VarArgVal), - CallArgs(DSCS.CallArgs), MappedSites(DSCS.MappedSites) {} - - /// Mapping copy constructor - This constructor takes a preexisting call site - /// to copy plus a map that specifies how the links should be transformed. - /// This is useful when moving a call site from one graph to another. - /// - template - DSCallSite(const DSCallSite &FromCall, MapTy &NodeMap) { - Site = FromCall.Site; - InitNH(RetVal, FromCall.RetVal, NodeMap); - InitNH(CalleeN, FromCall.CalleeN, NodeMap); - InitNH(VarArgVal, FromCall.VarArgVal, NodeMap); - CalleeF = FromCall.CalleeF; - - CallArgs.resize(FromCall.CallArgs.size()); - for (unsigned i = 0, e = FromCall.CallArgs.size(); i != e; ++i) - InitNH(CallArgs[i], FromCall.CallArgs[i], NodeMap); - MappedSites = FromCall.MappedSites; - } - - const DSCallSite &operator=(const DSCallSite &RHS) { - Site = RHS.Site; - CalleeF = RHS.CalleeF; - CalleeN = RHS.CalleeN; - RetVal = RHS.RetVal; - VarArgVal = RHS.VarArgVal; - CallArgs = RHS.CallArgs; - MappedSites = RHS.MappedSites; - return *this; - } - - /// isDirectCall - Return true if this call site is a direct call of the - /// function specified by getCalleeFunc. If not, it is an indirect call to - /// the node specified by getCalleeNode. - /// - bool isDirectCall() const { return CalleeF != 0; } - bool isIndirectCall() const { return !isDirectCall(); } - - - // Accessor functions... - const Function &getCaller() const; - CallSite getCallSite() const { return Site; } - DSNodeHandle &getRetVal() { return RetVal; } - const DSNodeHandle &getRetVal() const { return RetVal; } - DSNodeHandle &getVAVal() { return VarArgVal; } - const DSNodeHandle &getVAVal() const { return VarArgVal; } - - DSNode *getCalleeNode() const { - assert(!CalleeF && CalleeN.getNode()); return CalleeN.getNode(); - } - const Function *getCalleeFunc() const { - assert(!CalleeN.getNode() && CalleeF); return CalleeF; - } - - unsigned getNumPtrArgs() const { return CallArgs.size(); } - - unsigned getNumMappedSites() const { return MappedSites.size(); } - - DSNodeHandle &getPtrArg(unsigned i) { - assert(i < CallArgs.size() && "Argument to getPtrArgNode is out of range!"); - return CallArgs[i]; - } - const DSNodeHandle &getPtrArg(unsigned i) const { - assert(i < CallArgs.size() && "Argument to getPtrArgNode is out of range!"); - return CallArgs[i]; - } - - - const MappedSites_t::iterator ms_begin() const { return MappedSites.begin(); } - const MappedSites_t::iterator ms_end() const { return MappedSites.end(); } - - void addPtrArg(const DSNodeHandle &NH) { - CallArgs.push_back(NH); - } - - void swap(DSCallSite &CS) { - if (this != &CS) { - std::swap(Site, CS.Site); - std::swap(RetVal, CS.RetVal); - std::swap(VarArgVal, CS.VarArgVal); - std::swap(CalleeN, CS.CalleeN); - std::swap(CalleeF, CS.CalleeF); - std::swap(CallArgs, CS.CallArgs); - std::swap(MappedSites, CS.MappedSites); - } - } - - /// mergeWith - Merge the return value and parameters of the these two call - /// sites. - /// - void mergeWith(DSCallSite &CS) { - getRetVal().mergeWith(CS.getRetVal()); - getVAVal().mergeWith(CS.getVAVal()); - unsigned MinArgs = getNumPtrArgs(); - if (CS.getNumPtrArgs() < MinArgs) MinArgs = CS.getNumPtrArgs(); - - for (unsigned a = 0; a != MinArgs; ++a) - getPtrArg(a).mergeWith(CS.getPtrArg(a)); - - for (unsigned a = MinArgs, e = CS.getNumPtrArgs(); a != e; ++a) - CallArgs.push_back(CS.getPtrArg(a)); - - MappedSites.insert(CS.getCallSite()); - MappedSites.insert(CS.ms_begin(), CS.ms_end()); - } - - /// markReachableNodes - This method recursively traverses the specified - /// DSNodes, marking any nodes which are reachable. All reachable nodes it - /// adds to the set, which allows it to only traverse visited nodes once. - /// - void markReachableNodes(DenseSet &Nodes) const; - - bool operator<(const DSCallSite &CS) const { - if (isDirectCall()) { // This must sort by callee first! - if (CS.isIndirectCall()) return true; - if (CalleeF < CS.CalleeF) return true; - if (CalleeF > CS.CalleeF) return false; - } else { - if (CS.isDirectCall()) return false; - if (CalleeN < CS.CalleeN) return true; - if (CalleeN > CS.CalleeN) return false; - } - if (RetVal < CS.RetVal) return true; - if (RetVal > CS.RetVal) return false; - if (VarArgVal < CS.VarArgVal) return true; - if (VarArgVal > CS.VarArgVal) return false; - return CallArgs < CS.CallArgs; - } - - bool operator==(const DSCallSite &CS) const { - return CalleeF == CS.CalleeF && CalleeN == CS.CalleeN && - RetVal == CS.RetVal && CallArgs == CS.CallArgs && - VarArgVal == CS.VarArgVal; - } - - bool operator==(DSCallSite &CS) { - return CalleeF == CS.CalleeF && CalleeN == CS.CalleeN && - RetVal == CS.RetVal && CallArgs == CS.CallArgs && - VarArgVal == CS.VarArgVal; - } - - /// FunctionTypeOfCallSite - Helper method to extract the signature of a function - /// that is called a given CallSite - /// - static const FunctionType *FunctionTypeOfCallSite(const CallSite & Site); - - /// isVarArg - Determines if the call this represents is to a variable argument - /// function - /// - bool isVarArg() const; - - /// isUnresolvable - Determines if this call has properties that would - /// prevent it from ever being resolvded. Put another way, no amount - /// additional information will make this callsite resolvable. - /// - bool isUnresolvable() const; -}; - -} // End llvm namespace - -namespace std { - template<> - inline void swap(llvm::DSCallSite &CS1, - llvm::DSCallSite &CS2) { CS1.swap(CS2); } -} -#endif diff --git a/include/dsa/DataStructure.h b/include/dsa/DataStructure.h deleted file mode 100644 index 5586417e9..000000000 --- a/include/dsa/DataStructure.h +++ /dev/null @@ -1,403 +0,0 @@ -//===- DataStructure.h - Build data structure graphs ------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Implement the LLVM data structure analysis library. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_ANALYSIS_DATA_STRUCTURE_H -#define LLVM_ANALYSIS_DATA_STRUCTURE_H - -#include "dsa/DSCallGraph.h" -#include "dsa/svset.h" -#include "dsa/super_set.h" -#include "dsa/AddressTakenAnalysis.h" -#include "dsa/AllocatorIdentification.h" - -#include "llvm/Pass.h" -#include "llvm/IR/DataLayout.h" -#include "llvm/IR/CallSite.h" -#include "llvm/ADT/EquivalenceClasses.h" -#include "llvm/ADT/DenseSet.h" - -#include - -namespace llvm { - -class Type; -class Instruction; -class GlobalValue; -class DSGraph; -class DSCallSite; -class DSNode; -class DSNodeHandle; - -FunctionPass *createDataStructureStatsPass(); -FunctionPass *createDataStructureGraphCheckerPass(); - -class DataStructures : public ModulePass { - typedef std::map DSInfoTy; - - /// DataLayout, comes in handy - const DataLayout* TD; - - /// Pass to get Graphs from - DataStructures* GraphSource; - - /// Do we clone Graphs or steal them? - bool Clone; - - /// do we reset the aux list to the func list? - bool resetAuxCalls; - - /// Were are DSGraphs stolen by another pass? - bool DSGraphsStolen; - - void buildGlobalECs(svset& ECGlobals); - - void eliminateUsesOfECGlobals(DSGraph& G, const svset &ECGlobals); - - // DSInfo, one graph for each function - DSInfoTy DSInfo; - - // Name for printing - const char* printname; - -protected: - - /// The Globals Graph contains all information on the globals - DSGraph *GlobalsGraph; - - /// GlobalECs - The equivalence classes for each global value that is merged - /// with other global values in the DSGraphs. - EquivalenceClasses GlobalECs; - - SuperSet* TypeSS; - - // Callgraph, as computed so far - DSCallGraph callgraph; - // List of all address taken functions. - // This is used as target, of indirect calls for any indirect call site with // incomplete callee node. - std::vector GlobalFunctionList; - - void init(DataStructures* D, bool clone, bool useAuxCalls, bool copyGlobalAuxCalls, bool resetAux); - void init(const DataLayout* T); - - void formGlobalECs(); - - void cloneIntoGlobals(DSGraph* G, unsigned cloneFlags); - void cloneGlobalsInto(DSGraph* G, unsigned cloneFlags); - - void restoreCorrectCallGraph(); - - void formGlobalFunctionList(); - - DataStructures(char & id, const char* name) - : ModulePass(id), TD(0), GraphSource(0), printname(name), GlobalsGraph(0) { - // For now, the graphs are owned by this pass - DSGraphsStolen = false; - } - -public: - /// print - Print out the analysis results... - /// - void print(llvm::raw_ostream &O, const Module *M) const; - void dumpCallGraph() const; - - /// handleTest - Handles various user-specified testing options. - /// Returns true iff the user specified for us to test something. - /// - bool handleTest(llvm::raw_ostream &O, const Module *M) const; - - virtual void releaseMemory(); - - virtual bool hasDSGraph(const Function &F) const { - return DSInfo.find(&F) != DSInfo.end(); - } - - /// getDSGraph - Return the data structure graph for the specified function. - /// - virtual DSGraph *getDSGraph(const Function &F) const { - std::map::const_iterator I = DSInfo.find(&F); - assert(I != DSInfo.end() && "Function not in module!"); - return I->second; - } - - void setDSGraph(const Function& F, DSGraph* G) { - DSInfo[&F] = G; - } - - DSGraph* getOrCreateGraph(const Function* F); - - DSGraph* getGlobalsGraph() const { return GlobalsGraph; } - - EquivalenceClasses &getGlobalECs() { return GlobalECs; } - - const DataLayout& getDataLayout() const { return *TD; } - - const DSCallGraph& getCallGraph() const { return callgraph; } - - SuperSet& getTypeSS() const { return *TypeSS; } - - /// deleteValue/copyValue - Interfaces to update the DSGraphs in the program. - /// These correspond to the interfaces defined in the AliasAnalysis class. - void deleteValue(Value *V); - void copyValue(Value *From, Value *To); -}; - -// BasicDataStructures - The analysis is a dummy one -- all pointers can points -// to all possible locations. -// -class BasicDataStructures : public DataStructures { -public: - static char ID; - BasicDataStructures() : DataStructures(ID, "basic.") {} - ~BasicDataStructures() { releaseMemory(); } - - virtual bool runOnModule(Module &M); - - /// getAnalysisUsage - This obviously provides a data structure graph. - /// - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.setPreservesAll(); - } -}; - -// LocalDataStructures - The analysis that computes the local data structure -// graphs for all of the functions in the program. -// -// FIXME: This should be a Function pass that can be USED by a Pass, and would -// be automatically preserved. Until we can do that, this is a Pass. -// -class LocalDataStructures : public DataStructures { - AddressTakenAnalysis* addrAnalysis; -public: - static char ID; - LocalDataStructures() : DataStructures(ID, "local.") {} - ~LocalDataStructures() { releaseMemory(); } - - virtual bool runOnModule(Module &M); - - /// getAnalysisUsage - This obviously provides a data structure graph. - /// - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired(); - AU.setPreservesAll(); - } -}; - -// StdLibDataStructures - This analysis recognizes common standard c library -// functions and generates graphs for them. -class StdLibDataStructures : public DataStructures { - void eraseCallsTo(Function* F); - void processRuntimeCheck (Module & M, std::string name, unsigned arg); - void processFunction(int x, Function *F); - AllocIdentify *AllocWrappersAnalysis; -public: - static char ID; - StdLibDataStructures() : DataStructures(ID, "stdlib.") {} - ~StdLibDataStructures() { releaseMemory(); } - - virtual bool runOnModule(Module &M); - - /// getAnalysisUsage - This obviously provides a data structure graph. - /// - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired(); - AU.addRequired(); - AU.setPreservesAll(); - } -}; - -/// BUDataStructures - The analysis that computes the interprocedurally closed -/// data structure graphs for all of the functions in the program. This pass -/// only performs a "Bottom Up" propagation (hence the name). -/// -class BUDataStructures : public DataStructures { -protected: - - const char* debugname; - - - // filterCallees -- Whether or not we filter out illegal callees - // from the CallGraph. This is useful while doing original BU, - // but might be undesirable in other passes such as CBU/EQBU. - bool filterCallees; -public: - static char ID; - //Child constructor (CBU) - BUDataStructures(char & CID, const char* name, const char* printname, - bool filter) - : DataStructures(CID, printname), debugname(name), filterCallees(filter) {} - //main constructor - BUDataStructures() - : DataStructures(ID, "bu."), debugname("dsa-bu"), - filterCallees(true) {} - ~BUDataStructures() { releaseMemory(); } - - virtual bool runOnModule(Module &M); - - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired(); - AU.setPreservesAll(); - } - -protected: - bool runOnModuleInternal(Module &M); - -private: - // Private typedefs - typedef std::map TarjanMap; - typedef std::vector TarjanStack; - typedef svset FuncSet; - - void postOrderInline (Module & M); - unsigned calculateGraphs (const Function *F, - TarjanStack & Stack, - unsigned & NextID, - TarjanMap & ValMap); - - void calculateGraph(DSGraph* G); - - void CloneAuxIntoGlobal(DSGraph* G); - - void getAllCallees(const DSCallSite &CS, FuncSet &Callees); - void getAllAuxCallees (DSGraph* G, FuncSet &Callees); - void applyCallsiteFilter(const DSCallSite &DCS, FuncSet &Callees); -}; - -/// CompleteBUDataStructures - This is the exact same as the bottom-up graphs, -/// but we use take a completed call graph and inline all indirect callees into -/// their callers graphs, making the result more useful for things like pool -/// allocation. -/// -class CompleteBUDataStructures : public BUDataStructures { -protected: - void buildIndirectFunctionSets (void); -public: - static char ID; - CompleteBUDataStructures(char & CID = ID, - const char* name = "dsa-cbu", - const char* printname = "cbu.") - : BUDataStructures(CID, name, printname, false) {} - ~CompleteBUDataStructures() { releaseMemory(); } - - virtual bool runOnModule(Module &M); - - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired(); - AU.setPreservesAll(); - } - -}; - -/// EquivBUDataStructures - This is the same as the complete bottom-up graphs, but -/// with functions partitioned into equivalence classes and a single merged -/// DS graph for all functions in an equivalence class. After this merging, -/// graphs are inlined bottom-up on the SCCs of the final (CBU) call graph. -/// -class EquivBUDataStructures : public CompleteBUDataStructures { - void mergeGraphsByGlobalECs(); - void verifyMerging(); - -public: - static char ID; - EquivBUDataStructures() - : CompleteBUDataStructures(ID, "dsa-eq", "eq.") {} - ~EquivBUDataStructures() { releaseMemory(); } - - virtual bool runOnModule(Module &M); - - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired(); - AU.setPreservesAll(); - } - -}; - -/// TDDataStructures - Analysis that computes new data structure graphs -/// for each function using the closed graphs for the callers computed -/// by the bottom-up pass. -/// -class TDDataStructures : public DataStructures { - svset ExternallyCallable; - - /// CallerCallEdges - For a particular graph, we keep a list of these records - /// which indicates which graphs call this function and from where. - struct CallerCallEdge { - DSGraph *CallerGraph; // The graph of the caller function. - const DSCallSite *CS; // The actual call site. - const Function *CalledFunction; // The actual function being called. - - CallerCallEdge(DSGraph *G, const DSCallSite *cs, const Function *CF) - : CallerGraph(G), CS(cs), CalledFunction(CF) {} - - bool operator<(const CallerCallEdge &RHS) const { - return CallerGraph < RHS.CallerGraph || - (CallerGraph == RHS.CallerGraph && CS < RHS.CS); - } - }; - - std::map > CallerEdges; - - - // IndCallMap - We memoize the results of indirect call inlining operations - // that have multiple targets here to avoid N*M inlining. The key to the map - // is a sorted set of callee functions, the value is the DSGraph that holds - // all of the caller graphs merged together, and the DSCallSite to merge with - // the arguments for each function. - std::map, DSGraph*> IndCallMap; - - bool useEQBU; - -public: - static char ID; - TDDataStructures(char & CID = ID, const char* printname = "td.", bool useEQ = false) - : DataStructures(CID, printname), useEQBU(useEQ) {} - ~TDDataStructures(); - - virtual bool runOnModule(Module &M); - - /// getAnalysisUsage - This obviously provides a data structure graph. - /// - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - if (useEQBU) { - AU.addRequired(); - } else { - AU.addRequired(); - AU.addPreserved(); - } - AU.setPreservesAll(); - } - -private: - void markReachableFunctionsExternallyAccessible(DSNode *N, - DenseSet &Visited); - - void InlineCallersIntoGraph(DSGraph* G); - void ComputePostOrder(const Function &F, DenseSet &Visited, - std::vector &PostOrder); -}; - -/// EQTDDataStructures - Analysis that computes new data structure graphs -/// for each function using the closed graphs for the callers computed -/// by the EQ bottom-up pass. -/// -class EQTDDataStructures : public TDDataStructures { -public: - static char ID; - EQTDDataStructures() - :TDDataStructures(ID, "eqtd.", true) - {} - ~EQTDDataStructures(); -}; - -} // End llvm namespace - -#endif diff --git a/include/dsa/EntryPointAnalysis.h b/include/dsa/EntryPointAnalysis.h deleted file mode 100644 index 1b3968ba6..000000000 --- a/include/dsa/EntryPointAnalysis.h +++ /dev/null @@ -1,53 +0,0 @@ -//===-- EntryPointAnalysis.h - Entry point Finding Pass -------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This is a general way of finding entry points in a system. Simple programs -// will use the main version. Libraries and OS kernels can have more -// specialized versions. -// -//===----------------------------------------------------------------------===// - -#ifndef _ENTRYPOINTANALYSIS_H -#define _ENTRYPOINTANALYSIS_H - -namespace llvm { -class Function; -class Module; -} - -#include -#include "llvm/Pass.h" - -class EntryPointAnalysis : public llvm::ModulePass { - std::set names; - bool haveNames; -public: - static char ID; - EntryPointAnalysis(); - virtual ~EntryPointAnalysis(); - - /// print - Print out the analysis results... - /// - void print(llvm::raw_ostream &O, const llvm::Module *M) const; - - bool runOnModule(llvm::Module&); - - virtual void getAnalysisUsage(llvm::AnalysisUsage &Info) const; - - bool isEntryPoint(const llvm::Function* F) const; - - void findEntryPoints(const llvm::Module& M, - std::vector& dest) const; - -}; - - - -#endif /* _ENTRYPOINTANALYSIS_H */ - diff --git a/include/dsa/TypeSafety.h b/include/dsa/TypeSafety.h deleted file mode 100644 index b003434d2..000000000 --- a/include/dsa/TypeSafety.h +++ /dev/null @@ -1,90 +0,0 @@ -//===- TypeSafety.h - Find Type-Safe Pointers ---------------------*- C++ -*--// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This analysis pass determines which pointers within a program are used in -// a type-safe fashion. It uses DSA to determine type-consistency and -// abstracts the details of interpreting DSA's results. -// -//===----------------------------------------------------------------------===// - -#ifndef DSA_TYPESAFETY_H -#define DSA_TYPESAFETY_H - -#include "dsa/DataStructure.h" -#include "dsa/DSGraph.h" - -#include "llvm/Pass.h" - -#include -#include - -using namespace llvm; - -namespace dsa { - -// -// Pass: TypeSafety -// -// Description: -// This pass determines which pointers within a function are type-safe. It is -// used to abstract away the interpretation of the DSNode flags and fields -// for clients. -// -// Template parameters: -// dsa - The name of the DSA Pass which this pass should use. -// -template -struct TypeSafety : public ModulePass { - typedef std::unordered_map FieldMap; - typedef std::unordered_map NodeMap; - protected: - // Methods - DSNodeHandle getDSNodeHandle (const Value * V, const Function * F); - DSNodeHandle getDSNodeHandle (const GlobalValue * V); - void findTypeSafeDSNodes (const DSGraph * Graph); - bool isTypeSafe (const DSNode * N); - bool typeFieldsOverlap (const DSNode * N); - void fieldMapUpdate(const DSNode * N); - - // Pointers to prerequisite passes - const DataLayout * TD; - dsa * dsaPass; - - // Data structures - std::set TypeSafeNodes; - NodeMap NodeInfo; - - public: - static char ID; - TypeSafety() : ModulePass(ID) {} - virtual bool runOnModule (Module & M); - - virtual StringRef getPassName() const { - return "DSA Type-Safety Analysis"; - } - - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired(); - AU.setPreservesAll(); - } - - virtual void releaseMemory () { - TypeSafeNodes.clear(); - return; - } - - // Methods for clients to use - virtual bool isTypeSafe (const Value * V, const Function * F); - virtual bool isTypeSafe (const GlobalValue * V); - virtual bool isFieldDisjoint(const Value * V, const Function *F); - virtual bool isFieldDisjoint(const GlobalValue * V, unsigned offset); -}; - -} -#endif diff --git a/include/dsa/keyiterator.h b/include/dsa/keyiterator.h deleted file mode 100644 index 7f9e04782..000000000 --- a/include/dsa/keyiterator.h +++ /dev/null @@ -1,249 +0,0 @@ -//===- keyiterator.h - Iterator over just the keys in a map -----*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Implement a map key iterator -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_KEYITERATOR_H -#define LLVM_KEYITERATOR_H - -#include - -//From SGI's STL - -// select1st and select2nd are extensions: they are not part of the standard. - -template -class _Select1st : public std::unary_function<_Pair, typename _Pair::first_type> { -public: - - const typename _Pair::first_type & operator()(const _Pair& __x) const { - return __x.first; - } -}; - -template -class _Select2nd : public std::unary_function<_Pair, typename _Pair::second_type> { -public: - - const typename _Pair::second_type & operator()(const _Pair& __x) const { - return __x.second; - } -}; - -template class select1st : public _Select1st<_Pair> { -}; - -template class select2nd : public _Select2nd<_Pair> { -}; - - -// ref_mapped_iterator - This is a simple iterator adapter that causes a -// function to be dereferenced whenever operator* is invoked on the iterator. -// It is assumed that the function returns a valid reference, which differs -// from llvm::mapped_iterator from which this was derived (copied) - -template -class ref_mapped_iterator { - RootIt current; - UnaryFunc Fn; -public: - typedef typename std::iterator_traits::iterator_category - iterator_category; - typedef typename std::iterator_traits::difference_type - difference_type; - typedef typename UnaryFunc::result_type value_type; - - typedef typename UnaryFunc::result_type *pointer; - typedef typename UnaryFunc::result_type& reference; - - typedef RootIt iterator_type; - typedef ref_mapped_iterator _Self; - - inline const RootIt &getCurrent() const { - return current; - } - - inline const UnaryFunc &getFunc() const { - return Fn; - } - - inline explicit ref_mapped_iterator(const RootIt &I, UnaryFunc F) - : current(I), Fn(F) { } - - inline ref_mapped_iterator(const ref_mapped_iterator &It) - : current(It.current), Fn(It.Fn) { } - - inline reference operator*() const { // All this work to do this - return Fn(*current); // little change - } - - _Self & operator++() { - ++current; - return *this; - } - - _Self & operator--() { - --current; - return *this; - } - - _Self operator++(int) { - _Self __tmp = *this; - ++current; - return __tmp; - } - - _Self operator--(int) { - _Self __tmp = *this; - --current; - return __tmp; - } - - _Self operator+(difference_type n) const { - return _Self(current + n, Fn); - } - - _Self & operator+=(difference_type n) { - current += n; - return *this; - } - - _Self operator-(difference_type n) const { - return _Self(current - n, Fn); - } - - _Self & operator-=(difference_type n) { - current -= n; - return *this; - } - - reference operator[](difference_type n) const { - return *(*this +n); - } - - inline bool operator!=(const _Self &X) const { - return !operator==(X); - } - - inline bool operator==(const _Self &X) const { - return current == X.current; - } - - inline bool operator<(const _Self &X) const { - return current < X.current; - } - - inline difference_type operator-(const _Self &X) const { - return current - X.current; - } -}; - -template -class KeyIterator -: public ref_mapped_iterator > { - typedef select1st FunTy; -public: - - KeyIterator(const Iter I) - : ref_mapped_iterator >(I, FunTy()) { } -}; - -template -class ValueIterator -: public ref_mapped_iterator > { - typedef select2nd FunTy; -public: - - ValueIterator(const Iter I) - : ref_mapped_iterator >(I, FunTy()) { } -}; - - -#if 0 - -template -class KeyIterator { - Iter I; - -public: - typedef typename Iter::difference_type difference_type; - typedef typename Iter::value_type::first_type value_type; - typedef typename Iter::value_type::first_type* pointer; - typedef typename Iter::value_type::first_type& reference; - typedef typename Iter::iterator_category iterator_category; - - KeyIterator(Iter i) : I(i) { } - - Iter base() const { - return I; - } - - reference operator*() const { - return I->first; - } - - KeyIterator operator+(difference_type n) const { - return KeyIterator(I + n); - } - - KeyIterator & operator++() { - ++I; - return *this; - } - - KeyIterator operator++(int) { - Iter OI = I; - ++I; - return KeyIterator(OI); - } - - KeyIterator & operator+=(difference_type n) { - I += n; - return *this; - } - - KeyIterator operator-(difference_type n) const { - return KeyIterator(I - n); - } - - KeyIterator & operator--() { - --I; - return *this; - } - - KeyIterator operator--(int) { - Iter OI = I; - --I; - return KeyIterator(OI); - } - - KeyIterator & operator-=(difference_type n) { - I -= n; - return *this; - } - - pointer operator->() const { - return &I->first; - } - - bool operator==(const KeyIterator& RHS) const { - return I == RHS.I; - } - - bool operator!=(const KeyIterator& RHS) const { - return I != RHS.I; - } -}; -#endif - - -#endif /* LLVM_KEYITERATOR_H */ - diff --git a/include/dsa/stl_util.h b/include/dsa/stl_util.h deleted file mode 100644 index 9366e1af4..000000000 --- a/include/dsa/stl_util.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef _DSA_STL_UTIL_H_ -#define _DSA_STL_UTIL_H_ - -#include "llvm/ADT/ilist.h" -#include -#include -#include - -namespace llvm { - -// Splicing one container into another as efficiently as we can -template -inline void splice(std::list& Dst, std::list& Src) { - Dst.splice(Dst.end(), Src); -} -template -inline void splice(ilist& Dst, ilist& Src) { - Dst.splice(Dst.end(), Src); -} - -template -static void splice(std::vector& Dst, std::vector& Src) { - if (Dst.empty()) - Dst.swap(Src); - else { - Dst.insert(Dst.end(), Src.begin(), Src.end()); - Src.clear(); - } -} - -template -inline void splice(std::map& Dst, std::map& Src) { - if (Dst.empty()) - Dst.swap(Src); - else { - Dst.insert(Src.begin(), Src.end()); - Src.clear(); - } -} - -// Efficient sort -template -inline void sort(std::vector& L) { - std::sort(L.begin(), L.end()); -} - -template -inline void sort(std::list& L) { - L.sort(); -} - -} // end namespace llvm -#endif // _DSA_STL_UTIL_H_ - diff --git a/include/dsa/super_set.h b/include/dsa/super_set.h deleted file mode 100644 index 0d1aa2915..000000000 --- a/include/dsa/super_set.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * File: super_set.h - * Author: andrew - * - * Created on March 10, 2010, 2:04 PM - */ - -#ifndef _SUPER_SET_H -#define _SUPER_SET_H - -#include "dsa/svset.h" -#include - -// Contains stable references to a set -// The sets can be grown. - -template -class SuperSet { - //std::set provides stable iterators, and that matters a lot - typedef svset InnerSetTy; - typedef std::set OuterSetTy; - OuterSetTy container; -public: - typedef const typename OuterSetTy::value_type* setPtr; - - setPtr getOrCreate(svset& S) { - if (S.empty()) return 0; - return &(*container.insert(S).first); - } - - setPtr getOrCreate(setPtr P, Ty t) { - svset s; - if (P) - s.insert(P->begin(), P->end()); - s.insert(t); - return getOrCreate(s); - } -}; - - - -#endif /* _SUPER_SET_H */ - diff --git a/include/dsa/svset.h b/include/dsa/svset.h deleted file mode 100644 index d39eea6f9..000000000 --- a/include/dsa/svset.h +++ /dev/null @@ -1,240 +0,0 @@ -#ifndef _SV_ORDERED_SET_HH_ -#define _SV_ORDERED_SET_HH_ 1 - -#include -#include -#include - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/// A set implemented atop a sorted vector. -/// Iterators are not stable accross insert or delete -template< typename Key, - typename Compare = std::less, - typename Alloc = std::allocator > -class svset { - typedef std::vector internal_type; - -// Types -public: - - typedef Key key_type; - typedef Key value_type; - typedef Compare key_compare; - typedef Compare value_compare; - typedef Alloc allocator_type; - typedef typename Alloc::reference reference; - typedef typename Alloc::const_reference const_reference; - typedef typename Alloc::pointer pointer; - typedef typename Alloc::const_pointer const_pointer; - typedef typename internal_type::const_iterator const_iterator; - typedef typename internal_type::iterator iterator; - typedef typename internal_type::reverse_iterator reverse_iterator; - typedef typename internal_type::const_reverse_iterator const_reverse_iterator; - typedef typename internal_type::size_type size_type; - typedef typename internal_type::difference_type difference_type; - -private: - - internal_type container_; - - void sort_unique() { - std::sort(container_.begin(), container_.end()); - iterator i = std::unique(container_.begin(), container_.end()); - container_.erase(i, container_.end()); - } - -public: - /// Empty constructor. - svset() - : container_() { } - - /// Constructor taking a range. - template< typename Iterator > - svset(const Iterator& begin, const Iterator& end) - : container_(begin, end) { - sort_unique(); - } - - /// Copy-constructor. - svset(const svset& rhs) - : container_(rhs.container_) - {} - - /// Affectation of a sorted vector to another one. - - svset & operator=(const svset& rhs) { - if (&rhs != this) { - this->container_ = rhs.container_; - } - return *this; - } - - /// Returns the beginning of the sorted vector. - const_iterator begin() const { - return container_.begin(); - } - - /// Returns the end of the sorted vector. - const_iterator end() const { - return container_.end(); - } - - /// Returns the beginning of the sorted vector. - iterator begin() { - return container_.begin(); - } - - /// Returns the end of the sorted vector. - iterator end() { - return container_.end(); - } - - /// Returns the beginning of the sorted vector. - const_reverse_iterator rrbegin() const { - return container_.rbegin(); - } - - /// Returns the end of the sorted vector. - const_reverse_iterator rend() const { - return container_.rend(); - } - - /// Returns the beginning of the sorted vector. - reverse_iterator rbegin() { - return container_.rbegin(); - } - - /// Returns the end of the sorted vector. - reverse_iterator rend() { - return container_.rend(); - } - - bool empty() const { - return container_.empty(); - } - - size_type size() const { - return container_.size(); - } - - size_type max_size() const { - return container_.max_size(); - } - - /// Insert a value into the sorted vector. - std::pair - insert(const value_type& x) { - bool insertion = false; - iterator i = std::lower_bound(container_.begin(), container_.end(), x); - if (i == container_.end() || x < *i) { - i = container_.insert(i, x); - insertion = true; - } - return std::make_pair(i, insertion); - } - - /// Insert a value into the sorted vector. - iterator insert(iterator position, const value_type& x) { - return insert(x).first; - } - - /// Insert a range. - template < typename Iterator > - void insert(Iterator _begin, Iterator _end) { - while (_begin != _end) - container_.push_back(*_begin++); - sort_unique(); - } - - //specialized insert for another svset - void insert(const_iterator ii, const_iterator ie ) { - internal_type ctemp; - ctemp.reserve(container_.size()); - std::set_union(ii, ie, container_.begin(), container_.end(), - std::back_inserter(ctemp)); - container_.swap(ctemp); - } - - iterator erase ( iterator position ) { - return container_.erase(position); - } - - size_type erase(const key_type& x) { - iterator i = find(x); - if (i != end()) { - erase(i); - return 1; - } - return 0; - } - - iterator erase ( iterator first, iterator last ) { - return container_.erase(first, last); - } - - /// Swap the content of two sorted_vector. - void swap(svset& s) { - container_.swap(s.container_); - } - - void clear() { - container_.clear(); - } - - /// Find the key k. - - const_iterator find(const key_type& k) const { - const_iterator i = std::lower_bound(container_.begin(), container_.end(), k); - if (i != container_.end() && *i == k) return i; - return container_.end(); - } - - iterator find(const key_type& k) { - iterator i = std::lower_bound(container_.begin(), container_.end(), k); - if (i != container_.end() && *i == k) return i; - return container_.end(); - } - - bool count(const key_type& k) const { - return find(k) != end(); - } - - iterator lower_bound(const key_type& x) const { - return std::lower_bound(container_.begin(), container_.end(), x); - } - - iterator upper_bound(const key_type& x) const { - return std::upper_bound(container_.begin(), container_.end(), x); - } - - std::pair equal_range(const key_type& x) const { - return std::make_pair(lower_bound(x),upper_bound(x)); - } - - - /// Tells if this sorted vector is equal to another one. - bool operator==(const svset& rhs) const { - return container_ == rhs.container_; - } - bool operator<(const svset& rhs) const { - if (rhs.size() < size()) return false; - if (size() < rhs.size()) return true; - const_iterator ii = begin(); - const_iterator ee = end(); - const_iterator rhs_ii = rhs.begin(); - while (ii != ee) { - if (*rhs_ii < *ii) return false; - if (*ii < *rhs_ii) return true; - ++ii; - ++rhs_ii; - } - // Equal - return false; - } - -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -#endif // _SV_ORDERED_SET_HH_ diff --git a/include/smack/BoogieAst.h b/include/smack/BoogieAst.h index aa3c6b0aa..ffddfeac0 100644 --- a/include/smack/BoogieAst.h +++ b/include/smack/BoogieAst.h @@ -22,9 +22,9 @@ class Expr { static const Expr *forall(std::list, const Expr *e); static const Expr *and_(const Expr *l, const Expr *r); static const Expr *or_(const Expr *l, const Expr *r); - static const Expr *cond(const Expr *c, const Expr *t, const Expr *e); static const Expr *eq(const Expr *l, const Expr *r); static const Expr *lt(const Expr *l, const Expr *r); + static const Expr *ifThenElse(const Expr *c, const Expr *t, const Expr *e); static const Expr *fn(std::string f, const Expr *x); static const Expr *fn(std::string f, const Expr *x, const Expr *y); static const Expr *fn(std::string f, const Expr *x, const Expr *y, @@ -48,7 +48,6 @@ class Expr { static const Expr *sel(const Expr *b, const Expr *i); static const Expr *sel(std::string b, std::string i); static const Expr *upd(const Expr *b, const Expr *i, const Expr *v); - static const Expr *if_then_else(const Expr *c, const Expr *t, const Expr *e); static const Expr *bvExtract(const Expr *v, const Expr *upper, const Expr *lower); static const Expr *bvExtract(std::string v, unsigned upper, unsigned lower); @@ -89,17 +88,6 @@ class BinExpr : public Expr { void print(std::ostream &os) const; }; -class CondExpr : public Expr { - const Expr *cond; - const Expr *then; - const Expr *else_; - -public: - CondExpr(const Expr *c, const Expr *t, const Expr *e) - : cond(c), then(t), else_(e) {} - void print(std::ostream &os) const; -}; - class FunExpr : public Expr { std::string fun; std::list args; @@ -247,12 +235,12 @@ class VarExpr : public Expr { class IfThenElseExpr : public Expr { const Expr *cond; - const Expr *true_value; - const Expr *false_value; + const Expr *trueValue; + const Expr *falseValue; public: IfThenElseExpr(const Expr *c, const Expr *t, const Expr *e) - : cond(c), true_value(t), false_value(e) {} + : cond(c), trueValue(t), falseValue(e) {} void print(std::ostream &os) const; }; diff --git a/include/smack/DSAWrapper.h b/include/smack/DSAWrapper.h index ef009069d..1e16f1c53 100644 --- a/include/smack/DSAWrapper.h +++ b/include/smack/DSAWrapper.h @@ -7,52 +7,32 @@ #ifndef DSAWRAPPER_H #define DSAWRAPPER_H -#include "llvm/Analysis/MemoryLocation.h" -#include "llvm/IR/InstVisitor.h" +#include #include -namespace llvm { -class DSNode; -class DSGraph; -class TDDataStructures; -class BUDataStructures; -class DSNodeEquivs; -} // namespace llvm - -namespace dsa { -template struct TypeSafety; -} +#include "sea_dsa/DsaAnalysis.hh" +#include "sea_dsa/Global.hh" +#include "sea_dsa/Graph.hh" namespace smack { -class MemcpyCollector : public llvm::InstVisitor { -private: - llvm::DSNodeEquivs *nodeEqs; - std::vector memcpys; - -public: - MemcpyCollector(llvm::DSNodeEquivs *neqs) : nodeEqs(neqs) {} - void visitMemCpyInst(llvm::MemCpyInst &mci); - std::vector getMemcpys() { return memcpys; } -}; - class DSAWrapper : public llvm::ModulePass { private: llvm::Module *module; - llvm::TDDataStructures *TD; - llvm::BUDataStructures *BU; - llvm::DSNodeEquivs *nodeEqs; - dsa::TypeSafety *TS; - std::vector staticInits; - std::vector memcpys; - std::unordered_set intConversions; + sea_dsa::GlobalAnalysis *SD; + // The ds graph since we're using the context-insensitive version which + // results in one graph for the whole module. + sea_dsa::Graph *DG; + std::unordered_set staticInits; + std::unordered_set memOpds; + // Mapping from the DSNodes associated with globals to the numbers of + // globals associated with them. + std::unordered_map globalRefCount; const llvm::DataLayout *dataLayout; - std::vector collectMemcpys(llvm::Module &M, - MemcpyCollector *mcc); - std::vector collectStaticInits(llvm::Module &M); - llvm::DSGraph *getGraphForValue(const llvm::Value *V); - int getOffset(const llvm::MemoryLocation *l); + void collectStaticInits(llvm::Module &M); + void collectMemOpds(llvm::Module &M); + void countGlobalRefs(); public: static char ID; @@ -61,18 +41,15 @@ class DSAWrapper : public llvm::ModulePass { virtual void getAnalysisUsage(llvm::AnalysisUsage &AU) const; virtual bool runOnModule(llvm::Module &M); - bool isMemcpyd(const llvm::DSNode *n); - bool isStaticInitd(const llvm::DSNode *n); - bool isFieldDisjoint(const llvm::Value *V, const llvm::Function *F); - bool isFieldDisjoint(const llvm::GlobalValue *V, unsigned offset); + bool isStaticInitd(const sea_dsa::Node *n); + bool isMemOpd(const sea_dsa::Node *n); bool isRead(const llvm::Value *V); - bool isAlloced(const llvm::Value *v); - bool isExternal(const llvm::Value *v); bool isSingletonGlobal(const llvm::Value *V); unsigned getPointedTypeSize(const llvm::Value *v); - int getOffset(const llvm::Value *v); - const llvm::DSNode *getNode(const llvm::Value *v); - void printDSAGraphs(const char *Filename); + unsigned getOffset(const llvm::Value *v); + const sea_dsa::Node *getNode(const llvm::Value *v); + bool isTypeSafe(const llvm::Value *v); + unsigned getNumGlobals(const sea_dsa::Node *n); }; } // namespace smack diff --git a/include/smack/Regions.h b/include/smack/Regions.h index a5b903107..efc1903b9 100644 --- a/include/smack/Regions.h +++ b/include/smack/Regions.h @@ -4,6 +4,7 @@ #ifndef REGIONS_H #define REGIONS_H +#include "sea_dsa/Graph.hh" #include "llvm/IR/InstVisitor.h" using namespace llvm; @@ -19,7 +20,7 @@ class DSAWrapper; class Region { private: LLVMContext *context; - const DSNode *representative; + const sea_dsa::Node *representative; const Type *type; unsigned offset; unsigned length; @@ -33,12 +34,10 @@ class Region { static const DataLayout *DL; static DSAWrapper *DSA; - // static DSNodeEquivs* NEQS; - static bool isSingleton(const DSNode *N, unsigned offset, unsigned length); - static bool isAllocated(const DSNode *N); - static bool bytewiseAccess(const DSNode *N); - static bool isComplicated(const DSNode *N); + static bool isSingleton(const llvm::Value *v, unsigned length); + static bool isAllocated(const sea_dsa::Node *N); + static bool isComplicated(const sea_dsa::Node *N); void init(const Value *V, unsigned length); bool isDisjoint(unsigned offset, unsigned length); @@ -89,7 +88,8 @@ class Regions : public ModulePass, public InstVisitor { void visitStoreInst(StoreInst &); void visitAtomicCmpXchgInst(AtomicCmpXchgInst &); void visitAtomicRMWInst(AtomicRMWInst &); - void visitMemIntrinsic(MemIntrinsic &); + void visitMemSetInst(MemSetInst &); + void visitMemTransferInst(MemTransferInst &); void visitCallInst(CallInst &); }; } // namespace smack diff --git a/include/smack/SmackOptions.h b/include/smack/SmackOptions.h index 66c0f34dc..a6c210311 100644 --- a/include/smack/SmackOptions.h +++ b/include/smack/SmackOptions.h @@ -10,6 +10,8 @@ #include "smack/SmackWarnings.h" namespace smack { +enum class LLVMAssumeType { none, use, check }; + class SmackOptions { public: static const llvm::cl::list EntryPoints; @@ -28,6 +30,7 @@ class SmackOptions { static const llvm::cl::opt FloatEnabled; static const llvm::cl::opt MemorySafety; static const llvm::cl::opt IntegerOverflow; + static const llvm::cl::opt LLVMAssumes; static const llvm::cl::opt AddTiming; static bool isEntryPoint(std::string); diff --git a/include/assistDS/Devirt.h b/include/utils/Devirt.h similarity index 92% rename from include/assistDS/Devirt.h rename to include/utils/Devirt.h index c6d7a9d3b..1aad10057 100644 --- a/include/assistDS/Devirt.h +++ b/include/utils/Devirt.h @@ -12,7 +12,6 @@ // //===----------------------------------------------------------------------===// -#include "dsa/CallTargets.h" #include "llvm/IR/Constants.h" #include "llvm/Transforms/IPO.h" @@ -24,6 +23,10 @@ #include "llvm/IR/InstVisitor.h" #include "llvm/IR/DataLayout.h" +#include "sea_dsa/CompleteCallGraph.hh" + +#include + using namespace llvm; namespace llvm { @@ -38,7 +41,7 @@ namespace llvm { class Devirtualize : public ModulePass, public InstVisitor { private: // Access to analysis pass which finds targets of indirect function calls - dsa::CallTargetFinder *CTF; + sea_dsa::CompleteCallGraph *CCG; // Access to the target data analysis pass const DataLayout * TD; @@ -57,12 +60,12 @@ namespace llvm { public: static char ID; - Devirtualize() : ModulePass(ID), CTF(0) {} + Devirtualize() : ModulePass(ID) {} virtual bool runOnModule(Module & M); virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired >(); + AU.addRequired(); } // Visitor methods for analyzing instructions diff --git a/include/assistDS/LICENSE.TXT b/include/utils/LICENSE.TXT similarity index 100% rename from include/assistDS/LICENSE.TXT rename to include/utils/LICENSE.TXT diff --git a/include/assistDS/MergeGEP.h b/include/utils/MergeGEP.h similarity index 100% rename from include/assistDS/MergeGEP.h rename to include/utils/MergeGEP.h diff --git a/include/assistDS/SimplifyExtractValue.h b/include/utils/SimplifyExtractValue.h similarity index 100% rename from include/assistDS/SimplifyExtractValue.h rename to include/utils/SimplifyExtractValue.h diff --git a/include/assistDS/SimplifyInsertValue.h b/include/utils/SimplifyInsertValue.h similarity index 100% rename from include/assistDS/SimplifyInsertValue.h rename to include/utils/SimplifyInsertValue.h diff --git a/lib/AssistDS/ArgCast.cpp b/lib/AssistDS/ArgCast.cpp deleted file mode 100644 index 796867b05..000000000 --- a/lib/AssistDS/ArgCast.cpp +++ /dev/null @@ -1,218 +0,0 @@ -//===-------- ArgCast.cpp - Cast Arguments to Calls -----------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// Convert -// call(bitcast (.., T1 arg, ...)F to(..., T2 arg, ...))(..., T2 val, ...) -// to -// val1 = bitcast T2 val to T1 -// call F (..., T1 val1, ...) -//===----------------------------------------------------------------------===// -#define DEBUG_TYPE "argcast" - -#include "assistDS/ArgCast.h" -#include "llvm/IR/Attributes.h" -#include "llvm/Transforms/Utils/Cloning.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/Support/FormattedStream.h" -#include "smack/Debug.h" - -#include -#include -#include - -using namespace llvm; - -// Pass statistics -STATISTIC(numChanged, "Number of Args bitcasted"); - -// -// Method: runOnModule() -// -// Description: -// Entry point for this LLVM pass. -// Search for all call sites to casted functions. -// Check if they only differ in an argument type -// Cast the argument, and call the original function -// -// Inputs: -// M - A reference to the LLVM module to transform -// -// Outputs: -// M - The transformed LLVM module. -// -// Return value: -// true - The module was modified. -// false - The module was not modified. -// -bool ArgCast::runOnModule(Module& M) { - - std::vector worklist; - for (Function &F : M) { - if (F.isInterposable()) - continue; - // Find all uses of this function - for (User *U : F.users()) { - // check if is ever casted to a different function type - ConstantExpr *CE = dyn_cast(U++); - if(!CE) - continue; - if (CE->getOpcode() != Instruction::BitCast) - continue; - if(CE->getOperand(0) != &F) - continue; - const PointerType *PTy = dyn_cast(CE->getType()); - if (!PTy) - continue; - const Type *ETy = PTy->getElementType(); - const FunctionType *FTy = dyn_cast(ETy); - if(!FTy) - continue; - // casting to a varargs funtion - // or function with same number of arguments - // possibly varying types of arguments - - if(FTy->getNumParams() != F.arg_size() && !FTy->isVarArg()) - continue; - for(Value::user_iterator uii = CE->user_begin(), - uee = CE->user_end(); uii != uee; ++uii) { - // Find all uses of the casted value, and check if it is - // used in a Call Instruction - if (CallInst* CI = dyn_cast(*uii)) { - // Check that it is the called value, and not an argument - if(CI->getCalledValue() != CE) - continue; - // Check that the number of arguments passed, and expected - // by the function are the same. - if(!F.isVarArg()) { - if(CI->getNumOperands() != F.arg_size() + 1) - continue; - } else { - if(CI->getNumOperands() < F.arg_size() + 1) - continue; - } - // If so, add to worklist - worklist.push_back(CI); - } - } - } - } - - // Proces the worklist of potential call sites to transform - while(!worklist.empty()) { - CallInst *CI = worklist.back(); - worklist.pop_back(); - // Get the called Function - Function *F = cast(CI->getCalledValue()->stripPointerCasts()); - const FunctionType *FTy = F->getFunctionType(); - - SmallVector Args; - unsigned i =0; - for(i =0; i< FTy->getNumParams(); ++i) { - Type *ArgType = CI->getOperand(i+1)->getType(); - Type *FormalType = FTy->getParamType(i); - // If the types for this argument match, just add it to the - // parameter list. No cast needs to be inserted. - if(ArgType == FormalType) { - Args.push_back(CI->getOperand(i+1)); - } - else if(ArgType->isPointerTy() && FormalType->isPointerTy()) { - CastInst *CastI = CastInst::CreatePointerCast(CI->getOperand(i+1), - FormalType, "", CI); - Args.push_back(CastI); - } else if (ArgType->isIntegerTy() && FormalType->isIntegerTy()) { - unsigned SrcBits = ArgType->getScalarSizeInBits(); - unsigned DstBits = FormalType->getScalarSizeInBits(); - if(SrcBits > DstBits) { - CastInst *CastI = CastInst::CreateIntegerCast(CI->getOperand(i+1), - FormalType, true, "", CI); - Args.push_back(CastI); - } else { - if (F->getAttributes().hasAttribute(i+1, Attribute::SExt)) { - CastInst *CastI = CastInst::CreateIntegerCast(CI->getOperand(i+1), - FormalType, true, "", CI); - Args.push_back(CastI); - } else if (F->getAttributes().hasAttribute(i+1, Attribute::ZExt)) { - CastInst *CastI = CastInst::CreateIntegerCast(CI->getOperand(i+1), - FormalType, false, "", CI); - Args.push_back(CastI); - } else { - // Use ZExt in default case. - // Derived from InstCombine. Also, the only reason this should happen - // is mismatched prototypes. - // Seen in case of integer constants which get interpreted as i32, - // even if being used as i64. - // TODO: is this correct? - CastInst *CastI = CastInst::CreateIntegerCast(CI->getOperand(i+1), - FormalType, false, "", CI); - Args.push_back(CastI); - } - } - } else { - SDEBUG(ArgType->print(smack::dbgs(), true)); - SDEBUG(FormalType->print(smack::dbgs(), true)); - break; - } - } - - // If we found an argument we could not cast, try the next instruction - if(i != FTy->getNumParams()) { - continue; - } - - if(FTy->isVarArg()) { - for(; i< CI->getNumOperands() - 1 ;i++) { - Args.push_back(CI->getOperand(i+1)); - } - } - - // else replace the call instruction - CallInst *CINew = CallInst::Create(F, Args, "", CI); - CINew->setCallingConv(CI->getCallingConv()); - CINew->setAttributes(CI->getAttributes()); - if(!CI->use_empty()) { - CastInst *RetCast; - if(CI->getType() != CINew->getType()) { - if(CI->getType()->isPointerTy() && CINew->getType()->isPointerTy()) - RetCast = CastInst::CreatePointerCast(CINew, CI->getType(), "", CI); - else if(CI->getType()->isIntOrIntVectorTy() && CINew->getType()->isIntOrIntVectorTy()) - RetCast = CastInst::CreateIntegerCast(CINew, CI->getType(), false, "", CI); - else if(CI->getType()->isIntOrIntVectorTy() && CINew->getType()->isPointerTy()) - RetCast = CastInst::CreatePointerCast(CINew, CI->getType(), "", CI); - else if(CI->getType()->isPointerTy() && CINew->getType()->isIntOrIntVectorTy()) - RetCast = new IntToPtrInst(CINew, CI->getType(), "", CI); - else { - // TODO: I'm not sure what right behavior is here, but this case should be handled. - llvm_unreachable("Unexpected type conversion in call!"); - abort(); - } - CI->replaceAllUsesWith(RetCast); - } else { - CI->replaceAllUsesWith(CINew); - } - } - - // Debug printing - SDEBUG(errs() << "ARGCAST:"); - SDEBUG(errs() << "ERASE:"); - SDEBUG(CI->print(smack::dbgs(), true)); - SDEBUG(errs() << "ARGCAST:"); - SDEBUG(errs() << "ADDED:"); - SDEBUG(CINew->print(smack::dbgs(), true)); - - CI->eraseFromParent(); - numChanged++; - } - return true; -} - -// Pass ID variable -char ArgCast::ID = 0; - -// Register the pass -static RegisterPass -X("arg-cast", "Cast Arguments"); diff --git a/lib/AssistDS/ArgSimplify.cpp b/lib/AssistDS/ArgSimplify.cpp deleted file mode 100644 index 2ddb0521a..000000000 --- a/lib/AssistDS/ArgSimplify.cpp +++ /dev/null @@ -1,173 +0,0 @@ -//===-- ArgSimplify.cpp - Special case for conditional ptr args ----------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -#define DEBUG_TYPE "argsimpl" - -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/Module.h" -#include "llvm/Pass.h" -#include "llvm/Transforms/Utils/Cloning.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/Support/FormattedStream.h" -#include "smack/Debug.h" - -#include -#include -#include - -using namespace llvm; - -STATISTIC(numTransformable, "Number of Args changeable"); - -namespace { - - // F - Function to modify - // arg_count - The argument to function I that may be changed - // type - Declared type of the argument - - static void simplify(Function *F, unsigned arg_count, Type* type) { - - // Go through all uses of the function - for(Value::user_iterator ui = F->user_begin(), ue = F->user_end(); - ui != ue; ++ui) { - - if (Constant *C = dyn_cast(*ui)) { - if (ConstantExpr *CE = dyn_cast(C)) { - if (CE->getOpcode() == Instruction::BitCast) { - if(CE->getOperand(0) == F) { - for(Value::user_iterator uii = CE->user_begin(), uee = CE->user_end(); - uii != uee; ) { - // check if it is ever used as a call (bitcast F to ...)() - if (CallInst* CI = dyn_cast(*uii++)) { - if(CI->getCalledValue() == CE) { - // if I is ever called as a bitcasted function - if(F->getReturnType() == CI->getType()){ - // if the return types match. - if(F->arg_size() == (CI->getNumOperands()-1)){ - // and the numeber of args match too - unsigned arg_count1 = 1; - bool change = true; - for (Function::arg_iterator ii1 = F->arg_begin(), ee1 = F->arg_end(); - ii1 != ee1; ++ii1,arg_count1++) { - if(arg_count1 == (arg_count + 1)) { - if(ii1->getType() == CI->getOperand(arg_count1)->getType()){ - change = false; - break; - } - else - continue; - } - if(ii1->getType() != CI->getOperand(arg_count1)->getType()) { - change = false; - break; - } - } - // if all types match except the argument we are interested in - - if(change){ - // create a new function, to do the cast from ptr to int, - // and call the original function, with the casted value - std::vectorTP; - for(unsigned c = 1; cgetNumOperands();c++) { - TP.push_back(CI->getOperand(c)->getType()); - } - FunctionType *NewFTy = FunctionType:: - get(CI->getType(), TP, false); - - Module *M = F->getParent(); - Function *NewF = Function::Create(NewFTy, - GlobalValue::InternalLinkage, - "argbounce", - M); - std::vector fargs; - for (auto &Arg : NewF->args()) { - fargs.push_back(&Arg); - Arg.setName("arg"); - } - Value *CastedVal; - BasicBlock* entryBB = BasicBlock:: - Create (M->getContext(), "entry", NewF); - - Type *FromTy = fargs.at(arg_count)->getType(); - if(FromTy->isPointerTy()) { - CastedVal = CastInst::CreatePointerCast(fargs.at(arg_count), - type, "castd", entryBB); - } else { - CastedVal = CastInst::CreateIntegerCast(fargs.at(arg_count), - type, false, "casted", entryBB); - } - - SmallVector Args; - for (auto &Arg : NewF->args()) { - if(Arg.getArgNo() == arg_count) - Args.push_back(CastedVal); - else - Args.push_back(&Arg); - } - - CallInst * CallI = CallInst::Create(F,Args, - "", entryBB); - if(CallI->getType()->isVoidTy()) - ReturnInst::Create(M->getContext(), entryBB); - else - ReturnInst::Create(M->getContext(), CallI, entryBB); - - CI->setCalledFunction(NewF); - numTransformable++; - } - } - } - } - } - } - } - } - } - } - } - } - - class ArgSimplify : public ModulePass { - public: - static char ID; - ArgSimplify() : ModulePass(ID) {} - - bool runOnModule(Module& M) { - - for (Function &F : M) - if (!F.isDeclaration() && !F.isInterposable()) { - if(F.getName().str() == "main") - continue; - std::vector Args; - for (auto &Arg : F.args()) { - bool change = true; - for (User *U : Arg.users()) { - // check if the argument is used exclusively in ICmp Instructions - if(!isa(U)) { - change = false; - break; - } - } - // if this argument is only used in ICMP instructions, we can - // replace it. - if(change) { - simplify(&F, Arg.getArgNo(), Arg.getType()); - } - } - } - - - return true; - } - }; -} - -char ArgSimplify::ID = 0; -static RegisterPass -X("arg-simplify", "Specialize for Conditional Arguments"); diff --git a/lib/AssistDS/DSNodeEquivs.cpp b/lib/AssistDS/DSNodeEquivs.cpp deleted file mode 100644 index 8a9052ea5..000000000 --- a/lib/AssistDS/DSNodeEquivs.cpp +++ /dev/null @@ -1,289 +0,0 @@ -//===- DSNodeEquivs.cpp - Build DSNode equivalence classes ---------------===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements a pass to compute DSNode equivalence classes across -// DSGraphs. -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "DSNodeEquivs" -#include "assistDS/DSNodeEquivs.h" - -#include "llvm/IR/Constants.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/InstIterator.h" -#include "llvm/ADT/SmallSet.h" -#include "smack/Debug.h" - -#include - -namespace llvm { - -char DSNodeEquivs::ID = 0; - -static RegisterPass -X("dsnodeequivs", "Compute DSNode equivalence classes"); - -// Build equivalence classes of DSNodes that are mapped between graphs. -void DSNodeEquivs::buildDSNodeEquivs(Module &M) { - TDDataStructures &TDDS = getAnalysis(); - - Module::iterator FuncIt = M.begin(), FuncItEnd = M.end(); - for (; FuncIt != FuncItEnd; ++FuncIt) { - Function &F = *FuncIt; - - if (!TDDS.hasDSGraph(F)) - continue; - - inst_iterator InstIt = inst_begin(F), InstItEnd = inst_end(F); - for (; InstIt != InstItEnd; ++InstIt) { - if (CallInst *Call = dyn_cast(&*InstIt)) { - equivNodesThroughCallsite(Call); - } - } - - DSGraph *Graph = TDDS.getDSGraph(F); - - // Ensure all nodes from this function's graph are in an equivalence class. - addNodesFromGraph(Graph); - - equivNodesToGlobals(Graph); - } - - // Ensure all nodes from the globals graph are in an equivalence class. - addNodesFromGraph(TDDS.getGlobalsGraph()); -} - -// Add nodes from the given graph into the equivalence classes. -void DSNodeEquivs::addNodesFromGraph(DSGraph *Graph) { - DSGraph::node_iterator NodeIt = Graph->node_begin(); - DSGraph::node_iterator NodeItEnd = Graph->node_end(); - for (; NodeIt != NodeItEnd; ++NodeIt) - Classes.insert(&*NodeIt); -} - -FunctionList DSNodeEquivs::getCallees(CallSite &CS) { - const Function *CalledFunc = CS.getCalledFunction(); - - // If the called function is casted from one function type to another, peer - // into the cast instruction and pull out the actual function being called. - if (ConstantExpr *CExpr = dyn_cast(CS.getCalledValue())) { - if (CExpr->getOpcode() == Instruction::BitCast && - isa(CExpr->getOperand(0))) - CalledFunc = cast(CExpr->getOperand(0)); - } - - FunctionList Callees; - - // Direct calls are simple. - if (CalledFunc) { - Callees.push_back(CalledFunc); - return Callees; - } - - // Okay, indirect call. - // Ask the DSCallGraph what this calls... - - TDDataStructures &TDDS = getAnalysis(); - const DSCallGraph &DSCG = TDDS.getCallGraph(); - - DSCallGraph::callee_iterator CalleeIt = DSCG.callee_begin(CS); - DSCallGraph::callee_iterator CalleeItEnd = DSCG.callee_end(CS); - for (; CalleeIt != CalleeItEnd; ++CalleeIt) - Callees.push_back(*CalleeIt); - - // If the callgraph doesn't give us what we want, query the DSGraph - // ourselves. - if (Callees.empty()) { - Instruction *Inst = CS.getInstruction(); - Function *Parent = Inst->getParent()->getParent(); - Value *CalledValue = CS.getCalledValue(); - DSNodeHandle &NH = TDDS.getDSGraph(*Parent)->getNodeForValue(CalledValue); - - if (!NH.isNull()) { - DSNode *Node = NH.getNode(); - Node->addFullFunctionList(Callees); - } - } - - // For debugging, dump out the callsites we are unable to get callees for. - SDEBUG( - if (Callees.empty()) { - errs() << "Failed to get callees for callsite:\n"; - CS.getInstruction()->print(smack::dbgs(), true); - }); - - return Callees; -} - -// Compute mappings through the given call site. -void DSNodeEquivs::equivNodesThroughCallsite(CallInst *CI) { - TDDataStructures &TDDS = getAnalysis(); - DSGraph &Graph = *TDDS.getDSGraph(*CI->getParent()->getParent()); - CallSite CS(CI); - FunctionList Callees = getCallees(CS); - - FunctionList_it CalleeIt = Callees.begin(), CalleeItEnd = Callees.end(); - for (; CalleeIt != CalleeItEnd; ++CalleeIt) { - const Function &Callee = **CalleeIt; - - // We can't merge through graphs that don't exist. - if (!TDDS.hasDSGraph(Callee)) - continue; - - DSGraph &CalleeGraph = *TDDS.getDSGraph(Callee); - DSGraph::NodeMapTy NodeMap; - - // Heavily lifted/inspired by PA code - - // Map arguments - Function::const_arg_iterator FArgIt = Callee.arg_begin(); - Function::const_arg_iterator FArgItEnd = Callee.arg_end(); - CallSite::arg_iterator ArgIt = CS.arg_begin(), ArgItEnd = CS.arg_end(); - for (; FArgIt != FArgItEnd && ArgIt != ArgItEnd; ++FArgIt, ++ArgIt) { - if (isa(*ArgIt)) - continue; - - DSNodeHandle &CalleeArgNH = CalleeGraph.getNodeForValue(&*FArgIt); - DSNodeHandle &CSArgNH = Graph.getNodeForValue(*ArgIt); - DSGraph::computeNodeMapping(CalleeArgNH, CSArgNH, NodeMap, false); - } - - // Map return value - if (isa(CI->getType())) { - DSNodeHandle &CalleeRetNH = CalleeGraph.getReturnNodeFor(Callee); - DSNodeHandle &CINH = Graph.getNodeForValue(CI); - DSGraph::computeNodeMapping(CalleeRetNH, CINH, NodeMap, false); - } - - // Merge information from the computed node mapping into the equivalence - // classes. - equivNodeMapping(NodeMap); - } -} - -// Compute mappings with the globals graph. -void DSNodeEquivs::equivNodesToGlobals(DSGraph *G) { - DSGraph *GlobalsGr = G->getGlobalsGraph(); - DSGraph::NodeMapTy NodeMap; - DSScalarMap &ScalarMap = GlobalsGr->getScalarMap(); - - DSScalarMap::global_iterator GlobalIt = ScalarMap.global_begin(); - DSScalarMap::global_iterator GlobalItEnd = ScalarMap.global_end(); - for (; GlobalIt != GlobalItEnd; ++GlobalIt) { - const GlobalValue *Global = *GlobalIt; - - DSNode *LocalNode = G->getNodeForValue(Global).getNode(); - - // It's quite possible this (local) graph doesn't have this global. - // If that's the case, there's nothing to do here. - if (!LocalNode) continue; - - DSNode *GlobalNode = GlobalsGr->getNodeForValue(Global).getNode(); - assert(GlobalNode && "No node for global in global scalar map?"); - - // Map the two together and all reachable from each... - NodeMap.clear(); - DSGraph::computeNodeMapping(LocalNode, GlobalNode, NodeMap, false); - - // Build EC's with this mapping. - equivNodeMapping(NodeMap); - } -} - -// Utility function to put nodes that map together into equivalence classes. -void DSNodeEquivs::equivNodeMapping(DSGraph::NodeMapTy &NodeMap) { - DSGraph::NodeMapTy::iterator NodeMapIt = NodeMap.begin(); - DSGraph::NodeMapTy::iterator NodeMapItEnd = NodeMap.end(); - for (; NodeMapIt != NodeMapItEnd; ++NodeMapIt) { - DSNodeHandle &NH = NodeMapIt->second; - if (NH.isNull()) - continue; - - const DSNode *N1 = NodeMapIt->first; - const DSNode *N2 = NH.getNode(); - - Classes.unionSets(N1, N2); - } -} - -bool DSNodeEquivs::runOnModule(Module &M) { - buildDSNodeEquivs(M); - // Does not modify module. - return false; -} - -// Returns the computed equivalence classes. -const EquivalenceClasses & -DSNodeEquivs::getEquivalenceClasses() { - return Classes; -} - -// Returns a DSNode for the specified value. -// Returns null for a node that was not found. -const DSNode *DSNodeEquivs::getMemberForValue(const Value *V) { - TDDataStructures &TDDS = getAnalysis(); - DSNodeHandle *NHForV = 0; - - if (isa(V)) { - NHForV = &TDDS.getGlobalsGraph()->getNodeForValue(V); - } else if (isa(V)) { - const Function *Parent = cast(V)->getParent()->getParent(); - NHForV = &TDDS.getDSGraph(*Parent)->getNodeForValue(V); - } else if (isa(V)) { - const Function *Parent = cast(V)->getParent(); - NHForV = &TDDS.getDSGraph(*Parent)->getNodeForValue(V); - } else { - // - // Iterate over the users to attempt to discover a DSNode that the value - // maps to. - // - std::deque WL; - SmallSet Visited; - - for (auto U : V->users()) - WL.push_back(U); - - do { - const User *TheUser = WL.front(); - WL.pop_front(); - - if (!Visited.insert(TheUser).second) - continue; - - // - // If the use is a global variable or instruction, then the value should - // be in the corresponding DSGraph. - // - // TODO: Is it possible for a value to belong to some DSGraph and never - // be relied upon by a GlobalValue or Instruction? - // - if (const Instruction *I = dyn_cast(TheUser)) { - const Function *Parent = I->getParent()->getParent(); - NHForV = &TDDS.getDSGraph(*Parent)->getNodeForValue(V); - break; - } else if (isa(TheUser)) { - NHForV = &TDDS.getGlobalsGraph()->getNodeForValue(V); - break; - } else { - // - // If this use is of some other nature, look at the users of this use. - // - for (auto U : TheUser->users()) - WL.push_back(U); - } - } while (!WL.empty()); - } - - assert(NHForV && "Unable to find node handle for given value!"); - - return NHForV->getNode(); -} - -} diff --git a/lib/AssistDS/DataStructureCallGraph.cpp b/lib/AssistDS/DataStructureCallGraph.cpp deleted file mode 100644 index ab373174f..000000000 --- a/lib/AssistDS/DataStructureCallGraph.cpp +++ /dev/null @@ -1,125 +0,0 @@ -//===- DataStructureCallGraph.cpp - Provide a CallGraph using DSA ---------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file contains the DataStructureCallGraph implementation of the -// CallGraph analysis. Based on llvm/lib/Analysis/IPA/CallGraph.cpp. -// -//===----------------------------------------------------------------------===// - -#include "assistDS/DataStructureCallGraph.h" -#include "dsa/DSGraph.h" -#include "dsa/DSNode.h" - -#include "llvm/ADT/SmallPtrSet.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/IntrinsicInst.h" -#include "llvm/IR/CallSite.h" -#include "llvm/IR/InstIterator.h" - -using namespace llvm; - -char DataStructureCallGraph::ID; - -namespace { - -static RegisterPass -X("dsa-cg", "DSA-based CallGraph implementation"); - -RegisterAnalysisGroup Y(X); - -} - -bool DataStructureCallGraph::runOnModule(Module &M) { - ExternalCallingNode = getOrInsertFunction(0); - CallsExternalNode = new CallGraphNode(0); - Root = 0; - - // Add every function to the call graph. - for (Function &F : M) - addToCallGraph(&F); - - // If we didn't find a main function, use the external call graph node - if (Root == 0) Root = ExternalCallingNode; - - return false; -} - -// Add a function to the call graph, and link the node to all of the functions -// that it calls. -void DataStructureCallGraph::addToCallGraph(Function *F) { - CallGraphNode *Node = getOrInsertFunction(F); - - if (!F->hasLocalLinkage()) { - ExternalCallingNode->addCalledFunction(CallSite(), Node); - - // Found the entry point? - if (F->getName() == "main") { - if (Root) // Found multiple external mains? Don't pick one. - Root = ExternalCallingNode; - else - Root = Node; // Found a main, keep track of it! - } - } - - // If this function is not defined in this translation unit, it could call - // anything. - if (F->isDeclaration() && !F->isIntrinsic()) { - Node->addCalledFunction(CallSite(), CallsExternalNode); - return; - } - - TDDataStructures &DS = getAnalysis(); - DSGraph &GG = *DS.getGlobalsGraph(); - CallTargetFinderTy &CTF = getAnalysis(); - - // Determine if the function can be called by external code by looking up - // its DSNode in the globals graph. A node marked External, Unknown, or - // Incomplete has the possibility of being called from external code. - DSNode *N = GG.getNodeForValue(F).getNode(); - - if (N && - (N->isExternalNode() || - N->isUnknownNode() || - N->isIncompleteNode())) { - ExternalCallingNode->addCalledFunction(CallSite(), Node); - } - - // Go over the instructions in the function and determine the call targets - // for each call site. - for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) { - CallSite CS(&*I); - - // Only look through valid call sites that are not calls to intrinsics. - if (!CS || isa(&*I)) - continue; - - if (const Function *F = - dyn_cast(CS.getCalledValue()->stripPointerCasts())) { - // Direct call: Don't use DSA, just add the function we discovered as - // the call target. - - Node->addCalledFunction(CS, getOrInsertFunction(F)); - } else { - // Indirect call: Use CallTargetFinder to determine the set of targets to - // the indirect call site. Be conservative about incomplete call sites. - - if (!CTF.isComplete(CS)) { - // Add CallsExternalNode as a target of incomplete call sites. - Node->addCalledFunction(CS, CallsExternalNode); - } - - SmallPtrSet Targets(CTF.begin(CS), CTF.end(CS)); - - for (SmallPtrSet::const_iterator - TI = Targets.begin(), TE = Targets.end(); TI != TE; ++TI) { - Node->addCalledFunction(CS, getOrInsertFunction(*TI)); - } - } - } -} diff --git a/lib/AssistDS/DynCount.cpp b/lib/AssistDS/DynCount.cpp deleted file mode 100644 index de0e0459a..000000000 --- a/lib/AssistDS/DynCount.cpp +++ /dev/null @@ -1,140 +0,0 @@ -//===- Dyncount.cpp - Test pass for using constraints -----------*- C++ -*-===// -// -// The SAFECode Compiler -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements a test pass which helps test the generation of formulas -// and constraints. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Pass.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/DataLayout.h" -#include "dsa/TypeSafety.h" - -using namespace llvm; -class Dyncount : public ModulePass { -protected: - void instrumentLoad (GlobalVariable * Counter, LoadInst * LI); - void instrumentStore (GlobalVariable * Counter, StoreInst * SI); - dsa::TypeSafety *TS; - -public: - static char ID; - Dyncount () : ModulePass (ID) { } - virtual StringRef getPassName() const { - return "Count safe/unsafe load/store"; - } - virtual bool runOnModule (Module & M); - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired >(); - } -}; - -char Dyncount::ID = 0; -static RegisterPass -X ("dyncount", "Instrument code to count number of Load/Stores"); - - -void -Dyncount::instrumentLoad (GlobalVariable * Counter, LoadInst * LI) { - // - // Generate a load, increment, and store right before the GEP. - // - LLVMContext & Context = Counter->getParent()->getContext(); - ConstantInt * One = ConstantInt::get (Type::getInt64Ty(Context), 1); - LoadInst * OldValue = new LoadInst (Counter, "count", LI); - Instruction * NewValue = BinaryOperator::Create (BinaryOperator::Add, - OldValue, - One, - "count", - LI); - new StoreInst (NewValue, Counter, LI); - return; -} -void -Dyncount::instrumentStore (GlobalVariable * Counter, StoreInst * SI) { - // - // Generate a load, increment, and store right before the GEP. - // - LLVMContext & Context = Counter->getParent()->getContext(); - ConstantInt * One = ConstantInt::get (Type::getInt64Ty(Context), 1); - LoadInst * OldValue = new LoadInst (Counter, "count", SI); - Instruction * NewValue = BinaryOperator::Create (BinaryOperator::Add, - OldValue, - One, - "count", - SI); - new StoreInst (NewValue, Counter, SI); - return; -} - -// -// Method: runOnModule() -// -// Description: -// Entry point for this pass. -// -bool -Dyncount::runOnModule (Module & M) { - // - // Create two global counters within the module. - // - TS = &getAnalysis >(); - ConstantInt * Zero = ConstantInt::get (Type::getInt64Ty(M.getContext()), 0); - GlobalVariable * Total = new GlobalVariable (M, - Type::getInt64Ty(M.getContext()), - false, - GlobalValue::InternalLinkage, - Zero, - "Total"); - GlobalVariable * Safe = new GlobalVariable (M, - Type::getInt64Ty(M.getContext()), - false, - GlobalValue::InternalLinkage, - Zero, - "Safe"); - - for (Module::iterator F = M.begin(); F != M.end(); ++F){ - for (Function::iterator B = F->begin(), FE = F->end(); B != FE; ++B) { - for (BasicBlock::iterator I = B->begin(), BE = B->end(); I != BE; I++) { - if(LoadInst *LI = dyn_cast(I)) { - instrumentLoad(Total, LI); - if(TS->isTypeSafe(LI->getOperand(0),LI->getParent()->getParent())) - instrumentLoad(Safe, LI); - - // check if safe - } else if(StoreInst *SI = dyn_cast(I)) { - instrumentStore(Total, SI); - if(TS->isTypeSafe(SI->getOperand(1),SI->getParent()->getParent())) - instrumentStore(Safe, SI); - // check if safe - } - } - } - } - - // - // Add a call to main() that will record the values on exit(). - // - Function *MainFunc = M.getFunction("main") ? M.getFunction("main") - : M.getFunction ("MAIN__"); - - BasicBlock & BB = MainFunc->getEntryBlock(); - Constant * Setup = M.getOrInsertFunction ("DYN_COUNT_setup", Type::getVoidTy(M.getContext()), Total->getType(), Safe->getType()); - std::vector args; - args.push_back (Total); - args.push_back (Safe); - CallInst::Create (Setup, args, "", BB.getFirstNonPHI()); - - - return true; -} - diff --git a/lib/AssistDS/FuncSimplify.cpp b/lib/AssistDS/FuncSimplify.cpp deleted file mode 100644 index 4640fb86c..000000000 --- a/lib/AssistDS/FuncSimplify.cpp +++ /dev/null @@ -1,72 +0,0 @@ -//===-------- FuncSimplify.cpp - Replace Global Aliases -------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// -#define DEBUG_TYPE "func-simplify" - -#include "assistDS/FuncSimplify.h" -#include "llvm/IR/Attributes.h" -#include "llvm/Transforms/Utils/Cloning.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/Support/FormattedStream.h" -#include "smack/Debug.h" - -#include -#include -#include - -using namespace llvm; - -// Pass statistics -STATISTIC(numChanged, "Number of aliases deleted"); - -// -// Method: runOnModule() -// -// Description: -// Entry point for this LLVM pass. -// Replace all internal aliases with the -// aliasee value -// -// Inputs: -// M - A reference to the LLVM module to transform -// -// Outputs: -// M - The transformed LLVM module. -// -// Return value: -// true - The module was modified. -// false - The module was not modified. -// -bool FuncSimplify::runOnModule(Module& M) { - - std::vector toDelete; - for (GlobalAlias &GA : M.aliases()) { - if(!GA.hasInternalLinkage()) - continue; - GA.replaceAllUsesWith(GA.getAliasee()); - toDelete.push_back(&GA); - } - numChanged += toDelete.size(); - - while(!toDelete.empty()) { - GlobalAlias *I = toDelete.back(); - toDelete.pop_back(); - I->eraseFromParent(); - } - - - return true; -} - -// Pass ID variable -char FuncSimplify::ID = 0; - -// Register the pass -static RegisterPass -X("func-simplify", "Delete Aliases"); diff --git a/lib/AssistDS/FuncSpec.cpp b/lib/AssistDS/FuncSpec.cpp deleted file mode 100644 index 3cbaa91ef..000000000 --- a/lib/AssistDS/FuncSpec.cpp +++ /dev/null @@ -1,121 +0,0 @@ -//===-- FuncSpec.cpp - Clone Functions With Constant Function Ptr Args ----===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This pass clones functions that take constant function pointers as arguments -// from some call sites. It changes those call sites to call cloned functions. -// -//===----------------------------------------------------------------------===// -#define DEBUG_TYPE "funcspec" - -#include "assistDS/FuncSpec.h" - -#include "llvm/Transforms/Utils/Cloning.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/Support/FormattedStream.h" -#include "smack/Debug.h" - -#include -#include -#include - -using namespace llvm; - -// Pass statistics -STATISTIC(numCloned, "Number of Functions Cloned in FuncSpec"); -STATISTIC(numReplaced, "Number of Calls Replaced"); -// -// Method: runOnModule() -// -// Description: -// Entry point for this LLVM pass. Search for call sites, that take functions as arguments -// Clone those functions, and pass the clone. -// -// Inputs: -// M - A reference to the LLVM module to transform -// -// Outputs: -// M - The transformed LLVM module. -// -// Return value: -// true - The module was modified. -// false - The module was not modified. -// -bool FuncSpec::runOnModule(Module& M) { - std::map > > cloneSites; - std::map > >, Function* > toClone; - - for (Function &F : M) - if (!F.isDeclaration() && !F.isInterposable()) { - std::vector FPArgs; - for (auto &Arg : F.args()) { - // check if this function has a FunctionType(or a pointer to) argument - if (const PointerType* Ty = dyn_cast(Arg.getType())) { - if (isa(Ty->getElementType())) { - // Store the index of such an argument - FPArgs.push_back(Arg.getArgNo()); - SDEBUG(errs() << "Eligible: " << F.getName().str() << "\n"); - } - } else if (isa(Arg.getType())) { - // Store the index of such an argument - FPArgs.push_back(Arg.getArgNo()); - SDEBUG(errs() << "Eligible: " << F.getName().str() << "\n"); - } - } - // Now find all call sites that it is called from - for (User *U : F.users()) { - if (CallInst* CI = dyn_cast(U)) { - // Check that it is the called value (and not an argument) - if(CI->getCalledValue()->stripPointerCasts() == &F) { - std::vector > Consts; - for (unsigned x = 0; x < FPArgs.size(); ++x) - if (Constant* C = dyn_cast(U->getOperand(FPArgs.at(x) + 1))) { - // If the argument passed, at any of the locations noted earlier - // is a constant function, store the pair - Consts.push_back(std::make_pair(FPArgs.at(x), C)); - } - if (!Consts.empty()) { - // If at least one of the arguments is a constant function, - // we must clone the function. - cloneSites[CI] = Consts; - toClone[std::make_pair(&F, Consts)] = 0; - } - } - } - } - } - - numCloned += toClone.size(); - - for (std::map > >, Function* >::iterator I = toClone.begin(), E = toClone.end(); I != E; ++I) { - // Clone all the functions we need cloned - ValueToValueMapTy VMap; - Function* DirectF = CloneFunction(I->first.first, VMap); - DirectF->setName(I->first.first->getName().str() + "_SPEC"); - DirectF->setLinkage(GlobalValue::InternalLinkage); - I->first.first->getParent()->getFunctionList().push_back(DirectF); - I->second = DirectF; - } - - for (std::map > >::iterator ii = cloneSites.begin(), ee = cloneSites.end(); ii != ee; ++ii) { - // Transform the call sites, to call the clones - Function *OldCallee = cast(ii->first->getCalledValue()); - Function *NewCallee = toClone[std::make_pair(OldCallee, ii->second)]; - ii->first->setCalledFunction(NewCallee); - ++numReplaced; - } - - return true; -} - -// Pass ID variable -char FuncSpec::ID = 0; - -// Register the pass -static RegisterPass -X("funcspec", "Specialize for Function Pointers"); diff --git a/lib/AssistDS/GEPExprArgs.cpp b/lib/AssistDS/GEPExprArgs.cpp deleted file mode 100644 index 289655d05..000000000 --- a/lib/AssistDS/GEPExprArgs.cpp +++ /dev/null @@ -1,187 +0,0 @@ -//===-- GEPExprArg.cpp - Promote args if they come from GEPs -------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Identify GEPs used as arguments to call sites. -// -//===----------------------------------------------------------------------===// -#define DEBUG_TYPE "gepexprargs" - -#include "assistDS/GEPExprArgs.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/Operator.h" -#include "llvm/IR/Use.h" -#include "llvm/IR/GetElementPtrTypeIterator.h" -#include "llvm/Transforms/Utils/Cloning.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/IR/ValueMap.h" -#include "llvm/Support/FormattedStream.h" -#include "smack/Debug.h" -#include -#include - -// Pass statistics -STATISTIC(numSimplified, "Number of Calls Modified"); - -using namespace llvm; - -// -// Method: runOnModule() -// -// Description: -// Entry point for this LLVM pass. -// Clone functions that take GEPs as arguments -// -// Inputs: -// M - A reference to the LLVM module to transform -// -// Outputs: -// M - The transformed LLVM module. -// -// Return value: -// true - The module was modified. -// false - The module was not modified. -// -bool GEPExprArgs::runOnModule(Module& M) { - bool changed; - do { - changed = false; - for (Module::iterator F = M.begin(); F != M.end(); ++F){ - for (Function::iterator B = F->begin(), FE = F->end(); B != FE; ++B) { - for (BasicBlock::iterator I = B->begin(), BE = B->end(); I != BE;) { - CallInst *CI = dyn_cast(I++); - if(!CI) - continue; - - if(CI->hasByValArgument()) - continue; - // if the GEP calls a function, that is externally defined, - // or might be changed, ignore this call site. - Function *F = CI->getCalledFunction(); - - if (!F || (F->isDeclaration() || F->isInterposable())) - continue; - if(F->hasStructRetAttr()) - continue; - if(F->isVarArg()) - continue; - - // find the argument we must replace - Function::arg_iterator ai = F->arg_begin(), ae = F->arg_end(); - unsigned argNum = 1; - for(; argNum < CI->getNumOperands();argNum++, ++ai) { - if(ai->use_empty()) - continue; - if (isa(CI->getOperand(argNum))) - break; - } - - // if no argument was a GEP operator to be changed - if(ai == ae) - continue; - - GEPOperator *GEP = dyn_cast(CI->getOperand(argNum)); - if(!GEP->hasAllConstantIndices()) - continue; - - // Construct the new Type - // Appends the struct Type at the beginning - std::vectorTP; - TP.push_back(GEP->getPointerOperand()->getType()); - for(unsigned c = 1; c < CI->getNumOperands();c++) { - TP.push_back(CI->getOperand(c)->getType()); - } - - //return type is same as that of original instruction - FunctionType *NewFTy = FunctionType::get(CI->getType(), TP, false); - Function *NewF; - numSimplified++; - if(numSimplified > 800) - return true; - - NewF = Function::Create(NewFTy, - GlobalValue::InternalLinkage, - F->getName().str() + ".TEST", - &M); - - auto NI = NewF->arg_begin(); - NI->setName("GEParg"); - ++NI; - - ValueToValueMapTy ValueMap; - - for (auto II = F->arg_begin(); NI != NewF->arg_end(); ++II, ++NI) { - ValueMap[&*II] = &*NI; - NI->setName(II->getName()); - auto ArgAttrs = AttrBuilder(F->getAttributes().getParamAttributes(II->getArgNo() + 1)); - NI->addAttrs(ArgAttrs); - } - NewF->setAttributes(NewF->getAttributes().addAttributes( - F->getContext(), 0, F->getAttributes().getRetAttributes())); - // Perform the cloning. - SmallVector Returns; - CloneFunctionInto(NewF, F, ValueMap, false, Returns); - std::vector fargs; - for (auto &Arg : NewF->args()) { - fargs.push_back(&Arg); - } - - NewF->setAttributes(NewF->getAttributes().addAttributes( - F->getContext(), ~0, F->getAttributes().getFnAttributes())); - //Get the point to insert the GEP instr. - SmallVector Ops(CI->op_begin()+1, CI->op_end()); - Instruction *InsertPoint; - for (Instruction *insrt = &*NewF->front().begin(); - isa(InsertPoint = insrt); ++insrt) {;} - - NI = NewF->arg_begin(); - SmallVector Indices; - Indices.append(GEP->op_begin()+1, GEP->op_end()); - GetElementPtrInst *GEP_new = GetElementPtrInst::Create(nullptr, - cast(NI), - Indices, - "", InsertPoint); - fargs.at(argNum)->replaceAllUsesWith(GEP_new); - unsigned j = argNum + 1; - for(; j < CI->getNumOperands();j++) { - if(CI->getOperand(j) == GEP) - fargs.at(j)->replaceAllUsesWith(GEP_new); - } - - // Get the initial attributes of the call - AttributeList CallPAL = CI->getAttributes(); - AttributeSet RAttrs = CallPAL.getRetAttributes(); - AttributeSet FnAttrs = CallPAL.getFnAttributes(); - - SmallVector Args; - SmallVector ArgAttrs; - Args.push_back(GEP->getPointerOperand()); - ArgAttrs.push_back(AttributeSet()); - for(unsigned j =1;jgetNumOperands();j++) { - Args.push_back(CI->getOperand(j)); - ArgAttrs.push_back(CallPAL.getParamAttributes(j)); - } - - auto NewCallPAL = AttributeList::get(F->getContext(), FnAttrs, RAttrs, ArgAttrs); - CallInst *CallI = CallInst::Create(NewF,Args,"", CI); - CallI->setCallingConv(CI->getCallingConv()); - CallI->setAttributes(NewCallPAL); - CI->replaceAllUsesWith(CallI); - CI->eraseFromParent(); - changed = true; - } - } - } - } while(changed); - return true; -} - - -char GEPExprArgs::ID = 0; -static RegisterPass -X("gep-expr-arg", "Find GEP Exprs passed as args"); diff --git a/lib/AssistDS/IndCloner.cpp b/lib/AssistDS/IndCloner.cpp deleted file mode 100644 index bdc0cd5e5..000000000 --- a/lib/AssistDS/IndCloner.cpp +++ /dev/null @@ -1,167 +0,0 @@ -//===-- IndCloner.cpp - Clone Indirectly Called Functions -----------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This pass clones functions which can be called indirectly and then updates -// direct calls to call the clone. -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "indclone" - -#include "assistDS/IndCloner.h" - -#include "llvm/Transforms/Utils/Cloning.h" -#include "llvm/ADT/Statistic.h" - -#include - -using namespace llvm; - -// Pass ID variable -char IndClone::ID = 0; - -// Register the Indirect Function Cloner Pass -static RegisterPass -X("indclone", "Indirect call cloning"); - -// Pass statistics -STATISTIC(numCloned, "Number of Functions Cloned in IndCloner"); -STATISTIC(numReplaced, "Number of Calls Replaced"); - -// -// Method: runOnModule() -// -// Description: -// Entry point for this LLVM pass. Search for functions which could be called -// indirectly and create clones for them which are only called by direct -// calls. -// -// Inputs: -// M - A reference to the LLVM module to transform. -// -// Outputs: -// M - The transformed LLVM module. -// -// Return value: -// true - The module was modified. -// false - The module was not modified. -// -bool -IndClone::runOnModule(Module& M) { - // Set of functions to clone - std::vector toClone; - - // - // Check all of the functions in the module. If the function could be called - // by an indirect function call, add it to our worklist of functions to - // clone. - // - for (Function &F : M) { - // Flag whether the function should be cloned - bool pleaseCloneTheFunction = false; - - // - // Only clone functions which are defined and cannot be replaced by another - // function by the linker. - // - if (!F.isDeclaration() && !F.isInterposable()) { - for (User *U : F.users()) { - if (!isa(U) && !isa(U)) { - if(!U->use_empty()) - // - // If this function is used for anything other than a direct function - // call, then we want to clone it. - // - pleaseCloneTheFunction = true; - } else { - // - // This is a call instruction, but hold up ranger! We need to make - // sure that the function isn't passed as an argument to *another* - // function. That would make the function usable in an indirect - // function call. - // - for (unsigned index = 1; index < U->getNumOperands(); ++index) { - if (U->getOperand(index)->stripPointerCasts() == &F) { - pleaseCloneTheFunction = true; - break; - } - } - } - - // - // If we've discovered that the function could be used by an indirect - // call site, schedule it for cloning. - // - if (pleaseCloneTheFunction) { - toClone.push_back(&F); - break; - } - } - } - } - - // - // Update the statistics on the number of functions we'll be cloning. - // We only update the statistic if we want to clone one or more functions; - // due to the magic of how statistics work, avoiding assignment prevents it - // from needlessly showing up. - // - if (toClone.size()) - numCloned += toClone.size(); - - // - // Go through the worklist and clone each function. After cloning a - // function, change all direct calls to use the clone instead of using the - // original function. - // - for (unsigned index = 0; index < toClone.size(); ++index) { - // - // Clone the function and give it a name indicating that it is a clone to - // be used for direct function calls. - // - Function * Original = toClone[index]; - ValueToValueMapTy VMap; - Function* DirectF = CloneFunction(Original, VMap); - DirectF->setName(Original->getName() + "_DIRECT"); - - // - // Make the clone internal; external code can use the original function. - // - DirectF->setLinkage(GlobalValue::InternalLinkage); - - // - // Link the cloned function into the set of functions belonging to the - // module. - // - Original->getParent()->getFunctionList().push_back(DirectF); - - // - // Find all uses of the function that use it as a direct call. Change - // them to use the clone. - // - for (Value::user_iterator ui = Original->user_begin(), - ue = Original->user_end(); - ui != ue; ) { - CallInst *CI = dyn_cast(*ui); - ui++; - if (CI) { - if (CI->getCalledFunction() == Original) { - ++numReplaced; - CI->setCalledFunction(DirectF); - } - } - } - } - - // - // Assume that we've cloned at least one function. - // - return true; -} - diff --git a/lib/AssistDS/Int2PtrCmp.cpp b/lib/AssistDS/Int2PtrCmp.cpp deleted file mode 100644 index f06b9267e..000000000 --- a/lib/AssistDS/Int2PtrCmp.cpp +++ /dev/null @@ -1,184 +0,0 @@ -//===-- Int2PtrCmp.cpp - Merge inttoptr/ptrtoint --------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Remove unnecessary inttoptr casts -// Specially ones used in just compares -// Most cases derived from InstCombine -// -//===----------------------------------------------------------------------===// -#define DEBUG_TYPE "int2ptr-cmp" - -#include "assistDS/Int2PtrCmp.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/Support/FormattedStream.h" -#include "smack/Debug.h" -#include "llvm/IR/PatternMatch.h" - -#include -#include -#include - -using namespace llvm; -using namespace PatternMatch; - -// -// Method: runOnModule() -// -// Description: -// Entry point for this LLVM pass. -// Remove unnecessary inttoptr instructions. -// -// Inputs: -// M - A reference to the LLVM module to transform -// -// Outputs: -// M - The transformed LLVM module. -// -// Return value: -// true - The module was modified. -// false - The module was not modified. -// -bool Int2PtrCmp::runOnModule(Module& M) { - TD = &M.getDataLayout(); - for (Module::iterator F = M.begin(); F != M.end(); ++F) { - for (Function::iterator B = F->begin(), FE = F->end(); B != FE; ++B) { - for (BasicBlock::iterator I = B->begin(), BE = B->end(); I != BE;) { - // ptrtoint(inttoptr ty Y) to ty -> Y - if(PtrToIntInst *P2I = dyn_cast(I++)) { - if(IntToPtrInst *I2P = dyn_cast(P2I->getOperand(0))) { - if(I2P->getSrcTy() == P2I->getDestTy()){ - P2I->replaceAllUsesWith(I2P->getOperand(0)); - P2I->eraseFromParent(); - if(I2P->use_empty()) { - // If this is the only use of the cast delete it. - I2P->eraseFromParent(); - } - } - } - } - } - for (BasicBlock::iterator I = B->begin(), BE = B->end(); I != BE;) { - //icmp pred inttoptr(X), null -> icmp pred X 0 - if(ICmpInst *CI = dyn_cast(I++)) { - Value *Op0 = CI->getOperand(0); - Value *Op1 = CI->getOperand(1); - if (Constant *RHSC = dyn_cast(Op1)) { - if (Instruction *LHSI = dyn_cast(Op0)){ - if(LHSI->getOpcode() == Instruction::IntToPtr) { - if (RHSC->isNullValue() && TD && - TD->getIntPtrType(RHSC->getContext(), 0) == - LHSI->getOperand(0)->getType()){ - ICmpInst *CI_new = new ICmpInst(CI, CI->getPredicate(), LHSI->getOperand(0), - Constant::getNullValue(LHSI->getOperand(0)->getType())); - - CI->replaceAllUsesWith(CI_new); - CI->eraseFromParent(); - if(LHSI->use_empty()) { - // If this is the only use of the cast delete it. - LHSI->eraseFromParent(); - } - } - } - } - } - } - } - } - } - - for (Module::iterator F = M.begin(); F != M.end(); ++F) { - for (Function::iterator B = F->begin(), FE = F->end(); B != FE; ++B) { - for (BasicBlock::iterator I = B->begin(), BE = B->end(); I != BE;) { - if(ICmpInst *ICI = dyn_cast(I++)) { - Value *Op0 = ICI->getOperand(0); - Value *Op1 = ICI->getOperand(1); - if (ConstantInt *CI = dyn_cast(Op1)) { - // Since the RHS is a ConstantInt (CI), if the left hand side is an - // instruction, see if that instruction also has constants so that the - // instruction can be folded into the icmp - if (Instruction *LHSI = dyn_cast(Op0)){ - if(LHSI->getOpcode() == Instruction::Or) { - if (!ICI->isEquality() || !CI->isNullValue() || !LHSI->hasOneUse()) - break; - Value *P, *Q, *R; - if (match(LHSI, m_Or(m_PtrToInt(m_Value(P)), m_PtrToInt(m_Value(Q))))) { - // Simplify icmp eq (or (ptrtoint P), (ptrtoint Q)), 0 - // -> and (icmp eq P, null), (icmp eq Q, null). - Value *ICIP = new ICmpInst(ICI, ICI->getPredicate(), P, - Constant::getNullValue(P->getType())); - Value *ICIQ = new ICmpInst(ICI, ICI->getPredicate(), Q, - Constant::getNullValue(Q->getType())); - Instruction *Op; - if (ICI->getPredicate() == ICmpInst::ICMP_EQ) - Op = BinaryOperator::CreateAnd(ICIP, ICIQ,"",ICI); - else - Op = BinaryOperator::CreateOr(ICIP, ICIQ, "", ICI); - ICI->replaceAllUsesWith(Op); - - } else if(match(LHSI, m_Or(m_Or(m_PtrToInt(m_Value(P)), - m_PtrToInt(m_Value(Q))), - m_PtrToInt(m_Value(R))))) { - // Simplify icmp eq (or (or (ptrtoint P), (ptrtoint Q)), ptrtoint(R)), 0 - // -> and (and (icmp eq P, null), (icmp eq Q, null)), (icmp eq R, null). - Value *ICIP = new ICmpInst(ICI, ICI->getPredicate(), P, - Constant::getNullValue(P->getType())); - Value *ICIQ = new ICmpInst(ICI, ICI->getPredicate(), Q, - Constant::getNullValue(Q->getType())); - Value *ICIR = new ICmpInst(ICI, ICI->getPredicate(), R, - Constant::getNullValue(R->getType())); - Instruction *Op; - if (ICI->getPredicate() == ICmpInst::ICMP_EQ) - Op = BinaryOperator::CreateAnd(ICIP, ICIQ,"",ICI); - else - Op = BinaryOperator::CreateOr(ICIP, ICIQ, "", ICI); - - if (ICI->getPredicate() == ICmpInst::ICMP_EQ) - Op = BinaryOperator::CreateAnd(Op, ICIR,"",ICI); - else - Op = BinaryOperator::CreateOr(Op, ICIR, "", ICI); - ICI->replaceAllUsesWith(Op); - - } else if(match(LHSI, m_Or(m_PtrToInt(m_Value(Q)), m_Or( - m_PtrToInt(m_Value(P)), m_PtrToInt(m_Value(R)))))) { - // Simplify icmp eq (or (ptrtoint P), or((ptrtoint Q), ptrtoint(R))), 0 - // -> and (icmp eq P, null), (and (icmp eq Q, null), (icmp eq R, null)). - Value *ICIP = new ICmpInst(ICI, ICI->getPredicate(), P, - Constant::getNullValue(P->getType())); - Value *ICIQ = new ICmpInst(ICI, ICI->getPredicate(), Q, - Constant::getNullValue(Q->getType())); - Value *ICIR = new ICmpInst(ICI, ICI->getPredicate(), R, - Constant::getNullValue(R->getType())); - Instruction *Op; - if (ICI->getPredicate() == ICmpInst::ICMP_EQ) - Op = BinaryOperator::CreateAnd(ICIP, ICIQ,"",ICI); - else - Op = BinaryOperator::CreateOr(ICIP, ICIQ, "", ICI); - - if (ICI->getPredicate() == ICmpInst::ICMP_EQ) - Op = BinaryOperator::CreateAnd(Op, ICIR,"",ICI); - else - Op = BinaryOperator::CreateOr(Op, ICIR, "", ICI); - ICI->replaceAllUsesWith(Op); - } - } - } - } - } - } - } - } - return true; -} - -// Pass ID variable -char Int2PtrCmp::ID = 0; - -// Register the pass -static RegisterPass -X("int2ptrcmp", "Simplify inttoptr/ptrtoint insts"); diff --git a/lib/AssistDS/LICENSE.TXT b/lib/AssistDS/LICENSE.TXT deleted file mode 100644 index dba02fa23..000000000 --- a/lib/AssistDS/LICENSE.TXT +++ /dev/null @@ -1,65 +0,0 @@ -============================================================================== -LLVM Pool Allocator Release License -============================================================================== -University of Illinois/NCSA -Open Source License - -Copyright (c) 2003-2013 University of Illinois at Urbana-Champaign. -All rights reserved. - -Developed by: - - LLVM Team - - University of Illinois at Urbana-Champaign - - http://llvm.cs.uiuc.edu - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal with -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimers. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimers in the - documentation and/or other materials provided with the distribution. - - * Neither the names of the LLVM Team, University of Illinois at - Urbana-Champaign, nor the names of its contributors may be used to - endorse or promote products derived from this Software without specific - prior written permission. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE -SOFTWARE. - -============================================================================== -Copyrights and Licenses for Third Party Software Distributed with LLVM: -============================================================================== -The Automatic Pool Allocation software contains code written by third parties. -Such software will have its own individual LICENSE.TXT file in the directory in -which it appears. This file will describe the copyrights, license, and -restrictions which apply to that code. - -The disclaimer of warranty in the University of Illinois Open Source License -applies to all code in this distribution, and nothing in any of the -other licenses gives permission to use the names of the LLVM Team or the -University of Illinois to endorse or promote products derived from this -Software. - -The following pieces of software have additional or alternate copyrights, -licenses, and/or restrictions: - -Program Directory -------- --------- -Watchdog poolalloc/tools/WatchDog - diff --git a/lib/AssistDS/LoadArgs.cpp b/lib/AssistDS/LoadArgs.cpp deleted file mode 100644 index abd464b59..000000000 --- a/lib/AssistDS/LoadArgs.cpp +++ /dev/null @@ -1,215 +0,0 @@ -//===-- LoadArgs.cpp - Promote args if they came from loads ---------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Identify calls, that are passed arguments that are LoadInsts. -// Pass the original pointer instead. Helps improve some -// context sensitivity. -// -//===----------------------------------------------------------------------===// -#define DEBUG_TYPE "ld-args" - -#include "assistDS/LoadArgs.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/Use.h" -#include "llvm/IR/GetElementPtrTypeIterator.h" -#include "llvm/Transforms/Utils/Cloning.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/IR/ValueMap.h" -#include "llvm/Support/FormattedStream.h" -#include "smack/Debug.h" -#include -#include -#include - -// Pass statistics -STATISTIC(numSimplified, "Number of Calls Modified"); - -using namespace llvm; - -// -// Method: runOnModule() -// -// Description: -// Entry point for this LLVM pass. -// Clone functions that take LoadInsts as arguments -// -// Inputs: -// M - A reference to the LLVM module to transform -// -// Outputs: -// M - The transformed LLVM module. -// -// Return value: -// true - The module was modified. -// false - The module was not modified. -// -bool LoadArgs::runOnModule(Module& M) { - std::map , Function* > fnCache; - bool changed; - do { - changed = false; - for (Module::iterator Func = M.begin(); Func != M.end(); ++Func) { - for (Function::iterator B = Func->begin(), FE = Func->end(); B != FE; ++B) { - for (BasicBlock::iterator I = B->begin(), BE = B->end(); I != BE;) { - CallInst *CI = dyn_cast(I++); - if(!CI) - continue; - - if(CI->hasByValArgument()) - continue; - // if the CallInst calls a function, that is externally defined, - // or might be changed, ignore this call site. - Function *F = CI->getCalledFunction(); - if (!F || (F->isDeclaration() || F->isInterposable())) - continue; - if(F->hasStructRetAttr()) - continue; - if(F->isVarArg()) - continue; - - // find the argument we must replace - Function::arg_iterator ai = F->arg_begin(), ae = F->arg_end(); - unsigned argNum = 0; - for(; argNum < CI->getNumArgOperands();argNum++, ++ai) { - // do not care about dead arguments - if(ai->use_empty()) - continue; - if(F->getAttributes().getParamAttributes(argNum).hasAttribute(Attribute::SExt) || - F->getAttributes().getParamAttributes(argNum).hasAttribute(Attribute::ZExt)) - continue; - if (isa(CI->getArgOperand(argNum))) - break; - } - - // if no argument was a GEP operator to be changed - if(ai == ae) - continue; - - LoadInst *LI = dyn_cast(CI->getArgOperand(argNum)); - Instruction * InsertPt = &(Func->getEntryBlock().front()); - AllocaInst *NewVal = new AllocaInst( - LI->getType(), LI->getPointerAddressSpace(), "", InsertPt); - - StoreInst *Copy = new StoreInst(LI, NewVal); - Copy->insertAfter(LI); - /*if(LI->getParent() != CI->getParent()) - continue; - // Also check that there is no store after the load. - // TODO: Check if the load/store do not alias. - BasicBlock::iterator bii = LI->getParent()->begin(); - Instruction *BII = bii; - while(BII != LI) { - ++bii; - BII = bii; - } - while(BII != CI) { - if(isa(BII)) - break; - ++bii; - BII = bii; - } - if(isa(bii)){ - continue; - }*/ - - // Construct the new Type - // Appends the struct Type at the beginning - std::vectorTP; - for(unsigned c = 0; c < CI->getNumArgOperands();c++) { - if(c == argNum) - TP.push_back(LI->getPointerOperand()->getType()); - TP.push_back(CI->getArgOperand(c)->getType()); - } - - //return type is same as that of original instruction - FunctionType *NewFTy = FunctionType::get(CI->getType(), TP, false); - numSimplified++; - //if(numSimplified > 1000) - //return true; - - Function *NewF; - std::map , Function* >::iterator Test; - Test = fnCache.find(std::make_pair(F, NewFTy)); - if(Test != fnCache.end()) { - NewF = Test->second; - } else { - NewF = Function::Create(NewFTy, - GlobalValue::InternalLinkage, - F->getName().str() + ".TEST", - &M); - - fnCache[std::make_pair(F, NewFTy)] = NewF; - auto NI = NewF->arg_begin(); - - ValueToValueMapTy ValueMap; - - unsigned count = 0; - for (auto II = F->arg_begin(); NI != NewF->arg_end(); ++count, ++NI) { - if(count == argNum) { - NI->setName("LDarg"); - continue; - } - ValueMap[&*II] = &*NI; - NI->setName(II->getName()); - auto ArgAttrs = AttrBuilder(F->getAttributes().getParamAttributes(II->getArgNo() + 1)); - NI->addAttrs(ArgAttrs); - ++II; - } - // Perform the cloning. - SmallVector Returns; - CloneFunctionInto(NewF, F, ValueMap, false, Returns); - std::vector fargs; - for (auto &Arg : NewF->args()) { - fargs.push_back(&Arg); - } - - NewF->setAttributes(NewF->getAttributes().addAttributes( - F->getContext(), 0, F->getAttributes().getRetAttributes())); - NewF->setAttributes(NewF->getAttributes().addAttributes( - F->getContext(), ~0, F->getAttributes().getFnAttributes())); - //Get the point to insert the GEP instr. - Instruction *InsertPoint; - for (auto insrt = NewF->front().begin(); isa(InsertPoint = &*insrt); ++insrt) {;} - LoadInst *LI_new = new LoadInst(fargs.at(argNum), "", InsertPoint); - fargs.at(argNum+1)->replaceAllUsesWith(LI_new); - } - - // Get the initial attributes of the call - AttributeList CallPAL = CI->getAttributes(); - AttributeSet RAttrs = CallPAL.getRetAttributes(); - AttributeSet FnAttrs = CallPAL.getFnAttributes(); - - SmallVector Args; - SmallVector ArgAttrs; - for(unsigned j =0;jgetNumArgOperands();j++) { - if(j == argNum) { - Args.push_back(NewVal); - ArgAttrs.push_back(AttributeSet()); - } - Args.push_back(CI->getArgOperand(j)); - ArgAttrs.push_back(CallPAL.getParamAttributes(j)); - } - - auto NewCallPAL = AttributeList::get(F->getContext(), FnAttrs, RAttrs, ArgAttrs); - CallInst *CallI = CallInst::Create(NewF,Args,"", CI); - CallI->setCallingConv(CI->getCallingConv()); - CallI->setAttributes(NewCallPAL); - CI->replaceAllUsesWith(CallI); - CI->eraseFromParent(); - changed = true; - } - } - } - } while(changed); - return true; -} - -char LoadArgs::ID = 0; -static RegisterPass -X("ld-args", "Find Load Inst passed as args"); diff --git a/lib/AssistDS/SVADevirt.cpp b/lib/AssistDS/SVADevirt.cpp deleted file mode 100644 index 9f7db4cb6..000000000 --- a/lib/AssistDS/SVADevirt.cpp +++ /dev/null @@ -1,255 +0,0 @@ -//===- Devirt.cpp - Devirtualize using the sig match intrinsic in llva ----===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -#define DEBUG_TYPE "devirt" -#include "llvm/IR/Constants.h" -#include "llvm/Transforms/IPO.h" -#include "dsa/CallTargets.h" -#include "llvm/Pass.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/ADT/Statistic.h" - -#include -#include -#include - -using namespace llvm; - -#if 0 - -// -// Function: castTo() -// -// Description: // Given an LLVM value, insert a cast instruction to make it a given type. -// -static inline Value * -castTo (Value * V, const Type * Ty, Instruction * InsertPt) { // - // Don't bother creating a cast if it's already the correct type. - // - if (V->getType() == Ty) - return V; - - // - // If it's a constant, just create a constant expression. - // - if (Constant * C = dyn_cast(V)) { - Constant * CE = ConstantExpr::getCast (C, Ty); - return CE; - } - - // - // Otherwise, insert a cast instruction. - // - return new CastInst::Create(V, Ty, "cast", InsertPt); -} - -namespace { - - static cl::opt - VirtualLimit("devirt-limit", cl::Hidden, cl::init(16), - cl::desc("Maximum number of callees to devirtualize at a call site")); - STATISTIC(FuncAdded, "Number of bounce functions added"); - STATISTIC(CSConvert, "Number of call sites converted"); - - - class Devirtualize : public ModulePass { - - - Function * IndirectFuncFail; - - std::map >, Function*> cache; - int fnum; - - // - // Method: buildBounce() - // - // Description: - // Replaces the given call site with a call to a bounce function. The - // bounce function compares the function pointer to one of the given - // target functions and calls the function directly if the pointer - // matches. - Function* buildBounce (CallSite cs, - std::vector& Targets, - Module& M) { - - Value* ptr = cs.getCalledValue(); - const FunctionType* OrigType = - cast(cast(ptr->getType())->getElementType());; - ++FuncAdded; - - std::vector< const Type *> TP(OrigType->param_begin(), OrigType->param_end()); - TP.insert(TP.begin(), ptr->getType()); - const FunctionType* NewTy = FunctionType::get(OrigType->getReturnType(), TP, false); - Function* F = new Function(NewTy, GlobalValue::InternalLinkage, "devirtbounce", &M); - std::map targets; - - F->arg_begin()->setName("funcPtr"); - std::vector fargs; - for(Function::arg_iterator ai = F->arg_begin(), ae = F->arg_end(); ai != ae; ++ai) - if (ai != F->arg_begin()) { - fargs.push_back(ai); - ai->setName("arg"); - } - - for (std::vector::iterator i = Targets.begin(), e = Targets.end(); - i != e; ++i) { - Function* FL = *i; - BasicBlock* BL = new BasicBlock(FL->getName(), F); - targets[FL] = BL; - - //Make call - Value* call = new CallInst(FL, fargs, "", BL); - - //return correctly - if (OrigType->getReturnType() == Type::VoidTy) - new ReturnInst(0, BL); - else - new ReturnInst(call, BL); - } - - // Create a set of tests that search for the correct function target - // and call it directly. If none of the target functions match, - // call pchk_ind_fail() to note the failure. - - // - // Create the failure basic block. Then, add the following: - // o the terminating instruction - // o the indirect call to the original function - // o a call to phck_ind_fail() - // - BasicBlock* tail = new BasicBlock("fail", F, &F->getEntryBlock()); - Instruction * InsertPt; -#if 0 - InsertPt = new UnreachableInst(tail); -#else - Value* p = F->arg_begin(); - Instruction * realCall = new CallInst (p, fargs, "", tail); - if (OrigType->getReturnType() == Type::VoidTy) - InsertPt = new ReturnInst(0, tail); - else - InsertPt = new ReturnInst(realCall, tail); -#endif - Value * FuncVoidPtr = castTo (p, - PointerType::get(Type::SByteTy), - realCall); - new CallInst (IndirectFuncFail, FuncVoidPtr, "", realCall); - - - // Create basic blocks for valid target functions - for (std::vector::iterator i = Targets.begin(), e = Targets.end(); - i != e; ++i) { - BasicBlock* TB = targets[*i]; - BasicBlock* newB = new BasicBlock("test." + (*i)->getName(), F, &F->getEntryBlock()); - SetCondInst* setcc = new SetCondInst(Instruction::SetEQ, *i, p, "sc", newB); - new BranchInst(TB, tail, setcc, newB); - tail = newB; - } - return F; - } - - public: - static char ID; - Devirtualize() : ModulePass(&ID) {} - - virtual bool runOnModule(Module &M) { - CallTargetFinder* CTF = &getAnalysis(); - - // Get references to functions that are needed in the module - Function* ams = M.getNamedFunction ("llva_assert_match_sig"); - if (!ams) - return false; - - IndirectFuncFail = M.getOrInsertFunction ("pchk_ind_fail", - Type::VoidTy, - PointerType::getUnqual(Type::Int8Ty)); - - std::set safecalls; - std::vector toDelete; - - for (Value::user_iterator ii = ams->user_begin(), ee = ams->user_end(); - ii != ee; ++ii) { - if (CallInst* CI = dyn_cast(*ii)) { - std::cerr << "Found safe call site in " - << CI->getParent()->getParent()->getName() << "\n"; - Value* V = CI->getOperand(1); - toDelete.push_back(CI); - do { - //V->dump(); - safecalls.insert(V); - if (CastInst* CV = dyn_cast(V)) - V = CV->getOperand(0); - else V = 0; - } while (V); - } - } - - for(std::set::iterator i = safecalls.begin(), e = safecalls.end(); - i != e; ++i) { - for (Value::user_iterator uii = (*i)->user_begin(), uie = (*i)->user_end(); - uii != uie; ++uii) { - CallSite cs = CallSite::get(*uii); - bool isSafeCall = cs.getInstruction() && - safecalls.find(cs.getCalledValue()) != safecalls.end(); - if (cs.getInstruction() && !cs.getCalledFunction() && - (isSafeCall || CTF->isComplete(cs))) { - std::vector Targets; - for (std::vector::iterator ii = CTF->begin(cs), ee = CTF->end(cs); - ii != ee; ++ii) - if (!isSafeCall || (*ii)->getType() == cs.getCalledValue()->getType()) - Targets.push_back(*ii); - - if (Targets.size() > 0) { - std::cerr << "Target count: " << Targets.size() << " in " << cs.getInstruction()->getParent()->getParent()->getName() << "\n"; - Function* NF = buildBounce(cs, Targets, M); - if (CallInst* ci = dyn_cast(cs.getInstruction())) { - ++CSConvert; - std::vector Par(ci->op_begin(), ci->op_end()); - CallInst* cn = CallInst::Create(NF, Par.begin(), Par.end(), - ci->getName() + ".dv", ci); - ci->replaceAllUsesWith(cn); - toDelete.push_back(ci); - } else if (InvokeInst* ci = dyn_cast(cs.getInstruction())) { - ++CSConvert; - std::vector Par(ci->op_begin(), ci->op_end()); - InvokeInst* cn = InvokeInst::Create(NF, ci->getNormalDest(), - ci->getUnwindDest(), - Par, ci->getName()+".dv", - ci); - ci->replaceAllUsesWith(cn); - toDelete.push_back(ci); - } - } else //Target size == 0 - std::cerr << "Call site found, but no Targets\n"; - } - } - } - - bool changed = false; - for (std::vector::iterator ii = toDelete.begin(), ee = toDelete.end(); - ii != ee; ++ii) { - changed = true; - (*ii)->eraseFromParent(); - } - return changed; - } - - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired(); - } - - }; - - RegisterPass X("devirt", "Devirtualization"); - -} - -#endif diff --git a/lib/AssistDS/SimplifyGEP.cpp b/lib/AssistDS/SimplifyGEP.cpp deleted file mode 100644 index 6c5d44c5a..000000000 --- a/lib/AssistDS/SimplifyGEP.cpp +++ /dev/null @@ -1,221 +0,0 @@ -//===--------------- SimplifyGEP.cpp - Simplify GEPs types ---------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Simplify GEPs with bitcasts (mostly cloned from InstCombine) -// -//===----------------------------------------------------------------------===// - - -#define DEBUG_TYPE "simplify-gep" - -#include "assistDS/SimplifyGEP.h" -#include "llvm/IR/GetElementPtrTypeIterator.h" -#include "llvm/Support/FormattedStream.h" -#include "smack/Debug.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/DataLayout.h" -#include "llvm/IR/Operator.h" - -#include - -using namespace llvm; - -// -// Method: preprocess() -// -// Description: -// %p = bitcast %p1 to T1 -// gep(%p) ... -// -> -// gep (bitcast %p1 to T1), ... -// -// Inputs: -// M - A reference to the LLVM module to process -// -// Outputs: -// M - The transformed LLVM module. -// -static void preprocess(Module& M) { - for (Module::iterator F = M.begin(); F != M.end(); ++F){ - for (Function::iterator B = F->begin(), FE = F->end(); B != FE; ++B) { - for (BasicBlock::iterator I = B->begin(), BE = B->end(); I != BE; I++) { - if(!(isa(I))) - continue; - GetElementPtrInst *GEP = cast(I); - if(BitCastInst *BI = dyn_cast(GEP->getOperand(0))) { - if(Constant *C = dyn_cast(BI->getOperand(0))) { - GEP->setOperand(0, ConstantExpr::getBitCast(C, BI->getType())); - } - } - } - } - } -} -// -// Method: runOnModule() -// -// Description: -// Entry point for this LLVM pass. -// Find all GEPs, and simplify them. -// -// Inputs: -// M - A reference to the LLVM module to transform -// -// Outputs: -// M - The transformed LLVM module. -// -// Return value: -// true - The module was modified. -// false - The module was not modified. -// -bool SimplifyGEP::runOnModule(Module& M) { - TD = &M.getDataLayout(); - preprocess(M); - for (Module::iterator F = M.begin(); F != M.end(); ++F){ - for (Function::iterator B = F->begin(), FE = F->end(); B != FE; ++B) { - for (BasicBlock::iterator I = B->begin(), BE = B->end(); I != BE; I++) { - if(!(isa(I))) - continue; - GetElementPtrInst *GEP = cast(I); - Value *PtrOp = GEP->getOperand(0); - Value *StrippedPtr = PtrOp->stripPointerCasts(); - // Check if the GEP base pointer is enclosed in a cast - if (StrippedPtr != PtrOp) { - const PointerType *StrippedPtrTy =cast(StrippedPtr->getType()); - bool HasZeroPointerIndex = false; - if (ConstantInt *C = dyn_cast(GEP->getOperand(1))) - HasZeroPointerIndex = C->isZero(); - // Transform: GEP (bitcast [10 x i8]* X to [0 x i8]*), i32 0, ... - // into : GEP [10 x i8]* X, i32 0, ... - // - // Likewise, transform: GEP (bitcast i8* X to [0 x i8]*), i32 0, ... - // into : GEP i8* X, ... - // - // This occurs when the program declares an array extern like "int X[];" - if (HasZeroPointerIndex) { - const PointerType *CPTy = cast(PtrOp->getType()); - if (const ArrayType *CATy = - dyn_cast(CPTy->getElementType())) { - // GEP (bitcast i8* X to [0 x i8]*), i32 0, ... ? - if (CATy->getElementType() == StrippedPtrTy->getElementType()) { - // -> GEP i8* X, ... - SmallVector Idx(GEP->idx_begin()+1, GEP->idx_end()); - GetElementPtrInst *Res = - GetElementPtrInst::Create(nullptr, StrippedPtr, Idx, GEP->getName(), GEP); - Res->setIsInBounds(GEP->isInBounds()); - GEP->replaceAllUsesWith(Res); - continue; - } - - if (const ArrayType *XATy = - dyn_cast(StrippedPtrTy->getElementType())){ - // GEP (bitcast [10 x i8]* X to [0 x i8]*), i32 0, ... ? - if (CATy->getElementType() == XATy->getElementType()) { - // -> GEP [10 x i8]* X, i32 0, ... - // At this point, we know that the cast source type is a pointer - // to an array of the same type as the destination pointer - // array. Because the array type is never stepped over (there - // is a leading zero) we can fold the cast into this GEP. - GEP->setOperand(0, StrippedPtr); - continue; - } - } - } - } else if (GEP->getNumOperands() == 2) { - // Transform things like: - // %t = getelementptr i32* bitcast ([2 x i32]* %str to i32*), i32 %V - // into: %t1 = getelementptr [2 x i32]* %str, i32 0, i32 %V; bitcast - Type *SrcElTy = StrippedPtrTy->getElementType(); - Type *ResElTy=cast(PtrOp->getType())->getElementType(); - if (TD && SrcElTy->isArrayTy() && - TD->getTypeAllocSize(cast(SrcElTy)->getElementType()) == - TD->getTypeAllocSize(ResElTy)) { - Value *Idx[2]; - Idx[0] = Constant::getNullValue(Type::getInt32Ty(GEP->getContext())); - Idx[1] = GEP->getOperand(1); - Value *NewGEP = GetElementPtrInst::Create(nullptr, StrippedPtr, Idx, - GEP->getName(), GEP); - // V and GEP are both pointer types --> BitCast - GEP->replaceAllUsesWith(new BitCastInst(NewGEP, GEP->getType(), GEP->getName(), GEP)); - continue; - } - - // Transform things like: - // getelementptr i8* bitcast ([100 x double]* X to i8*), i32 %tmp - // (where tmp = 8*tmp2) into: - // getelementptr [100 x double]* %arr, i32 0, i32 %tmp2; bitcast - - if (TD && SrcElTy->isArrayTy() && ResElTy->isIntegerTy(8)) { - uint64_t ArrayEltSize = - TD->getTypeAllocSize(cast(SrcElTy)->getElementType()); - - // Check to see if "tmp" is a scale by a multiple of ArrayEltSize. We - // allow either a mul, shift, or constant here. - Value *NewIdx = 0; - ConstantInt *Scale = 0; - if (ArrayEltSize == 1) { - NewIdx = GEP->getOperand(1); - Scale = ConstantInt::get(cast(NewIdx->getType()), 1); - } else if (ConstantInt *CI = dyn_cast(GEP->getOperand(1))) { - NewIdx = ConstantInt::get(CI->getType(), 1); - Scale = CI; - } else if (Instruction *Inst =dyn_cast(GEP->getOperand(1))){ - if (Inst->getOpcode() == Instruction::Shl && - isa(Inst->getOperand(1))) { - ConstantInt *ShAmt = cast(Inst->getOperand(1)); - uint32_t ShAmtVal = ShAmt->getLimitedValue(64); - Scale = ConstantInt::get(cast(Inst->getType()), - 1ULL << ShAmtVal); - NewIdx = Inst->getOperand(0); - } else if (Inst->getOpcode() == Instruction::Mul && - isa(Inst->getOperand(1))) { - Scale = cast(Inst->getOperand(1)); - NewIdx = Inst->getOperand(0); - } - } - - // If the index will be to exactly the right offset with the scale taken - // out, perform the transformation. Note, we don't know whether Scale is - // signed or not. We'll use unsigned version of division/modulo - // operation after making sure Scale doesn't have the sign bit set. - if (ArrayEltSize && Scale && Scale->getSExtValue() >= 0LL && - Scale->getZExtValue() % ArrayEltSize == 0) { - Scale = ConstantInt::get(Scale->getType(), - Scale->getZExtValue() / ArrayEltSize); - if (Scale->getZExtValue() != 1) { - Constant *C = ConstantExpr::getIntegerCast(Scale, NewIdx->getType(), - false /*ZExt*/); - NewIdx = BinaryOperator::Create(BinaryOperator::Mul, NewIdx, C, "idxscale"); - } - - // Insert the new GEP instruction. - Value *Idx[2]; - Idx[0] = Constant::getNullValue(Type::getInt32Ty(GEP->getContext())); - Idx[1] = NewIdx; - Value *NewGEP = GetElementPtrInst::Create(nullptr, StrippedPtr, Idx, - GEP->getName(), GEP); - GEP->replaceAllUsesWith(new BitCastInst(NewGEP, GEP->getType(), GEP->getName(), GEP)); - continue; - } - } - } - } - } - } - } - - return true; -} - -// Pass ID variable -char SimplifyGEP::ID = 0; - -// Register the pass -static RegisterPass -X("simplify-gep", "Simplify GEPs"); diff --git a/lib/AssistDS/SimplifyLoad.cpp b/lib/AssistDS/SimplifyLoad.cpp deleted file mode 100644 index d79dcbc48..000000000 --- a/lib/AssistDS/SimplifyLoad.cpp +++ /dev/null @@ -1,86 +0,0 @@ -//===--------------- SimplifyLoad.cpp - Simplify load insts ---------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// Derived from InstCombine -// -//===----------------------------------------------------------------------===// -#define DEBUG_TYPE "simplifyload" - -#include "assistDS/SimplifyLoad.h" -#include "llvm/Transforms/Utils/Cloning.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/Support/FormattedStream.h" -#include "smack/Debug.h" -#include "llvm/IR/PatternMatch.h" -#include "llvm/IR/DataLayout.h" - -#include -#include -#include - -using namespace llvm; - -// Pass statistic -STATISTIC(numErased, "Number of Instructions Deleted"); - -// -// Method: runOnModule() -// -// Description: -// Entry point for this LLVM pass. Search for insert/extractvalue instructions -// that can be simplified. -// -// Inputs: -// M - A reference to the LLVM module to transform. -// -// Outputs: -// M - The transformed LLVM module. -// -// Return value: -// true - The module was modified. -// false - The module was not modified. -// -bool SimplifyLoad::runOnModule(Module& M) { - // Repeat till no change - bool changed; - do { - changed = false; - for (Module::iterator F = M.begin(); F != M.end(); ++F) { - for (Function::iterator B = F->begin(), FE = F->end(); B != FE; ++B) { - for (BasicBlock::iterator I = B->begin(), BE = B->end(); I != BE;) { - LoadInst *LI = dyn_cast(I++); - if(!LI) - continue; - if(LI->hasOneUse()) { - if(CastInst *CI = dyn_cast(*(LI->use_begin()))) { - if(LI->getType()->isPointerTy()) { - if(ConstantExpr *CE = dyn_cast(LI->getOperand(0))) { - if(const PointerType *PTy = dyn_cast(CE->getOperand(0)->getType())) - if(PTy->getElementType() == CI->getType()) { - LoadInst *LINew = new LoadInst(CE->getOperand(0), "", LI); - CI->replaceAllUsesWith(LINew); - } - } - } - } - } - - - } - } - } - } while(changed); - return (numErased > 0); -} - -// Pass ID variable -char SimplifyLoad::ID = 0; - -// Register the pass -static RegisterPass -X("simplify-load", "Simplify load insts"); diff --git a/lib/AssistDS/StructReturnToPointer.cpp b/lib/AssistDS/StructReturnToPointer.cpp deleted file mode 100644 index 17e1579ee..000000000 --- a/lib/AssistDS/StructReturnToPointer.cpp +++ /dev/null @@ -1,184 +0,0 @@ -//===-------- StructReturnToPointer.cpp ------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// For functions that return structures, -// transform them to return a pointer to the structure instead. -// -//===----------------------------------------------------------------------===// -#define DEBUG_TYPE "struct-ret" - -#include "assistDS/StructReturnToPointer.h" -#include "llvm/IR/Attributes.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/Transforms/Utils/Cloning.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/IR/ValueMap.h" -#include "llvm/Support/FormattedStream.h" -#include "smack/Debug.h" - -#include -#include -#include - -using namespace llvm; - - -// -// Method: runOnModule() -// -// Description: -// Entry point for this LLVM pass. -// If a function returns a struct, make it return -// a pointer to the struct. -// -// Inputs: -// M - A reference to the LLVM module to transform -// -// Outputs: -// M - The transformed LLVM module. -// -// Return value: -// true - The module was modified. -// false - The module was not modified. -// -bool StructRet::runOnModule(Module& M) { - const llvm::DataLayout targetData(&M); - - std::vector worklist; - for (Function &F : M) - if (!F.isInterposable()) { - if(F.isDeclaration()) - continue; - if(F.hasAddressTaken()) - continue; - if(F.getReturnType()->isStructTy()) { - worklist.push_back(&F); - } - } - - while(!worklist.empty()) { - Function *F = worklist.back(); - worklist.pop_back(); - Type *NewArgType = F->getReturnType()->getPointerTo(); - - // Construct the new Type - std::vectorTP; - TP.push_back(NewArgType); - for (Function::arg_iterator ii = F->arg_begin(), ee = F->arg_end(); - ii != ee; ++ii) { - TP.push_back(ii->getType()); - } - - FunctionType *NFTy = FunctionType::get(F->getReturnType(), TP, F->isVarArg()); - - // Create the new function body and insert it into the module. - Function *NF = Function::Create(NFTy, - F->getLinkage(), - F->getName(), &M); - ValueToValueMapTy ValueMap; - auto NI = NF->arg_begin(); - NI->setName("ret"); - ++NI; - for (auto II = F->arg_begin(); II != F->arg_end(); ++II, ++NI) { - ValueMap[&*II] = &*NI; - NI->setName(II->getName()); - AttributeSet attrs = F->getAttributes().getParamAttributes(II->getArgNo() + 1); - if (attrs.hasAttributes()) { - auto AB = AttrBuilder(attrs); - NI->addAttrs(AB); - } - } - // Perform the cloning. - SmallVector Returns; - if (!F->isDeclaration()) - CloneFunctionInto(NF, F, ValueMap, false, Returns); - std::vector fargs; - for (auto &Arg : NF->args()) { - fargs.push_back(&Arg); - } - NF->setAttributes(NF->getAttributes().addAttributes( - M.getContext(), 0, F->getAttributes().getRetAttributes())); - NF->setAttributes(NF->getAttributes().addAttributes( - M.getContext(), ~0, F->getAttributes().getFnAttributes())); - - for (Function::iterator B = NF->begin(), FE = NF->end(); B != FE; ++B) { - for (BasicBlock::iterator I = B->begin(), BE = B->end(); I != BE;) { - ReturnInst * RI = dyn_cast(I++); - if(!RI) - continue; - IRBuilder<> Builder(RI); - if (auto LI = dyn_cast(RI->getOperand(0))) { - Builder.CreateMemCpy( - fargs.at(0), - targetData.getPrefTypeAlignment(fargs.at(0)->getType()), - LI->getPointerOperand(), - targetData.getPrefTypeAlignment(LI->getType()), - targetData.getTypeStoreSize(LI->getType())); - } else if (auto CS = dyn_cast(RI->getReturnValue())) { - StructType* ST = CS->getType(); - // We could store the struct into the allocated space pointed by the first - // argument and then load it once SMACK can handle store inst of structs. - for (unsigned i = 0; i < ST->getNumElements(); ++i) { - std::vector idxs = {ConstantInt::get(Type::getInt32Ty(CS->getContext()),0), - ConstantInt::get(Type::getInt32Ty(CS->getContext()),i)}; - Builder.CreateStore(CS->getAggregateElement(i), - Builder.CreateGEP(fargs.at(0), ArrayRef(idxs))); - } - assert(RI->getNumOperands() == 1 && "Return should only have one operand"); - RI->setOperand(0, UndefValue::get(ST)); - } else - llvm_unreachable("Unexpected struct-type return value."); - } - } - - for(Value::user_iterator ui = F->user_begin(), ue = F->user_end(); - ui != ue; ) { - CallInst *CI = dyn_cast(*ui++); - if(!CI) - continue; - if(CI->getCalledFunction() != F) - continue; - if(CI->hasByValArgument()) - continue; - AllocaInst *AllocaNew = new AllocaInst(F->getReturnType(), 0, "", CI); - SmallVector Args; - SmallVector ArgAttrs; - - // Get the initial attributes of the call - AttributeList CallPAL = CI->getAttributes(); - AttributeSet RAttrs = CallPAL.getRetAttributes(); - AttributeSet FnAttrs = CallPAL.getFnAttributes(); - - Args.push_back(AllocaNew); - ArgAttrs.push_back(AttributeSet()); - for(unsigned j = 0; j < CI->getNumOperands()-1; j++) { - Args.push_back(CI->getOperand(j)); - ArgAttrs.push_back(CallPAL.getParamAttributes(j)); - } - - auto NewCallPAL = AttributeList::get(F->getContext(), FnAttrs, RAttrs, ArgAttrs); - CallInst *CallI = CallInst::Create(NF, Args, "", CI); - CallI->setCallingConv(CI->getCallingConv()); - CallI->setAttributes(NewCallPAL); - LoadInst *LI = new LoadInst(AllocaNew, "", CI); - CI->replaceAllUsesWith(LI); - CI->eraseFromParent(); - } - if(F->use_empty()) - F->eraseFromParent(); - } - return true; -} - -// Pass ID variable -char StructRet::ID = 0; - -// Register the pass -static RegisterPass -X("struct-ret", "Find struct arguments"); diff --git a/lib/AssistDS/TypeChecks.cpp b/lib/AssistDS/TypeChecks.cpp deleted file mode 100644 index 1716a6187..000000000 --- a/lib/AssistDS/TypeChecks.cpp +++ /dev/null @@ -1,2243 +0,0 @@ -//===------------ TypeChecks.cpp - Insert runtime type checks -------------===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This pass inserts checks to enforce type safety during runtime. -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "dsa-type-checks" -#include "assistDS/TypeChecks.h" -#include "llvm/IR/Constants.h" -#include "llvm/Transforms/Utils/Cloning.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/Module.h" -#include "smack/Debug.h" -#include "llvm/IR/InstIterator.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/IR/Intrinsics.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/ADT/Statistic.h" - -#include -#include -#include - -using namespace llvm; - -char TypeChecks::ID = 0; - -static RegisterPass -TC("typechecks", "Insert runtime type checks", false, true); - -// Pass statistics -STATISTIC(numLoadChecks, "Number of Load Insts that need type checks"); -STATISTIC(numStoreChecks, "Number of Store Insts that need type checks"); -STATISTIC(numTypes, "Number of Types used in the module"); - -namespace { - static cl::opt EnablePointerTypeChecks("enable-ptr-type-checks", - cl::desc("Distinguish pointer types in loads"), - cl::Hidden, - cl::init(false)); - static cl::opt DisablePtrCmpChecks("no-ptr-cmp-checks", - cl::desc("Dont instrument cmp statements"), - cl::Hidden, - cl::init(false)); - static cl::opt TrackAllLoads("track-all-loads", - cl::desc("Check at all loads irrespective of use"), - cl::Hidden, - cl::init(false)); -} - -static int tagCounter = 0; -static Type *VoidTy = 0; -static Type *Int8Ty = 0; -static Type *Int32Ty = 0; -static Type *Int64Ty = 0; -static PointerType *VoidPtrTy = 0; - -static Type *TypeTagTy = 0; -static Type *TypeTagPtrTy = 0; - -static Constant *One = 0; -static Constant *Zero = 0; - -static Constant *RegisterArgv; -static Constant *RegisterEnvp; - -static Constant *trackGlobal; -static Constant *trackStoreInst; -static Constant *trackStringInput; -static Constant *trackArray; - -static Constant *trackInitInst; -static Constant *trackUnInitInst; - -static Constant *getTypeTag; -static Constant *checkTypeInst; - -static Constant *copyTypeInfo; -static Constant *setTypeInfo; - -static Constant *setVAInfo; -static Constant *copyVAInfo; -static Constant *checkVAArg; - -unsigned int TypeChecks::getTypeMarker(Type * Ty) { - if(!EnablePointerTypeChecks) { - if(Ty->isPointerTy()) { - Ty = VoidPtrTy; - } - } - if(UsedTypes.find(Ty) == UsedTypes.end()) - UsedTypes[Ty] = UsedTypes.size(); - - assert((UsedTypes.size() < 254) && "Too many types found. Not enough metadata bits"); - return UsedTypes[Ty]; -} - -unsigned int TypeChecks::getTypeMarker(Value *V) { - return getTypeMarker(V->getType()); -} - -unsigned int TypeChecks::getSize(Type *Ty) { - return TD->getTypeStoreSize(Ty); -} - -Constant *TypeChecks::getSizeConstant(Type *Ty) { - return (ConstantInt::get(Int64Ty, getSize(Ty))); -} - -static Constant *getTagCounter() { - return ConstantInt::get(Int32Ty, tagCounter++); -} - -Constant *TypeChecks::getTypeMarkerConstant(Value * V) { - return ConstantInt::get(TypeTagTy, getTypeMarker(V)); -} - -Constant *TypeChecks::getTypeMarkerConstant(Type *T) { - return ConstantInt::get(TypeTagTy, getTypeMarker(T)); -} - -static inline Value * -castTo (Value * V, Type * Ty, std::string Name, Instruction * InsertPt) { - // - // Don't bother creating a cast if it's already the correct type. - // - if (V->getType() == Ty) - return V; - - // - // If it's a constant, just create a constant expression. - // - if (Constant * C = dyn_cast(V)) { - Constant * CE = ConstantExpr::getZExtOrBitCast (C, Ty); - return CE; - } - - // - // Otherwise, insert a cast instruction. - // - return CastInst::CreateZExtOrBitCast (V, Ty, Name, InsertPt); -} - -bool TypeChecks::runOnModule(Module &M) { - bool modified = false; // Flags whether we modified the module. - bool transformIndirectCalls = true; - - TD = &M.getDataLayout(); - addrAnalysis = &getAnalysis(); - - // Create the necessary prototypes - VoidTy = IntegerType::getVoidTy(M.getContext()); - Int8Ty = IntegerType::getInt8Ty(M.getContext()); - Int32Ty = IntegerType::getInt32Ty(M.getContext()); - Int64Ty = IntegerType::getInt64Ty(M.getContext()); - VoidPtrTy = PointerType::getUnqual(Int8Ty); - - TypeTagTy = Int8Ty; - TypeTagPtrTy = PointerType::getUnqual(TypeTagTy); - - One = ConstantInt::get(Int64Ty, 1); - Zero = ConstantInt::get(Int64Ty, 0); - - // Add prototypes for the dynamic type checking functions - initRuntimeCheckPrototypes(M); - - UsedTypes.clear(); // Reset if run multiple times. - VAArgFunctions.clear(); - ByValFunctions.clear(); - AddressTakenFunctions.clear(); - - // Only works for whole program analysis - Function *MainF = M.getFunction("main"); - if (MainF == 0 || MainF->isDeclaration()) { - llvm_unreachable("No main function found"); - return false; - } - - // Insert the shadow initialization function. - modified |= initShadow(M); - - // Record argv/envp - modified |= visitMain(M, *MainF); - - // Recognize special cases - for (Module::iterator MI = M.begin(), ME = M.end(); MI != ME; ++MI) { - Function &F = *MI; - if(F.isDeclaration()) { - if(addrAnalysis->hasAddressTaken(&F)) - transformIndirectCalls = false; - - continue; - } - - std::string name = F.getName(); - if (strncmp(name.c_str(), "tc.", 3) == 0) continue; - if (strncmp(name.c_str(), "main", 4) == 0) continue; - - // Iterate and find all byval functions - bool hasByValArg = false; - for (Function::arg_iterator I = F.arg_begin(); I != F.arg_end(); ++I) { - if (I->hasByValAttr()) { - hasByValArg = true; - break; - } - } - if(hasByValArg) { - ByValFunctions.push_back(&F); - } - - // Iterate and find all address taken functions - if(addrAnalysis->hasAddressTaken(&F)) { - AddressTakenFunctions.push_back(&F); - } - - // Iterate and find all varargs functions - if(F.isVarArg()) { - VAArgFunctions.push_back(&F); - continue; - } - } - - // Modify all byval functions - while(!ByValFunctions.empty()) { - Function *F = ByValFunctions.back(); - ByValFunctions.pop_back(); - modified |= visitByValFunction(M, *F); - } - - // Modify all the var arg functions - while(!VAArgFunctions.empty()) { - Function *F = VAArgFunctions.back(); - VAArgFunctions.pop_back(); - assert(F->isVarArg()); - modified |= visitVarArgFunction(M, *F); - } - - // Modify all the address taken functions - if(transformIndirectCalls) { - while(!AddressTakenFunctions.empty()) { - Function *F = AddressTakenFunctions.back(); - AddressTakenFunctions.pop_back(); - if(F->isVarArg()) - continue; - visitAddressTakenFunction(M, *F); - } - } - - for (Module::iterator MI = M.begin(), ME = M.end(); MI != ME; ++MI) { - Function &F = *MI; - if(F.isDeclaration()) - continue; - DominatorTree & DT = getAnalysis(F).getDomTree(); - std::deque Worklist; - Worklist.push_back (DT.getRootNode()); - while(Worklist.size()) { - DomTreeNode * Node = Worklist.front(); - Worklist.pop_front(); - BasicBlock *BB = Node->getBlock(); - for (BasicBlock::iterator bi = BB->begin(); bi != BB->end(); ++bi) { - Instruction &I = *bi; - if (StoreInst *SI = dyn_cast(&I)) { - modified |= visitStoreInst(M, *SI); - } else if (LoadInst *LI = dyn_cast(&I)) { - modified |= visitLoadInst(M, *LI); - } else if (CallInst *CI = dyn_cast(&I)) { - modified |= visitCallInst(M, *CI); - } else if (InvokeInst *II = dyn_cast(&I)) { - modified |= visitInvokeInst(M, *II); - } else if (AllocaInst *AI = dyn_cast(&I)) { - modified |= visitAllocaInst(M, *AI); - } else if (VAArgInst *VI = dyn_cast(&I)) { - modified |= visitVAArgInst(M, *VI); - } - } - Worklist.insert(Worklist.end(), Node->begin(), Node->end()); - } - - // Loop over all of the instructions in the function, - // adding instrumentation where needed. - /*for (inst_iterator II = inst_begin(F), IE = inst_end(F); II != IE;++II) { - Instruction &I = *II; - if (StoreInst *SI = dyn_cast(&I)) { - modified |= visitStoreInst(M, *SI); - } else if (LoadInst *LI = dyn_cast(&I)) { - modified |= visitLoadInst(M, *LI); - } else if (CallInst *CI = dyn_cast(&I)) { - modified |= visitCallInst(M, *CI); - } else if (InvokeInst *II = dyn_cast(&I)) { - modified |= visitInvokeInst(M, *II); - } else if (AllocaInst *AI = dyn_cast(&I)) { - modified |= visitAllocaInst(M, *AI); - } else if (VAArgInst *VI = dyn_cast(&I)) { - modified |= visitVAArgInst(M, *VI); - } - }*/ - } - - // visit all the indirect call sites - if(transformIndirectCalls) { - std::set::iterator II = IndCalls.begin(); - for(; II != IndCalls.end();) { - Instruction *I = *II++; - modified |= visitIndirectCallSite(M,I); - } - } - - // visit all the uses of the address taken functions and var arg functions and modify if - // not being passed to external code - std::map::iterator FI = IndFunctionsMap.begin(), FE = IndFunctionsMap.end(); - for(;FI!=FE;++FI) { - Function *F = FI->first; - - Constant *CNew = ConstantExpr::getBitCast(FI->second, F->getType()); - - std::set toReplace; - for(Function::user_iterator User = F->user_begin(); - User != F->user_end();++User) { - toReplace.insert(*User); - } - for(std::set::iterator userI = toReplace.begin(); userI != toReplace.end(); ++userI) { - llvm::User * user = *userI; - if(Constant *C = dyn_cast(user)) { - if(!isa(C)) { - bool changeUse = true; - for(Value::user_iterator II = user->user_begin(); - II != user->user_end(); II++) { - if(CallInst *CI = dyn_cast(*II)) - if(CI->getCalledFunction()) { - if(CI->getCalledFunction()->isDeclaration()) - changeUse = false; - } - } - if(!changeUse) - continue; - std::vector ReplaceWorklist; - for (User::op_iterator use = user->op_begin(); - use != user->op_end(); - ++use) { - if (use->get() == F) { - ReplaceWorklist.push_back (use); - } - } - - if (ReplaceWorklist.size() > 0) { - C->handleOperandChange(F, CNew); - } - continue; - } - } - if(CallInst *CI = dyn_cast(user)) { - if(CI->getCalledFunction()) { - if(CI->getCalledFunction()->isDeclaration()) - continue; - } - } - user->replaceUsesOfWith(F, CNew); - } - } - - // remove redundant checks, caused due to insturmenting uses of loads - // Remove a check if it is dominated by another check for the same instruction - optimizeChecks(M); - - // add a global that contains the mapping from metadata to strings - addTypeMap(M); - - // Update stats - numTypes += UsedTypes.size(); - - return modified; -} - -void TypeChecks::initRuntimeCheckPrototypes(Module &M) { - - RegisterArgv = M.getOrInsertFunction("trackArgvType", - VoidTy, - Int32Ty, /*argc */ - VoidPtrTy->getPointerTo() /*argv*/); - - RegisterEnvp = M.getOrInsertFunction("trackEnvpType", - VoidTy, - VoidPtrTy->getPointerTo() /*envp*/); - - trackGlobal = M.getOrInsertFunction("trackGlobal", - VoidTy, - VoidPtrTy,/*ptr*/ - TypeTagTy,/*type*/ - Int64Ty,/*size*/ - Int32Ty /*tag*/); - - trackArray = M.getOrInsertFunction("trackArray", - VoidTy, - VoidPtrTy,/*ptr*/ - Int64Ty,/*size*/ - Int64Ty,/*count*/ - Int32Ty /*tag*/); - - trackInitInst = M.getOrInsertFunction("trackInitInst", - VoidTy, - VoidPtrTy,/*ptr*/ - Int64Ty,/*size*/ - Int32Ty /*tag*/); - - trackUnInitInst = M.getOrInsertFunction("trackUnInitInst", - VoidTy, - VoidPtrTy,/*ptr*/ - Int64Ty,/*size*/ - Int32Ty /*tag*/); - - trackStoreInst = M.getOrInsertFunction("trackStoreInst", - VoidTy, - VoidPtrTy,/*ptr*/ - TypeTagTy,/*type*/ - Int64Ty,/*size*/ - Int32Ty /*tag*/); - getTypeTag = M.getOrInsertFunction("getTypeTag", - VoidTy, - VoidPtrTy, /*ptr*/ - Int64Ty, /*size*/ - TypeTagPtrTy, /*dest for type tag*/ - Int32Ty /*tag*/); - checkTypeInst = M.getOrInsertFunction("checkType", - VoidTy, - TypeTagTy,/*type*/ - Int64Ty,/*size*/ - TypeTagPtrTy,/*ptr to metadata*/ - VoidPtrTy,/*ptr*/ - Int32Ty /*tag*/); - setTypeInfo = M.getOrInsertFunction("setTypeInfo", - VoidTy, - VoidPtrTy,/*dest ptr*/ - TypeTagPtrTy,/*metadata*/ - Int64Ty,/*size*/ - TypeTagTy, - VoidPtrTy, /*src ptr*/ - Int32Ty /*tag*/); - copyTypeInfo = M.getOrInsertFunction("copyTypeInfo", - VoidTy, - VoidPtrTy,/*dest ptr*/ - VoidPtrTy,/*src ptr*/ - Int64Ty,/*size*/ - Int32Ty /*tag*/); - trackStringInput = M.getOrInsertFunction("trackStringInput", - VoidTy, - VoidPtrTy, - Int32Ty); - setVAInfo = M.getOrInsertFunction("setVAInfo", - VoidTy, - VoidPtrTy,/*va_list ptr*/ - Int64Ty,/*total num of elements in va_list */ - TypeTagPtrTy,/*ptr to metadta*/ - Int32Ty /*tag*/); - copyVAInfo = M.getOrInsertFunction("copyVAInfo", - VoidTy, - VoidPtrTy,/*dst va_list*/ - VoidPtrTy,/*src va_list */ - Int32Ty /*tag*/); - checkVAArg = M.getOrInsertFunction("checkVAArgType", - VoidTy, - VoidPtrTy,/*va_list ptr*/ - TypeTagTy,/*type*/ - Int32Ty /*tag*/); - -} - -// Delete checks, if it is dominated by another check for the same value. -// We might get multiple checks on a path, if there are multiple uses of -// a load inst. - -void TypeChecks::optimizeChecks(Module &M) { - for (Module::iterator MI = M.begin(), ME = M.end(); MI != ME; ++MI) { - Function &F = *MI; - if(F.isDeclaration()) - continue; - DominatorTree & DT = getAnalysis(F).getDomTree(); - std::deque Worklist; - Worklist.push_back (DT.getRootNode()); - while(Worklist.size()) { - DomTreeNode * Node = Worklist.front(); - Worklist.pop_front(); - BasicBlock *BB = Node->getBlock(); - for (BasicBlock::iterator bi = BB->begin(); bi != BB->end(); ++bi) { - CallInst *CI = dyn_cast(bi); - if(!CI) - continue; - if(CI->getCalledFunction() != checkTypeInst) - continue; - std::listtoDelete; - for(Value::user_iterator User = CI->getOperand(3)->user_begin(); User != CI->getOperand(3)->user_end(); ++User) { - CallInst *CI2 = dyn_cast(*User); - if(!CI2) - continue; - if(CI2 == CI) - continue; - // Check that they are refering to the same pointer - if(CI->getOperand(4) != CI2->getOperand(4)) - continue; - // Check that they are using the same metadata for comparison. - if(CI->getOperand(3) != CI2->getOperand(3)) - continue; - if(!DT.dominates(CI, CI2)) - continue; - // if CI, dominates CI2, delete CI2 - toDelete.push_back(CI2); - } - while(!toDelete.empty()) { - Instruction *I = toDelete.back(); - toDelete.pop_back(); - I->eraseFromParent(); - } - } - Worklist.insert(Worklist.end(), Node->begin(), Node->end()); - } - } - for (Module::iterator MI = M.begin(), ME = M.end(); MI != ME; ++MI) { - Function &F = *MI; - if(F.isDeclaration()) - continue; - DominatorTree & DT = getAnalysis(F).getDomTree(); - LoopInfo & LI = getAnalysis(F).getLoopInfo(); - std::deque Worklist; - Worklist.push_back (DT.getRootNode()); - while(Worklist.size()) { - DomTreeNode * Node = Worklist.front(); - Worklist.pop_front(); - Worklist.insert(Worklist.end(), Node->begin(), Node->end()); - BasicBlock *BB = Node->getBlock(); - Loop *L = LI.getLoopFor(BB); - if(!L) - continue; - if(!L->getLoopPreheader()) - continue; - for (BasicBlock::iterator bi = BB->begin(); bi != BB->end(); ) { - CallInst *CI = dyn_cast(bi++); - if(!CI) - continue; - if(CI->getCalledFunction() != checkTypeInst) - continue; - bool hoist = true; - // The instruction is loop invariant if all of its operands are loop-invariant - for (unsigned i = 1, e = CI->getNumOperands(); i != e; ++i) - if (!L->isLoopInvariant(CI->getOperand(i))) - hoist = false; - - if(hoist) { - CI->removeFromParent(); - L->getLoopPreheader()->getInstList().insert(L->getLoopPreheader()->getTerminator()->getIterator(), CI); - } - } - } - } -} - -// add a global that has the metadata -> typeString mapping -void TypeChecks::addTypeMap(Module &M) { - - // Declare the type of the global - ArrayType* AType = ArrayType::get(VoidPtrTy, UsedTypes.size() + 1); - std::vector Values; - Values.reserve(UsedTypes.size() + 1); - - // Declare indices useful for creating a GEP - std::vector Indices; - Indices.push_back(ConstantInt::get(Int32Ty,0)); - Indices.push_back(ConstantInt::get(Int32Ty,0)); - - // Add an entry for uninitialized(Type Number = 0) - - Constant *CA = ConstantDataArray::getString(M.getContext(), - "UNINIT", true); - GlobalVariable *GV = new GlobalVariable(M, - CA->getType(), - true, - GlobalValue::ExternalLinkage, - CA, - ""); - GV->setInitializer(CA); - Constant *C = ConstantExpr::getGetElementPtr(nullptr,GV,Indices); - Values[0] = C; - - // For each used type, create a new entry. - // Also add these strings to the Values list - std::map::iterator TI = UsedTypes.begin(), - TE = UsedTypes.end(); - for(;TI!=TE; ++TI) { - std::string *type = new std::string(); - llvm::raw_string_ostream *test = new llvm::raw_string_ostream(*type); - - *test << TI->first; - //WriteTypeSymbolic(*test, TI->first, &M); - Constant *CA = ConstantDataArray::getString(M.getContext(), - test->str(), true); - GlobalVariable *GV = new GlobalVariable(M, - CA->getType(), - true, - GlobalValue::ExternalLinkage, - CA, - ""); - GV->setInitializer(CA); - Constant *C = ConstantExpr::getGetElementPtr(nullptr,GV, Indices); - Values[TI->second]= C; - } - - new GlobalVariable(M, - AType, - true, - GlobalValue::ExternalLinkage, - ConstantArray::get(AType,Values), - "typeNames" - ); -} - -// For each address taken function, create a clone -// that takes 2 extra arguments(same as a var arg function). -// Modify call sites. -bool TypeChecks::visitAddressTakenFunction(Module &M, Function &F) { - // Clone function - // 1. Create the new argument types vector - std::vector TP; - TP.push_back(Int64Ty); // for count - TP.push_back(VoidPtrTy); // for MD - for(Function::arg_iterator I = F.arg_begin(); I !=F.arg_end(); ++I) { - TP.push_back(I->getType()); - } - - // 2. Create the new function prototype - FunctionType *NewFTy = FunctionType::get(F.getReturnType(), - TP, - false); - Function *NewF = Function::Create(NewFTy, - GlobalValue::InternalLinkage, - F.getName().str() + ".mod", - &M); - - // 3. Set the mapping for the args - auto NI = NewF->arg_begin(); - ValueToValueMapTy ValueMap; - NI->setName("TotalCount"); - NI++; - NI->setName("MD"); - NI++; - for(auto II = F.arg_begin(); NI!=NewF->arg_end(); ++II, ++NI) { - // Each new argument maps to the argument in the old function - // For each of these also copy attributes - ValueMap[&*II] = &*NI; - NI->setName(II->getName()); - AttrBuilder AB{F.getAttributes().getParamAttributes(II->getArgNo()+1)}; - NI->addAttrs(AB); - } - - // 4. Copy over attributes for the function - NewF->setAttributes(NewF->getAttributes() - .addAttributes(M.getContext(), 0, F.getAttributes().getRetAttributes())); - NewF->setAttributes(NewF->getAttributes() - .addAttributes(M.getContext(), ~0, F.getAttributes().getFnAttributes())); - - // 5. Perform the cloning - SmallVectorReturns; - // TODO: Review the boolean flag here - CloneFunctionInto(NewF, &F, ValueMap, true, Returns); - // Store in the map of original -> cloned function - IndFunctionsMap[&F] = NewF; - - std::vectortoDelete; - // Find all uses of the function - for(Value::user_iterator ui = F.user_begin(), ue = F.user_end(); - ui != ue;++ui) { - if(InvokeInst *II = dyn_cast(*ui)) { - if(II->getCalledValue()->stripPointerCasts() != &F) - continue; - std::vector Args; - inst_iterator InsPt = inst_begin(II->getParent()->getParent()); - unsigned int i; - unsigned int NumArgs = II->getNumOperands() - 3; - Value *NumArgsVal = ConstantInt::get(Int32Ty, NumArgs); - AllocaInst *AI = new AllocaInst(TypeTagTy, 0, NumArgsVal, "", &*InsPt); - // set the metadata for the varargs in AI - for(i = 3; i getNumOperands(); i++) { - Value *Idx[1]; - Idx[0] = ConstantInt::get(Int32Ty, i - 3 ); - // For each vararg argument, also add its type information - GetElementPtrInst *GEP = GetElementPtrInst::CreateInBounds(AI,Idx, "", II); - Constant *C = getTypeMarkerConstant(II->getOperand(i)); - new StoreInst(C, GEP, II); - } - - // As the first argument pass the number of var_arg arguments - Args.push_back(ConstantInt::get(Int64Ty, NumArgs)); - Args.push_back(AI); - for(i = 3 ;i < II->getNumOperands(); i++) { - // Add the original argument - Args.push_back(II->getOperand(i)); - } - - // Create the new call - InvokeInst *II_New = InvokeInst::Create(NewF, - II->getNormalDest(), - II->getUnwindDest(), - Args, - "", II); - II->replaceAllUsesWith(II_New); - toDelete.push_back(II); - } - // Check for call sites - else if(CallInst *CI = dyn_cast(*ui)) { - if(CI->getCalledValue()->stripPointerCasts() != &F) - continue; - std::vector Args; - unsigned int i; - unsigned int NumArgs = CI->getNumOperands() - 1; - inst_iterator InsPt = inst_begin(CI->getParent()->getParent()); - Value *NumArgsVal = ConstantInt::get(Int32Ty, NumArgs); - AllocaInst *AI = new AllocaInst(TypeTagTy, 0, NumArgsVal, "", &*InsPt); - // set the metadata for the varargs in AI - for(i = 1; i getNumOperands(); i++) { - Value *Idx[1]; - Idx[0] = ConstantInt::get(Int32Ty, i - 1 ); - // For each vararg argument, also add its type information - GetElementPtrInst *GEP = GetElementPtrInst::CreateInBounds(AI,Idx, "", CI); - Constant *C = getTypeMarkerConstant(CI->getOperand(i)); - new StoreInst(C, GEP, CI); - } - - // As the first argument pass the number of var_arg arguments - Args.push_back(ConstantInt::get(Int64Ty, NumArgs)); - Args.push_back(AI); - for(i = 1 ;i < CI->getNumOperands(); i++) { - // Add the original argument - Args.push_back(CI->getOperand(i)); - } - - // Create the new call - CallInst *CI_New = CallInst::Create(NewF, Args, "", CI); - CI->replaceAllUsesWith(CI_New); - toDelete.push_back(CI); - } - } - while(!toDelete.empty()) { - Instruction *I = toDelete.back(); - toDelete.pop_back(); - I->eraseFromParent(); - } - - return true; -} - -// Transform Variable Argument functions, by also passing -// the relavant metadata info -bool TypeChecks::visitVarArgFunction(Module &M, Function &F) { - if(F.hasInternalLinkage()) { - return visitInternalVarArgFunction(M, F); - } - - // create internal clone - ValueToValueMapTy VMap; - Function *F_clone = CloneFunction(&F, VMap); - F_clone->setName(F.getName().str() + "internal"); - F.setLinkage(GlobalValue::InternalLinkage); - F.getParent()->getFunctionList().push_back(F_clone); - F.replaceAllUsesWith(F_clone); - return visitInternalVarArgFunction(M, *F_clone); -} - -// each vararg function is modified so that the first -// argument is the number of arguments passed in, -// and the second is a pointer to a metadata array, -// containing type information for each of the arguments - -// These are read and stored at the beginning of the function. - -// We keep a counter for the number of arguments accessed -// from the va_list(Counter). It is incremented and -// checked on every va_arg access. It is initialized to zero. -// It is also reset to zero on a call to va_start. - -// Similiarly we check type on every va_arg access. - -// Aside from this, this function also transforms all -// callsites of the var_arg function. - -bool TypeChecks::visitInternalVarArgFunction(Module &M, Function &F) { - - // Clone function - // 1. Create the new argument types vector - std::vector TP; - TP.push_back(Int64Ty); // for count - TP.push_back(TypeTagPtrTy); // for MD - for(Function::arg_iterator I = F.arg_begin(); I !=F.arg_end(); ++I) { - TP.push_back(I->getType()); - } - - // 2. Create the new function prototype - FunctionType *NewFTy = FunctionType::get(F.getReturnType(), - TP, - true); - Function *NewF = Function::Create(NewFTy, - GlobalValue::InternalLinkage, - F.getName().str() + ".vararg", - &M); - - // 3. Set the mapping for the args - auto NI = NewF->arg_begin(); - ValueToValueMapTy ValueMap; - NI->setName("TotalArgCount"); - NI++; - NI->setName("MD"); - NI++; - for(auto II = F.arg_begin(); NI!=NewF->arg_end(); ++II, ++NI) { - // Each new argument maps to the argument in the old function - // For each of these also copy attributes - ValueMap[&*II] = &*NI; - NI->setName(II->getName()); - AttrBuilder AB{F.getAttributes().getParamAttributes(II->getArgNo()+1)}; - NI->addAttrs(AB); - } - - // 4. Copy over attributes for the function - NewF->setAttributes(NewF->getAttributes() - .addAttributes(M.getContext(), 0, F.getAttributes().getRetAttributes())); - NewF->setAttributes(NewF->getAttributes() - .addAttributes(M.getContext(), ~0, F.getAttributes().getFnAttributes())); - - // 5. Perform the cloning - SmallVectorReturns; - // TODO: Review the boolean flag here - CloneFunctionInto(NewF, &F, ValueMap, true, Returns); - - - // Store the information - inst_iterator InsPt = inst_begin(NewF); - auto NII = NewF->arg_begin(); - // Subtract the number of initial arguments - Constant *InitialArgs = ConstantInt::get(Int64Ty, F.arg_size()); - Instruction *NewValue = BinaryOperator::Create(BinaryOperator::Sub, - &*NII, - InitialArgs, - "varargs", - &*InsPt); - NII++; - - // Increment by the number of Initial Args, so as to not read the metadata - //for those. - Value *Idx[1]; - Idx[0] = InitialArgs; - // For each vararg argument, also add its type information - GetElementPtrInst *GEP = GetElementPtrInst::CreateInBounds(&*NII,Idx, "", &*InsPt); - // visit all VAStarts and initialize the counter - for (Function::iterator B = NewF->begin(), FE = NewF->end(); B != FE; ++B) { - for (BasicBlock::iterator I = B->begin(), BE = B->end(); I != BE;I++) { - CallInst *CI = dyn_cast(I); - if(!CI) - continue; - Function *CalledF = dyn_cast(CI->getCalledFunction()); - if(!CalledF) - continue; - if(!CalledF->isIntrinsic()) - continue; - if(CalledF->getIntrinsicID() != Intrinsic::vastart) - continue; - // Reinitialize the counter - Value *BCI = castTo(CI->getArgOperand(0), VoidPtrTy, "", CI); - std::vector Args; - Args.push_back(BCI); - Args.push_back(NewValue); - Args.push_back(GEP); - Args.push_back(getTagCounter()); - CallInst::Create(setVAInfo, Args, "", CI); - } - } - - // Find all va_copy calls - for (Function::iterator B = NewF->begin(), FE = NewF->end(); B != FE; ++B) { - for (BasicBlock::iterator I = B->begin(), BE = B->end(); I != BE;I++) { - CallInst *CI = dyn_cast(I); - if(!CI) - continue; - Function *CalledF = dyn_cast(CI->getCalledFunction()); - if(!CalledF) - continue; - if(!CalledF->isIntrinsic()) - continue; - if(CalledF->getIntrinsicID() != Intrinsic::vacopy) - continue; - Value *BCI_Src = castTo(CI->getArgOperand(1), VoidPtrTy, "", CI); - Value *BCI_Dest = castTo(CI->getArgOperand(0), VoidPtrTy, "", CI); - std::vector Args; - Args.push_back(BCI_Dest); - Args.push_back(BCI_Src); - Args.push_back(getTagCounter()); - CallInst::Create(copyVAInfo, Args, "", CI); - } - } - - std::vectortoDelete; - // Find all uses of the function - for(Value::user_iterator ui = F.user_begin(), ue = F.user_end(); - ui != ue;ui ++) { - - // Check for call sites - if(InvokeInst *II = dyn_cast(*ui)) { - std::vector Args; - inst_iterator InsPt = inst_begin(II->getParent()->getParent()); - unsigned int i; - unsigned int NumArgs = II->getNumOperands() - 3; - Value *NumArgsVal = ConstantInt::get(Int32Ty, NumArgs); - AllocaInst *AI = new AllocaInst(TypeTagTy, 0, NumArgsVal, "", &*InsPt); - // set the metadata for the varargs in AI - for(i = 3; i getNumOperands(); i++) { - Value *Idx[1]; - Idx[0] = ConstantInt::get(Int32Ty, i - 3 ); - // For each vararg argument, also add its type information - GetElementPtrInst *GEP = GetElementPtrInst::CreateInBounds(AI, Idx, "", II); - Constant *C = getTypeMarkerConstant(II->getOperand(i)); - new StoreInst(C, GEP, II); - } - - // As the first argument pass the number of var_arg arguments - Args.push_back(ConstantInt::get(Int64Ty, NumArgs)); - Args.push_back(AI); - for(i = 3 ;i < II->getNumOperands(); i++) { - // Add the original argument - Args.push_back(II->getOperand(i)); - } - - // Create the new call - InvokeInst *II_New = InvokeInst::Create(NewF, - II->getNormalDest(), - II->getUnwindDest(), - Args, - "", II); - II->replaceAllUsesWith(II_New); - toDelete.push_back(II); - } else if (CallInst *CI = dyn_cast(*ui)) { - std::vector Args; - inst_iterator InsPt = inst_begin(CI->getParent()->getParent()); - unsigned int i; - unsigned int NumArgs = CI->getNumArgOperands(); - Value *NumArgsVal = ConstantInt::get(Int32Ty, NumArgs); - AllocaInst *AI = new AllocaInst(TypeTagTy, 0, NumArgsVal, "", &*InsPt); - // set the metadata for the varargs in AI - for(i = 0; i getNumArgOperands(); i++) { - Value *Idx[1]; - Idx[0] = ConstantInt::get(Int32Ty, i); - // For each vararg argument, also add its type information - GetElementPtrInst *GEP = GetElementPtrInst::CreateInBounds(AI,Idx, "", CI); - Constant *C = getTypeMarkerConstant(CI->getArgOperand(i)); - new StoreInst(C, GEP, CI); - } - - // As the first argument pass the number of var_arg arguments - Args.push_back(ConstantInt::get(Int64Ty, NumArgs)); - Args.push_back(AI); - for(i = 0 ;i < CI->getNumArgOperands(); i++) { - // Add the original argument - Args.push_back(CI->getArgOperand(i)); - } - - // Create the new call - CallInst *CI_New = CallInst::Create(NewF, Args, "", CI); - CI->replaceAllUsesWith(CI_New); - toDelete.push_back(CI); - } - } - while(!toDelete.empty()) { - Instruction *I = toDelete.back(); - toDelete.pop_back(); - I->eraseFromParent(); - } - IndFunctionsMap[&F] = NewF; - return true; -} - -bool TypeChecks::visitByValFunction(Module &M, Function &F) { - - // For internal functions - // Replace with a function with a a new function with no byval attr. - // Add an explicity copy in the function - // Also update all the call sites. - - // For external functions - // Create an internal clone (treated same as internal functions) - // Modify the original function - // To assume that the metadata for the byval arguments is TOP - - if(F.hasInternalLinkage()) { - visitInternalByValFunction(M, F); - } else { - // create internal clone - ValueToValueMapTy VMap; - Function *F_clone = CloneFunction(&F, VMap); - F_clone->setName(F.getName().str() + "internal"); - F.setLinkage(GlobalValue::InternalLinkage); - F.getParent()->getFunctionList().push_back(F_clone); - F.replaceAllUsesWith(F_clone); - visitInternalByValFunction(M, *F_clone); - visitExternalByValFunction(M, F); - } - return true; -} - -bool TypeChecks::visitInternalByValFunction(Module &M, Function &F) { - - // for every byval argument - // add an alloca, a load, and a store inst - Instruction * InsertBefore = &(F.getEntryBlock().front()); - for (auto &Arg : F.args()) { - if (!Arg.hasByValAttr()) - continue; - assert(Arg.getType()->isPointerTy()); - Type *ETy = (cast(Arg.getType()))->getElementType(); - AllocaInst *AI = new AllocaInst(ETy, 0, "", InsertBefore); - // Do this before adding the load/store pair, so that those uses are not replaced. - Arg.replaceAllUsesWith(AI); - LoadInst *LI = new LoadInst(&Arg, "", InsertBefore); - new StoreInst(LI, AI, InsertBefore); - } - - // Update the call sites - std::vectortoDelete; - for(Value::user_iterator ui = F.user_begin(), ue = F.user_end(); - ui != ue; ui++) { - // Check that F is the called value - if(InvokeInst *II = dyn_cast(*ui)) { - if(II->getCalledFunction() == &F) { - SmallVector Args; - SmallVector ArgAttrs; - - // Get the initial attributes of the call - AttributeList CallPAL = II->getAttributes(); - AttributeSet RAttrs = CallPAL.getRetAttributes(); - AttributeSet FnAttrs = CallPAL.getFnAttributes(); - - Function::arg_iterator NI = F.arg_begin(); - for(unsigned j =3;jgetNumOperands();j++, NI++) { - // Add the original argument - Args.push_back(II->getOperand(j)); - - // If there are attributes on this argument, copy them to the correct - // position in the NewCallPAL - //FIXME: copy the rest of the attributes. - if(NI->hasByValAttr()) - continue; - ArgAttrs.push_back(CallPAL.getParamAttributes(j)); - } - - // Create the substitute call - InvokeInst *CallI = InvokeInst::Create(&F, - II->getNormalDest(), - II->getUnwindDest(), - Args, - "", II); - - auto NewCallPAL = AttributeList::get(F.getContext(), FnAttrs, RAttrs, ArgAttrs); - CallI->setCallingConv(II->getCallingConv()); - CallI->setAttributes(NewCallPAL); - II->replaceAllUsesWith(CallI); - toDelete.push_back(II); - - } - } else if(CallInst *CI = dyn_cast(*ui)) { - if(CI->getCalledFunction() == &F) { - SmallVector Args; - SmallVector ArgAttrs; - - // Get the initial attributes of the call - AttributeList CallPAL = CI->getAttributes(); - AttributeSet RAttrs = CallPAL.getRetAttributes(); - AttributeSet FnAttrs = CallPAL.getFnAttributes(); - - Function::arg_iterator II = F.arg_begin(); - for(unsigned j =1;jgetNumOperands();j++, II++) { - // Add the original argument - Args.push_back(CI->getOperand(j)); - // If there are attributes on this argument, copy them to the correct - // position in the NewCallPAL - //FIXME: copy the rest of the attributes. - if(II->hasByValAttr()) - continue; - ArgAttrs.push_back(CallPAL.getParamAttributes(j)); - } - - // Create the substitute call - CallInst *CallI = CallInst::Create(&F, - Args, - "", CI); - - auto NewCallPAL = AttributeList::get(F.getContext(), FnAttrs, RAttrs, ArgAttrs); - CallI->setCallingConv(CI->getCallingConv()); - CallI->setAttributes(NewCallPAL); - CI->replaceAllUsesWith(CallI); - toDelete.push_back(CI); - } - } - } - while(!toDelete.empty()) { - Instruction *I = toDelete.back(); - toDelete.pop_back(); - I->eraseFromParent(); - } - - // remove the byval attribute from the function - for (Function::arg_iterator I = F.arg_begin(); I != F.arg_end(); ++I) { - if (I->hasByValAttr()) - I->removeAttr(Attribute::AttrKind::ByVal); - } - return true; -} - -bool TypeChecks::visitExternalByValFunction(Module &M, Function &F) { - // A list of the byval arguments that we are setting metadata for - typedef SmallVector RegisteredArgTy; - RegisteredArgTy registeredArguments; - for (auto &Arg : F.args()) { - if (Arg.hasByValAttr()) { - assert (isa(Arg.getType())); - PointerType * PT = cast(Arg.getType()); - Type * ET = PT->getElementType(); - Value * AllocSize = ConstantInt::get(Int64Ty, TD->getTypeAllocSize(ET)); - Instruction * InsertPt = &(F.getEntryBlock().front()); - Value *BCI = castTo(&Arg, VoidPtrTy, "", InsertPt); - std::vector Args; - Args.push_back(BCI); - Args.push_back(AllocSize); - Args.push_back(getTagCounter()); - // Set the metadata for the byval argument to TOP/Initialized - CallInst::Create(trackInitInst, Args, "", InsertPt); - registeredArguments.push_back(&Arg); - } - } - - // Find all basic blocks which terminate the function. - std::set exitBlocks; - for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ++I) { - if (isa(*I) || isa(*I)) { - exitBlocks.insert(I->getParent()); - } - } - - // At each function exit, insert code to set the metadata as uninitialized. - for (std::set::const_iterator BI = exitBlocks.begin(), - BE = exitBlocks.end(); - BI != BE; ++BI) { - for (RegisteredArgTy::const_iterator I = registeredArguments.begin(), - E = registeredArguments.end(); - I != E; ++I) { - SmallVector args; - Instruction * Pt = &((*BI)->back()); - PointerType * PT = cast((*I)->getType()); - Type * ET = PT->getElementType(); - Value * AllocSize = ConstantInt::get(Int64Ty, TD->getTypeAllocSize(ET)); - Value *BCI = castTo(*I, VoidPtrTy, "", Pt); - std::vector Args; - Args.push_back(BCI); - Args.push_back(AllocSize); - Args.push_back(getTagCounter()); - CallInst::Create(trackUnInitInst, Args, "", Pt); - } - } - return true; -} - -// Print the types found in the module. If the optional Module parameter is -// passed in, then the types are printed symbolically if possible, using the -// symbol table from the module. -void TypeChecks::print(raw_ostream &OS, const Module *M) const { - OS << "Types in use by this module:\n"; - std::map::const_iterator I = UsedTypes.begin(), - E = UsedTypes.end(); - for (; I != E; ++I) { - OS << " "; - OS << I->first; - // WriteTypeSymbolic(OS, I->first, M); - OS << " : " << I->second; - OS << '\n'; - } - - OS << "\nNumber of types: " << UsedTypes.size() << '\n'; -} - -// Initialize the shadow memory which contains the 1:1 mapping. -bool TypeChecks::initShadow(Module &M) { - // Create the call to the runtime initialization function and place it before the store instruction. - - Constant * RuntimeCtor = M.getOrInsertFunction("tc.init", VoidTy); - Constant * InitFn = M.getOrInsertFunction("shadowInit", VoidTy); - - //RuntimeCtor->setDoesNotThrow(); - //RuntimeCtor->setLinkage(GlobalValue::InternalLinkage); - - BasicBlock *BB = BasicBlock::Create(M.getContext(), "entry", cast(RuntimeCtor)); - CallInst::Create(InitFn, "", BB); - - Instruction *InsertPt = ReturnInst::Create(M.getContext(), BB); - - // record all globals - for (GlobalVariable &G : M.globals()) { - if(G.use_empty()) - continue; - if(G.getName().str() == "stderr" || - G.getName().str() == "stdout" || - G.getName().str() == "stdin" || - G.getName().str() == "optind" || - G.getName().str() == "optarg") { - // assume initialized - Value *BCI = castTo(&G, VoidPtrTy, "", InsertPt); - std::vector Args; - Args.push_back(BCI); - Args.push_back(getSizeConstant(G.getType()->getElementType())); - Args.push_back(getTagCounter()); - CallInst::Create(trackInitInst, Args, "", InsertPt); - continue; - } - if(!G.hasInitializer()) - continue; - SmallVectorindex; - index.push_back(Zero); - visitGlobal(M, G, G.getInitializer(), *InsertPt, index); - } - // - // Insert the run-time ctor into the ctor list. - // - std::vector CtorInits; - CtorInits.push_back (ConstantInt::get (Int32Ty, 65535)); - CtorInits.push_back (RuntimeCtor); - Constant * RuntimeCtorInit=ConstantStruct::getAnon(M.getContext(),CtorInits, false); - - // - // Get the current set of static global constructors and add the new ctor - // to the list. - // - std::vector CurrentCtors; - GlobalVariable * GVCtor = M.getNamedGlobal ("llvm.global_ctors"); - if (GVCtor) { - if (Constant * C = GVCtor->getInitializer()) { - for (unsigned index = 0; index < C->getNumOperands(); ++index) { - CurrentCtors.push_back (cast(C->getOperand (index))); - } - } - - // - // Rename the global variable so that we can name our global - // llvm.global_ctors. - // - GVCtor->setName ("removed"); - } - - // - // The ctor list seems to be initialized in different orders on different - // platforms, and the priority settings don't seem to work. Examine the - // module's platform string and take a best guess to the order. - // - if (M.getTargetTriple().find ("linux") == std::string::npos) - CurrentCtors.insert (CurrentCtors.begin(), RuntimeCtorInit); - else - CurrentCtors.push_back (RuntimeCtorInit); - - // - // Create a new initializer. - // - ArrayType * AT = ArrayType::get (RuntimeCtorInit-> getType(), - CurrentCtors.size()); - Constant * NewInit=ConstantArray::get (AT, CurrentCtors); - - // - // Create the new llvm.global_ctors global variable and replace all uses of - // the old global variable with the new one. - // - new GlobalVariable (M, - NewInit->getType(), - false, - GlobalValue::AppendingLinkage, - NewInit, - "llvm.global_ctors"); - - - return true; -} - - bool TypeChecks::visitMain(Module &M, Function &MainFunc) { - if(MainFunc.arg_size() < 2) - // No need to register - return false; - - auto AI = MainFunc.arg_begin(); - Value *Argc = &*AI; - Value *Argv = &*(++AI); - - Instruction *InsertPt = &*MainFunc.front().begin(); - std::vector fargs; - fargs.push_back (Argc); - fargs.push_back (Argv); - CallInst::Create (RegisterArgv, fargs, "", InsertPt); - - if(MainFunc.arg_size() < 3) - return true; - - Value *Envp = &*(++AI); - std::vector Args; - Args.push_back(Envp); - CallInst::Create(RegisterEnvp, Args, "", InsertPt); - return true; - } - -bool TypeChecks::visitGlobal(Module &M, GlobalVariable &GV, - Constant *C, Instruction &I, SmallVector Indices) { - - if(ConstantArray *CA = dyn_cast(C)) { - Type * ElementType = CA->getType()->getElementType(); - // Create the type entry for the first element - // using recursive creation till we get to the base types - Indices.push_back(ConstantInt::get(Int64Ty,0)); - visitGlobal(M, GV, CA->getOperand(0), I, Indices); - Indices.pop_back(); - GetElementPtrInst *GEP = GetElementPtrInst::CreateInBounds(&GV, Indices, "", &I); - - Value *BCI = castTo(GEP, VoidPtrTy, "", &I); - - // Copy the type metadata for the first element - // over for the rest of the elements. - std::vector Args; - Args.push_back(BCI); - Args.push_back(getSizeConstant(ElementType)); - Args.push_back(ConstantInt::get(Int64Ty, CA->getNumOperands())); - Args.push_back(getTagCounter()); - CallInst::Create(trackArray, Args, "", &I); - } - else if(ConstantStruct *CS = dyn_cast(C)) { - // Create metadata for each field of the struct - // at the correct offset. - const StructLayout *SL = TD->getStructLayout(cast(CS->getType())); - for (unsigned i = 0, e = CS->getNumOperands(); i != e; ++i) { - if (SL->getElementOffset(i) < SL->getSizeInBytes()) { - Constant * ConstElement = cast(CS->getOperand(i)); - Indices.push_back(ConstantInt::get(Int32Ty, i)); - visitGlobal(M, GV, ConstElement, I, Indices); - Indices.pop_back(); - } - } - } else if(ConstantAggregateZero *CAZ = dyn_cast(C)) { - // Similiar to having an initializer with all values NULL - // Must set metadata, similiar to the previous 2 cases. - Type *Ty = CAZ->getType(); - if(ArrayType * ATy = dyn_cast(Ty)) { - Type * ElementType = ATy->getElementType(); - Indices.push_back(ConstantInt::get(Int64Ty,0)); - visitGlobal(M, GV, Constant::getNullValue(ElementType), I, Indices); - Indices.pop_back(); - GetElementPtrInst *GEP = GetElementPtrInst::CreateInBounds(&GV, Indices, "", &I); - - Value *BCI = castTo(GEP, VoidPtrTy, "", &I); - std::vector Args; - Args.push_back(BCI); - Args.push_back(getSizeConstant(ElementType)); - Args.push_back(ConstantInt::get(Int64Ty, ATy->getNumElements())); - Args.push_back(getTagCounter()); - CallInst::Create(trackArray, Args, "", &I); - } else if(StructType *STy = dyn_cast(Ty)) { - const StructLayout *SL = TD->getStructLayout(STy); - for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i) { - if (SL->getElementOffset(i) < SL->getSizeInBytes()) { - Indices.push_back(ConstantInt::get(Int32Ty, i)); - visitGlobal(M, GV, Constant::getNullValue(STy->getElementType(i)), I, Indices); - Indices.pop_back(); - } - } - } else { - // Zeroinitializer of a primitive type - GetElementPtrInst *GEP = GetElementPtrInst::CreateInBounds(&GV, Indices, "", &I); - - Value *BCI = castTo(GEP, VoidPtrTy, "", &I); - std::vector Args; - Args.push_back(BCI); - Args.push_back(getTypeMarkerConstant(CAZ)); - Args.push_back(getSizeConstant(CAZ->getType())); - Args.push_back(getTagCounter()); - CallInst::Create(trackGlobal, Args, "", &I); - } - } - else { - // Primitive type value - GetElementPtrInst *GEP = GetElementPtrInst::CreateInBounds(&GV, Indices, "", &I); - - Value *BCI = castTo(GEP, VoidPtrTy, "", &I); - std::vector Args; - Args.push_back(BCI); - Args.push_back(getTypeMarkerConstant(C)); - Args.push_back(getSizeConstant(C->getType())); - Args.push_back(getTagCounter()); - CallInst::Create(trackGlobal, Args, "", &I); - } - return true; -} - bool TypeChecks::visitVAArgInst(Module &M, VAArgInst &VI) { - if(!VI.getParent()->getParent()->hasInternalLinkage()) - return false; - Value *BCI = castTo(VI.getOperand(0), VoidPtrTy, "", &VI); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(getTypeMarkerConstant(&VI)); - Args.push_back(getTagCounter()); - CallInst::Create(checkVAArg, Args, "", &VI); - return false; - } - -// Insert code to initialize meta data to bottom -// Insert code to set objects to 0 -bool TypeChecks::visitAllocaInst(Module &M, AllocaInst &AI) { - - PointerType * PT = AI.getType(); - Type * ET = PT->getElementType(); - Value * AllocSize = ConstantInt::get(Int64Ty, TD->getTypeAllocSize(ET)); - CastInst *BCI = BitCastInst::CreatePointerCast(&AI, VoidPtrTy); - BCI->insertAfter(&AI); - - Value *TotalSize; - if(!AI.isArrayAllocation()) { - TotalSize = AllocSize; - } else { - CastInst *ArraySize = CastInst::CreateSExtOrBitCast(AI.getArraySize(), Int64Ty, "", &AI); - BinaryOperator *Size = BinaryOperator::Create(Instruction::Mul, AllocSize, ArraySize, "", &AI); - TotalSize = Size; - } - - // Setting metadata to be 0(BOTTOM/Uninitialized) - - std::vector Args; - Args.push_back(BCI); - Args.push_back(TotalSize); - Args.push_back(getTagCounter()); - CallInst *CI = CallInst::Create(trackUnInitInst, Args); - CI->insertAfter(BCI); - return true; -} - -// Insert runtime checks for certain call instructions -bool TypeChecks::visitCallInst(Module &M, CallInst &CI) { - return visitCallSite(M, &CI); -} - -// Insert runtime checks for certain call instructions -bool TypeChecks::visitInvokeInst(Module &M, InvokeInst &II) { - return visitCallSite(M, &II); -} - -bool TypeChecks::visitCallSite(Module &M, CallSite CS) { - // - // Get the called value. Strip off any casts which are lossless. - // - Value *Callee = CS.getCalledValue()->stripPointerCasts(); - Instruction *I = CS.getInstruction(); - - // Special case handling of certain libc allocation functions here. - if (Function *F = dyn_cast(Callee)) { - if (F->isIntrinsic()) { - switch(F->getIntrinsicID()) { - case Intrinsic::memcpy: - case Intrinsic::memmove: - { - Value *BCI_Src = castTo(CS.getArgument(1), VoidPtrTy, "", I); - Value *BCI_Dest = castTo(CS.getArgument(0), VoidPtrTy, "", I); - std::vector Args; - Args.push_back(BCI_Dest); - Args.push_back(BCI_Src); - CastInst *Size = CastInst::CreateIntegerCast(CS.getArgument(2), Int64Ty, false, "", I); - Args.push_back(Size); - Args.push_back(getTagCounter()); - CallInst::Create(copyTypeInfo, Args, "", I); - return true; - } - - case Intrinsic::memset: - { - Value *BCI = castTo(CS.getArgument(0), VoidPtrTy, "", I); - std::vector Args; - Args.push_back(BCI); - CastInst *Size = CastInst::CreateIntegerCast(CS.getArgument(2), Int64Ty, false, "", I); - Args.push_back(Size); - Args.push_back(getTagCounter()); - CallInst::Create(trackInitInst, Args, "", I); - return true; - } - - default: break; - } - } else if (F->getName().str() == std::string("_ZNKSs5c_strEv")) { //c_str - std::vectorArgs; - Args.push_back(I); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackgetcwd", VoidTy, VoidPtrTy, Int32Ty); - CallInst *CI = CallInst::Create(F, Args); - Instruction *InsertPt = I; - if (InvokeInst *II = dyn_cast(InsertPt)) { - InsertPt = &*II->getNormalDest()->begin(); - while (isa(InsertPt)) - ++InsertPt; - } else - ++InsertPt; - CI->insertBefore(InsertPt); - } else if (F->getName().str() == std::string("_ZNSsC1EPKcRKSaIcE")) { //c_str() - Value *BCI = castTo(CS.getArgument(0), VoidPtrTy, "", I); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackgetcwd", VoidTy, VoidPtrTy, Int32Ty); - CallInst *CI = CallInst::Create(F, Args); - Instruction *InsertPt = I; - if (InvokeInst *II = dyn_cast(InsertPt)) { - InsertPt = &*II->getNormalDest()->begin(); - while (isa(InsertPt)) - ++InsertPt; - } else - ++InsertPt; - CI->insertBefore(InsertPt); - } else if (F->getName().str() == std::string("accept")) { - CastInst *BCI = BitCastInst::CreatePointerCast(CS.getArgument(1), VoidPtrTy); - BCI->insertAfter(I); - CastInst *BCI_Size = BitCastInst::CreatePointerCast(CS.getArgument(2), VoidPtrTy); - BCI_Size->insertAfter(I); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(BCI_Size); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackaccept", VoidTy, VoidPtrTy,VoidPtrTy, Int32Ty); - CallInst *CI = CallInst::Create(F, Args); - CI->insertAfter(BCI); - } else if (F->getName().str() == std::string("poll")) { - CastInst *BCI = BitCastInst::CreatePointerCast(CS.getArgument(0), VoidPtrTy); - BCI->insertAfter(I); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(CS.getArgument(1)); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackpoll", VoidTy, VoidPtrTy, Int64Ty, Int32Ty); - CallInst *CI = CallInst::Create(F, Args); - CI->insertAfter(BCI); - } else if (F->getName().str() == std::string("getaddrinfo")) { - CastInst *BCI = BitCastInst::CreatePointerCast(CS.getArgument(3), VoidPtrTy); - BCI->insertAfter(I); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackgetaddrinfo", VoidTy, VoidPtrTy, Int32Ty); - CallInst *CI = CallInst::Create(F, Args); - CI->insertAfter(BCI); - } else if (F->getName().str() == std::string("mmap")) { - CastInst *BCI = BitCastInst::CreatePointerCast(I, VoidPtrTy); - BCI->insertAfter(I); - std::vector Args; - Args.push_back(BCI); - Args.push_back(CS.getArgument(1)); - Args.push_back(getTagCounter()); - CallInst *CI = CallInst::Create(trackInitInst, Args); - CI->insertAfter(BCI); - } else if (F->getName().str() == std::string("__strdup")) { - CastInst *BCI_Dest = BitCastInst::CreatePointerCast(I, VoidPtrTy); - BCI_Dest->insertAfter(I); - CastInst *BCI_Src = BitCastInst::CreatePointerCast(CS.getArgument(0), VoidPtrTy); - BCI_Src->insertAfter(BCI_Dest); - std::vector Args; - Args.push_back(BCI_Dest); - Args.push_back(BCI_Src); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackStrcpyInst", VoidTy, VoidPtrTy, VoidPtrTy, Int32Ty); - CallInst *CI = CallInst::Create(F, Args); - CI->insertAfter(BCI_Src); - } else if (F->getName().str() == std::string("gettimeofday") || - F->getName().str() == std::string("time") || - F->getName().str() == std::string("times")) { - Value *BCI = castTo(CS.getArgument(0), VoidPtrTy, "", I); - assert (isa(CS.getArgument(0)->getType())); - PointerType * PT = cast(CS.getArgument(0)->getType()); - Type * ET = PT->getElementType(); - Value * AllocSize = ConstantInt::get(Int64Ty, TD->getTypeAllocSize(ET)); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(AllocSize); - Args.push_back(getTagCounter()); - CallInst::Create(trackInitInst, Args, "", I); - } else if (F->getName().str() == std::string("getpwuid")) { - CastInst *BCI = BitCastInst::CreatePointerCast(I, VoidPtrTy); - BCI->insertAfter(I); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackgetpwuid", VoidTy, VoidPtrTy, Int32Ty); - CallInst *CI = CallInst::Create(F, Args); - CI->insertAfter(BCI); - } else if (F->getName().str() == std::string("getpwnam")) { - CastInst *BCI = BitCastInst::CreatePointerCast(I, VoidPtrTy); - BCI->insertAfter(I); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackgetpwuid", VoidTy, VoidPtrTy, Int32Ty); - CallInst *CI = CallInst::Create(F, Args); - CI->insertAfter(BCI); - } else if(F->getName().str() == std::string("getopt_long")) { - Value *OptArg = M.getNamedGlobal("optarg"); - LoadInst *LI = new LoadInst(OptArg); - LI->insertAfter(I); - std::vectorArgs; - Args.push_back(LI); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackgetcwd", VoidTy, VoidPtrTy, Int32Ty); - CallInst *CI = CallInst::Create(F, Args); - CI->insertAfter(LI); - } else if (F->getName().str() == std::string("getgruid") || - F->getName().str() == std::string("getgrnam") || - F->getName().str() == std::string("getpwnam") || - F->getName().str() == std::string("__errno_location")) { - CastInst *BCI = BitCastInst::CreatePointerCast(I, VoidPtrTy); - assert (isa(I->getType())); - PointerType * PT = cast(I->getType()); - Type * ET = PT->getElementType(); - Value * AllocSize = ConstantInt::get(Int64Ty, TD->getTypeAllocSize(ET)); - BCI->insertAfter(I); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(AllocSize); - Args.push_back(getTagCounter()); - CallInst *CI = CallInst::Create(trackInitInst, Args); - CI->insertAfter(BCI); - } else if (F->getName().str() == std::string("getservbyname")) { - CastInst *BCI = BitCastInst::CreatePointerCast(I, VoidPtrTy); - BCI->insertAfter(I); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackgetservbyname", VoidTy, VoidPtrTy, Int32Ty); - CallInst *CI = CallInst::Create(F, Args); - CI->insertAfter(BCI); - } else if (F->getName().str() == std::string("gethostbyname") || - F->getName().str() == std::string("gethostbyaddr")) { - CastInst *BCI = BitCastInst::CreatePointerCast(I, VoidPtrTy); - BCI->insertAfter(I); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackgethostbyname", VoidTy, VoidPtrTy, Int32Ty); - CallInst *CI = CallInst::Create(F, Args); - CI->insertAfter(BCI); - } else if (F->getName().str() == std::string("gethostname")) { - CastInst *BCI = BitCastInst::CreatePointerCast(CS.getArgument(0), VoidPtrTy); - BCI->insertAfter(I); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackgethostname", VoidTy, VoidPtrTy, Int32Ty); - CallInst *CI = CallInst::Create(F, Args); - CI->insertAfter(BCI); - } else if (F->getName().str() == std::string("getenv") || - F->getName().str() == std::string("strerror") || - F->getName().str() == std::string("inet_ntoa")) { - CastInst *BCI = BitCastInst::CreatePointerCast(I, VoidPtrTy); - BCI->insertAfter(I); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackgetcwd", VoidTy, VoidPtrTy, Int32Ty); - CallInst *CI = CallInst::Create(F, Args); - CI->insertAfter(BCI); - } else if (F->getName().str() == std::string("getcwd")) { - CastInst *BCI = BitCastInst::CreatePointerCast(I, VoidPtrTy); - BCI->insertAfter(I); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackgetcwd", VoidTy, VoidPtrTy, Int32Ty); - CallInst *CI = CallInst::Create(F, Args); - CI->insertAfter(BCI); - } else if(F->getName().str() == std::string("crypt")) { - CastInst *BCI = BitCastInst::CreatePointerCast(I, VoidPtrTy); - BCI->insertAfter(I); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackgetcwd", VoidTy, VoidPtrTy, Int32Ty); - CallInst *CI = CallInst::Create(F, Args); - CI->insertAfter(BCI); - } else if (F->getName().str() == std::string("getrusage") || - F->getName().str() == std::string("getrlimit") || - F->getName().str() == std::string("stat") || - F->getName().str() == std::string("vfsstat") || - F->getName().str() == std::string("fstat") || - F->getName().str() == std::string("lstat")) { - Value *BCI = castTo(CS.getArgument(1), VoidPtrTy, "", I); - assert (isa(CS.getArgument(1)->getType())); - PointerType * PT = cast(CS.getArgument(1)->getType()); - Type * ET = PT->getElementType(); - Value * AllocSize = ConstantInt::get(Int64Ty, TD->getTypeAllocSize(ET)); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(AllocSize); - Args.push_back(getTagCounter()); - CallInst::Create(trackInitInst, Args, "", I); - } else if (F->getName().str() == std::string("sigaction")) { - Value *BCI = castTo(CS.getArgument(2), VoidPtrTy, "", I); - assert (isa(CS.getArgument(2)->getType())); - PointerType * PT = cast(CS.getArgument(2)->getType()); - Type * ET = PT->getElementType(); - Value * AllocSize = ConstantInt::get(Int64Ty, TD->getTypeAllocSize(ET)); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(AllocSize); - Args.push_back(getTagCounter()); - CallInst::Create(trackInitInst, Args, "", I); - } else if (F->getName().str() == std::string("__ctype_b_loc")) { - CastInst *BCI = BitCastInst::CreatePointerCast(I, VoidPtrTy); - BCI->insertAfter(I); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackctype", VoidTy, VoidPtrTy, Int32Ty); - CallInst *CI = CallInst::Create(F, Args); - CI->insertAfter(BCI); - } else if (F->getName().str() == std::string("__ctype_toupper_loc")) { - CastInst *BCI = BitCastInst::CreatePointerCast(I, VoidPtrTy); - BCI->insertAfter(I); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackctype_32", VoidTy, VoidPtrTy, Int32Ty); - CallInst *CI = CallInst::Create(F, Args); - CI->insertAfter(BCI); - } else if (F->getName().str() == std::string("__ctype_tolower_loc")) { - CastInst *BCI = BitCastInst::CreatePointerCast(I, VoidPtrTy); - BCI->insertAfter(I); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackctype_32", VoidTy, VoidPtrTy, Int32Ty); - CallInst *CI = CallInst::Create(F, Args); - CI->insertAfter(BCI); - } else if (F->getName().str() == std::string("strtol") || - F->getName().str() == std::string("strtod")) { - Value *BCI = castTo(CS.getArgument(1), VoidPtrTy, "", I); - PointerType *PTy = cast(CS.getArgument(1)->getType()); - Type * ElementType = PTy->getElementType(); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(getSizeConstant(ElementType)); - Args.push_back(getTagCounter()); - CallInst::Create(trackInitInst, Args, "", I); - return true; - } else if (F->getName().str() == std::string("strcat") || - F->getName().str() == std::string("_ZNSspLEPKc")) { - Value *BCI_Src = castTo(CS.getArgument(1), VoidPtrTy, "", I); - Value *BCI_Dest = castTo(CS.getArgument(0), VoidPtrTy, "", I); - std::vector Args; - Args.push_back(BCI_Dest); - Args.push_back(BCI_Src); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackStrcatInst", VoidTy, VoidPtrTy, VoidPtrTy, Int32Ty); - CallInst::Create(F, Args, "", I); - } else if (F->getName().str() == std::string("strcpy")) { - std::vector Args; - Args.push_back(CS.getArgument(0)); - Args.push_back(CS.getArgument(1)); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackStrcpyInst", VoidTy, VoidPtrTy, VoidPtrTy, Int32Ty); - CallInst::Create(F, Args, "", I); - } else if (F->getName().str() == std::string("strncpy")) { - std::vectorArgs; - Args.push_back(CS.getArgument(0)); - Args.push_back(CS.getArgument(1)); - Args.push_back(CS.getArgument(2)); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackStrncpyInst", VoidTy, VoidPtrTy, VoidPtrTy, I->getOperand(3)->getType(), Int32Ty); - CallInst::Create(F, Args, "", I); - } else if (F->getName().str() == std::string("readlink")) { - std::vectorArgs; - Args.push_back(CS.getArgument(1)); - Args.push_back(I); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackReadLink", VoidTy, VoidPtrTy, I->getType(), Int32Ty); - CallInst *CI = CallInst::Create(F, Args); - CI->insertAfter(I); - } else if (F->getName().str() == std::string("pipe")) { - Value *BCI = castTo(CS.getArgument(0), VoidPtrTy, "", I); - std::vector Args; - Args.push_back(BCI); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackpipe", VoidTy, VoidPtrTy, Int32Ty); - CallInst::Create(F, Args, "", I); - return true; - } else if (F->getName().str() == std::string("getsockname")) { - CastInst *BCI = BitCastInst::CreatePointerCast(CS.getArgument(1), VoidPtrTy); - BCI->insertAfter(I); - CastInst *BCI_Size = BitCastInst::CreatePointerCast(CS.getArgument(2), VoidPtrTy); - BCI_Size->insertAfter(I); - std::vector Args; - Args.push_back(BCI); - Args.push_back(BCI_Size); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackgetsockname", VoidTy, VoidPtrTy, VoidPtrTy, Int32Ty); - CallInst *CI = CallInst::Create(F, Args); - CI->insertAfter(BCI); - return true; - } else if (F->getName().str() == std::string("readdir")) { - CastInst *BCI = BitCastInst::CreatePointerCast(I, VoidPtrTy); - BCI->insertAfter(I); - PointerType *PTy = cast(I->getType()); - Type * ElementType = PTy->getElementType(); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(getSizeConstant(ElementType)); - Args.push_back(getTagCounter()); - CallInst *CI = CallInst::Create(trackInitInst, Args); - CI->insertAfter(BCI); - return true; - } else if (F->getName().str() == std::string("localtime") || - F->getName().str() == std::string("gmtime")) { - CastInst *BCI = BitCastInst::CreatePointerCast(I, VoidPtrTy); - BCI->insertAfter(I); - PointerType *PTy = cast(I->getType()); - Type * ElementType = PTy->getElementType(); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(getSizeConstant(ElementType)); - Args.push_back(getTagCounter()); - CallInst *CI = CallInst::Create(trackInitInst, Args); - CI->insertAfter(BCI); - } else if (F->getName().str() == std::string("ftime") || - F->getName().str() == std::string("gettimeofday")) { - Value *BCI = castTo(CS.getArgument(0), VoidPtrTy, "", I); - PointerType *PTy = cast(CS.getArgument(0)->getType()); - Type * ElementType = PTy->getElementType(); - std::vector Args; - Args.push_back(BCI); - Args.push_back(getSizeConstant(ElementType)); - Args.push_back(getTagCounter()); - CallInst::Create(trackInitInst, Args, "", I); - return true; - } else if(F->getName().str() == std::string("read")) { - CastInst *BCI = BitCastInst::CreatePointerCast(CS.getArgument(1), VoidPtrTy); - BCI->insertAfter(I); - std::vector Args; - Args.push_back(BCI); - CastInst *Size = CastInst::CreateIntegerCast(I, Int64Ty, false); - Size->insertAfter(I); - Args.push_back(Size); - Args.push_back(getTagCounter()); - CallInst *CI = CallInst::Create(trackInitInst, Args); - CI->insertAfter(BCI); - return true; - } else if(F->getName().str() == std::string("fread")) { - CastInst *BCI = BitCastInst::CreatePointerCast(CS.getArgument(0), VoidPtrTy); - BCI->insertAfter(I); - std::vector Args; - Args.push_back(BCI); - CastInst *Elem = CastInst::CreateIntegerCast(I, Int64Ty, false); - BinaryOperator *Size = BinaryOperator::Create(Instruction::Mul, Elem, CS.getArgument(1)); - Elem->insertAfter(I); - Size->insertAfter(Elem); - Args.push_back(Size); - Args.push_back(getTagCounter()); - CallInst *CI = CallInst::Create(trackInitInst, Args); - CI->insertAfter(BCI); - return true; - } else if(F->getName().str() == std::string("calloc")) { - CastInst *BCI = BitCastInst::CreatePointerCast(I, VoidPtrTy); - BCI->insertAfter(I); - std::vector Args; - Args.push_back(BCI); - CastInst *Size = CastInst::CreateIntegerCast(CS.getArgument(1), Int64Ty, false, "", I); - Args.push_back(Size); - Args.push_back(getTagCounter()); - CallInst *CI = CallInst::Create(trackInitInst, Args); - CI->insertAfter(BCI); - std::vector Args1; - Args1.push_back(BCI); - Args1.push_back(Size); - CastInst *Num = CastInst::CreateIntegerCast(CS.getArgument(0), Int64Ty, false, "", I); - Args1.push_back(Num); - Args1.push_back(getTagCounter()); - CallInst *CI_Arr = CallInst::Create(trackArray, Args1); - CI_Arr->insertAfter(CI); - return true; - } else if(F->getName().str() == std::string("realloc")) { - CastInst *BCI_Src = BitCastInst::CreatePointerCast(CS.getArgument(0), VoidPtrTy); - CastInst *BCI_Dest = BitCastInst::CreatePointerCast(I, VoidPtrTy); - BCI_Src->insertAfter(I); - BCI_Dest->insertAfter(BCI_Src); - std::vector Args; - Args.push_back(BCI_Dest); - Args.push_back(BCI_Src); - CastInst *Size = CastInst::CreateIntegerCast(CS.getArgument(1), Int64Ty, false, "", I); - Args.push_back(Size); - Args.push_back(getTagCounter()); - CallInst *CI = CallInst::Create(copyTypeInfo, Args); - CI->insertAfter(BCI_Dest); - return true; - } else if(F->getName().str() == std::string("fgets")) { - Value *BCI = castTo(CS.getArgument(0), VoidPtrTy, "", I); - std::vector Args; - Args.push_back(BCI); - CastInst *Size = CastInst::CreateIntegerCast(CS.getArgument(1), Int64Ty, false, "", I); - Args.push_back(Size); - Args.push_back(getTagCounter()); - CallInst::Create(trackInitInst, Args, "", I); - return true; - } else if(F->getName().str() == std::string("snprintf") || - F->getName().str() == std::string("vsnprintf")) { - Value *BCI = castTo(CS.getArgument(0), VoidPtrTy, "", I); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(getTagCounter()); - Constant *F = M.getOrInsertFunction("trackgetcwd", VoidTy, VoidPtrTy, Int32Ty); - CallInst *CINew = CallInst::Create(F, Args); - CINew->insertAfter(I); - } else if(F->getName().str() == std::string("sprintf")) { - Value *BCI = castTo(CS.getArgument(0), VoidPtrTy, "", I); - std::vectorArgs; - Args.push_back(BCI); - CastInst *Size = CastInst::CreateIntegerCast(I, Int64Ty, false); - Size->insertAfter(I); - Instruction *NewValue = BinaryOperator::Create(BinaryOperator::Add, - Size, - One); - NewValue->insertAfter(Size); - Args.push_back(NewValue); - Args.push_back(getTagCounter()); - CallInst *CINew = CallInst::Create(trackInitInst, Args); - CINew->insertAfter(NewValue); - } else if(F->getName().str() == std::string("scanf")) { - unsigned i = 1; - while(i < CS.arg_size()) { - visitInputFunctionValue(M, CS.getArgument(i), I); - i++; - } - } else if(F->getName().str() == std::string("sscanf")) { - // FIXME: Need to look at the format string and check - unsigned i = 2; - while(i < CS.arg_size()) { - visitInputFunctionValue(M, CS.getArgument(i), I); - i++; - } - } else if(F->getName().str() == std::string("fscanf")) { - unsigned i = 2; - while(i < CS.arg_size()) { - visitInputFunctionValue(M, CS.getArgument(i), I); - i++; - } - } - } else { - // indirect call site - IndCalls.insert(CS.getInstruction()); - return false; - } - return false; -} - -// Add extra arguments to each indirect call site -bool TypeChecks::visitIndirectCallSite(Module &M, Instruction *I) { - // add the number of arguments as the first argument - Type* OrigType = I->getOperand(0)->getType(); - assert(OrigType->isPointerTy()); - FunctionType *FOldType = cast((cast(OrigType))->getElementType()); - std::vectorTP; - TP.push_back(Int64Ty); - TP.push_back(TypeTagPtrTy); - - for(llvm::FunctionType::param_iterator ArgI = FOldType->param_begin(); ArgI != FOldType->param_end(); ++ArgI) - TP.push_back(*ArgI); - - FunctionType *FTy = FunctionType::get(FOldType->getReturnType(), TP, FOldType->isVarArg()); - Value *Func = castTo(I->getOperand(0), FTy->getPointerTo(), "", I); - - inst_iterator InsPt = inst_begin(I->getParent()->getParent()); - CallSite CS = CallSite(I); - unsigned int NumArgs = CS.arg_size(); - Value *NumArgsVal = ConstantInt::get(Int32Ty, NumArgs); - AllocaInst *AI = new AllocaInst(TypeTagTy, 0, NumArgsVal, "", &*InsPt); - for(unsigned int i = 0; i < CS.arg_size(); i++) { - Value *Idx[1]; - Idx[0] = ConstantInt::get(Int32Ty, i-1); - GetElementPtrInst *GEP = GetElementPtrInst::CreateInBounds(AI, Idx, "", I); - Constant *C = getTypeMarkerConstant(CS.getArgument(i)); - new StoreInst(C, GEP, I); - } - std::vector Args; - Args.push_back(ConstantInt::get(Int64Ty, NumArgs)); - Args.push_back(AI); - for(unsigned int i = 0; i < CS.arg_size(); i++) - Args.push_back(CS.getArgument(i)); - if(CallInst *CI = dyn_cast(I)) { - CallInst *CI_New = CallInst::Create(Func, Args, "", CI); - CI->replaceAllUsesWith(CI_New); - CI->eraseFromParent(); - } else if(InvokeInst *II = dyn_cast(I)) { - InvokeInst *INew = InvokeInst::Create(Func, - II->getNormalDest(), - II->getUnwindDest(), - Args, - "", I); - II->replaceAllUsesWith(INew); - II->eraseFromParent(); - } - return true; -} - -bool TypeChecks::visitInputFunctionValue(Module &M, Value *V, Instruction *CI) { - // Cast the pointer operand to i8* for the runtime function. - Value *BCI = castTo(V, VoidPtrTy, "", CI); - PointerType *PTy = dyn_cast(V->getType()); - if(!PTy) - return false; - - std::vector Args; - Args.push_back(BCI); - Args.push_back(getTypeMarkerConstant(PTy->getElementType())); - Args.push_back(getSizeConstant(PTy->getElementType())); - Args.push_back(getTagCounter()); - - // Create the call to the runtime check and place it before the store instruction. - CallInst::Create(trackStoreInst, Args, "", CI); - - if(PTy == VoidPtrTy) { - // TODO: This is currently a heuristic for strings. If we see a i8* in a call to - // input functions, treat as string, and get length using strlen. - std::vector Args; - Args.push_back(BCI); - Args.push_back(getTagCounter()); - CallInst *CINew = CallInst::Create(trackStringInput, Args); - CINew->insertAfter(CI); - } - - return true; -} - -// Insert runtime checks before all load instructions. -bool TypeChecks::visitLoadInst(Module &M, LoadInst &LI) { - inst_iterator InsPt = inst_begin(LI.getParent()->getParent()); - // Cast the pointer operand to i8* for the runtime function. - Value *BCI = castTo(LI.getPointerOperand(), VoidPtrTy, "", &LI); - - Value *Size = ConstantInt::get(Int32Ty, getSize(LI.getType())); - AllocaInst *AI = new AllocaInst(TypeTagTy, LI.getPointerAddressSpace(), Size, "", &*InsPt); - - std::vectorArgs1; - Args1.push_back(BCI); - Args1.push_back(getSizeConstant(LI.getType())); - Args1.push_back(AI); - Args1.push_back(getTagCounter()); - CallInst *getTypeCall = CallInst::Create(getTypeTag, Args1, "", &LI); - if(TrackAllLoads) { - std::vector Args; - Args.push_back(getTypeMarkerConstant(&LI)); - Args.push_back(getSizeConstant(LI.getType())); - Args.push_back(AI); - Args.push_back(BCI); - Args.push_back(getTagCounter()); - CallInst::Create(checkTypeInst, Args, "", &LI); - } - visitUses(&LI, AI, BCI); - - if(AI->hasOneUse()) { - // No uses needed checks - getTypeCall->eraseFromParent(); - } - - // Create the call to the runtime check and place it before the load instruction. - numLoadChecks++; - return true; -} - -// AI - metadata -// BCI - ptr -// I - instruction whose uses to instrument -bool TypeChecks::visitUses(Instruction *I, Instruction *AI, Value *BCI) { - for(Value::user_iterator II = I->user_begin(); II != I->user_end(); ++II) { - if(DisablePtrCmpChecks) { - if(isa(*II)) { - if(I->getType()->isPointerTy()) - continue; - } - } - std::vector Args; - Args.push_back(getTypeMarkerConstant(I)); - Args.push_back(getSizeConstant(I->getType())); - Args.push_back(AI); - Args.push_back(BCI); - Args.push_back(getTagCounter()); - if(StoreInst *SI = dyn_cast(*II)) { - if(SI->getOperand(0) == I) { - // Cast the pointer operand to i8* for the runtime function. - Value *BCI_Dest = castTo(SI->getPointerOperand(), VoidPtrTy, "", SI); - - std::vector Args; - Args.push_back(BCI_Dest); - Args.push_back(AI); - Args.push_back(getSizeConstant(SI->getOperand(0)->getType())); - Args.push_back(getTypeMarkerConstant(SI->getOperand(0)->getType())); - Args.push_back(BCI); - Args.push_back(getTagCounter()); - // Create the call to the runtime check and place it before the copying store instruction. - CallInst::Create(setTypeInfo, Args, "", SI); - } else { - CallInst::Create(checkTypeInst, Args, "", cast(II.getUse().getUser())); - } - } else if(SelectInst *SelI = dyn_cast(*II)) { - if(SelI->getOperand(0) == I) { - CallInst::Create(checkTypeInst, Args, "", cast(II.getUse().getUser())); - // if it is used as the condition, just insert a check - } else { - SelectInst *Prev = NULL; - SelectInst *PrevBasePtr = NULL; - if(SelectInst_MD_Map.find(SelI) != SelectInst_MD_Map.end()) { - Prev = SelectInst_MD_Map[SelI]; - PrevBasePtr = SelectInst_BasePtr_Map[SelI]; - } - SelectInst *AI_New; - SelectInst *BCI_New; - if(SelI->getTrueValue() == I) { - if(!Prev) { - AI_New = SelectInst::Create(SelI->getCondition(), AI, Constant::getNullValue(AI->getType()), "", SelI); - BCI_New = SelectInst::Create(SelI->getCondition(), BCI, Constant::getNullValue(BCI->getType()), "", SelI); - } else { - AI_New = SelectInst::Create(SelI->getCondition(), AI, Prev->getFalseValue(), "", SelI); - BCI_New = SelectInst::Create(SelI->getCondition(), BCI, Prev->getFalseValue(), "", SelI); - Prev->replaceAllUsesWith(AI_New); - PrevBasePtr->replaceAllUsesWith(BCI_New); - } - } - else { - if(!Prev) { - AI_New = SelectInst::Create(SelI->getCondition(), Constant::getNullValue(AI->getType()), AI, "", SelI); - BCI_New = SelectInst::Create(SelI->getCondition(), Constant::getNullValue(BCI->getType()), BCI, "", SelI); - } else { - AI_New = SelectInst::Create(SelI->getCondition(), Prev->getTrueValue(), AI, "", SelI); - BCI_New = SelectInst::Create(SelI->getCondition(), Prev->getTrueValue(), BCI, "", SelI); - Prev->replaceAllUsesWith(AI_New); - PrevBasePtr->replaceAllUsesWith(BCI_New); - } - } - SelectInst_MD_Map[SelI] = AI_New; - SelectInst_BasePtr_Map[SelI] = BCI_New; - if(!Prev) - visitUses(SelI, AI_New, BCI_New); - } - } else if(PHINode *PH = dyn_cast(*II)) { - PHINode *Prev = NULL; - PHINode *PrevBasePtr = NULL; - if(PHINode_MD_Map.find(PH) != PHINode_MD_Map.end()) { - Prev = PHINode_MD_Map[PH]; - PrevBasePtr = PHINode_BasePtr_Map[PH]; - } - if(InsertedPHINodes.find(PH) != InsertedPHINodes.end()) - continue; - /*if(isa(I)) { - std::string name = PH->getName(); - if (strncmp(name.c_str(), "baseptr.", 8) == 0) continue; - }*/ - PHINode *AI_New; - PHINode *BCI_New; - if(!Prev) { - AI_New = PHINode::Create(AI->getType(), - PH->getNumIncomingValues(), - PH->getName().str() + ".md", - PH); - BCI_New = PHINode::Create(BCI->getType(), - PH->getNumIncomingValues(), - PH->getName().str() + ".baseptr", - PH); - for(unsigned c = 0; c < PH->getNumIncomingValues(); c++) { - if(PH->getIncomingValue(c) == I) { - AI_New->addIncoming(AI, PH->getIncomingBlock(c)); - BCI_New->addIncoming(BCI, PH->getIncomingBlock(c)); - } - else { - AI_New->addIncoming(Constant::getNullValue(AI->getType()), PH->getIncomingBlock(c)); - BCI_New->addIncoming(Constant::getNullValue(BCI->getType()), PH->getIncomingBlock(c)); - } - } - PHINode_MD_Map[PH] = AI_New; - PHINode_BasePtr_Map[PH] = BCI_New; - InsertedPHINodes.insert(AI_New); - InsertedPHINodes.insert(BCI_New); - visitUses(PH, AI_New, BCI_New); - } - else { - for(unsigned c = 0; c < PH->getNumIncomingValues(); c++) { - if(PH->getIncomingValue(c) == I) { - Prev->setIncomingValue(c, AI); - PrevBasePtr->setIncomingValue(c, BCI); - } - } - } - } else if(BitCastInst *BI = dyn_cast(*II)) { - BitCast_MD_Map[BI] = AI; - visitUses(BI, AI, BCI); - //CallInst::Create(checkTypeInst, Args.begin(), Args.end(), "", cast(II.getUse().getUser())); - /*} else if(PtrToIntInst *P2I = dyn_cast(II)) { - visitUses(P2I, AI, BCI); - } else if(IntToPtrInst *I2P = dyn_cast(II)) { - visitUses(I2P, AI, BCI);*/ - }else { - CallInst::Create(checkTypeInst, Args, "", cast(II.getUse().getUser())); - } - } - return true; -} - -// Insert runtime checks before all store instructions. -bool TypeChecks::visitStoreInst(Module &M, StoreInst &SI) { - if(isa(SI.getOperand(0)->stripPointerCasts())) { - return false; - } - if(PHINode *PH = dyn_cast(SI.getOperand(0)->stripPointerCasts())) { - if(PHINode_MD_Map.find(PH) != PHINode_MD_Map.end()) - return false; - } - if(SelectInst *SelI = dyn_cast(SI.getOperand(0)->stripPointerCasts())) { - if(SelectInst_MD_Map.find(SelI) != SelectInst_MD_Map.end()) - return false; - } - if(BitCastInst *BI = dyn_cast(SI.getOperand(0)->stripPointerCasts())) { - if(BitCast_MD_Map.find(BI) != BitCast_MD_Map.end()) - return false; - } - // Cast the pointer operand to i8* for the runtime function. - Value *BCI = castTo(SI.getPointerOperand(), VoidPtrTy, "", &SI); - - std::vector Args; - Args.push_back(BCI); - Args.push_back(getTypeMarkerConstant(SI.getOperand(0))); // SI.getValueOperand() - Args.push_back(getSizeConstant(SI.getOperand(0)->getType())); - Args.push_back(getTagCounter()); - - // Create the call to the runtime check and place it before the store instruction. - CallInst::Create(trackStoreInst, Args, "", &SI); - numStoreChecks++; - - return true; -} diff --git a/lib/AssistDS/TypeChecksOpt.cpp b/lib/AssistDS/TypeChecksOpt.cpp deleted file mode 100644 index 490458959..000000000 --- a/lib/AssistDS/TypeChecksOpt.cpp +++ /dev/null @@ -1,259 +0,0 @@ -//===---------- TypeChecksOpt.h - Remove safe runtime type checks ---------===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This pass removes type checks that are statically proven safe -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "dsa-type-checks-opt" -#include "assistDS/TypeChecksOpt.h" -#include "llvm/IR/Constants.h" -#include "llvm/Transforms/Utils/Cloning.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/Module.h" -#include "smack/Debug.h" -#include "llvm/IR/InstIterator.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/IR/Intrinsics.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/ADT/Statistic.h" - -#include - -using namespace llvm; - -char TypeChecksOpt::ID = 0; - -static RegisterPass -TC("typechecks-opt", "Remove safe runtime type checks", false, true); - -// Pass statistics -STATISTIC(numSafe, "Number of statically proven safe type checks"); - -static Type *VoidTy = 0; -static Type *Int8Ty = 0; -static Type *Int32Ty = 0; -static Type *Int64Ty = 0; -static PointerType *VoidPtrTy = 0; -static Type *TypeTagTy = 0; -static Type *TypeTagPtrTy = 0; -static Constant *trackGlobal; -static Constant *trackStringInput; -static Constant *trackInitInst; -static Constant *trackUnInitInst; -static Constant *trackStoreInst; -static Constant *copyTypeInfo; -static Constant *setTypeInfo; -static Constant *checkTypeInst; -static Constant *getTypeTag; -static Constant *MallocFunc; - -bool TypeChecksOpt::runOnModule(Module &M) { - TS = &getAnalysis >(); - - // Create the necessary prototypes - VoidTy = IntegerType::getVoidTy(M.getContext()); - Int8Ty = IntegerType::getInt8Ty(M.getContext()); - Int32Ty = IntegerType::getInt32Ty(M.getContext()); - Int64Ty = IntegerType::getInt64Ty(M.getContext()); - VoidPtrTy = PointerType::getUnqual(Int8Ty); - TypeTagTy = Int8Ty; - TypeTagPtrTy = PointerType::getUnqual(TypeTagTy); - - Constant *memsetF = M.getOrInsertFunction ("llvm.memset.i64", VoidTy, - VoidPtrTy, - Int8Ty, - Int64Ty, - Int32Ty); - trackGlobal = M.getOrInsertFunction("trackGlobal", - VoidTy, - VoidPtrTy,/*ptr*/ - TypeTagTy,/*type*/ - Int64Ty,/*size*/ - Int32Ty /*tag*/); - trackInitInst = M.getOrInsertFunction("trackInitInst", - VoidTy, - VoidPtrTy,/*ptr*/ - Int64Ty,/*size*/ - Int32Ty /*tag*/); - trackUnInitInst = M.getOrInsertFunction("trackUnInitInst", - VoidTy, - VoidPtrTy,/*ptr*/ - Int64Ty,/*size*/ - Int32Ty /*tag*/); - trackStoreInst = M.getOrInsertFunction("trackStoreInst", - VoidTy, - VoidPtrTy,/*ptr*/ - TypeTagTy,/*type*/ - Int64Ty,/*size*/ - Int32Ty /*tag*/); - checkTypeInst = M.getOrInsertFunction("checkType", - VoidTy, - TypeTagTy,/*type*/ - Int64Ty,/*size*/ - TypeTagPtrTy, - VoidPtrTy,/*ptr*/ - Int32Ty /*tag*/); - copyTypeInfo = M.getOrInsertFunction("copyTypeInfo", - VoidTy, - VoidPtrTy,/*dest ptr*/ - VoidPtrTy,/*src ptr*/ - Int64Ty,/*size*/ - Int32Ty /*tag*/); - setTypeInfo = M.getOrInsertFunction("setTypeInfo", - VoidTy, - VoidPtrTy,/*dest ptr*/ - TypeTagPtrTy,/*metadata*/ - Int64Ty,/*size*/ - TypeTagTy, - VoidPtrTy, - Int32Ty /*tag*/); - trackStringInput = M.getOrInsertFunction("trackStringInput", - VoidTy, - VoidPtrTy, - Int32Ty ); - getTypeTag = M.getOrInsertFunction("getTypeTag", - VoidTy, - VoidPtrTy, /*ptr*/ - Int64Ty, /*size*/ - TypeTagPtrTy, /*dest for type tag*/ - Int32Ty /*tag*/); - MallocFunc = M.getFunction("malloc"); - - for(Value::user_iterator User = trackGlobal->user_begin(); User != trackGlobal->user_end(); ++User) { - CallInst *CI = dyn_cast(*User); - assert(CI); - if(TS->isTypeSafe(CI->getOperand(1)->stripPointerCasts(), CI->getParent()->getParent())) { - std::vectorArgs; - Args.push_back(CI->getOperand(1)); - Args.push_back(CI->getOperand(3)); - Args.push_back(CI->getOperand(4)); - CallInst::Create(trackInitInst, Args, "", CI); - toDelete.push_back(CI); - } - } - - for(Value::user_iterator User = checkTypeInst->user_begin(); User != checkTypeInst->user_end(); ++User) { - CallInst *CI = dyn_cast(*User); - assert(CI); - - if(TS->isTypeSafe(CI->getOperand(4)->stripPointerCasts(), CI->getParent()->getParent())) { - toDelete.push_back(CI); - } - } - - for(Value::user_iterator User = trackStoreInst->user_begin(); User != trackStoreInst->user_end(); ++User) { - CallInst *CI = dyn_cast(*User); - assert(CI); - - if(TS->isTypeSafe(CI->getOperand(1)->stripPointerCasts(), CI->getParent()->getParent())) { - toDelete.push_back(CI); - } - } - - // for alloca's if they are type known - // assume initialized with TOP - for(Value::user_iterator User = trackUnInitInst->user_begin(); User != trackUnInitInst->user_end(); ) { - CallInst *CI = dyn_cast(*(User++)); - assert(CI); - - // check if operand is an alloca inst. - if(TS->isTypeSafe(CI->getOperand(1)->stripPointerCasts(), CI->getParent()->getParent())) { - CI->setCalledFunction(trackInitInst); - - if(AllocaInst *AI = dyn_cast(CI->getOperand(1)->stripPointerCasts())) { - // Initialize the allocation to NULL - std::vector Args2; - Args2.push_back(CI->getOperand(1)); - Args2.push_back(ConstantInt::get(Int8Ty, 0)); - Args2.push_back(CI->getOperand(2)); - Args2.push_back(ConstantInt::get(Int32Ty, AI->getAlignment())); - CallInst::Create(memsetF, Args2, "", CI); - } - } - } - - if(MallocFunc) { - for(Value::user_iterator User = MallocFunc->user_begin(); User != MallocFunc->user_end(); User ++) { - CallInst *CI = dyn_cast(*User); - if(!CI) - continue; - if(TS->isTypeSafe(CI, CI->getParent()->getParent())){ - CastInst *BCI = BitCastInst::CreatePointerCast(CI, VoidPtrTy); - CastInst *Size = CastInst::CreateSExtOrBitCast(CI->getOperand(1), Int64Ty); - Size->insertAfter(CI); - BCI->insertAfter(Size); - std::vectorArgs; - Args.push_back(BCI); - Args.push_back(Size); - Args.push_back(ConstantInt::get(Int32Ty, 0)); - CallInst *CINew = CallInst::Create(trackInitInst, Args); - CINew->insertAfter(BCI); - } - } - } - - // also do for mallocs/calloc/other allocators??? - // other allocators?? - - for(Value::user_iterator User = copyTypeInfo->user_begin(); User != copyTypeInfo->user_end(); ++User) { - CallInst *CI = dyn_cast(*User); - assert(CI); - - if(TS->isTypeSafe(CI->getOperand(1)->stripPointerCasts(), CI->getParent()->getParent())) { - std::vector Args; - Args.push_back(CI->getOperand(1)); - Args.push_back(CI->getOperand(3)); // size - Args.push_back(CI->getOperand(4)); - CallInst::Create(trackInitInst, Args, "", CI); - toDelete.push_back(CI); - } - } - for(Value::user_iterator User = setTypeInfo->user_begin(); User != setTypeInfo->user_end(); ++User) { - CallInst *CI = dyn_cast(*User); - assert(CI); - - if(TS->isTypeSafe(CI->getOperand(1)->stripPointerCasts(), CI->getParent()->getParent())) { - std::vector Args; - Args.push_back(CI->getOperand(1)); - Args.push_back(CI->getOperand(3)); // size - Args.push_back(CI->getOperand(6)); - CallInst::Create(trackInitInst, Args, "", CI); - toDelete.push_back(CI); - } - } - - for(Value::user_iterator User = getTypeTag->user_begin(); User != getTypeTag->user_end(); ++User) { - CallInst *CI = dyn_cast(*User); - assert(CI); - if(TS->isTypeSafe(CI->getOperand(1)->stripPointerCasts(), CI->getParent()->getParent())) { - AllocaInst *AI = dyn_cast(CI->getOperand(3)->stripPointerCasts()); - assert(AI); - std::vectorArgs; - Args.push_back(CI->getOperand(3)); - Args.push_back(ConstantInt::get(Int8Ty, 255)); - Args.push_back(CI->getOperand(2)); - Args.push_back(ConstantInt::get(Int32Ty, AI->getAlignment())); - CallInst::Create(memsetF, Args, "", CI); - toDelete.push_back(CI); - } - } - - numSafe += toDelete.size(); - - while(!toDelete.empty()) { - Instruction *I = toDelete.back(); - toDelete.pop_back(); - I->eraseFromParent(); - } - - return (numSafe > 0); -} - - diff --git a/lib/DSA/AddressTakenAnalysis.cpp b/lib/DSA/AddressTakenAnalysis.cpp deleted file mode 100644 index 281e0c63f..000000000 --- a/lib/DSA/AddressTakenAnalysis.cpp +++ /dev/null @@ -1,89 +0,0 @@ -//===-- AddressTakenAnalysis.cpp - Address Taken Functions Finding Pass ---===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This pass helps find which functions are address taken in a module. -// Functions are considered to be address taken if they are either stored, -// or passed as arguments to functions. -// -// -//===----------------------------------------------------------------------===// - -#include "llvm/Pass.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/Instructions.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/FormattedStream.h" -#include "smack/Debug.h" -#include "llvm/IR/CallSite.h" - -#include -#include - -#include "dsa/AddressTakenAnalysis.h" - -using namespace llvm; - - -AddressTakenAnalysis::~AddressTakenAnalysis() {} - -static bool isAddressTaken(Value* V) { - for (Value::const_use_iterator I = V->use_begin(), E = V->use_end(); I != E; ++I) { - User *U = I->getUser(); - if(isa(U)) - return true; - if (!isa(U) && !isa(U)) { - if(U->use_empty()) - continue; - if(isa(U)) { - if(isAddressTaken(U)) - return true; - } else { - if (Constant *C = dyn_cast(U)) { - if (ConstantExpr *CE = dyn_cast(C)) { - if (CE->getOpcode() == Instruction::BitCast) { - return isAddressTaken(CE); - } - } - } - return true; - } - - // FIXME: Can be more robust here for weak aliases that - // are never used - } else { - llvm::CallSite CS(cast(U)); - if (!CS.isCallee(&*I)) - return true; - } - } - return false; -} - -bool AddressTakenAnalysis::runOnModule(llvm::Module& M) { - for (Function &F : M) { - if(isAddressTaken(&F)) { - addressTakenFunctions.insert(&F); - } - } - - return false; -} - -bool AddressTakenAnalysis::hasAddressTaken(llvm::Function *F){ - return addressTakenFunctions.find(F) != addressTakenFunctions.end(); -} - -void AddressTakenAnalysis::getAnalysisUsage(llvm::AnalysisUsage &AU) const { - AU.setPreservesAll(); -} - -char AddressTakenAnalysis::ID; -static RegisterPass A("ata", "Identify Address Taken Functions"); diff --git a/lib/DSA/AllocatorIdentification.cpp b/lib/DSA/AllocatorIdentification.cpp deleted file mode 100644 index 220b8fc51..000000000 --- a/lib/DSA/AllocatorIdentification.cpp +++ /dev/null @@ -1,198 +0,0 @@ -//===-- AllocatorIdentification.cpp - Identify wrappers to allocators -----===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// A pass to identify functions that act as wrappers to malloc and other -// allocators. -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "allocator-identify" - -#include "llvm/IR/Constants.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" -#include "llvm/Pass.h" -#include "llvm/Analysis/LoopInfo.h" -#include "llvm/Transforms/Utils/Cloning.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/Support/FormattedStream.h" -#include "smack/Debug.h" - -#include -#include -#include -#include - -#include "dsa/AllocatorIdentification.h" - -using namespace llvm; - -STATISTIC(numAllocators, "Number of malloc-like allocators"); -STATISTIC(numDeallocators, "Number of free-like deallocators"); - -bool AllocIdentify::flowsFrom(Value *Dest,Value *Src) { - if(Dest == Src) - return true; - if(ReturnInst *Ret = dyn_cast(Dest)) { - return flowsFrom(Ret->getReturnValue(), Src); - } - if(PHINode *PN = dyn_cast(Dest)) { - Function *F = PN->getParent()->getParent(); - LoopInfo &LI = getAnalysis(*F).getLoopInfo(); - // If this is a loop phi, ignore. - if(LI.isLoopHeader(PN->getParent())) - return false; - bool ret = true; - for (unsigned i = 0, e = PN->getNumIncomingValues(); i != e; ++i) { - ret = ret && flowsFrom(PN->getIncomingValue(i), Src); - } - return ret; - } - if(BitCastInst *BI = dyn_cast(Dest)) { - return flowsFrom(BI->getOperand(0), Src); - } - if(isa(Dest)) - return true; - return false; -} - -bool isNotStored(Value *V) { - // check that V is not stored to a location that is accessible outside this fn - for(Value::user_iterator ui = V->user_begin(), ue = V->user_end(); - ui != ue; ++ui) { - if(isa(*ui)) - return false; - if(isa(*ui)) - continue; - if(isa(*ui)) - continue; - if(BitCastInst *BI = dyn_cast(*ui)) { - if(isNotStored(BI)) - continue; - else - return false; - } - if(PHINode *PN = dyn_cast(*ui)) { - if(isNotStored(PN)) - continue; - else - return false; - } - - return false; - } - return true; -} - -AllocIdentify::AllocIdentify() : ModulePass(ID) {} -AllocIdentify::~AllocIdentify() {} - -bool AllocIdentify::runOnModule(Module& M) { - - allocators.emplace("malloc"); - allocators.emplace("calloc"); - //allocators.emplace("realloc"); - //allocators.emplace("memset"); - deallocators.emplace("free"); - deallocators.emplace("cfree"); - - bool changed; - do { - changed = false; - std::set TempAllocators; - TempAllocators.insert( allocators.begin(), allocators.end()); - std::set::iterator it; - for(it = TempAllocators.begin(); it != TempAllocators.end(); ++it) { - Function* F = M.getFunction(*it); - if(!F) - continue; - for(Value::user_iterator ui = F->user_begin(), ue = F->user_end(); - ui != ue; ++ui) { - // iterate though all calls to malloc - if (CallInst* CI = dyn_cast(*ui)) { - // The function that calls malloc could be a potential allocator - Function *WrapperF = CI->getParent()->getParent(); - if(WrapperF->doesNotReturn()) - continue; - if(!(WrapperF->getReturnType()->isPointerTy())) - continue; - bool isWrapper = true; - for (Function::iterator BBI = WrapperF->begin(), E = WrapperF->end(); BBI != E; ) { - BasicBlock &BB = *BBI++; - - // Only look at return blocks. - ReturnInst *Ret = dyn_cast(BB.getTerminator()); - if (Ret == 0) continue; - - //check for ALL return values - if(flowsFrom(Ret, CI)) { - continue; - } else { - isWrapper = false; - break; - } - // if true for all return add to list of allocators - } - if(isWrapper) - isWrapper = isWrapper && isNotStored(CI); - if(isWrapper) { - changed = (allocators.find(WrapperF->getName()) == allocators.end()); - if(changed) { - ++numAllocators; - allocators.emplace(WrapperF->getName()); - SDEBUG(errs() << WrapperF->getName().str() << "\n"); - } - } - } - } - } - } while(changed); - - do { - changed = false; - std::set TempDeallocators; - TempDeallocators.insert( deallocators.begin(), deallocators.end()); - std::set::iterator it; - for(it = TempDeallocators.begin(); it != TempDeallocators.end(); ++it) { - Function* F = M.getFunction(*it); - - if(!F) - continue; - for(Value::user_iterator ui = F->user_begin(), ue = F->user_end(); - ui != ue; ++ui) { - // iterate though all calls to malloc - if (CallInst* CI = dyn_cast(*ui)) { - // The function that calls malloc could be a potential allocator - Function *WrapperF = CI->getParent()->getParent(); - - if(WrapperF->arg_size() != 1) - continue; - if(!WrapperF->arg_begin()->getType()->isPointerTy()) - continue; - Argument *arg = dyn_cast(WrapperF->arg_begin()); - if(flowsFrom(CI->getOperand(1), arg)) { - changed = (deallocators.find(WrapperF->getName()) == deallocators.end()); - if(changed) { - ++numDeallocators; - deallocators.emplace(WrapperF->getName()); - SDEBUG(errs() << WrapperF->getName().str() << "\n"); - } - } - } - } - } - } while(changed); - return false; -} -void AllocIdentify::getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired(); - AU.setPreservesAll(); -} - -char AllocIdentify::ID = 0; -static RegisterPass -X("alloc-identify", "Identify allocator wrapper functions"); diff --git a/lib/DSA/Basic.cpp b/lib/DSA/Basic.cpp deleted file mode 100644 index 0088aef40..000000000 --- a/lib/DSA/Basic.cpp +++ /dev/null @@ -1,89 +0,0 @@ -//===- Basic.cpp ----------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Implementation of the basic data structure analysis pass. It simply assumes -// that all pointers can points to all possible locations. -// -//===----------------------------------------------------------------------===// - -#include "dsa/DataStructure.h" -#include "dsa/DSGraph.h" - -#include "llvm/IR/InstVisitor.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Intrinsics.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/InstIterator.h" -#include "llvm/IR/GetElementPtrTypeIterator.h" - -using namespace llvm; - -static RegisterPass -X("dsa-basic", "Basic Data Structure Analysis(No Analysis)"); - -char BasicDataStructures::ID = 0; - -bool BasicDataStructures::runOnModule(Module &M) { - init(&M.getDataLayout()); - - // - // Create a void pointer type. This is simply a pointer to an 8 bit value. - // - - DSNode * GVNodeInternal = new DSNode(GlobalsGraph); - DSNode * GVNodeExternal = new DSNode(GlobalsGraph); - for (Module::global_iterator I = M.global_begin(), E = M.global_end(); - I != E; ++I) { - if (I->isDeclaration() || (!(I->hasInternalLinkage()))) { - GlobalsGraph->getNodeForValue(&*I).mergeWith(GVNodeExternal); - } else { - GlobalsGraph->getNodeForValue(&*I).mergeWith(GVNodeInternal); - } - } - - GVNodeInternal->foldNodeCompletely(); - GVNodeInternal->maskNodeTypes(DSNode::IncompleteNode); - - GVNodeExternal->foldNodeCompletely(); - GVNodeExternal->setExternalMarker(); - - // Next step, iterate through the nodes in the globals graph, unioning - // together the globals into equivalence classes. - formGlobalECs(); - - for (Function &F : M) { - if (!F.isDeclaration()) { - DSGraph* G = new DSGraph(GlobalECs, getDataLayout(), *TypeSS, GlobalsGraph); - DSNode * Node = new DSNode(G); - - if (!F.hasInternalLinkage()) - Node->setExternalMarker(); - - // Create scalar nodes for all pointer arguments... - for (auto &Arg : F.args()) { - if (isa(Arg.getType())) { - G->getNodeForValue(&Arg).mergeWith(Node); - } - } - - for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ++I) { - G->getNodeForValue(&*I).mergeWith(Node); - } - - Node->foldNodeCompletely(); - Node->maskNodeTypes(DSNode::IncompleteNode); - - setDSGraph(F, G); - } - } - - return false; -} diff --git a/lib/DSA/BottomUpClosure.cpp b/lib/DSA/BottomUpClosure.cpp deleted file mode 100644 index 7f1281c25..000000000 --- a/lib/DSA/BottomUpClosure.cpp +++ /dev/null @@ -1,731 +0,0 @@ -//===- BottomUpClosure.cpp - Compute bottom-up interprocedural closure ----===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements the BUDataStructures class, which represents the -// Bottom-Up Interprocedural closure of the data structure graph over the -// program. This is useful for applications like pool allocation, but **not** -// applications like alias analysis. -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "dsa-bu" -#include "llvm/IR/Constants.h" -#include "dsa/DataStructure.h" -#include "dsa/DSGraph.h" -#include "llvm/IR/Module.h" -#include "llvm/ADT/Statistic.h" -#include "smack/Debug.h" -#include "llvm/Support/FormattedStream.h" - -using namespace llvm; - -namespace { - STATISTIC (MaxSCC, "Maximum SCC Size in Call Graph"); - STATISTIC (NumInlines, "Number of graphs inlined"); - STATISTIC (NumCallEdges, "Number of 'actual' call edges"); - STATISTIC (NumIndResolved, "Number of resolved IndCalls"); - STATISTIC (NumIndUnresolved, "Number of unresolved IndCalls"); - // NumEmptyCalls = NumIndUnresolved + Number of calls to external functions - STATISTIC (NumEmptyCalls, "Number of calls we know nothing about"); - STATISTIC (NumRecalculations, "Number of DSGraph recalculations"); - STATISTIC (NumRecalculationsSkipped, "Number of DSGraph recalculations skipped"); - - RegisterPass - X("dsa-bu", "Bottom-up Data Structure Analysis"); -} - -char BUDataStructures::ID; - -// run - Calculate the bottom up data structure graphs for each function in the -// program. -// -bool BUDataStructures::runOnModule(Module &M) { - init(&getAnalysis(), true, true, false, false ); - - return runOnModuleInternal(M); -} - -// BU: -// Construct the callgraph from the local graphs -// Find SCCs -// inline bottom up -// -bool BUDataStructures::runOnModuleInternal(Module& M) { - - // - // Make sure we have a DSGraph for all declared functions in the Module. - // While we may not need them in this DSA pass, a later DSA pass may ask us - // for their DSGraphs, and we want to have them if asked. - // - for (Function &F : M) { - if (!(F.isDeclaration())){ - getOrCreateGraph(&F); - } - } - - // - // Do a post-order traversal of the SCC callgraph and do bottom-up inlining. - // - postOrderInline (M); - - // At the end of the bottom-up pass, the globals graph becomes complete. - // FIXME: This is not the right way to do this, but it is sorta better than - // nothing! In particular, externally visible globals and unresolvable call - // nodes at the end of the BU phase should make things that they point to - // incomplete in the globals graph. - // - - GlobalsGraph->removeTriviallyDeadNodes(); - GlobalsGraph->maskIncompleteMarkers(); - - // Mark external globals incomplete. - GlobalsGraph->markIncompleteNodes(DSGraph::IgnoreGlobals); - GlobalsGraph->computeExternalFlags(DSGraph::DontMarkFormalsExternal); - GlobalsGraph->computeIntPtrFlags(); - - // - // Create equivalence classes for aliasing globals so that we only need to - // record one global per DSNode. - // - formGlobalECs(); - - // Merge the globals variables (not the calls) from the globals graph back - // into the individual function's graph so that changes made to globals during - // BU can be reflected. This is specifically needed for correct call graph - // - for (Function &F : M) { - if (!(F.isDeclaration())){ - DSGraph *Graph = getOrCreateGraph(&F); - cloneGlobalsInto(Graph, DSGraph::DontCloneCallNodes | - DSGraph::DontCloneAuxCallNodes); - Graph->buildCallGraph(callgraph, GlobalFunctionList, filterCallees); - Graph->maskIncompleteMarkers(); - Graph->markIncompleteNodes(DSGraph::MarkFormalArgs | - DSGraph::IgnoreGlobals); - Graph->computeExternalFlags(DSGraph::DontMarkFormalsExternal); - Graph->computeIntPtrFlags(); - } - } - - // Once the correct flags have been calculated. Update the callgraph. - for (Function &F : M) { - if (!(F.isDeclaration())){ - DSGraph *Graph = getOrCreateGraph(&F); - Graph->buildCompleteCallGraph(callgraph, - GlobalFunctionList, filterCallees); - } - } - - NumCallEdges += callgraph.size(); - - // Put the call graph in canonical form - callgraph.buildSCCs(); - callgraph.buildRoots(); - - SDEBUG(print(errs(), &M)); - return false; -} - -// -// Function: applyCallsiteFilter -// -// Description: -// Given a DSCallSite, and a list of functions, filter out the ones -// that aren't callable from the given Callsite. -// -// Does no filtering if 'filterCallees' is set to false. -// -void BUDataStructures:: -applyCallsiteFilter(const DSCallSite &DCS, FuncSet &Callees) { - - if (!filterCallees) return; - - FuncSet::iterator I = Callees.begin(); - CallSite CS = DCS.getCallSite(); - while (I != Callees.end()) { - if (functionIsCallable(CS, *I)) { - ++I; - } else { - I = Callees.erase(I); - } - } -} - -// -// Function: getAllCallees() -// -// Description: -// Given a DSCallSite, add to the list the functions that can be called by -// the call site *if* it is resolvable. Uses 'applyCallsiteFilter' to -// only add the functions that are valid targets of this callsite. -// -void BUDataStructures:: -getAllCallees(const DSCallSite &CS, FuncSet &Callees) { - // - // FIXME: Should we check for the Unknown flag on indirect call sites? - // - // Direct calls to functions that have bodies are always resolvable. - // Indirect function calls that are for a complete call site (the analysis - // knows everything about the call site) and do not target external functions - // are also resolvable. - // - if (CS.isDirectCall()) { - if (!CS.getCalleeFunc()->isDeclaration()) - Callees.insert(CS.getCalleeFunc()); - } else if (CS.getCalleeNode()->isCompleteNode()) { - // Get all callees. - if (!CS.getCalleeNode()->isExternFuncNode()) { - // Get all the callees for this callsite - FuncSet TempCallees; - CS.getCalleeNode()->addFullFunctionSet(TempCallees); - // Filter out the ones that are invalid targets with respect - // to this particular callsite. - applyCallsiteFilter(CS, TempCallees); - // Insert the remaining callees (legal ones, if we're filtering) - // into the master 'Callees' list - Callees.insert(TempCallees.begin(), TempCallees.end()); - } - } -} - -// -// Function: getAllAuxCallees() -// -// Description: -// Return a list containing all of the resolvable callees in the auxiliary -// list for the specified graph in the Callees vector. -// -// Inputs: -// G - The DSGraph for which the callers wants a list of resolvable call -// sites. -// -// Outputs: -// Callees - A list of all functions that can be called from resolvable call -// sites. This list is always cleared by this function before any -// functions are added to it. -// -void BUDataStructures:: -getAllAuxCallees (DSGraph* G, FuncSet & Callees) { - // - // Clear out the list of callees. - // - Callees.clear(); - for (DSGraph::afc_iterator I = G->afc_begin(), E = G->afc_end(); I != E; ++I) - getAllCallees(*I, Callees); -} - -// -// Method: postOrderInline() -// -// Description: -// This methods does a post order traversal of the call graph and performs -// bottom-up inlining of the DSGraphs. -// -void -BUDataStructures::postOrderInline (Module & M) { - // Variables used for Tarjan SCC-finding algorithm. These are passed into - // the recursive function used to find SCCs. - std::vector Stack; - std::map ValMap; - unsigned NextID = 1; - - - // Do post order traversal on the global ctors. Use this information to update - // the globals graph. - const char *Name = "llvm.global_ctors"; - GlobalVariable *GV = M.getNamedGlobal(Name); - if (GV && !(GV->isDeclaration()) && !(GV->hasLocalLinkage())) { - // Should be an array of '{ int, void ()* }' structs. The first value is - // the init priority, which we ignore. - ConstantArray *InitList = dyn_cast(GV->getInitializer()); - if (InitList) { - for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i) - if (ConstantStruct *CS = dyn_cast(InitList->getOperand(i))) { - if (CS->getNumOperands() != 2) - break; // Not array of 2-element structs. - Constant *FP = CS->getOperand(1); - if (FP->isNullValue()) - break; // Found a null terminator, exit. - - if (ConstantExpr *CE = dyn_cast(FP)) - if (CE->isCast()) - FP = CE->getOperand(0); - Function *F = dyn_cast(FP); - if (F && !F->isDeclaration() && !ValMap.count(F)) { - calculateGraphs(F, Stack, NextID, ValMap); - CloneAuxIntoGlobal(getDSGraph(*F)); - } - } - GlobalsGraph->removeTriviallyDeadNodes(); - GlobalsGraph->maskIncompleteMarkers(); - - // Mark external globals incomplete. - GlobalsGraph->markIncompleteNodes(DSGraph::IgnoreGlobals); - GlobalsGraph->computeExternalFlags(DSGraph::DontMarkFormalsExternal); - GlobalsGraph->computeIntPtrFlags(); - - // - // Create equivalence classes for aliasing globals so that we only need to - // record one global per DSNode. - // - formGlobalECs(); - // propogte information calculated - // from the globals graph to the other graphs. - for (Module::iterator F = M.begin(); F != M.end(); ++F) { - if (!(F->isDeclaration())){ - DSGraph *Graph = getDSGraph(*F); - cloneGlobalsInto(Graph, DSGraph::DontCloneCallNodes | - DSGraph::DontCloneAuxCallNodes); - Graph->buildCallGraph(callgraph, GlobalFunctionList, filterCallees); - Graph->maskIncompleteMarkers(); - Graph->markIncompleteNodes(DSGraph::MarkFormalArgs | - DSGraph::IgnoreGlobals); - Graph->computeExternalFlags(DSGraph::DontMarkFormalsExternal); - Graph->computeIntPtrFlags(); - } - } - } - } - - // - // Start the post order traversal with the main() function. If there is no - // main() function, don't worry; we'll have a separate traversal for inlining - // graphs for functions not reachable from main(). - // - Function *MainFunc = M.getFunction ("main"); - if (MainFunc && !MainFunc->isDeclaration()) { - calculateGraphs(MainFunc, Stack, NextID, ValMap); - CloneAuxIntoGlobal(getDSGraph(*MainFunc)); - } - - // - // Calculate the graphs for any functions that are unreachable from main... - // - for (Function &F : M) - if (!F.isDeclaration() && !ValMap.count(&F)) { - if (MainFunc) - SDEBUG(errs() << debugname << ": Function unreachable from main: " - << F.getName() << "\n"); - calculateGraphs(&F, Stack, NextID, ValMap); // Calculate all graphs. - CloneAuxIntoGlobal(getDSGraph(F)); - - // Mark this graph as processed. Do this by finding all functions - // in the graph that map to it, and mark them visited. - // Note that this really should be handled neatly by calculateGraphs - // itself, not here. However this catches the worst offenders. - DSGraph *G = getDSGraph(F); - for(DSGraph::retnodes_iterator RI = G->retnodes_begin(), - RE = G->retnodes_end(); RI != RE; ++RI) { - if (getDSGraph(*RI->first) == G) { - if (!ValMap.count(RI->first)) - ValMap[RI->first] = ~0U; - else - assert(ValMap[RI->first] == ~0U); - } - } - } - return; -} - -static bool hasNewCallees(svset &New, - svset &Old) { - if (New.size() > Old.size()) return true; - - svset::iterator NI = New.begin(), NE = New.end(); - - for (; NI != NE; ++NI) - if (!Old.count(*NI)) return true; - - return false; -} - -// -// Method: calculateGraphs() -// -// Description: -// Perform recursive bottom-up inlining of DSGraphs from callee to caller. -// -// Inputs: -// F - The function which should have its callees' DSGraphs merged into its -// own DSGraph. -// Stack - The stack used for Tarjan's SCC-finding algorithm. -// NextID - The nextID value used for Tarjan's SCC-finding algorithm. -// ValMap - The map used for Tarjan's SCC-finding algorithm. -// -// Return value: -// -unsigned -BUDataStructures::calculateGraphs (const Function *F, - TarjanStack & Stack, - unsigned & NextID, - TarjanMap & ValMap) { - assert(!ValMap.count(F) && "Shouldn't revisit functions!"); - unsigned Min = NextID++, MyID = Min; - ValMap[F] = Min; - Stack.push_back(F); - - // - // FIXME: This test should be generalized to be any function that we have - // already processed in the case when there isn't a main() or there are - // unreachable functions! - // - if (F->isDeclaration()) { // sprintf, fprintf, sscanf, etc... - // No callees! - Stack.pop_back(); - ValMap[F] = ~0; - return Min; - } - - // - // Get the DSGraph of the current function. Make one if one doesn't exist. - // - DSGraph* Graph = getOrCreateGraph(F); - - // - // Find all callee functions. Use the DSGraph for this (do not use the call - // graph (DSCallgraph) as we're still in the process of constructing it). - // - FuncSet CalleeFunctions; - getAllAuxCallees(Graph, CalleeFunctions); - - // - // Iterate through each call target (these are the edges out of the current - // node (i.e., the current function) in Tarjan graph parlance). Find the - // minimum assigned ID. - // - for (FuncSet::iterator I = CalleeFunctions.begin(), E = CalleeFunctions.end(); - I != E; ++I) { - const Function *Callee = *I; - unsigned M; - // - // If we have not visited this callee before, visit it now (this is the - // post-order component of the Bottom-Up algorithm). Otherwise, look up - // the assigned ID value from the Tarjan Value Map. - // - TarjanMap::iterator It = ValMap.find(Callee); - if (It == ValMap.end()) // No, visit it now. - M = calculateGraphs(Callee, Stack, NextID, ValMap); - else // Yes, get it's number. - M = It->second; - - // - // If we've found a function with a smaller ID than this funtion, record - // that ID as the minimum ID. - // - if (M < Min) Min = M; - } - - assert(ValMap[F] == MyID && "SCC construction assumption wrong!"); - - // - // If the minimum ID found is not this function's ID, then this function is - // part of a larger SCC. - // - if (Min != MyID) - return Min; - - // - // If this is a new SCC, process it now. - // - if (Stack.back() == F) { // Special case the single "SCC" case here. - SDEBUG(errs() << "Visiting single node SCC #: " << MyID << " fn: " - << F->getName() << "\n"); - Stack.pop_back(); - SDEBUG(errs() << " [BU] Calculating graph for: " << F->getName()<< "\n"); - DSGraph* G = getOrCreateGraph(F); - calculateGraph(G); - SDEBUG(errs() << " [BU] Done inlining: " << F->getName() << " [" - << G->getGraphSize() << "+" << G->getAuxFunctionCalls().size() - << "]\n"); - - if (MaxSCC < 1) MaxSCC = 1; - - // - // Should we revisit the graph? Only do it if there are now new resolvable - // callees. - FuncSet NewCallees; - getAllAuxCallees(G, NewCallees); - if (!NewCallees.empty()) { - if (hasNewCallees(NewCallees, CalleeFunctions)) { - SDEBUG(errs() << "Recalculating " << F->getName() << " due to new knowledge\n"); - ValMap.erase(F); - ++NumRecalculations; - return calculateGraphs(F, Stack, NextID, ValMap); - } - ++NumRecalculationsSkipped; - } - ValMap[F] = ~0U; - return MyID; - } else { - unsigned SCCSize = 1; - const Function *NF = Stack.back(); - if(NF != F) - ValMap[NF] = ~0U; - DSGraph* SCCGraph = getDSGraph(*NF); - - // - // First thing first: collapse all of the DSGraphs into a single graph for - // the entire SCC. Splice all of the graphs into one and discard all of - // the old graphs. - // - while (NF != F) { - Stack.pop_back(); - NF = Stack.back(); - if(NF != F) - ValMap[NF] = ~0U; - - DSGraph* NFG = getDSGraph(*NF); - - if (NFG != SCCGraph) { - // Update the Function -> DSG map. - for (DSGraph::retnodes_iterator I = NFG->retnodes_begin(), - E = NFG->retnodes_end(); I != E; ++I) - setDSGraph(*I->first, SCCGraph); - - SCCGraph->spliceFrom(NFG); - delete NFG; - ++SCCSize; - } - } - Stack.pop_back(); - - SDEBUG(errs() << "Calculating graph for SCC #: " << MyID << " of size: " - << SCCSize << "\n"); - - // Compute the Max SCC Size. - if (MaxSCC < SCCSize) - MaxSCC = SCCSize; - - // Clean up the graph before we start inlining a bunch again... - SCCGraph->removeDeadNodes(DSGraph::KeepUnreachableGlobals); - - // Now that we have one big happy family, resolve all of the call sites in - // the graph... - calculateGraph(SCCGraph); - SDEBUG(errs() << " [BU] Done inlining SCC [" << SCCGraph->getGraphSize() - << "+" << SCCGraph->getAuxFunctionCalls().size() << "]\n" - << "DONE with SCC #: " << MyID << "\n"); - FuncSet NewCallees; - getAllAuxCallees(SCCGraph, NewCallees); - if (!NewCallees.empty()) { - if (hasNewCallees(NewCallees, CalleeFunctions)) { - SDEBUG(errs() << "Recalculating SCC Graph " << F->getName() << " due to new knowledge\n"); - ValMap.erase(F); - ++NumRecalculations; - return calculateGraphs(F, Stack, NextID, ValMap); - } - ++NumRecalculationsSkipped; - } - ValMap[F] = ~0U; - return MyID; - } -} - -// -// Method: CloneAuxIntoGlobal() -// -// Description: -// This method takes the specified graph and processes each unresolved call -// site (a call site for which all targets are not yet known). For each -// unresolved call site, it adds it to the globals graph and merges -// information about the call site if the globals graph already had the call -// site in its own list of unresolved call sites. -// -void BUDataStructures::CloneAuxIntoGlobal(DSGraph* G) { - // - // If this DSGraph has no unresolved call sites, do nothing. We do enough - // work that wastes time even when the list is empty that this extra check - // is probably worth it. - // - if (G->afc_begin() == G->afc_end()) - return; - - DSGraph* GG = G->getGlobalsGraph(); - ReachabilityCloner RC(GG, G, 0); - - // - // Determine which called values are both within the local graph DSCallsites - // and the global graph DSCallsites. Note that we require that the global - // graph have a DSNode for the called value. - // - std::map CommonCallValues; - for (DSGraph::afc_iterator ii = G->afc_begin(), ee = G->afc_end(); - ii != ee; - ++ii) { - // - // If the globals graph has a DSNode for the LLVM value used in the local - // unresolved call site, then it might have a DSCallSite for it, too. - // Record this call site as a potential call site that will need to be - // merged. - // - // Otherwise, just add the call site to the globals graph. - // - Value * V = ii->getCallSite().getCalledValue(); - if (GG->hasNodeForValue(V)) { - DSCallSite & DS = *ii; - CommonCallValues[V] = &DS; - } else { - GG->addAuxFunctionCall(RC.cloneCallSite(*ii)); - } - } - - // - // Scan through all the unresolved call sites in the globals graph and see if - // the local graph has a call using the same LLVM value. If so, merge the - // call sites. - // - DSGraph::afc_iterator GGii = GG->afc_begin(); - for (; GGii != GG->afc_end(); ++GGii) { - // - // Determine if this unresolved call site is also in the local graph. - // If so, then merge it. - // - Value * CalledValue = GGii->getCallSite().getCalledValue(); - std::map::iterator v; - v = CommonCallValues.find (CalledValue); - if (v != CommonCallValues.end()) { - // - // Merge the unresolved call site into the globals graph. - // - RC.cloneCallSite(*(v->second)).mergeWith(*GGii); - - // - // Mark that this call site was merged by removing the called LLVM value - // from the set of values common to both the local and global DSGraphs. - // - CommonCallValues.erase (v); - } - } - - // - // We've now merged all DSCallSites that were known both to the local graph - // and the globals graph. Now, there are still some local call sites that - // need to be *added* to the globals graph; they are in DSCallSites remaining - // in CommonCallValues. - // - std::map::iterator v = CommonCallValues.begin (); - for (; v != CommonCallValues.end(); ++v) { - GG->addAuxFunctionCall(RC.cloneCallSite(*(v->second))); - } - - return; -} - - -// -// Description: -// Inline all graphs in the callgraph and remove callsites that are completely -// dealt with -// -void BUDataStructures::calculateGraph(DSGraph* Graph) { - SDEBUG(Graph->AssertGraphOK(); Graph->getGlobalsGraph()->AssertGraphOK()); - Graph->buildCallGraph(callgraph, GlobalFunctionList, filterCallees); - - // Move our call site list into TempFCs so that inline call sites go into the - // new call site list and doesn't invalidate our iterators! - DSGraph::FunctionListTy TempFCs; - DSGraph::FunctionListTy &AuxCallsList = Graph->getAuxFunctionCalls(); - TempFCs.swap(AuxCallsList); - - for(DSGraph::FunctionListTy::iterator I = TempFCs.begin(), E = TempFCs.end(); - I != E; ++I) { - SDEBUG(Graph->AssertGraphOK(); Graph->getGlobalsGraph()->AssertGraphOK()); - - DSCallSite &CS = *I; - - // Fast path for noop calls. Note that we don't care about merging globals - // in the callee with nodes in the caller here. - if (!CS.isIndirectCall() && CS.getRetVal().isNull() - && CS.getNumPtrArgs() == 0 && !CS.isVarArg()) { - continue; - } - - // If this callsite is unresolvable, get rid of it now. - if (CS.isUnresolvable()) { - continue; - } - - // Find all callees for this callsite, according to the DSGraph! - // Do *not* use the callgraph, because we're updating that as we go! - FuncSet CalledFuncs; - getAllCallees(CS,CalledFuncs); - - if (CalledFuncs.empty()) { - ++NumEmptyCalls; - if (CS.isIndirectCall()) - ++NumIndUnresolved; - // Remember that we could not resolve this yet! - DSGraph::FunctionListTy::iterator S = I++; - AuxCallsList.splice(AuxCallsList.end(), TempFCs, S); - continue; - } - // If we get to this point, we know the callees, and can inline. - // This means, that either it is a direct call site. Or if it is - // an indirect call site, its calleeNode is complete, and we can - // resolve this particular call site. - assert((CS.isDirectCall() || CS.getCalleeNode()->isCompleteNode()) - && "Resolving an indirect incomplete call site"); - - if (CS.isIndirectCall()) { - ++NumIndResolved; - } - - DSGraph *GI; - - for (FuncSet::iterator I = CalledFuncs.begin(), E = CalledFuncs.end(); - I != E; ++I) { - const Function *Callee = *I; - // Get the data structure graph for the called function. - - GI = getDSGraph(*Callee); // Graph to inline - SDEBUG(GI->AssertGraphOK(); GI->getGlobalsGraph()->AssertGraphOK()); - SDEBUG(errs() << " Inlining graph for " << Callee->getName() - << "[" << GI->getGraphSize() << "+" - << GI->getAuxFunctionCalls().size() << "] into '" - << Graph->getFunctionNames() << "' [" << Graph->getGraphSize() <<"+" - << Graph->getAuxFunctionCalls().size() << "]\n"); - - // - // Merge in the DSGraph of the called function. - // - // TODO: - // Why are the strip alloca bit and don't clone call nodes bit set? - // - // I believe the answer is on page 6 of the PLDI paper on DSA. The - // idea is that stack objects are invalid if they escape. - // - Graph->mergeInGraph(CS, *Callee, *GI, - DSGraph::StripAllocaBit|DSGraph::DontCloneCallNodes); - ++NumInlines; - SDEBUG(Graph->AssertGraphOK();); - } - } - TempFCs.clear(); - - // Recompute the Incomplete markers - Graph->maskIncompleteMarkers(); - Graph->markIncompleteNodes(DSGraph::MarkFormalArgs); - Graph->computeExternalFlags(DSGraph::DontMarkFormalsExternal); - Graph->computeIntPtrFlags(); - - // - // Update the callgraph with the new information that we have gleaned. - // NOTE : This must be called before removeDeadNodes, so that no - // information is lost due to deletion of DSCallNodes. - Graph->buildCallGraph(callgraph, GlobalFunctionList, filterCallees); - - // Delete dead nodes. Treat globals that are unreachable but that can - // reach live nodes as live. - Graph->removeDeadNodes(DSGraph::KeepUnreachableGlobals); - - cloneIntoGlobals(Graph, DSGraph::DontCloneCallNodes | - DSGraph::DontCloneAuxCallNodes | - DSGraph::StripAllocaBit); - //Graph->writeGraphToFile(cerr, "bu_" + F.getName()); -} diff --git a/lib/DSA/CallTargets.cpp b/lib/DSA/CallTargets.cpp deleted file mode 100644 index 4fa9fc477..000000000 --- a/lib/DSA/CallTargets.cpp +++ /dev/null @@ -1,173 +0,0 @@ -//=- lib/Analysis/IPA/CallTargets.cpp - Resolve Call Targets --*- C++ -*-=====// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This pass uses DSA to map targets of all calls, and reports on if it -// thinks it knows all targets of a given call. -// -// Loop over all callsites, and lookup the DSNode for that site. Pull the -// Functions from the node as callees. -// This is essentially a utility pass to simplify later passes that only depend -// on call sites and callees to operate (such as a devirtualizer). -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "call-targets" -#include "llvm/IR/Module.h" -#include "llvm/IR/Instructions.h" -#include "dsa/DataStructure.h" -#include "dsa/DSGraph.h" -#include "dsa/CallTargets.h" -#include "llvm/ADT/Statistic.h" -#include "smack/Debug.h" -#include "llvm/Support/FormattedStream.h" -#include "llvm/IR/Constants.h" -#include -using namespace llvm; - -RegisterPass > X("calltarget-eqtd","Find Call Targets (uses DSA-EQTD)"); -RegisterPass > Y("calltarget-td","Find Call Targets (uses DSA-TD)"); -namespace { - STATISTIC (DirCall, "Number of direct calls"); - STATISTIC (IndCall, "Number of indirect calls"); - STATISTIC (CompleteInd, "Number of complete indirect calls"); - STATISTIC (CompleteEmpty, "Number of complete empty calls"); - -} - -namespace dsa { - -template -char CallTargetFinder::ID = 0; - - template -void CallTargetFinder::findIndTargets(Module &M) -{ - dsa* T = &getAnalysis(); - const DSCallGraph & callgraph = T->getCallGraph(); - DSGraph* G = T->getGlobalsGraph(); - DSGraph::ScalarMapTy& SM = G->getScalarMap(); - for (Function &F : M) - if (!F.isDeclaration()) - for (BasicBlock &B : F) - for (Instruction &I : B) - if (isa(&I) || isa(&I)) { - CallSite cs(&I); - AllSites.push_back(cs); - Function* CF = cs.getCalledFunction(); - - if (isa(cs.getCalledValue())) continue; - if (isa(cs.getCalledValue())) continue; - - // - // If the called function is casted from one function type to - // another, peer into the cast instruction and pull out the actual - // function being called. - // - if (!CF) - CF = dyn_cast(cs.getCalledValue()->stripPointerCasts()); - - if (!CF) { - Value * calledValue = cs.getCalledValue()->stripPointerCasts(); - if (isa(calledValue)) { - ++DirCall; - CompleteSites.insert(cs); - } else { - IndCall++; - - DSCallGraph::callee_iterator csi = callgraph.callee_begin(cs), - cse = callgraph.callee_end(cs); - while(csi != cse) { - const Function *F = *csi; - DSCallGraph::scc_iterator sccii = callgraph.scc_begin(F), - sccee = callgraph.scc_end(F); - for(;sccii != sccee; ++sccii) { - DSGraph::ScalarMapTy::const_iterator I = SM.find(SM.getLeaderForGlobal(*sccii)); - if (I != SM.end()) { - IndMap[cs].push_back (*sccii); - } - } - ++csi; - } - const Function *F1 = (cs).getInstruction()->getParent()->getParent(); - F1 = callgraph.sccLeader(&*F1); - - DSCallGraph::scc_iterator sccii = callgraph.scc_begin(F1), - sccee = callgraph.scc_end(F1); - for(;sccii != sccee; ++sccii) { - DSGraph::ScalarMapTy::const_iterator I = SM.find(SM.getLeaderForGlobal(*sccii)); - if (I != SM.end()) { - IndMap[cs].push_back (*sccii); - } - } - - DSNode* N = T->getDSGraph(*cs.getCaller()) - ->getNodeForValue(cs.getCalledValue()).getNode(); - assert (N && "CallTarget: findIndTargets: No DSNode!"); - - if (!N->isIncompleteNode() && !N->isExternalNode() && IndMap[cs].size()) { - CompleteSites.insert(cs); - ++CompleteInd; - } - if (!N->isIncompleteNode() && !N->isExternalNode() && !IndMap[cs].size()) { - ++CompleteEmpty; - SDEBUG(errs() << "Call site empty: '" - << cs.getInstruction()->getName() - << "' In '" - << cs.getInstruction()->getParent()->getParent()->getName() - << "'\n"); - } - } - } else { - ++DirCall; - IndMap[cs].push_back(CF); - CompleteSites.insert(cs); - } - } -} - - template -void CallTargetFinder::print(llvm::raw_ostream &O, const Module *M) const -{ - O << "[* = incomplete] CS: func list\n"; - for (std::map >::const_iterator ii = - IndMap.begin(), - ee = IndMap.end(); ii != ee; ++ii) { - - if (ii->first.getCalledFunction()) //only print indirect - continue; - if(isa(ii->first.getCalledValue()->stripPointerCasts())) - continue; - if (!isComplete(ii->first)) { - O << "* "; - CallSite cs = ii->first; - cs.getInstruction()->print(O, true); - O << cs.getInstruction()->getParent()->getParent()->getName().str() << " " - << cs.getInstruction()->getName().str() << " "; - } - O << ii->first.getInstruction() << ":"; - for (std::vector::const_iterator i = ii->second.begin(), - e = ii->second.end(); i != e; ++i) { - O << " " << (*i)->getName().str(); - } - O << "\n"; - } -} - - template -bool CallTargetFinder::runOnModule(Module &M) { - findIndTargets(M); - return false; -} - - template -void CallTargetFinder::getAnalysisUsage(AnalysisUsage &AU) const { - AU.setPreservesAll(); - AU.addRequired(); -} -} diff --git a/lib/DSA/CompleteBottomUp.cpp b/lib/DSA/CompleteBottomUp.cpp deleted file mode 100644 index 17adf0cfb..000000000 --- a/lib/DSA/CompleteBottomUp.cpp +++ /dev/null @@ -1,220 +0,0 @@ -//===- CompleteBottomUp.cpp - Complete Bottom-Up Data Structure Graphs ----===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This is the exact same as the bottom-up graphs, but we use take a completed -// call graph and inline all indirect callees into their callers graphs, making -// the result more useful for things like pool allocation. -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "dsa-cbu" -#include "dsa/DataStructure.h" -#include "dsa/DSGraph.h" -#include "llvm/IR/Module.h" -#include "llvm/ADT/Statistic.h" -#include "smack/Debug.h" -#include "llvm/Support/FormattedStream.h" -using namespace llvm; - -namespace { - RegisterPass - X("dsa-cbu", "'Complete' Bottom-up Data Structure Analysis"); -} - -char CompleteBUDataStructures::ID; - -// -// Method: runOnModule() -// -// Description: -// Entry point for this pass. Calculate the bottom up data structure graphs -// for each function in the program. -// -// Return value: -// true - The module was modified. -// false - The module was not modified. -// -bool -CompleteBUDataStructures::runOnModule (Module &M) { - init(&getAnalysis(), true, true, false, true); - - - // - // Make sure we have a DSGraph for all declared functions in the Module. - // formGlobalECs assumes that DSInfo is populated with a list of - // DSgraphs for all the functions. - - for (Function &F : M) { - if (!(F.isDeclaration())){ - getOrCreateGraph(&F); - } - } - - buildIndirectFunctionSets(); - formGlobalECs(); - for (Function &F : M) { - if (!(F.isDeclaration())) { - if (DSGraph * Graph = getOrCreateGraph(&F)) { - cloneIntoGlobals(Graph, DSGraph::DontCloneCallNodes | - DSGraph::DontCloneAuxCallNodes | - DSGraph::StripAllocaBit); - } - } - } - - for (Function &F : M) { - if (!(F.isDeclaration())) { - if (DSGraph * Graph = getOrCreateGraph(&F)) { - cloneGlobalsInto(Graph, DSGraph::DontCloneCallNodes | - DSGraph::DontCloneAuxCallNodes); - } - } - } - - // - // Do bottom-up propagation. - // - bool modified = runOnModuleInternal(M); - - callgraph.buildSCCs(); - callgraph.buildRoots(); - - return modified; -} - -// -// Method: buildIndirectFunctionSets() -// -// Description: -// For every indirect call site, ensure that every function target is -// associated with a single DSNode. -// -void -CompleteBUDataStructures::buildIndirectFunctionSets (void) { - // - // Loop over all of the indirect calls in the program. If a call site can - // call multiple different functions, we need to unify all of the callees into - // the same equivalence class. - // - DSGraph* G = getGlobalsGraph(); - DSGraph::ScalarMapTy& SM = G->getScalarMap(); - - // Merge nodes in the global graph for these functions - for (DSCallGraph::callee_key_iterator ii = callgraph.key_begin(), - ee = callgraph.key_end(); ii != ee; ++ii) { - -#ifndef NDEBUG - // --Verify that for every callee of an indirect function call - // we have an entry in the GlobalsGraph - - // If any function in an SCC is a callee of an indirect function - // call, the DScallgraph contains the leader of the SCC as the - // callee of the indirect call. - // The leasder of the SCC may not have an entry in the Globals - // Graph, but at least one of the functions in the SCC - // should have an entry in the GlobalsGraph - - Value *CalledValue = (*ii).getCalledValue()->stripPointerCasts(); - - bool isIndirect = (!isa(CalledValue)); - if (isIndirect) { - DSCallGraph::callee_iterator csii = callgraph.callee_begin(*ii), - csee = callgraph.callee_end(*ii); - - for (; csii != csee; ++csii) { - // - // Declarations don't have to have entries. Functions may be - // equivalence classed already, so we have to check their equivalence - // class leader instead of the global itself. - // - const Function * F = *csii; - if (!(F->isDeclaration())){ - DSCallGraph::scc_iterator sccii = callgraph.scc_begin(F), - sccee = callgraph.scc_end(F); - bool flag = false; - for(; sccii != sccee; ++sccii) { - flag |= SM.count(SM.getLeaderForGlobal(*sccii)); - } - assert (flag && - "Indirect function callee not in globals?"); - } - } - } -#endif - - // - // Note: The code above and below is dealing with the fact that the targets - // of *direct* function calls do not show up in the Scalar Map of the - // globals graph. The above assertion simply verifies that all targets of - // indirect function calls show up in the Scalar Map of the globals graph, - // and then the code below can just check the scalar map to see if the - // call needs to be processed because it is an indirect function call. - // - // I suspect that this code is designed this way more for historical - // reasons than for simplicity. We should simplify the code is possible at - // a future date. - // - // FIXME: Given the above is a valid assertion, we could probably replace - // this code with something that *assumes* we have entries in the Scalar - // Map. However, because - // I'm not convinced that we can just *skip* direct calls in this function - // this code is careful to handle callees not existing in the globals graph - // In other words what we have here should be correct, but might be overkill - // that we can trim down later as needed. - - DSNodeHandle calleesNH; - - // When we build SCCs we remove any calls that are to functions in the - // same SCC. Hence, for every indirect call site we must assume that it - // might call functions in its function's SCC that are address taken. - const Function *F1 = (*ii).getInstruction()->getParent()->getParent(); - F1 = callgraph.sccLeader(&*F1); - - DSCallGraph::scc_iterator sccii = callgraph.scc_begin(F1), - sccee = callgraph.scc_end(F1); - for(;sccii != sccee; ++sccii) { - DSGraph::ScalarMapTy::const_iterator I = SM.find(SM.getLeaderForGlobal(*sccii)); - if (I != SM.end()) { - calleesNH.mergeWith(I->second); - } - } - - DSCallGraph::callee_iterator csi = callgraph.callee_begin(*ii), - cse = callgraph.callee_end(*ii); - - - // We get all the callees, and then for all functions in that SCC, find the - // ones that have entries in the GlobalsGraph. - - // We merge all the functions in the SCC that have entries, and then move - // on to the next callee and repeat. - - // If an SCC has functions that have entries in the GlobalsGraph, and are - // targets of an indirect function call site, they will be merged. - - // However, if an SCC has functions, that have entries in the GlobalsGraph, - // bur are not the targets of an indirect function call site, they will not - // be merged by CBU. - - // This NH starts off empty, but ends up merging them all together - - while(csi != cse) { - const Function *F = *csi; - DSCallGraph::scc_iterator sccii = callgraph.scc_begin(F), - sccee = callgraph.scc_end(F); - for(;sccii != sccee; ++sccii) { - DSGraph::ScalarMapTy::const_iterator I = SM.find(SM.getLeaderForGlobal(*sccii)); - if (I != SM.end()) { - calleesNH.mergeWith(I->second); - } - } - ++csi; - } - } -} diff --git a/lib/DSA/DSCallGraph.cpp b/lib/DSA/DSCallGraph.cpp deleted file mode 100644 index 0db83228c..000000000 --- a/lib/DSA/DSCallGraph.cpp +++ /dev/null @@ -1,273 +0,0 @@ -//===- DSCallGraph.cpp - Implement the Call Graph Support class -----------===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements the Call Graph -// -//===----------------------------------------------------------------------===// - -#include "dsa/DSCallGraph.h" -#include "dsa/DataStructure.h" -#include "dsa/DSGraph.h" - -#include "llvm/IR/Function.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/Support/FormattedStream.h" -#include "llvm/Support/CommandLine.h" - -#include - -using namespace llvm; - -static bool _hasPointers(const llvm::FunctionType* T) { - if (T->isVarArg()) return true; - if (T->getReturnType()->isPointerTy()) return true; - for (unsigned x = 0; x < T->getNumParams(); ++x) - if (T->getParamType(x)->isPointerTy()) - return true; - return false; -} - -bool DSCallGraph::hasPointers(const llvm::Function* F) { - return _hasPointers(F->getFunctionType()); -} - -bool DSCallGraph::hasPointers(llvm::CallSite& CS) { - if (CS.getCalledFunction()) - return hasPointers(CS.getCalledFunction()); - - const llvm::Value* Callee = CS.getCalledValue(); - const llvm::Type* T = Callee->getType(); - if (const llvm::PointerType* PT = llvm::dyn_cast(T)) - T = PT->getElementType(); - return _hasPointers(llvm::cast(T)); -} - -unsigned DSCallGraph::tarjan_rec(const llvm::Function* F, TFStack& Stack, - unsigned &NextID, TFMap& ValMap) { - assert(!ValMap.count(F) && "Shouldn't revisit functions!"); - unsigned Min = NextID++, MyID = Min; - ValMap[F] = Min; - Stack.push_back(F); - - // The edges out of the current node are the call site targets... - for (flat_iterator ii = flat_callee_begin(F), - ee = flat_callee_end(F); ii != ee; ++ii) { - unsigned M = Min; - // Have we visited the destination function yet? - TFMap::iterator It = ValMap.find(*ii); - if (It == ValMap.end()) // No, visit it now. - M = tarjan_rec(*ii, Stack, NextID, ValMap); - else if (std::find(Stack.begin(), Stack.end(), *ii) != Stack.end()) - M = It->second; - if (M < Min) Min = M; - } - - assert(ValMap[F] == MyID && "SCC construction assumption wrong!"); - if (Min != MyID) - return Min; // This is part of a larger SCC! - - // If this is a new SCC, process it now. - if (F == Stack.back()) { - // single node case - Stack.pop_back(); - SCCs.insert(F); - } else { - // Take care that the leader is not an external function - std::vector microSCC; - const llvm::Function* NF = 0; - const llvm::Function* Leader = 0; - do { - NF = Stack.back(); - Stack.pop_back(); - microSCC.push_back(NF); - if (!Leader && !NF->isDeclaration()) Leader = NF; - } while (NF != F); - //Leader is not an extern function - //No multi-function SCC can not have a defined function, as all externs - //are treated as having no callees - assert(Leader && "No Leader?"); - SCCs.insert(Leader); - Leader = SCCs.getLeaderValue(Leader); - assert(!Leader->isDeclaration() && "extern leader"); - for (std::vector::iterator ii = microSCC.begin(), - ee = microSCC.end(); ii != ee; ++ii) { - SCCs.insert(*ii); - const llvm::Function* Temp = SCCs.getLeaderValue(*ii); - //Order Matters - SCCs.unionSets(Leader, Temp); - assert (SCCs.getLeaderValue(Leader) == Leader && "SCC construction wrong"); - assert (SCCs.getLeaderValue(Temp) == Leader && "SCC construction wrong"); - } - } - - return MyID; -} - -void DSCallGraph::buildSCCs() { - TFStack Stack; - TFMap ValMap; - unsigned NextID = 1; - - for (flat_key_iterator ii = flat_key_begin(), ee = flat_key_end(); - ii != ee; ++ii) - if (!ValMap.count(*ii)) - tarjan_rec(*ii, Stack, NextID, ValMap); - - removeECFunctions(); -} - -static void removeECs(DSCallGraph::FuncSet& F, - llvm::EquivalenceClasses& ECs) { - DSCallGraph::FuncSet result; - for (DSCallGraph::FuncSet::const_iterator ii = F.begin(), ee = F.end(); - ii != ee; ++ii) - result.insert(ECs.getLeaderValue(*ii)); - - F.swap(result); -} - -void DSCallGraph::removeECFunctions() { - //First the callers - for (SimpleCalleesTy::iterator ii = SimpleCallees.begin(), - ee = SimpleCallees.end(); ii != ee;) { - const llvm::Function* Leader = SCCs.getLeaderValue(ii->first); - if (Leader == ii->first) { - // This is the leader, leave it alone - ++ii; - } else { - //This is not the leader, merge into the leader - SimpleCallees[Leader].insert(ii->second.begin(), ii->second.end()); - SimpleCalleesTy::iterator tmpii = ii; - ++ii; - SimpleCallees.erase(tmpii); - } - } - // then the callees - for (SimpleCalleesTy::iterator ii = SimpleCallees.begin(), - ee = SimpleCallees.end(); ii != ee; ++ii) { - removeECs(ii->second, SCCs); - //and apparent self loops inside an SCC - ii->second.erase(ii->first); - } - for (ActualCalleesTy::iterator ii = ActualCallees.begin(), - ee = ActualCallees.end(); ii != ee; ++ii) - removeECs(ii->second, SCCs); -} - -void DSCallGraph::buildRoots() { - FuncSet knownCallees; - FuncSet knownCallers; - for (SimpleCalleesTy::iterator ii = SimpleCallees.begin(), - ee = SimpleCallees.end(); ii != ee; ++ii) { - knownCallees.insert(ii->second.begin(), ii->second.end()); - knownCallers.insert(ii->first); - } - knownRoots.clear(); - std::set_difference(knownCallers.begin(), knownCallers.end(), - knownCallees.begin(), knownCallees.end(), - std::inserter(knownRoots, knownRoots.begin())); -} - -void DSCallGraph::buildIncompleteCalleeSet(svset callees) { - IncompleteCalleeSet.insert(callees.begin(), callees.end()); -} - -template -void printNameOrPtr(T& Out, const llvm::Function* F) { - if (F->hasName()) - Out << F->getName(); - else - Out << F; -} - -void DSCallGraph::dump() const { - //function map - - //CallGraph map - for (SimpleCalleesTy::const_iterator ii = SimpleCallees.begin(), - ee = SimpleCallees.end(); ii != ee; ++ii) { - llvm::errs() << "CallGraph["; - printNameOrPtr(llvm::errs(), ii->first); - llvm::errs() << "]"; - for (FuncSet::const_iterator i = ii->second.begin(), - e = ii->second.end(); i != e; ++i) { - llvm::errs() << " "; - printNameOrPtr(llvm::errs(), *i); - } - llvm::errs() << "\n"; - } - - //Functions we know about that aren't called - llvm::errs() << "Roots:"; - for (FuncSet::const_iterator ii = knownRoots.begin(), ee = knownRoots.end(); - ii != ee; ++ii) { - llvm::errs() << " "; - printNameOrPtr(llvm::errs(), *ii); - } - llvm::errs() << "\n"; -} - -// -// Method: insert() -// -// Description: -// Insert a new entry into the call graph. This entry says that the specified -// call site calls the specified function. -// -// Inputs: -// CS - The call site which calls the specified function. -// F - The function which is called. This is permitted to be NULL. It is -// possible to have call sites that don't have any targets, and sometimes -// users just want to ensure that a call site has an entry within the -// call graph. -// -void -DSCallGraph::insert(llvm::CallSite CS, const llvm::Function* F) { - // - // Find the function to which the call site belongs. - // - const llvm::Function * Parent = CS.getInstruction()->getParent()->getParent(); - - // - // Determine the SCC leaders for both the calling function and the called - // function. If they don't belong to an SCC, add them as leaders. - // - SCCs.insert (Parent); - SCCs.insert (F); - const llvm::Function * ParentLeader = SCCs.getLeaderValue (Parent); - const llvm::Function * FLeader = SCCs.getLeaderValue (F); - - // - // Create an empty set for the callee; hence, all called functions get to be - // in the call graph also. This simplifies SCC formation. - // - SimpleCallees[ParentLeader]; - if (F) { - ActualCallees[CS].insert(FLeader); - SimpleCallees[ParentLeader].insert(FLeader); - } -} - -void DSCallGraph::insureEntry(const llvm::Function* F) { - SimpleCallees[F]; -} - -void DSCallGraph::addFullFunctionSet(llvm::CallSite CS, - svset &Set) const { - DSCallGraph::callee_iterator csi = callee_begin(CS), - cse = callee_end(CS); - while(csi != cse) { - const Function *F = *csi; - Set.insert(scc_begin(F), scc_end(F)); - ++csi; - } - const Function *F1 = CS.getInstruction()->getParent()->getParent(); - F1 = sccLeader(&*F1); - Set.insert(scc_begin(F1), scc_end(F1)); -} diff --git a/lib/DSA/DSGraph.cpp b/lib/DSA/DSGraph.cpp deleted file mode 100644 index f1d34fd25..000000000 --- a/lib/DSA/DSGraph.cpp +++ /dev/null @@ -1,1687 +0,0 @@ -//===- DataStructure.cpp - Implement the core data structure analysis -----===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements the core data structure functionality. -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "DSGraph" -#include "dsa/DSGraphTraits.h" -#include "dsa/DataStructure.h" -#include "dsa/DSGraph.h" -#include "dsa/DSSupport.h" -#include "dsa/DSNode.h" -#include "dsa/stl_util.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/GlobalVariable.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/DataLayout.h" -#include "llvm/Support/CommandLine.h" -#include "smack/Debug.h" -#include "llvm/ADT/DepthFirstIterator.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SCCIterator.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/Support/Timer.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/IR/Type.h" -#include "llvm/IR/GlobalAlias.h" - -#include -#include -using namespace llvm; - -#define COLLAPSE_ARRAYS_AGGRESSIVELY 0 -namespace { - STATISTIC (NumCallNodesMerged , "Number of call nodes merged"); - STATISTIC (NumDNE , "Number of nodes removed by reachability"); - STATISTIC (NumTrivialDNE , "Number of nodes trivially removed"); - STATISTIC (NumTrivialGlobalDNE , "Number of globals trivially removed"); - STATISTIC (NumFiltered , "Number of calls filtered"); - - static cl::opt noDSACallConv("dsa-no-filter-callcc", - cl::desc("Don't filter call sites based on calling convention."), - cl::Hidden, - cl::init(false)); - static cl::opt noDSACallNumArgs("dsa-no-filter-numargs", - cl::desc("Don't filter call sites based on number of arguments."), - cl::Hidden, - cl::init(false)); - static cl::opt noDSACallVA("dsa-no-filter-vararg", - cl::desc("Don't filter call sites based on vararg presense"), - cl::Hidden, - cl::init(true)); - static cl::opt noDSACallFP("dsa-no-filter-intfp", - cl::desc("Don't filter call sites based on implicit integer to FP conversion"), - cl::Hidden, - cl::init(false)); -} - -extern cl::opt TypeInferenceOptimize; - -// Determines if the DSGraph 'should' have a node for a given value. -static bool shouldHaveNodeForValue(const Value *V) { - // Peer through casts - V = V->stripPointerCasts(); - - // Only pointers get nodes - if (!isa(V->getType())) return false; - - // Undef values, even ones of pointer type, don't get nodes. - if (isa(V)) return false; - - if (isa(V)) - return false; - - // Use the Aliasee of GlobalAliases - // FIXME: This check might not be required, it's here because - // something similar is done in the Local pass. - if (const GlobalAlias *GA = dyn_cast(V)) - return shouldHaveNodeForValue(GA->getAliasee()); - - return true; -} - -/// getFunctionNames - Return a space separated list of the name of the -/// functions in this graph (if any) -std::string DSGraph::getFunctionNames() const { - switch (getReturnNodes().size()) { - case 0: return "Globals graph"; - case 1: return retnodes_begin()->first->getName(); - default: - std::string Return; - for (DSGraph::retnodes_iterator I = retnodes_begin(); - I != retnodes_end(); ++I) - Return += I->first->getName().str() + " "; - Return.erase(Return.end()-1, Return.end()); // Remove last space character - return Return; - } -} - - -DSGraph::DSGraph(DSGraph* G, EquivalenceClasses &ECs, - SuperSet& tss, - unsigned CloneFlags) - : GlobalsGraph(0), ScalarMap(ECs), TD(G->TD), TypeSS(tss) { - UseAuxCalls = false; - cloneInto(G, CloneFlags); -} - -DSGraph::~DSGraph() { - FunctionCalls.clear(); - AuxFunctionCalls.clear(); - ScalarMap.clear(); - ReturnNodes.clear(); - VANodes.clear(); - - // Drop all intra-node references, so that assertions don't fail... - for (node_iterator NI = node_begin(), E = node_end(); NI != E; ++NI) - NI->dropAllReferences(); - - // Free all of the nodes. - Nodes.clear(); -} - -// dump - Allow inspection of graph in a debugger. -void DSGraph::dump() const { print(errs()); } - -void DSGraph::removeFunctionCalls(Function& F) { - FunctionListTy::iterator Erase = FunctionCalls.end(); - for (FunctionListTy::iterator I = FunctionCalls.begin(); - I != Erase; ) { - if (I->isDirectCall() && I->getCalleeFunc() == &F) - std::swap(*I, *--Erase); - else - ++I; - } - FunctionCalls.erase(Erase, FunctionCalls.end()); - - Erase = AuxFunctionCalls.end(); - for (FunctionListTy::iterator I = AuxFunctionCalls.begin(); - I != Erase; ) { - if (I->isDirectCall() && I->getCalleeFunc() == &F) - std::swap(*I, *--Erase); - else - ++I; - } - AuxFunctionCalls.erase(Erase, AuxFunctionCalls.end()); -} - -/// addObjectToGraph - This method can be used to add global, stack, and heap -/// objects to the graph. This can be used when updating DSGraphs due to the -/// introduction of new temporary objects. The new object is not pointed to -/// and does not point to any other objects in the graph. -DSNode *DSGraph::addObjectToGraph(Value *Ptr, bool UseDeclaredType) { - assert(isa(Ptr->getType()) && "Ptr is not a pointer!"); - DSNode *N = new DSNode(this); - assert(ScalarMap[Ptr].isNull() && "Object already in this graph!"); - ScalarMap[Ptr] = N; - - if (GlobalValue *GV = dyn_cast(Ptr)) { - N->addGlobal(GV); -// } else if (isa(Ptr)) { -// N->setHeapMarker(); - } else if (isa(Ptr)) { - N->setAllocaMarker(); - } else { - llvm_unreachable("Illegal memory object input!"); - } - return N; -} - - -/// cloneInto - Clone the specified DSGraph into the current graph. The -/// translated ScalarMap for the old function is filled into the ScalarMap -/// for the graph, the translated ReturnNodes map is returned into -/// ReturnNodes, and the translated VANodes map is return into VANodes. -/// -/// The CloneFlags member controls various aspects of the cloning process. -/// -void DSGraph::cloneInto( DSGraph* G, unsigned CloneFlags) { - assert(G != this && "Cannot clone graph into itself!"); - - NodeMapTy OldNodeMap; - - // Remove alloca or mod/ref bits as specified... - unsigned BitsToClear = ((CloneFlags & StripAllocaBit)? DSNode::AllocaNode : 0) - | ((CloneFlags & StripModRefBits)? (DSNode::ModifiedNode | DSNode::ReadNode) : 0) - | ((CloneFlags & StripIncompleteBit)? DSNode::IncompleteNode : 0); - BitsToClear |= DSNode::DeadNode; // Clear dead flag... - - for (node_const_iterator I = G->node_begin(), E = G->node_end(); I != E; ++I) { - assert(!I->isForwarding() && - "Forward nodes shouldn't be in node list!"); - DSNode *New = new DSNode(*I, this); - New->maskNodeTypes(~BitsToClear); - OldNodeMap[&*I] = New; - } - - // Rewrite the links in the new nodes to point into the current graph now. - // Note that we don't loop over the node's list to do this. The problem is - // that remaping links can cause recursive merging to happen, which means - // that node_iterator's can get easily invalidated! Because of this, we - // loop over the OldNodeMap, which contains all of the new nodes as the - // .second element of the map elements. Also note that if we remap a node - // more than once, we won't break anything. - for (NodeMapTy::iterator I = OldNodeMap.begin(), E = OldNodeMap.end(); - I != E; ++I) - I->second.getNode()->remapLinks(OldNodeMap); - - // Copy the scalar map... merging all of the global nodes... - for (DSScalarMap::const_iterator I = G->ScalarMap.begin(), - E = G->ScalarMap.end(); I != E; ++I) { - DSNodeHandle &MappedNode = OldNodeMap[I->second.getNode()]; - DSNodeHandle &H = ScalarMap.getRawEntryRef(I->first); - DSNode *MappedNodeN = MappedNode.getNode(); - H.mergeWith(DSNodeHandle(MappedNodeN, - I->second.getOffset()+MappedNode.getOffset())); - } - - if (!(CloneFlags & DontCloneCallNodes)) { - // Copy the function calls list. - for (fc_iterator I = G->fc_begin(), E = G->fc_end(); I != E; ++I) - FunctionCalls.push_back(DSCallSite(*I, OldNodeMap)); - } - - if (!(CloneFlags & DontCloneAuxCallNodes)) { - // Copy the auxiliary function calls list. - for (afc_iterator I = G->afc_begin(), E = G->afc_end(); I != E; ++I) - AuxFunctionCalls.push_back(DSCallSite(*I, OldNodeMap)); - } - - // Map the return node pointers over... - for (retnodes_iterator I = G->retnodes_begin(), - E = G->retnodes_end(); I != E; ++I) { - const DSNodeHandle &Ret = I->second; - DSNodeHandle &MappedRet = OldNodeMap[Ret.getNode()]; - DSNode *MappedRetN = MappedRet.getNode(); - ReturnNodes.insert(std::make_pair(I->first, - DSNodeHandle(MappedRetN, - MappedRet.getOffset()+Ret.getOffset()))); - } - - // Map the VA node pointers over... - for (vanodes_iterator I = G->vanodes_begin(), - E = G->vanodes_end(); I != E; ++I) { - const DSNodeHandle &VarArg = I->second; - DSNodeHandle &MappedVarArg = OldNodeMap[VarArg.getNode()]; - DSNode *MappedVarArgN = MappedVarArg.getNode(); - VANodes.insert(std::make_pair(I->first, - DSNodeHandle(MappedVarArgN, - MappedVarArg.getOffset()+VarArg.getOffset()))); - } -} - -/// spliceFrom - Logically perform the operation of cloning the RHS graph into -/// this graph, then clearing the RHS graph. Instead of performing this as -/// two seperate operations, do it as a single, much faster, one. -/// -void DSGraph::spliceFrom(DSGraph* RHS) { - assert(this != RHS && "Splicing self"); - // Change all of the nodes in RHS to think we are their parent. - for (NodeListTy::iterator I = RHS->Nodes.begin(), E = RHS->Nodes.end(); - I != E; ++I) - I->setParentGraph(this); - // Take all of the nodes. - splice(Nodes, RHS->Nodes); - - // Take all of the calls. - splice(FunctionCalls, RHS->FunctionCalls); - splice(AuxFunctionCalls, RHS->AuxFunctionCalls); - - // Take all of the return nodes. - splice(ReturnNodes, RHS->ReturnNodes); - - // Same for the VA nodes - splice(VANodes, RHS->VANodes); - - // Merge the scalar map in. - ScalarMap.spliceFrom(RHS->ScalarMap); -} - -/// getFunctionArgumentsForCall - Given a function that is currently in this -/// graph, return the DSNodeHandles that correspond to the pointer-compatible -/// function arguments. The vector is filled in with the return value (or -/// null if it is not pointer compatible), a vararg node (null if not -/// applicable) followed by all of the pointer-compatible arguments. -void DSGraph::getFunctionArgumentsForCall(const Function *F, - std::vector &Args) const { - Args.push_back(getReturnNodeFor(*F)); - Args.push_back(getVANodeFor(*F)); - for (const auto &Arg : F->args()) - if (isa(Arg.getType())) { - Args.push_back(getNodeForValue(&Arg)); - assert(!Args.back().isNull() && "Pointer argument w/o scalarmap entry!?"); - } -} - -namespace { - // HackedGraphSCCFinder - This is used to find nodes that have a path from the - // node to a node cloned by the ReachabilityCloner object contained. To be - // extra obnoxious it ignores edges from nodes that are globals, and truncates - // search at RC marked nodes. This is designed as an object so that - // intermediate results can be memoized across invocations of - // PathExistsToClonedNode. - struct HackedGraphSCCFinder { - ReachabilityCloner &RC; - unsigned CurNodeId; - std::vector SCCStack; - std::map > NodeInfo; - - HackedGraphSCCFinder(ReachabilityCloner &rc) : RC(rc), CurNodeId(1) { - // Remove null pointer as a special case. - NodeInfo[0] = std::make_pair(0, false); - } - - std::pair &VisitForSCCs(const DSNode *N); - - bool PathExistsToClonedNode(const DSNode *N) { - return VisitForSCCs(N).second; - } - - bool PathExistsToClonedNode(const DSCallSite &CS) { - if (PathExistsToClonedNode(CS.getRetVal().getNode())) - return true; - if (CS.isIndirectCall() && PathExistsToClonedNode(CS.getCalleeNode())) - return true; - for (unsigned i = 0, e = CS.getNumPtrArgs(); i != e; ++i) - if (PathExistsToClonedNode(CS.getPtrArg(i).getNode())) - return true; - if (PathExistsToClonedNode(CS.getVAVal().getNode())) - return true; - return false; - } - }; -} - -std::pair &HackedGraphSCCFinder:: -VisitForSCCs(const DSNode *N) { - std::map >::iterator - NodeInfoIt = NodeInfo.lower_bound(N); - if (NodeInfoIt != NodeInfo.end() && NodeInfoIt->first == N) - return NodeInfoIt->second; - - unsigned Min = CurNodeId++; - unsigned MyId = Min; - std::pair &ThisNodeInfo = - NodeInfo.insert(NodeInfoIt, - std::make_pair(N, std::make_pair(MyId, false)))->second; - - - // Base case: if this does reach the cloned graph portion... it does. :) - if (RC.hasClonedNode(N)) { - ThisNodeInfo.second = true; - return ThisNodeInfo; - } - // Base case: if we find a global, this doesn't reach the cloned graph - // portion. - if (N->isGlobalNode()) { - ThisNodeInfo.second = false; - return ThisNodeInfo; - } - - SCCStack.push_back(N); - - // Otherwise, check all successors. - bool AnyDirectSuccessorsReachClonedNodes = false; - for (DSNode::const_edge_iterator EI = N->edge_begin(), EE = N->edge_end(); - EI != EE; ++EI) - if (DSNode * Succ = EI->second.getNode()) { - std::pair &SuccInfo = VisitForSCCs(Succ); - if (SuccInfo.first < Min) Min = SuccInfo.first; - AnyDirectSuccessorsReachClonedNodes |= SuccInfo.second; - } - - if (Min != MyId) - return ThisNodeInfo; // Part of a large SCC. Leave self on stack. - - if (SCCStack.back() == N) { // Special case single node SCC. - SCCStack.pop_back(); - ThisNodeInfo.second = AnyDirectSuccessorsReachClonedNodes; - return ThisNodeInfo; - } - - // Find out if any direct successors of any node reach cloned nodes. - if (!AnyDirectSuccessorsReachClonedNodes) - for (unsigned i = SCCStack.size() - 1; SCCStack[i] != N; --i) - for (DSNode::const_edge_iterator EI = N->edge_begin(), EE = N->edge_end(); - EI != EE; ++EI) - if (DSNode * N = EI->second.getNode()) - if (NodeInfo[N].second) { - AnyDirectSuccessorsReachClonedNodes = true; - goto OutOfLoop; - } -OutOfLoop: - // If any successor reaches a cloned node, mark all nodes in this SCC as - // reaching the cloned node. - if (AnyDirectSuccessorsReachClonedNodes) - while (SCCStack.back() != N) { - NodeInfo[SCCStack.back()].second = true; - SCCStack.pop_back(); - } - SCCStack.pop_back(); - ThisNodeInfo.second = AnyDirectSuccessorsReachClonedNodes; - return ThisNodeInfo; -} - -/// mergeInCallFromOtherGraph - This graph merges in the minimal number of -/// nodes from G2 into 'this' graph, merging the bindings specified by the -/// call site (in this graph) with the bindings specified by the vector in G2. -/// The two DSGraphs must be different. -/// -void DSGraph::mergeInGraph(const DSCallSite &CS, - std::vector &Args, - const DSGraph &Graph, unsigned CloneFlags) { - assert((CloneFlags & DontCloneCallNodes) && - "Doesn't support copying of call nodes!"); - - // If this is not a recursive call, clone the graph into this graph... - if (&Graph == this) { - // Merge the return value with the return value of the context. - Args[0].mergeWith(CS.getRetVal()); - - // Merge var-arg node - Args[1].mergeWith(CS.getVAVal()); - - // Resolve all of the function arguments. - for (unsigned i = 0, e = CS.getNumPtrArgs(); i != e; ++i) { - if (i == Args.size()-2) - break; - - // Add the link from the argument scalar to the provided value. - Args[i+2].mergeWith(CS.getPtrArg(i)); - } - return; - } - - // Clone the callee's graph into the current graph, keeping track of where - // scalars in the old graph _used_ to point, and of the new nodes matching - // nodes of the old graph. - ReachabilityCloner RC(this, &Graph, CloneFlags); - - // Map the return node pointer over. - if (!CS.getRetVal().isNull()) - RC.merge(CS.getRetVal(), Args[0]); - - // Map the variable arguments - if (!CS.getVAVal().isNull()) - RC.merge(CS.getVAVal(), Args[1]); - - // Map over all of the arguments. - for (unsigned i = 0, e = CS.getNumPtrArgs(); i != e; ++i) { - if (i == Args.size()-2) - break; - - // Add the link from the argument scalar to the provided value. - RC.merge(CS.getPtrArg(i), Args[i+2]); - } - - // We generally don't want to copy global nodes or aux calls from the callee - // graph to the caller graph. However, we have to copy them if there is a - // path from the node to a node we have already copied which does not go - // through another global. Compute the set of node that can reach globals and - // aux call nodes to copy over, then do it. - std::vector AuxCallToCopy; - std::vector GlobalsToCopy; - - // NodesReachCopiedNodes - Memoize results for efficiency. Contains a - // true/false value for every visited node that reaches a copied node without - // going through a global. - HackedGraphSCCFinder SCCFinder(RC); - - if (!(CloneFlags & DontCloneAuxCallNodes)) - for (afc_const_iterator I = Graph.afc_begin(), E = Graph.afc_end(); I!=E; ++I) - if (!I->isUnresolvable() && SCCFinder.PathExistsToClonedNode(*I)) - AuxCallToCopy.push_back(&*I); - - // Copy aux calls that are needed. - // Copy these before calculating the globals to be copied, as there might be - // globals that reach the nodes cloned due to aux calls. - for (unsigned i = 0, e = AuxCallToCopy.size(); i != e; ++i) - AuxFunctionCalls.push_back(DSCallSite(*AuxCallToCopy[i], RC)); - - const DSScalarMap &GSM = Graph.getScalarMap(); - for (DSScalarMap::global_iterator GI = GSM.global_begin(), - E = GSM.global_end(); GI != E; ++GI) { - DSNode *GlobalNode = Graph.getNodeForValue(*GI).getNode(); - for (DSNode::edge_iterator EI = GlobalNode->edge_begin(), - EE = GlobalNode->edge_end(); EI != EE; ++EI) - if (SCCFinder.PathExistsToClonedNode(EI->second.getNode())) { - GlobalsToCopy.push_back(*GI); - break; - } - } - - // Copy globals that are needed. - for (unsigned i = 0, e = GlobalsToCopy.size(); i != e; ++i) - RC.getClonedNH(Graph.getNodeForValue(GlobalsToCopy[i])); -} - - - -/// mergeInGraph - The method is used for merging graphs together. If the -/// argument graph is not *this, it makes a clone of the specified graph, then -/// merges the nodes specified in the call site with the formal arguments in the -/// graph. -/// -void DSGraph::mergeInGraph(const DSCallSite &CS, const Function &F, - const DSGraph &Graph, unsigned CloneFlags) { - // Set up argument bindings. - std::vector Args; - Graph.getFunctionArgumentsForCall(&F, Args); - - mergeInGraph(CS, Args, Graph, CloneFlags); -} - -/// getCallSiteForArguments - Get the arguments and return value bindings for -/// the specified function in the current graph. -/// -DSCallSite DSGraph::getCallSiteForArguments(const Function &F) const { - std::vector Args; - - for (const auto &Arg : F.args()) - if (isa(Arg.getType())) - Args.push_back(getNodeForValue(&Arg)); - - return DSCallSite(CallSite(), getReturnNodeFor(F), getVANodeFor(F), &F, Args); -} - -/// getDSCallSiteForCallSite - Given an LLVM CallSite object that is live in -/// the context of this graph, return the DSCallSite for it. -DSCallSite DSGraph::getDSCallSiteForCallSite(CallSite CS) const { - DSNodeHandle RetVal, VarArg; - Instruction *I = CS.getInstruction(); - if (shouldHaveNodeForValue(I)) - RetVal = getNodeForValue(I); - - //FIXME: Here we trust the signature of the callsite to determine which arguments - //are var-arg and which are fixed. Apparently we can't assume this, but I'm not sure - //of a better way. For now, this assumption is known limitation. - const FunctionType *CalleeFuncType = DSCallSite::FunctionTypeOfCallSite(CS); - int NumFixedArgs = CalleeFuncType->getNumParams(); - - // Sanity check--this really, really shouldn't happen - if (!CalleeFuncType->isVarArg()) - assert(CS.arg_size() == static_cast(NumFixedArgs) && - "Too many arguments/incorrect function signature!"); - - std::vector Args; - Args.reserve(CS.arg_end()-CS.arg_begin()); - - // Calculate the arguments vector... - for (CallSite::arg_iterator I = CS.arg_begin(), E = CS.arg_end(); I != E; ++I) - if (isa((*I)->getType())) { - DSNodeHandle ArgNode; // Initially empty - if (shouldHaveNodeForValue(*I)) ArgNode = getNodeForValue(*I); - if (I - CS.arg_begin() < NumFixedArgs) { - Args.push_back(ArgNode); - } else { - VarArg.mergeWith(ArgNode); - } - } - - // - // Add a new function call entry. We get the called value from the call site - // and strip pointer casts instead of asking the CallSite class to do that - // since CallSite::getCalledFunction() returns 0 if the called value is - // a bit-casted function constant. - // - if (Function *F=dyn_cast(CS.getCalledValue()->stripPointerCasts())) - return DSCallSite(CS, RetVal, VarArg, F, Args); - else - return DSCallSite(CS, RetVal, VarArg, - getNodeForValue(CS.getCalledValue()).getNode(), Args); -} - - - -// markIncompleteNodes - Mark the specified node as having contents that are not -// known with the current analysis we have performed. Because a node makes all -// of the nodes it can reach incomplete if the node itself is incomplete, we -// must recursively traverse the data structure graph, marking all reachable -// nodes as incomplete. -// -static void markIncompleteNode(DSNode *N) { - // Stop recursion if no node, or if node already marked... - if (N == 0 || N->isIncompleteNode()) return; - - // Actually mark the node - N->setIncompleteMarker(); - - // Recursively process children... - for (DSNode::edge_iterator ii = N->edge_begin(), ee = N->edge_end(); - ii != ee; ++ii) - markIncompleteNode(ii->second.getNode()); -} - -static void markIncomplete(DSCallSite &Call) { - // Then the return value is certainly incomplete! - markIncompleteNode(Call.getRetVal().getNode()); - - markIncompleteNode(Call.getVAVal().getNode()); - - // All objects pointed to by function arguments are incomplete! - for (unsigned i = 0, e = Call.getNumPtrArgs(); i != e; ++i) - markIncompleteNode(Call.getPtrArg(i).getNode()); -} - -// markIncompleteNodes - Traverse the graph, identifying nodes that may be -// modified by other functions that have not been resolved yet. This marks -// nodes that are reachable through three sources of "unknownness": -// -// Global Variables, Function Calls, and Incoming Arguments -// -// For any node that may have unknown components (because something outside the -// scope of current analysis may have modified it), the 'Incomplete' flag is -// added to the NodeType. -// -void DSGraph::markIncompleteNodes(unsigned Flags) { - // Mark any incoming arguments as incomplete. - if (Flags & DSGraph::MarkFormalArgs) { - for (ReturnNodesTy::iterator FI = ReturnNodes.begin(), E =ReturnNodes.end(); - FI != E; ++FI) { - const Function &F = *FI->first; - for (const auto &Arg : F.args()) - if (isa(Arg.getType())) - markIncompleteNode(getNodeForValue(&Arg).getNode()); - markIncompleteNode(FI->second.getNode()); - } - // Mark all vanodes as incomplete (they are also arguments) - for (vanodes_iterator I = vanodes_begin(), E = vanodes_end(); - I != E; ++I) - markIncompleteNode(I->second.getNode()); - } - - // Mark stuff passed into functions calls as being incomplete. - if (!shouldUseAuxCalls()) - for (FunctionListTy::iterator I = FunctionCalls.begin(), - E = FunctionCalls.end(); I != E; ++I) - markIncomplete(*I); - else - for (FunctionListTy::iterator I = AuxFunctionCalls.begin(), - E = AuxFunctionCalls.end(); I != E; ++I) - markIncomplete(*I); - - // Mark all global nodes as incomplete that aren't initialized and constant. - if ((Flags & DSGraph::IgnoreGlobals) == 0) - for (DSScalarMap::global_iterator I = ScalarMap.global_begin(), - E = ScalarMap.global_end(); I != E; ++I) - if (const GlobalVariable *GV = dyn_cast(*I)) { - if (!(GV->hasInitializer() && GV->isConstant())){ - markIncompleteNode(ScalarMap[GV].getNode()); - } - } - - // Mark any node with the VAStart flag as incomplete. - if (Flags & DSGraph::MarkVAStart) { - for (node_iterator i=node_begin(); i != node_end(); ++i) { - if (i->isVAStartNode()) - markIncompleteNode(&*i); - } - } -} - -// -// Function: markExternalNode() -// -// Description: -// Marks the specified node, and all that's reachable from it, as external. -// It uses 'processedNodes' to track recursion. -// -static void markExternalNode(DSNode *N, DenseSet & processedNodes) { - // Stop recursion if no node, or if node already processed - if (N == 0 || processedNodes.count(N) ) return; - - processedNodes.insert(N); - - // Actually mark the node - N->setExternalMarker(); - - // FIXME: Should we 'collapse' the node as well? - - // Recursively process children... - for (DSNode::edge_iterator ii = N->edge_begin(), ee = N->edge_end(); - ii != ee; ++ii) - markExternalNode(ii->second.getNode(), processedNodes); -} - -// markExternal --marks the specified callsite external, using 'processedNodes' to track recursion. -static void markExternal(const DSCallSite &Call, DenseSet & processedNodes) { - markExternalNode(Call.getRetVal().getNode(), processedNodes); - - markExternalNode(Call.getVAVal().getNode(), processedNodes); - - // Mark all pointer arguments... - for (unsigned i = 0, e = Call.getNumPtrArgs(); i != e; ++i) - markExternalNode(Call.getPtrArg(i).getNode(), processedNodes); -} - -// -// Method: propagateExternal() -// -// Description: -// Walk the given DSGraph and ensure that, within this graph, -// everything reachable from a node marked External is also marked External. -// -static void propagateExternal(DSGraph * G, DenseSet & processedNodes) { - DSGraph::node_iterator I = G->node_begin(), - E = G->node_end(); - for ( ; I != E; ++I ) { - if (I->isExternalNode()) - markExternalNode(&*I, processedNodes); - } -} - -// -// Method: computeIntPtrFlags() -// -// Description: -// Mark all nodes that must get P2 flags due to type overlap. -// -void DSGraph::computeIntPtrFlags() { - DSGraph::node_iterator I = node_begin(), - E = node_end(); - for ( ; I != E; ++I ) { - I->markIntPtrFlags(); - } -} - -// computeExternalFlags -- mark all reachable from external as external -void DSGraph::computeExternalFlags(unsigned Flags) { - - DenseSet processedNodes; - - // Reset if indicated - if (Flags & ResetExternal) { - maskNodeTypes(~DSNode::ExternalNode); - } - - // Make sure that everything reachable from something already external is - // also external - propagateExternal(this, processedNodes); - - // If requested, we mark all functions (their formals) in this - // graph (read: SCC) as external. - if (Flags & MarkFormalsExternal) { - for (ReturnNodesTy::iterator FI = ReturnNodes.begin(), E =ReturnNodes.end(); - FI != E; ++FI) { - const Function &F = *FI->first; - // Mark its arguments, return value (and vanode) as external. - for (const auto &Arg : F.args()) { - if(TypeInferenceOptimize) { - if(Arg.getName().str() == "argv") - continue; - } - if (isa(Arg.getType())) - markExternalNode(getNodeForValue(&Arg).getNode(), processedNodes); - } - markExternalNode(FI->second.getNode(), processedNodes); - markExternalNode(getVANodeFor(F).getNode(), processedNodes); - } - } - - // If requested, look for callsites to external functions and make - // sure that they're marked external as appropriate. - if (Flags & ProcessCallSites) { - // Get List of all callsites, resolved or not... - std::list AllCalls; - AllCalls.insert(AllCalls.begin(), fc_begin(), fc_end()); - AllCalls.insert(AllCalls.begin(), afc_begin(), afc_end()); - - // ...and use that list to find all CallSites that call external functions - // and mark them accordingly. - for (std::list::iterator I = AllCalls.begin(), - E = AllCalls.end(); I != E; ++I) { - bool shouldBeMarkedExternal = false; - - // Figure out what this callsite calls... - std::vector Functions; - if (I->isDirectCall()) - Functions.push_back(I->getCalleeFunc()); - else - I->getCalleeNode()->addFullFunctionList(Functions); - - // ...And examine each callee: - for (std::vector::iterator II = Functions.begin(), - EE = Functions.end(); - (II != EE) && !shouldBeMarkedExternal; ++II) { - - // Calls to external functions should be marked external - shouldBeMarkedExternal |= (*II)->isDeclaration(); - } - - // If this callsite can call external code, it better be the case that - // the pointer arguments and the return values are all marked external - // (and what's reachable from them) - if (shouldBeMarkedExternal) { - markExternal(*I, processedNodes); - } - } - } - - // Finally handle all external globals... - for (DSScalarMap::global_iterator I = ScalarMap.global_begin(), - E = ScalarMap.global_end(); I != E; ++I) { - if (const GlobalVariable *GV = dyn_cast(*I)) { - if(TypeInferenceOptimize) { - if(GV->getName().str() == "stderr"){ - continue; - } - if(GV->getName().str() == "stdout"){ - continue; - } - if(GV->getName().str() == "stdin"){ - continue; - } - } - // If the global is external... mark it as such! - DSNode * N = ScalarMap[GV].getNode(); - if (!(GV->hasInternalLinkage() || GV->hasPrivateLinkage()) || N->isExternalNode()) - markExternalNode(N, processedNodes); - } - } - -} - -static inline void killIfUselessEdge(DSNodeHandle &Edge) { - if (DSNode * N = Edge.getNode()) // Is there an edge? - if (N->getNumReferrers() == 1) // Does it point to a lonely node? - // No interesting info? - if ((N->getNodeFlags() & ~DSNode::IncompleteNode) == 0 - && N->hasNoType() - && !N->isNodeCompletelyFolded()) - Edge.setTo(0, 0); // Kill the edge! -} - -static void removeIdenticalCalls(DSGraph::FunctionListTy &Calls) { - // Remove trivially identical function calls - - unsigned NumDeleted = 0; - - // First, scan through killing off useless edges and trivially dead callsites - for (DSGraph::FunctionListTy::iterator I = Calls.begin(), E = Calls.end(); - I != E; ) { - DSCallSite &CS = *I; - - // If the return value or any arguments point to a void node with no - // information at all in it, and the call node is the only node to point - // to it, remove the edge to the node (killing the node). - // - killIfUselessEdge(CS.getRetVal()); - killIfUselessEdge(CS.getVAVal()); - for (unsigned a = 0, e = CS.getNumPtrArgs(); a != e; ++a) - killIfUselessEdge(CS.getPtrArg(a)); - - // If this is an indirect callsite, but the Callee DSNode isn't - // tied to from anything, remove it trivially. - if (CS.isIndirectCall()) { - DSNode *Callee = CS.getCalleeNode(); - if (Callee->getNumReferrers() == 1 && Callee->isCompleteNode() && - Callee->isEmptyGlobals()) { // No useful info? - SDEBUG(errs() << "WARNING: Useless call site found.\n"); - I = Calls.erase(I); - E = Calls.end(); - ++NumDeleted; - continue; - } - } - ++I; - } - - // Now scan for redundant indirect callsites - // First, sort by callee (using DSCallSite::operator<) - sort(Calls); - - // Then find adjacent callsites that are equivalent and handle accordingly - DSGraph::FunctionListTy::iterator I = Calls.begin(); - while((I = std::adjacent_find(I, Calls.end())) != Calls.end()) { - DSGraph::FunctionListTy::iterator Second = I; - DSCallSite &DCS1 = *I, &DCS2 = *++Second; - - if (DCS1.isIndirectCall()) { - // Merge them together (into the first one) - DCS1.mergeWith(DCS2); - } - - // Remove the second one - ++NumDeleted; - Calls.erase(Second); - - // Carry on, searching from 'I' (first one)... - } - - // Track the number of call nodes merged away... - NumCallNodesMerged += NumDeleted; - - if (NumDeleted) - SDEBUG(errs() << "Merged " << NumDeleted << " call nodes.\n"); -} -// removeTriviallyDeadNodes - After the graph has been constructed, this method -// removes all unreachable nodes that are created because they got merged with -// other nodes in the graph. These nodes will all be trivially unreachable, so -// we don't have to perform any non-trivial analysis here. -// -void DSGraph::removeTriviallyDeadNodes() { - /// NOTE: This code is disabled. This slows down DSA on 177.mesa - /// substantially! - - // Loop over all of the nodes in the graph, calling getNode on each field. - // This will cause all nodes to update their forwarding edges, causing - // forwarded nodes to be delete-able. Further, reclaim any memory used by - // useless edge or type entries - for (node_iterator NI = node_begin(), E = node_end(); NI != E; ++NI) - for (DSNode::edge_iterator ii = NI->edge_begin(), ee = NI->edge_end(); - ii != ee; ++ii) { - ii->second.getNode()->cleanEdges(); - } - - // Likewise, forward any edges from the scalar nodes. While we are at it, - // clean house a bit. - for (DSScalarMap::iterator I = ScalarMap.begin(), E = ScalarMap.end(); - I != E; ++I) - I->second.getNode(); - - bool isGlobalsGraph = !GlobalsGraph; - - for (NodeListTy::iterator NI = Nodes.begin(), E = Nodes.end(); NI != E; ) { - DSNode &Node = *NI; - - // Do not remove *any* global nodes in the globals graph. - // This is a special case because such nodes may not have I, M, R flags set. - if (Node.isGlobalNode() && isGlobalsGraph) { - ++NI; - continue; - } - - if (Node.isCompleteNode() && !Node.isModifiedNode() && !Node.isReadNode()) { - // This is a useless node if it has no mod/ref info (checked above), - // outgoing edges (which it cannot, as it is not modified in this - // context), and it has no incoming edges. If it is a global node it may - // have all of these properties and still have incoming edges, due to the - // scalar map, so we check those now. - // - if (Node.getNumReferrers() == Node.numGlobals()) { - - // Loop through and make sure all of the globals are referring directly - // to the node... - for (DSNode::globals_iterator j = Node.globals_begin(), e = Node.globals_end(); - j != e; ++j) { - getNodeForValue(*j).getNode(); - assert((getNodeForValue(*j).getNode()) == &Node && "ScalarMap doesn't match globals list!"); - } - - // Make sure NumReferrers still agrees, if so, the node is truly dead. - if (Node.getNumReferrers() == Node.numGlobals()) { - for (DSNode::globals_iterator j = Node.globals_begin(), e = Node.globals_end(); - j != e; ++j) - if (ScalarMap.find(*j) != ScalarMap.end()) - ScalarMap.erase(*j); - Node.makeNodeDead(); - ++NumTrivialGlobalDNE; - } - } - } - - if ((Node.getNodeFlags() == 0 && Node.hasNoReferrers()) - || (isGlobalsGraph && Node.hasNoReferrers() && !Node.isGlobalNode())){ - // This node is dead! - NI = Nodes.erase(NI); // Erase & remove from node list. - ++NumTrivialDNE; - } else { - ++NI; - } - } -#if 0 -#endif - removeIdenticalCalls(FunctionCalls); - removeIdenticalCalls(AuxFunctionCalls); -} - -// CanReachAliveNodes - Simple graph walker that recursively traverses the graph -// looking for a node that is marked alive. If an alive node is found, return -// true, otherwise return false. If an alive node is reachable, this node is -// marked as alive... -// -static bool CanReachAliveNodes(DSNode *N, DenseSet &Alive, - DenseSet &Visited, - bool IgnoreGlobals) { - if (N == 0) return false; - assert(N->isForwarding() == 0 && "Cannot mark a forwarded node!"); - - // If this is a global node, it will end up in the globals graph anyway, so we - // don't need to worry about it. - if (IgnoreGlobals && N->isGlobalNode()) return false; - - // If we know that this node is alive, return so! - if (Alive.count(N)) return true; - - // Otherwise, we don't think the node is alive yet, check for infinite - // recursion. - if (Visited.count(N)) return false; // Found a cycle - Visited.insert(N); // No recursion, insert into Visited... - - for (DSNode::edge_iterator I = N->edge_begin(),E = N->edge_end(); I != E; ++I) - if (CanReachAliveNodes(I->second.getNode(), Alive, Visited, IgnoreGlobals)) { - N->markReachableNodes(Alive); - return true; - } - return false; -} - -// CallSiteUsesAliveArgs - Return true if the specified call site can reach any -// alive nodes. -// -static bool CallSiteUsesAliveArgs(const DSCallSite &CS, - DenseSet &Alive, - DenseSet &Visited, - bool IgnoreGlobals) { - if (CanReachAliveNodes(CS.getRetVal().getNode(), Alive, Visited, - IgnoreGlobals)) - return true; - if (CanReachAliveNodes(CS.getVAVal().getNode(), Alive, Visited, IgnoreGlobals)) - return true; - if (CS.isIndirectCall() && - CanReachAliveNodes(CS.getCalleeNode(), Alive, Visited, IgnoreGlobals)) - return true; - for (unsigned i = 0, e = CS.getNumPtrArgs(); i != e; ++i) - if (CanReachAliveNodes(CS.getPtrArg(i).getNode(), Alive, Visited, - IgnoreGlobals)) - return true; - return false; -} - -// removeDeadNodes - Use a more powerful reachability analysis to eliminate -// subgraphs that are unreachable. This often occurs because the data -// structure doesn't "escape" into it's caller, and thus should be eliminated -// from the caller's graph entirely. This is only appropriate to use when -// inlining graphs. -// -// This function also clones information about globals back into the globals -// graph before it deletes the nodes. -void DSGraph::removeDeadNodes(unsigned Flags) { - SDEBUG(AssertGraphOK(); if (GlobalsGraph) GlobalsGraph->AssertGraphOK()); - - // Reduce the amount of work we have to do... remove dummy nodes left over by - // merging... - removeTriviallyDeadNodes(); - - // FIXME: Merge non-trivially identical call nodes... - - // Alive - a set that holds all nodes found to be reachable/alive. - DenseSet Alive; - std::vector > GlobalNodes; - - // Copy and merge all information about globals to the GlobalsGraph if this is - // not a final pass (where unreachable globals are removed). - // - // Strip all alloca bits since we are merging information into the globals - // graph. - // Strip all incomplete bits since they are short-lived properties and they - // will be correctly computed when rematerializing nodes into the functions. - // - // This code merges information learned about the globals in 'this' graph - // back into the globals graph, before it deletes any such global nodes, - // (with some new information possibly) from 'this' current function graph. - ReachabilityCloner GGCloner(GlobalsGraph, this, DSGraph::StripAllocaBit | - DSGraph::StripIncompleteBit); - - // Mark all nodes reachable by (non-global) scalar nodes as alive... - for (DSScalarMap::iterator I = ScalarMap.begin(), E = ScalarMap.end(); - I != E; ++I) - if (isa (I->first)) { // Keep track of global nodes - assert(!I->second.isNull() && "Null global node?"); - assert(I->second.getNode()->isGlobalNode() && "Should be a global node!"); - GlobalNodes.push_back(std::make_pair(I->first, I->second.getNode())); - - // Make sure that all globals are cloned over as roots. - if (!(Flags & DSGraph::RemoveUnreachableGlobals) && GlobalsGraph) { - GGCloner.getClonedNH(I->second); - } - } else { - I->second.getNode()->markReachableNodes(Alive); - } - - // The return values are alive as well. - for (ReturnNodesTy::iterator I = ReturnNodes.begin(), E = ReturnNodes.end(); - I != E; ++I) - I->second.getNode()->markReachableNodes(Alive); - - // Mark any nodes reachable by primary calls as alive... - for (fc_iterator I = fc_begin(), E = fc_end(); I != E; ++I) - I->markReachableNodes(Alive); - - - // Now find globals and aux call nodes that are already live or reach a live - // value (which makes them live in turn), and continue till no more are found. - // - bool Iterate; - DenseSet Visited; - std::set AuxFCallsAlive; - do { - Visited.clear(); - // If any global node points to a non-global that is "alive", the global is - // "alive" as well... Remove it from the GlobalNodes list so we only have - // unreachable globals in the list. - // - Iterate = false; - if (!(Flags & DSGraph::RemoveUnreachableGlobals)) - for (unsigned i = 0; i != GlobalNodes.size(); ++i) - if (CanReachAliveNodes(GlobalNodes[i].second, Alive, Visited, - Flags & DSGraph::RemoveUnreachableGlobals)) { - std::swap(GlobalNodes[i--], GlobalNodes.back()); // Move to end to... - GlobalNodes.pop_back(); // erase efficiently - Iterate = true; - } - - // Mark only unresolvable call nodes for moving to the GlobalsGraph since - // call nodes that get resolved will be difficult to remove from that graph. - // The final unresolved call nodes must be handled specially at the end of - // the BU pass (i.e., in main or other roots of the call graph). - for (afc_iterator CI = afc_begin(), E = afc_end(); CI != E; ++CI) - if (!AuxFCallsAlive.count(&*CI) && - (CI->isIndirectCall() - || CallSiteUsesAliveArgs(*CI, Alive, Visited, - Flags & DSGraph::RemoveUnreachableGlobals))) { - CI->markReachableNodes(Alive); - AuxFCallsAlive.insert(&*CI); - Iterate = true; - } - } while (Iterate); - - // If only some of the aux calls are alive - if (AuxFCallsAlive.size() != AuxFunctionCalls.size()) { - // Move dead aux function calls to the end of the list - FunctionListTy::iterator Erase = AuxFunctionCalls.end(); - for (FunctionListTy::iterator CI = AuxFunctionCalls.begin(); CI != Erase; ) - if (AuxFCallsAlive.count(&*CI)) - ++CI; - else { - // Copy and merge global nodes and dead aux call nodes into the - // GlobalsGraph, and all nodes reachable from those nodes. Update their - // target pointers using the GGCloner. - // - if (!(Flags & DSGraph::RemoveUnreachableGlobals)) - GlobalsGraph->AuxFunctionCalls.push_back(DSCallSite(*CI, GGCloner)); - - std::swap(*CI, *--Erase); - } - AuxFunctionCalls.erase(Erase, AuxFunctionCalls.end()); - } - AuxFCallsAlive.clear(); - - // We are finally done with the GGCloner so we can destroy it. - GGCloner.destroy(); - - // At this point, any nodes which are visited, but not alive, are nodes - // which can be removed. Loop over all nodes, eliminating completely - // unreachable nodes. - // - std::vector DeadNodes; - DeadNodes.reserve(Nodes.size()); - for (NodeListTy::iterator NI = Nodes.begin(), E = Nodes.end(); NI != E;) { - DSNode *N = &*NI; - ++NI; - assert(!N->isForwarding() && "Forwarded node in nodes list?"); - - if (!Alive.count(N)) { - Nodes.remove(N); - assert(!N->isForwarding() && "Cannot remove a forwarding node!"); - DeadNodes.push_back(N); - N->dropAllReferences(); - ++NumDNE; - } - } - - // Remove all unreachable globals from the ScalarMap. - // If flag RemoveUnreachableGlobals is set, GlobalNodes has only dead nodes. - // In either case, the dead nodes will not be in the set Alive. - for (unsigned i = 0, e = GlobalNodes.size(); i != e; ++i) - if (!Alive.count(GlobalNodes[i].second)) - ScalarMap.erase(GlobalNodes[i].first); - else - assert((Flags & DSGraph::RemoveUnreachableGlobals) && "non-dead global"); - - // Delete all dead nodes now since their referrer counts are zero. - for (unsigned i = 0, e = DeadNodes.size(); i != e; ++i) - delete DeadNodes[i]; - - SDEBUG(AssertGraphOK(); GlobalsGraph->AssertGraphOK()); -} - -void DSGraph::AssertNodeContainsGlobal(const DSNode *N, const GlobalValue *GV) const { - assert(std::find(N->globals_begin(),N->globals_end(), GV) != - N->globals_end() && "Global value not in node!"); -} - -void DSGraph::AssertCallSiteInGraph(const DSCallSite &CS) const { - if (CS.isIndirectCall()) { - AssertNodeInGraph(CS.getCalleeNode()); -#if 0 - if (CS.getNumPtrArgs() && CS.getCalleeNode() == CS.getPtrArg(0).getNode() && - CS.getCalleeNode() && CS.getCalleeNode()->getGlobals().empty()) - SDEBUG(errs() << "WARNING: WEIRD CALL SITE FOUND!\n"); -#endif - } - AssertNodeInGraph(CS.getRetVal().getNode()); - AssertNodeInGraph(CS.getVAVal().getNode()); - for (unsigned j = 0, e = CS.getNumPtrArgs(); j != e; ++j) - AssertNodeInGraph(CS.getPtrArg(j).getNode()); -} - -void DSGraph::AssertCallNodesInGraph() const { - for (fc_iterator I = fc_begin(), E = fc_end(); I != E; ++I) - AssertCallSiteInGraph(*I); -} -void DSGraph::AssertAuxCallNodesInGraph() const { - for (afc_const_iterator I = afc_begin(), E = afc_end(); I != E; ++I) - AssertCallSiteInGraph(*I); -} - -void DSGraph::AssertGraphOK() const { - for (node_const_iterator NI = node_begin(), E = node_end(); NI != E; ++NI) - NI->assertOK(); - - for (ScalarMapTy::const_iterator I = ScalarMap.begin(), - E = ScalarMap.end(); I != E; ++I) { - assert(!I->second.isNull() && "Null node in scalarmap!"); - AssertNodeInGraph(I->second.getNode()); - if (const GlobalValue *GV = dyn_cast(I->first)) { - assert(I->second.getNode()->isGlobalNode() && - "Global points to node, but node isn't global?"); - AssertNodeContainsGlobal(I->second.getNode(), GV); - } - } - AssertCallNodesInGraph(); - AssertAuxCallNodesInGraph(); - - // Check that all pointer arguments to any functions in this graph have - // destinations. - for (ReturnNodesTy::const_iterator RI = ReturnNodes.begin(), - E = ReturnNodes.end(); - RI != E; ++RI) { - const Function &F = *RI->first; - for (const auto &Arg : F.args()) - if (isa(Arg.getType())) - assert(!getNodeForValue(&Arg).isNull() && - "Pointer argument must be in the scalar map!"); - if (F.isVarArg()) - assert(VANodes.find(&F) != VANodes.end() && - "VarArg function missing VANode!"); - } -} - -/// computeNodeMapping - Given roots in two different DSGraphs, traverse the -/// nodes reachable from the two graphs, computing the mapping of nodes from the -/// first to the second graph. This mapping may be many-to-one (i.e. the first -/// graph may have multiple nodes representing one node in the second graph), -/// but it will not work if there is a one-to-many or many-to-many mapping. -/// -/// Inputs: -/// @NH1 - The first root value for which a node mapping is -/// desired. This value can have a NULL DSNode. -/// @NH2 - The second root value for which a node mapping is -/// desired. This value can have a NULL DSNode. -/// @StrictChecking - Flags whether strict sanity checks should be enforced. -/// -/// Outputs: -/// @NodeMap - A mapping of DSNodes to DSNode handles providing the node -/// mapping desired by the caller. -/// -/// Notes: -/// FIXME: Why was StrictChecking not passed in the recursive calls? -/// FIXME: Why isn't StrictChecking always desired? -/// -void DSGraph::computeNodeMapping(const DSNodeHandle &NH1, - const DSNodeHandle &NH2, NodeMapTy &NodeMap, - bool StrictChecking) { - // - // Get the DSNodes associated with the root values. If either one of them is - // NULL, then we are done. - // - DSNode *N1 = NH1.getNode(), *N2 = NH2.getNode(); - if (N1 == 0 || N2 == 0) return; - - DSNodeHandle &Entry = NodeMap[N1]; - if (!Entry.isNull()) { - // Termination of recursion! - if (StrictChecking) { - assert(Entry.getNode() == N2 && "Inconsistent mapping detected!"); - assert((Entry.getOffset() == (NH2.getOffset()-NH1.getOffset()) || - Entry.getNode()->isNodeCompletelyFolded()) && - "Inconsistent mapping detected!"); - } - return; - } - - // - // Modify the entry in the node map so that the DSNode from the first - // DSNodeHandle is mapped to the second DSNodeHandle. - // - // FIXME: AA:I am not sure what the right mapping for the - // following case is. I believe we do not need to create any - // new mapping. - //assert(((signed int)(NH2.getOffset()-NH1.getOffset())>=0) && " Underflow error "); - if(NH2.getOffset() >= NH1.getOffset()) { - Entry.setTo(N2, NH2.getOffset()-NH1.getOffset()); - } - - // - // The two DSNodes that we have could be strucures with outgoing links to - // other DSNodes. Recursively map such outgoing edges together, too. - // - - // - // If the second DSNode has no outgoing edges, then stop processing. There - // is nothing more to do. - // - unsigned N2Size = N2->getSize(); - if (N2Size == 0) return; // No edges to map to. - - // - // Recursively link outgoing edges together. - // - int N2Idx = NH2.getOffset()-NH1.getOffset(); - for (unsigned i = 0, e = N1->getSize(); i < e; ++i) { - const DSNodeHandle &N1NH = N1->getLink(i); - // - // Don't call N2->getLink if not needed (avoiding crash if N2Idx is not - // aligned correctly). - // - if (!N1NH.isNull()) { - // - // Compute the offset into the second DSNode. - // - unsigned offset = 0; - if (unsigned(N2Idx)+i < N2Size) - offset = N2Idx+i; - else - offset = (unsigned(N2Idx+i) % N2Size); - - // - // Compute the node mapping for the link. - // - const DSNodeHandle &N2NH = N2->getLink(offset); - - // - // Make sure not to recurse into the same node; - // that can cause recursion that does not terminate. - // - if (N1NH.getNode() == N1 && !N2NH.isNull()) { - DSNode *N1L = N1NH.getNode(), *N2L = N2NH.getNode(); - - DSNodeHandle &Entry = NodeMap[N1L]; - if (Entry.isNull()) { - // - // Modify the entry in the node map so that the DSNode from the first - // DSNodeHandle is mapped to the second DSNodeHandle. - // - // FIXME: AA:I am not sure what the right mapping for the - // following case is. I believe we do not need to create any - // new mapping. - //assert(((signed int)(N2NH.getOffset()-N1NH.getOffset())>=0) && " Underflow error "); - if(N2NH.getOffset() >= N1NH.getOffset()) { - Entry.setTo(N2L, N2NH.getOffset()-N1NH.getOffset()); - } - } - } else { - computeNodeMapping (N1NH, N2NH, NodeMap, StrictChecking); - } - } - } -} - - -/// computeGToGGMapping - Compute the mapping of nodes in the global graph to -/// nodes in this graph. -void DSGraph::computeGToGGMapping(NodeMapTy &NodeMap) { - DSGraph &GG = *getGlobalsGraph(); - - DSScalarMap &SM = getScalarMap(); - - // - // Iterate through all values used by this function (i.e., those values in - // the local graph in the function's DSGraph). For each one, compute the - // mapping between its DSNode in the local graph and its DSNode in the - // globals graph. - // - // Note that we use variables to hold intermediate values. This allows us - // to query these values more easily in the debugger. - // - for (DSScalarMap::global_iterator I = SM.global_begin(), - E = SM.global_end(); I != E; ++I) { - // Local value in the scalar map - const Value * LocalValue = *I; - - // DSNode Handle for the value in the local graph - DSNodeHandle LocalNodeHandle = SM[LocalValue]; - - // DSNode Handle for the value in the globals graph - DSNodeHandle GlobalNodeHandle = GG.getNodeForValue(LocalValue); - - // - // Add to the node mapping the mapping between the DSNode in the local - // graph and the DSNode in the globals graph. - // - DSGraph::computeNodeMapping(LocalNodeHandle, GlobalNodeHandle, NodeMap); - } -} - -/// computeGGToGMapping - Compute the mapping of nodes in the global graph to -/// nodes in this graph. Note that any uses of this method are probably bugs, -/// unless it is known that the globals graph has been merged into this graph! -void DSGraph::computeGGToGMapping(InvNodeMapTy &InvNodeMap) { - NodeMapTy NodeMap; - computeGToGGMapping(NodeMap); - - while (!NodeMap.empty()) { - InvNodeMap.insert(std::make_pair(NodeMap.begin()->second, - NodeMap.begin()->first)); - NodeMap.erase(NodeMap.begin()); - } -} - - -/// computeCalleeCallerMapping - Given a call from a function in the current -/// graph to the 'Callee' function (which lives in 'CalleeGraph'), compute the -/// mapping of nodes from the callee to nodes in the caller. -void DSGraph::computeCalleeCallerMapping(DSCallSite CS, const Function &Callee, - DSGraph &CalleeGraph, - NodeMapTy &NodeMap) { - - DSCallSite CalleeArgs = - CalleeGraph.getCallSiteForArguments(const_cast(Callee)); - - computeNodeMapping(CalleeArgs.getRetVal(), CS.getRetVal(), NodeMap); - computeNodeMapping(CalleeArgs.getVAVal(), CS.getVAVal(), NodeMap); - - unsigned NumArgs = CS.getNumPtrArgs(); - if (NumArgs > CalleeArgs.getNumPtrArgs()) - NumArgs = CalleeArgs.getNumPtrArgs(); - - for (unsigned i = 0; i != NumArgs; ++i) - computeNodeMapping(CalleeArgs.getPtrArg(i), CS.getPtrArg(i), NodeMap); - - // Map the nodes that are pointed to by globals. - DSScalarMap &CalleeSM = CalleeGraph.getScalarMap(); - DSScalarMap &CallerSM = getScalarMap(); - - if (CalleeSM.global_size() >= CallerSM.global_size()) { - for (DSScalarMap::global_iterator GI = CallerSM.global_begin(), - E = CallerSM.global_end(); GI != E; ++GI) - if (CalleeSM.global_count(*GI)) - computeNodeMapping(CalleeSM[*GI], CallerSM[*GI], NodeMap); - } else { - for (DSScalarMap::global_iterator GI = CalleeSM.global_begin(), - E = CalleeSM.global_end(); GI != E; ++GI) - if (CallerSM.global_count(*GI)) - computeNodeMapping(CalleeSM[*GI], CallerSM[*GI], NodeMap); - } -} - -/// updateFromGlobalGraph - This function rematerializes global nodes and -/// nodes reachable from them from the globals graph into the current graph. -/// -void DSGraph::updateFromGlobalGraph() { - ReachabilityCloner RC(this, GlobalsGraph, 0); - - // Clone the non-up-to-date global nodes into this graph. - for (DSScalarMap::global_iterator I = getScalarMap().global_begin(), - E = getScalarMap().global_end(); I != E; ++I) { - DSScalarMap::iterator It = GlobalsGraph->ScalarMap.find(*I); - if (It != GlobalsGraph->ScalarMap.end()) - RC.merge(getNodeForValue(*I), It->second); - } -} - -// -// Function: functionIsCallable() -// -// Description: -// Determine whether the specified function can be a target of the specified -// call site. We allow the user to configure what we consider to be -// uncallable at an indirect function call site. -// -// Inputs: -// CS - The call site which calls the function. -// F - The function that is potentially called by CS. -// -// Return value: -// true - The function F can be called by the call site. -// false - The function F cannot be called by the call site. -// -bool -llvm::functionIsCallable (ImmutableCallSite CS, const Function* F) { - //Which targets do we choose? - //Conservative: all of them - //Pretty Safe: same calling convention, otherwise undefined behavior - //Safe on some archs: - //Safe?: vararg call only calling vararg functions - //Safe?: non-vararg call only calling non-vararg functions - //Safe?: iany/ptr can't be interchanged in args w/ float/double - //Not so safe: number of args matching - const PointerType* PT = cast(CS.getCalledValue()->getType()); - const FunctionType* FT = cast(PT->getElementType()); - - // - // If the calling convention doesn't match, then the function cannot be - // called by this call site. - // - if (!noDSACallConv && CS.getCallingConv() != F->getCallingConv()) - return false; - - // - // We will consider the byval parameter attribute to be a part of the calling - // convention. If an actual argument is marked byval while the formal - // argument is not (or vice-versa), then the function is not a valid target. - // - if (!noDSACallConv) { - Function::const_arg_iterator farg = F->arg_begin(), fend = F->arg_end(); - for (unsigned index = 0; index < CS.getNumArgOperands() && farg != fend; - ++farg, ++index) { - if (CS.isByValArgument(index) != farg->hasByValAttr()) { - return false; - } - } - } - - // - // If the caller and callee don't agree on whether the target is a vararg - // function, then the function is not a valid target. - // - if (!noDSACallVA && FT->isVarArg() != F->isVarArg()) - return false; - - // - // If calling this function from this call site would require an implicit - // integer to floating point cast (or vice-versa), then don't consider the - // function callable from this call site. - // - if (!noDSACallFP) { - unsigned ANumParams = F->getFunctionType()->getNumParams(); - unsigned PNumParams = FT->getNumParams(); - unsigned NumParams = (ANumParams < PNumParams) ? ANumParams : PNumParams; - for (unsigned index = 0; index < NumParams; ++index) { - Type * AType = F->getFunctionType()->getParamType(index); - Type * PType = FT->getParamType(index); - if ((AType->isFPOrFPVectorTy() && !PType->isFPOrFPVectorTy()) - || - (!AType->isFPOrFPVectorTy() && PType->isFPOrFPVectorTy())) - return false; - } - } - - if (!noDSACallNumArgs) { - if(CS.arg_size() < F->arg_size()) { - return false; - } - } - - // - // We've done all the checks we've cared to do. The function F can be called - // from this call site. - // - return true; -} - -// -// Method: buildCallGraph() -// -// Description: -// This method updates the given call graph with new information about targets -// of function calls that can be inferred from the unresolved call sites -// within the DSGraph. -// -// The parameter GlobalFunctionList, is a list of all the address taken -// functions in the module. This is used as the list of targets when a callee -// node is Incomplete. -// -// The parameter 'filter' determines if we should attempt to prune callees -// that are illegal to be called from the callsite. -// -void DSGraph::buildCallGraph(DSCallGraph& DCG, std::vector& GlobalFunctionList, bool filter) const { - // - // Get the list of unresolved call sites. - // - const FunctionListTy& Calls = getFunctionCalls(); - for (FunctionListTy::const_iterator ii = Calls.begin(), - ee = Calls.end(); - ii != ee; ++ii) { - // - // Direct calls are easy. We know to where they go. - // - - if (ii->isDirectCall()) { - DCG.insert(ii->getCallSite(), ii->getCalleeFunc()); - } else { - CallSite CS = ii->getCallSite(); - std::vector MaybeTargets; - - if(ii->getCalleeNode()->isIncompleteNode()) - continue; - // - // Get the list of known targets of this function. - // - ii->getCalleeNode()->addFullFunctionList(MaybeTargets); - - // - // Ensure that the call graph at least knows about (has a record of) this - // call site. - // - DCG.insert(CS, 0); - - // - // Add to the call graph only function targets that have well-defined - // behavior using LLVM semantics. - // - for (std::vector::iterator Fi = MaybeTargets.begin(), - Fe = MaybeTargets.end(); Fi != Fe; ++Fi) - if (!filter || functionIsCallable(CS, *Fi)) - DCG.insert(CS, *Fi); - else - ++NumFiltered; - for (DSCallSite::MappedSites_t::iterator I = ii->ms_begin(), - E = ii->ms_end(); I != E; ++I) { - CallSite MCS = *I; - for (std::vector::iterator Fi = MaybeTargets.begin(), - Fe = MaybeTargets.end(); Fi != Fe; ++Fi) - if (!filter || functionIsCallable(MCS, *Fi)) - DCG.insert(MCS, *Fi); - else - ++NumFiltered; - } - } - } -} - -void DSGraph::buildCompleteCallGraph(DSCallGraph& DCG, - std::vector& GlobalFunctionList, bool filter) const { - // - // Get the list of unresolved call sites. - // - const FunctionListTy& Calls = getAuxFunctionCalls(); - for (FunctionListTy::const_iterator ii = Calls.begin(), - ee = Calls.end(); - ii != ee; ++ii) { - - if (ii->isDirectCall()) continue; - CallSite CS = ii->getCallSite(); - if (DCG.callee_size(CS) != 0) continue; - std::vector MaybeTargets; - MaybeTargets.assign(GlobalFunctionList.begin(), GlobalFunctionList.end()); - - DCG.insert(CS, 0); - // - // Add to the call graph only function targets that have well-defined - // behavior using LLVM semantics. - // - for (std::vector::iterator Fi = MaybeTargets.begin(), - Fe = MaybeTargets.end(); Fi != Fe; ++Fi) - if (!filter || functionIsCallable(CS, *Fi)) - DCG.insert(CS, *Fi); - else - ++NumFiltered; - - for (DSCallSite::MappedSites_t::iterator I = ii->ms_begin(), - E = ii->ms_end(); I != E; ++I) { - CallSite MCS = *I; - for (std::vector::iterator Fi = MaybeTargets.begin(), - Fe = MaybeTargets.end(); Fi != Fe; ++Fi) { - if (!filter || functionIsCallable(MCS, *Fi)) - DCG.insert(MCS, *Fi); - else - ++NumFiltered; - } - } - } - svset callees; - callees.insert(GlobalFunctionList.begin(), GlobalFunctionList.end()); - DCG.buildIncompleteCalleeSet(callees); -} diff --git a/lib/DSA/DSMonitor.cpp b/lib/DSA/DSMonitor.cpp deleted file mode 100644 index 733d68db3..000000000 --- a/lib/DSA/DSMonitor.cpp +++ /dev/null @@ -1,212 +0,0 @@ - -#include "dsa/DSMonitor.h" -#include "dsa/DSGraph.h" -#include "llvm/IR/DebugInfo.h" -#include "llvm/Support/raw_ostream.h" -#include "smack/SmackWarnings.h" - -#include - -namespace llvm { - -// XXX duplicated from DSA/Printer.cpp XXX -static std::string getCaption(const DSNode *N, const DSGraph *G) { - std::string empty; - raw_string_ostream OS(empty); - const Module *M = 0; - - if (!G) G = N->getParentGraph(); - - // Get the module from ONE of the functions in the graph it is available. - if (G && G->retnodes_begin() != G->retnodes_end()) - M = G->retnodes_begin()->first->getParent(); - if (M == 0 && G) { - // If there is a global in the graph, we can use it to find the module. - const DSScalarMap &SM = G->getScalarMap(); - if (SM.global_begin() != SM.global_end()) - M = (*SM.global_begin())->getParent(); - } - - if (N->isNodeCompletelyFolded()) - OS << "COLLAPSED"; - else { - if (N->type_begin() != N->type_end()) - for (DSNode::TyMapTy::const_iterator ii = N->type_begin(), - ee = N->type_end(); ii != ee; ++ii) { - OS << ii->first << ": "; - if (ii->second) - for (svset::const_iterator ni = ii->second->begin(), - ne = ii->second->end(); ni != ne; ++ni) { - Type * t = *ni; - t->print (OS); - OS << ", "; - } - else - OS << "VOID"; - OS << " "; - } - else - OS << "VOID"; - if (N->isArrayNode()) - OS << " array"; - } - if (unsigned NodeType = N->getNodeFlags()) { - OS << ": "; - if (NodeType & DSNode::AllocaNode ) OS << "S"; - if (NodeType & DSNode::HeapNode ) OS << "H"; - if (NodeType & DSNode::GlobalNode ) OS << "G"; - if (NodeType & DSNode::UnknownNode ) OS << "U"; - if (NodeType & DSNode::IncompleteNode ) OS << "I"; - if (NodeType & DSNode::ModifiedNode ) OS << "M"; - if (NodeType & DSNode::ReadNode ) OS << "R"; - if (NodeType & DSNode::ExternalNode ) OS << "E"; - if (NodeType & DSNode::ExternFuncNode ) OS << "X"; - if (NodeType & DSNode::IntToPtrNode ) OS << "P"; - if (NodeType & DSNode::PtrToIntNode ) OS << "2"; - if (NodeType & DSNode::VAStartNode ) OS << "V"; - -#ifndef NDEBUG - if (NodeType & DSNode::DeadNode ) OS << ""; -#endif - OS << "\n"; - } - - //Indicate if this is a VANode for some function - for (DSGraph::vanodes_iterator I = G->vanodes_begin(), E = G->vanodes_end(); - I != E; ++I) { - DSNodeHandle VANode = I->second; - if (N == VANode.getNode()) { - OS << "(VANode for " << I->first->getName().str() << ")\n"; - } - } - - EquivalenceClasses *GlobalECs = 0; - if (G) GlobalECs = &G->getGlobalECs(); - - for (DSNode::globals_iterator i = N->globals_begin(), e = N->globals_end(); - i != e; ++i) { - (*i)->printAsOperand(OS,false,M); - - // Figure out how many globals are equivalent to this one. - if (GlobalECs) { - EquivalenceClasses::iterator I = - GlobalECs->findValue(*i); - if (I != GlobalECs->end()) { - unsigned NumMembers = - std::distance(GlobalECs->member_begin(I), GlobalECs->member_end()); - if (NumMembers != 1) OS << " + " << (NumMembers-1) << " EC"; - } - } - OS << "\n"; - } - - return OS.str(); -} - -Instruction * getInstruction(Value *V) { - std::set covered{ V }; - std::deque workList{ V }; - - while (!workList.empty()) { - V = workList.front(); - workList.pop_front(); - if (auto I = dyn_cast(V)) { - return I; - } else { - for (auto U : V->users()) { - if (covered.count(U) == 0) { - workList.push_back(U); - covered.insert(U); - } - } - } - } - - return nullptr; -} - -void DSMonitor::unwatch() { - if (!N.isNull()) - N.setTo(nullptr, 0); - caption = ""; - location = ""; -} - -void DSMonitor::watch(DSNodeHandle N, std::vector VS, std::string M) { - if (N.isNull() || N.getNode()->isCollapsedNode()) { - unwatch(); - return; - } - - this->N = N; - this->VS = VS; - this->message = M; - DSGraph *G = N.getNode()->getParentGraph(); - caption = getCaption(N.getNode(), G); - - if (!VS.empty()) { - Instruction *I = getInstruction(VS[0]); - if (I && I->getMetadata("dbg")) { - const DebugLoc DL = I->getDebugLoc(); - auto *scope = cast(DL.getScope()); - location = scope->getFilename().str() + ":" - + std::to_string(DL.getLine()) + ":" - + std::to_string(DL.getCol()); - } - } -} - -void DSMonitor::warn() { - std::string msg; - raw_string_ostream ss(msg); - - if (location != "") - ss << location << ": "; - - ss << "warning: collapsing DSA node\n"; - - if (message != "") - ss << message << "\n"; - - if (VS.empty()) { - ss << "(unknown value)" << "\n"; - - } else { - if (Instruction *I = getInstruction(VS[0])) { - if (BasicBlock *B = I->getParent()) { - if (Function *F = B->getParent()) { - if (F->hasName()) - ss << "in function:\n " << F->getName() << "\n"; - } - if (B->hasName()) - ss << "in block:\n " << I->getParent()->getName() << "\n"; - } - ss << "at instruction:\n" << *I << "\n"; - } - - for (auto V : VS) - ss << "at value:\n" << *V << "\n"; - - if (caption != "") - ss << "node:\n " << caption << "\n"; - } - ss << "\n"; - - smack::SmackWarnings::warnInfo(ss.str()); -} - -void DSMonitor::witness(DSNodeHandle N, std::vector VS, std::string M) { - if (N.isNull() || N.getNode()->isCollapsedNode()) - return; - - watch(N,VS,M); - check(); -} - -void DSMonitor::check() { - if (!N.isNull() && N.getNode()->isCollapsedNode()) - warn(); - unwatch(); -} - -} diff --git a/lib/DSA/DSTest.cpp b/lib/DSA/DSTest.cpp deleted file mode 100644 index a969fbd2e..000000000 --- a/lib/DSA/DSTest.cpp +++ /dev/null @@ -1,709 +0,0 @@ -//===- DSTest.cpp - Queries DSA results for testing -----------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines various commandline options to DSA to help in regression tests. -// These options are: -// -print-node-for-value= Print the DSNodes for the given values -// -print-only-flags Only print Flags for the given values -// -print-only-values Only print the values pointed to by the given values -// -print-only-types Only print the types for the given values -// -check-same-node= Verify the given values' nodes were merged. -// -check-not-same-node= Verify the given values' nodes weren't merged. -// -check-type=,type Verify the given nodes have the given type -// -check-callees=caller, Verify the given caller has the following callees -// -check-not-callees=caller, Verify the given caller does not have the following callees -// -verify-flags= Verify the given values match the flag specifications. -// -// In general a 'value' query on the DSA results looks like this: -// graph:value[:offset]* -// Examples: -// "value" specifies 'value' in the globals graph -// "func:value" specifies 'value' in graph for function 'func' -// "func:value:0" the node pointed to at offset 0 from the above -// "func:value:0:1" the node pointed to at offset 1 from the above -// ..etc -// We are also robust to "@value" and "@func" notation for convenience -// The -verify-flags option takes values in this format, but also followed -// by any number of 'flag specifiers' of the form '+flags' and '-flags', -// which indicate flags that the node should and shouldn't have. -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "dsgraph-test" - -#include "dsa/DataStructure.h" -#include "dsa/DSGraph.h" -#include "dsa/DSNode.h" -#include "dsa/DSCallGraph.h" -#include "llvm/IR/Module.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/IR/ValueSymbolTable.h" -using namespace llvm; - -namespace { - cl::list PrintNodesForValues("print-node-for-value", - cl::CommaSeparated, cl::ReallyHidden); - cl::opt OnlyPrintFlags("print-only-flags", cl::ReallyHidden); - cl::opt OnlyPrintValues("print-only-values", cl::ReallyHidden); - cl::opt OnlyPrintTypes("print-only-types", cl::ReallyHidden); - // Test if all mentioned values are in the same node (merged) - cl::list CheckNodesSame("check-same-node", - cl::CommaSeparated, cl::ReallyHidden); - // Test if all mentioned values are in distinct nodes - cl::list CheckNodesNotSame("check-not-same-node", - cl::CommaSeparated, cl::ReallyHidden); - // For each value, verify they have (or don't have) the specified flags - cl::list VerifyFlags("verify-flags", - cl::CommaSeparated, cl::ReallyHidden); - // For each value, verify that type is as given - cl::list CheckType("check-type", - cl::CommaSeparated, cl::ReallyHidden); - // For first function, verify that it calls the other functions - cl::list CheckCallees("check-callees", - cl::CommaSeparated, cl::ReallyHidden); - // For first function, verify that it does not call the other functions - cl::list CheckNotCallees("check-not-callees", - cl::CommaSeparated, cl::ReallyHidden); -} - -typedef std::set FuncSetTy; - -/// NodeValue -- represents a particular node in a DSGraph -/// constructed from a serialized string representation of a value -/// -/// FIXME: Make this integrated into cl parsing, as mentioned: -/// http://llvm.org/docs/CommandLine.html#customparser -/// -/// FIXME: Support querying special nodes like return nodes, VANodes, etc -class NodeValue { - // Containing Function, if applicable. - Function *F; - Module *ParentM; - // Value in that graph's scalarmap that we base off of - // (note that the NH we have below could be indexed a few times - // from this value, only corresponds directly if no offsets) - Value *V; - // DSNodehandle - DSNodeHandle NH; - - // String version (that we were given) - std::string serialized; - - // Parsed list of offsets - typedef SmallVector OffsetVectorTy; - OffsetVectorTy offsets; - - NodeValue() {} - void operator=(const NodeValue&); - NodeValue(const NodeValue&); - - void initialize(const Module *M, const DataStructures *DS) { - parseValue(M); - assert(V && "Failed to parse value?"); - if (isa(V)) { - DSGraph *G = DS->getGlobalsGraph(); - assert(G->hasNodeForValue(V) && "Node not in specified graph!"); - NH = G->getNodeForValue(V); - } else { - assert(F && "No function?"); - DSGraph *G = DS->getDSGraph(*F); - assert(G->hasNodeForValue(V) && "Node not in specified graph!"); - NH = G->getNodeForValue(V); - } - // Handle offsets, if any - // For each offset in the offsets vector, follow the link at that offset - for (OffsetVectorTy::const_iterator I = offsets.begin(), E = offsets.end(); - I != E; ++I ) { - assert(!NH.isNull() && "Null NodeHandle?"); - assert(NH.hasLink(*I) && "Handle doesn't have link?"); - // Follow the offset - NH = NH.getLink(*I); - } - } - - /// stripOffsets -- strips the offsets - /// Walks backwards, stripping offsets. - /// Returns serialized without the offsets - /// - std::string stripOffsets() { - std::vector offsets_reversed; - SmallVector colonSeparated; - StringRef serializedRef = serialized; - serializedRef.split(colonSeparated,":"); - SmallVector::reverse_iterator I = colonSeparated.rbegin(), - E = colonSeparated.rend(); - for(; I != E; ++I ) { - unsigned offset; - // If this isn't an integer (offset), then bail - if (I->getAsInteger(0,offset)) - break; - offsets_reversed.push_back(offset); - } - // Okay so we built reversed list of offsets, now put things back together - - // If we have more than 2 values left, then we have something like: - // name1:name2:name3[:offset]*, which is no good. - // Also, if we have *nothing* left, something is similarly wrong. - assert(((E - I) > 0) && "Node was entirely made of offsets?"); - assert(((E - I) <= 2) && "Too many colons! (Invalid node/offset given)"); - - // Now rebuild the string, without the offsets. - std::string rebuilt = I++->str(); - for(; I != E; ++I) { - rebuilt = I->str() + ":" + rebuilt; - } - - // Reverse the offsets (since we parsed backwards) and put the result - // into the 'offsets' vector for use elsewhere. - offsets.insert(offsets.begin(), - offsets_reversed.rbegin(),offsets_reversed.rend()); - - return rebuilt; - } - - - /// parseValue -- sets value for the string we were constructed on, - /// using the provided module as the context to find the value - void parseValue(const Module *M) { - // Parse the offsets, and remove from the string - StringRef stripped = stripOffsets(); - - unsigned count = stripped.count(':'); - if (count == 0) { - // Global case - // format: "[@]value" - StringRef globalName = stripAtIfRequired(stripped); - - V = M->getNamedValue(globalName); - assert(V && "Unable to find specified global!"); - } else if (count == 1) { - // Function-specific case - // format: "[@]func:value" - - std::pair split = stripped.split(':'); - StringRef func = stripAtIfRequired(split.first); - StringRef value = split.second; - - // First, find the function - F = M->getFunction(func); - ParentM = const_cast(M); - assert(F && "Unable to find function specified!"); - - // Now we try to find the value... - // FIXME: This only works for named values, things like "%1" don't work. - // That might not be a deal breaker, but should be clear. - V = F->getValueSymbolTable()->lookup(value); - - assert(V && "Unable to find value in specified function!"); - - } else { - llvm_unreachable("Too many colons, offsets not stripped?"); - } - - assert(V && "Parsing value failed!"); - } - - /// stripAtIfRequired -- removes the leading '@' character if one exists - /// - StringRef stripAtIfRequired(StringRef v) { - if (!v.startswith("@")) - return v; - - assert(v.size() > 1 && "String too short"); - - return v.substr(1); - } -public: - /// Constructor (from string) - NodeValue(std::string & raw, const Module * M, const DataStructures *DS) - : F(NULL), V(NULL), serialized(raw) { - initialize(M,DS); - assert(V && NH.getNode() && "Parse failed!"); - } - - /// Accessors - DSNodeHandle & getNodeH() { return NH; } - DSGraph * getGraph() { return getNode()->getParentGraph(); } - // FIXME: These two (value/function) aren't used presently, and furthermore - // are a bit confusing in the context of offsets. Make this not lame. - Value * getValue() { return V; } - Function * getFunction() { return F; } - Module * getParentModule() { return ParentM; } - - /// Helper to fetch the node from the nodehandle - DSNode * getNode() { - assert(NH.getNode() && "NULL node?"); - return NH.getNode(); - } -}; - -/// printAllValuesForNode -- prints all values for a given node, without a newline -/// (meant to be a helper) -static void printAllValuesForNode(llvm::raw_ostream &O, NodeValue &NV) { - // We only consider other values that are in the graph - // containing the specified node (by design) - - // Look for values that have an equivalent NH - DSNodeHandle &NH = NV.getNodeH(); - const DSGraph::ScalarMapTy &SM = NV.getGraph()->getScalarMap(); - bool first = true; - - for (DSGraph::ScalarMapTy::const_iterator I = SM.begin(), E = SM.end(); - I != E; ++I ) - if (NH == I->second) { - //Found one! - const Value *V = I->first; - - //Print them out, separated by commas - if (!first) O << ","; - first = false; - - // Print out name, if it has one. - // FIXME: Get "%0, "%1", naming like the .ll has? - if (V->hasName()) - O << V->getName(); - else - O << ""; - } - - //FIXME: Search globals in this graph too (not just scalarMap)? -} - -// printTypesForNode --prints all the types for the given NodeValue, without a newline -// (meant to be called as a helper) -static void printTypesForNode(llvm::raw_ostream &O, NodeValue &NV) { - DSNode *N = NV.getNode(); - - if (N->isNodeCompletelyFolded()) { - O << "Folded"; - } - // Go through all the types, and just dump them. - // FIXME: Lifted from Printer.cpp, probably should be shared - bool firstType = true; - if (N->type_begin() != N->type_end()) - for (DSNode::TyMapTy::const_iterator ii = N->type_begin(), - ee = N->type_end(); ii != ee; ++ii) { - if (!firstType) O << "::"; - firstType = false; - O << ii->first << ":"; - if (ii->second) { - bool first = true; - for (svset::const_iterator ni = ii->second->begin(), - ne = ii->second->end(); ni != ne; ++ni) { - if (!first) O << "|"; - Type * t = *ni; - t->print (O); - first = false; - } - } - else - O << "VOID"; - } - else - O << "VOID"; - - if (N->isArrayNode()) - O << "Array"; -} - -FuncSetTy -getCalleesFor(const Function * caller, const DSCallGraph & cg) -{ - FuncSetTy callees; - - Function const*leader = cg.sccLeader(&*caller); - - // Add all methods in same SCC as caller... - for(DSCallGraph::scc_iterator sccii = cg.scc_begin(leader), - sccee = cg.scc_end(leader); sccii != sccee; ++sccii) - callees.insert(*sccii); - - // And all methods in the SCC's called by the caller - for(DSCallGraph::flat_iterator CI = cg.flat_callee_begin(caller); - CI != cg.flat_callee_end(caller); CI ++) { - callees.insert(*CI); - for(DSCallGraph::scc_iterator sccii = cg.scc_begin(*CI), - sccee = cg.scc_end(*CI); sccii != sccee; ++sccii) - callees.insert(*sccii); - } - - return callees; -} - -static void printCallees(FuncSetTy & Funcs, raw_ostream & O) -{ - FuncSetTy::iterator I = Funcs.begin(), - E = Funcs.end(); - if (I != E) - { - O << (*I)->getName(); - while(++I != E) - O << ", " << (*I)->getName(); - } -} - -static std::string getFlags(DSNode *N) { - std::string flags(""); - - // FIXME: This code is lifted directly from Printer.cpp - // Probably would be good to make this code shared... - // Leaving it separate for now to minimize invasiveness - if (unsigned NodeType = N->getNodeFlags()) { - if (NodeType & DSNode::AllocaNode ) flags += "S"; - if (NodeType & DSNode::HeapNode ) flags += "H"; - if (NodeType & DSNode::GlobalNode ) flags += "G"; - if (NodeType & DSNode::UnknownNode ) flags += "U"; - if (NodeType & DSNode::IncompleteNode ) flags += "I"; - if (NodeType & DSNode::ModifiedNode ) flags += "M"; - if (NodeType & DSNode::ReadNode ) flags += "R"; - if (NodeType & DSNode::ExternalNode ) flags += "E"; - if (NodeType & DSNode::ExternFuncNode ) flags += "X"; - if (NodeType & DSNode::IntToPtrNode ) flags += "P"; - if (NodeType & DSNode::PtrToIntNode ) flags += "2"; - if (NodeType & DSNode::VAStartNode ) flags += "V"; - } - - return flags; -} - -static void printFlags(llvm::raw_ostream &O, DSNode *N) { - O << getFlags(N); -} - -/// printNodes -- print the node specified by NV -/// -/// Format: -/// "flags:{value(s)}:{type(s)}" -/// -/// Additionally, the user can specify to print just one piece -static void printNode(llvm::raw_ostream &O, NodeValue &NV) { - assert( - ((!OnlyPrintFlags && !OnlyPrintValues)|| - (!OnlyPrintFlags && !OnlyPrintTypes) || - (!OnlyPrintValues && !OnlyPrintTypes)) && - "Only one \"Only\" option allowed!"); - - if (OnlyPrintFlags) { - printFlags(O,NV.getNode()); - } else if (OnlyPrintValues) { - printAllValuesForNode(O, NV); - } else if (OnlyPrintTypes) { - printTypesForNode(O, NV); - } else { - //Print all of them - printFlags(O,NV.getNode()); - O << ":{"; - printAllValuesForNode(O, NV); - O << "}:{"; - printTypesForNode(O, NV); - O << "}"; - } - - O << "\n"; -} - - -/// printNodes -- For each node the user indicated, print the node. -/// See 'printNode' for more details. -/// Returns true iff the user specified nodes to print. -/// -static bool printNodes(llvm::raw_ostream &O, const Module *M, - const DataStructures *DS) { - cl::list::iterator I = PrintNodesForValues.begin(), - E = PrintNodesForValues.end(); - if (I != E) { - for ( ; I != E; ++I ) { - // Make sense of what the user gave us - NodeValue NV(*I, M, DS); - // Print corresponding node - printNode(O, NV); - } - return true; - } - return false; -} - -/// checkIfNodesAreSame -- Verify each node that the user indicated -/// should be merged, is in fact merged. -/// Returns true iff the user specified any nodes for this option. -/// -static bool checkIfNodesAreSame(llvm::raw_ostream &O, const Module *M, - const DataStructures *DS) { - - // Verify all nodes listed in "CheckNodesSame" belong to the same node. - cl::list::iterator I = CheckNodesSame.begin(), - E = CheckNodesSame.end(); - // If the user specified that a set of values should be in the same node... - if (I != E) { - // Take the first such value as the reference to compare to the others - NodeValue NVReference(*I++, M, DS); - - // Iterate through the remaining to verify they're the same node. - for(; I != E; ++I) { - NodeValue NV(*I, M, DS); - assert(NVReference.getNodeH()==NV.getNodeH() && "Nodes don't match!"); - } - return true; - } - - return false; -} - -/// checkIfNodesAreNotSame -- Verify each node that the user indicated -/// shouldn't be merged, wasn't merged -/// Returns true iff the user specified any nodes for this option. -/// -static bool checkIfNodesAreNotSame(llvm::raw_ostream &O, const Module *M, - const DataStructures *DS) { - - // Verify all nodes listed in "CheckNodesNotSame" belong to distinct nodes. - cl::list::iterator I = CheckNodesNotSame.begin(), - E = CheckNodesNotSame.end(); - - // If the user specified that a set of values should be in separate nodes... - if (I != E) { - // Lookup all the values - unsigned count = E - I; - NodeValue ** NV = new NodeValue*[count]; - for(unsigned i = 0; I != E; ++I, ++i) - NV[i] = new NodeValue(*I, M, DS); - - //Compare all pairs to make sure they're distinct - for(unsigned i = 0; i < count; ++i) - for(unsigned j = i+1; j < count; ++j) { - assert(NV[i]->getNodeH() != NV[j]->getNodeH() && "Nodes not distinct!"); - } - - for(unsigned i = 0; i < count; ++i) - delete NV[i]; - delete [] NV; - - return true; - } - - return false; -} - -/// checkTypes -- Verify type for the given nodes. -/// Returns true iff the user specified anything for this option -/// - -static bool checkTypes(llvm::raw_ostream &O, const Module *M, - const DataStructures *DS) { - - // Verify all nodes listed in "CheckType" have the same Type - cl::list::iterator I = CheckType.begin(), - E = CheckType.end(); - // If the user specified that a set of values should be in the same node... - if (I != E) { - // last value is type string - std::string typeRef = *(--E); - //typeRef = typeRef.substr(1, typeRef.length()-2); - // Iterate through the remaining to verify they're the same node. - for(; I != E; ++I) { - NodeValue NV(*I, M, DS); - std::string *type = new std::string(); - llvm::raw_string_ostream *test= new llvm::raw_string_ostream(*type); - printTypesForNode(*test, NV); - std::string type1 = test->str(); - type1.erase(remove_if(type1.begin(), type1.end(), isspace), type1.end()); - typeRef.erase(remove_if(typeRef.begin(), typeRef.end(), isspace), typeRef.end()); - - if(type1 != typeRef) { - errs() << "ERROR: Testing for type : \t" << - typeRef << "\n"; - errs() << " But found this type :\t" << - test->str() << "\n"; - llvm_unreachable("Type verification failed!"); - } - } - return true; - } - return false; -} - -/// VerifyFlags -- Verify flag properties for the given nodes. -/// This is a common enough testing process that this was added to make it simpler. -/// Returns true iff the user specified anything for this option. -/// -/// This builds upon the node notation used elsewhere, and tacks on -/// node+flags, node-flags, node+flags-flags -/// Where +flags means 'this node should have these flags' -/// And -flags means 'this node should NOT have these flags' -/// -static bool verifyFlags(llvm::raw_ostream &O, const Module *M, - const DataStructures *DS) { - cl::list::iterator I = VerifyFlags.begin(), - E = VerifyFlags.end(); - if (I != E) { - for(; I != E; ++I) { - std::string NodeFlagOption = *I; - std::string::size_type FlagPos = NodeFlagOption.find_first_of("+-"); - if (FlagPos == std::string::npos) { - errs() << "No flags given for option \"" << NodeFlagOption << "\"!\n"; - llvm_unreachable("Invalid input!"); - } - - // Grab the part before the flag specifiers and parse that as a node - std::string NodeString = std::string(I->begin(),I->begin()+FlagPos); - NodeValue NV(NodeString, M, DS); - - // Process each of the flag specifiers (+flag, or -flag) - do { - bool shouldHaveFlag = (NodeFlagOption[FlagPos] == '+'); - - // Find the next specifier... - std::string::size_type NextPos = NodeFlagOption.find_first_of("+-",FlagPos+1); - - // Parse out the flags for this option - std::string FlagsListed; - if (NextPos != std::string::npos) - FlagsListed = std::string(I->begin()+FlagPos+1,I->begin()+NextPos); - else - FlagsListed = std::string(I->begin()+FlagPos+1,I->end()); - - // Do the checking! - std::string ActualFlags = getFlags(NV.getNode()); - for (std::string::iterator I = FlagsListed.begin(), E = FlagsListed.end(); - I != E; ++I ) { - if (shouldHaveFlag == (ActualFlags.find(*I) == std::string::npos)) - { - errs() << "ERROR: Verify flags for: \t" << - NodeFlagOption << "\n"; - errs() << " But found these flags: \t" << - ActualFlags << "\n"; - llvm_unreachable("Flag verification failed!"); - } - } - - - // Update FlagPos - FlagPos = NextPos; - } while(FlagPos != std::string::npos); - } - return true; - } - return false; -} -/// checkNotCallees -- Verify non-callees for the given function -/// Returns true iff the user specified anything for this option -/// -/// checks that the first function does not callsthe rest of the -/// functions in the list -static bool checkNotCallees(llvm::raw_ostream &O, const Module *M, - const DataStructures *DS) { - //Mangled names must be provided for C++ - cl::list::iterator I = CheckNotCallees.begin(), - E = CheckNotCallees.end(); - - // User didn't specify this option, bail. - if (I == E) return false; - - std::string &func = *(I); - Function *caller = M->getFunction(func); - assert(caller && "Function not found in module"); - - FuncSetTy notCallees; - while (++I != E) { - std::string &func = *(I); - const Function *callee = M->getFunction(func); - assert(callee && "Specified callee function not found in module!"); - notCallees.insert(callee); - } - - const DSCallGraph callgraph = DS->getCallGraph(); - FuncSetTy analysisCallees = getCalleesFor(caller, callgraph); - - if (std::includes(analysisCallees.begin(), analysisCallees.end(), - notCallees.begin(), notCallees.end())) { - FuncSetTy invalid; - std::set_intersection(analysisCallees.begin(), analysisCallees.end(), - notCallees.begin(), notCallees.end(), - std::inserter(invalid, invalid.begin())); - errs() << "ERROR: Callgraph check failed for: \t" << caller->getName() << "\n"; - errs() << " Analysis says calls: \t"; - printCallees(analysisCallees, errs()); errs() << "\n"; - errs() << " Testing to not call: \t"; - printCallees(notCallees, errs()); errs() << "\n"; - errs() << " *** Overlap: \t"; - printCallees(invalid, errs()); errs() << "\n"; - llvm_unreachable("Analysis contained the specified callees!"); - } - - return true; -} - -/// checkCallees -- Verify callees for the given function -/// Returns true iff the user specified anything for this option -/// -/// checks that the first function calls the rest of the -/// functions in the list -static bool checkCallees(llvm::raw_ostream &O, const Module *M, - const DataStructures *DS) { - - //Mangled names must be provided for C++ - cl::list::iterator I = CheckCallees.begin(), - E = CheckCallees.end(); - - // User didn't specify this option, bail. - if (I == E) return false; - - std::string &func = *(I); - Function *caller = M->getFunction(func); - assert(caller && "Function not found in module"); - - FuncSetTy expectedCallees; - while (++I != E) { - std::string &func = *(I); - const Function *callee = M->getFunction(func); - assert(callee && "Specified callee function not found in module!"); - expectedCallees.insert(callee); - } - - const DSCallGraph callgraph = DS->getCallGraph(); - FuncSetTy analysisCallees = getCalleesFor(caller, callgraph); - - if (!std::includes(analysisCallees.begin(), analysisCallees.end(), - expectedCallees.begin(), expectedCallees.end())) { - FuncSetTy missing; - std::set_difference(expectedCallees.begin(), expectedCallees.end(), - analysisCallees.begin(), analysisCallees.end(), - std::inserter(missing, missing.begin())); - errs() << "ERROR: Callgraph check failed for: \t" << caller->getName() << "\n"; - errs() << " Analysis says calls: \t"; - printCallees(analysisCallees, errs()); errs() << "\n"; - errs() << " Testing to make sure calls: \t"; - printCallees(expectedCallees, errs()); errs() << "\n"; - errs() << " *** Missing: \t"; - printCallees(missing, errs()); errs() << "\n"; - llvm_unreachable("Analysis didn't contain the specified callees!"); - } - - return true; -} - -/// handleTest -- handles any user-specified testing options. -/// returns true iff the user specified something to test. -/// -bool DataStructures::handleTest(llvm::raw_ostream &O, const Module *M) const { - - bool tested = false; - - tested |= printNodes(O,M,this); - tested |= checkIfNodesAreSame(O,M,this); - tested |= checkIfNodesAreNotSame(O,M,this); - tested |= verifyFlags(O,M,this); - tested |= checkTypes(O,M,this); - tested |= checkCallees(O,M,this); - tested |= checkNotCallees(O,M,this); - - return tested; -} - diff --git a/lib/DSA/DataStructure.cpp b/lib/DSA/DataStructure.cpp deleted file mode 100644 index 60676135e..000000000 --- a/lib/DSA/DataStructure.cpp +++ /dev/null @@ -1,1558 +0,0 @@ -//===- DataStructure.cpp - Implement the core data structure analysis -----===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements the core data structure functionality. -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "data-structure" -#include "dsa/DSGraphTraits.h" -#include "dsa/DataStructure.h" -#include "dsa/DSGraph.h" -#include "dsa/DSSupport.h" -#include "dsa/DSNode.h" -#include "dsa/DSMonitor.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/GlobalVariable.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/DataLayout.h" -#include "llvm/Support/CommandLine.h" -#include "smack/Debug.h" -#include "llvm/ADT/DepthFirstIterator.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SCCIterator.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/Support/Timer.h" -#include "llvm/Support/raw_ostream.h" - -#include -#include -using namespace llvm; - -#define COLLAPSE_ARRAYS_AGGRESSIVELY 0 -namespace { - STATISTIC (NumFolds, "Number of nodes completely folded"); - STATISTIC (NumFoldsOOBOffset, "Number of OOB offsets that caused node folding"); - STATISTIC (NumNodeAllocated , "Number of nodes allocated"); -} - -/// isForwarding - Return true if this NodeHandle is forwarding to another -/// one. -bool DSNodeHandle::isForwarding() const { - return N && N->isForwarding(); -} - -DSNode *DSNodeHandle::HandleForwarding() const { - assert(N->isForwarding() && "Can only be invoked if forwarding!"); - SDEBUG( - { //assert not looping - DSNode* NH = N; - svset seen; - while(NH && NH->isForwarding()) { - assert(seen.find(NH) == seen.end() && "Loop detected"); - seen.insert(NH); - NH = NH->ForwardNH.N; - } - } - ); - // Handle node forwarding here! - DSNode *Next = N->ForwardNH.getNode(); // Cause recursive shrinkage - Offset += N->ForwardNH.getOffset(); - - if (--N->NumReferrers == 0) { - // Removing the last referrer to the node, sever the forwarding link - N->stopForwarding(); - } - - N = Next; - N->NumReferrers++; - - if (N->getSize() <= Offset) { - assert(N->getSize() <= 1 && "Forwarded to shrunk but not collapsed node?"); - Offset = 0; - } - return N; -} - -//===----------------------------------------------------------------------===// -// DSScalarMap Implementation -//===----------------------------------------------------------------------===// - -DSNodeHandle &DSScalarMap::AddGlobal(const GlobalValue *GV) { - assert(GV); - assert(ValueMap.count(GV) == 0 && "GV already exists!"); - - // If the node doesn't exist, check to see if it's a global that is - // equated to another global in the program. - EquivalenceClasses::iterator ECI = GlobalECs.findValue(GV); - if (ECI != GlobalECs.end()) { - const GlobalValue *Leader = *GlobalECs.findLeader(ECI); - if (Leader != GV) { - GV = Leader; - iterator I = ValueMap.find(GV); - if (I != ValueMap.end()) - return I->second; - } - } - - // Okay, this is either not an equivalenced global or it is the leader, it - // will be inserted into the scalar map now. - GlobalSet.insert(GV); - - return ValueMap.insert(std::make_pair(GV, DSNodeHandle())).first->second; -} - -/// spliceFrom - Copy all entries from RHS, then clear RHS. -/// -void DSScalarMap::spliceFrom(DSScalarMap &RHS) { - // Special case if this is empty. - if (ValueMap.empty()) { - ValueMap.swap(RHS.ValueMap); - GlobalSet.swap(RHS.GlobalSet); - } else { - GlobalSet.insert(RHS.GlobalSet.begin(), RHS.GlobalSet.end()); - for (ValueMapTy::iterator I = RHS.ValueMap.begin(), E = RHS.ValueMap.end(); - I != E; ++I) - ValueMap[I->first].mergeWith(I->second); - RHS.ValueMap.clear(); - } -} - -//===----------------------------------------------------------------------===// -// DSNode Implementation -//===----------------------------------------------------------------------===// - -DSNode::DSNode(DSGraph *G) - : NumReferrers(0), Size(0), ParentGraph(G), NodeType(0) { - // Add the type entry if it is specified... - if (G) G->addNode(this); - ++NumNodeAllocated; - } - -// DSNode copy constructor... do not copy over the referrers list! -DSNode::DSNode(const DSNode &N, DSGraph *G, bool NullLinks) - : NumReferrers(0), Size(N.Size), ParentGraph(G), TyMap(N.TyMap), - Globals(N.Globals), NodeType(N.NodeType) { - if (!NullLinks) Links = N.Links; - G->addNode(this); - ++NumNodeAllocated; - } - -DSNode::~DSNode() { - dropAllReferences(); - assert(hasNoReferrers() && "Referrers to dead node exist!"); -} - -void DSNode::assertOK() const { - // assert(((Ty && Ty->getTypeID() != Type::VoidTyID) || - // ((!Ty || Ty->getTypeID() == Type::VoidTyID) && (Size == 0 || - // (NodeType & DSNode::ArrayNode)))) && - // "Node not OK!"); - - assert(ParentGraph && "Node has no parent?"); - for (globals_iterator ii = globals_begin(), ee = globals_end(); - ii != ee; ++ii) { - assert(ParentGraph->getScalarMap().global_count(*ii)); - assert(ParentGraph->getScalarMap().find(*ii)->second.getNode() == this); - } -} - -/// forwardNode - Mark this node as being obsolete, and all references to it -/// should be forwarded to the specified node and offset. -/// -void DSNode::forwardNode(DSNode *To, unsigned Offset) { - assert(this != To && "Cannot forward a node to itself!"); - assert(ForwardNH.isNull() && "Already forwarding from this node!"); - if (To->Size <= 1) Offset = 0; - assert((Offset < To->Size || (Offset == To->Size && Offset == 0)) && - "Forwarded offset is wrong!"); - ForwardNH.setTo(To, Offset); - NodeType = DeadNode; - Size = 0; - - DSNodeHandle ToNH(To,Offset); - - //Move the Links - for (LinkMapTy::iterator ii = Links.begin(), ee = Links.end(); - ii != ee; ++ii) { - if (!ii->second.isNull()) { - // Compute the offset into the current node at which to - // merge this link. In the common case, this is a linear - // relation to the offset in the original node (with - // wrapping), but if the current node gets collapsed due to - // recursive merging, we must make sure to merge in all remaining - // links at offset zero. - unsigned MergeOffset = 0; - if (ToNH.getNode()->getSize() != 1) - MergeOffset = (ii->first + Offset) % ToNH.getNode()->getSize(); - ToNH.getNode()->addEdgeTo(MergeOffset, ii->second); - } - } - Links.clear(); - - // Remove this node from the parent graph's Nodes list. - ParentGraph->unlinkNode(this); - ParentGraph = 0; -} - -// addGlobal - Add an entry for a global value to the Globals list. This also -// marks the node with the 'G' flag if it does not already have it. -// -void DSNode::addGlobal(const GlobalValue *GV) { - // First, check to make sure this is the leader if the global is in an - // equivalence class. - GV = getParentGraph()->getScalarMap().getLeaderForGlobal(GV); - - Globals.insert(GV); - setGlobalMarker(); -} - -void DSNode::addFunction(const Function* F) { - addGlobal(F); -} - -// removeGlobal - Remove the specified global that is explicitly in the globals -// list. -void DSNode::removeGlobal(const GlobalValue *GV) { - assert (Globals.count(GV) && "Global not in Node!"); - Globals.erase(GV); -} - -/// foldNodeCompletely - If we determine that this node has some funny -/// behavior happening to it that we cannot represent, we fold it down to a -/// single, completely pessimistic, node. This node is represented as a -/// single byte with a single TypeEntry of "void". -/// -void DSNode::foldNodeCompletely() { - if (isNodeCompletelyFolded()) return; // If this node is already folded... - - // llvm_unreachable("Folding is happening"); - - ++NumFolds; - - //Collapsed nodes don't really need a type - //Clear the array flag too. Node should be of type VOID - TyMap.clear(); - maskNodeTypes(~ArrayNode); - - // If this node has a size that is <= 1, we don't need to create a forwarding - // node. - if (getSize() <= 1) { - setCollapsedMarker(); - Size = 1; - assert(Links.size() <= 1 && "Size is 1, but has more links?"); - } else { - // Create the node we are going to forward to. This is required because - // some referrers may have an offset that is > 0. By forcing them to - // forward, the forwarder has the opportunity to correct the offset. - DSNode *DestNode = new DSNode(ParentGraph); - DestNode->NodeType = NodeType; - DestNode->setCollapsedMarker(); - DestNode->Size = 1; - DestNode->Globals.swap(Globals); - - // Start forwarding to the destination node... - forwardNode(DestNode, 0); - - } -} - -/// isNodeCompletelyFolded - Return true if this node has been completely -/// folded down to something that can never be expanded, effectively losing -/// all of the field sensitivity that may be present in the node. -/// -bool DSNode::isNodeCompletelyFolded() const { - return isCollapsedNode(); -} - -void DSNode::addValueList(std::vector &List) const { - DSScalarMap &SN = getParentGraph()->getScalarMap(); - for(DSScalarMap::const_iterator I = SN.begin(), E = SN.end(); I!= E; I++) { - if(SN[I->first].getNode() == this){ - //I->first->print(smack::dbgs(), true); - } - - } -} -/// addFullGlobalsSet - Compute the full set of global values that are -/// represented by this node. Unlike getGlobalsList(), this requires fair -/// amount of work to compute, so don't treat this method call as free. -void DSNode::addFullGlobalsSet(svset &Set) const { - if (globals_begin() == globals_end()) return; - - EquivalenceClasses &EC = getParentGraph()->getGlobalECs(); - - for (globals_iterator I = globals_begin(), E = globals_end(); I != E; ++I) { - EquivalenceClasses::iterator ECI = EC.findValue(*I); - if (ECI == EC.end()) - Set.insert(*I); - else - Set.insert(EC.member_begin(ECI), EC.member_end()); - } -} - -/// addFullFunctionSet - Identical to addFullGlobalsSet, but only return the -/// functions in the full list. -void DSNode::addFullFunctionSet(svset &Set) const { - if (globals_begin() == globals_end()) return; - - EquivalenceClasses &EC = getParentGraph()->getGlobalECs(); - - for (globals_iterator I = globals_begin(), E = globals_end(); I != E; ++I) { - EquivalenceClasses::iterator ECI = EC.findValue(*I); - if (ECI == EC.end()) { - if (const Function *F = dyn_cast(*I)) - Set.insert(F); - } else { - for (EquivalenceClasses::member_iterator MI = - EC.member_begin(ECI), E = EC.member_end(); MI != E; ++MI) - if (const Function *F = dyn_cast(*MI)) - Set.insert(F); - } - } -} - -void DSNode::dumpFuncs() { - std::vector List; - addFullFunctionList (List); - for (unsigned index = 0; index < List.size(); ++index) { - std::cerr << List[index]->getName().str() << std::endl; - } - return; -} - -/// markIntPtrFlags - Mark P2 flags on node, if integer and pointer types -/// overlap at any offset. -/// -void DSNode::markIntPtrFlags() { - // check if the types merged have both int and pointer at the same offset, - - const DataLayout &TD = getParentGraph()->getDataLayout(); - // check all offsets for that node. - for(unsigned offset = 0; offset < getSize() ; offset++) { - // if that Node has no Type information, skip - if(TyMap.find(offset) == TyMap.end()) - continue; - if(!TyMap[offset]) - continue; - - bool pointerTy = false; - bool integerTy = false; - unsigned intSize = 0; - unsigned ptrSize = 0; - - // Iterate through all the Types, at that offset, checking if we have - // found a pointer type/integer type - for (svset::const_iterator ni = TyMap[offset]->begin(), - ne = TyMap[offset]->end(); ni != ne; ++ni) { - if((*ni)->isPointerTy()) { - PointerType * PT = dyn_cast(*ni); - pointerTy = true; - ptrSize = TD.getPointerSize(PT->getAddressSpace()); - } - if((*ni)->isIntegerTy()) { - integerTy = true; - if (TD.getTypeStoreSize(*ni) > intSize) - intSize = TD.getTypeStoreSize(*ni); - } - } - // If this offset itself contains both pointer and integer, set the - // flags and exit. - if(pointerTy && integerTy){ - setUnknownMarker()->setIntToPtrMarker()->setPtrToIntMarker(); - return; - } - if(!pointerTy && !integerTy){ - continue; - } - - // If only either integer or pointer was found, we must see if it - // overlaps with any other pointer or integer type at an offset that - // comes later. - unsigned maxOffset = offset + (pointerTy ? ptrSize:intSize); - unsigned offset2 = offset; - while(offset2 < maxOffset && offset2 < getSize()) { - if(TyMap.find(offset2) == TyMap.end()) { - offset2++; - continue; - } - for (svset::const_iterator ni = TyMap[offset2]->begin(), - ne = TyMap[offset2]->end(); ni != ne; ++ni) { - if((*ni)->isPointerTy()) { - pointerTy = true; - } - if((*ni)->isIntegerTy()) { - integerTy = true; - } - } - // whenever we have found overlapping integer and pointer types, - // we can set the flags, and exit. - if(pointerTy && integerTy){ - setUnknownMarker()->setIntToPtrMarker()->setPtrToIntMarker(); - return; - } - offset2++; - } - } -} - -/// growSizeForType - This method increases the size of the node -/// to accomodate NewTy at the given offset. This is useful for -/// updating the size of a DSNode, without actually inferring a -/// Type. -void DSNode::growSizeForType(Type *NewTy, unsigned Offset) { - - if (!NewTy || NewTy->isVoidTy()) return; - - if (isCollapsedNode()) return; - if (isArrayNode() && getSize() > 0) { - Offset %= getSize(); - } - const DataLayout &TD = getParentGraph()->getDataLayout(); - if (Offset + TD.getTypeAllocSize(NewTy) >= getSize()) - growSize(Offset + TD.getTypeAllocSize(NewTy)); - -} - -/// mergeTypeInfo - This method merges the specified type into the current node -/// at the specified offset. This may update the current node's type record if -/// this gives more information to the node, it may do nothing to the node if -/// this information is already known, or it may merge the node completely (and -/// return true) if the information is incompatible with what is already known. -/// -/// This method returns true if the node is completely folded, otherwise false. -/// -void DSNode::mergeTypeInfo(Type *NewTy, unsigned Offset) { - if (!NewTy || NewTy->isVoidTy()) return; - if (isCollapsedNode()) return; - - growSizeForType(NewTy, Offset); - - // Clang generates loads and stores of struct types. - // %tmp12 = load %struct.demand* %retval, align 1 - - // In such cases, merge type information for each struct field - // individually(at the appropriate offset), instead of the - // struct type. - if(NewTy->isStructTy()) { - const DataLayout &TD = getParentGraph()->getDataLayout(); - StructType *STy = cast(NewTy); - const StructLayout *SL = TD.getStructLayout(cast(STy)); - unsigned count = 0; - for(Type::subtype_iterator ii = STy->element_begin(), ee = STy->element_end(); ii!= ee; ++ii, ++count) { - unsigned FieldOffset = SL->getElementOffset(count); - mergeTypeInfo(*ii, Offset + FieldOffset); - } - } else { - TyMap[Offset] = getParentGraph()->getTypeSS().getOrCreate(TyMap[Offset], NewTy); - } - - assert(TyMap[Offset]); -} - -void DSNode::mergeTypeInfo(const TyMapTy::mapped_type TyIt, unsigned Offset) { - if (isCollapsedNode()) return; - if (isArrayNode()) Offset %= getSize(); - - const DataLayout &TD = getParentGraph()->getDataLayout(); - if (!TyMap[Offset]){ - TyMap[Offset] = TyIt; - for (svset::const_iterator ni = TyMap[Offset]->begin(), - ne = TyMap[Offset]->end(); ni != ne; ++ni) { - if (Offset + TD.getTypeAllocSize(*ni) >= getSize()) - growSize(Offset + TD.getTypeAllocSize(*ni)); - } - } else if (TyIt) { - svset S(*TyMap[Offset]); - S.insert(TyIt->begin(), TyIt->end()); - TyMap[Offset] = getParentGraph()->getTypeSS().getOrCreate(S); - } - assert(TyMap[Offset]); -} - -void DSNode::mergeTypeInfo(const DSNode* DN, unsigned Offset) { - if (isCollapsedNode()) return; - - for (TyMapTy::const_iterator ii = DN->TyMap.begin(), ee = DN->TyMap.end(); - ii != ee; ++ii) - mergeTypeInfo(ii->second, ii->first + Offset); -} - -/// addEdgeTo - Add an edge from the current node to the specified node. This -/// can cause merging of nodes in the graph. -/// -void DSNode::addEdgeTo(unsigned Offset, const DSNodeHandle &NH) { - if (NH.isNull()) return; // Nothing to do - - if (isNodeCompletelyFolded()) - Offset = 0; - - DSNodeHandle &ExistingEdge = getLink(Offset); - if (!ExistingEdge.isNull()) { - // Merge the two nodes... - ExistingEdge.mergeWith(NH); - } else { // No merging to perform... - setLink(Offset, NH); // Just force a link in there... - } -} - -void DSNode::mergeGlobals(const DSNode &RHS) { - Globals.insert(RHS.Globals.begin(), RHS.Globals.end()); -} - -// MergeNodes - Helper function for DSNode::mergeWith(). -// This function does the hard work of merging two nodes, CurNodeH -// and NH after filtering out trivial cases and making sure that -// CurNodeH.offset >= NH.offset. -// -// ***WARNING*** -// Since merging may cause either node to go away, we must always -// use the node-handles to refer to the nodes. These node handles are -// automatically updated during merging, so will always provide access -// to the correct node after a merge. -// -void DSNode::MergeNodes(DSNodeHandle& CurNodeH, DSNodeHandle& NH) { - assert(CurNodeH.getOffset() >= NH.getOffset() && - "This should have been enforced in the caller."); - assert(CurNodeH.getNode()->getParentGraph()==NH.getNode()->getParentGraph() && - "Cannot merge two nodes that are not in the same graph!"); - - // Now we know that Offset >= NH.Offset, so convert it so our "Offset" (with - // respect to NH.Offset) is now zero. NOffset is the distance from the base - // of our object that N starts from. - // - unsigned NOffset = CurNodeH.getOffset()-NH.getOffset(); - unsigned NSize = NH.getNode()->getSize(); - - // If the two nodes are of different size, and the smaller node has the array - // bit set, collapse! - if (NSize != CurNodeH.getNode()->getSize()) { -#if COLLAPSE_ARRAYS_AGGRESSIVELY - if (NSize < CurNodeH.getNode()->getSize()) { - if (NH.getNode()->isArrayNode()) - NH.getNode()->foldNodeCompletely(); - } else if (CurNodeH.getNode()->isArrayNode()) { - NH.getNode()->foldNodeCompletely(); - } -#endif - } - - // If we are merging a node with a completely folded node, then both nodes are - // now completely folded. - // - if (CurNodeH.getNode()->isNodeCompletelyFolded()) { - if (!NH.getNode()->isNodeCompletelyFolded()) { - NH.getNode()->foldNodeCompletely(); - assert(NH.getNode() && NH.getOffset() == 0 && - "folding did not make offset 0?"); - NOffset = NH.getOffset(); - NSize = NH.getNode()->getSize(); - assert(NOffset == 0 && NSize == 1); - } - } else if (NH.getNode()->isNodeCompletelyFolded()) { - CurNodeH.getNode()->foldNodeCompletely(); - assert(CurNodeH.getNode() && CurNodeH.getOffset() == 0 && - "folding did not make offset 0?"); - NSize = NH.getNode()->getSize(); - NOffset = NH.getOffset(); - assert(NOffset == 0 && NSize == 1); - } - - // FIXME:Add comments. - if(NH.getNode()->isArrayNode() && !CurNodeH.getNode()->isArrayNode()) { - if(NH.getNode()->getSize() != 0 && CurNodeH.getNode()->getSize() != 0) { - if((NH.getNode()->getSize() != CurNodeH.getNode()->getSize() && - (NH.getOffset() != 0 || CurNodeH.getOffset() != 0) - && NH.getNode()->getSize() < CurNodeH.getNode()->getSize())) { - CurNodeH.getNode()->foldNodeCompletely(); - NH.getNode()->foldNodeCompletely(); - NSize = NH.getNode()->getSize(); - NOffset = NH.getOffset(); - } - } - } - if(!NH.getNode()->isArrayNode() && CurNodeH.getNode()->isArrayNode()) { - if(NH.getNode()->getSize() != 0 && CurNodeH.getNode()->getSize() != 0) { - if((NH.getNode()->getSize() != CurNodeH.getNode()->getSize() && - (NH.getOffset() != 0 || CurNodeH.getOffset() != 0) - && NH.getNode()->getSize() > CurNodeH.getNode()->getSize())) { - CurNodeH.getNode()->foldNodeCompletely(); - NH.getNode()->foldNodeCompletely(); - NSize = NH.getNode()->getSize(); - NOffset = NH.getOffset(); - } - } - } - - if (CurNodeH.getNode()->isArrayNode() && NH.getNode()->isArrayNode()) { - if(NH.getNode()->getSize() != 0 && CurNodeH.getNode()->getSize() != 0 - && (NH.getNode()->getSize() != CurNodeH.getNode()->getSize())){ - CurNodeH.getNode()->foldNodeCompletely(); - NH.getNode()->foldNodeCompletely(); - NSize = NH.getNode()->getSize(); - NOffset = NH.getOffset(); - } - } - - - DSNode *N = NH.getNode(); - if (CurNodeH.getNode() == N || N == 0) return; - assert(!CurNodeH.getNode()->isDeadNode()); - - // Merge the type entries of the two nodes together... - CurNodeH.getNode()->mergeTypeInfo(NH.getNode(), NOffset); - if (NH.getNode()->getSize() + NOffset > CurNodeH.getNode()->getSize()) - CurNodeH.getNode()->growSize(NH.getNode()->getSize() + NOffset); - assert(!CurNodeH.getNode()->isDeadNode()); - - // Merge the NodeType information. - CurNodeH.getNode()->NodeType |= N->NodeType; - - // Start forwarding to the new node! - N->forwardNode(CurNodeH.getNode(), NOffset); - assert(!CurNodeH.getNode()->isDeadNode()); - - // Make all of the outgoing links of N now be outgoing links of CurNodeH. - // - for (LinkMapTy::iterator ii = N->Links.begin(), ee = N->Links.end(); - ii != ee; ++ii) - if (ii->second.getNode()) { - // Compute the offset into the current node at which to - // merge this link. In the common case, this is a linear - // relation to the offset in the original node (with - // wrapping), but if the current node gets collapsed due to - // recursive merging, we must make sure to merge in all remaining - // links at offset zero. - unsigned MergeOffset = 0; - DSNode *CN = CurNodeH.getNode(); - if (CN->Size != 1) - MergeOffset = (ii->first + NOffset) % CN->getSize(); - CN->addEdgeTo(MergeOffset, ii->second); - } - - // Now that there are no outgoing edges, all of the Links are dead. - N->Links.clear(); - - // Merge the globals list... - CurNodeH.getNode()->mergeGlobals(*N); - - // Delete the globals from the old node... - N->Globals.clear(); -} - - -/// mergeWith - Merge this node and the specified node, moving all links to and -/// from the argument node into the current node, deleting the node argument. -/// Offset indicates what offset the specified node is to be merged into the -/// current node. -/// -/// The specified node may be a null pointer (in which case, we update it to -/// point to this node). -/// -void DSNode::mergeWith(const DSNodeHandle &NH, unsigned Offset) { - DSNode *N = NH.getNode(); - if (N == this && NH.getOffset() == Offset) - return; // Noop - - // If the RHS is a null node, make it point to this node! - if (N == 0) { - NH.mergeWith(DSNodeHandle(this, Offset)); - return; - } - - assert(!N->isDeadNode() && !isDeadNode()); - assert(!hasNoReferrers() && "Should not try to fold a useless node!"); - - if (N == this) { - // We cannot merge two pieces of the same node together, collapse the node - // completely. - SDEBUG(errs() << "Attempting to merge two chunks of the same node together!\n"); - foldNodeCompletely(); - return; - } - - // If both nodes are not at offset 0, make sure that we are merging the node - // at an later offset into the node with the zero offset. - // - if (Offset < NH.getOffset()) { - N->mergeWith(DSNodeHandle(this, Offset), NH.getOffset()); - return; - } else if (Offset == NH.getOffset() && getSize() < N->getSize()) { - // If the offsets are the same, merge the smaller node into the bigger node - N->mergeWith(DSNodeHandle(this, Offset), NH.getOffset()); - return; - } - - // Ok, now we can merge the two nodes. Use a static helper that works with - // two node handles, since "this" may get merged away at intermediate steps. - DSNodeHandle CurNodeH(this, Offset); - DSNodeHandle NHCopy(NH); - if (CurNodeH.getOffset() >= NHCopy.getOffset()) - DSNode::MergeNodes(CurNodeH, NHCopy); - else - DSNode::MergeNodes(NHCopy, CurNodeH); -} - -void DSNode::cleanEdges() { - //get rid of any type edge pointing to the null type - for (type_iterator ii = type_begin(); ii != type_end(); ) { - if (ii->second) - ++ii; - else { - type_iterator backup = ii; - ++backup; - TyMap.erase(ii); - ii = backup; - } - } - //get rid of any node edge pointing to nothing - for (edge_iterator ii = edge_begin(); ii != edge_end(); ) { - if (ii->second.isNull()) { - edge_iterator backup = ii; - ++backup; - Links.erase(ii); - ii = backup; - } else - ++ii; - } -} - -void DSNode::checkOffsetFoldIfNeeded(int Offset) { - if (!isNodeCompletelyFolded() && - (Size != 0 || Offset != 0) && - !isForwarding()) { - if ((Offset >= (int)Size) || Offset < 0) { - // Accessing offsets out of node size range - // This is seen in the "magic" struct in named (from bind), where the - // fourth field is an array of length 0, presumably used to create struct - // instances of different sizes - // More generally this happens whenever code indexes past the end - // of a struct type. We don't model this, so fold! - - // Collapse the node since its size is now variable - foldNodeCompletely(); - - ++NumFoldsOOBOffset; - } - } -} -//===----------------------------------------------------------------------===// -// ReachabilityCloner Implementation -//===----------------------------------------------------------------------===// - -DSNodeHandle ReachabilityCloner::getClonedNH(const DSNodeHandle &SrcNH) { - if (SrcNH.isNull()) return DSNodeHandle(); - const DSNode *SN = SrcNH.getNode(); - - DSNodeHandle &NH = NodeMap[SN]; - if (!NH.isNull()) { // Node already mapped? - DSNode *NHN = NH.getNode(); - unsigned NewOffset = NH.getOffset() + SrcNH.getOffset(); - if (NHN) { - NHN->checkOffsetFoldIfNeeded(NewOffset); - NHN = NH.getNode(); - } - return DSNodeHandle(NHN, NewOffset); - } - - // If SrcNH has globals and the destination graph has one of the same globals, - // merge this node with the destination node, which is much more efficient. - if (SN->globals_begin() != SN->globals_end()) { - DSScalarMap &DestSM = Dest->getScalarMap(); - for (DSNode::globals_iterator I = SN->globals_begin(),E = SN->globals_end(); - I != E; ++I) { - const GlobalValue *GV = *I; - DSScalarMap::iterator GI = DestSM.find(GV); - if (GI != DestSM.end() && !GI->second.isNull()) { - // We found one, use merge instead! - merge(GI->second, Src->getNodeForValue(GV)); - assert(!NH.isNull() && "Didn't merge node!"); - DSNode *NHN = NH.getNode(); - unsigned NewOffset = NH.getOffset() + SrcNH.getOffset(); - if (NHN) { - NHN->checkOffsetFoldIfNeeded(NewOffset); - NHN = NH.getNode(); - } - return DSNodeHandle(NHN, NewOffset); - } - } - } - - if (!createDest) return DSNodeHandle(0,0); - - DSNode *DN = new DSNode(*SN, Dest, true /* Null out all links */); - DN->maskNodeTypes(BitsToKeep); - NH = DN; - - // Next, recursively clone all outgoing links as necessary. Note that - // adding these links can cause the node to collapse itself at any time, and - // the current node may be merged with arbitrary other nodes. For this - // reason, we must always go through NH. - DN = 0; - for (DSNode::const_edge_iterator ii = SN->edge_begin(), ee = SN->edge_end(); - ii != ee; ++ii) { - const DSNodeHandle &SrcEdge = ii->second; - if (!SrcEdge.isNull()) { - const DSNodeHandle &DestEdge = getClonedNH(SrcEdge); - // Compute the offset into the current node at which to - // merge this link. In the common case, this is a linear - // relation to the offset in the original node (with - // wrapping), but if the current node gets collapsed due to - // recursive merging, we must make sure to merge in all remaining - // links at offset zero. - unsigned MergeOffset = 0; - DSNode *CN = NH.getNode(); - if (CN->getSize() != 1) - MergeOffset = (ii->first + NH.getOffset()) % CN->getSize(); - CN->addEdgeTo(MergeOffset, DestEdge); - } - } - - // If this node contains any globals, make sure they end up in the scalar - // map with the correct offset. - for (DSNode::globals_iterator I = SN->globals_begin(), E = SN->globals_end(); - I != E; ++I) { - const GlobalValue *GV = *I; - const DSNodeHandle &SrcGNH = Src->getNodeForValue(GV); - DSNodeHandle &DestGNH = NodeMap[SrcGNH.getNode()]; - assert(DestGNH.getNode() == NH.getNode() &&"Global mapping inconsistent"); - Dest->getNodeForValue(GV).mergeWith(DSNodeHandle(DestGNH.getNode(), - DestGNH.getOffset()+SrcGNH.getOffset())); - } - NH.getNode()->mergeGlobals(*SN); - - DSNode* NHN = NH.getNode(); - unsigned NewOffset = NH.getOffset() + SrcNH.getOffset(); - if (NHN) { - NHN->checkOffsetFoldIfNeeded(NewOffset); - NHN = NH.getNode(); - } - return DSNodeHandle(NHN, NewOffset); -} - -void ReachabilityCloner::merge(const DSNodeHandle &NH, - const DSNodeHandle &SrcNH) { - if (SrcNH.isNull()) return; // Noop - if (NH.isNull()) { - // If there is no destination node, just clone the source and assign the - // destination node to be it. - NH.mergeWith(getClonedNH(SrcNH)); - return; - } - - // Okay, at this point, we know that we have both a destination and a source - // node that need to be merged. Check to see if the source node has already - // been cloned. - const DSNode *SN = SrcNH.getNode(); - DSNodeHandle &SCNH = NodeMap[SN]; // SourceClonedNodeHandle - if (!SCNH.isNull()) { // Node already cloned? - DSNode *SCNHN = SCNH.getNode(); - NH.mergeWith(DSNodeHandle(SCNHN, - SCNH.getOffset()+SrcNH.getOffset())); - return; // Nothing to do! - } - - // Okay, so the source node has not already been cloned. Instead of creating - // a new DSNode, only to merge it into the one we already have, try to perform - // the merge in-place. The only case we cannot handle here is when the offset - // into the existing node is less than the offset into the virtual node we are - // merging in. In this case, we have to extend the existing node, which - // requires an allocation anyway. - DSNode *DN = NH.getNode(); // Make sure the Offset is up-to-date - if (NH.getOffset() >= SrcNH.getOffset()) { - if (!DN->isNodeCompletelyFolded()) { - // Make sure the destination node is folded if the source node is folded. - if (SN->isNodeCompletelyFolded()) { - DN->foldNodeCompletely(); - DN = NH.getNode(); - } else if (SN->getSize() != DN->getSize()) { - // If the two nodes are of different size, and the smaller node has the - // array bit set, collapse! -#if COLLAPSE_ARRAYS_AGGRESSIVELY - if (SN->getSize() < DN->getSize()) { - if (SN->isArrayNode()) { - DN->foldNodeCompletely(); - DN = NH.getNode(); - } - } else if (DN->isArrayNode()) { - DN->foldNodeCompletely(); - DN = NH.getNode(); - } -#endif - } - - - // FIXME:Add comments. - if(!DN->isArrayNode() && SN->isArrayNode()) { - if(DN->getSize() != 0 && SN->getSize() != 0) { - if((DN->getSize() != SN->getSize() && - (NH.getOffset() != 0 || SrcNH.getOffset() != 0) - && DN->getSize() > SN->getSize())) { - DN->foldNodeCompletely(); - DN = NH.getNode(); - } - } - } - if(!SN->isArrayNode() && DN->isArrayNode()) { - if(DN->getSize() != 0 && SN->getSize() != 0) { - if((DN->getSize() != SN->getSize() && - (NH.getOffset() != 0 || SrcNH.getOffset() != 0) - && DN->getSize() < SN->getSize())) { - DN->foldNodeCompletely(); - DN = NH.getNode(); - } - } - } - - if (SN->isArrayNode() && DN->isArrayNode()) { - if((SN->getSize() != DN->getSize()) && (SN->getSize() != 0) - && DN->getSize() != 0) { - DN->foldNodeCompletely(); - DN = NH.getNode(); - } - } - if (!DN->isNodeCompletelyFolded() && DN->getSize() < SN->getSize()) - DN->growSize(SN->getSize()); - - - // Merge the type entries of the two nodes together... - if (!DN->isNodeCompletelyFolded()) - DN->mergeTypeInfo(SN, NH.getOffset() - SrcNH.getOffset()); - } - - assert(!DN->isDeadNode()); - - // Merge the NodeType information. - DN->mergeNodeFlags(SN->getNodeFlags() & BitsToKeep); - - // Before we start merging outgoing links and updating the scalar map, make - // sure it is known that this is the representative node for the src node. - SCNH = DSNodeHandle(DN, NH.getOffset()-SrcNH.getOffset()); - - // If the source node contains any globals, make sure they end up in the - // scalar map with the correct offset. - if (SN->globals_begin() != SN->globals_end()) { - // Update the globals in the destination node itself. - DN->mergeGlobals(*SN); - - // Update the scalar map for the graph we are merging the source node - // into. - for (DSNode::globals_iterator I = SN->globals_begin(), - E = SN->globals_end(); I != E; ++I) { - const GlobalValue *GV = *I; - const DSNodeHandle &SrcGNH = Src->getNodeForValue(GV); - DSNodeHandle &DestGNH = NodeMap[SrcGNH.getNode()]; - assert(DestGNH.getNode()==NH.getNode() &&"Global mapping inconsistent"); - Dest->getNodeForValue(GV).mergeWith(DSNodeHandle(DestGNH.getNode(), - DestGNH.getOffset()+SrcGNH.getOffset())); - } - NH.getNode()->mergeGlobals(*SN); - } - } else { - // We cannot handle this case without allocating a temporary node. Fall - // back on being simple. - DSNode *NewDN = new DSNode(*SN, Dest, true /* Null out all links */); - NewDN->maskNodeTypes(BitsToKeep); - -#ifndef NDEBUG - unsigned NHOffset = NH.getOffset(); -#endif - NH.mergeWith(DSNodeHandle(NewDN, SrcNH.getOffset())); - -#ifndef NDEBUG - assert(NH.getNode() && - (NH.getOffset() > NHOffset || - (NH.getOffset() == 0 && NH.getNode()->isNodeCompletelyFolded())) && - "Merging did not adjust the offset!"); -#endif - - // Before we start merging outgoing links and updating the scalar map, make - // sure it is known that this is the representative node for the src node. - SCNH = DSNodeHandle(NH.getNode(), NH.getOffset()-SrcNH.getOffset()); - - // If the source node contained any globals, make sure to create entries - // in the scalar map for them! - for (DSNode::globals_iterator I = SN->globals_begin(), - E = SN->globals_end(); I != E; ++I) { - const GlobalValue *GV = *I; - const DSNodeHandle &SrcGNH = Src->getNodeForValue(GV); - DSNodeHandle &DestGNH = NodeMap[SrcGNH.getNode()]; - assert(DestGNH.getNode()==NH.getNode() &&"Global mapping inconsistent"); - assert(SrcGNH.getNode() == SN && "Global mapping inconsistent"); - Dest->getNodeForValue(GV).mergeWith(DSNodeHandle(DestGNH.getNode(), - DestGNH.getOffset()+SrcGNH.getOffset())); - } - } - - // DOUT << "LLVA: mergeWith: " << SN << " becomes " << DN << "\n"; - - // Next, recursively merge all outgoing links as necessary. Note that - // adding these links can cause the destination node to collapse itself at - // any time, and the current node may be merged with arbitrary other nodes. - // For this reason, we must always go through NH. - DN = 0; - for (DSNode::const_edge_iterator ii = SN->edge_begin(), ee = SN->edge_end(); - ii != ee; ++ii) { - const DSNodeHandle &SrcEdge = ii->second; - if (!SrcEdge.isNull()) { - // Compute the offset into the current node at which to - // merge this link. In the common case, this is a linear - // relation to the offset in the original node (with - // wrapping), but if the current node gets collapsed due to - // recursive merging, we must make sure to merge in all remaining - // links at offset zero. - DSNode *CN = SCNH.getNode(); - unsigned MergeOffset = (ii->first+SCNH.getOffset()) % CN->getSize(); - - DSNodeHandle Tmp = CN->getLink(MergeOffset); - if (!Tmp.isNull()) { - // Perform the recursive merging. Make sure to create a temporary NH, - // because the Link can disappear in the process of recursive merging. - merge(Tmp, SrcEdge); - } else { - Tmp.mergeWith(getClonedNH(SrcEdge)); - // Merging this could cause all kinds of recursive things to happen, - // culminating in the current node being eliminated. Since this is - // possible, make sure to reaquire the link from 'CN'. - - unsigned MergeOffset = 0; - CN = SCNH.getNode(); - MergeOffset = (ii->first + SCNH.getOffset()) % CN->getSize(); - CN->getLink(MergeOffset).mergeWith(Tmp); - } - } - } -} - -/// mergeCallSite - Merge the nodes reachable from the specified src call -/// site into the nodes reachable from DestCS. -void ReachabilityCloner::mergeCallSite(DSCallSite &DestCS, - const DSCallSite &SrcCS) { - merge(DestCS.getRetVal(), SrcCS.getRetVal()); - merge(DestCS.getVAVal(), SrcCS.getVAVal()); - unsigned MinArgs = DestCS.getNumPtrArgs(); - if (SrcCS.getNumPtrArgs() < MinArgs) MinArgs = SrcCS.getNumPtrArgs(); - - for (unsigned a = 0; a != MinArgs; ++a) { - DSMonitor M; - CallSite CS = SrcCS.getCallSite(); - Function* F = CS.getCalledFunction(); - std::string name = F ? F->getName() : "(unknown)"; - unsigned b = 0; - for (unsigned i=0, j=0; i(CS.getArgument(i)->getType())) - if (j++ == a) { - b = i; - break; - } - - M.watch(DestCS.getPtrArg(a), {SrcCS.getCallSite().getArgument(b)}, - "unable to merge call-site arguments with parameters to function " - + name - ); - merge(DestCS.getPtrArg(a), SrcCS.getPtrArg(a)); - M.check(); - } - - for (unsigned a = MinArgs, e = SrcCS.getNumPtrArgs(); a != e; ++a) { - // If a call site passes more params, ignore the extra params. - // If the called function is varargs, merge the extra params, with - // the varargs node. - if(DestCS.getVAVal() != NULL) { - merge(DestCS.getVAVal(), SrcCS.getPtrArg(a)); - } - } - - for (unsigned a = MinArgs, e = DestCS.getNumPtrArgs(); a!=e; ++a) { - // If a call site passes less explicit params, than the function needs - // But passes params through a varargs node, merge those in. - if(SrcCS.getVAVal() != NULL) { - merge(DestCS.getPtrArg(a), SrcCS.getVAVal()); - } - } -} - -DSCallSite ReachabilityCloner::cloneCallSite(const DSCallSite& SrcCS) { - std::vector Args; - for(unsigned x = 0; x < SrcCS.getNumPtrArgs(); ++x) - Args.push_back(getClonedNH(SrcCS.getPtrArg(x))); - if (SrcCS.isDirectCall()) - return DSCallSite(SrcCS.getCallSite(), - getClonedNH(SrcCS.getRetVal()), - getClonedNH(SrcCS.getVAVal()), - SrcCS.getCalleeFunc(), - Args); - else { - DSNodeHandle Ret = getClonedNH(SrcCS.getRetVal()), - VA = getClonedNH(SrcCS.getVAVal()), - Callee = getClonedNH(SrcCS.getCalleeNode()); - // Resolve forwarding now as much as possible. - Ret.getNode(); VA.getNode(); - // Most importantly, ensure the node passed to DSCallSite - // is not a forwarding node: - DSNode * CalleeN = Callee.getNode(); - return DSCallSite(SrcCS.getCallSite(), Ret, VA, CalleeN, Args); - } -} - -//===----------------------------------------------------------------------===// -// DSCallSite Implementation -//===----------------------------------------------------------------------===// - -// Define here to avoid including iOther.h and BasicBlock.h in DSGraph.h -const Function &DSCallSite::getCaller() const { - return *Site.getInstruction()->getParent()->getParent(); -} - -void DSCallSite::InitNH(DSNodeHandle &NH, const DSNodeHandle &Src, - ReachabilityCloner &RC) { - NH = RC.getClonedNH(Src); -} - -/// FunctionTypeOfCallSite - Helper method to extract the signature of a function -/// that is called a given CallSite -/// -const FunctionType *DSCallSite::FunctionTypeOfCallSite(const CallSite & Site) { - Value *Callee = Site.getCalledValue(); - - // Direct call, simple - if (Function *F = dyn_cast(Callee)) - return F->getFunctionType(); - - // Indirect call, extract the type - const FunctionType *CalleeFuncType = NULL; - - const PointerType *CalleeType = dyn_cast(Callee->getType()); - if (!CalleeType) { - llvm_unreachable("Call through a non-pointer type?"); - } else { - CalleeFuncType = dyn_cast(CalleeType->getElementType()); - assert(CalleeFuncType && - "Call through pointer to non-function?"); - } - - return CalleeFuncType; -} - -/// isVarArg - Determines if the call this represents is to a variable argument -/// function -/// -bool DSCallSite::isVarArg() const { - const FunctionType *FT = FunctionTypeOfCallSite(Site); - return FT->isVarArg(); -} - -/// isUnresolvable - Determines if this call has properties that would -/// prevent it from ever being resolvded. Put another way, no amount -/// additional information will make this callsite resolvable. -/// -bool DSCallSite::isUnresolvable() const { - // Direct calls are forever unresolvable if they are calls to declarations. - if (isDirectCall()) - return getCalleeFunc()->isDeclaration(); - // Indirect calls are forever unresolvable if the call node is marked - // external. - // (Nodes can't become non-external through additional information) - return getCalleeNode()->isExternFuncNode(); -} - -/// remapLinks - Change all of the Links in the current node according to the -/// specified mapping. -/// -void DSNode::remapLinks(DSGraph::NodeMapTy &OldNodeMap) { - for (LinkMapTy::iterator ii = edge_begin(), ee = edge_end(); - ii != ee; ++ii) - if (DSNode *N = ii->second.getNode()) { - DSGraph::NodeMapTy::const_iterator ONMI = OldNodeMap.find(N); - if (ONMI != OldNodeMap.end()) { - DSNode *ONMIN = ONMI->second.getNode(); - ii->second.setTo(ONMIN, ii->second.getOffset()+ONMI->second.getOffset()); - } - } -} - -/// markReachableNodes - This method recursively traverses the specified -/// DSNodes, marking any nodes which are reachable. All reachable nodes it adds -/// to the set, which allows it to only traverse visited nodes once. -/// -void DSNode::markReachableNodes(DenseSet &ReachableNodes) const { - // warning: 'this' pointer cannot be null in well-defined C++ code; - // comparison may be assumed to always evaluate to false - // [-Wtautological-undefined-compare] - if (this == ((void*) 0)) return; - assert(!isForwarding() && "Cannot mark a forwarded node!"); - if (ReachableNodes.insert(this).second) // Is newly reachable? - for (DSNode::const_edge_iterator I = edge_begin(), E = edge_end(); - I != E; ++I) - I->second.getNode()->markReachableNodes(ReachableNodes); -} - -void DSCallSite::markReachableNodes(DenseSet &Nodes) const { - getRetVal().getNode()->markReachableNodes(Nodes); - getVAVal().getNode()->markReachableNodes(Nodes); - if (isIndirectCall()) getCalleeNode()->markReachableNodes(Nodes); - - for (unsigned i = 0, e = getNumPtrArgs(); i != e; ++i) - getPtrArg(i).getNode()->markReachableNodes(Nodes); -} - -//////////////////////////////////////////////////////////////////////////////// -//Base DataStructures impl: -//////////////////////////////////////////////////////////////////////////////// - - static const Function *getFnForValue(const Value *V) { - if (const Instruction *I = dyn_cast(V)) - return I->getParent()->getParent(); - else if (const Argument *A = dyn_cast(V)) - return A->getParent(); - else if (const BasicBlock *BB = dyn_cast(V)) - return BB->getParent(); - return 0; - } - -/// deleteValue/copyValue - Interfaces to update the DSGraphs in the program. -/// These correspond to the interfaces defined in the AliasAnalysis class. -/// FIXME: Do these update all the datastructures needed? -/// FIXME: What exactly does it mean to tell DSA to 'copy' a value? or delete it? (particularly a function) -void DataStructures::deleteValue(Value *V) { - if (const Function *F = getFnForValue(V)) { // Function local value? - // If this is a function local value, just delete it from the scalar map! - getDSGraph(*F)->getScalarMap().eraseIfExists(V); - return; - } - - if (Function *F = dyn_cast(V)) { - DSGraph *G = getDSGraph(*F); - if (G->getReturnNodes().size() == 1) { - // If this is function is part of its own SCC, just delete the graph for it - delete G; - DSInfo.erase(F); - } else { - // SCC case - - // Remove some of the graph's information about this function since it's no longer needed - G->getReturnNodes().erase(F); - G->getVANodes().erase(F); - - // Remove entry for the function, but don't delete the graph since others need it - DSInfo.erase(F); - - // FIXME: Can more be done here? Is there a good way to remove from the SCC's graph more - // of the information this function contributed? - - } - - return; - } - - assert(!isa(V) && "Do not know how to delete GV's yet!"); - - llvm_unreachable("Unrecognized value!"); - abort(); -} - -void DataStructures::copyValue(Value *From, Value *To) { - if (From == To) return; - if (const Function *F = getFnForValue(From)) { // Function local value? - // If this is a function local value, just delete it from the scalar map! - getDSGraph(*F)->getScalarMap().copyScalarIfExists(From, To); - return; - } - - if (Function *FromF = dyn_cast(From)) { - Function *ToF = cast(To); - assert(!DSInfo.count(ToF) && "New Function already exists!"); - DSGraph *G = getDSGraph(*FromF); - if (G->getReturnNodes().size() == 1) { - // Copy a single function by duplicating its dsgraph - - DSGraph *NG = new DSGraph(getDSGraph(*FromF), GlobalECs, *TypeSS); - DSInfo[ToF] = NG; - - // Change the Function* is the returnnodes map to the ToF. - DSNodeHandle Ret = NG->retnodes_begin()->second; - NG->getReturnNodes().clear(); - NG->getReturnNodes()[ToF] = Ret; - - // Change the Function* in the vanodes map to the ToF - DSNodeHandle VA = NG->vanodes_begin()->second; - NG->getVANodes().clear(); - NG->getVANodes()[ToF] = VA; - } else { - // A copy request on a function that's part of an SCC, we just map the new function to the same information - - // G is the graph for ToF as well (add it to the SCC) - setDSGraph(*ToF,G); - - // Map ToF to the same return/va nodes as FromF - G->getReturnNodes()[ToF] = G->getReturnNodes()[FromF]; - G->getVANodes()[ToF] = G->getVANodes()[FromF]; - } - - return; - } - - if (const Function *F = getFnForValue(To)) { - getDSGraph(*F)->getScalarMap().copyScalarIfExists(From, To); - return; - } - - errs() << *From; - errs() << *To; - llvm_unreachable("Do not know how to copy this yet!"); - abort(); -} - -DSGraph* DataStructures::getOrCreateGraph(const Function* F) { - assert(F && "No function"); - DSGraph *&G = DSInfo[F]; - if (!G) { - assert (F->isDeclaration() || GraphSource->hasDSGraph(*F)); - //Clone or Steal the Source Graph - DSGraph* BaseGraph = GraphSource->getDSGraph(*F); - if (Clone) { - G = new DSGraph(BaseGraph, GlobalECs, *TypeSS); - if (resetAuxCalls) - G->getAuxFunctionCalls() = G->getFunctionCalls(); - } else { - G = new DSGraph(GlobalECs, GraphSource->getDataLayout(), *TypeSS); - G->spliceFrom(BaseGraph); - if (resetAuxCalls) - G->getAuxFunctionCalls() = G->getFunctionCalls(); - } - G->setUseAuxCalls(); - G->setGlobalsGraph(GlobalsGraph); - - // Note that this graph is the graph for ALL of the function in the SCC, not - // just F. - for (DSGraph::retnodes_iterator RI = G->retnodes_begin(), - E = G->retnodes_end(); RI != E; ++RI) - if (RI->first != F) - DSInfo[RI->first] = G; - } - return G; -} - -void DataStructures::formGlobalFunctionList() { - std::vector List; - DSScalarMap &SN = GlobalsGraph->getScalarMap(); - EquivalenceClasses &EC = GlobalsGraph->getGlobalECs(); - for (DSScalarMap::global_iterator I = SN.global_begin(), E = SN.global_end(); I != E; ++I) { - EquivalenceClasses::iterator ECI = EC.findValue(*I); - if (ECI == EC.end()) { - if (const Function *F = dyn_cast(*I)) - List.push_back(F); - } else { - for (EquivalenceClasses::member_iterator MI = - EC.member_begin(ECI), ME = EC.member_end(); MI != ME; ++MI){ - if (const Function *F = dyn_cast(*MI)) - List.push_back(F); - } - } - } - GlobalFunctionList.swap(List); -} - - -void DataStructures::formGlobalECs() { - // Grow the equivalence classes for the globals to include anything that we - // now know to be aliased. - svset ECGlobals; - buildGlobalECs(ECGlobals); - if (!ECGlobals.empty()) { - SDEBUG(errs() << "Eliminating " << ECGlobals.size() << " EC Globals!\n"); - for (DSInfoTy::iterator I = DSInfo.begin(), - E = DSInfo.end(); I != E; ++I) - eliminateUsesOfECGlobals(*I->second, ECGlobals); - } -} - -/// BuildGlobalECs - Look at all of the nodes in the globals graph. If any node -/// contains multiple globals, DSA will never, ever, be able to tell the globals -/// apart. Instead of maintaining this information in all of the graphs -/// throughout the entire program, store only a single global (the "leader") in -/// the graphs, and build equivalence classes for the rest of the globals. -void DataStructures::buildGlobalECs(svset &ECGlobals) { - DSScalarMap &SM = GlobalsGraph->getScalarMap(); - EquivalenceClasses &GlobalECs = SM.getGlobalECs(); - for (DSGraph::node_iterator I = GlobalsGraph->node_begin(), - E = GlobalsGraph->node_end(); - I != E; ++I) { - if (I->numGlobals() <= 1) continue; - - // First, build up the equivalence set for this block of globals. - DSNode::globals_iterator i = I->globals_begin(); - const GlobalValue *First = *i; - if (GlobalECs.findValue(*i) != GlobalECs.end()) - First = GlobalECs.getLeaderValue(*i); - if (*i == First) ++i; - for( ; i != I->globals_end(); ++i) { - GlobalECs.unionSets(First, *i); - ECGlobals.insert(*i); - if (SM.find(*i) != SM.end()) - SM.erase(SM.find(*i)); - else - errs() << "Global missing in scalar map " << (*i)->getName() << "\n"; - } - - // Next, get the leader element. - assert(First == GlobalECs.getLeaderValue(First) && - "First did not end up being the leader?"); - - // Finally, change the global node to only contain the leader. - I->clearGlobals(); - I->addGlobal(First); - } - - SDEBUG(GlobalsGraph->AssertGraphOK()); -} - -/// EliminateUsesOfECGlobals - Once we have determined that some globals are in -/// really just equivalent to some other globals, remove the globals from the -/// specified DSGraph (if present), and merge any nodes with their leader nodes. -void DataStructures::eliminateUsesOfECGlobals(DSGraph &G, - const svset &ECGlobals) { - DSScalarMap &SM = G.getScalarMap(); - EquivalenceClasses &GlobalECs = SM.getGlobalECs(); - -#ifndef NDEBUG - bool MadeChange = false; -#endif - std::vector SMGVV(SM.global_begin(), SM.global_end()); - - for (std::vector::iterator GI = SMGVV.begin(), - E = SMGVV.end(); GI != E; ) { - const GlobalValue *GV = *GI; ++GI; - if (!ECGlobals.count(GV)) continue; - - const DSNodeHandle &GVNH = SM[GV]; - assert(!GVNH.isNull() && "Global has null NH!?"); - - // Okay, this global is in some equivalence class. Start by finding the - // leader of the class. - const GlobalValue *Leader = GlobalECs.getLeaderValue(GV); - - // If the leader isn't already in the graph, insert it into the node - // corresponding to GV. - if (!SM.global_count(Leader)) { - GVNH.getNode()->addGlobal(Leader); - SM[Leader] = GVNH; - } else { - // Otherwise, the leader is in the graph, make sure the nodes are the - // merged in the specified graph. - const DSNodeHandle &LNH = SM[Leader]; - if (LNH.getNode() != GVNH.getNode()) - LNH.mergeWith(GVNH); - } - - // Next step, remove the global from the DSNode. - GVNH.getNode()->removeGlobal(GV); - - // Finally, remove the global from the ScalarMap. - SM.erase(GV); -#ifndef NDEBUG - MadeChange = true; -#endif - } - - SDEBUG(if(MadeChange) G.AssertGraphOK()); -} - -//For Entry Points -void DataStructures::cloneGlobalsInto(DSGraph* Graph, unsigned cloneFlags) { - // If this graph contains main, copy the contents of the globals graph over. - // Note that this is *required* for correctness. If a callee contains a use - // of a global, we have to make sure to link up nodes due to global-argument - // bindings. - const DSGraph* GG = Graph->getGlobalsGraph(); - ReachabilityCloner RC(Graph, GG, cloneFlags); - - // Clone the global nodes into this graph. - for (DSScalarMap::global_iterator I = Graph->getScalarMap().global_begin(), - E = Graph->getScalarMap().global_end(); I != E; ++I) - RC.getClonedNH(GG->getNodeForValue(*I)); -} - -//For all graphs -void DataStructures::cloneIntoGlobals(DSGraph* Graph, unsigned cloneFlags) { - // When this graph is finalized, clone the globals in the graph into the - // globals graph to make sure it has everything, from all graphs. - DSScalarMap &MainSM = Graph->getScalarMap(); - ReachabilityCloner RC(GlobalsGraph, Graph, cloneFlags); - - // Clone everything reachable from globals in the function graph into the - // globals graph. - for (DSScalarMap::global_iterator I = MainSM.global_begin(), - E = MainSM.global_end(); I != E; ++I) - RC.getClonedNH(MainSM[*I]); -} - - -void DataStructures::init(DataStructures* D, bool clone, bool useAuxCalls, - bool copyGlobalAuxCalls, bool resetAux) { - assert (!GraphSource && "Already init"); - GraphSource = D; - Clone = clone; - resetAuxCalls = resetAux; - TD = D->TD; - TypeSS = D->TypeSS; - callgraph = D->callgraph; - GlobalFunctionList = D->GlobalFunctionList; - GlobalECs = D->getGlobalECs(); - GlobalsGraph = new DSGraph(D->getGlobalsGraph(), GlobalECs, *TypeSS, - copyGlobalAuxCalls? DSGraph::CloneAuxCallNodes - :DSGraph::DontCloneAuxCallNodes); - if (useAuxCalls) GlobalsGraph->setUseAuxCalls(); - - // - // Tell the other DSA pass if we're stealing its graph. - // - if (!clone) D->DSGraphsStolen = true; -} - -void DataStructures::init(const DataLayout* T) { - assert (!TD && "Already init"); - GraphSource = 0; - Clone = false; - TD = T; - TypeSS = new SuperSet(); - GlobalsGraph = new DSGraph(GlobalECs, *T, *TypeSS); -} - -// CBU has the correct call graph. All the passes that follow it -// must resotre the call graph, at the end, so that it it correct. -// This is simpler than keeping all the CBU data structures around. -// EQBU and subsequent passes must call this. -void DataStructures::restoreCorrectCallGraph(){ - callgraph = GraphSource->callgraph; -} - -void DataStructures::releaseMemory() { - // - // If the DSGraphs were stolen by another pass, free nothing. - // - if (DSGraphsStolen) return; - - std::set toDelete; - for (DSInfoTy::iterator I = DSInfo.begin(), E = DSInfo.end(); I != E; ++I) { - I->second->getReturnNodes().clear(); - toDelete.insert(I->second); - } - for (std::set::iterator I = toDelete.begin(), E = toDelete.end(); I != E; ++I) - delete *I; - - // Empty map so next time memory is released, data structures are not - // re-deleted. - DSInfo.clear(); - - delete GlobalsGraph; - GlobalsGraph = 0; -} diff --git a/lib/DSA/DataStructureStats.cpp b/lib/DSA/DataStructureStats.cpp deleted file mode 100644 index 997f24b5d..000000000 --- a/lib/DSA/DataStructureStats.cpp +++ /dev/null @@ -1,245 +0,0 @@ -//===- DataStructureStats.cpp - Various statistics for DS Graphs ----------===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines a little pass that prints out statistics for DS Graphs. -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "DSStats" -#include "dsa/DataStructure.h" -#include "dsa/DSGraph.h" -#include "dsa/TypeSafety.h" - -#include "llvm/IR/Constants.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/InstVisitor.h" -#include "llvm/Pass.h" -#include "llvm/ADT/Statistic.h" -#include "smack/Debug.h" -#include "llvm/Support/FormattedStream.h" - -#include -using namespace llvm; - -namespace { - STATISTIC (TotalNumCallees, - "Total number of callee functions at all indirect call sites"); - STATISTIC (NumIndirectCalls, - "Total number of indirect call sites in the program"); - - // Typed/Untyped memory accesses: If DSA can infer that the types the loads - // and stores are accessing are correct (ie, the node has not been collapsed), - // increment the appropriate counter. - STATISTIC (NumTypedMemAccesses, - "Number of loads/stores which are fully typed"); - STATISTIC (NumUntypedMemAccesses, - "Number of loads/stores which are untyped"); - STATISTIC (NumTypeCount0Accesses, - "Number of loads/stores which are access a DSNode with 0 type"); - STATISTIC (NumTypeCount1Accesses, - "Number of loads/stores which are access a DSNode with 1 type"); - STATISTIC (NumTypeCount2Accesses, - "Number of loads/stores which are access a DSNode with 2 type"); - STATISTIC (NumTypeCount3Accesses, - "Number of loads/stores which are access a DSNode with 3 type"); - STATISTIC (NumTypeCount4Accesses, - "Number of loads/stores which are access a DSNode with >3 type"); - STATISTIC (NumIncompleteAccesses, - "Number of loads/stores which are on incomplete nodes"); - STATISTIC (NumUnknownAccesses, - "Number of loads/stores which are on unknown nodes"); - STATISTIC (NumExternalAccesses, - "Number of loads/stores which are on external nodes"); - STATISTIC (NumI2PAccesses, - "Number of loads/stores which are on inttoptr nodes"); - STATISTIC (NumFoldedAccess, - "Number of loads/stores which are on folded nodes"); - - class DSGraphStats : public FunctionPass, public InstVisitor { - void countCallees(const Function &F); - const TDDataStructures *DS; - const DataLayout *TD; - const DSGraph *TDGraph; - dsa::TypeSafety *TS; - DSNodeHandle getNodeHandleForValue(Value *V); - bool isNodeForValueUntyped(Value *V, unsigned offset, const Function *); - public: - static char ID; - DSGraphStats() : FunctionPass(ID) {} - - /// Driver functions to compute the Load/Store Dep. Graph per function. - bool runOnFunction(Function& F); - - /// getAnalysisUsage - This modify nothing, and uses the Top-Down Graph. - void getAnalysisUsage(AnalysisUsage &AU) const { - AU.setPreservesAll(); - AU.addRequired(); - AU.addRequired >(); - } - - void visitLoad(LoadInst &LI); - void visitStore(StoreInst &SI); - - /// Debugging support methods - void print(llvm::raw_ostream &O, const Module* = 0) const { } - }; - - static RegisterPass Z("dsstats", "DS Graph Statistics"); -} - -char DSGraphStats::ID; - -FunctionPass *llvm::createDataStructureStatsPass() { - return new DSGraphStats(); -} - - -static bool isIndirectCallee(Value *V) { - if (isa(V)) return false; - - if (CastInst *CI = dyn_cast(V)) - return isIndirectCallee(CI->getOperand(0)); - - if (ConstantExpr *CE = dyn_cast(V)) - if (CE->isCast()) - return isIndirectCallee(CE->getOperand(0)); - - if (GlobalAlias *GA = dyn_cast(V)) { - return isIndirectCallee(GA->getAliasee()); - } - return true; -} - - -void DSGraphStats::countCallees(const Function& F) { - const DSCallGraph callgraph = DS->getCallGraph(); - unsigned numIndirectCalls = 0, totalNumCallees = 0; - - for (DSGraph::fc_iterator I = TDGraph->fc_begin(), E = TDGraph->fc_end(); - I != E; ++I) - if (isIndirectCallee(I->getCallSite().getCalledValue())) { - // This is an indirect function call - std::vector Callees; - callgraph.addFullFunctionList(I->getCallSite(), Callees); - - if (Callees.size() > 0) { - totalNumCallees += Callees.size(); - ++numIndirectCalls; - } else { - SDEBUG(errs() << "WARNING: No callee in Function '" - << F.getName().str() << " at call: \n" - << *I->getCallSite().getInstruction()); - } - } - - TotalNumCallees += totalNumCallees; - NumIndirectCalls += numIndirectCalls; - - if (numIndirectCalls) { - SDEBUG(errs() << " In function " << F.getName() << ": " - << (totalNumCallees / (double) numIndirectCalls) - << " average callees per indirect call\n"); - } -} - -DSNodeHandle DSGraphStats::getNodeHandleForValue(Value *V) { - const DSGraph *G = TDGraph; - const DSGraph::ScalarMapTy &ScalarMap = G->getScalarMap(); - DSGraph::ScalarMapTy::const_iterator I = ScalarMap.find(V); - if (I != ScalarMap.end()) - return I->second; - - G = TDGraph->getGlobalsGraph(); - const DSGraph::ScalarMapTy &GlobalScalarMap = G->getScalarMap(); - I = GlobalScalarMap.find(V); - if (I != GlobalScalarMap.end()) - return I->second; - - return 0; -} - -bool DSGraphStats::isNodeForValueUntyped(Value *V, unsigned Offset, const Function *F) { - DSNodeHandle NH = getNodeHandleForValue(V); - if(!NH.getNode()){ - return true; - } - else { - DSNode *N = NH.getNode(); - if (N->isNodeCompletelyFolded()){ - ++NumFoldedAccess; - return true; - } - if ( N->isExternalNode()){ - ++NumExternalAccesses; - return true; - } - if ( N->isIncompleteNode()){ - ++NumIncompleteAccesses; - return true; - } - if (N->isUnknownNode()){ - ++NumUnknownAccesses; - return true; - } - if (N->isIntToPtrNode()){ - ++NumI2PAccesses; - return true; - } - // it is a complete node, now check how many types are present - int count = 0; - unsigned offset = NH.getOffset() + Offset; - if (N->type_begin() != N->type_end()) - for (DSNode::TyMapTy::const_iterator ii = N->type_begin(), - ee = N->type_end(); ii != ee; ++ii) { - if(ii->first != offset) - continue; - count += ii->second->size(); - } - - if (count ==0) - ++NumTypeCount0Accesses; - else if(count == 1) - ++NumTypeCount1Accesses; - else if(count == 2) - ++NumTypeCount2Accesses; - else if(count == 3) - ++NumTypeCount3Accesses; - else - ++NumTypeCount4Accesses; - SDEBUG(assert(TS->isTypeSafe(V,F))); - } - return false; -} - -void DSGraphStats::visitLoad(LoadInst &LI) { - if (isNodeForValueUntyped(LI.getOperand(0), 0,LI.getParent()->getParent())) { - NumUntypedMemAccesses++; - } else { - NumTypedMemAccesses++; - } -} - -void DSGraphStats::visitStore(StoreInst &SI) { - if (isNodeForValueUntyped(SI.getOperand(1), 0,SI.getParent()->getParent())) { - NumUntypedMemAccesses++; - } else { - NumTypedMemAccesses++; - } -} - -bool DSGraphStats::runOnFunction(Function& F) { - DS = &getAnalysis(); - TD = &F.getParent()->getDataLayout(); - TS = &getAnalysis >(); - TDGraph = DS->getDSGraph(F); - countCallees(F); - visit(F); - return false; -} diff --git a/lib/DSA/EntryPointAnalysis.cpp b/lib/DSA/EntryPointAnalysis.cpp deleted file mode 100644 index 07399e6b4..000000000 --- a/lib/DSA/EntryPointAnalysis.cpp +++ /dev/null @@ -1,102 +0,0 @@ -//===-- EntryPointAnalysis.cpp - Entry point Finding Pass -----------------===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This is a general way of finding entry points in a system. Simple programs -// will use the main version. Libraries and OS kernels can have more -// specialized versions. This is done as an analysis group to allow more -// convinient opt invocations. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Pass.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Function.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/FormattedStream.h" -#include "smack/Debug.h" - -#include -#include - -#include "dsa/EntryPointAnalysis.h" - -using namespace llvm; - -static cl::opt epaFile("epa-file", - cl::desc("File with entry point names")); //, cl::ReallyHidden); - -static void readNames(std::set& names) { - std::ifstream msf(epaFile.c_str(), std::ifstream::in); - if (!msf.good()) - errs() << "Failed to open file: " << epaFile << " (continuing anyway)\n"; - while (msf.good()) { - std::string n; - msf >> n; - if (n.size()) { - names.insert(n); -// errs() << "Read " << n << "\n"; - } - } -} - - -EntryPointAnalysis::EntryPointAnalysis() :ModulePass(ID), haveNames(false) { -} - -EntryPointAnalysis::~EntryPointAnalysis() {} - -void EntryPointAnalysis::findEntryPoints(const Module& M, - std::vector& dest) const { - for (const Function &F : M) - if (isEntryPoint(&F)) - dest.push_back(&F); -} - -void EntryPointAnalysis::print(llvm::raw_ostream& O, const Module* M) const { - std::vector d; - findEntryPoints(*M, d); - O << "EntryPoints: "; - bool prev = false; - for (std::vector::iterator ii = d.begin(), ee = d.end(); - ii != ee; ++ii) { - O << (prev ? ", " : "") << (*ii)->getName().str(); - prev = true; - } - O << "\n"; -} - -bool EntryPointAnalysis::runOnModule(llvm::Module& M) { - if (epaFile.size()) { - haveNames = true; - readNames(names); - } - return false; -} - -void EntryPointAnalysis::getAnalysisUsage(llvm::AnalysisUsage &AU) const { - AU.setPreservesAll(); -} - -bool EntryPointAnalysis::isEntryPoint(const llvm::Function* F) const { - if (haveNames) { - return !F->isDeclaration() - && F->hasExternalLinkage() - && F->hasName() - && names.find(F->getName().str()) != names.end(); - } else { - return !F->isDeclaration() - && F->hasExternalLinkage() - && F->hasName() && F->getName() == "main"; - } -} - - - -char EntryPointAnalysis::ID; -static RegisterPass A("epa", "Identify EntryPoints"); diff --git a/lib/DSA/EquivClassGraphs.cpp b/lib/DSA/EquivClassGraphs.cpp deleted file mode 100644 index dcc9dcdba..000000000 --- a/lib/DSA/EquivClassGraphs.cpp +++ /dev/null @@ -1,257 +0,0 @@ -//===- EquivClassGraphs.cpp - Merge equiv-class graphs & inline bottom-up -===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This pass is the same as the complete bottom-up graphs, but -// with functions partitioned into equivalence classes and a single merged -// DS graph for all functions in an equivalence class. After this merging, -// graphs are inlined bottom-up on the SCCs of the final (CBU) call graph. -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "ECGraphs" -#include "dsa/DataStructure.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/Module.h" -#include "llvm/Pass.h" -#include "dsa/DSGraph.h" -#include "llvm/IR/CallSite.h" -#include "smack/Debug.h" -#include "llvm/ADT/SCCIterator.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/ADT/EquivalenceClasses.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/Support/FormattedStream.h" -#include -using namespace llvm; - -namespace { - RegisterPass X("dsa-eq", - "Equivalence-class Bottom-up Data Structure Analysis"); -} -char EquivBUDataStructures::ID = 0; - -// runOnModule - Calculate the bottom up data structure graphs for each function -// in the program. -// -bool EquivBUDataStructures::runOnModule(Module &M) { - init(&getAnalysis(), true, true, false, true); - - //make a list of all the DSGraphs - std::setgraphList; - for(Function &F : M) - { - if(!(F.isDeclaration())) - graphList.insert(getOrCreateGraph(&F)); - } - - //update the EQ class from indirect calls - buildIndirectFunctionSets(); - formGlobalECs(); - mergeGraphsByGlobalECs(); - - //remove all the DSGraph, that still have references - for(Function &F : M) - { - if(!(F.isDeclaration())) - graphList.erase(getOrCreateGraph(&F)); - } - // free memory for the DSGraphs, no longer in use. - for(std::set::iterator i = graphList.begin(),e = graphList.end(); - i!=e;i++) { - delete (*i); - } - SDEBUG(verifyMerging()); - - formGlobalECs(); - - for (Function &F: M) { - if (!(F.isDeclaration())) { - if (DSGraph * Graph = getOrCreateGraph(&F)) { - cloneGlobalsInto(Graph, DSGraph::DontCloneCallNodes | - DSGraph::DontCloneAuxCallNodes); - } - } - } - - bool result = runOnModuleInternal(M); - - // CBU contains the correct call graph. - // Restore it, so that subsequent passes and clients can get it. - // TODO (Zvonimir): There is strong indication that EquivBUDataStructures - // in fact produces a (more) correct call graph than CBU. So restoring - // call graph here produces wrong results on a class of SVCOMP benchmarks. - // It also appears that not restoring call graph here does not introduce - // any new issues. - // restoreCorrectCallGraph(); - return result; -} - -// Verifies that all the functions in an equivalence calss have been merged. -// This is required by to be true by poolallocation. -void -EquivBUDataStructures::verifyMerging() { - - EquivalenceClasses::iterator EQSI = GlobalECs.begin(); - EquivalenceClasses::iterator EQSE = GlobalECs.end(); - for (;EQSI != EQSE; ++EQSI) { - if (!EQSI->isLeader()) continue; - EquivalenceClasses::member_iterator MI; - bool first = true; - DSGraph *firstG = 0; - for (MI = GlobalECs.member_begin(EQSI); MI != GlobalECs.member_end(); ++MI){ - if (const Function* F = dyn_cast(*MI)){ - if (F->isDeclaration()) - continue; - - if(first) { - firstG = getOrCreateGraph(F); - first = false; - } - DSGraph *G = getOrCreateGraph(F); - if( G != firstG) { - assert(G == firstG && "all functions in a Global EC do not have a merged graph"); - } - } - } - } -} - -// -// Method: mergeGraphsByGlobalECs() -// -// Description: -// Merge all graphs that are in the same equivalence class. This ensures -// that transforms like Automatic Pool Allocation only see one graph for a -// call site. -// -// After this method is executed, all functions in an equivalence class will -// have the *same* DSGraph. -// -void -EquivBUDataStructures::mergeGraphsByGlobalECs() { - // - // Merge the graphs for each equivalence class. We first scan all elements - // in the equivalence classes and look for those elements which are leaders. - // For each leader, we scan through all of its members and merge the DSGraphs - // for members which are functions. - // - - EquivalenceClasses::iterator EQSI = GlobalECs.begin(); - EquivalenceClasses::iterator EQSE = GlobalECs.end(); - for (;EQSI != EQSE; ++EQSI) { - // - // If this element is not a leader, then skip it. - // - if (!EQSI->isLeader()) continue; - DSGraph* BaseGraph = 0; - std::vector Args; - - // - // Iterate through all members of this equivalence class, looking for - // functions. - // - EquivalenceClasses::member_iterator MI; - for (MI = GlobalECs.member_begin(EQSI); MI != GlobalECs.member_end(); ++MI){ - if (const Function* F = dyn_cast(*MI)) { - // - // If the function has no body, then it has no DSGraph. - // - // FIXME: I don't believe this is correct; the stdlib pass can assign - // DSGraphs to C standard library functions. - // - if (F->isDeclaration()) - continue; - - // - // We have one of three possibilities: - // 1) This is the first function we've seen. If so, grab its DSGraph - // and the DSNodes for its arguments. - // - // 2) We have already seen this function before. Do nothing. - // - // 3) We haven't seen this function before, and it's not the first one - // we've seen. Merge its DSGraph into the DSGraph we're creating. - // - if (!BaseGraph) { - BaseGraph = getOrCreateGraph(F); - BaseGraph->getFunctionArgumentsForCall(F, Args); - } else if (BaseGraph->containsFunction(F)) { - // - // The DSGraph for this function has already been merged into the - // graph that we are creating. However, that does not mean that - // function arguments of this function have been merged with the - // function arguments of the other functions in the equivalence graph - // (or even with functions belonging to the same SCC in the call - // graph). Furthermore, it doesn't necessarily imply that the - // contained function's DSGraph is the same as the one we're - // building; it is possible (I think) for only the function's DSNodes - // and other information to have been merged in. - // - // For these reasons, we will merge the function argument DSNodes and - // set this function's DSGraph to be the same graph used for all - // other function's in this equivalence class. - // - - // - // Merge the arguments together. - // - std::vector NextArgs; - BaseGraph->getFunctionArgumentsForCall(F, NextArgs); - unsigned i = 0, e = Args.size(); - for (; i != e; ++i) { - if (i == NextArgs.size()) break; - Args[i].mergeWith(NextArgs[i]); - } - for (e = NextArgs.size(); i != e; ++i) - Args.push_back(NextArgs[i]); - - // - // Make this function use the DSGraph that we're creating for all of - // the functions in this equivalence class. - // - setDSGraph(*F, BaseGraph); - } else { - // - // Merge in the DSGraph. - // - BaseGraph->cloneInto(getOrCreateGraph(F)); - - // - // Merge the arguments together. - // - std::vector NextArgs; - BaseGraph->getFunctionArgumentsForCall(F, NextArgs); - unsigned i = 0, e = Args.size(); - for (; i != e; ++i) { - if (i == NextArgs.size()) break; - Args[i].mergeWith(NextArgs[i]); - } - for (e = NextArgs.size(); i != e; ++i) - Args.push_back(NextArgs[i]); - - // - // Make this function use the DSGraph that we're creating for all of - // the functions in this equivalence class. - // - setDSGraph(*F, BaseGraph); - } - } - } - - // - // Update the globals graph with any information that has changed due to - // graph merging. - // - if (BaseGraph) - cloneIntoGlobals(BaseGraph, DSGraph::DontCloneCallNodes | - DSGraph::DontCloneAuxCallNodes | - DSGraph::StripAllocaBit); - } -} - diff --git a/lib/DSA/GraphChecker.cpp b/lib/DSA/GraphChecker.cpp deleted file mode 100644 index 0dafbadba..000000000 --- a/lib/DSA/GraphChecker.cpp +++ /dev/null @@ -1,207 +0,0 @@ -//===- GraphChecker.cpp - Assert that various graph properties hold -------===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This pass is used to test DSA with regression tests. It can be used to check -// that certain graph properties hold, such as two nodes being disjoint, whether -// or not a node is collapsed, etc. These are the command line arguments that -// it supports: -// -// --dsgc-dspass={local,bu,td} - Specify what flavor of graph to check -// --dsgc-abort-if-any-collapsed - Abort if any collapsed nodes are found -// --dsgc-abort-if-collapsed= - Abort if a node pointed to by an SSA -// value with name in is collapsed -// --dsgc-check-flags= - Abort if the specified nodes have flags -// that are not specified. -// --dsgc-abort-if-merged= - Abort if any of the named SSA values -// point to the same node. -// -//===----------------------------------------------------------------------===// - -#include "dsa/DataStructure.h" -#include "dsa/DSGraph.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/FormattedStream.h" -#include "llvm/IR/Value.h" -#include -using namespace llvm; - -namespace { - enum DSPass { local, bu, td }; - cl::opt - DSPass("dsgc-dspass", cl::Hidden, - cl::desc("Specify which DSA pass the -datastructure-gc pass should use"), - cl::values(clEnumVal(local, "Local pass"), - clEnumVal(bu, "Bottom-up pass"), - clEnumVal(td, "Top-down pass")), - cl::init(local)); - - cl::opt - AbortIfAnyCollapsed("dsgc-abort-if-any-collapsed", cl::Hidden, - cl::desc("Abort if any collapsed nodes are found")); - cl::list - AbortIfCollapsed("dsgc-abort-if-collapsed", cl::Hidden, cl::CommaSeparated, - cl::desc("Abort if any of the named symbols is collapsed")); - cl::list - CheckFlags("dsgc-check-flags", cl::Hidden, cl::CommaSeparated, - cl::desc("Check that flags are specified for nodes")); - cl::list - AbortIfMerged("dsgc-abort-if-merged", cl::Hidden, cl::CommaSeparated, - cl::desc("Abort if any of the named symbols are merged together")); - - struct DSGC : public FunctionPass { - static char ID; - DSGC(); - bool doFinalization(Module &M); - bool runOnFunction(Function &F); - - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - switch (DSPass) { - case local: AU.addRequired(); break; - case bu: AU.addRequired(); break; - case td: AU.addRequired(); break; - } - AU.setPreservesAll(); - } - void print(llvm::raw_ostream &O, const Module *M) const {} - - private: - void verify(const DSGraph* G); - }; - - RegisterPass X("datastructure-gc", "DSA Graph Checking Pass"); -} - -char DSGC::ID; - -FunctionPass *llvm::createDataStructureGraphCheckerPass() { - return new DSGC(); -} - - -DSGC::DSGC() : FunctionPass(ID) { - if (!AbortIfAnyCollapsed && AbortIfCollapsed.empty() && - CheckFlags.empty() && AbortIfMerged.empty()) { - errs() << "The -datastructure-gc is useless if you don't specify any" - << " -dsgc-* options. See the -help-hidden output for a list.\n"; - abort(); - } -} - - -/// doFinalization - Verify that the globals graph is in good shape... -/// -bool DSGC::doFinalization(Module &M) { - switch (DSPass) { - case local:verify(getAnalysis().getGlobalsGraph());break; - case bu: verify(getAnalysis().getGlobalsGraph()); break; - case td: verify(getAnalysis().getGlobalsGraph()); break; - } - return false; -} - -/// runOnFunction - Get the DSGraph for this function and verify that it is ok. -/// -bool DSGC::runOnFunction(Function &F) { - switch (DSPass) { - case local: verify(getAnalysis().getDSGraph(F)); break; - case bu: verify(getAnalysis().getDSGraph(F)); break; - case td: verify(getAnalysis().getDSGraph(F)); break; - } - - return false; -} - -/// verify - This is the function which checks to make sure that all of the -/// invariants established on the command line are true. -/// -void DSGC::verify(const DSGraph* G) { - // Loop over all of the nodes, checking to see if any are collapsed... - if (AbortIfAnyCollapsed) { - for (DSGraph::node_const_iterator I = G->node_begin(), E = G->node_end(); - I != E; ++I) - if (I->isNodeCompletelyFolded()) { - errs() << "Node is collapsed: "; - I->print(errs(), G); - abort(); - } - } - - if (!AbortIfCollapsed.empty() || !CheckFlags.empty() || - !AbortIfMerged.empty()) { - // Convert from a list to a set, because we don't have cl::set's yet. FIXME - std::set AbortIfCollapsedS(AbortIfCollapsed.begin(), - AbortIfCollapsed.end()); - std::set AbortIfMergedS(AbortIfMerged.begin(), - AbortIfMerged.end()); - std::map CheckFlagsM; - - for (cl::list::iterator I = CheckFlags.begin(), - E = CheckFlags.end(); I != E; ++I) { - std::string::size_type ColonPos = I->rfind(':'); - if (ColonPos == std::string::npos) { - errs() << "Error: '" << *I - << "' is an invalid value for the --dsgc-check-flags option!\n"; - abort(); - } - - unsigned Flags = 0; - for (unsigned C = ColonPos+1; C != I->size(); ++C) - switch ((*I)[C]) { - case 'S': Flags |= DSNode::AllocaNode; break; - case 'H': Flags |= DSNode::HeapNode; break; - case 'G': Flags |= DSNode::GlobalNode; break; - case 'U': Flags |= DSNode::UnknownNode; break; - case 'I': Flags |= DSNode::IncompleteNode; break; - case 'M': Flags |= DSNode::ModifiedNode; break; - case 'R': Flags |= DSNode::ReadNode; break; - case 'A': Flags |= DSNode::ArrayNode; break; - default: errs() << "Invalid DSNode flag!\n"; abort(); - } - CheckFlagsM[std::string(I->begin(), I->begin()+ColonPos)] = Flags; - } - - // Now we loop over all of the scalars, checking to see if any are collapsed - // that are not supposed to be, or if any are merged together. - const DSGraph::ScalarMapTy &SM = G->getScalarMap(); - std::map AbortIfMergedNodes; - - for (DSGraph::ScalarMapTy::const_iterator I = SM.begin(), E = SM.end(); - I != E; ++I) - if (I->first->hasName() && I->second.getNode()) { - const std::string &Name = I->first->getName(); - DSNode *N = I->second.getNode(); - - // Verify it is not collapsed if it is not supposed to be... - if (N->isNodeCompletelyFolded() && AbortIfCollapsedS.count(Name)) { - errs() << "Node for value '%" << Name << "' is collapsed: "; - N->print(errs(), G); - abort(); - } - - if (CheckFlagsM.count(Name) && CheckFlagsM[Name] != N->getNodeFlags()) { - errs() << "Node flags are not as expected for node: " << Name - << " (" << CheckFlagsM[Name] << ":" <getNodeFlags() - << ")\n"; - N->print(errs(), G); - abort(); - } - - // Verify that it is not merged if it is not supposed to be... - if (AbortIfMergedS.count(Name)) { - if (AbortIfMergedNodes.count(N)) { - errs() << "Nodes for values '%" << Name << "' and '%" - << AbortIfMergedNodes[N] << "' is merged: "; - N->print(errs(), G); - abort(); - } - AbortIfMergedNodes[N] = Name; - } - } - } -} diff --git a/lib/DSA/LICENSE b/lib/DSA/LICENSE deleted file mode 100644 index dba02fa23..000000000 --- a/lib/DSA/LICENSE +++ /dev/null @@ -1,65 +0,0 @@ -============================================================================== -LLVM Pool Allocator Release License -============================================================================== -University of Illinois/NCSA -Open Source License - -Copyright (c) 2003-2013 University of Illinois at Urbana-Champaign. -All rights reserved. - -Developed by: - - LLVM Team - - University of Illinois at Urbana-Champaign - - http://llvm.cs.uiuc.edu - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal with -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimers. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimers in the - documentation and/or other materials provided with the distribution. - - * Neither the names of the LLVM Team, University of Illinois at - Urbana-Champaign, nor the names of its contributors may be used to - endorse or promote products derived from this Software without specific - prior written permission. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE -SOFTWARE. - -============================================================================== -Copyrights and Licenses for Third Party Software Distributed with LLVM: -============================================================================== -The Automatic Pool Allocation software contains code written by third parties. -Such software will have its own individual LICENSE.TXT file in the directory in -which it appears. This file will describe the copyrights, license, and -restrictions which apply to that code. - -The disclaimer of warranty in the University of Illinois Open Source License -applies to all code in this distribution, and nothing in any of the -other licenses gives permission to use the names of the LLVM Team or the -University of Illinois to endorse or promote products derived from this -Software. - -The following pieces of software have additional or alternate copyrights, -licenses, and/or restrictions: - -Program Directory -------- --------- -Watchdog poolalloc/tools/WatchDog - diff --git a/lib/DSA/Local.cpp b/lib/DSA/Local.cpp deleted file mode 100644 index 06f372e16..000000000 --- a/lib/DSA/Local.cpp +++ /dev/null @@ -1,1592 +0,0 @@ -//===- Local.cpp - Compute a local data structure graph for a function ----===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Compute the local version of the data structure graph for a function. The -// external interface to this file is the DSGraph constructor. -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "dsa-local" - -#include "dsa/DataStructure.h" -#include "dsa/DSGraph.h" -#include "dsa/DSMonitor.h" - -#include "llvm/ADT/Statistic.h" -#include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/Triple.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/DataLayout.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/InlineAsm.h" -#include "llvm/IR/Intrinsics.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/IntrinsicInst.h" -#include "llvm/IR/Use.h" -#include "llvm/Support/CommandLine.h" -#include "smack/Debug.h" -#include "llvm/Support/FormattedStream.h" -#include "llvm/IR/GetElementPtrTypeIterator.h" -#include "llvm/IR/InstVisitor.h" -#include "llvm/Support/Timer.h" - -#include - -// FIXME: This should eventually be a FunctionPass that is automatically -// aggregated into a Pass. -// -#include "llvm/IR/Module.h" - -using namespace llvm; - -namespace { -STATISTIC(NumDirectCall, "Number of direct calls added"); -STATISTIC(NumIndirectCall, "Number of indirect calls added"); -STATISTIC(NumAsmCall, "Number of asm calls collapsed/seen"); -STATISTIC(NumIntrinsicCall, "Number of intrinsics called"); -STATISTIC(NumBoringIntToPtr, "Number of inttoptr used only in cmp"); -//STATISTIC(NumSimpleIntToPtr, "Number of inttoptr from ptrtoint"); -STATISTIC(NumIgnoredInst, "Number of instructions ignored"); - -RegisterPass -X("dsa-local", "Local Data Structure Analysis"); - -cl::opt hasMagicSections("dsa-magic-sections", - cl::desc("File with section to global mapping")); //, cl::ReallyHidden); -} -cl::opt TypeInferenceOptimize("enable-type-inference-opts", - cl::desc("Enable Type Inference Optimizations added to DSA."), - cl::Hidden, - cl::init(false)); - -namespace { - //===--------------------------------------------------------------------===// - // GraphBuilder Class - //===--------------------------------------------------------------------===// - // - /// This class is the builder class that constructs the local data structure - /// graph by performing a single pass over the function in question. - /// - class GraphBuilder : InstVisitor { - DSGraph &G; - Function* FB; - LocalDataStructures* DS; - const DataLayout& TD; - DSNode *VAArray; - DSMonitor M; - - //////////////////////////////////////////////////////////////////////////// - // Helper functions used to implement the visitation functions... - - void MergeConstantInitIntoNode(DSNodeHandle &NH, Type* Ty, Constant *C); - - /// createNode - Create a new DSNode, ensuring that it is properly added to - /// the graph. - /// - DSNode *createNode() - { - DSNode* ret = new DSNode(&G); - assert(ret->getParentGraph() && "No parent?"); - return ret; - } - - /// setDestTo - Set the ScalarMap entry for the specified value to point to - /// the specified destination. If the Value already points to a node, make - /// sure to merge the two destinations together. - /// - void setDestTo(Value &V, const DSNodeHandle &NH); - - /// getValueDest - Return the DSNode that the actual value points to. - /// - DSNodeHandle getValueDest(Value* V); - - /// getLink - This method is used to return the specified link in the - /// specified node if one exists. If a link does not already exist (it's - /// null), then we create a new node, link it, then return it. - /// - DSNodeHandle &getLink(const DSNodeHandle &Node, unsigned Link = 0); - - //////////////////////////////////////////////////////////////////////////// - // Visitor functions, used to handle each instruction type we encounter... - friend class InstVisitor; - - void visitAllocaInst(AllocaInst &AI) - { setDestTo(AI, createNode()->setAllocaMarker()); } - - //the simple ones - void visitPHINode(PHINode &PN); - void visitSelectInst(SelectInst &SI); - void visitLoadInst(LoadInst &LI); - void visitStoreInst(StoreInst &SI); - void visitAtomicCmpXchgInst(AtomicCmpXchgInst &I); - void visitAtomicRMWInst(AtomicRMWInst &I); - void visitReturnInst(ReturnInst &RI); - void visitVAArgInst(VAArgInst &I); - void visitIntToPtrInst(IntToPtrInst &I); - void visitPtrToIntInst(PtrToIntInst &I); - void visitBitCastInst(BitCastInst &I); - void visitCmpInst(CmpInst &I); - void visitInsertValueInst(InsertValueInst& I); - void visitExtractValueInst(ExtractValueInst& I); - - //the nasty ones - void visitGetElementPtrInst(User &GEP); - void visitCallInst(CallInst &CI); - void visitInvokeInst(InvokeInst &II); - void visitInstruction(Instruction &I); - - bool visitIntrinsic(CallSite CS, Function* F); - void visitCallSite(CallSite CS); - void visitVAStart(CallSite CS); - void visitVAStartNode(DSNode* N); - - public: - GraphBuilder(Function &f, DSGraph &g, LocalDataStructures& DSi) - : G(g), FB(&f), DS(&DSi), TD(g.getDataLayout()), VAArray(0) { - - SDEBUG(errs() << "[local] Building graph for function: " - << f.getName() << "\n"); - - // Create scalar nodes for all pointer arguments... - for (auto &Arg : f.args()) { - if (isa(Arg.getType())) { - // WD: Why do we set the external marker so early in the analysis? - // Functions we have definitions for, but are externally reachable have no external contexts - // that we'd want to BU external information into (since those contexts are by definition - // ones we don't have code for). Shouldn't this just be set in TD? -#if 0 - DSNode * Node = getValueDest(I).getNode(); - - if (!f.hasInternalLinkage() || !f.hasPrivateLinkage()) - Node->setExternalMarker(); -#else - getValueDest(&Arg).getNode(); -#endif - - } - } - - // Create an entry for the return, which tracks which functions are in - // the graph - g.getOrCreateReturnNodeFor(f); - - // Create a node to handle information about variable arguments - g.getOrCreateVANodeFor(f); - - visit(f); // Single pass over the function - - // If there are any constant globals referenced in this function, merge - // their initializers into the local graph from the globals graph. - // This resolves indirect calls in some common cases - // Only merge info for nodes that already exist in the local pass - // otherwise leaf functions could contain less collapsing than the globals - // graph - if (g.getScalarMap().global_begin() != g.getScalarMap().global_end()) { - ReachabilityCloner RC(&g, g.getGlobalsGraph(), 0); - for (DSScalarMap::global_iterator I = g.getScalarMap().global_begin(), - E = g.getScalarMap().global_end(); I != E; ++I) { - if (const GlobalVariable * GV = dyn_cast (*I)) - if (GV->isConstant()) - RC.merge(g.getNodeForValue(GV), g.getGlobalsGraph()->getNodeForValue(GV)); - } - } - - g.markIncompleteNodes(DSGraph::MarkFormalArgs); - - // Compute sources of external - unsigned EFlags = 0 - | DSGraph::DontMarkFormalsExternal - | DSGraph::ProcessCallSites; - - g.computeExternalFlags(EFlags); - g.computeIntPtrFlags(); - - // Remove any nodes made dead due to merging... - g.removeDeadNodes(DSGraph::KeepUnreachableGlobals); - } - - // GraphBuilder ctor for working on the globals graph - explicit GraphBuilder(DSGraph& g, LocalDataStructures& DSi) - :G(g), FB(0), TD(g.getDataLayout()), VAArray(0) - {} - - void mergeInGlobalInitializer(GlobalVariable *GV); - void mergeExternalGlobal(GlobalVariable* GV); - void mergeFunction(Function* F) { getValueDest(F); } - }; - - /// Traverse the whole DSGraph, and propagate the unknown flags through all - /// out edges. - static void propagateUnknownFlag(DSGraph * G) { - std::vector workList; - DenseSet visited; - for (DSGraph::node_iterator I = G->node_begin(), E = G->node_end(); I != E; ++I) - if (I->isUnknownNode()) - workList.push_back(&*I); - - while (!workList.empty()) { - DSNode * N = workList.back(); - workList.pop_back(); - if (visited.count(N) != 0) continue; - visited.insert(N); - N->setUnknownMarker(); - for (DSNode::edge_iterator I = N->edge_begin(), E = N->edge_end(); I != E; ++I) - if (!I->second.isNull()) - workList.push_back(I->second.getNode()); - } - } -} - -//===----------------------------------------------------------------------===// -// Helper method implementations... -// - - -/// -/// getValueDest - Return the DSNode that the actual value points to. -/// -DSNodeHandle GraphBuilder::getValueDest(Value* V) { - if (isa(V) && cast(V)->isNullValue()) - return 0; // Null doesn't point to anything, don't add to ScalarMap! - - DSNodeHandle &NH = G.getNodeForValue(V); - if (!NH.isNull()) - return NH; // Already have a node? Just return it... - - // Otherwise we need to create a new node to point to. - // Check first for constant expressions that must be traversed to - // extract the actual value. - DSNode* N; - if (Function * F = dyn_cast (V)) { - // Create a new global node for this function. - N = createNode(); - N->addFunction(F); - if (F->isDeclaration()) - N->setExternFuncMarker(); - } else if (GlobalValue * GV = dyn_cast (V)) { - // Create a new global node for this global variable. - N = createNode(); - N->addGlobal(GV); - if (GV->isDeclaration()) - N->setExternGlobalMarker(); - } else if (Constant *C = dyn_cast(V)) { - if (ConstantExpr *CE = dyn_cast(C)) { - if (CE->isCast()) { - if (isa(CE->getOperand(0)->getType())) - NH = getValueDest(CE->getOperand(0)); - else - NH = createNode()->setUnknownMarker(); - } else if (CE->getOpcode() == Instruction::GetElementPtr) { - visitGetElementPtrInst(*CE); - assert(G.hasNodeForValue(CE) && "GEP didn't get processed right?"); - NH = G.getNodeForValue(CE); - } else { - // This returns a conservative unknown node for any unhandled ConstExpr - NH = createNode()->setUnknownMarker(); - } - if (NH.isNull()) { // (getelementptr null, X) returns null - G.eraseNodeForValue(V); - return 0; - } - return NH; - } else if (isa(C)) { - G.eraseNodeForValue(V); - return 0; - } else if (isa(C)) { - // XXX: Need more investigation - // According to Andrew, DSA is broken on global aliasing, since it does - // not handle the aliases of parameters correctly. Here is only a quick - // fix for some special cases. - NH = getValueDest(cast(C)->getAliasee()); - return NH; - } else if (isa(C)) { - // - // FIXME: This may not be quite right; we should probably add a - // BlockAddress flag to the DSNode instead of using the unknown flag. - // - N = createNode(); - N->setUnknownMarker(); - } else if (isa(C) || isa(C) || - isa(C) || isa(C) || - isa(C)) { - // Treat these the same way we treat global initializers - N = createNode(); - NH.mergeWith(N); - MergeConstantInitIntoNode(NH, C->getType(), C); - } else { - errs() << "Unknown constant: " << *C << "\n"; - llvm_unreachable("Unknown constant type!"); - } - N = createNode(); // just create a shadow node - } else { - // Otherwise just create a shadow node - N = createNode(); - } - - NH.setTo(N, 0); // Remember that we are pointing to it... - return NH; -} - - -/// getLink - This method is used to return the specified link in the -/// specified node if one exists. If a link does not already exist (it's -/// null), then we create a new node, link it, then return it. We must -/// specify the type of the Node field we are accessing so that we know what -/// type should be linked to if we need to create a new node. -/// -DSNodeHandle &GraphBuilder::getLink(const DSNodeHandle &node, unsigned LinkNo) { - DSNodeHandle &Node = const_cast(node); - DSNodeHandle &Link = Node.getLink(LinkNo); - if (Link.isNull()) { - // If the link hasn't been created yet, make and return a new shadow node - Link = createNode(); - } - return Link; -} - - -/// setDestTo - Set the ScalarMap entry for the specified value to point to the -/// specified destination. If the Value already points to a node, make sure to -/// merge the two destinations together. -/// -void GraphBuilder::setDestTo(Value &V, const DSNodeHandle &NH) { - G.getNodeForValue(&V).mergeWith(NH); -} - - -//===----------------------------------------------------------------------===// -// Specific instruction type handler implementations... -// - -// PHINode - Make the scalar for the PHI node point to all of the things the -// incoming values point to... which effectively causes them to be merged. -// -void GraphBuilder::visitPHINode(PHINode &PN) { - SDEBUG(errs() << "[local] visiting phi node: " << PN.getName() << "\n"); - - if (!isa(PN.getType())) return; // Only pointer PHIs - - DSNodeHandle &PNDest = G.getNodeForValue(&PN); - for (unsigned i = 0, e = PN.getNumIncomingValues(); i != e; ++i) - PNDest.mergeWith(getValueDest(PN.getIncomingValue(i))); -} - -void GraphBuilder::visitSelectInst(SelectInst &SI) { - if (!isa(SI.getType())) - return; // Only pointer Selects - - DSNodeHandle &Dest = G.getNodeForValue(&SI); - DSNodeHandle S1 = getValueDest(SI.getOperand(1)); - DSNodeHandle S2 = getValueDest(SI.getOperand(2)); - Dest.mergeWith(S1); - Dest.mergeWith(S2); -} - -void GraphBuilder::visitLoadInst(LoadInst &LI) { - SDEBUG(errs() << "[local] visiting load: " << LI << "\n"); - // - // Create a DSNode for the pointer dereferenced by the load. If the DSNode - // is NULL, do nothing more (this can occur if the load is loading from a - // NULL pointer constant (bugpoint can generate such code). - // - DSNodeHandle Ptr = getValueDest(LI.getPointerOperand()); - if (Ptr.isNull()) return; // Load from null - - // Make that the node is read from... - Ptr.getNode()->setReadMarker(); - - // Ensure a typerecord exists... - Ptr.getNode()->growSizeForType(LI.getType(), Ptr.getOffset()); - - if (isa(LI.getType())) - setDestTo(LI, getLink(Ptr)); - - // check that it is the inserted value - if(TypeInferenceOptimize) - if(LI.hasOneUse()) - if(StoreInst *SI = dyn_cast(*(LI.use_begin()))) - if(SI->getOperand(0) == &LI) { - ++NumIgnoredInst; - return; - } - Ptr.getNode()->mergeTypeInfo(LI.getType(), Ptr.getOffset()); -} - -void GraphBuilder::visitStoreInst(StoreInst &SI) { - SDEBUG(errs() << "[local] visiting store: " << SI << "\n"); - Type *StoredTy = SI.getOperand(0)->getType(); - DSNodeHandle Dest = getValueDest(SI.getOperand(1)); - if (Dest.isNull()) return; - - // Mark that the node is written to... - Dest.getNode()->setModifiedMarker(); - - // Ensure a type-record exists... - Dest.getNode()->growSizeForType(StoredTy, Dest.getOffset()); - - // Avoid adding edges from null, or processing non-"pointer" stores - if (isa(StoredTy)) - Dest.addEdgeTo(getValueDest(SI.getOperand(0))); - - if(TypeInferenceOptimize) - if(SI.getOperand(0)->hasOneUse()) - if(isa(SI.getOperand(0))){ - ++NumIgnoredInst; - return; - } - Dest.getNode()->mergeTypeInfo(StoredTy, Dest.getOffset()); -} - -void GraphBuilder::visitAtomicCmpXchgInst(AtomicCmpXchgInst &I) { - SDEBUG(errs() << "[local] visiting atomic cmpxchg: " << I << "\n"); - if (isa(I.getType())) { - visitInstruction (I); - return; - } - - // - // Create a DSNode for the dereferenced pointer . If the DSNode is NULL, do - // nothing more (this can occur if the pointer is a NULL constant; bugpoint - // can generate such code). - // - DSNodeHandle Ptr = getValueDest(I.getPointerOperand()); - if (Ptr.isNull()) return; - - // - // Make that the memory object is read and written. - // - Ptr.getNode()->setReadMarker(); - Ptr.getNode()->setModifiedMarker(); - - // - // If the result of the compare-and-swap is a pointer, then we need to do - // a few things: - // o Merge the compare and swap values (which are pointers) with the result - // o Merge the DSNode of the pointer *within* the memory object with the - // DSNode of the compare, swap, and result DSNode. - // - if (isa(I.getType())) { - // - // Get the DSNodeHandle of the memory object returned from the load. Make - // it the DSNodeHandle of the instruction's result. - // - DSNodeHandle FieldPtr = getLink (Ptr); - setDestTo(I, getLink(Ptr)); - - // - // Merge the result, compare, and swap values of the instruction. - // - FieldPtr.mergeWith (getValueDest (I.getCompareOperand())); - FieldPtr.mergeWith (getValueDest (I.getNewValOperand())); - } - - // - // Modify the DSNode so that it has the loaded/written type at the - // appropriate offset. - // - Ptr.getNode()->growSizeForType(I.getType(), Ptr.getOffset()); - Ptr.getNode()->mergeTypeInfo(I.getType(), Ptr.getOffset()); - return; -} - -void GraphBuilder::visitAtomicRMWInst(AtomicRMWInst &I) { - SDEBUG(errs() << "[local] visiting atomic RMW: " << I << "\n"); - // - // Create a DSNode for the dereferenced pointer . If the DSNode is NULL, do - // nothing more (this can occur if the pointer is a NULL constant; bugpoint - // can generate such code). - // - DSNodeHandle Ptr = getValueDest(I.getPointerOperand()); - if (Ptr.isNull()) return; - - // - // Make that the memory object is read and written. - // - Ptr.getNode()->setReadMarker(); - Ptr.getNode()->setModifiedMarker(); - - // - // Modify the DSNode so that it has the loaded/written type at the - // appropriate offset. - // - Ptr.getNode()->growSizeForType(I.getType(), Ptr.getOffset()); - Ptr.getNode()->mergeTypeInfo(I.getType(), Ptr.getOffset()); - return; -} - -void GraphBuilder::visitReturnInst(ReturnInst &RI) { - SDEBUG(errs() << "[local] visiting return: " << RI << "\n"); - if (RI.getNumOperands() && isa(RI.getOperand(0)->getType())) - G.getOrCreateReturnNodeFor(*FB).mergeWith(getValueDest(RI.getOperand(0))); -} - -void GraphBuilder::visitVAArgInst(VAArgInst &I) { - SDEBUG(errs() << "[local] visiting vaarg: " << I << "\n"); - Module *M = FB->getParent(); - Triple TargetTriple(M->getTargetTriple()); - Triple::ArchType Arch = TargetTriple.getArch(); - switch(Arch) { - case Triple::x86_64: { - // On x86_64, we have va_list as a struct {i32, i32, i8*, i8* } - // The first i8* is where arguments generally go, but the second i8* can - // be used also to pass arguments by register. - // We model this by having both the i8*'s point to an array of pointers - // to the arguments. - DSNodeHandle Ptr = G.getVANodeFor(*FB); - DSNodeHandle Dest = getValueDest(&I); - if (Ptr.isNull()) return; - - // Make that the node is read and written - Ptr.getNode()->setReadMarker()->setModifiedMarker(); - - // Not updating type info, as it is already a collapsed node - - if (isa(I.getType())) - Dest.mergeWith(Ptr); - return; - } - - default: { - llvm_unreachable("What frontend generates this?"); - DSNodeHandle Ptr = getValueDest(I.getOperand(0)); - - //FIXME: also updates the argument - if (Ptr.isNull()) return; - - // Make that the node is read and written - Ptr.getNode()->setReadMarker()->setModifiedMarker(); - - // Ensure a type record exists. - DSNode *PtrN = Ptr.getNode(); - PtrN->mergeTypeInfo(I.getType(), Ptr.getOffset()); - - if (isa(I.getType())) - setDestTo(I, getLink(Ptr)); - } - } -} - -void GraphBuilder::visitIntToPtrInst(IntToPtrInst &I) { - SDEBUG(errs() << "[local] visiting inttoptr: " << I << "\n"); - DSNode *N = createNode(); - if(I.hasOneUse()) { - if(isa(*(I.use_begin()))) { - NumBoringIntToPtr++; - return; - } - } - N->setIntToPtrMarker(); - N->setUnknownMarker(); - setDestTo(I, N); -} - -void GraphBuilder::visitPtrToIntInst(PtrToIntInst& I) { - SDEBUG(errs() << "[local] visiting ptrtoint: " << I << "\n"); - DSNode* N = getValueDest(I.getOperand(0)).getNode(); - if(I.hasOneUse()) { - if(isa(*(I.use_begin()))) { - NumBoringIntToPtr++; - return; - } - } - if(I.hasOneUse()) { - Value *V = dyn_cast(*(I.use_begin())); - DenseSet Seen; - while(V && V->hasOneUse() && - Seen.insert(V).second) { - if(isa(V)) - break; - if(isa(V)) - break; - if(isa(V)) - break; - V = dyn_cast(*(V->use_begin())); - } - if(isa(V)){ - NumBoringIntToPtr++; - return; - } - } - if(N) - N->setPtrToIntMarker(); -} - - -void GraphBuilder::visitBitCastInst(BitCastInst &I) { - SDEBUG(errs() << "[local] visiting bitcast: " << I << "\n"); - if (!isa(I.getType())) return; // Only pointers - DSNodeHandle Ptr = getValueDest(I.getOperand(0)); - if (Ptr.isNull()) return; - setDestTo(I, Ptr); -} - -void GraphBuilder::visitCmpInst(CmpInst &I) { - SDEBUG(errs() << "[local] visiting compare: " << I << "\n"); - //Address can escape through cmps -} - -unsigned getValueOffset(Type *Ty, ArrayRef Idxs, - const DataLayout &TD) { - unsigned Offset = 0; - for (ArrayRef::iterator I = Idxs.begin(), E = Idxs.end(); I != E; - ++I) { - // Lifted from DataLayout.cpp's getIndexedOffset. - // We can't use that because it insists on only allowing pointer types. - if (StructType *STy = dyn_cast(Ty)) { - unsigned FieldNo = *I; - - // Get structure layout information... - const StructLayout *Layout = TD.getStructLayout(STy); - - // Add in the offset, as calculated by the structure layout info... - Offset += Layout->getElementOffset(FieldNo); - - // Update Ty to refer to current element - Ty = STy->getElementType(FieldNo); - } else { - // Update Ty to refer to current element - Ty = cast(Ty)->getElementType(); - - // Get the array index and the size of each array element. - int64_t arrayIdx = *I; - Offset += (uint64_t)arrayIdx * TD.getTypeAllocSize(Ty); - } - } - return Offset; -} - -void GraphBuilder::visitInsertValueInst(InsertValueInst& I) { - SDEBUG(errs() << "[local] visiting insertvalue: " << I << "\n"); - setDestTo(I, createNode()->setAllocaMarker()); - - Type *StoredTy = I.getInsertedValueOperand()->getType(); - DSNodeHandle Dest = getValueDest(&I); - Dest.mergeWith(getValueDest(I.getAggregateOperand())); - - // Mark that the node is written to... - Dest.getNode()->setModifiedMarker(); - Type* STy = I.getAggregateOperand()->getType(); - - unsigned Offset = getValueOffset(STy, I.getIndices(), TD); - - // Ensure a type-record exists... - Dest.getNode()->mergeTypeInfo(StoredTy, Offset); - - // Avoid adding edges from null, or processing non-"pointer" stores - if (isa(StoredTy)) - Dest.addEdgeTo(getValueDest(I.getInsertedValueOperand())); -} - -void GraphBuilder::visitExtractValueInst(ExtractValueInst& I) { - SDEBUG(errs() << "[local] visiting extractvalue: " << I << "\n"); - DSNodeHandle Ptr = getValueDest(I.getAggregateOperand()); - - // Make that the node is read from... - Ptr.getNode()->setReadMarker(); - Type* STy = I.getAggregateOperand()->getType(); - - unsigned Offset = getValueOffset(STy, I.getIndices(), TD); - - // Ensure a typerecord exists... - Ptr.getNode()->mergeTypeInfo(I.getType(), Offset); - - if (isa(I.getType())) - setDestTo(I, getLink(Ptr)); -} - -void GraphBuilder::visitGetElementPtrInst(User &GEP) { - SDEBUG(errs() << "[local] visiting GEP: " << GEP << "\n"); - // - // Ensure that the indexed pointer has a DSNode. - // - DSNodeHandle NodeH = getValueDest(GEP.getOperand(0)); - if (NodeH.isNull()) - NodeH = createNode(); - - // - // There are a few quick and easy cases to handle. If the DSNode of the - // indexed pointer is already folded, then we know that the result of the - // GEP will have the same offset into the same DSNode - // as the indexed pointer. - // - - if (!NodeH.isNull() && - NodeH.getNode()->isNodeCompletelyFolded()) { - setDestTo(GEP, NodeH); - return; - } - - // - // Okay, no easy way out. Calculate the offset into the object being - // indexed. - // - - int Offset = 0; - - // FIXME: I am not sure if the code below is completely correct (especially - // if we start doing fancy analysis on non-constant array indices). - // What if the array is indexed using a larger index than its declared - // size? Does the LLVM verifier catch such issues? - // - - // - // Determine the offset (in bytes) between the result of the GEP and the - // GEP's pointer operand. - // - // Note: All of these subscripts are indexing INTO the elements we have... - // - // FIXME: We can do better for array indexing. First, if the array index is - // constant, we can determine how much farther we're moving the - // pointer. Second, we can try to use the results of other analysis - // passes (e.g., ScalarEvolution) to find min/max values to do less - // conservative type-folding. - // - for (gep_type_iterator I = gep_type_begin(GEP), E = gep_type_end(GEP); - I != E; ++I) { - - if (StructType *STy = I.getStructTypeOrNull()) { - // indexing into a structure - // next index must be a constant - const ConstantInt* CUI = cast(I.getOperand()); - int FieldNo = CUI->getSExtValue(); - // increment the offset by the actual byte offset being accessed - - unsigned requiredSize = TD.getTypeAllocSize(STy) + NodeH.getOffset() + Offset; - - // - // Grow the DSNode size as needed. - // - if (!NodeH.getNode()->isArrayNode() || NodeH.getNode()->getSize() <= 0){ - if (requiredSize > NodeH.getNode()->getSize()) - NodeH.getNode()->growSize(requiredSize); - } - - Offset += (unsigned)TD.getStructLayout(STy)->getElementOffset(FieldNo); - if (TypeInferenceOptimize) { - if (ArrayType* AT = dyn_cast(STy->getTypeAtIndex(FieldNo))) { - NodeH.getNode()->mergeTypeInfo(AT, NodeH.getOffset() + Offset); - if ((++I) == E) { - break; - } - - // Check if we are still indexing into an array. - // We only record the topmost array type of any nested array. - // Keep skipping indexes till we reach a non-array type. - // J is the type of the next index. - // Uncomment the line below to get all the nested types. - gep_type_iterator J = I; - while ((++J).isSequential()) { - // NodeH.getNode()->mergeTypeInfo(AT1, NodeH.getOffset() + Offset); - if((++I) == E) { - break; - } - J = I; - } - if ((I) == E) { - break; - } - } - } - } else { - Type *CurTy = I.getIndexedType(); - if (I.isBoundedSequential()) { - // indexing into an array. - NodeH.getNode()->setArrayMarker(); - - // - // Ensure that the DSNode's size is large enough to contain one - // element of the type to which the pointer points. - // - if (!isa(CurTy) && NodeH.getNode()->getSize() <= 0) { - NodeH.getNode()->growSize(TD.getTypeAllocSize(CurTy)); - } else if(isa(CurTy) && NodeH.getNode()->getSize() <= 0){ - Type *ETy = (cast(CurTy))->getElementType(); - while(isa(ETy)) { - ETy = (cast(ETy))->getElementType(); - } - NodeH.getNode()->growSize(TD.getTypeAllocSize(ETy)); - } - - // Find if the DSNode belongs to the array - // If not fold. - if((NodeH.getOffset() || Offset != 0) - || (!isa(CurTy) - && (NodeH.getNode()->getSize() != TD.getTypeAllocSize(CurTy)))) { - - M.witness(NodeH, {&GEP, I.getOperand()}, - "node does not belong to array" - ); - - SDEBUG( - errs() << "[local] FOLDING FOR ARRAY ACCESS" << "\n"; - errs() << "[local] type: " << *CurTy << "\n"; - errs() << "[local] offset: " << Offset - << " (" << NodeH.getOffset() << ")\n"; - errs() << "[local] size: " << TD.getTypeAllocSize(CurTy) - << " (" << NodeH.getNode()->getSize() << ")\n"; - errs() << "[local] value: " << GEP << "\n"; - errs() << "[local] index: " << *I.getOperand() << "\n"; - ); - NodeH.getNode()->foldNodeCompletely(); - NodeH.getNode(); - Offset = 0; - break; - } - } else { - // Pointer type - - // - // Some LLVM transforms lower structure indexing into byte-level - // indexing. Try to recognize forms of that here. - // - Type * Int8Type = Type::getInt8Ty(CurTy->getContext()); - ConstantInt * IS = dyn_cast(I.getOperand()); - if (IS && - (NodeH.getOffset() == 0) && - (!(NodeH.getNode()->isArrayNode())) && - (CurTy == Int8Type)) { - // Calculate the offset of the field - Offset += IS->getSExtValue() * TD.getTypeAllocSize (Int8Type); - - // - // Grow the DSNode size as needed. - // - unsigned requiredSize = Offset + TD.getTypeAllocSize (Int8Type); - if (NodeH.getNode()->getSize() <= requiredSize){ - NodeH.getNode()->growSize (requiredSize); - } - - // Add in the offset calculated... - NodeH.setOffset(NodeH.getOffset()+Offset); - - // Check the offset - DSNode *N = NodeH.getNode(); - if (N) N->checkOffsetFoldIfNeeded(NodeH.getOffset()); - - // NodeH is now the pointer we want to GEP to be... - setDestTo(GEP, NodeH); - return; - } - - // - // Unless we're advancing the pointer by zero bytes via array indexing, - // fold the node (i.e., mark it type-unknown) and indicate that we're - // indexing zero bytes into the object (because all fields are aliased). - // - // Note that we break out of the loop if we fold the node. Once - // something is folded, all values within it are considered to alias. - // - if (!isa(I.getOperand()) || - !cast(I.getOperand())->isNullValue()) { - - // - // Treat the memory object (DSNode) as an array. - // - NodeH.getNode()->setArrayMarker(); - - // - // Ensure that the DSNode's size is large enough to contain one - // element of the type to which the pointer points. - // - if (!isa(CurTy) && NodeH.getNode()->getSize() <= 0){ - NodeH.getNode()->growSize(TD.getTypeAllocSize(CurTy)); - } else if (isa(CurTy) && NodeH.getNode()->getSize() <= 0){ - Type *ETy = (cast(CurTy))->getElementType(); - while (isa(ETy)) { - ETy = (cast(ETy))->getElementType(); - } - NodeH.getNode()->growSize(TD.getTypeAllocSize(ETy)); - } - - // - // Fold the DSNode if we're indexing into it in a type-incompatible - // manner. That can occur if: - // 1) The DSNode represents a pointer into the object at a non-zero - // offset. - // 2) The offset of the pointer is already non-zero. - // 3) The size of the array element does not match the size into which - // the pointer indexing is indexing. - // - if (NodeH.getOffset() || Offset != 0 || - (!isa(CurTy) && - (NodeH.getNode()->getSize() != TD.getTypeAllocSize(CurTy)))) { - - M.witness(NodeH, {&GEP, I.getOperand()}, - "type-incompatible access into node" - ); - - SDEBUG( - errs() << "[local] FOLDING FOR POINTER ACCESS" << "\n"; - errs() << "[local] type: " << *CurTy << "\n"; - errs() << "[local] offset: " << Offset - << " (" << NodeH.getOffset() << ")\n"; - errs() << "[local] size: " << TD.getTypeAllocSize(CurTy) - << " (" << NodeH.getNode()->getSize() << ")\n"; - errs() << "[local] value: " << GEP << "\n"; - errs() << "[local] index: " << *I.getOperand() << "\n"; - ); - NodeH.getNode()->foldNodeCompletely(); - NodeH.getNode(); - Offset = 0; - break; - } - } - } - } - } - - // Add in the offset calculated... - NodeH.setOffset(NodeH.getOffset()+Offset); - - // Check the offset - DSNode *N = NodeH.getNode(); - if (N) N->checkOffsetFoldIfNeeded(NodeH.getOffset()); - - // NodeH is now the pointer we want to GEP to be... - setDestTo(GEP, NodeH); -} - - -void GraphBuilder::visitCallInst(CallInst &CI) { - SDEBUG(errs() << "[local] visiting call: " << CI << "\n"); - visitCallSite(&CI); -} - -void GraphBuilder::visitInvokeInst(InvokeInst &II) { - SDEBUG(errs() << "[local] visiting invoke: " << II << "\n"); - visitCallSite(&II); -} - -void GraphBuilder::visitVAStart(CallSite CS) { - SDEBUG(errs() << "[local] visiting VA start: " - << CS.getCalledValue()->getName() << "\n"); - - // Build out DSNodes for the va_list depending on the target arch - // And assosiate the right node with the VANode for this function - // so it can be merged with the right arguments from callsites - - DSNodeHandle RetNH = getValueDest(CS.getArgument(0)); - - if (DSNode *N = RetNH.getNode()) - visitVAStartNode(N); -} - -void GraphBuilder::visitVAStartNode(DSNode* N) { - assert(N && "Null node as argument"); - assert(FB && "No function for this graph?"); - Module *M = FB->getParent(); - assert(M && "No module for function"); - Triple TargetTriple(M->getTargetTriple()); - Triple::ArchType Arch = TargetTriple.getArch(); - - // Fetch the VANode associated with the func containing the call to va_start - DSNodeHandle & VANH = G.getVANodeFor(*FB); - // Make sure this NodeHandle has a node to go with it - if (VANH.isNull()) VANH.mergeWith(createNode()); - - // Create a dsnode for an array of pointers to the VAInfo for this func - // We create one such array for each function analyzed, as all - // calls to va_start will populate their argument with the same data. - if (!VAArray) VAArray = createNode(); - VAArray->setArrayMarker(); - VAArray->foldNodeCompletely(); - VAArray->setLink(0,VANH); - - //VAStart modifies its argument - N->setModifiedMarker(); - - // For the architectures we support, build dsnodes that match - // how we know va_list is used. - switch (Arch) { - case Triple::x86: - // On x86, we have: - // va_list as a pointer to an array of pointers to the variable arguments - if (N->getSize() < 1) - N->growSize(1); - N->setLink(0, VAArray); - break; - case Triple::x86_64: - // On x86_64, we have va_list as a struct {i32, i32, i8*, i8* } - // The first i8* is where arguments generally go, but the second i8* can - // be used also to pass arguments by register. - // We model this by having both the i8*'s point to an array of pointers - // to the arguments. - if (N->getSize() < 24) - N->growSize(24); //sizeof the va_list struct mentioned above - N->setLink(8,VAArray); //first i8* - N->setLink(16,VAArray); //second i8* - - break; - default: - // FIXME: For now we abort if we don't know how to handle this arch - // Either add support for other architectures, or at least mark the - // nodes unknown/incomplete or whichever results in the correct - // conservative behavior in the general case - llvm_unreachable("VAstart not supported on this architecture!"); - //XXX: This might be good enough in those cases that we don't know - //what the arch does - N->setIncompleteMarker()->setUnknownMarker()->foldNodeCompletely(); - } - - // XXX: We used to set the alloca marker for the DSNode passed to va_start. - // Seems to me that you could allocate the va_list on the heap, so ignoring - // for now. - N->setModifiedMarker()->setVAStartMarker(); -} - -/// -/// Method: visitIntrinsic() -/// -/// Description: -/// Generate correct DSNodes for calls to LLVM intrinsic functions. -/// -/// Inputs: -/// CS - The CallSite representing the call or invoke to the intrinsic. -/// F - A pointer to the function called by the call site. -/// -/// Return value: -/// true - This intrinsic is properly handled by this method. -/// false - This intrinsic is not recognized by DSA. -/// -bool GraphBuilder::visitIntrinsic(CallSite CS, Function *F) { - SDEBUG(errs() << "[local] visiting intrinsic: " << F->getName() << "\n"); - - ++NumIntrinsicCall; - - // - // If this is a debug intrinsic, then don't do any special processing. - // - if (isa(CS.getInstruction())) - return true; - - switch (F->getIntrinsicID()) { - case Intrinsic::vastart: { - visitVAStart(CS); - return true; - } - case Intrinsic::vacopy: { - // Simply merge the two arguments to va_copy. - // This results in loss of precision on the temporaries used to manipulate - // the va_list, and so isn't a big deal. In theory we would build a - // separate graph for this (like the one created in visitVAStartNode) - // and only merge the node containing the variable arguments themselves. - DSNodeHandle destNH = getValueDest(CS.getArgument(0)); - DSNodeHandle srcNH = getValueDest(CS.getArgument(1)); - destNH.mergeWith(srcNH); - return true; - } - case Intrinsic::stacksave: { - DSNode * Node = createNode(); - Node->setAllocaMarker()->setIncompleteMarker()->setUnknownMarker(); - Node->foldNodeCompletely(); - setDestTo (*(CS.getInstruction()), Node); - return true; - } - case Intrinsic::stackrestore: - getValueDest(CS.getInstruction()).getNode()->setAllocaMarker() - ->setIncompleteMarker() - ->setUnknownMarker() - ->foldNodeCompletely(); - return true; - case Intrinsic::vaend: - // TODO: What to do here? - return true; - case Intrinsic::memcpy: - case Intrinsic::memmove: { - // Merge the first & second arguments, and mark the memory read and - // modified. - DSNodeHandle RetNH = getValueDest(CS.getArgument(0)); - RetNH.mergeWith(getValueDest(CS.getArgument(1))); - if (DSNode *N = RetNH.getNode()) - N->setModifiedMarker()->setReadMarker(); - return true; - } - case Intrinsic::memset: - // Mark the memory modified. - if (DSNode *N = getValueDest(CS.getArgument(0)).getNode()) - N->setModifiedMarker(); - return true; - - // TODO: Add support for the new EH system -#if 0 - case Intrinsic::eh_exception: { - DSNode * Node = createNode(); - Node->setIncompleteMarker(); - Node->foldNodeCompletely(); - setDestTo (*(CS.getInstruction()), Node); - return true; - } - - case Intrinsic::eh_selector: { - for (CallSite::arg_iterator I = CS.arg_begin(), E = CS.arg_end(); - I != E; ++I) { - if (isa((*I)->getType())) { - DSNodeHandle Ptr = getValueDest(*I); - if(Ptr.getNode()) { - Ptr.getNode()->setReadMarker(); - Ptr.getNode()->setIncompleteMarker(); - } - } - } - return true; - } -#endif - - case Intrinsic::eh_typeid_for: { - DSNodeHandle Ptr = getValueDest(CS.getArgument(0)); - Ptr.getNode()->setReadMarker(); - Ptr.getNode()->setIncompleteMarker(); - return true; - } - - case Intrinsic::prefetch: - return true; - - case Intrinsic::objectsize: - return true; - - // - // The return address/frame address aliases with the stack, - // is type-unknown, and should - // have the unknown flag set since we don't know where it goes. - // - case Intrinsic::returnaddress: - case Intrinsic::frameaddress: { - DSNode * Node = createNode(); - Node->setAllocaMarker()->setIncompleteMarker()->setUnknownMarker(); - Node->foldNodeCompletely(); - setDestTo (*(CS.getInstruction()), Node); - return true; - } - - // Process lifetime intrinsics - case Intrinsic::lifetime_start: - case Intrinsic::lifetime_end: - case Intrinsic::invariant_start: - case Intrinsic::invariant_end: - return true; - - default: { - //ignore pointer free intrinsics - if (!isa(F->getReturnType())) { - bool hasPtr = false; - for (Function::const_arg_iterator I = F->arg_begin(), E = F->arg_end(); - I != E && !hasPtr; ++I) - if (isa(I->getType())) - hasPtr = true; - if (!hasPtr) - return true; - } - - SDEBUG(errs() << "[dsa:local] Unhandled intrinsic: " << F->getName() << "\n"); - llvm_unreachable("Unhandled intrinsic"); - return false; - } - } -} - -void GraphBuilder::visitCallSite(CallSite CS) { - // - // Get the called value. Strip off any casts which are lossless. - // - Value *Callee = CS.getCalledValue()->stripPointerCasts(); - - // Special case handling of certain libc allocation functions here. - if (Function *F = dyn_cast(Callee)) - if (F->isIntrinsic() && visitIntrinsic(CS, F)) - return; - - //Can't do much about inline asm (yet!) - if (isa (Callee)) { - ++NumAsmCall; - DSNodeHandle RetVal; - Instruction *I = CS.getInstruction(); - if (isa (I->getType())) - RetVal = getValueDest(I); - - // Calculate the arguments vector... - for (CallSite::arg_iterator I = CS.arg_begin(), E = CS.arg_end(); I != E; ++I) - if (isa ((*I)->getType())) - RetVal.mergeWith(getValueDest(*I)); - if (!RetVal.isNull()) { - - M.witness(RetVal, {I}, - "inline asm call" - ); - - RetVal.getNode()->foldNodeCompletely(); - } - return; - } - - // Set up the return value... - DSNodeHandle RetVal; - Instruction *I = CS.getInstruction(); - if (isa(I->getType())) - RetVal = getValueDest(I); - - DSNode *CalleeNode = 0; - if (!isa(Callee)) { - CalleeNode = getValueDest(Callee).getNode(); - if (CalleeNode == 0) { - SDEBUG(errs() << "WARNING: Program is calling through a null pointer?\n" << *I); - return; // Calling a null pointer? - } - } - - // NOTE: This code is identical to 'DSGraph::getDSCallSiteForCallSite', - // the reason it's duplicated is because this calls getValueDest instead - // of getNodeForValue to get the DSNodes for the arguments. Since we're in - // local it's possible that we need to create a DSNode for the argument, as - // opposed to getNodeForValue which simply retrieves the existing node. - - - //Get the FunctionType for the called function - const FunctionType *CalleeFuncType = DSCallSite::FunctionTypeOfCallSite(CS); - int NumFixedArgs = CalleeFuncType->getNumParams(); - - // Sanity check--this really, really shouldn't happen - if (!CalleeFuncType->isVarArg()) - assert(CS.arg_size() == static_cast(NumFixedArgs) && - "Too many arguments/incorrect function signature!"); - - std::vector Args; - Args.reserve(CS.arg_size()); - DSNodeHandle VarArgNH; - - // Calculate the arguments vector... - // Add all fixed pointer arguments, then merge the rest together - for (CallSite::arg_iterator I = CS.arg_begin(), E = CS.arg_end(); - I != E; ++I) - if (isa((*I)->getType())) { - DSNodeHandle ArgNode = getValueDest(*I); - if (I - CS.arg_begin() < NumFixedArgs) { - Args.push_back(ArgNode); - } else { - VarArgNH.mergeWith(ArgNode); - } - } - - // Add a new function call entry... - if (CalleeNode) { - ++NumIndirectCall; - G.getFunctionCalls().push_back(DSCallSite(CS, RetVal, VarArgNH, CalleeNode, - Args)); - } else { - ++NumDirectCall; - G.getFunctionCalls().push_back(DSCallSite(CS, RetVal, VarArgNH, - cast(Callee), - Args)); - } - - -} - -// visitInstruction - For all other instruction types, if we have any arguments -// that are of pointer type, make them have unknown composition bits, and merge -// the nodes together. -void GraphBuilder::visitInstruction(Instruction &Inst) { - SDEBUG(errs() << "[local] visiting instruction: " << Inst << "\n"); - DSNodeHandle CurNode; - if (isa(Inst.getType())) - CurNode = getValueDest(&Inst); - for (User::op_iterator I = Inst.op_begin(), E = Inst.op_end(); I != E; ++I) - if (isa((*I)->getType())) - CurNode.mergeWith(getValueDest(*I)); - - if (DSNode *N = CurNode.getNode()) - N->setUnknownMarker(); -} - - - -//===----------------------------------------------------------------------===// -// LocalDataStructures Implementation -//===----------------------------------------------------------------------===// - -// -// Function: MergeConstantInitIntoNode() -// -// Description: -// Merge the specified constant into the specified DSNode. -// -void -GraphBuilder::MergeConstantInitIntoNode(DSNodeHandle &NH, - Type* Ty, - Constant *C) { - // - // Ensure a type-record exists... - // - DSNode *NHN = NH.getNode(); - //NHN->mergeTypeInfo(Ty, NH.getOffset()); - - // - // If we've found something of pointer type, create or find its DSNode and - // make a link from the specified DSNode to the new DSNode describing the - // pointer we've just found. - // - if (isa(Ty)) { - NHN->mergeTypeInfo(Ty, NH.getOffset()); - NH.addEdgeTo(getValueDest(C)); - return; - } - - // - // If the type of the object (array element, structure field, etc.) is an - // integer or floating point type, then just ignore it. It has no DSNode. - // - if (Ty->isIntOrIntVectorTy() || Ty->isFPOrFPVectorTy()) return; - - // - // Handle aggregate constants. - // - if (ConstantArray *CA = dyn_cast(C)) { - // - // For an array, we don't worry about different elements pointing to - // different objects; we essentially pretend that all array elements alias. - // - Type * ElementType = cast(Ty)->getElementType(); - for (unsigned i = 0, e = CA->getNumOperands(); i != e; ++i) { - Constant * ConstElement = cast(CA->getOperand(i)); - MergeConstantInitIntoNode(NH, ElementType, ConstElement); - } - } else if (ConstantStruct *CS = dyn_cast(C)) { - // - // For a structure, we need to merge each element of the constant structure - // into the specified DSNode. However, we must also handle structures that - // end with a zero-length array ([0 x sbyte]); this is a common C idiom - // that continues to plague the world. - // - //NHN->mergeTypeInfo(Ty, NH.getOffset()); - - const StructLayout *SL = TD.getStructLayout(cast(Ty)); - - for (unsigned i = 0, e = CS->getNumOperands(); i != e; ++i) { - DSNode *NHN = NH.getNode(); - if (SL->getElementOffset(i) < SL->getSizeInBytes()) { - // - // Get the type and constant value of this particular element of the - // constant structure. - // - Type * ElementType = cast(Ty)->getElementType(i); - Constant * ConstElement = cast(CS->getOperand(i)); - - // - // Get the offset (in bytes) into the memory object that we're - // analyzing. - // - unsigned offset = NH.getOffset()+(unsigned)SL->getElementOffset(i); - NHN->mergeTypeInfo(ElementType, offset); - // - // Create a new DSNodeHandle. This DSNodeHandle will point to the same - // DSNode as the one we're constructing for our caller; however, it - // will point into a different offset into that DSNode. - // - DSNodeHandle NewNH (NHN, offset); - assert ((NHN->isNodeCompletelyFolded() || (NewNH.getOffset() == offset)) - && "Need to resize DSNode!"); - - // - // Recursively merge in this element of the constant struture into the - // DSNode. - // - MergeConstantInitIntoNode(NewNH, ElementType, ConstElement); - } else if (SL->getElementOffset(i) == SL->getSizeInBytes()) { - // - // If this is one of those cute structures that ends with a zero-length - // array, just fold the DSNode now and get it over with. - // - SDEBUG(errs() << "Zero size element at end of struct\n" ); - - M.witness(NHN, {CS}, - "zero size element at end of struct" - ); - - NHN->foldNodeCompletely(); - } else { - llvm_unreachable("type was smaller than offsets of struct layout indicate"); - } - } - } else if (isa(C) || isa(C)) { - // - // Undefined values and NULL pointers have no DSNodes, so they do nothing. - // - } else if (isa(C)) { - // - // ConstantDataSequential's are arrays of integers or floats, so they - // have no DSNodes. Nothing to do here. - // - } else { - llvm_unreachable("Unknown constant type!"); - } -} - -void GraphBuilder::mergeInGlobalInitializer(GlobalVariable *GV) { - // Ensure that the global variable is not external - assert(!GV->isDeclaration() && "Cannot merge in external global!"); - - // - // Get a node handle to the global node and merge the initializer into it. - // - DSNodeHandle NH = getValueDest(GV); - - // - // Ensure that the DSNode is large enough to hold the new constant that we'll - // be adding to it. - // - Type * ElementType = GV->getType()->getElementType(); - while(ArrayType *ATy = dyn_cast(ElementType)) { - ElementType = ATy->getElementType(); - } - if(!NH.getNode()->isNodeCompletelyFolded()) { - unsigned requiredSize = TD.getTypeAllocSize(ElementType) + NH.getOffset(); - if (NH.getNode()->getSize() < requiredSize){ - NH.getNode()->growSize (requiredSize); - } - } - - // - // Do the actual merging in of the constant initializer. - // - MergeConstantInitIntoNode(NH, GV->getType()->getElementType(), GV->getInitializer()); - -} - -void GraphBuilder::mergeExternalGlobal(GlobalVariable *GV) { - // Get a node handle to the global node and merge the initializer into it. - DSNodeHandle NH = getValueDest(GV); -} - -// some evil programs use sections as linker generated arrays -// read a description of this behavior in and apply it -// format: numglobals section globals... -// terminates when numglobals == 0 -void handleMagicSections(DSGraph* GlobalsGraph, Module& M) { - std::ifstream msf(hasMagicSections.c_str(), std::ifstream::in); - if (msf.good()) { - //no checking happens here - unsigned count = 0; - msf >> count; - while (count) { - std::string section; - msf >> section; - svset inSection; - for (Function &F : M) - if (F.hasSection() && F.getSection() == section) - inSection.insert(&F); - for (GlobalVariable &GV : M.globals()) - if (GV.hasSection() && GV.getSection() == section) - inSection.insert(&GV); - - for (unsigned x = 0; x < count; ++x) { - std::string global; - msf >> global; - Value* V = M.getNamedValue(global); - if (V) { - DSNodeHandle& DHV = GlobalsGraph->getNodeForValue(V); - for (svset::iterator SI = inSection.begin(), - SE = inSection.end(); SI != SE; ++SI) { - SDEBUG(errs() << "Merging " << V->getName().str() << " with " - << (*SI)->getName().str() << "\n"); - GlobalsGraph->getNodeForValue(*SI).mergeWith(DHV); - } - } - } - msf >> count; - } - } else { - errs() << "Failed to open magic sections file:" << hasMagicSections << - "\n"; - } -} - -char LocalDataStructures::ID; - -bool LocalDataStructures::runOnModule(Module &M) { - init(&M.getDataLayout()); - addrAnalysis = &getAnalysis(); - - // First step, build the globals graph. - { - GraphBuilder GGB(*GlobalsGraph, *this); - - // Add initializers for all of the globals to the globals graph. - for (GlobalVariable &GV : M.globals()) - if (!(GV.hasSection() && std::string(GV.getSection()) == "llvm.metadata")) { - if (GV.isDeclaration()) - GGB.mergeExternalGlobal(&GV); - else - GGB.mergeInGlobalInitializer(&GV); - } - // Add Functions to the globals graph. - for (Function &F : M) { - if(addrAnalysis->hasAddressTaken(&F)) { - GGB.mergeFunction(&F); - } - } - } - - if (hasMagicSections.size()) - handleMagicSections(GlobalsGraph, M); - - // Next step, iterate through the nodes in the globals graph, unioning - // together the globals into equivalence classes. - formGlobalECs(); - - // Iterate through the address taken functions in the globals graph, - // collecting them in a list, to be used as target for call sites that - // cant be resolved. - formGlobalFunctionList(); - GlobalsGraph->maskIncompleteMarkers(); - - // Calculate all of the graphs... - for (Function &F : M) - if (!F.isDeclaration()) { - DSGraph* G = new DSGraph(GlobalECs, getDataLayout(), *TypeSS, GlobalsGraph); - setDSGraph(F, G); - GraphBuilder GGB(F, *G, *this); - G->getAuxFunctionCalls() = G->getFunctionCalls(); - propagateUnknownFlag(G); - callgraph.insureEntry(&F); - G->buildCallGraph(callgraph, GlobalFunctionList, true); - G->maskIncompleteMarkers(); - G->markIncompleteNodes(DSGraph::MarkFormalArgs - |DSGraph::IgnoreGlobals); - cloneIntoGlobals(G, DSGraph::DontCloneCallNodes | - DSGraph::DontCloneAuxCallNodes | - DSGraph::StripAllocaBit); - formGlobalECs(); - SDEBUG(G->AssertGraphOK()); - } - - //GlobalsGraph->removeTriviallyDeadNodes(); - GlobalsGraph->markIncompleteNodes(DSGraph::MarkFormalArgs - |DSGraph::IgnoreGlobals); - GlobalsGraph->computeExternalFlags(DSGraph::ProcessCallSites); - - // Now that we've computed all of the graphs, and merged all of the info into - // the globals graph, see if we have further constrained the globals in the - // program if so, update GlobalECs and remove the extraneous globals from the - // program. - formGlobalECs(); - - propagateUnknownFlag(GlobalsGraph); - for (Function &F : M) - if (!F.isDeclaration()) { - DSGraph *Graph = getOrCreateGraph(&F); - Graph->maskIncompleteMarkers(); - cloneGlobalsInto(Graph, DSGraph::DontCloneCallNodes | - DSGraph::DontCloneAuxCallNodes); - Graph->markIncompleteNodes(DSGraph::MarkFormalArgs - |DSGraph::IgnoreGlobals); - } - - SDEBUG(print(errs(), &M)); - return false; -} diff --git a/lib/DSA/Printer.cpp b/lib/DSA/Printer.cpp deleted file mode 100644 index 1220888e9..000000000 --- a/lib/DSA/Printer.cpp +++ /dev/null @@ -1,415 +0,0 @@ -//===- Printer.cpp - Code for printing data structure graphs nicely -------===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements the 'dot' graph printer. -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "dsgraph-printer" - -#include "dsa/DataStructure.h" -#include "dsa/DSGraph.h" -#include "dsa/DSGraphTraits.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Constants.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/GraphWriter.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/Config/llvm-config.h" -#include "llvm/Support/FormattedStream.h" -#include -using namespace llvm; - -// OnlyPrintMain - The DataStructure printer exposes this option to allow -// printing of only the graph for "main". -// -namespace { - cl::list OnlyPrint("dsa-only-print", cl::ReallyHidden); - cl::opt DontPrintGraphs("dont-print-ds", cl::ReallyHidden); - cl::opt LimitPrint("dsa-limit-print", cl::Hidden); - STATISTIC (MaxGraphSize , "Maximum graph size"); - STATISTIC (NumFoldedNodes , "Number of folded nodes (in final graph)"); -} - -void DSNode::dump() const { print(errs(), 0); } -void DSNode::dumpParentGraph() const { getParentGraph()->dump(); } - -static std::string getCaption(const DSNode *N, const DSGraph *G) { - std::string empty; - raw_string_ostream OS(empty); - const Module *M = 0; - - if (!G) G = N->getParentGraph(); - - // Get the module from ONE of the functions in the graph it is available. - if (G && G->retnodes_begin() != G->retnodes_end()) - M = G->retnodes_begin()->first->getParent(); - if (M == 0 && G) { - // If there is a global in the graph, we can use it to find the module. - const DSScalarMap &SM = G->getScalarMap(); - if (SM.global_begin() != SM.global_end()) - M = (*SM.global_begin())->getParent(); - } - - if (N->isNodeCompletelyFolded()) - OS << "COLLAPSED"; - else { - if (N->type_begin() != N->type_end()) - for (DSNode::TyMapTy::const_iterator ii = N->type_begin(), - ee = N->type_end(); ii != ee; ++ii) { - OS << ii->first << ": "; - if (ii->second) - for (svset::const_iterator ni = ii->second->begin(), - ne = ii->second->end(); ni != ne; ++ni) { - Type * t = *ni; - t->print (OS); - OS << ", "; - } - else - OS << "VOID"; - OS << " "; - } - else - OS << "VOID"; - if (N->isArrayNode()) - OS << " array"; - } - if (unsigned NodeType = N->getNodeFlags()) { - OS << ": "; - if (NodeType & DSNode::AllocaNode ) OS << "S"; - if (NodeType & DSNode::HeapNode ) OS << "H"; - if (NodeType & DSNode::GlobalNode ) OS << "G"; - if (NodeType & DSNode::UnknownNode ) OS << "U"; - if (NodeType & DSNode::IncompleteNode ) OS << "I"; - if (NodeType & DSNode::ModifiedNode ) OS << "M"; - if (NodeType & DSNode::ReadNode ) OS << "R"; - if (NodeType & DSNode::ExternalNode ) OS << "E"; - if (NodeType & DSNode::ExternFuncNode ) OS << "X"; - if (NodeType & DSNode::IntToPtrNode ) OS << "P"; - if (NodeType & DSNode::PtrToIntNode ) OS << "2"; - if (NodeType & DSNode::VAStartNode ) OS << "V"; - -#ifndef NDEBUG - if (NodeType & DSNode::DeadNode ) OS << ""; -#endif - OS << "\n"; - } - - //Indicate if this is a VANode for some function - for (DSGraph::vanodes_iterator I = G->vanodes_begin(), E = G->vanodes_end(); - I != E; ++I) { - DSNodeHandle VANode = I->second; - if (N == VANode.getNode()) { - OS << "(VANode for " << I->first->getName().str() << ")\n"; - } - } - - EquivalenceClasses *GlobalECs = 0; - if (G) GlobalECs = &G->getGlobalECs(); - - for (DSNode::globals_iterator i = N->globals_begin(), e = N->globals_end(); - i != e; ++i) { - (*i)->printAsOperand(OS,false,M); - - // Figure out how many globals are equivalent to this one. - if (GlobalECs) { - EquivalenceClasses::iterator I = - GlobalECs->findValue(*i); - if (I != GlobalECs->end()) { - unsigned NumMembers = - std::distance(GlobalECs->member_begin(I), GlobalECs->member_end()); - if (NumMembers != 1) OS << " + " << (NumMembers-1) << " EC"; - } - } - OS << "\n"; - } - - return OS.str(); -} - -namespace llvm { -template<> -struct DOTGraphTraits : public DefaultDOTGraphTraits { - DOTGraphTraits(bool& b) {} - DOTGraphTraits() {} - static std::string getGraphName(const DSGraph *G) { - switch (G->getReturnNodes().size()) { - case 0: return G->getFunctionNames(); - case 1: return "Function " + G->getFunctionNames(); - default: return "Functions: " + G->getFunctionNames(); - } - } - - static std::string - getNodeLabel(const DSNode* Node, const DSGraph *Graph) { - return getCaption(Node, Graph); - } - - static std::string getNodeAttributes(const DSNode *N, const DSGraph *Graph) { - return "shape=Mrecord"; - } - - static bool edgeTargetsEdgeSource(const void *Node, - DSNode::const_iterator I) { - if (I.getOffset() < I->getSize()) { - if (I->hasLink (I.getOffset())) { - unsigned O = I->getLink(I.getOffset()).getOffset(); - return O != 0; - } else { - return false; - } - } else { - return false; - } - } - - static std::string getEdgeSourceLabel(const void* Node, - DSNode::const_iterator I) { - std::string S; - llvm::raw_string_ostream O(S); - O << I.getOffset(); - return O.str(); - } - - static DSNode::const_iterator getEdgeTarget(const DSNode *Node, - DSNode::const_iterator I) { - unsigned O = I->getLink(I.getOffset()).getOffset(); - unsigned LinkNo = O; - const DSNode *N = *I; - DSNode::const_iterator R = N->begin(); - for (; LinkNo; --LinkNo) - ++R; - return R; - } - - - /// addCustomGraphFeatures - Use this graph writing hook to emit call nodes - /// and the return node. - /// - static void addCustomGraphFeatures(const DSGraph *G, - GraphWriter &GW) { - const Module *CurMod = 0; - if (G->retnodes_begin() != G->retnodes_end()) - CurMod = G->retnodes_begin()->first->getParent(); - else { - // If there is a global in the graph, we can use it to find the module. - const DSScalarMap &SM = G->getScalarMap(); - if (SM.global_begin() != SM.global_end()) - CurMod = (*SM.global_begin())->getParent(); - } - - - if (!LimitPrint) { - // Add scalar nodes to the graph... - const DSGraph::ScalarMapTy &VM = G->getScalarMap(); - for (DSGraph::ScalarMapTy::const_iterator I = VM.begin(); - I != VM.end(); ++I) - if (!isa(I->first)) { - std::string OS_str; - llvm::raw_string_ostream OS(OS_str); - I->first->printAsOperand(OS, false, CurMod); - GW.emitSimpleNode(I->first, "", OS.str()); - - // Add edge from return node to real destination - DSNode *DestNode = I->second.getNode(); - int EdgeDest = I->second.getOffset(); - if (EdgeDest == 0) EdgeDest = -1; - GW.emitEdge(I->first, -1, DestNode, - EdgeDest, "arrowtail=tee,color=gray63"); - } - } - - - // Output the returned value pointer... - for (DSGraph::retnodes_iterator I = G->retnodes_begin(), - E = G->retnodes_end(); I != E; ++I) - if (I->second.getNode()) { - std::string Label; - if (G->getReturnNodes().size() == 1) - Label = "returning"; - else - Label = I->first->getName().str() + " ret node"; - // Output the return node... - GW.emitSimpleNode(const_cast(I->first), "plaintext=circle", Label); - - // Add edge from return node to real destination - DSNode *RetNode = I->second.getNode(); - int RetEdgeDest = I->second.getOffset(); - if (RetEdgeDest == 0) RetEdgeDest = -1; - GW.emitEdge(const_cast(I->first), -1, RetNode, - RetEdgeDest, "arrowtail=tee,color=gray63"); - } - - // Output all of the call nodes... - const DSGraph::FunctionListTy &FCs = - G->shouldUseAuxCalls() ? G->getAuxFunctionCalls() - : G->getFunctionCalls(); - for (DSGraph::FunctionListTy::const_iterator I = FCs.begin(), E = FCs.end(); - I != E; ++I) { - const DSCallSite &Call = *I; - std::vector EdgeSourceCaptions(Call.getNumPtrArgs()+2); - EdgeSourceCaptions[0] = "r"; - if (Call.isDirectCall()) - EdgeSourceCaptions[1] = Call.getCalleeFunc()->getName(); - else - EdgeSourceCaptions[1] = "f"; - - GW.emitSimpleNode(&Call, "shape=record", "call", Call.getNumPtrArgs()+2, - &EdgeSourceCaptions); - - if (DSNode *N = Call.getRetVal().getNode()) { - int EdgeDest = Call.getRetVal().getOffset(); - if (EdgeDest == 0) EdgeDest = -1; - GW.emitEdge(&Call, 0, N, EdgeDest, "color=gray63,tailclip=false"); - } - - // FIXME: visualize the VANode? - - // Print out the callee... - if (Call.isIndirectCall()) { - DSNode *N = Call.getCalleeNode(); - assert(N && "Null call site callee node!"); - GW.emitEdge(&Call, 1, N, -1, "color=gray63,tailclip=false"); - } - - for (unsigned j = 0, e = Call.getNumPtrArgs(); j != e; ++j) - if (DSNode *N = Call.getPtrArg(j).getNode()) { - int EdgeDest = Call.getPtrArg(j).getOffset(); - if (EdgeDest == 0) EdgeDest = -1; - GW.emitEdge(&Call, j+2, N, EdgeDest, "color=gray63,tailclip=false"); - } - } - } -}; -} // end namespace llvm - -void DSNode::print(llvm::raw_ostream &O, const DSGraph *G) const { - GraphWriter W(O, G, false); - W.writeNode(this); -} - -void DSGraph::print(llvm::raw_ostream &O) const { - WriteGraph(O, this); -} - -void DSGraph::writeGraphToFile(llvm::raw_ostream &O, - const std::string &GraphName) const { - std::string Filename = GraphName + ".dot"; - O << "Writing '" << Filename << "'..."; - if (!DontPrintGraphs) { - std::error_code EC; - llvm::raw_fd_ostream F(Filename.c_str(), EC, sys::fs::F_None); - - if (EC) { - O << " error opening file for writing! " << EC.message() << "\n"; - return; - } - - print(F); - } else { - O << "(disabled by command-line flag)"; - } - unsigned NumCalls = shouldUseAuxCalls() ? - getAuxFunctionCalls().size() : getFunctionCalls().size(); - O << " [" << getGraphSize() << "+" << NumCalls << "]\n"; -} - -/// viewGraph - Emit a dot graph, run 'dot', run gv on the postscript file, -/// then cleanup. For use from the debugger. -/// -void DSGraph::viewGraph() const { - ViewGraph(this, "ds.tempgraph"); -} - - -template -static void printCollection(const Collection &C, llvm::raw_ostream &O, - const Module *M, const std::string &Prefix) { - if (M == 0) { - O << "Null Module pointer, cannot continue!\n"; - return; - } - - unsigned TotalNumNodes = 0, TotalCallNodes = 0; - for (Module::const_iterator I = M->begin(), E = M->end(); I != E; ++I) - if (C.hasDSGraph(*I)) { - DSGraph* Gr = C.getDSGraph((const Function&)*I); - unsigned NumCalls = Gr->shouldUseAuxCalls() ? - Gr->getAuxFunctionCalls().size() : Gr->getFunctionCalls().size(); - bool IsDuplicateGraph = false; - - //if no only print options, print everything - bool doPrint = OnlyPrint.begin() == OnlyPrint.end(); - //otherwise check the name - if (!doPrint) - doPrint = OnlyPrint.end() != - std::find(OnlyPrint.begin(), OnlyPrint.end(), I->getName().str()); - - if (doPrint) { - const Function *SCCFn = Gr->retnodes_begin()->first; - if (&*I == SCCFn) { - Gr->writeGraphToFile(O, Prefix+I->getName().str()); - } else { - IsDuplicateGraph = true; // Don't double count node/call nodes. - O << "Didn't write '" << Prefix+I->getName().str() - << ".dot' - Graph already emitted to '" << Prefix+SCCFn->getName().str() - << "\n"; - } - } else { - const Function *SCCFn = Gr->retnodes_begin()->first; - if (&*I == SCCFn) { - //O << "Skipped Writing '" << Prefix+I->getName().str() << ".dot'... [" - // << Gr->getGraphSize() << "+" << NumCalls << "]\n"; - } else { - IsDuplicateGraph = true; // Don't double count node/call nodes. - } - } - - if (!IsDuplicateGraph) { - unsigned GraphSize = Gr->getGraphSize(); - if (MaxGraphSize < GraphSize) MaxGraphSize = GraphSize; - - TotalNumNodes += Gr->getGraphSize(); - TotalCallNodes += NumCalls; - for (DSGraph::node_iterator NI = Gr->node_begin(), E = Gr->node_end(); - NI != E; ++NI) - if (NI->isNodeCompletelyFolded()) - ++NumFoldedNodes; - } - } - - DSGraph* GG = C.getGlobalsGraph(); - TotalNumNodes += GG->getGraphSize(); - TotalCallNodes += GG->getFunctionCalls().size(); - GG->writeGraphToFile(O, Prefix + "GlobalsGraph"); - - O << "\nGraphs contain [" << TotalNumNodes << "+" << TotalCallNodes - << "] nodes total\n"; -} - - -void DataStructures::dumpCallGraph() const { - for (DSCallGraph::flat_key_iterator ki = callgraph.flat_key_begin(), - ke = callgraph.flat_key_end(); ki != ke; ++ki) { - errs() << (*ki)->getName() << ": ["; - for (DSCallGraph::flat_iterator cbi = callgraph.flat_callee_begin(*ki), - cbe = callgraph.flat_callee_end(*ki); cbi != cbe; ++cbi) - errs() << (*cbi)->getName() << " "; - errs() << "]\n"; - } -} - -// print - Print out the analysis results... -void DataStructures::print(llvm::raw_ostream &O, const Module *M) const { - if (handleTest(O, M)) return; - - printCollection(*this, O, M, printname); - //dumpCallGraph(); -} diff --git a/lib/DSA/README b/lib/DSA/README deleted file mode 100644 index 48cb2355b..000000000 --- a/lib/DSA/README +++ /dev/null @@ -1,15 +0,0 @@ -DSA is changed since PLDI07 in (at least) the following ways: - -1) DSA tracks types per offset. -2) DSA does not assume that all clients will want to collapse a node if types - conflict. Many clients don't care that an offset is used as an int or a - double, they just care about the points-to result. - -In Progress: - -1) DSA handles multiple entry points in a module. -2) Optional assumption that only legal targets are called at indirect call sites -3) Positional arguments, rather than pointer relative arguments. This handles - the case where pointer and ints are assumed compatible and function pointers - containing them are cast freely. - diff --git a/lib/DSA/SanityCheck.cpp b/lib/DSA/SanityCheck.cpp deleted file mode 100644 index 2f187a39b..000000000 --- a/lib/DSA/SanityCheck.cpp +++ /dev/null @@ -1,140 +0,0 @@ -//===- SanityCheck.cpp - Check sanity and consistency of DSA results ------===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Perform a sanity check on the DSA results. -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "dsa-check" - -#include "dsa/DataStructure.h" -#include "dsa/DSGraph.h" - -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" - -using namespace llvm; - -// -// Class: CheckSanity -// -// Description: -// This is an LLVM pass that will request DSA results and then do some sanity -// checks on the DSA results. -// -class SanityCheck : public ModulePass { - public: - static char ID; - SanityCheck() : ModulePass (ID) { - dsaPass = 0; - } - virtual bool runOnModule(Module &M); - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired(); - AU.setPreservesAll(); - } - - protected: - void checkLoad (LoadInst & LI); - - // The DSA pass which is to be sanitized - DataStructures * dsaPass; -}; - -// Sanity Check Pass ID -char SanityCheck::ID = 0; - -static RegisterPass -X("dsa-check", "Check consistency of Data Structure Analysis"); - -// -// Method: checkLoad() -// -// Description: -// This method ensures that the DSNode associated with the result of a load -// instruction is linked from the DSNode associated with the pointer -// dereferenced by the load. -// -void -SanityCheck::checkLoad (LoadInst & LI) { - // - // Get the DSA Graph for the function in which the load instruction lives. - // - Function * F = LI.getParent()->getParent(); - DSGraph * G = dsaPass->getDSGraph (*F); - - // - // Get the DSNode information for both the result of the load instruction - // and its pointer operand. If the result of the load has no DSNode, then - // nothing needs to be done. - // - DSNode* ResultNode = G->getNodeForValue (&LI).getNode(); - DSNode* PtrNode = G->getNodeForValue (LI.getPointerOperand()).getNode(); - if (!ResultNode) { - return; - } - assert (PtrNode && "Load operand has no DSNode!\n"); - - // - // Scan through the links of the DSNode of the load's pointer operand; we - // need to determine the offset into the memory object from which the result - // of the load is loaded. - // - DSNode::LinkMapTy::iterator linki = PtrNode->edge_begin(); -#ifndef NDEBUG - bool found = false; -#endif - for (; linki != PtrNode->edge_end(); ++linki) { - DSNodeHandle & LinkNode = linki->second; - if (LinkNode.getNode() == ResultNode) { -#ifndef NDEBUG - found = true; -#endif - } - } - - assert (found && "Can't find link for load result!\n"); - return; -} - -// -// Method: runOnModule() -// -// Description: -// This method is the entry point for the sanity checking pass. -// -// Inputs: -// M - The LLVM module which should be analyzed for sanity and consistency. -// -// Return value: -// false - The Module has not been modified (this pass should never modify the -// Module). -// -bool -SanityCheck::runOnModule(Module & M) { - // - // Get a reference to the DSA pass to check. - // - dsaPass = &getAnalysis(); - - // - // Scan through all instructions in the module and sanity check the load - // instructions. - // - for (Module::iterator F = M.begin(); F != M.end(); ++F) { - for (Function::iterator BB = F->begin(); BB != F->end(); ++BB) { - for (BasicBlock::iterator I = BB->begin(); I != BB->end(); ++I) { - if (LoadInst * LI = dyn_cast(I)) - checkLoad (*LI); - } - } - } - - return false; -} diff --git a/lib/DSA/StdLibPass.cpp b/lib/DSA/StdLibPass.cpp deleted file mode 100644 index f5b7726c3..000000000 --- a/lib/DSA/StdLibPass.cpp +++ /dev/null @@ -1,911 +0,0 @@ -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Recognize common standard c library functions and generate graphs for them -// FIXME: Move table to separate analysis pass, so that even the Local Pass -// may query it. -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "stdlib-pass" -#include "llvm/ADT/Statistic.h" -#include "dsa/DataStructure.h" -#include "dsa/AllocatorIdentification.h" -#include "dsa/DSGraph.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Intrinsics.h" -#include "llvm/IR/GetElementPtrTypeIterator.h" -#include "llvm/IR/DataLayout.h" -#include "llvm/Support/CommandLine.h" -#include "smack/Debug.h" -#include "llvm/Support/FormattedStream.h" -#include "llvm/Support/Timer.h" -#include -#include "llvm/IR/Module.h" - -using namespace llvm; - -static RegisterPass -X("dsa-stdlib", "Standard Library Local Data Structure Analysis"); - -STATISTIC(NumNodesFoldedInStdLib, "Number of nodes folded in std lib"); - -char StdLibDataStructures::ID; - -#define numOps 10 -namespace { - static cl::opt noStdLibFold("dsa-stdlib-no-fold", - cl::desc("Don't fold nodes in std-lib."), - cl::Hidden, - cl::init(false)); - static cl::opt DisableStdLib("disable-dsa-stdlib", - cl::desc("Don't use DSA's stdlib pass."), - cl::Hidden, - cl::init(false)); -} - -// -// Structure: libAction -// -// Description: -// Describe how the DSGraph of a function should be built. Note that for the -// boolean arrays of arity numOps, the first element is a flag describing the -// return value, and the remaining elements are flags describing the -// function's arguments. -// -struct libAction { - // The return value/arguments that should be marked read. - bool read[numOps]; - - // The return value/arguments that should be marked modified. - bool write[numOps]; - - // The return value/arguments that should be marked as heap. - bool heap[numOps]; - - // Flags whether the return value should be merged with all arguments. - bool mergeNodes[numOps]; - - // Flags whether the return value and arguments should be folded. - bool collapse; -}; - -#define NRET_NARGS {0,0,0,0,0,0,0,0,0,0} -#define YRET_NARGS {1,0,0,0,0,0,0,0,0,0} -#define NRET_YARGS {0,1,1,1,1,1,1,1,1,1} -#define YRET_YARGS {1,1,1,1,1,1,1,1,1,1} -#define NRET_NYARGS {0,0,1,1,1,1,1,1,1,1} -#define YRET_NYARGS {1,0,1,1,1,1,1,1,1,1} -#define NRET_YNARGS {0,1,0,0,0,0,0,0,0,0} -#define YRET_YNARGS {1,1,0,0,0,0,0,0,0,0} -#define YRET_NNYARGS {1,0,0,1,1,1,1,1,1,1} -#define YRET_YNYARGS {1,1,0,1,1,1,1,1,1,1} -#define NRET_NNYARGS {0,0,0,1,1,1,1,1,1,1} -#define YRET_NNYNARGS {1,0,0,1,0,0,0,0,0,0} -#define NRET_NNNYARGS {0,0,0,0,1,1,1,1,1,1} - -const struct { - const char* name; - libAction action; -} recFuncs[] = { - {"stat", {NRET_YNARGS, NRET_NYARGS, NRET_NARGS, NRET_NARGS, false}}, - {"fstat", {NRET_YNARGS, NRET_NYARGS, NRET_NARGS, NRET_NARGS, false}}, - {"lstat", {NRET_YNARGS, NRET_NYARGS, NRET_NARGS, NRET_NARGS, false}}, - - {"getenv", {NRET_YNARGS, YRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"getrusage", {NRET_YNARGS, YRET_NYARGS, NRET_NARGS, NRET_NARGS, false}}, - {"getrlimit", {NRET_YNARGS, YRET_NYARGS, NRET_NARGS, NRET_NARGS, false}}, - {"setrlimit", {NRET_YARGS, YRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"getcwd", {NRET_NYARGS, YRET_YNARGS, NRET_NARGS, YRET_YNARGS, false}}, - - {"select", {NRET_YARGS, YRET_YNARGS, NRET_NARGS, NRET_NARGS, false}}, - {"_setjmp", {NRET_YARGS, YRET_YARGS, NRET_NARGS, NRET_NARGS, false}}, - {"longjmp", {NRET_YARGS, NRET_YARGS, NRET_NARGS, NRET_NARGS, false}}, - - {"remove", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"rename", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"unlink", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"fileno", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"create", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"write", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"read", {NRET_YARGS, YRET_YARGS, NRET_NARGS, NRET_NARGS, false}}, - {"truncate", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"open", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - - {"chdir", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"mkdir", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"rmdir", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - - {"chmod", {NRET_YARGS, YRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"fchmod", {NRET_YARGS, YRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - - {"kill", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pipe", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - - {"execl", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"execlp", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"execle", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"execv", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"execvp", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - - {"time", {NRET_YARGS, YRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"times", {NRET_YARGS, YRET_YARGS, NRET_NARGS, NRET_NARGS, false}}, - {"ctime", {NRET_YARGS, YRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"asctime", {NRET_YARGS, YRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"utime", {NRET_YARGS, YRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"localtime", {NRET_YARGS, YRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"gmtime", {NRET_YARGS, YRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"ftime", {NRET_YARGS, NRET_YARGS, NRET_NARGS, NRET_NARGS, false}}, - - // printf not strictly true, %n could cause a write - {"printf", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"fprintf", {NRET_YARGS, NRET_YNARGS, NRET_NARGS, NRET_NARGS, false}}, - {"fprintf", {NRET_YARGS, NRET_YNARGS, NRET_NARGS, NRET_NARGS, false}}, - {"sprintf", {NRET_YARGS, NRET_YNARGS, NRET_NARGS, NRET_NARGS, false}}, - {"snprintf", {NRET_YARGS, NRET_YNARGS, NRET_NARGS, NRET_NARGS, false}}, - {"vsnprintf", {NRET_YARGS, YRET_YNARGS, NRET_NARGS, NRET_NARGS, false}}, - {"sscanf", {NRET_YARGS, YRET_NYARGS, NRET_NARGS, NRET_NARGS, false}}, - {"scanf", {NRET_YARGS, YRET_NYARGS, NRET_NARGS, NRET_NARGS, false}}, - {"fscanf", {NRET_YARGS, YRET_NYARGS, NRET_NARGS, NRET_NARGS, false}}, - - {"calloc", {NRET_NARGS, YRET_NARGS, YRET_NARGS, NRET_NARGS, false}}, - {"malloc", {NRET_NARGS, YRET_NARGS, YRET_NARGS, NRET_NARGS, false}}, - {"valloc", {NRET_NARGS, YRET_NARGS, YRET_NARGS, NRET_NARGS, false}}, - {"realloc", {NRET_NARGS, YRET_NARGS, YRET_YNARGS, YRET_YNARGS,false}}, - {"free", {NRET_NARGS, NRET_NARGS, NRET_YNARGS, NRET_NARGS, false}}, - - {"strdup", {NRET_YARGS, YRET_NARGS, YRET_NARGS, YRET_YARGS, false}}, - {"__strdup", {NRET_YARGS, YRET_NARGS, YRET_NARGS, YRET_YARGS, false}}, - {"wcsdup", {NRET_YARGS, YRET_NARGS, YRET_NARGS, YRET_YARGS, false}}, - - {"strlen", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"wcslen", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - - {"atoi", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"atof", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"atol", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"atoll", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"atoq", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - - {"memcmp", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"strcmp", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"wcscmp", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"strncmp", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"wcsncmp", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"strcasecmp", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"wcscasecmp", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"strncasecmp", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"wcsncasecmp", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - - {"strcat", {NRET_YARGS, YRET_YARGS, NRET_NARGS, YRET_YARGS, true}}, - {"strncat", {NRET_YARGS, YRET_YARGS, NRET_NARGS, YRET_YARGS, true}}, - - {"strcpy", {NRET_YARGS, YRET_YARGS, NRET_NARGS, YRET_YARGS, true}}, - {"stpcpy", {NRET_YARGS, YRET_YARGS, NRET_NARGS, YRET_YARGS, true}}, - {"wcscpy", {NRET_YARGS, YRET_YARGS, NRET_NARGS, YRET_YARGS, true}}, - {"strncpy", {NRET_YARGS, YRET_YARGS, NRET_NARGS, YRET_YARGS, true}}, - {"wcsncpy", {NRET_YARGS, YRET_YARGS, NRET_NARGS, YRET_YARGS, true}}, - {"memcpy", {NRET_YARGS, YRET_YARGS, NRET_NARGS, YRET_YARGS, true}}, - {"memccpy", {NRET_YARGS, YRET_YARGS, NRET_NARGS, YRET_YARGS, true}}, - {"wmemccpy", {NRET_YARGS, YRET_YARGS, NRET_NARGS, YRET_YARGS, true}}, - {"memmove", {NRET_YARGS, YRET_YARGS, NRET_NARGS, YRET_YARGS, true}}, - - {"bcopy", {NRET_YARGS, NRET_YARGS, NRET_NARGS, NRET_YARGS, true}}, - {"bcmp", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, true}}, - - {"strerror", {NRET_YARGS, YRET_NARGS, NRET_NARGS, NRET_NARGS, true}}, - {"clearerr", {NRET_YARGS, NRET_YARGS, NRET_NARGS, NRET_NARGS, false}}, - {"strstr", {NRET_YARGS, YRET_NARGS, NRET_NARGS, YRET_YNARGS, true}}, - {"wcsstr", {NRET_YARGS, YRET_NARGS, NRET_NARGS, YRET_YNARGS, true}}, - {"strspn", {NRET_YARGS, YRET_NARGS, NRET_NARGS, NRET_NARGS, true}}, - {"wcsspn", {NRET_YARGS, YRET_NARGS, NRET_NARGS, NRET_NARGS, true}}, - {"strcspn", {NRET_YARGS, YRET_NARGS, NRET_NARGS, NRET_NARGS, true}}, - {"wcscspn", {NRET_YARGS, YRET_NARGS, NRET_NARGS, NRET_NARGS, true}}, - {"strtok", {NRET_YARGS, YRET_YARGS, NRET_NARGS, YRET_YNARGS, true}}, - {"strpbrk", {NRET_YARGS, YRET_NARGS, NRET_NARGS, YRET_YNARGS, true}}, - {"wcspbrk", {NRET_YARGS, YRET_NARGS, NRET_NARGS, YRET_YNARGS, true}}, - - {"strchr", {NRET_YARGS, YRET_NARGS, NRET_NARGS, YRET_YNARGS, true}}, - {"wcschr", {NRET_YARGS, YRET_NARGS, NRET_NARGS, YRET_YNARGS, true}}, - {"strrchr", {NRET_YARGS, YRET_NARGS, NRET_NARGS, YRET_YNARGS, true}}, - {"wcsrchr", {NRET_YARGS, YRET_NARGS, NRET_NARGS, YRET_YNARGS, true}}, - {"strchrnul", {NRET_YARGS, YRET_NARGS, NRET_NARGS, YRET_YNARGS, true}}, - {"wcschrnul", {NRET_YARGS, YRET_NARGS, NRET_NARGS, YRET_YNARGS, true}}, - - {"memchr", {NRET_YARGS, YRET_NARGS, NRET_NARGS, YRET_YNARGS, true}}, - {"wmemchr", {NRET_YARGS, YRET_NARGS, NRET_NARGS, YRET_YNARGS, true}}, - {"memrchr", {NRET_YARGS, YRET_NARGS, NRET_NARGS, YRET_YNARGS, true}}, - - {"memalign", {NRET_NARGS, YRET_NARGS, YRET_NARGS, NRET_NARGS, false}}, - //{"posix_memalign", {NRET_YARGS, YRET_YNARGS, NRET_NARGS, NRET_NARGS, false}}, - - {"perror", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - - {"feof", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"fflush", {NRET_YARGS, NRET_YARGS, NRET_NARGS, NRET_NARGS, false}}, - {"fpurge", {NRET_YARGS, NRET_YARGS, NRET_NARGS, NRET_NARGS, false}}, - {"fclose", {NRET_YARGS, NRET_YARGS, NRET_NARGS, NRET_NARGS, false}}, - {"fopen", {NRET_YARGS, YRET_NARGS, YRET_NARGS, NRET_NARGS, false}}, - {"ftell", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"fseek", {NRET_YARGS, NRET_YARGS, NRET_NARGS, NRET_NARGS, true}}, - {"rewind", {NRET_YARGS, NRET_YARGS, NRET_NARGS, NRET_NARGS, true}}, - {"ferror", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"fwrite", {NRET_YARGS, NRET_NYARGS,NRET_NARGS, NRET_NARGS, false}}, - {"fread", {NRET_NYARGS, NRET_YARGS, NRET_NARGS, NRET_NARGS, false}}, - {"fdopen", {NRET_YARGS, YRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - - {"__errno_location", {NRET_NARGS, YRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - - {"puts", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"gets", {NRET_NARGS, YRET_YARGS, NRET_NARGS, YRET_YNARGS, false}}, - {"fgets", {NRET_NYARGS, YRET_YNARGS, NRET_NARGS, YRET_YNARGS, false}}, - {"getc", {NRET_YNARGS, YRET_YNARGS, NRET_NARGS, NRET_NARGS, false}}, - {"ungetc", {NRET_YNARGS, YRET_YARGS, NRET_NARGS, NRET_NARGS, false}}, - {"_IO_getc", {NRET_YNARGS, YRET_YNARGS, NRET_NARGS, NRET_NARGS, false}}, - {"fgetc", {NRET_YNARGS, YRET_YNARGS, NRET_NARGS, NRET_NARGS, false}}, - {"putc", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"_IO_putc", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"putchar", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"fputs", {NRET_YARGS, NRET_NYARGS, NRET_NARGS, NRET_NARGS, false}}, - {"fputc", {NRET_YARGS, NRET_NYARGS, NRET_NARGS, NRET_NARGS, false}}, - - - // SAFECode Intrinsics - {"pool_init_logfile",{NRET_YNARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"poolcheck", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"poolcheckui", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"poolcheckstr", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"poolcheckstrui", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"fastlscheck", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"poolcheckalign", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"poolcheckalignui", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"poolcheck_free", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"poolcheck_freeui", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"funccheck", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"funccheckui", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - - {"poolcheck_debug", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"poolcheckui_debug", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"poolcheckstr_debug", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"poolcheckstrui_debug", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"fastlscheck_debug", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"poolcheckalign_debug", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"poolcheckalignui_debug", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"poolcheck_free_debug", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"poolcheck_freeui_debug", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"funccheck_debug", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"funccheckui_debug",{NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - - {"pool_register_stack", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_unregister_stack", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_register_global", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_unregister_global", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_register", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_unregister", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_argvregister", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - - {"pool_register_stack_debug", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_unregister_stack_debug", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_register_global_debug", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_unregister_global_debug", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_register_debug", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_unregister_debug", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - - // CIF Intrinsics - {"__if_pool_get_label", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"__if_pool_set_label", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - // CStdLib Runtime Wrapper Functions - {"pool_strncpy", {NRET_NNYARGS, YRET_NNYARGS, NRET_NARGS, YRET_NNYARGS, true}}, - {"pool_strcpy", {NRET_NNYARGS, YRET_NNYARGS, NRET_NARGS, YRET_NNYARGS, true}}, - {"pool_stpcpy", {NRET_NNYARGS, YRET_NNYARGS, NRET_NARGS, YRET_NNYARGS, true}}, - // strchr and index have same functionality - {"pool_strchr", {NRET_NYARGS, YRET_NARGS, NRET_NARGS, YRET_NYARGS, true}}, - {"pool_index", {NRET_NYARGS, YRET_NARGS, NRET_NARGS, YRET_NYARGS, true}}, - // strrchr and rindex have same functionality - {"pool_strrchr", {NRET_NYARGS, YRET_NARGS, NRET_NARGS, YRET_NYARGS, true}}, - {"pool_rindex", {NRET_NYARGS, YRET_NARGS, NRET_NARGS, YRET_NYARGS, true}}, - {"pool_strcat", {NRET_NNYARGS, YRET_NNYARGS, NRET_NARGS, YRET_NNYARGS, true}}, - {"pool_strncat", {NRET_NNYARGS, YRET_NNYARGS, NRET_NARGS, YRET_NNYARGS, true}}, - {"pool_strstr", {NRET_NNYARGS, YRET_NARGS, NRET_NARGS, YRET_NNYNARGS, true}}, - {"pool_strcasestr", {NRET_NNYARGS, YRET_NARGS, NRET_NARGS, YRET_NNYNARGS, true}}, - {"pool_strpbrk", {NRET_NNYARGS, YRET_NARGS, NRET_NARGS, YRET_NNYNARGS, true}}, - {"pool_strspn", {NRET_NYARGS, YRET_NARGS, NRET_NARGS, NRET_NARGS, true}}, - {"pool_strcspn", {NRET_NYARGS, YRET_NARGS, NRET_NARGS, NRET_NARGS, true}}, - {"pool_memccpy", {NRET_NNYARGS, YRET_NNYARGS, NRET_NARGS, YRET_NNYARGS, true}}, - {"pool_memchr", {NRET_NYARGS, YRET_NARGS, NRET_NARGS, YRET_NYARGS, true}}, - {"pool_strcmp", {NRET_NNYARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_strncmp", {NRET_NNYARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_strlen", {NRET_NYARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_strnlen", {NRET_NYARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_memcmp", {NRET_NNYARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_strcasecmp", {NRET_NNYARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_strncasecmp",{NRET_NNYARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_bcopy", {NRET_NNYARGS, NRET_NNNYARGS, NRET_NARGS, NRET_NNYARGS, true}}, - {"pool_bcmp", {NRET_NNYARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_read", {NRET_NARGS, NRET_YARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_recv", {NRET_NARGS, NRET_YARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_write", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_send", {NRET_YARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_readlink", {NRET_YNARGS, NRET_NYARGS, NRET_NARGS, NRET_NARGS, true}}, - {"pool_realpath", {NRET_YNARGS, NRET_NYARGS, NRET_NARGS, NRET_NARGS, true}}, - {"pool_getcwd", {NRET_YARGS, NRET_NYARGS, NRET_NARGS, YRET_NYARGS, false}}, - - // format string intrinsics and functions - {"sc.fsparameter", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"sc.fscallinfo", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"sc.fscallinfo_debug",{NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_printf", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_fprintf", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_sprintf", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_snprintf", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_err", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_errx", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_warn", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_warnx", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_syslog", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_scanf", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_fscanf", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool_sscanf", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool___printf_chk", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool___fprintf_chk", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool___sprintf_chk", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"pool___snprintf_chk", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - - // Important C I/O functions - {"pool_fgets", {NRET_NNYARGS, YRET_YNYARGS, NRET_NARGS, YRET_YNARGS, true}}, - {"pool_fgets_debug",{NRET_NNYARGS, YRET_YNYARGS, NRET_NARGS, YRET_YNARGS, true}}, - - // Type Checks - {"trackArgvType", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackEnvpType", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackGlobal", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackArray", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackStoreInst", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackStringInput", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"compareTypeAndNumber", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"compareVAArgType", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"getTypeTag", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"checkType", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackInitInst", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackUnInitInst", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"copyTypeInfo", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"setTypeInfo", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"setVAInfo", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"copyVAInfo", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackctype", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackctype_32", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackStrcpyInst", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackStrcnpyInst", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackStrcatInst", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackgetcwd", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackgetpwuid", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackgethostname", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackgethostbyname", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackgetservbyname", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackgetaddrinfo", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackgetsockname", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackaccept", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackpoll", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackpipe", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - {"trackReadLink", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - -#if 0 - {"wait", {false, false, false, false, true, false, false, false, false}}, -#endif - - // C++ functions, as mangled on linux gcc 4.2 - // operator new(unsigned long) - {"_Znwm", {NRET_NARGS, YRET_NARGS, YRET_NARGS, NRET_NARGS, false}}, - // operator new[](unsigned long) - {"_Znam", {NRET_NARGS, YRET_NARGS, YRET_NARGS, NRET_NARGS, false}}, - // operator new(unsigned int) - {"_Znwj", {NRET_NARGS, YRET_NARGS, YRET_NARGS, NRET_NARGS, false}}, - // operator new[](unsigned int) - {"_Znaj", {NRET_NARGS, YRET_NARGS, YRET_NARGS, NRET_NARGS, false}}, - // operator delete(void*) - {"_ZdlPv", {NRET_NARGS, NRET_NARGS, NRET_YNARGS,NRET_NARGS, false}}, - // operator delete[](void*) - {"_ZdaPv", {NRET_NARGS, NRET_NARGS, NRET_YNARGS, NRET_NARGS, false}}, - // flush - {"_ZNSo5flushEv", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - // << operator - {"_ZNSolsEd", {NRET_YARGS, NRET_YNARGS, NRET_NARGS, NRET_NARGS, false}}, - // << operator - {"_ZNSolsEPFRSoS_E", {NRET_YARGS, NRET_YNARGS, NRET_NARGS, NRET_NARGS, false}}, - //endl - {"_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_", {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, - // Added by Jingyue - {"strtoll", {NRET_YARGS, NRET_NYARGS, NRET_NYARGS, NRET_YARGS, false}}, - // Terminate the list of special functions recognized by this pass - {0, {NRET_NARGS, NRET_NARGS, NRET_NARGS, NRET_NARGS, false}}, -}; - -/* - Functions to add - freopen - strftime - strtoul - strtol - strtoll - ctype family - setbuf - setvbuf - __strpbrk_c3 - open64/fopen64/lseek64 - */ - -// -// Method: eraseCallsTo() -// -// Description: -// This method removes the specified function from DSCallsites within the -// specified function. We do not do anything with call sites that call this -// function indirectly (for which there is not much point as we do not yet -// know the targets of indirect function calls). -// -void -StdLibDataStructures::eraseCallsTo(Function* F) { - typedef std::pair RemovalPair; - DenseSet ToRemove; - for (Value::user_iterator ii = F->user_begin(), ee = F->user_end(); - ii != ee; ++ii) - if (CallInst* CI = dyn_cast(*ii)){ - if (CI->getCalledValue() == F) { - DSGraph* Graph = getDSGraph(*CI->getParent()->getParent()); - //delete the call - SDEBUG(errs() << "Removing " << F->getName().str() << " from " - << CI->getParent()->getParent()->getName().str() << "\n"); - ToRemove.insert(std::make_pair(Graph, F)); - } - }else if (InvokeInst* CI = dyn_cast(*ii)){ - if (CI->getCalledValue() == F) { - DSGraph* Graph = getDSGraph(*CI->getParent()->getParent()); - //delete the call - SDEBUG(errs() << "Removing " << F->getName().str() << " from " - << CI->getParent()->getParent()->getName().str() << "\n"); - ToRemove.insert(std::make_pair(Graph, F)); - } - } else if(ConstantExpr *CE = dyn_cast(*ii)) { - if(CE->isCast()) { - for (Value::user_iterator ci = CE->user_begin(), ce = CE->user_end(); - ci != ce; ++ci) { - if (CallInst* CI = dyn_cast(*ci)){ - if(CI->getCalledValue() == CE) { - DSGraph* Graph = getDSGraph(*CI->getParent()->getParent()); - //delete the call - SDEBUG(errs() << "Removing " << F->getName().str() << " from " - << CI->getParent()->getParent()->getName().str() << "\n"); - ToRemove.insert(std::make_pair(Graph, F)); - } - } - } - } - } - - for(DenseSet::iterator I = ToRemove.begin(), E = ToRemove.end(); - I != E; ++I) - I->first->removeFunctionCalls(*I->second); -} - -// -// Function: processRuntimeCheck() -// -// Description: -// Modify a run-time check so that its return value has the same DSNode as the -// checked pointer. -// -// Inputs: -// M - The module in which calls to the function live. -// name - The name of the function for which direct calls should be processed. -// arg - The argument index that contains the pointer which the run-time -// check returns. -// -void -StdLibDataStructures::processRuntimeCheck (Module & M, - std::string name, - unsigned arg) { - // - // Get a pointer to the function. - // - Function* F = M.getFunction (name); - - // - // If the function doesn't exist, then there is no work to do. - // - if (!F) return; - - // - // Scan through all direct calls to the function (there should only be direct - // calls) and process each one. - // - for (Value::user_iterator ii = F->user_begin(), ee = F->user_end(); - ii != ee; ++ii) { - if (CallInst* CI = dyn_cast(*ii)) { - if (CI->getCalledValue() == F) { - DSGraph* Graph = getDSGraph(*CI->getParent()->getParent()); - DSNodeHandle & RetNode = Graph->getNodeForValue(CI); - DSNodeHandle & ArgNode = Graph->getNodeForValue(CI->getArgOperand(arg)); - RetNode.mergeWith(ArgNode); - } - } - } - - // - // Erase the DSCallSites for this function. This should prevent other DSA - // passes from making the DSNodes passed to/returned from the function - // from becoming Incomplete or External. - // - eraseCallsTo (F); - return; -} - -bool -StdLibDataStructures::runOnModule (Module &M) { - // - // Get the results from the local pass. - // - init (&getAnalysis(), true, true, false, false); - AllocWrappersAnalysis = &getAnalysis(); - - // - // Fetch the DSGraphs for all defined functions within the module. - // - for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) - if (!I->isDeclaration()) - getOrCreateGraph(&*I); - - // - // Erase direct calls to functions that don't return a pointer and are marked - // with the readnone annotation. - // - for (Function &F : M) - if (F.isDeclaration() && F.doesNotAccessMemory() && - !isa(F.getReturnType())) - eraseCallsTo(&F); - - // - // Erase direct calls to external functions that are not varargs, do not - // return a pointer, and do not take pointers. - // - for (Function &F : M) - if (F.isDeclaration() && !F.isVarArg() && - !isa(F.getReturnType())) { - bool hasPtr = false; - for (auto &Arg : F.args()) - if (isa(Arg.getType())) { - hasPtr = true; - break; - } - if (!hasPtr) - eraseCallsTo(&F); - } - - if(!DisableStdLib) { - - // - // Scan through the function summaries and process functions by summary. - // - for (int x = 0; recFuncs[x].name; ++x) - if (Function* F = M.getFunction(recFuncs[x].name)) - if (F->isDeclaration()) { - processFunction(x, F); - } - - std::set::iterator ai = AllocWrappersAnalysis->alloc_begin(); - std::set::iterator ae = AllocWrappersAnalysis->alloc_end(); - int x; - for (x = 0; recFuncs[x].name; ++x) { - if(recFuncs[x].name == std::string("malloc")) - break; - } - - for(;ai != ae; ++ai) { - if(Function* F = M.getFunction(*ai)) - processFunction(x, F); - } - - ai = AllocWrappersAnalysis->dealloc_begin(); - ae = AllocWrappersAnalysis->dealloc_end(); - for (x = 0; recFuncs[x].name; ++x) { - if(recFuncs[x].name == std::string("free")) - break; - } - - for(;ai != ae; ++ai) { - if(Function* F = M.getFunction(*ai)) - processFunction(x, F); - } - - // - // Merge return values and checked pointer values for SAFECode run-time - // checks. - // - processRuntimeCheck (M, "boundscheck", 2); - processRuntimeCheck (M, "boundscheckui", 2); - processRuntimeCheck (M, "exactcheck2", 1); - - processRuntimeCheck (M, "boundscheck_debug", 2); - processRuntimeCheck (M, "boundscheckui_debug", 2); - processRuntimeCheck (M, "exactcheck2_debug", 1); - - processRuntimeCheck (M, "pchk_getActualValue", 1); - } - - // - // In the Local DSA Pass, we marked nodes passed to/returned from 'StdLib' - // functions as External because, at that point, they were. However, they no - // longer are necessarily External, and we need to update accordingly. - // - GlobalsGraph->maskIncompleteMarkers(); - - GlobalsGraph->computeExternalFlags(DSGraph::ResetExternal); - for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) - if (!I->isDeclaration()) { - DSGraph * G = getDSGraph(*I); - unsigned EFlags = 0 - | DSGraph::ResetExternal - | DSGraph::DontMarkFormalsExternal - | DSGraph::ProcessCallSites; - G->maskIncompleteMarkers(); - G->markIncompleteNodes(DSGraph::MarkFormalArgs - |DSGraph::IgnoreGlobals); - G->computeExternalFlags(EFlags); - SDEBUG(G->AssertGraphOK()); - } - GlobalsGraph->markIncompleteNodes(DSGraph::MarkFormalArgs - |DSGraph::IgnoreGlobals); - GlobalsGraph->computeExternalFlags(DSGraph::ProcessCallSites); - SDEBUG(GlobalsGraph->AssertGraphOK()); - for (Function &F : M) - if (!F.isDeclaration()) { - DSGraph *Graph = getOrCreateGraph(&F); - Graph->maskIncompleteMarkers(); - cloneGlobalsInto(Graph, DSGraph::DontCloneCallNodes | - DSGraph::DontCloneAuxCallNodes); - Graph->markIncompleteNodes(DSGraph::MarkFormalArgs - |DSGraph::IgnoreGlobals); - } - - return false; -} - - -void StdLibDataStructures::processFunction(int x, Function *F) { - for (Value::user_iterator ii = F->user_begin(), ee = F->user_end(); - ii != ee; ++ii) - - if (CallInst* CI = dyn_cast(*ii)){ - if (CI->getCalledValue() == F) { - DSGraph* Graph = getDSGraph(*CI->getParent()->getParent()); - - // - // Set the read, write, and heap markers on the return value - // as appropriate. - // - if(isa((CI)->getType())){ - if(Graph->hasNodeForValue(CI)){ - if (recFuncs[x].action.read[0]) - Graph->getNodeForValue(CI).getNode()->setReadMarker(); - if (recFuncs[x].action.write[0]) - Graph->getNodeForValue(CI).getNode()->setModifiedMarker(); - if (recFuncs[x].action.heap[0]) - Graph->getNodeForValue(CI).getNode()->setHeapMarker(); - } - } - - // - // Set the read, write, and heap markers on the actual arguments - // as appropriate. - // - for (unsigned y = 0; y < CI->getNumArgOperands(); ++y) - if (isa(CI->getArgOperand(y)->getType())){ - if (Graph->hasNodeForValue(CI->getArgOperand(y))){ - if (recFuncs[x].action.read[y + 1]) - Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->setReadMarker(); - if (recFuncs[x].action.write[y + 1]) - Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->setModifiedMarker(); - if (recFuncs[x].action.heap[y + 1]) - Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->setHeapMarker(); - } - } - - // - // Merge the DSNoes for return values and parameters as - // appropriate. - // - std::vector toMerge; - if (recFuncs[x].action.mergeNodes[0]) - if (isa(CI->getType())) - if (Graph->hasNodeForValue(CI)) - toMerge.push_back(Graph->getNodeForValue(CI)); - for (unsigned y = 0; y < CI->getNumArgOperands(); ++y) - if (recFuncs[x].action.mergeNodes[y + 1]) - if (isa(CI->getArgOperand(y)->getType())) - if (Graph->hasNodeForValue(CI->getArgOperand(y))) - toMerge.push_back(Graph->getNodeForValue(CI->getArgOperand(y))); - for (unsigned y = 1; y < toMerge.size(); ++y) - toMerge[0].mergeWith(toMerge[y]); - - // - // Collapse (fold) the DSNode of the return value and the actual - // arguments if directed to do so. - // - if (!noStdLibFold && recFuncs[x].action.collapse) { - if (isa(CI->getType())){ - if (Graph->hasNodeForValue(CI)) - Graph->getNodeForValue(CI).getNode()->foldNodeCompletely(); - NumNodesFoldedInStdLib++; - } - for (unsigned y = 0; y < CI->getNumArgOperands(); ++y){ - if (isa(CI->getArgOperand(y)->getType())){ - if (Graph->hasNodeForValue(CI->getArgOperand(y))){ - Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->foldNodeCompletely(); - NumNodesFoldedInStdLib++; - } - } - } - } - } - } else if (InvokeInst* CI = dyn_cast(*ii)){ - if (CI->getCalledValue() == F) { - DSGraph* Graph = getDSGraph(*CI->getParent()->getParent()); - - // - // Set the read, write, and heap markers on the return value - // as appropriate. - // - if(isa((CI)->getType())){ - if(Graph->hasNodeForValue(CI)){ - if (recFuncs[x].action.read[0]) - Graph->getNodeForValue(CI).getNode()->setReadMarker(); - if (recFuncs[x].action.write[0]) - Graph->getNodeForValue(CI).getNode()->setModifiedMarker(); - if (recFuncs[x].action.heap[0]) - Graph->getNodeForValue(CI).getNode()->setHeapMarker(); - } - } - - // - // Set the read, write, and heap markers on the actual arguments - // as appropriate. - // - for (unsigned y = 0; y < CI->getNumArgOperands(); ++y) - if (isa(CI->getArgOperand(y)->getType())){ - if (Graph->hasNodeForValue(CI->getArgOperand(y))){ - if (recFuncs[x].action.read[y + 1]) - Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->setReadMarker(); - if (recFuncs[x].action.write[y + 1]) - Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->setModifiedMarker(); - if (recFuncs[x].action.heap[y + 1]) - Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->setHeapMarker(); - } - } - - // - // Merge the DSNoes for return values and parameters as - // appropriate. - // - std::vector toMerge; - if (recFuncs[x].action.mergeNodes[0]) - if (isa(CI->getType())) - if (Graph->hasNodeForValue(CI)) - toMerge.push_back(Graph->getNodeForValue(CI)); - for (unsigned y = 0; y < CI->getNumArgOperands(); ++y) - if (recFuncs[x].action.mergeNodes[y + 1]) - if (isa(CI->getArgOperand(y)->getType())) - if (Graph->hasNodeForValue(CI->getArgOperand(y))) - toMerge.push_back(Graph->getNodeForValue(CI->getArgOperand(y))); - for (unsigned y = 1; y < toMerge.size(); ++y) - toMerge[0].mergeWith(toMerge[y]); - - // - // Collapse (fold) the DSNode of the return value and the actual - // arguments if directed to do so. - // - if (!noStdLibFold && recFuncs[x].action.collapse) { - if (isa(CI->getType())){ - if (Graph->hasNodeForValue(CI)) - Graph->getNodeForValue(CI).getNode()->foldNodeCompletely(); - NumNodesFoldedInStdLib++; - } - for (unsigned y = 0; y < CI->getNumArgOperands(); ++y){ - if (isa(CI->getArgOperand(y)->getType())){ - if (Graph->hasNodeForValue(CI->getArgOperand(y))){ - Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->foldNodeCompletely(); - NumNodesFoldedInStdLib++; - } - } - } - } - } - } else if(ConstantExpr *CE = dyn_cast(*ii)) { - if(CE->isCast()) - for (Value::user_iterator ci = CE->user_begin(), ce = CE->user_end(); - ci != ce; ++ci) { - - if (CallInst* CI = dyn_cast(*ci)){ - if (CI->getCalledValue() == CE) { - DSGraph* Graph = getDSGraph(*CI->getParent()->getParent()); - - // - // Set the read, write, and heap markers on the return value - // as appropriate. - // - if(isa((CI)->getType())){ - if(Graph->hasNodeForValue(CI)){ - if (recFuncs[x].action.read[0]) - Graph->getNodeForValue(CI).getNode()->setReadMarker(); - if (recFuncs[x].action.write[0]) - Graph->getNodeForValue(CI).getNode()->setModifiedMarker(); - if (recFuncs[x].action.heap[0]) - Graph->getNodeForValue(CI).getNode()->setHeapMarker(); - } - } - - // - // Set the read, write, and heap markers on the actual arguments - // as appropriate. - // - for (unsigned y = 0; y < CI->getNumArgOperands(); ++y) - if (recFuncs[x].action.read[y + 1]){ - if (isa(CI->getArgOperand(y)->getType())){ - if (Graph->hasNodeForValue(CI->getArgOperand(y))) - Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->setReadMarker(); - if (Graph->hasNodeForValue(CI->getArgOperand(y))) - Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->setModifiedMarker(); - if (Graph->hasNodeForValue(CI->getArgOperand(y))) - Graph->getNodeForValue(CI->getArgOperand(y)).getNode()->setHeapMarker(); - } - } - - // - // Merge the DSNoes for return values and parameters as - // appropriate. - // - std::vector toMerge; - if (recFuncs[x].action.mergeNodes[0]) - if (isa(CI->getType())) - if (Graph->hasNodeForValue(CI)) - toMerge.push_back(Graph->getNodeForValue(CI)); - for (unsigned y = 0; y < CI->getNumArgOperands(); ++y) - if (recFuncs[x].action.mergeNodes[y + 1]) - if (isa(CI->getArgOperand(y)->getType())) - if (Graph->hasNodeForValue(CI->getArgOperand(y))) - toMerge.push_back(Graph->getNodeForValue(CI->getArgOperand(y))); - for (unsigned y = 1; y < toMerge.size(); ++y) - toMerge[0].mergeWith(toMerge[y]); - - // - // Collapse (fold) the DSNode of the return value and the actual - // arguments if directed to do so. - // - if (!noStdLibFold && recFuncs[x].action.collapse) { - if (isa(CI->getType())){ - if (Graph->hasNodeForValue(CI)) - Graph->getNodeForValue(CI).getNode()->foldNodeCompletely(); - NumNodesFoldedInStdLib++; - } - for (unsigned y = 0; y < CI->getNumArgOperands(); ++y) - if (isa(CI->getArgOperand(y)->getType())){ - if (Graph->hasNodeForValue(CI->getArgOperand(y))){ - DSNode * Node=Graph->getNodeForValue(CI->getArgOperand(y)).getNode(); - Node->foldNodeCompletely(); - NumNodesFoldedInStdLib++; - } - } - } - } - } - } - } - - // - // Pretend that this call site does not call this function anymore. - // - eraseCallsTo(F); -} diff --git a/lib/DSA/TopDownClosure.cpp b/lib/DSA/TopDownClosure.cpp deleted file mode 100644 index 3e50bb6e5..000000000 --- a/lib/DSA/TopDownClosure.cpp +++ /dev/null @@ -1,425 +0,0 @@ -//===- TopDownClosure.cpp - Compute the top-down interprocedure closure ---===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements the TDDataStructures class, which represents the -// Top-down Interprocedural closure of the data structure graph over the -// program. This is useful (but not strictly necessary?) for applications -// like pointer analysis. -// -//===----------------------------------------------------------------------===// -#define DEBUG_TYPE "td_dsa" - -#include "dsa/DataStructure.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/DerivedTypes.h" -#include "dsa/DSGraph.h" -#include "smack/Debug.h" -#include "llvm/Support/FormattedStream.h" -#include "llvm/Support/Timer.h" -#include "llvm/ADT/Statistic.h" -using namespace llvm; - -#define TIME_REGION(VARNAME, DESC) - -namespace { - RegisterPass // Register the pass - Y("dsa-td", "Top-down Data Structure Analysis"); - - RegisterPass // Register the pass - Z("dsa-eqtd", "EQ Top-down Data Structure Analysis"); - - STATISTIC (NumTDInlines, "Number of graphs inlined"); -} - -char TDDataStructures::ID; -char EQTDDataStructures::ID; - -TDDataStructures::~TDDataStructures() { - releaseMemory(); -} - -EQTDDataStructures::~EQTDDataStructures() { - releaseMemory(); -} - -void TDDataStructures::markReachableFunctionsExternallyAccessible(DSNode *N, - DenseSet &Visited) { - if (!N || Visited.count(N)) return; - Visited.insert(N); - - // Handle this node - { - N->addFullFunctionSet(ExternallyCallable); - } - - for (DSNode::edge_iterator ii = N->edge_begin(), - ee = N->edge_end(); ii != ee; ++ii) - if (!ii->second.isNull()) { - DSNodeHandle &NH = ii->second; - DSNode * NN = NH.getNode(); - NN->addFullFunctionSet(ExternallyCallable); - markReachableFunctionsExternallyAccessible(NN, Visited); - } -} - - -// run - Calculate the top down data structure graphs for each function in the -// program. -// -bool TDDataStructures::runOnModule(Module &M) { - - init(useEQBU ? &getAnalysis() - : &getAnalysis(), - true, true, true, false); - // Figure out which functions must not mark their arguments complete because - // they are accessible outside this compilation unit. Currently, these - // arguments are functions which are reachable by incomplete or external - // nodes in the globals graph. - const DSScalarMap &GGSM = GlobalsGraph->getScalarMap(); - DenseSet Visited; - for (DSScalarMap::global_iterator I=GGSM.global_begin(), E=GGSM.global_end(); - I != E; ++I) { - DSNode *N = GGSM.find(*I)->second.getNode(); - if (N->isIncompleteNode() || N->isExternalNode()) - markReachableFunctionsExternallyAccessible(N, Visited); - } - - // Loop over unresolved call nodes. Any functions passed into (but not - // returned!) from unresolvable call nodes may be invoked outside of the - // current module. - for (DSGraph::afc_iterator I = GlobalsGraph->afc_begin(), - E = GlobalsGraph->afc_end(); I != E; ++I) - for (unsigned arg = 0, e = I->getNumPtrArgs(); arg != e; ++arg) - markReachableFunctionsExternallyAccessible(I->getPtrArg(arg).getNode(), - Visited); - Visited.clear(); - - // Clear Aux of Globals Graph to be refilled in later by post-TD unresolved - // functions - GlobalsGraph->getAuxFunctionCalls().clear(); - - // Functions without internal linkage are definitely externally callable! - for (Function &F : M) - if (!F.isDeclaration() && !F.hasInternalLinkage() && !F.hasPrivateLinkage()) - ExternallyCallable.insert(&F); - - // Debug code to print the functions that are externally callable -#if 0 - for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) - if (ExternallyCallable.count(I)) { - errs() << "ExternallyCallable: " << I->getNameStr() << "\n"; - } -#endif - - // We want to traverse the call graph in reverse post-order. To do this, we - // calculate a post-order traversal, then reverse it. - DenseSet VisitedGraph; - std::vector PostOrder; - -{TIME_REGION(XXX, "td:Compute postorder"); - - // Calculate top-down from main... - if (Function *F = M.getFunction("main")) - ComputePostOrder(*F, VisitedGraph, PostOrder); - - // Next calculate the graphs for each unreachable function... - for (Function &F : M) - if (!F.isDeclaration()) - ComputePostOrder(F, VisitedGraph, PostOrder); - - VisitedGraph.clear(); // Release memory! -} - -{TIME_REGION(XXX, "td:Inline stuff"); - - // Visit each of the graphs in reverse post-order now! - while (!PostOrder.empty()) { - InlineCallersIntoGraph(PostOrder.back()); - PostOrder.pop_back(); - } -} - - // Free the IndCallMap. - while (!IndCallMap.empty()) { - delete IndCallMap.begin()->second; - IndCallMap.erase(IndCallMap.begin()); - } - - formGlobalECs(); - - ExternallyCallable.clear(); - GlobalsGraph->removeTriviallyDeadNodes(); - GlobalsGraph->computeExternalFlags(DSGraph::DontMarkFormalsExternal); - GlobalsGraph->computeIntPtrFlags(); - - // Make sure each graph has updated external information about globals - // in the globals graph. - VisitedGraph.clear(); - for (Function &F : M) { - if (!(F.isDeclaration())){ - DSGraph *Graph = getOrCreateGraph(&F); - if (!VisitedGraph.insert(Graph).second) continue; - - cloneGlobalsInto(Graph, DSGraph::DontCloneCallNodes | - DSGraph::DontCloneAuxCallNodes); - - Graph->computeExternalFlags(DSGraph::DontMarkFormalsExternal); - Graph->computeIntPtrFlags(); - // Clean up uninteresting nodes - Graph->removeDeadNodes(0); - - } - } - - // CBU contains the correct call graph. - // Restore it, so that subsequent passes and clients can get it. - restoreCorrectCallGraph(); - SDEBUG(print(errs(), &M)); - return false; -} - - -void TDDataStructures::ComputePostOrder(const Function &F, - DenseSet &Visited, - std::vector &PostOrder) { - if (F.isDeclaration()) return; - DSGraph* G = getOrCreateGraph(&F); - if (!Visited.insert(G).second) return; - - // Recursively traverse all of the callee graphs. - svset Callees; - - // Go through all of the callsites in this graph and find all callees - // Here we're trying to capture all possible callees so that we can ensure - // each function has all possible callers inlined into it. - for (DSGraph::fc_iterator CI = G->fc_begin(), E = G->fc_end(); - CI != E; ++CI) { - // Direct calls are easy, no reason to look at DSCallGraph - // or anything to do with SCC's - if (CI->isDirectCall()) { - ComputePostOrder(*CI->getCalleeFunc(), Visited, PostOrder); - } - else { - // Otherwise, ask the DSCallGraph for the full set of possible - // callees for this callsite. - // This includes all members of the SCC's of those callees, - // and well as others in F's SCC, since we must assume - // any indirect call might be intra-SCC. - callgraph.addFullFunctionSet(CI->getCallSite(), Callees); - } - } - - for (svset::iterator I = Callees.begin(), - E = Callees.end(); I != E; ++I) - ComputePostOrder(**I, Visited, PostOrder); - - PostOrder.push_back(G); -} - -/// InlineCallersIntoGraph - Inline all of the callers of the specified DS graph -/// into it, then recompute completeness of nodes in the resultant graph. -void TDDataStructures::InlineCallersIntoGraph(DSGraph* DSG) { - // Inline caller graphs into this graph. First step, get the list of call - // sites that call into this graph. - std::vector EdgesFromCaller; - std::map >::iterator - CEI = CallerEdges.find(DSG); - if (CEI != CallerEdges.end()) { - std::swap(CEI->second, EdgesFromCaller); - CallerEdges.erase(CEI); - } - - // Sort the caller sites to provide a by-caller-graph ordering. - std::sort(EdgesFromCaller.begin(), EdgesFromCaller.end()); - - - // Merge information from the globals graph into this graph. FIXME: This is - // stupid. Instead of us cloning information from the GG into this graph, - // then having RemoveDeadNodes clone it back, we should do all of this as a - // post-pass over all of the graphs. We need to take cloning out of - // removeDeadNodes and gut removeDeadNodes at the same time first though. :( - cloneGlobalsInto(DSG, DSGraph::DontCloneCallNodes | - DSGraph::DontCloneAuxCallNodes); - - SDEBUG(errs() << "[TD] Inlining callers into '" - << DSG->getFunctionNames() << "'\n"); - - DSG->maskIncompleteMarkers(); - // Iteratively inline caller graphs into this graph. - while (!EdgesFromCaller.empty()) { - DSGraph* CallerGraph = EdgesFromCaller.back().CallerGraph; - - // Iterate through all of the call sites of this graph, cloning and merging - // any nodes required by the call. - ReachabilityCloner RC(DSG, CallerGraph, - DSGraph::DontCloneCallNodes | - DSGraph::DontCloneAuxCallNodes); - - // Inline all call sites from this caller graph. - do { - const DSCallSite &CS = *EdgesFromCaller.back().CS; - const Function &CF = *EdgesFromCaller.back().CalledFunction; - SDEBUG(errs() << " [TD] Inlining graph into Fn '" - << CF.getName().str() << "' from "); - if (CallerGraph->getReturnNodes().empty()) { - SDEBUG(errs() << "SYNTHESIZED INDIRECT GRAPH"); - } else { - SDEBUG(errs() << "Fn '" << CS.getCallSite().getInstruction()-> - getParent()->getParent()->getName().str() << "'"); - } - SDEBUG(errs() << ": " << CF.getFunctionType()->getNumParams() - << " args\n"); - - // Get the formal argument and return nodes for the called function and - // merge them with the cloned subgraph. - DSCallSite T1 = DSG->getCallSiteForArguments(CF); - RC.mergeCallSite(T1, CS); - ++NumTDInlines; - - EdgesFromCaller.pop_back(); - } while (!EdgesFromCaller.empty() && - EdgesFromCaller.back().CallerGraph == CallerGraph); - } - - - - // Next, now that this graph is finalized, we need to recompute the - // incompleteness markers for this graph and remove unreachable nodes. - - // If any of the functions is externally callable, treat everything in its - // SCC as externally callable. - bool isExternallyCallable = false; - for (DSGraph::retnodes_iterator I = DSG->retnodes_begin(), - E = DSG->retnodes_end(); I != E; ++I) - if (ExternallyCallable.count(I->first)) { - isExternallyCallable = true; - break; - } - - // Recompute the Incomplete markers. Depends on whether args are complete - unsigned IncFlags = DSGraph::IgnoreFormalArgs; - IncFlags |= DSGraph::IgnoreGlobals | DSGraph::MarkVAStart; - DSG->markIncompleteNodes(IncFlags); - - // If this graph contains functions that are externally callable, now is the time to mark - // their arguments and return values as external. At this point TD is inlining all caller information, - // and that means External callers too. - unsigned ExtFlags - = isExternallyCallable ? DSGraph::MarkFormalsExternal : DSGraph::DontMarkFormalsExternal; - DSG->computeExternalFlags(ExtFlags); - DSG->computeIntPtrFlags(); - - cloneIntoGlobals(DSG, DSGraph::DontCloneCallNodes | - DSGraph::DontCloneAuxCallNodes); - // - // Delete dead nodes. Treat globals that are unreachable as dead also. - // - // FIXME: - // Do not delete unreachable globals as the comment describes. For its - // alignment checks on the results of load instructions, SAFECode must be - // able to find the DSNode of both the result of the load as well as the - // pointer dereferenced by the load. If we remove unreachable globals, then - // if the dereferenced pointer is a global, its DSNode will not reachable - // from the local graph's scalar map, and chaos ensues. - // - // So, for now, just remove dead nodes but leave the globals alone. - // - DSG->removeDeadNodes(0); - - // We are done with computing the current TD Graph! Finally, before we can - // finish processing this function, we figure out which functions it calls and - // records these call graph edges, so that we have them when we process the - // callee graphs. - if (DSG->fc_begin() == DSG->fc_end()) return; - - // Loop over all the call sites and all the callees at each call site, and add - // edges to the CallerEdges structure for each callee. - for (DSGraph::fc_iterator CI = DSG->fc_begin(), E = DSG->fc_end(); - CI != E; ++CI) { - - // Handle direct calls efficiently. - if (CI->isDirectCall()) { - if (!CI->getCalleeFunc()->isDeclaration() && - !DSG->getReturnNodes().count(CI->getCalleeFunc())) - CallerEdges[getOrCreateGraph(CI->getCalleeFunc())] - .push_back(CallerCallEdge(DSG, &*CI, CI->getCalleeFunc())); - continue; - } - - svset AllCallees; - std::vector Callees; - - // Get the list of callees - callgraph.addFullFunctionSet(CI->getCallSite(), AllCallees); - - // Filter all non-declarations, and calls within this DSGraph - for (svset::iterator I = AllCallees.begin(), - E = AllCallees.end(); I != E; ++I) { - const Function *F = *I; - if (!F->isDeclaration() && getDSGraph(**I) != DSG) - Callees.push_back(F); - } - AllCallees.clear(); - - // If there is exactly one callee from this call site, remember the edge in - // CallerEdges. - if (Callees.size() == 1) { - const Function * Callee = Callees[0]; - CallerEdges[getOrCreateGraph(Callee)] - .push_back(CallerCallEdge(DSG, &*CI, Callee)); - } - if (Callees.size() <= 1) continue; - - // Otherwise, there are multiple callees from this call site, so it must be - // an indirect call. Chances are that there will be other call sites with - // this set of targets. If so, we don't want to do M*N inlining operations, - // so we build up a new, private, graph that represents the calls of all - // calls to this set of functions. - - std::map, DSGraph*>::iterator IndCallRecI = - IndCallMap.lower_bound(Callees); - - // If we already have this graph, recycle it. - if (IndCallRecI != IndCallMap.end() && IndCallRecI->first == Callees) { - SDEBUG(errs() << " [TD] *** Reuse of indcall graph for " << Callees.size() - << " callees!\n"); - DSGraph * IndCallGraph = IndCallRecI->second; - assert(IndCallGraph->getFunctionCalls().size() == 1); - - // Merge the call into the CS already in the IndCallGraph - ReachabilityCloner RC(IndCallGraph, DSG, 0); - RC.mergeCallSite(IndCallGraph->getFunctionCalls().front(), *CI); - } else { - // Otherwise, create a new DSGraph to represent this. - DSGraph* IndCallGraph = new DSGraph(DSG->getGlobalECs(), - DSG->getDataLayout(), *TypeSS); - - // Clone over the call into the new DSGraph - ReachabilityCloner RC(IndCallGraph, DSG, 0); - DSCallSite ClonedCS = RC.cloneCallSite(*CI); - - // Add the cloned CS to the graph, as if it were an original call. - IndCallGraph->getFunctionCalls().push_back(ClonedCS); - - // Save this graph for use later, should we need it. - IndCallRecI = IndCallMap.insert(IndCallRecI, - std::make_pair(Callees, IndCallGraph)); - - // Additionally, make sure that each of the callees inlines this graph - // exactly once. - DSCallSite *NCS = &IndCallGraph->getFunctionCalls().front(); - for (unsigned i = 0, e = Callees.size(); i != e; ++i) { - DSGraph* CalleeGraph = getDSGraph(*Callees[i]); - if (CalleeGraph != DSG) - CallerEdges[CalleeGraph].push_back(CallerCallEdge(IndCallGraph, NCS, - Callees[i])); - } - } - } -} diff --git a/lib/DSA/TypeSafety.cpp b/lib/DSA/TypeSafety.cpp deleted file mode 100644 index 128365479..000000000 --- a/lib/DSA/TypeSafety.cpp +++ /dev/null @@ -1,553 +0,0 @@ -//===- TypeSafety.cpp - Find type-safe pointers --------------------------- --// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This pass implements code that determines if memory objects are used in -// a type-consistent fashion. It is built using DSA and essentially abstracts -// away the details of interpreting DSNodes. -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "type-safety" - -#include "dsa/TypeSafety.h" - -#include "llvm/IR/Module.h" -#include "llvm/IR/DerivedTypes.h" -#include "smack/Debug.h" -#include "llvm/Support/FormattedStream.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/ADT/Statistic.h" - -static RegisterPass > -X ("typesafety-eqtd", "Find type-safe pointers"); -static RegisterPass > -Y ("typesafety-td", "Find type-safe pointers"); - -// Pass Statistics -namespace { - //STATISTIC (TypeSafeNodes, "Type-safe DSNodes"); -} -extern cl::opt TypeInferenceOptimize; - -namespace dsa { - -template -char TypeSafety::ID = 0; - -// Method: getDSNodeHandle() -// -// Description: -// This method looks up the DSNodeHandle for a given LLVM globalvalue. -// The value is looked up in the globals graph -// -// Return value: -// A DSNodeHandle for the value is returned. This DSNodeHandle is from -// the GlobalsGraph. Note that the DSNodeHandle may represent a NULL DSNode. -// -template DSNodeHandle -TypeSafety::getDSNodeHandle(const GlobalValue *V) { - DSNodeHandle DSH; - const DSGraph * GlobalsGraph = dsaPass->getGlobalsGraph (); - if(GlobalsGraph->hasNodeForValue(V)) { - DSH = GlobalsGraph->getNodeForValue(V); - } - // - // Try looking up this DSNode value in the globals graph. Note that - // globals are put into equivalence classes; we may need to first find the - // equivalence class to which our global belongs, find the global that - // represents all globals in that equivalence class, and then look up the - // DSNode Handle for *that* global. - // - if (DSH.isNull()) { - // - // DSA does not currently handle global aliases. - // - if (!isa(V)) { - // - // We have to dig into the globalEC of the DSGraph to find the DSNode. - // - const GlobalValue * GV = dyn_cast(V); - const GlobalValue * Leader; - Leader = GlobalsGraph->getGlobalECs().getLeaderValue(GV); - DSH = GlobalsGraph->getNodeForValue(Leader); - } - } - return DSH; -} - -// Method: getDSNodeHandle() -// -// Description: -// This method looks up the DSNodeHandle for a given LLVM value. The context -// of the value is the specified function, although if it is a global value, -// the DSNodeHandle may exist within the global DSGraph. -// -// Return value: -// A DSNodeHandle for the value is returned. This DSNodeHandle could either -// be in the function's DSGraph or from the GlobalsGraph. Note that the -// DSNodeHandle may represent a NULL DSNode. -// -template DSNodeHandle -TypeSafety::getDSNodeHandle (const Value * V, const Function * F) { - // - // Ensure that the function has a DSGraph - // - assert (dsaPass->hasDSGraph(*F) && "No DSGraph for function!\n"); - - // - // Lookup the DSNode for the value in the function's DSGraph. - // - const DSGraph * TDG = dsaPass->getDSGraph(*F); - - DSNodeHandle DSH; - if(TDG->hasNodeForValue(V)) - DSH = TDG->getNodeForValue(V); - - // - // If the value wasn't found in the function's DSGraph, then maybe we can - // find the value in the globals graph. - // - if ((DSH.isNull()) && (isa(V))) { - // - // Try looking up this DSNode value in the globals graph. Note that - // globals are put into equivalence classes; we may need to first find the - // equivalence class to which our global belongs, find the global that - // represents all globals in that equivalence class, and then look up the - // DSNode Handle for *that* global. - // - DSH = getDSNodeHandle(cast(V)); - } - return DSH; -} - -template bool -TypeSafety::isTypeSafe (const Value * V, const Function * F) { - // - // Get the DSNode for the specified value. - // - DSNodeHandle DH = getDSNodeHandle (V, F); - - // - // If there is no DSNode, claim that it is not type safe. - // - if (DH.isNull()) { - return false; - } - - // - // See if the DSNode is one that we think is type-safe. - // - if (TypeSafeNodes.count (DH.getNode())) - return true; - - return false; -} - -template bool -TypeSafety::isFieldDisjoint (const Value * V, const Function * F) { - // - // Get the DSNode for the specified value. - // - DSNodeHandle DH = getDSNodeHandle (V, F); - DSNode *node = DH.getNode(); - unsigned offset = DH.getOffset(); - SDEBUG(errs() << " check fields overlap at: " << offset << "\n"); - - // - // If there is no DSNode, claim that it is not type safe. - // - if (DH.isNull()) { - return false; - } - // - // If the DSNode is completely folded, then we know for sure that it is not - // type-safe. - // - if (node->isNodeCompletelyFolded()) - return false; - - // - // If the memory object represented by this DSNode can be manipulated by - // external code or DSA has otherwise not finished analyzing all operations - // on it, declare it type-unsafe. - // - if (node->isExternalNode() || node->isIncompleteNode()) - return false; - - // - // If the pointer to the memory object came from some source not understood - // by DSA or somehow came from/escapes to the realm of integers, declare it - // type-unsafe. - // - if (node->isUnknownNode() || node->isIntToPtrNode() || node->isPtrToIntNode()) { - return false; - } - - return !((NodeInfo[node])[offset]); -} - -// -// TODO -// -template bool -TypeSafety::isFieldDisjoint (const GlobalValue * V, unsigned offset) { - // - // Get the DSNode for the specified value. - // - DSNodeHandle DH = getDSNodeHandle (V); - DSNode *node = DH.getNode(); - //unsigned offset = DH.getOffset(); - SDEBUG(errs() << " check fields overlap at: " << offset << "\n"); - - // - // If there is no DSNode, claim that it is not type safe. - // - if (DH.isNull()) { - return false; - } - // - // If the DSNode is completely folded, then we know for sure that it is not - // type-safe. - // - if (node->isNodeCompletelyFolded()) - return false; - - // - // If the memory object represented by this DSNode can be manipulated by - // external code or DSA has otherwise not finished analyzing all operations - // on it, declare it type-unsafe. - // - if (node->isExternalNode() || node->isIncompleteNode()) - return false; - - // - // If the pointer to the memory object came from some source not understood - // by DSA or somehow came from/escapes to the realm of integers, declare it - // type-unsafe. - // - if (node->isUnknownNode() || node->isIntToPtrNode() || node->isPtrToIntNode()) { - return false; - } - - return !((NodeInfo[node])[offset]); -} - -template bool -TypeSafety::isTypeSafe(const GlobalValue *V) { - // - // Get the DSNode for the specified value. - // - DSNodeHandle DH = getDSNodeHandle(V); - - // - // If there is no DSNode, claim that it is not typesafe. - // - if (DH.isNull()) - return false; - - // - // See if the DSNode is one that we think is type-safe. - // - if (TypeSafeNodes.count (DH.getNode())) - return true; - - return false; -} - -// -// Method: fieldMapUpdate() -// -// Description: -// Update the fieldmap -// -template void -TypeSafety::fieldMapUpdate (const DSNode * N) { - FieldMap fmap; - // - // There are no overlapping fields if the DSNode has no fields. - // - if (N->type_begin() == N->type_end()) - return; - - // - // Iterate through the DSNode to see if the previous fields overlaps with the - // current field. - // - DSNode::const_type_iterator tn = N->type_begin(); - while (true) { - // - // If this is the last field, then we are done updating. - // - if (tn == N->type_end()) { - break; - } - // - // Get the information about the current field. - // - unsigned offset = tn->first; - SuperSet::setPtr TypeSet = tn->second; - - - // - // If there are multiple types in the current field, then the field is type-unsafe. - // - if (TypeSet) { - svset::const_iterator tb = TypeSet->begin(); - if (++tb != TypeSet->end()) { - fmap[offset] = true; - SDEBUG(errs() << "Multiple fields at " << offset << "\n"); - } - } - - for (DSNode::const_type_iterator ti = ++tn; ti != N->type_end(); ++ti) { - - // - // Get the offset of the next field. - // - unsigned next_offset = ti->first; - assert((next_offset >= offset) && "next offset should be larger than offset."); - - // - // Check to see if any of the types in the current field extend into the - // next field. - // - if (TypeSet) { - bool overlaps = false; - for (svset::const_iterator ni = TypeSet->begin(), - ne = TypeSet->end(); ni != ne; ++ni) { - unsigned field_length = TD->getTypeStoreSize (*ni); - if ((offset + field_length) > next_offset) { - if(TypeInferenceOptimize) { - if(const ArrayType *AT = dyn_cast(*ni)) { - Type *ElemTy = AT->getElementType(); - while(ArrayType *AT1 = dyn_cast(ElemTy)) - ElemTy = AT1->getElementType(); - if(next_offset < (TD->getTypeStoreSize(ElemTy) + offset)) { - assert(isa(ElemTy) && "Array Not of Struct Type??"); - //overlaps = false; - //fmap[next_offset] = false; - continue; - } - } - } - fmap[offset] = true; - fmap[next_offset] = true; - overlaps = true; - if(overlaps) { - SDEBUG(errs() << "Found overlap at " << offset << " with " << next_offset << "\n"); - break; - } - } - } - if (!overlaps) - break; - } - } - - if (fmap.find(offset) == fmap.end()) - fmap[offset] = false; - } - - // - // Return the result. - // - NodeInfo[N] = fmap; - return; -} - -// -// Method: typeFieldsOverlap() -// -// Description: -// Determine if any of the type fields can overlap within the DSNode. -// -// Notes: -// o) We take advantage of the fact that a std::map keeps its elements sorted -// by key. -// -template bool -TypeSafety::typeFieldsOverlap (const DSNode * N) { - // - // There are no overlapping fields if the DSNode has no fields. - // - if (N->type_begin() == N->type_end()) - return false; - - // - // Iterate through the DSNode to see if the previous fields overlaps with the - // current field. - // - DSNode::const_type_iterator tn = N->type_begin(); - bool overlaps = false; - while (!overlaps) { - // - // Get the information about the current field. - // - unsigned offset = tn->first; - SuperSet::setPtr TypeSet = tn->second; - - // - // If there are multiple types in the current field, then the node is type-unsafe. - // - if (TypeSet) { - svset::const_iterator tb = TypeSet->begin(); - if (++tb != TypeSet->end()) { - overlaps = true; - break; - } - } - // - // If this is the last field, then we are done searching. - // - if ((++tn) == N->type_end()) { - break; - } - - // - // Get the offset of the next field. - // - unsigned next_offset = tn->first; - - // - // Check to see if any of the types in the current field extend into the - // next field. - // - if (TypeSet) { - for (svset::const_iterator ni = TypeSet->begin(), - ne = TypeSet->end(); ni != ne; ++ni) { - unsigned field_length = TD->getTypeStoreSize (*ni); - if ((offset + field_length) > next_offset) { - overlaps = true; - if(TypeInferenceOptimize) { - if(const ArrayType *AT = dyn_cast(*ni)) { - Type *ElemTy = AT->getElementType(); - while(ArrayType *AT1 = dyn_cast(ElemTy)) - ElemTy = AT1->getElementType(); - if(next_offset < (TD->getTypeStoreSize(ElemTy) + offset)) { - assert(isa(ElemTy) && "Array Not of Struct Type??"); - overlaps = false; - } - } - } - if(overlaps) { - SDEBUG(errs() << " Found overlap at " << offset << " with " << next_offset << "\n"); - break; - } - } - } - } - } - - // - // Return the result. - // - return overlaps; -} - -// -// Method: isTypeSafe() -// -// Description: -// Determine whether a DSNode represents a piece of memory that is accessed -// in a type-safe fasion. -// -// Inputs: -// N - A pointer to a DSNode representing the memory object. -// -// Return value: -// true - The memory object is used in a type-safe fasion. -// false - The memory object *may* be used in a type-unsafe fasion. -// -template bool -TypeSafety::isTypeSafe (const DSNode * N) { - // - // If the DSNode is completely folded, then we know for sure that it is not - // type-safe. - // - if (N->isNodeCompletelyFolded()) - return false; - - // - // If the memory object represented by this DSNode can be manipulated by - // external code or DSA has otherwise not finished analyzing all operations - // on it, declare it type-unsafe. - // - if (N->isExternalNode() || N->isIncompleteNode()) - return false; - - // - // If the pointer to the memory object came from some source not understood - // by DSA or somehow came from/escapes to the realm of integers, declare it - // type-unsafe. - // - if (N->isUnknownNode() || N->isIntToPtrNode() || N->isPtrToIntNode()) { - return false; - } - - // - // Scan through all of the fields within the DSNode and see if any overlap. - // If they do, then the DSNode is not type-safe. - // - if (typeFieldsOverlap (N)) - return false; - - // - // We have run out of reasons for this DSNode to be type-unsafe. Declare it - // type-safe. - // - return true; -} - -// -// Method: findTypeSafeDSNodes() -// -// Description: -// Find and record all type-safe DSNodes. -// -// Inputs: -// Graph - The DSGraph for which we should search for type-safe nodes. -// -// Side-effects: -// Class level data structures are updated to record which DSNodes are -// type-safe. -// -template void -TypeSafety::findTypeSafeDSNodes (const DSGraph * Graph) { - DSGraph::node_const_iterator N = Graph->node_begin(); - DSGraph::node_const_iterator NE = Graph->node_end(); - for (; N != NE; ++N) { - if (isTypeSafe (&*N)) { - TypeSafeNodes.insert (&*N); - } - fieldMapUpdate(&*N); - } -} - -template bool -TypeSafety::runOnModule(Module & M) { - // - // Get access to prerequisite passes. - // - TD = &M.getDataLayout(); - dsaPass = &getAnalysis(); - - // - // For every DSGraph, find which DSNodes are type-safe. - // - findTypeSafeDSNodes (dsaPass->getGlobalsGraph()); - for (Module::iterator F = M.begin(); F != M.end(); ++F) { - if (dsaPass->hasDSGraph (*F)) { - findTypeSafeDSNodes (dsaPass->getDSGraph (*F)); - } - } - - return false; -} - -} diff --git a/lib/smack/BoogieAst.cpp b/lib/smack/BoogieAst.cpp index aa1061d57..bc5a68fee 100644 --- a/lib/smack/BoogieAst.cpp +++ b/lib/smack/BoogieAst.cpp @@ -28,10 +28,6 @@ const Expr *Expr::or_(const Expr *l, const Expr *r) { return new BinExpr(BinExpr::Or, l, r); } -const Expr *Expr::cond(const Expr *c, const Expr *t, const Expr *e) { - return new CondExpr(c, t, e); -} - const Expr *Expr::eq(const Expr *l, const Expr *r) { return new BinExpr(BinExpr::Eq, l, r); } @@ -40,6 +36,10 @@ const Expr *Expr::lt(const Expr *l, const Expr *r) { return new BinExpr(BinExpr::Lt, l, r); } +const Expr *Expr::ifThenElse(const Expr *c, const Expr *t, const Expr *e) { + return new IfThenElseExpr(c, t, e); +} + const Expr *Expr::fn(std::string f, std::list args) { return new FunExpr(f, args); } @@ -115,10 +115,6 @@ const Expr *Expr::upd(const Expr *b, const Expr *i, const Expr *v) { return new UpdExpr(b, i, v); } -const Expr *Expr::if_then_else(const Expr *c, const Expr *t, const Expr *e) { - return new IfThenElseExpr(c, t, e); -} - const Expr *Expr::bvExtract(const Expr *v, const Expr *u, const Expr *l) { return new BvExtract(v, u, l); } @@ -424,10 +420,6 @@ void BinExpr::print(std::ostream &os) const { os << " " << rhs << ")"; } -void CondExpr::print(std::ostream &os) const { - os << "(if " << cond << " then " << then << " else " << else_ << ")"; -} - void FunExpr::print(std::ostream &os) const { os << fun; print_seq(os, args, "(", ", ", ")"); @@ -510,7 +502,8 @@ void CodeExpr::print(std::ostream &os) const { } void IfThenElseExpr::print(std::ostream &os) const { - os << "if " << cond << " then " << true_value << " else " << false_value; + os << "(if " << cond << " then " << trueValue << " else " << falseValue + << ")"; } void BvExtract::print(std::ostream &os) const { diff --git a/lib/smack/DSAWrapper.cpp b/lib/smack/DSAWrapper.cpp index a419fb8bf..32be24a5d 100644 --- a/lib/smack/DSAWrapper.cpp +++ b/lib/smack/DSAWrapper.cpp @@ -5,12 +5,16 @@ // the University of Illinois Open Source License. See LICENSE for details. // #include "smack/DSAWrapper.h" -#include "assistDS/DSNodeEquivs.h" -#include "dsa/DSGraph.h" -#include "dsa/DataStructure.h" -#include "dsa/TypeSafety.h" +#include "smack/Debug.h" +#include "smack/SmackOptions.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/IntrinsicInst.h" + #include "llvm/Support/FileSystem.h" +#include +#include + #define DEBUG_TYPE "dsa-wrapper" namespace smack { @@ -22,185 +26,73 @@ RegisterPass DSAWrapperPass("dsa-wrapper", "SMACK Data Structure Graph Based Alias Analysis Wrapper"); -void MemcpyCollector::visitMemCpyInst(llvm::MemCpyInst &mci) { - const llvm::EquivalenceClasses &eqs = - nodeEqs->getEquivalenceClasses(); - const llvm::DSNode *n1 = - eqs.getLeaderValue(nodeEqs->getMemberForValue(mci.getOperand(0))); - const llvm::DSNode *n2 = - eqs.getLeaderValue(nodeEqs->getMemberForValue(mci.getOperand(1))); - - bool f1 = false, f2 = false; - for (unsigned i = 0; i < memcpys.size() && (!f1 || !f2); i++) { - f1 = f1 || memcpys[i] == n1; - f2 = f2 || memcpys[i] == n2; - } - - if (!f1) - memcpys.push_back(eqs.getLeaderValue(n1)); - if (!f2) - memcpys.push_back(eqs.getLeaderValue(n2)); -} - void DSAWrapper::getAnalysisUsage(llvm::AnalysisUsage &AU) const { AU.setPreservesAll(); - AU.addRequiredTransitive(); - AU.addRequiredTransitive(); - AU.addRequiredTransitive(); - AU.addRequired>(); + AU.addRequiredTransitive(); } bool DSAWrapper::runOnModule(llvm::Module &M) { dataLayout = &M.getDataLayout(); - TD = &getAnalysis(); - BU = &getAnalysis(); - nodeEqs = &getAnalysis(); - TS = &getAnalysis>(); - memcpys = collectMemcpys(M, new MemcpyCollector(nodeEqs)); - staticInits = collectStaticInits(M); + SD = &getAnalysis().getDsaAnalysis(); + assert(SD->kind() == sea_dsa::GlobalAnalysisKind::CONTEXT_INSENSITIVE && + "Currently we only want the context-insensitive sea-dsa."); + DG = &SD->getGraph(*M.begin()); + // Print the graph in dot format when debugging + SDEBUG(DG->writeGraph("main.mem.dot")); + collectStaticInits(M); + collectMemOpds(M); + countGlobalRefs(); module = &M; return false; } -std::vector -DSAWrapper::collectMemcpys(llvm::Module &M, MemcpyCollector *mcc) { - - for (llvm::Module::iterator func = M.begin(), e = M.end(); func != e; - ++func) { - - for (llvm::Function::iterator block = func->begin(); block != func->end(); - ++block) { - - mcc->visit(*block); - } - } - return mcc->getMemcpys(); -} - -std::vector -DSAWrapper::collectStaticInits(llvm::Module &M) { - std::vector sis; +void DSAWrapper::collectStaticInits(llvm::Module &M) { for (GlobalVariable &GV : M.globals()) { if (GV.hasInitializer()) { if (auto *N = getNode(&GV)) { - sis.push_back(N); + assert(N && "Global values should have nodes."); + staticInits.insert(N); } } } - return sis; } -DSGraph *DSAWrapper::getGraphForValue(const Value *V) { - if (const Instruction *I = dyn_cast(V)) - return TD->getDSGraph(*I->getParent()->getParent()); - else if (const Argument *A = dyn_cast(V)) - return TD->getDSGraph(*A->getParent()); - else if (const BasicBlock *BB = dyn_cast(V)) - return TD->getDSGraph(*BB->getParent()); - else if (isa(V)) - return TD->getGlobalsGraph(); - - // XXX I know this looks bad, but it works for now - for (auto U : V->users()) { - return getGraphForValue(U); +void DSAWrapper::collectMemOpds(llvm::Module &M) { + for (auto &f : M) { + for (inst_iterator I = inst_begin(&f), E = inst_end(&f); I != E; ++I) { + if (MemCpyInst *memcpyInst = dyn_cast(&*I)) { + memOpds.insert(getNode(memcpyInst->getSource())); + memOpds.insert(getNode(memcpyInst->getDest())); + } else if (MemSetInst *memsetInst = dyn_cast(&*I)) + memOpds.insert(getNode(memsetInst->getDest())); + } } - - llvm_unreachable("Unexpected value."); } -int DSAWrapper::getOffset(const MemoryLocation *l) { - const DSGraph::ScalarMapTy &S = getGraphForValue(l->Ptr)->getScalarMap(); - DSGraph::ScalarMapTy::const_iterator I = S.find((const Value *)l->Ptr); - if (I == S.end()) - return 0; - if (I->second.getNode() && I->second.getNode()->isCollapsedNode()) - return -1; - unsigned offset = I->second.getOffset(); - assert(offset <= INT_MAX && "Cannot handle large offsets"); - return (int)offset; -} - -bool DSAWrapper::isMemcpyd(const llvm::DSNode *n) { - const llvm::EquivalenceClasses &eqs = - nodeEqs->getEquivalenceClasses(); - const llvm::DSNode *nn = eqs.getLeaderValue(n); - for (unsigned i = 0; i < memcpys.size(); i++) - if (memcpys[i] == nn) - return true; - return false; -} - -bool DSAWrapper::isStaticInitd(const llvm::DSNode *n) { - const llvm::EquivalenceClasses &eqs = - nodeEqs->getEquivalenceClasses(); - const llvm::DSNode *nn = eqs.getLeaderValue(n); - for (unsigned i = 0; i < staticInits.size(); i++) - if (staticInits[i] == nn) - return true; - return false; +void DSAWrapper::countGlobalRefs() { + for (auto &g : DG->globals()) { + auto &cellRef = g.second; + auto *node = cellRef->getNode(); + assert(node && "Global values should have DSNodes."); + if (!globalRefCount.count(node)) + globalRefCount[node] = 1; + else + globalRefCount[node]++; + } } -bool DSAWrapper::isFieldDisjoint(const llvm::Value *V, - const llvm::Function *F) { - return TS->isFieldDisjoint(V, F); +bool DSAWrapper::isStaticInitd(const sea_dsa::Node *n) { + return staticInits.count(n) > 0; } -bool DSAWrapper::isFieldDisjoint(const GlobalValue *V, unsigned offset) { - return TS->isFieldDisjoint(V, offset); +bool DSAWrapper::isMemOpd(const sea_dsa::Node *n) { + return memOpds.count(n) > 0; } bool DSAWrapper::isRead(const Value *V) { - const DSNode *N = getNode(V); - return N && (N->isReadNode()); -} - -bool DSAWrapper::isAlloced(const Value *v) { - const DSNode *N = getNode(v); - return N && (N->isHeapNode() || N->isAllocaNode()); -} - -bool DSAWrapper::isExternal(const Value *v) { - const DSNode *N = getNode(v); - return N && N->isExternalNode(); -} - -bool DSAWrapper::isSingletonGlobal(const Value *V) { - const DSNode *N = getNode(V); - if (!N || !N->isGlobalNode() || N->numGlobals() > 1) - return false; - - // Ensure this node has a unique scalar type... (?) - DSNode::const_type_iterator TSi = N->type_begin(); - if (TSi == N->type_end()) - return false; - svset::const_iterator Ti = TSi->second->begin(); - if (Ti == TSi->second->end()) - return false; - const Type *T = *Ti; - while (T->isPointerTy()) - T = T->getPointerElementType(); - if (!T->isSingleValueType()) - return false; - ++Ti; - if (Ti != TSi->second->end()) - return false; - ++TSi; - if (TSi != N->type_end()) - return false; - - // Ensure this node is in its own class... (?) - const EquivalenceClasses &Cs = - nodeEqs->getEquivalenceClasses(); - EquivalenceClasses::iterator C = Cs.findValue(N); - assert(C != Cs.end() && "Did not find value."); - EquivalenceClasses::member_iterator I = Cs.member_begin(C); - if (I == Cs.member_end()) - return false; - ++I; - if (I != Cs.member_end()) - return false; - - return true; + auto node = getNode(V); + assert(node && "Global values should have nodes."); + return node->isRead(); } unsigned DSAWrapper::getPointedTypeSize(const Value *v) { @@ -214,24 +106,104 @@ unsigned DSAWrapper::getPointedTypeSize(const Value *v) { llvm_unreachable("Type should be pointer."); } -int DSAWrapper::getOffset(const Value *v) { - return getOffset(new MemoryLocation(v)); +unsigned DSAWrapper::getOffset(const Value *v) { + if (!DG->hasCell(*v)) + return 0; + return DG->getCell(*v).getOffset(); +} + +const sea_dsa::Node *DSAWrapper::getNode(const Value *v) { + // For sea-dsa, a node is obtained by getting the cell first. + // It's possible that a value doesn't have a cell, e.g., undef. + if (!DG->hasCell(*v)) + return nullptr; + auto node = DG->getCell(*v).getNode(); + assert(node && "Values should have nodes if they have cells."); + return node; } -// TODO: Should this return the node or its leader? -const DSNode *DSAWrapper::getNode(const Value *v) { - const llvm::EquivalenceClasses &eqs = - nodeEqs->getEquivalenceClasses(); - auto *N = nodeEqs->getMemberForValue(v); - return N ? eqs.getLeaderValue(N) : nullptr; +bool DSAWrapper::isTypeSafe(const Value *v) { + typedef std::unordered_map FieldMap; + typedef std::unordered_map NodeMap; + static NodeMap nodeMap; + + auto node = getNode(v); + + if (node->isOffsetCollapsed() || node->isExternal() || node->isIncomplete() || + node->isUnknown() || node->isIntToPtr() || node->isPtrToInt()) + // We consider it type-unsafe to be safe for these cases + return false; + + if (!nodeMap.count(node)) { + // Iterate all the fields of a node to find out + // the type-safety of each field. Then, we cache the results. + FieldMap fieldMap; + auto &types = node->types(); + std::set offsets; + + for (auto &t : types) + offsets.insert(t.first); + + auto offsetIterator = offsets.begin(); + + while (true) { + if (offsetIterator == offsets.end()) + // We have reached the last field and exit the loop + break; + + unsigned offset = *offsetIterator; + + auto &typeSet = types.find(offset)->second; + + auto ti = typeSet.begin(); + if (++ti != typeSet.end()) + // If there are multiple access types, then it's trivially type-unsafe. + fieldMap[offset] = false; + + // Get the maximum length + unsigned fieldLength = 0; + for (auto &t : typeSet) { + // TODO: fix the const_cast + unsigned length = + dataLayout->getTypeStoreSize(const_cast(t)); + if (length > fieldLength) + fieldLength = length; + } + + // Check if the current field overlaps with the next *fields* + for (auto oi = ++offsetIterator; oi != offsets.end(); ++oi) { + unsigned next_offset = *oi; + if (offset + fieldLength > next_offset) { + // Overlaps; mark the current field and the next unsafe + fieldMap[offset] = false; + fieldMap[next_offset] = false; + } else + // If the current field doesn't overlap with the next one, + // it certainly won't overlap with the rest. + break; + } + + if (!fieldMap.count(offset)) + fieldMap[offset] = true; + } + + nodeMap[node] = fieldMap; + } + + auto offset = getOffset(v); + if (nodeMap[node].count(offset)) + return nodeMap[node][offset]; + else + // Chances to hit this branch are when we visit memcpy/memset + // pointer operands. + return false; } -void DSAWrapper::printDSAGraphs(const char *Filename) { - std::error_code EC; - llvm::raw_fd_ostream F(Filename, EC, sys::fs::OpenFlags::F_None); - TD->print(F, module); - BU->print(F, module); - TS->print(F, module); +unsigned DSAWrapper::getNumGlobals(const sea_dsa::Node *n) { + if (globalRefCount.count(n)) + return globalRefCount[n]; + else + return 0; } } // namespace smack diff --git a/lib/smack/Prelude.cpp b/lib/smack/Prelude.cpp index 6cb920185..c3bd83a9f 100644 --- a/lib/smack/Prelude.cpp +++ b/lib/smack/Prelude.cpp @@ -343,7 +343,7 @@ struct IntOpGen::IntArithOp : public IntOp { static const Expr *bvMinMaxExpr(unsigned size) { std::string signLetter = SIGN ? "s" : "u"; std::string pred = MIN ? "lt" : "gt"; - return Expr::if_then_else( + return Expr::ifThenElse( Expr::fn(indexedName("$" + signLetter + pred, {getBvTypeName(size), Naming::BOOL_TYPE}), makeIntVarExprs(2)), @@ -356,7 +356,45 @@ struct IntOpGen::IntArithOp : public IntOp { const Expr *a1 = makeIntVarExpr(1); const Expr *a2 = makeIntVarExpr(2); auto pred = MIN ? Expr::lt(a1, a2) : Expr::lt(a2, a1); - return Expr::if_then_else(pred, a1, a2); + return Expr::ifThenElse(pred, a1, a2); + } + + // generate inlined `srem(i1, i2)` function body like + // if (mod(i1,i2) != 0 && i1 < 0) + // then mod(i1,i2) - abs(i2) else mod(i1,i2), where + // `i1` is the dividend and `i2` is the divisor + // `mod` is the Euclidean function defined in the SMT lib + // therefore its result is always positive + // the `srem` operation defined by LLVM follows the same + // definition as modern C standards + // (https://en.wikipedia.org/wiki/Modulo_operation#In_programming_languages) + // its result has the same sign as i1 because the division rounds to 0 + // therefore, the only case `mod` and `srem` differ is when i1 is negative + // and the remainder is not 0 + // for this case, the result of `srem` is the result of `mod` minus |i2| + static const Expr *sremExpr(unsigned size) { + std::string type = getIntTypeName(size); + const Expr *dividend = makeIntVarExpr(1); + const Expr *divisor = makeIntVarExpr(2); + const Expr *zero = Expr::lit((unsigned long long)0); + const Expr *mod = Expr::fn(indexedName("$smod", {type}), dividend, divisor); + const Expr *modNeZero = + Expr::fn(indexedName("$ne", {type, "bool"}), mod, zero); + const Expr *dividendLtZero = + Expr::fn(indexedName("$slt", {type, "bool"}), dividend, zero); + const Expr *negRemainder = Expr::fn( + indexedName("$sub", {type}), mod, + Expr::fn(indexedName("$smax", {type}), divisor, + Expr::fn(indexedName("$sub", {type}), zero, divisor))); + return Expr::ifThenElse(Expr::and_(modNeZero, dividendLtZero), negRemainder, + mod); + } + + // generate inlined `urem` function body like + // $smod.i32(i1,i2), where `$smod` is a wrapper to SMT's `mod` function + static const Expr *uremExpr(unsigned size) { + return Expr::fn(indexedName("$smod", {getIntTypeName(size)}), + makeIntVarExpr(1), makeIntVarExpr(2)); } }; @@ -381,9 +419,11 @@ void IntOpGen::generateArithOps(std::stringstream &s) const { bvBuiltinOp, true}, {"sdiv", 2, intBuiltinOp, bvBuiltinOp, false}, {"smod", 2, intBuiltinOp, bvBuiltinOp, false}, - {"srem", 2, intBuiltinOp, bvBuiltinOp, false}, {"udiv", 2, intBuiltinOp, bvBuiltinOp, false}, - {"urem", 2, intBuiltinOp, bvBuiltinOp, false}, + {"srem", 2, new InlinedOp(IntOpGen::IntArithOp::sremExpr), + bvBuiltinOp, false}, + {"urem", 2, new InlinedOp(IntOpGen::IntArithOp::uremExpr), + bvBuiltinOp, false}, {"shl", 2, uninterpretedOp, bvBuiltinOp, false}, {"lshr", 2, uninterpretedOp, bvBuiltinOp, false}, {"ashr", 2, uninterpretedOp, bvBuiltinOp, false}, @@ -465,12 +505,11 @@ struct IntOpGen::IntPred : public IntOp { Naming::BOOL_TYPE, ((IntOp::exprT)iop->func)(size)); // e.g.: function {:inline} $ule.i1(i1: i1, i2: i1) returns (i1) // { if $ule.i1.bool(i1, i2) then 1 else 0 } - auto predFunc = - inlinedOp(name, {type}, makeIntVars(2, type), getIntTypeName(1), - Expr::if_then_else( - Expr::fn(indexedName(name, {type, Naming::BOOL_TYPE}), - makeIntVarExprs(2)), - Expr::lit(1ll), Expr::lit(0ll))); + auto predFunc = inlinedOp( + name, {type}, makeIntVars(2, type), getIntTypeName(1), + Expr::ifThenElse(Expr::fn(indexedName(name, {type, Naming::BOOL_TYPE}), + makeIntVarExprs(2)), + Expr::lit(1ll), Expr::lit(0ll))); return {compFunc, predFunc}; } @@ -494,12 +533,11 @@ struct IntOpGen::IntPred : public IntOp { llvm_unreachable("no uninterpreted bv predicates"); // e.g.: function {:inline} $ule.bv1(i1: bv1, i2: bv1) returns (bv1) // { if $ule.bv1.bool(i1, i2) then 1bv1 else 0bv1 } - predFunc = - inlinedOp(name, {type}, makeIntVars(2, type), getBvTypeName(1), - Expr::if_then_else( - Expr::fn(indexedName(name, {type, Naming::BOOL_TYPE}), - makeIntVarExpr(1), makeIntVarExpr(2)), - Expr::lit(1, 1), Expr::lit(0, 1))); + predFunc = inlinedOp( + name, {type}, makeIntVars(2, type), getBvTypeName(1), + Expr::ifThenElse(Expr::fn(indexedName(name, {type, Naming::BOOL_TYPE}), + makeIntVarExpr(1), makeIntVarExpr(2)), + Expr::lit(1, 1), Expr::lit(0, 1))); return {compFunc, predFunc}; } @@ -731,7 +769,7 @@ void IntOpGen::generateBvIntConvs(std::stringstream &s) const { << "\n"; if (SmackOptions::BitPrecise && !SmackOptions::BitPrecisePointers) { s << Decl::function(indexedName("$bv2uint", {ptrSize}), {{"i", bt}}, it, - nullptr, {makeBuiltinAttr("bv2int")}) + nullptr, {makeBuiltinAttr("bv2nat")}) << "\n"; const Expr *arg = Expr::id("i"); const Expr *uint = Expr::fn(indexedName("$bv2uint", {ptrSize}), arg); @@ -744,16 +782,17 @@ void IntOpGen::generateBvIntConvs(std::stringstream &s) const { llvm_unreachable("Unexpected pointer bit width."); s << Decl::function( indexedName("$bv2int", {ptrSize}), {{"i", bt}}, it, - Expr::cond(Expr::fn(indexedName("$slt", {bt, Naming::BOOL_TYPE}), - {arg, Expr::lit(0ULL, ptrSize)}), - Expr::fn(indexedName("$sub", {it}), - {uint, Expr::lit(offset, 0U)}), - uint), + Expr::ifThenElse( + Expr::fn(indexedName("$slt", {bt, Naming::BOOL_TYPE}), + {arg, Expr::lit(0ULL, ptrSize)}), + Expr::fn(indexedName("$sub", {it}), + {uint, Expr::lit(offset, 0U)}), + uint), {makeInlineAttr()}) << "\n"; } else s << Decl::function(indexedName("$bv2int", {ptrSize}), {{"i", bt}}, it, - nullptr, {makeBuiltinAttr("bv2int")}) + nullptr, {makeBuiltinAttr("bv2nat")}) << "\n"; s << "\n"; } @@ -866,7 +905,8 @@ void MemDeclGen::generateAddrBoundsAndPred(std::stringstream &s) const { s << Decl::function( Naming::EXTERNAL_ADDR, makePtrVars(1), Naming::BOOL_TYPE, Expr::fn(indexedName("$slt", {Naming::PTR_TYPE, Naming::BOOL_TYPE}), - makePtrVarExpr(0), Expr::id(Naming::EXTERNS_BOTTOM))) + makePtrVarExpr(0), Expr::id(Naming::EXTERNS_BOTTOM)), + {makeInlineAttr()}) << "\n"; s << "\n"; } @@ -927,11 +967,12 @@ void PtrOpGen::generatePreds(std::stringstream &s) const { indexedName(pred, {Naming::PTR_TYPE}), {{"p1", Naming::PTR_TYPE}, {"p2", Naming::PTR_TYPE}}, prelude.rep.intType(1), - Expr::cond(Expr::fn(indexedName(pred, {prelude.rep.pointerType(), - Naming::BOOL_TYPE}), - {Expr::id("p1"), Expr::id("p2")}), - prelude.rep.integerLit(1LL, 1), - prelude.rep.integerLit(0LL, 1)), + Expr::ifThenElse( + Expr::fn(indexedName(pred, {prelude.rep.pointerType(), + Naming::BOOL_TYPE}), + {Expr::id("p1"), Expr::id("p2")}), + prelude.rep.integerLit(1LL, 1), + prelude.rep.integerLit(0LL, 1)), {makeInlineAttr()}) << "\n"; s << Decl::function( diff --git a/lib/smack/Regions.cpp b/lib/smack/Regions.cpp index c549d8bf5..3d58c15eb 100644 --- a/lib/smack/Regions.cpp +++ b/lib/smack/Regions.cpp @@ -2,11 +2,6 @@ // This file is distributed under the MIT License. See LICENSE for details. // #include "smack/Regions.h" -#include "assistDS/DSNodeEquivs.h" -#include "dsa/DSGraph.h" -#include "dsa/DSNode.h" -#include "dsa/DataStructure.h" -#include "dsa/TypeSafety.h" #include "smack/DSAWrapper.h" #include "smack/Debug.h" #include "smack/SmackOptions.h" @@ -18,111 +13,27 @@ namespace smack { const DataLayout *Region::DL = nullptr; DSAWrapper *Region::DSA = nullptr; -// DSNodeEquivs* Region::NEQS = nullptr; - -namespace { -const Function *getFunction(const Value *V) { - if (const Instruction *I = dyn_cast(V)) - return I->getParent()->getParent(); - else if (const Argument *A = dyn_cast(V)) - return A->getParent(); - else if (const BasicBlock *BB = dyn_cast(V)) - return BB->getParent(); - - // XXX I know this looks bad, but it works for now - for (auto U : V->users()) - return getFunction(U); - - llvm_unreachable("Unexpected value."); -} - -bool isFieldDisjoint(DSAWrapper *DSA, const Value *V, unsigned offset) { - if (const GlobalValue *G = dyn_cast(V)) - return DSA->isFieldDisjoint(G, offset); - else - return DSA->isFieldDisjoint(V, getFunction(V)); -} -} // namespace void Region::init(Module &M, Pass &P) { DL = &M.getDataLayout(); DSA = &P.getAnalysis(); } -namespace { -unsigned numGlobals(const DSNode *N) { - unsigned count = 0; - - // shamelessly ripped from getCaption(..) in lib/DSA/Printer.cpp - EquivalenceClasses *GlobalECs = 0; - const DSGraph *G = N->getParentGraph(); - if (G) - GlobalECs = &G->getGlobalECs(); - - for (auto i = N->globals_begin(), e = N->globals_end(); i != e; ++i) { - count += 1; - - if (GlobalECs) { - // Figure out how many globals are equivalent to this one. - auto I = GlobalECs->findValue(*i); - if (I != GlobalECs->end()) { - count += - std::distance(GlobalECs->member_begin(I), GlobalECs->member_end()) - - 1; - } - } - } +bool Region::isSingleton(const Value *v, unsigned length) { + // TODO can we do something for non-global nodes? + auto node = DSA->getNode(v); - return count; -} -} // namespace - -bool Region::isSingleton(const DSNode *N, unsigned offset, unsigned length) { - if (N->isGlobalNode() && numGlobals(N) == 1 && !N->isArrayNode() && - !N->isAllocaNode() && !N->isHeapNode() && !N->isExternalNode() && - !N->isUnknownNode()) { - - // TODO can we do something for non-global nodes? - - // TODO don’t need to know if there are other members of this class, right? - // assert(NEQS && "Missing DS node equivalence information."); - // auto &Cs = NEQS->getEquivalenceClasses(); - // auto C = Cs.findValue(representative); - // assert(C != Cs.end() && "No equivalence class found."); - // assert(Cs.member_begin(C) != Cs.member_end() && "Found empty class."); - // if (++(Cs.member_begin(C)) != Cs.member_end()) return false; - - assert(DL && "Missing data layout information."); - - for (auto I = N->type_begin(), E = N->type_end(); I != E; ++I) { - if (I->first < offset) - continue; - if (I->first > offset) - break; - if (I->second->begin() == I->second->end()) - break; - if ((++(I->second->begin())) != I->second->end()) - break; - Type *T = *I->second->begin(); - if (!T->isSized()) - break; - if (DL->getTypeAllocSize(T) != length) - break; - if (!T->isSingleValueType()) - break; - return true; - } - } - return false; + return !isAllocated(node) && DSA->getNumGlobals(node) == 1 && + !node->isArray() && DSA->isTypeSafe(v) && !DSA->isMemOpd(node); } -bool Region::isAllocated(const DSNode *N) { - return N->isHeapNode() || N->isAllocaNode(); +bool Region::isAllocated(const sea_dsa::Node *N) { + return N->isHeap() || N->isAlloca(); } -bool Region::isComplicated(const DSNode *N) { - return N->isIntToPtrNode() || N->isPtrToIntNode() || N->isExternalNode() || - N->isUnknownNode(); +bool Region::isComplicated(const sea_dsa::Node *N) { + return N->isIntToPtr() || N->isPtrToInt() || N->isExternal() || + N->isUnknown(); } void Region::init(const Value *V, unsigned length) { @@ -133,25 +44,17 @@ void Region::init(const Value *V, unsigned length) { representative = (DSA && !dyn_cast(V)) ? DSA->getNode(V) : nullptr; this->type = T; - int offset = DSA ? DSA->getOffset(V) : 0; - if (offset < 0) { - this->offset = 0; - this->length = -1U; - } else { - this->offset = offset; - this->length = length; - } + this->offset = DSA ? DSA->getOffset(V) : 0; + this->length = length; - singleton = - DL && representative && isSingleton(representative, offset, length); + singleton = DL && representative && isSingleton(V, length); allocated = !representative || isAllocated(representative); bytewise = DSA && SmackOptions::BitPrecise && (SmackOptions::NoByteAccessInference || - !isFieldDisjoint(DSA, V, offset) || - DSA->isMemcpyd(representative) || T->isIntegerTy(8)); - incomplete = !representative || representative->isIncompleteNode(); + (!representative || !DSA->isTypeSafe(V)) || T->isIntegerTy(8)); + incomplete = !representative || representative->isIncomplete(); complicated = !representative || isComplicated(representative); - collapsed = !representative || representative->isCollapsedNode(); + collapsed = !representative || representative->isOffsetCollapsed(); } Region::Region(const Value *V) { @@ -196,13 +99,17 @@ void Region::print(raw_ostream &O) { else O << "*"; O << ">[" << offset << "," << (offset + length) << "]{"; - if (isSingleton()) + if (singleton) O << "S"; if (bytewise) O << "B"; if (complicated) O << "C"; - if (isAllocated()) + if (incomplete) + O << "I"; + if (collapsed) + O << "L"; + if (allocated) O << "A"; O << "}"; } @@ -212,17 +119,25 @@ RegisterPass RegionsPass("smack-regions", "SMACK Memory Regions Pass"); void Regions::getAnalysisUsage(llvm::AnalysisUsage &AU) const { AU.setPreservesAll(); - if (!SmackOptions::NoMemoryRegionSplitting) { - AU.addRequiredTransitive(); - AU.addRequiredTransitive(); - AU.addRequiredTransitive(); - AU.addRequiredTransitive(); - AU.addRequiredTransitive>(); + if (!SmackOptions::NoMemoryRegionSplitting) AU.addRequired(); - } } bool Regions::runOnModule(Module &M) { + // Shaobo: my understanding of how this class works: + // First, a bunch of instructions involving pointers are visited (via + // Regions::idx). During a visit on an instruction, a region is created + // (Region::init) for the pointer operand. Note that a region is always + // created for a pointer when it's visited, regardless of whether it alias + // with the existing ones. A region can be roughly seen as a tuple of (cell, + // length) or (node, offset, length) since a cell is essentially a tuple of + // (node, offset). After a region is created, we will merge it to the existing + // ones if it overlaps with the them. So after this pass, we will get a bunch + // of regions which are mutually exclusive to each other. + // After that, SmackRep will call Regions::idx to get the region for a pointer + // operand, which repeats the aforementioned process. Note that we don't have + // fancy caching, so a region is created and merged everytime Regions::idx + // is called. if (!SmackOptions::NoMemoryRegionSplitting) { Region::init(M, *this); visit(M); @@ -332,7 +247,18 @@ void Regions::visitAtomicRMWInst(AtomicRMWInst &I) { idx(I.getPointerOperand()); } -void Regions::visitMemIntrinsic(MemIntrinsic &I) { +void Regions::visitMemSetInst(MemSetInst &I) { + unsigned length; + + if (auto CI = dyn_cast(I.getLength())) + length = CI->getZExtValue(); + else + length = std::numeric_limits::max(); + + idx(I.getDest(), length); +} + +void Regions::visitMemTransferInst(MemTransferInst &I) { unsigned length; if (auto CI = dyn_cast(I.getLength())) @@ -340,6 +266,10 @@ void Regions::visitMemIntrinsic(MemIntrinsic &I) { else length = std::numeric_limits::max(); + // We need to visit the source location otherwise + // extra merges will happen in the translation phrase, + // resulting in ``hanging'' regions. + idx(I.getSource(), length); idx(I.getDest(), length); } @@ -347,7 +277,7 @@ void Regions::visitCallInst(CallInst &I) { Function *F = I.getCalledFunction(); std::string name = F && F->hasName() ? F->getName().str() : ""; - if (I.getType()->isPointerTy()) + if (F && F->isDeclaration() && I.getType()->isPointerTy() && name != "malloc") idx(&I); if (name.find("__SMACK_values") != std::string::npos) { diff --git a/lib/smack/SmackInstGenerator.cpp b/lib/smack/SmackInstGenerator.cpp index 55fcd4807..cf9d14645 100644 --- a/lib/smack/SmackInstGenerator.cpp +++ b/lib/smack/SmackInstGenerator.cpp @@ -16,7 +16,6 @@ #include "llvm/Support/GraphWriter.h" #include -#include "dsa/DSNode.h" #include "llvm/Support/raw_ostream.h" #include @@ -539,7 +538,8 @@ void SmackInstGenerator::visitAtomicCmpXchgInst(llvm::AtomicCmpXchgInst &i) { const Expr *cmp = rep->expr(i.getOperand(1)); const Expr *swp = rep->expr(i.getOperand(2)); emit(Stmt::assign(res, mem)); - emit(rep->store(i.getOperand(0), Expr::cond(Expr::eq(mem, cmp), swp, mem))); + emit(rep->store(i.getOperand(0), + Expr::ifThenElse(Expr::eq(mem, cmp), swp, mem))); } void SmackInstGenerator::visitAtomicRMWInst(llvm::AtomicRMWInst &i) { @@ -623,7 +623,7 @@ void SmackInstGenerator::visitSelectInst(llvm::SelectInst &i) { "Vector condition is not supported."); emit(Stmt::assign( Expr::id(x), - Expr::if_then_else(Expr::eq(c, rep->integerLit(1LL, 1)), v1, v2))); + Expr::ifThenElse(Expr::eq(c, rep->integerLit(1LL, 1)), v1, v2))); } void SmackInstGenerator::visitCallInst(llvm::CallInst &ci) { @@ -645,10 +645,8 @@ void SmackInstGenerator::visitCallInst(llvm::CallInst &ci) { } else if (name.find(Naming::RUST_ENTRY) != std::string::npos) { // Set the entry point for Rust programs auto castExpr = ci.getArgOperand(0); - if (auto CE = dyn_cast(castExpr)) { - auto mainFunc = CE->getOperand(0); - emit(Stmt::call(mainFunc->getName(), {}, {})); - } + auto mainFunction = cast(castExpr); + emit(Stmt::call(mainFunction->getName(), {}, {})); } else if (name.find(Naming::RUST_PANIC1) != std::string::npos || name.find(Naming::RUST_PANIC2) != std::string::npos) { @@ -718,7 +716,7 @@ void SmackInstGenerator::visitCallInst(llvm::CallInst &ci) { // args.push_back(Expr::id(m.first)); // auto E = Expr::fn(F->getName(), args); // emit(Stmt::assign(rep->expr(&ci), - // Expr::cond(Expr::forall(binding, "int", E), + // Expr::ifThenElse(Expr::forall(binding, "int", E), // rep->integerLit(1U,1), rep->integerLit(0U,1)))); } else if (name == Naming::CONTRACT_REQUIRES || @@ -799,9 +797,9 @@ void SmackInstGenerator::visitCallInst(llvm::CallInst &ci) { emit(rep->call(f, ci)); } - if (f->isDeclaration() && rep->isExternal(&ci)) { + if (f->isDeclaration()) { std::string name = naming->get(*f); - if (!EXTERNAL_PROC_IGNORE.match(name)) + if (!EXTERNAL_PROC_IGNORE.match(name) && rep->isExternal(&ci)) emit(Stmt::assume(Expr::fn(Naming::EXTERNAL_ADDR, rep->expr(&ci)))); } @@ -902,6 +900,28 @@ void SmackInstGenerator::visitIntrinsicInst(llvm::IntrinsicInst &ii) { }; }; + // Optionally generate a boogie assume statement from assume statements in + // LLVM. Currently this behavior is experimental and must be enabled by + // passing the -llvm-assumes flag. The default behavior of this + // function is to ignore the assume statement, specified by the "none" + // argument. If the check argument is given, an additional assertion is + // generated to check the validity of the assumption. + static const auto assume = [this](CallInst *ci) { + if (SmackOptions::LLVMAssumes != LLVMAssumeType::none) { + auto arg = rep->expr(ci->getArgOperand(0)); + auto llvmTrue = + SmackOptions::BitPrecise ? Expr::lit(1, 1) : Expr::lit(1LL); + auto chkStmt = Expr::eq(arg, llvmTrue); + if (SmackOptions::LLVMAssumes == LLVMAssumeType::check) + emit(Stmt::assert_(chkStmt)); + else + emit(Stmt::assume(chkStmt)); + } else { + // Skip assume statements + return; + } + }; + static const auto f16UpCast = conditionalModel( [this](CallInst *ci) { // translation: $f := $fpext.bvhalf.*($rmode, $bitcast.bv16.bvhalf($i)); @@ -999,7 +1019,7 @@ void SmackInstGenerator::visitIntrinsicInst(llvm::IntrinsicInst &ii) { // ... else if v[1:0] == 1 then 31bv32 else 32bv32 const Expr *body = Expr::lit(width, width); for (unsigned i = 0; i < width; ++i) { - body = Expr::if_then_else( + body = Expr::ifThenElse( Expr::eq(Expr::bvExtract(var, i + 1, i), Expr::lit(1, 1)), Expr::lit(width - i - 1, width), body); } @@ -1008,11 +1028,11 @@ void SmackInstGenerator::visitIntrinsicInst(llvm::IntrinsicInst &ii) { // argument // is zero, then the result is undefined. auto isZeroUndef = rep->expr(ci->getArgOperand(1)); - body = Expr::if_then_else( - Expr::and_(Expr::eq(isZeroUndef, Expr::lit(1, 1)), - Expr::eq(var, Expr::lit(0, width))), - rep->expr(ci), // The result is undefined - body); + body = + Expr::ifThenElse(Expr::and_(Expr::eq(isZeroUndef, Expr::lit(1, 1)), + Expr::eq(var, Expr::lit(0, width))), + rep->expr(ci), // The result is undefined + body); emit(Stmt::havoc(rep->expr(ci))); emit(Stmt::assign(rep->expr(ci), body)); }, @@ -1028,7 +1048,7 @@ void SmackInstGenerator::visitIntrinsicInst(llvm::IntrinsicInst &ii) { // ... else if v[32:31] == 1 then 31bv32 else 32bv32 const Expr *body = Expr::lit(width, width); for (unsigned i = width; i > 0; --i) { - body = Expr::if_then_else( + body = Expr::ifThenElse( Expr::eq(Expr::bvExtract(arg, i, i - 1), Expr::lit(1, 1)), Expr::lit(i - 1, width), body); } @@ -1037,11 +1057,11 @@ void SmackInstGenerator::visitIntrinsicInst(llvm::IntrinsicInst &ii) { // argument // is zero, then the result is undefined. auto isZeroUndef = rep->expr(ci->getArgOperand(1)); - body = Expr::if_then_else( - Expr::and_(Expr::eq(isZeroUndef, Expr::lit(1, 1)), - Expr::eq(arg, Expr::lit(0, width))), - rep->expr(ci), // The result is undefined - body); + body = + Expr::ifThenElse(Expr::and_(Expr::eq(isZeroUndef, Expr::lit(1, 1)), + Expr::eq(arg, Expr::lit(0, width))), + rep->expr(ci), // The result is undefined + body); emit(Stmt::havoc(rep->expr(ci))); emit(Stmt::assign(rep->expr(ci), body)); }, @@ -1130,6 +1150,7 @@ void SmackInstGenerator::visitIntrinsicInst(llvm::IntrinsicInst &ii) { static const std::map> stmtMap{ + {llvm::Intrinsic::assume, assume}, {llvm::Intrinsic::bitreverse, assignBvExpr(bitreverse)}, {llvm::Intrinsic::bswap, assignBvExpr(bswap)}, {llvm::Intrinsic::convert_from_fp16, f16UpCast}, diff --git a/lib/smack/SmackOptions.cpp b/lib/smack/SmackOptions.cpp index 6c835170c..1376bede0 100644 --- a/lib/smack/SmackOptions.cpp +++ b/lib/smack/SmackOptions.cpp @@ -66,6 +66,17 @@ const llvm::cl::opt const llvm::cl::opt SmackOptions::IntegerOverflow( "integer-overflow", llvm::cl::desc("Enable integer overflow checks")); +const llvm::cl::opt SmackOptions::LLVMAssumes( + "llvm-assumes", + llvm::cl::desc( + "Optionally enable generation of Boogie assumes from LLVM assumes"), + llvm::cl::values(clEnumValN(LLVMAssumeType::none, "none", + "disable generation of assume statements"), + clEnumValN(LLVMAssumeType::use, "use", + "enable generation of assume statements"), + clEnumValN(LLVMAssumeType::check, "check", + "enable checking of assume statements"))); + bool SmackOptions::isEntryPoint(std::string name) { for (auto EP : EntryPoints) if (name == EP) diff --git a/lib/smack/SmackRep.cpp b/lib/smack/SmackRep.cpp index 0067f7c57..825b1c958 100644 --- a/lib/smack/SmackRep.cpp +++ b/lib/smack/SmackRep.cpp @@ -937,8 +937,8 @@ const Expr *SmackRep::cmp(unsigned predicate, const llvm::Value *lhs, const Expr *e1 = expr(lhs, isUnsigned); const Expr *e2 = expr(rhs, isUnsigned); if (lhs->getType()->isFloatingPointTy()) - return Expr::if_then_else(Expr::fn(fn + ".bool", e1, e2), - integerLit(1ULL, 1), integerLit(0ULL, 1)); + return Expr::ifThenElse(Expr::fn(fn + ".bool", e1, e2), integerLit(1ULL, 1), + integerLit(0ULL, 1)); else return Expr::fn(fn, e1, e2); } diff --git a/lib/AssistDS/Devirt.cpp b/lib/utils/Devirt.cpp similarity index 96% rename from lib/AssistDS/Devirt.cpp rename to lib/utils/Devirt.cpp index 3fe33086e..a4b7c907f 100644 --- a/lib/AssistDS/Devirt.cpp +++ b/lib/utils/Devirt.cpp @@ -9,7 +9,7 @@ #define DEBUG_TYPE "devirt" -#include "assistDS/Devirt.h" +#include "utils/Devirt.h" #include "smack/Debug.h" #include "llvm/Support/CommandLine.h" @@ -374,16 +374,12 @@ Devirtualize::makeDirectCall (CallSite & CS) { std::vector Targets; - if (CTF->size(CS) && CTF->isComplete(CS)) { - // TODO should we allow non-matching targets? - // TODO non-matching targets leads to crashes in bounce creation - // TODO formerly, all call-target-finder tarets were included: - // Targets.insert (Targets.begin(), CTF->begin(CS), CTF->end(CS)); - // TODO presently we filter out unmatching targets: - for (auto F = CTF->begin(CS); F != CTF->end(CS); ++F) + // TODO should we allow non-matching targets? + // TODO non-matching targets leads to crashes in bounce creation + if (CCG->isComplete(CS)) { + for (auto F = CCG->begin(CS); F != CCG->end(CS); ++F) if (match(CS, **F)) Targets.push_back(*F); - } else { for (auto &F : *CS.getInstruction()->getParent()->getParent()->getParent()) if (F.hasAddressTaken() && match(CS, F)) @@ -472,7 +468,7 @@ Devirtualize::visitCallSite (CallSite &CS) { // Second, we will only transform those call sites which are complete (i.e., // for which we know all of the call targets). // - if (SKIP_INCOMPLETE_NODES && !CTF->isComplete(CS)) + if (SKIP_INCOMPLETE_NODES && !CCG->isComplete(CS)) return; // @@ -495,7 +491,7 @@ Devirtualize::runOnModule (Module & M) { // // Get the targets of indirect function calls. // - CTF = &getAnalysis >(); + CCG = &getAnalysis(); // // Get information on the target system. diff --git a/include/dsa/LICENSE.TXT b/lib/utils/LICENSE.TXT similarity index 100% rename from include/dsa/LICENSE.TXT rename to lib/utils/LICENSE.TXT diff --git a/lib/AssistDS/MergeGEP.cpp b/lib/utils/MergeGEP.cpp similarity index 99% rename from lib/AssistDS/MergeGEP.cpp rename to lib/utils/MergeGEP.cpp index 9d9e7cdf9..77d1e0fa0 100644 --- a/lib/AssistDS/MergeGEP.cpp +++ b/lib/utils/MergeGEP.cpp @@ -12,7 +12,7 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "merge-gep" -#include "assistDS/MergeGEP.h" +#include "utils/MergeGEP.h" #include "llvm/Pass.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Instructions.h" diff --git a/lib/AssistDS/SimplifyExtractValue.cpp b/lib/utils/SimplifyExtractValue.cpp similarity index 99% rename from lib/AssistDS/SimplifyExtractValue.cpp rename to lib/utils/SimplifyExtractValue.cpp index e645fae6f..8cf04ecf9 100644 --- a/lib/AssistDS/SimplifyExtractValue.cpp +++ b/lib/utils/SimplifyExtractValue.cpp @@ -14,7 +14,7 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "simplifyev" -#include "assistDS/SimplifyExtractValue.h" +#include "utils/SimplifyExtractValue.h" #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/ArrayRef.h" diff --git a/lib/AssistDS/SimplifyInsertValue.cpp b/lib/utils/SimplifyInsertValue.cpp similarity index 98% rename from lib/AssistDS/SimplifyInsertValue.cpp rename to lib/utils/SimplifyInsertValue.cpp index 7bf59dd91..9614b64b5 100644 --- a/lib/AssistDS/SimplifyInsertValue.cpp +++ b/lib/utils/SimplifyInsertValue.cpp @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "simplify-iv" -#include "assistDS/SimplifyInsertValue.h" +#include "utils/SimplifyInsertValue.h" #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/FormattedStream.h" diff --git a/sea-dsa b/sea-dsa new file mode 160000 index 000000000..855c78b30 --- /dev/null +++ b/sea-dsa @@ -0,0 +1 @@ +Subproject commit 855c78b304f275a5ed2f527d35ea4a53a7070338 diff --git a/share/smack/doctor.py b/share/smack/doctor.py index 329d26c96..ef09832a0 100755 --- a/share/smack/doctor.py +++ b/share/smack/doctor.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # # This file is distributed under the MIT License. See LICENSE for details. # @@ -21,9 +21,9 @@ def check(text, condition): global count if condition: if not args.quiet: - print green("[X] " + text) + print(green("[X] " + text)) else: - print >> sys.stderr, red("[-] " + text) + print(red("[-] " + text), file=sys.stderr) count += 1 def full_path(program): @@ -88,19 +88,19 @@ def main(): count = 0 if not args.quiet: - print "Checking front-end dependencies..." + print("Checking front-end dependencies...") check_command("clang") check_command("clang++") check_command("llvm-config") check_command("llvm-link") if not args.quiet: - print "Checking back-end dependencies..." + print("Checking back-end dependencies...") check_verifier("boogie") check_verifier("corral") if not args.quiet: - print "Checking SMACK itself..." + print("Checking SMACK itself...") check_command("llvm2bpl") check_command("smack") diff --git a/share/smack/frontend.py b/share/smack/frontend.py index 9927fae10..def79535e 100644 --- a/share/smack/frontend.py +++ b/share/smack/frontend.py @@ -1,6 +1,6 @@ import os import sys -from utils import temporary_file, try_command +from .utils import temporary_file, try_command def languages(): """A dictionary of languages per file extension.""" @@ -28,7 +28,7 @@ def frontends(): """A dictionary of front-ends per language.""" # Avoid circular import - from svcomp.utils import svcomp_frontend + from .svcomp.utils import svcomp_frontend return { 'c' : clang_frontend, @@ -72,12 +72,13 @@ def default_clang_compile_command(args, lib = False): # in order to enable optimization passes. # See: https://stackoverflow.com/a/46753969. cmd += ['-Xclang', '-disable-O0-optnone'] - cmd += map(lambda path: '-I' + path, smack_headers(args)) + cmd += ['-I' + path for path in smack_headers(args)] cmd += args.clang_options.split() cmd += ['-DMEMORY_MODEL_' + args.mem_mod.upper().replace('-','_')] if args.memory_safety: cmd += ['-DMEMORY_SAFETY'] if args.integer_overflow: cmd += (['-fsanitize=signed-integer-overflow,shift'] if not lib else ['-DSIGNED_INTEGER_OVERFLOW_CHECK']) if args.float: cmd += ['-DFLOAT_ENABLED'] + if args.bit_precise: cmd += ['-DBIT_PRECISE'] if sys.stdout.isatty(): cmd += ['-fcolor-diagnostics'] return cmd @@ -156,7 +157,7 @@ def d_frontend(input_file, args): # note: -g and -O0 are not used here. # Right now, it works, and with these options, smack crashes. compile_command = ['ldc2', '-output-ll'] - compile_command += map(lambda path: '-I=' + path, smack_headers(args)) + compile_command += ['-I=' + path for path in smack_headers(args)] args.entry_points += ['_Dmain'] return d_compile_to_bc(input_file,compile_command,args) @@ -202,7 +203,7 @@ def json_compilation_database_frontend(input_file, args): for cc in json.load(f): if 'objects' in cc: # TODO what to do when there are multiple linkings? - bit_codes = map(lambda f: re.sub('[.]o$','.bc',f), cc['objects']) + bit_codes = [re.sub('[.]o$','.bc',f) for f in cc['objects']] try_command(['llvm-link', '-o', args.bc_file] + bit_codes) try_command(['llvm-link', '-o', args.linked_bc_file, args.bc_file] + build_libs(args)) @@ -220,22 +221,20 @@ def rust_frontend(input_file, args): """Generate Boogie code from Rust programming language source(s).""" compile_command = ['rustc', '-A', 'unused-imports', '-C', 'opt-level=0', '-C', 'no-prepopulate-passes', '-g', '--emit=llvm-bc', - '--cfg', 'verifier="smack"'] + '--cfg', 'verifier="smack"', '-C', 'passes=name-anon-globals'] # This links in the Rust SMACK library. This is needed due to the way rustc - # finds a programs libraries. + # finds a program's libraries. + abs_path = os.path.dirname(os.path.abspath(input_file)) + link_target = os.path.join(abs_path, "smack.rs") + link_source = os.path.join(smack_lib(), 'smack.rs') try: - abs_path = os.path.dirname(os.path.abspath(input_file)) - mod_path = os.path.join(abs_path, "smack") - if not os.path.exists(mod_path): - os.mkdir(mod_path) - link_target = os.path.join(mod_path, "mod.rs") - if not os.path.exists(link_target): - rust_macros = os.path.join(smack_lib(), 'smack.rs') - os.symlink(rust_macros, link_target) + os.symlink(link_source, link_target) except: - raise RuntimeError("Could not find or create smack module.") - + if not os.path.exists(link_source): + raise RuntimeError("Could not find or create smack module.") + # Otherwise okay + return compile_to_bc(input_file,compile_command,args) # Build libs functions here @@ -256,7 +255,7 @@ def default_build_libs(args): libs += ['fenv.c'] compile_command = default_clang_compile_command(args, True) - for c in map(lambda c: os.path.join(smack_lib(), c), libs): + for c in [os.path.join(smack_lib(), c) for c in libs]: bc = compile_to_bc(c,compile_command,args) bitcodes.append(bc) @@ -271,7 +270,7 @@ def fortran_build_libs(args): compile_command = default_clang_compile_command(args) compile_command[0] = 'flang' - for c in map(lambda c: os.path.join(smack_lib(), c), libs): + for c in [os.path.join(smack_lib(), c) for c in libs]: bc = fortran_compile_to_bc(c,compile_command,args) bitcodes.append(bc) @@ -286,7 +285,7 @@ def cplusplus_build_libs(args): compile_command = default_clang_compile_command(args,True) compile_command[0] = 'clang++' - for c in map(lambda c: os.path.join(smack_lib(), c), libs): + for c in [os.path.join(smack_lib(), c) for c in libs]: bc = compile_to_bc(c,compile_command,args) bitcodes.append(bc) @@ -305,6 +304,6 @@ def link_bc_files(bitcodes, libs, args): try_command(['llvm-link', '-o', args.linked_bc_file, args.bc_file] + smack_libs) # import here to avoid a circular import - from top import llvm_to_bpl + from .top import llvm_to_bpl llvm_to_bpl(args) diff --git a/share/smack/include/pthreadtypes.h b/share/smack/include/bits/pthreadtypes.h similarity index 85% rename from share/smack/include/pthreadtypes.h rename to share/smack/include/bits/pthreadtypes.h index 151cecc72..50c542a44 100644 --- a/share/smack/include/pthreadtypes.h +++ b/share/smack/include/bits/pthreadtypes.h @@ -39,5 +39,13 @@ typedef struct { } pthread_cond_t; typedef int pthread_condattr_t; +typedef int pthread_once_t; +typedef int pthread_key_t; +typedef int pthread_rwlock_t; +typedef int pthread_rwlockattr_t; + +struct sched_param { + int dummy; +}; #endif diff --git a/share/smack/include/bits/types/struct_timespec.h b/share/smack/include/bits/types/struct_timespec.h new file mode 100644 index 000000000..f11b7491c --- /dev/null +++ b/share/smack/include/bits/types/struct_timespec.h @@ -0,0 +1,11 @@ +// +// This file is distributed under the MIT License. See LICENSE for details. +// +#ifndef STRUCT_TIMESPEC_H +#define STRUCT_TIMESPEC_H + +struct timespec { + int dummy; +}; + +#endif diff --git a/share/smack/include/pthread.h b/share/smack/include/pthread.h index d93e54ce4..aa195ae00 100644 --- a/share/smack/include/pthread.h +++ b/share/smack/include/pthread.h @@ -4,7 +4,9 @@ #ifndef PTHREAD_H #define PTHREAD_H -#include "pthreadtypes.h" +#include "bits/pthreadtypes.h" +#include "bits/types/struct_timespec.h" +#include // model mutex: // 0 = unlocked, @@ -18,59 +20,81 @@ #define PTHREAD_MUTEX_INITIALIZER \ { UNLOCKED, INITIALIZED } -// Declare corral primitive procedures -//__SMACK_INIT(corral_primitives); - -// Initialize thread status tracking variables (OS level behavior) -//__SMACK_INIT(thread); - -void __VERIFIER_atomic_begin(); - -void __VERIFIER_atomic_end(); - -pthread_t pthread_self(void); - +int pthread_atfork(void (*prepare)(void), void (*parent)(void), + void (*child)(void)); +int pthread_attr_destroy(pthread_attr_t *attr); +int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); +int pthread_attr_getinheritsched(pthread_attr_t *attr, int *inheritsched); +int pthread_attr_getschedparam(pthread_attr_t *attr, struct sched_param *param); +int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy); +int pthread_attr_getscope(pthread_attr_t *attr, int *scope); +int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr); +int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize); +int pthread_attr_init(pthread_attr_t *attr); +int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); +int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched); +int pthread_attr_setschedparam(pthread_attr_t *attr, + const struct sched_param *param); +int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy); +int pthread_attr_setscope(pthread_attr_t *attr, int scope); +int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr); +int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); +int pthread_cancel(pthread_t thread); +void pthread_cleanup_pop(int execute); +void pthread_cleanup_push(void (*routine)(void *), void *routine_arg); +int pthread_create(pthread_t *__newthread, __const pthread_attr_t *__attr, + void *(*__start_routine)(void *), void *__arg); +int pthread_cond_broadcast(pthread_cond_t *cond); +int pthread_cond_destroy(pthread_cond_t *cond); +int pthread_cond_init(pthread_cond_t *__cond, pthread_condattr_t *__condattr); +int pthread_cond_signal(pthread_cond_t *cond); +int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, + const struct timespec *abstime); +int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *mutex); +int pthread_condattr_destroy(pthread_condattr_t *attr); +int pthread_condattr_init(pthread_condattr_t *attr); +int pthread_detach(pthread_t thread); int pthread_equal(pthread_t t1, pthread_t t2); - -int pthread_join(pthread_t __th, void **__thread_return); - void pthread_exit(void *retval); - +void *pthread_getspecific(pthread_key_t key); +int pthread_join(pthread_t thread, void **retval); +int pthread_key_create(pthread_key_t *key, void (*routine)(void *)); +int pthread_key_delete(pthread_key_t key); +int pthread_kill(pthread_t thread, int sig); +int pthread_mutex_destroy(pthread_mutex_t *mutex); +int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); +int pthread_mutex_lock(pthread_mutex_t *mutex); +int pthread_mutex_trylock(pthread_mutex_t *mutex); +int pthread_mutex_unlock(pthread_mutex_t *mutex); +int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); +int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *attr, + int *prioceiling); +int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr, + int *protocol); +int pthread_mutexattr_gettype(pthread_mutexattr_t *attr, int *type); int pthread_mutexattr_init(pthread_mutexattr_t *attr); - +int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *attr, + int prioceiling); +int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol); int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type); - -int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); - -int pthread_mutex_lock(pthread_mutex_t *__mutex); - -int pthread_mutex_unlock(pthread_mutex_t *__mutex); - -int pthread_mutex_destroy(pthread_mutex_t *__mutex); - -int pthread_cond_init(pthread_cond_t *__cond, pthread_condattr_t *__condattr); - -int pthread_cond_wait(pthread_cond_t *__cond, pthread_mutex_t *__mutex); - -int pthread_cond_signal(pthread_cond_t *__cond); - -// NOTE: How to handle broadcast? Technically, as is, all threads have -// the opportunity to read __cond->cond before one of the other threads -// switch this value back to 0... I guess this is sufficient, but -// will this require a high number of context switches for a true -// broadcast to happen? -// -// I thought about using cond->cond = 2 to signal broadcast, but then -// how to know which thread to have switch it back to 0? - -int pthread_cond_broadcast(pthread_cond_t *__cond); - -int pthread_cond_destroy(pthread_cond_t *__cond); - -void __call_wrapper(pthread_t *__newthread, void *(*__start_routine)(void *), - void *__arg); - -int pthread_create(pthread_t *__newthread, __const pthread_attr_t *__attr, - void *(*__start_routine)(void *), void *__arg); +int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)); +int pthread_rwlock_destroy(pthread_rwlock_t *lock); +int pthread_rwlock_init(pthread_rwlock_t *rwlock, + const pthread_rwlockattr_t *attr); +int pthread_rwlock_rdlock(pthread_rwlock_t *lock); +int pthread_rwlock_tryrdlock(pthread_rwlock_t *lock); +int pthread_rwlock_trywrlock(pthread_rwlock_t *lock); +int pthread_rwlock_unlock(pthread_rwlock_t *lock); +int pthread_rwlock_wrlock(pthread_rwlock_t *lock); +int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr); +int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, + int *pshared); +int pthread_rwlockattr_init(pthread_rwlockattr_t *attr); +int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared); +pthread_t pthread_self(void); +int pthread_setcancelstate(int state, int *oldstate); +int pthread_setcanceltype(int type, int *oldtype); +int pthread_setspecific(pthread_key_t key, const void *value_ptr); +void pthread_testcancel(void); #endif // PTHREAD_H diff --git a/share/smack/include/smack.h b/share/smack/include/smack.h index e633a01fb..30fa3d297 100644 --- a/share/smack/include/smack.h +++ b/share/smack/include/smack.h @@ -58,8 +58,16 @@ void __VERIFIER_assert(int); void __VERIFIER_error(void); #ifndef AVOID_NAME_CONFLICTS -#define assert(EX) __VERIFIER_assert(EX) -#define assume(EX) __VERIFIER_assume(EX) +#define assert(EX) \ + do { \ + if (!(EX)) \ + __VERIFIER_assert(0); \ + } while (0) +#define assume(EX) \ + do { \ + if (!(EX)) \ + __VERIFIER_assume(0); \ + } while (0) #endif #define S4(a, b, c, d) a b c d @@ -160,6 +168,10 @@ void *__VERIFIER_nondet_pointer(void); void __SMACK_decls(void); +// Concurrency helper functions +void __VERIFIER_atomic_begin(); +void __VERIFIER_atomic_end(); + #ifdef __cplusplus } #endif diff --git a/share/smack/lib/math.c b/share/smack/lib/math.c index 248cf3da7..303a06567 100644 --- a/share/smack/lib/math.c +++ b/share/smack/lib/math.c @@ -354,7 +354,7 @@ long double fdiml(long double x, long double y) { if (__isnanl(x) || __isnanl(y)) { return nanl(0); } - double val = __VERIFIER_nondet_long_double(); + long double val = __VERIFIER_nondet_long_double(); __SMACK_code("@ := $fsub.bvlongdouble($rmode, @, @);", val, x, y); return fmaxl(0.0l, val); } diff --git a/share/smack/lib/pthread.c b/share/smack/lib/pthread.c index 3f87e68a2..f15608683 100644 --- a/share/smack/lib/pthread.c +++ b/share/smack/lib/pthread.c @@ -4,32 +4,34 @@ #include "pthread.h" #include "smack.h" +void __SMACK_init_tidtype() { +#ifdef BIT_PRECISE + __SMACK_top_decl("type $tidtype = bv32;"); +#else + __SMACK_top_decl("type $tidtype = i32;"); +#endif +} + void __SMACK_init_func_corral_primitives() { // Declare these, so bpl parsing doesn't complain - __SMACK_top_decl("procedure corral_getThreadID() returns (x:int);"); - __SMACK_top_decl("procedure corral_getChildThreadID() returns (x:int);"); - __SMACK_top_decl("procedure corral_atomic_begin();"); - __SMACK_top_decl("procedure corral_atomic_end();"); + __SMACK_top_decl("procedure corral_getThreadID() returns (x:$tidtype);"); + __SMACK_top_decl("procedure corral_getChildThreadID() returns (x:$tidtype);"); } void __SMACK_init_func_thread() { // Array and possible statuses for tracking pthreads __SMACK_top_decl("//dim0=tid, dim1= idx 0 gets status, 1 gets return value"); - __SMACK_top_decl("var $pthreadStatus: [int][int]int;"); + __SMACK_top_decl("var $pthreadStatus: [$tidtype][int]int;"); __SMACK_top_decl("const unique $pthread_uninitialized: int;"); __SMACK_top_decl("const unique $pthread_initialized: int;"); __SMACK_top_decl("const unique $pthread_waiting: int;"); __SMACK_top_decl("const unique $pthread_running: int;"); __SMACK_top_decl("const unique $pthread_stopped: int;"); // Initialize this array so all threads begin as uninitialized - __SMACK_code("assume (forall i:int :: $pthreadStatus[i][0] == " + __SMACK_code("assume (forall i:$tidtype :: $pthreadStatus[i][0] == " "$pthread_uninitialized);"); } -void __VERIFIER_atomic_begin() { __SMACK_code("call corral_atomic_begin();"); } - -void __VERIFIER_atomic_end() { __SMACK_code("call corral_atomic_end();"); } - pthread_t pthread_self(void) { int tmp_tid = __VERIFIER_nondet_int(); __SMACK_code("call @ := corral_getThreadID();", tmp_tid); @@ -61,10 +63,13 @@ int pthread_join(pthread_t __th, void **__thread_return) { // Get the thread's return value void *tmp_thread_return_pointer = (void *)__VERIFIER_nondet_long(); __SMACK_code("@ := $pthreadStatus[@][1];", tmp_thread_return_pointer, __th); - *__thread_return = tmp_thread_return_pointer; - // Print return pointer value to SMACK traces - void *actual_thread_return_pointer = *__thread_return; + if (__thread_return) { + *__thread_return = tmp_thread_return_pointer; + + // Print return pointer value to SMACK traces + void *actual_thread_return_pointer = *__thread_return; + } return 0; } diff --git a/share/smack/lib/smack.c b/share/smack/lib/smack.c index 23692291a..c984ad493 100644 --- a/share/smack/lib/smack.c +++ b/share/smack/lib/smack.c @@ -268,9 +268,15 @@ void __SMACK_decls(void) { D("var $exn: bool;"); D("var $exnv: int;"); + // Concurrency primitives + D("procedure corral_atomic_begin();"); + D("procedure corral_atomic_end();"); + D("procedure $alloc(n: ref) returns (p: ref)\n" "{\n" + " call corral_atomic_begin();\n" " call p := $$alloc(n);\n" + " call corral_atomic_end();\n" "}\n"); #if MEMORY_SAFETY @@ -297,10 +303,12 @@ void __SMACK_decls(void) { D("procedure $malloc(n: ref) returns (p: ref)\n" "modifies $allocatedCounter;\n" "{\n" + " call corral_atomic_begin();\n" " if ($ne.ref.bool(n, $0.ref)) {\n" " $allocatedCounter := $allocatedCounter + 1;\n" " }\n" " call p := $$alloc(n);\n" + " call corral_atomic_end();\n" "}\n"); #if MEMORY_MODEL_NO_REUSE_IMPLS @@ -308,6 +316,8 @@ void __SMACK_decls(void) { D("function $Size(ref) returns (ref);"); D("var $CurrAddr:ref;\n"); + // LLVM does not allocated globals explicitly. Hence, we do it in our prelude + // before the program starts using the $galloc procedure. D("procedure $galloc(base_addr: ref, size: ref)\n" "{\n" " assume $Size(base_addr) == size;\n" @@ -339,6 +349,7 @@ void __SMACK_decls(void) { D("procedure $free(p: ref)\n" "modifies $Alloc, $allocatedCounter;\n" "{\n" + " call corral_atomic_begin();\n" " if ($ne.ref.bool(p, $0.ref)) {\n" " assert {:valid_free} $eq.ref.bool($base(p), p);\n" " assert {:valid_free} $Alloc[p];\n" @@ -346,12 +357,15 @@ void __SMACK_decls(void) { " $Alloc[p] := false;\n" " $allocatedCounter := $allocatedCounter - 1;\n" " }\n" + " call corral_atomic_end();\n" "}\n"); #elif MEMORY_MODEL_REUSE // can reuse previously-allocated and freed addresses D("var $Alloc: [ref] bool;"); D("var $Size: [ref] ref;\n"); + // LLVM does not allocated globals explicitly. Hence, we do it in our prelude + // before the program starts using the $galloc procedure. D("procedure $galloc(base_addr: ref, size: ref);\n" "modifies $Alloc, $Size;\n" "ensures $Size[base_addr] == size;\n" @@ -398,6 +412,8 @@ void __SMACK_decls(void) { D("function $Size(ref) returns (ref);"); D("var $CurrAddr:ref;\n"); + // LLVM does not allocated globals explicitly. Hence, we do it in our prelude + // before the program starts using the $galloc procedure. D("procedure $galloc(base_addr: ref, size: ref);\n" "modifies $Alloc;\n" "ensures $Size(base_addr) == size;\n" @@ -441,7 +457,9 @@ void __SMACK_decls(void) { #else D("procedure $malloc(n: ref) returns (p: ref)\n" "{\n" + " call corral_atomic_begin();\n" " call p := $$alloc(n);\n" + " call corral_atomic_end();\n" "}\n"); #if MEMORY_MODEL_NO_REUSE_IMPLS @@ -524,3 +542,7 @@ void __SMACK_init_func_memory_model(void) { __SMACK_code("$allocatedCounter := 0;"); #endif } + +void __VERIFIER_atomic_begin() { __SMACK_code("call corral_atomic_begin();"); } + +void __VERIFIER_atomic_end() { __SMACK_code("call corral_atomic_end();"); } diff --git a/share/smack/lib/stdlib.c b/share/smack/lib/stdlib.c index 76f8be73a..eb98f7e3f 100644 --- a/share/smack/lib/stdlib.c +++ b/share/smack/lib/stdlib.c @@ -19,15 +19,21 @@ void *calloc(size_t num, size_t size) { if (__VERIFIER_nondet_int()) { ret = 0; } else { + __VERIFIER_atomic_begin(); ret = malloc(num * size); memset(ret, 0, num * size); + __VERIFIER_atomic_end(); } return ret; } void *realloc(void *ptr, size_t size) { + void *ret; + __VERIFIER_atomic_begin(); free(ptr); - return malloc(size); + ret = malloc(size); + __VERIFIER_atomic_end(); + return ret; } long int strtol(const char *nptr, char **endptr, int base) { diff --git a/share/smack/reach.py b/share/smack/reach.py index 1ff4f2723..750667ea5 100755 --- a/share/smack/reach.py +++ b/share/smack/reach.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # # This file is distributed under the MIT License. See LICENSE for details. # @@ -11,7 +11,7 @@ from smackgen import * from smackverify import * -VERSION = '2.4.0' +VERSION = '2.4.1' def reachParser(): parser = argparse.ArgumentParser(add_help=False, parents=[verifyParser()]) @@ -106,10 +106,10 @@ def GetCodeCoverage(verifier, bplFileName, timeLimit, unroll, contextSwitches, d result[curfile] = sorted(result[curfile], key=lambda e:e[0], reverse=False) if(smackd): - print(json.dumps(result)) + print((json.dumps(result))) else: - print '\nSMACK verifier version ' + VERSION + '\n\n' - print "Unreachable code:" + print('\nSMACK verifier version ' + VERSION + '\n\n') + print("Unreachable code:") pprint.pprint(result, width=100) def TestReachability(verifier, bplFileName, timeLimit, unroll, contextSwitches, debug, lineInfo): @@ -169,7 +169,7 @@ def main(): # remove arguments not recognized by lower scripts # not sure of a better way to do this sysArgv = sys.argv[:] - for i in reversed(range(len(sysArgv))): + for i in reversed(list(range(len(sysArgv)))): if sysArgv[i] == '--smackd': del sysArgv[i] elif sys.argv[i] == '--time-limit': diff --git a/share/smack/replay.py b/share/smack/replay.py index 3fb996920..8064eafda 100644 --- a/share/smack/replay.py +++ b/share/smack/replay.py @@ -2,7 +2,7 @@ import re import subprocess import sys -from utils import temporary_file, try_command +from .utils import temporary_file, try_command SPECIAL_NAMES = [ '__VERIFIER_assert', @@ -12,36 +12,36 @@ def replay_error_trace(verifier_output, args): if args.verifier != 'corral': - print "Replay for verifiers other than 'corral' currently unsupported; skipping replay" + print("Replay for verifiers other than 'corral' currently unsupported; skipping replay") return - print "Attempting to replay error trace." + print("Attempting to replay error trace.") missing_definitions = detect_missing_definitions(args.bc_file) if '__SMACK_code' in missing_definitions: - print "warning: inline Boogie code found; replay may fail" + print("warning: inline Boogie code found; replay may fail") arguments, return_values = extract_values(verifier_output) with open(args.replay_harness, 'w') as f: f.write(harness(arguments, return_values, missing_definitions)) - print "Generated replay harness:", args.replay_harness + print("Generated replay harness:", args.replay_harness) stubs_bc = temporary_file('stubs', '.bc', args) try_command(['clang', '-c', '-emit-llvm', '-o', stubs_bc, args.replay_harness]) try_command(['clang', '-Wl,-e,_smack_replay_main', '-o', args.replay_exe_file, args.bc_file, stubs_bc]) - print "Generated replay executable:", args.replay_exe_file + print("Generated replay executable:", args.replay_exe_file) try: if 'error reached!' in try_command(["./" + args.replay_exe_file]): - print "Error-trace replay successful." + print("Error-trace replay successful.") return True else: - print "Error-trace replay failed." + print("Error-trace replay failed.") except Exception as err: - print "Error-trace replay caught", err.message + print("Error-trace replay caught", err.message) return False @@ -67,7 +67,7 @@ def extract_values(trace): arguments = {} return_values = {} - for key, val in filter(lambda x: x, map(extract, trace.split('\n'))): + for key, val in [x for x in map(extract, trace.split('\n')) if x]: if 'smack:entry:' in key: _, _, fn = key.split(':') arguments[fn] = [] @@ -85,7 +85,7 @@ def extract_values(trace): return_values[fn].append(val) else: - print "warning: unexpected key %s" % key + print("warning: unexpected key %s" % key) return arguments, return_values @@ -128,7 +128,7 @@ def harness(arguments, return_values, missing_definitions): """ % {'fn': fn, 'vals': ", ".join(return_values[fn])}) else: - print "warning: unknown return value for %s" % fn + print("warning: unknown return value for %s" % fn) code.append("""// stub for function %(fn)s void %(fn)s() { return; @@ -136,12 +136,12 @@ def harness(arguments, return_values, missing_definitions): """ % {'fn': fn}) if len(arguments) > 1: - print "warning: multiple entrypoint argument annotations found" + print("warning: multiple entrypoint argument annotations found") elif len(arguments) < 1: - print "warning: no entrypoint argument annotations found" + print("warning: no entrypoint argument annotations found") - for fn, args in arguments.items(): + for fn, args in list(arguments.items()): code.append("""// entry point wrapper int _smack_replay_main() { %(fn)s(%(vals)s); diff --git a/share/smack/svcomp/filters.py b/share/smack/svcomp/filters.py index 1a0c8a47e..8e0ccda6d 100644 --- a/share/smack/svcomp/filters.py +++ b/share/smack/svcomp/filters.py @@ -175,8 +175,10 @@ def scrub_pthreads(s): fltrs.append(r'typedef\s+union\s+{\s+struct\s+__pthread_mutex_s\s+{\s+int\s+__lock;\s+unsigned\s+int\s+__count;\s+int\s+__owner;\s+unsigned\s+int\s+__nusers;\s+int\s+__kind;\s+short\s+__spins;\s+short\s+__elision;\s+__pthread_list_t\s+__list;\s+# 124 "/usr/include/x86_64-linux-gnu/bits/pthreadtypes\.h" 3 4\s+}\s+__data;\s+\s+char\s+__size\[\d+\];\s+long\s+int\s+__align;\s+} pthread_mutex_t;') fltrs.append(r'typedef\s+union\s+{\s+struct\s+__pthread_mutex_s\s+{\s+int\s+__lock;\s+unsigned\s+int\s+__count;\s+int\s+__owner;\s+int\s+__kind;\s+unsigned\s+int\s+__nusers;\s+__extension__\s+union\s+{\s+struct\s+{\s+short\s+__espins;\s+short\s+__elision;\s+}\s+d;\s+__pthread_slist_t\s+__list;\s+};\s+}\s+__data;\s+char\s+__size\[\d+\];\s+long\s+int\s+__align;\s+}\s+pthread_mutex_t;') fltrs.append(r'typedef\s+union\s+{\s+struct\s+__pthread_mutex_s\s+{\s+int\s+__lock;\s+unsigned\s+int\s+__count;\s+int\s+__owner;\s+unsigned\s+int\s+__nusers;\s+int\s+__kind;\s+short\s+__spins;\s+short\s+__elision;\s+__pthread_list_t\s+__list;\s+}\s+__data;\s+char\s+__size\[\d+\];\s+long\s+int\s+__align;\s+}\s+pthread_mutex_t;') + fltrs.append(r'typedef\s+union\s+{\s+struct\s+__pthread_mutex_s\s+__data;\s+char\s+__size\[\d+\];\s+long\s+int\s+__align;\s+}\s+pthread_mutex_t;') #pthread_cond_t fltrs.append(r'typedef\s+union\s+{\s+struct\s+{\s+int\s+__lock;\s+unsigned\s+int\s+__futex;\s+__extension__\s+unsigned\s+long\s+long\s+int\s+__total_seq;\s+__extension__\s+unsigned\s+long\s+long\s+int\s+__wakeup_seq;\s+__extension__\s+unsigned\s+long\s+long\s+int\s+__woken_seq;\s+void\s+\*__mutex;\s+unsigned\s+int\s+__nwaiters;\s+unsigned\s+int\s+__broadcast_seq;\s+}\s+__data;\s+char\s+__size\[\d+\];\s+__extension__\s+long\s+long\s+int\s+__align;\s+}\s+pthread_cond_t;') + fltrs.append(r'typedef\s+union\s+{\s+struct\s+__pthread_cond_s\s+__data;\s+char\s+__size\[\d+\];\s+__extension__\s+long\s+long\s+int\s+__align;\s+}\s+pthread_cond_t;') #pthread_condattr_t fltrs.append(r'typedef\s+union\s+{\s+char\s+__size\[\d+\];\s+int\s+__align;\s+}\s+pthread_condattr_t;') fltrs.append(r'typedef\s+union\s+{\s+char\s+__size\[\d+\];\s+long\s+int\s+__align;\s+}\s+pthread_condattr_t;') @@ -211,6 +213,6 @@ def scrub_pthreads(s): if __name__ == '__main__': - print "What?" - print svcomp_filter(sys.argv[1]) + print("What?") + print(svcomp_filter(sys.argv[1])) diff --git a/share/smack/svcomp/random_testing.py b/share/smack/svcomp/random_testing.py index 169547f7f..18430c7c1 100644 --- a/share/smack/svcomp/random_testing.py +++ b/share/smack/svcomp/random_testing.py @@ -12,7 +12,7 @@ def random_test(args, result): s = fi.read() l = s.split('\n') - if not (len(l) < 20 and len(filter(lambda x: re.search(r'=\s*__VERIFIER_nondet_uint', x), l)) == 1): + if not (len(l) < 20 and len([x for x in l if re.search(r'=\s*__VERIFIER_nondet_uint', x)]) == 1): return 'abort' UMAX_INT = 2**32 - 1 @@ -47,8 +47,8 @@ def compile_and_run(f, s, n, args): rc = proc.returncode if rc: - print 'Compiling error' - print err + print('Compiling error') + print(err) return 'unknown' else: cmd = [r'./' + tmp2] @@ -59,8 +59,8 @@ def compile_and_run(f, s, n, args): if re.search(r'Assertion.*failed', err): return 'false' else: - print 'Execution error' - print err + print('Execution error') + print(err) return 'unknown' else: return 'true' diff --git a/share/smack/svcomp/toSVCOMPformat.py b/share/smack/svcomp/toSVCOMPformat.py index db3166c11..d9fe441d2 100644 --- a/share/smack/svcomp/toSVCOMPformat.py +++ b/share/smack/svcomp/toSVCOMPformat.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python2 +#! /usr/bin/env python3 import xml.etree as etree import xml.etree.ElementTree as ET @@ -118,7 +118,7 @@ def buildEmptyXmlGraph(args, hasBug): programfile = os.path.abspath(args.orig_files[0]) addKey(graph, "programfile", programfile) with open(programfile, 'r') as pgf: - addKey(graph, "programhash", hashlib.sha256(pgf.read()).hexdigest()) + addKey(graph, "programhash", hashlib.sha256(pgf.read().encode('utf-8')).hexdigest()) addKey(graph, "architecture", re.search(r'-m(32|64)', args.clang_options).group(1) + 'bit') addKey(graph, "creationtime", datetime.datetime.now().replace(microsecond=0).isoformat()) @@ -189,7 +189,7 @@ def smackJsonToXmlGraph(strJsonOutput, args, hasBug, status): # Add function to call stack calledFunc = str(jsonTrace["description"][len("CALL "):]).strip() if calledFunc.startswith("devirtbounce"): - print "Warning: calling function pointer dispatch procedure at line {0}".format(jsonTrace["line"]) + print("Warning: calling function pointer dispatch procedure at line {0}".format(jsonTrace["line"])) continue if isSMACKInitFunc(calledFunc): continue @@ -197,15 +197,15 @@ def smackJsonToXmlGraph(strJsonOutput, args, hasBug, status): if "RETURN from" in desc: returnedFunc = str(desc[len("RETURN from "):]).strip() if returnedFunc.startswith("devirtbounce"): - print "Warning: returning from function pointer dispatch procedure at line {0}".format(jsonTrace["line"]) + print("Warning: returning from function pointer dispatch procedure at line {0}".format(jsonTrace["line"])) continue if isSMACKInitFunc(returnedFunc): continue if returnedFunc != callStack[-1][0]: raise RuntimeError('Procedure Call/Return dismatch at line {0}. Call stack head: {1}, returning from: {2}'.format(jsonTrace["line"], callStack[-1][0], returnedFunc)) callStack.pop() - print - print + print() + print() return prettify(tree.getroot()) @@ -214,7 +214,7 @@ def smackJsonToXmlGraph(strJsonOutput, args, hasBug, status): if len(sys.argv) > 1: jsonStr = open(sys.argv[1], "r").read() - print(smackJsonToXmlGraph(jsonStr)) + print((smackJsonToXmlGraph(jsonStr))) '''print(prettify(tree.getroot())) diff --git a/share/smack/svcomp/utils.py b/share/smack/svcomp/utils.py index 2c69c5180..370c5d764 100644 --- a/share/smack/svcomp/utils.py +++ b/share/smack/svcomp/utils.py @@ -6,9 +6,9 @@ from shutil import copyfile import smack.top import smack.frontend -import filters -from toSVCOMPformat import smackJsonToXmlGraph -from random_testing import random_test +from . import filters +from .toSVCOMPformat import smackJsonToXmlGraph +from .random_testing import random_test def svcomp_frontend(input_file, args): """Generate Boogie code from SVCOMP-style C-language source(s).""" @@ -254,7 +254,7 @@ def verify_bpl_svcomp(args): if args.pthread and "strcpy" in bpl: heurTrace += "We are not modeling strcpy - aborting\n" if not args.quiet: - print(heurTrace + "\n") + print((heurTrace + "\n")) sys.exit(smack.top.results(args)['unknown']) # Setting good loop unroll bound based on benchmark class @@ -356,7 +356,7 @@ def verify_bpl_svcomp(args): if not args.quiet: error = smack.top.error_trace(verifier_output, args) - print error + print(error) if args.memory_safety: heurTrace += (args.prop_to_check + "has errors\n") if args.prop_to_check == 'valid-free': @@ -392,16 +392,16 @@ def verify_bpl_svcomp(args): if execution_result != 'true': heurTrace += "Oops, execution result says {0}.\n".format(execution_result) if not args.quiet: - print(heurTrace + "\n") + print((heurTrace + "\n")) sys.exit(smack.top.results(args)['unknown']) random_test_result = random_test(args, result) if random_test_result == 'false' or random_test_result == 'unknown': heurTrace += "Oops, random testing says {0}.\n".format(random_test_result) if not args.quiet: - print(heurTrace + "\n") + print((heurTrace + "\n")) sys.exit(smack.top.results(args)['unknown']) if not args.quiet: - print(heurTrace + "\n") + print((heurTrace + "\n")) if args.memory_safety: heurTrace += (args.prop_to_check + "is verified\n") if args.prop_to_check == 'valid-deref': @@ -422,7 +422,7 @@ def verify_bpl_svcomp(args): heurTrace += "Insufficient unrolls to consider 'verified'. " heurTrace += "Reporting 'timeout'.\n" if not args.quiet: - print(heurTrace + "\n") + print((heurTrace + "\n")) sys.stdout.flush() if args.memory_safety: heurTrace += (args.prop_to_check + " times out\n") @@ -444,7 +444,7 @@ def verify_bpl_svcomp(args): else: #normal inlining heurTrace += "Normal inlining returned 'unknown'. See errors above.\n" if not args.quiet: - print(heurTrace + "\n") + print((heurTrace + "\n")) if args.memory_safety and result == 'verified': heurTrace += (args.prop_to_check + " is verified\n") if args.prop_to_check == 'valid-deref': @@ -479,7 +479,7 @@ def write_error_file(args, status, verifier_output): error = smack.top.error_trace(verifier_output, args) if error is not None: with open(args.error_file, 'w') as f: - f.write(error) + f.write(error.decode('utf-8')) def run_binary(args): #process the file to make it runnable @@ -510,8 +510,8 @@ def run_binary(args): rc = proc.returncode if rc: - print 'Compiling error' - print err + print('Compiling error') + print(err) return 'unknown' else: cmd = [r'./' + tmp2] @@ -522,8 +522,8 @@ def run_binary(args): if re.search(r'Assertion.*failed', err): return 'false' else: - print 'Execution error' - print err + print('Execution error') + print(err) return 'unknown' else: return 'true' diff --git a/share/smack/top.py b/share/smack/top.py index 97cb68db2..021437c0e 100755 --- a/share/smack/top.py +++ b/share/smack/top.py @@ -10,12 +10,12 @@ import shlex import subprocess import signal -from svcomp.utils import verify_bpl_svcomp -from utils import temporary_file, try_command, remove_temp_files -from replay import replay_error_trace -from frontend import link_bc_files, frontends, languages, extra_libs +from .svcomp.utils import verify_bpl_svcomp +from .utils import temporary_file, try_command, remove_temp_files +from .replay import replay_error_trace +from .frontend import link_bc_files, frontends, languages, extra_libs -VERSION = '2.4.0' +VERSION = '2.4.1' def results(args): """A dictionary of the result output messages.""" @@ -72,7 +72,7 @@ def validate_input_file(file): elif not file_extension in languages(): exit_with_error("Unexpected source file extension '%s'" % file_extension) - map(validate_input_file, files) + list(map(validate_input_file, files)) def validate_output_file(file): dir_name = os.path.dirname(os.path.abspath(file)) @@ -128,7 +128,7 @@ def arguments(): frontend_group = parser.add_argument_group('front-end options') frontend_group.add_argument('-x', '--language', metavar='LANG', - choices=frontends().keys(), default=None, + choices=list(frontends().keys()), default=None, help='Treat input files as having type LANG.') frontend_group.add_argument('-bc', '--bc-file', metavar='FILE', default=None, action=FileAction, @@ -197,6 +197,10 @@ def arguments(): translate_group.add_argument('--integer-overflow', action='store_true', default=False, help='enable integer overflow checks') + translate_group.add_argument('--llvm-assumes', choices=['none', 'use', 'check'], default='none', + help='optionally enable generation of Boogie assume statements from LLVM assume statements ' + + '(none=no generation [default], use=generate assume statements, check=check assume statements)') + translate_group.add_argument('--float', action="store_true", default=False, help='enable bit-precise floating-point functions') @@ -208,6 +212,10 @@ def arguments(): choices=['boogie', 'corral', 'symbooglix', 'svcomp'], default='corral', help='back-end verification engine') + verifier_group.add_argument('--solver', + choices=['z3', 'cvc4'], default='z3', + help='back-end SMT solver') + verifier_group.add_argument('--unroll', metavar='N', default='1', type = lambda x: int(x) if int(x) > 0 else parser.error('Unroll bound has to be positive.'), help='loop/recursion unroll bound [default: %(default)s]') @@ -331,6 +339,9 @@ def llvm_to_bpl(args): cmd = ['llvm2bpl', args.linked_bc_file, '-bpl', args.bpl_file] cmd += ['-warn-type', args.warn] + cmd += ['-sea-dsa=ci'] + # This flag can lead to unsoundness in Rust regressions. + #cmd += ['-sea-dsa-type-aware'] if sys.stdout.isatty(): cmd += ['-colored-warnings'] cmd += ['-source-loc-syms'] for ep in args.entry_points: @@ -347,6 +358,7 @@ def llvm_to_bpl(args): if args.no_memory_splitting: cmd += ['-no-memory-splitting'] if args.memory_safety: cmd += ['-memory-safety'] if args.integer_overflow: cmd += ['-integer-overflow'] + if args.llvm_assumes: cmd += ['-llvm-assumes=' + args.llvm_assumes] if args.float: cmd += ['-float'] if args.modular: cmd += ['-modular'] try_command(cmd, console=True) @@ -412,13 +424,16 @@ def transform_bpl(args): old = bpl.read() bpl.seek(0) bpl.truncate() - tx = subprocess.Popen(shlex.split(args.transform_bpl), stdin=subprocess.PIPE, stdout=bpl) + tx = subprocess.Popen(shlex.split(args.transform_bpl), + stdin=subprocess.PIPE, stdout=bpl, universal_newlines=True) tx.communicate(input = old) def transform_out(args, old): out = old if args.transform_out: - tx = subprocess.Popen(shlex.split(args.transform_out), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + tx = subprocess.Popen(shlex.split(args.transform_out), + stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, universal_newlines=True) out, err = tx.communicate(input = old) return out @@ -460,6 +475,8 @@ def verify_bpl(args): command += ["/errorLimit:%s" % args.max_violations] if not args.modular: command += ["/loopUnroll:%d" % args.unroll] + if args.solver == 'cvc4': + command += ["/proverOpt:SOLVER=cvc4"] elif args.verifier == 'corral': command = ["corral"] @@ -471,7 +488,9 @@ def verify_bpl(args): command += ["/cex:%s" % args.max_violations] command += ["/maxStaticLoopBound:%d" % args.loop_limit] command += ["/recursionBound:%d" % args.unroll] - + if args.solver == 'cvc4': + command += ["/bopt:proverOpt:SOLVER=cvc4"] + elif args.verifier == 'symbooglix': command = ['symbooglix'] command += [args.bpl_file] @@ -493,10 +512,10 @@ def verify_bpl(args): result = verification_result(verifier_output) if args.smackd: - print smackdOutput(verifier_output) + print(smackdOutput(verifier_output)) elif result == 'verified': - print results(args)[result] + print(results(args)[result]) else: if result == 'error' or result == 'invalid-deref' or result == 'invalid-free' or result == 'invalid-memtrack' or result == 'overflow': @@ -507,7 +526,7 @@ def verify_bpl(args): f.write(error) if not args.quiet: - print error + print(error) if args.replay: replay_error_trace(verifier_output, args) @@ -560,8 +579,7 @@ def repl(m): return re.sub('((\d+)bv\d+|(-?)0x([0-9a-fA-F]+\.[0-9a-fA-F]+)e(-?)(\d+)f(\d+)e(\d+))', repl, line.strip()) def transform(info): - return ','.join(map(reformat_assignment, filter( - lambda x: not re.search('((CALL|RETURN from)\s+(\$|__SMACK))|Done|ASSERTION', x), info.split(',')))) + return ','.join(map(reformat_assignment, [x for x in info.split(',') if not re.search('((CALL|RETURN from)\s+(\$|__SMACK))|Done|ASSERTION', x)])) def corral_error_step(step): m = re.match('([^\s]*)\s+Trace:\s+(Thread=\d+)\s+\((.*)[\)|;]', step) @@ -659,13 +677,13 @@ def main(): target_selection(args) if not args.quiet: - print "SMACK program verifier version %s" % VERSION + print("SMACK program verifier version %s" % VERSION) frontend(args) if args.no_verify: if not args.quiet: - print "SMACK generated %s" % args.bpl_file + print("SMACK generated %s" % args.bpl_file) else: verify_bpl(args) diff --git a/share/smack/utils.py b/share/smack/utils.py index d21c1f314..908b005ac 100644 --- a/share/smack/utils.py +++ b/share/smack/utils.py @@ -4,7 +4,7 @@ import subprocess import signal from threading import Timer -import top +from . import top temporary_files = [] @@ -34,10 +34,10 @@ def try_command(cmd, cwd=None, console=False, timeout=None): timer = None try: if args.debug: - print "Running %s" % " ".join(cmd) + print("Running %s" % " ".join(cmd)) proc = subprocess.Popen(cmd, cwd=cwd, preexec_fn=os.setsid, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) if timeout: timed_out = [False] @@ -49,7 +49,7 @@ def try_command(cmd, cwd=None, console=False, timeout=None): line = proc.stdout.readline() if line: output += line - print line, + print(line, end=' ') elif proc.poll() is not None: break proc.wait @@ -71,7 +71,7 @@ def try_command(cmd, cwd=None, console=False, timeout=None): return output except (RuntimeError, OSError) as err: - print >> sys.stderr, output + print(output, file=sys.stderr) sys.exit("Error invoking command:\n%s\n%s" % (" ".join(cmd), err)) finally: diff --git a/test/basic/select.c b/test/basic/select.c deleted file mode 100644 index 9a090a8a9..000000000 --- a/test/basic/select.c +++ /dev/null @@ -1,9 +0,0 @@ -#include "smack.h" - -// @expect verified -// @checkbpl grep -E ":= if.+then.+else.+" - -int main(void) { - int c = 2; - assert(c == 2 ? 1 : 0); -} diff --git a/test/basic/select_fail.c b/test/basic/select_fail.c deleted file mode 100644 index 5972bc283..000000000 --- a/test/basic/select_fail.c +++ /dev/null @@ -1,9 +0,0 @@ -#include "smack.h" - -// @expect error -// @checkbpl grep -E ":= if.+then.+else.+" - -int main(void) { - int c = 2; - assert(c != 2 ? 1 : 0); -} diff --git a/test/basic/ase_example.c b/test/c/basic/ase_example.c similarity index 100% rename from test/basic/ase_example.c rename to test/c/basic/ase_example.c diff --git a/test/basic/ase_example_fail.c b/test/c/basic/ase_example_fail.c similarity index 100% rename from test/basic/ase_example_fail.c rename to test/c/basic/ase_example_fail.c diff --git a/test/basic/big_numbers.c b/test/c/basic/big_numbers.c similarity index 100% rename from test/basic/big_numbers.c rename to test/c/basic/big_numbers.c diff --git a/test/basic/big_numbers_fail.c b/test/c/basic/big_numbers_fail.c similarity index 100% rename from test/basic/big_numbers_fail.c rename to test/c/basic/big_numbers_fail.c diff --git a/test/basic/big_types.c b/test/c/basic/big_types.c similarity index 100% rename from test/basic/big_types.c rename to test/c/basic/big_types.c diff --git a/test/basic/checking.c b/test/c/basic/checking.c similarity index 100% rename from test/basic/checking.c rename to test/c/basic/checking.c diff --git a/test/basic/checking_invert_bpl.c b/test/c/basic/checking_invert_bpl.c similarity index 100% rename from test/basic/checking_invert_bpl.c rename to test/c/basic/checking_invert_bpl.c diff --git a/test/basic/checking_invert_out.c b/test/c/basic/checking_invert_out.c similarity index 100% rename from test/basic/checking_invert_out.c rename to test/c/basic/checking_invert_out.c diff --git a/test/basic/config.yml b/test/c/basic/config.yml similarity index 100% rename from test/basic/config.yml rename to test/c/basic/config.yml diff --git a/test/basic/extern_func.c b/test/c/basic/extern_func.c similarity index 100% rename from test/basic/extern_func.c rename to test/c/basic/extern_func.c diff --git a/test/basic/extern_mem.c b/test/c/basic/extern_mem.c similarity index 100% rename from test/basic/extern_mem.c rename to test/c/basic/extern_mem.c diff --git a/test/basic/gcd.c b/test/c/basic/gcd.c similarity index 100% rename from test/basic/gcd.c rename to test/c/basic/gcd.c diff --git a/test/basic/gcd_1_true.c b/test/c/basic/gcd_1_true.c similarity index 100% rename from test/basic/gcd_1_true.c rename to test/c/basic/gcd_1_true.c diff --git a/test/basic/globals.c b/test/c/basic/globals.c similarity index 100% rename from test/basic/globals.c rename to test/c/basic/globals.c diff --git a/test/basic/globals_fail.c b/test/c/basic/globals_fail.c similarity index 100% rename from test/basic/globals_fail.c rename to test/c/basic/globals_fail.c diff --git a/test/basic/init_funcs_example.c b/test/c/basic/init_funcs_example.c similarity index 100% rename from test/basic/init_funcs_example.c rename to test/c/basic/init_funcs_example.c diff --git a/test/basic/init_funcs_example_fail.c b/test/c/basic/init_funcs_example_fail.c similarity index 100% rename from test/basic/init_funcs_example_fail.c rename to test/c/basic/init_funcs_example_fail.c diff --git a/test/basic/init_funcs_global.c b/test/c/basic/init_funcs_global.c similarity index 100% rename from test/basic/init_funcs_global.c rename to test/c/basic/init_funcs_global.c diff --git a/test/basic/init_funcs_global_fail.c b/test/c/basic/init_funcs_global_fail.c similarity index 100% rename from test/basic/init_funcs_global_fail.c rename to test/c/basic/init_funcs_global_fail.c diff --git a/test/basic/jain_1_true.c b/test/c/basic/jain_1_true.c similarity index 100% rename from test/basic/jain_1_true.c rename to test/c/basic/jain_1_true.c diff --git a/test/basic/jain_2_true.c b/test/c/basic/jain_2_true.c similarity index 100% rename from test/basic/jain_2_true.c rename to test/c/basic/jain_2_true.c diff --git a/test/basic/jain_4_true.c b/test/c/basic/jain_4_true.c similarity index 100% rename from test/basic/jain_4_true.c rename to test/c/basic/jain_4_true.c diff --git a/test/basic/jain_5_true.c b/test/c/basic/jain_5_true.c similarity index 100% rename from test/basic/jain_5_true.c rename to test/c/basic/jain_5_true.c diff --git a/test/basic/limits.c b/test/c/basic/limits.c similarity index 100% rename from test/basic/limits.c rename to test/c/basic/limits.c diff --git a/test/basic/limits_fail.c b/test/c/basic/limits_fail.c similarity index 100% rename from test/basic/limits_fail.c rename to test/c/basic/limits_fail.c diff --git a/test/basic/list.c b/test/c/basic/list.c similarity index 100% rename from test/basic/list.c rename to test/c/basic/list.c diff --git a/test/basic/list_fail.c b/test/c/basic/list_fail.c similarity index 100% rename from test/basic/list_fail.c rename to test/c/basic/list_fail.c diff --git a/test/basic/lock.c b/test/c/basic/lock.c similarity index 100% rename from test/basic/lock.c rename to test/c/basic/lock.c diff --git a/test/basic/lock_fail.c b/test/c/basic/lock_fail.c similarity index 100% rename from test/basic/lock_fail.c rename to test/c/basic/lock_fail.c diff --git a/test/basic/loop.c b/test/c/basic/loop.c similarity index 100% rename from test/basic/loop.c rename to test/c/basic/loop.c diff --git a/test/basic/loop1.c b/test/c/basic/loop1.c similarity index 100% rename from test/basic/loop1.c rename to test/c/basic/loop1.c diff --git a/test/basic/loop1_fail.c b/test/c/basic/loop1_fail.c similarity index 100% rename from test/basic/loop1_fail.c rename to test/c/basic/loop1_fail.c diff --git a/test/basic/loop_fail.c b/test/c/basic/loop_fail.c similarity index 100% rename from test/basic/loop_fail.c rename to test/c/basic/loop_fail.c diff --git a/test/c/basic/malloc_collapsing.c b/test/c/basic/malloc_collapsing.c new file mode 100644 index 000000000..be5ac8729 --- /dev/null +++ b/test/c/basic/malloc_collapsing.c @@ -0,0 +1,14 @@ +#include "smack.h" +#include + +// @expect verified +// @checkbpl grep "var \$M.0: \[ref\] i32;" + +int main(void) { + int *p = (int *)malloc(sizeof(int)); + *p = 2; + p = (int *)realloc(p, sizeof(int)); + *p = 1; + assert(*p != 2); + return *p; +} diff --git a/test/basic/negative_numbers.c b/test/c/basic/negative_numbers.c similarity index 100% rename from test/basic/negative_numbers.c rename to test/c/basic/negative_numbers.c diff --git a/test/basic/negative_numbers_fail.c b/test/c/basic/negative_numbers_fail.c similarity index 100% rename from test/basic/negative_numbers_fail.c rename to test/c/basic/negative_numbers_fail.c diff --git a/test/basic/nondet.c b/test/c/basic/nondet.c similarity index 100% rename from test/basic/nondet.c rename to test/c/basic/nondet.c diff --git a/test/basic/printfs.c b/test/c/basic/printfs.c similarity index 100% rename from test/basic/printfs.c rename to test/c/basic/printfs.c diff --git a/test/basic/return_label.c b/test/c/basic/return_label.c similarity index 100% rename from test/basic/return_label.c rename to test/c/basic/return_label.c diff --git a/test/c/basic/select.c b/test/c/basic/select.c new file mode 100644 index 000000000..439fe3b9f --- /dev/null +++ b/test/c/basic/select.c @@ -0,0 +1,12 @@ +#include "smack.h" + +// @expect verified +// @checkbpl grep -E ":= \(if.+then.+else.+\)" + +void foo(int x) { assert(x); } + +int main(void) { + int c = __VERIFIER_nondet_int(); + assume(c == 2); + foo(c == 2 ? 1 : 0); +} diff --git a/test/c/basic/select_fail.c b/test/c/basic/select_fail.c new file mode 100644 index 000000000..61453a8fe --- /dev/null +++ b/test/c/basic/select_fail.c @@ -0,0 +1,12 @@ +#include "smack.h" + +// @expect error +// @checkbpl grep -E ":= \(if.+then.+else.+\)" + +void foo(int x) { assert(x); } + +int main(void) { + int c = __VERIFIER_nondet_int(); + assume(c == 2); + foo(c != 2 ? 1 : 0); +} diff --git a/test/c/basic/sign_cast.c b/test/c/basic/sign_cast.c new file mode 100644 index 000000000..b4bcc168e --- /dev/null +++ b/test/c/basic/sign_cast.c @@ -0,0 +1,13 @@ +#include "smack.h" +#include + +// @expect verified + +int main(void) { + unsigned x = __VERIFIER_nondet_unsigned(); + + // -n's two's complement is UNINT_MAX - (n - 1) + assume(x < UINT_MAX - 1); + assert(x < ((unsigned)-2)); + return 0; +} diff --git a/test/c/basic/sign_cast_fail.c b/test/c/basic/sign_cast_fail.c new file mode 100644 index 000000000..bce1a8c40 --- /dev/null +++ b/test/c/basic/sign_cast_fail.c @@ -0,0 +1,13 @@ +#include "smack.h" +#include + +// @expect error + +int main(void) { + unsigned x = __VERIFIER_nondet_unsigned(); + + // -n's two's complement is UNINT_MAX - (n - 1) + assume(x < UINT_MAX - 1); + assert(x < ((unsigned)-3)); + return 0; +} diff --git a/test/basic/simple.c b/test/c/basic/simple.c similarity index 100% rename from test/basic/simple.c rename to test/c/basic/simple.c diff --git a/test/basic/simple_fail.c b/test/c/basic/simple_fail.c similarity index 100% rename from test/basic/simple_fail.c rename to test/c/basic/simple_fail.c diff --git a/test/basic/simple_pre.c b/test/c/basic/simple_pre.c similarity index 100% rename from test/basic/simple_pre.c rename to test/c/basic/simple_pre.c diff --git a/test/basic/simple_pre1.c b/test/c/basic/simple_pre1.c similarity index 100% rename from test/basic/simple_pre1.c rename to test/c/basic/simple_pre1.c diff --git a/test/basic/simple_pre1_fail.c b/test/c/basic/simple_pre1_fail.c similarity index 100% rename from test/basic/simple_pre1_fail.c rename to test/c/basic/simple_pre1_fail.c diff --git a/test/basic/simple_pre2.c b/test/c/basic/simple_pre2.c similarity index 100% rename from test/basic/simple_pre2.c rename to test/c/basic/simple_pre2.c diff --git a/test/basic/simple_pre2_fail.c b/test/c/basic/simple_pre2_fail.c similarity index 100% rename from test/basic/simple_pre2_fail.c rename to test/c/basic/simple_pre2_fail.c diff --git a/test/basic/simple_pre3.c b/test/c/basic/simple_pre3.c similarity index 100% rename from test/basic/simple_pre3.c rename to test/c/basic/simple_pre3.c diff --git a/test/basic/simple_pre3_fail.c b/test/c/basic/simple_pre3_fail.c similarity index 100% rename from test/basic/simple_pre3_fail.c rename to test/c/basic/simple_pre3_fail.c diff --git a/test/basic/simple_pre4.c b/test/c/basic/simple_pre4.c similarity index 100% rename from test/basic/simple_pre4.c rename to test/c/basic/simple_pre4.c diff --git a/test/basic/simple_pre4_fail.c b/test/c/basic/simple_pre4_fail.c similarity index 100% rename from test/basic/simple_pre4_fail.c rename to test/c/basic/simple_pre4_fail.c diff --git a/test/basic/simple_pre_fail.c b/test/c/basic/simple_pre_fail.c similarity index 100% rename from test/basic/simple_pre_fail.c rename to test/c/basic/simple_pre_fail.c diff --git a/test/basic/smack_code_call.c b/test/c/basic/smack_code_call.c similarity index 100% rename from test/basic/smack_code_call.c rename to test/c/basic/smack_code_call.c diff --git a/test/basic/smack_code_call_fail.c b/test/c/basic/smack_code_call_fail.c similarity index 100% rename from test/basic/smack_code_call_fail.c rename to test/c/basic/smack_code_call_fail.c diff --git a/test/basic/split_aggregate_values.c b/test/c/basic/split_aggregate_values.c similarity index 100% rename from test/basic/split_aggregate_values.c rename to test/c/basic/split_aggregate_values.c diff --git a/test/basic/split_aggregate_values_fail.c b/test/c/basic/split_aggregate_values_fail.c similarity index 100% rename from test/basic/split_aggregate_values_fail.c rename to test/c/basic/split_aggregate_values_fail.c diff --git a/test/c/basic/srem.c b/test/c/basic/srem.c new file mode 100644 index 000000000..355c9baa3 --- /dev/null +++ b/test/c/basic/srem.c @@ -0,0 +1,47 @@ +#include "smack.h" + +// @expect verified +// @checkbpl grep ':= \$srem' + +int main(void) { + int x; + x = 62951; + assert(x % (-37) == 14); + x = 81165; + assert(x % 34 == 7); + x = -74602; + assert(x % 47 == (-13)); + x = 82682; + assert(x % (-28) == 26); + x = 26476; + assert(x % (-19) == 9); + x = -80492; + assert(x % 5 == (-2)); + x = -44299; + assert(x % (-31) == 0); + x = 9380; + assert(x % 50 == 30); + x = 91508; + assert(x % 50 == 8); + x = 92985; + assert(x % 47 == 19); + x = -68477; + assert(x % 23 == (-6)); + x = 94126; + assert(x % 49 == 46); + x = 91440; + assert(x % (-39) == 24); + x = -2922; + assert(x % 30 == (-12)); + x = 60062; + assert(x % (-21) == 2); + x = -71622; + assert(x % (-50) == (-22)); + x = -15645; + assert(x % (-39) == (-6)); + x = 83154; + assert(x % 14 == 8); + x = 58447; + assert(x % 38 == 3); + return 0; +} diff --git a/test/c/basic/srem_fail.c b/test/c/basic/srem_fail.c new file mode 100644 index 000000000..a6b160f97 --- /dev/null +++ b/test/c/basic/srem_fail.c @@ -0,0 +1,47 @@ +#include "smack.h" + +// @expect error +// @checkbpl grep ':= \$srem' + +int main(void) { + int x; + x = 62951; + assert(x % (-37) == 14); + x = 81165; + assert(x % 34 == 7); + x = -74602; + assert(x % 47 == (-13)); + x = 82682; + assert(x % (-28) == 26); + x = 26476; + assert(x % (-19) == 9); + x = -80492; + assert(x % 5 == (-2)); + x = -44299; + assert(x % (-31) == 0); + x = 9380; + assert(x % 50 == 30); + x = 91508; + assert(x % 50 == 8); + x = 92985; + assert(x % 47 == 19); + x = -68477; + assert(x % 23 == (-6)); + x = 94126; + assert(x % 49 == 46); + x = 91440; + assert(x % (-39) == 24); + x = -2922; + assert(x % 30 != (-12)); + x = 60062; + assert(x % (-21) == 2); + x = -71622; + assert(x % (-50) == (-22)); + x = -15645; + assert(x % (-39) == (-6)); + x = 83154; + assert(x % 14 == 8); + x = 58447; + assert(x % 38 == 3); + return 0; +} diff --git a/test/basic/strings.c b/test/c/basic/strings.c similarity index 100% rename from test/basic/strings.c rename to test/c/basic/strings.c diff --git a/test/basic/strings1.c b/test/c/basic/strings1.c similarity index 100% rename from test/basic/strings1.c rename to test/c/basic/strings1.c diff --git a/test/basic/strings1_fail.c b/test/c/basic/strings1_fail.c similarity index 100% rename from test/basic/strings1_fail.c rename to test/c/basic/strings1_fail.c diff --git a/test/basic/strings_fail.c b/test/c/basic/strings_fail.c similarity index 100% rename from test/basic/strings_fail.c rename to test/c/basic/strings_fail.c diff --git a/test/basic/test_memcpy.c b/test/c/basic/test_memcpy.c similarity index 100% rename from test/basic/test_memcpy.c rename to test/c/basic/test_memcpy.c diff --git a/test/basic/test_memcpy_fail.c b/test/c/basic/test_memcpy_fail.c similarity index 100% rename from test/basic/test_memcpy_fail.c rename to test/c/basic/test_memcpy_fail.c diff --git a/test/basic/timing-annotations.c b/test/c/basic/timing-annotations.c similarity index 100% rename from test/basic/timing-annotations.c rename to test/c/basic/timing-annotations.c diff --git a/test/basic/transform-bpl.c b/test/c/basic/transform-bpl.c similarity index 100% rename from test/basic/transform-bpl.c rename to test/c/basic/transform-bpl.c diff --git a/test/basic/transform-out.c b/test/c/basic/transform-out.c similarity index 100% rename from test/basic/transform-out.c rename to test/c/basic/transform-out.c diff --git a/test/basic/uninterpreted.c b/test/c/basic/uninterpreted.c similarity index 100% rename from test/basic/uninterpreted.c rename to test/c/basic/uninterpreted.c diff --git a/test/basic/uninterpreted_fail.c b/test/c/basic/uninterpreted_fail.c similarity index 100% rename from test/basic/uninterpreted_fail.c rename to test/c/basic/uninterpreted_fail.c diff --git a/test/c/basic/urem.c b/test/c/basic/urem.c new file mode 100644 index 000000000..74e6256ed --- /dev/null +++ b/test/c/basic/urem.c @@ -0,0 +1,49 @@ +#include "smack.h" + +// @expect verified +// @checkbpl grep ':= \$urem' + +int main(void) { + unsigned x; + x = 162951; + assert(x % 7 == 5); + x = 181165; + assert(x % 42 == 19); + x = 25398; + assert(x % 49 == 16); + x = 182682; + assert(x % 12 == 6); + x = 126476; + assert(x % 16 == 12); + x = 19508; + assert(x % 28 == 20); + x = 55701; + assert(x % 10 == 1); + x = 109380; + assert(x % 50 == 30); + x = 191508; + assert(x % 50 == 8); + x = 192985; + assert(x % 49 == 23); + x = 31523; + assert(x % 37 == 36); + x = 194126; + assert(x % 50 == 26); + x = 191440; + assert(x % 6 == 4); + x = 97078; + assert(x % 40 == 38); + x = 160062; + assert(x % 15 == 12); + x = 28378; + assert(x % 1 == 0); + x = 84355; + assert(x % 6 == 1); + x = 183154; + assert(x % 32 == 18); + x = 158447; + assert(x % 44 == 3); + x = 191905; + assert(x % 26 == 25); + return 0; +} diff --git a/test/c/basic/urem_fail.c b/test/c/basic/urem_fail.c new file mode 100644 index 000000000..dd802a908 --- /dev/null +++ b/test/c/basic/urem_fail.c @@ -0,0 +1,49 @@ +#include "smack.h" + +// @expect error +// @checkbpl grep ':= \$urem' + +int main(void) { + unsigned x; + x = 162951; + assert(x % 7 == 5); + x = 181165; + assert(x % 42 == 19); + x = 25398; + assert(x % 49 == 16); + x = 182682; + assert(x % 12 == 6); + x = 126476; + assert(x % 16 == 12); + x = 19508; + assert(x % 28 == 20); + x = 55701; + assert(x % 10 == 1); + x = 109380; + assert(x % 50 == 30); + x = 191508; + assert(x % 50 == 8); + x = 192985; + assert(x % 49 == 23); + x = 31523; + assert(x % 37 != 36); + x = 194126; + assert(x % 50 == 26); + x = 191440; + assert(x % 6 == 4); + x = 97078; + assert(x % 40 == 38); + x = 160062; + assert(x % 15 == 12); + x = 28378; + assert(x % 1 == 0); + x = 84355; + assert(x % 6 == 1); + x = 183154; + assert(x % 32 == 18); + x = 158447; + assert(x % 44 == 3); + x = 191905; + assert(x % 26 == 25); + return 0; +} diff --git a/test/basic/vararg.c b/test/c/basic/vararg.c similarity index 100% rename from test/basic/vararg.c rename to test/c/basic/vararg.c diff --git a/test/basic/vararg_fail.c b/test/c/basic/vararg_fail.c similarity index 100% rename from test/basic/vararg_fail.c rename to test/c/basic/vararg_fail.c diff --git a/test/bits/absolute.c b/test/c/bits/absolute.c similarity index 100% rename from test/bits/absolute.c rename to test/c/bits/absolute.c diff --git a/test/bits/absolute_fail.c b/test/c/bits/absolute_fail.c similarity index 100% rename from test/bits/absolute_fail.c rename to test/c/bits/absolute_fail.c diff --git a/test/bits/bit_field.c b/test/c/bits/bit_field.c similarity index 100% rename from test/bits/bit_field.c rename to test/c/bits/bit_field.c diff --git a/test/bits/bit_field_fail.c b/test/c/bits/bit_field_fail.c similarity index 100% rename from test/bits/bit_field_fail.c rename to test/c/bits/bit_field_fail.c diff --git a/test/bits/bit_fields.c b/test/c/bits/bit_fields.c similarity index 100% rename from test/bits/bit_fields.c rename to test/c/bits/bit_fields.c diff --git a/test/bits/bit_fields_fail.c b/test/c/bits/bit_fields_fail.c similarity index 100% rename from test/bits/bit_fields_fail.c rename to test/c/bits/bit_fields_fail.c diff --git a/test/bits/bitreverse.c b/test/c/bits/bitreverse.c similarity index 100% rename from test/bits/bitreverse.c rename to test/c/bits/bitreverse.c diff --git a/test/bits/bitreverse_fail.c b/test/c/bits/bitreverse_fail.c similarity index 100% rename from test/bits/bitreverse_fail.c rename to test/c/bits/bitreverse_fail.c diff --git a/test/bits/byte_swap.c b/test/c/bits/byte_swap.c similarity index 100% rename from test/bits/byte_swap.c rename to test/c/bits/byte_swap.c diff --git a/test/bits/byte_swap_fail.c b/test/c/bits/byte_swap_fail.c similarity index 100% rename from test/bits/byte_swap_fail.c rename to test/c/bits/byte_swap_fail.c diff --git a/test/bits/config.yml b/test/c/bits/config.yml similarity index 100% rename from test/bits/config.yml rename to test/c/bits/config.yml diff --git a/test/bits/countlz.c b/test/c/bits/countlz.c similarity index 100% rename from test/bits/countlz.c rename to test/c/bits/countlz.c diff --git a/test/bits/countlz_fail.c b/test/c/bits/countlz_fail.c similarity index 100% rename from test/bits/countlz_fail.c rename to test/c/bits/countlz_fail.c diff --git a/test/bits/countlz_fail2.c b/test/c/bits/countlz_fail2.c similarity index 100% rename from test/bits/countlz_fail2.c rename to test/c/bits/countlz_fail2.c diff --git a/test/bits/countlz_zero_fail.c b/test/c/bits/countlz_zero_fail.c similarity index 100% rename from test/bits/countlz_zero_fail.c rename to test/c/bits/countlz_zero_fail.c diff --git a/test/bits/countpop32.c b/test/c/bits/countpop32.c similarity index 100% rename from test/bits/countpop32.c rename to test/c/bits/countpop32.c diff --git a/test/bits/countpop32_fail.c b/test/c/bits/countpop32_fail.c similarity index 100% rename from test/bits/countpop32_fail.c rename to test/c/bits/countpop32_fail.c diff --git a/test/bits/counttz.c b/test/c/bits/counttz.c similarity index 100% rename from test/bits/counttz.c rename to test/c/bits/counttz.c diff --git a/test/bits/counttz_fail.c b/test/c/bits/counttz_fail.c similarity index 100% rename from test/bits/counttz_fail.c rename to test/c/bits/counttz_fail.c diff --git a/test/bits/counttz_fail2.c b/test/c/bits/counttz_fail2.c similarity index 100% rename from test/bits/counttz_fail2.c rename to test/c/bits/counttz_fail2.c diff --git a/test/bits/interleave_bits_fail.c b/test/c/bits/interleave_bits_fail.c similarity index 100% rename from test/bits/interleave_bits_fail.c rename to test/c/bits/interleave_bits_fail.c diff --git a/test/bits/interleave_bits_true.c b/test/c/bits/interleave_bits_true.c similarity index 100% rename from test/bits/interleave_bits_true.c rename to test/c/bits/interleave_bits_true.c diff --git a/test/bits/left_shift_negative_fail.c b/test/c/bits/left_shift_negative_fail.c similarity index 100% rename from test/bits/left_shift_negative_fail.c rename to test/c/bits/left_shift_negative_fail.c diff --git a/test/bits/left_shift_overflow.c b/test/c/bits/left_shift_overflow.c similarity index 100% rename from test/bits/left_shift_overflow.c rename to test/c/bits/left_shift_overflow.c diff --git a/test/bits/left_shift_overflow_fail.c b/test/c/bits/left_shift_overflow_fail.c similarity index 100% rename from test/bits/left_shift_overflow_fail.c rename to test/c/bits/left_shift_overflow_fail.c diff --git a/test/bits/left_shift_unsigned.c b/test/c/bits/left_shift_unsigned.c similarity index 100% rename from test/bits/left_shift_unsigned.c rename to test/c/bits/left_shift_unsigned.c diff --git a/test/bits/left_shift_unsigned_fail.c b/test/c/bits/left_shift_unsigned_fail.c similarity index 100% rename from test/bits/left_shift_unsigned_fail.c rename to test/c/bits/left_shift_unsigned_fail.c diff --git a/test/bits/malloc_non_alias.c b/test/c/bits/malloc_non_alias.c similarity index 100% rename from test/bits/malloc_non_alias.c rename to test/c/bits/malloc_non_alias.c diff --git a/test/bits/mm.c b/test/c/bits/mm.c similarity index 100% rename from test/bits/mm.c rename to test/c/bits/mm.c diff --git a/test/bits/mm_fail.c b/test/c/bits/mm_fail.c similarity index 100% rename from test/bits/mm_fail.c rename to test/c/bits/mm_fail.c diff --git a/test/bits/num_conversion_1_fail.c b/test/c/bits/num_conversion_1_fail.c similarity index 100% rename from test/bits/num_conversion_1_fail.c rename to test/c/bits/num_conversion_1_fail.c diff --git a/test/bits/num_conversion_1_true.c b/test/c/bits/num_conversion_1_true.c similarity index 100% rename from test/bits/num_conversion_1_true.c rename to test/c/bits/num_conversion_1_true.c diff --git a/test/bits/num_conversion_2_fail.c b/test/c/bits/num_conversion_2_fail.c similarity index 100% rename from test/bits/num_conversion_2_fail.c rename to test/c/bits/num_conversion_2_fail.c diff --git a/test/bits/num_conversion_2_true.c b/test/c/bits/num_conversion_2_true.c similarity index 100% rename from test/bits/num_conversion_2_true.c rename to test/c/bits/num_conversion_2_true.c diff --git a/test/c/bits/pack_struct.c b/test/c/bits/pack_struct.c new file mode 100644 index 000000000..5905eb805 --- /dev/null +++ b/test/c/bits/pack_struct.c @@ -0,0 +1,24 @@ +#include "smack.h" +#include + +// @expect verified + +struct S { + int x; + int y; +}; + +// Clang packs each argument as a 64-bit integer, +// which introduces false alarms without +// the `--bit-precise` flag +bool eq(struct S p1, struct S p2) { return p1.y == p2.y; } + +int main(void) { + struct S p; + struct S q; + + p = q; + assert(eq(p, q)); + + return 0; +} diff --git a/test/c/bits/pack_struct_fail.c b/test/c/bits/pack_struct_fail.c new file mode 100644 index 000000000..0be2ac0aa --- /dev/null +++ b/test/c/bits/pack_struct_fail.c @@ -0,0 +1,24 @@ +#include "smack.h" +#include + +// @expect error + +struct S { + int x; + int y; +}; + +// Clang packs each argument as a 64-bit integer, +// which introduces false alarms without +// the `--bit-precise` flag +bool eq(struct S p1, struct S p2) { return p1.y == p2.y; } + +int main(void) { + struct S p; + struct S q; + + p = q; + assert(!eq(p, q)); + + return 0; +} diff --git a/test/bits/pointers4.c b/test/c/bits/pointers4.c similarity index 100% rename from test/bits/pointers4.c rename to test/c/bits/pointers4.c diff --git a/test/bits/pointers4_fail.c b/test/c/bits/pointers4_fail.c similarity index 100% rename from test/bits/pointers4_fail.c rename to test/c/bits/pointers4_fail.c diff --git a/test/bits/pointers6.c b/test/c/bits/pointers6.c similarity index 100% rename from test/bits/pointers6.c rename to test/c/bits/pointers6.c diff --git a/test/bits/pointers7.c b/test/c/bits/pointers7.c similarity index 100% rename from test/bits/pointers7.c rename to test/c/bits/pointers7.c diff --git a/test/bits/pointers7_fail.c b/test/c/bits/pointers7_fail.c similarity index 100% rename from test/bits/pointers7_fail.c rename to test/c/bits/pointers7_fail.c diff --git a/test/bits/smack_code_annot.c b/test/c/bits/smack_code_annot.c similarity index 100% rename from test/bits/smack_code_annot.c rename to test/c/bits/smack_code_annot.c diff --git a/test/bits/smack_code_annot_fail.c b/test/c/bits/smack_code_annot_fail.c similarity index 100% rename from test/bits/smack_code_annot_fail.c rename to test/c/bits/smack_code_annot_fail.c diff --git a/test/bits/sync-fetch.c b/test/c/bits/sync-fetch.c similarity index 100% rename from test/bits/sync-fetch.c rename to test/c/bits/sync-fetch.c diff --git a/test/bits/sync-fetch_fail.c b/test/c/bits/sync-fetch_fail.c similarity index 100% rename from test/bits/sync-fetch_fail.c rename to test/c/bits/sync-fetch_fail.c diff --git a/test/bits/unaligned_struct.c b/test/c/bits/unaligned_struct.c similarity index 100% rename from test/bits/unaligned_struct.c rename to test/c/bits/unaligned_struct.c diff --git a/test/bits/unaligned_struct_fail.c b/test/c/bits/unaligned_struct_fail.c similarity index 100% rename from test/bits/unaligned_struct_fail.c rename to test/c/bits/unaligned_struct_fail.c diff --git a/test/contracts/.gitignore b/test/c/contracts/.gitignore similarity index 100% rename from test/contracts/.gitignore rename to test/c/contracts/.gitignore diff --git a/test/contracts/and.c b/test/c/contracts/and.c similarity index 100% rename from test/contracts/and.c rename to test/c/contracts/and.c diff --git a/test/contracts/and_fail.c b/test/c/contracts/and_fail.c similarity index 100% rename from test/contracts/and_fail.c rename to test/c/contracts/and_fail.c diff --git a/test/contracts/array.c b/test/c/contracts/array.c similarity index 100% rename from test/contracts/array.c rename to test/c/contracts/array.c diff --git a/test/contracts/array_fail.c b/test/c/contracts/array_fail.c similarity index 100% rename from test/contracts/array_fail.c rename to test/c/contracts/array_fail.c diff --git a/test/contracts/config.yml b/test/c/contracts/config.yml similarity index 100% rename from test/contracts/config.yml rename to test/c/contracts/config.yml diff --git a/test/contracts/failing/array_forall.c b/test/c/contracts/failing/array_forall.c similarity index 100% rename from test/contracts/failing/array_forall.c rename to test/c/contracts/failing/array_forall.c diff --git a/test/contracts/failing/array_forall_fail.c b/test/c/contracts/failing/array_forall_fail.c similarity index 100% rename from test/contracts/failing/array_forall_fail.c rename to test/c/contracts/failing/array_forall_fail.c diff --git a/test/contracts/failing/forall.c b/test/c/contracts/failing/forall.c similarity index 100% rename from test/contracts/failing/forall.c rename to test/c/contracts/failing/forall.c diff --git a/test/contracts/failing/forall_fail.c b/test/c/contracts/failing/forall_fail.c similarity index 100% rename from test/contracts/failing/forall_fail.c rename to test/c/contracts/failing/forall_fail.c diff --git a/test/contracts/failing/old.c b/test/c/contracts/failing/old.c similarity index 100% rename from test/contracts/failing/old.c rename to test/c/contracts/failing/old.c diff --git a/test/contracts/failing/old_fail.c b/test/c/contracts/failing/old_fail.c similarity index 100% rename from test/contracts/failing/old_fail.c rename to test/c/contracts/failing/old_fail.c diff --git a/test/contracts/failing/result.c b/test/c/contracts/failing/result.c similarity index 100% rename from test/contracts/failing/result.c rename to test/c/contracts/failing/result.c diff --git a/test/contracts/failing/result_fail.c b/test/c/contracts/failing/result_fail.c similarity index 100% rename from test/contracts/failing/result_fail.c rename to test/c/contracts/failing/result_fail.c diff --git a/test/contracts/invariant.c b/test/c/contracts/invariant.c similarity index 100% rename from test/contracts/invariant.c rename to test/c/contracts/invariant.c diff --git a/test/contracts/invariant_fail.c b/test/c/contracts/invariant_fail.c similarity index 100% rename from test/contracts/invariant_fail.c rename to test/c/contracts/invariant_fail.c diff --git a/test/contracts/requires_const.c b/test/c/contracts/requires_const.c similarity index 100% rename from test/contracts/requires_const.c rename to test/c/contracts/requires_const.c diff --git a/test/contracts/simple.c b/test/c/contracts/simple.c similarity index 100% rename from test/contracts/simple.c rename to test/c/contracts/simple.c diff --git a/test/contracts/simple_fail.c b/test/c/contracts/simple_fail.c similarity index 100% rename from test/contracts/simple_fail.c rename to test/c/contracts/simple_fail.c diff --git a/test/data/array.c b/test/c/data/array.c similarity index 100% rename from test/data/array.c rename to test/c/data/array.c diff --git a/test/data/array1.c b/test/c/data/array1.c similarity index 100% rename from test/data/array1.c rename to test/c/data/array1.c diff --git a/test/data/array1_fail.c b/test/c/data/array1_fail.c similarity index 100% rename from test/data/array1_fail.c rename to test/c/data/array1_fail.c diff --git a/test/data/array2.c b/test/c/data/array2.c similarity index 100% rename from test/data/array2.c rename to test/c/data/array2.c diff --git a/test/data/array2_fail.c b/test/c/data/array2_fail.c similarity index 100% rename from test/data/array2_fail.c rename to test/c/data/array2_fail.c diff --git a/test/data/array3.c b/test/c/data/array3.c similarity index 100% rename from test/data/array3.c rename to test/c/data/array3.c diff --git a/test/data/array3_fail.c b/test/c/data/array3_fail.c similarity index 100% rename from test/data/array3_fail.c rename to test/c/data/array3_fail.c diff --git a/test/data/array4.c b/test/c/data/array4.c similarity index 100% rename from test/data/array4.c rename to test/c/data/array4.c diff --git a/test/data/array4_fail.c b/test/c/data/array4_fail.c similarity index 100% rename from test/data/array4_fail.c rename to test/c/data/array4_fail.c diff --git a/test/data/array_free.c b/test/c/data/array_free.c similarity index 100% rename from test/data/array_free.c rename to test/c/data/array_free.c diff --git a/test/data/array_free2.c b/test/c/data/array_free2.c similarity index 100% rename from test/data/array_free2.c rename to test/c/data/array_free2.c diff --git a/test/data/array_free2_fail.c b/test/c/data/array_free2_fail.c similarity index 100% rename from test/data/array_free2_fail.c rename to test/c/data/array_free2_fail.c diff --git a/test/data/array_free_fail.c b/test/c/data/array_free_fail.c similarity index 100% rename from test/data/array_free_fail.c rename to test/c/data/array_free_fail.c diff --git a/test/data/extern_func_ptr.c b/test/c/data/extern_func_ptr.c similarity index 100% rename from test/data/extern_func_ptr.c rename to test/c/data/extern_func_ptr.c diff --git a/test/data/extern_func_ptr_fail.c b/test/c/data/extern_func_ptr_fail.c similarity index 100% rename from test/data/extern_func_ptr_fail.c rename to test/c/data/extern_func_ptr_fail.c diff --git a/test/data/extern_struct.c b/test/c/data/extern_struct.c similarity index 100% rename from test/data/extern_struct.c rename to test/c/data/extern_struct.c diff --git a/test/data/free_as_func_ptr.c b/test/c/data/free_as_func_ptr.c similarity index 100% rename from test/data/free_as_func_ptr.c rename to test/c/data/free_as_func_ptr.c diff --git a/test/data/func_ptr.c b/test/c/data/func_ptr.c similarity index 100% rename from test/data/func_ptr.c rename to test/c/data/func_ptr.c diff --git a/test/data/func_ptr1.c b/test/c/data/func_ptr1.c similarity index 100% rename from test/data/func_ptr1.c rename to test/c/data/func_ptr1.c diff --git a/test/data/func_ptr1_fail.c b/test/c/data/func_ptr1_fail.c similarity index 100% rename from test/data/func_ptr1_fail.c rename to test/c/data/func_ptr1_fail.c diff --git a/test/data/func_ptr2.c b/test/c/data/func_ptr2.c similarity index 100% rename from test/data/func_ptr2.c rename to test/c/data/func_ptr2.c diff --git a/test/data/func_ptr2_fail.c b/test/c/data/func_ptr2_fail.c similarity index 100% rename from test/data/func_ptr2_fail.c rename to test/c/data/func_ptr2_fail.c diff --git a/test/data/func_ptr_alias.c b/test/c/data/func_ptr_alias.c similarity index 100% rename from test/data/func_ptr_alias.c rename to test/c/data/func_ptr_alias.c diff --git a/test/data/func_ptr_alias1.c b/test/c/data/func_ptr_alias1.c similarity index 100% rename from test/data/func_ptr_alias1.c rename to test/c/data/func_ptr_alias1.c diff --git a/test/data/func_ptr_alias1_fail.c b/test/c/data/func_ptr_alias1_fail.c similarity index 100% rename from test/data/func_ptr_alias1_fail.c rename to test/c/data/func_ptr_alias1_fail.c diff --git a/test/data/func_ptr_alias_fail.c b/test/c/data/func_ptr_alias_fail.c similarity index 100% rename from test/data/func_ptr_alias_fail.c rename to test/c/data/func_ptr_alias_fail.c diff --git a/test/data/func_ptr_array.c b/test/c/data/func_ptr_array.c similarity index 100% rename from test/data/func_ptr_array.c rename to test/c/data/func_ptr_array.c diff --git a/test/data/func_ptr_array_fail.c b/test/c/data/func_ptr_array_fail.c similarity index 100% rename from test/data/func_ptr_array_fail.c rename to test/c/data/func_ptr_array_fail.c diff --git a/test/data/func_ptr_fail.c b/test/c/data/func_ptr_fail.c similarity index 100% rename from test/data/func_ptr_fail.c rename to test/c/data/func_ptr_fail.c diff --git a/test/data/func_ptr_vararg.c b/test/c/data/func_ptr_vararg.c similarity index 100% rename from test/data/func_ptr_vararg.c rename to test/c/data/func_ptr_vararg.c diff --git a/test/data/func_ptr_vararg_fail.c b/test/c/data/func_ptr_vararg_fail.c similarity index 100% rename from test/data/func_ptr_vararg_fail.c rename to test/c/data/func_ptr_vararg_fail.c diff --git a/test/data/global_structs.c b/test/c/data/global_structs.c similarity index 100% rename from test/data/global_structs.c rename to test/c/data/global_structs.c diff --git a/test/data/global_structs_fail.c b/test/c/data/global_structs_fail.c similarity index 100% rename from test/data/global_structs_fail.c rename to test/c/data/global_structs_fail.c diff --git a/test/data/globals_func_ptr.c b/test/c/data/globals_func_ptr.c similarity index 100% rename from test/data/globals_func_ptr.c rename to test/c/data/globals_func_ptr.c diff --git a/test/data/globals_func_ptr_fail.c b/test/c/data/globals_func_ptr_fail.c similarity index 100% rename from test/data/globals_func_ptr_fail.c rename to test/c/data/globals_func_ptr_fail.c diff --git a/test/data/nested_struct.c b/test/c/data/nested_struct.c similarity index 100% rename from test/data/nested_struct.c rename to test/c/data/nested_struct.c diff --git a/test/data/nested_struct1.c b/test/c/data/nested_struct1.c similarity index 100% rename from test/data/nested_struct1.c rename to test/c/data/nested_struct1.c diff --git a/test/data/nested_struct1_fail.c b/test/c/data/nested_struct1_fail.c similarity index 100% rename from test/data/nested_struct1_fail.c rename to test/c/data/nested_struct1_fail.c diff --git a/test/data/nested_struct2.c b/test/c/data/nested_struct2.c similarity index 100% rename from test/data/nested_struct2.c rename to test/c/data/nested_struct2.c diff --git a/test/data/nested_struct2_fail.c b/test/c/data/nested_struct2_fail.c similarity index 100% rename from test/data/nested_struct2_fail.c rename to test/c/data/nested_struct2_fail.c diff --git a/test/data/nested_struct_fail.c b/test/c/data/nested_struct_fail.c similarity index 100% rename from test/data/nested_struct_fail.c rename to test/c/data/nested_struct_fail.c diff --git a/test/data/nondet_pointer_fail.c b/test/c/data/nondet_pointer_fail.c similarity index 100% rename from test/data/nondet_pointer_fail.c rename to test/c/data/nondet_pointer_fail.c diff --git a/test/data/pointers.c b/test/c/data/pointers.c similarity index 100% rename from test/data/pointers.c rename to test/c/data/pointers.c diff --git a/test/data/pointers1.c b/test/c/data/pointers1.c similarity index 100% rename from test/data/pointers1.c rename to test/c/data/pointers1.c diff --git a/test/data/pointers1_fail.c b/test/c/data/pointers1_fail.c similarity index 100% rename from test/data/pointers1_fail.c rename to test/c/data/pointers1_fail.c diff --git a/test/data/pointers2.c b/test/c/data/pointers2.c similarity index 100% rename from test/data/pointers2.c rename to test/c/data/pointers2.c diff --git a/test/data/pointers2_fail.c b/test/c/data/pointers2_fail.c similarity index 100% rename from test/data/pointers2_fail.c rename to test/c/data/pointers2_fail.c diff --git a/test/data/pointers3.c b/test/c/data/pointers3.c similarity index 100% rename from test/data/pointers3.c rename to test/c/data/pointers3.c diff --git a/test/data/pointers3_fail.c b/test/c/data/pointers3_fail.c similarity index 100% rename from test/data/pointers3_fail.c rename to test/c/data/pointers3_fail.c diff --git a/test/data/pointers4.c b/test/c/data/pointers4.c similarity index 100% rename from test/data/pointers4.c rename to test/c/data/pointers4.c diff --git a/test/data/pointers4_fail.c b/test/c/data/pointers4_fail.c similarity index 100% rename from test/data/pointers4_fail.c rename to test/c/data/pointers4_fail.c diff --git a/test/data/pointers5.c b/test/c/data/pointers5.c similarity index 100% rename from test/data/pointers5.c rename to test/c/data/pointers5.c diff --git a/test/data/pointers5_fail.c b/test/c/data/pointers5_fail.c similarity index 100% rename from test/data/pointers5_fail.c rename to test/c/data/pointers5_fail.c diff --git a/test/data/pointers8.c b/test/c/data/pointers8.c similarity index 100% rename from test/data/pointers8.c rename to test/c/data/pointers8.c diff --git a/test/data/pointers_fail.c b/test/c/data/pointers_fail.c similarity index 100% rename from test/data/pointers_fail.c rename to test/c/data/pointers_fail.c diff --git a/test/data/struct_alias.c b/test/c/data/struct_alias.c similarity index 100% rename from test/data/struct_alias.c rename to test/c/data/struct_alias.c diff --git a/test/data/struct_alias_fail.c b/test/c/data/struct_alias_fail.c similarity index 100% rename from test/data/struct_alias_fail.c rename to test/c/data/struct_alias_fail.c diff --git a/test/data/struct_array.c b/test/c/data/struct_array.c similarity index 100% rename from test/data/struct_array.c rename to test/c/data/struct_array.c diff --git a/test/data/struct_array_fail.c b/test/c/data/struct_array_fail.c similarity index 100% rename from test/data/struct_array_fail.c rename to test/c/data/struct_array_fail.c diff --git a/test/data/struct_assign.c b/test/c/data/struct_assign.c similarity index 100% rename from test/data/struct_assign.c rename to test/c/data/struct_assign.c diff --git a/test/data/struct_assign_fail.c b/test/c/data/struct_assign_fail.c similarity index 100% rename from test/data/struct_assign_fail.c rename to test/c/data/struct_assign_fail.c diff --git a/test/data/struct_cast.c b/test/c/data/struct_cast.c similarity index 100% rename from test/data/struct_cast.c rename to test/c/data/struct_cast.c diff --git a/test/data/struct_cast1.c b/test/c/data/struct_cast1.c similarity index 100% rename from test/data/struct_cast1.c rename to test/c/data/struct_cast1.c diff --git a/test/data/struct_cast1_fail.c b/test/c/data/struct_cast1_fail.c similarity index 100% rename from test/data/struct_cast1_fail.c rename to test/c/data/struct_cast1_fail.c diff --git a/test/data/struct_cast_fail.c b/test/c/data/struct_cast_fail.c similarity index 100% rename from test/data/struct_cast_fail.c rename to test/c/data/struct_cast_fail.c diff --git a/test/data/struct_const_return.c b/test/c/data/struct_const_return.c similarity index 100% rename from test/data/struct_const_return.c rename to test/c/data/struct_const_return.c diff --git a/test/data/struct_const_return_fail.c b/test/c/data/struct_const_return_fail.c similarity index 100% rename from test/data/struct_const_return_fail.c rename to test/c/data/struct_const_return_fail.c diff --git a/test/data/struct_init.c b/test/c/data/struct_init.c similarity index 100% rename from test/data/struct_init.c rename to test/c/data/struct_init.c diff --git a/test/data/struct_init_fail.c b/test/c/data/struct_init_fail.c similarity index 100% rename from test/data/struct_init_fail.c rename to test/c/data/struct_init_fail.c diff --git a/test/data/struct_return.c b/test/c/data/struct_return.c similarity index 100% rename from test/data/struct_return.c rename to test/c/data/struct_return.c diff --git a/test/data/two_arrays.c b/test/c/data/two_arrays.c similarity index 100% rename from test/data/two_arrays.c rename to test/c/data/two_arrays.c diff --git a/test/data/two_arrays1.c b/test/c/data/two_arrays1.c similarity index 100% rename from test/data/two_arrays1.c rename to test/c/data/two_arrays1.c diff --git a/test/data/two_arrays1_fail.c b/test/c/data/two_arrays1_fail.c similarity index 100% rename from test/data/two_arrays1_fail.c rename to test/c/data/two_arrays1_fail.c diff --git a/test/data/two_arrays2.c b/test/c/data/two_arrays2.c similarity index 100% rename from test/data/two_arrays2.c rename to test/c/data/two_arrays2.c diff --git a/test/data/two_arrays3.c b/test/c/data/two_arrays3.c similarity index 100% rename from test/data/two_arrays3.c rename to test/c/data/two_arrays3.c diff --git a/test/data/two_arrays4.c b/test/c/data/two_arrays4.c similarity index 100% rename from test/data/two_arrays4.c rename to test/c/data/two_arrays4.c diff --git a/test/data/two_arrays5.c b/test/c/data/two_arrays5.c similarity index 100% rename from test/data/two_arrays5.c rename to test/c/data/two_arrays5.c diff --git a/test/data/two_arrays6.c b/test/c/data/two_arrays6.c similarity index 100% rename from test/data/two_arrays6.c rename to test/c/data/two_arrays6.c diff --git a/test/data/two_arrays6_fail.c b/test/c/data/two_arrays6_fail.c similarity index 100% rename from test/data/two_arrays6_fail.c rename to test/c/data/two_arrays6_fail.c diff --git a/test/data/two_arrays_fail.c b/test/c/data/two_arrays_fail.c similarity index 100% rename from test/data/two_arrays_fail.c rename to test/c/data/two_arrays_fail.c diff --git a/test/basic/atomic_cas.c b/test/c/failing/atomic_cas.c similarity index 100% rename from test/basic/atomic_cas.c rename to test/c/failing/atomic_cas.c diff --git a/test/basic/atomic_cas_fail.c b/test/c/failing/atomic_cas_fail.c similarity index 100% rename from test/basic/atomic_cas_fail.c rename to test/c/failing/atomic_cas_fail.c diff --git a/test/failing/config.yml b/test/c/failing/config.yml similarity index 100% rename from test/failing/config.yml rename to test/c/failing/config.yml diff --git a/test/failing/exit.c b/test/c/failing/exit.c similarity index 100% rename from test/failing/exit.c rename to test/c/failing/exit.c diff --git a/test/failing/extern_mem2.c b/test/c/failing/extern_mem2.c similarity index 100% rename from test/failing/extern_mem2.c rename to test/c/failing/extern_mem2.c diff --git a/test/basic/extern_mem_fail.c b/test/c/failing/extern_mem_fail.c similarity index 78% rename from test/basic/extern_mem_fail.c rename to test/c/failing/extern_mem_fail.c index b2fb4d566..d432dc7f2 100644 --- a/test/basic/extern_mem_fail.c +++ b/test/c/failing/extern_mem_fail.c @@ -3,6 +3,7 @@ // @expect error +// see: https://github.com/smackers/smack/issues/553 void foo(int *); int *bar(); diff --git a/test/ntdrivers/floppy_false.i.cil.c b/test/c/failing/floppy_false.i.cil.c similarity index 99% rename from test/ntdrivers/floppy_false.i.cil.c rename to test/c/failing/floppy_false.i.cil.c index de2af3864..9d57493ea 100644 --- a/test/ntdrivers/floppy_false.i.cil.c +++ b/test/c/failing/floppy_false.i.cil.c @@ -2,6 +2,7 @@ // @expect error +// see: https://github.com/smackers/smack/issues/554 extern char __VERIFIER_nondet_char(void); extern int __VERIFIER_nondet_int(void); extern long __VERIFIER_nondet_long(void); diff --git a/test/ntdrivers/floppy_true.i.cil.c b/test/c/failing/floppy_true.i.cil.c similarity index 99% rename from test/ntdrivers/floppy_true.i.cil.c rename to test/c/failing/floppy_true.i.cil.c index d548c4135..67388109c 100644 --- a/test/ntdrivers/floppy_true.i.cil.c +++ b/test/c/failing/floppy_true.i.cil.c @@ -2,6 +2,7 @@ // @expect verified +// see: https://github.com/smackers/smack/issues/554 extern char __VERIFIER_nondet_char(void); extern int __VERIFIER_nondet_int(void); extern long __VERIFIER_nondet_long(void); diff --git a/test/pthread/join_return.c b/test/c/failing/join_return.c similarity index 92% rename from test/pthread/join_return.c rename to test/c/failing/join_return.c index d5c7b812b..9e811a092 100644 --- a/test/pthread/join_return.c +++ b/test/c/failing/join_return.c @@ -8,6 +8,7 @@ // @expect verified +// see: https://github.com/smackers/smack/issues/555 int x = 1; typedef struct pair { diff --git a/test/pthread/join_return_fail.c b/test/c/failing/join_return_fail.c similarity index 100% rename from test/pthread/join_return_fail.c rename to test/c/failing/join_return_fail.c diff --git a/test/ntdrivers/parport_false.i.cil.c b/test/c/failing/parport_false.i.cil.c similarity index 99% rename from test/ntdrivers/parport_false.i.cil.c rename to test/c/failing/parport_false.i.cil.c index 8f6290522..e6dda6b9f 100644 --- a/test/ntdrivers/parport_false.i.cil.c +++ b/test/c/failing/parport_false.i.cil.c @@ -2,6 +2,7 @@ // @expect error +// see: https://github.com/smackers/smack/issues/554 extern char __VERIFIER_nondet_char(void); extern int __VERIFIER_nondet_int(void); extern long __VERIFIER_nondet_long(void); diff --git a/test/ntdrivers/parport_true.i.cil.c b/test/c/failing/parport_true.i.cil.c similarity index 99% rename from test/ntdrivers/parport_true.i.cil.c rename to test/c/failing/parport_true.i.cil.c index 36921913e..1606668e4 100644 --- a/test/ntdrivers/parport_true.i.cil.c +++ b/test/c/failing/parport_true.i.cil.c @@ -2,6 +2,7 @@ // @expect verified +// see: https://github.com/smackers/smack/issues/554 extern char __VERIFIER_nondet_char(void); extern int __VERIFIER_nondet_int(void); extern long __VERIFIER_nondet_long(void); diff --git a/test/c/failing/regression_525_calloc.c b/test/c/failing/regression_525_calloc.c new file mode 100644 index 000000000..aa9c294fb --- /dev/null +++ b/test/c/failing/regression_525_calloc.c @@ -0,0 +1,34 @@ +#include "smack.h" +#include +#include +#include +#include + +// @expect verified +// https://github.com/smackers/smack/issues/525 + +_Atomic(atomic_int *) x; + +void *t1(void *arg) { + atomic_store_explicit(&x, (_Atomic(int) *)calloc(1, sizeof(int)), + memory_order_relaxed); + if (atomic_load_explicit(&x, memory_order_relaxed)) { + atomic_store_explicit(x, 42, memory_order_relaxed); + } + return NULL; +} + +int main(void) { + atomic_init(&x, NULL); + pthread_t tid1; + pthread_create(&tid1, 0, t1, 0); + if (atomic_load_explicit(&x, memory_order_relaxed)) { + int r1 = atomic_load_explicit(x, memory_order_relaxed); + assert(r1 == 0 || r1 == 42); + } + pthread_join(tid1, 0); + if (x) { + free(x); + } + return 0; +} diff --git a/test/c/failing/regression_525_malloc.c b/test/c/failing/regression_525_malloc.c new file mode 100644 index 000000000..a11c59dc2 --- /dev/null +++ b/test/c/failing/regression_525_malloc.c @@ -0,0 +1,44 @@ +#include "smack.h" +#include +#include + +// @expect verified +// see: https://github.com/smackers/smack/issues/555 +// https://github.com/smackers/smack/issues/525 + +pthread_t tid1; +pthread_t tid2; + +void *t1(void *arg) { + int *x = malloc(sizeof(*x)); + if (!x) + pthread_exit(NULL); + *x = 1; + pthread_exit(x); + return NULL; +} + +void *t2(void *arg) { + int *y = malloc(sizeof(*y)); + if (!y) + pthread_exit(NULL); + *y = 2; + pthread_exit(y); + return NULL; +} + +int main(void) { + pthread_create(&tid1, 0, t1, 0); + pthread_create(&tid2, 0, t2, 0); + int *tid1_retval; + int *tid2_retval; + pthread_join(tid1, (void **)&tid1_retval); + pthread_join(tid2, (void **)&tid2_retval); + assert(!tid1_retval || *tid1_retval == 1); + assert(!tid2_retval || *tid2_retval == 2); + if (tid1_retval) + free(tid1_retval); + if (tid2_retval) + free(tid2_retval); + return 0; +} diff --git a/test/failing/struct_by_value.c b/test/c/failing/struct_by_value.c similarity index 100% rename from test/failing/struct_by_value.c rename to test/c/failing/struct_by_value.c diff --git a/test/memory-safety/null_dereference_fail.c b/test/c/failing/unallocated_dereference_fail.c similarity index 68% rename from test/memory-safety/null_dereference_fail.c rename to test/c/failing/unallocated_dereference_fail.c index 3b4ceaad5..8e3f49232 100644 --- a/test/memory-safety/null_dereference_fail.c +++ b/test/c/failing/unallocated_dereference_fail.c @@ -3,6 +3,7 @@ // @expect error +// see: https://github.com/smackers/smack/issues/554 int main(void) { int *a; int b = a[0]; diff --git a/test/float/bitcast.c b/test/c/float/bitcast.c similarity index 100% rename from test/float/bitcast.c rename to test/c/float/bitcast.c diff --git a/test/float/bitcast_fail.c b/test/c/float/bitcast_fail.c similarity index 100% rename from test/float/bitcast_fail.c rename to test/c/float/bitcast_fail.c diff --git a/test/float/change_rm.c b/test/c/float/change_rm.c similarity index 100% rename from test/float/change_rm.c rename to test/c/float/change_rm.c diff --git a/test/float/change_rm_fail.c b/test/c/float/change_rm_fail.c similarity index 100% rename from test/float/change_rm_fail.c rename to test/c/float/change_rm_fail.c diff --git a/test/float/config.yml b/test/c/float/config.yml similarity index 100% rename from test/float/config.yml rename to test/c/float/config.yml diff --git a/test/float/double_op_fail.c b/test/c/float/double_op_fail.c similarity index 100% rename from test/float/double_op_fail.c rename to test/c/float/double_op_fail.c diff --git a/test/float/double_ops.c b/test/c/float/double_ops.c similarity index 100% rename from test/float/double_ops.c rename to test/c/float/double_ops.c diff --git a/test/float/double_to_int.c b/test/c/float/double_to_int.c similarity index 100% rename from test/float/double_to_int.c rename to test/c/float/double_to_int.c diff --git a/test/float/double_to_int_fail.c b/test/c/float/double_to_int_fail.c similarity index 100% rename from test/float/double_to_int_fail.c rename to test/c/float/double_to_int_fail.c diff --git a/test/float/float_int_union.c b/test/c/float/float_int_union.c similarity index 100% rename from test/float/float_int_union.c rename to test/c/float/float_int_union.c diff --git a/test/float/float_int_union_fail.c b/test/c/float/float_int_union_fail.c similarity index 100% rename from test/float/float_int_union_fail.c rename to test/c/float/float_int_union_fail.c diff --git a/test/float/float_op_fail.c b/test/c/float/float_op_fail.c similarity index 100% rename from test/float/float_op_fail.c rename to test/c/float/float_op_fail.c diff --git a/test/float/float_ops.c b/test/c/float/float_ops.c similarity index 100% rename from test/float/float_ops.c rename to test/c/float/float_ops.c diff --git a/test/float/float_ops_rm.c b/test/c/float/float_ops_rm.c similarity index 100% rename from test/float/float_ops_rm.c rename to test/c/float/float_ops_rm.c diff --git a/test/float/float_ops_rm_fail.c b/test/c/float/float_ops_rm_fail.c similarity index 100% rename from test/float/float_ops_rm_fail.c rename to test/c/float/float_ops_rm_fail.c diff --git a/test/float/floats_in_memory.c b/test/c/float/floats_in_memory.c similarity index 100% rename from test/float/floats_in_memory.c rename to test/c/float/floats_in_memory.c diff --git a/test/float/floats_in_memory_fail.c b/test/c/float/floats_in_memory_fail.c similarity index 100% rename from test/float/floats_in_memory_fail.c rename to test/c/float/floats_in_memory_fail.c diff --git a/test/float/floor.c b/test/c/float/floor.c similarity index 100% rename from test/float/floor.c rename to test/c/float/floor.c diff --git a/test/float/floor_fail.c b/test/c/float/floor_fail.c similarity index 100% rename from test/float/floor_fail.c rename to test/c/float/floor_fail.c diff --git a/test/float/get_rm_invalid.c b/test/c/float/get_rm_invalid.c similarity index 100% rename from test/float/get_rm_invalid.c rename to test/c/float/get_rm_invalid.c diff --git a/test/float/get_rm_invalid_fail.c b/test/c/float/get_rm_invalid_fail.c similarity index 100% rename from test/float/get_rm_invalid_fail.c rename to test/c/float/get_rm_invalid_fail.c diff --git a/test/float/half_intrinsics.c b/test/c/float/half_intrinsics.c similarity index 100% rename from test/float/half_intrinsics.c rename to test/c/float/half_intrinsics.c diff --git a/test/float/half_intrinsics_fail.c b/test/c/float/half_intrinsics_fail.c similarity index 100% rename from test/float/half_intrinsics_fail.c rename to test/c/float/half_intrinsics_fail.c diff --git a/test/float/intrinsics.c b/test/c/float/intrinsics.c similarity index 100% rename from test/float/intrinsics.c rename to test/c/float/intrinsics.c diff --git a/test/float/intrinsics_fail.c b/test/c/float/intrinsics_fail.c similarity index 100% rename from test/float/intrinsics_fail.c rename to test/c/float/intrinsics_fail.c diff --git a/test/float/llvm_intrinsic.c b/test/c/float/llvm_intrinsic.c similarity index 100% rename from test/float/llvm_intrinsic.c rename to test/c/float/llvm_intrinsic.c diff --git a/test/float/llvm_intrinsic_fail.c b/test/c/float/llvm_intrinsic_fail.c similarity index 100% rename from test/float/llvm_intrinsic_fail.c rename to test/c/float/llvm_intrinsic_fail.c diff --git a/test/float/set_rm_invalid.c b/test/c/float/set_rm_invalid.c similarity index 100% rename from test/float/set_rm_invalid.c rename to test/c/float/set_rm_invalid.c diff --git a/test/float/set_rm_invalid_fail.c b/test/c/float/set_rm_invalid_fail.c similarity index 100% rename from test/float/set_rm_invalid_fail.c rename to test/c/float/set_rm_invalid_fail.c diff --git a/test/float/simple_double.c b/test/c/float/simple_double.c similarity index 100% rename from test/float/simple_double.c rename to test/c/float/simple_double.c diff --git a/test/float/simple_double_fail.c b/test/c/float/simple_double_fail.c similarity index 100% rename from test/float/simple_double_fail.c rename to test/c/float/simple_double_fail.c diff --git a/test/float/simple_float.c b/test/c/float/simple_float.c similarity index 100% rename from test/float/simple_float.c rename to test/c/float/simple_float.c diff --git a/test/float/simple_float_fail.c b/test/c/float/simple_float_fail.c similarity index 100% rename from test/float/simple_float_fail.c rename to test/c/float/simple_float_fail.c diff --git a/test/float/smack_code_annot.c b/test/c/float/smack_code_annot.c similarity index 100% rename from test/float/smack_code_annot.c rename to test/c/float/smack_code_annot.c diff --git a/test/float/smack_code_annot_fail.c b/test/c/float/smack_code_annot_fail.c similarity index 100% rename from test/float/smack_code_annot_fail.c rename to test/c/float/smack_code_annot_fail.c diff --git a/test/locks/.gitignore b/test/c/locks/.gitignore similarity index 100% rename from test/locks/.gitignore rename to test/c/locks/.gitignore diff --git a/test/locks/config.yml b/test/c/locks/config.yml similarity index 100% rename from test/locks/config.yml rename to test/c/locks/config.yml diff --git a/test/locks/test_locks_10_true.c b/test/c/locks/test_locks_10_true.c similarity index 100% rename from test/locks/test_locks_10_true.c rename to test/c/locks/test_locks_10_true.c diff --git a/test/locks/test_locks_11_true.c b/test/c/locks/test_locks_11_true.c similarity index 100% rename from test/locks/test_locks_11_true.c rename to test/c/locks/test_locks_11_true.c diff --git a/test/locks/test_locks_12_true.c b/test/c/locks/test_locks_12_true.c similarity index 100% rename from test/locks/test_locks_12_true.c rename to test/c/locks/test_locks_12_true.c diff --git a/test/locks/test_locks_13_true.c b/test/c/locks/test_locks_13_true.c similarity index 100% rename from test/locks/test_locks_13_true.c rename to test/c/locks/test_locks_13_true.c diff --git a/test/locks/test_locks_14_false.c b/test/c/locks/test_locks_14_false.c similarity index 100% rename from test/locks/test_locks_14_false.c rename to test/c/locks/test_locks_14_false.c diff --git a/test/locks/test_locks_14_true.c b/test/c/locks/test_locks_14_true.c similarity index 100% rename from test/locks/test_locks_14_true.c rename to test/c/locks/test_locks_14_true.c diff --git a/test/locks/test_locks_15_false.c b/test/c/locks/test_locks_15_false.c similarity index 100% rename from test/locks/test_locks_15_false.c rename to test/c/locks/test_locks_15_false.c diff --git a/test/locks/test_locks_15_true.c b/test/c/locks/test_locks_15_true.c similarity index 100% rename from test/locks/test_locks_15_true.c rename to test/c/locks/test_locks_15_true.c diff --git a/test/locks/test_locks_5_true.c b/test/c/locks/test_locks_5_true.c similarity index 100% rename from test/locks/test_locks_5_true.c rename to test/c/locks/test_locks_5_true.c diff --git a/test/locks/test_locks_6_true.c b/test/c/locks/test_locks_6_true.c similarity index 100% rename from test/locks/test_locks_6_true.c rename to test/c/locks/test_locks_6_true.c diff --git a/test/locks/test_locks_7_true.c b/test/c/locks/test_locks_7_true.c similarity index 100% rename from test/locks/test_locks_7_true.c rename to test/c/locks/test_locks_7_true.c diff --git a/test/locks/test_locks_8_true.c b/test/c/locks/test_locks_8_true.c similarity index 100% rename from test/locks/test_locks_8_true.c rename to test/c/locks/test_locks_8_true.c diff --git a/test/locks/test_locks_9_true.c b/test/c/locks/test_locks_9_true.c similarity index 100% rename from test/locks/test_locks_9_true.c rename to test/c/locks/test_locks_9_true.c diff --git a/test/mathc/ceil.c b/test/c/mathc/ceil.c similarity index 100% rename from test/mathc/ceil.c rename to test/c/mathc/ceil.c diff --git a/test/mathc/ceil_fail.c b/test/c/mathc/ceil_fail.c similarity index 100% rename from test/mathc/ceil_fail.c rename to test/c/mathc/ceil_fail.c diff --git a/test/mathc/ceilf.c b/test/c/mathc/ceilf.c similarity index 100% rename from test/mathc/ceilf.c rename to test/c/mathc/ceilf.c diff --git a/test/mathc/ceilf_fail.c b/test/c/mathc/ceilf_fail.c similarity index 100% rename from test/mathc/ceilf_fail.c rename to test/c/mathc/ceilf_fail.c diff --git a/test/mathc/ceill.c b/test/c/mathc/ceill.c similarity index 100% rename from test/mathc/ceill.c rename to test/c/mathc/ceill.c diff --git a/test/mathc/ceill_fail.c b/test/c/mathc/ceill_fail.c similarity index 100% rename from test/mathc/ceill_fail.c rename to test/c/mathc/ceill_fail.c diff --git a/test/mathc/config.yml b/test/c/mathc/config.yml similarity index 100% rename from test/mathc/config.yml rename to test/c/mathc/config.yml diff --git a/test/mathc/copysign.c b/test/c/mathc/copysign.c similarity index 100% rename from test/mathc/copysign.c rename to test/c/mathc/copysign.c diff --git a/test/mathc/copysign_fail.c b/test/c/mathc/copysign_fail.c similarity index 100% rename from test/mathc/copysign_fail.c rename to test/c/mathc/copysign_fail.c diff --git a/test/mathc/copysignf.c b/test/c/mathc/copysignf.c similarity index 100% rename from test/mathc/copysignf.c rename to test/c/mathc/copysignf.c diff --git a/test/mathc/copysignf_fail.c b/test/c/mathc/copysignf_fail.c similarity index 100% rename from test/mathc/copysignf_fail.c rename to test/c/mathc/copysignf_fail.c diff --git a/test/mathc/copysignl.c b/test/c/mathc/copysignl.c similarity index 100% rename from test/mathc/copysignl.c rename to test/c/mathc/copysignl.c diff --git a/test/mathc/copysignl_fail.c b/test/c/mathc/copysignl_fail.c similarity index 100% rename from test/mathc/copysignl_fail.c rename to test/c/mathc/copysignl_fail.c diff --git a/test/mathc/fabs.c b/test/c/mathc/fabs.c similarity index 100% rename from test/mathc/fabs.c rename to test/c/mathc/fabs.c diff --git a/test/mathc/fabs_fail.c b/test/c/mathc/fabs_fail.c similarity index 100% rename from test/mathc/fabs_fail.c rename to test/c/mathc/fabs_fail.c diff --git a/test/mathc/fabsf.c b/test/c/mathc/fabsf.c similarity index 100% rename from test/mathc/fabsf.c rename to test/c/mathc/fabsf.c diff --git a/test/mathc/fabsf_fail.c b/test/c/mathc/fabsf_fail.c similarity index 100% rename from test/mathc/fabsf_fail.c rename to test/c/mathc/fabsf_fail.c diff --git a/test/mathc/fabsl.c b/test/c/mathc/fabsl.c similarity index 100% rename from test/mathc/fabsl.c rename to test/c/mathc/fabsl.c diff --git a/test/mathc/fabsl_fail.c b/test/c/mathc/fabsl_fail.c similarity index 100% rename from test/mathc/fabsl_fail.c rename to test/c/mathc/fabsl_fail.c diff --git a/test/mathc/fdim.c b/test/c/mathc/fdim.c similarity index 100% rename from test/mathc/fdim.c rename to test/c/mathc/fdim.c diff --git a/test/mathc/fdim_fail.c b/test/c/mathc/fdim_fail.c similarity index 100% rename from test/mathc/fdim_fail.c rename to test/c/mathc/fdim_fail.c diff --git a/test/mathc/fdimf.c b/test/c/mathc/fdimf.c similarity index 100% rename from test/mathc/fdimf.c rename to test/c/mathc/fdimf.c diff --git a/test/mathc/fdimf_fail.c b/test/c/mathc/fdimf_fail.c similarity index 100% rename from test/mathc/fdimf_fail.c rename to test/c/mathc/fdimf_fail.c diff --git a/test/mathc/fdiml.c b/test/c/mathc/fdiml.c similarity index 100% rename from test/mathc/fdiml.c rename to test/c/mathc/fdiml.c diff --git a/test/mathc/fdiml_fail.c b/test/c/mathc/fdiml_fail.c similarity index 100% rename from test/mathc/fdiml_fail.c rename to test/c/mathc/fdiml_fail.c diff --git a/test/mathc/floor.c b/test/c/mathc/floor.c similarity index 100% rename from test/mathc/floor.c rename to test/c/mathc/floor.c diff --git a/test/mathc/floor_fail.c b/test/c/mathc/floor_fail.c similarity index 100% rename from test/mathc/floor_fail.c rename to test/c/mathc/floor_fail.c diff --git a/test/mathc/floorf.c b/test/c/mathc/floorf.c similarity index 100% rename from test/mathc/floorf.c rename to test/c/mathc/floorf.c diff --git a/test/mathc/floorf_fail.c b/test/c/mathc/floorf_fail.c similarity index 100% rename from test/mathc/floorf_fail.c rename to test/c/mathc/floorf_fail.c diff --git a/test/mathc/floorl.c b/test/c/mathc/floorl.c similarity index 100% rename from test/mathc/floorl.c rename to test/c/mathc/floorl.c diff --git a/test/mathc/floorl_fail.c b/test/c/mathc/floorl_fail.c similarity index 100% rename from test/mathc/floorl_fail.c rename to test/c/mathc/floorl_fail.c diff --git a/test/mathc/fmax.c b/test/c/mathc/fmax.c similarity index 100% rename from test/mathc/fmax.c rename to test/c/mathc/fmax.c diff --git a/test/mathc/fmax_fail.c b/test/c/mathc/fmax_fail.c similarity index 100% rename from test/mathc/fmax_fail.c rename to test/c/mathc/fmax_fail.c diff --git a/test/mathc/fmaxf.c b/test/c/mathc/fmaxf.c similarity index 100% rename from test/mathc/fmaxf.c rename to test/c/mathc/fmaxf.c diff --git a/test/mathc/fmaxf_fail.c b/test/c/mathc/fmaxf_fail.c similarity index 100% rename from test/mathc/fmaxf_fail.c rename to test/c/mathc/fmaxf_fail.c diff --git a/test/mathc/fmaxl.c b/test/c/mathc/fmaxl.c similarity index 100% rename from test/mathc/fmaxl.c rename to test/c/mathc/fmaxl.c diff --git a/test/mathc/fmaxl_fail.c b/test/c/mathc/fmaxl_fail.c similarity index 100% rename from test/mathc/fmaxl_fail.c rename to test/c/mathc/fmaxl_fail.c diff --git a/test/mathc/fmin.c b/test/c/mathc/fmin.c similarity index 100% rename from test/mathc/fmin.c rename to test/c/mathc/fmin.c diff --git a/test/mathc/fmin_fail.c b/test/c/mathc/fmin_fail.c similarity index 100% rename from test/mathc/fmin_fail.c rename to test/c/mathc/fmin_fail.c diff --git a/test/mathc/fminf.c b/test/c/mathc/fminf.c similarity index 100% rename from test/mathc/fminf.c rename to test/c/mathc/fminf.c diff --git a/test/mathc/fminf_fail.c b/test/c/mathc/fminf_fail.c similarity index 100% rename from test/mathc/fminf_fail.c rename to test/c/mathc/fminf_fail.c diff --git a/test/mathc/fminl.c b/test/c/mathc/fminl.c similarity index 100% rename from test/mathc/fminl.c rename to test/c/mathc/fminl.c diff --git a/test/mathc/fminl_fail.c b/test/c/mathc/fminl_fail.c similarity index 100% rename from test/mathc/fminl_fail.c rename to test/c/mathc/fminl_fail.c diff --git a/test/mathc/fmod.c b/test/c/mathc/fmod.c similarity index 100% rename from test/mathc/fmod.c rename to test/c/mathc/fmod.c diff --git a/test/mathc/fmod_fail.c b/test/c/mathc/fmod_fail.c similarity index 100% rename from test/mathc/fmod_fail.c rename to test/c/mathc/fmod_fail.c diff --git a/test/mathc/fmodf.c b/test/c/mathc/fmodf.c similarity index 100% rename from test/mathc/fmodf.c rename to test/c/mathc/fmodf.c diff --git a/test/mathc/fmodf_fail.c b/test/c/mathc/fmodf_fail.c similarity index 100% rename from test/mathc/fmodf_fail.c rename to test/c/mathc/fmodf_fail.c diff --git a/test/mathc/fmodl.c b/test/c/mathc/fmodl.c similarity index 100% rename from test/mathc/fmodl.c rename to test/c/mathc/fmodl.c diff --git a/test/mathc/fmodl_fail.c b/test/c/mathc/fmodl_fail.c similarity index 100% rename from test/mathc/fmodl_fail.c rename to test/c/mathc/fmodl_fail.c diff --git a/test/mathc/issue_198.c b/test/c/mathc/issue_198.c similarity index 100% rename from test/mathc/issue_198.c rename to test/c/mathc/issue_198.c diff --git a/test/mathc/issue_198_fail.c b/test/c/mathc/issue_198_fail.c similarity index 100% rename from test/mathc/issue_198_fail.c rename to test/c/mathc/issue_198_fail.c diff --git a/test/mathc/issue_244.c b/test/c/mathc/issue_244.c similarity index 100% rename from test/mathc/issue_244.c rename to test/c/mathc/issue_244.c diff --git a/test/mathc/issue_244_fail.c b/test/c/mathc/issue_244_fail.c similarity index 100% rename from test/mathc/issue_244_fail.c rename to test/c/mathc/issue_244_fail.c diff --git a/test/mathc/lrint.c b/test/c/mathc/lrint.c similarity index 100% rename from test/mathc/lrint.c rename to test/c/mathc/lrint.c diff --git a/test/mathc/lrint_fail.c b/test/c/mathc/lrint_fail.c similarity index 100% rename from test/mathc/lrint_fail.c rename to test/c/mathc/lrint_fail.c diff --git a/test/mathc/lrintf.c b/test/c/mathc/lrintf.c similarity index 100% rename from test/mathc/lrintf.c rename to test/c/mathc/lrintf.c diff --git a/test/mathc/lrintf_fail.c b/test/c/mathc/lrintf_fail.c similarity index 100% rename from test/mathc/lrintf_fail.c rename to test/c/mathc/lrintf_fail.c diff --git a/test/mathc/lrintl.c b/test/c/mathc/lrintl.c similarity index 100% rename from test/mathc/lrintl.c rename to test/c/mathc/lrintl.c diff --git a/test/mathc/lrintl_fail.c b/test/c/mathc/lrintl_fail.c similarity index 100% rename from test/mathc/lrintl_fail.c rename to test/c/mathc/lrintl_fail.c diff --git a/test/mathc/lround.c b/test/c/mathc/lround.c similarity index 100% rename from test/mathc/lround.c rename to test/c/mathc/lround.c diff --git a/test/mathc/lround_fail.c b/test/c/mathc/lround_fail.c similarity index 100% rename from test/mathc/lround_fail.c rename to test/c/mathc/lround_fail.c diff --git a/test/mathc/lroundf.c b/test/c/mathc/lroundf.c similarity index 100% rename from test/mathc/lroundf.c rename to test/c/mathc/lroundf.c diff --git a/test/mathc/lroundf_fail.c b/test/c/mathc/lroundf_fail.c similarity index 100% rename from test/mathc/lroundf_fail.c rename to test/c/mathc/lroundf_fail.c diff --git a/test/mathc/lroundl.c b/test/c/mathc/lroundl.c similarity index 100% rename from test/mathc/lroundl.c rename to test/c/mathc/lroundl.c diff --git a/test/mathc/lroundl_fail.c b/test/c/mathc/lroundl_fail.c similarity index 100% rename from test/mathc/lroundl_fail.c rename to test/c/mathc/lroundl_fail.c diff --git a/test/mathc/modf.c b/test/c/mathc/modf.c similarity index 100% rename from test/mathc/modf.c rename to test/c/mathc/modf.c diff --git a/test/mathc/modf_fail.c b/test/c/mathc/modf_fail.c similarity index 100% rename from test/mathc/modf_fail.c rename to test/c/mathc/modf_fail.c diff --git a/test/mathc/modff.c b/test/c/mathc/modff.c similarity index 100% rename from test/mathc/modff.c rename to test/c/mathc/modff.c diff --git a/test/mathc/modff_fail.c b/test/c/mathc/modff_fail.c similarity index 100% rename from test/mathc/modff_fail.c rename to test/c/mathc/modff_fail.c diff --git a/test/mathc/modfl.c b/test/c/mathc/modfl.c similarity index 100% rename from test/mathc/modfl.c rename to test/c/mathc/modfl.c diff --git a/test/mathc/modfl_fail.c b/test/c/mathc/modfl_fail.c similarity index 100% rename from test/mathc/modfl_fail.c rename to test/c/mathc/modfl_fail.c diff --git a/test/mathc/nearbyint.c b/test/c/mathc/nearbyint.c similarity index 100% rename from test/mathc/nearbyint.c rename to test/c/mathc/nearbyint.c diff --git a/test/mathc/nearbyint_fail.c b/test/c/mathc/nearbyint_fail.c similarity index 100% rename from test/mathc/nearbyint_fail.c rename to test/c/mathc/nearbyint_fail.c diff --git a/test/mathc/nearbyintf.c b/test/c/mathc/nearbyintf.c similarity index 100% rename from test/mathc/nearbyintf.c rename to test/c/mathc/nearbyintf.c diff --git a/test/mathc/nearbyintf_fail.c b/test/c/mathc/nearbyintf_fail.c similarity index 100% rename from test/mathc/nearbyintf_fail.c rename to test/c/mathc/nearbyintf_fail.c diff --git a/test/mathc/nearbyintl.c b/test/c/mathc/nearbyintl.c similarity index 100% rename from test/mathc/nearbyintl.c rename to test/c/mathc/nearbyintl.c diff --git a/test/mathc/nearbyintl_fail.c b/test/c/mathc/nearbyintl_fail.c similarity index 100% rename from test/mathc/nearbyintl_fail.c rename to test/c/mathc/nearbyintl_fail.c diff --git a/test/mathc/remainder.c b/test/c/mathc/remainder.c similarity index 100% rename from test/mathc/remainder.c rename to test/c/mathc/remainder.c diff --git a/test/mathc/remainder_fail.c b/test/c/mathc/remainder_fail.c similarity index 100% rename from test/mathc/remainder_fail.c rename to test/c/mathc/remainder_fail.c diff --git a/test/mathc/remainderf.c b/test/c/mathc/remainderf.c similarity index 100% rename from test/mathc/remainderf.c rename to test/c/mathc/remainderf.c diff --git a/test/mathc/remainderf_fail.c b/test/c/mathc/remainderf_fail.c similarity index 100% rename from test/mathc/remainderf_fail.c rename to test/c/mathc/remainderf_fail.c diff --git a/test/mathc/remainderl.c b/test/c/mathc/remainderl.c similarity index 100% rename from test/mathc/remainderl.c rename to test/c/mathc/remainderl.c diff --git a/test/mathc/remainderl_fail.c b/test/c/mathc/remainderl_fail.c similarity index 100% rename from test/mathc/remainderl_fail.c rename to test/c/mathc/remainderl_fail.c diff --git a/test/mathc/rint.c b/test/c/mathc/rint.c similarity index 100% rename from test/mathc/rint.c rename to test/c/mathc/rint.c diff --git a/test/mathc/rint_fail.c b/test/c/mathc/rint_fail.c similarity index 100% rename from test/mathc/rint_fail.c rename to test/c/mathc/rint_fail.c diff --git a/test/mathc/rintf.c b/test/c/mathc/rintf.c similarity index 100% rename from test/mathc/rintf.c rename to test/c/mathc/rintf.c diff --git a/test/mathc/rintf_fail.c b/test/c/mathc/rintf_fail.c similarity index 100% rename from test/mathc/rintf_fail.c rename to test/c/mathc/rintf_fail.c diff --git a/test/mathc/rintl.c b/test/c/mathc/rintl.c similarity index 100% rename from test/mathc/rintl.c rename to test/c/mathc/rintl.c diff --git a/test/mathc/rintl_fail.c b/test/c/mathc/rintl_fail.c similarity index 100% rename from test/mathc/rintl_fail.c rename to test/c/mathc/rintl_fail.c diff --git a/test/mathc/round.c b/test/c/mathc/round.c similarity index 100% rename from test/mathc/round.c rename to test/c/mathc/round.c diff --git a/test/mathc/round_fail.c b/test/c/mathc/round_fail.c similarity index 100% rename from test/mathc/round_fail.c rename to test/c/mathc/round_fail.c diff --git a/test/mathc/roundf.c b/test/c/mathc/roundf.c similarity index 100% rename from test/mathc/roundf.c rename to test/c/mathc/roundf.c diff --git a/test/mathc/roundf_fail.c b/test/c/mathc/roundf_fail.c similarity index 100% rename from test/mathc/roundf_fail.c rename to test/c/mathc/roundf_fail.c diff --git a/test/mathc/roundl.c b/test/c/mathc/roundl.c similarity index 100% rename from test/mathc/roundl.c rename to test/c/mathc/roundl.c diff --git a/test/mathc/roundl_fail.c b/test/c/mathc/roundl_fail.c similarity index 100% rename from test/mathc/roundl_fail.c rename to test/c/mathc/roundl_fail.c diff --git a/test/mathc/sqrt.c b/test/c/mathc/sqrt.c similarity index 100% rename from test/mathc/sqrt.c rename to test/c/mathc/sqrt.c diff --git a/test/mathc/sqrt_fail.c b/test/c/mathc/sqrt_fail.c similarity index 100% rename from test/mathc/sqrt_fail.c rename to test/c/mathc/sqrt_fail.c diff --git a/test/mathc/sqrtf.c b/test/c/mathc/sqrtf.c similarity index 100% rename from test/mathc/sqrtf.c rename to test/c/mathc/sqrtf.c diff --git a/test/mathc/sqrtf_fail.c b/test/c/mathc/sqrtf_fail.c similarity index 100% rename from test/mathc/sqrtf_fail.c rename to test/c/mathc/sqrtf_fail.c diff --git a/test/mathc/sqrtl.c b/test/c/mathc/sqrtl.c similarity index 100% rename from test/mathc/sqrtl.c rename to test/c/mathc/sqrtl.c diff --git a/test/mathc/sqrtl_fail.c b/test/c/mathc/sqrtl_fail.c similarity index 100% rename from test/mathc/sqrtl_fail.c rename to test/c/mathc/sqrtl_fail.c diff --git a/test/mathc/trunc.c b/test/c/mathc/trunc.c similarity index 100% rename from test/mathc/trunc.c rename to test/c/mathc/trunc.c diff --git a/test/mathc/trunc_fail.c b/test/c/mathc/trunc_fail.c similarity index 100% rename from test/mathc/trunc_fail.c rename to test/c/mathc/trunc_fail.c diff --git a/test/mathc/truncf.c b/test/c/mathc/truncf.c similarity index 100% rename from test/mathc/truncf.c rename to test/c/mathc/truncf.c diff --git a/test/mathc/truncf_fail.c b/test/c/mathc/truncf_fail.c similarity index 100% rename from test/mathc/truncf_fail.c rename to test/c/mathc/truncf_fail.c diff --git a/test/mathc/truncl.c b/test/c/mathc/truncl.c similarity index 100% rename from test/mathc/truncl.c rename to test/c/mathc/truncl.c diff --git a/test/mathc/truncl_fail.c b/test/c/mathc/truncl_fail.c similarity index 100% rename from test/mathc/truncl_fail.c rename to test/c/mathc/truncl_fail.c diff --git a/test/memory-safety/adjacent.c b/test/c/memory-safety/adjacent.c similarity index 100% rename from test/memory-safety/adjacent.c rename to test/c/memory-safety/adjacent.c diff --git a/test/memory-safety/adjacent_fail.c b/test/c/memory-safety/adjacent_fail.c similarity index 100% rename from test/memory-safety/adjacent_fail.c rename to test/c/memory-safety/adjacent_fail.c diff --git a/test/memory-safety/adjacent_global.c b/test/c/memory-safety/adjacent_global.c similarity index 100% rename from test/memory-safety/adjacent_global.c rename to test/c/memory-safety/adjacent_global.c diff --git a/test/memory-safety/adjacent_global_fail.c b/test/c/memory-safety/adjacent_global_fail.c similarity index 100% rename from test/memory-safety/adjacent_global_fail.c rename to test/c/memory-safety/adjacent_global_fail.c diff --git a/test/memory-safety/array1.c b/test/c/memory-safety/array1.c similarity index 100% rename from test/memory-safety/array1.c rename to test/c/memory-safety/array1.c diff --git a/test/memory-safety/array1_fail.c b/test/c/memory-safety/array1_fail.c similarity index 100% rename from test/memory-safety/array1_fail.c rename to test/c/memory-safety/array1_fail.c diff --git a/test/memory-safety/array2_free_fail.c b/test/c/memory-safety/array2_free_fail.c similarity index 100% rename from test/memory-safety/array2_free_fail.c rename to test/c/memory-safety/array2_free_fail.c diff --git a/test/memory-safety/array_free1.c b/test/c/memory-safety/array_free1.c similarity index 100% rename from test/memory-safety/array_free1.c rename to test/c/memory-safety/array_free1.c diff --git a/test/memory-safety/array_free1_fail.c b/test/c/memory-safety/array_free1_fail.c similarity index 100% rename from test/memory-safety/array_free1_fail.c rename to test/c/memory-safety/array_free1_fail.c diff --git a/test/memory-safety/array_free1_fail1.c b/test/c/memory-safety/array_free1_fail1.c similarity index 100% rename from test/memory-safety/array_free1_fail1.c rename to test/c/memory-safety/array_free1_fail1.c diff --git a/test/c/memory-safety/calloc.c b/test/c/memory-safety/calloc.c new file mode 100644 index 000000000..1758e4f08 --- /dev/null +++ b/test/c/memory-safety/calloc.c @@ -0,0 +1,15 @@ +#include "smack.h" +#include + +// @expect verified + +int main(void) { + int *arr = (int *)calloc(5, sizeof(int)); + int idx = __VERIFIER_nondet_int(); + assume(0 <= idx && idx < 5); + if (arr == NULL) + return 1; + assert(arr[idx] == 0); + free(arr); + return 0; +} diff --git a/test/c/memory-safety/calloc_fail.c b/test/c/memory-safety/calloc_fail.c new file mode 100644 index 000000000..e6cdb6176 --- /dev/null +++ b/test/c/memory-safety/calloc_fail.c @@ -0,0 +1,14 @@ +#include "smack.h" +#include + +// @expect error + +int main(void) { + int *arr = (int *)calloc(5, sizeof(int)); + int idx = __VERIFIER_nondet_int(); + if (arr == NULL) + return 1; + assert(arr[idx] == 0); + free(arr); + return 0; +} diff --git a/test/memory-safety/config.yml b/test/c/memory-safety/config.yml similarity index 100% rename from test/memory-safety/config.yml rename to test/c/memory-safety/config.yml diff --git a/test/memory-safety/errno_test.c b/test/c/memory-safety/errno_test.c similarity index 100% rename from test/memory-safety/errno_test.c rename to test/c/memory-safety/errno_test.c diff --git a/test/memory-safety/global_alloc.c b/test/c/memory-safety/global_alloc.c similarity index 100% rename from test/memory-safety/global_alloc.c rename to test/c/memory-safety/global_alloc.c diff --git a/test/memory-safety/global_alloc_fail_free.c b/test/c/memory-safety/global_alloc_fail_free.c similarity index 100% rename from test/memory-safety/global_alloc_fail_free.c rename to test/c/memory-safety/global_alloc_fail_free.c diff --git a/test/memory-safety/global_and_heap_alloc.c b/test/c/memory-safety/global_and_heap_alloc.c similarity index 100% rename from test/memory-safety/global_and_heap_alloc.c rename to test/c/memory-safety/global_and_heap_alloc.c diff --git a/test/memory-safety/global_and_heap_alloc_fail_free.c b/test/c/memory-safety/global_and_heap_alloc_fail_free.c similarity index 100% rename from test/memory-safety/global_and_heap_alloc_fail_free.c rename to test/c/memory-safety/global_and_heap_alloc_fail_free.c diff --git a/test/memory-safety/global_and_heap_alloc_fail_no_free.c b/test/c/memory-safety/global_and_heap_alloc_fail_no_free.c similarity index 53% rename from test/memory-safety/global_and_heap_alloc_fail_no_free.c rename to test/c/memory-safety/global_and_heap_alloc_fail_no_free.c index a4b18b6b0..6de2153d6 100644 --- a/test/memory-safety/global_and_heap_alloc_fail_no_free.c +++ b/test/c/memory-safety/global_and_heap_alloc_fail_no_free.c @@ -9,5 +9,7 @@ int main(void) { int *a = &x; int *b = malloc(4); int c = a[0]; - return c; + // We have to use `b` otherwise the call to malloc will be removed. + // This is only observable after sea-dsa is used. + return *b; } diff --git a/test/memory-safety/malloc_nondet.c b/test/c/memory-safety/malloc_nondet.c similarity index 100% rename from test/memory-safety/malloc_nondet.c rename to test/c/memory-safety/malloc_nondet.c diff --git a/test/memory-safety/malloc_nondet_fail.c b/test/c/memory-safety/malloc_nondet_fail.c similarity index 100% rename from test/memory-safety/malloc_nondet_fail.c rename to test/c/memory-safety/malloc_nondet_fail.c diff --git a/test/memory-safety/simple_double_free.c b/test/c/memory-safety/simple_double_free.c similarity index 100% rename from test/memory-safety/simple_double_free.c rename to test/c/memory-safety/simple_double_free.c diff --git a/test/memory-safety/structure1.c b/test/c/memory-safety/structure1.c similarity index 100% rename from test/memory-safety/structure1.c rename to test/c/memory-safety/structure1.c diff --git a/test/memory-safety/structure1_fail.c b/test/c/memory-safety/structure1_fail.c similarity index 100% rename from test/memory-safety/structure1_fail.c rename to test/c/memory-safety/structure1_fail.c diff --git a/test/ntdrivers-simplified/cdaudio_simpl1_false.cil.c b/test/c/ntdrivers-simplified/cdaudio_simpl1_false.cil.c similarity index 100% rename from test/ntdrivers-simplified/cdaudio_simpl1_false.cil.c rename to test/c/ntdrivers-simplified/cdaudio_simpl1_false.cil.c diff --git a/test/ntdrivers-simplified/cdaudio_simpl1_true.cil.c b/test/c/ntdrivers-simplified/cdaudio_simpl1_true.cil.c similarity index 100% rename from test/ntdrivers-simplified/cdaudio_simpl1_true.cil.c rename to test/c/ntdrivers-simplified/cdaudio_simpl1_true.cil.c diff --git a/test/ntdrivers-simplified/config.yml b/test/c/ntdrivers-simplified/config.yml similarity index 100% rename from test/ntdrivers-simplified/config.yml rename to test/c/ntdrivers-simplified/config.yml diff --git a/test/ntdrivers-simplified/diskperf_simpl1_true.cil.c b/test/c/ntdrivers-simplified/diskperf_simpl1_true.cil.c similarity index 100% rename from test/ntdrivers-simplified/diskperf_simpl1_true.cil.c rename to test/c/ntdrivers-simplified/diskperf_simpl1_true.cil.c diff --git a/test/ntdrivers-simplified/floppy_simpl3_false.cil.c b/test/c/ntdrivers-simplified/floppy_simpl3_false.cil.c similarity index 100% rename from test/ntdrivers-simplified/floppy_simpl3_false.cil.c rename to test/c/ntdrivers-simplified/floppy_simpl3_false.cil.c diff --git a/test/ntdrivers-simplified/floppy_simpl3_true.cil.c b/test/c/ntdrivers-simplified/floppy_simpl3_true.cil.c similarity index 100% rename from test/ntdrivers-simplified/floppy_simpl3_true.cil.c rename to test/c/ntdrivers-simplified/floppy_simpl3_true.cil.c diff --git a/test/ntdrivers-simplified/floppy_simpl4_false.cil.c b/test/c/ntdrivers-simplified/floppy_simpl4_false.cil.c similarity index 100% rename from test/ntdrivers-simplified/floppy_simpl4_false.cil.c rename to test/c/ntdrivers-simplified/floppy_simpl4_false.cil.c diff --git a/test/ntdrivers-simplified/floppy_simpl4_true.cil.c b/test/c/ntdrivers-simplified/floppy_simpl4_true.cil.c similarity index 100% rename from test/ntdrivers-simplified/floppy_simpl4_true.cil.c rename to test/c/ntdrivers-simplified/floppy_simpl4_true.cil.c diff --git a/test/ntdrivers-simplified/kbfiltr_simpl1_true.cil.c b/test/c/ntdrivers-simplified/kbfiltr_simpl1_true.cil.c similarity index 100% rename from test/ntdrivers-simplified/kbfiltr_simpl1_true.cil.c rename to test/c/ntdrivers-simplified/kbfiltr_simpl1_true.cil.c diff --git a/test/ntdrivers-simplified/kbfiltr_simpl2_false.cil.c b/test/c/ntdrivers-simplified/kbfiltr_simpl2_false.cil.c similarity index 100% rename from test/ntdrivers-simplified/kbfiltr_simpl2_false.cil.c rename to test/c/ntdrivers-simplified/kbfiltr_simpl2_false.cil.c diff --git a/test/ntdrivers-simplified/kbfiltr_simpl2_true.cil.c b/test/c/ntdrivers-simplified/kbfiltr_simpl2_true.cil.c similarity index 100% rename from test/ntdrivers-simplified/kbfiltr_simpl2_true.cil.c rename to test/c/ntdrivers-simplified/kbfiltr_simpl2_true.cil.c diff --git a/test/ntdrivers/cdaudio_true.i.cil.c b/test/c/ntdrivers/cdaudio_true.i.cil.c similarity index 100% rename from test/ntdrivers/cdaudio_true.i.cil.c rename to test/c/ntdrivers/cdaudio_true.i.cil.c diff --git a/test/ntdrivers/config.yml b/test/c/ntdrivers/config.yml similarity index 100% rename from test/ntdrivers/config.yml rename to test/c/ntdrivers/config.yml diff --git a/test/ntdrivers/diskperf_false.i.cil.c b/test/c/ntdrivers/diskperf_false.i.cil.c similarity index 100% rename from test/ntdrivers/diskperf_false.i.cil.c rename to test/c/ntdrivers/diskperf_false.i.cil.c diff --git a/test/ntdrivers/diskperf_true.i.cil.c b/test/c/ntdrivers/diskperf_true.i.cil.c similarity index 100% rename from test/ntdrivers/diskperf_true.i.cil.c rename to test/c/ntdrivers/diskperf_true.i.cil.c diff --git a/test/ntdrivers/floppy2_true.i.cil.c b/test/c/ntdrivers/floppy2_true.i.cil.c similarity index 99% rename from test/ntdrivers/floppy2_true.i.cil.c rename to test/c/ntdrivers/floppy2_true.i.cil.c index a9268e30a..0c4227cf3 100644 --- a/test/ntdrivers/floppy2_true.i.cil.c +++ b/test/c/ntdrivers/floppy2_true.i.cil.c @@ -6803,7 +6803,7 @@ NTSTATUS FlQueueIrpToThread(PIRP Irp, PDISKETTE_EXTENSION DisketteExtension) { } else { { #line 949 - __VERIFIER_assert(0); + assert(0); } } { @@ -7643,7 +7643,7 @@ NTSTATUS FloppyDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) { } else { { #line 1098 - __VERIFIER_assert(0); + assert(0); } } { @@ -9322,7 +9322,7 @@ NTSTATUS FloppyDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) { } else { { #line 1534 - __VERIFIER_assert(0); + assert(0); } } { @@ -10108,7 +10108,7 @@ NTSTATUS FloppyPnp(PDEVICE_OBJECT DeviceObject, PIRP Irp) { } else { { #line 1627 - __VERIFIER_assert(0); + assert(0); } } { @@ -10291,7 +10291,7 @@ NTSTATUS FloppyPnp(PDEVICE_OBJECT DeviceObject, PIRP Irp) { } else { { #line 1673 - __VERIFIER_assert(0); + assert(0); } } { @@ -10446,7 +10446,7 @@ NTSTATUS FloppyPnp(PDEVICE_OBJECT DeviceObject, PIRP Irp) { } else { { #line 1707 - __VERIFIER_assert(0); + assert(0); } } { @@ -10593,14 +10593,14 @@ NTSTATUS FloppyPnp(PDEVICE_OBJECT DeviceObject, PIRP Irp) { if (s != 1) { { #line 1733 - __VERIFIER_assert(0); + assert(0); } } else { #line 1735 if (compRegistered != 0) { { #line 1735 - __VERIFIER_assert(0); + assert(0); } } else { #line 1737 @@ -10819,7 +10819,7 @@ NTSTATUS FloppyPnp(PDEVICE_OBJECT DeviceObject, PIRP Irp) { } else { { #line 1785 - __VERIFIER_assert(0); + assert(0); } } { @@ -10943,7 +10943,7 @@ NTSTATUS FloppyPnp(PDEVICE_OBJECT DeviceObject, PIRP Irp) { } else { { #line 1820 - __VERIFIER_assert(0); + assert(0); } } { @@ -11186,7 +11186,7 @@ NTSTATUS FloppyPnp(PDEVICE_OBJECT DeviceObject, PIRP Irp) { } else { { #line 1863 - __VERIFIER_assert(0); + assert(0); } } { @@ -11645,14 +11645,14 @@ NTSTATUS FloppyStartDevice(PDEVICE_OBJECT DeviceObject, PIRP Irp) { if (s != 1) { { #line 1904 - __VERIFIER_assert(0); + assert(0); } } else { #line 1906 if (compRegistered != 0) { { #line 1906 - __VERIFIER_assert(0); + assert(0); } } else { #line 1908 @@ -12679,7 +12679,7 @@ NTSTATUS FloppyPower(PDEVICE_OBJECT DeviceObject, PIRP Irp) { } else { { #line 2169 - __VERIFIER_assert(0); + assert(0); } } { @@ -29275,7 +29275,7 @@ NTSTATUS FloppyQueueRequest(PDISKETTE_EXTENSION DisketteExtension, PIRP Irp) { } else { { #line 6362 - __VERIFIER_assert(0); + assert(0); } } { @@ -30224,7 +30224,7 @@ int main(void) { if (status == 259L) { { #line 207 - __VERIFIER_assert(0); + assert(0); } } else { } @@ -30236,7 +30236,7 @@ int main(void) { if (status != __cil_tmp23) { { #line 209 - __VERIFIER_assert(0); + assert(0); } } else { } @@ -30870,7 +30870,7 @@ void stubMoreProcessingRequired(void) { } else { { #line 599 - __VERIFIER_assert(0); + assert(0); } } #line 602 @@ -30977,7 +30977,7 @@ NTSTATUS(__attribute__((__fastcall__)) IofCallDriver) } else { { #line 640 - __VERIFIER_assert(0); + assert(0); } } } @@ -31001,7 +31001,7 @@ void(__attribute__((__fastcall__)) IofCompleteRequest)(PIRP Irp, } else { { #line 674 - __VERIFIER_assert(0); + assert(0); } } #line 677 @@ -31153,7 +31153,7 @@ NTSTATUS KeWaitForSingleObject(PVOID Object, KWAIT_REASON WaitReason, if (s == 6) { { #line 794 - __VERIFIER_assert(0); + assert(0); } } else { } diff --git a/test/ntdrivers/kbfiltr_false.i.cil.c b/test/c/ntdrivers/kbfiltr_false.i.cil.c similarity index 100% rename from test/ntdrivers/kbfiltr_false.i.cil.c rename to test/c/ntdrivers/kbfiltr_false.i.cil.c diff --git a/test/pthread/account.c b/test/c/pthread/account.c similarity index 100% rename from test/pthread/account.c rename to test/c/pthread/account.c diff --git a/test/pthread/account_fail.c b/test/c/pthread/account_fail.c similarity index 100% rename from test/pthread/account_fail.c rename to test/c/pthread/account_fail.c diff --git a/test/pthread/cond.c b/test/c/pthread/cond.c similarity index 100% rename from test/pthread/cond.c rename to test/c/pthread/cond.c diff --git a/test/pthread/cond_fail.c b/test/c/pthread/cond_fail.c similarity index 100% rename from test/pthread/cond_fail.c rename to test/c/pthread/cond_fail.c diff --git a/test/pthread/config.yml b/test/c/pthread/config.yml similarity index 100% rename from test/pthread/config.yml rename to test/c/pthread/config.yml diff --git a/test/pthread/equal.c b/test/c/pthread/equal.c similarity index 100% rename from test/pthread/equal.c rename to test/c/pthread/equal.c diff --git a/test/pthread/equal2.c b/test/c/pthread/equal2.c similarity index 100% rename from test/pthread/equal2.c rename to test/c/pthread/equal2.c diff --git a/test/pthread/join.c b/test/c/pthread/join.c similarity index 100% rename from test/pthread/join.c rename to test/c/pthread/join.c diff --git a/test/pthread/join_fail.c b/test/c/pthread/join_fail.c similarity index 100% rename from test/pthread/join_fail.c rename to test/c/pthread/join_fail.c diff --git a/test/c/pthread/join_null_retval.c b/test/c/pthread/join_null_retval.c new file mode 100644 index 000000000..950e76a52 --- /dev/null +++ b/test/c/pthread/join_null_retval.c @@ -0,0 +1,15 @@ +#include "smack.h" +#include +#include + +// @expect verified +// @flag --memory-safety + +void *t1(void *arg) { return NULL; } + +int main(void) { + pthread_t tid1; + pthread_create(&tid1, 0, t1, 0); + pthread_join(tid1, NULL); + return 0; +} diff --git a/test/pthread/join_return2.c b/test/c/pthread/join_return2.c similarity index 100% rename from test/pthread/join_return2.c rename to test/c/pthread/join_return2.c diff --git a/test/pthread/join_return2_fail.c b/test/c/pthread/join_return2_fail.c similarity index 100% rename from test/pthread/join_return2_fail.c rename to test/c/pthread/join_return2_fail.c diff --git a/test/pthread/join_self.c b/test/c/pthread/join_self.c similarity index 100% rename from test/pthread/join_self.c rename to test/c/pthread/join_self.c diff --git a/test/pthread/join_self_fail.c b/test/c/pthread/join_self_fail.c similarity index 100% rename from test/pthread/join_self_fail.c rename to test/c/pthread/join_self_fail.c diff --git a/test/pthread/lock.c b/test/c/pthread/lock.c similarity index 100% rename from test/pthread/lock.c rename to test/c/pthread/lock.c diff --git a/test/pthread/lock2.c b/test/c/pthread/lock2.c similarity index 100% rename from test/pthread/lock2.c rename to test/c/pthread/lock2.c diff --git a/test/pthread/lock2_fail.c b/test/c/pthread/lock2_fail.c similarity index 100% rename from test/pthread/lock2_fail.c rename to test/c/pthread/lock2_fail.c diff --git a/test/pthread/lock3.c b/test/c/pthread/lock3.c similarity index 100% rename from test/pthread/lock3.c rename to test/c/pthread/lock3.c diff --git a/test/pthread/lock3_fail.c b/test/c/pthread/lock3_fail.c similarity index 100% rename from test/pthread/lock3_fail.c rename to test/c/pthread/lock3_fail.c diff --git a/test/pthread/lock4.c b/test/c/pthread/lock4.c similarity index 100% rename from test/pthread/lock4.c rename to test/c/pthread/lock4.c diff --git a/test/pthread/lock4_fail.c b/test/c/pthread/lock4_fail.c similarity index 100% rename from test/pthread/lock4_fail.c rename to test/c/pthread/lock4_fail.c diff --git a/test/pthread/lock5.c b/test/c/pthread/lock5.c similarity index 100% rename from test/pthread/lock5.c rename to test/c/pthread/lock5.c diff --git a/test/pthread/lock5_fail.c b/test/c/pthread/lock5_fail.c similarity index 100% rename from test/pthread/lock5_fail.c rename to test/c/pthread/lock5_fail.c diff --git a/test/pthread/lock_fail.c b/test/c/pthread/lock_fail.c similarity index 100% rename from test/pthread/lock_fail.c rename to test/c/pthread/lock_fail.c diff --git a/test/pthread/lockattr.c b/test/c/pthread/lockattr.c similarity index 100% rename from test/pthread/lockattr.c rename to test/c/pthread/lockattr.c diff --git a/test/c/pthread/regression_525_stackalloc.c b/test/c/pthread/regression_525_stackalloc.c new file mode 100644 index 000000000..309bf4f5c --- /dev/null +++ b/test/c/pthread/regression_525_stackalloc.c @@ -0,0 +1,47 @@ +#include "smack.h" +#include +#include +#include + +// @expect verified +// https://github.com/smackers/smack/issues/525 + +struct atomic_var { + void *value; +}; + +struct S { + struct atomic_var x; + struct atomic_var y; +}; + +struct T { + int z; +}; + +void atomic_store_ptr(struct atomic_var *var, void *p) { + __atomic_store_n(&var->value, p, __ATOMIC_SEQ_CST); +} + +void *foo(void *arg) { + struct T t = *(struct T *)arg; + return NULL; +} + +int main(void) { + struct S s; + struct T t; + pthread_t thread; + + (*(size_t *)(&s.x.value)) = 0; + s.y.value = NULL; + + if (pthread_create(&thread, NULL, foo, (void *)&t)) + return 1; + + uint64_t v = (size_t)s.x.value; + atomic_store_ptr(&s.y, NULL); + uint64_t w = (size_t)s.x.value; + assert(v == w); + return 0; +} diff --git a/test/pthread_extras/config.yml b/test/c/pthread_extras/config.yml similarity index 100% rename from test/pthread_extras/config.yml rename to test/c/pthread_extras/config.yml diff --git a/test/pthread_extras/dekker_true-unreach-call.c b/test/c/pthread_extras/dekker_true-unreach-call.c similarity index 100% rename from test/pthread_extras/dekker_true-unreach-call.c rename to test/c/pthread_extras/dekker_true-unreach-call.c diff --git a/test/pthread_extras/lamport_true-unreach-call.c b/test/c/pthread_extras/lamport_true-unreach-call.c similarity index 100% rename from test/pthread_extras/lamport_true-unreach-call.c rename to test/c/pthread_extras/lamport_true-unreach-call.c diff --git a/test/pthread_extras/lazy01_false-unreach-call.c b/test/c/pthread_extras/lazy01_false-unreach-call.c similarity index 100% rename from test/pthread_extras/lazy01_false-unreach-call.c rename to test/c/pthread_extras/lazy01_false-unreach-call.c diff --git a/test/pthread_extras/peterson_true-unreach-call.c b/test/c/pthread_extras/peterson_true-unreach-call.c similarity index 100% rename from test/pthread_extras/peterson_true-unreach-call.c rename to test/c/pthread_extras/peterson_true-unreach-call.c diff --git a/test/pthread_extras/queue_false-unreach-call.c b/test/c/pthread_extras/queue_false-unreach-call.c similarity index 100% rename from test/pthread_extras/queue_false-unreach-call.c rename to test/c/pthread_extras/queue_false-unreach-call.c diff --git a/test/pthread_extras/queue_ok_true-unreach-call.c b/test/c/pthread_extras/queue_ok_true-unreach-call.c similarity index 100% rename from test/pthread_extras/queue_ok_true-unreach-call.c rename to test/c/pthread_extras/queue_ok_true-unreach-call.c diff --git a/test/pthread_extras/reorder_2_false-unreach-call.c b/test/c/pthread_extras/reorder_2_false-unreach-call.c similarity index 100% rename from test/pthread_extras/reorder_2_false-unreach-call.c rename to test/c/pthread_extras/reorder_2_false-unreach-call.c diff --git a/test/pthread_extras/reorder_5_false-unreach-call.c b/test/c/pthread_extras/reorder_5_false-unreach-call.c similarity index 100% rename from test/pthread_extras/reorder_5_false-unreach-call.c rename to test/c/pthread_extras/reorder_5_false-unreach-call.c diff --git a/test/pthread_extras/scull_true-unreach-call.c b/test/c/pthread_extras/scull_true-unreach-call.c similarity index 100% rename from test/pthread_extras/scull_true-unreach-call.c rename to test/c/pthread_extras/scull_true-unreach-call.c diff --git a/test/pthread_extras/sigma_false-unreach-call.c b/test/c/pthread_extras/sigma_false-unreach-call.c similarity index 100% rename from test/pthread_extras/sigma_false-unreach-call.c rename to test/c/pthread_extras/sigma_false-unreach-call.c diff --git a/test/pthread_extras/sigma_false_GREAT-unreach-call.c b/test/c/pthread_extras/sigma_false_GREAT-unreach-call.c similarity index 100% rename from test/pthread_extras/sigma_false_GREAT-unreach-call.c rename to test/c/pthread_extras/sigma_false_GREAT-unreach-call.c diff --git a/test/pthread_extras/singleton_false-unreach-call.c b/test/c/pthread_extras/singleton_false-unreach-call.c similarity index 100% rename from test/pthread_extras/singleton_false-unreach-call.c rename to test/c/pthread_extras/singleton_false-unreach-call.c diff --git a/test/pthread_extras/singleton_with-uninit-problems-true.c b/test/c/pthread_extras/singleton_with-uninit-problems-true.c similarity index 100% rename from test/pthread_extras/singleton_with-uninit-problems-true.c rename to test/c/pthread_extras/singleton_with-uninit-problems-true.c diff --git a/test/pthread_extras/sssc12_true-unreach-call.c b/test/c/pthread_extras/sssc12_true-unreach-call.c similarity index 100% rename from test/pthread_extras/sssc12_true-unreach-call.c rename to test/c/pthread_extras/sssc12_true-unreach-call.c diff --git a/test/pthread_extras/stack_false-unreach-call.c b/test/c/pthread_extras/stack_false-unreach-call.c similarity index 100% rename from test/pthread_extras/stack_false-unreach-call.c rename to test/c/pthread_extras/stack_false-unreach-call.c diff --git a/test/pthread_extras/stack_true-unreach-call.c b/test/c/pthread_extras/stack_true-unreach-call.c similarity index 100% rename from test/pthread_extras/stack_true-unreach-call.c rename to test/c/pthread_extras/stack_true-unreach-call.c diff --git a/test/pthread_extras/stateful01_false-unreach-call.c b/test/c/pthread_extras/stateful01_false-unreach-call.c similarity index 100% rename from test/pthread_extras/stateful01_false-unreach-call.c rename to test/c/pthread_extras/stateful01_false-unreach-call.c diff --git a/test/pthread_extras/stateful01_true-unreach-call.c b/test/c/pthread_extras/stateful01_true-unreach-call.c similarity index 100% rename from test/pthread_extras/stateful01_true-unreach-call.c rename to test/c/pthread_extras/stateful01_true-unreach-call.c diff --git a/test/pthread_extras/sync01_true-unreach-call.c b/test/c/pthread_extras/sync01_true-unreach-call.c similarity index 100% rename from test/pthread_extras/sync01_true-unreach-call.c rename to test/c/pthread_extras/sync01_true-unreach-call.c diff --git a/test/pthread_extras/szymanski_true-unreach-call.c b/test/c/pthread_extras/szymanski_true-unreach-call.c similarity index 100% rename from test/pthread_extras/szymanski_true-unreach-call.c rename to test/c/pthread_extras/szymanski_true-unreach-call.c diff --git a/test/pthread_extras/time_var_mutex_true-unreach-call.c b/test/c/pthread_extras/time_var_mutex_true-unreach-call.c similarity index 100% rename from test/pthread_extras/time_var_mutex_true-unreach-call.c rename to test/c/pthread_extras/time_var_mutex_true-unreach-call.c diff --git a/test/pthread_extras/twostage_3_false-unreach-call.c b/test/c/pthread_extras/twostage_3_false-unreach-call.c similarity index 100% rename from test/pthread_extras/twostage_3_false-unreach-call.c rename to test/c/pthread_extras/twostage_3_false-unreach-call.c diff --git a/test/reach/.gitignore b/test/c/reach/.gitignore similarity index 100% rename from test/reach/.gitignore rename to test/c/reach/.gitignore diff --git a/test/reach/break.c b/test/c/reach/break.c similarity index 100% rename from test/reach/break.c rename to test/c/reach/break.c diff --git a/test/reach/break.expected b/test/c/reach/break.expected similarity index 100% rename from test/reach/break.expected rename to test/c/reach/break.expected diff --git a/test/reach/config.yml b/test/c/reach/config.yml similarity index 100% rename from test/reach/config.yml rename to test/c/reach/config.yml diff --git a/test/reach/continue.c b/test/c/reach/continue.c similarity index 100% rename from test/reach/continue.c rename to test/c/reach/continue.c diff --git a/test/reach/continue.expected b/test/c/reach/continue.expected similarity index 100% rename from test/reach/continue.expected rename to test/c/reach/continue.expected diff --git a/test/reach/do.c b/test/c/reach/do.c similarity index 100% rename from test/reach/do.c rename to test/c/reach/do.c diff --git a/test/reach/do.expected b/test/c/reach/do.expected similarity index 100% rename from test/reach/do.expected rename to test/c/reach/do.expected diff --git a/test/reach/for.c b/test/c/reach/for.c similarity index 100% rename from test/reach/for.c rename to test/c/reach/for.c diff --git a/test/reach/for.expected b/test/c/reach/for.expected similarity index 100% rename from test/reach/for.expected rename to test/c/reach/for.expected diff --git a/test/reach/for2.c b/test/c/reach/for2.c similarity index 100% rename from test/reach/for2.c rename to test/c/reach/for2.c diff --git a/test/reach/for2.expected b/test/c/reach/for2.expected similarity index 100% rename from test/reach/for2.expected rename to test/c/reach/for2.expected diff --git a/test/reach/func.c b/test/c/reach/func.c similarity index 100% rename from test/reach/func.c rename to test/c/reach/func.c diff --git a/test/reach/func.expected b/test/c/reach/func.expected similarity index 100% rename from test/reach/func.expected rename to test/c/reach/func.expected diff --git a/test/reach/func2.c b/test/c/reach/func2.c similarity index 100% rename from test/reach/func2.c rename to test/c/reach/func2.c diff --git a/test/reach/func2.expected b/test/c/reach/func2.expected similarity index 100% rename from test/reach/func2.expected rename to test/c/reach/func2.expected diff --git a/test/reach/func3.c b/test/c/reach/func3.c similarity index 100% rename from test/reach/func3.c rename to test/c/reach/func3.c diff --git a/test/reach/func3.expected b/test/c/reach/func3.expected similarity index 100% rename from test/reach/func3.expected rename to test/c/reach/func3.expected diff --git a/test/reach/if.c b/test/c/reach/if.c similarity index 100% rename from test/reach/if.c rename to test/c/reach/if.c diff --git a/test/reach/if.expected b/test/c/reach/if.expected similarity index 100% rename from test/reach/if.expected rename to test/c/reach/if.expected diff --git a/test/reach/if2.c b/test/c/reach/if2.c similarity index 100% rename from test/reach/if2.c rename to test/c/reach/if2.c diff --git a/test/reach/if2.expected b/test/c/reach/if2.expected similarity index 100% rename from test/reach/if2.expected rename to test/c/reach/if2.expected diff --git a/test/reach/if3.c b/test/c/reach/if3.c similarity index 100% rename from test/reach/if3.c rename to test/c/reach/if3.c diff --git a/test/reach/if3.expected b/test/c/reach/if3.expected similarity index 100% rename from test/reach/if3.expected rename to test/c/reach/if3.expected diff --git a/test/reach/if4.c b/test/c/reach/if4.c similarity index 100% rename from test/reach/if4.c rename to test/c/reach/if4.c diff --git a/test/reach/if4.expected b/test/c/reach/if4.expected similarity index 100% rename from test/reach/if4.expected rename to test/c/reach/if4.expected diff --git a/test/reach/libs.c b/test/c/reach/libs.c similarity index 100% rename from test/reach/libs.c rename to test/c/reach/libs.c diff --git a/test/reach/libs.expected b/test/c/reach/libs.expected similarity index 100% rename from test/reach/libs.expected rename to test/c/reach/libs.expected diff --git a/test/reach/regtest.py b/test/c/reach/regtest.py similarity index 83% rename from test/reach/regtest.py rename to test/c/reach/regtest.py index fb516f534..d1b8ab39d 100755 --- a/test/reach/regtest.py +++ b/test/c/reach/regtest.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python2 +#! /usr/bin/env python3 import subprocess import re @@ -46,7 +46,7 @@ def runtests(): expected = ansFile.read() ansFile.close() - print "{0:>20} {1:>16}:".format(test[0], "(" + verifier + ")"), + print("{0:>20} {1:>16}:".format(test[0], "(" + verifier + ")")) # invoke smack-reach t0 = time.time() @@ -54,17 +54,17 @@ def runtests(): '--verifier=' + verifier, '--unroll=' + str(test[1]), '-o', test[0] +'.bpl', '--smackd'], - stdout=subprocess.PIPE) + stdout=subprocess.PIPE, universal_newlines=True) smackOutput = p.communicate()[0] elapsed = time.time() - t0 # check SMACK output if(json.loads(expected) == json.loads(smackOutput)): - print green('PASSED') + ' [%.2fs]' % round(elapsed, 2) + print(green('PASSED') + ' [%.2fs]' % round(elapsed, 2)) passed += 1 else: - print red('FAILED') + print(red('FAILED')) failed += 1 return passed, failed @@ -73,5 +73,5 @@ def runtests(): passed, failed = runtests() - print '\nPASSED count: ', passed - print 'FAILED count: ', failed + print('\nPASSED count: ', passed) + print('FAILED count: ', failed) diff --git a/test/reach/return.c b/test/c/reach/return.c similarity index 100% rename from test/reach/return.c rename to test/c/reach/return.c diff --git a/test/reach/return.expected b/test/c/reach/return.expected similarity index 100% rename from test/reach/return.expected rename to test/c/reach/return.expected diff --git a/test/reach/switch.c b/test/c/reach/switch.c similarity index 100% rename from test/reach/switch.c rename to test/c/reach/switch.c diff --git a/test/reach/switch.expected b/test/c/reach/switch.expected similarity index 100% rename from test/reach/switch.expected rename to test/c/reach/switch.expected diff --git a/test/reach/switch2.c b/test/c/reach/switch2.c similarity index 100% rename from test/reach/switch2.c rename to test/c/reach/switch2.c diff --git a/test/reach/switch2.expected b/test/c/reach/switch2.expected similarity index 100% rename from test/reach/switch2.expected rename to test/c/reach/switch2.expected diff --git a/test/reach/switch3.c b/test/c/reach/switch3.c similarity index 100% rename from test/reach/switch3.c rename to test/c/reach/switch3.c diff --git a/test/reach/switch3.expected b/test/c/reach/switch3.expected similarity index 100% rename from test/reach/switch3.expected rename to test/c/reach/switch3.expected diff --git a/test/reach/switch4.c b/test/c/reach/switch4.c similarity index 100% rename from test/reach/switch4.c rename to test/c/reach/switch4.c diff --git a/test/reach/switch4.expected b/test/c/reach/switch4.expected similarity index 100% rename from test/reach/switch4.expected rename to test/c/reach/switch4.expected diff --git a/test/reach/while.c b/test/c/reach/while.c similarity index 100% rename from test/reach/while.c rename to test/c/reach/while.c diff --git a/test/reach/while.expected b/test/c/reach/while.expected similarity index 100% rename from test/reach/while.expected rename to test/c/reach/while.expected diff --git a/test/reach/while2.c b/test/c/reach/while2.c similarity index 100% rename from test/reach/while2.c rename to test/c/reach/while2.c diff --git a/test/reach/while2.expected b/test/c/reach/while2.expected similarity index 100% rename from test/reach/while2.expected rename to test/c/reach/while2.expected diff --git a/test/reach/while3.c b/test/c/reach/while3.c similarity index 100% rename from test/reach/while3.c rename to test/c/reach/while3.c diff --git a/test/reach/while3.expected b/test/c/reach/while3.expected similarity index 100% rename from test/reach/while3.expected rename to test/c/reach/while3.expected diff --git a/test/simd/add.c b/test/c/simd/add.c similarity index 100% rename from test/simd/add.c rename to test/c/simd/add.c diff --git a/test/simd/add_fail.c b/test/c/simd/add_fail.c similarity index 100% rename from test/simd/add_fail.c rename to test/c/simd/add_fail.c diff --git a/test/simd/cast.c b/test/c/simd/cast.c similarity index 100% rename from test/simd/cast.c rename to test/c/simd/cast.c diff --git a/test/simd/cast_fail.c b/test/c/simd/cast_fail.c similarity index 100% rename from test/simd/cast_fail.c rename to test/c/simd/cast_fail.c diff --git a/test/simd/config.yml b/test/c/simd/config.yml similarity index 100% rename from test/simd/config.yml rename to test/c/simd/config.yml diff --git a/test/simd/constant.c b/test/c/simd/constant.c similarity index 100% rename from test/simd/constant.c rename to test/c/simd/constant.c diff --git a/test/simd/constant_fail.c b/test/c/simd/constant_fail.c similarity index 100% rename from test/simd/constant_fail.c rename to test/c/simd/constant_fail.c diff --git a/test/simd/shuffle.c b/test/c/simd/shuffle.c similarity index 100% rename from test/simd/shuffle.c rename to test/c/simd/shuffle.c diff --git a/test/simd/shuffle_fail.c b/test/c/simd/shuffle_fail.c similarity index 100% rename from test/simd/shuffle_fail.c rename to test/c/simd/shuffle_fail.c diff --git a/test/c/special/assume.c b/test/c/special/assume.c new file mode 100644 index 000000000..883e1455a --- /dev/null +++ b/test/c/special/assume.c @@ -0,0 +1,12 @@ +#include "smack.h" + +// @expect verified +// @flag --llvm-assumes=use + +int main(void) { + unsigned int y = 2 * (unsigned int)__VERIFIER_nondet_unsigned_short(); + // This assumption is used for verification, even though bit-precise + // is not enabled, the assertion will pass. + __builtin_assume((y & 1) == 0); + assert((y & 1) == 0); +} diff --git a/test/c/special/assume2.c b/test/c/special/assume2.c new file mode 100644 index 000000000..03b70c663 --- /dev/null +++ b/test/c/special/assume2.c @@ -0,0 +1,12 @@ +#include "smack.h" + +// @expect verified +// @flag --llvm-assumes=use + +int main(void) { + unsigned int y = (2 * (unsigned int)__VERIFIER_nondet_unsigned_short()) + 1; + // This assumption is used for verification, even though the assumption + // is false, the assertion will pass. + __builtin_assume((y & 1) == 0); + assert((y & 1) == 0); +} diff --git a/test/c/special/assume_check.c b/test/c/special/assume_check.c new file mode 100644 index 000000000..9b8afcfa4 --- /dev/null +++ b/test/c/special/assume_check.c @@ -0,0 +1,12 @@ +#include "smack.h" + +// @expect verified +// @flag --llvm-assumes=check +// @flag --bit-precise + +int main(void) { + unsigned int y = 2 * (unsigned int)__VERIFIER_nondet_unsigned_short(); + // This assumption is checked under bit-precise and is verified. + __builtin_assume((y & 1) == 0); + assert((y & 1) == 0); +} diff --git a/test/c/special/assume_check2.c b/test/c/special/assume_check2.c new file mode 100644 index 000000000..031b9c115 --- /dev/null +++ b/test/c/special/assume_check2.c @@ -0,0 +1,13 @@ +#include "smack.h" + +// @expect verified +// @flag --llvm-assumes=check +// @flag --bit-precise + +int main(void) { + unsigned int y = (2 * (unsigned int)__VERIFIER_nondet_unsigned_short()) + 1; + // This assumption is checked at verification time, and since bit-precise + // is enabled, and y is clearly odd, the check will pass. + __builtin_assume((y & 1) == 1); + assert((y & 1) == 1); +} diff --git a/test/c/special/assume_check_fail.c b/test/c/special/assume_check_fail.c new file mode 100644 index 000000000..3dbfacd72 --- /dev/null +++ b/test/c/special/assume_check_fail.c @@ -0,0 +1,13 @@ +#include "smack.h" + +// @expect error +// @flag --llvm-assumes=check +// @flag --bit-precise + +int main(void) { + unsigned int y = (2 * (unsigned int)__VERIFIER_nondet_unsigned_short()) + 1; + // This assumption is checked at verification time, and since bit-precise + // is enabled, and y is clearly odd, the assumption should be shown false. + __builtin_assume((y & 1) == 0); + assert((y & 1) == 0); +} diff --git a/test/c/special/assume_fail.c b/test/c/special/assume_fail.c new file mode 100644 index 000000000..b775dee9c --- /dev/null +++ b/test/c/special/assume_fail.c @@ -0,0 +1,12 @@ +#include "smack.h" + +// @expect error +// @flag --llvm-assumes=none + +int main(void) { + unsigned int y = 2 * (unsigned int)__VERIFIER_nondet_unsigned_short(); + // This assumption is not used, and since bit-precise is not enabled, + // verification will fail. + __builtin_assume((y & 1) == 0); + assert((y & 1) == 0); +} diff --git a/test/strings/config.yml b/test/c/strings/config.yml similarity index 100% rename from test/strings/config.yml rename to test/c/strings/config.yml diff --git a/test/strings/strcat.c b/test/c/strings/strcat.c similarity index 100% rename from test/strings/strcat.c rename to test/c/strings/strcat.c diff --git a/test/strings/strcat_fail.c b/test/c/strings/strcat_fail.c similarity index 100% rename from test/strings/strcat_fail.c rename to test/c/strings/strcat_fail.c diff --git a/test/strings/strcat_overflow.c b/test/c/strings/strcat_overflow.c similarity index 100% rename from test/strings/strcat_overflow.c rename to test/c/strings/strcat_overflow.c diff --git a/test/strings/strchr.c b/test/c/strings/strchr.c similarity index 100% rename from test/strings/strchr.c rename to test/c/strings/strchr.c diff --git a/test/strings/strchr_fail.c b/test/c/strings/strchr_fail.c similarity index 100% rename from test/strings/strchr_fail.c rename to test/c/strings/strchr_fail.c diff --git a/test/strings/strcmp.c b/test/c/strings/strcmp.c similarity index 100% rename from test/strings/strcmp.c rename to test/c/strings/strcmp.c diff --git a/test/strings/strcmp_fail.c b/test/c/strings/strcmp_fail.c similarity index 100% rename from test/strings/strcmp_fail.c rename to test/c/strings/strcmp_fail.c diff --git a/test/strings/strcpy.c b/test/c/strings/strcpy.c similarity index 100% rename from test/strings/strcpy.c rename to test/c/strings/strcpy.c diff --git a/test/strings/strcpy_fail.c b/test/c/strings/strcpy_fail.c similarity index 100% rename from test/strings/strcpy_fail.c rename to test/c/strings/strcpy_fail.c diff --git a/test/strings/strcpy_overflow.c b/test/c/strings/strcpy_overflow.c similarity index 100% rename from test/strings/strcpy_overflow.c rename to test/c/strings/strcpy_overflow.c diff --git a/test/strings/strcspn.c b/test/c/strings/strcspn.c similarity index 100% rename from test/strings/strcspn.c rename to test/c/strings/strcspn.c diff --git a/test/strings/strcspn_fail.c b/test/c/strings/strcspn_fail.c similarity index 100% rename from test/strings/strcspn_fail.c rename to test/c/strings/strcspn_fail.c diff --git a/test/strings/strlen.c b/test/c/strings/strlen.c similarity index 100% rename from test/strings/strlen.c rename to test/c/strings/strlen.c diff --git a/test/strings/strlen_fail.c b/test/c/strings/strlen_fail.c similarity index 100% rename from test/strings/strlen_fail.c rename to test/c/strings/strlen_fail.c diff --git a/test/strings/strncat.c b/test/c/strings/strncat.c similarity index 100% rename from test/strings/strncat.c rename to test/c/strings/strncat.c diff --git a/test/strings/strncat_fail.c b/test/c/strings/strncat_fail.c similarity index 100% rename from test/strings/strncat_fail.c rename to test/c/strings/strncat_fail.c diff --git a/test/strings/strncmp.c b/test/c/strings/strncmp.c similarity index 100% rename from test/strings/strncmp.c rename to test/c/strings/strncmp.c diff --git a/test/strings/strncmp_fail.c b/test/c/strings/strncmp_fail.c similarity index 100% rename from test/strings/strncmp_fail.c rename to test/c/strings/strncmp_fail.c diff --git a/test/strings/strncmp_toolong.c b/test/c/strings/strncmp_toolong.c similarity index 100% rename from test/strings/strncmp_toolong.c rename to test/c/strings/strncmp_toolong.c diff --git a/test/strings/strpbrk.c b/test/c/strings/strpbrk.c similarity index 100% rename from test/strings/strpbrk.c rename to test/c/strings/strpbrk.c diff --git a/test/strings/strpbrk_fail.c b/test/c/strings/strpbrk_fail.c similarity index 100% rename from test/strings/strpbrk_fail.c rename to test/c/strings/strpbrk_fail.c diff --git a/test/strings/strrchr.c b/test/c/strings/strrchr.c similarity index 100% rename from test/strings/strrchr.c rename to test/c/strings/strrchr.c diff --git a/test/strings/strrchr_fail.c b/test/c/strings/strrchr_fail.c similarity index 100% rename from test/strings/strrchr_fail.c rename to test/c/strings/strrchr_fail.c diff --git a/test/strings/strspn.c b/test/c/strings/strspn.c similarity index 100% rename from test/strings/strspn.c rename to test/c/strings/strspn.c diff --git a/test/strings/strspn_fail.c b/test/c/strings/strspn_fail.c similarity index 100% rename from test/strings/strspn_fail.c rename to test/c/strings/strspn_fail.c diff --git a/test/strings/strstr.c b/test/c/strings/strstr.c similarity index 100% rename from test/strings/strstr.c rename to test/c/strings/strstr.c diff --git a/test/strings/strstr_fail.c b/test/c/strings/strstr_fail.c similarity index 100% rename from test/strings/strstr_fail.c rename to test/c/strings/strstr_fail.c diff --git a/test/strings/strtok.c b/test/c/strings/strtok.c similarity index 100% rename from test/strings/strtok.c rename to test/c/strings/strtok.c diff --git a/test/strings/strtok_fail.c b/test/c/strings/strtok_fail.c similarity index 100% rename from test/strings/strtok_fail.c rename to test/c/strings/strtok_fail.c diff --git a/test/timeouts/config.yml b/test/c/timeouts/config.yml similarity index 100% rename from test/timeouts/config.yml rename to test/c/timeouts/config.yml diff --git a/test/cplusplus/config.yml b/test/cplusplus/basic/config.yml similarity index 100% rename from test/cplusplus/config.yml rename to test/cplusplus/basic/config.yml diff --git a/test/cplusplus/hello.cc b/test/cplusplus/basic/hello.cc similarity index 100% rename from test/cplusplus/hello.cc rename to test/cplusplus/basic/hello.cc diff --git a/test/cplusplus/hello_fail.cc b/test/cplusplus/basic/hello_fail.cc similarity index 100% rename from test/cplusplus/hello_fail.cc rename to test/cplusplus/basic/hello_fail.cc diff --git a/test/regtest.py b/test/regtest.py index 9562627d5..3e6a6840f 100755 --- a/test/regtest.py +++ b/test/regtest.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python2 +#! /usr/bin/env python3 from os import path from multiprocessing.pool import ThreadPool @@ -20,6 +20,10 @@ OVERRIDE_FIELDS = ['verifiers', 'memory', 'time-limit', 'memory-limit', 'skip'] APPEND_FIELDS = ['flags', 'checkbpl', 'checkout'] +LANGUAGES = {'c': {'*.c'}, + 'cplusplus': {'*.cpp'}, + 'rust': {'*.rs'}} + def bold(text): return '\033[1m' + text + '\033[0m' @@ -94,11 +98,11 @@ def metadata(file): if not m['skip']: if not 'expect' in m: - print red("WARNING: @expect MISSING IN %s" % file, None) + print(red("WARNING: @expect MISSING IN %s" % file, None)) m['expect'] = 'verified' if not m['expect'] in ['verified', 'error', 'timeout', 'unknown']: - print red("WARNING: unexpected @expect annotation '%s'" % m['expect'], None) + print(red("WARNING: unexpected @expect annotation '%s'" % m['expect'], None)) return m @@ -115,7 +119,8 @@ def process_test(cmd, test, memory, verifier, expect, checkbpl, checkout, log_fi str_result += "{0:>20} {1:>10} :".format(memory, verifier) t0 = time.time() - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=True) out, err = p.communicate() elapsed = time.time() - t0 status = 0 @@ -165,6 +170,21 @@ def tally_result(result): elif "UNKNOWN" in result: unknowns += 1 +def get_extensions(languages): + languages = list(languages.split(',')) + extensions = set() + for language in languages: + extensions |= LANGUAGES[language] + return extensions + +def get_tests(folder, extensions): + tests = [] + for ext in extensions: + tests_path = path.dirname(__file__) + tests.extend(glob.glob(path.join(tests_path, folder, ext))) + tests.sort() + return tests + def main(): """ Main entry point for the test suite. @@ -178,7 +198,7 @@ def main(): parser.add_argument("--exhaustive", help="check all configurations on all examples", action="store_true") parser.add_argument("--all-configs", help="check all configurations per example", action="store_true") parser.add_argument("--all-examples", help="check all examples", action="store_true") - parser.add_argument("--folder", action="store", default="**", type=str, + parser.add_argument("--folder", action="store", default="**/**", type=str, help="sets the regressions folder to run") parser.add_argument("--threads", action="store", dest="n_threads", default=num_cpus, type=int, help="execute regressions using the selected number of threads in parallel") @@ -186,12 +206,17 @@ def main(): help="sets the logging level (DEBUG, INFO, WARNING)") parser.add_argument("--output-log", action="store", dest="log_path", type=str, help="sets the output log path. (std out by default)") + parser.add_argument("--languages", action="store", default="c", choices=list(LANGUAGES.keys()), + help="Comma separated list of langauges to test. C[c],C++[cplusplus],Rust[rust]") args = parser.parse_args() if args.exhaustive: args.all_examples = True; args.all_configs = True; + extensions = get_extensions(args.languages) + tests = get_tests(args.folder, extensions) + # configure the logging log_format = '' log_level = logging.DEBUG @@ -217,8 +242,9 @@ def main(): logging.info("Running regression tests...") # start processing the tests. + results = [] - for test in sorted(glob.glob("./" + args.folder + "/*.c")): + for test in tests: # get the meta data for this test meta = metadata(test) diff --git a/test/rust/basic/add_fail.rs b/test/rust/basic/add_fail.rs new file mode 100644 index 000000000..3f32c4834 --- /dev/null +++ b/test/rust/basic/add_fail.rs @@ -0,0 +1,11 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect error + +fn main() { + let a = 2; + let b = 3; + assert!(a+b != 5); +} diff --git a/test/rust/basic/add_overflow.rs b/test/rust/basic/add_overflow.rs new file mode 100644 index 000000000..aa29698fe --- /dev/null +++ b/test/rust/basic/add_overflow.rs @@ -0,0 +1,12 @@ +#[macro_use] +mod smack; +use smack::*; + +// @flag --integer-overflow +// @expect error + +fn main() { + let a: u8 = 128; + let b: u8 = 128; + let c = a + b; +} diff --git a/test/rust/basic/arith.rs b/test/rust/basic/arith.rs new file mode 100644 index 000000000..94075f3fc --- /dev/null +++ b/test/rust/basic/arith.rs @@ -0,0 +1,54 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect verified + +fn main() { + // unsigned + { + let a: u32 = 2; + let b: u32 = 3; + { + let c = a + b; + assert!(c == 5); + } + { + let c = a * b; + assert!(c == 6); + } + { + let c = b - a; + assert!(c == 1); + } + { + let c = a % b; + assert!(c == 2); + let d = b % a; + assert!(d == 1); + } + { + let c = a / b; + assert!(c == 0); + let d = b / a; + assert!(d == 1); + } + } + // signed + { + let a: i32 = -3; + let b: i32 = 5; + { + let c = a + b; + assert!(c == 2); + } + { + let c = a * b; + assert!(c == -15); + } + { + let c = b - a; + assert!(c == 8); + } + } +} diff --git a/test/rust/basic/arith_assume.rs b/test/rust/basic/arith_assume.rs new file mode 100644 index 000000000..6a2d1d381 --- /dev/null +++ b/test/rust/basic/arith_assume.rs @@ -0,0 +1,13 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect verified + +fn main() { + let a = 6i32.nondet(); + let b = 7i32.nondet(); + assume!(4 < a && a < 8); // a in [5,7] + assume!(5 < b && b < 9); // b in [6,8] + assert!(30 <= a * b && a * b <= 56); // a*b in [30,56] +} diff --git a/test/rust/basic/arith_assume2.rs b/test/rust/basic/arith_assume2.rs new file mode 100644 index 000000000..6a2d1d381 --- /dev/null +++ b/test/rust/basic/arith_assume2.rs @@ -0,0 +1,13 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect verified + +fn main() { + let a = 6i32.nondet(); + let b = 7i32.nondet(); + assume!(4 < a && a < 8); // a in [5,7] + assume!(5 < b && b < 9); // b in [6,8] + assert!(30 <= a * b && a * b <= 56); // a*b in [30,56] +} diff --git a/test/rust/basic/arith_assume_fail.rs b/test/rust/basic/arith_assume_fail.rs new file mode 100644 index 000000000..554b3e108 --- /dev/null +++ b/test/rust/basic/arith_assume_fail.rs @@ -0,0 +1,16 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect error + +fn main() { + let a = 6i32.nondet(); + let b = 7i32.nondet(); + assume!(4 < a && a < 8); // a in [5,7] + assume!(5 < b && b < 9); // b in [6,8] + let x = a * b; + assert!(!(x==30 || x==35 || x==40 || + x==36 || x==48 || x==42 || + x==49 || x==56)); // a*b != anything allowed +} diff --git a/test/rust/basic/div_fail.rs b/test/rust/basic/div_fail.rs new file mode 100644 index 000000000..812120970 --- /dev/null +++ b/test/rust/basic/div_fail.rs @@ -0,0 +1,11 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect error + +fn main() { + let a = 2; + let b = 3; + assert!(b/a != 1); +} diff --git a/test/rust/basic/mod_fail.rs b/test/rust/basic/mod_fail.rs new file mode 100644 index 000000000..89522cc79 --- /dev/null +++ b/test/rust/basic/mod_fail.rs @@ -0,0 +1,11 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect error + +fn main() { + let a = 2; + let b = 3; + assert!(b%a != 1); +} diff --git a/test/rust/basic/mul_fail.rs b/test/rust/basic/mul_fail.rs new file mode 100644 index 000000000..82638cfc4 --- /dev/null +++ b/test/rust/basic/mul_fail.rs @@ -0,0 +1,11 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect error + +fn main() { + let a = 2; + let b = 3; + assert!(b*a != 6); +} diff --git a/test/rust/basic/mul_overflow.rs b/test/rust/basic/mul_overflow.rs new file mode 100644 index 000000000..9d1c11f05 --- /dev/null +++ b/test/rust/basic/mul_overflow.rs @@ -0,0 +1,12 @@ +#[macro_use] +mod smack; +use smack::*; + +// @flag --integer-overflow +// @expect error + +fn main() { + let a: u8 = 128; + let b: u8 = 2; + let c = a * b; +} diff --git a/test/rust/basic/sub_fail.rs b/test/rust/basic/sub_fail.rs new file mode 100644 index 000000000..b32beb617 --- /dev/null +++ b/test/rust/basic/sub_fail.rs @@ -0,0 +1,11 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect error + +fn main() { + let a = 2; + let b = 3; + assert!(b-a != 1); +} diff --git a/test/rust/basic/sub_overflow.rs b/test/rust/basic/sub_overflow.rs new file mode 100644 index 000000000..3128212d9 --- /dev/null +++ b/test/rust/basic/sub_overflow.rs @@ -0,0 +1,12 @@ +#[macro_use] +mod smack; +use smack::*; + +// @flag --integer-overflow +// @expect error + +fn main() { + let a: u8 = 128; + let b: u8 = 129; + let c = a - b; +} diff --git a/test/rust/functions/closure.rs b/test/rust/functions/closure.rs new file mode 100644 index 000000000..e20a855db --- /dev/null +++ b/test/rust/functions/closure.rs @@ -0,0 +1,23 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect verified + +fn call_with_one(mut some_closure: F) -> () + where F : FnMut(i32) -> () { + + some_closure(1); +} + +fn main() { + let mut num = 5i32.nondet(); + let original_num = num; + { + let mut add_num = |x: i32| num += x; + + add_num(5); + call_with_one(&mut add_num); + } + assert_eq!(original_num + 6, num); +} diff --git a/test/rust/functions/closure_fail.rs b/test/rust/functions/closure_fail.rs new file mode 100644 index 000000000..40ca9948e --- /dev/null +++ b/test/rust/functions/closure_fail.rs @@ -0,0 +1,23 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect error + +fn call_with_one(mut some_closure: F) -> () + where F : FnMut(i32) -> () { + + some_closure(1); +} + +fn main() { + let mut num = 5i32.nondet(); + let old_num = num; + { + let mut add_num = |x: i32| num += x; + + add_num(5); + call_with_one(&mut add_num); + } + assert!(old_num + 6 != num); // Should be old_num + 6 +} diff --git a/test/rust/functions/double.rs b/test/rust/functions/double.rs new file mode 100644 index 000000000..761afe1c9 --- /dev/null +++ b/test/rust/functions/double.rs @@ -0,0 +1,15 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect verified + +fn double(a: u32) -> u32 { + a * 2 +} + +fn main() { + let a = 2u32.nondet(); + let b = double(a); + assert!(b == 2*a); +} diff --git a/test/rust/functions/double_fail.rs b/test/rust/functions/double_fail.rs new file mode 100644 index 000000000..034b36db9 --- /dev/null +++ b/test/rust/functions/double_fail.rs @@ -0,0 +1,15 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect error + +fn double(a: u32) -> u32 { + a * 2 +} + +fn main() { + let a = 2u32.nondet(); + let b = double(a); + assert!(b != 2*a); +} diff --git a/test/rust/generics/config.yml b/test/rust/generics/config.yml new file mode 100644 index 000000000..f538f2a68 --- /dev/null +++ b/test/rust/generics/config.yml @@ -0,0 +1 @@ +memory: [no-reuse-impls, no-reuse] diff --git a/test/rust/generics/generic_function.rs b/test/rust/generics/generic_function.rs new file mode 100644 index 000000000..0cde8c545 --- /dev/null +++ b/test/rust/generics/generic_function.rs @@ -0,0 +1,54 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect verified + +struct Point { + pub x:T, + pub y:T +} + +struct Point3 { + pub x:T, + pub y:T, + pub z:T +} + +trait S { + fn swap_items(self)->Self; +} + +impl S for Point { + fn swap_items(self) -> Point { + Point::{x: self.y, y: self.x} + } +} + +impl S for Point3 { + fn swap_items(self) -> Point3 { + Point3::{x: self.y, y: self.z, z: self.x} + } +} + +fn swapem>(s: U) -> U { + s.swap_items() +} + +fn main() { + let x2 = 7i64.nondet(); + let y2 = 8i64.nondet(); + let x3 = 1i64.nondet(); + let y3 = 2i64.nondet(); + let z3 = 3i64.nondet(); + let p2 = Point::{x: x2, y: y2}; + let p3 = Point3::{x: x3, y: y3, z: z3}; + + let q2 = swapem(p2); + let q3 = swapem(p3); + assert!(q2.x == y2); + assert!(q2.y == x2); + assert!(q3.x == y3); + assert!(q3.y == z3); + assert!(q3.z == x3); +} diff --git a/test/rust/generics/generic_function_fail1.rs b/test/rust/generics/generic_function_fail1.rs new file mode 100644 index 000000000..493e3b507 --- /dev/null +++ b/test/rust/generics/generic_function_fail1.rs @@ -0,0 +1,54 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect error + +struct Point { + pub x:T, + pub y:T +} + +struct Point3 { + pub x:T, + pub y:T, + pub z:T +} + +trait S { + fn swap_items(self)->Self; +} + +impl S for Point { + fn swap_items(self) -> Point { + Point::{x: self.y, y: self.x} + } +} + +impl S for Point3 { + fn swap_items(self) -> Point3 { + Point3::{x: self.y, y: self.z, z: self.x} + } +} + +fn swapem>(s: U) -> U { + s.swap_items() +} + +fn main() { + let x2 = 7i64.nondet(); + let y2 = 8i64.nondet(); + let x3 = 1i64.nondet(); + let y3 = 2i64.nondet(); + let z3 = 3i64.nondet(); + let p2 = Point::{x: x2, y: y2}; + let p3 = Point3::{x: x3, y: y3, z: z3}; + + let q2 = swapem(p2); + let q3 = swapem(p3); + assert!(q2.x != y2); + assert!(q2.y == x2); + assert!(q3.x == y3); + assert!(q3.y == z3); + assert!(q3.z == x3); +} diff --git a/test/rust/generics/generic_function_fail2.rs b/test/rust/generics/generic_function_fail2.rs new file mode 100644 index 000000000..9295c1d29 --- /dev/null +++ b/test/rust/generics/generic_function_fail2.rs @@ -0,0 +1,54 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect error + +struct Point { + pub x:T, + pub y:T +} + +struct Point3 { + pub x:T, + pub y:T, + pub z:T +} + +trait S { + fn swap_items(self)->Self; +} + +impl S for Point { + fn swap_items(self) -> Point { + Point::{x: self.y, y: self.x} + } +} + +impl S for Point3 { + fn swap_items(self) -> Point3 { + Point3::{x: self.y, y: self.z, z: self.x} + } +} + +fn swapem>(s: U) -> U { + s.swap_items() +} + +fn main() { + let x2 = 7i64.nondet(); + let y2 = 8i64.nondet(); + let x3 = 1i64.nondet(); + let y3 = 2i64.nondet(); + let z3 = 3i64.nondet(); + let p2 = Point::{x: x2, y: y2}; + let p3 = Point3::{x: x3, y: y3, z: z3}; + + let q2 = swapem(p2); + let q3 = swapem(p3); + assert!(q2.x == y2); + assert!(q2.y != x2); + assert!(q3.x == y3); + assert!(q3.y == z3); + assert!(q3.z == x3); +} diff --git a/test/rust/generics/generic_function_fail3.rs b/test/rust/generics/generic_function_fail3.rs new file mode 100644 index 000000000..9a800da1e --- /dev/null +++ b/test/rust/generics/generic_function_fail3.rs @@ -0,0 +1,54 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect error + +struct Point { + pub x:T, + pub y:T +} + +struct Point3 { + pub x:T, + pub y:T, + pub z:T +} + +trait S { + fn swap_items(self)->Self; +} + +impl S for Point { + fn swap_items(self) -> Point { + Point::{x: self.y, y: self.x} + } +} + +impl S for Point3 { + fn swap_items(self) -> Point3 { + Point3::{x: self.y, y: self.z, z: self.x} + } +} + +fn swapem>(s: U) -> U { + s.swap_items() +} + +fn main() { + let x2 = 7i64.nondet(); + let y2 = 8i64.nondet(); + let x3 = 1i64.nondet(); + let y3 = 2i64.nondet(); + let z3 = 3i64.nondet(); + let p2 = Point::{x: x2, y: y2}; + let p3 = Point3::{x: x3, y: y3, z: z3}; + + let q2 = swapem(p2); + let q3 = swapem(p3); + assert!(q2.x == y2); + assert!(q2.y == x2); + assert!(q3.x != y3); + assert!(q3.y == z3); + assert!(q3.z == x3); +} diff --git a/test/rust/generics/generic_function_fail4.rs b/test/rust/generics/generic_function_fail4.rs new file mode 100644 index 000000000..6aad57efd --- /dev/null +++ b/test/rust/generics/generic_function_fail4.rs @@ -0,0 +1,54 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect error + +struct Point { + pub x:T, + pub y:T +} + +struct Point3 { + pub x:T, + pub y:T, + pub z:T +} + +trait S { + fn swap_items(self)->Self; +} + +impl S for Point { + fn swap_items(self) -> Point { + Point::{x: self.y, y: self.x} + } +} + +impl S for Point3 { + fn swap_items(self) -> Point3 { + Point3::{x: self.y, y: self.z, z: self.x} + } +} + +fn swapem>(s: U) -> U { + s.swap_items() +} + +fn main() { + let x2 = 7i64.nondet(); + let y2 = 8i64.nondet(); + let x3 = 1i64.nondet(); + let y3 = 2i64.nondet(); + let z3 = 3i64.nondet(); + let p2 = Point::{x: x2, y: y2}; + let p3 = Point3::{x: x3, y: y3, z: z3}; + + let q2 = swapem(p2); + let q3 = swapem(p3); + assert!(q2.x == y2); + assert!(q2.y == x2); + assert!(q3.x == y3); + assert!(q3.y != z3); + assert!(q3.z == x3); +} diff --git a/test/rust/generics/generic_function_fail5.rs b/test/rust/generics/generic_function_fail5.rs new file mode 100644 index 000000000..7c4ec4455 --- /dev/null +++ b/test/rust/generics/generic_function_fail5.rs @@ -0,0 +1,54 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect error + +struct Point { + pub x:T, + pub y:T +} + +struct Point3 { + pub x:T, + pub y:T, + pub z:T +} + +trait S { + fn swap_items(self)->Self; +} + +impl S for Point { + fn swap_items(self) -> Point { + Point::{x: self.y, y: self.x} + } +} + +impl S for Point3 { + fn swap_items(self) -> Point3 { + Point3::{x: self.y, y: self.z, z: self.x} + } +} + +fn swapem>(s: U) -> U { + s.swap_items() +} + +fn main() { + let x2 = 7i64.nondet(); + let y2 = 8i64.nondet(); + let x3 = 1i64.nondet(); + let y3 = 2i64.nondet(); + let z3 = 3i64.nondet(); + let p2 = Point::{x: x2, y: y2}; + let p3 = Point3::{x: x3, y: y3, z: z3}; + + let q2 = swapem(p2); + let q3 = swapem(p3); + assert!(q2.x == y2); + assert!(q2.y == x2); + assert!(q3.x == y3); + assert!(q3.y == z3); + assert!(q3.z != x3); +} diff --git a/test/rust/loops/config.yml b/test/rust/loops/config.yml new file mode 100644 index 000000000..f538f2a68 --- /dev/null +++ b/test/rust/loops/config.yml @@ -0,0 +1 @@ +memory: [no-reuse-impls, no-reuse] diff --git a/test/rust/loops/gauss_sum_nondet.rs b/test/rust/loops/gauss_sum_nondet.rs new file mode 100644 index 000000000..fee9cf404 --- /dev/null +++ b/test/rust/loops/gauss_sum_nondet.rs @@ -0,0 +1,16 @@ +#[macro_use] +mod smack; +use smack::*; + +// @flag --unroll=4 +// @expect verified + +fn main() { + let mut sum = 0; + let b = 7u64.nondet(); + assume!(b < 5 && b > 1); + for i in 0..b as u64 { + sum += i; + } + assert!(2*sum == b*(b-1)); +} diff --git a/test/rust/loops/gauss_sum_nondet_fail.rs b/test/rust/loops/gauss_sum_nondet_fail.rs new file mode 100644 index 000000000..47c79428a --- /dev/null +++ b/test/rust/loops/gauss_sum_nondet_fail.rs @@ -0,0 +1,16 @@ +#[macro_use] +mod smack; +use smack::*; + +// @flag --unroll=10 +// @expect error + +fn main() { + let mut sum = 0; + let b = 7u64.nondet(); + assume!(b > 1); + for i in 0..b as u64 { + sum += i; + } + assert!(2*sum != b*(b-1)); +} diff --git a/test/rust/loops/iterator.rs b/test/rust/loops/iterator.rs new file mode 100644 index 000000000..20dad0d14 --- /dev/null +++ b/test/rust/loops/iterator.rs @@ -0,0 +1,24 @@ +#[macro_use] +mod smack; +use smack::*; + +// @flag --unroll=4 +// @expect verified + +fn fac(n: u64) -> u64 { + match n { + 0 => 1, + 1 => 1, + _ => n*fac(n-1) + } +} + +fn main() { + let mut a = 1; + let n = 6u64.nondet(); + assume!(n < 5); + for i in 1..n+1 as u64 { + a *= i; + } + assert!(a == fac(n)); // a == 6! +} diff --git a/test/rust/loops/iterator_fail.rs b/test/rust/loops/iterator_fail.rs new file mode 100644 index 000000000..2f740e534 --- /dev/null +++ b/test/rust/loops/iterator_fail.rs @@ -0,0 +1,22 @@ +#[macro_use] +mod smack; +use smack::*; + +// @flag --unroll=10 +// @expect error + +fn fac(n: u64) -> u64 { + match n { + 0 => 1, + 1 => 1, + _ => n*fac(n-1) + } +} +fn main() { + let mut a = 1; + let n = 6u64.nondet(); + for i in 1..n+1 as u64 { + a *= i; + } + assert!(a != fac(n)); // a should equal 6! +} diff --git a/test/rust/recursion/config.yml b/test/rust/recursion/config.yml new file mode 100644 index 000000000..03dbc1e0f --- /dev/null +++ b/test/rust/recursion/config.yml @@ -0,0 +1 @@ +verifiers: [corral] \ No newline at end of file diff --git a/test/rust/recursion/fac.rs b/test/rust/recursion/fac.rs new file mode 100644 index 000000000..cbbebc0dd --- /dev/null +++ b/test/rust/recursion/fac.rs @@ -0,0 +1,18 @@ +#[macro_use] +mod smack; +use smack::*; + +// @flag --unroll=10 +// @expect verified + +fn fac(n: u64, acc: u64) -> u64 { + match n { + 0 => acc, + _ => fac(n - 1, acc * n) + } +} + +fn main() { + let x = fac(5, 1); + assert!(x == 120); +} diff --git a/test/rust/recursion/fac_fail.rs b/test/rust/recursion/fac_fail.rs new file mode 100644 index 000000000..c8d9c8259 --- /dev/null +++ b/test/rust/recursion/fac_fail.rs @@ -0,0 +1,18 @@ +#[macro_use] +mod smack; +use smack::*; + +// @flag --unroll=10 +// @expect error + +fn fac(n: u64, acc: u64) -> u64 { + match n { + 0 => acc, + _ => fac(n - 1, acc * n) + } +} + +fn main() { + let x = fac(5, 1); + assert!(x != 120); +} diff --git a/test/rust/recursion/fib.rs b/test/rust/recursion/fib.rs new file mode 100644 index 000000000..a03027636 --- /dev/null +++ b/test/rust/recursion/fib.rs @@ -0,0 +1,19 @@ +#[macro_use] +mod smack; +use smack::*; + +// @flag --unroll=10 +// @expect verified + +fn fib(x: u64) -> u64 { + match x { + 0 => 1, + 1 => 1, + _ => fib(x-1) + fib(x-2) + } +} + +fn main() { + let x = fib(6); + assert!(x == 13); +} diff --git a/test/rust/recursion/fib_fail.rs b/test/rust/recursion/fib_fail.rs new file mode 100644 index 000000000..9b6dee652 --- /dev/null +++ b/test/rust/recursion/fib_fail.rs @@ -0,0 +1,19 @@ +#[macro_use] +mod smack; +use smack::*; + +// @flag --unroll=10 +// @expect error + +fn fib(x: u64) -> u64 { + match x { + 0 => 1, + 1 => 1, + _ => fib(x-1) + fib(x-2) + } +} + +fn main() { + let x = fib(6); + assert!(x != 13); +} diff --git a/test/rust/structures/option.rs b/test/rust/structures/option.rs new file mode 100644 index 000000000..cbfb1e52a --- /dev/null +++ b/test/rust/structures/option.rs @@ -0,0 +1,29 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect verified + +fn safe_div(x: u64, y: u64) -> Option { + if y != 0 { + Some(x/y) + } + else { + None + } +} + +fn main() { + let x = 2u64.nondet(); + assume!(x > 0); + let a = safe_div(2*x,x); + match a { + Some(x) => assert!(x == 2), + None => assert!(false) + }; + let b = safe_div(x,0); + match b { + Some(x) => assert!(false), + None => assert!(true) + }; +} diff --git a/test/rust/structures/option_fail.rs b/test/rust/structures/option_fail.rs new file mode 100644 index 000000000..dc11d8f5b --- /dev/null +++ b/test/rust/structures/option_fail.rs @@ -0,0 +1,29 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect error + +fn safe_div(x: u64, y: u64) -> Option { + if y != 0 { + Some(x/y) + } + else { + None + } +} + +fn main() { + let x = 2u64.nondet(); + assume!(x > 0); + let a = safe_div(2*x,x); + match a { + Some(x) => assert!(x == 2), + None => assert!(false) + }; + let b = safe_div(x,0); + match b { + Some(x) => assert!(true), + None => assert!(false) // Division by zero should return None + }; +} diff --git a/test/rust/structures/point.rs b/test/rust/structures/point.rs new file mode 100644 index 000000000..34caa22ce --- /dev/null +++ b/test/rust/structures/point.rs @@ -0,0 +1,51 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect verified + +use std::ops::{Add, AddAssign}; + +#[derive(PartialEq,Clone,Copy)] +struct Point { + x: u64, + y: u64 +} + +impl Point { + pub fn new(_x: u64, _y: u64) -> Point { + Point { x: _x, y: _y } + } + pub fn get_x(self) -> u64 { + self.x + } + pub fn get_y(self) -> u64 { + self.y + } +} + +impl Add for Point { + type Output = Point; + fn add(self, other: Point) -> Point { + Point::new(self.x + other.x, self.y + other.y) + } +} + +impl AddAssign for Point { + fn add_assign(&mut self, other: Point) { + self.x += other.x; + self.y += other.y; + } +} + +fn main() { + let w = 1u64.nondet(); + let x = 2u64.nondet(); + let y = 3u64.nondet(); + let z = 4u64.nondet(); + + let a = Point::new(w, x); + let b = Point::new(y, z); + let c = a + b; + assert!(c == Point::new(w+y,x+z)); +} diff --git a/test/rust/structures/point_fail.rs b/test/rust/structures/point_fail.rs new file mode 100644 index 000000000..9a228ce66 --- /dev/null +++ b/test/rust/structures/point_fail.rs @@ -0,0 +1,51 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect error + +use std::ops::{Add, AddAssign}; + +#[derive(PartialEq,Clone,Copy)] +struct Point { + x: u64, + y: u64 +} + +impl Point { + pub fn new(_x: u64, _y: u64) -> Point { + Point { x: _x, y: _y } + } + pub fn get_x(self) -> u64 { + self.x + } + pub fn get_y(self) -> u64 { + self.y + } +} + +impl Add for Point { + type Output = Point; + fn add(self, other: Point) -> Point { + Point::new(self.x + other.x, self.y + other.y) + } +} + +impl AddAssign for Point { + fn add_assign(&mut self, other: Point) { + self.x += other.x; + self.y += other.y; + } +} + +fn main() { + let w = 1u64.nondet(); + let x = 2u64.nondet(); + let y = 3u64.nondet(); + let z = 4u64.nondet(); + + let a = Point::new(w,x); + let b = Point::new(y,z); + let c = a + b; + assert!(c != Point::new(w+y,x+z)); +} diff --git a/test/rust/vector/config.yml b/test/rust/vector/config.yml new file mode 100644 index 000000000..f538f2a68 --- /dev/null +++ b/test/rust/vector/config.yml @@ -0,0 +1 @@ +memory: [no-reuse-impls, no-reuse] diff --git a/test/rust/vector/vec1.rs b/test/rust/vector/vec1.rs new file mode 100644 index 000000000..eca63922b --- /dev/null +++ b/test/rust/vector/vec1.rs @@ -0,0 +1,19 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect verified + +fn main() { + let mut v: Vec = Vec::new(); + v.push(0); + v.push(1); + v.push(3); + assert!(v[0] == 0); + assert!(v[1] == 1); + assert!(v[2] == 3); + v[2] = v[0]+v[1]; + assert!(v[0] == 0); + assert!(v[1] == 1); + assert!(v[2] == 1); +} diff --git a/test/rust/vector/vec1_fail1.rs b/test/rust/vector/vec1_fail1.rs new file mode 100644 index 000000000..de2d5fdd1 --- /dev/null +++ b/test/rust/vector/vec1_fail1.rs @@ -0,0 +1,19 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect error + +fn main() { + let mut v: Vec = Vec::new(); + v.push(0); + v.push(1); + v.push(3); + assert!(v[0] == 0); + assert!(v[1] == 1); + assert!(v[2] == 3); + v[2] = v[0]+v[1]; + assert!(v[0] != 0); + assert!(v[1] == 1); + assert!(v[2] == 1); +} diff --git a/test/rust/vector/vec1_fail2.rs b/test/rust/vector/vec1_fail2.rs new file mode 100644 index 000000000..79e642f58 --- /dev/null +++ b/test/rust/vector/vec1_fail2.rs @@ -0,0 +1,19 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect error + +fn main() { + let mut v: Vec = Vec::new(); + v.push(0); + v.push(1); + v.push(3); + assert!(v[0] == 0); + assert!(v[1] == 1); + assert!(v[2] == 3); + v[2] = v[0]+v[1]; + assert!(v[0] == 0); + assert!(v[1] != 1); + assert!(v[2] == 1); +} diff --git a/test/rust/vector/vec1_fail3.rs b/test/rust/vector/vec1_fail3.rs new file mode 100644 index 000000000..dbf298842 --- /dev/null +++ b/test/rust/vector/vec1_fail3.rs @@ -0,0 +1,19 @@ +#[macro_use] +mod smack; +use smack::*; + +// @expect error + +fn main() { + let mut v: Vec = Vec::new(); + v.push(0); + v.push(1); + v.push(3); + assert!(v[0] == 0); + assert!(v[1] == 1); + assert!(v[2] == 3); + v[2] = v[0]+v[1]; + assert!(v[0] == 0); + assert!(v[1] == 1); + assert!(v[2] != 1); +} diff --git a/test/rust/vector/vec_resize.rs b/test/rust/vector/vec_resize.rs new file mode 100644 index 000000000..c6a5ccc22 --- /dev/null +++ b/test/rust/vector/vec_resize.rs @@ -0,0 +1,13 @@ +#[macro_use] +mod smack; +use smack::*; + +// @flag --unroll=3 +// @expect verified + +fn main() { + let mut v1:Vec = vec![0]; + let mut v2:Vec = vec![3]; + v1.append(&mut v2); + assert!(v1[1] == 3); +} diff --git a/test/rust/vector/vec_resize_fail.rs b/test/rust/vector/vec_resize_fail.rs new file mode 100644 index 000000000..7dad2eadd --- /dev/null +++ b/test/rust/vector/vec_resize_fail.rs @@ -0,0 +1,13 @@ +#[macro_use] +mod smack; +use smack::*; + +// @flag --unroll=3 +// @expect error + +fn main() { + let mut v1:Vec = vec![0]; + let mut v2:Vec = vec![3]; + v1.append(&mut v2); + assert!(v1[1] != 3); +} diff --git a/tools/llvm2bpl/llvm2bpl.cpp b/tools/llvm2bpl/llvm2bpl.cpp index 41b97c056..f16935d41 100644 --- a/tools/llvm2bpl/llvm2bpl.cpp +++ b/tools/llvm2bpl/llvm2bpl.cpp @@ -24,11 +24,10 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachine.h" -#include "assistDS/Devirt.h" -#include "assistDS/MergeGEP.h" -#include "assistDS/SimplifyExtractValue.h" -#include "assistDS/SimplifyInsertValue.h" -#include "assistDS/StructReturnToPointer.h" +#include "utils/Devirt.h" +#include "utils/MergeGEP.h" +#include "utils/SimplifyExtractValue.h" +#include "utils/SimplifyInsertValue.h" #include "smack/AddTiming.h" #include "smack/BplFilePrinter.h" #include "smack/CodifyStaticInits.h" @@ -153,7 +152,8 @@ int main(int argc, char **argv) { pass_manager.add(llvm::createLowerSwitchPass()); // pass_manager.add(llvm::createCFGSimplificationPass()); - pass_manager.add(llvm::createInternalizePass()); + // Shaobo: sea-dsa is inconsistent with the pass below. + //pass_manager.add(llvm::createInternalizePass()); pass_manager.add(llvm::createPromoteMemoryToRegisterPass()); if (StaticUnroll) {