Compare commits

...

33 Commits

Author SHA1 Message Date
Erki
12afc6633e Okay maybe?
Some checks failed
continuous-integration/drone/push Build is failing
2022-06-18 11:27:16 +03:00
7925b87f09 Update 'README.md'
Some checks failed
continuous-integration/drone/push Build is failing
Fixed an outdated package reference.
2022-05-23 20:04:41 +00:00
254eb87889 Licensing and readme
Some checks failed
continuous-integration/drone/push Build is failing
2021-05-06 16:14:48 +03:00
905dcb160c Readd branch information 2021-05-06 16:14:24 +03:00
91fbb3bbbd Revert branch identification for now 2021-03-11 00:52:04 +02:00
1c3107e8cc hide the playgrounds 2021-03-11 00:51:46 +02:00
df8e5c5d09 Add unit testing for expected output
All checks were successful
continuous-integration/drone/push Build is passing
2021-03-10 01:12:47 +02:00
cdf6b850f9 Update op-summarizer to match branch counts properly 2021-03-09 23:14:16 +02:00
6ed38077c6 Fix for-loops opening branches without a full header
All checks were successful
continuous-integration/drone/push Build is passing
2021-03-07 19:55:54 +02:00
a88a57e2ec Function call tests
All checks were successful
continuous-integration/drone/push Build is passing
2021-03-07 18:37:13 +02:00
021a270e08 Fix for-loops without inits counting as branching
All checks were successful
continuous-integration/drone/push Build is passing
2021-03-07 18:14:17 +02:00
eb640e513a Initiate build
Some checks failed
continuous-integration/drone/push Build is failing
2021-03-07 17:35:46 +02:00
2ba019373e Docker and drone files 2021-03-07 16:26:08 +02:00
6e27e99735 Initial unit tests 2021-03-07 15:58:45 +02:00
326decd3de Unit tests for basic operation 2021-03-07 15:32:02 +02:00
7b294045bf Unit test framework integration
Along with conan
2021-03-07 14:31:43 +02:00
53c24c6d5e Separate op-finder into library and executable 2021-03-07 13:51:29 +02:00
83ca2deb0e Better branch detection model
For loops are now broken up into 2 branches: 1 (init) and 2 (cond + inc).
2021-03-07 13:02:14 +02:00
a23e5c66ab Add subscript operation recognition 2021-03-07 11:49:16 +02:00
7a852f8f94 Add function call recognition and pretty printing 2021-03-07 01:25:48 +02:00
34eba970cc Initial analyzer/summarizer commit 2021-03-04 02:37:12 +02:00
0509d250ab Better for-loop/fallthrough branch detection. 2021-03-04 02:36:12 +02:00
a0b9775f13 Make for-loop initializer detector use post traverse statement 2021-03-03 01:51:06 +02:00
5a2d5ed933 Loop initializations are excluded from eval 2021-03-03 01:27:14 +02:00
be4a71325d cmake profile update 2021-03-03 00:34:13 +02:00
16f4c85f6d AST Visitor based implementation of the operation finder 2021-03-03 00:34:06 +02:00
aff90d5a9d Refactor AST matcher to a separate class 2021-03-02 23:24:38 +02:00
0e7cf93c9b Refactor for later library use 2021-02-28 22:09:23 +02:00
b609c48bfe .idea configuration 2021-02-28 18:54:29 +02:00
cff605642f Add output serialization to JSON file. 2021-02-28 18:50:09 +02:00
375e3f9af6 Add nlohmann::json as a dependency. 2021-02-28 18:49:23 +02:00
6f3dce5972 Resolve macro expansions properly 2021-02-24 23:05:38 +02:00
2df47d4bc5 Fixes the compiler not acknowledging preprocessor and include statements. 2021-02-24 21:49:50 +02:00
61 changed files with 2988 additions and 313 deletions

66
.clang-format Normal file
View File

@ -0,0 +1,66 @@
# Generated from CLion C/C++ Code Style settings
BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignOperands: true
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Always
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterReturnType: None
AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: false
AfterClass: true
AfterControlStatement: Always
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterUnion: true
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: true
BreakBeforeBinaryOperators: None
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeComma
ColumnLimit: 0
CompactNamespaces: false
ContinuationIndentWidth: 8
IndentCaseLabels: true
IndentPPDirectives: None
IndentWidth: 2
KeepEmptyLinesAtTheStartOfBlocks: true
MaxEmptyLinesToKeep: 2
NamespaceIndentation: None
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PointerAlignment: Left
ReflowComments: false
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 0
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
TabWidth: 2
UseTab: Never

36
.drone.yml Normal file
View File

@ -0,0 +1,36 @@
kind: pipeline
type: docker
name: default
steps:
- name: build and test
pull: never
image: erki/llvm:latest
commands:
- apt install --no-install-recommends python3-pip -y
- pip3 install --upgrade conan
- conan profile new default --detect
- conan profile update settings.compiler.libcxx=libstdc++11 default
- mkdir -p build
- cd build
- conan install .. --build=missing
- cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=ON
- ninja
- ctest . --output-on-failure
- name: opsummarizer integration tests
pull: never
image: erki/llvm:latest
commands:
- apt install --no-install-recommends gcovr python3-pip -y
- pip3 install --upgrade conan
- conan profile new default --detect
- conan profile update settings.compiler.libcxx=libstdc++11 default
- mkdir -p build
- cd build
- conan install .. --build=missing
- cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=OFF
- ninja op-finder
- cp -r ../testcases ./testcases
- cd ./testcases
- ln ../op-finder/op-finder ./op-finder
- python3 ../../op-summarizer/tests.py

144
.gitignore vendored
View File

@ -1,3 +1,7 @@
## Project specifics
testcases/*/
## C & C++
# Prerequisites # Prerequisites
*.d *.d
@ -34,3 +38,143 @@
# Build folders # Build folders
[Bb]uild/ [Bb]uild/
cmake-*/ cmake-*/
## Python.
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/

8
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

1
.idea/.name generated Normal file
View File

@ -0,0 +1 @@
C Analyzer

2
.idea/analyzer.git.iml generated Normal file
View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="CMake" type="CPP_MODULE" version="4" />

59
.idea/codeStyles/Project.xml generated Normal file
View File

@ -0,0 +1,59 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<Objective-C>
<option name="INDENT_NAMESPACE_MEMBERS" value="0" />
<option name="INDENT_C_STRUCT_MEMBERS" value="2" />
<option name="INDENT_CLASS_MEMBERS" value="2" />
<option name="INDENT_INSIDE_CODE_BLOCK" value="2" />
<option name="NAMESPACE_BRACE_PLACEMENT" value="2" />
<option name="FUNCTION_BRACE_PLACEMENT" value="2" />
<option name="BLOCK_BRACE_PLACEMENT" value="2" />
<option name="SUPERCLASS_LIST_COMMA_ON_NEXT_LINE" value="true" />
<option name="SPACE_WITHIN_EMPTY_BRACES" value="true" />
<option name="SPACE_BEFORE_POINTER_IN_DECLARATION" value="false" />
<option name="SPACE_AFTER_POINTER_IN_DECLARATION" value="true" />
<option name="SPACE_BEFORE_REFERENCE_IN_DECLARATION" value="false" />
<option name="SPACE_AFTER_REFERENCE_IN_DECLARATION" value="true" />
</Objective-C>
<Objective-C-extensions>
<extensions>
<pair source="cpp" header="hpp" fileNamingConvention="PASCAL_CASE" />
<pair source="c" header="h" fileNamingConvention="NONE" />
<pair source="cu" header="cuh" fileNamingConvention="NONE" />
</extensions>
<rules>
<rule entity="NAMESPACE" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="MACRO" visibility="ANY" specifier="ANY" prefix="" style="SCREAMING_SNAKE_CASE" suffix="" />
<rule entity="CLASS" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="STRUCT" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="ENUM" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="ENUMERATOR" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="TYPEDEF" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="UNION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="CLASS_MEMBER_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="STRUCT_MEMBER_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="CLASS_MEMBER_FIELD" visibility="ANY" specifier="ANY" prefix="" style="SNAKE_CASE" suffix="" />
<rule entity="STRUCT_MEMBER_FIELD" visibility="ANY" specifier="ANY" prefix="" style="SNAKE_CASE" suffix="" />
<rule entity="GLOBAL_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="GLOBAL_VARIABLE" visibility="ANY" specifier="ANY" prefix="" style="SNAKE_CASE" suffix="" />
<rule entity="PARAMETER" visibility="ANY" specifier="ANY" prefix="" style="SNAKE_CASE" suffix="" />
<rule entity="LOCAL_VARIABLE" visibility="ANY" specifier="ANY" prefix="" style="SNAKE_CASE" suffix="" />
<rule entity="CLASS_MEMBER_FIELD,STRUCT_MEMBER_FIELD" visibility="PROTECTED,PRIVATE" specifier="ANY" prefix="_" style="SNAKE_CASE" suffix="" />
<rule entity="CLASS_MEMBER_FUNCTION,STRUCT_MEMBER_FUNCTION" visibility="PROTECTED,PRIVATE" specifier="ANY" prefix="_" style="PASCAL_CASE" suffix="" />
</rules>
</Objective-C-extensions>
<codeStyleSettings language="CMake">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="ObjectiveC">
<option name="BRACE_STYLE" value="2" />
<option name="CLASS_BRACE_STYLE" value="2" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

4
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
</project>

8
.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/analyzer.git.iml" filepath="$PROJECT_DIR$/.idea/analyzer.git.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@ -1,8 +1,16 @@
cmake_minimum_required(VERSION 3.17) cmake_minimum_required(VERSION 3.14)
project("C Analyzer") project("C Analyzer")
set(CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR})
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter")
option(WITH_TESTS "Build unit tests as well." OFF)
add_subdirectory(op-finder-lib)
add_subdirectory(op-finder) add_subdirectory(op-finder)
if(WITH_TESTS)
enable_testing()
add_subdirectory(op-finder-tests)
endif()

16
Dockerfile Normal file
View File

@ -0,0 +1,16 @@
FROM ubuntu:20.04
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update -y
RUN apt-get install -y git build-essential cmake ninja-build python3 python3-pip
RUN git clone --depth=1 https://github.com/llvm/llvm-project.git
RUN mkdir /llvm-project/build
WORKDIR /llvm-project/build
RUN cmake ../llvm -G"Ninja" -DLLVM_ENABLE_PROJECTS="clang" -DCMAKE_BUILD_TYPE=Release
RUN ninja install
WORKDIR /
RUN rm -r /llvm-project

View File

@ -1,4 +1,4 @@
MIT License Copyright (c) <year> <copyright holders> MIT License Copyright (c) 2021 Erki Meinberg
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

124
README.md
View File

@ -1,2 +1,124 @@
# masters-thesis # clang Based Atomic Operations Parser & Analyzer
The source code attached for a master's thesis work carried out in 2021.
Contained within is the source code for an **Atomic Operations Parser** and
a **Performance Analyzer**.
## Atomic Operations Parser (Operatioon Finder)
The **Atomic Operations Parser** is a C++ program which is meant to parse C-code
source files, and extract atomic operations from them. The tool uses LLVM & clang.
So precompiling the development libraries of those two is required.
### Building LLVM & clang
A docker file which automatically compiles and installs the required dependencies
is included for convenience.
Otherwise, the steps to installing clang are as follows:
Install the required dependencies via apt:
```shell
apt update -y
apt install -y git build-essential cmake ninja-build python3 python3-pip
```
Clone LLVM from the repo. Depth 1 makes the process faster. Also set up the various
folders for building and installing.
```shell
cd ~
git clone --branch "release/11.x" --depth 1 https://github.com/llvm/llvm-project.git
mkdir ~/llvm-project/build
mkdir ~/llvm-install
cd ~/llvm-project/build
```
Run cmake to configure the project. Followed by ninja to install it.
Note that when installing, you can modify `-DCMAKE_INSTALL_PREFIX` to specify
where the libraries should be installed to. In this case, we'll put them into
`~/llvm-install`.
```shell
cmake ../llvm -G "Ninja" -DCMAKE_INSTALL_PREFIX=~/llvm-install -DLLVM_ENABLE_PROJECTS="clang" -DCMAKE_BUILD_TYPE=Release
ninja install
```
### Building the Tool
Using the conan package manager is recommended. Otherwise you have to provide `Catch2_ROOT`
and `nlohmann_json_ROOT` yourself.
The variables `Clang_ROOT` and `LLVM_ROOT` depend on the previous step. If you installed the libraries
into your system, then you don't need to specify them. Otherwise, assuming an installation directory of
`~/llvm_install`, they'd look as follows:
```
-DClang_ROOT=~/llvm_install/lib/cmake/clang/
-DLLVM_ROOT=~/llvm_install/lib/cmake/llvm/
```
Now clone this repo and `cd` inside of it. Make a build directory and build the project:
```shell
mkdir build
cd build
conan install .. --build=missing
export CLANG_ROOT=~/llvm_install/lib/cmake/clang/
export LLVM_ROOT=~/llvm_install/lib/cmake/llvm/
cmake .. -DWITH_TESTS=ON -DClang_ROOT=${CLANG_ROOT} -DLLVM_ROOT=${LLVM_ROOT} -GNinja
ninja
```
You are now left with `op-finder/op-finder` and `op-finder-tests/op-finder-tests` executables.
### Usage
Use `op-finder --help` for help.
The finder will process multiple source files and output them to a single JSON file. For example:
`op-finder ./source1.c ./source2.c -o=project_opfinder.json`
The above line will take the C-code source files of `source1.c` and `source2.c`, extract atomic operations
from them, and output the JSON to the `./project_opfinder.json` file. This file can then be given to the
analyzer along with a gcov report.
## Analyzer (Operation Summarizer)
The Analyzer is responsible for taking the Atomic Operations Finder report and a gcov code coverage report
and combining them into a singular analysis of the codebase. In the present implementation, it will
summarize all unique atomic operations. This can then be combined with a database of atomic operations
and turned into a performance estimation.
The Analyzer is written in Python and requires no tooling beyond having Python 3 installed. gcov is needed
to generate the simulation reports.
### Prerequisites
Install the prerequisites from your package manager:
```shell
apt update -y
apt install gcovr python3 python3-pip
pip3 install gcovr
```
### Usage
Assuming Atomic Operations Parser was used in the previous usage example. The first step is to compile
the C program with GCC and to acquire a code coverage report from it using gcovr. This is done as follows:
```shell
gcc -fprofile-arcs -ftest-coverage -fPIC -O0 ./source1.c ./source2.c -o project.out
./project.out
gcovr -r ./ --json-pretty -o project_gcov.json
```
This will output the coverage report in human-readable JSON into the `project_gcov.json` file.
Next, the Analyzer needs to be ran:
```shell
python3 op-summarizer/opsummarizer.py --gcov project_gcov.json --finder project_opfinder.json --output project_summarized.json source1.c source2.c
```
The list of files in the specifies which source files should be taken into consideration. If a file is not present, then
that file will not be evaluated during the summarization.
The summarizer will then generate an output report in JSON, along with printing a human-readable version out
on the screen.

6
conanfile.txt Normal file
View File

@ -0,0 +1,6 @@
[requires]
catch2/2.13.4
nlohmann_json/3.9.1
[generators]
cmake_find_package

View File

@ -0,0 +1,49 @@
cmake_minimum_required(VERSION 3.14)
find_package(nlohmann_json REQUIRED)
find_package(Clang REQUIRED)
find_package(LLVM REQUIRED COMPONENTS Support Option Core)
# LLVM is typically compiled without RTTI. Weird linker errors ensue if
# you keep RTTI on and try to link.
if (NOT LLVM_ENABLE_RTTI)
if (MSVC)
string(REGEX REPLACE "/GR" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-")
else ()
string(REGEX REPLACE "-frtti" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
endif ()
endif ()
llvm_map_components_to_libnames(llvm_libs support option core)
add_library(op-finder-lib STATIC
src/OperationFinder.cpp
src/OperationStorage.cpp
src/OperationAstMatcher.cpp
src/OperationFinderAstVisitor.cpp
src/OperationFinderAstConsumer.cpp
src/OperationFinderAstAction.cpp
src/OperationLog.cpp)
target_include_directories(op-finder-lib
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
${LLVM_INCLUDE_DIRS}
)
target_compile_definitions(op-finder-lib
PUBLIC
${LLVM_DEFINITIONS}
)
target_link_libraries(op-finder-lib
PUBLIC
nlohmann_json::nlohmann_json
PRIVATE
${llvm_libs}
clangTooling
clangBasic
clangASTMatchers
)

View File

@ -0,0 +1,25 @@
//
// Created by erki on 02.03.21.
//
#ifndef C_ANALYZER_OPERATIONASTMATCHER_HPP
#define C_ANALYZER_OPERATIONASTMATCHER_HPP
#include <clang/ASTMatchers/ASTMatchFinder.h>
class OperationFinder;
class OperationASTMatcher : public clang::ast_matchers::MatchFinder::MatchCallback
{
public:
explicit OperationASTMatcher(OperationFinder* finder);
void addToFinder(clang::ast_matchers::MatchFinder& finder);
void run(const clang::ast_matchers::MatchFinder::MatchResult& result) override;
private:
OperationFinder* _op_finder;
};
#endif //C_ANALYZER_OPERATIONASTMATCHER_HPP

View File

@ -0,0 +1,35 @@
//
// Created by erki on 16.02.21.
//
#ifndef LLVM_PROTO_OPERATIONFINDER_HPP
#define LLVM_PROTO_OPERATIONFINDER_HPP
#include <clang/ASTMatchers/ASTMatchFinder.h>
#include "OperationLog.hpp"
class OperationFinder
{
public:
explicit OperationFinder(IOperationOutput* storage);
void processArithmetic(const clang::BinaryOperator* op, const clang::SourceManager& source_manager);
void processUnaryArithmetic(const clang::UnaryOperator* op, const clang::SourceManager& source_manager);
void processFunctionCall(const clang::CallExpr* call, const clang::SourceManager& source_manager);
void processArraySubscript(const clang::ArraySubscriptExpr* subscript, const clang::SourceManager& source_manager);
void branchEntered();
void branchExited();
private:
std::unique_ptr<OperationLog::BasicOperation>
_createBasicOperationLogEntry(const std::string& opcode, const clang::Expr* source, const clang::Expr* op1, const clang::Expr* op2);
std::pair<std::string, OperationLog> _createBaseOperationLog(const clang::Stmt* stmt, const clang::SourceManager& source_manager);
int _current_branch = 0;
IOperationOutput* _storage;
};
#endif //LLVM_PROTO_OPERATIONFINDER_HPP

View File

@ -0,0 +1,24 @@
//
// Created by erki on 02.03.21.
//
#ifndef C_ANALYZER_OPERATIONFINDERASTACTION_HPP
#define C_ANALYZER_OPERATIONFINDERASTACTION_HPP
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Frontend/FrontendActions.h>
class OperationFinder;
class OperationFinderAstAction
{
public:
explicit OperationFinderAstAction(OperationFinder* op_finder);
std::unique_ptr<clang::ASTConsumer> newASTConsumer();
private:
OperationFinder* _op_finder;
};
#endif //C_ANALYZER_OPERATIONFINDERASTACTION_HPP

View File

@ -0,0 +1,27 @@
//
// Created by erki on 02.03.21.
//
#ifndef C_ANALYZER_OPERATIONFINDERASTCONSUMER_HPP
#define C_ANALYZER_OPERATIONFINDERASTCONSUMER_HPP
#include <clang/AST/ASTConsumer.h>
#include "OperationFinderAstVisitor.hpp"
class OperationFinder;
class OperationFinderAstConsumer : public clang::ASTConsumer
{
public:
explicit OperationFinderAstConsumer(OperationFinder* op_finder);
void Initialize(clang::ASTContext& context) override;
void HandleTranslationUnit(clang::ASTContext& context) override;
private:
OperationFinderAstVisitor _visitor;
};
#endif //C_ANALYZER_OPERATIONFINDERASTCONSUMER_HPP

View File

@ -0,0 +1,48 @@
//
// Created by erki on 02.03.21.
//
#ifndef C_ANALYZER_OPERATIONFINDERASTVISITOR_HPP
#define C_ANALYZER_OPERATIONFINDERASTVISITOR_HPP
#include <clang/AST/RecursiveASTVisitor.h>
class OperationFinder;
class OperationFinderAstVisitor
: public clang::RecursiveASTVisitor<OperationFinderAstVisitor>
{
public:
explicit OperationFinderAstVisitor(OperationFinder* op_finder);
void NewContext(clang::ASTContext* context);
bool VisitForStmt(clang::ForStmt* stmt);
bool VisitBinaryOperator(clang::BinaryOperator* op);
bool VisitUnaryOperator(clang::UnaryOperator* op);
bool VisitCallExpr(clang::CallExpr* call);
bool VisitArraySubscriptExpr(clang::ArraySubscriptExpr* subscript);
bool dataTraverseStmtPre(clang::Stmt* stmt);
bool dataTraverseStmtPost(clang::Stmt* stmt);
private:
clang::ASTContext* _context;
OperationFinder* _op_finder;
struct _LoopHeaderStateMachine
{
bool in_loop_header = false;
clang::Stmt* init = nullptr;
clang::Stmt* header_start = nullptr;
clang::Stmt* header_end = nullptr;
};
_LoopHeaderStateMachine _loop_header;
std::vector<clang::Stmt*> _branch_stack;
clang::Stmt* _isBranchEntry(clang::Stmt* stmt);
};
#endif //C_ANALYZER_OPERATIONFINDERASTVISITOR_HPP

View File

@ -0,0 +1,114 @@
//
// Created by erki on 28.02.21.
//
#ifndef C_ANALYZER_OPERATIONLOG_HPP
#define C_ANALYZER_OPERATIONLOG_HPP
#include <vector>
#include <variant>
#include <nlohmann/json.hpp>
struct OperationLog
{
struct IEntry
{
[[nodiscard]] virtual nlohmann::json toJson() const = 0;
};
struct FunctionCall : IEntry
{
static constexpr char TYPE_NAME[] = "function_call";
std::string function_name;
std::string call_result_type;
[[nodiscard]] nlohmann::json toJson() const override;
};
struct BasicOperation : IEntry
{
static constexpr char TYPE_NAME[] = "basic_operation";
std::string operation_name;
std::string type_lhs;
std::string type_rhs;
std::string type_result;
[[nodiscard]] nlohmann::json toJson() const override;
};
unsigned int line = 0;
int branch_number = 0;
std::string entry_type;
std::unique_ptr<IEntry> entry;
OperationLog() = default;
OperationLog(const OperationLog&) = delete;
OperationLog(OperationLog&&) = default;
void DecodeEntry(const nlohmann::json& j);
};
class IOperationOutput
{
public:
virtual ~IOperationOutput() = default;
virtual void pushOperation(const std::string& filename, OperationLog&& op) = 0;
};
inline void to_json(nlohmann::json& j, const OperationLog& l)
{
j = nlohmann::json{
{"line", l.line},
{"entry_type", l.entry_type},
{"entry", l.entry->toJson()},
{"branch_number", l.branch_number}
};
}
inline void from_json(const nlohmann::json& j, OperationLog& l)
{
j.at("line").get_to(l.line);
j.at("entry_type").get_to(l.entry_type);
l.DecodeEntry(j["entry"]);
j.at("branch_number").get_to(l.branch_number);
}
inline void to_json(nlohmann::json& j, const OperationLog::BasicOperation& bo)
{
j = nlohmann::json{
{"operation_name", bo.operation_name},
{"type_lhs", bo.type_lhs},
{"type_rhs", bo.type_rhs},
{"type_result", bo.type_result}
};
}
inline void from_json(const nlohmann::json& j, OperationLog::BasicOperation& bo)
{
j.at("operation_name").get_to(bo.operation_name);
j.at("type_lhs").get_to(bo.type_lhs);
j.at("type_rhs").get_to(bo.type_rhs);
j.at("type_result").get_to(bo.type_result);
}
inline void to_json(nlohmann::json& j, const OperationLog::FunctionCall& fcall)
{
j = nlohmann::json{
{"function_name", fcall.function_name},
{"call_result_type", fcall.call_result_type}
};
}
inline void from_json(const nlohmann::json& j, OperationLog::FunctionCall& fcall)
{
j.at("function_name").get_to(fcall.function_name);
j.at("call_result_type").get_to(fcall.call_result_type);
}
#endif //C_ANALYZER_OPERATIONLOG_HPP

View File

@ -0,0 +1,36 @@
//
// Created by erki on 28.02.21.
//
#ifndef C_ANALYZER_OPERATIONSTORAGE_HPP
#define C_ANALYZER_OPERATIONSTORAGE_HPP
#include <string>
#include <unordered_map>
#include <vector>
#include "OperationLog.hpp"
class OperationStorage : public IOperationOutput
{
public:
OperationStorage() = default;
OperationStorage(const OperationStorage&) = delete;
~OperationStorage() override;
void enablePrettyPrint();
void toStream(std::ostream& stream);
void toFile(const std::string& output_filename);
void pushOperation(const std::string& original_filename, OperationLog&& op) override;
[[nodiscard]] const std::unordered_map<std::string, std::vector<OperationLog>>& getOperations() const;
private:
std::unordered_map<std::string, std::vector<OperationLog>> _operations;
bool _pretty_print = false;
std::string _convertFilepath(const std::string& original);
};
#endif //C_ANALYZER_OPERATIONSTORAGE_HPP

View File

@ -0,0 +1,52 @@
//src
// Created by erki on 02.03.21.
//
#ifndef C_ANALYZER_ASTHELPERS_HPP
#define C_ANALYZER_ASTHELPERS_HPP
#include <clang/AST/ASTContext.h>
template<typename TOp>
clang::StringRef getOpcode(const TOp *op)
{
return op->getOpcodeStr(op->getOpcode());
}
inline clang::SourceLocation resolveOperationSourceLocation(const clang::SourceManager& source_manager,
const clang::SourceLocation& original)
{
if (source_manager.isMacroBodyExpansion(original))
{
return source_manager.getExpansionLoc(original);
}
return original;
}
inline std::tuple<std::string, unsigned int, unsigned int> resolveLocationsWithLoc(const clang::SourceLocation& loc,
const clang::SourceManager& source_manager)
{
const auto& loc_resolved = resolveOperationSourceLocation(source_manager, loc);
return {
source_manager.getFilename(loc_resolved).str(),
source_manager.getSpellingLineNumber(loc_resolved),
source_manager.getSpellingColumnNumber(loc_resolved)
};
}
template<typename TStmt>
std::tuple<std::string, unsigned int, unsigned int> resolveLocations(const TStmt* op,
const clang::SourceManager& source_manager)
{
const auto& loc = resolveOperationSourceLocation(source_manager, op->getBeginLoc());
return {
source_manager.getFilename(loc).str(),
source_manager.getSpellingLineNumber(loc),
source_manager.getSpellingColumnNumber(loc)
};
}
#endif //C_ANALYZER_ASTHELPERS_HPP

View File

@ -0,0 +1,57 @@
//
// Created by erki on 02.03.21.
//
#include "OperationAstMatcher.hpp"
#include "OperationFinder.hpp"
using namespace clang;
using namespace clang::ast_matchers;
namespace
{
StatementMatcher AssignmentMatcher =
binaryOperation(isAssignmentOperator(),
hasLHS(expr().bind("lhs")),
hasRHS(expr().bind("rhs"))).bind("assignment");
StatementMatcher ArithmeticMatcher =
binaryOperation(hasAnyOperatorName("+", "-", "/", "*", "<", ">",
"<=", ">=", "==", "<<", ">>", "%"),
hasLHS(expr().bind("lhs")),
hasRHS(expr().bind("lhs"))).bind("arithmetic");
StatementMatcher UnaryArithmeticMatcher =
unaryOperator(hasAnyOperatorName("!", "++", "--"),
hasUnaryOperand(expr().bind("lhs"))).bind("unary_arithmetic");
}
OperationASTMatcher::OperationASTMatcher(OperationFinder* finder)
: _op_finder(finder)
{ }
void OperationASTMatcher::addToFinder(clang::ast_matchers::MatchFinder& finder)
{
finder.addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, ArithmeticMatcher), this);
finder.addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, AssignmentMatcher), this);
finder.addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, UnaryArithmeticMatcher), this);
}
void OperationASTMatcher::run(const clang::ast_matchers::MatchFinder::MatchResult& result)
{
if (const auto* op = result.Nodes.getNodeAs<clang::BinaryOperator>("assignment"))
{
_op_finder->processArithmetic(op, *result.SourceManager);
}
else if (const auto* op = result.Nodes.getNodeAs<clang::BinaryOperator>("arithmetic"))
{
_op_finder->processArithmetic(op, *result.SourceManager);
}
else if (const auto* op = result.Nodes.getNodeAs<clang::UnaryOperator>("unary_arithmetic"))
{
_op_finder->processUnaryArithmetic(op, *result.SourceManager);
}
}

