Reland "[dict-proto] SIMD support for SwissNameDictionary in Torque"

This is a reland of 856e8577e3
The diff compared to the first attempt is that the tests that
require SSSE3/AVX are not run when these CPU features are not
available.

Original change's description:
> [dict-proto] SIMD support for SwissNameDictionary in Torque
>
> This CL adds a Torque-counterpart for swiss_table::GroupSse2Impl in
> Torque. This allows the Torque version of SwissNameDictionary to use
> SSE for lookups, rather than needing to bailout to the runtime on
> x64/ia32.
>
> Bug: v8:11330
> Change-Id: I74e3f97c460a8b89031016967ec0e545265016a9
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2787485
> Reviewed-by: Igor Sheludko <ishell@chromium.org>
> Reviewed-by: Santiago Aboy Solanes <solanes@chromium.org>
> Reviewed-by: Zhi An Ng <zhin@chromium.org>
> Commit-Queue: Igor Sheludko <ishell@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#73727}

Bug: v8:11330
Cq-Include-Trybots: luci.v8.try:v8_linux_optional_rel_ng
Change-Id: Ibfa5ae5a39333778ea0d0406d5ea4ad683ad0dbe
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2794431
Reviewed-by: Santiago Aboy Solanes <solanes@chromium.org>
Reviewed-by: Marja Hölttä <marja@chromium.org>
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#73740}
This commit is contained in:
Igor Sheludko 2021-03-31 14:10:07 +02:00 committed by Commit Bot
parent 4baf07a769
commit 4cd6ad9ef2
13 changed files with 230 additions and 48 deletions

View File

