Commit 923127f8 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[ic] Teach KeyedLoadICGeneric about ToName.

In the KeyedLoadICGeneric case the engine previously immediately fell
back to the %KeyedGetProperty runtime function if the key was not a
Name or a valid array index. This turns out to be really slow if a
program passes for example objects as keys. Since we already have all
the logic in place to convert an arbitrary JavaScript value to a Name,
we can just call into ToName first and then operate on the result of
that, which is significantly faster since C++ usually doesn't need to
call back into JavaScript then to convert a JSReceiver into a Name.

This also changes the ToName builtin to use the existing builtin for
NonPrimitiveToPrimitive, which stays in JavaScript land completely.
Since there's not really a point in inlining ToName into the call
sites, the other uses were also changed to call the builtin instead,
which saves some space and might also help with instruction cache
utilization (especially when the ToName logic is more involved now).

This improves the performance on the microbenchmark

```js
const n = 1e7;
const obj = {};
const key = [1,2];

const start = Date.now();
for (let i = 0; i < n; ++i) {
  if (obj[key] === undefined) obj[key] = key;
}
print(`time: ${Date.now() - start} ms.`);
```

by up to 36%. On the ARES-6 ML benchmark the steady state improves by up
to ~7% and the overall mean for ARES-6 ML improves by up to ~6%. Further
improvements might be possible here if the GetProperty builtin could be
made faster for common prototype lookups like Symbol.toPrimitive and the
"valueOf" and "toString" functions.

