Index: libc/config/linux/api.td =================================================================== --- libc/config/linux/api.td +++ libc/config/linux/api.td @@ -18,6 +18,12 @@ }]; } +def FILE : TypeDecl<"FILE"> { + let Decl = [{ + typedef struct {} 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", ]; } Index: libc/include/CMakeLists.txt =================================================================== --- libc/include/CMakeLists.txt +++ libc/include/CMakeLists.txt @@ -75,6 +75,14 @@ ../config/${LIBC_TARGET_OS}/signal.h.in ) +add_gen_header( + stdio_h + DEF_FILE stdio.h.def + GEN_HDR stdio.h + DEPENDS + llvm_libc_common_h +) + add_gen_header( stdlib_h DEF_FILE stdlib.h.def Index: libc/include/stdio.h.def =================================================================== --- /dev/null +++ libc/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 Index: libc/spec/stdc.td =================================================================== --- libc/spec/stdc.td +++ libc/spec/stdc.td @@ -18,6 +18,10 @@ NamedType SigHandlerT = NamedType<"__sighandler_t">; + NamedType FILE = NamedType<"FILE">; + PtrType FILEPtr = PtrType; + RestrictedPtrType FILERestrictedPtr = RestrictedPtrType; + HeaderSpec Assert = HeaderSpec< "assert.h", [ @@ -190,16 +194,17 @@ [], // Macros [ // Types SizeTType, + FILE, ], [], // Enumerations [ FunctionSpec< - "snprintf", - RetValSpec, - [ArgSpec, - ArgSpec, - ArgSpec, - ArgSpec] + "fwrite", + RetValSpec, + [ArgSpec, + ArgSpec, + ArgSpec, + ArgSpec] >, ] >; Index: libc/src/CMakeLists.txt =================================================================== --- libc/src/CMakeLists.txt +++ libc/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. Index: libc/src/stdio/CMakeLists.txt =================================================================== --- /dev/null +++ libc/src/stdio/CMakeLists.txt @@ -0,0 +1,11 @@ + +add_entrypoint_object( + fwrite + SRCS + fwrite.cpp + HDRS + fwrite.h + DEPENDS + mtx_lock + mtx_unlock +) Index: libc/src/stdio/FILE.h =================================================================== --- /dev/null +++ libc/src/stdio/FILE.h @@ -0,0 +1,29 @@ +//===-- 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 + +namespace __llvm_libc { + +// TOOD: This should return ssize_t but it's defined in sys/types.h +using cookie_write_function_t = size_t(void *, const char *, size_t); + +struct FILE { + mtx_t lock; + + void *cookie; + cookie_write_function_t *write; +}; + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_FILE_H Index: libc/src/stdio/fwrite.h =================================================================== --- /dev/null +++ libc/src/stdio/fwrite.h @@ -0,0 +1,24 @@ +//===-- Implementation header 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_STDIO_FWRITE_H +#define LLVM_LIBC_SRC_STDIO_FWRITE_H + +#include "src/stdio/FILE.h" +#include + +namespace __llvm_libc { + +size_t fwrite_unlocked(const void *__restrict ptr, size_t size, size_t nmeb, + FILE *__restrict stream); +size_t fwrite(const void *__restrict ptr, size_t size, size_t nmeb, + FILE *__restrict stream); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_FWRITE_H Index: libc/src/stdio/fwrite.cpp =================================================================== --- /dev/null +++ libc/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, + FILE *__restrict stream) { + return stream->write(stream->cookie, reinterpret_cast(ptr), + size * nmeb); +} + +size_t fwrite(const void *__restrict ptr, size_t size, size_t nmeb, + 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 Index: libc/test/config/linux/x86_64/syscall_test.cpp =================================================================== --- libc/test/config/linux/x86_64/syscall_test.cpp +++ libc/test/config/linux/x86_64/syscall_test.cpp @@ -9,36 +9,45 @@ #include "config/linux/syscall.h" #include "utils/UnitTest/Test.h" -#include +namespace __llvm_libc { + +template struct function; + +template struct function { + Ret (*func)(Params...); + + template function(Func &&f) : func(f) {} +}; + +} // namespace __llvm_libc TEST(X86_64_SyscallTest, APITest) { // We only do a signature test here. Actual functionality tests are // done by the unit tests of the syscall wrappers like mmap. - std::function f([](long n) { return __llvm_libc::syscall(n); }); - std::function f1( + __llvm_libc::function f( + [](long n) { return __llvm_libc::syscall(n); }); + __llvm_libc::function f1( [](long n, long a1) { return __llvm_libc::syscall(n, a1); }); - std::function f2( + __llvm_libc::function f2( [](long n, long a1, long a2) { return __llvm_libc::syscall(n, a1, a2); }); - std::function f3( + __llvm_libc::function f3( [](long n, long a1, long a2, long a3) { return __llvm_libc::syscall(n, a1, a2, a3); }); - std::function f4( + __llvm_libc::function f4( [](long n, long a1, long a2, long a3, long a4) { return __llvm_libc::syscall(n, a1, a2, a3, a4); }); - std::function f5( + __llvm_libc::function f5( [](long n, long a1, long a2, long a3, long a4, long a5) { return __llvm_libc::syscall(n, a1, a2, a3, a4, a5); }); - std::function f6( + __llvm_libc::function f6( [](long n, long a1, long a2, long a3, long a4, long a5, long a6) { return __llvm_libc::syscall(n, a1, a2, a3, a4, a5, a6); }); - std::function notLongType( - [](long n, void *a1) { - return __llvm_libc::syscall(n, a1); - }); + __llvm_libc::function notLongType( + [](long n, void *a1) { return __llvm_libc::syscall(n, a1); }); } Index: libc/test/src/CMakeLists.txt =================================================================== --- libc/test/src/CMakeLists.txt +++ libc/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) Index: libc/test/src/stdio/CMakeLists.txt =================================================================== --- /dev/null +++ libc/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 + fwrite + # TODO(sivachandra): remove private dependencies of fwrite + mtx_lock + mtx_unlock +) Index: libc/test/src/stdio/fwrite_test.cpp =================================================================== --- /dev/null +++ libc/test/src/stdio/fwrite_test.cpp @@ -0,0 +1,26 @@ +//===-- 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) { + __llvm_libc::FILE f; + char array[6]; + f.cookie = array; + f.write = +[](void *cookie, const char *ptr, size_t size) { + char *buf = reinterpret_cast(cookie); + for (size_t i = 0; i < size; ++i) + buf[i] = ptr[i]; + return size; + }; + EXPECT_EQ(fwrite("hello", 1, 6, &f), 6UL); + EXPECT_STREQ(array, "hello"); +}