https://github.com/aengelke created 
https://github.com/llvm/llvm-project/pull/206319

Currently, format_object creates a 32B vtable for every instantiation.
This is costs space and dynamic relocations. Make format_object
non-virtual and adapt the two printing users to use a function_ref
instead.


>From 6cdebc210ce08f67eb7cafa24b2e63c14df77080 Mon Sep 17 00:00:00 2001
From: Alexis Engelke <[email protected]>
Date: Sun, 28 Jun 2026 08:45:20 +0000
Subject: [PATCH] [spr] initial version

Created using spr 1.3.8-wip
---
 clang/tools/libclang/CIndex.cpp         |  5 --
 clang/tools/libclang/CLog.h             |  8 ++-
 llvm/include/llvm/Support/Format.h      | 74 +++++++++++--------------
 llvm/include/llvm/Support/raw_ostream.h |  3 +-
 llvm/lib/Support/raw_ostream.cpp        | 15 ++---
 5 files changed, 44 insertions(+), 61 deletions(-)

diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index ac2fad38a1348..9bc2f66d7f75f 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -10179,11 +10179,6 @@ Logger &cxindex::Logger::operator<<(CXString Str) {
   return *this;
 }
 
-Logger &cxindex::Logger::operator<<(const llvm::format_object_base &Fmt) {
-  LogOS << Fmt;
-  return *this;
-}
-
 static llvm::ManagedStatic<std::mutex> LoggingMutex;
 
 cxindex::Logger::~Logger() {
diff --git a/clang/tools/libclang/CLog.h b/clang/tools/libclang/CLog.h
index 6ce43a01ee8f2..a880fb03b4e87 100644
--- a/clang/tools/libclang/CLog.h
+++ b/clang/tools/libclang/CLog.h
@@ -16,6 +16,7 @@
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Compiler.h"
+#include "llvm/Support/Format.h"
 #include "llvm/Support/raw_ostream.h"
 #include <string>
 
@@ -82,7 +83,12 @@ class Logger : public RefCountedBase<Logger> {
   Logger &operator<<(char C) { LogOS << C; return *this; }
   Logger &operator<<(unsigned char C) { LogOS << C; return *this; }
   Logger &operator<<(signed char C) { LogOS << C; return *this; }
-  Logger &operator<<(const llvm::format_object_base &Fmt);
+
+  template <typename... Ts>
+  Logger &operator<<(const llvm::format_object<Ts...> &Fmt) {
+    LogOS << Fmt;
+    return *this;
+  }
 };
 
 }
diff --git a/llvm/include/llvm/Support/Format.h 
b/llvm/include/llvm/Support/Format.h
index b549341a40e7f..fff1bc5464c57 100644
--- a/llvm/include/llvm/Support/Format.h
+++ b/llvm/include/llvm/Support/Format.h
@@ -27,6 +27,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/DataTypes.h"
+#include "llvm/Support/raw_ostream.h"
 #include <cassert>
 #include <cstdio>
 #include <optional>
@@ -35,44 +36,6 @@
 
 namespace llvm {
 
-/// This is a helper class used for handling formatted output.  It is the
-/// abstract base class of a templated derived class.
-class LLVM_ABI format_object_base {
-protected:
-  const char *Fmt;
-  ~format_object_base() = default; // Disallow polymorphic deletion.
-  format_object_base(const format_object_base &) = default;
-  virtual void home(); // Out of line virtual method.
-
-  /// Call snprintf() for this object, on the given buffer and size.
-  virtual int snprint(char *Buffer, unsigned BufferSize) const = 0;
-
-public:
-  format_object_base(const char *fmt) : Fmt(fmt) {}
-
-  /// Format the object into the specified buffer.  On success, this returns
-  /// the length of the formatted string.  If the buffer is too small, this
-  /// returns a length to retry with, which will be larger than BufferSize.
-  unsigned print(char *Buffer, unsigned BufferSize) const {
-    assert(BufferSize && "Invalid buffer size!");
-
-    // Print the string, leaving room for the terminating null.
-    int N = snprint(Buffer, BufferSize);
-
-    // VC++ and old GlibC return negative on overflow, just double the size.
-    if (N < 0)
-      return BufferSize * 2;
-
-    // Other implementations yield number of bytes needed, not including the
-    // final '\0'.
-    if (unsigned(N) >= BufferSize)
-      return N + 1;
-
-    // Otherwise N is the length of output (not including the final '\0').
-    return N;
-  }
-};
-
 /// These are templated helper classes used by the format function that
 /// capture the object to be formatted and the format string. When actually
 /// printed, this synthesizes the string into a temporary buffer provided and
@@ -89,8 +52,8 @@ template <typename T>
 using decay_if_c_char_array_t = typename decay_if_c_char_array<T>::type;
 } // namespace detail
 
