From 8797851e87b0c3c25247c18dd167cbbea02581cd Mon Sep 17 00:00:00 2001 From: Kuznetsov-Artyom <110616662+Kuznetsov-Artyom@users.noreply.github.com> Date: Fri, 14 Feb 2025 09:49:13 +0300 Subject: [PATCH 01/50] Update readme (#1) 1) Move resources 2) Add status build --- README.md | 53 +++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 616afe0927d2e..ebd30a67f8fab 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,32 @@ # Compiler course 2025 -[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/llvm/llvm-project/badge)](https://securityscorecards.dev/viewer/?uri=github.com/llvm/llvm-project) -[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8273/badge)](https://www.bestpractices.dev/projects/8273) -[![libc++](https://github.com/llvm/llvm-project/actions/workflows/libcxx-build-and-test.yaml/badge.svg?branch=main&event=schedule)](https://github.com/llvm/llvm-project/actions/workflows/libcxx-build-and-test.yaml?query=event%3Aschedule) +[![Build LLVM](https://github.com/NN-complr-tech/compiler-course-2025/actions/workflows/compiler-course-build.yml/badge.svg)](https://github.com/NN-complr-tech/compiler-course-2025/actions/workflows/compiler-course-build.yml) + +# Resources +- [Telegram сhat][chat] +- [Telegram сhannel][channel] +- [Tasks and results][results] +- Materials + - [Lecture recordings][recording] + - [Lecture presentations][lecture] + - [LLVM][llvm] + - [MLIR][mlir] + - [Clang][clang] + - [Official YouTube channel LLVM][youtube_llvm] + + + +[results]: https://docs.google.com/spreadsheets/d/1LiZ5FMd5t61yoGdnpANTFpzqtKD_ejtvLl1cHKZxvXQ/edit?usp=sharing + +[channel]: https://t.me/+TPntKPD8z0E3OWJi +[chat]: https://t.me/+JG3n1jeSAiIxZjMy + +[recording]: https://disk.yandex.ru/d/52gu5vJTSt1VFg +[lecture]: https://github.com/NN-complr-tech/Complr-course-lectures +[llvm]: https://llvm.org/ +[mlir]: https://mlir.llvm.org/ +[clang]: https://clang.llvm.org/ +[youtube_llvm]: https://www.youtube.com/@LLVMPROJ # What is LLVM? LLLVM is a set of compiler and toolchain technologies that can be used to develop a frontend for any programming language and a backend for any instruction set architecture. LLVM is designed around a language-independent intermediate representation (IR) that serves as a portable, high-level assembly language that can be optimized with a variety of transformations over multiple passes. The name LLVM originally stood for Low Level Virtual Machine, though the project has expanded and the name is no longer officially an initialism. @@ -105,26 +129,3 @@ For one test ```bash ./build/bin/llvm-lit -v /path/to/test_file ``` -# 6. Resources -- [Telegram сhat][chat] -- [Telegram сhannel][channel] -- [Tasks and results][results] -- Materials - - [Lectures][lecture] - - [LLVM][llvm] - - [MLIR][mlir] - - [Clang][clang] - - [Official YouTube channel LLVM][youtube_llvm] - - - -[results]: https://docs.google.com/spreadsheets/d/1LiZ5FMd5t61yoGdnpANTFpzqtKD_ejtvLl1cHKZxvXQ/edit?usp=sharing - -[channel]: https://t.me/+TPntKPD8z0E3OWJi -[chat]: https://t.me/+JG3n1jeSAiIxZjMy - -[lecture]: https://github.com/NN-complr-tech/Complr-course-lectures -[llvm]: https://llvm.org/ -[mlir]: https://mlir.llvm.org/ -[clang]: https://clang.llvm.org/ -[youtube_llvm]: https://www.youtube.com/@LLVMPROJ From 080632ec8593660318176ef43efbb3303b24d022 Mon Sep 17 00:00:00 2001 From: Kuznetsov-Artyom <110616662+Kuznetsov-Artyom@users.noreply.github.com> Date: Thu, 20 Feb 2025 20:31:48 +0300 Subject: [PATCH 02/50] Update README.md (#2) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ebd30a67f8fab..9014f555a0efb 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Recommended OS - Linux (WSL). 1. Create fork this repository 2. Clone local fork ```bash -git clone https://github.com//llvm.git +git clone https://github.com//compiler-course-2025.git cd llvm/ git checkout -b ``` From 16003525da0bcaf5780fd0914c0e7a966662e337 Mon Sep 17 00:00:00 2001 From: Kuznetsov-Artyom <110616662+Kuznetsov-Artyom@users.noreply.github.com> Date: Fri, 21 Feb 2025 10:18:43 +0300 Subject: [PATCH 03/50] Update readme and add label `docs` (#3) --- .github/labeler.yml | 3 +++ README.md | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 4201fd9ae4b57..ba181109ee6ca 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -12,5 +12,8 @@ tests: - "llvm/test/compiler-course/**" - "mlir/test/compiler-course/**" +docs: + - "README.md" + ci: - ".github/**" diff --git a/README.md b/README.md index 9014f555a0efb..bcb15276d6bba 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ - [LLVM][llvm] - [MLIR][mlir] - [Clang][clang] + - [FileCheck][filecheck] - [Official YouTube channel LLVM][youtube_llvm] @@ -26,6 +27,7 @@ [llvm]: https://llvm.org/ [mlir]: https://mlir.llvm.org/ [clang]: https://clang.llvm.org/ +[filecheck]: https://llvm.org/docs/CommandGuide/FileCheck.html [youtube_llvm]: https://www.youtube.com/@LLVMPROJ # What is LLVM? @@ -52,7 +54,7 @@ Recommended OS - Linux (WSL). 2. Clone local fork ```bash git clone https://github.com//compiler-course-2025.git -cd llvm/ +cd compiler-course-2025/ git checkout -b ``` From 58509a0178caa79f319e093e0ef1933d7b368d62 Mon Sep 17 00:00:00 2001 From: Kuznetsov-Artyom <110616662+Kuznetsov-Artyom@users.noreply.github.com> Date: Thu, 27 Feb 2025 20:35:05 +0300 Subject: [PATCH 04/50] Add deadline lab1 (#4) --- .github/deadline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/deadline.py b/.github/deadline.py index 0b3b8a44dbfe9..485dc8a102deb 100644 --- a/.github/deadline.py +++ b/.github/deadline.py @@ -9,7 +9,7 @@ def main(): moscow_tz = ZoneInfo("Europe/Moscow") current_date = datetime.now(moscow_tz) deadline_date = { - "lab:clang": datetime(2025, 6, 1, hour=19, tzinfo=moscow_tz), + "lab:clang": datetime(2025, 3, 19, hour=19, tzinfo=moscow_tz), "lab:llvm ir": datetime(2025, 6, 1, hour=19, tzinfo=moscow_tz), "lab:backend": datetime(2025, 6, 1, hour=19, tzinfo=moscow_tz), "lab:mlir": datetime(2025, 6, 1, hour=19, tzinfo=moscow_tz), From 6eb53f432ab7dc8da2c3ab9e510af75cc02ce06e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BB=D0=B0=D0=B4=D0=B8=D0=BC=D0=B8=D1=80=20=D0=92?= =?UTF-8?q?=D0=BB=D0=B0=D0=B4=D0=B8=D0=BC=D0=B8=D1=80=D0=BE=D0=B2=D0=B8?= =?UTF-8?q?=D1=87=20=D0=9F=D1=83=D1=82=D0=B8=D0=BD!?= <144924225+PutinVVV@users.noreply.github.com> Date: Tue, 4 Mar 2025 20:43:14 +0300 Subject: [PATCH 05/50] =?UTF-8?q?=D0=A8=D1=83=D1=80=D1=8B=D0=B3=D0=B8?= =?UTF-8?q?=D0=BD=20=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9.=20=20=D0=97?= =?UTF-8?q?=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5=20=E2=84=961.=20=D0=92?= =?UTF-8?q?=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=20=E2=84=964.=20(#5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Вариант 4. Добавить к статическим, локальным,глобальным объектам и параметрам функции соответствующие префиксы (static_, local_, global_, param_). > Код автоматически переименовывает переменные, добавляя префиксы, и выводит изменённый исходный код. **ExampleVisitor**: Просматривает объявления переменных (**VarDecl**), параметров (**ParmVarDecl**) и выражений ссылок на объявления (**DeclRefExpr**), добавляя к именам префиксы в зависимости от типа переменной. --------- Co-authored-by: Sergey --- .../CMakeLists.txt | 18 +++ .../ShuriginAST.cpp | 130 ++++++++++++++++++ .../ShuriginS_AST.cpp | 31 +++++ 3 files changed, 179 insertions(+) create mode 100644 clang/compiler-course/Shurigin_S_FI1_ClangAst_var4/CMakeLists.txt create mode 100644 clang/compiler-course/Shurigin_S_FI1_ClangAst_var4/ShuriginAST.cpp create mode 100644 clang/test/compiler-course/Shurigin_S_FI1_ClangAst_var4_test/ShuriginS_AST.cpp diff --git a/clang/compiler-course/Shurigin_S_FI1_ClangAst_var4/CMakeLists.txt b/clang/compiler-course/Shurigin_S_FI1_ClangAst_var4/CMakeLists.txt new file mode 100644 index 0000000000000..9ec74cffd5d94 --- /dev/null +++ b/clang/compiler-course/Shurigin_S_FI1_ClangAst_var4/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "ClangAST_1") +set(Student "ShuriginS") +set(Group "FIIT1") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/Shurigin_S_FI1_ClangAst_var4/ShuriginAST.cpp b/clang/compiler-course/Shurigin_S_FI1_ClangAst_var4/ShuriginAST.cpp new file mode 100644 index 0000000000000..36f4cd84e482c --- /dev/null +++ b/clang/compiler-course/Shurigin_S_FI1_ClangAst_var4/ShuriginAST.cpp @@ -0,0 +1,130 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/raw_ostream.h" +#include + +namespace { + +class ExampleVisitor final : public clang::RecursiveASTVisitor { +public: + explicit ExampleVisitor(clang::ASTContext *Context, clang::Rewriter &Rewriter) + : MRewriter(Rewriter) {} + + bool VisitVarDecl(clang::VarDecl *Var) { + if (Var->getName().empty()) { + return true; + } + + std::string Prefix; + if (Var->isStaticLocal()) { + Prefix = "static_"; + } else if (Var->isLocalVarDecl()) { + Prefix = "local_"; + } else if (Var->hasGlobalStorage()) { + Prefix = "global_"; + } + + if (!Prefix.empty()) { + std::string OldName = Var->getName().str(); + if (OldName.find(Prefix) != 0) { + std::string NewName = Prefix + OldName; + MRenamedVars[OldName] = NewName; + clang::SourceLocation Loc = Var->getLocation(); + MRewriter.ReplaceText(Loc, OldName.length(), NewName); + } + } + return true; + } + + bool VisitParmVarDecl(clang::ParmVarDecl *Param) { + if (Param->getName().empty()) { + return true; + } + + std::string OldName = Param->getName().str(); + std::string NewName = "param_" + OldName; + MRenamedVars[OldName] = NewName; + MRewriter.ReplaceText(Param->getLocation(), OldName.size(), NewName); + return true; + } + + bool VisitDeclRefExpr(clang::DeclRefExpr *Expr) { + clang::ValueDecl *Decl = Expr->getDecl(); + if (Decl->getName().empty()) { + return true; + } + + std::string OldName = Decl->getName().str(); + + if (llvm::isa(Decl)) { + return true; + } + + if (auto VD = llvm::dyn_cast(Decl)) { + VD = VD->getCanonicalDecl(); + + if (VD->hasExternalStorage()) { + std::string NewName = "global_" + OldName; + MRenamedVars[OldName] = NewName; + MRewriter.ReplaceText(Expr->getLocation(), OldName.length(), NewName); + return true; + } + } + + auto It = MRenamedVars.find(OldName); + if (It != MRenamedVars.end()) { + clang::SourceLocation Loc = Expr->getLocation(); + MRewriter.ReplaceText(Loc, OldName.length(), It->second); + } + return true; + } + +private: + clang::Rewriter &MRewriter; + std::unordered_map MRenamedVars; +}; + +class ExampleConsumer final : public clang::ASTConsumer { +public: + explicit ExampleConsumer(clang::ASTContext *Context, + clang::Rewriter &Rewriter) + : MRewriter(Rewriter), MVisitor(Context, Rewriter) {} + + void HandleTranslationUnit(clang::ASTContext &Context) override { + MVisitor.TraverseDecl(Context.getTranslationUnitDecl()); + } + +private: + clang::Rewriter &MRewriter; + ExampleVisitor MVisitor; +}; + +class ExampleAction final : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &Ci, llvm::StringRef) override { + MRewriter.setSourceMgr(Ci.getSourceManager(), Ci.getLangOpts()); + return std::make_unique(&Ci.getASTContext(), MRewriter); + } + + bool ParseArgs(const clang::CompilerInstance &Ci, + const std::vector &Args) override { + return true; + } + + void EndSourceFileAction() override { + MRewriter.getEditBuffer(MRewriter.getSourceMgr().getMainFileID()) + .write(llvm::outs()); + } + +private: + clang::Rewriter MRewriter; +}; + +} // namespace + +static clang::FrontendPluginRegistry::Add + X("ClangAST_1_ShuriginS_FIIT1_ClangAST", "Adds prefixes to variables"); diff --git a/clang/test/compiler-course/Shurigin_S_FI1_ClangAst_var4_test/ShuriginS_AST.cpp b/clang/test/compiler-course/Shurigin_S_FI1_ClangAst_var4_test/ShuriginS_AST.cpp new file mode 100644 index 0000000000000..5e3e15c9ae007 --- /dev/null +++ b/clang/test/compiler-course/Shurigin_S_FI1_ClangAst_var4_test/ShuriginS_AST.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/ClangAST_1_ShuriginS_FIIT1_ClangAST%pluginext -plugin ClangAST_1_ShuriginS_FIIT1_ClangAST -fsyntax-only %s 2>&1 | FileCheck --match-full-lines %s + +// CHECK: int static global_Var1 = 0; +// CHECK-NEXT: extern int global_Var3; +// CHECK-NEXT: int rA(int param_A) { +// CHECK-NEXT: return -param_A; +// CHECK-NEXT: } +// CHECK-NEXT: int static foo(int param_A, int param_B) { +// CHECK-NEXT: static int static_Var2 = 0; +// CHECK-NEXT: int local_Var3 = 123; +// CHECK-NEXT: int &local_rA = param_A; +// CHECK-NEXT: ++static_Var2; +// CHECK-NEXT: return local_rA + param_B + global_Var1 + static_Var2 + local_Var3; +// CHECK-NEXT: } +// CHECK-NEXT: int static global_X = foo(1, rA(global_Var3)); + +int static Var1 = 0; +extern int Var3; +int rA(int A) { + return -A; +} +int static foo(int A, int B) { + static int Var2 = 0; + int Var3 = 123; + int &rA = A; + ++Var2; + return rA + B + Var1 + Var2 + Var3; +} +int static X = foo(1, rA(Var3)); + + From 435ec1febc57dec986b6b4d4ee72bd527852d31e Mon Sep 17 00:00:00 2001 From: ascannel <113050263+ascannel@users.noreply.github.com> Date: Wed, 5 Mar 2025 22:35:47 +0300 Subject: [PATCH 06/50] Lopatin Ilya. Lab 1: Clang AST. Option 1. (#6) This Clang plugin analyzes C++ user-defined types (classes/structs) using LLVM/Clang's AST traversal capabilities. ### Functionality: - Extracts class inheritance chains - Lists fields with types/access modifiers - Identifies methods with signatures, qualifiers (const/noexcept), and special properties (virtual/override/pure) ### LLVM/Clang APIs Used: - `RecursiveASTVisitor` for deep AST traversal - `CXXRecordDecl` for class declarations - `CXXBaseSpecifier` for inheritance info - `FieldDecl`/`CXXMethodDecl` for member analysis ### Workflow: - Registers as FrontendPlugin - Traverses Translation Unit AST - Filters implicit/compiler-generated elements - Outputs structured type info via LLVM's raw_ostream --- .../lopatin_i_user_data_type/CMakeLists.txt | 18 +++ .../lopatin_i_user_data_type_plugin.cpp | 115 ++++++++++++++++++ .../lopatin_i_user_data_type_test.cpp | 62 ++++++++++ 3 files changed, 195 insertions(+) create mode 100644 clang/compiler-course/lopatin_i_user_data_type/CMakeLists.txt create mode 100644 clang/compiler-course/lopatin_i_user_data_type/lopatin_i_user_data_type_plugin.cpp create mode 100644 clang/test/compiler-course/lopatin_i_user_data_type/lopatin_i_user_data_type_test.cpp diff --git a/clang/compiler-course/lopatin_i_user_data_type/CMakeLists.txt b/clang/compiler-course/lopatin_i_user_data_type/CMakeLists.txt new file mode 100644 index 0000000000000..d65f2670f4d5f --- /dev/null +++ b/clang/compiler-course/lopatin_i_user_data_type/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "UserDataTypePlugin") +set(Student "LopatinIlya") +set(Group "FIIT3") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/lopatin_i_user_data_type/lopatin_i_user_data_type_plugin.cpp b/clang/compiler-course/lopatin_i_user_data_type/lopatin_i_user_data_type_plugin.cpp new file mode 100644 index 0000000000000..067261f8e6e4e --- /dev/null +++ b/clang/compiler-course/lopatin_i_user_data_type/lopatin_i_user_data_type_plugin.cpp @@ -0,0 +1,115 @@ +#include +#include + +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "llvm/Support/raw_ostream.h" + +namespace { + +std::string getAccessSpelling(clang::AccessSpecifier access) { + switch (access) { + case clang::AS_public: return "public"; + case clang::AS_protected: return "protected"; + case clang::AS_private: return "private"; + default: return ""; + } +} + +class UserDataTypeVisitor final : public clang::RecursiveASTVisitor { +public: + explicit UserDataTypeVisitor(clang::ASTContext *context) {} + bool VisitCXXRecordDecl(clang::CXXRecordDecl *D) { + if (!D->isThisDeclarationADefinition() || D->isImplicit()) + return true; + + auto& os = llvm::outs(); + os << D->getName(); + + // classes + if (D->getNumBases()) { + os << " -> "; + bool first = true; + for (const clang::CXXBaseSpecifier &B : D->bases()) { + if (!first) os << ", "; + first = false; + os << B.getType()->getAsCXXRecordDecl()->getName(); + } + } + os << "\n"; + + // fields + os << "|_Fields\n"; + bool hasFields = false; + for (clang::FieldDecl *F : D->fields()) { + hasFields = true; + os << "| |_ " << F->getName() << " (" + << F->getType().getAsString() << "|" + << ::getAccessSpelling(F->getAccess()) << ")\n"; + } + if (!hasFields) os << "| |_ (no fields)\n"; + + // methods + if (D->method_begin() != D->method_end()) { + os << "|_Methods\n"; + for (clang::CXXMethodDecl *M : D->methods()) { + if (M->isImplicit()) continue; + + std::stringstream TypeStr; + TypeStr << M->getReturnType().getAsString() << "("; + for (unsigned i = 0; i < M->getNumParams(); ++i) { + if (i > 0) TypeStr << ", "; + TypeStr << M->getParamDecl(i)->getType().getAsString(); + } + TypeStr << ")"; + + // specs + llvm::SmallVector Specs; + if (M->isVirtual() && !M->hasAttr()) + Specs.push_back("virtual"); + if (M->isPureVirtual()) Specs.push_back("pure"); + if (M->hasAttr()) Specs.push_back("override"); + if (M->isConst()) Specs.push_back("const"); + + os << "| |_ " << M->getNameAsString() << " (" << TypeStr.str() << "|" + << ::getAccessSpelling(M->getAccess()); + for (const auto &S : Specs) + os << "|" << S; + os << ")\n"; + } + } + return true; + } +}; + +class UserDataTypeConsumer final : public clang::ASTConsumer { +public: + explicit UserDataTypeConsumer(clang::ASTContext *context) : m_visitor(context) {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + m_visitor.TraverseDecl(context.getTranslationUnitDecl()); + } + +private: + UserDataTypeVisitor m_visitor; +}; + +class UserDataTypeAction final : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + return std::make_unique(&ci.getASTContext()); + } + + bool ParseArgs(const clang::CompilerInstance &ci, + const std::vector &args) override { + return true; + } +}; + +} // namespace + +static clang::FrontendPluginRegistry::Add + X("UserDataTypePlugin_LopatinIlya_FIIT3_ClangAST", "Prints info about user defined data types"); diff --git a/clang/test/compiler-course/lopatin_i_user_data_type/lopatin_i_user_data_type_test.cpp b/clang/test/compiler-course/lopatin_i_user_data_type/lopatin_i_user_data_type_test.cpp new file mode 100644 index 0000000000000..409f28d4ad729 --- /dev/null +++ b/clang/test/compiler-course/lopatin_i_user_data_type/lopatin_i_user_data_type_test.cpp @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/UserDataTypePlugin_LopatinIlya_FIIT3_ClangAST%pluginext -plugin UserDataTypePlugin_LopatinIlya_FIIT3_ClangAST %s -fsyntax-only 2>&1 | FileCheck %s + +// CHECK: Base1 +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ (no fields) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ virtualMethod (void()|public|virtual|pure) + +// CHECK: Base2 +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ (no fields) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ baseMethod (void()|public|virtual) + +// CHECK: Derived -> Base1, Base2 +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ publicField (int|public) +// CHECK-NEXT: | |_ protectedField (float|protected) +// CHECK-NEXT: | |_ privateField (char|private) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ virtualMethod (void()|public|override) +// CHECK-NEXT: | |_ baseMethod (void()|public|override) +// CHECK-NEXT: | |_ constMethod (int()|private|const) +// CHECK-NEXT: | |_ anotherMethod (void(int)|protected) + +// CHECK: Array +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ ptr (T[SZ]|public) + + +struct Base1 { + virtual void virtualMethod() = 0; +}; + +struct Base2 { + virtual void baseMethod() {} +}; + +struct Derived : Base1, Base2 { +public: + int publicField; +protected: + float protectedField; +private: + char privateField; + +public: + void virtualMethod() override {} + void baseMethod() override {} + +private: + int constMethod() const { return 0; } + +protected: + void anotherMethod(int) {} +}; + +template +struct Array { + T ptr[SZ]{}; +}; + From 62d577dfb25de83c29ad4caf5b71586b5d2fa66c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D1=81=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=91=D0=B5=D1=81?= =?UTF-8?q?=D1=85=D0=BC=D0=B5=D0=BB=D1=8C=D0=BD=D0=BE=D0=B2=D0=B0?= <113203195+KseniyaBeskhmelnova@users.noreply.github.com> Date: Sun, 9 Mar 2025 18:02:24 +0300 Subject: [PATCH 07/50] =?UTF-8?q?=D0=91=D0=B5=D1=81=D1=85=D0=BC=D0=B5?= =?UTF-8?q?=D0=BB=D1=8C=D0=BD=D0=BE=D0=B2=D0=B0=20=D0=9A=D1=81=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F.=20=D0=9B=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=BD=D0=B0=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=B0=201:=20Clang=20AST.=20=D0=92=D0=B0=D1=80=D0=B8=D0=B0?= =?UTF-8?q?=D0=BD=D1=82=203.=20(#8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Решение реализует плагин для Clang, который анализирует исходный код на предмет неявных преобразований типов. Он использует RecursiveASTVisitor для обхода абстрактного синтаксического дерева (AST) и отслеживает преобразования типов в выражениях. Каждый случай неявного преобразования фиксируется с указанием исходного и целевого типов, а также их порядка в пределах функции. Результаты выводятся по функциям, где для каждого преобразования указывается количество. Плагин сортирует и выводит информацию о преобразованиях для каждого метода. --- .../CMakeLists.txt | 18 +++ .../beskhmelnova_k_implicit_conversions.cpp | 116 ++++++++++++++++++ ...skhmelnova_k_implicit_conversions_test.cpp | 50 ++++++++ 3 files changed, 184 insertions(+) create mode 100644 clang/compiler-course/beskhmelnova_k_implicit_conversions/CMakeLists.txt create mode 100644 clang/compiler-course/beskhmelnova_k_implicit_conversions/beskhmelnova_k_implicit_conversions.cpp create mode 100644 clang/test/compiler-course/beskhmelnova_k_implicit_conversions/beskhmelnova_k_implicit_conversions_test.cpp diff --git a/clang/compiler-course/beskhmelnova_k_implicit_conversions/CMakeLists.txt b/clang/compiler-course/beskhmelnova_k_implicit_conversions/CMakeLists.txt new file mode 100644 index 0000000000000..c4377d6591845 --- /dev/null +++ b/clang/compiler-course/beskhmelnova_k_implicit_conversions/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "ClangAST_1") +set(Student "BeskhmelnovaK") +set(Group "FIIT1") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/beskhmelnova_k_implicit_conversions/beskhmelnova_k_implicit_conversions.cpp b/clang/compiler-course/beskhmelnova_k_implicit_conversions/beskhmelnova_k_implicit_conversions.cpp new file mode 100644 index 0000000000000..015e1366147e2 --- /dev/null +++ b/clang/compiler-course/beskhmelnova_k_implicit_conversions/beskhmelnova_k_implicit_conversions.cpp @@ -0,0 +1,116 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include + +namespace { + + class ImplicitCastVisitor final : public clang::RecursiveASTVisitor { + public: + explicit ImplicitCastVisitor() = default; + + bool VisitFunctionDecl(clang::FunctionDecl* Func) { + CurrentFunction = Func->getNameInfo().getName().getAsString(); + return true; + } + + bool VisitCXXConstructExpr(clang::CXXConstructExpr* Ctor) { + if (Ctor->getNumArgs() < 1) { + return true; + } + + clang::QualType FromType = Ctor->getArg(0)->getType(); + clang::QualType ToType = Ctor->getType(); + + if (FromType == ToType) { + return true; + } + + CastList.emplace_back(CastEntry{ CurrentFunction, FromType.getAsString(), ToType.getAsString() }); + return true; + } + + clang::QualType getRealType(clang::QualType Type) { + Type = Type.getCanonicalType(); + if (auto* TST = Type->getAs()) { + return TST->getDecl()->getUnderlyingType().getCanonicalType(); + } + return Type; + } + + bool VisitImplicitCastExpr(clang::ImplicitCastExpr* Cast) { + clang::CastKind Kind = Cast->getCastKind(); + if (Kind == clang::CK_NoOp || Kind == clang::CK_LValueToRValue || Kind == clang::CK_FunctionToPointerDecay) { + return true; + } + + clang::QualType FromType = getRealType(Cast->getSubExpr()->getType()); + clang::QualType ToType = getRealType(Cast->getType()); + + if (FromType == ToType) { + return true; + } + + CastList.emplace_back(CastEntry{ CurrentFunction, FromType.getAsString(), ToType.getAsString() }); + return true; + } + + void PrintResults() { + std::string LastFunction; + for (const auto& Entry : CastList) { + if (Entry.FunctionName != LastFunction) { + llvm::outs() << "Function " << Entry.FunctionName << "\n"; + LastFunction = Entry.FunctionName; + } + llvm::outs() << Entry.getCastDescription() << ": 1\n"; + } + llvm::outs() << "Total implicit conversions: " << CastList.size() << "\n"; + } + + private: + struct CastEntry { + std::string FunctionName; + std::string FromType; + std::string ToType; + + std::string getCastDescription() const { + return FromType + " -> " + ToType; + } + }; + + std::string CurrentFunction; + std::vector CastList; + }; + + class ImplicitCastConsumer final : public clang::ASTConsumer { + public: + explicit ImplicitCastConsumer() = default; + + void HandleTranslationUnit(clang::ASTContext& Context) override { + Visitor.TraverseDecl(Context.getTranslationUnitDecl()); + Visitor.PrintResults(); + } + + private: + ImplicitCastVisitor Visitor; + }; + + class ImplicitCastAction final : public clang::PluginASTAction { + public: + std::unique_ptr CreateASTConsumer(clang::CompilerInstance& CI, llvm::StringRef) override { + return std::make_unique(); + } + + bool ParseArgs(const clang::CompilerInstance& CI, const std::vector& Args) override { + return true; + } + }; + +} // namespace + +static clang::FrontendPluginRegistry::Add +X("ClangAST_1_BeskhmelnovaK_FIIT1_ClangAST", "Counts implicit type conversions"); diff --git a/clang/test/compiler-course/beskhmelnova_k_implicit_conversions/beskhmelnova_k_implicit_conversions_test.cpp b/clang/test/compiler-course/beskhmelnova_k_implicit_conversions/beskhmelnova_k_implicit_conversions_test.cpp new file mode 100644 index 0000000000000..9d0354ec9134e --- /dev/null +++ b/clang/test/compiler-course/beskhmelnova_k_implicit_conversions/beskhmelnova_k_implicit_conversions_test.cpp @@ -0,0 +1,50 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/ClangAST_1_BeskhmelnovaK_FIIT1_ClangAST%pluginext -plugin ClangAST_1_BeskhmelnovaK_FIIT1_ClangAST -fsyntax-only %s 2>&1 | FileCheck --match-full-lines %s + +// Проверка порядка выводимых строк: +// CHECK: Function sum +// CHECK-NEXT: float -> double: 1 +// CHECK-NEXT: int -> float: 1 + +double sum(int a, float b) { + return a + b; +} + +// CHECK: Function mul +// CHECK-NEXT: double -> int: 1 +// CHECK-NEXT: float -> double: 1 +// CHECK-NEXT: float -> int: 1 + +int mul(float a, float b) { + return a + sum(a, b); +} + +// CHECK: Function createX +// CHECK-NEXT: int -> X: 1 + +class X { + int x; +public: + X(int val) : x(val) {} +}; + +X createX() { + return 10; +} + +// CHECK: Function foo +// CHECK-NEXT: int -> float: 1 +// CHECK-NEXT: float -> int: 1 +// CHECK-NEXT: int * -> void *: 1 + +using Abrakadabra = float; +using Boom = int; + +void boo(void*); + +void foo() { + Abrakadabra x = Boom(); + Boom y = x; + boo(&y); +} + +// CHECK: Total implicit conversions: 9 From 8c3ef28612e63116e4089e605fafd628ac7beffe Mon Sep 17 00:00:00 2001 From: MatveyKurakin <113084585+MatveyKurakin@users.noreply.github.com> Date: Tue, 11 Mar 2025 10:11:10 +0300 Subject: [PATCH 08/50] Kurakin Matvey. FIIT1. Lab 1. Clang AST (#10) The task was to analyze user-defined classes and structures (user-defined types) For user-defined types, the base types are defined The type and access modifier are defined for fields. The methods are defined by the return type and the types of parameters they accept, the access modifier, virtual, pure virtual, or override. Implicitly created methods are ignored --- .../kurakin_user_data_type/CMakeLists.txt | 18 +++ .../kurakin_user_data_type_plugin.cpp | 110 +++++++++++++++++ .../kurakin_user_data_type_test.cpp | 112 ++++++++++++++++++ 3 files changed, 240 insertions(+) create mode 100644 clang/compiler-course/kurakin_user_data_type/CMakeLists.txt create mode 100644 clang/compiler-course/kurakin_user_data_type/kurakin_user_data_type_plugin.cpp create mode 100644 clang/test/compiler-course/kurakin_user_data_type/kurakin_user_data_type_test.cpp diff --git a/clang/compiler-course/kurakin_user_data_type/CMakeLists.txt b/clang/compiler-course/kurakin_user_data_type/CMakeLists.txt new file mode 100644 index 0000000000000..83993472be215 --- /dev/null +++ b/clang/compiler-course/kurakin_user_data_type/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "UserDataTypePlugin") +set(Student "Kurakin_Matvey") +set(Group "FIIT1") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/kurakin_user_data_type/kurakin_user_data_type_plugin.cpp b/clang/compiler-course/kurakin_user_data_type/kurakin_user_data_type_plugin.cpp new file mode 100644 index 0000000000000..e5c33341ed6aa --- /dev/null +++ b/clang/compiler-course/kurakin_user_data_type/kurakin_user_data_type_plugin.cpp @@ -0,0 +1,110 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/ADT/STLExtras.h" + +namespace { + +std::string getAccessModifier(clang::AccessSpecifier am){ + if(am == clang::AS_public) return "|public"; + else if(am == clang::AS_private) return "|private"; + else if(am == clang::AS_protected) return "|protected"; + return ""; +} + +class UserDataTypeVisitor final : public clang::RecursiveASTVisitor { +public: + explicit UserDataTypeVisitor(clang::ASTContext *context) : m_context(context) {} + bool VisitCXXRecordDecl(clang::CXXRecordDecl *rd) { + auto &os = llvm::outs(); + + os << rd->getNameAsString(); + if(rd->getNumBases()) { + os << " -> "; + llvm::interleave( + rd->bases(), + os, + [&](const clang::CXXBaseSpecifier &x) { + os << x.getType().getAsString(); + }, + "," + ); + } + os << "\n"; + + os << "|_Fields\n"; + for(const auto &f : rd->fields()) { + os << "| |_ " << f->getName() << " (" << f->getType().getAsString(); + os << getAccessModifier(f->getAccess()); + os << ")\n"; + } + if(rd->fields().empty()) + os << "| |_ (has no fields)\n"; + + os << "|\n"; + + os << "|_Methods\n"; + for(const auto &m : rd->methods()) { + if(m->isImplicit()) continue; + + os << "| |_ " << m->getNameAsString() << " (" << m->getReturnType().getAsString() << "("; + if(m->getNumParams()) { + llvm::interleave( + m->parameters(), + os, + [&](const clang::ParmVarDecl* x) { + os << x->getType().getAsString(); + }, + "," + ); + } + os << ")"; + os << getAccessModifier(m->getAccess()); + + if(m->hasAttr()) os << "|override"; + else if(m->isVirtual()) os << "|virtual"; + if(m->isPureVirtual()) os << "|pure"; + if(m->isStatic()) os << "|static"; + + os << ")\n"; + } + if(rd->methods().empty()) + os << "| |_ (has no methods)\n"; + + return true; + } + +private: + clang::ASTContext *m_context; +}; + +class UserDataTypeConsumer final : public clang::ASTConsumer { +public: + explicit UserDataTypeConsumer(clang::ASTContext *context) : m_visitor(context) {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + m_visitor.TraverseDecl(context.getTranslationUnitDecl()); + } + +private: + UserDataTypeVisitor m_visitor; +}; + +class UserDataTypeAction final : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + return std::make_unique(&ci.getASTContext()); + } + + bool ParseArgs(const clang::CompilerInstance &ci, + const std::vector &args) override { + return true; + } +}; +} // namespace + +static clang::FrontendPluginRegistry::Add + X("UserDataTypePlugin_Kurakin_Matvey_FIIT1_ClangAST", "Print information about the user's data type"); diff --git a/clang/test/compiler-course/kurakin_user_data_type/kurakin_user_data_type_test.cpp b/clang/test/compiler-course/kurakin_user_data_type/kurakin_user_data_type_test.cpp new file mode 100644 index 0000000000000..86ec55876783d --- /dev/null +++ b/clang/test/compiler-course/kurakin_user_data_type/kurakin_user_data_type_test.cpp @@ -0,0 +1,112 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/UserDataTypePlugin_Kurakin_Matvey_FIIT1_ClangAST%pluginext -plugin UserDataTypePlugin_Kurakin_Matvey_FIIT1_ClangAST -fsyntax-only %s 2>&1 | FileCheck %s + +//CHECK: Human +//CHECK-NEXT: |_Fields +//CHECK-NEXT: | |_ age (unsigned int|public) +//CHECK-NEXT: | |_ height (unsigned int|public) +//CHECK-NEXT: | +//CHECK-NEXT: |_Methods +//CHECK-NEXT: | |_ sleep (void()|public|virtual|pure) +//CHECK-NEXT: | |_ eat (void()|public|virtual|pure) + +struct Human { + unsigned age; + unsigned height; + virtual void sleep() = 0; + virtual void eat() = 0; +}; + +//CHECK: Engineer -> Human +//CHECK-NEXT: |_Fields +//CHECK-NEXT: | |_ salary (unsigned int|public) +//CHECK-NEXT: | +//CHECK-NEXT: |_Methods +//CHECK-NEXT: | |_ sleep (void()|public|override) +//CHECK-NEXT: | |_ eat (void()|public|override) +//CHECK-NEXT: | |_ work (void()|public) + +struct Engineer : Human { + unsigned salary; + void sleep() override { /* something */ } + void eat() override { /* something */ } + void work() { /* something */ } +}; + +//CHECK: A +//CHECK-NEXT: |_Fields +//CHECK-NEXT: | |_ a1 (int|private) +//CHECK-NEXT: | |_ a2 (float|private) +//CHECK-NEXT: | +//CHECK-NEXT: |_Methods +//CHECK-NEXT: | |_ somefunc (int(float)|private) + +class A { + int a1; + float a2; + int somefunc(float); +}; + +//CHECK: B +//CHECK-NEXT: |_Fields +//CHECK-NEXT: | |_ (has no fields) +//CHECK-NEXT: | +//CHECK-NEXT: |_Methods +//CHECK-NEXT: | |_ b1 (void(int,int)|private|virtual) + +class B { + virtual void b1(int b2, int b3); +}; + +//CHECK: C -> A,B +//CHECK-NEXT: |_Fields +//CHECK-NEXT: | |_ (has no fields) +//CHECK-NEXT: | +//CHECK-NEXT: |_Methods +//CHECK-NEXT: | |_ b1 (void(int,int)|private|override) +//CHECK-NEXT: | |_ somefunc (int(float)|private) + +class C : A,B{ + void b1(int b2, int b3) override; + int somefunc(float); +}; + +//CHECK: D +//CHECK-NEXT: |_Fields +//CHECK-NEXT: | |_ d1 (int[5]|private) +//CHECK-NEXT: | |_ d2 (T|protected) +//CHECK-NEXT: | +//CHECK-NEXT: |_Methods +//CHECK-NEXT: | |_ d3 (T(T,int)|public|virtual|pure) + +template +struct D{ +private: + int d1[5]; +protected: + T d2; +public: + virtual T d3(T d4, int d5) = 0; +}; + + +//CHECK: E +//CHECK-NEXT: |_Fields +//CHECK-NEXT: | |_ (has no fields) +//CHECK-NEXT: | +//CHECK-NEXT: |_Methods +//CHECK-NEXT: | |_ e2 (char(char)|public|static) + +struct E{ + static char e2(char); +}; + +//CHECK: F +//CHECK-NEXT: |_Fields +//CHECK-NEXT: | |_ f1 (int|private) +//CHECK-NEXT: | +//CHECK-NEXT: |_Methods +//CHECK-NEXT: | |_ (has no methods) + +class F{ + int f1; +}; From 687a39badb149b5b1a262f2cc8b99083afc8d6d9 Mon Sep 17 00:00:00 2001 From: Alexey Chistov <112825972+wabka22@users.noreply.github.com> Date: Tue, 11 Mar 2025 19:11:34 +0300 Subject: [PATCH 09/50] =?UTF-8?q?=D0=A7=D0=B8=D1=81=D1=82=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9.=20=D0=9B=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD=D0=B0=D1=8F=20?= =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=201.=20Clang=20AST.=20?= =?UTF-8?q?=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=201.=20(#11)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Плагин выводит имя класса или структуры, указывает, является ли тип структурой или классом, а также является ли он шаблонным, перечисляет все поля и методы класса, указывая их имена, типы и спецификаторы доступа, а для методов дополнительно выводит информацию о том, является ли метод виртуальным, перегруженным оператором или чисто виртуальным. При наличии базовых классов плагин выводит их имена и спецификаторы доступа (public, protected, private). --- .../chistov_a_user_data_type/CMakeLists.txt | 18 +++ .../chistov_a_user_data_type_plugin.cpp | 119 ++++++++++++++++++ .../chistov_a_user_data_type_test.cpp | 92 ++++++++++++++ 3 files changed, 229 insertions(+) create mode 100644 clang/compiler-course/chistov_a_user_data_type/CMakeLists.txt create mode 100644 clang/compiler-course/chistov_a_user_data_type/chistov_a_user_data_type_plugin.cpp create mode 100644 clang/test/compiler-course/chistov_a_user_data_type/chistov_a_user_data_type_test.cpp diff --git a/clang/compiler-course/chistov_a_user_data_type/CMakeLists.txt b/clang/compiler-course/chistov_a_user_data_type/CMakeLists.txt new file mode 100644 index 0000000000000..9bc1da836206b --- /dev/null +++ b/clang/compiler-course/chistov_a_user_data_type/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "PrintData") +set(Student "Chistov_Alexey") +set(Group "FIIT1") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/chistov_a_user_data_type/chistov_a_user_data_type_plugin.cpp b/clang/compiler-course/chistov_a_user_data_type/chistov_a_user_data_type_plugin.cpp new file mode 100644 index 0000000000000..cdf558e7d6084 --- /dev/null +++ b/clang/compiler-course/chistov_a_user_data_type/chistov_a_user_data_type_plugin.cpp @@ -0,0 +1,119 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "llvm/Support/raw_ostream.h" + +namespace { +class PrintDataVisitor final : public clang::RecursiveASTVisitor { + clang::ASTContext *class_context_; + + std::string AccessSpecifierToString(clang::AccessSpecifier accessSpecifier) { + switch (accessSpecifier) { + case clang::AS_public: + return "public"; + case clang::AS_protected: + return "protected"; + case clang::AS_private: + return "private"; + default: + llvm::errs() << "Error: Unknown access specifier\n"; + return "unknown access specifier"; + } + } + + void PrintMember(const clang::ValueDecl *member) { + auto &os = llvm::outs(); + os << "| |_ " << member->getNameAsString() << ' '; + os << '('; + + if (const auto *method = llvm::dyn_cast(member)) { + os << method->getReturnType().getAsString(); + os << '|' << AccessSpecifierToString(member->getAccess()); + if (method->isStatic()) { + os << "|static"; + } + if (method->isVirtual()) { + os << "|virtual"; + } + if (method->isOverloadedOperator()) { + os << "|override"; + } + if (method->isPureVirtual()) { + os << "|pure"; + } + } else { + os << member->getType().getAsString() << '|' + << AccessSpecifierToString(member->getAccess()); + } + + os << ")\n"; + } + +public: + explicit PrintDataVisitor(clang::ASTContext *context) : class_context_(context) {} + + bool VisitCXXRecordDecl(clang::CXXRecordDecl *declaration) { + auto &os = llvm::outs(); + os << declaration->getNameAsString() + << (declaration->isStruct() ? "(struct" : "(class") + << (declaration->isTemplated() ? "|template)" : ")") << '\n'; + + if (!declaration->bases().empty()) { + for (const auto &base : declaration->bases()) { + if (auto *baseDecl = base.getType()->getAsCXXRecordDecl()) { + clang::AccessSpecifier accessSpecifier = base.getAccessSpecifier(); + os << declaration->getName() << " -> " + << AccessSpecifierToString(accessSpecifier) << " " + << baseDecl->getName() << "\n"; + } + } + } + + if (!declaration->field_empty()) { + os << "|_Fields\n"; + for (const auto *decl : declaration->decls()) { + if (auto *field = llvm::dyn_cast(decl)) { + PrintMember(field); + } + } + } + + if (!declaration->methods().empty()) { + os << "|_Methods\n"; + for (const auto *method : declaration->methods()) { + PrintMember(method); + } + } + os << '\n'; + return true; + } +}; + +class PrintDataConsumer final : public clang::ASTConsumer { +public: + explicit PrintDataConsumer(clang::ASTContext *context) : visitor_(context) {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + visitor_.TraverseDecl(context.getTranslationUnitDecl()); + } + +private: + PrintDataVisitor visitor_; +}; + +class PrintDataAction final : public clang::PluginASTAction { +public: + std::unique_ptr CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + return std::make_unique(&ci.getASTContext()); + } + + bool ParseArgs(const clang::CompilerInstance &ci, const std::vector &args) override { + return true; + } +}; + +} // namespace + +static clang::FrontendPluginRegistry::Add + X("PrintDataPlugin", "Print information about a custom data type"); diff --git a/clang/test/compiler-course/chistov_a_user_data_type/chistov_a_user_data_type_test.cpp b/clang/test/compiler-course/chistov_a_user_data_type/chistov_a_user_data_type_test.cpp new file mode 100644 index 0000000000000..91a9691fb0272 --- /dev/null +++ b/clang/test/compiler-course/chistov_a_user_data_type/chistov_a_user_data_type_test.cpp @@ -0,0 +1,92 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/PrintData_Chistov_Alexey_FIIT1_ClangAST%pluginext -plugin PrintDataPlugin %s -fsyntax-only 2>&1 | FileCheck %s + +// CHECK: A(class) +class A {}; + +// CHECK: B(struct) +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ a (A|public) +struct B { + A a; +}; + +// CHECK: Simple(struct) +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ x (int|public) +// CHECK-NEXT: | |_ y (double|public) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ method (int|public) +struct Simple { + int x; + double y; + + int method(int x, int y) {} +}; + +// CHECK: Static(class) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ method (void|private|static) +class Static { + static void method() {} +}; + +// CHECK: Template(class|template) +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ x (T|private) +// CHECK-NEXT: | |_ z (int|private) +template +class Template { + T x; + int z; +}; + +// CHECK: MultiTemplate(class|template) +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ first (T|private) +// CHECK-NEXT: | |_ second (U|private) +template +class MultiTemplate { + T first; + U second; +}; + +// CHECK: C -> public A +// CHECK: C -> public B +class C : public A, public B {}; + +// CHECK: PublicDerived -> public Base +// CHECK: ProtectedDerived -> protected Base +// CHECK: PrivateDerived -> private Base +class Base {}; +class PublicDerived : public Base {}; +class ProtectedDerived : protected Base {}; +class PrivateDerived : private Base {}; + +// CHECK: AccessModifiers +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ x (float|private) +// CHECK-NEXT: | |_ y (double|protected) +// CHECK-NEXT: | |_ z (long long|public) +class AccessModifiers { +private: + float x; +protected: + double y; +public: + long long z; +}; + +// CHECK: Person +// CHECK: |_Fields +// CHECK: | |_ age (unsigned int|public) +// CHECK: | |_ height (unsigned int|public) +// CHECK: |_Methods +// CHECK: | |_ sleep (void|public|virtual|pure) +// CHECK: | |_ eat (void|public|virtual|pure) +struct Person { + unsigned age; + unsigned height; + + virtual void sleep() = 0; + virtual void eat() = 0; +}; \ No newline at end of file From 823c5e413f30fc9ea38911ec83ad210c10b43904 Mon Sep 17 00:00:00 2001 From: Arseniy Obolenskiy Date: Thu, 13 Mar 2025 13:59:16 +0100 Subject: [PATCH 10/50] [CI] Fix clang-format job --- .github/workflows/compiler-course-tidy.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/compiler-course-tidy.yml b/.github/workflows/compiler-course-tidy.yml index 7e0bf5a963d5f..1c236234c840a 100644 --- a/.github/workflows/compiler-course-tidy.yml +++ b/.github/workflows/compiler-course-tidy.yml @@ -6,12 +6,15 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Install clang-format run: | sudo apt-get install -y clang-format - name: Run clang-format run: | - git-clang-format --diff `git merge-base ${GITHUB_SHA} ${GITHUB_BASE_REF}` + git-clang-format --diff `git merge-base ${GITHUB_SHA} origin/${GITHUB_BASE_REF}` ${GITHUB_SHA} 2>&1 | tee log.txt + exit `grep -c diff log.txt` clang-tidy: runs-on: ubuntu-latest steps: From 3b62a1605e1cd879d5fa47cf7a1303267130754a Mon Sep 17 00:00:00 2001 From: Kirius257 <113035841+Kirius257@users.noreply.github.com> Date: Thu, 13 Mar 2025 15:56:59 +0300 Subject: [PATCH 11/50] =?UTF-8?q?=D0=A5=D0=BE=D0=BB=D0=B8=D0=BD=20=D0=9A?= =?UTF-8?q?=D0=B8=D1=80=D0=B8=D0=BB=D0=BB.=20=D0=9B=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD=D0=B0=D1=8F=20=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=B0=201:=20=20Clang=20AST.=20=D0=92=D0=B0?= =?UTF-8?q?=D1=80=D0=B8=D0=B0=D0=BD=D1=82=204.=20(#9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **ПОСТАНОВКА ЗАДАЧИ** Разработать Clang плагин с использованием AST для добавления префиксов к переменным узлов следующих типов: VarDecl, DeclRefExpr, ParmVarDecl. Плагин должен успешно находить объявления переменных ,добавлять префиксы к ним и выводить изменённый код. **ОПИСАНИЕ РЕШЕНИЯ** Для обхода AST необходимо использовать стандартный шаблон Clang плагина, указанный в файлах PR. Перед обходом следует создать Rewriter - инструмент-модификатор кода программы, который будет изменять префиксы переменных, в классе FindNamedClassAction и затем передать в FindNamedClassConsumer для использования в каждой Translation Unit. Далее в соответствующих методах, начинающихся с Visit, обнаружить декларации переменных и определить их область видимости: static, local, global и т.д После обнаружения создаётся новый префикс и с помощью метода ReplaceText Rewriter модифицируется исходный код. **СПИСОК ИЗМЕНЕНИЙ** - Переименована переменная класса Rewriter; - Названия директорий л/р приведены в соответствие требованиям; - Убран артефакт // в файле с плагином; - Дублирование кода в файле с плагином значительно устранено; - Тест усложнён: добавлены дополнительные CHECK; - Нестрогая проверка строк изменена на строгую; - Добавлены пустые строки в конец файлов с плагином и тестом; - В контексте функции добавления префиксов теперь возвращается спецзначение ; в том случае, если не удалось определить область видимости переменной; - Исключено дублирование обработки префиксов для параметров функции; - Установлен правильный порядок проверок области видимости переменных; - Обобщена обработка узлов VarDecl, DeclRefExpr, ParmVarDecl в одном методе; - Бесполезные методы убраны из кода; --- .../Kholin_K_prefixes/CMakeLists.txt | 18 +++ .../Kholin_K_prefixes_plugin.cpp | 105 ++++++++++++++++++ .../Kholin_K_prefixes_test.cpp | 26 +++++ 3 files changed, 149 insertions(+) create mode 100644 clang/compiler-course/Kholin_K_prefixes/CMakeLists.txt create mode 100644 clang/compiler-course/Kholin_K_prefixes/Kholin_K_prefixes_plugin.cpp create mode 100644 clang/test/compiler-course/Kholin_K_prefixes/Kholin_K_prefixes_test.cpp diff --git a/clang/compiler-course/Kholin_K_prefixes/CMakeLists.txt b/clang/compiler-course/Kholin_K_prefixes/CMakeLists.txt new file mode 100644 index 0000000000000..b93f2b11ac933 --- /dev/null +++ b/clang/compiler-course/Kholin_K_prefixes/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "PrefixesPlugin") +set(Student "KholinKirill") +set(Group "FIIT3") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/Kholin_K_prefixes/Kholin_K_prefixes_plugin.cpp b/clang/compiler-course/Kholin_K_prefixes/Kholin_K_prefixes_plugin.cpp new file mode 100644 index 0000000000000..5cd720b1aba93 --- /dev/null +++ b/clang/compiler-course/Kholin_K_prefixes/Kholin_K_prefixes_plugin.cpp @@ -0,0 +1,105 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include + +using namespace clang; + +class FindNamedClassVisitor + : public RecursiveASTVisitor { +public: + explicit FindNamedClassVisitor(ASTContext *Context, Rewriter &R) + : Context(Context), Rewrite(R) {} + + std::optional getVarPrefix(const VarDecl *Decl) { + if (Decl->isStaticLocal()) { + return "static_"; + } else if (Decl->isFileVarDecl()) { + return "global_"; + } else if (isa(Decl)) { + return "param_"; + } else if (Decl->isLocalVarDecl()) { + return "local_"; + } + return std::nullopt; + } + + void renameVariable(Decl *Decl, SourceLocation StartLocation) { + if (!Decl) return; + + std::optional VarPrefix = getVarPrefix(cast(Decl)); + if (!VarPrefix.has_value()) return; + + std::string NewID = VarPrefix.value() + cast(Decl)->getNameAsString(); + SourceLocation EndLocation = + StartLocation.getLocWithOffset(cast(Decl)->getNameAsString().length()); + Rewrite.ReplaceText(SourceRange(StartLocation, EndLocation), NewID); + + FullSourceLoc FullLocation = Context->getFullLoc(StartLocation); + if (FullLocation.isValid()) { + llvm::outs() << "Found variable: " << cast(Decl)->getNameAsString() << " -> " + << NewID << " at " << FullLocation.getSpellingLineNumber() << ":" + << FullLocation.getSpellingColumnNumber() << "\n"; + } + } + + bool VisitVarDecl(VarDecl *Decl) { + if (!Decl) return false; + renameVariable(Decl, Decl->getLocation()); + return true; + } + + bool VisitDeclRefExpr(DeclRefExpr *Expr) { + if (!Expr) return false; + + VarDecl *Decl = dyn_cast(Expr->getDecl()); + + renameVariable(Decl, Expr->getLocation()); + return true; + } + +private: + ASTContext *Context; + Rewriter &Rewrite; +}; + +class FindNamedClassConsumer : public clang::ASTConsumer { +public: + explicit FindNamedClassConsumer(ASTContext *Context, Rewriter &R) + : Visitor(Context, R) {} + + void HandleTranslationUnit(ASTContext &Context) override { + Visitor.TraverseDecl(Context.getTranslationUnitDecl()); + } + +private: + FindNamedClassVisitor Visitor; +}; + +class FindNamedClassAction : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(CompilerInstance &Compiler, + llvm::StringRef InFile) override { + m_rewriter.setSourceMgr(Compiler.getSourceManager(), + Compiler.getLangOpts()); + return std::make_unique(&Compiler.getASTContext(), + m_rewriter); + } + + bool ParseArgs(const CompilerInstance &CI, + const std::vector &Args) override { + return true; + } + +private: + Rewriter m_rewriter; +}; + +static clang::FrontendPluginRegistry::Add + X("PrefixesPlugin_KholinKirill_FIIT3_ClangAST", "set prefixes variables"); diff --git a/clang/test/compiler-course/Kholin_K_prefixes/Kholin_K_prefixes_test.cpp b/clang/test/compiler-course/Kholin_K_prefixes/Kholin_K_prefixes_test.cpp new file mode 100644 index 0000000000000..34cac25e4f2b4 --- /dev/null +++ b/clang/test/compiler-course/Kholin_K_prefixes/Kholin_K_prefixes_test.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/PrefixesPlugin_KholinKirill_FIIT3_ClangAST%pluginext -plugin PrefixesPlugin_KholinKirill_FIIT3_ClangAST %s -fsyntax-only 2>&1 | FileCheck -v %s + +// CHECK: Found variable: var1 -> global_var1 at 18:5 +// CHECK-NEXT: Found variable: var3 -> global_var3 at 19:14 +// CHECK-NEXT: Found variable: ll -> global_ll at 20:21 +// CHECK-NEXT: Found variable: a -> param_a at 21:13 +// CHECK-NEXT: Found variable: b -> param_b at 21:20 +// CHECK-NEXT: Found variable: var2 -> static_var2 at 22:14 +// CHECK-NEXT: Found variable: var3 -> local_var3 at 23:7 +// CHECK-NEXT: Found variable: var2 -> static_var2 at 24:5 +// CHECK-NEXT: Found variable: a -> param_a at 25:10 +// CHECK-NEXT: Found variable: b -> param_b at 25:14 +// CHECK-NEXT: Found variable: var1 -> global_var1 at 25:18 +// CHECK-NEXT: Found variable: var2 -> static_var2 at 25:25 +// CHECK-NEXT: Found variable: var3 -> local_var3 at 25:32 +// CHECK-NEXT: Found variable: ll -> global_ll at 25:39 + +int var1 = 0; +extern float var3; +constexpr long long ll = 1L; +int foo(int a, int b) { + static int var2 = 0; + int var3 = 123; + ++var2; + return a + b + var1 + var2 + var3 + ll; +} From 000ed03755e6ab237cfeb776fbfbb8fcd56ba6a9 Mon Sep 17 00:00:00 2001 From: Irina2004-tech <111091810+Irina2004-tech@users.noreply.github.com> Date: Sat, 15 Mar 2025 01:41:11 +0300 Subject: [PATCH 12/50] =?UTF-8?q?=D0=9A=D1=83=D0=B4=D1=80=D1=8F=D1=88?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=20=D0=98=D1=80=D0=B8=D0=BD=D0=B0.=20=D0=9B?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD=D0=B0?= =?UTF-8?q?=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=201:=20Clang=20AS?= =?UTF-8?q?T.=20=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=203.=20(#7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Плагин обходит абстрактное синтаксическое дерево (AST) программы, используя механизм `RecursiveASTVisitor`. Класс `ImplicitConvVisitor` перехватывает узлы `ImplicitCastExpr`, соответствующие неявным преобразованиям. Игнорируются преобразования, не влияющие на семантику такие как `LValueToRValue`, `NoOp`, `FunctionToPointerDecay`. Для каждого преобразования извлекаются исходный и целевой типы, а также контекст — функция, где оно произошло. Статистика сохраняется в виде отображения `[функция -> (тип_источник -> тип_цель) -> количество]`. После обхода AST результаты выводятся в стандартный поток ошибок, включая общее число преобразований. --- .../CMakeLists.txt | 18 +++ .../kudryashova_i_clangast_plugin.cpp | 127 ++++++++++++++++++ .../kudryashova_i_clangast_test.cpp | 53 ++++++++ 3 files changed, 198 insertions(+) create mode 100644 clang/compiler-course/kudryashova_i_compilers_clangast/CMakeLists.txt create mode 100644 clang/compiler-course/kudryashova_i_compilers_clangast/kudryashova_i_clangast_plugin.cpp create mode 100644 clang/test/compiler-course/kudryashova_i_test_clangast/kudryashova_i_clangast_test.cpp diff --git a/clang/compiler-course/kudryashova_i_compilers_clangast/CMakeLists.txt b/clang/compiler-course/kudryashova_i_compilers_clangast/CMakeLists.txt new file mode 100644 index 0000000000000..978ec399ea946 --- /dev/null +++ b/clang/compiler-course/kudryashova_i_compilers_clangast/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "Implicit_Conv") +set(Student "Kudryashova_Irina") +set(Group "FIIT3") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/kudryashova_i_compilers_clangast/kudryashova_i_clangast_plugin.cpp b/clang/compiler-course/kudryashova_i_compilers_clangast/kudryashova_i_clangast_plugin.cpp new file mode 100644 index 0000000000000..f061be7ce289b --- /dev/null +++ b/clang/compiler-course/kudryashova_i_compilers_clangast/kudryashova_i_clangast_plugin.cpp @@ -0,0 +1,127 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ParentMapContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/Support/raw_ostream.h" + +namespace { + +class ImplicitConvVisitor + : public clang::RecursiveASTVisitor { + +private: + clang::ASTContext *m_context; + std::map, int>> + m_functionStats; + int m_totalConversions = 0; + +public: + explicit ImplicitConvVisitor(clang::ASTContext *context) + : m_context(context) {} + + bool VisitImplicitCastExpr(clang::ImplicitCastExpr *ICE) { + + auto castKind = ICE->getCastKind(); + if (castKind == clang::CK_LValueToRValue || castKind == clang::CK_NoOp || + castKind == clang::CK_FunctionToPointerDecay) { + return true; + } + clang::QualType SourceType = ICE->getSubExpr()->getType(); + clang::QualType TargetType = ICE->getType(); + + if (SourceType.getCanonicalType() == TargetType.getCanonicalType()) { + return true; + } + + auto Parents = m_context->getParents(*ICE); + while (!Parents.empty()) { + if (const auto *FD = Parents[0].get()) { + recordConversion(FD, SourceType, TargetType); + break; + } else if (const auto *Lambda = Parents[0].get()) { + if (const auto *CallOp = Lambda->getCallOperator()) { + recordConversion(CallOp, SourceType, TargetType); + break; + } + } else if (const auto *ME = Parents[0].get()) { + recordConversion(ME, SourceType, TargetType); + break; + } + Parents = m_context->getParents(Parents[0]); + } + return true; + } + + std::string normalizeTypeName(std::string typeName) { + const std::vector prefixes = {"struct ", "class "}; + for (const auto &prefix : prefixes) { + size_t pos = typeName.find(prefix); + if (pos == 0) { + typeName.erase(0, prefix.length()); + break; + } + } + typeName.erase(std::remove_if(typeName.begin(), typeName.end(), ::isspace), + typeName.end()); + size_t pos; + while ((pos = typeName.find("_Bool")) != std::string::npos) { + typeName.replace(pos, 5, "bool"); + } + return typeName; + } + + void recordConversion(const clang::FunctionDecl *FD, clang::QualType From, + clang::QualType To) { + std::string FromStr = + normalizeTypeName(From.getCanonicalType().getAsString()); + std::string ToStr = normalizeTypeName(To.getCanonicalType().getAsString()); + m_functionStats[FD][std::make_pair(FromStr, ToStr)]++; + m_totalConversions++; + } + + void printStats(llvm::raw_ostream &OS) { + for (const auto &[func, convs] : m_functionStats) { + OS << "Function `" << func->getName() << "`\n"; + for (const auto &[conv, num] : convs) { + OS << conv.first << " -> " << conv.second << ": " << num << "\n"; + } + } + OS << "Total implicit conversions: " << m_totalConversions << "\n"; + } +}; + +class ImplicitConvConsumer : public clang::ASTConsumer { + +private: + ImplicitConvVisitor m_visitor; + +public: + explicit ImplicitConvConsumer(clang::ASTContext *context) + : m_visitor(context) {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + m_visitor.TraverseDecl(context.getTranslationUnitDecl()); + m_visitor.printStats(llvm::errs()); + } +}; + +class ImplicitConvAction : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + return std::make_unique(&ci.getASTContext()); + } + + bool ParseArgs(const clang::CompilerInstance &ci, + const std::vector &args) override { + return true; + } +}; + +} // namespace + +static clang::FrontendPluginRegistry::Add + X("ImplicitConvPlugin", "Count implicit type conversions"); diff --git a/clang/test/compiler-course/kudryashova_i_test_clangast/kudryashova_i_clangast_test.cpp b/clang/test/compiler-course/kudryashova_i_test_clangast/kudryashova_i_clangast_test.cpp new file mode 100644 index 0000000000000..cd51ee4f09a2e --- /dev/null +++ b/clang/test/compiler-course/kudryashova_i_test_clangast/kudryashova_i_clangast_test.cpp @@ -0,0 +1,53 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/Implicit_Conv_Kudryashova_Irina_FIIT3_ClangAST%pluginext -plugin ImplicitConvPlugin %s -fsyntax-only 2>&1 | FileCheck %s + +// CHECK: Function `sum` +// CHECK-NEXT: float -> double: 1 +// CHECK-NEXT: int -> float: 1 + +double sum(int a, float b) { + return a + b; +} + +// CHECK-NEXT: Function `mul` +// CHECK-NEXT: double -> int: 1 +// CHECK-NEXT: float -> double: 1 +// CHECK-NEXT: float -> int: 1 + +int mul(float a, float b) { + return a + sum(a, b); +} + +// CHECK: Function `foo` +// CHECK-NEXT: float -> int: 1 +// CHECK-NEXT: int -> float: 1 + +using Abrakadabra = float; +using Boom = int; + +void foo() { + Abrakadabra x = Boom(); + Boom y = x; +} + +// CHECK: Function `moo` +// CHECK-NEXT: int* -> void*: 1 +void moo() { + int x; + void* vp = &x; +} + +// CHECK: Function `goo` +// CHECK-NEXT: int* -> bool: 1 +void goo(int* p) { + if (p) {} +} + +// CHECK: Function `boo` +// CHECK-NEXT: int -> bool: 1 +void boo() { + int x = 7; + bool b = x; +} + +// CHECK: Total implicit conversions: 10 + From bb696cd8602ad38a47823900a5b799a5296a00c2 Mon Sep 17 00:00:00 2001 From: dima-dimka04 <57627727+dima-dimka04@users.noreply.github.com> Date: Sat, 15 Mar 2025 07:19:58 +0300 Subject: [PATCH 13/50] =?UTF-8?q?=D0=94=D1=80=D0=BE=D0=B6=D0=B4=D0=B8?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=20=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= =?UTF-8?q?.=20=D0=9B=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=BD=D0=B0=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=201.?= =?UTF-8?q?=20Clang=20AST.=20=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=20?= =?UTF-8?q?3.=20(#13)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clang плагин `CastCounter` реализует одноимённый класс, наследованный от `RecursiveASTVisitor` и выполняющий обход AST, выявляя случаи неявного приведения типов. В классе `CastCounter` реализованы методы: - `VisitFunctionDecl`, получающий имя функции, в которую попали при обходе; - `VisitCXXConstructExpr`, проверяющий случаи, когда аргумент передаётся в конструктор и вызывает преобразование - `VisitImplicitCastExpr`, анализирующий неявные преобразования (игнорирует LValueToRValue, FunctionToPointerDecay) - `getResult` выводит информацию о найденных преобразованиях. Информация о преобразованиях содержится в мапе `CastMap`. Класс `CastCounterConsumer` управляет запуском обхода AST и вызывает `getResult`. Класс `CastCounterAction` регистрирует плагин в компиляторе Clang. Тест для плагина выполнен согласно образцам `example`. --- .../drozhdinov_d_clangast_var3/CMakeLists.txt | 18 +++ .../drozhdinov_clang.cpp | 106 ++++++++++++++++++ .../drozhdinov_d_clangast_var3_test/test.cpp | 55 +++++++++ 3 files changed, 179 insertions(+) create mode 100644 clang/compiler-course/drozhdinov_d_clangast_var3/CMakeLists.txt create mode 100644 clang/compiler-course/drozhdinov_d_clangast_var3/drozhdinov_clang.cpp create mode 100644 clang/test/compiler-course/drozhdinov_d_clangast_var3_test/test.cpp diff --git a/clang/compiler-course/drozhdinov_d_clangast_var3/CMakeLists.txt b/clang/compiler-course/drozhdinov_d_clangast_var3/CMakeLists.txt new file mode 100644 index 0000000000000..a5ed44e068030 --- /dev/null +++ b/clang/compiler-course/drozhdinov_d_clangast_var3/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "CastCounter") +set(Student "DrozhdinovD") +set(Group "FIIT1") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/drozhdinov_d_clangast_var3/drozhdinov_clang.cpp b/clang/compiler-course/drozhdinov_d_clangast_var3/drozhdinov_clang.cpp new file mode 100644 index 0000000000000..48372679026e2 --- /dev/null +++ b/clang/compiler-course/drozhdinov_d_clangast_var3/drozhdinov_clang.cpp @@ -0,0 +1,106 @@ +#include +#include +#include + +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "llvm/Support/raw_ostream.h" + +using llvm::outs; +using std::map; +using std::string; +using std::vector; + +namespace { +class CastCounter final : public clang::RecursiveASTVisitor { +public: + CastCounter() = default; + bool VisitFunctionDecl(clang::FunctionDecl *Expr) { + CurrentFunction = Expr->getNameAsString(); + return true; + } + + bool VisitCXXConstructExpr(clang::CXXConstructExpr *Expr) { + if (Expr->getNumArgs() < 1) { + return true; + } + + clang::QualType SourceType = Expr->getArg(0)->getType(); + clang::QualType DestType = Expr->getType(); + + if (SourceType == DestType) { + return true; + } + CastMap[CurrentFunction] + [std::make_pair(SourceType.getAsString(), DestType.getAsString())]++; + return true; + } + + bool VisitImplicitCastExpr(clang::ImplicitCastExpr *Expr) { + clang::CastKind Kind = Expr->getCastKind(); + + if (Kind == clang::CK_LValueToRValue || + Kind == clang::CK_FunctionToPointerDecay) { + return true; + } + + clang::QualType SourceType = + Expr->getSubExpr()->getType().getCanonicalType(); + clang::QualType DestType = Expr->getType().getCanonicalType(); + + if (SourceType == DestType) { + return true; + } + CastMap[CurrentFunction] + [std::make_pair(SourceType.getAsString(), DestType.getAsString())]++; + return true; + } + + void getResult() { + for (auto iter = CastMap.rbegin(); iter != CastMap.rend(); iter++) { + outs() << "Function " << iter->first << "\n"; + for (auto [cast, val] : iter->second) { + outs() << cast.first << " -> " << cast.second << ": " << val << "\n"; + } + } + } + +private: + map, int>> CastMap; + string CurrentFunction; +}; + +class CastCounterConsumer final : public clang::ASTConsumer { +private: + CastCounter cc; + +public: + CastCounterConsumer() = default; + + void HandleTranslationUnit(clang::ASTContext &Context) override { + cc.TraverseDecl(Context.getTranslationUnitDecl()); + cc.getResult(); + } +}; + +class CastCounterAction final : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &CI, llvm::StringRef) override { + return std::make_unique(); + } + + bool ParseArgs(const clang::CompilerInstance &CI, + const vector &Args) override { + return true; + } +}; + +} // namespace + +static clang::FrontendPluginRegistry::Add + X("CastCounter_DrozhdinovD_FIIT1_ClangAST", + "Detects and counts implicit casts in function bodies and constructor " + "conversions"); diff --git a/clang/test/compiler-course/drozhdinov_d_clangast_var3_test/test.cpp b/clang/test/compiler-course/drozhdinov_d_clangast_var3_test/test.cpp new file mode 100644 index 0000000000000..5c62ae75953c5 --- /dev/null +++ b/clang/test/compiler-course/drozhdinov_d_clangast_var3_test/test.cpp @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/CastCounter_DrozhdinovD_FIIT1_ClangAST%pluginext -plugin CastCounter_DrozhdinovD_FIIT1_ClangAST %s -fsyntax-only 2>&1 | FileCheck %s +using Abrakadabra = int; +#define int Abrakadabra + +// CHECK: Function sum +// CHECK-NEXT: float -> double: 1 +// CHECK-NEXT: int -> float: 1 + +// CHECK: Function mul +// CHECK-NEXT: double -> int: 1 +// CHECK-NEXT: float -> double: 1 +// CHECK-NEXT: float -> int: 1 + +double sum(int a, float b) { + return a + b; +} + +int mul(float a, float b) { + return a + sum(a, b); +} + +// CHECK: Function func +// CHECK-NEXT: _Bool -> int: 1 +// CHECK-NEXT: double -> _Bool: 2 +// CHECK-NEXT: int -> double: 1 + +void func() { + bool b = 3.14; + bool bb = 0.00; + int x = b; + double d = x; +} + +// CHECK: Function foo +// CHECK-NEXT: int * -> const int *: 1 + +void bar(const int*); +void foo() { + int x = 42; + bar(&x); +} + +// CHECK: Function createX +// CHECK-NEXT: int -> X: 1 + +class X { + int x; +public: + X(int val) : x(val) {} +}; + + +X createX() { + return 10; +} From a44724b6e24b11d8d069b68b876e620d25686927 Mon Sep 17 00:00:00 2001 From: Arseniy Obolenskiy Date: Sat, 15 Mar 2025 15:27:06 +0100 Subject: [PATCH 14/50] =?UTF-8?q?Revert=20"=D0=9A=D1=83=D0=B4=D1=80=D1=8F?= =?UTF-8?q?=D1=88=D0=BE=D0=B2=D0=B0=20=D0=98=D1=80=D0=B8=D0=BD=D0=B0.=20?= =?UTF-8?q?=D0=9B=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=201:=20Clan?= =?UTF-8?q?g=20AST.=20=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=203."=20(?= =?UTF-8?q?#29)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverts NN-complr-tech/compiler-course-2025#7 https://github.com/NN-complr-tech/compiler-course-2025/actions/runs/13873280316/job/38822484696 image --- .../CMakeLists.txt | 18 --- .../kudryashova_i_clangast_plugin.cpp | 127 ------------------ .../kudryashova_i_clangast_test.cpp | 53 -------- 3 files changed, 198 deletions(-) delete mode 100644 clang/compiler-course/kudryashova_i_compilers_clangast/CMakeLists.txt delete mode 100644 clang/compiler-course/kudryashova_i_compilers_clangast/kudryashova_i_clangast_plugin.cpp delete mode 100644 clang/test/compiler-course/kudryashova_i_test_clangast/kudryashova_i_clangast_test.cpp diff --git a/clang/compiler-course/kudryashova_i_compilers_clangast/CMakeLists.txt b/clang/compiler-course/kudryashova_i_compilers_clangast/CMakeLists.txt deleted file mode 100644 index 978ec399ea946..0000000000000 --- a/clang/compiler-course/kudryashova_i_compilers_clangast/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -set(Title "Implicit_Conv") -set(Student "Kudryashova_Irina") -set(Group "FIIT3") -set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") - -file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) -add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) - -if(WIN32 OR CYGWIN) - set(LLVM_LINK_COMPONENTS Support) - clang_target_link_libraries(${TARGET_NAME} PRIVATE - clangAST - clangBasic - clangFrontend - ) -endif() - -set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/kudryashova_i_compilers_clangast/kudryashova_i_clangast_plugin.cpp b/clang/compiler-course/kudryashova_i_compilers_clangast/kudryashova_i_clangast_plugin.cpp deleted file mode 100644 index f061be7ce289b..0000000000000 --- a/clang/compiler-course/kudryashova_i_compilers_clangast/kudryashova_i_clangast_plugin.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include "clang/AST/ASTConsumer.h" -#include "clang/AST/ParentMapContext.h" -#include "clang/AST/RecursiveASTVisitor.h" -#include "clang/Frontend/CompilerInstance.h" -#include "clang/Frontend/FrontendPluginRegistry.h" -#include "llvm/ADT/MapVector.h" -#include "llvm/Support/raw_ostream.h" - -namespace { - -class ImplicitConvVisitor - : public clang::RecursiveASTVisitor { - -private: - clang::ASTContext *m_context; - std::map, int>> - m_functionStats; - int m_totalConversions = 0; - -public: - explicit ImplicitConvVisitor(clang::ASTContext *context) - : m_context(context) {} - - bool VisitImplicitCastExpr(clang::ImplicitCastExpr *ICE) { - - auto castKind = ICE->getCastKind(); - if (castKind == clang::CK_LValueToRValue || castKind == clang::CK_NoOp || - castKind == clang::CK_FunctionToPointerDecay) { - return true; - } - clang::QualType SourceType = ICE->getSubExpr()->getType(); - clang::QualType TargetType = ICE->getType(); - - if (SourceType.getCanonicalType() == TargetType.getCanonicalType()) { - return true; - } - - auto Parents = m_context->getParents(*ICE); - while (!Parents.empty()) { - if (const auto *FD = Parents[0].get()) { - recordConversion(FD, SourceType, TargetType); - break; - } else if (const auto *Lambda = Parents[0].get()) { - if (const auto *CallOp = Lambda->getCallOperator()) { - recordConversion(CallOp, SourceType, TargetType); - break; - } - } else if (const auto *ME = Parents[0].get()) { - recordConversion(ME, SourceType, TargetType); - break; - } - Parents = m_context->getParents(Parents[0]); - } - return true; - } - - std::string normalizeTypeName(std::string typeName) { - const std::vector prefixes = {"struct ", "class "}; - for (const auto &prefix : prefixes) { - size_t pos = typeName.find(prefix); - if (pos == 0) { - typeName.erase(0, prefix.length()); - break; - } - } - typeName.erase(std::remove_if(typeName.begin(), typeName.end(), ::isspace), - typeName.end()); - size_t pos; - while ((pos = typeName.find("_Bool")) != std::string::npos) { - typeName.replace(pos, 5, "bool"); - } - return typeName; - } - - void recordConversion(const clang::FunctionDecl *FD, clang::QualType From, - clang::QualType To) { - std::string FromStr = - normalizeTypeName(From.getCanonicalType().getAsString()); - std::string ToStr = normalizeTypeName(To.getCanonicalType().getAsString()); - m_functionStats[FD][std::make_pair(FromStr, ToStr)]++; - m_totalConversions++; - } - - void printStats(llvm::raw_ostream &OS) { - for (const auto &[func, convs] : m_functionStats) { - OS << "Function `" << func->getName() << "`\n"; - for (const auto &[conv, num] : convs) { - OS << conv.first << " -> " << conv.second << ": " << num << "\n"; - } - } - OS << "Total implicit conversions: " << m_totalConversions << "\n"; - } -}; - -class ImplicitConvConsumer : public clang::ASTConsumer { - -private: - ImplicitConvVisitor m_visitor; - -public: - explicit ImplicitConvConsumer(clang::ASTContext *context) - : m_visitor(context) {} - - void HandleTranslationUnit(clang::ASTContext &context) override { - m_visitor.TraverseDecl(context.getTranslationUnitDecl()); - m_visitor.printStats(llvm::errs()); - } -}; - -class ImplicitConvAction : public clang::PluginASTAction { -public: - std::unique_ptr - CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { - return std::make_unique(&ci.getASTContext()); - } - - bool ParseArgs(const clang::CompilerInstance &ci, - const std::vector &args) override { - return true; - } -}; - -} // namespace - -static clang::FrontendPluginRegistry::Add - X("ImplicitConvPlugin", "Count implicit type conversions"); diff --git a/clang/test/compiler-course/kudryashova_i_test_clangast/kudryashova_i_clangast_test.cpp b/clang/test/compiler-course/kudryashova_i_test_clangast/kudryashova_i_clangast_test.cpp deleted file mode 100644 index cd51ee4f09a2e..0000000000000 --- a/clang/test/compiler-course/kudryashova_i_test_clangast/kudryashova_i_clangast_test.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// RUN: %clang_cc1 -load %llvmshlibdir/Implicit_Conv_Kudryashova_Irina_FIIT3_ClangAST%pluginext -plugin ImplicitConvPlugin %s -fsyntax-only 2>&1 | FileCheck %s - -// CHECK: Function `sum` -// CHECK-NEXT: float -> double: 1 -// CHECK-NEXT: int -> float: 1 - -double sum(int a, float b) { - return a + b; -} - -// CHECK-NEXT: Function `mul` -// CHECK-NEXT: double -> int: 1 -// CHECK-NEXT: float -> double: 1 -// CHECK-NEXT: float -> int: 1 - -int mul(float a, float b) { - return a + sum(a, b); -} - -// CHECK: Function `foo` -// CHECK-NEXT: float -> int: 1 -// CHECK-NEXT: int -> float: 1 - -using Abrakadabra = float; -using Boom = int; - -void foo() { - Abrakadabra x = Boom(); - Boom y = x; -} - -// CHECK: Function `moo` -// CHECK-NEXT: int* -> void*: 1 -void moo() { - int x; - void* vp = &x; -} - -// CHECK: Function `goo` -// CHECK-NEXT: int* -> bool: 1 -void goo(int* p) { - if (p) {} -} - -// CHECK: Function `boo` -// CHECK-NEXT: int -> bool: 1 -void boo() { - int x = 7; - bool b = x; -} - -// CHECK: Total implicit conversions: 10 - From b23eb5a456a6ac7aa0d4c116977cd04716539327 Mon Sep 17 00:00:00 2001 From: sudo-technoir Date: Sat, 15 Mar 2025 19:12:29 +0300 Subject: [PATCH 15/50] =?UTF-8?q?=D0=A8=D0=BA=D1=83=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=81=D0=BA=D0=B0=D1=8F=20=D0=95=D0=BB=D0=B5=D0=BD=D0=B0.=20?= =?UTF-8?q?=D0=9B=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=201.=20Clan?= =?UTF-8?q?g=20AST.=20=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=204.=20(#?= =?UTF-8?q?18)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Плагин ClangAST_4 для Clang автоматически добавляет префиксы (static_, global_, local_, param_) к именам переменных и параметров функций, чтобы обозначить их область видимости. Он использует RecursiveASTVisitor для обхода AST и Rewriter для изменения кода. PrefixVisitor выполняет анализ и переименование, PrefixConsumer запускает обработку, а PrefixAction управляет работой плагина и выводит измененный код. --- .../CMakeLists.txt | 18 ++++ .../shkurinskaya_e_var_renaming.cpp | 85 +++++++++++++++++++ .../shkurinskaya_e_var_renaming_test.cpp | 46 ++++++++++ 3 files changed, 149 insertions(+) create mode 100644 clang/compiler-course/shkurinskaya_e_var_renaming/CMakeLists.txt create mode 100644 clang/compiler-course/shkurinskaya_e_var_renaming/shkurinskaya_e_var_renaming.cpp create mode 100644 clang/test/compiler-course/shkurinskaya_e_var_renaming/shkurinskaya_e_var_renaming_test.cpp diff --git a/clang/compiler-course/shkurinskaya_e_var_renaming/CMakeLists.txt b/clang/compiler-course/shkurinskaya_e_var_renaming/CMakeLists.txt new file mode 100644 index 0000000000000..2ba4b4c8b0ca5 --- /dev/null +++ b/clang/compiler-course/shkurinskaya_e_var_renaming/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "ClangAST_4") +set(Student "Shkurinskaya_Elena") +set(Group "FIIT2") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/shkurinskaya_e_var_renaming/shkurinskaya_e_var_renaming.cpp b/clang/compiler-course/shkurinskaya_e_var_renaming/shkurinskaya_e_var_renaming.cpp new file mode 100644 index 0000000000000..a8d890d9c25b5 --- /dev/null +++ b/clang/compiler-course/shkurinskaya_e_var_renaming/shkurinskaya_e_var_renaming.cpp @@ -0,0 +1,85 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/raw_ostream.h" + +namespace { +class PrefixVisitor final : public clang::RecursiveASTVisitor { +public: + explicit PrefixVisitor(clang::ASTContext *context, clang::Rewriter &rewriter) + : m_rewriter(rewriter) {} + + bool VisitVarDecl(clang::VarDecl *VD) { + if (VD->getLocation().isInvalid() || !VD->getIdentifier()) + return true; + + std::string newName = VD->getNameAsString(); + if (VD->isStaticLocal()) { + newName = "static_" + newName; + } else if (VD->hasGlobalStorage() && !VD->isStaticLocal()) { + newName = "global_" + newName; + } else if (VD->isLocalVarDecl() && !VD->isStaticLocal()) { + newName = "local_" + newName; + } + + if (newName != VD->getNameAsString()) { + m_rewriter.ReplaceText(VD->getLocation(), VD->getNameAsString().size(), + newName); + } + return true; + } + + bool VisitParmVarDecl(clang::ParmVarDecl *PVD) { + if (PVD->getLocation().isInvalid() || !PVD->getIdentifier()) + return true; + + std::string newName = "param_" + PVD->getNameAsString(); + m_rewriter.ReplaceText(PVD->getLocation(), PVD->getNameAsString().size(), + newName); + return true; + } + +private: + clang::Rewriter &m_rewriter; +}; + +class PrefixConsumer final : public clang::ASTConsumer { +public: + explicit PrefixConsumer(clang::ASTContext *context, clang::Rewriter &rewriter) + : m_visitor(context, rewriter) {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + m_visitor.TraverseDecl(context.getTranslationUnitDecl()); + } + +private: + PrefixVisitor m_visitor; +}; + +class PrefixAction final : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + m_rewriter.setSourceMgr(ci.getSourceManager(), ci.getLangOpts()); + return std::make_unique(&ci.getASTContext(), m_rewriter); + } + + bool ParseArgs(const clang::CompilerInstance &ci, + const std::vector &args) override { + return true; + } + + void EndSourceFileAction() override { + m_rewriter.getEditBuffer(m_rewriter.getSourceMgr().getMainFileID()) + .write(llvm::outs()); + } + +private: + clang::Rewriter m_rewriter; +}; +} // namespace + +static clang::FrontendPluginRegistry::Add + X("ClangAST_4", "Adding prefixes to variables"); diff --git a/clang/test/compiler-course/shkurinskaya_e_var_renaming/shkurinskaya_e_var_renaming_test.cpp b/clang/test/compiler-course/shkurinskaya_e_var_renaming/shkurinskaya_e_var_renaming_test.cpp new file mode 100644 index 0000000000000..c76e2e0bac320 --- /dev/null +++ b/clang/test/compiler-course/shkurinskaya_e_var_renaming/shkurinskaya_e_var_renaming_test.cpp @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/ClangAST_4_Shkurinskaya_Elena_FIIT2_ClangAST%pluginext -plugin ClangAST_4 -fsyntax-only %s 2>&1 | FileCheck %s + +// CHECK: int global_var = 42; +// CHECK-NEXT: float global_pi = 3.1415; +// CHECK-NEXT: static double global_static_value = 2.71828; +// CHECK-NEXT: void process(int param_a, float param_b, char param_c) { +// CHECK-NEXT: int local_sum = param_a + param_b; +// CHECK-NEXT: static int static_counter = 0; +// CHECK-NEXT: for (int local_i = 0; local_i < 5; ++local_i) { +// CHECK-NEXT: char local_buf[10]; +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: void anotherFunction() { +// CHECK-NEXT: static int static_flag = 1; +// CHECK-NEXT: int local_value = static_flag + 10; +// CHECK-NEXT: } +// CHECK-NEXT: void testLoop() { +// CHECK-NEXT: for (int local_j = 0; local_j < 3; ++local_j) { +// CHECK-NEXT: int local_temp = local_j * 2; +// CHECK-NEXT: } +// CHECK-NEXT: } + +int var = 42; +float pi = 3.1415; +static double static_value = 2.71828; + +void process(int a, float b, char c) { + int sum = a + b; + static int counter = 0; + + for (int i = 0; i < 5; ++i) { + char buf[10]; + buf[0] = c; + } +} + +void anotherFunction() { + static int flag = 1; + int value = flag + 10; +} + +void testLoop() { + for (int j = 0; j < 3; ++j) { + int temp = j * 2; + } +} From 3828bab86bca7976f0a2114b015caaae0db32d2e Mon Sep 17 00:00:00 2001 From: IonovvA <114287192+IonovvA@users.noreply.github.com> Date: Sat, 15 Mar 2025 20:32:22 +0300 Subject: [PATCH 16/50] =?UTF-8?q?=D0=98=D0=BE=D0=BD=D0=BE=D0=B2=D0=B0=20?= =?UTF-8?q?=D0=95=D0=BA=D0=B0=D1=82=D0=B5=D1=80=D0=B8=D0=BD=D0=B0.=20?= =?UTF-8?q?=D0=9B=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=201:=20Clan?= =?UTF-8?q?g=20AST.=20=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=201.=20?= =?UTF-8?q?=20(#15)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Плагин анализирует пользовательский тип данных, определяя, обладает ли структура наследованием, содержит ли поля и методы. При наличии элементов выводится их описание: тип данных, уровень доступа, а также характеристики методов — виртуальные, чисто виртуальные или переопределенные. --------- Co-authored-by: Arseniy Obolenskiy --- .../ionova_ekaterina_lab_1/CMakeLists.txt | 18 +++ .../ClassInfoPlugin.cpp | 148 ++++++++++++++++++ .../ionova_ekaterina_lab_1/test.cpp | 117 ++++++++++++++ 3 files changed, 283 insertions(+) create mode 100644 clang/compiler-course/ionova_ekaterina_lab_1/CMakeLists.txt create mode 100644 clang/compiler-course/ionova_ekaterina_lab_1/ClassInfoPlugin.cpp create mode 100644 clang/test/compiler-course/ionova_ekaterina_lab_1/test.cpp diff --git a/clang/compiler-course/ionova_ekaterina_lab_1/CMakeLists.txt b/clang/compiler-course/ionova_ekaterina_lab_1/CMakeLists.txt new file mode 100644 index 0000000000000..d3c9f653967a9 --- /dev/null +++ b/clang/compiler-course/ionova_ekaterina_lab_1/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "ClassInfoVisitorPlugin") +set(Student "Ionova_Ekaterina") +set(Group "FIIT1") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/ionova_ekaterina_lab_1/ClassInfoPlugin.cpp b/clang/compiler-course/ionova_ekaterina_lab_1/ClassInfoPlugin.cpp new file mode 100644 index 0000000000000..02a8761a1d41c --- /dev/null +++ b/clang/compiler-course/ionova_ekaterina_lab_1/ClassInfoPlugin.cpp @@ -0,0 +1,148 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "llvm/Support/raw_ostream.h" + +namespace { +class ClassInfoVisitor final + : public clang::RecursiveASTVisitor { +public: + explicit ClassInfoVisitor(clang::ASTContext *context) + : m_context(context), m_os(llvm::outs()) {} + + bool VisitCXXRecordDecl(clang::CXXRecordDecl *declaration) { + if (!declaration->isThisDeclarationADefinition() || + declaration->isImplicit()) + return true; + + printClassInfo(declaration); + printBaseClasses(declaration); + printFields(declaration); + printMethods(declaration); + + return true; + } + +private: + void printClassInfo(const clang::CXXRecordDecl *userType) { + m_os << userType->getNameAsString() << ' ' + << (userType->isStruct() ? "(struct" : "(class") + << (userType->isTemplated() ? "|template)" : ")") << '\n'; + } + + void printBaseClasses(const clang::CXXRecordDecl *declaration) { + if (declaration->getNumBases() == 0) + return; + + m_os << "|_Base Classes: "; + + llvm::interleaveComma( + declaration->bases(), m_os, [&](const clang::CXXBaseSpecifier &base) { + if (auto baseDecl = base.getType()->getAsCXXRecordDecl()) + m_os << baseDecl->getNameAsString(); + }); + + m_os << "\n"; + } + + void printFields(const clang::CXXRecordDecl *declaration) { + m_os << "|_Fields\n"; + + bool hasFields = false; + for (const auto *field : declaration->fields()) { + hasFields = true; + m_os << "| |_ " << field->getNameAsString() << " (" + << field->getType().getAsString() << "|" + << getAccessSpecifierAsString(field) << ")\n"; + } + + for (const auto *decl : declaration->decls()) { + if (const auto *varDecl = llvm::dyn_cast(decl)) { + if (varDecl->isStaticDataMember()) { + hasFields = true; + m_os << "| |_ " << varDecl->getNameAsString() << " (" + << varDecl->getType().getAsString() << "|" + << getAccessSpecifierAsString(varDecl) << "|static)\n"; + } + } + } + + if (!hasFields) + m_os << "| |_ (no fields)\n"; + } + + void printMethods(const clang::CXXRecordDecl *declaration) { + if (declaration->method_begin() == declaration->method_end()) + return; + + m_os << "|_Methods\n"; + + for (const auto *method : declaration->methods()) { + if (method->isImplicit()) + continue; + + m_os << "| |_ " << method->getNameAsString() << " (" + << method->getReturnType().getAsString() << "|" + << getAccessSpecifierAsString(method); + + if (method->isVirtual()) + m_os << "|virtual"; + if (method->isPureVirtual()) + m_os << "|pure"; + if (method->isConst()) + m_os << "|const"; + + m_os << ")\n"; + } + } + + std::string getAccessSpecifierAsString(const clang::Decl *decl) { + if (auto *namedDecl = llvm::dyn_cast(decl)) { + switch (namedDecl->getAccess()) { + case clang::AS_public: + return "public"; + case clang::AS_protected: + return "protected"; + case clang::AS_private: + return "private"; + default: + return "unknown"; + } + } + return "unknown"; + } + + clang::ASTContext *m_context; + llvm::raw_ostream &m_os; +}; + +class ClassInfoConsumer final : public clang::ASTConsumer { +public: + explicit ClassInfoConsumer(clang::ASTContext *context) : m_visitor(context) {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + m_visitor.TraverseDecl(context.getTranslationUnitDecl()); + } + +private: + ClassInfoVisitor m_visitor; +}; + +class ClassInfoAction final : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + return std::make_unique(&ci.getASTContext()); + } + + bool ParseArgs(const clang::CompilerInstance &ci, + const std::vector &args) override { + return true; + } +}; +} // namespace + +static clang::FrontendPluginRegistry::Add + X("ClassInfoVisitorPlugin_Ionova_Ekaterina_FIIT1_ClangAST", + "Prints detailed info about user-defined types"); diff --git a/clang/test/compiler-course/ionova_ekaterina_lab_1/test.cpp b/clang/test/compiler-course/ionova_ekaterina_lab_1/test.cpp new file mode 100644 index 0000000000000..47ad82d542470 --- /dev/null +++ b/clang/test/compiler-course/ionova_ekaterina_lab_1/test.cpp @@ -0,0 +1,117 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/ClassInfoVisitorPlugin_Ionova_Ekaterina_FIIT1_ClangAST%pluginext -plugin ClassInfoVisitorPlugin_Ionova_Ekaterina_FIIT1_ClangAST %s 1>&1 | FileCheck %s + +// CHECK: Point3D +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ m_x (double|public) +// CHECK-NEXT: | |_ m_y (double|public) +// CHECK-NEXT: | |_ m_z (double|public) +struct Point3D { + double m_x{}; + double m_y{}; + double m_z{}; +}; + +// CHECK: Pair +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ first (T1|public) +// CHECK-NEXT: | |_ second (T2|public) +template +struct Pair { + T1 first{}; + T2 second{}; +}; + +// CHECK: EmptyClass +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ (no fields) +class EmptyClass {}; + +// CHECK: NodeList +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ data (T|public) +// CHECK-NEXT: | |_ next (NodeList *|public) +template +struct NodeList { + T data{}; + NodeList *next{}; +}; + +// CHECK: CheckSpecifiers +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ publicMember (Point3D|public) +// CHECK-NEXT: | |_ protectedMember (Pair > >|protected) +// CHECK-NEXT: | |_ privateMember (EmptyClass|private) +class CheckSpecifiers { +public: + Point3D publicMember{}; +protected: + Pair>> protectedMember{}; +private: + EmptyClass privateMember{}; +}; + +// CHECK: User +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ m_id (int|public) +// CHECK-NEXT: | |_ m_human (Human|public) +// CHECK: Human +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ m_age (int|public) +// CHECK-NEXT: | |_ m_cash (int|public) +struct User { + struct Human { + int m_age{}; + int m_cash{}; + }; + int m_id{}; + Human m_human{}; +}; + +// CHECK: CheckStatic +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ m_staticMember (float|private|static) +class CheckStatic { + static float m_staticMember; +}; +float CheckStatic::m_staticMember = 0.0f; + +// CHECK: CheckConst +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ m_constMember (const char|private) +class CheckConst { + const char m_constMember{}; +}; + +// CHECK: CheckStaticConstTemplate +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ m_member (const T|private|static) +template +class CheckStaticConstTemplate { + static const T m_member; +}; +template +const T CheckStaticConstTemplate::m_member{}; + +// CHECK: MultipleInheritance +// CHECK-NEXT: |_Base Classes: Base1, Base2, Base3 +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ m_value (int|public) +class Base1 { +public: + int base1Value{}; +}; + +class Base2 { +public: + double base2Value{}; +}; + +class Base3 { +public: + char base3Value{}; +}; + +class MultipleInheritance : public Base1, public Base2, private Base3 { +public: + int m_value{}; +}; From b3e051a8c5c6e5c48839f01fcce2bb0890122438 Mon Sep 17 00:00:00 2001 From: MikasaAkc Date: Sun, 16 Mar 2025 23:01:15 +0300 Subject: [PATCH 17/50] =?UTF-8?q?=D0=9A=D0=BE=D0=BC=D1=88=D0=B8=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=94=D0=B0=D1=80=D1=8C=D1=8F.=D0=9B=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD=D0=B0=D1=8F=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=201.Clang=20AST.=20=D0=92=D0=B0?= =?UTF-8?q?=D1=80=D0=B8=D0=B0=D0=BD=D1=82=203=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Код реализует плагин для Clang, который анализирует неявные преобразования типов в исходном коде. Он посещает функции и выражения, такие как неявные преобразования, касты и конструкторы, чтобы отслеживать, какие типы преобразуются друг в друга. Когда обнаруживается преобразование типов, оно записывается в список. Плагин выводит отчет о том, какие функции выполняют неявные преобразования типов, с указанием исходного и целевого типов. --- .../CMakeLists.txt | 18 +++ .../komshina_d_num_implicit_conversions.cpp | 129 ++++++++++++++++++ .../komshina_d_num_implicit_conversions.cpp | 62 +++++++++ 3 files changed, 209 insertions(+) create mode 100644 clang/compiler-course/komshina_d_num_implicit_conversions/CMakeLists.txt create mode 100644 clang/compiler-course/komshina_d_num_implicit_conversions/komshina_d_num_implicit_conversions.cpp create mode 100644 clang/test/compiler-course/komshina_d_num_implicit_conversions/komshina_d_num_implicit_conversions.cpp diff --git a/clang/compiler-course/komshina_d_num_implicit_conversions/CMakeLists.txt b/clang/compiler-course/komshina_d_num_implicit_conversions/CMakeLists.txt new file mode 100644 index 0000000000000..c0dd8653d3623 --- /dev/null +++ b/clang/compiler-course/komshina_d_num_implicit_conversions/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "Implicit_Conversions") +set(Student "Komshina_Daria") +set(Group "FIIT1") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/komshina_d_num_implicit_conversions/komshina_d_num_implicit_conversions.cpp b/clang/compiler-course/komshina_d_num_implicit_conversions/komshina_d_num_implicit_conversions.cpp new file mode 100644 index 0000000000000..9de0c0b78328b --- /dev/null +++ b/clang/compiler-course/komshina_d_num_implicit_conversions/komshina_d_num_implicit_conversions.cpp @@ -0,0 +1,129 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include + +namespace { + +class ImplicitConversionsAnalyzer final + : public clang::RecursiveASTVisitor { +public: + ImplicitConversionsAnalyzer() {} + + bool VisitFunctionDecl(clang::FunctionDecl *func) { + CurrentFunction = func->getNameAsString(); + return true; + } + + bool VisitCXXConstructExpr(clang::CXXConstructExpr *Conv) { + if (Conv->getNumArgs() == 0) + return true; + HandleConv(Conv->getArg(0)->getType(), Conv->getType()); + return true; + } + + bool VisitImplicitCastExpr(clang::ImplicitCastExpr *CastExpr) { + switch (CastExpr->getCastKind()) { + case clang::CK_NoOp: + case clang::CK_LValueToRValue: + case clang::CK_FunctionToPointerDecay: + return true; + default: + HandleConv(CastExpr->getSubExpr()->getType(), CastExpr->getType()); + return true; + } + } + + void PrintReport(bool includeTotal = false) const { + std::set processedFunctions; + int totalConversions = 0; + + for (const auto &entry : CastList) { + const auto &[FunctionName, FromType, ToType] = entry; + + if (processedFunctions.find(FunctionName) == processedFunctions.end()) { + llvm::outs() << "Function " << FunctionName << "\n"; + processedFunctions.insert(FunctionName); + } + + llvm::outs() << FromType + " -> " + ToType << ": 1\n"; + totalConversions++; + } + + if (includeTotal) { + llvm::outs() << "Total implicit conversions: " << totalConversions + << "\n"; + } + } + +private: + void HandleConv(clang::QualType From, clang::QualType To) { + From = ResolveType(From); + To = ResolveType(To); + if (From == To) + return; + + CastList.emplace_back(CurrentFunction, From.getAsString(), + To.getAsString()); + } + + clang::QualType ResolveType(clang::QualType Type) { + Type = Type.getCanonicalType(); + if (auto *TST = Type->getAs()) { + return TST->getDecl()->getUnderlyingType().getCanonicalType(); + } + if (auto *ET = Type->getAs()) { + return ET->getNamedType().getCanonicalType(); + } + return Type; + } + + std::string CurrentFunction; + std::vector> CastList; +}; + +class ImplicitConversionsConsumer final : public clang::ASTConsumer { +public: + ImplicitConversionsConsumer() {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + m_visitor.TraverseDecl(context.getTranslationUnitDecl()); + m_visitor.PrintReport(true); + } + +private: + ImplicitConversionsAnalyzer m_visitor; +}; + +class ImplicitConversionsAction final : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + return std::make_unique(); + } + + bool ParseArgs(const clang::CompilerInstance &ci, + const std::vector &args) override { + for (const auto &arg : args) { + if (arg == "--help") { + llvm::errs() << "Usage: -Xclang -plugin -Xclang " + "Implicit_Conversions_Komshina_Daria_FIIT1_ClangAST\n" + << "Options:\n" + << " --help Show this help message\n"; + return false; + } + } + return true; + } +}; + +} // namespace + +static clang::FrontendPluginRegistry::Add + X("Implicit_Conversions_Komshina_Daria_FIIT1_ClangAST", + "Count implicit type conversions"); diff --git a/clang/test/compiler-course/komshina_d_num_implicit_conversions/komshina_d_num_implicit_conversions.cpp b/clang/test/compiler-course/komshina_d_num_implicit_conversions/komshina_d_num_implicit_conversions.cpp new file mode 100644 index 0000000000000..e2429b70d6a9b --- /dev/null +++ b/clang/test/compiler-course/komshina_d_num_implicit_conversions/komshina_d_num_implicit_conversions.cpp @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/Implicit_Conversions_Komshina_Daria_FIIT1_ClangAST%pluginext -plugin Implicit_Conversions_Komshina_Daria_FIIT1_ClangAST -fsyntax-only %s 2>&1 | FileCheck %s +// CHECK: Function sum +// CHECK-NEXT: float -> double: 1 +// CHECK-NEXT: int -> float: 1 + +double sum(int a, float b) { + return a + b; +} + +// CHECK: Function mul +// CHECK-NEXT: double -> int: 1 +// CHECK-NEXT: float -> double: 1 +// CHECK-NEXT: float -> int: 1 + +int mul(float a, float b) { + return a + sum(a, b); +} + +// CHECK: Function createX +// CHECK-NEXT: int -> class X: 1 + +class X { + int x; +public: + X(int val) : x(val) {} +}; + +X createX() { + return 10; +} + +// CHECK: Function foo +// CHECK-NEXT: int -> float: 1 +// CHECK-NEXT: float -> int: 1 + +using Abrakadabra = float; +using Boom = int; + +void foo() { + Abrakadabra x = Boom(); + Boom y = x; +} + +// CHECK: Function boo +// CHECK-NEXT: int -> _Bool: 1 + +void boo() { + int x = 3; + bool b = x; +} + +// CHECK: Function goo +// CHECK-NEXT: int * -> void *: 1 + +void loo(void*); + +void goo() { + int x = 20; + loo(&x); +} + +// CHECK: Total implicit conversions: 10 From 0cb240f8808a415241238fd09984b86743b4a6de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=BB=D0=B8=D0=B7=D0=B0=D0=B2=D0=B5=D1=82=D0=B0=20?= =?UTF-8?q?=D0=A4=D1=80=D0=BE=D0=BB=D0=BE=D0=B2=D0=B0?= <113036050+ElizavetaFrolova@users.noreply.github.com> Date: Tue, 18 Mar 2025 10:35:44 +0300 Subject: [PATCH 18/50] =?UTF-8?q?=D0=A4=D1=80=D0=BE=D0=BB=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=20=D0=95=D0=BB=D0=B8=D0=B7=D0=B0=D0=B2=D0=B5=D1=82=D0=B0?= =?UTF-8?q?.=20=D0=9B=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=BD=D0=B0=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=201.?= =?UTF-8?q?=20Clang=20AST.=20=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=20?= =?UTF-8?q?3.=20(#21)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Плагин использует возможности Clang для обхода абстрактного синтаксического дерева (AST) и регистрирует все случаи неявных преобразований, происходящих в коде. При каждом неявном преобразовании плагин фиксирует типы, участвующие в преобразовании, и подсчитывает их количество для каждой функции. Результаты анализа выводятся для каждой функции, отображается имя и список неявных преобразований с соответствующими количествами. --- .../CMakeLists.txt | 18 +++ .../frolova_e_implicit_conversions.cpp | 116 ++++++++++++++++++ .../frolova_e_implicit_conversions.cpp | 81 ++++++++++++ 3 files changed, 215 insertions(+) create mode 100644 clang/compiler-course/frolova_e_implicit_conversions/CMakeLists.txt create mode 100644 clang/compiler-course/frolova_e_implicit_conversions/frolova_e_implicit_conversions.cpp create mode 100644 clang/test/compiler-course/frolova_e_implicit_conversions/frolova_e_implicit_conversions.cpp diff --git a/clang/compiler-course/frolova_e_implicit_conversions/CMakeLists.txt b/clang/compiler-course/frolova_e_implicit_conversions/CMakeLists.txt new file mode 100644 index 0000000000000..73db204e0f5b8 --- /dev/null +++ b/clang/compiler-course/frolova_e_implicit_conversions/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "ImplicitConvPlugin") +set(Student "Frolova_Elizaveta") +set(Group "FIIT3") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/frolova_e_implicit_conversions/frolova_e_implicit_conversions.cpp b/clang/compiler-course/frolova_e_implicit_conversions/frolova_e_implicit_conversions.cpp new file mode 100644 index 0000000000000..3ea67a7610f05 --- /dev/null +++ b/clang/compiler-course/frolova_e_implicit_conversions/frolova_e_implicit_conversions.cpp @@ -0,0 +1,116 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include + +namespace { +class ImplicitConvVisitor final + : public clang::RecursiveASTVisitor { + void handleTypeConversion(const clang::QualType &fromType, + const clang::QualType &toType) { + std::string fromTypeStr = fromType.getAsString(); + std::string toTypeStr = toType.getAsString(); + + fromTypeStr = (fromTypeStr == "_Bool") ? "bool" : fromTypeStr; + toTypeStr = (toTypeStr == "_Bool") ? "bool" : toTypeStr; + + if (fromTypeStr == toTypeStr) + return; + + std::string conversion = fromTypeStr + " -> " + toTypeStr; + auto &convList = conversions[currentFunction]; + + for (auto &entry : convList) { + if (entry.first == conversion) { + entry.second++; + break; + } + } + + convList.push_back({conversion, 1}); + } + +public: + explicit ImplicitConvVisitor(clang::ASTContext *context) : Context(context) {} + + bool VisitFunctionDecl(clang::FunctionDecl *func) { + currentFunction = func->getNameInfo().getName().getAsString(); + if (conversions.find(currentFunction) == conversions.end()) { + functionOrder.push_back(currentFunction); + } + return true; + } + + bool VisitCXXConstructExpr(clang::CXXConstructExpr *expr) { + if (expr->getNumArgs() == 1) { + clang::QualType fromType = expr->getArg(0)->getType(); + clang::QualType toType = expr->getType(); + handleTypeConversion(fromType, toType); + } + return true; + } + + bool VisitImplicitCastExpr(clang::ImplicitCastExpr *cast) { + if (!cast || !cast->getSubExpr() || + cast->getCastKind() == clang::CK_FunctionToPointerDecay) { + return true; + } + + clang::QualType fromType = cast->getSubExpr()->getType(); + clang::QualType toType = cast->getType(); + handleTypeConversion(fromType, toType); + return true; + } + + void printResults() { + auto &os = llvm::outs(); + for (const auto &funcName : functionOrder) { + os << "Function: " << funcName << "\n"; + for (const auto &[c1, c2] : conversions[funcName]) { + os << " " << c1 << ": " << c2 << "\n"; + } + os << "\n"; + } + } + +private: + clang::ASTContext *Context; + std::string currentFunction; + std::vector functionOrder; + std::map>> conversions; +}; + +class ConversionConsumer final : public clang::ASTConsumer { +public: + explicit ConversionConsumer(clang::ASTContext *context) : Visitor(context) {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + Visitor.TraverseDecl(context.getTranslationUnitDecl()); + Visitor.printResults(); + } + +private: + ImplicitConvVisitor Visitor; +}; + +class ConversionAction final : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + return std::make_unique(&ci.getASTContext()); + } + + bool ParseArgs(const clang::CompilerInstance &ci, + const std::vector &args) override { + return true; + } +}; +} // namespace + +static clang::FrontendPluginRegistry::Add + X("ImplicitConvPlugin", + "Output the number of implicit conversions in the entire file"); diff --git a/clang/test/compiler-course/frolova_e_implicit_conversions/frolova_e_implicit_conversions.cpp b/clang/test/compiler-course/frolova_e_implicit_conversions/frolova_e_implicit_conversions.cpp new file mode 100644 index 0000000000000..f9eb00674e815 --- /dev/null +++ b/clang/test/compiler-course/frolova_e_implicit_conversions/frolova_e_implicit_conversions.cpp @@ -0,0 +1,81 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/ImplicitConvPlugin_Frolova_Elizaveta_FIIT3_ClangAST%pluginext -plugin ImplicitConvPlugin -fsyntax-only %s 2>&1 | FileCheck %s +using ll = long long; +#define long long ll + +// CHECK: Function: sum +// CHECK-NEXT: float -> double: 1 +// CHECK-NEXT: int -> float: 1 +double sum(int a, float b) { + return a + b; // 'a' (int) -> 'b' (float) -> 'double' +} + +// CHECK: Function: mul +// CHECK-NEXT: double -> int: 1 +// CHECK-NEXT: float -> double: 1 +// CHECK-NEXT: float -> int: 1 +int mul(float a, float b) { + return a + sum(a, b); +} + +// CHECK: Function: checkCondition +// CHECK-NEXT: int -> bool: 1 +// CHECK-NEXT: int -> double: 1 +double checkCondition() { + int x = 1; + if (x) { x++; } + return x; +} + +// CHECK: Function: convertAndReturn +// CHECK-NEXT: double -> float: 1 +// CHECK-NEXT: float -> int: 1 +int convertAndReturn() { + double x = 1.2; + float y = x; + return y; +} + +// CHECK: Function: convertLongLongToInt +// CHECK-NEXT: ll -> int: 1 +int convertLongLongToInt() { + ll x = 10000000000LL; + int y = x; + return y; +} + +// CHECK: Function: charToIntAndDouble +// CHECK-NEXT: char -> int: 1 +// CHECK-NEXT: int -> double: 1 +double charToIntAndDouble() { + char c = 'A'; + int i = c; + double d = i; + return d; +} + +// CHECK: Function: callProcessPointer +// CHECK-NEXT: int * -> const int *: 1 +void processPointer(const int*); +void callProcessPointer() { + int x = 42; + processPointer(&x); +} + +// CHECK: Function: createAndReturnObject +// CHECK-NEXT: int -> MyClass: 1 +class MyClass { + int x; + public: + MyClass(int); +}; + +MyClass createAndReturnObject() { + return 10; +} + +// CHECK:Function: foo +// CHECK-NEXT:int[3] -> int *: 1 +int foo(int a) { + static int aa[] = {1, 2, 3}; + return aa[a]; +} \ No newline at end of file From 12fec08c32c5f810c78f2591dc6a7dbf3cdedb2d Mon Sep 17 00:00:00 2001 From: KolodkinGrigorii <113025092+KolodkinGrigorii@users.noreply.github.com> Date: Tue, 18 Mar 2025 10:36:17 +0300 Subject: [PATCH 19/50] =?UTF-8?q?=D0=9A=D0=BE=D0=BB=D0=BE=D0=B4=D0=BA?= =?UTF-8?q?=D0=B8=D0=BD=20=D0=93=D1=80=D0=B8=D0=B3=D0=BE=D1=80=D0=B8=D0=B9?= =?UTF-8?q?.=20=D0=9B=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=BD=D0=B0=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=20?= =?UTF-8?q?=E2=84=961:=20ClangAST.=20=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD?= =?UTF-8?q?=D1=82=201.=20(#12)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Данный плагин выводит информацию о пользовательском типе данных: является ли структура наследственной, имеет ли какие-либо поля и методы и, если имеет, выводит информацию о них(тип данных, уровень доступа, является ли метод виртуальным/чисто виртуальным/переопределенным) --------- Co-authored-by: Kuznetsov-Artyom <110616662+Kuznetsov-Artyom@users.noreply.github.com> --- .../kolodkin_g_lab_1_var_1/CMakeLists.txt | 18 ++ .../kolodkin_g_lab1_plugin.cpp | 155 ++++++++++++++++++ .../kolodkin_g_lab1_test.cpp | 117 +++++++++++++ 3 files changed, 290 insertions(+) create mode 100644 clang/compiler-course/kolodkin_g_lab_1_var_1/CMakeLists.txt create mode 100644 clang/compiler-course/kolodkin_g_lab_1_var_1/kolodkin_g_lab1_plugin.cpp create mode 100644 clang/test/compiler-course/kolodkin_g_lab_1_var_1/kolodkin_g_lab1_test.cpp diff --git a/clang/compiler-course/kolodkin_g_lab_1_var_1/CMakeLists.txt b/clang/compiler-course/kolodkin_g_lab_1_var_1/CMakeLists.txt new file mode 100644 index 0000000000000..a8f00e4f397ea --- /dev/null +++ b/clang/compiler-course/kolodkin_g_lab_1_var_1/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "LabPlugin") +set(Student "KolodkinGrigorii") +set(Group "FIIT3") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/kolodkin_g_lab_1_var_1/kolodkin_g_lab1_plugin.cpp b/clang/compiler-course/kolodkin_g_lab_1_var_1/kolodkin_g_lab1_plugin.cpp new file mode 100644 index 0000000000000..806f396908e29 --- /dev/null +++ b/clang/compiler-course/kolodkin_g_lab_1_var_1/kolodkin_g_lab1_plugin.cpp @@ -0,0 +1,155 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +namespace { +std::string getAccessLevel(clang::AccessSpecifier access) { + switch (static_cast(access)) { + case clang::AccessSpecifier::AS_public: + return "public"; + case clang::AccessSpecifier::AS_protected: + return "protected"; + case clang::AccessSpecifier::AS_private: + return "private"; + default: + return ""; + } +} + +class LabPluginVisitor final + : public clang::RecursiveASTVisitor { +public: + explicit LabPluginVisitor(clang::ASTContext *context) {} + + bool VisitCXXRecordDecl(const clang::CXXRecordDecl *record) { + if (!record->isCompleteDefinition()) + return true; + + llvm::outs() << record->getName(); + + if (!record->bases().empty()) { + llvm::outs() << " -> "; + for (auto base = record->bases_begin(); base != record->bases_end(); + ++base) { + if (base != record->bases_begin()) { + llvm::outs() << ", "; + } + llvm::outs() << base->getType()->getAsCXXRecordDecl()->getName(); + } + } + + llvm::outs() << "\n"; + + llvm::outs() << "|_Friends\n"; + if (record->friend_begin() == record->friend_end()) { + llvm::outs() << "| |_ (no friends)\n"; + } else { + for (const auto *friendDecl : record->friends()) { + const clang::Decl *friendDeclType = friendDecl->getFriendDecl(); + if (const auto *friendClass = + llvm::dyn_cast(friendDeclType)) { + llvm::outs() << "| |_ " << friendClass->getName() << "\n"; + } else if (const auto *friendFunction = + llvm::dyn_cast(friendDeclType)) { + llvm::outs() << "| |_ " << friendFunction->getName() << "\n"; + } else { + llvm::outs() << "| |_ (unknown friend kind)\n"; + } + } + } + + llvm::outs() << "|_Fields\n"; + if (record->field_empty()) { + llvm::outs() << "| |_ (no fields)\n"; + } else { + for (const auto *field : record->fields()) { + llvm::outs() << "| |_ " << field->getName() << " (" + << field->getType().getAsString() << "|" + << ::getAccessLevel(field->getAccess()) << ")\n"; + } + } + + llvm::outs() << "|_Methods\n"; + if (record->methods().empty()) { + llvm::outs() << "| |_ (no methods)\n"; + } else { + for (const auto *method : record->methods()) { + if (method->isImplicit()) + continue; + + llvm::SmallVector specs; + + if (method->isVirtual() && !method->hasAttr()) { + specs.push_back("virtual"); + } + if (method->isPureVirtual()) { + specs.push_back("pure"); + } + if (method->hasAttr()) { + specs.push_back("override"); + } + + std::string oss_str; + llvm::raw_string_ostream oss(oss_str); + oss << method->getReturnType().getAsString() << "("; + + std::vector paramTypes; + for (unsigned i = 0; i < method->getNumParams(); ++i) { + const auto *param = method->getParamDecl(i); + paramTypes.push_back(param->getType().getAsString()); + } + + llvm::interleaveComma(paramTypes, oss, + [&](const std::string &type) { return type; }); + oss << ")"; + + llvm::outs() << "| |_ " << method->getName() << " (" << oss.str() << "|" + << ::getAccessLevel(method->getAccess()); + + for (const auto &s : specs) { + llvm::outs() << "|" << s; + } + llvm::outs() << ")\n"; + } + } + + llvm::outs() << "\n"; + + return true; + } +}; + +class LabPluginConsumer final : public clang::ASTConsumer { +public: + explicit LabPluginConsumer(clang::ASTContext *context) : m_visitor(context) {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + m_visitor.TraverseDecl(context.getTranslationUnitDecl()); + } + +private: + LabPluginVisitor m_visitor; +}; + +class LabPluginAction final : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + return std::make_unique(&ci.getASTContext()); + } + + bool ParseArgs(const clang::CompilerInstance &ci, + const std::vector &args) override { + return true; + } +}; + +} // namespace + +static clang::FrontendPluginRegistry::Add<::LabPluginAction> + X("LabPlugin_KolodkinGrigorii_FIIT3_ClangAST", + "Prints info about user defined data types"); \ No newline at end of file diff --git a/clang/test/compiler-course/kolodkin_g_lab_1_var_1/kolodkin_g_lab1_test.cpp b/clang/test/compiler-course/kolodkin_g_lab_1_var_1/kolodkin_g_lab1_test.cpp new file mode 100644 index 0000000000000..0da4e3b8cf262 --- /dev/null +++ b/clang/test/compiler-course/kolodkin_g_lab_1_var_1/kolodkin_g_lab1_test.cpp @@ -0,0 +1,117 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/LabPlugin_KolodkinGrigorii_FIIT3_ClangAST%pluginext -plugin LabPlugin_KolodkinGrigorii_FIIT3_ClangAST -fsyntax-only %s 2>&1 | FileCheck %s + +// CHECK: Shape +// CHECK-NEXT: |_Friends +// CHECK-NEXT: | |_ (no friends) +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ (no fields) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ area (double()|public|virtual|pure) + +struct Shape { + virtual double area() = 0; +}; + +// CHECK: Circle -> Shape +// CHECK-NEXT: |_Friends +// CHECK-NEXT: | |_ (no friends) +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ radius (double|public) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ area (double()|public|override) + +struct Circle : Shape { +public: + double radius; + + double area() override { + return 3.14 * radius * radius; + } +}; + +// CHECK: Rectangle -> Shape +// CHECK-NEXT: |_Friends +// CHECK-NEXT: | |_ (no friends) +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ width (double|public) +// CHECK-NEXT: | |_ height (double|public) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ area (double()|public|override) + +struct Rectangle : Shape { +public: + double width, height; + + double area() override { + return width * height; + } +}; + +// CHECK: ShapeA +// CHECK-NEXT: |_Friends +// CHECK-NEXT: | |_ (no friends) +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ valueA (int|public) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ displayA (void()|public) + +struct ShapeA { +public: + int valueA; + + void displayA() {} +}; + +// CHECK: ShapeB +// CHECK-NEXT: |_Friends +// CHECK-NEXT: | |_ (no friends) +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ valueB (int|public) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ displayB (void()|public) + +struct ShapeB { +public: + int valueB; + + void displayB() {} +}; + +// CHECK: EmptyClass +// CHECK-NEXT: |_Friends +// CHECK-NEXT: | |_ (no friends) +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ (no fields) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ (no methods) + +class EmptyClass{}; + +// CHECK: CombinedShape -> ShapeA, ShapeB +// CHECK-NEXT: |_Friends +// CHECK-NEXT: | |_ (no friends) +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ valueA (int|public) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ displayB (void()|public) + +class CombinedShape : public ShapeA, public ShapeB { +public: + int valueA; + + void displayB() {} +}; + +void FriendFunc(); + +// CHECK: FriendClass +// CHECK-NEXT: |_Friends +// CHECK-NEXT: | |_ FriendFunc +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ (no fields) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ (no methods) + +class FriendClass { + friend void FriendFunc(); +}; From f3bde0c5357713d7f929d5c50a846b80ddb7f83c Mon Sep 17 00:00:00 2001 From: Anastasia Rezantseva <62182112+AnastasiaRezantseva@users.noreply.github.com> Date: Tue, 18 Mar 2025 10:36:37 +0300 Subject: [PATCH 20/50] =?UTF-8?q?=D0=A0=D0=B5=D0=B7=D0=B0=D0=BD=D1=86?= =?UTF-8?q?=D0=B5=D0=B2=D0=B0=20=D0=90=D0=BD=D0=B0=D1=81=D1=82=D0=B0=D1=81?= =?UTF-8?q?=D0=B8=D1=8F.=20=D0=9B=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=BD=D0=B0=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=B0=201:=20Clang=20AST.=20=D0=92=D0=B0=D1=80=D0=B8=D0=B0?= =?UTF-8?q?=D0=BD=D1=82=204.=20(#14)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Реализован Clang плагин, который добавляет к статическим, локальным, глобальным объектам и параметрам функции соответствующие префиксы (static_, local_, global_, param_). Класс PrefixVisitor обходит AST (Abstract Syntax Tree) с помощью RecursiveASTVisitor и добавляет префиксы к переменным и параметрам в зависимости от их типа, а также обновляет имена переменных в вызовах функций, чтобы они соответствовали новым именам с префиксами. **Используемые API LLVM/Clang:** - RecursiveASTVisitor для обхода AST - VarDecl для работы с переменными - ParmVarDecl для работы с параметрами функций - CallExpr для анализа вызовов функций - Rewriter для модификации исходного кода --------- Co-authored-by: Arseniy Obolenskiy Co-authored-by: Aleksey --- .../rezantseva_a_prefixes/CMakeLists.txt | 18 +++ .../rezantseva_a_prefixes.cpp | 133 ++++++++++++++++++ .../rezantseva_a_prefixes_test.cpp | 26 ++++ 3 files changed, 177 insertions(+) create mode 100644 clang/compiler-course/rezantseva_a_prefixes/CMakeLists.txt create mode 100644 clang/compiler-course/rezantseva_a_prefixes/rezantseva_a_prefixes.cpp create mode 100644 clang/test/compiler-course/rezantseva_a_prefixes/rezantseva_a_prefixes_test.cpp diff --git a/clang/compiler-course/rezantseva_a_prefixes/CMakeLists.txt b/clang/compiler-course/rezantseva_a_prefixes/CMakeLists.txt new file mode 100644 index 0000000000000..618b95297bef3 --- /dev/null +++ b/clang/compiler-course/rezantseva_a_prefixes/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "PrefixesPlugin") +set(Student "RezantsevaAnastasia") +set(Group "FIIT1") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/rezantseva_a_prefixes/rezantseva_a_prefixes.cpp b/clang/compiler-course/rezantseva_a_prefixes/rezantseva_a_prefixes.cpp new file mode 100644 index 0000000000000..b1a5d42d7c757 --- /dev/null +++ b/clang/compiler-course/rezantseva_a_prefixes/rezantseva_a_prefixes.cpp @@ -0,0 +1,133 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/raw_ostream.h" +#include + +namespace { +class PrefixVisitor : public clang::RecursiveASTVisitor { +public: + explicit PrefixVisitor(clang::ASTContext *context, clang::Rewriter &rewriter) + : m_rewriter(rewriter) {} + + bool VisitVarDecl(clang::VarDecl *var) { + if (!var) { + return true; + } + if (var->getName().empty()) { + return true; + } + std::string prefix; + if (var->isStaticLocal()) { + prefix = "static_"; + } else if (var->isLocalVarDecl()) { + prefix = "local_"; + } else if (var->hasGlobalStorage()) { + prefix = "global_"; + } + + if (!prefix.empty()) { + std::string newName = prefix + var->getName().str(); + renameVar(var, newName); + m_renamedVars[var] = newName; + } + return true; + } + + bool VisitParmVarDecl(clang::ParmVarDecl *param) { + if (!param) { + return true; + } + if (param->getName().empty()) { + return true; + } + std::string newName = "param_" + param->getName().str(); + renameVar(param, newName); + m_renamedVars[param] = newName; + return true; + } + + bool VisitDeclRefExpr(clang::DeclRefExpr *expr) { + if (!expr) { + return true; + } + auto *decl = expr->getDecl(); + if (!decl) { + return true; + } + if (decl->getName().empty()) { + return true; + } + + if (auto *var = clang::dyn_cast(decl)) { + auto it = m_renamedVars.find(var); + if (it != m_renamedVars.end()) { + m_rewriter.ReplaceText(expr->getLocation(), var->getName().size(), + it->second); + } + } + return true; + } + +private: + clang::Rewriter &m_rewriter; + std::unordered_map m_renamedVars; + + void renameVar(clang::VarDecl *var, const std::string &newName) { + m_rewriter.ReplaceText(var->getLocation(), var->getName().size(), newName); + } +}; + +class PrefixConsumer : public clang::ASTConsumer { +public: + explicit PrefixConsumer(clang::ASTContext *context, clang::Rewriter &rewriter) + : m_visitor(context, rewriter), m_rewriter(rewriter) {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + m_visitor.TraverseDecl(context.getTranslationUnitDecl()); + m_rewriter.getEditBuffer(context.getSourceManager().getMainFileID()) + .write(llvm::outs()); + } + +private: + PrefixVisitor m_visitor; + clang::Rewriter &m_rewriter; +}; + +class PrefixAction : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + if (m_shouldExit) { + return nullptr; + } + + m_rewriter.setSourceMgr(ci.getSourceManager(), ci.getLangOpts()); + return std::make_unique(&ci.getASTContext(), m_rewriter); + } + + bool ParseArgs(const clang::CompilerInstance &ci, + const std::vector &args) override { + for (const auto &arg : args) { + if (arg == "--help") { + llvm::outs() << "PrefixesPlugin_RezantsevaAnastasia_FIIT1_ClangAST:\n"; + llvm::outs() << "This plugin adds appropriate prefixes to variables " + "and parameters in the code.\n"; + m_shouldExit = true; + return true; + } + } + return true; + } + +private: + clang::Rewriter m_rewriter; + bool m_shouldExit = false; +}; +} // namespace + +static clang::FrontendPluginRegistry::Add + X("PrefixesPlugin_RezantsevaAnastasia_FIIT1_ClangAST", + "Adds appropriate prefixes to objects and parameters"); \ No newline at end of file diff --git a/clang/test/compiler-course/rezantseva_a_prefixes/rezantseva_a_prefixes_test.cpp b/clang/test/compiler-course/rezantseva_a_prefixes/rezantseva_a_prefixes_test.cpp new file mode 100644 index 0000000000000..b30b5d531298c --- /dev/null +++ b/clang/test/compiler-course/rezantseva_a_prefixes/rezantseva_a_prefixes_test.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/PrefixesPlugin_RezantsevaAnastasia_FIIT1_ClangAST%pluginext -plugin PrefixesPlugin_RezantsevaAnastasia_FIIT1_ClangAST -fsyntax-only %s 2>&1 | FileCheck --match-full-lines %s + +// CHECK: int global_var1 = 0; +// CHECK-NEXT: int foo(int param_a, int param_b) { +// CHECK-NEXT: static int static_var2 = 0; +// CHECK-NEXT: int local_var3 = 123; +// CHECK-NEXT: ++static_var2; +// CHECK-NEXT: return param_a + param_b + global_var1 + static_var2 + local_var3; +// CHECK-NEXT: } +// CHECK-NEXT: int static global_var4 = 3; +// CHECK-NEXT: int static global_var5 = foo(1, global_var4); + +int var1 = 0; +int foo(int a, int b) { + static int var2 = 0; + int var3 = 123; + ++var2; + return a + b + var1 + var2 + var3; +} +int static var4 = 3; +int static var5 = foo(1, var4); + +// RUN: %clang_cc1 -load %llvmshlibdir/PrefixesPlugin_RezantsevaAnastasia_FIIT1_ClangAST%pluginext -plugin PrefixesPlugin_RezantsevaAnastasia_FIIT1_ClangAST -plugin-arg-PrefixesPlugin_RezantsevaAnastasia_FIIT1_ClangAST --help -fsyntax-only %s 2>&1 | FileCheck --match-full-lines %s --check-prefix=CHECK-HELP +// CHECK-HELP: PrefixesPlugin_RezantsevaAnastasia_FIIT1_ClangAST: +// CHECK-HELP-NEXT: This plugin adds appropriate prefixes to variables and parameters in the code. + \ No newline at end of file From 1d3993c056b43f2d8ebe338e25614e6f45b4f0d8 Mon Sep 17 00:00:00 2001 From: Grudzin Konstantin <113104424+Konstantin-Grudzin@users.noreply.github.com> Date: Tue, 18 Mar 2025 10:36:54 +0300 Subject: [PATCH 21/50] =?UTF-8?q?=D0=93=D1=80=D1=83=D0=B4=D0=B7=D0=B8?= =?UTF-8?q?=D0=BD=20=D0=9A=D0=BE=D0=BD=D1=81=D1=82=D0=B0=D0=BD=D1=82=D0=B8?= =?UTF-8?q?=D0=BD.=20=D0=9B=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=BD=D0=B0=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0?= =?UTF-8?q?=201.=20Clang=20AST.=20=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD?= =?UTF-8?q?=D1=82=202=20(#17)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Данный плагин проходит по параметрам функций и переменным и проверяет, используются ли они. Если нет, то добавляет соответствующий атрибут --------- Co-authored-by: Arseniy Obolenskiy --- .../grudzin_k_unused_vars_ast/CMakeLists.txt | 18 ++++ .../grudzin_k_unused_vars.cpp | 86 +++++++++++++++++++ .../grudzin_k_unused_vars_ast/test.cpp | 23 +++++ .../grudzin_k_unused_vars_ast/test_help.cpp | 3 + 4 files changed, 130 insertions(+) create mode 100644 clang/compiler-course/grudzin_k_unused_vars_ast/CMakeLists.txt create mode 100644 clang/compiler-course/grudzin_k_unused_vars_ast/grudzin_k_unused_vars.cpp create mode 100644 clang/test/compiler-course/grudzin_k_unused_vars_ast/test.cpp create mode 100644 clang/test/compiler-course/grudzin_k_unused_vars_ast/test_help.cpp diff --git a/clang/compiler-course/grudzin_k_unused_vars_ast/CMakeLists.txt b/clang/compiler-course/grudzin_k_unused_vars_ast/CMakeLists.txt new file mode 100644 index 0000000000000..b9068694ea757 --- /dev/null +++ b/clang/compiler-course/grudzin_k_unused_vars_ast/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "UnusedVars") +set(Student "Grudzin_Konstantin") +set(Group "FIIT1") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/grudzin_k_unused_vars_ast/grudzin_k_unused_vars.cpp b/clang/compiler-course/grudzin_k_unused_vars_ast/grudzin_k_unused_vars.cpp new file mode 100644 index 0000000000000..eac3cdadeb905 --- /dev/null +++ b/clang/compiler-course/grudzin_k_unused_vars_ast/grudzin_k_unused_vars.cpp @@ -0,0 +1,86 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/Attr.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/raw_ostream.h" + +namespace { + +class MyUnVarsVisitor final + : public clang::RecursiveASTVisitor { +public: + explicit MyUnVarsVisitor(clang::ASTContext *context, clang::Rewriter &R) + : m_context(context), TheRewriter(R) {} + + bool VisitParamVarDecl(clang::ParmVarDecl *parametr) { + if (!parametr->isUsed() && !parametr->hasAttr()) { + clang::SourceLocation loc = parametr->getSourceRange().getBegin(); + TheRewriter.InsertText(loc, "[[maybe_unused]] ", true, true); + } + return true; + } + + bool VisitVarDecl(clang::VarDecl *variable) { + if (!variable->isUsed() && !variable->hasAttr()) { + clang::SourceLocation loc = variable->getSourceRange().getBegin(); + TheRewriter.InsertText(loc, "[[maybe_unused]] ", true, true); + } + return true; + } + +private: + clang::ASTContext *m_context; + clang::Rewriter &TheRewriter; +}; + +class MyUnVarsConsumer final : public clang::ASTConsumer { +public: + MyUnVarsConsumer(clang::ASTContext *context, clang::Rewriter &R) + : Visitor(context, R) {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + Visitor.TraverseDecl(context.getTranslationUnitDecl()); + } + +private: + MyUnVarsVisitor Visitor; +}; + +class MyUnVarsAction final : public clang::PluginASTAction { +private: + clang::Rewriter TheRewriter; + +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &CI, llvm::StringRef) override { + TheRewriter.setSourceMgr(CI.getSourceManager(), CI.getLangOpts()); + return std::make_unique(&CI.getASTContext(), TheRewriter); + } + + void EndSourceFileAction() override { + TheRewriter.getEditBuffer(TheRewriter.getSourceMgr().getMainFileID()) + .write(llvm::outs()); + } + + bool ParseArgs(const clang::CompilerInstance &CI, + const std::vector &args) override { + for (const auto &arg : args) { + if (arg == "--help") { + // Returning false stops further processing after printing help. + llvm::outs() << "This plugin marks unused variables with " + "[[maybe_unused]] attribute"; + return false; + } + } + return true; + } +}; + +} // namespace + +static clang::FrontendPluginRegistry::Add + X("grudzin_k_UnVars_plugin", + "This plugin marks unused variables by adding attribute and adding " + "[[maybe_unused]] flag in code"); diff --git a/clang/test/compiler-course/grudzin_k_unused_vars_ast/test.cpp b/clang/test/compiler-course/grudzin_k_unused_vars_ast/test.cpp new file mode 100644 index 0000000000000..5b5d5da553601 --- /dev/null +++ b/clang/test/compiler-course/grudzin_k_unused_vars_ast/test.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/UnusedVars_Grudzin_Konstantin_FIIT1_ClangAST%pluginext -plugin grudzin_k_UnVars_plugin -fsyntax-only %s 2>&1 | FileCheck %s + +// CHECK: \[\[maybe_unused\]\] int ntd=-1; +int ntd=-1; +// CHECK: int baz(int p, int q, \[\[maybe_unused\]\] int r) { +// CHECK: int a=5; +// CHECK: int b=17; +// CHECK: \[\[maybe_unused\]\]int c = a+b; +// CHECK: \[\[maybe_unused\]\] int temp = 42; +// CHECK: return p + q; +int baz(int p, int q, int r) { + int a=5; + int b=17; + int c = a+b; + int temp = 42; + return p + q; + } + +// CHECK: void qux(double a, double b, \[\[maybe_unused\]\] double c = 5.0) { +// CHECK: \[\[maybe_unused\]\] double result = a * b; +void qux(double a, double b, double c = 5.0) { + double result = a * b; + } diff --git a/clang/test/compiler-course/grudzin_k_unused_vars_ast/test_help.cpp b/clang/test/compiler-course/grudzin_k_unused_vars_ast/test_help.cpp new file mode 100644 index 0000000000000..522ccdb239d95 --- /dev/null +++ b/clang/test/compiler-course/grudzin_k_unused_vars_ast/test_help.cpp @@ -0,0 +1,3 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/UnusedVars_Grudzin_Konstantin_FIIT1_ClangAST%pluginext -plugin grudzin_k_UnVars_plugin 2>&1 | FileCheck %s --help + +// CHECK:This plugin marks unused variables with \[\[maybe_unused\]\] attribute From 0dc49fa42767703508e7739e15e11afb747a6207 Mon Sep 17 00:00:00 2001 From: Aleksey Baranov Date: Tue, 18 Mar 2025 10:37:13 +0300 Subject: [PATCH 22/50] =?UTF-8?q?=D0=91=D0=B0=D1=80=D0=B0=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9.=20=D0=9B?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD=D0=B0?= =?UTF-8?q?=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=201.Clang=20AST.?= =?UTF-8?q?=20=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=201.=20(#19)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## General Реализован плагин для clang который работает с abstract syntax tree, который строит clang во время frontend-analysis исходного кода. ## Specific Наш плагин умеет различать пользовательские типы данных, анализировать их и принтить их: - Имена - Struct или class - Структуру наследования - Поля - - Модификаторы доступа - - Типы - Методы - -Возвращаемое значение - -Квалификаторы доступа - -Являются ли методы - - - Виртуальными - - - Чисто виртуальными - - - Перегруженными - - - Статическими --------- Co-authored-by: Torcna Co-authored-by: Arseniy Obolenskiy --- .../compiler-course/baranov_a/CMakeLists.txt | 18 ++ .../compiler-course/baranov_a/baranov_ast.cpp | 166 ++++++++++++++++++ .../baranov_a_ast_tests/test.cpp | 124 +++++++++++++ 3 files changed, 308 insertions(+) create mode 100644 clang/compiler-course/baranov_a/CMakeLists.txt create mode 100644 clang/compiler-course/baranov_a/baranov_ast.cpp create mode 100644 clang/test/compiler-course/baranov_a_ast_tests/test.cpp diff --git a/clang/compiler-course/baranov_a/CMakeLists.txt b/clang/compiler-course/baranov_a/CMakeLists.txt new file mode 100644 index 0000000000000..96b336719c35e --- /dev/null +++ b/clang/compiler-course/baranov_a/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "DataTypes") +set(Student "BARANOV_ALEKSEY") +set(Group "FIIT1") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/baranov_a/baranov_ast.cpp b/clang/compiler-course/baranov_a/baranov_ast.cpp new file mode 100644 index 0000000000000..f3768365010d2 --- /dev/null +++ b/clang/compiler-course/baranov_a/baranov_ast.cpp @@ -0,0 +1,166 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +namespace baranov_ast_lab_1 { +std::string getQualifier(clang::AccessSpecifier temp) { + if (temp == clang::AS_public) + return "public"; + else if (temp == clang::AS_private) + return "private"; + else if (temp == clang::AS_protected) + return "protected"; + return ""; +} + +bool hasAnyField(const clang::CXXRecordDecl *recordDecl) { + for (const auto *Decl : recordDecl->decls()) { + if (llvm::isa(Decl)) + return true; + if (const auto *Var = llvm::dyn_cast(Decl)) { + return true; + } + } + return false; +} +void handleFields(const clang::CXXRecordDecl *recordDecl, + llvm::raw_ostream &output) { + for (const auto *Decl : recordDecl->decls()) { + if (const auto *Field = llvm::dyn_cast(Decl)) { + output << "| |_ " << Field->getName() << " (" + << Field->getType().getAsString() << "|" + << getQualifier(Field->getAccess()) << ")\n"; + } else if (const auto *Var = llvm::dyn_cast(Decl)) { + std::string typeStr = Var->getType().getAsString(); + output << "| |_ " << Var->getName() << " (" << typeStr; + if (Var->isStaticDataMember()) + output << "|static"; + + output << "|" << getQualifier(Var->getAccess()); + + output << ")\n"; + } + } +} + +void handleMethods(const clang::CXXRecordDecl *recordDecl, + llvm::raw_ostream &output) { + if (std::none_of(recordDecl->method_begin(), recordDecl->method_end(), + [](const clang::CXXMethodDecl *method) { + return !method->isImplicit(); + })) { + output << "| |_ (has no methods)\n"; + return; + } + + for (auto &&method : recordDecl->methods()) { + if (method->isImplicit()) + continue; + + output << "| |_ " << method->getNameAsString() << " (" + << method->getReturnType().getAsString(); + output << "("; + llvm::interleaveComma(method->parameters(), output, + [](const clang::ParmVarDecl *param) { + llvm::outs() << param->getType().getAsString(); + } + + ); + output << ")"; + if (method->isStatic()) + output << "|static"; + output << "|" << getQualifier(method->getAccess()); + if (recordDecl && + std::any_of(recordDecl->friends().begin(), recordDecl->friends().end(), + [method](const clang::FriendDecl *friendDecl) { + if (const auto *FD = friendDecl->getFriendDecl()) + return FD == method; + return false; + })) + output << "|friend"; + if (method->hasAttr()) + output << "|override"; + else if (method->isVirtual()) + output << (method->isPureVirtual() ? "|virtual|pure" : "|virtual"); + + output << ")\n"; + } +} + +class DataTypesVisitor final + : public clang::RecursiveASTVisitor { +public: + explicit DataTypesVisitor(clang::ASTContext *context) : m_context_(context) {} + bool VisitCXXRecordDecl(clang::CXXRecordDecl *recordDecl) { + auto &&output = llvm::outs(); + // is union + if (recordDecl->isUnion()) { + output << recordDecl->getNameAsString() << "(union)\n"; + } else { + // is class or whatever + output << recordDecl->getNameAsString(); + if (recordDecl->getDescribedClassTemplate()) { + output << "(" << (recordDecl->isStruct() ? "struct" : "class") + << "|template)"; + } else { + output << "(" << (recordDecl->isStruct() ? "struct" : "class") << ")"; + } + + if (recordDecl->getNumBases()) { + output << " -> "; + llvm::interleaveComma(recordDecl->bases(), output, + [&](const clang::CXXBaseSpecifier &base) { + output + << getQualifier(base.getAccessSpecifier()) + << " " << base.getType().getAsString(); + }); + } + output << "\n"; + } + output << "|_Fields\n"; + if (!hasAnyField(recordDecl)) { + output << "| |_ (has no fields)\n"; + } else { + handleFields(recordDecl, output); + } + + output << "|_Methods\n"; + handleMethods(recordDecl, output); + + return true; + } + +private: + clang::ASTContext *m_context_; +}; + +class DataTypesConsumer final : public clang::ASTConsumer { +public: + explicit DataTypesConsumer(clang::ASTContext *context) : visitor_(context) {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + visitor_.TraverseDecl(context.getTranslationUnitDecl()); + } + +private: + DataTypesVisitor visitor_; +}; +class DataTypesAction final : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + return std::make_unique(&ci.getASTContext()); + } + bool ParseArgs(const clang::CompilerInstance &ci, + const std::vector &args) override { + return true; + } +}; +} // namespace baranov_ast_lab_1 + +static clang::FrontendPluginRegistry::Add + X("BaranovDataPlugin", "Traverses data types, print info and qualifiers"); diff --git a/clang/test/compiler-course/baranov_a_ast_tests/test.cpp b/clang/test/compiler-course/baranov_a_ast_tests/test.cpp new file mode 100644 index 0000000000000..3b8f8ecfa8630 --- /dev/null +++ b/clang/test/compiler-course/baranov_a_ast_tests/test.cpp @@ -0,0 +1,124 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/DataTypes_BARANOV_ALEKSEY_FIIT1_ClangAST%pluginext -plugin BaranovDataPlugin -fsyntax-only %s 2>&1 | FileCheck %s + +// CHECK: myClass(class|template) +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ x (T|private) +// CHECK-NEXT: | |_ inner (int|private) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ (has no methods) + +template class myClass { + T x; + int inner; +}; +// CHECK: A(struct) +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ (has no fields) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ (has no methods) +struct A {}; +// CHECK: B(struct) +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ (has no fields) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ (has no methods) +struct B {}; + +// CHECK: C(class) -> public A, public B +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ (has no fields) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ (has no methods) +class C : public A, public B {}; + +// CHECK: myClassBase(class) +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ (has no fields) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ (has no methods) +class myClassBase {}; + +// CHECK: myClassPubl(class) -> public myClassBase +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ (has no fields) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ (has no methods) +class myClassPubl : public myClassBase {}; + +// CHECK: myClassPriv(class) -> private myClassBase +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ (has no fields) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ (has no methods) +class myClassPriv : private myClassBase {}; + +// CHECK: publPriv(struct) +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ i (long long|public) +// CHECK-NEXT: | |_ x (float|private) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ (has no methods) +struct publPriv { + long long i; + +private: + float x; +}; + +// CHECK: Person(struct) +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ age (unsigned int|public) +// CHECK-NEXT: | |_ height (unsigned int|public) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ sleep (void()|public|virtual|pure) +// CHECK-NEXT: | |_ eat (void()|public|virtual|pure) +struct Person { + unsigned age; + unsigned height; + + virtual void sleep() = 0; + virtual void eat() = 0; +}; + +// CHECK: twoTemplates(class|template) +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ first (T|private) +// CHECK-NEXT: | |_ second (U|private) +template class twoTemplates { + T first; + U second; +}; + +// reviewers request + +struct vi { + virtual void foo() = 0; +}; +// CHECK: overrideCheck(class) -> public vi +// CHECK: |_Methods +// CHECK-NEXT: | |_ foo (void()|private|override) +class overrideCheck : public vi { + void foo() override; +}; + +// CHECK: staticCheck(struct) +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ r (long long|static|public) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ bbbb (int(double)|static|public) +struct staticCheck { + static long long int r; + static int bbbb(double arg); +}; + +// CHECK: E(union) +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ F (int|public) +// CHECK-NEXT: | |_ I (float|public) +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ t (float(int, double *)|static|public) +union E { + static float t(int i, double *d); + int F; + float I; +}; From 0af8b4530e37fcb07aa80c6170ae8235254bff5f Mon Sep 17 00:00:00 2001 From: Arsenii Mironov <98156294+Napkin-AI@users.noreply.github.com> Date: Tue, 18 Mar 2025 11:37:34 +0400 Subject: [PATCH 23/50] =?UTF-8?q?=D0=9C=D0=B8=D1=80=D0=BE=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=90=D1=80=D1=81=D0=B5=D0=BD=D0=B8=D0=B9.=203822?= =?UTF-8?q?=D0=911=D0=A4=D0=981.=20=D0=9B=D0=B0=D0=B1=D0=BE=D1=80=D0=B0?= =?UTF-8?q?=D1=82=D0=BE=D1=80=D0=BD=D0=B0=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=B0=201.=20=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82?= =?UTF-8?q?=203.=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Реализация плагина Clang, анализирующий исходный код C++ и подсчитывает количество неявных преобразований типов (implicit casts) в функциях. Он использует Clang AST (Abstract Syntax Tree) для обхода узлов и сбора статистики. Не учитываются неявные преобразования укзателей функций. Для тестирования плагина были написаны lit тесты. --------- Co-authored-by: Arseniy <> --- .../mironov_a_lab1/CMakeLists.txt | 18 +++ .../compiler-course/mironov_a_lab1/source.cpp | 108 ++++++++++++++++++ .../compiler-course/mironov_a_lab1/test.cpp | 84 ++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 clang/compiler-course/mironov_a_lab1/CMakeLists.txt create mode 100644 clang/compiler-course/mironov_a_lab1/source.cpp create mode 100644 clang/test/compiler-course/mironov_a_lab1/test.cpp diff --git a/clang/compiler-course/mironov_a_lab1/CMakeLists.txt b/clang/compiler-course/mironov_a_lab1/CMakeLists.txt new file mode 100644 index 0000000000000..d2df09e091b55 --- /dev/null +++ b/clang/compiler-course/mironov_a_lab1/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "Lab1_AST") +set(Student "Mironov_Arseniy") +set(Group "FIIT1") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/mironov_a_lab1/source.cpp b/clang/compiler-course/mironov_a_lab1/source.cpp new file mode 100644 index 0000000000000..0bf14008e4a04 --- /dev/null +++ b/clang/compiler-course/mironov_a_lab1/source.cpp @@ -0,0 +1,108 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include + +namespace { +class CastVisitor final : public clang::RecursiveASTVisitor { +public: + CastVisitor() {} + + bool VisitFunctionDecl(clang::FunctionDecl *function) { + scope_name_ = function->getNameAsString(); + return true; + } + + bool VisitImplicitCastExpr(clang::ImplicitCastExpr *cast) { + + clang::CastKind cast_kind = cast->getCastKind(); + + if (cast_kind == clang::CK_FunctionToPointerDecay || + cast_kind == clang::CK_NoOp || cast_kind == clang::CK_LValueToRValue) { + return true; + } + + // CanonicalType needs not to use any type qualifiers + std::string source_type = + cast->getSubExpr()->getType().getCanonicalType().getAsString(); + std::string target_type = cast->getType().getCanonicalType().getAsString(); + + // needs if cast int->int (it is possible cuz type qualifiers); + if (source_type == target_type) { + return true; + } + + casts_[scope_name_][std::make_pair(source_type, target_type)]++; + return true; + } + + bool VisitCXXConstructExpr(clang::CXXConstructExpr *construct) { + + std::string target_type = + construct->getType().getCanonicalType().getAsString(); + + for (const auto *arg : construct->arguments()) { + std::string source_type = arg->getType().getCanonicalType().getAsString(); + + casts_[scope_name_][std::make_pair(source_type, target_type)]++; + } + return true; + } + + void Results() { + for (const auto &[func, table] : casts_) { + if (func == "global_scope") { + llvm::outs() << "In global scope\n"; + } else { + llvm::outs() << "In function: " << func << '\n'; + } + for (const auto &[types, value] : table) { + llvm::outs() << types.first << " -> " << types.second << ": " << value + << '\n'; + } + } + } + +private: + // table: function: {type1, type2}: a number of implicit casts + std::map, int>> + casts_; + std::string scope_name_ = "global_scope"; +}; + +class CastConsumer final : public clang::ASTConsumer { +public: + explicit CastConsumer() {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + visitor_.TraverseDecl(context.getTranslationUnitDecl()); + visitor_.Results(); + } + +private: + CastVisitor visitor_; +}; + +class CastAction final : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + return std::make_unique(); + } + + bool ParseArgs(const clang::CompilerInstance &ci, + const std::vector &args) override { + return true; + } +}; + +} // namespace + +static clang::FrontendPluginRegistry::Add + X("Lab1_AST_Mironov_Arseniy_FIIT1_ClangAST", + "The plugin counts the number of implicit conversations and returns it"); diff --git a/clang/test/compiler-course/mironov_a_lab1/test.cpp b/clang/test/compiler-course/mironov_a_lab1/test.cpp new file mode 100644 index 0000000000000..52442821ecb3b --- /dev/null +++ b/clang/test/compiler-course/mironov_a_lab1/test.cpp @@ -0,0 +1,84 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/Lab1_AST_Mironov_Arseniy_FIIT1_ClangAST%pluginext -plugin Lab1_AST_Mironov_Arseniy_FIIT1_ClangAST -fsyntax-only %s 2>&1 | FileCheck %s -dump-input=always + +// CHECK: In global scope +// CHECK-NEXT: int -> float: 1 +float global_scope_test = 4; + + +// CHECK: In function: mul +// CHECK-NEXT: double -> int: 1 +// CHECK-NEXT: float -> double: 1 +// CHECK-NEXT: float -> int: 1 + +// CHECK: In function: sum +// CHECK-NEXT: float -> double: 1 +// CHECK-NEXT: int -> float: 1 + +double sum(int a, float b) { + return a + b; +} + +int mul(float a, float b) { + return a + sum(a, b); +} + +// CHECK: In function: test_casts1 +// CHECK-NEXT: double -> _Bool +// CHECK-NEXT: double -> float +// CHECK-NEXT: double -> int +// CHECK-NEXT: int -> _Bool: 1 +// CHECK-NEXT: int -> double: 1 + +void test_casts1(){ + bool f1 = 1; + bool f2 = 1.0; + int x = 1.0; + float y = 1.0; + double c = 1; +} + +// CHECK: In function: test_casts2 +// CHECK-NEXT: _Bool -> char: 1 +// CHECK-NEXT: _Bool -> double: 2 +// CHECK-NEXT: _Bool -> float: 1 +// CHECK-NEXT: _Bool -> int: 1 + +void test_casts2(){ + int c = true; + double c1 = true; + double c2 = false; + float c3 = false; + char c4= true; +} + +class X{ +private: + int created; +public: + X(int val): created(val) {} +}; + +// CHECK: In function: test_casts3 +// CHECK-NEXT: double -> int: 1 +// CHECK-NEXT: int -> class X: 1 + +void test_casts3(){ + X sample(1.0); +} + +// CHECK: In function: test_casts4 +// CHECK-NEXT: _Bool -> int: 1 +// CHECK-NEXT: int -> class X: 1 + +X test_casts4(){ + return true; +} + +// CHECK: In function: test_casts5 +// CHECK-NEXT: int * -> void *: 1 + +void test_casts5(){ + int obj = 5; + int *ptr = &obj; + void* cp = ptr; +} From c6312a96dab8b00eeb05bdd90ed4ef5f89515658 Mon Sep 17 00:00:00 2001 From: Irina2004-tech <111091810+Irina2004-tech@users.noreply.github.com> Date: Wed, 19 Mar 2025 17:19:46 +0300 Subject: [PATCH 24/50] =?UTF-8?q?Revert=20"=D0=9A=D1=83=D0=B4=D1=80=D1=8F?= =?UTF-8?q?=D1=88=D0=BE=D0=B2=D0=B0=20=D0=98=D1=80=D0=B8=D0=BD=D0=B0.=20?= =?UTF-8?q?=D0=9B=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=201:=20Clan?= =?UTF-8?q?g=20AST.=20=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=203.=20(#?= =?UTF-8?q?36)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Плагин обходит абстрактное синтаксическое дерево (AST) программы, используя механизм `RecursiveASTVisitor`. Класс `ImplicitConvVisitor` перехватывает узлы `ImplicitCastExpr`, соответствующие неявным преобразованиям. Игнорируются преобразования, не влияющие на семантику такие как `LValueToRValue`, `NoOp`, `FunctionToPointerDecay`. Для каждого преобразования извлекаются исходный и целевой типы, а также контекст — функция, где оно произошло. Статистика сохраняется в виде отображения `[функция -> (тип_источник -> тип_цель) -> количество]`. После обхода AST результаты выводятся в стандартный поток ошибок, включая общее число преобразований. revert: https://github.com/NN-complr-tech/compiler-course-2025/pull/29 Была проведена замена std::map на llvm:MapVector, так как llvm::MapVector сохраняет порядок вставки функций в AST. Это гарантирует, что при выводе функции будут перечислены в том же порядке, в котором они встречались в исходном коде, что исключит дальнейшее появление ошибок --- .../CMakeLists.txt | 18 +++ .../kudryashova_i_clangast_plugin.cpp | 129 ++++++++++++++++++ .../kudryashova_i_clangast_test.cpp | 52 +++++++ 3 files changed, 199 insertions(+) create mode 100644 clang/compiler-course/kudryashova_i_compilers_clangast/CMakeLists.txt create mode 100644 clang/compiler-course/kudryashova_i_compilers_clangast/kudryashova_i_clangast_plugin.cpp create mode 100644 clang/test/compiler-course/kudryashova_i_test_clangast/kudryashova_i_clangast_test.cpp diff --git a/clang/compiler-course/kudryashova_i_compilers_clangast/CMakeLists.txt b/clang/compiler-course/kudryashova_i_compilers_clangast/CMakeLists.txt new file mode 100644 index 0000000000000..978ec399ea946 --- /dev/null +++ b/clang/compiler-course/kudryashova_i_compilers_clangast/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "Implicit_Conv") +set(Student "Kudryashova_Irina") +set(Group "FIIT3") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/kudryashova_i_compilers_clangast/kudryashova_i_clangast_plugin.cpp b/clang/compiler-course/kudryashova_i_compilers_clangast/kudryashova_i_clangast_plugin.cpp new file mode 100644 index 0000000000000..beec55923c46d --- /dev/null +++ b/clang/compiler-course/kudryashova_i_compilers_clangast/kudryashova_i_clangast_plugin.cpp @@ -0,0 +1,129 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ParentMapContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/Support/raw_ostream.h" + +namespace { + +class ImplicitConvVisitor + : public clang::RecursiveASTVisitor { + +private: + clang::ASTContext *m_context; + llvm::MapVector, int>> + m_functionStats; + int m_totalConversions = 0; + +public: + explicit ImplicitConvVisitor(clang::ASTContext *context) + : m_context(context) {} + + bool VisitImplicitCastExpr(clang::ImplicitCastExpr *ICE) { + + auto castKind = ICE->getCastKind(); + if (castKind == clang::CK_LValueToRValue || castKind == clang::CK_NoOp || + castKind == clang::CK_FunctionToPointerDecay) { + return true; + } + clang::QualType SourceType = ICE->getSubExpr()->getType(); + clang::QualType TargetType = ICE->getType(); + + if (SourceType.getCanonicalType() == TargetType.getCanonicalType()) { + return true; + } + + auto Parents = m_context->getParents(*ICE); + while (!Parents.empty()) { + if (const auto *FD = Parents[0].get()) { + recordConversion(FD, SourceType, TargetType); + break; + } else if (const auto *Lambda = Parents[0].get()) { + if (const auto *CallOp = Lambda->getCallOperator()) { + recordConversion(CallOp, SourceType, TargetType); + break; + } + } else if (const auto *ME = Parents[0].get()) { + recordConversion(ME, SourceType, TargetType); + break; + } + Parents = m_context->getParents(Parents[0]); + } + return true; + } + + std::string normalizeTypeName(std::string typeName) { + const std::vector prefixes = {"struct ", "class ", "enum "}; + for (const auto &prefix : prefixes) { + size_t pos = typeName.find(prefix); + if (pos == 0) { + typeName.erase(0, prefix.length()); + break; + } + } + typeName.erase( + std::remove_if(typeName.begin(), typeName.end(), + [](unsigned char c) { return std::isspace(c); }), + typeName.end()); + size_t pos; + while ((pos = typeName.find("_Bool")) != std::string::npos) { + typeName.replace(pos, 5, "bool"); + } + return typeName; + } + + void recordConversion(const clang::FunctionDecl *FD, clang::QualType From, + clang::QualType To) { + std::string FromStr = + normalizeTypeName(From.getCanonicalType().getAsString()); + std::string ToStr = normalizeTypeName(To.getCanonicalType().getAsString()); + m_functionStats[FD][std::make_pair(FromStr, ToStr)]++; + m_totalConversions++; + } + + void printStats(llvm::raw_ostream &OS) { + for (const auto &[func, convs] : m_functionStats) { + OS << "Function `" << func->getName() << "`\n"; + for (const auto &[conv, num] : convs) { + OS << conv.first << " -> " << conv.second << ": " << num << "\n"; + } + } + OS << "Total implicit conversions: " << m_totalConversions << "\n"; + } +}; + +class ImplicitConvConsumer : public clang::ASTConsumer { + +private: + ImplicitConvVisitor m_visitor; + +public: + explicit ImplicitConvConsumer(clang::ASTContext *context) + : m_visitor(context) {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + m_visitor.TraverseDecl(context.getTranslationUnitDecl()); + m_visitor.printStats(llvm::errs()); + } +}; + +class ImplicitConvAction : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + return std::make_unique(&ci.getASTContext()); + } + + bool ParseArgs(const clang::CompilerInstance &ci, + const std::vector &args) override { + return true; + } +}; + +} // namespace + +static clang::FrontendPluginRegistry::Add + X("ImplicitConvPlugin", "Count implicit type conversions"); diff --git a/clang/test/compiler-course/kudryashova_i_test_clangast/kudryashova_i_clangast_test.cpp b/clang/test/compiler-course/kudryashova_i_test_clangast/kudryashova_i_clangast_test.cpp new file mode 100644 index 0000000000000..2cc026d98dcde --- /dev/null +++ b/clang/test/compiler-course/kudryashova_i_test_clangast/kudryashova_i_clangast_test.cpp @@ -0,0 +1,52 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/Implicit_Conv_Kudryashova_Irina_FIIT3_ClangAST%pluginext -plugin ImplicitConvPlugin %s -fsyntax-only 2>&1 | FileCheck %s + +// CHECK: Function `sum` +// CHECK-NEXT: float -> double: 1 +// CHECK-NEXT: int -> float: 1 + +double sum(int a, float b) { + return a + b; +} + +// CHECK-NEXT: Function `mul` +// CHECK-NEXT: double -> int: 1 +// CHECK-NEXT: float -> double: 1 +// CHECK-NEXT: float -> int: 1 + +int mul(float a, float b) { + return a + sum(a, b); +} + +// CHECK: Function `foo` +// CHECK-NEXT: float -> int: 1 +// CHECK-NEXT: int -> float: 1 + +using Abrakadabra = float; +using Boom = int; + +void foo() { + Abrakadabra x = Boom(); + Boom y = x; +} + +// CHECK: Function `moo` +// CHECK-NEXT: int* -> void*: 1 +void moo() { + int x; + void* vp = &x; +} + +// CHECK: Function `goo` +// CHECK-NEXT: int* -> bool: 1 +void goo(int* p) { + if (p) {} +} + +// CHECK: Function `boo` +// CHECK-NEXT: int -> bool: 1 +void boo() { + int x = 7; + bool b = x; +} + +// CHECK: Total implicit conversions: 10 From bc5f22ee79631f508c9c533edab5e316adc518a6 Mon Sep 17 00:00:00 2001 From: m1likus <113242173+m1likus@users.noreply.github.com> Date: Wed, 19 Mar 2025 18:10:10 +0300 Subject: [PATCH 25/50] [CI] Fix workflow stop on new commit (#49) --- .github/workflows/compiler-course-build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/compiler-course-build.yml b/.github/workflows/compiler-course-build.yml index e0d32823ada70..2fc5f2c7fa4e2 100644 --- a/.github/workflows/compiler-course-build.yml +++ b/.github/workflows/compiler-course-build.yml @@ -1,6 +1,10 @@ name: Build LLVM on: [push, pull_request] +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: ${{ github.ref != 'refs/heads/course-spring-2025' && github.event_name != 'merge_group' && !startsWith(github.ref, 'refs/heads/gh-readonly-queue') }} + jobs: ubuntu-gcc-build: runs-on: ubuntu-latest From 68a3fb6d98c3ab315b0f11acc578d92e5a3de253 Mon Sep 17 00:00:00 2001 From: Arseniy Obolenskiy Date: Wed, 19 Mar 2025 16:11:28 +0100 Subject: [PATCH 26/50] [CI] Normalize cache (#48) --- .github/workflows/compiler-course-build.yml | 10 ++++++---- .github/workflows/compiler-course-tidy.yml | 7 +++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/compiler-course-build.yml b/.github/workflows/compiler-course-build.yml index 2fc5f2c7fa4e2..0454ac74c43da 100644 --- a/.github/workflows/compiler-course-build.yml +++ b/.github/workflows/compiler-course-build.yml @@ -23,8 +23,9 @@ jobs: - name: Setup ccache uses: hendrikmuhs/ccache-action@v1.2 with: - max-size: 500M - key: ccache-${{ github.job }} + key: ${{ runner.os }} + create-symlink: true + max-size: 1.5G - name: Build run: | cmake -S llvm -B build \ @@ -58,8 +59,9 @@ jobs: - name: Setup ccache uses: hendrikmuhs/ccache-action@v1.2 with: - max-size: 500M - key: ccache-${{ github.job }} + key: ${{ runner.os }} + create-symlink: true + max-size: 1.5G - name: Build run: | cmake -S llvm -B build \ diff --git a/.github/workflows/compiler-course-tidy.yml b/.github/workflows/compiler-course-tidy.yml index 1c236234c840a..712e9bfb9744f 100644 --- a/.github/workflows/compiler-course-tidy.yml +++ b/.github/workflows/compiler-course-tidy.yml @@ -23,10 +23,13 @@ jobs: submodules: recursive - name: Update submodules run: git submodule update --init --recursive - - name: ccache + + - name: Setup ccache uses: hendrikmuhs/ccache-action@v1.2 with: - key: ${{ github.job }} + key: ${{ runner.os }} + create-symlink: true + max-size: 1.5G - uses: aobolensk/clang-tidy-review@v0.20.1-patch-clang-tidy-19 id: review with: From b7c6fe09444e8bf2e199727690ee89727604e3e4 Mon Sep 17 00:00:00 2001 From: Alexey-Solovev <149035736+Alexey-Solovev@users.noreply.github.com> Date: Thu, 20 Mar 2025 16:06:03 +0300 Subject: [PATCH 27/50] =?UTF-8?q?=D0=A1=D0=BE=D0=BB=D0=BE=D0=B2=D1=8C?= =?UTF-8?q?=D0=B5=D0=B2=20=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9.=20?= =?UTF-8?q?=D0=9B=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=201:=20Clan?= =?UTF-8?q?g=20AST.=20=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=204.=20(#?= =?UTF-8?q?32)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Реализован Clang-плагин, который автоматически добавляет префиксы к именам переменных в исходном коде в зависимости от их области видимости. Используя RecursiveASTVisitor, он обходит AST, находит объявления переменных (VarDecl, ParmVarDecl) и ссылки на них (DeclRefExpr). Метод determinePrefix определяет нужный префикс: global_, local_, static_, param_. Rewriter переименовывает переменные в исходном коде. PrefixConsumer запускает обход AST, а PrefixAction инициализирует Rewriter и записывает изменения в выходной поток. --------- Co-authored-by: Alexey-Solovev Co-authored-by: Kuznetsov-Artyom <110616662+Kuznetsov-Artyom@users.noreply.github.com> --- .../compiler-course/solovev_a/CMakeLists.txt | 18 +++ clang/compiler-course/solovev_a/solovev_a.cpp | 111 ++++++++++++++++++ .../solovev_a_test/solovev_a_test.cpp | 45 +++++++ 3 files changed, 174 insertions(+) create mode 100644 clang/compiler-course/solovev_a/CMakeLists.txt create mode 100644 clang/compiler-course/solovev_a/solovev_a.cpp create mode 100644 clang/test/compiler-course/solovev_a_test/solovev_a_test.cpp diff --git a/clang/compiler-course/solovev_a/CMakeLists.txt b/clang/compiler-course/solovev_a/CMakeLists.txt new file mode 100644 index 0000000000000..01dba9da886bc --- /dev/null +++ b/clang/compiler-course/solovev_a/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "ClangAST_1") +set(Student "Solovev_a") +set(Group "FIIT1") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/solovev_a/solovev_a.cpp b/clang/compiler-course/solovev_a/solovev_a.cpp new file mode 100644 index 0000000000000..428bfa0037f64 --- /dev/null +++ b/clang/compiler-course/solovev_a/solovev_a.cpp @@ -0,0 +1,111 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +namespace { + +class PrefixVariableNames + : public clang::RecursiveASTVisitor { +public: + explicit PrefixVariableNames(clang::Rewriter &rewriter) + : rewriter_(rewriter) {} + + bool VisitVarDecl(clang::VarDecl *varDecl) { + if (!varDecl->getName().empty()) { + std::string prefix = determinePrefix(varDecl); + if (!prefix.empty() && varDecl->getName().str().find(prefix) != 0) { + std::string name = varDecl->getName().str(); + std::string mod_name = prefix + name; + renamedVariables_[name] = mod_name; + rewriter_.ReplaceText(varDecl->getLocation(), name.length(), mod_name); + } + } + return true; + } + + bool VisitParmVarDecl(clang::ParmVarDecl *paramVarDecl) { + if (!paramVarDecl->getName().empty()) { + std::string name = paramVarDecl->getName().str(); + std::string mod_name = "param_" + name; + renamedVariables_[name] = mod_name; + rewriter_.ReplaceText(paramVarDecl->getLocation(), name.length(), + mod_name); + } + return true; + } + + bool VisitDeclRefExpr(clang::DeclRefExpr *expr) { + auto decl = expr->getDecl(); + if (decl) { + std::string name = decl->getName().str(); + auto it = renamedVariables_.find(name); + if (it != renamedVariables_.end()) { + rewriter_.ReplaceText(expr->getLocation(), name.length(), it->second); + } + } + return true; + } + +private: + std::string determinePrefix(clang::VarDecl *varDecl) { + if (varDecl->isStaticLocal()) { + return "static_"; + } else if (varDecl->isLocalVarDecl()) { + return "local_"; + } else if (varDecl->hasGlobalStorage()) { + if (varDecl->getStorageClass() == clang::SC_Static) { + return "static_global_"; + } + return "global_"; + } + return ""; + } + + clang::Rewriter &rewriter_; + std::unordered_map renamedVariables_; +}; + +class PrefixConsumer : public clang::ASTConsumer { +public: + explicit PrefixConsumer(clang::Rewriter &rewriter) : visitor_(rewriter) {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + visitor_.TraverseDecl(context.getTranslationUnitDecl()); + } + +private: + PrefixVariableNames visitor_; +}; + +class PrefixAction : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + rewriter_.setSourceMgr(ci.getSourceManager(), ci.getLangOpts()); + return std::make_unique(rewriter_); + } + + bool ParseArgs(const clang::CompilerInstance &, + const std::vector &) override { + return true; + } + + void EndSourceFileAction() override { + rewriter_.getEditBuffer(rewriter_.getSourceMgr().getMainFileID()) + .write(llvm::outs()); + } + +private: + clang::Rewriter rewriter_; +}; + +} // namespace + +static clang::FrontendPluginRegistry::Add + X("ClangAST_1_Solovev_a_FIIT1_ClangAST", + "Prefixes variables based on type"); diff --git a/clang/test/compiler-course/solovev_a_test/solovev_a_test.cpp b/clang/test/compiler-course/solovev_a_test/solovev_a_test.cpp new file mode 100644 index 0000000000000..6cc3ed0247b48 --- /dev/null +++ b/clang/test/compiler-course/solovev_a_test/solovev_a_test.cpp @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/ClangAST_1_Solovev_a_FIIT1_ClangAST%pluginext -plugin ClangAST_1_Solovev_a_FIIT1_ClangAST -fsyntax-only %s 2>&1 | FileCheck --match-full-lines %s + +// CHECK: int global_a = 0; +// CHECK-NEXT: static int static_global_b = 3; +// CHECK-NEXT: int sum(int param_a, int param_b) { +// CHECK-NEXT: static int static_d = param_a + param_b; +// CHECK-NEXT: return static_d; +// CHECK-NEXT: } +// CHECK-NEXT: int minus(int param_c) { +// CHECK-NEXT: int local_d = -param_c; +// CHECK-NEXT: return local_d; +// CHECK-NEXT: } +// CHECK-NEXT: void func_1(int param_val_1, int param_val_2, int param_val_3) { +// CHECK-NEXT: int local_mult = param_val_1 * param_val_2; +// CHECK-NEXT: for (int local_i = 0; local_i < local_mult; local_i++) { +// CHECK-NEXT: param_val_3 += local_i; +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: void func_2(int param_val_4, int param_val_5) { +// CHECK-NEXT: static int static_x = param_val_4 / param_val_5; +// CHECK-NEXT: static int static_y = param_val_5 / param_val_4; +// CHECK-NEXT: int local_res = static_x + static_y; +// CHECK-NEXT: } + +int a = 0; +static int b = 3; +int sum(int a, int b) { + static int d = a + b; + return d; +} +int minus(int c) { + int d = -c; + return d; +} +void func_1(int val_1, int val_2, int val_3) { + int mult = val_1 * val_2; + for (int i = 0; i < mult; i++) { + val_3 += i; + } +} +void func_2(int val_4, int val_5) { + static int x = val_4 / val_5; + static int y = val_5 / val_4; + int res = x + y; +} From 388e5c9a214ede4c55ee0bacd7e6dda084f0b6a8 Mon Sep 17 00:00:00 2001 From: KatyaKozlova <113104616+KatyaKozlova@users.noreply.github.com> Date: Thu, 20 Mar 2025 16:06:21 +0300 Subject: [PATCH 28/50] =?UTF-8?q?=D0=9A=D0=BE=D0=B7=D0=BB=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=20=D0=95=D0=BA=D0=B0=D1=82=D0=B5=D1=80=D0=B8=D0=BD=D0=B0?= =?UTF-8?q?.=20=D0=9B=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=BD=D0=B0=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=201.?= =?UTF-8?q?=20Clang=20AST.=20=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=20?= =?UTF-8?q?4.=20(#33)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Плагин использует возможности Clang для обхода абстрактного синтаксического дерева (AST) и автоматического изменения исходного кода на основе анализа структуры программы. Префиксы добавляются в процессе обхода AST с помощью класса ExampleVisitor, который проверяет тип и область видимости переменной. В зависимости от того, является ли переменная локальной, статической, глобальной или параметром функции, к её имени добавляется соответствующий префикс (local_, static_, global_, param_). Изменения выполняются через clang::Rewriter, который заменяет старые имена на новые в исходном коде. --- .../kozlova_e_variant_4/CMakeLists.txt | 18 +++ .../kozlova_e_variant_4/kozlova_e_var_4.cpp | 143 ++++++++++++++++++ .../kozlova_e_var_4_test/test.cpp | 33 ++++ 3 files changed, 194 insertions(+) create mode 100644 clang/compiler-course/kozlova_e_variant_4/CMakeLists.txt create mode 100644 clang/compiler-course/kozlova_e_variant_4/kozlova_e_var_4.cpp create mode 100644 clang/test/compiler-course/kozlova_e_var_4_test/test.cpp diff --git a/clang/compiler-course/kozlova_e_variant_4/CMakeLists.txt b/clang/compiler-course/kozlova_e_variant_4/CMakeLists.txt new file mode 100644 index 0000000000000..50fb18da5a766 --- /dev/null +++ b/clang/compiler-course/kozlova_e_variant_4/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "FirstPlugin") +set(Student "KozlovaEkaterina") +set(Group "FIIT3") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/kozlova_e_variant_4/kozlova_e_var_4.cpp b/clang/compiler-course/kozlova_e_variant_4/kozlova_e_var_4.cpp new file mode 100644 index 0000000000000..90d1751797c03 --- /dev/null +++ b/clang/compiler-course/kozlova_e_variant_4/kozlova_e_var_4.cpp @@ -0,0 +1,143 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/raw_ostream.h" + +namespace { + +class ExampleVisitor : public clang::RecursiveASTVisitor { +public: + explicit ExampleVisitor(clang::ASTContext *context, clang::Rewriter &rewriter) + : m_rewriter(rewriter) {} + + bool VisitVarDecl(clang::VarDecl *var) { + if (var->isLocalVarDecl()) { + if (var->isStaticLocal()) { + renameVar(var, "static_"); + } else { + renameVar(var, "local_"); + } + } else if (var->isFileVarDecl()) { + if (var->getStorageClass() == clang::SC_Static) { + renameVar(var, "static_global_"); + } else { + renameVar(var, "global_"); + } + } + return true; + } + + bool VisitParmVarDecl(clang::ParmVarDecl *param) { + llvm::StringRef oldName = param->getName(); + if (oldName.empty()) + return true; + + std::string newName = "param_" + oldName.str(); + std::string defaultValueStr; + if (param->hasDefaultArg()) { + clang::Expr *defaultArg = param->getDefaultArg(); + llvm::raw_string_ostream stream(defaultValueStr); + defaultArg->printPretty(stream, nullptr, + clang::PrintingPolicy(clang::LangOptions())); + stream.flush(); + newName += "_default_" + defaultValueStr; + clang::SourceLocation loc = param->getLocation(); + if (loc.isValid() && m_rewriter.getSourceMgr().isWrittenInMainFile(loc)) { + m_rewriter.ReplaceText(loc, oldName.size() + defaultValueStr.size() + 3, + newName); + } + } else { + clang::SourceLocation loc = param->getLocation(); + if (loc.isValid() && m_rewriter.getSourceMgr().isWrittenInMainFile(loc)) { + m_rewriter.ReplaceText(loc, oldName.size(), newName); + } + } + renamedVars[param] = newName; + return true; + } + + bool VisitDeclRefExpr(clang::DeclRefExpr *expr) { + if (auto *var = llvm::dyn_cast(expr->getDecl())) { + llvm::StringRef oldName = var->getName(); + + if (!shouldRename(var)) + return true; + + clang::SourceLocation loc = expr->getLocation(); + if (loc.isValid() && m_rewriter.getSourceMgr().isWrittenInMainFile(loc)) { + std::string newName = getPrefixedName(var); + m_rewriter.ReplaceText(loc, oldName.size(), newName); + } + } + return true; + } + +private: + clang::Rewriter &m_rewriter; + llvm::DenseMap renamedVars; + + bool shouldRename(clang::VarDecl *var) { return renamedVars.count(var) > 0; } + + std::string getPrefixedName(clang::VarDecl *var) { return renamedVars[var]; } + + void renameVar(clang::VarDecl *var, llvm::StringRef prefix) { + llvm::StringRef oldName = var->getName(); + if (oldName.empty()) + return; + + std::string newName = (prefix + oldName).str(); + + clang::SourceLocation loc = var->getLocation(); + if (loc.isValid() && m_rewriter.getSourceMgr().isWrittenInMainFile(loc)) { + m_rewriter.ReplaceText(loc, oldName.size(), newName); + } + + renamedVars[var] = newName; + } +}; + +class ExampleConsumer : public clang::ASTConsumer { +public: + explicit ExampleConsumer(clang::ASTContext *context, + clang::Rewriter &rewriter) + : m_visitor(context, rewriter), m_rewriter(rewriter) {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + m_visitor.TraverseDecl(context.getTranslationUnitDecl()); + + auto &buffer = + m_rewriter.getEditBuffer(m_rewriter.getSourceMgr().getMainFileID()); + if (buffer.size() > 0) { + buffer.write(llvm::outs()); + } + } + +private: + ExampleVisitor m_visitor; + clang::Rewriter &m_rewriter; +}; + +class ExampleAction : public clang::PluginASTAction { +public: + bool ParseArgs(const clang::CompilerInstance &ci, + const std::vector &args) override { + return true; + } + + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + m_rewriter.setSourceMgr(ci.getSourceManager(), ci.getLangOpts()); + return std::make_unique(&ci.getASTContext(), m_rewriter); + } + +private: + clang::Rewriter m_rewriter; +}; + +} // namespace + +static clang::FrontendPluginRegistry::Add + X("FirstPlugin_KozlovaEkaterina_FIIT3_ClangAST", + "Adds prefixes to variables"); diff --git a/clang/test/compiler-course/kozlova_e_var_4_test/test.cpp b/clang/test/compiler-course/kozlova_e_var_4_test/test.cpp new file mode 100644 index 0000000000000..39735d7750974 --- /dev/null +++ b/clang/test/compiler-course/kozlova_e_var_4_test/test.cpp @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/FirstPlugin_KozlovaEkaterina_FIIT3_ClangAST%pluginext -plugin FirstPlugin_KozlovaEkaterina_FIIT3_ClangAST -fsyntax-only %s 2>&1 | FileCheck --match-full-lines %s + + +// CHECK: int global_var1 = 0; +// CHECK-NEXT: int foo(int param_a, int param_b) { +// CHECK-NEXT: static int static_var2 = 0; +// CHECK-NEXT: int local_var3 = 123; +// CHECK-NEXT: ++static_var2; +// CHECK-NEXT: return param_a + param_b + global_var1 + static_var2 + local_var3; +// CHECK-NEXT: } +// CHECK-NEXT: int static static_global_var4 = 1; +// CHECK-NEXT: int static static_global_var5 = foo(1, static_global_var4); +// CHECK-NEXT: int foo2(int param_a, int param_b_default_10) { +// CHECK-NEXT: return param_a * param_b_default_10; +// CHECK-NEXT: } +// CHECK-NEXT: int global_var6 = foo2(1); +// CHECK-NEXT: int global_var7 = foo2(1, 2); + + +int var1 = 0; +int foo(int a, int b) { + static int var2 = 0; + int var3 = 123; + ++var2; + return a + b + var1 + var2 + var3; +} +int static var4 = 1; +int static var5 = foo(1, var4); +int foo2(int a, int b = 10) { + return a * b; +} +int var6 = foo2(1); +int var7 = foo2(1, 2); From c58b192ae408a0c4086d3fb2b8b0e8af8f4a1c61 Mon Sep 17 00:00:00 2001 From: suvorovDm-1 <113026535+suvorovDm-1@users.noreply.github.com> Date: Thu, 20 Mar 2025 16:06:34 +0300 Subject: [PATCH 29/50] =?UTF-8?q?=D0=A1=D1=83=D0=B2=D0=BE=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9.=20=D0=9B?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD=D0=B0?= =?UTF-8?q?=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=201.=20Clang=20AS?= =?UTF-8?q?T.=20=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=204.=20(#37)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Реализован плагин, который добавляет к именам статических, глобальных, локальных переменных соответственно префиксы static_, global_, local_ и к именам параметров функций - префикс param_. Плагин изменяет имена не только при объявлении переменной, но и везде, где эта переменная используется. Также плагин может принимать при запуске два аргумента командной строки: --help - выводит описание плагина и список аргументов, которые плагин умеет принимать; --no-log - отключает логирование каждого места, в котором прошло изменение имени переменной или параметра. --------- Co-authored-by: root --- .../suvorov_d_lab1_var4/CMakeLists.txt | 18 ++ .../suvorov_d_adding_prefs.cpp | 154 ++++++++++++++++++ .../suvorov_d_adding_prefs_test.cpp | 53 ++++++ 3 files changed, 225 insertions(+) create mode 100644 clang/compiler-course/suvorov_d_lab1_var4/CMakeLists.txt create mode 100755 clang/compiler-course/suvorov_d_lab1_var4/suvorov_d_adding_prefs.cpp create mode 100644 clang/test/compiler-course/suvorov_d_lab1_var4/suvorov_d_adding_prefs_test.cpp diff --git a/clang/compiler-course/suvorov_d_lab1_var4/CMakeLists.txt b/clang/compiler-course/suvorov_d_lab1_var4/CMakeLists.txt new file mode 100644 index 0000000000000..e89fc8a9a66c9 --- /dev/null +++ b/clang/compiler-course/suvorov_d_lab1_var4/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "AddingPrefixesPlugin") +set(Student "Suvorov_Dmitrii") +set(Group "FIIT1") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/suvorov_d_lab1_var4/suvorov_d_adding_prefs.cpp b/clang/compiler-course/suvorov_d_lab1_var4/suvorov_d_adding_prefs.cpp new file mode 100755 index 0000000000000..1a7d4a780c527 --- /dev/null +++ b/clang/compiler-course/suvorov_d_lab1_var4/suvorov_d_adding_prefs.cpp @@ -0,0 +1,154 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +namespace { +class RenameVisitor final : public clang::RecursiveASTVisitor { +public: + RenameVisitor(clang::ASTContext *context, clang::Rewriter &rewriter, + bool logChanges) + : m_rewriter(rewriter), m_logChanges(logChanges) {} + + std::string getPrefixForVarDecl(const clang::VarDecl *varDecl) const { + if (varDecl->isStaticLocal()) { + return "static_"; + } else if (varDecl->hasGlobalStorage() && !varDecl->isStaticLocal()) { + return "global_"; + } else if (varDecl->isLocalVarDecl() && !varDecl->isStaticLocal()) { + return "local_"; + } + return ""; + } + + bool VisitVarDecl(clang::VarDecl *varDecl) { + if (varDecl->getLocation().isInvalid() || !varDecl->getIdentifier()) + return true; + + const std::string originalName = varDecl->getNameAsString(); + const std::string prefix = getPrefixForVarDecl(varDecl); + + if (!prefix.empty() && originalName.find(prefix) != 0) { + const std::string updatedName = prefix + originalName; + m_nameMap[varDecl] = updatedName; + m_rewriter.ReplaceText(varDecl->getLocation(), originalName.size(), + updatedName); + + if (m_logChanges) { + llvm::outs() << "Renamed variable at " + << varDecl->getLocation().printToString( + m_rewriter.getSourceMgr()) + << ": " << originalName << " -> " << updatedName << "\n"; + } + } + return true; + } + + bool VisitParmVarDecl(clang::ParmVarDecl *paramDecl) { + if (paramDecl->getLocation().isInvalid() || !paramDecl->getIdentifier()) + return true; + + const std::string originalName = paramDecl->getNameAsString(); + const std::string prefix = "param_"; + + if (originalName.find(prefix) != 0) { + const std::string updatedName = prefix + originalName; + m_nameMap[paramDecl] = updatedName; + m_rewriter.ReplaceText(paramDecl->getLocation(), originalName.size(), + updatedName); + + if (m_logChanges) { + llvm::outs() << "Renamed parameter at " + << paramDecl->getLocation().printToString( + m_rewriter.getSourceMgr()) + << ": " << originalName << " -> " << updatedName << "\n"; + } + } + return true; + } + + bool VisitDeclRefExpr(clang::DeclRefExpr *declRef) { + if (auto *varDecl = llvm::dyn_cast(declRef->getDecl())) { + auto it = m_nameMap.find(varDecl); + if (it != m_nameMap.end()) { + const std::string originalName = varDecl->getNameAsString(); + const std::string updatedName = it->second; + m_rewriter.ReplaceText(declRef->getLocation(), originalName.size(), + updatedName); + + if (m_logChanges) { + llvm::outs() << "Updated reference at " + << declRef->getLocation().printToString( + m_rewriter.getSourceMgr()) + << ": " << originalName << " -> " << updatedName << "\n"; + } + } + } + return true; + } + +private: + clang::Rewriter &m_rewriter; + bool m_logChanges; + std::unordered_map m_nameMap; +}; + +class RenameConsumer final : public clang::ASTConsumer { +public: + RenameConsumer(clang::ASTContext *context, clang::Rewriter &rewriter, + bool logChanges) + : m_visitor(context, rewriter, logChanges) {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + m_visitor.TraverseDecl(context.getTranslationUnitDecl()); + } + +private: + RenameVisitor m_visitor; +}; + +class RenameAction final : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + m_rewriter.setSourceMgr(ci.getSourceManager(), ci.getLangOpts()); + return std::make_unique(&ci.getASTContext(), m_rewriter, + m_logChanges); + } + + bool ParseArgs(const clang::CompilerInstance &ci, + const std::vector &args) override { + for (const auto &arg : args) { + if (arg == "--help") { + llvm::outs() + << "Rename Plugin - Adds prefixes to variables and parameters.\n" + << "Usage:\n" + << " --no-log Disable logging of changes\n" + << " --help Show this help message\n"; + return true; + } else if (arg == "--no-log") { + m_logChanges = false; + } + } + return true; + } + + void EndSourceFileAction() override { + m_rewriter.getEditBuffer(m_rewriter.getSourceMgr().getMainFileID()) + .write(llvm::outs()); + } + +private: + clang::Rewriter m_rewriter; + bool m_logChanges = true; +}; + +} // namespace + +static clang::FrontendPluginRegistry::Add + X("AddingPrefixesPlugin_Suvorov_Dmitrii_FIIT1_ClangAST", + "Renames variables and parameters with appropriate prefixes"); diff --git a/clang/test/compiler-course/suvorov_d_lab1_var4/suvorov_d_adding_prefs_test.cpp b/clang/test/compiler-course/suvorov_d_lab1_var4/suvorov_d_adding_prefs_test.cpp new file mode 100644 index 0000000000000..08ca0961c1538 --- /dev/null +++ b/clang/test/compiler-course/suvorov_d_lab1_var4/suvorov_d_adding_prefs_test.cpp @@ -0,0 +1,53 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/AddingPrefixesPlugin_Suvorov_Dmitrii_FIIT1_ClangAST%pluginext -plugin AddingPrefixesPlugin_Suvorov_Dmitrii_FIIT1_ClangAST %s -fsyntax-only 2>&1 | FileCheck --match-full-lines %s + +// CHECK: int global_v1 = -10; +// CHECK-NEXT: extern float global_v3; +// CHECK-NEXT: long long global_ll = 1L; +// CHECK-NEXT: int static global_v4 = 52; +// CHECK-NEXT: int test_func(int param_a, int param_b) { +// CHECK-NEXT: static int static_v2 = 0; +// CHECK-NEXT: int local_v3 = 1; +// CHECK-NEXT: static_v2 = local_v3 - ++static_v2; +// CHECK-NEXT: return global_v1 + param_a + param_b + static_v2 + local_v3 + global_ll; +// CHECK-NEXT: } +// CHECK-NEXT: int static global_v5 = test_func(43, global_v4); + +int v1 = -10; +extern float v3; +long long ll = 1L; +int static v4 = 52; +int test_func(int a, int b) { + static int v2 = 0; + int v3 = 1; + v2 = v3 - ++v2; + return v1 + a + b + v2 + v3 + ll; +} +int static v5 = test_func(43, v4); + +// RUN: %clang_cc1 -load %llvmshlibdir/AddingPrefixesPlugin_Suvorov_Dmitrii_FIIT1_ClangAST%pluginext -plugin AddingPrefixesPlugin_Suvorov_Dmitrii_FIIT1_ClangAST %s -fsyntax-only 2>&1 | FileCheck -v %s -check-prefix=CHECK-LOGS + +// CHECK-LOGS: Renamed variable at test.cpp:15:5: v1 -> global_v1 +// CHECK-LOGS-NEXT: Renamed variable at test.cpp:16:14: v3 -> global_v3 +// CHECK-LOGS-NEXT: Renamed variable at test.cpp:17:13: ll -> global_ll +// CHECK-LOGS-NEXT: Renamed variable at test.cpp:18:12: v4 -> static_v4 +// CHECK-LOGS-NEXT: Renamed parameter at test.cpp:19:16: a -> param_a +// CHECK-LOGS-NEXT: Renamed parameter at test.cpp:19:23: b -> param_b +// CHECK-LOGS-NEXT: Renamed variable at test.cpp:20:14: v2 -> static_v2 +// CHECK-LOGS-NEXT: Renamed variable at test.cpp:21:7: v3 -> local_v3 +// CHECK-LOGS-NEXT: Updated reference at test.cpp:22:5: v2 -> static_v2 +// CHECK-LOGS-NEXT: Updated reference at test.cpp:22:10: v3 -> local_v3 +// CHECK-LOGS-NEXT: Updated reference at test.cpp:22:16: v2 -> static_v2 +// CHECK-LOGS-NEXT: Updated reference at test.cpp:23:10: v1 -> global_v1 +// CHECK-LOGS-NEXT: Updated reference at test.cpp:23:15: a -> param_a +// CHECK-LOGS-NEXT: Updated reference at test.cpp:23:19: b -> param_b +// CHECK-LOGS-NEXT: Updated reference at test.cpp:23:23: v2 -> static_v2 +// CHECK-LOGS-NEXT: Updated reference at test.cpp:23:27: v3 -> local_v3 +// CHECK-LOGS-NEXT: Updated reference at test.cpp:23:31: ll -> global_ll +// CHECK-LOGS-NEXT: Renamed variable at test.cpp:25:12: v5 -> static_v5 +// CHECK-LOGS-NEXT: Updated reference at test.cpp:25:35: v4 -> static_v4 + +// RUN: %clang_cc1 -load %llvmshlibdir/AddingPrefixesPlugin_Suvorov_Dmitrii_FIIT1_ClangAST%pluginext -plugin AddingPrefixesPlugin_Suvorov_Dmitrii_FIIT1_ClangAST -plugin-arg-AddingPrefixesPlugin_Suvorov_Dmitrii_FIIT1_ClangAST --help -fsyntax-only %s 2>&1 | FileCheck --match-full-lines %s --check-prefix=CHECK-HELP-ARG +// CHECK-HELP-ARG: Rename Plugin - Adds prefixes to variables and parameters. +// CHECK-HELP-ARG-NEXT: Usage: +// CHECK-HELP-ARG-NEXT: --no-log Disable logging of changes +// CHECK-HELP-ARG-NEXT: --help Show this help message From fbcec98d3d809d2aca53fdbf0299c8baa0f722c9 Mon Sep 17 00:00:00 2001 From: m1likus <113242173+m1likus@users.noreply.github.com> Date: Thu, 20 Mar 2025 16:07:42 +0300 Subject: [PATCH 30/50] =?UTF-8?q?=D0=9A=D0=B0=D0=B1=D0=B0=D0=BB=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=20=D0=92=D0=B0=D0=BB=D0=B5=D1=80=D0=B8=D1=8F.=20?= =?UTF-8?q?=D0=9B=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=201.=20?= =?UTF-8?q?=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=202.=20(#38)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Вариант 2 на compiler explorer: https://compiler-explorer.com/z/P6z8G1M9x Проходим по дереву с помощью recursive visitor. Как только попадаем в объявление параметров функции или объявление переменной, то проверяем используется переменная или нет, а также есть ли уже атрибут [[maybe_unused]]. Если переменная не используется и атрибута еще нет, то добавляем атрибут. Код переписывается с помощью интерфейса rewriter. В конце берем готовый буфер из rewriter и перенаправляем его в вывод. Тесты включают в себя проверку глобальной переменной, проверку параметров функции и тела функции, а также переменной, уже помеченной как Unused. Дополнительно реализован флаг --help, позволяющий узнать подробнее про данный плагин. --- .../kabalova_clangAST/CMakeLists.txt | 18 ++++ .../kabalova_clangAST/kabalova_ClangAST.cpp | 88 +++++++++++++++++++ .../kabalova_testClangAST.cpp | 24 +++++ 3 files changed, 130 insertions(+) create mode 100644 clang/compiler-course/kabalova_clangAST/CMakeLists.txt create mode 100644 clang/compiler-course/kabalova_clangAST/kabalova_ClangAST.cpp create mode 100644 clang/test/compiler-course/kabalova_testClangAST/kabalova_testClangAST.cpp diff --git a/clang/compiler-course/kabalova_clangAST/CMakeLists.txt b/clang/compiler-course/kabalova_clangAST/CMakeLists.txt new file mode 100644 index 0000000000000..0b5690ff4d4d9 --- /dev/null +++ b/clang/compiler-course/kabalova_clangAST/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "ClangAST") +set(Student "Kabalova_Valeria") +set(Group "FIIT1") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/kabalova_clangAST/kabalova_ClangAST.cpp b/clang/compiler-course/kabalova_clangAST/kabalova_ClangAST.cpp new file mode 100644 index 0000000000000..9fbcdbf81bc75 --- /dev/null +++ b/clang/compiler-course/kabalova_clangAST/kabalova_ClangAST.cpp @@ -0,0 +1,88 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/raw_ostream.h" + +namespace { + +class UnusedVisitor final : public clang::RecursiveASTVisitor { +public: + UnusedVisitor(clang::ASTContext *context, clang::Rewriter &rewriter) + : m_context(context), m_rewriter(rewriter) {} + bool VisitFuncParameterDecl(clang::ParmVarDecl *parameter) { + if (!parameter->isUsed() && !parameter->hasAttr()) { + clang::SourceLocation Loc = parameter->getBeginLoc(); + if (clang::Rewriter::isRewritable(Loc)) { + m_rewriter.InsertTextBefore(Loc, "[[maybe_unused]] "); + } else + return false; + } + return true; + } + bool VisitVarDecl(clang::VarDecl *variable) { + if (!variable->isUsed() && !variable->hasAttr()) { + clang::SourceLocation Loc = variable->getBeginLoc(); + if (clang::Rewriter::isRewritable(Loc)) { + m_rewriter.InsertTextBefore(Loc, "[[maybe_unused]] "); + } else + return false; + } + return true; + } + +private: + clang::ASTContext *m_context; + // Using rewriter - the main interface to rewrite buffers + clang::Rewriter &m_rewriter; +}; + +class UnusedConsumer final : public clang::ASTConsumer { +public: + UnusedConsumer(clang::ASTContext *context, clang::Rewriter &rewriter) + : m_visitor(context, rewriter) {} + void HandleTranslationUnit(clang::ASTContext &context) override { + // TraverseDecl - Recursively visit a declaration + m_visitor.TraverseDecl(context.getTranslationUnitDecl()); + } + +private: + UnusedVisitor m_visitor; +}; + +class UnusedAction final : public clang::PluginASTAction { +private: + clang::Rewriter m_rewriter; + +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + m_rewriter.setSourceMgr(ci.getSourceManager(), ci.getLangOpts()); + return std::make_unique(&ci.getASTContext(), m_rewriter); + } + + void EndSourceFile() override { + m_rewriter.getEditBuffer(m_rewriter.getSourceMgr().getMainFileID()) + .write(llvm::outs()); + } + + bool ParseArgs(const clang::CompilerInstance &ci, + const std::vector &args) override { + for (auto &i : args) { + if (i == "--help") { + llvm::outs() + << "This plugin searches for unused variables or unused parameters " + "of functions and marks them with attribute [[maybe__unused]]" + << "\n"; + } + } + return true; + } +}; + +} // namespace + +static clang::FrontendPluginRegistry::Add + X("ClangAST_Kabalova_Valeria_FIIT1_ClangAST", + "Adds attribute [[maybe_unused]] if the variable or parameter is unused"); diff --git a/clang/test/compiler-course/kabalova_testClangAST/kabalova_testClangAST.cpp b/clang/test/compiler-course/kabalova_testClangAST/kabalova_testClangAST.cpp new file mode 100644 index 0000000000000..48a059b128378 --- /dev/null +++ b/clang/test/compiler-course/kabalova_testClangAST/kabalova_testClangAST.cpp @@ -0,0 +1,24 @@ + +// RUN: %clang_cc1 -load %llvmshlibdir/ClangAST_Kabalova_Valeria_FIIT1_ClangAST%pluginext -plugin ClangAST_Kabalova_Valeria_FIIT1_ClangAST %s -fsyntax-only 2>&1 | FileCheck %s + +// CHECK:\[\[maybe_unused\]\] int f = 0; + +// CHECK:int foo(int a, int b, \[\[maybe_unused\]\] int c) { +// CHECK-NEXT:\[\[maybe_unused\]\] double value = 0.0; +// CHECK-NEXT: double tmp = 1.0; +// CHECK-NEXT: return a + b + tmp; +// CHECK: \[\[maybe_unused\]\] int d = 0; + +int f = 0; + +int example(int a, int b, int c) { + double value = 0.0; + double tmp = 1.0; + return a + b + tmp; +} + +[[maybe_unused]] int d = 0; + +// RUN: %clang_cc1 -load %llvmshlibdir/ClangAST_Kabalova_Valeria_FIIT1_ClangAST%pluginext -plugin ClangAST_Kabalova_Valeria_FIIT1_ClangAST -plugin-arg-ClangAST_Kabalova_Valeria_FIIT1_ClangAST --help %s -fsyntax-only 2>&1 | FileCheck %s + +// CHECK: This plugin searches for unused variables or unused parameters of functions and marks them with attribute \[\[maybe__unused\]\] From 44b9d82fdf769879c0bf9de29cb377801842f93f Mon Sep 17 00:00:00 2001 From: 0xG00SE <61384845+DSolo03@users.noreply.github.com> Date: Thu, 20 Mar 2025 16:07:50 +0300 Subject: [PATCH 31/50] =?UTF-8?q?=D0=A1=D0=BE=D0=BB=D0=BE=D0=B2=D1=8C?= =?UTF-8?q?=D0=B5=D0=B2=20=D0=94=D0=B0=D0=BD=D0=B8=D0=BB=D0=B0.=20=D0=9B?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD=D0=B0?= =?UTF-8?q?=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=201:=20Clang=20AS?= =?UTF-8?q?T.=20=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=202.=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Плагин обходит все переменные и параметры функций и если данная переменная не используется, то помечает ее аттрибутом `maybe_unused`. После этого, плагин обходит все функции, делая `dump` их AST. Обход реализован двумя обходчиками: `UnusedVisitor` и `DumpVisitor`. Сначала вызывается `UnusedVisitor`, который помечает переменные и параметры функций, а затем вызывается `DumpVisitor` выводящий `dump` AST для всех функций, для проверки наличия аттрибута `maybe_unused` при помощи тестов. --- .../solovyev_d_unused_variable/CMakeLists.txt | 18 +++++ .../solovyev_d_unused_variable_plugin.cpp | 81 +++++++++++++++++++ .../solovyev_d_unused_variable_test.cpp | 34 ++++++++ 3 files changed, 133 insertions(+) create mode 100644 clang/compiler-course/solovyev_d_unused_variable/CMakeLists.txt create mode 100644 clang/compiler-course/solovyev_d_unused_variable/solovyev_d_unused_variable_plugin.cpp create mode 100644 clang/test/compiler-course/solovyev_d_unused_variable/solovyev_d_unused_variable_test.cpp diff --git a/clang/compiler-course/solovyev_d_unused_variable/CMakeLists.txt b/clang/compiler-course/solovyev_d_unused_variable/CMakeLists.txt new file mode 100644 index 0000000000000..8ab892226ec37 --- /dev/null +++ b/clang/compiler-course/solovyev_d_unused_variable/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "UnusedVariable") +set(Student "Solovyev_Danila") +set(Group "FIIT3") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/solovyev_d_unused_variable/solovyev_d_unused_variable_plugin.cpp b/clang/compiler-course/solovyev_d_unused_variable/solovyev_d_unused_variable_plugin.cpp new file mode 100644 index 0000000000000..ae05e2698f360 --- /dev/null +++ b/clang/compiler-course/solovyev_d_unused_variable/solovyev_d_unused_variable_plugin.cpp @@ -0,0 +1,81 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/Attr.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/raw_ostream.h" +namespace { +class UnusedVisitor final : public clang::RecursiveASTVisitor { +public: + explicit UnusedVisitor(clang::ASTContext *context, clang::Rewriter &Rewriter) + : m_context(context), MRewriter(Rewriter) {} + bool VisitVarDecl(clang::VarDecl *var) { + if (!var->isUsed() && !var->hasAttr()) { + MRewriter.InsertText(var->getSourceRange().getBegin(), + "[[maybe_unused]] ", true, true); + var->addAttr(clang::UnusedAttr::Create(*m_context)); + } + return true; + } + + bool VisitParmVarDecl(clang::ParmVarDecl *param) { + if (!param->isUsed() && !param->hasAttr()) { + MRewriter.InsertText(param->getSourceRange().getBegin(), + "[[maybe_unused]] ", true, true); + param->addAttr(clang::UnusedAttr::Create(*m_context)); + } + return true; + } + +private: + clang::Rewriter &MRewriter; + clang::ASTContext *m_context; +}; + +class UnusedConsumer final : public clang::ASTConsumer { +public: + explicit UnusedConsumer(clang::ASTContext *context, clang::Rewriter &Rewriter) + : um_visitor(context, Rewriter) {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + um_visitor.TraverseDecl(context.getTranslationUnitDecl()); + } + +private: + UnusedVisitor um_visitor; +}; + +class UnusedAction final : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + MRewriter.setSourceMgr(ci.getSourceManager(), ci.getLangOpts()); + return std::make_unique(&ci.getASTContext(), MRewriter); + } + + bool ParseArgs(const clang::CompilerInstance &ci, + const std::vector &args) override { + for (const auto &arg : args) { + if (arg == "--help") { + llvm::outs() << "Marking unused variables and function parameters as " + "[[maybe_unused]]\n"; + return true; + } + } + return true; + } + + void EndSourceFileAction() override { + MRewriter.getEditBuffer(MRewriter.getSourceMgr().getMainFileID()) + .write(llvm::outs()); + } + +private: + clang::Rewriter MRewriter; +}; +} // namespace + +static clang::FrontendPluginRegistry::Add + X("unused_variable_plugin", + "Marking unused variables and function parameters as [[maybe_unused]]"); diff --git a/clang/test/compiler-course/solovyev_d_unused_variable/solovyev_d_unused_variable_test.cpp b/clang/test/compiler-course/solovyev_d_unused_variable/solovyev_d_unused_variable_test.cpp new file mode 100644 index 0000000000000..bde48c12b042e --- /dev/null +++ b/clang/test/compiler-course/solovyev_d_unused_variable/solovyev_d_unused_variable_test.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/UnusedVariable_Solovyev_Danila_FIIT3_ClangAST%pluginext -plugin unused_variable_plugin -fsyntax-only %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -load %llvmshlibdir/UnusedVariable_Solovyev_Danila_FIIT3_ClangAST%pluginext -plugin unused_variable_plugin -plugin-arg-unused_variable_plugin --help 2>&1 | FileCheck --check-prefix=HELP %s + +//HELP:Marking unused variables and function parameters as {{\[\[maybe_unused\]\]}} + +//CHECK: int foo(int a, int b, \[\[maybe_unused\]\] int c) { +//CHECK: \[\[maybe_unused\]\] double value = 0.0; + +int foo(int a, int b, int c) { + double value = 0.0; + return a + b; +} + +//CHECK: bool bar(int a, bool b, \[\[maybe_unused\]\] double c=3.0){ + +bool bar(int a, bool b, double c=3.0){ + b=a++; + return true; +} + +//CHECK: \[\[maybe_unused\]\] int test; +//CHECK: \[\[maybe_unused\]\] bool c=b; + +int test; +int b=1; +bool c=b; + +//CHECK: A(int a,\[\[maybe_unused\]\] int b){ + +class A{ + A(int a,int b){ + a=1; + } +}; From bc5bd3668b7c455c7ae3c55adb9719825bfd4430 Mon Sep 17 00:00:00 2001 From: Sozonov_Ilushka <113029719+sozozzya@users.noreply.github.com> Date: Fri, 21 Mar 2025 08:01:11 +0300 Subject: [PATCH 32/50] =?UTF-8?q?=D0=A1=D0=BE=D0=B7=D0=BE=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=98=D0=BB=D1=8C=D1=8F.=20=D0=9B=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD=D0=B0=D1=8F=20=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=B0=201.=20Clang=20AST.=20=D0=92=D0=B0?= =?UTF-8?q?=D1=80=D0=B8=D0=B0=D0=BD=D1=82=201.=20=20(#40)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This Clang plugin analyzes C++ classes and prints their inheritance, fields, and methods. 1. **ClassInfoVisitor** - Traverses the AST, finds class declarations, and checks their definitions. - Outputs the class name, base classes, fields, and methods. **Processing methods:** - **PrintBaseClassesInfo()** – prints the list of base classes. - **PrintFieldsInfo()** – displays fields with their types and access specifiers. - **PrintMethodsInfo(**) – prints methods with their parameters, return type, and specifiers (virtual, override, const). 2. **ClassInfoConsumer** – initiates AST traversal. 3. **ClassInfoAction** – registers the plugin in Clang. --- .../CMakeLists.txt | 18 ++ ...ozonov_i_user_defined_data_type_plugin.cpp | 157 ++++++++++++++++++ .../sozonov_i_user_defined_data_type_test.cpp | 115 +++++++++++++ 3 files changed, 290 insertions(+) create mode 100644 clang/compiler-course/sozonov_i_user_defined_data_type/CMakeLists.txt create mode 100644 clang/compiler-course/sozonov_i_user_defined_data_type/sozonov_i_user_defined_data_type_plugin.cpp create mode 100644 clang/test/compiler-course/sozonov_i_user_defined_data_type/sozonov_i_user_defined_data_type_test.cpp diff --git a/clang/compiler-course/sozonov_i_user_defined_data_type/CMakeLists.txt b/clang/compiler-course/sozonov_i_user_defined_data_type/CMakeLists.txt new file mode 100644 index 0000000000000..73c90c6e2f38c --- /dev/null +++ b/clang/compiler-course/sozonov_i_user_defined_data_type/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "UserDefinedDataTypePlugin") +set(Student "SozonovIlya") +set(Group "FIIT3") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/sozonov_i_user_defined_data_type/sozonov_i_user_defined_data_type_plugin.cpp b/clang/compiler-course/sozonov_i_user_defined_data_type/sozonov_i_user_defined_data_type_plugin.cpp new file mode 100644 index 0000000000000..a3c858d3d8b45 --- /dev/null +++ b/clang/compiler-course/sozonov_i_user_defined_data_type/sozonov_i_user_defined_data_type_plugin.cpp @@ -0,0 +1,157 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "llvm/Support/raw_ostream.h" + +namespace { + +std::string AccessToString(clang::AccessSpecifier access) { + switch (access) { + case clang::AS_public: + return "public"; + case clang::AS_protected: + return "protected"; + case clang::AS_private: + return "private"; + default: + return ""; + } +} + +class ClassInfoVisitor final + : public clang::RecursiveASTVisitor { +public: + explicit ClassInfoVisitor(clang::ASTContext *context) {} + bool VisitCXXRecordDecl(clang::CXXRecordDecl *record) { + if (!record->isThisDeclarationADefinition() || record->isImplicit()) + return true; + + auto &output = llvm::outs(); + output << record->getQualifiedNameAsString(); + + PrintBaseClassesInfo(output, record); + output << "\n|_Fields\n"; + PrintFieldsInfo(output, record); + output << "|\n|_Methods\n"; + PrintMethodsInfo(output, record); + output << "|\n|_Nested Types\n"; + PrintNestedTypesInfo(output, record); + output << "\n"; + + return true; + } + +private: + void PrintBaseClassesInfo(llvm::raw_ostream &output, + const clang::CXXRecordDecl *record) { + if (record->getNumBases() == 0) + return; + + llvm::SmallVector bases; + for (const auto &base : record->bases()) + if (auto *base_decl = base.getType()->getAsCXXRecordDecl()) + bases.push_back(base_decl->getQualifiedNameAsString()); + + output << " -> " << llvm::join(bases, ", "); + } + + void PrintFieldsInfo(llvm::raw_ostream &output, + const clang::CXXRecordDecl *record) { + if (record->field_empty()) { + output << "| |_ (no fields)\n"; + return; + } + + for (const auto *field : record->fields()) + output << "| |_ " << field->getNameAsString() << " (" + << field->getType().getAsString() << "|" + << AccessToString(field->getAccess()) << ")\n"; + } + + void PrintMethodsInfo(llvm::raw_ostream &output, + const clang::CXXRecordDecl *record) { + if (record->method_begin() == record->method_end()) { + output << "| |_ (no methods)\n"; + return; + } + + for (const auto *method : record->methods()) { + if (method->isImplicit()) + continue; + + output << "| |_ " << method->getNameAsString() << " (" + << method->getReturnType().getAsString() << "("; + + llvm::interleaveComma(method->parameters(), output, + [](const clang::ParmVarDecl *param) { + llvm::outs() << param->getType().getAsString(); + }); + + output << ")|" << AccessToString(method->getAccess()); + + if (method->isVirtual() && !method->hasAttr()) + output << "|virtual"; + if (method->isPureVirtual()) + output << "|pure"; + if (method->hasAttr()) + output << "|override"; + if (method->isConst()) + output << "|const"; + + output << ")\n"; + } + } + + void PrintNestedTypesInfo(llvm::raw_ostream &output, + const clang::CXXRecordDecl *record) { + bool has_nested_types = false; + + for (const auto *decl : record->decls()) { + if (const auto *nested = llvm::dyn_cast(decl)) { + if (!nested->isThisDeclarationADefinition() || nested->isImplicit()) + continue; + + if (!has_nested_types) { + has_nested_types = true; + } + output << "| |_ " << nested->getNameAsString() << "\n"; + } + } + + if (!has_nested_types) + output << "| |_ (no nested types)\n"; + } +}; + +class ClassInfoConsumer final : public clang::ASTConsumer { +public: + explicit ClassInfoConsumer(clang::ASTContext *context) + : class_visitor(context) {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + class_visitor.TraverseDecl(context.getTranslationUnitDecl()); + } + +private: + ClassInfoVisitor class_visitor; +}; + +class ClassInfoAction final : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + return std::make_unique(&ci.getASTContext()); + } + + bool ParseArgs(const clang::CompilerInstance &ci, + const std::vector &args) override { + return true; + } +}; +} // namespace + +static clang::FrontendPluginRegistry::Add + X("user_defined_data_type_plugin", + "Prints information about the user defined data type: 1) Fields; 2) " + "Methods; 3) Basic classes"); diff --git a/clang/test/compiler-course/sozonov_i_user_defined_data_type/sozonov_i_user_defined_data_type_test.cpp b/clang/test/compiler-course/sozonov_i_user_defined_data_type/sozonov_i_user_defined_data_type_test.cpp new file mode 100644 index 0000000000000..e6ce883d63944 --- /dev/null +++ b/clang/test/compiler-course/sozonov_i_user_defined_data_type/sozonov_i_user_defined_data_type_test.cpp @@ -0,0 +1,115 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/UserDefinedDataTypePlugin_SozonovIlya_FIIT3_ClangAST%pluginext -plugin user_defined_data_type_plugin -fsyntax-only %s 2>&1 | FileCheck %s + +//CHECK: Human +//CHECK-NEXT: |_Fields +//CHECK-NEXT: | |_ age (unsigned int|public) +//CHECK-NEXT: | |_ height (unsigned int|public) +//CHECK-NEXT: | +//CHECK-NEXT: |_Methods +//CHECK-NEXT: | |_ sleep (void()|public|virtual|pure) +//CHECK-NEXT: | |_ eat (void()|public|virtual|pure) + +struct Human { + unsigned age; + unsigned height; + virtual void sleep() = 0; + virtual void eat() = 0; +}; + +//CHECK: Engineer -> Human +//CHECK-NEXT: |_Fields +//CHECK-NEXT: | |_ salary (unsigned int|public) +//CHECK-NEXT: | +//CHECK-NEXT: |_Methods +//CHECK-NEXT: | |_ sleep (void()|public|override) +//CHECK-NEXT: | |_ eat (void()|public|override) +//CHECK-NEXT: | |_ work (void()|public) + +struct Engineer : Human { + unsigned salary; + void sleep() override { /* something */ } + void eat() override { /* something */ } + void work() { /* something */ } +}; + +// CHECK: EmptyStruct +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ (no fields) +// CHECK-NEXT: | +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ (no methods) + +struct EmptyStruct {}; + +// CHECK: Data +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ publicField (int|public) +// CHECK-NEXT: | |_ protectedField (double|protected) +// CHECK-NEXT: | |_ privateField (char|private) +// CHECK-NEXT: | +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ (no methods) + +struct Data { + int publicField; +protected: + double protectedField; +private: + char privateField; +}; + +// CHECK: MethodsExample +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ (no fields) +// CHECK-NEXT: | +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ foo (void()|public) +// CHECK-NEXT: | |_ bar (int(int, double)|public) +// CHECK-NEXT: | |_ baz (void()|public|const) + +struct MethodsExample { + void foo() {} + int bar(int x, double y) { return x; } + void baz() const {} +}; + +// CHECK: Base +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ (no fields) +// CHECK-NEXT: | +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ virtualFunc (void()|public|virtual|pure) + +struct Base { + virtual void virtualFunc() = 0; +}; + +// CHECK: Derived -> Base +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ (no fields) +// CHECK-NEXT: | +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ virtualFunc (void()|public|override) + +struct Derived : Base { + void virtualFunc() override {} +}; + +// CHECK: A + +struct A {}; + +// CHECK: B -> A +// CHECK-NEXT: |_Fields +// CHECK-NEXT: | |_ value (A|public) +// CHECK-NEXT: | +// CHECK-NEXT: |_Methods +// CHECK-NEXT: | |_ (no methods) +// CHECK-NEXT: | +// CHECK-NEXT: |_Nested Types +// CHECK-NEXT: | |_ A + +struct B : public A { + struct A {}; + A value; +}; From 6d884884f671f9dfbb900cf0d2923fd3f4378ba4 Mon Sep 17 00:00:00 2001 From: Danie <125826285+Dxppi@users.noreply.github.com> Date: Sat, 22 Mar 2025 12:13:42 +0300 Subject: [PATCH 33/50] =?UTF-8?q?=D0=9F=D0=BB=D0=B5=D1=85=D0=B0=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=94=D0=B0=D0=BD=D0=B8=D0=B8=D0=BB.=20=D0=9B?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD=D0=B0?= =?UTF-8?q?=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=201.=20Clang=20AS?= =?UTF-8?q?T.=20=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=202=20(#39)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Проверка неиспользуемых переменных, если такие имеются, выводится UNUSED VAR --- .../plekhanov_unused_variables/CMakeLists.txt | 18 +++++ .../plekhanov_unused_variables/plekhanov.cpp | 78 +++++++++++++++++++ .../plekhanov_unused_variables/test.cpp | 19 +++++ 3 files changed, 115 insertions(+) create mode 100644 clang/compiler-course/plekhanov_unused_variables/CMakeLists.txt create mode 100644 clang/compiler-course/plekhanov_unused_variables/plekhanov.cpp create mode 100644 clang/test/compiler-course/plekhanov_unused_variables/test.cpp diff --git a/clang/compiler-course/plekhanov_unused_variables/CMakeLists.txt b/clang/compiler-course/plekhanov_unused_variables/CMakeLists.txt new file mode 100644 index 0000000000000..fc3eb378d423e --- /dev/null +++ b/clang/compiler-course/plekhanov_unused_variables/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "FindUnused") +set(Student "Plekhanov_Daniil") +set(Group "FIIT2") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/plekhanov_unused_variables/plekhanov.cpp b/clang/compiler-course/plekhanov_unused_variables/plekhanov.cpp new file mode 100644 index 0000000000000..d1229531eb0c6 --- /dev/null +++ b/clang/compiler-course/plekhanov_unused_variables/plekhanov.cpp @@ -0,0 +1,78 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/Attr.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/raw_ostream.h" + +namespace { + +class UnusedVariablesVisitor final + : public clang::RecursiveASTVisitor { +public: + UnusedVariablesVisitor(clang::ASTContext *Context, clang::Rewriter &R) + : m_context(Context), TheRewriter(R) {} + + bool VisitParamVarDecl(clang::ParmVarDecl *param) { + if (!param->isUsed() && !param->hasAttr()) { + clang::SourceLocation loc = param->getSourceRange().getBegin(); + TheRewriter.InsertText(loc, "[[maybe_unused]] "); + } + return true; + } + + bool VisitVarDecl(clang::VarDecl *var) { + if (!var->isUsed() && !var->hasAttr()) { + clang::SourceLocation loc = var->getSourceRange().getBegin(); + TheRewriter.InsertText(loc, "[[maybe_unused]] "); + } + return true; + } + +private: + clang::ASTContext *m_context; + clang::Rewriter &TheRewriter; +}; + +class UnusedVariablesConsumer final : public clang::ASTConsumer { +public: + UnusedVariablesConsumer(clang::ASTContext *Context, clang::Rewriter &R) + : Visitor(Context, R) {} + + void HandleTranslationUnit(clang::ASTContext &Context) override { + Visitor.TraverseDecl(Context.getTranslationUnitDecl()); + } + +private: + UnusedVariablesVisitor Visitor; +}; + +class UnusedVariablesAction final : public clang::PluginASTAction { +private: + clang::Rewriter TheRewriter; + +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &CI, llvm::StringRef) override { + TheRewriter.setSourceMgr(CI.getSourceManager(), CI.getLangOpts()); + return std::make_unique(&CI.getASTContext(), + TheRewriter); + } + + void EndSourceFileAction() override { + TheRewriter.getEditBuffer(TheRewriter.getSourceMgr().getMainFileID()) + .write(llvm::outs()); + } + + bool ParseArgs(const clang::CompilerInstance &CI, + const std::vector &args) override { + return true; + } +}; + +} // namespace + +static clang::FrontendPluginRegistry::Add + X("FindUnused", + "Find unused variables in code and mark them as the /MAYBE UNUSED/"); diff --git a/clang/test/compiler-course/plekhanov_unused_variables/test.cpp b/clang/test/compiler-course/plekhanov_unused_variables/test.cpp new file mode 100644 index 0000000000000..73bbe19eebd7e --- /dev/null +++ b/clang/test/compiler-course/plekhanov_unused_variables/test.cpp @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/FindUnused_Plekhanov_Daniil_FIIT2_ClangAST%pluginext -plugin FindUnused -fsyntax-only %s 2>&1 | FileCheck %s + +// CHECK: \[\[maybe_unused\]\] int global_param=10; +int global_param=10; + +// CHECK: int baz(int a, int b, \[\[maybe_unused\]\] int c) { +// CHECK: \[\[maybe_unused\]\] double value=0.0; +// CHECK: return a + b; + +int foo(int a, int b, int c) { + double value = 0.0; + return a + b; +} + +// CHECK: double foo(double tmp, double tmp1, \[\[maybe_unused\]\] double tmp2) { +// CHECK: \[\[maybe_unused\]\] double c = tmp + tmp1; +double foo(double tmp, double tmp1, double tmp2) { + double c = tmp + tmp1; +} From 0ee90b4213b07610f943f97b4ac06478aef09e51 Mon Sep 17 00:00:00 2001 From: Mike Ivanov <45334246+misha-ivanov@users.noreply.github.com> Date: Sun, 23 Mar 2025 23:15:32 +0300 Subject: [PATCH 34/50] =?UTF-8?q?=D0=98=D0=B2=D0=B0=D0=BD=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB.=20=D0=9B=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD=D0=B0=D1=8F=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=201.=20Clang=20AST.=20=D0=92?= =?UTF-8?q?=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=204.=20(#54)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Цель лабораторной работы: с помощью механизма обхода AST переименовать переменные в соответствии со следующим правилом: - для локальных переменных добавляется приставка `local_` - для глобальных переменных добавляется приставка `global_` - для статических переменных добавляется приставка `static_` - для параметров функции добавляется приставка `param_` Основной задействованный функционал: - `VisitVarDecl` - обрабатывает объявления переменных - - проверяет валидность имени переменной - - конструирует новое имя - - заменяет исходное имя на новое - - сохраняет имя в `unordered_map` `rewrittenDecl` - `VisitParmVarDecl `- обрабатывает объявления параметров функций (аналогично `VisitVarDecl`) - `VisitDeclRefExpr `- обрабатывает ссылки на переменные (заменяет на соответствующее имя из `rewrittenDecl`) --------- Signed-off-by: Mikhail Ivanov --- .../ivanov_m_prefix_4/CMakeLists.txt | 18 +++ .../ivanov_m_prefix_4/ivanov_m_prefix.cpp | 106 ++++++++++++++++++ .../ivanov_m_prefix_test.cpp | 23 ++++ 3 files changed, 147 insertions(+) create mode 100644 clang/compiler-course/ivanov_m_prefix_4/CMakeLists.txt create mode 100644 clang/compiler-course/ivanov_m_prefix_4/ivanov_m_prefix.cpp create mode 100644 clang/test/compiler-course/ivanov_m_prefix_4_test/ivanov_m_prefix_test.cpp diff --git a/clang/compiler-course/ivanov_m_prefix_4/CMakeLists.txt b/clang/compiler-course/ivanov_m_prefix_4/CMakeLists.txt new file mode 100644 index 0000000000000..0a5fdbc62e4f3 --- /dev/null +++ b/clang/compiler-course/ivanov_m_prefix_4/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "Prefix_Plugin") +set(Student "Ivanov_Mikhail") +set(Group "FIIT1") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/ivanov_m_prefix_4/ivanov_m_prefix.cpp b/clang/compiler-course/ivanov_m_prefix_4/ivanov_m_prefix.cpp new file mode 100644 index 0000000000000..9b225e3a28df7 --- /dev/null +++ b/clang/compiler-course/ivanov_m_prefix_4/ivanov_m_prefix.cpp @@ -0,0 +1,106 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/raw_ostream.h" +#include + +namespace { +class NewPrefixVisitor final + : public clang::RecursiveASTVisitor { + clang::Rewriter &m_rewriter; + std::unordered_map rewrittenDecl; + +public: + explicit NewPrefixVisitor(clang::ASTContext *context, + clang::Rewriter &rewriter) + : m_rewriter(rewriter) {} + bool VisitVarDecl(clang::VarDecl *varDecl) { + if (!varDecl) + return true; + + std::string prevName = varDecl->getName().str(); + + if (prevName.empty()) + return true; + + std::string prefix; + + if (varDecl->isStaticLocal()) { + prefix = "static_"; + } else if (varDecl->hasGlobalStorage()) { + prefix = "global_"; + } else if (varDecl->isLocalVarDecl()) { + prefix = "local_"; + } + + if (!prefix.empty()) { + std::string newName = prefix + prevName; + m_rewriter.ReplaceText(varDecl->getLocation(), prevName.size(), newName); + rewrittenDecl[varDecl] = newName; + } + return true; + } + + bool VisitParmVarDecl(clang::ParmVarDecl *parDecl) { + std::string prevName = parDecl->getName().str(); + if (prevName.empty()) + return true; + + std::string newName = "param_" + prevName; + m_rewriter.ReplaceText(parDecl->getLocation(), prevName.size(), newName); + rewrittenDecl[parDecl] = newName; + return true; + } + + bool VisitDeclRefExpr(clang::DeclRefExpr *declRef) { + auto *varDecl = clang::dyn_cast(declRef->getDecl()); + auto it = rewrittenDecl.find(varDecl); + if (it != rewrittenDecl.end()) { + std::string newName = it->second; + clang::SourceLocation loc = declRef->getLocation(); + std::string prevName = varDecl->getName().str(); + m_rewriter.ReplaceText(loc, prevName.size(), newName); + } + return true; + } +}; + +class NewPrefixConsumer final : public clang::ASTConsumer { + clang::Rewriter &m_rewriter; + NewPrefixVisitor m_visitor; + +public: + explicit NewPrefixConsumer(clang::ASTContext *context, + clang::Rewriter &rewriter) + : m_rewriter(rewriter), m_visitor(context, rewriter) {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + m_visitor.TraverseDecl(context.getTranslationUnitDecl()); + m_rewriter.getEditBuffer(context.getSourceManager().getMainFileID()) + .write(llvm::outs()); + } +}; + +class NewPrefixAction : public clang::PluginASTAction { + clang::Rewriter m_rewriter; + +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + m_rewriter.setSourceMgr(ci.getSourceManager(), ci.getLangOpts()); + return std::make_unique(&ci.getASTContext(), m_rewriter); + } + + bool ParseArgs(const clang::CompilerInstance &ci, + const std::vector &args) override { + return true; + } +}; +} // namespace + +static clang::FrontendPluginRegistry::Add + X("Prefix_Plugin_Ivanov_Mikhail_FIIT1_ClangAST", + "Plugin adds corresponding prefixes to static, local and global " + "variables and parameters"); diff --git a/clang/test/compiler-course/ivanov_m_prefix_4_test/ivanov_m_prefix_test.cpp b/clang/test/compiler-course/ivanov_m_prefix_4_test/ivanov_m_prefix_test.cpp new file mode 100644 index 0000000000000..528c994be444d --- /dev/null +++ b/clang/test/compiler-course/ivanov_m_prefix_4_test/ivanov_m_prefix_test.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/Prefix_Plugin_Ivanov_Mikhail_FIIT1_ClangAST%pluginext -plugin Prefix_Plugin_Ivanov_Mikhail_FIIT1_ClangAST -fsyntax-only %s 2>&1 | FileCheck --match-full-lines %s -dump-input=always + +// CHECK: int global_var1 = 1; +// CHECK-NEXT: int foo(int param_a, int param_b){ +// CHECK-NEXT: int local_var2 = 0; +// CHECK-NEXT: static int static_var3 = 10; +// CHECK-NEXT: ++local_var2; +// CHECK-NEXT: return param_a + param_b; +// CHECK-NEXT: } +// CHECK-NEXT: static int global_var4 = foo(global_var1, 10); +// CHECK-NEXT: extern double global_dvar1 = 0; +// CHECK-NEXT: static double global_dvar2 = 1; + +int var1 = 1; +int foo(int a, int b){ + int var2 = 0; + static int var3 = 10; + ++var2; + return a + b; +} +static int var4 = foo(var1, 10); +extern double dvar1 = 0; +static double dvar2 = 1; From 49fafeb029ee44c5b0e5dd1459eaa0606690f7cb Mon Sep 17 00:00:00 2001 From: Maksim Savchenko <113035529+MaxikGuy@users.noreply.github.com> Date: Tue, 25 Mar 2025 07:42:09 +0000 Subject: [PATCH 35/50] =?UTF-8?q?=D0=A1=D0=B0=D0=B2=D1=87=D0=B5=D0=BD?= =?UTF-8?q?=D0=BA=D0=BE=20=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC.=20=D0=9B?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD=D0=B0?= =?UTF-8?q?=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=201.=20Clang=20AS?= =?UTF-8?q?T.=20=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=202.=20(#51)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Плагин проходится по всем параметрам и переменным программы. Если переменная нигде не используется, то она помечается флагом [[maybe_unused]]. --------- Co-authored-by: m-ly4 --- .../savchenko_m_l1_v2/CMakeLists.txt | 18 ++++ .../savchenko_m_l1_v2/savchenko_m_l1_v2.cpp | 94 +++++++++++++++++++ .../savchenko_m_l1_v2_tests.cpp | 34 +++++++ 3 files changed, 146 insertions(+) create mode 100644 clang/compiler-course/savchenko_m_l1_v2/CMakeLists.txt create mode 100644 clang/compiler-course/savchenko_m_l1_v2/savchenko_m_l1_v2.cpp create mode 100644 clang/test/compiler-course/savchenko_m_l1_v2_tests/savchenko_m_l1_v2_tests.cpp diff --git a/clang/compiler-course/savchenko_m_l1_v2/CMakeLists.txt b/clang/compiler-course/savchenko_m_l1_v2/CMakeLists.txt new file mode 100644 index 0000000000000..4d7686fd764fd --- /dev/null +++ b/clang/compiler-course/savchenko_m_l1_v2/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "UnusedVariables") +set(Student "Savchenko_Maxim") +set(Group "FIIT1") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/savchenko_m_l1_v2/savchenko_m_l1_v2.cpp b/clang/compiler-course/savchenko_m_l1_v2/savchenko_m_l1_v2.cpp new file mode 100644 index 0000000000000..87ca54c2360f5 --- /dev/null +++ b/clang/compiler-course/savchenko_m_l1_v2/savchenko_m_l1_v2.cpp @@ -0,0 +1,94 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/Attr.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/raw_ostream.h" + +namespace { +class UnusedVarsVisitor final + : public clang::RecursiveASTVisitor { +public: + explicit UnusedVarsVisitor(clang::ASTContext *context, + clang::Rewriter &rewriter) + : m_context(context), m_rewriter(rewriter) {} + + bool VisitParamVarDecl(clang::ParmVarDecl *param) { + if (!param->isUsed() && !param->hasAttr()) { + clang::SourceLocation location = param->getBeginLoc(); + if (clang::Rewriter::isRewritable(location)) { + m_rewriter.InsertTextBefore(location, "[[maybe_unused]] "); + } else { + return false; + } + } + return true; + } + + bool VisitVarDecl(clang::VarDecl *var) { + if (!var->isUsed() && !var->hasAttr()) { + clang::SourceLocation location = var->getBeginLoc(); + if (clang::Rewriter::isRewritable(location)) { + m_rewriter.InsertTextBefore(location, "[[maybe_unused]] "); + } else { + return false; + } + } + return true; + } + +private: + clang::ASTContext *m_context; + clang::Rewriter &m_rewriter; +}; + +class UnusedVarsConsumer final : public clang::ASTConsumer { +public: + explicit UnusedVarsConsumer(clang::ASTContext *context, + clang::Rewriter &rewriter) + : m_visitor(context, rewriter) {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + m_visitor.TraverseDecl(context.getTranslationUnitDecl()); + } + +private: + UnusedVarsVisitor m_visitor; +}; + +class UnusedVarsAction final : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + m_rewriter.setSourceMgr(ci.getSourceManager(), ci.getLangOpts()); + return std::make_unique(&ci.getASTContext(), + m_rewriter); + } + + void EndSourceFileAction() override { + m_rewriter.getEditBuffer(m_rewriter.getSourceMgr().getMainFileID()) + .write(llvm::outs()); + } + + bool ParseArgs(const clang::CompilerInstance &ci, + const std::vector &args) override { + for (const auto &arg : args) { + if (arg == "-h" || arg == "--help") { + llvm::outs() << "The plugin adds the [[maybe_unused]] flag to " + "variables and parameters that are not in use.\n"; + return true; + } + } + return true; + } + +private: + clang::Rewriter m_rewriter; +}; +} // namespace + +static clang::FrontendPluginRegistry::Add + X("savchenko_m_UnusedVars_plugin", + "Adding the [[maybe_unused]] flag to variables and parameters if they " + "are not in use."); diff --git a/clang/test/compiler-course/savchenko_m_l1_v2_tests/savchenko_m_l1_v2_tests.cpp b/clang/test/compiler-course/savchenko_m_l1_v2_tests/savchenko_m_l1_v2_tests.cpp new file mode 100644 index 0000000000000..9d75f9c444358 --- /dev/null +++ b/clang/test/compiler-course/savchenko_m_l1_v2_tests/savchenko_m_l1_v2_tests.cpp @@ -0,0 +1,34 @@ +//// --help arg test //// +// RUN: %clang_cc1 -load %llvmshlibdir/UnusedVariables_Savchenko_Maxim_FIIT1_ClangAST%pluginext -plugin savchenko_m_UnusedVars_plugin -plugin-arg-savchenko_m_UnusedVars_plugin --help 2>&1 | FileCheck --check-prefix=HELP %s +// HELP: The plugin adds the {{\[\[maybe_unused\]\]}} flag to variables and parameters that are not in use. + + +//// main tests //// +// RUN: %clang_cc1 -load %llvmshlibdir/UnusedVariables_Savchenko_Maxim_FIIT1_ClangAST%pluginext -plugin savchenko_m_UnusedVars_plugin -fsyntax-only %s 2>&1 | FileCheck %s + +// CHECK: double used = 3.0; +// CHECK-NEXT: \[\[maybe_unused\]\] double unused = 2.0; +double used = 3.0; +double unused = 2.0; + +// CHECK: int foo1(int a, int b, \[\[maybe_unused\]\] int c) { +// CHECK-NEXT: \[\[maybe_unused\]\] int tmp = 47; +// CHECK-NEXT: int x = 24; +// CHECK-NEXT: int y = 54; +// CHECK-NEXT: \[\[maybe_unused\]\] int xy = x + y; +// CHECK-NEXT: return a + b; +int foo1(int a, int b, int c) { + int tmp = 47; + int x = 24; + int y = 54; + int xy = x + y; + return a + b; +} + +// CHECK: double mult(double a, double b, \[\[maybe_unused\]\] double c) { +// CHECK-NEXT: \[\[maybe_unused\]\] double result = a * b; +// CHECK-NEXT: return a * b * used; +double mult(double a, double b, double c) { + double result = a * b; + return a * b * used; +} From f6b95a33e6cdceab7d66ae8fc46f6ebf92ce3bfd Mon Sep 17 00:00:00 2001 From: Kuznetsov-Artyom <110616662+Kuznetsov-Artyom@users.noreply.github.com> Date: Tue, 25 Mar 2025 23:05:13 +0300 Subject: [PATCH 36/50] Add deadline lab 2 (#61) --- .github/deadline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/deadline.py b/.github/deadline.py index 485dc8a102deb..ca9a097e4b6b0 100644 --- a/.github/deadline.py +++ b/.github/deadline.py @@ -10,7 +10,7 @@ def main(): current_date = datetime.now(moscow_tz) deadline_date = { "lab:clang": datetime(2025, 3, 19, hour=19, tzinfo=moscow_tz), - "lab:llvm ir": datetime(2025, 6, 1, hour=19, tzinfo=moscow_tz), + "lab:llvm ir": datetime(2025, 4, 9, hour=19, tzinfo=moscow_tz), "lab:backend": datetime(2025, 6, 1, hour=19, tzinfo=moscow_tz), "lab:mlir": datetime(2025, 6, 1, hour=19, tzinfo=moscow_tz), } From 540cd5cc81c4f20a15b971c4ce5792582e7547f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D1=81=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=91=D0=B5=D1=81?= =?UTF-8?q?=D1=85=D0=BC=D0=B5=D0=BB=D1=8C=D0=BD=D0=BE=D0=B2=D0=B0?= <113203195+KseniyaBeskhmelnova@users.noreply.github.com> Date: Thu, 27 Mar 2025 07:53:05 +0300 Subject: [PATCH 37/50] =?UTF-8?q?=D0=91=D0=B5=D1=81=D1=85=D0=BC=D0=B5?= =?UTF-8?q?=D0=BB=D1=8C=D0=BD=D0=BE=D0=B2=D0=B0=20=D0=9A=D1=81=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F.=20=D0=9B=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=BD=D0=B0=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=B0=202:=20LLVM=20IR.=20=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD?= =?UTF-8?q?=D1=82=203.=20(#57)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Решение реализует пасс для LLVM, который анализирует модуль и ищет инструкции сложения (add). Если в модуле есть функция add, которая принимает два аргумента с типами, совпадающими с типами операндов инструкции add, то эта инструкция заменяется на вызов соответствующей функции. Пасс проверяет наличие такой функции в модуле, а затем проходит по всем функциям, исключая саму add, и ищет бинарные операторы сложения. Когда такие операторы обнаружены, и их операнды соответствуют типам аргументов функции add, они заменяются на вызов этой функции. В случае, если функция add не найдена или типы не совпадают, исходная инструкция остается неизменной. --- .../beskhmelnova_k_replace_add/CMakeLists.txt | 14 ++++ .../ReplaceAddPass.cpp | 84 +++++++++++++++++++ .../litTestReplaceAdd.ll | 46 ++++++++++ 3 files changed, 144 insertions(+) create mode 100644 llvm/compiler-course/llvm-ir/beskhmelnova_k_replace_add/CMakeLists.txt create mode 100644 llvm/compiler-course/llvm-ir/beskhmelnova_k_replace_add/ReplaceAddPass.cpp create mode 100644 llvm/test/compiler-course/beskhmelnova_k_replace_add/litTestReplaceAdd.ll diff --git a/llvm/compiler-course/llvm-ir/beskhmelnova_k_replace_add/CMakeLists.txt b/llvm/compiler-course/llvm-ir/beskhmelnova_k_replace_add/CMakeLists.txt new file mode 100644 index 0000000000000..fb3c03a2d461c --- /dev/null +++ b/llvm/compiler-course/llvm-ir/beskhmelnova_k_replace_add/CMakeLists.txt @@ -0,0 +1,14 @@ +set(Title "ReplaceAddPass") +set(Student "Beskhmelnova_Kseniya") +set(Group "FIIT1") +set(TARGET_NAME "${Title}_${Student}_${Group}_LLVM_IR") + +if (NOT WIN32 AND NOT CYGWIN) + file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) + add_llvm_pass_plugin(${TARGET_NAME} ${SOURCES} + DEPENDS + intrinsics_gen + BUILDTREE_ONLY + ) + set(LLVM_TEST_DEPENDS ${TARGET_NAME} ${LLVM_TEST_DEPENDS} PARENT_SCOPE) +endif() diff --git a/llvm/compiler-course/llvm-ir/beskhmelnova_k_replace_add/ReplaceAddPass.cpp b/llvm/compiler-course/llvm-ir/beskhmelnova_k_replace_add/ReplaceAddPass.cpp new file mode 100644 index 0000000000000..4decc906e3928 --- /dev/null +++ b/llvm/compiler-course/llvm-ir/beskhmelnova_k_replace_add/ReplaceAddPass.cpp @@ -0,0 +1,84 @@ +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/PassPlugin.h" +#include "llvm/Support/raw_ostream.h" +#include + +namespace { +struct ReplaceAddPass : llvm::PassInfoMixin { + llvm::SmallDenseMap opToFunc = { + {llvm::Instruction::Add, "add"}, + {llvm::Instruction::Sub, "sub"}, + {llvm::Instruction::Mul, "mul"}, + {llvm::Instruction::SDiv, "div"}}; + + llvm::PreservedAnalyses run(llvm::Function &F, + llvm::FunctionAnalysisManager &) { + if (llvm::any_of(opToFunc, [&](const auto &entry) { + return F.getName() == entry.second; + })) { + return llvm::PreservedAnalyses::all(); + } + + bool changed = false; + llvm::Module *M = F.getParent(); + + std::map funcMap; + for (llvm::Function &Func : M->functions()) { + for (const auto &[opcode, funcName] : opToFunc) { + if (Func.getName().contains(funcName) && Func.arg_size() == 2) { + funcMap[opcode] = &Func; + } + } + } + + if (funcMap.empty()) { + return llvm::PreservedAnalyses::all(); + } + + for (llvm::BasicBlock &BB : F) { + for (llvm::Instruction &I : llvm::make_early_inc_range(BB)) { + if (auto *binOp = llvm::dyn_cast(&I)) { + auto it = funcMap.find(binOp->getOpcode()); + if (it != funcMap.end()) { + llvm::Function *replacementFunction = it->second; + llvm::IRBuilder<> builder(binOp); + + llvm::Value *call = + builder.CreateCall(replacementFunction, {binOp->getOperand(0), + binOp->getOperand(1)}); + + call->setName(binOp->getName()); + binOp->replaceAllUsesWith(call); + binOp->eraseFromParent(); + changed = true; + } + } + } + } + return changed ? llvm::PreservedAnalyses::none() + : llvm::PreservedAnalyses::all(); + } + + static bool isRequired() { return true; } +}; +} // namespace + +extern "C" LLVM_ATTRIBUTE_WEAK llvm::PassPluginLibraryInfo +llvmGetPassPluginInfo() { + return {LLVM_PLUGIN_API_VERSION, "ReplaceAddPass", "0.1", + [](llvm::PassBuilder &PB) { + PB.registerPipelineParsingCallback( + [](llvm::StringRef name, llvm::FunctionPassManager &FPM, + llvm::ArrayRef) -> bool { + if (name == "ReplaceAddPass") { + FPM.addPass(ReplaceAddPass{}); + return true; + } + return false; + }); + }}; +} diff --git a/llvm/test/compiler-course/beskhmelnova_k_replace_add/litTestReplaceAdd.ll b/llvm/test/compiler-course/beskhmelnova_k_replace_add/litTestReplaceAdd.ll new file mode 100644 index 0000000000000..245660cf96798 --- /dev/null +++ b/llvm/test/compiler-course/beskhmelnova_k_replace_add/litTestReplaceAdd.ll @@ -0,0 +1,46 @@ +; RUN: opt -load-pass-plugin %llvmshlibdir/ReplaceAddPass_Beskhmelnova_Kseniya_FIIT1_LLVM_IR%pluginext \ +; RUN: -passes="ReplaceAddPass" -S %s | FileCheck %s + +; CHECK: define i32 @add(i32 %a, i32 %b) +; CHECK: %result = add i32 %a, %b +; CHECK: ret i32 %result + +; CHECK: define i32 @foo(i32 %x, i32 %y) +; CHECK: call i32 @add(i32 %x, i32 %y) +; CHECK: ret i32 %sum + +; CHECK-NOT: add i32 + +define i32 @add(i32 %a, i32 %b) { +entry: + %result = add i32 %a, %b + ret i32 %result +} + +define i32 @foo(i32 %x, i32 %y) { +entry: + %sum = add i32 %x, %y + ret i32 %sum +} + +; CHECK: define i32 @boo(i32 %x, i32 %y) +; CHECK: call i32 @sub(i32 %x, i32 %y) +; CHECK: ret i32 %sum + +; CHECK: define i32 @sub(i32 %a, i32 %b) +; CHECK: %result = sub i32 %a, %b +; CHECK: ret i32 %result + +; CHECK-NOT: sub i32 + +define i32 @boo(i32 %x, i32 %y) { +entry: + %sum = sub i32 %x, %y + ret i32 %sum +} + +define i32 @sub(i32 %a, i32 %b) { +entry: + %result = sub i32 %a, %b + ret i32 %result +} From 97f913a856cb6e03f1083c69596702700b03523b Mon Sep 17 00:00:00 2001 From: ascannel <113050263+ascannel@users.noreply.github.com> Date: Thu, 27 Mar 2025 17:53:23 +0300 Subject: [PATCH 38/50] Lopatin Ilya. Lab 2: LLVM IR. Option 1. (#53) This LLVM Pass optimizes floating point arithmetics in LLVM IR by merging `fmul` and `fadd` instructions into `llvm.fmuladd` intrinsic. ### Functionality: - Merge operations - Dead code elimination: automatically removes `fmul` and `fadd` instructions when they become unused - Works with `float` and `double` data types ### LLVM APIs Used: - `FunctionPass`: Analyzes and modifies functions within LLVM IR. - `IRBuilder<>`: Constructs intrinsics and manages instruction insertion. - `BinaryOperator`: Identifies FAdd and FMul operations. - `Intrinsic::fmuladd`: Generates the intrinsic. - `PreservedAnalyses`: Tracks whether IR analyses remain valid after transformations. ### Workflow: 1. Plugin Registration: Integrates with LLVM via `PassPlugin` for compatibility with modern passmanager. 2. Basic Block Traversal: Iterates through all basic blocks in a function. 3. Pattern Matching: Detects valid fmul+fadd patterns. 4. Intrinsic Replacement: Uses `IRBuilder` to create `llvm.fmuladd` and replaces the original instructions. 5. Cleanup: Safely removes orphaned fmul and fadd instructions. --- .../llvm-ir/lopatin_fmul_fadd/CMakeLists.txt | 14 +++ .../lopatin_fmul_fadd/fmulFaddMergePass.cpp | 91 +++++++++++++++++++ .../lopatin_fmul_fadd/litTestFmulFaddMerge.ll | 53 +++++++++++ 3 files changed, 158 insertions(+) create mode 100644 llvm/compiler-course/llvm-ir/lopatin_fmul_fadd/CMakeLists.txt create mode 100644 llvm/compiler-course/llvm-ir/lopatin_fmul_fadd/fmulFaddMergePass.cpp create mode 100644 llvm/test/compiler-course/lopatin_fmul_fadd/litTestFmulFaddMerge.ll diff --git a/llvm/compiler-course/llvm-ir/lopatin_fmul_fadd/CMakeLists.txt b/llvm/compiler-course/llvm-ir/lopatin_fmul_fadd/CMakeLists.txt new file mode 100644 index 0000000000000..67b0a19e150a3 --- /dev/null +++ b/llvm/compiler-course/llvm-ir/lopatin_fmul_fadd/CMakeLists.txt @@ -0,0 +1,14 @@ +set(Title "FmulFaddMergePass") +set(Student "LopatinIlya") +set(Group "FIIT3") +set(TARGET_NAME "${Title}_${Student}_${Group}_LLVM_IR") + +if (NOT WIN32 AND NOT CYGWIN) + file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) + add_llvm_pass_plugin(${TARGET_NAME} ${SOURCES} + DEPENDS + intrinsics_gen + BUILDTREE_ONLY + ) + set(LLVM_TEST_DEPENDS ${TARGET_NAME} ${LLVM_TEST_DEPENDS} PARENT_SCOPE) +endif() diff --git a/llvm/compiler-course/llvm-ir/lopatin_fmul_fadd/fmulFaddMergePass.cpp b/llvm/compiler-course/llvm-ir/lopatin_fmul_fadd/fmulFaddMergePass.cpp new file mode 100644 index 0000000000000..961b8ea78dbe2 --- /dev/null +++ b/llvm/compiler-course/llvm-ir/lopatin_fmul_fadd/fmulFaddMergePass.cpp @@ -0,0 +1,91 @@ +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/PassPlugin.h" +#include "llvm/Support/raw_ostream.h" + +namespace { +struct FmulFaddMergePass : llvm::PassInfoMixin { + llvm::PreservedAnalyses run(llvm::Function &Func, + llvm::FunctionAnalysisManager &) { + bool Changed = false; + + struct ReplacementStruct { + llvm::BinaryOperator *FAdd; + llvm::BinaryOperator *FMul; + llvm::Value *ThirdOperand; + }; + + for (llvm::BasicBlock &BB : Func) { + std::vector ToReplace; + + auto CheckOperand = + [](llvm::Value *Operand, + llvm::Value *OtherOperand) -> llvm::BinaryOperator * { + if (auto *FMul = llvm::dyn_cast(Operand)) { + if (FMul->getOpcode() == llvm::Instruction::FMul) { + return FMul; + } + } + return nullptr; + }; + + // 1st pass: Collect replacements + for (llvm::Instruction &Inst : BB) { + if (auto *FAdd = llvm::dyn_cast(&Inst)) { + if (FAdd->getOpcode() == llvm::Instruction::FAdd) { + llvm::Value *Op0 = FAdd->getOperand(0); + llvm::Value *Op1 = FAdd->getOperand(1); + + if (auto *FMul = CheckOperand(Op0, Op1)) { + ToReplace.push_back({FAdd, FMul, Op1}); + } else if (auto *FMul = CheckOperand(Op1, Op0)) { + ToReplace.push_back({FAdd, FMul, Op0}); + } + } + } + } + + // 2nd pass, replacing + for (const auto &Info : ToReplace) { + llvm::IRBuilder<> Builder(Info.FAdd); + llvm::Value *FMulAdd = Builder.CreateIntrinsic( + llvm::Intrinsic::fmuladd, Info.FMul->getType(), + {Info.FMul->getOperand(0), Info.FMul->getOperand(1), + Info.ThirdOperand}); + + Info.FAdd->replaceAllUsesWith(FMulAdd); + Info.FAdd->eraseFromParent(); + + if (Info.FMul->use_empty()) { + Info.FMul->eraseFromParent(); + } + + Changed = true; + } + } + + return Changed ? llvm::PreservedAnalyses::none() + : llvm::PreservedAnalyses::all(); + } + + static bool isRequired() { return true; } +}; +} // namespace + +extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo +llvmGetPassPluginInfo() { + return {LLVM_PLUGIN_API_VERSION, "FmulFaddMergePass", "0.1", + [](llvm::PassBuilder &PB) { + PB.registerPipelineParsingCallback( + [](llvm::StringRef Name, llvm::FunctionPassManager &FPM, + llvm::ArrayRef) -> bool { + if (Name == "FmulFaddMergePass") { + FPM.addPass(FmulFaddMergePass{}); + return true; + } + return false; + }); + }}; +} diff --git a/llvm/test/compiler-course/lopatin_fmul_fadd/litTestFmulFaddMerge.ll b/llvm/test/compiler-course/lopatin_fmul_fadd/litTestFmulFaddMerge.ll new file mode 100644 index 0000000000000..ff56f4d2fa5f2 --- /dev/null +++ b/llvm/test/compiler-course/lopatin_fmul_fadd/litTestFmulFaddMerge.ll @@ -0,0 +1,53 @@ +; RUN: opt -load-pass-plugin %llvmshlibdir/FmulFaddMergePass_LopatinIlya_FIIT3_LLVM_IR%pluginext \ +; RUN: -passes="FmulFaddMergePass" -S %s | FileCheck %s + +; CHECK-LABEL: @fmaDouble +; CHECK: call double @llvm.fmuladd.f64(double %A, double %B, double %C) +; CHECK-NOT: fmul double +; CHECK-NOT: fadd double +define dso_local noundef double @fmaDouble(double %A, double %B, double %C) { +entry: + %mul = fmul double %A, %B + %add = fadd double %mul, %C + ret double %add +} + +; CHECK-LABEL: @fmaFloat +; CHECK: call float @llvm.fmuladd.f32(float %A, float %B, float %C) +; CHECK-NOT: fmul float +; CHECK-NOT: fadd float +define dso_local noundef float @fmaFloat(float %A, float %B, float %C) { +entry: + %mul = fmul float %A, %B + %add = fadd float %mul, %C + ret float %add +} + +; CHECK-LABEL: @recursiveTest +; CHECK: fadd float %B, %C +; CHECK: fadd float %A, %B +; CHECK: call float @llvm.fmuladd.f32(float %add1, float %B, float %add) +define dso_local noundef float @recursiveTest(float %A, float %B, float %C) { +entry: + %add = fadd float %B, %C + %add1 = fadd float %A, %B + %mul = fmul float %add1, %B + %add2 = fadd float %add, %mul + ret float %add2 +} + +; CHECK-LABEL: @foo +; CHECK: call float @llvm.fmuladd.f32(float %A, float %B, float %C) +; CHECK: call float @llvm.fmuladd.f32(float %A, float %B, float 1.000000e+00) +; CHECK: fdiv float %0, %1 +; CHECK-NOT: fmul float +; CHECK-NOT: fadd float +define float @foo(float %A, float %B, float %C) { +entry: + %mul = fmul float %A, %B + %add = fadd float %mul, %C + %add1 = fadd float %mul, 1.000000e+00 + %div = fdiv float %add, %add1 + ret float %div +} + From f98e023b32ebc8754a19993420d81c328efc3e49 Mon Sep 17 00:00:00 2001 From: NeeMiyuki Date: Sat, 29 Mar 2025 14:23:05 +0300 Subject: [PATCH 39/50] =?UTF-8?q?=D0=A8=D1=83=D0=BB=D1=8C=D0=BF=D0=B8?= =?UTF-8?q?=D0=BD=20=D0=98=D0=BB=D1=8C=D1=8F.=20=D0=9B=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD=D0=B0=D1=8F=20=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=B0=201.=20Clang=20AST.=20=D0=92=D0=B0?= =?UTF-8?q?=D1=80=D0=B8=D0=B0=D0=BD=D1=82=203=20(#46)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Данный плагин для Clang анализирует исходный код на предмет неявных преобразований типов. Он использует RecursiveASTVisitor для обхода AST. Все случаи неявных преобразований фиксируются, сохраняя информацию об исходном и целевом типах, а так же их количестве в пределах функции. Результаты группируются по функциям и выводятся с общим числом преобразований в коде. --- .../CMakeLists.txt | 18 ++ .../shulpin_i_ClangAST_lab.cpp | 159 ++++++++++++++++++ .../shulpin_i_ClangAST_lab_tests.cpp | 90 ++++++++++ 3 files changed, 267 insertions(+) create mode 100644 clang/compiler-course/shulpin_i_implicit_conversion/CMakeLists.txt create mode 100644 clang/compiler-course/shulpin_i_implicit_conversion/shulpin_i_ClangAST_lab.cpp create mode 100644 clang/test/compiler-course/shulpin_i_implicit_conversion/shulpin_i_ClangAST_lab_tests.cpp diff --git a/clang/compiler-course/shulpin_i_implicit_conversion/CMakeLists.txt b/clang/compiler-course/shulpin_i_implicit_conversion/CMakeLists.txt new file mode 100644 index 0000000000000..d1920484cacab --- /dev/null +++ b/clang/compiler-course/shulpin_i_implicit_conversion/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "ClangAST_Lab") +set(Student "ShulpinIlya") +set(Group "FIIT1") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/shulpin_i_implicit_conversion/shulpin_i_ClangAST_lab.cpp b/clang/compiler-course/shulpin_i_implicit_conversion/shulpin_i_ClangAST_lab.cpp new file mode 100644 index 0000000000000..504a60e96f2a9 --- /dev/null +++ b/clang/compiler-course/shulpin_i_implicit_conversion/shulpin_i_ClangAST_lab.cpp @@ -0,0 +1,159 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include + +namespace { +class ImplicitVisitor final + : public clang::RecursiveASTVisitor { +public: + ImplicitVisitor() = default; + + bool VisitFunctionDecl(clang::FunctionDecl *Func) { + FunctionToCheck = Func->getNameInfo().getName().getAsString(); + return true; + } + + bool VisitCXXConstructExpr(clang::CXXConstructExpr *Ctor) { + if (!Ctor || Ctor->getNumArgs() == 0) { + return true; + } + + const auto *FirstArg = Ctor->getArg(0); + if (!FirstArg) { + return true; + } + + clang::QualType FromType = FirstArg->getType(); + clang::QualType ToType = Ctor->getType(); + + static std::unordered_map TypeComparisonCache; + std::string FromStr = FromType.getAsString(); + std::string ToStr = ToType.getAsString(); + + TypeComparisonCache.try_emplace(FromStr + ToStr, FromType == ToType); + + if (TypeComparisonCache[FromStr + ToStr]) { + return true; + } + + CastInfo.emplace_back(CastAgrigation{FunctionToCheck, std::move(FromStr), + std::move(ToStr), 1}); + return true; + } + + clang::QualType getRealType(clang::QualType Type) { + while (Type->getAs()) { + Type = Type->getAs()->getDecl()->getUnderlyingType(); + } + return Type.getCanonicalType(); + } + + bool VisitImplicitCastExpr(clang::ImplicitCastExpr *Cast) { + if (!Cast) { + return true; + } + + if (const auto *SubExpr = Cast->getSubExpr()) { + clang::CastKind Kind = Cast->getCastKind(); + if ((Kind == clang::CK_NoOp) || (Kind == clang::CK_LValueToRValue) || + (Kind == clang::CK_FunctionToPointerDecay)) { + return true; + } + + clang::QualType FromType = getRealType(SubExpr->getType()); + clang::QualType ToType = getRealType(Cast->getType()); + + if (!FromType.isNull() && !ToType.isNull() && + FromType.getTypePtr() != ToType.getTypePtr()) { + + std::string FromStr = FromType.getAsString(); + std::string ToStr = ToType.getAsString(); + + auto it = std::find_if( + CastInfo.begin(), CastInfo.end(), [&](const CastAgrigation &entry) { + return entry.FuncName == FunctionToCheck && + entry.FromType == FromStr && entry.ToType == ToStr; + }); + + if (it != CastInfo.end()) { + it->CastNum++; + } else { + CastInfo.emplace_back(CastAgrigation{ + FunctionToCheck, std::move(FromStr), std::move(ToStr), 1}); + } + } + } + return true; + } + + void CastsResult() { + std::string LastFunction; + + std::size_t totalCasts = + std::accumulate(CastInfo.begin(), CastInfo.end(), 0, + [](std::size_t sum, const CastAgrigation &cast) { + return sum + cast.CastNum; + }); + + for (const auto &Entry : CastInfo) { + if (Entry.FuncName != LastFunction) { + llvm::outs() << "Function " << Entry.FuncName << "\n"; + LastFunction = Entry.FuncName; + } + llvm::outs() << Entry.getCastDescription() << "\n"; + } + llvm::outs() << "Summary of total conversions: " << totalCasts << "\n"; + } + +private: + struct CastAgrigation { + std::string FuncName; + std::string FromType; + std::string ToType; + std::size_t CastNum; + + std::string getCastDescription() const { + return FromType + " -> " + ToType + ": " + std::to_string(CastNum); + } + }; + std::string FunctionToCheck; + std::vector CastInfo; +}; + +class ImplicitCastConsumer final : public clang::ASTConsumer { +public: + ImplicitCastConsumer() = default; + + void HandleTranslationUnit(clang::ASTContext &Context) override { + Visitor.TraverseDecl(Context.getTranslationUnitDecl()); + Visitor.CastsResult(); + } + +private: + ImplicitVisitor Visitor; +}; + +class ImplicitCastAction final : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &CI, llvm::StringRef) override { + return std::make_unique(); + } + + bool ParseArgs(const clang::CompilerInstance &CI, + const std::vector &Args) override { + return true; + } +}; + +} // namespace + +static clang::FrontendPluginRegistry::Add + X("ClangAST_Lab_ShulpinIlya_FIIT1_ClangAST", + "Counts implicit type conversions"); diff --git a/clang/test/compiler-course/shulpin_i_implicit_conversion/shulpin_i_ClangAST_lab_tests.cpp b/clang/test/compiler-course/shulpin_i_implicit_conversion/shulpin_i_ClangAST_lab_tests.cpp new file mode 100644 index 0000000000000..bb472f276c23c --- /dev/null +++ b/clang/test/compiler-course/shulpin_i_implicit_conversion/shulpin_i_ClangAST_lab_tests.cpp @@ -0,0 +1,90 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/ClangAST_Lab_ShulpinIlya_FIIT1_ClangAST%pluginext -plugin ClangAST_Lab_ShulpinIlya_FIIT1_ClangAST -fsyntax-only %s 2>&1 | FileCheck --match-full-lines %s + +// CHECK: Function sum +// CHECK-NEXT: float -> double: 1 +// CHECK-NEXT: int -> float: 1 + +double sum(int a, float b) { + return a + b; +} + +// CHECK: Function mul +// CHECK-NEXT: double -> int: 1 +// CHECK-NEXT: float -> double: 1 +// CHECK-NEXT: float -> int: 1 + +int mul(float a, float b) { + return a + sum(a, b); +} + +// CHECK: Function structConversion +// CHECK-NEXT: const Base -> Base: 1 +// CHECK-NEXT: const struct Derived -> const struct Base: 1 + +struct Base {}; +struct Derived : Base {}; + +void structConversion() { + Derived d; + Base b = d; +} + +// CHECK: Function testPointers +// CHECK-NEXT: char * -> void *: 1 +// CHECK-NEXT: int * -> void *: 1 + +void testPointers() { + char* cptr; + int* iptr; + void* v1 = cptr; + void* v2 = iptr; +} + +// CHECK: Function foo +// CHECK-NEXT: int -> float: 1 +// CHECK-NEXT: float -> int: 1 +// CHECK-NEXT: int * -> void *: 1 + +using Abra = float; +using Kadabra = int; + +void Alakazam(void*); + +void foo() { + Abra x = Kadabra(); + Kadabra y = x; + Alakazam(&y); +} + +// CHECK: Function createX +// CHECK-NEXT: int -> X: 1 + +class X { + int x; +public: + X(int val) : x(val) {} +}; + +X createX() { + return 10; +} + +// CHECK: Function multCast +// CHECK-NEXT: int -> float: 5 +// CHECK-NEXT: float -> int: 5 + +void multCast() { + float f1 = 1; + float f2 = 2; + float f3 = 3; + float f4 = 4; + float f5 = 5; + + int i1 = f1; + int i2 = f2; + int i3 = f3; + int i4 = f4; + int i5 = f5; +} + +// CHECK: Summary of total conversions: 23 From 7d13cdf8e61a672a988a8753fc8d78ea38bc5f3f Mon Sep 17 00:00:00 2001 From: Kirius257 <113035841+Kirius257@users.noreply.github.com> Date: Sat, 29 Mar 2025 14:35:22 +0300 Subject: [PATCH 40/50] =?UTF-8?q?=D0=A5=D0=BE=D0=BB=D0=B8=D0=BD=20=D0=9A?= =?UTF-8?q?=D0=B8=D1=80=D0=B8=D0=BB=D0=BB.=20=D0=9B=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD=D0=B0=D1=8F=20=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=B0=202:=20LLVM=20IR.=20=D0=92=D0=B0=D1=80?= =?UTF-8?q?=D0=B8=D0=B0=D0=BD=D1=82=204.=20(#60)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### [**ПОСТАНОВКА ЗАДАЧИ**] Написать transform LLVM Pass, который должен выполнять следующие оптимизации: - Замена в некотором тестовом файле инструкций деления, где это возможно, на инструкцию побитовый сдвиг вправо; |_ОГРАНИЧЕНИЯ_| Оптимизация применяется только в случае, если: 1. Побитовый сдвиг вправо согласно стандарту языка C++ применяется только к целочисленным операндам; 2. Правый операнд, отвечающий за кол-во бит сдвига, неотрицательный; 3. Количество бит, на которое выполняется сдвиг, не превышает число бит в двоичной записи левого операнда; ### [**ОПИСАНИЕ РЕШЕНИЯ**] **СРЕДСТВА, ОПРЕДЕЛЯЮЩИЕ ИНТЕРФЕЙС PASS** _Выбор подкласса PASS:_ FunctionPass Class Reference; _Уровень оптимизации:_ BasicBlock Class Reference; _Инструменты:_ IRBuilder; _Структура данных для замены инструкций:_ SmallVector; **АЛГОРИТМ ОБХОДА** 1. Создать два цикла: по базовым блокам функций и инструкциям этих блоков; 2. В каждом блоке найти все инструкции деления, для которых возможна замена на инструкцию побитовый сдвиг вправо и соблюдаются все ограничения; 3. Найденную инструкцию добавить в вектор для замены инструкций деления; 4. Выполнить замену инструкции деления на побитовый сдвиг с помощью конструктора IRBuilder. **РЕЗУЛЬТАТ** Optimized IR ### СПИСОК ИСПРАВЛЕНИЙ - Удалены артефакты в тестовом файле с расширением .cpp; - Добавлены пустые строки в конец исходных файлов; - endif() исключён из CMakeLists.txt для pass; - Убрана директива using из .cpp файла с pass; - Сгенерирован IR с удалением alloca, store, load инструкций; - .cpp файл с тестом удалён; - std::vector заменён на оптимизированный вариант llvm::SmallVector; --- .../llvm-ir/Kholin_K_DivOpt/CMakeLists.txt | 15 ++ .../Kholin_K_DivOpt/Kholin_K_DivOpt_pass.cpp | 89 ++++++++++ .../compiler-course/Kholin_K_DivOpt/test.ll | 164 ++++++++++++++++++ 3 files changed, 268 insertions(+) create mode 100644 llvm/compiler-course/llvm-ir/Kholin_K_DivOpt/CMakeLists.txt create mode 100644 llvm/compiler-course/llvm-ir/Kholin_K_DivOpt/Kholin_K_DivOpt_pass.cpp create mode 100644 llvm/test/compiler-course/Kholin_K_DivOpt/test.ll diff --git a/llvm/compiler-course/llvm-ir/Kholin_K_DivOpt/CMakeLists.txt b/llvm/compiler-course/llvm-ir/Kholin_K_DivOpt/CMakeLists.txt new file mode 100644 index 0000000000000..70d1991f42b3d --- /dev/null +++ b/llvm/compiler-course/llvm-ir/Kholin_K_DivOpt/CMakeLists.txt @@ -0,0 +1,15 @@ +set(Title "DivOptPass") +set(Student "KholinKirill") +set(Group "FIIT3") +set(TARGET_NAME "${Title}_${Student}_${Group}_LLVM_IR") + +if (NOT WIN32 AND NOT CYGWIN) + file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) + add_llvm_pass_plugin(${TARGET_NAME} ${SOURCES} + DEPENDS + intrinsics_gen + BUILDTREE_ONLY + ) + set(LLVM_TEST_DEPENDS ${TARGET_NAME} ${LLVM_TEST_DEPENDS} PARENT_SCOPE) +endif() + diff --git a/llvm/compiler-course/llvm-ir/Kholin_K_DivOpt/Kholin_K_DivOpt_pass.cpp b/llvm/compiler-course/llvm-ir/Kholin_K_DivOpt/Kholin_K_DivOpt_pass.cpp new file mode 100644 index 0000000000000..fff67f6d53644 --- /dev/null +++ b/llvm/compiler-course/llvm-ir/Kholin_K_DivOpt/Kholin_K_DivOpt_pass.cpp @@ -0,0 +1,89 @@ +#include "llvm/ADT/SmallVector.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instructions.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/PassPlugin.h" +#include "llvm/Support/raw_ostream.h" + +namespace { + +void visitor(llvm::Function &F) { + for (auto &BB : F) { // function cycle block// + llvm::SmallVector DivsToReplace; + for (auto &I : BB) { // instruction cycle block + if (auto *BinOp = llvm::dyn_cast(&I)) { + if (BinOp->getOpcode() == llvm::Instruction::SDiv || + BinOp->getOpcode() == llvm::Instruction::UDiv) { + llvm::Value *Divisor = BinOp->getOperand( + 1); // get right operand from result = lhs op rhs + if (llvm::ConstantInt *ConstantDivisor = + llvm::dyn_cast(Divisor)) { + if (ConstantDivisor->getValue() == 0) { // value of rhs + continue; + } + if (ConstantDivisor->getValue().isPowerOf2()) { // check degree 2 + if (BinOp->getOpcode() == llvm::Instruction::SDiv && + ConstantDivisor->isNegative()) { // negative case + continue; + } + DivsToReplace.push_back(BinOp); + } + } + } + } + } + + for (auto *BinOp : DivsToReplace) { // replace div with shift + uint32_t ShiftAmount = llvm::cast(BinOp->getOperand(1)) + ->getValue() + .exactLogBase2(); + + llvm::IRBuilder<> Builder(BinOp); // initialize builder + + llvm::Value *Shift = nullptr; + + if (BinOp->getOpcode() == llvm::Instruction::SDiv) { // signed + Shift = Builder.CreateAShr(BinOp->getOperand(0), ShiftAmount); + } else { // unsigned + Shift = Builder.CreateLShr(BinOp->getOperand(0), ShiftAmount); + } + + BinOp->replaceAllUsesWith(Shift); // replace + + BinOp->eraseFromParent(); // remove old + } + } +} + +struct DivOptPass : llvm::PassInfoMixin { + llvm::PreservedAnalyses run(llvm::Function &F, + llvm::FunctionAnalysisManager &) { + visitor(F); + return llvm::PreservedAnalyses::all(); // preserve all passes + } + + static bool isRequired() { return true; } +}; + +} // namespace + +llvm::PassPluginLibraryInfo getDivToShiftPluginInfo() { + return {LLVM_PLUGIN_API_VERSION, "DivOptPass", LLVM_VERSION_STRING, + [](llvm::PassBuilder &PB) { + PB.registerPipelineParsingCallback( + [](llvm::StringRef Name, llvm::FunctionPassManager &FPM, + llvm::ArrayRef) { + if (Name == "div-opt-pass") { + FPM.addPass(DivOptPass()); + return true; + } + return false; + }); + }}; +} + +extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo +llvmGetPassPluginInfo() { + return getDivToShiftPluginInfo(); +} diff --git a/llvm/test/compiler-course/Kholin_K_DivOpt/test.ll b/llvm/test/compiler-course/Kholin_K_DivOpt/test.ll new file mode 100644 index 0000000000000..9b915f4671460 --- /dev/null +++ b/llvm/test/compiler-course/Kholin_K_DivOpt/test.ll @@ -0,0 +1,164 @@ +; RUN: opt -load-pass-plugin %llvmshlibdir/DivOptPass_KholinKirill_FIIT3_LLVM_IR%pluginext -passes=div-opt-pass -S %s | FileCheck %s + +; CHECK-LABEL: define dso_local noundef i32 @_Z18unsigned_part_testjj(i32 noundef %lhs1, i32 noundef %lhs2) { +; CHECK-NEXT: entry: +; CHECK-NEXT: %0 = lshr i32 %lhs1, 1 +; CHECK-NEXT: %div1 = udiv i32 %lhs1, -2 +; CHECK-NEXT: %1 = lshr i32 %lhs2, 3 +; CHECK-NEXT: %add = add i32 %0, %div1 +; CHECK-NEXT: %add3 = add i32 %add, %1 + + +define dso_local noundef i32 @_Z18unsigned_part_testjj(i32 noundef %lhs1, i32 noundef %lhs2) #0 { +entry: + %div = udiv i32 %lhs1, 2 + %div1 = udiv i32 %lhs1, -2 + %div2 = udiv i32 %lhs2, 8 + %add = add i32 %div, %div1 + %add3 = add i32 %add, %div2 + ret i32 %add3 +} + +;unsigned unsigned_part_test(unsigned lhs1, unsigned lhs2) { +; unsigned int result_type_OK1 = lhs1 / 2; +; unsigned int UB1 = lhs1 / (-2); +; unsigned int result_type_OK2 = lhs2 / 8u; +; +; return result_type_OK1 + UB1 + result_type_OK2; +;} + +; CHECK-LABEL: define dso_local noundef i32 @_Z17signed_part1_testii(i32 noundef %lhs1, i32 noundef %lhs2) { +; CHECK-NEXT: entry: +; CHECK-NEXT: %0 = ashr i32 %lhs1, 6 +; CHECK-NEXT: %div1 = sdiv i32 %lhs1, -64 +; CHECK: %1 = ashr i32 %sub, 6 +; CHECK-NEXT: %div4 = sdiv i32 %sub, -64 +; CHECK-NEXT: %2 = lshr i32 %lhs2, 5 +; CHECK: %3 = lshr i32 %sub6, 5 +; CHECK-NEXT: %add = add nsw i32 %0, %div1 +; CHECK-NEXT: %add8 = add nsw i32 %add, %1 +; CHECK-NEXT: %add9 = add nsw i32 %add8, %div4 +; CHECK-NEXT: %add10 = add nsw i32 %add9, %2 +; CHECK-NEXT: %add11 = add nsw i32 %add10, %3 + +define dso_local noundef i32 @_Z17signed_part1_testii(i32 noundef %lhs1, i32 noundef %lhs2) #0 { +entry: + %div = sdiv i32 %lhs1, 64 + %div1 = sdiv i32 %lhs1, -64 + %sub = sub nsw i32 0, %lhs1 + %div2 = sdiv i32 %sub, 64 + %div4 = sdiv i32 %sub, -64 + %div5 = udiv i32 %lhs2, 32 + %sub6 = sub nsw i32 0, %lhs2 + %div7 = udiv i32 %sub6, 32 + %add = add nsw i32 %div, %div1 + %add8 = add nsw i32 %add, %div2 + %add9 = add nsw i32 %add8, %div4 + %add10 = add nsw i32 %add9, %div5 + %add11 = add nsw i32 %add10, %div7 + ret i32 %add11 +} + +;signed int signed_part1_test(signed int lhs1, signed int lhs2) { +; signed int result_type_OK3 = lhs1 / 64; +; signed int Not_UB1 = lhs1 / (-64); +; signed int result_type_OK4 = (-lhs1) / 64; +; signed int Not_UB2 = (-lhs1) / (-64); +; signed int result_type_OK5 = lhs2 / 32u; +; signed int UB2 = (-lhs2) / 32u; +; +; return result_type_OK3 + Not_UB1 + result_type_OK4 + +; Not_UB2 + result_type_OK5 + UB2; +;} + +; CHECK-LABEL: define dso_local noundef i32 @_Z17signed_part2_testii(i32 noundef %lhs1, i32 noundef %lhs2) { +; CHECK-NEXT: entry: +; CHECK-NEXT: %div = udiv i32 %lhs1, 3 +; CHECK: %div1 = udiv i32 %sub, 3 +; CHECK-NEXT: %0 = ashr i32 %lhs2, 2 +; CHECK: %1 = lshr i32 %sub3, 2 +; CHECK-NEXT: %add = add nsw i32 %div, %div1 +; CHECK-NEXT: %add5 = add nsw i32 %add, %0 +; CHECK-NEXT: %add6 = add nsw i32 %add5, %1 + +define dso_local noundef i32 @_Z17signed_part2_testii(i32 noundef %lhs1, i32 noundef %lhs2) #0 { +entry: + %div = udiv i32 %lhs1, 3 + %sub = sub nsw i32 0, %lhs1 + %div1 = udiv i32 %sub, 3 + %div2 = sdiv i32 %lhs2, 4 + %sub3 = sub nsw i32 0, %lhs2 + %div4 = udiv i32 %sub3, 4 + %add = add nsw i32 %div, %div1 + %add5 = add nsw i32 %add, %div2 + %add6 = add nsw i32 %add5, %div4 + ret i32 %add6 +} + +;signed int signed_part2_test(signed int lhs1, signed int lhs2) { +; signed int result_type_OK7 = lhs1 / 3u; +; signed int UB3 = (-lhs1) / 3u; +; signed int result_type_OK9 = lhs2 / 4; +; signed int UB4 = (-lhs2) / 4u; +; +; return result_type_OK7 + UB3 + result_type_OK9 + UB4; +;} + +; CHECK-LABEL: define dso_local noundef i32 @_Z11other_typesdix(double noundef %lhs1, i32 noundef %lhs2, i64 noundef %lhs3) { +; CHECK-NEXT: entry: +; CHECK-NEXT: %div = fdiv double %lhs1, 4.000000e+00 +; CHECK-NEXT: %div1 = fdiv double %lhs1, -4.000000e+00 +; CHECK-NEXT: %conv = sitofp i32 %lhs2 to float +; CHECK-NEXT: %div2 = fdiv float %conv, 0x4042BC2900000000 +; CHECK-NEXT: %conv3 = fptosi float %div2 to i32 +; CHECK-NEXT: %div5 = fdiv float %conv, 0xC042BC2900000000 +; CHECK-NEXT: %conv6 = fptosi float %div5 to i32 +; CHECK-NEXT: %0 = ashr i64 %lhs3, 5 +; CHECK-NEXT: %div8 = sdiv i64 %lhs3, -32 +; CHECK-NEXT: %add = fadd double %div, %div1 +; CHECK-NEXT: %conv9 = sitofp i32 %conv3 to double +; CHECK-NEXT: %add10 = fadd double %add, %conv9 +; CHECK-NEXT: %conv11 = sitofp i32 %conv6 to double +; CHECK-NEXT: %add12 = fadd double %add10, %conv11 +; CHECK-NEXT: %conv13 = sitofp i64 %0 to double +; CHECK-NEXT: %add14 = fadd double %add12, %conv13 +; CHECK-NEXT: %conv15 = sitofp i64 %div8 to double +; CHECK-NEXT: %add16 = fadd double %add14, %conv15 +; CHECK-NEXT: %conv17 = fptosi double %add16 to i32 + +define dso_local noundef i32 @_Z11other_typesdix(double noundef %lhs1, i32 noundef %lhs2, i64 noundef %lhs3) #0 { +entry: + %div = fdiv double %lhs1, 4.000000e+00 + %div1 = fdiv double %lhs1, -4.000000e+00 + %conv = sitofp i32 %lhs2 to float + %div2 = fdiv float %conv, 0x4042BC2900000000 + %conv3 = fptosi float %div2 to i32 + %div5 = fdiv float %conv, 0xC042BC2900000000 + %conv6 = fptosi float %div5 to i32 + %div7 = sdiv i64 %lhs3, 32 + %div8 = sdiv i64 %lhs3, -32 + %add = fadd double %div, %div1 + %conv9 = sitofp i32 %conv3 to double + %add10 = fadd double %add, %conv9 + %conv11 = sitofp i32 %conv6 to double + %add12 = fadd double %add10, %conv11 + %conv13 = sitofp i64 %div7 to double + %add14 = fadd double %add12, %conv13 + %conv15 = sitofp i64 %div8 to double + %add16 = fadd double %add14, %conv15 + %conv17 = fptosi double %add16 to i32 + ret i32 %conv17 +} + +;int other_types(double lhs1, int lhs2, long long lhs3) { +; double result_type_OK11 = lhs1 / 4; +; double result_type_OK12 = lhs1 / (-4); +; int result_type_OK13 = lhs2 / 37.47f; +; int result_type_OK14 = lhs2 / (-37.47f); +; long long result_type_OK15 = lhs3 / 32; +; long long result_type_OK16 = lhs3 / (-32); +; int over_bits = 32 / 64; +; +; return result_type_OK11 + result_type_OK12 + result_type_OK13 + +; result_type_OK14 + result_type_OK15 + result_type_OK16; +;} From 819375f758205d19379fbd07f8d14608ccd40ddd Mon Sep 17 00:00:00 2001 From: IonovvA <114287192+IonovvA@users.noreply.github.com> Date: Sat, 29 Mar 2025 15:46:16 +0300 Subject: [PATCH 41/50] =?UTF-8?q?=D0=98=D0=BE=D0=BD=D0=BE=D0=B2=D0=B0=20?= =?UTF-8?q?=D0=95=D0=BA=D0=B0=D1=82=D0=B5=D1=80=D0=B8=D0=BD=D0=B0.=20?= =?UTF-8?q?=D0=9B=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=202:=20LLVM?= =?UTF-8?q?=20IR.=20=D0=92=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=201.=20(#59?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Пасс анализирует LLVM IR, находя последовательности операций **fmul** (умножение) и **fadd** (сложение). Если эти операции используются вместе, он заменяет их одной инструкцией **llvm.fmuladd**, которая выполняет умножение и сложение за один шаг. Это делает код более эффективным. --- .../ionova_ekaterina_lab_2/CMakeLists.txt | 14 ++ .../ionova_ekaterina_lab_2/FMAPass.cpp | 80 ++++++++ .../ionova_ekaterina_lab_2/test.ll | 194 ++++++++++++++++++ 3 files changed, 288 insertions(+) create mode 100644 llvm/compiler-course/llvm-ir/ionova_ekaterina_lab_2/CMakeLists.txt create mode 100644 llvm/compiler-course/llvm-ir/ionova_ekaterina_lab_2/FMAPass.cpp create mode 100644 llvm/test/compiler-course/ionova_ekaterina_lab_2/test.ll diff --git a/llvm/compiler-course/llvm-ir/ionova_ekaterina_lab_2/CMakeLists.txt b/llvm/compiler-course/llvm-ir/ionova_ekaterina_lab_2/CMakeLists.txt new file mode 100644 index 0000000000000..549f286c07685 --- /dev/null +++ b/llvm/compiler-course/llvm-ir/ionova_ekaterina_lab_2/CMakeLists.txt @@ -0,0 +1,14 @@ +set(Title "FMAPass") +set(Student "Ionova_Ekaterina") +set(Group "FIIT1") +set(TARGET_NAME "${Title}_${Student}_${Group}_LLVM_IR") + +if (NOT WIN32 AND NOT CYGWIN) + file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) + add_llvm_pass_plugin(${TARGET_NAME} ${SOURCES} + DEPENDS + intrinsics_gen + BUILDTREE_ONLY + ) + set(LLVM_TEST_DEPENDS ${TARGET_NAME} ${LLVM_TEST_DEPENDS} PARENT_SCOPE) +endif() diff --git a/llvm/compiler-course/llvm-ir/ionova_ekaterina_lab_2/FMAPass.cpp b/llvm/compiler-course/llvm-ir/ionova_ekaterina_lab_2/FMAPass.cpp new file mode 100644 index 0000000000000..3d16a3316bc4a --- /dev/null +++ b/llvm/compiler-course/llvm-ir/ionova_ekaterina_lab_2/FMAPass.cpp @@ -0,0 +1,80 @@ +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/PassPlugin.h" + +namespace { +struct FMAPass : llvm::PassInfoMixin { + llvm::PreservedAnalyses run(llvm::Function &F, + llvm::FunctionAnalysisManager &) { + bool Changed = false; + + for (llvm::BasicBlock &BB : F) { + for (llvm::Instruction &I : llvm::make_early_inc_range(BB)) { + if (auto *AddOp = llvm::dyn_cast(&I)) { + if (AddOp->getOpcode() != llvm::Instruction::FAdd) { + continue; + } + + for (unsigned i = 0; i < 2; ++i) { + if (auto *MultiplyOp = llvm::dyn_cast( + AddOp->getOperand(i))) { + if (MultiplyOp->getOpcode() == llvm::Instruction::FMul && + canReplaceFMul(MultiplyOp, AddOp)) { + llvm::Value *Addend = AddOp->getOperand(1 - i); + replaceWithFMA(AddOp, MultiplyOp, Addend); + Changed = true; + + break; + } + } + } + } + } + } + + return Changed ? llvm::PreservedAnalyses::none() + : llvm::PreservedAnalyses::all(); + } + + static bool isRequired() { return true; } + +private: + bool canReplaceFMul(llvm::BinaryOperator *MultiplyOp, + llvm::BinaryOperator *AddOp) { + return MultiplyOp->getOpcode() == llvm::Instruction::FMul && + MultiplyOp->hasOneUse(); + } + + void replaceWithFMA(llvm::BinaryOperator *AddOp, + llvm::BinaryOperator *MultiplyOp, llvm::Value *Addend) { + llvm::IRBuilder<> Builder(AddOp); + llvm::Value *FMA = Builder.CreateIntrinsic( + llvm::Intrinsic::fmuladd, {MultiplyOp->getType()}, + {MultiplyOp->getOperand(0), MultiplyOp->getOperand(1), Addend}, nullptr, + "fma"); + + AddOp->replaceAllUsesWith(FMA); + AddOp->eraseFromParent(); + + if (MultiplyOp->use_empty()) { + MultiplyOp->eraseFromParent(); + } + } +}; +} // namespace + +extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo +llvmGetPassPluginInfo() { + return {LLVM_PLUGIN_API_VERSION, "FMAPass", "0.1", [](llvm::PassBuilder &PB) { + PB.registerPipelineParsingCallback( + [](llvm::StringRef name, llvm::FunctionPassManager &FPM, + llvm::ArrayRef) -> bool { + if (name == "fmapass") { + FPM.addPass(FMAPass{}); + return true; + } + return false; + }); + }}; +} diff --git a/llvm/test/compiler-course/ionova_ekaterina_lab_2/test.ll b/llvm/test/compiler-course/ionova_ekaterina_lab_2/test.ll new file mode 100644 index 0000000000000..5ac6585349113 --- /dev/null +++ b/llvm/test/compiler-course/ionova_ekaterina_lab_2/test.ll @@ -0,0 +1,194 @@ +; RUN: opt -load-pass-plugin %llvmshlibdir/FMAPass_Ionova_Ekaterina_FIIT1_LLVM_IR%pluginext \ +; RUN: -passes="fmapass" -S %s | FileCheck %s + +; double f1(double a, double b, double c) { +; return a * b + c; +; } + +; CHECK-LABEL: @_Z2f1ddd +; CHECK-NEXT: %fma = call double @llvm.fmuladd.f64(double %0, double %1, double %2) +; CHECK-NEXT: ret double %fma + +define dso_local noundef double @_Z2f1ddd(double noundef %0, double noundef %1, double noundef %2) local_unnamed_addr { + %4 = fmul double %0, %1 + %5 = fadd double %4, %2 + ret double %5 +} + + +; double f2(double a, double b, double c, double d, double e) { +; double result1 = a * b + c; +; double result2 = d * e + result1; +; return result2; +; } + +; CHECK-LABEL: @_Z2f2ddddd +; CHECK-NEXT: %fma = call double @llvm.fmuladd.f64(double %0, double %1, double %2) +; CHECK-NEXT: %fma1 = call double @llvm.fmuladd.f64(double %3, double %4, double %fma) +; CHECK-NEXT: ret double %fma1 + +define dso_local noundef double @_Z2f2ddddd(double noundef %0, double noundef %1, double noundef %2, double noundef %3, double noundef %4) local_unnamed_addr { + %6 = fmul double %0, %1 + %7 = fadd double %6, %2 + %8 = fmul double %3, %4 + %9 = fadd double %7, %8 + ret double %9 +} + + +; double f3(double a, double b, double c, double d) { +; double result1 = a * b; +; double result3 = result1 + c; +; double result4 = result1 + d; +; return result3 + result4; +; } + +; CHECK-LABEL: @_Z2f3dddd +; CHECK: %5 = fmul double %0, %1 +; CHECK-NEXT: %6 = fadd double %5, %2 +; CHECK-NEXT: %7 = fadd double %5, %3 +; CHECK-NEXT: %8 = fadd double %6, %7 +; CHECK-NEXT: ret double %8 + +define dso_local noundef double @_Z2f3dddd(double noundef %0, double noundef %1, double noundef %2, double noundef %3) local_unnamed_addr { + %5 = fmul double %0, %1 + %6 = fadd double %5, %2 + %7 = fadd double %5, %3 + %8 = fadd double %6, %7 + ret double %8 +} + + +; double f4(double a, double b, double c) { +; double result1 = a * b; +; double result2 = c + 1.0; +; return result1 + result2; +; } + +; CHECK-LABEL: @_Z2f4ddd +; CHECK: %4 = fadd double %2, 1.000000e+00 +; CHECK-NEXT: %fma = call double @llvm.fmuladd.f64(double %0, double %1, double %4) +; CHECK-NEXT: ret double %fma + +define dso_local noundef double @_Z2f4ddd(double noundef %0, double noundef %1, double noundef %2) local_unnamed_addr { + %4 = fmul double %0, %1 + %5 = fadd double %2, 1.000000e+00 + %6 = fadd double %4, %5 + ret double %6 +} + + +; double f5(double a, double b, double c, double d) { +; double result1 = a * b + c; +; double result2 = result1 * d + c; +; return result2; +; } + +; CHECK-LABEL: @_Z2f5dddd +; CHECK-NEXT: %fma = call double @llvm.fmuladd.f64(double %0, double %1, double %2) +; CHECK-NEXT: %fma1 = call double @llvm.fmuladd.f64(double %fma, double %3, double %2) +; CHECK-NEXT: ret double %fma1 + +define dso_local noundef double @_Z2f5dddd(double noundef %0, double noundef %1, double noundef %2, double noundef %3) local_unnamed_addr { + %5 = fmul double %0, %1 + %6 = fadd double %5, %2 + %7 = fmul double %6, %3 + %8 = fadd double %7, %2 + ret double %8 +} + + +; float f6(float a, float b, float c) { +; return a * b + c; +; } + +; CHECK-LABEL: @_Z2f6fff +; CHECK-NEXT: %fma = call float @llvm.fmuladd.f32(float %0, float %1, float %2) +; CHECK-NEXT: ret float %fma + +define dso_local noundef float @_Z2f6fff(float noundef %0, float noundef %1, float noundef %2) local_unnamed_addr { + %4 = fmul float %0, %1 + %5 = fadd float %4, %2 + ret float %5 +} + + +; float f7(float a, float b, float c, float d, float e) { +; float result1 = a * b + c; +; float result2 = d * e + result1; +; return result2; +; } + +; CHECK-LABEL: @_Z2f7fffff +; CHECK-NEXT: %fma = call float @llvm.fmuladd.f32(float %0, float %1, float %2) +; CHECK-NEXT: %fma1 = call float @llvm.fmuladd.f32(float %3, float %4, float %fma) +; CHECK-NEXT: ret float %fma1 + +define dso_local noundef float @_Z2f7fffff(float noundef %0, float noundef %1, float noundef %2, float noundef %3, float noundef %4) local_unnamed_addr { + %6 = fmul float %0, %1 + %7 = fadd float %6, %2 + %8 = fmul float %3, %4 + %9 = fadd float %7, %8 + ret float %9 +} + + +; int f8(int a, int b, int c) { +; return a * b + c; +; } + +; CHECK-LABEL: @_Z2f8iii +; CHECK: %4 = mul nsw i32 %0, %1 +; CHECK-NEXT: %5 = add nsw i32 %4, %2 +; CHECK-NEXT: ret i32 %5 + +define dso_local noundef i32 @_Z2f8iii(i32 noundef %0, i32 noundef %1, i32 noundef %2) local_unnamed_addr { + %4 = mul nsw i32 %0, %1 + %5 = add nsw i32 %4, %2 + ret i32 %5 +} + + +; int f9(int a, int b, int c, int d, int e) { +; int result1 = a * b + c; +; int result2 = d * e + result1; +; return result2; +; } + +; CHECK-LABEL: @_Z2f9iiiii +; CHECK: %6 = mul nsw i32 %0, %1 +; CHECK-NEXT: %7 = add nsw i32 %6, %2 +; CHECK-NEXT: %8 = mul nsw i32 %3, %4 +; CHECK-NEXT: %9 = add nsw i32 %7, %8 +; CHECK-NEXT: ret i32 %9 + +define dso_local noundef i32 @_Z2f9iiiii(i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3, i32 noundef %4) local_unnamed_addr { + %6 = mul nsw i32 %0, %1 + %7 = add nsw i32 %6, %2 + %8 = mul nsw i32 %3, %4 + %9 = add nsw i32 %7, %8 + ret i32 %9 +} + + +; float foo(float a, float b, float c) { +; float t1 = a * b; +; float t2 = t1 + c; +; float t3 = t2 / t1 + 1; +; return t3; +; } + +; CHECK-LABEL: @_Z3foofff +; CHECK: %4 = fmul float %0, %1 +; CHECK-NEXT: %5 = fadd float %4, %2 +; CHECK-NEXT: %6 = fdiv float %5, %4 +; CHECK-NEXT: %7 = fadd float %6, 1.000000e+00 +; CHECK-NEXT: ret float %7 + +define dso_local noundef float @_Z3foofff(float noundef %0, float noundef %1, float noundef %2) local_unnamed_addr { + %4 = fmul float %0, %1 + %5 = fadd float %4, %2 + %6 = fdiv float %5, %4 + %7 = fadd float %6, 1.000000e+00 + ret float %7 +} From 086a03c3343de269a2167814e308dbfe3ba6429e Mon Sep 17 00:00:00 2001 From: MaximChizhov <113035533+MaximChizhov@users.noreply.github.com> Date: Sat, 29 Mar 2025 16:10:46 +0300 Subject: [PATCH 42/50] =?UTF-8?q?=D0=A7=D0=B8=D0=B6=D0=BE=D0=B2=20=D0=9C?= =?UTF-8?q?=D0=B0=D0=BA=D1=81=D0=B8=D0=BC.=20=D0=9B=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BD=D0=B0=D1=8F=20=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=B0=201.=20Clang=20AST.=20=D0=92=D0=B0?= =?UTF-8?q?=D1=80=D0=B8=D0=B0=D0=BD=D1=82=203.=20(#41)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Плагин анализирует абстрактное синтаксическое дерево (AST) программы с использованием механизма RecursiveASTVisitor. Его основная задача заключается в выявлении и подсчете неявных преобразований типов в коде. Для этого применяется класс ImplicitCastVisitor, который перехватывает узлы ImplicitCastExpr, соответствующие неявным преобразованиям типов. При этом игнорируются преобразования, не оказывающие влияния на семантику программы, такие как LValueToRValue, NoOp и FunctionToPointerDecay. Для каждого обнаруженного преобразования извлекаются исходный и целевой типы, а также контекст, в котором оно произошло (например, функция). --------- Co-authored-by: Arseniy Obolenskiy Co-authored-by: Kuznetsov-Artyom <110616662+Kuznetsov-Artyom@users.noreply.github.com> --- .../CMakeLists.txt | 18 +++ .../chizhov_m_implicit_conversions.cpp | 133 ++++++++++++++++++ .../chizhov_m_implicit_conversions.cpp | 73 ++++++++++ 3 files changed, 224 insertions(+) create mode 100644 clang/compiler-course/chizhov_m_implicit_conversions/CMakeLists.txt create mode 100644 clang/compiler-course/chizhov_m_implicit_conversions/chizhov_m_implicit_conversions.cpp create mode 100644 clang/test/compiler-course/chizhov_m_implicit_conversions/chizhov_m_implicit_conversions.cpp diff --git a/clang/compiler-course/chizhov_m_implicit_conversions/CMakeLists.txt b/clang/compiler-course/chizhov_m_implicit_conversions/CMakeLists.txt new file mode 100644 index 0000000000000..cf5c01f8e30a4 --- /dev/null +++ b/clang/compiler-course/chizhov_m_implicit_conversions/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "Implicit_Conv") +set(Student "Chizhov_Maxim") +set(Group "FIIT3") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/chizhov_m_implicit_conversions/chizhov_m_implicit_conversions.cpp b/clang/compiler-course/chizhov_m_implicit_conversions/chizhov_m_implicit_conversions.cpp new file mode 100644 index 0000000000000..d03d42e0ae7fe --- /dev/null +++ b/clang/compiler-course/chizhov_m_implicit_conversions/chizhov_m_implicit_conversions.cpp @@ -0,0 +1,133 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +namespace { + +class ImplicitCastVisitor final + : public clang::RecursiveASTVisitor { +public: + void Conversions(const clang::QualType &FromType, + const clang::QualType &ToType) { + if (FromType == ToType) { + return; + } + + clang::QualType canonicalFromType = FromType.getCanonicalType(); + clang::QualType canonicalToType = ToType.getCanonicalType(); + + std::string fromTypeStr = canonicalFromType.getAsString(); + std::string toTypeStr = canonicalToType.getAsString(); + + std::string conversion = fromTypeStr + " -> " + toTypeStr; + + bool foundFunction = false; + for (auto &entry : converList) { + if (entry.funcName == currentFunction) { + bool foundConversion = false; + for (auto &[conversionStr, count] : entry.conv) { + if (conversionStr == conversion) { + count++; + foundConversion = true; + break; + } + } + + if (!foundConversion) { + entry.conv.push_back({conversion, 1}); + } + + foundFunction = true; + break; + } + } + + if (!foundFunction) { + Conv newConv; + newConv.funcName = currentFunction; + newConv.conv.push_back({conversion, 1}); + converList.push_back(newConv); + } + } + + bool VisitFunctionDecl(clang::FunctionDecl *Func) { + currentFunction = Func->getNameInfo().getName().getAsString(); + return true; + } + + bool VisitCXXConstructExpr(clang::CXXConstructExpr *construct) { + if (construct->getNumArgs() == 1) { + clang::QualType FromType = construct->getArg(0)->getType(); + clang::QualType ToType = construct->getType(); + Conversions(FromType, ToType); + } + return true; + } + + bool VisitImplicitCastExpr(clang::ImplicitCastExpr *Cast) { + clang::CastKind Kind = Cast->getCastKind(); + if (Kind == clang::CK_NoOp || Kind == clang::CK_LValueToRValue || + Kind == clang::CK_FunctionToPointerDecay) { + return true; + } + + clang::QualType FromType = Cast->getSubExpr()->getType(); + clang::QualType ToType = Cast->getType(); + + Conversions(FromType, ToType); + + return true; + } + + void PrintResults() { + for (const auto &Conv : converList) { + llvm::outs() << "Function " << Conv.funcName << "\n"; + + for (const auto &[conversion, count] : Conv.conv) { + llvm::outs() << " " << conversion << ": " << count << "\n"; + } + } + } + +private: + struct Conv { + std::string funcName; + std::vector> conv; + }; + std::string currentFunction; + std::vector converList; +}; + +class ImplicitCastConsumer final : public clang::ASTConsumer { +public: + void HandleTranslationUnit(clang::ASTContext &Context) override { + Visitor.TraverseDecl(Context.getTranslationUnitDecl()); + Visitor.PrintResults(); + } + +private: + ImplicitCastVisitor Visitor; +}; + +class ImplicitCastAction final : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &C, llvm::StringRef) override { + return std::make_unique(); + } + + bool ParseArgs(const clang::CompilerInstance &C, + const std::vector &Args) override { + return true; + } +}; + +} // namespace + +static clang::FrontendPluginRegistry::Add + X("Implicit_Conv_Chizhov_Maxim_FIIT3_ClangAST", + "Counts implicit type conversions"); diff --git a/clang/test/compiler-course/chizhov_m_implicit_conversions/chizhov_m_implicit_conversions.cpp b/clang/test/compiler-course/chizhov_m_implicit_conversions/chizhov_m_implicit_conversions.cpp new file mode 100644 index 0000000000000..df6aefbe39e01 --- /dev/null +++ b/clang/test/compiler-course/chizhov_m_implicit_conversions/chizhov_m_implicit_conversions.cpp @@ -0,0 +1,73 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/Implicit_Conv_Chizhov_Maxim_FIIT3_ClangAST%pluginext -plugin Implicit_Conv_Chizhov_Maxim_FIIT3_ClangAST -fsyntax-only %s 2>&1 | FileCheck --match-full-lines %s + +using Abrakadabra = int; +#define int Abrakadabra + +// CHECK: Function foo +// CHECK-NEXT: float -> int: 1 +// CHECK-NEXT: int -> double: 1 +// CHECK-NEXT: double -> int: 1 + +void foo(float a) { + int tmp = a; + double tmp2 = tmp; + Abrakadabra tmp3 = tmp2; +} + +// CHECK: Function sum +// CHECK-NEXT: float -> double: 1 +// CHECK-NEXT: int -> float: 1 +double sum(int a, float b) { + return a + b; +} + +// CHECK: Function mul +// CHECK-NEXT: double -> int: 1 +// CHECK-NEXT: float -> double: 1 +// CHECK-NEXT: float -> int: 1 + +int mul(float a, float b) { + return a + sum(a, b); +} + +// CHECK: Function Create +// CHECK-NEXT: int -> class Class: 1 +class Class { + int x; + public: + Class(int); +}; + +Class Create() { + return 1; +} + +// CHECK: Function LongToFloat +// CHECK-NEXT: long long -> float: 1 + +float LongToFloat() { + long long x = 9223372036854775807LL; + float y = x; + return y; +} + +// CHECK: Function CharToInt +// CHECK-NEXT: char -> int: 1 + +int CharToInt() { + char x = 'c'; + int y = x; + return y; +} + +// CHECK: Function PointerImplicitCast +// CHECK-NEXT: int * -> void *: 1 + +void PointerImplicitCast(int* ptr) { + void* voidPtr = ptr; +} + + +int NoImplicitCasts(int a) { + return a; +} From 01c68701be8152498c0baea911500e3f38bbc45e Mon Sep 17 00:00:00 2001 From: Ivan Lysov <111500735+Ivan-Lysov@users.noreply.github.com> Date: Sun, 30 Mar 2025 22:17:20 +0300 Subject: [PATCH 43/50] =?UTF-8?q?=D0=9B=D1=8B=D1=81=D0=BE=D0=B2=20=D0=98?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD.=20=D0=9B=D0=B0=D0=B1=D0=BE=D1=80=D0=B0?= =?UTF-8?q?=D1=82=D0=BE=D1=80=D0=BD=D0=B0=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=B0=201.=20Clang=20AST.=20=D0=92=D0=B0=D1=80=D0=B8?= =?UTF-8?q?=D0=B0=D0=BD=D1=82=202=20(#42)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Плагин который автоматически помечает неиспользуемые переменные и параметры атрибутом [[maybe_unused]]. Плагин анализирует AST, находит неиспользуемые переменные (VarDecl) и параметры функций (ParmVarDecl), после чего добавляет к ним атрибут [[maybe_unused]] с помощью Rewriter. --- .../lysov_ivan_1stLabs/CMakeLists.txt | 18 +++++ .../lysov_ivan_1stLabs/lysov_ivan_1stLabs.cpp | 76 +++++++++++++++++++ .../lysov_ivan_1stLabsTests/test.cpp | 40 ++++++++++ 3 files changed, 134 insertions(+) create mode 100644 clang/compiler-course/lysov_ivan_1stLabs/CMakeLists.txt create mode 100644 clang/compiler-course/lysov_ivan_1stLabs/lysov_ivan_1stLabs.cpp create mode 100644 clang/test/compiler-course/lysov_ivan_1stLabsTests/test.cpp diff --git a/clang/compiler-course/lysov_ivan_1stLabs/CMakeLists.txt b/clang/compiler-course/lysov_ivan_1stLabs/CMakeLists.txt new file mode 100644 index 0000000000000..64b1eff998eda --- /dev/null +++ b/clang/compiler-course/lysov_ivan_1stLabs/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "unUsedVarPlugin") +set(Student "LysovIvan") +set(Group "FIIT3") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/lysov_ivan_1stLabs/lysov_ivan_1stLabs.cpp b/clang/compiler-course/lysov_ivan_1stLabs/lysov_ivan_1stLabs.cpp new file mode 100644 index 0000000000000..a06bd563d2324 --- /dev/null +++ b/clang/compiler-course/lysov_ivan_1stLabs/lysov_ivan_1stLabs.cpp @@ -0,0 +1,76 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/Attr.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/raw_ostream.h" + +namespace { +class UnusedVarMyVisitor final + : public clang::RecursiveASTVisitor { +public: + UnusedVarMyVisitor(clang::ASTContext *ctx, clang::Rewriter &rwrt) + : context(ctx), rewriter(rwrt) {} + bool VisitVarDecl(clang::VarDecl *var) { + if (!var->isUsed() && !var->hasAttr()) { + clang::SourceLocation loc = var->getSourceRange().getBegin(); + rewriter.InsertText(loc, "[[maybe_unused]]"); + } + return true; + } + + bool VisitParmVarDecl(clang::ParmVarDecl *param) { + if (!param->isUsed() && !param->hasAttr()) { + clang::SourceLocation loc = param->getSourceRange().getBegin(); + rewriter.InsertText(loc, "[[maybe_unused]]"); + } + return true; + } + +private: + clang::ASTContext *context; + clang::Rewriter &rewriter; +}; + +class UnusedVarConsumer final : public clang::ASTConsumer { +public: + UnusedVarConsumer(clang::ASTContext *ctx, clang::Rewriter &rwrt) + : Label(ctx, rwrt) {} + + void HandleTranslationUnit(clang::ASTContext &ctx) override { + Label.TraverseDecl(ctx.getTranslationUnitDecl()); + } + +private: + UnusedVarMyVisitor Label; +}; + +class UnusedVarAction final : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + rewriter.setSourceMgr(ci.getSourceManager(), ci.getLangOpts()); + return std::make_unique(&ci.getASTContext(), rewriter); + } + + bool ParseArgs(const clang::CompilerInstance &ci, + const std::vector &args) override { + return true; + } + + void EndSourceFileAction() override { + rewriter.getEditBuffer(rewriter.getSourceMgr().getMainFileID()) + .write(llvm::outs()); + } + +private: + clang::Rewriter rewriter; +}; + +} // namespace + +static clang::FrontendPluginRegistry::Add + X("unUsedVarPlugin_LysovIvan_FIIT3_ClangAST", + "The plugin detects unused variables and function parameters in code and " + "adds the [[maybe_unused]] attribute."); diff --git a/clang/test/compiler-course/lysov_ivan_1stLabsTests/test.cpp b/clang/test/compiler-course/lysov_ivan_1stLabsTests/test.cpp new file mode 100644 index 0000000000000..db092444581c0 --- /dev/null +++ b/clang/test/compiler-course/lysov_ivan_1stLabsTests/test.cpp @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/unUsedVarPlugin_LysovIvan_FIIT3_ClangAST%pluginext -plugin unUsedVarPlugin_LysovIvan_FIIT3_ClangAST %s -fsyntax-only 2>&1 | FileCheck %s + +//CHECK: int FunctionFromTheProblemStatement(int a, int b, int c) { +//CHECK: \[\[maybe_unused\]\] double value = 0.0; +//CHECK: return a + b; +int FunctionFromTheProblemStatement(int a, int b, int c) { + double value = 0.0; + return a + b; +} + +//CHECK: void test_params(int used, \[\[maybe_unused\]\] int unused) { +//CHECK: int x = used; +//CHECK: return x; +int test_params(int used, int unused) { + int x = used; + return x; +} + +//CHECK: \[\[maybe_unused\]\] int globalVar = 6; +int globalVar = 6; + +//CHECK: void checkWithoutReturn(int a, \[\[maybe_unused\]\] long d){ +//CHECK: \[\[maybe_unused\]\] int g = a; +void checkWithoutReturn(int a, long d){ + int g = a; +} + +//CHECK: void checkConstantParamsFunction(\[\[maybe_unused\]\] const int x, float j){ +//CHECK: \[\[maybe_unused\]\] float y = j ; +void checkConstantParamsFunction(const int x, float j){ + float y = j; +} + +//CHECK: void funcforReview(){ +//CHECK: int x __attribute__((unused)); +//CHECK: \[\[maybe_unused\]\] int y; +void funcforReview() { + int x __attribute__((unused)); + int y; +} From 5a263a082cb057e71d8773195682f31960a33635 Mon Sep 17 00:00:00 2001 From: OlgaKotleta <90023764+OlgaKotleta@users.noreply.github.com> Date: Wed, 2 Apr 2025 09:01:31 +0300 Subject: [PATCH 44/50] Mamaeva _Olga_Lab1_Var3 (#35) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Плагин для Clang, который анализирует неявные преобразования типов в программах на C++. Он использует RecursiveASTVisitor для обхода абстрактного синтаксического дерева (AST) и перехватывает узлы ImplicitCastExpr, соответствующие неявным преобразованиям. Игнорируются тривиальные преобразования, такие как LValueToRValue, NoOp и FunctionToPointerDecay. --------- Co-authored-by: Kuznetsov-Artyom <110616662+Kuznetsov-Artyom@users.noreply.github.com> --- .../clang-plugin-Mamaeva/CMakeLists.txt | 18 +++ .../clang-plugin-Mamaeva/myClangPlugin.cpp | 127 ++++++++++++++++++ .../clang-plugin-Mamaeva/mamaeva-test.cpp | 63 +++++++++ 3 files changed, 208 insertions(+) create mode 100644 clang/compiler-course/clang-plugin-Mamaeva/CMakeLists.txt create mode 100644 clang/compiler-course/clang-plugin-Mamaeva/myClangPlugin.cpp create mode 100644 clang/test/compiler-course/clang-plugin-Mamaeva/mamaeva-test.cpp diff --git a/clang/compiler-course/clang-plugin-Mamaeva/CMakeLists.txt b/clang/compiler-course/clang-plugin-Mamaeva/CMakeLists.txt new file mode 100644 index 0000000000000..8d061e837a071 --- /dev/null +++ b/clang/compiler-course/clang-plugin-Mamaeva/CMakeLists.txt @@ -0,0 +1,18 @@ +set(Title "ClangAST_1") +set(Student "Mamaeva_Olga") +set(Group "FIIT3") +set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) +add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Support) + clang_target_link_libraries(${TARGET_NAME} PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/compiler-course/clang-plugin-Mamaeva/myClangPlugin.cpp b/clang/compiler-course/clang-plugin-Mamaeva/myClangPlugin.cpp new file mode 100644 index 0000000000000..5571484662975 --- /dev/null +++ b/clang/compiler-course/clang-plugin-Mamaeva/myClangPlugin.cpp @@ -0,0 +1,127 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ParentMapContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/Support/raw_ostream.h" + +namespace { + +class MyClangVisitor : public clang::RecursiveASTVisitor { + +private: + clang::ASTContext *m_context; + llvm::MapVector, int>> + m_functionStats; + int m_totalConversions = 0; + +public: + explicit MyClangVisitor(clang::ASTContext *context) : m_context(context) {} + + bool VisitImplicitCastExpr(clang::ImplicitCastExpr *ICE) { + + auto castKind = ICE->getCastKind(); + if (castKind == clang::CK_LValueToRValue || castKind == clang::CK_NoOp || + castKind == clang::CK_FunctionToPointerDecay) { + return true; + } + clang::QualType SourceType = ICE->getSubExpr()->getType(); + clang::QualType TargetType = ICE->getType(); + + if (SourceType.getCanonicalType() == TargetType.getCanonicalType()) { + return true; + } + + auto Parents = m_context->getParents(*ICE); + while (!Parents.empty()) { + if (const auto *FD = Parents[0].get()) { + recordConversion(FD, SourceType, TargetType); + break; + } else if (const auto *Lambda = Parents[0].get()) { + if (const auto *CallOp = Lambda->getCallOperator()) { + recordConversion(CallOp, SourceType, TargetType); + break; + } + } else if (const auto *ME = Parents[0].get()) { + recordConversion(ME, SourceType, TargetType); + break; + } + Parents = m_context->getParents(Parents[0]); + } + return true; + } + + std::string normalizeTypeName(std::string typeName) { + const std::vector prefixes = {"struct ", "class ", "enum "}; + for (const auto &prefix : prefixes) { + size_t pos = typeName.find(prefix); + if (pos == 0) { + typeName.erase(0, prefix.length()); + break; + } + } + typeName.erase( + std::remove_if(typeName.begin(), typeName.end(), + [](unsigned char c) { return std::isspace(c); }), + typeName.end()); + size_t pos; + while ((pos = typeName.find("_Bool")) != std::string::npos) { + typeName.replace(pos, 5, "bool"); + } + return typeName; + } + + void recordConversion(const clang::FunctionDecl *FD, clang::QualType From, + clang::QualType To) { + std::string FromStr = + normalizeTypeName(From.getCanonicalType().getAsString()); + std::string ToStr = normalizeTypeName(To.getCanonicalType().getAsString()); + m_functionStats[FD][std::make_pair(FromStr, ToStr)]++; + m_totalConversions++; + } + + void printStats(llvm::raw_ostream &OS) { + for (const auto &[func, convs] : m_functionStats) { + OS << "Function `" << func->getName() << "`\n"; + for (const auto &[conv, num] : convs) { + OS << conv.first << " -> " << conv.second << ": " << num << "\n"; + } + } + OS << "Total implicit conversions: " << m_totalConversions << "\n"; + } +}; + +class MyClangConsumer : public clang::ASTConsumer { + +private: + MyClangVisitor m_visitor; + +public: + explicit MyClangConsumer(clang::ASTContext *context) : m_visitor(context) {} + + void HandleTranslationUnit(clang::ASTContext &context) override { + m_visitor.TraverseDecl(context.getTranslationUnitDecl()); + m_visitor.printStats(llvm::errs()); + } +}; + +class MyClangPlugin : public clang::PluginASTAction { +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { + return std::make_unique(&ci.getASTContext()); + } + + bool ParseArgs(const clang::CompilerInstance &ci, + const std::vector &args) override { + return true; + } +}; + +} // namespace + +// Регистрация плагина +static clang::FrontendPluginRegistry::Add + X("ClangAST_1", "Counts implicit type conversions"); diff --git a/clang/test/compiler-course/clang-plugin-Mamaeva/mamaeva-test.cpp b/clang/test/compiler-course/clang-plugin-Mamaeva/mamaeva-test.cpp new file mode 100644 index 0000000000000..acfc4c46c4b0e --- /dev/null +++ b/clang/test/compiler-course/clang-plugin-Mamaeva/mamaeva-test.cpp @@ -0,0 +1,63 @@ +// RUN: %clang_cc1 -load %llvmshlibdir/ClangAST_1_Mamaeva_Olga_FIIT3_ClangAST%pluginext -plugin ClangAST_1 -fsyntax-only %s 2>&1 | FileCheck %s -dump-input=always + +enum class Color { Red }; +enum OldStyleEnum { ONE, TWO, THREE }; + +class NumberWrapper { +public: + operator int() const { return 42; } +}; + +// CHECK: Function `sum` +// CHECK-NEXT: float -> double: 1 +// CHECK-NEXT: int -> float: 1 +double sum(int a, float b) { + return a + b; +} + +// CHECK-NEXT: Function `mul` +// CHECK-NEXT: double -> int: 1 +// CHECK-NEXT: float -> double: 1 +// CHECK-NEXT: float -> int: 1 +int mul(float a, float b) { + return a + sum(a, b); +} + +// CHECK: Function `convertTypes` +// CHECK-NEXT: float -> int: 1 +// CHECK-NEXT: int -> float: 1 +using FloatType = float; +using IntType = int; +void convertTypes() { + FloatType floatVar = IntType(); + IntType intVar = floatVar; +} + +// CHECK: Function `handlePointers` +// CHECK-NEXT: int* -> void*: 1 +void handlePointers() { + int value; + void* voidPtr = &value; +} + +// CHECK: Function `checkPointer` +// CHECK-NEXT: int* -> bool: 1 +void checkPointer(int* ptr) { + if (ptr) {} +} + +// CHECK: Function `convertToBool` +// CHECK-NEXT: int -> bool: 1 +void convertToBool() { + int number = 7; + bool booleanValue = number; +} + +// CHECK: Function `useEnums` +// CHECK-NEXT: OldStyleEnum -> int: 1 +void useEnums() { + OldStyleEnum e = TWO; + int x = e; +} + +// CHECK: Total implicit conversions: 11 From 47be0e4cbf8dcf0288f4294dbee7345a4bd7b595 Mon Sep 17 00:00:00 2001 From: sozozzya Date: Sun, 6 Apr 2025 21:13:24 +0300 Subject: [PATCH 45/50] initial structure --- .../sozonov_i_llvm_fmuladd/CMakeLists.txt | 14 +++ .../sozonov_i_llvm_fmuladd/FmulFaddPass.cpp | 87 +++++++++++++++++++ .../lit_test_llvm_ir_.ll | 41 +++++++++ 3 files changed, 142 insertions(+) create mode 100644 llvm/compiler-course/llvm-ir/sozonov_i_llvm_fmuladd/CMakeLists.txt create mode 100644 llvm/compiler-course/llvm-ir/sozonov_i_llvm_fmuladd/FmulFaddPass.cpp create mode 100644 llvm/test/compiler-course/sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll diff --git a/llvm/compiler-course/llvm-ir/sozonov_i_llvm_fmuladd/CMakeLists.txt b/llvm/compiler-course/llvm-ir/sozonov_i_llvm_fmuladd/CMakeLists.txt new file mode 100644 index 0000000000000..66abbe57e5044 --- /dev/null +++ b/llvm/compiler-course/llvm-ir/sozonov_i_llvm_fmuladd/CMakeLists.txt @@ -0,0 +1,14 @@ +set(Title "FmulFaddPass") +set(Student "Sozonov_Ilya") +set(Group "FIIT3") +set(TARGET_NAME "${Title}_${Student}_${Group}_LLVM_IR") + +if (NOT WIN32 AND NOT CYGWIN) + file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) + add_llvm_pass_plugin(${TARGET_NAME} ${SOURCES} + DEPENDS + intrinsics_gen + BUILDTREE_ONLY + ) + set(LLVM_TEST_DEPENDS ${TARGET_NAME} ${LLVM_TEST_DEPENDS} PARENT_SCOPE) +endif() diff --git a/llvm/compiler-course/llvm-ir/sozonov_i_llvm_fmuladd/FmulFaddPass.cpp b/llvm/compiler-course/llvm-ir/sozonov_i_llvm_fmuladd/FmulFaddPass.cpp new file mode 100644 index 0000000000000..a5eb6b5f91dbc --- /dev/null +++ b/llvm/compiler-course/llvm-ir/sozonov_i_llvm_fmuladd/FmulFaddPass.cpp @@ -0,0 +1,87 @@ +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/PassPlugin.h" +#include "llvm/Support/raw_ostream.h" + +namespace { +struct FmulFaddPass : llvm::PassInfoMixin { + llvm::PreservedAnalyses run(llvm::Function &func, + llvm::FunctionAnalysisManager &) { + bool isModified = false; + llvm::SmallVector instructionsToRemove; + + for (llvm::BasicBlock &BB : func) { + for (llvm::Instruction &I : llvm::make_early_inc_range(BB)) { + if (I.getOpcode() != llvm::Instruction::FAdd) + continue; + + llvm::Value *Op1 = I.getOperand(0); + llvm::Value *Op2 = I.getOperand(1); + + llvm::Instruction *MulInst = nullptr; + llvm::Value *A = nullptr, *B = nullptr, *C = nullptr; + + if (auto *Mul = llvm::dyn_cast(Op1)) { + if (Mul->getOpcode() == llvm::Instruction::FMul) { + A = Mul->getOperand(0); + B = Mul->getOperand(1); + C = Op2; + MulInst = Mul; + } + } + + if (!MulInst) { + if (auto *Mul = llvm::dyn_cast(Op2)) { + if (Mul->getOpcode() == llvm::Instruction::FMul) { + A = Mul->getOperand(0); + B = Mul->getOperand(1); + C = Op1; + MulInst = Mul; + } + } + } + + if (MulInst) { + llvm::IRBuilder<> builder(&I); + llvm::Function *FmaFunc = llvm::Intrinsic::getDeclaration( + func.getParent(), llvm::Intrinsic::fmuladd, I.getType()); + + llvm::Value *Fma = builder.CreateCall(FmaFunc, {A, B, C}); + I.replaceAllUsesWith(Fma); + instructionsToRemove.push_back(&I); + instructionsToRemove.push_back(MulInst); + isModified = true; + } + } + } + + for (llvm::Instruction *I : instructionsToRemove) + I->eraseFromParent(); + + return isModified ? llvm::PreservedAnalyses::none() + : llvm::PreservedAnalyses::all(); + } + + static bool isRequired() { return true; } +}; +} // namespace + +extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo +llvmGetPassPluginInfo() { + return {LLVM_PLUGIN_API_VERSION, "FmulFaddPass", "0.1", + [](llvm::PassBuilder &PB) { + PB.registerPipelineParsingCallback( + [](llvm::StringRef name, llvm::FunctionPassManager &FPM, + llvm::ArrayRef) -> bool { + if (name == "FmulFaddPass") { + FPM.addPass(FmulFaddPass{}); + return true; + } + return false; + }); + }}; +} diff --git a/llvm/test/compiler-course/sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll b/llvm/test/compiler-course/sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll new file mode 100644 index 0000000000000..ff8abb2667f01 --- /dev/null +++ b/llvm/test/compiler-course/sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll @@ -0,0 +1,41 @@ +; RUN: opt -load-pass-plugin %llvmshlibdir/FmulFaddPass_Sozonov_Ilya_FIIT3_LLVM_IR%pluginext -passes=FmulFaddPass -S %s | FileCheck %s + +; Test 1: (a * b) + c - should be replaced with llvm.fmuladd(a, b, c) +; CHECK-LABEL: @mul_add_direct +; CHECK-NEXT: entry: +; CHECK-NEXT: %0 = call float @llvm.fmuladd.f32(float %a, float %b, float %c) +; CHECK-NEXT: ret float %0 + +define float @mul_add_direct(float %a, float %b, float %c) { +entry: + %mul = fmul float %a, %b + %add = fadd float %mul, %c + ret float %add +} + +; Test 2: c + (a * b) - should also be replaced with llvm.fmuladd(a, b, c) +; CHECK-LABEL: @add_commuted_mul +; CHECK-NEXT: entry: +; CHECK-NEXT: %0 = call float @llvm.fmuladd.f32(float %a, float %b, float %c) +; CHECK-NEXT: ret float %0 + +define float @add_commuted_mul(float %a, float %b, float %c) { +entry: + %mul = fmul float %a, %b + %add = fadd float %c, %mul + ret float %add +} + +; Test 3: non-pattern input - should NOT be replaced +; CHECK-LABEL: @no_pattern +; CHECK-NEXT: entry: +; CHECK-NEXT: %add = fadd float %c, %b +; CHECK-NEXT: %mul = fmul float %a, %add +; CHECK-NEXT: ret float %mul + +define float @no_pattern(float %a, float %b, float %c) { +entry: + %add = fadd float %c, %b + %mul = fmul float %a, %add + ret float %mul +} \ No newline at end of file From 493fc25e515d400f43081cf9092ef753bd3ec692 Mon Sep 17 00:00:00 2001 From: sozozzya Date: Mon, 7 Apr 2025 23:23:16 +0300 Subject: [PATCH 46/50] added more tests --- .../lit_test_llvm_ir_.ll | 105 ++++++++++++++++-- 1 file changed, 94 insertions(+), 11 deletions(-) diff --git a/llvm/test/compiler-course/sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll b/llvm/test/compiler-course/sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll index ff8abb2667f01..f1b2c8828420e 100644 --- a/llvm/test/compiler-course/sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll +++ b/llvm/test/compiler-course/sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll @@ -1,12 +1,11 @@ ; RUN: opt -load-pass-plugin %llvmshlibdir/FmulFaddPass_Sozonov_Ilya_FIIT3_LLVM_IR%pluginext -passes=FmulFaddPass -S %s | FileCheck %s ; Test 1: (a * b) + c - should be replaced with llvm.fmuladd(a, b, c) -; CHECK-LABEL: @mul_add_direct +; CHECK-LABEL: @fmul_fadd_ab_c ; CHECK-NEXT: entry: ; CHECK-NEXT: %0 = call float @llvm.fmuladd.f32(float %a, float %b, float %c) ; CHECK-NEXT: ret float %0 - -define float @mul_add_direct(float %a, float %b, float %c) { +define float @fmul_fadd_ab_c(float %a, float %b, float %c) { entry: %mul = fmul float %a, %b %add = fadd float %mul, %c @@ -14,28 +13,112 @@ entry: } ; Test 2: c + (a * b) - should also be replaced with llvm.fmuladd(a, b, c) -; CHECK-LABEL: @add_commuted_mul +; CHECK-LABEL: @fadd_c_fmul_ab ; CHECK-NEXT: entry: ; CHECK-NEXT: %0 = call float @llvm.fmuladd.f32(float %a, float %b, float %c) ; CHECK-NEXT: ret float %0 - -define float @add_commuted_mul(float %a, float %b, float %c) { +define float @fadd_c_fmul_ab(float %a, float %b, float %c) { entry: %mul = fmul float %a, %b %add = fadd float %c, %mul ret float %add } -; Test 3: non-pattern input - should NOT be replaced -; CHECK-LABEL: @no_pattern +; Test 3: (c + b) * a - addition first (no matching pattern) +; CHECK-LABEL: @no_match_fadd_then_fmul ; CHECK-NEXT: entry: ; CHECK-NEXT: %add = fadd float %c, %b ; CHECK-NEXT: %mul = fmul float %a, %add ; CHECK-NEXT: ret float %mul - -define float @no_pattern(float %a, float %b, float %c) { +define float @no_match_fadd_then_fmul(float %a, float %b, float %c) { entry: %add = fadd float %c, %b %mul = fmul float %a, %add ret float %mul -} \ No newline at end of file +} + +; Test 4: (a + b) * c — addition comes first (should not match) +; CHECK-LABEL: @no_match_add_then_mul +; CHECK-NEXT: entry: +; CHECK-NEXT: %add = fadd float %a, %b +; CHECK-NEXT: %mul = fmul float %add, %c +; CHECK-NEXT: ret float %mul +define float @no_match_add_then_mul(float %a, float %b, float %c) { +entry: + %add = fadd float %a, %b + %mul = fmul float %add, %c + ret float %mul +} + +; Test 5: (a * a) + a - same operand used in multiple places (should still replace) +; CHECK-LABEL: @fmul_fadd_reuse +; CHECK-NEXT: entry: +; CHECK-NEXT: %0 = call float @llvm.fmuladd.f32(float %a, float %a, float %a) +; CHECK-NEXT: ret float %0 +define float @fmul_fadd_reuse(float %a) { +entry: + %mul = fmul float %a, %a + %add = fadd float %mul, %a + ret float %add +} + +; Test 6: ((a * b) + c) + d - nested expression (should replace inner only) +; CHECK-LABEL: @nested_fmul_fadd_then_add +; CHECK-NEXT: entry: +; CHECK-NEXT: %0 = call float @llvm.fmuladd.f32(float %a, float %b, float %c) +; CHECK-NEXT: %add2 = fadd float %0, %d +; CHECK-NEXT: ret float %add2 +define float @nested_fmul_fadd_then_add(float %a, float %b, float %c, float %d) { +entry: + %mul = fmul float %a, %b + %add1 = fadd float %mul, %c + %add2 = fadd float %add1, %d + ret float %add2 +} + +; Test 7: (a * b) + c — double precision (should replace) +; CHECK-LABEL: @fmul_fadd_double +; CHECK-NEXT: entry: +; CHECK-NEXT: %0 = call double @llvm.fmuladd.f64(double %a, double %b, double %c) +; CHECK-NEXT: ret double %0 +define double @fmul_fadd_double(double %a, double %b, double %c) { +entry: + %mul = fmul double %a, %b + %add = fadd double %mul, %c + ret double %add +} + +; Test 8: (2.0 * a) + 1.0 - used constants (should replace) +; CHECK-LABEL: @fmul_fadd_with_constants +; CHECK-NEXT: entry: +; CHECK-NEXT: %0 = call float @llvm.fmuladd.f32(float 2.000000e+00, float %a, float 1.000000e+00) +; CHECK-NEXT: ret float %0 +define float @fmul_fadd_with_constants(float %a) { +entry: + %mul = fmul float 2.0, %a + %add = fadd float %mul, 1.0 + ret float %add +} + +; Test 9: (x * x) + y - one operand used multiple times (should replace) +; CHECK-LABEL: @fmul_fadd_duplicate_operand +; CHECK-NEXT: entry: +; CHECK-NEXT: %0 = call float @llvm.fmuladd.f32(float %x, float %x, float %y) +; CHECK-NEXT: ret float %0 +define float @fmul_fadd_duplicate_operand(float %x, float %y) { +entry: + %mul = fmul float %x, %x + %add = fadd float %mul, %y + ret float %add +} + +; Test 10: already fused with llvm.fmuladd +; CHECK-LABEL: @already_fused_fmuladd +; CHECK-NEXT: entry: +; CHECK-NEXT: %fma = call float @llvm.fmuladd.f32(float %a, float %b, float %c) +; CHECK-NEXT: ret float %fma +define float @already_fused_fmuladd(float %a, float %b, float %c) { +entry: + %fma = call float @llvm.fmuladd.f32(float %a, float %b, float %c) + ret float %fma +} From 154e6c9fecd3c792b5c8411e6134993afd4cf3c7 Mon Sep 17 00:00:00 2001 From: sozozzya Date: Wed, 9 Apr 2025 00:34:50 +0300 Subject: [PATCH 47/50] added a test and removed code duplication --- .../sozonov_i_llvm_fmuladd/FmulFaddPass.cpp | 24 +++++++++---------- .../lit_test_llvm_ir_.ll | 20 ++++++++++++++-- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/llvm/compiler-course/llvm-ir/sozonov_i_llvm_fmuladd/FmulFaddPass.cpp b/llvm/compiler-course/llvm-ir/sozonov_i_llvm_fmuladd/FmulFaddPass.cpp index a5eb6b5f91dbc..592d7346582e0 100644 --- a/llvm/compiler-course/llvm-ir/sozonov_i_llvm_fmuladd/FmulFaddPass.cpp +++ b/llvm/compiler-course/llvm-ir/sozonov_i_llvm_fmuladd/FmulFaddPass.cpp @@ -25,22 +25,16 @@ struct FmulFaddPass : llvm::PassInfoMixin { llvm::Instruction *MulInst = nullptr; llvm::Value *A = nullptr, *B = nullptr, *C = nullptr; - if (auto *Mul = llvm::dyn_cast(Op1)) { - if (Mul->getOpcode() == llvm::Instruction::FMul) { - A = Mul->getOperand(0); - B = Mul->getOperand(1); - C = Op2; - MulInst = Mul; - } - } + llvm::Value *Operands[2] = {Op1, Op2}; - if (!MulInst) { - if (auto *Mul = llvm::dyn_cast(Op2)) { + for (int i = 0; i < 2; ++i) { + if (auto *Mul = llvm::dyn_cast(Operands[i])) { if (Mul->getOpcode() == llvm::Instruction::FMul) { A = Mul->getOperand(0); B = Mul->getOperand(1); - C = Op1; + C = Operands[1 - i]; MulInst = Mul; + break; } } } @@ -49,11 +43,15 @@ struct FmulFaddPass : llvm::PassInfoMixin { llvm::IRBuilder<> builder(&I); llvm::Function *FmaFunc = llvm::Intrinsic::getDeclaration( func.getParent(), llvm::Intrinsic::fmuladd, I.getType()); - + llvm::Value *Fma = builder.CreateCall(FmaFunc, {A, B, C}); I.replaceAllUsesWith(Fma); instructionsToRemove.push_back(&I); - instructionsToRemove.push_back(MulInst); + + if (MulInst->hasOneUse()) { + instructionsToRemove.push_back(MulInst); + } + isModified = true; } } diff --git a/llvm/test/compiler-course/sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll b/llvm/test/compiler-course/sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll index f1b2c8828420e..f0ab078b43d90 100644 --- a/llvm/test/compiler-course/sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll +++ b/llvm/test/compiler-course/sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll @@ -24,7 +24,7 @@ entry: ret float %add } -; Test 3: (c + b) * a - addition first (no matching pattern) +; Test 3: a * (c + b) - addition first (no matching pattern) ; CHECK-LABEL: @no_match_fadd_then_fmul ; CHECK-NEXT: entry: ; CHECK-NEXT: %add = fadd float %c, %b @@ -112,7 +112,23 @@ entry: ret float %add } -; Test 10: already fused with llvm.fmuladd +; Test 10: (x = a * b; y = x + c; return x + y) - use of intermediate result +; CHECK-LABEL: @fmul_used_multiple_times +; CHECK-NEXT: entry: +; CHECK-NEXT: %mul = fmul float %a, %b +; CHECK-NEXT: %add = fadd float %mul, %c +; CHECK-NEXT: %add2 = fadd float %mul, %add +; CHECK-NEXT: ret float %add2 + +define float @fmul_used_multiple_times(float %a, float %b, float %c) { +entry: + %mul = fmul float %a, %b + %add = fadd float %mul, %c + %add2 = fadd float %mul, %add + ret float %add2 +} + +; Test 11: already fused with llvm.fmuladd ; CHECK-LABEL: @already_fused_fmuladd ; CHECK-NEXT: entry: ; CHECK-NEXT: %fma = call float @llvm.fmuladd.f32(float %a, float %b, float %c) From cdfdb79c235c8207b6ba68d1d06a1532817297b5 Mon Sep 17 00:00:00 2001 From: sozozzya Date: Wed, 9 Apr 2025 01:13:40 +0300 Subject: [PATCH 48/50] fix errors --- .../llvm-ir/sozonov_i_llvm_fmuladd/FmulFaddPass.cpp | 12 ++++++------ .../sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll | 1 - 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/llvm/compiler-course/llvm-ir/sozonov_i_llvm_fmuladd/FmulFaddPass.cpp b/llvm/compiler-course/llvm-ir/sozonov_i_llvm_fmuladd/FmulFaddPass.cpp index 592d7346582e0..bfbf79cf12157 100644 --- a/llvm/compiler-course/llvm-ir/sozonov_i_llvm_fmuladd/FmulFaddPass.cpp +++ b/llvm/compiler-course/llvm-ir/sozonov_i_llvm_fmuladd/FmulFaddPass.cpp @@ -40,18 +40,18 @@ struct FmulFaddPass : llvm::PassInfoMixin { } if (MulInst) { + if (!MulInst->hasOneUse()) + continue; + llvm::IRBuilder<> builder(&I); llvm::Function *FmaFunc = llvm::Intrinsic::getDeclaration( func.getParent(), llvm::Intrinsic::fmuladd, I.getType()); - + llvm::Value *Fma = builder.CreateCall(FmaFunc, {A, B, C}); I.replaceAllUsesWith(Fma); instructionsToRemove.push_back(&I); - - if (MulInst->hasOneUse()) { - instructionsToRemove.push_back(MulInst); - } - + instructionsToRemove.push_back(MulInst); + isModified = true; } } diff --git a/llvm/test/compiler-course/sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll b/llvm/test/compiler-course/sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll index f0ab078b43d90..c8034099d22ff 100644 --- a/llvm/test/compiler-course/sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll +++ b/llvm/test/compiler-course/sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll @@ -119,7 +119,6 @@ entry: ; CHECK-NEXT: %add = fadd float %mul, %c ; CHECK-NEXT: %add2 = fadd float %mul, %add ; CHECK-NEXT: ret float %add2 - define float @fmul_used_multiple_times(float %a, float %b, float %c) { entry: %mul = fmul float %a, %b From e5a99f64156d1ab55118f5699169c60d4cb89973 Mon Sep 17 00:00:00 2001 From: sozozzya Date: Fri, 2 May 2025 16:15:31 +0300 Subject: [PATCH 49/50] some fixes --- .../llvm-ir/sozonov_i_llvm_fmuladd/FmulFaddPass.cpp | 2 +- .../sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/llvm/compiler-course/llvm-ir/sozonov_i_llvm_fmuladd/FmulFaddPass.cpp b/llvm/compiler-course/llvm-ir/sozonov_i_llvm_fmuladd/FmulFaddPass.cpp index bfbf79cf12157..a191b019ce221 100644 --- a/llvm/compiler-course/llvm-ir/sozonov_i_llvm_fmuladd/FmulFaddPass.cpp +++ b/llvm/compiler-course/llvm-ir/sozonov_i_llvm_fmuladd/FmulFaddPass.cpp @@ -34,7 +34,7 @@ struct FmulFaddPass : llvm::PassInfoMixin { B = Mul->getOperand(1); C = Operands[1 - i]; MulInst = Mul; - break; + break; //hasOneUse } } } diff --git a/llvm/test/compiler-course/sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll b/llvm/test/compiler-course/sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll index c8034099d22ff..d61de55262c17 100644 --- a/llvm/test/compiler-course/sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll +++ b/llvm/test/compiler-course/sozonov_i_llvm_fmuladd/lit_test_llvm_ir_.ll @@ -91,6 +91,8 @@ entry: ; Test 8: (2.0 * a) + 1.0 - used constants (should replace) ; CHECK-LABEL: @fmul_fadd_with_constants ; CHECK-NEXT: entry: +; CHECK-NOT: %mul = fmul float 2.0, %a +; CHECK-NOT: %add = fadd float %mul, 1.0 ; CHECK-NEXT: %0 = call float @llvm.fmuladd.f32(float 2.000000e+00, float %a, float 1.000000e+00) ; CHECK-NEXT: ret float %0 define float @fmul_fadd_with_constants(float %a) { @@ -137,3 +139,13 @@ entry: %fma = call float @llvm.fmuladd.f32(float %a, float %b, float %c) ret float %fma } + + +define float @fmul_used_multiple_times_3(float %a, float %b, float %c, float %d) { +entry: + %mul1 = fmul float %a, %b + %mul2 = fmul float %c, %d + %add = fadd float %mul1, %mul2 + %add2 = fadd float %mul1, %add + ret float %add2 +} \ No newline at end of file From 14b1b1dc30fd6547643bf51c49809a12087bd9ca Mon Sep 17 00:00:00 2001 From: sozozzya Date: Tue, 6 May 2025 00:48:16 +0300 Subject: [PATCH 50/50] initial structure --- .../sozonov_i_logical_combiner/CMakeLists.txt | 17 + .../LogicalCombinerPass.cpp | 98 ++ .../test_backend.mir | 1461 +++++++++++++++++ 3 files changed, 1576 insertions(+) create mode 100644 llvm/compiler-course/backend/sozonov_i_logical_combiner/CMakeLists.txt create mode 100644 llvm/compiler-course/backend/sozonov_i_logical_combiner/LogicalCombinerPass.cpp create mode 100644 llvm/test/compiler-course/sozonov_i_logical_combiner/test_backend.mir diff --git a/llvm/compiler-course/backend/sozonov_i_logical_combiner/CMakeLists.txt b/llvm/compiler-course/backend/sozonov_i_logical_combiner/CMakeLists.txt new file mode 100644 index 0000000000000..8970c72ab9cec --- /dev/null +++ b/llvm/compiler-course/backend/sozonov_i_logical_combiner/CMakeLists.txt @@ -0,0 +1,17 @@ +set(Title "LogicalCombinerPass") +set(Student "Sozonov_Ilya") +set(Group "FIIT3") +set(TARGET_NAME "${Title}_${Student}_${Group}_BACKEND") + +file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) + +add_llvm_pass_plugin(${TARGET_NAME} + ${SOURCES} + DEPENDS + intrinsics_gen + X86 + BUILDTREE_ONLY +) + +target_include_directories(${TARGET_NAME} PUBLIC ${PATH_TO_X86}) +set(LLVM_TEST_DEPENDS ${TARGET_NAME} ${LLVM_TEST_DEPENDS} PARENT_SCOPE) diff --git a/llvm/compiler-course/backend/sozonov_i_logical_combiner/LogicalCombinerPass.cpp b/llvm/compiler-course/backend/sozonov_i_logical_combiner/LogicalCombinerPass.cpp new file mode 100644 index 0000000000000..8f02ffbe10c76 --- /dev/null +++ b/llvm/compiler-course/backend/sozonov_i_logical_combiner/LogicalCombinerPass.cpp @@ -0,0 +1,98 @@ +#include "X86.h" +#include "X86InstrInfo.h" +#include "X86Subtarget.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +namespace { +class LogicalCombinerPass : public MachineFunctionPass { +public: + static char ID; + LogicalCombinerPass() : MachineFunctionPass(ID) {} + bool runOnMachineFunction(MachineFunction &MF) override; +}; + +char LogicalCombinerPass::ID = 0; + +bool LogicalCombinerPass::runOnMachineFunction(MachineFunction &MF) { + llvm::outs() << MF.getName() << '\n'; + + const X86Subtarget &STI = MF.getSubtarget(); + const X86InstrInfo *TII = STI.getInstrInfo(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + bool Changed = false; + + for (auto &MBB : MF) { + for (auto MI = MBB.begin(); MI != MBB.end();) { + MachineInstr &AndInstr = *MI; + unsigned AndOpc = AndInstr.getOpcode(); + + if (AndOpc != X86::ANDPSrr && AndOpc != X86::VPANDrr) { + ++MI; + continue; + } + + if (!AndInstr.getOperand(0).isReg() || !AndInstr.getOperand(1).isReg() || + !AndInstr.getOperand(2).isReg()) { + ++MI; + continue; + } + + Register AndDst = AndInstr.getOperand(0).getReg(); + Register AndOp1 = AndInstr.getOperand(1).getReg(); + Register AndOp2 = AndInstr.getOperand(2).getReg(); + + auto NextMI = std::next(MI); + if (NextMI == MBB.end()) { + ++MI; + continue; + } + + MachineInstr &OrInstr = *NextMI; + unsigned OrOpc = OrInstr.getOpcode(); + + if ((OrOpc != X86::ORPSrr && OrOpc != X86::VPORrr) || + !OrInstr.getOperand(1).isReg() || + OrInstr.getOperand(1).getReg() != AndDst || + !OrInstr.getOperand(0).isReg() || !OrInstr.getOperand(2).isReg()) { + ++MI; + continue; + } + + Register OrDst = OrInstr.getOperand(0).getReg(); + Register OrOther = OrInstr.getOperand(2).getReg(); + + DebugLoc DL = AndInstr.getDebugLoc(); + + const TargetRegisterClass *RC = MRI.getRegClass(AndOp1); + if (!RC) + RC = MRI.getRegClass(AndOp2); + if (!RC) + continue; + + Register TmpReg = MRI.createVirtualRegister(RC); + + BuildMI(MBB, AndInstr, DL, TII->get(X86::VPANDrr), TmpReg) + .addReg(AndOp1) + .addReg(AndOp2); + + BuildMI(MBB, OrInstr, DL, TII->get(X86::VPORrr), OrDst) + .addReg(TmpReg) + .addReg(OrOther); + + MI = MBB.erase(MI); + MBB.erase(NextMI); + + Changed = true; + } + } + + return Changed; +} +} // namespace + +static RegisterPass + X("logical-combiner-x86", "Combine logical ops into AVX", false, false); diff --git a/llvm/test/compiler-course/sozonov_i_logical_combiner/test_backend.mir b/llvm/test/compiler-course/sozonov_i_logical_combiner/test_backend.mir new file mode 100644 index 0000000000000..4b2414cb7e9a9 --- /dev/null +++ b/llvm/test/compiler-course/sozonov_i_logical_combiner/test_backend.mir @@ -0,0 +1,1461 @@ +# RUN: llc -mtriple x86_64-unknown-linux-gnu --load=%llvmshlibdir/LogicalCombinerPass_Sozonov_Ilya_FIIT3_BACKEND%shlibext \ +# RUN: -run-pass=logical-combiner-x86 %s -o - | FileCheck %s + +--- | + ; ModuleID = 'llvm/test/compiler-course/sozonov_i_logical_combiner/test.ll' + source_filename = "test.cpp" + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" + target triple = "x86_64-pc-linux-gnu" + + module asm ".globl _ZSt21ios_base_library_initv" + + %"class.std::basic_ostream" = type { ptr, %"class.std::basic_ios" } + %"class.std::basic_ios" = type { %"class.std::ios_base", ptr, i8, i8, ptr, ptr, ptr, ptr } + %"class.std::ios_base" = type { ptr, i64, i64, i32, i32, i32, ptr, %"struct.std::ios_base::_Words", [8 x %"struct.std::ios_base::_Words"], i32, ptr, %"class.std::locale" } + %"struct.std::ios_base::_Words" = type { ptr, i64 } + %"class.std::locale" = type { ptr } + %"class.std::ctype" = type <{ %"class.std::locale::facet.base", [4 x i8], ptr, i8, [7 x i8], ptr, ptr, ptr, i8, [256 x i8], [256 x i8], i8, [6 x i8] }> + %"class.std::locale::facet.base" = type <{ ptr, i32 }> + + @_ZSt4cout = external global %"class.std::basic_ostream", align 8 + + ; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable + define dso_local noundef zeroext i1 @_Z11and_or_testiii(i32 noundef %0, i32 noundef %1, i32 noundef %2) local_unnamed_addr #0 { + %4 = and i32 %1, %0 + %5 = icmp ne i32 %4, 0 + %6 = zext i1 %5 to i32 + %7 = or i32 %6, %2 + %8 = icmp ne i32 %7, 0 + ret i1 %8 + } + + ; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable + define dso_local noundef zeroext i1 @_Z11or_xor_testiii(i32 noundef %0, i32 noundef %1, i32 noundef %2) local_unnamed_addr #0 { + %4 = or i32 %1, %0 + %5 = icmp ne i32 %4, 0 + %6 = zext i1 %5 to i32 + %7 = icmp ne i32 %6, %2 + ret i1 %7 + } + + ; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable + define dso_local noundef zeroext i1 @_Z12and_xor_testiii(i32 noundef %0, i32 noundef %1, i32 noundef %2) local_unnamed_addr #0 { + %4 = and i32 %1, %0 + %5 = icmp ne i32 %4, 0 + %6 = zext i1 %5 to i32 + %7 = icmp ne i32 %6, %2 + ret i1 %7 + } + + ; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable + define dso_local noundef zeroext i1 @_Z15and_or_xor_testiiii(i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3) local_unnamed_addr #0 { + %5 = and i32 %1, %0 + %6 = icmp ne i32 %5, 0 + %7 = zext i1 %6 to i32 + %8 = or i32 %7, %2 + %9 = icmp ne i32 %8, 0 + %10 = zext i1 %9 to i32 + %11 = icmp ne i32 %10, %3 + ret i1 %11 + } + + ; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable + define dso_local noundef zeroext i1 @_Z12and_and_testiii(i32 noundef %0, i32 noundef %1, i32 noundef %2) local_unnamed_addr #0 { + %4 = and i32 %1, %0 + %5 = icmp ne i32 %4, 0 + %6 = and i32 %2, 1 + %7 = icmp ne i32 %6, 0 + %8 = and i1 %5, %7 + ret i1 %8 + } + + ; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable + define dso_local noundef zeroext i1 @_Z12xor_xor_testiii(i32 noundef %0, i32 noundef %1, i32 noundef %2) local_unnamed_addr #0 { + %4 = icmp ne i32 %0, %1 + %5 = zext i1 %4 to i32 + %6 = icmp ne i32 %5, %2 + ret i1 %6 + } + + ; Function Attrs: mustprogress norecurse uwtable + define dso_local noundef i32 @main() local_unnamed_addr #1 { + %1 = tail call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSo9_M_insertIbEERSoT_(ptr noundef nonnull align 8 dereferenceable(8) @_ZSt4cout, i1 noundef zeroext true) + %2 = load ptr, ptr %1, align 8, !tbaa !5 + %3 = getelementptr i8, ptr %2, i64 -24 + %4 = load i64, ptr %3, align 8 + %5 = getelementptr inbounds i8, ptr %1, i64 %4 + %6 = getelementptr inbounds %"class.std::basic_ios", ptr %5, i64 0, i32 5 + %7 = load ptr, ptr %6, align 8, !tbaa !8 + %8 = icmp eq ptr %7, null + br i1 %8, label %9, label %10 + + 9: ; preds = %0 + tail call void @_ZSt16__throw_bad_castv() #4 + unreachable + + 10: ; preds = %0 + %11 = getelementptr inbounds %"class.std::ctype", ptr %7, i64 0, i32 8 + %12 = load i8, ptr %11, align 8, !tbaa !20 + %13 = icmp eq i8 %12, 0 + br i1 %13, label %17, label %14 + + 14: ; preds = %10 + %15 = getelementptr inbounds %"class.std::ctype", ptr %7, i64 0, i32 9, i64 10 + %16 = load i8, ptr %15, align 1, !tbaa !23 + br label %22 + + 17: ; preds = %10 + tail call void @_ZNKSt5ctypeIcE13_M_widen_initEv(ptr noundef nonnull align 8 dereferenceable(570) %7) + %18 = load ptr, ptr %7, align 8, !tbaa !5 + %19 = getelementptr inbounds ptr, ptr %18, i64 6 + %20 = load ptr, ptr %19, align 8 + %21 = tail call noundef signext i8 %20(ptr noundef nonnull align 8 dereferenceable(570) %7, i8 noundef signext 10) + br label %22 + + 22: ; preds = %17, %14 + %23 = phi i8 [ %16, %14 ], [ %21, %17 ] + %24 = tail call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSo3putEc(ptr noundef nonnull align 8 dereferenceable(8) %1, i8 noundef signext %23) + %25 = tail call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSo5flushEv(ptr noundef nonnull align 8 dereferenceable(8) %24) + %26 = tail call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSo9_M_insertIbEERSoT_(ptr noundef nonnull align 8 dereferenceable(8) @_ZSt4cout, i1 noundef zeroext false) + %27 = load ptr, ptr %26, align 8, !tbaa !5 + %28 = getelementptr i8, ptr %27, i64 -24 + %29 = load i64, ptr %28, align 8 + %30 = getelementptr inbounds i8, ptr %26, i64 %29 + %31 = getelementptr inbounds %"class.std::basic_ios", ptr %30, i64 0, i32 5 + %32 = load ptr, ptr %31, align 8, !tbaa !8 + %33 = icmp eq ptr %32, null + br i1 %33, label %34, label %35 + + 34: ; preds = %22 + tail call void @_ZSt16__throw_bad_castv() #4 + unreachable + + 35: ; preds = %22 + %36 = getelementptr inbounds %"class.std::ctype", ptr %32, i64 0, i32 8 + %37 = load i8, ptr %36, align 8, !tbaa !20 + %38 = icmp eq i8 %37, 0 + br i1 %38, label %42, label %39 + + 39: ; preds = %35 + %40 = getelementptr inbounds %"class.std::ctype", ptr %32, i64 0, i32 9, i64 10 + %41 = load i8, ptr %40, align 1, !tbaa !23 + br label %47 + + 42: ; preds = %35 + tail call void @_ZNKSt5ctypeIcE13_M_widen_initEv(ptr noundef nonnull align 8 dereferenceable(570) %32) + %43 = load ptr, ptr %32, align 8, !tbaa !5 + %44 = getelementptr inbounds ptr, ptr %43, i64 6 + %45 = load ptr, ptr %44, align 8 + %46 = tail call noundef signext i8 %45(ptr noundef nonnull align 8 dereferenceable(570) %32, i8 noundef signext 10) + br label %47 + + 47: ; preds = %42, %39 + %48 = phi i8 [ %41, %39 ], [ %46, %42 ] + %49 = tail call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSo3putEc(ptr noundef nonnull align 8 dereferenceable(8) %26, i8 noundef signext %48) + %50 = tail call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSo5flushEv(ptr noundef nonnull align 8 dereferenceable(8) %49) + %51 = tail call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSo9_M_insertIbEERSoT_(ptr noundef nonnull align 8 dereferenceable(8) @_ZSt4cout, i1 noundef zeroext true) + %52 = load ptr, ptr %51, align 8, !tbaa !5 + %53 = getelementptr i8, ptr %52, i64 -24 + %54 = load i64, ptr %53, align 8 + %55 = getelementptr inbounds i8, ptr %51, i64 %54 + %56 = getelementptr inbounds %"class.std::basic_ios", ptr %55, i64 0, i32 5 + %57 = load ptr, ptr %56, align 8, !tbaa !8 + %58 = icmp eq ptr %57, null + br i1 %58, label %59, label %60 + + 59: ; preds = %47 + tail call void @_ZSt16__throw_bad_castv() #4 + unreachable + + 60: ; preds = %47 + %61 = getelementptr inbounds %"class.std::ctype", ptr %57, i64 0, i32 8 + %62 = load i8, ptr %61, align 8, !tbaa !20 + %63 = icmp eq i8 %62, 0 + br i1 %63, label %67, label %64 + + 64: ; preds = %60 + %65 = getelementptr inbounds %"class.std::ctype", ptr %57, i64 0, i32 9, i64 10 + %66 = load i8, ptr %65, align 1, !tbaa !23 + br label %72 + + 67: ; preds = %60 + tail call void @_ZNKSt5ctypeIcE13_M_widen_initEv(ptr noundef nonnull align 8 dereferenceable(570) %57) + %68 = load ptr, ptr %57, align 8, !tbaa !5 + %69 = getelementptr inbounds ptr, ptr %68, i64 6 + %70 = load ptr, ptr %69, align 8 + %71 = tail call noundef signext i8 %70(ptr noundef nonnull align 8 dereferenceable(570) %57, i8 noundef signext 10) + br label %72 + + 72: ; preds = %67, %64 + %73 = phi i8 [ %66, %64 ], [ %71, %67 ] + %74 = tail call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSo3putEc(ptr noundef nonnull align 8 dereferenceable(8) %51, i8 noundef signext %73) + %75 = tail call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSo5flushEv(ptr noundef nonnull align 8 dereferenceable(8) %74) + %76 = tail call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSo9_M_insertIbEERSoT_(ptr noundef nonnull align 8 dereferenceable(8) @_ZSt4cout, i1 noundef zeroext true) + %77 = load ptr, ptr %76, align 8, !tbaa !5 + %78 = getelementptr i8, ptr %77, i64 -24 + %79 = load i64, ptr %78, align 8 + %80 = getelementptr inbounds i8, ptr %76, i64 %79 + %81 = getelementptr inbounds %"class.std::basic_ios", ptr %80, i64 0, i32 5 + %82 = load ptr, ptr %81, align 8, !tbaa !8 + %83 = icmp eq ptr %82, null + br i1 %83, label %84, label %85 + + 84: ; preds = %72 + tail call void @_ZSt16__throw_bad_castv() #4 + unreachable + + 85: ; preds = %72 + %86 = getelementptr inbounds %"class.std::ctype", ptr %82, i64 0, i32 8 + %87 = load i8, ptr %86, align 8, !tbaa !20 + %88 = icmp eq i8 %87, 0 + br i1 %88, label %92, label %89 + + 89: ; preds = %85 + %90 = getelementptr inbounds %"class.std::ctype", ptr %82, i64 0, i32 9, i64 10 + %91 = load i8, ptr %90, align 1, !tbaa !23 + br label %97 + + 92: ; preds = %85 + tail call void @_ZNKSt5ctypeIcE13_M_widen_initEv(ptr noundef nonnull align 8 dereferenceable(570) %82) + %93 = load ptr, ptr %82, align 8, !tbaa !5 + %94 = getelementptr inbounds ptr, ptr %93, i64 6 + %95 = load ptr, ptr %94, align 8 + %96 = tail call noundef signext i8 %95(ptr noundef nonnull align 8 dereferenceable(570) %82, i8 noundef signext 10) + br label %97 + + 97: ; preds = %92, %89 + %98 = phi i8 [ %91, %89 ], [ %96, %92 ] + %99 = tail call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSo3putEc(ptr noundef nonnull align 8 dereferenceable(8) %76, i8 noundef signext %98) + %100 = tail call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSo5flushEv(ptr noundef nonnull align 8 dereferenceable(8) %99) + %101 = tail call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSo9_M_insertIbEERSoT_(ptr noundef nonnull align 8 dereferenceable(8) @_ZSt4cout, i1 noundef zeroext false) + %102 = load ptr, ptr %101, align 8, !tbaa !5 + %103 = getelementptr i8, ptr %102, i64 -24 + %104 = load i64, ptr %103, align 8 + %105 = getelementptr inbounds i8, ptr %101, i64 %104 + %106 = getelementptr inbounds %"class.std::basic_ios", ptr %105, i64 0, i32 5 + %107 = load ptr, ptr %106, align 8, !tbaa !8 + %108 = icmp eq ptr %107, null + br i1 %108, label %109, label %110 + + 109: ; preds = %97 + tail call void @_ZSt16__throw_bad_castv() #4 + unreachable + + 110: ; preds = %97 + %111 = getelementptr inbounds %"class.std::ctype", ptr %107, i64 0, i32 8 + %112 = load i8, ptr %111, align 8, !tbaa !20 + %113 = icmp eq i8 %112, 0 + br i1 %113, label %117, label %114 + + 114: ; preds = %110 + %115 = getelementptr inbounds %"class.std::ctype", ptr %107, i64 0, i32 9, i64 10 + %116 = load i8, ptr %115, align 1, !tbaa !23 + br label %122 + + 117: ; preds = %110 + tail call void @_ZNKSt5ctypeIcE13_M_widen_initEv(ptr noundef nonnull align 8 dereferenceable(570) %107) + %118 = load ptr, ptr %107, align 8, !tbaa !5 + %119 = getelementptr inbounds ptr, ptr %118, i64 6 + %120 = load ptr, ptr %119, align 8 + %121 = tail call noundef signext i8 %120(ptr noundef nonnull align 8 dereferenceable(570) %107, i8 noundef signext 10) + br label %122 + + 122: ; preds = %117, %114 + %123 = phi i8 [ %116, %114 ], [ %121, %117 ] + %124 = tail call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSo3putEc(ptr noundef nonnull align 8 dereferenceable(8) %101, i8 noundef signext %123) + %125 = tail call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSo5flushEv(ptr noundef nonnull align 8 dereferenceable(8) %124) + %126 = tail call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSo9_M_insertIbEERSoT_(ptr noundef nonnull align 8 dereferenceable(8) @_ZSt4cout, i1 noundef zeroext false) + %127 = load ptr, ptr %126, align 8, !tbaa !5 + %128 = getelementptr i8, ptr %127, i64 -24 + %129 = load i64, ptr %128, align 8 + %130 = getelementptr inbounds i8, ptr %126, i64 %129 + %131 = getelementptr inbounds %"class.std::basic_ios", ptr %130, i64 0, i32 5 + %132 = load ptr, ptr %131, align 8, !tbaa !8 + %133 = icmp eq ptr %132, null + br i1 %133, label %134, label %135 + + 134: ; preds = %122 + tail call void @_ZSt16__throw_bad_castv() #4 + unreachable + + 135: ; preds = %122 + %136 = getelementptr inbounds %"class.std::ctype", ptr %132, i64 0, i32 8 + %137 = load i8, ptr %136, align 8, !tbaa !20 + %138 = icmp eq i8 %137, 0 + br i1 %138, label %142, label %139 + + 139: ; preds = %135 + %140 = getelementptr inbounds %"class.std::ctype", ptr %132, i64 0, i32 9, i64 10 + %141 = load i8, ptr %140, align 1, !tbaa !23 + br label %147 + + 142: ; preds = %135 + tail call void @_ZNKSt5ctypeIcE13_M_widen_initEv(ptr noundef nonnull align 8 dereferenceable(570) %132) + %143 = load ptr, ptr %132, align 8, !tbaa !5 + %144 = getelementptr inbounds ptr, ptr %143, i64 6 + %145 = load ptr, ptr %144, align 8 + %146 = tail call noundef signext i8 %145(ptr noundef nonnull align 8 dereferenceable(570) %132, i8 noundef signext 10) + br label %147 + + 147: ; preds = %142, %139 + %148 = phi i8 [ %141, %139 ], [ %146, %142 ] + %149 = tail call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSo3putEc(ptr noundef nonnull align 8 dereferenceable(8) %126, i8 noundef signext %148) + %150 = tail call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSo5flushEv(ptr noundef nonnull align 8 dereferenceable(8) %149) + ret i32 0 + } + + declare noundef nonnull align 8 dereferenceable(8) ptr @_ZNSo9_M_insertIbEERSoT_(ptr noundef nonnull align 8 dereferenceable(8), i1 noundef zeroext) local_unnamed_addr #2 + + declare noundef nonnull align 8 dereferenceable(8) ptr @_ZNSo3putEc(ptr noundef nonnull align 8 dereferenceable(8), i8 noundef signext) local_unnamed_addr #2 + + declare noundef nonnull align 8 dereferenceable(8) ptr @_ZNSo5flushEv(ptr noundef nonnull align 8 dereferenceable(8)) local_unnamed_addr #2 + + ; Function Attrs: noreturn + declare void @_ZSt16__throw_bad_castv() local_unnamed_addr #3 + + declare void @_ZNKSt5ctypeIcE13_M_widen_initEv(ptr noundef nonnull align 8 dereferenceable(570)) local_unnamed_addr #2 + + attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } + attributes #1 = { mustprogress norecurse uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } + attributes #2 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } + attributes #3 = { noreturn "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } + attributes #4 = { noreturn } + + !llvm.module.flags = !{!0, !1, !2, !3} + !llvm.ident = !{!4} + + !0 = !{i32 1, !"wchar_size", i32 4} + !1 = !{i32 8, !"PIC Level", i32 2} + !2 = !{i32 7, !"PIE Level", i32 2} + !3 = !{i32 7, !"uwtable", i32 2} + !4 = !{!"Ubuntu clang version 18.1.3 (1ubuntu1)"} + !5 = !{!6, !6, i64 0} + !6 = !{!"vtable pointer", !7, i64 0} + !7 = !{!"Simple C++ TBAA"} + !8 = !{!9, !15, i64 240} + !9 = !{!"_ZTSSt9basic_iosIcSt11char_traitsIcEE", !10, i64 0, !15, i64 216, !12, i64 224, !19, i64 225, !15, i64 232, !15, i64 240, !15, i64 248, !15, i64 256} + !10 = !{!"_ZTSSt8ios_base", !11, i64 8, !11, i64 16, !13, i64 24, !14, i64 28, !14, i64 32, !15, i64 40, !16, i64 48, !12, i64 64, !17, i64 192, !15, i64 200, !18, i64 208} + !11 = !{!"long", !12, i64 0} + !12 = !{!"omnipotent char", !7, i64 0} + !13 = !{!"_ZTSSt13_Ios_Fmtflags", !12, i64 0} + !14 = !{!"_ZTSSt12_Ios_Iostate", !12, i64 0} + !15 = !{!"any pointer", !12, i64 0} + !16 = !{!"_ZTSNSt8ios_base6_WordsE", !15, i64 0, !11, i64 8} + !17 = !{!"int", !12, i64 0} + !18 = !{!"_ZTSSt6locale", !15, i64 0} + !19 = !{!"bool", !12, i64 0} + !20 = !{!21, !12, i64 56} + !21 = !{!"_ZTSSt5ctypeIcE", !22, i64 0, !15, i64 16, !19, i64 24, !15, i64 32, !15, i64 40, !15, i64 48, !12, i64 56, !12, i64 57, !12, i64 313, !12, i64 569} + !22 = !{!"_ZTSNSt6locale5facetE", !17, i64 8} + !23 = !{!12, !12, i64 0} + +... +--- +name: _Z11and_or_testiii +alignment: 16 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: true +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gr32, preferred-register: '' } + - { id: 1, class: gr32, preferred-register: '' } + - { id: 2, class: gr32, preferred-register: '' } + - { id: 3, class: gr8, preferred-register: '' } + - { id: 4, class: gr32, preferred-register: '' } + - { id: 5, class: gr32, preferred-register: '' } + - { id: 6, class: gr8, preferred-register: '' } +liveins: + - { reg: '$edi', virtual-reg: '%0' } + - { reg: '$esi', virtual-reg: '%1' } + - { reg: '$edx', virtual-reg: '%2' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 4294967295 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + amxProgModel: None +body: | + bb.0 (%ir-block.3): + liveins: $edi, $esi, $edx + + ; CHECK: %2:gr32 = COPY $edx + ; CHECK-NEXT: %1:gr32 = COPY $esi + ; CHECK-NEXT: %0:gr32 = COPY $edi + ; CHECK-NEXT: TEST32rr %1, %0, implicit-def $eflags + ; CHECK-NEXT: %3:gr8 = SETCCr 5, implicit $eflags + ; CHECK-NEXT: %4:gr32 = MOVZX32rr8 killed %3 + ; CHECK-NEXT: %5:gr32 = OR32rr %4, %2, implicit-def $eflags + ; CHECK-NEXT: %6:gr8 = SETCCr 5, implicit $eflags + ; CHECK-NEXT: $al = COPY %6 + ; CHECK-NEXT: RET 0, $al + + %2:gr32 = COPY $edx + %1:gr32 = COPY $esi + %0:gr32 = COPY $edi + TEST32rr %1, %0, implicit-def $eflags + %3:gr8 = SETCCr 5, implicit $eflags + %4:gr32 = MOVZX32rr8 killed %3 + %5:gr32 = OR32rr %4, %2, implicit-def $eflags + %6:gr8 = SETCCr 5, implicit $eflags + $al = COPY %6 + RET 0, $al + +... +--- +name: _Z11or_xor_testiii +alignment: 16 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: true +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gr32, preferred-register: '' } + - { id: 1, class: gr32, preferred-register: '' } + - { id: 2, class: gr32, preferred-register: '' } + - { id: 3, class: gr32, preferred-register: '' } + - { id: 4, class: gr8, preferred-register: '' } + - { id: 5, class: gr32, preferred-register: '' } + - { id: 6, class: gr32, preferred-register: '' } + - { id: 7, class: gr8, preferred-register: '' } +liveins: + - { reg: '$edi', virtual-reg: '%0' } + - { reg: '$esi', virtual-reg: '%1' } + - { reg: '$edx', virtual-reg: '%2' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 4294967295 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + amxProgModel: None +body: | + bb.0 (%ir-block.3): + liveins: $edi, $esi, $edx + + ; CHECK: %2:gr32 = COPY $edx + ; CHECK-NEXT: %1:gr32 = COPY $esi + ; CHECK-NEXT: %0:gr32 = COPY $edi + ; CHECK-NEXT: %3:gr32 = OR32rr %1, %0, implicit-def $eflags + ; CHECK-NEXT: %4:gr8 = SETCCr 5, implicit $eflags + ; CHECK-NEXT: %5:gr32 = MOVZX32rr8 killed %4 + ; CHECK-NEXT: %6:gr32 = SUB32rr %5, %2, implicit-def $eflags + ; CHECK-NEXT: %7:gr8 = SETCCr 5, implicit $eflags + ; CHECK-NEXT: $al = COPY %7 + ; CHECK-NEXT: RET 0, $al + + %2:gr32 = COPY $edx + %1:gr32 = COPY $esi + %0:gr32 = COPY $edi + %3:gr32 = OR32rr %1, %0, implicit-def $eflags + %4:gr8 = SETCCr 5, implicit $eflags + %5:gr32 = MOVZX32rr8 killed %4 + %6:gr32 = SUB32rr %5, %2, implicit-def $eflags + %7:gr8 = SETCCr 5, implicit $eflags + $al = COPY %7 + RET 0, $al + +... +--- +name: _Z12and_xor_testiii +alignment: 16 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: true +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gr32, preferred-register: '' } + - { id: 1, class: gr32, preferred-register: '' } + - { id: 2, class: gr32, preferred-register: '' } + - { id: 3, class: gr8, preferred-register: '' } + - { id: 4, class: gr32, preferred-register: '' } + - { id: 5, class: gr32, preferred-register: '' } + - { id: 6, class: gr8, preferred-register: '' } +liveins: + - { reg: '$edi', virtual-reg: '%0' } + - { reg: '$esi', virtual-reg: '%1' } + - { reg: '$edx', virtual-reg: '%2' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 4294967295 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + amxProgModel: None +body: | + bb.0 (%ir-block.3): + liveins: $edi, $esi, $edx + + ; CHECK: %2:gr32 = COPY $edx + ; CHECK-NEXT: %1:gr32 = COPY $esi + ; CHECK-NEXT: %0:gr32 = COPY $edi + ; CHECK-NEXT: TEST32rr %1, %0, implicit-def $eflags + ; CHECK-NEXT: %3:gr8 = SETCCr 5, implicit $eflags + ; CHECK-NEXT: %4:gr32 = MOVZX32rr8 killed %3 + ; CHECK-NEXT: %5:gr32 = SUB32rr %4, %2, implicit-def $eflags + ; CHECK-NEXT: %6:gr8 = SETCCr 5, implicit $eflags + ; CHECK-NEXT: $al = COPY %6 + ; CHECK-NEXT: RET 0, $al + + %2:gr32 = COPY $edx + %1:gr32 = COPY $esi + %0:gr32 = COPY $edi + TEST32rr %1, %0, implicit-def $eflags + %3:gr8 = SETCCr 5, implicit $eflags + %4:gr32 = MOVZX32rr8 killed %3 + %5:gr32 = SUB32rr %4, %2, implicit-def $eflags + %6:gr8 = SETCCr 5, implicit $eflags + $al = COPY %6 + RET 0, $al + +... +--- +name: _Z15and_or_xor_testiiii +alignment: 16 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: true +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gr32, preferred-register: '' } + - { id: 1, class: gr32, preferred-register: '' } + - { id: 2, class: gr32, preferred-register: '' } + - { id: 3, class: gr32, preferred-register: '' } + - { id: 4, class: gr8, preferred-register: '' } + - { id: 5, class: gr32, preferred-register: '' } + - { id: 6, class: gr32, preferred-register: '' } + - { id: 7, class: gr8, preferred-register: '' } + - { id: 8, class: gr32, preferred-register: '' } + - { id: 9, class: gr32, preferred-register: '' } + - { id: 10, class: gr8, preferred-register: '' } +liveins: + - { reg: '$edi', virtual-reg: '%0' } + - { reg: '$esi', virtual-reg: '%1' } + - { reg: '$edx', virtual-reg: '%2' } + - { reg: '$ecx', virtual-reg: '%3' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 4294967295 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + amxProgModel: None +body: | + bb.0 (%ir-block.4): + liveins: $edi, $esi, $edx, $ecx + + ; CHECK: %3:gr32 = COPY $ecx + ; CHECK-NEXT: %2:gr32 = COPY $edx + ; CHECK-NEXT: %1:gr32 = COPY $esi + ; CHECK-NEXT: %0:gr32 = COPY $edi + ; CHECK-NEXT: TEST32rr %1, %0, implicit-def $eflags + ; CHECK-NEXT: %4:gr8 = SETCCr 5, implicit $eflags + ; CHECK-NEXT: %5:gr32 = MOVZX32rr8 killed %4 + ; CHECK-NEXT: %6:gr32 = OR32rr %5, %2, implicit-def $eflags + ; CHECK-NEXT: %7:gr8 = SETCCr 5, implicit $eflags + ; CHECK-NEXT: %8:gr32 = MOVZX32rr8 killed %7 + ; CHECK-NEXT: %9:gr32 = SUB32rr %8, %3, implicit-def $eflags + ; CHECK-NEXT: %10:gr8 = SETCCr 5, implicit $eflags + ; CHECK-NEXT: $al = COPY %10 + ; CHECK-NEXT: RET 0, $al + + %3:gr32 = COPY $ecx + %2:gr32 = COPY $edx + %1:gr32 = COPY $esi + %0:gr32 = COPY $edi + TEST32rr %1, %0, implicit-def $eflags + %4:gr8 = SETCCr 5, implicit $eflags + %5:gr32 = MOVZX32rr8 killed %4 + %6:gr32 = OR32rr %5, %2, implicit-def $eflags + %7:gr8 = SETCCr 5, implicit $eflags + %8:gr32 = MOVZX32rr8 killed %7 + %9:gr32 = SUB32rr %8, %3, implicit-def $eflags + %10:gr8 = SETCCr 5, implicit $eflags + $al = COPY %10 + RET 0, $al + +... +--- +name: _Z12and_and_testiii +alignment: 16 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: true +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gr32, preferred-register: '' } + - { id: 1, class: gr32, preferred-register: '' } + - { id: 2, class: gr32, preferred-register: '' } + - { id: 3, class: gr8, preferred-register: '' } + - { id: 4, class: gr8, preferred-register: '' } + - { id: 5, class: gr8, preferred-register: '' } +liveins: + - { reg: '$edi', virtual-reg: '%0' } + - { reg: '$esi', virtual-reg: '%1' } + - { reg: '$edx', virtual-reg: '%2' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 4294967295 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + amxProgModel: None +body: | + bb.0 (%ir-block.3): + liveins: $edi, $esi, $edx + + ; CHECK: %2:gr32 = COPY $edx + ; CHECK-NEXT: %1:gr32 = COPY $esi + ; CHECK-NEXT: %0:gr32 = COPY $edi + ; CHECK-NEXT: TEST32rr %1, %0, implicit-def $eflags + ; CHECK-NEXT: %3:gr8 = SETCCr 5, implicit $eflags + ; CHECK-NEXT: %4:gr8 = COPY %2.sub_8bit + ; CHECK-NEXT: %5:gr8 = AND8rr %3, killed %4, implicit-def dead $eflags + ; CHECK-NEXT: $al = COPY %5 + ; CHECK-NEXT: RET 0, $al + + %2:gr32 = COPY $edx + %1:gr32 = COPY $esi + %0:gr32 = COPY $edi + TEST32rr %1, %0, implicit-def $eflags + %3:gr8 = SETCCr 5, implicit $eflags + %4:gr8 = COPY %2.sub_8bit + %5:gr8 = AND8rr %3, killed %4, implicit-def dead $eflags + $al = COPY %5 + RET 0, $al + +... +--- +name: _Z12xor_xor_testiii +alignment: 16 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: true +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gr32, preferred-register: '' } + - { id: 1, class: gr32, preferred-register: '' } + - { id: 2, class: gr32, preferred-register: '' } + - { id: 3, class: gr32, preferred-register: '' } + - { id: 4, class: gr8, preferred-register: '' } + - { id: 5, class: gr32, preferred-register: '' } + - { id: 6, class: gr32, preferred-register: '' } + - { id: 7, class: gr8, preferred-register: '' } +liveins: + - { reg: '$edi', virtual-reg: '%0' } + - { reg: '$esi', virtual-reg: '%1' } + - { reg: '$edx', virtual-reg: '%2' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 4294967295 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + amxProgModel: None +body: | + bb.0 (%ir-block.3): + liveins: $edi, $esi, $edx + + ; CHECK: %2:gr32 = COPY $edx + ; CHECK-NEXT: %1:gr32 = COPY $esi + ; CHECK-NEXT: %0:gr32 = COPY $edi + ; CHECK-NEXT: %3:gr32 = SUB32rr %0, %1, implicit-def $eflags + ; CHECK-NEXT: %4:gr8 = SETCCr 5, implicit $eflags + ; CHECK-NEXT: %5:gr32 = MOVZX32rr8 killed %4 + ; CHECK-NEXT: %6:gr32 = SUB32rr %5, %2, implicit-def $eflags + ; CHECK-NEXT: %7:gr8 = SETCCr 5, implicit $eflags + ; CHECK-NEXT: $al = COPY %7 + ; CHECK-NEXT: RET 0, $al + + %2:gr32 = COPY $edx + %1:gr32 = COPY $esi + %0:gr32 = COPY $edi + %3:gr32 = SUB32rr %0, %1, implicit-def $eflags + %4:gr8 = SETCCr 5, implicit $eflags + %5:gr32 = MOVZX32rr8 killed %4 + %6:gr32 = SUB32rr %5, %2, implicit-def $eflags + %7:gr8 = SETCCr 5, implicit $eflags + $al = COPY %7 + RET 0, $al + +... +--- +name: main +alignment: 16 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: true +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gr64, preferred-register: '' } + - { id: 1, class: gr64, preferred-register: '' } + - { id: 2, class: gr8, preferred-register: '' } + - { id: 3, class: gr8, preferred-register: '' } + - { id: 4, class: gr8, preferred-register: '' } + - { id: 5, class: gr64, preferred-register: '' } + - { id: 6, class: gr64, preferred-register: '' } + - { id: 7, class: gr8, preferred-register: '' } + - { id: 8, class: gr8, preferred-register: '' } + - { id: 9, class: gr8, preferred-register: '' } + - { id: 10, class: gr64, preferred-register: '' } + - { id: 11, class: gr64, preferred-register: '' } + - { id: 12, class: gr8, preferred-register: '' } + - { id: 13, class: gr8, preferred-register: '' } + - { id: 14, class: gr8, preferred-register: '' } + - { id: 15, class: gr64, preferred-register: '' } + - { id: 16, class: gr64, preferred-register: '' } + - { id: 17, class: gr8, preferred-register: '' } + - { id: 18, class: gr8, preferred-register: '' } + - { id: 19, class: gr8, preferred-register: '' } + - { id: 20, class: gr64, preferred-register: '' } + - { id: 21, class: gr64, preferred-register: '' } + - { id: 22, class: gr8, preferred-register: '' } + - { id: 23, class: gr8, preferred-register: '' } + - { id: 24, class: gr8, preferred-register: '' } + - { id: 25, class: gr64, preferred-register: '' } + - { id: 26, class: gr64, preferred-register: '' } + - { id: 27, class: gr8, preferred-register: '' } + - { id: 28, class: gr8, preferred-register: '' } + - { id: 29, class: gr8, preferred-register: '' } + - { id: 30, class: gr64, preferred-register: '' } + - { id: 31, class: gr32, preferred-register: '' } + - { id: 32, class: gr64, preferred-register: '' } + - { id: 33, class: gr64, preferred-register: '' } + - { id: 34, class: gr64_nosp, preferred-register: '' } + - { id: 35, class: gr64, preferred-register: '' } + - { id: 36, class: gr32, preferred-register: '' } + - { id: 37, class: gr8, preferred-register: '' } + - { id: 38, class: gr32, preferred-register: '' } + - { id: 39, class: gr64, preferred-register: '' } + - { id: 40, class: gr64, preferred-register: '' } + - { id: 41, class: gr64, preferred-register: '' } + - { id: 42, class: gr32, preferred-register: '' } + - { id: 43, class: gr64, preferred-register: '' } + - { id: 44, class: gr64, preferred-register: '' } + - { id: 45, class: gr64_nosp, preferred-register: '' } + - { id: 46, class: gr64, preferred-register: '' } + - { id: 47, class: gr32, preferred-register: '' } + - { id: 48, class: gr8, preferred-register: '' } + - { id: 49, class: gr32, preferred-register: '' } + - { id: 50, class: gr64, preferred-register: '' } + - { id: 51, class: gr64, preferred-register: '' } + - { id: 52, class: gr64, preferred-register: '' } + - { id: 53, class: gr32, preferred-register: '' } + - { id: 54, class: gr64, preferred-register: '' } + - { id: 55, class: gr64, preferred-register: '' } + - { id: 56, class: gr64_nosp, preferred-register: '' } + - { id: 57, class: gr64, preferred-register: '' } + - { id: 58, class: gr32, preferred-register: '' } + - { id: 59, class: gr8, preferred-register: '' } + - { id: 60, class: gr32, preferred-register: '' } + - { id: 61, class: gr64, preferred-register: '' } + - { id: 62, class: gr64, preferred-register: '' } + - { id: 63, class: gr64, preferred-register: '' } + - { id: 64, class: gr32, preferred-register: '' } + - { id: 65, class: gr64, preferred-register: '' } + - { id: 66, class: gr64, preferred-register: '' } + - { id: 67, class: gr64_nosp, preferred-register: '' } + - { id: 68, class: gr64, preferred-register: '' } + - { id: 69, class: gr32, preferred-register: '' } + - { id: 70, class: gr8, preferred-register: '' } + - { id: 71, class: gr32, preferred-register: '' } + - { id: 72, class: gr64, preferred-register: '' } + - { id: 73, class: gr64, preferred-register: '' } + - { id: 74, class: gr64, preferred-register: '' } + - { id: 75, class: gr32, preferred-register: '' } + - { id: 76, class: gr64, preferred-register: '' } + - { id: 77, class: gr64, preferred-register: '' } + - { id: 78, class: gr64_nosp, preferred-register: '' } + - { id: 79, class: gr64, preferred-register: '' } + - { id: 80, class: gr32, preferred-register: '' } + - { id: 81, class: gr8, preferred-register: '' } + - { id: 82, class: gr32, preferred-register: '' } + - { id: 83, class: gr64, preferred-register: '' } + - { id: 84, class: gr64, preferred-register: '' } + - { id: 85, class: gr64, preferred-register: '' } + - { id: 86, class: gr32, preferred-register: '' } + - { id: 87, class: gr64, preferred-register: '' } + - { id: 88, class: gr64, preferred-register: '' } + - { id: 89, class: gr64_nosp, preferred-register: '' } + - { id: 90, class: gr64, preferred-register: '' } + - { id: 91, class: gr32, preferred-register: '' } + - { id: 92, class: gr8, preferred-register: '' } + - { id: 93, class: gr32, preferred-register: '' } + - { id: 94, class: gr64, preferred-register: '' } + - { id: 95, class: gr64, preferred-register: '' } + - { id: 96, class: gr32, preferred-register: '' } +liveins: [] +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: true + hasCalls: true + stackProtector: '' + functionContext: '' + maxCallFrameSize: 4294967295 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + amxProgModel: None +body: | + bb.0 (%ir-block.0): + successors: %bb.1(0x00000800), %bb.2(0x7ffff800) + + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %30:gr64 = MOV64rm $rip, 1, $noreg, target-flags(x86-gotpcrel) @_ZSt4cout, $noreg :: (load (s64) from got) + %31:gr32 = MOV32ri 1 + $rdi = COPY %30 + $esi = COPY %31 + CALL64pcrel32 target-flags(x86-plt) @_ZNSo9_M_insertIbEERSoT_, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $esi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %32:gr64 = COPY $rax + %0:gr64 = COPY %32 + %33:gr64 = MOV64rm %32, 1, $noreg, 0, $noreg :: (dereferenceable load (s64) from %ir.1, !tbaa !5) + %34:gr64_nosp = MOV64rm killed %33, 1, $noreg, -24, $noreg :: (load (s64) from %ir.3) + %1:gr64 = MOV64rm %32, 1, killed %34, 240, $noreg :: (load (s64) from %ir.6, !tbaa !8) + TEST64rr %1, %1, implicit-def $eflags + JCC_1 %bb.2, 5, implicit $eflags + JMP_1 %bb.1 + + bb.1 (%ir-block.9): + successors: + + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + CALL64pcrel32 target-flags(x86-plt) @_ZSt16__throw_bad_castv, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + + bb.2 (%ir-block.10): + successors: %bb.4(0x30000000), %bb.3(0x50000000) + + CMP8mi %1, 1, $noreg, 56, $noreg, 0, implicit-def $eflags :: (load (s8) from %ir.11, align 8, !tbaa !20) + JCC_1 %bb.4, 4, implicit $eflags + JMP_1 %bb.3 + + bb.3 (%ir-block.14): + successors: %bb.5(0x80000000) + + %2:gr8 = MOV8rm %1, 1, $noreg, 67, $noreg :: (load (s8) from %ir.15, !tbaa !23) + JMP_1 %bb.5 + + bb.4 (%ir-block.17): + successors: %bb.5(0x80000000) + + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + $rdi = COPY %1 + CALL64pcrel32 target-flags(x86-plt) @_ZNKSt5ctypeIcE13_M_widen_initEv, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %35:gr64 = MOV64rm %1, 1, $noreg, 0, $noreg :: (load (s64) from %ir.7, !tbaa !5) + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %36:gr32 = MOV32ri 10 + $rdi = COPY %1 + $esi = COPY %36 + CALL64m killed %35, 1, $noreg, 48, $noreg, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $esi, implicit-def $rsp, implicit-def $ssp, implicit-def $al :: (load (s64) from %ir.19) + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %37:gr8 = COPY $al + %3:gr8 = COPY %37 + + bb.5 (%ir-block.22): + successors: %bb.6(0x00000800), %bb.7(0x7ffff800) + + %4:gr8 = PHI %2, %bb.3, %3, %bb.4 + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %38:gr32 = MOVSX32rr8 %4 + $rdi = COPY %0 + $esi = COPY %38 + CALL64pcrel32 target-flags(x86-plt) @_ZNSo3putEc, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $esi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %39:gr64 = COPY $rax + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + $rdi = COPY %39 + CALL64pcrel32 target-flags(x86-plt) @_ZNSo5flushEv, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %40:gr64 = COPY $rax + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %41:gr64 = MOV64rm $rip, 1, $noreg, target-flags(x86-gotpcrel) @_ZSt4cout, $noreg :: (load (s64) from got) + %42:gr32 = MOV32r0 implicit-def dead $eflags + $rdi = COPY %41 + $esi = COPY %42 + CALL64pcrel32 target-flags(x86-plt) @_ZNSo9_M_insertIbEERSoT_, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $esi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %43:gr64 = COPY $rax + %5:gr64 = COPY %43 + %44:gr64 = MOV64rm %43, 1, $noreg, 0, $noreg :: (dereferenceable load (s64) from %ir.26, !tbaa !5) + %45:gr64_nosp = MOV64rm killed %44, 1, $noreg, -24, $noreg :: (load (s64) from %ir.28) + %6:gr64 = MOV64rm %43, 1, killed %45, 240, $noreg :: (load (s64) from %ir.31, !tbaa !8) + TEST64rr %6, %6, implicit-def $eflags + JCC_1 %bb.7, 5, implicit $eflags + JMP_1 %bb.6 + + bb.6 (%ir-block.34): + successors: + + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + CALL64pcrel32 target-flags(x86-plt) @_ZSt16__throw_bad_castv, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + + bb.7 (%ir-block.35): + successors: %bb.9(0x30000000), %bb.8(0x50000000) + + CMP8mi %6, 1, $noreg, 56, $noreg, 0, implicit-def $eflags :: (load (s8) from %ir.36, align 8, !tbaa !20) + JCC_1 %bb.9, 4, implicit $eflags + JMP_1 %bb.8 + + bb.8 (%ir-block.39): + successors: %bb.10(0x80000000) + + %7:gr8 = MOV8rm %6, 1, $noreg, 67, $noreg :: (load (s8) from %ir.40, !tbaa !23) + JMP_1 %bb.10 + + bb.9 (%ir-block.42): + successors: %bb.10(0x80000000) + + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + $rdi = COPY %6 + CALL64pcrel32 target-flags(x86-plt) @_ZNKSt5ctypeIcE13_M_widen_initEv, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %46:gr64 = MOV64rm %6, 1, $noreg, 0, $noreg :: (load (s64) from %ir.32, !tbaa !5) + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %47:gr32 = MOV32ri 10 + $rdi = COPY %6 + $esi = COPY %47 + CALL64m killed %46, 1, $noreg, 48, $noreg, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $esi, implicit-def $rsp, implicit-def $ssp, implicit-def $al :: (load (s64) from %ir.44) + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %48:gr8 = COPY $al + %8:gr8 = COPY %48 + + bb.10 (%ir-block.47): + successors: %bb.11(0x00000800), %bb.12(0x7ffff800) + + %9:gr8 = PHI %7, %bb.8, %8, %bb.9 + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %49:gr32 = MOVSX32rr8 %9 + $rdi = COPY %5 + $esi = COPY %49 + CALL64pcrel32 target-flags(x86-plt) @_ZNSo3putEc, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $esi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %50:gr64 = COPY $rax + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + $rdi = COPY %50 + CALL64pcrel32 target-flags(x86-plt) @_ZNSo5flushEv, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %51:gr64 = COPY $rax + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %52:gr64 = MOV64rm $rip, 1, $noreg, target-flags(x86-gotpcrel) @_ZSt4cout, $noreg :: (load (s64) from got) + %53:gr32 = MOV32ri 1 + $rdi = COPY %52 + $esi = COPY %53 + CALL64pcrel32 target-flags(x86-plt) @_ZNSo9_M_insertIbEERSoT_, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $esi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %54:gr64 = COPY $rax + %10:gr64 = COPY %54 + %55:gr64 = MOV64rm %54, 1, $noreg, 0, $noreg :: (dereferenceable load (s64) from %ir.51, !tbaa !5) + %56:gr64_nosp = MOV64rm killed %55, 1, $noreg, -24, $noreg :: (load (s64) from %ir.53) + %11:gr64 = MOV64rm %54, 1, killed %56, 240, $noreg :: (load (s64) from %ir.56, !tbaa !8) + TEST64rr %11, %11, implicit-def $eflags + JCC_1 %bb.12, 5, implicit $eflags + JMP_1 %bb.11 + + bb.11 (%ir-block.59): + successors: + + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + CALL64pcrel32 target-flags(x86-plt) @_ZSt16__throw_bad_castv, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + + bb.12 (%ir-block.60): + successors: %bb.14(0x30000000), %bb.13(0x50000000) + + CMP8mi %11, 1, $noreg, 56, $noreg, 0, implicit-def $eflags :: (load (s8) from %ir.61, align 8, !tbaa !20) + JCC_1 %bb.14, 4, implicit $eflags + JMP_1 %bb.13 + + bb.13 (%ir-block.64): + successors: %bb.15(0x80000000) + + %12:gr8 = MOV8rm %11, 1, $noreg, 67, $noreg :: (load (s8) from %ir.65, !tbaa !23) + JMP_1 %bb.15 + + bb.14 (%ir-block.67): + successors: %bb.15(0x80000000) + + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + $rdi = COPY %11 + CALL64pcrel32 target-flags(x86-plt) @_ZNKSt5ctypeIcE13_M_widen_initEv, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %57:gr64 = MOV64rm %11, 1, $noreg, 0, $noreg :: (load (s64) from %ir.57, !tbaa !5) + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %58:gr32 = MOV32ri 10 + $rdi = COPY %11 + $esi = COPY %58 + CALL64m killed %57, 1, $noreg, 48, $noreg, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $esi, implicit-def $rsp, implicit-def $ssp, implicit-def $al :: (load (s64) from %ir.69) + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %59:gr8 = COPY $al + %13:gr8 = COPY %59 + + bb.15 (%ir-block.72): + successors: %bb.16(0x00000800), %bb.17(0x7ffff800) + + %14:gr8 = PHI %12, %bb.13, %13, %bb.14 + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %60:gr32 = MOVSX32rr8 %14 + $rdi = COPY %10 + $esi = COPY %60 + CALL64pcrel32 target-flags(x86-plt) @_ZNSo3putEc, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $esi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %61:gr64 = COPY $rax + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + $rdi = COPY %61 + CALL64pcrel32 target-flags(x86-plt) @_ZNSo5flushEv, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %62:gr64 = COPY $rax + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %63:gr64 = MOV64rm $rip, 1, $noreg, target-flags(x86-gotpcrel) @_ZSt4cout, $noreg :: (load (s64) from got) + %64:gr32 = MOV32ri 1 + $rdi = COPY %63 + $esi = COPY %64 + CALL64pcrel32 target-flags(x86-plt) @_ZNSo9_M_insertIbEERSoT_, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $esi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %65:gr64 = COPY $rax + %15:gr64 = COPY %65 + %66:gr64 = MOV64rm %65, 1, $noreg, 0, $noreg :: (dereferenceable load (s64) from %ir.76, !tbaa !5) + %67:gr64_nosp = MOV64rm killed %66, 1, $noreg, -24, $noreg :: (load (s64) from %ir.78) + %16:gr64 = MOV64rm %65, 1, killed %67, 240, $noreg :: (load (s64) from %ir.81, !tbaa !8) + TEST64rr %16, %16, implicit-def $eflags + JCC_1 %bb.17, 5, implicit $eflags + JMP_1 %bb.16 + + bb.16 (%ir-block.84): + successors: + + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + CALL64pcrel32 target-flags(x86-plt) @_ZSt16__throw_bad_castv, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + + bb.17 (%ir-block.85): + successors: %bb.19(0x30000000), %bb.18(0x50000000) + + CMP8mi %16, 1, $noreg, 56, $noreg, 0, implicit-def $eflags :: (load (s8) from %ir.86, align 8, !tbaa !20) + JCC_1 %bb.19, 4, implicit $eflags + JMP_1 %bb.18 + + bb.18 (%ir-block.89): + successors: %bb.20(0x80000000) + + %17:gr8 = MOV8rm %16, 1, $noreg, 67, $noreg :: (load (s8) from %ir.90, !tbaa !23) + JMP_1 %bb.20 + + bb.19 (%ir-block.92): + successors: %bb.20(0x80000000) + + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + $rdi = COPY %16 + CALL64pcrel32 target-flags(x86-plt) @_ZNKSt5ctypeIcE13_M_widen_initEv, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %68:gr64 = MOV64rm %16, 1, $noreg, 0, $noreg :: (load (s64) from %ir.82, !tbaa !5) + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %69:gr32 = MOV32ri 10 + $rdi = COPY %16 + $esi = COPY %69 + CALL64m killed %68, 1, $noreg, 48, $noreg, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $esi, implicit-def $rsp, implicit-def $ssp, implicit-def $al :: (load (s64) from %ir.94) + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %70:gr8 = COPY $al + %18:gr8 = COPY %70 + + bb.20 (%ir-block.97): + successors: %bb.21(0x00000800), %bb.22(0x7ffff800) + + %19:gr8 = PHI %17, %bb.18, %18, %bb.19 + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %71:gr32 = MOVSX32rr8 %19 + $rdi = COPY %15 + $esi = COPY %71 + CALL64pcrel32 target-flags(x86-plt) @_ZNSo3putEc, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $esi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %72:gr64 = COPY $rax + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + $rdi = COPY %72 + CALL64pcrel32 target-flags(x86-plt) @_ZNSo5flushEv, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %73:gr64 = COPY $rax + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %74:gr64 = MOV64rm $rip, 1, $noreg, target-flags(x86-gotpcrel) @_ZSt4cout, $noreg :: (load (s64) from got) + %75:gr32 = MOV32r0 implicit-def dead $eflags + $rdi = COPY %74 + $esi = COPY %75 + CALL64pcrel32 target-flags(x86-plt) @_ZNSo9_M_insertIbEERSoT_, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $esi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %76:gr64 = COPY $rax + %20:gr64 = COPY %76 + %77:gr64 = MOV64rm %76, 1, $noreg, 0, $noreg :: (dereferenceable load (s64) from %ir.101, !tbaa !5) + %78:gr64_nosp = MOV64rm killed %77, 1, $noreg, -24, $noreg :: (load (s64) from %ir.103) + %21:gr64 = MOV64rm %76, 1, killed %78, 240, $noreg :: (load (s64) from %ir.106, !tbaa !8) + TEST64rr %21, %21, implicit-def $eflags + JCC_1 %bb.22, 5, implicit $eflags + JMP_1 %bb.21 + + bb.21 (%ir-block.109): + successors: + + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + CALL64pcrel32 target-flags(x86-plt) @_ZSt16__throw_bad_castv, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + + bb.22 (%ir-block.110): + successors: %bb.24(0x30000000), %bb.23(0x50000000) + + CMP8mi %21, 1, $noreg, 56, $noreg, 0, implicit-def $eflags :: (load (s8) from %ir.111, align 8, !tbaa !20) + JCC_1 %bb.24, 4, implicit $eflags + JMP_1 %bb.23 + + bb.23 (%ir-block.114): + successors: %bb.25(0x80000000) + + %22:gr8 = MOV8rm %21, 1, $noreg, 67, $noreg :: (load (s8) from %ir.115, !tbaa !23) + JMP_1 %bb.25 + + bb.24 (%ir-block.117): + successors: %bb.25(0x80000000) + + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + $rdi = COPY %21 + CALL64pcrel32 target-flags(x86-plt) @_ZNKSt5ctypeIcE13_M_widen_initEv, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %79:gr64 = MOV64rm %21, 1, $noreg, 0, $noreg :: (load (s64) from %ir.107, !tbaa !5) + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %80:gr32 = MOV32ri 10 + $rdi = COPY %21 + $esi = COPY %80 + CALL64m killed %79, 1, $noreg, 48, $noreg, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $esi, implicit-def $rsp, implicit-def $ssp, implicit-def $al :: (load (s64) from %ir.119) + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %81:gr8 = COPY $al + %23:gr8 = COPY %81 + + bb.25 (%ir-block.122): + successors: %bb.26(0x00000800), %bb.27(0x7ffff800) + + %24:gr8 = PHI %22, %bb.23, %23, %bb.24 + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %82:gr32 = MOVSX32rr8 %24 + $rdi = COPY %20 + $esi = COPY %82 + CALL64pcrel32 target-flags(x86-plt) @_ZNSo3putEc, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $esi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %83:gr64 = COPY $rax + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + $rdi = COPY %83 + CALL64pcrel32 target-flags(x86-plt) @_ZNSo5flushEv, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %84:gr64 = COPY $rax + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %85:gr64 = MOV64rm $rip, 1, $noreg, target-flags(x86-gotpcrel) @_ZSt4cout, $noreg :: (load (s64) from got) + %86:gr32 = MOV32r0 implicit-def dead $eflags + $rdi = COPY %85 + $esi = COPY %86 + CALL64pcrel32 target-flags(x86-plt) @_ZNSo9_M_insertIbEERSoT_, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $esi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %87:gr64 = COPY $rax + %25:gr64 = COPY %87 + %88:gr64 = MOV64rm %87, 1, $noreg, 0, $noreg :: (dereferenceable load (s64) from %ir.126, !tbaa !5) + %89:gr64_nosp = MOV64rm killed %88, 1, $noreg, -24, $noreg :: (load (s64) from %ir.128) + %26:gr64 = MOV64rm %87, 1, killed %89, 240, $noreg :: (load (s64) from %ir.131, !tbaa !8) + TEST64rr %26, %26, implicit-def $eflags + JCC_1 %bb.27, 5, implicit $eflags + JMP_1 %bb.26 + + bb.26 (%ir-block.134): + successors: + + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + CALL64pcrel32 target-flags(x86-plt) @_ZSt16__throw_bad_castv, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + + bb.27 (%ir-block.135): + successors: %bb.29(0x30000000), %bb.28(0x50000000) + + CMP8mi %26, 1, $noreg, 56, $noreg, 0, implicit-def $eflags :: (load (s8) from %ir.136, align 8, !tbaa !20) + JCC_1 %bb.29, 4, implicit $eflags + JMP_1 %bb.28 + + bb.28 (%ir-block.139): + successors: %bb.30(0x80000000) + + %27:gr8 = MOV8rm %26, 1, $noreg, 67, $noreg :: (load (s8) from %ir.140, !tbaa !23) + JMP_1 %bb.30 + + bb.29 (%ir-block.142): + successors: %bb.30(0x80000000) + + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + $rdi = COPY %26 + CALL64pcrel32 target-flags(x86-plt) @_ZNKSt5ctypeIcE13_M_widen_initEv, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %90:gr64 = MOV64rm %26, 1, $noreg, 0, $noreg :: (load (s64) from %ir.132, !tbaa !5) + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %91:gr32 = MOV32ri 10 + $rdi = COPY %26 + $esi = COPY %91 + CALL64m killed %90, 1, $noreg, 48, $noreg, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $esi, implicit-def $rsp, implicit-def $ssp, implicit-def $al :: (load (s64) from %ir.144) + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %92:gr8 = COPY $al + %28:gr8 = COPY %92 + + bb.30 (%ir-block.147): + %29:gr8 = PHI %27, %bb.28, %28, %bb.29 + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %93:gr32 = MOVSX32rr8 %29 + $rdi = COPY %25 + $esi = COPY %93 + CALL64pcrel32 target-flags(x86-plt) @_ZNSo3putEc, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $esi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %94:gr64 = COPY $rax + ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + $rdi = COPY %94 + CALL64pcrel32 target-flags(x86-plt) @_ZNSo5flushEv, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax + ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp + %95:gr64 = COPY $rax + %96:gr32 = MOV32r0 implicit-def dead $eflags + $eax = COPY %96 + RET 0, $eax + +...