Commit 7bfc2e00 authored by Leszek Swirski's avatar Leszek Swirski Committed by Commit Bot

[parser] Make parser string table empty-valued

Add support for empty values (i.e. set behaviour) and heterogeneous
lookup (lookup with a different key than the one you'll insert) to
TemplateHashMap, and use it for the string table in AstValueFactory.

Change-Id: I0c1487c9598127aac97059d4b9220e5c3c6283ce
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2494705
Auto-Submit: Leszek Swirski <leszeks@chromium.org>
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70765}
parent 27b226b7
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "src/ast/ast-value-factory.h" #include "src/ast/ast-value-factory.h"
#include "src/base/hashmap-entry.h"
#include "src/base/logging.h" #include "src/base/logging.h"
#include "src/common/globals.h" #include "src/common/globals.h"
#include "src/heap/factory-inl.h" #include "src/heap/factory-inl.h"
...@@ -113,9 +114,7 @@ uint16_t AstRawString::FirstCharacter() const { ...@@ -113,9 +114,7 @@ uint16_t AstRawString::FirstCharacter() const {
return *c; return *c;
} }
bool AstRawString::Compare(void* a, void* b) { bool AstRawString::Compare(const AstRawString* lhs, const AstRawString* rhs) {
const AstRawString* lhs = static_cast<AstRawString*>(a);
const AstRawString* rhs = static_cast<AstRawString*>(b);
DCHECK_EQ(lhs->Hash(), rhs->Hash()); DCHECK_EQ(lhs->Hash(), rhs->Hash());
if (lhs->length() != rhs->length()) return false; if (lhs->length() != rhs->length()) return false;
...@@ -248,7 +247,7 @@ std::forward_list<const AstRawString*> AstConsString::ToRawStrings() const { ...@@ -248,7 +247,7 @@ std::forward_list<const AstRawString*> AstConsString::ToRawStrings() const {
AstStringConstants::AstStringConstants(Isolate* isolate, uint64_t hash_seed) AstStringConstants::AstStringConstants(Isolate* isolate, uint64_t hash_seed)
: zone_(isolate->allocator(), ZONE_NAME), : zone_(isolate->allocator(), ZONE_NAME),
string_table_(AstRawString::Compare), string_table_(),
hash_seed_(hash_seed) { hash_seed_(hash_seed) {
DCHECK_EQ(ThreadId::Current(), isolate->thread_id()); DCHECK_EQ(ThreadId::Current(), isolate->thread_id());
#define F(name, str) \ #define F(name, str) \
...@@ -262,16 +261,13 @@ AstStringConstants::AstStringConstants(Isolate* isolate, uint64_t hash_seed) ...@@ -262,16 +261,13 @@ AstStringConstants::AstStringConstants(Isolate* isolate, uint64_t hash_seed)
/* The Handle returned by the factory is located on the roots */ \ /* The Handle returned by the factory is located on the roots */ \
/* array, not on the temporary HandleScope, so this is safe. */ \ /* array, not on the temporary HandleScope, so this is safe. */ \
name##_string_->set_string(isolate->factory()->name##_string()); \ name##_string_->set_string(isolate->factory()->name##_string()); \
base::HashMap::Entry* entry = \ string_table_.InsertNew(name##_string_, name##_string_->Hash()); \
string_table_.InsertNew(name##_string_, name##_string_->Hash()); \
DCHECK_NULL(entry->value); \
entry->value = reinterpret_cast<void*>(1); \
} }
AST_STRING_CONSTANTS(F) AST_STRING_CONSTANTS(F)
#undef F #undef F
} }
AstRawString* AstValueFactory::GetOneByteStringInternal( const AstRawString* AstValueFactory::GetOneByteStringInternal(
Vector<const uint8_t> literal) { Vector<const uint8_t> literal) {
if (literal.length() == 1 && literal[0] < kMaxOneCharStringValue) { if (literal.length() == 1 && literal[0] < kMaxOneCharStringValue) {
int key = literal[0]; int key = literal[0];
...@@ -287,7 +283,7 @@ AstRawString* AstValueFactory::GetOneByteStringInternal( ...@@ -287,7 +283,7 @@ AstRawString* AstValueFactory::GetOneByteStringInternal(
return GetString(hash_field, true, literal); return GetString(hash_field, true, literal);
} }
AstRawString* AstValueFactory::GetTwoByteStringInternal( const AstRawString* AstValueFactory::GetTwoByteStringInternal(
Vector<const uint16_t> literal) { Vector<const uint16_t> literal) {
uint32_t hash_field = StringHasher::HashSequentialString<uint16_t>( uint32_t hash_field = StringHasher::HashSequentialString<uint16_t>(
literal.begin(), literal.length(), hash_seed_); literal.begin(), literal.length(), hash_seed_);
...@@ -295,7 +291,7 @@ AstRawString* AstValueFactory::GetTwoByteStringInternal( ...@@ -295,7 +291,7 @@ AstRawString* AstValueFactory::GetTwoByteStringInternal(
} }
const AstRawString* AstValueFactory::GetString(Handle<String> literal) { const AstRawString* AstValueFactory::GetString(Handle<String> literal) {
AstRawString* result = nullptr; const AstRawString* result = nullptr;
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
String::FlatContent content = literal->GetFlatContent(no_gc); String::FlatContent content = literal->GetFlatContent(no_gc);
if (content.IsOneByte()) { if (content.IsOneByte()) {
...@@ -348,27 +344,29 @@ template EXPORT_TEMPLATE_DEFINE( ...@@ -348,27 +344,29 @@ template EXPORT_TEMPLATE_DEFINE(
template EXPORT_TEMPLATE_DEFINE( template EXPORT_TEMPLATE_DEFINE(
V8_EXPORT_PRIVATE) void AstValueFactory::Internalize(LocalIsolate* isolate); V8_EXPORT_PRIVATE) void AstValueFactory::Internalize(LocalIsolate* isolate);
AstRawString* AstValueFactory::GetString(uint32_t hash_field, bool is_one_byte, const AstRawString* AstValueFactory::GetString(
Vector<const byte> literal_bytes) { uint32_t hash_field, bool is_one_byte, Vector<const byte> literal_bytes) {
// literal_bytes here points to whatever the user passed, and this is OK // literal_bytes here points to whatever the user passed, and this is OK
// because we use vector_compare (which checks the contents) to compare // because we use vector_compare (which checks the contents) to compare
// against the AstRawStrings which are in the string_table_. We should not // against the AstRawStrings which are in the string_table_. We should not
// return this AstRawString. // return this AstRawString.
AstRawString key(is_one_byte, literal_bytes, hash_field); AstRawString key(is_one_byte, literal_bytes, hash_field);
base::HashMap::Entry* entry = string_table_.LookupOrInsert(&key, key.Hash()); AstRawStringMap::Entry* entry = string_table_.LookupOrInsert(
if (entry->value == nullptr) { &key, key.Hash(),
// Copy literal contents for later comparison. [&]() {
int length = literal_bytes.length(); // Copy literal contents for later comparison.
byte* new_literal_bytes = zone()->NewArray<byte>(length); int length = literal_bytes.length();
memcpy(new_literal_bytes, literal_bytes.begin(), length); byte* new_literal_bytes = zone()->NewArray<byte>(length);
AstRawString* new_string = zone()->New<AstRawString>( memcpy(new_literal_bytes, literal_bytes.begin(), length);
is_one_byte, Vector<const byte>(new_literal_bytes, length), hash_field); AstRawString* new_string = zone()->New<AstRawString>(
CHECK_NOT_NULL(new_string); is_one_byte, Vector<const byte>(new_literal_bytes, length),
AddString(new_string); hash_field);
entry->key = new_string; CHECK_NOT_NULL(new_string);
entry->value = reinterpret_cast<void*>(1); AddString(new_string);
} return new_string;
return reinterpret_cast<AstRawString*>(entry->key); },
[&]() { return base::NoHashMapValue(); });
return entry->key;
} }
} // namespace internal } // namespace internal
......
...@@ -48,6 +48,8 @@ class Isolate; ...@@ -48,6 +48,8 @@ class Isolate;
class AstRawString final : public ZoneObject { class AstRawString final : public ZoneObject {
public: public:
static bool Compare(const AstRawString* a, const AstRawString* b);
bool IsEmpty() const { return literal_bytes_.length() == 0; } bool IsEmpty() const { return literal_bytes_.length() == 0; }
int length() const { int length() const {
return is_one_byte() ? literal_bytes_.length() return is_one_byte() ? literal_bytes_.length()
...@@ -85,7 +87,6 @@ class AstRawString final : public ZoneObject { ...@@ -85,7 +87,6 @@ class AstRawString final : public ZoneObject {
friend Zone; friend Zone;
// Members accessed only by the AstValueFactory & related classes: // Members accessed only by the AstValueFactory & related classes:
static bool Compare(void* a, void* b);
AstRawString(bool is_one_byte, const Vector<const byte>& literal_bytes, AstRawString(bool is_one_byte, const Vector<const byte>& literal_bytes,
uint32_t hash_field) uint32_t hash_field)
: next_(nullptr), : next_(nullptr),
...@@ -205,6 +206,19 @@ class AstBigInt { ...@@ -205,6 +206,19 @@ class AstBigInt {
const char* bigint_; const char* bigint_;
}; };
struct AstRawStringMapMatcher {
bool operator()(uint32_t hash1, uint32_t hash2,
const AstRawString* lookup_key,
const AstRawString* entry_key) const {
return hash1 == hash2 && AstRawString::Compare(lookup_key, entry_key);
}
};
using AstRawStringMap =
base::TemplateHashMapImpl<const AstRawString*, base::NoHashMapValue,
AstRawStringMapMatcher,
base::DefaultAllocationPolicy>;
// For generating constants. // For generating constants.
#define AST_STRING_CONSTANTS(F) \ #define AST_STRING_CONSTANTS(F) \
F(anonymous, "anonymous") \ F(anonymous, "anonymous") \
...@@ -270,13 +284,11 @@ class AstStringConstants final { ...@@ -270,13 +284,11 @@ class AstStringConstants final {
#undef F #undef F
uint64_t hash_seed() const { return hash_seed_; } uint64_t hash_seed() const { return hash_seed_; }
const base::CustomMatcherHashMap* string_table() const { const AstRawStringMap* string_table() const { return &string_table_; }
return &string_table_;
}
private: private:
Zone zone_; Zone zone_;
base::CustomMatcherHashMap string_table_; AstRawStringMap string_table_;
uint64_t hash_seed_; uint64_t hash_seed_;
#define F(name, str) AstRawString* name##_string_; #define F(name, str) AstRawString* name##_string_;
...@@ -354,14 +366,14 @@ class AstValueFactory { ...@@ -354,14 +366,14 @@ class AstValueFactory {
strings_ = nullptr; strings_ = nullptr;
strings_end_ = &strings_; strings_end_ = &strings_;
} }
V8_EXPORT_PRIVATE AstRawString* GetOneByteStringInternal( V8_EXPORT_PRIVATE const AstRawString* GetOneByteStringInternal(
Vector<const uint8_t> literal); Vector<const uint8_t> literal);
AstRawString* GetTwoByteStringInternal(Vector<const uint16_t> literal); const AstRawString* GetTwoByteStringInternal(Vector<const uint16_t> literal);
AstRawString* GetString(uint32_t hash, bool is_one_byte, const AstRawString* GetString(uint32_t hash, bool is_one_byte,
Vector<const byte> literal_bytes); Vector<const byte> literal_bytes);
// All strings are copied here, one after another (no zeroes inbetween). // All strings are copied here.
base::CustomMatcherHashMap string_table_; AstRawStringMap string_table_;
AstRawString* strings_; AstRawString* strings_;
AstRawString** strings_end_; AstRawString** strings_end_;
...@@ -373,7 +385,7 @@ class AstValueFactory { ...@@ -373,7 +385,7 @@ class AstValueFactory {
// Caches one character lowercase strings (for minified code). // Caches one character lowercase strings (for minified code).
static const int kMaxOneCharStringValue = 128; static const int kMaxOneCharStringValue = 128;
AstRawString* one_character_strings_[kMaxOneCharStringValue]; const AstRawString* one_character_strings_[kMaxOneCharStringValue];
Zone* zone_; Zone* zone_;
......
...@@ -6,15 +6,25 @@ ...@@ -6,15 +6,25 @@
#define V8_BASE_HASHMAP_ENTRY_H_ #define V8_BASE_HASHMAP_ENTRY_H_
#include <cstdint> #include <cstdint>
#include <type_traits>
#include "src/base/memory.h"
namespace v8 { namespace v8 {
namespace base { namespace base {
// Marker type for hashmaps without a value (i.e. hashsets). These won't
// allocate space for the value in the entry.
struct NoHashMapValue {};
// HashMap entries are (key, value, hash) triplets, with a boolean indicating if // HashMap entries are (key, value, hash) triplets, with a boolean indicating if
// they are an empty entry. Some clients may not need to use the value slot // they are an empty entry. Some clients may not need to use the value slot
// (e.g. implementers of sets, where the key is the value). // (e.g. implementers of sets, where the key is the value), in which case they
// should use NoHashMapValue.
template <typename Key, typename Value> template <typename Key, typename Value>
struct TemplateHashMapEntry { struct TemplateHashMapEntry {
STATIC_ASSERT((!std::is_same<Value, NoHashMapValue>::value));
Key key; Key key;
Value value; Value value;
uint32_t hash; // The full hash value for key uint32_t hash; // The full hash value for key
...@@ -33,6 +43,8 @@ struct TemplateHashMapEntry { ...@@ -33,6 +43,8 @@ struct TemplateHashMapEntry {
// Specialization for pointer-valued keys // Specialization for pointer-valued keys
template <typename Key, typename Value> template <typename Key, typename Value>
struct TemplateHashMapEntry<Key*, Value> { struct TemplateHashMapEntry<Key*, Value> {
STATIC_ASSERT((!std::is_same<Value, NoHashMapValue>::value));
Key* key; Key* key;
Value value; Value value;
uint32_t hash; // The full hash value for key uint32_t hash; // The full hash value for key
...@@ -45,8 +57,42 @@ struct TemplateHashMapEntry<Key*, Value> { ...@@ -45,8 +57,42 @@ struct TemplateHashMapEntry<Key*, Value> {
void clear() { key = nullptr; } void clear() { key = nullptr; }
}; };
// TODO(leszeks): There could be a specialisation for void values (e.g. for // Specialization for no value.
// sets), which omits the value field template <typename Key>
struct TemplateHashMapEntry<Key, NoHashMapValue> {
union {
Key key;
NoHashMapValue value; // Value in union with key to not take up space.
};
uint32_t hash; // The full hash value for key
TemplateHashMapEntry(Key key, NoHashMapValue value, uint32_t hash)
: key(key), hash(hash), exists_(true) {}
bool exists() const { return exists_; }
void clear() { exists_ = false; }
private:
bool exists_;
};
// Specialization for pointer-valued keys and no value.
template <typename Key>
struct TemplateHashMapEntry<Key*, NoHashMapValue> {
union {
Key* key;
NoHashMapValue value; // Value in union with key to not take up space.
};
uint32_t hash; // The full hash value for key
TemplateHashMapEntry(Key* key, NoHashMapValue value, uint32_t hash)
: key(key), hash(hash) {}
bool exists() const { return key != nullptr; }
void clear() { key = nullptr; }
};
} // namespace base } // namespace base
} // namespace v8 } // namespace v8
......
...@@ -72,6 +72,20 @@ class TemplateHashMapImpl { ...@@ -72,6 +72,20 @@ class TemplateHashMapImpl {
template <typename Func> template <typename Func>
Entry* LookupOrInsert(const Key& key, uint32_t hash, const Func& value_func); Entry* LookupOrInsert(const Key& key, uint32_t hash, const Func& value_func);
// Heterogeneous version of LookupOrInsert, which allows a
// different lookup key type than the hashmap's key type.
// The requirement is that MatchFun has an overload:
//
// operator()(const LookupKey& lookup_key, const Key& entry_key)
//
// If an entry with matching key is found, returns that entry.
// If no matching entry is found, a new entry is inserted with
// a key created by key_func, key hash, and value created by
// value_func.
template <typename LookupKey, typename KeyFunc, typename ValueFunc>
Entry* LookupOrInsert(const LookupKey& lookup_key, uint32_t hash,
const KeyFunc& key_func, const ValueFunc& value_func);
Entry* InsertNew(const Key& key, uint32_t hash); Entry* InsertNew(const Key& key, uint32_t hash);
// Removes the entry with matching key. // Removes the entry with matching key.
...@@ -115,7 +129,8 @@ class TemplateHashMapImpl { ...@@ -115,7 +129,8 @@ class TemplateHashMapImpl {
private: private:
Entry* map_end() const { return impl_.map_ + impl_.capacity_; } Entry* map_end() const { return impl_.map_ + impl_.capacity_; }
Entry* Probe(const Key& key, uint32_t hash) const; template <typename LookupKey>
Entry* Probe(const LookupKey& key, uint32_t hash) const;
Entry* FillEmptyEntry(Entry* entry, const Key& key, const Value& value, Entry* FillEmptyEntry(Entry* entry, const Key& key, const Value& value,
uint32_t hash); uint32_t hash);
void Resize(); void Resize();
...@@ -214,13 +229,24 @@ template <typename Func> ...@@ -214,13 +229,24 @@ template <typename Func>
typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry* typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::LookupOrInsert( TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::LookupOrInsert(
const Key& key, uint32_t hash, const Func& value_func) { const Key& key, uint32_t hash, const Func& value_func) {
return LookupOrInsert(
key, hash, [&key]() { return key; }, value_func);
}
template <typename Key, typename Value, typename MatchFun,
class AllocationPolicy>
template <typename LookupKey, typename KeyFunc, typename ValueFunc>
typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::LookupOrInsert(
const LookupKey& lookup_key, uint32_t hash, const KeyFunc& key_func,
const ValueFunc& value_func) {
// Find a matching entry. // Find a matching entry.
Entry* entry = Probe(key, hash); Entry* entry = Probe(lookup_key, hash);
if (entry->exists()) { if (entry->exists()) {
return entry; return entry;
} }
return FillEmptyEntry(entry, key, value_func(), hash); return FillEmptyEntry(entry, key_func(), value_func(), hash);
} }
template <typename Key, typename Value, typename MatchFun, template <typename Key, typename Value, typename MatchFun,
...@@ -328,9 +354,10 @@ TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Next( ...@@ -328,9 +354,10 @@ TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Next(
template <typename Key, typename Value, typename MatchFun, template <typename Key, typename Value, typename MatchFun,
class AllocationPolicy> class AllocationPolicy>
template <typename LookupKey>
typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry* typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Probe( TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Probe(
const Key& key, uint32_t hash) const { const LookupKey& key, uint32_t hash) const {
DCHECK(base::bits::IsPowerOfTwo(capacity())); DCHECK(base::bits::IsPowerOfTwo(capacity()));
size_t i = hash & (capacity() - 1); size_t i = hash & (capacity() - 1);
DCHECK(i < capacity()); DCHECK(i < capacity());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment