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