diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -111,6 +111,8 @@ libc.src.stdio.remove libc.src.stdio.sprintf libc.src.stdio.snprintf + libc.src.stdio.fprintf + libc.src.stdio.printf # sys/mman.h entrypoints libc.src.sys.mman.madvise @@ -409,10 +411,8 @@ libc.src.stdio.funlockfile libc.src.stdio.fwrite libc.src.stdio.fwrite_unlocked - libc.src.stdio.fprintf libc.src.stdio.getc libc.src.stdio.getc_unlocked - libc.src.stdio.printf libc.src.stdio.sscanf libc.src.stdio.scanf libc.src.stdio.fscanf 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 @@ -491,7 +491,8 @@ libc.src.stdio.printf_core.vfprintf_internal ) - +# In fullbuild mode, printf uses the internal stdout. +if(LLVM_LIBC_FULL_BUILD) add_entrypoint_object( printf SRCS @@ -504,6 +505,21 @@ libc.src.__support.arg_list libc.src.stdio.printf_core.vfprintf_internal ) +else() +# In overlay mode, printf uses the system's stdout. +add_entrypoint_object( + printf + SRCS + printf.cpp + HDRS + printf.h + DEPENDS + libc.src.__support.arg_list + libc.src.stdio.printf_core.vfprintf_internal + COMPILE_OPTIONS + -DLIBC_COPT_PRINTF_USE_PUBLIC_FILE +) +endif() add_entrypoint_object( ftell diff --git a/libc/src/stdio/printf.cpp b/libc/src/stdio/printf.cpp --- a/libc/src/stdio/printf.cpp +++ b/libc/src/stdio/printf.cpp @@ -12,6 +12,10 @@ #include "src/__support/arg_list.h" #include "src/stdio/printf_core/vfprintf_internal.h" +#ifdef LIBC_COPT_PRINTF_USE_PUBLIC_FILE +#include +#endif + #include namespace __llvm_libc { @@ -23,8 +27,12 @@ // and pointer semantics, as well as handling // destruction automatically. va_end(vlist); +#ifndef LIBC_COPT_PRINTF_USE_PUBLIC_FILE int ret_val = printf_core::vfprintf_internal( reinterpret_cast<::FILE *>(__llvm_libc::stdout), format, args); +#else // LIBC_COPT_PRINTF_USE_PUBLIC_FILE + int ret_val = printf_core::vfprintf_internal(::stdout, format, args); +#endif // LIBC_COPT_PRINTF_USE_PUBLIC_FILE return ret_val; } diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt --- a/libc/src/stdio/printf_core/CMakeLists.txt +++ b/libc/src/stdio/printf_core/CMakeLists.txt @@ -112,12 +112,15 @@ libc.src.__support.arg_list ) -if(NOT (TARGET libc.src.__support.File.file)) - # Not all platforms have a file implementation. If file is unvailable, - # then we must skip all file based printf sections. +if(NOT (TARGET libc.src.__support.File.file) AND LLVM_LIBC_FULL_BUILD) + # Not all platforms have a file implementation. If file is unvailable, and a + # full build is requested, then we must skip all file based printf sections. return() endif() +# In full build mode, the file writer will talk directly to the internal file +# implementation to improve performance. +if(LLVM_LIBC_FULL_BUILD) add_object_library( file_writer SRCS @@ -146,3 +149,36 @@ libc.src.stdio.printf_core.file_writer libc.src.stdio.printf_core.writer ) +else() +# Else we're in overlay mode so we use the system's FILE. This will require a +# lock on every write, which is slower. +add_object_library( + file_writer + SRCS + file_writer.cpp + HDRS + file_writer.h + DEPENDS + libc.src.__support.CPP.string_view + libc.src.string.memory_utils.memset_implementation + .core_structs + COMPILE_OPTIONS + -DLIBC_COPT_PRINTF_USE_PUBLIC_FILE +) + +add_object_library( + vfprintf_internal + SRCS + vfprintf_internal.cpp + HDRS + vfprintf_internal.h + DEPENDS + libc.include.stdio + libc.src.__support.arg_list + libc.src.stdio.printf_core.printf_main + libc.src.stdio.printf_core.file_writer + libc.src.stdio.printf_core.writer + COMPILE_OPTIONS + -DLIBC_COPT_PRINTF_USE_PUBLIC_FILE +) +endif() diff --git a/libc/src/stdio/printf_core/file_writer.h b/libc/src/stdio/printf_core/file_writer.h --- a/libc/src/stdio/printf_core/file_writer.h +++ b/libc/src/stdio/printf_core/file_writer.h @@ -18,6 +18,7 @@ namespace __llvm_libc { namespace printf_core { +#ifndef LIBC_COPT_PRINTF_USE_PUBLIC_FILE class FileWriter { __llvm_libc::File *file; @@ -37,6 +38,24 @@ static int write_chars(void *raw_pointer, char new_char, size_t len); static int write_char(void *raw_pointer, char new_char); }; +#else // defined(LIBC_COPT_PRINTF_USE_PUBLIC_FILE) +class FileWriter { + ::FILE *file; + +public: + FileWriter(::FILE *init_file) { file = init_file; } + + ~FileWriter() = default; + + int write(const char *__restrict to_write, size_t len); + + // These write functions take a FileWriter as a void* in raw_pointer, and + // call the appropriate write function on it. + static int write_str(void *raw_pointer, cpp::string_view new_string); + static int write_chars(void *raw_pointer, char new_char, size_t len); + static int write_char(void *raw_pointer, char new_char); +}; +#endif // LIBC_COPT_PRINTF_USE_PUBLIC_FILE } // namespace printf_core } // namespace __llvm_libc diff --git a/libc/src/stdio/printf_core/file_writer.cpp b/libc/src/stdio/printf_core/file_writer.cpp --- a/libc/src/stdio/printf_core/file_writer.cpp +++ b/libc/src/stdio/printf_core/file_writer.cpp @@ -12,19 +12,34 @@ #include "src/stdio/printf_core/core_structs.h" #include +#ifdef LIBC_COPT_PRINTF_USE_PUBLIC_FILE +#include +#endif // LIBC_COPT_PRINTF_USE_PUBLIC_FILE + namespace __llvm_libc { namespace printf_core { +#ifndef LIBC_COPT_PRINTF_USE_PUBLIC_FILE int FileWriter::write(const char *__restrict to_write, size_t len) { auto result = file->write_unlocked(to_write, len); - int written = result.value; - if (written != static_cast(len) || result.has_error()) + size_t written = result.value; + if (written != len || result.has_error()) written = FILE_WRITE_ERROR; if (file->error_unlocked()) written = FILE_STATUS_ERROR; return written; } +#else // defined(LIBC_COPT_PRINTF_USE_PUBLIC_FILE) +int FileWriter::write(const char *__restrict to_write, size_t len) { + size_t written = ::fwrite(to_write, 1, len, file); + if (written != len || ::ferror(file)) + written = FILE_WRITE_ERROR; + return written; +} + +#endif // LIBC_COPT_PRINTF_USE_PUBLIC_FILE + int FileWriter::write_str(void *raw_pointer, cpp::string_view new_string) { FileWriter *file_writer = reinterpret_cast(raw_pointer); return file_writer->write(new_string.data(), new_string.size()); 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 @@ -134,6 +134,8 @@ libc.src.stdio.snprintf ) +# In fullbuild mode, fprintf's tests use the internal FILE for other functions. +if(LLVM_LIBC_FULL_BUILD) add_libc_unittest( fprintf_test SUITE @@ -147,7 +149,20 @@ libc.src.stdio.fopen libc.src.stdio.fread ) - +else() +# Else in overlay mode they use the system's FILE. +add_libc_unittest( + fprintf_test + SUITE + libc_stdio_unittests + SRCS + fprintf_test.cpp + DEPENDS + libc.src.stdio.fprintf + COMPILE_OPTIONS + -DLIBC_COPT_PRINTF_USE_PUBLIC_FILE +) +endif() add_libc_unittest( printf_test diff --git a/libc/test/src/stdio/fprintf_test.cpp b/libc/test/src/stdio/fprintf_test.cpp --- a/libc/test/src/stdio/fprintf_test.cpp +++ b/libc/test/src/stdio/fprintf_test.cpp @@ -6,10 +6,12 @@ // //===----------------------------------------------------------------------===// +#ifndef LIBC_COPT_PRINTF_USE_PUBLIC_FILE #include "src/stdio/fclose.h" #include "src/stdio/ferror.h" #include "src/stdio/fopen.h" #include "src/stdio/fread.h" +#endif // LIBC_COPT_PRINTF_USE_PUBLIC_FILE #include "src/stdio/fprintf.h" @@ -17,9 +19,23 @@ #include +namespace file_test { +#ifndef LIBC_COPT_PRINTF_USE_PUBLIC_FILE +using __llvm_libc::fclose; +using __llvm_libc::ferror; +using __llvm_libc::fopen; +using __llvm_libc::fread; +#else // defined(LIBC_COPT_PRINTF_USE_PUBLIC_FILE) +using ::fclose; +using ::ferror; +using ::fopen; +using ::fread; +#endif // LIBC_COPT_PRINTF_USE_PUBLIC_FILE +} // namespace file_test + TEST(LlvmLibcFPrintfTest, WriteToFile) { constexpr char FILENAME[] = "testdata/fprintf_output.test"; - ::FILE *file = __llvm_libc::fopen(FILENAME, "w"); + ::FILE *file = file_test::fopen(FILENAME, "w"); ASSERT_FALSE(file == nullptr); int written; @@ -37,31 +53,31 @@ written = __llvm_libc::fprintf(file, format_more, short_numbers); EXPECT_EQ(written, 14); - ASSERT_EQ(0, __llvm_libc::fclose(file)); + ASSERT_EQ(0, file_test::fclose(file)); - file = __llvm_libc::fopen(FILENAME, "r"); + file = file_test::fopen(FILENAME, "r"); ASSERT_FALSE(file == nullptr); char data[50]; - ASSERT_EQ(__llvm_libc::fread(data, 1, sizeof(simple) - 1, file), + ASSERT_EQ(file_test::fread(data, 1, sizeof(simple) - 1, file), sizeof(simple) - 1); data[sizeof(simple) - 1] = '\0'; ASSERT_STREQ(data, simple); - ASSERT_EQ(__llvm_libc::fread(data, 1, sizeof(numbers) - 1, file), + ASSERT_EQ(file_test::fread(data, 1, sizeof(numbers) - 1, file), sizeof(numbers) - 1); data[sizeof(numbers) - 1] = '\0'; ASSERT_STREQ(data, numbers); - ASSERT_EQ(__llvm_libc::fread( + ASSERT_EQ(file_test::fread( data, 1, sizeof(format_more) + sizeof(short_numbers) - 4, file), sizeof(format_more) + sizeof(short_numbers) - 4); data[sizeof(format_more) + sizeof(short_numbers) - 4] = '\0'; ASSERT_STREQ(data, "1234 and more\n"); - ASSERT_EQ(__llvm_libc::ferror(file), 0); + ASSERT_EQ(file_test::ferror(file), 0); written = __llvm_libc::fprintf(file, "Writing to a read only file should fail."); EXPECT_LT(written, 0); - ASSERT_EQ(__llvm_libc::fclose(file), 0); + ASSERT_EQ(file_test::fclose(file), 0); }