Skip to content

Commit 7c725a9

Browse files
committed
[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.
1 parent b348126 commit 7c725a9

File tree

7 files changed

+308
-0
lines changed

7 files changed

+308
-0
lines changed

libc/config/baremetal/arm/headers.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ set(TARGET_PUBLIC_HEADERS
88
libc.include.stdlib
99
libc.include.string
1010
libc.include.strings
11+
libc.include.sys_queue
1112
)

libc/config/baremetal/riscv/headers.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ set(TARGET_PUBLIC_HEADERS
88
libc.include.stdlib
99
libc.include.string
1010
libc.include.strings
11+
libc.include.sys_queue
1112
)

libc/include/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,14 @@ add_gen_header(
345345
.llvm_libc_common_h
346346
)
347347

348+
add_header(
349+
sys_queue
350+
HDR
351+
sys/queue.h
352+
DEPENDS
353+
.llvm-libc-macros.null_macro
354+
)
355+
348356
add_gen_header(
349357
sys_random
350358
DEF_FILE sys/random.h.def

libc/include/sys/queue.h

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
//===-- Macros defined in sys/queue.h header file -------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef __LLVM_LIBC_MACROS_SYS_QUEUE_MACROS_H
10+
#define __LLVM_LIBC_MACROS_SYS_QUEUE_MACROS_H
11+
12+
#include <llvm-libc-macros/null-macro.h>
13+
14+
// Singly-linked list definitions.
15+
16+
#define SLIST_HEAD(name, type) \
17+
struct name { \
18+
struct type *first; \
19+
}
20+
21+
#define SLIST_HEAD_INITIALIZER(head) \
22+
{ NULL }
23+
24+
#define SLIST_ENTRY(type) \
25+
struct { \
26+
struct type *next; \
27+
}
28+
29+
// Singly-linked list access methods.
30+
31+
#define SLIST_EMPTY(head) ((head)->first == NULL)
32+
#define SLIST_FIRST(head) ((head)->first)
33+
#define SLIST_NEXT(elem, field) ((elem)->field.next)
34+
35+
#define SLIST_FOREACH(var, head, field) \
36+
for ((var) = SLIST_FIRST(head); (var); (var) = SLIST_NEXT(var, field))
37+
38+
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
39+
for ((var) = SLIST_FIRST(head); \
40+
(var) && ((tvar) = SLIST_NEXT(var, field), 1); (var) = (tvar))
41+
42+
// Singly-linked list functions.
43+
44+
#define SLIST_INIT(head) \
45+
do { \
46+
SLIST_FIRST(head) = NULL; \
47+
} while (0)
48+
49+
#define SLIST_INSERT_HEAD(head, elem, field) \
50+
do { \
51+
SLIST_NEXT(elem, field) = SLIST_FIRST(head); \
52+
SLIST_FIRST(head) = (elem); \
53+
} while (0)
54+
55+
#define SLIST_INSERT_AFTER(slistelem, elem, field) \
56+
do { \
57+
SLIST_NEXT(elem, field) = SLIST_NEXT(slistelem, field); \
58+
SLIST_NEXT(slistelem, field) = (elem); \
59+
} while (0)
60+
61+
#define SLIST_REMOVE_HEAD(head, field) \
62+
do { \
63+
SLIST_FIRST(head) = SLIST_NEXT(SLIST_FIRST(head), field); \
64+
} while (0)
65+
66+
#define SLIST_REMOVE_AFTER(elem, field) \
67+
do { \
68+
SLIST_NEXT(elem, field) = SLIST_NEXT(SLIST_NEXT(elem, field), field); \
69+
} while (0)
70+
71+
#define SLIST_REMOVE(head, elem, type, field) \
72+
do { \
73+
if (SLIST_FIRST(head) == (elem)) { \
74+
SLIST_REMOVE_HEAD(head, field); \
75+
} else { \
76+
struct type *cur = SLIST_FIRST(head); \
77+
while (SLIST_NEXT(elem, field) != (elem)) \
78+
cur = SLIST_NEXT(elem, field); \
79+
SLIST_REMOVE_AFTER(cur, field); \
80+
} \
81+
} while (0)
82+
83+
// Singly-linked tail queue definitions.
84+
85+
#define STAILQ_HEAD(name, type) \
86+
struct name { \
87+
struct type *first; \
88+
struct type **last; \
89+
}
90+
91+
#define STAILQ_HEAD_INITIALIZER(head) \
92+
{ NULL, &(head).first }
93+
94+
#define STAILQ_ENTRY(type) \
95+
struct { \
96+
struct type *next; \
97+
}
98+
99+
// Singly-linked tail queue access methods.
100+
101+
#define STAILQ_EMPTY(head) ((head)->first == NULL)
102+
#define STAILQ_FIRST(head) ((head)->first)
103+
#define STAILQ_NEXT(elem, field) ((elem)->field.next)
104+
105+
#define STAILQ_FOREACH(var, head, field) \
106+
for ((var) = STAILQ_FIRST(head); (var); (var) = STAILQ_NEXT(var, field))
107+
108+
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
109+
for ((var) = STAILQ_FIRST(head); \
110+
(var) && ((tvar) = STAILQ_NEXT(var, field), 1); (var) = (tvar))
111+
112+
// Singly-linked tail queue functions.
113+
114+
#define STAILQ_INIT(head) \
115+
do { \
116+
STAILQ_FIRST(head) = NULL; \
117+
(head)->last = &STAILQ_FIRST(head); \
118+
} while (0)
119+
120+
#define STAILQ_INSERT_HEAD(head, elem, field) \
121+
do { \
122+
if ((STAILQ_NEXT(elem, field) = STAILQ_FIRST(head)) == NULL) \
123+
(head)->last = &STAILQ_NEXT(elem, field); \
124+
STAILQ_FIRST(head) = (elem); \
125+
} while (0)
126+
127+
#define STAILQ_INSERT_TAIL(head, elem, field) \
128+
do { \
129+
STAILQ_NEXT(elem, field) = NULL; \
130+
*(head)->last = (elem); \
131+
(head)->last = &STAILQ_NEXT(elem, field); \
132+
} while (0)
133+
134+
#define STAILQ_INSERT_AFTER(head, listelem, elem, field) \
135+
do { \
136+
if ((STAILQ_NEXT(elem, field) = STAILQ_NEXT(listelem, field)) == NULL) \
137+
(head)->last = &STAILQ_NEXT(elem, field); \
138+
STAILQ_NEXT(listelem, next) = (elem); \
139+
} while (0)
140+
141+
#define STAILQ_REMOVE_HEAD(head, field) \
142+
do { \
143+
if ((STAILQ_FIRST(head) = STAILQ_NEXT(STAILQ_FIRST(head), field)) == NULL) \
144+
(head)->last = &STAILQ_FIRST(head); \
145+
} while (0)
146+
147+
#define STAILQ_REMOVE(head, elem, type, field) \
148+
do { \
149+
if (STAILQ_FIRST(head) == (elem)) { \
150+
STAILQ_REMOVE_HEAD(head, field); \
151+
} else { \
152+
struct type *cur = STAILQ_FIRST(head); \
153+
while (STAILQ_NEXT(elem, field) != (elem)) \
154+
cur = STAILQ_NEXT(cur, field); \
155+
if ((STAILQ_NEXT(cur, field) = \
156+
STAILQ_NEXT(STAILQ_NEXT(cur, field), field)) == NULL) \
157+
(head)->last = &STAILQ_NEXT(cur, field); \
158+
} \
159+
} while (0)
160+
161+
#endif // __LLVM_LIBC_MACROS_SYS_QUEUE_MACROS_H

libc/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ if(LIBC_TARGET_ARCHITECTURE_IS_GPU AND
1414
return()
1515
endif()
1616

17+
add_subdirectory(include)
1718
add_subdirectory(src)
1819
add_subdirectory(utils)
1920

libc/test/include/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
add_custom_target(libc_include_tests)
2+
3+
add_libc_test(
4+
sys_queue_test
5+
SUITE
6+
libc_include_tests
7+
SRCS
8+
sys/queue_test.cpp
9+
DEPENDS
10+
libc.include.sys_queue
11+
libc.src.__support.char_vector
12+
libc.src.__support.CPP.string
13+
)

libc/test/include/sys/queue_test.cpp

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
//===-- Unittests for queue -----------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDSList-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/__support/CPP/string.h"
10+
#include "src/__support/char_vector.h"
11+
#include "test/UnitTest/Test.h"
12+
13+
#include <sys/queue.h>
14+
15+
using LIBC_NAMESPACE::CharVector;
16+
using LIBC_NAMESPACE::cpp::string;
17+
18+
namespace LIBC_NAMESPACE {
19+
20+
TEST(LlvmLibcQueueTest, SList) {
21+
struct Contrived {
22+
char c;
23+
SLIST_ENTRY(Contrived) entry;
24+
};
25+
26+
SLIST_HEAD(Head, Contrived) head = SLIST_HEAD_INITIALIZER(head);
27+
28+
struct Contains : public testing::Matcher<Head> {
29+
string s;
30+
Contains(string s) : s(s) {}
31+
bool match(Head head) {
32+
Contrived *e;
33+
CharVector v;
34+
SLIST_FOREACH(e, &head, entry) { v.append(e->c); }
35+
return s == v.c_str();
36+
}
37+
};
38+
39+
SLIST_INIT(&head);
40+
ASSERT_TRUE(SLIST_EMPTY(&head));
41+
42+
Contrived e1 = {'a', {NULL}};
43+
SLIST_INSERT_HEAD(&head, &e1, entry);
44+
45+
ASSERT_THAT(head, Contains("a"));
46+
47+
Contrived e2 = {'b', {NULL}};
48+
SLIST_INSERT_AFTER(&e1, &e2, entry);
49+
50+
ASSERT_THAT(head, Contains("ab"));
51+
52+
Contrived *e, *tmp = NULL;
53+
SLIST_FOREACH_SAFE(e, &head, entry, tmp) {
54+
if (e == &e2) {
55+
SLIST_REMOVE(&head, e, Contrived, entry);
56+
}
57+
}
58+
59+
ASSERT_THAT(head, Contains("a"));
60+
61+
while (!SLIST_EMPTY(&head)) {
62+
e = SLIST_FIRST(&head);
63+
SLIST_REMOVE_HEAD(&head, entry);
64+
}
65+
66+
ASSERT_TRUE(SLIST_EMPTY(&head));
67+
}
68+
69+
TEST(LlvmLibcQueueTest, STailQ) {
70+
struct Contrived {
71+
char c;
72+
STAILQ_ENTRY(Contrived) entry;
73+
};
74+
75+
STAILQ_HEAD(Head, Contrived) head = STAILQ_HEAD_INITIALIZER(head);
76+
77+
struct Contains : public testing::Matcher<Head> {
78+
string s;
79+
Contains(string s) : s(s) {}
80+
bool match(Head head) {
81+
Contrived *e;
82+
CharVector v;
83+
STAILQ_FOREACH(e, &head, entry) { v.append(e->c); }
84+
return s == v.c_str();
85+
}
86+
};
87+
88+
STAILQ_INIT(&head);
89+
ASSERT_TRUE(STAILQ_EMPTY(&head));
90+
91+
Contrived e1 = {'a', {NULL}};
92+
STAILQ_INSERT_HEAD(&head, &e1, entry);
93+
94+
ASSERT_THAT(head, Contains("a"));
95+
96+
Contrived e2 = {'b', {NULL}};
97+
STAILQ_INSERT_TAIL(&head, &e2, entry);
98+
99+
ASSERT_THAT(head, Contains("ab"));
100+
101+
Contrived e3 = {'c', {NULL}};
102+
SLIST_INSERT_AFTER(&e2, &e3, entry);
103+
104+
ASSERT_THAT(head, Contains("abc"));
105+
106+
Contrived *e, *tmp = NULL;
107+
STAILQ_FOREACH_SAFE(e, &head, entry, tmp) {
108+
if (e == &e2) {
109+
STAILQ_REMOVE(&head, e, Contrived, entry);
110+
}
111+
}
112+
113+
ASSERT_THAT(head, Contains("ac"));
114+
115+
while (!STAILQ_EMPTY(&head)) {
116+
e = STAILQ_FIRST(&head);
117+
STAILQ_REMOVE_HEAD(&head, entry);
118+
}
119+
120+
ASSERT_TRUE(STAILQ_EMPTY(&head));
121+
}
122+
123+
} // namespace LIBC_NAMESPACE

0 commit comments

Comments
 (0)