// 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. #include "test/cctest/test-swiss-name-dictionary-infra.h" namespace v8 { namespace internal { namespace test_swiss_hash_table { // 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); void Add(Handle key, Handle value, PropertyDetails details); InternalIndex FindEntry(Handle key); void Put(InternalIndex entry, Handle new_value, PropertyDetails new_details); void Delete(InternalIndex entry); void RehashInplace(); void Shrink(); Handle GetData(InternalIndex entry); void CheckCounts(base::Optional capacity, base::Optional elements, base::Optional deleted); void CheckEnumerationOrder(const std::vector& expected_keys); void CheckCopy(); void VerifyHeap(); void PrintTable(); Handle table; private: void CheckAgainstReference(); void Allocate(Handle capacity); Isolate* isolate_; // Used to mirror all operations using C++ versions of all operations, // yielding a reference to compare against. Handle reference_; // CSA functions execute the corresponding dictionary operation. compiler::FunctionTester find_entry_ft_; compiler::FunctionTester get_data_ft_; compiler::FunctionTester put_ft_; compiler::FunctionTester delete_ft_; compiler::FunctionTester add_ft_; compiler::FunctionTester allocate_ft_; // Used to create the FunctionTesters above. static Handle create_get_data(Isolate* isolate); static Handle create_find_entry(Isolate* isolate); static Handle create_put(Isolate* isolate); static Handle create_delete(Isolate* isolate); static Handle create_add(Isolate* isolate); static Handle create_allocate(Isolate* isolate); // Number of parameters of each of the tester functions above. static constexpr int kFindEntryParams = 2; // (table, key) static constexpr int kGetDataParams = 2; // (table, entry) static constexpr int kPutParams = 4; // (table, entry, value, details) static constexpr int kDeleteParams = 2; // (table, entry) static constexpr int kAddParams = 4; // (table, key, value, details) static constexpr int kAllocateParams = 1; // (capacity) }; // TODO(v8:11330): Currently, the CSATestRunner isn't doing much, except // generating runtime calls. That will change once we have the CSA // implementations ready. CSATestRunner::CSATestRunner(Isolate* isolate, int initial_capacity, KeyCache& keys) : isolate_{isolate}, reference_{isolate_->factory()->NewSwissNameDictionaryWithCapacity( initial_capacity, AllocationType::kYoung)}, find_entry_ft_(create_find_entry(isolate), kFindEntryParams), get_data_ft_(create_get_data(isolate), kGetDataParams), put_ft_{create_put(isolate), kPutParams}, delete_ft_{create_delete(isolate), kDeleteParams}, add_ft_{create_add(isolate), kAddParams}, allocate_ft_{create_allocate(isolate), kAllocateParams} { int at_least_space_for = SwissNameDictionary::MaxUsableCapacity(initial_capacity); Allocate(handle(Smi::FromInt(at_least_space_for), isolate)); } void CSATestRunner::Add(Handle key, Handle value, PropertyDetails details) { reference_ = SwissNameDictionary::Add(isolate_, reference_, key, value, details); Handle details_smi = handle(details.AsSmi(), isolate_); table = add_ft_.CallChecked(table, key, value, details_smi); CheckAgainstReference(); } void CSATestRunner::Allocate(Handle capacity) { table = allocate_ft_.CallChecked(capacity); CheckAgainstReference(); } InternalIndex CSATestRunner::FindEntry(Handle key) { Handle index = find_entry_ft_.CallChecked(table, key); if (index->value() == SwissNameDictionary::kNotFoundSentinel) { return InternalIndex::NotFound(); } else { return InternalIndex(index->value()); } } Handle CSATestRunner::GetData(InternalIndex entry) { DCHECK(entry.is_found()); return get_data_ft_.CallChecked( table, handle(Smi::FromInt(entry.as_int()), isolate_)); } void CSATestRunner::CheckCounts(base::Optional capacity, base::Optional elements, base::Optional deleted) { // TODO(v8:11330) Do actual check here once CSA/Torque version exists. CheckAgainstReference(); } void CSATestRunner::CheckEnumerationOrder( const std::vector& expected_keys) { // TODO(v8:11330) Do actual check here once CSA/Torque version exists. CheckAgainstReference(); } void CSATestRunner::Put(InternalIndex entry, Handle new_value, PropertyDetails new_details) { DCHECK(entry.is_found()); reference_->ValueAtPut(entry, *new_value); reference_->DetailsAtPut(entry, new_details); Handle entry_smi = handle(Smi::FromInt(entry.as_int()), isolate_); Handle details_smi = handle(new_details.AsSmi(), isolate_); put_ft_.Call(table, entry_smi, new_value, details_smi); CheckAgainstReference(); } void CSATestRunner::Delete(InternalIndex entry) { DCHECK(entry.is_found()); reference_ = SwissNameDictionary::DeleteEntry(isolate_, reference_, entry); Handle entry_smi = handle(Smi::FromInt(entry.as_int()), isolate_); table = delete_ft_.CallChecked(table, entry_smi); CheckAgainstReference(); } void CSATestRunner::RehashInplace() { // There's no CSA version of this. Use IsRuntimeTest to ensure that we only // run a test using this if it's a runtime test. UNREACHABLE(); } void CSATestRunner::Shrink() { // There's no CSA version of this. Use IsRuntimeTest to ensure that we only // run a test using this if it's a runtime test. UNREACHABLE(); } void CSATestRunner::CheckCopy() { // TODO(v8:11330) Do actual check here once CSA/Torque version exists. } void CSATestRunner::VerifyHeap() { #if VERIFY_HEAP table->SwissNameDictionaryVerify(isolate_, true); #endif } void CSATestRunner::PrintTable() { #ifdef OBJECT_PRINT table->SwissNameDictionaryPrint(std::cout); #endif } Handle CSATestRunner::create_find_entry(Isolate* isolate) { STATIC_ASSERT(kFindEntryParams == 2); // (table, key) compiler::CodeAssemblerTester asm_tester(isolate, kFindEntryParams + 1); CodeStubAssembler m(asm_tester.state()); { TNode table = m.Parameter(1); TNode key = m.Parameter(2); TNode index = m.CallRuntime(Runtime::kSwissTableFindEntry, m.NoContextConstant(), table, key); m.Return(index); } return asm_tester.GenerateCodeCloseAndEscape(); } Handle CSATestRunner::create_get_data(Isolate* isolate) { STATIC_ASSERT(kGetDataParams == 2); // (table, entry) compiler::CodeAssemblerTester asm_tester(isolate, kGetDataParams + 1); CodeStubAssembler m(asm_tester.state()); using Label = compiler::CodeAssemblerLabel; { TNode table = m.Parameter(1); TNode index = m.Parameter(2); Label not_found(&m); m.GotoIf(m.SmiEqual(index, m.SmiConstant(SwissNameDictionary::kNotFoundSentinel)), ¬_found); TNode data = m.AllocateZeroedFixedArray(m.IntPtrConstant(3)); TNode key = m.CallRuntime(Runtime::kSwissTableKeyAt, m.NoContextConstant(), table, index); TNode value = m.CallRuntime(Runtime::kSwissTableValueAt, m.NoContextConstant(), table, index); TNode details = m.UncheckedCast(m.CallRuntime( Runtime::kSwissTableDetailsAt, m.NoContextConstant(), table, index)); m.StoreFixedArrayElement(data, 0, key); m.StoreFixedArrayElement(data, 1, value); m.StoreFixedArrayElement(data, 2, details); m.Return(data); m.Bind(¬_found); m.Return(m.EmptyFixedArrayConstant()); } return asm_tester.GenerateCodeCloseAndEscape(); } Handle CSATestRunner::create_put(Isolate* isolate) { STATIC_ASSERT(kPutParams == 4); // (table, entry, value, details) compiler::CodeAssemblerTester asm_tester(isolate, kPutParams + 1); CodeStubAssembler m(asm_tester.state()); { TNode table = m.Parameter(1); TNode entry = m.Parameter(2); TNode value = m.Parameter(3); TNode details = m.Parameter(4); m.CallRuntime(Runtime::kSwissTableUpdate, m.NoContextConstant(), table, entry, value, details); m.Return(m.UndefinedConstant()); } return asm_tester.GenerateCodeCloseAndEscape(); } Handle CSATestRunner::create_delete(Isolate* isolate) { STATIC_ASSERT(kDeleteParams == 2); // (table, entry) compiler::CodeAssemblerTester asm_tester(isolate, kDeleteParams + 1); CodeStubAssembler m(asm_tester.state()); { TNode table = m.Parameter(1); TNode entry = m.Parameter(2); TNode new_table = m.CallRuntime( Runtime::kSwissTableDelete, m.NoContextConstant(), table, entry); m.Return(new_table); } return asm_tester.GenerateCodeCloseAndEscape(); } Handle CSATestRunner::create_add(Isolate* isolate) { STATIC_ASSERT(kAddParams == 4); // (table, key, value, details) compiler::CodeAssemblerTester asm_tester(isolate, kAddParams + 1); CodeStubAssembler m(asm_tester.state()); { TNode table = m.Parameter(1); TNode key = m.Parameter(2); TNode value = m.Parameter(3); TNode details = m.Parameter(4); TNode new_table = m.CallRuntime( Runtime::kSwissTableAdd, m.NoContextConstant(), table, key, value, details); m.Return(new_table); } return asm_tester.GenerateCodeCloseAndEscape(); } Handle CSATestRunner::create_allocate(Isolate* isolate) { STATIC_ASSERT(kAllocateParams == 1); // (capacity) compiler::CodeAssemblerTester asm_tester(isolate, kAllocateParams + 1); CodeStubAssembler m(asm_tester.state()); { TNode at_least_space_for = m.Parameter(1); TNode table = m.CallRuntime( Runtime::kSwissTableAllocate, m.NoContextConstant(), at_least_space_for); m.Return(table); } return asm_tester.GenerateCodeCloseAndEscape(); } void CSATestRunner::CheckAgainstReference() { CHECK(table->EqualsForTesting(*reference_)); } } // namespace test_swiss_hash_table } // namespace internal } // namespace v8