@ -113,6 +113,9 @@ type bool generates 'TNode<BoolT>' constexpr 'bool';
type bint generates 'TNode<BInt>' constexpr 'BInt';
type string constexpr 'const char*';
type Simd128 generates 'TNode<Simd128T>';
type I8X16 extends Simd128 generates 'TNode<I8x16T>';
// Represents a std::function which produces the generated TNode type of T.
// Useful for passing values to and from CSA code that uses LazyNode<T>, which
// is a typedef for std::function<TNode<T>()>. Can be created with %MakeLazy and
@ -917,7 +920,7 @@ extern operator '*' macro ConstexprInt31Mul(
extern operator '-' macro Int32Sub(int16, int16): int32;
extern operator '-' macro Int32Sub(uint16, uint16): int32;
extern operator '-' macro Int32Sub(int32, int32): int32;
extern operator '-' macro UInt32Sub(uint32, uint32): uint32;
extern operator '-' macro Uint32Sub(uint32, uint32): uint32;
extern operator '*' macro Int32Mul(int32, int32): int32;
extern operator '*' macro Uint32Mul(uint32, uint32): uint32;
extern operator '/' macro Int32Div(int32, int32): int32;
@ -1050,6 +1053,7 @@ operator '==' macro PromiseStateEquals(
}
extern macro CountLeadingZeros64(uint64): int64;
extern macro CountTrailingZeros32(uint32): int32;
extern macro CountTrailingZeros64(uint64): int64;
extern macro TaggedIsSmi(Object): bool;
@ -1845,3 +1849,8 @@ extern operator '[]' macro LoadWeakFixedArrayElement(
const kNoHashSentinel:
constexpr int32 generates 'PropertyArray::kNoHashSentinel';
extern macro LoadNameHash(Name): uint32;
extern macro LoadSimd128(intptr): Simd128;
extern macro I8x16BitMask(I8X16): int32;
extern macro I8x16Eq(I8X16, I8X16): I8X16;
extern macro I8x16Splat(int32): I8X16;

View File

@ -335,3 +335,6 @@ Convert<PromiseState, int32>(s: int32): PromiseState {
Convert<ScopeFlags, Smi>(s: Smi): ScopeFlags {
return %RawDownCast<ScopeFlags>(Unsigned(SmiToInt32(s)));
}
Convert<I8X16, Simd128>(s: Simd128): I8X16 {
return %RawDownCast<I8X16>(s);
}

View File

@ -1137,6 +1137,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
Map::kConstructorOrBackPointerOrNativeContextOffset);
}
TNode<Simd128T> LoadSimd128(TNode<IntPtrT> ptr) {
return Load<Simd128T>(ptr);
}
// Reference is the CSA-equivalent of a Torque reference value, representing
// an inner pointer into a HeapObject.
//

View File

@ -110,6 +110,16 @@ struct BoolT : Word32T {};
template <class T1, class T2>
struct PairT {};
struct Simd128T : UntaggedT {
static const MachineRepresentation kMachineRepresentation =
MachineRepresentation::kSimd128;
static constexpr MachineType kMachineType = MachineType::Simd128();
};
struct I8x16T : Simd128T {};
struct I16x8T : Simd128T {};
struct I32x2T : Simd128T {};
inline constexpr MachineType CommonMachineType(MachineType type1,
MachineType type2) {
return (type1 == type2) ? type1

View File

@ -270,6 +270,7 @@ class CodeAssemblerParameterizedLabel;
V(Float64Min, Float64T, Float64T, Float64T) \
V(Float64InsertLowWord32, Float64T, Float64T, Word32T) \
V(Float64InsertHighWord32, Float64T, Float64T, Word32T) \
V(I8x16Eq, I8x16T, I8x16T, I8x16T) \
V(IntPtrAdd, WordT, WordT, WordT) \
V(IntPtrSub, WordT, WordT, WordT) \
V(IntPtrMul, WordT, WordT, WordT) \
@ -374,6 +375,8 @@ TNode<Float64T> Float64Add(TNode<Float64T> a, TNode<Float64T> b);
V(Word32BitwiseNot, Word32T, Word32T) \
V(WordNot, WordT, WordT) \
V(Word64Not, Word64T, Word64T) \
V(I8x16BitMask, Int32T, I8x16T) \
V(I8x16Splat, I8x16T, Int32T) \
V(Int32AbsWithOverflow, PAIR_TYPE(Int32T, BoolT), Int32T) \
V(Int64AbsWithOverflow, PAIR_TYPE(Int64T, BoolT), Int64T) \
V(IntPtrAbsWithOverflow, PAIR_TYPE(IntPtrT, BoolT), IntPtrT) \

View File

@ -272,6 +272,7 @@ class MachineRepresentationInferrer {
case IrOpcode::kFloat64ExtractLowWord32:
case IrOpcode::kFloat64ExtractHighWord32:
case IrOpcode::kWord32Popcnt:
case IrOpcode::kI8x16BitMask:
MACHINE_UNOP_32_LIST(LABEL)
MACHINE_BINOP_32_LIST(LABEL) {
representation_vector_[node->id()] =
@ -323,6 +324,8 @@ class MachineRepresentationInferrer {
break;
case IrOpcode::kI32x4ReplaceLane:
case IrOpcode::kI32x4Splat:
case IrOpcode::kI8x16Splat:
case IrOpcode::kI8x16Eq:
representation_vector_[node->id()] =
MachineRepresentation::kSimd128;
break;
@ -445,6 +448,7 @@ class MachineRepresentationChecker {
case IrOpcode::kI32x4ExtractLane:
case IrOpcode::kI16x8ExtractLaneU:
case IrOpcode::kI16x8ExtractLaneS:
case IrOpcode::kI8x16BitMask:
case IrOpcode::kI8x16ExtractLaneU:
case IrOpcode::kI8x16ExtractLaneS:
CheckValueInputRepresentationIs(node, 0,
@ -456,8 +460,16 @@ class MachineRepresentationChecker {
CheckValueInputForInt32Op(node, 1);
break;
case IrOpcode::kI32x4Splat:
case IrOpcode::kI8x16Splat:
CheckValueInputForInt32Op(node, 0);
break;
case IrOpcode::kI8x16Eq:
CheckValueInputRepresentationIs(node, 0,
MachineRepresentation::kSimd128);
CheckValueInputRepresentationIs(node, 1,
MachineRepresentation::kSimd128);
break;
#define LABEL(opcode) case IrOpcode::k##opcode:
case IrOpcode::kChangeInt32ToTagged:
case IrOpcode::kChangeUint32ToTagged:

View File

@ -881,6 +881,12 @@ class V8_EXPORT_PRIVATE RawMachineAssembler {
Node* I16x8Splat(Node* a) { return AddNode(machine()->I16x8Splat(), a); }
Node* I8x16Splat(Node* a) { return AddNode(machine()->I8x16Splat(), a); }
Node* I8x16BitMask(Node* a) { return AddNode(machine()->I8x16BitMask(), a); }
Node* I8x16Eq(Node* a, Node* b) {
return AddNode(machine()->I8x16Eq(), a, b);
}
// Stack operations.
Node* LoadFramePointer() { return AddNode(machine()->LoadFramePointer()); }
Node* LoadParentFramePointer() {

View File

@ -349,11 +349,29 @@ struct GroupPortableImpl {
};
// Determine which Group implementation SwissNameDictionary uses.
#if defined(V8_ENABLE_SWISS_NAME_DICTIONARY) && DEBUG
// TODO(v8:11388) If v8_enable_swiss_name_dictionary is enabled, we are supposed
// to use SwissNameDictionary as the dictionary backing store. If we want to use
// the SIMD version of SwissNameDictionary, that would require us to compile SSE
// instructions into the snapshot that exceed the minimum requirements for V8
// SSE support. Therefore, this fails a DCHECK. However, given the experimental
// nature of v8_enable_swiss_name_dictionary mode, we only except this to be run
// by developers/bots, that always have the necessary instructions. This means
// that if v8_enable_swiss_name_dictionary is enabled and debug mode isn't, we
// ignore the DCHECK that would fail in debug mode. However, if both
// v8_enable_swiss_name_dictionary and debug mode are enabled, we must fallback
// to the non-SSE implementation. Given that V8 requires SSE2, there should be a
// solution that doesn't require the workaround present here. Instead, the
// backend should only use SSE2 when compiling the SIMD version of
// SwissNameDictionary into the builtin.
using Group = GroupPortableImpl;
#else
#if SWISS_TABLE_HAVE_SSE2
using Group = GroupSse2Impl;
#else
using Group = GroupPortableImpl;
#endif
#endif
#undef SWISS_TABLE_HAVE_SSE2
#undef SWISS_TABLE_HAVE_SSE3

View File

@ -45,6 +45,10 @@ struct ProbeSequence {
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
@ -61,12 +65,31 @@ struct ByteMask {
// Counterpart to operator++() in C++ version.
macro ClearLowestSetBit() {
this.mask = this.mask & (this.mask - FromConstexpr<uint64>(1));
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() {
this.mask = ClearLowestSetBit<uint32>(this.mask);
}
mask: uint32;
}
macro H1(hash: uint32): uint32 {
return hash >>> Unsigned(FromConstexpr<int32>(kH2Bits));
}
@ -80,6 +103,7 @@ const kLsbs: constexpr uint64
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)));
@ -95,6 +119,45 @@ struct GroupPortableImpl {
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{
@ -102,4 +165,10 @@ struct GroupPortableLoader {
};
}
}
struct GroupSse2Loader {
macro LoadGroup(ctrlPtr: intptr): GroupSse2Impl {
return GroupSse2Impl{ctrl: Convert<I8X16>(LoadSimd128(ctrlPtr))};
}
}
}

View File

@ -28,12 +28,6 @@ const kMax2ByteMetaTableCapacity: constexpr int32
const kNotFoundSentinel:
constexpr int32 generates 'SwissNameDictionary::kNotFoundSentinel';
extern macro LoadSwissNameDictionaryNumberOfElements(
SwissNameDictionary, intptr): intptr;
extern macro LoadSwissNameDictionaryNumberOfDeletedElements(
SwissNameDictionary, intptr): intptr;
extern macro LoadSwissNameDictionaryKey(SwissNameDictionary, intptr): Name;
extern macro StoreSwissNameDictionaryKeyAndValue(
@ -287,14 +281,8 @@ macro SwissNameDictionaryDelete(table: SwissNameDictionary, entry: intptr)
@export
macro SwissNameDictionaryFindEntrySIMD(table: SwissNameDictionary, key: Name):
never labels Found(intptr), NotFound {
// TODO(v8:11330) Not implemented in Torque, yet, doing runtime call
// instead.
const res = runtime::SwissTableFindEntry(kNoContext, table, key);
if (res == kNotFoundSentinel) {
goto NotFound;
} else {
goto Found(Convert<intptr>(res));
}
FindEntry<GroupSse2Loader>(table, key)
otherwise Found, NotFound;
}
@export
@ -317,26 +305,8 @@ Found(intptr),
macro SwissNameDictionaryAddSIMD(
table: SwissNameDictionary, key: Name, value: Object,
propertyDetails: uint8) labels Bailout {
// TODO(v8:11330) Not implemented in Torque, yet, doing runtime call
// instead. However, must bailout if the runtime call would allocate a new
// dictionary.
// Determine if bailout needed:
const capacity = Convert<intptr>(table.capacity);
const maxUsable = SwissNameDictionaryMaxUsableCapacity(capacity);
// Doing two independent accesses to the meta table here (repeating the
// branching), rather than combining the accesses. Accepting that due to
// the fact that this is a slow placeholder until the SIMD version
// replaces it.
const nof = LoadSwissNameDictionaryNumberOfElements(table, capacity);
const nod = LoadSwissNameDictionaryNumberOfDeletedElements(table, capacity);
const used = nof + nod;
if (used >= maxUsable) {
goto Bailout;
}
runtime::SwissTableAdd(
kNoContext, table, key, value,
Convert<Smi>(Convert<int32>(propertyDetails)));
Add<GroupSse2Loader>(table, key, value, propertyDetails)
otherwise Bailout;
}
@export

View File

@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "src/codegen/code-stub-assembler.h"
#include "src/codegen/cpu-features.h"
#include "src/objects/objects-inl.h"
#include "src/objects/swiss-name-dictionary-inl.h"
#include "test/cctest/compiler/code-assembler-tester.h"
@ -14,12 +15,37 @@ namespace v8 {
namespace internal {
namespace test_swiss_hash_table {
// The non-SIMD SwissNameDictionary implementation requires 64 bit integer
// operations, which CSA/Torque don't offer on 32 bit platforms. Therefore, we
// cannot run the CSA version of the tests on 32 bit platforms. The only
// exception is IA32, where we can use SSE and don't need 64 bit integers.
// TODO(v8:11330) The Torque SIMD implementation is not specific to SSE (like
// the C++ one), but works on other platforms. It should be possible to create a
// workaround where on 32 bit, non-IA32 platforms we use the "portable", non-SSE
// implementation on the C++ side (which uses a group size of 8) and create a
// special version of the SIMD Torque implementation that works for group size 8
// instead of 16.
#if V8_TARGET_ARCH_64_BIT || V8_TARGET_ARCH_IA32
// Executes tests by executing CSA/Torque versions of dictionary operations.
// See RuntimeTestRunner for description of public functions.
class CSATestRunner {
public:
CSATestRunner(Isolate* isolate, int initial_capacity, KeyCache& keys);
// TODO(v8:11330): Remove once CSA implementation has a fallback for
// non-SSSE3/AVX configurations.
static bool IsEnabled() {
#if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32
CpuFeatures::SupportedFeatures();
return CpuFeatures::IsSupported(CpuFeature::AVX) ||
CpuFeatures::IsSupported(CpuFeature::SSSE3);
#else
// Other 64-bit architectures always support the required operations.
return true;
#endif
}
void Add(Handle<Name> key, Handle<Object> value, PropertyDetails details);
InternalIndex FindEntry(Handle<Name> key);
void Put(InternalIndex entry, Handle<Object> new_value,
@ -237,6 +263,11 @@ void CSATestRunner::PrintTable() {
}
Handle<Code> CSATestRunner::create_find_entry(Isolate* isolate) {
// TODO(v8:11330): Remove once CSA implementation has a fallback for
// non-SSSE3/AVX configurations.
if (!IsEnabled()) {
return isolate->builtins()->builtin_handle(Builtins::kIllegal);
}
STATIC_ASSERT(kFindEntryParams == 2); // (table, key)
compiler::CodeAssemblerTester asm_tester(isolate, kFindEntryParams + 1);
CodeStubAssembler m(asm_tester.state());
@ -304,6 +335,11 @@ Handle<Code> CSATestRunner::create_put(Isolate* isolate) {
}
Handle<Code> CSATestRunner::create_delete(Isolate* isolate) {
// TODO(v8:11330): Remove once CSA implementation has a fallback for
// non-SSSE3/AVX configurations.
if (!IsEnabled()) {
return isolate->builtins()->builtin_handle(Builtins::kIllegal);
}
STATIC_ASSERT(kDeleteParams == 2); // (table, entry)
compiler::CodeAssemblerTester asm_tester(isolate, kDeleteParams + 1);
CodeStubAssembler m(asm_tester.state());
@ -324,6 +360,11 @@ Handle<Code> CSATestRunner::create_delete(Isolate* isolate) {
}
Handle<Code> CSATestRunner::create_add(Isolate* isolate) {
// TODO(v8:11330): Remove once CSA implementation has a fallback for
// non-SSSE3/AVX configurations.
if (!IsEnabled()) {
return isolate->builtins()->builtin_handle(Builtins::kIllegal);
}
STATIC_ASSERT(kAddParams == 4); // (table, key, value, details)
compiler::CodeAssemblerTester asm_tester(isolate, kAddParams + 1);
CodeStubAssembler m(asm_tester.state());
@ -411,18 +452,6 @@ void CSATestRunner::CheckAgainstReference() {
CHECK(table->EqualsForTesting(*reference_));
}
// The non-SIMD SwissNameDictionary implementation requires 64 bit integer
// operations, which CSA/Torque don't offer on 32 bit platforms. Therefore, we
// cannot run the CSA version of the tests on 32 bit platforms. The only
// exception is IA32, where we can use SSE and don't need 64 bit integers.
// TODO(v8:11330) The Torque SIMD implementation is not specific to SSE (like
// the C++ one), but works on other platforms. It should be possible to create a
// workaround where on 32 bit, non-IA32 platforms we use the "portable", non-SSE
// implementation on the C++ side (which uses a group size of 8) and create a
// special version of the SIMD Torque implementation that works for group size 8
// instead of 16.
#if defined(V8_TARGET_ARCH_64_BIT) || defined(V8_TARGET_ARCH_IA32)
// Executes the tests defined in test-swiss-name-dictionary-shared-tests.h as if
// they were defined in this file, using the CSATestRunner. See comments in
// test-swiss-name-dictionary-shared-tests.h and in

View File

@ -141,6 +141,9 @@ struct SharedSwissTableTests {
// Simple test for adding entries. Also uses non-Symbol keys and non-String
// values, which is not supported by the higher-level testing infrastructure.
MEMBER_TEST(SimpleAdd) {
// TODO(v8:11330): Remove once CSA implementation has a fallback for
// non-SSSE3/AVX configurations.
if (!TestRunner::IsEnabled()) return;
TS::WithInitialCapacity(4, [](TS& s) {
Handle<String> key1 = s.isolate->factory()->InternalizeUtf8String("foo");
Handle<String> value1 =
@ -173,6 +176,9 @@ struct SharedSwissTableTests {
// non-String values, which is not supported by the higher-level testing
// infrastructure.
MEMBER_TEST(SimpleUpdate) {
// TODO(v8:11330): Remove once CSA implementation has a fallback for
// non-SSSE3/AVX configurations.
if (!TestRunner::IsEnabled()) return;
TS::WithInitialCapacity(4, [](TS& s) {
Handle<String> key1 = s.isolate->factory()->InternalizeUtf8String("foo");
Handle<String> value1 =
@ -214,6 +220,9 @@ struct SharedSwissTableTests {
// non-String values, which is not supported by the higher-level testing
// infrastructure.
MEMBER_TEST(SimpleDelete) {
// TODO(v8:11330): Remove once CSA implementation has a fallback for
// non-SSSE3/AVX configurations.
if (!TestRunner::IsEnabled()) return;
TS::WithInitialCapacity(4, [](TS& s) {
Handle<String> key1 = s.isolate->factory()->InternalizeUtf8String("foo");
Handle<String> value1 =
@ -249,6 +258,9 @@ struct SharedSwissTableTests {
// Adds entries that occuppy the boundaries (first and last
// buckets) of the hash table.
MEMBER_TEST(AddAtBoundaries) {
// TODO(v8:11330): Remove once CSA implementation has a fallback for
// non-SSSE3/AVX configurations.
if (!TestRunner::IsEnabled()) return;
TS::WithAllInterestingInitialCapacities([](TS& s) {
AddAtBoundaries(s);
@ -273,6 +285,9 @@ struct SharedSwissTableTests {
// Adds entries that occuppy the boundaries of the hash table, then updates
// their values and property details.
MEMBER_TEST(UpdateAtBoundaries) {
// TODO(v8:11330): Remove once CSA implementation has a fallback for
// non-SSSE3/AVX configurations.
if (!TestRunner::IsEnabled()) return;
TS::WithAllInterestingInitialCapacities([](TS& s) {
AddAtBoundaries(s);
@ -306,6 +321,9 @@ struct SharedSwissTableTests {
// Adds entries that occuppy the boundaries of the hash table, then updates
// their values and property details.
MEMBER_TEST(DeleteAtBoundaries) {
// TODO(v8:11330): Remove once CSA implementation has a fallback for
// non-SSSE3/AVX configurations.
if (!TestRunner::IsEnabled()) return;
// The maximum value of {TS::boundary_indices(capacity).size()} for any
// |capacity|.
int count = 4;
@ -350,6 +368,9 @@ struct SharedSwissTableTests {
// Adds entries that occuppy the boundaries of the hash table, then add
// further entries targeting the same buckets.
MEMBER_TEST(OverwritePresentAtBoundaries) {
// TODO(v8:11330): Remove once CSA implementation has a fallback for
// non-SSSE3/AVX configurations.
if (!TestRunner::IsEnabled()) return;
TS::WithAllInterestingInitialCapacities([](TS& s) {
AddAtBoundaries(s);
@ -398,6 +419,9 @@ struct SharedSwissTableTests {
}
MEMBER_TEST(Empty) {
// TODO(v8:11330): Remove once CSA implementation has a fallback for
// non-SSSE3/AVX configurations.
if (!TestRunner::IsEnabled()) return;
TS::WithInitialCapacities({0}, [](TS& s) {
// FindEntry on empty table succeeds.
s.CheckKeyAbsent(Key{"some non-existing key"});
@ -434,6 +458,9 @@ struct SharedSwissTableTests {
// We test that hash tables get resized/rehashed correctly by repeatedly
// adding an deleting elements.
MEMBER_TEST(Resize1) {
// TODO(v8:11330): Remove once CSA implementation has a fallback for
// non-SSSE3/AVX configurations.
if (!TestRunner::IsEnabled()) return;
TS::WithInitialCapacity(0, [](TS& s) {
// Should be at least 8 so that we capture the transition from 8 bit to 16
// bit meta table entries:
@ -486,6 +513,9 @@ struct SharedSwissTableTests {
// Check that we resize exactly when expected.
MEMBER_TEST(Resize2) {
// TODO(v8:11330): Remove once CSA implementation has a fallback for
// non-SSSE3/AVX configurations.
if (!TestRunner::IsEnabled()) return;
TS::WithInitialCapacities({4, 8, 16, 128}, [](TS& s) {
int count = SwissNameDictionary::MaxUsableCapacity(s.initial_capacity);
@ -503,6 +533,9 @@ struct SharedSwissTableTests {
// table before resizing (i.e., the max load factor is 100% for those
// particular configurations. Test that this works as intended.
MEMBER_TEST(AtFullCapacity) {
// TODO(v8:11330): Remove once CSA implementation has a fallback for
// non-SSSE3/AVX configurations.
if (!TestRunner::IsEnabled()) return;
// Determine those capacities, allowing 100% max load factor. We trust
// MaxUsableCapacity to tell us which capacities that are (e.g., 4 and 8),
// because we tested that function separately elsewhere.
@ -599,6 +632,9 @@ struct SharedSwissTableTests {
// Make sure that keys with colliding H1 and same H2 don't get mixed up.
MEMBER_TEST(SameH2) {
// TODO(v8:11330): Remove once CSA implementation has a fallback for
// non-SSSE3/AVX configurations.
if (!TestRunner::IsEnabled()) return;
int i = 0;
TS::WithAllInterestingInitialCapacities([&](TS& s) {
// Let's try a few differnet values for h1, starting at big_modulus;.
@ -629,6 +665,9 @@ struct SharedSwissTableTests {
// Check that we can delete a key and add it again.
MEMBER_TEST(ReAddSameKey) {
// TODO(v8:11330): Remove once CSA implementation has a fallback for
// non-SSSE3/AVX configurations.
if (!TestRunner::IsEnabled()) return;
TS::WithInitialCapacity(4, [](TS& s) {
s.Add(Key{"some_key"}, "some_value", distinct_details(0));
s.DeleteByKey(Key{"some_key"});
@ -643,6 +682,9 @@ struct SharedSwissTableTests {
// group and that the quadratic probing for choosing subsequent groups to
// probe works as intended.
MEMBER_TEST(BeyondInitialGroup) {
// TODO(v8:11330): Remove once CSA implementation has a fallback for
// non-SSSE3/AVX configurations.
if (!TestRunner::IsEnabled()) return;
TS::WithInitialCapacity(128, [](TS& s) {
int h1 = 33; // Arbitrarily chosen.
int count = 37; // Will lead to more than 2 groups being filled.
@ -825,6 +867,9 @@ struct SharedSwissTableTests {
}
MEMBER_TEST(ShrinkOnDelete) {
// TODO(v8:11330): Remove once CSA implementation has a fallback for
// non-SSSE3/AVX configurations.
if (!TestRunner::IsEnabled()) return;
TS::WithInitialCapacity(32, [](TS& s) {
// Adds key0 ... key9:
AddMultiple(s, 10);

View File

@ -20,6 +20,10 @@ class RuntimeTestRunner {
initial_capacity, AllocationType::kYoung);
}
// The runtime implementations does not depend on the CPU features and
// therefore always work.
static bool IsEnabled() { return true; }
void Add(Handle<Name> key, Handle<Object> value, PropertyDetails details);
InternalIndex FindEntry(Handle<Name> key);
// Updates the value and property details of the given entry.