From 7849afc4dd03f9bcc7186b14071762b3ee9cbc1e Mon Sep 17 00:00:00 2001 From: Erki Date: Sun, 21 Feb 2021 17:42:42 +0200 Subject: [PATCH] First working version. --- .gitignore | 36 ++++++ CMakeLists.txt | 8 ++ op-finder/CMakeLists.txt | 37 ++++++ op-finder/OperationFinder.cpp | 206 ++++++++++++++++++++++++++++++++++ op-finder/OperationFinder.hpp | 26 +++++ op-finder/main.cpp | 46 ++++++++ 6 files changed, 359 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 op-finder/CMakeLists.txt create mode 100644 op-finder/OperationFinder.cpp create mode 100644 op-finder/OperationFinder.hpp create mode 100644 op-finder/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..32e3af5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# Build folders +[Bb]uild/ +cmake-*/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..3abab79 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.17) +project("C Analyzer") + +set(CMAKE_CXX_STANDARD 17) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter") + +add_subdirectory(op-finder) diff --git a/op-finder/CMakeLists.txt b/op-finder/CMakeLists.txt new file mode 100644 index 0000000..84c37ce --- /dev/null +++ b/op-finder/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.17) + +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_executable(llvm_proto main.cpp OperationFinder.cpp) + +target_include_directories(llvm_proto + PRIVATE ${LLVM_INCLUDE_DIRS} +) + +target_compile_definitions(llvm_proto + PRIVATE + ${LLVM_DEFINITIONS} +) + +target_link_libraries(llvm_proto + PRIVATE + ${llvm_libs} + clangTooling + clangBasic + clangASTMatchers +) diff --git a/op-finder/OperationFinder.cpp b/op-finder/OperationFinder.cpp new file mode 100644 index 0000000..1c04481 --- /dev/null +++ b/op-finder/OperationFinder.cpp @@ -0,0 +1,206 @@ +// +// Created by erki on 16.02.21. +// + +#include "OperationFinder.hpp" + +#include +#include + +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 +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("assignment")) + { + _processAssignment(result); + } + else if (result.Nodes.getNodeAs("arithmetic")) + { + _processArithmetic(result); + } + else if (result.Nodes.getNodeAs("compound_stmt")) + { + _processCompoundStmt(result); + } + else if (result.Nodes.getNodeAs("unary_arithmetic")) + { + _processUnaryArithmetic(result); + } +} + +void OperationFinder::_processAssignment(const clang::ast_matchers::MatchFinder::MatchResult& result) +{ + const BinaryOperator* op = result.Nodes.getNodeAs("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("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("compound_stmt"); + + assert(stmt); + + const CompoundAssignOperator* op = dyn_cast(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("unary_arithmetic"); + const Expr* lhs = result.Nodes.getNodeAs("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(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()); + } +} diff --git a/op-finder/OperationFinder.hpp b/op-finder/OperationFinder.hpp new file mode 100644 index 0000000..4242c00 --- /dev/null +++ b/op-finder/OperationFinder.hpp @@ -0,0 +1,26 @@ +// +// Created by erki on 16.02.21. +// + +#ifndef LLVM_PROTO_OPERATIONFINDER_HPP +#define LLVM_PROTO_OPERATIONFINDER_HPP + +#include +#include + +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 diff --git a/op-finder/main.cpp b/op-finder/main.cpp new file mode 100644 index 0000000..8aa6d3c --- /dev/null +++ b/op-finder/main.cpp @@ -0,0 +1,46 @@ +// Declares clang::SyntaxOnlyAction. +#include "clang/Frontend/FrontendActions.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Tooling.h" +// Declares llvm::cl::extrahelp. +#include "llvm/Support/CommandLine.h" + +#include "OperationFinder.hpp" + +using namespace clang::tooling; +using namespace clang::ast_matchers; +using namespace llvm; + +// Apply a custom category to all command-line options so that they are the +// only ones displayed. +static llvm::cl::OptionCategory MyToolCategory("opcounter options"); + +// CommonOptionsParser declares HelpMessage with a description of the common +// command-line options related to the compilation database and input files. +// It's nice to have this help message in all tools. +static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); + +// A help message for this specific tool can be added afterwards. +static cl::extrahelp MoreHelp("\nMore help text...\n"); + +int main(int argc, const char** argv) +{ + auto ExpectedParser = CommonOptionsParser::create(argc, argv, MyToolCategory); + if (!ExpectedParser) + { + // Fail gracefully for unsupported options. + llvm::errs() << ExpectedParser.takeError(); + return 1; + } + + CommonOptionsParser& OptionsParser = ExpectedParser.get(); + ClangTool Tool(OptionsParser.getCompilations(), + OptionsParser.getSourcePathList()); + + OperationFinder op_finder; + MatchFinder finder; + + op_finder.addMatcher(finder); + + return Tool.run(newFrontendActionFactory(&finder).get()); +} \ No newline at end of file