[libc-commits] [libc] 16d5c24 - [libc] Add v variants of printf functions

Michael Jones via libc-commits libc-commits at lists.llvm.org
Fri Aug 4 14:50:29 PDT 2023


Author: Michael Jones
Date: 2023-08-04T14:50:24-07:00
New Revision: 16d5c242261832a4bff3de482bd16bd03389fb6d

URL: https://github.com/llvm/llvm-project/commit/16d5c242261832a4bff3de482bd16bd03389fb6d
DIFF: https://github.com/llvm/llvm-project/commit/16d5c242261832a4bff3de482bd16bd03389fb6d.diff

LOG: [libc] Add v variants of printf functions

The v variants of the printf functions take their variadic arguments as
a va_list instead of as individual arguments. They are otherwise
identical to the corresponding printf variants. This patch adds them
(vprintf, vfprintf, vsprintf, and vsnprintf) as well as tests.

Reviewed By: phosek

Differential Revision: https://reviews.llvm.org/D157138

Added: 
    libc/src/stdio/vfprintf.cpp
    libc/src/stdio/vfprintf.h
    libc/src/stdio/vprintf.cpp
    libc/src/stdio/vprintf.h
    libc/src/stdio/vsnprintf.cpp
    libc/src/stdio/vsnprintf.h
    libc/src/stdio/vsprintf.cpp
    libc/src/stdio/vsprintf.h
    libc/test/src/stdio/vfprintf_test.cpp
    libc/test/src/stdio/vprintf_test.cpp
    libc/test/src/stdio/vsnprintf_test.cpp
    libc/test/src/stdio/vsprintf_test.cpp

Modified: 
    libc/config/linux/aarch64/entrypoints.txt
    libc/config/linux/riscv64/entrypoints.txt
    libc/config/linux/x86_64/entrypoints.txt
    libc/include/stdio.h.def
    libc/spec/spec.td
    libc/spec/stdc.td
    libc/src/stdio/CMakeLists.txt
    libc/test/src/stdio/CMakeLists.txt
    utils/bazel/llvm-project-overlay/libc/BUILD.bazel
    utils/bazel/llvm-project-overlay/libc/test/src/stdio/BUILD.bazel

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index f807d0745bfc1d..46676df5c2d292 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -124,6 +124,8 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.remove
     libc.src.stdio.sprintf
     libc.src.stdio.snprintf
+    libc.src.stdio.vsprintf
+    libc.src.stdio.vsnprintf
 
     # sys/mman.h entrypoints
     libc.src.sys.mman.madvise
@@ -405,10 +407,13 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.stdio.funlockfile
     libc.src.stdio.fwrite
     libc.src.stdio.fwrite_unlocked
-    libc.src.stdio.fprintf
     libc.src.stdio.getchar
     libc.src.stdio.getchar_unlocked
+    #TODO: Look into if fprintf can be enabled for overlay on aarch64
+    libc.src.stdio.fprintf
     libc.src.stdio.printf
+    libc.src.stdio.vfprintf
+    libc.src.stdio.vprintf
     libc.src.stdio.putc
     libc.src.stdio.putchar
     libc.src.stdio.puts

diff  --git a/libc/config/linux/riscv64/entrypoints.txt b/libc/config/linux/riscv64/entrypoints.txt
index 7884993d2c8972..d6933c909a66a8 100644
--- a/libc/config/linux/riscv64/entrypoints.txt
+++ b/libc/config/linux/riscv64/entrypoints.txt
@@ -128,6 +128,10 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.snprintf
     libc.src.stdio.fprintf
     libc.src.stdio.printf
+    libc.src.stdio.vsprintf
+    libc.src.stdio.vsnprintf
+    libc.src.stdio.vfprintf
+    libc.src.stdio.vprintf
 
     # sys/mman.h entrypoints
     libc.src.sys.mman.madvise

diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index fbef1cfda65670..28cdd8a44f1179 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -128,6 +128,10 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.snprintf
     libc.src.stdio.fprintf
     libc.src.stdio.printf
