├── .gitignore
├── .idea
├── .gitignore
├── .name
├── inspectionProfiles
│ └── Project_Default.xml
├── misc.xml
├── modules.xml
├── myanalyzer.iml
└── vcs.xml
├── CMakeLists.txt
├── LICENSE
├── README.md
├── docs
├── CMakeLists.txt
├── CONTRIBUTING.md
├── DataflowAnalysis.md
└── ProgramRepresentation.md
├── include
├── World.h
├── analysis
│ ├── Analysis.h
│ ├── AnalysisResult.h
│ ├── dataflow
│ │ ├── AnalysisDriver.h
│ │ ├── ConstantPropagation.h
│ │ ├── DataflowAnalysis.h
│ │ ├── LiveVariable.h
│ │ ├── ReachingDefinition.h
│ │ ├── fact
│ │ │ ├── DataflowResult.h
│ │ │ ├── MapFact.h
│ │ │ ├── NodeResult.h
│ │ │ └── SetFact.h
│ │ └── solver
│ │ │ └── Solver.h
│ └── graph
│ │ └── CFG.h
├── config
│ └── AnalysisConfig.h
├── ir
│ ├── IR.h
│ ├── Stmt.h
│ └── Var.h
├── language
│ ├── CPPMethod.h
│ └── Type.h
└── util
│ ├── Copyable.h
│ └── Logger.h
├── lib
├── CMakeLists.txt
├── World.cpp
├── analysis
│ ├── Analysis.cpp
│ ├── dataflow
│ │ ├── ConstantPropagation.cpp
│ │ ├── LiveVariable.cpp
│ │ └── ReachingDefinition.cpp
│ └── graph
│ │ └── DefaultCFG.cpp
├── config
│ └── DefaultAnalysisConfig.cpp
├── ir
│ ├── ClangStmtWrapper.cpp
│ ├── ClangVarWrapper.cpp
│ ├── DefaultIR.cpp
│ ├── DefaultIRBuilder.cpp
│ ├── DefaultStmtBuilder.cpp
│ ├── DefaultVarBuilder.cpp
│ └── NopStmt.cpp
├── language
│ ├── CPPMethod.cpp
│ ├── DefaultTypeBuilder.cpp
│ └── Type.cpp
└── util
│ └── Logger.cpp
├── resources
├── dataflow
│ ├── ConstPropagation
│ │ └── ConstPropagationTest.cpp
│ ├── LiveVar
│ │ └── LiveVarTest.cpp
│ └── ReachDef
│ │ └── ReachDefTest.cpp
├── example01
│ ├── include
│ │ ├── factor.h
│ │ └── fib.h
│ └── src
│ │ ├── factor
│ │ └── factor.cpp
│ │ ├── fib
│ │ └── fib.cpp
│ │ └── main.cpp
└── example02
│ ├── dump.sh
│ ├── foo.h
│ └── main.cpp
├── tests
├── CMakeLists.txt
├── TestCPPMethod.cpp
├── TestConstantPropagation.cpp
├── TestDataflowFacts.cpp
├── TestIR.cpp
├── TestLiveVariable.cpp
├── TestReachingDefinition.cpp
├── TestWorld.cpp
├── doctest.h
└── main.cpp
└── tools
├── CLI11.h
├── CMakeLists.txt
├── constant-propagation-analyzer.cpp
├── live-variable-analyzer.cpp
└── reaching-definition-analyzer.cpp
/.gitignore:
--------------------------------------------------------------------------------
1 | cmake-build-debug
2 | build
3 | .DS_Store
4 | .cache
5 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # 默认忽略的文件
2 | /shelf/
3 | /workspace.xml
4 | # 基于编辑器的 HTTP 客户端请求
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | cpp-static-analyzer
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/myanalyzer.iml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.14)
2 |
3 | project(cpp-static-analyzer
4 | VERSION 1.1.0
5 | DESCRIPTION "This is a simple cpp static analysis framework."
6 | )
7 |
8 | set(CMAKE_CXX_STANDARD 17) # the standard used in this project and llvm
9 | # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") # add for those build llvm without rtti
10 |
11 | find_package(LLVM REQUIRED CONFIG)
12 | message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
13 | message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
14 |
15 | find_package(Clang REQUIRED CONFIG)
16 | message(STATUS "Found Clang ${LLVM_VERSION}")
17 | message(STATUS "Using ClangConfig.cmake in ${Clang_DIR}")
18 |
19 | include_directories(${LLVM_INCLUDE_DIRS})
20 | link_directories(${LLVM_LIBRARY_DIRS})
21 |
22 | separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS})
23 | add_definitions(${LLVM_DEFINITIONS_LIST})
24 |
25 | llvm_map_components_to_libnames(LLVM_LIBS support frontendopenmp)
26 |
27 | # define CLANG_LIBS: some clang libs to be used in link time
28 | # !!! DO NOT change the order of the libs !!!
29 | # libs will be linked in this order, and changing the order could lead to link errors
30 | set(CLANG_LIBS
31 | clangTooling
32 | clangFrontendTool
33 | clangFrontend
34 | clangDriver
35 | clangSerialization
36 | clangCodeGen
37 | clangParse
38 | clangSema
39 | clangStaticAnalyzerFrontend
40 | clangStaticAnalyzerCheckers
41 | clangStaticAnalyzerCore
42 | clangAnalysis
43 | clangARCMigrate
44 | clangRewriteFrontend
45 | clangEdit
46 | clangAST
47 | clangASTMatchers
48 | clangLex
49 | clangBasic
50 | )
51 |
52 | include_directories(include)
53 |
54 | add_subdirectory(lib)
55 | add_subdirectory(tools)
56 |
57 | enable_testing()
58 | add_subdirectory(tests)
59 |
60 | add_subdirectory(docs)
61 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # A Simple CPP Static Analysis Framework
2 |
3 | > Copyright (C) 2023 Jiacai Cui <201220014@smail.nju.edu.cn>
4 |
5 | ## Introduction
6 |
7 | This is a simple cpp static analysis framework developed during
8 | a software engineering lab course of Nanjing University.
9 |
10 | The project is personal and distributed
11 | under [GNU general public license](LICENSE).
12 |
13 | The design of this project is well inspired by
14 | [tai-e](https://github.com/pascal-lab/Tai-e.git),
15 | a static analysis framework for java, please view
16 | its [technique report](https://arxiv.org/abs/2208.00337)
17 | for more details.
18 |
19 | ## Environment Setup
20 |
21 | This cpp static analyzer uses clang as its front end.
22 | So you need to install llvm and clang on your local
23 | system inorder to build and run this project.
24 | It is developed under [llvm](https://llvm.org/)
25 | 16.0.2, tested under llvm 16.0.2 and 17.0.0,
26 | but more recent versions should also be ok.
27 |
28 | It is recommended to install llvm using precompiled binaries
29 | instead of building from source manually.
30 |
31 | Here is the way to set up the proper environment of this
32 | project.
33 |
34 | ### On macOS
35 |
36 | Use [homebrew](https://brew.sh/) as the package manager, run
37 |
38 | ```shell
39 | brew install cmake ninja llvm doxygen
40 | ```
41 |
42 | Then, check your installed version of cmake, ninja, llvm, and clang by
43 |
44 | ```shell
45 | cmake --version
46 | ninja --version
47 | llvm-config --version
48 | clang --version
49 | doxygen --version
50 | ```
51 |
52 | ### On Ubuntu 22.04
53 |
54 | Using [llvm apt source](https://apt.llvm.org/), run
55 |
56 | ```shell
57 | sudo apt update
58 | sudo apt install lsb-release wget software-properties-common gnupg zlib1g zlib1g-dev git cmake ninja-build build-essential doxygen graphviz
59 | wget https://apt.llvm.org/llvm.sh
60 | chmod +x llvm.sh
61 | ./llvm.sh 17 all
62 | ```
63 |
64 | Then, check your installed version of cmake, ninja, llvm, and clang by
65 |
66 | ```shell
67 | cmake --version
68 | ninja --version
69 | clang-17 --version
70 | llvm-config-17 --version
71 | doxygen --version
72 | ```
73 |
74 | ## Get the Project
75 |
76 | ```shell
77 | git clone https://github.com/JacyCui/cpp-static-analyzer.git
78 | ```
79 |
80 | ### Build
81 |
82 | In the project root directory, run
83 |
84 | ```shell
85 | mkdir build
86 | cd build
87 | cmake -G=Ninja ..
88 | ninja
89 | ```
90 |
91 | ### Run Tests
92 |
93 | After compiling, **in the project root directory**, run
94 |
95 | ```shell
96 | ./build/tests/tests
97 | ```
98 |
99 | And then, you should see something like below, which means the **627**
100 | testing assertions are passed.
101 |
102 | ```shell
103 | # a lot of log information here ...
104 | ===============================================================================
105 | [doctest] test cases: 35 | 35 passed | 0 failed | 0 skipped
106 | [doctest] assertions: 627 | 627 passed | 0 failed |
107 | [doctest] Status: SUCCESS!
108 | ```
109 |
110 | ### Run the Example Dataflow Analysis
111 |
112 | After compiling, **in the project root directory**, run
113 |
114 | ```shell
115 | ./build/tools/reaching-definition-analyzer --source-dir=resources/dataflow/ReachDef
116 | ```
117 |
118 | This will run the reaching definition analysis
119 | for all source files in the `resources/dataflow/ReachDef` directory.
120 |
121 | ```shell
122 | ./build/tools/reaching-definition-analyzer --help
123 | A Simple CPP Reaching Definition Static Analyzer
124 | Copyright (c) 2023-2023
125 | Usage: ./build/tools/reaching-definition-analyzer/reaching-definition-analyzer [OPTIONS]
126 |
127 | Options:
128 | -h,--help Print this help message and exit
129 | -S,--source-dir TEXT REQUIRED
130 | directory of all source files
131 | -I,--include-dir TEXT directory of all header files
132 | --std,--standard TEXT c++ language standard (support all standards that clang supports)
133 | ```
134 |
135 | Similarly, **in the project root directory**, run
136 |
137 | ```shell
138 | ./build/tools/live-variable-analyzer --source-dir=resources/dataflow/LiveVar
139 | ```
140 |
141 | This will run the live variable analysis
142 | for all source files in the `resources/dataflow/LiveVar` directory.
143 |
144 | ```shell
145 | ./build/tools/live-variable-analyzer --help
146 | A Simple CPP Live Variable Static Analyzer
147 | Copyright (c) 2023-2023
148 | Usage: ./build/tools/live-variable-analyzer [OPTIONS]
149 |
150 | Options:
151 | -h,--help Print this help message and exit
152 | -S,--source-dir TEXT REQUIRED
153 | directory of all source files
154 | -I,--include-dir TEXT directory of all header files
155 | --std,--standard TEXT c++ language standard (support all standards that clang supports)
156 | ```
157 |
158 | ## How to use it as a library in your project
159 |
160 | ### Linking Steps
161 |
162 | **Step01**: Take this repository as a submodule of your project repository.
163 |
164 | ```shell
165 | git submodule add https://github.com/JacyCui/cpp-static-analyzer.git path/to/put/this/project
166 | ```
167 |
168 | **Step02**: Link to `libanalyzer` in you `CMakeLists.txt`.
169 |
170 | ```cmake
171 | # suppose your target is called your_target
172 |
173 | add_subdirectory(path/to/put/this/project)
174 |
175 | target_include_directories(your_target
176 | PUBLIC path/to/put/this/project/include
177 | )
178 |
179 | target_link_libraries(your_target
180 | libanalyzer
181 | )
182 | ```
183 |
184 | **Step03**: Use APIs provided [here](https://jacycui.github.io/cpp-static-analyzer/) in your source code.
185 | An example usage is provided in the [test of reaching definition](tests/TestReachingDefinition.cpp).
186 |
187 |
188 | ### Get the API Documentation
189 |
190 | You can build the html doxygen api documentation locally
191 | **in the build directory** by running
192 |
193 | ```shell
194 | # in the build directory
195 | ninja libanalyzer-api-doc
196 | ```
197 |
198 | And you'll find your html documentation located at `build/docs/api-doc/html` directory.
199 |
200 | You can read it by opening `build/docs/api-doc/html/index.html` in your local web browser.
201 |
202 | Note that the documentation is not included in the default build target.
203 |
204 | You must build it explicitly like above if you need to read it.
205 |
206 |
--------------------------------------------------------------------------------
/docs/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | find_package(Doxygen
2 | REQUIRED dot
3 | OPTIONAL_COMPONENTS mscgen dia)
4 |
5 | set(DOXYGEN_OUTPUT_DIRECTORY api-doc)
6 |
7 | doxygen_add_docs(libanalyzer-api-doc
8 | ${PROJECT_SOURCE_DIR}/include
9 | ${PROJECT_SOURCE_DIR}/README.md
10 | ${PROJECT_SOURCE_DIR}/docs
11 | COMMENT "Generate html documentation of libanalyzer."
12 | )
13 |
--------------------------------------------------------------------------------
/docs/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guide
2 |
3 | ## Commit Message Convention
4 |
5 | Grammar for commit messages:
6 |
7 | ```text
8 | :
9 |
10 | [optional body]
11 | ```
12 |
13 | The commit type `` can include the following:
14 |
15 | - `feat` – a new feature is introduced with the changes
16 | - `fix` – a bug fix has occurred
17 | - `chore` – changes that do not relate to a fix or feature and don't modify src or test files (for example updating dependencies)
18 | - `refactor` – refactored code that neither fixes a bug nor adds a feature
19 | - `docs` – updates to documentation such as the README or other markdown files
20 | - `style` – changes that do not affect the meaning of the code, likely related to code formatting such as white-space, missing semi-colons, and so on.
21 | - `test` – including new or correcting previous tests
22 | - `perf` – performance improvements
23 | - `ci` – continuous integration related
24 | - `build` – changes that affect the build system or external dependencies
25 | - `revert` – reverts a previous commit
26 |
27 | The commit type should be all lowercase and limited to the above 11 types. `` should be a brief description of the commit and should not exceed one line. If you have more to describe, put them in
28 | the optional body.
29 |
30 | ## Comment Convention
31 |
32 | Doxygen recognizable comments should be provided for each class and methods declared in the header files.
33 |
34 | Method that overrides a base class method can just inherit comments in the base class.
35 |
36 | You can take `include/World.h` as an example.
37 |
--------------------------------------------------------------------------------
/docs/DataflowAnalysis.md:
--------------------------------------------------------------------------------
1 | # Dataflow Analysis
2 |
3 | > You should first read the tutorial about program representation before proceeding to this guide.
4 |
5 |
6 |
7 | ## High Level Overview of an Analysis
8 |
9 | An analysis is an algorithm that takes a program and some configuration as input and returns some results. Typically, results are bound to statements.
10 |
11 | ```mermaid
12 | ---
13 | title: Top Interfaces for an Analsis
14 | ---
15 | classDiagram
16 | class Analysis {
17 | <>
18 | +getAnalysisConfig() AnalysisConfig
19 | }
20 |
21 | class AnalysisConfig {
22 | <>
23 | +getDescription()* String
24 | }
25 |
26 | note for MethodAnalysis "R is a generic result type of some analysis"
27 |
28 | class MethodAnalysis~R~ {
29 | <>
30 | +analyze(IR)* R
31 | }
32 |
33 | Analysis <|-- MethodAnalysis
34 | Analysis o-- AnalysisConfig
35 |
36 | note for StmtResult "R is a generic result type for a single statement"
37 |
38 | class StmtResult~R~ {
39 | <>
40 | +isRelevant(Stmt)* bool
41 | +getResult(Stmt)* R
42 | }
43 |
44 | MethodAnalysis --> StmtResult
45 | ```
46 |
47 | You can easily create your own classes to override these interfaces in order to develop your own analysis.
48 |
49 |
50 |
51 | ## Dataflow Analysis Framework
52 |
53 | This project provide a complete and easy-to-use framework to develop dataflow analysis.
54 |
55 | ```mermaid
56 | ---
57 | title: Dataflow Analysis Framework
58 | ---
59 | classDiagram
60 | class MethodAnalysis~R~ {
61 | <>
62 | +analyze(IR)* R
63 | }
64 |
65 | class AnalysisDriver {
66 | <>
67 | +analyze(IR) DataflowResult
68 | #makeAnalysis()* DataflowAnalysis
69 | }
70 |
71 | MethodAnalysis <|-- AnalysisDriver
72 | AnalysisDriver --> Solver
73 | AnalysisDriver --> DataflowAnalysis
74 | AnalysisDriver --> DataflowResult
75 |
76 | class Solver {
77 | <>
78 | +solve(DataflowAnalysis)* DataflowResult
79 | }
80 |
81 | class AbstractSolver {
82 | <>
83 | +solve(DataflowAnalysis) DataflowResult
84 | #initializeForward(DataflowAnalysis, DataflowResult)
85 | #initializeBackward(DataflowAnalysis, DataflowResult)
86 | #doSolveForward(DataflowAnalysis, DataflowResult)*
87 | #doSolveBackward(DataflowAnalysis, DataflowResult)*
88 | }
89 |
90 | Solver <|-- AbstractSolver
91 |
92 | class DataflowAnalysis {
93 | <>
94 | +isForward()* bool
95 | +newBoundaryFact()* Fact
96 | +newInitialFact()* Fact
97 | +meetInto(Fact fact, Fact target)*
98 | +transferNode(Stmt, Fact in, Fact out)* bool
99 | +getCFG() CFG
100 | }
101 |
102 | NodeResult~Fact~ <|-- DataflowResult~Fact~
103 |
104 | class NodeResult~Fact~ {
105 | +getInFact(Stmt)* Fact
106 | +getOutFact(Stmt)* Fact
107 | }
108 |
109 | class DataflowResult~Fact~ {
110 | +getInFact(Stmt) Fact
111 | +getOutFact(Stmt) Fact
112 | +setInFact(Stmt) Fact
113 | +setOutFact(Stmt) Fact
114 | }
115 | ```
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/docs/ProgramRepresentation.md:
--------------------------------------------------------------------------------
1 | # Program Representation
2 |
3 |
4 |
5 | > It is highly recommended that you generate API documentation first accroding to project `README` before working through this tutorial. The *API documentation* mentioned below all refers to the one generated according to project `README`.
6 |
7 |
8 |
9 | ## World
10 |
11 | ```cpp
12 | #include "World.h"
13 |
14 | namespace al=analyzer;
15 | namespace lang=al::language;
16 |
17 | al::World::initialize("resources/example01/src", "resources/example01/include");
18 | const World& world = al::World::get();
19 | ```
20 |
21 | All resources about the program representation is managed by the only singleton `World` object, which can be accessed through `get` static method of `World` class. And this `world` object is immutable.
22 |
23 | A CPP program is divided into several `CPPMethod` objects when the `World` is initialized. You can get them through a bunch of methods provided by the `world` object. Several ways to get cpp methods through `world` are listed below.
24 |
25 | ```cpp
26 | const std::unordered_map>& getAllMethods() const;
27 | std::shared_ptr getMethodBySignature(const std::string& signature) const;
28 | std::shared_ptr getMainMethod() const;
29 | ```
30 |
31 | Refer to API documentation for more information about these methods.
32 |
33 |
34 |
35 | ## CPPMethod
36 |
37 | A `CPPMethod` object contains all information that you'll need to perform an intra-procedural analysis or build a whole-program analysis. There's also a bunch of convenient methods provided in this class. Frequently used methods are listed below.
38 |
39 | ```cpp
40 | const std::string& getMethodSignatureAsString() const;
41 | std::string getMethodSourceCode() const;
42 | std::string getContainingFilePath() const;
43 | std::size_t getParamCount() const;
44 | std::shared_ptr getParamType(std::size_t i) const;
45 | const std::string& getParamName(std::size_t i) const;
46 | std::shared_ptr getReturnType() const;
47 | std::shared_ptr getIR();
48 | bool isGlobalMethod() const;
49 | bool isClassStaticMethod() const;
50 | bool isClassMemberMethod() const;
51 | bool isVirtual() const;
52 | ```
53 |
54 | Refer to API documentation for more detailed information about these methods.
55 |
56 | The most important method above is `getIR`, whose first call triggers the build of intermediate representation of this method using the `IRBuilder` provided by `world`.
57 |
58 |
59 |
60 | ## IR and CFG
61 |
62 | IR and CFG are basic infrastructures for most static analysis algorithms. The Construction of them are based on the interfaces below.
63 |
64 | ```mermaid
65 | ---
66 | title: How World Supports IR and CFG building
67 | ---
68 | classDiagram
69 | class World {
70 | +getTypeBuilder() TypeBuilder
71 | +getIRBuilder() IRBuilder
72 | +getVarBuilder() VarBuilder
73 | +getStmtBuilder() StmtBuilder
74 | +......()
75 | }
76 |
77 | class TypeBuilder {
78 | <>
79 | +buildType(ClangQualType)* Type
80 | }
81 |
82 | class IRBuilder {
83 | <>
84 | +buildIR(CPPMethod)* IR
85 | }
86 |
87 | class StmtBuilder {
88 | <>
89 | +buildStmt(CPPMethod, ClangStmt, Map~ClangVarDecl, Var~)* Stmt
90 | }
91 |
92 | class VarBuilder {
93 | <>
94 | +buildVar(CPPMethod, ClangVarDecl)* Var
95 | }
96 |
97 | World o-- TypeBuilder
98 | World o-- IRBuilder
99 | World o-- StmtBuilder
100 | World o-- VarBuilder
101 |
102 | class Type {
103 | <>
104 | +getName()*: String
105 | }
106 |
107 | class IR {
108 | <>
109 | +getMethod()* CPPMethod
110 | +getCFG()* CFG
111 | +getParams()* List~Var~
112 | +getVars()* List~Var~
113 | +getStmts()* List~Stmt~
114 | }
115 |
116 | class Stmt {
117 | <>
118 | +getStartLine()* int
119 | +getEndLine()* int
120 | +getStartColumn()* int
121 | +getEndColumn()* int
122 | +getMethod()* CPPMethod
123 | +getDefs()* Set~Var~
124 | +getUses()* Set~Var~
125 | +str()* String
126 | }
127 |
128 | class Var {
129 | <>
130 | +getMethod()* CPPMethod
131 | +getName()* String
132 | +getType()* Type
133 | }
134 |
135 | class CFG {
136 | <>
137 | +getEntry()* Stmt
138 | +getExit()* Stmt
139 | +hasEdge(Stmt source, Stmt target)* bool
140 | +getPredsOf(Stmt)* Set~Stmt~
141 | +getSuccsOf(Stmt)* Set~Stmt~
142 | +getInEdgesOf(Stmt)* Set~CFGEdge~
143 | +getOutEdgesOf(Stmt)* Set~CFGEdge~
144 | +getIR()* IR
145 | +getEdgeNum()* int
146 | }
147 |
148 | IRBuilder <.. StmtBuilder
149 | IRBuilder <.. VarBuilder
150 | IRBuilder <.. TypeBuilder
151 |
152 | TypeBuilder --> Type
153 | IRBuilder --> IR
154 | StmtBuilder --> Stmt
155 | VarBuilder --> Var
156 | IR <.. CFG
157 | ```
158 |
159 | All the above interfaces have a default implementation.
160 |
161 | Since they are all interfaces and they all rely on interfaces, you can easily substitute my default implementation by your own implementation of one part without having to change any other part.
162 |
163 |
164 |
165 | ## How to get more details
166 |
167 | The code of this project is well commented, documented and tested, so that the best way to get familiar with this project is **reading the tests** and meanwhile **seaching through header files and api documentations**.
168 |
169 |
170 |
171 |
--------------------------------------------------------------------------------
/include/World.h:
--------------------------------------------------------------------------------
1 | #ifndef STATIC_ANALYZER_WORLD_H
2 | #define STATIC_ANALYZER_WORLD_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include
10 | #include
11 |
12 | #include "language/CPPMethod.h"
13 | #include "ir/IR.h"
14 | #include "util/Logger.h"
15 |
16 | namespace analyzer {
17 |
18 | namespace lang = language;
19 |
20 | /**
21 | * @class WorldBuilder
22 | * @brief Interface for the world builder
23 | */
24 | class WorldBuilder {
25 | public:
26 |
27 | /**
28 | * @brief build the world
29 | */
30 | virtual void build() = 0;
31 |
32 | virtual ~WorldBuilder() = default;
33 |
34 | };
35 |
36 | /**
37 | * @class World
38 | * @brief Manages the whole-program information of the program being analyzed.
39 | */
40 | class World final: public WorldBuilder {
41 | public:
42 |
43 | /**
44 | * @brief Initialize the word the whole program, including ast, cfg and call graph
45 | * @param sourceDir the directory path of all source files
46 | * @param includeDir the directory path of all
47 | * @param std language standard, e.g. c++98, c++11, c99
48 | * @param optArgs optional compilation arguments, such as -D__MACRO__ etc.
49 | */
50 | static void initialize(const std::string& sourceDir, const std::string& includeDir="",
51 | const std::string& std=std::string("c++98"),
52 | const std::vector& optArgs={});
53 |
54 | /**
55 | * @brief must be called after calling {@code initialize}
56 | * @return const reference to the world instance created by {@code initialize}
57 | */
58 | [[nodiscard]] static const World& get();
59 |
60 | /**
61 | * @return the current logger of the entire world
62 | */
63 | [[nodiscard]] static util::Logger& getLogger();
64 |
65 | /**
66 | * @param newLogger a new logger to be used from now on
67 | */
68 | static void setLogger(util::Logger newLogger);
69 |
70 | public:
71 |
72 | void build() override;
73 |
74 | /**
75 | * @return a map from source file path to source file content
76 | */
77 | [[nodiscard]] const std::unordered_map& getSourceCode() const;
78 |
79 | /**
80 | * @return the ASTUnit list of the whole program
81 | */
82 | [[nodiscard]] const std::vector>& getAstList() const;
83 |
84 | /**
85 | * @brief pretty dump asts of all source codes in the world into a stream
86 | * @param[out] out the llvm raw out stream (e.g. outs(), errs() or other user defined streams)
87 | */
88 | void dumpAST(llvm::raw_ostream& out) const;
89 |
90 | /**
91 | * @brief pretty dump asts of a given source file
92 | * @param fileName relative path of the source file from current working directory
93 | * @param[out] out the llvm raw out stream (e.g. outs(), errs() or other user defined streams)
94 | */
95 | void dumpAST(const std::string& fileName, llvm::raw_ostream& out) const;
96 |
97 | /**
98 | * @return return a vector of all methods in the program
99 | */
100 | [[nodiscard]] const std::unordered_map>& getAllMethods() const;
101 |
102 | /**
103 | * @brief get the cpp method by it's method
104 | * @param signature signature string (e.g. int add(int, int))
105 | * @return a cpp method, nullptr if it doesn't exist
106 | */
107 | [[nodiscard]] std::shared_ptr getMethodBySignature(const std::string& signature) const;
108 |
109 | /**
110 | * @return return the main method (nullptr if there's no main method)
111 | */
112 | [[nodiscard]] std::shared_ptr getMainMethod() const;
113 |
114 | /**
115 | * @return the global ir builder of this world
116 | */
117 | [[nodiscard]] const std::unique_ptr& getIRBuilder() const;
118 |
119 | /**
120 | * @return the global type builder of this world
121 | */
122 | [[nodiscard]] const std::unique_ptr& getTypeBuilder() const;
123 |
124 | /**
125 | * @return the global variable builder of this world
126 | */
127 | [[nodiscard]] const std::unique_ptr& getVarBuilder() const;
128 |
129 | /**
130 | * @return the global statement builder of this world
131 | */
132 | [[nodiscard]] const std::unique_ptr& getStmtBuilder() const;
133 |
134 | private:
135 |
136 | static World* theWorld; ///< the only world instance in the program
137 |
138 | static util::Logger logger; ///< the logger for the whole world
139 |
140 | private:
141 |
142 | std::unordered_map sourceCode; ///< sourcefile name -> sourcefile content
143 |
144 | std::vector args; ///< compiler arguments
145 |
146 | std::vector> astList; ///< asts of a program
147 |
148 | std::unordered_map> allMethods; ///< all cpp methods in the program
149 |
150 | std::shared_ptr mainMethod; ///< main method
151 |
152 | std::unique_ptr irBuilder; ///< global ir builder
153 |
154 | std::unique_ptr typeBuilder; ///< global type builder
155 |
156 | std::unique_ptr varBuilder; ///< global variable builder
157 |
158 | std::unique_ptr stmtBuilder; ///< global statement builder
159 |
160 | /**
161 | * @brief Construct the world
162 | * @param sourceCode cpp or c source file and corresponding code string
163 | * @param args compiler arguments
164 | */
165 | World(std::unordered_map&& sourceCode,
166 | std::vector&& args);
167 |
168 | /**
169 | * @brief build a map from method signature to CPPMethod
170 | */
171 | void buildMethodMap();
172 |
173 | public:
174 |
175 | World(const World&) = delete;
176 |
177 | World& operator=(const World&) = delete;
178 |
179 | };
180 |
181 | /**
182 | * @brief get c/cpp source codes recursively from a source file directory
183 | * @param sourceDir the directory containing all the source files
184 | * @return a map from filename(relative, end with .c / .cpp / .cxx / .cc) to its contents
185 | */
186 | [[nodiscard]] std::unordered_map loadSourceCodes(const std::string& sourceDir);
187 |
188 | namespace language {
189 |
190 | /**
191 | * @brief generate string signature of a function decl ast
192 | * @param functionDecl function declaration ast
193 | * @return a string representation of signature
194 | */
195 | [[nodiscard]] std::string generateFunctionSignature(const clang::FunctionDecl* functionDecl);
196 |
197 | }
198 |
199 | } // analyzer
200 |
201 | #endif //STATIC_ANALYZER_WORLD_H
202 |
--------------------------------------------------------------------------------
/include/analysis/Analysis.h:
--------------------------------------------------------------------------------
1 | #ifndef STATIC_ANALYZER_ANALYSIS_H
2 | #define STATIC_ANALYZER_ANALYSIS_H
3 |
4 | #include
5 |
6 | #include "config/AnalysisConfig.h"
7 | #include "ir/IR.h"
8 |
9 | namespace analyzer::analysis {
10 |
11 | /**
12 | * @class Analysis
13 | * @brief abstract base class for all analysis
14 | */
15 | class Analysis {
16 | public:
17 |
18 | [[nodiscard]] const std::unique_ptr& getAnalysisConfig() const;
19 |
20 | virtual ~Analysis() = default;
21 |
22 | protected:
23 |
24 | /**
25 | * @brief construct an analysis from its configuration
26 | * @param analysisConfig a unique configuration object
27 | */
28 | explicit Analysis(std::unique_ptr& analysisConfig)
29 | :analysisConfig(std::move(analysisConfig))
30 | {
31 |
32 | }
33 |
34 | std::unique_ptr analysisConfig; ///< the configuration of this analysis
35 |
36 | };
37 |
38 | /**
39 | * @class MethodAnalysis
40 | * @brief abstract base class for all method analyses, i.e. intra-procedural analyses.
41 | * @tparam R the result type
42 | */
43 | template
44 | class MethodAnalysis: public Analysis {
45 | public:
46 |
47 | /**
48 | * @brief analyze an intermediate representation to get the result
49 | * @param myIR the ir to be analyzed
50 | * @return the method analysis result
51 | */
52 | [[nodiscard]] virtual std::shared_ptr analyze(std::shared_ptr myIR) = 0;
53 |
54 | protected:
55 |
56 | explicit MethodAnalysis(std::unique_ptr& analysisConfig)
57 | :Analysis(analysisConfig)
58 | {
59 |
60 | }
61 |
62 | };
63 |
64 | } // analysis
65 |
66 | #endif //STATIC_ANALYZER_ANALYSIS_H
67 |
--------------------------------------------------------------------------------
/include/analysis/AnalysisResult.h:
--------------------------------------------------------------------------------
1 | #ifndef STATIC_ANALYZER_ANALYSISRESULT_H
2 | #define STATIC_ANALYZER_ANALYSISRESULT_H
3 |
4 | #include "ir/Stmt.h"
5 |
6 | namespace analyzer::analysis {
7 |
8 | /**
9 | * @class StmtResult
10 | * @brief An interface for querying analysis results of Stmt.
11 | * @tparam R type of analysis results
12 | */
13 | template
14 | class StmtResult {
15 | public:
16 |
17 | /**
18 | * @brief judge the relevance of a given statement
19 | * @param stmt a statement
20 | * @return true if the stmt is relevant to this result
21 | */
22 | [[nodiscard]] virtual bool isRelevant(std::shared_ptr stmt) const = 0;
23 |
24 | /**
25 | * @brief get the result of a given statement
26 | * @param stmt statement to get result
27 | * @return the analysis result of stmt, nullptr if it doesn't exist
28 | */
29 | [[nodiscard]] virtual std::shared_ptr getResult(std::shared_ptr stmt) const = 0;
30 |
31 | virtual ~StmtResult() = default;
32 |
33 | };
34 |
35 | }
36 |
37 | #endif //STATIC_ANALYZER_ANALYSISRESULT_H
38 |
--------------------------------------------------------------------------------
/include/analysis/dataflow/AnalysisDriver.h:
--------------------------------------------------------------------------------
1 | #ifndef STATIC_ANALYZER_ANALYSISDRIVER_H
2 | #define STATIC_ANALYZER_ANALYSISDRIVER_H
3 |
4 | #include "analysis/Analysis.h"
5 |
6 | #include "analysis/dataflow/solver/Solver.h"
7 |
8 | namespace analyzer::analysis::dataflow {
9 |
10 | /**
11 | * @brief an abstract analysis driver for all dataflow analysis
12 | * @class AnalysisDriver
13 | * @tparam Fact the dataflow fact type
14 | */
15 | template
16 | class AnalysisDriver: public MethodAnalysis> {
17 | public:
18 |
19 | /**
20 | * @brief analyze the given ir to get the dataflow result
21 | * @param myIR the ir to be analyzed
22 | * @return the dataflow result
23 | */
24 | [[nodiscard]] std::shared_ptr>
25 | analyze(std::shared_ptr myIR) override
26 | {
27 | World::getLogger().Progress("Start dataflow analysis: " + this->analysisConfig->getDescription());
28 | std::shared_ptr cfg = myIR->getCFG();
29 |
30 | World::getLogger().Info("Getting dataflow analysis algorithm ...");
31 | std::unique_ptr> dataflowAnalysis = makeAnalysis(cfg);
32 |
33 | World::getLogger().Info("Getting dataflow analysis solver (worklist solver by default) ...");
34 | std::unique_ptr> mySolver = solver::makeSolver();
35 |
36 | World::getLogger().Info("Solving the dataflow analysis ...");
37 | std::shared_ptr> result = mySolver->solve(dataflowAnalysis);
38 |
39 | World::getLogger().Success("Finish dataflow analysis: " + this->analysisConfig->getDescription());
40 | return result;
41 | }
42 |
43 | protected:
44 |
45 | /**
46 | * @brief Creates an analysis object for given cfg.
47 | * @param cfg a control flow graph
48 | * @return a dataflow analysis object
49 | */
50 | virtual std::unique_ptr>
51 | makeAnalysis(const std::shared_ptr& cfg) const = 0;
52 |
53 | /**
54 | * @brief Construct an analysis driver from analysis config
55 | * @param analysisConfig the configuration of this analysis
56 | */
57 | explicit AnalysisDriver(std::unique_ptr& analysisConfig)
58 | :MethodAnalysis>(analysisConfig)
59 | {
60 |
61 | }
62 |
63 | };
64 |
65 | } // dataflow
66 |
67 | #endif //STATIC_ANALYZER_ANALYSISDRIVER_H
68 |
--------------------------------------------------------------------------------
/include/analysis/dataflow/ConstantPropagation.h:
--------------------------------------------------------------------------------
1 | #ifndef STATIC_ANALYZER_CONSTANTPROPAGATION_H
2 | #define STATIC_ANALYZER_CONSTANTPROPAGATION_H
3 |
4 | #include
5 | #include
6 |
7 | #include "analysis/dataflow/AnalysisDriver.h"
8 | #include "analysis/dataflow/fact/MapFact.h"
9 | #include "llvm/IR/Constants.h"
10 |
11 | namespace analyzer::analysis::dataflow {
12 |
13 | /**
14 | * @class CPValue
15 | * @brief constant propagation value
16 | */
17 | class CPValue {
18 | public:
19 |
20 | /**
21 | * @return the Undefined value of constant propagation
22 | */
23 | [[nodiscard]] static std::shared_ptr getUndef();
24 |
25 | /**
26 | * @return the Not A Constant value of constant propagation
27 | */
28 | [[nodiscard]] static std::shared_ptr getNAC();
29 |
30 | /**
31 | * @brief make a constant value for constant propagation
32 | * @param constantValue the constant value
33 | * @return the constant value for constant propagation
34 | */
35 | [[nodiscard]] static std::shared_ptr makeConstant(const llvm::APSInt &constantValue);
36 |
37 | public:
38 |
39 | /**
40 | * @class CPValueType
41 | * @brief constant propagation value kind
42 | */
43 | enum class Kind {
44 | UNDEF, ///< undefined value for const propagation
45 | NAC, ///< Not A Constant value for const propagation
46 | CONSTANT ///< constant value for const propagation
47 | };
48 |
49 | /**
50 | * @brief constructor for constant propagation value
51 | * @param kind the constant propagation value type
52 | * @param constantValue the constant value
53 | */
54 | explicit CPValue(Kind kind, llvm::APSInt constantValue = llvm::APSInt());
55 |
56 | /**
57 | * @return true if the constant propagation value is undefined, false otherwise
58 | */
59 | [[nodiscard]] bool isUndef() const;
60 |
61 | /**
62 | * @return true if the constant propagation value is not a constant, false otherwise
63 | */
64 | [[nodiscard]] bool isNAC() const;
65 |
66 | /**
67 | * @return true if the constant propagation value is a constant, false otherwise
68 | */
69 | [[nodiscard]] bool isConstant() const;
70 |
71 | /**
72 | * @return the constant value
73 | */
74 | [[nodiscard]] const llvm::APSInt& getConstantValue() const;
75 |
76 | /**
77 | * @return the string representation of this constant propagation value
78 | */
79 | [[nodiscard]] std::string str() const;
80 |
81 | /**
82 | * @brief operator== for constant propagation value
83 | * @param other the other constant propagation value
84 | * @return true if the constant propagation value is equal to the other constant propagation value, false otherwise
85 | */
86 | bool operator==(const CPValue& other) const {
87 | return kind == other.kind && constantValue == other.constantValue;
88 | }
89 |
90 | private:
91 |
92 | static std::shared_ptr Undef, NAC; ///< two special constant propagation value
93 |
94 | private:
95 |
96 | Kind kind; ///< constant propagation value kind
97 |
98 | llvm::APSInt constantValue; ///< constant value
99 |
100 | };
101 |
102 | /**
103 | * @class CPFact
104 | * @brief constant propagation fact
105 | */
106 | class CPFact: public fact::MapFact {
107 | public:
108 |
109 | /**
110 | * @brief get the CPValue of a given var
111 | * @param key the var to be searched
112 | * @return the CPValue to which the specified key is mapped,
113 | * or Undef if this map contains no mapping for the given var
114 | */
115 | [[nodiscard]] std::shared_ptr get(const std::shared_ptr& key) const override;
116 |
117 | /**
118 | * @brief Updates the key-value mapping in this fact.
119 | * @param key the var to update
120 | * @param value the CPValue to be bound to the var
121 | * @return true if the update changes this fact, otherwise
122 | */
123 | bool update(const std::shared_ptr& key, const std::shared_ptr& value) override;
124 |
125 | };
126 |
127 |
128 | /**
129 | * @class CPResult
130 | * @brief constant propagation result
131 | */
132 | class CPResult : public fact::DataflowResult {
133 | public:
134 |
135 | /**
136 | * @brief constructor for constant propagation result
137 | */
138 | explicit CPResult();
139 |
140 | /**
141 | * @brief update the constant propagation value of a given clang expr
142 | * @param expr the clang expr to be updated
143 | * @param value the constant propagation value to be bound to the clang expr
144 | */
145 | void updateExprValue(const clang::Expr* expr, const std::shared_ptr& value);
146 |
147 | /**
148 | * @brief get the constant propagation value of a given clang expr
149 | * @param expr the clang expr to be searched
150 | * @return the constant propagation value to which the specified clang expr is mapped,
151 | * or nullptr if this map contains no mapping for the given clang expr
152 | */
153 | [[nodiscard]] std::shared_ptr getExprValue(const clang::Expr* expr) const;
154 |
155 | private:
156 |
157 | std::unordered_map>
158 | exprValues; ///< map from clang expression to constant propagation dataflow value
159 |
160 | };
161 |
162 | /**
163 | * @class ConstantPropagation
164 | * @brief constant propagation analysis
165 | */
166 | class ConstantPropagation: public AnalysisDriver {
167 | public:
168 |
169 | /**
170 | * @brief constructor for constant propagation analysis
171 | * @param analysisConfig the analysis configuration
172 | */
173 | explicit ConstantPropagation(std::unique_ptr& analysisConfig);
174 |
175 | protected:
176 |
177 | [[nodiscard]] std::unique_ptr>
178 | makeAnalysis(const std::shared_ptr& cfg) const override;
179 |
180 | };
181 |
182 | } // dataflow
183 |
184 |
185 | #endif //STATIC_ANALYZER_CONSTANTPROPAGATION_H
186 |
--------------------------------------------------------------------------------
/include/analysis/dataflow/DataflowAnalysis.h:
--------------------------------------------------------------------------------
1 | #ifndef STATIC_ANALYZER_DataflowAnalysis_H
2 | #define STATIC_ANALYZER_DataflowAnalysis_H
3 |
4 | #include "World.h"
5 | #include "analysis/graph/CFG.h"
6 | #include "analysis/Analysis.h"
7 | #include "analysis/dataflow/fact/DataflowResult.h"
8 |
9 | namespace analyzer::analysis::dataflow {
10 |
11 | /**
12 | * @brief Template interface for defining data-flow analysis.
13 | * @tparam Fact type of data-flow facts
14 | */
15 | template
16 | class DataflowAnalysis {
17 | public:
18 |
19 | /**
20 | * @return true if this analysis is forward, otherwise false.
21 | */
22 | [[nodiscard]] virtual bool isForward() const = 0;
23 |
24 | /**
25 | * @return new fact in boundary conditions, i.e., the fact for
26 | * entry (exit) node in forward (backward) analysis.
27 | */
28 | [[nodiscard]] virtual std::shared_ptr newBoundaryFact() const = 0;
29 |
30 | /**
31 | * @return new initial fact for non-boundary nodes.
32 | */
33 | [[nodiscard]] virtual std::shared_ptr newInitialFact() const = 0;
34 |
35 | /**
36 | * @brief Meets a fact into another (target) fact.
37 | * This function will be used to handle control-flow confluences.
38 | * @param fact fact to be meet
39 | * @param target target fact
40 | */
41 | virtual void meetInto(std::shared_ptr fact, std::shared_ptr target) const = 0;
42 |
43 | /**
44 | * @brief Node Transfer function for the analysis.
45 | * The function transfers data-flow from in (out) fact to out (in) fact
46 | * for forward (backward) analysis.
47 | * @param stmt stmt to be transferred
48 | * @param in in facts
49 | * @param out out facts
50 | * @return true if the transfer changed the out (in) fact, otherwise false.
51 | */
52 | virtual bool transferNode(std::shared_ptr stmt, std::shared_ptr in, std::shared_ptr out) const = 0;
53 |
54 | /**
55 | * @brief By default, a data-flow analysis does not have edge transfer, i.e.,
56 | * does not need to perform transfer for any edges.
57 | * @param edge the edge to check
58 | * @return true if this analysis needs to perform transfer for given edge, otherwise false.
59 | */
60 | [[nodiscard, maybe_unused]] virtual bool needTransferEdge(std::shared_ptr edge) const = 0;
61 |
62 | /**
63 | * @brief Edge Transfer function for this analysis.
64 | * Note that this function should NOT modify nodeFact.
65 | * @param edge the edge that the transfer function is applied on
66 | * @param nodeFact the fact of the source node of the edge. Note that
67 | * which node is the source node of an edge depends on
68 | * the direction of the analysis.
69 | * @return the resulting edge fact
70 | */
71 | [[nodiscard, maybe_unused]] virtual std::shared_ptr transferEdge
72 | (std::shared_ptr edge, std::shared_ptr nodeFact) const = 0;
73 |
74 | /**
75 | * @return the control-flow graph that this analysis works on.
76 | */
77 | [[nodiscard]] virtual std::shared_ptr getCFG() const = 0;
78 |
79 | /**
80 | * @return the dataflow result to be modified by the solver
81 | * ( Note: called by the solver only once )
82 | */
83 | [[nodiscard]] virtual std::shared_ptr> getResult() const = 0;
84 |
85 | virtual ~DataflowAnalysis() = default;
86 |
87 | };
88 |
89 | /**
90 | * @class AbstractDataflowAnalysis
91 | * @brief An abstract dataflow analysis that implements some general logic
92 | * @tparam Fact dataflow fact type
93 | */
94 | template
95 | class AbstractDataflowAnalysis: public DataflowAnalysis {
96 | public:
97 |
98 | [[nodiscard]] bool needTransferEdge(
99 | [[maybe_unused]] std::shared_ptr edge) const override
100 | {
101 | return false;
102 | }
103 |
104 | [[nodiscard]] std::shared_ptr transferEdge
105 | ([[maybe_unused]] std::shared_ptr edge,
106 | [[maybe_unused]] std::shared_ptr nodeFact) const override
107 | {
108 | World::getLogger().Error("Transfer Edge is unsupported in dataflow analysis by default.");
109 | throw std::runtime_error("Transfer Edge is unsupported in dataflow analysis by default.");
110 | }
111 |
112 | [[nodiscard]] std::shared_ptr getCFG() const override
113 | {
114 | return cfg;
115 | }
116 |
117 | protected:
118 |
119 | /**
120 | * @brief construct a dataflow analysis from a given cfg
121 | * @param cfg the cfg to be analyzed
122 | */
123 | explicit AbstractDataflowAnalysis(const std::shared_ptr& cfg)
124 | :cfg(cfg)
125 | {
126 |
127 | }
128 |
129 | std::shared_ptr cfg; ///< the cfg to be analyzed
130 |
131 | };
132 |
133 |
134 | } // dataflow
135 |
136 | #endif //STATIC_ANALYZER_DataflowAnalysis_H
137 |
--------------------------------------------------------------------------------
/include/analysis/dataflow/LiveVariable.h:
--------------------------------------------------------------------------------
1 | #ifndef CPP_STATIC_ANALYZER_LIVEVARIABLE_H
2 | #define CPP_STATIC_ANALYZER_LIVEVARIABLE_H
3 |
4 | #include
5 |
6 | #include "analysis/dataflow/AnalysisDriver.h"
7 | #include "analysis/dataflow/fact/SetFact.h"
8 |
9 | namespace analyzer::analysis::dataflow {
10 |
11 | /**
12 | * @class LiveVariable
13 | * @brief live variable analysis
14 | */
15 | class LiveVariable: public AnalysisDriver> {
16 | public:
17 |
18 | /**
19 | * @brief constructor for live variable analysis
20 | * @param analysisConfig the analysis configuration
21 | */
22 | explicit LiveVariable(std::unique_ptr& analysisConfig);
23 |
24 | protected:
25 |
26 | [[nodiscard]] std::unique_ptr>>
27 | makeAnalysis(const std::shared_ptr& cfg) const override;
28 |
29 | };
30 |
31 |
32 | } // dataflow
33 |
34 | #endif //CPP_STATIC_ANALYZER_LIVEVARIABLE_H
35 |
--------------------------------------------------------------------------------
/include/analysis/dataflow/ReachingDefinition.h:
--------------------------------------------------------------------------------
1 | #ifndef STATIC_ANALYZER_REACHINGDEFINITION_H
2 | #define STATIC_ANALYZER_REACHINGDEFINITION_H
3 |
4 | #include
5 |
6 | #include "analysis/dataflow/AnalysisDriver.h"
7 | #include "analysis/dataflow/fact/SetFact.h"
8 |
9 | namespace analyzer::analysis::dataflow {
10 |
11 | /**
12 | * @class ReachingDefinition
13 | * @brief reaching definition analysis
14 | */
15 | class ReachingDefinition: public AnalysisDriver> {
16 | public:
17 |
18 | /**
19 | * @brief constructor for reaching definition analysis
20 | * @param analysisConfig the analysis configuration
21 | */
22 | explicit ReachingDefinition(std::unique_ptr& analysisConfig);
23 |
24 | protected:
25 |
26 | [[nodiscard]] std::unique_ptr>>
27 | makeAnalysis(const std::shared_ptr& cfg) const override;
28 |
29 | };
30 |
31 |
32 | } // dataflow
33 |
34 |
35 | #endif //STATIC_ANALYZER_REACHINGDEFINITION_H
36 |
--------------------------------------------------------------------------------
/include/analysis/dataflow/fact/DataflowResult.h:
--------------------------------------------------------------------------------
1 | #ifndef STATIC_ANALYZER_DATAFLOWRESULT_H
2 | #define STATIC_ANALYZER_DATAFLOWRESULT_H
3 |
4 | #include
5 |
6 | #include "analysis/dataflow/fact/NodeResult.h"
7 |
8 | namespace analyzer::analysis::dataflow::fact {
9 |
10 | template
11 | class DataflowResult: public NodeResult {
12 | public:
13 |
14 | [[nodiscard]] std::shared_ptr getInFact(std::shared_ptr node) const override
15 | {
16 | if (inFacts.find(node) != inFacts.end()) {
17 | return inFacts.at(node);
18 | }
19 | return nullptr;
20 | }
21 |
22 | [[nodiscard]] std::shared_ptr getOutFact(std::shared_ptr node) const override
23 | {
24 | if (outFacts.find(node) != outFacts.end()) {
25 | return outFacts.at(node);
26 | }
27 | return nullptr;
28 | }
29 |
30 | /**
31 | * @brief Associates a dataflow fact with a node as its flowing-in fact.
32 | * @param node a statement
33 | * @param fact a dataflow fact
34 | */
35 | void setInFact(std::shared_ptr node, std::shared_ptr fact) {
36 | inFacts.insert_or_assign(node, fact);
37 | }
38 |
39 | /**
40 | * @brief Associates a dataflow fact with a node as its flowing-out fact.
41 | * @param node a statement
42 | * @param fact a dataflow fact
43 | */
44 | void setOutFact(std::shared_ptr node, std::shared_ptr fact) {
45 | outFacts.insert_or_assign(node, fact);
46 | }
47 |
48 | /**
49 | * @brief construct a predefined dataflow result
50 | * @param inFacts all in-flowing facts
51 | * @param outFacts all out-flowing facts
52 | */
53 | DataflowResult(std::unordered_map, std::shared_ptr> inFacts,
54 | std::unordered_map, std::shared_ptr> outFacts)
55 | :inFacts(std::move(inFacts)), outFacts(std::move(outFacts))
56 | {
57 |
58 | }
59 |
60 | /**
61 | * @brief construct an empty dataflow result
62 | */
63 | DataflowResult()
64 | :DataflowResult({}, {})
65 | {
66 |
67 | }
68 |
69 | private:
70 |
71 | std::unordered_map, std::shared_ptr>
72 | inFacts; ///< in-flowing facts of each statement
73 |
74 | std::unordered_map, std::shared_ptr>
75 | outFacts; ///< out-flowing facts of each statement
76 |
77 | };
78 |
79 | } // fact
80 |
81 | #endif //STATIC_ANALYZER_DATAFLOWRESULT_H
82 |
--------------------------------------------------------------------------------
/include/analysis/dataflow/fact/MapFact.h:
--------------------------------------------------------------------------------
1 | #ifndef STATIC_ANALYZER_MAPFACT_H
2 | #define STATIC_ANALYZER_MAPFACT_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | #include "util/Copyable.h"
9 |
10 | namespace analyzer::analysis::dataflow::fact {
11 |
12 | /**
13 | * @class MapFact
14 | * @brief Represents map-like data-flow facts.
15 | *
16 | * Elements are checked by identity rather than equality !!!
17 | *
18 | * @tparam K key type
19 | * @tparam V value type
20 | */
21 | template
22 | class MapFact: public util::Copyable> {
23 | public:
24 |
25 | /**
26 | * @brief get the value of a given key
27 | * @param key the key to be searched
28 | * @return the value to which the specified key is mapped,
29 | * or nullptr if this map contains no mapping for the key.
30 | */
31 | [[nodiscard]] virtual std::shared_ptr get(const std::shared_ptr& key) const
32 | {
33 | if (map.find(key) == map.end()) {
34 | return nullptr;
35 | }
36 | return map.at(key);
37 | }
38 |
39 | /**
40 | * @brief Updates the key-value mapping in this fact.
41 | * @param key the key to update
42 | * @param value the value to be bound to the key
43 | * @return true if the update changes this fact, otherwise
44 | */
45 | virtual bool update(const std::shared_ptr& key, const std::shared_ptr& value)
46 | {
47 | if (map.find(key) == map.end()) {
48 | map.emplace(key, value);
49 | return true;
50 | }
51 | if (map.at(key) == value) {
52 | return false;
53 | }
54 | map.at(key) = value;
55 | return true;
56 | }
57 |
58 | /**
59 | * @brief Removes the key-value mapping for given key.
60 | * @param key the key to remove its value
61 | * @return the previous value associated with key, or null if there was no mapping for key.
62 | */
63 | virtual std::shared_ptr remove(const std::shared_ptr& key)
64 | {
65 | if (map.find(key) == map.end()) {
66 | return nullptr;
67 | }
68 | std::shared_ptr result = map.at(key);
69 | map.erase(key);
70 | return result;
71 | }
72 |
73 | /**
74 | * @brief Copies the content from given fact to this fact.
75 | * @param fact the fact to be copied
76 | * @return true if this fact changed as a result of the call, otherwise false.
77 | */
78 | virtual bool copyFrom(const std::shared_ptr> fact)
79 | {
80 | bool changed = false;
81 | for (const auto& [key, value] : fact->getMap()) {
82 | changed = update(key, value) || changed;
83 | }
84 | return changed;
85 | }
86 |
87 | /**
88 | * @brief creates and returns a copy of this fact
89 | * @return a copy of this fact
90 | */
91 | [[nodiscard]] std::shared_ptr> copy() const override
92 | {
93 | return std::make_shared>(map);
94 | }
95 |
96 | /**
97 | * @brief Clears all content in this fact.
98 | */
99 | virtual void clear()
100 | {
101 | map.clear();
102 | }
103 |
104 | /**
105 | * @return a set of the keys contained in this fact.
106 | */
107 | [[nodiscard]] virtual std::unordered_set> keySet() const
108 | {
109 | std::unordered_set> result;
110 | for (auto& [k, _] : map) {
111 | result.emplace(k);
112 | }
113 | return result;
114 | }
115 |
116 | /**
117 | * @return a set of the values contained in this fact.
118 | */
119 | [[nodiscard]] virtual std::unordered_set> valueSet() const
120 | {
121 | std::unordered_set> result;
122 | for (auto& [_, v] : map) {
123 | result.emplace(v);
124 | }
125 | return result;
126 | }
127 |
128 | /**
129 | * @return true if this fact is empty, otherwise false
130 | */
131 | [[nodiscard]] virtual bool isEmpty() const
132 | {
133 | return map.empty();
134 | }
135 |
136 | /**
137 | * @return the size of this map fact
138 | */
139 | [[nodiscard]] virtual std::size_t size() const
140 | {
141 | return map.size();
142 | }
143 |
144 | /**
145 | * @brief compares equality of map fact
146 | * @param other another map fact
147 | * @return true if this map fact is equal to other map fact, otherwise false
148 | */
149 | [[nodiscard]] virtual bool equalsTo(const std::shared_ptr>& other) const
150 | {
151 | return map == other->getMap();
152 | }
153 |
154 | /**
155 | * @brief call processor function for each key-value pairs in this map fact
156 | * @param processor a processor function to process each key-value pair
157 | */
158 | virtual void forEach(std::function, std::shared_ptr)> processor)
159 | {
160 | for (auto [k, v] : map) {
161 | processor(k, v);
162 | }
163 | }
164 |
165 | /**
166 | * @brief Constructs a new MapFact with the same mappings as specified Map.
167 | * @param map the map whose mappings are to be placed in this map.
168 | */
169 | explicit MapFact(std::unordered_map, std::shared_ptr> map)
170 | :map(std::move(map))
171 | {
172 |
173 | }
174 |
175 | /**
176 | * @brief Construct an empty set
177 | */
178 | MapFact()
179 | : MapFact(std::unordered_map, std::shared_ptr>{})
180 | {
181 |
182 | }
183 |
184 | private:
185 |
186 | /**
187 | * @return a constant reference to the inner map
188 | */
189 | const std::unordered_map, std::shared_ptr>& getMap() const
190 | {
191 | return map;
192 | }
193 |
194 | std::unordered_map, std::shared_ptr>
195 | map; ///< The map holding the mappings of this MapFact.
196 |
197 | };
198 |
199 | } // fact
200 |
201 | #endif //STATIC_ANALYZER_MAPFACT_H
202 |
--------------------------------------------------------------------------------
/include/analysis/dataflow/fact/NodeResult.h:
--------------------------------------------------------------------------------
1 | #ifndef STATIC_ANALYZER_NODERESULT_H
2 | #define STATIC_ANALYZER_NODERESULT_H
3 |
4 | #include "analysis/AnalysisResult.h"
5 |
6 | namespace analyzer::analysis::dataflow::fact {
7 |
8 | /**
9 | * @brief An interface for querying data-flow results.
10 | * @class NodeResult
11 | * @tparam Fact the type of dataflow fact
12 | */
13 | template
14 | class NodeResult: public StmtResult {
15 | public:
16 |
17 | /**
18 | * @brief get the flowing-in fact of a given node
19 | * @param node a statement to get the in fact
20 | * @return a dataflow fact, nullptr if not exist
21 | */
22 | [[nodiscard]] virtual std::shared_ptr getInFact(std::shared_ptr node) const = 0;
23 |
24 | /**
25 | * @brief get the flowing-out fact of a given node
26 | * @param node a statement to get the out fact
27 | * @return a dataflow fact, nullptr if not exist
28 | */
29 | [[nodiscard]] virtual std::shared_ptr getOutFact(std::shared_ptr node) const = 0;
30 |
31 | /**
32 | * @brief typically, all statements are relevant in NodeResult
33 | * @param stmt an unused argument
34 | * @return true
35 | */
36 | [[nodiscard]] bool isRelevant(std::shared_ptr stmt) const override
37 | {
38 | return true;
39 | }
40 |
41 | /**
42 | * @brief typically, the result of a statement is its out-flowing fact
43 | * @param stmt the statement to get result
44 | * @return the result of given stmt, which is also its out-flowing fact
45 | */
46 | [[nodiscard]] std::shared_ptr getResult(std::shared_ptr stmt) const override
47 | {
48 | return getOutFact(stmt);
49 | }
50 |
51 | };
52 |
53 | } // fact
54 |
55 | #endif //STATIC_ANALYZER_NODERESULT_H
56 |
--------------------------------------------------------------------------------
/include/analysis/dataflow/fact/SetFact.h:
--------------------------------------------------------------------------------
1 | #ifndef STATIC_ANALYZER_SETFACT_H
2 | #define STATIC_ANALYZER_SETFACT_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | #include "util/Copyable.h"
9 |
10 | namespace analyzer::analysis::dataflow::fact {
11 |
12 | /**
13 | * @class SetFact
14 | * @brief Represents set-like data-flow facts.
15 | *
16 | * Elements are checked by identity rather than equality !!!
17 | *
18 | * @tparam E elements type
19 | */
20 | template
21 | class SetFact: public util::Copyable> {
22 | public:
23 |
24 | /**
25 | * @brief check the existence of an element
26 | * @param e the element to be checked
27 | * @return true if this set contains the specified element, otherwise false.
28 | */
29 | [[nodiscard]] virtual bool contains(const std::shared_ptr& e) const
30 | {
31 | return set.find(e) != set.end();
32 | }
33 |
34 | /**
35 | * @brief Adds an element to this fact.
36 | * @param e the element to be added
37 | * @return true if this fact changed as a result of the call, otherwise false.
38 | */
39 | virtual bool add(const std::shared_ptr& e)
40 | {
41 | return set.emplace(e).second;
42 | }
43 |
44 | /**
45 | * @brief Removes an element from this fact
46 | * @param e the element to be removed
47 | * @return true if any elements were removed as a result of the call, otherwise false.
48 | */
49 | virtual bool remove(const std::shared_ptr& e)
50 | {
51 | return set.erase(e) == 1;
52 | }
53 |
54 | /**
55 | * @brief Removes all the elements of this fact that satisfy the given predicate.
56 | * @param filter a predicate for elements to be removed
57 | * @return true if any elements were removed as a result of the call, otherwise false.
58 | */
59 | virtual bool removeIf(std::function&)> filter)
60 | {
61 | std::size_t oldSize = set.size();
62 | std::vector> toRemove;
63 | for (const std::shared_ptr& e : set) {
64 | if (filter(e)) {
65 | toRemove.emplace_back(e);
66 | }
67 | }
68 | for (const std::shared_ptr& e : toRemove) {
69 | set.erase(e);
70 | }
71 | return set.size() != oldSize;
72 | }
73 |
74 | /**
75 | * @brief Removes all elements of other fact.
76 | * @param other another set fact
77 | * @return true if this fact changed as a result of the call, otherwise false.
78 | */
79 | virtual bool removeAll(const std::shared_ptr>& other)
80 | {
81 | return removeIf([&](const std::shared_ptr& e) -> bool {
82 | return other->contains(e);
83 | });
84 | }
85 |
86 | /**
87 | * @brief Unions other fact into this fact.
88 | * @param other another set fact
89 | * @return true if this fact changed as a result of the call, otherwise false.
90 | */
91 | virtual bool unionN(const std::shared_ptr>& other)
92 | {
93 | std::size_t oldSize = set.size();
94 | set.insert(other->getSet().begin(), other->getSet().end());
95 | return set.size() != oldSize;
96 | }
97 |
98 | /**
99 | * @brief create a new fact as the union of this and other
100 | * @param other another set fact
101 | * @return a new fact which is the union of this and other facts.
102 | */
103 | [[nodiscard]] virtual std::shared_ptr>
104 | unionWith(const std::shared_ptr>& other) const
105 | {
106 | std::unordered_set> result;
107 | std::set_union(set.begin(), set.end(),
108 | other->getSet().begin(), other->getSet().end(),
109 | std::inserter(result, result.begin()));
110 | return std::make_shared>(std::move(result));
111 | }
112 |
113 | /**
114 | * @brief Intersects this fact with other fact.
115 | * @param other another set fact
116 | * @return true if this fact changed as a result of the call, otherwise false.
117 | */
118 | virtual bool intersect(const std::shared_ptr>& other)
119 | {
120 | return removeIf([&] (const std::shared_ptr& e) -> bool {
121 | return !other->contains(e);
122 | });
123 | }
124 |
125 | /**
126 | * @brief create a new fact as the intersection of this and other
127 | * @param other another set fact
128 | * @return a new fact which is the intersection of this and other facts.
129 | */
130 | [[nodiscard]] virtual std::shared_ptr>
131 | intersectWith(const std::shared_ptr>& other) const
132 | {
133 | std::unordered_set> result;
134 | std::set_intersection(set.begin(), set.end(),
135 | other->getSet().begin(), other->getSet().end(),
136 | std::inserter(result, result.begin()));
137 | return std::make_shared>(std::move(result));
138 | }
139 |
140 | /**
141 | * @brief sets the content of this set to the same as other set.
142 | * @param other another set fact
143 | */
144 | virtual void setSetFact(const std::shared_ptr>& other)
145 | {
146 | set = other->getSet();
147 | }
148 |
149 | /**
150 | * @brief creates and returns
151 | * @return a copy of this fact
152 | */
153 | [[nodiscard]] std::shared_ptr> copy() const override
154 | {
155 | return std::make_shared>(set);
156 | }
157 |
158 | /**
159 | * @brief clears all content in this fact.
160 | */
161 | virtual void clear()
162 | {
163 | set.clear();
164 | }
165 |
166 | /**
167 | * @return true if this fact is empty, otherwise false
168 | */
169 | [[nodiscard]] virtual bool isEmpty() const
170 | {
171 | return set.empty();
172 | }
173 |
174 | /**
175 | * @return the size of this set fact
176 | */
177 | [[nodiscard]] virtual std::size_t size() const
178 | {
179 | return set.size();
180 | }
181 |
182 | /**
183 | * @brief compares equality of set fact
184 | * @param other another set fact
185 | * @return true if this set fact is equal to other set fact, otherwise false
186 | */
187 | [[nodiscard]] virtual bool equalsTo(const std::shared_ptr>& other) const
188 | {
189 | return set == other->getSet();
190 | }
191 |
192 | /**
193 | * @brief call processor function for each elements in this set fact
194 | * @param processor a processor function to process each element
195 | */
196 | virtual void forEach(std::function)> processor)
197 | {
198 | for (std::shared_ptr e : set) {
199 | processor(e);
200 | }
201 | }
202 |
203 | /**
204 | * @brief Construct a set from a given set
205 | * @param set
206 | */
207 | explicit SetFact(std::unordered_set> set)
208 | :set(std::move(set))
209 | {
210 |
211 | }
212 |
213 | /**
214 | * @brief Construct an empty set
215 | */
216 | SetFact()
217 | : SetFact(std::unordered_set>{})
218 | {
219 |
220 | }
221 |
222 | private:
223 |
224 | /**
225 | * @return a const reference to the inner set
226 | */
227 | const std::unordered_set>& getSet() const
228 | {
229 | return set;
230 | }
231 |
232 | std::unordered_set> set; ///< the inner set of this dataflow fact
233 |
234 | };
235 |
236 | } // fact
237 |
238 |
239 | #endif //STATIC_ANALYZER_SETFACT_H
240 |
--------------------------------------------------------------------------------
/include/analysis/dataflow/solver/Solver.h:
--------------------------------------------------------------------------------
1 | #ifndef STATIC_ANALYZER_SOLVER_H
2 | #define STATIC_ANALYZER_SOLVER_H
3 |
4 | #include
5 | #include
6 |
7 | #include "analysis/dataflow/fact/DataflowResult.h"
8 | #include "analysis/dataflow/DataflowAnalysis.h"
9 |
10 | namespace analyzer::analysis::dataflow::solver {
11 |
12 | /**
13 | * @class Solver
14 | * @brief Interface of data-flow analysis solver.
15 | * @tparam Fact
16 | */
17 | template
18 | class Solver {
19 | public:
20 |
21 | /**
22 | * @brief solves the given analysis problem
23 | * @param dataflowAnalysis a dataflow analysis problem
24 | * @return the analysis result
25 | */
26 | [[nodiscard]] virtual std::shared_ptr>
27 | solve(const std::unique_ptr>& dataflowAnalysis) const = 0;
28 |
29 | virtual ~Solver() = default;
30 |
31 | };
32 |
33 | /**
34 | * @class AbstractSolver
35 | * @brief provides common functionalities for Solver
36 | * @tparam Fact type of data-flow facts
37 | */
38 | template
39 | class AbstractSolver: public Solver {
40 | public:
41 |
42 | [[nodiscard]] std::shared_ptr>
43 | solve(const std::unique_ptr>& dataflowAnalysis) const override
44 | {
45 | World::getLogger().Info("Initializing dataflow facts ...");
46 | std::shared_ptr> result = initialize(dataflowAnalysis);
47 | World::getLogger().Info("Doing the solving ...");
48 | doSolve(dataflowAnalysis, result);
49 | return result;
50 | }
51 |
52 | protected:
53 |
54 | /**
55 | * @brief initialize the data facts for a forward analysis
56 | * @param dataflowAnalysis the corresponding dataflow analysis
57 | * @param result the result to be initialized
58 | */
59 | virtual void initializeForward(
60 | const std::unique_ptr>& dataflowAnalysis,
61 | std::shared_ptr> result) const
62 | {
63 | std::shared_ptr cfg = dataflowAnalysis->getCFG();
64 | std::shared_ptr entry = cfg->getEntry();
65 | result->setInFact(entry, dataflowAnalysis->newBoundaryFact());
66 | result->setOutFact(entry, dataflowAnalysis->newBoundaryFact());
67 | for (const std::shared_ptr& stmt : cfg->getIR()->getStmts()) {
68 | result->setInFact(stmt, dataflowAnalysis->newInitialFact());
69 | result->setOutFact(stmt, dataflowAnalysis->newInitialFact());
70 | }
71 | std::shared_ptr exit = cfg->getExit();
72 | result->setInFact(exit, dataflowAnalysis->newInitialFact());
73 | result->setOutFact(exit, dataflowAnalysis->newInitialFact());
74 | }
75 |
76 | /**
77 | * @brief initialize the data facts for a backward analysis
78 | * @param dataflowAnalysis the corresponding dataflow analysis
79 | * @param result the result to be initialized
80 | */
81 | virtual void initializeBackward(
82 | const std::unique_ptr>& dataflowAnalysis,
83 | std::shared_ptr> result) const
84 | {
85 | std::shared_ptr cfg = dataflowAnalysis->getCFG();
86 | std::shared_ptr exit = cfg->getExit();
87 | result->setInFact(exit, dataflowAnalysis->newBoundaryFact());
88 | result->setOutFact(exit, dataflowAnalysis->newBoundaryFact());
89 | for (const std::shared_ptr& stmt : cfg->getIR()->getStmts()) {
90 | result->setInFact(stmt, dataflowAnalysis->newInitialFact());
91 | result->setOutFact(stmt, dataflowAnalysis->newInitialFact());
92 | }
93 | std::shared_ptr entry = cfg->getEntry();
94 | result->setInFact(entry, dataflowAnalysis->newInitialFact());
95 | result->setOutFact(entry, dataflowAnalysis->newInitialFact());
96 | }
97 |
98 | /**
99 | * @brief solve a forward dataflow analysis
100 | * @param dataflowAnalysis the dataflow analysis to be solved
101 | * @param result where to store the results
102 | */
103 | virtual void doSolveForward(
104 | const std::unique_ptr>& dataflowAnalysis,
105 | std::shared_ptr> result) const = 0;
106 |
107 | /**
108 | * @brief solve a backward dataflow analysis
109 | * @param dataflowAnalysis the dataflow analysis to be solved
110 | * @param result where to store the results
111 | */
112 | virtual void doSolveBackward(
113 | const std::unique_ptr>& dataflowAnalysis,
114 | std::shared_ptr> result) const = 0;
115 |
116 | private:
117 |
118 | [[nodiscard]] std::shared_ptr>
119 | initialize(const std::unique_ptr>& dataflowAnalysis) const
120 | {
121 | std::shared_ptr