[libc-commits] [libc] [libc][WIP] add iprintf (PR #115194)

Michael Jones via libc-commits libc-commits at lists.llvm.org
Thu Nov 7 13:00:50 PST 2024


https://github.com/michaelrj-google updated https://github.com/llvm/llvm-project/pull/115194

>From 07cf7cb666203d5687cf10e47539a774907ef23e Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Wed, 6 Nov 2024 10:54:44 -0800
Subject: [PATCH 1/2] [libc][WIP] add iprintf

iprintf is printf without floats, clang supports this here: https://github.com/llvm/llvm-project/blob/main/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp#L3392
---
 libc/config/linux/x86_64/entrypoints.txt  |  1 +
 libc/src/stdio/CMakeLists.txt             |  1 +
 libc/src/stdio/generic/CMakeLists.txt     | 17 +++++-
 libc/src/stdio/generic/iprintf.cpp        | 39 ++++++++++++
 libc/src/stdio/iprintf.h                  | 21 +++++++
 libc/src/stdio/printf_core/CMakeLists.txt | 74 +++++++++++++++++++++++
 libc/test/src/stdio/CMakeLists.txt        | 11 ++++
 libc/test/src/stdio/iprintf_test.cpp      | 33 ++++++++++
 8 files changed, 196 insertions(+), 1 deletion(-)
 create mode 100644 libc/src/stdio/generic/iprintf.cpp
 create mode 100644 libc/src/stdio/iprintf.h
 create mode 100644 libc/test/src/stdio/iprintf_test.cpp

diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 9a4a0ff9e75a40..e5d90dfa2010fa 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -212,6 +212,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.fscanf
     libc.src.stdio.vfscanf
     libc.src.stdio.printf
+    libc.src.stdio.iprintf
     libc.src.stdio.remove
     libc.src.stdio.rename
     libc.src.stdio.scanf
diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index b9bc904471df9a..4456e9cb3ad16b 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -286,6 +286,7 @@ add_stdio_entrypoint_object(fputc)
 add_stdio_entrypoint_object(putc)
 add_stdio_entrypoint_object(putchar)
 add_stdio_entrypoint_object(printf)
+add_stdio_entrypoint_object(iprintf)
 add_stdio_entrypoint_object(fprintf)
 add_stdio_entrypoint_object(fgetc)
 add_stdio_entrypoint_object(fgetc_unlocked)
diff --git a/libc/src/stdio/generic/CMakeLists.txt b/libc/src/stdio/generic/CMakeLists.txt
index bf301a6b0cb3c6..250d0ec58a12fc 100644
--- a/libc/src/stdio/generic/CMakeLists.txt
+++ b/libc/src/stdio/generic/CMakeLists.txt
@@ -366,7 +366,6 @@ add_entrypoint_object(
 list(APPEND fprintf_deps
       libc.hdr.types.FILE
       libc.src.__support.arg_list
-      libc.src.stdio.printf_core.vfprintf_internal
 )
 
 if(LLVM_LIBC_FULL_BUILD)
@@ -393,6 +392,19 @@ add_entrypoint_object(
     ../printf.h
   DEPENDS
     ${printf_deps}
+    libc.src.stdio.printf_core.vfprintf_internal
+)
+
+# the printf variant with no floats is called iprintf for some reason
+add_entrypoint_object(
+  iprintf
+  SRCS
+    iprintf.cpp
+  HDRS
+    ../iprintf.h
+  DEPENDS
+    ${printf_deps}
+    libc.src.stdio.printf_core.vfprintf_internal_nofloat
 )
 
 add_entrypoint_object(
@@ -403,6 +415,7 @@ add_entrypoint_object(
     ../vprintf.h
   DEPENDS
     ${printf_deps}
+    libc.src.stdio.printf_core.vfprintf_internal
 )
 
 add_entrypoint_object(
@@ -413,6 +426,7 @@ add_entrypoint_object(
     ../fprintf.h
   DEPENDS
     ${fprintf_deps}
+    libc.src.stdio.printf_core.vfprintf_internal
 )
 
 add_entrypoint_object(
@@ -423,6 +437,7 @@ add_entrypoint_object(
     ../vfprintf.h
   DEPENDS
     ${fprintf_deps}
+    libc.src.stdio.printf_core.vfprintf_internal
 )
 
 add_entrypoint_object(
diff --git a/libc/src/stdio/generic/iprintf.cpp b/libc/src/stdio/generic/iprintf.cpp
new file mode 100644
index 00000000000000..5b124d74311144
--- /dev/null
+++ b/libc/src/stdio/generic/iprintf.cpp
@@ -0,0 +1,39 @@
+//===-- Implementation of iprintf -------------------------------*- 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/iprintf.h"
+
+#include "src/__support/File/file.h"
+#include "src/__support/arg_list.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/vfprintf_internal.h"
+
+#include "hdr/types/FILE.h"
+#include <stdarg.h>
+
+#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
+#define PRINTF_STDOUT LIBC_NAMESPACE::stdout
+#else // LIBC_COPT_STDIO_USE_SYSTEM_FILE
+#define PRINTF_STDOUT ::stdout
+#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, iprintf, (const char *__restrict format, ...)) {
+  va_list vlist;
+  va_start(vlist, format);
+  internal::ArgList args(vlist); // This holder class allows for easier copying
+                                 // and pointer semantics, as well as handling
+                                 // destruction automatically.
+  va_end(vlist);
+  int ret_val = printf_core::vfprintf_internal(
+      reinterpret_cast<::FILE *>(PRINTF_STDOUT), format, args);
+  return ret_val;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/iprintf.h b/libc/src/stdio/iprintf.h
new file mode 100644
index 00000000000000..8844c03618da8a
--- /dev/null
+++ b/libc/src/stdio/iprintf.h
@@ -0,0 +1,21 @@
+//===-- Implementation header of iprintf ------------------------*- 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_IPRINTF_H
+#define LLVM_LIBC_SRC_STDIO_IPRINTF_H
+
+#include "hdr/types/FILE.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int iprintf(const char *__restrict format, ...);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDIO_IPRINTF_H
diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
index 9eaffe2f7ed621..d8489e8dcf1cf7 100644
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -22,6 +22,11 @@ endif()
 if(LIBC_CONF_PRINTF_DISABLE_STRERROR)
   list(APPEND printf_config_copts "-DLIBC_COPT_PRINTF_DISABLE_STRERROR")
 endif()
+
+set(printf_config_copts_nofloat ${printf_config_copts})
+list(APPEND printf_config_copts_nofloat "-DLIBC_COPT_PRINTF_DISABLE_FLOAT")
+list(PREPEND printf_config_copts_nofloat "COMPILE_OPTIONS")
+
 if(printf_config_copts)
   list(PREPEND printf_config_copts "COMPILE_OPTIONS")
 endif()
@@ -33,6 +38,14 @@ add_header_library(
   ${printf_config_copts}
 )
 
+
+add_header_library(
+  printf_config_nofloat
+  HDRS
+    printf_config.h
+  ${printf_config_copts_nofloat}
+)
+
 add_header_library(
   core_structs
   HDRS
@@ -110,6 +123,41 @@ add_object_library(
     libc.src.__support.StringUtil.error_to_string
 )
 
+
+add_object_library(
+  converter_nofloat
+  SRCS
+    converter.cpp
+  HDRS
+    converter.h
+    converter_atlas.h
+    converter_utils.h
+    string_converter.h
+    char_converter.h
+    int_converter.h
+    ptr_converter.h
+    write_int_converter.h
+    fixed_converter.h #TODO: Check if this should be disabled when fixed unavail
+    strerror_converter.h
+  DEPENDS
+    .core_structs
+    .printf_config_nofloat
+    .writer
+    libc.src.__support.big_int
+    libc.src.__support.common
+    libc.src.__support.CPP.limits
+    libc.src.__support.CPP.span
+    libc.src.__support.CPP.string_view
+    libc.src.__support.float_to_string
+    libc.src.__support.FPUtil.fenv_impl
+    libc.src.__support.FPUtil.fp_bits
+    libc.src.__support.FPUtil.rounding_mode
+    libc.src.__support.integer_to_string
+    libc.src.__support.libc_assert
+    libc.src.__support.uint128
+    libc.src.__support.StringUtil.error_to_string
+)
+
 add_object_library(
   printf_main
   SRCS
@@ -124,6 +172,20 @@ add_object_library(
     libc.src.__support.arg_list
 )
 
+add_object_library(
+  printf_main_nofloat
+  SRCS
+    printf_main.cpp
+  HDRS
+    printf_main.h
+  DEPENDS
+    .parser
+    .converter_nofloat
+    .writer
+    .core_structs
+    libc.src.__support.arg_list
+)
+
 add_header_library(
   vasprintf_internal
   HDRS
@@ -154,3 +216,15 @@ add_header_library(
     libc.src.stdio.printf_core.writer
   ${use_system_file}
 )
+
+add_header_library(
+  vfprintf_internal_nofloat
+  HDRS
+    vfprintf_internal.h
+  DEPENDS
+    libc.src.__support.File.file
+    libc.src.__support.arg_list
+    libc.src.stdio.printf_core.printf_main_nofloat
+    libc.src.stdio.printf_core.writer
+  ${use_system_file}
+)
diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt
index e17f8d8c101a96..a4e166fd7beec9 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -197,6 +197,17 @@ add_libc_test(
     libc.src.stdio.printf
 )
 
+add_libc_test(
+  iprintf_test
+  ${hermetic_test_only}
+  SUITE
+    libc_stdio_unittests
+  SRCS
+    iprintf_test.cpp
+  DEPENDS
+    libc.src.stdio.iprintf
+)
+
 add_libc_test(
    asprintf_test
    SUITE
diff --git a/libc/test/src/stdio/iprintf_test.cpp b/libc/test/src/stdio/iprintf_test.cpp
new file mode 100644
index 00000000000000..3be6b9c9edf86e
--- /dev/null
+++ b/libc/test/src/stdio/iprintf_test.cpp
@@ -0,0 +1,33 @@
+//===-- Unittests for iprintf ---------------------------------------------===//
+//
+// 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/iprintf.h"
+
+#include "test/UnitTest/Test.h"
+
+TEST(LlvmLibcIprintfTest, PrintOut) {
+  int written;
+
+  constexpr char simple[] = "A simple string with no conversions.\n";
+  written = LIBC_NAMESPACE::iprintf(simple);
+  EXPECT_EQ(written, static_cast<int>(sizeof(simple) - 1));
+
+  constexpr char numbers[] = "1234567890\n";
+  written = LIBC_NAMESPACE::iprintf("%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 = LIBC_NAMESPACE::iprintf(format_more, short_numbers);
+  EXPECT_EQ(written,
+            static_cast<int>(sizeof(format_more) + sizeof(short_numbers) - 4));
+
+  constexpr char format_float[] = "%f doesn't work\n";
+  written = LIBC_NAMESPACE::iprintf(format_float, 1.0);
+  EXPECT_EQ(written, static_cast<int>(sizeof(format_float) - 1));
+}

>From 7b048e6913512e586d56166175c0a77bdabbcbd9 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Thu, 7 Nov 2024 12:58:33 -0800
Subject: [PATCH 2/2] ROTFO: Cleanup cmake, add printf variants

ROTFO stands for Rest of the Formatting Options, of course.
---
 libc/config/linux/x86_64/entrypoints.txt  |  7 +-
 libc/src/stdio/CMakeLists.txt             | 25 ++++++
 libc/src/stdio/fiprintf.h                 | 21 +++++
 libc/src/stdio/generic/CMakeLists.txt     | 55 +++++++++----
 libc/src/stdio/generic/fiprintf.cpp       | 34 ++++++++
 libc/src/stdio/printf_core/CMakeLists.txt | 98 +++++++++++------------
 libc/src/stdio/siprintf.cpp               | 39 +++++++++
 libc/src/stdio/siprintf.h                 | 20 +++++
 libc/src/stdio/sniprintf.cpp              | 39 +++++++++
 libc/src/stdio/sniprintf.h                | 22 +++++
 libc/test/src/stdio/CMakeLists.txt        | 33 ++++++++
 libc/test/src/stdio/fiprintf_test.cpp     | 92 +++++++++++++++++++++
 libc/test/src/stdio/siprintf_test.cpp     | 38 +++++++++
 libc/test/src/stdio/sniprintf_test.cpp    | 66 +++++++++++++++
 14 files changed, 520 insertions(+), 69 deletions(-)
 create mode 100644 libc/src/stdio/fiprintf.h
 create mode 100644 libc/src/stdio/generic/fiprintf.cpp
 create mode 100644 libc/src/stdio/siprintf.cpp
 create mode 100644 libc/src/stdio/siprintf.h
 create mode 100644 libc/src/stdio/sniprintf.cpp
 create mode 100644 libc/src/stdio/sniprintf.h
 create mode 100644 libc/test/src/stdio/fiprintf_test.cpp
 create mode 100644 libc/test/src/stdio/siprintf_test.cpp
 create mode 100644 libc/test/src/stdio/sniprintf_test.cpp

diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index e5d90dfa2010fa..42b7cc59139239 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -212,7 +212,6 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.fscanf
     libc.src.stdio.vfscanf
     libc.src.stdio.printf
-    libc.src.stdio.iprintf
     libc.src.stdio.remove
     libc.src.stdio.rename
     libc.src.stdio.scanf
@@ -228,6 +227,12 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.vsprintf
     libc.src.stdio.vasprintf
 
+    # stdio.h hidden entrypoints
+    libc.src.stdio.iprintf
+    libc.src.stdio.fiprintf
+    libc.src.stdio.siprintf
+    libc.src.stdio.sniprintf
+
     # sys/epoll.h entrypoints
     libc.src.sys.epoll.epoll_create
     libc.src.sys.epoll.epoll_create1
diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index 4456e9cb3ad16b..42b1bded7dd9b0 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -195,6 +195,30 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.writer
 )
 
+add_entrypoint_object(
+  siprintf
+  SRCS
+    siprintf.cpp
+  HDRS
+    siprintf.h
+  DEPENDS
+    libc.src.stdio.printf_core.printf_main_nofloat
+    libc.src.stdio.printf_core.writer
+)
+
+# technically clang doesn't yet support sniprintf, so I could rename it to the
+# objectively cooler (and more accurate) sinprintf. Alas I chose not to.
+add_entrypoint_object(
+  sniprintf
+  SRCS
+    sniprintf.cpp
+  HDRS
+    sniprintf.h
+  DEPENDS
+    libc.src.stdio.printf_core.printf_main_nofloat
+    libc.src.stdio.printf_core.writer
+)
+
 add_entrypoint_object(
   asprintf
   SRCS
@@ -288,6 +312,7 @@ add_stdio_entrypoint_object(putchar)
 add_stdio_entrypoint_object(printf)
 add_stdio_entrypoint_object(iprintf)
 add_stdio_entrypoint_object(fprintf)
+add_stdio_entrypoint_object(fiprintf)
 add_stdio_entrypoint_object(fgetc)
 add_stdio_entrypoint_object(fgetc_unlocked)
 add_stdio_entrypoint_object(getc)
diff --git a/libc/src/stdio/fiprintf.h b/libc/src/stdio/fiprintf.h
new file mode 100644
index 00000000000000..4e210f7cf8908d
--- /dev/null
+++ b/libc/src/stdio/fiprintf.h
@@ -0,0 +1,21 @@
+//===-- Implementation header of fiprintf -----------------------*- 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_FIPRINTF_H
+#define LLVM_LIBC_SRC_STDIO_FIPRINTF_H
+
+#include "hdr/types/FILE.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int fiprintf(::FILE *__restrict stream, const char *__restrict format, ...);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDIO_FIPRINTF_H
diff --git a/libc/src/stdio/generic/CMakeLists.txt b/libc/src/stdio/generic/CMakeLists.txt
index 250d0ec58a12fc..f32cc22a72a0f8 100644
--- a/libc/src/stdio/generic/CMakeLists.txt
+++ b/libc/src/stdio/generic/CMakeLists.txt
@@ -375,13 +375,28 @@ if(LLVM_LIBC_FULL_BUILD)
   )
 endif()
 
+# Copy the deps for fiprintf_deps
+set(fiprintf_deps ${fprintf_deps})
+
+list(APPEND fprintf_deps
+  libc.src.stdio.printf_core.vfprintf_internal
+)
+
+list(APPEND fiprintf_deps
+  libc.src.stdio.printf_core.vfprintf_internal_nofloat
+)
+
 # Copy the deps for printf_deps
 set(printf_deps ${fprintf_deps})
+set(iprintf_deps ${fiprintf_deps})
 
 if(LLVM_LIBC_FULL_BUILD)
   list(APPEND printf_deps
       libc.src.__support.File.platform_stdout
   )
+  list(APPEND iprintf_deps
+      libc.src.__support.File.platform_stdout
+  )
 endif()
 
 add_entrypoint_object(
@@ -392,19 +407,6 @@ add_entrypoint_object(
     ../printf.h
   DEPENDS
     ${printf_deps}
-    libc.src.stdio.printf_core.vfprintf_internal
-)
-
-# the printf variant with no floats is called iprintf for some reason
-add_entrypoint_object(
-  iprintf
-  SRCS
-    iprintf.cpp
-  HDRS
-    ../iprintf.h
-  DEPENDS
-    ${printf_deps}
-    libc.src.stdio.printf_core.vfprintf_internal_nofloat
 )
 
 add_entrypoint_object(
@@ -415,7 +417,6 @@ add_entrypoint_object(
     ../vprintf.h
   DEPENDS
     ${printf_deps}
-    libc.src.stdio.printf_core.vfprintf_internal
 )
 
 add_entrypoint_object(
@@ -426,7 +427,6 @@ add_entrypoint_object(
     ../fprintf.h
   DEPENDS
     ${fprintf_deps}
-    libc.src.stdio.printf_core.vfprintf_internal
 )
 
 add_entrypoint_object(
@@ -437,7 +437,30 @@ add_entrypoint_object(
     ../vfprintf.h
   DEPENDS
     ${fprintf_deps}
-    libc.src.stdio.printf_core.vfprintf_internal
+)
+
+# the printf variant with no floats is called iprintf for some reason. Idk ask XMOS
+add_entrypoint_object(
+  iprintf
+  SRCS
+    iprintf.cpp
+  HDRS
+    ../iprintf.h
+  DEPENDS
+    ${iprintf_deps}
+)
+
+# Similarly I'm unclear why the "i" modifier goes after the target modifier but w/e.
+# Oldest commit I could find mentioning it dates from 2011:
+# https://github.com/llvm/llvm-project/commit/2dfb888392d4d82f467dfc8c9c4d2192cd71afb6
+add_entrypoint_object(
+  fiprintf
+  SRCS
+    fiprintf.cpp
+  HDRS
+    ../fiprintf.h
+  DEPENDS
+    ${fiprintf_deps}
 )
 
 add_entrypoint_object(
diff --git a/libc/src/stdio/generic/fiprintf.cpp b/libc/src/stdio/generic/fiprintf.cpp
new file mode 100644
index 00000000000000..6f1223f33977da
--- /dev/null
+++ b/libc/src/stdio/generic/fiprintf.cpp
@@ -0,0 +1,34 @@
+//===-- Implementation of fiprintf ------------------------------*- 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/fiprintf.h"
+
+#include "src/__support/File/file.h"
+#include "src/__support/arg_list.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/vfprintf_internal.h"
+
+#include "hdr/types/FILE.h"
+#include <stdarg.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, fiprintf,
+                   (::FILE *__restrict stream, const char *__restrict format,
+                    ...)) {
+  va_list vlist;
+  va_start(vlist, format);
+  internal::ArgList args(vlist); // This holder class allows for easier copying
+                                 // and pointer semantics, as well as handling
+                                 // destruction automatically.
+  va_end(vlist);
+  int ret_val = printf_core::vfprintf_internal(stream, format, args);
+  return ret_val;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
index d8489e8dcf1cf7..c62c45fe90af14 100644
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -86,76 +86,70 @@ add_object_library(
     libc.src.string.memory_utils.inline_memset
 )
 
+list(APPEND printf_converter_deps_nofloat
+  .core_structs
+  .writer
+  libc.src.__support.big_int
+  libc.src.__support.common
+  libc.src.__support.CPP.limits
+  libc.src.__support.CPP.span
+  libc.src.__support.CPP.string_view
+  libc.src.__support.integer_to_string
+  libc.src.__support.libc_assert
+  libc.src.__support.uint128
+  libc.src.__support.StringUtil.error_to_string
+)
+
+list(APPEND printf_converter_hdrs_nofloat
+  converter_atlas.h
+  converter_utils.h
+  string_converter.h
+  char_converter.h
+  int_converter.h
+  ptr_converter.h
+  write_int_converter.h
+  fixed_converter.h #TODO: Check if this should be disabled when fixed unavail
+  strerror_converter.h
+)
+
+set(printf_converter_deps ${printf_converter_deps_nofloat})
+set(printf_converter_hdrs ${printf_converter_hdrs_nofloat})
+
+list(APPEND printf_converter_deps
+  libc.src.__support.float_to_string
+  libc.src.__support.FPUtil.fenv_impl
+  libc.src.__support.FPUtil.fp_bits
+  libc.src.__support.FPUtil.rounding_mode
+)
+
+list(APPEND printf_converter_hdrs
+  float_inf_nan_converter.h
+  float_hex_converter.h
+  float_dec_converter.h
+)
+
 add_object_library(
   converter
   SRCS
     converter.cpp
   HDRS
     converter.h
-    converter_atlas.h
-    converter_utils.h
-    string_converter.h
-    char_converter.h
-    int_converter.h
-    ptr_converter.h
-    write_int_converter.h
-    float_inf_nan_converter.h
-    float_hex_converter.h
-    float_dec_converter.h
-    fixed_converter.h #TODO: Check if this should be disabled when fixed unavail
-    strerror_converter.h
+    ${printf_converter_hdrs}
   DEPENDS
-    .core_structs
     .printf_config
-    .writer
-    libc.src.__support.big_int
-    libc.src.__support.common
-    libc.src.__support.CPP.limits
-    libc.src.__support.CPP.span
-    libc.src.__support.CPP.string_view
-    libc.src.__support.float_to_string
-    libc.src.__support.FPUtil.fenv_impl
-    libc.src.__support.FPUtil.fp_bits
-    libc.src.__support.FPUtil.rounding_mode
-    libc.src.__support.integer_to_string
-    libc.src.__support.libc_assert
-    libc.src.__support.uint128
-    libc.src.__support.StringUtil.error_to_string
+    ${printf_converter_deps}
 )
 
-
 add_object_library(
   converter_nofloat
   SRCS
     converter.cpp
   HDRS
     converter.h
-    converter_atlas.h
-    converter_utils.h
-    string_converter.h
-    char_converter.h
-    int_converter.h
-    ptr_converter.h
-    write_int_converter.h
-    fixed_converter.h #TODO: Check if this should be disabled when fixed unavail
-    strerror_converter.h
+    ${printf_converter_hdrs_nofloat}
   DEPENDS
-    .core_structs
     .printf_config_nofloat
-    .writer
-    libc.src.__support.big_int
-    libc.src.__support.common
-    libc.src.__support.CPP.limits
-    libc.src.__support.CPP.span
-    libc.src.__support.CPP.string_view
-    libc.src.__support.float_to_string
-    libc.src.__support.FPUtil.fenv_impl
-    libc.src.__support.FPUtil.fp_bits
-    libc.src.__support.FPUtil.rounding_mode
-    libc.src.__support.integer_to_string
-    libc.src.__support.libc_assert
-    libc.src.__support.uint128
-    libc.src.__support.StringUtil.error_to_string
+    ${printf_converter_deps_nofloat}
 )
 
 add_object_library(
diff --git a/libc/src/stdio/siprintf.cpp b/libc/src/stdio/siprintf.cpp
new file mode 100644
index 00000000000000..9f78581ec9451f
--- /dev/null
+++ b/libc/src/stdio/siprintf.cpp
@@ -0,0 +1,39 @@
+//===-- Implementation of siprintf ------------------------------*- 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/siprintf.h"
+
+#include "src/__support/CPP/limits.h"
+#include "src/__support/arg_list.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/printf_main.h"
+#include "src/stdio/printf_core/writer.h"
+
+#include <stdarg.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, siprintf,
+                   (char *__restrict buffer, const char *__restrict format,
+                    ...)) {
+  va_list vlist;
+  va_start(vlist, format);
+  internal::ArgList args(vlist); // This holder class allows for easier copying
+                                 // and pointer semantics, as well as handling
+                                 // destruction automatically.
+  va_end(vlist);
+
+  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 LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/siprintf.h b/libc/src/stdio/siprintf.h
new file mode 100644
index 00000000000000..9d7b389c7fd9a8
--- /dev/null
+++ b/libc/src/stdio/siprintf.h
@@ -0,0 +1,20 @@
+//===-- Implementation header of siprintf -----------------------*- 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_SIPRINTF_H
+#define LLVM_LIBC_SRC_STDIO_SIPRINTF_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int siprintf(char *__restrict buffer, const char *__restrict format, ...);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDIO_SIPRINTF_H
diff --git a/libc/src/stdio/sniprintf.cpp b/libc/src/stdio/sniprintf.cpp
new file mode 100644
index 00000000000000..45c7e9f50bb983
--- /dev/null
+++ b/libc/src/stdio/sniprintf.cpp
@@ -0,0 +1,39 @@
+//===-- Implementation of sniprintf -----------------------------*- 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/sniprintf.h"
+
+#include "src/__support/arg_list.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/printf_main.h"
+#include "src/stdio/printf_core/writer.h"
+
+#include <stdarg.h>
+#include <stddef.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, sniprintf,
+                   (char *__restrict buffer, size_t buffsz,
+                    const char *__restrict format, ...)) {
+  va_list vlist;
+  va_start(vlist, format);
+  internal::ArgList args(vlist); // This holder class allows for easier copying
+                                 // and pointer semantics, as well as handling
+                                 // destruction automatically.
+  va_end(vlist);
+  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 LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/sniprintf.h b/libc/src/stdio/sniprintf.h
new file mode 100644
index 00000000000000..589b0a6b0ae116
--- /dev/null
+++ b/libc/src/stdio/sniprintf.h
@@ -0,0 +1,22 @@
+//===-- Implementation header of sniprintf ----------------------*- 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_SNIPRINTF_H
+#define LLVM_LIBC_SRC_STDIO_SNIPRINTF_H
+
+#include "src/__support/macros/config.h"
+#include <stddef.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+int sniprintf(char *__restrict buffer, size_t buffsz,
+              const char *__restrict format, ...);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDIO_SNIPRINTF_H
diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt
index a4e166fd7beec9..cf4c1c319ebd6f 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -147,6 +147,16 @@ add_fp_unittest(
     ${sprintf_test_copts}
 )
 
+add_libc_test(
+  siprintf_test
+  SUITE
+    libc_stdio_unittests
+  SRCS
+    siprintf_test.cpp
+  DEPENDS
+    libc.src.stdio.siprintf
+)
+
 add_libc_test(
   snprintf_test
   SUITE
@@ -157,6 +167,16 @@ add_libc_test(
     libc.src.stdio.snprintf
 )
 
+add_libc_test(
+  sniprintf_test
+  SUITE
+    libc_stdio_unittests
+  SRCS
+    sniprintf_test.cpp
+  DEPENDS
+    libc.src.stdio.sniprintf
+)
+
 if(LLVM_LIBC_FULL_BUILD)
   # In fullbuild mode, fprintf's tests use the internal FILE for other functions.
   list(APPEND fprintf_test_deps
@@ -208,6 +228,19 @@ add_libc_test(
     libc.src.stdio.iprintf
 )
 
+add_libc_test(
+  fiprintf_test
+  SUITE
+    libc_stdio_unittests
+  SRCS
+    fiprintf_test.cpp
+  DEPENDS
+    libc.src.stdio.fiprintf
+    ${fprintf_test_deps}
+  COMPILE_OPTIONS
+    ${use_system_file}
+)
+
 add_libc_test(
    asprintf_test
    SUITE
diff --git a/libc/test/src/stdio/fiprintf_test.cpp b/libc/test/src/stdio/fiprintf_test.cpp
new file mode 100644
index 00000000000000..ea6046f1ea51bd
--- /dev/null
+++ b/libc/test/src/stdio/fiprintf_test.cpp
@@ -0,0 +1,92 @@
+//===-- Unittests for fiprintf --------------------------------------------===//
+//
+// 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 LIBC_COPT_STDIO_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_STDIO_USE_SYSTEM_FILE
+
+#include "src/stdio/fiprintf.h"
+
+#include "test/UnitTest/Test.h"
+
+namespace printf_test {
+#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
+using LIBC_NAMESPACE::fclose;
+using LIBC_NAMESPACE::ferror;
+using LIBC_NAMESPACE::fopen;
+using LIBC_NAMESPACE::fread;
+#else  // defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
+using ::fclose;
+using ::ferror;
+using ::fopen;
+using ::fread;
+#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
+} // namespace printf_test
+
+TEST(LlvmLibcFIPrintfTest, WriteToFile) {
+  const char *FILENAME = "fiprintf_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 = LIBC_NAMESPACE::fiprintf(file, simple);
+  EXPECT_EQ(written, 37);
+
+  constexpr char numbers[] = "1234567890\n";
+  written = LIBC_NAMESPACE::fiprintf(file, "%s", numbers);
+  EXPECT_EQ(written, 11);
+
+  constexpr char format_more[] = "%s and more\n";
+  constexpr char short_numbers[] = "1234";
+  written = LIBC_NAMESPACE::fiprintf(file, format_more, short_numbers);
+  EXPECT_EQ(written, 14);
+
+  constexpr char format_float[] = "%f doesn't work\n";
+  written = LIBC_NAMESPACE::fiprintf(file, format_float, 1.0);
+  EXPECT_EQ(written, static_cast<int>(sizeof(format_float) - 1));
+
+  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::fread(data, 1, sizeof(format_float) - 1, file),
+            sizeof(format_float) - 1);
+  data[sizeof(format_float) - 1] = '\0';
+  ASSERT_STREQ(data, "%f doesn't work\n");
+
+  ASSERT_EQ(printf_test::ferror(file), 0);
+
+  written = LIBC_NAMESPACE::fiprintf(
+      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/siprintf_test.cpp b/libc/test/src/stdio/siprintf_test.cpp
new file mode 100644
index 00000000000000..1bfed210dc3856
--- /dev/null
+++ b/libc/test/src/stdio/siprintf_test.cpp
@@ -0,0 +1,38 @@
+//===-- Unittests for siprintf --------------------------------------------===//
+//
+// 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/siprintf.h"
+
+#include "test/UnitTest/Test.h"
+
+TEST(LlvmLibcSIPrintfTest, SimpleParsing) {
+  char buff[100];
+  int written;
+
+  constexpr char simple[] = "A simple string with no conversions.";
+  written = LIBC_NAMESPACE::siprintf(buff, simple);
+  EXPECT_EQ(written, static_cast<int>(sizeof(simple) - 1));
+  ASSERT_STREQ(buff, simple);
+
+  constexpr char numbers[] = "1234567890";
+  written = LIBC_NAMESPACE::siprintf(buff, "%s", numbers);
+  EXPECT_EQ(written, static_cast<int>(sizeof(numbers) - 1));
+  ASSERT_STREQ(buff, numbers);
+
+  constexpr char format_more[] = "%s and more";
+  constexpr char short_numbers[] = "1234";
+  written = LIBC_NAMESPACE::siprintf(buff, format_more, short_numbers);
+  EXPECT_EQ(written,
+            static_cast<int>(sizeof(format_more) + sizeof(short_numbers) - 4));
+  ASSERT_STREQ(buff, "1234 and more");
+
+  constexpr char format_float[] = "%f doesn't work\n";
+  written = LIBC_NAMESPACE::siprintf(buff, format_float, 1.0);
+  EXPECT_EQ(written, static_cast<int>(sizeof(format_float) - 1));
+  ASSERT_STREQ(buff, format_float);
+}
diff --git a/libc/test/src/stdio/sniprintf_test.cpp b/libc/test/src/stdio/sniprintf_test.cpp
new file mode 100644
index 00000000000000..7ec7aee57653b5
--- /dev/null
+++ b/libc/test/src/stdio/sniprintf_test.cpp
@@ -0,0 +1,66 @@
+//===-- Unittests for sniprintf -------------------------------------------===//
+//
+// 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/sniprintf.h"
+
+#include "test/UnitTest/Test.h"
+
+// The sprintf test cases cover testing the shared printf functionality, so
+// these tests will focus on sniprintf exclusive features.
+
+TEST(LlvmLibcSNIPrintfTest, CutOff) {
+  char buff[100];
+  int written;
+
+  written = LIBC_NAMESPACE::sniprintf(buff, 16,
+                                      "A simple string with no conversions.");
+  EXPECT_EQ(written, 36);
+  ASSERT_STREQ(buff, "A simple string");
+
+  written = LIBC_NAMESPACE::sniprintf(buff, 5, "%s", "1234567890");
+  EXPECT_EQ(written, 10);
+  ASSERT_STREQ(buff, "1234");
+
+  written = LIBC_NAMESPACE::sniprintf(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
+                     "        "
+                     "        "
+                     "        "
+                     "        "
+                     "        ");
+
+  written =
+      LIBC_NAMESPACE::sniprintf(buff, 10, "%f before and %f after", 1.0, 2.0);
+  EXPECT_EQ(written, 22);
+  ASSERT_STREQ(buff, "%f before");
+
+  // passing null as the output pointer is allowed as long as buffsz is 0.
+  written = LIBC_NAMESPACE::sniprintf(nullptr, 0, "%s and more", "1234567890");
+  EXPECT_EQ(written, 19);
+
+  written = LIBC_NAMESPACE::sniprintf(nullptr, 0, "%*s", INT_MIN, "nothing");
+  EXPECT_EQ(written, INT_MAX);
+}
+
+TEST(LlvmLibcSNIPrintfTest, NoCutOff) {
+  char buff[64];
+  int written;
+
+  written = LIBC_NAMESPACE::sniprintf(buff, 37,
+                                      "A simple string with no conversions.");
+  EXPECT_EQ(written, 36);
+  ASSERT_STREQ(buff, "A simple string with no conversions.");
+
+  written = LIBC_NAMESPACE::sniprintf(buff, 20, "%s", "1234567890");
+  EXPECT_EQ(written, 10);
+  ASSERT_STREQ(buff, "1234567890");
+}



More information about the libc-commits mailing list