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:
parent
f94cd449e4
commit
d2729be4ae
@ -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 */ \
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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>( // --
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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()))) {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
187
src/ic/ic.cc
187
src/ic/ic.cc
@ -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
|
||||
|
16
src/ic/ic.h
16
src/ic/ic.h
@ -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() {
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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) \
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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>);
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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());
|
||||
|
@ -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>&) {}
|
||||
|
@ -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));
|
||||
})();
|
||||
|
@ -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";
|
@ -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]();
|
||||
}
|
@ -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));
|
||||
})();
|
@ -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()
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user