blob: a0115df0dbb00919b9ad60f0e1e4ae0967656c08 [file] [log] [blame]
Kaido Kertf309f9a2021-04-30 12:09:15 -07001// 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/api/api-inl.h"
8#include "src/builtins/accessors.h"
9#include "src/codegen/code-factory.h"
10#include "src/codegen/string-constants.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/compilation-dependencies.h"
15#include "src/compiler/js-graph.h"
16#include "src/compiler/js-operator.h"
17#include "src/compiler/linkage.h"
18#include "src/compiler/map-inference.h"
19#include "src/compiler/node-matchers.h"
20#include "src/compiler/property-access-builder.h"
21#include "src/compiler/type-cache.h"
22#include "src/execution/isolate-inl.h"
23#include "src/numbers/dtoa.h"
24#include "src/objects/feedback-vector.h"
25#include "src/objects/field-index-inl.h"
26#include "src/objects/heap-number.h"
27#include "src/objects/js-array-buffer-inl.h"
28#include "src/objects/js-array-inl.h"
29#include "src/objects/templates.h"
30
31namespace v8 {
32namespace internal {
33namespace compiler {
34
35namespace {
36
37bool HasNumberMaps(JSHeapBroker* broker, ZoneVector<Handle<Map>> const& maps) {
38 for (auto map : maps) {
39 MapRef map_ref(broker, map);
40 if (map_ref.IsHeapNumberMap()) return true;
41 }
42 return false;
43}
44
45bool HasOnlyJSArrayMaps(JSHeapBroker* broker,
46 ZoneVector<Handle<Map>> const& maps) {
47 for (auto map : maps) {
48 MapRef map_ref(broker, map);
49 if (!map_ref.IsJSArrayMap()) return false;
50 }
51 return true;
52}
53
54} // namespace
55
56bool JSNativeContextSpecialization::should_disallow_heap_access() const {
57 return broker()->is_concurrent_inlining();
58}
59
60JSNativeContextSpecialization::JSNativeContextSpecialization(
61 Editor* editor, JSGraph* jsgraph, JSHeapBroker* broker, Flags flags,
62 CompilationDependencies* dependencies, Zone* zone, Zone* shared_zone)
63 : AdvancedReducer(editor),
64 jsgraph_(jsgraph),
65 broker_(broker),
66 flags_(flags),
67 global_object_(broker->target_native_context().global_object().object()),
68 global_proxy_(
69 broker->target_native_context().global_proxy_object().object()),
70 dependencies_(dependencies),
71 zone_(zone),
72 shared_zone_(shared_zone),
73 type_cache_(TypeCache::Get()) {}
74
75Reduction JSNativeContextSpecialization::Reduce(Node* node) {
76 DisallowHeapAccessIf disallow_heap_access(should_disallow_heap_access());
77
78 switch (node->opcode()) {
79 case IrOpcode::kJSAdd:
80 return ReduceJSAdd(node);
81 case IrOpcode::kJSAsyncFunctionEnter:
82 return ReduceJSAsyncFunctionEnter(node);
83 case IrOpcode::kJSAsyncFunctionReject:
84 return ReduceJSAsyncFunctionReject(node);
85 case IrOpcode::kJSAsyncFunctionResolve:
86 return ReduceJSAsyncFunctionResolve(node);
87 case IrOpcode::kJSGetSuperConstructor:
88 return ReduceJSGetSuperConstructor(node);
89 case IrOpcode::kJSInstanceOf:
90 return ReduceJSInstanceOf(node);
91 case IrOpcode::kJSHasInPrototypeChain:
92 return ReduceJSHasInPrototypeChain(node);
93 case IrOpcode::kJSOrdinaryHasInstance:
94 return ReduceJSOrdinaryHasInstance(node);
95 case IrOpcode::kJSPromiseResolve:
96 return ReduceJSPromiseResolve(node);
97 case IrOpcode::kJSResolvePromise:
98 return ReduceJSResolvePromise(node);
99 case IrOpcode::kJSLoadGlobal:
100 return ReduceJSLoadGlobal(node);
101 case IrOpcode::kJSStoreGlobal:
102 return ReduceJSStoreGlobal(node);
103 case IrOpcode::kJSLoadNamed:
104 return ReduceJSLoadNamed(node);
105 case IrOpcode::kJSLoadNamedFromSuper:
106 return ReduceJSLoadNamedFromSuper(node);
107 case IrOpcode::kJSStoreNamed:
108 return ReduceJSStoreNamed(node);
109 case IrOpcode::kJSHasProperty:
110 return ReduceJSHasProperty(node);
111 case IrOpcode::kJSLoadProperty:
112 return ReduceJSLoadProperty(node);
113 case IrOpcode::kJSStoreProperty:
114 return ReduceJSStoreProperty(node);
115 case IrOpcode::kJSStoreNamedOwn:
116 return ReduceJSStoreNamedOwn(node);
117 case IrOpcode::kJSStoreDataPropertyInLiteral:
118 return ReduceJSStoreDataPropertyInLiteral(node);
119 case IrOpcode::kJSStoreInArrayLiteral:
120 return ReduceJSStoreInArrayLiteral(node);
121 case IrOpcode::kJSToObject:
122 return ReduceJSToObject(node);
123 case IrOpcode::kJSToString:
124 return ReduceJSToString(node);
125 case IrOpcode::kJSGetIterator:
126 return ReduceJSGetIterator(node);
127 default:
128 break;
129 }
130 return NoChange();
131}
132
133// static
134base::Optional<size_t> JSNativeContextSpecialization::GetMaxStringLength(
135 JSHeapBroker* broker, Node* node) {
136 if (node->opcode() == IrOpcode::kDelayedStringConstant) {
137 return StringConstantBaseOf(node->op())->GetMaxStringConstantLength();
138 }
139
140 HeapObjectMatcher matcher(node);
141 if (matcher.HasResolvedValue() && matcher.Ref(broker).IsString()) {
142 StringRef input = matcher.Ref(broker).AsString();
143 return input.length();
144 }
145
146 NumberMatcher number_matcher(node);
147 if (number_matcher.HasResolvedValue()) {
148 return kBase10MaximalLength + 1;
149 }
150
151 // We don't support objects with possibly monkey-patched prototype.toString
152 // as it might have side-effects, so we shouldn't attempt lowering them.
153 return base::nullopt;
154}
155
156Reduction JSNativeContextSpecialization::ReduceJSToString(Node* node) {
157 DCHECK_EQ(IrOpcode::kJSToString, node->opcode());
158 Node* const input = node->InputAt(0);
159 Reduction reduction;
160
161 HeapObjectMatcher matcher(input);
162 if (matcher.HasResolvedValue() && matcher.Ref(broker()).IsString()) {
163 reduction = Changed(input); // JSToString(x:string) => x
164 ReplaceWithValue(node, reduction.replacement());
165 return reduction;
166 }
167
168 // TODO(turbofan): This optimization is weaker than what we used to have
169 // in js-typed-lowering for OrderedNumbers. We don't have types here though,
170 // so alternative approach should be designed if this causes performance
171 // regressions and the stronger optimization should be re-implemented.
172 NumberMatcher number_matcher(input);
173 if (number_matcher.HasResolvedValue()) {
174 const StringConstantBase* base = shared_zone()->New<NumberToStringConstant>(
175 number_matcher.ResolvedValue());
176 reduction =
177 Replace(graph()->NewNode(common()->DelayedStringConstant(base)));
178 ReplaceWithValue(node, reduction.replacement());
179 return reduction;
180 }
181
182 return NoChange();
183}
184
185const StringConstantBase*
186JSNativeContextSpecialization::CreateDelayedStringConstant(Node* node) {
187 if (node->opcode() == IrOpcode::kDelayedStringConstant) {
188 return StringConstantBaseOf(node->op());
189 } else {
190 NumberMatcher number_matcher(node);
191 if (number_matcher.HasResolvedValue()) {
192 return shared_zone()->New<NumberToStringConstant>(
193 number_matcher.ResolvedValue());
194 } else {
195 HeapObjectMatcher matcher(node);
196 if (matcher.HasResolvedValue() && matcher.Ref(broker()).IsString()) {
197 StringRef s = matcher.Ref(broker()).AsString();
198 return shared_zone()->New<StringLiteral>(
199 s.object(), static_cast<size_t>(s.length()));
200 } else {
201 UNREACHABLE();
202 }
203 }
204 }
205}
206
207namespace {
208bool IsStringConstant(JSHeapBroker* broker, Node* node) {
209 if (node->opcode() == IrOpcode::kDelayedStringConstant) {
210 return true;
211 }
212
213 HeapObjectMatcher matcher(node);
214 return matcher.HasResolvedValue() && matcher.Ref(broker).IsString();
215}
216} // namespace
217
218Reduction JSNativeContextSpecialization::ReduceJSAsyncFunctionEnter(
219 Node* node) {
220 DCHECK_EQ(IrOpcode::kJSAsyncFunctionEnter, node->opcode());
221 Node* closure = NodeProperties::GetValueInput(node, 0);
222 Node* receiver = NodeProperties::GetValueInput(node, 1);
223 Node* context = NodeProperties::GetContextInput(node);
224 Node* frame_state = NodeProperties::GetFrameStateInput(node);
225 Node* effect = NodeProperties::GetEffectInput(node);
226 Node* control = NodeProperties::GetControlInput(node);
227
228 if (!dependencies()->DependOnPromiseHookProtector()) return NoChange();
229
230 // Create the promise for the async function.
231 Node* promise = effect =
232 graph()->NewNode(javascript()->CreatePromise(), context, effect);
233
234 // Create the JSAsyncFunctionObject based on the SharedFunctionInfo
235 // extracted from the top-most frame in {frame_state}.
236 SharedFunctionInfoRef shared(
237 broker(),
238 FrameStateInfoOf(frame_state->op()).shared_info().ToHandleChecked());
239 DCHECK(shared.is_compiled());
240 int register_count = shared.internal_formal_parameter_count() +
241 shared.GetBytecodeArray().register_count();
242 Node* value = effect =
243 graph()->NewNode(javascript()->CreateAsyncFunctionObject(register_count),
244 closure, receiver, promise, context, effect, control);
245 ReplaceWithValue(node, value, effect, control);
246 return Replace(value);
247}
248
249Reduction JSNativeContextSpecialization::ReduceJSAsyncFunctionReject(
250 Node* node) {
251 DCHECK_EQ(IrOpcode::kJSAsyncFunctionReject, node->opcode());
252 Node* async_function_object = NodeProperties::GetValueInput(node, 0);
253 Node* reason = NodeProperties::GetValueInput(node, 1);
254 Node* context = NodeProperties::GetContextInput(node);
255 Node* frame_state = NodeProperties::GetFrameStateInput(node);
256 Node* effect = NodeProperties::GetEffectInput(node);
257 Node* control = NodeProperties::GetControlInput(node);
258
259 if (!dependencies()->DependOnPromiseHookProtector()) return NoChange();
260
261 // Load the promise from the {async_function_object}.
262 Node* promise = effect = graph()->NewNode(
263 simplified()->LoadField(AccessBuilder::ForJSAsyncFunctionObjectPromise()),
264 async_function_object, effect, control);
265
266 // Create a nested frame state inside the current method's most-recent
267 // {frame_state} that will ensure that lazy deoptimizations at this
268 // point will still return the {promise} instead of the result of the
269 // JSRejectPromise operation (which yields undefined).
270 Node* parameters[] = {promise};
271 frame_state = CreateStubBuiltinContinuationFrameState(
272 jsgraph(), Builtins::kAsyncFunctionLazyDeoptContinuation, context,
273 parameters, arraysize(parameters), frame_state,
274 ContinuationFrameStateMode::LAZY);
275
276 // Disable the additional debug event for the rejection since a
277 // debug event already happend for the exception that got us here.
278 Node* debug_event = jsgraph()->FalseConstant();
279 effect = graph()->NewNode(javascript()->RejectPromise(), promise, reason,
280 debug_event, context, frame_state, effect, control);
281 ReplaceWithValue(node, promise, effect, control);
282 return Replace(promise);
283}
284
285Reduction JSNativeContextSpecialization::ReduceJSAsyncFunctionResolve(
286 Node* node) {
287 DCHECK_EQ(IrOpcode::kJSAsyncFunctionResolve, node->opcode());
288 Node* async_function_object = NodeProperties::GetValueInput(node, 0);
289 Node* value = NodeProperties::GetValueInput(node, 1);
290 Node* context = NodeProperties::GetContextInput(node);
291 Node* frame_state = NodeProperties::GetFrameStateInput(node);
292 Node* effect = NodeProperties::GetEffectInput(node);
293 Node* control = NodeProperties::GetControlInput(node);
294
295 if (!dependencies()->DependOnPromiseHookProtector()) return NoChange();
296
297 // Load the promise from the {async_function_object}.
298 Node* promise = effect = graph()->NewNode(
299 simplified()->LoadField(AccessBuilder::ForJSAsyncFunctionObjectPromise()),
300 async_function_object, effect, control);
301
302 // Create a nested frame state inside the current method's most-recent
303 // {frame_state} that will ensure that lazy deoptimizations at this
304 // point will still return the {promise} instead of the result of the
305 // JSResolvePromise operation (which yields undefined).
306 Node* parameters[] = {promise};
307 frame_state = CreateStubBuiltinContinuationFrameState(
308 jsgraph(), Builtins::kAsyncFunctionLazyDeoptContinuation, context,
309 parameters, arraysize(parameters), frame_state,
310 ContinuationFrameStateMode::LAZY);
311
312 effect = graph()->NewNode(javascript()->ResolvePromise(), promise, value,
313 context, frame_state, effect, control);
314 ReplaceWithValue(node, promise, effect, control);
315 return Replace(promise);
316}
317
318Reduction JSNativeContextSpecialization::ReduceJSAdd(Node* node) {
319 // TODO(turbofan): This has to run together with the inlining and
320 // native context specialization to be able to leverage the string
321 // constant-folding for optimizing property access, but we should
322 // nevertheless find a better home for this at some point.
323 DCHECK_EQ(IrOpcode::kJSAdd, node->opcode());
324
325 Node* const lhs = node->InputAt(0);
326 Node* const rhs = node->InputAt(1);
327
328 base::Optional<size_t> lhs_len = GetMaxStringLength(broker(), lhs);
329 base::Optional<size_t> rhs_len = GetMaxStringLength(broker(), rhs);
330 if (!lhs_len || !rhs_len) {
331 return NoChange();
332 }
333
334 // Fold into DelayedStringConstant if at least one of the parameters is a
335 // string constant and the addition won't throw due to too long result.
336 if (*lhs_len + *rhs_len <= String::kMaxLength &&
337 (IsStringConstant(broker(), lhs) || IsStringConstant(broker(), rhs))) {
338 const StringConstantBase* left = CreateDelayedStringConstant(lhs);
339 const StringConstantBase* right = CreateDelayedStringConstant(rhs);
340 const StringConstantBase* cons =
341 shared_zone()->New<StringCons>(left, right);
342
343 Node* reduced = graph()->NewNode(common()->DelayedStringConstant(cons));
344 ReplaceWithValue(node, reduced);
345 return Replace(reduced);
346 }
347
348 return NoChange();
349}
350
351Reduction JSNativeContextSpecialization::ReduceJSGetSuperConstructor(
352 Node* node) {
353 DCHECK_EQ(IrOpcode::kJSGetSuperConstructor, node->opcode());
354 Node* constructor = NodeProperties::GetValueInput(node, 0);
355
356 // Check if the input is a known JSFunction.
357 HeapObjectMatcher m(constructor);
358 if (!m.HasResolvedValue() || !m.Ref(broker()).IsJSFunction()) {
359 return NoChange();
360 }
361 JSFunctionRef function = m.Ref(broker()).AsJSFunction();
362 MapRef function_map = function.map();
363 if (should_disallow_heap_access() && !function_map.serialized_prototype()) {
364 TRACE_BROKER_MISSING(broker(), "data for map " << function_map);
365 return NoChange();
366 }
367 HeapObjectRef function_prototype = function_map.prototype();
368
369 // We can constant-fold the super constructor access if the
370 // {function}s map is stable, i.e. we can use a code dependency
371 // to guard against [[Prototype]] changes of {function}.
372 if (function_map.is_stable()) {
373 dependencies()->DependOnStableMap(function_map);
374 Node* value = jsgraph()->Constant(function_prototype);
375 ReplaceWithValue(node, value);
376 return Replace(value);
377 }
378
379 return NoChange();
380}
381
382Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
383 JSInstanceOfNode n(node);
384 FeedbackParameter const& p = n.Parameters();
385 Node* object = n.left();
386 Node* constructor = n.right();
387 TNode<Object> context = n.context();
388 FrameState frame_state = n.frame_state();
389 Effect effect = n.effect();
390 Control control = n.control();
391
392 // Check if the right hand side is a known {receiver}, or
393 // we have feedback from the InstanceOfIC.
394 Handle<JSObject> receiver;
395 HeapObjectMatcher m(constructor);
396 if (m.HasResolvedValue() && m.Ref(broker()).IsJSObject()) {
397 receiver = m.Ref(broker()).AsJSObject().object();
398 } else if (p.feedback().IsValid()) {
399 ProcessedFeedback const& feedback =
400 broker()->GetFeedbackForInstanceOf(FeedbackSource(p.feedback()));
401 if (feedback.IsInsufficient()) return NoChange();
402 base::Optional<JSObjectRef> maybe_receiver =
403 feedback.AsInstanceOf().value();
404 if (!maybe_receiver.has_value()) return NoChange();
405 receiver = maybe_receiver->object();
406 } else {
407 return NoChange();
408 }
409
410 JSObjectRef receiver_ref(broker(), receiver);
411 MapRef receiver_map = receiver_ref.map();
412
413 PropertyAccessInfo access_info = PropertyAccessInfo::Invalid(graph()->zone());
414 if (should_disallow_heap_access()) {
415 access_info = broker()->GetPropertyAccessInfo(
416 receiver_map,
417 NameRef(broker(), isolate()->factory()->has_instance_symbol()),
418 AccessMode::kLoad);
419 } else {
420 AccessInfoFactory access_info_factory(broker(), dependencies(),
421 graph()->zone());
422 access_info = access_info_factory.ComputePropertyAccessInfo(
423 receiver_map.object(), factory()->has_instance_symbol(),
424 AccessMode::kLoad);
425 }
426
427 if (access_info.IsInvalid()) return NoChange();
428 access_info.RecordDependencies(dependencies());
429
430 PropertyAccessBuilder access_builder(jsgraph(), broker(), dependencies());
431
432 if (access_info.IsNotFound()) {
433 // If there's no @@hasInstance handler, the OrdinaryHasInstance operation
434 // takes over, but that requires the constructor to be callable.
435 if (!receiver_map.is_callable()) return NoChange();
436
437 dependencies()->DependOnStablePrototypeChains(
438 access_info.lookup_start_object_maps(), kStartAtPrototype);
439
440 // Monomorphic property access.
441 access_builder.BuildCheckMaps(constructor, &effect, control,
442 access_info.lookup_start_object_maps());
443
444 // Lower to OrdinaryHasInstance(C, O).
445 NodeProperties::ReplaceValueInput(node, constructor, 0);
446 NodeProperties::ReplaceValueInput(node, object, 1);
447 NodeProperties::ReplaceEffectInput(node, effect);
448 STATIC_ASSERT(n.FeedbackVectorIndex() == 2);
449 node->RemoveInput(n.FeedbackVectorIndex());
450 NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance());
451 return Changed(node).FollowedBy(ReduceJSOrdinaryHasInstance(node));
452 }
453
454 if (access_info.IsDataConstant()) {
455 Handle<JSObject> holder;
456 bool found_on_proto = access_info.holder().ToHandle(&holder);
457 JSObjectRef holder_ref =
458 found_on_proto ? JSObjectRef(broker(), holder) : receiver_ref;
459 base::Optional<ObjectRef> constant = holder_ref.GetOwnDataProperty(
460 access_info.field_representation(), access_info.field_index());
461 if (!constant.has_value() || !constant->IsHeapObject() ||
462 !constant->AsHeapObject().map().is_callable())
463 return NoChange();
464
465 if (found_on_proto) {
466 dependencies()->DependOnStablePrototypeChains(
467 access_info.lookup_start_object_maps(), kStartAtPrototype,
468 JSObjectRef(broker(), holder));
469 }
470
471 // Check that {constructor} is actually {receiver}.
472 constructor =
473 access_builder.BuildCheckValue(constructor, &effect, control, receiver);
474
475 // Monomorphic property access.
476 access_builder.BuildCheckMaps(constructor, &effect, control,
477 access_info.lookup_start_object_maps());
478
479 // Create a nested frame state inside the current method's most-recent frame
480 // state that will ensure that deopts that happen after this point will not
481 // fallback to the last Checkpoint--which would completely re-execute the
482 // instanceof logic--but rather create an activation of a version of the
483 // ToBoolean stub that finishes the remaining work of instanceof and returns
484 // to the caller without duplicating side-effects upon a lazy deopt.
485 Node* continuation_frame_state = CreateStubBuiltinContinuationFrameState(
486 jsgraph(), Builtins::kToBooleanLazyDeoptContinuation, context, nullptr,
487 0, frame_state, ContinuationFrameStateMode::LAZY);
488
489 // Call the @@hasInstance handler.
490 Node* target = jsgraph()->Constant(*constant);
491 Node* feedback = jsgraph()->UndefinedConstant();
492 // Value inputs plus context, frame state, effect, control.
493 STATIC_ASSERT(JSCallNode::ArityForArgc(1) + 4 == 8);
494 node->EnsureInputCount(graph()->zone(), 8);
495 node->ReplaceInput(JSCallNode::TargetIndex(), target);
496 node->ReplaceInput(JSCallNode::ReceiverIndex(), constructor);
497 node->ReplaceInput(JSCallNode::ArgumentIndex(0), object);
498 node->ReplaceInput(3, feedback);
499 node->ReplaceInput(4, context);
500 node->ReplaceInput(5, continuation_frame_state);
501 node->ReplaceInput(6, effect);
502 node->ReplaceInput(7, control);
503 NodeProperties::ChangeOp(
504 node, javascript()->Call(JSCallNode::ArityForArgc(1), CallFrequency(),
505 FeedbackSource(),
506 ConvertReceiverMode::kNotNullOrUndefined));
507
508 // Rewire the value uses of {node} to ToBoolean conversion of the result.
509 Node* value = graph()->NewNode(simplified()->ToBoolean(), node);
510 for (Edge edge : node->use_edges()) {
511 if (NodeProperties::IsValueEdge(edge) && edge.from() != value) {
512 edge.UpdateTo(value);
513 Revisit(edge.from());
514 }
515 }
516 return Changed(node);
517 }
518
519 return NoChange();
520}
521
522JSNativeContextSpecialization::InferHasInPrototypeChainResult
523JSNativeContextSpecialization::InferHasInPrototypeChain(
524 Node* receiver, Node* effect, HeapObjectRef const& prototype) {
525 ZoneHandleSet<Map> receiver_maps;
526 NodeProperties::InferMapsResult result = NodeProperties::InferMapsUnsafe(
527 broker(), receiver, effect, &receiver_maps);
528 if (result == NodeProperties::kNoMaps) return kMayBeInPrototypeChain;
529
530 // Try to determine either that all of the {receiver_maps} have the given
531 // {prototype} in their chain, or that none do. If we can't tell, return
532 // kMayBeInPrototypeChain.
533 bool all = true;
534 bool none = true;
535 for (size_t i = 0; i < receiver_maps.size(); ++i) {
536 MapRef map(broker(), receiver_maps[i]);
537 if (result == NodeProperties::kUnreliableMaps && !map.is_stable()) {
538 return kMayBeInPrototypeChain;
539 }
540 while (true) {
541 if (IsSpecialReceiverInstanceType(map.instance_type())) {
542 return kMayBeInPrototypeChain;
543 }
544 if (!map.IsJSObjectMap()) {
545 all = false;
546 break;
547 }
548 if (should_disallow_heap_access() && !map.serialized_prototype()) {
549 TRACE_BROKER_MISSING(broker(), "prototype data for map " << map);
550 return kMayBeInPrototypeChain;
551 }
552 if (map.prototype().equals(prototype)) {
553 none = false;
554 break;
555 }
556 map = map.prototype().map();
557 if (!map.is_stable()) return kMayBeInPrototypeChain;
558 if (map.oddball_type() == OddballType::kNull) {
559 all = false;
560 break;
561 }
562 }
563 }
564 DCHECK_IMPLIES(all, !none);
565 if (!all && !none) return kMayBeInPrototypeChain;
566
567 {
568 base::Optional<JSObjectRef> last_prototype;
569 if (all) {
570 // We don't need to protect the full chain if we found the prototype, we
571 // can stop at {prototype}. In fact we could stop at the one before
572 // {prototype} but since we're dealing with multiple receiver maps this
573 // might be a different object each time, so it's much simpler to include
574 // {prototype}. That does, however, mean that we must check {prototype}'s
575 // map stability.
576 if (!prototype.map().is_stable()) return kMayBeInPrototypeChain;
577 last_prototype = prototype.AsJSObject();
578 }
579 WhereToStart start = result == NodeProperties::kUnreliableMaps
580 ? kStartAtReceiver
581 : kStartAtPrototype;
582 dependencies()->DependOnStablePrototypeChains(receiver_maps, start,
583 last_prototype);
584 }
585
586 DCHECK_EQ(all, !none);
587 return all ? kIsInPrototypeChain : kIsNotInPrototypeChain;
588}
589
590Reduction JSNativeContextSpecialization::ReduceJSHasInPrototypeChain(
591 Node* node) {
592 DCHECK_EQ(IrOpcode::kJSHasInPrototypeChain, node->opcode());
593 Node* value = NodeProperties::GetValueInput(node, 0);
594 Node* prototype = NodeProperties::GetValueInput(node, 1);
595 Node* effect = NodeProperties::GetEffectInput(node);
596
597 // Check if we can constant-fold the prototype chain walk
598 // for the given {value} and the {prototype}.
599 HeapObjectMatcher m(prototype);
600 if (m.HasResolvedValue()) {
601 InferHasInPrototypeChainResult result =
602 InferHasInPrototypeChain(value, effect, m.Ref(broker()));
603 if (result != kMayBeInPrototypeChain) {
604 Node* value = jsgraph()->BooleanConstant(result == kIsInPrototypeChain);
605 ReplaceWithValue(node, value);
606 return Replace(value);
607 }
608 }
609
610 return NoChange();
611}
612
613Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance(
614 Node* node) {
615 DCHECK_EQ(IrOpcode::kJSOrdinaryHasInstance, node->opcode());
616 Node* constructor = NodeProperties::GetValueInput(node, 0);
617 Node* object = NodeProperties::GetValueInput(node, 1);
618
619 // Check if the {constructor} is known at compile time.
620 HeapObjectMatcher m(constructor);
621 if (!m.HasResolvedValue()) return NoChange();
622
623 if (m.Ref(broker()).IsJSBoundFunction()) {
624 // OrdinaryHasInstance on bound functions turns into a recursive invocation
625 // of the instanceof operator again.
626 JSBoundFunctionRef function = m.Ref(broker()).AsJSBoundFunction();
627 if (should_disallow_heap_access() && !function.serialized()) {
628 TRACE_BROKER_MISSING(broker(), "data for JSBoundFunction " << function);
629 return NoChange();
630 }
631
632 JSReceiverRef bound_target_function = function.bound_target_function();
633
634 Node* feedback = jsgraph()->UndefinedConstant();
635 NodeProperties::ReplaceValueInput(node, object,
636 JSInstanceOfNode::LeftIndex());
637 NodeProperties::ReplaceValueInput(
638 node, jsgraph()->Constant(bound_target_function),
639 JSInstanceOfNode::RightIndex());
640 node->InsertInput(zone(), JSInstanceOfNode::FeedbackVectorIndex(),
641 feedback);
642 NodeProperties::ChangeOp(node, javascript()->InstanceOf(FeedbackSource()));
643 return Changed(node).FollowedBy(ReduceJSInstanceOf(node));
644 }
645
646 if (m.Ref(broker()).IsJSFunction()) {
647 // Optimize if we currently know the "prototype" property.
648
649 JSFunctionRef function = m.Ref(broker()).AsJSFunction();
650 if (should_disallow_heap_access() && !function.serialized()) {
651 TRACE_BROKER_MISSING(broker(), "data for JSFunction " << function);
652 return NoChange();
653 }
654
655 // TODO(neis): Remove the has_prototype_slot condition once the broker is
656 // always enabled.
657 if (!function.map().has_prototype_slot() || !function.has_prototype() ||
658 function.PrototypeRequiresRuntimeLookup()) {
659 return NoChange();
660 }
661
662 ObjectRef prototype = dependencies()->DependOnPrototypeProperty(function);
663 Node* prototype_constant = jsgraph()->Constant(prototype);
664
665 // Lower the {node} to JSHasInPrototypeChain.
666 NodeProperties::ReplaceValueInput(node, object, 0);
667 NodeProperties::ReplaceValueInput(node, prototype_constant, 1);
668 NodeProperties::ChangeOp(node, javascript()->HasInPrototypeChain());
669 return Changed(node).FollowedBy(ReduceJSHasInPrototypeChain(node));
670 }
671
672 return NoChange();
673}
674
675// ES section #sec-promise-resolve
676Reduction JSNativeContextSpecialization::ReduceJSPromiseResolve(Node* node) {
677 DCHECK_EQ(IrOpcode::kJSPromiseResolve, node->opcode());
678 Node* constructor = NodeProperties::GetValueInput(node, 0);
679 Node* value = NodeProperties::GetValueInput(node, 1);
680 Node* context = NodeProperties::GetContextInput(node);
681 Node* frame_state = NodeProperties::GetFrameStateInput(node);
682 Node* effect = NodeProperties::GetEffectInput(node);
683 Node* control = NodeProperties::GetControlInput(node);
684
685 // Check if the {constructor} is the %Promise% function.
686 HeapObjectMatcher m(constructor);
687 if (!m.HasResolvedValue() ||
688 !m.Ref(broker()).equals(native_context().promise_function())) {
689 return NoChange();
690 }
691
692 // Only optimize if {value} cannot be a JSPromise.
693 MapInference inference(broker(), value, effect);
694 if (!inference.HaveMaps() ||
695 inference.AnyOfInstanceTypesAre(JS_PROMISE_TYPE)) {
696 return NoChange();
697 }
698
699 if (!dependencies()->DependOnPromiseHookProtector()) return NoChange();
700
701 // Create a %Promise% instance and resolve it with {value}.
702 Node* promise = effect =
703 graph()->NewNode(javascript()->CreatePromise(), context, effect);
704 effect = graph()->NewNode(javascript()->ResolvePromise(), promise, value,
705 context, frame_state, effect, control);
706 ReplaceWithValue(node, promise, effect, control);
707 return Replace(promise);
708}
709
710// ES section #sec-promise-resolve-functions
711Reduction JSNativeContextSpecialization::ReduceJSResolvePromise(Node* node) {
712 DCHECK_EQ(IrOpcode::kJSResolvePromise, node->opcode());
713 Node* promise = NodeProperties::GetValueInput(node, 0);
714 Node* resolution = NodeProperties::GetValueInput(node, 1);
715 Node* context = NodeProperties::GetContextInput(node);
716 Node* effect = NodeProperties::GetEffectInput(node);
717 Node* control = NodeProperties::GetControlInput(node);
718
719 // Check if we know something about the {resolution}.
720 MapInference inference(broker(), resolution, effect);
721 if (!inference.HaveMaps()) return NoChange();
722 MapHandles const& resolution_maps = inference.GetMaps();
723
724 // Compute property access info for "then" on {resolution}.
725 ZoneVector<PropertyAccessInfo> access_infos(graph()->zone());
726 AccessInfoFactory access_info_factory(broker(), dependencies(),
727 graph()->zone());
728 if (!should_disallow_heap_access()) {
729 access_info_factory.ComputePropertyAccessInfos(
730 resolution_maps, factory()->then_string(), AccessMode::kLoad,
731 &access_infos);
732 } else {
733 // Obtain pre-computed access infos from the broker.
734 for (auto map : resolution_maps) {
735 MapRef map_ref(broker(), map);
736 access_infos.push_back(broker()->GetPropertyAccessInfo(
737 map_ref, NameRef(broker(), isolate()->factory()->then_string()),
738 AccessMode::kLoad));
739 }
740 }
741 PropertyAccessInfo access_info =
742 access_info_factory.FinalizePropertyAccessInfosAsOne(access_infos,
743 AccessMode::kLoad);
744 if (access_info.IsInvalid()) return inference.NoChange();
745
746 // Only optimize when {resolution} definitely doesn't have a "then" property.
747 if (!access_info.IsNotFound()) return inference.NoChange();
748
749 if (!inference.RelyOnMapsViaStability(dependencies())) {
750 return inference.NoChange();
751 }
752
753 dependencies()->DependOnStablePrototypeChains(
754 access_info.lookup_start_object_maps(), kStartAtPrototype);
755
756 // Simply fulfill the {promise} with the {resolution}.
757 Node* value = effect =
758 graph()->NewNode(javascript()->FulfillPromise(), promise, resolution,
759 context, effect, control);
760 ReplaceWithValue(node, value, effect, control);
761 return Replace(value);
762}
763
764namespace {
765
766FieldAccess ForPropertyCellValue(MachineRepresentation representation,
767 Type type, MaybeHandle<Map> map,
768 NameRef const& name) {
769 WriteBarrierKind kind = kFullWriteBarrier;
770 if (representation == MachineRepresentation::kTaggedSigned) {
771 kind = kNoWriteBarrier;
772 } else if (representation == MachineRepresentation::kTaggedPointer) {
773 kind = kPointerWriteBarrier;
774 }
775 MachineType r = MachineType::TypeForRepresentation(representation);
776 FieldAccess access = {
777 kTaggedBase, PropertyCell::kValueOffset, name.object(), map, type, r,
778 kind};
779 return access;
780}
781
782} // namespace
783
784Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
785 Node* node, Node* lookup_start_object, Node* receiver, Node* value,
786 NameRef const& name, AccessMode access_mode, Node* key, Node* effect) {
787 base::Optional<PropertyCellRef> cell =
788 native_context().global_object().GetPropertyCell(name);
789 return cell.has_value()
790 ? ReduceGlobalAccess(node, lookup_start_object, receiver, value,
791 name, access_mode, key, *cell, effect)
792 : NoChange();
793}
794
795// TODO(neis): Try to merge this with ReduceNamedAccess by introducing a new
796// PropertyAccessInfo kind for global accesses and using the existing mechanism
797// for building loads/stores.
798// Note: The "receiver" parameter is only used for DCHECKS, but that's on
799// purpose. This way we can assert the super property access cases won't hit the
800// code which hasn't been modified to support super property access.
801Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
802 Node* node, Node* lookup_start_object, Node* receiver, Node* value,
803 NameRef const& name, AccessMode access_mode, Node* key,
804 PropertyCellRef const& property_cell, Node* effect) {
805 Node* control = NodeProperties::GetControlInput(node);
806 if (effect == nullptr) {
807 effect = NodeProperties::GetEffectInput(node);
808 }
809
810 ObjectRef property_cell_value = property_cell.value();
811 if (property_cell_value.IsHeapObject() &&
812 property_cell_value.AsHeapObject().map().oddball_type() ==
813 OddballType::kHole) {
814 // The property cell is no longer valid.
815 return NoChange();
816 }
817
818 PropertyDetails property_details = property_cell.property_details();
819 PropertyCellType property_cell_type = property_details.cell_type();
820 DCHECK_EQ(kData, property_details.kind());
821
822 // We have additional constraints for stores.
823 if (access_mode == AccessMode::kStore) {
824 DCHECK_EQ(receiver, lookup_start_object);
825 if (property_details.IsReadOnly()) {
826 // Don't even bother trying to lower stores to read-only data properties.
827 return NoChange();
828 } else if (property_cell_type == PropertyCellType::kUndefined) {
829 // There's no fast-path for dealing with undefined property cells.
830 return NoChange();
831 } else if (property_cell_type == PropertyCellType::kConstantType) {
832 // There's also no fast-path to store to a global cell which pretended
833 // to be stable, but is no longer stable now.
834 if (property_cell_value.IsHeapObject() &&
835 !property_cell_value.AsHeapObject().map().is_stable()) {
836 return NoChange();
837 }
838 }
839 } else if (access_mode == AccessMode::kHas) {
840 DCHECK_EQ(receiver, lookup_start_object);
841 // has checks cannot follow the fast-path used by loads when these
842 // conditions hold.
843 if ((property_details.IsConfigurable() || !property_details.IsReadOnly()) &&
844 property_details.cell_type() != PropertyCellType::kConstant &&
845 property_details.cell_type() != PropertyCellType::kUndefined)
846 return NoChange();
847 }
848
849 // Ensure that {key} matches the specified {name} (if {key} is given).
850 if (key != nullptr) {
851 effect = BuildCheckEqualsName(name, key, effect, control);
852 }
853
854 // If we have a {lookup_start_object} to validate, we do so by checking that
855 // its map is the (target) global proxy's map. This guarantees that in fact
856 // the lookup start object is the global proxy.
857 if (lookup_start_object != nullptr) {
858 effect = graph()->NewNode(
859 simplified()->CheckMaps(
860 CheckMapsFlag::kNone,
861 ZoneHandleSet<Map>(
862 HeapObjectRef(broker(), global_proxy()).map().object())),
863 lookup_start_object, effect, control);
864 }
865
866 if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
867 // Load from non-configurable, read-only data property on the global
868 // object can be constant-folded, even without deoptimization support.
869 if (!property_details.IsConfigurable() && property_details.IsReadOnly()) {
870 value = access_mode == AccessMode::kHas
871 ? jsgraph()->TrueConstant()
872 : jsgraph()->Constant(property_cell_value);
873 } else {
874 // Record a code dependency on the cell if we can benefit from the
875 // additional feedback, or the global property is configurable (i.e.
876 // can be deleted or reconfigured to an accessor property).
877 if (property_details.cell_type() != PropertyCellType::kMutable ||
878 property_details.IsConfigurable()) {
879 dependencies()->DependOnGlobalProperty(property_cell);
880 }
881
882 // Load from constant/undefined global property can be constant-folded.
883 if (property_details.cell_type() == PropertyCellType::kConstant ||
884 property_details.cell_type() == PropertyCellType::kUndefined) {
885 value = access_mode == AccessMode::kHas
886 ? jsgraph()->TrueConstant()
887 : jsgraph()->Constant(property_cell_value);
888 DCHECK(!property_cell_value.IsHeapObject() ||
889 property_cell_value.AsHeapObject().map().oddball_type() !=
890 OddballType::kHole);
891 } else {
892 DCHECK_NE(AccessMode::kHas, access_mode);
893
894 // Load from constant type cell can benefit from type feedback.
895 MaybeHandle<Map> map;
896 Type property_cell_value_type = Type::NonInternal();
897 MachineRepresentation representation = MachineRepresentation::kTagged;
898 if (property_details.cell_type() == PropertyCellType::kConstantType) {
899 // Compute proper type based on the current value in the cell.
900 if (property_cell_value.IsSmi()) {
901 property_cell_value_type = Type::SignedSmall();
902 representation = MachineRepresentation::kTaggedSigned;
903 } else if (property_cell_value.IsHeapNumber()) {
904 property_cell_value_type = Type::Number();
905 representation = MachineRepresentation::kTaggedPointer;
906 } else {
907 MapRef property_cell_value_map =
908 property_cell_value.AsHeapObject().map();
909 property_cell_value_type = Type::For(property_cell_value_map);
910 representation = MachineRepresentation::kTaggedPointer;
911
912 // We can only use the property cell value map for map check
913 // elimination if it's stable, i.e. the HeapObject wasn't
914 // mutated without the cell state being updated.
915 if (property_cell_value_map.is_stable()) {
916 dependencies()->DependOnStableMap(property_cell_value_map);
917 map = property_cell_value_map.object();
918 }
919 }
920 }
921 value = effect = graph()->NewNode(
922 simplified()->LoadField(ForPropertyCellValue(
923 representation, property_cell_value_type, map, name)),
924 jsgraph()->Constant(property_cell), effect, control);
925 }
926 }
927 } else {
928 DCHECK_EQ(AccessMode::kStore, access_mode);
929 DCHECK_EQ(receiver, lookup_start_object);
930 DCHECK(!property_details.IsReadOnly());
931 switch (property_details.cell_type()) {
932 case PropertyCellType::kUndefined: {
933 UNREACHABLE();
934 break;
935 }
936 case PropertyCellType::kConstant: {
937 // Record a code dependency on the cell, and just deoptimize if the new
938 // value doesn't match the previous value stored inside the cell.
939 dependencies()->DependOnGlobalProperty(property_cell);
940 Node* check =
941 graph()->NewNode(simplified()->ReferenceEqual(), value,
942 jsgraph()->Constant(property_cell_value));
943 effect = graph()->NewNode(
944 simplified()->CheckIf(DeoptimizeReason::kValueMismatch), check,
945 effect, control);
946 break;
947 }
948 case PropertyCellType::kConstantType: {
949 // Record a code dependency on the cell, and just deoptimize if the new
950 // values' type doesn't match the type of the previous value in the
951 // cell.
952 dependencies()->DependOnGlobalProperty(property_cell);
953 Type property_cell_value_type;
954 MachineRepresentation representation = MachineRepresentation::kTagged;
955 if (property_cell_value.IsHeapObject()) {
956 // We cannot do anything if the {property_cell_value}s map is no
957 // longer stable.
958 MapRef property_cell_value_map =
959 property_cell_value.AsHeapObject().map();
960 dependencies()->DependOnStableMap(property_cell_value_map);
961
962 // Check that the {value} is a HeapObject.
963 value = effect = graph()->NewNode(simplified()->CheckHeapObject(),
964 value, effect, control);
965
966 // Check {value} map against the {property_cell} map.
967 effect = graph()->NewNode(
968 simplified()->CheckMaps(
969 CheckMapsFlag::kNone,
970 ZoneHandleSet<Map>(property_cell_value_map.object())),
971 value, effect, control);
972 property_cell_value_type = Type::OtherInternal();
973 representation = MachineRepresentation::kTaggedPointer;
974 } else {
975 // Check that the {value} is a Smi.
976 value = effect = graph()->NewNode(
977 simplified()->CheckSmi(FeedbackSource()), value, effect, control);
978 property_cell_value_type = Type::SignedSmall();
979 representation = MachineRepresentation::kTaggedSigned;
980 }
981 effect = graph()->NewNode(simplified()->StoreField(ForPropertyCellValue(
982 representation, property_cell_value_type,
983 MaybeHandle<Map>(), name)),
984 jsgraph()->Constant(property_cell), value,
985 effect, control);
986 break;
987 }
988 case PropertyCellType::kMutable: {
989 // Record a code dependency on the cell, and just deoptimize if the
990 // property ever becomes read-only.
991 dependencies()->DependOnGlobalProperty(property_cell);
992 effect = graph()->NewNode(
993 simplified()->StoreField(ForPropertyCellValue(
994 MachineRepresentation::kTagged, Type::NonInternal(),
995 MaybeHandle<Map>(), name)),
996 jsgraph()->Constant(property_cell), value, effect, control);
997 break;
998 }
999 }
1000 }
1001
1002 ReplaceWithValue(node, value, effect, control);
1003 return Replace(value);
1004}
1005
1006Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) {
1007 JSLoadGlobalNode n(node);
1008 LoadGlobalParameters const& p = n.Parameters();
1009 if (!p.feedback().IsValid()) return NoChange();
1010
1011 ProcessedFeedback const& processed =
1012 broker()->GetFeedbackForGlobalAccess(FeedbackSource(p.feedback()));
1013 if (processed.IsInsufficient()) return NoChange();
1014
1015 GlobalAccessFeedback const& feedback = processed.AsGlobalAccess();
1016 if (feedback.IsScriptContextSlot()) {
1017 Effect effect = n.effect();
1018 Node* script_context = jsgraph()->Constant(feedback.script_context());
1019 Node* value = effect =
1020 graph()->NewNode(javascript()->LoadContext(0, feedback.slot_index(),
1021 feedback.immutable()),
1022 script_context, effect);
1023 ReplaceWithValue(node, value, effect);
1024 return Replace(value);
1025 } else if (feedback.IsPropertyCell()) {
1026 return ReduceGlobalAccess(node, nullptr, nullptr, nullptr,
1027 NameRef(broker(), p.name()), AccessMode::kLoad,
1028 nullptr, feedback.property_cell());
1029 } else {
1030 DCHECK(feedback.IsMegamorphic());
1031 return NoChange();
1032 }
1033}
1034
1035Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) {
1036 JSStoreGlobalNode n(node);
1037 StoreGlobalParameters const& p = n.Parameters();
1038 Node* value = n.value();
1039 if (!p.feedback().IsValid()) return NoChange();
1040
1041 ProcessedFeedback const& processed =
1042 broker()->GetFeedbackForGlobalAccess(FeedbackSource(p.feedback()));
1043 if (processed.IsInsufficient()) return NoChange();
1044
1045 GlobalAccessFeedback const& feedback = processed.AsGlobalAccess();
1046 if (feedback.IsScriptContextSlot()) {
1047 if (feedback.immutable()) return NoChange();
1048 Effect effect = n.effect();
1049 Control control = n.control();
1050 Node* script_context = jsgraph()->Constant(feedback.script_context());
1051 effect =
1052 graph()->NewNode(javascript()->StoreContext(0, feedback.slot_index()),
1053 value, script_context, effect, control);
1054 ReplaceWithValue(node, value, effect, control);
1055 return Replace(value);
1056 } else if (feedback.IsPropertyCell()) {
1057 return ReduceGlobalAccess(node, nullptr, nullptr, value,
1058 NameRef(broker(), p.name()), AccessMode::kStore,
1059 nullptr, feedback.property_cell());
1060 } else {
1061 DCHECK(feedback.IsMegamorphic());
1062 return NoChange();
1063 }
1064}
1065
1066Reduction JSNativeContextSpecialization::ReduceMinimorphicPropertyAccess(
1067 Node* node, Node* value,
1068 MinimorphicLoadPropertyAccessFeedback const& feedback,
1069 FeedbackSource const& source) {
1070 DCHECK(node->opcode() == IrOpcode::kJSLoadNamed ||
1071 node->opcode() == IrOpcode::kJSLoadProperty ||
1072 node->opcode() == IrOpcode::kJSLoadNamedFromSuper);
1073 STATIC_ASSERT(JSLoadNamedNode::ObjectIndex() == 0 &&
1074 JSLoadPropertyNode::ObjectIndex() == 0);
1075
1076 Node* effect = NodeProperties::GetEffectInput(node);
1077 Node* control = NodeProperties::GetControlInput(node);
1078
1079 Node* lookup_start_object;
1080 if (node->opcode() == IrOpcode::kJSLoadNamedFromSuper) {
1081 DCHECK(FLAG_super_ic);
1082 JSLoadNamedFromSuperNode n(node);
1083 // Lookup start object is the __proto__ of the home object.
1084 lookup_start_object = effect =
1085 BuildLoadPrototypeFromObject(n.home_object(), effect, control);
1086 } else {
1087 lookup_start_object = NodeProperties::GetValueInput(node, 0);
1088 }
1089
1090 MinimorphicLoadPropertyAccessInfo access_info =
1091 broker()->GetPropertyAccessInfo(
1092 feedback, source,
1093 should_disallow_heap_access()
1094 ? SerializationPolicy::kAssumeSerialized
1095 : SerializationPolicy::kSerializeIfNeeded);
1096 if (access_info.IsInvalid()) return NoChange();
1097
1098 // The dynamic map check operator loads the feedback vector from the
1099 // function's frame, so we can only use this for non-inlined functions.
1100 // TODO(rmcilroy): Add support for using a trampoline like LoadICTrampoline
1101 // and otherwise pass feedback vector explicitly if we need support for
1102 // inlined functions.
1103 // TODO(rmcilroy): Ideally we would check whether we are have an inlined frame
1104 // state here, but there isn't a good way to distinguish inlined from OSR
1105 // framestates.
1106 DCHECK(broker()->is_turboprop());
1107
1108 PropertyAccessBuilder access_builder(jsgraph(), broker(), nullptr);
1109 CheckMapsFlags flags = CheckMapsFlag::kNone;
1110 if (feedback.has_migration_target_maps()) {
1111 flags |= CheckMapsFlag::kTryMigrateInstance;
1112 }
1113
1114 ZoneHandleSet<Map> maps;
1115 for (Handle<Map> map : feedback.maps()) {
1116 maps.insert(map, graph()->zone());
1117 }
1118
1119 effect = graph()->NewNode(
1120 simplified()->DynamicCheckMaps(flags, feedback.handler(), maps, source),
1121 lookup_start_object, effect, control);
1122 value = access_builder.BuildMinimorphicLoadDataField(
1123 feedback.name(), access_info, lookup_start_object, &effect, &control);
1124
1125 ReplaceWithValue(node, value, effect, control);
1126 return Replace(value);
1127}
1128
1129Reduction JSNativeContextSpecialization::ReduceNamedAccess(
1130 Node* node, Node* value, NamedAccessFeedback const& feedback,
1131 AccessMode access_mode, Node* key) {
1132 DCHECK(node->opcode() == IrOpcode::kJSLoadNamed ||
1133 node->opcode() == IrOpcode::kJSStoreNamed ||
1134 node->opcode() == IrOpcode::kJSLoadProperty ||
1135 node->opcode() == IrOpcode::kJSStoreProperty ||
1136 node->opcode() == IrOpcode::kJSStoreNamedOwn ||
1137 node->opcode() == IrOpcode::kJSStoreDataPropertyInLiteral ||
1138 node->opcode() == IrOpcode::kJSHasProperty ||
1139 node->opcode() == IrOpcode::kJSLoadNamedFromSuper);
1140 STATIC_ASSERT(JSLoadNamedNode::ObjectIndex() == 0 &&
1141 JSStoreNamedNode::ObjectIndex() == 0 &&
1142 JSLoadPropertyNode::ObjectIndex() == 0 &&
1143 JSStorePropertyNode::ObjectIndex() == 0 &&
1144 JSStoreNamedOwnNode::ObjectIndex() == 0 &&
1145 JSStoreNamedNode::ObjectIndex() == 0 &&
1146 JSStoreDataPropertyInLiteralNode::ObjectIndex() == 0 &&
1147 JSHasPropertyNode::ObjectIndex() == 0);
1148 STATIC_ASSERT(JSLoadNamedFromSuperNode::ReceiverIndex() == 0);
1149
1150 Node* context = NodeProperties::GetContextInput(node);
1151 Node* frame_state = NodeProperties::GetFrameStateInput(node);
1152 Node* effect = NodeProperties::GetEffectInput(node);
1153 Node* control = NodeProperties::GetControlInput(node);
1154
1155 // receiver = the object we pass to the accessor (if any) as the "this" value.
1156 Node* receiver = NodeProperties::GetValueInput(node, 0);
1157 // lookup_start_object = the object where we start looking for the property.
1158 Node* lookup_start_object;
1159 if (node->opcode() == IrOpcode::kJSLoadNamedFromSuper) {
1160 DCHECK(FLAG_super_ic);
1161 JSLoadNamedFromSuperNode n(node);
1162 // Lookup start object is the __proto__ of the home object.
1163 lookup_start_object = effect =
1164 BuildLoadPrototypeFromObject(n.home_object(), effect, control);
1165 } else {
1166 lookup_start_object = receiver;
1167 }
1168
1169 // Either infer maps from the graph or use the feedback.
1170 ZoneVector<Handle<Map>> lookup_start_object_maps(zone());
1171 if (!InferMaps(lookup_start_object, effect, &lookup_start_object_maps)) {
1172 lookup_start_object_maps = feedback.maps();
1173 }
1174 RemoveImpossibleMaps(lookup_start_object, &lookup_start_object_maps);
1175
1176 // Check if we have an access o.x or o.x=v where o is the target native
1177 // contexts' global proxy, and turn that into a direct access to the
1178 // corresponding global object instead.
1179 if (lookup_start_object_maps.size() == 1) {
1180 MapRef lookup_start_object_map(broker(), lookup_start_object_maps[0]);
1181 if (lookup_start_object_map.equals(
1182 broker()->target_native_context().global_proxy_object().map()) &&
1183 !broker()->target_native_context().global_object().IsDetached()) {
1184 return ReduceGlobalAccess(node, lookup_start_object, receiver, value,
1185 feedback.name(), access_mode, key, effect);
1186 }
1187 }
1188
1189 ZoneVector<PropertyAccessInfo> access_infos(zone());
1190 {
1191 ZoneVector<PropertyAccessInfo> access_infos_for_feedback(zone());
1192 for (Handle<Map> map_handle : lookup_start_object_maps) {
1193 MapRef map(broker(), map_handle);
1194 if (map.is_deprecated()) continue;
1195 PropertyAccessInfo access_info = broker()->GetPropertyAccessInfo(
1196 map, feedback.name(), access_mode, dependencies(),
1197 should_disallow_heap_access()
1198 ? SerializationPolicy::kAssumeSerialized
1199 : SerializationPolicy::kSerializeIfNeeded);
1200 access_infos_for_feedback.push_back(access_info);
1201 }
1202
1203 AccessInfoFactory access_info_factory(broker(), dependencies(),
1204 graph()->zone());
1205 if (!access_info_factory.FinalizePropertyAccessInfos(
1206 access_infos_for_feedback, access_mode, &access_infos)) {
1207 return NoChange();
1208 }
1209 }
1210
1211 // Ensure that {key} matches the specified name (if {key} is given).
1212 if (key != nullptr) {
1213 effect = BuildCheckEqualsName(feedback.name(), key, effect, control);
1214 }
1215
1216 // Collect call nodes to rewire exception edges.
1217 ZoneVector<Node*> if_exception_nodes(zone());
1218 ZoneVector<Node*>* if_exceptions = nullptr;
1219 Node* if_exception = nullptr;
1220 if (NodeProperties::IsExceptionalCall(node, &if_exception)) {
1221 if_exceptions = &if_exception_nodes;
1222 }
1223
1224 PropertyAccessBuilder access_builder(jsgraph(), broker(), dependencies());
1225
1226 // Check for the monomorphic cases.
1227 if (access_infos.size() == 1) {
1228 PropertyAccessInfo access_info = access_infos.front();
1229 if (receiver != lookup_start_object) {
1230 // Super property access. lookup_start_object is a JSReceiver or
1231 // null. It can't be a number, a string etc. So trying to build the
1232 // checks in the "else if" branch doesn't make sense.
1233 access_builder.BuildCheckMaps(lookup_start_object, &effect, control,
1234 access_info.lookup_start_object_maps());
1235
1236 } else if (!access_builder.TryBuildStringCheck(
1237 broker(), access_info.lookup_start_object_maps(), &receiver,
1238 &effect, control) &&
1239 !access_builder.TryBuildNumberCheck(
1240 broker(), access_info.lookup_start_object_maps(), &receiver,
1241 &effect, control)) {
1242 // Try to build string check or number check if possible. Otherwise build
1243 // a map check.
1244
1245 // TryBuildStringCheck and TryBuildNumberCheck don't update the receiver
1246 // if they fail.
1247 DCHECK_EQ(receiver, lookup_start_object);
1248 if (HasNumberMaps(broker(), access_info.lookup_start_object_maps())) {
1249 // We need to also let Smi {receiver}s through in this case, so
1250 // we construct a diamond, guarded by the Sminess of the {receiver}
1251 // and if {receiver} is not a Smi just emit a sequence of map checks.
1252 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
1253 Node* branch = graph()->NewNode(common()->Branch(), check, control);
1254
1255 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
1256 Node* etrue = effect;
1257
1258 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
1259 Node* efalse = effect;
1260 {
1261 access_builder.BuildCheckMaps(receiver, &efalse, if_false,
1262 access_info.lookup_start_object_maps());
1263 }
1264
1265 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
1266 effect =
1267 graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
1268 } else {
1269 access_builder.BuildCheckMaps(receiver, &effect, control,
1270 access_info.lookup_start_object_maps());
1271 }
1272 } else {
1273 // At least one of TryBuildStringCheck & TryBuildNumberCheck succeeded
1274 // and updated the receiver. Update lookup_start_object to match (they
1275 // should be the same).
1276 lookup_start_object = receiver;
1277 }
1278
1279 // Generate the actual property access.
1280 ValueEffectControl continuation = BuildPropertyAccess(
1281 lookup_start_object, receiver, value, context, frame_state, effect,
1282 control, feedback.name(), if_exceptions, access_info, access_mode);
1283 value = continuation.value();
1284 effect = continuation.effect();
1285 control = continuation.control();
1286 } else {
1287 // The final states for every polymorphic branch. We join them with
1288 // Merge+Phi+EffectPhi at the bottom.
1289 ZoneVector<Node*> values(zone());
1290 ZoneVector<Node*> effects(zone());
1291 ZoneVector<Node*> controls(zone());
1292
1293 Node* receiverissmi_control = nullptr;
1294 Node* receiverissmi_effect = effect;
1295
1296 if (receiver == lookup_start_object) {
1297 // Check if {receiver} may be a number.
1298 bool receiverissmi_possible = false;
1299 for (PropertyAccessInfo const& access_info : access_infos) {
1300 if (HasNumberMaps(broker(), access_info.lookup_start_object_maps())) {
1301 receiverissmi_possible = true;
1302 break;
1303 }
1304 }
1305
1306 // Handle the case that {receiver} may be a number.
1307 if (receiverissmi_possible) {
1308 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
1309 Node* branch = graph()->NewNode(common()->Branch(), check, control);
1310 control = graph()->NewNode(common()->IfFalse(), branch);
1311 receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch);
1312 receiverissmi_effect = effect;
1313 }
1314 }
1315
1316 // Generate code for the various different property access patterns.
1317 Node* fallthrough_control = control;
1318 for (size_t j = 0; j < access_infos.size(); ++j) {
1319 PropertyAccessInfo const& access_info = access_infos[j];
1320 Node* this_value = value;
1321 Node* this_lookup_start_object = lookup_start_object;
1322 Node* this_receiver = receiver;
1323 Node* this_effect = effect;
1324 Node* this_control = fallthrough_control;
1325
1326 // Perform map check on {lookup_start_object}.
1327 ZoneVector<Handle<Map>> const& lookup_start_object_maps =
1328 access_info.lookup_start_object_maps();
1329 {
1330 // Whether to insert a dedicated MapGuard node into the
1331 // effect to be able to learn from the control flow.
1332 bool insert_map_guard = true;
1333
1334 // Check maps for the {lookup_start_object}s.
1335 if (j == access_infos.size() - 1) {
1336 // Last map check on the fallthrough control path, do a
1337 // conditional eager deoptimization exit here.
1338 access_builder.BuildCheckMaps(lookup_start_object, &this_effect,
1339 this_control, lookup_start_object_maps);
1340 fallthrough_control = nullptr;
1341
1342 // Don't insert a MapGuard in this case, as the CheckMaps
1343 // node already gives you all the information you need
1344 // along the effect chain.
1345 insert_map_guard = false;
1346 } else {
1347 // Explicitly branch on the {lookup_start_object_maps}.
1348 ZoneHandleSet<Map> maps;
1349 for (Handle<Map> map : lookup_start_object_maps) {
1350 maps.insert(map, graph()->zone());
1351 }
1352 Node* check = this_effect =
1353 graph()->NewNode(simplified()->CompareMaps(maps),
1354 lookup_start_object, this_effect, this_control);
1355 Node* branch =
1356 graph()->NewNode(common()->Branch(), check, this_control);
1357 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
1358 this_control = graph()->NewNode(common()->IfTrue(), branch);
1359 }
1360
1361 // The Number case requires special treatment to also deal with Smis.
1362 if (HasNumberMaps(broker(), lookup_start_object_maps)) {
1363 // Join this check with the "receiver is smi" check above.
1364 DCHECK_EQ(receiver, lookup_start_object);
1365 DCHECK_NOT_NULL(receiverissmi_effect);
1366 DCHECK_NOT_NULL(receiverissmi_control);
1367 this_control = graph()->NewNode(common()->Merge(2), this_control,
1368 receiverissmi_control);
1369 this_effect = graph()->NewNode(common()->EffectPhi(2), this_effect,
1370 receiverissmi_effect, this_control);
1371 receiverissmi_effect = receiverissmi_control = nullptr;
1372
1373 // The {lookup_start_object} can also be a Smi in this case, so
1374 // a MapGuard doesn't make sense for this at all.
1375 insert_map_guard = false;
1376 }
1377
1378 // Introduce a MapGuard to learn from this on the effect chain.
1379 if (insert_map_guard) {
1380 ZoneHandleSet<Map> maps;
1381 for (auto lookup_start_object_map : lookup_start_object_maps) {
1382 maps.insert(lookup_start_object_map, graph()->zone());
1383 }
1384 this_effect =
1385 graph()->NewNode(simplified()->MapGuard(maps),
1386 lookup_start_object, this_effect, this_control);
1387 }
1388
1389 // If all {lookup_start_object_maps} are Strings we also need to rename
1390 // the {lookup_start_object} here to make sure that TurboFan knows that
1391 // along this path the {this_lookup_start_object} is a String. This is
1392 // because we want strict checking of types, for example for
1393 // StringLength operators.
1394 if (HasOnlyStringMaps(broker(), lookup_start_object_maps)) {
1395 DCHECK_EQ(receiver, lookup_start_object);
1396 this_lookup_start_object = this_receiver = this_effect =
1397 graph()->NewNode(common()->TypeGuard(Type::String()),
1398 lookup_start_object, this_effect, this_control);
1399 }
1400 }
1401
1402 // Generate the actual property access.
1403 ValueEffectControl continuation = BuildPropertyAccess(
1404 this_lookup_start_object, this_receiver, this_value, context,
1405 frame_state, this_effect, this_control, feedback.name(),
1406 if_exceptions, access_info, access_mode);
1407 values.push_back(continuation.value());
1408 effects.push_back(continuation.effect());
1409 controls.push_back(continuation.control());
1410 }
1411
1412 DCHECK_NULL(fallthrough_control);
1413
1414 // Generate the final merge point for all (polymorphic) branches.
1415 int const control_count = static_cast<int>(controls.size());
1416 if (control_count == 0) {
1417 value = effect = control = jsgraph()->Dead();
1418 } else if (control_count == 1) {
1419 value = values.front();
1420 effect = effects.front();
1421 control = controls.front();
1422 } else {
1423 control = graph()->NewNode(common()->Merge(control_count), control_count,
1424 &controls.front());
1425 values.push_back(control);
1426 value = graph()->NewNode(
1427 common()->Phi(MachineRepresentation::kTagged, control_count),
1428 control_count + 1, &values.front());
1429 effects.push_back(control);
1430 effect = graph()->NewNode(common()->EffectPhi(control_count),
1431 control_count + 1, &effects.front());
1432 }
1433 }
1434
1435 // Properly rewire IfException edges if {node} is inside a try-block.
1436 if (!if_exception_nodes.empty()) {
1437 DCHECK_NOT_NULL(if_exception);
1438 DCHECK_EQ(if_exceptions, &if_exception_nodes);
1439 int const if_exception_count = static_cast<int>(if_exceptions->size());
1440 Node* merge = graph()->NewNode(common()->Merge(if_exception_count),
1441 if_exception_count, &if_exceptions->front());
1442 if_exceptions->push_back(merge);
1443 Node* ephi =
1444 graph()->NewNode(common()->EffectPhi(if_exception_count),
1445 if_exception_count + 1, &if_exceptions->front());
1446 Node* phi = graph()->NewNode(
1447 common()->Phi(MachineRepresentation::kTagged, if_exception_count),
1448 if_exception_count + 1, &if_exceptions->front());
1449 ReplaceWithValue(if_exception, phi, ephi, merge);
1450 }
1451
1452 ReplaceWithValue(node, value, effect, control);
1453 return Replace(value);
1454}
1455
1456Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
1457 JSLoadNamedNode n(node);
1458 NamedAccess const& p = n.Parameters();
1459 Node* const receiver = n.object();
1460 NameRef name(broker(), p.name());
1461
1462 // Check if we have a constant receiver.
1463 HeapObjectMatcher m(receiver);
1464 if (m.HasResolvedValue()) {
1465 ObjectRef object = m.Ref(broker());
1466 if (object.IsJSFunction() &&
1467 name.equals(ObjectRef(broker(), factory()->prototype_string()))) {
1468 // Optimize "prototype" property of functions.
1469 JSFunctionRef function = object.AsJSFunction();
1470 if (should_disallow_heap_access() && !function.serialized()) {
1471 TRACE_BROKER_MISSING(broker(), "data for function " << function);
1472 return NoChange();
1473 }
1474 // TODO(neis): Remove the has_prototype_slot condition once the broker is
1475 // always enabled.
1476 if (!function.map().has_prototype_slot() || !function.has_prototype() ||
1477 function.PrototypeRequiresRuntimeLookup()) {
1478 return NoChange();
1479 }
1480 ObjectRef prototype = dependencies()->DependOnPrototypeProperty(function);
1481 Node* value = jsgraph()->Constant(prototype);
1482 ReplaceWithValue(node, value);
1483 return Replace(value);
1484 } else if (object.IsString() &&
1485 name.equals(ObjectRef(broker(), factory()->length_string()))) {
1486 // Constant-fold "length" property on constant strings.
1487 Node* value = jsgraph()->Constant(object.AsString().length());
1488 ReplaceWithValue(node, value);
1489 return Replace(value);
1490 }
1491 }
1492
1493 if (!p.feedback().IsValid()) return NoChange();
1494 return ReducePropertyAccess(node, nullptr, name, jsgraph()->Dead(),
1495 FeedbackSource(p.feedback()), AccessMode::kLoad);
1496}
1497
1498Reduction JSNativeContextSpecialization::ReduceJSLoadNamedFromSuper(
1499 Node* node) {
1500 JSLoadNamedFromSuperNode n(node);
1501 NamedAccess const& p = n.Parameters();
1502 NameRef name(broker(), p.name());
1503
1504 if (!p.feedback().IsValid()) return NoChange();
1505 return ReducePropertyAccess(node, nullptr, name, jsgraph()->Dead(),
1506 FeedbackSource(p.feedback()), AccessMode::kLoad);
1507}
1508
1509Reduction JSNativeContextSpecialization::ReduceJSGetIterator(Node* node) {
1510 JSGetIteratorNode n(node);
1511 GetIteratorParameters const& p = n.Parameters();
1512
1513 TNode<Object> receiver = n.receiver();
1514 TNode<Object> context = n.context();
1515 FrameState frame_state = n.frame_state();
1516 Effect effect = n.effect();
1517 Control control = n.control();
1518
1519 // Load iterator property operator
1520 Handle<Name> iterator_symbol = factory()->iterator_symbol();
1521 const Operator* load_op =
1522 javascript()->LoadNamed(iterator_symbol, p.loadFeedback());
1523
1524 // Lazy deopt of the load iterator property
1525 // TODO(v8:10047): Use TaggedIndexConstant here once deoptimizer supports it.
1526 Node* call_slot = jsgraph()->SmiConstant(p.callFeedback().slot.ToInt());
1527 Node* call_feedback = jsgraph()->HeapConstant(p.callFeedback().vector);
1528 Node* lazy_deopt_parameters[] = {receiver, call_slot, call_feedback};
1529 Node* lazy_deopt_frame_state = CreateStubBuiltinContinuationFrameState(
1530 jsgraph(), Builtins::kGetIteratorWithFeedbackLazyDeoptContinuation,
1531 context, lazy_deopt_parameters, arraysize(lazy_deopt_parameters),
1532 frame_state, ContinuationFrameStateMode::LAZY);
1533 Node* load_property =
1534 graph()->NewNode(load_op, receiver, n.feedback_vector(), context,
1535 lazy_deopt_frame_state, effect, control);
1536 effect = load_property;
1537 control = load_property;
1538
1539 // Handle exception path for the load named property
1540 Node* iterator_exception_node = nullptr;
1541 if (NodeProperties::IsExceptionalCall(node, &iterator_exception_node)) {
1542 // If there exists an exception node for the given iterator_node, create a
1543 // pair of IfException/IfSuccess nodes on the current control path. The uses
1544 // of new exception node are merged with the original exception node. The
1545 // IfSuccess node is returned as a control path for further reduction.
1546 Node* exception_node =
1547 graph()->NewNode(common()->IfException(), effect, control);
1548 Node* if_success = graph()->NewNode(common()->IfSuccess(), control);
1549
1550 // Use dead_node as a placeholder for the original exception node until
1551 // its uses are rewired to the nodes merging the exceptions
1552 Node* dead_node = jsgraph()->Dead();
1553 Node* merge_node =
1554 graph()->NewNode(common()->Merge(2), dead_node, exception_node);
1555 Node* effect_phi = graph()->NewNode(common()->EffectPhi(2), dead_node,
1556 exception_node, merge_node);
1557 Node* phi =
1558 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
1559 dead_node, exception_node, merge_node);
1560 ReplaceWithValue(iterator_exception_node, phi, effect_phi, merge_node);
1561 phi->ReplaceInput(0, iterator_exception_node);
1562 effect_phi->ReplaceInput(0, iterator_exception_node);
1563 merge_node->ReplaceInput(0, iterator_exception_node);
1564 control = if_success;
1565 }
1566
1567 // Eager deopt of call iterator property
1568 Node* parameters[] = {receiver, load_property, call_slot, call_feedback};
1569 Node* eager_deopt_frame_state = CreateStubBuiltinContinuationFrameState(
1570 jsgraph(), Builtins::kCallIteratorWithFeedback, context, parameters,
1571 arraysize(parameters), frame_state, ContinuationFrameStateMode::EAGER);
1572 Node* deopt_checkpoint = graph()->NewNode(
1573 common()->Checkpoint(), eager_deopt_frame_state, effect, control);
1574 effect = deopt_checkpoint;
1575
1576 // Call iterator property operator
1577 ProcessedFeedback const& feedback =
1578 broker()->GetFeedbackForCall(p.callFeedback());
1579 SpeculationMode mode = feedback.IsInsufficient()
1580 ? SpeculationMode::kDisallowSpeculation
1581 : feedback.AsCall().speculation_mode();
1582 const Operator* call_op = javascript()->Call(
1583 JSCallNode::ArityForArgc(0), CallFrequency(), p.callFeedback(),
1584 ConvertReceiverMode::kNotNullOrUndefined, mode,
1585 CallFeedbackRelation::kRelated);
1586 Node* call_property =
1587 graph()->NewNode(call_op, load_property, receiver, n.feedback_vector(),
1588 context, frame_state, effect, control);
1589
1590 return Replace(call_property);
1591}
1592
1593Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) {
1594 JSStoreNamedNode n(node);
1595 NamedAccess const& p = n.Parameters();
1596 if (!p.feedback().IsValid()) return NoChange();
1597 return ReducePropertyAccess(node, nullptr, NameRef(broker(), p.name()),
1598 n.value(), FeedbackSource(p.feedback()),
1599 AccessMode::kStore);
1600}
1601
1602Reduction JSNativeContextSpecialization::ReduceJSStoreNamedOwn(Node* node) {
1603 JSStoreNamedOwnNode n(node);
1604 StoreNamedOwnParameters const& p = n.Parameters();
1605 if (!p.feedback().IsValid()) return NoChange();
1606 return ReducePropertyAccess(node, nullptr, NameRef(broker(), p.name()),
1607 n.value(), FeedbackSource(p.feedback()),
1608 AccessMode::kStoreInLiteral);
1609}
1610
1611Reduction JSNativeContextSpecialization::ReduceElementAccessOnString(
1612 Node* node, Node* index, Node* value, KeyedAccessMode const& keyed_mode) {
1613 Node* receiver = NodeProperties::GetValueInput(node, 0);
1614 Node* effect = NodeProperties::GetEffectInput(node);
1615 Node* control = NodeProperties::GetControlInput(node);
1616
1617 // Strings are immutable in JavaScript.
1618 if (keyed_mode.access_mode() == AccessMode::kStore) return NoChange();
1619
1620 // `in` cannot be used on strings.
1621 if (keyed_mode.access_mode() == AccessMode::kHas) return NoChange();
1622
1623 // Ensure that the {receiver} is actually a String.
1624 receiver = effect = graph()->NewNode(
1625 simplified()->CheckString(FeedbackSource()), receiver, effect, control);
1626
1627 // Determine the {receiver} length.
1628 Node* length = graph()->NewNode(simplified()->StringLength(), receiver);
1629
1630 // Load the single character string from {receiver} or yield undefined
1631 // if the {index} is out of bounds (depending on the {load_mode}).
1632 value = BuildIndexedStringLoad(receiver, index, length, &effect, &control,
1633 keyed_mode.load_mode());
1634
1635 ReplaceWithValue(node, value, effect, control);
1636 return Replace(value);
1637}
1638
1639namespace {
1640base::Optional<JSTypedArrayRef> GetTypedArrayConstant(JSHeapBroker* broker,
1641 Node* receiver) {
1642 HeapObjectMatcher m(receiver);
1643 if (!m.HasResolvedValue()) return base::nullopt;
1644 ObjectRef object = m.Ref(broker);
1645 if (!object.IsJSTypedArray()) return base::nullopt;
1646 JSTypedArrayRef typed_array = object.AsJSTypedArray();
1647 if (typed_array.is_on_heap()) return base::nullopt;
1648 return typed_array;
1649}
1650} // namespace
1651
1652void JSNativeContextSpecialization::RemoveImpossibleMaps(
1653 Node* object, ZoneVector<Handle<Map>>* maps) const {
1654 base::Optional<MapRef> root_map = InferRootMap(object);
1655 if (root_map.has_value()) {
1656 DCHECK(!root_map->is_abandoned_prototype_map());
1657 maps->erase(
1658 std::remove_if(maps->begin(), maps->end(),
1659 [root_map, this](Handle<Map> map) {
1660 MapRef map_ref(broker(), map);
1661 return map_ref.is_abandoned_prototype_map() ||
1662 (map_ref.FindRootMap().has_value() &&
1663 !map_ref.FindRootMap()->equals(*root_map));
1664 }),
1665 maps->end());
1666 }
1667}
1668
1669// Possibly refine the feedback using inferred map information from the graph.
1670ElementAccessFeedback const&
1671JSNativeContextSpecialization::TryRefineElementAccessFeedback(
1672 ElementAccessFeedback const& feedback, Node* receiver, Node* effect) const {
1673 AccessMode access_mode = feedback.keyed_mode().access_mode();
1674 bool use_inference =
1675 access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas;
1676 if (!use_inference) return feedback;
1677
1678 ZoneVector<Handle<Map>> inferred_maps(zone());
1679 if (!InferMaps(receiver, effect, &inferred_maps)) return feedback;
1680
1681 RemoveImpossibleMaps(receiver, &inferred_maps);
1682 // TODO(neis): After Refine, the resulting feedback can still contain
1683 // impossible maps when a target is kept only because more than one of its
1684 // sources was inferred. Think of a way to completely rule out impossible
1685 // maps.
1686 return feedback.Refine(inferred_maps, zone());
1687}
1688
1689Reduction JSNativeContextSpecialization::ReduceElementAccess(
1690 Node* node, Node* index, Node* value,
1691 ElementAccessFeedback const& feedback) {
1692 DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
1693 node->opcode() == IrOpcode::kJSStoreProperty ||
1694 node->opcode() == IrOpcode::kJSStoreInArrayLiteral ||
1695 node->opcode() == IrOpcode::kJSStoreDataPropertyInLiteral ||
1696 node->opcode() == IrOpcode::kJSHasProperty);
1697 STATIC_ASSERT(JSLoadPropertyNode::ObjectIndex() == 0 &&
1698 JSStorePropertyNode::ObjectIndex() == 0 &&
1699 JSStoreInArrayLiteralNode::ArrayIndex() == 0 &&
1700 JSStoreDataPropertyInLiteralNode::ObjectIndex() == 0 &&
1701 JSHasPropertyNode::ObjectIndex() == 0);
1702
1703 Node* receiver = NodeProperties::GetValueInput(node, 0);
1704 Node* effect = NodeProperties::GetEffectInput(node);
1705 Node* control = NodeProperties::GetControlInput(node);
1706 Node* frame_state =
1707 NodeProperties::FindFrameStateBefore(node, jsgraph()->Dead());
1708
1709 // TODO(neis): It's odd that we do optimizations below that don't really care
1710 // about the feedback, but we don't do them when the feedback is megamorphic.
1711 if (feedback.transition_groups().empty()) return NoChange();
1712
1713 ElementAccessFeedback const& refined_feedback =
1714 TryRefineElementAccessFeedback(feedback, receiver, effect);
1715
1716 AccessMode access_mode = refined_feedback.keyed_mode().access_mode();
1717 if ((access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) &&
1718 receiver->opcode() == IrOpcode::kHeapConstant) {
1719 Reduction reduction = ReduceElementLoadFromHeapConstant(
1720 node, index, access_mode, refined_feedback.keyed_mode().load_mode());
1721 if (reduction.Changed()) return reduction;
1722 }
1723
1724 if (!refined_feedback.transition_groups().empty() &&
1725 refined_feedback.HasOnlyStringMaps(broker())) {
1726 return ReduceElementAccessOnString(node, index, value,
1727 refined_feedback.keyed_mode());
1728 }
1729
1730 AccessInfoFactory access_info_factory(broker(), dependencies(),
1731 graph()->zone());
1732 ZoneVector<ElementAccessInfo> access_infos(zone());
1733 if (!access_info_factory.ComputeElementAccessInfos(refined_feedback,
1734 &access_infos) ||
1735 access_infos.empty()) {
1736 return NoChange();
1737 }
1738
1739 // For holey stores or growing stores, we need to check that the prototype
1740 // chain contains no setters for elements, and we need to guard those checks
1741 // via code dependencies on the relevant prototype maps.
1742 if (access_mode == AccessMode::kStore) {
1743 // TODO(turbofan): We could have a fast path here, that checks for the
1744 // common case of Array or Object prototype only and therefore avoids
1745 // the zone allocation of this vector.
1746 ZoneVector<MapRef> prototype_maps(zone());
1747 for (ElementAccessInfo const& access_info : access_infos) {
1748 for (Handle<Map> map : access_info.lookup_start_object_maps()) {
1749 MapRef receiver_map(broker(), map);
1750 // If the {receiver_map} has a prototype and its elements backing
1751 // store is either holey, or we have a potentially growing store,
1752 // then we need to check that all prototypes have stable maps with
1753 // fast elements (and we need to guard against changes to that below).
1754 if ((IsHoleyOrDictionaryElementsKind(receiver_map.elements_kind()) ||
1755 IsGrowStoreMode(feedback.keyed_mode().store_mode())) &&
1756 !receiver_map.HasOnlyStablePrototypesWithFastElements(
1757 &prototype_maps)) {
1758 return NoChange();
1759 }
1760 }
1761 }
1762 for (MapRef const& prototype_map : prototype_maps) {
1763 dependencies()->DependOnStableMap(prototype_map);
1764 }
1765 } else if (access_mode == AccessMode::kHas) {
1766 // If we have any fast arrays, we need to check and depend on
1767 // NoElementsProtector.
1768 for (ElementAccessInfo const& access_info : access_infos) {
1769 if (IsFastElementsKind(access_info.elements_kind())) {
1770 if (!dependencies()->DependOnNoElementsProtector()) return NoChange();
1771 break;
1772 }
1773 }
1774 }
1775
1776 // Check if we have the necessary data for building element accesses.
1777 for (ElementAccessInfo const& access_info : access_infos) {
1778 if (!IsTypedArrayElementsKind(access_info.elements_kind())) continue;
1779 base::Optional<JSTypedArrayRef> typed_array =
1780 GetTypedArrayConstant(broker(), receiver);
1781 if (typed_array.has_value()) {
1782 if (should_disallow_heap_access() && !typed_array->serialized()) {
1783 TRACE_BROKER_MISSING(broker(), "data for typed array " << *typed_array);
1784 return NoChange();
1785 }
1786 }
1787 }
1788
1789 // Check for the monomorphic case.
1790 PropertyAccessBuilder access_builder(jsgraph(), broker(), dependencies());
1791 if (access_infos.size() == 1) {
1792 ElementAccessInfo access_info = access_infos.front();
1793
1794 // Perform possible elements kind transitions.
1795 MapRef transition_target(broker(),
1796 access_info.lookup_start_object_maps().front());
1797 for (auto source : access_info.transition_sources()) {
1798 DCHECK_EQ(access_info.lookup_start_object_maps().size(), 1);
1799 MapRef transition_source(broker(), source);
1800 effect = graph()->NewNode(
1801 simplified()->TransitionElementsKind(ElementsTransition(
1802 IsSimpleMapChangeTransition(transition_source.elements_kind(),
1803 transition_target.elements_kind())
1804 ? ElementsTransition::kFastTransition
1805 : ElementsTransition::kSlowTransition,
1806 transition_source.object(), transition_target.object())),
1807 receiver, effect, control);
1808 }
1809
1810 // TODO(turbofan): The effect/control linearization will not find a
1811 // FrameState after the StoreField or Call that is generated for the
1812 // elements kind transition above. This is because those operators
1813 // don't have the kNoWrite flag on it, even though they are not
1814 // observable by JavaScript.
1815 effect =
1816 graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
1817
1818 // Perform map check on the {receiver}.
1819 access_builder.BuildCheckMaps(receiver, &effect, control,
1820 access_info.lookup_start_object_maps());
1821
1822 // Access the actual element.
1823 ValueEffectControl continuation =
1824 BuildElementAccess(receiver, index, value, effect, control, access_info,
1825 feedback.keyed_mode());
1826 value = continuation.value();
1827 effect = continuation.effect();
1828 control = continuation.control();
1829 } else {
1830 // The final states for every polymorphic branch. We join them with
1831 // Merge+Phi+EffectPhi at the bottom.
1832 ZoneVector<Node*> values(zone());
1833 ZoneVector<Node*> effects(zone());
1834 ZoneVector<Node*> controls(zone());
1835
1836 // Generate code for the various different element access patterns.
1837 Node* fallthrough_control = control;
1838 for (size_t j = 0; j < access_infos.size(); ++j) {
1839 ElementAccessInfo const& access_info = access_infos[j];
1840 Node* this_receiver = receiver;
1841 Node* this_value = value;
1842 Node* this_index = index;
1843 Node* this_effect = effect;
1844 Node* this_control = fallthrough_control;
1845
1846 // Perform possible elements kind transitions.
1847 MapRef transition_target(broker(),
1848 access_info.lookup_start_object_maps().front());
1849 for (auto source : access_info.transition_sources()) {
1850 MapRef transition_source(broker(), source);
1851 DCHECK_EQ(access_info.lookup_start_object_maps().size(), 1);
1852 this_effect = graph()->NewNode(
1853 simplified()->TransitionElementsKind(ElementsTransition(
1854 IsSimpleMapChangeTransition(transition_source.elements_kind(),
1855 transition_target.elements_kind())
1856 ? ElementsTransition::kFastTransition
1857 : ElementsTransition::kSlowTransition,
1858 transition_source.object(), transition_target.object())),
1859 receiver, this_effect, this_control);
1860 }
1861
1862 // Perform map check(s) on {receiver}.
1863 ZoneVector<Handle<Map>> const& receiver_maps =
1864 access_info.lookup_start_object_maps();
1865 if (j == access_infos.size() - 1) {
1866 // Last map check on the fallthrough control path, do a
1867 // conditional eager deoptimization exit here.
1868 access_builder.BuildCheckMaps(receiver, &this_effect, this_control,
1869 receiver_maps);
1870 fallthrough_control = nullptr;
1871 } else {
1872 // Explicitly branch on the {receiver_maps}.
1873 ZoneHandleSet<Map> maps;
1874 for (Handle<Map> map : receiver_maps) {
1875 maps.insert(map, graph()->zone());
1876 }
1877 Node* check = this_effect =
1878 graph()->NewNode(simplified()->CompareMaps(maps), receiver,
1879 this_effect, fallthrough_control);
1880 Node* branch =
1881 graph()->NewNode(common()->Branch(), check, fallthrough_control);
1882 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
1883 this_control = graph()->NewNode(common()->IfTrue(), branch);
1884
1885 // Introduce a MapGuard to learn from this on the effect chain.
1886 this_effect = graph()->NewNode(simplified()->MapGuard(maps), receiver,
1887 this_effect, this_control);
1888 }
1889
1890 // Access the actual element.
1891 ValueEffectControl continuation =
1892 BuildElementAccess(this_receiver, this_index, this_value, this_effect,
1893 this_control, access_info, feedback.keyed_mode());
1894 values.push_back(continuation.value());
1895 effects.push_back(continuation.effect());
1896 controls.push_back(continuation.control());
1897 }
1898
1899 DCHECK_NULL(fallthrough_control);
1900
1901 // Generate the final merge point for all (polymorphic) branches.
1902 int const control_count = static_cast<int>(controls.size());
1903 if (control_count == 0) {
1904 value = effect = control = jsgraph()->Dead();
1905 } else if (control_count == 1) {
1906 value = values.front();
1907 effect = effects.front();
1908 control = controls.front();
1909 } else {
1910 control = graph()->NewNode(common()->Merge(control_count), control_count,
1911 &controls.front());
1912 values.push_back(control);
1913 value = graph()->NewNode(
1914 common()->Phi(MachineRepresentation::kTagged, control_count),
1915 control_count + 1, &values.front());
1916 effects.push_back(control);
1917 effect = graph()->NewNode(common()->EffectPhi(control_count),
1918 control_count + 1, &effects.front());
1919 }
1920 }
1921
1922 ReplaceWithValue(node, value, effect, control);
1923 return Replace(value);
1924}
1925
1926Reduction JSNativeContextSpecialization::ReduceElementLoadFromHeapConstant(
1927 Node* node, Node* key, AccessMode access_mode,
1928 KeyedAccessLoadMode load_mode) {
1929 DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
1930 node->opcode() == IrOpcode::kJSHasProperty);
1931 Node* receiver = NodeProperties::GetValueInput(node, 0);
1932 Node* effect = NodeProperties::GetEffectInput(node);
1933 Node* control = NodeProperties::GetControlInput(node);
1934
1935 HeapObjectMatcher mreceiver(receiver);
1936 HeapObjectRef receiver_ref = mreceiver.Ref(broker());
1937 if (receiver_ref.map().oddball_type() == OddballType::kHole ||
1938 receiver_ref.map().oddball_type() == OddballType::kNull ||
1939 receiver_ref.map().oddball_type() == OddballType::kUndefined ||
1940 // The 'in' operator throws a TypeError on primitive values.
1941 (receiver_ref.IsString() && access_mode == AccessMode::kHas)) {
1942 return NoChange();
1943 }
1944
1945 // Check whether we're accessing a known element on the {receiver} and can
1946 // constant-fold the load.
1947 NumberMatcher mkey(key);
1948 if (mkey.IsInteger() && mkey.IsInRange(0.0, kMaxUInt32 - 1.0)) {
1949 uint32_t index = static_cast<uint32_t>(mkey.ResolvedValue());
1950 base::Optional<ObjectRef> element =
1951 receiver_ref.GetOwnConstantElement(index);
1952 if (!element.has_value() && receiver_ref.IsJSArray()) {
1953 // We didn't find a constant element, but if the receiver is a cow-array
1954 // we can exploit the fact that any future write to the element will
1955 // replace the whole elements storage.
1956 element = receiver_ref.AsJSArray().GetOwnCowElement(index);
1957 if (element.has_value()) {
1958 Node* elements = effect = graph()->NewNode(
1959 simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
1960 receiver, effect, control);
1961 FixedArrayRef array_elements =
1962 receiver_ref.AsJSArray().elements().AsFixedArray();
1963 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), elements,
1964 jsgraph()->Constant(array_elements));
1965 effect = graph()->NewNode(
1966 simplified()->CheckIf(DeoptimizeReason::kCowArrayElementsChanged),
1967 check, effect, control);
1968 }
1969 }
1970 if (element.has_value()) {
1971 Node* value = access_mode == AccessMode::kHas
1972 ? jsgraph()->TrueConstant()
1973 : jsgraph()->Constant(*element);
1974 ReplaceWithValue(node, value, effect, control);
1975 return Replace(value);
1976 }
1977 }
1978
1979 // For constant Strings we can eagerly strength-reduce the keyed
1980 // accesses using the known length, which doesn't change.
1981 if (receiver_ref.IsString()) {
1982 DCHECK_NE(access_mode, AccessMode::kHas);
1983 // Ensure that {key} is less than {receiver} length.
1984 Node* length = jsgraph()->Constant(receiver_ref.AsString().length());
1985
1986 // Load the single character string from {receiver} or yield
1987 // undefined if the {key} is out of bounds (depending on the
1988 // {load_mode}).
1989 Node* value = BuildIndexedStringLoad(receiver, key, length, &effect,
1990 &control, load_mode);
1991 ReplaceWithValue(node, value, effect, control);
1992 return Replace(value);
1993 }
1994
1995 return NoChange();
1996}
1997
1998Reduction JSNativeContextSpecialization::ReducePropertyAccess(
1999 Node* node, Node* key, base::Optional<NameRef> static_name, Node* value,
2000 FeedbackSource const& source, AccessMode access_mode) {
2001 DisallowHeapAccessIf disallow_heap_access(should_disallow_heap_access());
2002
2003 DCHECK_EQ(key == nullptr, static_name.has_value());
2004 DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
2005 node->opcode() == IrOpcode::kJSStoreProperty ||
2006 node->opcode() == IrOpcode::kJSStoreInArrayLiteral ||
2007 node->opcode() == IrOpcode::kJSStoreDataPropertyInLiteral ||
2008 node->opcode() == IrOpcode::kJSHasProperty ||
2009 node->opcode() == IrOpcode::kJSLoadNamed ||
2010 node->opcode() == IrOpcode::kJSStoreNamed ||
2011 node->opcode() == IrOpcode::kJSStoreNamedOwn ||
2012 node->opcode() == IrOpcode::kJSLoadNamedFromSuper);
2013 DCHECK_GE(node->op()->ControlOutputCount(), 1);
2014
2015 ProcessedFeedback const& feedback =
2016 broker()->GetFeedbackForPropertyAccess(source, access_mode, static_name);
2017 switch (feedback.kind()) {
2018 case ProcessedFeedback::kInsufficient:
2019 return ReduceSoftDeoptimize(
2020 node,
2021 DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess);
2022 case ProcessedFeedback::kNamedAccess:
2023 return ReduceNamedAccess(node, value, feedback.AsNamedAccess(),
2024 access_mode, key);
2025 case ProcessedFeedback::kMinimorphicPropertyAccess:
2026 DCHECK_EQ(access_mode, AccessMode::kLoad);
2027 DCHECK_NULL(key);
2028 return ReduceMinimorphicPropertyAccess(
2029 node, value, feedback.AsMinimorphicPropertyAccess(), source);
2030 case ProcessedFeedback::kElementAccess:
2031 DCHECK_EQ(feedback.AsElementAccess().keyed_mode().access_mode(),
2032 access_mode);
2033 DCHECK_NE(node->opcode(), IrOpcode::kJSLoadNamedFromSuper);
2034 return ReduceElementAccess(node, key, value, feedback.AsElementAccess());
2035 default:
2036 UNREACHABLE();
2037 }
2038}
2039
2040Reduction JSNativeContextSpecialization::ReduceSoftDeoptimize(
2041 Node* node, DeoptimizeReason reason) {
2042 if (!(flags() & kBailoutOnUninitialized)) return NoChange();
2043
2044 Node* effect = NodeProperties::GetEffectInput(node);
2045 Node* control = NodeProperties::GetControlInput(node);
2046 Node* frame_state =
2047 NodeProperties::FindFrameStateBefore(node, jsgraph()->Dead());
2048 Node* deoptimize = graph()->NewNode(
2049 common()->Deoptimize(DeoptimizeKind::kSoft, reason, FeedbackSource()),
2050 frame_state, effect, control);
2051 // TODO(bmeurer): This should be on the AdvancedReducer somehow.
2052 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
2053 Revisit(graph()->end());
2054 node->TrimInputCount(0);
2055 NodeProperties::ChangeOp(node, common()->Dead());
2056 return Changed(node);
2057}
2058
2059Reduction JSNativeContextSpecialization::ReduceJSHasProperty(Node* node) {
2060 JSHasPropertyNode n(node);
2061 PropertyAccess const& p = n.Parameters();
2062 Node* value = jsgraph()->Dead();
2063 if (!p.feedback().IsValid()) return NoChange();
2064 return ReducePropertyAccess(node, n.key(), base::nullopt, value,
2065 FeedbackSource(p.feedback()), AccessMode::kHas);
2066}
2067
2068Reduction JSNativeContextSpecialization::ReduceJSLoadPropertyWithEnumeratedKey(
2069 Node* node) {
2070 // We can optimize a property load if it's being used inside a for..in:
2071 // for (name in receiver) {
2072 // value = receiver[name];
2073 // ...
2074 // }
2075 //
2076 // If the for..in is in fast-mode, we know that the {receiver} has {name}
2077 // as own property, otherwise the enumeration wouldn't include it. The graph
2078 // constructed by the BytecodeGraphBuilder in this case looks like this:
2079
2080 // receiver
2081 // ^ ^
2082 // | |
2083 // | +-+
2084 // | |
2085 // | JSToObject
2086 // | ^
2087 // | |
2088 // | |
2089 // | JSForInNext
2090 // | ^
2091 // | |
2092 // +----+ |
2093 // | |
2094 // | |
2095 // JSLoadProperty
2096
2097 // If the for..in has only seen maps with enum cache consisting of keys
2098 // and indices so far, we can turn the {JSLoadProperty} into a map check
2099 // on the {receiver} and then just load the field value dynamically via
2100 // the {LoadFieldByIndex} operator. The map check is only necessary when
2101 // TurboFan cannot prove that there is no observable side effect between
2102 // the {JSForInNext} and the {JSLoadProperty} node.
2103 //
2104 // Also note that it's safe to look through the {JSToObject}, since the
2105 // [[Get]] operation does an implicit ToObject anyway, and these operations
2106 // are not observable.
2107
2108 DCHECK_EQ(IrOpcode::kJSLoadProperty, node->opcode());
2109 Node* receiver = NodeProperties::GetValueInput(node, 0);
2110 JSForInNextNode name(NodeProperties::GetValueInput(node, 1));
2111 Node* effect = NodeProperties::GetEffectInput(node);
2112 Node* control = NodeProperties::GetControlInput(node);
2113
2114 if (name.Parameters().mode() != ForInMode::kUseEnumCacheKeysAndIndices) {
2115 return NoChange();
2116 }
2117
2118 Node* object = name.receiver();
2119 Node* cache_type = name.cache_type();
2120 Node* index = name.index();
2121 if (object->opcode() == IrOpcode::kJSToObject) {
2122 object = NodeProperties::GetValueInput(object, 0);
2123 }
2124 if (object != receiver) return NoChange();
2125
2126 // No need to repeat the map check if we can prove that there's no
2127 // observable side effect between {effect} and {name].
2128 if (!NodeProperties::NoObservableSideEffectBetween(effect, name)) {
2129 // Check that the {receiver} map is still valid.
2130 Node* receiver_map = effect =
2131 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
2132 receiver, effect, control);
2133 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), receiver_map,
2134 cache_type);
2135 effect =
2136 graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kWrongMap),
2137 check, effect, control);
2138 }
2139
2140 // Load the enum cache indices from the {cache_type}.
2141 Node* descriptor_array = effect = graph()->NewNode(
2142 simplified()->LoadField(AccessBuilder::ForMapDescriptors()), cache_type,
2143 effect, control);
2144 Node* enum_cache = effect = graph()->NewNode(
2145 simplified()->LoadField(AccessBuilder::ForDescriptorArrayEnumCache()),
2146 descriptor_array, effect, control);
2147 Node* enum_indices = effect = graph()->NewNode(
2148 simplified()->LoadField(AccessBuilder::ForEnumCacheIndices()), enum_cache,
2149 effect, control);
2150
2151 // Ensure that the {enum_indices} are valid.
2152 Node* check = graph()->NewNode(
2153 simplified()->BooleanNot(),
2154 graph()->NewNode(simplified()->ReferenceEqual(), enum_indices,
2155 jsgraph()->EmptyFixedArrayConstant()));
2156 effect = graph()->NewNode(
2157 simplified()->CheckIf(DeoptimizeReason::kWrongEnumIndices), check, effect,
2158 control);
2159
2160 // Determine the key from the {enum_indices}.
2161 Node* key = effect = graph()->NewNode(
2162 simplified()->LoadElement(
2163 AccessBuilder::ForFixedArrayElement(PACKED_SMI_ELEMENTS)),
2164 enum_indices, index, effect, control);
2165
2166 // Load the actual field value.
2167 Node* value = effect = graph()->NewNode(simplified()->LoadFieldByIndex(),
2168 receiver, key, effect, control);
2169 ReplaceWithValue(node, value, effect, control);
2170 return Replace(value);
2171}
2172
2173Reduction JSNativeContextSpecialization::ReduceJSLoadProperty(Node* node) {
2174 JSLoadPropertyNode n(node);
2175 PropertyAccess const& p = n.Parameters();
2176 Node* name = n.key();
2177
2178 if (name->opcode() == IrOpcode::kJSForInNext) {
2179 Reduction reduction = ReduceJSLoadPropertyWithEnumeratedKey(node);
2180 if (reduction.Changed()) return reduction;
2181 }
2182
2183 if (!p.feedback().IsValid()) return NoChange();
2184 Node* value = jsgraph()->Dead();
2185 return ReducePropertyAccess(node, name, base::nullopt, value,
2186 FeedbackSource(p.feedback()), AccessMode::kLoad);
2187}
2188
2189Reduction JSNativeContextSpecialization::ReduceJSStoreProperty(Node* node) {
2190 JSStorePropertyNode n(node);
2191 PropertyAccess const& p = n.Parameters();
2192 if (!p.feedback().IsValid()) return NoChange();
2193 return ReducePropertyAccess(node, n.key(), base::nullopt, n.value(),
2194 FeedbackSource(p.feedback()), AccessMode::kStore);
2195}
2196
2197Node* JSNativeContextSpecialization::InlinePropertyGetterCall(
2198 Node* receiver, Node* context, Node* frame_state, Node** effect,
2199 Node** control, ZoneVector<Node*>* if_exceptions,
2200 PropertyAccessInfo const& access_info) {
2201 ObjectRef constant(broker(), access_info.constant());
2202 Node* target = jsgraph()->Constant(constant);
2203 FrameStateInfo const& frame_info = FrameStateInfoOf(frame_state->op());
2204 // Introduce the call to the getter function.
2205 Node* value;
2206 if (constant.IsJSFunction()) {
2207 Node* feedback = jsgraph()->UndefinedConstant();
2208 value = *effect = *control = graph()->NewNode(
2209 jsgraph()->javascript()->Call(JSCallNode::ArityForArgc(0),
2210 CallFrequency(), FeedbackSource(),
2211 ConvertReceiverMode::kNotNullOrUndefined),
2212 target, receiver, feedback, context, frame_state, *effect, *control);
2213 } else {
2214 Node* holder = access_info.holder().is_null()
2215 ? receiver
2216 : jsgraph()->Constant(ObjectRef(
2217 broker(), access_info.holder().ToHandleChecked()));
2218 SharedFunctionInfoRef shared_info(
2219 broker(), frame_info.shared_info().ToHandleChecked());
2220
2221 value =
2222 InlineApiCall(receiver, holder, frame_state, nullptr, effect, control,
2223 shared_info, constant.AsFunctionTemplateInfo());
2224 }
2225 // Remember to rewire the IfException edge if this is inside a try-block.
2226 if (if_exceptions != nullptr) {
2227 // Create the appropriate IfException/IfSuccess projections.
2228 Node* const if_exception =
2229 graph()->NewNode(common()->IfException(), *control, *effect);
2230 Node* const if_success = graph()->NewNode(common()->IfSuccess(), *control);
2231 if_exceptions->push_back(if_exception);
2232 *control = if_success;
2233 }
2234 return value;
2235}
2236
2237void JSNativeContextSpecialization::InlinePropertySetterCall(
2238 Node* receiver, Node* value, Node* context, Node* frame_state,
2239 Node** effect, Node** control, ZoneVector<Node*>* if_exceptions,
2240 PropertyAccessInfo const& access_info) {
2241 ObjectRef constant(broker(), access_info.constant());
2242 Node* target = jsgraph()->Constant(constant);
2243 FrameStateInfo const& frame_info = FrameStateInfoOf(frame_state->op());
2244 // Introduce the call to the setter function.
2245 if (constant.IsJSFunction()) {
2246 Node* feedback = jsgraph()->UndefinedConstant();
2247 *effect = *control = graph()->NewNode(
2248 jsgraph()->javascript()->Call(JSCallNode::ArityForArgc(1),
2249 CallFrequency(), FeedbackSource(),
2250 ConvertReceiverMode::kNotNullOrUndefined),
2251 target, receiver, value, feedback, context, frame_state, *effect,
2252 *control);
2253 } else {
2254 Node* holder = access_info.holder().is_null()
2255 ? receiver
2256 : jsgraph()->Constant(ObjectRef(
2257 broker(), access_info.holder().ToHandleChecked()));
2258 SharedFunctionInfoRef shared_info(
2259 broker(), frame_info.shared_info().ToHandleChecked());
2260 InlineApiCall(receiver, holder, frame_state, value, effect, control,
2261 shared_info, constant.AsFunctionTemplateInfo());
2262 }
2263 // Remember to rewire the IfException edge if this is inside a try-block.
2264 if (if_exceptions != nullptr) {
2265 // Create the appropriate IfException/IfSuccess projections.
2266 Node* const if_exception =
2267 graph()->NewNode(common()->IfException(), *control, *effect);
2268 Node* const if_success = graph()->NewNode(common()->IfSuccess(), *control);
2269 if_exceptions->push_back(if_exception);
2270 *control = if_success;
2271 }
2272}
2273
2274Node* JSNativeContextSpecialization::InlineApiCall(
2275 Node* receiver, Node* holder, Node* frame_state, Node* value, Node** effect,
2276 Node** control, SharedFunctionInfoRef const& shared_info,
2277 FunctionTemplateInfoRef const& function_template_info) {
2278 if (!function_template_info.has_call_code()) {
2279 return nullptr;
2280 }
2281
2282 if (!function_template_info.call_code().has_value()) {
2283 TRACE_BROKER_MISSING(broker(), "call code for function template info "
2284 << function_template_info);
2285 return nullptr;
2286 }
2287 CallHandlerInfoRef call_handler_info = *function_template_info.call_code();
2288
2289 // Only setters have a value.
2290 int const argc = value == nullptr ? 0 : 1;
2291 // The stub always expects the receiver as the first param on the stack.
2292 Callable call_api_callback = CodeFactory::CallApiCallback(isolate());
2293 CallInterfaceDescriptor call_interface_descriptor =
2294 call_api_callback.descriptor();
2295 auto call_descriptor = Linkage::GetStubCallDescriptor(
2296 graph()->zone(), call_interface_descriptor,
2297 call_interface_descriptor.GetStackParameterCount() + argc +
2298 1 /* implicit receiver */,
2299 CallDescriptor::kNeedsFrameState);
2300
2301 Node* data = jsgraph()->Constant(call_handler_info.data());
2302 ApiFunction function(call_handler_info.callback());
2303 Node* function_reference =
2304 graph()->NewNode(common()->ExternalConstant(ExternalReference::Create(
2305 &function, ExternalReference::DIRECT_API_CALL)));
2306 Node* code = jsgraph()->HeapConstant(call_api_callback.code());
2307
2308 // Add CallApiCallbackStub's register argument as well.
2309 Node* context = jsgraph()->Constant(native_context());
2310 Node* inputs[11] = {
2311 code, function_reference, jsgraph()->Constant(argc), data, holder,
2312 receiver};
2313 int index = 6 + argc;
2314 inputs[index++] = context;
2315 inputs[index++] = frame_state;
2316 inputs[index++] = *effect;
2317 inputs[index++] = *control;
2318 // This needs to stay here because of the edge case described in
2319 // http://crbug.com/675648.
2320 if (value != nullptr) {
2321 inputs[6] = value;
2322 }
2323
2324 return *effect = *control =
2325 graph()->NewNode(common()->Call(call_descriptor), index, inputs);
2326}
2327
2328JSNativeContextSpecialization::ValueEffectControl
2329JSNativeContextSpecialization::BuildPropertyLoad(
2330 Node* lookup_start_object, Node* receiver, Node* context, Node* frame_state,
2331 Node* effect, Node* control, NameRef const& name,
2332 ZoneVector<Node*>* if_exceptions, PropertyAccessInfo const& access_info) {
2333 // Determine actual holder and perform prototype chain checks.
2334 Handle<JSObject> holder;
2335 if (access_info.holder().ToHandle(&holder)) {
2336 dependencies()->DependOnStablePrototypeChains(
2337 access_info.lookup_start_object_maps(), kStartAtPrototype,
2338 JSObjectRef(broker(), holder));
2339 }
2340
2341 // Generate the actual property access.
2342 Node* value;
2343 if (access_info.IsNotFound()) {
2344 value = jsgraph()->UndefinedConstant();
2345 } else if (access_info.IsAccessorConstant()) {
2346 value = InlinePropertyGetterCall(receiver, context, frame_state, &effect,
2347 &control, if_exceptions, access_info);
2348 } else if (access_info.IsModuleExport()) {
2349 Node* cell = jsgraph()->Constant(
2350 ObjectRef(broker(), access_info.constant()).AsCell());
2351 value = effect =
2352 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForCellValue()),
2353 cell, effect, control);
2354 } else if (access_info.IsStringLength()) {
2355 DCHECK_EQ(receiver, lookup_start_object);
2356 value = graph()->NewNode(simplified()->StringLength(), receiver);
2357 } else {
2358 DCHECK(access_info.IsDataField() || access_info.IsDataConstant());
2359 PropertyAccessBuilder access_builder(jsgraph(), broker(), dependencies());
2360 value = access_builder.BuildLoadDataField(
2361 name, access_info, lookup_start_object, &effect, &control);
2362 }
2363
2364 return ValueEffectControl(value, effect, control);
2365}
2366
2367JSNativeContextSpecialization::ValueEffectControl
2368JSNativeContextSpecialization::BuildPropertyTest(
2369 Node* effect, Node* control, PropertyAccessInfo const& access_info) {
2370 // Determine actual holder and perform prototype chain checks.
2371 Handle<JSObject> holder;
2372 if (access_info.holder().ToHandle(&holder)) {
2373 dependencies()->DependOnStablePrototypeChains(
2374 access_info.lookup_start_object_maps(), kStartAtPrototype,
2375 JSObjectRef(broker(), holder));
2376 }
2377
2378 Node* value = access_info.IsNotFound() ? jsgraph()->FalseConstant()
2379 : jsgraph()->TrueConstant();
2380 return ValueEffectControl(value, effect, control);
2381}
2382
2383JSNativeContextSpecialization::ValueEffectControl
2384JSNativeContextSpecialization::BuildPropertyAccess(
2385 Node* lookup_start_object, Node* receiver, Node* value, Node* context,
2386 Node* frame_state, Node* effect, Node* control, NameRef const& name,
2387 ZoneVector<Node*>* if_exceptions, PropertyAccessInfo const& access_info,
2388 AccessMode access_mode) {
2389 switch (access_mode) {
2390 case AccessMode::kLoad:
2391 return BuildPropertyLoad(lookup_start_object, receiver, context,
2392 frame_state, effect, control, name,
2393 if_exceptions, access_info);
2394 case AccessMode::kStore:
2395 case AccessMode::kStoreInLiteral:
2396 DCHECK_EQ(receiver, lookup_start_object);
2397 return BuildPropertyStore(receiver, value, context, frame_state, effect,
2398 control, name, if_exceptions, access_info,
2399 access_mode);
2400 case AccessMode::kHas:
2401 DCHECK_EQ(receiver, lookup_start_object);
2402 return BuildPropertyTest(effect, control, access_info);
2403 }
2404 UNREACHABLE();
2405}
2406
2407JSNativeContextSpecialization::ValueEffectControl
2408JSNativeContextSpecialization::BuildPropertyStore(
2409 Node* receiver, Node* value, Node* context, Node* frame_state, Node* effect,
2410 Node* control, NameRef const& name, ZoneVector<Node*>* if_exceptions,
2411 PropertyAccessInfo const& access_info, AccessMode access_mode) {
2412 // Determine actual holder and perform prototype chain checks.
2413 Handle<JSObject> holder;
2414 PropertyAccessBuilder access_builder(jsgraph(), broker(), dependencies());
2415 if (access_info.holder().ToHandle(&holder)) {
2416 DCHECK_NE(AccessMode::kStoreInLiteral, access_mode);
2417 dependencies()->DependOnStablePrototypeChains(
2418 access_info.lookup_start_object_maps(), kStartAtPrototype,
2419 JSObjectRef(broker(), holder));
2420 }
2421
2422 DCHECK(!access_info.IsNotFound());
2423
2424 // Generate the actual property access.
2425 if (access_info.IsAccessorConstant()) {
2426 InlinePropertySetterCall(receiver, value, context, frame_state, &effect,
2427 &control, if_exceptions, access_info);
2428 } else {
2429 DCHECK(access_info.IsDataField() || access_info.IsDataConstant());
2430 DCHECK(access_mode == AccessMode::kStore ||
2431 access_mode == AccessMode::kStoreInLiteral);
2432 FieldIndex const field_index = access_info.field_index();
2433 Type const field_type = access_info.field_type();
2434 MachineRepresentation const field_representation =
2435 PropertyAccessBuilder::ConvertRepresentation(
2436 access_info.field_representation());
2437 Node* storage = receiver;
2438 if (!field_index.is_inobject()) {
2439 storage = effect = graph()->NewNode(
2440 simplified()->LoadField(
2441 AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer()),
2442 storage, effect, control);
2443 }
2444 bool store_to_existing_constant_field = access_info.IsDataConstant() &&
2445 access_mode == AccessMode::kStore &&
2446 !access_info.HasTransitionMap();
2447 FieldAccess field_access = {
2448 kTaggedBase,
2449 field_index.offset(),
2450 name.object(),
2451 MaybeHandle<Map>(),
2452 field_type,
2453 MachineType::TypeForRepresentation(field_representation),
2454 kFullWriteBarrier,
2455 LoadSensitivity::kUnsafe,
2456 access_info.GetConstFieldInfo(),
2457 access_mode == AccessMode::kStoreInLiteral};
2458
2459 switch (field_representation) {
2460 case MachineRepresentation::kFloat64: {
2461 value = effect =
2462 graph()->NewNode(simplified()->CheckNumber(FeedbackSource()), value,
2463 effect, control);
2464 if (!field_index.is_inobject() || !FLAG_unbox_double_fields) {
2465 if (access_info.HasTransitionMap()) {
2466 // Allocate a HeapNumber for the new property.
2467 AllocationBuilder a(jsgraph(), effect, control);
2468 a.Allocate(HeapNumber::kSize, AllocationType::kYoung,
2469 Type::OtherInternal());
2470 a.Store(AccessBuilder::ForMap(),
2471 MapRef(broker(), factory()->heap_number_map()));
2472 FieldAccess value_field_access =
2473 AccessBuilder::ForHeapNumberValue();
2474 value_field_access.const_field_info = field_access.const_field_info;
2475 a.Store(value_field_access, value);
2476 value = effect = a.Finish();
2477
2478 field_access.type = Type::Any();
2479 field_access.machine_type = MachineType::TaggedPointer();
2480 field_access.write_barrier_kind = kPointerWriteBarrier;
2481 } else {
2482 // We just store directly to the HeapNumber.
2483 FieldAccess const storage_access = {
2484 kTaggedBase,
2485 field_index.offset(),
2486 name.object(),
2487 MaybeHandle<Map>(),
2488 Type::OtherInternal(),
2489 MachineType::TaggedPointer(),
2490 kPointerWriteBarrier,
2491 LoadSensitivity::kUnsafe,
2492 access_info.GetConstFieldInfo(),
2493 access_mode == AccessMode::kStoreInLiteral};
2494 storage = effect =
2495 graph()->NewNode(simplified()->LoadField(storage_access),
2496 storage, effect, control);
2497 field_access.offset = HeapNumber::kValueOffset;
2498 field_access.name = MaybeHandle<Name>();
2499 field_access.machine_type = MachineType::Float64();
2500 }
2501 }
2502 if (store_to_existing_constant_field) {
2503 DCHECK(!access_info.HasTransitionMap());
2504 // If the field is constant check that the value we are going
2505 // to store matches current value.
2506 Node* current_value = effect = graph()->NewNode(
2507 simplified()->LoadField(field_access), storage, effect, control);
2508
2509 Node* check =
2510 graph()->NewNode(simplified()->SameValue(), current_value, value);
2511 effect = graph()->NewNode(
2512 simplified()->CheckIf(DeoptimizeReason::kWrongValue), check,
2513 effect, control);
2514 return ValueEffectControl(value, effect, control);
2515 }
2516 break;
2517 }
2518 case MachineRepresentation::kTaggedSigned:
2519 case MachineRepresentation::kTaggedPointer:
2520 case MachineRepresentation::kTagged:
2521 if (store_to_existing_constant_field) {
2522 DCHECK(!access_info.HasTransitionMap());
2523 // If the field is constant check that the value we are going
2524 // to store matches current value.
2525 Node* current_value = effect = graph()->NewNode(
2526 simplified()->LoadField(field_access), storage, effect, control);
2527
2528 Node* check = graph()->NewNode(simplified()->SameValueNumbersOnly(),
2529 current_value, value);
2530 effect = graph()->NewNode(
2531 simplified()->CheckIf(DeoptimizeReason::kWrongValue), check,
2532 effect, control);
2533 return ValueEffectControl(value, effect, control);
2534 }
2535
2536 if (field_representation == MachineRepresentation::kTaggedSigned) {
2537 value = effect = graph()->NewNode(
2538 simplified()->CheckSmi(FeedbackSource()), value, effect, control);
2539 field_access.write_barrier_kind = kNoWriteBarrier;
2540
2541 } else if (field_representation ==
2542 MachineRepresentation::kTaggedPointer) {
2543 Handle<Map> field_map;
2544 if (access_info.field_map().ToHandle(&field_map)) {
2545 // Emit a map check for the value.
2546 effect = graph()->NewNode(
2547 simplified()->CheckMaps(CheckMapsFlag::kNone,
2548 ZoneHandleSet<Map>(field_map)),
2549 value, effect, control);
2550 } else {
2551 // Ensure that {value} is a HeapObject.
2552 value = effect = graph()->NewNode(simplified()->CheckHeapObject(),
2553 value, effect, control);
2554 }
2555 field_access.write_barrier_kind = kPointerWriteBarrier;
2556
2557 } else {
2558 DCHECK(field_representation == MachineRepresentation::kTagged);
2559 }
2560 break;
2561 case MachineRepresentation::kNone:
2562 case MachineRepresentation::kBit:
2563 case MachineRepresentation::kCompressedPointer:
2564 case MachineRepresentation::kCompressed:
2565 case MachineRepresentation::kWord8:
2566 case MachineRepresentation::kWord16:
2567 case MachineRepresentation::kWord32:
2568 case MachineRepresentation::kWord64:
2569 case MachineRepresentation::kFloat32:
2570 case MachineRepresentation::kSimd128:
2571 UNREACHABLE();
2572 break;
2573 }
2574 // Check if we need to perform a transitioning store.
2575 Handle<Map> transition_map;
2576 if (access_info.transition_map().ToHandle(&transition_map)) {
2577 // Check if we need to grow the properties backing store
2578 // with this transitioning store.
2579 MapRef transition_map_ref(broker(), transition_map);
2580 MapRef original_map = transition_map_ref.GetBackPointer().AsMap();
2581 if (original_map.UnusedPropertyFields() == 0) {
2582 DCHECK(!field_index.is_inobject());
2583
2584 // Reallocate the properties {storage}.
2585 storage = effect = BuildExtendPropertiesBackingStore(
2586 original_map, storage, effect, control);
2587
2588 // Perform the actual store.
2589 effect = graph()->NewNode(simplified()->StoreField(field_access),
2590 storage, value, effect, control);
2591
2592 // Atomically switch to the new properties below.
2593 field_access = AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer();
2594 value = storage;
2595 storage = receiver;
2596 }
2597 effect = graph()->NewNode(
2598 common()->BeginRegion(RegionObservability::kObservable), effect);
2599 effect = graph()->NewNode(
2600 simplified()->StoreField(AccessBuilder::ForMap()), receiver,
2601 jsgraph()->Constant(transition_map_ref), effect, control);
2602 effect = graph()->NewNode(simplified()->StoreField(field_access), storage,
2603 value, effect, control);
2604 effect = graph()->NewNode(common()->FinishRegion(),
2605 jsgraph()->UndefinedConstant(), effect);
2606 } else {
2607 // Regular non-transitioning field store.
2608 effect = graph()->NewNode(simplified()->StoreField(field_access), storage,
2609 value, effect, control);
2610 }
2611 }
2612
2613 return ValueEffectControl(value, effect, control);
2614}
2615
2616Reduction JSNativeContextSpecialization::ReduceJSStoreDataPropertyInLiteral(
2617 Node* node) {
2618 JSStoreDataPropertyInLiteralNode n(node);
2619 FeedbackParameter const& p = n.Parameters();
2620 if (!p.feedback().IsValid()) return NoChange();
2621
2622 NumberMatcher mflags(n.flags());
2623 CHECK(mflags.HasResolvedValue());
2624 DataPropertyInLiteralFlags cflags(mflags.ResolvedValue());
2625 DCHECK(!(cflags & DataPropertyInLiteralFlag::kDontEnum));
2626 if (cflags & DataPropertyInLiteralFlag::kSetFunctionName) return NoChange();
2627
2628 return ReducePropertyAccess(node, n.name(), base::nullopt, n.value(),
2629 FeedbackSource(p.feedback()),
2630 AccessMode::kStoreInLiteral);
2631}
2632
2633Reduction JSNativeContextSpecialization::ReduceJSStoreInArrayLiteral(
2634 Node* node) {
2635 JSStoreInArrayLiteralNode n(node);
2636 FeedbackParameter const& p = n.Parameters();
2637 if (!p.feedback().IsValid()) return NoChange();
2638 return ReducePropertyAccess(node, n.index(), base::nullopt, n.value(),
2639 FeedbackSource(p.feedback()),
2640 AccessMode::kStoreInLiteral);
2641}
2642
2643Reduction JSNativeContextSpecialization::ReduceJSToObject(Node* node) {
2644 DCHECK_EQ(IrOpcode::kJSToObject, node->opcode());
2645 Node* receiver = NodeProperties::GetValueInput(node, 0);
2646 Node* effect = NodeProperties::GetEffectInput(node);
2647
2648 MapInference inference(broker(), receiver, effect);
2649 if (!inference.HaveMaps() || !inference.AllOfInstanceTypesAreJSReceiver()) {
2650 return NoChange();
2651 }
2652
2653 ReplaceWithValue(node, receiver, effect);
2654 return Replace(receiver);
2655}
2656
2657namespace {
2658
2659ExternalArrayType GetArrayTypeFromElementsKind(ElementsKind kind) {
2660 switch (kind) {
2661#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
2662 case TYPE##_ELEMENTS: \
2663 return kExternal##Type##Array;
2664 TYPED_ARRAYS(TYPED_ARRAY_CASE)
2665#undef TYPED_ARRAY_CASE
2666 default:
2667 break;
2668 }
2669 UNREACHABLE();
2670}
2671
2672} // namespace
2673
2674JSNativeContextSpecialization::ValueEffectControl
2675JSNativeContextSpecialization::BuildElementAccess(
2676 Node* receiver, Node* index, Node* value, Node* effect, Node* control,
2677 ElementAccessInfo const& access_info, KeyedAccessMode const& keyed_mode) {
2678 // TODO(bmeurer): We currently specialize based on elements kind. We should
2679 // also be able to properly support strings and other JSObjects here.
2680 ElementsKind elements_kind = access_info.elements_kind();
2681 ZoneVector<Handle<Map>> const& receiver_maps =
2682 access_info.lookup_start_object_maps();
2683
2684 if (IsTypedArrayElementsKind(elements_kind)) {
2685 Node* buffer_or_receiver = receiver;
2686 Node* length;
2687 Node* base_pointer;
2688 Node* external_pointer;
2689
2690 // Check if we can constant-fold information about the {receiver} (e.g.
2691 // for asm.js-like code patterns).
2692 base::Optional<JSTypedArrayRef> typed_array =
2693 GetTypedArrayConstant(broker(), receiver);
2694 if (typed_array.has_value()) {
2695 length = jsgraph()->Constant(static_cast<double>(typed_array->length()));
2696
2697 DCHECK(!typed_array->is_on_heap());
2698 // Load the (known) data pointer for the {receiver} and set {base_pointer}
2699 // and {external_pointer} to the values that will allow to generate typed
2700 // element accesses using the known data pointer.
2701 // The data pointer might be invalid if the {buffer} was detached,
2702 // so we need to make sure that any access is properly guarded.
2703 base_pointer = jsgraph()->ZeroConstant();
2704 external_pointer = jsgraph()->PointerConstant(typed_array->data_ptr());
2705 } else {
2706 // Load the {receiver}s length.
2707 length = effect = graph()->NewNode(
2708 simplified()->LoadField(AccessBuilder::ForJSTypedArrayLength()),
2709 receiver, effect, control);
2710
2711 // Load the base pointer for the {receiver}. This will always be Smi
2712 // zero unless we allow on-heap TypedArrays, which is only the case
2713 // for Chrome. Node and Electron both set this limit to 0. Setting
2714 // the base to Smi zero here allows the EffectControlLinearizer to
2715 // optimize away the tricky part of the access later.
2716 if (JSTypedArray::kMaxSizeInHeap == 0) {
2717 base_pointer = jsgraph()->ZeroConstant();
2718 } else {
2719 base_pointer = effect =
2720 graph()->NewNode(simplified()->LoadField(
2721 AccessBuilder::ForJSTypedArrayBasePointer()),
2722 receiver, effect, control);
2723 }
2724
2725 // Load the external pointer for the {receiver}.
2726 external_pointer = effect =
2727 graph()->NewNode(simplified()->LoadField(
2728 AccessBuilder::ForJSTypedArrayExternalPointer()),
2729 receiver, effect, control);
2730 }
2731
2732 // See if we can skip the detaching check.
2733 if (!dependencies()->DependOnArrayBufferDetachingProtector()) {
2734 // Load the buffer for the {receiver}.
2735 Node* buffer =
2736 typed_array.has_value()
2737 ? jsgraph()->Constant(typed_array->buffer())
2738 : (effect = graph()->NewNode(
2739 simplified()->LoadField(
2740 AccessBuilder::ForJSArrayBufferViewBuffer()),
2741 receiver, effect, control));
2742
2743 // Deopt if the {buffer} was detached.
2744 // Note: A detached buffer leads to megamorphic feedback.
2745 Node* buffer_bit_field = effect = graph()->NewNode(
2746 simplified()->LoadField(AccessBuilder::ForJSArrayBufferBitField()),
2747 buffer, effect, control);
2748 Node* check = graph()->NewNode(
2749 simplified()->NumberEqual(),
2750 graph()->NewNode(
2751 simplified()->NumberBitwiseAnd(), buffer_bit_field,
2752 jsgraph()->Constant(JSArrayBuffer::WasDetachedBit::kMask)),
2753 jsgraph()->ZeroConstant());
2754 effect = graph()->NewNode(
2755 simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasDetached),
2756 check, effect, control);
2757
2758 // Retain the {buffer} instead of {receiver} to reduce live ranges.
2759 buffer_or_receiver = buffer;
2760 }
2761
2762 enum Situation { kBoundsCheckDone, kHandleOOB_SmiCheckDone };
2763 Situation situation;
2764 if ((keyed_mode.IsLoad() &&
2765 keyed_mode.load_mode() == LOAD_IGNORE_OUT_OF_BOUNDS) ||
2766 (keyed_mode.IsStore() &&
2767 keyed_mode.store_mode() == STORE_IGNORE_OUT_OF_BOUNDS)) {
2768 // Only check that the {index} is in SignedSmall range. We do the actual
2769 // bounds check below and just skip the property access if it's out of
2770 // bounds for the {receiver}.
2771 index = effect = graph()->NewNode(
2772 simplified()->CheckSmi(FeedbackSource()), index, effect, control);
2773
2774 // Cast the {index} to Unsigned32 range, so that the bounds checks
2775 // below are performed on unsigned values, which means that all the
2776 // Negative32 values are treated as out-of-bounds.
2777 index = graph()->NewNode(simplified()->NumberToUint32(), index);
2778 situation = kHandleOOB_SmiCheckDone;
2779 } else {
2780 // Check that the {index} is in the valid range for the {receiver}.
2781 index = effect = graph()->NewNode(
2782 simplified()->CheckBounds(
2783 FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero),
2784 index, length, effect, control);
2785 situation = kBoundsCheckDone;
2786 }
2787
2788 // Access the actual element.
2789 ExternalArrayType external_array_type =
2790 GetArrayTypeFromElementsKind(elements_kind);
2791 switch (keyed_mode.access_mode()) {
2792 case AccessMode::kLoad: {
2793 // Check if we can return undefined for out-of-bounds loads.
2794 if (situation == kHandleOOB_SmiCheckDone) {
2795 Node* check =
2796 graph()->NewNode(simplified()->NumberLessThan(), index, length);
2797 Node* branch = graph()->NewNode(
2798 common()->Branch(BranchHint::kTrue,
2799 IsSafetyCheck::kCriticalSafetyCheck),
2800 check, control);
2801
2802 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
2803 Node* etrue = effect;
2804 Node* vtrue;
2805 {
2806 // Do a real bounds check against {length}. This is in order to
2807 // protect against a potential typer bug leading to the elimination
2808 // of the NumberLessThan above.
2809 index = etrue = graph()->NewNode(
2810 simplified()->CheckBounds(
2811 FeedbackSource(),
2812 CheckBoundsFlag::kConvertStringAndMinusZero |
2813 CheckBoundsFlag::kAbortOnOutOfBounds),
2814 index, length, etrue, if_true);
2815
2816 // Perform the actual load
2817 vtrue = etrue = graph()->NewNode(
2818 simplified()->LoadTypedElement(external_array_type),
2819 buffer_or_receiver, base_pointer, external_pointer, index,
2820 etrue, if_true);
2821 }
2822
2823 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
2824 Node* efalse = effect;
2825 Node* vfalse;
2826 {
2827 // Materialize undefined for out-of-bounds loads.
2828 vfalse = jsgraph()->UndefinedConstant();
2829 }
2830
2831 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
2832 effect =
2833 graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
2834 value =
2835 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
2836 vtrue, vfalse, control);
2837 } else {
2838 // Perform the actual load.
2839 DCHECK_EQ(kBoundsCheckDone, situation);
2840 value = effect = graph()->NewNode(
2841 simplified()->LoadTypedElement(external_array_type),
2842 buffer_or_receiver, base_pointer, external_pointer, index, effect,
2843 control);
2844 }
2845 break;
2846 }
2847 case AccessMode::kStoreInLiteral:
2848 UNREACHABLE();
2849 break;
2850 case AccessMode::kStore: {
2851 // Ensure that the {value} is actually a Number or an Oddball,
2852 // and truncate it to a Number appropriately.
2853 value = effect = graph()->NewNode(
2854 simplified()->SpeculativeToNumber(
2855 NumberOperationHint::kNumberOrOddball, FeedbackSource()),
2856 value, effect, control);
2857
2858 // Introduce the appropriate truncation for {value}. Currently we
2859 // only need to do this for ClamedUint8Array {receiver}s, as the
2860 // other truncations are implicit in the StoreTypedElement, but we
2861 // might want to change that at some point.
2862 if (external_array_type == kExternalUint8ClampedArray) {
2863 value = graph()->NewNode(simplified()->NumberToUint8Clamped(), value);
2864 }
2865
2866 if (situation == kHandleOOB_SmiCheckDone) {
2867 // We have to detect OOB stores and handle them without deopt (by
2868 // simply not performing them).
2869 Node* check =
2870 graph()->NewNode(simplified()->NumberLessThan(), index, length);
2871 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
2872 check, control);
2873
2874 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
2875 Node* etrue = effect;
2876 {
2877 // Do a real bounds check against {length}. This is in order to
2878 // protect against a potential typer bug leading to the elimination
2879 // of the NumberLessThan above.
2880 index = etrue = graph()->NewNode(
2881 simplified()->CheckBounds(
2882 FeedbackSource(),
2883 CheckBoundsFlag::kConvertStringAndMinusZero |
2884 CheckBoundsFlag::kAbortOnOutOfBounds),
2885 index, length, etrue, if_true);
2886
2887 // Perform the actual store.
2888 etrue = graph()->NewNode(
2889 simplified()->StoreTypedElement(external_array_type),
2890 buffer_or_receiver, base_pointer, external_pointer, index,
2891 value, etrue, if_true);
2892 }
2893
2894 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
2895 Node* efalse = effect;
2896 {
2897 // Just ignore the out-of-bounds write.
2898 }
2899
2900 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
2901 effect =
2902 graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
2903 } else {
2904 // Perform the actual store
2905 DCHECK_EQ(kBoundsCheckDone, situation);
2906 effect = graph()->NewNode(
2907 simplified()->StoreTypedElement(external_array_type),
2908 buffer_or_receiver, base_pointer, external_pointer, index, value,
2909 effect, control);
2910 }
2911 break;
2912 }
2913 case AccessMode::kHas:
2914 if (situation == kHandleOOB_SmiCheckDone) {
2915 value = effect =
2916 graph()->NewNode(simplified()->SpeculativeNumberLessThan(
2917 NumberOperationHint::kSignedSmall),
2918 index, length, effect, control);
2919 } else {
2920 DCHECK_EQ(kBoundsCheckDone, situation);
2921 // For has-property on a typed array, all we need is a bounds check.
2922 value = jsgraph()->TrueConstant();
2923 }
2924 break;
2925 }
2926 } else {
2927 // Load the elements for the {receiver}.
2928 Node* elements = effect = graph()->NewNode(
2929 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
2930 effect, control);
2931
2932 // Don't try to store to a copy-on-write backing store (unless supported by
2933 // the store mode).
2934 if (keyed_mode.access_mode() == AccessMode::kStore &&
2935 IsSmiOrObjectElementsKind(elements_kind) &&
2936 !IsCOWHandlingStoreMode(keyed_mode.store_mode())) {
2937 effect = graph()->NewNode(
2938 simplified()->CheckMaps(
2939 CheckMapsFlag::kNone,
2940 ZoneHandleSet<Map>(factory()->fixed_array_map())),
2941 elements, effect, control);
2942 }
2943
2944 // Check if the {receiver} is a JSArray.
2945 bool receiver_is_jsarray = HasOnlyJSArrayMaps(broker(), receiver_maps);
2946
2947 // Load the length of the {receiver}.
2948 Node* length = effect =
2949 receiver_is_jsarray
2950 ? graph()->NewNode(
2951 simplified()->LoadField(
2952 AccessBuilder::ForJSArrayLength(elements_kind)),
2953 receiver, effect, control)
2954 : graph()->NewNode(
2955 simplified()->LoadField(AccessBuilder::ForFixedArrayLength()),
2956 elements, effect, control);
2957
2958 // Check if we might need to grow the {elements} backing store.
2959 if (keyed_mode.IsStore() && IsGrowStoreMode(keyed_mode.store_mode())) {
2960 // For growing stores we validate the {index} below.
2961 } else if (keyed_mode.IsLoad() &&
2962 keyed_mode.load_mode() == LOAD_IGNORE_OUT_OF_BOUNDS &&
2963 CanTreatHoleAsUndefined(receiver_maps)) {
2964 // Check that the {index} is a valid array index, we do the actual
2965 // bounds check below and just skip the store below if it's out of
2966 // bounds for the {receiver}.
2967 index = effect = graph()->NewNode(
2968 simplified()->CheckBounds(
2969 FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero),
2970 index, jsgraph()->Constant(Smi::kMaxValue), effect, control);
2971 } else {
2972 // Check that the {index} is in the valid range for the {receiver}.
2973 index = effect = graph()->NewNode(
2974 simplified()->CheckBounds(
2975 FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero),
2976 index, length, effect, control);
2977 }
2978
2979 // Compute the element access.
2980 Type element_type = Type::NonInternal();
2981 MachineType element_machine_type = MachineType::AnyTagged();
2982 if (IsDoubleElementsKind(elements_kind)) {
2983 element_type = Type::Number();
2984 element_machine_type = MachineType::Float64();
2985 } else if (IsSmiElementsKind(elements_kind)) {
2986 element_type = Type::SignedSmall();
2987 element_machine_type = MachineType::TaggedSigned();
2988 }
2989 ElementAccess element_access = {
2990 kTaggedBase, FixedArray::kHeaderSize,
2991 element_type, element_machine_type,
2992 kFullWriteBarrier, LoadSensitivity::kCritical};
2993
2994 // Access the actual element.
2995 if (keyed_mode.access_mode() == AccessMode::kLoad) {
2996 // Compute the real element access type, which includes the hole in case
2997 // of holey backing stores.
2998 if (IsHoleyElementsKind(elements_kind)) {
2999 element_access.type =
3000 Type::Union(element_type, Type::Hole(), graph()->zone());
3001 }
3002 if (elements_kind == HOLEY_ELEMENTS ||
3003 elements_kind == HOLEY_SMI_ELEMENTS) {
3004 element_access.machine_type = MachineType::AnyTagged();
3005 }
3006
3007 // Check if we can return undefined for out-of-bounds loads.
3008 if (keyed_mode.load_mode() == LOAD_IGNORE_OUT_OF_BOUNDS &&
3009 CanTreatHoleAsUndefined(receiver_maps)) {
3010 Node* check =
3011 graph()->NewNode(simplified()->NumberLessThan(), index, length);
3012 Node* branch = graph()->NewNode(
3013 common()->Branch(BranchHint::kTrue,
3014 IsSafetyCheck::kCriticalSafetyCheck),
3015 check, control);
3016
3017 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
3018 Node* etrue = effect;
3019 Node* vtrue;
3020 {
3021 // Do a real bounds check against {length}. This is in order to
3022 // protect against a potential typer bug leading to the elimination of
3023 // the NumberLessThan above.
3024 index = etrue =
3025 graph()->NewNode(simplified()->CheckBounds(
3026 FeedbackSource(),
3027 CheckBoundsFlag::kConvertStringAndMinusZero |
3028 CheckBoundsFlag::kAbortOnOutOfBounds),
3029 index, length, etrue, if_true);
3030
3031 // Perform the actual load
3032 vtrue = etrue =
3033 graph()->NewNode(simplified()->LoadElement(element_access),
3034 elements, index, etrue, if_true);
3035
3036 // Handle loading from holey backing stores correctly, by either
3037 // mapping the hole to undefined if possible, or deoptimizing
3038 // otherwise.
3039 if (elements_kind == HOLEY_ELEMENTS ||
3040 elements_kind == HOLEY_SMI_ELEMENTS) {
3041 // Turn the hole into undefined.
3042 vtrue = graph()->NewNode(
3043 simplified()->ConvertTaggedHoleToUndefined(), vtrue);
3044 } else if (elements_kind == HOLEY_DOUBLE_ELEMENTS) {
3045 // Return the signaling NaN hole directly if all uses are
3046 // truncating.
3047 vtrue = etrue = graph()->NewNode(
3048 simplified()->CheckFloat64Hole(
3049 CheckFloat64HoleMode::kAllowReturnHole, FeedbackSource()),
3050 vtrue, etrue, if_true);
3051 }
3052 }
3053
3054 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
3055 Node* efalse = effect;
3056 Node* vfalse;
3057 {
3058 // Materialize undefined for out-of-bounds loads.
3059 vfalse = jsgraph()->UndefinedConstant();
3060 }
3061
3062 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
3063 effect =
3064 graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
3065 value =
3066 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
3067 vtrue, vfalse, control);
3068 } else {
3069 // Perform the actual load.
3070 value = effect =
3071 graph()->NewNode(simplified()->LoadElement(element_access),
3072 elements, index, effect, control);
3073
3074 // Handle loading from holey backing stores correctly, by either mapping
3075 // the hole to undefined if possible, or deoptimizing otherwise.
3076 if (elements_kind == HOLEY_ELEMENTS ||
3077 elements_kind == HOLEY_SMI_ELEMENTS) {
3078 // Check if we are allowed to turn the hole into undefined.
3079 if (CanTreatHoleAsUndefined(receiver_maps)) {
3080 // Turn the hole into undefined.
3081 value = graph()->NewNode(
3082 simplified()->ConvertTaggedHoleToUndefined(), value);
3083 } else {
3084 // Bailout if we see the hole.
3085 value = effect = graph()->NewNode(
3086 simplified()->CheckNotTaggedHole(), value, effect, control);
3087 }
3088 } else if (elements_kind == HOLEY_DOUBLE_ELEMENTS) {
3089 // Perform the hole check on the result.
3090 CheckFloat64HoleMode mode = CheckFloat64HoleMode::kNeverReturnHole;
3091 // Check if we are allowed to return the hole directly.
3092 if (CanTreatHoleAsUndefined(receiver_maps)) {
3093 // Return the signaling NaN hole directly if all uses are
3094 // truncating.
3095 mode = CheckFloat64HoleMode::kAllowReturnHole;
3096 }
3097 value = effect = graph()->NewNode(
3098 simplified()->CheckFloat64Hole(mode, FeedbackSource()), value,
3099 effect, control);
3100 }
3101 }
3102 } else if (keyed_mode.access_mode() == AccessMode::kHas) {
3103 // For packed arrays with NoElementsProctector valid, a bound check
3104 // is equivalent to HasProperty.
3105 value = effect = graph()->NewNode(simplified()->SpeculativeNumberLessThan(
3106 NumberOperationHint::kSignedSmall),
3107 index, length, effect, control);
3108 if (IsHoleyElementsKind(elements_kind)) {
3109 // If the index is in bounds, do a load and hole check.
3110
3111 Node* branch = graph()->NewNode(common()->Branch(), value, control);
3112
3113 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
3114 Node* efalse = effect;
3115 Node* vfalse = jsgraph()->FalseConstant();
3116
3117 element_access.type =
3118 Type::Union(element_type, Type::Hole(), graph()->zone());
3119
3120 if (elements_kind == HOLEY_ELEMENTS ||
3121 elements_kind == HOLEY_SMI_ELEMENTS) {
3122 element_access.machine_type = MachineType::AnyTagged();
3123 }
3124
3125 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
3126 Node* etrue = effect;
3127
3128 Node* checked = etrue = graph()->NewNode(
3129 simplified()->CheckBounds(
3130 FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero),
3131 index, length, etrue, if_true);
3132
3133 Node* element = etrue =
3134 graph()->NewNode(simplified()->LoadElement(element_access),
3135 elements, checked, etrue, if_true);
3136
3137 Node* vtrue;
3138 if (CanTreatHoleAsUndefined(receiver_maps)) {
3139 if (elements_kind == HOLEY_ELEMENTS ||
3140 elements_kind == HOLEY_SMI_ELEMENTS) {
3141 // Check if we are allowed to turn the hole into undefined.
3142 // Turn the hole into undefined.
3143 vtrue = graph()->NewNode(simplified()->ReferenceEqual(), element,
3144 jsgraph()->TheHoleConstant());
3145 } else {
3146 vtrue =
3147 graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
3148 }
3149
3150 // has == !IsHole
3151 vtrue = graph()->NewNode(simplified()->BooleanNot(), vtrue);
3152 } else {
3153 if (elements_kind == HOLEY_ELEMENTS ||
3154 elements_kind == HOLEY_SMI_ELEMENTS) {
3155 // Bailout if we see the hole.
3156 etrue = graph()->NewNode(simplified()->CheckNotTaggedHole(),
3157 element, etrue, if_true);
3158 } else {
3159 etrue = graph()->NewNode(
3160 simplified()->CheckFloat64Hole(
3161 CheckFloat64HoleMode::kNeverReturnHole, FeedbackSource()),
3162 element, etrue, if_true);
3163 }
3164
3165 vtrue = jsgraph()->TrueConstant();
3166 }
3167
3168 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
3169 effect =
3170 graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
3171 value =
3172 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
3173 vtrue, vfalse, control);
3174 }
3175 } else {
3176 DCHECK(keyed_mode.access_mode() == AccessMode::kStore ||
3177 keyed_mode.access_mode() == AccessMode::kStoreInLiteral);
3178
3179 if (IsSmiElementsKind(elements_kind)) {
3180 value = effect = graph()->NewNode(
3181 simplified()->CheckSmi(FeedbackSource()), value, effect, control);
3182 } else if (IsDoubleElementsKind(elements_kind)) {
3183 value = effect =
3184 graph()->NewNode(simplified()->CheckNumber(FeedbackSource()), value,
3185 effect, control);
3186 // Make sure we do not store signalling NaNs into double arrays.
3187 value = graph()->NewNode(simplified()->NumberSilenceNaN(), value);
3188 }
3189
3190 // Ensure that copy-on-write backing store is writable.
3191 if (IsSmiOrObjectElementsKind(elements_kind) &&
3192 keyed_mode.store_mode() == STORE_HANDLE_COW) {
3193 elements = effect =
3194 graph()->NewNode(simplified()->EnsureWritableFastElements(),
3195 receiver, elements, effect, control);
3196 } else if (IsGrowStoreMode(keyed_mode.store_mode())) {
3197 // Determine the length of the {elements} backing store.
3198 Node* elements_length = effect = graph()->NewNode(
3199 simplified()->LoadField(AccessBuilder::ForFixedArrayLength()),
3200 elements, effect, control);
3201
3202 // Validate the {index} depending on holeyness:
3203 //
3204 // For HOLEY_*_ELEMENTS the {index} must not exceed the {elements}
3205 // backing store capacity plus the maximum allowed gap, as otherwise
3206 // the (potential) backing store growth would normalize and thus
3207 // the elements kind of the {receiver} would change to slow mode.
3208 //
3209 // For PACKED_*_ELEMENTS the {index} must be within the range
3210 // [0,length+1[ to be valid. In case {index} equals {length},
3211 // the {receiver} will be extended, but kept packed.
3212 Node* limit =
3213 IsHoleyElementsKind(elements_kind)
3214 ? graph()->NewNode(simplified()->NumberAdd(), elements_length,
3215 jsgraph()->Constant(JSObject::kMaxGap))
3216 : graph()->NewNode(simplified()->NumberAdd(), length,
3217 jsgraph()->OneConstant());
3218 index = effect = graph()->NewNode(
3219 simplified()->CheckBounds(
3220 FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero),
3221 index, limit, effect, control);
3222
3223 // Grow {elements} backing store if necessary.
3224 GrowFastElementsMode mode =
3225 IsDoubleElementsKind(elements_kind)
3226 ? GrowFastElementsMode::kDoubleElements
3227 : GrowFastElementsMode::kSmiOrObjectElements;
3228 elements = effect = graph()->NewNode(
3229 simplified()->MaybeGrowFastElements(mode, FeedbackSource()),
3230 receiver, elements, index, elements_length, effect, control);
3231
3232 // If we didn't grow {elements}, it might still be COW, in which case we
3233 // copy it now.
3234 if (IsSmiOrObjectElementsKind(elements_kind) &&
3235 keyed_mode.store_mode() == STORE_AND_GROW_HANDLE_COW) {
3236 elements = effect =
3237 graph()->NewNode(simplified()->EnsureWritableFastElements(),
3238 receiver, elements, effect, control);
3239 }
3240
3241 // Also update the "length" property if {receiver} is a JSArray.
3242 if (receiver_is_jsarray) {
3243 Node* check =
3244 graph()->NewNode(simplified()->NumberLessThan(), index, length);
3245 Node* branch = graph()->NewNode(common()->Branch(), check, control);
3246
3247 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
3248 Node* etrue = effect;
3249 {
3250 // We don't need to do anything, the {index} is within
3251 // the valid bounds for the JSArray {receiver}.
3252 }
3253
3254 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
3255 Node* efalse = effect;
3256 {
3257 // Update the JSArray::length field. Since this is observable,
3258 // there must be no other check after this.
3259 Node* new_length = graph()->NewNode(
3260 simplified()->NumberAdd(), index, jsgraph()->OneConstant());
3261 efalse = graph()->NewNode(
3262 simplified()->StoreField(
3263 AccessBuilder::ForJSArrayLength(elements_kind)),
3264 receiver, new_length, efalse, if_false);
3265 }
3266
3267 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
3268 effect =
3269 graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
3270 }
3271 }
3272
3273 // Perform the actual element access.
3274 effect = graph()->NewNode(simplified()->StoreElement(element_access),
3275 elements, index, value, effect, control);
3276 }
3277 }
3278
3279 return ValueEffectControl(value, effect, control);
3280}
3281
3282Node* JSNativeContextSpecialization::BuildIndexedStringLoad(
3283 Node* receiver, Node* index, Node* length, Node** effect, Node** control,
3284 KeyedAccessLoadMode load_mode) {
3285 if (load_mode == LOAD_IGNORE_OUT_OF_BOUNDS &&
3286 dependencies()->DependOnNoElementsProtector()) {
3287 // Ensure that the {index} is a valid String length.
3288 index = *effect = graph()->NewNode(
3289 simplified()->CheckBounds(FeedbackSource(),
3290 CheckBoundsFlag::kConvertStringAndMinusZero),
3291 index, jsgraph()->Constant(String::kMaxLength), *effect, *control);
3292
3293 // Load the single character string from {receiver} or yield
3294 // undefined if the {index} is not within the valid bounds.
3295 Node* check =
3296 graph()->NewNode(simplified()->NumberLessThan(), index, length);
3297 Node* branch =
3298 graph()->NewNode(common()->Branch(BranchHint::kTrue,
3299 IsSafetyCheck::kCriticalSafetyCheck),
3300 check, *control);
3301
3302 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
3303 // Do a real bounds check against {length}. This is in order to protect
3304 // against a potential typer bug leading to the elimination of the
3305 // NumberLessThan above.
3306 Node* etrue = index = graph()->NewNode(
3307 simplified()->CheckBounds(FeedbackSource(),
3308 CheckBoundsFlag::kConvertStringAndMinusZero |
3309 CheckBoundsFlag::kAbortOnOutOfBounds),
3310 index, length, *effect, if_true);
3311 Node* masked_index = graph()->NewNode(simplified()->PoisonIndex(), index);
3312 Node* vtrue = etrue =
3313 graph()->NewNode(simplified()->StringCharCodeAt(), receiver,
3314 masked_index, etrue, if_true);
3315 vtrue = graph()->NewNode(simplified()->StringFromSingleCharCode(), vtrue);
3316
3317 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
3318 Node* vfalse = jsgraph()->UndefinedConstant();
3319
3320 *control = graph()->NewNode(common()->Merge(2), if_true, if_false);
3321 *effect =
3322 graph()->NewNode(common()->EffectPhi(2), etrue, *effect, *control);
3323 return graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
3324 vtrue, vfalse, *control);
3325 } else {
3326 // Ensure that {index} is less than {receiver} length.
3327 index = *effect = graph()->NewNode(
3328 simplified()->CheckBounds(FeedbackSource(),
3329 CheckBoundsFlag::kConvertStringAndMinusZero),
3330 index, length, *effect, *control);
3331
3332 Node* masked_index = graph()->NewNode(simplified()->PoisonIndex(), index);
3333
3334 // Return the character from the {receiver} as single character string.
3335 Node* value = *effect =
3336 graph()->NewNode(simplified()->StringCharCodeAt(), receiver,
3337 masked_index, *effect, *control);
3338 value = graph()->NewNode(simplified()->StringFromSingleCharCode(), value);
3339 return value;
3340 }
3341}
3342
3343Node* JSNativeContextSpecialization::BuildExtendPropertiesBackingStore(
3344 const MapRef& map, Node* properties, Node* effect, Node* control) {
3345 // TODO(bmeurer/jkummerow): Property deletions can undo map transitions
3346 // while keeping the backing store around, meaning that even though the
3347 // map might believe that objects have no unused property fields, there
3348 // might actually be some. It would be nice to not create a new backing
3349 // store in that case (i.e. when properties->length() >= new_length).
3350 // However, introducing branches and Phi nodes here would make it more
3351 // difficult for escape analysis to get rid of the backing stores used
3352 // for intermediate states of chains of property additions. That makes
3353 // it unclear what the best approach is here.
3354 DCHECK_EQ(0, map.UnusedPropertyFields());
3355 // Compute the length of the old {properties} and the new properties.
3356 int length = map.NextFreePropertyIndex() - map.GetInObjectProperties();
3357 int new_length = length + JSObject::kFieldsAdded;
3358 // Collect the field values from the {properties}.
3359 ZoneVector<Node*> values(zone());
3360 values.reserve(new_length);
3361 for (int i = 0; i < length; ++i) {
3362 Node* value = effect = graph()->NewNode(
3363 simplified()->LoadField(AccessBuilder::ForFixedArraySlot(i)),
3364 properties, effect, control);
3365 values.push_back(value);
3366 }
3367 // Initialize the new fields to undefined.
3368 for (int i = 0; i < JSObject::kFieldsAdded; ++i) {
3369 values.push_back(jsgraph()->UndefinedConstant());
3370 }
3371
3372 // Compute new length and hash.
3373 Node* hash;
3374 if (length == 0) {
3375 hash = graph()->NewNode(
3376 common()->Select(MachineRepresentation::kTaggedSigned),
3377 graph()->NewNode(simplified()->ObjectIsSmi(), properties), properties,
3378 jsgraph()->SmiConstant(PropertyArray::kNoHashSentinel));
3379 hash = effect = graph()->NewNode(common()->TypeGuard(Type::SignedSmall()),
3380 hash, effect, control);
3381 hash =
3382 graph()->NewNode(simplified()->NumberShiftLeft(), hash,
3383 jsgraph()->Constant(PropertyArray::HashField::kShift));
3384 } else {
3385 hash = effect = graph()->NewNode(
3386 simplified()->LoadField(AccessBuilder::ForPropertyArrayLengthAndHash()),
3387 properties, effect, control);
3388 hash =
3389 graph()->NewNode(simplified()->NumberBitwiseAnd(), hash,
3390 jsgraph()->Constant(PropertyArray::HashField::kMask));
3391 }
3392 Node* new_length_and_hash = graph()->NewNode(
3393 simplified()->NumberBitwiseOr(), jsgraph()->Constant(new_length), hash);
3394 // TDOO(jarin): Fix the typer to infer tighter bound for NumberBitwiseOr.
3395 new_length_and_hash = effect =
3396 graph()->NewNode(common()->TypeGuard(Type::SignedSmall()),
3397 new_length_and_hash, effect, control);
3398
3399 // Allocate and initialize the new properties.
3400 AllocationBuilder a(jsgraph(), effect, control);
3401 a.Allocate(PropertyArray::SizeFor(new_length), AllocationType::kYoung,
3402 Type::OtherInternal());
3403 a.Store(AccessBuilder::ForMap(), jsgraph()->PropertyArrayMapConstant());
3404 a.Store(AccessBuilder::ForPropertyArrayLengthAndHash(), new_length_and_hash);
3405 for (int i = 0; i < new_length; ++i) {
3406 a.Store(AccessBuilder::ForFixedArraySlot(i), values[i]);
3407 }
3408 return a.Finish();
3409}
3410
3411Node* JSNativeContextSpecialization::BuildCheckEqualsName(NameRef const& name,
3412 Node* value,
3413 Node* effect,
3414 Node* control) {
3415 DCHECK(name.IsUniqueName());
3416 Operator const* const op =
3417 name.IsSymbol() ? simplified()->CheckEqualsSymbol()
3418 : simplified()->CheckEqualsInternalizedString();
3419 return graph()->NewNode(op, jsgraph()->Constant(name), value, effect,
3420 control);
3421}
3422
3423bool JSNativeContextSpecialization::CanTreatHoleAsUndefined(
3424 ZoneVector<Handle<Map>> const& receiver_maps) {
3425 // Check if all {receiver_maps} have one of the initial Array.prototype
3426 // or Object.prototype objects as their prototype (in any of the current
3427 // native contexts, as the global Array protector works isolate-wide).
3428 for (Handle<Map> map : receiver_maps) {
3429 MapRef receiver_map(broker(), map);
3430 ObjectRef receiver_prototype = receiver_map.prototype();
3431 if (!receiver_prototype.IsJSObject() ||
3432 !broker()->IsArrayOrObjectPrototype(receiver_prototype.AsJSObject())) {
3433 return false;
3434 }
3435 }
3436
3437 // Check if the array prototype chain is intact.
3438 return dependencies()->DependOnNoElementsProtector();
3439}
3440
3441bool JSNativeContextSpecialization::InferMaps(
3442 Node* object, Node* effect, ZoneVector<Handle<Map>>* maps) const {
3443 ZoneHandleSet<Map> map_set;
3444 NodeProperties::InferMapsResult result =
3445 NodeProperties::InferMapsUnsafe(broker(), object, effect, &map_set);
3446 if (result == NodeProperties::kReliableMaps) {
3447 for (size_t i = 0; i < map_set.size(); ++i) {
3448 maps->push_back(map_set[i]);
3449 }
3450 return true;
3451 } else if (result == NodeProperties::kUnreliableMaps) {
3452 // For untrusted maps, we can still use the information
3453 // if the maps are stable.
3454 for (size_t i = 0; i < map_set.size(); ++i) {
3455 MapRef map(broker(), map_set[i]);
3456 if (!map.is_stable()) return false;
3457 }
3458 for (size_t i = 0; i < map_set.size(); ++i) {
3459 maps->push_back(map_set[i]);
3460 }
3461 return true;
3462 }
3463 return false;
3464}
3465
3466base::Optional<MapRef> JSNativeContextSpecialization::InferRootMap(
3467 Node* object) const {
3468 HeapObjectMatcher m(object);
3469 if (m.HasResolvedValue()) {
3470 MapRef map = m.Ref(broker()).map();
3471 return map.FindRootMap();
3472 } else if (m.IsJSCreate()) {
3473 base::Optional<MapRef> initial_map =
3474 NodeProperties::GetJSCreateMap(broker(), object);
3475 if (initial_map.has_value()) {
3476 if (!initial_map->FindRootMap().has_value()) {
3477 return base::nullopt;
3478 }
3479 DCHECK(initial_map->equals(*initial_map->FindRootMap()));
3480 return *initial_map;
3481 }
3482 }
3483 return base::nullopt;
3484}
3485
3486Node* JSNativeContextSpecialization::BuildLoadPrototypeFromObject(
3487 Node* object, Node* effect, Node* control) {
3488 Node* map = effect =
3489 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), object,
3490 effect, control);
3491 return graph()->NewNode(
3492 simplified()->LoadField(AccessBuilder::ForMapPrototype()), map, effect,
3493 control);
3494}
3495
3496Graph* JSNativeContextSpecialization::graph() const {
3497 return jsgraph()->graph();
3498}
3499
3500Isolate* JSNativeContextSpecialization::isolate() const {
3501 return jsgraph()->isolate();
3502}
3503
3504Factory* JSNativeContextSpecialization::factory() const {
3505 return isolate()->factory();
3506}
3507
3508CommonOperatorBuilder* JSNativeContextSpecialization::common() const {
3509 return jsgraph()->common();
3510}
3511
3512JSOperatorBuilder* JSNativeContextSpecialization::javascript() const {
3513 return jsgraph()->javascript();
3514}
3515
3516SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const {
3517 return jsgraph()->simplified();
3518}
3519
3520} // namespace compiler
3521} // namespace internal
3522} // namespace v8