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