From 6dd4caf3c78bd48fb2e4dad3bfaad984647ed12e Mon Sep 17 00:00:00 2001 From: Dmitrii Artiukhov Date: Thu, 22 Jan 2026 21:30:36 +0000 Subject: [PATCH] Add parameter to the clangpass which substitutes std::atomic's with their ltest custom implementation --- runtime/include/latomic.h | 317 ++++++++++++++++++++++++++++++++++++ runtime/include/verifying.h | 1 + verifying/CMakeLists.txt | 4 +- 3 files changed, 320 insertions(+), 2 deletions(-) create mode 100644 runtime/include/latomic.h diff --git a/runtime/include/latomic.h b/runtime/include/latomic.h new file mode 100644 index 00000000..b2502aaa --- /dev/null +++ b/runtime/include/latomic.h @@ -0,0 +1,317 @@ +#pragma once + +#include + +namespace ltest { + +// This class is intended to be the entry point +// for the weak memory logic later. +template +class latomic { + 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 latomic() noexcept = default; + constexpr latomic(T desired) noexcept : atomicValue(desired) {} + latomic(const latomic&) = delete; + latomic& operator=(const latomic&) = delete; + latomic& operator=(const latomic&) 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 { + return 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 { + return 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 { + return 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 { + return 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); } +}; + +} // namespace ltest \ No newline at end of file diff --git a/runtime/include/verifying.h b/runtime/include/verifying.h index 10a9c3d4..9eecc83f 100644 --- a/runtime/include/verifying.h +++ b/runtime/include/verifying.h @@ -6,6 +6,7 @@ #include #include "blocking_primitives.h" +#include "latomic.h" #include "lib.h" #include "lincheck_recursive.h" #include "logger.h" diff --git a/verifying/CMakeLists.txt b/verifying/CMakeLists.txt index 4d0f1ed9..ca6d48f7 100644 --- a/verifying/CMakeLists.txt +++ b/verifying/CMakeLists.txt @@ -29,8 +29,8 @@ function(verify_target_without_plugin target) COMMAND ${CLANG_TOOL_EXECUTABLE} -p=${CMAKE_BINARY_DIR}/compile_commands.json # passing compilation database, make sure CMAKE_EXPORT_COMPILE_COMMANDS flag is set --temp-prefix ${CLANG_TOOL_TMP_PREFIX} - --replace-names ::std::mutex,::std::shared_mutex,::std::condition_variable - --insert-names ltest::mutex,ltest::shared_mutex,ltest::condition_variable + --replace-names ::std::mutex,::std::shared_mutex,::std::condition_variable,::std::atomic + --insert-names ltest::mutex,ltest::shared_mutex,ltest::condition_variable,ltest::latomic ${CMAKE_CURRENT_SOURCE_DIR}/${source_name} DEPENDS ${CLANG_TOOL} COMMENT "Running Clang Pass Tool on ${source_name}"