diff --git a/libc/config/gpu/entrypoints.txt b/libc/config/gpu/entrypoints.txt --- a/libc/config/gpu/entrypoints.txt +++ b/libc/config/gpu/entrypoints.txt @@ -83,9 +83,6 @@ # stdio.h entrypoints libc.src.stdio.puts libc.src.stdio.fputs - libc.src.stdio.fread - libc.src.stdio.fclose - libc.src.stdio.fopen libc.src.stdio.stdin libc.src.stdio.stdout libc.src.stdio.stderr diff --git a/libc/docs/gpu/support.rst b/libc/docs/gpu/support.rst --- a/libc/docs/gpu/support.rst +++ b/libc/docs/gpu/support.rst @@ -124,6 +124,6 @@ ============= ========= ============ puts |check| |check| fputs |check| |check| -fclose |check| |check| -fopen |check| |check| +fclose |check| +fopen |check| ============= ========= ============ diff --git a/libc/src/__support/File/CMakeLists.txt b/libc/src/__support/File/CMakeLists.txt --- a/libc/src/__support/File/CMakeLists.txt +++ b/libc/src/__support/File/CMakeLists.txt @@ -1,4 +1,5 @@ -if(NOT (TARGET libc.src.__support.threads.mutex)) +if(NOT (TARGET libc.src.__support.threads.mutex) + OR LIBC_TARGET_ARCHITECTURE_IS_GPU) # Not all platforms have a mutex implementation. If mutex is unvailable, # we just skip everything about files. return() diff --git a/libc/src/__support/File/file.h b/libc/src/__support/File/file.h --- a/libc/src/__support/File/file.h +++ b/libc/src/__support/File/file.h @@ -38,15 +38,6 @@ public: static constexpr size_t DEFAULT_BUFFER_SIZE = 1024; -// Some platforms like the GPU build cannot support buffering due to extra -// resource usage or hardware constraints. This function allows us to optimize -// out the buffering portions of the code in the general implementation. -#if defined(LIBC_TARGET_ARCH_IS_GPU) - static constexpr bool ENABLE_BUFFER = false; -#else - static constexpr bool ENABLE_BUFFER = true; -#endif - using LockFunc = void(File *); using UnlockFunc = void(File *); @@ -167,8 +158,7 @@ buf(buffer), bufsize(buffer_size), bufmode(buffer_mode), own_buf(owned), mode(modeflags), pos(0), prev_op(FileOp::NONE), read_limit(0), eof(false), err(false) { - if constexpr (ENABLE_BUFFER) - adjust_buf(); + adjust_buf(); } // Buffered write of |len| bytes from |data| without the file lock. diff --git a/libc/src/__support/File/file.cpp b/libc/src/__support/File/file.cpp --- a/libc/src/__support/File/file.cpp +++ b/libc/src/__support/File/file.cpp @@ -25,7 +25,7 @@ prev_op = FileOp::WRITE; - if (!ENABLE_BUFFER || bufmode == _IONBF) { // unbuffered. + if (bufmode == _IONBF) { // unbuffered. size_t ret_val = write_unlocked_nbf(static_cast(data), len); flush_unlocked(); @@ -38,7 +38,7 @@ } FileIOResult File::write_unlocked_nbf(const uint8_t *data, size_t len) { - if (ENABLE_BUFFER && pos > 0) { // If the buffer is not empty + if (pos > 0) { // If the buffer is not empty // Flush the buffer const size_t write_size = pos; auto write_result = platform_write(this, buf, write_size); @@ -325,9 +325,6 @@ } int File::flush_unlocked() { - if constexpr (!ENABLE_BUFFER) - return 0; - if (prev_op == FileOp::WRITE && pos > 0) { auto buf_result = platform_write(this, buf, pos); if (buf_result.has_error() || buf_result.value < pos) { @@ -341,9 +338,6 @@ } int File::set_buffer(void *buffer, size_t size, int buffer_mode) { - if constexpr (!ENABLE_BUFFER) - return EINVAL; - // We do not need to lock the file as this method should be called before // other operations are performed on the file. if (buffer != nullptr && size == 0) diff --git a/libc/src/__support/File/gpu/CMakeLists.txt b/libc/src/__support/File/gpu/CMakeLists.txt deleted file mode 100644 --- a/libc/src/__support/File/gpu/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -add_object_library( - gpu_file - SRCS - file.cpp - DEPENDS - libc.include.stdio - libc.src.errno.errno - libc.src.__support.CPP.new - libc.src.__support.error_or - libc.src.__support.File.file -) - -add_object_library( - gpu_dir - SRCS - dir.cpp - DEPENDS - libc.src.errno.errno - libc.src.__support.error_or - libc.src.__support.File.dir -) diff --git a/libc/src/__support/File/gpu/file.cpp b/libc/src/__support/File/gpu/file.cpp deleted file mode 100644 --- a/libc/src/__support/File/gpu/file.cpp +++ /dev/null @@ -1,183 +0,0 @@ -//===--- GPU specialization of the File data structure --------------------===// -// -// 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/__support/File/file.h" - -#include "src/__support/RPC/rpc_client.h" -#include "src/errno/libc_errno.h" // For error macros -#include "src/string/string_utils.h" - -#include - -namespace __llvm_libc { - -namespace { - -FileIOResult write_func(File *, const void *, size_t); -FileIOResult read_func(File *, void *, size_t); -int close_func(File *); - -} // namespace - -class GPUFile : public File { - uintptr_t file; - -public: - constexpr GPUFile(uintptr_t file, File::ModeFlags modeflags) - : File(&write_func, &read_func, nullptr, &close_func, nullptr, 0, _IONBF, - false, modeflags), - file(file) {} - - uintptr_t get_file() const { return file; } -}; - -namespace { - -int write_to_stdout(const void *data, size_t size) { - uint64_t ret = 0; - rpc::Client::Port port = rpc::client.open(); - port.send_n(data, size); - port.recv([&](rpc::Buffer *buffer) { - ret = reinterpret_cast(buffer->data)[0]; - }); - port.close(); - return ret; -} - -int write_to_stderr(const void *data, size_t size) { - uint64_t ret = 0; - rpc::Client::Port port = rpc::client.open(); - port.send_n(data, size); - port.recv([&](rpc::Buffer *buffer) { - ret = reinterpret_cast(buffer->data)[0]; - }); - port.close(); - return ret; -} - -int write_to_stream(uintptr_t file, const void *data, size_t size) { - uint64_t ret = 0; - rpc::Client::Port port = rpc::client.open(); - port.send([&](rpc::Buffer *buffer) { - reinterpret_cast(buffer->data)[0] = file; - }); - port.send_n(data, size); - port.recv([&](rpc::Buffer *buffer) { - ret = reinterpret_cast(buffer->data)[0]; - }); - port.close(); - return ret; -} - -FileIOResult write_func(File *f, const void *data, size_t size) { - auto *gpu_file = reinterpret_cast(f); - int ret = 0; - if (gpu_file == stdout) - ret = write_to_stdout(data, size); - else if (gpu_file == stderr) - ret = write_to_stderr(data, size); - else - ret = write_to_stream(gpu_file->get_file(), data, size); - if (ret < 0) - return {0, -ret}; - return ret; -} - -int read_from_stdin(void *buf, size_t size) { - int ret = 0; - uint64_t recv_size; - rpc::Client::Port port = rpc::client.open(); - port.send([=](rpc::Buffer *buffer) { buffer->data[0] = size; }); - port.recv_n(&buf, &recv_size, [&](uint64_t) { return buf; }); - port.recv([&](rpc::Buffer *buffer) { ret = buffer->data[0]; }); - port.close(); - return ret; -} - -int read_from_stream(uintptr_t file, void *buf, size_t size) { - int ret = 0; - uint64_t recv_size; - // TODO: For large sizes being written to a pointer in global memory, we - // should be able to initiate a H2D memcpy via a separate RPC call at high - // bandwidth. - rpc::Client::Port port = rpc::client.open(); - port.send([=](rpc::Buffer *buffer) { - buffer->data[0] = size; - buffer->data[1] = file; - }); - port.recv_n(&buf, &recv_size, [&](uint64_t) { return buf; }); - port.recv([&](rpc::Buffer *buffer) { ret = buffer->data[0]; }); - port.close(); - return ret; -} - -FileIOResult read_func(File *f, void *buf, size_t size) { - auto *gpu_file = reinterpret_cast(f); - int ret = 0; - if (gpu_file == stdin) - ret = read_from_stdin(buf, size); - else - ret = read_from_stream(gpu_file->get_file(), buf, size); - if (ret < 0) - return {0, -ret}; - return ret; -} - -int close_func(File *file) { - int ret = 0; - GPUFile *gpu_file = reinterpret_cast(file); - rpc::Client::Port port = rpc::client.open(); - port.send_and_recv( - [=](rpc::Buffer *buffer) { buffer->data[0] = gpu_file->get_file(); }, - [&](rpc::Buffer *buffer) { ret = buffer->data[0]; }); - port.close(); - - return ret; -} - -} // namespace - -void *ptr; - -ErrorOr openfile(const char *path, const char *mode) { - auto modeflags = File::mode_flags(mode); - if (modeflags == 0) - return Error(EINVAL); - - uintptr_t file; - rpc::Client::Port port = rpc::client.open(); - port.send_n(path, internal::string_length(path) + 1); - port.send_and_recv( - [=](rpc::Buffer *buffer) { - inline_memcpy(buffer->data, mode, internal::string_length(mode) + 1); - }, - [&](rpc::Buffer *buffer) { file = buffer->data[0]; }); - port.close(); - - static GPUFile gpu_file(0, 0); - gpu_file = GPUFile(file, modeflags); - return &gpu_file; -} - -static GPUFile StdIn(0UL, File::ModeFlags(File::OpenMode::READ)); -File *stdin = &StdIn; - -static GPUFile StdOut(0UL, File::ModeFlags(File::OpenMode::APPEND)); -File *stdout = &StdOut; - -static GPUFile StdErr(0UL, File::ModeFlags(File::OpenMode::APPEND)); -File *stderr = &StdErr; - -} // namespace __llvm_libc - -// Provide the external defintitions of the standard IO streams. -extern "C" { -FILE *stdin = reinterpret_cast(&__llvm_libc::StdIn); -FILE *stderr = reinterpret_cast(&__llvm_libc::StdErr); -FILE *stdout = reinterpret_cast(&__llvm_libc::StdOut); -} diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt --- a/libc/src/stdio/CMakeLists.txt +++ b/libc/src/stdio/CMakeLists.txt @@ -1,3 +1,23 @@ +# Helper function that creates an alias if a target specific implementation +# exists, otherwise it uses a generic one. +function(add_stdio_entrypoint_object name) + if(TARGET libc.src.stdio.${LIBC_TARGET_OS}.${name}) + add_entrypoint_object( + ${name} + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.${name} + ) + elseif(TARGET libc.src.stdio.generic_${name}) + add_entrypoint_object( + ${name} + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.${name} + ) + endif() +endfunction(add_stdio_entrypoint_object) + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) endif() @@ -322,7 +342,7 @@ ) add_entrypoint_object( - fputs + generic_fputs SRCS fputs.cpp HDRS @@ -336,7 +356,7 @@ add_entrypoint_object( - puts + generic_puts SRCS puts.cpp HDRS @@ -386,7 +406,7 @@ ) add_entrypoint_object( - stdin + generic_stdin SRCS stdin.cpp HDRS @@ -398,7 +418,7 @@ ) add_entrypoint_object( - stdout + generic_stdout SRCS stdout.cpp HDRS @@ -410,7 +430,7 @@ ) add_entrypoint_object( - stderr + generic_stderr SRCS stderr.cpp HDRS @@ -608,3 +628,10 @@ DEPENDS .${LIBC_TARGET_OS}.remove ) + +# These entrypoints have multiple potential implementations. +add_stdio_entrypoint_object(puts) +add_stdio_entrypoint_object(fputs) +add_stdio_entrypoint_object(stdin) +add_stdio_entrypoint_object(stdout) +add_stdio_entrypoint_object(stderr) diff --git a/libc/src/stdio/gpu/CMakeLists.txt b/libc/src/stdio/gpu/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/src/stdio/gpu/CMakeLists.txt @@ -0,0 +1,61 @@ +add_header_library( + gpu_file + HDRS + file.h + DEPENDS + libc.src.__support.common + libc.src.__support.CPP.string_view + libc.src.__support.RPC.rpc_client +) + +add_entrypoint_object( + puts + SRCS + puts.cpp + HDRS + ../puts.h + DEPENDS + libc.include.stdio + .gpu_file +) + +add_entrypoint_object( + fputs + SRCS + fputs.cpp + HDRS + ../fputs.h + DEPENDS + libc.include.stdio + .gpu_file +) + +add_entrypoint_object( + stdin + SRCS + stdin.cpp + HDRS + ../stdin.h + DEPENDS + libc.include.stdio +) + +add_entrypoint_object( + stdout + SRCS + stdout.cpp + HDRS + ../stdout.h + DEPENDS + libc.include.stdio +) + +add_entrypoint_object( + stderr + SRCS + stderr.cpp + HDRS + ../stderr.h + DEPENDS + libc.include.stdio +) diff --git a/libc/src/stdio/gpu/file.h b/libc/src/stdio/gpu/file.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/gpu/file.h @@ -0,0 +1,89 @@ +//===--- GPU helper functions--------------------===// +// +// 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/__support/RPC/rpc_client.h" +#include "src/string/string_utils.h" + +#include + +namespace __llvm_libc { +namespace file { + +LIBC_INLINE uint64_t write_to_stdout(const void *data, size_t size) { + uint64_t ret = 0; + rpc::Client::Port port = rpc::client.open(); + port.send_n(data, size); + port.recv([&](rpc::Buffer *buffer) { + ret = reinterpret_cast(buffer->data)[0]; + }); + port.close(); + return ret; +} + +LIBC_INLINE uint64_t write_to_stderr(const void *data, size_t size) { + uint64_t ret = 0; + rpc::Client::Port port = rpc::client.open(); + port.send_n(data, size); + port.recv([&](rpc::Buffer *buffer) { + ret = reinterpret_cast(buffer->data)[0]; + }); + port.close(); + return ret; +} + +LIBC_INLINE uint64_t write_to_stream(uintptr_t file, const void *data, + size_t size) { + uint64_t ret = 0; + rpc::Client::Port port = rpc::client.open(); + port.send([&](rpc::Buffer *buffer) { + reinterpret_cast(buffer->data)[0] = file; + }); + port.send_n(data, size); + port.recv([&](rpc::Buffer *buffer) { + ret = reinterpret_cast(buffer->data)[0]; + }); + port.close(); + return ret; +} + +LIBC_INLINE uint64_t write(FILE *f, const void *data, size_t size) { + if (f == stdout) + return write_to_stdout(data, size); + else if (f == stderr) + return write_to_stderr(data, size); + else + return write_to_stream(reinterpret_cast(f), data, size); +} + +LIBC_INLINE uint64_t read_from_stdin(void *buf, size_t size) { + uint64_t ret = 0; + uint64_t recv_size; + rpc::Client::Port port = rpc::client.open(); + port.send([=](rpc::Buffer *buffer) { buffer->data[0] = size; }); + port.recv_n(&buf, &recv_size, [&](uint64_t) { return buf; }); + port.recv([&](rpc::Buffer *buffer) { ret = buffer->data[0]; }); + port.close(); + return ret; +} + +LIBC_INLINE uint64_t read_from_stream(uintptr_t file, void *buf, size_t size) { + uint64_t ret = 0; + uint64_t recv_size; + rpc::Client::Port port = rpc::client.open(); + port.send([=](rpc::Buffer *buffer) { + buffer->data[0] = size; + buffer->data[1] = file; + }); + port.recv_n(&buf, &recv_size, [&](uint64_t) { return buf; }); + port.recv([&](rpc::Buffer *buffer) { ret = buffer->data[0]; }); + port.close(); + return ret; +} + +} // namespace file +} // namespace __llvm_libc diff --git a/libc/src/stdio/gpu/fputs.cpp b/libc/src/stdio/gpu/fputs.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/gpu/fputs.cpp @@ -0,0 +1,27 @@ +//===-- GPU Implementation of fputs ---------------------------------------===// +// +// 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/fputs.h" +#include "src/__support/CPP/string_view.h" +#include "src/errno/libc_errno.h" +#include "src/stdio/gpu/file.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, fputs, + (const char *__restrict str, ::FILE *__restrict stream)) { + cpp::string_view str_view(str); + auto written = file::write(stream, str, str_view.size()); + if (written != str_view.size()) + return EOF; + return 0; +} + +} // namespace __llvm_libc diff --git a/libc/src/stdio/gpu/puts.cpp b/libc/src/stdio/gpu/puts.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/gpu/puts.cpp @@ -0,0 +1,29 @@ +//===-- GPU Implementation of puts ----------------------------------------===// +// +// 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/puts.h" +#include "src/__support/CPP/string_view.h" +#include "src/errno/libc_errno.h" +#include "src/stdio/gpu/file.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, puts, (const char *__restrict str)) { + cpp::string_view str_view(str); + auto written = file::write(stdout, str, str_view.size()); + if (written != str_view.size()) + return EOF; + written = file::write(stdout, "\n", 1); + if (written != 1) + return EOF; + return 0; +} + +} // namespace __llvm_libc diff --git a/libc/src/__support/File/gpu/dir.cpp b/libc/src/stdio/gpu/stderr.cpp rename from libc/src/__support/File/gpu/dir.cpp rename to libc/src/stdio/gpu/stderr.cpp --- a/libc/src/__support/File/gpu/dir.cpp +++ b/libc/src/stdio/gpu/stderr.cpp @@ -1,4 +1,4 @@ -//===--- GPU implementation of the Dir helpers ----------------------------===// +//===-- Definition of the global stderr object ----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,8 +6,11 @@ // //===----------------------------------------------------------------------===// -#include "src/__support/File/dir.h" +#include -#include "src/__support/error_or.h" - -namespace __llvm_libc {} // namespace __llvm_libc +namespace __llvm_libc { +static struct { +} stub; +FILE *stderr = reinterpret_cast(&stub); +} // namespace __llvm_libc +extern "C" FILE *stderr = reinterpret_cast(&__llvm_libc::stub); diff --git a/libc/src/__support/File/gpu/dir.cpp b/libc/src/stdio/gpu/stdin.cpp copy from libc/src/__support/File/gpu/dir.cpp copy to libc/src/stdio/gpu/stdin.cpp --- a/libc/src/__support/File/gpu/dir.cpp +++ b/libc/src/stdio/gpu/stdin.cpp @@ -1,4 +1,4 @@ -//===--- GPU implementation of the Dir helpers ----------------------------===// +//===-- Definition of the global stdin object -----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,8 +6,11 @@ // //===----------------------------------------------------------------------===// -#include "src/__support/File/dir.h" +#include -#include "src/__support/error_or.h" - -namespace __llvm_libc {} // namespace __llvm_libc +namespace __llvm_libc { +static struct { +} stub; +FILE *stdin = reinterpret_cast(&stub); +} // namespace __llvm_libc +extern "C" FILE *stdin = reinterpret_cast(&__llvm_libc::stub); diff --git a/libc/src/__support/File/gpu/dir.cpp b/libc/src/stdio/gpu/stdout.cpp rename from libc/src/__support/File/gpu/dir.cpp rename to libc/src/stdio/gpu/stdout.cpp --- a/libc/src/__support/File/gpu/dir.cpp +++ b/libc/src/stdio/gpu/stdout.cpp @@ -1,4 +1,4 @@ -//===--- GPU implementation of the Dir helpers ----------------------------===// +//===-- Definition of the global stdout object ----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,8 +6,11 @@ // //===----------------------------------------------------------------------===// -#include "src/__support/File/dir.h" +#include -#include "src/__support/error_or.h" - -namespace __llvm_libc {} // namespace __llvm_libc +namespace __llvm_libc { +static struct { +} stub; +FILE *stdout = reinterpret_cast(&stub); +} // namespace __llvm_libc +extern "C" FILE *stdout = reinterpret_cast(&__llvm_libc::stub); diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt --- a/libc/test/src/stdio/CMakeLists.txt +++ b/libc/test/src/stdio/CMakeLists.txt @@ -260,6 +260,8 @@ fputs_test.cpp DEPENDS libc.src.stdio.fputs + libc.src.stdio.stdout + libc.src.stdio.stderr ) add_libc_test(