Revert "Optimize in operator"

This reverts commit 32fc0acfef.

Reason for revert:

https://ci.chromium.org/p/v8/builders/luci.v8.ci/V8-Blink%20Linux%2064/30270

layout test breakage:

https://test-results.appspot.com/data/layout_results/V8-Blink_Linux_64/30270/webkit_layout_tests%20%28with%20patch%29/layout-test-results/results.html

There is a dead node arriving in representation selection, which might indicate that the problem is not in this CL, but that this CL stirs up the node soup in such a way that dead code elimination gets confused.

Original change's description:
> Optimize `in` operator
> 
> This change implements optimizations for the `in` operator for packed array
> elements and object properties. It adds a new feedback slot kind and an IC
> path similar to KeyedLoadIC for handling the lookups. TurboFan uses the
> feedback to optimize based on the maps and keys.
> 
> For more details see:
> https://docs.google.com/document/d/1tIfzywY8AeNVcy_sen-5Xev21MeZwjcU8QhSdzHvXig
> 
> This can provide 10x performance improvements of on loops of the form:
> 
>     for (let i = 0; i < ary.length; ++i) {
>       if (i in ary) {
>         ...
>       }
>     }
> 
> 
> Bug: v8:8733
> Change-Id: I766bf865a547a059e5bce5399bb6112e5d9a85c8
> Reviewed-on: https://chromium-review.googlesource.com/c/1432598
> Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> Reviewed-by: Igor Sheludko <ishell@chromium.org>
> Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
> Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
> Reviewed-by: Toon Verwaest <verwaest@chromium.org>
> Commit-Queue: Matt Gardner <magardn@microsoft.com>
> Cr-Commit-Position: refs/heads/master@{#59843}

TBR=ulan@chromium.org,rmcilroy@chromium.org,jkummerow@chromium.org,jarin@chromium.org,ishell@chromium.org,bmeurer@chromium.org,verwaest@chromium.org,magardn@microsoft.com

Change-Id: Ib2db974e5bed4c4a2b6b450f796bdc4b0b8fd562
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: v8:8733
Reviewed-on: https://chromium-review.googlesource.com/c/1488761
Reviewed-by: Sigurd Schneider <sigurds@chromium.org>
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59857}
This commit is contained in:
Sigurd Schneider 2019-02-26 10:40:14 +00:00 committed by Commit Bot
parent f94cd449e4
commit d2729be4ae
38 changed files with 363 additions and 1620 deletions

View File

@ -251,10 +251,6 @@ namespace internal {
TFH(ElementsTransitionAndStore_GrowNoTransitionHandleCOW, StoreTransition) \
TFH(ElementsTransitionAndStore_NoTransitionIgnoreOOB, StoreTransition) \
TFH(ElementsTransitionAndStore_NoTransitionHandleCOW, StoreTransition) \
TFH(KeyedHasIC_PolymorphicName, LoadWithVector) \
TFH(KeyedHasIC_SloppyArguments, LoadWithVector) \
TFH(HasIndexedInterceptorIC, LoadWithVector) \
TFH(HasIC_Slow, LoadWithVector) \
\
/* Microtask helpers */ \
TFS(EnqueueMicrotask, kMicrotask) \
@ -630,8 +626,6 @@ namespace internal {
TFH(LoadGlobalICInsideTypeofTrampoline, LoadGlobal) \
TFH(CloneObjectIC, CloneObjectWithVector) \
TFH(CloneObjectIC_Slow, CloneObjectWithVector) \
TFH(KeyedHasIC, LoadWithVector) \
TFH(KeyedHasIC_Megamorphic, LoadWithVector) \
\
/* IterableToList */ \
/* ES #sec-iterabletolist */ \

View File

@ -493,50 +493,5 @@ TF_BUILTIN(LoadIndexedInterceptorIC, CodeStubAssembler) {
vector);
}
TF_BUILTIN(KeyedHasIC_SloppyArguments, CodeStubAssembler) {
Node* receiver = Parameter(Descriptor::kReceiver);
Node* key = Parameter(Descriptor::kName);
Node* slot = Parameter(Descriptor::kSlot);
Node* vector = Parameter(Descriptor::kVector);
Node* context = Parameter(Descriptor::kContext);
Label miss(this);
Node* result = HasKeyedSloppyArguments(receiver, key, &miss);
Return(result);
BIND(&miss);
{
Comment("Miss");
TailCallRuntime(Runtime::kKeyedHasIC_Miss, context, receiver, key, slot,
vector);
}
}
TF_BUILTIN(HasIndexedInterceptorIC, CodeStubAssembler) {
Node* receiver = Parameter(Descriptor::kReceiver);
Node* key = Parameter(Descriptor::kName);
Node* slot = Parameter(Descriptor::kSlot);
Node* vector = Parameter(Descriptor::kVector);
Node* context = Parameter(Descriptor::kContext);
Label if_keyispositivesmi(this), if_keyisinvalid(this);
Branch(TaggedIsPositiveSmi(key), &if_keyispositivesmi, &if_keyisinvalid);
BIND(&if_keyispositivesmi);
TailCallRuntime(Runtime::kHasElementWithInterceptor, context, receiver, key);
BIND(&if_keyisinvalid);
TailCallRuntime(Runtime::kKeyedHasIC_Miss, context, receiver, key, slot,
vector);
}
TF_BUILTIN(HasIC_Slow, CodeStubAssembler) {
Node* receiver = Parameter(Descriptor::kReceiver);
Node* name = Parameter(Descriptor::kName);
Node* context = Parameter(Descriptor::kContext);
TailCallRuntime(Runtime::kHasProperty, context, receiver, name);
}
} // namespace internal
} // namespace v8

View File

@ -40,9 +40,6 @@ IC_BUILTIN(KeyedStoreICTrampoline)
IC_BUILTIN(StoreInArrayLiteralIC)
IC_BUILTIN(CloneObjectIC)
IC_BUILTIN(CloneObjectIC_Slow)
IC_BUILTIN(KeyedHasIC)
IC_BUILTIN(KeyedHasIC_Megamorphic)
IC_BUILTIN(KeyedHasIC_PolymorphicName)
IC_BUILTIN_PARAM(LoadGlobalIC, LoadGlobalIC, NOT_INSIDE_TYPEOF)
IC_BUILTIN_PARAM(LoadGlobalICInsideTypeof, LoadGlobalIC, INSIDE_TYPEOF)

View File

@ -10123,9 +10123,8 @@ TNode<IntPtrT> CodeStubAssembler::TryToIntptr(Node* key, Label* miss) {
return var_intptr_key.value();
}
Node* CodeStubAssembler::EmitKeyedSloppyArguments(
Node* receiver, Node* key, Node* value, Label* bailout,
ArgumentsAccessMode access_mode) {
Node* CodeStubAssembler::EmitKeyedSloppyArguments(Node* receiver, Node* key,
Node* value, Label* bailout) {
// Mapped arguments are actual arguments. Unmapped arguments are values added
// to the arguments object after it was created for the call. Mapped arguments
// are stored in the context at indexes given by elements[key + 2]. Unmapped
@ -10152,6 +10151,8 @@ Node* CodeStubAssembler::EmitKeyedSloppyArguments(
// index into the context array given at elements[0]. Return the value at
// context[t].
bool is_load = value == nullptr;
GotoIfNot(TaggedIsSmi(key), bailout);
key = SmiUntag(key);
GotoIf(IntPtrLessThan(key, IntPtrConstant(0)), bailout);
@ -10160,11 +10161,8 @@ Node* CodeStubAssembler::EmitKeyedSloppyArguments(
TNode<IntPtrT> elements_length = LoadAndUntagFixedArrayBaseLength(elements);
VARIABLE(var_result, MachineRepresentation::kTagged);
if (access_mode == ArgumentsAccessMode::kStore) {
if (!is_load) {
var_result.Bind(value);
} else {
DCHECK(access_mode == ArgumentsAccessMode::kLoad ||
access_mode == ArgumentsAccessMode::kHas);
}
Label if_mapped(this), if_unmapped(this), end(this, &var_result);
Node* intptr_two = IntPtrConstant(2);
@ -10180,14 +10178,10 @@ Node* CodeStubAssembler::EmitKeyedSloppyArguments(
{
TNode<IntPtrT> mapped_index_intptr = SmiUntag(CAST(mapped_index));
TNode<Context> the_context = CAST(LoadFixedArrayElement(elements, 0));
if (access_mode == ArgumentsAccessMode::kLoad) {
if (is_load) {
Node* result = LoadContextElement(the_context, mapped_index_intptr);
CSA_ASSERT(this, WordNotEqual(result, TheHoleConstant()));
var_result.Bind(result);
} else if (access_mode == ArgumentsAccessMode::kHas) {
CSA_ASSERT(this, Word32BinaryNot(IsTheHole(LoadContextElement(
the_context, mapped_index_intptr))));
var_result.Bind(TrueConstant());
} else {
StoreContextElement(the_context, mapped_index_intptr, value);
}
@ -10204,31 +10198,17 @@ Node* CodeStubAssembler::EmitKeyedSloppyArguments(
TNode<IntPtrT> backing_store_length =
LoadAndUntagFixedArrayBaseLength(backing_store);
if (access_mode == ArgumentsAccessMode::kHas) {
Label out_of_bounds(this);
GotoIf(UintPtrGreaterThanOrEqual(key, backing_store_length),
&out_of_bounds);
GotoIf(UintPtrGreaterThanOrEqual(key, backing_store_length), bailout);
// The key falls into unmapped range.
if (is_load) {
Node* result = LoadFixedArrayElement(backing_store, key);
var_result.Bind(
SelectBooleanConstant(WordNotEqual(result, TheHoleConstant())));
Goto(&end);
BIND(&out_of_bounds);
var_result.Bind(FalseConstant());
Goto(&end);
GotoIf(WordEqual(result, TheHoleConstant()), bailout);
var_result.Bind(result);
} else {
GotoIf(UintPtrGreaterThanOrEqual(key, backing_store_length), bailout);
// The key falls into unmapped range.
if (access_mode == ArgumentsAccessMode::kLoad) {
Node* result = LoadFixedArrayElement(backing_store, key);
GotoIf(WordEqual(result, TheHoleConstant()), bailout);
var_result.Bind(result);
} else {
StoreFixedArrayElement(backing_store, key, value);
}
Goto(&end);
StoreFixedArrayElement(backing_store, key, value);
}
Goto(&end);
}
BIND(&end);
@ -11032,63 +11012,63 @@ void CodeStubAssembler::BranchIfNumberRelationalComparison(
TVARIABLE(Float64T, var_left_float);
TVARIABLE(Float64T, var_right_float);
Branch(
TaggedIsSmi(left),
[&] {
TNode<Smi> smi_left = CAST(left);
Branch(TaggedIsSmi(left),
[&] {
TNode<Smi> smi_left = CAST(left);
Branch(
TaggedIsSmi(right),
[&] {
TNode<Smi> smi_right = CAST(right);
Branch(TaggedIsSmi(right),
[&] {
TNode<Smi> smi_right = CAST(right);
// Both {left} and {right} are Smi, so just perform a fast
// Smi comparison.
switch (op) {
case Operation::kEqual:
BranchIfSmiEqual(smi_left, smi_right, if_true, if_false);
break;
case Operation::kLessThan:
BranchIfSmiLessThan(smi_left, smi_right, if_true, if_false);
break;
case Operation::kLessThanOrEqual:
BranchIfSmiLessThanOrEqual(smi_left, smi_right, if_true,
if_false);
break;
case Operation::kGreaterThan:
BranchIfSmiLessThan(smi_right, smi_left, if_true, if_false);
break;
case Operation::kGreaterThanOrEqual:
BranchIfSmiLessThanOrEqual(smi_right, smi_left, if_true,
if_false);
break;
default:
UNREACHABLE();
}
},
[&] {
CSA_ASSERT(this, IsHeapNumber(right));
var_left_float = SmiToFloat64(smi_left);
var_right_float = LoadHeapNumberValue(right);
Goto(&do_float_comparison);
});
},
[&] {
CSA_ASSERT(this, IsHeapNumber(left));
var_left_float = LoadHeapNumberValue(left);
// Both {left} and {right} are Smi, so just perform a fast
// Smi comparison.
switch (op) {
case Operation::kEqual:
BranchIfSmiEqual(smi_left, smi_right, if_true,
if_false);
break;
case Operation::kLessThan:
BranchIfSmiLessThan(smi_left, smi_right, if_true,
if_false);
break;
case Operation::kLessThanOrEqual:
BranchIfSmiLessThanOrEqual(smi_left, smi_right, if_true,
if_false);
break;
case Operation::kGreaterThan:
BranchIfSmiLessThan(smi_right, smi_left, if_true,
if_false);
break;
case Operation::kGreaterThanOrEqual:
BranchIfSmiLessThanOrEqual(smi_right, smi_left, if_true,
if_false);
break;
default:
UNREACHABLE();
}
},
[&] {
CSA_ASSERT(this, IsHeapNumber(right));
var_left_float = SmiToFloat64(smi_left);
var_right_float = LoadHeapNumberValue(right);
Goto(&do_float_comparison);
});
},
[&] {
CSA_ASSERT(this, IsHeapNumber(left));
var_left_float = LoadHeapNumberValue(left);
Branch(
TaggedIsSmi(right),
[&] {
var_right_float = SmiToFloat64(right);
Goto(&do_float_comparison);
},
[&] {
CSA_ASSERT(this, IsHeapNumber(right));
var_right_float = LoadHeapNumberValue(right);
Goto(&do_float_comparison);
});
});
Branch(TaggedIsSmi(right),
[&] {
var_right_float = SmiToFloat64(right);
Goto(&do_float_comparison);
},
[&] {
CSA_ASSERT(this, IsHeapNumber(right));
var_right_float = LoadHeapNumberValue(right);
Goto(&do_float_comparison);
});
});
BIND(&do_float_comparison);
{

View File

@ -2892,26 +2892,16 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<Map> LoadReceiverMap(SloppyTNode<Object> receiver);
enum class ArgumentsAccessMode { kLoad, kStore, kHas };
// Emits keyed sloppy arguments has. Returns whether the key is in the
// arguments.
Node* HasKeyedSloppyArguments(Node* receiver, Node* key, Label* bailout) {
return EmitKeyedSloppyArguments(receiver, key, nullptr, bailout,
ArgumentsAccessMode::kHas);
}
// Emits keyed sloppy arguments load. Returns either the loaded value.
Node* LoadKeyedSloppyArguments(Node* receiver, Node* key, Label* bailout) {
return EmitKeyedSloppyArguments(receiver, key, nullptr, bailout,
ArgumentsAccessMode::kLoad);
return EmitKeyedSloppyArguments(receiver, key, nullptr, bailout);
}
// Emits keyed sloppy arguments store.
void StoreKeyedSloppyArguments(Node* receiver, Node* key, Node* value,
Label* bailout) {
DCHECK_NOT_NULL(value);
EmitKeyedSloppyArguments(receiver, key, value, bailout,
ArgumentsAccessMode::kStore);
EmitKeyedSloppyArguments(receiver, key, value, bailout);
}
// Loads script context from the script context table.
@ -3419,8 +3409,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
// Emits keyed sloppy arguments load if the |value| is nullptr or store
// otherwise. Returns either the loaded value or |value|.
Node* EmitKeyedSloppyArguments(Node* receiver, Node* key, Node* value,
Label* bailout,
ArgumentsAccessMode access_mode);
Label* bailout);
TNode<String> AllocateSlicedString(RootIndex map_root_index,
TNode<Uint32T> length,

View File

@ -63,8 +63,6 @@ std::ostream& operator<<(std::ostream& os, AccessMode access_mode) {
return os << "Store";
case AccessMode::kStoreInLiteral:
return os << "StoreInLiteral";
case AccessMode::kHas:
return os << "Has";
}
UNREACHABLE();
}
@ -179,7 +177,6 @@ bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
if (this->field_index_.GetFieldAccessStubKey() ==
that->field_index_.GetFieldAccessStubKey()) {
switch (access_mode) {
case AccessMode::kHas:
case AccessMode::kLoad: {
if (this->field_representation_ != that->field_representation_) {
if (!IsAnyTagged(this->field_representation_) ||
@ -309,7 +306,7 @@ void ProcessFeedbackMaps(Isolate* isolate, MapHandles const& maps,
bool AccessInfoFactory::ComputeElementAccessInfos(
MapHandles const& maps, AccessMode access_mode,
ZoneVector<ElementAccessInfo>* access_infos) const {
if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
if (access_mode == AccessMode::kLoad) {
// For polymorphic loads of similar elements kinds (i.e. all tagged or all
// double), always use the "worst case" code without a transition. This is
// much faster than transitioning the elements to the worst case, trading a
@ -417,12 +414,6 @@ bool AccessInfoFactory::ComputeAccessorDescriptorAccessInfo(
PropertyAccessInfo::ModuleExport(MapHandles{receiver_map}, cell);
return true;
}
if (access_mode == AccessMode::kHas) {
// HasProperty checks don't call getter/setters, existence is sufficient.
*access_info = PropertyAccessInfo::AccessorConstant(
MapHandles{receiver_map}, Handle<Object>(), holder);
return true;
}
Handle<Object> accessors(descriptors->GetStrongValue(number), isolate());
if (!accessors->IsAccessorPair()) return false;
Handle<Object> accessor(access_mode == AccessMode::kLoad
@ -465,13 +456,11 @@ bool AccessInfoFactory::ComputePropertyAccessInfo(
PropertyAccessInfo* access_info) const {
CHECK(name->IsUniqueName());
if (access_mode == AccessMode::kHas && !map->IsJSReceiverMap()) return false;
// Check if it is safe to inline property access for the {map}.
if (!CanInlinePropertyAccess(map)) return false;
// We support fast inline cases for certain JSObject getters.
if ((access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) &&
if (access_mode == AccessMode::kLoad &&
LookupSpecialFieldAccessor(map, name, access_info)) {
return true;
}

View File

@ -29,7 +29,7 @@ class TypeCache;
// Whether we are loading a property or storing to a property.
// For a store during literal creation, do not walk up the prototype chain.
enum class AccessMode { kLoad, kStore, kStoreInLiteral, kHas };
enum class AccessMode { kLoad, kStore, kStoreInLiteral };
std::ostream& operator<<(std::ostream&, AccessMode);

View File

@ -2542,9 +2542,7 @@ void BytecodeGraphBuilder::VisitTestIn() {
Node* object = environment()->LookupAccumulator();
Node* key =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
VectorSlotPair feedback =
CreateVectorSlotPair(bytecode_iterator().GetIndexOperand(1));
Node* node = NewNode(javascript()->HasProperty(feedback), object, key);
Node* node = NewNode(javascript()->HasProperty(), object, key);
environment()->BindAccumulator(node, Environment::kAttachFrameState);
}

View File

@ -944,10 +944,9 @@ Reduction JSCallReducer::ReduceReflectHas(Node* node) {
Node* etrue = effect;
Node* vtrue;
{
// TODO(magardn): collect feedback so this can be optimized
vtrue = etrue = if_true =
graph()->NewNode(javascript()->HasProperty(VectorSlotPair()), target,
key, context, frame_state, etrue, if_true);
graph()->NewNode(javascript()->HasProperty(), target, key, context,
frame_state, etrue, if_true);
}
// Rewire potential exception edges.

View File

@ -106,8 +106,6 @@ Reduction JSNativeContextSpecialization::Reduce(Node* node) {
return ReduceJSLoadNamed(node);
case IrOpcode::kJSStoreNamed:
return ReduceJSStoreNamed(node);
case IrOpcode::kJSHasProperty:
return ReduceJSHasProperty(node);
case IrOpcode::kJSLoadProperty:
return ReduceJSLoadProperty(node);
case IrOpcode::kJSStoreProperty:
@ -210,7 +208,7 @@ bool IsStringConstant(JSHeapBroker* broker, Node* node) {
HeapObjectMatcher matcher(node);
return matcher.HasValue() && matcher.Ref(broker).IsString();
}
} // namespace
}
Reduction JSNativeContextSpecialization::ReduceJSAsyncFunctionEnter(
Node* node) {
@ -1110,8 +1108,7 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
node->opcode() == IrOpcode::kJSStoreNamed ||
node->opcode() == IrOpcode::kJSLoadProperty ||
node->opcode() == IrOpcode::kJSStoreProperty ||
node->opcode() == IrOpcode::kJSStoreNamedOwn ||
node->opcode() == IrOpcode::kJSHasProperty);
node->opcode() == IrOpcode::kJSStoreNamedOwn);
Node* receiver = NodeProperties::GetValueInput(node, 0);
Node* context = NodeProperties::GetContextInput(node);
Node* frame_state = NodeProperties::GetFrameStateInput(node);
@ -1447,6 +1444,7 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
AccessMode::kLoad);
}
Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) {
DCHECK_EQ(IrOpcode::kJSStoreNamed, node->opcode());
NamedAccess const& p = NamedAccessOf(node->op());
@ -1507,8 +1505,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
KeyedAccessStoreMode store_mode) {
DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
node->opcode() == IrOpcode::kJSStoreProperty ||
node->opcode() == IrOpcode::kJSStoreInArrayLiteral ||
node->opcode() == IrOpcode::kJSHasProperty);
node->opcode() == IrOpcode::kJSStoreInArrayLiteral);
Node* receiver = NodeProperties::GetValueInput(node, 0);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
@ -1569,17 +1566,6 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
for (Handle<Map> prototype_map : prototype_maps) {
dependencies()->DependOnStableMap(MapRef(broker(), prototype_map));
}
} else if (access_mode == AccessMode::kHas) {
// If we have any fast arrays, we need to check and depend on
// NoElementsProtector.
for (ElementAccessInfo const& access_info : access_infos) {
if (IsFastElementsKind(access_info.elements_kind())) {
if (!isolate()->IsNoElementsProtectorIntact()) return NoChange();
dependencies()->DependOnProtector(
PropertyCellRef(broker(), factory()->no_elements_protector()));
break;
}
}
}
// Ensure that {receiver} is a heap object.
@ -1718,7 +1704,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
}
Reduction JSNativeContextSpecialization::ReduceKeyedLoadFromHeapConstant(
Node* node, Node* index, FeedbackNexus const& nexus, AccessMode access_mode,
Node* node, Node* index, FeedbackNexus const& nexus,
KeyedAccessLoadMode load_mode) {
DCHECK_EQ(node->opcode(), IrOpcode::kJSLoadProperty);
Node* receiver = NodeProperties::GetValueInput(node, 0);
@ -1729,8 +1715,7 @@ Reduction JSNativeContextSpecialization::ReduceKeyedLoadFromHeapConstant(
HeapObjectRef receiver_ref = mreceiver.Ref(broker()).AsHeapObject();
if (receiver_ref.map().oddball_type() == OddballType::kHole ||
receiver_ref.map().oddball_type() == OddballType::kNull ||
receiver_ref.map().oddball_type() == OddballType::kUndefined ||
(receiver_ref.map().IsString() && access_mode == AccessMode::kHas)) {
receiver_ref.map().oddball_type() == OddballType::kUndefined) {
return NoChange();
}
@ -1747,9 +1732,7 @@ Reduction JSNativeContextSpecialization::ReduceKeyedLoadFromHeapConstant(
// We can safely constant-fold the {index} access to {receiver},
// since the element is non-configurable, non-writable and thus
// cannot change anymore.
Node* value = access_mode == AccessMode::kHas
? jsgraph()->TrueConstant()
: jsgraph()->Constant(it.GetDataValue());
Node* value = jsgraph()->Constant(it.GetDataValue());
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
@ -1778,9 +1761,7 @@ Reduction JSNativeContextSpecialization::ReduceKeyedLoadFromHeapConstant(
effect = graph()->NewNode(
simplified()->CheckIf(DeoptimizeReason::kCowArrayElementsChanged),
check, effect, control);
Node* value = access_mode == AccessMode::kHas
? jsgraph()->TrueConstant()
: jsgraph()->Constant(it.GetDataValue());
Node* value = jsgraph()->Constant(it.GetDataValue());
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
@ -1790,7 +1771,7 @@ Reduction JSNativeContextSpecialization::ReduceKeyedLoadFromHeapConstant(
// For constant Strings we can eagerly strength-reduce the keyed
// accesses using the known length, which doesn't change.
if (receiver_ref.IsString() && access_mode != AccessMode::kHas) {
if (receiver_ref.IsString()) {
// We can only assume that the {index} is a valid array index if the
// IC is in element access mode and not MEGAMORPHIC, otherwise there's
// no guard for the bounds check below.
@ -1816,16 +1797,14 @@ Reduction JSNativeContextSpecialization::ReduceKeyedAccess(
AccessMode access_mode, KeyedAccessLoadMode load_mode,
KeyedAccessStoreMode store_mode) {
DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
node->opcode() == IrOpcode::kJSStoreProperty ||
node->opcode() == IrOpcode::kJSHasProperty);
node->opcode() == IrOpcode::kJSStoreProperty);
Node* receiver = NodeProperties::GetValueInput(node, 0);
Node* effect = NodeProperties::GetEffectInput(node);
if (access_mode == AccessMode::kLoad &&
receiver->opcode() == IrOpcode::kHeapConstant) {
Reduction reduction = ReduceKeyedLoadFromHeapConstant(
node, index, nexus, access_mode, load_mode);
Reduction reduction =
ReduceKeyedLoadFromHeapConstant(node, index, nexus, load_mode);
if (reduction.Changed()) return reduction;
}
@ -1900,21 +1879,6 @@ Reduction JSNativeContextSpecialization::ReduceSoftDeoptimize(
return NoChange();
}
Reduction JSNativeContextSpecialization::ReduceJSHasProperty(Node* node) {
DCHECK_EQ(IrOpcode::kJSHasProperty, node->opcode());
PropertyAccess const& p = PropertyAccessOf(node->op());
Node* index = NodeProperties::GetValueInput(node, 1);
Node* value = jsgraph()->Dead();
// Extract receiver maps from the has property IC using the FeedbackNexus.
if (!p.feedback().IsValid()) return NoChange();
FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot());
// Try to lower the keyed access based on the {nexus}.
return ReduceKeyedAccess(node, index, value, nexus, AccessMode::kHas,
STANDARD_LOAD, STANDARD_STORE);
}
Reduction JSNativeContextSpecialization::ReduceJSLoadPropertyWithEnumeratedKey(
Node* node) {
// We can optimize a property load if it's being used inside a for..in:
@ -2224,22 +2188,6 @@ JSNativeContextSpecialization::BuildPropertyLoad(
return ValueEffectControl(value, effect, control);
}
JSNativeContextSpecialization::ValueEffectControl
JSNativeContextSpecialization::BuildPropertyTest(
Node* effect, Node* control, PropertyAccessInfo const& access_info) {
// Determine actual holder and perform prototype chain checks.
Handle<JSObject> holder;
if (access_info.holder().ToHandle(&holder)) {
dependencies()->DependOnStablePrototypeChains(
broker(), access_info.receiver_maps(), JSObjectRef(broker(), holder));
}
Node* value = access_info.IsNotFound() ? jsgraph()->FalseConstant()
: jsgraph()->TrueConstant();
return ValueEffectControl(value, effect, control);
}
JSNativeContextSpecialization::ValueEffectControl
JSNativeContextSpecialization::BuildPropertyAccess(
Node* receiver, Node* value, Node* context, Node* frame_state, Node* effect,
@ -2254,8 +2202,6 @@ JSNativeContextSpecialization::BuildPropertyAccess(
return BuildPropertyStore(receiver, value, context, frame_state, effect,
control, name, if_exceptions, access_info,
access_mode);
case AccessMode::kHas:
return BuildPropertyTest(effect, control, access_info);
}
UNREACHABLE();
return ValueEffectControl();
@ -2624,6 +2570,7 @@ JSNativeContextSpecialization::BuildElementAccess(
Node* receiver, Node* index, Node* value, Node* effect, Node* control,
ElementAccessInfo const& access_info, AccessMode access_mode,
KeyedAccessLoadMode load_mode, KeyedAccessStoreMode store_mode) {
// TODO(bmeurer): We currently specialize based on elements kind. We should
// also be able to properly support strings and other JSObjects here.
ElementsKind elements_kind = access_info.elements_kind();
@ -2723,7 +2670,7 @@ JSNativeContextSpecialization::BuildElementAccess(
// below are performed on unsigned values, which means that all the
// Negative32 values are treated as out-of-bounds.
index = graph()->NewNode(simplified()->NumberToUint32(), index);
} else if (access_mode != AccessMode::kHas) {
} else {
// Check that the {index} is in the valid range for the {receiver}.
index = effect =
graph()->NewNode(simplified()->CheckBounds(VectorSlotPair()), index,
@ -2828,13 +2775,6 @@ JSNativeContextSpecialization::BuildElementAccess(
}
break;
}
case AccessMode::kHas:
// For has property on a typed array, all we need is a bounds check.
value = effect =
graph()->NewNode(simplified()->SpeculativeNumberLessThan(
NumberOperationHint::kSignedSmall),
index, length, effect, control);
break;
}
} else {
// Load the elements for the {receiver}.
@ -2881,7 +2821,7 @@ JSNativeContextSpecialization::BuildElementAccess(
index = effect = graph()->NewNode(
simplified()->CheckBounds(VectorSlotPair()), index,
jsgraph()->Constant(Smi::kMaxValue), effect, control);
} else if (access_mode != AccessMode::kHas) {
} else {
// Check that the {index} is in the valid range for the {receiver}.
index = effect =
graph()->NewNode(simplified()->CheckBounds(VectorSlotPair()), index,
@ -3001,58 +2941,6 @@ JSNativeContextSpecialization::BuildElementAccess(
effect, control);
}
}
} else if (access_mode == AccessMode::kHas) {
// For packed arrays with NoElementsProctector valid, a bound check
// is equivalent to HasProperty.
value = effect = graph()->NewNode(simplified()->SpeculativeNumberLessThan(
NumberOperationHint::kSignedSmall),
index, length, effect, control);
if (IsHoleyElementsKind(elements_kind)) {
// If the index is in bounds, do a load and hole check.
Node* branch = graph()->NewNode(common()->Branch(), value, control);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* efalse = effect;
Node* vfalse = jsgraph()->FalseConstant();
element_access.type =
Type::Union(element_type, Type::Hole(), graph()->zone());
if (elements_kind == HOLEY_ELEMENTS ||
elements_kind == HOLEY_SMI_ELEMENTS) {
element_access.machine_type = MachineType::AnyTagged();
}
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* etrue = effect;
Node* checked = etrue =
graph()->NewNode(simplified()->CheckBounds(VectorSlotPair()), index,
length, etrue, if_true);
Node* element = etrue =
graph()->NewNode(simplified()->LoadElement(element_access),
elements, checked, etrue, if_true);
Node* vtrue;
if (IsDoubleElementsKind(elements_kind)) {
vtrue =
graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
} else {
vtrue = graph()->NewNode(simplified()->ReferenceEqual(), element,
jsgraph()->TheHoleConstant());
}
vtrue = graph()->NewNode(simplified()->BooleanNot(), vtrue);
control = graph()->NewNode(common()->Merge(2), if_true, if_false);
effect =
graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
value =
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
vtrue, vfalse, control);
}
} else {
DCHECK(access_mode == AccessMode::kStore ||
access_mode == AccessMode::kStoreInLiteral);

View File

@ -84,7 +84,6 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
Reduction ReduceJSStoreGlobal(Node* node);
Reduction ReduceJSLoadNamed(Node* node);
Reduction ReduceJSStoreNamed(Node* node);
Reduction ReduceJSHasProperty(Node* node);
Reduction ReduceJSLoadProperty(Node* node);
Reduction ReduceJSStoreProperty(Node* node);
Reduction ReduceJSStoreNamedOwn(Node* node);
@ -118,7 +117,6 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
Node* index, Handle<PropertyCell> property_cell);
Reduction ReduceKeyedLoadFromHeapConstant(Node* node, Node* index,
FeedbackNexus const& nexus,
AccessMode access_mode,
KeyedAccessLoadMode load_mode);
Reduction ReduceElementAccessOnString(Node* node, Node* index, Node* value,
AccessMode access_mode,
@ -171,9 +169,6 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
PropertyAccessInfo const& access_info,
AccessMode access_mode);
ValueEffectControl BuildPropertyTest(Node* effect, Node* control,
PropertyAccessInfo const& access_info);
// Helpers for accessor inlining.
Node* InlinePropertyGetterCall(Node* receiver, Node* context,
Node* frame_state, Node** effect,

View File

@ -282,8 +282,7 @@ bool operator!=(PropertyAccess const& lhs, PropertyAccess const& rhs) {
PropertyAccess const& PropertyAccessOf(const Operator* op) {
DCHECK(op->opcode() == IrOpcode::kJSHasProperty ||
op->opcode() == IrOpcode::kJSLoadProperty ||
DCHECK(op->opcode() == IrOpcode::kJSLoadProperty ||
op->opcode() == IrOpcode::kJSStoreProperty);
return OpParameter<PropertyAccess>(op);
}
@ -625,6 +624,7 @@ CompareOperationHint CompareOperationHintOf(const Operator* op) {
V(CreateTypedArray, Operator::kNoProperties, 5, 1) \
V(CreateObject, Operator::kNoProperties, 1, 1) \
V(ObjectIsArray, Operator::kNoProperties, 1, 1) \
V(HasProperty, Operator::kNoProperties, 2, 1) \
V(HasInPrototypeChain, Operator::kNoProperties, 2, 1) \
V(OrdinaryHasInstance, Operator::kNoProperties, 2, 1) \
V(ForInEnumerate, Operator::kNoProperties, 1, 1) \
@ -950,15 +950,6 @@ const Operator* JSOperatorBuilder::LoadProperty(
access); // parameter
}
const Operator* JSOperatorBuilder::HasProperty(VectorSlotPair const& feedback) {
PropertyAccess access(LanguageMode::kSloppy, feedback);
return new (zone()) Operator1<PropertyAccess>( // --
IrOpcode::kJSHasProperty, Operator::kNoProperties, // opcode
"JSHasProperty", // name
2, 1, 1, 1, 1, 2, // counts
access); // parameter
}
const Operator* JSOperatorBuilder::InstanceOf(VectorSlotPair const& feedback) {
FeedbackParameter parameter(feedback);
return new (zone()) Operator1<FeedbackParameter>( // --

View File

@ -789,7 +789,7 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* DeleteProperty();
const Operator* HasProperty(VectorSlotPair const& feedback);
const Operator* HasProperty();
const Operator* GetSuperConstructor();

View File

@ -71,7 +71,6 @@ int FeedbackMetadata::GetSlotSize(FeedbackSlotKind kind) {
case FeedbackSlotKind::kLoadGlobalInsideTypeof:
case FeedbackSlotKind::kLoadGlobalNotInsideTypeof:
case FeedbackSlotKind::kLoadKeyed:
case FeedbackSlotKind::kHasKeyed:
case FeedbackSlotKind::kStoreNamedSloppy:
case FeedbackSlotKind::kStoreNamedStrict:
case FeedbackSlotKind::kStoreOwnNamed:
@ -268,7 +267,6 @@ void FeedbackVector::ComputeCounts(int* with_type_info, int* generic,
case FeedbackSlotKind::kLoadGlobalInsideTypeof:
case FeedbackSlotKind::kLoadGlobalNotInsideTypeof:
case FeedbackSlotKind::kLoadKeyed:
case FeedbackSlotKind::kHasKeyed:
case FeedbackSlotKind::kStoreNamedSloppy:
case FeedbackSlotKind::kStoreNamedStrict:
case FeedbackSlotKind::kStoreOwnNamed:

View File

@ -143,8 +143,6 @@ const char* FeedbackMetadata::Kind2String(FeedbackSlotKind kind) {
return "LoadGlobalNotInsideTypeof";
case FeedbackSlotKind::kLoadKeyed:
return "LoadKeyed";
case FeedbackSlotKind::kHasKeyed:
return "HasKeyed";
case FeedbackSlotKind::kStoreNamedSloppy:
return "StoreNamedSloppy";
case FeedbackSlotKind::kStoreNamedStrict:
@ -266,7 +264,6 @@ Handle<FeedbackVector> FeedbackVector::New(Isolate* isolate,
case FeedbackSlotKind::kCloneObject:
case FeedbackSlotKind::kLoadProperty:
case FeedbackSlotKind::kLoadKeyed:
case FeedbackSlotKind::kHasKeyed:
case FeedbackSlotKind::kStoreNamedSloppy:
case FeedbackSlotKind::kStoreNamedStrict:
case FeedbackSlotKind::kStoreOwnNamed:
@ -447,7 +444,6 @@ void FeedbackNexus::ConfigureUninitialized() {
case FeedbackSlotKind::kStoreOwnNamed:
case FeedbackSlotKind::kLoadProperty:
case FeedbackSlotKind::kLoadKeyed:
case FeedbackSlotKind::kHasKeyed:
case FeedbackSlotKind::kStoreDataPropertyInLiteral: {
SetFeedback(*FeedbackVector::UninitializedSentinel(isolate),
SKIP_WRITE_BARRIER);
@ -488,7 +484,6 @@ bool FeedbackNexus::Clear() {
case FeedbackSlotKind::kStoreOwnNamed:
case FeedbackSlotKind::kLoadProperty:
case FeedbackSlotKind::kLoadKeyed:
case FeedbackSlotKind::kHasKeyed:
case FeedbackSlotKind::kStoreGlobalSloppy:
case FeedbackSlotKind::kStoreGlobalStrict:
case FeedbackSlotKind::kLoadGlobalNotInsideTypeof:
@ -599,8 +594,7 @@ InlineCacheState FeedbackNexus::ic_state() const {
case FeedbackSlotKind::kStoreInArrayLiteral:
case FeedbackSlotKind::kStoreOwnNamed:
case FeedbackSlotKind::kLoadProperty:
case FeedbackSlotKind::kLoadKeyed:
case FeedbackSlotKind::kHasKeyed: {
case FeedbackSlotKind::kLoadKeyed: {
if (feedback == MaybeObject::FromObject(
*FeedbackVector::UninitializedSentinel(isolate))) {
return UNINITIALIZED;
@ -625,8 +619,7 @@ InlineCacheState FeedbackNexus::ic_state() const {
return POLYMORPHIC;
}
if (heap_object->IsName()) {
DCHECK(IsKeyedLoadICKind(kind()) || IsKeyedStoreICKind(kind()) ||
IsKeyedHasICKind(kind()));
DCHECK(IsKeyedLoadICKind(kind()) || IsKeyedStoreICKind(kind()));
Object extra = GetFeedbackExtra()->GetHeapObjectAssumeStrong();
WeakFixedArray extra_array = WeakFixedArray::cast(extra);
return extra_array->length() > 2 ? POLYMORPHIC : MONOMORPHIC;
@ -933,7 +926,7 @@ int FeedbackNexus::ExtractMaps(MapHandles* maps) const {
DCHECK(IsLoadICKind(kind()) || IsStoreICKind(kind()) ||
IsKeyedLoadICKind(kind()) || IsKeyedStoreICKind(kind()) ||
IsStoreOwnICKind(kind()) || IsStoreDataPropertyInLiteralKind(kind()) ||
IsStoreInArrayLiteralICKind(kind()) || IsKeyedHasICKind(kind()));
IsStoreInArrayLiteralICKind(kind()));
Isolate* isolate = GetIsolate();
MaybeObject feedback = GetFeedback();
@ -981,8 +974,7 @@ int FeedbackNexus::ExtractMaps(MapHandles* maps) const {
MaybeObjectHandle FeedbackNexus::FindHandlerForMap(Handle<Map> map) const {
DCHECK(IsLoadICKind(kind()) || IsStoreICKind(kind()) ||
IsKeyedLoadICKind(kind()) || IsKeyedStoreICKind(kind()) ||
IsStoreOwnICKind(kind()) || IsStoreDataPropertyInLiteralKind(kind()) ||
IsKeyedHasICKind(kind()));
IsStoreOwnICKind(kind()) || IsStoreDataPropertyInLiteralKind(kind()));
MaybeObject feedback = GetFeedback();
Isolate* isolate = GetIsolate();
@ -1028,7 +1020,7 @@ bool FeedbackNexus::FindHandlers(MaybeObjectHandles* code_list,
DCHECK(IsLoadICKind(kind()) || IsStoreICKind(kind()) ||
IsKeyedLoadICKind(kind()) || IsKeyedStoreICKind(kind()) ||
IsStoreOwnICKind(kind()) || IsStoreDataPropertyInLiteralKind(kind()) ||
IsStoreInArrayLiteralICKind(kind()) || IsKeyedHasICKind(kind()));
IsStoreInArrayLiteralICKind(kind()));
MaybeObject feedback = GetFeedback();
Isolate* isolate = GetIsolate();
@ -1070,8 +1062,7 @@ bool FeedbackNexus::FindHandlers(MaybeObjectHandles* code_list,
}
Name FeedbackNexus::GetName() const {
if (IsKeyedStoreICKind(kind()) || IsKeyedLoadICKind(kind()) ||
IsKeyedHasICKind(kind())) {
if (IsKeyedStoreICKind(kind()) || IsKeyedLoadICKind(kind())) {
MaybeObject feedback = GetFeedback();
if (IsPropertyNameFeedback(feedback)) {
return Name::cast(feedback->GetHeapObjectAssumeStrong());
@ -1205,7 +1196,7 @@ KeyedAccessStoreMode FeedbackNexus::GetKeyedAccessStoreMode() const {
IcCheckType FeedbackNexus::GetKeyType() const {
DCHECK(IsKeyedStoreICKind(kind()) || IsKeyedLoadICKind(kind()) ||
IsStoreInArrayLiteralICKind(kind()) || IsKeyedHasICKind(kind()));
IsStoreInArrayLiteralICKind(kind()));
MaybeObject feedback = GetFeedback();
if (feedback == MaybeObject::FromObject(
*FeedbackVector::MegamorphicSentinel(GetIsolate()))) {

View File

@ -40,7 +40,6 @@ enum class FeedbackSlotKind {
kLoadGlobalNotInsideTypeof,
kLoadGlobalInsideTypeof,
kLoadKeyed,
kHasKeyed,
kStoreGlobalStrict,
kStoreNamedStrict,
kStoreOwnNamed,
@ -76,10 +75,6 @@ inline bool IsKeyedLoadICKind(FeedbackSlotKind kind) {
return kind == FeedbackSlotKind::kLoadKeyed;
}
inline bool IsKeyedHasICKind(FeedbackSlotKind kind) {
return kind == FeedbackSlotKind::kHasKeyed;
}
inline bool IsStoreGlobalICKind(FeedbackSlotKind kind) {
return kind == FeedbackSlotKind::kStoreGlobalSloppy ||
kind == FeedbackSlotKind::kStoreGlobalStrict;
@ -353,10 +348,6 @@ class V8_EXPORT_PRIVATE FeedbackVectorSpec {
return AddSlot(FeedbackSlotKind::kLoadKeyed);
}
FeedbackSlot AddKeyedHasICSlot() {
return AddSlot(FeedbackSlotKind::kHasKeyed);
}
FeedbackSlotKind GetStoreICSlot(LanguageMode language_mode) {
STATIC_ASSERT(LanguageModeSize == 2);
return is_strict(language_mode) ? FeedbackSlotKind::kStoreNamedStrict

View File

@ -578,7 +578,6 @@ static ObjectStats::VirtualInstanceType GetFeedbackSlotType(
case FeedbackSlotKind::kLoadGlobalInsideTypeof:
case FeedbackSlotKind::kLoadGlobalNotInsideTypeof:
case FeedbackSlotKind::kLoadKeyed:
case FeedbackSlotKind::kHasKeyed:
if (obj == *isolate->factory()->uninitialized_symbol() ||
obj == *isolate->factory()->premonomorphic_symbol()) {
return ObjectStats::FEEDBACK_VECTOR_SLOT_LOAD_UNUSED_TYPE;

View File

@ -140,7 +140,7 @@ void AccessorAssembler::HandlePolymorphicCase(
void AccessorAssembler::HandleLoadICHandlerCase(
const LoadICParameters* p, TNode<Object> handler, Label* miss,
ExitPoint* exit_point, ICMode ic_mode, OnNonExistent on_nonexistent,
ElementSupport support_elements, LoadAccessMode access_mode) {
ElementSupport support_elements) {
Comment("have_handler");
VARIABLE(var_holder, MachineRepresentation::kTagged, p->holder);
@ -159,15 +159,14 @@ void AccessorAssembler::HandleLoadICHandlerCase(
{
HandleLoadICSmiHandlerCase(p, var_holder.value(), var_smi_handler.value(),
handler, miss, exit_point, on_nonexistent,
support_elements, access_mode);
support_elements);
}
BIND(&try_proto_handler);
{
GotoIf(IsCodeMap(LoadMap(CAST(handler))), &call_handler);
HandleLoadICProtoHandler(p, handler, &var_holder, &var_smi_handler,
&if_smi_handler, miss, exit_point, ic_mode,
access_mode);
&if_smi_handler, miss, exit_point, ic_mode);
}
BIND(&call_handler);
@ -308,8 +307,7 @@ TNode<MaybeObject> AccessorAssembler::LoadDescriptorValueOrFieldType(
void AccessorAssembler::HandleLoadICSmiHandlerCase(
const LoadICParameters* p, Node* holder, SloppyTNode<Smi> smi_handler,
SloppyTNode<Object> handler, Label* miss, ExitPoint* exit_point,
OnNonExistent on_nonexistent, ElementSupport support_elements,
LoadAccessMode access_mode) {
OnNonExistent on_nonexistent, ElementSupport support_elements) {
VARIABLE(var_double_value, MachineRepresentation::kFloat64);
Label rebox_double(this, &var_double_value);
@ -320,17 +318,8 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase(
Label if_element(this), if_indexed_string(this), if_property(this);
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kElement)),
&if_element);
if (access_mode == LoadAccessMode::kHas) {
CSA_ASSERT(this,
WordNotEqual(handler_kind,
IntPtrConstant(LoadHandler::kIndexedString)));
Goto(&if_property);
} else {
Branch(
WordEqual(handler_kind, IntPtrConstant(LoadHandler::kIndexedString)),
&if_indexed_string, &if_property);
}
Branch(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kIndexedString)),
&if_indexed_string, &if_property);
BIND(&if_element);
Comment("element_load");
@ -345,7 +334,7 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase(
EmitElementLoad(holder, elements, elements_kind, intptr_index,
is_jsarray_condition, &if_hole, &rebox_double,
&var_double_value, &unimplemented_elements_kind, &if_oob,
miss, exit_point, access_mode);
miss, exit_point);
BIND(&unimplemented_elements_kind);
{
@ -379,65 +368,41 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase(
miss);
BIND(&return_undefined);
exit_point->Return(access_mode == LoadAccessMode::kHas
? FalseConstant()
: UndefinedConstant());
exit_point->Return(UndefinedConstant());
}
BIND(&if_hole);
{
Comment("convert hole");
if (access_mode != LoadAccessMode::kHas) {
GotoIfNot(IsSetWord<LoadHandler::ConvertHoleBits>(handler_word), miss);
}
GotoIfNot(IsSetWord<LoadHandler::ConvertHoleBits>(handler_word), miss);
GotoIf(IsNoElementsProtectorCellInvalid(), miss);
exit_point->Return(access_mode == LoadAccessMode::kHas
? FalseConstant()
: UndefinedConstant());
exit_point->Return(UndefinedConstant());
}
if (access_mode != LoadAccessMode::kHas) {
BIND(&if_indexed_string);
{
Label if_oob(this, Label::kDeferred);
BIND(&if_indexed_string);
{
Label if_oob(this, Label::kDeferred);
Comment("indexed string");
Node* intptr_index = TryToIntptr(p->name, miss);
Node* length = LoadStringLengthAsWord(holder);
GotoIf(UintPtrGreaterThanOrEqual(intptr_index, length), &if_oob);
TNode<Int32T> code = StringCharCodeAt(holder, intptr_index);
TNode<String> result = StringFromSingleCharCode(code);
Return(result);
Comment("indexed string");
Node* intptr_index = TryToIntptr(p->name, miss);
Node* length = LoadStringLengthAsWord(holder);
GotoIf(UintPtrGreaterThanOrEqual(intptr_index, length), &if_oob);
TNode<Int32T> code = StringCharCodeAt(holder, intptr_index);
TNode<String> result = StringFromSingleCharCode(code);
Return(result);
BIND(&if_oob);
Node* allow_out_of_bounds =
IsSetWord<LoadHandler::AllowOutOfBoundsBits>(handler_word);
GotoIfNot(allow_out_of_bounds, miss);
GotoIf(IsNoElementsProtectorCellInvalid(), miss);
Return(UndefinedConstant());
}
BIND(&if_oob);
Node* allow_out_of_bounds =
IsSetWord<LoadHandler::AllowOutOfBoundsBits>(handler_word);
GotoIfNot(allow_out_of_bounds, miss);
GotoIf(IsNoElementsProtectorCellInvalid(), miss);
Return(UndefinedConstant());
}
BIND(&if_property);
Comment("property_load");
}
if (access_mode == LoadAccessMode::kHas) {
HandleLoadICSmiHandlerHasNamedCase(p, holder, handler_kind, miss,
exit_point);
} else {
HandleLoadICSmiHandlerLoadNamedCase(
p, holder, handler_kind, handler_word, &rebox_double, &var_double_value,
handler, miss, exit_point, on_nonexistent, support_elements);
}
}
void AccessorAssembler::HandleLoadICSmiHandlerLoadNamedCase(
const LoadICParameters* p, Node* holder, TNode<IntPtrT> handler_kind,
TNode<WordT> handler_word, Label* rebox_double, Variable* var_double_value,
SloppyTNode<Object> handler, Label* miss, ExitPoint* exit_point,
OnNonExistent on_nonexistent, ElementSupport support_elements) {
Label constant(this), field(this), normal(this, Label::kDeferred),
interceptor(this, Label::kDeferred), nonexistent(this),
accessor(this, Label::kDeferred), global(this, Label::kDeferred),
@ -477,7 +442,7 @@ void AccessorAssembler::HandleLoadICSmiHandlerLoadNamedCase(
&module_export, &interceptor);
BIND(&field);
HandleLoadField(holder, handler_word, var_double_value, rebox_double,
HandleLoadField(holder, handler_word, &var_double_value, &rebox_double,
exit_point);
BIND(&nonexistent);
@ -624,88 +589,8 @@ void AccessorAssembler::HandleLoadICSmiHandlerLoadNamedCase(
}
}
BIND(rebox_double);
exit_point->Return(AllocateHeapNumberWithValue(var_double_value->value()));
}
void AccessorAssembler::HandleLoadICSmiHandlerHasNamedCase(
const LoadICParameters* p, Node* holder, TNode<IntPtrT> handler_kind,
Label* miss, ExitPoint* exit_point) {
Label return_true(this), return_false(this), return_lookup(this),
normal(this), global(this);
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kField)),
&return_true);
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kConstant)),
&return_true);
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNonExistent)),
&return_false);
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNormal)),
&normal);
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kAccessor)),
&return_true);
GotoIf(
WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNativeDataProperty)),
&return_true);
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kApiGetter)),
&return_true);
GotoIf(WordEqual(handler_kind,
IntPtrConstant(LoadHandler::kApiGetterHolderIsPrototype)),
&return_true);
Branch(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kGlobal)), &global,
&return_lookup);
BIND(&return_true);
exit_point->Return(TrueConstant());
BIND(&return_false);
exit_point->Return(FalseConstant());
BIND(&return_lookup);
{
CSA_ASSERT(
this,
Word32Or(
WordEqual(handler_kind, IntPtrConstant(LoadHandler::kInterceptor)),
Word32Or(
WordEqual(handler_kind, IntPtrConstant(LoadHandler::kProxy)),
WordEqual(handler_kind,
IntPtrConstant(LoadHandler::kModuleExport)))));
exit_point->ReturnCallStub(
Builtins::CallableFor(isolate(), Builtins::kHasProperty), p->context,
p->receiver, p->name);
}
BIND(&normal);
{
Comment("has_normal");
TNode<NameDictionary> properties = CAST(LoadSlowProperties(holder));
TVARIABLE(IntPtrT, var_name_index);
Label found(this);
NameDictionaryLookup<NameDictionary>(properties, CAST(p->name), &found,
&var_name_index, miss);
BIND(&found);
exit_point->Return(TrueConstant());
}
BIND(&global);
{
CSA_ASSERT(this, IsPropertyCell(holder));
// Ensure the property cell doesn't contain the hole.
Node* value = LoadObjectField(holder, PropertyCell::kValueOffset);
GotoIf(IsTheHole(value), miss);
exit_point->Return(TrueConstant());
}
BIND(&rebox_double);
exit_point->Return(AllocateHeapNumberWithValue(var_double_value.value()));
}
// Performs actions common to both load and store handlers:
@ -822,7 +707,7 @@ Node* AccessorAssembler::HandleProtoHandler(
void AccessorAssembler::HandleLoadICProtoHandler(
const LoadICParameters* p, Node* handler, Variable* var_holder,
Variable* var_smi_handler, Label* if_smi_handler, Label* miss,
ExitPoint* exit_point, ICMode ic_mode, LoadAccessMode access_mode) {
ExitPoint* exit_point, ICMode ic_mode) {
DCHECK_EQ(MachineRepresentation::kTagged, var_holder->rep());
DCHECK_EQ(MachineRepresentation::kTagged, var_smi_handler->rep());
@ -832,18 +717,14 @@ void AccessorAssembler::HandleLoadICProtoHandler(
nullptr,
// on_found_on_receiver
[=](Node* properties, Node* name_index) {
if (access_mode == LoadAccessMode::kHas) {
exit_point->Return(TrueConstant());
} else {
VARIABLE(var_details, MachineRepresentation::kWord32);
VARIABLE(var_value, MachineRepresentation::kTagged);
LoadPropertyFromNameDictionary(properties, name_index, &var_details,
&var_value);
Node* value =
CallGetterIfAccessor(var_value.value(), var_details.value(),
p->context, p->receiver, miss);
exit_point->Return(value);
}
VARIABLE(var_details, MachineRepresentation::kWord32);
VARIABLE(var_value, MachineRepresentation::kTagged);
LoadPropertyFromNameDictionary(properties, name_index, &var_details,
&var_value);
Node* value =
CallGetterIfAccessor(var_value.value(), var_details.value(),
p->context, p->receiver, miss);
exit_point->Return(value);
},
miss, ic_mode);
@ -1883,7 +1764,7 @@ void AccessorAssembler::EmitElementLoad(
SloppyTNode<IntPtrT> intptr_index, Node* is_jsarray_condition,
Label* if_hole, Label* rebox_double, Variable* var_double_value,
Label* unimplemented_elements_kind, Label* out_of_bounds, Label* miss,
ExitPoint* exit_point, LoadAccessMode access_mode) {
ExitPoint* exit_point) {
Label if_typed_array(this), if_fast_packed(this), if_fast_holey(this),
if_fast_double(this), if_fast_holey_double(this), if_nonfast(this),
if_dictionary(this);
@ -1916,9 +1797,7 @@ void AccessorAssembler::EmitElementLoad(
{
Comment("fast packed elements");
exit_point->Return(
access_mode == LoadAccessMode::kHas
? TrueConstant()
: UnsafeLoadFixedArrayElement(CAST(elements), intptr_index));
UnsafeLoadFixedArrayElement(CAST(elements), intptr_index));
}
BIND(&if_fast_holey);
@ -1926,20 +1805,15 @@ void AccessorAssembler::EmitElementLoad(
Comment("fast holey elements");
Node* element = UnsafeLoadFixedArrayElement(CAST(elements), intptr_index);
GotoIf(WordEqual(element, TheHoleConstant()), if_hole);
exit_point->Return(access_mode == LoadAccessMode::kHas ? TrueConstant()
: element);
exit_point->Return(element);
}
BIND(&if_fast_double);
{
Comment("packed double elements");
if (access_mode == LoadAccessMode::kHas) {
exit_point->Return(TrueConstant());
} else {
var_double_value->Bind(LoadFixedDoubleArrayElement(
elements, intptr_index, MachineType::Float64()));
Goto(rebox_double);
}
var_double_value->Bind(LoadFixedDoubleArrayElement(elements, intptr_index,
MachineType::Float64()));
Goto(rebox_double);
}
BIND(&if_fast_holey_double);
@ -1948,12 +1822,8 @@ void AccessorAssembler::EmitElementLoad(
Node* value = LoadFixedDoubleArrayElement(elements, intptr_index,
MachineType::Float64(), 0,
INTPTR_PARAMETERS, if_hole);
if (access_mode == LoadAccessMode::kHas) {
exit_point->Return(TrueConstant());
} else {
var_double_value->Bind(value);
Goto(rebox_double);
}
var_double_value->Bind(value);
Goto(rebox_double);
}
BIND(&if_nonfast);
@ -1975,8 +1845,7 @@ void AccessorAssembler::EmitElementLoad(
TNode<Object> value = BasicLoadNumberDictionaryElement(
CAST(elements), intptr_index, miss, if_hole);
exit_point->Return(access_mode == LoadAccessMode::kHas ? TrueConstant()
: value);
exit_point->Return(value);
}
BIND(&if_typed_array);
@ -1989,101 +1858,97 @@ void AccessorAssembler::EmitElementLoad(
// Bounds check.
Node* length = SmiUntag(LoadJSTypedArrayLength(CAST(object)));
GotoIfNot(UintPtrLessThan(intptr_index, length), out_of_bounds);
if (access_mode == LoadAccessMode::kHas) {
exit_point->Return(TrueConstant());
} else {
Node* backing_store = LoadFixedTypedArrayBackingStore(CAST(elements));
Label uint8_elements(this), int8_elements(this), uint16_elements(this),
int16_elements(this), uint32_elements(this), int32_elements(this),
float32_elements(this), float64_elements(this),
bigint64_elements(this), biguint64_elements(this);
Label* elements_kind_labels[] = {
&uint8_elements, &uint8_elements, &int8_elements,
&uint16_elements, &int16_elements, &uint32_elements,
&int32_elements, &float32_elements, &float64_elements,
&bigint64_elements, &biguint64_elements};
int32_t elements_kinds[] = {
UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS, INT8_ELEMENTS,
UINT16_ELEMENTS, INT16_ELEMENTS, UINT32_ELEMENTS,
INT32_ELEMENTS, FLOAT32_ELEMENTS, FLOAT64_ELEMENTS,
BIGINT64_ELEMENTS, BIGUINT64_ELEMENTS};
const size_t kTypedElementsKindCount =
LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND -
FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND + 1;
DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kinds));
DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kind_labels));
Switch(elements_kind, miss, elements_kinds, elements_kind_labels,
kTypedElementsKindCount);
BIND(&uint8_elements);
{
Comment("UINT8_ELEMENTS"); // Handles UINT8_CLAMPED_ELEMENTS too.
Node* element = Load(MachineType::Uint8(), backing_store, intptr_index);
exit_point->Return(SmiFromInt32(element));
}
BIND(&int8_elements);
{
Comment("INT8_ELEMENTS");
Node* element = Load(MachineType::Int8(), backing_store, intptr_index);
exit_point->Return(SmiFromInt32(element));
}
BIND(&uint16_elements);
{
Comment("UINT16_ELEMENTS");
Node* index = WordShl(intptr_index, IntPtrConstant(1));
Node* element = Load(MachineType::Uint16(), backing_store, index);
exit_point->Return(SmiFromInt32(element));
}
BIND(&int16_elements);
{
Comment("INT16_ELEMENTS");
Node* index = WordShl(intptr_index, IntPtrConstant(1));
Node* element = Load(MachineType::Int16(), backing_store, index);
exit_point->Return(SmiFromInt32(element));
}
BIND(&uint32_elements);
{
Comment("UINT32_ELEMENTS");
Node* index = WordShl(intptr_index, IntPtrConstant(2));
Node* element = Load(MachineType::Uint32(), backing_store, index);
exit_point->Return(ChangeUint32ToTagged(element));
}
BIND(&int32_elements);
{
Comment("INT32_ELEMENTS");
Node* index = WordShl(intptr_index, IntPtrConstant(2));
Node* element = Load(MachineType::Int32(), backing_store, index);
exit_point->Return(ChangeInt32ToTagged(element));
}
BIND(&float32_elements);
{
Comment("FLOAT32_ELEMENTS");
Node* index = WordShl(intptr_index, IntPtrConstant(2));
Node* element = Load(MachineType::Float32(), backing_store, index);
var_double_value->Bind(ChangeFloat32ToFloat64(element));
Goto(rebox_double);
}
BIND(&float64_elements);
{
Comment("FLOAT64_ELEMENTS");
Node* index = WordShl(intptr_index, IntPtrConstant(3));
Node* element = Load(MachineType::Float64(), backing_store, index);
var_double_value->Bind(element);
Goto(rebox_double);
}
BIND(&bigint64_elements);
{
Comment("BIGINT64_ELEMENTS");
exit_point->Return(LoadFixedTypedArrayElementAsTagged(
backing_store, intptr_index, BIGINT64_ELEMENTS, INTPTR_PARAMETERS));
}
BIND(&biguint64_elements);
{
Comment("BIGUINT64_ELEMENTS");
exit_point->Return(LoadFixedTypedArrayElementAsTagged(
backing_store, intptr_index, BIGUINT64_ELEMENTS,
INTPTR_PARAMETERS));
}
Node* backing_store = LoadFixedTypedArrayBackingStore(CAST(elements));
Label uint8_elements(this), int8_elements(this), uint16_elements(this),
int16_elements(this), uint32_elements(this), int32_elements(this),
float32_elements(this), float64_elements(this), bigint64_elements(this),
biguint64_elements(this);
Label* elements_kind_labels[] = {
&uint8_elements, &uint8_elements, &int8_elements,
&uint16_elements, &int16_elements, &uint32_elements,
&int32_elements, &float32_elements, &float64_elements,
&bigint64_elements, &biguint64_elements};
int32_t elements_kinds[] = {
UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS, INT8_ELEMENTS,
UINT16_ELEMENTS, INT16_ELEMENTS, UINT32_ELEMENTS,
INT32_ELEMENTS, FLOAT32_ELEMENTS, FLOAT64_ELEMENTS,
BIGINT64_ELEMENTS, BIGUINT64_ELEMENTS};
const size_t kTypedElementsKindCount =
LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND -
FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND + 1;
DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kinds));
DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kind_labels));
Switch(elements_kind, miss, elements_kinds, elements_kind_labels,
kTypedElementsKindCount);
BIND(&uint8_elements);
{
Comment("UINT8_ELEMENTS"); // Handles UINT8_CLAMPED_ELEMENTS too.
Node* element = Load(MachineType::Uint8(), backing_store, intptr_index);
exit_point->Return(SmiFromInt32(element));
}
BIND(&int8_elements);
{
Comment("INT8_ELEMENTS");
Node* element = Load(MachineType::Int8(), backing_store, intptr_index);
exit_point->Return(SmiFromInt32(element));
}
BIND(&uint16_elements);
{
Comment("UINT16_ELEMENTS");
Node* index = WordShl(intptr_index, IntPtrConstant(1));
Node* element = Load(MachineType::Uint16(), backing_store, index);
exit_point->Return(SmiFromInt32(element));
}
BIND(&int16_elements);
{
Comment("INT16_ELEMENTS");
Node* index = WordShl(intptr_index, IntPtrConstant(1));
Node* element = Load(MachineType::Int16(), backing_store, index);
exit_point->Return(SmiFromInt32(element));
}
BIND(&uint32_elements);
{
Comment("UINT32_ELEMENTS");
Node* index = WordShl(intptr_index, IntPtrConstant(2));
Node* element = Load(MachineType::Uint32(), backing_store, index);
exit_point->Return(ChangeUint32ToTagged(element));
}
BIND(&int32_elements);
{
Comment("INT32_ELEMENTS");
Node* index = WordShl(intptr_index, IntPtrConstant(2));
Node* element = Load(MachineType::Int32(), backing_store, index);
exit_point->Return(ChangeInt32ToTagged(element));
}
BIND(&float32_elements);
{
Comment("FLOAT32_ELEMENTS");
Node* index = WordShl(intptr_index, IntPtrConstant(2));
Node* element = Load(MachineType::Float32(), backing_store, index);
var_double_value->Bind(ChangeFloat32ToFloat64(element));
Goto(rebox_double);
}
BIND(&float64_elements);
{
Comment("FLOAT64_ELEMENTS");
Node* index = WordShl(intptr_index, IntPtrConstant(3));
Node* element = Load(MachineType::Float64(), backing_store, index);
var_double_value->Bind(element);
Goto(rebox_double);
}
BIND(&bigint64_elements);
{
Comment("BIGINT64_ELEMENTS");
exit_point->Return(LoadFixedTypedArrayElementAsTagged(
backing_store, intptr_index, BIGINT64_ELEMENTS, INTPTR_PARAMETERS));
}
BIND(&biguint64_elements);
{
Comment("BIGUINT64_ELEMENTS");
exit_point->Return(LoadFixedTypedArrayElementAsTagged(
backing_store, intptr_index, BIGUINT64_ELEMENTS, INTPTR_PARAMETERS));
}
}
}
@ -2796,8 +2661,7 @@ void AccessorAssembler::LoadGlobalIC_TryHandlerCase(
on_nonexistent);
}
void AccessorAssembler::KeyedLoadIC(const LoadICParameters* p,
LoadAccessMode access_mode) {
void AccessorAssembler::KeyedLoadIC(const LoadICParameters* p) {
ExitPoint direct_exit(this);
TVARIABLE(MaybeObject, var_handler);
@ -2817,9 +2681,9 @@ void AccessorAssembler::KeyedLoadIC(const LoadICParameters* p,
&var_handler, &try_polymorphic);
BIND(&if_handler);
{
HandleLoadICHandlerCase(
p, CAST(var_handler.value()), &miss, &direct_exit, ICMode::kNonGlobalIC,
OnNonExistent::kReturnUndefined, kSupportElements, access_mode);
HandleLoadICHandlerCase(p, CAST(var_handler.value()), &miss, &direct_exit,
ICMode::kNonGlobalIC,
OnNonExistent::kReturnUndefined, kSupportElements);
}
BIND(&try_polymorphic);
@ -2843,10 +2707,8 @@ void AccessorAssembler::KeyedLoadIC(const LoadICParameters* p,
BIND(&generic);
{
// TODO(jkummerow): Inline this? Or some of it?
TailCallBuiltin(access_mode == LoadAccessMode::kLoad
? Builtins::kKeyedLoadIC_Megamorphic
: Builtins::kKeyedHasIC_Megamorphic,
p->context, p->receiver, p->name, p->slot, p->vector);
TailCallBuiltin(Builtins::kKeyedLoadIC_Megamorphic, p->context, p->receiver,
p->name, p->slot, p->vector);
}
BIND(&try_polymorphic_name);
@ -2892,20 +2754,16 @@ void AccessorAssembler::KeyedLoadIC(const LoadICParameters* p,
// If the name comparison succeeded, we know we have a weak fixed array
// with at least one map/handler pair.
Node* name = var_name.value();
TailCallBuiltin(access_mode == LoadAccessMode::kLoad
? Builtins::kKeyedLoadIC_PolymorphicName
: Builtins::kKeyedHasIC_PolymorphicName,
p->context, p->receiver, name, p->slot, p->vector);
TailCallBuiltin(Builtins::kKeyedLoadIC_PolymorphicName, p->context,
p->receiver, name, p->slot, p->vector);
}
}
BIND(&miss);
{
Comment("KeyedLoadIC_miss");
TailCallRuntime(access_mode == LoadAccessMode::kLoad
? Runtime::kKeyedLoadIC_Miss
: Runtime::kKeyedHasIC_Miss,
p->context, p->receiver, p->name, p->slot, p->vector);
TailCallRuntime(Runtime::kKeyedLoadIC_Miss, p->context, p->receiver,
p->name, p->slot, p->vector);
}
}
@ -2988,8 +2846,7 @@ void AccessorAssembler::KeyedLoadICGeneric(const LoadICParameters* p) {
}
}
void AccessorAssembler::KeyedLoadICPolymorphicName(const LoadICParameters* p,
LoadAccessMode access_mode) {
void AccessorAssembler::KeyedLoadICPolymorphicName(const LoadICParameters* p) {
TVARIABLE(MaybeObject, var_handler);
Label if_handler(this, &var_handler), miss(this, Label::kDeferred);
@ -3017,18 +2874,16 @@ void AccessorAssembler::KeyedLoadICPolymorphicName(const LoadICParameters* p,
BIND(&if_handler);
{
ExitPoint direct_exit(this);
HandleLoadICHandlerCase(
p, CAST(var_handler.value()), &miss, &direct_exit, ICMode::kNonGlobalIC,
OnNonExistent::kReturnUndefined, kOnlyProperties, access_mode);
HandleLoadICHandlerCase(p, CAST(var_handler.value()), &miss, &direct_exit,
ICMode::kNonGlobalIC,
OnNonExistent::kReturnUndefined, kOnlyProperties);
}
BIND(&miss);
{
Comment("KeyedLoadIC_miss");
TailCallRuntime(access_mode == LoadAccessMode::kLoad
? Runtime::kKeyedLoadIC_Miss
: Runtime::kKeyedHasIC_Miss,
context, receiver, name, slot, vector);
TailCallRuntime(Runtime::kKeyedLoadIC_Miss, context, receiver, name, slot,
vector);
}
}
@ -3525,7 +3380,7 @@ void AccessorAssembler::GenerateKeyedLoadIC() {
Node* context = Parameter(Descriptor::kContext);
LoadICParameters p(context, receiver, name, slot, vector);
KeyedLoadIC(&p, LoadAccessMode::kLoad);
KeyedLoadIC(&p);
}
void AccessorAssembler::GenerateKeyedLoadIC_Megamorphic() {
@ -3577,7 +3432,7 @@ void AccessorAssembler::GenerateKeyedLoadIC_PolymorphicName() {
Node* context = Parameter(Descriptor::kContext);
LoadICParameters p(context, receiver, name, slot, vector);
KeyedLoadICPolymorphicName(&p, LoadAccessMode::kLoad);
KeyedLoadICPolymorphicName(&p);
}
void AccessorAssembler::GenerateStoreGlobalIC() {
@ -3899,42 +3754,5 @@ void AccessorAssembler::GenerateCloneObjectIC() {
}
}
void AccessorAssembler::GenerateKeyedHasIC() {
typedef LoadWithVectorDescriptor Descriptor;
Node* receiver = Parameter(Descriptor::kReceiver);
Node* name = Parameter(Descriptor::kName);
Node* slot = Parameter(Descriptor::kSlot);
Node* vector = Parameter(Descriptor::kVector);
Node* context = Parameter(Descriptor::kContext);
LoadICParameters p(context, receiver, name, slot, vector);
KeyedLoadIC(&p, LoadAccessMode::kHas);
}
void AccessorAssembler::GenerateKeyedHasIC_Megamorphic() {
typedef LoadWithVectorDescriptor Descriptor;
Node* receiver = Parameter(Descriptor::kReceiver);
Node* name = Parameter(Descriptor::kName);
Node* context = Parameter(Descriptor::kContext);
// TODO(magardn): implement HasProperty handling in KeyedLoadICGeneric
Return(HasProperty(context, receiver, name,
HasPropertyLookupMode::kHasProperty));
}
void AccessorAssembler::GenerateKeyedHasIC_PolymorphicName() {
typedef LoadWithVectorDescriptor Descriptor;
Node* receiver = Parameter(Descriptor::kReceiver);
Node* name = Parameter(Descriptor::kName);
Node* slot = Parameter(Descriptor::kSlot);
Node* vector = Parameter(Descriptor::kVector);
Node* context = Parameter(Descriptor::kContext);
LoadICParameters p(context, receiver, name, slot, vector);
KeyedLoadICPolymorphicName(&p, LoadAccessMode::kHas);
}
} // namespace internal
} // namespace v8

View File

@ -44,9 +44,6 @@ class AccessorAssembler : public CodeStubAssembler {
void GenerateStoreGlobalICTrampoline();
void GenerateCloneObjectIC();
void GenerateCloneObjectIC_Slow();
void GenerateKeyedHasIC();
void GenerateKeyedHasIC_Megamorphic();
void GenerateKeyedHasIC_PolymorphicName();
void GenerateLoadGlobalIC(TypeofMode typeof_mode);
void GenerateLoadGlobalICTrampoline(TypeofMode typeof_mode);
@ -108,7 +105,6 @@ class AccessorAssembler : public CodeStubAssembler {
SloppyTNode<Object> value;
};
enum class LoadAccessMode { kLoad, kHas };
enum class ICMode { kNonGlobalIC, kGlobalIC };
enum ElementSupport { kOnlyProperties, kSupportElements };
void HandleStoreICHandlerCase(
@ -157,10 +153,9 @@ class AccessorAssembler : public CodeStubAssembler {
void LoadIC_Uninitialized(const LoadICParameters* p);
void KeyedLoadIC(const LoadICParameters* p, LoadAccessMode access_mode);
void KeyedLoadIC(const LoadICParameters* p);
void KeyedLoadICGeneric(const LoadICParameters* p);
void KeyedLoadICPolymorphicName(const LoadICParameters* p,
LoadAccessMode access_mode);
void KeyedLoadICPolymorphicName(const LoadICParameters* p);
void StoreIC(const StoreICParameters* p);
void StoreGlobalIC(const StoreICParameters* p);
void StoreGlobalIC_PropertyCellCase(Node* property_cell, Node* value,
@ -185,22 +180,19 @@ class AccessorAssembler : public CodeStubAssembler {
const LoadICParameters* p, TNode<Object> handler, Label* miss,
ExitPoint* exit_point, ICMode ic_mode = ICMode::kNonGlobalIC,
OnNonExistent on_nonexistent = OnNonExistent::kReturnUndefined,
ElementSupport support_elements = kOnlyProperties,
LoadAccessMode access_mode = LoadAccessMode::kLoad);
ElementSupport support_elements = kOnlyProperties);
void HandleLoadICSmiHandlerCase(const LoadICParameters* p, Node* holder,
SloppyTNode<Smi> smi_handler,
SloppyTNode<Object> handler, Label* miss,
ExitPoint* exit_point,
OnNonExistent on_nonexistent,
ElementSupport support_elements,
LoadAccessMode access_mode);
ElementSupport support_elements);
void HandleLoadICProtoHandler(const LoadICParameters* p, Node* handler,
Variable* var_holder, Variable* var_smi_handler,
Label* if_smi_handler, Label* miss,
ExitPoint* exit_point, ICMode ic_mode,
LoadAccessMode access_mode);
ExitPoint* exit_point, ICMode ic_mode);
void HandleLoadCallbackProperty(const LoadICParameters* p,
TNode<JSObject> holder,
@ -219,18 +211,6 @@ class AccessorAssembler : public CodeStubAssembler {
void EmitAccessCheck(Node* expected_native_context, Node* context,
Node* receiver, Label* can_access, Label* miss);
void HandleLoadICSmiHandlerLoadNamedCase(
const LoadICParameters* p, Node* holder, TNode<IntPtrT> handler_kind,
TNode<WordT> handler_word, Label* rebox_double,
Variable* var_double_value, SloppyTNode<Object> handler, Label* miss,
ExitPoint* exit_point, OnNonExistent on_nonexistent,
ElementSupport support_elements);
void HandleLoadICSmiHandlerHasNamedCase(const LoadICParameters* p,
Node* holder,
TNode<IntPtrT> handler_kind,
Label* miss, ExitPoint* exit_point);
// LoadGlobalIC implementation.
void LoadGlobalIC_TryPropertyCellCase(
@ -316,8 +296,7 @@ class AccessorAssembler : public CodeStubAssembler {
Label* if_hole, Label* rebox_double,
Variable* var_double_value,
Label* unimplemented_elements_kind, Label* out_of_bounds,
Label* miss, ExitPoint* exit_point,
LoadAccessMode access_mode = LoadAccessMode::kLoad);
Label* miss, ExitPoint* exit_point);
void NameDictionaryNegativeLookup(Node* object, SloppyTNode<Name> name,
Label* miss);
TNode<BoolT> IsPropertyDetailsConst(Node* details);

View File

@ -216,7 +216,7 @@ JSFunction IC::GetHostFunction() const {
return frame->function();
}
static void LookupForRead(LookupIterator* it, bool is_has_property) {
static void LookupForRead(Isolate* isolate, LookupIterator* it) {
for (; it->IsFound(); it->Next()) {
switch (it->state()) {
case LookupIterator::NOT_FOUND:
@ -227,13 +227,7 @@ static void LookupForRead(LookupIterator* it, bool is_has_property) {
case LookupIterator::INTERCEPTOR: {
// If there is a getter, return; otherwise loop to perform the lookup.
Handle<JSObject> holder = it->GetHolder<JSObject>();
if (!holder->GetNamedInterceptor()->getter()->IsUndefined(
it->isolate())) {
return;
}
if (is_has_property &&
!holder->GetNamedInterceptor()->query()->IsUndefined(
it->isolate())) {
if (!holder->GetNamedInterceptor()->getter()->IsUndefined(isolate)) {
return;
}
break;
@ -290,6 +284,7 @@ bool IC::RecomputeHandlerForName(Handle<Object> name) {
return true;
}
void IC::UpdateState(Handle<Object> receiver, Handle<Object> name) {
if (state() == NO_FEEDBACK) return;
update_receiver_map(receiver);
@ -311,6 +306,7 @@ MaybeHandle<Object> IC::TypeError(MessageTemplate index, Handle<Object> object,
THROW_NEW_ERROR(isolate(), NewTypeError(index, key, object), Object);
}
MaybeHandle<Object> IC::ReferenceError(Handle<Name> name) {
HandleScope scope(isolate());
THROW_NEW_ERROR(
@ -433,8 +429,7 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name) {
// If the object is undefined or null it's illegal to try to get any
// of its properties; throw a TypeError in that case.
if (IsAnyHas() ? !object->IsJSReceiver()
: object->IsNullOrUndefined(isolate())) {
if (object->IsNullOrUndefined(isolate())) {
if (use_ic && state() != PREMONOMORPHIC) {
// Ensure the IC state progresses.
TRACE_HANDLER_STATS(isolate(), LoadIC_NonReceiver);
@ -446,9 +441,7 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name) {
if (*name == ReadOnlyRoots(isolate()).iterator_symbol()) {
return Runtime::ThrowIteratorError(isolate(), object);
}
return TypeError(IsAnyHas() ? MessageTemplate::kInvalidInOperatorUse
: MessageTemplate::kNonObjectPropertyLoad,
object, name);
return TypeError(MessageTemplate::kNonObjectPropertyLoad, object, name);
}
if (MigrateDeprecated(object)) use_ic = false;
@ -457,11 +450,9 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name) {
JSObject::MakePrototypesFast(object, kStartAtReceiver, isolate());
update_receiver_map(object);
}
LookupIterator it(isolate(), object, name);
// Named lookup in the object.
LookupForRead(&it, IsAnyHas());
LookupIterator it(isolate(), object, name);
LookupForRead(isolate(), &it);
if (name->IsPrivate()) {
if (name->IsPrivateName() && !it.IsFound()) {
@ -482,14 +473,6 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name) {
// Update inline cache and stub cache.
if (use_ic) UpdateCaches(&it);
if (IsAnyHas()) {
// Named lookup in the object.
Maybe<bool> maybe = JSReceiver::HasProperty(&it);
if (maybe.IsNothing()) return MaybeHandle<Object>();
return maybe.FromJust() ? ReadOnlyRoots(isolate()).true_value_handle()
: ReadOnlyRoots(isolate()).false_value_handle();
}
// Get the property.
Handle<Object> result;
@ -636,6 +619,7 @@ void IC::UpdateMonomorphicIC(const MaybeObjectHandle& handler,
ConfigureVectorState(name, receiver_map(), handler);
}
void IC::CopyICToMegamorphicCache(Handle<Name> name) {
MapHandles maps;
MaybeObjectHandles handlers;
@ -670,7 +654,7 @@ void IC::PatchCache(Handle<Name> name, Handle<Object> handler) {
void IC::PatchCache(Handle<Name> name, const MaybeObjectHandle& handler) {
DCHECK(IsHandler(*handler));
// Currently only load and store ICs support non-code handlers.
DCHECK(IsAnyLoad() || IsAnyStore() || IsAnyHas());
DCHECK(IsAnyLoad() || IsAnyStore());
switch (state()) {
case NO_FEEDBACK:
break;
@ -744,7 +728,6 @@ void LoadIC::UpdateCaches(LookupIterator* lookup) {
}
StubCache* IC::stub_cache() {
DCHECK(!IsAnyHas());
if (IsAnyLoad()) {
return isolate()->load_stub_cache();
} else {
@ -755,15 +738,13 @@ StubCache* IC::stub_cache() {
void IC::UpdateMegamorphicCache(Handle<Map> map, Handle<Name> name,
const MaybeObjectHandle& handler) {
if (!IsAnyHas()) {
stub_cache()->Set(*name, *map, *handler);
}
stub_cache()->Set(*name, *map, *handler);
}
void IC::TraceHandlerCacheHitStats(LookupIterator* lookup) {
DCHECK_EQ(LookupIterator::ACCESSOR, lookup->state());
if (V8_LIKELY(!FLAG_runtime_stats)) return;
if (IsAnyLoad() || IsAnyHas()) {
if (IsAnyLoad()) {
TRACE_HANDLER_STATS(isolate(), LoadIC_HandlerCacheHit_Accessor);
} else {
DCHECK(IsAnyStore());
@ -774,29 +755,22 @@ void IC::TraceHandlerCacheHitStats(LookupIterator* lookup) {
Handle<Object> LoadIC::ComputeHandler(LookupIterator* lookup) {
Handle<Object> receiver = lookup->GetReceiver();
ReadOnlyRoots roots(isolate());
if (receiver->IsString() && *lookup->name() == roots.length_string()) {
TRACE_HANDLER_STATS(isolate(), LoadIC_StringLength);
return BUILTIN_CODE(isolate(), LoadIC_StringLength);
}
// `in` cannot be called on strings, and will always return true for string
// wrapper length and function prototypes. The latter two cases are given
// LoadHandler::LoadNativeDataProperty below.
if (!IsAnyHas()) {
if (receiver->IsString() && *lookup->name() == roots.length_string()) {
TRACE_HANDLER_STATS(isolate(), LoadIC_StringLength);
return BUILTIN_CODE(isolate(), LoadIC_StringLength);
}
if (receiver->IsStringWrapper() && *lookup->name() == roots.length_string()) {
TRACE_HANDLER_STATS(isolate(), LoadIC_StringWrapperLength);
return BUILTIN_CODE(isolate(), LoadIC_StringWrapperLength);
}
if (receiver->IsStringWrapper() &&
*lookup->name() == roots.length_string()) {
TRACE_HANDLER_STATS(isolate(), LoadIC_StringWrapperLength);
return BUILTIN_CODE(isolate(), LoadIC_StringWrapperLength);
}
// Use specialized code for getting prototype of functions.
if (receiver->IsJSFunction() &&
*lookup->name() == roots.prototype_string() &&
!JSFunction::cast(*receiver)->PrototypeRequiresRuntimeLookup()) {
TRACE_HANDLER_STATS(isolate(), LoadIC_FunctionPrototypeStub);
return BUILTIN_CODE(isolate(), LoadIC_FunctionPrototype);
}
// Use specialized code for getting prototype of functions.
if (receiver->IsJSFunction() && *lookup->name() == roots.prototype_string() &&
!JSFunction::cast(*receiver)->PrototypeRequiresRuntimeLookup()) {
Handle<Code> stub;
TRACE_HANDLER_STATS(isolate(), LoadIC_FunctionPrototypeStub);
return BUILTIN_CODE(isolate(), LoadIC_FunctionPrototype);
}
Handle<Map> map = receiver_map();
@ -1144,30 +1118,22 @@ bool AllowConvertHoleElementToUndefined(Isolate* isolate,
Handle<Object> KeyedLoadIC::LoadElementHandler(Handle<Map> receiver_map,
KeyedAccessLoadMode load_mode) {
// Has a getter interceptor, or is any has and has a query interceptor.
if (receiver_map->has_indexed_interceptor() &&
(!receiver_map->GetIndexedInterceptor()->getter()->IsUndefined(
isolate()) ||
(IsAnyHas() &&
!receiver_map->GetIndexedInterceptor()->query()->IsUndefined(
isolate()))) &&
!receiver_map->GetIndexedInterceptor()->getter()->IsUndefined(
isolate()) &&
!receiver_map->GetIndexedInterceptor()->non_masking()) {
// TODO(jgruber): Update counter name.
TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_LoadIndexedInterceptorStub);
return IsAnyHas() ? BUILTIN_CODE(isolate(), HasIndexedInterceptorIC)
: BUILTIN_CODE(isolate(), LoadIndexedInterceptorIC);
return BUILTIN_CODE(isolate(), LoadIndexedInterceptorIC);
}
InstanceType instance_type = receiver_map->instance_type();
if (instance_type < FIRST_NONSTRING_TYPE) {
TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_LoadIndexedStringDH);
if (IsAnyHas()) return BUILTIN_CODE(isolate(), HasIC_Slow);
return LoadHandler::LoadIndexedString(isolate(), load_mode);
}
if (instance_type < FIRST_JS_RECEIVER_TYPE) {
TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_SlowStub);
return IsAnyHas() ? BUILTIN_CODE(isolate(), HasIC_Slow)
: BUILTIN_CODE(isolate(), KeyedLoadIC_Slow);
return BUILTIN_CODE(isolate(), KeyedLoadIC_Slow);
}
if (instance_type == JS_PROXY_TYPE) {
return LoadHandler::LoadProxy(isolate());
@ -1177,8 +1143,7 @@ Handle<Object> KeyedLoadIC::LoadElementHandler(Handle<Map> receiver_map,
if (IsSloppyArgumentsElementsKind(elements_kind)) {
// TODO(jgruber): Update counter name.
TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_KeyedLoadSloppyArgumentsStub);
return IsAnyHas() ? BUILTIN_CODE(isolate(), KeyedHasIC_SloppyArguments)
: BUILTIN_CODE(isolate(), KeyedLoadIC_SloppyArguments);
return BUILTIN_CODE(isolate(), KeyedLoadIC_SloppyArguments);
}
bool is_js_array = instance_type == JS_ARRAY_TYPE;
if (elements_kind == DICTIONARY_ELEMENTS) {
@ -1279,27 +1244,14 @@ KeyedAccessLoadMode GetLoadMode(Isolate* isolate, Handle<Object> receiver,
} // namespace
MaybeHandle<Object> KeyedLoadIC::RuntimeLoad(Handle<Object> object,
Handle<Object> key) {
Handle<Object> result;
if (IsKeyedLoadIC()) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate(), result, Runtime::GetObjectProperty(isolate(), object, key),
Object);
} else {
DCHECK(IsKeyedHasIC());
ASSIGN_RETURN_ON_EXCEPTION(isolate(), result,
Runtime::HasProperty(isolate(), object, key),
Object);
}
return result;
}
MaybeHandle<Object> KeyedLoadIC::Load(Handle<Object> object,
Handle<Object> key) {
if (MigrateDeprecated(object)) {
return RuntimeLoad(object, key);
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate(), result, Runtime::GetObjectProperty(isolate(), object, key),
Object);
return result;
}
Handle<Object> load_handle;
@ -1330,7 +1282,11 @@ MaybeHandle<Object> KeyedLoadIC::Load(Handle<Object> object,
if (!load_handle.is_null()) return load_handle;
return RuntimeLoad(object, key);
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(isolate(), result,
Runtime::GetObjectProperty(isolate(), object, key),
Object);
return result;
}
bool StoreIC::LookupForWrite(LookupIterator* it, Handle<Object> value,
@ -1889,6 +1845,7 @@ void KeyedStoreIC::UpdateStoreElement(Handle<Map> receiver_map,
}
}
Handle<Map> KeyedStoreIC::ComputeTransitionedMap(
Handle<Map> map, KeyedAccessStoreMode store_mode) {
switch (store_mode) {
@ -2026,6 +1983,7 @@ void KeyedStoreIC::StoreElementPolymorphicHandlers(
}
}
static KeyedAccessStoreMode GetStoreMode(Handle<JSObject> receiver,
uint32_t index, Handle<Object> value) {
bool oob_access = IsOutOfBoundsAccess(receiver, index);
@ -2070,6 +2028,7 @@ static KeyedAccessStoreMode GetStoreMode(Handle<JSObject> receiver,
}
}
MaybeHandle<Object> KeyedStoreIC::Store(Handle<Object> object,
Handle<Object> key,
Handle<Object> value) {
@ -2863,6 +2822,7 @@ RUNTIME_FUNCTION(Runtime_LoadPropertyWithInterceptor) {
isolate, NewReferenceError(MessageTemplate::kNotDefined, it.name()));
}
RUNTIME_FUNCTION(Runtime_StorePropertyWithInterceptor) {
HandleScope scope(isolate);
DCHECK_EQ(5, args.length());
@ -2910,6 +2870,7 @@ RUNTIME_FUNCTION(Runtime_StorePropertyWithInterceptor) {
return *value;
}
RUNTIME_FUNCTION(Runtime_LoadElementWithInterceptor) {
// TODO(verwaest): This should probably get the holder and receiver as input.
HandleScope scope(isolate);
@ -2935,61 +2896,5 @@ RUNTIME_FUNCTION(Runtime_LoadElementWithInterceptor) {
return *result;
}
RUNTIME_FUNCTION(Runtime_KeyedHasIC_Miss) {
HandleScope scope(isolate);
DCHECK_EQ(4, args.length());
// Runtime functions don't follow the IC's calling convention.
Handle<Object> receiver = args.at(0);
Handle<Object> key = args.at(1);
Handle<Smi> slot = args.at<Smi>(2);
Handle<HeapObject> maybe_vector = args.at<HeapObject>(3);
Handle<FeedbackVector> vector = Handle<FeedbackVector>();
if (!maybe_vector->IsUndefined()) {
DCHECK(maybe_vector->IsFeedbackVector());
vector = Handle<FeedbackVector>::cast(maybe_vector);
}
FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value());
KeyedLoadIC ic(isolate, vector, vector_slot, FeedbackSlotKind::kHasKeyed);
ic.UpdateState(receiver, key);
RETURN_RESULT_OR_FAILURE(isolate, ic.Load(receiver, key));
}
RUNTIME_FUNCTION(Runtime_HasElementWithInterceptor) {
HandleScope scope(isolate);
Handle<JSObject> receiver = args.at<JSObject>(0);
DCHECK_GE(args.smi_at(1), 0);
uint32_t index = args.smi_at(1);
Handle<InterceptorInfo> interceptor(receiver->GetIndexedInterceptor(),
isolate);
PropertyCallbackArguments arguments(isolate, interceptor->data(), *receiver,
*receiver, Just(kDontThrow));
if (!interceptor->query()->IsUndefined(isolate)) {
Handle<Object> result = arguments.CallIndexedQuery(interceptor, index);
if (!result.is_null()) {
int32_t value;
CHECK(result->ToInt32(&value));
return value == ABSENT ? ReadOnlyRoots(isolate).false_value()
: ReadOnlyRoots(isolate).true_value();
}
} else if (!interceptor->getter()->IsUndefined(isolate)) {
Handle<Object> result = arguments.CallIndexedGetter(interceptor, index);
if (!result.is_null()) {
return ReadOnlyRoots(isolate).true_value();
}
}
LookupIterator it(isolate, receiver, index, receiver);
DCHECK_EQ(LookupIterator::INTERCEPTOR, it.state());
it.Next();
Maybe<bool> maybe = JSReceiver::HasProperty(&it);
if (maybe.IsNothing()) return ReadOnlyRoots(isolate).exception();
return maybe.FromJust() ? ReadOnlyRoots(isolate).true_value()
: ReadOnlyRoots(isolate).false_value();
}
} // namespace internal
} // namespace v8

View File

@ -50,7 +50,6 @@ class IC {
state_ = RECOMPUTE_HANDLER;
}
bool IsAnyHas() const { return IsKeyedHasIC(); }
bool IsAnyLoad() const {
return IsLoadIC() || IsLoadGlobalIC() || IsKeyedLoadIC();
}
@ -131,10 +130,9 @@ class IC {
bool IsStoreIC() const { return IsStoreICKind(kind_); }
bool IsStoreOwnIC() const { return IsStoreOwnICKind(kind_); }
bool IsKeyedStoreIC() const { return IsKeyedStoreICKind(kind_); }
bool IsKeyedHasIC() const { return IsKeyedHasICKind(kind_); }
bool is_keyed() const {
return IsKeyedLoadIC() || IsKeyedStoreIC() ||
IsStoreInArrayLiteralICKind(kind_) || IsKeyedHasIC();
IsStoreInArrayLiteralICKind(kind_);
}
bool ShouldRecomputeHandler(Handle<String> name);
@ -202,12 +200,13 @@ class IC {
DISALLOW_IMPLICIT_CONSTRUCTORS(IC);
};
class LoadIC : public IC {
public:
LoadIC(Isolate* isolate, Handle<FeedbackVector> vector, FeedbackSlot slot,
FeedbackSlotKind kind)
: IC(isolate, vector, slot, kind) {
DCHECK(IsAnyLoad() || IsAnyHas());
DCHECK(IsAnyLoad());
}
static bool ShouldThrowReferenceError(FeedbackSlotKind kind) {
@ -223,8 +222,7 @@ class LoadIC : public IC {
protected:
virtual Handle<Code> slow_stub() const {
return IsAnyHas() ? BUILTIN_CODE(isolate(), HasIC_Slow)
: BUILTIN_CODE(isolate(), LoadIC_Slow);
return BUILTIN_CODE(isolate(), LoadIC_Slow);
}
// Update the inline cache and the global stub cache based on the
@ -262,9 +260,6 @@ class KeyedLoadIC : public LoadIC {
Handle<Object> key);
protected:
V8_WARN_UNUSED_RESULT MaybeHandle<Object> RuntimeLoad(Handle<Object> object,
Handle<Object> key);
// receiver is HeapObject because it could be a String or a JSObject
void UpdateLoadElement(Handle<HeapObject> receiver,
KeyedAccessLoadMode load_mode);
@ -285,6 +280,7 @@ class KeyedLoadIC : public LoadIC {
bool CanChangeToAllowOutOfBounds(Handle<Map> receiver_map);
};
class StoreIC : public IC {
public:
StoreIC(Isolate* isolate, Handle<FeedbackVector> vector, FeedbackSlot slot,
@ -335,8 +331,10 @@ class StoreGlobalIC : public StoreIC {
enum KeyedStoreCheckMap { kDontCheckMap, kCheckMap };
enum KeyedStoreIncrementLength { kDontIncrementLength, kIncrementLength };
class KeyedStoreIC : public StoreIC {
public:
KeyedAccessStoreMode GetKeyedAccessStoreMode() {

View File

@ -506,8 +506,17 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CompareOperation(
case Token::Value::INSTANCEOF:
OutputTestInstanceOf(reg, feedback_slot);
break;
default:
UNREACHABLE();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CompareOperation(Token::Value op,
Register reg) {
switch (op) {
case Token::Value::IN:
OutputTestIn(reg, feedback_slot);
OutputTestIn(reg);
break;
default:
UNREACHABLE();

View File

@ -379,6 +379,7 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final {
// Tests.
BytecodeArrayBuilder& CompareOperation(Token::Value op, Register reg,
int feedback_slot);
BytecodeArrayBuilder& CompareOperation(Token::Value op, Register reg);
BytecodeArrayBuilder& CompareReference(Register reg);
BytecodeArrayBuilder& CompareUndetectable();
BytecodeArrayBuilder& CompareUndefined();

View File

@ -4850,15 +4850,15 @@ void BytecodeGenerator::VisitCompareOperation(CompareOperation* expr) {
Register lhs = VisitForRegisterValue(expr->left());
VisitForAccumulatorValue(expr->right());
builder()->SetExpressionPosition(expr);
FeedbackSlot slot;
if (expr->op() == Token::IN) {
slot = feedback_spec()->AddKeyedHasICSlot();
builder()->CompareOperation(expr->op(), lhs);
} else if (expr->op() == Token::INSTANCEOF) {
slot = feedback_spec()->AddInstanceOfSlot();
FeedbackSlot slot = feedback_spec()->AddInstanceOfSlot();
builder()->CompareOperation(expr->op(), lhs, feedback_index(slot));
} else {
slot = feedback_spec()->AddCompareICSlot();
FeedbackSlot slot = feedback_spec()->AddCompareICSlot();
builder()->CompareOperation(expr->op(), lhs, feedback_index(slot));
}
builder()->CompareOperation(expr->op(), lhs, feedback_index(slot));
}
// Always returns a boolean value.
execution_result()->SetResultIsBoolean();

View File

@ -235,7 +235,7 @@ namespace interpreter {
V(TestReferenceEqual, AccumulatorUse::kReadWrite, OperandType::kReg) \
V(TestInstanceOf, AccumulatorUse::kReadWrite, OperandType::kReg, \
OperandType::kIdx) \
V(TestIn, AccumulatorUse::kReadWrite, OperandType::kReg, OperandType::kIdx) \
V(TestIn, AccumulatorUse::kReadWrite, OperandType::kReg) \
V(TestUndetectable, AccumulatorUse::kReadWrite) \
V(TestNull, AccumulatorUse::kReadWrite) \
V(TestUndefined, AccumulatorUse::kReadWrite) \

View File

@ -1876,22 +1876,16 @@ IGNITION_HANDLER(TestReferenceEqual, InterpreterAssembler) {
Dispatch();
}
// TestIn <src> <feedback_slot>
// TestIn <src>
//
// Test if the object referenced by the register operand is a property of the
// object referenced by the accumulator.
IGNITION_HANDLER(TestIn, InterpreterAssembler) {
Node* name = LoadRegisterAtOperandIndex(0);
Node* property = LoadRegisterAtOperandIndex(0);
Node* object = GetAccumulator();
Node* raw_slot = BytecodeOperandIdx(1);
Node* smi_slot = SmiTag(raw_slot);
Node* feedback_vector = LoadFeedbackVectorUnchecked();
Node* context = GetContext();
VARIABLE(var_result, MachineRepresentation::kTagged);
var_result.Bind(CallBuiltin(Builtins::kKeyedHasIC, context, object, name,
smi_slot, feedback_vector));
SetAccumulator(var_result.value());
SetAccumulator(HasProperty(context, object, property, kHasProperty));
Dispatch();
}

View File

@ -1128,22 +1128,21 @@ void FeedbackVector::FeedbackSlotPrint(std::ostream& os,
void FeedbackNexus::Print(std::ostream& os) { // NOLINT
switch (kind()) {
case FeedbackSlotKind::kCall:
case FeedbackSlotKind::kCloneObject:
case FeedbackSlotKind::kHasKeyed:
case FeedbackSlotKind::kInstanceOf:
case FeedbackSlotKind::kLoadProperty:
case FeedbackSlotKind::kLoadKeyed:
case FeedbackSlotKind::kLoadGlobalInsideTypeof:
case FeedbackSlotKind::kLoadGlobalNotInsideTypeof:
case FeedbackSlotKind::kLoadKeyed:
case FeedbackSlotKind::kLoadProperty:
case FeedbackSlotKind::kStoreDataPropertyInLiteral:
case FeedbackSlotKind::kStoreGlobalSloppy:
case FeedbackSlotKind::kStoreGlobalStrict:
case FeedbackSlotKind::kStoreInArrayLiteral:
case FeedbackSlotKind::kStoreKeyedSloppy:
case FeedbackSlotKind::kStoreKeyedStrict:
case FeedbackSlotKind::kStoreNamedSloppy:
case FeedbackSlotKind::kStoreNamedStrict:
case FeedbackSlotKind::kStoreOwnNamed: {
case FeedbackSlotKind::kStoreOwnNamed:
case FeedbackSlotKind::kStoreGlobalSloppy:
case FeedbackSlotKind::kStoreGlobalStrict:
case FeedbackSlotKind::kStoreKeyedSloppy:
case FeedbackSlotKind::kInstanceOf:
case FeedbackSlotKind::kStoreDataPropertyInLiteral:
case FeedbackSlotKind::kStoreKeyedStrict:
case FeedbackSlotKind::kStoreInArrayLiteral:
case FeedbackSlotKind::kCloneObject: {
os << InlineCacheState2String(ic_state());
break;
}

View File

@ -53,30 +53,6 @@ MaybeHandle<Object> Runtime::GetObjectProperty(Isolate* isolate,
return result;
}
MaybeHandle<Object> Runtime::HasProperty(Isolate* isolate,
Handle<Object> object,
Handle<Object> key) {
// Check that {object} is actually a receiver.
if (!object->IsJSReceiver()) {
THROW_NEW_ERROR(
isolate,
NewTypeError(MessageTemplate::kInvalidInOperatorUse, key, object),
Object);
}
Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object);
// Convert the {key} to a name.
Handle<Name> name;
ASSIGN_RETURN_ON_EXCEPTION(isolate, name, Object::ToName(isolate, key),
Object);
// Lookup the {name} on {receiver}.
Maybe<bool> maybe = JSReceiver::HasProperty(receiver, name);
if (maybe.IsNothing()) return MaybeHandle<Object>();
return maybe.FromJust() ? ReadOnlyRoots(isolate).true_value_handle()
: ReadOnlyRoots(isolate).false_value_handle();
}
namespace {
bool DeleteObjectPropertyFast(Isolate* isolate, Handle<JSReceiver> receiver,

View File

@ -574,9 +574,7 @@ namespace internal {
F(StoreIC_Miss, 5, 1) \
F(StoreInArrayLiteralIC_Slow, 5, 1) \
F(StorePropertyWithInterceptor, 5, 1) \
F(CloneObjectIC_Miss, 4, 1) \
F(KeyedHasIC_Miss, 4, 1) \
F(HasElementWithInterceptor, 2, 1)
F(CloneObjectIC_Miss, 4, 1)
#define FOR_EACH_INTRINSIC_RETURN_OBJECT_IMPL(F, I) \
FOR_EACH_INTRINSIC_ARRAY(F, I) \
@ -711,9 +709,6 @@ class Runtime : public AllStatic {
Isolate* isolate, Handle<Object> object, Handle<Object> key,
bool* is_found_out = nullptr);
V8_WARN_UNUSED_RESULT static MaybeHandle<Object> HasProperty(
Isolate* isolate, Handle<Object> object, Handle<Object> key);
V8_WARN_UNUSED_RESULT static MaybeHandle<JSArray> GetInternalProperties(
Isolate* isolate, Handle<Object>);

View File

@ -171,11 +171,11 @@ snippet: "
"
frame size: 0
parameter count: 3
bytecode array length: 15
bytecode array length: 14
bytecodes: [
/* 10 E> */ B(StackCheck),
/* 19 S> */ B(Ldar), R(arg1),
/* 25 E> */ B(TestIn), R(arg0), U8(0),
/* 25 E> */ B(TestIn), R(arg0),
B(JumpIfFalse), U8(7),
/* 33 S> */ B(Wide), B(LdaSmi), I16(200),
/* 44 S> */ B(Return),
@ -396,7 +396,7 @@ snippet: "
"
frame size: 0
parameter count: 3
bytecode array length: 83
bytecode array length: 82
bytecodes: [
/* 10 E> */ B(StackCheck),
/* 21 S> */ B(Ldar), R(arg1),
@ -430,12 +430,12 @@ bytecodes: [
/* 174 S> */ B(LdaSmi), I8(1),
/* 183 S> */ B(Return),
/* 188 S> */ B(Ldar), R(arg1),
/* 194 E> */ B(TestIn), R(arg0), U8(6),
/* 194 E> */ B(TestIn), R(arg0),
B(JumpIfFalse), U8(5),
/* 202 S> */ B(LdaSmi), I8(1),
/* 211 S> */ B(Return),
/* 216 S> */ B(Ldar), R(arg1),
/* 222 E> */ B(TestInstanceOf), R(arg0), U8(8),
/* 222 E> */ B(TestInstanceOf), R(arg0), U8(6),
B(JumpIfFalse), U8(5),
/* 238 S> */ B(LdaSmi), I8(1),
/* 247 S> */ B(Return),

View File

@ -2275,26 +2275,21 @@ TEST(InterpreterTestIn) {
const char* properties[] = {"length", "fuzzle", "x", "0"};
for (size_t i = 0; i < arraysize(properties); i++) {
bool expected_value = (i == 0);
FeedbackVectorSpec feedback_spec(zone);
BytecodeArrayBuilder builder(zone, 1, 1, &feedback_spec);
BytecodeArrayBuilder builder(zone, 1, 1);
Register r0(0);
builder.LoadLiteral(ast_factory.GetOneByteString(properties[i]))
.StoreAccumulatorInRegister(r0);
FeedbackSlot slot = feedback_spec.AddKeyedHasICSlot();
Handle<i::FeedbackMetadata> metadata =
NewFeedbackMetadata(isolate, &feedback_spec);
size_t array_entry = builder.AllocateDeferredConstantPoolEntry();
builder.SetDeferredConstantPoolEntry(array_entry, array);
builder.LoadConstantPoolEntry(array_entry)
.CompareOperation(Token::Value::IN, r0, GetIndex(slot))
.CompareOperation(Token::Value::IN, r0)
.Return();
ast_factory.Internalize(isolate);
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray(isolate);
InterpreterTester tester(isolate, bytecode_array, metadata);
InterpreterTester tester(isolate, bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->IsBoolean());

View File

@ -470,9 +470,6 @@ THREADED_TEST(QueryInterceptor) {
v8_compile("Object.isFrozen('obj.x');")->Run(env.local()).ToLocalChecked();
CHECK_EQ(8, query_counter_int);
v8_compile("'x' in obj;")->Run(env.local()).ToLocalChecked();
CHECK_EQ(9, query_counter_int);
}
namespace {
@ -877,14 +874,15 @@ THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
CHECK(!value->BooleanValue(isolate));
}
static void CheckInterceptorIC(v8::GenericNamedPropertyGetterCallback getter,
v8::GenericNamedPropertyQueryCallback query,
const char* source, int expected) {
static void CheckInterceptorLoadIC(
v8::GenericNamedPropertyGetterCallback getter, const char* source,
int expected) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
getter, nullptr, query, nullptr, nullptr, v8_str("data")));
getter, nullptr, nullptr, nullptr, nullptr, v8_str("data")));
LocalContext context;
context->Global()
->Set(context.local(), v8_str("o"),
@ -894,11 +892,6 @@ static void CheckInterceptorIC(v8::GenericNamedPropertyGetterCallback getter,
CHECK_EQ(expected, value->Int32Value(context.local()).FromJust());
}
static void CheckInterceptorLoadIC(
v8::GenericNamedPropertyGetterCallback getter, const char* source,
int expected) {
CheckInterceptorIC(getter, nullptr, source, expected);
}
static void InterceptorLoadICGetter(
Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
@ -1439,92 +1432,6 @@ THREADED_TEST(InterceptorReturningZero) {
0);
}
namespace {
template <typename TKey, v8::internal::PropertyAttributes attribute>
void HasICQuery(TKey name, const v8::PropertyCallbackInfo<v8::Integer>& info) {
ApiTestFuzzer::Fuzz();
v8::Isolate* isolate = CcTest::isolate();
CHECK_EQ(isolate, info.GetIsolate());
info.GetReturnValue().Set(v8::Integer::New(isolate, attribute));
}
template <typename TKey>
void HasICQueryToggle(TKey name,
const v8::PropertyCallbackInfo<v8::Integer>& info) {
ApiTestFuzzer::Fuzz();
static bool toggle = false;
toggle = !toggle;
v8::Isolate* isolate = CcTest::isolate();
CHECK_EQ(isolate, info.GetIsolate());
info.GetReturnValue().Set(v8::Integer::New(
isolate, toggle ? v8::internal::ABSENT : v8::internal::NONE));
}
int named_query_counter = 0;
void NamedQueryCallback(Local<Name> name,
const v8::PropertyCallbackInfo<v8::Integer>& info) {
named_query_counter++;
}
} // namespace
THREADED_TEST(InterceptorHasIC) {
named_query_counter = 0;
CheckInterceptorIC(nullptr, NamedQueryCallback,
"var result = 0;"
"for (var i = 0; i < 1000; i++) {"
" 'x' in o;"
"}",
0);
CHECK_EQ(1000, named_query_counter);
}
THREADED_TEST(InterceptorHasICQueryAbsent) {
CheckInterceptorIC(nullptr, HasICQuery<Local<Name>, v8::internal::ABSENT>,
"var result = 0;"
"for (var i = 0; i < 1000; i++) {"
" if ('x' in o) ++result;"
"}",
0);
}
THREADED_TEST(InterceptorHasICQueryNone) {
CheckInterceptorIC(nullptr, HasICQuery<Local<Name>, v8::internal::NONE>,
"var result = 0;"
"for (var i = 0; i < 1000; i++) {"
" if ('x' in o) ++result;"
"}",
1000);
}
THREADED_TEST(InterceptorHasICGetter) {
CheckInterceptorIC(InterceptorLoadICGetter, nullptr,
"var result = 0;"
"for (var i = 0; i < 1000; i++) {"
" if ('x' in o) ++result;"
"}",
1000);
}
THREADED_TEST(InterceptorHasICQueryGetter) {
CheckInterceptorIC(InterceptorLoadICGetter,
HasICQuery<Local<Name>, v8::internal::ABSENT>,
"var result = 0;"
"for (var i = 0; i < 1000; i++) {"
" if ('x' in o) ++result;"
"}",
0);
}
THREADED_TEST(InterceptorHasICQueryToggle) {
CheckInterceptorIC(InterceptorLoadICGetter, HasICQueryToggle<Local<Name>>,
"var result = 0;"
"for (var i = 0; i < 1000; i++) {"
" if ('x' in o) ++result;"
"}",
500);
}
static void InterceptorStoreICSetter(
Local<Name> key, Local<Value> value,
@ -3366,101 +3273,6 @@ THREADED_TEST(IndexedInterceptorOnProto) {
ExpectString(code, "PASSED");
}
namespace {
void CheckIndexedInterceptorHasIC(v8::IndexedPropertyGetterCallback getter,
v8::IndexedPropertyQueryCallback query,
const char* source, int expected) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
getter, nullptr, query, nullptr, nullptr, v8_str("data")));
LocalContext context;
context->Global()
->Set(context.local(), v8_str("o"),
templ->NewInstance(context.local()).ToLocalChecked())
.FromJust();
v8::Local<Value> value = CompileRun(source);
CHECK_EQ(expected, value->Int32Value(context.local()).FromJust());
}
int indexed_query_counter = 0;
void IndexedQueryCallback(uint32_t index,
const v8::PropertyCallbackInfo<v8::Integer>& info) {
indexed_query_counter++;
}
void IndexHasICQueryAbsent(uint32_t index,
const v8::PropertyCallbackInfo<v8::Integer>& info) {
ApiTestFuzzer::Fuzz();
v8::Isolate* isolate = CcTest::isolate();
CHECK_EQ(isolate, info.GetIsolate());
info.GetReturnValue().Set(v8::Integer::New(isolate, v8::internal::ABSENT));
}
} // namespace
THREADED_TEST(IndexedInterceptorHasIC) {
indexed_query_counter = 0;
CheckIndexedInterceptorHasIC(nullptr, IndexedQueryCallback,
"var result = 0;"
"for (var i = 0; i < 1000; i++) {"
" i in o;"
"}",
0);
CHECK_EQ(1000, indexed_query_counter);
}
THREADED_TEST(IndexedInterceptorHasICQueryAbsent) {
CheckIndexedInterceptorHasIC(nullptr,
// HasICQuery<uint32_t, v8::internal::ABSENT>,
IndexHasICQueryAbsent,
"var result = 0;"
"for (var i = 0; i < 1000; i++) {"
" if (i in o) ++result;"
"}",
0);
}
THREADED_TEST(IndexedInterceptorHasICQueryNone) {
CheckIndexedInterceptorHasIC(nullptr,
HasICQuery<uint32_t, v8::internal::NONE>,
"var result = 0;"
"for (var i = 0; i < 1000; i++) {"
" if (i in o) ++result;"
"}",
1000);
}
THREADED_TEST(IndexedInterceptorHasICGetter) {
CheckIndexedInterceptorHasIC(IdentityIndexedPropertyGetter, nullptr,
"var result = 0;"
"for (var i = 0; i < 1000; i++) {"
" if (i in o) ++result;"
"}",
1000);
}
THREADED_TEST(IndexedInterceptorHasICQueryGetter) {
CheckIndexedInterceptorHasIC(IdentityIndexedPropertyGetter,
HasICQuery<uint32_t, v8::internal::ABSENT>,
"var result = 0;"
"for (var i = 0; i < 1000; i++) {"
" if (i in o) ++result;"
"}",
0);
}
THREADED_TEST(IndexedInterceptorHasICQueryToggle) {
CheckIndexedInterceptorHasIC(IdentityIndexedPropertyGetter,
HasICQueryToggle<uint32_t>,
"var result = 0;"
"for (var i = 0; i < 1000; i++) {"
" if (i in o) ++result;"
"}",
500);
}
static void NoBlockGetterX(Local<Name> name,
const v8::PropertyCallbackInfo<v8::Value>&) {}

View File

@ -366,14 +366,3 @@ assertEquals(117, arg_set(0xFFFFFFFF));
f7(1,2,3,4,5,6,7);
f7(1,2,3,4,5,6,7,8);
})();
(function testArgumentsHole() {
function f(a) {
arguments[3] = 1;
return arguments[2];
};
assertEquals(undefined, f(1));
assertEquals(undefined, f(1));
assertEquals(undefined, f(1));
})();

View File

@ -1,9 +0,0 @@
// Copyright 2019 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.
// MODULE
export var a = "A";
export var b = "B";
export var c = "C";

View File

@ -1,70 +0,0 @@
// Copyright 2019 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.
// Flags: --allow-natives-syntax
// MODULE
import * as mod from "keyed-has-ic-module-export.js";
function testIn(obj, key) {
return key in obj;
}
function expectTrue(obj, key) {
assertTrue(testIn(obj, key));
}
function expectFalse(obj, key) {
assertFalse(testIn(obj, key));
}
var tests = {
monomporphicTrue: function() {
expectTrue(mod, "a");
expectTrue(mod, "a");
expectTrue(mod, "a");
},
polymprohicKeyTrue: function() {
expectTrue(mod, "a");
expectTrue(mod, "b");
expectTrue(mod, "c");
},
monomorphicFalse: function() {
expectFalse(mod, "d");
expectFalse(mod, "d");
expectFalse(mod, "d");
},
polymorphicKeyFalse: function() {
expectFalse(mod, "d");
expectFalse(mod, "e");
expectFalse(mod, "f");
},
polymorphicTrue: function() {
var o = {a: "A"};
expectTrue(mod, "a");
expectTrue(o, "a");
expectTrue(mod, "a");
expectTrue(o, "a");
},
polymorphicFalse: function() {
var o = {a: "A"};
expectFalse(mod, "d");
expectFalse(o, "d");
expectFalse(mod, "d");
expectFalse(o, "d");
}
};
for (let test in tests) {
%DeoptimizeFunction(testIn);
%ClearFunctionFeedback(testIn);
tests[test]();
%OptimizeFunctionOnNextCall(testIn);
tests[test]();
}

View File

@ -1,402 +0,0 @@
// Copyright 2019 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.
// Flags: --allow-natives-syntax
function testIn(obj, key) {
return key in obj;
}
function expectTrue(obj, key) {
assertTrue(testIn(obj, key));
}
function expectFalse(obj, key) {
assertFalse(testIn(obj, key));
}
var tests = {
TestMonomorphicPackedSMIArray: function() {
var a = [0, 1, 2];
expectTrue(a, 0);
expectTrue(a, 1);
expectTrue(a, 2);
expectFalse(a, 3);
},
TestMonomorphicPackedArrayPrototypeProperty: function()
{
var a = [0, 1, 2];
expectTrue(a, 0);
expectTrue(a, 1);
expectFalse(a, 3);
Array.prototype[3] = 3;
expectTrue(a, 3);
// ensure the prototype change doesn't affect later tests
delete Array.prototype[3];
assertFalse((3 in Array.prototype));
expectFalse(a, 3);
},
TestMonomorphicPackedDoubleArray: function() {
var a = [0.0, 1.1, 2.2];
expectTrue(a, 0);
expectTrue(a, 1);
expectTrue(a, 2);
expectFalse(a, 3);
},
TestMonomorphicPackedArray: function() {
var a = ["A", "B", {}];
expectTrue(a, 0);
expectTrue(a, 1);
expectTrue(a, 2);
expectFalse(a, 3);
},
TestMonomorphicHoleyArray: function() {
var a = [0, 1, 2];
a[4] = 4;
expectTrue(a, 0);
expectTrue(a, 1);
expectTrue(a, 2);
expectFalse(a, 3);
expectTrue(a, 4);
},
TestMonomorphicTypedArray: function() {
var a = new Int32Array(3);
expectTrue(a, 0);
expectTrue(a, 1);
expectTrue(a, 2);
expectFalse(a, 3);
expectFalse(a, 4);
},
TestPolymorphicPackedArrays: function() {
var a = [0, 1, 2];
var b = [0.0, 1.1, 2.2];
var c = ["A", "B", {}];
expectTrue(c, 0);
expectTrue(b, 1);
expectTrue(a, 2);
expectTrue(c, 1);
expectTrue(b, 2);
expectTrue(a, 0);
expectFalse(c, 3);
expectFalse(b, 4);
expectFalse(a, 5);
},
TestPolymorphicMixedArrays: function() {
var a = new Array(3); // holey SMI
var b = [0.0,1.1,2.2]; // packed double
var c = new Int8Array(3); // typed array
expectFalse(a, 0);
expectTrue(b, 1);
expectTrue(c, 2);
expectFalse(a, 1);
expectTrue(b, 2);
expectTrue(c, 0);
expectFalse(a, 3);
expectFalse(b, 4);
expectFalse(c, 5);
},
TestMegamorphicArrays: function() {
var a = [0,1,2,3] // packed SMI
var b = new Array(3); // holey SMI
var c = [0.0,1.1,2.2]; // packed double
var d = ['a', 'b', 'c'] // packed
var e = new Int8Array(3); // typed array
var f = new Uint8Array(3); // typed array
var g = new Int32Array(3); // typed array
expectTrue(a, 0);
expectFalse(b, 1);
expectTrue(c, 2);
expectFalse(d, 3);
expectFalse(e, 4);
expectFalse(f, 5);
expectFalse(g, 6);
expectFalse(a, 5);
expectFalse(b, 4);
expectFalse(c, 3);
expectTrue(d, 2);
expectTrue(e, 1);
expectTrue(f, 0);
expectTrue(g, 0);
},
TestMonomorphicObject: function() {
var a = { a: "A", b: "B" };
expectTrue(a, 'a');
expectTrue(a, 'a');
expectTrue(a, 'a');
},
TestMonomorphicProxyHasPropertyNoTrap: function() {
var a = new Proxy({a: 'A'}, {});
expectTrue(a, 'a');
expectTrue(a, 'a');
expectTrue(a, 'a');
},
TestMonomorphicProxyNoPropertyNoTrap: function() {
var a = new Proxy({}, {});
expectFalse(a, 'a');
expectFalse(a, 'a');
expectFalse(a, 'a');
},
TestMonomorphicProxyHasPropertyHasTrap: function() {
var a = new Proxy({a: 'A'}, { has: function() {return false;}});
expectFalse(a, 'a');
expectFalse(a, 'a');
expectFalse(a, 'a');
},
TestMonomorphicProxyNoPropertyHasTrap: function() {
var a = new Proxy({}, { has: function() { return true; }});
expectTrue(a, 'a');
expectTrue(a, 'a');
expectTrue(a, 'a');
},
TestMonomorphicObjectPrototype: function() {
var a = { b: "B" };
expectFalse(a, 'a');
expectFalse(a, 'a');
expectFalse(a, 'a');
Object.prototype.a = 'A';
expectTrue(a, 'a');
delete Object.prototype.a;
assertFalse((a in Object.prototype));
expectFalse(a, 'a');
},
TestPolymorphicObject: function() {
var a = { a: "A" };
var b = { a: "A", b: "B" };
var c = { b: "B", c: "C" };
expectTrue(a, 'a');
expectTrue(a, 'a');
expectTrue(b, 'a');
expectFalse(c, 'a');
expectTrue(a, 'a');
expectTrue(b, 'a');
expectFalse(c, 'a');
},
TestMegamorphicObject: function() {
var a = { a: "A" };
var b = { a: "A", b: "B" };
var c = { b: "B", c: "C" };
var d = { b: "A", a: "B" };
var e = { e: "E", a: "A" };
var f = { f: "F", b: "B", c: "C" };
expectTrue(a, 'a');
expectTrue(a, 'a');
expectTrue(b, 'a');
expectFalse(c, 'a');
expectTrue(d, 'a');
expectTrue(e, 'a');
expectFalse(f, 'a');
expectTrue(a, 'a');
expectTrue(b, 'a');
expectFalse(c, 'a');
expectTrue(d, 'a');
expectTrue(e, 'a');
expectFalse(f, 'a');
},
TestPolymorphicKeys: function() {
var a = { a: "A", b: "B" };
expectTrue(a, 'a');
expectTrue(a, 'b');
expectFalse(a, 'c');
expectTrue(a, 'a');
expectTrue(a, 'b');
expectFalse(a, 'c');
expectTrue(a, 'a');
expectTrue(a, 'b');
expectFalse(a, 'c');
},
TestPolymorphicMixed: function() {
var a = { a: "A" };
var b = new Proxy({}, {});
var c = new Int32Array(3);
expectTrue(a, 'a');
expectTrue(a, 'a');
expectFalse(b, 'a');
expectFalse(c, 'a');
expectTrue(a, 'a');
expectFalse(b, 'a');
expectFalse(c, 'a');
},
};
for (test in tests) {
%DeoptimizeFunction(testIn);
%ClearFunctionFeedback(testIn);
tests[test]();
%OptimizeFunctionOnNextCall(testIn);
tests[test]();
}
// test function prototypes.
(function() {
var o = function() {};
var proto = function() {
assertTrue("prototype" in o);
o.prototype;
}
proto();
proto();
proto();
})();
// `in` is not allowed on string
(function() {
function test() {
0 in "string"
};
assertThrows(test, TypeError);
})();
// `in` is allowed on `this` even when `this` is a string
(function() {
function test() {
assertTrue("length" in this);
};
test.call("");
test.call("");
test.call("");
})();
(function() {
var index = 0;
function test(i) {
return index in arguments;
};
assertFalse(test())
assertFalse(test())
assertTrue(test(0));
assertTrue(test(0,1));
index = 2;
assertFalse(test())
assertFalse(test(0));
assertFalse(test(0,1));
assertTrue(test(0,1,2));
})();
(function() {
function test(a) {
arguments[3] = 1;
return 2 in arguments;
};
assertFalse(test(1));
assertFalse(test(1));
assertFalse(test(1));
})();
(function() {
function test(o, k) {
try {
k in o;
} catch (e) {
return false;
}
return true;
}
var str = "string";
// this will place slow_stub in the IC for strings.
assertFalse(test(str, "length"));
assertFalse(test(str, "length"));
// this turns the cache polymorphic, and causes generats LoadElement
// handlers for everything in the cache. This test ensures that
// KeyedLoadIC::LoadElementHandler can handle seeing string maps.
var ary = [0,1,2,3];
assertTrue(test(ary, 1));
assertTrue(test(ary, 1));
assertFalse(test(str, 0));
assertFalse(test(str, 0));
})();
(function() {
function test(o, k) {
try {
k in o;
} catch (e) {
return false;
}
return true;
}
var str = "string";
assertFalse(test(str, "length"));
assertFalse(test(str, "length"));
assertFalse(test(str, "length"));
})();
(function() {
function test(o, k) {
try {
k in o;
} catch (e) {
return false;
}
return true;
}
var str = "string";
assertFalse(test(str, 0));
assertFalse(test(str, 0));
assertFalse(test(str, 0));
})();
(function() {
function test(o, k) {
try {
k in o;
} catch (e) {
return false;
}
return true;
}
var ary = [0,1,2,3];
assertTrue(test(ary, 1));
assertTrue(test(ary, 1));
var str = "string";
assertFalse(test(str, 0));
assertFalse(test(str, 0));
assertFalse(test(str, 0));
})();

View File

@ -261,7 +261,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.CompareOperation(Token::Value::GTE, reg, 6)
.CompareTypeOf(TestTypeOfFlags::LiteralFlag::kNumber)
.CompareOperation(Token::Value::INSTANCEOF, reg, 7)
.CompareOperation(Token::Value::IN, reg, 8)
.CompareOperation(Token::Value::IN, reg)
.CompareReference(reg)
.CompareUndetectable()
.CompareUndefined()

View File

@ -89,7 +89,7 @@ TEST(OperandScaling, ScalableAndNonScalable) {
1 + 2 + 2 * scale);
CHECK_EQ(Bytecodes::Size(Bytecode::kCreateObjectLiteral, operand_scale),
1 + 2 * scale + 1);
CHECK_EQ(Bytecodes::Size(Bytecode::kTestIn, operand_scale), 1 + 2 * scale);
CHECK_EQ(Bytecodes::Size(Bytecode::kTestIn, operand_scale), 1 + scale);
}
}