View File

@ -0,0 +1,170 @@
//
// Created by erki on 16.02.21.
//
#include "OperationFinder.hpp"
#include <iostream>
#include "ASTHelpers.hpp"
using namespace clang;
using namespace clang::ast_matchers;
namespace
{
std::string ResolveTypeName(const QualType& t)
{
if (t->isTypedefNameType())
{
const TypedefType* tdt = cast<TypedefType>(t);
assert(tdt);
llvm::outs() << "Typedef type: " << t.getAsString()
<< ", underlying: " << tdt->desugar().getAsString()
<< ".";
return tdt->desugar().getAsString();
}
else if (t->isBuiltinType())
{
llvm::outs() << "Builtin type: " << t.getAsString() << ".";
return t.getAsString();
}
else
{
llvm::outs() << "Other type: " << t.getAsString() << ".";
return t.getAsString();
}
};
}
OperationFinder::OperationFinder(IOperationOutput* storage)
: _storage(storage)
{
assert(storage);
}
void OperationFinder::processArithmetic(const clang::BinaryOperator* op, const clang::SourceManager& source_manager)
{
auto [file_name, log] = _createBaseOperationLog(op, source_manager);
const std::string op_code = getOpcode(op).str();
llvm::outs() << "\n\tBinary arithmetic: Type: " << op_code;
log.entry_type = OperationLog::BasicOperation::TYPE_NAME;
log.entry = _createBasicOperationLogEntry(op_code, op, op->getLHS(), op->getRHS());
_storage->pushOperation(file_name, std::move(log));
llvm::outs() << "\n";
}
void OperationFinder::processUnaryArithmetic(const clang::UnaryOperator* op, const clang::SourceManager& source_manager)
{
auto [file_name, log] = _createBaseOperationLog(op, source_manager);
const std::string op_code = getOpcode(op).str();
llvm::outs() << "\n\tUnary arithmetic: Type: " << op_code;
log.entry_type = OperationLog::BasicOperation::TYPE_NAME;
log.entry = _createBasicOperationLogEntry(op_code, op, op->getExprStmt(), nullptr);
_storage->pushOperation(file_name, std::move(log));
llvm::outs() << "\n";
}
void OperationFinder::processFunctionCall(const clang::CallExpr* call, const SourceManager& source_manager)
{
const FunctionDecl* func = call->getDirectCallee();
assert(func);
auto [file_name, log] = _createBaseOperationLog(call, source_manager);
const std::string func_name = func->getNameAsString();
llvm::outs() << "\n\tFunction call: func name: " << func_name << "\n\tResult eval type: ";
const std::string res_type = ResolveTypeName(func->getReturnType());
auto func_call = std::make_unique<OperationLog::FunctionCall>();
func_call->function_name = func_name;
func_call->call_result_type = res_type;
log.entry_type = OperationLog::FunctionCall::TYPE_NAME;
log.entry = std::move(func_call);
_storage->pushOperation(file_name, std::move(log));
llvm::outs() << "\n";
}
void OperationFinder::processArraySubscript(const clang::ArraySubscriptExpr* subscript, const clang::SourceManager& source_manager)
{
auto [file_name, log] = _createBaseOperationLog(subscript, source_manager);
llvm::outs() << "\n\tSubscript:";
log.entry_type = OperationLog::BasicOperation::TYPE_NAME;
log.entry = _createBasicOperationLogEntry("subscript", subscript, subscript->getBase(), subscript->getIdx());
_storage->pushOperation(file_name, std::move(log));
llvm::outs() << "\n";
}
void OperationFinder::branchEntered()
{
_current_branch++;
llvm::outs() << "Branch entered: " << _current_branch << "\n";
}
void OperationFinder::branchExited()
{
llvm::outs() << "Branch exited: " << _current_branch << "\n";
_current_branch--;
assert(_current_branch > -1);
}
std::unique_ptr<OperationLog::BasicOperation>
OperationFinder::_createBasicOperationLogEntry(const std::string& opcode, const Expr* source, const Expr* op1, const Expr* op2)
{
auto log = std::make_unique<OperationLog::BasicOperation>();
log->operation_name = opcode;
llvm::outs() << "\n\tExpression eval type: ";
log->type_result = ResolveTypeName(source->getType());
if (op1)
{
llvm::outs() << "\n\tLHS eval type: ";
log->type_lhs = ResolveTypeName(op1->getType());
}
if (op2)
{
llvm::outs() << "\n\tRHS eval type: ";
log->type_rhs = ResolveTypeName(op2->getType());
}
return log;
}
std::pair<std::string, OperationLog> OperationFinder::_createBaseOperationLog(const clang::Stmt* stmt, const clang::SourceManager& source_manager)
{
const auto [file_name, line_number, column_number] = resolveLocations(stmt, source_manager);
OperationLog log;
log.line = line_number;
log.branch_number = _current_branch;
llvm::outs() << file_name << ":"
<< line_number << ":"
<< column_number << ":";
return { file_name, std::move(log) };
}

View File

@ -0,0 +1,17 @@
//
// Created by erki on 02.03.21.
//
#include "OperationFinderAstAction.hpp"
#include "OperationFinderAstConsumer.hpp"
OperationFinderAstAction::OperationFinderAstAction(OperationFinder* op_finder)
: _op_finder(op_finder)
{ }
std::unique_ptr<clang::ASTConsumer> OperationFinderAstAction::newASTConsumer()
{
return std::unique_ptr<clang::ASTConsumer>(
new OperationFinderAstConsumer(_op_finder));
}

View File

@ -0,0 +1,19 @@
//
// Created by erki on 02.03.21.
//
#include "OperationFinderAstConsumer.hpp"
OperationFinderAstConsumer::OperationFinderAstConsumer(OperationFinder* op_finder)
: _visitor(op_finder)
{ }
void OperationFinderAstConsumer::Initialize(clang::ASTContext& context)
{
_visitor.NewContext(&context);
}
void OperationFinderAstConsumer::HandleTranslationUnit(clang::ASTContext& context)
{
_visitor.TraverseAST(context);
}

View File

@ -0,0 +1,154 @@
//
// Created by erki on 02.03.21.
//
#include "OperationFinderAstVisitor.hpp"
#include "OperationFinder.hpp"
#include "ASTHelpers.hpp"
OperationFinderAstVisitor::OperationFinderAstVisitor(OperationFinder* op_finder)
: _context(nullptr)
, _op_finder(op_finder)
{ }
void OperationFinderAstVisitor::NewContext(clang::ASTContext* context)
{
_context = context;
}
bool OperationFinderAstVisitor::VisitForStmt(clang::ForStmt* stmt)
{
assert(_context);
return true;
}
bool OperationFinderAstVisitor::VisitBinaryOperator(clang::BinaryOperator* op)
{
assert(_context);
_op_finder->processArithmetic(op, _context->getSourceManager());
return true;
}
bool OperationFinderAstVisitor::VisitUnaryOperator(clang::UnaryOperator* op)
{
assert(_context);
_op_finder->processUnaryArithmetic(op, _context->getSourceManager());
return true;
}
bool OperationFinderAstVisitor::VisitCallExpr(clang::CallExpr* call)
{
assert(_context);
_op_finder->processFunctionCall(call, _context->getSourceManager());
return true;
}
bool OperationFinderAstVisitor::VisitArraySubscriptExpr(clang::ArraySubscriptExpr* subscript)
{
assert(_context);
_op_finder->processArraySubscript(subscript, _context->getSourceManager());
return true;
}
bool OperationFinderAstVisitor::dataTraverseStmtPre(clang::Stmt* stmt)
{
assert(_context);
if (clang::Stmt* branch_entry = _isBranchEntry(stmt))
{
_branch_stack.push_back(branch_entry);
_op_finder->branchEntered();
}
return true;
}
bool OperationFinderAstVisitor::dataTraverseStmtPost(clang::Stmt* stmt)
{
assert(_context);
if (_loop_header.in_loop_header && (_loop_header.init == stmt || _loop_header.header_end == stmt))
{
if (_loop_header.header_end == stmt)
{
assert(_branch_stack.back() == stmt);
_branch_stack.pop_back();
_op_finder->branchExited();
if (_loop_header.init)
{
assert(_branch_stack.back() == _loop_header.init);
_branch_stack.pop_back();
_op_finder->branchExited();
}
_loop_header = {};
}
else if (_loop_header.init == stmt && !_loop_header.header_end)
{
assert(_branch_stack.back() == stmt);
_branch_stack.pop_back();
_op_finder->branchExited();
_loop_header = {};
}
}
else if (!_branch_stack.empty() && _branch_stack.back() == stmt)
{
_branch_stack.pop_back();
_op_finder->branchExited();
}
else
{
assert(std::find(_branch_stack.cbegin(), _branch_stack.cend(), stmt)
== _branch_stack.cend());
}
return true;
}
clang::Stmt* OperationFinderAstVisitor::_isBranchEntry(clang::Stmt* stmt)
{
if (auto* loop = clang::dyn_cast<clang::ForStmt>(stmt);
loop && loop->getInit() && (loop->getInc() || loop->getCond()))
{
_loop_header.init = loop->getInit();
if (loop->getCond())
{
_loop_header.header_start = loop->getCond();
_loop_header.header_end = loop->getCond();
}
if (loop->getInc())
{
if (!_loop_header.header_start)
_loop_header.header_start = loop->getInc();
_loop_header.header_end = loop->getInc();
}
_loop_header.in_loop_header = _loop_header.init || _loop_header.header_end;
if (_loop_header.init)
return _loop_header.init;
else if (_loop_header.header_start)
return _loop_header.header_end;
}
else if (_loop_header.in_loop_header && _loop_header.header_start == stmt)
{
return _loop_header.header_end;
}
return nullptr;
}