-template <typename... Ts>
-class format_object final : public format_object_base {
+template <typename... Ts> class format_object {
+  const char *Fmt;
   std::tuple<detail::decay_if_c_char_array_t<Ts>...> Vals;
 
   template <std::size_t... Is>
@@ -104,18 +67,43 @@ class format_object final : public format_object_base {
   }
 
 public:
-  format_object(const char *fmt, const Ts &... vals)
-      : format_object_base(fmt), Vals(vals...) {
+  format_object(const char *fmt, const Ts &...vals) : Fmt(fmt), Vals(vals...) {
     static_assert(
         (std::is_scalar_v<detail::decay_if_c_char_array_t<Ts>> && ...),
         "format can't be used with non fundamental / non pointer type");
   }
 
-  int snprint(char *Buffer, unsigned BufferSize) const override {
+  int snprint(char *Buffer, unsigned BufferSize) const {
     return snprint_tuple(Buffer, BufferSize, std::index_sequence_for<Ts...>());
   }
+
+  unsigned print(char *Buffer, unsigned BufferSize) const {
+    assert(BufferSize && "Invalid buffer size!");
+
+    // Print the string, leaving room for the terminating null.
+    int N = snprint(Buffer, BufferSize);
+
+    // VC++ and old GlibC return negative on overflow, just double the size.
+    if (N < 0)
+      return BufferSize * 2;
+
+    // Other implementations yield number of bytes needed, not including the
+    // final '\0'.
+    if (unsigned(N) >= BufferSize)
+      return N + 1;
+
+    // Otherwise N is the length of output (not including the final '\0').
+    return N;
+  }
 };
 
+template <typename... Ts>
+raw_ostream &operator<<(raw_ostream &OS, format_object<Ts...> Fmt) {
+  OS <<
+      [&Fmt](char *Buf, size_t Size) -> size_t { return Fmt.print(Buf, Size); 
};
+  return OS;
+}
+
 /// These are helper functions used to produce formatted output.  They use
 /// template type deduction to construct the appropriate instance of the
 /// format_object class to simplify their construction.
diff --git a/llvm/include/llvm/Support/raw_ostream.h 
b/llvm/include/llvm/Support/raw_ostream.h
index 1e66052c849b7..afd45465bcd1c 100644
--- a/llvm/include/llvm/Support/raw_ostream.h
+++ b/llvm/include/llvm/Support/raw_ostream.h
@@ -13,6 +13,7 @@
 #ifndef LLVM_SUPPORT_RAW_OSTREAM_H
 #define LLVM_SUPPORT_RAW_OSTREAM_H
 
+#include "llvm/ADT/STLFunctionalExtras.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Compiler.h"
@@ -305,7 +306,7 @@ class LLVM_ABI raw_ostream {
   raw_ostream &write(const char *Ptr, size_t Size);
 
   // Formatted output, see the format() function in Support/Format.h.
-  raw_ostream &operator<<(const format_object_base &Fmt);
+  raw_ostream &operator<<(function_ref<size_t(char *, size_t)> Print);
 
   // Formatted output, see the leftJustify() function in Support/Format.h.
   raw_ostream &operator<<(const FormattedString &);
diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp
index 9b590dbc2ae27..4c99854dd9bd9 100644
--- a/llvm/lib/Support/raw_ostream.cpp
+++ b/llvm/lib/Support/raw_ostream.cpp
@@ -295,13 +295,14 @@ void raw_ostream::copy_to_buffer(const char *Ptr, size_t 
Size) {
 }
 
 // Formatted output.
-raw_ostream &raw_ostream::operator<<(const format_object_base &Fmt) {
+raw_ostream &
+raw_ostream::operator<<(function_ref<size_t(char *, size_t)> Print) {
   // If we have more than a few bytes left in our output buffer, try
   // formatting directly onto its end.
   size_t NextBufferSize = 127;
   size_t BufferBytesLeft = OutBufEnd - OutBufCur;
   if (BufferBytesLeft > 3) {
-    size_t BytesUsed = Fmt.print(OutBufCur, BufferBytesLeft);
+    size_t BytesUsed = Print(OutBufCur, BufferBytesLeft);
 
     // Common case is that we have plenty of space.
     if (BytesUsed <= BufferBytesLeft) {
@@ -323,7 +324,7 @@ raw_ostream &raw_ostream::operator<<(const 
format_object_base &Fmt) {
     V.resize(NextBufferSize);
 
     // Try formatting into the SmallVector.
-    size_t BytesUsed = Fmt.print(V.data(), NextBufferSize);
+    size_t BytesUsed = Print(V.data(), NextBufferSize);
 
     // If BytesUsed fit into the vector, we win.
     if (BytesUsed <= NextBufferSize)
@@ -540,14 +541,6 @@ raw_ostream &raw_ostream::reverseColor() {
 
 void raw_ostream::anchor() {}
 
-//===----------------------------------------------------------------------===//
-//  Formatted Output
-//===----------------------------------------------------------------------===//
-
-// Out of line virtual method.
-void format_object_base::home() {
-}
-
 
//===----------------------------------------------------------------------===//
 //  raw_fd_ostream
 
//===----------------------------------------------------------------------===//

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to