blob: 0d8543d5d1387adc5dc936cfdf9df0c8fdcdf7c0 [file] [log] [blame]
// 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<uint8_t>(swiss_table::Ctrl::kEmpty)';
const kDeleted: constexpr uint8
generates 'static_cast<uint8_t>(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<int32>(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<T: type>(value: T): T {
return value & (value - FromConstexpr<T>(1));
}
const kByteMaskShift: uint64 = 3;
// Counterpart to swiss_table::BitMask<uint64_t, kWidth, 3>, as used by
// swiss_table::GroupPortableImpl in C++ implementation.
struct ByteMask {
macro HasBitsSet(): bool {
return this.mask != FromConstexpr<uint64>(0);
}
macro LowestBitSet(): int32 {
return Convert<int32>(
CountTrailingZeros64(this.mask) >> Signed(kByteMaskShift));
}
// Counterpart to operator++() in C++ version.
macro ClearLowestSetBit(): void {
this.mask = ClearLowestSetBit<uint64>(this.mask);
}
mask: uint64;
}
// Counterpart to swiss_table::BitMask<uint32t, kWidth, 0>, as used by
// swiss_table::GroupSse2Impl in C++ implementation.
struct BitMask {
macro HasBitsSet(): bool {
return this.mask != FromConstexpr<uint32>(0);
}
macro LowestBitSet(): int32 {
return Convert<int32>(CountTrailingZeros32(this.mask));
}
// Counterpart to operator++() in C++ version.
macro ClearLowestSetBit(): void {
this.mask = ClearLowestSetBit<uint32>(this.mask);
}
mask: uint32;
}
macro H1(hash: uint32): uint32 {
return hash >>> Unsigned(FromConstexpr<int32>(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<uint64>(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<int32>(FromConstexpr<uint8>(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<I8X16>(LoadSimd128(ctrlPtr))};
}
}
}