View File

@ -0,0 +1,44 @@
//
// Created by erki on 06.03.21.
//
#include "OperationLog.hpp"
namespace
{
template<typename T>
std::unique_ptr<T> DecodeType(const nlohmann::json& j, const std::string& entry_type)
{
if (entry_type == T::TYPE_NAME)
{
auto t = std::make_unique<T>();
j.get_to(*t);
return t;
}
else
{
return nullptr;
}
}
}
nlohmann::json OperationLog::FunctionCall::toJson() const
{
return *this;
}
nlohmann::json OperationLog::BasicOperation::toJson() const
{
return *this;
}
void OperationLog::DecodeEntry(const nlohmann::json& j)
{
if ((entry = DecodeType<BasicOperation>(j, entry_type)))
return;
else if ((entry = DecodeType<FunctionCall>(j, entry_type)))
return;
}

View File

@ -0,0 +1,59 @@
//
// Created by erki on 28.02.21.
//
#include "OperationStorage.hpp"
#include <fstream>
#include <filesystem>
#include <llvm/Support/CommandLine.h>
OperationStorage::~OperationStorage()
{ }
void OperationStorage::enablePrettyPrint()
{
_pretty_print = true;
}
void OperationStorage::toStream(std::ostream& stream)
{
nlohmann::json json = _operations;
if (_pretty_print)
stream << std::setw(4) << json;
else
stream << json;
}
void OperationStorage::toFile(const std::string& output_filename)
{
std::ofstream file(output_filename);
toStream(file);
}
void OperationStorage::pushOperation(const std::string& original_filename, OperationLog&& op)
{
const std::string filename = _convertFilepath(original_filename);
auto it = _operations.find(filename);
if (it == _operations.end())
it = _operations.emplace(filename, std::vector<OperationLog>()).first;
it->second.emplace_back(std::move(op));
}
const std::unordered_map<std::string, std::vector<OperationLog>>& OperationStorage::getOperations() const
{
return _operations;
}
std::string OperationStorage::_convertFilepath(const std::string& original)
{
const std::filesystem::path path = original;
return path.filename().string();
}

View File

