[libc-commits] [libc] [libc] implement insque and remque (PR #80305)
Sirui Mu via libc-commits
libc-commits at lists.llvm.org
Thu Feb 1 08:20:13 PST 2024
https://github.com/Lancern updated https://github.com/llvm/llvm-project/pull/80305
>From 50c1aeb989bbf892bc646082f5f2190795c5e57b Mon Sep 17 00:00:00 2001
From: Sirui Mu <msrlancern at gmail.com>
Date: Thu, 1 Feb 2024 23:22:10 +0800
Subject: [PATCH] [libc] implement insque and remque
This commit implements the insque and remque entrypoint functions.
---
libc/config/linux/aarch64/entrypoints.txt | 2 +
libc/config/linux/riscv/entrypoints.txt | 2 +
libc/config/linux/x86_64/entrypoints.txt | 2 +
libc/src/__support/CMakeLists.txt | 6 +
libc/src/__support/invasive_list.h | 60 +++++++
libc/src/search/CMakeLists.txt | 22 +++
libc/src/search/insque.cpp | 21 +++
libc/src/search/insque.h | 21 +++
libc/src/search/remque.cpp | 20 +++
libc/src/search/remque.h | 21 +++
libc/test/src/search/CMakeLists.txt | 11 ++
libc/test/src/search/insque_test.cpp | 186 ++++++++++++++++++++++
12 files changed, 374 insertions(+)
create mode 100644 libc/src/__support/invasive_list.h
create mode 100644 libc/src/search/insque.cpp
create mode 100644 libc/src/search/insque.h
create mode 100644 libc/src/search/remque.cpp
create mode 100644 libc/src/search/remque.h
create mode 100644 libc/test/src/search/insque_test.cpp
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index c8e12ff8d71bc..c57a2ec63dca9 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -497,6 +497,8 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.search.hsearch_r
libc.src.search.hdestroy
libc.src.search.hdestroy_r
+ libc.src.search.insque
+ libc.src.search.remque
# threads.h entrypoints
libc.src.threads.call_once
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index e2a3860e98afd..7473ac8e2066e 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -522,6 +522,8 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.search.hsearch_r
libc.src.search.hdestroy
libc.src.search.hdestroy_r
+ libc.src.search.insque
+ libc.src.search.remque
# threads.h entrypoints
libc.src.threads.call_once
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index c3aa7e72ebce2..72d55d56d292c 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -537,6 +537,8 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.search.hsearch_r
libc.src.search.hdestroy
libc.src.search.hdestroy_r
+ libc.src.search.insque
+ libc.src.search.remque
# threads.h entrypoints
libc.src.threads.call_once
diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt
index 595124702763b..35d0a2f2a6dbb 100644
--- a/libc/src/__support/CMakeLists.txt
+++ b/libc/src/__support/CMakeLists.txt
@@ -250,6 +250,12 @@ add_header_library(
libc.src.__support.macros.config
)
+add_header_library(
+ invasive_list
+ HDRS
+ invasive_list.h
+)
+
add_subdirectory(FPUtil)
add_subdirectory(OSUtil)
add_subdirectory(StringUtil)
diff --git a/libc/src/__support/invasive_list.h b/libc/src/__support/invasive_list.h
new file mode 100644
index 0000000000000..2cf0e62bb2ecf
--- /dev/null
+++ b/libc/src/__support/invasive_list.h
@@ -0,0 +1,60 @@
+//===-- Invasive queue implementation. --------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// An invasive list that implements the insque and remque semantics.
+//
+//===----------------------------------------------------------------------===//
+
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_INVASIVE_QUEUE_H
+#define LLVM_LIBC_SRC___SUPPORT_INVASIVE_QUEUE_H
+
+#include "common.h"
+
+namespace LIBC_NAMESPACE {
+namespace internal {
+
+struct InvasiveList {
+ struct InvasiveNodeHeader {
+ InvasiveNodeHeader *next;
+ InvasiveNodeHeader *prev;
+ };
+
+ LIBC_INLINE static void insert(InvasiveNodeHeader *elem, InvasiveNodeHeader *prev) {
+ if (!prev) {
+ // The list is linear and elem will be the only element.
+ elem->next = nullptr;
+ elem->prev = nullptr;
+ return;
+ }
+
+ auto next = prev->next;
+
+ elem->next = next;
+ elem->prev = prev;
+
+ prev->next = elem;
+ if (next)
+ next->prev = elem;
+ }
+
+ LIBC_INLINE static void remove(InvasiveNodeHeader *elem) {
+ auto prev = elem->prev;
+ auto next = elem->next;
+
+ if (prev)
+ prev->next = next;
+ if (next)
+ next->prev = prev;
+ }
+};
+
+} // namespace internal
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC___SUPPORT_INVASIVE_QUEUE_H
diff --git a/libc/src/search/CMakeLists.txt b/libc/src/search/CMakeLists.txt
index 24a4ba67decf7..2e43ed563a4f8 100644
--- a/libc/src/search/CMakeLists.txt
+++ b/libc/src/search/CMakeLists.txt
@@ -76,3 +76,25 @@ add_entrypoint_object(
libc.src.__support.HashTable.table
libc.include.search
)
+
+add_entrypoint_object(
+ insque
+ SRCS
+ insque.cpp
+ HDRS
+ insque.h
+ DEPENDS
+ libc.include.search
+ libc.src.__support.invasive_list
+)
+
+add_entrypoint_object(
+ remque
+ SRCS
+ remque.cpp
+ HDRS
+ remque.h
+ DEPENDS
+ libc.include.search
+ libc.src.__support.invasive_list
+)
diff --git a/libc/src/search/insque.cpp b/libc/src/search/insque.cpp
new file mode 100644
index 0000000000000..5e9600ac1d268
--- /dev/null
+++ b/libc/src/search/insque.cpp
@@ -0,0 +1,21 @@
+//===-- Implementation of insque --------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/search/insque.h"
+#include "src/__support/common.h"
+#include "src/__support/invasive_list.h"
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(void, insque, (void *elem, void *prev)) {
+ internal::InvasiveList::insert(
+ static_cast<internal::InvasiveList::InvasiveNodeHeader *>(elem),
+ static_cast<internal::InvasiveList::InvasiveNodeHeader *>(prev));
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/search/insque.h b/libc/src/search/insque.h
new file mode 100644
index 0000000000000..90bac3a05f300
--- /dev/null
+++ b/libc/src/search/insque.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for insque ------------------------*- 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_SEARCH_INSQUE_H
+#define LLVM_LIBC_SRC_SEARCH_INSQUE_H
+
+#include <search.h>
+
+namespace LIBC_NAMESPACE {
+
+void insque(void *elem, void *prev);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_SEARCH_INSQUE_H
diff --git a/libc/src/search/remque.cpp b/libc/src/search/remque.cpp
new file mode 100644
index 0000000000000..ea13bead60c7f
--- /dev/null
+++ b/libc/src/search/remque.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation of remque --------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/search/remque.h"
+#include "src/__support/common.h"
+#include "src/__support/invasive_list.h"
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(void, remque, (void *elem)) {
+ internal::InvasiveList::remove(
+ static_cast<internal::InvasiveList::InvasiveNodeHeader *>(elem));
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/search/remque.h b/libc/src/search/remque.h
new file mode 100644
index 0000000000000..806951c041fdb
--- /dev/null
+++ b/libc/src/search/remque.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for remque ------------------------*- 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_SEARCH_REMQUE_H
+#define LLVM_LIBC_SRC_SEARCH_REMQUE_H
+
+#include <search.h>
+
+namespace LIBC_NAMESPACE {
+
+void remque(void *elem);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_SEARCH_REMQUE_H
diff --git a/libc/test/src/search/CMakeLists.txt b/libc/test/src/search/CMakeLists.txt
index d624f14430949..8a33edc4293ab 100644
--- a/libc/test/src/search/CMakeLists.txt
+++ b/libc/test/src/search/CMakeLists.txt
@@ -14,3 +14,14 @@ add_libc_unittest(
libc.src.search.hdestroy
libc.src.errno.errno
)
+
+add_libc_unittest(
+ insque_test
+ SUITE
+ libc_search_unittests
+ SRCS
+ insque_test.cpp
+ DEPENDS
+ libc.src.search.insque
+ libc.src.search.remque
+)
diff --git a/libc/test/src/search/insque_test.cpp b/libc/test/src/search/insque_test.cpp
new file mode 100644
index 0000000000000..8aab53c0147fa
--- /dev/null
+++ b/libc/test/src/search/insque_test.cpp
@@ -0,0 +1,186 @@
+//===-- Unittests for insque ----------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/search/insque.h"
+#include "src/search/remque.h"
+#include "test/UnitTest/Test.h"
+
+namespace {
+
+struct Node {
+ Node *next;
+ Node *prev;
+};
+
+template <unsigned N> void make_linear_links(Node (&nodes)[N]) {
+ for (unsigned i = 0; i < N; ++i) {
+ if (i == N - 1)
+ nodes[i].next = nullptr;
+ else
+ nodes[i].next = &nodes[i + 1];
+
+ if (i == 0)
+ nodes[i].prev = nullptr;
+ else
+ nodes[i].prev = &nodes[i - 1];
+ }
+}
+
+template <unsigned N> void make_circular_links(Node (&nodes)[N]) {
+ for (unsigned i = 0; i < N; ++i) {
+ nodes[i].next = &nodes[(i + 1) % N];
+ nodes[i].prev = &nodes[(i + N - 1) % N];
+ }
+}
+
+} // namespace
+
+class LlvmLibcInsqueTest : public LIBC_NAMESPACE::testing::Test {
+protected:
+ template <unsigned N>
+ void check_linear(const Node *head, const Node *const (&nodes)[N]) {
+ // First make sure that the given N nodes form a valid linear list.
+ for (unsigned i = 0; i < N; ++i) {
+ const Node *next = nullptr;
+ if (i + 1 < N)
+ next = nodes[i + 1];
+
+ const Node *prev = nullptr;
+ if (i > 0)
+ prev = nodes[i - 1];
+
+ EXPECT_EQ(static_cast<const Node *>(nodes[i]->next), next);
+ EXPECT_EQ(static_cast<const Node *>(nodes[i]->prev), prev);
+ }
+
+ // Then check the list nodes match.
+ for (unsigned i = 0; i < N; ++i) {
+ EXPECT_EQ(head, nodes[i]);
+ // Traversal by head should always be OK since we have already confirmed
+ // the validity of links.
+ head = head->next;
+ }
+ }
+
+ template <unsigned N>
+ void check_circular(const Node *head, const Node *const (&nodes)[N]) {
+ // First make sure that the given N nodes form a valid linear list.
+ for (unsigned i = 0; i < N; ++i) {
+ auto next = nodes[(i + 1) % N];
+ auto prev = nodes[(i + N - 1) % N];
+
+ EXPECT_EQ(static_cast<const Node *>(nodes[i]->prev), prev);
+ EXPECT_EQ(static_cast<const Node *>(nodes[i]->next), next);
+ }
+
+ // Then check the list nodes match.
+ for (unsigned i = 0; i < N; ++i) {
+ EXPECT_EQ(head, nodes[i]);
+ // Traversal by head should always be OK since we have already confirmed
+ // the validity of links.
+ head = head->next;
+ }
+ }
+};
+
+TEST_F(LlvmLibcInsqueTest, InsertToNull) {
+ Node node{nullptr, nullptr};
+ LIBC_NAMESPACE::insque(&node, nullptr);
+ check_linear(&node, {&node});
+}
+
+TEST_F(LlvmLibcInsqueTest, InsertToLinearSingleton) {
+ Node base[1];
+ make_linear_links(base);
+
+ Node incoming{nullptr, nullptr};
+ LIBC_NAMESPACE::insque(&incoming, &base[0]);
+ check_linear(base, {&base[0], &incoming});
+}
+
+TEST_F(LlvmLibcInsqueTest, InsertToLinearMiddle) {
+ Node base[3];
+ make_linear_links(base);
+
+ Node incoming{nullptr, nullptr};
+ LIBC_NAMESPACE::insque(&incoming, &base[1]);
+ check_linear(base, {&base[0], &base[1], &incoming, &base[2]});
+}
+
+TEST_F(LlvmLibcInsqueTest, InsertToLinearBack) {
+ Node base[3];
+ make_linear_links(base);
+
+ Node incoming{nullptr, nullptr};
+ LIBC_NAMESPACE::insque(&incoming, &base[2]);
+ check_linear(base, {&base[0], &base[1], &base[2], &incoming});
+}
+
+TEST_F(LlvmLibcInsqueTest, InsertToCircularSingleton) {
+ Node base[1];
+ make_circular_links(base);
+
+ Node incoming{nullptr, nullptr};
+ LIBC_NAMESPACE::insque(&incoming, &base[0]);
+ check_circular(base, {&base[0], &incoming});
+}
+
+TEST_F(LlvmLibcInsqueTest, InsertToCircular) {
+ Node base[3];
+ make_circular_links(base);
+
+ Node incoming{nullptr, nullptr};
+ LIBC_NAMESPACE::insque(&incoming, &base[1]);
+ check_circular(base, {&base[0], &base[1], &incoming, &base[2]});
+}
+
+TEST_F(LlvmLibcInsqueTest, RemoveFromLinearSingleton) {
+ Node node{nullptr, nullptr};
+ LIBC_NAMESPACE::remque(&node);
+ ASSERT_EQ(node.next, static_cast<Node *>(nullptr));
+ ASSERT_EQ(node.prev, static_cast<Node *>(nullptr));
+}
+
+TEST_F(LlvmLibcInsqueTest, RemoveFromLinearFront) {
+ Node base[3];
+ make_linear_links(base);
+
+ LIBC_NAMESPACE::remque(&base[0]);
+ check_linear(&base[1], {&base[1], &base[2]});
+}
+
+TEST_F(LlvmLibcInsqueTest, RemoveFromLinearMiddle) {
+ Node base[3];
+ make_linear_links(base);
+
+ LIBC_NAMESPACE::remque(&base[1]);
+ check_linear(&base[0], {&base[0], &base[2]});
+}
+
+TEST_F(LlvmLibcInsqueTest, RemoveFromLinearBack) {
+ Node base[3];
+ make_linear_links(base);
+
+ LIBC_NAMESPACE::remque(&base[2]);
+ check_linear(&base[0], {&base[0], &base[1]});
+}
+
+TEST_F(LlvmLibcInsqueTest, RemoveFromCircularSingleton) {
+ Node node[1];
+ make_circular_links(node);
+
+ LIBC_NAMESPACE::remque(&node[0]);
+}
+
+TEST_F(LlvmLibcInsqueTest, RemoveFromCircular) {
+ Node base[3];
+ make_circular_links(base);
+
+ LIBC_NAMESPACE::remque(&base[1]);
+ check_circular(&base[0], {&base[0], &base[2]});
+}
More information about the libc-commits
mailing list