[libc-commits] [libc] [libc] Template the writing mode for the writer class (PR #111559)

Joseph Huber via libc-commits libc-commits at lists.llvm.org
Tue Oct 8 10:06:54 PDT 2024


https://github.com/jhuber6 created https://github.com/llvm/llvm-project/pull/111559

Summary:
Currently we dispatch the writing mode off of a runtime enum passed in
by the constructor. This causes very unfortunate codegen for the GPU
targets where we get worst-case codegen because of the unused function
pointer for `sprintf`. Instead, this patch moves all of this to a
template so it can be masked out. This results in no dynamic stack and
uses 60 VGPRs instead of 117. It also compiles about 5x as fast.


>From c7394d2504f84de0195261d03b43c21c1197980e Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Tue, 8 Oct 2024 12:04:13 -0500
Subject: [PATCH] [libc] Template the writing mode for the writer class

Summary:
Currently we dispatch the writing mode off of a runtime enum passed in
by the constructor. This causes very unfortunate codegen for the GPU
targets where we get worst-case codegen because of the unused function
pointer for `sprintf`. Instead, this patch moves all of this to a
template so it can be masked out. This results in no dynamic stack and
uses 60 VGPRs instead of 117. It also compiles about 5x as fast.
---
 libc/src/stdio/printf_core/CMakeLists.txt     |  12 +-
 libc/src/stdio/printf_core/char_converter.h   |   4 +-
 libc/src/stdio/printf_core/converter.cpp      | 105 -----------------
 libc/src/stdio/printf_core/converter.h        |  85 +++++++++++++-
 libc/src/stdio/printf_core/fixed_converter.h  |   4 +-
 .../stdio/printf_core/float_dec_converter.h   |  60 ++++++----
 .../stdio/printf_core/float_hex_converter.h   |   3 +-
 .../printf_core/float_inf_nan_converter.h     |   4 +-
 libc/src/stdio/printf_core/int_converter.h    |   4 +-
 libc/src/stdio/printf_core/printf_main.cpp    |  43 -------
 libc/src/stdio/printf_core/printf_main.h      |  24 +++-
 libc/src/stdio/printf_core/ptr_converter.h    |   4 +-
 .../stdio/printf_core/strerror_converter.h    |   4 +-
 libc/src/stdio/printf_core/string_converter.h |   4 +-
 .../stdio/printf_core/vasprintf_internal.h    |  15 +--
 .../src/stdio/printf_core/vfprintf_internal.h |   6 +-
 .../stdio/printf_core/write_int_converter.h   |   3 +-
 libc/src/stdio/printf_core/writer.cpp         |  46 --------
 libc/src/stdio/printf_core/writer.h           | 107 +++++++++++-------
 libc/src/stdio/snprintf.cpp                   |   5 +-
 libc/src/stdio/sprintf.cpp                    |   5 +-
 libc/src/stdio/vsnprintf.cpp                  |   5 +-
 libc/src/stdio/vsprintf.cpp                   |   5 +-
 libc/src/stdlib/str_from_util.h               |   4 +-
 libc/src/stdlib/strfromd.cpp                  |   5 +-
 libc/src/stdlib/strfromf.cpp                  |   5 +-
 libc/src/stdlib/strfroml.cpp                  |   5 +-
 .../src/stdio/printf_core/converter_test.cpp  |  12 +-
 .../src/stdio/printf_core/writer_test.cpp     |  80 +++++++------
 libc/utils/gpu/server/CMakeLists.txt          |   6 +-
 libc/utils/gpu/server/rpc_server.cpp          |  13 ++-
 31 files changed, 335 insertions(+), 352 deletions(-)
 delete mode 100644 libc/src/stdio/printf_core/converter.cpp
 delete mode 100644 libc/src/stdio/printf_core/printf_main.cpp
 delete mode 100644 libc/src/stdio/printf_core/writer.cpp

diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
index 8172fda1e866ed..4c9a9f5ceddb2f 100644
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -59,10 +59,8 @@ add_header_library(
     libc.src.__support.common
 )
 
-add_object_library(
+add_header_library(
   writer
-  SRCS
-    writer.cpp
   HDRS
     writer.h
   DEPENDS
@@ -73,10 +71,8 @@ add_object_library(
     libc.src.string.memory_utils.inline_memset
 )
 
-add_object_library(
+add_header_library(
   converter
-  SRCS
-    converter.cpp
   HDRS
     converter.h
     converter_atlas.h
@@ -110,10 +106,8 @@ add_object_library(
     libc.src.__support.StringUtil.error_to_string
 )
 
-add_object_library(
+add_header_library(
   printf_main
-  SRCS
-    printf_main.cpp
   HDRS
     printf_main.h
   DEPENDS
diff --git a/libc/src/stdio/printf_core/char_converter.h b/libc/src/stdio/printf_core/char_converter.h
index 2596cba813c2e9..fd2eb2553887a8 100644
--- a/libc/src/stdio/printf_core/char_converter.h
+++ b/libc/src/stdio/printf_core/char_converter.h
@@ -17,7 +17,9 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-LIBC_INLINE int convert_char(Writer *writer, const FormatSection &to_conv) {
+template <WriteMode write_mode>
+LIBC_INLINE int convert_char(Writer<write_mode> *writer,
+                             const FormatSection &to_conv) {
   char c = static_cast<char>(to_conv.conv_val_raw);
 
   constexpr int STRING_LEN = 1;
diff --git a/libc/src/stdio/printf_core/converter.cpp b/libc/src/stdio/printf_core/converter.cpp
deleted file mode 100644
index b1c66451f53f0f..00000000000000
--- a/libc/src/stdio/printf_core/converter.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-//===-- Format specifier converter implmentation for printf -----*- 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/printf_core/converter.h"
-
-#include "src/__support/macros/config.h"
-#include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/printf_config.h"
-#include "src/stdio/printf_core/strerror_converter.h"
-#include "src/stdio/printf_core/writer.h"
-
-// This option allows for replacing all of the conversion functions with custom
-// replacements. This allows conversions to be replaced at compile time.
-#ifndef LIBC_COPT_PRINTF_CONV_ATLAS
-#include "src/stdio/printf_core/converter_atlas.h"
-#else
-#include LIBC_COPT_PRINTF_CONV_ATLAS
-#endif
-
-#include <stddef.h>
-
-namespace LIBC_NAMESPACE_DECL {
-namespace printf_core {
-
-int convert(Writer *writer, const FormatSection &to_conv) {
-  if (!to_conv.has_conv)
-    return writer->write(to_conv.raw_string);
-
-#if !defined(LIBC_COPT_PRINTF_DISABLE_FLOAT) &&                                \
-    defined(LIBC_COPT_PRINTF_HEX_LONG_DOUBLE)
-  if (to_conv.length_modifier == LengthModifier::L) {
-    switch (to_conv.conv_name) {
-    case 'f':
-    case 'F':
-    case 'e':
-    case 'E':
-    case 'g':
-    case 'G':
-      return convert_float_hex_exp(writer, to_conv);
-    default:
-      break;
-    }
-  }
-#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
-
-  switch (to_conv.conv_name) {
-  case '%':
-    return writer->write("%");
-  case 'c':
-    return convert_char(writer, to_conv);
-  case 's':
-    return convert_string(writer, to_conv);
-  case 'd':
-  case 'i':
-  case 'u':
-  case 'o':
-  case 'x':
-  case 'X':
-  case 'b':
-  case 'B':
-    return convert_int(writer, to_conv);
-#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT
-  case 'f':
-  case 'F':
-    return convert_float_decimal(writer, to_conv);
-  case 'e':
-  case 'E':
-    return convert_float_dec_exp(writer, to_conv);
-  case 'a':
-  case 'A':
-    return convert_float_hex_exp(writer, to_conv);
-  case 'g':
-  case 'G':
-    return convert_float_dec_auto(writer, to_conv);
-#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
-#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
-  case 'r':
-  case 'R':
-  case 'k':
-  case 'K':
-    return convert_fixed(writer, to_conv);
-#endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
-#ifndef LIBC_COPT_PRINTF_DISABLE_STRERROR
-  case 'm':
-    return convert_strerror(writer, to_conv);
-#endif // LIBC_COPT_PRINTF_DISABLE_STRERROR
-#ifndef LIBC_COPT_PRINTF_DISABLE_WRITE_INT
-  case 'n':
-    return convert_write_int(writer, to_conv);
-#endif // LIBC_COPT_PRINTF_DISABLE_WRITE_INT
-  case 'p':
-    return convert_pointer(writer, to_conv);
-  default:
-    return writer->write(to_conv.raw_string);
-  }
-  return -1;
-}
-
-} // namespace printf_core
-} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/converter.h b/libc/src/stdio/printf_core/converter.h
index 2b3f06d0aa7a36..f26ed727f05f40 100644
--- a/libc/src/stdio/printf_core/converter.h
+++ b/libc/src/stdio/printf_core/converter.h
@@ -11,8 +11,18 @@
 
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/printf_config.h"
+#include "src/stdio/printf_core/strerror_converter.h"
 #include "src/stdio/printf_core/writer.h"
 
+// This option allows for replacing all of the conversion functions with custom
+// replacements. This allows conversions to be replaced at compile time.
+#ifndef LIBC_COPT_PRINTF_CONV_ATLAS
+#include "src/stdio/printf_core/converter_atlas.h"
+#else
+#include LIBC_COPT_PRINTF_CONV_ATLAS
+#endif
+
 #include <stddef.h>
 
 namespace LIBC_NAMESPACE_DECL {
@@ -21,7 +31,80 @@ namespace printf_core {
 // convert will call a conversion function to convert the FormatSection into
 // its string representation, and then that will write the result to the
 // writer.
-int convert(Writer *writer, const FormatSection &to_conv);
+template <WriteMode write_mode>
+int convert(Writer<write_mode> *writer, const FormatSection &to_conv) {
+  if (!to_conv.has_conv)
+    return writer->write(to_conv.raw_string);
+
+#if !defined(LIBC_COPT_PRINTF_DISABLE_FLOAT) &&                                \
+    defined(LIBC_COPT_PRINTF_HEX_LONG_DOUBLE)
+  if (to_conv.length_modifier == LengthModifier::L) {
+    switch (to_conv.conv_name) {
+    case 'f':
+    case 'F':
+    case 'e':
+    case 'E':
+    case 'g':
+    case 'G':
+      return convert_float_hex_exp(writer, to_conv);
+    default:
+      break;
+    }
+  }
+#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
+
+  switch (to_conv.conv_name) {
+  case '%':
+    return writer->write("%");
+  case 'c':
+    return convert_char(writer, to_conv);
+  case 's':
+    return convert_string(writer, to_conv);
+  case 'd':
+  case 'i':
+  case 'u':
+  case 'o':
+  case 'x':
+  case 'X':
+  case 'b':
+  case 'B':
+    return convert_int(writer, to_conv);
+#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT
+  case 'f':
+  case 'F':
+    return convert_float_decimal(writer, to_conv);
+  case 'e':
+  case 'E':
+    return convert_float_dec_exp(writer, to_conv);
+  case 'a':
+  case 'A':
+    return convert_float_hex_exp(writer, to_conv);
+  case 'g':
+  case 'G':
+    return convert_float_dec_auto(writer, to_conv);
+#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
+#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
+  case 'r':
+  case 'R':
+  case 'k':
+  case 'K':
+    return convert_fixed(writer, to_conv);
+#endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
+#ifndef LIBC_COPT_PRINTF_DISABLE_STRERROR
+  case 'm':
+    return convert_strerror(writer, to_conv);
+#endif // LIBC_COPT_PRINTF_DISABLE_STRERROR
+#ifndef LIBC_COPT_PRINTF_DISABLE_WRITE_INT
+  case 'n':
+    return convert_write_int(writer, to_conv);
+#endif // LIBC_COPT_PRINTF_DISABLE_WRITE_INT
+  case 'p':
+    return convert_pointer(writer, to_conv);
+  default:
+    return writer->write(to_conv.raw_string);
+  }
+  return -1;
+}
 
 } // namespace printf_core
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/fixed_converter.h b/libc/src/stdio/printf_core/fixed_converter.h
index c8812d77b62e34..6013c36b4cf3e9 100644
--- a/libc/src/stdio/printf_core/fixed_converter.h
+++ b/libc/src/stdio/printf_core/fixed_converter.h
@@ -62,7 +62,9 @@ LIBC_INLINE constexpr uint32_t const_ten_exp(uint32_t exponent) {
     }                                                                          \
   } while (false)
 
-LIBC_INLINE int convert_fixed(Writer *writer, const FormatSection &to_conv) {
+template <WriteMode write_mode>
+LIBC_INLINE int convert_fixed(Writer<write_mode> *writer,
+                              const FormatSection &to_conv) {
   // Long accum should be the largest type, so we can store all the smaller
   // numbers in things sized for it.
   using LARep = fixed_point::FXRep<unsigned long accum>;
diff --git a/libc/src/stdio/printf_core/float_dec_converter.h b/libc/src/stdio/printf_core/float_dec_converter.h
index e39ba6ecea8d48..fd729f67d4dda5 100644
--- a/libc/src/stdio/printf_core/float_dec_converter.h
+++ b/libc/src/stdio/printf_core/float_dec_converter.h
@@ -92,7 +92,7 @@ zero_after_digits(int32_t base_2_exp, int32_t digits_after_point, T mantissa,
   return has_trailing_zeros;
 }
 
-class PaddingWriter {
+template <WriteMode write_mode> class PaddingWriter {
   bool left_justified = false;
   bool leading_zeroes = false;
   char sign_char = 0;
@@ -106,7 +106,8 @@ class PaddingWriter {
         sign_char(init_sign_char),
         min_width(to_conv.min_width > 0 ? to_conv.min_width : 0) {}
 
-  LIBC_INLINE int write_left_padding(Writer *writer, size_t total_digits) {
+  LIBC_INLINE int write_left_padding(Writer<write_mode> *writer,
+                                     size_t total_digits) {
     // The pattern is (spaces) (sign) (zeroes), but only one of spaces and
     // zeroes can be written, and only if the padding amount is positive.
     int padding_amount =
@@ -129,7 +130,8 @@ class PaddingWriter {
     return 0;
   }
 
-  LIBC_INLINE int write_right_padding(Writer *writer, size_t total_digits) {
+  LIBC_INLINE int write_right_padding(Writer<write_mode> *writer,
+                                      size_t total_digits) {
     // If and only if the conversion is left justified, there may be trailing
     // spaces.
     int padding_amount =
@@ -154,7 +156,7 @@ class PaddingWriter {
   This FloatWriter class does the buffering and counting, and writes to the
   output when necessary.
 */
-class FloatWriter {
+template <WriteMode write_mode> class FloatWriter {
   char block_buffer[BLOCK_SIZE]; // The buffer that holds a block.
   size_t buffered_digits = 0;    // The number of digits held in the buffer.
   bool has_written = false;      // True once any digits have been output.
@@ -163,8 +165,9 @@ class FloatWriter {
   size_t digits_before_decimal = 0; // The # of digits to write before the '.'
   size_t total_digits_written = 0;  // The # of digits that have been output.
   bool has_decimal_point;           // True if the number has a decimal point.
-  Writer *writer;                   // Writes to the final output.
-  PaddingWriter padding_writer; // Handles prefixes/padding, uses total_digits.
+  Writer<write_mode> *writer;       // Writes to the final output.
+  PaddingWriter<write_mode>
+      padding_writer; // Handles prefixes/padding, uses total_digits.
 
   LIBC_INLINE int flush_buffer(bool round_up_max_blocks = false) {
     const char MAX_BLOCK_DIGIT = (round_up_max_blocks ? '0' : '9');
@@ -244,8 +247,9 @@ class FloatWriter {
   static_assert(fputil::FPBits<long double>::EXP_LEN < (sizeof(int) * 8));
 
 public:
-  LIBC_INLINE FloatWriter(Writer *init_writer, bool init_has_decimal_point,
-                          const PaddingWriter &init_padding_writer)
+  LIBC_INLINE FloatWriter(Writer<write_mode> *init_writer,
+                          bool init_has_decimal_point,
+                          const PaddingWriter<write_mode> &init_padding_writer)
       : has_decimal_point(init_has_decimal_point), writer(init_writer),
         padding_writer(init_padding_writer) {}
 
@@ -465,12 +469,24 @@ class FloatWriter {
   }
 };
 
+// Class-template auto deduction helpers.
+FloatWriter(Writer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>, bool,
+            const PaddingWriter<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>)
+    -> FloatWriter<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>;
+FloatWriter(Writer<WriteMode::RESIZE_AND_FILL_BUFF>, bool,
+            const PaddingWriter<WriteMode::RESIZE_AND_FILL_BUFF>)
+    -> FloatWriter<WriteMode::RESIZE_AND_FILL_BUFF>;
+FloatWriter(Writer<WriteMode::FLUSH_TO_STREAM>, bool,
+            const PaddingWriter<WriteMode::FLUSH_TO_STREAM>)
+    -> FloatWriter<WriteMode::FLUSH_TO_STREAM>;
+
 // This implementation is based on the Ryu Printf algorithm by Ulf Adams:
 // Ulf Adams. 2019. Ryƫ revisited: printf floating point conversion.
 // Proc. ACM Program. Lang. 3, OOPSLA, Article 169 (October 2019), 23 pages.
 // https://doi.org/10.1145/3360595
-template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
-LIBC_INLINE int convert_float_decimal_typed(Writer *writer,
+template <typename T, WriteMode write_mode,
+          cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
+LIBC_INLINE int convert_float_decimal_typed(Writer<write_mode> *writer,
                                             const FormatSection &to_conv,
                                             fputil::FPBits<T> float_bits) {
   // signed because later we use -FRACTION_LEN
@@ -497,7 +513,7 @@ LIBC_INLINE int convert_float_decimal_typed(Writer *writer,
   // ignored.
   bool nonzero = false;
 
-  PaddingWriter padding_writer(to_conv, sign_char);
+  PaddingWriter<write_mode> padding_writer(to_conv, sign_char);
   FloatWriter float_writer(writer, has_decimal_point, padding_writer);
   FloatToString<T> float_converter(float_bits.get_val());
 
@@ -578,8 +594,9 @@ LIBC_INLINE int convert_float_decimal_typed(Writer *writer,
   return WRITE_OK;
 }
 
-template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
-LIBC_INLINE int convert_float_dec_exp_typed(Writer *writer,
+template <typename T, WriteMode write_mode,
+          cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
+LIBC_INLINE int convert_float_dec_exp_typed(Writer<write_mode> *writer,
                                             const FormatSection &to_conv,
                                             fputil::FPBits<T> float_bits) {
   // signed because later we use -FRACTION_LEN
@@ -604,7 +621,7 @@ LIBC_INLINE int convert_float_dec_exp_typed(Writer *writer,
   bool has_decimal_point =
       (precision > 0) || ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0);
 
-  PaddingWriter padding_writer(to_conv, sign_char);
+  PaddingWriter<write_mode> padding_writer(to_conv, sign_char);
   FloatWriter float_writer(writer, has_decimal_point, padding_writer);
   FloatToString<T> float_converter(float_bits.get_val());
 
@@ -740,8 +757,9 @@ LIBC_INLINE int convert_float_dec_exp_typed(Writer *writer,
   return WRITE_OK;
 }
 
-template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
-LIBC_INLINE int convert_float_dec_auto_typed(Writer *writer,
+template <typename T, WriteMode write_mode,
+          cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
+LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer,
                                              const FormatSection &to_conv,
                                              fputil::FPBits<T> float_bits) {
   // signed because later we use -FRACTION_LEN
@@ -1107,7 +1125,9 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer *writer,
 }
 
 // TODO: unify the float converters to remove the duplicated checks for inf/nan.
-LIBC_INLINE int convert_float_decimal(Writer *writer,
+
+template <WriteMode write_mode>
+LIBC_INLINE int convert_float_decimal(Writer<write_mode> *writer,
                                       const FormatSection &to_conv) {
   if (to_conv.length_modifier == LengthModifier::L) {
     fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;
@@ -1128,7 +1148,8 @@ LIBC_INLINE int convert_float_decimal(Writer *writer,
   return convert_inf_nan(writer, to_conv);
 }
 
-LIBC_INLINE int convert_float_dec_exp(Writer *writer,
+template <WriteMode write_mode>
+LIBC_INLINE int convert_float_dec_exp(Writer<write_mode> *writer,
                                       const FormatSection &to_conv) {
   if (to_conv.length_modifier == LengthModifier::L) {
     fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;
@@ -1149,7 +1170,8 @@ LIBC_INLINE int convert_float_dec_exp(Writer *writer,
   return convert_inf_nan(writer, to_conv);
 }
 
-LIBC_INLINE int convert_float_dec_auto(Writer *writer,
+template <WriteMode write_mode>
+LIBC_INLINE int convert_float_dec_auto(Writer<write_mode> *writer,
                                        const FormatSection &to_conv) {
   if (to_conv.length_modifier == LengthModifier::L) {
     fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;
diff --git a/libc/src/stdio/printf_core/float_hex_converter.h b/libc/src/stdio/printf_core/float_hex_converter.h
index 0b3ff3dd1cbfdc..3d41f8a45b0bb3 100644
--- a/libc/src/stdio/printf_core/float_hex_converter.h
+++ b/libc/src/stdio/printf_core/float_hex_converter.h
@@ -24,7 +24,8 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-LIBC_INLINE int convert_float_hex_exp(Writer *writer,
+template <WriteMode write_mode>
+LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
                                       const FormatSection &to_conv) {
   using LDBits = fputil::FPBits<long double>;
   using StorageType = LDBits::StorageType;
diff --git a/libc/src/stdio/printf_core/float_inf_nan_converter.h b/libc/src/stdio/printf_core/float_inf_nan_converter.h
index a7da682b835bee..8988d6474bf409 100644
--- a/libc/src/stdio/printf_core/float_inf_nan_converter.h
+++ b/libc/src/stdio/printf_core/float_inf_nan_converter.h
@@ -23,7 +23,9 @@ namespace printf_core {
 
 using StorageType = fputil::FPBits<long double>::StorageType;
 
-LIBC_INLINE int convert_inf_nan(Writer *writer, const FormatSection &to_conv) {
+template <WriteMode write_mode>
+LIBC_INLINE int convert_inf_nan(Writer<write_mode> *writer,
+                                const FormatSection &to_conv) {
   // All of the letters will be defined relative to variable a, which will be
   // the appropriate case based on the case of the conversion.
   const char a = (to_conv.conv_name & 32) | 'A';
diff --git a/libc/src/stdio/printf_core/int_converter.h b/libc/src/stdio/printf_core/int_converter.h
index f345e86b97a691..22f4dd627a104b 100644
--- a/libc/src/stdio/printf_core/int_converter.h
+++ b/libc/src/stdio/printf_core/int_converter.h
@@ -65,7 +65,9 @@ num_to_strview(uintmax_t num, cpp::span<char> bufref, char conv_name) {
 
 } // namespace details
 
-LIBC_INLINE int convert_int(Writer *writer, const FormatSection &to_conv) {
+template <WriteMode write_mode>
+LIBC_INLINE int convert_int(Writer<write_mode> *writer,
+                            const FormatSection &to_conv) {
   static constexpr size_t BITS_IN_BYTE = 8;
   static constexpr size_t BITS_IN_NUM = sizeof(uintmax_t) * BITS_IN_BYTE;
 
diff --git a/libc/src/stdio/printf_core/printf_main.cpp b/libc/src/stdio/printf_core/printf_main.cpp
deleted file mode 100644
index bd4a5a168bd23e..00000000000000
--- a/libc/src/stdio/printf_core/printf_main.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===-- Starting point for printf -------------------------------*- 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/printf_core/printf_main.h"
-
-#include "src/__support/arg_list.h"
-#include "src/__support/macros/config.h"
-#include "src/stdio/printf_core/converter.h"
-#include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/parser.h"
-#include "src/stdio/printf_core/writer.h"
-
-#include <stddef.h>
-
-namespace LIBC_NAMESPACE_DECL {
-namespace printf_core {
-
-int printf_main(Writer *writer, const char *__restrict str,
-                internal::ArgList &args) {
-  Parser<internal::ArgList> parser(str, args);
-  int result = 0;
-  for (FormatSection cur_section = parser.get_next_section();
-       !cur_section.raw_string.empty();
-       cur_section = parser.get_next_section()) {
-    if (cur_section.has_conv)
-      result = convert(writer, cur_section);
-    else
-      result = writer->write(cur_section.raw_string);
-
-    if (result < 0)
-      return result;
-  }
-
-  return writer->get_chars_written();
-}
-
-} // namespace printf_core
-} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/printf_main.h b/libc/src/stdio/printf_core/printf_main.h
index 3e73bf36e0e309..57f29858d5298d 100644
--- a/libc/src/stdio/printf_core/printf_main.h
+++ b/libc/src/stdio/printf_core/printf_main.h
@@ -11,6 +11,9 @@
 
 #include "src/__support/arg_list.h"
 #include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/converter.h"
+#include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/parser.h"
 #include "src/stdio/printf_core/writer.h"
 
 #include <stddef.h>
@@ -18,8 +21,25 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-int printf_main(Writer *writer, const char *__restrict str,
-                internal::ArgList &args);
+template <WriteMode write_mode>
+int printf_main(Writer<write_mode> *writer, const char *__restrict str,
+                internal::ArgList &args) {
+  Parser<internal::ArgList> parser(str, args);
+  int result = 0;
+  for (FormatSection cur_section = parser.get_next_section();
+       !cur_section.raw_string.empty();
+       cur_section = parser.get_next_section()) {
+    if (cur_section.has_conv)
+      result = convert(writer, cur_section);
+    else
+      result = writer->write(cur_section.raw_string);
+
+    if (result < 0)
+      return result;
+  }
+
+  return writer->get_chars_written();
+}
 
 } // namespace printf_core
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/ptr_converter.h b/libc/src/stdio/printf_core/ptr_converter.h
index bf84718dfe6a86..c2a74e3043e6f6 100644
--- a/libc/src/stdio/printf_core/ptr_converter.h
+++ b/libc/src/stdio/printf_core/ptr_converter.h
@@ -18,7 +18,9 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-LIBC_INLINE int convert_pointer(Writer *writer, const FormatSection &to_conv) {
+template <WriteMode write_mode>
+LIBC_INLINE int convert_pointer(Writer<write_mode> *writer,
+                                const FormatSection &to_conv) {
   FormatSection new_conv = to_conv;
 
   if (to_conv.conv_val_ptr == nullptr) {
diff --git a/libc/src/stdio/printf_core/strerror_converter.h b/libc/src/stdio/printf_core/strerror_converter.h
index 2902fd37c31ae3..2cd6df0c01d457 100644
--- a/libc/src/stdio/printf_core/strerror_converter.h
+++ b/libc/src/stdio/printf_core/strerror_converter.h
@@ -19,7 +19,9 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-LIBC_INLINE int convert_strerror(Writer *writer, const FormatSection &to_conv) {
+template <WriteMode write_mode>
+LIBC_INLINE int convert_strerror(Writer<write_mode> *writer,
+                                 const FormatSection &to_conv) {
   FormatSection new_conv = to_conv;
   const int error_num = static_cast<int>(to_conv.conv_val_raw);
 
diff --git a/libc/src/stdio/printf_core/string_converter.h b/libc/src/stdio/printf_core/string_converter.h
index 1f36d511241076..74c9f598210f77 100644
--- a/libc/src/stdio/printf_core/string_converter.h
+++ b/libc/src/stdio/printf_core/string_converter.h
@@ -20,7 +20,9 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-LIBC_INLINE int convert_string(Writer *writer, const FormatSection &to_conv) {
+template <WriteMode write_mode>
+LIBC_INLINE int convert_string(Writer<write_mode> *writer,
+                               const FormatSection &to_conv) {
   size_t string_len = 0;
   const char *str_ptr = reinterpret_cast<const char *>(to_conv.conv_val_ptr);
 
diff --git a/libc/src/stdio/printf_core/vasprintf_internal.h b/libc/src/stdio/printf_core/vasprintf_internal.h
index 24ebc02a0b33f2..a3112e16aef0ad 100644
--- a/libc/src/stdio/printf_core/vasprintf_internal.h
+++ b/libc/src/stdio/printf_core/vasprintf_internal.h
@@ -13,12 +13,13 @@
 #include "src/stdio/printf_core/writer.h"
 #include <stdlib.h> // malloc, realloc, free
 
-namespace LIBC_NAMESPACE {
+namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
 LIBC_INLINE int resize_overflow_hook(cpp::string_view new_str, void *target) {
-  printf_core::WriteBuffer *wb =
-      reinterpret_cast<printf_core::WriteBuffer *>(target);
+  printf_core::WriteBuffer<WriteMode::RESIZE_AND_FILL_BUFF> *wb =
+      reinterpret_cast<
+          printf_core::WriteBuffer<WriteMode::RESIZE_AND_FILL_BUFF> *>(target);
   size_t new_size = new_str.size() + wb->buff_cur;
   const bool isBuffOnStack = (wb->buff == wb->init_buff);
   char *new_buff = static_cast<char *>(
@@ -43,9 +44,9 @@ constexpr size_t DEFAULT_BUFFER_SIZE = 200;
 LIBC_INLINE int vasprintf_internal(char **ret, const char *format,
                                    internal::ArgList args) {
   char init_buff_on_stack[DEFAULT_BUFFER_SIZE];
-  printf_core::WriteBuffer wb(init_buff_on_stack, DEFAULT_BUFFER_SIZE,
-                              resize_overflow_hook);
-  printf_core::Writer writer(&wb);
+  printf_core::WriteBuffer<WriteMode::RESIZE_AND_FILL_BUFF> wb(
+      init_buff_on_stack, DEFAULT_BUFFER_SIZE, resize_overflow_hook);
+  printf_core::Writer writer(wb);
 
   auto ret_val = printf_core::printf_main(&writer, format, args);
   if (ret_val < 0) {
@@ -64,4 +65,4 @@ LIBC_INLINE int vasprintf_internal(char **ret, const char *format,
   return ret_val;
 }
 } // namespace printf_core
-} // namespace LIBC_NAMESPACE
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/vfprintf_internal.h b/libc/src/stdio/printf_core/vfprintf_internal.h
index 3becfee71dd273..810b135776cffe 100644
--- a/libc/src/stdio/printf_core/vfprintf_internal.h
+++ b/libc/src/stdio/printf_core/vfprintf_internal.h
@@ -72,9 +72,9 @@ LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream,
                                   internal::ArgList &args) {
   constexpr size_t BUFF_SIZE = 1024;
   char buffer[BUFF_SIZE];
-  printf_core::WriteBuffer wb(buffer, BUFF_SIZE, &file_write_hook,
-                              reinterpret_cast<void *>(stream));
-  Writer writer(&wb);
+  printf_core::WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb(
+      buffer, BUFF_SIZE, &file_write_hook, reinterpret_cast<void *>(stream));
+  Writer writer(wb);
   internal::flockfile(stream);
   int retval = printf_main(&writer, format, args);
   int flushval = wb.overflow_write("");
diff --git a/libc/src/stdio/printf_core/write_int_converter.h b/libc/src/stdio/printf_core/write_int_converter.h
index a47cb41cb3287c..efcff278bd284c 100644
--- a/libc/src/stdio/printf_core/write_int_converter.h
+++ b/libc/src/stdio/printf_core/write_int_converter.h
@@ -19,7 +19,8 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-LIBC_INLINE int convert_write_int(Writer *writer,
+template <WriteMode write_mode>
+LIBC_INLINE int convert_write_int(Writer<write_mode> *writer,
                                   const FormatSection &to_conv) {
 
 #ifndef LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
diff --git a/libc/src/stdio/printf_core/writer.cpp b/libc/src/stdio/printf_core/writer.cpp
deleted file mode 100644
index d1cf85df1c8f89..00000000000000
--- a/libc/src/stdio/printf_core/writer.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-//===-- Writer definition for printf ----------------------------*- 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 "writer.h"
-#include "src/__support/CPP/string_view.h"
-#include "src/__support/macros/config.h"
-#include "src/stdio/printf_core/core_structs.h"
-#include "src/string/memory_utils/inline_memset.h"
-#include <stddef.h>
-
-namespace LIBC_NAMESPACE_DECL {
-namespace printf_core {
-
-int Writer::pad(char new_char, size_t length) {
-  // First, fill as much of the buffer as possible with the padding char.
-  size_t written = 0;
-  const size_t buff_space = wb->buff_len - wb->buff_cur;
-  // ASSERT: length > buff_space
-  if (buff_space > 0) {
-    inline_memset(wb->buff + wb->buff_cur, new_char, buff_space);
-    wb->buff_cur += buff_space;
-    written = buff_space;
-  }
-
-  // Next, overflow write the rest of length using the mini_buff.
-  constexpr size_t MINI_BUFF_SIZE = 64;
-  char mini_buff[MINI_BUFF_SIZE];
-  inline_memset(mini_buff, new_char, MINI_BUFF_SIZE);
-  cpp::string_view mb_string_view(mini_buff, MINI_BUFF_SIZE);
-  while (written + MINI_BUFF_SIZE < length) {
-    int result = wb->overflow_write(mb_string_view);
-    if (result != WRITE_OK)
-      return result;
-    written += MINI_BUFF_SIZE;
-  }
-  cpp::string_view mb_substr = mb_string_view.substr(0, length - written);
-  return wb->overflow_write(mb_substr);
-}
-
-} // namespace printf_core
-} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/writer.h b/libc/src/stdio/printf_core/writer.h
index 5526a478ea620e..aef91304532576 100644
--- a/libc/src/stdio/printf_core/writer.h
+++ b/libc/src/stdio/printf_core/writer.h
@@ -21,12 +21,13 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-struct WriteBuffer {
-  enum class WriteMode {
-    FILL_BUFF_AND_DROP_OVERFLOW,
-    FLUSH_TO_STREAM,
-    RESIZE_AND_FILL_BUFF,
-  };
+enum class WriteMode {
+  FILL_BUFF_AND_DROP_OVERFLOW,
+  FLUSH_TO_STREAM,
+  RESIZE_AND_FILL_BUFF,
+};
+
+template <WriteMode write_mode> struct WriteBuffer {
   using StreamWriter = int (*)(cpp::string_view, void *);
   char *buff;
   const char *init_buff; // for checking when resize.
@@ -35,23 +36,21 @@ struct WriteBuffer {
 
   // The stream writer will be called when the buffer is full. It will be passed
   // string_views to write to the stream.
-  StreamWriter stream_writer;
+  const StreamWriter stream_writer;
   void *output_target;
-  WriteMode write_mode;
 
-  LIBC_INLINE WriteBuffer(char *Buff, size_t Buff_len, StreamWriter hook,
+  LIBC_INLINE WriteBuffer(char *buff, size_t buff_len, StreamWriter hook,
                           void *target)
-      : buff(Buff), init_buff(Buff), buff_len(Buff_len), stream_writer(hook),
-        output_target(target), write_mode(WriteMode::FLUSH_TO_STREAM) {}
+      : buff(buff), init_buff(buff), buff_len(buff_len), stream_writer(hook),
+        output_target(target) {}
 
-  LIBC_INLINE WriteBuffer(char *Buff, size_t Buff_len)
-      : buff(Buff), init_buff(Buff), buff_len(Buff_len), stream_writer(nullptr),
-        output_target(nullptr),
-        write_mode(WriteMode::FILL_BUFF_AND_DROP_OVERFLOW) {}
+  LIBC_INLINE WriteBuffer(char *buff, size_t buff_len)
+      : buff(buff), init_buff(buff), buff_len(buff_len), stream_writer(nullptr),
+        output_target(nullptr) {}
 
-  LIBC_INLINE WriteBuffer(char *Buff, size_t Buff_len, StreamWriter hook)
-      : buff(Buff), init_buff(Buff), buff_len(Buff_len), stream_writer(hook),
-        output_target(this), write_mode(WriteMode::RESIZE_AND_FILL_BUFF) {}
+  LIBC_INLINE WriteBuffer(char *buff, size_t buff_len, StreamWriter hook)
+      : buff(buff), init_buff(buff), buff_len(buff_len), stream_writer(hook),
+        output_target(this) {}
 
   LIBC_INLINE int flush_to_stream(cpp::string_view new_str) {
     if (buff_cur > 0) {
@@ -92,40 +91,60 @@ struct WriteBuffer {
   // this with an empty string will flush the buffer if relevant.
 
   LIBC_INLINE int overflow_write(cpp::string_view new_str) {
-    switch (write_mode) {
-    case WriteMode::FILL_BUFF_AND_DROP_OVERFLOW:
+    if constexpr (write_mode == WriteMode::FILL_BUFF_AND_DROP_OVERFLOW)
       return fill_remaining_to_buff(new_str);
-    case WriteMode::FLUSH_TO_STREAM:
+    else if constexpr (write_mode == WriteMode::FLUSH_TO_STREAM)
       return flush_to_stream(new_str);
-    case WriteMode::RESIZE_AND_FILL_BUFF:
+    else if constexpr (write_mode == WriteMode::RESIZE_AND_FILL_BUFF)
       return resize_and_write(new_str);
-    }
     __builtin_unreachable();
   }
 };
 
-class Writer final {
-  WriteBuffer *wb;
+template <WriteMode write_mode> class Writer final {
+  WriteBuffer<write_mode> &wb;
   int chars_written = 0;
 
-  // This is a separate, non-inlined function so that the inlined part of the
-  // write function is shorter.
-  int pad(char new_char, size_t length);
+  LIBC_INLINE int pad(char new_char, size_t length) {
+    // First, fill as much of the buffer as possible with the padding char.
+    size_t written = 0;
+    const size_t buff_space = wb.buff_len - wb.buff_cur;
+    // ASSERT: length > buff_space
+    if (buff_space > 0) {
+      inline_memset(wb.buff + wb.buff_cur, new_char, buff_space);
+      wb.buff_cur += buff_space;
+      written = buff_space;
+    }
+
+    // Next, overflow write the rest of length using the mini_buff.
+    constexpr size_t MINI_BUFF_SIZE = 64;
+    char mini_buff[MINI_BUFF_SIZE];
+    inline_memset(mini_buff, new_char, MINI_BUFF_SIZE);
+    cpp::string_view mb_string_view(mini_buff, MINI_BUFF_SIZE);
+    while (written + MINI_BUFF_SIZE < length) {
+      int result = wb.overflow_write(mb_string_view);
+      if (result != WRITE_OK)
+        return result;
+      written += MINI_BUFF_SIZE;
+    }
+    cpp::string_view mb_substr = mb_string_view.substr(0, length - written);
+    return wb.overflow_write(mb_substr);
+  }
 
 public:
-  LIBC_INLINE Writer(WriteBuffer *WB) : wb(WB) {}
+  LIBC_INLINE Writer(WriteBuffer<write_mode> &wb) : wb(wb) {}
 
   // Takes a string, copies it into the buffer if there is space, else passes it
   // to the overflow mechanism to be handled separately.
   LIBC_INLINE int write(cpp::string_view new_string) {
     chars_written += static_cast<int>(new_string.size());
-    if (LIBC_LIKELY(wb->buff_cur + new_string.size() <= wb->buff_len)) {
-      inline_memcpy(wb->buff + wb->buff_cur, new_string.data(),
+    if (LIBC_LIKELY(wb.buff_cur + new_string.size() <= wb.buff_len)) {
+      inline_memcpy(wb.buff + wb.buff_cur, new_string.data(),
                     new_string.size());
-      wb->buff_cur += new_string.size();
+      wb.buff_cur += new_string.size();
       return WRITE_OK;
     }
-    return wb->overflow_write(new_string);
+    return wb.overflow_write(new_string);
   }
 
   // Takes a char and a length, memsets the next length characters of the buffer
@@ -134,9 +153,9 @@ class Writer final {
   LIBC_INLINE int write(char new_char, size_t length) {
     chars_written += static_cast<int>(length);
 
-    if (LIBC_LIKELY(wb->buff_cur + length <= wb->buff_len)) {
-      inline_memset(wb->buff + wb->buff_cur, new_char, length);
-      wb->buff_cur += length;
+    if (LIBC_LIKELY(wb.buff_cur + length <= wb.buff_len)) {
+      inline_memset(wb.buff + wb.buff_cur, new_char, length);
+      wb.buff_cur += length;
       return WRITE_OK;
     }
     return pad(new_char, length);
@@ -146,18 +165,26 @@ class Writer final {
   // to the overflow mechanism to be handled separately.
   LIBC_INLINE int write(char new_char) {
     chars_written += 1;
-    if (LIBC_LIKELY(wb->buff_cur + 1 <= wb->buff_len)) {
-      wb->buff[wb->buff_cur] = new_char;
-      wb->buff_cur += 1;
+    if (LIBC_LIKELY(wb.buff_cur + 1 <= wb.buff_len)) {
+      wb.buff[wb.buff_cur] = new_char;
+      wb.buff_cur += 1;
       return WRITE_OK;
     }
     cpp::string_view char_string_view(&new_char, 1);
-    return wb->overflow_write(char_string_view);
+    return wb.overflow_write(char_string_view);
   }
 
   LIBC_INLINE int get_chars_written() { return chars_written; }
 };
 
+// Class-template auto deduction helpers.
+Writer(WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>)
+    -> Writer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>;
+Writer(WriteBuffer<WriteMode::RESIZE_AND_FILL_BUFF>)
+    -> Writer<WriteMode::RESIZE_AND_FILL_BUFF>;
+Writer(WriteBuffer<WriteMode::FLUSH_TO_STREAM>)
+    -> Writer<WriteMode::FLUSH_TO_STREAM>;
+
 } // namespace printf_core
 } // namespace LIBC_NAMESPACE_DECL
 
diff --git a/libc/src/stdio/snprintf.cpp b/libc/src/stdio/snprintf.cpp
index 12ad3cd1f762b2..8ddb1cbba3d9cc 100644
--- a/libc/src/stdio/snprintf.cpp
+++ b/libc/src/stdio/snprintf.cpp
@@ -27,8 +27,9 @@ LLVM_LIBC_FUNCTION(int, snprintf,
                                  // 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);
+  printf_core::WriteBuffer<printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>
+      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.
diff --git a/libc/src/stdio/sprintf.cpp b/libc/src/stdio/sprintf.cpp
index 1f59e6bae4723c..13c472ada6250f 100644
--- a/libc/src/stdio/sprintf.cpp
+++ b/libc/src/stdio/sprintf.cpp
@@ -28,8 +28,9 @@ LLVM_LIBC_FUNCTION(int, sprintf,
                                  // destruction automatically.
   va_end(vlist);
 
-  printf_core::WriteBuffer wb(buffer, cpp::numeric_limits<size_t>::max());
-  printf_core::Writer writer(&wb);
+  printf_core::WriteBuffer<printf_core::WriteMode::RESIZE_AND_FILL_BUFF> 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';
diff --git a/libc/src/stdio/vsnprintf.cpp b/libc/src/stdio/vsnprintf.cpp
index a584c76833a2dd..12192e03577368 100644
--- a/libc/src/stdio/vsnprintf.cpp
+++ b/libc/src/stdio/vsnprintf.cpp
@@ -24,8 +24,9 @@ LLVM_LIBC_FUNCTION(int, vsnprintf,
   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);
+  printf_core::WriteBuffer<printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>
+      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.
diff --git a/libc/src/stdio/vsprintf.cpp b/libc/src/stdio/vsprintf.cpp
index b3978a09c671ab..989a803a4e0bcf 100644
--- a/libc/src/stdio/vsprintf.cpp
+++ b/libc/src/stdio/vsprintf.cpp
@@ -25,8 +25,9 @@ LLVM_LIBC_FUNCTION(int, vsprintf,
                                  // 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);
+  printf_core::WriteBuffer<printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>
+      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';
diff --git a/libc/src/stdlib/str_from_util.h b/libc/src/stdlib/str_from_util.h
index 7f54bdf71a0182..61e6ba24b38177 100644
--- a/libc/src/stdlib/str_from_util.h
+++ b/libc/src/stdlib/str_from_util.h
@@ -104,8 +104,8 @@ printf_core::FormatSection parse_format_string(const char *__restrict format,
   return section;
 }
 
-template <typename T>
-int strfromfloat_convert(printf_core::Writer *writer,
+template <typename T, printf_core::WriteMode write_mode>
+int strfromfloat_convert(printf_core::Writer<write_mode> *writer,
                          const printf_core::FormatSection &section) {
   if (!section.has_conv)
     return writer->write(section.raw_string);
diff --git a/libc/src/stdlib/strfromd.cpp b/libc/src/stdlib/strfromd.cpp
index 4c51e4c5c8a011..7e03f25cdc5901 100644
--- a/libc/src/stdlib/strfromd.cpp
+++ b/libc/src/stdlib/strfromd.cpp
@@ -19,8 +19,9 @@ LLVM_LIBC_FUNCTION(int, strfromd,
 
   printf_core::FormatSection section =
       internal::parse_format_string(format, fp);
-  printf_core::WriteBuffer wb(s, (n > 0 ? n - 1 : 0));
-  printf_core::Writer writer(&wb);
+  printf_core::WriteBuffer<printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>
+      wb(s, (n > 0 ? n - 1 : 0));
+  printf_core::Writer writer(wb);
 
   int result = 0;
   if (section.has_conv)
diff --git a/libc/src/stdlib/strfromf.cpp b/libc/src/stdlib/strfromf.cpp
index ea98a69ee4d608..488ff19caa1f0b 100644
--- a/libc/src/stdlib/strfromf.cpp
+++ b/libc/src/stdlib/strfromf.cpp
@@ -19,8 +19,9 @@ LLVM_LIBC_FUNCTION(int, strfromf,
 
   printf_core::FormatSection section =
       internal::parse_format_string(format, fp);
-  printf_core::WriteBuffer wb(s, (n > 0 ? n - 1 : 0));
-  printf_core::Writer writer(&wb);
+  printf_core::WriteBuffer<printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>
+      wb(s, (n > 0 ? n - 1 : 0));
+  printf_core::Writer writer(wb);
 
   int result = 0;
   if (section.has_conv)
diff --git a/libc/src/stdlib/strfroml.cpp b/libc/src/stdlib/strfroml.cpp
index d5bee7609f69ca..259dba95395ab0 100644
--- a/libc/src/stdlib/strfroml.cpp
+++ b/libc/src/stdlib/strfroml.cpp
@@ -24,8 +24,9 @@ LLVM_LIBC_FUNCTION(int, strfroml,
   // the length modifier has to be set to LenghtModifier::L
   section.length_modifier = printf_core::LengthModifier::L;
 
-  printf_core::WriteBuffer wb(s, (n > 0 ? n - 1 : 0));
-  printf_core::Writer writer(&wb);
+  printf_core::WriteBuffer<printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>
+      wb(s, (n > 0 ? n - 1 : 0));
+  printf_core::Writer writer(wb);
 
   int result = 0;
   if (section.has_conv)
diff --git a/libc/test/src/stdio/printf_core/converter_test.cpp b/libc/test/src/stdio/printf_core/converter_test.cpp
index 9da749f3b8ad1a..96a00ae598ec22 100644
--- a/libc/test/src/stdio/printf_core/converter_test.cpp
+++ b/libc/test/src/stdio/printf_core/converter_test.cpp
@@ -18,10 +18,14 @@ class LlvmLibcPrintfConverterTest : public LIBC_NAMESPACE::testing::Test {
   // void TearDown() override {}
 
   char str[60];
-  LIBC_NAMESPACE::printf_core::WriteBuffer wb =
-      LIBC_NAMESPACE::printf_core::WriteBuffer(str, sizeof(str) - 1);
-  LIBC_NAMESPACE::printf_core::Writer writer =
-      LIBC_NAMESPACE::printf_core::Writer(&wb);
+  LIBC_NAMESPACE::printf_core::WriteBuffer<
+      LIBC_NAMESPACE::printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>
+      wb = LIBC_NAMESPACE::printf_core::WriteBuffer<
+          LIBC_NAMESPACE::printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>(
+          str, sizeof(str) - 1);
+  LIBC_NAMESPACE::printf_core::Writer<
+      LIBC_NAMESPACE::printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>
+      writer = LIBC_NAMESPACE::printf_core::Writer(wb);
 };
 
 TEST_F(LlvmLibcPrintfConverterTest, SimpleRawConversion) {
diff --git a/libc/test/src/stdio/printf_core/writer_test.cpp b/libc/test/src/stdio/printf_core/writer_test.cpp
index 4fe5ffb4aa9891..8611caa2dfa58d 100644
--- a/libc/test/src/stdio/printf_core/writer_test.cpp
+++ b/libc/test/src/stdio/printf_core/writer_test.cpp
@@ -15,19 +15,20 @@
 
 using LIBC_NAMESPACE::cpp::string_view;
 using LIBC_NAMESPACE::printf_core::WriteBuffer;
+using LIBC_NAMESPACE::printf_core::WriteMode;
 using LIBC_NAMESPACE::printf_core::Writer;
 
 TEST(LlvmLibcPrintfWriterTest, Constructor) {
   char str[10];
-  WriteBuffer wb(str, sizeof(str) - 1);
-  Writer writer(&wb);
+  WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
+  Writer writer(wb);
   (void)writer;
 }
 
 TEST(LlvmLibcPrintfWriterTest, Write) {
   char str[4] = {'D', 'E', 'F', 'G'};
-  WriteBuffer wb(str, sizeof(str) - 1);
-  Writer writer(&wb);
+  WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
+  Writer writer(wb);
   writer.write({"abc", 3});
 
   EXPECT_EQ(str[3], 'G');
@@ -42,8 +43,8 @@ TEST(LlvmLibcPrintfWriterTest, Write) {
 
 TEST(LlvmLibcPrintfWriterTest, WriteMultipleTimes) {
   char str[10];
-  WriteBuffer wb(str, sizeof(str) - 1);
-  Writer writer(&wb);
+  WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
+  Writer writer(wb);
   writer.write({"abc", 3});
   writer.write({"DEF", 3});
   writer.write({"1234", 3});
@@ -56,8 +57,8 @@ TEST(LlvmLibcPrintfWriterTest, WriteMultipleTimes) {
 
 TEST(LlvmLibcPrintfWriterTest, WriteChars) {
   char str[4] = {'D', 'E', 'F', 'G'};
-  WriteBuffer wb(str, sizeof(str) - 1);
-  Writer writer(&wb);
+  WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
+  Writer writer(wb);
   writer.write('a', 3);
 
   EXPECT_EQ(str[3], 'G');
@@ -69,8 +70,8 @@ TEST(LlvmLibcPrintfWriterTest, WriteChars) {
 
 TEST(LlvmLibcPrintfWriterTest, WriteCharsMultipleTimes) {
   char str[10];
-  WriteBuffer wb(str, sizeof(str) - 1);
-  Writer writer(&wb);
+  WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
+  Writer writer(wb);
   writer.write('a', 3);
   writer.write('D', 3);
   writer.write('1', 3);
@@ -83,8 +84,8 @@ TEST(LlvmLibcPrintfWriterTest, WriteCharsMultipleTimes) {
 
 TEST(LlvmLibcPrintfWriterTest, WriteManyChars) {
   char str[100];
-  WriteBuffer wb(str, sizeof(str) - 1);
-  Writer writer(&wb);
+  WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
+  Writer writer(wb);
   writer.write('Z', 99);
 
   wb.buff[wb.buff_cur] = '\0';
@@ -105,8 +106,8 @@ TEST(LlvmLibcPrintfWriterTest, WriteManyChars) {
 
 TEST(LlvmLibcPrintfWriterTest, MixedWrites) {
   char str[13];
-  WriteBuffer wb(str, sizeof(str) - 1);
-  Writer writer(&wb);
+  WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
+  Writer writer(wb);
   writer.write('a', 3);
   writer.write({"DEF", 3});
   writer.write('1', 3);
@@ -120,8 +121,8 @@ TEST(LlvmLibcPrintfWriterTest, MixedWrites) {
 
 TEST(LlvmLibcPrintfWriterTest, WriteWithMaxLength) {
   char str[11];
-  WriteBuffer wb(str, sizeof(str) - 1);
-  Writer writer(&wb);
+  WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
+  Writer writer(wb);
   writer.write({"abcDEF123456", 12});
 
   wb.buff[wb.buff_cur] = '\0';
@@ -132,8 +133,8 @@ TEST(LlvmLibcPrintfWriterTest, WriteWithMaxLength) {
 
 TEST(LlvmLibcPrintfWriterTest, WriteCharsWithMaxLength) {
   char str[11];
-  WriteBuffer wb(str, sizeof(str) - 1);
-  Writer writer(&wb);
+  WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
+  Writer writer(wb);
   writer.write('1', 15);
 
   wb.buff[wb.buff_cur] = '\0';
@@ -144,9 +145,9 @@ TEST(LlvmLibcPrintfWriterTest, WriteCharsWithMaxLength) {
 
 TEST(LlvmLibcPrintfWriterTest, MixedWriteWithMaxLength) {
   char str[11];
-  WriteBuffer wb(str, sizeof(str) - 1);
+  WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
 
-  Writer writer(&wb);
+  Writer writer(wb);
   writer.write('a', 3);
   writer.write({"DEF", 3});
   writer.write('1', 3);
@@ -162,9 +163,9 @@ TEST(LlvmLibcPrintfWriterTest, StringWithMaxLengthOne) {
   char str[1];
   // This is because the max length should be at most 1 less than the size of
   // the buffer it's writing to.
-  WriteBuffer wb(str, 0);
+  WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, 0);
 
-  Writer writer(&wb);
+  Writer writer(wb);
   writer.write('a', 3);
   writer.write({"DEF", 3});
   writer.write('1', 3);
@@ -177,9 +178,9 @@ TEST(LlvmLibcPrintfWriterTest, StringWithMaxLengthOne) {
 }
 
 TEST(LlvmLibcPrintfWriterTest, NullStringWithZeroMaxLength) {
-  WriteBuffer wb(nullptr, 0);
+  WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(nullptr, 0);
 
-  Writer writer(&wb);
+  Writer writer(wb);
   writer.write('a', 3);
   writer.write({"DEF", 3});
   writer.write('1', 3);
@@ -213,9 +214,10 @@ TEST(LlvmLibcPrintfWriterTest, WriteWithMaxLengthWithCallback) {
   OutBuff out_buff = {str, 0};
 
   char wb_buff[8];
-  WriteBuffer wb(wb_buff, sizeof(wb_buff), &copy_to_out,
-                 reinterpret_cast<void *>(&out_buff));
-  Writer writer(&wb);
+  WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb(
+      wb_buff, sizeof(wb_buff), &copy_to_out,
+      reinterpret_cast<void *>(&out_buff));
+  Writer writer(wb);
   writer.write({"abcDEF123456", 12});
 
   // Flush the buffer
@@ -232,9 +234,10 @@ TEST(LlvmLibcPrintfWriterTest, WriteCharsWithMaxLengthWithCallback) {
   OutBuff out_buff = {str, 0};
 
   char wb_buff[8];
-  WriteBuffer wb(wb_buff, sizeof(wb_buff), &copy_to_out,
-                 reinterpret_cast<void *>(&out_buff));
-  Writer writer(&wb);
+  WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb(
+      wb_buff, sizeof(wb_buff), &copy_to_out,
+      reinterpret_cast<void *>(&out_buff));
+  Writer writer(wb);
   writer.write('1', 15);
 
   // Flush the buffer
@@ -251,9 +254,10 @@ TEST(LlvmLibcPrintfWriterTest, MixedWriteWithMaxLengthWithCallback) {
   OutBuff out_buff = {str, 0};
 
   char wb_buff[8];
-  WriteBuffer wb(wb_buff, sizeof(wb_buff), &copy_to_out,
-                 reinterpret_cast<void *>(&out_buff));
-  Writer writer(&wb);
+  WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb(
+      wb_buff, sizeof(wb_buff), &copy_to_out,
+      reinterpret_cast<void *>(&out_buff));
+  Writer writer(wb);
   writer.write('a', 3);
   writer.write({"DEF", 3});
   writer.write('1', 3);
@@ -273,9 +277,10 @@ TEST(LlvmLibcPrintfWriterTest, ZeroLengthBufferWithCallback) {
   OutBuff out_buff = {str, 0};
 
   char wb_buff[1];
-  WriteBuffer wb(wb_buff, 0, &copy_to_out, reinterpret_cast<void *>(&out_buff));
+  WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb(
+      wb_buff, 0, &copy_to_out, reinterpret_cast<void *>(&out_buff));
 
-  Writer writer(&wb);
+  Writer writer(wb);
   writer.write('a', 3);
   writer.write({"DEF", 3});
   writer.write('1', 3);
@@ -294,9 +299,10 @@ TEST(LlvmLibcPrintfWriterTest, NullStringWithZeroMaxLengthWithCallback) {
 
   OutBuff out_buff = {str, 0};
 
-  WriteBuffer wb(nullptr, 0, &copy_to_out, reinterpret_cast<void *>(&out_buff));
+  WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb(
+      nullptr, 0, &copy_to_out, reinterpret_cast<void *>(&out_buff));
 
-  Writer writer(&wb);
+  Writer writer(wb);
   writer.write('a', 3);
   writer.write({"DEF", 3});
   writer.write('1', 3);
diff --git a/libc/utils/gpu/server/CMakeLists.txt b/libc/utils/gpu/server/CMakeLists.txt
index 50056fb376b699..01e0b62eb0ebcf 100644
--- a/libc/utils/gpu/server/CMakeLists.txt
+++ b/libc/utils/gpu/server/CMakeLists.txt
@@ -1,8 +1,4 @@
-add_library(llvmlibc_rpc_server STATIC
-  ${LIBC_SOURCE_DIR}/src/stdio/printf_core/writer.cpp
-  ${LIBC_SOURCE_DIR}/src/stdio/printf_core/converter.cpp
-  rpc_server.cpp
-)
+add_library(llvmlibc_rpc_server STATIC rpc_server.cpp)
 
 # Include the RPC implemenation from libc.
 target_include_directories(llvmlibc_rpc_server PRIVATE ${LIBC_SOURCE_DIR})
diff --git a/libc/utils/gpu/server/rpc_server.cpp b/libc/utils/gpu/server/rpc_server.cpp
index ca10e67509ae63..cd393effd01778 100644
--- a/libc/utils/gpu/server/rpc_server.cpp
+++ b/libc/utils/gpu/server/rpc_server.cpp
@@ -83,8 +83,8 @@ static void handle_printf(rpc::Server::Port &port, TempStorage &temp_storage) {
     if (!format[lane])
       continue;
 
-    WriteBuffer wb(nullptr, 0);
-    Writer writer(&wb);
+    WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(nullptr, 0);
+    Writer writer(wb);
 
     internal::DummyArgList<packed> printf_args;
     Parser<internal::DummyArgList<packed> &> parser(
@@ -110,8 +110,8 @@ static void handle_printf(rpc::Server::Port &port, TempStorage &temp_storage) {
     if (!format[lane])
       continue;
 
-    WriteBuffer wb(nullptr, 0);
-    Writer writer(&wb);
+    WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(nullptr, 0);
+    Writer writer(wb);
 
     internal::StructArgList<packed> printf_args(args[lane], args_sizes[lane]);
     Parser<internal::StructArgList<packed>> parser(
@@ -167,8 +167,9 @@ static void handle_printf(rpc::Server::Port &port, TempStorage &temp_storage) {
       continue;
 
     char *buffer = temp_storage.alloc(buffer_size[lane]);
-    WriteBuffer wb(buffer, buffer_size[lane]);
-    Writer writer(&wb);
+    WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(buffer,
+                                                           buffer_size[lane]);
+    Writer writer(wb);
 
     internal::StructArgList<packed> printf_args(args[lane], args_sizes[lane]);
     Parser<internal::StructArgList<packed>> parser(



More information about the libc-commits mailing list