@ -0,0 +1,31 @@
cmake_minimum_required(VERSION 3.14)
find_package(Catch2 REQUIRED)
# LLVM is typically compiled without RTTI. Weird linker errors ensue if
# you keep RTTI on and try to link.
if (NOT LLVM_ENABLE_RTTI)
if (MSVC)
string(REGEX REPLACE "/GR" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-")
else ()
string(REGEX REPLACE "-frtti" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
endif ()
endif ()
add_executable(op-finder-tests
main.cpp
fixtures/RunOnCodeFixture.cpp
basic_operations.cpp
branches.cpp
function_calls.cpp)
target_link_libraries(op-finder-tests
PUBLIC
op-finder-lib
Catch2::Catch2)
include(CTest)
include(Catch)
catch_discover_tests(op-finder-tests)

View File

@ -0,0 +1,95 @@
//
// Created by erki on 07.03.21.
//
#include <catch2/catch.hpp>
#include "fixtures/RunOnCodeFixture.hpp"
TEST_CASE("Finds binary operations", "[basic_operation]")
{
auto binary_operand = GENERATE(std::string("="), std::string("+"), std::string("-"), std::string("/"),
std::string("*"), std::string("<<"), std::string(">>"),
std::string("^"), std::string("=="), std::string("|"), std::string("&"));
RunOnCodeFixture fixture;
const std::string code = "int main() { int a; (void)(a " + binary_operand + " 4); }";
const auto& operations = fixture(code);
REQUIRE(operations.size() == 1);
const OperationLog& log = operations.front();
REQUIRE(log.entry_type == OperationLog::BasicOperation::TYPE_NAME);
const OperationLog::BasicOperation* op = (OperationLog::BasicOperation*)(log.entry.get());
REQUIRE(op->operation_name == binary_operand);
REQUIRE(op->type_lhs == "int");
REQUIRE(op->type_rhs == "int");
REQUIRE(op->type_result == "int");
}
TEST_CASE("Find unary operations", "[basic_operation]")
{
auto unary_operand = GENERATE(std::string("++"), std::string("--"), std::string("~"), std::string("!"));
RunOnCodeFixture fixture;
const std::string code = "int main() { int a; (void)(" + unary_operand + "a); }";
const auto& operations = fixture(code);
REQUIRE(operations.size() == 1);
const OperationLog& log = operations.front();
REQUIRE(log.entry_type == OperationLog::BasicOperation::TYPE_NAME);
const OperationLog::BasicOperation* op = (OperationLog::BasicOperation*)(log.entry.get());
REQUIRE(op->operation_name == unary_operand);
REQUIRE(op->type_lhs == "int");
REQUIRE(op->type_rhs.empty());
REQUIRE(op->type_result == "int");
}
TEST_CASE("Find subscript operation", "[basic_operation]")
{
RunOnCodeFixture fixture;
const std::string code = "int main() { int a[4]; (void)a[4]; }";
const auto& operations = fixture(code);
REQUIRE(operations.size() == 1);
const OperationLog& log = operations.front();
REQUIRE(log.entry_type == OperationLog::BasicOperation::TYPE_NAME);
const OperationLog::BasicOperation* op = (OperationLog::BasicOperation*)(log.entry.get());
REQUIRE(op->operation_name == "subscript");
REQUIRE(op->type_lhs == "int *");
REQUIRE(op->type_rhs == "int");
REQUIRE(op->type_result == "int");
}
TEST_CASE("Find subscript operation reversed", "[basic_operation]")
{
RunOnCodeFixture fixture;
const std::string code = "int main() { int a[4]; (void)4[a]; }";
const auto& operations = fixture(code);
REQUIRE(operations.size() == 1);
const OperationLog& log = operations.front();
REQUIRE(log.entry_type == OperationLog::BasicOperation::TYPE_NAME);
const OperationLog::BasicOperation* op = (OperationLog::BasicOperation*)(log.entry.get());
REQUIRE(op->operation_name == "subscript");
REQUIRE(op->type_lhs == "int *");
REQUIRE(op->type_rhs == "int");
REQUIRE(op->type_result == "int");
}

View File

@ -0,0 +1,125 @@
//
// Created by erki on 07.03.21.
//
#include <catch2/catch.hpp>
#include "fixtures/RunOnCodeFixture.hpp"
TEST_CASE("For loop without header.", "[branches][for_loops]")
{
RunOnCodeFixture fixture;
const std::string code = "int main() { int a; for (;;) {} }";
REQUIRE(fixture.runCode(code));
REQUIRE(fixture.storage.getOperations().empty());
}
TEST_CASE("For loop with init only.", "[branches][for_loops]")
{
RunOnCodeFixture fixture;
const std::string code = "int main() { int a; for (a = 4;;) {} }";
const auto& operations = fixture(code);
REQUIRE(operations.size() == 1);
const OperationLog& log = operations.front();
REQUIRE(log.branch_number == 0);
}
TEST_CASE("For loop with init & cond.", "[branches][for_loops]")
{
RunOnCodeFixture fixture;
const std::string code = "int main() { int a; for (a = 4; a < 4;) {} }";
const auto& operations = fixture(code);
REQUIRE(operations.size() == 2);
const OperationLog& log_init = operations.at(0);
REQUIRE(log_init.branch_number == 1);
const OperationLog& log_cond = operations.at(1);
REQUIRE(log_cond.branch_number == 2);
}
TEST_CASE("For loop with init & inc.", "[branches][for_loops]")
{
RunOnCodeFixture fixture;
const std::string code = "int main() { int a; for (a = 4;;a++) {} }";
const auto& operations = fixture(code);
REQUIRE(operations.size() == 2);
const OperationLog& log_init = operations.at(0);
REQUIRE(log_init.branch_number == 1);
const OperationLog& log_inc = operations.at(1);
REQUIRE(log_inc.branch_number == 2);
}
TEST_CASE("For loop with full header.", "[branches][for_loops]")
{
RunOnCodeFixture fixture;
const std::string code = "int main() { int a; for (a = 4; a < 4 ;a++) {} }";
const auto& operations = fixture(code);
REQUIRE(operations.size() == 3);
const OperationLog& log_init = operations.at(0);
REQUIRE(log_init.branch_number == 1);
const OperationLog& log_cond = operations.at(1);
REQUIRE(log_cond.branch_number == 2);
const OperationLog& log_inc = operations.at(2);
REQUIRE(log_inc.branch_number == 2);
}
TEST_CASE("For loop without init.", "[branches][for_loops]")
{
RunOnCodeFixture fixture;
const std::string code = "int main() { int a; for (;a < 4 ;a++) {} }";
const auto& operations = fixture(code);
REQUIRE(operations.size() == 2);
const OperationLog& log_cond = operations.at(0);
REQUIRE(log_cond.branch_number == 0);
const OperationLog& log_inc = operations.at(1);
REQUIRE(log_inc.branch_number == 0);
}
TEST_CASE("For loop closes branches inside the loop.", "[branches][for_loops]")
{
RunOnCodeFixture fixture;
const std::string code = "int main() { int a; for (a = 4; a < 4 ; a++) { a = 5; } }";
const auto& operations = fixture(code);
REQUIRE(operations.size() == 4);
const OperationLog& log_inner = operations.back();
REQUIRE(log_inner.branch_number == 0);
}
TEST_CASE("For loop closes branches outside the loop.", "[branches][for_loops]")
{
RunOnCodeFixture fixture;
const std::string code = "int main() { int a; for (a = 4; a < 4 ; a++) {} a = 5; }";
const auto& operations = fixture(code);
REQUIRE(operations.size() == 4);
const OperationLog& log_outer = operations.back();
REQUIRE(log_outer.branch_number == 0);
}

View File

@ -0,0 +1,37 @@
//
// Created by erki on 07.03.21.
//
#include "RunOnCodeFixture.hpp"
#include <catch2/catch.hpp>
#include <clang/Tooling/Tooling.h>
#include <clang/Frontend/FrontendActions.h>
RunOnCodeFixture::RunOnCodeFixture()
: finder(&storage)
, action(&finder)
{ }
bool RunOnCodeFixture::runCode(const std::string& code)
{
return clang::tooling::runToolOnCode(
clang::tooling::newFrontendActionFactory(&action)->create(),
code,
RunOnCodeFixture::INPUT_FILE
);
}
const std::vector<OperationLog>& RunOnCodeFixture::operator()(const std::string& code)
{
const bool success = runCode(code);
REQUIRE(success);
const auto& operations = storage.getOperations();
REQUIRE(operations.count(RunOnCodeFixture::INPUT_FILE) == 1);
return operations.at(RunOnCodeFixture::INPUT_FILE);
}

View File

@ -0,0 +1,27 @@
//
// Created by erki on 07.03.21.
//
#ifndef C_ANALYZER_RUNONCODEFIXTURE_HPP
#define C_ANALYZER_RUNONCODEFIXTURE_HPP
#include <OperationStorage.hpp>
#include <OperationFinder.hpp>
#include <OperationFinderAstAction.hpp>
struct RunOnCodeFixture
{
constexpr static char INPUT_FILE[] = "input.c";
OperationStorage storage;
OperationFinder finder;
OperationFinderAstAction action;
RunOnCodeFixture();
bool runCode(const std::string& code);
const std::vector<OperationLog>& operator()(const std::string& code);
};
#endif //C_ANALYZER_RUNONCODEFIXTURE_HPP

View File

@ -0,0 +1,55 @@
//
// Created by erki on 07.03.21.
//
#include <catch2/catch.hpp>
#include "fixtures/RunOnCodeFixture.hpp"
TEST_CASE("Find function call.", "[functions]")
{
RunOnCodeFixture fixture;
const std::string code = "int b(); int main() { b(); }";
const auto& operations = fixture(code);
REQUIRE(operations.size() == 1);
const OperationLog& log = operations.front();
REQUIRE(log.entry_type == OperationLog::FunctionCall::TYPE_NAME);
const OperationLog::FunctionCall* call = (OperationLog::FunctionCall*)log.entry.get();
REQUIRE(call->function_name == "b");
REQUIRE(call->call_result_type == "int");
}
TEST_CASE("Find stacked function calls.", "[functions]")
{
RunOnCodeFixture fixture;
const std::string code = "int a(int); int b(); int main() { a(b()); }";
const auto& operations = fixture(code);
REQUIRE(operations.size() == 2);
const OperationLog& log = operations.front();
REQUIRE(log.entry_type == OperationLog::FunctionCall::TYPE_NAME);
const OperationLog::FunctionCall* call = (OperationLog::FunctionCall*)log.entry.get();
REQUIRE(call->function_name == "a");
REQUIRE(call->call_result_type == "int");
const OperationLog& log_two = operations.back();
REQUIRE(log_two.entry_type == OperationLog::FunctionCall::TYPE_NAME);
const OperationLog::FunctionCall* call_two = (OperationLog::FunctionCall*)log_two.entry.get();
REQUIRE(call_two->function_name == "b");
REQUIRE(call_two->call_result_type == "int");
}

6
op-finder-tests/main.cpp Normal file
View File

@ -0,0 +1,6 @@
//
// Created by erki on 07.03.21.
//
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
#include <catch2/catch.hpp>

View File

@ -1,7 +1,4 @@
cmake_minimum_required(VERSION 3.17) cmake_minimum_required(VERSION 3.14)
find_package(Clang REQUIRED)
find_package(LLVM REQUIRED COMPONENTS Support Option Core)
# LLVM is typically compiled without RTTI. Weird linker errors ensue if # LLVM is typically compiled without RTTI. Weird linker errors ensue if
# you keep RTTI on and try to link. # you keep RTTI on and try to link.
@ -15,26 +12,9 @@ if (NOT LLVM_ENABLE_RTTI)
endif () endif ()
endif () endif ()
llvm_map_components_to_libnames(llvm_libs support option core)
add_executable(op-finder add_executable(op-finder
main.cpp main.cpp)
OperationFinder.cpp
)
target_include_directories(op-finder
PRIVATE ${LLVM_INCLUDE_DIRS}
)
target_compile_definitions(op-finder
PRIVATE
${LLVM_DEFINITIONS}
)
target_link_libraries(op-finder target_link_libraries(op-finder
PRIVATE PUBLIC
${llvm_libs} op-finder-lib)
clangTooling
clangBasic
clangASTMatchers
)

View File

@ -1,206 +0,0 @@
//
// Created by erki on 16.02.21.
//
#include "OperationFinder.hpp"
#include <iostream>
#include <sstream>
using namespace clang;
using namespace clang::ast_matchers;
namespace
{
StatementMatcher AssignmentMatcher =
binaryOperation(isAssignmentOperator(),
hasLHS(expr().bind("lhs")),
hasRHS(expr().bind("rhs"))).bind("assignment");
StatementMatcher ArithmeticMatcher =
binaryOperation(hasAnyOperatorName("+", "-", "/", "*", "<", ">",
"<=", ">=", "==", "<<", ">>", "%"),
hasLHS(expr().bind("lhs")),
hasRHS(expr().bind("lhs"))).bind("arithmetic");
StatementMatcher UnaryArithmeticMatcher =
unaryOperator(hasAnyOperatorName("!", "++", "--"),
hasUnaryOperand(expr().bind("lhs"))).bind("unary_arithmetic");
StatementMatcher CompoundMatcher =
compoundStmt().bind("compound_stmt");
bool isInMainFile(const MatchFinder::MatchResult &result, const SourceLocation &loc)
{
return result.Context->getSourceManager().isWrittenInMainFile(loc);
}
template<typename TOp>
StringRef getOpcode(const TOp *op)
{
return op->getOpcodeStr(op->getOpcode());
}
}
void OperationFinder::addMatcher(MatchFinder &finder)
{
finder.addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, ArithmeticMatcher), this);
finder.addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, AssignmentMatcher), this);
finder.addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, CompoundMatcher), this);
finder.addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, UnaryArithmeticMatcher), this);
}
void OperationFinder::run(const MatchFinder::MatchResult &result)
{
if (result.Nodes.getNodeAs<clang::BinaryOperator>("assignment"))
{
_processAssignment(result);
}
else if (result.Nodes.getNodeAs<clang::BinaryOperator>("arithmetic"))
{
_processArithmetic(result);
}
else if (result.Nodes.getNodeAs<clang::CompoundStmt>("compound_stmt"))
{
_processCompoundStmt(result);
}
else if (result.Nodes.getNodeAs<clang::UnaryOperator>("unary_arithmetic"))
{
_processUnaryArithmetic(result);
}
}
void OperationFinder::_processAssignment(const clang::ast_matchers::MatchFinder::MatchResult& result)
{
const BinaryOperator* op = result.Nodes.getNodeAs<BinaryOperator>("assignment");
assert(op);
if (!isInMainFile(result, op->getBeginLoc()))
return;
const auto& loc = op->getBeginLoc();
const auto& source_manager = *result.SourceManager;
llvm::outs() << source_manager.getFilename(loc) << ":"
<< source_manager.getSpellingLineNumber(loc) << ":"
<< source_manager.getSpellingColumnNumber(loc) << ":"
<< "Assignment: Assigned to: ";
_processExpressionTypes(op, op->getLHS(), op->getRHS());
llvm::outs() << "\n";
}
void OperationFinder::_processArithmetic(const MatchFinder::MatchResult& result)
{
const BinaryOperator* op = result.Nodes.getNodeAs<clang::BinaryOperator>("arithmetic");
assert(op);
if (!isInMainFile(result, op->getBeginLoc()))
return;
const auto& loc = op->getBeginLoc();
const auto& source_manager = *result.SourceManager;
llvm::outs() << source_manager.getFilename(loc) << ":"
<< source_manager.getSpellingLineNumber(loc) << ":"
<< source_manager.getSpellingColumnNumber(loc) << ":"
<< "Arithmetic: Type: " << getOpcode(op) << " LHS: ";
_processExpressionTypes(op, op->getLHS(), op->getRHS());
llvm::outs() << "\n";
}
void OperationFinder::_processCompoundStmt(const clang::ast_matchers::MatchFinder::MatchResult& result)
{
const CompoundStmt* stmt = result.Nodes.getNodeAs<CompoundStmt>("compound_stmt");
assert(stmt);
const CompoundAssignOperator* op = dyn_cast<CompoundAssignOperator>(stmt->getStmtExprResult());
if (!op || !op->isCompoundAssignmentOp())
return;
if (!isInMainFile(result, op->getBeginLoc()))
return;
const auto& loc = op->getBeginLoc();
const auto& source_manager = *result.SourceManager;
llvm::outs() << source_manager.getFilename(loc) << ":"
<< source_manager.getSpellingLineNumber(loc) << ":"
<< source_manager.getSpellingColumnNumber(loc) << ":"
<< "Compound assignment: Op code: " << getOpcode(op) << " ";
_processExpressionTypes(op, op->getLHS(), op->getRHS());
llvm::outs() << "\n";
}
void OperationFinder::_processUnaryArithmetic(const MatchFinder::MatchResult &result)
{
const UnaryOperator* op = result.Nodes.getNodeAs<UnaryOperator>("unary_arithmetic");
const Expr* lhs = result.Nodes.getNodeAs<Expr>("lhs");
assert(op);
assert(lhs);
if (!isInMainFile(result, lhs->getBeginLoc()))
return;
const auto& loc = lhs->getBeginLoc();
const auto& source_manager = *result.SourceManager;
llvm::outs() << source_manager.getFilename(loc) << ":"
<< source_manager.getSpellingLineNumber(loc) << ":"
<< source_manager.getSpellingColumnNumber(loc) << ":"
<< "Unary arithmetic: Type: " << getOpcode(op) << " LHS: ";
_processExpressionTypes(op, lhs, nullptr);
llvm::outs() << "\n";
}
void OperationFinder::_processExpressionTypes(const Expr* source, const Expr* op1, const Expr* op2)
{
auto printTypeName = [](const QualType& t) -> void
{
if (t->isTypedefNameType())
{
const TypedefType* tdt = cast<TypedefType>(t);
assert(tdt);
llvm::outs() << "Typedef type: " << t.getAsString()
<< ", underlying: " << tdt->desugar().getAsString()
<< ".";
}
else if (t->isBuiltinType())
{
llvm::outs() << "Builtin type: " << t.getAsString() << ".";
}
else
{
llvm::outs() << "Other type: " << t.getAsString() << ".";
}
};
llvm::outs() << "Expression types:\n\tExpression eval type: ";
printTypeName(source->getType());
if (op1)
{
llvm::outs() << "\n\tLHS eval type: ";
printTypeName(op1->getType());
}
if (op2)
{
llvm::outs() << "\n\tRHS eval type: ";
printTypeName(op2->getType());
}
}

View File

@ -1,26 +0,0 @@
//
// Created by erki on 16.02.21.
//
#ifndef LLVM_PROTO_OPERATIONFINDER_HPP
#define LLVM_PROTO_OPERATIONFINDER_HPP
#include <clang/ASTMatchers/ASTMatchers.h>
#include <clang/ASTMatchers/ASTMatchFinder.h>
class OperationFinder : public clang::ast_matchers::MatchFinder::MatchCallback
{
public:
void addMatcher(clang::ast_matchers::MatchFinder& finder);
virtual void run(const clang::ast_matchers::MatchFinder::MatchResult& result) override;
private:
void _processAssignment(const clang::ast_matchers::MatchFinder::MatchResult& result);
void _processArithmetic(const clang::ast_matchers::MatchFinder::MatchResult& result);
void _processCompoundStmt(const clang::ast_matchers::MatchFinder::MatchResult& result);
void _processUnaryArithmetic(const clang::ast_matchers::MatchFinder::MatchResult& result);
void _processExpressionTypes(const clang::Expr* source, const clang::Expr* op1, const clang::Expr* op2);
};
#endif //LLVM_PROTO_OPERATIONFINDER_HPP

View File

@ -5,7 +5,12 @@
// Declares llvm::cl::extrahelp. // Declares llvm::cl::extrahelp.
#include "llvm/Support/CommandLine.h" #include "llvm/Support/CommandLine.h"
#include <iostream>
#include "OperationAstMatcher.hpp"
#include "OperationFinder.hpp" #include "OperationFinder.hpp"
#include "OperationStorage.hpp"
#include "OperationFinderAstAction.hpp"
using namespace clang::tooling; using namespace clang::tooling;
using namespace clang::ast_matchers; using namespace clang::ast_matchers;
@ -13,7 +18,15 @@ using namespace llvm;
// Apply a custom category to all command-line options so that they are the // Apply a custom category to all command-line options so that they are the
// only ones displayed. // only ones displayed.
static llvm::cl::OptionCategory MyToolCategory("opcounter options"); static cl::OptionCategory MyToolCategory("op-finder options");
static cl::opt<std::string> OutputFile("o", cl::desc("File to output the JSON to."),
cl::cat(MyToolCategory));
static cl::opt<std::string> RootDirectory("r", cl::desc("The root directory of the source files."),
cl::cat(MyToolCategory));
static cl::opt<bool> PrettyPrint("pretty", cl::desc("Pretty-print the output JSON."));
// CommonOptionsParser declares HelpMessage with a description of the common // CommonOptionsParser declares HelpMessage with a description of the common
// command-line options related to the compilation database and input files. // command-line options related to the compilation database and input files.
@ -21,7 +34,9 @@ static llvm::cl::OptionCategory MyToolCategory("opcounter options");
static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
// A help message for this specific tool can be added afterwards. // A help message for this specific tool can be added afterwards.
static cl::extrahelp MoreHelp("\nMore help text...\n"); static cl::extrahelp MoreHelp("\nThe program takes the input <source0> ... files, parses their\n"
"AST and outputs a singular file containing a list of all noteworthy operations\n"
"for later analysis.\n");
int main(int argc, const char** argv) int main(int argc, const char** argv)
{ {
@ -37,10 +52,30 @@ int main(int argc, const char** argv)
ClangTool Tool(OptionsParser.getCompilations(), ClangTool Tool(OptionsParser.getCompilations(),
OptionsParser.getSourcePathList()); OptionsParser.getSourcePathList());
OperationFinder op_finder; OperationStorage storage;
MatchFinder finder;
op_finder.addMatcher(finder); if (PrettyPrint.getValue())
storage.enablePrettyPrint();
OperationFinder op_finder(&storage);
#if 0
MatchFinder matcher;
OperationASTMatcher finder(&op_finder);
finder.addToFinder(matcher);
//Tool.run(newFrontendActionFactory<DebuggeringASTAction>().get());
return Tool.run(newFrontendActionFactory(&finder).get()); return Tool.run(newFrontendActionFactory(&finder).get());
#endif
OperationFinderAstAction action(&op_finder);
Tool.run(newFrontendActionFactory(&action).get());
if (!OutputFile.getValue().empty())
storage.toFile(OutputFile.getValue());
else
storage.toStream(std::cout);
} }

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,213 @@
{
"matrix.c": [
{
"branch_number": 1,
"entry": {
"operation_name": "=",
"type_lhs": "int",
"type_result": "int",
"type_rhs": "int"
},
"entry_type": "basic_operation",
"line": 20
},
{
"branch_number": 2,
"entry": {
"operation_name": "<",
"type_lhs": "int",
"type_result": "int",
"type_rhs": "int"
},
"entry_type": "basic_operation",
"line": 20
},
{
"branch_number": 2,
"entry": {
"operation_name": "++",
"type_lhs": "int",
"type_result": "int",
"type_rhs": ""
},
"entry_type": "basic_operation",
"line": 20
},
{
"branch_number": 1,
"entry": {
"operation_name": "=",
"type_lhs": "int",
"type_result": "int",
"type_rhs": "int"
},
"entry_type": "basic_operation",
"line": 22
},
{
"branch_number": 2,
"entry": {
"operation_name": "<",
"type_lhs": "int",
"type_result": "int",
"type_rhs": "int"
},
"entry_type": "basic_operation",
"line": 22
},
{
"branch_number": 2,
"entry": {
"operation_name": "++",
"type_lhs": "int",
"type_result": "int",
"type_rhs": ""
},
"entry_type": "basic_operation",
"line": 22
},
{
"branch_number": 0,
"entry": {
"operation_name": "=",
"type_lhs": "unsigned short",
"type_result": "unsigned short",
"type_rhs": "unsigned short"
},
"entry_type": "basic_operation",
"line": 24
},
{
"branch_number": 0,
"entry": {
"operation_name": "subscript",
"type_lhs": "volatile UInt16 *",
"type_result": "unsigned short",
"type_rhs": "int"
},
"entry_type": "basic_operation",
"line": 24
},
{
"branch_number": 0,
"entry": {
"operation_name": "subscript",
"type_lhs": "volatile UInt16 (*)[5]",
"type_result": "volatile UInt16 [5]",
"type_rhs": "int"
},
"entry_type": "basic_operation",
"line": 24
},
{
"branch_number": 1,
"entry": {
"operation_name": "=",
"type_lhs": "int",
"type_result": "int",
"type_rhs": "int"
},
"entry_type": "basic_operation",
"line": 25
},
{
"branch_number": 2,
"entry": {
"operation_name": "<",
"type_lhs": "int",
"type_result": "int",
"type_rhs": "int"
},
"entry_type": "basic_operation",
"line": 25
},
{
"branch_number": 2,
"entry": {
"operation_name": "++",
"type_lhs": "int",
"type_result": "int",
"type_rhs": ""
},
"entry_type": "basic_operation",
"line": 25
},
{
"branch_number": 0,
"entry": {
"operation_name": "subscript",
"type_lhs": "volatile UInt16 *",
"type_result": "unsigned short",
"type_rhs": "int"
},
"entry_type": "basic_operation",
"line": 27
},
{
"branch_number": 0,
"entry": {
"operation_name": "subscript",
"type_lhs": "volatile UInt16 (*)[5]",
"type_result": "volatile UInt16 [5]",
"type_rhs": "int"
},
"entry_type": "basic_operation",
"line": 27
},
{
"branch_number": 0,
"entry": {
"operation_name": "*",
"type_lhs": "int",
"type_result": "int",
"type_rhs": "int"
},
"entry_type": "basic_operation",
"line": 27
},
{
"branch_number": 0,
"entry": {
"operation_name": "subscript",
"type_lhs": "const UInt16 *",
"type_result": "unsigned short",
"type_rhs": "int"
},
"entry_type": "basic_operation",
"line": 27
},
{
"branch_number": 0,
"entry": {
"operation_name": "subscript",
"type_lhs": "const UInt16 (*)[4]",
"type_result": "const UInt16 [4]",
"type_rhs": "int"
},
"entry_type": "basic_operation",
"line": 27
},
{
"branch_number": 0,
"entry": {
"operation_name": "subscript",
"type_lhs": "const UInt16 *",
"type_result": "unsigned short",
"type_rhs": "int"
},
"entry_type": "basic_operation",
"line": 27
},
{
"branch_number": 0,
"entry": {
"operation_name": "subscript",
"type_lhs": "const UInt16 (*)[5]",
"type_result": "const UInt16 [5]",
"type_rhs": "int"
},
"entry_type": "basic_operation",
"line": 27
}
]
}

View File

@ -0,0 +1,39 @@
import json
from dataclasses import dataclass
from typing import Dict, List
@dataclass
class GCovLine:
line_number: int
count: int
branch_number: int = 0
class GCovFile:
def __init__(self, path: str) -> None:
self._path = path
self._data: dict = {}
self.files: Dict[str, List[GCovLine]] = {}
def read(self) -> None:
with open(self._path, "r") as infile:
self._data = json.load(infile)
for file in self._data["files"]:
name: str = file["file"]
lines: List[GCovLine] = []
for line in file["lines"]:
# Branch specific identification. TODO! later.
lines.append(GCovLine(line["line_number"], line["count"]))
if len(line["branches"]):
# GCov reports branches in reverse order to our parser.
branch_number = len(line["branches"])
for branch in line["branches"]:
lines.append(GCovLine(line["line_number"], branch["count"], branch_number))
branch_number -= 1
self.files[name] = lines

View File

@ -0,0 +1,73 @@
import json
from enum import Enum
from dataclasses import dataclass
from typing import Any, Dict, List, Union
class OperationType(Enum):
BASIC = "basic_operation"
FUNCTION_CALL = "function_call"
@dataclass(frozen=True)
class BasicOperation:
operation_name: str
type_lhs: str
type_rhs: str
type_result: str
@dataclass(frozen=True)
class FunctionCall:
function_name: str
call_result_type: str
UniqueOperation = Union[BasicOperation, FunctionCall]
@dataclass
class OperationLog:
line: int
branch_number: int
entry_type: OperationType
entry: UniqueOperation
def __post_init__(self) -> None:
self.entry_type = OperationType(self.entry_type)
if self.entry_type == OperationType.BASIC:
self.entry = BasicOperation(**self.entry)
elif self.entry_type == OperationType.FUNCTION_CALL:
self.entry = FunctionCall(**self.entry)
else:
assert False, "Unaccounted for operation type."
class OperationLogReader:
def __init__(self, path: str) -> None:
self._path = path
self._data: dict = {}
self.files: Dict[str, List[OperationLog]] = {}
def read(self) -> None:
with open(self._path, "r") as infile:
self._data = json.load(infile)
for name, ops_list in self._data.items():
ops: List[OperationLog] = []
for op_json in ops_list:
ops.append(OperationLog(**op_json))
self.files[name] = ops
def get_lines(self, file: str, line_number: int) -> List[OperationLog]:
res: List[OperationLog] = []
for line in self.files[file]:
if line.line == line_number:
res.append(line)
return res

View File

@ -0,0 +1,140 @@
import json
from dataclasses import asdict
from typing import Dict, List, Tuple
from gcovreader import GCovFile
from opfinderreader import OperationLogReader, UniqueOperation, OperationLog
class OpSummarizer:
def __init__(self, gcov_path: str, opfinder_path: str) -> None:
self.gcov = GCovFile(gcov_path)
self.ops = OperationLogReader(opfinder_path)
self.gcov.read()
self.ops.read()
self.model: Dict[UniqueOperation, Tuple[float, float]] = None
self.uncounted_estimates: Dict[UniqueOperation, int] = {}
self.energy_consumption_estimation = 0.0
self.execution_time_estimation = 0.0
def add_model(self, model_path: str) -> None:
with open(model_path, "r") as model_file:
data = json.load(model_file)
assert type(data) == list, "Model file did not contain a JSON list."
self.model = {}
for entry in data:
unique_op = OperationLog(**entry["unique_operation"]).entry
energy = entry["energy_consumption"]
time = entry["execution_time"]
self.model[unique_op] = [energy, time]
def count_operations(self, file: str) -> Dict[UniqueOperation, int]:
if file not in self.gcov.files or file not in self.ops.files:
print(f"Gcov files: {self.gcov.files.keys()}")
print(f"Opfinder files: {self.ops.files.keys()}")
raise RuntimeError(f"File {file} not in both parsers.")
op_counter: Dict[UniqueOperation, int] = {}
for gcov_line in self.gcov.files[file]:
op_lines = self.ops.get_lines(file, gcov_line.line_number)
for op_log in op_lines:
# TODO: revise this. Need a special case for for-loop clauses
# or branching in general.
if op_log.branch_number != gcov_line.branch_number:
continue
unique_op = op_log.entry
if unique_op in op_counter:
op_counter[unique_op] += gcov_line.count
else:
op_counter[unique_op] = gcov_line.count
return op_counter
def update_estimations(self, uops: Dict[UniqueOperation, int]) -> None:
assert self.model, "Model not populated."
for unique_op, op_count in uops.items():
if unique_op in self.model:
energy, time = self.model[unique_op]
self.energy_consumption_estimation += energy * op_count
self.execution_time_estimation += time * op_count
elif unique_op in self.uncounted_estimates:
self.uncounted_estimates[unique_op] += op_count
else:
self.uncounted_estimates[unique_op] = op_count
@staticmethod
def operation_count_to_json_dict(unique_ops: Dict[UniqueOperation, int]) -> List[Dict]:
out = []
for uo, uo_count in unique_ops.items():
d = asdict(uo)
d["count"] = uo_count
out.append(d)
return out
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Merges gcovr and op-finder outputs.")
parser.add_argument("files", metavar="FILES", type=str, nargs="+",
help="The files to accumulate.")
parser.add_argument("--gcov", type=str, default="./data/gcov.json",
help="The gcovr json file to use.")
parser.add_argument("--finder", type=str, default="./data/opfinder.json",
help="The op-finder json file to use.")
parser.add_argument("--output", type=str, default=None, required=False,
help="The file to output the data to.")
parser.add_argument("--model", type=str, default=None, required=False,
help="The JSON file containing the system model.")
args = parser.parse_args()
summarizer = OpSummarizer(args.gcov, args.finder)
if args.model:
summarizer.add_model(args.model)
uops_dictionary = {}
total_num = 0
for file_name in args.files:
ops = summarizer.count_operations(file_name)
uops_dictionary[file_name] = summarizer.operation_count_to_json_dict(ops)
if args.model:
summarizer.update_estimations(ops)
print(f"Unique operations for file {file_name}:")
for uop, count in ops.items():
print(f"\t{count}: {uop}")
total_num += count
print("---------")
print(f"Total count: {total_num}")
if args.output:
with open(args.output, "w") as outfile:
json.dump(uops_dictionary, outfile)
if args.model:
power_usage_estimate = summarizer.energy_consumption_estimation
time_estimate = summarizer.execution_time_estimation
print("---------")
print(f"Total energy usage estimation: {power_usage_estimate} mJ\nTotal execution time estimation: {time_estimate} ms")
if summarizer.uncounted_estimates:
print("Operations not accounted for (missing from the model):")
for uop, count in summarizer.uncounted_estimates.items():
print(f"\t{uop}: {count}")
else:
print("No operations missing from the model.")

76
op-summarizer/tests.py Normal file
View File

@ -0,0 +1,76 @@
import unittest
import subprocess
import json
from dataclasses import asdict
from typing import Dict
from opfinderreader import UniqueOperation
from opsummarizer import OpSummarizer
class Compiler:
def __init__(self, root_file: str) -> None:
self.root_file = root_file
self.gcov_file = f"./{root_file}_gcov.json"
self.opfinder_file = f"./{root_file}_opfinder.json"
def compile_and_profile(self) -> None:
output_file = f"{self.root_file}.out"
input_file = f"{self.root_file}.c"
subprocess.call(["gcc",
"-fprofile-arcs",
"-ftest-coverage",
"-o", output_file,
input_file])
subprocess.call([f"./{output_file}"])
subprocess.call(["gcovr",
"-r", ".",
"--json",
"--output", self.gcov_file])
def find_operations(self) -> None:
input_file = f"{self.root_file}.c"
subprocess.call(["./op-finder",
"-o", self.opfinder_file,
input_file])
class SummarizerCreatesExpectedOutput(unittest.TestCase):
def __init__(self, test_name, file_name) -> None:
super(SummarizerCreatesExpectedOutput, self).__init__(test_name)
self.root_file_name = file_name
self.compiler = Compiler(file_name)
def setUp(self) -> None:
self.compiler.compile_and_profile()
self.compiler.find_operations()
def _get_etalon(self) -> Dict:
filename = f"{self.root_file_name}_expected.json"
with open(filename, "r") as f:
return json.load(f)[f"{self.root_file_name}.c"]
def test_summarizer_output(self) -> None:
summarizer = OpSummarizer(self.compiler.gcov_file, self.compiler.opfinder_file)
found = summarizer.count_operations(f"{self.root_file_name}.c")
found = summarizer.operation_count_to_json_dict(found)
etalon = self._get_etalon()
self.assertEqual(found, etalon, msg="Found operations doesn't match etalon dictionary.")
if __name__ == "__main__":
suite = unittest.TestSuite()
suite.addTest(SummarizerCreatesExpectedOutput("test_summarizer_output", "matrix"))
# suite.addTest(SummarizerCreatesExpectedOutput("test_summarizer_output", "gauss_blur"))
# suite.addTest(SummarizerCreatesExpectedOutput("test_summarizer_output", "for_loop"))
# suite.addTest(SummarizerCreatesExpectedOutput("test_summarizer_output", "fir"))
unittest.TextTestRunner(verbosity=2).run(suite)

235
testcases/Dhrystone.c Normal file
View File

@ -0,0 +1,235 @@
/*******************************************************************************
*
* Name : Dhrystone
* Purpose : Benchmark the Dhrystone code. This benchmark is used to gauge
* the performance of the microcontroller in handling pointers,
* structures and strings.
*
*******************************************************************************/
#include <stdio.h>
#include <string.h>
#define LOOPS 100 /* Use this for slow or 16 bit machines */
#define structassign(d, s) d = s
typedef enum {
Ident1, Ident2, Ident3, Ident4, Ident5
} Enumeration;
typedef int OneToThirty;
typedef int OneToFifty;
typedef unsigned char CapitalLetter;
typedef unsigned char String30[31];
typedef int Array1Dim[51];
typedef int Array2Dim[10][10];
struct Record
{
struct Record *PtrComp;
Enumeration Discr;
Enumeration EnumComp;
OneToFifty IntComp;
String30 StringComp;
};
typedef struct Record RecordType;
typedef RecordType * RecordPtr;
typedef int boolean;
//#define NULL 0
#define TRUE 1
#define FALSE 0
#define REG register
int IntGlob;
boolean BoolGlob;
unsigned char Char1Glob;
unsigned char Char2Glob;
Array1Dim Array1Glob;
Array2Dim Array2Glob;
RecordPtr PtrGlb;
RecordPtr PtrGlbNext;
RecordType rec1, rec2;
Enumeration Func1(CapitalLetter CharPar1, CapitalLetter CharPar2) {
REG CapitalLetter CharLoc1;
REG CapitalLetter CharLoc2;
CharLoc1 = CharPar1;
CharLoc2 = CharLoc1;
if (CharLoc2 != CharPar2)
return (Ident1);
else
return (Ident2);
}
boolean Func2(String30 StrParI1, String30 StrParI2) {
REG OneToThirty IntLoc;
REG CapitalLetter CharLoc;
IntLoc = 1;
while (IntLoc <= 1)
if (Func1(StrParI1[IntLoc], StrParI2[IntLoc + 1]) == Ident1) {
CharLoc = 'A';
++IntLoc;
}
if (CharLoc >= 'W' && CharLoc <= 'Z')
IntLoc = 7;
if (CharLoc == 'X')
return (TRUE);
else {
if (strcmp(StrParI1, StrParI2) > 0) {
IntLoc += 7;
return (TRUE);
} else
return (FALSE);
}
}
boolean Func3(Enumeration EnumParIn) {
REG Enumeration EnumLoc;
EnumLoc = EnumParIn;
if (EnumLoc == Ident3)
return (TRUE);
return (FALSE);
}
void Proc7(OneToFifty IntParI1, OneToFifty IntParI2, OneToFifty *IntParOut) {
REG OneToFifty IntLoc;
IntLoc = IntParI1 + 2;
*IntParOut = IntParI2 + IntLoc;
}
void Proc4(void) {
REG boolean BoolLoc;
BoolLoc = Char1Glob == 'A';
BoolLoc |= BoolGlob;
Char2Glob = 'B';
}
void Proc5(void) {
Char1Glob = 'A';
BoolGlob = FALSE;
}
void Proc6(Enumeration EnumParIn, Enumeration *EnumParOut) {
*EnumParOut = EnumParIn;
if (!Func3(EnumParIn))
*EnumParOut = Ident4;
switch (EnumParIn) {
case Ident1:
*EnumParOut = Ident1;
break;
case Ident2:
if (IntGlob > 100)
*EnumParOut = Ident1;
else
*EnumParOut = Ident4;
break;
case Ident3:
*EnumParOut = Ident2;
break;
case Ident4:
break;
case Ident5:
*EnumParOut = Ident3;
}
}
void Proc3(RecordPtr *PtrParOut) {
if (PtrGlb != NULL)
*PtrParOut = PtrGlb->PtrComp;
else
IntGlob = 100;
Proc7(10, IntGlob, &PtrGlb->IntComp);
}
void Proc1(RecordPtr PtrParIn) {
#define NextRecord (*(PtrParIn->PtrComp))
structassign(NextRecord, *PtrGlb);
PtrParIn->IntComp = 5;
NextRecord.IntComp = PtrParIn->IntComp;
NextRecord.PtrComp = PtrParIn->PtrComp;
Proc3(&NextRecord.PtrComp);
if (NextRecord.Discr == Ident1) {
NextRecord.IntComp = 6;
Proc6(PtrParIn->EnumComp, &NextRecord.EnumComp);
NextRecord.PtrComp = PtrGlb->PtrComp;
Proc7(NextRecord.IntComp, 10, &NextRecord.IntComp);
} else
structassign(*PtrParIn, NextRecord);
#undef NextRecord
}
void Proc2(OneToFifty *IntParIO) {
REG OneToFifty IntLoc;
REG Enumeration EnumLoc;
IntLoc = *IntParIO + 10;
for (;;) {
if (Char1Glob == 'A') {
--IntLoc;
*IntParIO = IntLoc - IntGlob;
EnumLoc = Ident1;
}
if (EnumLoc == Ident1)
break;
}
}
void Proc8(Array1Dim Array1Par, Array2Dim Array2Par, OneToFifty IntParI1,
OneToFifty IntParI2) {
REG OneToFifty IntLoc;
REG OneToFifty IntIndex;
IntLoc = IntParI1 + 5;
Array1Par[IntLoc] = IntParI2;
Array1Par[IntLoc + 1] = Array1Par[IntLoc];
Array1Par[IntLoc + 30] = IntLoc;
for (IntIndex = IntLoc; IntIndex <= (IntLoc + 1); ++IntIndex)
Array2Par[IntLoc][IntIndex] = IntLoc;
++Array2Par[IntLoc][IntLoc - 1];
Array2Par[IntLoc + 20][IntLoc] = Array1Par[IntLoc];
IntGlob = 5;
}
void Proc0(void) {
OneToFifty IntLoc1;
REG OneToFifty IntLoc2;
OneToFifty IntLoc3;
REG unsigned char CharLoc;
REG unsigned char CharIndex;
Enumeration EnumLoc;
String30 String1Loc;
String30 String2Loc;
//extern unsigned char *malloc();
long time(long *);
long starttime;
long benchtime;
long nulltime;
register unsigned int i;
for (i = 0; i < LOOPS; ++i)
;
PtrGlbNext = &rec1; /* (RecordPtr) malloc(sizeof(RecordType)); */
PtrGlb = &rec2; /* (RecordPtr) malloc(sizeof(RecordType)); */
PtrGlb->PtrComp = PtrGlbNext;
PtrGlb->Discr = Ident1;
PtrGlb->EnumComp = Ident3;
PtrGlb->IntComp = 40;
strcpy(PtrGlb->StringComp, "DHRYSTONE PROGRAM, SOME STRING");
strcpy(String1Loc, "DHRYSTONE PROGRAM, 1'ST STRING"); /*GOOF*/
Array2Glob[8][7] = 10; /* Was missing in published program */
for (i = 0; i < LOOPS; ++i) {
Proc5();
Proc4();
IntLoc1 = 2;
IntLoc2 = 3;
strcpy(String2Loc, "DHRYSTONE PROGRAM, 2'ND STRING");
EnumLoc = Ident2;
BoolGlob = !Func2(String1Loc, String2Loc);
while (IntLoc1 < IntLoc2) {
IntLoc3 = 5 * IntLoc1 - IntLoc2;
Proc7(IntLoc1, IntLoc2, &IntLoc3);
++IntLoc1;
}
Proc8(Array1Glob, Array2Glob, IntLoc1, IntLoc3);
Proc1(PtrGlb);
for (CharIndex = 'A'; CharIndex <= Char2Glob; ++CharIndex)
if (EnumLoc == Func1(CharIndex, 'C'))
Proc6(Ident1, &EnumLoc);
IntLoc3 = IntLoc2 * IntLoc1;
IntLoc2 = IntLoc3 / IntLoc1;
IntLoc2 = 7 * (IntLoc3 - IntLoc2) - IntLoc1;
Proc2(&IntLoc1);
}
}
int main(void) {
// TRISG = 0;
//PORTG = 0;
int i;
//while(1){
// LATGINV = 0x0001;
for(i = 0; i < 100; i++){
Proc0();
}
//}
}

241
testcases/Whetstone.c Normal file
View File

