diff --git a/.github/workflows/clang-format.yaml b/.github/workflows/clang-format.yaml index cbb2777e..fb99263a 100644 --- a/.github/workflows/clang-format.yaml +++ b/.github/workflows/clang-format.yaml @@ -10,5 +10,6 @@ jobs: - name: Run clang-format style check uses: jidicula/clang-format-action@v4.13.0 with: - clang-format-version: '14' + clang-format-version: '19' check-path: '.' + fallback-style: 'Google' diff --git a/.github/workflows/run-tests.yaml b/.github/workflows/run-tests.yaml index a809b7af..493977b8 100644 --- a/.github/workflows/run-tests.yaml +++ b/.github/workflows/run-tests.yaml @@ -3,6 +3,7 @@ on: push: pull_request: branches: [ "master" ] + jobs: unit-tests: runs-on: ubuntu-latest @@ -10,11 +11,11 @@ jobs: run: shell: bash container: - image: silkeh/clang:18 + image: silkeh/clang:19 options: --user root steps: - name: Install deps - run: apt update && apt install -y git ninja-build valgrind libboost-context-dev libgflags-dev + run: apt update && apt install -y git ninja-build valgrind libboost-context-dev libgflags-dev libclang-19-dev - name: Check out repository code uses: actions/checkout@v4 - name: Build @@ -29,12 +30,12 @@ jobs: run: shell: bash container: - image: silkeh/clang:18 + image: silkeh/clang:19 options: --user root steps: - name: Install deps run: | - apt update && apt install -y git ninja-build valgrind libgoogle-glog-dev libsnappy-dev protobuf-compiler libboost-context-dev pkg-config libcapstone-dev && \ + apt update && apt install -y git ninja-build valgrind libgoogle-glog-dev libsnappy-dev protobuf-compiler libboost-context-dev pkg-config libcapstone-dev libclang-19-dev && \ git clone https://github.com/Kirillog/syscall_intercept.git && \ cmake syscall_intercept -G Ninja -B syscall_intercept/build -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang && \ cmake --build syscall_intercept/build --target install @@ -100,12 +101,12 @@ jobs: run: shell: bash container: - image: silkeh/clang:18 + image: silkeh/clang:19 options: --user root steps: - name: Install deps run: | - apt update && apt install -y git ninja-build valgrind libgoogle-glog-dev libsnappy-dev protobuf-compiler libboost-context-dev pkg-config libcapstone-dev && \ + apt update && apt install -y git ninja-build valgrind libgoogle-glog-dev libsnappy-dev protobuf-compiler libboost-context-dev pkg-config libcapstone-dev libclang-19-dev && \ git clone https://github.com/Kirillog/syscall_intercept.git && \ cmake syscall_intercept -G Ninja -B syscall_intercept/build -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang && \ cmake --build syscall_intercept/build --target install diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a0d66cd..e16ac4b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,13 @@ project(lintest) set(CMAKE_CXX_STANDARD 20) +# in order for clang tool to work, this must be ON (it will generate the compilation database) +# see https://clang.llvm.org/docs/JSONCompilationDatabase.html set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +# set this to ON in order to substitute std::atomic with custom implementation +set(APPLY_CLANG_TOOL OFF) + # TODO(kmitkin): require to understand, what is it considered to be "optimized" build # set(CMAKE_CXX_FLAGS_RELEASE "???") set(CMAKE_CXX_FLAGS_DEBUG "-g -ggdb3 -O0 -fno-omit-frame-pointer") @@ -24,6 +29,7 @@ include(GoogleTest) fuzztest_setup_fuzzing_flags() enable_testing() +add_subdirectory(clangpass) add_subdirectory(codegen) add_subdirectory(runtime) diff --git a/Dockerfile b/Dockerfile index 377dbfd1..1b55f1ad 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM silkeh/clang:19 AS ltest -RUN apt update && apt install -y git ninja-build valgrind libboost-context-dev libgflags-dev libstdc++-11-dev +RUN apt update && apt install -y git ninja-build valgrind libboost-context-dev libgflags-dev libstdc++-11-dev libclang-19-dev RUN mv /usr/lib/gcc/x86_64-linux-gnu/12 /usr/lib/gcc/x86_64-linux-gnu/_12 FROM ltest as blocking diff --git a/clangpass/CMakeLists.txt b/clangpass/CMakeLists.txt new file mode 100644 index 00000000..2eae4dda --- /dev/null +++ b/clangpass/CMakeLists.txt @@ -0,0 +1,64 @@ +#=============================================================================== +# SETUP CLANG PLUGIN +#=============================================================================== +find_package(Clang REQUIRED CONFIG) +if("${LLVM_VERSION_MAJOR}" VERSION_LESS 19) + message(FATAL_ERROR "Found LLVM ${LLVM_VERSION_MAJOR}, but need LLVM 19 or above") +endif() + +include_directories(SYSTEM "${LLVM_INCLUDE_DIRS};${CLANG_INCLUDE_DIRS}") + +if(NOT LLVM_ENABLE_RTTI) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") +endif() + +# -fvisibility-inlines-hidden is set when building LLVM and on Darwin warnings +# are triggered if llvm-tutor is built without this flag (though otherwise it +# builds fine). For consistency, add it here too. +include(CheckCXXCompilerFlag) +check_cxx_compiler_flag("-fvisibility-inlines-hidden" + SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG) +if(${SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG} EQUAL "1") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden") +endif() + +add_library(ClangPass SHARED "${CMAKE_CURRENT_SOURCE_DIR}/clangpass.cpp") + +target_include_directories( + ClangPass + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/include" +) + +# Allow undefined symbols in shared objects on Darwin (this is the default +# behaviour on Linux) +target_link_libraries(ClangPass + "$<$:-undefined dynamic_lookup>") + + +#=============================================================================== +# SETUP CLANG TOOL +#=============================================================================== +set(CLANG_TOOL "ClangPassTool") +set(CLANG_TOOL_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/clangpass_tool.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/clangpass.cpp" +) + +add_executable( + ${CLANG_TOOL} + ${CLANG_TOOL_SOURCES} +) + +# Configure include directories for 'tool' +target_include_directories( + ${CLANG_TOOL} + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/include" +) + +# Link in the required libraries +target_link_libraries( + ${CLANG_TOOL} + clangTooling +) \ No newline at end of file diff --git a/clangpass/clangpass.cpp b/clangpass/clangpass.cpp new file mode 100644 index 00000000..8b03e5ef --- /dev/null +++ b/clangpass/clangpass.cpp @@ -0,0 +1,218 @@ +//============================================================================== +// FILE: +// clangpass.cpp +// +// DESCRIPTION: +// Substitutes all input files std::atomic usages with LTestAtomic with +// the same API. +// +// USAGE: +// clang++ -Xclang -load -Xclang ./build/lib/libClangPass.so -Xclang -add-plugin -Xclang ClangPass ./AtomicsReplacer/test-project/main.cpp +// +// License: The Unlicense +//============================================================================== + +#include "clang/AST/ASTConsumer.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Rewrite/Frontend/FixItRewriter.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Tooling/Refactoring/Rename/RenamingAction.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/raw_ostream.h" + +#include "clangpass.h" + +using namespace clang; +using namespace ast_matchers; +using namespace llvm; + +//----------------------------------------------------------------------------- +// ASTFinder callback +//----------------------------------------------------------------------------- +CodeRefactorMatcher::CodeRefactorMatcher( + ASTContext& Context, + clang::Rewriter &RewriterForCodeRefactor, + std::string ClassNameToReplace, + std::string ClassNameToInsert +) : Context(Context), + CodeRefactorRewriter(RewriterForCodeRefactor), + ClassNameToReplace(ClassNameToReplace), + ClassNameToInsert(ClassNameToInsert) {} + +void CodeRefactorMatcher::onEndOfTranslationUnit() { + const SourceManager& SM = CodeRefactorRewriter.getSourceMgr(); + FileID MainFileID = SM.getMainFileID(); + const RewriteBuffer& Buffer = CodeRefactorRewriter.getEditBuffer(MainFileID); + + // Output to stdout + llvm::outs() << "Transformed code:\n"; + Buffer.write(llvm::outs()); + llvm::outs() << "\n"; + + // Output to file + const FileEntry *Entry = SM.getFileEntryForID(MainFileID); + StringRef OriginalFilename = Entry->tryGetRealPathName(); + + size_t slashIndex = OriginalFilename.rfind("/"); + // the path should be absolute, so in the worst case we will get '/' as index 0 + assert(slashIndex != std::string::npos); + slashIndex += 1; // include the '/' itself + + std::string Path = std::string(OriginalFilename.begin(), OriginalFilename.begin() + slashIndex); + std::string SourceFilename = std::string(OriginalFilename.begin() + slashIndex, OriginalFilename.end()); + + llvm::outs() << "Original filename: " << OriginalFilename << "\n"; + std::string OutputFilename = Path + "__tmp_" + SourceFilename; + + std::error_code EC; + llvm::raw_fd_ostream OS(OutputFilename, EC, llvm::sys::fs::OF_None); + + if (EC) { + llvm::errs() << "Error: Could not open output file: " << EC.message() << "\n"; + return; + } + + llvm::outs() << "Writing to file: " << OutputFilename << "\n"; + //OS << std::string(Buffer->begin(), Buffer->end()); + Buffer.write(OS); + OS.close(); +} + +void CodeRefactorMatcher::run(const clang::ast_matchers::MatchFinder::MatchResult &Result) { + if (const auto* ETL = Result.Nodes.getNodeAs("ElaboratedTypeLoc")) { + const auto* TemplType = ETL->getType()->getAs(); + if (!TemplType) { + return; + } + + CodeRefactorRewriter.ReplaceText(ETL->getSourceRange(), ClassNameToInsert + GetArgumentsFromTemplateType(TemplType)); + } + + if (const auto* QTL = Result.Nodes.getNodeAs("QualifiedTypeLoc")) { + const auto* TemplType = QTL->getType()->getAs(); + if (!TemplType) { + return; + } + + CodeRefactorRewriter.ReplaceText(QTL->getSourceRange(), ClassNameToInsert + GetArgumentsFromTemplateType(TemplType)); + } +} + +std::string CodeRefactorMatcher::GetArgumentsFromTemplateType(const TemplateSpecializationType *TST) { + std::string args; + llvm::raw_string_ostream os(args); + printTemplateArgumentList(os, TST->template_arguments(), Context.getPrintingPolicy()); + return args; +} + +// Util function for debugging purposes +std::string CodeRefactorMatcher::getSourceRangeAsString(const SourceRange& SR) const { + auto& sm = CodeRefactorRewriter.getSourceMgr(); + auto& langOpts = CodeRefactorRewriter.getLangOpts(); + + clang::SourceLocation start = SR.getBegin(); + clang::SourceLocation end = SR.getEnd(); + end = clang::Lexer::getLocForEndOfToken(end, 0, sm, langOpts); + + bool isInvalid = false; + const char *startData = sm.getCharacterData(start, &isInvalid); + + if (isInvalid) { + return ""; + isInvalid = false; + } + + const char *endData = sm.getCharacterData(end, &isInvalid); + + if (isInvalid) { + return ""; + isInvalid = false; + } + size_t length = endData - startData; + + return std::string(startData, length); +} + + +//----------------------------------------------------------------------------- +// ASTConsumer +//----------------------------------------------------------------------------- +CodeRefactorASTConsumer::CodeRefactorASTConsumer( + ASTContext& Context, + clang::Rewriter &R, + std::string ClassNameToReplace, + std::string ClassNameToInsert +): CodeRefactorHandler(Context, R, ClassNameToReplace, ClassNameToInsert), + ClassNameToReplace(ClassNameToReplace), + ClassNameToInsert(ClassNameToInsert) { + // Does not support matching the parameters of the functions + const auto MatcherForFQTemplateTypes = elaboratedTypeLoc( + hasNamedTypeLoc( + loc( + templateSpecializationType( + hasDeclaration( + classTemplateSpecializationDecl( + hasName(ClassNameToReplace) + ) + ) + ) + ) + ) + ); + + // Uses previous matcher inside, but returns a wrapping QualifiedTypeLoc node + // which is used in the function parameters + const auto MatcherForFQTemplateParams = qualifiedTypeLoc( + hasUnqualifiedLoc( + MatcherForFQTemplateTypes + ) + ); + + Finder.addMatcher(MatcherForFQTemplateTypes.bind("ElaboratedTypeLoc"), &CodeRefactorHandler); + Finder.addMatcher(MatcherForFQTemplateParams.bind("QualifiedTypeLoc"), &CodeRefactorHandler); +} + +void CodeRefactorASTConsumer::HandleTranslationUnit(clang::ASTContext &Ctx) { + Finder.matchAST(Ctx); +} + + +//----------------------------------------------------------------------------- +// FrontendAction +//----------------------------------------------------------------------------- +class CodeRefactorAddPluginAction : public PluginASTAction { +public: + bool ParseArgs(const CompilerInstance &CI, const std::vector &Args) override { + return true; + } + + // Returns our ASTConsumer per translation unit. + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef file) override { + RewriterForCodeRefactor.setSourceMgr(CI.getSourceManager(), + CI.getLangOpts()); + return std::make_unique( + CI.getASTContext(), RewriterForCodeRefactor, ClassNameToReplace, ClassNameToInsert); + } + +private: + Rewriter RewriterForCodeRefactor; + // The shared library has predefined replace- and insert-class names, + // users are intended to use clangpass_tool.cpp instead. + std::string ClassNameToReplace = "::std::atomic"; + std::string ClassNameToInsert = "LTestAtomic"; +}; + + +static FrontendPluginRegistry::Add X( + "ClangPass", + "Replace all std::atomic usages with LTestAtomic" +); \ No newline at end of file diff --git a/clangpass/clangpass_tool.cpp b/clangpass/clangpass_tool.cpp new file mode 100644 index 00000000..a60d0537 --- /dev/null +++ b/clangpass/clangpass_tool.cpp @@ -0,0 +1,91 @@ +//============================================================================== +// FILE: +// clangpass_tool.cpp +// +// DESCRIPTION: +// A standalone tool that runs the clangpass plugin. See +// clangpass.cpp for a complete description. +// +// USAGE: +// * ./build/bin/clangpass_tool --replace-name=::std::atomic --insert-name=LTestAtomic ./AtomicsReplacer/test-project/main.cpp +// +// +// License: The Unlicense +//============================================================================== +#include "clangpass.h" + +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Refactoring.h" +#include "llvm/Support/CommandLine.h" + +using namespace llvm; +using namespace clang; + +//===----------------------------------------------------------------------===// +// Command line options +//===----------------------------------------------------------------------===// +static cl::OptionCategory CodeRefactorCategory("atomics-replacer options"); + +// TODO: make the prefix (e.g. `__tmp_`source.cpp) a external parameter as well +static cl::opt ClassNameToReplaceOpt{ + "replace-name", + cl::desc("The name of the class/struct which usages should be renamed"), + cl::Required, cl::init(""), cl::cat(CodeRefactorCategory)}; +static cl::opt ClassNameToInsertOpt{ + "insert-name", cl::desc("The name of the class/struct which should be used instead"), + cl::Required, cl::init(""), cl::cat(CodeRefactorCategory)}; + +//===----------------------------------------------------------------------===// +// PluginASTAction +//===----------------------------------------------------------------------===// +class CodeRefactorPluginAction : public PluginASTAction { +public: + explicit CodeRefactorPluginAction() {}; + // Not used + bool ParseArgs( + const CompilerInstance &CI, + const std::vector &args + ) override { + return true; + } + + std::unique_ptr CreateASTConsumer( + CompilerInstance &CI, + StringRef file + ) override { + RewriterForCodeRefactor.setSourceMgr( + CI.getSourceManager(), + CI.getLangOpts() + ); + return std::make_unique( + CI.getASTContext(), RewriterForCodeRefactor, ClassNameToReplaceOpt, ClassNameToInsertOpt); + } + +private: + Rewriter RewriterForCodeRefactor; +}; + +//===----------------------------------------------------------------------===// +// Main driver code. +//===----------------------------------------------------------------------===// +int main(int Argc, const char **Argv) { + Expected eOptParser = + clang::tooling::CommonOptionsParser::create( + Argc, + Argv, + CodeRefactorCategory + ); + if (auto E = eOptParser.takeError()) { + errs() << "Problem constructing CommonOptionsParser " + << toString(std::move(E)) << '\n'; + return EXIT_FAILURE; + } + clang::tooling::RefactoringTool Tool( + eOptParser->getCompilations(), + eOptParser->getSourcePathList() + ); + + return Tool.run(clang::tooling::newFrontendActionFactory().get()); +} diff --git a/clangpass/include/clangpass.h b/clangpass/include/clangpass.h new file mode 100644 index 00000000..fcf4aedc --- /dev/null +++ b/clangpass/include/clangpass.h @@ -0,0 +1,59 @@ +#pragma once + +#include "clang/AST/ASTConsumer.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/ASTMatchers/ASTMatchers.h" + +using namespace clang; +using namespace ast_matchers; +using namespace llvm; + + +//----------------------------------------------------------------------------- +// ASTFinder callback +//----------------------------------------------------------------------------- +class CodeRefactorMatcher : public clang::ast_matchers::MatchFinder::MatchCallback { +public: + explicit CodeRefactorMatcher( + ASTContext& Context, + clang::Rewriter &RewriterForCodeRefactor, + std::string ClassNameToReplace, + std::string ClassNameToInsert + ); + + void onEndOfTranslationUnit() override; + void run(const clang::ast_matchers::MatchFinder::MatchResult &Result) override; + std::string GetArgumentsFromTemplateType(const TemplateSpecializationType *TST); + +private: + ASTContext& Context; + clang::Rewriter CodeRefactorRewriter; + std::string ClassNameToReplace; + std::string ClassNameToInsert; + + std::string getSourceRangeAsString(const SourceRange& SR) const; +}; + +//----------------------------------------------------------------------------- +// ASTConsumer +//----------------------------------------------------------------------------- +class CodeRefactorASTConsumer : public clang::ASTConsumer { +public: + CodeRefactorASTConsumer( + ASTContext& Context, + clang::Rewriter &R, + std::string ClassNameToReplace, + std::string ClassNameToInsert + ); + + void HandleTranslationUnit(clang::ASTContext &Ctx) override; + +private: + clang::ast_matchers::MatchFinder Finder; + CodeRefactorMatcher CodeRefactorHandler; + + std::string ClassNameToReplace; + std::string ClassNameToInsert; +}; \ No newline at end of file diff --git a/runtime/include/ltest_atomic.h b/runtime/include/ltest_atomic.h new file mode 100644 index 00000000..dd6c00eb --- /dev/null +++ b/runtime/include/ltest_atomic.h @@ -0,0 +1,320 @@ +#pragma once + +#include +#include + +// This class is intended to be the entry point +// for the weak memory logic later. +template +class LTestAtomic { + std::atomic atomicValue; + +public: + #if __cplusplus >= 201703L // C++17 + static constexpr bool is_always_lock_free = std::atomic::is_always_lock_free; + #endif + + // Constructors + constexpr LTestAtomic() noexcept = default; + constexpr LTestAtomic(T desired) noexcept : atomicValue(desired) {} + LTestAtomic(const LTestAtomic&) = delete; + LTestAtomic& operator=(const LTestAtomic&) = delete; + LTestAtomic& operator=(const LTestAtomic&) volatile = delete; + + // operator= + T operator=(T desired) noexcept { + store(desired); + return desired; + } + + T operator=(T desired) volatile noexcept { + store(desired); + return desired; + } + + // is_lock_free + bool is_lock_free() const noexcept { + return atomicValue.is_lock_free(); + } + + bool is_lock_free() const volatile noexcept { + return atomicValue.is_lock_free(); + } + + // store + void store(T desired, std::memory_order order = std::memory_order_seq_cst) noexcept { + atomicValue.store(desired, order); + } + + void store(T desired, std::memory_order order = std::memory_order_seq_cst) volatile noexcept { + atomicValue.store(desired, order); + } + + // load + T load(std::memory_order order = std::memory_order_seq_cst) const noexcept { + return atomicValue.load(order); + } + + T load(std::memory_order order = std::memory_order_seq_cst) const volatile noexcept { + return atomicValue.load(order); + } + + // operator T() + operator T() const noexcept { + return atomicValue.load(); + } + + operator T() const volatile noexcept { + return atomicValue.load(); + } + + // exchange + T exchange(T desired, std::memory_order order = std::memory_order_seq_cst) noexcept { + return atomicValue.exchange(desired, order); + } + + T exchange(T desired, std::memory_order order = std::memory_order_seq_cst) volatile noexcept { + return atomicValue.exchange(desired, order); + } + + // compare_exchange_weak + bool compare_exchange_weak(T& expected, T desired, std::memory_order success, std::memory_order failure) noexcept { + atomicValue.compare_exchange_weak(expected, desired, success, failure); + } + + bool compare_exchange_weak(T& expected, T desired, std::memory_order success, std::memory_order failure) volatile noexcept { + atomicValue.compare_exchange_weak(expected, desired, success, failure); + } + + bool compare_exchange_weak(T& expected, T desired, std::memory_order order = std::memory_order_seq_cst) noexcept { + atomicValue.compare_exchange_weak(expected, desired, order); + } + + bool compare_exchange_weak(T& expected, T desired, std::memory_order order = std::memory_order_seq_cst) volatile noexcept { + atomicValue.compare_exchange_weak(expected, desired, order); + } + + // compare_exchange_strong + bool compare_exchange_strong(T& expected, T desired, std::memory_order success, std::memory_order failure) noexcept { + return atomicValue.compare_exchange_strong(expected, desired, success, failure); + } + + bool compare_exchange_strong(T& expected, T desired, std::memory_order success, std::memory_order failure) volatile noexcept { + return atomicValue.compare_exchange_strong(expected, desired, success, failure); + } + + bool compare_exchange_strong(T& expected, T desired, std::memory_order order = std::memory_order_seq_cst) noexcept { + return atomicValue.compare_exchange_strong(expected, desired, order); + } + + bool compare_exchange_strong(T& expected, T desired, std::memory_order order = std::memory_order_seq_cst) volatile noexcept { + return atomicValue.compare_exchange_strong(expected, desired, order); + } + + // wait + #if __cplusplus >= 202002L // C++20 + void wait(T old, std::memory_order order = std::memory_order_seq_cst) const noexcept { + atomicValue.wait(old, order); + } + + void wait(T old, std::memory_order order = std::memory_order_seq_cst) const volatile noexcept { + atomicValue.wait(old, order); + } + + // notify_one + void notify_one() noexcept { + atomicValue.notify_one(); + } + + void notify_one() volatile noexcept { + atomicValue.notify_one(); + } + + // notify all + void notify_all() noexcept { + atomicValue.notify_all(); + } + + void notify_all() volatile noexcept { + atomicValue.notify_all(); + } + #endif + + // fetch_add + T fetch_add(T arg, std::memory_order order = std::memory_order_seq_cst) noexcept { + return atomicValue.fetch_add(arg, order); + } + + T fetch_add(T arg, std::memory_order order = std::memory_order_seq_cst) volatile noexcept { + return atomicValue.fetch_add(arg, order); + } + + // TODO: fix ambiguity with specialization for T* + // T* fetch_add(std::ptrdiff_t arg, std::memory_order order = std::memory_order_seq_cst) noexcept { + // return atomicValue.fetch_add(arg, order); + // } + + // T* fetch_add(std::ptrdiff_t arg, std::memory_order order = std::memory_order_seq_cst) volatile noexcept { + // return atomicValue.fetch_add(arg, order); + // } + + // fetch_sub + T fetch_sub(T arg, std::memory_order order = std::memory_order_seq_cst) noexcept { + return atomicValue.fetch_sub(arg, order); + } + + T fetch_sub(T arg, std::memory_order order = std::memory_order_seq_cst) volatile noexcept { + return atomicValue.fetch_sub(arg, order); + } + + // TODO: fix ambiguity with specialization for T* + // T* fetch_sub(std::ptrdiff_t arg, std::memory_order order = std::memory_order_seq_cst) noexcept { + // return atomicValue.fetch_sub(arg, order); + // } + + // T* fetch_sub(std::ptrdiff_t arg, std::memory_order order = std::memory_order_seq_cst) volatile noexcept { + // return atomicValue.fetch_sub(arg, order); + // } + + // operator+= + T operator+=(T arg) noexcept { + return atomicValue.operator+=(arg); + } + + T operator+=(T arg) volatile noexcept { + return atomicValue.operator+=(arg); + } + + // TODO: fix ambiguity with specialization for T* + // T* operator+=(std::ptrdiff_t arg) noexcept { + // return atomicValue.operator+=(arg); + // } + + // T* operator+=(std::ptrdiff_t arg) volatile noexcept { + // return atomicValue.operator+=(arg); + // } + + // operator-= + T operator-=(T arg) noexcept { + return atomicValue.operator-=(arg); + } + + T operator-=(T arg) volatile noexcept { + return atomicValue.operator-=(arg); + } + + // TODO: fix ambiguity with specialization for T* + // T* operator-=(std::ptrdiff_t arg) noexcept { + // return atomicValue.operator-=(arg); + // } + + // T* operator-=(std::ptrdiff_t arg) volatile noexcept { + // return atomicValue.operator-=(arg); + // } + + // fetch_max + T fetch_max(T arg, std::memory_order order = std::memory_order_seq_cst) noexcept { + return atomicValue.fetch_max(arg, order); + } + + T fetch_max(T arg, std::memory_order order = std::memory_order_seq_cst) volatile noexcept { + return atomicValue.fetch_max(arg, order); + } + + // fetch_min + T fetch_min(T arg, std::memory_order order = std::memory_order_seq_cst) noexcept { + return atomicValue.fetch_min(arg, order); + } + + T fetch_min(T arg, std::memory_order order = std::memory_order_seq_cst) volatile noexcept { + return atomicValue.fetch_min(arg, order); + } + + // operator++ + T operator++() noexcept { + return atomicValue.operator++(); + } + + T operator++() volatile noexcept { + return atomicValue.operator++(); + } + + T operator++(int) noexcept { + return atomicValue.operator++(0); + } + + T operator++(int) volatile noexcept { + return atomicValue.operator++(0); + } + + // operator-- + T operator--() noexcept { + return atomicValue.operator--(); + } + + T operator--() volatile noexcept { + return atomicValue.operator--(); + } + + T operator--(int) noexcept { + return atomicValue.operator--(0); + } + + T operator--(int) volatile noexcept { + return atomicValue.operator--(0); + } + + // fetch_and + T fetch_and(T arg, std::memory_order order = std::memory_order_seq_cst) noexcept { + return atomicValue.fetch_and(arg, order); + } + + T fetch_and(T arg, std::memory_order order = std::memory_order_seq_cst) volatile noexcept { + return atomicValue.fetch_and(arg, order); + } + + // fetch_or + T fetch_or(T arg, std::memory_order order = std::memory_order_seq_cst) noexcept { + return atomicValue.fetch_or(arg, order); + } + + T fetch_or(T arg, std::memory_order order = std::memory_order_seq_cst) volatile noexcept { + return atomicValue.fetch_or(arg, order); + } + + // fetch_xor + T fetch_xor(T arg, std::memory_order order = std::memory_order_seq_cst) noexcept { + return atomicValue.fetch_xor(arg, order); + } + + T fetch_xor(T arg, std::memory_order order = std::memory_order_seq_cst) volatile noexcept { + return atomicValue.fetch_xor(arg, order); + } + + // operator&= + T operator&=(T arg) noexcept { + return atomicValue.operator&=(arg); + } + + T operator&=(T arg) volatile noexcept { + return atomicValue.operator&=(arg); + } + + // operator|= + T operator|=(T arg) noexcept { + return atomicValue.operator|=(arg); + } + + T operator|=(T arg) volatile noexcept { + return atomicValue.operator|=(arg); + } + + // operator^= + T operator^=(T arg) noexcept { + return atomicValue.operator^=(arg); + } + + T operator^=(T arg) volatile noexcept { + return atomicValue.operator^=(arg); + } +}; \ No newline at end of file diff --git a/runtime/include/pick_strategy.h b/runtime/include/pick_strategy.h index 71530642..71d2f5cc 100644 --- a/runtime/include/pick_strategy.h +++ b/runtime/include/pick_strategy.h @@ -76,6 +76,27 @@ struct PickStrategy : public BaseStrategyWithThreads { is_new, current_thread}; } + // TODO: same implementation for pct + std::optional> GetTask(int task_id) override { + // TODO: can this be optimized? + int thread_id = 0; + for (auto& thread : this->threads) { + size_t tasks = thread.size(); + + for (size_t i = 0; i < tasks; ++i) { + Task& task = thread[i]; + if (task->GetId() == task_id) { + std::tuple result = { task, thread_id }; + return result; + // return std::make_tuple(task, thread_id); + } + } + + thread_id++; + } + return std::nullopt; + } + void StartNextRound() override { this->new_task_id = 0; diff --git a/runtime/include/scheduler.h b/runtime/include/scheduler.h index aa6008b2..b9af3c97 100644 --- a/runtime/include/scheduler.h +++ b/runtime/include/scheduler.h @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include "lib.h" #include "lincheck.h" @@ -12,6 +14,11 @@ #include "pretty_print.h" #include "scheduler_fwd.h" #include "stable_vector.h" +#include "minimization.h" +#include "minimization_smart.h" +#include "pretty_print.h" +#include "scheduler_fwd.h" +#include "stable_vector.h" /// Generated by some strategy task, /// that may be not executed due to constraints of data structure diff --git a/runtime/include/verifying.h b/runtime/include/verifying.h index 810a8445..0363ee41 100644 --- a/runtime/include/verifying.h +++ b/runtime/include/verifying.h @@ -14,6 +14,10 @@ #include "strategy_verifier.h" #include "syscall_trap.h" #include "verifying_macro.h" +// This include is important, because when clangpass substitutes std::atomic usages with +// LTestAtomic, the new class should be included into the target source files. +// It will be guaranteed, since verifying.h is included in all targets. +#include "ltest_atomic.h" namespace ltest { diff --git a/runtime/minimization_smart.cpp b/runtime/minimization_smart.cpp index bc047f00..ca4dc256 100644 --- a/runtime/minimization_smart.cpp +++ b/runtime/minimization_smart.cpp @@ -236,4 +236,4 @@ SmartMinimizor::Solution::Solution(const Strategy& strategy, float SmartMinimizor::Solution::GetFitness() const { return fitness; } -int SmartMinimizor::Solution::GetValidTasks() const { return valid_tasks; } \ No newline at end of file +int SmartMinimizor::Solution::GetValidTasks() const { return valid_tasks; } diff --git a/verifying/CMakeLists.txt b/verifying/CMakeLists.txt index 9e279b1f..c2dcc06e 100644 --- a/verifying/CMakeLists.txt +++ b/verifying/CMakeLists.txt @@ -1,18 +1,53 @@ include_directories(lib) include_directories(specs) -set (PASS YieldPass) -set (PASS_PATH ${CMAKE_BINARY_DIR}/codegen/lib${PASS}.so) +set(PASS YieldPass) +set(PASS_PATH ${CMAKE_BINARY_DIR}/codegen/lib${PASS}.so) + +set(CLANG_PASS ClangPass) +set(CLANG_TOOL ClangPassTool) +set(CLANG_PASS_LIB ${CMAKE_BINARY_DIR}/clangpass/lib${CLANG_PASS}.so) +set(CLANG_TOOL_EXECUTABLE ${CMAKE_BINARY_DIR}/clangpass/${CLANG_TOOL}) find_package(Boost REQUIRED COMPONENTS context) function(verify_target target) - add_executable(${target} ${source_name}) + # message("Target: ${target}, sources: ${source_name}") + + if (APPLY_CLANG_TOOL) + # here we prefix the actual source file name with '__tmp_' which will be generate by clang pass tool + add_executable(${target} __tmp_${source_name}) + else() + add_executable(${target} ${source_name}) + endif() add_dependencies(${target} runtime plugin_pass) target_include_directories(${target} PRIVATE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/third_party) target_link_options(${target} PRIVATE ${CMAKE_ASAN_FLAGS}) target_compile_options(${target} PRIVATE -fpass-plugin=${PASS_PATH} ${CMAKE_ASAN_FLAGS}) target_link_libraries(${target} PRIVATE runtime ${PASS} gflags ${Boost_LIBRARIES}) + + + if (APPLY_CLANG_TOOL) + # here we are applying clangpass to the ${source_name} file + add_custom_command( + OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/__tmp_${source_name} + COMMAND ${CLANG_TOOL_EXECUTABLE} + -p=${CMAKE_BINARY_DIR}/compile_commands.json # passing compilation database, make sure CMAKE_EXPORT_COMPILE_COMMANDS flag is set + --replace-name=::std::atomic + --insert-name=LTestAtomic + ${CMAKE_CURRENT_SOURCE_DIR}/${source_name} + DEPENDS ${CLANG_TOOL} + COMMENT "Running Clang Pass Tool on ${source_name}" + ) + + # here we delete the temp file generated by previous command + add_custom_command( + TARGET ${target} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo "Removing temporary file '${CMAKE_CURRENT_SOURCE_DIR}/__tmp_${source_name}' generated after building ${target}" + COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_CURRENT_SOURCE_DIR}/__tmp_${source_name} + ) + endif() endfunction() add_subdirectory(targets) diff --git a/verifying/targets/nonlinear_ms_queue.cpp b/verifying/targets/nonlinear_ms_queue.cpp index 06e360ff..6eb81e21 100644 --- a/verifying/targets/nonlinear_ms_queue.cpp +++ b/verifying/targets/nonlinear_ms_queue.cpp @@ -130,7 +130,7 @@ using spec_t = ltest::Spec, spec::QueueHash<>, spec::QueueEquals<>>; LTEST_ENTRYPOINT(spec_t); - + target_method(generateInt, void, MSQueue, Push, int); target_method(ltest::generators::genEmpty, int, MSQueue, Pop); diff --git a/verifying/targets/nonlinear_set.cpp b/verifying/targets/nonlinear_set.cpp index 039f4ef1..68868c8c 100644 --- a/verifying/targets/nonlinear_set.cpp +++ b/verifying/targets/nonlinear_set.cpp @@ -86,4 +86,4 @@ LTEST_ENTRYPOINT(spec_t); target_method(generateInt, int, SlotsSet, Insert, int); -target_method(generateInt, int, SlotsSet, Erase, int); \ No newline at end of file +target_method(generateInt, int, SlotsSet, Erase, int); diff --git a/verifying/targets/nonlinear_treiber_stack.cpp b/verifying/targets/nonlinear_treiber_stack.cpp index 66ab8c9d..5b57747e 100644 --- a/verifying/targets/nonlinear_treiber_stack.cpp +++ b/verifying/targets/nonlinear_treiber_stack.cpp @@ -85,4 +85,4 @@ LTEST_ENTRYPOINT(spec_t); target_method(generateInt, void, TreiberStack, Push, int); -target_method(ltest::generators::genEmpty, int, TreiberStack, Pop); \ No newline at end of file +target_method(ltest::generators::genEmpty, int, TreiberStack, Pop);