Optimize raw_hash_set destruction for empty allocators.

Introduce DestructSooEmptyAlloc and DestructNonSooEmptyAlloc to handle destruction when the allocator is empty. This avoids the need to retrieve the allocator's address from the container, reducing code size for the common case of empty allocators.

```
       5c: leaq  -0x30(%rbp), %rdi
       60: movq  (%rip), %rsi           # reloc: DtorPolicy::R<...>::i
-      67: movq  %rdi, %rdx             # <--- 3 bytes eliminated!
-      6a: callq Destruct...
+      67: callq DestructSooEmptyAlloc
```

PiperOrigin-RevId: 976708404
Change-Id: Id4a5e311527de3a80e7f7f3c137fd812535ee9c5
diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc
index ba95254..c7fde55 100644
--- a/absl/container/internal/raw_hash_set.cc
+++ b/absl/container/internal/raw_hash_set.cc
@@ -913,7 +913,13 @@
   DeallocBackingArray(c, policy, alloc);
 }
 
-void DestructNonSoo(CommonFields& c, const DtorPolicy& __restrict policy,
+void DestructSooEmptyAlloc(CommonFields& c,
+                           const DtorPolicy& __restrict policy) {
+  DestructSoo(c, policy, /*alloc=*/&c);
+}
+
+void DestructNonSoo(CommonFields& c,
+                    const DtorPolicy& __restrict policy,
                     void* alloc) {
   ABSL_SWISSTABLE_ASSERT(c.capacity() > 0);
   if (policy.destroy_slot != nullptr) {
@@ -929,6 +935,11 @@
   DeallocBackingArray(c, policy, alloc);
 }
 
+void DestructNonSooEmptyAlloc(CommonFields& c,
+                              const DtorPolicy& __restrict policy) {
+  DestructNonSoo(c, policy, /*alloc=*/&c);
+}
+
 namespace {
 
 // Iterates over full slots in old table, finds new positions for them and
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h
index ad13057..9a15c2f 100644
--- a/absl/container/internal/raw_hash_set.h
+++ b/absl/container/internal/raw_hash_set.h
@@ -2197,10 +2197,12 @@
 // REQUIRES: !c.is_small || !c.empty()
 // REQUIRES: !c.is_small || policy.destroy_slot != nullptr
 void DestructSoo(CommonFields& c, const DtorPolicy& policy, void* alloc);
+void DestructSooEmptyAlloc(CommonFields& c, const DtorPolicy& policy);
 
 // Destructs all elements and deallocates the backing array for non-SOO tables.
 // REQUIRES: c.capacity > 0.
 void DestructNonSoo(CommonFields& c, const DtorPolicy& policy, void* alloc);
+void DestructNonSooEmptyAlloc(CommonFields& c, const DtorPolicy& policy);
 
 // Type-erased versions of raw_hash_set::erase_meta_only_{small,large}.
 void EraseMetaOnlySmall(CommonFields& c, bool soo_enabled, size_t slot_size);
@@ -3534,10 +3536,18 @@
           (PolicyTraits::template destroy_is_trivial<Alloc>() || empty())) {
         return;
       }
-      DestructSoo(common(), GetDtorPolicy(), &char_alloc_ref());
+      if constexpr (std::is_empty_v<Alloc>) {
+        DestructSooEmptyAlloc(common(), GetDtorPolicy());
+      } else {
+        DestructSoo(common(), GetDtorPolicy(), &char_alloc_ref());
+      }
     } else {
       if (capacity() == 0) return;
-      DestructNonSoo(common(), GetDtorPolicy(), &char_alloc_ref());
+      if constexpr (std::is_empty_v<Alloc>) {
+        DestructNonSooEmptyAlloc(common(), GetDtorPolicy());
+      } else {
+        DestructNonSoo(common(), GetDtorPolicy(), &char_alloc_ref());
+      }
     }
   }