[libc-commits] [libc] [libc] Provide sys/queue.h (PR #78081)

Petr Hosek via libc-commits libc-commits at lists.llvm.org
Sat Jan 13 22:36:23 PST 2024


https://github.com/petrhosek updated https://github.com/llvm/llvm-project/pull/78081

>From 7c725a98e7ede888b0540b2bdcbc103d598b2df3 Mon Sep 17 00:00:00 2001
From: Petr Hosek <phosek at google.com>
Date: Sun, 14 Jan 2024 04:37:41 +0000
Subject: [PATCH] [libc] Provide sys/queue.h

This header first appeared in 4.4BSD and is provided by a number of C
libraries including Newlib. Several of our embedded projects use this
header and so to make LLVM libc a drop-in replacement, we need to
provide it as well.

For the initial commit, we only implement singly linked variants (SLIST
and STAILQ). The doubly linked variants (LIST, TAILQ and CIRCLEQ) can be
implemented in the future as needed.
---
 libc/config/baremetal/arm/headers.txt   |   1 +
 libc/config/baremetal/riscv/headers.txt |   1 +
 libc/include/CMakeLists.txt             |   8 ++
 libc/include/sys/queue.h                | 161 ++++++++++++++++++++++++
 libc/test/CMakeLists.txt                |   1 +
 libc/test/include/CMakeLists.txt        |  13 ++
 libc/test/include/sys/queue_test.cpp    | 123 ++++++++++++++++++
 7 files changed, 308 insertions(+)
 create mode 100644 libc/include/sys/queue.h
 create mode 100644 libc/test/include/CMakeLists.txt
 create mode 100644 libc/test/include/sys/queue_test.cpp

diff --git a/libc/config/baremetal/arm/headers.txt b/libc/config/baremetal/arm/headers.txt
index e7be1fd80e8754..6ff51f9786772b 100644
--- a/libc/config/baremetal/arm/headers.txt
+++ b/libc/config/baremetal/arm/headers.txt
@@ -8,4 +8,5 @@ set(TARGET_PUBLIC_HEADERS
     libc.include.stdlib
     libc.include.string
     libc.include.strings
+    libc.include.sys_queue
 )
diff --git a/libc/config/baremetal/riscv/headers.txt b/libc/config/baremetal/riscv/headers.txt
index e7be1fd80e8754..6ff51f9786772b 100644
--- a/libc/config/baremetal/riscv/headers.txt
+++ b/libc/config/baremetal/riscv/headers.txt
@@ -8,4 +8,5 @@ set(TARGET_PUBLIC_HEADERS
     libc.include.stdlib
     libc.include.string
     libc.include.strings
+    libc.include.sys_queue
 )
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index 2c2d1b9b0fd155..cc846a25e85d65 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -345,6 +345,14 @@ add_gen_header(
     .llvm_libc_common_h
 )
 
+add_header(
+  sys_queue
+  HDR
+    sys/queue.h
+  DEPENDS
+    .llvm-libc-macros.null_macro
+)
+
 add_gen_header(
   sys_random
   DEF_FILE sys/random.h.def
diff --git a/libc/include/sys/queue.h b/libc/include/sys/queue.h
new file mode 100644
index 00000000000000..4eab6ef91f183c
--- /dev/null
+++ b/libc/include/sys/queue.h
@@ -0,0 +1,161 @@
+//===-- Macros defined in sys/queue.h header file -------------------------===//
+//
+// 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_MACROS_SYS_QUEUE_MACROS_H
+#define __LLVM_LIBC_MACROS_SYS_QUEUE_MACROS_H
+
+#include <llvm-libc-macros/null-macro.h>
+
+// Singly-linked list definitions.
+
+#define SLIST_HEAD(name, type)                                                 \
+  struct name {                                                                \
+    struct type *first;                                                        \
+  }
+
+#define SLIST_HEAD_INITIALIZER(head)                                           \
+  { NULL }
+
+#define SLIST_ENTRY(type)                                                      \
+  struct {                                                                     \
+    struct type *next;                                                         \
+  }
+
+// Singly-linked list access methods.
+
+#define SLIST_EMPTY(head) ((head)->first == NULL)
+#define SLIST_FIRST(head) ((head)->first)
+#define SLIST_NEXT(elem, field) ((elem)->field.next)
+
+#define SLIST_FOREACH(var, head, field)                                        \
+  for ((var) = SLIST_FIRST(head); (var); (var) = SLIST_NEXT(var, field))
+
+#define SLIST_FOREACH_SAFE(var, head, field, tvar)                             \
+  for ((var) = SLIST_FIRST(head);                                              \
+       (var) && ((tvar) = SLIST_NEXT(var, field), 1); (var) = (tvar))
+
+// Singly-linked list functions.
+
+#define SLIST_INIT(head)                                                       \
+  do {                                                                         \
+    SLIST_FIRST(head) = NULL;                                                  \
+  } while (0)
+
+#define SLIST_INSERT_HEAD(head, elem, field)                                   \
+  do {                                                                         \
+    SLIST_NEXT(elem, field) = SLIST_FIRST(head);                               \
+    SLIST_FIRST(head) = (elem);                                                \
+  } while (0)
+
+#define SLIST_INSERT_AFTER(slistelem, elem, field)                             \
+  do {                                                                         \
+    SLIST_NEXT(elem, field) = SLIST_NEXT(slistelem, field);                    \
+    SLIST_NEXT(slistelem, field) = (elem);                                     \
+  } while (0)
+
+#define SLIST_REMOVE_HEAD(head, field)                                         \
+  do {                                                                         \
+    SLIST_FIRST(head) = SLIST_NEXT(SLIST_FIRST(head), field);                  \
+  } while (0)
+
+#define SLIST_REMOVE_AFTER(elem, field)                                        \
+  do {                                                                         \
+    SLIST_NEXT(elem, field) = SLIST_NEXT(SLIST_NEXT(elem, field), field);      \
+  } while (0)
+
+#define SLIST_REMOVE(head, elem, type, field)                                  \
+  do {                                                                         \
+    if (SLIST_FIRST(head) == (elem)) {                                         \
+      SLIST_REMOVE_HEAD(head, field);                                          \
+    } else {                                                                   \
+      struct type *cur = SLIST_FIRST(head);                                    \
+      while (SLIST_NEXT(elem, field) != (elem))                                \
+        cur = SLIST_NEXT(elem, field);                                         \
+      SLIST_REMOVE_AFTER(cur, field);                                          \
+    }                                                                          \
+  } while (0)
+
+// Singly-linked tail queue definitions.
+
+#define STAILQ_HEAD(name, type)                                                \
+  struct name {                                                                \
+    struct type *first;                                                        \
+    struct type **last;                                                        \
+  }
+
+#define STAILQ_HEAD_INITIALIZER(head)                                          \
+  { NULL, &(head).first }
+
+#define STAILQ_ENTRY(type)                                                     \
+  struct {                                                                     \
+    struct type *next;                                                         \
+  }
+
+// Singly-linked tail queue access methods.
+
+#define STAILQ_EMPTY(head) ((head)->first == NULL)
+#define STAILQ_FIRST(head) ((head)->first)
+#define STAILQ_NEXT(elem, field) ((elem)->field.next)
+
+#define STAILQ_FOREACH(var, head, field)                                       \
+  for ((var) = STAILQ_FIRST(head); (var); (var) = STAILQ_NEXT(var, field))
+
+#define STAILQ_FOREACH_SAFE(var, head, field, tvar)                            \
+  for ((var) = STAILQ_FIRST(head);                                             \
+       (var) && ((tvar) = STAILQ_NEXT(var, field), 1); (var) = (tvar))
+
+// Singly-linked tail queue functions.
+
+#define STAILQ_INIT(head)                                                      \
+  do {                                                                         \
+    STAILQ_FIRST(head) = NULL;                                                 \
+    (head)->last = &STAILQ_FIRST(head);                                        \
+  } while (0)
+
+#define STAILQ_INSERT_HEAD(head, elem, field)                                  \
+  do {                                                                         \
+    if ((STAILQ_NEXT(elem, field) = STAILQ_FIRST(head)) == NULL)               \
+      (head)->last = &STAILQ_NEXT(elem, field);                                \
+    STAILQ_FIRST(head) = (elem);                                               \
+  } while (0)
+
+#define STAILQ_INSERT_TAIL(head, elem, field)                                  \
+  do {                                                                         \
+    STAILQ_NEXT(elem, field) = NULL;                                           \
+    *(head)->last = (elem);                                                    \
+    (head)->last = &STAILQ_NEXT(elem, field);                                  \
+  } while (0)
+
+#define STAILQ_INSERT_AFTER(head, listelem, elem, field)                       \
+  do {                                                                         \
+    if ((STAILQ_NEXT(elem, field) = STAILQ_NEXT(listelem, field)) == NULL)     \
+      (head)->last = &STAILQ_NEXT(elem, field);                                \
+    STAILQ_NEXT(listelem, next) = (elem);                                      \
+  } while (0)
+
+#define STAILQ_REMOVE_HEAD(head, field)                                        \
+  do {                                                                         \
+    if ((STAILQ_FIRST(head) = STAILQ_NEXT(STAILQ_FIRST(head), field)) == NULL) \
+      (head)->last = &STAILQ_FIRST(head);                                      \
+  } while (0)
+
+#define STAILQ_REMOVE(head, elem, type, field)                                 \
+  do {                                                                         \
+    if (STAILQ_FIRST(head) == (elem)) {                                        \
+      STAILQ_REMOVE_HEAD(head, field);                                         \
+    } else {                                                                   \
+      struct type *cur = STAILQ_FIRST(head);                                   \
+      while (STAILQ_NEXT(elem, field) != (elem))                               \
+        cur = STAILQ_NEXT(cur, field);                                         \
+      if ((STAILQ_NEXT(cur, field) =                                           \
+               STAILQ_NEXT(STAILQ_NEXT(cur, field), field)) == NULL)           \
+        (head)->last = &STAILQ_NEXT(cur, field);                               \
+    }                                                                          \
+  } while (0)
+
+#endif // __LLVM_LIBC_MACROS_SYS_QUEUE_MACROS_H
diff --git a/libc/test/CMakeLists.txt b/libc/test/CMakeLists.txt
index 5e8231ef27f461..f22f2b183aca92 100644
--- a/libc/test/CMakeLists.txt
+++ b/libc/test/CMakeLists.txt
@@ -14,6 +14,7 @@ if(LIBC_TARGET_ARCHITECTURE_IS_GPU AND
   return()
 endif()
 
+add_subdirectory(include)
 add_subdirectory(src)
 add_subdirectory(utils)
 
diff --git a/libc/test/include/CMakeLists.txt b/libc/test/include/CMakeLists.txt
new file mode 100644
index 00000000000000..ea1942befa5952
--- /dev/null
+++ b/libc/test/include/CMakeLists.txt
@@ -0,0 +1,13 @@
+add_custom_target(libc_include_tests)
+
+add_libc_test(
+  sys_queue_test
+  SUITE
+    libc_include_tests
+  SRCS
+    sys/queue_test.cpp
+  DEPENDS
+    libc.include.sys_queue
+    libc.src.__support.char_vector
+    libc.src.__support.CPP.string
+)
diff --git a/libc/test/include/sys/queue_test.cpp b/libc/test/include/sys/queue_test.cpp
new file mode 100644
index 00000000000000..4d0daf6190f12d
--- /dev/null
+++ b/libc/test/include/sys/queue_test.cpp
@@ -0,0 +1,123 @@
+//===-- Unittests for queue -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDSList-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/CPP/string.h"
+#include "src/__support/char_vector.h"
+#include "test/UnitTest/Test.h"
+
+#include <sys/queue.h>
+
+using LIBC_NAMESPACE::CharVector;
+using LIBC_NAMESPACE::cpp::string;
+
+namespace LIBC_NAMESPACE {
+
+TEST(LlvmLibcQueueTest, SList) {
+  struct Contrived {
+    char c;
+    SLIST_ENTRY(Contrived) entry;
+  };
+
+  SLIST_HEAD(Head, Contrived) head = SLIST_HEAD_INITIALIZER(head);
+
+  struct Contains : public testing::Matcher<Head> {
+    string s;
+    Contains(string s) : s(s) {}
+    bool match(Head head) {
+      Contrived *e;
+      CharVector v;
+      SLIST_FOREACH(e, &head, entry) { v.append(e->c); }
+      return s == v.c_str();
+    }
+  };
+
+  SLIST_INIT(&head);
+  ASSERT_TRUE(SLIST_EMPTY(&head));
+
+  Contrived e1 = {'a', {NULL}};
+  SLIST_INSERT_HEAD(&head, &e1, entry);
+
+  ASSERT_THAT(head, Contains("a"));
+
+  Contrived e2 = {'b', {NULL}};
+  SLIST_INSERT_AFTER(&e1, &e2, entry);
+
+  ASSERT_THAT(head, Contains("ab"));
+
+  Contrived *e, *tmp = NULL;
+  SLIST_FOREACH_SAFE(e, &head, entry, tmp) {
+    if (e == &e2) {
+      SLIST_REMOVE(&head, e, Contrived, entry);
+    }
+  }
+
+  ASSERT_THAT(head, Contains("a"));
+
+  while (!SLIST_EMPTY(&head)) {
+    e = SLIST_FIRST(&head);
+    SLIST_REMOVE_HEAD(&head, entry);
+  }
+
+  ASSERT_TRUE(SLIST_EMPTY(&head));
+}
+
+TEST(LlvmLibcQueueTest, STailQ) {
+  struct Contrived {
+    char c;
+    STAILQ_ENTRY(Contrived) entry;
+  };
+
+  STAILQ_HEAD(Head, Contrived) head = STAILQ_HEAD_INITIALIZER(head);
+
+  struct Contains : public testing::Matcher<Head> {
+    string s;
+    Contains(string s) : s(s) {}
+    bool match(Head head) {
+      Contrived *e;
+      CharVector v;
+      STAILQ_FOREACH(e, &head, entry) { v.append(e->c); }
+      return s == v.c_str();
+    }
+  };
+
+  STAILQ_INIT(&head);
+  ASSERT_TRUE(STAILQ_EMPTY(&head));
+
+  Contrived e1 = {'a', {NULL}};
+  STAILQ_INSERT_HEAD(&head, &e1, entry);
+
+  ASSERT_THAT(head, Contains("a"));
+
+  Contrived e2 = {'b', {NULL}};
+  STAILQ_INSERT_TAIL(&head, &e2, entry);
+
+  ASSERT_THAT(head, Contains("ab"));
+
+  Contrived e3 = {'c', {NULL}};
+  SLIST_INSERT_AFTER(&e2, &e3, entry);
+
+  ASSERT_THAT(head, Contains("abc"));
+
+  Contrived *e, *tmp = NULL;
+  STAILQ_FOREACH_SAFE(e, &head, entry, tmp) {
+    if (e == &e2) {
+      STAILQ_REMOVE(&head, e, Contrived, entry);
+    }
+  }
+
+  ASSERT_THAT(head, Contains("ac"));
+
+  while (!STAILQ_EMPTY(&head)) {
+    e = STAILQ_FIRST(&head);
+    STAILQ_REMOVE_HEAD(&head, entry);
+  }
+
+  ASSERT_TRUE(STAILQ_EMPTY(&head));
+}
+
+} // namespace LIBC_NAMESPACE



More information about the libc-commits mailing list