@ -0,0 +1,241 @@
/***************************************************************************
*
* Name : Whetstone
* Purpose : Benchmark the Whetstone code. The code focuses on scientific
* functions such as sine, cosine, exponents and logarithm on
* fixed and floating point numbers.
*
***************************************************************************/
/* List of changes (since TI slaa205b.pdf):
- void type of PA(), P0(), P3()
- dummy functions to prevent optimization
- float (not double) variants of math funcions and constants (this
is no matter for AVR)
*/
#include <math.h>
#ifdef __AVR__
# define atanf(x) atan(x)
# define cosf(x) cos(x)
# define expf(x) exp(x)
# define logf(x) log(x)
# define sinf(x) sin(x)
# define sqrtf(x) sqrt(x)
#endif
/* Extern functions. To prevent compiler's optimization. */
void dummy1 (float x ){}
void dummy2 (float x , float y){}
void PA (float E[5]);
void P0 (void);
void P3 (float *X, float *Y, float *Z);
float T, T1, T2, E1[5];
int J, K, L;
float X1, X2, X3, X4;
long ptime, time0;
int main()
{
//TRISG = 0;
//PORTG = 0;
int i;
//while(1){
//LATGINV = 0x0001;
for(i = 0; i < 100; i++){
int LOOP, I, II, JJ, N1, N2, N3, N4, N5, N6, N7, N8, N9, N10, N11;
float X, Y, Z;
T = .499975;
T1 = 0.50025;
T2 = 2.0;
LOOP = 1;
II = 1;
for (JJ = 1; JJ <= II; JJ++) {
N1 = 0;
N2 = 2 * LOOP;
N3 = 2 * LOOP;
N4 = 2 * LOOP;
N5 = 0;
N6 = 2 * LOOP;
N7 = 2 * LOOP;
N8 = 2 * LOOP;
N9 = 2 * LOOP;
N10 = 0;
N11 = 2 * LOOP;
/* Module 1: Simple identifiers */
X1 = 1.0;
X2 = -1.0;
X3 = -1.0;
X4 = -1.0;
if (N1 != 0) {
for (I = 1; I <= N1; I++) {
X1 = (X1 + X2 + X3 - X4) * T;
X2 = (X1 + X2 - X3 + X4) * T;
X3 = (X1 - X2 + X3 + X4) * T;
X4 = (-X1 + X2 + X3 + X4) * T;
}
}
/* Module 2: Array elements */
E1[1] = 1.0;
E1[2] = -1.0;
E1[3] = -1.0;
E1[4] = -1.0;
if (N2 != 0) {
for (I = 1; I <= N2; I++) {
E1[1] = (E1[1] + E1[2] + E1[3] - E1[4]) * T;
E1[2] = (E1[1] + E1[2] - E1[3] + E1[4]) * T;
E1[3] = (E1[1] - E1[2] + E1[3] + E1[4]) * T;
E1[4] = (-E1[1] + E1[2] + E1[3] + E1[4]) * T;
}
}
/* Module 3: Array as parameter */
if (N3 != 0) {
for (I = 1; I <= N3; I++) {
PA (E1);
}
}
/* Module 4: Conditional jumps */
J = 1;
if (N4 != 0) {
for (I = 1; I <= N4; I++) {
if (J == 1)
goto L51;
J = 3;
goto L52;
L51:
J = 2;
L52:
if (J > 2)
goto L53;
J = 1;
goto L54;
L53:
J = 0;
L54:
if (J < 1)
goto L55;
J = 0;
goto L60;
L55:
J = 1;
L60: ;
}
}
/* Module 5: Integer arithmetic */
J = 1;
K = 2;
L = 3;
if (N6 != 0) { /* ??? Where is N5? */
for (I = 1; I <= N6; I++) {
J = J * (K - J) * (L - K);
K = L * K - (L - J) * K;
L = (L - K) * (K + J);
E1[L - 1] = J + K + L;
E1[K - 1] = J * K * L;
}
}
/* Module 6: Trigonometric functions */
X = 0.5;
Y = 0.5;
if (N7 != 0) {
for (I = 1; I <= N7; I++) {
X = T * atanf (T2 * sinf (X) * cosf (X) /
(cosf (X + Y) + cosf (X - Y) - 1.0f));
Y = T * atanf (T2 * sinf (Y) * cosf (Y) /
(cosf (X + Y) + cosf (X - Y) - 1.0f));
}
}
dummy2 (X, Y);
/* Module 7: Procedure calls */
X = 1.0;
Y = 1.0;
Z = 1.0;
if (N8 != 0) {
for (I = 1; I <= N8; I++) {
P3 (&X, &Y, &Z);
}
}
/* Module 8: Array references */
J = 1;
K = 2;
L = 3;
E1[1] = 1.0;
E1[2] = 2.0;
E1[3] = 3.0;
if (N9 != 0) {
for (I = 1; I <= N9; I++) {
P0 ();
}
}
/* Module 9: Integer arithmetic */
J = 2;
K = 3;
if (N10 != 0) { /* TI skips this strange test. */
for (I = 1; I <= N10; I++) {
J = J + K;
K = J + K;
J = K - J;
K = K - J - J;
}
}
/* Module 10: Standard functions */
X = 0.75;
if (N11 != 0) {
for (I = 1; I <= N11; I++) {
X = sqrtf (expf (logf (X) / T1));
}
}
dummy1 (X);
}
}
// }
}
void PA (float E[5])
{
int J1;
J1 = 0;
L10:
E[1] = (E[1] + E[2] + E[3] - E[4]) * T;
E[2] = (E[1] + E[2] - E[3] + E[4]) * T;
E[3] = (E[1] - E[2] + E[3] + E[4]) * T;
E[4] = (-E[1] + E[2] + E[3] + E[4]) / T2;
J1 = J1 + 1;
if ((J1 - 6) < 0)
goto L10;
return;
}
void P0 ()
{
E1[J] = E1[K];
E1[K] = E1[L];
E1[L] = E1[J];
return;
}
void P3 (float *X, float *Y, float *Z)
{
float Y1;
X1 = *X;
Y1 = *Y;
X1 = T * (X1 + Y1);
Y1 = T * (X1 + Y1);
*Z = (X1 + Y1) / T2;
return;
}

View File

@ -1,24 +0,0 @@
#define A (unsigned long)4
#define STUFF int c = a + b
#warning butts
#include "stuff.h"
void g()
{
int a = 4;
int b = 6;
int c = a + b;
(void)c;
}
int main()
{
short a = A;
char b = 3;
STUFF;
int c2 = a + A;
}

View File

@ -0,0 +1 @@
{"fir.c": [{"operation_name": "<", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 324}, {"operation_name": "++", "type_lhs": "int", "type_rhs": "", "type_result": "int", "count": 324}, {"operation_name": "=", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 37}, {"operation_name": "=", "type_lhs": "volatile float", "type_rhs": "float", "type_result": "float", "count": 360}, {"operation_name": "/", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 360}, {"operation_name": "+", "type_lhs": "float", "type_rhs": "float", "type_result": "float", "count": 324}, {"operation_name": "*", "type_lhs": "float", "type_rhs": "float", "type_result": "float", "count": 324}, {"operation_name": "subscript", "type_lhs": "const float *", "type_rhs": "int", "type_result": "const float", "count": 324}, {"operation_name": "+", "type_lhs": "unsigned int", "type_rhs": "unsigned int", "type_result": "unsigned int", "count": 288}, {"operation_name": "subscript", "type_lhs": "const unsigned int *", "type_rhs": "int", "type_result": "const unsigned int", "count": 612}, {"operation_name": "-", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 288}, {"operation_name": "+", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 612}, {"operation_name": "subscript", "type_lhs": "volatile float *", "type_rhs": "int", "type_result": "volatile float", "count": 36}]}

7
testcases/for_loop.c Normal file
View File

@ -0,0 +1,7 @@
int main()
{
int i;
for (i = 0; i < 0; i++)
{
}
}

View File

@ -0,0 +1 @@
{"for_loop.c": [{"operation_name": "<", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 0}, {"operation_name": "++", "type_lhs": "int", "type_rhs": "", "type_result": "int", "count": 0}, {"operation_name": "=", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 1}]}

View File

@ -1,7 +1,8 @@
#include <stdlib.h> //#include <stdlib.h>
// TODO: stddef.h not found.
int gauss_blur(){ int main(){

View File

@ -0,0 +1 @@
{"gauss_blur.c": [{"operation_name": "=", "type_lhs": "unsigned char *", "type_rhs": "unsigned char *", "type_result": "unsigned char *", "count": 4}, {"function_name": "malloc", "call_result_type": "void *", "count": 6}, {"operation_name": "*", "type_lhs": "unsigned long", "type_rhs": "unsigned long", "type_result": "unsigned long", "count": 6}, {"operation_name": "*", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 36868}, {"operation_name": "=", "type_lhs": "unsigned char **", "type_rhs": "unsigned char **", "type_result": "unsigned char **", "count": 2}, {"operation_name": "=", "type_lhs": "unsigned char", "type_rhs": "unsigned char", "type_result": "unsigned char", "count": 5000}, {"operation_name": "subscript", "type_lhs": "unsigned char *", "type_rhs": "int", "type_result": "unsigned char", "count": 41864}, {"operation_name": "==", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 18}, {"operation_name": "<", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 18824}, {"operation_name": "++", "type_lhs": "int", "type_rhs": "", "type_result": "int", "count": 69512}, {"operation_name": "=", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 18820}, {"operation_name": "-", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 50688}, {"operation_name": "<=", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 50688}, {"operation_name": "-", "type_lhs": "int", "type_rhs": "", "type_result": "int", "count": 18432}, {"operation_name": "||", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 36864}, {"operation_name": "!=", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 73728}, {"operation_name": "=", "type_lhs": "char", "type_rhs": "char", "type_result": "char", "count": 36864}, {"function_name": "abs", "call_result_type": "int", "count": 36864}, {"operation_name": "+", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 110592}, {"operation_name": ">", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 8680}, {"function_name": "free", "call_result_type": "void", "count": 4}]}

54
testcases/if_sentences.c Normal file
View File

@ -0,0 +1,54 @@
int main()
{
int a, b, c;
b = 10;
c = 15;
// F
if (c == 10)
{
a = 5;
}
// F
if (c == 10)
{
a = 10;
}
else
{
a = 15;
}
// F T
if (c == 10 || b == 10)
{
a = 10;
}
// T T (not taken)
if (c == 15 || b == 10)
{
a = 10;
}
// T T
if (c == 15 && b == 10)
{
a = 10;
}
// T F
if (c == 15 && b == 15)
{
a = 10;
}
// F T
if (c == 10 && b == 15)
{
a = 10;
}
}

View File

@ -17,12 +17,12 @@ int main(void)
{ {
int m, n, p; int m, n, p;
volatile UInt16 m3[3][5]; volatile UInt16 m3[3][5];
for(m = 0; m < 3; m++) for(m = 0; m < 3; m++) // 1 + 3 * 2 // 3 * 3
{ {
for(p = 0; p < 5; p++) for(p = 0; p < 5; p++) // 3 + 15 * 2 // 15 * 3
{ {
m3[m][p] = 0; m3[m][p] = 0; // 15: BasicOperation(operation_name='=', type_lhs='unsigned short', type_rhs='unsigned short', type_result='unsigned short')
for(n = 0; n < 4; n++) for(n = 0; n < 4; n++) // 15 + 60 * 2 // 60 * 3
{ {
m3[m][p] += m1[m][n] * m2[n][p]; m3[m][p] += m1[m][n] * m2[n][p];
} }

View File

@ -0,0 +1 @@
{"matrix.c": [{"operation_name": "<", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 78}, {"operation_name": "++", "type_lhs": "int", "type_rhs": "", "type_result": "int", "count": 78}, {"operation_name": "=", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 19}, {"operation_name": "=", "type_lhs": "unsigned short", "type_rhs": "unsigned short", "type_result": "unsigned short", "count": 15}, {"operation_name": "subscript", "type_lhs": "volatile UInt16 *", "type_rhs": "int", "type_result": "unsigned short", "count": 75}, {"operation_name": "subscript", "type_lhs": "volatile UInt16 (*)[5]", "type_rhs": "int", "type_result": "volatile UInt16 [5]", "count": 75}, {"operation_name": "*", "type_lhs": "int", "type_rhs": "int", "type_result": "int", "count": 60}, {"operation_name": "subscript", "type_lhs": "const UInt16 *", "type_rhs": "int", "type_result": "unsigned short", "count": 120}, {"operation_name": "subscript", "type_lhs": "const UInt16 (*)[4]", "type_rhs": "int", "type_result": "const UInt16 [4]", "count": 60}, {"operation_name": "subscript", "type_lhs": "const UInt16 (*)[5]", "type_rhs": "int", "type_result": "const UInt16 [5]", "count": 60}]}

View File

@ -1,17 +0,0 @@
//
// Created by erki on 23.02.21.
//
#ifndef C_ANALYZER_STUFF_H
#define C_ANALYZER_STUFF_H
void f()
{
int a = 4;
int b = 6;
int c = a + b;
(void)c;
}
#endif //C_ANALYZER_STUFF_H