diff --git a/libc/config/baremetal/arm/headers.txt b/libc/config/baremetal/arm/headers.txt index e7be1fd80e875..6ff51f9786772 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 e7be1fd80e875..6ff51f9786772 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/config/linux/riscv/headers.txt b/libc/config/linux/riscv/headers.txt index 3e2b1630f1695..9c70a3bde74f0 100644 --- a/libc/config/linux/riscv/headers.txt +++ b/libc/config/linux/riscv/headers.txt @@ -31,6 +31,7 @@ set(TARGET_PUBLIC_HEADERS libc.include.sys_mman libc.include.sys_prctl libc.include.sys_random + libc.include.sys_queue libc.include.sys_resource libc.include.sys_select libc.include.sys_socket diff --git a/libc/config/linux/x86_64/headers.txt b/libc/config/linux/x86_64/headers.txt index 3e2b1630f1695..a85f87b2a3ee9 100644 --- a/libc/config/linux/x86_64/headers.txt +++ b/libc/config/linux/x86_64/headers.txt @@ -30,6 +30,7 @@ set(TARGET_PUBLIC_HEADERS libc.include.sys_ioctl libc.include.sys_mman libc.include.sys_prctl + libc.include.sys_queue libc.include.sys_random libc.include.sys_resource libc.include.sys_select diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt index 2c2d1b9b0fd15..9a06f829eee18 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.sys_queue_macros +) + add_gen_header( sys_random DEF_FILE sys/random.h.def diff --git a/libc/include/llvm-libc-macros/CMakeLists.txt b/libc/include/llvm-libc-macros/CMakeLists.txt index 9c9e6bfd12564..7b2616d4311d9 100644 --- a/libc/include/llvm-libc-macros/CMakeLists.txt +++ b/libc/include/llvm-libc-macros/CMakeLists.txt @@ -4,7 +4,7 @@ function(add_macro_header name) "MACRO_HEADER" "" # Optional arguments "HDR" # Single value arguments - "" # Multi-value arguments + "DEPENDS" # Multi-value arguments ${ARGN} ) if(TARGET libc.include.llvm-libc-macros.${LIBC_TARGET_OS}.${name}) @@ -14,12 +14,15 @@ function(add_macro_header name) ${MACRO_HEADER_HDR} DEPENDS .${LIBC_TARGET_OS}.${name} + ${MACRO_HEADER_DEPENDS} ) else() add_header( ${name} HDR ${MACRO_HEADER_HDR} + DEPENDS + ${MACRO_HEADER_DEPENDS} ) endif() endfunction(add_macro_header) @@ -70,6 +73,20 @@ add_macro_header( math-macros.h ) +add_macro_header( + offsetof_macro + HDR + offsetof-macro.h +) + +add_macro_header( + containerof_macro + HDR + containerof-macro.h + DEPENDS + .offsetof_macro +) + add_macro_header( sched_macros HDR @@ -118,6 +135,15 @@ add_macro_header( sys-mman-macros.h ) +add_macro_header( + sys_queue_macros + HDR + sys-queue-macros.h + DEPENDS + .null_macro + .containerof_macro +) + add_macro_header( sys_random_macros HDR diff --git a/libc/include/llvm-libc-macros/containerof-macro.h b/libc/include/llvm-libc-macros/containerof-macro.h new file mode 100644 index 0000000000000..ea91fa7097a4f --- /dev/null +++ b/libc/include/llvm-libc-macros/containerof-macro.h @@ -0,0 +1,20 @@ +//===-- Definition of the containerof macro -------------------------------===// +// +// 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_CONTAINEROF_MACRO_H +#define __LLVM_LIBC_MACROS_CONTAINEROF_MACRO_H + +#include + +#define __containerof(ptr, type, member) \ + ({ \ + const __typeof(((type *)0)->member) *__ptr = (ptr); \ + (type *)(void *)((const char *)__ptr - offsetof(type, member)); \ + }) + +#endif // __LLVM_LIBC_MACROS_CONTAINEROF_MACRO_H diff --git a/libc/include/llvm-libc-macros/offsetof-macro.h b/libc/include/llvm-libc-macros/offsetof-macro.h new file mode 100644 index 0000000000000..eeceb3db110b6 --- /dev/null +++ b/libc/include/llvm-libc-macros/offsetof-macro.h @@ -0,0 +1,15 @@ +//===-- Definition of the offsetof macro ----------------------------------===// +// +// 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_OFFSETOF_MACRO_H +#define __LLVM_LIBC_MACROS_OFFSETOF_MACRO_H + +#define __need_offsetof +#include + +#endif // __LLVM_LIBC_MACROS_OFFSETOF_MACRO_H diff --git a/libc/include/llvm-libc-macros/sys-queue-macros.h b/libc/include/llvm-libc-macros/sys-queue-macros.h new file mode 100644 index 0000000000000..f41e7363e418b --- /dev/null +++ b/libc/include/llvm-libc-macros/sys-queue-macros.h @@ -0,0 +1,260 @@ +//===-- 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 +#include + +#ifdef __cplusplus +#define QUEUE_TYPEOF(type) type +#else +#define QUEUE_TYPEOF(type) struct type +#endif + +// Singly-linked list definitions. + +#define SLIST_HEAD(name, type) \ + struct name { \ + struct type *first; \ + } + +#define SLIST_CLASS_HEAD(name, type) \ + struct name { \ + class type *first; \ + } + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ + struct { \ + struct type *next; \ + } + +#define SLIST_CLASS_ENTRY(type) \ + struct { \ + class 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_FROM(var, head, field) \ + if (!(var)) \ + (var) = SLIST_FIRST(head); \ + for (; (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)) + +#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ + if (!(var)) \ + (var) = SLIST_FIRST(head); \ + for (; (var) && ((tvar) = SLIST_NEXT(var, field), 1); (var) = (tvar)) + +// Singly-linked list functions. + +#define SLIST_CONCAT(head1, head2, type, field) \ + do { \ + if (SLIST_EMPTY(head1)) { \ + if ((SLIST_FIRST(head1) = SLIST_FIRST(head2)) != NULL) \ + SLIST_INIT(head2); \ + } else if (!SLIST_EMPTY(head2)) { \ + QUEUE_TYPEOF(type) *cur = SLIST_FIRST(head1); \ + while (SLIST_NEXT(cur, field) != NULL) \ + cur = SLIST_NEXT(cur, field); \ + SLIST_NEXT(cur, field) = SLIST_FIRST(head2); \ + SLIST_INIT(head2); \ + } \ + } while (0) + +#define SLIST_INIT(head) \ + do { \ + SLIST_FIRST(head) = NULL; \ + } 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_INSERT_HEAD(head, elem, field) \ + do { \ + SLIST_NEXT(elem, field) = SLIST_FIRST(head); \ + SLIST_FIRST(head) = (elem); \ + } while (0) + +#define SLIST_REMOVE(head, elem, type, field) \ + do { \ + if (SLIST_FIRST(head) == (elem)) { \ + SLIST_REMOVE_HEAD(head, field); \ + } else { \ + QUEUE_TYPEOF(type) *cur = SLIST_FIRST(head); \ + while (SLIST_NEXT(cur, field) != (elem)) \ + cur = SLIST_NEXT(cur, field); \ + SLIST_REMOVE_AFTER(cur, 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(head, field) \ + do { \ + SLIST_FIRST(head) = SLIST_NEXT(SLIST_FIRST(head), field); \ + } while (0) + +#define SLIST_SWAP(head1, head2, type) \ + do { \ + QUEUE_TYPEOF(type) *first = SLIST_FIRST(head1); \ + SLIST_FIRST(head1) = SLIST_FIRST(head2); \ + SLIST_FIRST(head2) = first; \ + } while (0) + +// Singly-linked tail queue definitions. + +#define STAILQ_HEAD(name, type) \ + struct name { \ + struct type *first; \ + struct type **last; \ + } + +#define STAILQ_CLASS_HEAD(name, type) \ + struct name { \ + class type *first; \ + class type **last; \ + } + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).first } + +#define STAILQ_ENTRY(type) \ + struct { \ + struct type *next; \ + } + +#define STAILQ_CLASS_ENTRY(type) \ + struct { \ + class type *next; \ + } + +// Singly-linked tail queue access methods. + +#define STAILQ_EMPTY(head) ((head)->first == NULL) +#define STAILQ_FIRST(head) ((head)->first) +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY(head) ? NULL : __containerof((head)->last, type, field.next)) +#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_FROM(var, head, field) \ + if (!(var)) \ + (var) = STAILQ_FIRST(head); \ + for (; (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)) + +#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ + if (!(var)) \ + (var) = STAILQ_FIRST(head); \ + for (; (var) && ((tvar) = STAILQ_NEXT(var, field), 1); (var) = (tvar)) + +// Singly-linked tail queue functions. + +#define STAILQ_CONCAT(head1, head2, type, field) \ + do { \ + if (!STAILQ_EMPTY(head2)) { \ + *(head1)->last = (head2)->first; \ + (head1)->last = (head2)->last; \ + STAILQ_INIT(head2); \ + } \ + } while (0) + +#define STAILQ_INIT(head) \ + do { \ + STAILQ_FIRST(head) = NULL; \ + (head)->last = &STAILQ_FIRST(head); \ + } 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, field) = (elem); \ + } 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_REMOVE(head, elem, type, field) \ + do { \ + if (STAILQ_FIRST(head) == (elem)) { \ + STAILQ_REMOVE_HEAD(head, field); \ + } else { \ + QUEUE_TYPEOF(type) *cur = STAILQ_FIRST(head); \ + while (STAILQ_NEXT(cur, field) != (elem)) \ + cur = STAILQ_NEXT(cur, field); \ + STAILQ_REMOVE_AFTER(head, cur, field); \ + } \ + } while (0) + +#define STAILQ_REMOVE_AFTER(head, elem, field) \ + do { \ + if ((STAILQ_NEXT(elem, field) = \ + STAILQ_NEXT(STAILQ_NEXT(elem, field), field)) == NULL) \ + (head)->last = &STAILQ_NEXT(elem, field); \ + } 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_SWAP(head1, head2, type) \ + do { \ + QUEUE_TYPEOF(type) *first = STAILQ_FIRST(head1); \ + QUEUE_TYPEOF(type) **last = (head1)->last; \ + STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ + (head1)->last = (head2)->last; \ + STAILQ_FIRST(head2) = first; \ + (head2)->last = last; \ + if (STAILQ_EMPTY(head1)) \ + (head1)->last = &STAILQ_FIRST(head1); \ + if (STAILQ_EMPTY(head2)) \ + (head2)->last = &STAILQ_FIRST(head2); \ + } while (0) + +#endif // __LLVM_LIBC_MACROS_SYS_QUEUE_MACROS_H diff --git a/libc/include/sys/queue.h b/libc/include/sys/queue.h new file mode 100644 index 0000000000000..2a4dc37712d6d --- /dev/null +++ b/libc/include/sys/queue.h @@ -0,0 +1,14 @@ +//===-- BSD sys/queue.h ---------------------------------------------------===// +// +// 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_SYS_QUEUE_H +#define LLVM_LIBC_SYS_QUEUE_H + +#include + +#endif // LLVM_LIBC_SYS_QUEUE_H diff --git a/libc/test/CMakeLists.txt b/libc/test/CMakeLists.txt index 5e8231ef27f46..f22f2b183aca9 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 0000000000000..95a3aba9d95d9 --- /dev/null +++ b/libc/test/include/CMakeLists.txt @@ -0,0 +1,16 @@ +add_custom_target(libc_include_tests) + +add_libc_unittest( + sys_queue_test + SUITE + libc_include_tests + SRCS + sys/queue_test.cpp + DEPENDS + libc.include.llvm-libc-macros.sys_queue_macros + libc.src.__support.char_vector + libc.src.__support.CPP.string + COMPILE_OPTIONS + # This is needed because the __containerof macro uses statement expression. + -Wno-gnu-statement-expression-from-macro-expansion +) diff --git a/libc/test/include/sys/queue_test.cpp b/libc/test/include/sys/queue_test.cpp new file mode 100644 index 0000000000000..48c0e811c6154 --- /dev/null +++ b/libc/test/include/sys/queue_test.cpp @@ -0,0 +1,168 @@ +//===-- 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 "llvm-libc-macros/sys-queue-macros.h" + +using LIBC_NAMESPACE::CharVector; +using LIBC_NAMESPACE::cpp::string; + +namespace LIBC_NAMESPACE { + +TEST(LlvmLibcQueueTest, SList) { + struct Entry { + char c; + SLIST_ENTRY(Entry) entries; + }; + + SLIST_HEAD(Head, Entry); + + Head head = SLIST_HEAD_INITIALIZER(head); + + struct Contains : public testing::Matcher { + string s; + Contains(string s) : s(s) {} + bool match(Head head) { + Entry *e; + CharVector v; + SLIST_FOREACH(e, &head, entries) { v.append(e->c); } + return s == v.c_str(); + } + }; + + Entry e1 = {'a', {NULL}}; + SLIST_INSERT_HEAD(&head, &e1, entries); + + ASSERT_THAT(head, Contains("a")); + + Entry e2 = {'b', {NULL}}; + SLIST_INSERT_AFTER(&e1, &e2, entries); + + ASSERT_THAT(head, Contains("ab")); + + Head head2 = SLIST_HEAD_INITIALIZER(head); + + Entry e3 = {'c', {NULL}}; + SLIST_INSERT_HEAD(&head2, &e3, entries); + + ASSERT_THAT(head2, Contains("c")); + + SLIST_SWAP(&head, &head2, Entry); + + ASSERT_THAT(head2, Contains("ab")); + + SLIST_CONCAT(&head2, &head, Entry, entries); + + ASSERT_THAT(head2, Contains("abc")); + + SLIST_CONCAT(&head, &head2, Entry, entries); + + ASSERT_THAT(head, Contains("abc")); + + Entry *e = NULL, *tmp = NULL; + SLIST_FOREACH_SAFE(e, &head, entries, tmp) { + if (e == &e2) { + SLIST_REMOVE(&head, e, Entry, entries); + } + } + + ASSERT_THAT(head, Contains("ac")); + + while (!SLIST_EMPTY(&head)) { + e = SLIST_FIRST(&head); + SLIST_REMOVE_HEAD(&head, entries); + } + + ASSERT_TRUE(SLIST_EMPTY(&head)); +} + +TEST(LlvmLibcQueueTest, STailQ) { + struct Entry { + char c; + STAILQ_ENTRY(Entry) entries; + }; + + STAILQ_HEAD(Head, Entry); + + Head head = STAILQ_HEAD_INITIALIZER(head); + + struct Contains : public testing::Matcher { + string s; + Contains(string s) : s(s) {} + bool match(Head head) { + Entry *e; + CharVector v; + STAILQ_FOREACH(e, &head, entries) { v.append(e->c); } + return s == v.c_str(); + } + }; + + STAILQ_INIT(&head); + ASSERT_TRUE(STAILQ_EMPTY(&head)); + + Entry e1 = {'a', {NULL}}; + STAILQ_INSERT_HEAD(&head, &e1, entries); + + ASSERT_THAT(head, Contains("a")); + + Entry e2 = {'b', {NULL}}; + STAILQ_INSERT_TAIL(&head, &e2, entries); + + ASSERT_THAT(head, Contains("ab")); + + Entry e3 = {'c', {NULL}}; + STAILQ_INSERT_AFTER(&head, &e2, &e3, entries); + + ASSERT_THAT(head, Contains("abc")); + + Head head2 = STAILQ_HEAD_INITIALIZER(head); + + Entry e4 = {'d', {NULL}}; + STAILQ_INSERT_HEAD(&head2, &e4, entries); + + ASSERT_THAT(head2, Contains("d")); + + STAILQ_SWAP(&head, &head2, Entry); + + ASSERT_THAT(head2, Contains("abc")); + + STAILQ_CONCAT(&head2, &head, Entry, entries); + + ASSERT_EQ(STAILQ_FIRST(&head2), &e1); + ASSERT_EQ(STAILQ_LAST(&head2, Entry, entries), &e4); + + ASSERT_THAT(head2, Contains("abcd")); + + STAILQ_CONCAT(&head, &head2, Entry, entries); + + ASSERT_EQ(STAILQ_FIRST(&head), &e1); + ASSERT_EQ(STAILQ_LAST(&head, Entry, entries), &e4); + + ASSERT_THAT(head, Contains("abcd")); + + Entry *e = NULL, *tmp = NULL; + STAILQ_FOREACH_SAFE(e, &head, entries, tmp) { + if (e == &e2) { + STAILQ_REMOVE(&head, e, Entry, entries); + } + } + + ASSERT_THAT(head, Contains("acd")); + + while (!STAILQ_EMPTY(&head)) { + e = STAILQ_FIRST(&head); + STAILQ_REMOVE_HEAD(&head, entries); + } + + ASSERT_TRUE(STAILQ_EMPTY(&head)); +} + +} // namespace LIBC_NAMESPACE