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`.