[libc] [lld] [llvm] [libc][rework] arc4random with vDSO support (PR #151361)
Schrodinger ZHU Yifan via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 31 19:15:19 PDT 2025
https://github.com/SchrodingerZhu updated https://github.com/llvm/llvm-project/pull/151361
>From ec5d9715dfc448ac2329dff4a48b344d6954c185 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Wed, 30 Jul 2025 12:47:24 -0400
Subject: [PATCH 1/3] wip
---
libc/src/__support/aba_ptr.h | 70 ++++++++++++++++++++++++++++++
libc/src/__support/mpmc_stack.h | 75 +++++++++++++++++++++++++++++++++
2 files changed, 145 insertions(+)
create mode 100644 libc/src/__support/aba_ptr.h
create mode 100644 libc/src/__support/mpmc_stack.h
diff --git a/libc/src/__support/aba_ptr.h b/libc/src/__support/aba_ptr.h
new file mode 100644
index 0000000000000..c702aae017502
--- /dev/null
+++ b/libc/src/__support/aba_ptr.h
@@ -0,0 +1,70 @@
+//===-- Transactional Ptr for ABA prevention --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_TAGGED_POINTER_H
+#define LLVM_LIBC_SRC___SUPPORT_TAGGED_POINTER_H
+
+#include "src/__support/common.h"
+#include "src/__support/threads/sleep.h"
+
+#ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16
+#define LIBC_ABA_PTR_IS_ATOMIC true
+#else
+#define LIBC_ABA_PTR_IS_ATOMIC false
+#endif
+
+namespace LIBC_NAMESPACE_DECL {
+
+template <class T, bool IsAtomic> struct AbaPtrImpl {
+ union Impl {
+ struct alignas(2 * alignof(void *)) Atomic {
+ T *ptr;
+ __SIZE_TYPE__ tag;
+ } atomic;
+ struct Mutex {
+ T *ptr;
+ bool locked;
+ } mutex;
+ } impl;
+
+ LIBC_INLINE constexpr AbaPtrImpl(T *ptr)
+ : impl(IsAtomic ? Impl{.atomic{ptr, 0}} : Impl{.mutex{ptr, false}}) {}
+
+ /// User must guarantee that operation is redoable.
+ template <class Op> LIBC_INLINE void transaction(Op &&op) {
+ if constexpr (IsAtomic) {
+ for (;;) {
+ typename Impl::Atomic snapshot, next;
+ __atomic_load(&impl.atomic, &snapshot, __ATOMIC_RELAXED);
+ next.ptr = op(snapshot.ptr);
+ // Wrapping add for unsigned integers.
+ next.tag = snapshot.tag + 1;
+ if (__atomic_compare_exchange(&impl.atomic, &snapshot, &next, true,
+ __ATOMIC_ACQ_REL, __ATOMIC_RELAXED)) {
+ return;
+ }
+ }
+ } else {
+ // Acquire the lock.
+ while (__atomic_exchange_n(&impl.mutex.locked, true, __ATOMIC_ACQUIRE)) {
+ while (__atomic_load_n(&impl.mutex.locked, __ATOMIC_RELAXED)) {
+ LIBC_NAMESPACE::sleep_briefly();
+ }
+ }
+ impl.mutex.ptr = op(impl.mutex.ptr);
+ // Release the lock.
+ __atomic_store_n(&impl.mutex.locked, false, __ATOMIC_RELEASE);
+ }
+ }
+};
+
+template <class T> using AbaPtr = AbaPtrImpl<T, LIBC_ABA_PTR_IS_ATOMIC>;
+} // namespace LIBC_NAMESPACE_DECL
+
+#undef LIBC_ABA_PTR_IS_ATOMIC
+#endif
diff --git a/libc/src/__support/mpmc_stack.h b/libc/src/__support/mpmc_stack.h
new file mode 100644
index 0000000000000..4892c3926b4b0
--- /dev/null
+++ b/libc/src/__support/mpmc_stack.h
@@ -0,0 +1,75 @@
+//===-- Simple Lock-free MPMC Stack -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_MPMC_STACK_H
+#define LLVM_LIBC_SRC___SUPPORT_MPMC_STACK_H
+
+#include "src/__support/CPP/atomic.h"
+#include "src/__support/CPP/new.h"
+#include "src/__support/CPP/optional.h"
+#include "src/__support/aba_ptr.h"
+
+namespace LIBC_NAMESPACE_DECL {
+template <class T> class MPMCStack {
+ struct Node {
+ cpp::Atomic<size_t> visitor;
+ AbaPtr<Node> next;
+ T value;
+
+ LIBC_INLINE Node(T val) : visitor(0), next(nullptr), value(val) {}
+ };
+ AbaPtr<Node> head;
+
+public:
+ static_assert(cpp::is_copy_constructible<T>::value,
+ "T must be copy constructible");
+ LIBC_INLINE MPMCStack() : head(nullptr) {}
+ LIBC_INLINE bool push(T value) {
+ AllocChecker ac;
+ Node *new_node = new Node(value, ac);
+ if (!ac) {
+ return false;
+ }
+ head.transaction([new_node](Node *old_head) {
+ new_node->next = old_head;
+ return new_node;
+ });
+ return true;
+ }
+ LIBC_INLINE cpp::optional<T> pop() {
+ cpp::optional<T> res;
+ Node *node;
+ head.transaction([&](Node *current_head) {
+ if (!current_head) {
+ res = cpp::nullopt;
+ return nullptr;
+ }
+ node = current_head;
+ node->visitor.fetch_add(1);
+ res = node->value;
+ auto next = node->next;
+ node->visitor.fetch_sub(1);
+ return next;
+ });
+ // On a successful transaction, a node is popped by us. So we must delete
+ // it. When we are at here, no one else can acquire
+ // new reference to the node, but we still need to wait until other threads
+ // inside the transaction who may potentially be holding a reference to the
+ // node.
+ if (res) {
+ // Spin until the node is no longer in use.
+ while (node->visitor.load() != 0)
+ LIBC_NAMESPACE::sleep_briefly();
+ delete node;
+ }
+ return res;
+ }
+};
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif
>From 0eac3c6dde7115a8c1d760f2892b963b3eebd3ef Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Wed, 30 Jul 2025 12:52:35 -0400
Subject: [PATCH 2/3] remove some extra braces
---
libc/src/__support/aba_ptr.h | 10 ++++------
libc/src/__support/mpmc_stack.h | 3 +--
2 files changed, 5 insertions(+), 8 deletions(-)
diff --git a/libc/src/__support/aba_ptr.h b/libc/src/__support/aba_ptr.h
index c702aae017502..f7ed601dfbe70 100644
--- a/libc/src/__support/aba_ptr.h
+++ b/libc/src/__support/aba_ptr.h
@@ -45,17 +45,15 @@ template <class T, bool IsAtomic> struct AbaPtrImpl {
// Wrapping add for unsigned integers.
next.tag = snapshot.tag + 1;
if (__atomic_compare_exchange(&impl.atomic, &snapshot, &next, true,
- __ATOMIC_ACQ_REL, __ATOMIC_RELAXED)) {
+ __ATOMIC_ACQ_REL, __ATOMIC_RELAXED))
return;
- }
}
} else {
// Acquire the lock.
- while (__atomic_exchange_n(&impl.mutex.locked, true, __ATOMIC_ACQUIRE)) {
- while (__atomic_load_n(&impl.mutex.locked, __ATOMIC_RELAXED)) {
+ while (__atomic_exchange_n(&impl.mutex.locked, true, __ATOMIC_ACQUIRE))
+ while (__atomic_load_n(&impl.mutex.locked, __ATOMIC_RELAXED))
LIBC_NAMESPACE::sleep_briefly();
- }
- }
+
impl.mutex.ptr = op(impl.mutex.ptr);
// Release the lock.
__atomic_store_n(&impl.mutex.locked, false, __ATOMIC_RELEASE);
diff --git a/libc/src/__support/mpmc_stack.h b/libc/src/__support/mpmc_stack.h
index 4892c3926b4b0..819433f24380c 100644
--- a/libc/src/__support/mpmc_stack.h
+++ b/libc/src/__support/mpmc_stack.h
@@ -32,9 +32,8 @@ template <class T> class MPMCStack {
LIBC_INLINE bool push(T value) {
AllocChecker ac;
Node *new_node = new Node(value, ac);
- if (!ac) {
+ if (!ac)
return false;
- }
head.transaction([new_node](Node *old_head) {
new_node->next = old_head;
return new_node;
>From 1ad958ea32c7515452e631ae751261632bf2ad6b Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Wed, 30 Jul 2025 16:21:43 -0400
Subject: [PATCH 3/3] add tests
---
libc/src/__support/CMakeLists.txt | 24 ++++++++
libc/src/__support/aba_ptr.h | 17 +++++-
libc/src/__support/mpmc_stack.h | 15 ++---
libc/test/IntegrationTest/test.cpp | 9 +--
.../integration/src/__support/CMakeLists.txt | 15 +++++
.../src/__support/mpmc_stack_test.cpp | 61 +++++++++++++++++++
lld/test/wasm/Inputs/libstub.so | 12 ++--
.../MC/AsmParser/preserve-comments-crlf.s | 26 ++++----
.../llvm-mca/X86/directives-handle-crlf.s | 8 +--
.../tools/split-file/Inputs/basic-aa.crlf | 4 +-
.../tools/split-file/Inputs/basic-bb.crlf | 8 +--
llvm/test/tools/split-file/basic.crlf.test | 20 +++---
12 files changed, 168 insertions(+), 51 deletions(-)
create mode 100644 libc/test/integration/src/__support/mpmc_stack_test.cpp
diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt
index 2196d9e23bba7..c9d89cf6fc286 100644
--- a/libc/src/__support/CMakeLists.txt
+++ b/libc/src/__support/CMakeLists.txt
@@ -398,6 +398,30 @@ add_header_library(
libc.src.__support.macros.attributes
)
+add_header_library(
+ aba_ptr
+ HDRS
+ aba_ptr.h
+ DEPENDS
+ libc.hdr.types.size_t
+ libc.src.__support.common
+ libc.src.__support.threads.sleep
+)
+
+add_header_library(
+ mpmc_stack
+ HDRS
+ mpmc_stack.h
+ DEPENDS
+ libc.src.__support.aba_ptr
+ libc.src.__support.common
+ libc.src.__support.CPP.atomic
+ libc.src.__support.CPP.new
+ libc.src.__support.CPP.optional
+ libc.src.__support.CPP.type_traits
+)
+
+
add_subdirectory(FPUtil)
add_subdirectory(OSUtil)
add_subdirectory(StringUtil)
diff --git a/libc/src/__support/aba_ptr.h b/libc/src/__support/aba_ptr.h
index f7ed601dfbe70..632cc466c295b 100644
--- a/libc/src/__support/aba_ptr.h
+++ b/libc/src/__support/aba_ptr.h
@@ -9,6 +9,7 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_TAGGED_POINTER_H
#define LLVM_LIBC_SRC___SUPPORT_TAGGED_POINTER_H
+#include "hdr/types/size_t.h"
#include "src/__support/common.h"
#include "src/__support/threads/sleep.h"
@@ -24,7 +25,7 @@ template <class T, bool IsAtomic> struct AbaPtrImpl {
union Impl {
struct alignas(2 * alignof(void *)) Atomic {
T *ptr;
- __SIZE_TYPE__ tag;
+ size_t tag;
} atomic;
struct Mutex {
T *ptr;
@@ -59,6 +60,20 @@ template <class T, bool IsAtomic> struct AbaPtrImpl {
__atomic_store_n(&impl.mutex.locked, false, __ATOMIC_RELEASE);
}
}
+
+ LIBC_INLINE T *get() const {
+ if constexpr (IsAtomic) {
+ // Weak micro-architectures typically reguards simultaneous partial word
+ // loading and full word loading as a race condition. While there are
+ // implementations that uses racy read anyway, we still load the whole
+ // word to avoid any complications.
+ typename Impl::Atomic snapshot;
+ __atomic_load(&impl.atomic, &snapshot, __ATOMIC_RELAXED);
+ return snapshot.ptr;
+ } else {
+ return impl.mutex.ptr;
+ }
+ }
};
template <class T> using AbaPtr = AbaPtrImpl<T, LIBC_ABA_PTR_IS_ATOMIC>;
diff --git a/libc/src/__support/mpmc_stack.h b/libc/src/__support/mpmc_stack.h
index 819433f24380c..c6546bac9be9a 100644
--- a/libc/src/__support/mpmc_stack.h
+++ b/libc/src/__support/mpmc_stack.h
@@ -12,13 +12,14 @@
#include "src/__support/CPP/atomic.h"
#include "src/__support/CPP/new.h"
#include "src/__support/CPP/optional.h"
+#include "src/__support/CPP/type_traits.h"
#include "src/__support/aba_ptr.h"
namespace LIBC_NAMESPACE_DECL {
template <class T> class MPMCStack {
struct Node {
cpp::Atomic<size_t> visitor;
- AbaPtr<Node> next;
+ Node *next;
T value;
LIBC_INLINE Node(T val) : visitor(0), next(nullptr), value(val) {}
@@ -31,7 +32,7 @@ template <class T> class MPMCStack {
LIBC_INLINE MPMCStack() : head(nullptr) {}
LIBC_INLINE bool push(T value) {
AllocChecker ac;
- Node *new_node = new Node(value, ac);
+ Node *new_node = new (ac) Node(value);
if (!ac)
return false;
head.transaction([new_node](Node *old_head) {
@@ -41,17 +42,17 @@ template <class T> class MPMCStack {
return true;
}
LIBC_INLINE cpp::optional<T> pop() {
- cpp::optional<T> res;
- Node *node;
- head.transaction([&](Node *current_head) {
+ cpp::optional<T> res = cpp::nullopt;
+ Node *node = nullptr;
+ head.transaction([&](Node *current_head) -> Node * {
if (!current_head) {
res = cpp::nullopt;
return nullptr;
}
node = current_head;
node->visitor.fetch_add(1);
- res = node->value;
- auto next = node->next;
+ res = cpp::optional<T>{node->value};
+ Node *next = node->next;
node->visitor.fetch_sub(1);
return next;
});
diff --git a/libc/test/IntegrationTest/test.cpp b/libc/test/IntegrationTest/test.cpp
index 8baf74637b309..35ca312939ab3 100644
--- a/libc/test/IntegrationTest/test.cpp
+++ b/libc/test/IntegrationTest/test.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "hdr/stdint_proxy.h"
+#include "src/__support/CPP/atomic.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include <stddef.h>
@@ -65,14 +66,14 @@ int atexit(void (*func)(void)) { return LIBC_NAMESPACE::atexit(func); }
static constexpr uint64_t MEMORY_SIZE = 16384;
static uint8_t memory[MEMORY_SIZE];
-static uint8_t *ptr = memory;
+static LIBC_NAMESPACE::cpp::Atomic<size_t> cursor = 0;
extern "C" {
void *malloc(size_t s) {
- void *mem = ptr;
- ptr += s;
- return static_cast<uint64_t>(ptr - memory) >= MEMORY_SIZE ? nullptr : mem;
+ size_t cur = cursor.fetch_add(s);
+ void *mem = &memory[cur];
+ return cur + s <= MEMORY_SIZE ? mem : nullptr;
}
void free(void *) {}
diff --git a/libc/test/integration/src/__support/CMakeLists.txt b/libc/test/integration/src/__support/CMakeLists.txt
index b5b6557e8d689..93f54083f3c00 100644
--- a/libc/test/integration/src/__support/CMakeLists.txt
+++ b/libc/test/integration/src/__support/CMakeLists.txt
@@ -2,3 +2,18 @@ add_subdirectory(threads)
if(LIBC_TARGET_OS_IS_GPU)
add_subdirectory(GPU)
endif()
+
+add_libc_integration_test_suite(libc-support-integration-tests)
+
+add_integration_test(
+ mpmc_stack_test
+ SUITE
+ libc-support-integration-tests
+ SRCS
+ mpmc_stack_test.cpp
+ DEPENDS
+ libc.src.__support.mpmc_stack
+ libc.src.__support.threads.thread
+ libc.src.pthread.pthread_create
+ libc.src.pthread.pthread_join
+)
diff --git a/libc/test/integration/src/__support/mpmc_stack_test.cpp b/libc/test/integration/src/__support/mpmc_stack_test.cpp
new file mode 100644
index 0000000000000..3cc8237c9e179
--- /dev/null
+++ b/libc/test/integration/src/__support/mpmc_stack_test.cpp
@@ -0,0 +1,61 @@
+#include "src/__support/CPP/atomic.h"
+#include "src/__support/mpmc_stack.h"
+#include "src/pthread/pthread_create.h"
+#include "src/pthread/pthread_join.h"
+#include "test/IntegrationTest/test.h"
+
+using namespace LIBC_NAMESPACE;
+
+void smoke_test() {
+ MPMCStack<int> stack;
+ for (int i = 0; i <= 100; ++i)
+ if (!stack.push(i))
+ __builtin_trap();
+ for (int i = 100; i >= 0; --i)
+ if (*stack.pop() != i)
+ __builtin_trap();
+ if (stack.pop())
+ __builtin_trap(); // Should be empty now.
+}
+
+void multithread_test() {
+ constexpr static size_t NUM_THREADS = 5;
+ constexpr static size_t NUM_PUSHES = 100;
+ struct State {
+ MPMCStack<size_t> stack;
+ cpp::Atomic<size_t> counter = 0;
+ cpp::Atomic<bool> flags[NUM_PUSHES];
+ } state;
+ pthread_t threads[NUM_THREADS];
+ for (size_t i = 0; i < NUM_THREADS; ++i) {
+ LIBC_NAMESPACE::pthread_create(
+ &threads[i], nullptr,
+ [](void *arg) -> void * {
+ State *state = static_cast<State *>(arg);
+ for (;;) {
+ size_t current = state->counter.fetch_add(1);
+ if (current >= NUM_PUSHES)
+ break;
+ if (!state->stack.push(current))
+ __builtin_trap();
+ }
+ while (auto res = state->stack.pop())
+ state->flags[res.value()].store(true);
+ return nullptr;
+ },
+ &state);
+ }
+ for (pthread_t thread : threads)
+ LIBC_NAMESPACE::pthread_join(thread, nullptr);
+ while (cpp::optional<size_t> res = state.stack.pop())
+ state.flags[res.value()].store(true);
+ for (size_t i = 0; i < NUM_PUSHES; ++i)
+ if (!state.flags[i].load())
+ __builtin_trap();
+}
+
+TEST_MAIN() {
+ smoke_test();
+ multithread_test();
+ return 0;
+}
diff --git a/lld/test/wasm/Inputs/libstub.so b/lld/test/wasm/Inputs/libstub.so
index 70f6648c60eee..99783d72f26bd 100644
--- a/lld/test/wasm/Inputs/libstub.so
+++ b/lld/test/wasm/Inputs/libstub.so
@@ -1,6 +1,6 @@
-#STUB
-# This is a comment
-foo_import: foodep1,foodep2
-# This symbols as no dependencies
-bar
-baz: bazdep
+#STUB
+# This is a comment
+foo_import: foodep1,foodep2
+# This symbols as no dependencies
+bar
+baz: bazdep
diff --git a/llvm/test/MC/AsmParser/preserve-comments-crlf.s b/llvm/test/MC/AsmParser/preserve-comments-crlf.s
index 27c5b2e070719..8bf094b73ef96 100644
--- a/llvm/test/MC/AsmParser/preserve-comments-crlf.s
+++ b/llvm/test/MC/AsmParser/preserve-comments-crlf.s
@@ -1,13 +1,13 @@
- #RUN: llvm-mc -preserve-comments -n -triple i386-linux-gnu < %s > %t
- #RUN: diff -b %s %t
- .text
-
-foo: #Comment here
- #comment here
- nop
- #if DIRECTIVE COMMENT
- ## WHOLE LINE COMMENT
- cmpl $196, %eax ## EOL COMMENT
- #endif
- .ident "clang version 3.9.0"
- .section ".note.GNU-stack","", at progbits
+ #RUN: llvm-mc -preserve-comments -n -triple i386-linux-gnu < %s > %t
+ #RUN: diff -b %s %t
+ .text
+
+foo: #Comment here
+ #comment here
+ nop
+ #if DIRECTIVE COMMENT
+ ## WHOLE LINE COMMENT
+ cmpl $196, %eax ## EOL COMMENT
+ #endif
+ .ident "clang version 3.9.0"
+ .section ".note.GNU-stack","", at progbits
diff --git a/llvm/test/tools/llvm-mca/X86/directives-handle-crlf.s b/llvm/test/tools/llvm-mca/X86/directives-handle-crlf.s
index db797a75c2617..67821474be72a 100644
--- a/llvm/test/tools/llvm-mca/X86/directives-handle-crlf.s
+++ b/llvm/test/tools/llvm-mca/X86/directives-handle-crlf.s
@@ -1,4 +1,4 @@
-# RUN: llvm-mca -mtriple=x86_64-unknown-unknown -mcpu=generic %s
-# LLVM-MCA-BEGIN foo
-addl $42, %eax
-# LLVM-MCA-END
+# RUN: llvm-mca -mtriple=x86_64-unknown-unknown -mcpu=generic %s
+# LLVM-MCA-BEGIN foo
+addl $42, %eax
+# LLVM-MCA-END
diff --git a/llvm/test/tools/split-file/Inputs/basic-aa.crlf b/llvm/test/tools/split-file/Inputs/basic-aa.crlf
index 0b9ddeb2fc12a..aa8822c8e1453 100644
--- a/llvm/test/tools/split-file/Inputs/basic-aa.crlf
+++ b/llvm/test/tools/split-file/Inputs/basic-aa.crlf
@@ -1,2 +1,2 @@
-
-aa
+
+aa
diff --git a/llvm/test/tools/split-file/Inputs/basic-bb.crlf b/llvm/test/tools/split-file/Inputs/basic-bb.crlf
index b6c3c808ec62f..3c765c42c0127 100644
--- a/llvm/test/tools/split-file/Inputs/basic-bb.crlf
+++ b/llvm/test/tools/split-file/Inputs/basic-bb.crlf
@@ -1,4 +1,4 @@
-
-
-
-bb
+
+
+
+bb
diff --git a/llvm/test/tools/split-file/basic.crlf.test b/llvm/test/tools/split-file/basic.crlf.test
index f01074a879630..6d89c69064e5e 100644
--- a/llvm/test/tools/split-file/basic.crlf.test
+++ b/llvm/test/tools/split-file/basic.crlf.test
@@ -1,10 +1,10 @@
-#--- aa
-aa
-;--- bb
-bb
-;--- end
-
-# RUN: rm -rf %t
-# RUN: split-file --leading-lines %s %t
-# RUN: diff %S/Inputs/basic-aa.crlf %t/aa
-# RUN: diff %S/Inputs/basic-bb.crlf %t/bb
+#--- aa
+aa
+;--- bb
+bb
+;--- end
+
+# RUN: rm -rf %t
+# RUN: split-file --leading-lines %s %t
+# RUN: diff %S/Inputs/basic-aa.crlf %t/aa
+# RUN: diff %S/Inputs/basic-bb.crlf %t/bb
More information about the llvm-commits
mailing list