Compare commits
33 Commits
testing/re
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12afc6633e | ||
| 7925b87f09 | |||
| 254eb87889 | |||
| 905dcb160c | |||
| 91fbb3bbbd | |||
| 1c3107e8cc | |||
| df8e5c5d09 | |||
| cdf6b850f9 | |||
| 6ed38077c6 | |||
| a88a57e2ec | |||
| 021a270e08 | |||
| eb640e513a | |||
| 2ba019373e | |||
| 6e27e99735 | |||
| 326decd3de | |||
| 7b294045bf | |||
| 53c24c6d5e | |||
| 83ca2deb0e | |||
| a23e5c66ab | |||
| 7a852f8f94 | |||
| 34eba970cc | |||
| 0509d250ab | |||
| a0b9775f13 | |||
| 5a2d5ed933 | |||
| be4a71325d | |||
| 16f4c85f6d | |||
| aff90d5a9d | |||
| 0e7cf93c9b | |||
| b609c48bfe | |||
| cff605642f | |||
| 375e3f9af6 | |||
| 6f3dce5972 | |||
| 2df47d4bc5 |
66
.clang-format
Normal file
66
.clang-format
Normal 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
36
.drone.yml
Normal 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
144
.gitignore
vendored
@ -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
8
.idea/.gitignore
generated
vendored
Normal 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
1
.idea/.name
generated
Normal file
@ -0,0 +1 @@
|
|||||||
|
C Analyzer
|
||||||
2
.idea/analyzer.git.iml
generated
Normal file
2
.idea/analyzer.git.iml
generated
Normal 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
59
.idea/codeStyles/Project.xml
generated
Normal 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
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal 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
4
.idea/misc.xml
generated
Normal 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
8
.idea/modules.xml
generated
Normal 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
6
.idea/vcs.xml
generated
Normal 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>
|
||||||
@ -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
16
Dockerfile
Normal 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
|
||||||
2
LICENSE
2
LICENSE
@ -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
124
README.md
@ -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
6
conanfile.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[requires]
|
||||||
|
catch2/2.13.4
|
||||||
|
nlohmann_json/3.9.1
|
||||||
|
|
||||||
|
[generators]
|
||||||
|
cmake_find_package
|
||||||
49
op-finder-lib/CMakeLists.txt
Normal file
49
op-finder-lib/CMakeLists.txt
Normal 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
|
||||||
|
)
|
||||||
25
op-finder-lib/include/OperationAstMatcher.hpp
Normal file
25
op-finder-lib/include/OperationAstMatcher.hpp
Normal 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
|
||||||
35
op-finder-lib/include/OperationFinder.hpp
Normal file
35
op-finder-lib/include/OperationFinder.hpp
Normal 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
|
||||||
24
op-finder-lib/include/OperationFinderAstAction.hpp
Normal file
24
op-finder-lib/include/OperationFinderAstAction.hpp
Normal 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
|
||||||
27
op-finder-lib/include/OperationFinderAstConsumer.hpp
Normal file
27
op-finder-lib/include/OperationFinderAstConsumer.hpp
Normal 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
|
||||||
48
op-finder-lib/include/OperationFinderAstVisitor.hpp
Normal file
48
op-finder-lib/include/OperationFinderAstVisitor.hpp
Normal 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
|
||||||
114
op-finder-lib/include/OperationLog.hpp
Normal file
114
op-finder-lib/include/OperationLog.hpp
Normal 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
|
||||||
36
op-finder-lib/include/OperationStorage.hpp
Normal file
36
op-finder-lib/include/OperationStorage.hpp
Normal 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
|
||||||
52
op-finder-lib/src/ASTHelpers.hpp
Normal file
52
op-finder-lib/src/ASTHelpers.hpp
Normal 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
|
||||||
57
op-finder-lib/src/OperationAstMatcher.cpp
Normal file
57
op-finder-lib/src/OperationAstMatcher.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
170
op-finder-lib/src/OperationFinder.cpp
Normal file
170
op-finder-lib/src/OperationFinder.cpp
Normal 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) };
|
||||||
|
}
|
||||||
17
op-finder-lib/src/OperationFinderAstAction.cpp
Normal file
17
op-finder-lib/src/OperationFinderAstAction.cpp
Normal 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));
|
||||||
|
}
|
||||||
19
op-finder-lib/src/OperationFinderAstConsumer.cpp
Normal file
19
op-finder-lib/src/OperationFinderAstConsumer.cpp
Normal 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);
|
||||||
|
}
|
||||||
154
op-finder-lib/src/OperationFinderAstVisitor.cpp
Normal file
154
op-finder-lib/src/OperationFinderAstVisitor.cpp
Normal 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;
|
||||||
|
}
|
||||||
44
op-finder-lib/src/OperationLog.cpp
Normal file
44
op-finder-lib/src/OperationLog.cpp
Normal 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;
|
||||||
|
}
|
||||||
59
op-finder-lib/src/OperationStorage.cpp
Normal file
59
op-finder-lib/src/OperationStorage.cpp
Normal 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();
|
||||||
|
}
|
||||||
31
op-finder-tests/CMakeLists.txt
Normal file
31
op-finder-tests/CMakeLists.txt
Normal 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)
|
||||||
95
op-finder-tests/basic_operations.cpp
Normal file
95
op-finder-tests/basic_operations.cpp
Normal 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");
|
||||||
|
}
|
||||||
125
op-finder-tests/branches.cpp
Normal file
125
op-finder-tests/branches.cpp
Normal 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);
|
||||||
|
}
|
||||||
37
op-finder-tests/fixtures/RunOnCodeFixture.cpp
Normal file
37
op-finder-tests/fixtures/RunOnCodeFixture.cpp
Normal 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);
|
||||||
|
}
|
||||||
27
op-finder-tests/fixtures/RunOnCodeFixture.hpp
Normal file
27
op-finder-tests/fixtures/RunOnCodeFixture.hpp
Normal 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
|
||||||
55
op-finder-tests/function_calls.cpp
Normal file
55
op-finder-tests/function_calls.cpp
Normal 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
6
op-finder-tests/main.cpp
Normal 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>
|
||||||
@ -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
|
|
||||||
)
|
|
||||||
|
|||||||
@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
@ -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);
|
||||||
}
|
}
|
||||||
53
op-summarizer/data/gcov.json
Normal file
53
op-summarizer/data/gcov.json
Normal file
File diff suppressed because one or more lines are too long
213
op-summarizer/data/opfinder.json
Normal file
213
op-summarizer/data/opfinder.json
Normal 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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
39
op-summarizer/gcovreader.py
Normal file
39
op-summarizer/gcovreader.py
Normal 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
|
||||||
73
op-summarizer/opfinderreader.py
Normal file
73
op-summarizer/opfinderreader.py
Normal 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
|
||||||
140
op-summarizer/opsummarizer.py
Normal file
140
op-summarizer/opsummarizer.py
Normal 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
76
op-summarizer/tests.py
Normal 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
235
testcases/Dhrystone.c
Normal 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
241
testcases/Whetstone.c
Normal 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;
|
||||||
|
}
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
1
testcases/fir_expected.json
Normal file
1
testcases/fir_expected.json
Normal 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
7
testcases/for_loop.c
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
int main()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 0; i++)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
1
testcases/for_loop_expected.json
Normal file
1
testcases/for_loop_expected.json
Normal 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}]}
|
||||||
@ -1,7 +1,8 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
//#include <stdlib.h>
|
||||||
|
// TODO: stddef.h not found.
|
||||||
|
|
||||||
int gauss_blur(){
|
int main(){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
1
testcases/gauss_blur_expected.json
Normal file
1
testcases/gauss_blur_expected.json
Normal 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
54
testcases/if_sentences.c
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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];
|
||||||
}
|
}
|
||||||
|
|||||||
1
testcases/matrix_expected.json
Normal file
1
testcases/matrix_expected.json
Normal 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}]}
|
||||||
@ -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
|
|
||||||
Loading…
x
Reference in New Issue
Block a user