Bug: v8:6344, v8:6670
Change-Id: Ic3ac2bc4d4277836ef03039de4eda5c5f66a85da
Reviewed-on: https://chromium-review.googlesource.com/1199022
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55540}
parent 87199f52
......@@ -48,10 +48,8 @@ void ConversionBuiltinsAssembler::Generate_NonPrimitiveToPrimitive(
if_resultisnotprimitive(this, Label::kDeferred);
GotoIf(TaggedIsSmi(result), &if_resultisprimitive);
Node* result_instance_type = LoadInstanceType(result);
STATIC_ASSERT(FIRST_PRIMITIVE_TYPE == FIRST_TYPE);
Branch(Int32LessThanOrEqual(result_instance_type,
Int32Constant(LAST_PRIMITIVE_TYPE)),
&if_resultisprimitive, &if_resultisnotprimitive);
Branch(IsPrimitiveInstanceType(result_instance_type), &if_resultisprimitive,
&if_resultisnotprimitive);
BIND(&if_resultisprimitive);
{
......@@ -108,7 +106,62 @@ TF_BUILTIN(ToName, CodeStubAssembler) {
Node* context = Parameter(Descriptor::kContext);
Node* input = Parameter(Descriptor::kArgument);
Return(ToName(context, input));
VARIABLE(var_input, MachineRepresentation::kTagged, input);
Label loop(this, &var_input);
Goto(&loop);
BIND(&loop);
{
// Load the current {input} value.
Node* input = var_input.value();
// Dispatch based on the type of the {input.}
Label if_inputisbigint(this), if_inputisname(this), if_inputisnumber(this),
if_inputisoddball(this), if_inputisreceiver(this, Label::kDeferred);
GotoIf(TaggedIsSmi(input), &if_inputisnumber);
Node* input_instance_type = LoadInstanceType(input);
STATIC_ASSERT(FIRST_NAME_TYPE == FIRST_TYPE);
GotoIf(IsNameInstanceType(input_instance_type), &if_inputisname);
GotoIf(IsJSReceiverInstanceType(input_instance_type), &if_inputisreceiver);
GotoIf(IsHeapNumberInstanceType(input_instance_type), &if_inputisnumber);
Branch(IsBigIntInstanceType(input_instance_type), &if_inputisbigint,
&if_inputisoddball);
BIND(&if_inputisbigint);
{
// We don't have a fast-path for BigInt currently, so just
// tail call to the %ToString runtime function here for now.
TailCallRuntime(Runtime::kToString, context, input);
}
BIND(&if_inputisname);
{
// The {input} is already a Name.
Return(input);
}
BIND(&if_inputisnumber);
{
// Convert the String {input} to a Number.
TailCallBuiltin(Builtins::kNumberToString, context, input);
}
BIND(&if_inputisoddball);
{
// Just return the {input}'s string representation.
CSA_ASSERT(this, IsOddballInstanceType(input_instance_type));
Return(LoadObjectField(input, Oddball::kToStringOffset));
}
BIND(&if_inputisreceiver);
{
// Convert the JSReceiver {input} to a primitive first,
// and then run the loop again with the new {input},
// which is then a primitive value.
var_input.Bind(CallBuiltin(Builtins::kNonPrimitiveToPrimitive_String,
context, input));
Goto(&loop);
}
}
}
TF_BUILTIN(NonNumberToNumber, CodeStubAssembler) {
......@@ -205,10 +258,7 @@ void ConversionBuiltinsAssembler::Generate_OrdinaryToPrimitive(
// Return the {result} if it is a primitive.
GotoIf(TaggedIsSmi(result), &return_result);
Node* result_instance_type = LoadInstanceType(result);
STATIC_ASSERT(FIRST_PRIMITIVE_TYPE == FIRST_TYPE);
GotoIf(Int32LessThanOrEqual(result_instance_type,
Int32Constant(LAST_PRIMITIVE_TYPE)),
&return_result);
GotoIf(IsPrimitiveInstanceType(result_instance_type), &return_result);
}
// Just continue with the next {name} if the {method} is not callable.
......
......@@ -1522,7 +1522,7 @@ TF_BUILTIN(ObjectGetOwnPropertyDescriptor, ObjectBuiltinsAssembler) {
object = ToObject_Inline(CAST(context), CAST(object));
// 2. Let key be ? ToPropertyKey(P).
key = ToName(context, key);
key = CallBuiltin(Builtins::kToName, context, key);
// 3. Let desc be ? obj.[[GetOwnProperty]](key).
Label if_keyisindex(this), if_iskeyunique(this),
......
......@@ -5652,6 +5652,16 @@ TNode<BoolT> CodeStubAssembler::IsHeapNumber(SloppyTNode<HeapObject> object) {
return IsHeapNumberMap(LoadMap(object));
}
TNode<BoolT> CodeStubAssembler::IsHeapNumberInstanceType(
SloppyTNode<Int32T> instance_type) {
return InstanceTypeEqual(instance_type, HEAP_NUMBER_TYPE);
}
TNode<BoolT> CodeStubAssembler::IsOddballInstanceType(
SloppyTNode<Int32T> instance_type) {
return InstanceTypeEqual(instance_type, ODDBALL_TYPE);
}
TNode<BoolT> CodeStubAssembler::IsMutableHeapNumber(
SloppyTNode<HeapObject> object) {
return IsMutableHeapNumberMap(LoadMap(object));
......@@ -5667,8 +5677,12 @@ TNode<BoolT> CodeStubAssembler::IsFeedbackVector(
}
TNode<BoolT> CodeStubAssembler::IsName(SloppyTNode<HeapObject> object) {
return Int32LessThanOrEqual(LoadInstanceType(object),
Int32Constant(LAST_NAME_TYPE));
return IsNameInstanceType(LoadInstanceType(object));
}
TNode<BoolT> CodeStubAssembler::IsNameInstanceType(
SloppyTNode<Int32T> instance_type) {
return Int32LessThanOrEqual(instance_type, Int32Constant(LAST_NAME_TYPE));
}
TNode<BoolT> CodeStubAssembler::IsString(SloppyTNode<HeapObject> object) {
......@@ -6721,52 +6735,6 @@ TNode<String> CodeStubAssembler::NumberToString(TNode<Number> input) {
return result.value();
}
TNode<Name> CodeStubAssembler::ToName(SloppyTNode<Context> context,
SloppyTNode<Object> value) {
Label end(this);
TVARIABLE(Name, var_result);
Label is_number(this);
GotoIf(TaggedIsSmi(value), &is_number);
Label not_name(this);
TNode<Int32T> value_instance_type = LoadInstanceType(CAST(value));
STATIC_ASSERT(FIRST_NAME_TYPE == FIRST_TYPE);
GotoIf(Int32GreaterThan(value_instance_type, Int32Constant(LAST_NAME_TYPE)),
&not_name);
var_result = CAST(value);
Goto(&end);
BIND(&is_number);
{
var_result = CAST(CallBuiltin(Builtins::kNumberToString, context, value));
Goto(&end);
}
BIND(&not_name);
{
GotoIf(InstanceTypeEqual(value_instance_type, HEAP_NUMBER_TYPE),
&is_number);
Label not_oddball(this);
GotoIfNot(InstanceTypeEqual(value_instance_type, ODDBALL_TYPE),
&not_oddball);
var_result = LoadObjectField<String>(CAST(value), Oddball::kToStringOffset);
Goto(&end);
BIND(&not_oddball);
{
var_result = CAST(CallRuntime(Runtime::kToName, context, value));
Goto(&end);
}
}
BIND(&end);
return var_result.value();
}
Node* CodeStubAssembler::NonNumberToNumberOrNumeric(
Node* context, Node* input, Object::Conversion mode,
BigIntHandling bigint_handling) {
......@@ -11480,7 +11448,7 @@ TNode<Oddball> CodeStubAssembler::HasProperty(SloppyTNode<Context> context,
BIND(&if_proxy);
{
TNode<Name> name = ToName(context, key);
TNode<Name> name = CAST(CallBuiltin(Builtins::kToName, context, key));
switch (mode) {
case kHasProperty:
GotoIf(IsPrivateSymbol(name), &return_false);
......
......@@ -1760,6 +1760,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
TNode<BoolT> IsHashTable(SloppyTNode<HeapObject> object);
TNode<BoolT> IsEphemeronHashTable(SloppyTNode<HeapObject> object);
TNode<BoolT> IsHeapNumber(SloppyTNode<HeapObject> object);
TNode<BoolT> IsHeapNumberInstanceType(SloppyTNode<Int32T> instance_type);
TNode<BoolT> IsOddballInstanceType(SloppyTNode<Int32T> instance_type);
TNode<BoolT> IsIndirectStringInstanceType(SloppyTNode<Int32T> instance_type);
TNode<BoolT> IsJSArrayBuffer(SloppyTNode<HeapObject> object);
TNode<BoolT> IsJSDataView(TNode<HeapObject> object);
......@@ -1792,6 +1794,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
TNode<BoolT> IsMap(SloppyTNode<HeapObject> object);
TNode<BoolT> IsMutableHeapNumber(SloppyTNode<HeapObject> object);
TNode<BoolT> IsName(SloppyTNode<HeapObject> object);
TNode<BoolT> IsNameInstanceType(SloppyTNode<Int32T> instance_type);
TNode<BoolT> IsNativeContext(SloppyTNode<HeapObject> object);
TNode<BoolT> IsNullOrJSReceiver(SloppyTNode<HeapObject> object);
TNode<BoolT> IsNullOrUndefined(SloppyTNode<Object> object);
......@@ -1933,8 +1936,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
TNode<Number> StringToNumber(TNode<String> input);
// Convert a Number to a String.
TNode<String> NumberToString(TNode<Number> input);
// Convert an object to a name.
TNode<Name> ToName(SloppyTNode<Context> context, SloppyTNode<Object> value);
// Convert a Non-Number object to a Number.
TNode<Number> NonNumberToNumber(
SloppyTNode<Context> context, SloppyTNode<HeapObject> input,
......
......@@ -1455,7 +1455,7 @@ void AccessorAssembler::HandleStoreToProxy(const StoreICParameters* p,
TailCallRuntime(Runtime::kSetPropertyWithReceiver, p->context, proxy,
p->name, p->value, p->receiver, language_mode);
} else {
Node* name = ToName(p->context, p->name);
Node* name = CallBuiltin(Builtins::kToName, p->context, p->name);
TailCallBuiltin(Builtins::kProxySetProperty, p->context, proxy, name,
p->value, p->receiver, language_mode);
}
......@@ -2738,29 +2738,40 @@ void AccessorAssembler::KeyedLoadIC(const LoadICParameters* p) {
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), if_notunique(this), slow(this);
VARIABLE(var_unique, MachineRepresentation::kTagged, p->name);
Label if_index(this), if_unique_name(this), if_notunique(this),
if_other(this, Label::kDeferred), if_runtime(this, Label::kDeferred);
Node* receiver = p->receiver;
GotoIf(TaggedIsSmi(receiver), &slow);
Node* receiver_map = LoadMap(receiver);
Node* instance_type = LoadMapInstanceType(receiver_map);
GotoIf(TaggedIsSmi(receiver), &if_runtime);
TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique, &slow,
&if_notunique);
TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique,
&if_other, &if_notunique);
BIND(&if_other);
{
Node* name = CallBuiltin(Builtins::kToName, p->context, p->name);
var_unique.Bind(name);
TryToName(name, &if_index, &var_index, &if_unique_name, &var_unique,
&if_runtime, &if_notunique);
}
BIND(&if_index);
{
Node* receiver_map = LoadMap(receiver);
Node* instance_type = LoadMapInstanceType(receiver_map);
GenericElementLoad(receiver, receiver_map, instance_type, var_index.value(),
&slow);
&if_runtime);
}
BIND(&if_unique_name);
{
LoadICParameters pp = *p;
pp.name = var_unique.value();
GenericPropertyLoad(receiver, receiver_map, instance_type, &pp, &slow);
Node* receiver_map = LoadMap(receiver);
Node* instance_type = LoadMapInstanceType(receiver_map);
GenericPropertyLoad(receiver, receiver_map, instance_type, &pp,
&if_runtime);
}
BIND(&if_notunique);
......@@ -2769,10 +2780,11 @@ void AccessorAssembler::KeyedLoadICGeneric(const LoadICParameters* p) {
// Ideally we could return undefined directly here if the name is not
// found in the string table, i.e. it was never internalized, but that
// invariant doesn't hold with named property interceptors (at this
// point), so we take the {slow} path instead.
// point), so we take the {if_runtime} path instead.
Label if_in_string_table(this);
TryInternalizeString(p->name, &if_index, &var_index, &if_in_string_table,
&var_unique, &slow, &slow);
TryInternalizeString(var_unique.value(), &if_index, &var_index,
&if_in_string_table, &var_unique, &if_runtime,
&if_runtime);
BIND(&if_in_string_table);
{
......@@ -2783,21 +2795,23 @@ void AccessorAssembler::KeyedLoadICGeneric(const LoadICParameters* p) {
// cache. We may want to re-evaluate that in the future.
LoadICParameters pp = *p;
pp.name = var_unique.value();
GenericPropertyLoad(receiver, receiver_map, instance_type, &pp, &slow,
kDontUseStubCache);
Node* receiver_map = LoadMap(receiver);
Node* instance_type = LoadMapInstanceType(receiver_map);
GenericPropertyLoad(receiver, receiver_map, instance_type, &pp,
&if_runtime, kDontUseStubCache);
}
} else {
Goto(&slow);
Goto(&if_runtime);
}
}
BIND(&slow);
BIND(&if_runtime);
{
Comment("KeyedLoadGeneric_slow");
IncrementCounter(isolate()->counters()->ic_keyed_load_generic_slow(), 1);
// TODO(jkummerow): Should we use the GetProperty TF stub instead?
TailCallRuntime(Runtime::kKeyedGetProperty, p->context, p->receiver,
p->name);
var_unique.value());
}
}
......
......@@ -1274,7 +1274,7 @@ IGNITION_HANDLER(Negate, NegateAssemblerImpl) { UnaryOpWithFeedback(); }
IGNITION_HANDLER(ToName, InterpreterAssembler) {
Node* object = GetAccumulator();
Node* context = GetContext();
Node* result = ToName(context, object);
Node* result = CallBuiltin(Builtins::kToName, context, object);
StoreRegisterAtOperandIndex(result, 0);
Dispatch();
}
......
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