Skip to content

Commit f4fd007

Browse files
authored
[Support] Make format_object non-virtual (#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.
1 parent 92f01b2 commit f4fd007

5 files changed

Lines changed: 44 additions & 61 deletions

File tree

‎clang/tools/libclang/CIndex.cpp‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10179,11 +10179,6 @@ Logger &cxindex::Logger::operator<<(CXString Str) {
1017910179
return *this;
1018010180
}
1018110181

10182-
Logger &cxindex::Logger::operator<<(const llvm::format_object_base &Fmt) {
10183-
LogOS << Fmt;
10184-
return *this;
10185-
}
10186-
1018710182
static llvm::ManagedStatic<std::mutex> LoggingMutex;
1018810183

1018910184
cxindex::Logger::~Logger() {

‎clang/tools/libclang/CLog.h‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "llvm/ADT/SmallString.h"
1717
#include "llvm/ADT/StringRef.h"
1818
#include "llvm/Support/Compiler.h"
19+
#include "llvm/Support/Format.h"
1920
#include "llvm/Support/raw_ostream.h"
2021
#include <string>
2122

@@ -82,7 +83,12 @@ class Logger : public RefCountedBase<Logger> {
8283
Logger &operator<<(char C) { LogOS << C; return *this; }
8384
Logger &operator<<(unsigned char C) { LogOS << C; return *this; }
8485
Logger &operator<<(signed char C) { LogOS << C; return *this; }
85-
Logger &operator<<(const llvm::format_object_base &Fmt);
86+
87+
template <typename... Ts>
88+
Logger &operator<<(const llvm::format_object<Ts...> &Fmt) {
89+
LogOS << Fmt;
90+
return *this;
91+
}
8692
};
8793

8894
}

‎llvm/include/llvm/Support/Format.h‎

Lines changed: 31 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "llvm/ADT/StringRef.h"
2828
#include "llvm/Support/Compiler.h"
2929
#include "llvm/Support/DataTypes.h"
30+
#include "llvm/Support/raw_ostream.h"
3031
#include <cassert>
3132
#include <cstdio>
3233
#include <optional>
@@ -35,44 +36,6 @@
3536

3637
namespace llvm {
3738

38-
/// This is a helper class used for handling formatted output. It is the
39-
/// abstract base class of a templated derived class.
40-
class LLVM_ABI format_object_base {
41-
protected:
42-
const char *Fmt;
43-
~format_object_base() = default; // Disallow polymorphic deletion.
44-
format_object_base(const format_object_base &) = default;
45-
virtual void home(); // Out of line virtual method.
46-
47-
/// Call snprintf() for this object, on the given buffer and size.
48-
virtual int snprint(char *Buffer, unsigned BufferSize) const = 0;
49-
50-
public:
51-
format_object_base(const char *fmt) : Fmt(fmt) {}
52-
53-
/// Format the object into the specified buffer. On success, this returns
54-
/// the length of the formatted string. If the buffer is too small, this
55-
/// returns a length to retry with, which will be larger than BufferSize.
56-
unsigned print(char *Buffer, unsigned BufferSize) const {
57-
assert(BufferSize && "Invalid buffer size!");
58-
59-
// Print the string, leaving room for the terminating null.
60-
int N = snprint(Buffer, BufferSize);
61-
62-
// VC++ and old GlibC return negative on overflow, just double the size.
63-
if (N < 0)
64-
return BufferSize * 2;
65-
66-
// Other implementations yield number of bytes needed, not including the
67-
// final '\0'.
68-
if (unsigned(N) >= BufferSize)
69-
return N + 1;
70-
71-
// Otherwise N is the length of output (not including the final '\0').
72-
return N;
73-
}
74-
};
75-
7639
/// These are templated helper classes used by the format function that
7740
/// capture the object to be formatted and the format string. When actually
7841
/// printed, this synthesizes the string into a temporary buffer provided and
@@ -89,8 +52,8 @@ template <typename T>
8952
using decay_if_c_char_array_t = typename decay_if_c_char_array<T>::type;
9053
} // namespace detail
9154

92-
template <typename... Ts>
93-
class format_object final : public format_object_base {
55+
template <typename... Ts> class format_object {
56+
const char *Fmt;
9457
std::tuple<detail::decay_if_c_char_array_t<Ts>...> Vals;
9558

9659
template <std::size_t... Is>
@@ -104,18 +67,43 @@ class format_object final : public format_object_base {
10467
}
10568

10669
public:
107-
format_object(const char *fmt, const Ts &... vals)
108-
: format_object_base(fmt), Vals(vals...) {
70+
format_object(const char *fmt, const Ts &...vals) : Fmt(fmt), Vals(vals...) {
10971
static_assert(
11072
(std::is_scalar_v<detail::decay_if_c_char_array_t<Ts>> && ...),
11173
"format can't be used with non fundamental / non pointer type");
11274
}
11375

114-
int snprint(char *Buffer, unsigned BufferSize) const override {
76+
int snprint(char *Buffer, unsigned BufferSize) const {
11577
return snprint_tuple(Buffer, BufferSize, std::index_sequence_for<Ts...>());
11678
}
79+
80+
unsigned print(char *Buffer, unsigned BufferSize) const {
81+
assert(BufferSize && "Invalid buffer size!");
82+
83+
// Print the string, leaving room for the terminating null.
84+
int N = snprint(Buffer, BufferSize);
85+
86+
// VC++ and old GlibC return negative on overflow, just double the size.
87+
if (N < 0)
88+
return BufferSize * 2;
89+
90+
// Other implementations yield number of bytes needed, not including the
91+
// final '\0'.
92+
if (unsigned(N) >= BufferSize)
93+
return N + 1;
94+
95+
// Otherwise N is the length of output (not including the final '\0').
96+
return N;
97+
}
11798
};
11899

100+
template <typename... Ts>
101+
raw_ostream &operator<<(raw_ostream &OS, format_object<Ts...> Fmt) {
102+
OS <<
103+
[&Fmt](char *Buf, size_t Size) -> size_t { return Fmt.print(Buf, Size); };
104+
return OS;
105+
}
106+
119107
/// These are helper functions used to produce formatted output. They use
120108
/// template type deduction to construct the appropriate instance of the
121109
/// format_object class to simplify their construction.

‎llvm/include/llvm/Support/raw_ostream.h‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#ifndef LLVM_SUPPORT_RAW_OSTREAM_H
1414
#define LLVM_SUPPORT_RAW_OSTREAM_H
1515

16+
#include "llvm/ADT/STLFunctionalExtras.h"
1617
#include "llvm/ADT/SmallVector.h"
1718
#include "llvm/ADT/StringRef.h"
1819
#include "llvm/Support/Compiler.h"
@@ -305,7 +306,7 @@ class LLVM_ABI raw_ostream {
305306
raw_ostream &write(const char *Ptr, size_t Size);
306307

307308
// Formatted output, see the format() function in Support/Format.h.
308-
raw_ostream &operator<<(const format_object_base &Fmt);
309+
raw_ostream &operator<<(function_ref<size_t(char *, size_t)> Print);
309310

310311
// Formatted output, see the leftJustify() function in Support/Format.h.
311312
raw_ostream &operator<<(const FormattedString &);

‎llvm/lib/Support/raw_ostream.cpp‎

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -295,13 +295,14 @@ void raw_ostream::copy_to_buffer(const char *Ptr, size_t Size) {
295295
}
296296

297297
// Formatted output.
298-
raw_ostream &raw_ostream::operator<<(const format_object_base &Fmt) {
298+
raw_ostream &
299+
raw_ostream::operator<<(function_ref<size_t(char *, size_t)> Print) {
299300
// If we have more than a few bytes left in our output buffer, try
300301
// formatting directly onto its end.
301302
size_t NextBufferSize = 127;
302303
size_t BufferBytesLeft = OutBufEnd - OutBufCur;
303304
if (BufferBytesLeft > 3) {
304-
size_t BytesUsed = Fmt.print(OutBufCur, BufferBytesLeft);
305+
size_t BytesUsed = Print(OutBufCur, BufferBytesLeft);
305306

306307
// Common case is that we have plenty of space.
307308
if (BytesUsed <= BufferBytesLeft) {
@@ -323,7 +324,7 @@ raw_ostream &raw_ostream::operator<<(const format_object_base &Fmt) {
323324
V.resize(NextBufferSize);
324325

325326
// Try formatting into the SmallVector.
326-
size_t BytesUsed = Fmt.print(V.data(), NextBufferSize);
327+
size_t BytesUsed = Print(V.data(), NextBufferSize);
327328

328329
// If BytesUsed fit into the vector, we win.
329330
if (BytesUsed <= NextBufferSize)
@@ -540,14 +541,6 @@ raw_ostream &raw_ostream::reverseColor() {
540541

541542
void raw_ostream::anchor() {}
542543

543-
//===----------------------------------------------------------------------===//
544-
// Formatted Output
545-
//===----------------------------------------------------------------------===//
546-
547-
// Out of line virtual method.
548-
void format_object_base::home() {
549-
}
550-
551544
//===----------------------------------------------------------------------===//
552545
// raw_fd_ostream
553546
//===----------------------------------------------------------------------===//

0 commit comments

Comments
 (0)