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) {
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) {
return ExternalReference(reinterpret_cast<Address>(page) +
MemoryChunk::kFlagsOffset);
......
......@@ -992,6 +992,8 @@ class ExternalReference BASE_EMBEDDED {
static ExternalReference libc_memcpy_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 ForDeoptEntry(Address entry);
......
......@@ -68,19 +68,31 @@ TF_BUILTIN(ObjectHasOwnProperty, ObjectBuiltinsAssembler) {
VARIABLE(var_index, MachineType::PointerRepresentation());
VARIABLE(var_unique, MachineRepresentation::kTagged);
Label keyisindex(this), if_iskeyunique(this);
TryToName(key, &keyisindex, &var_index, &if_iskeyunique, &var_unique,
&call_runtime);
Label if_index(this), if_unique_name(this), if_notunique_name(this);
TryToName(key, &if_index, &var_index, &if_unique_name, &var_unique,
&call_runtime, &if_notunique_name);
BIND(&if_iskeyunique);
BIND(&if_unique_name);
TryHasOwnProperty(object, map, instance_type, var_unique.value(),
&return_true, &return_false, &call_runtime);
BIND(&keyisindex);
// Handle negative keys in the runtime.
GotoIf(IntPtrLessThan(var_index.value(), IntPtrConstant(0)), &call_runtime);
TryLookupElement(object, map, instance_type, var_index.value(),
&return_true, &return_false, &return_false, &call_runtime);
BIND(&if_index);
{
// Handle negative keys in the runtime.
GotoIf(IntPtrLessThan(var_index.value(), IntPtrConstant(0)),
&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);
Return(BooleanConstant(true));
......
......@@ -4468,7 +4468,8 @@ void CodeStubAssembler::Use(Label* label) {
void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex,
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(MachineRepresentation::kTagged, var_unique->rep());
Comment("TryToName");
......@@ -4507,7 +4508,8 @@ void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex,
STATIC_ASSERT(kNotInternalizedTag != 0);
Node* not_internalized =
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);
BIND(&if_thinstring);
......@@ -4519,6 +4521,30 @@ void CodeStubAssembler::TryToName(Node* key, Label* 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>
Node* CodeStubAssembler::EntryToIndex(Node* entry, int field_index) {
Node* entry_index = IntPtrMul(entry, IntPtrConstant(Dictionary::kEntrySize));
......
......@@ -926,9 +926,24 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
void Use(Label* label);
// 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,
Label* if_keyisunique, Variable* var_unique,
Label* if_bailout);
Label* if_keyisunique, Variable* var_unique, 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.
// See Dictionary::EntryToIndex().
......
......@@ -709,6 +709,13 @@ Node* CodeAssembler::CallCFunctionN(Signature<MachineType>* signature,
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,
MachineType arg0_type,
MachineType arg1_type, Node* function,
......
......@@ -407,6 +407,10 @@ class V8_EXPORT_PRIVATE CodeAssembler {
Node* CallCFunctionN(Signature<MachineType>* signature, int input_count,
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.
Node* CallCFunction2(MachineType return_type, MachineType arg0_type,
MachineType arg1_type, Node* function, Node* arg0,
......
......@@ -237,6 +237,8 @@ void ExternalReferenceTable::AddReferences(Isolate* isolate) {
"libc_memcpy");
Add(ExternalReference::libc_memset_function(isolate).address(),
"libc_memset");
Add(ExternalReference::try_internalize_string_function(isolate).address(),
"try_internalize_string_function");
Add(ExternalReference::log_enter_external_function(isolate).address(),
"Logger::EnterExternal");
Add(ExternalReference::log_leave_external_function(isolate).address(),
......
......@@ -303,6 +303,9 @@ DEFINE_IMPLICATION(track_field_types, track_heap_object_fields)
DEFINE_BOOL(type_profile, false, "collect type information")
DEFINE_BOOL(feedback_normalization, false,
"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.
DEFINE_BOOL(optimize_for_size, false,
......
......@@ -674,6 +674,8 @@ enum InlineCacheState {
enum WhereToStart { kStartAtReceiver, kStartAtPrototype };
enum ResultSentinel { kNotFound = -1, kUnsupported = -2 };
// The Store Buffer (GC).
typedef enum {
kStoreBufferFullEvent,
......
......@@ -2090,15 +2090,15 @@ void AccessorAssembler::KeyedLoadICGeneric(const LoadICParameters* p) {
VARIABLE(var_index, MachineType::PointerRepresentation());
VARIABLE(var_unique, MachineRepresentation::kTagged);
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;
GotoIf(TaggedIsSmi(receiver), &slow);
Node* receiver_map = LoadMap(receiver);
Node* instance_type = LoadMapInstanceType(receiver_map);
TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique,
&slow);
TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique, &slow,
&if_notunique);
BIND(&if_index);
{
......@@ -2112,6 +2112,22 @@ void AccessorAssembler::KeyedLoadICGeneric(const LoadICParameters* p) {
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);
{
Comment("KeyedLoadGeneric_slow");
......
......@@ -833,6 +833,11 @@ bool String::HasOnlyOneByteChars() {
IsOneByteRepresentation();
}
bool StringShape::HasOnlyOneByteChars() {
return (type_ & kStringEncodingMask) == kOneByteStringTag ||
(type_ & kOneByteDataHintMask) == kOneByteDataHintTag;
}
bool StringShape::IsCons() {
return (type_ & kStringRepresentationMask) == kConsStringTag;
}
......
This diff is collapsed.
......@@ -7838,7 +7838,7 @@ class StringShape BASE_EMBEDDED {
inline StringRepresentationTag representation_tag();
inline uint32_t encoding_tag();
inline uint32_t full_representation_tag();
inline uint32_t size_tag();
inline bool HasOnlyOneByteChars();
#ifdef DEBUG
inline uint32_t type() { return type_; }
inline void invalidate() { valid_ = false; }
......
......@@ -58,6 +58,7 @@ class StringTable
Isolate* isolate, Handle<String> str);
MUST_USE_RESULT static MaybeHandle<String> LookupTwoCharsStringIfExists(
Isolate* isolate, uint16_t c1, uint16_t c2);
static Object* LookupStringIfExists_NoAllocate(String* string);
static void EnsureCapacityForDeserialization(Isolate* isolate, int expected);
......
......@@ -313,7 +313,7 @@ class BitFieldBase {
static const T kMax = static_cast<T>((kOne << size) - 1);
// 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;
}
......
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