about summary refs log tree commit diff
path: root/absl
diff options
context:
space:
mode:
Diffstat (limited to 'absl')
-rw-r--r--absl/base/internal/low_level_alloc.cc14
-rw-r--r--absl/base/internal/low_level_alloc.h7
-rw-r--r--absl/base/internal/thread_identity.cc10
-rw-r--r--absl/memory/memory_test.cc1
-rw-r--r--absl/strings/internal/str_format/arg.cc80
-rw-r--r--absl/strings/internal/str_format/arg.h210
-rw-r--r--absl/strings/internal/str_format/bind.h2
-rw-r--r--absl/strings/internal/str_format/extension.h10
-rw-r--r--absl/strings/str_format_test.cc36
9 files changed, 199 insertions, 171 deletions
diff --git a/absl/base/internal/low_level_alloc.cc b/absl/base/internal/low_level_alloc.cc
index 0a6f30705345..6e636a02c5d1 100644
--- a/absl/base/internal/low_level_alloc.cc
+++ b/absl/base/internal/low_level_alloc.cc
@@ -401,16 +401,20 @@ bool LowLevelAlloc::DeleteArena(Arena *arena) {
     ABSL_RAW_CHECK(munmap_result != 0,
                    "LowLevelAlloc::DeleteArena: VitualFree failed");
 #else
+#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
     if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) == 0) {
       munmap_result = munmap(region, size);
     } else {
       munmap_result = base_internal::DirectMunmap(region, size);
     }
+#else
+    munmap_result = munmap(region, size);
+#endif  // ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
     if (munmap_result != 0) {
       ABSL_RAW_LOG(FATAL, "LowLevelAlloc::DeleteArena: munmap failed: %d",
                    errno);
     }
-#endif
+#endif  // _WIN32
   }
   section.Leave();
   arena->~Arena();
