Use bit-shift extraction in `HashtableInlineData::read_byte` on non-x86 targets.

Reading `capacity` and `seed` through `reinterpret_cast<const unsigned char*>(&data_)[index]` allows x86 compilers to emit `movzbl` byte loads without masking on the lookup critical path. On AArch64, however, accessing `data_` via narrow byte loads prevents the compiler from coalescing the `inline_data_` and `control_` loads into a single `ldp` instruction, forces separate `ldrb` + `ldrb` + `ldr` loads when `empty()` or `size()` also inspects `data_`, and causes store-to-load forwarding stalls when a byte load follows a 64-bit store to `data_`.

Restrict the `unsigned char` byte-load path in `HashtableInlineData::read_byte` to x86 targets and extract the requested byte via bit shifts on `data_` on other architectures. This preserves byte-identical x86 codegen while restoring wide `ldr`/`ldp` loads and `ubfx` extraction on AArch64 without changing the bit layout of `HashtableInlineData::data_`.

PiperOrigin-RevId: 1003158224
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h
index 83b547e..4d76862 100644
--- a/absl/container/internal/raw_hash_set.h
+++ b/absl/container/internal/raw_hash_set.h
@@ -964,12 +964,21 @@
 #endif
 
   // Returns the `index`-th byte of `data_`. Accessing the storage through
-  // `unsigned char` is always allowed by the aliasing rules. Note: the reason
-  // we read the data this way rather than using bit manipulations as with other
-  // fields is that it results in better generated code (we get movzbl to read
-  // the seed this way).
+  // `unsigned char` is always allowed by the aliasing rules. Note: on x86, the
+  // reason we read the data this way rather than using bit manipulations as
+  // with other fields is that it results in better generated code (we get
+  // movzbl to read the seed this way). On other architectures (such as
+  // AArch64), byte loads prevent load-pair (ldp) of `inline_data_` with
+  // `control_` and introduce narrow loads after 64-bit stores to `data_`, so we
+  // use bit shifts on the wide word instead.
   uint8_t read_byte(size_t index) const {
+#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || \
+    defined(_M_IX86)
     return reinterpret_cast<const unsigned char*>(&data_)[index];
+#else
+    return static_cast<uint8_t>(little_endian::FromHost64(data_) >>
+                                (8 * index));
+#endif
   }
 
   // Returns the bits of `data_` that encode `capacity`.