// Copyright 2021 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Note that most structs and macros in this file have 1:1 C++ counterparts in // the corresponding .h file. #include 'src/objects/swiss-hash-table-helpers.h' namespace swiss_table { const kGroupWidth: constexpr int32 generates 'swiss_table::Group::kWidth'; const kUseSIMD: constexpr bool generates 'swiss_table::Group::kWidth == 16'; namespace ctrl { const kEmpty: constexpr uint8 generates 'static_cast(swiss_table::Ctrl::kEmpty)'; const kDeleted: constexpr uint8 generates 'static_cast(swiss_table::Ctrl::kDeleted)'; } const kH2Bits: constexpr int32 generates 'swiss_table::kH2Bits'; const kH2Mask: constexpr uint32 generates '((1 << swiss_table::kH2Bits) - 1)'; extern macro LoadSwissNameDictionaryCtrlTableGroup(intptr): uint64; // Counterpart to swiss_table::ProbeSequence in C++ implementation. struct ProbeSequence { macro Next(): void { this.index = this.index + Unsigned(FromConstexpr(kGroupWidth)); this.offset = (this.offset + this.index) & this.mask; } macro Offset(index: int32): uint32 { return (this.offset + Unsigned(index)) & this.mask; } mask: uint32; offset: uint32; index: uint32; } macro ClearLowestSetBit(value: T): T { return value & (value - FromConstexpr(1)); } const kByteMaskShift: uint64 = 3; // Counterpart to swiss_table::BitMask, as used by // swiss_table::GroupPortableImpl in C++ implementation. struct ByteMask { macro HasBitsSet(): bool { return this.mask != FromConstexpr(0); } macro LowestBitSet(): int32 { return Convert( CountTrailingZeros64(this.mask) >> Signed(kByteMaskShift)); } // Counterpart to operator++() in C++ version. macro ClearLowestSetBit(): void { this.mask = ClearLowestSetBit(this.mask); } mask: uint64; } // Counterpart to swiss_table::BitMask, as used by // swiss_table::GroupSse2Impl in C++ implementation. struct BitMask { macro HasBitsSet(): bool { return this.mask != FromConstexpr(0); } macro LowestBitSet(): int32 { return Convert(CountTrailingZeros32(this.mask)); } // Counterpart to operator++() in C++ version. macro ClearLowestSetBit(): void { this.mask = ClearLowestSetBit(this.mask); } mask: uint32; } macro H1(hash: uint32): uint32 { return hash >>> Unsigned(FromConstexpr(kH2Bits)); } macro H2(hash: uint32): uint32 { return hash & kH2Mask; } const kLsbs: constexpr uint64 generates 'swiss_table::GroupPortableImpl::kLsbs'; const kMsbs: constexpr uint64 generates 'swiss_table::GroupPortableImpl::kMsbs'; // Counterpart to swiss_table::GroupPortableImpl in C++. struct GroupPortableImpl { macro Match(h2: uint32): ByteMask { const x = Word64Xor(this.ctrl, (kLsbs * Convert(h2))); const result = (x - kLsbs) & ~x & kMsbs; return ByteMask{mask: result}; } macro MatchEmpty(): ByteMask { const result = ((this.ctrl & (~this.ctrl << 6)) & kMsbs); return ByteMask{mask: result}; } const ctrl: uint64; } // Counterpart to swiss_table::GroupSse2Impl in C++. Note that the name is // chosen for consistency, this struct is not actually SSE-specific. struct GroupSse2Impl { macro Match(h2: uint32): BitMask { // Fill 16 8-bit lanes with |h2|: const searchPattern = I8x16Splat(Signed(h2)); // Create a 128 bit mask such that in each of the 16 8-bit lanes, the MSB // indicates whether or not the corresponding lanes of |this.ctrl| and // |searchPattern| have the same value: const matches128 = I8x16Eq(searchPattern, this.ctrl); // Turn the 128 bit mask into a 32 bit one, by turning the MSB of the i-th // lane into the i-th bit in the output mask: const matches32 = Unsigned(I8x16BitMask(matches128)); return BitMask{mask: matches32}; } macro MatchEmpty(): BitMask { // TODO(v8:11330) The C++ implementation in // swiss_table::GroupSse2Impl::MatchEmpty utilizes a special trick that is // possible due to kEmpty being -128 and allows shaving off one SSE // instruction. This depends on having access to _mm_cmpeq_epi8 aka PCMPEQB, // which the V8 backend currently doesn't expose. // Fill 16 8-bit lanes with |kEmpty|: const searchPattern = I8x16Splat(Convert(FromConstexpr(ctrl::kEmpty))); // Create a 128 bit mask such that in each of the 16 8-bit lanes, the MSB // indicates whether or not the corresponding lanes of |this.ctrl| contains // |kEmpty|: const matches128 = I8x16Eq(searchPattern, this.ctrl); // Turn the 128 bit mask into a 32 bit one, by turning the MSB of the i-th // lane into the i-th bit in the output mask: const matches32 = Unsigned(I8x16BitMask(matches128)); return BitMask{mask: matches32}; } const ctrl: I8X16; } struct GroupPortableLoader { macro LoadGroup(ctrlPtr: intptr): GroupPortableImpl { return GroupPortableImpl{ ctrl: LoadSwissNameDictionaryCtrlTableGroup(ctrlPtr) }; } } struct GroupSse2Loader { macro LoadGroup(ctrlPtr: intptr): GroupSse2Impl { return GroupSse2Impl{ctrl: Convert(LoadSimd128(ctrlPtr))}; } } }