@@ -545,6 +549,7 @@ static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) {
                                MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
       ABSL_RAW_CHECK(new_pages != nullptr, "VirtualAlloc failed");
 #else
+#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
       if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) != 0) {
         new_pages = base_internal::DirectMmap(nullptr, new_pages_size,
             PROT_WRITE|PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
@@ -552,10 +557,15 @@ static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) {
         new_pages = mmap(nullptr, new_pages_size, PROT_WRITE | PROT_READ,
                          MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
       }
+#else
+      new_pages = mmap(nullptr, new_pages_size, PROT_WRITE | PROT_READ,
+                       MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+#endif  // ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
       if (new_pages == MAP_FAILED) {
         ABSL_RAW_LOG(FATAL, "mmap error: %d", errno);
       }
-#endif
+
+#endif  // _WIN32
       arena->mu.Lock();
       s = reinterpret_cast<AllocList *>(new_pages);
       s->header.size = new_pages_size;
diff --git a/absl/base/internal/low_level_alloc.h b/absl/base/internal/low_level_alloc.h
index 3c15605bed35..fba9466a7574 100644
--- a/absl/base/internal/low_level_alloc.h
+++ b/absl/base/internal/low_level_alloc.h
@@ -39,10 +39,13 @@
 #define ABSL_LOW_LEVEL_ALLOC_MISSING 1
 #endif
 
-// Using LowLevelAlloc with kAsyncSignalSafe isn't supported on Windows.
+// Using LowLevelAlloc with kAsyncSignalSafe isn't supported on Windows or
+// asm.js / WebAssembly.
+// See https://kripken.github.io/emscripten-site/docs/porting/pthreads.html
+// for more information.
 #ifdef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
 #error ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING cannot be directly set
-#elif defined(_WIN32)
+#elif defined(_WIN32) || defined(__asmjs__) || defined(__wasm__)
 #define ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING 1
 #endif
 
diff --git a/absl/base/internal/thread_identity.cc b/absl/base/internal/thread_identity.cc
index 678e8568d742..cff9c1b4f4dc 100644
--- a/absl/base/internal/thread_identity.cc
+++ b/absl/base/internal/thread_identity.cc
@@ -68,6 +68,14 @@ void SetCurrentThreadIdentity(
   // NOTE: Not async-safe.  But can be open-coded.
   absl::call_once(init_thread_identity_key_once, AllocateThreadIdentityKey,
                   reclaimer);
+
+#ifdef __EMSCRIPTEN__
+  // Emscripten PThread implementation does not support signals.
+  // See https://kripken.github.io/emscripten-site/docs/porting/pthreads.html
+  // for more information.
+  pthread_setspecific(thread_identity_pthread_key,
+                      reinterpret_cast<void*>(identity));
+#else
   // We must mask signals around the call to setspecific as with current glibc,
   // a concurrent getspecific (needed for GetCurrentThreadIdentityIfPresent())
   // may zero our value.
@@ -81,6 +89,8 @@ void SetCurrentThreadIdentity(
   pthread_setspecific(thread_identity_pthread_key,
                       reinterpret_cast<void*>(identity));
   pthread_sigmask(SIG_SETMASK, &curr_signals, nullptr);
+#endif  // !__EMSCRIPTEN__
+
 #elif ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS
   // NOTE: Not async-safe.  But can be open-coded.
   absl::call_once(init_thread_identity_key_once, AllocateThreadIdentityKey,
diff --git a/absl/memory/memory_test.cc b/absl/memory/memory_test.cc
index dee9b486a30d..2e453e22ba5b 100644
--- a/absl/memory/memory_test.cc
+++ b/absl/memory/memory_test.cc
@@ -149,7 +149,6 @@ TEST(Make_UniqueTest, NotAmbiguousWithStdMakeUnique) {
 }
 
 #if 0
-// TODO(billydonahue): Make a proper NC test.
 // These tests shouldn't compile.
 TEST(MakeUniqueTestNC, AcceptMoveOnlyLvalue) {
   auto m = MoveOnly();
diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc
index eafb068fe286..b40be8ff3824 100644
--- a/absl/strings/internal/str_format/arg.cc
+++ b/absl/strings/internal/str_format/arg.cc
@@ -112,7 +112,7 @@ class ConvertedIntInfo {
 // Note: 'o' conversions do not have a base indicator, it's just that
 // the '#' flag is specified to modify the precision for 'o' conversions.
 string_view BaseIndicator(const ConvertedIntInfo &info,
-                          const ConversionSpec &conv) {
+                          const ConversionSpec conv) {
   bool alt = conv.flags().alt;
   int radix = conv.conv().radix();
   if (conv.conv().id() == ConversionChar::p)
@@ -127,7 +127,7 @@ string_view BaseIndicator(const ConvertedIntInfo &info,
   return {};
 }
 
-string_view SignColumn(bool neg, const ConversionSpec &conv) {
+string_view SignColumn(bool neg, const ConversionSpec conv) {
   if (conv.conv().is_signed()) {
     if (neg) return "-";
     if (conv.flags().show_pos) return "+";
@@ -136,7 +136,7 @@ string_view SignColumn(bool neg, const ConversionSpec &conv) {
   return {};
 }
 
-bool ConvertCharImpl(unsigned char v, const ConversionSpec &conv,
+bool ConvertCharImpl(unsigned char v, const ConversionSpec conv,
                      FormatSinkImpl *sink) {
   size_t fill = 0;
   if (conv.width() >= 0) fill = conv.width();
@@ -148,7 +148,7 @@ bool ConvertCharImpl(unsigned char v, const ConversionSpec &conv,
 }
 
 bool ConvertIntImplInner(const ConvertedIntInfo &info,
-                         const ConversionSpec &conv, FormatSinkImpl *sink) {
+                         const ConversionSpec conv, FormatSinkImpl *sink) {
   // Print as a sequence of Substrings:
   //   [left_spaces][sign][base_indicator][zeroes][formatted][right_spaces]
   size_t fill = 0;
@@ -202,8 +202,7 @@ bool ConvertIntImplInner(const ConvertedIntInfo &info,
 }
 
 template <typename T>
-bool ConvertIntImplInner(T v, const ConversionSpec &conv,
-                         FormatSinkImpl *sink) {
+bool ConvertIntImplInner(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
   ConvertedIntInfo info(v, conv.conv());
   if (conv.flags().basic && conv.conv().id() != ConversionChar::p) {
     if (info.is_neg()) sink->Append(1, '-');
@@ -218,7 +217,7 @@ bool ConvertIntImplInner(T v, const ConversionSpec &conv,
 }
 
 template <typename T>
-bool ConvertIntArg(T v, const ConversionSpec &conv, FormatSinkImpl *sink) {
+bool ConvertIntArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
   if (conv.conv().is_float()) {
     return FormatConvertImpl(static_cast<double>(v), conv, sink).value;
   }
@@ -234,11 +233,11 @@ bool ConvertIntArg(T v, const ConversionSpec &conv, FormatSinkImpl *sink) {
 }
 
 template <typename T>
-bool ConvertFloatArg(T v, const ConversionSpec &conv, FormatSinkImpl *sink) {
+bool ConvertFloatArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
   return conv.conv().is_float() && ConvertFloatImpl(v, conv, sink);
 }
 
-inline bool ConvertStringArg(string_view v, const ConversionSpec &conv,
+inline bool ConvertStringArg(string_view v, const ConversionSpec conv,
                              FormatSinkImpl *sink) {
   if (conv.conv().id() != ConversionChar::s)
     return false;
@@ -254,19 +253,19 @@ inline bool ConvertStringArg(string_view v, const ConversionSpec &conv,
 
 // ==================== Strings ====================
 ConvertResult<Conv::s> FormatConvertImpl(const std::string &v,
-                                         const ConversionSpec &conv,
+                                         const ConversionSpec conv,
                                          FormatSinkImpl *sink) {
   return {ConvertStringArg(v, conv, sink)};
 }
 
 ConvertResult<Conv::s> FormatConvertImpl(string_view v,
-                                         const ConversionSpec &conv,
+                                         const ConversionSpec conv,
                                          FormatSinkImpl *sink) {
   return {ConvertStringArg(v, conv, sink)};
 }
 
 ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v,
-                                                   const ConversionSpec &conv,
+                                                   const ConversionSpec conv,
                                                    FormatSinkImpl *sink) {
   if (conv.conv().id() == ConversionChar::p)
     return {FormatConvertImpl(VoidPtr(v), conv, sink).value};
@@ -283,7 +282,7 @@ ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v,
 }
 
 // ==================== Raw pointers ====================
-ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec &conv,
+ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec conv,
                                          FormatSinkImpl *sink) {
   if (conv.conv().id() != ConversionChar::p)
     return {false};
@@ -295,104 +294,83 @@ ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec &conv,
 }
 
 // ==================== Floats ====================
-FloatingConvertResult FormatConvertImpl(float v, const ConversionSpec &conv,
+FloatingConvertResult FormatConvertImpl(float v, const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertFloatArg(v, conv, sink)};
 }
-FloatingConvertResult FormatConvertImpl(double v, const ConversionSpec &conv,
+FloatingConvertResult FormatConvertImpl(double v, const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertFloatArg(v, conv, sink)};
 }
 FloatingConvertResult FormatConvertImpl(long double v,
-                                        const ConversionSpec &conv,
+                                        const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertFloatArg(v, conv, sink)};
 }
 
 // ==================== Chars ====================
-IntegralConvertResult FormatConvertImpl(char v, const ConversionSpec &conv,
+IntegralConvertResult FormatConvertImpl(char v, const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
 IntegralConvertResult FormatConvertImpl(signed char v,
-                                        const ConversionSpec &conv,
+                                        const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
 IntegralConvertResult FormatConvertImpl(unsigned char v,
-                                        const ConversionSpec &conv,
+                                        const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
 
 // ==================== Ints ====================
 IntegralConvertResult FormatConvertImpl(short v,  // NOLINT
-                                        const ConversionSpec &conv,
+                                        const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
 IntegralConvertResult FormatConvertImpl(unsigned short v,  // NOLINT
-                                        const ConversionSpec &conv,
+                                        const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
-IntegralConvertResult FormatConvertImpl(int v, const ConversionSpec &conv,
+IntegralConvertResult FormatConvertImpl(int v, const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
-IntegralConvertResult FormatConvertImpl(unsigned v, const ConversionSpec &conv,
+IntegralConvertResult FormatConvertImpl(unsigned v, const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
 IntegralConvertResult FormatConvertImpl(long v,  // NOLINT
-                                        const ConversionSpec &conv,
+                                        const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
 IntegralConvertResult FormatConvertImpl(unsigned long v,  // NOLINT
-                                        const ConversionSpec &conv,
+                                        const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
 IntegralConvertResult FormatConvertImpl(long long v,  // NOLINT
-                                        const ConversionSpec &conv,
+                                        const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
 IntegralConvertResult FormatConvertImpl(unsigned long long v,  // NOLINT
-                                        const ConversionSpec &conv,
+                                        const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
 IntegralConvertResult FormatConvertImpl(absl::uint128 v,
-                                        const ConversionSpec &conv,
+                                        const ConversionSpec conv,
                                         FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
 
-template struct FormatArgImpl::TypedVTable<str_format_internal::VoidPtr>;
-
-template struct FormatArgImpl::TypedVTable<bool>;
-template struct FormatArgImpl::TypedVTable<char>;
-template struct FormatArgImpl::TypedVTable<signed char>;
-template struct FormatArgImpl::TypedVTable<unsigned char>;
-template struct FormatArgImpl::TypedVTable<short>;           // NOLINT
-template struct FormatArgImpl::TypedVTable<unsigned short>;  // NOLINT
-template struct FormatArgImpl::TypedVTable<int>;
-template struct FormatArgImpl::TypedVTable<unsigned>;
-template struct FormatArgImpl::TypedVTable<long>;                // NOLINT
-template struct FormatArgImpl::TypedVTable<unsigned long>;       // NOLINT
-template struct FormatArgImpl::TypedVTable<long long>;           // NOLINT
-template struct FormatArgImpl::TypedVTable<unsigned long long>;  // NOLINT
-template struct FormatArgImpl::TypedVTable<absl::uint128>;
-
-template struct FormatArgImpl::TypedVTable<float>;
-template struct FormatArgImpl::TypedVTable<double>;
-template struct FormatArgImpl::TypedVTable<long double>;
-
-template struct FormatArgImpl::TypedVTable<const char *>;
-template struct FormatArgImpl::TypedVTable<std::string>;
-template struct FormatArgImpl::TypedVTable<string_view>;
+ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_();
+
 
 }  // namespace str_format_internal
 
diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h
index a9562188ea91..3376d48afda2 100644
--- a/absl/strings/internal/str_format/arg.h
+++ b/absl/strings/internal/str_format/arg.h
@@ -33,7 +33,7 @@ struct HasUserDefinedConvert : std::false_type {};
 template <typename T>
 struct HasUserDefinedConvert<
     T, void_t<decltype(AbslFormatConvert(
-           std::declval<const T&>(), std::declval<const ConversionSpec&>(),
+           std::declval<const T&>(), std::declval<ConversionSpec>(),
            std::declval<FormatSink*>()))>> : std::true_type {};
 template <typename T>
 class StreamedWrapper;
@@ -50,25 +50,23 @@ struct VoidPtr {
       : value(ptr ? reinterpret_cast<uintptr_t>(ptr) : 0) {}
   uintptr_t value;
 };
-ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec& conv,
+ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, ConversionSpec conv,
                                          FormatSinkImpl* sink);
 
 // Strings.
-ConvertResult<Conv::s> FormatConvertImpl(const std::string& v,
-                                         const ConversionSpec& conv,
+ConvertResult<Conv::s> FormatConvertImpl(const std::string& v, ConversionSpec conv,
                                          FormatSinkImpl* sink);
-ConvertResult<Conv::s> FormatConvertImpl(string_view v,
-                                         const ConversionSpec& conv,
+ConvertResult<Conv::s> FormatConvertImpl(string_view v, ConversionSpec conv,
                                          FormatSinkImpl* sink);
 ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char* v,
-                                                   const ConversionSpec& conv,
+                                                   ConversionSpec conv,
                                                    FormatSinkImpl* sink);
 template <class AbslCord,
           typename std::enable_if<
               std::is_same<AbslCord, ::Cord>::value>::type* = nullptr,
           class AbslCordReader = ::CordReader>
 ConvertResult<Conv::s> FormatConvertImpl(const AbslCord& value,
-                                         const ConversionSpec& conv,
+                                         ConversionSpec conv,
                                          FormatSinkImpl* sink) {
   if (conv.conv().id() != ConversionChar::s) return {false};
 
@@ -104,51 +102,48 @@ using IntegralConvertResult =
 using FloatingConvertResult = ConvertResult<Conv::floating>;
 
 // Floats.
-FloatingConvertResult FormatConvertImpl(float v, const ConversionSpec& conv,
+FloatingConvertResult FormatConvertImpl(float v, ConversionSpec conv,
                                         FormatSinkImpl* sink);
-FloatingConvertResult FormatConvertImpl(double v, const ConversionSpec& conv,
+FloatingConvertResult FormatConvertImpl(double v, ConversionSpec conv,
                                         FormatSinkImpl* sink);
-FloatingConvertResult FormatConvertImpl(long double v,
-                                        const ConversionSpec& conv,
+FloatingConvertResult FormatConvertImpl(long double v, ConversionSpec conv,
                                         FormatSinkImpl* sink);
 
 // Chars.
-IntegralConvertResult FormatConvertImpl(char v, const ConversionSpec& conv,
+IntegralConvertResult FormatConvertImpl(char v, ConversionSpec conv,
                                         FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(signed char v,
-                                        const ConversionSpec& conv,
+IntegralConvertResult FormatConvertImpl(signed char v, ConversionSpec conv,
                                         FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(unsigned char v,
-                                        const ConversionSpec& conv,
+IntegralConvertResult FormatConvertImpl(unsigned char v, ConversionSpec conv,
                                         FormatSinkImpl* sink);
 
 // Ints.
 IntegralConvertResult FormatConvertImpl(short v,  // NOLINT
-                                        const ConversionSpec& conv,
+                                        ConversionSpec conv,
                                         FormatSinkImpl* sink);
 IntegralConvertResult FormatConvertImpl(unsigned short v,  // NOLINT
-                                        const ConversionSpec& conv,
+                                        ConversionSpec conv,
                                         FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(int v, const ConversionSpec& conv,
+IntegralConvertResult FormatConvertImpl(int v, ConversionSpec conv,
                                         FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(unsigned v, const ConversionSpec& conv,
+IntegralConvertResult FormatConvertImpl(unsigned v, ConversionSpec conv,
                                         FormatSinkImpl* sink);
 IntegralConvertResult FormatConvertImpl(long v,  // NOLINT
-                                        const ConversionSpec& conv,
+                                        ConversionSpec conv,
                                         FormatSinkImpl* sink);
 IntegralConvertResult FormatConvertImpl(unsigned long v,  // NOLINT
-                                        const ConversionSpec& conv,
+                                        ConversionSpec conv,
                                         FormatSinkImpl* sink);
 IntegralConvertResult FormatConvertImpl(long long v,  // NOLINT
-                                        const ConversionSpec& conv,
+                                        ConversionSpec conv,
                                         FormatSinkImpl* sink);
 IntegralConvertResult FormatConvertImpl(unsigned long long v,  // NOLINT
-                                        const ConversionSpec& conv,
+                                        ConversionSpec conv,
                                         FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(uint128 v, const ConversionSpec& conv,
+IntegralConvertResult FormatConvertImpl(uint128 v, ConversionSpec conv,
                                         FormatSinkImpl* sink);
 template <typename T, enable_if_t<std::is_same<T, bool>::value, int> = 0>
-IntegralConvertResult FormatConvertImpl(T v, const ConversionSpec& conv,
+IntegralConvertResult FormatConvertImpl(T v, ConversionSpec conv,
                                         FormatSinkImpl* sink) {
   return FormatConvertImpl(static_cast<int>(v), conv, sink);
 }
@@ -159,11 +154,11 @@ template <typename T>
 typename std::enable_if<std::is_enum<T>::value &&
                             !HasUserDefinedConvert<T>::value,
                         IntegralConvertResult>::type
-FormatConvertImpl(T v, const ConversionSpec& conv, FormatSinkImpl* sink);
+FormatConvertImpl(T v, ConversionSpec conv, FormatSinkImpl* sink);
 
 template <typename T>
 ConvertResult<Conv::s> FormatConvertImpl(const StreamedWrapper<T>& v,
-                                         const ConversionSpec& conv,
+                                         ConversionSpec conv,
                                          FormatSinkImpl* out) {
   std::ostringstream oss;
   oss << v.v_;
@@ -176,7 +171,7 @@ ConvertResult<Conv::s> FormatConvertImpl(const StreamedWrapper<T>& v,
 struct FormatCountCaptureHelper {
   template <class T = int>
   static ConvertResult<Conv::n> ConvertHelper(const FormatCountCapture& v,
-                                              const ConversionSpec& conv,
+                                              ConversionSpec conv,
                                               FormatSinkImpl* sink) {
     const absl::enable_if_t<sizeof(T) != 0, FormatCountCapture>& v2 = v;
 
@@ -189,7 +184,7 @@ struct FormatCountCaptureHelper {
 
 template <class T = int>
 ConvertResult<Conv::n> FormatConvertImpl(const FormatCountCapture& v,
-                                         const ConversionSpec& conv,
+                                         ConversionSpec conv,
                                          FormatSinkImpl* sink) {
   return FormatCountCaptureHelper::ConvertHelper(v, conv, sink);
 }
@@ -199,20 +194,20 @@ ConvertResult<Conv::n> FormatConvertImpl(const FormatCountCapture& v,
 struct FormatArgImplFriend {
   template <typename Arg>
   static bool ToInt(Arg arg, int* out) {
-    if (!arg.vtbl_->to_int) return false;
-    *out = arg.vtbl_->to_int(arg.data_);
-    return true;
+    // A value initialized ConversionSpec has a `none` conv, which tells the
+    // dispatcher to run the `int` conversion.
+    return arg.dispatcher_(arg.data_, {}, out);
   }
 
   template <typename Arg>
-  static bool Convert(Arg arg, const str_format_internal::ConversionSpec& conv,
+  static bool Convert(Arg arg, str_format_internal::ConversionSpec conv,
                       FormatSinkImpl* out) {
-    return arg.vtbl_->convert(arg.data_, conv, out);
+    return arg.dispatcher_(arg.data_, conv, out);
   }
 
   template <typename Arg>
-  static const void* GetVTablePtrForTest(Arg arg) {
-    return arg.vtbl_;
+  static typename Arg::Dispatcher GetVTablePtrForTest(Arg arg) {
+    return arg.dispatcher_;
   }
 };
 
@@ -229,11 +224,7 @@ class FormatArgImpl {
     char buf[kInlinedSpace];
   };
 
-  struct VTable {
-    bool (*convert)(Data, const str_format_internal::ConversionSpec& conv,
-                    FormatSinkImpl* out);
-    int (*to_int)(Data);
-  };
+  using Dispatcher = bool (*)(Data, ConversionSpec, void* out);
 
   template <typename T>
   struct store_by_value
@@ -253,10 +244,6 @@ class FormatArgImpl {
                                                                 : ByPointer))> {
   };
 
-  // An instance of an FormatArgImpl::VTable suitable for 'T'.
-  template <typename T>
-  struct TypedVTable;
-
   // To reduce the number of vtables we will decay values before hand.
   // Anything with a user-defined Convert will get its own vtable.
   // For everything else:
@@ -338,7 +325,10 @@ class FormatArgImpl {
   };
 
   template <typename T>
-  void Init(const T& value);
+  void Init(const T& value) {
+    data_ = Manager<T>::SetValue(value);
+    dispatcher_ = &Dispatch<T>;
+  }
 
   template <typename T>
   static int ToIntVal(const T& val) {
@@ -355,79 +345,75 @@ class FormatArgImpl {
     return static_cast<int>(val);
   }
 
-  Data data_;
-  const VTable* vtbl_;
-};
-
-template <typename T>
-struct FormatArgImpl::TypedVTable {
- private:
-  static bool ConvertImpl(Data arg,
-                          const str_format_internal::ConversionSpec& conv,
-                          FormatSinkImpl* out) {
-    return str_format_internal::FormatConvertImpl(Manager<T>::Value(arg), conv,
-                                                  out)
-        .value;
+  template <typename T>
+  static bool ToInt(Data arg, int* out, std::true_type /* is_integral */,
+                    std::false_type) {
+    *out = ToIntVal(Manager<T>::Value(arg));
+    return true;
   }
 
-  template <typename U = T, typename = void>
-  struct ToIntImpl {
-    static constexpr int (*value)(Data) = nullptr;
-  };
+  template <typename T>
+  static bool ToInt(Data arg, int* out, std::false_type,
+                    std::true_type /* is_enum */) {
+    *out = ToIntVal(static_cast<typename std::underlying_type<T>::type>(
+        Manager<T>::Value(arg)));
+    return true;
+  }
 
-  template <typename U>
-  struct ToIntImpl<U,
-                   typename std::enable_if<std::is_integral<U>::value>::type> {
-    static int Invoke(Data arg) { return ToIntVal(Manager<T>::Value(arg)); }
-    static constexpr int (*value)(Data) = &Invoke;
-  };
+  template <typename T>
+  static bool ToInt(Data, int*, std::false_type, std::false_type) {
+    return false;
+  }
 
-  template <typename U>
-  struct ToIntImpl<U, typename std::enable_if<std::is_enum<U>::value>::type> {
-    static int Invoke(Data arg) {
-      return ToIntVal(static_cast<typename std::underlying_type<T>::type>(
-          Manager<T>::Value(arg)));
+  template <typename T>
+  static bool Dispatch(Data arg, ConversionSpec spec, void* out) {
+    // A `none` conv indicates that we want the `int` conversion.
+    if (ABSL_PREDICT_FALSE(spec.conv().id() == ConversionChar::none)) {
+      return ToInt<T>(arg, static_cast<int*>(out), std::is_integral<T>(),
+                      std::is_enum<T>());
     }
-    static constexpr int (*value)(Data) = &Invoke;
-  };
 
- public:
-  static constexpr VTable value{&ConvertImpl, ToIntImpl<>::value};
-};
+    return str_format_internal::FormatConvertImpl(
+               Manager<T>::Value(arg), spec, static_cast<FormatSinkImpl*>(out))
+        .value;
+  }
 
-template <typename T>
-constexpr FormatArgImpl::VTable FormatArgImpl::TypedVTable<T>::value;
+  Data data_;
+  Dispatcher dispatcher_;
+};
 
-template <typename T>
-void FormatArgImpl::Init(const T& value) {
-  data_ = Manager<T>::SetValue(value);
-  vtbl_ = &TypedVTable<T>::value;
-}
+#define ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(T, E) \
+  E template bool FormatArgImpl::Dispatch<T>(Data, ConversionSpec, void*)
+
+#define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...)                   \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(str_format_internal::VoidPtr,     \
+                                             __VA_ARGS__);                     \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(bool, __VA_ARGS__);               \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(char, __VA_ARGS__);               \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(signed char, __VA_ARGS__);        \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned char, __VA_ARGS__);      \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(short, __VA_ARGS__); /* NOLINT */ \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned short,      /* NOLINT */ \
+                                             __VA_ARGS__);                     \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(int, __VA_ARGS__);                \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned int, __VA_ARGS__);       \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long, __VA_ARGS__); /* NOLINT */  \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned long,      /* NOLINT */  \
+                                             __VA_ARGS__);                     \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long long, /* NOLINT */           \
+                                             __VA_ARGS__);                     \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned long long, /* NOLINT */  \
+                                             __VA_ARGS__);                     \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(uint128, __VA_ARGS__);            \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(float, __VA_ARGS__);              \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(double, __VA_ARGS__);             \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long double, __VA_ARGS__);        \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(const char*, __VA_ARGS__);        \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::string, __VA_ARGS__);             \
+  ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(string_view, __VA_ARGS__)
+
+ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(extern);
 
-extern template struct FormatArgImpl::TypedVTable<str_format_internal::VoidPtr>;
-
-extern template struct FormatArgImpl::TypedVTable<bool>;
-extern template struct FormatArgImpl::TypedVTable<char>;
-extern template struct FormatArgImpl::TypedVTable<signed char>;
-extern template struct FormatArgImpl::TypedVTable<unsigned char>;
-extern template struct FormatArgImpl::TypedVTable<short>;           // NOLINT
-extern template struct FormatArgImpl::TypedVTable<unsigned short>;  // NOLINT
-extern template struct FormatArgImpl::TypedVTable<int>;
-extern template struct FormatArgImpl::TypedVTable<unsigned>;
-extern template struct FormatArgImpl::TypedVTable<long>;           // NOLINT
-extern template struct FormatArgImpl::TypedVTable<unsigned long>;  // NOLINT
-extern template struct FormatArgImpl::TypedVTable<long long>;      // NOLINT
-extern template struct FormatArgImpl::TypedVTable<
-    unsigned long long>;  // NOLINT
-extern template struct FormatArgImpl::TypedVTable<uint128>;
-
-extern template struct FormatArgImpl::TypedVTable<float>;
-extern template struct FormatArgImpl::TypedVTable<double>;
-extern template struct FormatArgImpl::TypedVTable<long double>;
-
-extern template struct FormatArgImpl::TypedVTable<const char*>;
-extern template struct FormatArgImpl::TypedVTable<std::string>;
-extern template struct FormatArgImpl::TypedVTable<string_view>;
 }  // namespace str_format_internal
 }  // namespace absl
 
diff --git a/absl/strings/internal/str_format/bind.h b/absl/strings/internal/str_format/bind.h
index a503b19bb675..1b52df9c7f56 100644
--- a/absl/strings/internal/str_format/bind.h
+++ b/absl/strings/internal/str_format/bind.h
@@ -186,7 +186,7 @@ class StreamedWrapper {
  private:
   template <typename S>
   friend ConvertResult<Conv::s> FormatConvertImpl(const StreamedWrapper<S>& v,
-                                                  const ConversionSpec& conv,
+                                                  ConversionSpec conv,
                                                   FormatSinkImpl* out);
   const T& v_;
 };
diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h
index f43195c127b1..11b996aefd37 100644
--- a/absl/strings/internal/str_format/extension.h
+++ b/absl/strings/internal/str_format/extension.h
@@ -18,6 +18,7 @@
 #define ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
 
 #include <limits.h>
+#include <cstddef>
 #include <cstring>
 #include <ostream>
 
@@ -307,7 +308,12 @@ class ConversionSpec {
  public:
   Flags flags() const { return flags_; }
   LengthMod length_mod() const { return length_mod_; }
-  ConversionChar conv() const { return conv_; }
+  ConversionChar conv() const {
+    // Keep this field first in the struct . It generates better code when
+    // accessing it when ConversionSpec is passed by value in registers.
+    static_assert(offsetof(ConversionSpec, conv_) == 0, "");
+    return conv_;
+  }
 
   // Returns the specified width. If width is unspecfied, it returns a negative
   // value.
@@ -324,9 +330,9 @@ class ConversionSpec {
   void set_left(bool b) { flags_.left = b; }
 
  private:
+  ConversionChar conv_;
   Flags flags_;
   LengthMod length_mod_;
-  ConversionChar conv_;
   int width_;
   int precision_;
 };
diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc
index 9a6576dc8252..aa14e211709a 100644
--- a/absl/strings/str_format_test.cc
+++ b/absl/strings/str_format_test.cc
@@ -601,3 +601,39 @@ TEST_F(ParsedFormatTest, RegressionMixPositional) {
 
 }  // namespace
 }  // namespace absl
+
+// Some codegen thunks that we can use to easily dump the generated assembly for
+// different StrFormat calls.
+
+inline std::string CodegenAbslStrFormatInt(int i) {
+  return absl::StrFormat("%d", i);
+}
+
+inline std::string CodegenAbslStrFormatIntStringInt64(int i, const std::string& s,
+                                                 int64_t i64) {
+  return absl::StrFormat("%d %s %d", i, s, i64);
+}
+
+inline void CodegenAbslStrAppendFormatInt(std::string* out, int i) {
+  absl::StrAppendFormat(out, "%d", i);
+}
+
+inline void CodegenAbslStrAppendFormatIntStringInt64(std::string* out, int i,
+                                                     const std::string& s,
+                                                     int64_t i64) {
+  absl::StrAppendFormat(out, "%d %s %d", i, s, i64);
+}
+
+auto absl_internal_str_format_force_codegen_funcs = std::make_tuple(
+    CodegenAbslStrFormatInt, CodegenAbslStrFormatIntStringInt64,
+    CodegenAbslStrAppendFormatInt, CodegenAbslStrAppendFormatIntStringInt64);
+
+bool absl_internal_str_format_force_codegen_always_false;
+// Force the compiler to generate the functions by making it look like we
+// escape the function pointers.
+// It can't statically know that
+// absl_internal_str_format_force_codegen_always_false is not changed by someone
+// else.
+bool absl_internal_str_format_force_codegen =
+    absl_internal_str_format_force_codegen_always_false &&
+    printf("%p", &absl_internal_str_format_force_codegen_funcs) == 0;