[libc-commits] [libc] [libc] implement insque and remque (PR #80305)

Sirui Mu via libc-commits libc-commits at lists.llvm.org
Sat Feb 3 05:34:46 PST 2024


https://github.com/Lancern updated https://github.com/llvm/llvm-project/pull/80305

>From 2ebe300c9e4d913b301bfc691e7da7a7b96e0bea Mon Sep 17 00:00:00 2001
From: Sirui Mu <msrlancern at gmail.com>
Date: Fri, 2 Feb 2024 13:33:47 +0800
Subject: [PATCH 1/3] [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         |   8 +
 libc/src/__support/intrusive_list.h       |  65 ++++++++
 libc/src/search/CMakeLists.txt            |  22 +++
 libc/src/search/insque.cpp                |  19 +++
 libc/src/search/insque.h                  |  20 +++
 libc/src/search/remque.cpp                |  19 +++
 libc/src/search/remque.h                  |  20 +++
 libc/test/src/search/CMakeLists.txt       |  11 ++
 libc/test/src/search/insque_test.cpp      | 186 ++++++++++++++++++++++
 12 files changed, 376 insertions(+)
 create mode 100644 libc/src/__support/intrusive_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..bd814a080c4f8 100644
--- a/libc/src/__support/CMakeLists.txt
+++ b/libc/src/__support/CMakeLists.txt
@@ -250,6 +250,14 @@ add_header_library(
     libc.src.__support.macros.config
 )
 
+add_header_library(
+  intrusive_list
+  HDRS
+    intrusive_list.h
+  DEPENDS
+    libc.src.__support.macros.attributes
+)
+
 add_subdirectory(FPUtil)
 add_subdirectory(OSUtil)
 add_subdirectory(StringUtil)
diff --git a/libc/src/__support/intrusive_list.h b/libc/src/__support/intrusive_list.h
new file mode 100644
index 0000000000000..913edacca55c2
--- /dev/null
+++ b/libc/src/__support/intrusive_list.h
@@ -0,0 +1,65 @@
+//===-- Intrusive 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 intrusive 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 {
+
+class IntrusiveList {
+  struct IntrusiveNodeHeader {
+    IntrusiveNodeHeader *next;
+    IntrusiveNodeHeader *prev;
+  };
+
+public:
+  LIBC_INLINE static void insert(void *elem, void *prev) {
+    auto elem_header = static_cast<IntrusiveNodeHeader *>(elem);
+    auto prev_header = static_cast<IntrusiveNodeHeader *>(prev);
+
+    if (!prev_header) {
+      // The list is linear and elem will be the only element.
+      elem_header->next = nullptr;
+      elem_header->prev = nullptr;
+      return;
+    }
+
+    auto next = prev_header->next;
+
+    elem_header->next = next;
+    elem_header->prev = prev_header;
+
+    prev_header->next = elem_header;
+    if (next)
+      next->prev = elem_header;
+  }
+
+  LIBC_INLINE static void remove(void *elem) {
+    auto elem_header = static_cast<IntrusiveNodeHeader *>(elem);
+
+    auto prev = elem_header->prev;
+    auto next = elem_header->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..46ad3e33c02fa 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.intrusive_list
+)
+
+add_entrypoint_object(
+  remque
+  SRCS
+    remque.cpp
+  HDRS
+    remque.h
+  DEPENDS
+    libc.include.search
+    libc.src.__support.intrusive_list
+)
diff --git a/libc/src/search/insque.cpp b/libc/src/search/insque.cpp
new file mode 100644
index 0000000000000..7b7d7c787af13
--- /dev/null
+++ b/libc/src/search/insque.cpp
@@ -0,0 +1,19 @@
+//===-- 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/intrusive_list.h"
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(void, insque, (void *elem, void *prev)) {
+  internal::IntrusiveList::insert(elem, prev);
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/search/insque.h b/libc/src/search/insque.h
new file mode 100644
index 0000000000000..e0fb69ed1df7c
--- /dev/null
+++ b/libc/src/search/insque.h
@@ -0,0 +1,20 @@
+//===-- 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..f1d9859eedb51
--- /dev/null
+++ b/libc/src/search/remque.cpp
@@ -0,0 +1,19 @@
+//===-- 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/intrusive_list.h"
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(void, remque, (void *elem)) {
+  internal::IntrusiveList::remove(elem);
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/search/remque.h b/libc/src/search/remque.h
new file mode 100644
index 0000000000000..51f225c004740
--- /dev/null
+++ b/libc/src/search/remque.h
@@ -0,0 +1,20 @@
+//===-- 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]});
+}

>From 12b831ed4da7c4e7743bafd6a02591fb1d575d80 Mon Sep 17 00:00:00 2001
From: Sirui Mu <msrlancern at gmail.com>
Date: Fri, 2 Feb 2024 13:48:15 +0800
Subject: [PATCH 2/3] Add function declaration to posix.td

---
 libc/spec/posix.td | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/libc/spec/posix.td b/libc/spec/posix.td
index 40a663afd2595..92d0f8edb2994 100644
--- a/libc/spec/posix.td
+++ b/libc/spec/posix.td
@@ -1323,6 +1323,21 @@ def POSIX : StandardSpec<"POSIX"> {
                 ArgSpec<ActionType>
             ]
         >,
+        FunctionSpec<
+            "insque",
+            RetValSpec<VoidType>,
+            [
+                ArgSpec<VoidPtr>,
+                ArgSpec<VoidPtr>
+            ]
+        >,
+        FunctionSpec<
+            "remque",
+            RetValSpec<VoidType>,
+            [
+                ArgSpec<VoidPtr>
+            ]
+        >,
     ]
   >; 
 

>From 3660f600e719eb21a3ac0df3c6228095bb3beb24 Mon Sep 17 00:00:00 2001
From: Sirui Mu <msrlancern at gmail.com>
Date: Sat, 3 Feb 2024 13:33:30 +0800
Subject: [PATCH 3/3] Update include guard for intrusive_list.h

---
 libc/src/__support/intrusive_list.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libc/src/__support/intrusive_list.h b/libc/src/__support/intrusive_list.h
index 913edacca55c2..69be877777f78 100644
--- a/libc/src/__support/intrusive_list.h
+++ b/libc/src/__support/intrusive_list.h
@@ -10,8 +10,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_LIBC_SRC___SUPPORT_INVASIVE_QUEUE_H
-#define LLVM_LIBC_SRC___SUPPORT_INVASIVE_QUEUE_H
+#ifndef LLVM_LIBC_SRC___SUPPORT_INTRUSIVE_LIST_H
+#define LLVM_LIBC_SRC___SUPPORT_INTRUSIVE_LIST_H
 
 #include "common.h"
 
@@ -62,4 +62,4 @@ class IntrusiveList {
 } // namespace internal
 } // namespace LIBC_NAMESPACE
 
-#endif // LLVM_LIBC_SRC___SUPPORT_INVASIVE_QUEUE_H
+#endif // LLVM_LIBC_SRC___SUPPORT_INTRUSIVE_LIST_H



More information about the libc-commits mailing list