Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[libc] implement insque and remque #80305

Merged
merged 4 commits into from
Feb 5, 2024
Merged

Conversation

Lancern
Copy link
Member

@Lancern Lancern commented Feb 1, 2024

This PR implements the insque and remque entrypoint functions.

@llvmbot llvmbot added the libc label Feb 1, 2024
@llvmbot
Copy link

llvmbot commented Feb 1, 2024

@llvm/pr-subscribers-libc

Author: Sirui Mu (Lancern)

Changes

This PR implements the insque and remque entrypoint functions.


Full diff: https://github.com/llvm/llvm-project/pull/80305.diff

12 Files Affected:

  • (modified) libc/config/linux/aarch64/entrypoints.txt (+2)
  • (modified) libc/config/linux/riscv/entrypoints.txt (+2)
  • (modified) libc/config/linux/x86_64/entrypoints.txt (+2)
  • (modified) libc/src/__support/CMakeLists.txt (+6)
  • (added) libc/src/__support/invasive_list.h (+60)
  • (modified) libc/src/search/CMakeLists.txt (+22)
  • (added) libc/src/search/insque.cpp (+21)
  • (added) libc/src/search/insque.h (+21)
  • (added) libc/src/search/remque.cpp (+20)
  • (added) libc/src/search/remque.h (+21)
  • (modified) libc/test/src/search/CMakeLists.txt (+13)
  • (added) libc/test/src/search/insque_test.cpp (+186)
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..05464a6c19bf1 100644
--- a/libc/test/src/search/CMakeLists.txt
+++ b/libc/test/src/search/CMakeLists.txt
@@ -14,3 +14,16 @@ add_libc_unittest(
     libc.src.search.hdestroy
     libc.src.errno.errno
 )
+
+message("!!!!!!!!!!!!!!!!!!!!!!!!!!! FUCK")
+
+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]});
+}

Copy link

github-actions bot commented Feb 1, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@lntue
Copy link
Contributor

lntue commented Feb 1, 2024

I think you will need to provide the declaration for these functions for it to work in full build mode. The search.h we generate is from here:
https://github.com/llvm/llvm-project/blob/main/libc/spec/posix.td#L1297

libc/src/__support/invasive_list.h Outdated Show resolved Hide resolved
libc/src/__support/invasive_list.h Outdated Show resolved Hide resolved
libc/src/search/insque.cpp Outdated Show resolved Hide resolved
libc/src/search/remque.cpp Outdated Show resolved Hide resolved
Copy link
Contributor

@SchrodingerZhu SchrodingerZhu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@SchrodingerZhu
Copy link
Contributor

I have asked @Lancern to update the associated doc. After that, if there is no further issue, I will go ahead and merge this. 😄

@Lancern
Copy link
Member Author

Lancern commented Feb 5, 2024

Documentation updated. Also rebased onto the latest main.

@SchrodingerZhu
Copy link
Contributor

Thank you.

@SchrodingerZhu SchrodingerZhu merged commit 78a12f9 into llvm:main Feb 5, 2024
6 checks passed
agozillon pushed a commit to agozillon/llvm-project that referenced this pull request Feb 5, 2024
This PR implements the `insque` and `remque` entrypoint functions.
@Lancern Lancern deleted the libc-insque branch February 6, 2024 01:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants