diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h
index 9d2b48e..5c2d376 100644
--- a/absl/algorithm/container.h
+++ b/absl/algorithm/container.h
@@ -1169,7 +1169,9 @@
 // Container-based version of the <algorithm> `std::sort()` function
 // to sort elements in ascending order of their values.
 template <typename C>
-constexpr void c_sort(C& c) {
+constexpr std::enable_if_t<
+    container_algorithm_internal::IsPermissibleDestinationRange<C>::value, void>
+c_sort(C&& c) {
   std::sort(container_algorithm_internal::c_begin(c),
             container_algorithm_internal::c_end(c));
 }
@@ -1177,7 +1179,9 @@
 // Overload of c_sort() for performing a `comp` comparison other than the
 // default `operator<`.
 template <typename C, typename LessThan>
-constexpr void c_sort(C& c, LessThan&& comp) {
+constexpr std::enable_if_t<
+    container_algorithm_internal::IsPermissibleDestinationRange<C>::value, void>
+c_sort(C&& c, LessThan&& comp) {
   std::sort(container_algorithm_internal::c_begin(c),
             container_algorithm_internal::c_end(c),
             std::forward<LessThan>(comp));
@@ -1189,7 +1193,9 @@
 // to sort elements in ascending order of their values, preserving the order
 // of equivalents.
 template <typename C>
-void c_stable_sort(C& c) {
+std::enable_if_t<
+    container_algorithm_internal::IsPermissibleDestinationRange<C>::value, void>
+c_stable_sort(C&& c) {
   std::stable_sort(container_algorithm_internal::c_begin(c),
                    container_algorithm_internal::c_end(c));
 }
@@ -1197,7 +1203,9 @@
 // Overload of c_stable_sort() for performing a `comp` comparison other than the
 // default `operator<`.
 template <typename C, typename LessThan>
-void c_stable_sort(C& c, LessThan&& comp) {
+std::enable_if_t<
+    container_algorithm_internal::IsPermissibleDestinationRange<C>::value, void>
+c_stable_sort(C&& c, LessThan&& comp) {
   std::stable_sort(container_algorithm_internal::c_begin(c),
                    container_algorithm_internal::c_end(c),
                    std::forward<LessThan>(comp));
diff --git a/absl/algorithm/container_test.cc b/absl/algorithm/container_test.cc
index e025382..50a621e 100644
--- a/absl/algorithm/container_test.cc
+++ b/absl/algorithm/container_test.cc
@@ -1338,6 +1338,18 @@
   EXPECT_THAT(test_vector, ElementsAre(4, 3, 2, 1));
 }
 
+TEST(MutatingTest, SortTemporarySpan) {
+  std::vector<int> test_vector = {2, 3, 1, 4};
+  absl::c_sort(absl::MakeSpan(test_vector));
+  EXPECT_THAT(test_vector, ElementsAre(1, 2, 3, 4));
+}
+
+TEST(MutatingTest, SortTemporarySpanWithPredicate) {
+  std::vector<int> test_vector = {2, 3, 1, 4};
+  absl::c_sort(absl::MakeSpan(test_vector), std::greater<int>());
+  EXPECT_THAT(test_vector, ElementsAre(4, 3, 2, 1));
+}
+
 // For absl::c_stable_sort tests. Needs an operator< that does not cover all
 // fields so that the test can check the sort preserves order of equal elements.
 struct Element {
@@ -1374,6 +1386,24 @@
                           IsElement(1, 1), IsElement(1, 0)));
 }
 
+TEST(MutatingTest, StableSortTemporarySpan) {
+  std::vector<Element> test_vector = {{1, 1}, {2, 1}, {2, 0}, {1, 0}, {2, 2}};
+  absl::c_stable_sort(absl::MakeSpan(test_vector));
+  EXPECT_THAT(test_vector,
+              ElementsAre(IsElement(1, 1), IsElement(1, 0), IsElement(2, 1),
+                          IsElement(2, 0), IsElement(2, 2)));
+}
+
+TEST(MutatingTest, StableSortTemporarySpanWithPredicate) {
+  std::vector<Element> test_vector = {{1, 1}, {2, 1}, {2, 0}, {1, 0}, {2, 2}};
+  absl::c_stable_sort(
+      absl::MakeSpan(test_vector),
+      [](const Element& e1, const Element& e2) { return e2 < e1; });
+  EXPECT_THAT(test_vector,
+              ElementsAre(IsElement(2, 1), IsElement(2, 0), IsElement(2, 2),
+                          IsElement(1, 1), IsElement(1, 0)));
+}
+
 TEST(MutatingTest, ReplaceCopyIf) {
   const std::vector<int> initial = {1, 2, 3, 4, 5};
   const std::vector<int> expected = {0, 2, 0, 4, 0};
diff --git a/absl/base/attributes.h b/absl/base/attributes.h
index e432bb7..1e4c7c3 100644
--- a/absl/base/attributes.h
+++ b/absl/base/attributes.h
@@ -88,8 +88,13 @@
 #if ABSL_HAVE_ATTRIBUTE(format) || (defined(__GNUC__) && !defined(__clang__))
 #define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check) \
   __attribute__((__format__(__printf__, string_index, first_to_check)))
+#if ABSL_HAVE_CPP_ATTRIBUTE(gnu::format)
+#define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check) \
+  [[gnu::format(scanf, string_index, first_to_check)]]
+#else
 #define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check) \
   __attribute__((__format__(__scanf__, string_index, first_to_check)))
+#endif
 #else
 #define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check)
 #define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check)
@@ -475,26 +480,16 @@
 // Example:
 //
 //   int foo() ABSL_ATTRIBUTE_HOT;
-#if defined(__cplusplus) && defined(__clang__) && \
-    ABSL_HAVE_CPP_ATTRIBUTE(gnu::hot)
-#define ABSL_ATTRIBUTE_HOT                                       \
-  _Pragma("clang diagnostic push")                               \
-      _Pragma("clang diagnostic error \"-Wignored-attributes\"") \
-          [[gnu::hot]] /*                                     */ \
-          _Pragma("clang diagnostic pop")
+#if defined(__cplusplus) && ABSL_HAVE_CPP_ATTRIBUTE(gnu::hot)
+#define ABSL_ATTRIBUTE_HOT [[gnu::hot]]
 #elif ABSL_HAVE_ATTRIBUTE(hot) || (defined(__GNUC__) && !defined(__clang__))
 #define ABSL_ATTRIBUTE_HOT __attribute__((hot))
 #else
 #define ABSL_ATTRIBUTE_HOT
 #endif
 
-#if defined(__cplusplus) && defined(__clang__) && \
-    ABSL_HAVE_CPP_ATTRIBUTE(gnu::cold)
-#define ABSL_ATTRIBUTE_COLD                                      \
-  _Pragma("clang diagnostic push")                               \
-      _Pragma("clang diagnostic error \"-Wignored-attributes\"") \
-          [[gnu::cold]] /*                                   */  \
-          _Pragma("clang diagnostic pop")
+#if defined(__cplusplus) && ABSL_HAVE_CPP_ATTRIBUTE(gnu::cold)
+#define ABSL_ATTRIBUTE_COLD [[gnu::cold]]
 #elif ABSL_HAVE_ATTRIBUTE(cold) || (defined(__GNUC__) && !defined(__clang__))
 #define ABSL_ATTRIBUTE_COLD __attribute__((cold))
 #else
@@ -851,7 +846,7 @@
 // Note that `ABSL_CONST_INIT` must not be used on a variable declared
 // constexpr.
 #if defined(__cpp_constinit) && __cpp_constinit >= 201907L
-#define ABSL_CONST_INIT constinit
+#define ABSL_CONST_INIT constinit  // NOLINT(clang-diagnostic-pre-c++20-compat)
 #elif ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization)
 #define ABSL_CONST_INIT [[clang::require_constant_initialization]]
 #else
diff --git a/absl/base/internal/cpu_detect.cc b/absl/base/internal/cpu_detect.cc
index b498465..43b6c4b 100644
--- a/absl/base/internal/cpu_detect.cc
+++ b/absl/base/internal/cpu_detect.cc
@@ -231,6 +231,8 @@
       switch (model_num) {
         case 0x2:
           return CpuType::kAmdTurin;
+        case 0x50:
+          return CpuType::kAmdVenice;
         default:
           return CpuType::kUnknown;
       }
diff --git a/absl/base/internal/cpu_detect.h b/absl/base/internal/cpu_detect.h
index 5ca993f..06eb72a 100644
--- a/absl/base/internal/cpu_detect.h
+++ b/absl/base/internal/cpu_detect.h
@@ -32,6 +32,7 @@
   kAmdGenoa,
   kAmdSiena,
   kAmdTurin,
+  kAmdVenice,
   kAmdRyzenV3000,
   kIntelCascadelakeXeon,
   kIntelSkylakeXeon,
diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc
index 7c39f6d..848e69f 100644
--- a/absl/base/internal/raw_logging.cc
+++ b/absl/base/internal/raw_logging.cc
@@ -89,8 +89,8 @@
 // consumed bytes, and return whether the message fit without truncation.  If
 // truncation occurred, if possible leave room in the buffer for the message
 // kTruncated[].
-bool VADoRawLog(char** buf, int* size, const char* format, va_list ap)
-    ABSL_PRINTF_ATTRIBUTE(3, 0);
+ABSL_PRINTF_ATTRIBUTE(3, 0)
+bool VADoRawLog(char** buf, int* size, const char* format, va_list ap);
 bool VADoRawLog(char** buf, int* size, const char* format, va_list ap) {
   if (*size < 0) return false;
   int n = vsnprintf(*buf, static_cast<size_t>(*size), format, ap);
@@ -117,8 +117,8 @@
 // Helper for RawLog below.
 // *DoRawLog writes to *buf of *size and move them past the written portion.
 // It returns true iff there was no overflow or error.
-bool DoRawLog(char** buf, int* size, const char* format, ...)
-    ABSL_PRINTF_ATTRIBUTE(3, 4);
+ABSL_PRINTF_ATTRIBUTE(3, 4)
+bool DoRawLog(char** buf, int* size, const char* format, ...);
 bool DoRawLog(char** buf, int* size, const char* format, ...) {
   if (*size < 0) return false;
   va_list ap;
@@ -143,8 +143,9 @@
 ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES
 absl::base_internal::AtomicHook<AbortHook> abort_hook;
 
+ABSL_PRINTF_ATTRIBUTE(4, 0)
 void RawLogVA(absl::LogSeverity severity, const char* file, int line,
-              const char* format, va_list ap) ABSL_PRINTF_ATTRIBUTE(4, 0);
+              const char* format, va_list ap);
 void RawLogVA(absl::LogSeverity severity, const char* file, int line,
               const char* format, va_list ap) {
   char buffer[kLogBufSize];
diff --git a/absl/base/internal/raw_logging.h b/absl/base/internal/raw_logging.h
index 8637030..9cc270b 100644
--- a/absl/base/internal/raw_logging.h
+++ b/absl/base/internal/raw_logging.h
@@ -128,8 +128,9 @@
 // Logs format... at "severity" level, reporting it
 // as called from file:line.
 // This does not allocate memory or acquire locks.
+ABSL_PRINTF_ATTRIBUTE(4, 5)
 void RawLog(absl::LogSeverity severity, const char* file, int line,
-            const char* format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5);
+            const char* format, ...);
 
 // Writes the provided buffer directly to stderr, in a signal-safe, low-level
 // manner.  Preserves errno.
diff --git a/absl/base/thread_annotations.h b/absl/base/thread_annotations.h
index 4a3f3e3..cede48c 100644
--- a/absl/base/thread_annotations.h
+++ b/absl/base/thread_annotations.h
@@ -188,7 +188,9 @@
 // acquired, and the destructor should use `UNLOCK_FUNCTION()` with no
 // arguments; the analysis will assume that the destructor unlocks whatever the
 // constructor locked.
-#if ABSL_HAVE_ATTRIBUTE(scoped_lockable)
+#if ABSL_HAVE_CPP_ATTRIBUTE(clang::scoped_lockable)
+#define ABSL_SCOPED_LOCKABLE [[clang::scoped_lockable]]
+#elif ABSL_HAVE_ATTRIBUTE(scoped_lockable)
 #define ABSL_SCOPED_LOCKABLE __attribute__((scoped_lockable))
 #else
 #define ABSL_SCOPED_LOCKABLE
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel
index d325e39..850eaec 100644
--- a/absl/container/BUILD.bazel
+++ b/absl/container/BUILD.bazel
@@ -1488,6 +1488,7 @@
         "//absl/base:core_headers",
         "//absl/base:hardening",
         "//absl/base:iterator_traits_internal",
+        "//absl/cleanup",
     ],
 )
 
diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt
index 79bcee0..7a2641c 100644
--- a/absl/container/CMakeLists.txt
+++ b/absl/container/CMakeLists.txt
@@ -1239,6 +1239,7 @@
   LINKOPTS
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
+     absl::cleanup
      absl::config
      absl::core_headers
      absl::hardening
diff --git a/absl/container/chunked_queue.h b/absl/container/chunked_queue.h
index 8ada208..54cb6d8 100644
--- a/absl/container/chunked_queue.h
+++ b/absl/container/chunked_queue.h
@@ -35,6 +35,8 @@
 //   shrink_to_fit()
 //   resize()
 //   assign()
+//   push_front()
+//   emplace_front()
 //   push_back()
 //   emplace_back()
 //   pop_front()
@@ -110,6 +112,7 @@
 #include "absl/base/internal/hardening.h"
 #include "absl/base/internal/iterator_traits.h"
 #include "absl/base/macros.h"
+#include "absl/cleanup/cleanup.h"
 #include "absl/container/internal/chunked_queue.h"
 #include "absl/container/internal/layout.h"
 
@@ -142,20 +145,20 @@
 
    protected:
     iterator_common() = default;
-    explicit iterator_common(Block* b)
-        : block(b), ptr(b->start()), limit(b->limit()) {}
+    explicit iterator_common(Block* b) : block(b), ptr(b->start()) {}
 
     void Incr() {
       // If we do not have a next block, make ptr point one past the end of this
       // block. If we do have a next block, make ptr point to the first element
       // of the next block.
       ++ptr;
-      if (ptr == limit && block->next()) *this = iterator_common(block->next());
+      if (ptr == block->limit() && block->next())
+        *this = iterator_common(block->next());
     }
 
     void IncrBy(size_t n) {
-      while (n > static_cast<size_t>(limit - ptr)) {
-        n -= static_cast<size_t>(limit - ptr);
+      while (n > static_cast<size_t>(block->limit() - ptr)) {
+        n -= static_cast<size_t>(block->limit() - ptr);
         *this = iterator_common(block->next());
       }
       ptr += n;
@@ -163,7 +166,6 @@
 
     Block* block = nullptr;
     T* ptr = nullptr;
-    T* limit = nullptr;
   };
 
   // CT can be either T or const T.
@@ -403,6 +405,20 @@
   // Replaces the contents with the elements from the initializer list `il`.
   void assign(std::initializer_list<T> il) { assign(il.begin(), il.end()); }
 
+  // Prepends the given element value to the beginning of the container.
+  // Invalidates `end()` iterator if the container was empty. References to
+  // other elements remain valid.
+  void push_front(const T& val) { emplace_front(val); }
+  void push_front(T&& val) { emplace_front(std::move(val)); }
+
+  // Prepends a new element to the beginning of the container.
+  // The element is constructed in-place with `args`.
+  // Returns a reference to the new element.
+  // Invalidates `end()` iterator if the container was empty. References to
+  // other elements remain valid.
+  template <typename... A>
+  T& emplace_front(A&&... args);
+
   // Appends the given element value to the end of the container.
   // Invalidates `end()` iterator. References to other elements remain valid.
   void push_back(const T& val) { emplace_back(val); }
@@ -502,7 +518,7 @@
   void RangeInit(Iter first, Iter last, std::input_iterator_tag) {
     while (first != last) {
       AddTailBlock();
-      for (; first != last && tail_.ptr != tail_.limit;
+      for (; first != last && tail_.ptr != tail_.block->limit();
            ++alloc_and_size_.size, ++tail_.ptr, ++first) {
         AllocatorTraits::construct(alloc_and_size_.allocator(), tail_.ptr,
                                    *first);
@@ -538,10 +554,13 @@
   }
 
   void AddTailBlock();
-  size_t NewBlockSize() {
-    // Double the last block size and bound to [kBlockSizeMin, kBlockSizeMax].
-    if (!tail_.block) return kBlockSizeMin;
-    return (std::min)(kBlockSizeMax, 2 * tail_.block->size());
+  // Returns the capacity (in elements) to allocate for a new block adjacent to
+  // `b` (`head_.block` when prepending or `tail_.block` when appending). Starts
+  // at `kBlockSizeMin` when `b` is null, otherwise doubles `b->size()` and
+  // clamps to `[kBlockSizeMin, kBlockSizeMax]`.
+  size_t NewBlockSize(Block* b) {
+    if (!b) return kBlockSizeMin;
+    return (std::min)(kBlockSizeMax, 2 * b->size());
   }
 
   T* AllocateBack();
@@ -556,16 +575,17 @@
   // (1) When we have just one block:
   //      [head_.ptr .. tail_.ptr-1]
   // (2) When we have multiple blocks:
-  //      [head_.ptr .. head_.limit-1]
+  //      [head_.ptr .. head_.block->limit()-1]
   //      ... concatenation of all elements from interior blocks ...
-  //      [tail_.ptr .. tail_.limit-1]
+  //      [tail_.block->start() .. tail_.ptr-1]
   //
   // Rep invariants:
   // When have just one block:
-  //   head_.limit == tail_.limit == &head_.block->element[kBlockSize]
-  // Always:
-  //   head_.ptr <= head_.limit
-  //   tail_.ptr <= tail_.limit
+  //   head_.block->limit() == tail_.block->limit() ==
+  //   &head_.block->element[kBlockSize]
+  // Always (when non-empty):
+  //   head_.ptr <= head_.block->limit()
+  //   tail_.ptr <= tail_.block->limit()
 
   iterator head_;
   iterator tail_;
@@ -628,11 +648,12 @@
 template <typename T, size_t BLo, size_t BHi, typename Allocator>
 void chunked_queue<T, BLo, BHi, Allocator>::resize(size_t new_size) {
   while (new_size > size()) {
-    if (tail_.ptr == tail_.limit) {
+    if (!tail_.block || tail_.ptr == tail_.block->limit()) {
       AddTailBlock();
     }
-    size_t to_add = (std::min)(new_size - size(),
-                               static_cast<size_t>(tail_.limit - tail_.ptr));
+    size_t to_add =
+        (std::min)(new_size - size(),
+                   static_cast<size_t>(tail_.block->limit() - tail_.ptr));
     T* start = tail_.ptr;
     T* limit = start + to_add;
     Construct(start, limit);
@@ -650,9 +671,70 @@
 }
 
 template <typename T, size_t BLo, size_t BHi, typename Allocator>
+template <typename... A>
+T& chunked_queue<T, BLo, BHi, Allocator>::emplace_front(A&&... args) {
+  // Per "The set of elements in the queue" / "Rep invariants" (documented above
+  // `head_` and `tail_`), in a multi-block queue the head block holds
+  // `[head_.ptr .. head_.block->limit()-1]`, interior blocks hold all elements
+  // `[b->start() .. b->limit()-1]`, and the tail block holds
+  // `[tail_.block->start() .. tail_.ptr-1]` (as also assumed by `block_begin()`
+  // and `iterator_common::Incr()`). Therefore, a head block is populated from
+  // `b->limit() - 1` down to `b->start()` before a new head block can be
+  // prepended.
+
+  // Determine the target block `b` and element slot `storage`, allocating a new
+  // head block if needed.
+  Block* b = head_.block;
+  T* storage;
+  if (empty()) {
+    // Empty queue: allocate the first block if none exists, or reuse the empty
+    // block retained by `clear()` / `pop_front()`.
+    if (!b) {
+      b = Block::New(NewBlockSize(b), &alloc_and_size_.allocator());
+    }
+    storage = b->limit() - 1;
+  } else if (head_.ptr == head_.block->start()) {
+    // Full head block (no spare capacity before `head_.ptr`): allocate a new
+    // head block to prepend.
+    b = Block::New(NewBlockSize(b), &alloc_and_size_.allocator());
+    storage = b->limit() - 1;
+  } else {
+    // Partially filled head block: use the slot immediately before `head_.ptr`.
+    storage = head_.ptr - 1;
+  }
+
+  // Construct the new element in `storage`.
+  {
+    // Exception safety: Unlike `emplace_back()` (where `AddTailBlock()` links
+    // the new block into `tail_` before construction with `tail_.ptr ==
+    // b->start()`, so the container owns it if construction throws), `b` is not
+    // linked into `head_` until after construction succeeds so that `head_`
+    // remains valid on throw. Delete `b` if construction throws.
+    absl::Cleanup cleanup = [&] {
+      if (b != head_.block) Block::Delete(b, &alloc_and_size_.allocator());
+    };
+    AllocatorTraits::construct(alloc_and_size_.allocator(), storage,
+                               std::forward<A>(args)...);
+    std::move(cleanup).Cancel();
+  }
+
+  // Link the new block (if allocated) and update queue iterators and size.
+  if (empty()) {
+    tail_.block = b;
+    tail_.ptr = b->limit();
+  } else if (b != head_.block) {
+    b->set_next(head_.block);
+  }
+  head_.block = b;
+  head_.ptr = storage;
+  ++alloc_and_size_.size;
+  return *storage;
+}
+
+template <typename T, size_t BLo, size_t BHi, typename Allocator>
 inline void chunked_queue<T, BLo, BHi, Allocator>::AddTailBlock() {
-  ABSL_ASSERT(tail_.ptr == tail_.limit);
-  auto* b = Block::New(NewBlockSize(), &alloc_and_size_.allocator());
+  ABSL_ASSERT(!tail_.block || tail_.ptr == tail_.block->limit());
+  auto* b = Block::New(NewBlockSize(tail_.block), &alloc_and_size_.allocator());
   if (!head_.block) {
     ABSL_ASSERT(!tail_.block);
     head_ = iterator(b);
@@ -665,7 +747,7 @@
 
 template <typename T, size_t BLo, size_t BHi, typename Allocator>
 inline T* chunked_queue<T, BLo, BHi, Allocator>::AllocateBack() {
-  if (tail_.ptr == tail_.limit) {
+  if (!tail_.block || tail_.ptr == tail_.block->limit()) {
     AddTailBlock();
   }
   return tail_.ptr;
@@ -677,7 +759,7 @@
     return;
   }
   ABSL_ASSERT(i.ptr);
-  ABSL_ASSERT(i.limit);
+  ABSL_ASSERT(i.block->limit());
   alloc_and_size_.size -= Destroy(i.ptr, block_end(i.block));
   Block* b = i.block->next();
   while (b) {
@@ -717,7 +799,7 @@
     head_.ptr = tail_.ptr = head_.block->start();
     return;
   }
-  if (head_.ptr == head_.limit) {
+  if (head_.ptr == head_.block->limit()) {
     Block* n = head_.block->next();
     Block::Delete(head_.block, &alloc_and_size_.allocator());
     head_ = iterator(n);
diff --git a/absl/container/chunked_queue_benchmark.cc b/absl/container/chunked_queue_benchmark.cc
index ee4d3c1..3c031ed 100644
--- a/absl/container/chunked_queue_benchmark.cc
+++ b/absl/container/chunked_queue_benchmark.cc
@@ -38,6 +38,16 @@
   ~forward_list_queue() = default;
 
   template <typename... Args>
+  void emplace_front(Args&&... args) {
+    if (list_.empty()) {
+      list_.emplace_front(std::forward<Args>(args)...);
+      tail_ = list_.begin();
+    } else {
+      list_.emplace_front(std::forward<Args>(args)...);
+    }
+  }
+
+  template <typename... Args>
   void emplace_back(Args&&... args) {
     if (list_.empty()) {
       list_.emplace_front(std::forward<Args>(args)...);
@@ -48,6 +58,7 @@
     }
   }
 
+  void push_front(const T& value) { emplace_front(value); }
   void push_back(const T& value) { emplace_back(value); }
   iterator begin() { return list_.begin(); }
   iterator end() { return list_.end(); }
@@ -146,6 +157,31 @@
 BENCHMARK_TEMPLATE(BM_destroy, ExpChunked<Element>)->Apply(CustomArgs);
 
 template <class Q>
+void BM_push_front(benchmark::State& state) {
+  const int64_t num_elements = state.range(0);
+
+  state.SetItemsProcessed(state.max_iterations * num_elements);
+  for (auto s : state) {
+    state.PauseTiming();
+    Q q;
+    state.ResumeTiming();
+    for (int j = 0; j < num_elements; j++) q.push_front(j);
+    benchmark::DoNotOptimize(q);
+  }
+}
+
+BENCHMARK_TEMPLATE(BM_push_front, Deque<int64_t>)->Apply(CustomArgs);
+BENCHMARK_TEMPLATE(BM_push_front, List<int64_t>)->Apply(CustomArgs);
+BENCHMARK_TEMPLATE(BM_push_front, FwdList<int64_t>)->Apply(CustomArgs);
+BENCHMARK_TEMPLATE(BM_push_front, Chunked<int64_t>)->Apply(CustomArgs);
+BENCHMARK_TEMPLATE(BM_push_front, ExpChunked<int64_t>)->Apply(CustomArgs);
+BENCHMARK_TEMPLATE(BM_push_front, Deque<Element>)->Apply(CustomArgs);
+BENCHMARK_TEMPLATE(BM_push_front, List<Element>)->Apply(CustomArgs);
+BENCHMARK_TEMPLATE(BM_push_front, FwdList<Element>)->Apply(CustomArgs);
+BENCHMARK_TEMPLATE(BM_push_front, Chunked<Element>)->Apply(CustomArgs);
+BENCHMARK_TEMPLATE(BM_push_front, ExpChunked<Element>)->Apply(CustomArgs);
+
+template <class Q>
 void BM_push_back(benchmark::State& state) {
   const int64_t num_elements = state.range(0);
 
diff --git a/absl/container/chunked_queue_test.cc b/absl/container/chunked_queue_test.cc
index 22ce944..b4604b6 100644
--- a/absl/container/chunked_queue_test.cc
+++ b/absl/container/chunked_queue_test.cc
@@ -346,6 +346,59 @@
   EXPECT_EQ(0, q.size());
 }
 
+TEST(ChunkedQueue, PushFrontAndPop) {
+  absl::chunked_queue<int64_t> q;
+  EXPECT_TRUE(q.empty());
+  EXPECT_EQ(0, q.size());
+  // Push enough elements to exercise exponential block growth from
+  // `kBlockSizeMin` (1) up to `kBlockSizeMax` (128) across multiple max-sized
+  // head blocks (`1 + 2 + 4 + 8 + 16 + 32 + 64 + 128 = 255`).
+  constexpr int kNumElements = 500;
+  for (int i = 0; i < kNumElements; i++) {
+    q.push_front(i);
+    EXPECT_EQ(q.front(), i) << ": iteration " << i;
+    EXPECT_EQ(q.back(), 0) << ": iteration " << i;
+    EXPECT_FALSE(q.empty());
+    EXPECT_EQ(i + 1, q.size());
+  }
+  for (int i = 0; i < kNumElements; i++) {
+    EXPECT_FALSE(q.empty());
+    EXPECT_EQ(kNumElements - i, q.size());
+    EXPECT_EQ(q.front(), kNumElements - 1 - i);
+    EXPECT_EQ(q.back(), 0);
+    q.pop_front();
+  }
+  EXPECT_TRUE(q.empty());
+  EXPECT_EQ(0, q.size());
+}
+
+TEST(ChunkedQueue, PushFrontAndPushBack) {
+  absl::chunked_queue<int64_t, 4> q;
+  q.push_front(2);
+  q.push_back(3);
+  q.push_front(1);
+  q.push_back(4);
+  q.push_front(0);
+  q.push_back(5);
+  EXPECT_THAT(q, ElementsAre(0, 1, 2, 3, 4, 5));
+  EXPECT_EQ(q.front(), 0);
+  EXPECT_EQ(q.back(), 5);
+
+  q.pop_front();
+  q.push_front(-1);
+  EXPECT_THAT(q, ElementsAre(-1, 1, 2, 3, 4, 5));
+
+  q.clear();
+  EXPECT_TRUE(q.empty());
+  q.push_front(10);
+  EXPECT_THAT(q, ElementsAre(10));
+  EXPECT_EQ(q.front(), 10);
+  EXPECT_EQ(q.back(), 10);
+  q.push_front(9);
+  q.push_back(11);
+  EXPECT_THAT(q, ElementsAre(9, 10, 11));
+}
+
 TEST(ChunkedQueue, Swap) {
   absl::chunked_queue<int64_t> q1;
   absl::chunked_queue<int64_t> q2;
@@ -662,6 +715,28 @@
   EXPECT_EQ(*++i, 2);
 }
 
+// Verifies that `push_front` on a non-empty queue does not invalidate
+// iterators to existing elements, both when prepending into remaining space in
+// the current head block (`1`) and when allocating a new head block (`0`), and
+// that advancing `begin()` across the block boundary reaches the existing
+// iterator `i`.
+TEST(ChunkedQueue, Iterator_PushFrontInvalidate) {
+  absl::chunked_queue<int64_t, 2> q;
+  q.push_front(2);
+  auto i = q.begin();
+  EXPECT_EQ(*i, 2);
+  q.push_front(1);
+  EXPECT_EQ(*i, 2);
+  q.push_front(0);
+  EXPECT_EQ(*i, 2);
+  auto j = q.begin();
+  EXPECT_EQ(*j, 0);
+  EXPECT_EQ(*++j, 1);
+  EXPECT_EQ(*++j, 2);
+  EXPECT_TRUE(j == i);
+  EXPECT_TRUE(++j == q.end());
+}
+
 struct MyType {
   static int constructor_calls;
   static int destructor_calls;
@@ -707,13 +782,56 @@
   absl::chunked_queue<std::unique_ptr<int>> q;
   q.push_back(std::make_unique<int>(10));
   q.push_back(std::make_unique<int>(11));
+  q.push_front(std::make_unique<int>(9));
 
+  EXPECT_EQ(9, *q.front());
+  q.pop_front();
   EXPECT_EQ(10, *q.front());
   q.pop_front();
   EXPECT_EQ(11, *q.front());
   q.pop_front();
 }
 
+// Verifies that `emplace_front` constructs elements in-place at the front of
+// the queue, returns a reference to the newly inserted element, and preserves
+// existing elements at the back.
+TEST(ChunkedQueue, EmplaceFrontUpdatesFrontAndReturnsReference) {
+  absl::chunked_queue<std::pair<int, int>> q;
+  auto& v1 = q.emplace_front(1, 2);
+  EXPECT_THAT(v1, Pair(1, 2));
+  EXPECT_THAT(q.front(), Pair(1, 2));
+  EXPECT_EQ(&v1, &q.front());
+  EXPECT_EQ(&v1, &q.back());
+
+  auto& v2 = q.emplace_front(3, 4);
+  EXPECT_THAT(v2, Pair(3, 4));
+  EXPECT_THAT(q.front(), Pair(3, 4));
+  EXPECT_THAT(q.back(), Pair(1, 2));
+  EXPECT_EQ(&v2, &q.front());
+  EXPECT_EQ(&v1, &q.back());
+}
+
+// Verifies that `emplace_front` forwards move-only arguments to construct the
+// element in-place.
+TEST(ChunkedQueue, EmplaceFrontMoveOnlyType) {
+  absl::chunked_queue<std::pair<std::unique_ptr<int>, std::string>> q;
+  auto& v = q.emplace_front(std::make_unique<int>(11), "val12");
+  EXPECT_THAT(v, Pair(Pointee(11), "val12"));
+  EXPECT_THAT(q.front(), Pair(Pointee(11), "val12"));
+}
+
+TEST(ChunkedQueue, OveralignmentEmplaceFront) {
+  struct alignas(64) Overaligned {
+    int x;
+    int y;
+  };
+  absl::chunked_queue<Overaligned, 1, 8> q;
+  for (int i = 0; i < 10; ++i) {
+    auto& v = q.emplace_front(Overaligned{i, i});
+    EXPECT_EQ(reinterpret_cast<uintptr_t>(&v) % 64, 0);
+  }
+}
+
 TEST(ChunkedQueue, EmplaceBack1) {
   absl::chunked_queue<std::pair<int, int>> q;
   auto& v = q.emplace_back(1, 2);
@@ -756,7 +874,6 @@
     struct MockIterator {
       void* block;
       void* ptr;
-      void* limit;
     };
     MockIterator head;
     MockIterator tail;
@@ -866,4 +983,111 @@
 }
 #endif
 
+TEST(ChunkedQueue, StrongExceptionSafetyEmplaceFrontEmptyQueue) {
+#ifndef ABSL_HAVE_EXCEPTIONS
+  GTEST_SKIP() << "Exceptions are not enabled.";
+#else
+  int ctor_count = 0;
+  int dtor_count = 0;
+  absl::chunked_queue<ThrowingCtor> q;
+
+  try {
+    q.emplace_front(5, true, &ctor_count, &dtor_count);
+  } catch (...) {
+  }
+
+  EXPECT_TRUE(q.empty());
+  EXPECT_EQ(ctor_count, 0);
+  EXPECT_EQ(dtor_count, 0);
+#endif
+}
+
+TEST(ChunkedQueue, StrongExceptionSafetyEmplaceFrontNewHeadBlock) {
+#ifndef ABSL_HAVE_EXCEPTIONS
+  GTEST_SKIP() << "Exceptions are not enabled.";
+#else
+  int ctor_count = 0;
+  int dtor_count = 0;
+  absl::chunked_queue<ThrowingCtor> q;
+  q.emplace_front(10, false, &ctor_count, &dtor_count);
+  ASSERT_EQ(q.size(), 1);
+  ASSERT_EQ(ctor_count, 1);
+  ASSERT_EQ(dtor_count, 0);
+
+  // The first block has size 1, so the next emplace_front allocates a new head
+  // block and must delete it if element construction throws.
+  try {
+    q.emplace_front(15, true, &ctor_count, &dtor_count);
+  } catch (...) {
+  }
+
+  EXPECT_EQ(q.size(), 1);
+  EXPECT_EQ(ctor_count, 1);
+  EXPECT_EQ(dtor_count, 0);
+  EXPECT_EQ(q.front().value, 10);
+  EXPECT_EQ(q.back().value, 10);
+
+  q.clear();
+  EXPECT_EQ(q.size(), 0);
+  EXPECT_EQ(dtor_count, 1);
+#endif
+}
+
+TEST(ChunkedQueue, StrongExceptionSafetyEmplaceFrontExistingHeadBlock) {
+#ifndef ABSL_HAVE_EXCEPTIONS
+  GTEST_SKIP() << "Exceptions are not enabled.";
+#else
+  int ctor_count = 0;
+  int dtor_count = 0;
+  absl::chunked_queue<ThrowingCtor, 2> q;
+  q.emplace_front(10, false, &ctor_count, &dtor_count);
+  ASSERT_EQ(q.size(), 1);
+  ASSERT_EQ(ctor_count, 1);
+  ASSERT_EQ(dtor_count, 0);
+
+  // The head block has capacity 2 with 1 element, so the next emplace_front
+  // constructs into the remaining slot in the existing head block.
+  try {
+    q.emplace_front(20, true, &ctor_count, &dtor_count);
+  } catch (...) {
+  }
+
+  EXPECT_EQ(q.size(), 1);
+  EXPECT_EQ(ctor_count, 1);
+  EXPECT_EQ(dtor_count, 0);
+  EXPECT_EQ(q.front().value, 10);
+  EXPECT_EQ(q.back().value, 10);
+
+  q.clear();
+  EXPECT_EQ(q.size(), 0);
+  EXPECT_EQ(dtor_count, 1);
+#endif
+}
+
+TEST(ChunkedQueue, StrongExceptionSafetyEmplaceFrontClearedQueue) {
+#ifndef ABSL_HAVE_EXCEPTIONS
+  GTEST_SKIP() << "Exceptions are not enabled.";
+#else
+  int ctor_count = 0;
+  int dtor_count = 0;
+  absl::chunked_queue<ThrowingCtor> q;
+  q.emplace_front(10, false, &ctor_count, &dtor_count);
+  q.clear();
+  ASSERT_TRUE(q.empty());
+  ASSERT_EQ(ctor_count, 1);
+  ASSERT_EQ(dtor_count, 1);
+
+  // `clear()` leaves one empty block allocated; throwing while constructing
+  // into that retained block must leave the queue empty and valid.
+  try {
+    q.emplace_front(20, true, &ctor_count, &dtor_count);
+  } catch (...) {
+  }
+
+  EXPECT_TRUE(q.empty());
+  EXPECT_EQ(ctor_count, 1);
+  EXPECT_EQ(dtor_count, 1);
+#endif
+}
+
 }  // namespace
diff --git a/absl/container/internal/container_memory.h b/absl/container/internal/container_memory.h
index 01fe8d0..c408131 100644
--- a/absl/container/internal/container_memory.h
+++ b/absl/container/internal/container_memory.h
@@ -562,6 +562,11 @@
   size_t operator()(size_t seed) const {
     return HashElement<Hash, kIsAbsl, kSeedShift>{hash, seed}(key);
   }
+
+  // Static version of the above to pass to type erased functions.
+  static size_t GetHash(void* data, size_t seed) {
+    return (*static_cast<const HashKey*>(data))(seed);
+  }
   const Hash& hash;
   const Key& key;
 };
diff --git a/absl/container/internal/raw_hash_map.h b/absl/container/internal/raw_hash_map.h
index e35954a..8984dfa 100644
--- a/absl/container/internal/raw_hash_map.h
+++ b/absl/container/internal/raw_hash_map.h
@@ -82,23 +82,24 @@
   //
   // MovableOrVoid is an optional, extra constraint (via SFINAE) to guard
   // separate move-only overloads.
-  template <class K, bool Value, typename MovableOrVoid = void>
+  template <class K, bool Value, bool ExtraConstraint = true>
   using LifetimeBoundK = std::bool_constant<
-      (std::is_void_v<MovableOrVoid> ||
+      (ExtraConstraint ||
        !IsAllocMoveSameAsCopy<absl::remove_cvref_t<K>, Alloc>::value) &&
       HasValue<Value, std::conditional_t<
                           policy_trait_element_is_owner<Policy>::value,
                           std::false_type,
                           type_traits_internal::IsLifetimeBoundAssignment<
                               typename Policy::key_type, K>>>::value>;
-  template <class V, bool Value, typename = void>
+  template <class V, bool Value>
   using LifetimeBoundV =
       HasValue<Value, type_traits_internal::IsLifetimeBoundAssignment<
                           typename Policy::mapped_type, V>>;
-  template <class K, bool KValue, class V, bool VValue, typename... Dummy>
-  using LifetimeBoundKV =
-      std::conjunction<LifetimeBoundK<K, KValue, std::void_t<Dummy...>>,
-                       LifetimeBoundV<V, VValue>>;
+  template <class K, bool KValue, class V, bool VValue,
+            bool ExtraConstraintK = true, bool ExtraConstraintV = true>
+  using LifetimeBoundKV = std::conjunction<
+      LifetimeBoundK<K, KValue, ExtraConstraintK && ExtraConstraintV>,
+      LifetimeBoundV<V, VValue>>;
 
  public:
   using key_type = typename Policy::key_type;
@@ -131,8 +132,10 @@
       typename K = key_type, class V = mapped_type,                            \
       ABSL_INTERNAL_IF_##KValue##_NOR_##VValue(                                \
           int = (EnableIf<LifetimeBoundKV<K, KValue, V, VValue,                \
-                                          IfRRef<int KQual>::AddPtr<K>,        \
-                                          IfRRef<int VQual>::AddPtr<V>>>()),   \
+                                          !std::is_reference_v<int KQual> ||   \
+                                              !std::is_reference_v<K>,         \
+                                          !std::is_reference_v<int VQual> ||   \
+                                              !std::is_reference_v<V>>>()),    \
           ABSL_INTERNAL_SINGLE_ARG(                                            \
               int&...,                                                         \
               decltype(EnableIf<LifetimeBoundKV<K, KValue, V, VValue>>()) =    \
@@ -226,7 +229,8 @@
   // arguments as `std::unordered_map::try_emplace()`, namely that these
   // functions will not move from rvalue arguments if insertions do not happen.
   template <
-      class K = key_type, int = EnableIf<LifetimeBoundK<K, false, K*>>(),
+      class K = key_type,
+      int = EnableIf<LifetimeBoundK<K, false, !std::is_reference_v<K>>>(),
       class... Args,
       std::enable_if_t<!std::is_convertible_v<K, const_iterator>, int> = 0>
   std::pair<iterator, bool> try_emplace(key_arg<K>&& k, Args&&... args)
@@ -237,7 +241,7 @@
 
   template <
       class K = key_type, class... Args,
-      EnableIf<LifetimeBoundK<K, true, K*>> = 0,
+      EnableIf<LifetimeBoundK<K, true, !std::is_reference_v<K>>> = 0,
       std::enable_if_t<!std::is_convertible_v<K, const_iterator>, int> = 0>
   std::pair<iterator, bool> try_emplace(
       key_arg<K>&& k ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY_THIS,
@@ -263,7 +267,8 @@
     return this->template try_emplace<K, 0>(k, std::forward<Args>(args)...);
   }
 
-  template <class K = key_type, int = EnableIf<LifetimeBoundK<K, false, K*>>(),
+  template <class K = key_type,
+            int = EnableIf<LifetimeBoundK<K, false, !std::is_reference_v<K>>>(),
             class... Args>
   iterator try_emplace(const_iterator, key_arg<K>&& k,
                        Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
@@ -271,7 +276,7 @@
         .first;
   }
   template <class K = key_type, class... Args,
-            EnableIf<LifetimeBoundK<K, true, K*>> = 0>
+            EnableIf<LifetimeBoundK<K, true, !std::is_reference_v<K>>> = 0>
   iterator try_emplace(const_iterator hint,
                        key_arg<K>&& k ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY_THIS,
                        Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
@@ -315,7 +320,7 @@
   }
 
   template <class K = key_type, class P = Policy,
-            int = EnableIf<LifetimeBoundK<K, false, K*>>()>
+            int = EnableIf<LifetimeBoundK<K, false, !std::is_reference_v<K>>>()>
   MappedReference<P> operator[](key_arg<K>&& key)
       ABSL_ATTRIBUTE_LIFETIME_BOUND {
     // It is safe to use unchecked_deref here because try_emplace
@@ -325,7 +330,7 @@
         try_emplace(std::forward<key_arg<K>>(key)).first));
   }
   template <class K = key_type, class P = Policy, int&...,
-            EnableIf<LifetimeBoundK<K, true, K*>> = 0>
+            EnableIf<LifetimeBoundK<K, true, !std::is_reference_v<K>>> = 0>
   MappedReference<P> operator[](
       key_arg<K>&& key ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY_THIS)
       ABSL_ATTRIBUTE_LIFETIME_BOUND {
@@ -377,23 +382,15 @@
       // Try to avoid using std::tuple since that slows compilation.
       // (example: https://godbolt.org/z/nefW93T6K)
 
-      // Simple trick to bypass instantiating a bunch of templates in cases
-      // where they're irrelevant.
-      using V = std::conditional_t<
-          sizeof...(Args) == 0 &&
-              std::is_same_v<typename std::allocator_traits<
-                                 Alloc>::template rebind_alloc<char>,
-                             std::allocator<char>>,
-          mapped_type, void>;
-
-      if constexpr (std::is_trivially_move_constructible_v<V> &&
-                    std::is_trivially_destructible_v<V> &&
-                    // Check if size is a power of 2, to ensure cheap calls.
-                    absl::has_single_bit(sizeof(mapped_type)) &&
-                    // Check if copying 1-2 registers is enough. We don't want
-                    // (for example) 32 one-byte values to move around.
-                    alignof(mapped_type) * 2 >= sizeof(mapped_type) &&
-                    sizeof(mapped_type) <= sizeof(std::pair<void*, size_t>)) {
+      if constexpr (ShouldPreferPassByValue<std::conditional_t<
+                        sizeof...(Args) == 0 &&
+                            std::is_same_v<
+                                typename std::allocator_traits<
+                                    Alloc>::template rebind_alloc<char>,
+                                std::allocator<char>>,
+                        mapped_type,
+                        // Trick to avoid unnecessary template instantiations.
+                        void>>::value) {
         // Common case: operator[]
         // Note: This has a different run-time performance characteristic than
         // the other branches, due to the usage of a temporary object. In the
diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc
index 15a80cb..3465a90 100644
--- a/absl/container/internal/raw_hash_set.cc
+++ b/absl/container/internal/raw_hash_set.cc
@@ -292,9 +292,6 @@
 
 void CommonFields::AssertNotDebugCapacityImpl() const {
   const HashtableCapacity cap = maybe_invalid_capacity();
-  if (ABSL_PREDICT_TRUE(cap.IsValid())) {
-    return;
-  }
   assert(!cap.IsReentrance() &&
          "Reentrant container access during element construction/destruction "
          "is not allowed.");
@@ -983,6 +980,35 @@
   Destruct<kSooEnabled>(c, policy, kStandardDeallocBackingArrayFn);
 }
 
+void AssertHashEqConsistentImpl(CommonFields& common, const void* hash_fn,
+                                HashSlotFn hash_slot, size_t slot_size,
+                                bool soo_enabled, size_t hash, const void* key,
+                                EqualToKeyFn equal) {
+  const size_t seed = common.seed().seed();
+
+  const auto assert_consistent = [&](const ctrl_t*, void* slot) {
+    const bool is_key_equal = equal(&common, key, slot);
+    if (!is_key_equal) return;
+
+    [[maybe_unused]] const bool is_hash_equal =
+        hash == hash_slot(hash_fn, slot, seed);
+    assert((!is_key_equal || is_hash_equal) &&
+           "eq(k1, k2) must imply that hash(k1) == hash(k2). "
+           "hash/eq functors are inconsistent.");
+  };
+
+  if (common.is_small()) {
+    assert_consistent(
+        /*unused*/ nullptr,
+        soo_enabled ? SingleSlotAddress</*kSooEnabled=*/true>(common)
+                           : SingleSlotAddress</*kSooEnabled=*/false>(common));
+    return;
+  }
+  // We only do validation for small tables so that it's constant time.
+  if (common.capacity() > 16) return;
+  IterateOverFullSlots(common, slot_size, assert_consistent);
+}
+
 namespace {
 
 // Iterates over full slots in old table, finds new positions for them and
@@ -1049,12 +1075,12 @@
 ABSL_ATTRIBUTE_COLD ABSL_ATTRIBUTE_NOINLINE void
 ReportSingleElementTableGrowthToInfozAndForceSample(
     CommonFields& common, const PolicyFunctions& __restrict policy,
-    absl::FunctionRef<size_t(size_t)> get_hash) {
+    void* hasher, HashForKeyFn get_hash) {
   HashtablezInfoHandle infoz =
       ForcedTrySample(policy.slot_size, policy.key_size, policy.value_size,
                       policy.soo_capacity());
   common.generate_new_seed(/*has_infoz=*/true);
-  ReportGrowthToInfozImpl(common, infoz, get_hash(common.seed().seed()),
+  ReportGrowthToInfozImpl(common, infoz, get_hash(hasher, common.seed().seed()),
                           /*total_probe_length=*/0,
                           /*distance_from_desired=*/0);
 }
@@ -1071,10 +1097,10 @@
 // avoid calling the hash function and seed extraction in the hot path.
 // `common` is first and `get_hash` is third to match argument order in callers.
 ABSL_ATTRIBUTE_COLD ABSL_ATTRIBUTE_NOINLINE void
-ReportInsertMissToInfozAndComputeHash(
-    CommonFields& common, size_t probe_length,
-    absl::FunctionRef<size_t(size_t)> get_hash) {
-  common.infoz().RecordInsertMiss(get_hash(common.seed().seed()), probe_length);
+ReportInsertMissToInfozAndComputeHash(CommonFields& common, size_t probe_length,
+                                      void* hasher, HashForKeyFn get_hash) {
+  common.infoz().RecordInsertMiss(get_hash(hasher, common.seed().seed()),
+                                  probe_length);
 }
 
 // Outlines cold infoz recording with computation of probe_length so callers can
@@ -1751,7 +1777,7 @@
 
 void* Grow1To3AndPrepareInsert(CommonFields& common,
                                const PolicyFunctions& __restrict policy,
-                               absl::FunctionRef<size_t(size_t)> get_hash) {
+                               void* hasher, HashForKeyFn get_hash) {
   // TODO(b/413062340): Refactor to reuse more code with
   // GrowSooTableToNextCapacityAndPrepareInsert.
   ABSL_SWISSTABLE_ASSERT(common.capacity() == 1);
@@ -1781,7 +1807,7 @@
     // When we're sampled, we already have a seed.
     common.generate_new_seed(/*has_infoz=*/false);
   }
-  const size_t new_hash = get_hash(common.seed().seed());
+  const size_t new_hash = get_hash(hasher, common.seed().seed());
   h2_t new_h2 = H2(new_hash);
   size_t orig_hash =
       policy.hash_slot(policy.hash_fn(common), old_slots, common.seed().seed());
@@ -1889,7 +1915,7 @@
 
 void* PrepareInsertSmallNonSoo(CommonFields& common,
                                const PolicyFunctions& __restrict policy,
-                               absl::FunctionRef<size_t(size_t)> get_hash) {
+                               void* hasher, HashForKeyFn get_hash) {
   ABSL_SWISSTABLE_ASSERT(common.is_small());
   ABSL_SWISSTABLE_ASSERT(!policy.soo_enabled);
   if (common.capacity() == 1) {
@@ -1900,11 +1926,11 @@
       // Call NOINLINE function to move infoz instructions out of line.
       if (common.has_infoz()) {
         ReportInsertMissToInfozAndComputeHash(common, /*probe_length=*/0,
-                                              get_hash);
+                                              hasher, get_hash);
       }
       return res;
     } else {
-      return Grow1To3AndPrepareInsert(common, policy, get_hash);
+      return Grow1To3AndPrepareInsert(common, policy, hasher, get_hash);
     }
   }
 
@@ -1926,7 +1952,7 @@
   PrepareInsertCommon(common);
 
   if (ABSL_PREDICT_FALSE(has_infoz)) {
-    ReportSingleElementTableGrowthToInfozAndForceSample(common, policy,
+    ReportSingleElementTableGrowthToInfozAndForceSample(common, policy, hasher,
                                                         get_hash);
   }
   return new_slots;
@@ -2040,14 +2066,14 @@
 ABSL_ATTRIBUTE_NOINLINE void*
 GrowEmptySooTableToNextCapacityForceSamplingAndPrepareInsert(
     CommonFields& common, const PolicyFunctions& __restrict policy,
-    absl::FunctionRef<size_t(size_t)> get_hash) {
+    void* hasher, HashForKeyFn get_hash) {
   const size_t kNewCapacity = NextCapacity(SooCapacity());
   ResizeEmptyNonAllocatedTableImpl(common, policy, kNewCapacity,
                                    /*blocked_element_count=*/0,
                                    /*force_infoz=*/true);
   PrepareInsertCommon(common);
   common.OverwriteEmptyAsFull();
-  const size_t new_hash = get_hash(common.seed().seed());
+  const size_t new_hash = get_hash(hasher, common.seed().seed());
   // Compute before `SetCtrl*` to avoid reloading `control_`.
   void* res = SlotAddress(common.slot_array(kNewCapacity), SooSlotIndex(),
                           policy.slot_size);
@@ -2186,12 +2212,12 @@
 template <size_t SooSlotMemcpySize, bool TransferUsesMemcpy>
 void* GrowSooTableToNextCapacityAndPrepareInsert(
     CommonFields& common, const PolicyFunctions& __restrict policy,
-    absl::FunctionRef<size_t(size_t)> get_hash, bool force_sampling) {
+    void* hasher, HashForKeyFn get_hash, bool force_sampling) {
   AssertSoo(common, policy);
   if (ABSL_PREDICT_FALSE(force_sampling)) {
     // The table is empty, it is only used for forced sampling of SOO tables.
     return GrowEmptySooTableToNextCapacityForceSamplingAndPrepareInsert(
-        common, policy, get_hash);
+        common, policy, hasher, get_hash);
   }
   ABSL_SWISSTABLE_ASSERT(common.size() == policy.soo_capacity());
   static constexpr size_t kNewCapacity = NextCapacity(SooCapacity());
@@ -2214,7 +2240,7 @@
   common.generate_new_seed(/*has_infoz=*/false);
   const h2_t soo_slot_h2 = H2(policy.hash_slot(
       policy.hash_fn(common), common.soo_data(), common.seed().seed()));
-  const size_t new_hash = get_hash(common.seed().seed());
+  const size_t new_hash = get_hash(hasher, common.seed().seed());
 
   const size_t offset = Resize1To3NewOffset(new_hash);
   InitializeThreeElementsControlBytes(soo_slot_h2, H2(new_hash), offset,
@@ -2500,18 +2526,17 @@
 // We need to instantiate ALL possible template combinations because we define
 // the function in the cc file.
 template void* GrowSooTableToNextCapacityAndPrepareInsert<0, false>(
-    CommonFields&, const PolicyFunctions&, absl::FunctionRef<size_t(size_t)>,
-    bool);
+    CommonFields&, const PolicyFunctions&, void*, HashForKeyFn, bool);
 template void* GrowSooTableToNextCapacityAndPrepareInsert<
-    OptimalMemcpySizeForSooSlotTransfer(1), true>(
-    CommonFields&, const PolicyFunctions&, absl::FunctionRef<size_t(size_t)>,
-    bool);
+    OptimalMemcpySizeForSooSlotTransfer(1), true>(CommonFields&,
+                                                  const PolicyFunctions&, void*,
+                                                  HashForKeyFn, bool);
 
 static_assert(VerifyOptimalMemcpySizeForSooSlotTransferRange(2, 3));
 template void* GrowSooTableToNextCapacityAndPrepareInsert<
-    OptimalMemcpySizeForSooSlotTransfer(3), true>(
-    CommonFields&, const PolicyFunctions&, absl::FunctionRef<size_t(size_t)>,
-    bool);
+    OptimalMemcpySizeForSooSlotTransfer(3), true>(CommonFields&,
+                                                  const PolicyFunctions&, void*,
+                                                  HashForKeyFn, bool);
 
 #if UINTPTR_MAX == UINT32_MAX
 static_assert(MaxSooSlotSize() == 4);
@@ -2519,9 +2544,9 @@
 #else
 static_assert(VerifyOptimalMemcpySizeForSooSlotTransferRange(4, 8));
 template void* GrowSooTableToNextCapacityAndPrepareInsert<
-    OptimalMemcpySizeForSooSlotTransfer(8), true>(
-    CommonFields&, const PolicyFunctions&, absl::FunctionRef<size_t(size_t)>,
-    bool);
+    OptimalMemcpySizeForSooSlotTransfer(8), true>(CommonFields&,
+                                                  const PolicyFunctions&, void*,
+                                                  HashForKeyFn, bool);
 static_assert(MaxSooSlotSize() == 8);
 #endif
 
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h
index 7c01b9e..a851710 100644
--- a/absl/container/internal/raw_hash_set.h
+++ b/absl/container/internal/raw_hash_set.h
@@ -1546,7 +1546,9 @@
   // Asserts that the capacity is not a sentinel invalid value.
   void AssertNotDebugCapacity() const {
     if constexpr (SwisstableGenerationsOrDebugEnabled()) {
-      AssertNotDebugCapacityImpl();
+      if (ABSL_PREDICT_FALSE(!maybe_invalid_capacity().IsValid())) {
+        AssertNotDebugCapacityImpl();
+      }
     }
   }
 
@@ -2174,22 +2176,24 @@
   return 8;
 }
 
+using HashForKeyFn = size_t (*)(void* hasher, size_t seed);
+
 // Resizes SOO table to the NextCapacity(SooCapacity()) and prepares insert for
 // the given new_hash. Returns the new slot.
 // All possible template combinations are defined in cc file to improve
 // compilation time.
 template <size_t SooSlotMemcpySize, bool TransferUsesMemcpy>
 void* GrowSooTableToNextCapacityAndPrepareInsert(
-    CommonFields& common, const PolicyFunctions& policy,
-    absl::FunctionRef<size_t(size_t)> get_hash, bool force_sampling);
+    CommonFields& common, const PolicyFunctions& policy, void* hasher,
+    HashForKeyFn get_hash, bool force_sampling);
 
 // PrepareInsert for small tables (is_small()==true).
 // Returns the new slot.
 // Hash is only computed if the table is sampled or grew to large size
 // (is_small()==false).
 void* PrepareInsertSmallNonSoo(CommonFields& common,
-                               const PolicyFunctions& policy,
-                               absl::FunctionRef<size_t(size_t)> get_hash);
+                               const PolicyFunctions& policy, void* hasher,
+                               HashForKeyFn get_hash);
 
 // Resizes table with allocated slots and change the table seed.
 // Tables with SOO enabled must have capacity > policy.soo_capacity.
@@ -2238,6 +2242,15 @@
 template <bool kSooEnabled>
 void Destruct(CommonFields& c, const DtorPolicy& policy);
 
+using EqualToKeyFn = bool (*)(void* set, const void* key, void* slot);
+
+// Asserts that hash and equal functors provided by the user are consistent,
+// meaning that `eq(k1, k2)` implies `hash(k1)==hash(k2)`.
+void AssertHashEqConsistentImpl(CommonFields& common, const void* hash_fn,
+                                HashSlotFn hash_slot, size_t slot_size,
+                                bool soo_enabled, size_t hash, const void* key,
+                                EqualToKeyFn equal);
+
 // Type-erased versions of raw_hash_set::erase_meta_only_{small,large}.
 void EraseMetaOnlySmall(CommonFields& c, bool soo_enabled, size_t slot_size);
 void EraseMetaOnlyLarge(CommonFields& c, size_t index, size_t slot_size);
@@ -2346,13 +2359,45 @@
                                               Alloc>::template rebind_alloc<T>>,
         T, void>>>;
 
+template <class T>
+using ShouldStructurallyPreferPassByValue = std::bool_constant<
+    // Check if size is a power of 2, to ensure cheap calls.
+    absl::has_single_bit(sizeof(T)) &&
+    // Check if copying 1-2 registers is enough. We don't want (for example) 32
+    // one-byte values to move around, since that sometimes results in poor
+    // codegen.
+    alignof(T) * 2 >= sizeof(T) &&
+    sizeof(T) <= sizeof(std::pair<void*, size_t>)>;
+
+template <class T>
+using ShouldSemanticallyPreferPassByValue =
+    std::bool_constant<std::is_trivially_move_constructible_v<T> &&
+                       std::is_trivially_destructible_v<T>>;
+
+// Checks the given type to see if it is a good candidate for accepting by value
+// instead of by rvalue-reference. This includes both a semantic constraint
+// (to ensure correctness), as well as a structural constraint (to ensure
+// performance).
+template <class T>
+using ShouldPreferPassByValue =
+    std::bool_constant<ShouldSemanticallyPreferPassByValue<T>::value &&
+                       ShouldStructurallyPreferPassByValue<std::conditional_t<
+                           std::is_void_v<T>, char, T>>::value>;
+
+// This is a 'struct' instead of 'using' in order to avoid eager evaluation for
+// callers that may be passing incomplete types.
+template <class T>
+struct AddConstRefIfExpensive
+    : std::conditional<ShouldPreferPassByValue<T>::value, T, const T&> {
+  static_assert(std::is_same_v<remove_cvref_t<T>, T>,
+                "passed types must be unqualified");
+};
+
 template <class T, class Alloc = std::allocator<T>>
-using PreferredParamType = std::conditional_t<
-    IsAllocMoveSameAsCopy<T, Alloc>::value,
-    std::conditional_t<std::is_arithmetic_v<T> || std::is_enum_v<T> ||
-                           std::is_member_pointer_v<T>,
-                       T, const T&>,
-    T&&>;
+using PreferredParamType =
+    typename std::conditional_t<IsAllocMoveSameAsCopy<T, Alloc>::value,
+                                AddConstRefIfExpensive<T>,
+                                std::add_rvalue_reference<T>>::type;
 
 // A SwissTable.
 //
@@ -2428,11 +2473,6 @@
 
   using slot_type = typename PolicyTraits::slot_type;
 
-  using IsDefaultPolicy =
-      std::bool_constant<std::is_same_v<Hash, typename Policy::DefaultHash> &&
-                         std::is_same_v<Eq, typename Policy::DefaultEq> &&
-                         std::is_same_v<Alloc, typename Policy::DefaultAlloc>>;
-
   constexpr static bool kIsAbslHash =
       std::is_same_v<hasher, absl::Hash<key_type>> ||
       (!HasAbslContainerHash<key_type>::value &&
@@ -3415,7 +3455,7 @@
     AssertOnFind(key);
     if (is_small()) return find_small(key);
     prefetch_heap_block();
-    return find_large(key);
+    return find_large<FindLargeKey<key_arg<K>>>(key);
   }
 
   template <class K = key_type>
@@ -3570,13 +3610,14 @@
 
   // TODO(b/289225379): consider having a helper class that has the impls for
   // SOO functionality.
-  template <class K = key_type>
-  ABSL_ATTRIBUTE_ALWAYS_INLINE iterator find_small(const key_arg<K>& key) {
+  template <class K>
+  ABSL_ATTRIBUTE_ALWAYS_INLINE iterator find_small(const K& key) {
     return empty() || !equal_to(key, single_slot()) ? end() : single_iterator();
   }
 
-  template <class K = key_type>
-  iterator find_large(const key_arg<K>& key) {
+  template <class K>
+  iterator find_large(absl::type_identity_t<K> key) {
+    static_assert(std::is_same_v<K, FindLargeKey<K>>);
     const size_t cap = common().capacity();
     ABSL_ASSUME(cap > kMaxSmallCapacity);
     const size_t hash = hash_of(key);
@@ -3590,10 +3631,13 @@
 #ifndef ABSL_HAVE_MEMORY_SANITIZER
       absl::PrefetchToLocalCache(slot_array + seq.offset());
 #endif
-      for (uint32_t i : g.Match(h2)) {
-        const size_t offset = seq.offset(i);
-        if (ABSL_PREDICT_TRUE(equal_to(key, slot_array + offset)))
-          return iterator_at_ptr(ctrl + offset, slot_array + offset);
+      // Predict one iteration (common case for find-hit).
+      if (auto match = g.Match(h2); ABSL_PREDICT_TRUE(match)) {
+        do {
+          const size_t offset = seq.offset(*match);
+          if (ABSL_PREDICT_TRUE(equal_to(key, slot_array + offset)))
+            return iterator_at_ptr(ctrl + offset, slot_array + offset);
+        } while (ABSL_PREDICT_FALSE(++match));
       }
       if (ABSL_PREDICT_TRUE(g.MaskEmpty())) return end();
       seq.next();
@@ -3838,14 +3882,14 @@
     }
     constexpr bool kUseMemcpy =
         PolicyTraits::transfer_uses_memcpy() && SooEnabled();
+    using Hasher = HashKey<hasher, K, kIsAbslHash, kSeedShift>;
+    Hasher hash_key{hash_ref(), key};
     slot = to_slot(
         GrowSooTableToNextCapacityAndPrepareInsert<
             kUseMemcpy ? OptimalMemcpySizeForSooSlotTransfer(sizeof(slot_type))
                        : 0,
-            kUseMemcpy>(
-            common(), GetPolicyFunctions(),
-            HashKey<hasher, K, kIsAbslHash, kSeedShift>{hash_ref(), key},
-            force_sampling));
+            kUseMemcpy>(common(), GetPolicyFunctions(), &hash_key,
+                        &Hasher::GetHash, force_sampling));
     return {slot, true};
   }
 
@@ -3860,14 +3904,17 @@
         return {single_slot(), false};
       }
     }
-    return {to_slot(PrepareInsertSmallNonSoo(
-                common(), GetPolicyFunctions(),
-                HashKey<hasher, K, kIsAbslHash, kSeedShift>{hash_ref(), key})),
+    using Hasher = HashKey<hasher, K, kIsAbslHash, kSeedShift>;
+    Hasher hash_key{hash_ref(), key};
+    return {to_slot(PrepareInsertSmallNonSoo(common(), GetPolicyFunctions(),
+                                             &hash_key, &Hasher::GetHash)),
             true};
   }
 
   template <class K>
-  std::pair<slot_type*, bool> find_or_prepare_insert_large(const K& key) {
+  std::pair<slot_type*, bool> find_or_prepare_insert_large(
+      absl::type_identity_t<K> key) {
+    static_assert(std::is_same_v<K, FindLargeKey<K>>);
     prefetch_heap_block();
     const size_t cap = capacity();
     const size_t hash = hash_of(key);
@@ -3881,24 +3928,25 @@
 #ifndef ABSL_HAVE_MEMORY_SANITIZER
       absl::PrefetchToLocalCache(slot_array + seq.offset());
 #endif
-      for (uint32_t i : g.Match(h2)) {
-        slot_type* slot = slot_array + seq.offset(i);
-        if (ABSL_PREDICT_TRUE(equal_to(key, slot))) {
-          return {slot, false};
-        }
+      // Predict one iteration (common case for insert-hit).
+      if (auto match = g.Match(h2); ABSL_PREDICT_TRUE(match)) {
+        do {
+          slot_type* slot = slot_array + seq.offset(*match);
+          if (ABSL_PREDICT_TRUE(equal_to(key, slot))) return {slot, false};
+        } while (ABSL_PREDICT_FALSE(++match));
       }
       auto mask_empty = g.MaskEmpty();
       if (ABSL_PREDICT_TRUE(mask_empty)) {
         size_t target_group_offset = seq.offset();
-        void* slot = SwisstableGenerationsEnabled()
-                         ? PrepareInsertLargeGenerationsEnabled(
-                               common(), GetPolicyFunctions(), hash, mask_empty,
-                               target_group_offset,
-                               HashKey<hasher, K, kIsAbslHash, kSeedShift>{
-                                   hash_ref(), key})
-                         : PrepareInsertLarge(
-                               common(), GetPolicyFunctions(), hash, mask_empty,
-                               target_group_offset);
+        void* slot =
+            SwisstableGenerationsEnabled()
+                ? PrepareInsertLargeGenerationsEnabled(
+                      common(), GetPolicyFunctions(), hash, mask_empty,
+                      target_group_offset,
+                      HashKey<hasher, absl::remove_cvref_t<K>, kIsAbslHash,
+                              kSeedShift>{hash_ref(), key})
+                : PrepareInsertLarge(common(), GetPolicyFunctions(), hash,
+                                     mask_empty, target_group_offset);
         return {to_slot(slot), true};
       }
       seq.next();
@@ -3921,6 +3969,12 @@
   // Asserts that the capacity is not a sentinel invalid value.
   void AssertNotDebugCapacity() const { common().AssertNotDebugCapacity(); }
 
+  template <class K>
+  static bool equal_to_key_fn(void* set, const void* key, void* slot) {
+    return static_cast<const raw_hash_set*>(set)->equal_to(
+        *static_cast<const K*>(key), to_slot(slot));
+  }
+
   // Asserts that hash and equal functors provided by the user are consistent,
   // meaning that `eq(k1, k2)` implies `hash(k1)==hash(k2)`.
   template <class K>
@@ -3936,26 +3990,10 @@
         std::is_same_v<key_equal, std::equal_to<key_type>>;
     if constexpr (kIsDebug && !kIsStringAbslHashEq && !kIsScalarAbslHashEq) {
       if (empty()) return;
-
-      const size_t hash_of_arg = hash_of(key);
-      const auto assert_consistent = [&](const ctrl_t*, void* slot) {
-        const bool is_key_equal = equal_to(key, to_slot(slot));
-        if (!is_key_equal) return;
-
-        [[maybe_unused]] const bool is_hash_equal =
-            hash_of_arg == hash_of(to_slot(slot));
-        assert((!is_key_equal || is_hash_equal) &&
-               "eq(k1, k2) must imply that hash(k1) == hash(k2). "
-               "hash/eq functors are inconsistent.");
-      };
-
-      if (is_small()) {
-        assert_consistent(/*unused*/ nullptr, single_slot());
-        return;
-      }
-      // We only do validation for small tables so that it's constant time.
-      if (capacity() > 16) return;
-      IterateOverFullSlots(common(), sizeof(slot_type), assert_consistent);
+      AssertHashEqConsistentImpl(
+          common(), get_hash_ref_fn(common()), get_hash_slot_fn(),
+          sizeof(slot_type), SooEnabled(), hash_of(key), &key,
+          &equal_to_key_fn<K>);
     }
   }
 
@@ -3966,7 +4004,7 @@
   std::pair<slot_type*, bool> find_or_prepare_insert(const K& key) {
     AssertOnFind(key);
     return is_small() ? find_or_prepare_insert_small(key)
-                      : find_or_prepare_insert_large(key);
+                      : find_or_prepare_insert_large<FindLargeKey<K>>(key);
   }
 
   // Constructs the value in the space pointed by the iterator. This only works
@@ -4011,6 +4049,23 @@
  private:
   friend struct RawHashSetTestOnlyAccess;
 
+  // For string-like keys, we convert to string_view when calling find*_large()
+  // operations to (a) avoid repeatedly (i) branching on SSO or (ii) calling
+  // strlen(), (b) share code between the string-like types, (c) avoid
+  // materializing string_view on the stack just to pass a reference to it. Note
+  // that we don't pass FindLargeKey to find*_small() to avoid unnecessarily
+  // constructing string_view for small empty tables. Note: we use
+  // type_identity_t and static_asserts to ensure that the key type in
+  // find*_large is FindLargeKey. (type_identity_t ensures that the template
+  // argument is not deduced and must be specified explicitly.)
+  // TODO(ezb): extend this to other types (e.g. small trivial types).
+  template <class K>
+  using FindLargeKey = std::conditional_t<
+      std::is_same_v<hasher, absl::container_internal::StringHash> &&
+          std::is_same_v<key_equal, absl::container_internal::StringEq> &&
+          std::is_convertible_v<const K&, absl::string_view>,
+      const absl::string_view, const K&>;
+
   // Prefetch the heap-allocated memory region to resolve potential TLB and
   // cache misses. This is intended to overlap with execution of calculating the
   // hash for a key.
@@ -4088,6 +4143,11 @@
     // TODO(b/397453582): Remove support for const hasher.
     return const_cast<std::remove_const_t<hasher>*>(&h->hash_ref());
   }
+  static constexpr HashSlotFn get_hash_slot_fn() {
+    return PolicyTraits::template get_hash_slot_fn<hasher, kIsAbslHash,
+                                                   kSeedShift>();
+  }
+
   static void transfer_n_slots_fn(void* set, void* dst, void* src,
                                   size_t count) {
     auto* src_slot = to_slot(src);
@@ -4206,8 +4266,7 @@
         // for standard layout and alignof(Hash) <= alignof(CommonFields).
         std::is_empty_v<hasher> ? &GetRefForEmptyClass
                                 : &raw_hash_set::get_hash_ref_fn,
-        PolicyTraits::template get_hash_slot_fn<hasher, kIsAbslHash,
-                                                kSeedShift>(),
+        get_hash_slot_fn(),
         PolicyTraits::transfer_uses_memcpy()
             ? TransferNRelocatable<sizeof(slot_type)>
             : &raw_hash_set::transfer_n_slots_fn,
@@ -4358,17 +4417,17 @@
 // Extern template instantiations reduce binary size and linker input size.
 // Function definition is in raw_hash_set.cc.
 extern template void* GrowSooTableToNextCapacityAndPrepareInsert<0, false>(
-    CommonFields&, const PolicyFunctions&, absl::FunctionRef<size_t(size_t)>,
+    CommonFields&, const PolicyFunctions&, void*, HashForKeyFn,
     bool);
 extern template void* GrowSooTableToNextCapacityAndPrepareInsert<1, true>(
-    CommonFields&, const PolicyFunctions&, absl::FunctionRef<size_t(size_t)>,
+    CommonFields&, const PolicyFunctions&, void*, HashForKeyFn,
     bool);
 extern template void* GrowSooTableToNextCapacityAndPrepareInsert<4, true>(
-    CommonFields&, const PolicyFunctions&, absl::FunctionRef<size_t(size_t)>,
+    CommonFields&, const PolicyFunctions&, void*, HashForKeyFn,
     bool);
 #if UINTPTR_MAX == UINT64_MAX
 extern template void* GrowSooTableToNextCapacityAndPrepareInsert<8, true>(
-    CommonFields&, const PolicyFunctions&, absl::FunctionRef<size_t(size_t)>,
+    CommonFields&, const PolicyFunctions&, void*, HashForKeyFn,
     bool);
 #endif
 
diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc
index a4254ed..429d7cf 100644
--- a/absl/container/internal/raw_hash_set_test.cc
+++ b/absl/container/internal/raw_hash_set_test.cc
@@ -3797,6 +3797,90 @@
   EXPECT_TRUE((VerifyResultOf<CallCount, TransparentTable>()));
 }
 
+TEST(Table, StringViewConversionCount) {
+  DisableSampling();
+
+  struct ConvertibleToStringView {
+    absl::string_view value;
+    mutable int num_conversions = 0;
+
+    operator absl::string_view() const {  // NOLINT(runtime/explicit)
+      ++num_conversions;
+      return value;
+    }
+  };
+
+  const auto test_table = [](auto soo) {
+    using Policy = ValuePolicy<absl::string_view, /*kTransferable=*/true,
+                               /*kSoo=*/decltype(soo)::value>;
+    using Table = raw_hash_set<Policy, hash_default_hash<absl::string_view>,
+                               hash_default_eq<absl::string_view>>;
+    Table t;
+
+    // Empty table: no conversion to string_view.
+    {
+      ConvertibleToStringView k{"a"};
+      EXPECT_EQ(t.find(k), t.end());
+      EXPECT_EQ(k.num_conversions, 0);
+    }
+    {
+      ConvertibleToStringView k{"a"};
+      t.lazy_emplace(k, [&](const auto& ctor) {
+        EXPECT_EQ(k.num_conversions, 0);
+        ctor("a");
+      });
+    }
+
+    // Single-element table: 1 conversion to string_view.
+    ASSERT_TRUE(RawHashSetTestOnlyAccess::GetCommon(t).is_small());
+    {
+      ConvertibleToStringView k{"a"};
+      EXPECT_NE(t.find(k), t.end());
+      EXPECT_EQ(k.num_conversions, 1);
+    }
+    {
+      ConvertibleToStringView k{"b"};
+      EXPECT_EQ(t.find(k), t.end());
+      EXPECT_EQ(k.num_conversions, 1);
+    }
+    {
+      ConvertibleToStringView k{"a"};
+      EXPECT_FALSE(t.emplace(k).second);
+      EXPECT_EQ(k.num_conversions, 1);
+    }
+
+    // Many-element table: 1 conversion to string_view.
+    t.insert("b");
+    t.insert("c");
+    ASSERT_FALSE(RawHashSetTestOnlyAccess::GetCommon(t).is_small());
+    {
+      ConvertibleToStringView k{"a"};
+      EXPECT_NE(t.find(k), t.end());
+      EXPECT_EQ(k.num_conversions, 1);
+    }
+    {
+      ConvertibleToStringView k{"z"};
+      EXPECT_EQ(t.find(k), t.end());
+      EXPECT_EQ(k.num_conversions, 1);
+    }
+    {
+      ConvertibleToStringView k{"a"};
+      EXPECT_FALSE(t.emplace(k).second);
+      EXPECT_EQ(k.num_conversions, 1);
+    }
+    {
+      ConvertibleToStringView k{"d"};
+      t.lazy_emplace(k, [&](const auto& ctor) {
+        EXPECT_EQ(k.num_conversions, 1);
+        ctor("d");
+      });
+    }
+  };
+
+  test_table(/*soo=*/std::false_type{});
+  test_table(/*soo=*/std::true_type{});
+}
+
 TEST(Iterator, IsDefaultConstructible) {
   StringTable::iterator i;
   EXPECT_TRUE(i == StringTable::iterator());
diff --git a/absl/container/linked_hash_map.h b/absl/container/linked_hash_map.h
index d04807b..60d53d3 100644
--- a/absl/container/linked_hash_map.h
+++ b/absl/container/linked_hash_map.h
@@ -436,7 +436,8 @@
     return LazyEmplaceInternal(key).first->second;
   }
 
-  template <class K = key_type, K* = nullptr>
+  template <class K = key_type,
+            std::enable_if_t<!std::is_reference_v<K>, int> = 0>
   mapped_type& operator[](key_arg<K>&& key) {
     return LazyEmplaceInternal(std::forward<key_arg<K>>(key)).first->second;
   }
@@ -485,19 +486,22 @@
   //   union { int n : 1; };
   //   linked_hash_map<int, int> m;
   //   m.insert_or_assign(n, n);
-  template <class K = key_type, class V = mapped_type, K* = nullptr,
-            V* = nullptr>
+  template <class K = key_type, class V = mapped_type,
+            std::enable_if_t<!std::is_reference_v<K> && !std::is_reference_v<V>,
+                             int> = 0>
   std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, V&& v) {
     return InsertOrAssignInternal(std::forward<key_arg<K>>(k),
                                   std::forward<V>(v));
   }
 
-  template <class K = key_type, class V = mapped_type, K* = nullptr>
+  template <class K = key_type, class V = mapped_type,
+            std::enable_if_t<!std::is_reference_v<K>, int> = 0>
   std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, const V& v) {
     return InsertOrAssignInternal(std::forward<key_arg<K>>(k), v);
   }
 
-  template <class K = key_type, class V = mapped_type, V* = nullptr>
+  template <class K = key_type, class V = mapped_type,
+            std::enable_if_t<!std::is_reference_v<V>, int> = 0>
   std::pair<iterator, bool> insert_or_assign(const key_arg<K>& k, V&& v) {
     return InsertOrAssignInternal(k, std::forward<V>(v));
   }
@@ -507,19 +511,22 @@
     return InsertOrAssignInternal(k, v);
   }
 
-  template <class K = key_type, class V = mapped_type, K* = nullptr,
-            V* = nullptr>
+  template <class K = key_type, class V = mapped_type,
+            std::enable_if_t<!std::is_reference_v<K> && !std::is_reference_v<V>,
+                             int> = 0>
   iterator insert_or_assign(const_iterator, key_arg<K>&& k, V&& v) {
     return insert_or_assign(std::forward<key_arg<K>>(k), std::forward<V>(v))
         .first;
   }
 
-  template <class K = key_type, class V = mapped_type, K* = nullptr>
+  template <class K = key_type, class V = mapped_type,
+            std::enable_if_t<!std::is_reference_v<K>, int> = 0>
   iterator insert_or_assign(const_iterator, key_arg<K>&& k, const V& v) {
     return insert_or_assign(std::forward<key_arg<K>>(k), v).first;
   }
 
-  template <class K = key_type, class V = mapped_type, V* = nullptr>
+  template <class K = key_type, class V = mapped_type,
+            std::enable_if_t<!std::is_reference_v<V>, int> = 0>
   iterator insert_or_assign(const_iterator, const key_arg<K>& k, V&& v) {
     return insert_or_assign(k, std::forward<V>(v)).first;
   }
@@ -540,7 +547,8 @@
     return {list_iter, true};
   }
 
-  template <class K = key_type, class... Args, K* = nullptr>
+  template <class K = key_type, class... Args,
+            std::enable_if_t<!std::is_reference_v<K>, int> = 0>
   iterator try_emplace(const_iterator, key_arg<K>&& k, Args&&... args) {
     return try_emplace(std::forward<key_arg<K>>(k), std::forward<Args>(args)...)
         .first;
@@ -551,7 +559,8 @@
     return emplace(std::forward<Args>(args)...).first;
   }
 
-  template <class K = key_type, typename... Args, K* = nullptr>
+  template <class K = key_type, typename... Args,
+            std::enable_if_t<!std::is_reference_v<K>, int> = 0>
   std::pair<iterator, bool> try_emplace(key_arg<K>&& key, Args&&... args) {
     return LazyEmplaceInternal(std::forward<key_arg<K>>(key),
                                std::forward<Args>(args)...);
diff --git a/absl/container/linked_hash_set.h b/absl/container/linked_hash_set.h
index 10ba289..18bee87 100644
--- a/absl/container/linked_hash_set.h
+++ b/absl/container/linked_hash_set.h
@@ -401,7 +401,8 @@
   std::pair<iterator, bool> insert(const key_arg<K>& k) {
     return InsertInternal(list_.end(), k);
   }
-  template <typename K = key_type, K* = nullptr>
+  template <typename K = key_type,
+            std::enable_if_t<!std::is_reference_v<K>, int> = 0>
   std::pair<iterator, bool> insert(key_arg<K>&& k) {
     return InsertInternal(list_.end(), std::move(k));
   }
@@ -414,11 +415,12 @@
   iterator insert(const_iterator hint, const key_arg<K>& k) {
     return InsertInternal(hint, k).first;
   }
-  template <
-      typename K = key_type, K* = nullptr,
-      std::enable_if_t<!std::is_convertible_v<key_arg<K>&&, const_iterator> &&
-                           !std::is_convertible_v<key_arg<K>&&, iterator>,
-                       int> = 0>
+  template <typename K = key_type,
+            std::enable_if_t<
+                !std::is_reference_v<K> &&
+                    !std::is_convertible_v<key_arg<K>&&, const_iterator> &&
+                    !std::is_convertible_v<key_arg<K>&&, iterator>,
+                int> = 0>
   iterator insert(const_iterator hint, key_arg<K>&& k) {
     return InsertInternal(hint, std::move(k)).first;
   }
diff --git a/absl/crc/internal/crc_x86_arm_combined.cc b/absl/crc/internal/crc_x86_arm_combined.cc
index 8a2a400..95221e4 100644
--- a/absl/crc/internal/crc_x86_arm_combined.cc
+++ b/absl/crc/internal/crc_x86_arm_combined.cc
@@ -593,6 +593,8 @@
   // instruction, and up to kMaxStreams blocks computed with PCLMULQDQ, which
   // are combined in the end.
   static constexpr size_t kMaxStreams = 3;
+  // When copying, at most kMaxCopyStreams streams are used in total.
+  static constexpr size_t kMaxCopyStreams = 3;
 };
 
 template <size_t num_crc_streams, size_t num_pclmul_streams,
@@ -745,6 +747,9 @@
                   "Invalid number of crc streams");
     static_assert(num_pclmul_streams >= 0 && num_pclmul_streams <= kMaxStreams,
                   "Invalid number of pclmul streams");
+    constexpr StreamCounts kStreams = GetStreamCounts(copy_type);
+    constexpr size_t kNumActiveCrcStreams = kStreams.crc;
+    constexpr size_t kNumActivePclmulStreams = kStreams.pclmul;
     const uint8_t* p = static_cast<const uint8_t*>(bytes);
     const uint8_t* e = p + length;
     char* d = static_cast<char*>(dst);
@@ -779,15 +784,15 @@
     }
 
     size_t bs = static_cast<size_t>(e - p) /
-                (num_crc_streams + num_pclmul_streams) / 64;
+                (kNumActiveCrcStreams + kNumActivePclmulStreams) / 64;
     const uint8_t* stream_start = p;
     const uint8_t* crc_streams[kMaxStreams];
-    for (size_t i = 0; i < num_crc_streams; i++) {
+    for (size_t i = 0; i < kNumActiveCrcStreams; i++) {
       crc_streams[i] = stream_start;
       stream_start += bs * 64;
     }
     const uint8_t* pclmul_streams[kMaxStreams];
-    for (size_t i = 0; i < num_pclmul_streams; i++) {
+    for (size_t i = 0; i < kNumActivePclmulStreams; i++) {
       pclmul_streams[i] = stream_start;
       stream_start += bs * 64;
     }
@@ -796,11 +801,11 @@
     uint8_t* dst_pclmul_streams[kMaxStreams];
     if constexpr (copy_type != CopyType::kNone) {
       uint8_t* dst_stream_start = reinterpret_cast<uint8_t*>(d);
-      for (size_t i = 0; i < num_crc_streams; i++) {
+      for (size_t i = 0; i < kNumActiveCrcStreams; i++) {
         dst_crc_streams[i] = dst_stream_start;
         dst_stream_start += bs * 64;
       }
-      for (size_t i = 0; i < num_pclmul_streams; i++) {
+      for (size_t i = 0; i < kNumActivePclmulStreams; i++) {
         dst_pclmul_streams[i] = dst_stream_start;
         dst_stream_start += bs * 64;
       }
@@ -812,26 +817,26 @@
 
     V256 pclmul_v0_0[kMaxStreams];
     V256 pclmul_v1_0[kMaxStreams];
-    for (size_t j = 0; j < num_pclmul_streams; j++) {
+    for (size_t j = 0; j < kNumActivePclmulStreams; j++) {
       V256_LoadPairU(pclmul_streams[j], &pclmul_v0_0[j], &pclmul_v1_0[j]);
     }
 
     if constexpr (copy_type != CopyType::kNone) {
-      for (size_t j = 0; j < num_crc_streams; j++) {
+      for (size_t j = 0; j < kNumActiveCrcStreams; j++) {
         Copy64Bytes<copy_type>(dst_crc_streams[j], crc_streams[j]);
         dst_crc_streams[j] += 64;
       }
-      for (size_t j = 0; j < num_pclmul_streams; j++) {
+      for (size_t j = 0; j < kNumActivePclmulStreams; j++) {
         Store64Bytes<copy_type>(dst_pclmul_streams[j], pclmul_v0_0[j],
                                 pclmul_v1_0[j]);
         dst_pclmul_streams[j] += 64;
       }
     }
 
-    if (num_crc_streams == 1) {
+    if (kNumActiveCrcStreams == 1) {
       l64_crc[0] = Process64BytesCRC(crc_streams[0], l64_crc[0]);
       crc_streams[0] += 16 * 4;
-    } else if (num_crc_streams == 2) {
+    } else if (kNumActiveCrcStreams == 2) {
       Process64BytesCRC2Streams(crc_streams[0], crc_streams[1], l64_crc);
       crc_streams[0] += 16 * 4;
       crc_streams[1] += 16 * 4;
@@ -845,21 +850,21 @@
 
     // Align to 32 bytes for vpclmul implementation.
     alignas(32) V128 partialCRC[kMaxStreams][4];
-    for (size_t i = 0; i < num_pclmul_streams; i++) {
+    for (size_t i = 0; i < kNumActivePclmulStreams; i++) {
       InitPclmulStream(pclmul_v0_0[i], pclmul_v1_0[i], partialCRC[i]);
       pclmul_streams[i] += 64;
     }
 
     for (size_t i = 1; i < bs; i++) {
       // Prefetch data for next iterations.
-      for (size_t j = 0; j < num_crc_streams; j++) {
+      for (size_t j = 0; j < kNumActiveCrcStreams; j++) {
         PrefetchToLocalCache(crc_streams[j] + kPrefetchHorizon);
       }
-      for (size_t j = 0; j < num_pclmul_streams; j++) {
+      for (size_t j = 0; j < kNumActivePclmulStreams; j++) {
         PrefetchToLocalCache(pclmul_streams[j] + kPrefetchHorizon);
       }
 
-      for (size_t j = 0; j < num_pclmul_streams; j++) {
+      for (size_t j = 0; j < kNumActivePclmulStreams; j++) {
         if constexpr (copy_type != CopyType::kNone) {
           ProcessPclmulStream<copy_type>(pclmul_streams[j],
                                          dst_pclmul_streams[j], partialCRC[j]);
@@ -871,10 +876,10 @@
         pclmul_streams[j] += 64;
       }
 
-      if (num_crc_streams == 1) {
+      if (kNumActiveCrcStreams == 1) {
         l64_crc[0] = Process64BytesCRC(crc_streams[0], l64_crc[0]);
         crc_streams[0] += 16 * 4;
-      } else if (num_crc_streams == 2) {
+      } else if (kNumActiveCrcStreams == 2) {
         Process64BytesCRC2Streams(crc_streams[0], crc_streams[1], l64_crc);
         crc_streams[0] += 16 * 4;
         crc_streams[1] += 16 * 4;
@@ -886,7 +891,7 @@
         crc_streams[2] += 16 * 4;
       }
       if constexpr (copy_type != CopyType::kNone) {
-        for (size_t j = 0; j < num_crc_streams; j++) {
+        for (size_t j = 0; j < kNumActiveCrcStreams; j++) {
           Copy64Bytes<copy_type>(dst_crc_streams[j], crc_streams[j] - 64);
           dst_crc_streams[j] += 64;
         }
@@ -895,7 +900,7 @@
 
     // PCLMULQDQ based streams require special final step;
     // CRC based don't.
-    for (size_t i = 0; i < num_pclmul_streams; i++) {
+    for (size_t i = 0; i < kNumActivePclmulStreams; i++) {
       l64_pclmul[i] = FinalizePclmulStream(partialCRC[i]);
     }
 
@@ -903,27 +908,28 @@
     static_assert(64 % (1 << kNumDroppedBits) == 0);
     uint32_t magic = ComputeZeroConstant(bs * 64);
     l64 = l64_crc[0];
-    for (size_t i = 1; i < num_crc_streams; i++) {
+    for (size_t i = 1; i < kNumActiveCrcStreams; i++) {
       l64 = MultiplyWithExtraX33(static_cast<uint32_t>(l64), magic);
       l64 ^= l64_crc[i];
     }
-    for (size_t i = 0; i < num_pclmul_streams; i++) {
+    for (size_t i = 0; i < kNumActivePclmulStreams; i++) {
       l64 = MultiplyWithExtraX33(static_cast<uint32_t>(l64), magic);
       l64 ^= l64_pclmul[i];
     }
 
     // Update p and d.
-    if constexpr (num_pclmul_streams > 0) {
-      p = pclmul_streams[num_pclmul_streams - 1];
+    if constexpr (kNumActivePclmulStreams > 0) {
+      p = pclmul_streams[kNumActivePclmulStreams - 1];
       if constexpr (copy_type != CopyType::kNone) {
-        d = reinterpret_cast<char*>(dst_pclmul_streams[num_pclmul_streams - 1]);
+        d = reinterpret_cast<char*>(
+            dst_pclmul_streams[kNumActivePclmulStreams - 1]);
         size_t remaining_to_copy = static_cast<size_t>(e - p);
         std::memcpy(d, p, remaining_to_copy);
       }
     } else {
-      p = crc_streams[num_crc_streams - 1];
+      p = crc_streams[kNumActiveCrcStreams - 1];
       if constexpr (copy_type != CopyType::kNone) {
-        d = reinterpret_cast<char*>(dst_crc_streams[num_crc_streams - 1]);
+        d = reinterpret_cast<char*>(dst_crc_streams[kNumActiveCrcStreams - 1]);
         size_t remaining_to_copy = static_cast<size_t>(e - p);
         std::memcpy(d, p, remaining_to_copy);
       }
@@ -966,6 +972,32 @@
   }
 
  private:
+  struct StreamCounts {
+    size_t crc;
+    size_t pclmul;
+  };
+
+  // Returns the number of crc and pclmul streams to use. Copying adds loads and
+  // stores, so fewer streams are needed to saturate the dispatch width. In copy
+  // mode, at most kMaxCopyStreams of the configured streams are used, taken in
+  // the order: pclmul 1, crc 1, pclmul 2, crc 2, pclmul 3, crc 3.
+  static constexpr StreamCounts GetStreamCounts(CopyType copy_type) {
+    if (copy_type == CopyType::kNone) {
+      return {num_crc_streams, num_pclmul_streams};
+    }
+    StreamCounts counts = {0, 0};
+    for (size_t i = 0; i < kMaxStreams; i++) {
+      if (i < num_pclmul_streams &&
+          counts.crc + counts.pclmul < kMaxCopyStreams) {
+        counts.pclmul++;
+      }
+      if (i < num_crc_streams && counts.crc + counts.pclmul < kMaxCopyStreams) {
+        counts.crc++;
+      }
+    }
+    return counts;
+  }
+
   ABSL_INTERNAL_ATTRIBUTE_AVX ABSL_ATTRIBUTE_ALWAYS_INLINE void
   InitPclmulStream(V256 v0, V256 v1, V128* partialCRC) const {
     if constexpr (pclmul_stream_type == PclmulStreamType::NONE) {
@@ -1046,9 +1078,6 @@
 CRCImpl* TryNewCRC32AcceleratedX86ARMCombined() {
   CpuType type = GetCpuType();
   switch (type) {
-    case CpuType::kAmdRome:
-      return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
-          3, 0, PclmulStreamType::PCLMUL>();
     case CpuType::kIntelHaswell:
     case CpuType::kAmdNaples:
       return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
@@ -1057,13 +1086,14 @@
     case CpuType::kAmdGenoa:
     case CpuType::kAmdSiena:
     case CpuType::kAmdTurin:
+    case CpuType::kAmdVenice:
 #if defined(ABSL_CRC_INTERNAL_HAVE_X86_SIMD) &&                   \
     (defined(__AVX__) || defined(ABSL_INTERNAL_CAN_FORCE_AVX)) && \
     (!defined(_MSC_VER) || defined(__clang__))
       // We don't have vector pclmul on arm, but this still needs to
       // compile.
       return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
-          3, 1, PclmulStreamType::VPCLMUL>();
+          2, 2, PclmulStreamType::VPCLMUL>();
 #else
       return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
           3, 1, PclmulStreamType::PCLMUL>();
@@ -1088,6 +1118,7 @@
       return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
           3, 2, PclmulStreamType::PCLMUL>();
     // PCLMULQDQ is slow, don't use it.
+    case CpuType::kAmdRome:
     case CpuType::kIntelIvybridge:
     case CpuType::kIntelSandybridge:
     case CpuType::kIntelWestmere:
diff --git a/absl/flags/flag_benchmark.cc b/absl/flags/flag_benchmark.cc
index be435d9..2a356ee 100644
--- a/absl/flags/flag_benchmark.cc
+++ b/absl/flags/flag_benchmark.cc
@@ -76,8 +76,8 @@
 
 struct UDT {
   UDT() = default;
-  UDT(const UDT&) {}
-  UDT& operator=(const UDT&) { return *this; }
+  UDT(const UDT&) = default;
+  UDT& operator=(const UDT&) = default;
 };
 // Next two functions represent Abseil Flags marshalling for UDT.
 bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; }
diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc
index 458f1b1..1d6a496 100644
--- a/absl/flags/flag_test.cc
+++ b/absl/flags/flag_test.cc
@@ -1022,10 +1022,8 @@
   NonTriviallyCopyableUDT(const NonTriviallyCopyableUDT& rhs) : c(rhs.c) {
     s_num_instance++;
   }
-  NonTriviallyCopyableUDT& operator=(const NonTriviallyCopyableUDT& rhs) {
-    c = rhs.c;
-    return *this;
-  }
+  NonTriviallyCopyableUDT& operator=(const NonTriviallyCopyableUDT& rhs) =
+      default;
   ~NonTriviallyCopyableUDT() { s_num_instance--; }
 
   static uint64_t s_num_instance;
diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt
index 914ba0f..8a31015 100644
--- a/absl/random/CMakeLists.txt
+++ b/absl/random/CMakeLists.txt
@@ -873,6 +873,7 @@
     ${ABSL_DEFAULT_LINKOPTS}
   DEPS
     absl::config
+    absl::core_headers
     absl::random_internal_platform
     absl::random_internal_randen_hwaes_impl
 )
diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel
index d1bf943..04f9a27 100644
--- a/absl/random/internal/BUILD.bazel
+++ b/absl/random/internal/BUILD.bazel
@@ -431,6 +431,7 @@
     deps = [
         ":platform",
         "//absl/base:config",
+        "//absl/base:core_headers",
         "//absl/numeric:int128",
     ],
 )
diff --git a/absl/random/internal/nanobenchmark.cc b/absl/random/internal/nanobenchmark.cc
index 0cf657b..08e7f15 100644
--- a/absl/random/internal/nanobenchmark.cc
+++ b/absl/random/internal/nanobenchmark.cc
@@ -61,15 +61,6 @@
 #include <time.h>  // NOLINT
 #endif
 
-// ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE prevents inlining of the method.
-#if ABSL_HAVE_ATTRIBUTE(noinline) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE __attribute__((noinline))
-#elif defined(_MSC_VER)
-#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE __declspec(noinline)
-#else
-#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE
-#endif
-
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace random_internal_nanobenchmark {
@@ -670,8 +661,8 @@
 }
 
 // (Nearly) empty Func for measuring timer overhead/resolution.
-ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE FuncOutput
-EmptyFunc(const void* arg, const FuncInput input) {
+ABSL_ATTRIBUTE_NOINLINE FuncOutput EmptyFunc(const void* arg [[maybe_unused]],
+                                             const FuncInput input) {
   return input;
 }
 
diff --git a/absl/random/internal/randen_detect.cc b/absl/random/internal/randen_detect.cc
index 83bde75..888e36f 100644
--- a/absl/random/internal/randen_detect.cc
+++ b/absl/random/internal/randen_detect.cc
@@ -243,8 +243,8 @@
   // * Rely on compiler-generated target-based dispatch.
   // Using x86/gcc it might look something like this:
   //
-  // int __attribute__((target("aes"))) HasAes() { return 1; }
-  // int __attribute__((target("default"))) HasAes() { return 0; }
+  // __attribute__((target("aes"))) int HasAes() { return 1; }
+  // __attribute__((target("default"))) int HasAes() { return 0; }
   //
   // This does not work on all architecture/compiler combinations.
   //
diff --git a/absl/random/internal/randen_hwaes.cc b/absl/random/internal/randen_hwaes.cc
index f8dd358..b632aa9 100644
--- a/absl/random/internal/randen_hwaes.cc
+++ b/absl/random/internal/randen_hwaes.cc
@@ -21,6 +21,7 @@
 
 #include <cstring>
 
+#include "absl/base/attributes.h"
 #include "absl/base/config.h"
 #include "absl/numeric/int128.h"
 #include "absl/random/internal/platform.h"
@@ -97,9 +98,17 @@
 // NOTE: Evaluate whether we should eliminate ABSL_TARGET_CRYPTO.
 #if (defined(__clang__) || defined(__GNUC__))
 #if defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32)
+#if ABSL_HAVE_CPP_ATTRIBUTE(gnu::target)
+#define ABSL_TARGET_CRYPTO [[gnu::target("aes")]]
+#else
 #define ABSL_TARGET_CRYPTO __attribute__((target("aes")))
+#endif
 #elif defined(ABSL_ARCH_PPC)
+#if ABSL_HAVE_CPP_ATTRIBUTE(gnu::target)
+#define ABSL_TARGET_CRYPTO [[gnu::target("crypto")]]
+#else
 #define ABSL_TARGET_CRYPTO __attribute__((target("crypto")))
+#endif
 #else
 #define ABSL_TARGET_CRYPTO
 #endif
@@ -125,7 +134,7 @@
 using Vector128 = __vector unsigned long long;  // NOLINT(runtime/int)
 
 namespace {
-inline ABSL_TARGET_CRYPTO Vector128 ReverseBytes(const Vector128& v) {
+ABSL_TARGET_CRYPTO inline Vector128 ReverseBytes(const Vector128& v) {
   // Reverses the bytes of the vector.
   const __vector unsigned char perm = {15, 14, 13, 12, 11, 10, 9, 8,
                                        7,  6,  5,  4,  3,  2,  1, 0};
@@ -135,23 +144,23 @@
 // WARNING: these load/store in native byte order. It is OK to load and then
 // store an unchanged vector, but interpreting the bits as a number or input
 // to AES will have undefined results.
-inline ABSL_TARGET_CRYPTO Vector128 Vector128Load(const void* from) {
+ABSL_TARGET_CRYPTO inline Vector128 Vector128Load(const void* from) {
   return vec_vsx_ld(0, reinterpret_cast<const Vector128*>(from));
 }
 
-inline ABSL_TARGET_CRYPTO void Vector128Store(const Vector128& v, void* to) {
+ABSL_TARGET_CRYPTO inline void Vector128Store(const Vector128& v, void* to) {
   vec_vsx_st(v, 0, reinterpret_cast<Vector128*>(to));
 }
 
 // One round of AES. "round_key" is a public constant for breaking the
 // symmetry of AES (ensures previously equal columns differ afterwards).
-inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state,
+ABSL_TARGET_CRYPTO inline Vector128 AesRound(const Vector128& state,
                                              const Vector128& round_key) {
   return Vector128(__builtin_crypto_vcipher(state, round_key));
 }
 
 // Enables native loads in the round loop by pre-swapping.
-inline ABSL_TARGET_CRYPTO void SwapEndian(absl::uint128* state) {
+ABSL_TARGET_CRYPTO inline void SwapEndian(absl::uint128* state) {
   for (uint32_t block = 0; block < RandenTraits::kFeistelBlocks; ++block) {
     Vector128Store(ReverseBytes(Vector128Load(state + block)), state + block);
   }
@@ -182,17 +191,17 @@
 
 namespace {
 
-inline ABSL_TARGET_CRYPTO Vector128 Vector128Load(const void* from) {
+ABSL_TARGET_CRYPTO inline Vector128 Vector128Load(const void* from) {
   return vld1q_u8(reinterpret_cast<const uint8_t*>(from));
 }
 
-inline ABSL_TARGET_CRYPTO void Vector128Store(const Vector128& v, void* to) {
+ABSL_TARGET_CRYPTO inline void Vector128Store(const Vector128& v, void* to) {
   vst1q_u8(reinterpret_cast<uint8_t*>(to), v);
 }
 
 // One round of AES. "round_key" is a public constant for breaking the
 // symmetry of AES (ensures previously equal columns differ afterwards).
-inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state,
+ABSL_TARGET_CRYPTO inline Vector128 AesRound(const Vector128& state,
                                              const Vector128& round_key) {
   // It is important to always use the full round function - omitting the
   // final MixColumns reduces security [https://eprint.iacr.org/2010/041.pdf]
@@ -204,7 +213,7 @@
   return vaesmcq_u8(vaeseq_u8(state, uint8x16_t{})) ^ round_key;
 }
 
-inline ABSL_TARGET_CRYPTO void SwapEndian(void*) {}
+ABSL_TARGET_CRYPTO inline void SwapEndian(void*) {}
 
 }  // namespace
 
@@ -232,17 +241,17 @@
   __m128i data_;
 };
 
-inline ABSL_TARGET_CRYPTO Vector128 Vector128Load(const void* from) {
+ABSL_TARGET_CRYPTO inline Vector128 Vector128Load(const void* from) {
   return Vector128(_mm_load_si128(reinterpret_cast<const __m128i*>(from)));
 }
 
-inline ABSL_TARGET_CRYPTO void Vector128Store(const Vector128& v, void* to) {
+ABSL_TARGET_CRYPTO inline void Vector128Store(const Vector128& v, void* to) {
   _mm_store_si128(reinterpret_cast<__m128i*>(to), v.data());
 }
 
 // One round of AES. "round_key" is a public constant for breaking the
 // symmetry of AES (ensures previously equal columns differ afterwards).
-inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state,
+ABSL_TARGET_CRYPTO inline Vector128 AesRound(const Vector128& state,
                                              const Vector128& round_key) {
   // It is important to always use the full round function - omitting the
   // final MixColumns reduces security [https://eprint.iacr.org/2010/041.pdf]
@@ -250,7 +259,7 @@
   return Vector128(_mm_aesenc_si128(state.data(), round_key.data()));
 }
 
-inline ABSL_TARGET_CRYPTO void SwapEndian(void*) {}
+ABSL_TARGET_CRYPTO inline void SwapEndian(void*) {}
 
 }  // namespace
 
@@ -276,7 +285,7 @@
 
 // Block shuffles applies a shuffle to the entire state between AES rounds.
 // Improved odd-even shuffle from "New criterion for diffusion property".
-inline ABSL_TARGET_CRYPTO void BlockShuffle(absl::uint128* state) {
+ABSL_TARGET_CRYPTO inline void BlockShuffle(absl::uint128* state) {
   static_assert(RandenTraits::kFeistelBlocks == 16,
                 "Expecting 16 FeistelBlocks.");
 
@@ -323,7 +332,7 @@
 // per 16 bytes (vs. 10 for AES-CTR). Computing eight round functions in
 // parallel hides the 7-cycle AESNI latency on HSW. Note that the Feistel
 // XORs are 'free' (included in the second AES instruction).
-inline ABSL_TARGET_CRYPTO const absl::uint128* FeistelRound(
+ABSL_TARGET_CRYPTO inline const absl::uint128* FeistelRound(
     absl::uint128* state,
     const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
   static_assert(RandenTraits::kFeistelBlocks == 16,
@@ -385,7 +394,7 @@
 // Indistinguishable from ideal by chosen-ciphertext adversaries using less than
 // 2^64 queries if the round function is a PRF. This is similar to the b=8 case
 // of Simpira v2, but more efficient than its generic construction for b=16.
-inline ABSL_TARGET_CRYPTO void Permute(
+ABSL_TARGET_CRYPTO inline void Permute(
     absl::uint128* state,
     const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
   // (Successfully unrolled; the first iteration jumps into the second half)
@@ -406,7 +415,7 @@
 
 bool HasRandenHwAesImplementation() { return true; }
 
-const void* ABSL_TARGET_CRYPTO RandenHwAes::GetKeys() {
+ABSL_TARGET_CRYPTO const void* RandenHwAes::GetKeys() {
   // Round keys for one AES per Feistel round and branch.
   // The canonical implementation uses first digits of Pi.
 #if defined(ABSL_ARCH_PPC)
@@ -417,7 +426,7 @@
 }
 
 // NOLINTNEXTLINE
-void ABSL_TARGET_CRYPTO RandenHwAes::Absorb(const void* seed_void,
+ABSL_TARGET_CRYPTO void RandenHwAes::Absorb(const void* seed_void,
                                             void* state_void) {
   static_assert(RandenTraits::kCapacityBytes / sizeof(Vector128) == 1,
                 "Unexpected Randen kCapacityBlocks");
@@ -492,7 +501,7 @@
 }
 
 // NOLINTNEXTLINE
-void ABSL_TARGET_CRYPTO RandenHwAes::Generate(const void* keys_void,
+ABSL_TARGET_CRYPTO void RandenHwAes::Generate(const void* keys_void,
                                               void* state_void) {
   static_assert(RandenTraits::kCapacityBytes == sizeof(Vector128),
                 "Capacity mismatch");
diff --git a/absl/random/internal/randen_slow.cc b/absl/random/internal/randen_slow.cc
index d2b7da3..12aa4b1 100644
--- a/absl/random/internal/randen_slow.cc
+++ b/absl/random/internal/randen_slow.cc
@@ -25,19 +25,6 @@
 #include "absl/random/internal/platform.h"
 #include "absl/random/internal/randen_traits.h"
 
-#if ABSL_HAVE_ATTRIBUTE(always_inline) || \
-    (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE \
-  __attribute__((always_inline))
-#elif defined(_MSC_VER)
-// We can achieve something similar to attribute((always_inline)) with MSVC by
-// using the __forceinline keyword, however this is not perfect. MSVC is
-// much less aggressive about inlining, and even with the __forceinline keyword.
-#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE __forceinline
-#else
-#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE
-#endif
-
 namespace {
 
 // AES portions based on rijndael-alg-fst.c,
@@ -236,22 +223,21 @@
   uint32_t s[4];
 };
 
-inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128
-Vector128Load(const void* from) {
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline Vector128 Vector128Load(const void* from) {
   Vector128 result;
   std::memcpy(result.s, from, sizeof(Vector128));
   return result;
 }
 
-inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void Vector128Store(
-    const Vector128& v, void* to) {
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void Vector128Store(const Vector128& v,
+                                                        void* to) {
   std::memcpy(to, v.s, sizeof(Vector128));
 }
 
 // One round of AES. "round_key" is a public constant for breaking the
 // symmetry of AES (ensures previously equal columns differ afterwards).
-inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128
-AesRound(const Vector128& state, const Vector128& round_key) {
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline Vector128 AesRound(
+    const Vector128& state, const Vector128& round_key) {
   Vector128 result;
 #ifdef ABSL_IS_LITTLE_ENDIAN
   result.s[0] = round_key.s[0] ^                  //
@@ -302,8 +288,7 @@
 using ::absl::random_internal::RandenTraits;
 
 // The improved Feistel block shuffle function for 16 blocks.
-inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void BlockShuffle(
-    absl::uint128* state) {
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void BlockShuffle(absl::uint128* state) {
   static_assert(RandenTraits::kFeistelBlocks == 16,
                 "Feistel block shuffle only works for 16 blocks.");
 
@@ -361,9 +346,9 @@
 // per 16 bytes (vs. 10 for AES-CTR). Computing eight round functions in
 // parallel hides the 7-cycle AESNI latency on HSW. Note that the Feistel
 // XORs are 'free' (included in the second AES instruction).
-inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE const absl::uint128*
-FeistelRound(absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT state,
-             const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline const absl::uint128* FeistelRound(
+    absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT state,
+    const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
   for (size_t branch = 0; branch < RandenTraits::kFeistelBlocks; branch += 4) {
     const Vector128 s0 = Vector128Load(state + branch);
     const Vector128 s1 = Vector128Load(state + branch + 1);
@@ -387,7 +372,7 @@
 // Indistinguishable from ideal by chosen-ciphertext adversaries using less than
 // 2^64 queries if the round function is a PRF. This is similar to the b=8 case
 // of Simpira v2, but more efficient than its generic construction for b=16.
-inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void Permute(
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void Permute(
     absl::uint128* state,
     const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
   for (size_t round = 0; round < RandenTraits::kFeistelRounds; ++round) {
@@ -397,8 +382,7 @@
 }
 
 // Enables native loads in the round loop by pre-swapping.
-inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void SwapEndian(
-    absl::uint128* state) {
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void SwapEndian(absl::uint128* state) {
 #ifdef ABSL_IS_BIG_ENDIAN
   for (uint32_t block = 0; block < RandenTraits::kFeistelBlocks; ++block) {
     uint64_t new_lo = absl::little_endian::ToHost64(
diff --git a/absl/status/internal/status_internal.h b/absl/status/internal/status_internal.h
index 3bd7b1b..07a5679 100644
--- a/absl/status/internal/status_internal.h
+++ b/absl/status/internal/status_internal.h
@@ -112,6 +112,11 @@
       absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor)
       const;
 
+  // Returns the raw payload list, or nullptr if there are none. Unlike
+  // `ForEachPayload`, this does not hide `kStackTraceUrl` payloads and does not
+  // invalidate `type_url` lifetimes in debug builds; callers must handle both.
+  const Payloads* absl_nullable payloads() const { return payloads_.get(); }
+
   absl::Span<const SourceLocation> GetSourceLocations() const;
   void AddSourceLocation(absl::SourceLocation loc);
 
diff --git a/absl/status/status.cc b/absl/status/status.cc
index dc7d622..bb03157 100644
--- a/absl/status/status.cc
+++ b/absl/status/status.cc
@@ -18,6 +18,7 @@
 #include <cstddef>
 #include <cstdint>
 #include <memory>
+#include <optional>
 #include <ostream>
 #include <string>
 #include <type_traits>
@@ -28,6 +29,7 @@
 #include "absl/base/no_destructor.h"
 #include "absl/base/nullability.h"
 #include "absl/status/internal/status_internal.h"
+#include "absl/strings/cord.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/string_view.h"
 #include "absl/types/source_location.h"
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel
index 19692b3..5fe9b68 100644
--- a/absl/strings/BUILD.bazel
+++ b/absl/strings/BUILD.bazel
@@ -1170,6 +1170,7 @@
     deps = [
         ":string_view",
         ":strings",
+        "//absl/algorithm:container",
         "//absl/base:core_headers",
         "//absl/base:dynamic_annotations",
         "//absl/container:btree",
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt
index 23a32c9..1461d94 100644
--- a/absl/strings/CMakeLists.txt
+++ b/absl/strings/CMakeLists.txt
@@ -401,6 +401,7 @@
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
+    absl::algorithm_container
     absl::btree
     absl::core_headers
     absl::dynamic_annotations
diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc
index 1b60404..f9f576a 100644
--- a/absl/strings/cord.cc
+++ b/absl/strings/cord.cc
@@ -15,6 +15,7 @@
 #include "absl/strings/cord.h"
 
 #include <algorithm>
+#include <array>
 #include <cassert>
 #include <cstddef>
 #include <cstdint>
@@ -1203,81 +1204,6 @@
   }
 }
 
-}  // namespace
-
-// A few options how this could be implemented:
-// (a) Flatten the Cord and find, i.e.
-//       haystack.Flatten().find(needle)
-//     For large 'haystack' (where Cord makes sense to be used), this copies
-//     the whole 'haystack' and can be slow.
-// (b) Use std::search, i.e.
-//       std::search(haystack.char_begin(), haystack.char_end(),
-//                   needle.begin(), needle.end())
-//     This avoids the copy, but compares one byte at a time, and branches a
-//     lot every time it has to advance. It is also not possible to use
-//     std::search as is, because CharIterator is only an input iterator, not a
-//     forward iterator.
-// (c) Use string_view::find in each fragment, and specifically handle fragment
-//     boundaries.
-//
-// This currently implements option (b).
-absl::Cord::CharIterator absl::Cord::FindImpl(CharIterator it,
-                                              absl::string_view needle) const {
-  // Ensure preconditions are met by callers first.
-
-  // Needle must not be empty.
-  assert(!needle.empty());
-  // Haystack must be at least as large as needle.
-  assert(it.chunk_iterator_.bytes_remaining_ >= needle.size());
-
-  // Cord is a sequence of chunks. To find `needle` we go chunk by chunk looking
-  // for the first char of needle, up until we have advanced `N` defined as
-  // `haystack.size() - needle.size()`. If we find the first char of needle at
-  // `P` and `P` is less than `N`, we then call `IsSubstringInCordAt` to
-  // see if this is the needle. If not, we advance to `P + 1` and try again.
-  while (it.chunk_iterator_.bytes_remaining_ >= needle.size()) {
-    auto haystack_chunk = Cord::ChunkRemaining(it);
-    assert(!haystack_chunk.empty());
-    // Look for the first char of `needle` in the current chunk.
-    auto idx = haystack_chunk.find(needle.front());
-    if (idx == absl::string_view::npos) {
-      // No potential match in this chunk, advance past it.
-      Cord::Advance(&it, haystack_chunk.size());
-      continue;
-    }
-    // We found the start of a potential match in the chunk. Advance the
-    // iterator and haystack chunk to the match the position.
-    Cord::Advance(&it, idx);
-    // Check if there is enough haystack remaining to actually have a match.
-    if (it.chunk_iterator_.bytes_remaining_ < needle.size()) {
-      break;
-    }
-    // Check if this is `needle`.
-    if (IsSubstringInCordAt(it, needle)) {
-      return it;
-    }
-    // No match, increment the iterator for the next attempt.
-    Cord::Advance(&it, 1);
-  }
-  // If we got here, we did not find `needle`.
-  return char_end();
-}
-
-absl::Cord::CharIterator absl::Cord::Find(absl::string_view needle) const {
-  if (needle.empty()) {
-    return char_begin();
-  }
-  if (needle.size() > size()) {
-    return char_end();
-  }
-  if (needle.size() == size()) {
-    return *this == needle ? char_begin() : char_end();
-  }
-  return FindImpl(char_begin(), needle);
-}
-
-namespace {
-
 // Tests whether the sequence of chunks beginning at `haystack` starts with the
 // sequence of chunks beginning at `needle_begin` and extending to `needle_end`.
 //
@@ -1306,57 +1232,245 @@
 //
 // REQUIRES: remaining `absl::Cord` starting at `position` is greater than or
 // equal to `needle.size()`.
-bool IsSubcordInCordAt(absl::Cord::CharIterator position,
-                       const absl::Cord& needle) {
+bool IsSubstringInCordAt(absl::Cord::CharIterator position,
+                         const absl::Cord& needle) {
   return IsSubcordInCordAt(position, needle.char_begin(), needle.char_end());
 }
 
+// Returns the character located `offset` bytes past `it`.
+char CharacterAt(absl::Cord::CharIterator it, size_t offset) {
+  absl::Cord::Advance(&it, offset);
+  return *it;
+}
+
+inline size_t BytesRemaining(const absl::Cord::CharIterator& it) {
+  return static_cast<size_t>(
+      absl::Cord::Distance(it, absl::Cord::CharIterator()));
+}
+
+// Returns whether the sequence of chunks beginning at `chunk` + `idx` starts
+// with the first `chunk_rem` bytes of `needle`.
+//
+// REQUIRES: `chunk.size() - idx >= chunk_rem`.
+inline bool IsPrefixMatch(absl::string_view chunk, size_t idx, size_t chunk_rem,
+                          absl::string_view needle) {
+  return chunk.substr(idx) == needle.substr(0, chunk_rem);
+}
+
+// Returns whether the sequence of chunks beginning at `chunk` + `idx` starts
+// with the first `chunk_rem` bytes of `needle`, where `needle` is a Cord.
+//
+// REQUIRES: `chunk.size() - idx >= chunk_rem` and `needle` is not empty.
+inline bool IsPrefixMatch(absl::string_view chunk, size_t idx, size_t chunk_rem,
+                          const absl::Cord& needle) {
+  auto needle_chunk = absl::Cord::ChunkRemaining(needle.char_begin());
+  size_t check_len = std::min(chunk_rem, needle_chunk.size());
+  return chunk.substr(idx, check_len) == needle_chunk.substr(0, check_len);
+}
+
+// Initializes the Boyer-Moore-Horspool shift table for a string_view needle
+// with a maximum scan length of `kMaxScan`.
+inline void InitShift(std::array<uint16_t, 256>& shift,
+                      absl::string_view needle, uint16_t default_shift,
+                      size_t kMaxScan) {
+  shift.fill(default_shift);
+  const size_t m = needle.size();
+  for (size_t i = kMaxScan; i > 0; --i) {
+    shift[static_cast<unsigned char>(needle[m - 1 - i])] =
+        static_cast<uint16_t>(i);
+  }
+}
+
+// Initializes the Boyer-Moore-Horspool shift table for a Cord needle with a
+// maximum scan length of `kMaxScan`.
+inline void InitShift(std::array<uint16_t, 256>& shift,
+                      const absl::Cord& needle, uint16_t default_shift,
+                      size_t kMaxScan) {
+  shift.fill(default_shift);
+  auto needle_it = needle.char_begin();
+  const size_t needle_size = needle.size();
+  absl::Cord::Advance(&needle_it, needle_size - 1 - kMaxScan);
+  for (size_t i = 0; i < kMaxScan; ++i) {
+    shift[static_cast<unsigned char>(*needle_it)] =
+        static_cast<uint16_t>(kMaxScan - i);
+    absl::Cord::Advance(&needle_it, 1);
+  }
+}
+
+template <typename Needle>
+absl::Cord::CharIterator FindBMH(absl::Cord::CharIterator it,
+                                 const Needle& needle) {
+  const size_t needle_size = needle.size();
+  const char first_needle = needle[0];
+  std::array<uint16_t, 256> shift;
+  bool shift_initialized = false;
+  const size_t kMaxScan = std::min(needle_size - 1, size_t{256});
+  const uint16_t default_shift = static_cast<uint16_t>(kMaxScan + 1);
+  const unsigned char last_needle =
+      static_cast<unsigned char>(needle[needle_size - 1]);
+
+  while (BytesRemaining(it) >= needle_size) {
+    absl::string_view chunk = absl::Cord::ChunkRemaining(it);
+    auto idx = chunk.find(first_needle);
+    if (idx == absl::string_view::npos) {
+      // No potential match in this chunk, advance past it.
+      absl::Cord::Advance(&it, chunk.size());
+      continue;
+    }
+    // Check if there is enough haystack remaining to actually have a match.
+    if (BytesRemaining(it) - idx < needle_size) {
+      break;
+    }
+    // We found the start of a potential match in the chunk. Advance the
+    // iterator to the match position.
+    absl::Cord::Advance(&it, idx);
+    size_t chunk_rem = chunk.size() - idx;
+    unsigned char last_hay;
+    if (chunk_rem >= needle_size) {
+      last_hay = static_cast<unsigned char>(chunk[idx + needle_size - 1]);
+    } else {
+      if (!IsPrefixMatch(chunk, idx, chunk_rem, needle)) {
+        absl::Cord::Advance(&it, 1);
+        continue;
+      }
+      last_hay = static_cast<unsigned char>(CharacterAt(it, needle_size - 1));
+    }
+    // Check if the rightmost byte of the candidate window (`last_hay`) matches
+    // the rightmost byte of `needle` before performing a full equality check.
+    if (last_hay != last_needle) {
+      if (!shift_initialized) {
+        InitShift(shift, needle, default_shift, kMaxScan);
+        shift_initialized = true;
+      }
+      // Mismatch on the rightmost byte. Advance candidate position according
+      // to the Boyer-Moore-Horspool shift table.
+      absl::Cord::Advance(&it, shift[last_hay]);
+      continue;
+    }
+    // Check if this is `needle`.
+    if (IsSubstringInCordAt(it, needle)) {
+      return it;
+    }
+    if (!shift_initialized) {
+      InitShift(shift, needle, default_shift, kMaxScan);
+      shift_initialized = true;
+    }
+    last_hay = last_needle;
+    // Match check failed; advance candidate position according to the shift
+    // table.
+    absl::Cord::Advance(&it, shift[last_hay]);
+  }
+  // If we got here, we did not find `needle`.
+  return absl::Cord::CharIterator();
+}
+
+absl::Cord::CharIterator FindChar(absl::Cord::CharIterator it, char target) {
+  while (BytesRemaining(it) > 0) {
+    absl::string_view chunk = absl::Cord::ChunkRemaining(it);
+    auto idx = chunk.find(target);
+    if (idx != absl::string_view::npos) {
+      // Found target inside the current chunk. Advance the iterator to its
+      // position.
+      absl::Cord::Advance(&it, idx);
+      return it;
+    }
+    // Target is not inside this chunk; advance past the entire chunk.
+    absl::Cord::Advance(&it, chunk.size());
+  }
+  return absl::Cord::CharIterator();
+}
+
 }  // namespace
 
-absl::Cord::CharIterator absl::Cord::Find(const absl::Cord& needle) const {
+// A few options how this could be implemented:
+// (a) Flatten the Cord and find, i.e.
+//       haystack.Flatten().find(needle)
+//     For large 'haystack' (where Cord makes sense to be used), this copies
+//     the whole 'haystack' and can be slow.
+// (b) Use std::search, i.e.
+//       std::search(haystack.char_begin(), haystack.char_end(),
+//                   needle.begin(), needle.end())
+//     This avoids the copy, but compares one byte at a time, and branches a
+//     lot every time it has to advance. It is also not possible to use
+//     std::search as is, because CharIterator is only an input iterator, not a
+//     forward iterator.
+// (c) Use string_view::find in each fragment, and specifically handle fragment
+//     boundaries.
+//
+// This currently implements option (b).
+template <typename T>
+absl::Cord::CharIterator absl::Cord::FindImpl(CharIterator it,
+                                              const T& needle) const {
+  // Ensure preconditions are met by callers first.
+
   if (needle.empty()) {
     return char_begin();
   }
-  const auto needle_size = needle.size();
-  if (needle_size > size()) {
+  if (needle.size() > size()) {
     return char_end();
   }
-  if (needle_size == size()) {
+  if (needle.size() == size()) {
     return *this == needle ? char_begin() : char_end();
   }
-  const auto needle_chunk = Cord::ChunkRemaining(needle.char_begin());
-  auto haystack_it = char_begin();
-  while (true) {
-    haystack_it = FindImpl(haystack_it, needle_chunk);
-    if (haystack_it == char_end() ||
-        haystack_it.chunk_iterator_.bytes_remaining_ < needle_size) {
-      break;
-    }
-    // We found the first chunk of `needle` at `haystack_it` but not the entire
-    // subcord. Advance past the first chunk and check for the remainder.
-    auto haystack_advanced_it = haystack_it;
-    auto needle_it = needle.char_begin();
-    Cord::Advance(&haystack_advanced_it, needle_chunk.size());
-    Cord::Advance(&needle_it, needle_chunk.size());
-    if (IsSubcordInCordAt(haystack_advanced_it, needle_it, needle.char_end())) {
-      return haystack_it;
-    }
-    Cord::Advance(&haystack_it, 1);
-    if (haystack_it.chunk_iterator_.bytes_remaining_ < needle_size) {
-      break;
-    }
-    if (haystack_it.chunk_iterator_.bytes_remaining_ == needle_size) {
-      // Special case, if there is exactly `needle_size` bytes remaining, the
-      // subcord is either at `haystack_it` or not at all.
-      if (IsSubcordInCordAt(haystack_it, needle)) {
-        return haystack_it;
-      }
-      break;
-    }
+
+  // Needle must not be empty.
+  assert(!needle.empty());
+  // Haystack must be at least as large as needle.
+  assert(it.chunk_iterator_.bytes_remaining_ >= needle.size());
+
+  const size_t m = needle.size();
+  const char first_needle = needle[0];
+  // Fast path for single-character needles.
+  if (m == 1) {
+    return FindChar(it, first_needle);
   }
+
+  // Fast path for long needles.
+  if (m >= 256) {
+    return FindBMH(it, needle);
+  }
+
+  // Cord is a sequence of chunks. To find `needle` we go chunk by chunk looking
+  // for the first char of needle, up until we have advanced `N` defined as
+  // `haystack.size() - needle.size()`. If we find the first char of needle at
+  // `P` and `P` is less than `N`, we then call `IsSubstringInCordAt` to
+  // see if this is the needle. If not, we advance to `P + 1` and try again.
+  while (it.chunk_iterator_.bytes_remaining_ >= needle.size()) {
+    auto haystack_chunk = Cord::ChunkRemaining(it);
+    assert(!haystack_chunk.empty());
+    // Look for the first char of `needle` in the current chunk.
+    auto idx = haystack_chunk.find(first_needle);
+    if (idx == absl::string_view::npos) {
+      // No potential match in this chunk, advance past it.
+      Cord::Advance(&it, haystack_chunk.size());
+      continue;
+    }
+    // We found the start of a potential match in the chunk. Advance the
+    // iterator and haystack chunk to the match the position.
+    Cord::Advance(&it, idx);
+    // Check if there is enough haystack remaining to actually have a match.
+    if (it.chunk_iterator_.bytes_remaining_ < needle.size()) {
+      break;
+    }
+    // Check if this is `needle`.
+    if (IsSubstringInCordAt(it, needle)) {
+      return it;
+    }
+    // No match, increment the iterator for the next attempt.
+    Cord::Advance(&it, 1);
+  }
+  // If we got here, we did not find `needle`.
   return char_end();
 }
 
+absl::Cord::CharIterator absl::Cord::Find(absl::string_view needle) const {
+  return FindImpl(char_begin(), needle);
+}
+
+absl::Cord::CharIterator absl::Cord::Find(const absl::Cord& needle) const {
+  return FindImpl(char_begin(), needle);
+}
+
 bool Cord::Contains(absl::string_view rhs) const {
   return rhs.empty() || Find(rhs) != char_end();
 }
diff --git a/absl/strings/cord.h b/absl/strings/cord.h
index ef900f8..8fe8e9f 100644
--- a/absl/strings/cord.h
+++ b/absl/strings/cord.h
@@ -1120,7 +1120,8 @@
   void SetCrcCordState(crc_internal::CrcCordState state);
   const crc_internal::CrcCordState* absl_nullable MaybeGetCrcCordState() const;
 
-  CharIterator FindImpl(CharIterator it, absl::string_view needle) const;
+  template <typename T>
+  CharIterator FindImpl(CharIterator it, const T& needle) const;
 
   void CopyToArrayImpl(char* absl_nonnull dst) const;
 };
diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc
index 1ae1f63..aed23ef 100644
--- a/absl/strings/cord_test.cc
+++ b/absl/strings/cord_test.cc
@@ -588,6 +588,482 @@
       std::next(fragmented_haystack.char_begin(), 5));
 }
 
+TEST_P(CordTest, FindLinearScaling) {
+  // Verifies that finding a mismatching string in an adversarial haystack
+  // scales sub-linearly/linearly via Boyer-Moore-Horspool without quadratic
+  // DoS.  This uses a 1MB haystack and a slightly smaller needle that should
+  // timeout for quadratic behavior.
+  constexpr size_t kSize = 256 * 1024;
+  std::string haystack_str(kSize, 'a');
+  absl::Cord haystack = absl::MakeFragmentedCord(
+      {haystack_str.substr(0, kSize / 2), haystack_str.substr(kSize / 2)});
+
+  std::string needle_str(kSize / 2 - 1, 'a');
+  needle_str.push_back('b');
+  absl::Cord needle_cord =
+      absl::MakeFragmentedCord({needle_str.substr(0, needle_str.size() / 2),
+                                needle_str.substr(needle_str.size() / 2)});
+
+  EXPECT_EQ(haystack.Find(needle_str), haystack.char_end());
+  EXPECT_FALSE(haystack.Contains(needle_str));
+  EXPECT_EQ(haystack.Find(needle_cord), haystack.char_end());
+  EXPECT_FALSE(haystack.Contains(needle_cord));
+}
+
+TEST_P(CordTest, FindBMHCrossChunkMatch) {
+  // Test finding a large needle (M >= 256) in a fragmented Cord haystack where
+  // the match spans across chunk boundaries.
+  const size_t kChunkSize = 512;
+  const size_t kNeedleSize = 300;
+
+  std::string needle_str(kNeedleSize, 'x');
+  for (size_t i = 0; i < kNeedleSize; ++i) {
+    needle_str[i] = static_cast<char>('A' + (i % 26));
+  }
+
+  std::vector<std::string> chunks;
+  chunks.push_back(std::string(kChunkSize, '.'));
+  chunks.push_back(std::string(kChunkSize, '.'));
+  chunks.push_back(std::string(kChunkSize, '.'));
+  absl::Cord haystack = absl::MakeFragmentedCord(chunks);
+
+  std::string flat_haystack(haystack);
+  flat_haystack.replace(400, kNeedleSize, needle_str);
+
+  std::vector<std::string> modified_chunks = {
+      flat_haystack.substr(0, kChunkSize),
+      flat_haystack.substr(kChunkSize, kChunkSize),
+      flat_haystack.substr(2 * kChunkSize, kChunkSize)};
+  haystack = absl::MakeFragmentedCord(modified_chunks);
+
+  // Test string_view needle
+  auto it = haystack.Find(needle_str);
+  EXPECT_NE(it, haystack.char_end());
+  EXPECT_EQ(std::distance(haystack.char_begin(), it), 400);
+  EXPECT_TRUE(haystack.Contains(needle_str));
+
+  // Test Cord needle
+  absl::Cord needle_cord = absl::MakeFragmentedCord(
+      {needle_str.substr(0, 150), needle_str.substr(150)});
+  auto it_cord = haystack.Find(needle_cord);
+  EXPECT_NE(it_cord, haystack.char_end());
+  EXPECT_EQ(std::distance(haystack.char_begin(), it_cord), 400);
+  EXPECT_TRUE(haystack.Contains(needle_cord));
+}
+
+TEST_P(CordTest, FindBMHChunkBoundaryPrefixMismatch) {
+  // Test BMH search when the first character of needle matches near the end of
+  // a chunk (chunk_rem < M), but the rest of the chunk prefix mismatches.
+  // Exercises the `chunk.substr(idx) != needle.substr(0, chunk_rem)` branch in
+  // FindImplBMH and FindCordBMH.
+  const size_t kNeedleSize = 300;
+  std::string needle_str(kNeedleSize, 'A');
+  needle_str[0] = 'Z';
+  needle_str[1] = 'B';
+
+  std::string chunk0(512, '.');
+  chunk0[500] = 'Z';
+  std::string chunk1(512, '.');
+
+  absl::Cord haystack = absl::MakeFragmentedCord({chunk0, chunk1});
+
+  EXPECT_EQ(haystack.Find(needle_str), haystack.char_end());
+  EXPECT_FALSE(haystack.Contains(needle_str));
+
+  absl::Cord needle_cord(needle_str);
+  EXPECT_EQ(haystack.Find(needle_cord), haystack.char_end());
+  EXPECT_FALSE(haystack.Contains(needle_cord));
+}
+
+TEST_P(CordTest, FindBMHCrossChunkLastByteMismatch) {
+  // Test BMH search when the first character and chunk prefix match near the
+  // end of a chunk, but the last byte across the chunk boundary mismatches.
+  // Exercises `last_hay != last_needle` after CharacterAt(it, m - 1) in
+  // FindImplBMH and FindCordBMH.
+  const size_t kNeedleSize = 300;
+  std::string needle_str(kNeedleSize, 'A');
+  needle_str[0] = 'Z';
+  needle_str.back() = 'Y';
+
+  std::string chunk0(512, '.');
+  chunk0.replace(500, 12, 12, 'A');
+  chunk0[500] = 'Z';
+
+  std::string chunk1(512, '.');
+  chunk1[287] = 'W';  // Mismatches needle_str.back() ('Y')
+
+  absl::Cord haystack = absl::MakeFragmentedCord({chunk0, chunk1});
+
+  EXPECT_EQ(haystack.Find(needle_str), haystack.char_end());
+  EXPECT_FALSE(haystack.Contains(needle_str));
+
+  absl::Cord needle_cord(needle_str);
+  EXPECT_EQ(haystack.Find(needle_cord), haystack.char_end());
+  EXPECT_FALSE(haystack.Contains(needle_cord));
+}
+
+TEST_P(CordTest, FindBMHCrossChunkInteriorMismatch) {
+  // Test BMH search when first and last characters match across chunk
+  // boundaries, but an interior character inside the second chunk mismatches.
+  // Exercises IsSubstringInCordAt / IsSubcordInCordAt returning false across
+  // chunks.
+  const size_t kNeedleSize = 300;
+  std::string needle_str(kNeedleSize, 'A');
+  needle_str[0] = 'Z';
+  needle_str.back() = 'Y';
+
+  std::string chunk0(512, '.');
+  chunk0.replace(500, 12, 12, 'A');
+  chunk0[500] = 'Z';
+
+  std::string chunk1(512, 'A');
+  chunk1[287] = 'Y';  // Last char matches
+  chunk1[100] = 'W';  // Interior byte mismatches
+
+  absl::Cord haystack = absl::MakeFragmentedCord({chunk0, chunk1});
+
+  EXPECT_EQ(haystack.Find(needle_str), haystack.char_end());
+  EXPECT_FALSE(haystack.Contains(needle_str));
+
+  // Verify valid match when interior byte is restored
+  chunk1[100] = 'A';
+  haystack = absl::MakeFragmentedCord({chunk0, chunk1});
+  auto it = haystack.Find(needle_str);
+  EXPECT_NE(it, haystack.char_end());
+  EXPECT_EQ(std::distance(haystack.char_begin(), it), 500);
+  EXPECT_TRUE(haystack.Contains(needle_str));
+}
+
+TEST_P(CordTest, FindBMHNearEndRemainingBytesBreak) {
+  // Test BMH search when the first character of needle matches near the end of
+  // haystack, but remaining bytes are less than needle size (bytes_remaining <
+  // M). Exercises the `idx + m > bytes_remaining` break condition in
+  // FindImplBMH and FindCordBMH.
+  const size_t kNeedleSize = 300;
+  std::string needle_str(kNeedleSize, 'A');
+  needle_str[0] = 'Z';
+
+  std::string chunk0(500, '.');
+  std::string chunk1(500, '.');
+  chunk1[300] = 'Z';  // Offset 800 in haystack, remaining bytes = 200 (< 300)
+
+  absl::Cord haystack = absl::MakeFragmentedCord({chunk0, chunk1});
+
+  EXPECT_EQ(haystack.Find(needle_str), haystack.char_end());
+  EXPECT_FALSE(haystack.Contains(needle_str));
+
+  absl::Cord needle_cord = absl::MakeFragmentedCord(
+      {needle_str.substr(0, 150), needle_str.substr(150)});
+  EXPECT_EQ(haystack.Find(needle_cord), haystack.char_end());
+  EXPECT_FALSE(haystack.Contains(needle_cord));
+}
+
+TEST_P(CordTest, FindCordBMHCrossChunkInteriorMismatch) {
+  // Test FindCordBMH when needle is a fragmented Cord (M >= 256), last byte
+  // matches, but IsSubcordInCordAt returns false due to an interior mismatch
+  // across chunks. Exercises lazy shift table initialization after
+  // IsSubcordInCordAt failure in FindCordBMH.
+  const size_t kNeedleSize = 300;
+  std::string needle_str(kNeedleSize, 'A');
+  needle_str[0] = 'Z';
+  needle_str.back() = 'Y';
+
+  std::string chunk0(512, '.');
+  chunk0.replace(500, 12, 12, 'A');
+  chunk0[500] = 'Z';
+
+  std::string chunk1(512, 'A');
+  chunk1[287] = 'Y';  // Last char matches
+  chunk1[150] = 'W';  // Interior mismatch in chunk 1
+
+  absl::Cord haystack = absl::MakeFragmentedCord({chunk0, chunk1});
+  absl::Cord needle_cord = absl::MakeFragmentedCord(
+      {needle_str.substr(0, 150), needle_str.substr(150)});
+
+  EXPECT_EQ(haystack.Find(needle_cord), haystack.char_end());
+  EXPECT_FALSE(haystack.Contains(needle_cord));
+
+  // Restore interior byte and verify match
+  chunk1[150] = 'A';
+  haystack = absl::MakeFragmentedCord({chunk0, chunk1});
+  auto it = haystack.Find(needle_cord);
+  EXPECT_NE(it, haystack.char_end());
+  EXPECT_EQ(std::distance(haystack.char_begin(), it), 500);
+  EXPECT_TRUE(haystack.Contains(needle_cord));
+}
+
+TEST_P(CordTest, FindBMHNonAsciiBytes) {
+  // Test BMH search with high-bit non-ASCII bytes (values > 127) in Cord
+  // haystack and needle across chunk boundaries.
+  const size_t kNeedleSize = 300;
+  std::string needle_str(kNeedleSize, '\0');
+  for (size_t i = 0; i < kNeedleSize; ++i) {
+    needle_str[i] = static_cast<char>(128 + (i % 128));
+  }
+
+  std::string chunk0(512, '\x7F');
+  std::string chunk1(512, '\x7F');
+
+  // Place needle straddling chunk 0 and chunk 1 at offset 400
+  std::string flat_haystack = chunk0 + chunk1;
+  flat_haystack.replace(400, kNeedleSize, needle_str);
+
+  absl::Cord haystack = absl::MakeFragmentedCord(
+      {flat_haystack.substr(0, 512), flat_haystack.substr(512)});
+
+  auto it = haystack.Find(needle_str);
+  EXPECT_NE(it, haystack.char_end());
+  EXPECT_EQ(std::distance(haystack.char_begin(), it), 400);
+  EXPECT_TRUE(haystack.Contains(needle_str));
+
+  absl::Cord needle_cord = absl::MakeFragmentedCord(
+      {needle_str.substr(0, 150), needle_str.substr(150)});
+  auto it_cord = haystack.Find(needle_cord);
+  EXPECT_NE(it_cord, haystack.char_end());
+  EXPECT_EQ(std::distance(haystack.char_begin(), it_cord), 400);
+  EXPECT_TRUE(haystack.Contains(needle_cord));
+}
+
+TEST_P(CordTest, FindSingleCharNeedle) {
+  // Test single-character string_view and Cord needles (M = 1) in fragmented
+  // Cord, exercising the FindChar fast path.
+  absl::Cord haystack = absl::MakeFragmentedCord({"hello", " ", "world"});
+
+  // string_view needle
+  auto it_sv = haystack.Find("w");
+  EXPECT_NE(it_sv, haystack.char_end());
+  EXPECT_EQ(std::distance(haystack.char_begin(), it_sv), 6);
+  EXPECT_TRUE(haystack.Contains("w"));
+
+  EXPECT_EQ(haystack.Find("z"), haystack.char_end());
+  EXPECT_FALSE(haystack.Contains("z"));
+
+  // Cord needle
+  auto it_cord = haystack.Find(absl::Cord("w"));
+  EXPECT_NE(it_cord, haystack.char_end());
+  EXPECT_EQ(std::distance(haystack.char_begin(), it_cord), 6);
+  EXPECT_TRUE(haystack.Contains(absl::Cord("w")));
+
+  EXPECT_EQ(haystack.Find(absl::Cord("z")), haystack.char_end());
+  EXPECT_FALSE(haystack.Contains(absl::Cord("z")));
+}
+
+TEST_P(CordTest, FindStringViewNeedleSizeLargerThanHaystack) {
+  absl::Cord haystack("hello");
+  EXPECT_EQ(haystack.Find("hello world"), haystack.char_end());
+  EXPECT_FALSE(haystack.Contains("hello world"));
+}
+
+TEST_P(CordTest, FindStringViewNeedleSizeEqualToHaystack) {
+  absl::Cord haystack("hello world");
+  EXPECT_EQ(haystack.Find("hello world"), haystack.char_begin());
+  EXPECT_TRUE(haystack.Contains("hello world"));
+
+  EXPECT_EQ(haystack.Find("hello WOrld"), haystack.char_end());
+  EXPECT_FALSE(haystack.Contains("hello WOrld"));
+}
+
+TEST_P(CordTest, FindCordNeedleSizeEqualToHaystack) {
+  absl::Cord haystack("hello world");
+  absl::Cord needle_match("hello world");
+  absl::Cord needle_mismatch("hello WOrld");
+
+  EXPECT_EQ(haystack.Find(needle_match), haystack.char_begin());
+  EXPECT_TRUE(haystack.Contains(needle_match));
+
+  EXPECT_EQ(haystack.Find(needle_mismatch), haystack.char_end());
+  EXPECT_FALSE(haystack.Contains(needle_mismatch));
+}
+
+TEST_P(CordTest, FindImplNearEndRemainingBytesBreak) {
+  std::string haystack_str(50, '.');
+  haystack_str[47] = 'a';
+  absl::Cord haystack = absl::MakeFragmentedCord(
+      {haystack_str.substr(0, 25), haystack_str.substr(25)});
+
+  EXPECT_EQ(haystack.Find("abcde"), haystack.char_end());
+  EXPECT_FALSE(haystack.Contains("abcde"));
+}
+
+TEST_P(CordTest, FindCordNearEndRemainingBytesBreak) {
+  std::string haystack_str(50, '.');
+  haystack_str[47] = 'a';
+  absl::Cord haystack = absl::MakeFragmentedCord(
+      {haystack_str.substr(0, 25), haystack_str.substr(25)});
+  absl::Cord needle_cord("abcde");
+
+  EXPECT_EQ(haystack.Find(needle_cord), haystack.char_end());
+  EXPECT_FALSE(haystack.Contains(needle_cord));
+}
+
+TEST_P(CordTest, FindImplBMHSingleChunkInteriorMismatch) {
+  const size_t kNeedleSize = 300;
+  std::string needle_str(kNeedleSize, 'A');
+  needle_str[0] = 'Z';
+  needle_str.back() = 'Y';
+
+  std::string haystack_str(1000, 'A');
+  haystack_str.replace(100, kNeedleSize, needle_str);
+  haystack_str[100 + 10] = 'W';
+
+  absl::Cord haystack(haystack_str);
+  EXPECT_EQ(haystack.Find(needle_str), haystack.char_end());
+  EXPECT_FALSE(haystack.Contains(needle_str));
+}
+
+TEST_P(CordTest, FindCordBMHSingleChunkInteriorMismatch) {
+  const size_t kNeedleSize = 300;
+  std::string needle_str(kNeedleSize, 'A');
+  needle_str[0] = 'Z';
+  needle_str.back() = 'Y';
+
+  std::string haystack_str(1000, 'A');
+  haystack_str.replace(100, kNeedleSize, needle_str);
+  haystack_str[100 + 10] = 'W';
+
+  absl::Cord haystack(haystack_str);
+  absl::Cord needle_cord(needle_str);
+
+  EXPECT_EQ(haystack.Find(needle_cord), haystack.char_end());
+  EXPECT_FALSE(haystack.Contains(needle_cord));
+}
+
+TEST_P(CordTest, FindBMHDuplicateCharsInNeedle) {
+  // Test BMH shift table calculation when needle contains duplicate characters
+  // at different distances from the end. Verifies that the smallest shift
+  // distance (rightmost occurrence) is preserved and prevents over-shifting.
+  const size_t kNeedleSize = 300;
+  std::string needle_str(kNeedleSize, 'A');
+  needle_str[0] = 'Z';
+  needle_str[kNeedleSize - 50] = 'Q';
+  needle_str[kNeedleSize - 3] = 'Q';
+  needle_str.back() = 'Y';
+
+  std::string haystack_str(1000, '.');
+  haystack_str.replace(202, kNeedleSize, needle_str);
+  haystack_str[200] = 'Z';
+  haystack_str[499] = 'Q';
+
+  absl::Cord haystack = absl::MakeFragmentedCord(
+      {haystack_str.substr(0, 500), haystack_str.substr(500)});
+
+  auto it = haystack.Find(needle_str);
+  EXPECT_NE(it, haystack.char_end());
+  EXPECT_EQ(std::distance(haystack.char_begin(), it), 202);
+  EXPECT_TRUE(haystack.Contains(needle_str));
+
+  absl::Cord needle_cord = absl::MakeFragmentedCord(
+      {needle_str.substr(0, 150), needle_str.substr(150)});
+  auto it_cord = haystack.Find(needle_cord);
+  EXPECT_NE(it_cord, haystack.char_end());
+  EXPECT_EQ(std::distance(haystack.char_begin(), it_cord), 202);
+  EXPECT_TRUE(haystack.Contains(needle_cord));
+}
+
+TEST_P(CordTest, FindBMHBoundaryThreshold256) {
+  // Test BMH search at the exact boundary size of 256 bytes for string_view
+  // and Cord needles. Verifies correct shift table boundary bounds.
+  const size_t kNeedleSize = 256;
+  std::string needle_str(kNeedleSize, 'B');
+  needle_str[0] = 'X';
+  needle_str[kNeedleSize - 2] = 'Y';
+  needle_str.back() = 'Z';
+
+  std::string haystack_str(1000, '.');
+  haystack_str.replace(500, kNeedleSize, needle_str);
+
+  absl::Cord haystack = absl::MakeFragmentedCord(
+      {haystack_str.substr(0, 400), haystack_str.substr(400)});
+
+  auto it = haystack.Find(needle_str);
+  EXPECT_NE(it, haystack.char_end());
+  EXPECT_EQ(std::distance(haystack.char_begin(), it), 500);
+  EXPECT_TRUE(haystack.Contains(needle_str));
+
+  absl::Cord needle_cord = absl::MakeFragmentedCord(
+      {needle_str.substr(0, 100), needle_str.substr(100)});
+  auto it_cord = haystack.Find(needle_cord);
+  EXPECT_NE(it_cord, haystack.char_end());
+  EXPECT_EQ(std::distance(haystack.char_begin(), it_cord), 500);
+  EXPECT_TRUE(haystack.Contains(needle_cord));
+}
+
+TEST_P(CordTest, FindBMHMatchAtVeryEndOfCord) {
+  // Test BMH search when the needle is located at the very end of haystack
+  // (idx + m == bytes_remaining_). Verifies that boundary check uses > instead
+  // of >=.
+  const size_t kNeedleSize = 300;
+  std::string needle_str(kNeedleSize, 'M');
+  needle_str[0] = 'S';
+  needle_str.back() = 'E';
+
+  std::string haystack_str = std::string(500, '.') + needle_str;
+  absl::Cord haystack = absl::MakeFragmentedCord(
+      {haystack_str.substr(0, 400), haystack_str.substr(400)});
+
+  auto it = haystack.Find(needle_str);
+  EXPECT_NE(it, haystack.char_end());
+  EXPECT_EQ(std::distance(haystack.char_begin(), it), 500);
+  EXPECT_TRUE(haystack.Contains(needle_str));
+
+  absl::Cord needle_cord = absl::MakeFragmentedCord(
+      {needle_str.substr(0, 150), needle_str.substr(150)});
+  auto it_cord = haystack.Find(needle_cord);
+  EXPECT_NE(it_cord, haystack.char_end());
+  EXPECT_EQ(std::distance(haystack.char_begin(), it_cord), 500);
+  EXPECT_TRUE(haystack.Contains(needle_cord));
+}
+
+TEST_P(CordTest, FindBMHMatchInLaterChunkAfterEmptyChunk) {
+  // Test BMH search when the first chunk of a fragmented Cord haystack does NOT
+  // contain needle[0]. Verifies that idx == npos advances past the entire first
+  // chunk and continues searching subsequent chunks.
+  const size_t kNeedleSize = 300;
+  std::string needle_str(kNeedleSize, 'A');
+  needle_str[0] = 'Z';
+
+  std::string chunk0(512, '.');
+  std::string chunk1 = needle_str + std::string(212, '.');
+
+  absl::Cord haystack = absl::MakeFragmentedCord({chunk0, chunk1});
+
+  auto it = haystack.Find(needle_str);
+  EXPECT_NE(it, haystack.char_end());
+  EXPECT_EQ(std::distance(haystack.char_begin(), it), 512);
+  EXPECT_TRUE(haystack.Contains(needle_str));
+
+  absl::Cord needle_cord = absl::MakeFragmentedCord(
+      {needle_str.substr(0, 150), needle_str.substr(150)});
+  auto it_cord = haystack.Find(needle_cord);
+  EXPECT_NE(it_cord, haystack.char_end());
+  EXPECT_EQ(std::distance(haystack.char_begin(), it_cord), 512);
+  EXPECT_TRUE(haystack.Contains(needle_cord));
+}
+
+TEST_P(CordTest, FindShortNeedleConsecutiveCandidates) {
+  // Test short needle (2 <= M < 256) when consecutive positions in haystack
+  // start with needle.front(), where position 10 mismatches but position 11
+  // matches. Verifies that failing IsSubstringInCordAt advances by exactly 1.
+  std::string needle = "ABZ";
+  std::string haystack_str(100, '.');
+  haystack_str.replace(10, 3, "ABW");
+  haystack_str.replace(11, 3, "ABZ");
+
+  absl::Cord haystack = absl::MakeFragmentedCord(
+      {haystack_str.substr(0, 50), haystack_str.substr(50)});
+
+  auto it = haystack.Find(needle);
+  EXPECT_NE(it, haystack.char_end());
+  EXPECT_EQ(std::distance(haystack.char_begin(), it), 11);
+  EXPECT_TRUE(haystack.Contains(needle));
+
+  absl::Cord needle_cord(needle);
+  auto it_cord = haystack.Find(needle_cord);
+  EXPECT_NE(it_cord, haystack.char_end());
+  EXPECT_EQ(std::distance(haystack.char_begin(), it_cord), 11);
+  EXPECT_TRUE(haystack.Contains(needle_cord));
+}
+
 TEST_P(CordTest, Subcord) {
   RandomEngine rng(GTEST_FLAG_GET(random_seed));
   const std::string s = RandomLowercaseString(&rng, 1024);
diff --git a/absl/strings/internal/generic_printer_test.cc b/absl/strings/internal/generic_printer_test.cc
index e7ecc09..47a11db 100644
--- a/absl/strings/internal/generic_printer_test.cc
+++ b/absl/strings/internal/generic_printer_test.cc
@@ -330,9 +330,9 @@
 TEST(GenericPrinterTest, StreamAdapter) {
   std::stringstream ss;
   static_assert(
-      std::is_same<std::remove_reference_t<decltype(ss << GenericPrint())>,
-                   internal_generic_printer::GenericPrintStreamAdapter::Impl<
-                       std::stringstream>>::value,
+      std::is_same_v<std::remove_reference_t<decltype(ss << GenericPrint())>,
+                     internal_generic_printer::GenericPrintStreamAdapter::Impl<
+                         std::stringstream>>,
       "expected ostream << GenericPrint() to yield adapter impl");
 
   ss << GenericPrint() << "again, " << "back-up, " << "cue, "
diff --git a/absl/strings/internal/str_format/bind.h b/absl/strings/internal/str_format/bind.h
index 1c87e1e..b5a312e 100644
--- a/absl/strings/internal/str_format/bind.h
+++ b/absl/strings/internal/str_format/bind.h
@@ -22,6 +22,7 @@
 #include <utility>
 
 #include "absl/base/config.h"
+#include "absl/base/macros.h"
 #include "absl/container/inlined_vector.h"
 #include "absl/strings/internal/str_format/arg.h"
 #include "absl/strings/internal/str_format/checker.h"
@@ -127,23 +128,17 @@
 #ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
 
   // Honeypot overload for when the string is not constexpr.
-  // We use the 'unavailable' attribute to give a better compiler error than
-  // just 'method is deleted'.
   FormatSpecTemplate(...)  // NOLINT
-      __attribute__((unavailable("Format string is not constexpr.")));
+      ABSL_BAD_CALL_IF(true, "Format string is not constexpr.");
 
   // Honeypot overload for when the format is constexpr and invalid.
-  // We use the 'unavailable' attribute to give a better compiler error than
-  // just 'method is deleted'.
   // To avoid checking the format twice, we just check that the format is
   // constexpr. If it is valid, then the overload below will kick in.
   // We add the template here to make this overload have lower priority.
   template <typename = void>
   FormatSpecTemplate(const char* s)  // NOLINT
-      __attribute__((
-          enable_if(str_format_internal::EnsureConstexpr(s), "constexpr trap"),
-          unavailable(
-              "Format specified does not match the arguments passed.")));
+      ABSL_BAD_CALL_IF(str_format_internal::EnsureConstexpr(s),
+                       "Format specified does not match the arguments passed.");
 
   template <typename T = void>
   FormatSpecTemplate(string_view s)  // NOLINT
diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc
index fc7d9ba..16fcaa9 100644
--- a/absl/strings/internal/str_format/convert_test.cc
+++ b/absl/strings/internal/str_format/convert_test.cc
@@ -213,7 +213,7 @@
   delete[] buf;
 }
 
-void StrAppend(std::string *, const char *, ...) ABSL_PRINTF_ATTRIBUTE(2, 3);
+ABSL_PRINTF_ATTRIBUTE(2, 3) void StrAppend(std::string*, const char*, ...);
 void StrAppend(std::string *out, const char *format, ...) {
   va_list ap;
   va_start(ap, format);
@@ -221,7 +221,7 @@
   va_end(ap);
 }
 
-std::string StrPrint(const char *, ...) ABSL_PRINTF_ATTRIBUTE(1, 2);
+ABSL_PRINTF_ATTRIBUTE(1, 2) std::string StrPrint(const char*, ...);
 std::string StrPrint(const char *format, ...) {
   va_list ap;
   va_start(ap, format);
diff --git a/absl/strings/str_split.h b/absl/strings/str_split.h
index e86b29a..dc32af1 100644
--- a/absl/strings/str_split.h
+++ b/absl/strings/str_split.h
@@ -96,7 +96,7 @@
 // function:
 //
 //   struct SimpleDelimiter {
-//     const char c_;
+//     char c_;
 //     explicit SimpleDelimiter(char c) : c_(c) {}
 //     absl::string_view Find(absl::string_view text, size_t pos) {
 //       auto found = text.find(c_, pos);
@@ -206,7 +206,7 @@
   absl::string_view Find(absl::string_view text, size_t pos) const;
 
  private:
-  const std::string delimiters_;
+  std::string delimiters_;
 };
 
 // ByLength
@@ -237,7 +237,7 @@
   absl::string_view Find(absl::string_view text, size_t pos) const;
 
  private:
-  const ptrdiff_t length_;
+  ptrdiff_t length_;
 };
 
 namespace strings_internal {
@@ -291,7 +291,7 @@
 
  private:
   Delimiter delimiter_;
-  const int limit_;
+  int limit_;
   int count_;
 };
 
diff --git a/absl/strings/str_split_test.cc b/absl/strings/str_split_test.cc
index 8155311..7487e00 100644
--- a/absl/strings/str_split_test.cc
+++ b/absl/strings/str_split_test.cc
@@ -14,6 +14,7 @@
 
 #include "absl/strings/str_split.h"
 
+#include <algorithm>
 #include <array>
 #include <cstddef>
 #include <cstdint>
@@ -32,6 +33,7 @@
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "absl/algorithm/container.h"
 #include "absl/base/macros.h"
 #include "absl/container/btree_map.h"
 #include "absl/container/btree_set.h"
@@ -1036,6 +1038,36 @@
   EXPECT_FALSE(IsFoundAt("abcd", four_char_delim, 0));
 }
 
+TEST(Split, ByAnyCharWorksWithRepeatedFind) {
+  auto splitter = absl::StrSplit("a,b;a", absl::ByAnyChar(",;"));
+  auto it = absl::c_find(splitter, "a");
+  ASSERT_NE(it, splitter.end());
+  // Search on from the next piece, which assigns the iterator.
+  it = std::find(std::next(it), splitter.end(), "a");
+  ASSERT_NE(it, splitter.end());
+  EXPECT_EQ(std::next(it), splitter.end());
+}
+
+TEST(Split, ByLengthWorksWithRepeatedFind) {
+  auto splitter = absl::StrSplit("aba", absl::ByLength(1));
+  auto it = absl::c_find(splitter, "a");
+  ASSERT_NE(it, splitter.end());
+  // Search on from the next piece, which assigns the iterator.
+  it = std::find(std::next(it), splitter.end(), "a");
+  ASSERT_NE(it, splitter.end());
+  EXPECT_EQ(std::next(it), splitter.end());
+}
+
+TEST(Split, MaxSplitsWorksWithRepeatedFind) {
+  auto splitter = absl::StrSplit("a,a,b,c", absl::MaxSplits(',', 2));
+  auto it = absl::c_find(splitter, "a");
+  ASSERT_NE(it, splitter.end());
+  // Search on from the next piece, which assigns the iterator.
+  it = std::find(std::next(it), splitter.end(), "a");
+  ASSERT_NE(it, splitter.end());
+  EXPECT_EQ(*std::next(it), "b,c");
+}
+
 TEST(Split, WorksWithLargeStrings) {
 #if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
     defined(ABSL_HAVE_MEMORY_SANITIZER) || defined(ABSL_HAVE_THREAD_SANITIZER)
diff --git a/absl/synchronization/internal/graphcycles.cc b/absl/synchronization/internal/graphcycles.cc
index 25b51ff..7079081 100644
--- a/absl/synchronization/internal/graphcycles.cc
+++ b/absl/synchronization/internal/graphcycles.cc
@@ -27,6 +27,9 @@
 //     implies rank[x] < rank[y].
 // (2) When a new edge (x->y) is inserted, do nothing if rank[x] < rank[y].
 // (3) Otherwise: adjust ranks in the neighborhood of x and y.
+//
+// Unlike in the paper, ranks are spaced apart, which often lets step (3) move
+// just x or just y (see "Rank allocation" below).
 
 #include "absl/base/internal/low_level_alloc.h"  // IWYU pragma: keep
 // This file is a no-op if the required LowLevelAlloc support is missing.
@@ -282,7 +285,9 @@
 }
 
 struct Node {
-  int32_t rank;          // rank number assigned by Pearce-Kelly algorithm
+  int64_t rank;          // Unique rank (see "Rank allocation")
+  int32_t prev_slot;     // Slot with the next lower rank, or -1 if none
+  int32_t next_slot;     // Slot with the next higher rank, or -1 if none
   uint32_t version;      // Current version number
   int32_t next_hash;     // Next entry in hash table
   bool visited;          // Temporary marker used by depth-first-search
@@ -348,17 +353,53 @@
 
 }  // namespace
 
+// Rank allocation
+//
+// Ranks are unique int64_t values. All slots, including free slots, are kept in
+// a doubly linked list sorted by rank (Node::prev_slot and Node::next_slot,
+// from Rep::lowest_slot to Rep::highest_slot). Free slots keep their rank and
+// their place in the list, which are reused along with the slot. New slots get
+// ranks kRankStride above the highest rank.
+//
+// When a new edge x->y violates the rank order, InsertEdge first tries to move
+// just x below y, or just y above x (see TryMoveXBelowY()): either to the
+// midpoint between two ranks that are adjacent in the list, or kRankStride
+// beyond the lowest or highest rank. No other slot can have such a rank, so
+// ranks stay unique.
+//
+// kRankStride leaves room for about 32 moves, one after another, into the gap
+// between two adjacent new slots. When a gap has no room left, Respace() first
+// spreads out the ranks around it. Ranks beyond the lowest and highest ones
+// stay within [kMinRank, kMaxRank], which leaves room for about 2^30 moves at
+// each end before all slots are respaced.
+static constexpr int64_t kRankStride = int64_t{1} << 32;
+static constexpr int64_t kMinRank = -(int64_t{1} << 62);
+static constexpr int64_t kMaxRank = int64_t{1} << 62;
+// Respace() spreads out ranks until they can be at least this far apart.
+static constexpr uint64_t kMinRespacedGap = uint64_t{1} << 16;
+// Maximum number of in-neighbors of x (or out-neighbors of y) that
+// TryMoveXBelowY() (or TryMoveYAboveX()) will examine.
+static constexpr int kMaxSingleNodeDegree = 16;
+
 struct GraphCycles::Rep {
   Vec<Node*> nodes_;
   Vec<int32_t> free_nodes_;  // Indices for unused entries in nodes_
   PointerMap ptrmap_;
+  int32_t lowest_slot = -1;   // Slot with the lowest rank, or -1 if none
+  int32_t highest_slot = -1;  // Slot with the highest rank, or -1 if none
+  int64_t rank_stride = kRankStride;  // See TestOnlySetRankStride()
 
   // Temporary state.
   Vec<int32_t> deltaf_;  // Results of forward DFS
   Vec<int32_t> deltab_;  // Results of backward DFS
   Vec<int32_t> list_;    // All nodes to reprocess
-  Vec<int32_t> merged_;  // Rank values to assign to list_ entries
   Vec<int32_t> stack_;   // Emulates recursion stack for depth-first searches
+  // Used by Reorder(): the slots to reorder, sorted by their current ranks, and
+  // the rank, prev_slot and next_slot of each.
+  Vec<int32_t> sorted_slots;
+  Vec<int64_t> sorted_ranks;
+  Vec<int32_t> old_prev_slots;
+  Vec<int32_t> old_next_slots;
 
   Rep() : ptrmap_(&nodes_) {}
 };
@@ -368,6 +409,119 @@
   return (n->version == NodeVersion(id)) ? n : nullptr;
 }
 
+static Node* NodeAt(GraphCycles::Rep* r, int32_t slot) {
+  return r->nodes_[static_cast<uint32_t>(slot)];
+}
+
+// Makes next_slot follow prev_slot in the rank list. prev_slot == -1 makes
+// next_slot the lowest slot, and next_slot == -1 makes prev_slot the highest.
+static void Link(GraphCycles::Rep* r, int32_t prev_slot, int32_t next_slot) {
+  if (prev_slot == -1) {
+    r->lowest_slot = next_slot;
+  } else {
+    NodeAt(r, prev_slot)->next_slot = next_slot;
+  }
+  if (next_slot == -1) {
+    r->highest_slot = prev_slot;
+  } else {
+    NodeAt(r, next_slot)->prev_slot = prev_slot;
+  }
+}
+
+// Removes slot from the rank list.
+static void Unlink(GraphCycles::Rep* r, int32_t slot) {
+  Link(r, NodeAt(r, slot)->prev_slot, NodeAt(r, slot)->next_slot);
+}
+
+// Returns upper - lower, for lower <= upper, without overflow.
+static uint64_t RankDistance(int64_t lower, int64_t upper) {
+  return static_cast<uint64_t>(upper) - static_cast<uint64_t>(lower);
+}
+
+// Gives the run of adjacent slots from first_slot to last_slot in the rank list
+// evenly spaced ranks, keeping their order. First grows the run on both sides
+// until its slots can be at least kMinRespacedGap apart between the ranks just
+// outside it (kMinRank and kMaxRank beyond the ends of the list), or until it
+// covers the whole list. The whole list is spaced across
+// [kMinRank / 2, kMaxRank / 2], which leaves room beyond both of its ends.
+static void Respace(GraphCycles::Rep* r, int32_t first_slot,
+                    int32_t last_slot) {
+  uint64_t count = 1;
+  for (int32_t slot = first_slot; slot != last_slot;
+       slot = NodeAt(r, slot)->next_slot) {
+    count++;
+  }
+  int64_t lower;
+  int64_t upper;
+  while (true) {
+    const int32_t before_slot = NodeAt(r, first_slot)->prev_slot;
+    const int32_t after_slot = NodeAt(r, last_slot)->next_slot;
+    if (before_slot == -1 && after_slot == -1) {
+      lower = kMinRank / 2;
+      upper = kMaxRank / 2;
+      break;
+    }
+    lower = before_slot == -1 ? kMinRank : NodeAt(r, before_slot)->rank;
+    upper = after_slot == -1 ? kMaxRank : NodeAt(r, after_slot)->rank;
+    if (RankDistance(lower, upper) / (count + 1) >= kMinRespacedGap) break;
+    // Double the size of the run, as far as the ends of the list allow.
+    for (const uint64_t target = 2 * count; count < target;) {
+      const int32_t prev_slot = NodeAt(r, first_slot)->prev_slot;
+      const int32_t next_slot = NodeAt(r, last_slot)->next_slot;
+      if (prev_slot == -1 && next_slot == -1) break;
+      if (prev_slot != -1) {
+        first_slot = prev_slot;
+        count++;
+      }
+      if (next_slot != -1) {
+        last_slot = next_slot;
+        count++;
+      }
+    }
+  }
+  const uint64_t step = RankDistance(lower, upper) / (count + 1);
+  int64_t rank = lower;
+  for (int32_t slot = first_slot;; slot = NodeAt(r, slot)->next_slot) {
+    rank += static_cast<int64_t>(step);
+    NodeAt(r, slot)->rank = rank;
+    if (slot == last_slot) return;
+  }
+}
+
+// Links slot, which is not in the rank list, between prev_slot and next_slot,
+// which are adjacent in it (-1 beyond its ends), with a rank between theirs:
+// rank_stride (kRankStride by default) beyond prev_slot or next_slot at the
+// ends of the list, and halfway between them otherwise. If there is no room
+// for such a rank, respaces ranks first.
+static void LinkBetween(GraphCycles::Rep* r, int32_t slot, int32_t prev_slot,
+                        int32_t next_slot) {
+  int64_t rank;
+  if (prev_slot == -1 && next_slot == -1) {
+    rank = 0;
+  } else if (prev_slot == -1) {
+    if (NodeAt(r, next_slot)->rank < kMinRank + r->rank_stride) {
+      Respace(r, r->lowest_slot, r->highest_slot);
+    }
+    rank = NodeAt(r, next_slot)->rank - r->rank_stride;
+  } else if (next_slot == -1) {
+    if (NodeAt(r, prev_slot)->rank > kMaxRank - r->rank_stride) {
+      Respace(r, r->lowest_slot, r->highest_slot);
+    }
+    rank = NodeAt(r, prev_slot)->rank + r->rank_stride;
+  } else {
+    const Node* prev_node = NodeAt(r, prev_slot);
+    const Node* next_node = NodeAt(r, next_slot);
+    if (RankDistance(prev_node->rank, next_node->rank) < 2) {
+      Respace(r, prev_slot, next_slot);
+    }
+    const uint64_t gap = RankDistance(prev_node->rank, next_node->rank);
+    rank = prev_node->rank + static_cast<int64_t>(gap / 2);
+  }
+  NodeAt(r, slot)->rank = rank;
+  Link(r, prev_slot, slot);
+  Link(r, slot, next_slot);
+}
+
 void GraphCycles::TestOnlyAddNodes(uint32_t n) {
   uint32_t old_size = rep_->nodes_.size();
   rep_->nodes_.resize(n);
@@ -376,6 +530,13 @@
   }
 }
 
+void GraphCycles::TestOnlySetRankStride(int64_t stride) {
+  // After Respace() of the whole list, there is room for a stride of 2^61
+  // beyond each end.
+  ABSL_RAW_CHECK(stride >= 1 && stride <= kMaxRank / 2, "Bad rank stride");
+  rep_->rank_stride = stride;
+}
+
 GraphCycles::GraphCycles() {
   InitArenaIfNecessary();
   rep_ = new (base_internal::LowLevelAlloc::AllocWithArena(sizeof(Rep), arena))
@@ -396,7 +557,6 @@
 
 bool GraphCycles::CheckInvariants() const {
   Rep* r = rep_;
-  NodeSet ranks;  // Set of ranks seen so far.
   for (uint32_t x = 0; x < r->nodes_.size(); x++) {
     Node* nx = r->nodes_[x];
     void* ptr = base_internal::UnhidePtr<void>(nx->masked_ptr);
@@ -407,19 +567,44 @@
     if (nx->visited) {
       ABSL_RAW_LOG(FATAL, "Did not clear visited marker on node %" PRIu32, x);
     }
-    if (!ranks.insert(nx->rank)) {
-      ABSL_RAW_LOG(FATAL, "Duplicate occurrence of rank %" PRId32, nx->rank);
-    }
     HASH_FOR_EACH(y, nx->out) {
       Node* ny = r->nodes_[static_cast<uint32_t>(y)];
       if (nx->rank >= ny->rank) {
         ABSL_RAW_LOG(FATAL,
                      "Edge %" PRIu32 " ->%" PRId32
-                     " has bad rank assignment %" PRId32 "->%" PRId32,
+                     " has bad rank assignment %" PRId64 "->%" PRId64,
                      x, y, nx->rank, ny->rank);
       }
     }
   }
+  // The rank list holds each slot once, in increasing rank order, so ranks are
+  // unique.
+  const uint32_t num_slots = r->nodes_.size();
+  uint32_t num_listed = 0;
+  int32_t prev_slot = -1;
+  for (int32_t slot = r->lowest_slot; slot != -1;
+       slot = NodeAt(r, slot)->next_slot) {
+    if (NodeAt(r, slot)->prev_slot != prev_slot) {
+      ABSL_RAW_LOG(FATAL,
+                   "Slot %" PRId32 " has prev_slot %" PRId32
+                   " instead of %" PRId32,
+                   slot, NodeAt(r, slot)->prev_slot, prev_slot);
+    }
+    if (prev_slot != -1 &&
+        NodeAt(r, prev_slot)->rank >= NodeAt(r, slot)->rank) {
+      ABSL_RAW_LOG(
+          FATAL, "Rank of slot %" PRId32 " is not above rank of slot %" PRId32,
+          slot, prev_slot);
+    }
+    if (++num_listed > num_slots) {
+      ABSL_RAW_LOG(FATAL, "Rank list has more entries than there are slots");
+    }
+    prev_slot = slot;
+  }
+  if (prev_slot != r->highest_slot || num_listed != num_slots) {
+    ABSL_RAW_LOG(FATAL, "Rank list has %" PRIu32 " of %" PRIu32 " slots",
+                 num_listed, num_slots);
+  }
   return true;
 }
 
@@ -428,29 +613,29 @@
   if (i != -1) {
     return MakeId(i, rep_->nodes_[static_cast<uint32_t>(i)]->version);
   } else if (rep_->free_nodes_.empty()) {
+    const int32_t slot = static_cast<int32_t>(rep_->nodes_.size());
     Node* n =
         new (base_internal::LowLevelAlloc::AllocWithArena(sizeof(Node), arena))
             Node;
     n->version = 1;  // Avoid 0 since it is used by InvalidGraphId()
     n->visited = false;
-    n->rank = static_cast<int32_t>(rep_->nodes_.size());
     n->masked_ptr = base_internal::HidePtr(ptr);
     n->nstack = 0;
     n->priority = 0;
     rep_->nodes_.push_back(n);
-    rep_->ptrmap_.Add(ptr, n->rank);
-    return MakeId(n->rank, n->version);
+    LinkBetween(rep_, slot, rep_->highest_slot, /*next_slot=*/-1);
+    rep_->ptrmap_.Add(ptr, slot);
+    return MakeId(slot, n->version);
   } else {
-    // Preserve preceding rank since the set of ranks in use must be
-    // a permutation of [0,rep_->nodes_.size()-1].
-    int32_t r = rep_->free_nodes_.back();
+    // The slot keeps its rank (see "Rank allocation").
+    int32_t slot = rep_->free_nodes_.back();
     rep_->free_nodes_.pop_back();
-    Node* n = rep_->nodes_[static_cast<uint32_t>(r)];
+    Node* n = rep_->nodes_[static_cast<uint32_t>(slot)];
     n->masked_ptr = base_internal::HidePtr(ptr);
     n->nstack = 0;
     n->priority = 0;
-    rep_->ptrmap_.Add(ptr, r);
-    return MakeId(r, n->version);
+    rep_->ptrmap_.Add(ptr, slot);
+    return MakeId(slot, n->version);
   }
 }
 
@@ -502,13 +687,65 @@
   }
 }
 
-static bool ForwardDFS(GraphCycles::Rep* r, int32_t n, int32_t upper_bound);
-static void BackwardDFS(GraphCycles::Rep* r, int32_t n, int32_t lower_bound);
+static bool ForwardDFS(GraphCycles::Rep* r, int32_t n, int64_t upper_bound);
+static void BackwardDFS(GraphCycles::Rep* r, int32_t n, int64_t lower_bound);
 static void Reorder(GraphCycles::Rep* r);
 static void Sort(const Vec<Node*>&, Vec<int32_t>* delta);
 static void MoveToList(GraphCycles::Rep* r, Vec<int32_t>* src,
                        Vec<int32_t>* dst);
 
+// TryMoveXBelowY() and TryMoveYAboveX() are called by InsertEdge(x, y) when
+// rank(x) > rank(y). They try to restore the rank order by moving only x below
+// y, or only y above x, which is much cheaper than the Pearce-Kelly search and
+// reorder.
+//
+// x can move below y if all of x's in-neighbors rank below y. This also shows
+// that the new edge does not close a cycle: every node reachable from y ranks
+// at or above y, so a path from y to x would have to enter x from such a node.
+// Similarly, y can move above x if all of y's out-neighbors rank above x.
+//
+// Each returns false, changing nothing, if the move is not possible or if
+// there are more than kMaxSingleNodeDegree neighbors to check.
+static bool TryMoveXBelowY(GraphCycles::Rep* r, int32_t x, int32_t y) {
+  Node* nx = NodeAt(r, x);
+  Node* ny = NodeAt(r, y);
+  int degree = 0;
+  HASH_FOR_EACH(u, nx->in) {
+    if (++degree > kMaxSingleNodeDegree) return false;
+    if (NodeAt(r, u)->rank >= ny->rank) return false;
+  }
+  Unlink(r, x);
+  if (degree == 0) {
+    // Move x below all other nodes.
+    LinkBetween(r, x, /*prev_slot=*/-1, r->lowest_slot);
+  } else {
+    // Move x right below y. Its in-neighbors rank below y, so they rank no
+    // higher than the node right below y.
+    LinkBetween(r, x, ny->prev_slot, y);
+  }
+  return true;
+}
+
+static bool TryMoveYAboveX(GraphCycles::Rep* r, int32_t x, int32_t y) {
+  Node* nx = NodeAt(r, x);
+  Node* ny = NodeAt(r, y);
+  int degree = 0;
+  HASH_FOR_EACH(w, ny->out) {
+    if (++degree > kMaxSingleNodeDegree) return false;
+    if (NodeAt(r, w)->rank <= nx->rank) return false;
+  }
+  Unlink(r, y);
+  if (degree == 0) {
+    // Move y above all other nodes.
+    LinkBetween(r, y, r->highest_slot, /*next_slot=*/-1);
+  } else {
+    // Move y right above x. Its out-neighbors rank above x, so they rank no
+    // lower than the node right above x.
+    LinkBetween(r, y, x, nx->next_slot);
+  }
+  return true;
+}
+
 bool GraphCycles::InsertEdge(GraphId idx, GraphId idy) {
   Rep* r = rep_;
   const int32_t x = NodeIndex(idx);
@@ -530,6 +767,9 @@
     return true;
   }
 
+  // Try moving just x or just y.
+  if (TryMoveXBelowY(r, x, y) || TryMoveYAboveX(r, x, y)) return true;
+
   // Current rank assignments are incompatible with the new edge.  Recompute.
   // We only need to consider nodes that fall in the range [ny->rank,nx->rank].
   if (!ForwardDFS(r, y, nx->rank)) {
@@ -548,7 +788,7 @@
   return true;
 }
 
-static bool ForwardDFS(GraphCycles::Rep* r, int32_t n, int32_t upper_bound) {
+static bool ForwardDFS(GraphCycles::Rep* r, int32_t n, int64_t upper_bound) {
   // Avoid recursion since stack space might be limited.
   // We instead keep a stack of nodes to visit.
   r->deltaf_.clear();
@@ -576,7 +816,7 @@
   return true;
 }
 
-static void BackwardDFS(GraphCycles::Rep* r, int32_t n, int32_t lower_bound) {
+static void BackwardDFS(GraphCycles::Rep* r, int32_t n, int64_t lower_bound) {
   r->deltab_.clear();
   r->stack_.clear();
   r->stack_.push_back(n);
@@ -602,19 +842,48 @@
   Sort(r->nodes_, &r->deltab_);
   Sort(r->nodes_, &r->deltaf_);
 
+  // The slots to reorder, sorted by their current ranks. In their new order
+  // (list_), they take over the same ranks and places in the rank list.
+  const uint32_t count = r->deltab_.size() + r->deltaf_.size();
+  r->sorted_slots.resize(count);
+  std::merge(r->deltab_.begin(), r->deltab_.end(), r->deltaf_.begin(),
+             r->deltaf_.end(), r->sorted_slots.begin(),
+             [r](int32_t a, int32_t b) {
+               return NodeAt(r, a)->rank < NodeAt(r, b)->rank;
+             });
+
   // Adds contents of delta lists to list_ (backwards deltas first).
   r->list_.clear();
   MoveToList(r, &r->deltab_, &r->list_);
   MoveToList(r, &r->deltaf_, &r->list_);
 
-  // Produce sorted list of all ranks that will be reassigned.
-  r->merged_.resize(r->deltab_.size() + r->deltaf_.size());
-  std::merge(r->deltab_.begin(), r->deltab_.end(), r->deltaf_.begin(),
-             r->deltaf_.end(), r->merged_.begin());
+  // Record the rank, prev_slot and next_slot of each place before changing
+  // them.
+  r->sorted_ranks.resize(count);
+  r->old_prev_slots.resize(count);
+  r->old_next_slots.resize(count);
+  for (uint32_t i = 0; i < count; i++) {
+    const Node* p = NodeAt(r, r->sorted_slots[i]);
+    r->sorted_ranks[i] = p->rank;
+    r->old_prev_slots[i] = p->prev_slot;
+    r->old_next_slots[i] = p->next_slot;
+  }
 
-  // Assign the ranks in order to the collected list.
-  for (uint32_t i = 0; i < r->list_.size(); i++) {
-    r->nodes_[static_cast<uint32_t>(r->list_[i])]->rank = r->merged_[i];
+  // Assign the ranks and places in order to the collected list. A prev_slot
+  // (or next_slot) that is also being reordered must be the previous (or next)
+  // place in sorted_slots, so link to the slot that now takes that place
+  // instead.
+  for (uint32_t i = 0; i < count; i++) {
+    const int32_t slot = r->list_[i];
+    NodeAt(r, slot)->rank = r->sorted_ranks[i];
+    if (i > 0 && r->old_prev_slots[i] == r->sorted_slots[i - 1]) {
+      Link(r, r->list_[i - 1], slot);
+    } else {
+      Link(r, r->old_prev_slots[i], slot);
+    }
+    if (i + 1 == count || r->old_next_slots[i] != r->sorted_slots[i + 1]) {
+      Link(r, slot, r->old_next_slots[i]);
+    }
   }
 }
 
@@ -633,10 +902,7 @@
 
 static void MoveToList(GraphCycles::Rep* r, Vec<int32_t>* src,
                        Vec<int32_t>* dst) {
-  for (auto& v : *src) {
-    int32_t w = v;
-    // Replace v entry with its rank
-    v = r->nodes_[static_cast<uint32_t>(w)]->rank;
+  for (int32_t w : *src) {
     // Prepare for future DFS calls
     r->nodes_[static_cast<uint32_t>(w)]->visited = false;
     dst->push_back(w);
diff --git a/absl/synchronization/internal/graphcycles.h b/absl/synchronization/internal/graphcycles.h
index da06504..27eaa6c 100644
--- a/absl/synchronization/internal/graphcycles.h
+++ b/absl/synchronization/internal/graphcycles.h
@@ -131,6 +131,12 @@
   // very full.
   void TestOnlyAddNodes(uint32_t n);
 
+  // Test-only method to set how far apart the ranks of new nodes are, and how
+  // far below (above) all other nodes a node is moved. The default is 2^32. A
+  // stride of up to 2^61 makes the ranks beyond the lowest and highest ones run
+  // out after a few new nodes or moves.
+  void TestOnlySetRankStride(int64_t stride);
+
   // ----------------------------------------------------
   struct Rep;
  private:
diff --git a/absl/synchronization/internal/graphcycles_benchmark.cc b/absl/synchronization/internal/graphcycles_benchmark.cc
index 9012801..9d11c38 100644
--- a/absl/synchronization/internal/graphcycles_benchmark.cc
+++ b/absl/synchronization/internal/graphcycles_benchmark.cc
@@ -40,4 +40,65 @@
 }
 BENCHMARK(BM_StressTest)->Range(2048, 1048576);
 
+using absl::synchronization_internal::GraphCycles;
+using absl::synchronization_internal::GraphId;
+
+// Returns a node for a new pointer value, next_ptr, and increments next_ptr.
+GraphId NewNode(GraphCycles& g, uintptr_t& next_ptr) {
+  return g.GetId(reinterpret_cast<void*>(next_ptr++));
+}
+
+// Adds a chain of n new nodes after node.
+void AddChain(GraphCycles& g, GraphId node, int n, uintptr_t& next_ptr) {
+  for (int i = 0; i < n; i++) {
+    const GraphId next = NewNode(g, next_ptr);
+    ABSL_RAW_CHECK(g.InsertEdge(node, next), "");
+    node = next;
+  }
+}
+
+// Inserts edges from new nodes to an old node that has a chain of
+// state.range(0) descendants. New nodes rank above old ones, so each edge
+// violates the rank order. Deadlock detection sees this pattern whenever a
+// newly created Mutex is held while an older one is acquired.
+void BM_NewNodeToOldNode(benchmark::State& state) {
+  const int num_descendants = static_cast<int>(state.range(0));
+  constexpr int kNewNodes = 10000;
+  while (state.KeepRunningBatch(kNewNodes)) {
+    GraphCycles g;
+    uintptr_t next_ptr = 1;
+    const GraphId old_node = NewNode(g, next_ptr);
+    AddChain(g, old_node, num_descendants, next_ptr);
+    for (int i = 0; i < kNewNodes; i++) {
+      const GraphId new_node = NewNode(g, next_ptr);
+      ABSL_RAW_CHECK(g.InsertEdge(new_node, old_node), "");
+    }
+  }
+}
+BENCHMARK(BM_NewNodeToOldNode)->Arg(256);
+
+// Inserts edges u -> new node -> y, where u -> y are old nodes and y has a
+// chain of state.range(0) descendants. Each new node must move between u and y,
+// so these moves all go into the same gap. Deadlock detection sees this
+// pattern when newly created Mutexes are each acquired while one Mutex is held,
+// and then held while another one is acquired.
+void BM_NewNodesBetweenOldNodes(benchmark::State& state) {
+  const int num_descendants = static_cast<int>(state.range(0));
+  constexpr int kNewNodes = 10000;
+  while (state.KeepRunningBatch(kNewNodes)) {
+    GraphCycles g;
+    uintptr_t next_ptr = 1;
+    const GraphId u = NewNode(g, next_ptr);
+    const GraphId y = NewNode(g, next_ptr);
+    ABSL_RAW_CHECK(g.InsertEdge(u, y), "");
+    AddChain(g, y, num_descendants, next_ptr);
+    for (int i = 0; i < kNewNodes; i++) {
+      const GraphId new_node = NewNode(g, next_ptr);
+      ABSL_RAW_CHECK(g.InsertEdge(u, new_node), "");
+      ABSL_RAW_CHECK(g.InsertEdge(new_node, y), "");
+    }
+  }
+}
+BENCHMARK(BM_NewNodesBetweenOldNodes)->Arg(256);
+
 }  // namespace
diff --git a/absl/synchronization/internal/graphcycles_test.cc b/absl/synchronization/internal/graphcycles_test.cc
index d4ba0f6..b1a6daf 100644
--- a/absl/synchronization/internal/graphcycles_test.cc
+++ b/absl/synchronization/internal/graphcycles_test.cc
@@ -483,6 +483,167 @@
   graph_cycles.RemoveNode(reinterpret_cast<void*>(buf));
 }
 
+// Returns ids for n new nodes. A new GraphCycles ranks nodes in creation order,
+// so InsertEdge(ids[i], ids[j]) with i > j violates the rank order and makes
+// InsertEdge reorder nodes.
+static std::vector<GraphId> NewNodes(GraphCycles* g, size_t n) {
+  std::vector<GraphId> ids(n);
+  for (size_t i = 0; i < n; ++i) {
+    ids[i] = g->GetId(reinterpret_cast<void*>(static_cast<uintptr_t>(i + 1)));
+  }
+  return ids;
+}
+
+// Each group of edges below violates the rank order in a different way. The
+// comments say how InsertEdge is expected to restore the order.
+TEST(GraphCycles, RankOrderRepairs) {
+  GraphCycles g;
+  std::vector<GraphId> n = NewNodes(&g, 16);
+
+  // x has no in-neighbors: move x below all nodes.
+  ASSERT_TRUE(g.InsertEdge(n[1], n[0]));
+
+  // x's in-neighbors rank below y: move x into the gap below y.
+  ASSERT_TRUE(g.InsertEdge(n[2], n[4]));
+  ASSERT_TRUE(g.InsertEdge(n[4], n[3]));
+
+  // y has no out-neighbors: move y above all nodes.
+  ASSERT_TRUE(g.InsertEdge(n[6], n[7]));
+  ASSERT_TRUE(g.InsertEdge(n[7], n[5]));
+
+  // y's out-neighbors rank above x: move y into the gap above x.
+  ASSERT_TRUE(g.InsertEdge(n[8], n[11]));
+  ASSERT_TRUE(g.InsertEdge(n[9], n[10]));
+  ASSERT_TRUE(g.InsertEdge(n[10], n[8]));
+
+  // Neither x nor y can move alone: Pearce-Kelly reorder.
+  ASSERT_TRUE(g.InsertEdge(n[12], n[13]));
+  ASSERT_TRUE(g.InsertEdge(n[14], n[15]));
+  ASSERT_TRUE(g.InsertEdge(n[15], n[12]));
+  CheckInvariants(g);
+
+  // Edges that would close a cycle are still rejected.
+  EXPECT_FALSE(g.InsertEdge(n[0], n[1]));
+  EXPECT_FALSE(g.InsertEdge(n[3], n[2]));
+  EXPECT_FALSE(g.InsertEdge(n[5], n[6]));
+  EXPECT_FALSE(g.InsertEdge(n[8], n[9]));
+  EXPECT_FALSE(g.InsertEdge(n[13], n[14]));
+  CheckInvariants(g);
+}
+
+// InsertEdge(x, y) does not move x if x has too many in-neighbors to check,
+// even if moving it would restore the rank order. Here y cannot move either, so
+// InsertEdge falls back to the Pearce-Kelly reorder.
+TEST(GraphCycles, TooManyNeighborsToMove) {
+  GraphCycles g;
+  std::vector<GraphId> n = NewNodes(&g, 41);
+
+  // x is 40, which has 17 in-neighbors. y is 30, which cannot move above x
+  // because of its out-neighbor 31.
+  for (size_t i = 0; i <= 16; ++i) {
+    ASSERT_TRUE(g.InsertEdge(n[i], n[40]));
+  }
+  ASSERT_TRUE(g.InsertEdge(n[30], n[31]));
+  ASSERT_TRUE(g.InsertEdge(n[40], n[30]));
+  CheckInvariants(g);
+
+  EXPECT_FALSE(g.InsertEdge(n[30], n[0]));
+  CheckInvariants(g);
+}
+
+// Each move of a node right below the same node halves the gap below it. Check
+// that InsertEdge keeps working when that gap runs out and the ranks around it
+// are respaced.
+TEST(GraphCycles, RespacesCrowdedGapBelow) {
+  GraphCycles g;
+  std::vector<GraphId> n = NewNodes(&g, 2000);
+
+  // Each n[i] has in-neighbor 50, which ranks below 51, so each n[i] -> n[51]
+  // edge below moves n[i] right below 51.
+  for (size_t i = 100; i < 2000; ++i) {
+    ASSERT_TRUE(g.InsertEdge(n[50], n[i]));
+    ASSERT_TRUE(g.InsertEdge(n[i], n[51]));
+    if (i % 100 == 0) CheckInvariants(g);
+  }
+  CheckInvariants(g);
+
+  EXPECT_FALSE(g.InsertEdge(n[51], n[50]));
+  EXPECT_FALSE(g.InsertEdge(n[51], n[100]));
+  EXPECT_FALSE(g.InsertEdge(n[1999], n[50]));
+  CheckInvariants(g);
+}
+
+// Same, for moves of a node right above the same node.
+TEST(GraphCycles, RespacesCrowdedGapAbove) {
+  GraphCycles g;
+  constexpr size_t kNumMoved = 1000;
+  std::vector<GraphId> n = NewNodes(&g, 2 * kNumMoved + 18);
+
+  // x ranks above each n[i], i < kNumMoved, and has too many in-neighbors to
+  // move. n[i] has an out-neighbor that ranks above x, so each x -> n[i] edge
+  // below moves n[i] right above x.
+  const GraphId x = n[kNumMoved + 17];
+  for (size_t j = 0; j <= 16; ++j) {
+    ASSERT_TRUE(g.InsertEdge(n[kNumMoved + j], x));
+  }
+  for (size_t i = 0; i < kNumMoved; ++i) {
+    ASSERT_TRUE(g.InsertEdge(n[i], n[kNumMoved + 18 + i]));
+    ASSERT_TRUE(g.InsertEdge(x, n[i]));
+    if (i % 100 == 0) CheckInvariants(g);
+  }
+  CheckInvariants(g);
+
+  EXPECT_FALSE(g.InsertEdge(n[0], x));
+  EXPECT_FALSE(g.InsertEdge(n[kNumMoved + 18], x));
+  EXPECT_FALSE(g.InsertEdge(n[kNumMoved - 1], n[kNumMoved]));
+  CheckInvariants(g);
+}
+
+// With a rank stride this large, the ranks beyond the lowest and highest ones
+// run out after a few new nodes, or a few moves of a node below or above all
+// other nodes, and then all ranks are respaced. Check that the rank order stays
+// valid.
+TEST(GraphCycles, RanksRunOut) {
+  GraphCycles g;
+  g.TestOnlySetRankStride(int64_t{1} << 60);
+  std::vector<GraphId> n = NewNodes(&g, 50);
+  for (size_t i = 0; i < 9; ++i) {
+    ASSERT_TRUE(g.InsertEdge(n[i], n[i + 1]));
+  }
+  CheckInvariants(g);
+  const GraphId a = n[10];
+  const GraphId b = n[11];
+
+  // a and b have no in-neighbors, so each InsertEdge call below moves one of
+  // them below all other nodes.
+  for (int i = 0; i < 1000; ++i) {
+    ASSERT_TRUE(g.InsertEdge(b, a));
+    g.RemoveEdge(b, a);
+    ASSERT_TRUE(g.InsertEdge(a, b));
+    g.RemoveEdge(a, b);
+  }
+  CheckInvariants(g);
+
+  // Now a and b have too many in-neighbors to move, so each InsertEdge call
+  // below moves the other one above all other nodes.
+  for (size_t i = 12; i < 50; ++i) {
+    ASSERT_TRUE(g.InsertEdge(n[i], i % 2 == 0 ? a : b));
+  }
+  for (int i = 0; i < 1000; ++i) {
+    ASSERT_TRUE(g.InsertEdge(b, a));
+    g.RemoveEdge(b, a);
+    ASSERT_TRUE(g.InsertEdge(a, b));
+    g.RemoveEdge(a, b);
+  }
+  CheckInvariants(g);
+
+  EXPECT_TRUE(g.IsReachable(n[0], n[9]));
+  EXPECT_FALSE(g.InsertEdge(n[9], n[0]));
+  ASSERT_TRUE(g.InsertEdge(n[9], a));
+  EXPECT_FALSE(g.InsertEdge(a, n[0]));
+  CheckInvariants(g);
+}
+
 }  // namespace synchronization_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/time/civil_time.cc b/absl/time/civil_time.cc
index a704247..76dbcaf 100644
--- a/absl/time/civil_time.cc
+++ b/absl/time/civil_time.cc
@@ -16,6 +16,7 @@
 
 #include <cerrno>
 #include <cstdlib>
+#include <limits>
 #include <ostream>
 #include <string>
 
@@ -68,7 +69,15 @@
     // end-of-year rollover) can carry into the year. The other fields are taken
     // from `cs`, so the same carry must be applied to the original year;
     // otherwise the reconstructed value would use the wrong (un-carried) year.
-    const civil_year_t year = y + (cs.year() - normalized_year);
+    // `cs.year()` and `normalized_year` both lie in the normalized range, so
+    // the carry is small, but applying it to `y` can overflow civil_year_t when
+    // `y` is at the extreme representable year; reject that rather than wrap.
+    const civil_year_t carry = cs.year() - normalized_year;
+    if ((carry > 0 && y > (std::numeric_limits<civil_year_t>::max)() - carry) ||
+        (carry < 0 && y < (std::numeric_limits<civil_year_t>::min)() - carry)) {
+      return false;
+    }
+    const civil_year_t year = y + carry;
     *c =
         CivilT(year, cs.month(), cs.day(), cs.hour(), cs.minute(), cs.second());
     return true;
diff --git a/absl/time/civil_time_test.cc b/absl/time/civil_time_test.cc
index 3f03fc1..53f52a5 100644
--- a/absl/time/civil_time_test.cc
+++ b/absl/time/civil_time_test.cc
@@ -873,6 +873,19 @@
   EXPECT_EQ(absl::CivilSecond(0, 1, 1, 0, 0, 0), ss);
 }
 
+TEST(CivilTime, ParseYearCarryDoesNotOverflow) {
+  // A ":60" leap second on the last second of the maximum representable year
+  // carries into the year, which would be civil_year_t(max) + 1 and is not
+  // representable, so the parse must fail cleanly rather than overflow.
+  absl::CivilSecond ss;
+  EXPECT_FALSE(absl::ParseCivilTime("9223372036854775807-12-31T23:59:60", &ss))
+      << ss;
+  // The same year without a carry remains valid and round-trips.
+  EXPECT_TRUE(absl::ParseCivilTime("9223372036854775807-12-31T23:59:59", &ss))
+      << ss;
+  EXPECT_EQ("9223372036854775807-12-31T23:59:59", absl::FormatCivilTime(ss));
+}
+
 TEST(CivilTime, AbslStringify) {
   static_assert(absl::HasAbslStringify<absl::CivilSecond>::value);
   static_assert(absl::HasAbslStringify<absl::CivilMinute>::value);
diff --git a/absl/time/internal/cctz/src/cctz_benchmark.cc b/absl/time/internal/cctz/src/cctz_benchmark.cc
index 17ff576..9457114 100644
--- a/absl/time/internal/cctz/src/cctz_benchmark.cc
+++ b/absl/time/internal/cctz/src/cctz_benchmark.cc
@@ -262,7 +262,7 @@
   while (state.KeepRunning()) {
     std::swap(t, t2);
     t += 1;
-#if defined(_WIN32) || defined(_WIN64)
+#if defined(_WIN32)
     benchmark::DoNotOptimize(localtime_s(&tm, &t));
 #else
     benchmark::DoNotOptimize(localtime_r(&t, &tm));
@@ -287,7 +287,7 @@
   struct tm tm;
   while (state.KeepRunning()) {
     t += 1;
-#if defined(_WIN32) || defined(_WIN64)
+#if defined(_WIN32)
     benchmark::DoNotOptimize(gmtime_s(&tm, &t));
 #else
     benchmark::DoNotOptimize(gmtime_r(&t, &tm));
diff --git a/absl/time/internal/cctz/src/time_zone_format.cc b/absl/time/internal/cctz/src/time_zone_format.cc
index ee4341d..c2839ea 100644
--- a/absl/time/internal/cctz/src/time_zone_format.cc
+++ b/absl/time/internal/cctz/src/time_zone_format.cc
@@ -198,7 +198,7 @@
   // as the result of a failed load_time_zone().
   char sign = '+';
   if (offset < 0) {
-    offset = -offset;  // bounded by 24h so no overflow
+    offset = -offset;  // bounded by -25h so no overflow
     sign = '-';
   }
   const int seconds = offset % 60;
@@ -219,7 +219,7 @@
     ep = Format02d(ep, minutes);
     if (sep != '\0') *--ep = sep;
   }
-  ep = Format02d(ep, hours);
+  ep = Format02d(ep, hours);  // bounded by 26h so fits in two digits
   *--ep = sign;
   return ep;
 }
diff --git a/absl/time/internal/cctz/src/time_zone_format_test.cc b/absl/time/internal/cctz/src/time_zone_format_test.cc
index cbaf09a..da3ff50 100644
--- a/absl/time/internal/cctz/src/time_zone_format_test.cc
+++ b/absl/time/internal/cctz/src/time_zone_format_test.cc
@@ -1998,7 +1998,7 @@
     EXPECT_EQ(in, out);  // RFC1123_full includes %z
   }
 
-#if defined(_WIN32) || defined(_WIN64)
+#if defined(_WIN32)
   // Initial investigations indicate the %c does not roundtrip on Windows.
   // TODO: Figure out what is going on here (perhaps a locale problem).
 #elif defined(__EMSCRIPTEN__)
diff --git a/absl/time/internal/cctz/src/time_zone_info.cc b/absl/time/internal/cctz/src/time_zone_info.cc
index 9d385b2..c04b680 100644
--- a/absl/time/internal/cctz/src/time_zone_info.cc
+++ b/absl/time/internal/cctz/src/time_zone_info.cc
@@ -790,8 +790,8 @@
   for (std::size_t i = 0; i != hdr.typecnt; ++i) {
     transition_types_[i].utc_offset =
         static_cast<std::int_least32_t>(Decode32(bp));
-    if (transition_types_[i].utc_offset >= kSecsPerDay ||
-        transition_types_[i].utc_offset <= -kSecsPerDay)
+    if (transition_types_[i].utc_offset >= 26 * 60 * 60 ||
+        transition_types_[i].utc_offset <= -25 * 60 * 60)
       return false;
     bp += 4;
     transition_types_[i].is_dst = (Decode8(bp++) != 0);
diff --git a/absl/time/internal/cctz/src/time_zone_libc.cc b/absl/time/internal/cctz/src/time_zone_libc.cc
index 362da1a..4e2680a 100644
--- a/absl/time/internal/cctz/src/time_zone_libc.cc
+++ b/absl/time/internal/cctz/src/time_zone_libc.cc
@@ -40,11 +40,11 @@
 
 namespace {
 
-#if defined(_WIN32) || defined(_WIN64)
+#if defined(_WIN32)
 // Uses the globals: '_timezone', '_dstbias' and '_tzname'.
-auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + _dstbias) {
+auto tm_gmtoff(const std::tm& tm) -> decltype(-_timezone + -_dstbias) {
   const bool is_dst = tm.tm_isdst > 0;
-  return _timezone + (is_dst ? _dstbias : 0);
+  return -_timezone + (is_dst ? -_dstbias : 0);
 }
 auto tm_zone(const std::tm& tm) -> decltype(_tzname[0]) {
   const bool is_dst = tm.tm_isdst > 0;
@@ -52,9 +52,9 @@
 }
 #elif defined(__sun) || defined(_AIX)
 // Uses the globals: 'timezone', 'altzone' and 'tzname'.
-auto tm_gmtoff(const std::tm& tm) -> decltype(timezone) {
+auto tm_gmtoff(const std::tm& tm) -> decltype(-timezone) {
   const bool is_dst = tm.tm_isdst > 0;
-  return is_dst ? altzone : timezone;
+  return is_dst ? -altzone : -timezone;
 }
 auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
   const bool is_dst = tm.tm_isdst > 0;
@@ -63,9 +63,9 @@
 #elif defined(__native_client__) || defined(__myriad2__) || \
     defined(__EMSCRIPTEN__)
 // Uses the globals: '_timezone' and 'tzname'.
-auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + 0) {
+auto tm_gmtoff(const std::tm& tm) -> decltype(-_timezone + 0) {
   const bool is_dst = tm.tm_isdst > 0;
-  return _timezone + (is_dst ? 60 * 60 : 0);
+  return -_timezone + (is_dst ? 60 * 60 : 0);
 }
 auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
   const bool is_dst = tm.tm_isdst > 0;
@@ -73,14 +73,38 @@
 }
 #elif defined(__VXWORKS__)
 // Uses the globals: 'timezone' and 'tzname'.
-auto tm_gmtoff(const std::tm& tm) -> decltype(timezone + 0) {
+auto tm_gmtoff(const std::tm& tm) -> decltype(-timezone + 0) {
   const bool is_dst = tm.tm_isdst > 0;
-  return timezone + (is_dst ? 60 * 60 : 0);
+  return -timezone + (is_dst ? 60 * 60 : 0);
 }
 auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
   const bool is_dst = tm.tm_isdst > 0;
   return tzname[is_dst];
 }
+#elif defined(__NEWLIB__)
+// The struct std::tm extension fields are named by __TM_GMTOFF and __TM_ZONE,
+// either of which the configuration may leave undefined. Where it does, fall
+// back on the globals: '_timezone' and 'tzname'.
+#if defined(__TM_GMTOFF)
+auto tm_gmtoff(const std::tm& tm) -> decltype(tm.__TM_GMTOFF) {
+  return tm.__TM_GMTOFF;
+}
+#else
+auto tm_gmtoff(const std::tm& tm) -> decltype(-_timezone + 0) {
+  const bool is_dst = tm.tm_isdst > 0;
+  return -_timezone + (is_dst ? 60 * 60 : 0);
+}
+#endif
+#if defined(__TM_ZONE)
+auto tm_zone(const std::tm& tm) -> decltype(tm.__TM_ZONE) {
+  return tm.__TM_ZONE;
+}
+#else
+auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
+  const bool is_dst = tm.tm_isdst > 0;
+  return tzname[is_dst];
+}
+#endif
 #else
 // Adapt to different spellings of the struct std::tm extension fields.
 #if defined(tm_gmtoff)
@@ -121,7 +145,7 @@
 using tm_gmtoff_t = decltype(tm_gmtoff(std::tm{}));
 
 inline std::tm* gm_time(const std::time_t* timep, std::tm* result) {
-#if defined(_WIN32) || defined(_WIN64)
+#if defined(_WIN32)
   return gmtime_s(result, timep) ? nullptr : result;
 #else
   return gmtime_r(timep, result);
@@ -129,7 +153,7 @@
 }
 
 inline std::tm* local_time(const std::time_t* timep, std::tm* result) {
-#if defined(_WIN32) || defined(_WIN64)
+#if defined(_WIN32)
   return localtime_s(result, timep) ? nullptr : result;
 #else
   return localtime_r(timep, result);
diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
index f53ac03..ab9a432 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
@@ -470,7 +470,7 @@
   if (sizeof(std::time_t) >= 8) {
     // Checks that "tm_year + 1900", as used by the "libc" implementation,
     // can produce year values beyond the range on an int without overflow.
-#if defined(_WIN32) || defined(_WIN64)
+#if defined(_WIN32)
     // localtime_s() and gmtime_s() don't believe in years outside [1970:3000].
 #else
     const time_zone cut = LoadZone("libc:UTC");
@@ -1087,6 +1087,22 @@
     return std::unique_ptr<ZoneInfoSource>(new StringZoneInfoSource(
         MakeExtendedTzif(0, -5 * 3600, "EST", "EST5EDT,M3.2.0,M11.1.0")));
   }
+  if (name == "test:ExtendedLargeOffset") {
+    // A file-supplied UTC offset of 25h is more than a day from UTC but still
+    // within the (-25h < utoff < 26h) range RFC 9636 allows, so the reader
+    // must accept it.
+    return std::unique_ptr<ZoneInfoSource>(
+        new StringZoneInfoSource(MakeExtendedTzif(
+            0, 25 * 3600, std::string{"EST", 4}, "EST5EDT,M3.2.0,M11.1.0")));
+  }
+  if (name == "test:MinUtcOffset") {
+    // The minimum 32-bit UTC offset is far outside the (-25h < utoff < 26h)
+    // range RFC 9636 allows, and is the one offset whose negation overflows
+    // in FormatOffset(), so the reader must reject it.
+    const std::int_fast32_t min_offset = -2147483647 - 1;
+    return std::unique_ptr<ZoneInfoSource>(new StringZoneInfoSource(
+        MakeExtendedTzif(0, min_offset, std::string{"X", 2}, "")));
+  }
   return fallback(name);
 }
 
@@ -1129,6 +1145,31 @@
   cctz_extension::zone_info_source_factory = prev_factory;
 }
 
+// A file-supplied UTC offset more than a day from UTC is still valid per
+// RFC 9636 (which allows -25h < utoff < 26h), so loading must succeed.
+TEST(TimeZoneEdgeCase, ExtendedLargeOffset) {
+  auto prev_factory = cctz_extension::zone_info_source_factory;
+  cctz_extension::zone_info_source_factory = ExtendedTestFactory;
+
+  time_zone tz;
+  EXPECT_TRUE(load_time_zone("test:ExtendedLargeOffset", &tz));
+
+  cctz_extension::zone_info_source_factory = prev_factory;
+}
+
+// A file-supplied UTC offset outside the range RFC 9636 allows (which is
+// -25h < utoff < 26h) would break the fixed-width offset formatting, so
+// loading must fail.
+TEST(TimeZoneEdgeCase, MinUtcOffset) {
+  auto prev_factory = cctz_extension::zone_info_source_factory;
+  cctz_extension::zone_info_source_factory = ExtendedTestFactory;
+
+  time_zone tz;
+  EXPECT_FALSE(load_time_zone("test:MinUtcOffset", &tz));
+
+  cctz_extension::zone_info_source_factory = prev_factory;
+}
+
 // Looking up the maximum time in an extended zone must fold back through the
 // 400-year cycle without overflowing when BreakTime() computes the shift.
 TEST(TimeZoneEdgeCase, ExtendedFarFuture) {
diff --git a/absl/time/internal/cctz/testdata/version b/absl/time/internal/cctz/testdata/version
index b62e268..2c2d640 100644
--- a/absl/time/internal/cctz/testdata/version
+++ b/absl/time/internal/cctz/testdata/version
@@ -1 +1 @@
-2026d
+2026e
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Rainy_River b/absl/time/internal/cctz/testdata/zoneinfo/America/Rainy_River
index 7e646d1..0ca1509 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Rainy_River
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Rainy_River
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Winnipeg b/absl/time/internal/cctz/testdata/zoneinfo/America/Winnipeg
index 7e646d1..0ca1509 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Winnipeg
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Winnipeg
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Central b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Central
index 7e646d1..0ca1509 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Central
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Central
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Eire b/absl/time/internal/cctz/testdata/zoneinfo/Eire
index 17d2b15..df91cd5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Eire
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Eire
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Dublin b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Dublin
index 17d2b15..df91cd5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Dublin
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Dublin
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
index 0c6a15c..1aed9b5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
+++ b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
@@ -107,7 +107,7 @@
 CA	+5320-06025	America/Goose_Bay	Atlantic - Labrador (most areas)
 CA,BS	+4339-07923	America/Toronto	Eastern - ON & QC (most areas)
 CA	+6344-06828	America/Iqaluit	Eastern - NU (most areas)
-CA	+4953-09709	America/Winnipeg	Central - ON (west), Manitoba
+CA	+4953-09709	America/Winnipeg	EST - Manitoba, ON (northwest)
 CA	+744144-0944945	America/Resolute	Central - NU (Resolute)
 CA	+624900-0920459	America/Rankin_Inlet	Central - NU (central)
 CA	+5024-10439	America/Regina	CST - SK (most areas)
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/zonenow.tab b/absl/time/internal/cctz/testdata/zoneinfo/zonenow.tab
index 1c9b44b..bd3a278 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/zonenow.tab
+++ b/absl/time/internal/cctz/testdata/zoneinfo/zonenow.tab
@@ -80,7 +80,10 @@
 XX	-2709-10926	Pacific/Easter	Easter Island
 #
 # -06/-05 - CST/CDT (North America DST)
-XX	+415100-0873900	America/Chicago	Central (CST/CDT) - US & Canada; Mexico near US border
+XX	+415100-0873900	America/Chicago	Central (CST/CDT) - US; Mexico near US border; central Nunavut
+#
+# -06/-05 - CST/CDT (North America DST) until 2026-11-01 02:00; then EST
+XX	+4953-09709	America/Winnipeg	Eastern Standard (EST) - Manitoba, northwestern Ontario
 #
 # -05
 XX	-1203-07703	America/Lima	eastern South America
diff --git a/ci/cmake_install_test.sh b/ci/cmake_install_test.sh
index 5e3e07b..d3bc5d0 100755
--- a/ci/cmake_install_test.sh
+++ b/ci/cmake_install_test.sh
@@ -44,7 +44,7 @@
     --rm \
     ${DOCKER_EXTRA_ARGS:-} \
     ${DOCKER_CONTAINER} \
-    /bin/bash -c "cmake /abseil-cpp-ro && make -j$(nproc) && make install"
+    /bin/sh -c "cmake /abseil-cpp-ro && make -j$(nproc) && make install"
 
 # Verify that a more complicated project works.
 for link_type in ${LINK_TYPE}; do
@@ -60,5 +60,5 @@
     --rm \
     ${DOCKER_EXTRA_ARGS:-} \
     ${DOCKER_CONTAINER} \
-    /bin/bash -c "cp -r /abseil-cpp-ro/* . && CMake/install_test_project/test.sh"
+    /bin/sh -c "cp -r /abseil-cpp-ro/* . && CMake/install_test_project/test.sh"
 done
diff --git a/ci/linux_arm_clang-latest_libcxx_bazel.sh b/ci/linux_arm_clang-latest_libcxx_bazel.sh
index 24d5e48..01c5610 100755
--- a/ci/linux_arm_clang-latest_libcxx_bazel.sh
+++ b/ci/linux_arm_clang-latest_libcxx_bazel.sh
@@ -33,7 +33,7 @@
 fi
 
 if [[ -z ${EXCEPTIONS_MODE:-} ]]; then
-  EXCEPTIONS_MODE="-fno-exceptions -fexceptions"
+  EXCEPTIONS_MODE="-fno-exceptions"
 fi
 
 source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh"
@@ -44,13 +44,13 @@
 docker run "${DOCKER_CONTAINER}" /opt/llvm/bin/clang -v
 docker run "${DOCKER_CONTAINER}" cat /root/cached_bazel_versions
 
-# USE_BAZEL_CACHE=1 only works on Kokoro.
+# Bazel cache only works on Kokoro.
 # Without access to the credentials this won't work.
-if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then
+if [[ ${KOKORO_KEYSTORE_DIR:-} ]]; then
   DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_KEYSTORE_DIR},target=/keystore,readonly ${DOCKER_EXTRA_ARGS:-}"
   # Bazel doesn't track changes to tools outside of the workspace
   # (e.g. /usr/bin/gcc), so by appending the docker container to the
-  # remote_http_cache url, we make changes to the container part of
+  # remote_cache url, we make changes to the container part of
   # the cache key. Hashing the key is to make it shorter and url-safe.
   container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
   BAZEL_EXTRA_ARGS="--remote_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
@@ -81,7 +81,7 @@
         --rm \
         ${DOCKER_EXTRA_ARGS:-} \
         ${DOCKER_CONTAINER} \
-        /bin/bash --login -c "
+        /bin/sh -c "
           cp -r /abseil-cpp-ro/* /abseil-cpp/
           if [ -n \"${ALTERNATE_OPTIONS:-}\" ]; then
             cp ${ALTERNATE_OPTIONS:-} absl/base/options.h || exit 1
diff --git a/ci/linux_clang-latest_libcxx_asan_bazel.sh b/ci/linux_clang-latest_libcxx_asan_bazel.sh
index b8d2b5f..d31f608 100755
--- a/ci/linux_clang-latest_libcxx_asan_bazel.sh
+++ b/ci/linux_clang-latest_libcxx_asan_bazel.sh
@@ -29,11 +29,11 @@
 fi
 
 if [[ -z ${COMPILATION_MODE:-} ]]; then
-  COMPILATION_MODE="fastbuild opt"
+  COMPILATION_MODE="fastbuild"
 fi
 
 if [[ -z ${EXCEPTIONS_MODE:-} ]]; then
-  EXCEPTIONS_MODE="-fno-exceptions -fexceptions"
+  EXCEPTIONS_MODE="-fno-exceptions"
 fi
 
 source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh"
@@ -44,24 +44,24 @@
 docker run "${DOCKER_CONTAINER}" /opt/llvm/bin/clang -v
 docker run "${DOCKER_CONTAINER}" cat /root/cached_bazel_versions
 
-# USE_BAZEL_CACHE=1 only works on Kokoro.
+# Bazel cache only works on Kokoro.
 # Without access to the credentials this won't work.
-if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then
+if [[ ${KOKORO_KEYSTORE_DIR:-} ]]; then
   DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_KEYSTORE_DIR},target=/keystore,readonly ${DOCKER_EXTRA_ARGS:-}"
   # Bazel doesn't track changes to tools outside of the workspace
   # (e.g. /usr/bin/gcc), so by appending the docker container to the
-  # remote_http_cache url, we make changes to the container part of
+  # remote_cache url, we make changes to the container part of
   # the cache key. Hashing the key is to make it shorter and url-safe.
   container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
   BAZEL_EXTRA_ARGS="--remote_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
 fi
 
-# Avoid depending on external sites like GitHub by checking --distdir for
-# external dependencies first.
-# https://docs.bazel.build/versions/master/guide.html#distdir
-if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then
-  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly ${DOCKER_EXTRA_ARGS:-}"
-  BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}"
+# Use Bazel Vendor mode to reduce reliance on external dependencies.
+# See https://bazel.build/external/vendor and the Dockerfile for
+# an explaination of how this works.
+if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f "${KOKORO_GFILE_DIR}/distdir/abseil-cpp_vendor.tar.gz" ]]; then
+  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly --env=BAZEL_VENDOR_ARCHIVE=/distdir/abseil-cpp_vendor.tar.gz ${DOCKER_EXTRA_ARGS:-}"
+  BAZEL_EXTRA_ARGS="--vendor_dir=/abseil-cpp_vendor ${BAZEL_EXTRA_ARGS:-}"
 fi
 
 # https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#available-checks
@@ -83,7 +83,7 @@
         --rm \
         ${DOCKER_EXTRA_ARGS:-} \
         ${DOCKER_CONTAINER} \
-        /bin/bash --login -c "
+        /bin/sh -c "
         /usr/local/bin/bazel test ... \
           --compilation_mode=\"${compilation_mode}\" \
           --copt=\"${exceptions_mode}\" \
diff --git a/ci/linux_clang-latest_libcxx_bazel.sh b/ci/linux_clang-latest_libcxx_bazel.sh
index 99e92b9..830dd14 100755
--- a/ci/linux_clang-latest_libcxx_bazel.sh
+++ b/ci/linux_clang-latest_libcxx_bazel.sh
@@ -33,7 +33,7 @@
 fi
 
 if [[ -z ${EXCEPTIONS_MODE:-} ]]; then
-  EXCEPTIONS_MODE="-fno-exceptions -fexceptions"
+  EXCEPTIONS_MODE="-fno-exceptions"
 fi
 
 source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh"
@@ -44,13 +44,13 @@
 docker run "${DOCKER_CONTAINER}" /opt/llvm/bin/clang -v
 docker run "${DOCKER_CONTAINER}" cat /root/cached_bazel_versions
 
-# USE_BAZEL_CACHE=1 only works on Kokoro.
+# Bazel cache only works on Kokoro.
 # Without access to the credentials this won't work.
-if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then
+if [[ ${KOKORO_KEYSTORE_DIR:-} ]]; then
   DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_KEYSTORE_DIR},target=/keystore,readonly ${DOCKER_EXTRA_ARGS:-}"
   # Bazel doesn't track changes to tools outside of the workspace
   # (e.g. /usr/bin/gcc), so by appending the docker container to the
-  # remote_http_cache url, we make changes to the container part of
+  # remote_cache url, we make changes to the container part of
   # the cache key. Hashing the key is to make it shorter and url-safe.
   container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
   BAZEL_EXTRA_ARGS="--remote_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
@@ -81,7 +81,7 @@
         --rm \
         ${DOCKER_EXTRA_ARGS:-} \
         ${DOCKER_CONTAINER} \
-        /bin/bash --login -c "
+        /bin/sh -c "
           cp -r /abseil-cpp-ro/* /abseil-cpp/
           if [ -n \"${ALTERNATE_OPTIONS:-}\" ]; then
             cp ${ALTERNATE_OPTIONS:-} absl/base/options.h || exit 1
diff --git a/ci/linux_clang-latest_libcxx_tsan_bazel.sh b/ci/linux_clang-latest_libcxx_tsan_bazel.sh
index 56c3c41..240dbea 100755
--- a/ci/linux_clang-latest_libcxx_tsan_bazel.sh
+++ b/ci/linux_clang-latest_libcxx_tsan_bazel.sh
@@ -29,11 +29,11 @@
 fi
 
 if [[ -z ${COMPILATION_MODE:-} ]]; then
-  COMPILATION_MODE="fastbuild opt"
+  COMPILATION_MODE="fastbuild"
 fi
 
 if [[ -z ${EXCEPTIONS_MODE:-} ]]; then
-  EXCEPTIONS_MODE="-fno-exceptions -fexceptions"
+  EXCEPTIONS_MODE="-fno-exceptions"
 fi
 
 source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh"
@@ -44,13 +44,13 @@
 docker run "${DOCKER_CONTAINER}" /opt/llvm/bin/clang -v
 docker run "${DOCKER_CONTAINER}" cat /root/cached_bazel_versions
 
-# USE_BAZEL_CACHE=1 only works on Kokoro.
+# Bazel cache only works on Kokoro.
 # Without access to the credentials this won't work.
-if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then
+if [[ ${KOKORO_KEYSTORE_DIR:-} ]]; then
   DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_KEYSTORE_DIR},target=/keystore,readonly ${DOCKER_EXTRA_ARGS:-}"
   # Bazel doesn't track changes to tools outside of the workspace
   # (e.g. /usr/bin/gcc), so by appending the docker container to the
-  # remote_http_cache url, we make changes to the container part of
+  # remote_cache url, we make changes to the container part of
   # the cache key. Hashing the key is to make it shorter and url-safe.
   container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
   BAZEL_EXTRA_ARGS="--remote_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
@@ -80,7 +80,7 @@
         --rm \
         ${DOCKER_EXTRA_ARGS:-} \
         ${DOCKER_CONTAINER} \
-        /bin/bash --login -c "
+        /bin/sh -c "
         /usr/local/bin/bazel test ... \
           --build_tag_filters=\"-notsan\" \
           --compilation_mode=\"${compilation_mode}\" \
diff --git a/ci/linux_clang-latest_libstdcxx_bazel.sh b/ci/linux_clang-latest_libstdcxx_bazel.sh
index 2fc8210..fe15f84 100755
--- a/ci/linux_clang-latest_libstdcxx_bazel.sh
+++ b/ci/linux_clang-latest_libstdcxx_bazel.sh
@@ -33,7 +33,7 @@
 fi
 
 if [[ -z ${EXCEPTIONS_MODE:-} ]]; then
-  EXCEPTIONS_MODE="-fno-exceptions -fexceptions"
+  EXCEPTIONS_MODE="-fno-exceptions"
 fi
 
 source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh"
@@ -45,13 +45,13 @@
 docker run "${DOCKER_CONTAINER}" /opt/llvm/bin/clang -v
 docker run "${DOCKER_CONTAINER}" cat /root/cached_bazel_versions
 
-# USE_BAZEL_CACHE=1 only works on Kokoro.
+# Bazel cache only works on Kokoro.
 # Without access to the credentials this won't work.
-if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then
+if [[ ${KOKORO_KEYSTORE_DIR:-} ]]; then
   DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_KEYSTORE_DIR},target=/keystore,readonly ${DOCKER_EXTRA_ARGS:-}"
   # Bazel doesn't track changes to tools outside of the workspace
   # (e.g. /usr/bin/gcc), so by appending the docker container to the
-  # remote_http_cache url, we make changes to the container part of
+  # remote_cache url, we make changes to the container part of
   # the cache key. Hashing the key is to make it shorter and url-safe.
   container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
   BAZEL_EXTRA_ARGS="--remote_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
@@ -79,7 +79,7 @@
         --rm \
         ${DOCKER_EXTRA_ARGS:-} \
         ${DOCKER_CONTAINER} \
-        /bin/bash --login -c "
+        /bin/sh -c "
         /usr/local/bin/bazel test ... \
           --compilation_mode=\"${compilation_mode}\" \
           --copt=\"--gcc-toolchain=/usr/local\" \
diff --git a/ci/linux_gcc-floor_libstdcxx_bazel.sh b/ci/linux_gcc-floor_libstdcxx_bazel.sh
index 765b784..f6f59cb 100755
--- a/ci/linux_gcc-floor_libstdcxx_bazel.sh
+++ b/ci/linux_gcc-floor_libstdcxx_bazel.sh
@@ -33,7 +33,7 @@
 fi
 
 if [[ -z ${EXCEPTIONS_MODE:-} ]]; then
-  EXCEPTIONS_MODE="-fno-exceptions -fexceptions"
+  EXCEPTIONS_MODE="-fno-exceptions"
 fi
 
 source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh"
@@ -44,16 +44,16 @@
 docker run "${DOCKER_CONTAINER}" /opt/gcc-10/bin/gcc -v
 docker run "${DOCKER_CONTAINER}" cat /root/cached_bazel_versions
 
-# USE_BAZEL_CACHE=1 only works on Kokoro.
+# Bazel cache only works on Kokoro.
 # Without access to the credentials this won't work.
-if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then
-  DOCKER_EXTRA_ARGS="--volume=${KOKORO_KEYSTORE_DIR}:/keystore:ro ${DOCKER_EXTRA_ARGS:-}"
+if [[ ${KOKORO_KEYSTORE_DIR:-} ]]; then
+  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_KEYSTORE_DIR},target=/keystore,readonly ${DOCKER_EXTRA_ARGS:-}"
   # Bazel doesn't track changes to tools outside of the workspace
   # (e.g. /usr/bin/gcc), so by appending the docker container to the
-  # remote_http_cache url, we make changes to the container part of
+  # remote_cache url, we make changes to the container part of
   # the cache key. Hashing the key is to make it shorter and url-safe.
   container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
-  BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
+  BAZEL_EXTRA_ARGS="--remote_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
 fi
 
 # Use Bazel Vendor mode to reduce reliance on external dependencies.
@@ -73,13 +73,13 @@
         --env="CC=/opt/gcc-10/bin/gcc" \
         --env="BAZEL_CXXOPTS=-std=${std}" \
         --env="BAZEL_LINKOPTS=-L/opt/gcc-10/lib64:-Wl,-rpath=/opt/gcc-10/lib64" \
-        --volume="${ABSEIL_ROOT}:/abseil-cpp:ro" \
+        --mount type=bind,source="${ABSEIL_ROOT}",target=/abseil-cpp,readonly \
         --workdir=/abseil-cpp \
         --cap-add=SYS_PTRACE \
         --rm \
         ${DOCKER_EXTRA_ARGS:-} \
         ${DOCKER_CONTAINER} \
-        /bin/bash --login -c "
+        /bin/sh -c "
         /usr/local/bin/bazel test ... \
           --compilation_mode=\"${compilation_mode}\" \
           --copt=\"${exceptions_mode}\" \
diff --git a/ci/linux_gcc-latest_libstdcxx_bazel.sh b/ci/linux_gcc-latest_libstdcxx_bazel.sh
index 509aeb9..51d3c9c 100755
--- a/ci/linux_gcc-latest_libstdcxx_bazel.sh
+++ b/ci/linux_gcc-latest_libstdcxx_bazel.sh
@@ -33,7 +33,7 @@
 fi
 
 if [[ -z ${EXCEPTIONS_MODE:-} ]]; then
-  EXCEPTIONS_MODE="-fno-exceptions -fexceptions"
+  EXCEPTIONS_MODE="-fno-exceptions"
 fi
 
 source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh"
@@ -44,13 +44,13 @@
 docker run "${DOCKER_CONTAINER}" /usr/local/bin/gcc -v
 docker run "${DOCKER_CONTAINER}" cat /root/cached_bazel_versions
 
-# USE_BAZEL_CACHE=1 only works on Kokoro.
+# Bazel cache only works on Kokoro.
 # Without access to the credentials this won't work.
-if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then
+if [[ ${KOKORO_KEYSTORE_DIR:-} ]]; then
   DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_KEYSTORE_DIR},target=/keystore,readonly ${DOCKER_EXTRA_ARGS:-}"
   # Bazel doesn't track changes to tools outside of the workspace
   # (e.g. /usr/bin/gcc), so by appending the docker container to the
-  # remote_http_cache url, we make changes to the container part of
+  # remote_cache url, we make changes to the container part of
   # the cache key. Hashing the key is to make it shorter and url-safe.
   container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
   BAZEL_EXTRA_ARGS="--remote_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
@@ -79,7 +79,7 @@
         --rm \
         ${DOCKER_EXTRA_ARGS:-} \
         ${DOCKER_CONTAINER} \
-        /bin/bash --login -c "
+        /bin/sh -c "
           cp -r /abseil-cpp-ro/* /abseil-cpp/
           if [ -n \"${ALTERNATE_OPTIONS:-}\" ]; then
             cp ${ALTERNATE_OPTIONS:-} absl/base/options.h || exit 1
diff --git a/ci/linux_gcc-latest_libstdcxx_cmake.sh b/ci/linux_gcc-latest_libstdcxx_cmake.sh
index 2f69bba..4463882 100755
--- a/ci/linux_gcc-latest_libstdcxx_cmake.sh
+++ b/ci/linux_gcc-latest_libstdcxx_cmake.sh
@@ -28,8 +28,8 @@
   DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly ${DOCKER_EXTRA_ARGS:-}"
 fi
 
-if [[ -z ${ABSL_CMAKE_CXX_STANDARDS:-} ]]; then
-  ABSL_CMAKE_CXX_STANDARDS="17 20"
+if [[ -z ${STD:-} ]]; then
+  STD="17"
 fi
 
 if [[ -z ${ABSL_CMAKE_BUILD_TYPES:-} ]]; then
@@ -47,7 +47,7 @@
 source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh"
 readonly DOCKER_CONTAINER=${LINUX_GCC_LATEST_CONTAINER}
 
-for std in ${ABSL_CMAKE_CXX_STANDARDS}; do
+for std in ${STD}; do
   for compilation_mode in ${ABSL_CMAKE_BUILD_TYPES}; do
     for build_shared in ${ABSL_CMAKE_BUILD_SHARED}; do
       if [[ $build_shared == "OFF" ]]; then
@@ -67,14 +67,14 @@
           -e CXXFLAGS="-Werror" \
           ${DOCKER_EXTRA_ARGS:-} \
           "${DOCKER_CONTAINER}" \
-          /bin/bash -c "
+          /bin/sh -c "
             cmake /abseil-cpp \
-              -DABSL_GOOGLETEST_DOWNLOAD_URL=${ABSL_GOOGLETEST_DOWNLOAD_URL} \
-              -DBUILD_SHARED_LIBS=${build_shared} \
+              -DABSL_GOOGLETEST_DOWNLOAD_URL=\"${ABSL_GOOGLETEST_DOWNLOAD_URL}\" \
+              -DBUILD_SHARED_LIBS=\"${build_shared}\" \
               -DABSL_BUILD_TESTING=ON \
-              -DCMAKE_BUILD_TYPE=${compilation_mode} \
-              -DCMAKE_CXX_STANDARD=${std} \
-              -DABSL_BUILD_MONOLITHIC_SHARED_LIBS=${monolithic_shared} \
+              -DCMAKE_BUILD_TYPE=\"${compilation_mode}\" \
+              -DCMAKE_CXX_STANDARD=\"${std}\" \
+              -DABSL_BUILD_MONOLITHIC_SHARED_LIBS=\"${monolithic_shared}\" \
               -DCMAKE_MODULE_LINKER_FLAGS=\"-Wl,--no-undefined\" && \
             make -j$(nproc) && \
             TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo \
diff --git a/ci/linux_gcc_alpine_cmake.sh b/ci/linux_gcc_alpine_cmake.sh
index 1d64a95..20393e9 100755
--- a/ci/linux_gcc_alpine_cmake.sh
+++ b/ci/linux_gcc_alpine_cmake.sh
@@ -28,16 +28,16 @@
   DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly ${DOCKER_EXTRA_ARGS:-}"
 fi
 
-if [[ -z ${ABSL_CMAKE_CXX_STANDARDS:-} ]]; then
-  ABSL_CMAKE_CXX_STANDARDS="17"
+if [[ -z ${STD:-} ]]; then
+  STD="17"
 fi
 
 if [[ -z ${ABSL_CMAKE_BUILD_TYPES:-} ]]; then
-  ABSL_CMAKE_BUILD_TYPES="Debug Release"
+  ABSL_CMAKE_BUILD_TYPES="Debug"
 fi
 
 if [[ -z ${ABSL_CMAKE_BUILD_SHARED:-} ]]; then
-  ABSL_CMAKE_BUILD_SHARED="OFF ON"
+  ABSL_CMAKE_BUILD_SHARED="OFF"
 fi
 
 if [[ -z ${ABSL_CMAKE_BUILD_MONOLITHIC_SHARED_LIBS:-} ]]; then
@@ -47,7 +47,7 @@
 source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh"
 readonly DOCKER_CONTAINER=${LINUX_ALPINE_CONTAINER}
 
-for std in ${ABSL_CMAKE_CXX_STANDARDS}; do
+for std in ${STD}; do
   for compilation_mode in ${ABSL_CMAKE_BUILD_TYPES}; do
     for build_shared in ${ABSL_CMAKE_BUILD_SHARED}; do
       if [[ $build_shared == "OFF" ]]; then
@@ -69,12 +69,12 @@
           "${DOCKER_CONTAINER}" \
           /bin/sh -c "
             cmake /abseil-cpp \
-              -DABSL_GOOGLETEST_DOWNLOAD_URL=${ABSL_GOOGLETEST_DOWNLOAD_URL} \
-              -DBUILD_SHARED_LIBS=${build_shared} \
+              -DABSL_GOOGLETEST_DOWNLOAD_URL=\"${ABSL_GOOGLETEST_DOWNLOAD_URL}\" \
+              -DBUILD_SHARED_LIBS=\"${build_shared}\" \
               -DABSL_BUILD_TESTING=ON \
-              -DCMAKE_BUILD_TYPE=${compilation_mode} \
-              -DCMAKE_CXX_STANDARD=${std} \
-              -DABSL_BUILD_MONOLITHIC_SHARED_LIBS=${monolithic_shared} \
+              -DCMAKE_BUILD_TYPE=\"${compilation_mode}\" \
+              -DCMAKE_CXX_STANDARD=\"${std}\" \
+              -DABSL_BUILD_MONOLITHIC_SHARED_LIBS=\"${monolithic_shared}\" \
               -DCMAKE_MODULE_LINKER_FLAGS=\"-Wl,--no-undefined\" && \
             make -j$(nproc) && \
             TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo \
diff --git a/ci/macos_xcode_cmake.sh b/ci/macos_xcode_cmake.sh
index 7a6ccd5..47b9e03 100755
--- a/ci/macos_xcode_cmake.sh
+++ b/ci/macos_xcode_cmake.sh
@@ -63,12 +63,12 @@
       # TODO(absl-team): Enable -Werror once all warnings are fixed.
       time cmake ${ABSEIL_ROOT} \
         -GXcode \
-        -DBUILD_SHARED_LIBS=${build_shared} \
+        -DBUILD_SHARED_LIBS="${build_shared}" \
         -DABSL_BUILD_TESTING=ON \
-        -DCMAKE_BUILD_TYPE=${compilation_mode} \
+        -DCMAKE_BUILD_TYPE="${compilation_mode}" \
         -DCMAKE_CXX_STANDARD=17 \
         -DCMAKE_MODULE_LINKER_FLAGS="-Wl,--no-undefined" \
-        -DABSL_BUILD_MONOLITHIC_SHARED_LIBS=${monolithic_shared} \
+        -DABSL_BUILD_MONOLITHIC_SHARED_LIBS="${monolithic_shared}" \
         -DABSL_GOOGLETEST_DOWNLOAD_URL="${ABSL_GOOGLETEST_DOWNLOAD_URL}"
       time cmake --build .
       time TZDIR=${ABSEIL_ROOT}/absl/time/internal/cctz/testdata/zoneinfo \
diff --git a/ci/windows_clangcl_bazel.bat b/ci/windows_clangcl_bazel.bat
index 42be0f7..e196d3b 100755
--- a/ci/windows_clangcl_bazel.bat
+++ b/ci/windows_clangcl_bazel.bat
@@ -32,15 +32,15 @@
 :: Set the standard version, [c++17|c++20|c++latest]
 :: https://msdn.microsoft.com/en-us/library/mt490614.aspx
 :: The default is c++17 if not set on command line.
-IF "%STD%"=="" SET STD=c++17
+IF NOT DEFINED STD SET STD=c++17
 
 :: Set the compilation_mode (fastbuild|opt|dbg)
 :: https://docs.bazel.build/versions/master/user-manual.html#flag--compilation_mode
 :: The default is fastbuild
-IF "%COMPILATION_MODE%"=="" SET COMPILATION_MODE=fastbuild
+IF NOT DEFINED COMPILATION_MODE SET COMPILATION_MODE=fastbuild
 
 :: Copy the alternate option file, if specified.
-IF NOT "%ALTERNATE_OPTIONS%"=="" copy %ALTERNATE_OPTIONS% absl\base\options.h
+IF DEFINED ALTERNATE_OPTIONS copy %ALTERNATE_OPTIONS% absl\base\options.h
 
 :: To upgrade Bazel, first download a new binary from
 :: https://github.com/bazelbuild/bazel/releases and copy it to
@@ -49,11 +49,11 @@
 :: TODO(absl-team): Remove -Wno-microsoft-cast
 %KOKORO_GFILE_DIR%\bazel-9.1.0-windows-x86_64.exe ^
   test ... ^
-  --compilation_mode=%COMPILATION_MODE% ^
+  --compilation_mode="%COMPILATION_MODE%" ^
   --compiler=clang-cl ^
   --copt=/WX ^
   --copt=-Wno-microsoft-cast ^
-  --cxxopt=/std:%STD% ^
+  --cxxopt="/std:%STD%" ^
   --define=absl=1 ^
   --enable_bzlmod=true ^
   --extra_execution_platforms=//:x64_windows-clang-cl ^
diff --git a/ci/windows_msvc_bazel.bat b/ci/windows_msvc_bazel.bat
index a4bd3d7..4cb0870 100755
--- a/ci/windows_msvc_bazel.bat
+++ b/ci/windows_msvc_bazel.bat
@@ -29,24 +29,24 @@
 :: Set the standard version, [c++17|c++latest]
 :: https://msdn.microsoft.com/en-us/library/mt490614.aspx
 :: The default is c++17 if not set on command line.
-IF "%STD%"=="" SET STD=c++17
+IF NOT DEFINED STD SET STD=c++17
 
 :: Set the compilation_mode (fastbuild|opt|dbg)
 :: https://docs.bazel.build/versions/master/user-manual.html#flag--compilation_mode
 :: The default is fastbuild
-IF "%COMPILATION_MODE%"=="" SET COMPILATION_MODE=fastbuild
+IF NOT DEFINED COMPILATION_MODE SET COMPILATION_MODE=fastbuild
 
 :: Copy the alternate option file, if specified.
-IF NOT "%ALTERNATE_OPTIONS%"=="" copy %ALTERNATE_OPTIONS% absl\base\options.h
+IF DEFINED ALTERNATE_OPTIONS copy %ALTERNATE_OPTIONS% absl\base\options.h
 
 :: To upgrade Bazel, first download a new binary from
 :: https://github.com/bazelbuild/bazel/releases and copy it to
 :: /google/data/rw/teams/absl/kokoro/windows.
 "%KOKORO_GFILE_DIR%\bazel-9.1.0-windows-x86_64.exe" ^
   test ... ^
-  --compilation_mode=%COMPILATION_MODE% ^
+  --compilation_mode="%COMPILATION_MODE%" ^
   --copt=/WX ^
-  --cxxopt=/std:%STD% ^
+  --cxxopt="/std:%STD%" ^
   --define=absl=1 ^
   --enable_bzlmod=true ^
   --keep_going ^
diff --git a/ci/windows_msvc_cmake.bat b/ci/windows_msvc_cmake.bat
index 6fbc324..a35dcec 100755
--- a/ci/windows_msvc_cmake.bat
+++ b/ci/windows_msvc_cmake.bat
@@ -40,6 +40,17 @@
 SET CMAKE_BUILD_PARALLEL_LEVEL=16
 SET CTEST_PARALLEL_LEVEL=16
 
+:: Set the standard version, [c++17|c++latest]
+:: https://msdn.microsoft.com/en-us/library/mt490614.aspx
+:: The default is c++17 if not set on command line.
+IF NOT DEFINED STD SET STD=17
+
+IF NOT DEFINED ABSL_CMAKE_BUILD_SHARED SET ABSL_CMAKE_BUILD_SHARED=OFF
+
+IF NOT DEFINED ABSL_CMAKE_BUILD_TYPE SET ABSL_CMAKE_BUILD_TYPE=Debug
+
+IF NOT DEFINED ABSL_CMAKE_GENERATOR SET "ABSL_CMAKE_GENERATOR=Visual Studio 17 2022"
+
 :: Change directory to the root of the project.
 CD %~dp0\..
 if %errorlevel% neq 0 EXIT /B 1
@@ -53,9 +64,9 @@
 
 %CMAKE_BIN% ^
   -DABSL_BUILD_TESTING=ON ^
-  -DABSL_GOOGLETEST_DOWNLOAD_URL=%ABSL_GOOGLETEST_DOWNLOAD_URL% ^
-  -DBUILD_SHARED_LIBS=%ABSL_CMAKE_BUILD_SHARED% ^
-  -DCMAKE_CXX_STANDARD=%ABSL_CMAKE_CXX_STANDARD% ^
+  -DABSL_GOOGLETEST_DOWNLOAD_URL="%ABSL_GOOGLETEST_DOWNLOAD_URL%" ^
+  -DBUILD_SHARED_LIBS="%ABSL_CMAKE_BUILD_SHARED%" ^
+  -DCMAKE_CXX_STANDARD="%STD%" ^
   -G "%ABSL_CMAKE_GENERATOR%" ^
   ..
 IF %errorlevel% neq 0 EXIT /B 1