diff --git a/.clang-tidy b/.clang-tidy index be4ac1fb..b688c5c2 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -14,4 +14,4 @@ Checks: ' HeaderFilterRegex: 'include/*.h' AnalyzeTemporaryDtors: false -FormatStyle: none \ No newline at end of file +FormatStyle: none diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 554a712f..31378e9e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: run: sudo apt-get install build-essential cmake wget lsb-release software-properties-common zlib1g-dev openjdk-11-jre python3 python3-pip python3-setuptools python3-psutil - name: Set up some more dependencies run: | - sudo apt-get install -y clang-9 llvm-9-dev llvm-9-tools llvm-9-runtime libboost-all-dev + sudo apt-get install -y clang-9 llvm-9-dev llvm-9-tools llvm-9-runtime sudo ln -sf /usr/bin/clang-9 /usr/bin/clang sudo ln -s `which opt-9` /usr/bin/opt -f sudo ln -s `which FileCheck-9` /usr/bin/FileCheck @@ -21,7 +21,9 @@ jobs: - name: Set up portfolio dependencies run: sudo apt-get install perl libyaml-tiny-perl libproc-processtable-perl - name: Build - run: cmake -DCMAKE_CXX_COMPILER=clang++-9 -DGAZER_ENABLE_UNIT_TESTS=On -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=On . && make + run: | + cmake -B build -DCMAKE_CXX_COMPILER=clang++-9 -DGAZER_ENABLE_UNIT_TESTS=On -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=On + cmake --build build/ -j2 - name: Get Theta run: | mkdir tools/gazer-theta/theta @@ -32,6 +34,6 @@ jobs: env: THETA_VERSION: v2.10.0 - name: Run unit tests - run: make check-unit + run: cmake --build build --target check-unit - name: Run functional tests - run: make check-functional + run: cmake --build build --target check-functional diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml new file mode 100644 index 00000000..a5d409a6 --- /dev/null +++ b/.github/workflows/sonarcloud.yml @@ -0,0 +1,64 @@ +name: Run SonarCloud analysis + +on: ['push', 'pull_request'] + +jobs: + build: + runs-on: ubuntu-20.04 + env: + SONAR_SCANNER_VERSION: 4.6.1.2450 + SONAR_SERVER_URL: "https://sonarcloud.io" + BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Update apt + run: sudo apt-get update + - name: Set up dependencies + run: sudo apt-get install build-essential cmake wget lsb-release software-properties-common zlib1g-dev openjdk-11-jre python3 python3-pip python3-setuptools python3-psutil + - name: Set up some more dependencies + run: | + sudo apt-get install -y clang-9 llvm-9-dev llvm-9-tools llvm-9-runtime + sudo ln -sf /usr/bin/clang-9 /usr/bin/clang + sudo ln -s `which opt-9` /usr/bin/opt -f + - name: Cache SonarCloud packages and analysis + uses: actions/cache@v1 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + - name: Download and set up sonar-scanner + env: + SONAR_SCANNER_DOWNLOAD_URL: https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${{ env.SONAR_SCANNER_VERSION }}-linux.zip + run: | + mkdir -p $HOME/.sonar + curl -sSLo $HOME/.sonar/sonar-scanner.zip ${{ env.SONAR_SCANNER_DOWNLOAD_URL }} + unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/ + echo "$HOME/.sonar/sonar-scanner-${{ env.SONAR_SCANNER_VERSION }}-linux/bin" >> $GITHUB_PATH + - name: Download and set up build-wrapper + env: + BUILD_WRAPPER_DOWNLOAD_URL: ${{ env.SONAR_SERVER_URL }}/static/cpp/build-wrapper-linux-x86.zip + run: | + curl -sSLo $HOME/.sonar/build-wrapper-linux-x86.zip ${{ env.BUILD_WRAPPER_DOWNLOAD_URL }} + unzip -o $HOME/.sonar/build-wrapper-linux-x86.zip -d $HOME/.sonar/ + echo "$HOME/.sonar/build-wrapper-linux-x86" >> $GITHUB_PATH + - name: Build + run: | + mkdir build + cmake -B build -DCMAKE_CXX_COMPILER=clang++-9 -DGAZER_ENABLE_UNIT_TESTS=On -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DGAZER_ENABLE_COVERAGE=On + build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build build/ + - name: Run unit tests + continue-on-error: true + run: | + cmake --build build --target check-unit + - name: Compile coverage reports + run: | + llvm-profdata-9 merge -sparse `find . -name "*.profraw"` -o coverage.profdata + llvm-cov-9 show -instr-profile=coverage.profdata $(for f in $(find ./build/src -name "libGazer*.so"); do echo -object $f; done) > llvm_cov_report.txt + - name: Run sonar-scanner + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: | + sonar-scanner --define sonar.host.url="${{ env.SONAR_SERVER_URL }}" --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a6fc7dd..02929e7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.13) project(gazer) # Set version information @@ -43,7 +43,8 @@ if(GAZER_ENABLE_SANITIZER) endif() endif() -# Coverage +## Coverage +################################################################################ option(GAZER_ENABLE_COVERAGE "Enable code coverage" OFF) if (GAZER_ENABLE_COVERAGE) if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") @@ -58,7 +59,7 @@ if (GAZER_ENABLE_COVERAGE) endif() # Get LLVM -find_package(LLVM 9.0 REQUIRED CONFIG) +find_package(LLVM 12.0 REQUIRED CONFIG) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") @@ -66,11 +67,48 @@ include_directories(${LLVM_INCLUDE_DIRS}) add_definitions(${LLVM_DEFINITIONS}) list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") +# If `GAZER_SHARED_LLVM` is on, we want to link against LLVM dynamically. +# If it is set to off, we want to map components to static library names +# and link against those. +option(GAZER_SHARED_LLVM "Link against shared libLLVM.so" OFF) + +function(require_llvm_libraries outVar) + if (GAZER_SHARED_LLVM) + message(STATUS "Searching for shared libLLVM in ${LLVM_LIBRARY_DIR}") + find_library( + LLVM_LIBS + NAMES LLVM-9 + PATHS ${LLVM_LIBRARY_DIRS} + NO_DEFAULT_PATH + ) + message(STATUS "Found shared libLLVM in ${LLVM_LIBS}") + set(${outVar} ${LLVM_LIBS} PARENT_SCOPE) + else() + llvm_map_components_to_libnames(LLVM_LIBRARY_NAMES ${ARGN}) + set(${outVar} ${LLVM_LIBRARY_NAMES} PARENT_SCOPE) + endif() +endfunction() + # Get boost -find_package(Boost 1.70 REQUIRED) -include_directories(${Boost_INCLUDE_DIR}) +include(FetchContent) + +FetchContent_Declare( + Boost + URL https://boostorg.jfrog.io/artifactory/main/release/1.76.0/source/boost_1_76_0.tar.bz2 + URL_HASH SHA256=f0397ba6e982c4450f27bf32a2a83292aba035b827a5623a14636ea583318c41 +) +FetchContent_GetProperties(Boost) + +if(NOT Boost_POPULATED) + message(STATUS "Fetching Boost") + FetchContent_Populate(Boost) + message(STATUS "Fetching Boost - done") +endif() + +message("Boost include directories are ${boost_SOURCE_DIR}") +add_definitions(-DBOOST_NO_RTTI -DBOOST_EXCEPTION_DISABLE -DBOOST_NO_EXCEPTIONS -DBOOST_NO_IOSTREAM) +include_directories(${boost_SOURCE_DIR}) -add_definitions(-DBOOST_NO_RTTI -DBOOST_EXCEPTION_DISABLE -DBOOST_NO_EXCEPTIONS) # Project directories set(GAZER_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/include") diff --git a/Dockerfile b/Dockerfile index 8e6e958e..2e34efb7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,7 @@ -FROM ubuntu:18.04 +FROM ubuntu:20.04 +ARG DEBIAN_FRONTEND=noninteractive +ENV TZ=Europe/Budapest ENV THETA_VERSION v2.10.0 RUN apt-get update && \ @@ -10,11 +12,10 @@ RUN apt-get update && \ # fetch LLVM and other dependencies RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && \ - add-apt-repository "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main" && \ + add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-12 main" && \ apt-get update && \ - add-apt-repository ppa:mhier/libboost-latest && \ apt-get update && \ - apt-get install -y clang-9 llvm-9-dev llvm-9-tools llvm-9-runtime libboost1.70-dev perl libyaml-tiny-perl + apt-get install -y clang-12 llvm-12-dev llvm-12-tools llvm-12-runtime perl libyaml-tiny-perl # create a new user `user` with the password `user` and sudo rights RUN useradd -m user && \ @@ -24,7 +25,7 @@ RUN useradd -m user && \ # (the portfolio uses clang) RUN ln -sf /usr/bin/clang-9 /usr/bin/clang - + USER user ENV GAZER_DIR /home/user/gazer @@ -32,7 +33,7 @@ ENV GAZER_DIR /home/user/gazer ADD --chown=user:user . $GAZER_DIR WORKDIR $GAZER_DIR -RUN cmake -DCMAKE_CXX_COMPILER=clang++-9 -DGAZER_ENABLE_UNIT_TESTS=On -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=On . && make +RUN cmake -DCMAKE_CXX_COMPILER=clang++-12 -DGAZER_ENABLE_UNIT_TESTS=On -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=On . && make # download theta (and libs) RUN mkdir $GAZER_DIR/tools/gazer-theta/theta && \ diff --git a/cmake/GoogleTestDownload.cmake b/cmake/GoogleTestDownload.cmake deleted file mode 100644 index e38548ca..00000000 --- a/cmake/GoogleTestDownload.cmake +++ /dev/null @@ -1,17 +0,0 @@ -# CMake configuration copied from the gtest documentation -# (https://github.com/google/googletest/blob/master/googletest/README.md) -cmake_minimum_required(VERSION 2.8.2) - -project(googletest-download NONE) - -include(ExternalProject) -ExternalProject_Add(googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG master - SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src" - BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" -) \ No newline at end of file diff --git a/doc/Developers.md b/doc/Developers.md index af82392e..0dfd9c73 100644 --- a/doc/Developers.md +++ b/doc/Developers.md @@ -21,6 +21,7 @@ In addition to the standard cmake flags, Gazer builds may be configured with the following Gazer-specific flags: * **GAZER_ENABLE_UNIT_TESTS:** Build Gazer unit tests. Defaults to ON. * **GAZER_ENABLE_SANITIZER:** Enable the address and undefined behavior sanitizers. Defaults to OFF. + * **GAZER_SHARED_LLVM:** Link LLVM components dynamically (e.g. using `libLLVM.so`). Only recommended for debug builds, defaults to OFF. ## Test diff --git a/include/gazer/ADT/Graph.h b/include/gazer/ADT/Graph.h index cde7f8d6..fc9ab35a 100644 --- a/include/gazer/ADT/Graph.h +++ b/include/gazer/ADT/Graph.h @@ -125,8 +125,8 @@ class GraphEdge template class Graph { - static_assert(std::is_base_of_v, NodeTy>, ""); - static_assert(std::is_base_of_v, EdgeTy>, ""); + static_assert(std::is_base_of_v, NodeTy>); + static_assert(std::is_base_of_v, EdgeTy>); using NodeVectorTy = std::vector>; using EdgeVectorTy = std::vector>; @@ -135,11 +135,11 @@ class Graph using node_iterator = SmartPtrGetIterator; using const_node_iterator = SmartPtrGetIterator; - node_iterator node_begin() { return mNodes.begin(); } - node_iterator node_end() { return mNodes.end(); } + node_iterator node_begin() { return node_iterator(mNodes.begin()); } + node_iterator node_end() { return node_iterator(mNodes.end()); } - const_node_iterator node_begin() const { return mNodes.begin(); } - const_node_iterator node_end() const { return mNodes.end(); } + const_node_iterator node_begin() const { return const_node_iterator(mNodes.begin()); } + const_node_iterator node_end() const { return const_node_iterator(mNodes.end()); } llvm::iterator_range nodes() { return llvm::make_range(node_begin(), node_end()); @@ -151,11 +151,11 @@ class Graph using edge_iterator = SmartPtrGetIterator; using const_edge_iterator = SmartPtrGetIterator; - edge_iterator edge_begin() { return mEdges.begin(); } - edge_iterator edge_end() { return mEdges.end(); } + edge_iterator edge_begin() { return edge_iterator(mEdges.begin()); } + edge_iterator edge_end() { return edge_iterator(mEdges.end()); } - const_edge_iterator edge_begin() const { return mEdges.begin(); } - const_edge_iterator edge_end() const { return mEdges.end(); } + const_edge_iterator edge_begin() const { return const_edge_iterator(mEdges.begin()); } + const_edge_iterator edge_end() const { return const_edge_iterator(mEdges.end()); } llvm::iterator_range edges() { return llvm::make_range(edge_begin(), edge_end()); } @@ -360,6 +360,6 @@ struct GraphTraits*>> } }; -} // namespace gazer +} // namespace llvm #endif diff --git a/include/gazer/ADT/Iterator.h b/include/gazer/ADT/Iterator.h index 6512716c..a528c3e9 100644 --- a/include/gazer/ADT/Iterator.h +++ b/include/gazer/ADT/Iterator.h @@ -20,37 +20,108 @@ #include #include +#include namespace gazer { -template -typename std::unique_ptr::pointer derefUniquePtr(std::unique_ptr& ptr) +/// An iterator adaptor that filters a range of polymorphic objects that use LLVM-style RTTI. +/// On any given range, the iterator skips all elements that do not have the runtime type \p T +/// (i.e. elements on which `llvm::isa(elem)` is false). Retained elements are mapped to their +/// proper type using `llvm::cast`. This allows to simplify code like: +/// for (llvm::Instruction& inst : llvm::instructions(function)) { +/// if (auto* call = llvm::dyn_cast(&inst)) { +/// ... +/// } +/// } +/// into the following: +/// for (llvm::CallInst& call : classof_range(llvm::instructions(function)) { +/// ... +/// } +/// . +/// Note that the returned type can be a raw pointer or reference extracted from a smart pointer, +/// which can lead to memory errors if the resulting value is incorrectly stored somewhere. +template(*std::declval()))> +class classof_iterator : + public llvm::iterator_adaptor_base< + classof_iterator, + WrappedIteratorType, + typename std::iterator_traits::iterator_category, + std::remove_reference_t + > { - return ptr.get(); + using BaseT = llvm::iterator_adaptor_base< + classof_iterator, + WrappedIteratorType, + typename std::iterator_traits::iterator_category, + std::remove_reference_t + >; + +public: + classof_iterator(WrappedIteratorType begin, WrappedIteratorType end) + : BaseT(begin), mEnd(end) + { + this->findNextValid(); + } + + classof_iterator& operator++() + { + BaseT::operator++(); + this->findNextValid(); + return *this; + } + + ReturnTy operator*() const + { + return llvm::cast(*this->I); + } + +private: + void findNextValid() { + while (this->I != mEnd && !llvm::isa(*this->I)) { + BaseT::operator++(); + } + } + + WrappedIteratorType mEnd; +}; + +template +auto classof_range(WrappedIteratorType first, WrappedIteratorType last) + -> llvm::iterator_range> +{ + classof_iterator begin(first, last); + classof_iterator end(last, last); + + return llvm::make_range(begin, end); } -template -const typename std::unique_ptr::pointer derefUniquePtr(const std::unique_ptr& ptr) +template()))> +auto classof_range(Range&& range) + -> llvm::iterator_range> { - return ptr.get(); + return classof_range(std::begin(range), std::end(range)); } +/// A simple iterator adaptor that simplifies a smart pointer into a raw pointer. +/// Note that this class is mostly present to work around some implementation difficulties +/// in our Graph structure. Pointers returned by this iterator should never be stored. template< class BaseIterator, class ReturnTy = decltype(std::declval()->get())> class SmartPtrGetIterator : public llvm::iterator_adaptor_base< SmartPtrGetIterator, BaseIterator, - typename std::iterator_traits::iterator_category + typename std::iterator_traits::iterator_category, + ReturnTy > { public: - /*implicit*/ SmartPtrGetIterator(BaseIterator it) + explicit SmartPtrGetIterator(BaseIterator it) : SmartPtrGetIterator::iterator_adaptor_base(std::move(it)) {} - ReturnTy operator*() { return this->wrapped()->get(); } + ReturnTy operator*() const { return this->wrapped()->get(); } }; } // namespace gazer diff --git a/include/gazer/ADT/ScopedCache.h b/include/gazer/ADT/ScopedCache.h index ec949c2e..f0aa289e 100644 --- a/include/gazer/ADT/ScopedCache.h +++ b/include/gazer/ADT/ScopedCache.h @@ -21,6 +21,7 @@ #include #include +#include namespace gazer { diff --git a/include/gazer/Automaton/CallGraph.h b/include/gazer/Automaton/CallGraph.h index cc550c50..5307fc37 100644 --- a/include/gazer/Automaton/CallGraph.h +++ b/include/gazer/Automaton/CallGraph.h @@ -41,7 +41,7 @@ class CallGraph { friend class CallGraph; public: - Node(Cfa* cfa) + explicit Node(Cfa* cfa) : mCfa(cfa) {} @@ -65,10 +65,10 @@ class CallGraph std::vector mCallsToOthers; std::vector mCallsToThis; }; -public: + explicit CallGraph(AutomataSystem& system); - ~CallGraph(); + ~CallGraph() = default; /// Returns true if the given procedure is tail-recursive. That is, /// if it is recursive and the recursive calls only occur in diff --git a/include/gazer/Automaton/Cfa.h b/include/gazer/Automaton/Cfa.h index acc065fa..abe66ecb 100644 --- a/include/gazer/Automaton/Cfa.h +++ b/include/gazer/Automaton/Cfa.h @@ -30,6 +30,8 @@ #include #include +#include + namespace gazer { @@ -101,9 +103,8 @@ class Transition : public GraphEdge class AssignTransition final : public Transition { friend class Cfa; -protected: - AssignTransition(Location* source, Location* target, ExprPtr guard, std::vector assignments); + AssignTransition(Location* source, Location* target, ExprPtr guard, std::vector assignments); public: using iterator = std::vector::const_iterator; iterator begin() const { return mAssignments.begin(); } @@ -126,7 +127,7 @@ class AssignTransition final : public Transition class CallTransition final : public Transition { friend class Cfa; -protected: + CallTransition( Location* source, Location* target, ExprPtr guard, @@ -182,9 +183,8 @@ class Cfa final : public Graph Cfa(const Cfa&) = delete; Cfa& operator=(const Cfa&) = delete; -public: - //===----------------------------------------------------------------------===// // Locations and edges + //===----------------------------------------------------------------------===// Location* createLocation(); Location* createErrorLocation(); @@ -233,28 +233,26 @@ class Cfa final : public Graph size_t getNumErrors() const { return mErrorFieldExprs.size(); } // Variable iterators - using var_iterator = boost::indirect_iterator::iterator>; + using var_iterator = std::vector::iterator; - var_iterator input_begin() { return mInputs.begin(); } - var_iterator input_end() { return mInputs.end(); } llvm::iterator_range inputs() { - return llvm::make_range(input_begin(), input_end()); + return llvm::make_range(mInputs.begin(), mInputs.end()); } - - var_iterator output_begin() { return mOutputs.begin(); } - var_iterator output_end() { return mOutputs.end(); } llvm::iterator_range outputs() { - return llvm::make_range(output_begin(), output_end()); + return llvm::make_range(mOutputs.begin(), mOutputs.end()); } - - var_iterator local_begin() { return mLocals.begin(); } - var_iterator local_end() { return mLocals.end(); } llvm::iterator_range locals() { - return llvm::make_range(local_begin(), local_end()); + return llvm::make_range(mLocals.begin(), mLocals.end()); + } + + /// Returns both inputs and locals. Note that all output variables are either inputs or locals, + /// so they will also be included in the iterator result. + decltype(auto) variables() { + return llvm::concat(inputs(), outputs()); } - //===----------------------------------------------------------------------===// // Others + //===----------------------------------------------------------------------===// llvm::StringRef getName() const { return mName; } Location* getEntry() const { return mEntry; } Location* getExit() const { return mExit; } @@ -400,7 +398,7 @@ template<> struct GraphTraits> : public GraphTraits>> { - static NodeRef getEntryNode(Inverse& cfa) { + static NodeRef getEntryNode(const Inverse& cfa) { return cfa.Graph.getExit(); } }; diff --git a/include/gazer/Automaton/CfaTransforms.h b/include/gazer/Automaton/CfaTransforms.h index c46a15c2..005ac7f7 100644 --- a/include/gazer/Automaton/CfaTransforms.h +++ b/include/gazer/Automaton/CfaTransforms.h @@ -30,14 +30,44 @@ namespace gazer { +// Cloning //===----------------------------------------------------------------------===// -/// Creates a clone of the given CFA with the given name. -/// Note that the clone shall be shallow one: automata called by the source -/// CFA shall be the same in the cloned one. -Cfa* CloneAutomaton(Cfa* cfa, llvm::StringRef name); +struct CfaCloneResult +{ + Cfa* clonedCfa; + llvm::DenseMap locToLocMap; + llvm::DenseMap varToVarMap; +}; +/// Creates a clone of \p cfa and inserts it into its parent automaton system. +CfaCloneResult CloneCfa(Cfa& cfa, const std::string& nameSuffix = "_clone"); +// Inlining //===----------------------------------------------------------------------===// + +struct CfaInlineResult +{ + llvm::DenseMap locToLocMap; + llvm::DenseMap edgeToEdgeMap; + llvm::DenseMap oldVarToNew; + std::vector newCalls; + llvm::DenseMap inlinedVariables; + llvm::DenseMap inlinedLocations; +}; + +/// Inlines the callee of a given call transition into the caller automaton and returns all the +/// necessary mappings to track the inlining result. Note that this function does *NOT* clean up +/// possibly removed components, you need to clean-up afterwards using `clearDisconnectedElements`. +CfaInlineResult InlineCall( + CallTransition* call, + Location* errorLoc, + Variable* errorFieldVariable, + const std::string& nameSuffix = "_inlined"); + + +// Translate recursive automata into cyclic +//===----------------------------------------------------------------------===// + struct RecursiveToCyclicResult { Location* errorLocation = nullptr; @@ -52,17 +82,6 @@ struct RecursiveToCyclicResult /// transformed into the input format of a different verifier. RecursiveToCyclicResult TransformRecursiveToCyclic(Cfa* cfa); -//===----------------------------------------------------------------------===// -struct InlineResult -{ - llvm::DenseMap VariableMap; - llvm::DenseMap InlinedLocations; - llvm::SmallVector NewErrors; - std::vector NewLocations; -}; - -//void InlineCall(CallTransition* call, InlineResult& result, ExprBuilder& exprBuilder, llvm::Twine suffix = "_inlined"); - -} +} // namespace gazer #endif \ No newline at end of file diff --git a/include/gazer/Automaton/CfaUtils.h b/include/gazer/Automaton/CfaUtils.h index a0217db7..3d6a70a6 100644 --- a/include/gazer/Automaton/CfaUtils.h +++ b/include/gazer/Automaton/CfaUtils.h @@ -21,48 +21,77 @@ #include "gazer/Automaton/Cfa.h" #include -#include +#include namespace gazer { class ExprBuilder; -template, class Map = llvm::DenseMap> -void createTopologicalSort(Cfa& cfa, Seq& topoVec, Map* locNumbers = nullptr) +/// A class which contains the topological sort of a recursive CFA. The mapping is bi-directional: +/// the class knows the index of each location stored in the topological sort. +class CfaTopoSort { - auto poBegin = llvm::po_begin(cfa.getEntry()); - auto poEnd = llvm::po_end(cfa.getEntry()); +public: + CfaTopoSort() = default; + explicit CfaTopoSort(Cfa& cfa); + + CfaTopoSort(const CfaTopoSort&) = default; + CfaTopoSort& operator=(const CfaTopoSort&) = default; + + Location* operator[](size_t idx) const; + size_t indexOf(Location* location) const; + size_t size() const { return mLocations.size(); } - topoVec.insert(topoVec.end(), poBegin, poEnd); - std::reverse(topoVec.begin(), topoVec.end()); + using iterator = std::vector::const_iterator; + iterator begin() const { return mLocations.begin(); } + iterator end() const { return mLocations.end(); } - if (locNumbers != nullptr) { - for (size_t i = 0; i < topoVec.size(); ++i) { - (*locNumbers)[topoVec[i]] = i; + template + iterator insert(size_t idx, LocationIterator first, LocationIterator second) + { + auto pos = std::next(mLocations.begin(), idx); + auto insertPos = mLocations.insert(pos, first, second); + + for (auto it = insertPos, ie = mLocations.end(); it != ie; ++it) { + size_t currIdx = std::distance(mLocations.begin(), it); + mLocNumbers[*it] = currIdx; } + + return insertPos; } -} + +private: + std::vector mLocations; + llvm::DenseMap mLocNumbers; +}; /// Class for calculating verification path conditions. class PathConditionCalculator { public: + /// Constructs a new path condition calculator. + /// + /// \param topo The topological sort to use. + /// \param builder An expression builder instance. + /// \param calls A function which tells the translation process how to represent call + /// transitions in the formula. + /// \param preds If non-null, the formula generation process will insert predecessor information + /// into the generated formula. The function passed here will be invoked on each generated + /// predecessor expression, so the caller may handle it (e.g. insert it into a map). PathConditionCalculator( - const std::vector& topo, + const CfaTopoSort& topo, ExprBuilder& builder, - std::function index, std::function calls, std::function preds = nullptr ); -public: + /// Calculates the reachability condition between \p source and \p target. ExprPtr encode(Location* source, Location* target); private: - const std::vector& mTopo; + const CfaTopoSort& mTopo; ExprBuilder& mExprBuilder; - std::function mIndex; std::function mCalls; std::function mPredecessors; unsigned mPredIdx = 0; @@ -77,19 +106,21 @@ class PathConditionCalculator /// entry location if empty. Location* findLowestCommonDominator( const std::vector& targets, - const std::vector& topo, - std::function index, + const CfaTopoSort& topo, Location* start = nullptr ); /// Returns the highest common post-dominator of each transition in \p targets. Location* findHighestCommonPostDominator( const std::vector& targets, - const std::vector& topo, - std::function index, + const CfaTopoSort& topo, Location* start ); -} +/// Replaces all of the callee's variables in \p expr with the actual call site arguments found in \p call. +ExprPtr applyCallTransitionCallingContext(CallTransition *call, const ExprPtr &expr, ExprBuilder& exprBuilder); + +} // namespace gazer + #endif diff --git a/include/gazer/Core/Decl.h b/include/gazer/Core/Decl.h index 6a58c8d4..a458f9d2 100644 --- a/include/gazer/Core/Decl.h +++ b/include/gazer/Core/Decl.h @@ -45,7 +45,8 @@ class Decl : mKind(kind), mType(type) {} -protected: + // Fields + //===------------------------------------------------------------------===// const DeclKind mKind; Type& mType; }; @@ -81,7 +82,7 @@ class Variable final : public Decl class VariableAssignment final { public: - VariableAssignment(); + VariableAssignment() = default; VariableAssignment(Variable *variable, ExprPtr value); bool operator==(const VariableAssignment& other) const; @@ -93,8 +94,8 @@ class VariableAssignment final void print(llvm::raw_ostream& os) const; private: - Variable* mVariable; - ExprPtr mValue; + Variable* mVariable = nullptr; + ExprPtr mValue = nullptr; }; llvm::raw_ostream& operator<<(llvm::raw_ostream& os, const Variable& variable); diff --git a/include/gazer/Core/Expr.h b/include/gazer/Core/Expr.h index 50a7b636..02c43867 100644 --- a/include/gazer/Core/Expr.h +++ b/include/gazer/Core/Expr.h @@ -191,7 +191,7 @@ class Expr Type& mType; private: - mutable unsigned mRefCount; + mutable unsigned mRefCount = 0; Expr* mNextPtr = nullptr; mutable size_t mHashCode = 0; }; @@ -221,9 +221,7 @@ llvm::raw_ostream& operator<<(llvm::raw_ostream& os, const Expr& expr); class AtomicExpr : public Expr { protected: - AtomicExpr(Expr::ExprKind kind, Type& type) - : Expr(kind, type) - {} + using Expr::Expr; public: static bool classof(const Expr* expr) { return expr->getKind() >= FirstAtomic && expr->getKind() <= LastAtomic; @@ -248,6 +246,7 @@ class LiteralExpr : public AtomicExpr } }; +/// Represents a reference to a named (or unnamed) variable. class VarRefExpr final : public Expr { friend class Variable; @@ -306,7 +305,6 @@ class NonNullaryExpr : public Expr size_t getNumOperands() const { return mOperands.size(); } ExprPtr getOperand(size_t idx) const { return mOperands[idx]; } -public: static bool classof(const Expr* expr) { return expr->getKind() >= FirstUnary; } @@ -326,13 +324,13 @@ namespace llvm template struct simplify_type> { - typedef T* SimpleType; + using SimpleType = T*; static SimpleType getSimplifiedValue(gazer::ExprRef &Val) { return Val.get(); } }; template struct simplify_type> { - typedef T* SimpleType; + using SimpleType = T*; static SimpleType getSimplifiedValue(const gazer::ExprRef &Val) { return Val.get(); } }; diff --git a/include/gazer/Core/Expr/ConstantFolder.h b/include/gazer/Core/Expr/ConstantFolder.h deleted file mode 100644 index 6d6f541b..00000000 --- a/include/gazer/Core/Expr/ConstantFolder.h +++ /dev/null @@ -1,114 +0,0 @@ -//==- ConstantFolder.h - Expression constant folding ------------*- C++ -*--==// -// -// Copyright 2019 Contributors to the Gazer project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -#ifndef GAZER_CORE_EXPR_CONSTANTFOLDER_H -#define GAZER_CORE_EXPR_CONSTANTFOLDER_H - -#include "gazer/Core/Expr.h" - -#include - -namespace gazer -{ - -class ConstantFolder -{ -public: - static ExprPtr Not(const ExprPtr& op); - static ExprPtr ZExt(const ExprPtr& op, BvType& type); - static ExprPtr SExt(const ExprPtr& op, BvType& type); - static ExprPtr Trunc(const ExprPtr& op, BvType& type); - static ExprPtr Extract(const ExprPtr& op, unsigned offset, unsigned width); - - //--- Binary ---// - static ExprPtr Add(const ExprPtr& left, const ExprPtr& right); - static ExprPtr Sub(const ExprPtr& left, const ExprPtr& right); - static ExprPtr Mul(const ExprPtr& left, const ExprPtr& right); - static ExprPtr BvSDiv(const ExprPtr& left, const ExprPtr& right); - static ExprPtr BvUDiv(const ExprPtr& left, const ExprPtr& right); - static ExprPtr BvSRem(const ExprPtr& left, const ExprPtr& right); - static ExprPtr BvURem(const ExprPtr& left, const ExprPtr& right); - - static ExprPtr Shl(const ExprPtr& left, const ExprPtr& right); - static ExprPtr LShr(const ExprPtr& left, const ExprPtr& right); - static ExprPtr AShr(const ExprPtr& left, const ExprPtr& right); - static ExprPtr BvAnd(const ExprPtr& left, const ExprPtr& right); - static ExprPtr BvOr(const ExprPtr& left, const ExprPtr& right); - static ExprPtr BvXor(const ExprPtr& left, const ExprPtr& right); - - static ExprPtr And(const ExprVector& vector); - static ExprPtr Or(const ExprVector& vector); - - static ExprPtr And(const ExprPtr& left, const ExprPtr& right) { - return And({left, right}); - } - static ExprPtr Or(const ExprPtr& left, const ExprPtr& right) { - return Or({left, right}); - } - - template - ExprPtr And(InputIterator begin, InputIterator end) { - return And(ExprVector(begin, end)); - } - template - ExprPtr Or(InputIterator begin, InputIterator end) { - return Or(ExprVector(begin, end)); - } - - static ExprPtr Xor(const ExprPtr& left, const ExprPtr& right); - static ExprPtr Imply(const ExprPtr& left, const ExprPtr& right); - - static ExprPtr Eq(const ExprPtr& left, const ExprPtr& right); - static ExprPtr NotEq(const ExprPtr& left, const ExprPtr& right); - - static ExprPtr Lt(const ExprPtr& left, const ExprPtr& right); - static ExprPtr LtEq(const ExprPtr& left, const ExprPtr& right); - static ExprPtr Gt(const ExprPtr& left, const ExprPtr& right); - static ExprPtr GtEq(const ExprPtr& left, const ExprPtr& right); - - static ExprPtr BvSLt(const ExprPtr& left, const ExprPtr& right); - static ExprPtr BvSLtEq(const ExprPtr& left, const ExprPtr& right); - static ExprPtr BvSGt(const ExprPtr& left, const ExprPtr& right); - static ExprPtr BvSGtEq(const ExprPtr& left, const ExprPtr& right); - - static ExprPtr BvULt(const ExprPtr& left, const ExprPtr& right); - static ExprPtr BvULtEq(const ExprPtr& left, const ExprPtr& right); - static ExprPtr BvUGt(const ExprPtr& left, const ExprPtr& right); - static ExprPtr BvUGtEq(const ExprPtr& left, const ExprPtr& right); - - //--- Floating point ---// - static ExprPtr FIsNan(const ExprPtr& op); - static ExprPtr FIsInf(const ExprPtr& op); - - static ExprPtr FAdd(const ExprPtr& left, const ExprPtr& right, llvm::APFloat::roundingMode rm); - static ExprPtr FSub(const ExprPtr& left, const ExprPtr& right, llvm::APFloat::roundingMode rm); - static ExprPtr FMul(const ExprPtr& left, const ExprPtr& right, llvm::APFloat::roundingMode rm); - static ExprPtr FDiv(const ExprPtr& left, const ExprPtr& right, llvm::APFloat::roundingMode rm); - - static ExprPtr FEq(const ExprPtr& left, const ExprPtr& right); - static ExprPtr FGt(const ExprPtr& left, const ExprPtr& right); - static ExprPtr FGtEq(const ExprPtr& left, const ExprPtr& right); - static ExprPtr FLt(const ExprPtr& left, const ExprPtr& right); - static ExprPtr FLtEq(const ExprPtr& left, const ExprPtr& right); - - //--- Ternary ---// - static ExprPtr Select(const ExprPtr& condition, const ExprPtr& then, const ExprPtr& elze); -}; - -} - -#endif \ No newline at end of file diff --git a/include/gazer/Core/Expr/ExprBuilder.h b/include/gazer/Core/Expr/ExprBuilder.h index 3895d6a3..bec7c194 100644 --- a/include/gazer/Core/Expr/ExprBuilder.h +++ b/include/gazer/Core/Expr/ExprBuilder.h @@ -24,7 +24,7 @@ namespace llvm { class APInt; -} +} // namespace llvm namespace gazer { @@ -157,14 +157,15 @@ class ExprBuilder virtual ExprPtr And(const ExprVector& vector) { return AndExpr::Create(vector); } virtual ExprPtr Or(const ExprVector& vector) { return OrExpr::Create(vector); } - template - ExprPtr And(const ExprRef& left, const ExprRef& right) { - return this->And({left, right}); + template + ExprPtr And(const First& first, const Second& second, const Args&... args) + { + return this->And(std::vector{first, second, args...}); } - template - ExprPtr Or(const ExprRef& left, const ExprRef& right) { - return this->Or({left, right}); + template + ExprPtr Or(const First& first, const Second& second, const Args&... args) { + return this->Or(std::vector{first, second, args...}); } ExprPtr Xor(const ExprPtr& left, const ExprPtr& right) @@ -285,11 +286,12 @@ class ExprBuilder return ArrayReadExpr::Create(array, index); } + ExprPtr Tuple(const ExprVector& elems); + template ExprPtr Tuple(const First& first, const Second& second, const Tail&... exprs) { - TupleType& type = TupleType::Get(first->getType(), second->getType(), exprs->getType()...); - return this->createTupleConstructor(type, {first, second, exprs...}); + return this->Tuple({first, second, exprs...}); } /// Constructs a new tuple based on \p tuple, where the element at \p index @@ -316,6 +318,6 @@ std::unique_ptr CreateExprBuilder(GazerContext& context); /// some basic simplifications. std::unique_ptr CreateFoldingExprBuilder(GazerContext& context); -} +} // namespace gazer #endif diff --git a/include/gazer/Core/Expr/ExprEvaluator.h b/include/gazer/Core/Expr/ExprEvaluator.h index 0ae76999..8191e2cd 100644 --- a/include/gazer/Core/Expr/ExprEvaluator.h +++ b/include/gazer/Core/Expr/ExprEvaluator.h @@ -30,14 +30,14 @@ class ExprEvaluator { public: virtual ExprRef evaluate(const ExprPtr& expr) = 0; + virtual ~ExprEvaluator() = default; }; /// Base class for expression evaluation implementations. /// This abstract class provides all methods to evaluate an expression, except for /// the means of acquiring the value of a variable. -class ExprEvaluatorBase : public ExprEvaluator, private ExprWalker> +class ExprEvaluatorBase : public ExprEvaluator, public ExprWalker> { - friend class ExprWalker>; public: ExprRef evaluate(const ExprPtr& expr) override { return this->walk(expr); @@ -47,96 +47,96 @@ class ExprEvaluatorBase : public ExprEvaluator, private ExprWalker getVariableValue(Variable& variable) = 0; private: - ExprRef visitExpr(const ExprPtr& expr); + ExprRef visitExpr(const ExprPtr& expr) override; // Nullary - ExprRef visitUndef(const ExprRef& expr); - ExprRef visitLiteral(const ExprRef& expr); - ExprRef visitVarRef(const ExprRef& expr); + ExprRef visitUndef(const ExprRef& expr) override; + ExprRef visitLiteral(const ExprRef& expr) override; + ExprRef visitVarRef(const ExprRef& expr) override; // Unary - ExprRef visitNot(const ExprRef& expr); - ExprRef visitZExt(const ExprRef& expr); - ExprRef visitSExt(const ExprRef& expr); - ExprRef visitExtract(const ExprRef& expr); - ExprRef visitBvConcat(const ExprRef& expr); + ExprRef visitNot(const ExprRef& expr) override; + ExprRef visitZExt(const ExprRef& expr) override; + ExprRef visitSExt(const ExprRef& expr) override; + ExprRef visitExtract(const ExprRef& expr) override; + ExprRef visitBvConcat(const ExprRef& expr) override; // Binary - ExprRef visitAdd(const ExprRef& expr); - ExprRef visitSub(const ExprRef& expr); - ExprRef visitMul(const ExprRef& expr); - ExprRef visitDiv(const ExprRef& expr); - ExprRef visitMod(const ExprRef& expr); - ExprRef visitRem(const ExprRef& expr); - - ExprRef visitBvSDiv(const ExprRef& expr); - ExprRef visitBvUDiv(const ExprRef& expr); - ExprRef visitBvSRem(const ExprRef& expr); - ExprRef visitBvURem(const ExprRef& expr); - - ExprRef visitShl(const ExprRef& expr); - ExprRef visitLShr(const ExprRef& expr); - ExprRef visitAShr(const ExprRef& expr); - ExprRef visitBvAnd(const ExprRef& expr); - ExprRef visitBvOr(const ExprRef& expr); - ExprRef visitBvXor(const ExprRef& expr); + ExprRef visitAdd(const ExprRef& expr) override; + ExprRef visitSub(const ExprRef& expr) override; + ExprRef visitMul(const ExprRef& expr) override; + ExprRef visitDiv(const ExprRef& expr) override; + ExprRef visitMod(const ExprRef& expr) override; + ExprRef visitRem(const ExprRef& expr) override; + + ExprRef visitBvSDiv(const ExprRef& expr) override; + ExprRef visitBvUDiv(const ExprRef& expr) override; + ExprRef visitBvSRem(const ExprRef& expr) override; + ExprRef visitBvURem(const ExprRef& expr) override; + + ExprRef visitShl(const ExprRef& expr) override; + ExprRef visitLShr(const ExprRef& expr) override; + ExprRef visitAShr(const ExprRef& expr) override; + ExprRef visitBvAnd(const ExprRef& expr) override; + ExprRef visitBvOr(const ExprRef& expr) override; + ExprRef visitBvXor(const ExprRef& expr) override; // Logic - ExprRef visitAnd(const ExprRef& expr); - ExprRef visitOr(const ExprRef& expr); - ExprRef visitImply(const ExprRef& expr); + ExprRef visitAnd(const ExprRef& expr) override; + ExprRef visitOr(const ExprRef& expr) override; + ExprRef visitImply(const ExprRef& expr) override; // Compare - ExprRef visitEq(const ExprRef& expr); - ExprRef visitNotEq(const ExprRef& expr); + ExprRef visitEq(const ExprRef& expr) override; + ExprRef visitNotEq(const ExprRef& expr) override; - ExprRef visitLt(const ExprRef& expr); - ExprRef visitLtEq(const ExprRef& expr); - ExprRef visitGt(const ExprRef& expr); - ExprRef visitGtEq(const ExprRef& expr); + ExprRef visitLt(const ExprRef& expr) override; + ExprRef visitLtEq(const ExprRef& expr) override; + ExprRef visitGt(const ExprRef& expr) override; + ExprRef visitGtEq(const ExprRef& expr) override; - ExprRef visitBvSLt(const ExprRef& expr); - ExprRef visitBvSLtEq(const ExprRef& expr); - ExprRef visitBvSGt(const ExprRef& expr); - ExprRef visitBvSGtEq(const ExprRef& expr); + ExprRef visitBvSLt(const ExprRef& expr) override; + ExprRef visitBvSLtEq(const ExprRef& expr) override; + ExprRef visitBvSGt(const ExprRef& expr) override; + ExprRef visitBvSGtEq(const ExprRef& expr) override; - ExprRef visitBvULt(const ExprRef& expr); - ExprRef visitBvULtEq(const ExprRef& expr); - ExprRef visitBvUGt(const ExprRef& expr); - ExprRef visitBvUGtEq(const ExprRef& expr); + ExprRef visitBvULt(const ExprRef& expr) override; + ExprRef visitBvULtEq(const ExprRef& expr) override; + ExprRef visitBvUGt(const ExprRef& expr) override; + ExprRef visitBvUGtEq(const ExprRef& expr) override; // Floating-point queries - ExprRef visitFIsNan(const ExprRef& expr); - ExprRef visitFIsInf(const ExprRef& expr); + ExprRef visitFIsNan(const ExprRef& expr) override; + ExprRef visitFIsInf(const ExprRef& expr) override; // Floating-point arithmetic - ExprRef visitFAdd(const ExprRef& expr); - ExprRef visitFSub(const ExprRef& expr); - ExprRef visitFMul(const ExprRef& expr); - ExprRef visitFDiv(const ExprRef& expr); + ExprRef visitFAdd(const ExprRef& expr) override; + ExprRef visitFSub(const ExprRef& expr) override; + ExprRef visitFMul(const ExprRef& expr) override; + ExprRef visitFDiv(const ExprRef& expr) override; // Floating-point compare - ExprRef visitFEq(const ExprRef& expr); - ExprRef visitFGt(const ExprRef& expr); - ExprRef visitFGtEq(const ExprRef& expr); - ExprRef visitFLt(const ExprRef& expr); - ExprRef visitFLtEq(const ExprRef& expr); + ExprRef visitFEq(const ExprRef& expr) override; + ExprRef visitFGt(const ExprRef& expr) override; + ExprRef visitFGtEq(const ExprRef& expr) override; + ExprRef visitFLt(const ExprRef& expr) override; + ExprRef visitFLtEq(const ExprRef& expr) override; // Floating-point casts - ExprRef visitFCast(const ExprRef& expr); - ExprRef visitSignedToFp(const ExprRef& expr); - ExprRef visitUnsignedToFp(const ExprRef& expr); - ExprRef visitFpToSigned(const ExprRef& expr); - ExprRef visitFpToUnsigned(const ExprRef& expr); - ExprRef visitFpToBv(const ExprRef& expr); - ExprRef visitBvToFp(const ExprRef& expr); + ExprRef visitFCast(const ExprRef& expr) override; + ExprRef visitSignedToFp(const ExprRef& expr) override; + ExprRef visitUnsignedToFp(const ExprRef& expr) override; + ExprRef visitFpToSigned(const ExprRef& expr) override; + ExprRef visitFpToUnsigned(const ExprRef& expr) override; + ExprRef visitFpToBv(const ExprRef& expr) override; + ExprRef visitBvToFp(const ExprRef& expr) override; // Ternary - ExprRef visitSelect(const ExprRef& expr); + ExprRef visitSelect(const ExprRef& expr) override; // Arrays - ExprRef visitArrayRead(const ExprRef& expr); - ExprRef visitArrayWrite(const ExprRef& expr); + ExprRef visitArrayRead(const ExprRef& expr) override; + ExprRef visitArrayWrite(const ExprRef& expr) override; }; /// Evaluates expressions based on a Valuation object @@ -154,6 +154,6 @@ class ValuationExprEvaluator : public ExprEvaluatorBase const Valuation& mValuation; }; -} +} // namespace gazer #endif \ No newline at end of file diff --git a/include/gazer/Core/Expr/ExprKind.def b/include/gazer/Core/Expr/ExprKind.def index f54628f8..097b0732 100644 --- a/include/gazer/Core/Expr/ExprKind.def +++ b/include/gazer/Core/Expr/ExprKind.def @@ -22,97 +22,152 @@ #error "Including ExprKind.def without defining GAZER_EXPR_KIND is not allowed!" #endif +// The following macros can be used to fine-tune some includes on expression kinds. +// We have two main categories: one for nullary and one for non-nullary expressions. +// Non-nullary expression kinds are also split into unary, binary, and multiary expressions. +// Note that here "binary" means that it only takes two arguments and *not* two expressions, +// i.e. "Add(left, right)" is binary, whereas "FAdd(left, right, roundingMode)" is not. +#ifndef GAZER_NULLARY_EXPR_KIND + #define GAZER_NULLARY_DEFINED_HERE + #define GAZER_NULLARY_EXPR_KIND(X) GAZER_EXPR_KIND(X) +#endif + +#ifndef GAZER_NON_NULLARY_EXPR_KIND + #define GAZER_NON_NULLARY_DEFINED_HERE + #define GAZER_NON_NULLARY_EXPR_KIND(X) GAZER_EXPR_KIND(X) +#endif + +#ifndef GAZER_UNARY_EXPR_KIND + #define GAZER_UNARY_DEFINED_HERE + #define GAZER_UNARY_EXPR_KIND(X) GAZER_NON_NULLARY_EXPR_KIND(X) +#endif + +#ifndef GAZER_BINARY_EXPR_KIND + #define GAZER_BINARY_DEFINED_HERE + #define GAZER_BINARY_EXPR_KIND(X) GAZER_NON_NULLARY_EXPR_KIND(X) +#endif + +#ifndef GAZER_MULTIARY_EXPR_KIND + #define GAZER_MULTIARY_DEFINED_HERE + #define GAZER_MULTIARY_EXPR_KIND(X) GAZER_NON_NULLARY_EXPR_KIND(X) +#endif + // Nullary expressions -GAZER_EXPR_KIND(Undef) // Havoc -GAZER_EXPR_KIND(Literal) // Literal expression of a type -GAZER_EXPR_KIND(VarRef) // Variable reference +GAZER_NULLARY_EXPR_KIND(Undef) // Havoc +GAZER_NULLARY_EXPR_KIND(Literal) // Literal expression of a type +GAZER_NULLARY_EXPR_KIND(VarRef) // Variable reference // Unary boolean operators -GAZER_EXPR_KIND(Not) // Boolean negation +GAZER_UNARY_EXPR_KIND(Not) // Boolean negation // Unary bit-vector operators -GAZER_EXPR_KIND(ZExt) // Zero-extend to another bit-vector type -GAZER_EXPR_KIND(SExt) // Sign-extend to another bit-vector type -GAZER_EXPR_KIND(Extract) // Extract bits from a bit-vector +GAZER_UNARY_EXPR_KIND(ZExt) // Zero-extend to another bit-vector type +GAZER_UNARY_EXPR_KIND(SExt) // Sign-extend to another bit-vector type +GAZER_NON_NULLARY_EXPR_KIND(Extract) // Extract bits from a bit-vector // Overloaded binary operators -GAZER_EXPR_KIND(Add) // Addition for Int, Real and Bv types -GAZER_EXPR_KIND(Sub) // Substraction for Int, Real and Bv types -GAZER_EXPR_KIND(Mul) // Multiplication for Int, Real and Bv types -GAZER_EXPR_KIND(Div) // Division operator for arithmetic types -GAZER_EXPR_KIND(Mod) // Modulo operator for arithmetic types -GAZER_EXPR_KIND(Rem) // Remainder operator for arithmetic types +GAZER_BINARY_EXPR_KIND(Add) // Addition for Int, Real and Bv types +GAZER_BINARY_EXPR_KIND(Sub) // Substraction for Int, Real and Bv types +GAZER_BINARY_EXPR_KIND(Mul) // Multiplication for Int, Real and Bv types +GAZER_BINARY_EXPR_KIND(Div) // Division operator for arithmetic types +GAZER_BINARY_EXPR_KIND(Mod) // Modulo operator for arithmetic types +GAZER_BINARY_EXPR_KIND(Rem) // Remainder operator for arithmetic types // Binary operations for bit-vectors -GAZER_EXPR_KIND(BvSDiv) // Signed division for bit-vectors -GAZER_EXPR_KIND(BvUDiv) // Unsigned division for bit-vectors -GAZER_EXPR_KIND(BvSRem) // Signed remainder for bit-vectors -GAZER_EXPR_KIND(BvURem) // Unsigned remainder for bit-vectors -GAZER_EXPR_KIND(Shl) // Binary shift left -GAZER_EXPR_KIND(LShr) // Logical shift right -GAZER_EXPR_KIND(AShr) // Arithmetic shift right -GAZER_EXPR_KIND(BvAnd) // Binary AND for bit-vectors -GAZER_EXPR_KIND(BvOr) // Binary OR for bit-vectors -GAZER_EXPR_KIND(BvXor) // Binary XOR for bit-vectors -GAZER_EXPR_KIND(BvConcat) // Bit-vector concatenation +GAZER_BINARY_EXPR_KIND(BvSDiv) // Signed division for bit-vectors +GAZER_BINARY_EXPR_KIND(BvUDiv) // Unsigned division for bit-vectors +GAZER_BINARY_EXPR_KIND(BvSRem) // Signed remainder for bit-vectors +GAZER_BINARY_EXPR_KIND(BvURem) // Unsigned remainder for bit-vectors +GAZER_BINARY_EXPR_KIND(Shl) // Binary shift left +GAZER_BINARY_EXPR_KIND(LShr) // Logical shift right +GAZER_BINARY_EXPR_KIND(AShr) // Arithmetic shift right +GAZER_BINARY_EXPR_KIND(BvAnd) // Binary AND for bit-vectors +GAZER_BINARY_EXPR_KIND(BvOr) // Binary OR for bit-vectors +GAZER_BINARY_EXPR_KIND(BvXor) // Binary XOR for bit-vectors +GAZER_BINARY_EXPR_KIND(BvConcat) // Bit-vector concatenation // Binary logic -GAZER_EXPR_KIND(And) // Multiary AND operator for booleans -GAZER_EXPR_KIND(Or) // Multiary OR operator for booleans -GAZER_EXPR_KIND(Imply) // Binary implication for booleans +GAZER_MULTIARY_EXPR_KIND(And) // Multiary AND operator for booleans +GAZER_MULTIARY_EXPR_KIND(Or) // Multiary OR operator for booleans +GAZER_BINARY_EXPR_KIND(Imply) // Binary implication for booleans // Compare -GAZER_EXPR_KIND(Eq) // Equality overloaded for all types -GAZER_EXPR_KIND(NotEq) -GAZER_EXPR_KIND(Lt) // Arithmetic less than -GAZER_EXPR_KIND(LtEq) // Arithmetic less than or equal -GAZER_EXPR_KIND(Gt) // Arithmetic greater than -GAZER_EXPR_KIND(GtEq) // Arithmetic greater than or equal +GAZER_BINARY_EXPR_KIND(Eq) // Equality overloaded for all types +GAZER_BINARY_EXPR_KIND(NotEq) +GAZER_BINARY_EXPR_KIND(Lt) // Arithmetic less than +GAZER_BINARY_EXPR_KIND(LtEq) // Arithmetic less than or equal +GAZER_BINARY_EXPR_KIND(Gt) // Arithmetic greater than +GAZER_BINARY_EXPR_KIND(GtEq) // Arithmetic greater than or equal // Bit-vector compare -GAZER_EXPR_KIND(BvSLt) // Bitvector signed less than or equal -GAZER_EXPR_KIND(BvSLtEq) // Bitvector signed less than or equal -GAZER_EXPR_KIND(BvSGt) // Bitvector signed greater than -GAZER_EXPR_KIND(BvSGtEq) // Bitvector signed greater than or equal -GAZER_EXPR_KIND(BvULt) // Bitvector unsigned less than -GAZER_EXPR_KIND(BvULtEq) // Bitvector unsigned less than or equal -GAZER_EXPR_KIND(BvUGt) // Bitvector unsigned greater than -GAZER_EXPR_KIND(BvUGtEq) // Bitvector unsigned greater than or equal +GAZER_BINARY_EXPR_KIND(BvSLt) // Bitvector signed less than or equal +GAZER_BINARY_EXPR_KIND(BvSLtEq) // Bitvector signed less than or equal +GAZER_BINARY_EXPR_KIND(BvSGt) // Bitvector signed greater than +GAZER_BINARY_EXPR_KIND(BvSGtEq) // Bitvector signed greater than or equal +GAZER_BINARY_EXPR_KIND(BvULt) // Bitvector unsigned less than +GAZER_BINARY_EXPR_KIND(BvULtEq) // Bitvector unsigned less than or equal +GAZER_BINARY_EXPR_KIND(BvUGt) // Bitvector unsigned greater than +GAZER_BINARY_EXPR_KIND(BvUGtEq) // Bitvector unsigned greater than or equal // Floating-point unary -GAZER_EXPR_KIND(FIsNan) -GAZER_EXPR_KIND(FIsInf) +GAZER_UNARY_EXPR_KIND(FIsNan) +GAZER_UNARY_EXPR_KIND(FIsInf) // Floating-point casts -GAZER_EXPR_KIND(FCast) -GAZER_EXPR_KIND(SignedToFp) -GAZER_EXPR_KIND(UnsignedToFp) -GAZER_EXPR_KIND(FpToSigned) -GAZER_EXPR_KIND(FpToUnsigned) -GAZER_EXPR_KIND(FpToBv) // Bit-cast a floating-point value to a bit-vector -GAZER_EXPR_KIND(BvToFp) // Bit-cast a bit-vector into a floating-point value +GAZER_NON_NULLARY_EXPR_KIND(FCast) +GAZER_NON_NULLARY_EXPR_KIND(SignedToFp) +GAZER_NON_NULLARY_EXPR_KIND(UnsignedToFp) +GAZER_NON_NULLARY_EXPR_KIND(FpToSigned) +GAZER_NON_NULLARY_EXPR_KIND(FpToUnsigned) +GAZER_NON_NULLARY_EXPR_KIND(FpToBv) // Bit-cast a floating-point value to a bit-vector +GAZER_NON_NULLARY_EXPR_KIND(BvToFp) // Bit-cast a bit-vector into a floating-point value // Floating point binary -GAZER_EXPR_KIND(FAdd) -GAZER_EXPR_KIND(FSub) -GAZER_EXPR_KIND(FMul) -GAZER_EXPR_KIND(FDiv) +GAZER_NON_NULLARY_EXPR_KIND(FAdd) +GAZER_NON_NULLARY_EXPR_KIND(FSub) +GAZER_NON_NULLARY_EXPR_KIND(FMul) +GAZER_NON_NULLARY_EXPR_KIND(FDiv) // Floating point compare // All comparisons evaluate to false if either operand is NaN -GAZER_EXPR_KIND(FEq) -GAZER_EXPR_KIND(FGt) -GAZER_EXPR_KIND(FGtEq) -GAZER_EXPR_KIND(FLt) -GAZER_EXPR_KIND(FLtEq) +GAZER_BINARY_EXPR_KIND(FEq) +GAZER_BINARY_EXPR_KIND(FGt) +GAZER_BINARY_EXPR_KIND(FGtEq) +GAZER_BINARY_EXPR_KIND(FLt) +GAZER_BINARY_EXPR_KIND(FLtEq) // Ternary -GAZER_EXPR_KIND(Select) // Ternary if-then-else (ITE) operator +GAZER_NON_NULLARY_EXPR_KIND(Select) // Ternary if-then-else (ITE) operator // Arrays -GAZER_EXPR_KIND(ArrayRead) -GAZER_EXPR_KIND(ArrayWrite) +GAZER_NON_NULLARY_EXPR_KIND(ArrayRead) +GAZER_NON_NULLARY_EXPR_KIND(ArrayWrite) // Tuples -GAZER_EXPR_KIND(TupleSelect) -GAZER_EXPR_KIND(TupleConstruct) +GAZER_NON_NULLARY_EXPR_KIND(TupleSelect) +GAZER_NON_NULLARY_EXPR_KIND(TupleConstruct) + +#ifdef GAZER_NULLARY_DEFINED_HERE + #undef GAZER_NULLARY_EXPR_KIND + #undef GAZER_NULLARY_DEFINED_HERE +#endif + +#ifdef GAZER_NON_NULLARY_DEFINED_HERE + #undef GAZER_NON_NULLARY_EXPR_KIND + #undef GAZER_NON_NULLARY_DEFINED_HERE +#endif + +#ifdef GAZER_UNARY_DEFINED_HERE + #undef GAZER_UNARY_EXPR_KIND + #undef GAZER_UNARY_DEFINED_HERE +#endif + +#ifdef GAZER_BINARY_DEFINED_HERE + #undef GAZER_BINARY_EXPR_KIND + #undef GAZER_BINARY_DEFINED_HERE +#endif + +#ifdef GAZER_MULTIARY_DEFINED_HERE + #undef GAZER_MULTIARY_EXPR_KIND + #undef GAZER_MULTIARY_DEFINED_HERE +#endif diff --git a/include/gazer/Core/Expr/ExprRewrite.h b/include/gazer/Core/Expr/ExprRewrite.h index 7a639fd3..22d36617 100644 --- a/include/gazer/Core/Expr/ExprRewrite.h +++ b/include/gazer/Core/Expr/ExprRewrite.h @@ -24,62 +24,57 @@ namespace gazer { -/// Non-template dependent functionality of the expression rewriter. -class ExprRewriteBase -{ -protected: - ExprRewriteBase(ExprBuilder& builder) - : mExprBuilder(builder) - {} - - ExprPtr rewriteNonNullary(const ExprRef& expr, const ExprVector& ops); -protected: - ExprBuilder& mExprBuilder; -}; /// Base class for expression rewrite implementations. -template -class ExprRewrite : public ExprWalker, public ExprRewriteBase +class ExprRewrite : public ExprWalker { - friend class ExprWalker; public: explicit ExprRewrite(ExprBuilder& builder) - : ExprRewriteBase(builder) + : mExprBuilder(builder) {} + ExprPtr rewrite(const ExprPtr& expr) { + return this->walk(expr); + } + protected: - ExprPtr visitExpr(const ExprPtr& expr) { return expr; } + ExprPtr visitExpr(const ExprPtr& expr) override { return expr; } - ExprPtr visitNonNullary(const ExprRef& expr) + ExprPtr visitNonNullary(const ExprRef& expr) override { ExprVector ops(expr->getNumOperands(), nullptr); + for (size_t i = 0; i < expr->getNumOperands(); ++i) { ops[i] = this->getOperand(i); } return this->rewriteNonNullary(expr, ops); } + +private: + ExprPtr rewriteNonNullary(const ExprRef& expr, const ExprVector& ops); + + ExprBuilder& mExprBuilder; }; /// An expression rewriter that replaces certain variables with some /// given expression, according to the values set by operator[]. -class VariableExprRewrite : public ExprRewrite +class VariableExprRewrite : public ExprRewrite { - friend class ExprWalker; public: - explicit VariableExprRewrite(ExprBuilder& builder) - : ExprRewrite(builder) - {} - + using ExprRewrite::ExprRewrite; ExprPtr& operator[](Variable* variable); + ExprPtr& operator[](const ExprRef& expr) { + return operator[](&expr->getVariable()); + } protected: - ExprPtr visitVarRef(const ExprRef& expr); + ExprPtr visitVarRef(const ExprRef& expr) override; private: llvm::DenseMap mRewriteMap; }; -} +} // namespace gazer #endif diff --git a/include/gazer/Core/Expr/ExprUtils.h b/include/gazer/Core/Expr/ExprUtils.h index 91d3149b..6218d85b 100644 --- a/include/gazer/Core/Expr/ExprUtils.h +++ b/include/gazer/Core/Expr/ExprUtils.h @@ -23,12 +23,8 @@ namespace gazer { -unsigned ExprDepth(const ExprPtr& expr); - -void FormatPrintExpr(const ExprPtr& expr, llvm::raw_ostream& os); - void InfixPrintExpr(const ExprPtr& expr, llvm::raw_ostream& os, unsigned bvRadix = 10); -} +} // namespace gazer #endif \ No newline at end of file diff --git a/include/gazer/Core/Expr/ExprWalker.h b/include/gazer/Core/Expr/ExprWalker.h index 08838470..8470f41e 100644 --- a/include/gazer/Core/Expr/ExprWalker.h +++ b/include/gazer/Core/Expr/ExprWalker.h @@ -42,20 +42,16 @@ namespace gazer /// handleResult() functions. The former should return true if the cache /// was hit and set the found value. The latter should be used to insert /// new entries into the cache. -/// -/// \tparam DerivedT A Curiously Recurring Template Pattern (CRTP) parameter of -/// the derived class. +/// /// \tparam ReturnT The visit result. Must be a default-constructible and /// copy-constructible. /// \tparam SlabSize Slab size of the underlying stack allocator. -template +template class ExprWalker { static_assert(std::is_default_constructible_v, "ExprWalker return type must be default-constructible!"); - size_t tmp = 0; - struct Frame { ExprPtr mExpr; @@ -63,28 +59,35 @@ class ExprWalker Frame* mParent = nullptr; size_t mState = 0; llvm::SmallVector mVisitedOps; + size_t mDepth = 0; - Frame(ExprPtr expr, size_t index, Frame* parent) + Frame(ExprPtr expr, size_t index, Frame* parent, size_t depth) : mExpr(std::move(expr)), mIndex(index), mParent(parent), mVisitedOps( mExpr->isNullary() ? 0 : llvm::cast(mExpr)->getNumOperands() - ) + ), + mDepth(depth) {} bool isFinished() const { return mExpr->isNullary() || llvm::cast(mExpr)->getNumOperands() == mState; } + + bool isFresh() const + { + return mState == 0; + } }; -private: + Frame* createFrame(const ExprPtr& expr, size_t idx, Frame* parent) { size_t siz = sizeof(Frame); - void* ptr = mAllocator.Allocate(siz, alignof(Frame)); + void* ptr = mAllocator.Allocate(siz, llvm::Align(alignof(Frame))); - return new (ptr) Frame(expr, idx, parent); + return new (ptr) Frame(expr, idx, parent, parent == nullptr ? 0 : parent->mDepth); } void destroyFrame(Frame* frame) @@ -93,13 +96,11 @@ class ExprWalker mAllocator.Deallocate(frame, sizeof(Frame)); } -private: - Frame* mTop; + Frame* mTop = nullptr; GrowingStackAllocator mAllocator; public: ExprWalker() - : mTop(nullptr) { mAllocator.Init(); } @@ -107,27 +108,46 @@ class ExprWalker ExprWalker(const ExprWalker&) = delete; ExprWalker& operator=(ExprWalker&) = delete; - ~ExprWalker() = default; + virtual ~ExprWalker() = default; + +protected: + + /// If this function returns true, the walker will not visit \p expr + /// and will use the value contained in \p ret. + virtual bool shouldSkip(const ExprPtr& expr, ReturnT* ret) { + return false; + } + + /// This function is called by the walker if an actual visit took place + /// for \p expr. The visit result is contained in \p ret. + virtual void handleResult(const ExprPtr& expr, ReturnT& ret) { + // Do nothing by default. + } + + size_t currentDepth() const { + return mTop->mDepth; + } + + ExprPtr getCurrentParent() const { + return mTop->mParent == nullptr ? nullptr : mTop->mParent->mExpr; + } -public: ReturnT walk(const ExprPtr& expr) { - static_assert( - std::is_base_of_v, - "The derived type must be passed to the ExprWalker!" - ); - assert(mTop == nullptr); mTop = createFrame(expr, 0, nullptr); while (mTop != nullptr) { Frame* current = mTop; ReturnT ret; - bool shouldSkip = static_cast(this)->shouldSkip(current->mExpr, &ret); + bool shouldSkip = false; + if (current->isFresh()) { + shouldSkip = this->shouldSkip(current->mExpr, &ret); + } if (current->isFinished() || shouldSkip) { if (!shouldSkip) { ret = this->doVisit(current->mExpr); - static_cast(this)->handleResult(current->mExpr, ret); + this->handleResult(current->mExpr, ret); } Frame* parent = current->mParent; size_t idx = current->mIndex; @@ -154,7 +174,6 @@ class ExprWalker llvm_unreachable("Invalid walker state!"); } -protected: /// Returns the operand of index \p i in the topmost frame. [[nodiscard]] ReturnT getOperand(size_t i) const { @@ -163,20 +182,11 @@ class ExprWalker return mTop->mVisitedOps[i]; } -public: - /// If this function returns true, the walker will not visit \p expr - /// and will use the value contained in \p ret. - bool shouldSkip(const ExprPtr& expr, ReturnT* ret) { return false; } - - /// This function is called by the walker if an actual visit took place - /// for \p expr. The visit result is contained in \p ret. - void handleResult(const ExprPtr& expr, ReturnT& ret) {} - ReturnT doVisit(const ExprPtr& expr) { #define GAZER_EXPR_KIND(KIND) \ case Expr::KIND: \ - return static_cast(this)->visit##KIND( \ + return this->visit##KIND( \ llvm::cast(expr) \ ); \ @@ -189,24 +199,26 @@ class ExprWalker #undef GAZER_EXPR_KIND } - void visitExpr(const ExprPtr& expr) {} + // Overridable visitation methods + //==--------------------------------------------------------------------==// + + virtual ReturnT visitExpr(const ExprPtr& expr) = 0; - ReturnT visitNonNullary(const ExprRef& expr) { - return static_cast(this)->visitExpr(expr); + virtual ReturnT visitNonNullary(const ExprRef& expr) { + return this->visitExpr(expr); } // Nullary - ReturnT visitUndef(const ExprRef& expr) { - return static_cast(this)->visitExpr(expr); + virtual ReturnT visitUndef(const ExprRef& expr) { + return this->visitExpr(expr); } - ReturnT visitLiteral(const ExprRef& expr) + virtual ReturnT visitLiteral(const ExprRef& expr) { // Disambiguate here for each literal type - #define EXPR_LITERAL_CASE(TYPE) \ - case Type::TYPE##TypeID: \ - return static_cast(this) \ - ->visit##TYPE##Literal(expr_cast(expr)); \ + #define EXPR_LITERAL_CASE(TYPE) \ + case Type::TYPE##TypeID: \ + return this->visit##TYPE##Literal(expr_cast(expr)); \ switch (expr->getType().getTypeID()) { EXPR_LITERAL_CASE(Bool) @@ -226,242 +238,41 @@ class ExprWalker llvm_unreachable("Unknown literal expression kind!"); } - ReturnT visitVarRef(const ExprRef& expr) { - return static_cast(this)->visitExpr(expr); + virtual ReturnT visitVarRef(const ExprRef& expr) { + return this->visitExpr(expr); } // Literals - ReturnT visitBoolLiteral(const ExprRef& expr) { - return static_cast(this)->visitExpr(expr); - } - ReturnT visitIntLiteral(const ExprRef& expr) { - return static_cast(this)->visitExpr(expr); - } - ReturnT visitRealLiteral(const ExprRef& expr) { - return static_cast(this)->visitExpr(expr); - } - ReturnT visitBvLiteral(const ExprRef& expr) { - return static_cast(this)->visitExpr(expr); - } - ReturnT visitFloatLiteral(const ExprRef& expr) { - return static_cast(this)->visitExpr(expr); - } - ReturnT visitArrayLiteral(const ExprRef& expr) { - return static_cast(this)->visitExpr(expr); - } - - // Unary - ReturnT visitNot(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitZExt(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitSExt(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitExtract(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - - // Binary - ReturnT visitAdd(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitSub(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitMul(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitDiv(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitMod(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitRem(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - - ReturnT visitBvSDiv(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitBvUDiv(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitBvSRem(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitBvURem(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - - ReturnT visitShl(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitLShr(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitAShr(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitBvAnd(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitBvOr(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitBvXor(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitBvConcat(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - - // Logic - ReturnT visitAnd(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitOr(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitImply(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - - // Compare - ReturnT visitEq(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitNotEq(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); + virtual ReturnT visitBoolLiteral(const ExprRef& expr) { + return this->visitExpr(expr); } - - ReturnT visitLt(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitLtEq(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitGt(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitGtEq(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - - ReturnT visitBvSLt(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitBvSLtEq(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitBvSGt(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); + virtual ReturnT visitIntLiteral(const ExprRef& expr) { + return this->visitExpr(expr); } - ReturnT visitBvSGtEq(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); + virtual ReturnT visitRealLiteral(const ExprRef& expr) { + return this->visitExpr(expr); } - - ReturnT visitBvULt(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitBvULtEq(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); + virtual ReturnT visitBvLiteral(const ExprRef& expr) { + return this->visitExpr(expr); } - ReturnT visitBvUGt(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); + virtual ReturnT visitFloatLiteral(const ExprRef& expr) { + return this->visitExpr(expr); } - ReturnT visitBvUGtEq(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - - // Floating-point queries - ReturnT visitFIsNan(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitFIsInf(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - - // Floating-point casts - ReturnT visitFCast(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); + virtual ReturnT visitArrayLiteral(const ExprRef& expr) { + return this->visitExpr(expr); } - ReturnT visitSignedToFp(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitUnsignedToFp(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitFpToSigned(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitFpToUnsigned(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - - ReturnT visitFpToBv(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitBvToFp(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - - // Floating-point arithmetic - ReturnT visitFAdd(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitFSub(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitFMul(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitFDiv(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - - // Floating-point compare - ReturnT visitFEq(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitFGt(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitFGtEq(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitFLt(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - ReturnT visitFLtEq(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - - // Ternary - ReturnT visitSelect(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - - // Arrays - ReturnT visitArrayRead(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } - - ReturnT visitArrayWrite(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } + // Include non-nullaries + #define GAZER_EXPR_KIND(TYPE) // Empty + #define GAZER_NON_NULLARY_EXPR_KIND(TYPE) \ + virtual ReturnT visit##TYPE(const ExprRef& expr) { \ + return this->visitNonNullary(expr); \ + } - ReturnT visitTupleSelect(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } + #include "gazer/Core/Expr/ExprKind.def" - ReturnT visitTupleConstruct(const ExprRef& expr) { - return static_cast(this)->visitNonNullary(expr); - } + #undef GAZER_NON_NULLARY_EXPR_KIND + #undef GAZER_EXPR_KIND }; } // end namespace gazer diff --git a/include/gazer/Core/ExprRef.h b/include/gazer/Core/ExprRef.h index 27e5acba..dc5d0ca9 100644 --- a/include/gazer/Core/ExprRef.h +++ b/include/gazer/Core/ExprRef.h @@ -32,6 +32,6 @@ class Expr; template using ExprRef = boost::intrusive_ptr; using ExprPtr = ExprRef; -} +} // namespace gazer #endif \ No newline at end of file diff --git a/include/gazer/Core/ExprTypes.h b/include/gazer/Core/ExprTypes.h index 3097da2f..7a9ce4fe 100644 --- a/include/gazer/Core/ExprTypes.h +++ b/include/gazer/Core/ExprTypes.h @@ -154,15 +154,13 @@ class ArithmeticExpr final : public BinaryExpr "An arithmetic expression must have an arithmetic expression kind."); friend class ExprStorage; -protected: + +private: using BinaryExpr::BinaryExpr; public: static ExprRef> Create(const ExprPtr& left, const ExprPtr& right); - /** - * Type inquiry support. - */ static bool classof(const Expr* expr) { return expr->getKind() == Kind; } @@ -195,9 +193,8 @@ class CompareExpr final : public BinaryExpr static_assert(Expr::FirstCompare <= Kind && Kind <= Expr::LastCompare, "A compare expression must have a compare expression kind."); friend class ExprStorage; -protected: - using BinaryExpr::BinaryExpr; + using BinaryExpr::BinaryExpr; public: static ExprRef> Create(const ExprPtr& left, const ExprPtr& right); @@ -227,7 +224,7 @@ class MultiaryLogicExpr final : public NonNullaryExpr static_assert(Expr::And == Kind || Expr::Or == Kind, "A logic expression must have a logic expression kind."); friend class ExprStorage; -protected: + using NonNullaryExpr::NonNullaryExpr; public: @@ -245,7 +242,6 @@ template class BinaryLogicExpr final : public BinaryExpr { friend class ExprStorage; -protected: using BinaryExpr::BinaryExpr; public: @@ -266,7 +262,7 @@ class FpQueryExpr final : public UnaryExpr static_assert(Kind == Expr::FIsNan || Kind == Expr::FIsInf, "A floating point query expression must be FIsNan or FIsInf."); friend class ExprStorage; -protected: + using UnaryExpr::UnaryExpr; public: @@ -342,7 +338,7 @@ class FpArithmeticExpr final : public BinaryExpr, public detail::FpExprWithRound // Needed for ExprStorage to call this constructor. friend class ExprStorage; -protected: + template FpArithmeticExpr(Expr::ExprKind kind, Type& type, InputIterator begin, InputIterator end, const llvm::APFloat::roundingMode& rm) : BinaryExpr(kind, type, begin, end), FpExprWithRoundingMode(rm) diff --git a/include/gazer/Core/Solver/Model.h b/include/gazer/Core/Solver/Model.h index aa22db36..f355ca95 100644 --- a/include/gazer/Core/Solver/Model.h +++ b/include/gazer/Core/Solver/Model.h @@ -28,14 +28,11 @@ class LiteralExpr; class Model : public ExprEvaluator { public: - // Inherited from ExprEvaluator: - virtual ExprRef evaluate(const ExprPtr& expr) = 0; - - virtual ~Model() = default; + // 'evaluate' is inherited from ExprEvaluator virtual void dump(llvm::raw_ostream& os) = 0; }; -} +} // namespace gazer #endif diff --git a/include/gazer/Core/Solver/Solver.h b/include/gazer/Core/Solver/Solver.h index d515713b..2c0b4223 100644 --- a/include/gazer/Core/Solver/Solver.h +++ b/include/gazer/Core/Solver/Solver.h @@ -47,11 +47,14 @@ class Solver void add(const ExprPtr& expr) { assert(expr->getType().isBoolType() && "Can only add bool expressions to a solver."); - mStatCount++; + mExprStack.push_back(expr); addConstraint(expr); } - unsigned getNumConstraints() const { return mStatCount; } + size_t getNumConstraints() const + { + return mExprStack.size(); + } GazerContext& getContext() const { return mContext; } @@ -61,60 +64,38 @@ class Solver virtual SolverStatus run() = 0; virtual std::unique_ptr getModel() = 0; - virtual void reset() = 0; - - virtual void push() = 0; - virtual void pop() = 0; - - virtual ~Solver() = default; - -protected: - virtual void addConstraint(ExprPtr expr) = 0; - - GazerContext& mContext; -private: - unsigned mStatCount = 0; -}; - -/// Identifies an interpolation group. -using ItpGroup = unsigned; - -/// Interface for interpolating solvers. -class ItpSolver : public Solver -{ - using ItpGroupMapTy = std::unordered_map>; -public: - using Solver::Solver; - - void add(ItpGroup group, const ExprPtr& expr) + virtual void reset() { - assert(expr->getType().isBoolType() && "Can only add bool expressions to a solver."); - mGroupFormulae[group].push_back(expr); - - this->addConstraint(group, expr); + this->doReset(); + mExprStack.clear(); + mScopes.clear(); } - // Interpolant groups - ItpGroup createItpGroup() { return mGroupId++; } - - using itp_formula_iterator = typename ItpGroupMapTy::mapped_type::iterator; - itp_formula_iterator group_formula_begin(ItpGroup group) { - return mGroupFormulae[group].begin(); + virtual void push() + { + this->doPush(); + mScopes.push_back(mExprStack.size()); } - itp_formula_iterator group_formula_end(ItpGroup group) { - return mGroupFormulae[group].end(); + + virtual void pop() + { + this->doPop(); + mExprStack.resize(mScopes.back()); + mScopes.pop_back(); } - /// Returns an interpolant for a given interpolation group. - virtual ExprPtr getInterpolant(ItpGroup group) = 0; + virtual ~Solver() = default; protected: - using Solver::addConstraint; - virtual void addConstraint(ItpGroup group, ExprPtr expr) = 0; + virtual void addConstraint(ExprPtr expr) = 0; + virtual void doReset() = 0; + virtual void doPush() = 0; + virtual void doPop() = 0; + GazerContext& mContext; private: - unsigned mGroupId = 1; - ItpGroupMapTy mGroupFormulae; + std::vector mExprStack; + std::vector mScopes; }; /// Base factory class for all solvers, used to create new Solver instances. @@ -123,8 +104,10 @@ class SolverFactory public: /// Creates a new solver instance with a given symbol table. virtual std::unique_ptr createSolver(GazerContext& symbols) = 0; + + virtual ~SolverFactory() = default; }; -} +} // namespace gazer #endif diff --git a/include/gazer/Core/Type.h b/include/gazer/Core/Type.h index 4827a85d..f3f371a5 100644 --- a/include/gazer/Core/Type.h +++ b/include/gazer/Core/Type.h @@ -32,10 +32,11 @@ #include #include -namespace llvm { +namespace llvm +{ struct fltSemantics; class raw_ostream; -} +} // namespace llvm namespace gazer { @@ -116,7 +117,7 @@ llvm::raw_ostream& operator<<(llvm::raw_ostream& os, const Type& type); class BoolType final : public Type { friend class GazerContextImpl; -protected: + explicit BoolType(GazerContext& context) : Type(context, BoolTypeID) {} @@ -131,7 +132,7 @@ class BoolType final : public Type class BvType final : public Type { friend class GazerContextImpl; -protected: + BvType(GazerContext& context, unsigned width) : Type(context, BvTypeID), mWidth(width) {} @@ -155,7 +156,7 @@ class BvType final : public Type class IntType final : public Type { friend class GazerContextImpl; -protected: + explicit IntType(GazerContext& context) : Type(context, IntTypeID) {} @@ -169,13 +170,12 @@ class IntType final : public Type static bool classof(const Type& type) { return type.getTypeID() == IntTypeID; } -private: }; class RealType final : public Type { friend class GazerContextImpl; -protected: + explicit RealType(GazerContext& context) : Type(context, RealTypeID) {} @@ -214,7 +214,7 @@ class FloatType final : public Type static constexpr unsigned ExponentBitsInDoubleTy = 11; static constexpr unsigned ExponentBitsInQuadTy = 15; -protected: +private: FloatType(GazerContext& context, FloatPrecision precision) : Type(context, FloatTypeID), mPrecision(precision) {} @@ -256,8 +256,17 @@ class CompositeType : public Type return *mSubTypes[idx]; } + size_t getNumSubtypes() const + { + return mSubTypes.size(); + } + + using subtype_iterator = boost::indirect_iterator::const_iterator>; + subtype_iterator subtype_begin() const { return boost::make_indirect_iterator(mSubTypes.begin()); } + subtype_iterator subtype_end() const { return boost::make_indirect_iterator(mSubTypes.end()); } + protected: - std::vector mSubTypes; + const std::vector mSubTypes; }; /// Represents an array type with arbitrary index and element types. @@ -279,24 +288,12 @@ class ArrayType final : public CompositeType } }; -class TupleType final : public Type +class TupleType final : public CompositeType { - TupleType(GazerContext& context, std::vector subtypes) - : Type(context, TupleTypeID), mSubtypeList(std::move(subtypes)) - { - assert(!mSubtypeList.empty()); - assert(mSubtypeList.size() >= 2); - } + TupleType(GazerContext& context, std::vector types); public: - Type& getTypeAtIndex(unsigned idx) const { return *mSubtypeList[idx]; } - unsigned getNumSubtypes() const { return mSubtypeList.size(); } - - using subtype_iterator = boost::indirect_iterator::const_iterator>; - subtype_iterator subtype_begin() const { return boost::make_indirect_iterator(mSubtypeList.begin()); } - subtype_iterator subtype_end() const { return boost::make_indirect_iterator(mSubtypeList.end()); } - template - static typename std::enable_if<(std::is_base_of_v && ...), TupleType&>::type + static typename std::enable_if_t<(std::is_base_of_v && ...), TupleType&> Get(Type& first, Tys&... tail) { std::vector subtypeList({ &first, &tail... }); @@ -305,16 +302,15 @@ class TupleType final : public Type static TupleType& Get(std::vector subtypes); - static bool classof(const Type* type) { + static bool classof(const Type* type) + { return type->getTypeID() == TupleTypeID; } - static bool classof(const Type& type) { + static bool classof(const Type& type) + { return classof(&type); } - -private: - std::vector mSubtypeList; }; class FunctionType : public Type @@ -325,7 +321,7 @@ class FunctionType : public Type public: template - static typename std::enable_if, FunctionType&>::type + static typename std::enable_if_t, FunctionType&> Get(Type& result, Tys&... params) { std::array paramTypes({ ¶ms... }); diff --git a/include/gazer/Core/Valuation.h b/include/gazer/Core/Valuation.h index fe3da857..0e915428 100644 --- a/include/gazer/Core/Valuation.h +++ b/include/gazer/Core/Valuation.h @@ -76,7 +76,7 @@ class Valuation const_iterator begin() const { return mMap.begin(); } const_iterator end() const { return mMap.end(); } - void print(llvm::raw_ostream& os); + void print(llvm::raw_ostream& os) const; private: ValuationMapT mMap; diff --git a/include/gazer/LLVM/Analysis/PDG.h b/include/gazer/LLVM/Analysis/PDG.h deleted file mode 100644 index e7b8b10e..00000000 --- a/include/gazer/LLVM/Analysis/PDG.h +++ /dev/null @@ -1,210 +0,0 @@ -//==-------------------------------------------------------------*- C++ -*--==// -// -// Copyright 2019 Contributors to the Gazer project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -#ifndef GAZER_LLVM_ANALYSIS_PDG_H -#define GAZER_LLVM_ANALYSIS_PDG_H - -#include -#include - -#include -#include - -namespace gazer -{ - -class PDGNode; - -class PDGEdge -{ - friend class ProgramDependenceGraph; -public: - enum Kind { Control, DataFlow, Memory }; - -private: - PDGEdge(PDGNode* source, PDGNode* target, Kind kind) - : mSource(source), mTarget(target), mKind(kind) - {} - - PDGEdge(const PDGEdge&) = delete; - PDGEdge& operator=(const PDGEdge&) = delete; - -public: - PDGNode* getSource() const { return mSource; } - PDGNode* getTarget() const { return mTarget; } - Kind getKind() const { return mKind; } - -private: - PDGNode* mSource; - PDGNode* mTarget; - Kind mKind; -}; - -class PDGNode -{ - friend class ProgramDependenceGraph; -private: - PDGNode(llvm::Instruction* inst) - : mInst(inst) - {} - - PDGNode(const PDGNode&) = delete; - PDGNode& operator=(const PDGNode&) = delete; -public: - using edge_iterator = std::vector::iterator; - edge_iterator incoming_begin() { return mIncoming.begin(); } - edge_iterator incoming_end() { return mIncoming.end(); } - llvm::iterator_range incoming() { - return llvm::make_range(incoming_begin(), incoming_end()); - } - - edge_iterator outgoing_begin() { return mOutgoing.begin(); } - edge_iterator outgoing_end() { return mOutgoing.end(); } - llvm::iterator_range outgoing() { - return llvm::make_range(outgoing_begin(), outgoing_end()); - } - - llvm::Instruction* getInstruction() const { return mInst; } - -private: - void addIncoming(PDGEdge* edge) { mIncoming.push_back(edge); } - void addOutgoing(PDGEdge* edge) { mOutgoing.push_back(edge); } - -private: - llvm::Instruction* mInst; - std::vector mIncoming; - std::vector mOutgoing; -}; - -class ProgramDependenceGraph final -{ - using NodeMapTy = std::unordered_map>; -private: - ProgramDependenceGraph( - llvm::Function& function, - std::unordered_map> nodes, - std::vector> edges - ) : mFunction(function), mNodes(std::move(nodes)), mEdges(std::move(edges)) - {} - -private: - static PDGNode* getNodeFromIterator(NodeMapTy::value_type& pair) { - return &*pair.second; - } - -public: - static std::unique_ptr Create( - llvm::Function& function, - llvm::PostDominatorTree& pdt - ); - - PDGNode* getNode(llvm::Instruction* inst) const { - return &*mNodes.at(inst); - } - - using node_iterator = llvm::mapped_iterator; - node_iterator node_begin() { return node_iterator(mNodes.begin(), &getNodeFromIterator); } - node_iterator node_end() { return node_iterator(mNodes.end(), &getNodeFromIterator); } - - unsigned node_size() const { return mNodes.size(); } - - llvm::Function& getFunction() const { return mFunction; } - - void view() const; - -private: - llvm::Function& mFunction; - NodeMapTy mNodes; - std::vector> mEdges; -}; - -class ProgramDependenceWrapperPass final : public llvm::FunctionPass -{ -public: - static char ID; -public: - ProgramDependenceWrapperPass() - : FunctionPass(ID) - {} - - virtual void getAnalysisUsage(llvm::AnalysisUsage& au) const override; - virtual bool runOnFunction(llvm::Function& function) override; - virtual llvm::StringRef getPassName() const override { return "ProgramDependenceWrapperPass"; } - - ProgramDependenceGraph& getProgramDependenceGraph() const { return *mResult; } - -private: - std::unique_ptr mResult; -}; - -llvm::FunctionPass* createProgramDependenceWrapperPass(); - -} - -// Graph traits specialization for PDGs -//===----------------------------------------------------------------------===// -namespace llvm -{ - -template<> -struct GraphTraits -{ - using NodeRef = gazer::PDGNode*; - using EdgeRef = gazer::PDGEdge*; - - static gazer::PDGNode* GetEdgeTarget(gazer::PDGEdge* edge) { - return edge->getTarget(); - } - - using ChildIteratorType = llvm::mapped_iterator< - gazer::PDGNode::edge_iterator, decltype(&GetEdgeTarget) - >; - static ChildIteratorType child_begin(NodeRef node) { - return ChildIteratorType(node->outgoing_begin(), &GetEdgeTarget); - } - static ChildIteratorType child_end(NodeRef node) { - return ChildIteratorType(node->outgoing_end(), &GetEdgeTarget); - } - - using ChildEdgeIteratorType = gazer::PDGNode::edge_iterator; - static ChildEdgeIteratorType child_edge_begin(NodeRef node) { - return node->outgoing_begin(); - } - static ChildEdgeIteratorType child_edge_end(NodeRef node) { - return node->outgoing_end(); - } - static NodeRef edge_dest(EdgeRef edge) { - return edge->getTarget(); - } - - using nodes_iterator = gazer::ProgramDependenceGraph::node_iterator; - - static nodes_iterator nodes_begin(gazer::ProgramDependenceGraph& pdg) { - return pdg.node_begin(); - } - static nodes_iterator nodes_end(gazer::ProgramDependenceGraph& pdg) { - return pdg.node_end(); - } - - static unsigned size(const gazer::ProgramDependenceGraph& pdg) { - return pdg.node_size(); - } -}; - -} - -#endif \ No newline at end of file diff --git a/include/gazer/LLVM/Automaton/InstToExpr.h b/include/gazer/LLVM/Automaton/InstToExpr.h index c97277c7..21f7a25a 100644 --- a/include/gazer/LLVM/Automaton/InstToExpr.h +++ b/include/gazer/LLVM/Automaton/InstToExpr.h @@ -66,7 +66,6 @@ class InstToExpr return nullptr; } -protected: ExprPtr visitBinaryOperator(const llvm::BinaryOperator& binop, Type& expectedType); ExprPtr visitSelectInst(const llvm::SelectInst& select, Type& expectedType); ExprPtr visitCastInst(const llvm::CastInst& cast, Type& expectedType); @@ -77,6 +76,7 @@ class InstToExpr ExprPtr visitExtractValueInst(const llvm::ExtractValueInst& extract); ExprPtr operand(ValueOrMemoryObject value); + ExprPtr operand(ValueOrMemoryObject value, Type& expectedType); ExprPtr asBool(const ExprPtr& operand); ExprPtr asBv(const ExprPtr& operand, unsigned int bits); @@ -102,6 +102,9 @@ class InstToExpr ExprPtr doTransform(const llvm::Instruction& inst, Type& expectedType); ExprPtr unsignedLessThan(const ExprPtr& left, const ExprPtr& right); + ExprPtr translateICmpBv(llvm::CmpInst::Predicate predicate, const ExprPtr& left, const ExprPtr& right); + ExprPtr translateICmpInt(llvm::CmpInst::Predicate predicate, const ExprPtr& left, const ExprPtr& right); + ExprPtr operandValue(const llvm::Value* value); ExprPtr operandMemoryObject(const MemoryObjectDef* def); diff --git a/include/gazer/LLVM/Automaton/ModuleToAutomata.h b/include/gazer/LLVM/Automaton/ModuleToAutomata.h index 6bd87c54..53c08e6e 100644 --- a/include/gazer/LLVM/Automaton/ModuleToAutomata.h +++ b/include/gazer/LLVM/Automaton/ModuleToAutomata.h @@ -61,7 +61,7 @@ class CfaGenInfo; class ExtensionPoint { protected: - ExtensionPoint(CfaGenInfo& genInfo) + explicit ExtensionPoint(CfaGenInfo& genInfo) : mGenInfo(genInfo) {} @@ -70,6 +70,7 @@ class ExtensionPoint ExtensionPoint& operator=(const ExtensionPoint&) = delete; const Cfa& getCfa() const; + GazerContext& getContext() const; llvm::Loop* getSourceLoop() const; llvm::Function* getSourceFunction() const; @@ -100,9 +101,7 @@ class AutomatonInterfaceExtensionPoint : public ExtensionPoint class VariableDeclExtensionPoint : public AutomatonInterfaceExtensionPoint { public: - VariableDeclExtensionPoint(CfaGenInfo& genInfo) - : AutomatonInterfaceExtensionPoint(genInfo) - {} + using AutomatonInterfaceExtensionPoint::AutomatonInterfaceExtensionPoint; Variable* createInput(ValueOrMemoryObject val, Type& type, const std::string& suffix = ""); Variable* createLocal(ValueOrMemoryObject val, Type& type, const std::string& suffix = ""); @@ -137,7 +136,15 @@ class GenerationStepExtensionPoint : public AutomatonInterfaceExtensionPoint /// Creates a new local variable into the current automaton. Variable* createAuxiliaryVariable(const std::string& name, Type& type); - virtual ExprPtr getAsOperand(ValueOrMemoryObject val) = 0; + ExprPtr getAsOperand(ValueOrMemoryObject val) { + return this->getAsOperand(val, nullptr); + } + + /// Represents 'val' as an expression of the given type. + /// If the expression cannot be represented in the given type, returns nullptr. + ExprPtr getAsOperand(ValueOrMemoryObject val, Type& type) { + return this->getAsOperand(val, &type); + } /// Attempts to inline and eliminate a given variable from the CFA. virtual bool tryToEliminate(ValueOrMemoryObject val, Variable* variable, const ExprPtr& expr) = 0; @@ -145,6 +152,9 @@ class GenerationStepExtensionPoint : public AutomatonInterfaceExtensionPoint virtual void insertAssignment(Variable* variable, const ExprPtr& value) = 0; virtual void splitCurrentTransition(const ExprPtr& guard) = 0; + +protected: + virtual ExprPtr getAsOperand(ValueOrMemoryObject val, Type* type) = 0; }; } // end namespace llvm2cfa @@ -197,7 +207,7 @@ class ModuleToAutomataPass : public llvm::ModulePass void getAnalysisUsage(llvm::AnalysisUsage& au) const override; - bool runOnModule(llvm::Module& module) override; + bool runOnModule(llvm::Module& llvmModule) override; llvm::StringRef getPassName() const override { return "Module to automata transformation"; @@ -216,7 +226,7 @@ class ModuleToAutomataPass : public llvm::ModulePass class SpecialFunctions; std::unique_ptr translateModuleToAutomata( - llvm::Module& module, + llvm::Module& llvmModule, const LLVMFrontendSettings& settings, llvm2cfa::LoopInfoFuncTy loopInfos, GazerContext& context, diff --git a/include/gazer/LLVM/Automaton/SpecialFunctions.h b/include/gazer/LLVM/Automaton/SpecialFunctions.h index fdc2f624..2ebe7b4a 100644 --- a/include/gazer/LLVM/Automaton/SpecialFunctions.h +++ b/include/gazer/LLVM/Automaton/SpecialFunctions.h @@ -29,7 +29,7 @@ namespace gazer class SpecialFunctionHandler { public: - using HandlerFuncTy = std::function; + using HandlerFuncTy = std::function; enum MemoryBehavior { @@ -39,12 +39,11 @@ class SpecialFunctionHandler Memory_DefinesAll ///< Clobber and define all known memory objects. }; -public: - SpecialFunctionHandler(HandlerFuncTy function, MemoryBehavior memory = Memory_Default) - : mHandlerFunction(function), mMemory(memory) + explicit SpecialFunctionHandler(HandlerFuncTy function, MemoryBehavior memory = Memory_Default) + : mHandlerFunction(std::move(function)), mMemory(memory) {} - void operator()(llvm::ImmutableCallSite cs, llvm2cfa::GenerationStepExtensionPoint& ep) const + void operator()(const llvm::CallBase* cs, llvm2cfa::GenerationStepExtensionPoint& ep) const { return mHandlerFunction(cs, ep); } @@ -58,19 +57,30 @@ class SpecialFunctions { static const SpecialFunctions EmptyInstance; public: - /// Returns an object with the default handlers + /// Returns a SpecialFunctions object with the default handlers static std::unique_ptr get(); + + /// Returns a handler without any registered handlers static const SpecialFunctions& empty() { return EmptyInstance; } void registerHandler( - llvm::StringRef name, SpecialFunctionHandler::HandlerFuncTy function, + llvm::StringRef name, const SpecialFunctionHandler::HandlerFuncTy& function, SpecialFunctionHandler::MemoryBehavior memory = SpecialFunctionHandler::Memory_Default); - bool handle(llvm::ImmutableCallSite cs, llvm2cfa::GenerationStepExtensionPoint& ep) const; + template + void registerMultipleHandlers( + Range& names, const SpecialFunctionHandler::HandlerFuncTy& function, + SpecialFunctionHandler::MemoryBehavior memory = SpecialFunctionHandler::Memory_Default) + { + for (const auto& name : names) { + this->registerHandler(name, function, memory); + } + } + + bool handle(const llvm::CallBase* cs, llvm2cfa::GenerationStepExtensionPoint& ep) const; // Some handlers for common cases -public: - static void handleAssume(llvm::ImmutableCallSite cs, llvm2cfa::GenerationStepExtensionPoint& ep); + static void handleAssume(const llvm::CallBase* cs, llvm2cfa::GenerationStepExtensionPoint& ep); private: llvm::StringMap mHandlers; diff --git a/include/gazer/LLVM/ClangFrontend.h b/include/gazer/LLVM/ClangFrontend.h index d33c6d1e..0b08eba1 100644 --- a/include/gazer/LLVM/ClangFrontend.h +++ b/include/gazer/LLVM/ClangFrontend.h @@ -27,7 +27,7 @@ namespace llvm { class Module; class LLVMContext; -} +} // namespace llvm namespace gazer { @@ -52,6 +52,6 @@ std::unique_ptr ClangCompileAndLink( ClangOptions& settings ); -} +} // namespace gazer -#endif //GAZER_CLANGFRONTEND_H +#endif //GAZER_LLVM_CLANGFRONTEND_H diff --git a/include/gazer/LLVM/Instrumentation/Check.h b/include/gazer/LLVM/Instrumentation/Check.h index cf3e7028..6395bb51 100644 --- a/include/gazer/LLVM/Instrumentation/Check.h +++ b/include/gazer/LLVM/Instrumentation/Check.h @@ -33,14 +33,12 @@ class Check : public llvm::ModulePass { friend class CheckRegistry; public: - explicit Check(char& id) - : ModulePass(id) - {} + using llvm::ModulePass::ModulePass; Check(const Check&) = delete; Check& operator=(const Check&) = delete; - bool runOnModule(llvm::Module& module) final; + bool runOnModule(llvm::Module& llvmModule) final; /// Returns a user-friendly error description on why this particular /// check failed. Such descriptions should be short and simple, e.g. @@ -108,7 +106,7 @@ class CheckRegistry CheckRegistry(const CheckRegistry&) = delete; CheckRegistry& operator=(const CheckRegistry&) = delete; - static llvm::FunctionCallee GetErrorFunction(llvm::Module& module); + static llvm::FunctionCallee GetErrorFunction(llvm::Module& llvmModule); static llvm::FunctionCallee GetErrorFunction(llvm::Module* module) { return GetErrorFunction(*module); } diff --git a/include/gazer/LLVM/Instrumentation/Intrinsics.h b/include/gazer/LLVM/Instrumentation/Intrinsics.h index 4ad3cfe0..adc1be8f 100644 --- a/include/gazer/LLVM/Instrumentation/Intrinsics.h +++ b/include/gazer/LLVM/Instrumentation/Intrinsics.h @@ -59,23 +59,23 @@ class GazerIntrinsic public: /// Returns a 'gazer.function.entry(metadata fn_name, args...)' intrinsic. - static llvm::FunctionCallee GetOrInsertFunctionEntry(llvm::Module& module, llvm::ArrayRef args); + static llvm::FunctionCallee GetOrInsertFunctionEntry(llvm::Module& llvmModule, llvm::ArrayRef args); /// Returns a 'gazer.function.return_void(metadata fn_name)' intrinsic. - static llvm::FunctionCallee GetOrInsertFunctionReturnVoid(llvm::Module& module); + static llvm::FunctionCallee GetOrInsertFunctionReturnVoid(llvm::Module& llvmModule); /// Returns a 'gazer.function.call_returned(metadata fn_name)' intrinsic. - static llvm::FunctionCallee GetOrInsertFunctionCallReturned(llvm::Module& module); + static llvm::FunctionCallee GetOrInsertFunctionCallReturned(llvm::Module& llvmModule); /// Returns a 'gazer.function.return_value.T(metadata fn_name, T retval)' intrinsic, /// where 'T' is the given return type. - static llvm::FunctionCallee GetOrInsertFunctionReturnValue(llvm::Module& module, llvm::Type* type); + static llvm::FunctionCallee GetOrInsertFunctionReturnValue(llvm::Module& llvmModule, llvm::Type* type); /// Returns a 'gazer.inlined_global_write.T(T value, metadata gv_name)' intrinsic. - static llvm::FunctionCallee GetOrInsertInlinedGlobalWrite(llvm::Module& module, llvm::Type* type); + static llvm::FunctionCallee GetOrInsertInlinedGlobalWrite(llvm::Module& llvmModule, llvm::Type* type); /// Returns a 'gazer.KIND.no_overflow.T(T left, T right)' intrinsic. - static llvm::FunctionCallee GetOrInsertOverflowCheck(llvm::Module& module, Overflow kind, llvm::Type* type); + static llvm::FunctionCallee GetOrInsertOverflowCheck(llvm::Module& llvmModule, Overflow kind, llvm::Type* type); }; } diff --git a/include/gazer/LLVM/LLVMFrontend.h b/include/gazer/LLVM/LLVMFrontend.h index 50fbc638..9ddf2def 100644 --- a/include/gazer/LLVM/LLVMFrontend.h +++ b/include/gazer/LLVM/LLVMFrontend.h @@ -26,6 +26,7 @@ #include #include #include +#include namespace gazer { @@ -80,7 +81,7 @@ class FrontendConfigWrapper { public: static void PrintVersion(llvm::raw_ostream& os); -public: + FrontendConfigWrapper() = default; std::unique_ptr buildFrontend(llvm::ArrayRef inputs) @@ -102,7 +103,7 @@ class LLVMFrontend { public: LLVMFrontend( - std::unique_ptr module, + std::unique_ptr llvmModule, GazerContext& context, LLVMFrontendSettings& settings ); diff --git a/include/gazer/LLVM/LLVMFrontendSettings.h b/include/gazer/LLVM/LLVMFrontendSettings.h index cf7a4d13..4395705d 100644 --- a/include/gazer/LLVM/LLVMFrontendSettings.h +++ b/include/gazer/LLVM/LLVMFrontendSettings.h @@ -114,10 +114,10 @@ class LLVMFrontendSettings MemoryModelSetting memoryModel = MemoryModelSetting::Flat; public: - /// Returns true if the current settings can be applied to the given module. - bool validate(const llvm::Module& module, llvm::raw_ostream& os) const; + /// Returns true if the current settings can be applied to the given llvmModule. + bool validate(const llvm::Module& llvmModule, llvm::raw_ostream& os) const; - llvm::Function* getEntryFunction(const llvm::Module& module) const; + llvm::Function* getEntryFunction(const llvm::Module& llvmModule) const; bool isElimVarsOff() const { return elimVars == ElimVarsLevel::Off; } bool isElimVarsNormal() const { return elimVars == ElimVarsLevel::Normal; } diff --git a/include/gazer/LLVM/Memory/MemoryInstructionHandler.h b/include/gazer/LLVM/Memory/MemoryInstructionHandler.h index 81520b36..90b6d874 100644 --- a/include/gazer/LLVM/Memory/MemoryInstructionHandler.h +++ b/include/gazer/LLVM/Memory/MemoryInstructionHandler.h @@ -129,7 +129,7 @@ class MemoryInstructionHandler /// The parameters \p inputAssignments and \p outputAssignments will be placed /// on the resulting automaton call _after_ the regular input/output assignments. virtual void handleCall( - llvm::CallSite call, + const llvm::CallBase* call, llvm2cfa::GenerationStepExtensionPoint& callerEp, llvm2cfa::AutomatonInterfaceExtensionPoint& calleeEp, llvm::SmallVectorImpl& inputAssignments, @@ -142,7 +142,7 @@ class MemoryInstructionHandler /// function, the translation process already generates a havoc assignment for /// it _before_ calling this function. virtual void handleExternalCall( - llvm::CallSite call, llvm2cfa::GenerationStepExtensionPoint& ep) {} + llvm::AbstractCallSite call, llvm2cfa::GenerationStepExtensionPoint& ep) {} // Memory safety predicates //==--------------------------------------------------------------------==// diff --git a/include/gazer/LLVM/Memory/MemoryModel.h b/include/gazer/LLVM/Memory/MemoryModel.h index d5b90be0..aebf1875 100644 --- a/include/gazer/LLVM/Memory/MemoryModel.h +++ b/include/gazer/LLVM/Memory/MemoryModel.h @@ -61,7 +61,7 @@ std::unique_ptr CreateHavocMemoryModel(GazerContext& context); std::unique_ptr CreateFlatMemoryModel( GazerContext& context, const LLVMFrontendSettings& settings, - llvm::Module& module, + llvm::Module& llvmModule, std::function dominators ); @@ -75,7 +75,7 @@ class MemoryModelWrapperPass : public llvm::ModulePass {} void getAnalysisUsage(llvm::AnalysisUsage& au) const override; - bool runOnModule(llvm::Module& module) override; + bool runOnModule(llvm::Module& llvmModule) override; MemoryModel& getMemoryModel() const { return *mMemoryModel; } diff --git a/include/gazer/LLVM/Memory/MemoryObject.h b/include/gazer/LLVM/Memory/MemoryObject.h index bf6470e7..77edbcc4 100644 --- a/include/gazer/LLVM/Memory/MemoryObject.h +++ b/include/gazer/LLVM/Memory/MemoryObject.h @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include @@ -50,7 +50,7 @@ class MemoryAccess { friend class memory::MemorySSABuilder; public: - MemoryAccess(MemoryObject* object) + explicit MemoryAccess(MemoryObject* object) : mObject(object) {} @@ -96,7 +96,7 @@ class MemoryObjectDef : public MemoryAccess void print(llvm::raw_ostream& os) const; - virtual ~MemoryObjectDef() {} + virtual ~MemoryObjectDef() = default; protected: virtual void doPrint(llvm::raw_ostream& os) const = 0; @@ -136,7 +136,7 @@ class MemoryObjectUse : public MemoryAccess virtual llvm::Instruction* getInstruction() const = 0; virtual void print(llvm::raw_ostream& os) const = 0; - virtual ~MemoryObjectUse() {} + virtual ~MemoryObjectUse() = default; private: Kind mKind; @@ -185,7 +185,7 @@ class MemoryObject MemoryObjectType getObjectType() const { return mObjectType; } llvm::Type* getValueType() const { return mValueType; } - llvm::StringRef getName() const { return mName; } + const std::string& getName() const { return mName; } unsigned getId() const { return mId; } @@ -210,8 +210,26 @@ class MemoryObject ~MemoryObject(); private: - void addDefinition(MemoryObjectDef* def); - void addUse(MemoryObjectUse* use); + template + T* createDef(Args&&... args) + { + std::unique_ptr ptr = std::make_unique(std::forward(args)...); + T* rawPtr = ptr.get(); + mDefs.emplace_back(std::move(ptr)); + return rawPtr; + } + + template + T* createUse(Args&&... args) + { + std::unique_ptr ptr = std::make_unique(std::forward(args)...); + T* rawPtr = ptr.get(); + mUses.emplace_back(std::move(ptr)); + return rawPtr; + } + + MemoryObjectDef* addDefinition(std::unique_ptr&& def); + MemoryObjectUse* addUse(std::unique_ptr&& use); void setEntryDef(MemoryObjectDef* def); void setExitUse(MemoryObjectUse* use); @@ -334,7 +352,7 @@ class StoreDef : public InstructionAnnotationDef class CallDef : public InstructionAnnotationDef { public: - CallDef(MemoryObject* object, unsigned int version, llvm::CallSite call) + CallDef(MemoryObject* object, unsigned int version, llvm::CallBase* call) : InstructionAnnotationDef(object, version, MemoryObjectDef::Call), mCall(call) {} @@ -342,13 +360,13 @@ class CallDef : public InstructionAnnotationDef return def->getKind() == MemoryObjectDef::Call; } - llvm::Instruction* getInstruction() const override { return mCall.getInstruction(); } + llvm::Instruction* getInstruction() const override { return mCall; } protected: void doPrint(llvm::raw_ostream& os) const override; private: - llvm::CallSite mCall; + llvm::CallBase* mCall; }; class LiveOnEntryDef : public BlockAnnotationDef @@ -445,12 +463,11 @@ class LoadUse : public MemoryObjectUse class CallUse : public MemoryObjectUse { public: - CallUse(MemoryObject* object, llvm::CallSite callSite) + CallUse(MemoryObject* object, llvm::CallBase* callSite) : MemoryObjectUse(object, MemoryObjectUse::Call), mCallSite(callSite) {} - llvm::Instruction* getInstruction() const override { return mCallSite.getInstruction(); } - llvm::CallSite getCallSite() const { return mCallSite; } + llvm::Instruction* getInstruction() const override { return mCallSite; } void print(llvm::raw_ostream& os) const override; @@ -458,7 +475,7 @@ class CallUse : public MemoryObjectUse return use->getKind() == MemoryObjectUse::Call; } private: - llvm::CallSite mCallSite; + llvm::CallBase* mCallSite; }; class RetUse : public MemoryObjectUse diff --git a/include/gazer/LLVM/Memory/MemorySSA.h b/include/gazer/LLVM/Memory/MemorySSA.h index 1821bdf9..7c81f2a3 100644 --- a/include/gazer/LLVM/Memory/MemorySSA.h +++ b/include/gazer/LLVM/Memory/MemorySSA.h @@ -88,7 +88,7 @@ class MemorySSA private: template - static void memoryAccessOfKindImpl(Range&& range, llvm::SmallVectorImpl& vec) + static void memoryAccessOfKindImpl(const Range& range, llvm::SmallVectorImpl& vec) { static_assert(std::is_base_of_v, "AccessKind must be a subclass of MemoryAccess!"); for (auto& access : range) { @@ -144,10 +144,10 @@ class MemorySSABuilder ); memory::AllocaDef* createAllocaDef(MemoryObject* object, llvm::AllocaInst& alloca); memory::StoreDef* createStoreDef(MemoryObject* object, llvm::StoreInst& inst); - memory::CallDef* createCallDef(MemoryObject* object, llvm::CallSite call); + memory::CallDef* createCallDef(MemoryObject* object, llvm::CallBase* call); memory::LoadUse* createLoadUse(MemoryObject* object, llvm::LoadInst& load); - memory::CallUse* createCallUse(MemoryObject* object, llvm::CallSite call); + memory::CallUse* createCallUse(MemoryObject* object, llvm::CallBase* call); memory::RetUse* createReturnUse(MemoryObject* object, llvm::ReturnInst& ret); std::unique_ptr build(); @@ -180,7 +180,7 @@ class MemorySSABuilderPass : public llvm::ModulePass {} void getAnalysisUsage(llvm::AnalysisUsage& au) const final; - bool runOnModule(llvm::Module& module) final; + bool runOnModule(llvm::Module& llvmModule) final; virtual void addRequiredAnalyses(llvm::AnalysisUsage& au) const {} virtual void initializeFunction(llvm::Function& function, MemorySSABuilder& builder) = 0; diff --git a/include/gazer/LLVM/Trace/TestHarnessGenerator.h b/include/gazer/LLVM/Trace/TestHarnessGenerator.h index ec7eb55c..9b872e71 100644 --- a/include/gazer/LLVM/Trace/TestHarnessGenerator.h +++ b/include/gazer/LLVM/Trace/TestHarnessGenerator.h @@ -26,9 +26,9 @@ namespace gazer { std::unique_ptr GenerateTestHarnessModuleFromTrace( - Trace& trace, llvm::LLVMContext& context, const llvm::Module& module + const Trace& trace, llvm::LLVMContext& context, const llvm::Module& llvmModule ); -} +} // namespace gazer #endif diff --git a/include/gazer/LLVM/Transform/BackwardSlicer.h b/include/gazer/LLVM/Transform/BackwardSlicer.h deleted file mode 100644 index 2bebf41b..00000000 --- a/include/gazer/LLVM/Transform/BackwardSlicer.h +++ /dev/null @@ -1,60 +0,0 @@ -//==-------------------------------------------------------------*- C++ -*--==// -// -// Copyright 2019 Contributors to the Gazer project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -#ifndef GAZER_LLVM_TRANSFORMS_BACKWARDSLICER_H -#define GAZER_LLVM_TRANSFORMS_BACKWARDSLICER_H - -#include "gazer/LLVM/Analysis/PDG.h" - -#include - -#include - -namespace gazer -{ - -class BackwardSlicer -{ -public: - BackwardSlicer( - llvm::Function& function, - std::function criterion - ); - - /// Slices the given function according to the given criteria. - bool slice(); - -private: - - bool collectRequiredNodes(llvm::DenseSet& visited); - void sliceBlocks( - const llvm::DenseSet& required, - llvm::SmallVectorImpl& preservedBlocks - ); - void sliceInstructions(llvm::BasicBlock& bb, const llvm::DenseSet& required); - -private: - llvm::Function& mFunction; - std::function mCriterion; - std::unique_ptr mPDG; -}; - -llvm::Pass* createBackwardSlicerPass(std::function criteria); - -} // end namespace gazer - -#endif \ No newline at end of file diff --git a/include/gazer/LLVM/Transform/Passes.h b/include/gazer/LLVM/Transform/Passes.h index bce13d39..849dd863 100644 --- a/include/gazer/LLVM/Transform/Passes.h +++ b/include/gazer/LLVM/Transform/Passes.h @@ -33,9 +33,6 @@ llvm::Pass* createInlineGlobalVariablesPass(); /// into a single one. llvm::Pass* createLiftErrorCallsPass(llvm::Function& entry); -/// This pass normalizes some known verifier calls into a uniform format. -llvm::Pass* createNormalizeVerifierCallsPass(); - /// A simpler (and more restricted) inlining pass. llvm::Pass* createSimpleInlinerPass(llvm::Function& entry, InlineLevel level); diff --git a/include/gazer/LLVM/TypeTranslator.h b/include/gazer/LLVM/TypeTranslator.h index 83bff61b..340630ca 100644 --- a/include/gazer/LLVM/TypeTranslator.h +++ b/include/gazer/LLVM/TypeTranslator.h @@ -39,7 +39,7 @@ class LLVMTypeTranslator final gazer::Type& get(const llvm::Type* type); -protected: +private: MemoryTypeTranslator& mMemoryTypes; const LLVMFrontendSettings& mSettings; }; diff --git a/include/gazer/Support/Float.h b/include/gazer/Support/Float.h deleted file mode 100644 index 237f6f7d..00000000 --- a/include/gazer/Support/Float.h +++ /dev/null @@ -1,50 +0,0 @@ -//==-------------------------------------------------------------*- C++ -*--==// -// -// Copyright 2019 Contributors to the Gazer project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -#ifndef GAZER_SUPPORT_FLOAT_H -#define GAZER_SUPPORT_FLOAT_H - -#include -#include -#include - -namespace gazer -{ - -inline double double_from_uint64(uint64_t bits) -{ - double result; - assert(sizeof(result) == sizeof(bits)); - memcpy(&result, &bits, sizeof(bits)); - - return result; -} - -inline float float_from_uint64(uint64_t bits) -{ - float result; - uint32_t trunc = bits; - assert(sizeof(trunc) == sizeof(bits)); - memcpy(&result, &bits, sizeof(trunc)); - - return result; -} - - -} - -#endif diff --git a/include/gazer/Support/GrowingStackAllocator.h b/include/gazer/Support/GrowingStackAllocator.h index 7cabe74c..9904f95d 100644 --- a/include/gazer/Support/GrowingStackAllocator.h +++ b/include/gazer/Support/GrowingStackAllocator.h @@ -41,15 +41,17 @@ class GrowingStackAllocator : public llvm::AllocatorBasestartNewSlab(); } - LLVM_ATTRIBUTE_RETURNS_NONNULL void* Allocate(size_t size, size_t alignment) + LLVM_ATTRIBUTE_RETURNS_NONNULL void* Allocate(size_t size, llvm::Align alignment) { - size_t adjustment = llvm::alignmentAdjustment(slab().Current, alignment); + size_t adjustment = llvm::offsetToAlignedAddr(slab().Current, alignment); assert(adjustment + size >= size); if (size > SlabSize) { @@ -111,6 +113,6 @@ class GrowingStackAllocator : public llvm::AllocatorBase createSolver(GazerContext& context) override; }; -/// Utility function which transforms an arbitrary Z3 bitvector into LLVM's APInt. -llvm::APInt z3_bv_to_apint(z3::context& context, z3::model& model, const z3::expr& expr); - } // end namespace gazer #endif diff --git a/sonar-project.properties b/sonar-project.properties index 5a5357b6..34eb05dc 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,10 +1,13 @@ -sonar.projectKey=ftsrg_gazer -sonar.organization=ftsrg +sonar.projectKey=sallaigy_gazer +sonar.organization=sallaigy sonar.projectName=gazer sonar.host.url=https://sonarcloud.io -sonar.sources=include,src +sonar.sources=include,src,tools +sonar.tests=unittest sonar.cfamily.threads=2 sonar.cfamily.cache.enabled=true +sonar.cfamily.cache.path=.sonar +sonar.cfamily.llvm-cov.reportPath=llvm_cov_report.txt diff --git a/src/Automaton/CMakeLists.txt b/src/Automaton/CMakeLists.txt index 2af57f4d..fdf0d08e 100644 --- a/src/Automaton/CMakeLists.txt +++ b/src/Automaton/CMakeLists.txt @@ -3,8 +3,9 @@ set(SOURCE_FILES CfaPrinter.cpp CallGraph.cpp CfaUtils.cpp + CfaInline.cpp RecursiveToCyclicCfa.cpp -) + CfaClone.cpp) add_library(GazerAutomaton SHARED ${SOURCE_FILES}) target_link_libraries(GazerAutomaton GazerCore) \ No newline at end of file diff --git a/src/Automaton/CallGraph.cpp b/src/Automaton/CallGraph.cpp index 88a3460b..9bdb3943 100644 --- a/src/Automaton/CallGraph.cpp +++ b/src/Automaton/CallGraph.cpp @@ -33,10 +33,8 @@ CallGraph::CallGraph(AutomataSystem& system) for (Cfa& cfa : system) { auto& node = mNodes[&cfa]; - for (Transition* edge : cfa.edges()) { - if (auto call = llvm::dyn_cast(edge)) { - node->addCall(call, mNodes[call->getCalledAutomaton()].get()); - } + for (auto* call : classof_range(cfa.edges())) { + node->addCall(call, mNodes[call->getCalledAutomaton()].get()); } } } @@ -48,10 +46,10 @@ bool CallGraph::isTailRecursive(Cfa* cfa) bool isRecursive = false; bool isTail = true; - for (auto& call : node->mCallsToOthers) { - if (call.first->getCalledAutomaton() == cfa) { + for (auto& [callEdge, _] : node->mCallsToOthers) { + if (callEdge->getCalledAutomaton() == cfa) { isRecursive = true; - if (call.first->getTarget() != cfa->getExit()) { + if (callEdge->getTarget() != cfa->getExit()) { isTail = false; break; } @@ -66,9 +64,3 @@ auto CallGraph::lookupNode(Cfa* cfa) -> Node* return mNodes[cfa].get(); } -CallGraph::~CallGraph() -{} - -// Visualization -//----------------------------------------------------------------------------- - diff --git a/src/Automaton/Cfa.cpp b/src/Automaton/Cfa.cpp index 6da51e85..9a4f796b 100644 --- a/src/Automaton/Cfa.cpp +++ b/src/Automaton/Cfa.cpp @@ -325,7 +325,7 @@ Cfa *AutomataSystem::createCfa(std::string name) Cfa* AutomataSystem::getAutomatonByName(llvm::StringRef name) const { - auto result = std::find_if(begin(), end(), [name](Cfa& cfa) { + auto result = std::find_if(begin(), end(), [name](const Cfa& cfa) { return cfa.getName() == name; }); diff --git a/src/Automaton/CfaClone.cpp b/src/Automaton/CfaClone.cpp new file mode 100644 index 00000000..235e8338 --- /dev/null +++ b/src/Automaton/CfaClone.cpp @@ -0,0 +1,122 @@ +//==-------------------------------------------------------------*- C++ -*--==// +// +// Copyright 2021 Contributors to the Gazer project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +#include "gazer/Automaton/CfaTransforms.h" + +#include "gazer/Core/Expr/ExprRewrite.h" + +using namespace gazer; + +CfaCloneResult gazer::CloneCfa(Cfa& cfa, const std::string& nameSuffix) +{ + CfaCloneResult result; + + AutomataSystem& system = cfa.getParent(); + Cfa* newCfa = system.createCfa(cfa.getName().str() + nameSuffix); + + llvm::DenseMap locToLocMap(cfa.node_size()); + llvm::DenseMap variablesMap(cfa.getNumLocals()); + + for (Location* loc : cfa.nodes()) { + Location* newLoc; + if (loc->isError()) { + newLoc = newCfa->createErrorLocation(); + } else { + newLoc = newCfa->createLocation(); + } + locToLocMap[loc] = newLoc; + } + locToLocMap[cfa.getEntry()] = newCfa->getEntry(); + locToLocMap[cfa.getExit()] = newCfa->getExit(); + + for (Variable* variable : cfa.locals()) { + Variable* newVar = newCfa->createLocal(variable->getName(), variable->getType()); + variablesMap[variable] = newVar; + } + + // Add inputs and outputs - order matters here. + for (unsigned i = 0; i < cfa.getNumInputs(); ++i) { + Variable* variable = cfa.getInput(i); + Variable* newVar = newCfa->createInput(variable->getName(), variable->getType()); + variablesMap[variable] = newVar; + } + + for (unsigned i = 0; i < cfa.getNumOutputs(); ++i) { + Variable* variable = cfa.getOutput(i); + Variable* newVar = variablesMap[variable]; + newCfa->addOutput(newVar); + } + + auto exprBuilder = CreateExprBuilder(system.getContext()); + VariableExprRewrite exprRewrite(*exprBuilder); + for (auto& [oldVar, newVar] : variablesMap) { + exprRewrite[oldVar] = newVar->getRefExpr(); + } + + for (Transition* edge : cfa.edges()) { + if (auto* assign = llvm::dyn_cast(edge)) { + std::vector newAssigns; + newAssigns.reserve(assign->getNumAssignments()); + for (const VariableAssignment& va : *assign) { + newAssigns.emplace_back(variablesMap[va.getVariable()], exprRewrite.rewrite(va.getValue())); + } + + newCfa->createAssignTransition( + locToLocMap[assign->getSource()], + locToLocMap[assign->getTarget()], + exprRewrite.rewrite(assign->getGuard()), + newAssigns + ); + } else if (auto* call = llvm::dyn_cast(edge)) { + Cfa* newCallee = call->getCalledAutomaton() == &cfa ? newCfa : call->getCalledAutomaton(); + + std::vector newInputs; + newInputs.reserve(call->getNumInputs()); + for (const VariableAssignment& va : call->inputs()) { + newInputs.emplace_back(va.getVariable(), exprRewrite.rewrite(va.getValue())); + } + + std::vector newOutputs; + newOutputs.reserve(call->getNumOutputs()); + for (const VariableAssignment& va : call->outputs()) { + newOutputs.emplace_back(variablesMap[va.getVariable()], va.getValue()); + } + + newCfa->createCallTransition( + locToLocMap[call->getSource()], + locToLocMap[call->getTarget()], + exprRewrite.rewrite(call->getGuard()), + newCallee, + newInputs, + newOutputs + ); + } else { + llvm_unreachable("Unknown transition kind!"); + } + } + + // Copy error codes + for (const auto& [loc, expr] : cfa.errors()) { + newCfa->addErrorCode(locToLocMap[loc], exprRewrite.rewrite(expr)); + } + + result.clonedCfa = newCfa; + result.locToLocMap = locToLocMap; + result.varToVarMap = variablesMap; + + return result; +} diff --git a/src/Automaton/CfaInline.cpp b/src/Automaton/CfaInline.cpp new file mode 100644 index 00000000..a2809e1f --- /dev/null +++ b/src/Automaton/CfaInline.cpp @@ -0,0 +1,169 @@ +//==-------------------------------------------------------------*- C++ -*--==// +// +// Copyright 2019 Contributors to the Gazer project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +#include "gazer/Automaton/CfaTransforms.h" + +#include "gazer/Core/Expr/ExprRewrite.h" + +#include + +using namespace gazer; + +CfaInlineResult gazer::InlineCall( + CallTransition* call, + Location* errorLoc, + Variable* errorFieldVariable, + const std::string& suffix) +{ + assert(call->getSource() != nullptr && call->getTarget() != nullptr); + + Cfa* callee = call->getCalledAutomaton(); + Cfa* target = call->getSource()->getAutomaton(); + + Location* before = call->getSource(); + Location* after = call->getTarget(); + + auto exprBuilder = CreateExprBuilder(target->getParent().getContext()); + VariableExprRewrite rewrite(*exprBuilder); + + CfaInlineResult result; + + // Clone local variables into the caller + for (Variable* local : callee->locals()) { + if (!callee->isOutput(local)) { + auto varname = local->getName() + suffix; + auto* newLocal = target->createLocal(varname, local->getType()); + result.oldVarToNew[local] = newLocal; + result.inlinedVariables[newLocal] = local; + rewrite[local] = newLocal->getRefExpr(); + } + } + + // Clone input variables as well; we will insert an assign transition + // with the initial values later. + std::vector inputTemporaries; + for (Variable* input : callee->inputs()) { + if (!callee->isOutput(input)) { + auto varname = input->getName() + suffix; + auto* newInput = target->createLocal(varname, input->getType()); + result.oldVarToNew[input] = newInput; + result.inlinedVariables[newInput] = input; + //rewrite[input] = call->getInputArgument(i); + rewrite[input] = newInput->getRefExpr(); + + auto* val = target->createLocal(varname+"_", input->getType()); + inputTemporaries.emplace_back(val); + } + } + + for (Variable* output : callee->outputs()) { + auto argument = call->getOutputArgument(*output); + assert(argument.has_value() && "Every callee output should be assigned in a call transition!"); + + auto* newOutput = argument->getVariable(); + result.oldVarToNew[output] = newOutput; + result.inlinedVariables[newOutput] = output; + rewrite[output] = newOutput->getRefExpr(); + } + + // Insert all locations + for (Location* origLoc : callee->nodes()) { + auto* newLoc = target->createLocation(); + result.locToLocMap[origLoc] = newLoc; + result.inlinedLocations[newLoc] = origLoc; + if (origLoc->isError()) { + target->createAssignTransition(newLoc, errorLoc, exprBuilder->True(), { + { errorFieldVariable, callee->getErrorFieldExpr(origLoc) } + }); + } + } + + for (auto* origEdge : callee->edges()) { + Transition* newEdge; + Location* from = result.locToLocMap[origEdge->getSource()]; + Location* to = result.locToLocMap[origEdge->getTarget()]; + + if (auto* assign = llvm::dyn_cast(origEdge)) { + // Transform the assignments of this edge to use the new variables. + std::vector newAssigns; + std::transform( + assign->begin(), assign->end(), std::back_inserter(newAssigns), + [&result, &rewrite] (const VariableAssignment& origAssign) { + return VariableAssignment { + result.oldVarToNew[origAssign.getVariable()], + rewrite.rewrite(origAssign.getValue()) + }; + } + ); + + newEdge = target->createAssignTransition( + from, to, rewrite.rewrite(assign->getGuard()), newAssigns + ); + } else if (auto* nestedCall = llvm::dyn_cast(origEdge)) { + std::vector newArgs; + std::vector newOuts; + + std::transform( + nestedCall->input_begin(), nestedCall->input_end(), + std::back_inserter(newArgs), + [&rewrite](const VariableAssignment& a) { + return VariableAssignment{a.getVariable(), rewrite.rewrite(a.getValue())}; + } + ); + std::transform( + nestedCall->output_begin(), nestedCall->output_end(), + std::back_inserter(newOuts), + [&result](const VariableAssignment& origAssign) { + Variable* newVar = result.oldVarToNew.lookup(origAssign.getVariable()); + assert(newVar != nullptr && "All variables should be present in the variable map!"); + + return VariableAssignment{ + newVar, + origAssign.getValue() + }; + } + ); + + auto* callEdge = target->createCallTransition( + from, to, + rewrite.rewrite(nestedCall->getGuard()), + nestedCall->getCalledAutomaton(), + newArgs, + newOuts + ); + + newEdge = callEdge; + result.newCalls.push_back(callEdge); + } else { + llvm_unreachable("Unknown transition kind!"); + } + + result.edgeToEdgeMap[origEdge] = newEdge; + } + + std::vector inputAssigns; + for (const auto& input : call->inputs()) { + VariableAssignment inputAssignment(result.oldVarToNew[input.getVariable()], input.getValue()); + inputAssigns.push_back(inputAssignment); + } + + target->createAssignTransition(before, result.locToLocMap[callee->getEntry()], call->getGuard(), inputAssigns); + target->createAssignTransition(result.locToLocMap[callee->getExit()], after , exprBuilder->True()); + target->disconnectEdge(call); + + return result; +} diff --git a/src/Automaton/CfaUtils.cpp b/src/Automaton/CfaUtils.cpp index e2636764..15a28c4e 100644 --- a/src/Automaton/CfaUtils.cpp +++ b/src/Automaton/CfaUtils.cpp @@ -17,21 +17,55 @@ //===----------------------------------------------------------------------===// #include "gazer/Automaton/CfaUtils.h" #include "gazer/Core/Expr/ExprBuilder.h" +#include "gazer/Core/Expr/ExprRewrite.h" + +#include #include using namespace gazer; +// Topological sorts +//===----------------------------------------------------------------------===// +CfaTopoSort::CfaTopoSort(Cfa &cfa) +{ + auto poBegin = llvm::po_begin(cfa.getEntry()); + auto poEnd = llvm::po_end(cfa.getEntry()); + + mLocations.reserve(cfa.getNumLocations()); + mLocations.insert(mLocations.end(), poBegin, poEnd); + std::reverse(mLocations.begin(), mLocations.end()); + + mLocNumbers.reserve(mLocations.size()); + for (size_t i = 0; i < mLocations.size(); ++i) { + mLocNumbers[mLocations[i]] = i; + } +} + +Location* CfaTopoSort::operator[](size_t idx) const +{ + assert(idx >= 0 && idx < mLocations.size() && "Out of bounds access on a topological sort!"); + return mLocations[idx]; +} + +size_t CfaTopoSort::indexOf(Location *location) const +{ + auto it = mLocNumbers.find(location); + assert(it != mLocNumbers.end() && "Attempting to fetch a non-existing index from a topological sort!"); + + return it->second; +} + + // Calculating path conditions //===----------------------------------------------------------------------===// PathConditionCalculator::PathConditionCalculator( - const std::vector& topo, + const CfaTopoSort& topo, ExprBuilder& builder, - std::function index, std::function calls, std::function preds -) : mTopo(topo), mExprBuilder(builder), mIndex(index), mCalls(calls), mPredecessors(preds) +) : mTopo(topo), mExprBuilder(builder), mCalls(calls), mPredecessors(preds) {} namespace @@ -39,14 +73,12 @@ namespace struct PathPredecessor { - Transition* edge; - size_t idx; + Transition* edge = nullptr; + size_t idx = 0; ExprPtr expr; - PathPredecessor() = default; - PathPredecessor(Transition* edge, size_t idx, ExprPtr expr) - : edge(edge), idx(idx), expr(expr) + : edge(edge), idx(idx), expr(std::move(expr)) {} }; @@ -58,8 +90,8 @@ ExprPtr PathConditionCalculator::encode(Location* source, Location* target) return mExprBuilder.True(); } - size_t startIdx = mIndex(source); - size_t targetIdx = mIndex(target); + size_t startIdx = mTopo.indexOf(source); + size_t targetIdx = mTopo.indexOf(target); auto& ctx = mExprBuilder.getContext(); assert(startIdx < targetIdx && "The source location must be before the target in a topological sort!"); @@ -78,7 +110,7 @@ ExprPtr PathConditionCalculator::encode(Location* source, Location* target) llvm::SmallVector preds; for (Transition* edge : loc->incoming()) { - size_t predIdx = mIndex(edge->getSource()); + size_t predIdx = mTopo.indexOf(edge->getSource()); assert(predIdx < i + startIdx && "Predecessors must be before block in a topological sort. " "Maybe there is a loop in the automaton?"); @@ -153,9 +185,6 @@ ExprPtr PathConditionCalculator::encode(Location* source, Location* target) } for (size_t j = 0; j < preds.size(); ++j) { - Transition* edge = preds[j].edge; - size_t predIdx = preds[j].idx; - ExprPtr predIdentification = mExprBuilder.True(); if (predDisc != nullptr) { @@ -185,8 +214,7 @@ ExprPtr PathConditionCalculator::encode(Location* source, Location* target) Location* gazer::findLowestCommonDominator( const std::vector& targets, - const std::vector& topo, - std::function index, + const CfaTopoSort& topo, Location* start) { if (targets.empty()) { @@ -199,12 +227,12 @@ Location* gazer::findLowestCommonDominator( } // Find the last interesting index in the topological sort. - auto end = std::max_element(targets.begin(), targets.end(), [index](auto& a, auto& b) { - return index(a->getSource()) < index(b->getSource()); + auto end = std::max_element(targets.begin(), targets.end(), [&topo](auto& a, auto& b) { + return topo.indexOf(a->getSource()) < topo.indexOf(b->getSource()); }); - size_t startIdx = index(start); - size_t lastIdx = index((*end)->getTarget()); + size_t startIdx = topo.indexOf(start); + size_t lastIdx = topo.indexOf((*end)->getTarget()); assert(lastIdx > startIdx && "The last interesting index must be larger than the start index!"); @@ -226,7 +254,7 @@ Location* gazer::findLowestCommonDominator( boost::dynamic_bitset<> bs(numLocs); bs.set(); for (Transition* edge : loc->incoming()) { - size_t predIdx = index(edge->getSource()); + size_t predIdx = topo.indexOf(edge->getSource()); assert(predIdx < i + startIdx && "Predecessors must be before node in a topological sort. " "Maybe there is a loop in the automaton?"); @@ -249,7 +277,7 @@ Location* gazer::findLowestCommonDominator( boost::dynamic_bitset<> commonDominators(numLocs); commonDominators.set(); for (Transition* edge : targets) { - size_t idx = index(edge->getSource()); + size_t idx = topo.indexOf(edge->getSource()); commonDominators = commonDominators & dominators[idx - startIdx]; } @@ -270,8 +298,7 @@ Location* gazer::findLowestCommonDominator( Location* gazer::findHighestCommonPostDominator( const std::vector& targets, - const std::vector& topo, - std::function index, + const CfaTopoSort& topo, Location* start ) { @@ -285,12 +312,12 @@ Location* gazer::findHighestCommonPostDominator( } // Find the last interesting index in the topological sort. - auto end = std::min_element(targets.begin(), targets.end(), [index](auto& a, auto& b) { - return index(a->getSource()) < index(b->getSource()); + auto end = std::min_element(targets.begin(), targets.end(), [&topo](auto& a, auto& b) { + return topo.indexOf(a->getSource()) < topo.indexOf(b->getSource()); }); - size_t startIdx = index(start); - size_t lastIdx = index((*end)->getSource()); + size_t startIdx = topo.indexOf(start); + size_t lastIdx = topo.indexOf((*end)->getSource()); assert(lastIdx < startIdx && "The last interesting index must be larger than the start index!"); @@ -312,7 +339,7 @@ Location* gazer::findHighestCommonPostDominator( boost::dynamic_bitset<> bs(numLocs); bs.set(); for (Transition* edge : loc->outgoing()) { - size_t succIdx = index(edge->getTarget()); + size_t succIdx = topo.indexOf(edge->getTarget()); if (succIdx > startIdx) { // We are skipping the predecessors we are not interested in. @@ -332,7 +359,7 @@ Location* gazer::findHighestCommonPostDominator( boost::dynamic_bitset<> commonDominators(numLocs); commonDominators.set(); for (Transition* edge : targets) { - size_t idx = index(edge->getTarget()); + size_t idx = topo.indexOf(edge->getTarget()); commonDominators = commonDominators & dominators[startIdx - idx]; } @@ -349,4 +376,20 @@ Location* gazer::findHighestCommonPostDominator( } return topo[startIdx - commonDominatorIndex]; -} \ No newline at end of file +} + +// Call contexts +//==------------------------------------------------------------------------==// +ExprPtr gazer::applyCallTransitionCallingContext(CallTransition *call, const ExprPtr &expr, ExprBuilder& exprBuilder) +{ + VariableExprRewrite variableExprRewrite(exprBuilder); + for (const VariableAssignment& input : call->inputs()) { + variableExprRewrite[input.getVariable()] = input.getValue(); + } + for (const VariableAssignment& output : call->outputs()) { + variableExprRewrite[llvm::cast(output.getValue())] = output.getVariable()->getRefExpr(); + } + + return variableExprRewrite.rewrite(expr); +} + diff --git a/src/Automaton/RecursiveToCyclicCfa.cpp b/src/Automaton/RecursiveToCyclicCfa.cpp index c713d11a..980fbb83 100644 --- a/src/Automaton/RecursiveToCyclicCfa.cpp +++ b/src/Automaton/RecursiveToCyclicCfa.cpp @@ -63,11 +63,9 @@ RecursiveToCyclicResult RecursiveToCyclicTransformer::transform() { this->addUniqueErrorLocation(); - for (Transition* edge : mRoot->edges()) { - if (auto call = llvm::dyn_cast(edge)) { - if (mCallGraph.isTailRecursive(call->getCalledAutomaton())) { - mTailRecursiveCalls.push_back(call); - } + for (auto* call : classof_range(mRoot->edges())) { + if (mCallGraph.isTailRecursive(call->getCalledAutomaton())) { + mTailRecursiveCalls.push_back(call); } } @@ -94,189 +92,63 @@ RecursiveToCyclicResult RecursiveToCyclicTransformer::transform() void RecursiveToCyclicTransformer::inlineCallIntoRoot(CallTransition* call, llvm::Twine suffix) { Cfa* callee = call->getCalledAutomaton(); - Location* before = call->getSource(); - Location* after = call->getTarget(); + CfaInlineResult inlineResult = InlineCall(call, mError, mErrorFieldVariable, suffix.str()); + + // TODO: This is super not-nice: we are copying all the maps from the rewrite object that we + // have to re-create after the inlining. VariableExprRewrite rewrite(*mExprBuilder); - llvm::DenseMap locToLocMap; - llvm::DenseMap oldVarToNew; - - // Clone all local variables into the parent - for (Variable& local : callee->locals()) { - if (!callee->isOutput(&local)) { - auto varname = (local.getName() + suffix).str(); - auto newLocal = mRoot->createLocal(varname, local.getType()); - oldVarToNew[&local] = newLocal; - mInlinedVariables[newLocal] = &local; - rewrite[&local] = newLocal->getRefExpr(); + llvm::for_each(inlineResult.oldVarToNew, [&rewrite](auto& entry) { + rewrite[entry.first] = entry.second->getRefExpr(); + }); + + // See the newly inserted calls: if it is the same automaton (callee is guaranteed to be + // tail-recursive at this point), replace the call edge to the final location with a back-edge + // to the entry location. + for (CallTransition* newCall : inlineResult.newCalls) { + if (newCall->getCalledAutomaton() != callee) { + if (mCallGraph.isTailRecursive(newCall->getCalledAutomaton())) { + mTailRecursiveCalls.emplace_back(newCall); + } + continue; } - } - // Clone input variables as well; we will insert an assign transition - // with the initial values later. - std::vector inputTemporaries; - for (Variable& input : callee->inputs()) { - if (!callee->isOutput(&input)) { - auto varname = (input.getName() + suffix).str(); - auto newInput = mRoot->createLocal(varname, input.getType()); - oldVarToNew[&input] = newInput; - mInlinedVariables[newInput] = &input; - //rewrite[input] = call->getInputArgument(i); - rewrite[&input] = newInput->getRefExpr(); - - auto val = mRoot->createLocal(varname+"_", input.getType()); - inputTemporaries.emplace_back(val); - } - } + std::vector recursiveInputArgs; + std::vector inputTemporaries; + for (size_t i = 0; i < callee->getNumInputs(); ++i) { + // result variable is different then original inputs #46 + // to simulate parallel assignments + Variable* realInput = callee->getInput(i); + Variable* tempInput = mRoot->createLocal(realInput->getName() + "_temp", realInput->getType()); - for (Variable& output : callee->outputs()) { - auto argument = call->getOutputArgument(output); - assert(argument.has_value() && "Every callee output should be assigned in a call transition!"); + auto value = rewrite.rewrite(newCall->getInputArgument(*realInput)->getValue()); - auto newOutput = argument->getVariable(); - oldVarToNew[&output] = newOutput; - mInlinedVariables[newOutput] = &output; - rewrite[&output] = newOutput->getRefExpr(); - } + if (tempInput->getRefExpr() != value) { + // Do not add unneeded assignments (X := X). + recursiveInputArgs.emplace_back( tempInput, value ); + } - // Insert all locations - for (Location* origLoc : callee->nodes()) { - auto newLoc = mRoot->createLocation(); - locToLocMap[&*origLoc] = newLoc; - mInlinedLocations[newLoc] = origLoc; - if (origLoc->isError()) { - mRoot->createAssignTransition(newLoc, mError, mExprBuilder->True(), { - { mErrorFieldVariable, callee->getErrorFieldExpr(origLoc) } - }); + inputTemporaries.emplace_back(tempInput); } - } - // Clone the edges - for (Transition* origEdge : callee->edges()) { - Location* source = locToLocMap[origEdge->getSource()]; - Location* target = locToLocMap[origEdge->getTarget()]; - - if (auto assign = llvm::dyn_cast(&*origEdge)) { - std::vector newAssigns; - std::transform( - assign->begin(), assign->end(), std::back_inserter(newAssigns), - [&oldVarToNew, &rewrite] (const VariableAssignment& origAssign) { - return VariableAssignment { - oldVarToNew[origAssign.getVariable()], - rewrite.walk(origAssign.getValue()) - }; - } - ); - - mRoot->createAssignTransition( - source, target, rewrite.walk(assign->getGuard()), newAssigns - ); - } else if (auto nestedCall = llvm::dyn_cast(&*origEdge)) { - if (nestedCall->getCalledAutomaton() == callee) { - // This is where the magic happens: if we are calling this - // same automaton, replace the recursive call with a back-edge - // to the entry. - std::vector recursiveInputArgs; - for (size_t i = 0; i < callee->getNumInputs(); ++i) { - // result variable is different then original inputs #46 - // to simulate parallel assignments - Variable* input = inputTemporaries[i]; - Variable* realInput = callee->getInput(i); - - auto variable = input; - auto value = rewrite.walk(nestedCall->getInputArgument(*realInput)->getValue()); - - if (variable->getRefExpr() != value) { - // Do not add unneeded assignments (X := X). - recursiveInputArgs.push_back({ - variable, - value - }); - } - } - - for (size_t i = 0; i < callee->getNumInputs(); ++i) { - Variable* inputTemp = inputTemporaries[i]; - Variable* realInput = callee->getInput(i); - - auto variable = oldVarToNew[realInput]; - auto value = inputTemp->getRefExpr(); - - recursiveInputArgs.push_back({ - variable, - value - }); - } - - // Create the assignment back-edge. - mRoot->createAssignTransition( - source, locToLocMap[callee->getEntry()], - nestedCall->getGuard(), recursiveInputArgs - ); - } else { - // Inline it as a normal call. - std::vector newArgs; - std::vector newOuts; - - std::transform( - nestedCall->input_begin(), nestedCall->input_end(), - std::back_inserter(newArgs), - [&rewrite](const VariableAssignment& assign) { - return VariableAssignment{assign.getVariable(), rewrite.walk(assign.getValue())}; - } - ); - std::transform( - nestedCall->output_begin(), nestedCall->output_end(), - std::back_inserter(newOuts), - [&oldVarToNew](const VariableAssignment& origAssign) { - Variable* newVar = oldVarToNew.lookup(origAssign.getVariable()); - assert(newVar != nullptr - && "All variables should be present in the variable map!"); - - return VariableAssignment{ - newVar, - origAssign.getValue() - }; - } - ); - - auto newCall = mRoot->createCallTransition( - source, target, - rewrite.walk(nestedCall->getGuard()), - nestedCall->getCalledAutomaton(), - newArgs, newOuts - ); - - if (mCallGraph.isTailRecursive(nestedCall->getCalledAutomaton())) { - // If the call is to another tail-recursive automaton, we add it - // to the worklist. - mTailRecursiveCalls.push_back(newCall); - } - } - } - } - - std::vector inputArgs; - for (size_t i = 0; i < callee->getNumInputs(); ++i) { - Variable* input = callee->getInput(i); - inputArgs.push_back({ - oldVarToNew[input], - call->getInputArgument(*input)->getValue() - }); - } + for (size_t i = 0; i < callee->getNumInputs(); ++i) { + Variable* inputTemp = inputTemporaries[i]; + Variable* realInput = callee->getInput(i); - // We set the input variables to their initial values on a transition - // between 'before' and the entry of the called CFA. - mRoot->createAssignTransition( - before, locToLocMap[callee->getEntry()], call->getGuard(), inputArgs - ); + Variable* variable = inlineResult.oldVarToNew[realInput]; + ExprPtr value = inputTemp->getRefExpr(); + recursiveInputArgs.emplace_back( variable, value ); + } - mRoot->createAssignTransition( - locToLocMap[callee->getExit()], after , mExprBuilder->True() - ); + // Create the assignment back-edge. + mRoot->createAssignTransition( + newCall->getSource(), inlineResult.locToLocMap[callee->getEntry()], + newCall->getGuard(), recursiveInputArgs + ); + mRoot->disconnectEdge(newCall); + } - // Remove the original call edge - mRoot->disconnectEdge(call); + mRoot->clearDisconnectedElements(); } void RecursiveToCyclicTransformer::addUniqueErrorLocation() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4599a1b7..53aa5d0b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,6 +3,7 @@ add_subdirectory(Automaton) add_subdirectory(LLVM) add_subdirectory(Trace) add_subdirectory(Support) +add_subdirectory(Verifier) # Add requested solvers if ("z3" IN_LIST GAZER_ENABLE_SOLVERS) diff --git a/src/Core/CMakeLists.txt b/src/Core/CMakeLists.txt index 52790560..65657929 100644 --- a/src/Core/CMakeLists.txt +++ b/src/Core/CMakeLists.txt @@ -10,7 +10,6 @@ set(SOURCE_FILES Expr/ExprPrinter.cpp Expr/ExprEvaluator.cpp Expr/ExprRewrite.cpp - Expr/ExprUtils.cpp ) add_library(GazerCore SHARED ${SOURCE_FILES}) diff --git a/src/Core/Expr.cpp b/src/Core/Expr.cpp index 3a6554c7..db62b973 100644 --- a/src/Core/Expr.cpp +++ b/src/Core/Expr.cpp @@ -34,8 +34,7 @@ std::size_t gazer::expr_kind_prime(Expr::ExprKind kind) 290623u, 241291u, 579499u, 384287u, 125287u, 920273u, 485833u, 326449u, 972683u, 485167u, 882599u, 535727u, 383651u, 159833u, 796001u, 218479u, 163993u, 622561u, 938881u, 692467u, 851971u, 478427u, 653969u, 650329u, - 645187u, 830827u, 431729u, 497663u, 392351u, 715237u, 111323u, - 359641u + 645187u, 830827u, 431729u, 497663u, 392351u, 715237u, 111323u, 359641u }; static_assert( @@ -53,7 +52,7 @@ std::size_t gazer::expr_kind_prime(Expr::ExprKind kind) //------------------- Expression creation and destruction -------------------// Expr::Expr(Expr::ExprKind kind, Type &type) - : mKind(kind), mType(type), mRefCount(0) + : mKind(kind), mType(type) {} void Expr::DeleteExpr(gazer::Expr *expr) @@ -328,7 +327,7 @@ ExprRef TupleSelectExpr::Create(const ExprPtr& tuple, unsigned assert(tupTy->getNumSubtypes() > index && "Invalid tuple index!"); auto& context = tupTy->getContext(); - return context.pImpl->Exprs.create(tupTy->getTypeAtIndex(index), { tuple }, index); + return context.pImpl->Exprs.create(tupTy->getSubType(index), { tuple }, index); } @@ -411,7 +410,7 @@ llvm::StringRef Expr::getKindName(ExprKind kind) #define GAZER_EXPR_KIND(KIND) case KIND: return #KIND; switch (kind) { #include "gazer/Core/Expr/ExprKind.def" - }; + } #undef GAZER_EXPR_KIND llvm_unreachable("Invalid expression kind."); diff --git a/src/Core/Expr/ExprBuilder.cpp b/src/Core/Expr/ExprBuilder.cpp index 09b4f08c..4ff79c74 100644 --- a/src/Core/Expr/ExprBuilder.cpp +++ b/src/Core/Expr/ExprBuilder.cpp @@ -28,13 +28,22 @@ ExprPtr ExprBuilder::createTupleConstructor(TupleType& type, const ExprVector& m return TupleConstructExpr::Create(type, members); } +ExprPtr ExprBuilder::Tuple(const ExprVector &elems) +{ + std::vector subtypes; + llvm::transform(elems, std::back_inserter(subtypes), [](auto& e) { return &e->getType(); }); + + TupleType& type = TupleType::Get(subtypes); + return this->createTupleConstructor(type, elems); +} + ExprPtr ExprBuilder::TupleInsert(const ExprPtr &tuple, const ExprPtr &value, unsigned int index) { assert(tuple->getType().isTupleType() && "Can only perform tuple insertion on tuples!"); auto& tupleTy = llvm::cast(tuple->getType()); assert(index <= tupleTy.getNumSubtypes() && "Tuple insert index out of range!"); - assert(tupleTy.getTypeAtIndex(index) == value->getType() && "Tuple insert of incompatible types!"); + assert(tupleTy.getSubType(index) == value->getType() && "Tuple insert of incompatible types!"); ExprVector newMembers; newMembers.reserve(tupleTy.getNumSubtypes()); diff --git a/src/Core/Expr/ExprEvaluator.cpp b/src/Core/Expr/ExprEvaluator.cpp index 1973604a..c9cd683b 100644 --- a/src/Core/Expr/ExprEvaluator.cpp +++ b/src/Core/Expr/ExprEvaluator.cpp @@ -48,7 +48,7 @@ ExprRef ExprEvaluatorBase::visitExpr(const ExprPtr& expr) llvm_unreachable("Unhandled expression type in ExprEvaluatorBase"); } -ExprRef ExprEvaluatorBase::visitLiteral(const ExprRef& expr) { +ExprRef ExprEvaluatorBase::visitLiteral(const ExprRef& expr) { return expr; } diff --git a/src/Core/Expr/ExprPrinter.cpp b/src/Core/Expr/ExprPrinter.cpp index 9b4f5ed6..69c60ad4 100644 --- a/src/Core/Expr/ExprPrinter.cpp +++ b/src/Core/Expr/ExprPrinter.cpp @@ -81,15 +81,20 @@ std::string castTypeName(const llvm::Twine& name, const Type& from, const Type& return (name + "." + Twine(fromStr) + "." + Twine(toStr)).str(); } -class InfixPrintVisitor : public ExprWalker +class InfixPrintVisitor : public ExprWalker { - friend class ExprWalker; public: - InfixPrintVisitor(unsigned radix) + explicit InfixPrintVisitor(unsigned radix) : mRadix(radix) { assert(mRadix == 2 || mRadix == 8 || mRadix == 16 || mRadix == 10); } + + std::string print(const ExprPtr& expr) + { + return this->walk(expr); + } + private: std::string printOp(const ExprRef& expr, size_t idx) { @@ -101,28 +106,28 @@ class InfixPrintVisitor : public ExprWalker return (Twine("(") + Twine(opStr) + ")").str(); } -private: - std::string visitExpr(const ExprPtr& expr) { llvm_unreachable("Unknown expression kind!"); } - std::string visitUndef(const ExprRef& expr) { return "undef"; } - std::string visitVarRef(const ExprRef& expr) +protected: + std::string visitExpr(const ExprPtr& expr) override { llvm_unreachable("Unknown expression kind!"); } + std::string visitUndef(const ExprRef& expr) override { return "undef"; } + std::string visitVarRef(const ExprRef& expr) override { return expr->getVariable().getName(); } - std::string visitLiteral(const ExprRef& expr) + std::string visitLiteral(const ExprRef& expr) override { return printLiteral(expr); } // Define some helper macros #define PRINT_UNARY_PREFIX(NAME, OPERATOR) \ - std::string visit##NAME(const ExprRef& expr) \ + std::string visit##NAME(const ExprRef& expr) override \ { \ return (Twine{OPERATOR} + "(" + getOperand(0) + ")").str(); \ } #define PRINT_UNARY_CAST(NAME, OPERATOR) \ - std::string visit##NAME(const ExprRef& expr) \ + std::string visit##NAME(const ExprRef& expr) override \ { \ auto fname = castTypeName( \ OPERATOR, expr->getOperand()->getType(), expr->getType()); \ @@ -130,13 +135,13 @@ class InfixPrintVisitor : public ExprWalker } #define PRINT_BINARY_INFIX(NAME, OPERATOR) \ - std::string visit##NAME(const ExprRef& expr) \ + std::string visit##NAME(const ExprRef& expr) override \ { \ return (getOperand(0)) + (OPERATOR) + (getOperand(1)); \ } #define PRINT_BINARY_PREFIX(NAME, OPERATOR) \ - std::string visit##NAME(const ExprRef& expr) \ + std::string visit##NAME(const ExprRef& expr) override \ { \ return (Twine{OPERATOR} + "(" \ + (getOperand(0)) + "," + (getOperand(1)) + ")" \ @@ -144,7 +149,7 @@ class InfixPrintVisitor : public ExprWalker } // Unary - std::string visitNot(const ExprRef& expr) + std::string visitNot(const ExprRef& expr) override { return "not " + printOp(expr, 0); } @@ -152,7 +157,7 @@ class InfixPrintVisitor : public ExprWalker PRINT_UNARY_CAST(ZExt, "zext") PRINT_UNARY_CAST(SExt, "sext") - std::string visitExtract(const ExprRef& expr) + std::string visitExtract(const ExprRef& expr) override { auto fname = castTypeName("extract", expr->getOperand()->getType(), expr->getType()); return (fname + "(" + getOperand(0) @@ -179,7 +184,7 @@ class InfixPrintVisitor : public ExprWalker // Logic PRINT_BINARY_INFIX(Imply, "imply") - std::string visitAnd(const ExprRef& expr) + std::string visitAnd(const ExprRef& expr) override { std::string buffer; llvm::raw_string_ostream rso{buffer}; @@ -193,7 +198,7 @@ class InfixPrintVisitor : public ExprWalker return rso.str(); } - std::string visitOr(const ExprRef& expr) + std::string visitOr(const ExprRef& expr) override { std::string buffer; llvm::raw_string_ostream rso{buffer}; @@ -249,7 +254,7 @@ class InfixPrintVisitor : public ExprWalker PRINT_BINARY_PREFIX(FLtEq, "fp.le") // Ternary - std::string visitSelect(const ExprRef& expr) + std::string visitSelect(const ExprRef& expr) override { return (Twine("if ") + printOp(expr, 0) + Twine(" then ") + printOp(expr, 1) @@ -257,12 +262,12 @@ class InfixPrintVisitor : public ExprWalker } // Arrays - std::string visitArrayRead(const ExprRef& expr) + std::string visitArrayRead(const ExprRef& expr) override { return this->visitNonNullary(expr); } - std::string visitArrayWrite(const ExprRef& expr) + std::string visitArrayWrite(const ExprRef& expr) override { return this->visitNonNullary(expr); } @@ -299,20 +304,20 @@ class InfixPrintVisitor : public ExprWalker bv->getValue().toStringSigned(buffer, mRadix); rso << "bv" << bv->getType().getWidth(); - return rso.str(); + return rso.str().str(); } if (auto fl = llvm::dyn_cast(expr)) { fl->getValue().toString(buffer); rso << "fp" << fl->getType().getWidth(); - return rso.str(); + return rso.str().str(); } if (auto rl = llvm::dyn_cast(expr)) { rso << rl->getValue().numerator() << "%" << rl->getValue().denominator(); - return rso.str(); + return rso.str().str(); } if (auto al = llvm::dyn_cast(expr)) { @@ -336,7 +341,7 @@ class InfixPrintVisitor : public ExprWalker rso << llvm::join(orderedElems, ", "); rso << "]"; - return rso.str(); + return rso.str().str(); } llvm_unreachable("Unknown literal expression kind."); @@ -351,17 +356,10 @@ class InfixPrintVisitor : public ExprWalker namespace gazer { -void FormatPrintExpr(const ExprPtr& expr, llvm::raw_ostream& os) -{ - // TODO - InfixPrintVisitor visitor{10}; - os << visitor.walk(expr); -} - void InfixPrintExpr(const ExprPtr& expr, llvm::raw_ostream& os, unsigned bvRadix) { InfixPrintVisitor visitor{bvRadix}; - os << visitor.walk(expr); + os << visitor.print(expr); } } // end namespace gazer \ No newline at end of file diff --git a/src/Core/Expr/ExprRewrite.cpp b/src/Core/Expr/ExprRewrite.cpp index e570094d..8901dcc9 100644 --- a/src/Core/Expr/ExprRewrite.cpp +++ b/src/Core/Expr/ExprRewrite.cpp @@ -19,7 +19,7 @@ using namespace gazer; -ExprPtr ExprRewriteBase::rewriteNonNullary(const ExprRef& expr, const ExprVector& ops) +ExprPtr ExprRewrite::rewriteNonNullary(const ExprRef& expr, const ExprVector& ops) { switch (expr->getKind()) { case Expr::Not: return mExprBuilder.Not(ops[0]); @@ -119,6 +119,8 @@ ExprPtr ExprRewriteBase::rewriteNonNullary(const ExprRef& expr, case Expr::Select: return mExprBuilder.Select(ops[0], ops[1], ops[2]); case Expr::ArrayRead: return mExprBuilder.Read(ops[0], ops[1]); case Expr::ArrayWrite: return mExprBuilder.Write(ops[0], ops[1], ops[2]); + case Expr::TupleConstruct: return mExprBuilder.Tuple(ops); + case Expr::TupleSelect: return mExprBuilder.TupSel(ops[0], llvm::cast(expr)->getIndex()); // Add all the invalid cases as well case Expr::Literal: case Expr::Undef: @@ -131,9 +133,8 @@ ExprPtr ExprRewriteBase::rewriteNonNullary(const ExprRef& expr, ExprPtr VariableExprRewrite::visitVarRef(const ExprRef& expr) { - auto result = mRewriteMap[&expr->getVariable()]; - if (result != nullptr) { - return result; + if (auto r = mRewriteMap.find(&expr->getVariable()); r != mRewriteMap.end()) { + return r->second; } return expr; diff --git a/src/Core/Expr/ExprUtils.cpp b/src/Core/Expr/ExprUtils.cpp deleted file mode 100644 index 60248a9b..00000000 --- a/src/Core/Expr/ExprUtils.cpp +++ /dev/null @@ -1,43 +0,0 @@ -//==-------------------------------------------------------------*- C++ -*--==// -// -// Copyright 2019 Contributors to the Gazer project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -#include "gazer/Core/Expr/ExprUtils.h" - -#include - -using namespace gazer; - -unsigned gazer::ExprDepth(const ExprPtr& expr) -{ - if (expr->isNullary()) { - return 1; - } - - if (auto nn = llvm::dyn_cast(expr.get())) { - unsigned max = 0; - for (auto& op : nn->operands()) { - unsigned d = ExprDepth(op); - if (d > max) { - max = d; - } - } - - return 1 + max; - } - - llvm_unreachable("An expression cannot be nullary and non-nullary at the same time!"); -} diff --git a/src/Core/Expr/FoldingExprBuilder.cpp b/src/Core/Expr/FoldingExprBuilder.cpp index 54b92803..e96283e7 100644 --- a/src/Core/Expr/FoldingExprBuilder.cpp +++ b/src/Core/Expr/FoldingExprBuilder.cpp @@ -25,10 +25,7 @@ #include "gazer/Core/ExprTypes.h" #include "gazer/Core/LiteralExpr.h" #include "gazer/Core/Expr/Matcher.h" -#include "gazer/ADT/Algorithm.h" -#include -#include #include #include @@ -49,9 +46,7 @@ namespace class FoldingExprBuilder : public ExprBuilder { public: - FoldingExprBuilder(GazerContext& context) - : ExprBuilder(context) - {} + using ExprBuilder::ExprBuilder; private: ExprPtr foldBinaryExpr(Expr::ExprKind kind, const ExprPtr& left, const ExprPtr& right); @@ -67,6 +62,11 @@ class FoldingExprBuilder : public ExprBuilder ExprPtr x, y; + // Not(Not(X)) --> X + if (auto notOp = llvm::dyn_cast(op)) { + return notOp->getOperand(); + } + // Not(Or(Not(X), Y)) --> And(X, Not(Y)) if (match(op, m_Or(m_Not(m_Expr(x)), m_Expr(y)))) { return this->And({x, this->Not(y)}); @@ -83,7 +83,7 @@ class FoldingExprBuilder : public ExprBuilder return ZExtExpr::Create(op, type); } - + ExprPtr SExt(const ExprPtr& op, BvType& type) override { if (auto bvLit = dyn_cast(op.get())) { @@ -136,7 +136,7 @@ class FoldingExprBuilder : public ExprBuilder ExprPtr BvConcat(const ExprPtr& left, const ExprPtr& right) override { unsigned width = cast(left->getType()).getWidth() + cast(right->getType()).getWidth(); - + if (auto lhsLit = llvm::dyn_cast(left)) { if (auto rhsLit = llvm::dyn_cast(right)) { llvm::APInt result = lhsLit->getValue().zext(width).shl(rhsLit->getType().getWidth()); @@ -158,15 +158,25 @@ class FoldingExprBuilder : public ExprBuilder { ExprVector newOps; + // First, see which operands we need for (const ExprPtr& op : vector) { if (auto lit = dyn_cast(op)) { // We are not adding unnecessary true literals if (lit->isFalse()) { return this->False(); - } + } } else if (auto andExpr = dyn_cast(op)) { // For AndExpr operands, we flatten the expression newOps.insert(newOps.end(), andExpr->op_begin(), andExpr->op_end()); + } else if (auto notExpr = dyn_cast(op); notExpr != nullptr && isa(notExpr->getOperand())) { + // And(..., Not(And(X1, X2))) --> And(..., Or(Not(X1), Not(X2))) + auto nestedAnd = llvm::cast(notExpr->getOperand()); + std::vector orOps; + llvm::transform(nestedAnd->operands(), std::back_inserter(orOps), [this](auto& o) { + return this->Not(o); + }); + + newOps.push_back(this->Or(orOps)); } else { newOps.push_back(op); } @@ -176,7 +186,7 @@ class FoldingExprBuilder : public ExprBuilder // If we eliminated all operands return this->True(); } - + if (newOps.size() == 1) { return *newOps.begin(); } @@ -198,6 +208,15 @@ class FoldingExprBuilder : public ExprBuilder } else if (auto orExpr = dyn_cast(op)) { // For OrExpr operands, we try to flatten the expression newOps.insert(newOps.end(), orExpr->op_begin(), orExpr->op_end()); + } else if (auto notExpr = dyn_cast(op); notExpr != nullptr && isa(notExpr->getOperand())) { + // Or(..., Not(And(X1, X2))) --> Or(..., Not(X1), Not(X2)) + auto nestedAnd = llvm::cast(notExpr->getOperand()); + std::vector orOps; + llvm::transform(nestedAnd->operands(), std::back_inserter(orOps), [this](auto& o) { + return this->Not(o); + }); + + newOps.insert(newOps.end(), orOps.begin(), orOps.end()); } else { newOps.push_back(op); } @@ -321,41 +340,73 @@ class FoldingExprBuilder : public ExprBuilder ExprPtr BvSLt(const ExprPtr& left, const ExprPtr& right) override { + if (ExprPtr folded = this->foldBinaryCompare(Expr::BvSLt, left, right)) { + return folded; + } + return BvSLtExpr::Create(left, right); } ExprPtr BvSLtEq(const ExprPtr& left, const ExprPtr& right) override { + if (ExprPtr folded = this->foldBinaryCompare(Expr::BvSLtEq, left, right)) { + return folded; + } + return BvSLtEqExpr::Create(left, right); } ExprPtr BvSGt(const ExprPtr& left, const ExprPtr& right) override { + if (ExprPtr folded = this->foldBinaryCompare(Expr::BvSGt, left, right)) { + return folded; + } + return BvSGtExpr::Create(left, right); } ExprPtr BvSGtEq(const ExprPtr& left, const ExprPtr& right) override { + if (ExprPtr folded = this->foldBinaryCompare(Expr::BvSGtEq, left, right)) { + return folded; + } + return BvSGtEqExpr::Create(left, right); } ExprPtr BvULt(const ExprPtr& left, const ExprPtr& right) override { + if (ExprPtr folded = this->foldBinaryCompare(Expr::BvULt, left, right)) { + return folded; + } + return BvULtExpr::Create(left, right); } ExprPtr BvULtEq(const ExprPtr& left, const ExprPtr& right) override { + if (ExprPtr folded = this->foldBinaryCompare(Expr::BvULtEq, left, right)) { + return folded; + } + return BvULtEqExpr::Create(left, right); } ExprPtr BvUGt(const ExprPtr& left, const ExprPtr& right) override { + if (ExprPtr folded = this->foldBinaryCompare(Expr::BvUGt, left, right)) { + return folded; + } + return BvUGtExpr::Create(left, right); } ExprPtr BvUGtEq(const ExprPtr& left, const ExprPtr& right) override { + if (ExprPtr folded = this->foldBinaryCompare(Expr::BvUGtEq, left, right)) { + return folded; + } + return BvUGtEqExpr::Create(left, right); } @@ -584,7 +635,8 @@ ExprPtr FoldingExprBuilder::foldBinaryExpr(Expr::ExprKind kind, const ExprPtr& l break; case Expr::BvOr: if (rhs->isZero()) { return left; } // X or 0 == X - if (rhs->isAllOnes()) { return rhs; } // X and 1..1 == 1..1 + if (rhs->isAllOnes()) { return rhs; } // X and 1..1 == 1..1 + break; case Expr::BvXor: if (rhs->isZero()) { return left; } // X xor 0 == X break; @@ -597,10 +649,10 @@ ExprPtr FoldingExprBuilder::foldBinaryExpr(Expr::ExprKind kind, const ExprPtr& l } // See if both of them are literals - if (auto lhs = dyn_cast(left)) { - if (auto rhs = dyn_cast(right)) { - auto l = lhs->getValue(); - auto r = rhs->getValue(); + if (auto intLeft = dyn_cast(left)) { + if (auto intRight = dyn_cast(right)) { + auto l = intLeft->getValue(); + auto r = intRight->getValue(); switch (kind) { case Expr::Add: return this->IntLit(l + r); @@ -614,10 +666,10 @@ ExprPtr FoldingExprBuilder::foldBinaryExpr(Expr::ExprKind kind, const ExprPtr& l break; } } - } else if (auto lhs = dyn_cast(left)) { - if (auto rhs = dyn_cast(right)) { - auto l = lhs->getValue(); - auto r = rhs->getValue(); + } else if (auto bvLeft = dyn_cast(left)) { + if (auto bvRight = dyn_cast(right)) { + auto l = bvLeft->getValue(); + auto r = bvRight->getValue(); switch (kind) { case Expr::Add: return this->BvLit(l + r); @@ -647,7 +699,7 @@ ExprPtr FoldingExprBuilder::foldBinaryExpr(Expr::ExprKind kind, const ExprPtr& l case Expr::LShr: case Expr::AShr: case Expr::Shl: - if (lhs->isZero()) { return lhs; } + if (bvLeft->isZero()) { return bvLeft; } break; default: break; @@ -764,5 +816,5 @@ ExprPtr FoldingExprBuilder::simplifyLtEq(const ExprPtr& left, const ExprPtr& rig } std::unique_ptr gazer::CreateFoldingExprBuilder(GazerContext& context) { - return std::unique_ptr(new FoldingExprBuilder(context)); + return std::make_unique(context); } \ No newline at end of file diff --git a/src/Core/GazerContext.cpp b/src/Core/GazerContext.cpp index 431ee8ee..48ea699e 100644 --- a/src/Core/GazerContext.cpp +++ b/src/Core/GazerContext.cpp @@ -35,12 +35,10 @@ namespace boost std::terminate(); } - #if BOOST_VERSION >= 107300 void throw_exception(std::exception const &e, boost::source_location const&) { boost::throw_exception(e); } - #endif } // end namespace boost #endif @@ -62,7 +60,7 @@ bool ::gazer::IsDebugEnabled = false; using namespace gazer; GazerContext::GazerContext() - : pImpl(new GazerContextImpl(*this)) + : pImpl(std::make_unique(*this)) {} GazerContext::~GazerContext() = default; @@ -71,9 +69,14 @@ GazerContext::~GazerContext() = default; Variable* GazerContext::createVariable(const std::string& name, Type &type) { - LLVM_DEBUG(llvm::dbgs() << "Adding variable with name " << name << " and type " << type << "\n"); - GAZER_DEBUG_ASSERT(pImpl->VariableTable.count(name) == 0); - auto ptr = new Variable(name, type); + std::string finalName = name; + while (pImpl->VariableTable.count(finalName) != 0) { + finalName = name + std::to_string(pImpl->UniqueNameCounter); + ++pImpl->UniqueNameCounter; + } + + LLVM_DEBUG(llvm::dbgs() << "Adding variable with name " << finalName << " and type " << type << "\n"); + auto* ptr = new Variable(finalName, type); pImpl->VariableTable[name] = std::unique_ptr(ptr); GAZER_DEBUG(llvm::errs() diff --git a/src/Core/GazerContextImpl.h b/src/Core/GazerContextImpl.h index f26592dd..d5fb742e 100644 --- a/src/Core/GazerContextImpl.h +++ b/src/Core/GazerContextImpl.h @@ -120,7 +120,7 @@ struct expr_hasher().getValue()); - static std::size_t hash_value(Type& type, ValT value) { + static std::size_t hash_value([[maybe_unused]] Type& type, ValT value) { return llvm::hash_value(value); } @@ -136,11 +136,11 @@ struct expr_hasher struct expr_hasher { - static std::size_t hash_value(Type& type, llvm::APFloat value) { + static std::size_t hash_value(Type& type, const llvm::APFloat& value) { return llvm::hash_value(value); } - static bool equals(const Expr* other, Type& type, llvm::APFloat value) { + static bool equals(const Expr* other, Type& type, const llvm::APFloat& value) { if (auto bv = literal_equals(other, type)) { return bv->getValue().bitwiseIsEqual(value); } @@ -179,11 +179,11 @@ template<> struct expr_hasher }; template<> struct expr_hasher { - static std::size_t hash_value(Variable* variable) { + static std::size_t hash_value(const Variable* variable) { return llvm::hash_value(variable->getName()); } - static bool equals(const Expr* other, Variable* variable) { + static bool equals(const Expr* other, const Variable* variable) { if (auto expr = llvm::dyn_cast(other)) { return &expr->getVariable() == variable; } @@ -295,18 +295,13 @@ class ExprStorage struct Bucket { - Bucket() - : Ptr(nullptr) - {} - + Bucket() = default; Bucket(const Bucket&) = delete; - - Expr* Ptr; + Expr* Ptr = nullptr; }; public: ExprStorage() - : mBucketCount(DefaultBucketCount) { mStorage = new Bucket[mBucketCount]; } @@ -316,7 +311,7 @@ class ExprStorage template< class ExprTy, - class = std::enable_if::value>, + class = std::enable_if>, class... SubclassData > ExprRef create(Type &type, std::initializer_list init, SubclassData&&... data) { @@ -325,7 +320,7 @@ class ExprStorage template< class ExprTy, - class = std::enable_if::value>, + class = std::enable_if>, Expr::ExprKind Kind = ExprTypeToExprKind::Kind, class InputIterator, class... SubclassData @@ -339,7 +334,7 @@ class ExprStorage template< class ExprTy, - class = std::enable_if::value>, + class = std::enable_if>, class... ConstructorArgs > ExprRef create(ConstructorArgs&&... args) { return createIfNotExists(std::forward(args)...); @@ -383,7 +378,7 @@ class ExprStorage << "[ExprStorage] Created new " << Expr::getKindName(expr->getKind()) << " address " << expr << "\n" - ); + ) ++mEntryCount; @@ -405,16 +400,16 @@ class ExprStorage private: Bucket* mStorage; - size_t mBucketCount; + size_t mBucketCount = DefaultBucketCount; size_t mEntryCount = 0; }; class GazerContextImpl { friend class GazerContext; - explicit GazerContextImpl(GazerContext& ctx); public: + explicit GazerContextImpl(GazerContext& ctx); ~GazerContextImpl(); public: @@ -443,10 +438,12 @@ class GazerContextImpl //------------------- Expressions -------------------// ExprStorage Exprs; - ExprRef TrueLit, FalseLit; + ExprRef TrueLit; + ExprRef FalseLit; llvm::StringMap> VariableTable; -private: + //------------------ Other helpers ------------------// + unsigned UniqueNameCounter = 0; }; } // end namespace gazer diff --git a/src/Core/Type.cpp b/src/Core/Type.cpp index 0f10288d..17df5589 100644 --- a/src/Core/Type.cpp +++ b/src/Core/Type.cpp @@ -216,6 +216,12 @@ ArrayType::ArrayType(GazerContext& context, std::vector types) assert(mSubTypes.size() == 2); } +TupleType::TupleType(GazerContext& context, std::vector types) + : CompositeType(context, TupleTypeID, std::move(types)) +{ + assert(mSubTypes.size() >= 2); +} + ArrayType& ArrayType::Get(Type& indexType, Type& elementType) { auto& ctx = indexType.getContext(); diff --git a/src/Core/Valuation.cpp b/src/Core/Valuation.cpp index 4ae2d667..8461f108 100644 --- a/src/Core/Valuation.cpp +++ b/src/Core/Valuation.cpp @@ -22,7 +22,7 @@ using namespace gazer; -void Valuation::print(llvm::raw_ostream& os) +void Valuation::print(llvm::raw_ostream& os) const { for (auto& [variable, expr] : mMap) { os << variable->getName() << " = "; diff --git a/src/Core/Variable.cpp b/src/Core/Variable.cpp index c46d2ba8..e7666fbd 100644 --- a/src/Core/Variable.cpp +++ b/src/Core/Variable.cpp @@ -45,10 +45,6 @@ void VarRefExpr::print(llvm::raw_ostream& os) const { os << mVariable->getType().getName() << " " << mVariable->getName(); } -VariableAssignment::VariableAssignment() - : mVariable(nullptr), mValue(nullptr) -{} - VariableAssignment::VariableAssignment(Variable *variable, ExprPtr value) : mVariable(variable), mValue(std::move(value)) { diff --git a/src/LLVM/Analysis/PDG.cpp b/src/LLVM/Analysis/PDG.cpp deleted file mode 100644 index 7b04ca76..00000000 --- a/src/LLVM/Analysis/PDG.cpp +++ /dev/null @@ -1,205 +0,0 @@ -//==-------------------------------------------------------------*- C++ -*--==// -// -// Copyright 2019 Contributors to the Gazer project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -#include "gazer/LLVM/Analysis/PDG.h" - -#include -#include -#include -#include - -using namespace gazer; - -auto ProgramDependenceGraph::Create( - llvm::Function& function, llvm::PostDominatorTree& pdt -) - -> std::unique_ptr -{ - std::unordered_map> nodes; - std::vector> edges; - - // Create a node for each instruction. - for (llvm::Instruction& inst : llvm::instructions(function)) { - nodes.try_emplace(&inst, new PDGNode(&inst)); - } - - // Collect control dependencies. - std::unordered_map> controlDeps; - - // We are using the algorithm by Ferrante et al. - for (llvm::BasicBlock& bb : function) { - for (llvm::BasicBlock* succ : llvm::successors(&bb)) { - // Let S consist of all edges (A, B) in the control flow graph... - llvm::BasicBlock *a = &bb; - llvm::BasicBlock *b = succ; - - // ...such that B is not an ancestor of A in the post-dominator tree. - if (!pdt.dominates(b, a)) { - auto domA = pdt.getNode(a); - auto domB = pdt.getNode(b); - - // Given (A, B), the desired effect will be achieved by - // traversing backwards from B in the post-dominator tree - // until we reach A’s parent (if it exists), marking all nodes - // visited before A’s parent as control dependent on A. - auto parent = domB; - while (parent != nullptr && parent != domA->getIDom()) { - controlDeps[a].insert(parent->getBlock()); - parent = parent->getIDom(); - } - } - } - } - - // Insert control dependencies. - // If a block B control depends on a block A, then all of B's instructions - // will depend on A's terminator in the PDG. - for (auto& [block, deps] : controlDeps) { - llvm::Instruction* terminator = block->getTerminator(); - - auto& source = nodes[terminator]; - for (llvm::BasicBlock* dependentBlock : deps) { - for (llvm::Instruction& inst : *dependentBlock) { - auto& target = nodes[&inst]; - auto& edge = edges.emplace_back(new PDGEdge(&*source, &*target, PDGEdge::Control)); - source->addOutgoing(&*edge); - target->addIncoming(&*edge); - } - } - } - - // Insert data flow dependencies - - // Collect all memory-access instructions. In order to be conservative, we will - // assume that all memory-access instructions depend on every call, store and load. - std::vector memoryAccesses; - for (llvm::Instruction& inst : llvm::instructions(function)) { - if (inst.mayReadFromMemory()) { - memoryAccesses.push_back(&inst); - } - } - - for (llvm::Instruction& inst : llvm::instructions(function)) { - // All uses of an instruction 'I' flow depend on 'I' - for (auto& use_it : inst.operands()) { - if (llvm::isa(&use_it)) { - auto use = llvm::dyn_cast(&use_it); - if (use != &inst) { - auto& source = nodes[use]; - auto& target = nodes[&inst]; - auto& edge = edges.emplace_back(new PDGEdge(&*source, &*target, PDGEdge::DataFlow)); - - source->addOutgoing(&*edge); - target->addIncoming(&*edge); - } - } - } - - if (auto phi = llvm::dyn_cast(&inst)) { - // PHI nodes may also depend on their incoming blocks - for (unsigned i = 0; i < phi->getNumIncomingValues(); ++i) { - llvm::BasicBlock* incoming = phi->getIncomingBlock(i); - auto& source = nodes[incoming->getTerminator()]; - auto& target = nodes[phi]; - auto& edge = edges.emplace_back(new PDGEdge(&*source, &*target, PDGEdge::DataFlow)); - - source->addOutgoing(&*edge); - target->addIncoming(&*edge); - } - } - - // TODO: We could be smarter with some alias analysis here. - if (inst.mayWriteToMemory()) { - for (llvm::Instruction* memRead : memoryAccesses) { - auto& source = nodes[&inst]; - auto& target = nodes[memRead]; - auto& edge = edges.emplace_back(new PDGEdge(&*source, &*target, PDGEdge::Memory)); - - source->addOutgoing(&*edge); - target->addIncoming(&*edge); - } - } - } - - return std::unique_ptr(new ProgramDependenceGraph( - function, std::move(nodes), std::move(edges) - )); -} - -void ProgramDependenceGraph::view() const -{ - // Create a random file in which we will dump the PDG - int fd; - std::string filename = llvm::createGraphFilename("", fd); - - llvm::raw_fd_ostream os(fd, /*shouldClose=*/ true); - if (fd == -1) { - llvm::errs() << "error opening file '" << filename << "' for writing!\n"; - return; - } - - os << "digraph PDG {\n"; - os << "node [shape=\"box\"];\n"; - for (auto& bb : mFunction) { - os << "subgraph cluster_" << static_cast(&bb) << " {\n"; - for (auto& it : bb) { - os << "node_" << static_cast(&it) << " [label=\""; - it.print(os, false); /* - if (it.getName() != "") { - it.printAsOperand(os, false); - os << " = "; - } - os << it.getOpcodeName(); - - if (it.getNumOperands() != 0) { - os << " "; - for (auto op = it.op_begin(); op != it.op_end() - 1; ++op) { - (*op)->printAsOperand(os, false); - os << ", "; - } - (*(it.op_end() - 1))->printAsOperand(os, false); - } */ - os << "\"];\n"; - } - os << "graph[style=dashed];\n"; - os << "label= \""; - bb.printAsOperand(os); - os << "\";"; - os << "color=black;"; - os << "}\n"; - } - - for (auto& edge : mEdges) { - os - << "node_" << static_cast(edge->getSource()->getInstruction()) - << " -> " - << "node_" << static_cast(edge->getTarget()->getInstruction()) - << "[color=\""; - switch (edge->getKind()) { - case PDGEdge::DataFlow: os << "green"; break; - case PDGEdge::Control: os << "blue"; break; - case PDGEdge::Memory: os << "red"; break; - } - os << "\"];\n"; - } - - os << "}"; - - llvm::errs() << " done. \n"; - - llvm::DisplayGraph(filename, false, llvm::GraphProgram::DOT); -} \ No newline at end of file diff --git a/src/LLVM/Automaton/AutomatonPasses.cpp b/src/LLVM/Automaton/AutomatonPasses.cpp index 14f00c0c..3db220c8 100644 --- a/src/LLVM/Automaton/AutomatonPasses.cpp +++ b/src/LLVM/Automaton/AutomatonPasses.cpp @@ -31,7 +31,7 @@ using namespace gazer; using namespace gazer::llvm2cfa; std::unique_ptr gazer::translateModuleToAutomata( - llvm::Module& module, + llvm::Module& llvmModule, const LLVMFrontendSettings& settings, LoopInfoFuncTy loopInfos, GazerContext& context, @@ -44,7 +44,7 @@ std::unique_ptr gazer::translateModuleToAutomata( } LLVMTypeTranslator types(memoryModel.getMemoryTypeTranslator(), settings); - ModuleToCfa transformer(module, std::move(loopInfos), context, memoryModel, types, *specialFunctions, settings); + ModuleToCfa transformer(llvmModule, std::move(loopInfos), context, memoryModel, types, *specialFunctions, settings); return transformer.generate(blockEntries); } @@ -60,12 +60,12 @@ void ModuleToAutomataPass::getAnalysisUsage(llvm::AnalysisUsage& au) const au.setPreservesAll(); } -bool ModuleToAutomataPass::runOnModule(llvm::Module& module) +bool ModuleToAutomataPass::runOnModule(llvm::Module& llvmModule) { // We need to save loop information here as a on-the-fly LoopInfo pass would delete // the acquired loop information when the lambda function exits. llvm::DenseMap> loopInfos; - for (const llvm::Function& function : module) { + for (const llvm::Function& function : llvmModule) { if (!function.isDeclaration()) { // The const_cast is needed here as getAnalysis expects a non-const function. // However, it should be safe as DominatorTreeWrapper does not modify the function. @@ -75,7 +75,7 @@ bool ModuleToAutomataPass::runOnModule(llvm::Module& module) } } - auto loops = [&loopInfos](const llvm::Function* function) -> llvm::LoopInfo* { + auto loops = [&loopInfos](const llvm::Function* function) { auto& result = loopInfos[function]; assert(result != nullptr); return result.get(); @@ -85,7 +85,7 @@ bool ModuleToAutomataPass::runOnModule(llvm::Module& module) auto specialFunctions = SpecialFunctions::get(); mSystem = translateModuleToAutomata( - module, mSettings, loops, mContext, memoryModel, mTraceInfo, specialFunctions.get()); + llvmModule, mSettings, loops, mContext, memoryModel, mTraceInfo, specialFunctions.get()); if (mSettings.loops == LoopRepresentation::Cycle) { // Transform the main automaton into a cyclic CFA if requested. @@ -115,7 +115,7 @@ class PrintCfaPass : public llvm::ModulePass au.setPreservesAll(); } - bool runOnModule(llvm::Module& module) override + bool runOnModule(llvm::Module& llvmModule) override { auto& moduleToCfa = getAnalysis(); AutomataSystem& system = moduleToCfa.getSystem(); diff --git a/src/LLVM/Automaton/ExtensionPoints.cpp b/src/LLVM/Automaton/ExtensionPoints.cpp index 7da09b9d..b1620c0d 100644 --- a/src/LLVM/Automaton/ExtensionPoints.cpp +++ b/src/LLVM/Automaton/ExtensionPoints.cpp @@ -41,7 +41,7 @@ std::string GenerationContext::uniqueName(const llvm::Twine& base) name = (base + llvm::Twine(mTmp++)).toStringRef(buffer); } - return buffer.str(); + return buffer.str().str(); } void CfaGenInfo::addVariableToContext(ValueOrMemoryObject value, Variable* variable) @@ -64,6 +64,11 @@ const Cfa& ExtensionPoint::getCfa() const return *mGenInfo.Automaton; } +GazerContext& ExtensionPoint::getContext() const +{ + return mGenInfo.Automaton->getParent().getContext(); +} + llvm::Loop* ExtensionPoint::getSourceLoop() const { return mGenInfo.getSourceLoop(); @@ -76,11 +81,11 @@ llvm::Function* ExtensionPoint::getSourceFunction() const llvm::Function* ExtensionPoint::getParent() const { - if (auto fun = getSourceFunction()) { + if (llvm::Function* fun = getSourceFunction()) { return fun; } - if (auto loop = getSourceLoop()) { + if (llvm::Loop* loop = getSourceLoop()) { return loop->getHeader()->getParent(); } @@ -90,8 +95,8 @@ llvm::Function* ExtensionPoint::getParent() const bool ExtensionPoint::isEntryProcedure() const { llvm::Function* function = getParent(); - llvm::Module* module = function->getParent(); - return mGenInfo.Context.getSettings().getEntryFunction(*module) == function; + llvm::Module* llvmModule = function->getParent(); + return mGenInfo.Context.getSettings().getEntryFunction(*llvmModule) == function; } Variable* VariableDeclExtensionPoint::createInput(ValueOrMemoryObject val, Type& type, const std::string& suffix) @@ -151,13 +156,13 @@ Variable* AutomatonInterfaceExtensionPoint::getVariableFor(ValueOrMemoryObject v void LoopVarDeclExtensionPoint::createLoopOutput(ValueOrMemoryObject val, Variable* output, const llvm::Twine& suffix) { std::string name = (val.getName() + suffix).str(); - auto copyOfVar = mGenInfo.Automaton->createLocal(name, output->getType()); + auto* copyOfVar = mGenInfo.Automaton->createLocal(name, output->getType()); this->markOutput(val, copyOfVar); mGenInfo.LoopOutputs[val] = VariableAssignment(copyOfVar, output->getRefExpr()); } -// Genaration step extension point +// Generation step extension point //===----------------------------------------------------------------------===// auto GenerationStepExtensionPoint::createAuxiliaryVariable(const std::string& name, Type& type) @@ -166,9 +171,13 @@ auto GenerationStepExtensionPoint::createAuxiliaryVariable(const std::string& na return mGenInfo.Automaton->createLocal(name, type); } -auto BlocksToCfa::ExtensionPointImpl::getAsOperand(ValueOrMemoryObject val) -> ExprPtr +auto BlocksToCfa::ExtensionPointImpl::getAsOperand(ValueOrMemoryObject val, Type* type) -> ExprPtr { - return mBlocksToCfa.operand(val); + if (type == nullptr) { + return mBlocksToCfa.operand(val); + } + + return mBlocksToCfa.operand(val, *type); } bool BlocksToCfa::ExtensionPointImpl::tryToEliminate(ValueOrMemoryObject val, Variable* variable, const ExprPtr& expr) diff --git a/src/LLVM/Automaton/FunctionToCfa.h b/src/LLVM/Automaton/FunctionToCfa.h index d26cb091..52d4ae9c 100644 --- a/src/LLVM/Automaton/FunctionToCfa.h +++ b/src/LLVM/Automaton/FunctionToCfa.h @@ -40,8 +40,6 @@ namespace gazer { -extern llvm::cl::opt PrintTrace; - namespace llvm2cfa { @@ -278,7 +276,7 @@ class ModuleToCfa final static constexpr char LoopOutputSelectorName[] = "__output_selector"; ModuleToCfa( - llvm::Module& module, + llvm::Module& llvmModule, LoopInfoFuncTy loops, GazerContext& context, MemoryModel& memoryModel, @@ -331,7 +329,7 @@ class BlocksToCfa : public InstToExpr mBlocksToCfa(blocksToCfa), mAssigns(assigns), mEntry(entry), mExit(exit) {} - ExprPtr getAsOperand(ValueOrMemoryObject val) override; + ExprPtr getAsOperand(ValueOrMemoryObject val, Type* type) override; bool tryToEliminate(ValueOrMemoryObject val, Variable* variable, const ExprPtr& expr) override; void insertAssignment(Variable* variable, const ExprPtr& value) override; @@ -368,11 +366,11 @@ class BlocksToCfa : public InstToExpr private: bool tryToEliminate(ValueOrMemoryObject val, Variable* variable, const ExprPtr& expr); - void insertOutputAssignments(CfaGenInfo& callee, std::vector& outputArgs); + void insertOutputAssignments(const CfaGenInfo& callee, std::vector& outputArgs); void insertPhiAssignments(const llvm::BasicBlock* source, const llvm::BasicBlock* target, std::vector& phiAssignments); bool handleCall(const llvm::CallInst* call, Location** entry, Location* exit, std::vector& previousAssignments); - void handleTerminator(const llvm::BasicBlock* bb, Location* entry, Location* exit); + void handleTerminator(const llvm::BasicBlock* bb, Location* exit); void handleSuccessor( const llvm::BasicBlock* succ, diff --git a/src/LLVM/Automaton/InstToExpr.cpp b/src/LLVM/Automaton/InstToExpr.cpp index 50c9ecf0..40e89cad 100644 --- a/src/LLVM/Automaton/InstToExpr.cpp +++ b/src/LLVM/Automaton/InstToExpr.cpp @@ -38,19 +38,19 @@ ExprPtr InstToExpr::doTransform(const llvm::Instruction& inst, Type& expectedTyp { LLVM_DEBUG(llvm::dbgs() << " Transforming instruction " << inst << "\n"); - if (auto binOp = llvm::dyn_cast(&inst)) { + if (const auto* binOp = llvm::dyn_cast(&inst)) { return visitBinaryOperator(*binOp, expectedType); } - if (auto cast = llvm::dyn_cast(&inst)) { + if (const auto* cast = llvm::dyn_cast(&inst)) { return visitCastInst(*cast, expectedType); } - if (auto select = llvm::dyn_cast(&inst)) { + if (const auto* select = llvm::dyn_cast(&inst)) { return visitSelectInst(*select, expectedType); } - if (auto gep = llvm::dyn_cast(&inst)) { + if (const auto* gep = llvm::dyn_cast(&inst)) { std::vector ops; ops.reserve(gep->getNumOperands()); @@ -66,11 +66,11 @@ ExprPtr InstToExpr::doTransform(const llvm::Instruction& inst, Type& expectedTyp return visit##NAME(*llvm::cast(&inst)); \ } - HANDLE_INST(Instruction::ICmp, ICmpInst) - HANDLE_INST(Instruction::Call, CallInst) - HANDLE_INST(Instruction::FCmp, FCmpInst) - HANDLE_INST(Instruction::InsertValue, InsertValueInst) - HANDLE_INST(Instruction::ExtractValue, ExtractValueInst) + HANDLE_INST(Instruction::ICmp, ICmpInst) + HANDLE_INST(Instruction::Call, CallInst) + HANDLE_INST(Instruction::FCmp, FCmpInst) + HANDLE_INST(Instruction::InsertValue, InsertValueInst) + HANDLE_INST(Instruction::ExtractValue, ExtractValueInst) #undef HANDLE_INST @@ -82,15 +82,6 @@ ExprPtr InstToExpr::doTransform(const llvm::Instruction& inst, Type& expectedTyp // Transformation functions //----------------------------------------------------------------------------- -static bool isLogicInstruction(unsigned opcode) { - return opcode == Instruction::And || opcode == Instruction::Or || opcode == Instruction::Xor; -} - -static bool isFloatInstruction(unsigned opcode) { - return opcode == Instruction::FAdd || opcode == Instruction::FSub - || opcode == Instruction::FMul || opcode == Instruction::FDiv; -} - static bool isNonConstValue(const llvm::Value* value) { return isa(value) || isa(value) || isa(value); } @@ -99,13 +90,18 @@ ExprPtr InstToExpr::visitBinaryOperator(const llvm::BinaryOperator& binop, Type& { auto lhs = operand(binop.getOperand(0)); auto rhs = operand(binop.getOperand(1)); - - auto opcode = binop.getOpcode(); - if (isLogicInstruction(opcode) && binop.getType()->isIntegerTy(1)) { + + assert(lhs->getType() == rhs->getType() && "Binary operator operand types must match!"); + + unsigned opcode = binop.getOpcode(); + + if (expectedType.isBoolType()) { + assert(binop.getType()->isIntegerTy(1) && "Can only represent boolean operations with 1-bit integer types!"); + auto boolLHS = asBool(lhs); auto boolRHS = asBool(rhs); - switch (binop.getOpcode()) { + switch (opcode) { case Instruction::And: return mExprBuilder.And(boolLHS, boolRHS); case Instruction::Or: @@ -116,9 +112,8 @@ ExprPtr InstToExpr::visitBinaryOperator(const llvm::BinaryOperator& binop, Type& llvm_unreachable("Unknown logic instruction opcode"); } } - - if (isFloatInstruction(opcode)) { - ExprPtr expr; + + if (expectedType.isFloatType()) { switch (binop.getOpcode()) { case Instruction::FAdd: return mExprBuilder.FAdd(lhs, rhs, llvm::APFloat::rmNearestTiesToEven); @@ -131,23 +126,18 @@ ExprPtr InstToExpr::visitBinaryOperator(const llvm::BinaryOperator& binop, Type& default: llvm_unreachable("Invalid floating-point operation"); } - - return expr; } - assert(expectedType.isIntType() || expectedType.isBvType()); - if (expectedType.isBvType()) { - BvType& bvType = llvm::cast(expectedType); + auto& bvType = llvm::cast(expectedType); - auto intLHS = asBv(lhs, bvType.getWidth()); - auto intRHS = asBv(rhs, bvType.getWidth()); + auto bvLHS = asBv(lhs, bvType.getWidth()); + auto bvRHS = asBv(rhs, bvType.getWidth()); - #define HANDLE_INSTCASE(OPCODE, EXPRNAME) \ - case OPCODE: \ - return mExprBuilder.EXPRNAME(intLHS, intRHS); \ + #define HANDLE_INSTCASE(OPCODE, EXPRNAME) \ + case OPCODE: \ + return mExprBuilder.EXPRNAME(bvLHS, bvRHS); \ - ExprPtr expr; switch (binop.getOpcode()) { HANDLE_INSTCASE(Instruction::Add, Add) HANDLE_INSTCASE(Instruction::Sub, Sub) @@ -200,14 +190,14 @@ ExprPtr InstToExpr::visitBinaryOperator(const llvm::BinaryOperator& binop, Type& } } - llvm_unreachable("Invalid binary operation kind"); + llvm_unreachable("Invalid expected type for binary operation!"); } ExprPtr InstToExpr::visitSelectInst(const llvm::SelectInst& select, Type& expectedType) { - auto cond = asBool(operand(select.getCondition())); - auto then = castResult(operand(select.getTrueValue()), expectedType); - auto elze = castResult(operand(select.getFalseValue()), expectedType); + auto cond = operand(select.getCondition(), BoolType::Get(mContext)); + auto then = operand(select.getTrueValue(), expectedType); + auto elze = operand(select.getFalseValue(), expectedType); return mExprBuilder.Select(cond, then, elze); } @@ -243,6 +233,58 @@ ExprPtr InstToExpr::unsignedLessThan(const ExprPtr& left, const ExprPtr& right) ); } +ExprPtr InstToExpr::translateICmpBv(llvm::CmpInst::Predicate predicate, const ExprPtr& left, const ExprPtr& right) +{ + #define HANDLE_PREDICATE(PREDNAME, EXPRNAME) \ + case PREDNAME: \ + return mExprBuilder.EXPRNAME(left, right); \ + + switch (predicate) { + HANDLE_PREDICATE(CmpInst::ICMP_UGT, BvUGt) + HANDLE_PREDICATE(CmpInst::ICMP_UGE, BvUGtEq) + HANDLE_PREDICATE(CmpInst::ICMP_ULT, BvULt) + HANDLE_PREDICATE(CmpInst::ICMP_ULE, BvULtEq) + HANDLE_PREDICATE(CmpInst::ICMP_SGT, BvSGt) + HANDLE_PREDICATE(CmpInst::ICMP_SGE, BvSGtEq) + HANDLE_PREDICATE(CmpInst::ICMP_SLT, BvSLt) + HANDLE_PREDICATE(CmpInst::ICMP_SLE, BvSLtEq) + default: + llvm_unreachable("Unknown ICMP predicate."); + } + + #undef HANDLE_PREDICATE +} + +ExprPtr InstToExpr::translateICmpInt(llvm::CmpInst::Predicate predicate, const ExprPtr& left, const ExprPtr& right) +{ + switch (predicate) { + case CmpInst::ICMP_UGT: + return unsignedLessThan(right, left); + case CmpInst::ICMP_SGT: + return mExprBuilder.Gt(left, right); + case CmpInst::ICMP_UGE: + return mExprBuilder.Or( + mExprBuilder.Eq(left, right), + unsignedLessThan(right, left) + ); + case CmpInst::ICMP_SGE: + return mExprBuilder.GtEq(left, right); + case CmpInst::ICMP_ULT: + return unsignedLessThan(left, right); + case CmpInst::ICMP_SLT: + return mExprBuilder.Lt(left, right); + case CmpInst::ICMP_ULE: + return mExprBuilder.Or( + mExprBuilder.Eq(left, right), + unsignedLessThan(left, right) + ); + case CmpInst::ICMP_SLE: + return mExprBuilder.LtEq(left, right); + default: + llvm_unreachable("Unknown ICMP predicate."); + } +} + ExprPtr InstToExpr::visitICmpInst(const llvm::ICmpInst& icmp) { using llvm::CmpInst; @@ -259,54 +301,12 @@ ExprPtr InstToExpr::visitICmpInst(const llvm::ICmpInst& icmp) return mExprBuilder.NotEq(left, right); } - #define HANDLE_PREDICATE(PREDNAME, EXPRNAME) \ - case PREDNAME: \ - return mExprBuilder.EXPRNAME(left, right); \ - if (left->getType().isBvType()) { - switch (pred) { - HANDLE_PREDICATE(CmpInst::ICMP_UGT, BvUGt) - HANDLE_PREDICATE(CmpInst::ICMP_UGE, BvUGtEq) - HANDLE_PREDICATE(CmpInst::ICMP_ULT, BvULt) - HANDLE_PREDICATE(CmpInst::ICMP_ULE, BvULtEq) - HANDLE_PREDICATE(CmpInst::ICMP_SGT, BvSGt) - HANDLE_PREDICATE(CmpInst::ICMP_SGE, BvSGtEq) - HANDLE_PREDICATE(CmpInst::ICMP_SLT, BvSLt) - HANDLE_PREDICATE(CmpInst::ICMP_SLE, BvSLtEq) - default: - llvm_unreachable("Unknown ICMP predicate."); - } + return this->translateICmpBv(pred, left, right); } - #undef HANDLE_PREDICATE - if (left->getType().isIntType()) { - switch (pred) { - case CmpInst::ICMP_UGT: - return unsignedLessThan(right, left); - case CmpInst::ICMP_SGT: - return mExprBuilder.Gt(left, right); - case CmpInst::ICMP_UGE: - return mExprBuilder.Or( - mExprBuilder.Eq(left, right), - unsignedLessThan(right, left) - ); - case CmpInst::ICMP_SGE: - return mExprBuilder.GtEq(left, right); - case CmpInst::ICMP_ULT: - return unsignedLessThan(left, right); - case CmpInst::ICMP_SLT: - return mExprBuilder.Lt(left, right); - case CmpInst::ICMP_ULE: - return mExprBuilder.Or( - mExprBuilder.Eq(left, right), - unsignedLessThan(left, right) - ); - case CmpInst::ICMP_SLE: - return mExprBuilder.LtEq(left, right); - default: - llvm_unreachable("Unknown ICMP predicate."); - } + return this->translateICmpInt(pred, left, right); } llvm_unreachable("Invalid type for comparison instruction!"); @@ -323,6 +323,22 @@ ExprPtr InstToExpr::visitFCmpInst(const llvm::FCmpInst& fcmp) ExprPtr cmpExpr = nullptr; switch (pred) { + case CmpInst::FCMP_FALSE: + return mExprBuilder.False(); + case CmpInst::FCMP_TRUE: + return mExprBuilder.True(); + case CmpInst::FCMP_ORD: + // Both operands must be not-NaN + return mExprBuilder.And( + mExprBuilder.Not(mExprBuilder.FIsNan(left)), + mExprBuilder.Not(mExprBuilder.FIsNan(right)) + ); + case CmpInst::FCMP_UNO: + // True if either operand is NaN + return mExprBuilder.Or( + mExprBuilder.FIsNan(left), + mExprBuilder.FIsNan(right) + ); case CmpInst::FCMP_OEQ: case CmpInst::FCMP_UEQ: cmpExpr = mExprBuilder.FEq(left, right); @@ -351,38 +367,23 @@ ExprPtr InstToExpr::visitFCmpInst(const llvm::FCmpInst& fcmp) break; } - ExprPtr expr = nullptr; - if (pred == CmpInst::FCMP_FALSE) { - expr = mExprBuilder.False(); - } else if (pred == CmpInst::FCMP_TRUE) { - expr = mExprBuilder.True(); - } else if (pred == CmpInst::FCMP_ORD) { - expr = mExprBuilder.And( - mExprBuilder.Not(mExprBuilder.FIsNan(left)), - mExprBuilder.Not(mExprBuilder.FIsNan(right)) - ); - } else if (pred == CmpInst::FCMP_UNO) { - expr = mExprBuilder.Or( - mExprBuilder.FIsNan(left), - mExprBuilder.FIsNan(right) - ); - } else if (CmpInst::isOrdered(pred)) { + if (CmpInst::isOrdered(pred)) { // An ordered instruction can only be true if it has no NaN operands. // As our comparison operators are defined to be false if either // argument is NaN, we can just return the compare expression. - expr = cmpExpr; - } else if (CmpInst::isUnordered(pred)) { + return cmpExpr; + } + + if (CmpInst::isUnordered(pred)) { // An unordered instruction may be true if either operand is NaN - expr = mExprBuilder.Or({ + return mExprBuilder.Or({ mExprBuilder.FIsNan(left), mExprBuilder.FIsNan(right), cmpExpr }); - } else { - llvm_unreachable("Invalid FCmp predicate"); } - return expr; + llvm_unreachable("Unknown FCMP predicate"); } ExprPtr InstToExpr::visitCastInst(const llvm::CastInst& cast, Type& expectedType) @@ -449,10 +450,10 @@ ExprPtr InstToExpr::tryToRepresentBitOperator(const llvm::BinaryOperator& binOp, assert(binOp.isBitwiseLogicOp() || llvm::BinaryOperator::isShift(binOp.getOpcode())); unsigned width = binOp.getType()->getIntegerBitWidth(); - if (auto rhs = llvm::dyn_cast(right)) { + if (auto* rhs = llvm::dyn_cast(right)) { llvm::APInt rightBv(width, static_cast(rhs->getValue())); - if (auto lhs = llvm::dyn_cast(left)) { + if (auto* lhs = llvm::dyn_cast(left)) { // If both operands are constants, calculate their value and represent them as arithmetic ints. llvm::APInt leftBv(width, static_cast(lhs->getValue())); @@ -482,10 +483,7 @@ ExprPtr InstToExpr::tryToRepresentBitOperator(const llvm::BinaryOperator& binOp, break; } } - - // FIXME: Some additional magic may be applied here to handle certain AND, OR and shift values, - // such as a single one/zero in the bit mask, etc. - } else if (auto lhs = llvm::dyn_cast(left)) { + } else if (auto* lhs = llvm::dyn_cast(left)) { llvm::APInt leftBv(width, static_cast(lhs->getValue())); if (lhs->isZero()) { switch (binOp.getOpcode()) { @@ -517,7 +515,7 @@ ExprPtr InstToExpr::tryToRepresentBitOperator(const llvm::BinaryOperator& binOp, ExprPtr InstToExpr::integerCast(const llvm::CastInst& cast, const ExprPtr& castOperand, Type& expectedType) { - if (auto bvTy = llvm::dyn_cast(&expectedType)) { + if (auto* bvTy = llvm::dyn_cast(&expectedType)) { ExprPtr intOp = asBv(castOperand, bvTy->getWidth()); switch (cast.getOpcode()) { @@ -586,7 +584,7 @@ ExprPtr InstToExpr::integerCast(const llvm::CastInst& cast, const ExprPtr& castO ExprPtr InstToExpr::boolToIntCast(const llvm::CastInst& cast, const ExprPtr& operand, Type& expectedType) { - if (auto bvTy = dyn_cast(&expectedType)) { + if (auto* bvTy = dyn_cast(&expectedType)) { auto one = llvm::APInt{1, 1}; auto zero = llvm::APInt{1, 0}; @@ -633,15 +631,15 @@ ExprPtr InstToExpr::bitCast(const ExprPtr &castOperand, Type &expectedType) { Type& srcType = castOperand->getType(); - if (auto floatTy = llvm::dyn_cast(&srcType)) { - if (auto bvTy = llvm::dyn_cast(&expectedType)) { + if (auto* floatTy = llvm::dyn_cast(&srcType)) { + if (auto* bvTy = llvm::dyn_cast(&expectedType)) { assert(floatTy->getWidth() == bvTy->getWidth() && "Can only perform bit-cast between types of equal width!"); return mExprBuilder.FpToBv(castOperand, *bvTy); } } - if (auto bvTy = llvm::dyn_cast(&srcType)) { - if (auto floatTy = llvm::dyn_cast(&expectedType)) { + if (auto* bvTy = llvm::dyn_cast(&srcType)) { + if (auto* floatTy = llvm::dyn_cast(&expectedType)) { assert(floatTy->getWidth() == bvTy->getWidth() && "Can only perform bit-cast between types of equal width!"); return mExprBuilder.BvToFp(castOperand, *floatTy); } @@ -653,7 +651,7 @@ ExprPtr InstToExpr::bitCast(const ExprPtr &castOperand, Type &expectedType) static GazerIntrinsic::Overflow getOverflowKind(llvm::StringRef name) { - #define HANDLE_PREFIX(PREFIX, KIND) \ + #define HANDLE_PREFIX(PREFIX, KIND) \ if (name.startswith(PREFIX)) { return GazerIntrinsic::Overflow::KIND; } \ HANDLE_PREFIX(GazerIntrinsic::SAddNoOverflowPrefix, SAdd) @@ -832,9 +830,14 @@ ExprPtr InstToExpr::operand(ValueOrMemoryObject value) llvm_unreachable("Invalid ValueOrMemoryObject state!"); } +ExprPtr InstToExpr::operand(ValueOrMemoryObject value, Type& expectedType) +{ + return castResult(operand(value), expectedType); +} + ExprPtr InstToExpr::operandValue(const llvm::Value* value) { - if (auto ci = dyn_cast(value)) { + if (auto* ci = dyn_cast(value)) { // Check for boolean literals if (ci->getType()->isIntegerTy(1)) { return ci->isZero() ? mExprBuilder.False() : mExprBuilder.True(); @@ -853,11 +856,11 @@ ExprPtr InstToExpr::operandValue(const llvm::Value* value) llvm_unreachable("Invalid int representation strategy!"); } - if (auto cfp = dyn_cast(value)) { + if (auto* cfp = dyn_cast(value)) { return mExprBuilder.FloatLit(cfp->getValueAPF()); } - if (auto ca = dyn_cast(value)) { + if (auto* ca = dyn_cast(value)) { // Translate each element in the array std::vector> elements; elements.reserve(ca->getNumElements()); @@ -874,7 +877,7 @@ ExprPtr InstToExpr::operandValue(const llvm::Value* value) return mMemoryInstHandler.handleConstantDataArray(ca, elements); } - if (auto caz = dyn_cast(value)) { + if (auto* caz = dyn_cast(value)) { return mMemoryInstHandler.handleZeroInitializedAggregate(caz); } @@ -886,8 +889,7 @@ ExprPtr InstToExpr::operandValue(const llvm::Value* value) } if (isNonConstValue(value)) { - auto result = this->lookupInlinedVariable(value); - if (result != nullptr) { + if (auto result = lookupInlinedVariable(value)) { return result; } @@ -904,8 +906,7 @@ ExprPtr InstToExpr::operandValue(const llvm::Value* value) ExprPtr InstToExpr::operandMemoryObject(const gazer::MemoryObjectDef* def) { - auto result = this->lookupInlinedVariable(def); - if (result != nullptr) { + if (auto result = this->lookupInlinedVariable(def)) { return result; } @@ -919,8 +920,8 @@ ExprPtr InstToExpr::asBool(const ExprPtr& operand) } if (operand->getType().isBvType()) { - auto bvTy = cast(&operand->getType()); - unsigned bits = bvTy->getWidth(); + auto& bvTy = cast(operand->getType()); + unsigned bits = bvTy.getWidth(); return mExprBuilder.Select( mExprBuilder.Eq(operand, mExprBuilder.BvLit(0, bits)), diff --git a/src/LLVM/Automaton/ModuleToAutomata.cpp b/src/LLVM/Automaton/ModuleToAutomata.cpp index 7bc73594..6622a73d 100644 --- a/src/LLVM/Automaton/ModuleToAutomata.cpp +++ b/src/LLVM/Automaton/ModuleToAutomata.cpp @@ -68,35 +68,24 @@ static void memoryAccessOfKind(Range& range, llvm::SmallVectorImpl& { static_assert(std::is_base_of_v, "AccessKind must be a subclass of MemoryAccess!"); - for (auto& access : range) { - if (auto casted = llvm::dyn_cast(&access)) { - vec.push_back(casted); - } - } + llvm::for_each(classof_range(range), [&vec](auto& v) { + vec.push_back(&v); + }); } size_t BlocksToCfa::getNumUsesInBlocks(const llvm::Instruction* inst) const { - size_t cnt = 0; - for (auto user : inst->users()) { - if (auto i = llvm::dyn_cast(user)) { - if (mGenInfo.Blocks.count(i->getParent()) != 0 ) { - cnt += 1; - } - } - } - - return cnt; + return llvm::count_if(classof_range(inst->users()), [this](auto* i) { + return mGenInfo.Blocks.count(i->getParent()) != 0; + }); } template static bool hasUsesInBlockRange(const llvm::Instruction* inst, Range& range) { - for (auto user : inst->users()) { - if (auto i = llvm::dyn_cast(user)) { - if (std::find(std::begin(range), std::end(range), i->getParent()) != std::end(range)) { - return true; - } + for (auto i : classof_range(inst->users())) { + if (llvm::find(range, i->getParent()) != std::end(range)) { + return true; } } @@ -127,25 +116,10 @@ static bool checkUsesInBlockRange(const MemoryObjectDef* def, Range& range, bool return false; } -template -static bool hasUsesInBlockRange(const MemoryObjectDef* def, Range& range) -{ - return checkUsesInBlockRange(def, range, true); -} - -template -static bool hasUsesOutsideOfBlockRange(const MemoryObjectDef* def, Range& range) -{ - return checkUsesInBlockRange(def, range, false); -} - static bool isErrorBlock(llvm::BasicBlock* bb) { - // In error blocks, the last instruction before a terminator should be the - // 'gazer.error_code' call. - auto inst = bb->getTerminator()->getPrevNonDebugInstruction(); - - if (auto call = llvm::dyn_cast_or_null(inst)) { + // In error blocks, the last instruction before a terminator should be the 'gazer.error_code' call. + if (auto call = llvm::dyn_cast_or_null(bb->getTerminator()->getPrevNonDebugInstruction())) { Function* function = call->getCalledFunction(); if (function != nullptr && function->getName() == CheckRegistry::ErrorFunctionName) { return true; @@ -157,7 +131,7 @@ static bool isErrorBlock(llvm::BasicBlock* bb) /// If \p bb is part of a loop nested into the CFA represented by \p genInfo, returns this loop. /// Otherwise, this function returns nullptr. -static llvm::Loop* getNestedLoopOf(GenerationContext& genCtx, CfaGenInfo& genInfo, const llvm::BasicBlock* bb) +static llvm::Loop* getNestedLoopOf(GenerationContext& genCtx, const CfaGenInfo& genInfo, const llvm::BasicBlock* bb) { auto nested = genCtx.getLoopInfoFor(bb->getParent())->getLoopFor(bb); if (nested == nullptr) { @@ -195,30 +169,31 @@ static std::string getLoopName(const llvm::Loop* loop, unsigned& loopCount, llvm const BasicBlock* header = loop->getHeader(); assert(header != nullptr && "Loop without a loop header?"); - std::string name = prefix; + std::string name = prefix.str(); name += '/'; if (header->hasName()) { name += header->getName(); } else { - name += "__loop_" + std::to_string(loopCount++); + name += "__loop_" + std::to_string(loopCount); + ++loopCount; } return name; } ModuleToCfa::ModuleToCfa( - llvm::Module& module, + llvm::Module& llvmModule, LoopInfoFuncTy loops, GazerContext& context, MemoryModel& memoryModel, LLVMTypeTranslator& types, const SpecialFunctions& specialFunctions, const LLVMFrontendSettings& settings -) : mModule(module), +) : mModule(llvmModule), mContext(context), mMemoryModel(memoryModel), mSettings(settings), - mSystem(new AutomataSystem(context)), + mSystem(std::make_unique(context)), mGenCtx(*mSystem, mMemoryModel, types, std::move(loops), specialFunctions, settings) { if (mSettings.simplifyExpr) { @@ -273,7 +248,7 @@ void ModuleToCfa::createAutomata() auto& memoryInstHandler = mMemoryModel.getMemoryInstructionHandler(function); - Cfa* cfa = mSystem->createCfa(function.getName()); + Cfa* cfa = mSystem->createCfa(function.getName().str()); LLVM_DEBUG(llvm::dbgs() << "Created CFA " << cfa->getName() << "\n"); DenseSet visitedBlocks; @@ -300,10 +275,10 @@ void ModuleToCfa::createAutomata() ArrayRef loopBlocks = loop->getBlocks(); std::vector loopOnlyBlocks; - std::copy_if( - loopBlocks.begin(), loopBlocks.end(), + llvm::copy_if( + loopBlocks, std::back_inserter(loopOnlyBlocks), - [&visitedBlocks] (BasicBlock* b) { return visitedBlocks.count(b) == 0; } + [&visitedBlocks] (auto b) { return visitedBlocks.count(b) == 0; } ); // Declare loop variables. @@ -365,22 +340,19 @@ void ModuleToCfa::createAutomata() memoryInstHandler.declareFunctionVariables(functionVarDecl); // For the local variables, we only need to add the values not present in any of the loops. - for (BasicBlock& bb : function) { - LLVM_DEBUG(llvm::dbgs() << "Translating function-level block " << bb.getName() << "\n"); - for (Instruction& inst : bb) { - LLVM_DEBUG(llvm::dbgs().indent(2) << "Instruction " << inst.getName() << "\n"); - if (auto loop = loopInfo->getLoopFor(&bb)) { - // If the variable is an output of a loop, add it here as a local variable - Variable* output = mGenCtx.getLoopCfa(loop).findOutput(&inst); - if (output == nullptr && !hasUsesInBlockRange(&inst, functionBlocks)) { - LLVM_DEBUG(llvm::dbgs().indent(4) << "Not adding (no uses in function) " << inst << "\n"); - continue; - } + for (Instruction& inst : llvm::instructions(function)) { + LLVM_DEBUG(llvm::dbgs().indent(2) << "Instruction " << inst.getName() << "\n"); + if (auto loop = loopInfo->getLoopFor(inst.getParent())) { + // If the variable is an output of a loop, add it here as a local variable + Variable* output = mGenCtx.getLoopCfa(loop).findOutput(&inst); + if (output == nullptr && !hasUsesInBlockRange(&inst, functionBlocks)) { + LLVM_DEBUG(llvm::dbgs().indent(4) << "Not adding (no uses in function) " << inst << "\n"); + continue; } + } - if (!inst.getType()->isVoidTy()) { - functionVarDecl.createLocal(&inst, mGenCtx.getTypes().get(inst.getType())); - } + if (!inst.getType()->isVoidTy()) { + functionVarDecl.createLocal(&inst, mGenCtx.getTypes().get(inst.getType())); } } @@ -528,12 +500,12 @@ void BlocksToCfa::encode() mCfa->createAssignTransition(entry, exit, mExprBuilder.True(), assignments); // Handle the outgoing edges - this->handleTerminator(bb, entry, exit); + this->handleTerminator(bb, exit); } // Do a clean-up, remove eliminated variables from the CFA. if (!mGenCtx.getSettings().isElimVarsOff()) { - mCfa->removeLocalsIf([this](Variable* v) { + mCfa->removeLocalsIf([this](auto v) { return mEliminatedVarsSet.count(v) != 0; }); } @@ -586,7 +558,7 @@ bool BlocksToCfa::handleCall(const llvm::CallInst* call, Location** entry, Locat // FIXME: This const_cast is needed because the memory model interface must // take a mutable call site as ImmutableCallSite objects cannot be put into // a map properly. This should be removed as soon as something about that changes. - mMemoryInstHandler.handleCall(const_cast(call), callerEP, calleeEP, inputs, outputs); + mMemoryInstHandler.handleCall(call, callerEP, calleeEP, inputs, outputs); if (!callee->getReturnType()->isVoidTy()) { Variable* variable = getVariable(call); @@ -621,7 +593,7 @@ bool BlocksToCfa::handleCall(const llvm::CallInst* call, Location** entry, Locat return true; } -void BlocksToCfa::handleTerminator(const llvm::BasicBlock* bb, Location* entry, Location* exit) +void BlocksToCfa::handleTerminator(const llvm::BasicBlock* bb, Location* exit) { auto terminator = bb->getTerminator(); @@ -700,9 +672,8 @@ bool BlocksToCfa::tryToEliminate(ValueOrMemoryObject val, Variable* variable, co // On 'Normal' level, we do not want to inline expressions which have multiple uses // and have already inlined operands. - bool hasInlinedOperands = std::any_of(inst->op_begin(), inst->op_end(), [this](const llvm::Use& op) { - const llvm::Value* v = &*op; - return llvm::isa(v) && mInlinedVars.count(llvm::cast(v)) != 0; + bool hasInlinedOperands = llvm::any_of(inst->operands(), [this](const llvm::Use& op) { + return llvm::isa(op) && mInlinedVars.count(llvm::cast(op)) != 0; }); if (!mGenCtx.getSettings().isElimVarsAggressive() @@ -734,8 +705,7 @@ void BlocksToCfa::createExitTransition(const BasicBlock* source, const BasicBloc } // Add the possible loop exit assignments - for (auto& entry : mGenInfo.LoopOutputs) { - VariableAssignment& assign = entry.second; + for (auto& [_, assign] : mGenInfo.LoopOutputs) { exitAssigns.push_back(assign); } @@ -855,11 +825,9 @@ void BlocksToCfa::createCallToLoop(llvm::Loop* loop, const llvm::BasicBlock* sou llvm::SmallVector exitEdges; loop->getExitEdges(exitEdges); - for (llvm::Loop::Edge& exitEdge : exitEdges) { - LLVM_DEBUG(llvm::dbgs() << " Handling exit edge " << exitEdge.first->getName() - << " to " << exitEdge.second->getName() << "\n"); - const llvm::BasicBlock* inBlock = exitEdge.first; - const llvm::BasicBlock* exitBlock = exitEdge.second; + for (const auto& [inBlock, exitBlock] : exitEdges) { + LLVM_DEBUG(llvm::dbgs() << " Handling exit edge " << inBlock->getName() + << " to " << exitBlock->getName() << "\n"); std::vector phiAssignments; insertPhiAssignments(inBlock, exitBlock, phiAssignments); @@ -878,7 +846,7 @@ void BlocksToCfa::createCallToLoop(llvm::Loop* loop, const llvm::BasicBlock* sou } } -void BlocksToCfa::insertOutputAssignments(CfaGenInfo& callee, std::vector& outputArgs) +void BlocksToCfa::insertOutputAssignments(const CfaGenInfo& callee, std::vector& outputArgs) { // For the outputs, find the corresponding variables in the parent and create the assignments. for (auto& [value, nestedOutputVar] : callee.Outputs) { @@ -891,8 +859,7 @@ void BlocksToCfa::insertOutputAssignments(CfaGenInfo& callee, std::vectorsecond.getVariable(); } else { parentVar = mGenInfo.findVariable(value); diff --git a/src/LLVM/Automaton/SpecialFunctions.cpp b/src/LLVM/Automaton/SpecialFunctions.cpp index adc4afee..adaec41f 100644 --- a/src/LLVM/Automaton/SpecialFunctions.cpp +++ b/src/LLVM/Automaton/SpecialFunctions.cpp @@ -21,33 +21,39 @@ using namespace gazer; const SpecialFunctions SpecialFunctions::EmptyInstance; +static constexpr std::array AssumeFunctionNames = { + "__VERIFIER_assume", + "klee_assume", + "__llbmc_assume", + "llvm.assume" +}; + auto SpecialFunctions::get() -> std::unique_ptr { auto result = std::make_unique(); // Verifier assumptions - result->registerHandler("verifier.assume", &SpecialFunctions::handleAssume, SpecialFunctionHandler::Memory_Pure); - result->registerHandler("llvm.assume", &SpecialFunctions::handleAssume, SpecialFunctionHandler::Memory_Pure); + result->registerMultipleHandlers(AssumeFunctionNames, &SpecialFunctions::handleAssume, SpecialFunctionHandler::Memory_Pure); return result; } void SpecialFunctions::registerHandler( llvm::StringRef name, - SpecialFunctionHandler::HandlerFuncTy function, + const SpecialFunctionHandler::HandlerFuncTy& function, SpecialFunctionHandler::MemoryBehavior memory) { auto result = mHandlers.try_emplace(name, function, memory); assert(result.second && "Attempt to register duplicate handler!"); } -auto SpecialFunctions::handle(llvm::ImmutableCallSite cs, llvm2cfa::GenerationStepExtensionPoint& ep) const +auto SpecialFunctions::handle(const llvm::CallBase* cs, llvm2cfa::GenerationStepExtensionPoint& ep) const -> bool { - assert(cs.getCalledFunction() != nullptr); + assert(cs->getCalledFunction() != nullptr); // Check if we have an appropriate handler - auto it = mHandlers.find(cs.getCalledFunction()->getName()); + auto it = mHandlers.find(cs->getCalledFunction()->getName()); if (it == mHandlers.end()) { return false; } @@ -59,10 +65,10 @@ auto SpecialFunctions::handle(llvm::ImmutableCallSite cs, llvm2cfa::GenerationSt // Default handler implementations //===----------------------------------------------------------------------===// -void SpecialFunctions::handleAssume(llvm::ImmutableCallSite cs, llvm2cfa::GenerationStepExtensionPoint& ep) +void SpecialFunctions::handleAssume(const llvm::CallBase* cs, llvm2cfa::GenerationStepExtensionPoint& ep) { - const llvm::Value* arg = cs.getArgOperand(0); - ExprPtr assumeExpr = ep.getAsOperand(arg); + const llvm::Value* arg = cs->getArgOperand(0); + ExprPtr assumeExpr = ep.getAsOperand(arg, BoolType::Get(ep.getContext())); ep.splitCurrentTransition(assumeExpr); } diff --git a/src/LLVM/CMakeLists.txt b/src/LLVM/CMakeLists.txt index d459da2f..d2d1d285 100644 --- a/src/LLVM/CMakeLists.txt +++ b/src/LLVM/CMakeLists.txt @@ -2,8 +2,6 @@ set(SOURCE_FILES Transform/InlineGlobalVariablesPass.cpp Transform/UndefToNondet.cpp Transform/LiftErrorsPass.cpp - Transform/NormalizeVerifierCalls.cpp - Transform/BackwardSlicer.cpp Transform/Inline.cpp Transform/TransformUtils.cpp Instrumentation/MarkFunctionEntries.cpp @@ -30,13 +28,12 @@ set(SOURCE_FILES Automaton/AutomatonPasses.cpp Automaton/ExtensionPoints.cpp Automaton/ValueOrMemoryObject.cpp - Analysis/PDG.cpp Instrumentation/Checks/AssertionFailCheck.cpp Instrumentation/Checks/DivisionByZeroCheck.cpp - Instrumentation/Checks/SignedIntegerOverflowCheck.cpp Transform/LoopExitCanonizationPass.cpp) - -llvm_map_components_to_libnames(GAZER_LLVM_LIBS core irreader transformutils scalaropts ipo) -message(STATUS "Using LLVM libraries: ${GAZER_LLVM_LIBS}") + Instrumentation/Checks/SignedIntegerOverflowCheck.cpp + Transform/LoopExitCanonizationPass.cpp) add_library(GazerLLVM SHARED ${SOURCE_FILES}) -target_link_libraries(GazerLLVM ${GAZER_LLVM_LIBS} GazerCore GazerTrace GazerZ3Solver GazerAutomaton) + +require_llvm_libraries(GAZER_LLVM_LIBS core irreader transformutils scalaropts ipo) +target_link_libraries(GazerLLVM ${GAZER_LLVM_LIBS} GazerCore GazerTrace GazerAutomaton) diff --git a/src/LLVM/ClangFrontend.cpp b/src/LLVM/ClangFrontend.cpp index 97bb8aa5..e7651e4c 100644 --- a/src/LLVM/ClangFrontend.cpp +++ b/src/LLVM/ClangFrontend.cpp @@ -163,24 +163,28 @@ auto gazer::ClangCompileAndLink( llvm::SMDiagnostic err; // Find clang and llvm-link. - auto clang = llvm::sys::findProgramByName("clang-9"); - if (clang.getError()) clang = llvm::sys::findProgramByName("clang"); - CHECK_ERROR(clang.getError(), "Could not find clang-9 or clang."); + auto clang = llvm::sys::findProgramByName("clang-12"); + if (clang.getError()) { + clang = llvm::sys::findProgramByName("clang"); + } + CHECK_ERROR(clang.getError(), "Could not find clang-12 or clang.") - auto llvm_link = llvm::sys::findProgramByName("llvm-link-9"); - if (llvm_link.getError()) llvm_link = llvm::sys::findProgramByName("llvm-link"); - CHECK_ERROR(llvm_link.getError(), "Could not find llvm-link-9 or llvm-link."); + auto llvm_link = llvm::sys::findProgramByName("llvm-link-12"); + if (llvm_link.getError()) { + llvm_link = llvm::sys::findProgramByName("llvm-link"); + } + CHECK_ERROR(llvm_link.getError(), "Could not find llvm-link-12 or llvm-link.") // Create a temporary working directory llvm::SmallString<128> workingDir; errorCode = llvm::sys::fs::createUniqueDirectory("gazer_workdir_", workingDir); - CHECK_ERROR(errorCode, "Could not create temporary working directory."); + CHECK_ERROR(errorCode, "Could not create temporary working directory.") std::vector bitcodeFiles; for (llvm::StringRef inputFile : files) { if (inputFile.endswith_lower(".bc") || inputFile.endswith_lower(".ll")) { - bitcodeFiles.push_back(inputFile); + bitcodeFiles.push_back(inputFile.str()); continue; } @@ -210,7 +214,7 @@ auto gazer::ClangCompileAndLink( return nullptr; } - bitcodeFiles.push_back(outputPath.str()); + bitcodeFiles.push_back(outputPath.str().str()); } // Run llvm-link @@ -236,7 +240,7 @@ using namespace gazer; void ClangOptions::addSanitizerFlag(llvm::StringRef flag) { - mSanitizerFlags.insert(flag); + mSanitizerFlags.insert(flag.str()); } void ClangOptions::createArgumentList(std::vector& args) diff --git a/src/LLVM/FrontendConfig.cpp b/src/LLVM/FrontendConfig.cpp index 8ed56d62..a4890473 100644 --- a/src/LLVM/FrontendConfig.cpp +++ b/src/LLVM/FrontendConfig.cpp @@ -56,18 +56,18 @@ auto FrontendConfig::buildFrontend( std::vector> checks; createChecks(checks); - auto module = ClangCompileAndLink(inputs, llvmContext, mClangSettings); - if (module == nullptr) { - llvm::errs() << "Failed to build input module.\n"; + auto llvmModule = ClangCompileAndLink(inputs, llvmContext, mClangSettings); + if (llvmModule == nullptr) { + llvm::errs() << "Failed to build input llvmModule.\n"; return nullptr; } - if (!mSettings.validate(*module, llvm::errs())) { - llvm::errs() << "Settings could not be applied to the input module.\n"; + if (!mSettings.validate(*llvmModule, llvm::errs())) { + llvm::errs() << "Settings could not be applied to the input llvmModule.\n"; return nullptr; } - auto frontend = std::make_unique(std::move(module), context, mSettings); + auto frontend = std::make_unique(std::move(llvmModule), context, mSettings); // The Witness generator has to get the initial name of the sourcefile // ( witnesses support programs with a single source file only ) @@ -110,7 +110,7 @@ void FrontendConfig::createChecks(std::vector>& checks) } for (llvm::StringRef name : fragments) { - auto it = mFactories.find(name); + auto it = mFactories.find(name.str()); if (it == mFactories.end()) { emit_warning("unknown check '%s', parameter ignored", name.data()); continue; diff --git a/src/LLVM/Instrumentation/Check.cpp b/src/LLVM/Instrumentation/Check.cpp index 3f09c06a..f2846158 100644 --- a/src/LLVM/Instrumentation/Check.cpp +++ b/src/LLVM/Instrumentation/Check.cpp @@ -30,7 +30,7 @@ using namespace gazer; using namespace llvm; -bool Check::runOnModule(llvm::Module& module) +bool Check::runOnModule(llvm::Module& llvmModule) { assert(mRegistry != nullptr && "The check registry was not set before check execution was initiated." @@ -38,7 +38,7 @@ bool Check::runOnModule(llvm::Module& module) bool changed = false; - for (llvm::Function& function : module) { + for (llvm::Function& function : llvmModule) { if (function.isDeclaration()) { continue; } @@ -126,7 +126,8 @@ void Check::setCheckRegistry(CheckRegistry& registry) llvm::Value* CheckRegistry::createCheckViolation(Check* check, llvm::DebugLoc loc) { - unsigned ec = mErrorCodeCnt++; + unsigned ec = mErrorCodeCnt; + ++mErrorCodeCnt; mCheckMap.try_emplace(ec, check, loc); return llvm::ConstantInt::get( @@ -149,15 +150,15 @@ llvm::IntegerType* CheckRegistry::GetErrorCodeType(llvm::LLVMContext& context) return llvm::Type::getInt16Ty(context); } -llvm::FunctionCallee CheckRegistry::GetErrorFunction(llvm::Module& module) +llvm::FunctionCallee CheckRegistry::GetErrorFunction(llvm::Module& llvmModule) { llvm::AttrBuilder ab; ab.addAttribute(llvm::Attribute::NoReturn); - return module.getOrInsertFunction( + return llvmModule.getOrInsertFunction( ErrorFunctionName, - GetErrorFunctionType(module.getContext()), - llvm::AttributeList::get(module.getContext(), AttributeList::FunctionIndex, ab) + GetErrorFunctionType(llvmModule.getContext()), + llvm::AttributeList::get(llvmModule.getContext(), AttributeList::FunctionIndex, ab) ); } diff --git a/src/LLVM/Instrumentation/Checks/SignedIntegerOverflowCheck.cpp b/src/LLVM/Instrumentation/Checks/SignedIntegerOverflowCheck.cpp index 079988cb..48e9f27c 100644 --- a/src/LLVM/Instrumentation/Checks/SignedIntegerOverflowCheck.cpp +++ b/src/LLVM/Instrumentation/Checks/SignedIntegerOverflowCheck.cpp @@ -17,6 +17,7 @@ //===----------------------------------------------------------------------===// #include "gazer/LLVM/Instrumentation/DefaultChecks.h" #include "gazer/LLVM/Instrumentation/Intrinsics.h" +#include "gazer/ADT/Iterator.h" #include "gazer/Support/Warnings.h" #include @@ -127,12 +128,10 @@ bool SignedIntegerOverflowCheck::mark(llvm::Function &function) llvm::SmallVector, 16> targets; llvm::SmallVector sanitizerCalls; - for (Instruction& inst : instructions(function)) { - if (auto call = dyn_cast(&inst)) { - GazerIntrinsic::Overflow ovrKind; - if (this->isOverflowIntrinsic(call->getCalledFunction(), &ovrKind)) { - targets.emplace_back(call, ovrKind); - } + for (auto& call : classof_range(llvm::instructions(function))) { + GazerIntrinsic::Overflow ovrKind; + if (this->isOverflowIntrinsic(call.getCalledFunction(), &ovrKind)) { + targets.emplace_back(&call, ovrKind); } } diff --git a/src/LLVM/Instrumentation/Intrinsics.cpp b/src/LLVM/Instrumentation/Intrinsics.cpp index b6d298e2..6bd227e0 100644 --- a/src/LLVM/Instrumentation/Intrinsics.cpp +++ b/src/LLVM/Instrumentation/Intrinsics.cpp @@ -34,13 +34,13 @@ static std::string getOverloadedFunctionName(llvm::StringRef prefix, llvm::Type* return rso.str(); } -llvm::FunctionCallee GazerIntrinsic::GetOrInsertFunctionEntry(llvm::Module& module, llvm::ArrayRef args) +llvm::FunctionCallee GazerIntrinsic::GetOrInsertFunctionEntry(llvm::Module& llvmModule, llvm::ArrayRef args) { std::vector funArgs; - funArgs.push_back(llvm::Type::getMetadataTy(module.getContext())); + funArgs.push_back(llvm::Type::getMetadataTy(llvmModule.getContext())); funArgs.insert(funArgs.end(), args.begin(), args.end()); - auto funTy = llvm::FunctionType::get(llvm::Type::getVoidTy(module.getContext()), funArgs, false); + auto funTy = llvm::FunctionType::get(llvm::Type::getVoidTy(llvmModule.getContext()), funArgs, false); std::string buffer; llvm::raw_string_ostream rso{buffer}; @@ -52,52 +52,52 @@ llvm::FunctionCallee GazerIntrinsic::GetOrInsertFunctionEntry(llvm::Module& modu } rso.flush(); - return module.getOrInsertFunction( + return llvmModule.getOrInsertFunction( rso.str(), funTy ); } -llvm::FunctionCallee GazerIntrinsic::GetOrInsertFunctionReturnVoid(llvm::Module& module) +llvm::FunctionCallee GazerIntrinsic::GetOrInsertFunctionReturnVoid(llvm::Module& llvmModule) { - return module.getOrInsertFunction( + return llvmModule.getOrInsertFunction( FunctionReturnVoidName, - llvm::Type::getVoidTy(module.getContext()), - llvm::Type::getMetadataTy(module.getContext()) + llvm::Type::getVoidTy(llvmModule.getContext()), + llvm::Type::getMetadataTy(llvmModule.getContext()) ); } -llvm::FunctionCallee GazerIntrinsic::GetOrInsertFunctionCallReturned(llvm::Module& module) +llvm::FunctionCallee GazerIntrinsic::GetOrInsertFunctionCallReturned(llvm::Module& llvmModule) { - return module.getOrInsertFunction( + return llvmModule.getOrInsertFunction( FunctionCallReturnedName, - llvm::Type::getVoidTy(module.getContext()), - llvm::Type::getMetadataTy(module.getContext()) + llvm::Type::getVoidTy(llvmModule.getContext()), + llvm::Type::getMetadataTy(llvmModule.getContext()) ); } -llvm::FunctionCallee GazerIntrinsic::GetOrInsertFunctionReturnValue(llvm::Module& module, llvm::Type* type) +llvm::FunctionCallee GazerIntrinsic::GetOrInsertFunctionReturnValue(llvm::Module& llvmModule, llvm::Type* type) { // Insert a new function for this mark type - return module.getOrInsertFunction( + return llvmModule.getOrInsertFunction( getOverloadedFunctionName(FunctionReturnValuePrefix, type), - llvm::Type::getVoidTy(module.getContext()), - llvm::Type::getMetadataTy(module.getContext()), + llvm::Type::getVoidTy(llvmModule.getContext()), + llvm::Type::getMetadataTy(llvmModule.getContext()), type ); } -llvm::FunctionCallee GazerIntrinsic::GetOrInsertInlinedGlobalWrite(llvm::Module& module, llvm::Type* type) +llvm::FunctionCallee GazerIntrinsic::GetOrInsertInlinedGlobalWrite(llvm::Module& llvmModule, llvm::Type* type) { - return module.getOrInsertFunction( + return llvmModule.getOrInsertFunction( getOverloadedFunctionName(InlinedGlobalWritePrefix, type), - llvm::Type::getVoidTy(module.getContext()), + llvm::Type::getVoidTy(llvmModule.getContext()), type, - llvm::Type::getMetadataTy(module.getContext()) + llvm::Type::getMetadataTy(llvmModule.getContext()) ); } -llvm::FunctionCallee GazerIntrinsic::GetOrInsertOverflowCheck(llvm::Module& module, Overflow kind, llvm::Type* type) +llvm::FunctionCallee GazerIntrinsic::GetOrInsertOverflowCheck(llvm::Module& llvmModule, Overflow kind, llvm::Type* type) { std::string name; @@ -116,9 +116,9 @@ llvm::FunctionCallee GazerIntrinsic::GetOrInsertOverflowCheck(llvm::Module& modu type->print(rso, false, true); rso.flush(); - return module.getOrInsertFunction( + return llvmModule.getOrInsertFunction( name, - llvm::Type::getInt1Ty(module.getContext()), + llvm::Type::getInt1Ty(llvmModule.getContext()), type, type ); diff --git a/src/LLVM/Instrumentation/MarkFunctionEntries.cpp b/src/LLVM/Instrumentation/MarkFunctionEntries.cpp index 0d2a183f..4f50e272 100644 --- a/src/LLVM/Instrumentation/MarkFunctionEntries.cpp +++ b/src/LLVM/Instrumentation/MarkFunctionEntries.cpp @@ -17,6 +17,7 @@ //===----------------------------------------------------------------------===// #include "gazer/LLVM/InstrumentationPasses.h" #include "gazer/LLVM/Instrumentation/Intrinsics.h" +#include "gazer/ADT/Iterator.h" #include #include @@ -43,16 +44,16 @@ class MarkFunctionEntriesPass : public ModulePass return "Mark function entries"; } - bool runOnModule(Module& module) override + bool runOnModule(Module& llvmModule) override { - LLVMContext& context = module.getContext(); - llvm::DenseMap returnValueMarks; + LLVMContext& context = llvmModule.getContext(); + llvm::DenseMap returnValueMarks; - auto retMarkVoid = GazerIntrinsic::GetOrInsertFunctionReturnVoid(module); - auto callReturnedMark = GazerIntrinsic::GetOrInsertFunctionCallReturned(module); + auto retMarkVoid = GazerIntrinsic::GetOrInsertFunctionReturnVoid(llvmModule); + auto callReturnedMark = GazerIntrinsic::GetOrInsertFunctionCallReturned(llvmModule); IRBuilder<> builder(context); - for (Function& function : module) { + for (Function& function : llvmModule) { if (function.isDeclaration()) { continue; } @@ -62,7 +63,7 @@ class MarkFunctionEntriesPass : public ModulePass auto dsp = function.getSubprogram(); if (dsp != nullptr) { - auto mark = GazerIntrinsic::GetOrInsertFunctionEntry(module, function.getFunctionType()->params()); + auto mark = GazerIntrinsic::GetOrInsertFunctionEntry(llvmModule, function.getFunctionType()->params()); builder.SetInsertPoint(&entry, entry.getFirstInsertionPt()); std::vector args; @@ -81,26 +82,22 @@ class MarkFunctionEntriesPass : public ModulePass // Also mark call returns to other functions std::vector returns; - for (Instruction& inst : llvm::instructions(function)) { - if (auto ret = dyn_cast(&inst)) { - returns.push_back(ret); - } - } + llvm::copy(llvm::make_pointer_range(classof_range(llvm::instructions(function))), std::back_inserter(returns)); for (ReturnInst* ret : returns) { builder.SetInsertPoint(ret); llvm::Value* retValue = ret->getReturnValue(); if (retValue != nullptr) { auto retValueTy = retValue->getType(); - llvm::Value* retMark = returnValueMarks[retValueTy]; - if (retMark == nullptr) { + llvm::FunctionCallee retMark = returnValueMarks[retValueTy]; + if (!retMark) { std::string nameBuffer; llvm::raw_string_ostream rso(nameBuffer); retValueTy->print(rso, false, true); rso.flush(); // Insert a new function for this mark type - retMark = GazerIntrinsic::GetOrInsertFunctionReturnValue(module, retValueTy).getCallee(); + retMark = GazerIntrinsic::GetOrInsertFunctionReturnValue(llvmModule, retValueTy); returnValueMarks[retValueTy] = retMark; } @@ -119,8 +116,7 @@ class MarkFunctionEntriesPass : public ModulePass std::vector calls; for (Instruction& inst : llvm::instructions(function)) { if (auto call = dyn_cast(&inst)) { - Function* callee = call->getCalledFunction(); - if (callee == nullptr || callee->isDeclaration()) { + if (Function* callee = call->getCalledFunction(); callee == nullptr || callee->isDeclaration()) { // Currently we do not bother with indirect calls, // also we only need functions which have a definition. continue; diff --git a/src/LLVM/LLVMFrontend.cpp b/src/LLVM/LLVMFrontend.cpp index 86abb3aa..0abc22cd 100644 --- a/src/LLVM/LLVMFrontend.cpp +++ b/src/LLVM/LLVMFrontend.cpp @@ -25,7 +25,6 @@ #include "gazer/Trace/TraceWriter.h" #include "gazer/LLVM/Trace/TestHarnessGenerator.h" #include "gazer/Trace/WitnessWriter.h" -#include "gazer/LLVM/Transform/BackwardSlicer.h" #include "gazer/Support/Warnings.h" #include @@ -41,6 +40,7 @@ #include #include #include +#include #include #include @@ -90,7 +90,7 @@ namespace au.setPreservesCFG(); } - bool runOnModule(llvm::Module& module) override; + bool runOnModule(llvm::Module& llvmModule) override; llvm::StringRef getPassName() const override { return "Verification backend pass"; @@ -108,11 +108,11 @@ namespace char RunVerificationBackendPass::ID; LLVMFrontend::LLVMFrontend( - std::unique_ptr module, + std::unique_ptr llvmModule, GazerContext& context, LLVMFrontendSettings& settings) : mContext(context), - mModule(std::move(module)), + mModule(std::move(llvmModule)), mChecks(mModule->getContext()), mSettings(settings) { @@ -149,7 +149,6 @@ void LLVMFrontend::registerVerificationPipeline() mPassManager.add(gazer::createPromoteUndefsPass()); // Perform check instrumentation. - mPassManager.add(gazer::createNormalizeVerifierCallsPass()); registerEnabledChecks(); // Execute early optimization passes. @@ -210,7 +209,7 @@ void LLVMFrontend::registerVerificationStep() } } -bool RunVerificationBackendPass::runOnModule(llvm::Module& module) +bool RunVerificationBackendPass::runOnModule(llvm::Module& llvmModule) { auto& moduleToCfa = getAnalysis(); @@ -259,9 +258,9 @@ bool RunVerificationBackendPass::runOnModule(llvm::Module& module) if (!mSettings.testHarnessFile.empty() && fail->hasTrace()) { llvm::outs() << "Generating test harness.\n"; auto test = GenerateTestHarnessModuleFromTrace( - fail->getTrace(), - module.getContext(), - module + fail->getTrace(), + llvmModule.getContext(), + llvmModule ); llvm::StringRef filename(mSettings.testHarnessFile); diff --git a/src/LLVM/LLVMFrontendSettings.cpp b/src/LLVM/LLVMFrontendSettings.cpp index 78a60523..33ded294 100644 --- a/src/LLVM/LLVMFrontendSettings.cpp +++ b/src/LLVM/LLVMFrontendSettings.cpp @@ -125,9 +125,9 @@ namespace ); } // end anonymous namespace -bool LLVMFrontendSettings::validate(const llvm::Module& module, llvm::raw_ostream& os) const +bool LLVMFrontendSettings::validate(const llvm::Module& llvmModule, llvm::raw_ostream& os) const { - if (module.getFunction(this->function) == nullptr) { + if (llvmModule.getFunction(this->function) == nullptr) { os << "The entry function '" << this->function << "' does not exist!\n"; return false; } @@ -135,9 +135,9 @@ bool LLVMFrontendSettings::validate(const llvm::Module& module, llvm::raw_ostrea return true; } -llvm::Function* LLVMFrontendSettings::getEntryFunction(const llvm::Module& module) const +llvm::Function* LLVMFrontendSettings::getEntryFunction(const llvm::Module& llvmModule) const { - llvm::Function* result = module.getFunction(this->function); + llvm::Function* result = llvmModule.getFunction(this->function); assert(result != nullptr && "The entry function must exist!"); return result; @@ -160,10 +160,10 @@ LLVMFrontendSettings LLVMFrontendSettings::initFromCommandLine() settings.elimVars = ElimVarsLevelOpt; settings.memoryModel = MemoryModelOpt; - settings.checks = EnabledChecks; + settings.checks = EnabledChecks.getValue(); - settings.function = EntryFunctionName; + settings.function = EntryFunctionName.getValue(); if (ArithInts) { settings.ints = IntRepresentation::Integers; @@ -174,9 +174,9 @@ LLVMFrontendSettings LLVMFrontendSettings::initFromCommandLine() settings.debugDumpMemorySSA = DebugDumpMemorySSA; settings.trace = PrintTrace; - settings.witness = GenerateWitness; - settings.hash = Hash; - settings.testHarnessFile = TestHarnessFile; + settings.witness = GenerateWitness.getValue(); + settings.hash = Hash.getValue(); + settings.testHarnessFile = TestHarnessFile.getValue(); return settings; } @@ -190,12 +190,16 @@ std::string LLVMFrontendSettings::toString() const case ElimVarsLevel::Off: str += "off"; break; case ElimVarsLevel::Normal: str += "normal"; break; case ElimVarsLevel::Aggressive: str += "aggressive"; break; + default: + break; } str += R"(", "loop_representation": ")"; switch (loops) { case LoopRepresentation::Recursion: str += "recursion"; break; case LoopRepresentation::Cycle: str += "cycle"; break; + default: + break; } str += R"(", "int_representation": ")"; @@ -203,6 +207,8 @@ std::string LLVMFrontendSettings::toString() const switch (ints) { case IntRepresentation::BitVectors: str += "bv"; break; case IntRepresentation::Integers: str += "int"; break; + default: + break; } str += R"(", "float_representation": ")"; @@ -211,6 +217,8 @@ std::string LLVMFrontendSettings::toString() const case FloatRepresentation::Fpa: str += "fpa"; break; case FloatRepresentation::Real: str += "real"; break; case FloatRepresentation::Undef: str += "undef"; break; + default: + break; } str += "\"}"; diff --git a/src/LLVM/LLVMTraceBuilder.cpp b/src/LLVM/LLVMTraceBuilder.cpp index 269586e7..220568dd 100644 --- a/src/LLVM/LLVMTraceBuilder.cpp +++ b/src/LLVM/LLVMTraceBuilder.cpp @@ -62,6 +62,7 @@ gazer::Type* LLVMTraceBuilder::preferredTypeFromDIType(llvm::DIType* diTy) default: break; } + break; default: break; } @@ -114,7 +115,7 @@ auto LLVMTraceBuilder::build( Valuation currentVals; ValuationExprEvaluator evaluator(currentVals); - auto shouldProcessEntry = [](CfaToLLVMTrace::BlockToLocationInfo info) -> bool { + auto shouldProcessEntry = [](CfaToLLVMTrace::BlockToLocationInfo info) { return info.block != nullptr && info.kind == CfaToLLVMTrace::Location_Entry; }; @@ -229,14 +230,14 @@ auto LLVMTraceBuilder::build( ); std::vector> args; - for (size_t i = 1; i < call->getNumArgOperands(); ++i) { + for (unsigned i = 1; i < call->getNumArgOperands(); ++i) { args.push_back( this->getLiteralFromValue(loc->getAutomaton(), call->getArgOperand(i), currentVals) ); } events.push_back(std::make_unique( - diSP->getName(), + diSP->getName().str(), args )); } else if (callee->getName() == GazerIntrinsic::FunctionReturnVoidName) { @@ -245,7 +246,7 @@ auto LLVMTraceBuilder::build( ); events.push_back(std::make_unique( - diSP->getName(), + diSP->getName().str(), nullptr )); } else if (callee->getName().startswith(GazerIntrinsic::FunctionReturnValuePrefix)) { @@ -255,7 +256,7 @@ auto LLVMTraceBuilder::build( auto expr = this->getLiteralFromValue(loc->getAutomaton(), call->getArgOperand(1), currentVals); events.push_back(std::make_unique( - diSP->getName(), + diSP->getName().str(), expr )); } else if (callee->getName() == GazerIntrinsic::FunctionCallReturnedName) { @@ -264,7 +265,7 @@ auto LLVMTraceBuilder::build( ); events.push_back(std::make_unique( - diSP->getName() + diSP->getName().str() )); } else if (callee->getName().startswith("gazer.undef_value.")) { // Register that we have passed through an undef value. @@ -299,7 +300,7 @@ auto LLVMTraceBuilder::build( } events.push_back(std::make_unique( - callee->getName(), + callee->getName().str(), expr, std::vector>(), location @@ -362,8 +363,7 @@ ExprRef LLVMTraceBuilder::getLiteralFromValue( ValuationExprEvaluator eval(model); - auto expr = mCfaToLlvmTrace.getExpressionForValue(cfa, value); - if (expr != nullptr) { + if (auto expr = mCfaToLlvmTrace.getExpressionForValue(cfa, value); expr != nullptr) { auto ret = eval.evaluate(expr); if (ret != nullptr) { return ret; @@ -396,6 +396,6 @@ TraceVariable LLVMTraceBuilder::traceVarFromDIVar(const llvm::DIVariable* diVar) } } - return TraceVariable(diVar->getName(), rep, diType->getSizeInBits()); + return TraceVariable(diVar->getName().str(), rep, diType->getSizeInBits()); } diff --git a/src/LLVM/Memory/FlatMemoryModel.cpp b/src/LLVM/Memory/FlatMemoryModel.cpp index a28551da..0461c89e 100644 --- a/src/LLVM/Memory/FlatMemoryModel.cpp +++ b/src/LLVM/Memory/FlatMemoryModel.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #define DEBUG_TYPE "FlatMemoryModel" @@ -38,7 +39,7 @@ using namespace gazer; namespace { -llvm::cl::opt FlatMemoryDumpMemSSA("flat-memory-dump-memssa"); +const llvm::cl::opt FlatMemoryDumpMemSSA("flat-memory-dump-memssa"); class FlatMemoryModelInstTranslator; @@ -62,7 +63,7 @@ struct FlatMemoryFunctionInfo // Maps non-lifted globals to their addresses in memory. llvm::DenseMap> globalPointers; - llvm::DenseMap calls; + llvm::DenseMap calls; std::unique_ptr memorySSA; }; @@ -80,12 +81,12 @@ class FlatMemoryModel : public MemoryModel, public MemoryTypeTranslator FlatMemoryModel( GazerContext& context, const LLVMFrontendSettings& settings, - llvm::Module& module, + llvm::Module& llvmModule, DominatorTreeFuncTy dominators ); void insertCallDefsUses( - llvm::CallSite call, FlatMemoryFunctionInfo& info, memory::MemorySSABuilder& builder); + llvm::CallBase* call, FlatMemoryFunctionInfo& info, memory::MemorySSABuilder& builder); MemoryTypeTranslator& getMemoryTypeTranslator() override { return *this; } @@ -112,7 +113,7 @@ class FlatMemoryModel : public MemoryModel, public MemoryTypeTranslator return ArrayType::Get(ptrType(), cellType()); } - ExprRef ptrConstant(unsigned addr) { + ExprRef ptrConstant(unsigned int addr) { return BvLiteralExpr::Get(ptrType(), addr); } @@ -140,11 +141,11 @@ class FlatMemoryModel : public MemoryModel, public MemoryTypeTranslator FlatMemoryModel::FlatMemoryModel( GazerContext& context, const LLVMFrontendSettings& settings, - llvm::Module& module, + llvm::Module& llvmModule, DominatorTreeFuncTy dominators ) : MemoryTypeTranslator(context), mSettings(settings), - mDataLayout(module.getDataLayout()), + mDataLayout(llvmModule.getDataLayout()), mTypes(*this, mSettings) { // Initialize the expression builder @@ -155,7 +156,7 @@ FlatMemoryModel::FlatMemoryModel( llvm::SmallPtrSet liftedGlobals; llvm::SmallPtrSet otherGlobals; - for (llvm::GlobalVariable& gv : module.globals()) { + for (llvm::GlobalVariable& gv : llvmModule.globals()) { // FIXME: We currently do not lift globals which have array or struct types. //if (!memory::isGlobalUsedAsPointer(gv) && gv.getType()->isSingleValueType()) { // liftedGlobals.insert(&gv); @@ -164,12 +165,12 @@ FlatMemoryModel::FlatMemoryModel( //} } - for (llvm::Function& function : module) { + for (llvm::Function& function : llvmModule) { if (function.isDeclaration()) { continue; } - bool isEntryFunction = mSettings.getEntryFunction(module) == &function; + bool isEntryFunction = mSettings.getEntryFunction(llvmModule) == &function; memory::MemorySSABuilder builder(function, mDataLayout, dominators(function)); auto& info = mFunctions[&function]; @@ -248,10 +249,16 @@ FlatMemoryModel::FlatMemoryModel( } } +static bool isIntrinsic(llvm::Function* callee) +{ + llvm::StringRef name = callee->getName(); + return name.startswith("gazer.") || name.startswith("llvm.") || name.startswith("verifier."); +} + void FlatMemoryModel::insertCallDefsUses( - llvm::CallSite call, FlatMemoryFunctionInfo& info, memory::MemorySSABuilder& builder) + llvm::CallBase* call, FlatMemoryFunctionInfo& info, memory::MemorySSABuilder& builder) { - llvm::Function* callee = call.getCalledFunction(); + llvm::Function* callee = call->getCalledFunction(); if (callee == nullptr) { builder.createCallDef(info.memory, call); @@ -259,9 +266,7 @@ void FlatMemoryModel::insertCallDefsUses( return; } - llvm::StringRef name = callee->getName(); - - if (name.startswith("gazer.") || name.startswith("llvm.") || name.startswith("verifier.")) { + if (isIntrinsic(callee)) { // TODO: This may need to change in the case of some intrinsics (e.g. llvm.memcpy) return; } @@ -271,8 +276,7 @@ void FlatMemoryModel::insertCallDefsUses( } if (callee->isDeclaration()) { - // TODO: We could handle some know function here or clobber the memory according to - // some configuration option. + // TODO: We could handle some know function here or clobber the memory according to some configuration option. return; } @@ -335,7 +339,7 @@ class FlatMemoryModelInstTranslator : public MemorySSABasedInstructionHandler llvm2cfa::GenerationStepExtensionPoint& ep) override; void handleCall( - llvm::CallSite call, + const llvm::CallBase* call, llvm2cfa::GenerationStepExtensionPoint& parentEp, llvm2cfa::AutomatonInterfaceExtensionPoint& calleeEp, llvm::SmallVectorImpl& inputAssignments, @@ -387,13 +391,13 @@ auto FlatMemoryModelInstTranslator::handleAlloca(const llvm::AllocaInst& alloc, assert(memDef != nullptr && "There must be exactly one Memory definition for an alloca!"); assert(spDef != nullptr && "There must be exactly one StackPtr definition for an alloca!"); - unsigned size = mDataLayout.getTypeAllocSize(alloc.getAllocatedType()); + unsigned long size = mDataLayout.getTypeAllocSize(alloc.getAllocatedType()); // This alloca returns the pointer to the current stack frame, // which is then advanced by the size of the allocated type. // We also clobber the relevant bytes of the memory array. ExprPtr ptr = ep.getAsOperand(spDef->getReachingDef()); - Variable* defVar = ep.getVariableFor(&*spDef); + Variable* defVar = ep.getVariableFor(spDef); assert(defVar != nullptr && "The definition variable should have been inserted earlier!"); @@ -410,8 +414,8 @@ auto FlatMemoryModelInstTranslator::handleAlloca(const llvm::AllocaInst& alloc, ); } - Variable* memVar = ep.getVariableFor(&*memDef); - if (!ep.tryToEliminate(&*memDef, memVar, resArray)) { + Variable* memVar = ep.getVariableFor(memDef); + if (!ep.tryToEliminate(memDef, memVar, resArray)) { ep.insertAssignment(memVar, resArray); } @@ -465,7 +469,7 @@ ExprPtr FlatMemoryModelInstTranslator::handleGetElementPtr( for (unsigned i = 1; i < ops.size(); ++i) { // Calculate the size of the current step - unsigned siz = mDataLayout.getTypeAllocSize(ti.getIndexedType()); + unsigned long size = mDataLayout.getTypeAllocSize(ti.getIndexedType()); // Index arguments may be integer types different from the pointer type. // Extend/truncate them into the proper pointer length. @@ -486,7 +490,7 @@ ExprPtr FlatMemoryModelInstTranslator::handleGetElementPtr( } addr = mExprBuilder.Add( - addr, mExprBuilder.Mul(index, mMemoryModel.ptrConstant(siz)) + addr, mExprBuilder.Mul(index, mMemoryModel.ptrConstant(size)) ); ++ti; @@ -503,7 +507,7 @@ ExprPtr FlatMemoryModelInstTranslator::handleConstantDataArray( ArrayLiteralExpr::Builder builder(ArrayType::Get(mMemoryModel.ptrType(), bv8ty()), BvLiteralExpr::Get(bv8ty(), 0)); llvm::Type* elemTy = cda->getType()->getArrayElementType(); - unsigned size = mDataLayout.getTypeAllocSize(elemTy); + unsigned long size = mDataLayout.getTypeAllocSize(elemTy); unsigned currentOffset = 0; for (unsigned i = 0; i < cda->getNumElements(); ++i) { @@ -598,7 +602,7 @@ void FlatMemoryModelInstTranslator::handleStore( MemoryObjectDef* memoryDef = mMemorySSA.getUniqueDefinitionFor(&store, mInfo.memory); assert(memoryDef != nullptr && "There must be exactly one definition for Memory on a store!"); - unsigned size = mDataLayout.getTypeAllocSize(store.getValueOperand()->getType()); + auto size = mDataLayout.getTypeAllocSize(store.getValueOperand()->getType()); ExprPtr array = ep.getAsOperand(memoryDef->getReachingDef()); ExprPtr value = ep.getAsOperand(store.getValueOperand()); @@ -631,15 +635,15 @@ ExprPtr FlatMemoryModelInstTranslator::handleLoad( } void FlatMemoryModelInstTranslator::handleCall( - llvm::CallSite call, + const llvm::CallBase* call, llvm2cfa::GenerationStepExtensionPoint& parentEp, llvm2cfa::AutomatonInterfaceExtensionPoint& calleeEp, llvm::SmallVectorImpl& inputAssignments, llvm::SmallVectorImpl& outputAssignments) { - LLVM_DEBUG(llvm::dbgs() << "Handling call instruction " << *call.getInstruction() << "\n"); + LLVM_DEBUG(llvm::dbgs() << "Handling call instruction " << *call << "\n"); - const llvm::Function* callee = call.getCalledFunction(); + const llvm::Function* callee = call->getCalledFunction(); assert(callee != nullptr); auto& calleeInfo = mMemoryModel.getInfoFor(callee); @@ -845,9 +849,9 @@ auto FlatMemoryModel::getMemoryInstructionHandler(llvm::Function& function) auto gazer::CreateFlatMemoryModel( GazerContext& context, const LLVMFrontendSettings& settings, - llvm::Module& module, + llvm::Module& llvmModule, std::function dominators ) -> std::unique_ptr { - return std::make_unique(context, settings, module, dominators); + return std::make_unique(context, settings, llvmModule, dominators); } \ No newline at end of file diff --git a/src/LLVM/Memory/HavocMemoryModel.cpp b/src/LLVM/Memory/HavocMemoryModel.cpp index f38e9ec8..101f95f5 100644 --- a/src/LLVM/Memory/HavocMemoryModel.cpp +++ b/src/LLVM/Memory/HavocMemoryModel.cpp @@ -93,7 +93,7 @@ class HavocMemoryModel : } void handleCall( - llvm::CallSite call, + const llvm::CallBase* call, llvm2cfa::GenerationStepExtensionPoint& callerEp, llvm2cfa::AutomatonInterfaceExtensionPoint& calleeEp, llvm::SmallVectorImpl& inputAssignments, @@ -106,7 +106,6 @@ class HavocMemoryModel : private: gazer::Type& ptrType() const { return BoolType::Get(mContext); } ExprPtr ptrValue() const { return UndefExpr::Get(this->ptrType()); } - }; } // namespace diff --git a/src/LLVM/Memory/MemoryInstructionHandler.cpp b/src/LLVM/Memory/MemoryInstructionHandler.cpp index da6ec955..f3dd8209 100644 --- a/src/LLVM/Memory/MemoryInstructionHandler.cpp +++ b/src/LLVM/Memory/MemoryInstructionHandler.cpp @@ -59,12 +59,11 @@ void MemorySSABasedInstructionHandler::declareFunctionVariables( } } + // See if the function has a return use llvm::TinyPtrVector retUses; - for (auto& u : object.uses()) { - if (auto retUse = llvm::dyn_cast(&u)) { - retUses.push_back(retUse); - } - } + llvm::for_each(classof_range(object.uses()), [&retUses](auto& u) { + retUses.push_back(&u); + }); assert(retUses.empty() || retUses.size() == 1); if (!retUses.empty()) { diff --git a/src/LLVM/Memory/MemoryModel.cpp b/src/LLVM/Memory/MemoryModel.cpp index bd0c2f2d..1ddf2209 100644 --- a/src/LLVM/Memory/MemoryModel.cpp +++ b/src/LLVM/Memory/MemoryModel.cpp @@ -43,7 +43,7 @@ void MemoryModelWrapperPass::getAnalysisUsage(llvm::AnalysisUsage& au) const au.setPreservesAll(); } -bool MemoryModelWrapperPass::runOnModule(llvm::Module& module) +bool MemoryModelWrapperPass::runOnModule(llvm::Module& llvmModule) { switch (mSettings.memoryModel) { case MemoryModelSetting::Flat: { @@ -51,7 +51,7 @@ bool MemoryModelWrapperPass::runOnModule(llvm::Module& module) return getAnalysis(function).getDomTree(); }; - mMemoryModel = CreateFlatMemoryModel(mContext, mSettings, module, dominators); + mMemoryModel = CreateFlatMemoryModel(mContext, mSettings, llvmModule, dominators); break; } case MemoryModelSetting::Havoc: { diff --git a/src/LLVM/Memory/MemoryObject.cpp b/src/LLVM/Memory/MemoryObject.cpp index 147ad5a9..69758dc7 100644 --- a/src/LLVM/Memory/MemoryObject.cpp +++ b/src/LLVM/Memory/MemoryObject.cpp @@ -60,14 +60,14 @@ void MemoryObject::print(llvm::raw_ostream& os) const os << ")"; } -void MemoryObject::addDefinition(MemoryObjectDef* def) +MemoryObjectDef* MemoryObject::addDefinition(std::unique_ptr&& def) { - mDefs.emplace_back(std::unique_ptr(def)); + return mDefs.emplace_back(std::move(def)).get(); } -void MemoryObject::addUse(MemoryObjectUse* use) +MemoryObjectUse* MemoryObject::addUse(std::unique_ptr&& use) { - mUses.emplace_back(std::unique_ptr(use)); + return mUses.emplace_back(std::move(use)).get(); } void MemoryObject::setEntryDef(MemoryObjectDef* def) diff --git a/src/LLVM/Memory/MemorySSA.cpp b/src/LLVM/Memory/MemorySSA.cpp index 5e82eb76..a20e3352 100644 --- a/src/LLVM/Memory/MemorySSA.cpp +++ b/src/LLVM/Memory/MemorySSA.cpp @@ -95,15 +95,15 @@ void MemorySSABuilderPass::getAnalysisUsage(llvm::AnalysisUsage& au) const this->addRequiredAnalyses(au); } -auto MemorySSABuilderPass::runOnModule(llvm::Module& module) -> bool +auto MemorySSABuilderPass::runOnModule(llvm::Module& llvmModule) -> bool { - for (llvm::Function& function : module) { + for (llvm::Function& function : llvmModule) { if (function.isDeclaration()) { continue; } auto& dt = getAnalysis(function).getDomTree(); - MemorySSABuilder builder(function, module.getDataLayout(), dt); + MemorySSABuilder builder(function, llvmModule.getDataLayout(), dt); this->initializeFunction(function, builder); @@ -136,13 +136,12 @@ memory::LiveOnEntryDef* MemorySSABuilder::createLiveOnEntryDef(gazer::MemoryObje assert(!object->hasEntryDef() && "Attempting to insert two entry definitions for a single object!"); llvm::BasicBlock* entryBlock = &mFunction.getEntryBlock(); - auto def = new memory::LiveOnEntryDef(object, mVersionNumber++, entryBlock); + auto def = object->createDef(object, mVersionNumber++, entryBlock); mObjectInfo[object].defBlocks.insert(entryBlock); mValueDefs[&mFunction.getEntryBlock()].emplace_back(def); - object->addDefinition(def); object->setEntryDef(def); return def; @@ -152,32 +151,28 @@ memory::GlobalInitializerDef* MemorySSABuilder::createGlobalInitializerDef( gazer::MemoryObject* object, llvm::GlobalVariable* gv) { llvm::BasicBlock* entryBlock = &mFunction.getEntryBlock(); - auto def = new memory::GlobalInitializerDef(object, mVersionNumber++, entryBlock, gv); + auto def = object->createDef(object, mVersionNumber++, entryBlock, gv); mObjectInfo[object].defBlocks.insert(entryBlock); mValueDefs[&mFunction.getEntryBlock()].emplace_back(def); - - object->addDefinition(def); return def; } memory::StoreDef* MemorySSABuilder::createStoreDef(MemoryObject* object, llvm::StoreInst& inst) { - auto def = new memory::StoreDef(object, mVersionNumber++, inst); + auto def = object->createDef(object, mVersionNumber++, inst); mObjectInfo[object].defBlocks.insert(inst.getParent()); mValueDefs[&inst].push_back(def); - object->addDefinition(def); return def; } -memory::CallDef* MemorySSABuilder::createCallDef(gazer::MemoryObject* object, llvm::CallSite call) +memory::CallDef* MemorySSABuilder::createCallDef(gazer::MemoryObject* object, llvm::CallBase* call) { - auto def = new memory::CallDef(object, mVersionNumber++, call); - mObjectInfo[object].defBlocks.insert(call.getInstruction()->getParent()); - mValueDefs[call.getInstruction()].push_back(def); - object->addDefinition(def); + auto def = object->createDef(object, mVersionNumber++, call); + mObjectInfo[object].defBlocks.insert(call->getParent()); + mValueDefs[call].push_back(def); return def; } @@ -186,28 +181,25 @@ memory::AllocaDef* MemorySSABuilder::createAllocaDef( MemoryObject* object, llvm::AllocaInst& alloca ) { - auto def = new memory::AllocaDef(object, mVersionNumber++, alloca); + auto def = object->createDef(object, mVersionNumber++, alloca); mObjectInfo[object].defBlocks.insert(alloca.getParent()); mValueDefs[&alloca].push_back(def); - object->addDefinition(def); return def; } memory::LoadUse* MemorySSABuilder::createLoadUse(MemoryObject* object, llvm::LoadInst& load) { - auto use = new memory::LoadUse(object, load); + auto use = object->createUse(object, load); mValueUses[&load].push_back(use); - object->addUse(use); return use; } -memory::CallUse* MemorySSABuilder::createCallUse(MemoryObject* object, llvm::CallSite call) +memory::CallUse* MemorySSABuilder::createCallUse(MemoryObject* object, llvm::CallBase* call) { - auto use = new memory::CallUse(object, call); - mValueUses[call.getInstruction()].push_back(use); - object->addUse(use); + auto use = object->createUse(object, call); + mValueUses[call].push_back(use); return use; } @@ -215,9 +207,8 @@ memory::CallUse* MemorySSABuilder::createCallUse(MemoryObject* object, llvm::Cal memory::RetUse* MemorySSABuilder::createReturnUse(MemoryObject* object, llvm::ReturnInst& ret) { assert(!object->hasExitUse() && "Attempting to add a duplicate exit use!"); - auto use = new memory::RetUse(object, ret); + auto use = object->createUse(object, ret); mValueUses[&ret].push_back(use); - object->addUse(use); object->setExitUse(use); return use; @@ -243,8 +234,7 @@ void MemorySSABuilder::calculatePHINodes() idf.calculate(phiBlocks); for (llvm::BasicBlock* bb : phiBlocks) { - auto phi = new memory::PhiDef(&*object, mVersionNumber++, bb); - object->addDefinition(phi); + auto phi = object->createDef(&*object, mVersionNumber++, bb); mValueDefs[bb].push_back(phi); } } @@ -305,7 +295,7 @@ void MemorySSABuilder::renameBlock(llvm::BasicBlock* block) } // Handle successors - for (llvm::DomTreeNode* child : mDominatorTree.getNode(block)->getChildren()) { + for (llvm::DomTreeNode* child : mDominatorTree.getNode(block)->children()) { renameBlock(child->getBlock()); } diff --git a/src/LLVM/Trace/TestHarnessGenerator.cpp b/src/LLVM/Trace/TestHarnessGenerator.cpp index 4b2a53ba..f016f24d 100644 --- a/src/LLVM/Trace/TestHarnessGenerator.cpp +++ b/src/LLVM/Trace/TestHarnessGenerator.cpp @@ -42,7 +42,7 @@ static llvm::Constant* exprToLLVMValue(ExprRef& expr, LLVMContext& c return llvm::ConstantInt::get(context, bvLit->getValue()); } else if (auto intLit = llvm::dyn_cast(expr)) { return llvm::ConstantInt::get(context, llvm::APInt{targetTy->getIntegerBitWidth(), static_cast(intLit->getValue())}); - } else if (auto boolLit =llvm::dyn_cast(expr)) { + } else if (auto boolLit = llvm::dyn_cast(expr)) { if (boolLit->getValue()) { return llvm::ConstantInt::getTrue(context); } @@ -56,16 +56,16 @@ static llvm::Constant* exprToLLVMValue(ExprRef& expr, LLVMContext& c } std::unique_ptr gazer::GenerateTestHarnessModuleFromTrace( - Trace& trace, LLVMContext& context, const llvm::Module& module + const Trace& trace, LLVMContext& context, const llvm::Module& llvmModule ) { - const DataLayout& dl = module.getDataLayout(); + const DataLayout& dl = llvmModule.getDataLayout(); std::unordered_map>> calls; for (auto& event : trace) { if (event->getKind() == TraceEvent::Event_FunctionCall) { auto callEvent = llvm::cast(event.get()); - llvm::Function* callee = module.getFunction(callEvent->getFunctionName()); + llvm::Function* callee = llvmModule.getFunction(callEvent->getFunctionName()); assert(callee != nullptr && "The function declaration should be present in the module"); auto expr = callEvent->getReturnValue(); @@ -73,7 +73,7 @@ std::unique_ptr gazer::GenerateTestHarnessModuleFromTrace( } } - std::unique_ptr test = std::make_unique("test", context); + auto test = std::make_unique("test", context); test->setDataLayout(dl); for (auto& [function, vec] : calls) { diff --git a/src/LLVM/Transform/BackwardSlicer.cpp b/src/LLVM/Transform/BackwardSlicer.cpp deleted file mode 100644 index 5175bb8e..00000000 --- a/src/LLVM/Transform/BackwardSlicer.cpp +++ /dev/null @@ -1,196 +0,0 @@ -//==-------------------------------------------------------------*- C++ -*--==// -// -// Copyright 2019 Contributors to the Gazer project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// - -#include "gazer/LLVM/Transform/BackwardSlicer.h" - -#include -#include - -using namespace gazer; -using namespace llvm; - -BackwardSlicer::BackwardSlicer( - llvm::Function& function, - std::function criterion -) : mFunction(function), mCriterion(criterion) -{ - llvm::PostDominatorTree pdt(function); - mPDG = ProgramDependenceGraph::Create(function, pdt); -} - -bool BackwardSlicer::collectRequiredNodes(llvm::DenseSet& visited) -{ - llvm::SmallVector wl; - - // Insert the original slicing criteria - for (llvm::Instruction& inst : llvm::instructions(mFunction)) { - if (mCriterion(&inst)) { - wl.push_back(&inst); - } - } - - if (wl.empty()) { - // None of the nodes matched the slicing criterion, bail out. - return false; - } - - // Walk the PDG, collecting needed nodes - while (!wl.empty()) { - Instruction* current = wl.pop_back_val(); - visited.insert(current); - - //llvm::errs() << "Visiting " << *current << "\n"; - auto pdgNode = mPDG->getNode(current); - for (PDGEdge* edge : pdgNode->incoming()) { - llvm::Instruction* inst = edge->getSource()->getInstruction(); - //llvm::errs() << " Child " << *inst << "\n"; - if (visited.count(inst) == 0) { - //llvm::errs() << " not visited yet\n"; - wl.push_back(inst); - } - } - } - - return true; -} - -void BackwardSlicer::sliceBlocks( - const llvm::DenseSet& required, - llvm::SmallVectorImpl& preservedBlocks -) { - auto& list = mFunction.getBasicBlockList(); - auto it = list.begin(), ie = list.end(); - - while (it != ie) { - llvm::BasicBlock& bb = *(it++); - bool shouldRemove = std::all_of(bb.begin(), bb.end(), [&required](llvm::Instruction& inst) { - return required.count(&inst) == 0; - }); - - if (!shouldRemove) { - preservedBlocks.push_back(&bb); - continue; - } - - // Remove all instructions in this block - for (auto j = bb.begin(); j != bb.end();) { - llvm::Instruction& inst = *(j++); - - inst.replaceAllUsesWith(UndefValue::get(inst.getType())); - inst.dropAllReferences(); - inst.eraseFromParent(); - } - - // Update successors PHI's - for (llvm::BasicBlock* succ : llvm::successors(&bb)) { - succ->removePredecessor(&bb); - } - - // Insert a new 'unreachable' instruction as the terminator - auto unreachable = new UnreachableInst(mFunction.getContext()); - bb.getInstList().push_back(unreachable); - } -} - -void BackwardSlicer::sliceInstructions( - llvm::BasicBlock& bb, - const llvm::DenseSet& required -) { - auto& list = bb.getInstList(); - auto it = list.begin(), ie = list.end(); - - while (it != ie) { - llvm::Instruction& current = *(it++); - - if (required.count(¤t) != 0) { - continue; - } - - // Do not remove terminator instructions, as they are needed for well-formedness. - if (¤t == bb.getTerminator()) { - continue; - } - - // Do not remove intrinsics and debug-related stuff. - // TODO: We should be more refined here, instead of keeping all calls. - if (auto call = llvm::dyn_cast(¤t)) { - continue; - } - - current.replaceAllUsesWith(UndefValue::get(current.getType())); - current.dropAllReferences(); - current.eraseFromParent(); - } -} - -bool BackwardSlicer::slice() -{ - llvm::DenseSet required; - bool shouldContinue = this->collectRequiredNodes(required); - - //mFunction.dump(); - //mPDG->view(); - if (!shouldContinue) { - return false; - } - - // Slice blocks which should completely be removed. - llvm::SmallVector preservedBlocks; - this->sliceBlocks(required, preservedBlocks); - - // Slice the remaining blocks. - for (llvm::BasicBlock* bb : preservedBlocks) { - this->sliceInstructions(*bb, required); - } - - //mFunction.dump(); - - return true; -} - -// LLVM pass implementation -//===----------------------------------------------------------------------===// - -namespace -{ - -class BackwardSlicerPass : public llvm::FunctionPass -{ -public: - char ID; - - BackwardSlicerPass(std::function criteria) - : FunctionPass(ID), mCriteria(criteria) - {} - - bool runOnFunction(llvm::Function& function) override - { - BackwardSlicer slicer(function, mCriteria); - return slicer.slice(); - } - -private: - std::function mCriteria; -}; - -} // end anonymous namespace - -llvm::Pass* gazer::createBackwardSlicerPass(std::function criteria) -{ - return new BackwardSlicerPass(criteria); -} \ No newline at end of file diff --git a/src/LLVM/Transform/Inline.cpp b/src/LLVM/Transform/Inline.cpp index 5dd02555..73838b39 100644 --- a/src/LLVM/Transform/Inline.cpp +++ b/src/LLVM/Transform/Inline.cpp @@ -43,7 +43,6 @@ class InlinePass : public llvm::ModulePass public: static char ID; -public: InlinePass(llvm::Function* entry, InlineLevel level) : ModulePass(ID), mEntryFunction(entry), mLevel(level) { @@ -55,14 +54,14 @@ class InlinePass : public llvm::ModulePass au.addRequired(); } - bool runOnModule(llvm::Module& module) override; + bool runOnModule(llvm::Module& llvmModule) override; llvm::StringRef getPassName() const override { return "Simplified inling"; } private: - bool shouldInlineFunction(llvm::CallGraphNode* target, unsigned allowedRefs); + bool shouldInlineFunction(llvm::CallGraphNode* target, unsigned allowedRefs) const; llvm::Function* mEntryFunction; InlineLevel mLevel; @@ -72,9 +71,9 @@ class InlinePass : public llvm::ModulePass char InlinePass::ID; -bool InlinePass::shouldInlineFunction(llvm::CallGraphNode* target, unsigned allowedRefs) +bool InlinePass::shouldInlineFunction(llvm::CallGraphNode* target, unsigned allowedRefs) const { - bool viable = llvm::isInlineViable(*target->getFunction()); + bool viable = llvm::isInlineViable(*target->getFunction()).isSuccess(); viable |= !isRecursive(target); if (!viable) { @@ -102,7 +101,7 @@ bool InlinePass::shouldInlineFunction(llvm::CallGraphNode* target, unsigned allo return false; } -bool InlinePass::runOnModule(llvm::Module& module) +bool InlinePass::runOnModule(llvm::Module& llvmModule) { if (mLevel == InlineLevel::Off) { return false; @@ -112,25 +111,25 @@ bool InlinePass::runOnModule(llvm::Module& module) llvm::CallGraph& cg = getAnalysis().getCallGraph(); llvm::InlineFunctionInfo ifi(&cg); - llvm::SmallVector wl; + llvm::SmallVector wl; llvm::CallGraphNode* entryCG = cg[mEntryFunction]; for (auto& [call, target] : *entryCG) { if (this->shouldInlineFunction(target, 1)) { LLVM_DEBUG(llvm::dbgs() << "Decided to inline call " << *call << " to target " << target->getFunction()->getName() << "\n"); - wl.emplace_back(call); + wl.emplace_back(llvm::cast(*call)); } } while (!wl.empty()) { - llvm::CallSite cs = wl.pop_back_val(); - bool success = llvm::InlineFunction(cs, ifi); + llvm::CallBase* cs = wl.pop_back_val(); + bool success = llvm::InlineFunction(*cs, ifi).isSuccess(); changed |= success; for (llvm::Value* newCall : ifi.InlinedCalls) { - llvm::CallSite newCS(newCall); - auto callee = newCS.getCalledFunction(); + llvm::CallBase* newCS = llvm::cast(newCall); + auto callee = newCS->getCalledFunction(); if (callee == nullptr) { continue; } diff --git a/src/LLVM/Transform/InlineGlobalVariablesPass.cpp b/src/LLVM/Transform/InlineGlobalVariablesPass.cpp index 95e639e9..6ab27269 100644 --- a/src/LLVM/Transform/InlineGlobalVariablesPass.cpp +++ b/src/LLVM/Transform/InlineGlobalVariablesPass.cpp @@ -20,6 +20,7 @@ #include "gazer/LLVM/Transform/Passes.h" #include "gazer/LLVM/Instrumentation/Intrinsics.h" +#include "gazer/ADT/Iterator.h" #include #include @@ -49,7 +50,7 @@ struct InlineGlobalVariablesPass final : public ModulePass au.addRequired(); } - bool runOnModule(Module& module) override; + bool runOnModule(Module& llvmModule) override; static llvm::Function* shouldInlineGlobal(llvm::CallGraph& cg, llvm::GlobalVariable& gv); }; @@ -105,10 +106,8 @@ void transformConstantUsersToInstructions(llvm::Constant& constant) { // Based on the similar utility found in SeaHorn llvm::SmallVector ceUsers; - for (llvm::User* user : constant.users()) { - if (auto ce = llvm::dyn_cast(user)) { - ceUsers.emplace_back(ce); - } + for (auto* ce : classof_range(constant.users())) { + ceUsers.emplace_back(ce); } for (llvm::ConstantExpr* user : ceUsers) { @@ -127,9 +126,23 @@ void transformConstantUsersToInstructions(llvm::Constant& constant) } } -bool InlineGlobalVariablesPass::runOnModule(Module& module) +llvm::DIGlobalVariableExpression* findDIGlobalVariableExpression(const llvm::GlobalVariable& gv) { - if (module.global_begin() == module.global_end()) { + llvm::SmallVector, 2> md; + gv.getAllMetadata(md); + + for (auto& [_, node] : md) { + if (auto* gve = llvm::dyn_cast(node)) { + return gve; + } + } + + return nullptr; +} + +bool InlineGlobalVariablesPass::runOnModule(Module& llvmModule) +{ + if (llvmModule.global_begin() == llvmModule.global_end()) { // No globals to inline return false; } @@ -137,14 +150,14 @@ bool InlineGlobalVariablesPass::runOnModule(Module& module) llvm::CallGraph& cg = getAnalysis().getCallGraph(); // Create a dbg declaration if it does not exist yet. - Intrinsic::getDeclaration(&module, Intrinsic::dbg_declare); + Intrinsic::getDeclaration(&llvmModule, Intrinsic::dbg_declare); - IRBuilder<> builder(module.getContext()); + IRBuilder<> builder(llvmModule.getContext()); - DIBuilder diBuilder(module); + DIBuilder diBuilder(llvmModule); - auto gvIt = module.global_begin(); - while (gvIt != module.global_end()) { + auto gvIt = llvmModule.global_begin(); + while (gvIt != llvmModule.global_end()) { GlobalVariable& gv = *(gvIt++); auto type = gv.getType()->getElementType(); @@ -161,41 +174,29 @@ bool InlineGlobalVariablesPass::runOnModule(Module& module) AllocaInst* alloc = builder.CreateAlloca(type, nullptr, gv.getName()); Constant* init = gv.getInitializer(); - builder.CreateAlignedStore(init, alloc, module.getDataLayout().getABITypeAlignment(type)); + builder.CreateAlignedStore(init, alloc, llvmModule.getDataLayout().getABITypeAlignment(type)); // TODO: We should check external calls and clobber the alloca with a nondetermistic // store if the ExternFuncGlobalBehavior setting requires this. - // Add some metadata stuff - auto mark = GazerIntrinsic::GetOrInsertInlinedGlobalWrite(module, gv.getType()->getPointerElementType()); - cg.getOrInsertFunction(llvm::cast(mark.getCallee())); + // Add `inlined_global.write` traceability if we have the original inlined variable + if (auto* diGlobalExpr = findDIGlobalVariableExpression(gv)) { + DIGlobalVariable* diGlobalVariable = diGlobalExpr->getVariable(); - // FIXME: There should be a more intelligent way for finding the DIGlobalVariable - llvm::SmallVector, 2> md; - gv.getAllMetadata(md); + auto mark = GazerIntrinsic::GetOrInsertInlinedGlobalWrite(llvmModule, gv.getType()->getPointerElementType()); + cg.getOrInsertFunction(llvm::cast(mark.getCallee())); - llvm::DIGlobalVariableExpression* diGlobalExpr = nullptr; - std::for_each(md.begin(), md.end(), [&diGlobalExpr](auto pair) { - if (auto ge = dyn_cast(pair.second)) { - diGlobalExpr = ge; - } - }); - - if (diGlobalExpr != nullptr) { - auto diGlobalVariable = diGlobalExpr->getVariable(); - for (llvm::Value* user : gv.users()) { - if (auto inst = llvm::dyn_cast(user)) { - llvm::Value* value = inst->getOperand(0); - - CallInst* call = CallInst::Create( - mark.getFunctionType(), mark.getCallee(), { - value, MetadataAsValue::get(module.getContext(), diGlobalVariable) - } - ); - - call->setDebugLoc(inst->getDebugLoc()); - call->insertAfter(inst); - } + for (auto* store : classof_range(gv.users())) { + llvm::Value* value = store->getOperand(0); + + CallInst* call = CallInst::Create( + mark.getFunctionType(), mark.getCallee(), { + value, MetadataAsValue::get(llvmModule.getContext(), diGlobalVariable) + } + ); + + call->setDebugLoc(store->getDebugLoc()); + call->insertAfter(store); } } diff --git a/src/LLVM/Transform/LiftErrorsPass.cpp b/src/LLVM/Transform/LiftErrorsPass.cpp index d19e08ce..74727e2c 100644 --- a/src/LLVM/Transform/LiftErrorsPass.cpp +++ b/src/LLVM/Transform/LiftErrorsPass.cpp @@ -16,13 +16,17 @@ // //===----------------------------------------------------------------------===// /// -/// \file This file defines the LiftErrorsPass, which lifts error calls -/// from loops and subroutines into the main module. +/// \file This file defines the LiftErrorsPass, which lifts error calls from +/// loops and subroutines into the main module. The algorithm is based on the +/// transformation presented in the paper: +/// Akash Lal and Shaz Qadeer. +/// A program transformation for faster goal-directed search. +/// DOI: https://doi.org/10.1109/FMCAD.2014.6987607 /// //===----------------------------------------------------------------------===// #include "gazer/LLVM/Transform/Passes.h" #include "gazer/LLVM/Instrumentation/Check.h" -#include "gazer/LLVM/Transform/BackwardSlicer.h" +#include "gazer/ADT/Iterator.h" #include #include @@ -46,29 +50,31 @@ class LiftErrorCalls struct FunctionInfo { std::vector selfFails; - std::vector mayFailCalls; + std::vector mayFailCalls; llvm::PHINode* uniqueErrorPhi = nullptr; llvm::CallInst* uniqueErrorCall = nullptr; - llvm::Function* alwaysFailClone = nullptr; llvm::BasicBlock* failCopyEntry = nullptr; std::vector failCopyArgumentPHIs; - - llvm::PHINode* failCloneErrorPhi = nullptr; bool canFail() const - { return !selfFails.empty() || !mayFailCalls.empty(); } + { + return !selfFails.empty() || !mayFailCalls.empty(); + } }; -public: - LiftErrorCalls(llvm::Module& module, llvm::CallGraph& cg, llvm::Function& entryFunction) - : mModule(module), mCallGraph(cg), mEntryFunction(&entryFunction), mBuilder(module.getContext()) + LiftErrorCalls(llvm::Module& llvmModule, llvm::CallGraph& cg, llvm::Function& entryFunction) + : mModule(llvmModule), mCallGraph(cg), mEntryFunction(&entryFunction), mBuilder(llvmModule.getContext()) {} bool run(); private: void combineErrorsInFunction(llvm::Function* function, FunctionInfo& info); + void collectFailingCalls(llvm::CallGraphNode* cgNode); + void replaceReturnsWithUnreachable(const llvm::SmallVectorImpl& returns); + void representArgumentsAsPhiNodes(llvm::Function* function, llvm::BasicBlock* failEntry, FunctionInfo& info, ValueToValueMapTy& vmap); + void createBranchToFail(llvm::CallBase* call); llvm::FunctionCallee getDummyBoolFunc() { @@ -85,12 +91,6 @@ class LiftErrorCalls ); } - llvm::Value* getErrorFunction() - { return CheckRegistry::GetErrorFunction(mModule).getCallee(); } - - llvm::Type* getErrorCodeType() - { return CheckRegistry::GetErrorCodeType(mModule.getContext()); } - private: llvm::Module& mModule; llvm::CallGraph& mCallGraph; @@ -104,8 +104,7 @@ class LiftErrorCallsPass : public llvm::ModulePass public: static char ID; -public: - LiftErrorCallsPass(llvm::Function* function) + explicit LiftErrorCallsPass(llvm::Function* function) : ModulePass(ID), mEntryFunction(function) {} @@ -114,16 +113,18 @@ class LiftErrorCallsPass : public llvm::ModulePass au.addRequired(); } - bool runOnModule(Module& module) override + bool runOnModule(Module& llvmModule) override { auto& cg = getAnalysis(); - LiftErrorCalls impl(module, cg.getCallGraph(), *mEntryFunction); + LiftErrorCalls impl(llvmModule, cg.getCallGraph(), *mEntryFunction); return impl.run(); } llvm::StringRef getPassName() const override - { return "Lift error calls into main."; } + { + return "Lift error calls into main."; + } private: llvm::Function* mEntryFunction; @@ -135,11 +136,11 @@ char LiftErrorCallsPass::ID; void LiftErrorCalls::combineErrorsInFunction(llvm::Function* function, FunctionInfo& info) { - auto errorBlock = llvm::BasicBlock::Create(function->getContext(), "error", function); + auto* errorBlock = llvm::BasicBlock::Create(function->getContext(), "error", function); mBuilder.SetInsertPoint(errorBlock); - auto phi = mBuilder.CreatePHI(getErrorCodeType(), info.selfFails.size(), "error_phi"); - auto err = mBuilder.CreateCall(getErrorFunction(), {phi}); + auto* phi = mBuilder.CreatePHI(CheckRegistry::GetErrorCodeType(mModule.getContext()), info.selfFails.size(), "error_phi"); + auto* err = mBuilder.CreateCall(CheckRegistry::GetErrorFunction(mModule), {phi}); mBuilder.CreateUnreachable(); for (llvm::CallInst* errorCall : info.selfFails) { @@ -158,48 +159,63 @@ void LiftErrorCalls::combineErrorsInFunction(llvm::Function* function, FunctionI info.uniqueErrorCall = err; } +void LiftErrorCalls::collectFailingCalls(llvm::CallGraphNode* cgNode) +{ + llvm::Function* function = cgNode->getFunction(); + if (function == nullptr || function->isDeclaration()) { + return; + } + + // Find error calls in this function. + llvm::copy_if( + classof_range(llvm::make_pointer_range(llvm::instructions(function))), + std::back_inserter(mInfos[function].selfFails), + [](const llvm::CallInst* call) { + const llvm::Function* callee = call->getCalledFunction(); + return callee != nullptr && callee->getName() == CheckRegistry::ErrorFunctionName; + } + ); + + // Find possibly failing calls to other infos. + for (auto& [callInst, calleeNode] : *cgNode) { + llvm::Function* callee = calleeNode->getFunction(); + if (callee == nullptr) { + // TODO: Indirect function calls are unsupported at the moment. + continue; + } + + if (callee->isDeclaration() || !mInfos[callee].canFail()) { + continue; + } + + if (CallInst* call = llvm::dyn_cast(*callInst)) { + mInfos[function].mayFailCalls.emplace_back(call); + } + } +} + +void LiftErrorCalls::replaceReturnsWithUnreachable(const llvm::SmallVectorImpl& returns) +{ + for (llvm::ReturnInst* ret : returns) { + llvm::BasicBlock* retBB = ret->getParent(); + ret->dropAllReferences(); + ret->eraseFromParent(); + + mBuilder.SetInsertPoint(retBB); + mBuilder.CreateCall(getDummyVoidFunc()); + mBuilder.CreateUnreachable(); + } +} + bool LiftErrorCalls::run() { auto sccIt = llvm::scc_begin(&mCallGraph); while (!sccIt.isAtEnd()) { const std::vector& scc = *sccIt; - - for (auto cgNode : scc) { - // TODO: What happens if we get an actual SCC? - auto function = cgNode->getFunction(); - if (function == nullptr || function->isDeclaration()) { - continue; - } - - // Find error calls in this function. - for (auto& inst : llvm::instructions(function)) { - if (auto call = llvm::dyn_cast(&inst)) { - llvm::Function* callee = call->getCalledFunction(); - if (callee != nullptr && callee->getName() == CheckRegistry::ErrorFunctionName) { - mInfos[function].selfFails.push_back(call); - } - } - } - - // Find possibly failing calls to other infos. - for (auto& callRecord : *cgNode) { - llvm::Function* callee = callRecord.second->getFunction(); - if (callee == nullptr) { - // TODO: Indirect function calls are unsupported at the moment. - continue; - } - - if (callee->isDeclaration() || !mInfos[callee].canFail()) { - continue; - } - - if (auto call = llvm::dyn_cast(callRecord.first)) { - mInfos[function].mayFailCalls.emplace_back(call); - } - } + for (CallGraphNode* cgNode : scc) { + this->collectFailingCalls(cgNode); } - ++sccIt; } @@ -214,12 +230,12 @@ bool LiftErrorCalls::run() // this single error location afterwards. for (auto& [function, info] : mInfos) { if (info.canFail()) { - combineErrorsInFunction(function, info); + this->combineErrorsInFunction(function, info); } } // Do the interprocedural transformation. - std::vector mayFailCallsInMain = mInfos[mEntryFunction].mayFailCalls; + std::vector mayFailCallsInMain = mInfos[mEntryFunction].mayFailCalls; // Copy the bodies of the possibly-failing functions into main. for (auto& [function, info] : mInfos) { @@ -234,32 +250,17 @@ bool LiftErrorCalls::run() // Create PHI nodes to represent arguments llvm::ValueToValueMapTy vmap; - info.failCopyArgumentPHIs.resize(function->arg_size()); - mBuilder.SetInsertPoint(failEntry); - for (size_t i = 0; i < function->arg_size(); ++i) { - llvm::Argument& argument = *(function->arg_begin() + i); - llvm::PHINode* phi = mBuilder.CreatePHI(argument.getType(), 0, ""); - info.failCopyArgumentPHIs[i] = phi; - vmap[&argument] = phi; - } + this->representArgumentsAsPhiNodes(function, failEntry, info, vmap); llvm::SmallVector returns; llvm::CloneFunctionInto(mEntryFunction, function, vmap, true, returns); - // The cloned function is in a invalid state, as we do not want the return instructions. - // Replace all returns with unreachable's. - for (auto ret : returns) { - llvm::BasicBlock* retBB = ret->getParent(); - ret->dropAllReferences(); - ret->eraseFromParent(); - - mBuilder.SetInsertPoint(retBB); - mBuilder.CreateCall(getDummyVoidFunc()); - mBuilder.CreateUnreachable(); - } + // The cloned function is in an invalid state, as we do not want the return instructions. + // Replace all returns with unreachable instructions. + this->replaceReturnsWithUnreachable(returns); // Wire the clone of the unique error call to main's error call. - auto mappedErrorCall = dyn_cast_or_null(vmap[info.uniqueErrorCall]); + auto* mappedErrorCall = dyn_cast_or_null(vmap[info.uniqueErrorCall]); assert(mappedErrorCall != nullptr && "The error call must map to a call instruction in the cloned function!"); @@ -276,7 +277,7 @@ bool LiftErrorCalls::run() // Add all possible calls into main mayFailCallsInMain.reserve(mayFailCallsInMain.size() + info.mayFailCalls.size()); for (auto cs : info.mayFailCalls) { - mayFailCallsInMain.emplace_back(vmap[cs.getInstruction()]); + mayFailCallsInMain.emplace_back(llvm::cast(vmap[cs])); } // Remove the error call from the original function @@ -285,37 +286,57 @@ bool LiftErrorCalls::run() info.uniqueErrorPhi->dropAllReferences(); info.uniqueErrorPhi->eraseFromParent(); - auto clonedEntry = cast(vmap[&function->getEntryBlock()]); + auto* clonedEntry = cast(vmap[&function->getEntryBlock()]); mBuilder.SetInsertPoint(failEntry); mBuilder.CreateBr(clonedEntry); } - for (llvm::CallSite call : mayFailCallsInMain) { - assert(call.getCalledFunction() != nullptr); - auto& calleeInfo = mInfos[call.getCalledFunction()]; + for (llvm::CallBase* call : mayFailCallsInMain) { + this->createBranchToFail(call); + } - // Split the block for each call, create a nondet branch. - llvm::BasicBlock* origBlock = call->getParent(); - llvm::BasicBlock* successBlock = llvm::SplitBlock(origBlock, call.getInstruction()); - llvm::BasicBlock* errorBlock = llvm::BasicBlock::Create(mModule.getContext(), "", mEntryFunction); + return true; +} +void LiftErrorCalls::createBranchToFail(llvm::CallBase* call) +{ + assert(call->getCalledFunction() != nullptr); + auto& calleeInfo = mInfos[call->getCalledFunction()]; - // Create the nondetermistic branch between the success and error. - origBlock->getTerminator()->eraseFromParent(); - mBuilder.SetInsertPoint(origBlock); + // Split the block for each call, create a nondet branch. + BasicBlock* origBlock = call->getParent(); + BasicBlock* successBlock = SplitBlock(origBlock, call); + BasicBlock* errorBlock = BasicBlock::Create(mModule.getContext(), "", mEntryFunction); - auto dummyCall = mBuilder.CreateCall(getDummyBoolFunc()); - mBuilder.CreateCondBr(dummyCall, errorBlock, successBlock); + // Create the nondetermistic branch between the success and error. + origBlock->getTerminator()->eraseFromParent(); + mBuilder.SetInsertPoint(origBlock); - // Branch to the appropriate basic block - mBuilder.SetInsertPoint(errorBlock); - mBuilder.CreateBr(calleeInfo.failCopyEntry); + llvm::Instruction* dummyCall = mBuilder.CreateCall(getDummyBoolFunc()); + mBuilder.CreateCondBr(dummyCall, errorBlock, successBlock); - for (size_t i = 0; i < call.arg_size(); ++i) { - calleeInfo.failCopyArgumentPHIs[i]->addIncoming(call.getArgOperand(i), errorBlock); - } + // Branch to the appropriate basic block + mBuilder.SetInsertPoint(errorBlock); + mBuilder.CreateBr(calleeInfo.failCopyEntry); + + for (unsigned i = 0; i < call->arg_size(); ++i) { + calleeInfo.failCopyArgumentPHIs[i]->addIncoming(call->getArgOperand(i), errorBlock); } +} - return true; +void LiftErrorCalls::representArgumentsAsPhiNodes( + Function* function, + BasicBlock* failEntry, + LiftErrorCalls::FunctionInfo& info, + ValueToValueMapTy& vmap) +{ + info.failCopyArgumentPHIs.resize(function->arg_size()); + mBuilder.SetInsertPoint(failEntry); + for (size_t i = 0; i < function->arg_size(); ++i) { + Argument& argument = *(function->arg_begin() + i); + PHINode* phi = mBuilder.CreatePHI(argument.getType(), 0, ""); + info.failCopyArgumentPHIs[i] = phi; + vmap[&argument] = phi; + } } llvm::Pass* gazer::createLiftErrorCallsPass(llvm::Function& entry) diff --git a/src/LLVM/Transform/NormalizeVerifierCalls.cpp b/src/LLVM/Transform/NormalizeVerifierCalls.cpp deleted file mode 100644 index fb73b51c..00000000 --- a/src/LLVM/Transform/NormalizeVerifierCalls.cpp +++ /dev/null @@ -1,138 +0,0 @@ -//==-------------------------------------------------------------*- C++ -*--==// -// -// Copyright 2019 Contributors to the Gazer project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -/// -/// \file This pass normalizes some known verifier calls into a uniform format. -/// -//===----------------------------------------------------------------------===// - -#include "gazer/LLVM/Transform/Passes.h" - -#include -#include -#include -#include - -using namespace gazer; - -namespace -{ - -// The implementation of this class is partly based on the PromoteVerifierCalls -// pass found in SeaHorn. -// FIXME: We should update the call graph information as we go. -class NormalizeVerifierCallsPass : public llvm::ModulePass -{ -public: - static char ID; - - NormalizeVerifierCallsPass() - : ModulePass(ID) - {} - - bool runOnModule(llvm::Module& module) override; - void runOnFunction(llvm::Function& function); - -private: - llvm::FunctionCallee mAssume; - llvm::FunctionCallee mError; -}; - -} // end anonymous namespace - -bool NormalizeVerifierCallsPass::runOnModule(llvm::Module& module) -{ - // Insert our uniform functions. - mAssume = module.getOrInsertFunction( - "verifier.assume", - llvm::Type::getVoidTy(module.getContext()), - llvm::Type::getInt1Ty(module.getContext()) - ); - - llvm::AttrBuilder attrBuilder; - attrBuilder.addAttribute(llvm::Attribute::NoReturn); - - mError = module.getOrInsertFunction( - "verifier.error", - llvm::AttributeList::get( - module.getContext(), llvm::AttributeList::FunctionIndex, attrBuilder - ), - llvm::Type::getVoidTy(module.getContext()) - ); - - for (llvm::Function& function : module) { - this->runOnFunction(function); - } - - return true; -} - -char NormalizeVerifierCallsPass::ID; - -void NormalizeVerifierCallsPass::runOnFunction(llvm::Function& function) -{ - llvm::SmallVector toKill; - for (llvm::Instruction& inst : llvm::instructions(function)) { - if (!llvm::isa(&inst)) { - continue; - } - - llvm::CallSite cs(llvm::cast(&inst)); - - llvm::IRBuilder<> builder(function.getContext()); - builder.SetInsertPoint(&inst); - - llvm::Function* callee = cs.getCalledFunction(); - if (callee == nullptr) { - // TODO: This should be handled for simple cases. - continue; - } - - if (callee->getName() == "__VERIFIER_assume" || - callee->getName() == "klee_assume" || - callee->getName() == "__llbmc_assume" - ) { - llvm::Value* condition = cs.getArgument(0); - - // These functions may have differing input argument types, such - // as i1, i32 or i64. Strip possible ZExt casts and convert to - // boolean if needed. - if (auto zext = llvm::dyn_cast(condition)) { - condition = zext->getOperand(0); - } - - if (!condition->getType()->isIntegerTy(1)) { - condition = builder.CreateICmpNE(condition, llvm::ConstantInt::get( - condition->getType(), 0 - )); - } - - builder.CreateCall(mAssume.getCallee(), { condition }); - toKill.emplace_back(&inst); - } - } - - for (llvm::Instruction* inst : toKill) { - inst->dropAllReferences(); - inst->eraseFromParent(); - } -} - -llvm::Pass* gazer::createNormalizeVerifierCallsPass() -{ - return new NormalizeVerifierCallsPass(); -} diff --git a/src/LLVM/Transform/TransformUtils.cpp b/src/LLVM/Transform/TransformUtils.cpp index 5fcf48a3..05d8f9a2 100644 --- a/src/LLVM/Transform/TransformUtils.cpp +++ b/src/LLVM/Transform/TransformUtils.cpp @@ -32,7 +32,7 @@ bool isRecursive(llvm::CallGraphNode* target) auto end = llvm::scc_end(target); for (auto it = begin; it != end; ++it) { - if (it.hasLoop()) { + if (it.hasCycle()) { return true; } } @@ -40,4 +40,4 @@ bool isRecursive(llvm::CallGraphNode* target) return false; } -} \ No newline at end of file +} // namespace gazer diff --git a/src/SolverZ3/CMakeLists.txt b/src/SolverZ3/CMakeLists.txt index 3406b682..bf3a1acf 100644 --- a/src/SolverZ3/CMakeLists.txt +++ b/src/SolverZ3/CMakeLists.txt @@ -1,30 +1,30 @@ -set(SOURCE_FILES - Z3Solver.cpp - Z3Model.cpp -) +# Fetch Z3 +set(Z3_DOWNLOAD_DIR "${CMAKE_CURRENT_BINARY_DIR}/vendor/z3_download") -# Z3 -ExternalProject_Add(z3_download - PREFIX vendor - # Download +FetchContent_Declare( + z3_download URL https://github.com/Z3Prover/z3/releases/download/z3-4.8.6/z3-4.8.6-x64-ubuntu-16.04.zip URL_HASH SHA1=1663a7825c45f24642045dfcc5e100d65c1b6b1e - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - UPDATE_COMMAND "" - INSTALL_COMMAND "" + SOURCE_DIR ${Z3_DOWNLOAD_DIR} ) -set(Z3_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/vendor/src/z3_download/include") -set(Z3_LIBRARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/vendor/src/z3_download/bin") +message(STATUS "Fetching Z3") +FetchContent_MakeAvailable(z3_download) + +set(Z3_INCLUDE_DIR "${Z3_DOWNLOAD_DIR}/include") +set(Z3_LIBRARY_DIR "${Z3_DOWNLOAD_DIR}/bin") -message("Z3 includes are ${Z3_INCLUDE_DIR}") +message(STATUS "Z3 includes are ${Z3_INCLUDE_DIR}") -# Z3 add_library(z3 SHARED IMPORTED) set_property(TARGET z3 PROPERTY IMPORTED_LOCATION "${Z3_LIBRARY_DIR}/libz3.so") include_directories("${Z3_INCLUDE_DIR}") add_dependencies(z3 z3_download) +set(SOURCE_FILES + Z3Solver.cpp + Z3Model.cpp +) + add_library(GazerZ3Solver SHARED ${SOURCE_FILES}) target_link_libraries(GazerZ3Solver GazerCore z3) diff --git a/src/SolverZ3/Z3Model.cpp b/src/SolverZ3/Z3Model.cpp index 7ee7ec15..e6db5cad 100644 --- a/src/SolverZ3/Z3Model.cpp +++ b/src/SolverZ3/Z3Model.cpp @@ -32,12 +32,15 @@ namespace class Z3Model : public Model { public: - Z3Model(GazerContext& context, Z3_context& z3Context, Z3_model model, Z3DeclMapTy& decls, Z3ExprTransformer& exprs) - : mContext(context), mZ3Context(z3Context), mModel(model), mDecls(decls), mExprTransformer(exprs) + Z3Model(GazerContext& context, Z3_context& z3Context, Z3_model model, Z3ExprTransformer& exprs) + : mContext(context), mZ3Context(z3Context), mModel(model), mExprTransformer(exprs) { Z3_model_inc_ref(mZ3Context, mModel); } + Z3Model(const Z3Model&) = delete; + Z3Model& operator=(const Z3Model&) = delete; + ExprRef evaluate(const ExprPtr& expr) override; void dump(llvm::raw_ostream& os) override { @@ -50,7 +53,6 @@ class Z3Model : public Model private: ExprRef evalAst(Z3AstHandle ast); -private: ExprRef evalBoolean(Z3AstHandle ast); ExprRef evalBv(Z3AstHandle ast, unsigned width); ExprRef evalFloat(Z3AstHandle ast, FloatType::FloatPrecision prec); @@ -65,7 +67,6 @@ class Z3Model : public Model GazerContext& mContext; Z3_context& mZ3Context; Z3_model mModel; - Z3DeclMapTy& mDecls; Z3ExprTransformer& mExprTransformer; }; @@ -74,12 +75,12 @@ class Z3Model : public Model auto Z3Solver::getModel() -> std::unique_ptr { return std::make_unique( - mContext, mZ3Context, Z3_solver_get_model(mZ3Context, mSolver), mDecls, mTransformer); + mContext, mZ3Context, Z3_solver_get_model(mZ3Context, mSolver), mTransformer); } auto Z3Model::evaluate(const ExprPtr& expr) -> ExprRef { - auto ast = mExprTransformer.walk(expr); + auto ast = mExprTransformer.translate(expr); return this->evalAst(ast); } @@ -105,9 +106,9 @@ auto Z3Model::evalAst(Z3AstHandle ast) -> ExprRef return this->evalFloat(result, this->getFloatPrecision(sort)); case Z3_ARRAY_SORT: return this->evalConstantArray(result, llvm::cast(this->sortToType(sort))); + default: + llvm_unreachable("Unknown Z3 sort!"); } - - llvm_unreachable("Unknown Z3 sort!"); } auto Z3Model::evalBoolean(Z3AstHandle ast) -> ExprRef @@ -117,10 +118,11 @@ auto Z3Model::evalBoolean(Z3AstHandle ast) -> ExprRef case Z3_L_TRUE: return BoolLiteralExpr::True(mContext); case Z3_L_FALSE: return BoolLiteralExpr::False(mContext); case Z3_L_UNDEF: - default: - llvm_unreachable("A function of boolean sort must be convertible to a boolean value!"); break; } + + llvm_unreachable("A function of boolean sort must be convertible to a boolean value!"); + return nullptr; } auto Z3Model::evalInt(Z3AstHandle ast) -> ExprRef @@ -167,8 +169,7 @@ auto Z3Model::evalFloat(Z3AstHandle ast, FloatType::FloatPrecision prec) auto Z3Model::evalConstantArray(Z3AstHandle ast, ArrayType& type) -> ExprRef { - auto kind = Z3_get_ast_kind(mZ3Context, ast); - if (kind == Z3_APP_AST) { + if (Z3_get_ast_kind(mZ3Context, ast) == Z3_APP_AST) { Z3_app app = Z3_to_app(mZ3Context, ast); Z3Handle decl(mZ3Context, Z3_get_app_decl(mZ3Context, app)); @@ -245,21 +246,17 @@ auto Z3Model::sortToType(Z3Handle sort) -> Type& return BoolType::Get(mContext); case Z3_INT_SORT: return IntType::Get(mContext); - case Z3_BV_SORT: { - unsigned size = Z3_get_bv_sort_size(mZ3Context, sort); - return BvType::Get(mContext, size); - } - case Z3_FLOATING_POINT_SORT: { - auto precision = this->getFloatPrecision(sort); - return FloatType::Get(mContext, precision); - } + case Z3_BV_SORT: + return BvType::Get(mContext, Z3_get_bv_sort_size(mZ3Context, sort)); + case Z3_FLOATING_POINT_SORT: + return FloatType::Get(mContext, this->getFloatPrecision(sort)); case Z3_ARRAY_SORT: { auto domain = Z3Handle(mZ3Context, Z3_get_array_sort_domain(mZ3Context, sort)); auto range = Z3Handle(mZ3Context, Z3_get_array_sort_range(mZ3Context, sort)); return ArrayType::Get(sortToType(domain), sortToType(range)); } + default: + llvm_unreachable("Unknown/unsupported Z3 sort!"); } - - llvm_unreachable("Unknown Z3 sort!"); } \ No newline at end of file diff --git a/src/SolverZ3/Z3Solver.cpp b/src/SolverZ3/Z3Solver.cpp index 27f5875a..53993af0 100644 --- a/src/SolverZ3/Z3Solver.cpp +++ b/src/SolverZ3/Z3Solver.cpp @@ -17,8 +17,6 @@ //===----------------------------------------------------------------------===// #include "Z3SolverImpl.h" -#include "gazer/Support/Float.h" - #include #include @@ -31,9 +29,9 @@ using namespace gazer; namespace { - llvm::cl::opt Z3SolveParallel("z3-solve-parallel", llvm::cl::desc("Enable Z3's parallel solver")); - llvm::cl::opt Z3ThreadsMax("z3-threads-max", llvm::cl::desc("Maximum number of threads"), llvm::cl::init(0)); - llvm::cl::opt Z3DumpModel("z3-dump-model", llvm::cl::desc("Dump Z3 model")); + const llvm::cl::opt Z3SolveParallel("z3-solve-parallel", llvm::cl::desc("Enable Z3's parallel solver")); + const llvm::cl::opt Z3ThreadsMax("z3-threads-max", llvm::cl::desc("Maximum number of threads"), llvm::cl::init(0)); + const llvm::cl::opt Z3DumpModel("z3-dump-model", llvm::cl::desc("Dump Z3 model")); } // end anonymous namespace @@ -79,7 +77,8 @@ Solver::SolverStatus Z3Solver::run() llvm::errs() << Z3_model_to_string(mZ3Context, Z3_solver_get_model(mZ3Context, mSolver)) << "\n"; } return SolverStatus::SAT; - case Z3_L_UNDEF: return SolverStatus::UNKNOWN; + case Z3_L_UNDEF: + return SolverStatus::UNKNOWN; } llvm_unreachable("Unknown solver status encountered."); @@ -87,25 +86,25 @@ Solver::SolverStatus Z3Solver::run() void Z3Solver::addConstraint(ExprPtr expr) { - auto z3Expr = mTransformer.walk(expr); + auto z3Expr = mTransformer.translate(expr); Z3_solver_assert(mZ3Context, mSolver, z3Expr); } -void Z3Solver::reset() +void Z3Solver::doReset() { mCache.clear(); mDecls.clear(); Z3_solver_reset(mZ3Context, mSolver); } -void Z3Solver::push() +void Z3Solver::doPush() { mCache.push(); mDecls.push(); Z3_solver_push(mZ3Context, mSolver); } -void Z3Solver::pop() +void Z3Solver::doPop() { mCache.pop(); mDecls.pop(); @@ -152,8 +151,7 @@ auto Z3ExprTransformer::shouldSkip(const ExprPtr& expr, Z3AstHandle* ret) -> boo return false; } - auto result = mCache.get(expr); - if (result) { + if (auto result = mCache.get(expr)) { *ret = *result; return true; } @@ -168,8 +166,7 @@ void Z3ExprTransformer::handleResult(const ExprPtr& expr, Z3AstHandle& ret) auto Z3ExprTransformer::translateDecl(Variable* variable) -> Z3Handle { - auto opt = mDecls.get(variable); - if (opt) { + if (auto opt = mDecls.get(variable)) { return *opt; } @@ -260,7 +257,7 @@ auto Z3ExprTransformer::visitTupleSelect(const ExprRef& expr) - auto Z3ExprTransformer::visitTupleConstruct(const ExprRef& expr) -> Z3AstHandle { - auto& tupTy = llvm::cast(expr->getOperand(0)->getType()); + auto& tupTy = expr->getType(); assert(mTupleInfo.count(&tupTy) != 0 && "Tuple types should have been handled before!"); auto& info = mTupleInfo[&tupTy]; @@ -276,15 +273,15 @@ auto Z3ExprTransformer::visitTupleConstruct(const ExprRef& e auto Z3ExprTransformer::transformRoundingMode(llvm::APFloat::roundingMode rm) -> Z3AstHandle { switch (rm) { - case llvm::APFloat::roundingMode::rmNearestTiesToEven: + case llvm::APFloat::roundingMode::NearestTiesToEven: return createHandle(Z3_mk_fpa_round_nearest_ties_to_even(mZ3Context)); - case llvm::APFloat::roundingMode::rmNearestTiesToAway: + case llvm::APFloat::roundingMode::NearestTiesToAway: return createHandle(Z3_mk_fpa_round_nearest_ties_to_away(mZ3Context)); - case llvm::APFloat::roundingMode::rmTowardPositive: + case llvm::APFloat::roundingMode::TowardPositive: return createHandle(Z3_mk_fpa_round_toward_positive(mZ3Context)); - case llvm::APFloat::roundingMode::rmTowardNegative: + case llvm::APFloat::roundingMode::TowardNegative: return createHandle(Z3_mk_fpa_round_toward_negative(mZ3Context)); - case llvm::APFloat::roundingMode::rmTowardZero: + case llvm::APFloat::roundingMode::TowardZero: return createHandle(Z3_mk_fpa_round_toward_zero(mZ3Context)); } @@ -335,39 +332,29 @@ Z3Handle Z3ExprTransformer::typeToSort(Type& type) Z3Handle Z3ExprTransformer::handleTupleType(TupleType& tupTy) { // Check if we already visited this type - auto it = mTupleInfo.find(&tupTy); - if (it != mTupleInfo.end()) { + if (auto it = mTupleInfo.find(&tupTy); it != mTupleInfo.end()) { return it->second.sort; } - auto name = Z3_mk_string_symbol(mZ3Context, ("mk_" + tupTy.getName()).c_str()); + Z3_symbol name = Z3_mk_string_symbol(mZ3Context, ("mk_" + tupTy.getName()).c_str()); std::vector projs; std::vector sorts; - for (size_t i = 0; i < tupTy.getNumSubtypes(); ++i) { + for (unsigned i = 0; i < tupTy.getNumSubtypes(); ++i) { std::string projName = tupTy.getName() + "_" + std::to_string(i); projs.emplace_back(Z3_mk_string_symbol(mZ3Context, projName.c_str())); - sorts.emplace_back(typeToSort(tupTy.getTypeAtIndex(i))); + sorts.emplace_back(typeToSort(tupTy.getSubType(i))); } Z3_func_decl constructDecl; std::vector projDecls(tupTy.getNumSubtypes()); - auto tup = Z3_mk_tuple_sort( + Z3_sort tup = Z3_mk_tuple_sort( mZ3Context, name, tupTy.getNumSubtypes(), projs.data(), sorts.data(), &constructDecl, projDecls.data() ); - - if (tup == nullptr) { - std::string errStr; - llvm::raw_string_ostream err{errStr}; - - err << "Invalid Z3_sort!\n" - << "Z3 error: " << Z3_get_error_msg(mZ3Context, Z3_get_error_code(mZ3Context)) - << "\n"; - llvm::report_fatal_error(err.str(), true); - } + checkErrors(mZ3Context, tup); auto& info = mTupleInfo[&tupTy]; @@ -380,12 +367,15 @@ Z3Handle Z3ExprTransformer::handleTupleType(TupleType& tupTy) return info.sort; } +Z3AstHandle Z3ExprTransformer::translateAPInt(const llvm::APInt& value, Z3Handle z3Sort) +{ + return createHandle(Z3_mk_numeral(mZ3Context, value.toString(10, false).c_str(), z3Sort)); +} + Z3AstHandle Z3ExprTransformer::translateLiteral(const ExprRef& expr) { if (auto bvLit = llvm::dyn_cast(expr)) { - llvm::SmallString<20> buffer; - bvLit->getValue().toStringUnsigned(buffer, 10); - return createHandle(Z3_mk_numeral(mZ3Context, buffer.c_str(), typeToSort(bvLit->getType()))); + return translateAPInt(bvLit->getValue(), typeToSort(bvLit->getType())); } if (auto bl = llvm::dyn_cast(expr)) { @@ -397,17 +387,12 @@ Z3AstHandle Z3ExprTransformer::translateLiteral(const ExprRef& expr } if (auto fl = llvm::dyn_cast(expr)) { - if (fl->getType().getPrecision() == FloatType::Single) { - return createHandle(Z3_mk_fpa_numeral_float( - mZ3Context, fl->getValue().convertToFloat(), typeToSort(fl->getType()) - )); - } - - if (fl->getType().getPrecision() == FloatType::Double) { - return createHandle(Z3_mk_fpa_numeral_double( - mZ3Context, fl->getValue().convertToDouble(), typeToSort(fl->getType()) - )); - } + llvm::APInt fpAsBv = fl->getValue().bitcastToAPInt(); + return createHandle(Z3_mk_fpa_to_fp_bv( + mZ3Context, + translateAPInt(fpAsBv, typeToSort(BvType::Get(fl->getContext(), fpAsBv.getBitWidth()))), + typeToSort(fl->getType()) + )); } if (auto arrayLit = llvm::dyn_cast(expr)) { @@ -432,5 +417,5 @@ Z3AstHandle Z3ExprTransformer::translateLiteral(const ExprRef& expr std::unique_ptr Z3SolverFactory::createSolver(GazerContext& context) { - return std::unique_ptr(new Z3Solver(context)); + return std::make_unique(context); } diff --git a/src/SolverZ3/Z3SolverImpl.h b/src/SolverZ3/Z3SolverImpl.h index a579cffe..5062f0d6 100644 --- a/src/SolverZ3/Z3SolverImpl.h +++ b/src/SolverZ3/Z3SolverImpl.h @@ -171,10 +171,8 @@ using Z3DeclMapTy = ScopedCache< >; /// Translates expressions into Z3 nodes. -class Z3ExprTransformer : public ExprWalker +class Z3ExprTransformer : private ExprWalker { - friend class ExprWalker; - struct TupleInfo { Z3Handle sort; @@ -190,6 +188,11 @@ class Z3ExprTransformer : public ExprWalker : mZ3Context(context), mTmpCount(tmpCount), mCache(cache), mDecls(decls) {} + Z3AstHandle translate(const ExprPtr& expr) + { + return this->walk(expr); + } + /// Free up all data owned by this object. void clear() { mTupleInfo.clear(); @@ -206,10 +209,10 @@ class Z3ExprTransformer : public ExprWalker Z3AstHandle translateLiteral(const ExprRef& expr); private: - bool shouldSkip(const ExprPtr& expr, Z3AstHandle* ret); - void handleResult(const ExprPtr& expr, Z3AstHandle& ret); + bool shouldSkip(const ExprPtr& expr, Z3AstHandle* ret) override; + void handleResult(const ExprPtr& expr, Z3AstHandle& ret) override; - Z3AstHandle visitExpr(const ExprPtr& expr) // NOLINT(readability-convert-member-functions-to-static) + Z3AstHandle visitExpr(const ExprPtr& expr) override // NOLINT(readability-convert-member-functions-to-static) { llvm::errs() << *expr << "\n"; llvm_unreachable("Unhandled expression type in Z3ExprTransformer."); @@ -218,23 +221,23 @@ class Z3ExprTransformer : public ExprWalker // Use some helper macros to translate trivial cases #define TRANSLATE_UNARY_OP(NAME, Z3_METHOD) \ - Z3AstHandle visit##NAME(const ExprRef& expr) { \ + Z3AstHandle visit##NAME(const ExprRef& expr) override { \ return createHandle(Z3_METHOD(mZ3Context, getOperand(0))); \ } \ #define TRANSLATE_BINARY_OP(NAME, Z3_METHOD) \ - Z3AstHandle visit##NAME(const ExprRef& expr) { \ + Z3AstHandle visit##NAME(const ExprRef& expr) override { \ return createHandle(Z3_METHOD(mZ3Context, getOperand(0), getOperand(1))); \ } \ #define TRANSLATE_TERNARY_OP(NAME, Z3_METHOD) \ - Z3AstHandle visit##NAME(const ExprRef& expr) { \ + Z3AstHandle visit##NAME(const ExprRef& expr) override { \ return createHandle(Z3_METHOD( \ mZ3Context, getOperand(0), getOperand(1), getOperand(2))); \ } \ #define TRANSLATE_BINARY_FPA_RM(NAME, Z3_METHOD) \ - Z3AstHandle visit##NAME(const ExprRef& expr) { \ + Z3AstHandle visit##NAME(const ExprRef& expr) override { \ return createHandle(Z3_METHOD(mZ3Context, \ transformRoundingMode(expr->getRoundingMode()), \ getOperand(0), getOperand(1) \ @@ -242,13 +245,21 @@ class Z3ExprTransformer : public ExprWalker } \ // Nullary - Z3AstHandle visitVarRef(const ExprRef& expr); + Z3AstHandle visitVarRef(const ExprRef& expr) override; - Z3AstHandle visitUndef(const ExprRef& expr) { + Z3AstHandle visitUndef(const ExprRef& expr) override + { + // Undef has special semantics here: as we only translate a formula only once and the result + // is cached, should an undef reach the solver, the fresh constant created with it is cached + // along the parent formula. This contradicts the general semantics of Undef, however, this + // way we do not have cases where given a formula F1 containing and undef, the formula + // And(F1, Not(F1)) is satisfiable. + + // TODO(sallaigy): Fix this behavior (maybe display an error?) if undef lifting is implemented for CFA. return createHandle(Z3_mk_fresh_const(mZ3Context, "", typeToSort(expr->getType()))); } - Z3AstHandle visitLiteral(const ExprRef& expr) { + Z3AstHandle visitLiteral(const ExprRef& expr) override { return this->translateLiteral(expr); } @@ -256,9 +267,9 @@ class Z3ExprTransformer : public ExprWalker TRANSLATE_UNARY_OP(Not, Z3_mk_not) // Arithmetic operators - Z3AstHandle visitAdd(const ExprRef& expr); - Z3AstHandle visitSub(const ExprRef& expr); - Z3AstHandle visitMul(const ExprRef& expr); + Z3AstHandle visitAdd(const ExprRef& expr) override; + Z3AstHandle visitSub(const ExprRef& expr) override; + Z3AstHandle visitMul(const ExprRef& expr) override; TRANSLATE_BINARY_OP(Div, Z3_mk_div) TRANSLATE_BINARY_OP(Mod, Z3_mk_mod) @@ -268,8 +279,8 @@ class Z3ExprTransformer : public ExprWalker TRANSLATE_BINARY_OP(Imply, Z3_mk_implies) // Multiary logic - Z3AstHandle visitAnd(const ExprRef& expr); - Z3AstHandle visitOr(const ExprRef& expr); + Z3AstHandle visitAnd(const ExprRef& expr) override; + Z3AstHandle visitOr(const ExprRef& expr) override; // Bit-vectors TRANSLATE_BINARY_OP(BvSDiv, Z3_mk_bvsdiv) @@ -291,7 +302,7 @@ class Z3ExprTransformer : public ExprWalker TRANSLATE_BINARY_OP(Gt, Z3_mk_gt) TRANSLATE_BINARY_OP(GtEq, Z3_mk_ge) - Z3AstHandle visitNotEq(const ExprRef& expr); + Z3AstHandle visitNotEq(const ExprRef& expr) override; // Bit-vector comparisons TRANSLATE_BINARY_OP(BvSLt, Z3_mk_bvslt) @@ -333,15 +344,15 @@ class Z3ExprTransformer : public ExprWalker #undef TRANSLATE_BINARY_FPA_RM // Bit-vector casts - Z3AstHandle visitZExt(const ExprRef& expr) { + Z3AstHandle visitZExt(const ExprRef& expr) override { return createHandle(Z3_mk_zero_ext(mZ3Context, expr->getWidthDiff(), getOperand(0))); } - Z3AstHandle visitSExt(const ExprRef& expr) { + Z3AstHandle visitSExt(const ExprRef& expr) override { return createHandle(Z3_mk_sign_ext(mZ3Context, expr->getWidthDiff(), getOperand(0))); } - Z3AstHandle visitExtract(const ExprRef& expr) + Z3AstHandle visitExtract(const ExprRef& expr) override { unsigned hi = expr->getOffset() + expr->getWidth() - 1; unsigned lo = expr->getOffset(); @@ -350,7 +361,7 @@ class Z3ExprTransformer : public ExprWalker } // Floating-point casts - Z3AstHandle visitFCast(const ExprRef& expr) + Z3AstHandle visitFCast(const ExprRef& expr) override { return createHandle(Z3_mk_fpa_to_fp_float(mZ3Context, transformRoundingMode(expr->getRoundingMode()), @@ -359,7 +370,7 @@ class Z3ExprTransformer : public ExprWalker )); } - Z3AstHandle visitSignedToFp(const ExprRef& expr) + Z3AstHandle visitSignedToFp(const ExprRef& expr) override { return createHandle(Z3_mk_fpa_to_fp_signed(mZ3Context, transformRoundingMode(expr->getRoundingMode()), @@ -368,7 +379,7 @@ class Z3ExprTransformer : public ExprWalker )); } - Z3AstHandle visitUnsignedToFp(const ExprRef& expr) + Z3AstHandle visitUnsignedToFp(const ExprRef& expr) override { return createHandle(Z3_mk_fpa_to_fp_unsigned(mZ3Context, transformRoundingMode(expr->getRoundingMode()), @@ -377,7 +388,7 @@ class Z3ExprTransformer : public ExprWalker )); } - Z3AstHandle visitFpToSigned(const ExprRef& expr) + Z3AstHandle visitFpToSigned(const ExprRef& expr) override { return createHandle(Z3_mk_fpa_to_sbv(mZ3Context, transformRoundingMode(expr->getRoundingMode()), @@ -386,7 +397,7 @@ class Z3ExprTransformer : public ExprWalker )); } - Z3AstHandle visitFpToUnsigned(const ExprRef& expr) + Z3AstHandle visitFpToUnsigned(const ExprRef& expr) override { return createHandle(Z3_mk_fpa_to_ubv(mZ3Context, transformRoundingMode(expr->getRoundingMode()), @@ -395,7 +406,7 @@ class Z3ExprTransformer : public ExprWalker )); } - Z3AstHandle visitFpToBv(const ExprRef& expr) + Z3AstHandle visitFpToBv(const ExprRef& expr) override { return createHandle(Z3_mk_fpa_to_ieee_bv( mZ3Context, @@ -403,7 +414,7 @@ class Z3ExprTransformer : public ExprWalker )); } - Z3AstHandle visitBvToFp(const ExprRef& expr) + Z3AstHandle visitBvToFp(const ExprRef& expr) override { return createHandle(Z3_mk_fpa_to_fp_bv( mZ3Context, @@ -412,17 +423,19 @@ class Z3ExprTransformer : public ExprWalker )); } - Z3AstHandle visitTupleSelect(const ExprRef& expr); - Z3AstHandle visitTupleConstruct(const ExprRef& expr); + Z3AstHandle visitTupleSelect(const ExprRef& expr) override; + Z3AstHandle visitTupleConstruct(const ExprRef& expr) override; -protected: Z3AstHandle transformRoundingMode(llvm::APFloat::roundingMode rm); + Z3AstHandle translateAPInt(const llvm::APInt& value, Z3Handle z3Sort); protected: Z3_context& mZ3Context; unsigned& mTmpCount; Z3CacheMapTy& mCache; Z3DeclMapTy& mDecls; + +private: std::unordered_map mTupleInfo; }; @@ -432,23 +445,26 @@ class Z3Solver : public Solver public: explicit Z3Solver(GazerContext& context); + Z3Solver(const Z3Solver&) = delete; + Z3Solver& operator=(const Z3Solver&) = delete; + void printStats(llvm::raw_ostream& os) override; void dump(llvm::raw_ostream& os) override; SolverStatus run() override; std::unique_ptr getModel() override; - void reset() override; - - void push() override; - void pop() override; - - ~Z3Solver(); + ~Z3Solver() override; protected: void addConstraint(ExprPtr expr) override; -protected: + void doReset() override; + + void doPush() override; + void doPop() override; + +private: Z3_config mConfig; Z3_context mZ3Context; Z3_solver mSolver; diff --git a/src/Support/CMakeLists.txt b/src/Support/CMakeLists.txt index 3fa14045..8f65ba4a 100644 --- a/src/Support/CMakeLists.txt +++ b/src/Support/CMakeLists.txt @@ -2,7 +2,7 @@ set(SOURCE_FILES SExpr.cpp Runtime.cpp) -llvm_map_components_to_libnames(LLVM_LIBS support) +require_llvm_libraries(GAZER_LLVM_LIBS support) add_library(GazerSupport ${SOURCE_FILES}) -target_link_libraries(GazerSupport ${LLVM_LIBS}) +target_link_libraries(GazerSupport ${GAZER_LLVM_LIBS}) diff --git a/src/Support/Runtime.cpp b/src/Support/Runtime.cpp index abed0403..7fc314d7 100644 --- a/src/Support/Runtime.cpp +++ b/src/Support/Runtime.cpp @@ -60,7 +60,9 @@ static llvm::ErrorOr read_symlink(llvm::StringRef path) if (ret < 0) { std::error_code ec(errno, std::system_category()); return ec; - } else if (ret < static_cast(size)) { + } + + if (ret < static_cast(size)) { return std::string(buffer.data(), static_cast(ret)); } size *= 2; @@ -83,19 +85,18 @@ llvm::ErrorOr gazer::findProgramLocation(llvm::StringRef argvZero) #endif // Try a heuristic based on argv[0], if supplied - if (argvZero.size() == 0) { + if (argvZero.empty()) { return result; } llvm::SmallString<64> path(argvZero); if (llvm::sys::path::is_absolute(argvZero) || llvm::sys::path::is_relative(argvZero)) { - auto ec = llvm::sys::fs::real_path(argvZero, path); - if (ec) { + if (auto ec = llvm::sys::fs::real_path(argvZero, path); ec) { return ec; } - return path.str(); + return path.str().str(); } // See if we can look it up in the PATH diff --git a/src/Support/SExpr.cpp b/src/Support/SExpr.cpp index 89896c04..3f7729c8 100644 --- a/src/Support/SExpr.cpp +++ b/src/Support/SExpr.cpp @@ -73,7 +73,7 @@ std::unique_ptr gazer::sexpr::parse(llvm::StringRef input) auto gazer::sexpr::atom(llvm::StringRef data) -> sexpr::Value* { - return new sexpr::Value(data); + return new sexpr::Value(data.str()); } auto gazer::sexpr::list(std::vector data) -> sexpr::Value* diff --git a/src/Trace/CorrectnessWitnessWriter.cpp b/src/Trace/CorrectnessWitnessWriter.cpp index a17120ac..5094a51f 100644 --- a/src/Trace/CorrectnessWitnessWriter.cpp +++ b/src/Trace/CorrectnessWitnessWriter.cpp @@ -15,13 +15,10 @@ // limitations under the License. // //===----------------------------------------------------------------------===// - -#include "gazer/Core/LiteralExpr.h" #include "gazer/Trace/WitnessWriter.h" -#include -#include -#include +#include +#include using namespace gazer; @@ -63,18 +60,14 @@ const std::string graph_data = R"( void CorrectnessWitnessWriter::outputWitness() { - time_t rawtime; - struct tm * ptm; - time ( &rawtime ); - ptm = gmtime( &rawtime ); // UTC timestamp - std::stringstream timestamp; - timestamp << std::put_time(ptm, "%FT%T"); + using namespace std::chrono; + llvm::sys::TimePoint timestamp = time_point_cast(system_clock::now()); mOS << schema; mOS << keys; mOS << graph_data; mOS << "" << mHash << ""; - mOS << "" << timestamp.str() << "\n"; + mOS << "" << llvm::formatv("{0:%FT%T}", timestamp) << "\n"; mOS << "" << SourceFileName << "\n"; mOS << nodes; mOS << ""; diff --git a/src/Trace/TextTraceWriter.cpp b/src/Trace/TextTraceWriter.cpp index 3c6a5dca..0ff9201d 100644 --- a/src/Trace/TextTraceWriter.cpp +++ b/src/Trace/TextTraceWriter.cpp @@ -32,7 +32,7 @@ namespace { public: explicit TextTraceWriter(llvm::raw_ostream& os, bool printBv = true) - : TraceWriter(os), mPrintBv(printBv), mFuncEntries(0) + : TraceWriter(os), mPrintBv(printBv) {} static constexpr auto INDENT = " "; @@ -45,7 +45,7 @@ namespace if (llvm::isa(expr.get())) { mOS << "???"; } else if (auto bv = llvm::dyn_cast(expr)) { - TraceVariable var = event.getVariable(); + const TraceVariable& var = event.getVariable(); unsigned varSize = var.getSize(); switch (var.getRepresentation()) { @@ -68,6 +68,8 @@ namespace break; case TraceVariable::Rep_Float: llvm_unreachable("Cannot represent a int BV type as a float!"); + default: + break; } if (mPrintBv) { @@ -75,9 +77,7 @@ namespace bv->getValue().zextOrSelf(32).toString(bits, 2, false, false); mOS << "\t(0b"; if (bits.size() < varSize) { - for (int i = 0; i < varSize - bits.size(); ++i) { - mOS.write('0'); - } + mOS.write_zeros(varSize - bits.size()); } mOS << bits << ')'; } @@ -85,8 +85,7 @@ namespace expr->print(mOS); } - auto location = event.getLocation(); - if (location.getLine() != 0) { + if (auto location = event.getLocation(); location.getLine() != 0) { mOS << "\t at " << location.getLine() << ":" @@ -135,8 +134,7 @@ namespace } mOS << "\t"; - auto location = event.getLocation(); - if (location.getLine() != 0) { + if (auto location = event.getLocation(); location.getLine() != 0) { mOS << "\t at " << location.getLine() << ":" @@ -157,8 +155,7 @@ namespace mOS << ")\t"; - auto location = event.getLocation(); - if (location.getLine() != 0) { + if (auto location = event.getLocation(); location.getLine() != 0) { mOS << "\t at " << location.getLine() << ":" @@ -170,7 +167,7 @@ namespace private: bool mPrintBv; - size_t mFuncEntries; + size_t mFuncEntries = 0; }; } // end anonymous namespace diff --git a/src/Trace/ViolationWitnessWriter.cpp b/src/Trace/ViolationWitnessWriter.cpp index 71a5f1d0..1ceb6673 100644 --- a/src/Trace/ViolationWitnessWriter.cpp +++ b/src/Trace/ViolationWitnessWriter.cpp @@ -20,8 +20,8 @@ #include "gazer/Trace/WitnessWriter.h" #include -#include -#include +#include +#include using namespace gazer; @@ -68,8 +68,12 @@ std::string ViolationWitnessWriter::SourceFileName{}; void ViolationWitnessWriter::createNode(bool violation) { mOS << "\n\n"; - if(mNodeCounter == 0) mOS << "\ttrue\n"; - if(violation) mOS << "\ttrue\n"; + if (mNodeCounter == 0) { + mOS << "\ttrue\n"; + } + if (violation){ + mOS << "\ttrue\n"; + } mOS << "\n"; mNodeCounter++; } @@ -96,18 +100,15 @@ void ViolationWitnessWriter::writeLocation(gazer::LocationInfo location) { void ViolationWitnessWriter::initializeWitness() { mNodeCounter = 0; - time_t rawtime; - struct tm * ptm; - time ( &rawtime ); - ptm = gmtime( &rawtime ); // UTC timestamp - std::stringstream timestamp; - timestamp << std::put_time(ptm, "%FT%T"); + + using namespace std::chrono; + llvm::sys::TimePoint timestamp = time_point_cast(system_clock::now()); mOS << schema; mOS << keys; mOS << graph_data; mOS << "" << mHash << "\n"; - mOS << "" << timestamp.str() << "\n"; + mOS << "" << llvm::formatv("{0:%FT%T}", timestamp) << "\n"; mOS << "" << SourceFileName << "\n"; createNode(); // entry node mInProgress = true; @@ -163,7 +164,7 @@ void ViolationWitnessWriter::visit(FunctionCallEvent& event) mOS << ";\n"; mOS << "\t" << event.getFunctionName() << "\n"; closeEdge(); - } else if (!retexpr->getKind() == Expr::Undef) { + } else if (retexpr->getKind() != Expr::Undef) { createNode(); openEdge(); writeLocation(event.getLocation()); diff --git a/tools/gazer-bmc/Verifier/BmcTrace.cpp b/src/Verifier/BmcTrace.cpp similarity index 54% rename from tools/gazer-bmc/Verifier/BmcTrace.cpp rename to src/Verifier/BmcTrace.cpp index 2b2fdaec..d82217a4 100644 --- a/tools/gazer-bmc/Verifier/BmcTrace.cpp +++ b/src/Verifier/BmcTrace.cpp @@ -46,7 +46,7 @@ void bmc::cex_iterator::advance() auto edge = std::find_if( current->incoming_begin(), current->incoming_end(), - [source](Transition* e) { return e->getSource() == source; } + [source](const Transition* e) { return e->getSource() == source; } ); assert(edge != current->incoming_end() @@ -55,66 +55,75 @@ void bmc::cex_iterator::advance() mState = { source, *edge }; } -// FIXME: Move this to BoundedModelChecker.cpp? -std::unique_ptr BoundedModelCheckerImpl::createFailResult() +std::unique_ptr BoundedModelCheckerImpl::constructTrace(Model& model) { - auto model = mSolver->getModel(); - - if (mSettings.dumpSolverModel) { - model->dump(llvm::errs()); + if (!mSettings.trace) { + return std::make_unique(std::vector>()); } - std::unique_ptr trace; - if (mSettings.trace) { - std::vector states; - std::vector> actions; + std::vector states; + std::vector> actions; - bmc::BmcCex cex{mError, *mRoot, *model, mPredecessors}; - for (auto state : cex) { - Location* loc = state.getLocation(); - Transition* edge = state.getOutgoingTransition(); + bmc::BmcCex cex{mError, *mRoot, model, mPredecessors}; + for (auto state : cex) { + Location* loc = state.getLocation(); + Transition* edge = state.getOutgoingTransition(); - Location* origLoc = mInlinedLocations.lookup(loc); + Location* origLoc = mInlinedLocations.lookup(loc); + if (origLoc == nullptr || edge == nullptr) { + // All meaningful locations should be inlined - we are either in the clone of the main + // automaton or in an inlined procedure. However, the algorithm may insert some auxiliary + // locations, we'll have to skip those. + continue; + } + + states.push_back(origLoc); + if (LLVM_UNLIKELY(mInlinedLocations.count(edge->getTarget()) == 0)) { + // In rare cases, it is possible that the edge points to an auxiliary location + continue; + } - states.push_back(origLoc != nullptr ? origLoc : loc); + auto* assignEdge = llvm::dyn_cast(edge); + assert(assignEdge != nullptr && "BMC traces must contain only assign transitions!"); - if (edge == nullptr) { - continue; + std::vector traceAction; + for (const VariableAssignment& assignment : *assignEdge) { + Variable* variable = assignment.getVariable(); + Variable* origVariable = mInlinedVariables.lookup(assignment.getVariable()); + if (origVariable == nullptr) { + // This variable was not inlined, just use the original one. + origVariable = variable; } - auto assignEdge = llvm::dyn_cast(edge); - assert(assignEdge != nullptr && "BMC traces must contain only assign transitions!"); - - std::vector traceAction; - for (const VariableAssignment& assignment : *assignEdge) { - Variable* variable = assignment.getVariable(); - Variable* origVariable = mInlinedVariables.lookup(assignment.getVariable()); - if (origVariable == nullptr) { - // This variable was not inlined, just use the original one. - origVariable = variable; - } - - ExprRef value; - if (auto lit = model->evaluate(assignment.getVariable()->getRefExpr())) { - value = lit; - } else { - value = UndefExpr::Get(variable->getType()); - } - - traceAction.emplace_back(origVariable, value); + ExprRef value; + if (auto lit = model.evaluate(assignment.getVariable()->getRefExpr())) { + value = lit; + } else { + value = UndefExpr::Get(variable->getType()); } - actions.push_back(traceAction); + traceAction.emplace_back(origVariable, value); } - std::reverse(states.begin(), states.end()); - std::reverse(actions.begin(), actions.end()); + actions.push_back(traceAction); + } + + std::reverse(states.begin(), states.end()); + std::reverse(actions.begin(), actions.end()); - trace = mTraceBuilder.build(states, actions); - } else { - trace = std::make_unique(std::vector>()); + return mTraceBuilder.build(states, actions); +} + +std::unique_ptr BoundedModelCheckerImpl::createFailResult() +{ + auto model = mSolver->getModel(); + + if (mSettings.dumpSolverModel) { + model->dump(llvm::errs()); } + std::unique_ptr trace = this->constructTrace(*model); + ExprRef errorExpr = model->evaluate(mErrorFieldVariable->getRefExpr()); assert(!errorExpr->isUndef() && "The error field must be present in the model as a literal expression!"); diff --git a/src/Verifier/BoundedModelChecker.cpp b/src/Verifier/BoundedModelChecker.cpp new file mode 100644 index 00000000..ccdd7a96 --- /dev/null +++ b/src/Verifier/BoundedModelChecker.cpp @@ -0,0 +1,613 @@ +//==-------------------------------------------------------------*- C++ -*--==// +// +// Copyright 2019 Contributors to the Gazer project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +#include "BoundedModelCheckerImpl.h" + +#include "gazer/Core/Expr/ExprRewrite.h" +#include "gazer/Core/Expr/ExprUtils.h" +#include "gazer/Automaton/CfaUtils.h" +#include "gazer/Automaton/CfaTransforms.h" + +#include "gazer/Support/Stopwatch.h" +#include "gazer/Support/Warnings.h" + +#include +#include + +#include +#include + +#include + +#define DEBUG_TYPE "BoundedModelChecker" + +using namespace gazer; + +namespace +{ + const llvm::cl::opt NoDomPush("bmc-no-dom-push", llvm::cl::Hidden); + const llvm::cl::opt NoPostDomPush("bmc-no-postdom-push", llvm::cl::Hidden); +} // end anonymous namespace + +auto BoundedModelChecker::check(AutomataSystem& system, CfaTraceBuilder& traceBuilder) + -> std::unique_ptr +{ + std::unique_ptr builder; + + if (mSettings.simplifyExpr) { + builder = CreateFoldingExprBuilder(system.getContext()); + } else { + builder = CreateExprBuilder(system.getContext()); + } + BoundedModelCheckerImpl impl{system, *builder, mSolverFactory, traceBuilder, mSettings}; + + auto result = impl.check(); + + impl.printStats(llvm::outs()); + + return result; +} + +BoundedModelCheckerImpl::BoundedModelCheckerImpl( + AutomataSystem& system, + ExprBuilder& builder, + SolverFactory& solverFactory, + CfaTraceBuilder& traceBuilder, + BmcSettings settings +) : mSystem(system), + mExprBuilder(builder), + mSolver(solverFactory.createSolver(system.getContext())), + mTraceBuilder(traceBuilder), + mSettings(settings) +{ + Cfa* main = mSystem.getMainAutomaton(); + assert(main != nullptr && "The main automaton must exist!"); + + CfaCloneResult cloneResult = CloneCfa(*main); + mRoot = cloneResult.clonedCfa; + for (auto& [oldLoc, newLoc] : cloneResult.locToLocMap) { + mInlinedLocations[newLoc] = oldLoc; + } + for (auto& [oldVar, newVar] : cloneResult.varToVarMap) { + mInlinedVariables[newVar] = oldVar; + } +} + +void BoundedModelCheckerImpl::createTopologicalSorts() +{ + for (Cfa& cfa : mSystem) { + mTopoSortMap.try_emplace(&cfa, cfa); + } + + mTopo = CfaTopoSort(*mRoot); +} + +auto BoundedModelCheckerImpl::initializeErrorField() -> bool +{ + // Set the verification goal - a single error location. + llvm::SmallVector errors; + Type* errorFieldType = nullptr; + for (const auto& [location, errExpr] : mRoot->errors()) { + errors.push_back(location); + errorFieldType = &errExpr->getType(); + } + + // Try to find the error field type from using another CFA. + if (errorFieldType == nullptr) { + for (const Cfa& cfa : mSystem) { + if (cfa.error_begin() != cfa.error_end()) { + auto errExpr = cfa.error_begin()->second; + errorFieldType = &errExpr->getType(); + } + } + } + + // If the error field type is still not known, there are no errors present in the system, + // the program is safe by definition + if (errorFieldType == nullptr) { + return false; + } + + mError = mRoot->createErrorLocation(); + mErrorFieldVariable = mRoot->createLocal("__error_field", *errorFieldType); + + if (errors.empty()) { + // If there are no error locations in the main automaton, they might still exist in a called CFA. + // A dummy error location will be used as a goal. + mRoot->createAssignTransition(mRoot->getEntry(), mError, mExprBuilder.False(), { + VariableAssignment{ mErrorFieldVariable, mExprBuilder.BvLit(0, 16) } + }); + } else { + // The error location which will be directly reachable from already existing error locations. + for (Location* err : errors) { + mRoot->createAssignTransition(err, mError, mExprBuilder.True(), { + VariableAssignment { mErrorFieldVariable, mRoot->getErrorFieldExpr(err) } + }); + } + } + + // Create a dummy transition from the error location into the exit location. + mRoot->createAssignTransition(mError, mRoot->getExit(), mExprBuilder.False()); + + return true; +} + +void BoundedModelCheckerImpl::initializeCallApproximations() +{ + for (Transition* edge : mRoot->edges()) { + if (auto* call = llvm::dyn_cast(edge)) { + mCalls[call].overApprox = mExprBuilder.False(); + mCalls[call].callChain.push_back(call->getCalledAutomaton()); + } + } +} + +void BoundedModelCheckerImpl::removeIrrelevantLocations() +{ + // The CFA construction algorithm must guarantee that the automata are + // connected graphs where every location is reachable from the entry, + // and all locations have a path (possibly annotated with an 'assume false') + // to the exit location. + llvm::df_iterator_default_set visited; + auto begin = llvm::idf_ext_begin(mError, visited); + auto end = llvm::idf_ext_end(mError, visited); + + // Do the DFS visit + while (begin != end) { + ++begin; + } + + // Disconnect all unvisited locations, minus the exit location. + visited.insert(mRoot->getExit()); + for (Location* loc : mRoot->nodes()) { + if (visited.count(loc) == 0) { + mRoot->disconnectNode(loc); + } + } + + mRoot->clearDisconnectedElements(); +} + +void BoundedModelCheckerImpl::performEagerUnrolling() +{ + for (size_t bound = 0; bound < mSettings.eagerUnroll; ++bound) { + llvm::outs() << "Eager iteration " << bound << "\n"; + mOpenCalls.clear(); + for (auto& [call, info] : mCalls) { + if (info.getCost() <= bound) { + mOpenCalls.insert(call); + } + } + + llvm::SmallVector callsToInline; + for (CallTransition* call : mOpenCalls) { + inlineCallIntoRoot(call, "_call" + llvm::Twine(mTmp++), callsToInline); + mCalls.erase(call); + } + } +} + +auto BoundedModelCheckerImpl::check() -> std::unique_ptr +{ + // Initialize error field + bool hasErrorLocation = this->initializeErrorField(); + if (!hasErrorLocation) { + llvm::outs() << "No error location is present or it was discarded by the frontend.\n"; + return VerificationResult::CreateSuccess(); + } + + // Do some basic pre-processing step: remove all locations from which the error + // location is not reachable. + this->removeIrrelevantLocations(); + + // Create the topological sorts + this->createTopologicalSorts(); + + // Insert initial call approximations. + this->initializeCallApproximations(); + + // See if -debug-dump-cfa is enabled. + this->dumpAutomataIfRequested(); + + // Initialize the path condition calculator + mPathConditions = std::make_unique( + mTopo, + mExprBuilder, + [this](CallTransition* call) { + return mCalls[call].overApprox; + }, + [this](Location* l, const ExprPtr& e) { + mPredecessors.insert(l, e); + } + ); + + // Do eager unrolling, if requested + if (mSettings.eagerUnroll >= mSettings.maxBound) { + emit_error("maximum bound (%d) must be larger than eager unrolling bound (%d)", mSettings.maxBound, mSettings.eagerUnroll); + return VerificationResult::CreateUnknown(); + } + + this->performEagerUnrolling(); + + mStats.NumBeginLocs = mRoot->getNumLocations(); + mStats.NumBeginLocals = mRoot->getNumLocals(); + + mTopLoc = mRoot->getEntry(); + mBottomLoc = mError; + mSkipUnderApprox = false; + + // Let's do some verification. + for (unsigned bound = mSettings.eagerUnroll + 1; bound <= mSettings.maxBound; ++bound) { + llvm::outs() << "Iteration " << bound << "\n"; + + while (true) { + unsigned numUnhandledCallSites = 0; + ExprPtr formula; + Solver::SolverStatus status; + + // Start with an under-approximation step: remove all calls from the automaton + // (by replacing them with 'assume false') and check if the error location is + // reachable. If it is reachable, we have found a true counterexample. + status = this->underApproximationStep(); + if (status == Solver::SAT) { + llvm::outs() << " Under-approximated formula is SAT.\n"; + return this->createFailResult(); + } + mSkipUnderApprox = false; + + // If the under-approximated formula was UNSAT, there is no feasible path from start + // to the error location which does not involve a call. Find the lowest common dominator + // of all calls, and set is as the start location. Similarly, we can calculate the + // highest common post-dominator for the error location of all calls to update the + // target state. These nodes are the lowest common ancestors (LCA) of the calls in + // the (post-)dominator trees. + auto lca = this->findCommonCallAncestor(mTopLoc, mBottomLoc); + + this->push(); + if (lca.first != nullptr) { + LLVM_DEBUG(llvm::dbgs() << "Found LCA, " << lca.first->getId() << ".\n"); + assert(lca.second != nullptr); + + mSolver->add(mPathConditions->encode(mTopLoc, lca.first)); + mSolver->add(mPathConditions->encode(lca.second, mBottomLoc)); + + // Run the solver and check whether top and bottom are consistent -- if not, + // we can return that the program is safe as all possible error paths will + // encode these program parts. + status = this->runSolver(); + + if (status == Solver::UNSAT) { + llvm::outs() << " Start and target points are inconsistent, no errors are reachable.\n"; + return VerificationResult::CreateSuccess(); + } + } else { + LLVM_DEBUG(llvm::dbgs() << "No calls present, LCA is " << mTopLoc->getId() << ".\n"); + lca = { mTopLoc, mBottomLoc }; + } + + // Now try to over-approximate. + llvm::outs() << " Over-approximating.\n"; + + // Find all calls which are to be over-approximated: that is, calls that have a cost + // less than the current bound + mOpenCalls.clear(); + numUnhandledCallSites += collectOpenCalls(bound); + + this->push(); + + llvm::outs() << " Calculating verification condition...\n"; + formula = mPathConditions->encode(lca.first, lca.second); + this->dumpFormulaIfRequested(formula); + + llvm::outs() << " Transforming formula...\n"; + mSolver->add(formula); + this->dumpSolverIfRequested(); + + status = this->runSolver(); + + if (status == Solver::SAT) { + llvm::outs() << " Over-approximated formula is SAT.\n"; + llvm::outs() << " Checking counterexample...\n"; + + // We have a counterexample, but it may be spurious. + auto model = mSolver->getModel(); + this->inlineOpenCalls(*model, bound); + + this->pop(); + mTopLoc = lca.first; + mBottomLoc = lca.second; + } else if (status == Solver::UNSAT) { + llvm::outs() << " Over-approximated formula is UNSAT.\n"; + + if (auto boundResult = this->checkBound(numUnhandledCallSites, bound)) { + return boundResult; + } + + // Try with an increased bound. + llvm::outs() << " Open call sites still present. Increasing bound.\n"; + this->pop(); + mTopLoc = lca.first; + mBottomLoc = lca.second; + + // Skip redundant under-approximation step - all calls in the system are + // under-approximated with 'False', which will not change when we jump + // back to the under-approximation step. + mSkipUnderApprox = true; + break; + } else { + llvm_unreachable("Unknown solver status."); + } + } + } + + return VerificationResult::CreateBoundReached(); +} + +unsigned BoundedModelCheckerImpl::collectOpenCalls(size_t bound) +{ + unsigned unhandledCalls = 0; + + for (auto& [call, info] : mCalls) { + if (info.getCost() > bound) { + LLVM_DEBUG( + llvm::dbgs() << " Skipping " << *call + << ": inline cost is greater than bound (" << + info.getCost() << " > " << bound << ").\n" + ); + info.overApprox = mExprBuilder.False(); + ++unhandledCalls; + continue; + } + + info.overApprox = mExprBuilder.True(); + mOpenCalls.insert(call); + } + + return unhandledCalls; +} + +auto BoundedModelCheckerImpl::underApproximationStep() -> Solver::SolverStatus +{ + if (mSkipUnderApprox) { + return Solver::UNKNOWN; + } + + llvm::outs() << " Under-approximating.\n"; + + for (auto& [_, callInfo] : mCalls) { + callInfo.overApprox = mExprBuilder.False(); + } + + ExprPtr formula = mPathConditions->encode(mTopLoc, mBottomLoc); + + this->push(); + llvm::outs() << " Transforming formula...\n"; + if (mSettings.dumpFormula) { + formula->print(llvm::errs()); + } + + mSolver->add(formula); + + if (mSettings.dumpSolver) { + mSolver->dump(llvm::errs()); + } + + auto status = this->runSolver(); + + this->pop(); + return status; +} + +void BoundedModelCheckerImpl::inlineOpenCalls(Model& model, size_t bound) +{ + llvm::SmallVector callsToInline; + this->findOpenCallsInCex(model, callsToInline); + + llvm::outs() << " Inlining calls...\n"; + while (!callsToInline.empty()) { + CallTransition* call = callsToInline.pop_back_val(); + llvm::outs() << " Inlining " << call->getSource()->getId() << " --> " + << call->getTarget()->getId() << " " + << call->getCalledAutomaton()->getName() << "\n"; + mStats.NumInlined++; + + llvm::SmallVector newCalls; + this->inlineCallIntoRoot( + call, "_call" + llvm::Twine(mTmp++), newCalls + ); + mCalls.erase(call); + mOpenCalls.erase(call); + + for (CallTransition* newCall : newCalls) { + if (mCalls[newCall].getCost() <= bound) { + callsToInline.push_back(newCall); + } + } + } + + mRoot->clearDisconnectedElements(); + + mStats.NumEndLocs = mRoot->getNumLocations(); + mStats.NumEndLocals = mRoot->getNumLocals(); + if (mSettings.debugDumpCfa) { + mRoot->view(); + } +} + +auto BoundedModelCheckerImpl::findCommonCallAncestor(Location* fwd, Location* bwd) + -> std::pair +{ + // Calculate the closest common (post-)dominator for all call nodes + std::vector targets; + std::transform(mCalls.begin(), mCalls.end(), std::back_inserter(targets), [](auto& pair) { + return pair.first; + }); + + Location* dom; + Location* pdom; + + if (!NoDomPush) { + dom = findLowestCommonDominator(targets, mTopo, fwd); + } else { + dom = fwd; + } + + if (!NoPostDomPush) { + pdom = findHighestCommonPostDominator(targets, mTopo, bwd); + } else { + pdom = bwd; + } + + return { dom, pdom }; +} + +void BoundedModelCheckerImpl::findOpenCallsInCex(Model& model, llvm::SmallVectorImpl& callsInCex) +{ + auto cex = bmc::BmcCex{mError, *mRoot, model, mPredecessors}; + + for (auto state : cex) { + auto* call = llvm::dyn_cast_or_null(state.getOutgoingTransition()); + if (call != nullptr && mOpenCalls.count(call) != 0) { + callsInCex.push_back(call); + if (callsInCex.size() == mOpenCalls.size()) { + // All possible calls were encountered, no point in iterating further. + break; + } + } + } +} + +void BoundedModelCheckerImpl::inlineCallIntoRoot( + CallTransition* call, + const llvm::Twine& suffix, + llvm::SmallVectorImpl& newCalls +) { + LLVM_DEBUG( + llvm::dbgs() << " Inlining call " << *call + << " edge " << call->getSource()->getId() + << " --> " << call->getTarget()->getId() + << "\n"; + ); + + const CallInfo& info = mCalls[call]; + size_t topoIdx = mTopo.indexOf(call->getTarget()); + + auto oldTopo = mTopoSortMap.find(call->getCalledAutomaton()); + assert(oldTopo != mTopoSortMap.end()); + + CfaInlineResult result = InlineCall(call, mError, mErrorFieldVariable, suffix.str()); + + // Add the new locations to the topological sort. + // As every inlined location should come between the source and target of the original call transition, + // we will insert them there in the topo sort. + auto getInlinedLocation = [&result](Location* loc) { + return result.locToLocMap[loc]; + }; + mTopo.insert(topoIdx, + llvm::map_iterator(oldTopo->second.begin(), getInlinedLocation), + llvm::map_iterator(oldTopo->second.end(), getInlinedLocation)); + + // Add information for the newly inserted calls + for (CallTransition* callEdge : result.newCalls) { + mCalls[callEdge].callChain = info.callChain; + mCalls[callEdge].callChain.push_back(callEdge->getCalledAutomaton()); + mCalls[callEdge].overApprox = mExprBuilder.False(); + newCalls.push_back(callEdge); + } + + mInlinedLocations.insert(result.inlinedLocations.begin(), result.inlinedLocations.end()); + mInlinedVariables.insert(result.inlinedVariables.begin(), result.inlinedVariables.end()); +} + +auto BoundedModelCheckerImpl::checkBound(unsigned openCallSites, unsigned bound) -> std::unique_ptr +{ + if (openCallSites == 0) { + // If we have no unhandled call sites, the program is guaranteed to be safe at this point. + mStats.NumEndLocs = mRoot->getNumLocations(); + mStats.NumEndLocals = mRoot->getNumLocals(); + + return VerificationResult::CreateSuccess(); + } + + if (bound == mSettings.maxBound) { + // The maximum bound was reached. + llvm::outs() << "Maximum bound is reached.\n"; + + mStats.NumEndLocs = mRoot->getNumLocations(); + mStats.NumEndLocals = mRoot->getNumLocals(); + + return VerificationResult::CreateBoundReached(); + } + + return nullptr; +} + +auto BoundedModelCheckerImpl::runSolver() -> Solver::SolverStatus +{ + llvm::outs() << " Running solver...\n"; + mTimer.start(); + auto status = mSolver->run(); + mTimer.stop(); + + llvm::outs() << " Elapsed time: "; + mTimer.format(llvm::outs(), "s"); + llvm::outs() << "\n"; + mStats.SolverTime += mTimer.elapsed(); + + return status; +} + +void BoundedModelCheckerImpl::printStats(llvm::raw_ostream& os) const +{ + os << "--------- Statistics ---------\n"; + os << "Total solver time: "; + llvm::format_provider::format(mStats.SolverTime, os, "s"); + os << "\n"; + os << "Number of inlined procedures: " << mStats.NumInlined << "\n"; + os << "Number of locations on start: " << mStats.NumBeginLocs << "\n"; + os << "Number of locations on finish: " << mStats.NumEndLocs << "\n"; + os << "Number of variables on start: " << mStats.NumBeginLocals << "\n"; + os << "Number of variables on finish: " << mStats.NumEndLocals << "\n"; + os << "------------------------------\n"; + if (mSettings.printSolverStats) { + mSolver->printStats(os); + } + os << "\n"; +} + +void BoundedModelCheckerImpl::dumpFormulaIfRequested(const ExprPtr& formula) const +{ + if (mSettings.dumpFormula) { + formula->print(llvm::errs()); + } +} + +void BoundedModelCheckerImpl::dumpSolverIfRequested() const +{ + if (mSettings.dumpSolver) { + mSolver->dump(llvm::errs()); + } +} + +void BoundedModelCheckerImpl::dumpAutomataIfRequested() const +{ + if (mSettings.debugDumpCfa) { + for (const Cfa& cfa : mSystem) { + cfa.view(); + } + } +} diff --git a/tools/gazer-bmc/Verifier/BoundedModelCheckerImpl.h b/src/Verifier/BoundedModelCheckerImpl.h similarity index 79% rename from tools/gazer-bmc/Verifier/BoundedModelCheckerImpl.h rename to src/Verifier/BoundedModelCheckerImpl.h index 1e583ec2..582a5680 100644 --- a/tools/gazer-bmc/Verifier/BoundedModelCheckerImpl.h +++ b/src/Verifier/BoundedModelCheckerImpl.h @@ -18,22 +18,22 @@ #ifndef GAZER_SRC_VERIFIER_BOUNDEDMODELCHECKERIMPL_H #define GAZER_SRC_VERIFIER_BOUNDEDMODELCHECKERIMPL_H -#include "gazer/Verifier/BoundedModelChecker.h" -#include "gazer/Core/Expr/ExprEvaluator.h" +#include "gazer/ADT/ScopedCache.h" +#include "gazer/Automaton/Cfa.h" #include "gazer/Core/Expr/ExprBuilder.h" -#include "gazer/Core/Solver/Solver.h" +#include "gazer/Core/Expr/ExprEvaluator.h" #include "gazer/Core/Solver/Model.h" -#include "gazer/Automaton/Cfa.h" -#include "gazer/Trace/Trace.h" - +#include "gazer/Core/Solver/Solver.h" #include "gazer/Support/Stopwatch.h" -#include "gazer/ADT/ScopedCache.h" +#include "gazer/Trace/Trace.h" +#include "gazer/Verifier/BoundedModelChecker.h" -#include #include #include +#include #include +#include namespace gazer { @@ -45,8 +45,8 @@ namespace bmc class CexState { public: - CexState(Location* location, Transition* incoming) - : mLocation(location), mOutgoing(incoming) + CexState(Location* location, Transition* outgoingTransition) + : mLocation(location), mOutgoing(outgoingTransition) {} bool operator==(const CexState& rhs) const { @@ -86,7 +86,6 @@ namespace bmc private: void advance(); - private: BmcCex& mCex; CexState mState; }; @@ -110,7 +109,7 @@ namespace bmc ExprEvaluator& mEval; PredecessorMapT& mPredecessors; }; -} +} // namespace bmc class BoundedModelCheckerImpl { @@ -120,18 +119,18 @@ class BoundedModelCheckerImpl std::vector callChain; unsigned getCost() const { - return std::count(callChain.begin(), callChain.end(), callChain.back()); + return llvm::count(callChain, callChain.back()); } }; public: struct Stats { std::chrono::milliseconds SolverTime{0}; - unsigned NumInlined = 0; - unsigned NumBeginLocs = 0; - unsigned NumEndLocs = 0; - unsigned NumBeginLocals = 0; - unsigned NumEndLocals = 0; + size_t NumInlined = 0; + size_t NumBeginLocs = 0; + size_t NumEndLocs = 0; + size_t NumBeginLocals = 0; + size_t NumEndLocals = 0; }; BoundedModelCheckerImpl( @@ -144,29 +143,38 @@ class BoundedModelCheckerImpl std::unique_ptr check(); - void printStats(llvm::raw_ostream& os); + void printStats(llvm::raw_ostream& os) const; private: void createTopologicalSorts(); - bool initializeErrorField(); void removeIrrelevantLocations(); + void initializeCallApproximations(); + void performEagerUnrolling(); + + bool initializeErrorField(); void inlineCallIntoRoot( CallTransition* call, - llvm::DenseMap& vmap, const llvm::Twine& suffix, llvm::SmallVectorImpl& newCalls ); - + + Solver::SolverStatus underApproximationStep(); + /// Finds the closest common (post-)dominating node for all call transitions. /// If no call transitions are present in the CFA, this function returns nullptr. std::pair findCommonCallAncestor(Location* fwd, Location* bwd); - std::function createLocNumberFunc(); + void inlineOpenCalls(Model& model, size_t bound); void findOpenCallsInCex(Model& model, llvm::SmallVectorImpl& callsInCex); + unsigned collectOpenCalls(size_t bound); + + std::unique_ptr checkBound(unsigned openCallSites, unsigned bound); + std::unique_ptr createFailResult(); + std::unique_ptr constructTrace(Model& model); void push() { mSolver->push(); @@ -180,7 +188,13 @@ class BoundedModelCheckerImpl Solver::SolverStatus runSolver(); -private: + // Logging + //==--------------------------------------------------------------------==// + void dumpFormulaIfRequested(const ExprPtr& formula) const; + void dumpSolverIfRequested() const; + void dumpAutomataIfRequested() const; + + // Fields AutomataSystem& mSystem; ExprBuilder& mExprBuilder; std::unique_ptr mSolver; @@ -188,32 +202,30 @@ class BoundedModelCheckerImpl BmcSettings mSettings; Cfa* mRoot; - std::vector mTopo; + CfaTopoSort mTopo; Location* mError = nullptr; + Location* mTopLoc = nullptr; + Location* mBottomLoc = nullptr; - llvm::DenseMap mLocNumbers; llvm::DenseSet mOpenCalls; std::unordered_map mCalls; - std::unordered_map> mTopoSortMap; + std::unordered_map mTopoSortMap; + std::unique_ptr mPathConditions; bmc::PredecessorMapT mPredecessors; llvm::DenseMap mInlinedLocations; llvm::DenseMap mInlinedVariables; size_t mTmp = 0; + bool mSkipUnderApprox = false; Stats mStats; Stopwatch<> mTimer; Variable* mErrorFieldVariable = nullptr; }; -std::unique_ptr buildBmcTrace( - const std::vector& states, - const std::vector>& actions -); - } // end namespace gazer #endif diff --git a/tools/gazer-bmc/Verifier/CMakeLists.txt b/src/Verifier/CMakeLists.txt similarity index 72% rename from tools/gazer-bmc/Verifier/CMakeLists.txt rename to src/Verifier/CMakeLists.txt index 6d1109a4..76c91746 100644 --- a/tools/gazer-bmc/Verifier/CMakeLists.txt +++ b/src/Verifier/CMakeLists.txt @@ -1,6 +1,6 @@ set(SOURCE_FILES - BoundedModelChecker.cpp - BmcTrace.cpp + BoundedModelChecker.cpp + BmcTrace.cpp ) add_library(GazerVerifier SHARED ${SOURCE_FILES}) diff --git a/test/portfolio/lit.local.cfg b/test/portfolio/lit.local.cfg index 632fa995..2d823a93 100644 --- a/test/portfolio/lit.local.cfg +++ b/test/portfolio/lit.local.cfg @@ -11,3 +11,7 @@ config.environment['GAZER_TOOLS_DIR'] = gazer_tools_dir scripts_path = gazer_tools_dir.rsplit("/gazer/", 1)[0] + "/gazer/scripts/portfolio/Portfolio.pl" config.substitutions.append(('%portfolio', scripts_path)) + +# TODO (sallaigy): I could not make it work, even after installing +# all the packages it needs. +config.unsupported = True diff --git a/test/theta/cfa/Expected/counter.theta b/test/theta/cfa/Expected/counter.theta index 139f30b1..26194e89 100644 --- a/test/theta/cfa/Expected/counter.theta +++ b/test/theta/cfa/Expected/counter.theta @@ -1,6 +1,7 @@ main process __gazer_main_process { var main_RET_VAL : int - var main_tmp : int + var main_i : int + var main_error_phi : int var main___gazer_error_field : int init loc loc0 final loc loc1 @@ -15,15 +16,16 @@ main process __gazer_main_process { } loc2 -> loc3 { - havoc main_tmp + havoc main_i } loc3 -> loc6 { - assume (not (1 <= main_tmp)) + assume ((if (main_i <= 0) then 0 else main_i) = 0) + main_error_phi := 2 } loc3 -> loc4 { - assume (not (not (1 <= main_tmp))) + assume (not ((if (main_i <= 0) then 0 else main_i) = 0)) } loc4 -> loc5 { @@ -41,7 +43,7 @@ main process __gazer_main_process { } loc7 -> loc8 { - main___gazer_error_field := 2 + main___gazer_error_field := main_error_phi } } diff --git a/test/theta/cfa/Output/counter.c.tmp b/test/theta/cfa/Output/counter.c.tmp index 139f30b1..26194e89 100644 --- a/test/theta/cfa/Output/counter.c.tmp +++ b/test/theta/cfa/Output/counter.c.tmp @@ -1,6 +1,7 @@ main process __gazer_main_process { var main_RET_VAL : int - var main_tmp : int + var main_i : int + var main_error_phi : int var main___gazer_error_field : int init loc loc0 final loc loc1 @@ -15,15 +16,16 @@ main process __gazer_main_process { } loc2 -> loc3 { - havoc main_tmp + havoc main_i } loc3 -> loc6 { - assume (not (1 <= main_tmp)) + assume ((if (main_i <= 0) then 0 else main_i) = 0) + main_error_phi := 2 } loc3 -> loc4 { - assume (not (not (1 <= main_tmp))) + assume (not ((if (main_i <= 0) then 0 else main_i) = 0)) } loc4 -> loc5 { @@ -41,7 +43,7 @@ main process __gazer_main_process { } loc7 -> loc8 { - main___gazer_error_field := 2 + main___gazer_error_field := main_error_phi } } diff --git a/tools/gazer-bmc/CMakeLists.txt b/tools/gazer-bmc/CMakeLists.txt index 5c9fa34f..6f000213 100644 --- a/tools/gazer-bmc/CMakeLists.txt +++ b/tools/gazer-bmc/CMakeLists.txt @@ -1,5 +1,3 @@ -add_subdirectory(Verifier) - set(SOURCE_FILES gazer-bmc.cpp ) diff --git a/tools/gazer-bmc/Verifier/BoundedModelChecker.cpp b/tools/gazer-bmc/Verifier/BoundedModelChecker.cpp deleted file mode 100644 index 9d3b2d3b..00000000 --- a/tools/gazer-bmc/Verifier/BoundedModelChecker.cpp +++ /dev/null @@ -1,707 +0,0 @@ -//==-------------------------------------------------------------*- C++ -*--==// -// -// Copyright 2019 Contributors to the Gazer project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -#include "BoundedModelCheckerImpl.h" - -#include "gazer/Core/Expr/ExprRewrite.h" -#include "gazer/Core/Expr/ExprUtils.h" -#include "gazer/Automaton/CfaUtils.h" - -#include "gazer/Support/Stopwatch.h" - -#include -#include -#include - -#include -#include - -#include - -#define DEBUG_TYPE "BoundedModelChecker" - -using namespace gazer; - -namespace -{ - llvm::cl::opt NoDomPush("bmc-no-dom-push", llvm::cl::Hidden); - llvm::cl::opt NoPostDomPush("bmc-no-postdom-push", llvm::cl::Hidden); -} // end anonymous namespace - -auto BoundedModelChecker::check(AutomataSystem& system, CfaTraceBuilder& traceBuilder) - -> std::unique_ptr -{ - std::unique_ptr builder; - - if (mSettings.simplifyExpr) { - builder = CreateFoldingExprBuilder(system.getContext()); - } else { - builder = CreateExprBuilder(system.getContext()); - } - BoundedModelCheckerImpl impl{system, *builder, mSolverFactory, traceBuilder, mSettings}; - - auto result = impl.check(); - - impl.printStats(llvm::outs()); - - return result; -} - -BoundedModelCheckerImpl::BoundedModelCheckerImpl( - AutomataSystem& system, - ExprBuilder& builder, - SolverFactory& solverFactory, - CfaTraceBuilder& traceBuilder, - BmcSettings settings -) : mSystem(system), - mExprBuilder(builder), - mSolver(solverFactory.createSolver(system.getContext())), - mTraceBuilder(traceBuilder), - mSettings(settings) -{ - // TODO: Clone the main automaton instead of modifying the original. - mRoot = mSystem.getMainAutomaton(); - assert(mRoot != nullptr && "The main automaton must exist!"); -} - -void BoundedModelCheckerImpl::createTopologicalSorts() -{ - for (Cfa& cfa : mSystem) { - auto& topoVec = mTopoSortMap[&cfa]; - createTopologicalSort(cfa, topoVec); - } - - auto& mainTopo = mTopoSortMap[mRoot]; - mTopo.insert(mTopo.end(), mainTopo.begin(), mainTopo.end()); - - for (size_t i = 0; i < mTopo.size(); ++i) { - mLocNumbers[mTopo[i]] = i; - } -} - -auto BoundedModelCheckerImpl::initializeErrorField() -> bool -{ - // Set the verification goal - a single error location. - llvm::SmallVector errors; - Type* errorFieldType = nullptr; - for (Location* loc : mRoot->nodes()) { - if (loc->isError()) { - errors.push_back(loc); - errorFieldType = &mRoot->getErrorFieldExpr(loc)->getType(); - } - } - - if (errorFieldType == nullptr) { - // Try to find the error field type from using another CFA. - for (Cfa& cfa : mSystem) { - for (auto& [location, errExpr] : cfa.errors()) { - errorFieldType = &errExpr->getType(); - goto ERROR_TYPE_FOUND; - } - } - - // There are no error calls in the system, it is safe by definition. - return false; - } - -ERROR_TYPE_FOUND: - - mError = mRoot->createErrorLocation(); - mErrorFieldVariable = mRoot->createLocal("__error_field", *errorFieldType); - if (errors.empty()) { - // If there are no error locations in the main automaton, they might still exist in a called CFA. - // A dummy error location will be used as a goal. - mRoot->createAssignTransition(mRoot->getEntry(), mError, mExprBuilder.False(), { - VariableAssignment{ mErrorFieldVariable, mExprBuilder.BvLit(0, 16) } - }); - } else { - // The error location which will be directly reachable from already existing error locations. - for (Location* err : errors) { - mRoot->createAssignTransition(err, mError, mExprBuilder.True(), { - VariableAssignment { mErrorFieldVariable, mRoot->getErrorFieldExpr(err) } - }); - } - } - - // Create a dummy transition from the error location into the exit location. - mRoot->createAssignTransition(mError, mRoot->getExit(), mExprBuilder.False()); - - return true; -} - -void BoundedModelCheckerImpl::removeIrrelevantLocations() -{ - // The CFA construction algorithm must guarantee that the automata are - // connected graphs where every location is reachable from the entry, - // and all locations have a path (possibly annotated with an 'assume false') - // to the exit location. - llvm::df_iterator_default_set visited; - auto begin = llvm::idf_ext_begin(mError, visited); - auto end = llvm::idf_ext_end(mError, visited); - - // Do the DFS visit - while (begin != end) { - ++begin; - } - - // Disconnect all unvisited locations, minus the exit location. - visited.insert(mRoot->getExit()); - for (Location* loc : mRoot->nodes()) { - if (visited.count(loc) == 0) { - mRoot->disconnectNode(loc); - } - } - - mRoot->clearDisconnectedElements(); -} - -auto BoundedModelCheckerImpl::check() -> std::unique_ptr -{ - // Initialize error field - bool hasErrorLocation = this->initializeErrorField(); - if (!hasErrorLocation) { - llvm::outs() << "No error location is present or it was discarded by the frontend.\n"; - return VerificationResult::CreateSuccess(); - } - - // Do a pre-processing step: remove all locations from which the error - // location is not reachable. - this->removeIrrelevantLocations(); - - // Create the topological sorts - this->createTopologicalSorts(); - - // Insert initial call approximations. - for (Transition* edge : mRoot->edges()) { - if (auto call = llvm::dyn_cast(edge)) { - mCalls[call].overApprox = mExprBuilder.False(); - mCalls[call].callChain.push_back(call->getCalledAutomaton()); - } - } - - if (mSettings.debugDumpCfa) { - for (Cfa& cfa : mSystem) { cfa.view(); } - } - - // Initialize the path condition calculator - PathConditionCalculator pathConditions( - mTopo, mExprBuilder, - this->createLocNumberFunc(), - [this](CallTransition* call) -> ExprPtr { - return mCalls[call].overApprox; - }, - [this](Location* l, ExprPtr e) { - mPredecessors.insert(l, e); - } - ); - - // Do eager unrolling, if requested - if (mSettings.eagerUnroll > mSettings.maxBound) { - llvm::errs() << "ERROR: Eager unrolling bound is larger than maximum bound.\n"; - return VerificationResult::CreateUnknown(); - } - - unsigned tmp = 0; - for (size_t bound = 1; bound <= mSettings.eagerUnroll; ++bound) { - llvm::outs() << "Eager iteration " << bound << "\n"; - mOpenCalls.clear(); - for (auto& [call, info] : mCalls) { - if (info.getCost() <= bound) { - mOpenCalls.insert(call); - } - } - - llvm::SmallVector callsToInline; - for (CallTransition* call : mOpenCalls) { - inlineCallIntoRoot(call, mInlinedVariables, "_call" + llvm::Twine(tmp++), callsToInline); - mCalls.erase(call); - } - } - - mStats.NumBeginLocs = mRoot->getNumLocations(); - mStats.NumBeginLocals = mRoot->getNumLocals(); - - Location* top = mRoot->getEntry(); - Location* bottom = mError; - - bool skipUnderApprox = false; - - // Let's do some verification. - for (size_t bound = mSettings.eagerUnroll + 1; bound <= mSettings.maxBound; ++bound) { - llvm::outs() << "Iteration " << bound << "\n"; - - while (true) { - unsigned numUnhandledCallSites = 0; - ExprPtr formula; - Solver::SolverStatus status = Solver::UNKNOWN; - - if (!skipUnderApprox) { - llvm::outs() << " Under-approximating.\n"; - - for (auto& entry : mCalls) { - entry.second.overApprox = mExprBuilder.False(); - } - - formula = pathConditions.encode(top, bottom); - - this->push(); - llvm::outs() << " Transforming formula...\n"; - if (mSettings.dumpFormula) { - formula->print(llvm::errs()); - } - - mSolver->add(formula); - - if (mSettings.dumpSolver) { - mSolver->dump(llvm::errs()); - } - - status = this->runSolver(); - - if (status == Solver::SAT) { - llvm::outs() << " Under-approximated formula is SAT.\n"; - return this->createFailResult(); - } - - this->pop(); - } - - skipUnderApprox = false; - - // If the under-approximated formula was UNSAT, there is no feasible path from start - // to the error location which does not involve a call. Find the lowest common dominator - // of all calls, and set is as the start location. Similarly, we can calculate the - // highest common post-dominator for the error location of all calls to update the - // target state. These nodes are the lowest common ancestors (LCA) of the calls in - // the (post-)dominator trees. - llvm::outs() << " Attempting to set new starting and target points...\n"; - auto lca = this->findCommonCallAncestor(top, bottom); - - this->push(); - if (lca.first != nullptr) { - LLVM_DEBUG(llvm::dbgs() << "Found LCA, " << lca.first->getId() << ".\n"); - assert(lca.second != nullptr); - - mSolver->add(pathConditions.encode(top, lca.first)); - mSolver->add(pathConditions.encode(lca.second, bottom)); - - // Run the solver and check whether top and bottom are consistent -- if not, - // we can return that the program is safe as all possible error paths will - // encode these program parts. - status = this->runSolver(); - - if (status == Solver::UNSAT) { - llvm::outs() << " Start and target points are inconsitent, no errors are reachable.\n"; - return VerificationResult::CreateSuccess(); - } - - } else { - LLVM_DEBUG(llvm::dbgs() << "No calls present, LCA is " << top->getId() << ".\n"); - lca = { top, bottom }; - } - - // Now try to over-approximate. - llvm::outs() << " Over-approximating.\n"; - - mOpenCalls.clear(); - for (auto& [call, info] : mCalls) { - if (info.getCost() > bound) { - LLVM_DEBUG( - llvm::dbgs() << " Skipping " << *call - << ": inline cost is greater than bound (" << - info.getCost() << " > " << bound << ").\n" - ); - info.overApprox = mExprBuilder.False(); - ++numUnhandledCallSites; - continue; - } - - info.overApprox = mExprBuilder.True(); - mOpenCalls.insert(call); - } - - this->push(); - - llvm::outs() << " Calculating verification condition...\n"; - formula = pathConditions.encode(lca.first, lca.second); - if (mSettings.dumpFormula) { - formula->print(llvm::errs()); - } - - llvm::outs() << " Transforming formula...\n"; - mSolver->add(formula); - - if (mSettings.dumpSolver) { - mSolver->dump(llvm::errs()); - } - - status = this->runSolver(); - - if (status == Solver::SAT) { - llvm::outs() << " Over-approximated formula is SAT.\n"; - llvm::outs() << " Checking counterexample...\n"; - - // We have a counterexample, but it may be spurious. - auto model = mSolver->getModel(); - - llvm::SmallVector callsToInline; - this->findOpenCallsInCex(*model, callsToInline); - - llvm::outs() << " Inlining calls...\n"; - while (!callsToInline.empty()) { - CallTransition* call = callsToInline.pop_back_val(); - llvm::outs() << " Inlining " << call->getSource()->getId() << " --> " - << call->getTarget()->getId() << " " - << call->getCalledAutomaton()->getName() << "\n"; - mStats.NumInlined++; - - llvm::SmallVector newCalls; - this->inlineCallIntoRoot( - call, mInlinedVariables, "_call" + llvm::Twine(tmp++), newCalls - ); - mCalls.erase(call); - mOpenCalls.erase(call); - - for (CallTransition* newCall : newCalls) { - if (mCalls[newCall].getCost() <= bound) { - callsToInline.push_back(newCall); - } - } - } - - mRoot->clearDisconnectedElements(); - - mStats.NumEndLocs = mRoot->getNumLocations(); - mStats.NumEndLocals = mRoot->getNumLocals(); - if (mSettings.debugDumpCfa) { - mRoot->view(); - } - - this->pop(); - top = lca.first; - bottom = lca.second; - } else if (status == Solver::UNSAT) { - llvm::outs() << " Over-approximated formula is UNSAT.\n"; - if (numUnhandledCallSites == 0) { - // If we have no unhandled call sites, - // the program is guaranteed to be safe at this point. - mStats.NumEndLocs = mRoot->getNumLocations(); - mStats.NumEndLocals = mRoot->getNumLocals(); - - return VerificationResult::CreateSuccess(); - } - - if (bound == mSettings.maxBound) { - // The maximum bound was reached. - llvm::outs() << "Maximum bound is reached.\n"; - - mStats.NumEndLocs = mRoot->getNumLocations(); - mStats.NumEndLocals = mRoot->getNumLocals(); - - return VerificationResult::CreateBoundReached(); - } - - // Try with an increased bound. - llvm::outs() << " Open call sites still present. Increasing bound.\n"; - this->pop(); - top = lca.first; - bottom = lca.second; - - // Skip redundant under-approximation step - all calls in the system are - // under-approximated with 'False', which will not change when we jump - // back to the under-approximation step. - skipUnderApprox = true; - break; - } else { - llvm_unreachable("Unknown solver status."); - } - } - } - - return VerificationResult::CreateBoundReached(); -} - -auto BoundedModelCheckerImpl::createLocNumberFunc() - -> std::function -{ - return [this](Location* loc) { - auto predIt = mLocNumbers.find(loc); - assert(predIt != mLocNumbers.end() - && "All locations must be present in the location map!"); - - return predIt->second; - }; -} - -auto BoundedModelCheckerImpl::findCommonCallAncestor(Location* fwd, Location* bwd) - -> std::pair -{ - // Calculate the closest common dominator for all call nodes - std::vector targets; - std::transform(mCalls.begin(), mCalls.end(), std::back_inserter(targets), [](auto& pair) { - return pair.first; - }); - - Location* dom; - Location* pdom; - - if (!NoDomPush) { - dom = findLowestCommonDominator(targets, mTopo, this->createLocNumberFunc(), fwd); - } else { - dom = fwd; - } - - if (!NoPostDomPush) { - pdom = findHighestCommonPostDominator(targets, mTopo, this->createLocNumberFunc(), bwd); - } else { - pdom = bwd; - } - - return { dom, pdom }; -} - -void BoundedModelCheckerImpl::findOpenCallsInCex(Model& model, llvm::SmallVectorImpl& callsInCex) -{ - auto cex = bmc::BmcCex{mError, *mRoot, model, mPredecessors}; - - for (auto state : cex) { - auto call = llvm::dyn_cast_or_null(state.getOutgoingTransition()); - if (call != nullptr && mOpenCalls.count(call) != 0) { - callsInCex.push_back(call); - if (callsInCex.size() == mOpenCalls.size()) { - // All possible calls were encountered, no point in iterating further. - break; - } - } - } -} - -void BoundedModelCheckerImpl::inlineCallIntoRoot( - CallTransition* call, - llvm::DenseMap& vmap, - const llvm::Twine& suffix, - llvm::SmallVectorImpl& newCalls -) { - LLVM_DEBUG( - llvm::dbgs() << " Inlining call " << *call - << " edge " << call->getSource()->getId() - << " --> " << call->getTarget()->getId() - << "\n"; - ); - - CallInfo& info = mCalls[call]; - auto callee = call->getCalledAutomaton(); - - llvm::DenseMap locToLocMap; - llvm::DenseMap oldVarToNew; - - llvm::DenseMap edgeToEdgeMap; - - VariableExprRewrite rewrite(mExprBuilder); - - // Clone all local variables into the parent - for (Variable& local : callee->locals()) { - LLVM_DEBUG(llvm::dbgs() << "Callee local " << local.getName() << "\n"); - if (!callee->isOutput(&local)) { - auto varname = (local.getName() + suffix).str(); - auto newLocal = mRoot->createLocal(varname, local.getType()); - oldVarToNew[&local] = newLocal; - vmap[newLocal] = &local; - rewrite[&local] = newLocal->getRefExpr(); - } - } - - for (Variable& input : callee->inputs()) { - LLVM_DEBUG(llvm::dbgs() << "Callee input " << input.getName() << "\n"); - if (!callee->isOutput(&input)) { - auto varname = (input.getName() + suffix).str(); - auto newInput = mRoot->createLocal(varname, input.getType()); - oldVarToNew[&input] = newInput; - vmap[newInput] = &input; - - auto arg = call->getInputArgument(input); - assert(arg.has_value() - && "Each call input assignment must map to an input variable in callee!"); - rewrite[&input] = arg->getValue(); - //rewrite[&input] = newInput->getRefExpr(); - } - } - - for (Variable& output : callee->outputs()) { - auto argument = call->getOutputArgument(output); - assert(argument.has_value() && "Every callee output should be assigned in a call transition!"); - - auto newOutput = argument->getVariable(); - oldVarToNew[&output] = newOutput; - vmap[newOutput] = &output; - rewrite[&output] = newOutput->getRefExpr(); - } - - // Insert the locations - for (Location* origLoc : callee->nodes()) { - auto newLoc = mRoot->createLocation(); - locToLocMap[origLoc] = newLoc; - mInlinedLocations[newLoc] = origLoc; - - if (origLoc->isError()) { - mRoot->createAssignTransition(newLoc, mError, mExprBuilder.True(), { - { mErrorFieldVariable, callee->getErrorFieldExpr(origLoc) } - }); - } - } - - // Transform the edges - for (auto origEdge : callee->edges()) { - Transition* newEdge = nullptr; - Location* source = locToLocMap[origEdge->getSource()]; - Location* target = locToLocMap[origEdge->getTarget()]; - - if (auto assign = llvm::dyn_cast(origEdge)) { - // Transform the assignments of this edge to use the new variables. - std::vector newAssigns; - std::transform( - assign->begin(), assign->end(), std::back_inserter(newAssigns), - [&oldVarToNew, &rewrite] (const VariableAssignment& origAssign) { - return VariableAssignment { - oldVarToNew[origAssign.getVariable()], - rewrite.walk(origAssign.getValue()) - }; - } - ); - - newEdge = mRoot->createAssignTransition( - source, target, rewrite.walk(assign->getGuard()), newAssigns - ); - } else if (auto nestedCall = llvm::dyn_cast(origEdge)) { - std::vector newArgs; - std::vector newOuts; - - std::transform( - nestedCall->input_begin(), nestedCall->input_end(), - std::back_inserter(newArgs), - [&rewrite](const VariableAssignment& assign) { - return VariableAssignment{assign.getVariable(), rewrite.walk(assign.getValue())}; - } - ); - std::transform( - nestedCall->output_begin(), nestedCall->output_end(), - std::back_inserter(newOuts), - [&oldVarToNew](const VariableAssignment& origAssign) { - //llvm::errs() << origAssign.getVariable()->getName() << "\n"; - - Variable* newVar = oldVarToNew.lookup(origAssign.getVariable()); - assert(newVar != nullptr && "All variables should be present in the variable map!"); - - return VariableAssignment{ - newVar, - //rewrite.visit(origAssign.getValue()) - origAssign.getValue() - }; - } - ); - - auto callEdge = mRoot->createCallTransition( - source, target, - rewrite.walk(nestedCall->getGuard()), - nestedCall->getCalledAutomaton(), - newArgs, - newOuts - ); - - newEdge = callEdge; - mCalls[callEdge].callChain = info.callChain; - mCalls[callEdge].callChain.push_back(callEdge->getCalledAutomaton()); - mCalls[callEdge].overApprox = mExprBuilder.False(); - newCalls.push_back(callEdge); - } else { - llvm_unreachable("Unknown transition kind!"); - } - - edgeToEdgeMap[origEdge] = newEdge; - } - - Location* before = call->getSource(); - Location* after = call->getTarget(); - - std::vector inputAssigns; - for (auto& input : call->inputs()) { - VariableAssignment inputAssignment(oldVarToNew[input.getVariable()], input.getValue()); - LLVM_DEBUG(llvm::dbgs() << "Added input assignment " << inputAssignment << - " for variable " << *input.getVariable() << "n"); - inputAssigns.push_back(inputAssignment); - } - - mRoot->createAssignTransition(before, locToLocMap[callee->getEntry()], call->getGuard(), inputAssigns); - mRoot->createAssignTransition(locToLocMap[callee->getExit()], after , mExprBuilder.True()); - - // Add the new locations to the topological sort. - // As every inlined location should come between the source and target of the original call transition, - // we will insert them there in the topo sort. - auto& oldTopo = mTopoSortMap[callee]; - auto getInlinedLocation = [&locToLocMap](Location* loc) { - return locToLocMap[loc]; - }; - - size_t callIdx = mLocNumbers[call->getTarget()]; - auto callPos = std::next(mTopo.begin(), callIdx); - auto insertPos = mTopo.insert(callPos, - llvm::map_iterator(oldTopo.begin(), getInlinedLocation), - llvm::map_iterator(oldTopo.end(), getInlinedLocation) - ); - - // Update the location numbers - for (auto it = insertPos, ie = mTopo.end(); it != ie; ++it) { - size_t idx = std::distance(mTopo.begin(), it); - mLocNumbers[*it] = idx; - } - - mRoot->disconnectEdge(call); -} - -auto BoundedModelCheckerImpl::runSolver() -> Solver::SolverStatus -{ - llvm::outs() << " Running solver...\n"; - mTimer.start(); - auto status = mSolver->run(); - mTimer.stop(); - - llvm::outs() << " Elapsed time: "; - mTimer.format(llvm::outs(), "s"); - llvm::outs() << "\n"; - mStats.SolverTime += mTimer.elapsed(); - - return status; -} - -void BoundedModelCheckerImpl::printStats(llvm::raw_ostream& os) -{ - os << "--------- Statistics ---------\n"; - os << "Total solver time: "; - llvm::format_provider::format(mStats.SolverTime, os, "s"); - os << "\n"; - os << "Number of inlined procedures: " << mStats.NumInlined << "\n"; - os << "Number of locations on start: " << mStats.NumBeginLocs << "\n"; - os << "Number of locations on finish: " << mStats.NumEndLocs << "\n"; - os << "Number of variables on start: " << mStats.NumBeginLocals << "\n"; - os << "Number of variables on finish: " << mStats.NumEndLocals << "\n"; - os << "------------------------------\n"; - if (mSettings.printSolverStats) { - mSolver->printStats(os); - } - os << "\n"; -} - diff --git a/tools/gazer-bmc/gazer-bmc.cpp b/tools/gazer-bmc/gazer-bmc.cpp index 2be82f00..f9e8973d 100644 --- a/tools/gazer-bmc/gazer-bmc.cpp +++ b/tools/gazer-bmc/gazer-bmc.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #ifndef NDEBUG #include @@ -112,7 +113,7 @@ int main(int argc, char* argv[]) return 0; } -BmcSettings initBmcSettingsFromCommandLine() +static BmcSettings initBmcSettingsFromCommandLine() { BmcSettings settings; settings.debugDumpCfa = DumpCfa; diff --git a/tools/gazer-cfa/gazer-cfa.cpp b/tools/gazer-cfa/gazer-cfa.cpp index 0d7fa1e1..04106ad5 100644 --- a/tools/gazer-cfa/gazer-cfa.cpp +++ b/tools/gazer-cfa/gazer-cfa.cpp @@ -26,6 +26,7 @@ #include "gazer/LLVM/Memory/MemoryModel.h" #include +#include #ifndef NDEBUG #include diff --git a/tools/gazer-theta/gazer-theta.cpp b/tools/gazer-theta/gazer-theta.cpp index 0bcdfd26..82b53be3 100644 --- a/tools/gazer-theta/gazer-theta.cpp +++ b/tools/gazer-theta/gazer-theta.cpp @@ -24,6 +24,7 @@ #include #include +#include #ifndef NDEBUG #include @@ -185,7 +186,7 @@ bool lookupTheta(llvm::StringRef argvZero, theta::ThetaSettings* settings) return false; } - std::string parentPath = llvm::sys::path::parent_path(pathToBinary.get()); + std::string parentPath = llvm::sys::path::parent_path(pathToBinary.get()).str(); if (settings->thetaCfaPath.empty()) { settings->thetaCfaPath = parentPath + "/theta/theta-cfa-cli.jar"; diff --git a/tools/gazer-theta/lib/ThetaCfaGenerator.cpp b/tools/gazer-theta/lib/ThetaCfaGenerator.cpp index 13a7ff07..9fa9eafa 100644 --- a/tools/gazer-theta/lib/ThetaCfaGenerator.cpp +++ b/tools/gazer-theta/lib/ThetaCfaGenerator.cpp @@ -232,20 +232,20 @@ void ThetaCfaGenerator::write(llvm::raw_ostream& os, ThetaNameMapping& nameTrace }; // Add variables - for (auto& variable : main->locals()) { - auto name = validName(variable.getName(), isValidVarName); - auto type = typeName(variable.getType()); + for (auto* variable : main->locals()) { + auto name = validName(variable->getName(), isValidVarName); + auto type = typeName(variable->getType()); - nameTrace.variables[name] = &variable; - vars.try_emplace(&variable, std::make_unique(name, type)); + nameTrace.variables[name] = variable; + vars.try_emplace(variable, std::make_unique(name, type)); } - for (auto& variable : main->inputs()) { - auto name = validName(variable.getName(), isValidVarName); - auto type = typeName(variable.getType()); + for (auto* variable : main->inputs()) { + auto name = validName(variable->getName(), isValidVarName); + auto type = typeName(variable->getType()); - nameTrace.variables[name] = &variable; - vars.try_emplace(&variable, std::make_unique(name, type)); + nameTrace.variables[name] = variable; + vars.try_emplace(variable, std::make_unique(name, type)); } // Add locations @@ -278,7 +278,7 @@ void ThetaCfaGenerator::write(llvm::raw_ostream& os, ThetaNameMapping& nameTrace if (auto assignEdge = dyn_cast(edge)) { for (auto& assignment : *assignEdge) { - auto lhsName = vars[assignment.getVariable()]->getName(); + auto lhsName = vars[assignment.getVariable()]->getName().str(); if (llvm::isa(assignment.getValue())) { stmts.push_back(ThetaStmt::Havoc(lhsName)); @@ -301,14 +301,14 @@ void ThetaCfaGenerator::write(llvm::raw_ostream& os, ThetaNameMapping& nameTrace return variable->getName(); } - return vars[variable]->getName(); + return vars[variable]->getName().str(); }; os << "main process __gazer_main_process {\n"; - for (auto& variable : llvm::concat(main->inputs(), main->locals())) { + for (auto* variable : main->variables()) { os << INDENT; - vars[&variable]->print(os); + vars[variable]->print(os); os << "\n"; } diff --git a/tools/gazer-theta/lib/ThetaExpr.cpp b/tools/gazer-theta/lib/ThetaExpr.cpp index d12b4bcf..5ff0087d 100644 --- a/tools/gazer-theta/lib/ThetaExpr.cpp +++ b/tools/gazer-theta/lib/ThetaExpr.cpp @@ -24,41 +24,46 @@ #include #include +#include using namespace gazer; -class ThetaExprPrinter : public ExprWalker +class ThetaExprPrinter : public ExprWalker { public: - ThetaExprPrinter(std::function replacedNames) - : mReplacedNames(replacedNames) + explicit ThetaExprPrinter(std::function replacedNames) + : mReplacedNames(std::move(replacedNames)) {} + std::string print(const ExprPtr& expr) { + return this->walk(expr); + } + /// If there was an expression which could not be handled by /// this walker, returns it. Otherwise returns nullptr. ExprPtr getInvalidExpr() { return mUnhandledExpr; } -public: - std::string visitExpr(const ExprPtr& expr) { +protected: + std::string visitExpr(const ExprPtr& expr) override { mUnhandledExpr = expr; llvm::errs() << "Unhandled expr " << *expr << "\n"; return "__UNHANDLED_EXPR__"; } - std::string visitLiteral(const ExprRef& expr) + std::string visitLiteral(const ExprRef& expr) override { - if (auto intLit = llvm::dyn_cast(expr)) { + if (auto* intLit = llvm::dyn_cast(expr)) { auto val = intLit->getValue(); return val < 0 ? "(" + std::to_string(val) + ")" : std::to_string(intLit->getValue()); } - if (auto boolLit = llvm::dyn_cast(expr)) { + if (auto* boolLit = llvm::dyn_cast(expr)) { return boolLit->getValue() ? "true" : "false"; } - if (auto realLit = llvm::dyn_cast(expr)) { + if (auto* realLit = llvm::dyn_cast(expr)) { auto val = realLit->getValue(); return std::to_string(val.numerator()) + "%" + std::to_string(val.denominator()); } @@ -66,7 +71,7 @@ class ThetaExprPrinter : public ExprWalker return visitExpr(expr); } - std::string visitVarRef(const ExprRef& expr) { + std::string visitVarRef(const ExprRef& expr) override { std::string newName = mReplacedNames(&expr->getVariable()); if (!newName.empty()) { return newName; @@ -75,32 +80,7 @@ class ThetaExprPrinter : public ExprWalker return expr->getVariable().getName(); } - std::string visitNot(const ExprRef& expr) { - return "(not " + getOperand(0) + ")"; - } - - // Binary - std::string visitAdd(const ExprRef& expr) { - return "(" + getOperand(0) + " + " + getOperand(1) + ")"; - } - - std::string visitSub(const ExprRef& expr) { - return "(" + getOperand(0) + " - " + getOperand(1) + ")"; - } - - std::string visitMul(const ExprRef& expr) { - return "(" + getOperand(0) + " * " + getOperand(1) + ")"; - } - - std::string visitDiv(const ExprRef& expr) { - return "(" + getOperand(0) + " / " + getOperand(1) + ")"; - } - - std::string visitMod(const ExprRef& expr) { - return "(" + getOperand(0) + " mod " + getOperand(1) + ")"; - } - - std::string visitAnd(const ExprRef& expr) + std::string visitAnd(const ExprRef& expr) override { std::string buffer; llvm::raw_string_ostream rso{buffer}; @@ -116,7 +96,7 @@ class ThetaExprPrinter : public ExprWalker return rso.str(); } - std::string visitOr(const ExprRef& expr) + std::string visitOr(const ExprRef& expr) override { std::string buffer; llvm::raw_string_ostream rso{buffer}; @@ -133,43 +113,41 @@ class ThetaExprPrinter : public ExprWalker return rso.str(); } - std::string visitImply(const ExprRef& expr) { - return "(" + getOperand(0) + " imply " + getOperand(1) + ")"; - } - - std::string visitEq(const ExprRef& expr) { - return "(" + getOperand(0) + " = " + getOperand(1) + ")"; - } - - std::string visitNotEq(const ExprRef& expr) { - return "(" + getOperand(0) + " /= " + getOperand(1) + ")"; + std::string visitNot(const ExprRef& expr) override { + return "(not " + getOperand(0) + ")"; } - std::string visitLt(const ExprRef& expr) { - return "(" + getOperand(0) + " < " + getOperand(1) + ")"; +#define PRINT_BINARY_INFIX(NAME, OPERATOR) \ + std::string visit##NAME(const ExprRef& expr) override \ + { \ + return "(" + (getOperand(0)) + " " + (OPERATOR) + " " + (getOperand(1)) + ")"; \ } - std::string visitLtEq(const ExprRef& expr) { - return "(" + getOperand(0) + " <= " + getOperand(1) + ")"; - } + PRINT_BINARY_INFIX(Add, "+") + PRINT_BINARY_INFIX(Sub, "-") + PRINT_BINARY_INFIX(Mul, "*") + PRINT_BINARY_INFIX(Div, "/") + PRINT_BINARY_INFIX(Mod, "mod") + PRINT_BINARY_INFIX(Imply, "imply") - std::string visitGt(const ExprRef& expr) { - return "(" + getOperand(0) + " > " + getOperand(1) + ")"; - } + PRINT_BINARY_INFIX(Eq, "=") + PRINT_BINARY_INFIX(NotEq, "/=") + PRINT_BINARY_INFIX(Lt, "<") + PRINT_BINARY_INFIX(LtEq, "<=") + PRINT_BINARY_INFIX(Gt, ">") + PRINT_BINARY_INFIX(GtEq, ">=") - std::string visitGtEq(const ExprRef& expr) { - return "(" + getOperand(0) + " >= " + getOperand(1) + ")"; - } +#undef PRINT_BINARY_INFIX - std::string visitSelect(const ExprRef& expr) { + std::string visitSelect(const ExprRef& expr) override { return "(if " + getOperand(0) + " then " + getOperand(1) + " else " + getOperand(2) + ")"; } - std::string visitArrayRead(const ExprRef& expr) { + std::string visitArrayRead(const ExprRef& expr) override { return "(" + getOperand(0) + ")[" + getOperand(1) + "]"; } - std::string visitArrayWrite(const ExprRef& expr) { + std::string visitArrayWrite(const ExprRef& expr) override { return "(" + getOperand(0) + ")[" + getOperand(1) + " <- " + getOperand(2) + "]"; } @@ -186,7 +164,7 @@ std::string gazer::theta::printThetaExpr(const ExprPtr& expr) std::string gazer::theta::printThetaExpr(const ExprPtr& expr, std::function variableNames) { - ThetaExprPrinter printer(variableNames); + ThetaExprPrinter printer(std::move(variableNames)); - return printer.walk(expr); + return printer.print(expr); } \ No newline at end of file diff --git a/unittest/ADT/CMakeLists.txt b/unittest/ADT/CMakeLists.txt index d912c8aa..e64b4b5d 100644 --- a/unittest/ADT/CMakeLists.txt +++ b/unittest/ADT/CMakeLists.txt @@ -1,8 +1,9 @@ SET(TEST_SOURCES IntersectionDifferenceTest.cpp GraphTest.cpp + IteratorTest.cpp ) add_executable(GazerAdtTest ${TEST_SOURCES}) target_link_libraries(GazerAdtTest gtest_main GazerSupport) -add_test(GazerAdtTest GazerAdtTest) \ No newline at end of file +add_test(NAME GazerAdtTest COMMAND GazerAdtTest) \ No newline at end of file diff --git a/unittest/ADT/IteratorTest.cpp b/unittest/ADT/IteratorTest.cpp new file mode 100644 index 00000000..ba8c6dcc --- /dev/null +++ b/unittest/ADT/IteratorTest.cpp @@ -0,0 +1,101 @@ +//==-------------------------------------------------------------*- C++ -*--==// +// +// Copyright 2019 Contributors to the Gazer project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +#include "gazer/ADT/Iterator.h" + +#include + +using namespace gazer; + +namespace +{ + +class Shape +{ +public: + enum Kind + { + Kind_Square, + Kind_Circle + }; + + explicit Shape(Kind kind) + : kind(kind) + {} + + Kind kind; +}; + +class Circle : public Shape +{ +public: + explicit Circle(double r) : Shape(Kind_Circle), radius(r) {} + + static bool classof(const Shape* s) + { + return s->kind == Kind_Circle; + } + + double radius; +}; + +class Square : public Shape +{ +public: + Square() + : Shape(Kind_Square) + {} + + static bool classof(const Shape* s) + { + return s->kind == Kind_Square; + } +}; + +TEST(ClassOfIteratorTest, Simple) +{ + Square s1, s2; + Circle c1(0.5); + + std::vector shapes = {&s1, &s2, &c1}; + + int numElems = 0; + for (const Circle* c : classof_range(shapes.begin(), shapes.end())) { + EXPECT_EQ(c->radius, 0.5); + ++numElems; + } + + ASSERT_EQ(numElems, 1); +} + +TEST(ClassOfIteratorTest, References) +{ + // This should also work with references + Square s1, s2; + Circle c1(0.5); + + std::vector shapes = {&s1, &s2, &c1}; + + int numElems = 0; + for (const Circle& c : classof_range(llvm::make_pointee_range(shapes))) { + EXPECT_EQ(c.radius, 0.5); + ++numElems; + } + ASSERT_EQ(numElems, 1); +} + +} // namespace \ No newline at end of file diff --git a/unittest/Automaton/CMakeLists.txt b/unittest/Automaton/CMakeLists.txt index bd2d7942..790cd478 100644 --- a/unittest/Automaton/CMakeLists.txt +++ b/unittest/Automaton/CMakeLists.txt @@ -2,7 +2,7 @@ SET(TEST_SOURCES CfaTest.cpp CfaPrinterTest.cpp PathConditionTest.cpp -) + ) add_executable(GazerAutomatonTest ${TEST_SOURCES}) target_link_libraries(GazerAutomatonTest gtest_main GazerCore GazerAutomaton) diff --git a/unittest/Automaton/PathConditionTest.cpp b/unittest/Automaton/PathConditionTest.cpp index b12bb2a8..e466585f 100644 --- a/unittest/Automaton/PathConditionTest.cpp +++ b/unittest/Automaton/PathConditionTest.cpp @@ -77,13 +77,9 @@ TEST(PathConditionTest, PredecessorTest) auto builder = CreateFoldingExprBuilder(ctx); - std::vector topo; - llvm::DenseMap indexMap; - createTopologicalSort(*cfa, topo, &indexMap); - + CfaTopoSort topo(*cfa); PathConditionCalculator pathCond( topo, *builder, - [&indexMap](auto l) { return indexMap[l]; }, [&ctx](auto t) { return BoolLiteralExpr::True(ctx); }, nullptr ); @@ -129,14 +125,11 @@ TEST(PathConditionTest, TestParallelEdges) // le --> exit [False] cfa->createAssignTransition(le, cfa->getExit(), BoolLiteralExpr::False(ctx)); - std::vector topo; - llvm::DenseMap indexMap; - createTopologicalSort(*cfa, topo, &indexMap); + CfaTopoSort topo(*cfa); auto builder = CreateFoldingExprBuilder(ctx); PathConditionCalculator pathCond( topo, *builder, - [&indexMap](auto l) { return indexMap[l]; }, [&ctx](auto t) { return BoolLiteralExpr::True(ctx); }, nullptr ); diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index 2d60903e..8ab58ea1 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -1,37 +1,16 @@ enable_testing() -set(GOOGLETEST_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src") -set(GOOGLETEST_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build") +include(FetchContent) -# Download and unpack googletest at configure time -configure_file(${PROJECT_SOURCE_DIR}/cmake/GoogleTestDownload.cmake googletest-download/CMakeLists.txt) -execute_process( - COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download +# Load GoogleTest +FetchContent_Declare( + gtest + GIT_REPOSITORY "https://github.com/google/googletest" + GIT_TAG master ) -if(result) - message(FATAL_ERROR "CMake step for googletest failed: ${result}") -endif() -execute_process( - COMMAND ${CMAKE_COMMAND} --build . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download -) -if(result) - message(FATAL_ERROR "Build step for googletest failed: ${result}") -endif() -# Prevent overriding the parent project's compiler/linker -# settings on Windows set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) - -# Add googletest directly to our build. This defines -# the gtest and gtest_main targets. -add_subdirectory(${GOOGLETEST_SOURCE_DIR} - ${GOOGLETEST_BINARY_DIR} - EXCLUDE_FROM_ALL -) +FetchContent_MakeAvailable(gtest) # Add tests add_subdirectory(ADT) @@ -39,6 +18,7 @@ add_subdirectory(Core) add_subdirectory(Automaton) add_subdirectory(LLVM) add_subdirectory(Support) +add_subdirectory(Verifier) add_subdirectory(tools/gazer-theta) # Only add tests for requested targets @@ -57,5 +37,6 @@ add_dependencies(check-unit GazerAutomatonTest GazerSolverZ3Test GazerToolsBackendThetaTest + GazerVerifierTest GazerSupportTest ) diff --git a/unittest/Core/Expr/ExprBuilderTest.cpp b/unittest/Core/Expr/ExprBuilderTest.cpp index 26b437d0..d935a007 100644 --- a/unittest/Core/Expr/ExprBuilderTest.cpp +++ b/unittest/Core/Expr/ExprBuilderTest.cpp @@ -37,9 +37,9 @@ TEST(ExprBuilderTest, TupleConstruct) auto& tupTy = llvm::cast(tuple->getType()); ASSERT_EQ(tupTy.getNumSubtypes(), 3); - EXPECT_EQ(tupTy.getTypeAtIndex(0), IntType::Get(ctx)); - EXPECT_EQ(tupTy.getTypeAtIndex(1), BvType::Get(ctx, 8)); - EXPECT_EQ(tupTy.getTypeAtIndex(2), BoolType::Get(ctx)); + EXPECT_EQ(tupTy.getSubType(0), IntType::Get(ctx)); + EXPECT_EQ(tupTy.getSubType(1), BvType::Get(ctx, 8)); + EXPECT_EQ(tupTy.getSubType(2), BoolType::Get(ctx)); } TEST(ExprBuilderTest, TupleInsertionsInTupleConstruct) diff --git a/unittest/Core/Expr/ExprWalkerTest.cpp b/unittest/Core/Expr/ExprWalkerTest.cpp index dbdd8524..330d64b0 100644 --- a/unittest/Core/Expr/ExprWalkerTest.cpp +++ b/unittest/Core/Expr/ExprWalkerTest.cpp @@ -27,21 +27,27 @@ using namespace gazer; namespace { -class PrintKindWalker : public ExprWalker +class PrintKindWalker : public ExprWalker { public: - void* visitExpr(const ExprPtr& expr) + void* print(const ExprPtr& expr) { + return this->walk(expr); + } + +protected: + void* visitExpr(const ExprPtr& expr) override { Res += Expr::getKindName(expr->getKind()).str() + " "; return nullptr; } - void* visitVarRef(const ExprRef& expr) + void* visitVarRef(const ExprRef& expr) override { Res += expr->getVariable().getName() + " "; return nullptr; } +public: std::string Res; }; @@ -72,15 +78,20 @@ TEST(ExprWalkerTest, TestTraversal) ); PrintKindWalker walker; - walker.walk(expr); + walker.print(expr); ASSERT_EQ(walker.Res, "X A B ZExt Eq And A B ZExt Eq Y Or Imply Not "); } -class PrintAndOperandsWalker : public ExprWalker +class PrintAndOperandsWalker : public ExprWalker { public: - std::string visitAnd(const ExprRef& expr) + std::string visit(const ExprPtr& expr) { + return this->walk(expr); + } + +protected: + std::string visitAnd(const ExprRef& expr) override { std::string buff; llvm::raw_string_ostream rso{buff}; @@ -93,12 +104,12 @@ class PrintAndOperandsWalker : public ExprWalker& expr) + std::string visitVarRef(const ExprRef& expr) override { return expr->getVariable().getName(); } - std::string visitExpr(const ExprPtr& expr) + std::string visitExpr(const ExprPtr& expr) override { return ""; } @@ -118,7 +129,7 @@ TEST(ExprWalkerTest, TestGetOperand) }); PrintAndOperandsWalker walker; - auto res = walker.walk(expr); + auto res = walker.visit(expr); ASSERT_EQ(res, "And(0: A 1: B 2: C 3: D )"); } diff --git a/unittest/LLVM/Automaton/InstToExprTest.cpp b/unittest/LLVM/Automaton/InstToExprTest.cpp index a3da0cd3..ea0f3b82 100644 --- a/unittest/LLVM/Automaton/InstToExprTest.cpp +++ b/unittest/LLVM/Automaton/InstToExprTest.cpp @@ -334,16 +334,16 @@ TEST_F(InstToExprTest, TransformBvCmp) builder->KIND(variableFor("x")->getRefExpr(), variableFor("y")->getRefExpr())); \ } - CHECK_BV_COMPARE(ICMP_EQ, Eq); - CHECK_BV_COMPARE(ICMP_NE, NotEq); - CHECK_BV_COMPARE(ICMP_SGT, BvSGt); - CHECK_BV_COMPARE(ICMP_SGE, BvSGtEq); - CHECK_BV_COMPARE(ICMP_SLT, BvSLt); - CHECK_BV_COMPARE(ICMP_SLE, BvSLtEq); - CHECK_BV_COMPARE(ICMP_UGT, BvUGt); - CHECK_BV_COMPARE(ICMP_UGE, BvUGtEq); - CHECK_BV_COMPARE(ICMP_ULT, BvULt); - CHECK_BV_COMPARE(ICMP_ULE, BvULtEq); + CHECK_BV_COMPARE(ICMP_EQ, Eq) + CHECK_BV_COMPARE(ICMP_NE, NotEq) + CHECK_BV_COMPARE(ICMP_SGT, BvSGt) + CHECK_BV_COMPARE(ICMP_SGE, BvSGtEq) + CHECK_BV_COMPARE(ICMP_SLT, BvSLt) + CHECK_BV_COMPARE(ICMP_SLE, BvSLtEq) + CHECK_BV_COMPARE(ICMP_UGT, BvUGt) + CHECK_BV_COMPARE(ICMP_UGE, BvUGtEq) + CHECK_BV_COMPARE(ICMP_ULT, BvULt) + CHECK_BV_COMPARE(ICMP_ULE, BvULtEq) #undef CHECK_BV_COMPARE } @@ -362,12 +362,12 @@ TEST_F(InstToExprTest, TransformIntCmp) builder->KIND(variableFor("x")->getRefExpr(), variableFor("y")->getRefExpr())); \ } - CHECK_INT_COMPARE(ICMP_EQ, Eq); - CHECK_INT_COMPARE(ICMP_NE, NotEq); - CHECK_INT_COMPARE(ICMP_SGT, Gt); - CHECK_INT_COMPARE(ICMP_SGE, GtEq); - CHECK_INT_COMPARE(ICMP_SLT, Lt); - CHECK_INT_COMPARE(ICMP_SLE, LtEq); + CHECK_INT_COMPARE(ICMP_EQ, Eq) + CHECK_INT_COMPARE(ICMP_NE, NotEq) + CHECK_INT_COMPARE(ICMP_SGT, Gt) + CHECK_INT_COMPARE(ICMP_SGE, GtEq) + CHECK_INT_COMPARE(ICMP_SLT, Lt) + CHECK_INT_COMPARE(ICMP_SLE, LtEq) #undef CHECK_INT_COMPARE } diff --git a/unittest/SolverZ3/Z3SolverTest.cpp b/unittest/SolverZ3/Z3SolverTest.cpp index e50b11b5..8ef189dc 100644 --- a/unittest/SolverZ3/Z3SolverTest.cpp +++ b/unittest/SolverZ3/Z3SolverTest.cpp @@ -20,24 +20,39 @@ #include "gazer/Core/ExprTypes.h" #include "gazer/Core/LiteralExpr.h" +#include + #include using namespace gazer; -TEST(SolverZ3Test, SmokeTest1) +namespace { + +class SolverZ3Test : public ::testing::Test { +public: + void SetUp() override + { + tmpCount = 0; + solver = factory.createSolver(ctx); + } + + ::testing::AssertionResult checkBvOp(const ExprPtr& op, const ExprPtr& expected); + +protected: GazerContext ctx; Z3SolverFactory factory; - auto solver = factory.createSolver(ctx); + std::unique_ptr solver; + unsigned tmpCount = 0; +}; +TEST_F(SolverZ3Test, SmokeTest1) +{ auto a = ctx.createVariable("A", BoolType::Get(ctx)); auto b = ctx.createVariable("B", BoolType::Get(ctx)); // (A & B) - solver->add(AndExpr::Create( - a->getRefExpr(), - b->getRefExpr() - )); + solver->add(AndExpr::Create(a->getRefExpr(), b->getRefExpr())); auto result = solver->run(); @@ -48,16 +63,276 @@ TEST(SolverZ3Test, SmokeTest1) ASSERT_EQ(model->evaluate(b->getRefExpr()), BoolLiteralExpr::True(ctx)); } -TEST(SolverZ3Test, FpaWithRoundingMode) +TEST_F(SolverZ3Test, BooleansUnsat) { - GazerContext ctx; - Z3SolverFactory factory; - auto solver = factory.createSolver(ctx); + auto x = ctx.createVariable("x", BoolType::Get(ctx))->getRefExpr(); + auto y = ctx.createVariable("y", BoolType::Get(ctx))->getRefExpr(); + + auto deMorgan = EqExpr::Create( + AndExpr::Create(x, y), + NotExpr::Create(OrExpr::Create(NotExpr::Create(x), NotExpr::Create(y)))); + + solver->add(NotExpr::Create(deMorgan)); + + auto result = solver->run(); + + ASSERT_EQ(result, Solver::UNSAT); +} + +TEST_F(SolverZ3Test, Undef) +{ + auto x = ctx.createVariable("x", BoolType::Get(ctx))->getRefExpr(); + auto y = ctx.createVariable("y", BoolType::Get(ctx))->getRefExpr(); + + auto f1 = AndExpr::Create(x, UndefExpr::Get(BoolType::Get(ctx))); + + solver->add(f1); + + auto status = solver->run(); + ASSERT_EQ(status, Solver::SAT); + + solver->add(NotExpr::Create(f1)); + status = solver->run(); + + // TODO(sallaigy): Fix this test if we implement undef lifting for CFA. See comment in + // Z3SolverImpl::visitUndef for more details. + ASSERT_EQ(status, Solver::UNSAT); +} + +TEST_F(SolverZ3Test, Integers) +{ + auto x = ctx.createVariable("x", IntType::Get(ctx))->getRefExpr(); + auto y = ctx.createVariable("y", IntType::Get(ctx))->getRefExpr(); + + // 2x + y = 11 + // 3x - y = 9 + solver->add(EqExpr::Create( + AddExpr::Create(MulExpr::Create(IntLiteralExpr::Get(ctx, 2), x), y), + IntLiteralExpr::Get(IntType::Get(ctx), 11))); + solver->add(EqExpr::Create( + SubExpr::Create(MulExpr::Create(IntLiteralExpr::Get(ctx, 3), x), y), + IntLiteralExpr::Get(ctx, 9))); + + auto status = solver->run(); + ASSERT_EQ(status, Solver::SAT); + + auto model = solver->getModel(); + ASSERT_EQ(model->evaluate(x), IntLiteralExpr::Get(ctx, 4)); + ASSERT_EQ(model->evaluate(y), IntLiteralExpr::Get(ctx, 3)); +} +TEST_F(SolverZ3Test, IntegerOperations) +{ + EXPECT_TRUE(checkBvOp( + DivExpr::Create(IntLiteralExpr::Get(ctx, 7), IntLiteralExpr::Get(ctx, 3)), IntLiteralExpr::Get(ctx, 2))); + EXPECT_TRUE(checkBvOp( + RemExpr::Create(IntLiteralExpr::Get(ctx, 7), IntLiteralExpr::Get(ctx, 3)), IntLiteralExpr::Get(ctx, 1))); + EXPECT_TRUE(checkBvOp( + ModExpr::Create(IntLiteralExpr::Get(ctx, 7), IntLiteralExpr::Get(ctx, 3)), IntLiteralExpr::Get(ctx, 1))); +} + +TEST_F(SolverZ3Test, IntegerCompare) +{ + auto left = IntLiteralExpr::Get(ctx, -5); + auto right = IntLiteralExpr::Get(ctx, 2); + + EXPECT_TRUE(checkBvOp( + LtExpr::Create(left, right), BoolLiteralExpr::True(ctx) + )); + EXPECT_TRUE(checkBvOp( + LtEqExpr::Create(left, right), BoolLiteralExpr::True(ctx) + )); + EXPECT_TRUE(checkBvOp( + LtEqExpr::Create(left, left), BoolLiteralExpr::True(ctx) + )); + EXPECT_TRUE(checkBvOp( + GtExpr::Create(left, right), BoolLiteralExpr::False(ctx) + )); + EXPECT_TRUE(checkBvOp( + GtExpr::Create(right, left), BoolLiteralExpr::True(ctx) + )); + EXPECT_TRUE(checkBvOp( + GtEqExpr::Create(left, right), BoolLiteralExpr::False(ctx) + )); + EXPECT_TRUE(checkBvOp( + GtEqExpr::Create(left, left), BoolLiteralExpr::True(ctx) + )); + EXPECT_TRUE(checkBvOp( + EqExpr::Create(left, right), BoolLiteralExpr::False(ctx) + )); + EXPECT_TRUE(checkBvOp( + EqExpr::Create(left, left), BoolLiteralExpr::True(ctx) + )); + EXPECT_TRUE(checkBvOp( + NotEqExpr::Create(left, right), BoolLiteralExpr::True(ctx) + )); + EXPECT_TRUE(checkBvOp( + NotEqExpr::Create(left, left), BoolLiteralExpr::False(ctx) + )); +} + +::testing::AssertionResult SolverZ3Test::checkBvOp(const ExprPtr& op, const ExprPtr& expected) +{ + solver->reset(); + + auto x = + ctx.createVariable("x" + std::to_string(tmpCount++), expected->getType())->getRefExpr(); + solver->add(EqExpr::Create(x, op)); + + auto status = solver->run(); + EXPECT_EQ(status, Solver::SAT); + + auto model = solver->getModel(); + auto value = model->evaluate(x); + + if (value != expected) { + std::string buffer; + llvm::raw_string_ostream rso(buffer); + + rso << "Expected " << *expected << " to be equal to actual value " << *value << ", but it was not.\n"; + rso.flush(); + + return ::testing::AssertionFailure() << rso.str(); + } + + return ::testing::AssertionSuccess(); +} + +TEST_F(SolverZ3Test, Bitvectors) +{ + auto& bv32Ty = BvType::Get(ctx, 32); + + auto seven = BvLiteralExpr::Get(bv32Ty, 7); + auto three = BvLiteralExpr::Get(bv32Ty, 3); + + EXPECT_TRUE(checkBvOp(AddExpr::Create(seven, three), BvLiteralExpr::Get(bv32Ty, 10))); + EXPECT_TRUE(checkBvOp(SubExpr::Create(seven, three), BvLiteralExpr::Get(bv32Ty, 4))); + EXPECT_TRUE(checkBvOp(MulExpr::Create(seven, three), BvLiteralExpr::Get(bv32Ty, 21))); + EXPECT_TRUE(checkBvOp(BvSRemExpr::Create(seven, three), BvLiteralExpr::Get(bv32Ty, 1))); + EXPECT_TRUE(checkBvOp(BvURemExpr::Create(seven, three), BvLiteralExpr::Get(bv32Ty, 1))); + EXPECT_TRUE(checkBvOp(ShlExpr::Create(seven, three), BvLiteralExpr::Get(bv32Ty, 56))); + EXPECT_TRUE(checkBvOp(AShrExpr::Create(seven, three), BvLiteralExpr::Get(bv32Ty, 0))); + EXPECT_TRUE(checkBvOp(LShrExpr::Create(seven, three), BvLiteralExpr::Get(bv32Ty, 0))); + EXPECT_TRUE(checkBvOp(BvSDivExpr::Create(seven, three), BvLiteralExpr::Get(bv32Ty, 2))); + EXPECT_TRUE(checkBvOp(BvUDivExpr::Create(seven, three), BvLiteralExpr::Get(bv32Ty, 2))); + EXPECT_TRUE(checkBvOp(BvAndExpr::Create(seven, three), three)); + EXPECT_TRUE(checkBvOp(BvOrExpr::Create(seven, three), seven)); + EXPECT_TRUE(checkBvOp(BvXorExpr::Create(seven, three),BvLiteralExpr::Get(bv32Ty, 4))); + + auto& bv16Ty = BvType::Get(ctx, 16); + + // 458752 + 3 + checkBvOp( + BvConcatExpr::Create(BvLiteralExpr::Get(bv16Ty, 7), BvLiteralExpr::Get(bv16Ty, 3)), + BvLiteralExpr::Get(bv32Ty, 458755)); +} + +TEST_F(SolverZ3Test, BitVectorCompare) +{ + auto& bv32Ty = BvType::Get(ctx, 32); + + auto left = BvLiteralExpr::Get(bv32Ty, llvm::APInt(32, "-5", 10)); + auto right = BvLiteralExpr::Get(bv32Ty, llvm::APInt(32, "2", 10)); + + // -5 slt 2 = True + // -5 slt -5 = False + // -5 ult 2 --> 4294967291 < 2 = False + EXPECT_TRUE(checkBvOp(BvSLtExpr::Create(left, right), BoolLiteralExpr::True(ctx))); + EXPECT_TRUE(checkBvOp(BvSLtExpr::Create(right, right), BoolLiteralExpr::False(ctx))); + EXPECT_TRUE(checkBvOp(BvULtExpr::Create(left, right), BoolLiteralExpr::False(ctx))); + EXPECT_TRUE(checkBvOp(BvSGtExpr::Create(left, right), BoolLiteralExpr::False(ctx))); + EXPECT_TRUE(checkBvOp(BvSGtExpr::Create(right, right), BoolLiteralExpr::False(ctx))); + EXPECT_TRUE(checkBvOp(BvUGtExpr::Create(left, right), BoolLiteralExpr::True(ctx))); + + // -5 sle 2 = True + // -5 sle -5 = True + // -5 sle -6 = False + // -5 ule 2 = False + EXPECT_TRUE(checkBvOp(BvSLtEqExpr::Create(left, right), BoolLiteralExpr::True(ctx))); + EXPECT_TRUE(checkBvOp(BvSLtEqExpr::Create(right, right), BoolLiteralExpr::True(ctx))); + EXPECT_TRUE(checkBvOp(BvULtEqExpr::Create(left, BvLiteralExpr::Get(bv32Ty, llvm::APInt(32, "-6", 10))), BoolLiteralExpr::False(ctx))); + EXPECT_TRUE(checkBvOp(BvULtEqExpr::Create(left, right), BoolLiteralExpr::False(ctx))); + + EXPECT_TRUE(checkBvOp(BvSGtEqExpr::Create(left, right), BoolLiteralExpr::False(ctx))); + EXPECT_TRUE(checkBvOp(BvSGtEqExpr::Create(right, right), BoolLiteralExpr::True(ctx))); + EXPECT_TRUE(checkBvOp(BvUGtEqExpr::Create(left, BvLiteralExpr::Get(bv32Ty, llvm::APInt(32, "-6", 10))), BoolLiteralExpr::True(ctx))); + EXPECT_TRUE(checkBvOp(BvUGtEqExpr::Create(left, right), BoolLiteralExpr::True(ctx))); + + EXPECT_TRUE(checkBvOp(EqExpr::Create(left, left), BoolLiteralExpr::True(ctx))); + EXPECT_TRUE(checkBvOp(EqExpr::Create(left, right), BoolLiteralExpr::False(ctx))); +} + +TEST_F(SolverZ3Test, BvCasts) +{ + auto x = ctx.createVariable("x", BvType::Get(ctx, 16))->getRefExpr(); + auto y1 = ctx.createVariable("y1", BvType::Get(ctx, 32))->getRefExpr(); + auto y2 = ctx.createVariable("y2", BvType::Get(ctx, 32))->getRefExpr(); + + solver->add(EqExpr::Create(x, BvLiteralExpr::Get(BvType::Get(ctx, 16), 65535))); + solver->add(EqExpr::Create(y1, ZExtExpr::Create(x, BvType::Get(ctx, 32)))); + solver->add(EqExpr::Create(y2, SExtExpr::Create(x, BvType::Get(ctx, 32)))); + + auto status = solver->run(); + ASSERT_EQ(status, Solver::SAT); + + auto model = solver->getModel(); + EXPECT_EQ(model->evaluate(y1), BvLiteralExpr::Get(BvType::Get(ctx, 32), 65535)); + EXPECT_EQ(model->evaluate(y2), BvLiteralExpr::Get(BvType::Get(ctx, 32), llvm::APInt(32, "-1", 10))); +} + +TEST_F(SolverZ3Test, BvExtract) +{ + auto x = ctx.createVariable("x", BvType::Get(ctx, 16))->getRefExpr(); + auto y1 = ctx.createVariable("y1", BvType::Get(ctx, 8))->getRefExpr(); + auto y2 = ctx.createVariable("y2", BvType::Get(ctx, 8))->getRefExpr(); + + // 1111 1111 0000 0000 + solver->add(EqExpr::Create(x, BvLiteralExpr::Get(BvType::Get(ctx, 16), 65280))); + solver->add(EqExpr::Create(y1, ExtractExpr::Create(x, 0, 8))); + solver->add(EqExpr::Create(y2, ExtractExpr::Create(x, 8, 8))); + + + auto status = solver->run(); + ASSERT_EQ(status, Solver::SAT); + + auto model = solver->getModel(); + EXPECT_EQ(model->evaluate(y1), BvLiteralExpr::Get(BvType::Get(ctx, 8), 0)); + EXPECT_EQ(model->evaluate(y2), BvLiteralExpr::Get(BvType::Get(ctx, 8), 255)); +} + +TEST_F(SolverZ3Test, FloatLiterals) +{ + auto& fp16Ty = FloatType::Get(ctx, FloatType::Half); + auto& fp32Ty = FloatType::Get(ctx, FloatType::Single); + auto& fp64Ty = FloatType::Get(ctx, FloatType::Double); + auto& fp128Ty = FloatType::Get(ctx, FloatType::Quad); + + auto largeFp16 = FloatLiteralExpr::Get(fp16Ty, llvm::APFloat::getLargest(fp16Ty.getLLVMSemantics())); + auto smallFp16 = FloatLiteralExpr::Get(fp16Ty, llvm::APFloat::getSmallestNormalized(fp16Ty.getLLVMSemantics())); + + auto x16 = ctx.createVariable("x16", fp16Ty)->getRefExpr(); + auto y16 = ctx.createVariable("y16", fp16Ty)->getRefExpr(); + + solver->add(FEqExpr::Create(x16, largeFp16)); + solver->add(FEqExpr::Create(y16, smallFp16)); + + auto status = solver->run(); + ASSERT_EQ(status, Solver::SAT); + + auto model = solver->getModel(); + EXPECT_EQ(model->evaluate(x16), largeFp16); + EXPECT_EQ(model->evaluate(y16), smallFp16); +} + +TEST_F(SolverZ3Test, FpaWithRoundingMode) +{ // fcast.fp64(tmp) == 0 auto tmp = ctx.createVariable("tmp", FloatType::Get(ctx, FloatType::Single)); - auto fcast = FCastExpr::Create(tmp->getRefExpr(), FloatType::Get(ctx, FloatType::Double), llvm::APFloatBase::rmNearestTiesToEven); - auto eq = FEqExpr::Create(fcast, FloatLiteralExpr::Get(FloatType::Get(ctx, FloatType::Double), llvm::APFloat{0.0})); + auto fcast = FCastExpr::Create( + tmp->getRefExpr(), FloatType::Get(ctx, FloatType::Double), + llvm::APFloatBase::rmNearestTiesToEven); + auto eq = FEqExpr::Create( + fcast, FloatLiteralExpr::Get(FloatType::Get(ctx, FloatType::Double), llvm::APFloat{0.0})); solver->add(eq); @@ -67,16 +342,180 @@ TEST(SolverZ3Test, FpaWithRoundingMode) ASSERT_EQ( model->evaluate(tmp->getRefExpr()), - FloatLiteralExpr::Get(FloatType::Get(ctx, FloatType::Single), llvm::APFloat(0.0f)) - ); + FloatLiteralExpr::Get(FloatType::Get(ctx, FloatType::Single), llvm::APFloat(0.0f))); } -TEST(SolverZ3Test, Arrays) +TEST_F(SolverZ3Test, FloatOperations) { - GazerContext ctx; - Z3SolverFactory factory; - auto solver = factory.createSolver(ctx); + auto& fp32Ty = FloatType::Get(ctx, FloatType::Single); + + EXPECT_TRUE(checkBvOp(FAddExpr::Create( + FloatLiteralExpr::Get(fp32Ty, llvm::APFloat(7.0f)), + FloatLiteralExpr::Get(fp32Ty, llvm::APFloat(3.5f)), + llvm::APFloat::rmNearestTiesToEven + ), FloatLiteralExpr::Get(fp32Ty, llvm::APFloat(10.5f)))); + EXPECT_TRUE(checkBvOp(FSubExpr::Create( + FloatLiteralExpr::Get(fp32Ty, llvm::APFloat(7.0f)), + FloatLiteralExpr::Get(fp32Ty, llvm::APFloat(3.5f)), + llvm::APFloat::rmNearestTiesToEven + ), FloatLiteralExpr::Get(fp32Ty, llvm::APFloat(3.5f)))); + EXPECT_TRUE(checkBvOp(FMulExpr::Create( + FloatLiteralExpr::Get(fp32Ty, llvm::APFloat(7.0f)), + FloatLiteralExpr::Get(fp32Ty, llvm::APFloat(3.5f)), + llvm::APFloat::rmNearestTiesToEven + ), FloatLiteralExpr::Get(fp32Ty, llvm::APFloat(24.5f)))); + EXPECT_TRUE(checkBvOp(FDivExpr::Create( + FloatLiteralExpr::Get(fp32Ty, llvm::APFloat(7.0f)), + FloatLiteralExpr::Get(fp32Ty, llvm::APFloat(3.5f)), + llvm::APFloat::rmNearestTiesToEven + ), FloatLiteralExpr::Get(fp32Ty, llvm::APFloat(2.0f)))); +} + +TEST_F(SolverZ3Test, FloatCompare) +{ + auto& fp32Ty = FloatType::Get(ctx, FloatType::Single); + auto left = FloatLiteralExpr::Get(fp32Ty, llvm::APFloat(-0.5f)); + auto right = FloatLiteralExpr::Get(fp32Ty, llvm::APFloat(6.5f)); + + EXPECT_TRUE(checkBvOp( + FEqExpr::Create(left, right), BoolLiteralExpr::False(ctx) + )); + EXPECT_TRUE(checkBvOp( + FEqExpr::Create(left, left), BoolLiteralExpr::True(ctx) + )); + + EXPECT_TRUE(checkBvOp( + FGtExpr::Create(left, right), BoolLiteralExpr::False(ctx) + )); + EXPECT_TRUE(checkBvOp( + FGtExpr::Create(right, left), BoolLiteralExpr::True(ctx) + )); + EXPECT_TRUE(checkBvOp( + FGtEqExpr::Create(right, left), BoolLiteralExpr::True(ctx) + )); + EXPECT_TRUE(checkBvOp( + FGtEqExpr::Create(left, left), BoolLiteralExpr::True(ctx) + )); + + EXPECT_TRUE(checkBvOp( + FLtExpr::Create(left, right), BoolLiteralExpr::True(ctx) + )); + EXPECT_TRUE(checkBvOp( + FLtExpr::Create(right, left), BoolLiteralExpr::False(ctx) + )); + EXPECT_TRUE(checkBvOp( + FLtEqExpr::Create(right, left), BoolLiteralExpr::False(ctx) + )); + EXPECT_TRUE(checkBvOp( + FLtEqExpr::Create(left, left), BoolLiteralExpr::True(ctx) + )); + + auto nan = FloatLiteralExpr::Get(fp32Ty, llvm::APFloat::getNaN(fp32Ty.getLLVMSemantics())); + EXPECT_TRUE(checkBvOp( + FEqExpr::Create(nan, nan), BoolLiteralExpr::False(ctx) + )); +} + +TEST_F(SolverZ3Test, FloatQuery) +{ + auto& fp32Ty = FloatType::Get(ctx, FloatType::Single); + + auto x = FloatLiteralExpr::Get(fp32Ty, llvm::APFloat(1.5f)); + auto nan = FloatLiteralExpr::Get(fp32Ty, llvm::APFloat::getNaN(fp32Ty.getLLVMSemantics())); + auto plusInf = FloatLiteralExpr::Get(fp32Ty, llvm::APFloat::getInf(fp32Ty.getLLVMSemantics(), false)); + auto negInf = FloatLiteralExpr::Get(fp32Ty, llvm::APFloat::getInf(fp32Ty.getLLVMSemantics(), true)); + + EXPECT_TRUE(checkBvOp( + FIsNanExpr::Create(nan), BoolLiteralExpr::True(ctx) + )); + EXPECT_TRUE(checkBvOp( + FIsNanExpr::Create(x), BoolLiteralExpr::False(ctx) + )); + EXPECT_TRUE(checkBvOp( + FIsNanExpr::Create(plusInf), BoolLiteralExpr::False(ctx) + )); + + EXPECT_TRUE(checkBvOp( + FIsInfExpr::Create(nan), BoolLiteralExpr::False(ctx) + )); + EXPECT_TRUE(checkBvOp( + FIsInfExpr::Create(plusInf), BoolLiteralExpr::True(ctx) + )); + EXPECT_TRUE(checkBvOp( + FIsInfExpr::Create(negInf), BoolLiteralExpr::True(ctx) + )); +} + +TEST_F(SolverZ3Test, BvToFpCasts) +{ + auto& fp32Ty = FloatType::Get(ctx, FloatType::Single); + auto& fp64Ty = FloatType::Get(ctx, FloatType::Double); + + std::array singlePrecisionInts{0, 1, 2, 5, 10, 20, 100, 200, 500}; + + // See semantic transformation with floats + for (int intVal : singlePrecisionInts) { + auto bvLit = BvLiteralExpr::Get(BvType::Get(ctx, 32), llvm::APInt(32, intVal, true)); + auto fp32Lit = FloatLiteralExpr::Get(fp32Ty, llvm::APFloat((float) intVal)); + + EXPECT_TRUE(checkBvOp(SignedToFpExpr::Create(bvLit, fp32Ty, llvm::APFloat::rmNearestTiesToEven), fp32Lit)); + EXPECT_TRUE(checkBvOp(UnsignedToFpExpr::Create(bvLit, fp32Ty, llvm::APFloat::rmNearestTiesToEven), fp32Lit)); + } + + // Now with the double type + for (int intVal : singlePrecisionInts) { + auto bvLit = BvLiteralExpr::Get(BvType::Get(ctx, 64), llvm::APInt(64, intVal, true)); + auto fp64Lit = FloatLiteralExpr::Get(fp64Ty, llvm::APFloat((double) intVal)); + + EXPECT_TRUE(checkBvOp(SignedToFpExpr::Create(bvLit, fp64Ty, llvm::APFloat::rmNearestTiesToEven), fp64Lit)); + EXPECT_TRUE(checkBvOp(UnsignedToFpExpr::Create(bvLit, fp64Ty, llvm::APFloat::rmNearestTiesToEven), fp64Lit)); + } +} + +TEST_F(SolverZ3Test, BvBitcastToFloat) +{ + auto& fp32Ty = FloatType::Get(ctx, FloatType::Single); + auto& bv32Ty = BvType::Get(ctx, 32); + + EXPECT_TRUE( + checkBvOp(BvToFpExpr::Create(BvLiteralExpr::Get(bv32Ty, 0x7F800000), fp32Ty), FloatLiteralExpr::Get(fp32Ty, llvm::APFloat::getInf(fp32Ty.getLLVMSemantics(), false)))); + EXPECT_TRUE( + checkBvOp(BvToFpExpr::Create(BvLiteralExpr::Get(bv32Ty, 0xFF800000), fp32Ty), FloatLiteralExpr::Get(fp32Ty, llvm::APFloat::getInf(fp32Ty.getLLVMSemantics(), true)))); + + EXPECT_TRUE( + checkBvOp(BvToFpExpr::Create(BvLiteralExpr::Get(bv32Ty, 0x80000000), fp32Ty), FloatLiteralExpr::Get(fp32Ty, llvm::APFloat::getZero(fp32Ty.getLLVMSemantics(), true)))); + EXPECT_TRUE( + checkBvOp(BvToFpExpr::Create(BvLiteralExpr::Get(bv32Ty, 0x00000000), fp32Ty), FloatLiteralExpr::Get(fp32Ty, llvm::APFloat::getZero(fp32Ty.getLLVMSemantics(), false)))); +} + +TEST_F(SolverZ3Test, FpToBvCasts) +{ + auto& fp32Ty = FloatType::Get(ctx, FloatType::Single); + auto& bv32Ty = BvType::Get(ctx, 32); + + EXPECT_TRUE(checkBvOp( + FpToSignedExpr::Create(FloatLiteralExpr::Get(fp32Ty, llvm::APFloat(1.0f)), bv32Ty, llvm::APFloat::rmNearestTiesToEven), + BvLiteralExpr::Get(bv32Ty, 1) + )); + EXPECT_TRUE(checkBvOp( + FpToUnsignedExpr::Create(FloatLiteralExpr::Get(fp32Ty, llvm::APFloat(1.0f)), bv32Ty, llvm::APFloat::rmNearestTiesToEven), + BvLiteralExpr::Get(bv32Ty, 1) + )); + + // See bit-cast to bv32 + EXPECT_TRUE(checkBvOp( + FpToBvExpr::Create(FloatLiteralExpr::Get(fp32Ty, llvm::APFloat::getInf(fp32Ty.getLLVMSemantics(), false)), bv32Ty), + BvLiteralExpr::Get(bv32Ty, 0x7F800000) + )); + EXPECT_TRUE(checkBvOp( + FpToBvExpr::Create(FloatLiteralExpr::Get(fp32Ty, llvm::APFloat::getInf(fp32Ty.getLLVMSemantics(), true)), bv32Ty), + BvLiteralExpr::Get(bv32Ty, 0xFF800000) + )); +} + +TEST_F(SolverZ3Test, Arrays) +{ // Example from the Z3 tutorial: // (declare-const x Int) // (declare-const y Int) @@ -87,14 +526,11 @@ TEST(SolverZ3Test, Arrays) auto y = ctx.createVariable("y", IntType::Get(ctx)); auto a1 = ctx.createVariable("a1", ArrayType::Get(IntType::Get(ctx), IntType::Get(ctx))); - solver->add(EqExpr::Create( - ArrayReadExpr::Create(a1->getRefExpr(), x->getRefExpr()), - x->getRefExpr() - )); + solver->add( + EqExpr::Create(ArrayReadExpr::Create(a1->getRefExpr(), x->getRefExpr()), x->getRefExpr())); solver->add(EqExpr::Create( ArrayWriteExpr::Create(a1->getRefExpr(), x->getRefExpr(), y->getRefExpr()), - a1->getRefExpr() - )); + a1->getRefExpr())); auto result = solver->run(); ASSERT_EQ(result, Solver::SAT); @@ -104,20 +540,13 @@ TEST(SolverZ3Test, Arrays) ASSERT_EQ(result, Solver::UNSAT); } -TEST(SolverZ3Test, ArrayLiterals) +TEST_F(SolverZ3Test, ArrayLiterals) { - GazerContext ctx; - Z3SolverFactory factory; - auto solver = factory.createSolver(ctx); - auto a1 = ArrayLiteralExpr::Get( ArrayType::Get(IntType::Get(ctx), IntType::Get(ctx)), - { - { IntLiteralExpr::Get(ctx, 1), IntLiteralExpr::Get(ctx, 1) }, - { IntLiteralExpr::Get(ctx, 2), IntLiteralExpr::Get(ctx, 2) } - }, - IntLiteralExpr::Get(ctx, 0) - ); + {{IntLiteralExpr::Get(ctx, 1), IntLiteralExpr::Get(ctx, 1)}, + {IntLiteralExpr::Get(ctx, 2), IntLiteralExpr::Get(ctx, 2)}}, + IntLiteralExpr::Get(ctx, 0)); auto v = ctx.createVariable("v", ArrayType::Get(IntType::Get(ctx), IntType::Get(ctx))); @@ -128,12 +557,8 @@ TEST(SolverZ3Test, ArrayLiterals) ASSERT_EQ(solver->getModel()->evaluate(v->getRefExpr()), a1); } -TEST(SolverZ3Test, Tuples) +TEST_F(SolverZ3Test, Tuples) { - GazerContext ctx; - Z3SolverFactory factory; - auto solver = factory.createSolver(ctx); - // Example from the Z3 tutorial: // (declare-const p1 (Pair Int Int)) // (declare-const p2 (Pair Int Int)) @@ -158,4 +583,118 @@ TEST(SolverZ3Test, Tuples) status = solver->run(); EXPECT_EQ(status, Solver::UNSAT); -} \ No newline at end of file +} + +TEST_F(SolverZ3Test, TuplesConstruct) +{ + auto& intTy = IntType::Get(ctx); + auto& tupTy = TupleType::Get(intTy, intTy); + + auto p1 = ctx.createVariable("p1", tupTy)->getRefExpr(); + auto p2 = ctx.createVariable("p2", tupTy)->getRefExpr(); + auto x = ctx.createVariable("x", intTy)->getRefExpr(); + + solver->add(EqExpr::Create(p1, p2)); + solver->add( + EqExpr::Create(p2, TupleConstructExpr::Create(tupTy, IntLiteralExpr::Get(intTy, 2), x))); + solver->add(EqExpr::Create(x, IntLiteralExpr::Get(intTy, 5))); + + auto status = solver->run(); + ASSERT_EQ(status, Solver::SAT); + + auto model = solver->getModel(); + // ASSERT_EQ(model->evaluate(TupleSelectExpr::Create(p1, 0)), IntLiteralExpr::Get(intTy, 2)); + // ASSERT_EQ(model->evaluate(TupleSelectExpr::Create(p1, 1)), IntLiteralExpr::Get(intTy, 5)); +} + +TEST_F(SolverZ3Test, Reset) +{ + auto x = ctx.createVariable("x", IntType::Get(ctx))->getRefExpr(); + auto y = ctx.createVariable("y", IntType::Get(ctx))->getRefExpr(); + + auto xEq1 = EqExpr::Create(x, IntLiteralExpr::Get(IntType::Get(ctx), 1)); + auto yEq2 = EqExpr::Create(y, IntLiteralExpr::Get(IntType::Get(ctx), 2)); + + solver->add(xEq1); + solver->add(yEq2); + + auto status = solver->run(); + ASSERT_EQ(status, Solver::SAT); + + solver->add(EqExpr::Create(x, y)); + status = solver->run(); + ASSERT_EQ(status, Solver::UNSAT); + + EXPECT_EQ(solver->getNumConstraints(), 3); + + solver->reset(); + ASSERT_EQ(solver->getNumConstraints(), 0); + + solver->add(xEq1); + status = solver->run(); + ASSERT_EQ(status, Solver::SAT); +} + +TEST_F(SolverZ3Test, PushPop) +{ + auto x = ctx.createVariable("x", IntType::Get(ctx))->getRefExpr(); + auto y = ctx.createVariable("y", IntType::Get(ctx))->getRefExpr(); + + auto xEq1 = EqExpr::Create(x, IntLiteralExpr::Get(IntType::Get(ctx), 1)); + auto yEq2 = EqExpr::Create(y, IntLiteralExpr::Get(IntType::Get(ctx), 2)); + + solver->push(); + + solver->add(xEq1); + solver->add(yEq2); + EXPECT_EQ(solver->getNumConstraints(), 2); + + auto status = solver->run(); + ASSERT_EQ(status, Solver::SAT); + + solver->push(); + + solver->add(EqExpr::Create(x, y)); + EXPECT_EQ(solver->getNumConstraints(), 3); + + status = solver->run(); + ASSERT_EQ(status, Solver::UNSAT); + + solver->pop(); + EXPECT_EQ(solver->getNumConstraints(), 2); + + status = solver->run(); + ASSERT_EQ(status, Solver::SAT); + + solver->pop(); + EXPECT_EQ(solver->getNumConstraints(), 0); +} + +TEST_F(SolverZ3Test, Dump) +{ + std::vector expectedParts = { + "(declare-fun x () Int)", + "(declare-fun y () Int)", + "(assert (= x y))", + "(assert (= y 5))", + }; + + std::string actual; + llvm::raw_string_ostream rso(actual); + + auto x = ctx.createVariable("x", IntType::Get(ctx))->getRefExpr(); + auto y = ctx.createVariable("y", IntType::Get(ctx))->getRefExpr(); + + solver->add(EqExpr::Create(x, y)); + solver->add(EqExpr::Create(y, IntLiteralExpr::Get(ctx, 5))); + + solver->dump(rso); + rso.flush(); + + // There might be other stuff in the string in any order, just search for the important part + for (auto& expected : expectedParts) { + ASSERT_TRUE(actual.find(expected) != std::string::npos); + } +} + +} // namespace diff --git a/unittest/Verifier/BoundedModelCheckerTest.cpp b/unittest/Verifier/BoundedModelCheckerTest.cpp new file mode 100644 index 00000000..6dc6e041 --- /dev/null +++ b/unittest/Verifier/BoundedModelCheckerTest.cpp @@ -0,0 +1,283 @@ +//==-------------------------------------------------------------*- C++ -*--==// +// +// Copyright 2021 Contributors to the Gazer project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +#include "gazer/Verifier/BoundedModelChecker.h" + +#include "gazer/Automaton/Cfa.h" +#include "gazer/Core/Expr/ExprBuilder.h" +#include "gazer/Z3Solver/Z3Solver.h" + +#include + +#include + +using namespace gazer; + +namespace { + +class TraceBuilderImpl : public CfaTraceBuilder +{ +public: + std::unique_ptr build( + std::vector& states, + std::vector>& actions) override + { + this->capturedStates = states; + this->capturedActions = actions; + + return nullptr; + } + + std::vector capturedStates; + std::vector> capturedActions; +}; + +class BmcTest : public ::testing::Test +{ +public: + void SetUp() override + { + system = std::make_unique(ctx); + builder = CreateExprBuilder(ctx); + solverFactory = std::make_unique(); + } + + std::unique_ptr createBmcEngine(unsigned bound = 0) const + { + BmcSettings settings{}; + settings.maxBound = bound; + settings.trace = true; + + return std::make_unique(*solverFactory, settings); + } + +protected: + Cfa* createCounterCfa(int count); + +protected: + GazerContext ctx; + std::unique_ptr system; + std::unique_ptr builder; + std::unique_ptr solverFactory; +}; + +TEST_F(BmcTest, SimpleFail) +{ + Cfa* foo = system->createCfa("foo"); + Variable* x = foo->createLocal("x", IntType::Get(ctx)); + + auto* l1 = foo->createLocation(); + auto* le = foo->createErrorLocation(); + foo->addErrorCode(le, builder->IntLit(1)); + + foo->createAssignTransition(foo->getEntry(), l1, {{x, builder->IntLit(0)}}); + foo->createAssignTransition(l1, le, builder->Eq(x->getRefExpr(), builder->IntLit(0))); + foo->createAssignTransition( + l1, foo->getExit(), builder->NotEq(x->getRefExpr(), builder->IntLit(0))); + + system->setMainAutomaton(foo); + + auto bmc = createBmcEngine(1); + TraceBuilderImpl traceBuilder; + + auto result = bmc->check(*system, traceBuilder); + + ASSERT_TRUE(result->isFail()); + ASSERT_EQ(llvm::cast(*result).getErrorID(), 1); + + std::vector expectedTrace = { + foo->getEntry(), l1, le + }; + + ASSERT_EQ(expectedTrace, traceBuilder.capturedStates); +} + +TEST_F(BmcTest, FailStraightPath) +{ + Cfa* foo = system->createCfa("foo"); + + auto* l1 = foo->createLocation(); + auto* le = foo->createErrorLocation(); + foo->addErrorCode(le, builder->IntLit(1)); + + foo->createAssignTransition(foo->getEntry(), l1); + foo->createAssignTransition(l1, le); + foo->createAssignTransition(le, foo->getExit(), builder->False()); + + system->setMainAutomaton(foo); + + auto bmc = createBmcEngine(1); + TraceBuilderImpl traceBuilder; + + auto result = bmc->check(*system, traceBuilder); + ASSERT_TRUE(result->isFail()); + ASSERT_EQ(traceBuilder.capturedStates.size(), traceBuilder.capturedActions.size() + 1); +} + +TEST_F(BmcTest, SimpleSuccess) +{ + Cfa* foo = system->createCfa("foo"); + Variable* x = foo->createLocal("x", IntType::Get(ctx)); + + auto* l1 = foo->createLocation(); + auto* le = foo->createErrorLocation(); + foo->addErrorCode(le, builder->IntLit(1)); + + foo->createAssignTransition(foo->getEntry(), l1, {{x, builder->IntLit(0)}}); + foo->createAssignTransition(l1, le, builder->NotEq(x->getRefExpr(), builder->IntLit(0))); + foo->createAssignTransition( + l1, foo->getExit(), builder->Eq(x->getRefExpr(), builder->IntLit(0))); + + system->setMainAutomaton(foo); + + auto bmc = createBmcEngine(1); + TraceBuilderImpl traceBuilder; + + auto result = bmc->check(*system, traceBuilder); + + ASSERT_TRUE(result->isSuccess()); +} + +TEST_F(BmcTest, FailWithTailRecursiveProcedure) +{ + Cfa* foo = system->createCfa("foo"); + Variable* x = foo->createLocal("x", IntType::Get(ctx)); + + Cfa* counter = this->createCounterCfa(3); + + auto* l1 = foo->createLocation(); + auto* le = foo->createErrorLocation(); + foo->addErrorCode(le, builder->IntLit(1)); + + foo->createCallTransition(foo->getEntry(), l1, counter, + { VariableAssignment{counter->getInput(0), builder->IntLit(0)} }, + { {x, counter->getOutput(0)->getRefExpr()} } + ); + foo->createAssignTransition(l1, le, builder->Eq(x->getRefExpr(), builder->IntLit(3))); + foo->createAssignTransition( + l1, foo->getExit(), builder->NotEq(x->getRefExpr(), builder->IntLit(3))); + + system->setMainAutomaton(foo); + TraceBuilderImpl traceBuilder; + + auto bmc1 = createBmcEngine(1); + auto result = bmc1->check(*system, traceBuilder); + ASSERT_EQ(result->getStatus(), VerificationResult::BoundReached); + + + Location* l0InCounter = counter->getEntry(); + Location* exitInCounter = counter->getExit(); + Location* l2InCounter = counter->findLocationById(2); + Location* l3InCounter = counter->findLocationById(3); + std::vector expectedTrace = { + // Entering the main automaton + foo->getEntry(), + // First iteration + l0InCounter, + l2InCounter, + l3InCounter, + // Second iteration + l0InCounter, + l2InCounter, + l3InCounter, + // Third iteration + l0InCounter, + l2InCounter, + // Going back through the "call stack" + exitInCounter, + exitInCounter, + exitInCounter, + // Back in 'foo' + l1, + le + }; + + auto bmc = createBmcEngine(3); + result = bmc->check(*system, traceBuilder); + + ASSERT_TRUE(result->isFail()); + ASSERT_EQ(llvm::cast(*result).getErrorID(), 1); + ASSERT_EQ(traceBuilder.capturedStates, expectedTrace); +} + +TEST_F(BmcTest, EagerUnrolling) +{ + Cfa* foo = system->createCfa("foo"); + Variable* x = foo->createLocal("x", IntType::Get(ctx)); + + Cfa* counter = this->createCounterCfa(10); + + auto* l1 = foo->createLocation(); + auto* le = foo->createErrorLocation(); + foo->addErrorCode(le, builder->IntLit(1)); + + foo->createCallTransition(foo->getEntry(), l1, counter, + { VariableAssignment{counter->getInput(0), builder->IntLit(0)} }, + { {x, counter->getOutput(0)->getRefExpr()} } + ); + foo->createAssignTransition(l1, le, builder->Eq(x->getRefExpr(), builder->IntLit(10))); + foo->createAssignTransition( + l1, foo->getExit(), builder->NotEq(x->getRefExpr(), builder->IntLit(10))); + + system->setMainAutomaton(foo); + + BmcSettings settings{}; + settings.maxBound = 10; + settings.eagerUnroll = 9; + auto bmc = std::make_unique(*solverFactory, settings); + + TraceBuilderImpl traceBuilder{}; + + auto result = bmc->check(*system, traceBuilder); + ASSERT_TRUE(result->isFail()); + ASSERT_EQ(llvm::cast(*result).getErrorID(), 1); +} + +Cfa* BmcTest::createCounterCfa(int count) +{ + Cfa* cfa = system->createCfa("counter"); + + auto* cnt = cfa->createInput("cnt", IntType::Get(ctx)); + auto* newCnt = cfa->createLocal("newCnt", IntType::Get(ctx)); + auto* resCnt = cfa->createLocal("res", IntType::Get(ctx)); + + cfa->addOutput(resCnt); + + auto* l1 = cfa->createLocation(); + auto* l2 = cfa->createLocation(); + + cfa->createAssignTransition( + cfa->getEntry(), l1, { + {newCnt, builder->Add(cnt->getRefExpr(), builder->IntLit(1))} + }); + + cfa->createAssignTransition( + l1, cfa->getExit(), + builder->Eq(newCnt->getRefExpr(), builder->IntLit(count)), + { {resCnt, newCnt->getRefExpr()} }); + cfa->createAssignTransition( + l1, l2, builder->Not(builder->Eq(newCnt->getRefExpr(), builder->IntLit(count)))); + cfa->createCallTransition( + l2, cfa->getExit(), cfa, { {cnt, newCnt->getRefExpr()} }, { {resCnt, resCnt->getRefExpr() }} + ); + + return cfa; +} + +} // namespace + + diff --git a/unittest/Verifier/CMakeLists.txt b/unittest/Verifier/CMakeLists.txt new file mode 100644 index 00000000..ff42a437 --- /dev/null +++ b/unittest/Verifier/CMakeLists.txt @@ -0,0 +1,7 @@ +SET(TEST_SOURCES + BoundedModelCheckerTest.cpp +) + +add_executable(GazerVerifierTest ${TEST_SOURCES}) +target_link_libraries(GazerVerifierTest gtest_main GazerVerifier GazerZ3Solver) +add_test(NAME GazerVerifierTest COMMAND GazerVerifierTest)