[libc] Add very basic stdio FILE and fwrite
Summary:
This patch adds a very basic `FILE` type and basic `fwrite`.
It also removes `snprintf` from `StdIO`'s function spec because `VarArgType` was causing the generation to fail.
Reviewers: sivachandra, PaulkaToast
Reviewed By: sivachandra
Subscribers: mgorny, MaskRay, tschuett, libc-commits
Differential Revision: https://reviews.llvm.org/D77626
GitOrigin-RevId: 99aea5792841ba48ebc427e1326631a584fc8b42
diff --git a/config/linux/api.td b/config/linux/api.td
index f520947..fe3064d 100644
--- a/config/linux/api.td
+++ b/config/linux/api.td
@@ -18,6 +18,12 @@
}];
}
+def FILE : TypeDecl<"FILE"> {
+ let Decl = [{
+ typedef struct FILE FILE;
+ }];
+}
+
def AssertMacro : MacroDef<"assert"> {
let Defn = [{
#undef assert
@@ -159,10 +165,11 @@
def StdIOAPI : PublicAPI<"stdio.h"> {
let TypeDeclarations = [
SizeT,
+ FILE,
];
let Functions = [
- "snprintf",
+ "fwrite",
];
}
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
index 28b7ee0..ae2f2ff 100644
--- a/include/CMakeLists.txt
+++ b/include/CMakeLists.txt
@@ -76,6 +76,14 @@
)
add_gen_header(
+ stdio
+ DEF_FILE stdio.h.def
+ GEN_HDR stdio.h
+ DEPENDS
+ llvm_libc_common_h
+)
+
+add_gen_header(
stdlib
DEF_FILE stdlib.h.def
GEN_HDR stdlib.h
diff --git a/include/stdio.h.def b/include/stdio.h.def
new file mode 100644
index 0000000..7120539
--- /dev/null
+++ b/include/stdio.h.def
@@ -0,0 +1,16 @@
+//===-- C standard library header stdio.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_STDIO_H
+#define LLVM_LIBC_STDIO_H
+
+#include <__llvm-libc-common.h>
+
+%%public_api()
+
+#endif // LLVM_LIBC_STDIO_H
diff --git a/spec/stdc.td b/spec/stdc.td
index 39067bc..4629645 100644
--- a/spec/stdc.td
+++ b/spec/stdc.td
@@ -18,6 +18,10 @@
NamedType SigHandlerT = NamedType<"__sighandler_t">;
+ NamedType FILE = NamedType<"FILE">;
+ PtrType FILEPtr = PtrType<FILE>;
+ RestrictedPtrType FILERestrictedPtr = RestrictedPtrType<FILE>;
+
HeaderSpec Assert = HeaderSpec<
"assert.h",
[
@@ -190,16 +194,17 @@
[], // Macros
[ // Types
SizeTType,
+ FILE,
],
[], // Enumerations
[
FunctionSpec<
- "snprintf",
- RetValSpec<IntType>,
- [ArgSpec<CharPtr>,
- ArgSpec<SizeTType>,
- ArgSpec<ConstCharRestrictedPtr>,
- ArgSpec<VarArgType>]
+ "fwrite",
+ RetValSpec<SizeTType>,
+ [ArgSpec<ConstVoidRestrictedPtr>,
+ ArgSpec<SizeTType>,
+ ArgSpec<SizeTType>,
+ ArgSpec<FILERestrictedPtr>]
>,
]
>;
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3ee30fd..72b4bca 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -2,6 +2,7 @@
add_subdirectory(errno)
add_subdirectory(math)
add_subdirectory(signal)
+add_subdirectory(stdio)
add_subdirectory(stdlib)
add_subdirectory(string)
# TODO: Add this target conditional to the target OS.
diff --git a/src/stdio/CMakeLists.txt b/src/stdio/CMakeLists.txt
new file mode 100644
index 0000000..337ee22
--- /dev/null
+++ b/src/stdio/CMakeLists.txt
@@ -0,0 +1,10 @@
+add_entrypoint_object(
+ fwrite
+ SRCS
+ fwrite.cpp
+ HDRS
+ fwrite.h
+ DEPENDS
+ libc.src.threads.mtx_lock
+ libc.src.threads.mtx_unlock
+)
diff --git a/src/stdio/FILE.h b/src/stdio/FILE.h
new file mode 100644
index 0000000..54bc9b2
--- /dev/null
+++ b/src/stdio/FILE.h
@@ -0,0 +1,27 @@
+//===-- Internal definition of FILE -----------------------------*- 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_STDIO_FILE_H
+#define LLVM_LIBC_SRC_STDIO_FILE_H
+
+#include "include/threads.h"
+#include <stddef.h>
+
+namespace __llvm_libc {
+
+struct FILE {
+ mtx_t lock;
+
+ using write_function_t = size_t(FILE *, const char *, size_t);
+
+ write_function_t *write;
+};
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDIO_FILE_H
diff --git a/src/stdio/fwrite.cpp b/src/stdio/fwrite.cpp
new file mode 100644
index 0000000..80cf50c
--- /dev/null
+++ b/src/stdio/fwrite.cpp
@@ -0,0 +1,30 @@
+//===-- Implementation of fwrite and fwrite_unlocked ------------*- 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/stdio/fwrite.h"
+#include "src/stdio/FILE.h"
+#include "src/threads/mtx_lock.h"
+#include "src/threads/mtx_unlock.h"
+
+namespace __llvm_libc {
+
+size_t fwrite_unlocked(const void *__restrict ptr, size_t size, size_t nmeb,
+ __llvm_libc::FILE *__restrict stream) {
+ return stream->write(stream, reinterpret_cast<const char *>(ptr),
+ size * nmeb);
+}
+
+size_t fwrite(const void *__restrict ptr, size_t size, size_t nmeb,
+ __llvm_libc::FILE *__restrict stream) {
+ __llvm_libc::mtx_lock(&stream->lock);
+ size_t written = fwrite_unlocked(ptr, size, nmeb, stream);
+ __llvm_libc::mtx_unlock(&stream->lock);
+ return written;
+}
+
+} // namespace __llvm_libc
diff --git a/src/stdio/fwrite.h b/src/stdio/fwrite.h
new file mode 100644
index 0000000..8a71ca1
--- /dev/null
+++ b/src/stdio/fwrite.h
@@ -0,0 +1,22 @@
+//===-- Implementation header of fwrite -------------------------*- 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_STDIO_FWRITE_H
+#define LLVM_LIBC_SRC_STDIO_FWRITE_H
+
+#include "src/stdio/FILE.h"
+#include <stddef.h>
+
+namespace __llvm_libc {
+
+size_t fwrite(const void *__restrict ptr, size_t size, size_t nmeb,
+ __llvm_libc::FILE *__restrict stream);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDIO_FWRITE_H
diff --git a/test/src/CMakeLists.txt b/test/src/CMakeLists.txt
index 30c5616..209d00b 100644
--- a/test/src/CMakeLists.txt
+++ b/test/src/CMakeLists.txt
@@ -1,6 +1,7 @@
add_subdirectory(assert)
add_subdirectory(errno)
add_subdirectory(signal)
+add_subdirectory(stdio)
add_subdirectory(stdlib)
add_subdirectory(string)
add_subdirectory(sys)
diff --git a/test/src/stdio/CMakeLists.txt b/test/src/stdio/CMakeLists.txt
new file mode 100644
index 0000000..1394b56
--- /dev/null
+++ b/test/src/stdio/CMakeLists.txt
@@ -0,0 +1,14 @@
+add_libc_testsuite(libc_stdio_unittests)
+
+add_libc_unittest(
+ fwrite_test
+ SUITE
+ libc_stdio_unittests
+ SRCS
+ fwrite_test.cpp
+ DEPENDS
+ libc.src.stdio.fwrite
+ # TODO(sivachandra): remove private dependencies of fwrite
+ libc.src.threads.mtx_lock
+ libc.src.threads.mtx_unlock
+)
diff --git a/test/src/stdio/fwrite_test.cpp b/test/src/stdio/fwrite_test.cpp
new file mode 100644
index 0000000..2b7bf0f
--- /dev/null
+++ b/test/src/stdio/fwrite_test.cpp
@@ -0,0 +1,28 @@
+//===-- Unittests for fwrite ----------------------------------------------===//
+//
+// 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/stdio/FILE.h"
+#include "src/stdio/fwrite.h"
+#include "utils/CPP/Array.h"
+#include "utils/UnitTest/Test.h"
+
+TEST(Stdio, FWriteBasic) {
+ struct StrcpyFile : __llvm_libc::FILE {
+ char *buf;
+ } f;
+ char array[6];
+ f.buf = array;
+ f.write = +[](__llvm_libc::FILE *file, const char *ptr, size_t size) {
+ StrcpyFile *strcpyFile = static_cast<StrcpyFile *>(file);
+ for (size_t i = 0; i < size; ++i)
+ strcpyFile->buf[i] = ptr[i];
+ return size;
+ };
+ EXPECT_EQ(fwrite("hello", 1, 6, &f), 6UL);
+ EXPECT_STREQ(array, "hello");
+}