Commit 204989a5 authored by jkummerow's avatar jkummerow Committed by Commit bot

[builtins] HasOwnProperty: handle non-internalized string keys

Taking the slow runtime path for every non-internalized string key
can be avoided by doing optimistic string table lookups: if there
is a matching entry, use that; if there isn't, then no existing
object has a property with that name.
The hashing/internalizing logic is in C++ and called directly.

Review-Url: https://codereview.chromium.org/2811333002
Cr-Commit-Position: refs/heads/master@{#44650}
parent fa0066d1
...@@ -1562,6 +1562,12 @@ ExternalReference ExternalReference::libc_memset_function(Isolate* isolate) { ...@@ -1562,6 +1562,12 @@ ExternalReference ExternalReference::libc_memset_function(Isolate* isolate) {
return ExternalReference(Redirect(isolate, FUNCTION_ADDR(libc_memset))); return ExternalReference(Redirect(isolate, FUNCTION_ADDR(libc_memset)));
} }
ExternalReference ExternalReference::try_internalize_string_function(
Isolate* isolate) {
return ExternalReference(Redirect(
isolate, FUNCTION_ADDR(StringTable::LookupStringIfExists_NoAllocate)));
}
ExternalReference ExternalReference::page_flags(Page* page) { ExternalReference ExternalReference::page_flags(Page* page) {
return ExternalReference(reinterpret_cast<Address>(page) + return ExternalReference(reinterpret_cast<Address>(page) +
MemoryChunk::kFlagsOffset); MemoryChunk::kFlagsOffset);
......
...@@ -992,6 +992,8 @@ class ExternalReference BASE_EMBEDDED { ...@@ -992,6 +992,8 @@ class ExternalReference BASE_EMBEDDED {
static ExternalReference libc_memcpy_function(Isolate* isolate); static ExternalReference libc_memcpy_function(Isolate* isolate);
static ExternalReference libc_memset_function(Isolate* isolate); static ExternalReference libc_memset_function(Isolate* isolate);
static ExternalReference try_internalize_string_function(Isolate* isolate);
static ExternalReference page_flags(Page* page); static ExternalReference page_flags(Page* page);
static ExternalReference ForDeoptEntry(Address entry); static ExternalReference ForDeoptEntry(Address entry);
......
...@@ -68,19 +68,31 @@ TF_BUILTIN(ObjectHasOwnProperty, ObjectBuiltinsAssembler) { ...@@ -68,19 +68,31 @@ TF_BUILTIN(ObjectHasOwnProperty, ObjectBuiltinsAssembler) {
VARIABLE(var_index, MachineType::PointerRepresentation()); VARIABLE(var_index, MachineType::PointerRepresentation());
VARIABLE(var_unique, MachineRepresentation::kTagged); VARIABLE(var_unique, MachineRepresentation::kTagged);
Label keyisindex(this), if_iskeyunique(this); Label if_index(this), if_unique_name(this), if_notunique_name(this);
TryToName(key, &keyisindex, &var_index, &if_iskeyunique, &var_unique, TryToName(key, &if_index, &var_index, &if_unique_name, &var_unique,
&call_runtime); &call_runtime, &if_notunique_name);
BIND(&if_iskeyunique); BIND(&if_unique_name);
TryHasOwnProperty(object, map, instance_type, var_unique.value(), TryHasOwnProperty(object, map, instance_type, var_unique.value(),
&return_true, &return_false, &call_runtime); &return_true, &return_false, &call_runtime);
BIND(&keyisindex); BIND(&if_index);
// Handle negative keys in the runtime. {
GotoIf(IntPtrLessThan(var_index.value(), IntPtrConstant(0)), &call_runtime); // Handle negative keys in the runtime.
TryLookupElement(object, map, instance_type, var_index.value(), GotoIf(IntPtrLessThan(var_index.value(), IntPtrConstant(0)),
&return_true, &return_false, &return_false, &call_runtime); &call_runtime);
TryLookupElement(object, map, instance_type, var_index.value(),
&return_true, &return_false, &return_false,
&call_runtime);
}
BIND(&if_notunique_name);
{
// If the string was not found in the string table, then no object can
// have a property with that name, so return |false|.
TryInternalizeString(key, &if_index, &var_index, &if_unique_name,
&var_unique, &return_false, &call_runtime);
}
} }
BIND(&return_true); BIND(&return_true);
Return(BooleanConstant(true)); Return(BooleanConstant(true));
......
...@@ -4468,7 +4468,8 @@ void CodeStubAssembler::Use(Label* label) { ...@@ -4468,7 +4468,8 @@ void CodeStubAssembler::Use(Label* label) {
void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex, void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex,
Variable* var_index, Label* if_keyisunique, Variable* var_index, Label* if_keyisunique,
Variable* var_unique, Label* if_bailout) { Variable* var_unique, Label* if_bailout,
Label* if_notinternalized) {
DCHECK_EQ(MachineType::PointerRepresentation(), var_index->rep()); DCHECK_EQ(MachineType::PointerRepresentation(), var_index->rep());
DCHECK_EQ(MachineRepresentation::kTagged, var_unique->rep()); DCHECK_EQ(MachineRepresentation::kTagged, var_unique->rep());
Comment("TryToName"); Comment("TryToName");
...@@ -4507,7 +4508,8 @@ void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex, ...@@ -4507,7 +4508,8 @@ void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex,
STATIC_ASSERT(kNotInternalizedTag != 0); STATIC_ASSERT(kNotInternalizedTag != 0);
Node* not_internalized = Node* not_internalized =
Word32And(key_instance_type, Int32Constant(kIsNotInternalizedMask)); Word32And(key_instance_type, Int32Constant(kIsNotInternalizedMask));
GotoIf(Word32NotEqual(not_internalized, Int32Constant(0)), if_bailout); GotoIf(Word32NotEqual(not_internalized, Int32Constant(0)),
if_notinternalized != nullptr ? if_notinternalized : if_bailout);
Goto(if_keyisunique); Goto(if_keyisunique);
BIND(&if_thinstring); BIND(&if_thinstring);
...@@ -4519,6 +4521,30 @@ void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex, ...@@ -4519,6 +4521,30 @@ void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex,
Goto(if_keyisindex); Goto(if_keyisindex);
} }
void CodeStubAssembler::TryInternalizeString(
Node* string, Label* if_index, Variable* var_index, Label* if_internalized,
Variable* var_internalized, Label* if_not_internalized, Label* if_bailout) {
DCHECK(var_index->rep() == MachineType::PointerRepresentation());
DCHECK(var_internalized->rep() == MachineRepresentation::kTagged);
Node* function = ExternalConstant(
ExternalReference::try_internalize_string_function(isolate()));
Node* result = CallCFunction1(MachineType::AnyTagged(),
MachineType::AnyTagged(), function, string);
Label internalized(this);
GotoIf(TaggedIsNotSmi(result), &internalized);
Node* word_result = SmiUntag(result);
GotoIf(WordEqual(word_result, IntPtrConstant(ResultSentinel::kNotFound)),
if_not_internalized);
GotoIf(WordEqual(word_result, IntPtrConstant(ResultSentinel::kUnsupported)),
if_bailout);
var_index->Bind(word_result);
Goto(if_index);
BIND(&internalized);
var_internalized->Bind(result);
Goto(if_internalized);
}
template <typename Dictionary> template <typename Dictionary>
Node* CodeStubAssembler::EntryToIndex(Node* entry, int field_index) { Node* CodeStubAssembler::EntryToIndex(Node* entry, int field_index) {
Node* entry_index = IntPtrMul(entry, IntPtrConstant(Dictionary::kEntrySize)); Node* entry_index = IntPtrMul(entry, IntPtrConstant(Dictionary::kEntrySize));
......
...@@ -926,9 +926,24 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { ...@@ -926,9 +926,24 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
void Use(Label* label); void Use(Label* label);
// Various building blocks for stubs doing property lookups. // Various building blocks for stubs doing property lookups.
// |if_notinternalized| is optional; |if_bailout| will be used by default.
void TryToName(Node* key, Label* if_keyisindex, Variable* var_index, void TryToName(Node* key, Label* if_keyisindex, Variable* var_index,
Label* if_keyisunique, Variable* var_unique, Label* if_keyisunique, Variable* var_unique, Label* if_bailout,
Label* if_bailout); Label* if_notinternalized = nullptr);
// Performs a hash computation and string table lookup for the given string,
// and jumps to:
// - |if_index| if the string is an array index like "123"; |var_index|
// will contain the intptr representation of that index.
// - |if_internalized| if the string exists in the string table; the
// internalized version will be in |var_internalized|.
// - |if_not_internalized| if the string is not in the string table (but
// does not add it).
// - |if_bailout| for unsupported cases (e.g. uncachable array index).
void TryInternalizeString(Node* string, Label* if_index, Variable* var_index,
Label* if_internalized, Variable* var_internalized,
Label* if_not_internalized, Label* if_bailout);
// Calculates array index for given dictionary entry and entry field. // Calculates array index for given dictionary entry and entry field.
// See Dictionary::EntryToIndex(). // See Dictionary::EntryToIndex().
......
...@@ -709,6 +709,13 @@ Node* CodeAssembler::CallCFunctionN(Signature<MachineType>* signature, ...@@ -709,6 +709,13 @@ Node* CodeAssembler::CallCFunctionN(Signature<MachineType>* signature,
return raw_assembler()->CallN(desc, input_count, inputs); return raw_assembler()->CallN(desc, input_count, inputs);
} }
Node* CodeAssembler::CallCFunction1(MachineType return_type,
MachineType arg0_type, Node* function,
Node* arg0) {
return raw_assembler()->CallCFunction1(return_type, arg0_type, function,
arg0);
}
Node* CodeAssembler::CallCFunction2(MachineType return_type, Node* CodeAssembler::CallCFunction2(MachineType return_type,
MachineType arg0_type, MachineType arg0_type,
MachineType arg1_type, Node* function, MachineType arg1_type, Node* function,
......
...@@ -407,6 +407,10 @@ class V8_EXPORT_PRIVATE CodeAssembler { ...@@ -407,6 +407,10 @@ class V8_EXPORT_PRIVATE CodeAssembler {
Node* CallCFunctionN(Signature<MachineType>* signature, int input_count, Node* CallCFunctionN(Signature<MachineType>* signature, int input_count,
Node* const* inputs); Node* const* inputs);
// Call to a C function with one argument.
Node* CallCFunction1(MachineType return_type, MachineType arg0_type,
Node* function, Node* arg0);
// Call to a C function with two arguments. // Call to a C function with two arguments.
Node* CallCFunction2(MachineType return_type, MachineType arg0_type, Node* CallCFunction2(MachineType return_type, MachineType arg0_type,
MachineType arg1_type, Node* function, Node* arg0, MachineType arg1_type, Node* function, Node* arg0,
......
...@@ -237,6 +237,8 @@ void ExternalReferenceTable::AddReferences(Isolate* isolate) { ...@@ -237,6 +237,8 @@ void ExternalReferenceTable::AddReferences(Isolate* isolate) {
"libc_memcpy"); "libc_memcpy");
Add(ExternalReference::libc_memset_function(isolate).address(), Add(ExternalReference::libc_memset_function(isolate).address(),
"libc_memset"); "libc_memset");
Add(ExternalReference::try_internalize_string_function(isolate).address(),
"try_internalize_string_function");
Add(ExternalReference::log_enter_external_function(isolate).address(), Add(ExternalReference::log_enter_external_function(isolate).address(),
"Logger::EnterExternal"); "Logger::EnterExternal");
Add(ExternalReference::log_leave_external_function(isolate).address(), Add(ExternalReference::log_leave_external_function(isolate).address(),
......
...@@ -303,6 +303,9 @@ DEFINE_IMPLICATION(track_field_types, track_heap_object_fields) ...@@ -303,6 +303,9 @@ DEFINE_IMPLICATION(track_field_types, track_heap_object_fields)
DEFINE_BOOL(type_profile, false, "collect type information") DEFINE_BOOL(type_profile, false, "collect type information")
DEFINE_BOOL(feedback_normalization, false, DEFINE_BOOL(feedback_normalization, false,
"feed back normalization to constructors") "feed back normalization to constructors")
// TODO(jkummerow): This currently adds too much load on the stub cache.
DEFINE_BOOL_READONLY(internalize_on_the_fly, false,
"internalize string keys for generic keyed ICs on the fly")
// Flags for optimization types. // Flags for optimization types.
DEFINE_BOOL(optimize_for_size, false, DEFINE_BOOL(optimize_for_size, false,
......
...@@ -674,6 +674,8 @@ enum InlineCacheState { ...@@ -674,6 +674,8 @@ enum InlineCacheState {
enum WhereToStart { kStartAtReceiver, kStartAtPrototype }; enum WhereToStart { kStartAtReceiver, kStartAtPrototype };
enum ResultSentinel { kNotFound = -1, kUnsupported = -2 };
// The Store Buffer (GC). // The Store Buffer (GC).
typedef enum { typedef enum {
kStoreBufferFullEvent, kStoreBufferFullEvent,
......
...@@ -2090,15 +2090,15 @@ void AccessorAssembler::KeyedLoadICGeneric(const LoadICParameters* p) { ...@@ -2090,15 +2090,15 @@ void AccessorAssembler::KeyedLoadICGeneric(const LoadICParameters* p) {
VARIABLE(var_index, MachineType::PointerRepresentation()); VARIABLE(var_index, MachineType::PointerRepresentation());
VARIABLE(var_unique, MachineRepresentation::kTagged); VARIABLE(var_unique, MachineRepresentation::kTagged);
var_unique.Bind(p->name); // Dummy initialization. var_unique.Bind(p->name); // Dummy initialization.
Label if_index(this), if_unique_name(this), slow(this); Label if_index(this), if_unique_name(this), if_notunique(this), slow(this);
Node* receiver = p->receiver; Node* receiver = p->receiver;
GotoIf(TaggedIsSmi(receiver), &slow); GotoIf(TaggedIsSmi(receiver), &slow);
Node* receiver_map = LoadMap(receiver); Node* receiver_map = LoadMap(receiver);
Node* instance_type = LoadMapInstanceType(receiver_map); Node* instance_type = LoadMapInstanceType(receiver_map);
TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique, TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique, &slow,
&slow); &if_notunique);
BIND(&if_index); BIND(&if_index);
{ {
...@@ -2112,6 +2112,22 @@ void AccessorAssembler::KeyedLoadICGeneric(const LoadICParameters* p) { ...@@ -2112,6 +2112,22 @@ void AccessorAssembler::KeyedLoadICGeneric(const LoadICParameters* p) {
var_unique.value(), p, &slow); var_unique.value(), p, &slow);
} }
BIND(&if_notunique);
{
if (FLAG_internalize_on_the_fly) {
Label not_in_string_table(this);
TryInternalizeString(p->name, &if_index, &var_index, &if_unique_name,
&var_unique, &not_in_string_table, &slow);
BIND(&not_in_string_table);
// If the string was not found in the string table, then no object can
// have a property with that name.
Return(UndefinedConstant());
} else {
Goto(&slow);
}
}
BIND(&slow); BIND(&slow);
{ {
Comment("KeyedLoadGeneric_slow"); Comment("KeyedLoadGeneric_slow");
......
...@@ -833,6 +833,11 @@ bool String::HasOnlyOneByteChars() { ...@@ -833,6 +833,11 @@ bool String::HasOnlyOneByteChars() {
IsOneByteRepresentation(); IsOneByteRepresentation();
} }
bool StringShape::HasOnlyOneByteChars() {
return (type_ & kStringEncodingMask) == kOneByteStringTag ||
(type_ & kOneByteDataHintMask) == kOneByteDataHintTag;
}
bool StringShape::IsCons() { bool StringShape::IsCons() {
return (type_ & kStringRepresentationMask) == kConsStringTag; return (type_ & kStringRepresentationMask) == kConsStringTag;
} }
......
This diff is collapsed.
...@@ -7838,7 +7838,7 @@ class StringShape BASE_EMBEDDED { ...@@ -7838,7 +7838,7 @@ class StringShape BASE_EMBEDDED {
inline StringRepresentationTag representation_tag(); inline StringRepresentationTag representation_tag();
inline uint32_t encoding_tag(); inline uint32_t encoding_tag();
inline uint32_t full_representation_tag(); inline uint32_t full_representation_tag();
inline uint32_t size_tag(); inline bool HasOnlyOneByteChars();
#ifdef DEBUG #ifdef DEBUG
inline uint32_t type() { return type_; } inline uint32_t type() { return type_; }
inline void invalidate() { valid_ = false; } inline void invalidate() { valid_ = false; }
......
...@@ -58,6 +58,7 @@ class StringTable ...@@ -58,6 +58,7 @@ class StringTable
Isolate* isolate, Handle<String> str); Isolate* isolate, Handle<String> str);
MUST_USE_RESULT static MaybeHandle<String> LookupTwoCharsStringIfExists( MUST_USE_RESULT static MaybeHandle<String> LookupTwoCharsStringIfExists(
Isolate* isolate, uint16_t c1, uint16_t c2); Isolate* isolate, uint16_t c1, uint16_t c2);
static Object* LookupStringIfExists_NoAllocate(String* string);
static void EnsureCapacityForDeserialization(Isolate* isolate, int expected); static void EnsureCapacityForDeserialization(Isolate* isolate, int expected);
......
...@@ -313,7 +313,7 @@ class BitFieldBase { ...@@ -313,7 +313,7 @@ class BitFieldBase {
static const T kMax = static_cast<T>((kOne << size) - 1); static const T kMax = static_cast<T>((kOne << size) - 1);
// Tells whether the provided value fits into the bit field. // Tells whether the provided value fits into the bit field.
static bool is_valid(T value) { static constexpr bool is_valid(T value) {
return (static_cast<U>(value) & ~static_cast<U>(kMax)) == 0; return (static_cast<U>(value) & ~static_cast<U>(kMax)) == 0;
} }
......
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