Line data Source code
1 : // Copyright 2015 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "src/compiler/js-native-context-specialization.h"
6 :
7 : #include "src/accessors.h"
8 : #include "src/api.h"
9 : #include "src/code-factory.h"
10 : #include "src/compilation-dependencies.h"
11 : #include "src/compiler/access-builder.h"
12 : #include "src/compiler/access-info.h"
13 : #include "src/compiler/allocation-builder.h"
14 : #include "src/compiler/js-graph.h"
15 : #include "src/compiler/js-operator.h"
16 : #include "src/compiler/linkage.h"
17 : #include "src/compiler/node-matchers.h"
18 : #include "src/compiler/property-access-builder.h"
19 : #include "src/compiler/type-cache.h"
20 : #include "src/feedback-vector.h"
21 : #include "src/field-index-inl.h"
22 : #include "src/isolate-inl.h"
23 :
24 : namespace v8 {
25 : namespace internal {
26 : namespace compiler {
27 :
28 : namespace {
29 :
30 : bool HasNumberMaps(MapHandles const& maps) {
31 89115 : for (auto map : maps) {
32 34901 : if (map->instance_type() == HEAP_NUMBER_TYPE) return true;
33 : }
34 : return false;
35 : }
36 :
37 : bool HasOnlyJSArrayMaps(MapHandles const& maps) {
38 55499 : for (auto map : maps) {
39 20238 : if (!map->IsJSArrayMap()) return false;
40 : }
41 : return true;
42 : }
43 :
44 : } // namespace
45 :
46 : struct JSNativeContextSpecialization::ScriptContextTableLookupResult {
47 : Handle<Context> context;
48 : bool immutable;
49 : int index;
50 : };
51 :
52 443380 : JSNativeContextSpecialization::JSNativeContextSpecialization(
53 : Editor* editor, JSGraph* jsgraph, Flags flags,
54 : Handle<Context> native_context, CompilationDependencies* dependencies,
55 : Zone* zone)
56 : : AdvancedReducer(editor),
57 : jsgraph_(jsgraph),
58 : flags_(flags),
59 : global_object_(native_context->global_object()),
60 443382 : global_proxy_(JSGlobalProxy::cast(native_context->global_proxy())),
61 : native_context_(native_context),
62 : dependencies_(dependencies),
63 : zone_(zone),
64 1330142 : type_cache_(TypeCache::Get()) {}
65 :
66 32189354 : Reduction JSNativeContextSpecialization::Reduce(Node* node) {
67 32189354 : switch (node->opcode()) {
68 : case IrOpcode::kJSAdd:
69 95697 : return ReduceJSAdd(node);
70 : case IrOpcode::kJSGetSuperConstructor:
71 4578 : return ReduceJSGetSuperConstructor(node);
72 : case IrOpcode::kJSInstanceOf:
73 3751 : return ReduceJSInstanceOf(node);
74 : case IrOpcode::kJSHasInPrototypeChain:
75 1605 : return ReduceJSHasInPrototypeChain(node);
76 : case IrOpcode::kJSOrdinaryHasInstance:
77 1732 : return ReduceJSOrdinaryHasInstance(node);
78 : case IrOpcode::kJSLoadContext:
79 816367 : return ReduceJSLoadContext(node);
80 : case IrOpcode::kJSLoadGlobal:
81 599578 : return ReduceJSLoadGlobal(node);
82 : case IrOpcode::kJSStoreGlobal:
83 185569 : return ReduceJSStoreGlobal(node);
84 : case IrOpcode::kJSLoadNamed:
85 607454 : return ReduceJSLoadNamed(node);
86 : case IrOpcode::kJSStoreNamed:
87 266529 : return ReduceJSStoreNamed(node);
88 : case IrOpcode::kJSLoadProperty:
89 61466 : return ReduceJSLoadProperty(node);
90 : case IrOpcode::kJSStoreProperty:
91 65893 : return ReduceJSStoreProperty(node);
92 : case IrOpcode::kJSStoreNamedOwn:
93 45461 : return ReduceJSStoreNamedOwn(node);
94 : case IrOpcode::kJSStoreDataPropertyInLiteral:
95 55213 : return ReduceJSStoreDataPropertyInLiteral(node);
96 : default:
97 : break;
98 : }
99 : return NoChange();
100 : }
101 :
102 99805 : Reduction JSNativeContextSpecialization::ReduceJSAdd(Node* node) {
103 : // TODO(turbofan): This has to run together with the inlining and
104 : // native context specialization to be able to leverage the string
105 : // constant-folding for optimizing property access, but we should
106 : // nevertheless find a better home for this at some point.
107 : DCHECK_EQ(IrOpcode::kJSAdd, node->opcode());
108 :
109 : // Constant-fold string concatenation.
110 95697 : HeapObjectBinopMatcher m(node);
111 227348 : if (m.left().HasValue() && m.left().Value()->IsString() &&
112 117768 : m.right().HasValue() && m.right().Value()->IsString()) {
113 : Handle<String> left = Handle<String>::cast(m.left().Value());
114 : Handle<String> right = Handle<String>::cast(m.right().Value());
115 4108 : if (left->length() + right->length() <= String::kMaxLength) {
116 : Handle<String> result =
117 8216 : factory()->NewConsString(left, right).ToHandleChecked();
118 4108 : Node* value = jsgraph()->HeapConstant(result);
119 4108 : ReplaceWithValue(node, value);
120 : return Replace(value);
121 : }
122 : }
123 : return NoChange();
124 : }
125 :
126 4578 : Reduction JSNativeContextSpecialization::ReduceJSGetSuperConstructor(
127 9092 : Node* node) {
128 : DCHECK_EQ(IrOpcode::kJSGetSuperConstructor, node->opcode());
129 4578 : Node* constructor = NodeProperties::GetValueInput(node, 0);
130 :
131 : // Check if the input is a known JSFunction.
132 : HeapObjectMatcher m(constructor);
133 4578 : if (!m.HasValue()) return NoChange();
134 : Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
135 : Handle<Map> function_map(function->map(), isolate());
136 : Handle<Object> function_prototype(function_map->prototype(), isolate());
137 :
138 : // We can constant-fold the super constructor access if the
139 : // {function}s map is stable, i.e. we can use a code dependency
140 : // to guard against [[Prototype]] changes of {function}.
141 4561 : if (function_map->is_stable()) {
142 4546 : Node* value = jsgraph()->Constant(function_prototype);
143 4546 : dependencies()->AssumeMapStable(function_map);
144 4546 : if (function_prototype->IsConstructor()) {
145 4522 : ReplaceWithValue(node, value);
146 : return Replace(value);
147 : }
148 : }
149 :
150 : return NoChange();
151 : }
152 :
153 10780 : Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
154 : DCHECK_EQ(IrOpcode::kJSInstanceOf, node->opcode());
155 3759 : Node* object = NodeProperties::GetValueInput(node, 0);
156 3759 : Node* constructor = NodeProperties::GetValueInput(node, 1);
157 3759 : Node* context = NodeProperties::GetContextInput(node);
158 3759 : Node* effect = NodeProperties::GetEffectInput(node);
159 3759 : Node* frame_state = NodeProperties::GetFrameStateInput(node);
160 3759 : Node* control = NodeProperties::GetControlInput(node);
161 :
162 : // Check if the right hand side is a known {receiver}.
163 : HeapObjectMatcher m(constructor);
164 5650 : if (!m.HasValue() || !m.Value()->IsJSObject()) return NoChange();
165 : Handle<JSObject> receiver = Handle<JSObject>::cast(m.Value());
166 : Handle<Map> receiver_map(receiver->map(), isolate());
167 :
168 : // Compute property access info for @@hasInstance on {receiver}.
169 1888 : PropertyAccessInfo access_info;
170 : AccessInfoFactory access_info_factory(dependencies(), native_context(),
171 3776 : graph()->zone());
172 1888 : if (!access_info_factory.ComputePropertyAccessInfo(
173 : receiver_map, factory()->has_instance_symbol(), AccessMode::kLoad,
174 1888 : &access_info)) {
175 : return NoChange();
176 : }
177 :
178 : PropertyAccessBuilder access_builder(jsgraph(), dependencies());
179 :
180 1717 : if (access_info.IsNotFound()) {
181 : // If there's no @@hasInstance handler, the OrdinaryHasInstance operation
182 : // takes over, but that requires the {receiver} to be callable.
183 8 : if (receiver->IsCallable()) {
184 : // Determine actual holder and perform prototype chain checks.
185 : Handle<JSObject> holder;
186 8 : if (access_info.holder().ToHandle(&holder)) {
187 : access_builder.AssumePrototypesStable(
188 0 : native_context(), access_info.receiver_maps(), holder);
189 : }
190 :
191 : // Monomorphic property access.
192 : access_builder.BuildCheckMaps(constructor, &effect, control,
193 8 : access_info.receiver_maps());
194 :
195 : // Lower to OrdinaryHasInstance(C, O).
196 8 : NodeProperties::ReplaceValueInput(node, constructor, 0);
197 8 : NodeProperties::ReplaceValueInput(node, object, 1);
198 8 : NodeProperties::ReplaceEffectInput(node, effect);
199 8 : NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance());
200 8 : Reduction const reduction = ReduceJSOrdinaryHasInstance(node);
201 8 : return reduction.Changed() ? reduction : Changed(node);
202 : }
203 1709 : } else if (access_info.IsDataConstant() ||
204 : access_info.IsDataConstantField()) {
205 : // Determine actual holder and perform prototype chain checks.
206 : Handle<JSObject> holder;
207 1708 : if (access_info.holder().ToHandle(&holder)) {
208 : access_builder.AssumePrototypesStable(
209 1674 : native_context(), access_info.receiver_maps(), holder);
210 : } else {
211 34 : holder = receiver;
212 : }
213 :
214 : Handle<Object> constant;
215 1708 : if (access_info.IsDataConstant()) {
216 : DCHECK(!FLAG_track_constant_fields);
217 1708 : constant = access_info.constant();
218 : } else {
219 : DCHECK(FLAG_track_constant_fields);
220 : DCHECK(access_info.IsDataConstantField());
221 : // The value must be callable therefore tagged.
222 : DCHECK(CanBeTaggedPointer(access_info.field_representation()));
223 0 : FieldIndex field_index = access_info.field_index();
224 : constant = JSObject::FastPropertyAt(holder, Representation::Tagged(),
225 0 : field_index);
226 : }
227 : DCHECK(constant->IsCallable());
228 :
229 : // Monomorphic property access.
230 : access_builder.BuildCheckMaps(constructor, &effect, control,
231 1708 : access_info.receiver_maps());
232 :
233 : // Create a nested frame state inside the current method's most-recent frame
234 : // state that will ensure that deopts that happen after this point will not
235 : // fallback to the last Checkpoint--which would completely re-execute the
236 : // instanceof logic--but rather create an activation of a version of the
237 : // ToBoolean stub that finishes the remaining work of instanceof and returns
238 : // to the caller without duplicating side-effects upon a lazy deopt.
239 : Node* continuation_frame_state = CreateStubBuiltinContinuationFrameState(
240 : jsgraph(), Builtins::kToBooleanLazyDeoptContinuation, context, nullptr,
241 1708 : 0, frame_state, ContinuationFrameStateMode::LAZY);
242 :
243 : // Call the @@hasInstance handler.
244 1708 : Node* target = jsgraph()->Constant(constant);
245 1708 : node->InsertInput(graph()->zone(), 0, target);
246 1708 : node->ReplaceInput(1, constructor);
247 1708 : node->ReplaceInput(2, object);
248 1708 : node->ReplaceInput(4, continuation_frame_state);
249 1708 : node->ReplaceInput(5, effect);
250 : NodeProperties::ChangeOp(
251 : node, javascript()->Call(3, CallFrequency(), VectorSlotPair(),
252 3416 : ConvertReceiverMode::kNotNullOrUndefined));
253 :
254 : // Rewire the value uses of {node} to ToBoolean conversion of the result.
255 : Node* value =
256 3416 : graph()->NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), node);
257 19158 : for (Edge edge : node->use_edges()) {
258 12367 : if (NodeProperties::IsValueEdge(edge) && edge.from() != value) {
259 1934 : edge.UpdateTo(value);
260 3868 : Revisit(edge.from());
261 : }
262 : }
263 : return Changed(node);
264 : }
265 :
266 : return NoChange();
267 : }
268 :
269 : JSNativeContextSpecialization::InferHasInPrototypeChainResult
270 3188 : JSNativeContextSpecialization::InferHasInPrototypeChain(
271 : Node* receiver, Node* effect, Handle<HeapObject> prototype) {
272 : ZoneHandleSet<Map> receiver_maps;
273 : NodeProperties::InferReceiverMapsResult result =
274 3188 : NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
275 3188 : if (result == NodeProperties::kNoReceiverMaps) return kMayBeInPrototypeChain;
276 :
277 : // Check if either all or none of the {receiver_maps} have the given
278 : // {prototype} in their prototype chain.
279 : bool all = true;
280 : bool none = true;
281 116 : for (size_t i = 0; i < receiver_maps.size(); ++i) {
282 : Handle<Map> receiver_map = receiver_maps[i];
283 48 : if (receiver_map->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) {
284 : return kMayBeInPrototypeChain;
285 : }
286 34 : if (result == NodeProperties::kUnreliableReceiverMaps) {
287 : // In case of an unreliable {result} we need to ensure that all
288 : // {receiver_maps} are stable, because otherwise we cannot trust
289 : // the {receiver_maps} information, since arbitrary side-effects
290 : // may have happened.
291 11 : if (!receiver_map->is_stable()) {
292 : return kMayBeInPrototypeChain;
293 : }
294 : }
295 51 : for (PrototypeIterator j(receiver_map);; j.Advance()) {
296 51 : if (j.IsAtEnd()) {
297 : all = false;
298 : break;
299 : }
300 : Handle<HeapObject> const current =
301 : PrototypeIterator::GetCurrent<HeapObject>(j);
302 43 : if (current.is_identical_to(prototype)) {
303 : none = false;
304 : break;
305 : }
306 34 : if (!current->map()->is_stable() ||
307 : current->map()->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) {
308 : return kMayBeInPrototypeChain;
309 : }
310 : }
311 : }
312 : DCHECK_IMPLIES(all, !none);
313 : DCHECK_IMPLIES(none, !all);
314 :
315 34 : if (all) return kIsInPrototypeChain;
316 8 : if (none) return kIsNotInPrototypeChain;
317 0 : return kMayBeInPrototypeChain;
318 : }
319 :
320 3188 : Reduction JSNativeContextSpecialization::ReduceJSHasInPrototypeChain(
321 34 : Node* node) {
322 : DCHECK_EQ(IrOpcode::kJSHasInPrototypeChain, node->opcode());
323 3188 : Node* value = NodeProperties::GetValueInput(node, 0);
324 3188 : Node* prototype = NodeProperties::GetValueInput(node, 1);
325 3188 : Node* effect = NodeProperties::GetEffectInput(node);
326 :
327 : // Check if we can constant-fold the prototype chain walk
328 : // for the given {value} and the {prototype}.
329 : HeapObjectMatcher m(prototype);
330 3188 : if (m.HasValue()) {
331 : InferHasInPrototypeChainResult result =
332 3188 : InferHasInPrototypeChain(value, effect, m.Value());
333 3188 : if (result != kMayBeInPrototypeChain) {
334 68 : Node* value = jsgraph()->BooleanConstant(result == kIsInPrototypeChain);
335 34 : ReplaceWithValue(node, value);
336 : return Replace(value);
337 : }
338 : }
339 :
340 : return NoChange();
341 : }
342 :
343 1740 : Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance(
344 3174 : Node* node) {
345 : DCHECK_EQ(IrOpcode::kJSOrdinaryHasInstance, node->opcode());
346 1740 : Node* constructor = NodeProperties::GetValueInput(node, 0);
347 1740 : Node* object = NodeProperties::GetValueInput(node, 1);
348 :
349 : // Check if the {constructor} is known at compile time.
350 : HeapObjectMatcher m(constructor);
351 1740 : if (!m.HasValue()) return NoChange();
352 :
353 : // Check if the {constructor} is a JSBoundFunction.
354 1740 : if (m.Value()->IsJSBoundFunction()) {
355 : // OrdinaryHasInstance on bound functions turns into a recursive
356 : // invocation of the instanceof operator again.
357 : // ES6 section 7.3.19 OrdinaryHasInstance (C, O) step 2.
358 : Handle<JSBoundFunction> function = Handle<JSBoundFunction>::cast(m.Value());
359 : Handle<JSReceiver> bound_target_function(function->bound_target_function());
360 8 : NodeProperties::ReplaceValueInput(node, object, 0);
361 : NodeProperties::ReplaceValueInput(
362 8 : node, jsgraph()->HeapConstant(bound_target_function), 1);
363 8 : NodeProperties::ChangeOp(node, javascript()->InstanceOf());
364 8 : Reduction const reduction = ReduceJSInstanceOf(node);
365 8 : return reduction.Changed() ? reduction : Changed(node);
366 : }
367 :
368 : // Check if the {constructor} is a JSFunction.
369 1732 : if (m.Value()->IsJSFunction()) {
370 : // Check if the {function} is a constructor and has an instance "prototype".
371 : Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
372 5036 : if (function->IsConstructor() && function->has_prototype_slot() &&
373 4981 : function->has_instance_prototype() &&
374 1605 : function->prototype()->IsJSReceiver()) {
375 : // Ensure that the {function} has a valid initial map, so we can
376 : // depend on that for the prototype constant-folding below.
377 1583 : JSFunction::EnsureHasInitialMap(function);
378 :
379 : // Install a code dependency on the {function}s initial map.
380 : Handle<Map> initial_map(function->initial_map(), isolate());
381 : dependencies()->AssumeInitialMapCantChange(initial_map);
382 : Node* prototype =
383 1583 : jsgraph()->Constant(handle(initial_map->prototype(), isolate()));
384 :
385 : // Lower the {node} to JSHasInPrototypeChain.
386 1583 : NodeProperties::ReplaceValueInput(node, object, 0);
387 1583 : NodeProperties::ReplaceValueInput(node, prototype, 1);
388 1583 : NodeProperties::ChangeOp(node, javascript()->HasInPrototypeChain());
389 1583 : Reduction const reduction = ReduceJSHasInPrototypeChain(node);
390 1583 : return reduction.Changed() ? reduction : Changed(node);
391 : }
392 : }
393 :
394 : return NoChange();
395 : }
396 :
397 827892 : Reduction JSNativeContextSpecialization::ReduceJSLoadContext(Node* node) {
398 : DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
399 816367 : ContextAccess const& access = ContextAccessOf(node->op());
400 : // Specialize JSLoadContext(NATIVE_CONTEXT_INDEX) to the known native
401 : // context (if any), so we can constant-fold those fields, which is
402 : // safe, since the NATIVE_CONTEXT_INDEX slot is always immutable.
403 816367 : if (access.index() == Context::NATIVE_CONTEXT_INDEX) {
404 11525 : Node* value = jsgraph()->HeapConstant(native_context());
405 11525 : ReplaceWithValue(node, value);
406 : return Replace(value);
407 : }
408 : return NoChange();
409 : }
410 :
411 : namespace {
412 :
413 105953 : FieldAccess ForPropertyCellValue(MachineRepresentation representation,
414 : Type* type, MaybeHandle<Map> map,
415 : Handle<Name> name) {
416 : WriteBarrierKind kind = kFullWriteBarrier;
417 105953 : if (representation == MachineRepresentation::kTaggedSigned) {
418 : kind = kNoWriteBarrier;
419 45955 : } else if (representation == MachineRepresentation::kTaggedPointer) {
420 : kind = kPointerWriteBarrier;
421 : }
422 105953 : MachineType r = MachineType::TypeForRepresentation(representation);
423 : FieldAccess access = {
424 211906 : kTaggedBase, PropertyCell::kValueOffset, name, map, type, r, kind};
425 105953 : return access;
426 : }
427 :
428 : } // namespace
429 :
430 775479 : Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
431 : Node* node, Node* receiver, Node* value, Handle<Name> name,
432 1038081 : AccessMode access_mode, Node* index) {
433 775479 : Node* effect = NodeProperties::GetEffectInput(node);
434 775479 : Node* control = NodeProperties::GetControlInput(node);
435 :
436 : // Lookup on the global object. We only deal with own data properties
437 : // of the global object here (represented as PropertyCell).
438 775479 : LookupIterator it(global_object(), name, LookupIterator::OWN);
439 775479 : it.TryLookupCachedProperty();
440 775479 : if (it.state() != LookupIterator::DATA) return NoChange();
441 650781 : if (!it.GetHolder<JSObject>()->IsJSGlobalObject()) return NoChange();
442 650781 : Handle<PropertyCell> property_cell = it.GetPropertyCell();
443 : PropertyDetails property_details = property_cell->property_details();
444 : Handle<Object> property_cell_value(property_cell->value(), isolate());
445 : PropertyCellType property_cell_type = property_details.cell_type();
446 :
447 : // We have additional constraints for stores.
448 650781 : if (access_mode == AccessMode::kStore) {
449 153140 : if (property_details.IsReadOnly()) {
450 : // Don't even bother trying to lower stores to read-only data properties.
451 : return NoChange();
452 153067 : } else if (property_cell_type == PropertyCellType::kUndefined) {
453 : // There's no fast-path for dealing with undefined property cells.
454 : return NoChange();
455 37386 : } else if (property_cell_type == PropertyCellType::kConstantType) {
456 : // There's also no fast-path to store to a global cell which pretended
457 : // to be stable, but is no longer stable now.
458 28115 : if (property_cell_value->IsHeapObject() &&
459 : !Handle<HeapObject>::cast(property_cell_value)->map()->is_stable()) {
460 : return NoChange();
461 : }
462 : }
463 : }
464 :
465 : // Ensure that {index} matches the specified {name} (if {index} is given).
466 535027 : if (index != nullptr) {
467 9 : effect = BuildCheckEqualsName(name, index, effect, control);
468 : }
469 :
470 : // Check if we have a {receiver} to validate. If so, we need to check that
471 : // the {receiver} is actually the JSGlobalProxy for the native context that
472 : // we are specializing to.
473 535027 : if (receiver != nullptr) {
474 : Node* check = graph()->NewNode(simplified()->ReferenceEqual(), receiver,
475 624 : jsgraph()->HeapConstant(global_proxy()));
476 : effect = graph()->NewNode(
477 : simplified()->CheckIf(DeoptimizeReason::kReceiverNotAGlobalProxy),
478 208 : check, effect, control);
479 : }
480 :
481 535027 : if (access_mode == AccessMode::kLoad) {
482 : // Load from non-configurable, read-only data property on the global
483 : // object can be constant-folded, even without deoptimization support.
484 798085 : if (!property_details.IsConfigurable() && property_details.IsReadOnly()) {
485 8809 : value = jsgraph()->Constant(property_cell_value);
486 : } else {
487 : // Record a code dependency on the cell if we can benefit from the
488 : // additional feedback, or the global property is configurable (i.e.
489 : // can be deleted or reconfigured to an accessor property).
490 488832 : if (property_details.cell_type() != PropertyCellType::kMutable ||
491 : property_details.IsConfigurable()) {
492 : dependencies()->AssumePropertyCell(property_cell);
493 : }
494 :
495 : // Load from constant/undefined global property can be constant-folded.
496 488832 : if (property_details.cell_type() == PropertyCellType::kConstant ||
497 : property_details.cell_type() == PropertyCellType::kUndefined) {
498 418000 : value = jsgraph()->Constant(property_cell_value);
499 : } else {
500 : // Load from constant type cell can benefit from type feedback.
501 : MaybeHandle<Map> map;
502 : Type* property_cell_value_type = Type::NonInternal();
503 : MachineRepresentation representation = MachineRepresentation::kTagged;
504 70832 : if (property_details.cell_type() == PropertyCellType::kConstantType) {
505 : // Compute proper type based on the current value in the cell.
506 38903 : if (property_cell_value->IsSmi()) {
507 : property_cell_value_type = Type::SignedSmall();
508 : representation = MachineRepresentation::kTaggedSigned;
509 5640 : } else if (property_cell_value->IsNumber()) {
510 : property_cell_value_type = Type::Number();
511 : representation = MachineRepresentation::kTaggedPointer;
512 : } else {
513 : Handle<Map> property_cell_value_map(
514 : Handle<HeapObject>::cast(property_cell_value)->map(),
515 : isolate());
516 : property_cell_value_type = Type::For(property_cell_value_map);
517 : representation = MachineRepresentation::kTaggedPointer;
518 :
519 : // We can only use the property cell value map for map check
520 : // elimination if it's stable, i.e. the HeapObject wasn't
521 : // mutated without the cell state being updated.
522 4504 : if (property_cell_value_map->is_stable()) {
523 4498 : dependencies()->AssumeMapStable(property_cell_value_map);
524 4498 : map = property_cell_value_map;
525 : }
526 : }
527 : }
528 : value = effect = graph()->NewNode(
529 : simplified()->LoadField(ForPropertyCellValue(
530 : representation, property_cell_value_type, map, name)),
531 212496 : jsgraph()->HeapConstant(property_cell), effect, control);
532 : }
533 : }
534 : } else {
535 : DCHECK_EQ(AccessMode::kStore, access_mode);
536 : DCHECK(!property_details.IsReadOnly());
537 37386 : switch (property_details.cell_type()) {
538 : case PropertyCellType::kUndefined: {
539 0 : UNREACHABLE();
540 : break;
541 : }
542 : case PropertyCellType::kConstant: {
543 : // Record a code dependency on the cell, and just deoptimize if the new
544 : // value doesn't match the previous value stored inside the cell.
545 : dependencies()->AssumePropertyCell(property_cell);
546 : Node* check =
547 : graph()->NewNode(simplified()->ReferenceEqual(), value,
548 4530 : jsgraph()->Constant(property_cell_value));
549 : effect = graph()->NewNode(
550 : simplified()->CheckIf(DeoptimizeReason::kValueMismatch), check,
551 2265 : effect, control);
552 2265 : break;
553 : }
554 : case PropertyCellType::kConstantType: {
555 : // Record a code dependency on the cell, and just deoptimize if the new
556 : // values' type doesn't match the type of the previous value in the
557 : // cell.
558 : dependencies()->AssumePropertyCell(property_cell);
559 : Type* property_cell_value_type;
560 : MachineRepresentation representation = MachineRepresentation::kTagged;
561 27425 : if (property_cell_value->IsHeapObject()) {
562 : // We cannot do anything if the {property_cell_value}s map is no
563 : // longer stable.
564 : Handle<Map> property_cell_value_map(
565 : Handle<HeapObject>::cast(property_cell_value)->map(), isolate());
566 : DCHECK(property_cell_value_map->is_stable());
567 690 : dependencies()->AssumeMapStable(property_cell_value_map);
568 :
569 : // Check that the {value} is a HeapObject.
570 : value = effect = graph()->NewNode(simplified()->CheckHeapObject(),
571 690 : value, effect, control);
572 :
573 : // Check {value} map against the {property_cell} map.
574 : effect =
575 : graph()->NewNode(simplified()->CheckMaps(
576 : CheckMapsFlag::kNone,
577 : ZoneHandleSet<Map>(property_cell_value_map)),
578 1380 : value, effect, control);
579 : property_cell_value_type = Type::OtherInternal();
580 : representation = MachineRepresentation::kTaggedPointer;
581 : } else {
582 : // Check that the {value} is a Smi.
583 : value = effect = graph()->NewNode(simplified()->CheckSmi(), value,
584 26735 : effect, control);
585 : property_cell_value_type = Type::SignedSmall();
586 : representation = MachineRepresentation::kTaggedSigned;
587 : }
588 : effect = graph()->NewNode(simplified()->StoreField(ForPropertyCellValue(
589 : representation, property_cell_value_type,
590 27425 : MaybeHandle<Map>(), name)),
591 : jsgraph()->HeapConstant(property_cell), value,
592 137125 : effect, control);
593 27425 : break;
594 : }
595 : case PropertyCellType::kMutable: {
596 : // Record a code dependency on the cell, and just deoptimize if the
597 : // property ever becomes read-only.
598 : dependencies()->AssumePropertyCell(property_cell);
599 : effect = graph()->NewNode(
600 : simplified()->StoreField(ForPropertyCellValue(
601 : MachineRepresentation::kTagged, Type::NonInternal(),
602 7696 : MaybeHandle<Map>(), name)),
603 38480 : jsgraph()->HeapConstant(property_cell), value, effect, control);
604 7696 : break;
605 : }
606 : }
607 : }
608 :
609 535027 : ReplaceWithValue(node, value, effect, control);
610 : return Replace(value);
611 : }
612 :
613 609770 : Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) {
614 : DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode());
615 599578 : Handle<Name> name = LoadGlobalParametersOf(node->op()).name();
616 599578 : Node* effect = NodeProperties::GetEffectInput(node);
617 :
618 : // Try to lookup the name on the script context table first (lexical scoping).
619 : ScriptContextTableLookupResult result;
620 599578 : if (LookupInScriptContextTable(name, &result)) {
621 20594 : if (result.context->is_the_hole(isolate(), result.index)) return NoChange();
622 10192 : Node* context = jsgraph()->HeapConstant(result.context);
623 : Node* value = effect = graph()->NewNode(
624 : javascript()->LoadContext(0, result.index, result.immutable), context,
625 20384 : effect);
626 10192 : ReplaceWithValue(node, value, effect);
627 : return Replace(value);
628 : }
629 :
630 : // Lookup the {name} on the global object instead.
631 589281 : return ReduceGlobalAccess(node, nullptr, nullptr, name, AccessMode::kLoad);
632 : }
633 :
634 185588 : Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) {
635 : DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode());
636 185569 : Handle<Name> name = StoreGlobalParametersOf(node->op()).name();
637 185569 : Node* value = NodeProperties::GetValueInput(node, 0);
638 185569 : Node* effect = NodeProperties::GetEffectInput(node);
639 185569 : Node* control = NodeProperties::GetControlInput(node);
640 :
641 : // Try to lookup the name on the script context table first (lexical scoping).
642 : ScriptContextTableLookupResult result;
643 185569 : if (LookupInScriptContextTable(name, &result)) {
644 50 : if (result.context->is_the_hole(isolate(), result.index)) return NoChange();
645 24 : if (result.immutable) return NoChange();
646 19 : Node* context = jsgraph()->HeapConstant(result.context);
647 : effect = graph()->NewNode(javascript()->StoreContext(0, result.index),
648 38 : value, context, effect, control);
649 19 : ReplaceWithValue(node, value, effect, control);
650 : return Replace(value);
651 : }
652 :
653 : // Lookup the {name} on the global object instead.
654 185544 : return ReduceGlobalAccess(node, nullptr, value, name, AccessMode::kStore);
655 : }
656 :
657 112220 : Reduction JSNativeContextSpecialization::ReduceNamedAccess(
658 112220 : Node* node, Node* value, MapHandles const& receiver_maps, Handle<Name> name,
659 441987 : AccessMode access_mode, Node* index) {
660 : DCHECK(node->opcode() == IrOpcode::kJSLoadNamed ||
661 : node->opcode() == IrOpcode::kJSStoreNamed ||
662 : node->opcode() == IrOpcode::kJSLoadProperty ||
663 : node->opcode() == IrOpcode::kJSStoreProperty ||
664 : node->opcode() == IrOpcode::kJSStoreNamedOwn);
665 112220 : Node* receiver = NodeProperties::GetValueInput(node, 0);
666 112220 : Node* context = NodeProperties::GetContextInput(node);
667 112220 : Node* frame_state = NodeProperties::GetFrameStateInput(node);
668 112220 : Node* effect = NodeProperties::GetEffectInput(node);
669 112220 : Node* control = NodeProperties::GetControlInput(node);
670 :
671 : // Check if we have an access o.x or o.x=v where o is the current
672 : // native contexts' global proxy, and turn that into a direct access
673 : // to the current native contexts' global object instead.
674 112220 : if (receiver_maps.size() == 1) {
675 101079 : Handle<Map> receiver_map = receiver_maps.front();
676 101079 : if (receiver_map->IsJSGlobalProxyMap()) {
677 227 : Object* maybe_constructor = receiver_map->GetConstructor();
678 : // Detached global proxies have |null| as their constructor.
679 454 : if (maybe_constructor->IsJSFunction() &&
680 : JSFunction::cast(maybe_constructor)->native_context() ==
681 : *native_context()) {
682 : return ReduceGlobalAccess(node, receiver, value, name, access_mode,
683 227 : index);
684 : }
685 : }
686 : }
687 :
688 : // Compute property access infos for the receiver maps.
689 : AccessInfoFactory access_info_factory(dependencies(), native_context(),
690 223986 : graph()->zone());
691 : ZoneVector<PropertyAccessInfo> access_infos(zone());
692 111993 : if (!access_info_factory.ComputePropertyAccessInfos(
693 111993 : receiver_maps, name, access_mode, &access_infos)) {
694 : return NoChange();
695 : }
696 :
697 : // Nothing to do if we have no non-deprecated maps.
698 105667 : if (access_infos.empty()) {
699 : return ReduceSoftDeoptimize(
700 0 : node, DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess);
701 : }
702 :
703 : // Ensure that {index} matches the specified {name} (if {index} is given).
704 105667 : if (index != nullptr) {
705 99 : effect = BuildCheckEqualsName(name, index, effect, control);
706 : }
707 :
708 : // Collect call nodes to rewire exception edges.
709 : ZoneVector<Node*> if_exception_nodes(zone());
710 : ZoneVector<Node*>* if_exceptions = nullptr;
711 105667 : Node* if_exception = nullptr;
712 105667 : if (NodeProperties::IsExceptionalCall(node, &if_exception)) {
713 : if_exceptions = &if_exception_nodes;
714 : }
715 :
716 : PropertyAccessBuilder access_builder(jsgraph(), dependencies());
717 :
718 : // Check for the monomorphic cases.
719 211334 : if (access_infos.size() == 1) {
720 99000 : PropertyAccessInfo access_info = access_infos.front();
721 : // Try to build string check or number check if possible.
722 : // Otherwise build a map check.
723 99000 : if (!access_builder.TryBuildStringCheck(access_info.receiver_maps(),
724 194424 : &receiver, &effect, control) &&
725 : !access_builder.TryBuildNumberCheck(access_info.receiver_maps(),
726 95424 : &receiver, &effect, control)) {
727 : receiver =
728 95173 : access_builder.BuildCheckHeapObject(receiver, &effect, control);
729 : access_builder.BuildCheckMaps(receiver, &effect, control,
730 95173 : access_info.receiver_maps());
731 : }
732 :
733 : // Generate the actual property access.
734 : ValueEffectControl continuation = BuildPropertyAccess(
735 : receiver, value, context, frame_state, effect, control, name,
736 99000 : if_exceptions, access_info, access_mode);
737 99000 : value = continuation.value();
738 99000 : effect = continuation.effect();
739 99000 : control = continuation.control();
740 : } else {
741 : // The final states for every polymorphic branch. We join them with
742 : // Merge+Phi+EffectPhi at the bottom.
743 : ZoneVector<Node*> values(zone());
744 : ZoneVector<Node*> effects(zone());
745 : ZoneVector<Node*> controls(zone());
746 :
747 : // Check if {receiver} may be a number.
748 : bool receiverissmi_possible = false;
749 26779 : for (PropertyAccessInfo const& access_info : access_infos) {
750 13547 : if (HasNumberMaps(access_info.receiver_maps())) {
751 : receiverissmi_possible = true;
752 : break;
753 : }
754 : }
755 :
756 : // Ensure that {receiver} is a heap object.
757 : Node* receiverissmi_control = nullptr;
758 6667 : Node* receiverissmi_effect = effect;
759 6667 : if (receiverissmi_possible) {
760 204 : Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
761 204 : Node* branch = graph()->NewNode(common()->Branch(), check, control);
762 204 : control = graph()->NewNode(common()->IfFalse(), branch);
763 102 : receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch);
764 102 : receiverissmi_effect = effect;
765 : } else {
766 : receiver =
767 6565 : access_builder.BuildCheckHeapObject(receiver, &effect, control);
768 : }
769 :
770 : // Generate code for the various different property access patterns.
771 6667 : Node* fallthrough_control = control;
772 40658 : for (size_t j = 0; j < access_infos.size(); ++j) {
773 : PropertyAccessInfo const& access_info = access_infos[j];
774 : Node* this_value = value;
775 13662 : Node* this_receiver = receiver;
776 13662 : Node* this_effect = effect;
777 : Node* this_control = fallthrough_control;
778 :
779 : // Perform map check on {receiver}.
780 13662 : MapHandles const& receiver_maps = access_info.receiver_maps();
781 : {
782 : // Whether to insert a dedicated MapGuard node into the
783 : // effect to be able to learn from the control flow.
784 : bool insert_map_guard = true;
785 :
786 : // Check maps for the {receiver}s.
787 13662 : if (j == access_infos.size() - 1) {
788 : // Last map check on the fallthrough control path, do a
789 : // conditional eager deoptimization exit here.
790 : access_builder.BuildCheckMaps(receiver, &this_effect, this_control,
791 6667 : receiver_maps);
792 : fallthrough_control = nullptr;
793 :
794 : // Don't insert a MapGuard in this case, as the CheckMaps
795 : // node already gives you all the information you need
796 : // along the effect chain.
797 : insert_map_guard = false;
798 : } else {
799 : // Explicitly branch on the {receiver_maps}.
800 : ZoneHandleSet<Map> maps;
801 24768 : for (Handle<Map> map : receiver_maps) {
802 10778 : maps.insert(map, graph()->zone());
803 : }
804 : Node* check = this_effect =
805 : graph()->NewNode(simplified()->CompareMaps(maps), receiver,
806 20985 : this_effect, this_control);
807 : Node* branch =
808 6995 : graph()->NewNode(common()->Branch(), check, this_control);
809 6995 : fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
810 6995 : this_control = graph()->NewNode(common()->IfTrue(), branch);
811 : }
812 :
813 : // The Number case requires special treatment to also deal with Smis.
814 13662 : if (HasNumberMaps(receiver_maps)) {
815 : // Join this check with the "receiver is smi" check above.
816 : DCHECK_NOT_NULL(receiverissmi_effect);
817 : DCHECK_NOT_NULL(receiverissmi_control);
818 : this_control = graph()->NewNode(common()->Merge(2), this_control,
819 102 : receiverissmi_control);
820 : this_effect = graph()->NewNode(common()->EffectPhi(2), this_effect,
821 306 : receiverissmi_effect, this_control);
822 : receiverissmi_effect = receiverissmi_control = nullptr;
823 :
824 : // The {receiver} can also be a Smi in this case, so
825 : // a MapGuard doesn't make sense for this at all.
826 : insert_map_guard = false;
827 : }
828 :
829 : // Introduce a MapGuard to learn from this on the effect chain.
830 13662 : if (insert_map_guard) {
831 : ZoneHandleSet<Map> maps;
832 24531 : for (auto receiver_map : receiver_maps) {
833 10699 : maps.insert(receiver_map, graph()->zone());
834 : }
835 : this_effect = graph()->NewNode(simplified()->MapGuard(maps), receiver,
836 20748 : this_effect, this_control);
837 : }
838 : }
839 :
840 : // Generate the actual property access.
841 : ValueEffectControl continuation = BuildPropertyAccess(
842 : this_receiver, this_value, context, frame_state, this_effect,
843 13662 : this_control, name, if_exceptions, access_info, access_mode);
844 27324 : values.push_back(continuation.value());
845 27324 : effects.push_back(continuation.effect());
846 27324 : controls.push_back(continuation.control());
847 : }
848 :
849 : DCHECK_NULL(fallthrough_control);
850 :
851 : // Generate the final merge point for all (polymorphic) branches.
852 13334 : int const control_count = static_cast<int>(controls.size());
853 6667 : if (control_count == 0) {
854 0 : value = effect = control = jsgraph()->Dead();
855 6667 : } else if (control_count == 1) {
856 0 : value = values.front();
857 0 : effect = effects.front();
858 0 : control = controls.front();
859 : } else {
860 : control = graph()->NewNode(common()->Merge(control_count), control_count,
861 13334 : &controls.front());
862 6667 : values.push_back(control);
863 : value = graph()->NewNode(
864 : common()->Phi(MachineRepresentation::kTagged, control_count),
865 20001 : control_count + 1, &values.front());
866 6667 : effects.push_back(control);
867 : effect = graph()->NewNode(common()->EffectPhi(control_count),
868 13334 : control_count + 1, &effects.front());
869 : }
870 : }
871 :
872 : // Properly rewire IfException edges if {node} is inside a try-block.
873 105667 : if (!if_exception_nodes.empty()) {
874 : DCHECK_NOT_NULL(if_exception);
875 : DCHECK_EQ(if_exceptions, &if_exception_nodes);
876 170 : int const if_exception_count = static_cast<int>(if_exceptions->size());
877 : Node* merge = graph()->NewNode(common()->Merge(if_exception_count),
878 170 : if_exception_count, &if_exceptions->front());
879 85 : if_exceptions->push_back(merge);
880 : Node* ephi =
881 : graph()->NewNode(common()->EffectPhi(if_exception_count),
882 255 : if_exception_count + 1, &if_exceptions->front());
883 : Node* phi = graph()->NewNode(
884 : common()->Phi(MachineRepresentation::kTagged, if_exception_count),
885 170 : if_exception_count + 1, &if_exceptions->front());
886 105752 : ReplaceWithValue(if_exception, phi, ephi, merge);
887 : }
888 :
889 105667 : ReplaceWithValue(node, value, effect, control);
890 : return Replace(value);
891 : }
892 :
893 876589 : Reduction JSNativeContextSpecialization::ReduceNamedAccessFromNexus(
894 : Node* node, Node* value, FeedbackNexus const& nexus, Handle<Name> name,
895 : AccessMode access_mode) {
896 : DCHECK(node->opcode() == IrOpcode::kJSLoadNamed ||
897 : node->opcode() == IrOpcode::kJSStoreNamed ||
898 : node->opcode() == IrOpcode::kJSStoreNamedOwn);
899 876589 : Node* const receiver = NodeProperties::GetValueInput(node, 0);
900 876590 : Node* const effect = NodeProperties::GetEffectInput(node);
901 :
902 : // Check if we are accessing the current native contexts' global proxy.
903 : HeapObjectMatcher m(receiver);
904 1026512 : if (m.HasValue() && m.Value().is_identical_to(global_proxy())) {
905 : // Optimize accesses to the current native contexts' global proxy.
906 427 : return ReduceGlobalAccess(node, nullptr, value, name, access_mode);
907 : }
908 :
909 : // Check if the {nexus} reports type feedback for the IC.
910 876162 : if (nexus.IsUninitialized()) {
911 743779 : if (flags() & kBailoutOnUninitialized) {
912 : return ReduceSoftDeoptimize(
913 : node,
914 0 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess);
915 : }
916 : return NoChange();
917 : }
918 :
919 : // Extract receiver maps from the IC using the {nexus}.
920 : MapHandles receiver_maps;
921 132383 : if (!ExtractReceiverMaps(receiver, effect, nexus, &receiver_maps)) {
922 : return NoChange();
923 111582 : } else if (receiver_maps.empty()) {
924 38 : if (flags() & kBailoutOnUninitialized) {
925 : return ReduceSoftDeoptimize(
926 : node,
927 38 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess);
928 : }
929 : return NoChange();
930 : }
931 :
932 : // Try to lower the named access based on the {receiver_maps}.
933 111544 : return ReduceNamedAccess(node, value, receiver_maps, name, access_mode);
934 : }
935 :
936 1300439 : Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
937 : DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode());
938 607453 : NamedAccess const& p = NamedAccessOf(node->op());
939 607454 : Node* const receiver = NodeProperties::GetValueInput(node, 0);
940 607453 : Node* const value = jsgraph()->Dead();
941 :
942 : // Check if we have a constant receiver.
943 : HeapObjectMatcher m(receiver);
944 607453 : if (m.HasValue()) {
945 242416 : if (m.Value()->IsJSFunction() &&
946 : p.name().is_identical_to(factory()->prototype_string())) {
947 : // Optimize "prototype" property of functions.
948 : Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
949 42684 : if (function->IsConstructor()) {
950 : // We need to add a code dependency on the initial map of the
951 : // {function} in order to be notified about changes to the
952 : // "prototype" of {function}.
953 42679 : JSFunction::EnsureHasInitialMap(function);
954 : Handle<Map> initial_map(function->initial_map(), isolate());
955 : dependencies()->AssumeInitialMapCantChange(initial_map);
956 42679 : Handle<Object> prototype(function->prototype(), isolate());
957 42679 : Node* value = jsgraph()->Constant(prototype);
958 42854 : ReplaceWithValue(node, value);
959 : return Replace(value);
960 : }
961 147076 : } else if (m.Value()->IsString() &&
962 : p.name().is_identical_to(factory()->length_string())) {
963 : // Constant-fold "length" property on constant strings.
964 : Handle<String> string = Handle<String>::cast(m.Value());
965 175 : Node* value = jsgraph()->Constant(string->length());
966 : ReplaceWithValue(node, value);
967 : return Replace(value);
968 : }
969 : }
970 :
971 : // Extract receiver maps from the load IC using the LoadICNexus.
972 564600 : if (!p.feedback().IsValid()) return NoChange();
973 : LoadICNexus nexus(p.feedback().vector(), p.feedback().slot());
974 :
975 : // Try to lower the named access based on the {receiver_maps}.
976 : return ReduceNamedAccessFromNexus(node, value, nexus, p.name(),
977 564600 : AccessMode::kLoad);
978 : }
979 :
980 :
981 266529 : Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) {
982 : DCHECK_EQ(IrOpcode::kJSStoreNamed, node->opcode());
983 266529 : NamedAccess const& p = NamedAccessOf(node->op());
984 266529 : Node* const value = NodeProperties::GetValueInput(node, 1);
985 :
986 : // Extract receiver maps from the store IC using the StoreICNexus.
987 266529 : if (!p.feedback().IsValid()) return NoChange();
988 : StoreICNexus nexus(p.feedback().vector(), p.feedback().slot());
989 :
990 : // Try to lower the named access based on the {receiver_maps}.
991 : return ReduceNamedAccessFromNexus(node, value, nexus, p.name(),
992 266529 : AccessMode::kStore);
993 : }
994 :
995 45461 : Reduction JSNativeContextSpecialization::ReduceJSStoreNamedOwn(Node* node) {
996 : DCHECK_EQ(IrOpcode::kJSStoreNamedOwn, node->opcode());
997 45461 : StoreNamedOwnParameters const& p = StoreNamedOwnParametersOf(node->op());
998 45461 : Node* const value = NodeProperties::GetValueInput(node, 1);
999 :
1000 : // Extract receiver maps from the IC using the StoreOwnICNexus.
1001 45461 : if (!p.feedback().IsValid()) return NoChange();
1002 : StoreOwnICNexus nexus(p.feedback().vector(), p.feedback().slot());
1003 :
1004 : // Try to lower the creation of a named property based on the {receiver_maps}.
1005 : return ReduceNamedAccessFromNexus(node, value, nexus, p.name(),
1006 45461 : AccessMode::kStoreInLiteral);
1007 : }
1008 :
1009 26327 : Reduction JSNativeContextSpecialization::ReduceElementAccess(
1010 : Node* node, Node* index, Node* value, MapHandles const& receiver_maps,
1011 95241 : AccessMode access_mode, KeyedAccessStoreMode store_mode) {
1012 : DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
1013 : node->opcode() == IrOpcode::kJSStoreProperty);
1014 26327 : Node* receiver = NodeProperties::GetValueInput(node, 0);
1015 26327 : Node* effect = NodeProperties::GetEffectInput(node);
1016 26327 : Node* control = NodeProperties::GetControlInput(node);
1017 26327 : Node* frame_state = NodeProperties::FindFrameStateBefore(node);
1018 :
1019 : // Check for keyed access to strings.
1020 26327 : if (HasOnlyStringMaps(receiver_maps)) {
1021 : // Strings are immutable in JavaScript.
1022 70 : if (access_mode == AccessMode::kStore) return NoChange();
1023 :
1024 : // Ensure that the {receiver} is actually a String.
1025 : receiver = effect = graph()->NewNode(simplified()->CheckString(), receiver,
1026 210 : effect, control);
1027 :
1028 : // Determine the {receiver} length.
1029 : Node* length = effect = graph()->NewNode(
1030 : simplified()->LoadField(AccessBuilder::ForStringLength()), receiver,
1031 210 : effect, control);
1032 :
1033 : // Ensure that {index} is less than {receiver} length.
1034 : index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
1035 210 : length, effect, control);
1036 :
1037 : // Return the character from the {receiver} as single character string.
1038 : value = graph()->NewNode(simplified()->StringCharAt(), receiver, index,
1039 140 : control);
1040 : } else {
1041 : // Retrieve the native context from the given {node}.
1042 : // Compute element access infos for the receiver maps.
1043 : AccessInfoFactory access_info_factory(dependencies(), native_context(),
1044 52514 : graph()->zone());
1045 : ZoneVector<ElementAccessInfo> access_infos(zone());
1046 26257 : if (!access_info_factory.ComputeElementAccessInfos(
1047 26257 : receiver_maps, access_mode, &access_infos)) {
1048 : return NoChange();
1049 : }
1050 :
1051 : // Nothing to do if we have no non-deprecated maps.
1052 25775 : if (access_infos.empty()) {
1053 : return ReduceSoftDeoptimize(
1054 : node,
1055 0 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess);
1056 : }
1057 :
1058 : // For holey stores or growing stores, we need to check that the prototype
1059 : // chain contains no setters for elements, and we need to guard those checks
1060 : // via code dependencies on the relevant prototype maps.
1061 25775 : if (access_mode == AccessMode::kStore) {
1062 : // TODO(turbofan): We could have a fast path here, that checks for the
1063 : // common case of Array or Object prototype only and therefore avoids
1064 : // the zone allocation of this vector.
1065 : ZoneVector<Handle<Map>> prototype_maps(zone());
1066 31319 : for (ElementAccessInfo const& access_info : access_infos) {
1067 33621 : for (Handle<Map> receiver_map : access_info.receiver_maps()) {
1068 : // If the {receiver_map} has a prototype and it's elements backing
1069 : // store is either holey, or we have a potentially growing store,
1070 : // then we need to check that all prototypes have stable maps with
1071 : // fast elements (and we need to guard against changes to that below).
1072 19379 : if (IsHoleyOrDictionaryElementsKind(receiver_map->elements_kind()) ||
1073 : IsGrowStoreMode(store_mode)) {
1074 : // Make sure all prototypes are stable and have fast elements.
1075 3556 : for (Handle<Map> map = receiver_map;;) {
1076 : Handle<Object> map_prototype(map->prototype(), isolate());
1077 9062 : if (map_prototype->IsNull(isolate())) break;
1078 5521 : if (!map_prototype->IsJSObject()) return NoChange();
1079 : map = handle(Handle<JSObject>::cast(map_prototype)->map(),
1080 5521 : isolate());
1081 5521 : if (!map->is_stable()) return NoChange();
1082 5506 : if (!IsFastElementsKind(map->elements_kind())) return NoChange();
1083 5506 : prototype_maps.push_back(map);
1084 5506 : }
1085 : }
1086 : }
1087 : }
1088 :
1089 : // Install dependencies on the relevant prototype maps.
1090 25590 : for (Handle<Map> prototype_map : prototype_maps) {
1091 5498 : dependencies()->AssumeMapStable(prototype_map);
1092 : }
1093 : }
1094 :
1095 : // Ensure that {receiver} is a heap object.
1096 : PropertyAccessBuilder access_builder(jsgraph(), dependencies());
1097 25760 : receiver = access_builder.BuildCheckHeapObject(receiver, &effect, control);
1098 :
1099 : // Check for the monomorphic case.
1100 51520 : if (access_infos.size() == 1) {
1101 24352 : ElementAccessInfo access_info = access_infos.front();
1102 :
1103 : // Perform possible elements kind transitions.
1104 49307 : for (auto transition : access_info.transitions()) {
1105 : Handle<Map> const transition_source = transition.first;
1106 : Handle<Map> const transition_target = transition.second;
1107 : effect = graph()->NewNode(
1108 : simplified()->TransitionElementsKind(ElementsTransition(
1109 : IsSimpleMapChangeTransition(transition_source->elements_kind(),
1110 : transition_target->elements_kind())
1111 : ? ElementsTransition::kFastTransition
1112 : : ElementsTransition::kSlowTransition,
1113 : transition_source, transition_target)),
1114 2412 : receiver, effect, control);
1115 : }
1116 :
1117 : // TODO(turbofan): The effect/control linearization will not find a
1118 : // FrameState after the StoreField or Call that is generated for the
1119 : // elements kind transition above. This is because those operators
1120 : // don't have the kNoWrite flag on it, even though they are not
1121 : // observable by JavaScript.
1122 : effect = graph()->NewNode(common()->Checkpoint(), frame_state, effect,
1123 73056 : control);
1124 :
1125 : // Perform map check on the {receiver}.
1126 : access_builder.BuildCheckMaps(receiver, &effect, control,
1127 24352 : access_info.receiver_maps());
1128 :
1129 : // Access the actual element.
1130 : ValueEffectControl continuation =
1131 : BuildElementAccess(receiver, index, value, effect, control,
1132 24352 : access_info, access_mode, store_mode);
1133 24352 : value = continuation.value();
1134 24352 : effect = continuation.effect();
1135 24352 : control = continuation.control();
1136 : } else {
1137 : // The final states for every polymorphic branch. We join them with
1138 : // Merge+Phi+EffectPhi at the bottom.
1139 : ZoneVector<Node*> values(zone());
1140 : ZoneVector<Node*> effects(zone());
1141 : ZoneVector<Node*> controls(zone());
1142 :
1143 : // Generate code for the various different element access patterns.
1144 1408 : Node* fallthrough_control = control;
1145 8778 : for (size_t j = 0; j < access_infos.size(); ++j) {
1146 : ElementAccessInfo const& access_info = access_infos[j];
1147 : Node* this_receiver = receiver;
1148 : Node* this_value = value;
1149 : Node* this_index = index;
1150 2981 : Node* this_effect = effect;
1151 : Node* this_control = fallthrough_control;
1152 :
1153 : // Perform possible elements kind transitions.
1154 5978 : for (auto transition : access_info.transitions()) {
1155 : Handle<Map> const transition_source = transition.first;
1156 : Handle<Map> const transition_target = transition.second;
1157 : this_effect = graph()->NewNode(
1158 : simplified()->TransitionElementsKind(
1159 : ElementsTransition(IsSimpleMapChangeTransition(
1160 : transition_source->elements_kind(),
1161 : transition_target->elements_kind())
1162 : ? ElementsTransition::kFastTransition
1163 : : ElementsTransition::kSlowTransition,
1164 : transition_source, transition_target)),
1165 64 : receiver, this_effect, this_control);
1166 : }
1167 :
1168 : // Perform map check(s) on {receiver}.
1169 2981 : MapHandles const& receiver_maps = access_info.receiver_maps();
1170 5962 : if (j == access_infos.size() - 1) {
1171 : // Last map check on the fallthrough control path, do a
1172 : // conditional eager deoptimization exit here.
1173 : access_builder.BuildCheckMaps(receiver, &this_effect, this_control,
1174 1408 : receiver_maps);
1175 : fallthrough_control = nullptr;
1176 : } else {
1177 : // Explicitly branch on the {receiver_maps}.
1178 : ZoneHandleSet<Map> maps;
1179 4719 : for (Handle<Map> map : receiver_maps) {
1180 1573 : maps.insert(map, graph()->zone());
1181 : }
1182 : Node* check = this_effect =
1183 : graph()->NewNode(simplified()->CompareMaps(maps), receiver,
1184 4719 : this_effect, fallthrough_control);
1185 : Node* branch =
1186 1573 : graph()->NewNode(common()->Branch(), check, fallthrough_control);
1187 1573 : fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
1188 1573 : this_control = graph()->NewNode(common()->IfTrue(), branch);
1189 :
1190 : // Introduce a MapGuard to learn from this on the effect chain.
1191 : this_effect = graph()->NewNode(simplified()->MapGuard(maps), receiver,
1192 4719 : this_effect, this_control);
1193 : }
1194 :
1195 : // Access the actual element.
1196 : ValueEffectControl continuation = BuildElementAccess(
1197 : this_receiver, this_index, this_value, this_effect, this_control,
1198 2981 : access_info, access_mode, store_mode);
1199 5962 : values.push_back(continuation.value());
1200 5962 : effects.push_back(continuation.effect());
1201 5962 : controls.push_back(continuation.control());
1202 : }
1203 :
1204 : DCHECK_NULL(fallthrough_control);
1205 :
1206 : // Generate the final merge point for all (polymorphic) branches.
1207 2816 : int const control_count = static_cast<int>(controls.size());
1208 1408 : if (control_count == 0) {
1209 0 : value = effect = control = jsgraph()->Dead();
1210 1408 : } else if (control_count == 1) {
1211 0 : value = values.front();
1212 0 : effect = effects.front();
1213 0 : control = controls.front();
1214 : } else {
1215 : control = graph()->NewNode(common()->Merge(control_count),
1216 2816 : control_count, &controls.front());
1217 1408 : values.push_back(control);
1218 : value = graph()->NewNode(
1219 : common()->Phi(MachineRepresentation::kTagged, control_count),
1220 4224 : control_count + 1, &values.front());
1221 1408 : effects.push_back(control);
1222 : effect = graph()->NewNode(common()->EffectPhi(control_count),
1223 2816 : control_count + 1, &effects.front());
1224 : }
1225 : }
1226 : }
1227 :
1228 25830 : ReplaceWithValue(node, value, effect, control);
1229 : return Replace(value);
1230 : }
1231 :
1232 : template <typename KeyedICNexus>
1233 126272 : Reduction JSNativeContextSpecialization::ReduceKeyedAccess(
1234 : Node* node, Node* index, Node* value, KeyedICNexus const& nexus,
1235 387 : AccessMode access_mode, KeyedAccessStoreMode store_mode) {
1236 : DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
1237 : node->opcode() == IrOpcode::kJSStoreProperty);
1238 126272 : Node* receiver = NodeProperties::GetValueInput(node, 0);
1239 126272 : Node* effect = NodeProperties::GetEffectInput(node);
1240 126272 : Node* control = NodeProperties::GetControlInput(node);
1241 :
1242 : // Optimize the case where we load from a constant {receiver}.
1243 126272 : if (access_mode == AccessMode::kLoad) {
1244 : HeapObjectMatcher mreceiver(receiver);
1245 76136 : if (mreceiver.HasValue() && !mreceiver.Value()->IsTheHole(isolate()) &&
1246 : !mreceiver.Value()->IsNullOrUndefined(isolate())) {
1247 : // Check whether we're accessing a known element on the {receiver}
1248 : // that is non-configurable, non-writable (i.e. the {receiver} was
1249 : // frozen using Object.freeze).
1250 : NumberMatcher mindex(index);
1251 10131 : if (mindex.IsInteger() && mindex.IsInRange(0.0, kMaxUInt32 - 1.0)) {
1252 : LookupIterator it(isolate(), mreceiver.Value(),
1253 : static_cast<uint32_t>(mindex.Value()),
1254 4534 : LookupIterator::OWN);
1255 2267 : if (it.state() == LookupIterator::DATA) {
1256 2083 : if (it.IsReadOnly() && !it.IsConfigurable()) {
1257 : // We can safely constant-fold the {index} access to {receiver},
1258 : // since the element is non-configurable, non-writable and thus
1259 : // cannot change anymore.
1260 70 : value = jsgraph()->Constant(it.GetDataValue());
1261 275 : ReplaceWithValue(node, value, effect, control);
1262 147 : return Replace(value);
1263 : }
1264 :
1265 : // Check if the {receiver} is a known constant with a copy-on-write
1266 : // backing store, and whether {index} is within the appropriate
1267 : // bounds. In that case we can constant-fold the access and only
1268 : // check that the {elements} didn't change. This is sufficient as
1269 : // the backing store of a copy-on-write JSArray is defensively copied
1270 : // whenever the length or the elements (might) change.
1271 : //
1272 : // What's interesting here is that we don't need to map check the
1273 : // {receiver}, since JSArray's will always have their elements in
1274 : // the backing store.
1275 2013 : if (mreceiver.Value()->IsJSArray()) {
1276 : Handle<JSArray> array = Handle<JSArray>::cast(mreceiver.Value());
1277 1208 : if (array->elements()->IsCowArray()) {
1278 : Node* elements = effect = graph()->NewNode(
1279 : simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
1280 336 : receiver, effect, control);
1281 : Handle<FixedArray> array_elements(
1282 : FixedArray::cast(array->elements()), isolate());
1283 : Node* check =
1284 : graph()->NewNode(simplified()->ReferenceEqual(), elements,
1285 336 : jsgraph()->HeapConstant(array_elements));
1286 : effect = graph()->NewNode(
1287 : simplified()->CheckIf(
1288 : DeoptimizeReason::kCowArrayElementsChanged),
1289 112 : check, effect, control);
1290 224 : value = jsgraph()->Constant(it.GetDataValue());
1291 : ReplaceWithValue(node, value, effect, control);
1292 : return Replace(value);
1293 : }
1294 : }
1295 : }
1296 : }
1297 :
1298 : // For constant Strings we can eagerly strength-reduce the keyed
1299 : // accesses using the known length, which doesn't change.
1300 7705 : if (mreceiver.Value()->IsString()) {
1301 : Handle<String> string = Handle<String>::cast(mreceiver.Value());
1302 :
1303 : // We can only assume that the {index} is a valid array index if the IC
1304 : // is in element access mode and not MEGAMORPHIC, otherwise there's no
1305 : // guard for the bounds check below.
1306 312 : if (nexus.ic_state() != MEGAMORPHIC && nexus.GetKeyType() == ELEMENT) {
1307 : // Ensure that {index} is less than {receiver} length.
1308 128 : Node* length = jsgraph()->Constant(string->length());
1309 : index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
1310 128 : length, effect, control);
1311 :
1312 : // Return the character from the {receiver} as single character
1313 : // string.
1314 : value = graph()->NewNode(simplified()->StringCharAt(), receiver,
1315 128 : index, control);
1316 : ReplaceWithValue(node, value, effect, control);
1317 : return Replace(value);
1318 : }
1319 : }
1320 : }
1321 : }
1322 :
1323 : // Check if the {nexus} reports type feedback for the IC.
1324 251994 : if (nexus.IsUninitialized()) {
1325 90568 : if (flags() & kBailoutOnUninitialized) {
1326 : return ReduceSoftDeoptimize(
1327 : node,
1328 0 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess);
1329 : }
1330 : return NoChange();
1331 : }
1332 :
1333 : // Extract receiver maps from the {nexus}.
1334 : MapHandles receiver_maps;
1335 35429 : if (!ExtractReceiverMaps(receiver, effect, nexus, &receiver_maps)) {
1336 : return NoChange();
1337 28321 : } else if (receiver_maps.empty()) {
1338 0 : if (flags() & kBailoutOnUninitialized) {
1339 : return ReduceSoftDeoptimize(
1340 : node,
1341 0 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess);
1342 : }
1343 : return NoChange();
1344 : }
1345 :
1346 : // Optimize access for constant {index}.
1347 : HeapObjectMatcher mindex(index);
1348 28898 : if (mindex.HasValue() && mindex.Value()->IsPrimitive()) {
1349 : // Keyed access requires a ToPropertyKey on the {index} first before
1350 : // looking up the property on the object (see ES6 section 12.3.2.1).
1351 : // We can only do this for non-observable ToPropertyKey invocations,
1352 : // so we limit the constant indices to primitives at this point.
1353 : Handle<Name> name;
1354 1122 : if (Object::ToName(isolate(), mindex.Value()).ToHandle(&name)) {
1355 : uint32_t array_index;
1356 561 : if (name->AsArrayIndex(&array_index)) {
1357 : // Use the constant array index.
1358 0 : index = jsgraph()->Constant(static_cast<double>(array_index));
1359 : } else {
1360 561 : name = factory()->InternalizeName(name);
1361 561 : return ReduceNamedAccess(node, value, receiver_maps, name, access_mode);
1362 : }
1363 : }
1364 : }
1365 :
1366 : // Check if we have feedback for a named access.
1367 27760 : if (Name* name = nexus.FindFirstName()) {
1368 : return ReduceNamedAccess(node, value, receiver_maps,
1369 115 : handle(name, isolate()), access_mode, index);
1370 27645 : } else if (nexus.GetKeyType() != ELEMENT) {
1371 : // The KeyedLoad/StoreIC has seen non-element accesses, so we cannot assume
1372 : // that the {index} is a valid array index, thus we just let the IC continue
1373 : // to deal with this load/store.
1374 : return NoChange();
1375 27625 : } else if (nexus.ic_state() == MEGAMORPHIC) {
1376 : // The KeyedLoad/StoreIC uses the MEGAMORPHIC state to guard the assumption
1377 : // that a numeric {index} is within the valid bounds for {receiver}, i.e.
1378 : // it transitions to MEGAMORPHIC once it sees an out-of-bounds access. Thus
1379 : // we cannot continue here if the IC state is MEGAMORPHIC.
1380 : return NoChange();
1381 : }
1382 :
1383 : // Try to lower the element access based on the {receiver_maps}.
1384 : return ReduceElementAccess(node, index, value, receiver_maps, access_mode,
1385 26327 : store_mode);
1386 : }
1387 :
1388 38 : Reduction JSNativeContextSpecialization::ReduceSoftDeoptimize(
1389 : Node* node, DeoptimizeReason reason) {
1390 38 : Node* effect = NodeProperties::GetEffectInput(node);
1391 38 : Node* control = NodeProperties::GetControlInput(node);
1392 38 : Node* frame_state = NodeProperties::FindFrameStateBefore(node);
1393 : Node* deoptimize =
1394 : graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kSoft, reason),
1395 38 : frame_state, effect, control);
1396 : // TODO(bmeurer): This should be on the AdvancedReducer somehow.
1397 38 : NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
1398 38 : Revisit(graph()->end());
1399 38 : node->TrimInputCount(0);
1400 38 : NodeProperties::ChangeOp(node, common()->Dead());
1401 38 : return Changed(node);
1402 : }
1403 :
1404 124019 : Reduction JSNativeContextSpecialization::ReduceJSLoadProperty(Node* node) {
1405 : DCHECK_EQ(IrOpcode::kJSLoadProperty, node->opcode());
1406 61466 : PropertyAccess const& p = PropertyAccessOf(node->op());
1407 61466 : Node* receiver = NodeProperties::GetValueInput(node, 0);
1408 122932 : Node* name = NodeProperties::GetValueInput(node, 1);
1409 61466 : Node* value = jsgraph()->Dead();
1410 61466 : Node* effect = NodeProperties::GetEffectInput(node);
1411 61466 : Node* control = NodeProperties::GetControlInput(node);
1412 :
1413 : // We can optimize a property load if it's being used inside a for..in,
1414 : // so for code like this:
1415 : //
1416 : // for (name in receiver) {
1417 : // value = receiver[name];
1418 : // ...
1419 : // }
1420 : //
1421 : // If the for..in is in fast-mode, we know that the {receiver} has {name}
1422 : // as own property, otherwise the enumeration wouldn't include it. The graph
1423 : // constructed by the BytecodeGraphBuilder in this case looks like this:
1424 :
1425 : // receiver
1426 : // ^ ^
1427 : // | |
1428 : // | +-+
1429 : // | |
1430 : // | JSToObject
1431 : // | ^
1432 : // | |
1433 : // | |
1434 : // | JSForInNext
1435 : // | ^
1436 : // | |
1437 : // +----+ |
1438 : // | |
1439 : // | |
1440 : // JSLoadProperty
1441 :
1442 : // If the for..in has only seen maps with enum cache consisting of keys
1443 : // and indices so far, we can turn the {JSLoadProperty} into a map check
1444 : // on the {receiver} and then just load the field value dynamically via
1445 : // the {LoadFieldByIndex} operator. The map check is only necessary when
1446 : // TurboFan cannot prove that there is no observable side effect between
1447 : // the {JSForInNext} and the {JSLoadProperty} node.
1448 : //
1449 : // Also note that it's safe to look through the {JSToObject}, since the
1450 : // [[Get]] operation does an implicit ToObject anyway, and these operations
1451 : // are not observable.
1452 61466 : if (name->opcode() == IrOpcode::kJSForInNext) {
1453 1574 : ForInMode const mode = ForInModeOf(name->op());
1454 1574 : if (mode == ForInMode::kUseEnumCacheKeysAndIndices) {
1455 2518 : Node* object = NodeProperties::GetValueInput(name, 0);
1456 1259 : Node* enumerator = NodeProperties::GetValueInput(name, 2);
1457 1259 : Node* index = NodeProperties::GetValueInput(name, 3);
1458 1259 : if (object->opcode() == IrOpcode::kJSToObject) {
1459 1203 : object = NodeProperties::GetValueInput(object, 0);
1460 : }
1461 1259 : if (object == receiver) {
1462 : // No need to repeat the map check if we can prove that there's no
1463 : // observable side effect between {effect} and {name].
1464 1087 : if (!NodeProperties::NoObservableSideEffectBetween(effect, name)) {
1465 : // Check that the {receiver} map is still valid.
1466 : Node* receiver_map = effect =
1467 : graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
1468 2559 : receiver, effect, control);
1469 : Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
1470 853 : receiver_map, enumerator);
1471 : effect = graph()->NewNode(
1472 : simplified()->CheckIf(DeoptimizeReason::kNoReason), check, effect,
1473 853 : control);
1474 : }
1475 :
1476 : // Load the enum cache indices from the {cache_type}.
1477 : Node* descriptor_array = effect = graph()->NewNode(
1478 : simplified()->LoadField(AccessBuilder::ForMapDescriptors()),
1479 3261 : enumerator, effect, control);
1480 : Node* enum_cache = effect =
1481 : graph()->NewNode(simplified()->LoadField(
1482 : AccessBuilder::ForDescriptorArrayEnumCache()),
1483 3261 : descriptor_array, effect, control);
1484 : Node* enum_indices = effect = graph()->NewNode(
1485 : simplified()->LoadField(AccessBuilder::ForEnumCacheIndices()),
1486 3261 : enum_cache, effect, control);
1487 :
1488 : // Ensure that the {enum_indices} are valid.
1489 : Node* check = graph()->NewNode(
1490 : simplified()->BooleanNot(),
1491 : graph()->NewNode(simplified()->ReferenceEqual(), enum_indices,
1492 3261 : jsgraph()->EmptyFixedArrayConstant()));
1493 : effect =
1494 : graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kNoReason),
1495 1087 : check, effect, control);
1496 :
1497 : // Determine the index from the {enum_indices}.
1498 : index = effect = graph()->NewNode(
1499 : simplified()->LoadElement(
1500 : AccessBuilder::ForFixedArrayElement(PACKED_SMI_ELEMENTS)),
1501 3261 : enum_indices, index, effect, control);
1502 :
1503 : // Load the actual field value.
1504 : Node* value = effect = graph()->NewNode(
1505 1087 : simplified()->LoadFieldByIndex(), receiver, index, effect, control);
1506 1087 : ReplaceWithValue(node, value, effect, control);
1507 : return Replace(value);
1508 : }
1509 : }
1510 : }
1511 :
1512 : // Extract receiver maps from the keyed load IC using the KeyedLoadICNexus.
1513 60379 : if (!p.feedback().IsValid()) return NoChange();
1514 : KeyedLoadICNexus nexus(p.feedback().vector(), p.feedback().slot());
1515 :
1516 : // Try to lower the keyed access based on the {nexus}.
1517 : return ReduceKeyedAccess(node, name, value, nexus, AccessMode::kLoad,
1518 60379 : STANDARD_STORE);
1519 : }
1520 :
1521 65893 : Reduction JSNativeContextSpecialization::ReduceJSStoreProperty(Node* node) {
1522 : DCHECK_EQ(IrOpcode::kJSStoreProperty, node->opcode());
1523 65893 : PropertyAccess const& p = PropertyAccessOf(node->op());
1524 65893 : Node* const index = NodeProperties::GetValueInput(node, 1);
1525 65893 : Node* const value = NodeProperties::GetValueInput(node, 2);
1526 :
1527 : // Extract receiver maps from the keyed store IC using the KeyedStoreICNexus.
1528 65893 : if (!p.feedback().IsValid()) return NoChange();
1529 : KeyedStoreICNexus nexus(p.feedback().vector(), p.feedback().slot());
1530 :
1531 : // Extract the keyed access store mode from the keyed store IC.
1532 65893 : KeyedAccessStoreMode store_mode = nexus.GetKeyedAccessStoreMode();
1533 :
1534 : // Try to lower the keyed access based on the {nexus}.
1535 : return ReduceKeyedAccess(node, index, value, nexus, AccessMode::kStore,
1536 65893 : store_mode);
1537 : }
1538 :
1539 2629 : Node* JSNativeContextSpecialization::InlinePropertyGetterCall(
1540 : Node* receiver, Node* context, Node* frame_state, Node** effect,
1541 : Node** control, ZoneVector<Node*>* if_exceptions,
1542 10447 : PropertyAccessInfo const& access_info) {
1543 2629 : Node* target = jsgraph()->Constant(access_info.constant());
1544 2629 : FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
1545 : Handle<SharedFunctionInfo> shared_info =
1546 2629 : frame_info.shared_info().ToHandleChecked();
1547 : // We need a FrameState for the getter stub to restore the correct
1548 : // context before returning to fullcodegen.
1549 : FrameStateFunctionInfo const* frame_info0 =
1550 : common()->CreateFrameStateFunctionInfo(FrameStateType::kGetterStub, 1, 0,
1551 2629 : shared_info);
1552 : Node* frame_state0 = graph()->NewNode(
1553 : common()->FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(),
1554 : frame_info0),
1555 : graph()->NewNode(common()->StateValues(1, SparseInputMask::Dense()),
1556 : receiver),
1557 : jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(), context,
1558 10516 : target, frame_state);
1559 :
1560 : // Introduce the call to the getter function.
1561 : Node* value;
1562 2629 : if (access_info.constant()->IsJSFunction()) {
1563 : value = *effect = *control = graph()->NewNode(
1564 : jsgraph()->javascript()->Call(2, CallFrequency(), VectorSlotPair(),
1565 : ConvertReceiverMode::kNotNullOrUndefined),
1566 7680 : target, receiver, context, frame_state0, *effect, *control);
1567 : } else {
1568 : DCHECK(access_info.constant()->IsFunctionTemplateInfo());
1569 : Handle<FunctionTemplateInfo> function_template_info(
1570 69 : Handle<FunctionTemplateInfo>::cast(access_info.constant()));
1571 : DCHECK(!function_template_info->call_code()->IsUndefined(isolate()));
1572 : Node* holder =
1573 : access_info.holder().is_null()
1574 : ? receiver
1575 69 : : jsgraph()->Constant(access_info.holder().ToHandleChecked());
1576 : value =
1577 : InlineApiCall(receiver, holder, context, target, frame_state0, nullptr,
1578 69 : effect, control, shared_info, function_template_info);
1579 : }
1580 : // Remember to rewire the IfException edge if this is inside a try-block.
1581 2629 : if (if_exceptions != nullptr) {
1582 : // Create the appropriate IfException/IfSuccess projections.
1583 : Node* const if_exception =
1584 162 : graph()->NewNode(common()->IfException(), *control, *effect);
1585 108 : Node* const if_success = graph()->NewNode(common()->IfSuccess(), *control);
1586 54 : if_exceptions->push_back(if_exception);
1587 54 : *control = if_success;
1588 : }
1589 2629 : return value;
1590 : }
1591 :
1592 879 : Node* JSNativeContextSpecialization::InlinePropertySetterCall(
1593 : Node* receiver, Node* value, Node* context, Node* frame_state,
1594 : Node** effect, Node** control, ZoneVector<Node*>* if_exceptions,
1595 3450 : PropertyAccessInfo const& access_info) {
1596 879 : Node* target = jsgraph()->Constant(access_info.constant());
1597 879 : FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
1598 : Handle<SharedFunctionInfo> shared_info =
1599 879 : frame_info.shared_info().ToHandleChecked();
1600 : // We need a FrameState for the setter stub to restore the correct
1601 : // context and return the appropriate value to fullcodegen.
1602 : FrameStateFunctionInfo const* frame_info0 =
1603 : common()->CreateFrameStateFunctionInfo(FrameStateType::kSetterStub, 2, 0,
1604 879 : shared_info);
1605 : Node* frame_state0 = graph()->NewNode(
1606 : common()->FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(),
1607 : frame_info0),
1608 : graph()->NewNode(common()->StateValues(2, SparseInputMask::Dense()),
1609 : receiver, value),
1610 : jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(), context,
1611 3516 : target, frame_state);
1612 :
1613 : // Introduce the call to the setter function.
1614 879 : if (access_info.constant()->IsJSFunction()) {
1615 : *effect = *control = graph()->NewNode(
1616 : jsgraph()->javascript()->Call(3, CallFrequency(), VectorSlotPair(),
1617 : ConvertReceiverMode::kNotNullOrUndefined),
1618 2439 : target, receiver, value, context, frame_state0, *effect, *control);
1619 : } else {
1620 : DCHECK(access_info.constant()->IsFunctionTemplateInfo());
1621 : Handle<FunctionTemplateInfo> function_template_info(
1622 66 : Handle<FunctionTemplateInfo>::cast(access_info.constant()));
1623 : DCHECK(!function_template_info->call_code()->IsUndefined(isolate()));
1624 : Node* holder =
1625 : access_info.holder().is_null()
1626 : ? receiver
1627 66 : : jsgraph()->Constant(access_info.holder().ToHandleChecked());
1628 : value =
1629 : InlineApiCall(receiver, holder, context, target, frame_state0, value,
1630 66 : effect, control, shared_info, function_template_info);
1631 : }
1632 : // Remember to rewire the IfException edge if this is inside a try-block.
1633 879 : if (if_exceptions != nullptr) {
1634 : // Create the appropriate IfException/IfSuccess projections.
1635 : Node* const if_exception =
1636 93 : graph()->NewNode(common()->IfException(), *control, *effect);
1637 62 : Node* const if_success = graph()->NewNode(common()->IfSuccess(), *control);
1638 31 : if_exceptions->push_back(if_exception);
1639 31 : *control = if_success;
1640 : }
1641 879 : return value;
1642 : }
1643 :
1644 135 : Node* JSNativeContextSpecialization::InlineApiCall(
1645 : Node* receiver, Node* holder, Node* context, Node* target,
1646 : Node* frame_state, Node* value, Node** effect, Node** control,
1647 : Handle<SharedFunctionInfo> shared_info,
1648 270 : Handle<FunctionTemplateInfo> function_template_info) {
1649 : Handle<CallHandlerInfo> call_handler_info = handle(
1650 : CallHandlerInfo::cast(function_template_info->call_code()), isolate());
1651 : Handle<Object> call_data_object(call_handler_info->data(), isolate());
1652 :
1653 : // Only setters have a value.
1654 135 : int const argc = value == nullptr ? 0 : 1;
1655 : // The stub always expects the receiver as the first param on the stack.
1656 : CallApiCallbackStub stub(
1657 : isolate(), argc,
1658 : true /* FunctionTemplateInfo doesn't have an associated context. */);
1659 : CallInterfaceDescriptor call_interface_descriptor =
1660 : stub.GetCallInterfaceDescriptor();
1661 : CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor(
1662 : isolate(), graph()->zone(), call_interface_descriptor,
1663 135 : call_interface_descriptor.GetStackParameterCount() + argc +
1664 : 1 /* implicit receiver */ + 1 /* accessor holder */,
1665 : CallDescriptor::kNeedsFrameState, Operator::kNoProperties,
1666 405 : MachineType::AnyTagged(), 1);
1667 :
1668 135 : Node* data = jsgraph()->Constant(call_data_object);
1669 : ApiFunction function(v8::ToCData<Address>(call_handler_info->callback()));
1670 : Node* function_reference =
1671 : graph()->NewNode(common()->ExternalConstant(ExternalReference(
1672 405 : &function, ExternalReference::DIRECT_API_CALL, isolate())));
1673 270 : Node* code = jsgraph()->HeapConstant(stub.GetCode());
1674 :
1675 : // Add CallApiCallbackStub's register argument as well.
1676 : Node* inputs[12] = {code, target, data, holder, function_reference,
1677 135 : holder, receiver};
1678 135 : int index = 7 + argc;
1679 135 : inputs[index++] = context;
1680 135 : inputs[index++] = frame_state;
1681 135 : inputs[index++] = *effect;
1682 135 : inputs[index++] = *control;
1683 : // This needs to stay here because of the edge case described in
1684 : // http://crbug.com/675648.
1685 135 : if (value != nullptr) {
1686 66 : inputs[7] = value;
1687 : }
1688 :
1689 : return *effect = *control =
1690 270 : graph()->NewNode(common()->Call(call_descriptor), index, inputs);
1691 : }
1692 :
1693 : JSNativeContextSpecialization::ValueEffectControl
1694 84954 : JSNativeContextSpecialization::BuildPropertyLoad(
1695 : Node* receiver, Node* context, Node* frame_state, Node* effect,
1696 : Node* control, Handle<Name> name, ZoneVector<Node*>* if_exceptions,
1697 146256 : PropertyAccessInfo const& access_info) {
1698 : // Determine actual holder and perform prototype chain checks.
1699 : Handle<JSObject> holder;
1700 : PropertyAccessBuilder access_builder(jsgraph(), dependencies());
1701 84954 : if (access_info.holder().ToHandle(&holder)) {
1702 : access_builder.AssumePrototypesStable(native_context(),
1703 18045 : access_info.receiver_maps(), holder);
1704 : }
1705 :
1706 : // Generate the actual property access.
1707 : Node* value;
1708 84954 : if (access_info.IsNotFound()) {
1709 2387 : value = jsgraph()->UndefinedConstant();
1710 82567 : } else if (access_info.IsDataConstant()) {
1711 : DCHECK(!FLAG_track_constant_fields);
1712 58915 : value = jsgraph()->Constant(access_info.constant());
1713 23652 : } else if (access_info.IsAccessorConstant()) {
1714 : value = InlinePropertyGetterCall(receiver, context, frame_state, &effect,
1715 2629 : &control, if_exceptions, access_info);
1716 21023 : } else if (access_info.IsModuleExport()) {
1717 0 : Node* cell = jsgraph()->Constant(access_info.export_cell());
1718 : value = effect =
1719 : graph()->NewNode(simplified()->LoadField(AccessBuilder::ForCellValue()),
1720 0 : cell, effect, control);
1721 : } else {
1722 : DCHECK(access_info.IsDataField() || access_info.IsDataConstantField());
1723 : value = access_builder.BuildLoadDataField(name, access_info, receiver,
1724 21023 : &effect, &control);
1725 : }
1726 :
1727 169908 : return ValueEffectControl(value, effect, control);
1728 : }
1729 :
1730 : JSNativeContextSpecialization::ValueEffectControl
1731 112777 : JSNativeContextSpecialization::BuildPropertyAccess(
1732 : Node* receiver, Node* value, Node* context, Node* frame_state, Node* effect,
1733 : Node* control, Handle<Name> name, ZoneVector<Node*>* if_exceptions,
1734 : PropertyAccessInfo const& access_info, AccessMode access_mode) {
1735 112777 : switch (access_mode) {
1736 : case AccessMode::kLoad:
1737 : return BuildPropertyLoad(receiver, context, frame_state, effect, control,
1738 84954 : name, if_exceptions, access_info);
1739 : case AccessMode::kStore:
1740 : case AccessMode::kStoreInLiteral:
1741 : return BuildPropertyStore(receiver, value, context, frame_state, effect,
1742 : control, name, if_exceptions, access_info,
1743 27823 : access_mode);
1744 : }
1745 0 : UNREACHABLE();
1746 : return ValueEffectControl();
1747 : }
1748 :
1749 : JSNativeContextSpecialization::ValueEffectControl
1750 27823 : JSNativeContextSpecialization::BuildPropertyStore(
1751 : Node* receiver, Node* value, Node* context, Node* frame_state, Node* effect,
1752 : Node* control, Handle<Name> name, ZoneVector<Node*>* if_exceptions,
1753 95639 : PropertyAccessInfo const& access_info, AccessMode access_mode) {
1754 : // Determine actual holder and perform prototype chain checks.
1755 : Handle<JSObject> holder;
1756 : PropertyAccessBuilder access_builder(jsgraph(), dependencies());
1757 27823 : if (access_info.holder().ToHandle(&holder)) {
1758 : DCHECK_NE(AccessMode::kStoreInLiteral, access_mode);
1759 : access_builder.AssumePrototypesStable(native_context(),
1760 20529 : access_info.receiver_maps(), holder);
1761 : }
1762 :
1763 : DCHECK(!access_info.IsNotFound());
1764 :
1765 : // Generate the actual property access.
1766 27823 : if (access_info.IsDataConstant()) {
1767 : DCHECK(!FLAG_track_constant_fields);
1768 7 : Node* constant_value = jsgraph()->Constant(access_info.constant());
1769 : Node* check =
1770 7 : graph()->NewNode(simplified()->ReferenceEqual(), value, constant_value);
1771 : effect =
1772 : graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kNoReason),
1773 21 : check, effect, control);
1774 : value = constant_value;
1775 27816 : } else if (access_info.IsAccessorConstant()) {
1776 : value =
1777 : InlinePropertySetterCall(receiver, value, context, frame_state, &effect,
1778 879 : &control, if_exceptions, access_info);
1779 : } else {
1780 : DCHECK(access_info.IsDataField() || access_info.IsDataConstantField());
1781 : FieldIndex const field_index = access_info.field_index();
1782 : Type* const field_type = access_info.field_type();
1783 : MachineRepresentation const field_representation =
1784 26937 : access_info.field_representation();
1785 : Node* storage = receiver;
1786 26937 : if (!field_index.is_inobject()) {
1787 : storage = effect = graph()->NewNode(
1788 : simplified()->LoadField(AccessBuilder::ForJSObjectPropertiesOrHash()),
1789 1629 : storage, effect, control);
1790 : }
1791 : FieldAccess field_access = {
1792 : kTaggedBase,
1793 : field_index.offset(),
1794 : name,
1795 : MaybeHandle<Map>(),
1796 : field_type,
1797 : MachineType::TypeForRepresentation(field_representation),
1798 80811 : kFullWriteBarrier};
1799 : bool store_to_constant_field = FLAG_track_constant_fields &&
1800 : (access_mode == AccessMode::kStore) &&
1801 : access_info.IsDataConstantField();
1802 :
1803 : DCHECK(access_mode == AccessMode::kStore ||
1804 : access_mode == AccessMode::kStoreInLiteral);
1805 26937 : switch (field_representation) {
1806 : case MachineRepresentation::kFloat64: {
1807 : value = effect = graph()->NewNode(simplified()->CheckNumber(), value,
1808 825 : effect, control);
1809 479 : if (!field_index.is_inobject() || field_index.is_hidden_field() ||
1810 : !FLAG_unbox_double_fields) {
1811 71 : if (access_info.HasTransitionMap()) {
1812 : // Allocate a MutableHeapNumber for the new property.
1813 30 : AllocationBuilder a(jsgraph(), effect, control);
1814 30 : a.Allocate(HeapNumber::kSize, NOT_TENURED, Type::OtherInternal());
1815 : a.Store(AccessBuilder::ForMap(),
1816 30 : factory()->mutable_heap_number_map());
1817 30 : a.Store(AccessBuilder::ForHeapNumberValue(), value);
1818 30 : value = effect = a.Finish();
1819 :
1820 30 : field_access.type = Type::Any();
1821 30 : field_access.machine_type = MachineType::TaggedPointer();
1822 30 : field_access.write_barrier_kind = kPointerWriteBarrier;
1823 : } else {
1824 : // We just store directly to the MutableHeapNumber.
1825 : FieldAccess const storage_access = {kTaggedBase,
1826 : field_index.offset(),
1827 : name,
1828 : MaybeHandle<Map>(),
1829 : Type::OtherInternal(),
1830 : MachineType::TaggedPointer(),
1831 82 : kPointerWriteBarrier};
1832 : storage = effect =
1833 : graph()->NewNode(simplified()->LoadField(storage_access),
1834 123 : storage, effect, control);
1835 41 : field_access.offset = HeapNumber::kValueOffset;
1836 41 : field_access.name = MaybeHandle<Name>();
1837 41 : field_access.machine_type = MachineType::Float64();
1838 : }
1839 : }
1840 : if (store_to_constant_field) {
1841 : DCHECK(!access_info.HasTransitionMap());
1842 : // If the field is constant check that the value we are going
1843 : // to store matches current value.
1844 : Node* current_value = effect = graph()->NewNode(
1845 : simplified()->LoadField(field_access), storage, effect, control);
1846 :
1847 : Node* check = graph()->NewNode(simplified()->NumberEqual(),
1848 : current_value, value);
1849 : effect = graph()->NewNode(
1850 : simplified()->CheckIf(DeoptimizeReason::kNoReason), check, effect,
1851 : control);
1852 : return ValueEffectControl(value, effect, control);
1853 : }
1854 : break;
1855 : }
1856 : case MachineRepresentation::kTaggedSigned:
1857 : case MachineRepresentation::kTaggedPointer:
1858 : case MachineRepresentation::kTagged:
1859 : if (store_to_constant_field) {
1860 : DCHECK(!access_info.HasTransitionMap());
1861 : // If the field is constant check that the value we are going
1862 : // to store matches current value.
1863 : Node* current_value = effect = graph()->NewNode(
1864 : simplified()->LoadField(field_access), storage, effect, control);
1865 :
1866 : Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
1867 : current_value, value);
1868 : effect = graph()->NewNode(
1869 : simplified()->CheckIf(DeoptimizeReason::kNoReason), check, effect,
1870 : control);
1871 : return ValueEffectControl(value, effect, control);
1872 : }
1873 :
1874 26662 : if (field_representation == MachineRepresentation::kTaggedSigned) {
1875 : value = effect = graph()->NewNode(simplified()->CheckSmi(), value,
1876 34599 : effect, control);
1877 11533 : field_access.write_barrier_kind = kNoWriteBarrier;
1878 :
1879 15129 : } else if (field_representation ==
1880 : MachineRepresentation::kTaggedPointer) {
1881 : // Ensure that {value} is a HeapObject.
1882 1769 : value = access_builder.BuildCheckHeapObject(value, &effect, control);
1883 : Handle<Map> field_map;
1884 1769 : if (access_info.field_map().ToHandle(&field_map)) {
1885 : // Emit a map check for the value.
1886 : effect = graph()->NewNode(
1887 : simplified()->CheckMaps(CheckMapsFlag::kNone,
1888 : ZoneHandleSet<Map>(field_map)),
1889 348 : value, effect, control);
1890 : }
1891 1769 : field_access.write_barrier_kind = kPointerWriteBarrier;
1892 :
1893 : } else {
1894 : DCHECK_EQ(MachineRepresentation::kTagged, field_representation);
1895 : }
1896 : break;
1897 : case MachineRepresentation::kNone:
1898 : case MachineRepresentation::kBit:
1899 : case MachineRepresentation::kWord8:
1900 : case MachineRepresentation::kWord16:
1901 : case MachineRepresentation::kWord32:
1902 : case MachineRepresentation::kWord64:
1903 : case MachineRepresentation::kFloat32:
1904 : case MachineRepresentation::kSimd128:
1905 0 : UNREACHABLE();
1906 : break;
1907 : }
1908 : // Check if we need to perform a transitioning store.
1909 : Handle<Map> transition_map;
1910 26937 : if (access_info.transition_map().ToHandle(&transition_map)) {
1911 : // Check if we need to grow the properties backing store
1912 : // with this transitioning store.
1913 : Handle<Map> original_map(Map::cast(transition_map->GetBackPointer()),
1914 20421 : isolate());
1915 20421 : if (original_map->UnusedPropertyFields() == 0) {
1916 : DCHECK(!field_index.is_inobject());
1917 :
1918 : // Reallocate the properties {storage}.
1919 : storage = effect = BuildExtendPropertiesBackingStore(
1920 236 : original_map, storage, effect, control);
1921 :
1922 : // Perform the actual store.
1923 : effect = graph()->NewNode(simplified()->StoreField(field_access),
1924 708 : storage, value, effect, control);
1925 :
1926 : // Atomically switch to the new properties below.
1927 236 : field_access = AccessBuilder::ForJSObjectPropertiesOrHash();
1928 : value = storage;
1929 : storage = receiver;
1930 : }
1931 : effect = graph()->NewNode(
1932 61263 : common()->BeginRegion(RegionObservability::kObservable), effect);
1933 : effect = graph()->NewNode(
1934 : simplified()->StoreField(AccessBuilder::ForMap()), receiver,
1935 81684 : jsgraph()->Constant(transition_map), effect, control);
1936 : effect = graph()->NewNode(simplified()->StoreField(field_access), storage,
1937 61263 : value, effect, control);
1938 : effect = graph()->NewNode(common()->FinishRegion(),
1939 61263 : jsgraph()->UndefinedConstant(), effect);
1940 : } else {
1941 : // Regular non-transitioning field store.
1942 : effect = graph()->NewNode(simplified()->StoreField(field_access), storage,
1943 19548 : value, effect, control);
1944 : }
1945 : }
1946 :
1947 27823 : return ValueEffectControl(value, effect, control);
1948 : }
1949 :
1950 55213 : Reduction JSNativeContextSpecialization::ReduceJSStoreDataPropertyInLiteral(
1951 55604 : Node* node) {
1952 : DCHECK_EQ(IrOpcode::kJSStoreDataPropertyInLiteral, node->opcode());
1953 :
1954 55213 : FeedbackParameter const& p = FeedbackParameterOf(node->op());
1955 :
1956 55213 : if (!p.feedback().IsValid()) return NoChange();
1957 :
1958 : StoreDataPropertyInLiteralICNexus nexus(p.feedback().vector(),
1959 : p.feedback().slot());
1960 55213 : if (nexus.IsUninitialized()) {
1961 : return NoChange();
1962 : }
1963 :
1964 190 : if (nexus.ic_state() == MEGAMORPHIC) {
1965 : return NoChange();
1966 : }
1967 :
1968 : DCHECK_EQ(MONOMORPHIC, nexus.ic_state());
1969 :
1970 161 : Map* map = nexus.FindFirstMap();
1971 161 : if (map == nullptr) {
1972 : // Maps are weakly held in the type feedback vector, we may not have one.
1973 : return NoChange();
1974 : }
1975 :
1976 : Handle<Map> receiver_map(map, isolate());
1977 322 : if (!Map::TryUpdate(receiver_map).ToHandle(&receiver_map)) return NoChange();
1978 :
1979 : Handle<Name> cached_name =
1980 161 : handle(Name::cast(nexus.GetFeedbackExtra()), isolate());
1981 :
1982 161 : PropertyAccessInfo access_info;
1983 : AccessInfoFactory access_info_factory(dependencies(), native_context(),
1984 322 : graph()->zone());
1985 161 : if (!access_info_factory.ComputePropertyAccessInfo(
1986 : receiver_map, cached_name, AccessMode::kStoreInLiteral,
1987 161 : &access_info)) {
1988 : return NoChange();
1989 : }
1990 :
1991 115 : Node* receiver = NodeProperties::GetValueInput(node, 0);
1992 115 : Node* effect = NodeProperties::GetEffectInput(node);
1993 115 : Node* control = NodeProperties::GetControlInput(node);
1994 :
1995 : // Monomorphic property access.
1996 : PropertyAccessBuilder access_builder(jsgraph(), dependencies());
1997 115 : receiver = access_builder.BuildCheckHeapObject(receiver, &effect, control);
1998 : access_builder.BuildCheckMaps(receiver, &effect, control,
1999 115 : access_info.receiver_maps());
2000 :
2001 : // Ensure that {name} matches the cached name.
2002 115 : Node* name = NodeProperties::GetValueInput(node, 1);
2003 : Node* check = graph()->NewNode(simplified()->ReferenceEqual(), name,
2004 345 : jsgraph()->HeapConstant(cached_name));
2005 : effect = graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kNoReason),
2006 345 : check, effect, control);
2007 :
2008 115 : Node* value = NodeProperties::GetValueInput(node, 2);
2009 115 : Node* context = NodeProperties::GetContextInput(node);
2010 115 : Node* frame_state_lazy = NodeProperties::GetFrameStateInput(node);
2011 :
2012 : // Generate the actual property access.
2013 : ValueEffectControl continuation = BuildPropertyAccess(
2014 : receiver, value, context, frame_state_lazy, effect, control, cached_name,
2015 115 : nullptr, access_info, AccessMode::kStoreInLiteral);
2016 115 : value = continuation.value();
2017 115 : effect = continuation.effect();
2018 115 : control = continuation.control();
2019 :
2020 115 : ReplaceWithValue(node, value, effect, control);
2021 : return Replace(value);
2022 : }
2023 :
2024 : namespace {
2025 :
2026 8625 : ExternalArrayType GetArrayTypeFromElementsKind(ElementsKind kind) {
2027 8625 : switch (kind) {
2028 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
2029 : case TYPE##_ELEMENTS: \
2030 : return kExternal##Type##Array;
2031 701 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
2032 : #undef TYPED_ARRAY_CASE
2033 : default:
2034 : break;
2035 : }
2036 0 : UNREACHABLE();
2037 : }
2038 :
2039 : } // namespace
2040 :
2041 : JSNativeContextSpecialization::ValueEffectControl
2042 27333 : JSNativeContextSpecialization::BuildElementAccess(
2043 : Node* receiver, Node* index, Node* value, Node* effect, Node* control,
2044 27333 : ElementAccessInfo const& access_info, AccessMode access_mode,
2045 23583 : KeyedAccessStoreMode store_mode) {
2046 : DCHECK_NE(AccessMode::kStoreInLiteral, access_mode);
2047 :
2048 : // TODO(bmeurer): We currently specialize based on elements kind. We should
2049 : // also be able to properly support strings and other JSObjects here.
2050 : ElementsKind elements_kind = access_info.elements_kind();
2051 27333 : MapHandles const& receiver_maps = access_info.receiver_maps();
2052 :
2053 27333 : if (IsFixedTypedArrayElementsKind(elements_kind)) {
2054 : Node* buffer;
2055 : Node* length;
2056 : Node* base_pointer;
2057 : Node* external_pointer;
2058 :
2059 : // Check if we can constant-fold information about the {receiver} (i.e.
2060 : // for asm.js-like code patterns).
2061 : HeapObjectMatcher m(receiver);
2062 11969 : if (m.HasValue() && m.Value()->IsJSTypedArray()) {
2063 : Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(m.Value());
2064 :
2065 : // Determine the {receiver}s (known) length.
2066 6688 : length = jsgraph()->Constant(typed_array->length_value());
2067 :
2068 : // Check if the {receiver}s buffer was neutered.
2069 6688 : buffer = jsgraph()->HeapConstant(typed_array->GetBuffer());
2070 :
2071 : // Load the (known) base and external pointer for the {receiver}. The
2072 : // {external_pointer} might be invalid if the {buffer} was neutered, so
2073 : // we need to make sure that any access is properly guarded.
2074 3344 : base_pointer = jsgraph()->ZeroConstant();
2075 : external_pointer = jsgraph()->PointerConstant(
2076 : FixedTypedArrayBase::cast(typed_array->elements())
2077 : ->external_pointer());
2078 : } else {
2079 : // Load the {receiver}s length.
2080 : length = effect = graph()->NewNode(
2081 : simplified()->LoadField(AccessBuilder::ForJSTypedArrayLength()),
2082 15843 : receiver, effect, control);
2083 :
2084 : // Load the buffer for the {receiver}.
2085 : buffer = effect = graph()->NewNode(
2086 : simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
2087 15843 : receiver, effect, control);
2088 :
2089 : // Load the elements for the {receiver}.
2090 : Node* elements = effect = graph()->NewNode(
2091 : simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
2092 15843 : receiver, effect, control);
2093 :
2094 : // Load the base and external pointer for the {receiver}s {elements}.
2095 : base_pointer = effect = graph()->NewNode(
2096 : simplified()->LoadField(
2097 : AccessBuilder::ForFixedTypedArrayBaseBasePointer()),
2098 15843 : elements, effect, control);
2099 : external_pointer = effect = graph()->NewNode(
2100 : simplified()->LoadField(
2101 : AccessBuilder::ForFixedTypedArrayBaseExternalPointer()),
2102 15843 : elements, effect, control);
2103 : }
2104 :
2105 : // See if we can skip the neutering check.
2106 8625 : if (isolate()->IsArrayBufferNeuteringIntact()) {
2107 : // Add a code dependency so we are deoptimized in case an ArrayBuffer
2108 : // gets neutered.
2109 : dependencies()->AssumePropertyCell(
2110 : factory()->array_buffer_neutering_protector());
2111 : } else {
2112 : // Default to zero if the {receiver}s buffer was neutered.
2113 : Node* check = effect = graph()->NewNode(
2114 130 : simplified()->ArrayBufferWasNeutered(), buffer, effect, control);
2115 : length = graph()->NewNode(
2116 : common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
2117 260 : check, jsgraph()->ZeroConstant(), length);
2118 : }
2119 :
2120 8625 : if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
2121 : // Check that the {index} is a valid array index, we do the actual
2122 : // bounds check below and just skip the store below if it's out of
2123 : // bounds for the {receiver}.
2124 : index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
2125 : jsgraph()->Constant(Smi::kMaxValue),
2126 320 : effect, control);
2127 : } else {
2128 : // Check that the {index} is in the valid range for the {receiver}.
2129 : index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
2130 8465 : length, effect, control);
2131 : }
2132 :
2133 : // Access the actual element.
2134 : ExternalArrayType external_array_type =
2135 8625 : GetArrayTypeFromElementsKind(elements_kind);
2136 8625 : switch (access_mode) {
2137 : case AccessMode::kLoad: {
2138 : value = effect = graph()->NewNode(
2139 : simplified()->LoadTypedElement(external_array_type), buffer,
2140 4240 : base_pointer, external_pointer, index, effect, control);
2141 4240 : break;
2142 : }
2143 : case AccessMode::kStoreInLiteral:
2144 0 : UNREACHABLE();
2145 : break;
2146 : case AccessMode::kStore: {
2147 : // Ensure that the {value} is actually a Number or an Oddball,
2148 : // and truncate it to a Number appropriately.
2149 : value = effect =
2150 : graph()->NewNode(simplified()->SpeculativeToNumber(
2151 : NumberOperationHint::kNumberOrOddball),
2152 4385 : value, effect, control);
2153 :
2154 : // Introduce the appropriate truncation for {value}. Currently we
2155 : // only need to do this for ClamedUint8Array {receiver}s, as the
2156 : // other truncations are implicit in the StoreTypedElement, but we
2157 : // might want to change that at some point.
2158 4385 : if (external_array_type == kExternalUint8ClampedArray) {
2159 623 : value = graph()->NewNode(simplified()->NumberToUint8Clamped(), value);
2160 : }
2161 :
2162 : // Check if we can skip the out-of-bounds store.
2163 4385 : if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
2164 : Node* check =
2165 160 : graph()->NewNode(simplified()->NumberLessThan(), index, length);
2166 : Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
2167 160 : check, control);
2168 :
2169 160 : Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
2170 : Node* etrue = effect;
2171 : {
2172 : // Perform the actual store.
2173 : etrue = graph()->NewNode(
2174 : simplified()->StoreTypedElement(external_array_type), buffer,
2175 160 : base_pointer, external_pointer, index, value, etrue, if_true);
2176 : }
2177 :
2178 160 : Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
2179 : Node* efalse = effect;
2180 : {
2181 : // Just ignore the out-of-bounds write.
2182 : }
2183 :
2184 160 : control = graph()->NewNode(common()->Merge(2), if_true, if_false);
2185 : effect =
2186 160 : graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
2187 : } else {
2188 : // Perform the actual store
2189 : effect = graph()->NewNode(
2190 : simplified()->StoreTypedElement(external_array_type), buffer,
2191 4225 : base_pointer, external_pointer, index, value, effect, control);
2192 : }
2193 : break;
2194 : }
2195 : }
2196 : } else {
2197 : // Load the elements for the {receiver}.
2198 : Node* elements = effect = graph()->NewNode(
2199 : simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
2200 56124 : effect, control);
2201 :
2202 : // Don't try to store to a copy-on-write backing store.
2203 25520 : if (access_mode == AccessMode::kStore &&
2204 24535 : IsSmiOrObjectElementsKind(elements_kind) &&
2205 : store_mode != STORE_NO_TRANSITION_HANDLE_COW) {
2206 : effect = graph()->NewNode(
2207 : simplified()->CheckMaps(
2208 : CheckMapsFlag::kNone,
2209 : ZoneHandleSet<Map>(factory()->fixed_array_map())),
2210 11250 : elements, effect, control);
2211 : }
2212 :
2213 : // Check if the {receiver} is a JSArray.
2214 : bool receiver_is_jsarray = HasOnlyJSArrayMaps(receiver_maps);
2215 :
2216 : // Load the length of the {receiver}.
2217 : Node* length = effect =
2218 : receiver_is_jsarray
2219 : ? graph()->NewNode(
2220 : simplified()->LoadField(
2221 : AccessBuilder::ForJSArrayLength(elements_kind)),
2222 68367 : receiver, effect, control)
2223 : : graph()->NewNode(
2224 : simplified()->LoadField(AccessBuilder::ForFixedArrayLength()),
2225 43881 : elements, effect, control);
2226 :
2227 : // Check if we might need to grow the {elements} backing store.
2228 18708 : if (IsGrowStoreMode(store_mode)) {
2229 : // For growing stores we validate the {index} below.
2230 : DCHECK_EQ(AccessMode::kStore, access_mode);
2231 : } else {
2232 : // Check that the {index} is in the valid range for the {receiver}.
2233 : index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
2234 17990 : length, effect, control);
2235 : }
2236 :
2237 : // Compute the element access.
2238 : Type* element_type = Type::NonInternal();
2239 : MachineType element_machine_type = MachineType::AnyTagged();
2240 18708 : if (IsDoubleElementsKind(elements_kind)) {
2241 : element_type = Type::Number();
2242 : element_machine_type = MachineType::Float64();
2243 16419 : } else if (IsSmiElementsKind(elements_kind)) {
2244 : element_type = Type::SignedSmall();
2245 : element_machine_type = MachineType::TaggedSigned();
2246 : }
2247 : ElementAccess element_access = {kTaggedBase, FixedArray::kHeaderSize,
2248 : element_type, element_machine_type,
2249 18708 : kFullWriteBarrier};
2250 :
2251 : // Access the actual element.
2252 18708 : if (access_mode == AccessMode::kLoad) {
2253 : // Compute the real element access type, which includes the hole in case
2254 : // of holey backing stores.
2255 11896 : if (IsHoleyOrDictionaryElementsKind(elements_kind)) {
2256 : element_access.type =
2257 3173 : Type::Union(element_type, Type::Hole(), graph()->zone());
2258 : }
2259 11896 : if (elements_kind == HOLEY_ELEMENTS ||
2260 : elements_kind == HOLEY_SMI_ELEMENTS) {
2261 2406 : element_access.machine_type = MachineType::AnyTagged();
2262 : }
2263 : // Perform the actual backing store access.
2264 : value = effect =
2265 : graph()->NewNode(simplified()->LoadElement(element_access), elements,
2266 11896 : index, effect, control);
2267 : // Handle loading from holey backing stores correctly, by either mapping
2268 : // the hole to undefined if possible, or deoptimizing otherwise.
2269 11896 : if (elements_kind == HOLEY_ELEMENTS ||
2270 : elements_kind == HOLEY_SMI_ELEMENTS) {
2271 : // Check if we are allowed to turn the hole into undefined.
2272 2406 : if (CanTreatHoleAsUndefined(receiver_maps)) {
2273 : // Turn the hole into undefined.
2274 : value = graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(),
2275 1430 : value);
2276 : } else {
2277 : // Bailout if we see the hole.
2278 : value = effect = graph()->NewNode(simplified()->CheckNotTaggedHole(),
2279 976 : value, effect, control);
2280 : }
2281 9490 : } else if (elements_kind == HOLEY_DOUBLE_ELEMENTS) {
2282 : // Perform the hole check on the result.
2283 : CheckFloat64HoleMode mode = CheckFloat64HoleMode::kNeverReturnHole;
2284 : // Check if we are allowed to return the hole directly.
2285 767 : if (CanTreatHoleAsUndefined(receiver_maps)) {
2286 : // Return the signaling NaN hole directly if all uses are truncating.
2287 : mode = CheckFloat64HoleMode::kAllowReturnHole;
2288 : }
2289 : value = effect = graph()->NewNode(simplified()->CheckFloat64Hole(mode),
2290 767 : value, effect, control);
2291 : }
2292 : } else {
2293 : DCHECK_EQ(AccessMode::kStore, access_mode);
2294 6812 : if (IsSmiElementsKind(elements_kind)) {
2295 : value = effect =
2296 2146 : graph()->NewNode(simplified()->CheckSmi(), value, effect, control);
2297 4666 : } else if (IsDoubleElementsKind(elements_kind)) {
2298 : value = effect = graph()->NewNode(simplified()->CheckNumber(), value,
2299 985 : effect, control);
2300 : // Make sure we do not store signalling NaNs into double arrays.
2301 985 : value = graph()->NewNode(simplified()->NumberSilenceNaN(), value);
2302 : }
2303 :
2304 : // Ensure that copy-on-write backing store is writable.
2305 6812 : if (IsSmiOrObjectElementsKind(elements_kind) &&
2306 : store_mode == STORE_NO_TRANSITION_HANDLE_COW) {
2307 : elements = effect =
2308 : graph()->NewNode(simplified()->EnsureWritableFastElements(),
2309 202 : receiver, elements, effect, control);
2310 6610 : } else if (IsGrowStoreMode(store_mode)) {
2311 : // Determine the length of the {elements} backing store.
2312 : Node* elements_length = effect = graph()->NewNode(
2313 : simplified()->LoadField(AccessBuilder::ForFixedArrayLength()),
2314 2154 : elements, effect, control);
2315 :
2316 : // Validate the {index} depending on holeyness:
2317 : //
2318 : // For HOLEY_*_ELEMENTS the {index} must not exceed the {elements}
2319 : // backing store capacity plus the maximum allowed gap, as otherwise
2320 : // the (potential) backing store growth would normalize and thus
2321 : // the elements kind of the {receiver} would change to slow mode.
2322 : //
2323 : // For PACKED_*_ELEMENTS the {index} must be within the range
2324 : // [0,length+1[ to be valid. In case {index} equals {length},
2325 : // the {receiver} will be extended, but kept packed.
2326 : Node* limit =
2327 : IsHoleyElementsKind(elements_kind)
2328 : ? graph()->NewNode(simplified()->NumberAdd(), elements_length,
2329 414 : jsgraph()->Constant(JSObject::kMaxGap))
2330 : : graph()->NewNode(simplified()->NumberAdd(), length,
2331 1740 : jsgraph()->OneConstant());
2332 : index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
2333 718 : limit, effect, control);
2334 :
2335 : // Grow {elements} backing store if necessary.
2336 : GrowFastElementsMode mode =
2337 : IsDoubleElementsKind(elements_kind)
2338 : ? GrowFastElementsMode::kDoubleElements
2339 718 : : GrowFastElementsMode::kSmiOrObjectElements;
2340 : elements = effect = graph()->NewNode(
2341 : simplified()->MaybeGrowFastElements(mode), receiver, elements,
2342 718 : index, elements_length, effect, control);
2343 :
2344 : // Also update the "length" property if {receiver} is a JSArray.
2345 718 : if (receiver_is_jsarray) {
2346 : Node* check =
2347 704 : graph()->NewNode(simplified()->NumberLessThan(), index, length);
2348 704 : Node* branch = graph()->NewNode(common()->Branch(), check, control);
2349 :
2350 704 : Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
2351 : Node* etrue = effect;
2352 : {
2353 : // We don't need to do anything, the {index} is within
2354 : // the valid bounds for the JSArray {receiver}.
2355 : }
2356 :
2357 704 : Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
2358 : Node* efalse = effect;
2359 : {
2360 : // Update the JSArray::length field. Since this is observable,
2361 : // there must be no other check after this.
2362 : Node* new_length = graph()->NewNode(
2363 1408 : simplified()->NumberAdd(), index, jsgraph()->OneConstant());
2364 : efalse = graph()->NewNode(
2365 : simplified()->StoreField(
2366 : AccessBuilder::ForJSArrayLength(elements_kind)),
2367 2112 : receiver, new_length, efalse, if_false);
2368 : }
2369 :
2370 704 : control = graph()->NewNode(common()->Merge(2), if_true, if_false);
2371 : effect =
2372 704 : graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
2373 : }
2374 : }
2375 :
2376 : // Perform the actual element access.
2377 : effect = graph()->NewNode(simplified()->StoreElement(element_access),
2378 6812 : elements, index, value, effect, control);
2379 : }
2380 : }
2381 :
2382 27333 : return ValueEffectControl(value, effect, control);
2383 : }
2384 :
2385 236 : Node* JSNativeContextSpecialization::BuildExtendPropertiesBackingStore(
2386 2102 : Handle<Map> map, Node* properties, Node* effect, Node* control) {
2387 : // TODO(bmeurer/jkummerow): Property deletions can undo map transitions
2388 : // while keeping the backing store around, meaning that even though the
2389 : // map might believe that objects have no unused property fields, there
2390 : // might actually be some. It would be nice to not create a new backing
2391 : // store in that case (i.e. when properties->length() >= new_length).
2392 : // However, introducing branches and Phi nodes here would make it more
2393 : // difficult for escape analysis to get rid of the backing stores used
2394 : // for intermediate states of chains of property additions. That makes
2395 : // it unclear what the best approach is here.
2396 : DCHECK_EQ(0, map->UnusedPropertyFields());
2397 : // Compute the length of the old {properties} and the new properties.
2398 472 : int length = map->NextFreePropertyIndex() - map->GetInObjectProperties();
2399 236 : int new_length = length + JSObject::kFieldsAdded;
2400 : // Collect the field values from the {properties}.
2401 : ZoneVector<Node*> values(zone());
2402 236 : values.reserve(new_length);
2403 302 : for (int i = 0; i < length; ++i) {
2404 : Node* value = effect = graph()->NewNode(
2405 66 : simplified()->LoadField(AccessBuilder::ForFixedArraySlot(i)),
2406 198 : properties, effect, control);
2407 66 : values.push_back(value);
2408 : }
2409 : // Initialize the new fields to undefined.
2410 708 : for (int i = 0; i < JSObject::kFieldsAdded; ++i) {
2411 1416 : values.push_back(jsgraph()->UndefinedConstant());
2412 : }
2413 :
2414 : // Compute new length and hash.
2415 : Node* hash;
2416 236 : if (length == 0) {
2417 : hash = graph()->NewNode(
2418 : common()->Select(MachineRepresentation::kTaggedSigned),
2419 : graph()->NewNode(simplified()->ObjectIsSmi(), properties), properties,
2420 428 : jsgraph()->SmiConstant(PropertyArray::kNoHashSentinel));
2421 : hash = graph()->NewNode(common()->TypeGuard(Type::SignedSmall()), hash,
2422 214 : control);
2423 : hash =
2424 : graph()->NewNode(simplified()->NumberShiftLeft(), hash,
2425 428 : jsgraph()->Constant(PropertyArray::HashField::kShift));
2426 : } else {
2427 : hash = effect = graph()->NewNode(
2428 : simplified()->LoadField(AccessBuilder::ForPropertyArrayLengthAndHash()),
2429 66 : properties, effect, control);
2430 : hash =
2431 : graph()->NewNode(simplified()->NumberBitwiseAnd(), hash,
2432 44 : jsgraph()->Constant(PropertyArray::HashField::kMask));
2433 : }
2434 : Node* new_length_and_hash = graph()->NewNode(
2435 472 : simplified()->NumberBitwiseOr(), jsgraph()->Constant(new_length), hash);
2436 :
2437 : // Allocate and initialize the new properties.
2438 : AllocationBuilder a(jsgraph(), effect, control);
2439 : a.Allocate(PropertyArray::SizeFor(new_length), NOT_TENURED,
2440 236 : Type::OtherInternal());
2441 236 : a.Store(AccessBuilder::ForMap(), jsgraph()->PropertyArrayMapConstant());
2442 236 : a.Store(AccessBuilder::ForPropertyArrayLengthAndHash(), new_length_and_hash);
2443 1010 : for (int i = 0; i < new_length; ++i) {
2444 1548 : a.Store(AccessBuilder::ForFixedArraySlot(i), values[i]);
2445 : }
2446 236 : return a.Finish();
2447 : }
2448 :
2449 108 : Node* JSNativeContextSpecialization::BuildCheckEqualsName(Handle<Name> name,
2450 : Node* value,
2451 : Node* effect,
2452 108 : Node* control) {
2453 : DCHECK(name->IsUniqueName());
2454 : Operator const* const op =
2455 : name->IsSymbol() ? simplified()->CheckEqualsSymbol()
2456 216 : : simplified()->CheckEqualsInternalizedString();
2457 : return graph()->NewNode(op, jsgraph()->HeapConstant(name), value, effect,
2458 216 : control);
2459 : }
2460 :
2461 3173 : bool JSNativeContextSpecialization::CanTreatHoleAsUndefined(
2462 2145 : MapHandles const& receiver_maps) {
2463 : // Check if all {receiver_maps} either have one of the initial Array.prototype
2464 : // or Object.prototype objects as their prototype (in any of the current
2465 : // native contexts, as the global Array protector works isolate-wide).
2466 8850 : for (Handle<Map> receiver_map : receiver_maps) {
2467 : DisallowHeapAllocation no_gc;
2468 : Object* const receiver_prototype = receiver_map->prototype();
2469 3449 : if (!isolate()->IsInAnyContext(receiver_prototype,
2470 4905 : Context::INITIAL_ARRAY_PROTOTYPE_INDEX) &&
2471 : !isolate()->IsInAnyContext(receiver_prototype,
2472 1456 : Context::INITIAL_OBJECT_PROTOTYPE_INDEX)) {
2473 : return false;
2474 : }
2475 : }
2476 :
2477 : // Check if the array prototype chain is intact.
2478 2228 : if (!isolate()->IsFastArrayConstructorPrototypeChainIntact()) return false;
2479 :
2480 : // Install code dependency on the array protector cell.
2481 : dependencies()->AssumePropertyCell(factory()->array_protector());
2482 2145 : return true;
2483 : }
2484 :
2485 167812 : bool JSNativeContextSpecialization::ExtractReceiverMaps(
2486 : Node* receiver, Node* effect, FeedbackNexus const& nexus,
2487 : MapHandles* receiver_maps) {
2488 : DCHECK_EQ(0, receiver_maps->size());
2489 : // See if we can infer a concrete type for the {receiver}.
2490 167812 : if (InferReceiverMaps(receiver, effect, receiver_maps)) {
2491 : // We can assume that the {receiver} still has the inferred {receiver_maps}.
2492 : return true;
2493 : }
2494 : // Try to extract some maps from the {nexus}.
2495 91425 : if (nexus.ExtractMaps(receiver_maps) != 0) {
2496 : // Try to filter impossible candidates based on inferred root map.
2497 : Handle<Map> receiver_map;
2498 127032 : if (InferReceiverRootMap(receiver).ToHandle(&receiver_map)) {
2499 : DCHECK(!receiver_map->is_abandoned_prototype_map());
2500 : receiver_maps->erase(
2501 : std::remove_if(receiver_maps->begin(), receiver_maps->end(),
2502 3253 : [receiver_map](const Handle<Map>& map) {
2503 6498 : return map->is_abandoned_prototype_map() ||
2504 3245 : map->FindRootMap() != *receiver_map;
2505 3253 : }),
2506 6368 : receiver_maps->end());
2507 : }
2508 : return true;
2509 : }
2510 : return false;
2511 : }
2512 :
2513 167812 : bool JSNativeContextSpecialization::InferReceiverMaps(
2514 : Node* receiver, Node* effect, MapHandles* receiver_maps) {
2515 : ZoneHandleSet<Map> maps;
2516 : NodeProperties::InferReceiverMapsResult result =
2517 167812 : NodeProperties::InferReceiverMaps(receiver, effect, &maps);
2518 167812 : if (result == NodeProperties::kReliableReceiverMaps) {
2519 64018 : for (size_t i = 0; i < maps.size(); ++i) {
2520 43890 : receiver_maps->push_back(maps[i]);
2521 : }
2522 : return true;
2523 147684 : } else if (result == NodeProperties::kUnreliableReceiverMaps) {
2524 : // For untrusted receiver maps, we can still use the information
2525 : // if the maps are stable.
2526 174560 : for (size_t i = 0; i < maps.size(); ++i) {
2527 61337 : if (!maps[i]->is_stable()) return false;
2528 : }
2529 169713 : for (size_t i = 0; i < maps.size(); ++i) {
2530 113454 : receiver_maps->push_back(maps[i]);
2531 : }
2532 : return true;
2533 : }
2534 : return false;
2535 : }
2536 :
2537 63516 : MaybeHandle<Map> JSNativeContextSpecialization::InferReceiverRootMap(
2538 : Node* receiver) {
2539 : HeapObjectMatcher m(receiver);
2540 63516 : if (m.HasValue()) {
2541 5918 : return handle(m.Value()->map()->FindRootMap(), isolate());
2542 60557 : } else if (m.IsJSCreate()) {
2543 : HeapObjectMatcher mtarget(m.InputAt(0));
2544 : HeapObjectMatcher mnewtarget(m.InputAt(1));
2545 2279 : if (mtarget.HasValue() && mnewtarget.HasValue()) {
2546 : Handle<JSFunction> constructor =
2547 : Handle<JSFunction>::cast(mtarget.Value());
2548 250 : if (constructor->has_initial_map()) {
2549 : Handle<Map> initial_map(constructor->initial_map(), isolate());
2550 250 : if (initial_map->constructor_or_backpointer() == *mnewtarget.Value()) {
2551 : DCHECK_EQ(*initial_map, initial_map->FindRootMap());
2552 225 : return initial_map;
2553 : }
2554 : }
2555 : }
2556 : }
2557 60332 : return MaybeHandle<Map>();
2558 : }
2559 :
2560 785147 : bool JSNativeContextSpecialization::LookupInScriptContextTable(
2561 : Handle<Name> name, ScriptContextTableLookupResult* result) {
2562 785147 : if (!name->IsString()) return false;
2563 : Handle<ScriptContextTable> script_context_table(
2564 : global_object()->native_context()->script_context_table(), isolate());
2565 : ScriptContextTable::LookupResult lookup_result;
2566 785147 : if (!ScriptContextTable::Lookup(script_context_table,
2567 785147 : Handle<String>::cast(name), &lookup_result)) {
2568 : return false;
2569 : }
2570 : Handle<Context> script_context = ScriptContextTable::GetContext(
2571 10322 : script_context_table, lookup_result.context_index);
2572 10322 : result->context = script_context;
2573 10322 : result->immutable = lookup_result.mode == CONST;
2574 10322 : result->index = lookup_result.slot_index;
2575 10322 : return true;
2576 : }
2577 :
2578 683269 : Graph* JSNativeContextSpecialization::graph() const {
2579 683307 : return jsgraph()->graph();
2580 : }
2581 :
2582 1705708 : Isolate* JSNativeContextSpecialization::isolate() const {
2583 1705843 : return jsgraph()->isolate();
2584 : }
2585 :
2586 0 : Factory* JSNativeContextSpecialization::factory() const {
2587 0 : return isolate()->factory();
2588 : }
2589 :
2590 131844 : CommonOperatorBuilder* JSNativeContextSpecialization::common() const {
2591 131844 : return jsgraph()->common();
2592 : }
2593 :
2594 13518 : JSOperatorBuilder* JSNativeContextSpecialization::javascript() const {
2595 13518 : return jsgraph()->javascript();
2596 : }
2597 :
2598 367165 : SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const {
2599 373022 : return jsgraph()->simplified();
2600 : }
2601 :
2602 : } // namespace compiler
2603 : } // namespace internal
2604 : } // namespace v8
|