+    libc.src.stdio.vsprintf
+    libc.src.stdio.vsnprintf
+    libc.src.stdio.vfprintf
+    libc.src.stdio.vprintf
 
     # sys/mman.h entrypoints
     libc.src.sys.mman.madvise

diff  --git a/libc/include/stdio.h.def b/libc/include/stdio.h.def
index 5eb0e98809603d..a28d009288d5a6 100644
--- a/libc/include/stdio.h.def
+++ b/libc/include/stdio.h.def
@@ -13,6 +13,8 @@
 #include <llvm-libc-macros/file-seek-macros.h>
 #include <llvm-libc-macros/stdio-macros.h>
 
+#include <stdarg.h>
+
 %%public_api()
 
 #endif // LLVM_LIBC_STDIO_H

diff  --git a/libc/spec/spec.td b/libc/spec/spec.td
index 2336754c5d030a..ac0fba3bd0af78 100644
--- a/libc/spec/spec.td
+++ b/libc/spec/spec.td
@@ -37,6 +37,7 @@ class RestrictedPtrType<Type type> : Type {
 
 // Builtin types.
 def VarArgType : NamedType<"...">;
+def VaListType : NamedType<"va_list">;
 def VoidType : NamedType<"void">;
 def IntType : NamedType<"int">;
 def UnsignedIntType : NamedType<"unsigned int">;

diff  --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index cbb3015d0f0c4c..96668ccb5558b5 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -698,6 +698,34 @@ def StdC : StandardSpec<"stdc"> {
                ArgSpec<ConstCharRestrictedPtr>,
                ArgSpec<VarArgType>]
           >,
+          FunctionSpec<
+              "vsprintf",
+              RetValSpec<IntType>,
+              [ArgSpec<CharRestrictedPtr>,
+               ArgSpec<ConstCharRestrictedPtr>,
+               ArgSpec<VaListType>]
+          >,
+          FunctionSpec<
+              "vsnprintf",
+              RetValSpec<IntType>,
+              [ArgSpec<CharRestrictedPtr>,
+               ArgSpec<SizeTType>,
+               ArgSpec<ConstCharRestrictedPtr>,
+               ArgSpec<VaListType>]
+          >,
+          FunctionSpec<
+              "vprintf",
+              RetValSpec<IntType>,
+              [ArgSpec<ConstCharRestrictedPtr>,
+               ArgSpec<VaListType>]
+          >,
+          FunctionSpec<
+              "vfprintf",
+              RetValSpec<IntType>,
+              [ArgSpec<FILERestrictedPtr>,
+               ArgSpec<ConstCharRestrictedPtr>,
+               ArgSpec<VaListType>]
+          >,
           FunctionSpec<
               "ungetc",
               RetValSpec<IntType>,

diff  --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index df0933fcce510b..6cd4f2c1c5f5f9 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -542,6 +542,53 @@ add_entrypoint_object(
     ${printf_copts}
 )
 
+add_entrypoint_object(
+  vsprintf
+  SRCS
+    vsprintf.cpp
+  HDRS
+    vsprintf.h
+  DEPENDS
+    libc.src.stdio.printf_core.printf_main
+    libc.src.stdio.printf_core.writer
+)
+
+add_entrypoint_object(
+  vsnprintf
+  SRCS
+    vsnprintf.cpp
+  HDRS
+    vsnprintf.h
+  DEPENDS
+    libc.src.stdio.printf_core.printf_main
+    libc.src.stdio.printf_core.writer
+)
+
+add_entrypoint_object(
+  vprintf
+  SRCS
+    vprintf.cpp
+  HDRS
+    vprintf.h
+  DEPENDS
+    ${printf_deps}
+  COMPILE_OPTIONS
+    ${printf_copts}
+)
+
+add_entrypoint_object(
+  vfprintf
+  SRCS
+    vfprintf.cpp
+  HDRS
+    vfprintf.h
+  DEPENDS
+    libc.src.__support.arg_list
+    libc.src.stdio.printf_core.vfprintf_internal
+  COMPILE_OPTIONS
+    ${printf_copts}
+)
+
 add_entrypoint_object(
   ftell
   SRCS

diff  --git a/libc/src/stdio/vfprintf.cpp b/libc/src/stdio/vfprintf.cpp
new file mode 100644
index 00000000000000..aa56c26b46d6a2
--- /dev/null
+++ b/libc/src/stdio/vfprintf.cpp
@@ -0,0 +1,30 @@
+//===-- Implementation of vfprintf ------------------------------*- 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/vfprintf.h"
+
+#include "src/__support/File/file.h"
+#include "src/__support/arg_list.h"
+#include "src/stdio/printf_core/vfprintf_internal.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, vfprintf,
+                   (::FILE *__restrict stream, const char *__restrict format,
+                    va_list vlist)) {
+  internal::ArgList args(vlist); // This holder class allows for easier copying
+                                 // and pointer semantics, as well as handling
+                                 // destruction automatically.
+  int ret_val = printf_core::vfprintf_internal(stream, format, args);
+  return ret_val;
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/stdio/vfprintf.h b/libc/src/stdio/vfprintf.h
new file mode 100644
index 00000000000000..b4a2d5e24042c6
--- /dev/null
+++ b/libc/src/stdio/vfprintf.h
@@ -0,0 +1,22 @@
+//===-- Implementation header of vfprintf -----------------------*- 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_VFPRINTF_H
+#define LLVM_LIBC_SRC_STDIO_VFPRINTF_H
+
+#include <stdarg.h>
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+int vfprintf(::FILE *__restrict stream, const char *__restrict format,
+             va_list vlist);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDIO_VFPRINTF_H

diff  --git a/libc/src/stdio/vprintf.cpp b/libc/src/stdio/vprintf.cpp
new file mode 100644
index 00000000000000..d38bf4fecfc07d
--- /dev/null
+++ b/libc/src/stdio/vprintf.cpp
@@ -0,0 +1,36 @@
+//===-- Implementation of vprintf -------------------------------*- 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/vprintf.h"
+
+#include "src/__support/File/file.h"
+#include "src/__support/arg_list.h"
+#include "src/stdio/printf_core/vfprintf_internal.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#ifndef LIBC_COPT_PRINTF_USE_SYSTEM_FILE
+#define PRINTF_STDOUT __llvm_libc::stdout
+#else // LIBC_COPT_PRINTF_USE_SYSTEM_FILE
+#define PRINTF_STDOUT ::stdout
+#endif // LIBC_COPT_PRINTF_USE_SYSTEM_FILE
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, vprintf,
+                   (const char *__restrict format, va_list vlist)) {
+  internal::ArgList args(vlist); // This holder class allows for easier copying
+                                 // and pointer semantics, as well as handling
+                                 // destruction automatically.
+  int ret_val = printf_core::vfprintf_internal(
+      reinterpret_cast<::FILE *>(PRINTF_STDOUT), format, args);
+  return ret_val;
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/stdio/vprintf.h b/libc/src/stdio/vprintf.h
new file mode 100644
index 00000000000000..ffc5d39eaede89
--- /dev/null
+++ b/libc/src/stdio/vprintf.h
@@ -0,0 +1,21 @@
+//===-- Implementation header of vprintf ------------------------*- 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_VPRINTF_H
+#define LLVM_LIBC_SRC_STDIO_VPRINTF_H
+
+#include <stdarg.h>
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+int vprintf(const char *__restrict format, va_list vlist);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDIO_VPRINTF_H

diff  --git a/libc/src/stdio/vsnprintf.cpp b/libc/src/stdio/vsnprintf.cpp
new file mode 100644
index 00000000000000..0dc34a83fd441d
--- /dev/null
+++ b/libc/src/stdio/vsnprintf.cpp
@@ -0,0 +1,35 @@
+//===-- Implementation of vsnprintf -----------------------------*- 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/vsnprintf.h"
+
+#include "src/__support/arg_list.h"
+#include "src/stdio/printf_core/printf_main.h"
+#include "src/stdio/printf_core/writer.h"
+
+#include <stdarg.h>
+#include <stddef.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, vsnprintf,
+                   (char *__restrict buffer, size_t buffsz,
+                    const char *__restrict format, va_list vlist)) {
+  internal::ArgList args(vlist); // This holder class allows for easier copying
+                                 // and pointer semantics, as well as handling
+                                 // destruction automatically.
+  printf_core::WriteBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
+  printf_core::Writer writer(&wb);
+
+  int ret_val = printf_core::printf_main(&writer, format, args);
+  if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
+    wb.buff[wb.buff_cur] = '\0';
+  return ret_val;
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/stdio/vsnprintf.h b/libc/src/stdio/vsnprintf.h
new file mode 100644
index 00000000000000..caa381e8383ad1
--- /dev/null
+++ b/libc/src/stdio/vsnprintf.h
@@ -0,0 +1,22 @@
+//===-- Implementation header of vsnprintf ----------------------*- 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_VSNPRINTF_H
+#define LLVM_LIBC_SRC_STDIO_VSNPRINTF_H
+
+#include <stdarg.h>
+#include <stddef.h>
+
+namespace __llvm_libc {
+
+int vsnprintf(char *__restrict buffer, size_t buffsz,
+              const char *__restrict format, va_list vlist);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDIO_VSNPRINTF_H

diff  --git a/libc/src/stdio/vsprintf.cpp b/libc/src/stdio/vsprintf.cpp
new file mode 100644
index 00000000000000..cf86f92ced4c31
--- /dev/null
+++ b/libc/src/stdio/vsprintf.cpp
@@ -0,0 +1,35 @@
+//===-- Implementation of vsprintf ------------------------------*- 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/vsprintf.h"
+
+#include "src/__support/CPP/limits.h"
+#include "src/__support/arg_list.h"
+#include "src/stdio/printf_core/printf_main.h"
+#include "src/stdio/printf_core/writer.h"
+
+#include <stdarg.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, vsprintf,
+                   (char *__restrict buffer, const char *__restrict format,
+                    va_list vlist)) {
+  internal::ArgList args(vlist); // This holder class allows for easier copying
+                                 // and pointer semantics, as well as handling
+                                 // destruction automatically.
+
+  printf_core::WriteBuffer wb(buffer, cpp::numeric_limits<size_t>::max());
+  printf_core::Writer writer(&wb);
+
+  int ret_val = printf_core::printf_main(&writer, format, args);
+  wb.buff[wb.buff_cur] = '\0';
+  return ret_val;
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/stdio/vsprintf.h b/libc/src/stdio/vsprintf.h
new file mode 100644
index 00000000000000..a129a2a420c2b8
--- /dev/null
+++ b/libc/src/stdio/vsprintf.h
@@ -0,0 +1,21 @@
+//===-- Implementation header of vsprintf -----------------------*- 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_VSPRINTF_H
+#define LLVM_LIBC_SRC_STDIO_VSPRINTF_H
+
+#include <stdarg.h>
+
+namespace __llvm_libc {
+
+int vsprintf(char *__restrict buffer, const char *__restrict format,
+             va_list vlist);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDIO_VSPRINTF_H

diff  --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt
index 6e9267e7190cf9..af87aa96059bca 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -135,9 +135,6 @@ add_libc_unittest(
     libc.src.stdio.snprintf
 )
 
-list(APPEND fprintf_test_deps
-      libc.src.stdio.fprintf
-)
 if(LLVM_LIBC_FULL_BUILD)
 # In fullbuild mode, fprintf's tests use the internal FILE for other functions.
  list(APPEND fprintf_test_deps
@@ -158,6 +155,7 @@ add_libc_unittest(
   SRCS
     fprintf_test.cpp
   DEPENDS
+    libc.src.stdio.fprintf
     ${fprintf_test_deps}
   COMPILE_OPTIONS
     ${fprintf_test_copts}
@@ -173,6 +171,50 @@ add_libc_unittest(
     libc.src.stdio.printf
 )
 
+add_fp_unittest(
+  vsprintf_test
+  UNIT_TEST_ONLY
+  SUITE
+    libc_stdio_unittests
+  SRCS
+    vsprintf_test.cpp
+  DEPENDS
+    libc.src.stdio.vsprintf
+)
+
+add_libc_unittest(
+  vsnprintf_test
+  SUITE
+    libc_stdio_unittests
+  SRCS
+    vsnprintf_test.cpp
+  DEPENDS
+    libc.src.stdio.vsnprintf
+)
+
+add_libc_unittest(
+  vfprintf_test
+  SUITE
+    libc_stdio_unittests
+  SRCS
+    vfprintf_test.cpp
+  DEPENDS
+    libc.src.stdio.vfprintf
+    ${fprintf_test_deps}
+  COMPILE_OPTIONS
+    ${fprintf_test_copts}
+)
+
+add_libc_unittest(
+  vprintf_test
+  SUITE
+    libc_stdio_unittests
+  SRCS
+    vprintf_test.cpp
+  DEPENDS
+    libc.src.stdio.vprintf
+)
+
 add_libc_unittest(
   fscanf_test
   SUITE

diff  --git a/libc/test/src/stdio/vfprintf_test.cpp b/libc/test/src/stdio/vfprintf_test.cpp
new file mode 100644
index 00000000000000..d7c5eb8292e5b5
--- /dev/null
+++ b/libc/test/src/stdio/vfprintf_test.cpp
@@ -0,0 +1,97 @@
+//===-- Unittests for vfprintf --------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// These tests are copies of the non-v variants of the printf functions. This is
+// because these functions are identical in every way except for how the varargs
+// are passed.
+
+#ifndef LIBC_COPT_PRINTF_USE_SYSTEM_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_SYSTEM_FILE
+
+#include "src/stdio/vfprintf.h"
+
+#include "test/UnitTest/Test.h"
+
+#include <stdio.h>
+
+namespace printf_test {
+#ifndef LIBC_COPT_PRINTF_USE_SYSTEM_FILE
+using __llvm_libc::fclose;
+using __llvm_libc::ferror;
+using __llvm_libc::fopen;
+using __llvm_libc::fread;
+#else  // defined(LIBC_COPT_PRINTF_USE_SYSTEM_FILE)
+using ::fclose;
+using ::ferror;
+using ::fopen;
+using ::fread;
+#endif // LIBC_COPT_PRINTF_USE_SYSTEM_FILE
+} // namespace printf_test
+
+int call_vfprintf(::FILE *__restrict stream, const char *__restrict format,
+                  ...) {
+  va_list vlist;
+  va_start(vlist, format);
+  int ret = __llvm_libc::vfprintf(stream, format, vlist);
+  va_end(vlist);
+  return ret;
+}
+
+TEST(LlvmLibcVFPrintfTest, WriteToFile) {
+  const char *FILENAME = "fprintf_output.test";
+  auto FILE_PATH = libc_make_test_file_path(FILENAME);
+
+  ::FILE *file = printf_test::fopen(FILE_PATH, "w");
+  ASSERT_FALSE(file == nullptr);
+
+  int written;
+
+  constexpr char simple[] = "A simple string with no conversions.\n";
+  written = call_vfprintf(file, simple);
+  EXPECT_EQ(written, 37);
+
+  constexpr char numbers[] = "1234567890\n";
+  written = call_vfprintf(file, "%s", numbers);
+  EXPECT_EQ(written, 11);
+
+  constexpr char format_more[] = "%s and more\n";
+  constexpr char short_numbers[] = "1234";
+  written = call_vfprintf(file, format_more, short_numbers);
+  EXPECT_EQ(written, 14);
+
+  ASSERT_EQ(0, printf_test::fclose(file));
+
+  file = printf_test::fopen(FILE_PATH, "r");
+  ASSERT_FALSE(file == nullptr);
+
+  char data[50];
+  ASSERT_EQ(printf_test::fread(data, 1, sizeof(simple) - 1, file),
+            sizeof(simple) - 1);
+  data[sizeof(simple) - 1] = '\0';
+  ASSERT_STREQ(data, simple);
+  ASSERT_EQ(printf_test::fread(data, 1, sizeof(numbers) - 1, file),
+            sizeof(numbers) - 1);
+  data[sizeof(numbers) - 1] = '\0';
+  ASSERT_STREQ(data, numbers);
+  ASSERT_EQ(printf_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(printf_test::ferror(file), 0);
+
+  written = call_vfprintf(file, "Writing to a read only file should fail.");
+  EXPECT_LT(written, 0);
+
+  ASSERT_EQ(printf_test::fclose(file), 0);
+}

diff  --git a/libc/test/src/stdio/vprintf_test.cpp b/libc/test/src/stdio/vprintf_test.cpp
new file mode 100644
index 00000000000000..0710bbffe5b137
--- /dev/null
+++ b/libc/test/src/stdio/vprintf_test.cpp
@@ -0,0 +1,41 @@
+//===-- Unittests for vprintf --------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// These tests are copies of the non-v variants of the printf functions. This is
+// because these functions are identical in every way except for how the varargs
+// are passed.
+
+#include "src/stdio/vprintf.h"
+
+#include "test/UnitTest/Test.h"
+
+int call_vprintf(const char *__restrict format, ...) {
+  va_list vlist;
+  va_start(vlist, format);
+  int ret = __llvm_libc::vprintf(format, vlist);
+  va_end(vlist);
+  return ret;
+}
+
+TEST(LlvmLibcVPrintfTest, PrintOut) {
+  int written;
+
+  constexpr char simple[] = "A simple string with no conversions.\n";
+  written = call_vprintf(simple);
+  EXPECT_EQ(written, static_cast<int>(sizeof(simple) - 1));
+
+  constexpr char numbers[] = "1234567890\n";
+  written = call_vprintf("%s", numbers);
+  EXPECT_EQ(written, static_cast<int>(sizeof(numbers) - 1));
+
+  constexpr char format_more[] = "%s and more\n";
+  constexpr char short_numbers[] = "1234";
+  written = call_vprintf(format_more, short_numbers);
+  EXPECT_EQ(written,
+            static_cast<int>(sizeof(format_more) + sizeof(short_numbers) - 4));
+}

diff  --git a/libc/test/src/stdio/vsnprintf_test.cpp b/libc/test/src/stdio/vsnprintf_test.cpp
new file mode 100644
index 00000000000000..08f10708b8dca3
--- /dev/null
+++ b/libc/test/src/stdio/vsnprintf_test.cpp
@@ -0,0 +1,69 @@
+//===-- Unittests for snprintf --------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// These tests are copies of the non-v variants of the printf functions. This is
+// because these functions are identical in every way except for how the varargs
+// are passed.
+
+#include "src/stdio/vsnprintf.h"
+
+#include "test/UnitTest/Test.h"
+
+int call_vsnprintf(char *__restrict buffer, size_t buffsz,
+                   const char *__restrict format, ...) {
+  va_list vlist;
+  va_start(vlist, format);
+  int ret = __llvm_libc::vsnprintf(buffer, buffsz, format, vlist);
+  va_end(vlist);
+  return ret;
+}
+
+// The sprintf test cases cover testing the shared printf functionality, so
+// these tests will focus on snprintf exclusive features.
+
+TEST(LlvmLibcVSNPrintfTest, CutOff) {
+  char buff[100];
+  int written;
+
+  written = call_vsnprintf(buff, 16, "A simple string with no conversions.");
+  EXPECT_EQ(written, 36);
+  ASSERT_STREQ(buff, "A simple string");
+
+  written = call_vsnprintf(buff, 5, "%s", "1234567890");
+  EXPECT_EQ(written, 10);
+  ASSERT_STREQ(buff, "1234");
+
+  written = call_vsnprintf(buff, 67, "%-101c", 'a');
+  EXPECT_EQ(written, 101);
+  ASSERT_STREQ(buff, "a "
+                     "        " // Each of these is 8 spaces, and there are 8.
+                     "        " // In total there are 65 spaces
+                     "        " // 'a' + 65 spaces + '\0' = 67
+                     "        "
+                     "        "
+                     "        "
+                     "        "
+                     "        ");
+
+  // passing null as the output pointer is allowed as long as buffsz is 0.
+  written = call_vsnprintf(nullptr, 0, "%s and more", "1234567890");
+  EXPECT_EQ(written, 19);
+}
+
+TEST(LlvmLibcVSNPrintfTest, NoCutOff) {
+  char buff[64];
+  int written;
+
+  written = call_vsnprintf(buff, 37, "A simple string with no conversions.");
+  EXPECT_EQ(written, 36);
+  ASSERT_STREQ(buff, "A simple string with no conversions.");
+
+  written = call_vsnprintf(buff, 20, "%s", "1234567890");
+  EXPECT_EQ(written, 10);
+  ASSERT_STREQ(buff, "1234567890");
+}

diff  --git a/libc/test/src/stdio/vsprintf_test.cpp b/libc/test/src/stdio/vsprintf_test.cpp
new file mode 100644
index 00000000000000..3f0551a877f60c
--- /dev/null
+++ b/libc/test/src/stdio/vsprintf_test.cpp
@@ -0,0 +1,66 @@
+//===-- Unittests for vsprintf --------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// These tests are shortened copies of the non-v variants of the printf
+// functions. This is because these functions are identical in every way except
+// for how the varargs are passed.
+
+#include "src/stdio/vsprintf.h"
+
+#include "test/UnitTest/Test.h"
+
+int call_vsprintf(char *__restrict buffer, const char *__restrict format, ...) {
+  va_list vlist;
+  va_start(vlist, format);
+  int ret = __llvm_libc::vsprintf(buffer, format, vlist);
+  va_end(vlist);
+  return ret;
+}
+
+TEST(LlvmLibcVSPrintfTest, SimpleNoConv) {
+  char buff[64];
+  int written;
+
+  written = call_vsprintf(buff, "A simple string with no conversions.");
+  EXPECT_EQ(written, 36);
+  ASSERT_STREQ(buff, "A simple string with no conversions.");
+}
+
+TEST(LlvmLibcVSPrintfTest, PercentConv) {
+  char buff[64];
+  int written;
+
+  written = call_vsprintf(buff, "%%");
+  EXPECT_EQ(written, 1);
+  ASSERT_STREQ(buff, "%");
+
+  written = call_vsprintf(buff, "abc %% def");
+  EXPECT_EQ(written, 9);
+  ASSERT_STREQ(buff, "abc % def");
+
+  written = call_vsprintf(buff, "%%%%%%");
+  EXPECT_EQ(written, 3);
+  ASSERT_STREQ(buff, "%%%");
+}
+
+TEST(LlvmLibcVSPrintfTest, CharConv) {
+  char buff[64];
+  int written;
+
+  written = call_vsprintf(buff, "%c", 'a');
+  EXPECT_EQ(written, 1);
+  ASSERT_STREQ(buff, "a");
+
+  written = call_vsprintf(buff, "%3c %-3c", '1', '2');
+  EXPECT_EQ(written, 7);
+  ASSERT_STREQ(buff, "  1 2  ");
+
+  written = call_vsprintf(buff, "%*c", 2, '3');
+  EXPECT_EQ(written, 2);
+  ASSERT_STREQ(buff, " 3");
+}

diff  --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index 0ee3395260b5b6..2fbab7ae00a41b 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -2642,6 +2642,19 @@ libc_support_library(
     ],
 )
 
+libc_support_library(
+    name = "vfprintf_internal",
+    hdrs = ["src/stdio/printf_core/vfprintf_internal.h"],
+    defines = PRINTF_COPTS,
+    deps = [
+        ":__support_arg_list",
+        ":__support_file_file",
+        ":__support_macros_attributes",
+        ":printf_main",
+        ":printf_writer",
+    ],
+)
+
 libc_function(
     name = "sprintf",
     srcs = ["src/stdio/sprintf.cpp"],
@@ -2669,23 +2682,63 @@ libc_function(
     ],
 )
 
-libc_support_library(
-    name = "vfprintf_internal",
-    hdrs = ["src/stdio/printf_core/vfprintf_internal.h"],
+libc_function(
+    name = "printf",
+    srcs = ["src/stdio/printf.cpp"],
+    hdrs = ["src/stdio/printf.h"],
     defines = PRINTF_COPTS,
     deps = [
         ":__support_arg_list",
         ":__support_file_file",
-        ":__support_macros_attributes",
+        ":errno",
+        ":vfprintf_internal",
+    ],
+)
+
+libc_function(
+    name = "fprintf",
+    srcs = ["src/stdio/fprintf.cpp"],
+    hdrs = ["src/stdio/fprintf.h"],
+    defines = PRINTF_COPTS,
+    deps = [
+        ":__support_arg_list",
+        ":__support_file_file",
+        ":errno",
+        ":vfprintf_internal",
+    ],
+)
+
+libc_function(
+    name = "vsprintf",
+    srcs = ["src/stdio/vsprintf.cpp"],
+    hdrs = ["src/stdio/vsprintf.h"],
+    defines = PRINTF_COPTS,
+    deps = [
+        ":__support_arg_list",
+        ":__support_cpp_limits",
+        ":errno",
         ":printf_main",
         ":printf_writer",
     ],
 )
 
 libc_function(
-    name = "printf",
-    srcs = ["src/stdio/printf.cpp"],
-    hdrs = ["src/stdio/printf.h"],
+    name = "vsnprintf",
+    srcs = ["src/stdio/vsnprintf.cpp"],
+    hdrs = ["src/stdio/vsnprintf.h"],
+    defines = PRINTF_COPTS,
+    deps = [
+        ":__support_arg_list",
+        ":errno",
+        ":printf_main",
+        ":printf_writer",
+    ],
+)
+
+libc_function(
+    name = "vprintf",
+    srcs = ["src/stdio/vprintf.cpp"],
+    hdrs = ["src/stdio/vprintf.h"],
     defines = PRINTF_COPTS,
     deps = [
         ":__support_arg_list",
@@ -2696,9 +2749,9 @@ libc_function(
 )
 
 libc_function(
-    name = "fprintf",
-    srcs = ["src/stdio/fprintf.cpp"],
-    hdrs = ["src/stdio/fprintf.h"],
+    name = "vfprintf",
+    srcs = ["src/stdio/vfprintf.cpp"],
+    hdrs = ["src/stdio/vfprintf.h"],
     defines = PRINTF_COPTS,
     deps = [
         ":__support_arg_list",

diff  --git a/utils/bazel/llvm-project-overlay/libc/test/src/stdio/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/src/stdio/BUILD.bazel
index 46bef299013a2b..e25ed33b908f8d 100644
--- a/utils/bazel/llvm-project-overlay/libc/test/src/stdio/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/test/src/stdio/BUILD.bazel
@@ -89,3 +89,35 @@ libc_test(
         "//libc:fprintf",
     ],
 )
+
+libc_test(
+    name = "vsprintf_test",
+    srcs = ["vsprintf_test.cpp"],
+    libc_function_deps = [
+        "//libc:vsprintf",
+    ],
+)
+
+libc_test(
+    name = "vsnprintf_test",
+    srcs = ["vsnprintf_test.cpp"],
+    libc_function_deps = [
+        "//libc:vsnprintf",
+    ],
+)
+
+libc_test(
+    name = "vprintf_test",
+    srcs = ["vprintf_test.cpp"],
+    libc_function_deps = [
+        "//libc:vprintf",
+    ],
+)
+
+libc_test(
+    name = "vfprintf_test",
+    srcs = ["vfprintf_test.cpp"],
+    libc_function_deps = [
+        "//libc:vfprintf",
+    ],
+)


        


More information about the libc-commits mailing list