• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2021 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Note that most structs and macros in this file have 1:1 C++ counterparts in
6// the corresponding .h file.
7
8#include 'src/objects/swiss-hash-table-helpers.h'
9
10namespace swiss_table {
11
12const kGroupWidth:
13    constexpr int32 generates 'swiss_table::Group::kWidth';
14
15const kUseSIMD:
16    constexpr bool generates 'swiss_table::Group::kWidth == 16';
17
18namespace ctrl {
19const kEmpty: constexpr uint8
20    generates 'static_cast<uint8_t>(swiss_table::Ctrl::kEmpty)';
21
22const kDeleted: constexpr uint8
23    generates 'static_cast<uint8_t>(swiss_table::Ctrl::kDeleted)';
24}
25
26const kH2Bits: constexpr int32 generates 'swiss_table::kH2Bits';
27const kH2Mask:
28    constexpr uint32 generates '((1 << swiss_table::kH2Bits) - 1)';
29
30extern macro LoadSwissNameDictionaryCtrlTableGroup(intptr): uint64;
31
32// Counterpart to swiss_table::ProbeSequence in C++ implementation.
33struct ProbeSequence {
34  macro Next(): void {
35    this.index = this.index + Unsigned(FromConstexpr<int32>(kGroupWidth));
36    this.offset = (this.offset + this.index) & this.mask;
37  }
38
39  macro Offset(index: int32): uint32 {
40    return (this.offset + Unsigned(index)) & this.mask;
41  }
42
43  mask: uint32;
44  offset: uint32;
45  index: uint32;
46}
47
48macro ClearLowestSetBit<T: type>(value: T): T {
49  return value & (value - FromConstexpr<T>(1));
50}
51
52const kByteMaskShift: uint64 = 3;
53
54// Counterpart to swiss_table::BitMask<uint64_t, kWidth, 3>, as used by
55// swiss_table::GroupPortableImpl in C++ implementation.
56struct ByteMask {
57  macro HasBitsSet(): bool {
58    return this.mask != FromConstexpr<uint64>(0);
59  }
60
61  macro LowestBitSet(): int32 {
62    return Convert<int32>(
63        CountTrailingZeros64(this.mask) >> Signed(kByteMaskShift));
64  }
65
66  // Counterpart to operator++() in C++ version.
67  macro ClearLowestSetBit(): void {
68    this.mask = ClearLowestSetBit<uint64>(this.mask);
69  }
70
71  mask: uint64;
72}
73
74// Counterpart to swiss_table::BitMask<uint32t, kWidth, 0>, as used by
75// swiss_table::GroupSse2Impl in C++ implementation.
76struct BitMask {
77  macro HasBitsSet(): bool {
78    return this.mask != FromConstexpr<uint32>(0);
79  }
80
81  macro LowestBitSet(): int32 {
82    return Convert<int32>(CountTrailingZeros32(this.mask));
83  }
84
85  // Counterpart to operator++() in C++ version.
86  macro ClearLowestSetBit(): void {
87    this.mask = ClearLowestSetBit<uint32>(this.mask);
88  }
89
90  mask: uint32;
91}
92
93macro H1(hash: uint32): uint32 {
94  return hash >>> Unsigned(FromConstexpr<int32>(kH2Bits));
95}
96
97macro H2(hash: uint32): uint32 {
98  return hash & kH2Mask;
99}
100
101const kLsbs: constexpr uint64
102    generates 'swiss_table::GroupPortableImpl::kLsbs';
103const kMsbs: constexpr uint64
104    generates 'swiss_table::GroupPortableImpl::kMsbs';
105
106// Counterpart to swiss_table::GroupPortableImpl in C++.
107struct GroupPortableImpl {
108  macro Match(h2: uint32): ByteMask {
109    const x = Word64Xor(this.ctrl, (kLsbs * Convert<uint64>(h2)));
110    const result = (x - kLsbs) & ~x & kMsbs;
111    return ByteMask{mask: result};
112  }
113
114  macro MatchEmpty(): ByteMask {
115    const result = ((this.ctrl & (~this.ctrl << 6)) & kMsbs);
116    return ByteMask{mask: result};
117  }
118
119  const ctrl: uint64;
120}
121
122// Counterpart to swiss_table::GroupSse2Impl in C++. Note that the name is
123// chosen for consistency, this struct is not actually SSE-specific.
124struct GroupSse2Impl {
125  macro Match(h2: uint32): BitMask {
126    // Fill 16 8-bit lanes with |h2|:
127    const searchPattern = I8x16Splat(Signed(h2));
128    // Create a 128 bit mask such that in each of the 16 8-bit lanes, the MSB
129    // indicates whether or not the corresponding lanes of |this.ctrl| and
130    // |searchPattern| have the same value:
131    const matches128 = I8x16Eq(searchPattern, this.ctrl);
132    // Turn the 128 bit mask into a 32 bit one, by turning the MSB of the i-th
133    // lane into the i-th bit in the output mask:
134    const matches32 = Unsigned(I8x16BitMask(matches128));
135    return BitMask{mask: matches32};
136  }
137
138  macro MatchEmpty(): BitMask {
139    // TODO(v8:11330) The C++ implementation in
140    // swiss_table::GroupSse2Impl::MatchEmpty utilizes a special trick that is
141    // possible due to kEmpty being -128 and allows shaving off one SSE
142    // instruction. This depends on having access to _mm_cmpeq_epi8 aka PCMPEQB,
143    // which the V8 backend currently doesn't expose.
144
145    // Fill 16 8-bit lanes with |kEmpty|:
146    const searchPattern =
147        I8x16Splat(Convert<int32>(FromConstexpr<uint8>(ctrl::kEmpty)));
148    // Create a 128 bit mask such that in each of the 16 8-bit lanes, the MSB
149    // indicates whether or not the corresponding lanes of |this.ctrl| contains
150    // |kEmpty|:
151    const matches128 = I8x16Eq(searchPattern, this.ctrl);
152    // Turn the 128 bit mask into a 32 bit one, by turning the MSB of the i-th
153    // lane into the i-th bit in the output mask:
154    const matches32 = Unsigned(I8x16BitMask(matches128));
155    return BitMask{mask: matches32};
156  }
157
158  const ctrl: I8X16;
159}
160
161struct GroupPortableLoader {
162  macro LoadGroup(ctrlPtr: intptr): GroupPortableImpl {
163    return GroupPortableImpl{
164      ctrl: LoadSwissNameDictionaryCtrlTableGroup(ctrlPtr)
165    };
166  }
167}
168
169struct GroupSse2Loader {
170  macro LoadGroup(ctrlPtr: intptr): GroupSse2Impl {
171    return GroupSse2Impl{ctrl: Convert<I8X16>(LoadSimd128(ctrlPtr))};
172  }
173}
174}
175