js-native-context-specialization.cc 142 KB
Newer Older
1 2 3 4
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
#include "src/compiler/js-native-context-specialization.h"
6

7
#include "src/api/api-inl.h"
8
#include "src/builtins/accessors.h"
9 10
#include "src/codegen/code-factory.h"
#include "src/codegen/string-constants.h"
11
#include "src/compiler/access-builder.h"
12
#include "src/compiler/access-info.h"
13
#include "src/compiler/allocation-builder.h"
14
#include "src/compiler/compilation-dependencies.h"
15 16
#include "src/compiler/js-graph.h"
#include "src/compiler/js-operator.h"
17
#include "src/compiler/linkage.h"
18
#include "src/compiler/map-inference.h"
19
#include "src/compiler/node-matchers.h"
20
#include "src/compiler/property-access-builder.h"
21
#include "src/compiler/type-cache.h"
22
#include "src/execution/isolate-inl.h"
23
#include "src/numbers/dtoa.h"
24
#include "src/objects/feedback-vector.h"
25
#include "src/objects/field-index-inl.h"
26
#include "src/objects/heap-number.h"
27
#include "src/objects/js-array-buffer-inl.h"
28
#include "src/objects/js-array-inl.h"
29
#include "src/objects/templates.h"
30 31 32 33 34

namespace v8 {
namespace internal {
namespace compiler {

35 36
namespace {

37
bool HasNumberMaps(JSHeapBroker* broker, ZoneVector<Handle<Map>> const& maps) {
38
  for (auto map : maps) {
39 40
    MapRef map_ref(broker, map);
    if (map_ref.IsHeapNumberMap()) return true;
41 42 43 44
  }
  return false;
}

45 46
bool HasOnlyJSArrayMaps(JSHeapBroker* broker,
                        ZoneVector<Handle<Map>> const& maps) {
47
  for (auto map : maps) {
48 49
    MapRef map_ref(broker, map);
    if (!map_ref.IsJSArrayMap()) return false;
50 51 52 53 54 55
  }
  return true;
}

}  // namespace

56
JSNativeContextSpecialization::JSNativeContextSpecialization(
57
    Editor* editor, JSGraph* jsgraph, JSHeapBroker* broker, Flags flags,
58
    CompilationDependencies* dependencies, Zone* zone, Zone* shared_zone)
59 60
    : AdvancedReducer(editor),
      jsgraph_(jsgraph),
61
      broker_(broker),
62
      flags_(flags),
63 64 65
      global_object_(broker->target_native_context().global_object().object()),
      global_proxy_(
          broker->target_native_context().global_proxy_object().object()),
66
      dependencies_(dependencies),
67
      zone_(zone),
68
      shared_zone_(shared_zone),
69
      type_cache_(TypeCache::Get()) {}
70

71
Reduction JSNativeContextSpecialization::Reduce(Node* node) {
72 73
  DisallowHeapAccessIf disallow_heap_access(FLAG_concurrent_inlining);

74
  switch (node->opcode()) {
75 76
    case IrOpcode::kJSAdd:
      return ReduceJSAdd(node);
77 78 79 80 81 82
    case IrOpcode::kJSAsyncFunctionEnter:
      return ReduceJSAsyncFunctionEnter(node);
    case IrOpcode::kJSAsyncFunctionReject:
      return ReduceJSAsyncFunctionReject(node);
    case IrOpcode::kJSAsyncFunctionResolve:
      return ReduceJSAsyncFunctionResolve(node);
83 84
    case IrOpcode::kJSGetSuperConstructor:
      return ReduceJSGetSuperConstructor(node);
85 86
    case IrOpcode::kJSInstanceOf:
      return ReduceJSInstanceOf(node);
87 88
    case IrOpcode::kJSHasInPrototypeChain:
      return ReduceJSHasInPrototypeChain(node);
89 90
    case IrOpcode::kJSOrdinaryHasInstance:
      return ReduceJSOrdinaryHasInstance(node);
91 92 93 94
    case IrOpcode::kJSPromiseResolve:
      return ReduceJSPromiseResolve(node);
    case IrOpcode::kJSResolvePromise:
      return ReduceJSResolvePromise(node);
95 96 97 98
    case IrOpcode::kJSLoadGlobal:
      return ReduceJSLoadGlobal(node);
    case IrOpcode::kJSStoreGlobal:
      return ReduceJSStoreGlobal(node);
99 100
    case IrOpcode::kJSLoadNamed:
      return ReduceJSLoadNamed(node);
101 102
    case IrOpcode::kJSStoreNamed:
      return ReduceJSStoreNamed(node);
103 104
    case IrOpcode::kJSHasProperty:
      return ReduceJSHasProperty(node);
105 106 107 108
    case IrOpcode::kJSLoadProperty:
      return ReduceJSLoadProperty(node);
    case IrOpcode::kJSStoreProperty:
      return ReduceJSStoreProperty(node);
109 110
    case IrOpcode::kJSStoreNamedOwn:
      return ReduceJSStoreNamedOwn(node);
111 112
    case IrOpcode::kJSStoreDataPropertyInLiteral:
      return ReduceJSStoreDataPropertyInLiteral(node);
113 114
    case IrOpcode::kJSStoreInArrayLiteral:
      return ReduceJSStoreInArrayLiteral(node);
115 116
    case IrOpcode::kJSToObject:
      return ReduceJSToObject(node);
117 118
    case IrOpcode::kJSToString:
      return ReduceJSToString(node);
119 120
    case IrOpcode::kJSGetIterator:
      return ReduceJSGetIterator(node);
121 122 123 124 125 126
    default:
      break;
  }
  return NoChange();
}

127 128
// static
base::Optional<size_t> JSNativeContextSpecialization::GetMaxStringLength(
129
    JSHeapBroker* broker, Node* node) {
130 131 132 133 134
  if (node->opcode() == IrOpcode::kDelayedStringConstant) {
    return StringConstantBaseOf(node->op())->GetMaxStringConstantLength();
  }

  HeapObjectMatcher matcher(node);
135 136 137
  if (matcher.HasValue() && matcher.Ref(broker).IsString()) {
    StringRef input = matcher.Ref(broker).AsString();
    return input.length();
138 139 140 141 142 143 144 145 146 147 148 149
  }

  NumberMatcher number_matcher(node);
  if (number_matcher.HasValue()) {
    return kBase10MaximalLength + 1;
  }

  // We don't support objects with possibly monkey-patched prototype.toString
  // as it might have side-effects, so we shouldn't attempt lowering them.
  return base::nullopt;
}

150 151 152 153 154 155
Reduction JSNativeContextSpecialization::ReduceJSToString(Node* node) {
  DCHECK_EQ(IrOpcode::kJSToString, node->opcode());
  Node* const input = node->InputAt(0);
  Reduction reduction;

  HeapObjectMatcher matcher(input);
156
  if (matcher.HasValue() && matcher.Ref(broker()).IsString()) {
157 158 159 160 161 162 163 164 165 166 167
    reduction = Changed(input);  // JSToString(x:string) => x
    ReplaceWithValue(node, reduction.replacement());
    return reduction;
  }

  // TODO(turbofan): This optimization is weaker than what we used to have
  // in js-typed-lowering for OrderedNumbers. We don't have types here though,
  // so alternative approach should be designed if this causes performance
  // regressions and the stronger optimization should be re-implemented.
  NumberMatcher number_matcher(input);
  if (number_matcher.HasValue()) {
168 169 170 171
    const StringConstantBase* base =
        new (shared_zone()) NumberToStringConstant(number_matcher.Value());
    reduction =
        Replace(graph()->NewNode(common()->DelayedStringConstant(base)));
172 173 174 175 176 177 178
    ReplaceWithValue(node, reduction.replacement());
    return reduction;
  }

  return NoChange();
}

179 180 181 182 183 184 185 186 187 188
const StringConstantBase*
JSNativeContextSpecialization::CreateDelayedStringConstant(Node* node) {
  if (node->opcode() == IrOpcode::kDelayedStringConstant) {
    return StringConstantBaseOf(node->op());
  } else {
    NumberMatcher number_matcher(node);
    if (number_matcher.HasValue()) {
      return new (shared_zone()) NumberToStringConstant(number_matcher.Value());
    } else {
      HeapObjectMatcher matcher(node);
189 190
      if (matcher.HasValue() && matcher.Ref(broker()).IsString()) {
        StringRef s = matcher.Ref(broker()).AsString();
191
        return new (shared_zone())
192
            StringLiteral(s.object(), static_cast<size_t>(s.length()));
193 194 195 196 197 198 199
      } else {
        UNREACHABLE();
      }
    }
  }
}

200 201
namespace {
bool IsStringConstant(JSHeapBroker* broker, Node* node) {
202 203 204 205 206
  if (node->opcode() == IrOpcode::kDelayedStringConstant) {
    return true;
  }

  HeapObjectMatcher matcher(node);
207 208
  return matcher.HasValue() && matcher.Ref(broker).IsString();
}
209
}  // namespace
210

211 212 213 214 215 216
Reduction JSNativeContextSpecialization::ReduceJSAsyncFunctionEnter(
    Node* node) {
  DCHECK_EQ(IrOpcode::kJSAsyncFunctionEnter, node->opcode());
  Node* closure = NodeProperties::GetValueInput(node, 0);
  Node* receiver = NodeProperties::GetValueInput(node, 1);
  Node* context = NodeProperties::GetContextInput(node);
217
  Node* frame_state = NodeProperties::GetFrameStateInput(node);
218 219 220
  Node* effect = NodeProperties::GetEffectInput(node);
  Node* control = NodeProperties::GetControlInput(node);

221
  if (!dependencies()->DependOnPromiseHookProtector()) return NoChange();
222

223 224 225 226 227 228
  // Create the promise for the async function.
  Node* promise = effect =
      graph()->NewNode(javascript()->CreatePromise(), context, effect);

  // Create the JSAsyncFunctionObject based on the SharedFunctionInfo
  // extracted from the top-most frame in {frame_state}.
229 230 231 232 233 234
  SharedFunctionInfoRef shared(
      broker(),
      FrameStateInfoOf(frame_state->op()).shared_info().ToHandleChecked());
  DCHECK(shared.is_compiled());
  int register_count = shared.internal_formal_parameter_count() +
                       shared.GetBytecodeArray().register_count();
235 236 237 238 239
  Node* value = effect =
      graph()->NewNode(javascript()->CreateAsyncFunctionObject(register_count),
                       closure, receiver, promise, context, effect, control);
  ReplaceWithValue(node, value, effect, control);
  return Replace(value);
240 241 242 243 244
}

Reduction JSNativeContextSpecialization::ReduceJSAsyncFunctionReject(
    Node* node) {
  DCHECK_EQ(IrOpcode::kJSAsyncFunctionReject, node->opcode());
245
  Node* async_function_object = NodeProperties::GetValueInput(node, 0);
246 247 248 249 250 251
  Node* reason = NodeProperties::GetValueInput(node, 1);
  Node* context = NodeProperties::GetContextInput(node);
  Node* frame_state = NodeProperties::GetFrameStateInput(node);
  Node* effect = NodeProperties::GetEffectInput(node);
  Node* control = NodeProperties::GetControlInput(node);

252
  if (!dependencies()->DependOnPromiseHookProtector()) return NoChange();
253

254 255 256 257 258
  // Load the promise from the {async_function_object}.
  Node* promise = effect = graph()->NewNode(
      simplified()->LoadField(AccessBuilder::ForJSAsyncFunctionObjectPromise()),
      async_function_object, effect, control);

259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
  // Create a nested frame state inside the current method's most-recent
  // {frame_state} that will ensure that lazy deoptimizations at this
  // point will still return the {promise} instead of the result of the
  // JSRejectPromise operation (which yields undefined).
  Node* parameters[] = {promise};
  frame_state = CreateStubBuiltinContinuationFrameState(
      jsgraph(), Builtins::kAsyncFunctionLazyDeoptContinuation, context,
      parameters, arraysize(parameters), frame_state,
      ContinuationFrameStateMode::LAZY);

  // Disable the additional debug event for the rejection since a
  // debug event already happend for the exception that got us here.
  Node* debug_event = jsgraph()->FalseConstant();
  effect = graph()->NewNode(javascript()->RejectPromise(), promise, reason,
                            debug_event, context, frame_state, effect, control);
  ReplaceWithValue(node, promise, effect, control);
  return Replace(promise);
}

Reduction JSNativeContextSpecialization::ReduceJSAsyncFunctionResolve(
    Node* node) {
  DCHECK_EQ(IrOpcode::kJSAsyncFunctionResolve, node->opcode());
281
  Node* async_function_object = NodeProperties::GetValueInput(node, 0);
282 283 284 285 286 287
  Node* value = NodeProperties::GetValueInput(node, 1);
  Node* context = NodeProperties::GetContextInput(node);
  Node* frame_state = NodeProperties::GetFrameStateInput(node);
  Node* effect = NodeProperties::GetEffectInput(node);
  Node* control = NodeProperties::GetControlInput(node);

288
  if (!dependencies()->DependOnPromiseHookProtector()) return NoChange();
289 290 291 292 293

  // Load the promise from the {async_function_object}.
  Node* promise = effect = graph()->NewNode(
      simplified()->LoadField(AccessBuilder::ForJSAsyncFunctionObjectPromise()),
      async_function_object, effect, control);
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310

  // Create a nested frame state inside the current method's most-recent
  // {frame_state} that will ensure that lazy deoptimizations at this
  // point will still return the {promise} instead of the result of the
  // JSResolvePromise operation (which yields undefined).
  Node* parameters[] = {promise};
  frame_state = CreateStubBuiltinContinuationFrameState(
      jsgraph(), Builtins::kAsyncFunctionLazyDeoptContinuation, context,
      parameters, arraysize(parameters), frame_state,
      ContinuationFrameStateMode::LAZY);

  effect = graph()->NewNode(javascript()->ResolvePromise(), promise, value,
                            context, frame_state, effect, control);
  ReplaceWithValue(node, promise, effect, control);
  return Replace(promise);
}

311 312 313 314 315 316 317
Reduction JSNativeContextSpecialization::ReduceJSAdd(Node* node) {
  // TODO(turbofan): This has to run together with the inlining and
  // native context specialization to be able to leverage the string
  // constant-folding for optimizing property access, but we should
  // nevertheless find a better home for this at some point.
  DCHECK_EQ(IrOpcode::kJSAdd, node->opcode());

318 319 320
  Node* const lhs = node->InputAt(0);
  Node* const rhs = node->InputAt(1);

321 322
  base::Optional<size_t> lhs_len = GetMaxStringLength(broker(), lhs);
  base::Optional<size_t> rhs_len = GetMaxStringLength(broker(), rhs);
323 324
  if (!lhs_len || !rhs_len) {
    return NoChange();
325
  }
326 327 328 329

  // Fold into DelayedStringConstant if at least one of the parameters is a
  // string constant and the addition won't throw due to too long result.
  if (*lhs_len + *rhs_len <= String::kMaxLength &&
330
      (IsStringConstant(broker(), lhs) || IsStringConstant(broker(), rhs))) {
331 332 333 334 335 336 337 338 339 340
    const StringConstantBase* left = CreateDelayedStringConstant(lhs);
    const StringConstantBase* right = CreateDelayedStringConstant(rhs);
    const StringConstantBase* cons =
        new (shared_zone()) StringCons(left, right);

    Node* reduced = graph()->NewNode(common()->DelayedStringConstant(cons));
    ReplaceWithValue(node, reduced);
    return Replace(reduced);
  }

341 342 343
  return NoChange();
}

344 345 346 347 348 349 350 351
Reduction JSNativeContextSpecialization::ReduceJSGetSuperConstructor(
    Node* node) {
  DCHECK_EQ(IrOpcode::kJSGetSuperConstructor, node->opcode());
  Node* constructor = NodeProperties::GetValueInput(node, 0);

  // Check if the input is a known JSFunction.
  HeapObjectMatcher m(constructor);
  if (!m.HasValue()) return NoChange();
352
  JSFunctionRef function = m.Ref(broker()).AsJSFunction();
353
  MapRef function_map = function.map();
354
  if (FLAG_concurrent_inlining && !function_map.serialized_prototype()) {
355
    TRACE_BROKER_MISSING(broker(), "data for map " << function_map);
356 357
    return NoChange();
  }
358
  ObjectRef function_prototype = function_map.prototype();
359 360 361 362

  // We can constant-fold the super constructor access if the
  // {function}s map is stable, i.e. we can use a code dependency
  // to guard against [[Prototype]] changes of {function}.
363 364 365
  if (function_map.is_stable() && function_prototype.IsHeapObject() &&
      function_prototype.AsHeapObject().map().is_constructor()) {
    dependencies()->DependOnStableMap(function_map);
366 367 368
    Node* value = jsgraph()->Constant(function_prototype);
    ReplaceWithValue(node, value);
    return Replace(value);
369 370 371 372 373
  }

  return NoChange();
}

374 375
Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
  DCHECK_EQ(IrOpcode::kJSInstanceOf, node->opcode());
376
  FeedbackParameter const& p = FeedbackParameterOf(node->op());
377 378 379 380
  Node* object = NodeProperties::GetValueInput(node, 0);
  Node* constructor = NodeProperties::GetValueInput(node, 1);
  Node* context = NodeProperties::GetContextInput(node);
  Node* effect = NodeProperties::GetEffectInput(node);
381
  Node* frame_state = NodeProperties::GetFrameStateInput(node);
382 383
  Node* control = NodeProperties::GetControlInput(node);

384 385 386
  // Check if the right hand side is a known {receiver}, or
  // we have feedback from the InstanceOfIC.
  Handle<JSObject> receiver;
387
  HeapObjectMatcher m(constructor);
388 389
  if (m.HasValue() && m.Ref(broker()).IsJSObject()) {
    receiver = m.Ref(broker()).AsJSObject().object();
390
  } else if (p.feedback().IsValid()) {
391 392 393 394 395 396 397
    ProcessedFeedback const& feedback =
        broker()->GetFeedbackForInstanceOf(FeedbackSource(p.feedback()));
    if (feedback.IsInsufficient()) return NoChange();
    base::Optional<JSObjectRef> maybe_receiver =
        feedback.AsInstanceOf().value();
    if (!maybe_receiver.has_value()) return NoChange();
    receiver = maybe_receiver->object();
398 399 400
  } else {
    return NoChange();
  }
401 402 403 404 405 406

  JSObjectRef receiver_ref(broker(), receiver);
  MapRef receiver_map = receiver_ref.map();

  PropertyAccessInfo access_info = PropertyAccessInfo::Invalid(graph()->zone());
  if (FLAG_concurrent_inlining) {
407 408 409 410
    access_info = broker()->GetPropertyAccessInfo(
        receiver_map,
        NameRef(broker(), isolate()->factory()->has_instance_symbol()),
        AccessMode::kLoad);
411 412 413 414 415 416 417 418
  } else {
    AccessInfoFactory access_info_factory(broker(), dependencies(),
                                          graph()->zone());
    access_info = access_info_factory.ComputePropertyAccessInfo(
        receiver_map.object(), factory()->has_instance_symbol(),
        AccessMode::kLoad);
  }

419
  if (access_info.IsInvalid()) return NoChange();
420
  access_info.RecordDependencies(dependencies());
421

422
  PropertyAccessBuilder access_builder(jsgraph(), broker(), dependencies());
423

424 425
  if (access_info.IsNotFound()) {
    // If there's no @@hasInstance handler, the OrdinaryHasInstance operation
426
    // takes over, but that requires the constructor to be callable.
427
    if (!receiver_map.is_callable()) return NoChange();
428

429 430
    dependencies()->DependOnStablePrototypeChains(access_info.receiver_maps(),
                                                  kStartAtPrototype);
431 432 433 434 435 436 437 438 439 440 441 442 443 444

    // Monomorphic property access.
    access_builder.BuildCheckMaps(constructor, &effect, control,
                                  access_info.receiver_maps());

    // Lower to OrdinaryHasInstance(C, O).
    NodeProperties::ReplaceValueInput(node, constructor, 0);
    NodeProperties::ReplaceValueInput(node, object, 1);
    NodeProperties::ReplaceEffectInput(node, effect);
    NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance());
    Reduction const reduction = ReduceJSOrdinaryHasInstance(node);
    return reduction.Changed() ? reduction : Changed(node);
  }

445
  if (access_info.IsDataConstant()) {
446
    Handle<JSObject> holder;
447
    bool found_on_proto = access_info.holder().ToHandle(&holder);
448 449 450 451 452 453
    JSObjectRef holder_ref =
        found_on_proto ? JSObjectRef(broker(), holder) : receiver_ref;
    base::Optional<ObjectRef> constant = holder_ref.GetOwnDataProperty(
        access_info.field_representation(), access_info.field_index());
    if (!constant.has_value() || !constant->IsHeapObject() ||
        !constant->AsHeapObject().map().is_callable())
454
      return NoChange();
455 456 457

    if (found_on_proto) {
      dependencies()->DependOnStablePrototypeChains(
458 459
          access_info.receiver_maps(), kStartAtPrototype,
          JSObjectRef(broker(), holder));
460
    }
461

462 463 464 465
    // Check that {constructor} is actually {receiver}.
    constructor =
        access_builder.BuildCheckValue(constructor, &effect, control, receiver);

466
    // Monomorphic property access.
467 468
    access_builder.BuildCheckMaps(constructor, &effect, control,
                                  access_info.receiver_maps());
469

470 471 472 473 474 475 476 477 478 479
    // Create a nested frame state inside the current method's most-recent frame
    // state that will ensure that deopts that happen after this point will not
    // fallback to the last Checkpoint--which would completely re-execute the
    // instanceof logic--but rather create an activation of a version of the
    // ToBoolean stub that finishes the remaining work of instanceof and returns
    // to the caller without duplicating side-effects upon a lazy deopt.
    Node* continuation_frame_state = CreateStubBuiltinContinuationFrameState(
        jsgraph(), Builtins::kToBooleanLazyDeoptContinuation, context, nullptr,
        0, frame_state, ContinuationFrameStateMode::LAZY);

480
    // Call the @@hasInstance handler.
481
    Node* target = jsgraph()->Constant(*constant);
482 483 484
    node->InsertInput(graph()->zone(), 0, target);
    node->ReplaceInput(1, constructor);
    node->ReplaceInput(2, object);
485
    node->ReplaceInput(4, continuation_frame_state);
486
    node->ReplaceInput(5, effect);
487
    NodeProperties::ChangeOp(
488
        node, javascript()->Call(3, CallFrequency(), FeedbackSource(),
489
                                 ConvertReceiverMode::kNotNullOrUndefined));
490 491

    // Rewire the value uses of {node} to ToBoolean conversion of the result.
492
    Node* value = graph()->NewNode(simplified()->ToBoolean(), node);
493 494 495 496 497 498 499 500 501 502 503 504
    for (Edge edge : node->use_edges()) {
      if (NodeProperties::IsValueEdge(edge) && edge.from() != value) {
        edge.UpdateTo(value);
        Revisit(edge.from());
      }
    }
    return Changed(node);
  }

  return NoChange();
}

505 506
JSNativeContextSpecialization::InferHasInPrototypeChainResult
JSNativeContextSpecialization::InferHasInPrototypeChain(
507
    Node* receiver, Node* effect, HeapObjectRef const& prototype) {
508 509
  ZoneHandleSet<Map> receiver_maps;
  NodeProperties::InferReceiverMapsResult result =
510 511
      NodeProperties::InferReceiverMapsUnsafe(broker(), receiver, effect,
                                              &receiver_maps);
512 513
  if (result == NodeProperties::kNoReceiverMaps) return kMayBeInPrototypeChain;

514 515 516
  // Try to determine either that all of the {receiver_maps} have the given
  // {prototype} in their chain, or that none do. If we can't tell, return
  // kMayBeInPrototypeChain.
517 518 519
  bool all = true;
  bool none = true;
  for (size_t i = 0; i < receiver_maps.size(); ++i) {
520
    MapRef map(broker(), receiver_maps[i]);
521 522 523
    if (result == NodeProperties::kUnreliableReceiverMaps && !map.is_stable()) {
      return kMayBeInPrototypeChain;
    }
524 525 526 527 528
    while (true) {
      if (IsSpecialReceiverInstanceType(map.instance_type())) {
        return kMayBeInPrototypeChain;
      }
      if (!map.IsJSObjectMap()) {
529 530 531
        all = false;
        break;
      }
532 533 534 535 536
      if (FLAG_concurrent_inlining && !map.serialized_prototype()) {
        TRACE_BROKER_MISSING(broker(), "prototype data for map " << map);
        return kMayBeInPrototypeChain;
      }
      if (map.prototype().equals(prototype)) {
537 538 539
        none = false;
        break;
      }
540
      map = map.prototype().map();
541
      if (!map.is_stable()) return kMayBeInPrototypeChain;
542 543 544
      if (map.oddball_type() == OddballType::kNull) {
        all = false;
        break;
545 546 547 548
      }
    }
  }
  DCHECK_IMPLIES(all, !none);
549 550 551 552 553
  if (!all && !none) return kMayBeInPrototypeChain;

  {
    base::Optional<JSObjectRef> last_prototype;
    if (all) {
554 555 556 557 558 559
      // We don't need to protect the full chain if we found the prototype, we
      // can stop at {prototype}.  In fact we could stop at the one before
      // {prototype} but since we're dealing with multiple receiver maps this
      // might be a different object each time, so it's much simpler to include
      // {prototype}. That does, however, mean that we must check {prototype}'s
      // map stability.
560 561
      if (!prototype.map().is_stable()) return kMayBeInPrototypeChain;
      last_prototype = prototype.AsJSObject();
562 563 564 565 566 567 568
    }
    WhereToStart start = result == NodeProperties::kUnreliableReceiverMaps
                             ? kStartAtReceiver
                             : kStartAtPrototype;
    dependencies()->DependOnStablePrototypeChains(receiver_maps, start,
                                                  last_prototype);
  }
569

570 571
  DCHECK_EQ(all, !none);
  return all ? kIsInPrototypeChain : kIsNotInPrototypeChain;
572 573 574 575 576 577 578 579 580 581 582 583 584 585
}

Reduction JSNativeContextSpecialization::ReduceJSHasInPrototypeChain(
    Node* node) {
  DCHECK_EQ(IrOpcode::kJSHasInPrototypeChain, node->opcode());
  Node* value = NodeProperties::GetValueInput(node, 0);
  Node* prototype = NodeProperties::GetValueInput(node, 1);
  Node* effect = NodeProperties::GetEffectInput(node);

  // Check if we can constant-fold the prototype chain walk
  // for the given {value} and the {prototype}.
  HeapObjectMatcher m(prototype);
  if (m.HasValue()) {
    InferHasInPrototypeChainResult result =
586
        InferHasInPrototypeChain(value, effect, m.Ref(broker()));
587 588 589 590 591 592 593 594 595 596
    if (result != kMayBeInPrototypeChain) {
      Node* value = jsgraph()->BooleanConstant(result == kIsInPrototypeChain);
      ReplaceWithValue(node, value);
      return Replace(value);
    }
  }

  return NoChange();
}

597 598 599 600 601 602
Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance(
    Node* node) {
  DCHECK_EQ(IrOpcode::kJSOrdinaryHasInstance, node->opcode());
  Node* constructor = NodeProperties::GetValueInput(node, 0);
  Node* object = NodeProperties::GetValueInput(node, 1);

603
  // Check if the {constructor} is known at compile time.
604
  HeapObjectMatcher m(constructor);
605 606
  if (!m.HasValue()) return NoChange();

607 608 609 610 611
  if (m.Ref(broker()).IsJSBoundFunction()) {
    // OrdinaryHasInstance on bound functions turns into a recursive invocation
    // of the instanceof operator again.
    JSBoundFunctionRef function = m.Ref(broker()).AsJSBoundFunction();
    if (FLAG_concurrent_inlining && !function.serialized()) {
612
      TRACE_BROKER_MISSING(broker(), "data for JSBoundFunction " << function);
613 614 615 616 617
      return NoChange();
    }

    JSReceiverRef bound_target_function = function.bound_target_function();

618 619
    NodeProperties::ReplaceValueInput(node, object, 0);
    NodeProperties::ReplaceValueInput(
620
        node, jsgraph()->Constant(bound_target_function), 1);
621
    NodeProperties::ChangeOp(node, javascript()->InstanceOf(FeedbackSource()));
622 623 624 625
    Reduction const reduction = ReduceJSInstanceOf(node);
    return reduction.Changed() ? reduction : Changed(node);
  }

626 627 628
  if (m.Ref(broker()).IsJSFunction()) {
    // Optimize if we currently know the "prototype" property.

629
    JSFunctionRef function = m.Ref(broker()).AsJSFunction();
630
    if (FLAG_concurrent_inlining && !function.serialized()) {
631
      TRACE_BROKER_MISSING(broker(), "data for JSFunction " << function);
632 633 634
      return NoChange();
    }

635 636 637 638 639
    // TODO(neis): Remove the has_prototype_slot condition once the broker is
    // always enabled.
    if (!function.map().has_prototype_slot() || !function.has_prototype() ||
        function.PrototypeRequiresRuntimeLookup()) {
      return NoChange();
640
    }
641

642 643 644 645 646 647 648 649 650
    ObjectRef prototype = dependencies()->DependOnPrototypeProperty(function);
    Node* prototype_constant = jsgraph()->Constant(prototype);

    // Lower the {node} to JSHasInPrototypeChain.
    NodeProperties::ReplaceValueInput(node, object, 0);
    NodeProperties::ReplaceValueInput(node, prototype_constant, 1);
    NodeProperties::ChangeOp(node, javascript()->HasInPrototypeChain());
    Reduction const reduction = ReduceJSHasInPrototypeChain(node);
    return reduction.Changed() ? reduction : Changed(node);
651 652
  }

653 654 655
  return NoChange();
}

656 657 658 659 660 661 662 663 664 665 666 667
// ES section #sec-promise-resolve
Reduction JSNativeContextSpecialization::ReduceJSPromiseResolve(Node* node) {
  DCHECK_EQ(IrOpcode::kJSPromiseResolve, node->opcode());
  Node* constructor = NodeProperties::GetValueInput(node, 0);
  Node* value = NodeProperties::GetValueInput(node, 1);
  Node* context = NodeProperties::GetContextInput(node);
  Node* frame_state = NodeProperties::GetFrameStateInput(node);
  Node* effect = NodeProperties::GetEffectInput(node);
  Node* control = NodeProperties::GetControlInput(node);

  // Check if the {constructor} is the %Promise% function.
  HeapObjectMatcher m(constructor);
668
  if (!m.HasValue() ||
669
      !m.Ref(broker()).equals(native_context().promise_function())) {
670
    return NoChange();
671
  }
672

673 674 675 676 677
  // Only optimize if {value} cannot be a JSPromise.
  MapInference inference(broker(), value, effect);
  if (!inference.HaveMaps() ||
      inference.AnyOfInstanceTypesAre(JS_PROMISE_TYPE)) {
    return NoChange();
678 679
  }

680
  if (!dependencies()->DependOnPromiseHookProtector()) return NoChange();
681

682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
  // Create a %Promise% instance and resolve it with {value}.
  Node* promise = effect =
      graph()->NewNode(javascript()->CreatePromise(), context, effect);
  effect = graph()->NewNode(javascript()->ResolvePromise(), promise, value,
                            context, frame_state, effect, control);
  ReplaceWithValue(node, promise, effect, control);
  return Replace(promise);
}

// ES section #sec-promise-resolve-functions
Reduction JSNativeContextSpecialization::ReduceJSResolvePromise(Node* node) {
  DCHECK_EQ(IrOpcode::kJSResolvePromise, node->opcode());
  Node* promise = NodeProperties::GetValueInput(node, 0);
  Node* resolution = NodeProperties::GetValueInput(node, 1);
  Node* context = NodeProperties::GetContextInput(node);
  Node* effect = NodeProperties::GetEffectInput(node);
  Node* control = NodeProperties::GetControlInput(node);

  // Check if we know something about the {resolution}.
701 702 703
  MapInference inference(broker(), resolution, effect);
  if (!inference.HaveMaps()) return NoChange();
  MapHandles const& resolution_maps = inference.GetMaps();
704

705
  // Compute property access info for "then" on {resolution}.
706 707 708
  ZoneVector<PropertyAccessInfo> access_infos(graph()->zone());
  AccessInfoFactory access_info_factory(broker(), dependencies(),
                                        graph()->zone());
709 710 711 712 713 714 715 716
  if (!FLAG_concurrent_inlining) {
    access_info_factory.ComputePropertyAccessInfos(
        resolution_maps, factory()->then_string(), AccessMode::kLoad,
        &access_infos);
  } else {
    // Obtain pre-computed access infos from the broker.
    for (auto map : resolution_maps) {
      MapRef map_ref(broker(), map);
717 718 719
      access_infos.push_back(broker()->GetPropertyAccessInfo(
          map_ref, NameRef(broker(), isolate()->factory()->then_string()),
          AccessMode::kLoad));
720 721
    }
  }
722 723 724
  PropertyAccessInfo access_info =
      access_info_factory.FinalizePropertyAccessInfosAsOne(access_infos,
                                                           AccessMode::kLoad);
725 726 727 728
  if (access_info.IsInvalid()) return inference.NoChange();

  // Only optimize when {resolution} definitely doesn't have a "then" property.
  if (!access_info.IsNotFound()) return inference.NoChange();
729

730 731 732
  if (!inference.RelyOnMapsViaStability(dependencies())) {
    return inference.NoChange();
  }
733

734 735
  dependencies()->DependOnStablePrototypeChains(access_info.receiver_maps(),
                                                kStartAtPrototype);
736

737 738 739 740 741 742 743 744
  // Simply fulfill the {promise} with the {resolution}.
  Node* value = effect =
      graph()->NewNode(javascript()->FulfillPromise(), promise, resolution,
                       context, effect, control);
  ReplaceWithValue(node, value, effect, control);
  return Replace(value);
}

745 746 747
namespace {

FieldAccess ForPropertyCellValue(MachineRepresentation representation,
748
                                 Type type, MaybeHandle<Map> map,
749
                                 NameRef const& name) {
750
  WriteBarrierKind kind = kFullWriteBarrier;
751 752
  if (representation == MachineRepresentation::kTaggedSigned ||
      representation == MachineRepresentation::kCompressedSigned) {
753
    kind = kNoWriteBarrier;
754 755
  } else if (representation == MachineRepresentation::kTaggedPointer ||
             representation == MachineRepresentation::kCompressedPointer) {
756 757 758 759
    kind = kPointerWriteBarrier;
  }
  MachineType r = MachineType::TypeForRepresentation(representation);
  FieldAccess access = {
760 761
      kTaggedBase, PropertyCell::kValueOffset, name.object(), map, type, r,
      kind};
762 763 764 765 766 767
  return access;
}

}  // namespace

Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
768
    Node* node, Node* receiver, Node* value, NameRef const& name,
769
    AccessMode access_mode, Node* key) {
770
  base::Optional<PropertyCellRef> cell =
771
      native_context().global_object().GetPropertyCell(name);
772
  return cell.has_value() ? ReduceGlobalAccess(node, receiver, value, name,
773
                                               access_mode, key, *cell)
774
                          : NoChange();
775 776
}

777 778 779
// TODO(neis): Try to merge this with ReduceNamedAccess by introducing a new
// PropertyAccessInfo kind for global accesses and using the existing mechanism
// for building loads/stores.
780
Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
781
    Node* node, Node* receiver, Node* value, NameRef const& name,
782
    AccessMode access_mode, Node* key, PropertyCellRef const& property_cell) {
783 784 785
  Node* effect = NodeProperties::GetEffectInput(node);
  Node* control = NodeProperties::GetControlInput(node);

786 787 788 789
  ObjectRef property_cell_value = property_cell.value();
  if (property_cell_value.IsHeapObject() &&
      property_cell_value.AsHeapObject().map().oddball_type() ==
          OddballType::kHole) {
790 791 792 793
    // The property cell is no longer valid.
    return NoChange();
  }

794
  PropertyDetails property_details = property_cell.property_details();
795
  PropertyCellType property_cell_type = property_details.cell_type();
796
  DCHECK_EQ(kData, property_details.kind());
797 798 799 800 801 802 803 804 805 806 807 808

  // We have additional constraints for stores.
  if (access_mode == AccessMode::kStore) {
    if (property_details.IsReadOnly()) {
      // Don't even bother trying to lower stores to read-only data properties.
      return NoChange();
    } else if (property_cell_type == PropertyCellType::kUndefined) {
      // There's no fast-path for dealing with undefined property cells.
      return NoChange();
    } else if (property_cell_type == PropertyCellType::kConstantType) {
      // There's also no fast-path to store to a global cell which pretended
      // to be stable, but is no longer stable now.
809 810
      if (property_cell_value.IsHeapObject() &&
          !property_cell_value.AsHeapObject().map().is_stable()) {
811 812 813
        return NoChange();
      }
    }
814 815 816 817 818 819 820
  } else if (access_mode == AccessMode::kHas) {
    // has checks cannot follow the fast-path used by loads when these
    // conditions hold.
    if ((property_details.IsConfigurable() || !property_details.IsReadOnly()) &&
        property_details.cell_type() != PropertyCellType::kConstant &&
        property_details.cell_type() != PropertyCellType::kUndefined)
      return NoChange();
821 822
  }

823 824 825
  // Ensure that {key} matches the specified {name} (if {key} is given).
  if (key != nullptr) {
    effect = BuildCheckEqualsName(name, key, effect, control);
826 827
  }

828 829 830
  // If we have a {receiver} to validate, we do so by checking that its map is
  // the (target) global proxy's map. This guarantees that in fact the receiver
  // is the global proxy.
831
  if (receiver != nullptr) {
832
    effect = graph()->NewNode(
833 834 835 836 837
        simplified()->CheckMaps(
            CheckMapsFlag::kNone,
            ZoneHandleSet<Map>(
                HeapObjectRef(broker(), global_proxy()).map().object())),
        receiver, effect, control);
838 839
  }

840
  if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
841 842 843
    // Load from non-configurable, read-only data property on the global
    // object can be constant-folded, even without deoptimization support.
    if (!property_details.IsConfigurable() && property_details.IsReadOnly()) {
844 845 846
      value = access_mode == AccessMode::kHas
                  ? jsgraph()->TrueConstant()
                  : jsgraph()->Constant(property_cell_value);
847 848 849 850 851 852
    } else {
      // Record a code dependency on the cell if we can benefit from the
      // additional feedback, or the global property is configurable (i.e.
      // can be deleted or reconfigured to an accessor property).
      if (property_details.cell_type() != PropertyCellType::kMutable ||
          property_details.IsConfigurable()) {
853
        dependencies()->DependOnGlobalProperty(property_cell);
854 855 856 857 858
      }

      // Load from constant/undefined global property can be constant-folded.
      if (property_details.cell_type() == PropertyCellType::kConstant ||
          property_details.cell_type() == PropertyCellType::kUndefined) {
859 860 861
        value = access_mode == AccessMode::kHas
                    ? jsgraph()->TrueConstant()
                    : jsgraph()->Constant(property_cell_value);
862 863 864
        DCHECK(!property_cell_value.IsHeapObject() ||
               property_cell_value.AsHeapObject().map().oddball_type() !=
                   OddballType::kHole);
865
      } else {
866 867
        DCHECK_NE(AccessMode::kHas, access_mode);

868 869
        // Load from constant type cell can benefit from type feedback.
        MaybeHandle<Map> map;
870
        Type property_cell_value_type = Type::NonInternal();
871 872
        MachineRepresentation representation =
            MachineType::RepCompressedTagged();
873 874
        if (property_details.cell_type() == PropertyCellType::kConstantType) {
          // Compute proper type based on the current value in the cell.
875
          if (property_cell_value.IsSmi()) {
876
            property_cell_value_type = Type::SignedSmall();
877
            representation = MachineType::RepCompressedTaggedSigned();
878
          } else if (property_cell_value.IsHeapNumber()) {
879
            property_cell_value_type = Type::Number();
880
            representation = MachineType::RepCompressedTaggedPointer();
881
          } else {
882 883
            MapRef property_cell_value_map =
                property_cell_value.AsHeapObject().map();
884
            property_cell_value_type = Type::For(property_cell_value_map);
885
            representation = MachineType::RepCompressedTaggedPointer();
886 887 888 889

            // We can only use the property cell value map for map check
            // elimination if it's stable, i.e. the HeapObject wasn't
            // mutated without the cell state being updated.
890 891
            if (property_cell_value_map.is_stable()) {
              dependencies()->DependOnStableMap(property_cell_value_map);
892
              map = property_cell_value_map.object();
893 894 895 896 897 898
            }
          }
        }
        value = effect = graph()->NewNode(
            simplified()->LoadField(ForPropertyCellValue(
                representation, property_cell_value_type, map, name)),
899
            jsgraph()->Constant(property_cell), effect, control);
900 901 902 903 904 905 906 907 908 909 910 911 912
      }
    }
  } else {
    DCHECK_EQ(AccessMode::kStore, access_mode);
    DCHECK(!property_details.IsReadOnly());
    switch (property_details.cell_type()) {
      case PropertyCellType::kUndefined: {
        UNREACHABLE();
        break;
      }
      case PropertyCellType::kConstant: {
        // Record a code dependency on the cell, and just deoptimize if the new
        // value doesn't match the previous value stored inside the cell.
913
        dependencies()->DependOnGlobalProperty(property_cell);
914 915 916
        Node* check =
            graph()->NewNode(simplified()->ReferenceEqual(), value,
                             jsgraph()->Constant(property_cell_value));
917 918 919
        effect = graph()->NewNode(
            simplified()->CheckIf(DeoptimizeReason::kValueMismatch), check,
            effect, control);
920 921 922 923 924 925
        break;
      }
      case PropertyCellType::kConstantType: {
        // Record a code dependency on the cell, and just deoptimize if the new
        // values' type doesn't match the type of the previous value in the
        // cell.
926
        dependencies()->DependOnGlobalProperty(property_cell);
927
        Type property_cell_value_type;
928 929
        MachineRepresentation representation =
            MachineType::RepCompressedTagged();
930
        if (property_cell_value.IsHeapObject()) {
931 932
          // We cannot do anything if the {property_cell_value}s map is no
          // longer stable.
933 934 935
          MapRef property_cell_value_map =
              property_cell_value.AsHeapObject().map();
          dependencies()->DependOnStableMap(property_cell_value_map);
936 937 938 939 940

          // Check that the {value} is a HeapObject.
          value = effect = graph()->NewNode(simplified()->CheckHeapObject(),
                                            value, effect, control);

941
          // Check {value} map against the {property_cell} map.
942 943 944 945 946
          effect = graph()->NewNode(
              simplified()->CheckMaps(
                  CheckMapsFlag::kNone,
                  ZoneHandleSet<Map>(property_cell_value_map.object())),
              value, effect, control);
947
          property_cell_value_type = Type::OtherInternal();
948
          representation = MachineType::RepCompressedTaggedPointer();
949 950
        } else {
          // Check that the {value} is a Smi.
951
          value = effect = graph()->NewNode(
952
              simplified()->CheckSmi(FeedbackSource()), value, effect, control);
953
          property_cell_value_type = Type::SignedSmall();
954
          representation = MachineType::RepCompressedTaggedSigned();
955 956 957 958
        }
        effect = graph()->NewNode(simplified()->StoreField(ForPropertyCellValue(
                                      representation, property_cell_value_type,
                                      MaybeHandle<Map>(), name)),
959
                                  jsgraph()->Constant(property_cell), value,
960 961 962 963 964 965
                                  effect, control);
        break;
      }
      case PropertyCellType::kMutable: {
        // Record a code dependency on the cell, and just deoptimize if the
        // property ever becomes read-only.
966
        dependencies()->DependOnGlobalProperty(property_cell);
967 968
        effect = graph()->NewNode(
            simplified()->StoreField(ForPropertyCellValue(
969
                MachineType::RepCompressedTagged(), Type::NonInternal(),
970
                MaybeHandle<Map>(), name)),
971
            jsgraph()->Constant(property_cell), value, effect, control);
972 973 974 975 976 977 978 979 980 981
        break;
      }
    }
  }

  ReplaceWithValue(node, value, effect, control);
  return Replace(value);
}

Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) {
982
  DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode());
983 984
  LoadGlobalParameters const& p = LoadGlobalParametersOf(node->op());
  if (!p.feedback().IsValid()) return NoChange();
985

986 987 988
  ProcessedFeedback const& processed =
      broker()->GetFeedbackForGlobalAccess(FeedbackSource(p.feedback()));
  if (processed.IsInsufficient()) return NoChange();
989

990 991
  GlobalAccessFeedback const& feedback = processed.AsGlobalAccess();
  if (feedback.IsScriptContextSlot()) {
992
    Node* effect = NodeProperties::GetEffectInput(node);
993
    Node* script_context = jsgraph()->Constant(feedback.script_context());
994
    Node* value = effect =
995 996
        graph()->NewNode(javascript()->LoadContext(0, feedback.slot_index(),
                                                   feedback.immutable()),
997
                         script_context, effect);
998 999
    ReplaceWithValue(node, value, effect);
    return Replace(value);
1000 1001 1002 1003 1004 1005 1006
  } else if (feedback.IsPropertyCell()) {
    return ReduceGlobalAccess(node, nullptr, nullptr,
                              NameRef(broker(), p.name()), AccessMode::kLoad,
                              nullptr, feedback.property_cell());
  } else {
    DCHECK(feedback.IsMegamorphic());
    return NoChange();
1007 1008 1009 1010
  }
}

Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) {
1011
  DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode());
1012
  Node* value = NodeProperties::GetValueInput(node, 0);
1013 1014
  StoreGlobalParameters const& p = StoreGlobalParametersOf(node->op());
  if (!p.feedback().IsValid()) return NoChange();
1015

1016 1017 1018
  ProcessedFeedback const& processed =
      broker()->GetFeedbackForGlobalAccess(FeedbackSource(p.feedback()));
  if (processed.IsInsufficient()) return NoChange();
1019

1020 1021 1022
  GlobalAccessFeedback const& feedback = processed.AsGlobalAccess();
  if (feedback.IsScriptContextSlot()) {
    if (feedback.immutable()) return NoChange();
1023 1024
    Node* effect = NodeProperties::GetEffectInput(node);
    Node* control = NodeProperties::GetControlInput(node);
1025
    Node* script_context = jsgraph()->Constant(feedback.script_context());
1026
    effect =
1027
        graph()->NewNode(javascript()->StoreContext(0, feedback.slot_index()),
1028
                         value, script_context, effect, control);
1029 1030
    ReplaceWithValue(node, value, effect, control);
    return Replace(value);
1031
  } else if (feedback.IsPropertyCell()) {
1032 1033
    return ReduceGlobalAccess(node, nullptr, value, NameRef(broker(), p.name()),
                              AccessMode::kStore, nullptr,
1034 1035 1036 1037
                              feedback.property_cell());
  } else {
    DCHECK(feedback.IsMegamorphic());
    return NoChange();
1038
  }
1039
}
1040

1041
Reduction JSNativeContextSpecialization::ReduceNamedAccess(
1042 1043
    Node* node, Node* value, NamedAccessFeedback const& feedback,
    AccessMode access_mode, Node* key) {
1044
  DCHECK(node->opcode() == IrOpcode::kJSLoadNamed ||
1045 1046
         node->opcode() == IrOpcode::kJSStoreNamed ||
         node->opcode() == IrOpcode::kJSLoadProperty ||
1047
         node->opcode() == IrOpcode::kJSStoreProperty ||
1048
         node->opcode() == IrOpcode::kJSStoreNamedOwn ||
1049
         node->opcode() == IrOpcode::kJSStoreDataPropertyInLiteral ||
1050
         node->opcode() == IrOpcode::kJSHasProperty);
1051
  Node* receiver = NodeProperties::GetValueInput(node, 0);
1052
  Node* context = NodeProperties::GetContextInput(node);
1053
  Node* frame_state = NodeProperties::GetFrameStateInput(node);
1054 1055 1056
  Node* effect = NodeProperties::GetEffectInput(node);
  Node* control = NodeProperties::GetControlInput(node);

1057 1058 1059 1060
  // Either infer maps from the graph or use the feedback.
  ZoneVector<Handle<Map>> receiver_maps(zone());
  if (!InferReceiverMaps(receiver, effect, &receiver_maps)) {
    receiver_maps = feedback.maps();
1061
  }
1062
  RemoveImpossibleReceiverMaps(receiver, &receiver_maps);
1063

1064 1065 1066 1067 1068
  // Check if we have an access o.x or o.x=v where o is the target native
  // contexts' global proxy, and turn that into a direct access to the
  // corresponding global object instead.
  if (receiver_maps.size() == 1) {
    MapRef receiver_map(broker(), receiver_maps[0]);
1069 1070 1071
    if (receiver_map.equals(
            broker()->target_native_context().global_proxy_object().map()) &&
        !broker()->target_native_context().global_object().IsDetached()) {
1072 1073
      return ReduceGlobalAccess(node, receiver, value, feedback.name(),
                                access_mode, key);
1074 1075 1076
    }
  }

1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097
  ZoneVector<PropertyAccessInfo> access_infos(zone());
  {
    ZoneVector<PropertyAccessInfo> access_infos_for_feedback(zone());
    for (Handle<Map> map_handle : receiver_maps) {
      MapRef map(broker(), map_handle);
      if (map.is_deprecated()) continue;
      PropertyAccessInfo access_info = broker()->GetPropertyAccessInfo(
          map, feedback.name(), access_mode, dependencies(),
          FLAG_concurrent_inlining ? SerializationPolicy::kAssumeSerialized
                                   : SerializationPolicy::kSerializeIfNeeded);
      access_infos_for_feedback.push_back(access_info);
    }

    AccessInfoFactory access_info_factory(broker(), dependencies(),
                                          graph()->zone());
    if (!access_info_factory.FinalizePropertyAccessInfos(
            access_infos_for_feedback, access_mode, &access_infos)) {
      return NoChange();
    }
  }

1098
  // Ensure that {key} matches the specified name (if {key} is given).
1099
  if (key != nullptr) {
1100
    effect = BuildCheckEqualsName(feedback.name(), key, effect, control);
1101 1102
  }

1103 1104 1105 1106 1107 1108 1109 1110
  // Collect call nodes to rewire exception edges.
  ZoneVector<Node*> if_exception_nodes(zone());
  ZoneVector<Node*>* if_exceptions = nullptr;
  Node* if_exception = nullptr;
  if (NodeProperties::IsExceptionalCall(node, &if_exception)) {
    if_exceptions = &if_exception_nodes;
  }

1111
  PropertyAccessBuilder access_builder(jsgraph(), broker(), dependencies());
1112

1113
  // Check for the monomorphic cases.
1114 1115
  if (access_infos.size() == 1) {
    PropertyAccessInfo access_info = access_infos.front();
1116 1117
    // Try to build string check or number check if possible.
    // Otherwise build a map check.
1118 1119
    if (!access_builder.TryBuildStringCheck(broker(),
                                            access_info.receiver_maps(),
1120
                                            &receiver, &effect, control) &&
1121 1122
        !access_builder.TryBuildNumberCheck(broker(),
                                            access_info.receiver_maps(),
1123
                                            &receiver, &effect, control)) {
1124
      if (HasNumberMaps(broker(), access_info.receiver_maps())) {
1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147
        // We need to also let Smi {receiver}s through in this case, so
        // we construct a diamond, guarded by the Sminess of the {receiver}
        // and if {receiver} is not a Smi just emit a sequence of map checks.
        Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
        Node* branch = graph()->NewNode(common()->Branch(), check, control);

        Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
        Node* etrue = effect;

        Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
        Node* efalse = effect;
        {
          access_builder.BuildCheckMaps(receiver, &efalse, if_false,
                                        access_info.receiver_maps());
        }

        control = graph()->NewNode(common()->Merge(2), if_true, if_false);
        effect =
            graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
      } else {
        access_builder.BuildCheckMaps(receiver, &effect, control,
                                      access_info.receiver_maps());
      }
1148
    }
1149

1150
    // Generate the actual property access.
1151
    ValueEffectControl continuation = BuildPropertyAccess(
1152
        receiver, value, context, frame_state, effect, control, feedback.name(),
1153
        if_exceptions, access_info, access_mode);
1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166
    value = continuation.value();
    effect = continuation.effect();
    control = continuation.control();
  } else {
    // The final states for every polymorphic branch. We join them with
    // Merge+Phi+EffectPhi at the bottom.
    ZoneVector<Node*> values(zone());
    ZoneVector<Node*> effects(zone());
    ZoneVector<Node*> controls(zone());

    // Check if {receiver} may be a number.
    bool receiverissmi_possible = false;
    for (PropertyAccessInfo const& access_info : access_infos) {
1167
      if (HasNumberMaps(broker(), access_info.receiver_maps())) {
1168 1169
        receiverissmi_possible = true;
        break;
1170
      }
1171 1172
    }

1173
    // Handle the case that {receiver} may be a number.
1174 1175 1176 1177 1178 1179 1180 1181
    Node* receiverissmi_control = nullptr;
    Node* receiverissmi_effect = effect;
    if (receiverissmi_possible) {
      Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
      Node* branch = graph()->NewNode(common()->Branch(), check, control);
      control = graph()->NewNode(common()->IfFalse(), branch);
      receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch);
      receiverissmi_effect = effect;
1182 1183
    }

1184 1185 1186 1187 1188 1189 1190
    // Generate code for the various different property access patterns.
    Node* fallthrough_control = control;
    for (size_t j = 0; j < access_infos.size(); ++j) {
      PropertyAccessInfo const& access_info = access_infos[j];
      Node* this_value = value;
      Node* this_receiver = receiver;
      Node* this_effect = effect;
1191
      Node* this_control = fallthrough_control;
1192 1193

      // Perform map check on {receiver}.
1194 1195
      ZoneVector<Handle<Map>> const& receiver_maps =
          access_info.receiver_maps();
1196
      {
1197 1198 1199 1200
        // Whether to insert a dedicated MapGuard node into the
        // effect to be able to learn from the control flow.
        bool insert_map_guard = true;

1201
        // Check maps for the {receiver}s.
1202 1203 1204
        if (j == access_infos.size() - 1) {
          // Last map check on the fallthrough control path, do a
          // conditional eager deoptimization exit here.
1205 1206
          access_builder.BuildCheckMaps(receiver, &this_effect, this_control,
                                        receiver_maps);
1207
          fallthrough_control = nullptr;
1208 1209 1210 1211 1212

          // Don't insert a MapGuard in this case, as the CheckMaps
          // node already gives you all the information you need
          // along the effect chain.
          insert_map_guard = false;
1213
        } else {
1214 1215 1216 1217
          // Explicitly branch on the {receiver_maps}.
          ZoneHandleSet<Map> maps;
          for (Handle<Map> map : receiver_maps) {
            maps.insert(map, graph()->zone());
1218
          }
1219 1220 1221 1222 1223 1224 1225
          Node* check = this_effect =
              graph()->NewNode(simplified()->CompareMaps(maps), receiver,
                               this_effect, this_control);
          Node* branch =
              graph()->NewNode(common()->Branch(), check, this_control);
          fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
          this_control = graph()->NewNode(common()->IfTrue(), branch);
1226
        }
1227 1228

        // The Number case requires special treatment to also deal with Smis.
1229
        if (HasNumberMaps(broker(), receiver_maps)) {
1230 1231 1232
          // Join this check with the "receiver is smi" check above.
          DCHECK_NOT_NULL(receiverissmi_effect);
          DCHECK_NOT_NULL(receiverissmi_control);
1233 1234 1235 1236
          this_control = graph()->NewNode(common()->Merge(2), this_control,
                                          receiverissmi_control);
          this_effect = graph()->NewNode(common()->EffectPhi(2), this_effect,
                                         receiverissmi_effect, this_control);
1237
          receiverissmi_effect = receiverissmi_control = nullptr;
1238 1239 1240 1241

          // The {receiver} can also be a Smi in this case, so
          // a MapGuard doesn't make sense for this at all.
          insert_map_guard = false;
1242
        }
1243

1244 1245 1246 1247 1248 1249
        // Introduce a MapGuard to learn from this on the effect chain.
        if (insert_map_guard) {
          ZoneHandleSet<Map> maps;
          for (auto receiver_map : receiver_maps) {
            maps.insert(receiver_map, graph()->zone());
          }
1250
          this_effect = graph()->NewNode(simplified()->MapGuard(maps), receiver,
1251 1252
                                         this_effect, this_control);
        }
1253 1254 1255 1256 1257

        // If all {receiver_maps} are Strings we also need to rename the
        // {receiver} here to make sure that TurboFan knows that along this
        // path the {this_receiver} is a String. This is because we want
        // strict checking of types, for example for StringLength operators.
1258
        if (HasOnlyStringMaps(broker(), receiver_maps)) {
1259 1260 1261 1262
          this_receiver = this_effect =
              graph()->NewNode(common()->TypeGuard(Type::String()), receiver,
                               this_effect, this_control);
        }
1263 1264
      }

1265
      // Generate the actual property access.
1266 1267 1268 1269
      ValueEffectControl continuation =
          BuildPropertyAccess(this_receiver, this_value, context, frame_state,
                              this_effect, this_control, feedback.name(),
                              if_exceptions, access_info, access_mode);
1270 1271 1272 1273
      values.push_back(continuation.value());
      effects.push_back(continuation.effect());
      controls.push_back(continuation.control());
    }
1274

1275
    DCHECK_NULL(fallthrough_control);
1276

1277 1278 1279
    // Generate the final merge point for all (polymorphic) branches.
    int const control_count = static_cast<int>(controls.size());
    if (control_count == 0) {
1280
      value = effect = control = jsgraph()->Dead();
1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295
    } else if (control_count == 1) {
      value = values.front();
      effect = effects.front();
      control = controls.front();
    } else {
      control = graph()->NewNode(common()->Merge(control_count), control_count,
                                 &controls.front());
      values.push_back(control);
      value = graph()->NewNode(
          common()->Phi(MachineRepresentation::kTagged, control_count),
          control_count + 1, &values.front());
      effects.push_back(control);
      effect = graph()->NewNode(common()->EffectPhi(control_count),
                                control_count + 1, &effects.front());
    }
1296
  }
1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314

  // Properly rewire IfException edges if {node} is inside a try-block.
  if (!if_exception_nodes.empty()) {
    DCHECK_NOT_NULL(if_exception);
    DCHECK_EQ(if_exceptions, &if_exception_nodes);
    int const if_exception_count = static_cast<int>(if_exceptions->size());
    Node* merge = graph()->NewNode(common()->Merge(if_exception_count),
                                   if_exception_count, &if_exceptions->front());
    if_exceptions->push_back(merge);
    Node* ephi =
        graph()->NewNode(common()->EffectPhi(if_exception_count),
                         if_exception_count + 1, &if_exceptions->front());
    Node* phi = graph()->NewNode(
        common()->Phi(MachineRepresentation::kTagged, if_exception_count),
        if_exception_count + 1, &if_exceptions->front());
    ReplaceWithValue(if_exception, phi, ephi, merge);
  }

1315 1316
  ReplaceWithValue(node, value, effect, control);
  return Replace(value);
1317 1318
}

1319 1320
Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
  DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode());
1321
  NamedAccess const& p = NamedAccessOf(node->op());
1322
  Node* const receiver = NodeProperties::GetValueInput(node, 0);
1323
  NameRef name(broker(), p.name());
1324

1325 1326 1327
  // Check if we have a constant receiver.
  HeapObjectMatcher m(receiver);
  if (m.HasValue()) {
1328 1329 1330
    ObjectRef object = m.Ref(broker());
    if (object.IsJSFunction() &&
        name.equals(ObjectRef(broker(), factory()->prototype_string()))) {
1331
      // Optimize "prototype" property of functions.
1332
      JSFunctionRef function = object.AsJSFunction();
1333
      if (FLAG_concurrent_inlining && !function.serialized()) {
1334
        TRACE_BROKER_MISSING(broker(), "data for function " << function);
1335 1336
        return NoChange();
      }
1337 1338 1339 1340
      // TODO(neis): Remove the has_prototype_slot condition once the broker is
      // always enabled.
      if (!function.map().has_prototype_slot() || !function.has_prototype() ||
          function.PrototypeRequiresRuntimeLookup()) {
1341 1342
        return NoChange();
      }
1343 1344 1345 1346
      ObjectRef prototype = dependencies()->DependOnPrototypeProperty(function);
      Node* value = jsgraph()->Constant(prototype);
      ReplaceWithValue(node, value);
      return Replace(value);
1347 1348
    } else if (object.IsString() &&
               name.equals(ObjectRef(broker(), factory()->length_string()))) {
1349
      // Constant-fold "length" property on constant strings.
1350
      Node* value = jsgraph()->Constant(object.AsString().length());
1351 1352
      ReplaceWithValue(node, value);
      return Replace(value);
1353 1354 1355
    }
  }

1356
  if (!p.feedback().IsValid()) return NoChange();
1357 1358
  return ReducePropertyAccess(node, nullptr, name, jsgraph()->Dead(),
                              FeedbackSource(p.feedback()), AccessMode::kLoad);
1359
}
1360

1361 1362
Reduction JSNativeContextSpecialization::ReduceJSGetIterator(Node* node) {
  DCHECK_EQ(IrOpcode::kJSGetIterator, node->opcode());
1363 1364 1365 1366 1367 1368 1369 1370
  GetIteratorParameters const& p = GetIteratorParametersOf(node->op());

  Node* receiver = NodeProperties::GetValueInput(node, 0);
  Node* context = NodeProperties::GetContextInput(node);
  Node* frame_state = NodeProperties::GetFrameStateInput(node);
  Node* effect = NodeProperties::GetEffectInput(node);
  Node* control = NodeProperties::GetControlInput(node);

1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399
  Node* iterator_exception_node = nullptr;
  Node* if_exception_merge = nullptr;
  Node* if_exception_effect_phi = nullptr;
  Node* if_exception_phi = nullptr;
  bool has_exception_node =
      NodeProperties::IsExceptionalCall(node, &iterator_exception_node);
  if (has_exception_node) {
    // If there exists an IfException node for the current {node}, we need
    // exception handling for all the desugared nodes. Create a combination
    // of Merge+Phi+EffectPhi nodes that consumes the exception paths from
    // from all the desugared nodes including the original exception node.
    // Usages of the original exception node are then rewired to the newly
    // created combination of Merge+Phi+EffectPhi. Here, use dead_node as a
    // placeholder for the original exception node until its uses are rewired.

    Node* dead_node = jsgraph()->Dead();
    if_exception_merge = graph()->NewNode(common()->Merge(1), dead_node);
    if_exception_effect_phi =
        graph()->NewNode(common()->EffectPhi(1), dead_node, if_exception_merge);
    if_exception_phi =
        graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 1),
                         dead_node, if_exception_merge);
    ReplaceWithValue(iterator_exception_node, if_exception_phi,
                     if_exception_effect_phi, if_exception_merge);
    if_exception_merge->ReplaceInput(0, iterator_exception_node);
    if_exception_effect_phi->ReplaceInput(0, iterator_exception_node);
    if_exception_phi->ReplaceInput(0, iterator_exception_node);
  }

1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418
  // Load iterator property operator
  Handle<Name> iterator_symbol = factory()->iterator_symbol();
  const Operator* load_op =
      javascript()->LoadNamed(iterator_symbol, p.loadFeedback());

  // Lazy deopt of the load iterator property
  Node* call_slot = jsgraph()->SmiConstant(p.callFeedback().slot.ToInt());
  Node* call_feedback = jsgraph()->HeapConstant(p.callFeedback().vector);
  Node* lazy_deopt_parameters[] = {receiver, call_slot, call_feedback};
  Node* lazy_deopt_frame_state = CreateStubBuiltinContinuationFrameState(
      jsgraph(), Builtins::kGetIteratorWithFeedbackLazyDeoptContinuation,
      context, lazy_deopt_parameters, arraysize(lazy_deopt_parameters),
      frame_state, ContinuationFrameStateMode::LAZY);
  Node* load_property = graph()->NewNode(
      load_op, receiver, context, lazy_deopt_frame_state, effect, control);
  effect = load_property;
  control = load_property;

  // Handle exception path for the load named property
1419 1420 1421 1422
  if (has_exception_node) {
    control =
        AppendExceptionHandling(effect, control, if_exception_merge,
                                if_exception_phi, if_exception_effect_phi);
1423 1424 1425 1426 1427 1428 1429 1430 1431 1432
  }

  // Eager deopt of call iterator property
  Node* parameters[] = {receiver, load_property, call_slot, call_feedback};
  Node* eager_deopt_frame_state = CreateStubBuiltinContinuationFrameState(
      jsgraph(), Builtins::kCallIteratorWithFeedback, context, parameters,
      arraysize(parameters), frame_state, ContinuationFrameStateMode::EAGER);
  Node* deopt_checkpoint = graph()->NewNode(
      common()->Checkpoint(), eager_deopt_frame_state, effect, control);
  effect = deopt_checkpoint;
1433

1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444
  // Call iterator property operator
  ProcessedFeedback const& feedback =
      broker()->GetFeedbackForCall(p.callFeedback());
  SpeculationMode mode = feedback.IsInsufficient()
                             ? SpeculationMode::kDisallowSpeculation
                             : feedback.AsCall().speculation_mode();
  const Operator* call_op =
      javascript()->Call(2, CallFrequency(), p.callFeedback(),
                         ConvertReceiverMode::kNotNullOrUndefined, mode);
  Node* call_property = graph()->NewNode(call_op, load_property, receiver,
                                         context, frame_state, effect, control);
1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480
  effect = call_property;
  control = call_property;
  if (has_exception_node) {
    control =
        AppendExceptionHandling(effect, control, if_exception_merge,
                                if_exception_phi, if_exception_effect_phi);
  }

  // Check if the call property returns a valid JSReceiver else throw an invalid
  // iterator runtime exception
  Node* is_receiver =
      graph()->NewNode(simplified()->ObjectIsReceiver(), call_property);
  Node* branch_node = graph()->NewNode(
      common()->Branch(BranchHint::kNone, IsSafetyCheck::kNoSafetyCheck),
      is_receiver, control);
  {
    // Create a version of effect and control for the false path of the branch
    Node* effect = call_property;
    Node* control = call_property;
    Node* if_not_receiver = graph()->NewNode(common()->IfFalse(), branch_node);
    control = if_not_receiver;
    const Operator* call_runtime_op =
        javascript()->CallRuntime(Runtime::kThrowSymbolIteratorInvalid, 0);
    Node* call_runtime = graph()->NewNode(call_runtime_op, context, frame_state,
                                          effect, control);
    control = call_runtime;
    effect = call_runtime;
    if (has_exception_node) {
      control =
          AppendExceptionHandling(effect, control, if_exception_merge,
                                  if_exception_phi, if_exception_effect_phi);
    }
    Node* throw_node =
        graph()->NewNode(common()->Throw(), call_runtime, control);
    NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
  }
1481

1482 1483 1484
  Node* if_receiver = graph()->NewNode(common()->IfTrue(), branch_node);
  ReplaceWithValue(node, call_property, effect, if_receiver);
  return Replace(if_receiver);
1485 1486
}

1487 1488 1489 1490
Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) {
  DCHECK_EQ(IrOpcode::kJSStoreNamed, node->opcode());
  NamedAccess const& p = NamedAccessOf(node->op());
  Node* const value = NodeProperties::GetValueInput(node, 1);
1491

1492
  if (!p.feedback().IsValid()) return NoChange();
1493 1494
  return ReducePropertyAccess(node, nullptr, NameRef(broker(), p.name()), value,
                              FeedbackSource(p.feedback()), AccessMode::kStore);
1495 1496
}

1497 1498 1499 1500 1501 1502
Reduction JSNativeContextSpecialization::ReduceJSStoreNamedOwn(Node* node) {
  DCHECK_EQ(IrOpcode::kJSStoreNamedOwn, node->opcode());
  StoreNamedOwnParameters const& p = StoreNamedOwnParametersOf(node->op());
  Node* const value = NodeProperties::GetValueInput(node, 1);

  if (!p.feedback().IsValid()) return NoChange();
1503 1504 1505
  return ReducePropertyAccess(node, nullptr, NameRef(broker(), p.name()), value,
                              FeedbackSource(p.feedback()),
                              AccessMode::kStoreInLiteral);
1506
}
1507

1508
Reduction JSNativeContextSpecialization::ReduceElementAccessOnString(
1509
    Node* node, Node* index, Node* value, KeyedAccessMode const& keyed_mode) {
1510 1511 1512 1513 1514
  Node* receiver = NodeProperties::GetValueInput(node, 0);
  Node* effect = NodeProperties::GetEffectInput(node);
  Node* control = NodeProperties::GetControlInput(node);

  // Strings are immutable in JavaScript.
1515
  if (keyed_mode.access_mode() == AccessMode::kStore) return NoChange();
1516

1517
  // `in` cannot be used on strings.
1518
  if (keyed_mode.access_mode() == AccessMode::kHas) return NoChange();
1519

1520 1521
  // Ensure that the {receiver} is actually a String.
  receiver = effect = graph()->NewNode(
1522
      simplified()->CheckString(FeedbackSource()), receiver, effect, control);
1523 1524 1525 1526 1527 1528 1529

  // Determine the {receiver} length.
  Node* length = graph()->NewNode(simplified()->StringLength(), receiver);

  // Load the single character string from {receiver} or yield undefined
  // if the {index} is out of bounds (depending on the {load_mode}).
  value = BuildIndexedStringLoad(receiver, index, length, &effect, &control,
1530
                                 keyed_mode.load_mode());
1531 1532 1533 1534 1535

  ReplaceWithValue(node, value, effect, control);
  return Replace(value);
}

1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548
namespace {
base::Optional<JSTypedArrayRef> GetTypedArrayConstant(JSHeapBroker* broker,
                                                      Node* receiver) {
  HeapObjectMatcher m(receiver);
  if (!m.HasValue()) return base::nullopt;
  ObjectRef object = m.Ref(broker);
  if (!object.IsJSTypedArray()) return base::nullopt;
  JSTypedArrayRef typed_array = object.AsJSTypedArray();
  if (typed_array.is_on_heap()) return base::nullopt;
  return typed_array;
}
}  // namespace

1549 1550
void JSNativeContextSpecialization::RemoveImpossibleReceiverMaps(
    Node* receiver, ZoneVector<Handle<Map>>* receiver_maps) const {
1551 1552
  base::Optional<MapRef> root_map = InferReceiverRootMap(receiver);
  if (root_map.has_value()) {
1553 1554 1555
    DCHECK(!root_map->is_abandoned_prototype_map());
    receiver_maps->erase(
        std::remove_if(receiver_maps->begin(), receiver_maps->end(),
1556 1557 1558 1559 1560
                       [root_map, this](Handle<Map> map) {
                         MapRef map_ref(broker(), map);
                         return map_ref.is_abandoned_prototype_map() ||
                                (map_ref.FindRootMap().has_value() &&
                                 !map_ref.FindRootMap()->equals(*root_map));
1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585
                       }),
        receiver_maps->end());
  }
}

// Possibly refine the feedback using inferred map information from the graph.
ElementAccessFeedback const&
JSNativeContextSpecialization::TryRefineElementAccessFeedback(
    ElementAccessFeedback const& feedback, Node* receiver, Node* effect) const {
  AccessMode access_mode = feedback.keyed_mode().access_mode();
  bool use_inference =
      access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas;
  if (!use_inference) return feedback;

  ZoneVector<Handle<Map>> inferred_maps(zone());
  if (!InferReceiverMaps(receiver, effect, &inferred_maps)) return feedback;

  RemoveImpossibleReceiverMaps(receiver, &inferred_maps);
  // TODO(neis): After Refine, the resulting feedback can still contain
  // impossible maps when a target is kept only because more than one of its
  // sources was inferred. Think of a way to completely rule out impossible
  // maps.
  return feedback.Refine(inferred_maps, zone());
}

1586
Reduction JSNativeContextSpecialization::ReduceElementAccess(
1587
    Node* node, Node* index, Node* value,
1588
    ElementAccessFeedback const& feedback) {
1589
  DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
1590
         node->opcode() == IrOpcode::kJSStoreProperty ||
1591
         node->opcode() == IrOpcode::kJSStoreInArrayLiteral ||
1592
         node->opcode() == IrOpcode::kJSStoreDataPropertyInLiteral ||
1593
         node->opcode() == IrOpcode::kJSHasProperty);
1594

1595 1596 1597
  Node* receiver = NodeProperties::GetValueInput(node, 0);
  Node* effect = NodeProperties::GetEffectInput(node);
  Node* control = NodeProperties::GetControlInput(node);
1598 1599
  Node* frame_state =
      NodeProperties::FindFrameStateBefore(node, jsgraph()->Dead());
1600

1601 1602 1603 1604 1605 1606 1607 1608
  // TODO(neis): It's odd that we do optimizations below that don't really care
  // about the feedback, but we don't do them when the feedback is megamorphic.
  if (feedback.transition_groups().empty()) return NoChange();

  ElementAccessFeedback const& refined_feedback =
      TryRefineElementAccessFeedback(feedback, receiver, effect);

  AccessMode access_mode = refined_feedback.keyed_mode().access_mode();
1609 1610
  if ((access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) &&
      receiver->opcode() == IrOpcode::kHeapConstant) {
1611
    Reduction reduction = ReduceElementLoadFromHeapConstant(
1612
        node, index, access_mode, refined_feedback.keyed_mode().load_mode());
1613 1614 1615
    if (reduction.Changed()) return reduction;
  }

1616 1617
  if (!refined_feedback.transition_groups().empty() &&
      refined_feedback.HasOnlyStringMaps(broker())) {
1618
    return ReduceElementAccessOnString(node, index, value,
1619
                                       refined_feedback.keyed_mode());
1620
  }
1621

1622 1623
  AccessInfoFactory access_info_factory(broker(), dependencies(),
                                        graph()->zone());
1624
  ZoneVector<ElementAccessInfo> access_infos(zone());
1625 1626 1627
  if (!access_info_factory.ComputeElementAccessInfos(refined_feedback,
                                                     &access_infos) ||
      access_infos.empty()) {
1628 1629
    return NoChange();
  }
1630

1631 1632 1633 1634 1635 1636 1637
  // For holey stores or growing stores, we need to check that the prototype
  // chain contains no setters for elements, and we need to guard those checks
  // via code dependencies on the relevant prototype maps.
  if (access_mode == AccessMode::kStore) {
    // TODO(turbofan): We could have a fast path here, that checks for the
    // common case of Array or Object prototype only and therefore avoids
    // the zone allocation of this vector.
1638
    ZoneVector<MapRef> prototype_maps(zone());
1639
    for (ElementAccessInfo const& access_info : access_infos) {
1640 1641
      for (Handle<Map> map : access_info.receiver_maps()) {
        MapRef receiver_map(broker(), map);
1642 1643 1644 1645
        // If the {receiver_map} has a prototype and its elements backing
        // store is either holey, or we have a potentially growing store,
        // then we need to check that all prototypes have stable maps with
        // fast elements (and we need to guard against changes to that below).
1646
        if ((IsHoleyOrDictionaryElementsKind(receiver_map.elements_kind()) ||
1647
             IsGrowStoreMode(feedback.keyed_mode().store_mode())) &&
1648 1649 1650
            !receiver_map.HasOnlyStablePrototypesWithFastElements(
                &prototype_maps)) {
          return NoChange();
1651 1652
        }
      }
1653
    }
1654 1655
    for (MapRef const& prototype_map : prototype_maps) {
      dependencies()->DependOnStableMap(prototype_map);
1656
    }
1657 1658 1659 1660 1661
  } else if (access_mode == AccessMode::kHas) {
    // If we have any fast arrays, we need to check and depend on
    // NoElementsProtector.
    for (ElementAccessInfo const& access_info : access_infos) {
      if (IsFastElementsKind(access_info.elements_kind())) {
1662
        if (!dependencies()->DependOnNoElementsProtector()) return NoChange();
1663 1664 1665
        break;
      }
    }
1666
  }
1667

1668 1669
  // Check if we have the necessary data for building element accesses.
  for (ElementAccessInfo const& access_info : access_infos) {
1670
    if (!IsTypedArrayElementsKind(access_info.elements_kind())) continue;
1671 1672 1673
    base::Optional<JSTypedArrayRef> typed_array =
        GetTypedArrayConstant(broker(), receiver);
    if (typed_array.has_value()) {
1674
      if (FLAG_concurrent_inlining && !typed_array->serialized()) {
1675
        TRACE_BROKER_MISSING(broker(), "data for typed array " << *typed_array);
1676 1677 1678 1679 1680
        return NoChange();
      }
    }
  }

1681
  // Check for the monomorphic case.
1682
  PropertyAccessBuilder access_builder(jsgraph(), broker(), dependencies());
1683 1684 1685 1686
  if (access_infos.size() == 1) {
    ElementAccessInfo access_info = access_infos.front();

    // Perform possible elements kind transitions.
1687 1688
    MapRef transition_target(broker(), access_info.receiver_maps().front());
    for (auto source : access_info.transition_sources()) {
1689
      DCHECK_EQ(access_info.receiver_maps().size(), 1);
1690
      MapRef transition_source(broker(), source);
1691 1692
      effect = graph()->NewNode(
          simplified()->TransitionElementsKind(ElementsTransition(
1693 1694
              IsSimpleMapChangeTransition(transition_source.elements_kind(),
                                          transition_target.elements_kind())
1695 1696
                  ? ElementsTransition::kFastTransition
                  : ElementsTransition::kSlowTransition,
1697
              transition_source.object(), transition_target.object())),
1698
          receiver, effect, control);
1699 1700
    }

1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715
    // TODO(turbofan): The effect/control linearization will not find a
    // FrameState after the StoreField or Call that is generated for the
    // elements kind transition above. This is because those operators
    // don't have the kNoWrite flag on it, even though they are not
    // observable by JavaScript.
    effect =
        graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);

    // Perform map check on the {receiver}.
    access_builder.BuildCheckMaps(receiver, &effect, control,
                                  access_info.receiver_maps());

    // Access the actual element.
    ValueEffectControl continuation =
        BuildElementAccess(receiver, index, value, effect, control, access_info,
1716
                           feedback.keyed_mode());
1717 1718 1719 1720 1721 1722 1723 1724 1725
    value = continuation.value();
    effect = continuation.effect();
    control = continuation.control();
  } else {
    // The final states for every polymorphic branch. We join them with
    // Merge+Phi+EffectPhi at the bottom.
    ZoneVector<Node*> values(zone());
    ZoneVector<Node*> effects(zone());
    ZoneVector<Node*> controls(zone());
1726

1727 1728 1729 1730 1731 1732 1733 1734 1735
    // Generate code for the various different element access patterns.
    Node* fallthrough_control = control;
    for (size_t j = 0; j < access_infos.size(); ++j) {
      ElementAccessInfo const& access_info = access_infos[j];
      Node* this_receiver = receiver;
      Node* this_value = value;
      Node* this_index = index;
      Node* this_effect = effect;
      Node* this_control = fallthrough_control;
1736 1737

      // Perform possible elements kind transitions.
1738 1739 1740
      MapRef transition_target(broker(), access_info.receiver_maps().front());
      for (auto source : access_info.transition_sources()) {
        MapRef transition_source(broker(), source);
1741
        DCHECK_EQ(access_info.receiver_maps().size(), 1);
1742
        this_effect = graph()->NewNode(
1743
            simplified()->TransitionElementsKind(ElementsTransition(
1744 1745
                IsSimpleMapChangeTransition(transition_source.elements_kind(),
                                            transition_target.elements_kind())
1746
                    ? ElementsTransition::kFastTransition
1747
                    : ElementsTransition::kSlowTransition,
1748
                transition_source.object(), transition_target.object())),
1749
            receiver, this_effect, this_control);
1750 1751
      }

1752
      // Perform map check(s) on {receiver}.
1753 1754
      ZoneVector<Handle<Map>> const& receiver_maps =
          access_info.receiver_maps();
1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765
      if (j == access_infos.size() - 1) {
        // Last map check on the fallthrough control path, do a
        // conditional eager deoptimization exit here.
        access_builder.BuildCheckMaps(receiver, &this_effect, this_control,
                                      receiver_maps);
        fallthrough_control = nullptr;
      } else {
        // Explicitly branch on the {receiver_maps}.
        ZoneHandleSet<Map> maps;
        for (Handle<Map> map : receiver_maps) {
          maps.insert(map, graph()->zone());
1766
        }
1767 1768 1769 1770 1771 1772 1773
        Node* check = this_effect =
            graph()->NewNode(simplified()->CompareMaps(maps), receiver,
                             this_effect, fallthrough_control);
        Node* branch =
            graph()->NewNode(common()->Branch(), check, fallthrough_control);
        fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
        this_control = graph()->NewNode(common()->IfTrue(), branch);
1774

1775 1776 1777
        // Introduce a MapGuard to learn from this on the effect chain.
        this_effect = graph()->NewNode(simplified()->MapGuard(maps), receiver,
                                       this_effect, this_control);
1778
      }
1779

1780
      // Access the actual element.
1781 1782
      ValueEffectControl continuation =
          BuildElementAccess(this_receiver, this_index, this_value, this_effect,
1783
                             this_control, access_info, feedback.keyed_mode());
1784 1785 1786 1787
      values.push_back(continuation.value());
      effects.push_back(continuation.effect());
      controls.push_back(continuation.control());
    }
1788

1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808
    DCHECK_NULL(fallthrough_control);

    // Generate the final merge point for all (polymorphic) branches.
    int const control_count = static_cast<int>(controls.size());
    if (control_count == 0) {
      value = effect = control = jsgraph()->Dead();
    } else if (control_count == 1) {
      value = values.front();
      effect = effects.front();
      control = controls.front();
    } else {
      control = graph()->NewNode(common()->Merge(control_count), control_count,
                                 &controls.front());
      values.push_back(control);
      value = graph()->NewNode(
          common()->Phi(MachineRepresentation::kTagged, control_count),
          control_count + 1, &values.front());
      effects.push_back(control);
      effect = graph()->NewNode(common()->EffectPhi(control_count),
                                control_count + 1, &effects.front());
1809
    }
1810
  }
1811

1812 1813
  ReplaceWithValue(node, value, effect, control);
  return Replace(value);
1814 1815
}

1816
Reduction JSNativeContextSpecialization::ReduceElementLoadFromHeapConstant(
1817
    Node* node, Node* key, AccessMode access_mode,
1818
    KeyedAccessLoadMode load_mode) {
1819 1820
  DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
         node->opcode() == IrOpcode::kJSHasProperty);
1821 1822 1823 1824
  Node* receiver = NodeProperties::GetValueInput(node, 0);
  Node* effect = NodeProperties::GetEffectInput(node);
  Node* control = NodeProperties::GetControlInput(node);

1825
  HeapObjectMatcher mreceiver(receiver);
1826
  HeapObjectRef receiver_ref = mreceiver.Ref(broker());
1827 1828
  if (receiver_ref.map().oddball_type() == OddballType::kHole ||
      receiver_ref.map().oddball_type() == OddballType::kNull ||
1829
      receiver_ref.map().oddball_type() == OddballType::kUndefined ||
1830
      // The 'in' operator throws a TypeError on primitive values.
1831
      (receiver_ref.IsString() && access_mode == AccessMode::kHas)) {
1832 1833
    return NoChange();
  }
1834

1835 1836
  // Check whether we're accessing a known element on the {receiver} and can
  // constant-fold the load.
1837 1838 1839
  NumberMatcher mkey(key);
  if (mkey.IsInteger() && mkey.IsInRange(0.0, kMaxUInt32 - 1.0)) {
    uint32_t index = static_cast<uint32_t>(mkey.Value());
1840
    base::Optional<ObjectRef> element =
1841
        receiver_ref.GetOwnConstantElement(index);
1842 1843 1844 1845
    if (!element.has_value() && receiver_ref.IsJSArray()) {
      // We didn't find a constant element, but if the receiver is a cow-array
      // we can exploit the fact that any future write to the element will
      // replace the whole elements storage.
1846
      element = receiver_ref.AsJSArray().GetOwnCowElement(index);
1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857
      if (element.has_value()) {
        Node* elements = effect = graph()->NewNode(
            simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
            receiver, effect, control);
        FixedArrayRef array_elements =
            receiver_ref.AsJSArray().elements().AsFixedArray();
        Node* check = graph()->NewNode(simplified()->ReferenceEqual(), elements,
                                       jsgraph()->Constant(array_elements));
        effect = graph()->NewNode(
            simplified()->CheckIf(DeoptimizeReason::kCowArrayElementsChanged),
            check, effect, control);
1858
      }
1859
    }
1860 1861 1862 1863 1864 1865 1866
    if (element.has_value()) {
      Node* value = access_mode == AccessMode::kHas
                        ? jsgraph()->TrueConstant()
                        : jsgraph()->Constant(*element);
      ReplaceWithValue(node, value, effect, control);
      return Replace(value);
    }
1867
  }
1868

1869 1870
  // For constant Strings we can eagerly strength-reduce the keyed
  // accesses using the known length, which doesn't change.
1871 1872
  if (receiver_ref.IsString()) {
    DCHECK_NE(access_mode, AccessMode::kHas);
1873 1874 1875 1876 1877 1878 1879 1880 1881 1882
    // Ensure that {key} is less than {receiver} length.
    Node* length = jsgraph()->Constant(receiver_ref.AsString().length());

    // Load the single character string from {receiver} or yield
    // undefined if the {key} is out of bounds (depending on the
    // {load_mode}).
    Node* value = BuildIndexedStringLoad(receiver, key, length, &effect,
                                         &control, load_mode);
    ReplaceWithValue(node, value, effect, control);
    return Replace(value);
1883 1884 1885 1886 1887
  }

  return NoChange();
}

1888
Reduction JSNativeContextSpecialization::ReducePropertyAccess(
1889
    Node* node, Node* key, base::Optional<NameRef> static_name, Node* value,
1890
    FeedbackSource const& source, AccessMode access_mode) {
1891 1892
  DisallowHeapAccessIf disallow_heap_access(FLAG_concurrent_inlining);

1893 1894
  DCHECK_EQ(key == nullptr, static_name.has_value());
  DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
1895
         node->opcode() == IrOpcode::kJSStoreProperty ||
1896
         node->opcode() == IrOpcode::kJSStoreInArrayLiteral ||
1897
         node->opcode() == IrOpcode::kJSStoreDataPropertyInLiteral ||
1898 1899 1900
         node->opcode() == IrOpcode::kJSHasProperty ||
         node->opcode() == IrOpcode::kJSLoadNamed ||
         node->opcode() == IrOpcode::kJSStoreNamed ||
1901
         node->opcode() == IrOpcode::kJSStoreNamedOwn);
1902
  DCHECK_GE(node->op()->ControlOutputCount(), 1);
1903

1904 1905 1906
  ProcessedFeedback const& feedback =
      broker()->GetFeedbackForPropertyAccess(source, access_mode, static_name);
  switch (feedback.kind()) {
1907 1908 1909 1910 1911
    case ProcessedFeedback::kInsufficient:
      return ReduceSoftDeoptimize(
          node,
          DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess);
    case ProcessedFeedback::kNamedAccess:
1912
      return ReduceNamedAccess(node, value, feedback.AsNamedAccess(),
1913 1914
                               access_mode, key);
    case ProcessedFeedback::kElementAccess:
1915 1916 1917 1918
      DCHECK_EQ(feedback.AsElementAccess().keyed_mode().access_mode(),
                access_mode);
      return ReduceElementAccess(node, key, value, feedback.AsElementAccess());
    default:
1919
      UNREACHABLE();
1920 1921 1922
  }
}

1923 1924
Reduction JSNativeContextSpecialization::ReduceSoftDeoptimize(
    Node* node, DeoptimizeReason reason) {
1925 1926 1927 1928 1929 1930 1931
  if (!(flags() & kBailoutOnUninitialized)) return NoChange();

  Node* effect = NodeProperties::GetEffectInput(node);
  Node* control = NodeProperties::GetControlInput(node);
  Node* frame_state =
      NodeProperties::FindFrameStateBefore(node, jsgraph()->Dead());
  Node* deoptimize = graph()->NewNode(
1932
      common()->Deoptimize(DeoptimizeKind::kSoft, reason, FeedbackSource()),
1933 1934 1935 1936 1937 1938 1939
      frame_state, effect, control);
  // TODO(bmeurer): This should be on the AdvancedReducer somehow.
  NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
  Revisit(graph()->end());
  node->TrimInputCount(0);
  NodeProperties::ChangeOp(node, common()->Dead());
  return Changed(node);
1940 1941
}

1942 1943 1944
Reduction JSNativeContextSpecialization::ReduceJSHasProperty(Node* node) {
  DCHECK_EQ(IrOpcode::kJSHasProperty, node->opcode());
  PropertyAccess const& p = PropertyAccessOf(node->op());
1945
  Node* key = NodeProperties::GetValueInput(node, 1);
1946 1947 1948
  Node* value = jsgraph()->Dead();

  if (!p.feedback().IsValid()) return NoChange();
1949 1950
  return ReducePropertyAccess(node, key, base::nullopt, value,
                              FeedbackSource(p.feedback()), AccessMode::kHas);
1951 1952
}

1953 1954 1955
Reduction JSNativeContextSpecialization::ReduceJSLoadPropertyWithEnumeratedKey(
    Node* node) {
  // We can optimize a property load if it's being used inside a for..in:
1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984
  //   for (name in receiver) {
  //     value = receiver[name];
  //     ...
  //   }
  //
  // If the for..in is in fast-mode, we know that the {receiver} has {name}
  // as own property, otherwise the enumeration wouldn't include it. The graph
  // constructed by the BytecodeGraphBuilder in this case looks like this:

  // receiver
  //  ^    ^
  //  |    |
  //  |    +-+
  //  |      |
  //  |   JSToObject
  //  |      ^
  //  |      |
  //  |      |
  //  |  JSForInNext
  //  |      ^
  //  |      |
  //  +----+ |
  //       | |
  //       | |
  //   JSLoadProperty

  // If the for..in has only seen maps with enum cache consisting of keys
  // and indices so far, we can turn the {JSLoadProperty} into a map check
  // on the {receiver} and then just load the field value dynamically via
1985 1986 1987
  // the {LoadFieldByIndex} operator. The map check is only necessary when
  // TurboFan cannot prove that there is no observable side effect between
  // the {JSForInNext} and the {JSLoadProperty} node.
1988 1989 1990 1991
  //
  // Also note that it's safe to look through the {JSToObject}, since the
  // [[Get]] operation does an implicit ToObject anyway, and these operations
  // are not observable.
1992

1993 1994 1995 1996 1997 1998
  DCHECK_EQ(IrOpcode::kJSLoadProperty, node->opcode());
  Node* receiver = NodeProperties::GetValueInput(node, 0);
  Node* name = NodeProperties::GetValueInput(node, 1);
  DCHECK_EQ(IrOpcode::kJSForInNext, name->opcode());
  Node* effect = NodeProperties::GetEffectInput(node);
  Node* control = NodeProperties::GetControlInput(node);
1999

2000 2001 2002 2003 2004 2005
  if (ForInModeOf(name->op()) != ForInMode::kUseEnumCacheKeysAndIndices) {
    return NoChange();
  }

  Node* object = NodeProperties::GetValueInput(name, 0);
  Node* enumerator = NodeProperties::GetValueInput(name, 2);
2006
  Node* key = NodeProperties::GetValueInput(name, 3);
2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045
  if (object->opcode() == IrOpcode::kJSToObject) {
    object = NodeProperties::GetValueInput(object, 0);
  }
  if (object != receiver) return NoChange();

  // No need to repeat the map check if we can prove that there's no
  // observable side effect between {effect} and {name].
  if (!NodeProperties::NoObservableSideEffectBetween(effect, name)) {
    // Check that the {receiver} map is still valid.
    Node* receiver_map = effect =
        graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
                         receiver, effect, control);
    Node* check = graph()->NewNode(simplified()->ReferenceEqual(), receiver_map,
                                   enumerator);
    effect =
        graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kWrongMap),
                         check, effect, control);
  }

  // Load the enum cache indices from the {cache_type}.
  Node* descriptor_array = effect = graph()->NewNode(
      simplified()->LoadField(AccessBuilder::ForMapDescriptors()), enumerator,
      effect, control);
  Node* enum_cache = effect = graph()->NewNode(
      simplified()->LoadField(AccessBuilder::ForDescriptorArrayEnumCache()),
      descriptor_array, effect, control);
  Node* enum_indices = effect = graph()->NewNode(
      simplified()->LoadField(AccessBuilder::ForEnumCacheIndices()), enum_cache,
      effect, control);

  // Ensure that the {enum_indices} are valid.
  Node* check = graph()->NewNode(
      simplified()->BooleanNot(),
      graph()->NewNode(simplified()->ReferenceEqual(), enum_indices,
                       jsgraph()->EmptyFixedArrayConstant()));
  effect = graph()->NewNode(
      simplified()->CheckIf(DeoptimizeReason::kWrongEnumIndices), check, effect,
      control);

2046 2047
  // Determine the key from the {enum_indices}.
  key = effect = graph()->NewNode(
2048 2049
      simplified()->LoadElement(
          AccessBuilder::ForFixedArrayElement(PACKED_SMI_ELEMENTS)),
2050
      enum_indices, key, effect, control);
2051 2052 2053

  // Load the actual field value.
  Node* value = effect = graph()->NewNode(simplified()->LoadFieldByIndex(),
2054
                                          receiver, key, effect, control);
2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066
  ReplaceWithValue(node, value, effect, control);
  return Replace(value);
}

Reduction JSNativeContextSpecialization::ReduceJSLoadProperty(Node* node) {
  DCHECK_EQ(IrOpcode::kJSLoadProperty, node->opcode());
  PropertyAccess const& p = PropertyAccessOf(node->op());
  Node* name = NodeProperties::GetValueInput(node, 1);

  if (name->opcode() == IrOpcode::kJSForInNext) {
    Reduction reduction = ReduceJSLoadPropertyWithEnumeratedKey(node);
    if (reduction.Changed()) return reduction;
2067
  }
2068 2069

  if (!p.feedback().IsValid()) return NoChange();
2070
  Node* value = jsgraph()->Dead();
2071 2072
  return ReducePropertyAccess(node, name, base::nullopt, value,
                              FeedbackSource(p.feedback()), AccessMode::kLoad);
2073 2074 2075 2076 2077
}

Reduction JSNativeContextSpecialization::ReduceJSStoreProperty(Node* node) {
  DCHECK_EQ(IrOpcode::kJSStoreProperty, node->opcode());
  PropertyAccess const& p = PropertyAccessOf(node->op());
2078
  Node* const key = NodeProperties::GetValueInput(node, 1);
2079 2080 2081
  Node* const value = NodeProperties::GetValueInput(node, 2);

  if (!p.feedback().IsValid()) return NoChange();
2082 2083
  return ReducePropertyAccess(node, key, base::nullopt, value,
                              FeedbackSource(p.feedback()), AccessMode::kStore);
2084 2085
}

2086 2087 2088 2089
Node* JSNativeContextSpecialization::InlinePropertyGetterCall(
    Node* receiver, Node* context, Node* frame_state, Node** effect,
    Node** control, ZoneVector<Node*>* if_exceptions,
    PropertyAccessInfo const& access_info) {
2090 2091
  ObjectRef constant(broker(), access_info.constant());
  Node* target = jsgraph()->Constant(constant);
2092
  FrameStateInfo const& frame_info = FrameStateInfoOf(frame_state->op());
2093 2094
  // Introduce the call to the getter function.
  Node* value;
2095
  if (constant.IsJSFunction()) {
2096
    value = *effect = *control = graph()->NewNode(
2097
        jsgraph()->javascript()->Call(2, CallFrequency(), FeedbackSource(),
2098
                                      ConvertReceiverMode::kNotNullOrUndefined),
2099
        target, receiver, context, frame_state, *effect, *control);
2100
  } else {
2101 2102 2103 2104
    Node* holder = access_info.holder().is_null()
                       ? receiver
                       : jsgraph()->Constant(ObjectRef(
                             broker(), access_info.holder().ToHandleChecked()));
2105 2106
    SharedFunctionInfoRef shared_info(
        broker(), frame_info.shared_info().ToHandleChecked());
2107

2108 2109 2110
    value =
        InlineApiCall(receiver, holder, frame_state, nullptr, effect, control,
                      shared_info, constant.AsFunctionTemplateInfo());
2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123
  }
  // Remember to rewire the IfException edge if this is inside a try-block.
  if (if_exceptions != nullptr) {
    // Create the appropriate IfException/IfSuccess projections.
    Node* const if_exception =
        graph()->NewNode(common()->IfException(), *control, *effect);
    Node* const if_success = graph()->NewNode(common()->IfSuccess(), *control);
    if_exceptions->push_back(if_exception);
    *control = if_success;
  }
  return value;
}

2124
void JSNativeContextSpecialization::InlinePropertySetterCall(
2125 2126 2127
    Node* receiver, Node* value, Node* context, Node* frame_state,
    Node** effect, Node** control, ZoneVector<Node*>* if_exceptions,
    PropertyAccessInfo const& access_info) {
2128 2129
  ObjectRef constant(broker(), access_info.constant());
  Node* target = jsgraph()->Constant(constant);
2130
  FrameStateInfo const& frame_info = FrameStateInfoOf(frame_state->op());
2131
  // Introduce the call to the setter function.
2132
  if (constant.IsJSFunction()) {
2133
    *effect = *control = graph()->NewNode(
2134
        jsgraph()->javascript()->Call(3, CallFrequency(), FeedbackSource(),
2135
                                      ConvertReceiverMode::kNotNullOrUndefined),
2136
        target, receiver, value, context, frame_state, *effect, *control);
2137
  } else {
2138 2139 2140 2141
    Node* holder = access_info.holder().is_null()
                       ? receiver
                       : jsgraph()->Constant(ObjectRef(
                             broker(), access_info.holder().ToHandleChecked()));
2142 2143
    SharedFunctionInfoRef shared_info(
        broker(), frame_info.shared_info().ToHandleChecked());
2144
    InlineApiCall(receiver, holder, frame_state, value, effect, control,
2145
                  shared_info, constant.AsFunctionTemplateInfo());
2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158
  }
  // Remember to rewire the IfException edge if this is inside a try-block.
  if (if_exceptions != nullptr) {
    // Create the appropriate IfException/IfSuccess projections.
    Node* const if_exception =
        graph()->NewNode(common()->IfException(), *control, *effect);
    Node* const if_success = graph()->NewNode(common()->IfSuccess(), *control);
    if_exceptions->push_back(if_exception);
    *control = if_success;
  }
}

Node* JSNativeContextSpecialization::InlineApiCall(
2159
    Node* receiver, Node* holder, Node* frame_state, Node* value, Node** effect,
2160 2161
    Node** control, SharedFunctionInfoRef const& shared_info,
    FunctionTemplateInfoRef const& function_template_info) {
2162 2163 2164 2165
  if (!function_template_info.has_call_code()) {
    return nullptr;
  }

2166 2167 2168 2169 2170
  if (!function_template_info.call_code().has_value()) {
    TRACE_BROKER_MISSING(broker(), "call code for function template info "
                                       << function_template_info);
    return nullptr;
  }
2171
  CallHandlerInfoRef call_handler_info = *function_template_info.call_code();
2172 2173 2174 2175

  // Only setters have a value.
  int const argc = value == nullptr ? 0 : 1;
  // The stub always expects the receiver as the first param on the stack.
2176
  Callable call_api_callback = CodeFactory::CallApiCallback(isolate());
2177
  CallInterfaceDescriptor call_interface_descriptor =
2178
      call_api_callback.descriptor();
2179
  auto call_descriptor = Linkage::GetStubCallDescriptor(
2180
      graph()->zone(), call_interface_descriptor,
2181
      call_interface_descriptor.GetStackParameterCount() + argc +
2182
          1 /* implicit receiver */,
2183
      CallDescriptor::kNeedsFrameState);
2184

2185 2186
  Node* data = jsgraph()->Constant(call_handler_info.data());
  ApiFunction function(call_handler_info.callback());
2187
  Node* function_reference =
2188 2189
      graph()->NewNode(common()->ExternalConstant(ExternalReference::Create(
          &function, ExternalReference::DIRECT_API_CALL)));
2190
  Node* code = jsgraph()->HeapConstant(call_api_callback.code());
2191 2192

  // Add CallApiCallbackStub's register argument as well.
2193
  Node* context = jsgraph()->Constant(native_context());
2194
  Node* inputs[11] = {
2195 2196 2197 2198
      code,    function_reference, jsgraph()->Constant(argc), data, holder,
      receiver};
  int index = 6 + argc;
  inputs[index++] = context;
2199 2200 2201 2202 2203 2204
  inputs[index++] = frame_state;
  inputs[index++] = *effect;
  inputs[index++] = *control;
  // This needs to stay here because of the edge case described in
  // http://crbug.com/675648.
  if (value != nullptr) {
2205
    inputs[6] = value;
2206 2207 2208 2209 2210 2211 2212 2213 2214
  }

  return *effect = *control =
             graph()->NewNode(common()->Call(call_descriptor), index, inputs);
}

JSNativeContextSpecialization::ValueEffectControl
JSNativeContextSpecialization::BuildPropertyLoad(
    Node* receiver, Node* context, Node* frame_state, Node* effect,
2215
    Node* control, NameRef const& name, ZoneVector<Node*>* if_exceptions,
2216
    PropertyAccessInfo const& access_info) {
2217 2218 2219
  // Determine actual holder and perform prototype chain checks.
  Handle<JSObject> holder;
  if (access_info.holder().ToHandle(&holder)) {
2220
    dependencies()->DependOnStablePrototypeChains(
2221 2222
        access_info.receiver_maps(), kStartAtPrototype,
        JSObjectRef(broker(), holder));
2223 2224 2225 2226 2227 2228 2229 2230 2231
  }

  // Generate the actual property access.
  Node* value;
  if (access_info.IsNotFound()) {
    value = jsgraph()->UndefinedConstant();
  } else if (access_info.IsAccessorConstant()) {
    value = InlinePropertyGetterCall(receiver, context, frame_state, &effect,
                                     &control, if_exceptions, access_info);
2232
  } else if (access_info.IsModuleExport()) {
2233 2234
    Node* cell = jsgraph()->Constant(
        ObjectRef(broker(), access_info.constant()).AsCell());
2235 2236 2237
    value = effect =
        graph()->NewNode(simplified()->LoadField(AccessBuilder::ForCellValue()),
                         cell, effect, control);
2238 2239
  } else if (access_info.IsStringLength()) {
    value = graph()->NewNode(simplified()->StringLength(), receiver);
2240
  } else {
2241
    DCHECK(access_info.IsDataField() || access_info.IsDataConstant());
2242
    PropertyAccessBuilder access_builder(jsgraph(), broker(), dependencies());
2243 2244 2245 2246 2247 2248 2249
    value = access_builder.BuildLoadDataField(name, access_info, receiver,
                                              &effect, &control);
  }

  return ValueEffectControl(value, effect, control);
}

2250 2251 2252 2253 2254 2255 2256
JSNativeContextSpecialization::ValueEffectControl
JSNativeContextSpecialization::BuildPropertyTest(
    Node* effect, Node* control, PropertyAccessInfo const& access_info) {
  // Determine actual holder and perform prototype chain checks.
  Handle<JSObject> holder;
  if (access_info.holder().ToHandle(&holder)) {
    dependencies()->DependOnStablePrototypeChains(
2257 2258
        access_info.receiver_maps(), kStartAtPrototype,
        JSObjectRef(broker(), holder));
2259 2260 2261 2262 2263 2264 2265
  }

  Node* value = access_info.IsNotFound() ? jsgraph()->FalseConstant()
                                         : jsgraph()->TrueConstant();
  return ValueEffectControl(value, effect, control);
}

2266 2267
JSNativeContextSpecialization::ValueEffectControl
JSNativeContextSpecialization::BuildPropertyAccess(
2268
    Node* receiver, Node* value, Node* context, Node* frame_state, Node* effect,
2269
    Node* control, NameRef const& name, ZoneVector<Node*>* if_exceptions,
2270
    PropertyAccessInfo const& access_info, AccessMode access_mode) {
2271 2272 2273
  switch (access_mode) {
    case AccessMode::kLoad:
      return BuildPropertyLoad(receiver, context, frame_state, effect, control,
2274
                               name, if_exceptions, access_info);
2275 2276 2277 2278
    case AccessMode::kStore:
    case AccessMode::kStoreInLiteral:
      return BuildPropertyStore(receiver, value, context, frame_state, effect,
                                control, name, if_exceptions, access_info,
2279
                                access_mode);
2280 2281
    case AccessMode::kHas:
      return BuildPropertyTest(effect, control, access_info);
2282 2283 2284 2285 2286 2287 2288
  }
  UNREACHABLE();
}

JSNativeContextSpecialization::ValueEffectControl
JSNativeContextSpecialization::BuildPropertyStore(
    Node* receiver, Node* value, Node* context, Node* frame_state, Node* effect,
2289
    Node* control, NameRef const& name, ZoneVector<Node*>* if_exceptions,
2290
    PropertyAccessInfo const& access_info, AccessMode access_mode) {
2291 2292
  // Determine actual holder and perform prototype chain checks.
  Handle<JSObject> holder;
2293
  PropertyAccessBuilder access_builder(jsgraph(), broker(), dependencies());
2294
  if (access_info.holder().ToHandle(&holder)) {
2295
    DCHECK_NE(AccessMode::kStoreInLiteral, access_mode);
2296
    dependencies()->DependOnStablePrototypeChains(
2297 2298
        access_info.receiver_maps(), kStartAtPrototype,
        JSObjectRef(broker(), holder));
2299 2300
  }

2301 2302
  DCHECK(!access_info.IsNotFound());

2303
  // Generate the actual property access.
2304
  if (access_info.IsAccessorConstant()) {
2305 2306
    InlinePropertySetterCall(receiver, value, context, frame_state, &effect,
                             &control, if_exceptions, access_info);
2307
  } else {
2308
    DCHECK(access_info.IsDataField() || access_info.IsDataConstant());
2309 2310
    DCHECK(access_mode == AccessMode::kStore ||
           access_mode == AccessMode::kStoreInLiteral);
2311
    FieldIndex const field_index = access_info.field_index();
2312
    Type const field_type = access_info.field_type();
2313
    MachineRepresentation const field_representation =
2314 2315
        PropertyAccessBuilder::ConvertRepresentation(
            access_info.field_representation());
2316 2317 2318
    Node* storage = receiver;
    if (!field_index.is_inobject()) {
      storage = effect = graph()->NewNode(
2319 2320
          simplified()->LoadField(
              AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer()),
2321 2322
          storage, effect, control);
    }
2323 2324 2325
    bool store_to_existing_constant_field = access_info.IsDataConstant() &&
                                            access_mode == AccessMode::kStore &&
                                            !access_info.HasTransitionMap();
2326
    FieldAccess field_access = {
2327 2328
        kTaggedBase,
        field_index.offset(),
2329
        name.object(),
2330
        MaybeHandle<Map>(),
2331 2332
        field_type,
        MachineType::TypeForRepresentation(field_representation),
2333 2334
        kFullWriteBarrier,
        LoadSensitivity::kUnsafe,
2335 2336
        access_info.GetConstFieldInfo(),
        access_mode == AccessMode::kStoreInLiteral};
2337 2338 2339

    switch (field_representation) {
      case MachineRepresentation::kFloat64: {
2340
        value = effect =
2341
            graph()->NewNode(simplified()->CheckNumber(FeedbackSource()), value,
2342
                             effect, control);
2343
        if (!field_index.is_inobject() || !FLAG_unbox_double_fields) {
2344
          if (access_info.HasTransitionMap()) {
2345
            // Allocate a HeapNumber for the new property.
2346
            AllocationBuilder a(jsgraph(), effect, control);
2347 2348
            a.Allocate(HeapNumber::kSize, AllocationType::kYoung,
                       Type::OtherInternal());
2349 2350
            a.Store(AccessBuilder::ForMap(),
                    MapRef(broker(), factory()->heap_number_map()));
2351 2352
            FieldAccess value_field_access =
                AccessBuilder::ForHeapNumberValue();
2353
            value_field_access.const_field_info = field_access.const_field_info;
2354
            a.Store(value_field_access, value);
2355
            value = effect = a.Finish();
2356 2357

            field_access.type = Type::Any();
2358 2359
            field_access.machine_type =
                MachineType::TypeCompressedTaggedPointer();
2360 2361
            field_access.write_barrier_kind = kPointerWriteBarrier;
          } else {
2362
            // We just store directly to the HeapNumber.
2363
            FieldAccess const storage_access = {
2364 2365 2366 2367 2368 2369
                kTaggedBase,
                field_index.offset(),
                name.object(),
                MaybeHandle<Map>(),
                Type::OtherInternal(),
                MachineType::TypeCompressedTaggedPointer(),
2370 2371
                kPointerWriteBarrier,
                LoadSensitivity::kUnsafe,
2372 2373
                access_info.GetConstFieldInfo(),
                access_mode == AccessMode::kStoreInLiteral};
2374 2375 2376 2377 2378 2379
            storage = effect =
                graph()->NewNode(simplified()->LoadField(storage_access),
                                 storage, effect, control);
            field_access.offset = HeapNumber::kValueOffset;
            field_access.name = MaybeHandle<Name>();
            field_access.machine_type = MachineType::Float64();
2380 2381
          }
        }
2382
        if (store_to_existing_constant_field) {
2383 2384 2385 2386 2387 2388
          DCHECK(!access_info.HasTransitionMap());
          // If the field is constant check that the value we are going
          // to store matches current value.
          Node* current_value = effect = graph()->NewNode(
              simplified()->LoadField(field_access), storage, effect, control);

2389 2390
          Node* check =
              graph()->NewNode(simplified()->SameValue(), current_value, value);
2391
          effect = graph()->NewNode(
2392 2393
              simplified()->CheckIf(DeoptimizeReason::kWrongValue), check,
              effect, control);
2394 2395 2396
          return ValueEffectControl(value, effect, control);
        }
        break;
2397
      }
2398 2399 2400
      case MachineRepresentation::kTaggedSigned:
      case MachineRepresentation::kTaggedPointer:
      case MachineRepresentation::kTagged:
2401 2402 2403
      case MachineRepresentation::kCompressedSigned:
      case MachineRepresentation::kCompressedPointer:
      case MachineRepresentation::kCompressed:
2404
        if (store_to_existing_constant_field) {
2405 2406 2407 2408 2409 2410
          DCHECK(!access_info.HasTransitionMap());
          // If the field is constant check that the value we are going
          // to store matches current value.
          Node* current_value = effect = graph()->NewNode(
              simplified()->LoadField(field_access), storage, effect, control);

2411
          Node* check = graph()->NewNode(simplified()->SameValueNumbersOnly(),
2412
                                         current_value, value);
2413
          effect = graph()->NewNode(
2414 2415
              simplified()->CheckIf(DeoptimizeReason::kWrongValue), check,
              effect, control);
2416
          return ValueEffectControl(value, effect, control);
2417
        }
2418

2419 2420
        if (field_representation == MachineRepresentation::kTaggedSigned ||
            field_representation == MachineRepresentation::kCompressedSigned) {
2421
          value = effect = graph()->NewNode(
2422
              simplified()->CheckSmi(FeedbackSource()), value, effect, control);
2423 2424 2425
          field_access.write_barrier_kind = kNoWriteBarrier;

        } else if (field_representation ==
2426 2427 2428
                       MachineRepresentation::kTaggedPointer ||
                   field_representation ==
                       MachineRepresentation::kCompressedPointer) {
2429 2430 2431 2432 2433 2434 2435
          Handle<Map> field_map;
          if (access_info.field_map().ToHandle(&field_map)) {
            // Emit a map check for the value.
            effect = graph()->NewNode(
                simplified()->CheckMaps(CheckMapsFlag::kNone,
                                        ZoneHandleSet<Map>(field_map)),
                value, effect, control);
2436 2437 2438 2439
          } else {
            // Ensure that {value} is a HeapObject.
            value = effect = graph()->NewNode(simplified()->CheckHeapObject(),
                                              value, effect, control);
2440
          }
2441 2442 2443
          field_access.write_barrier_kind = kPointerWriteBarrier;

        } else {
2444 2445
          DCHECK(field_representation == MachineRepresentation::kTagged ||
                 field_representation == MachineRepresentation::kCompressed);
2446
        }
2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463
        break;
      case MachineRepresentation::kNone:
      case MachineRepresentation::kBit:
      case MachineRepresentation::kWord8:
      case MachineRepresentation::kWord16:
      case MachineRepresentation::kWord32:
      case MachineRepresentation::kWord64:
      case MachineRepresentation::kFloat32:
      case MachineRepresentation::kSimd128:
        UNREACHABLE();
        break;
    }
    // Check if we need to perform a transitioning store.
    Handle<Map> transition_map;
    if (access_info.transition_map().ToHandle(&transition_map)) {
      // Check if we need to grow the properties backing store
      // with this transitioning store.
2464 2465 2466
      MapRef transition_map_ref(broker(), transition_map);
      MapRef original_map = transition_map_ref.GetBackPointer().AsMap();
      if (original_map.UnusedPropertyFields() == 0) {
2467 2468 2469 2470
        DCHECK(!field_index.is_inobject());

        // Reallocate the properties {storage}.
        storage = effect = BuildExtendPropertiesBackingStore(
2471
            original_map, storage, effect, control);
2472 2473

        // Perform the actual store.
2474 2475
        effect = graph()->NewNode(simplified()->StoreField(field_access),
                                  storage, value, effect, control);
2476 2477

        // Atomically switch to the new properties below.
2478
        field_access = AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer();
2479 2480
        value = storage;
        storage = receiver;
2481
      }
2482 2483 2484 2485
      effect = graph()->NewNode(
          common()->BeginRegion(RegionObservability::kObservable), effect);
      effect = graph()->NewNode(
          simplified()->StoreField(AccessBuilder::ForMap()), receiver,
2486
          jsgraph()->Constant(transition_map_ref), effect, control);
2487 2488 2489 2490 2491 2492 2493 2494
      effect = graph()->NewNode(simplified()->StoreField(field_access), storage,
                                value, effect, control);
      effect = graph()->NewNode(common()->FinishRegion(),
                                jsgraph()->UndefinedConstant(), effect);
    } else {
      // Regular non-transitioning field store.
      effect = graph()->NewNode(simplified()->StoreField(field_access), storage,
                                value, effect, control);
2495 2496 2497 2498 2499 2500
    }
  }

  return ValueEffectControl(value, effect, control);
}

2501 2502
Reduction JSNativeContextSpecialization::ReduceJSStoreDataPropertyInLiteral(
    Node* node) {
2503
  DCHECK_EQ(IrOpcode::kJSStoreDataPropertyInLiteral, node->opcode());
2504
  FeedbackParameter const& p = FeedbackParameterOf(node->op());
2505 2506
  Node* const key = NodeProperties::GetValueInput(node, 1);
  Node* const value = NodeProperties::GetValueInput(node, 2);
2507 2508

  if (!p.feedback().IsValid()) return NoChange();
2509 2510 2511
  return ReducePropertyAccess(node, key, base::nullopt, value,
                              FeedbackSource(p.feedback()),
                              AccessMode::kStoreInLiteral);
2512 2513
}

2514 2515 2516 2517 2518 2519 2520 2521
Reduction JSNativeContextSpecialization::ReduceJSStoreInArrayLiteral(
    Node* node) {
  DCHECK_EQ(IrOpcode::kJSStoreInArrayLiteral, node->opcode());
  FeedbackParameter const& p = FeedbackParameterOf(node->op());
  Node* const index = NodeProperties::GetValueInput(node, 1);
  Node* const value = NodeProperties::GetValueInput(node, 2);

  if (!p.feedback().IsValid()) return NoChange();
2522 2523 2524
  return ReducePropertyAccess(node, index, base::nullopt, value,
                              FeedbackSource(p.feedback()),
                              AccessMode::kStoreInLiteral);
2525 2526
}

2527 2528 2529 2530 2531
Reduction JSNativeContextSpecialization::ReduceJSToObject(Node* node) {
  DCHECK_EQ(IrOpcode::kJSToObject, node->opcode());
  Node* receiver = NodeProperties::GetValueInput(node, 0);
  Node* effect = NodeProperties::GetEffectInput(node);

2532 2533 2534
  MapInference inference(broker(), receiver, effect);
  if (!inference.HaveMaps() || !inference.AllOfInstanceTypesAreJSReceiver()) {
    return NoChange();
2535 2536 2537 2538 2539 2540
  }

  ReplaceWithValue(node, receiver, effect);
  return Replace(receiver);
}

2541 2542 2543 2544
namespace {

ExternalArrayType GetArrayTypeFromElementsKind(ElementsKind kind) {
  switch (kind) {
2545 2546
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
  case TYPE##_ELEMENTS:                           \
2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557
    return kExternal##Type##Array;
    TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
    default:
      break;
  }
  UNREACHABLE();
}

}  // namespace

2558 2559 2560
JSNativeContextSpecialization::ValueEffectControl
JSNativeContextSpecialization::BuildElementAccess(
    Node* receiver, Node* index, Node* value, Node* effect, Node* control,
2561
    ElementAccessInfo const& access_info, KeyedAccessMode const& keyed_mode) {
2562 2563 2564
  // TODO(bmeurer): We currently specialize based on elements kind. We should
  // also be able to properly support strings and other JSObjects here.
  ElementsKind elements_kind = access_info.elements_kind();
2565
  ZoneVector<Handle<Map>> const& receiver_maps = access_info.receiver_maps();
2566

2567 2568
  if (IsTypedArrayElementsKind(elements_kind)) {
    Node* buffer_or_receiver = receiver;
2569 2570 2571 2572
    Node* length;
    Node* base_pointer;
    Node* external_pointer;

2573
    // Check if we can constant-fold information about the {receiver} (e.g.
2574
    // for asm.js-like code patterns).
2575
    base::Optional<JSTypedArrayRef> typed_array =
2576
        GetTypedArrayConstant(broker(), receiver);
2577
    if (typed_array.has_value()) {
2578
      length = jsgraph()->Constant(static_cast<double>(typed_array->length()));
2579

2580 2581 2582 2583 2584 2585
      DCHECK(!typed_array->is_on_heap());
      // Load the (known) data pointer for the {receiver} and set {base_pointer}
      // and {external_pointer} to the values that will allow to generate typed
      // element accesses using the known data pointer.
      // The data pointer might be invalid if the {buffer} was detached,
      // so we need to make sure that any access is properly guarded.
2586
      base_pointer = jsgraph()->ZeroConstant();
2587
      external_pointer = jsgraph()->PointerConstant(typed_array->data_ptr());
2588 2589 2590 2591 2592 2593
    } else {
      // Load the {receiver}s length.
      length = effect = graph()->NewNode(
          simplified()->LoadField(AccessBuilder::ForJSTypedArrayLength()),
          receiver, effect, control);

2594 2595 2596 2597 2598
      // Load the base pointer for the {receiver}. This will always be Smi
      // zero unless we allow on-heap TypedArrays, which is only the case
      // for Chrome. Node and Electron both set this limit to 0. Setting
      // the base to Smi zero here allows the EffectControlLinearizer to
      // optimize away the tricky part of the access later.
2599
      if (JSTypedArray::kMaxSizeInHeap == 0) {
2600 2601
        base_pointer = jsgraph()->ZeroConstant();
      } else {
2602 2603 2604 2605
        base_pointer = effect =
            graph()->NewNode(simplified()->LoadField(
                                 AccessBuilder::ForJSTypedArrayBasePointer()),
                             receiver, effect, control);
2606 2607
      }

2608 2609 2610 2611 2612
      // Load the external pointer for the {receiver}.
      external_pointer = effect =
          graph()->NewNode(simplified()->LoadField(
                               AccessBuilder::ForJSTypedArrayExternalPointer()),
                           receiver, effect, control);
2613
    }
2614

2615
    // See if we can skip the detaching check.
2616
    if (!dependencies()->DependOnArrayBufferDetachingProtector()) {
2617 2618 2619 2620 2621 2622 2623 2624 2625
      // Load the buffer for the {receiver}.
      Node* buffer =
          typed_array.has_value()
              ? jsgraph()->Constant(typed_array->buffer())
              : (effect = graph()->NewNode(
                     simplified()->LoadField(
                         AccessBuilder::ForJSArrayBufferViewBuffer()),
                     receiver, effect, control));

2626 2627
      // Deopt if the {buffer} was detached.
      // Note: A detached buffer leads to megamorphic feedback.
2628 2629 2630 2631 2632 2633 2634
      Node* buffer_bit_field = effect = graph()->NewNode(
          simplified()->LoadField(AccessBuilder::ForJSArrayBufferBitField()),
          buffer, effect, control);
      Node* check = graph()->NewNode(
          simplified()->NumberEqual(),
          graph()->NewNode(
              simplified()->NumberBitwiseAnd(), buffer_bit_field,
2635
              jsgraph()->Constant(JSArrayBuffer::WasDetachedBit::kMask)),
2636 2637
          jsgraph()->ZeroConstant());
      effect = graph()->NewNode(
2638
          simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasDetached),
2639
          check, effect, control);
2640 2641 2642

      // Retain the {buffer} instead of {receiver} to reduce live ranges.
      buffer_or_receiver = buffer;
2643
    }
2644

2645 2646 2647 2648
    if ((keyed_mode.IsLoad() &&
         keyed_mode.load_mode() == LOAD_IGNORE_OUT_OF_BOUNDS) ||
        (keyed_mode.IsStore() &&
         keyed_mode.store_mode() == STORE_IGNORE_OUT_OF_BOUNDS)) {
2649
      // Only check that the {index} is in SignedSmall range. We do the actual
2650
      // bounds check below and just skip the property access if it's out of
2651
      // bounds for the {receiver}.
2652
      index = effect = graph()->NewNode(
2653
          simplified()->CheckSmi(FeedbackSource()), index, effect, control);
2654 2655 2656 2657 2658

      // Cast the {index} to Unsigned32 range, so that the bounds checks
      // below are performed on unsigned values, which means that all the
      // Negative32 values are treated as out-of-bounds.
      index = graph()->NewNode(simplified()->NumberToUint32(), index);
2659
    } else {
2660
      // Check that the {index} is in the valid range for the {receiver}.
2661
      index = effect =
2662
          graph()->NewNode(simplified()->CheckBounds(FeedbackSource()), index,
2663
                           length, effect, control);
2664
    }
2665 2666 2667 2668

    // Access the actual element.
    ExternalArrayType external_array_type =
        GetArrayTypeFromElementsKind(elements_kind);
2669
    switch (keyed_mode.access_mode()) {
2670
      case AccessMode::kLoad: {
2671
        // Check if we can return undefined for out-of-bounds loads.
2672
        if (keyed_mode.load_mode() == LOAD_IGNORE_OUT_OF_BOUNDS) {
2673 2674
          Node* check =
              graph()->NewNode(simplified()->NumberLessThan(), index, length);
2675 2676 2677 2678
          Node* branch = graph()->NewNode(
              common()->Branch(BranchHint::kTrue,
                               IsSafetyCheck::kCriticalSafetyCheck),
              check, control);
2679 2680 2681 2682 2683 2684 2685

          Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
          Node* etrue = effect;
          Node* vtrue;
          {
            // Perform the actual load
            vtrue = etrue = graph()->NewNode(
2686 2687 2688
                simplified()->LoadTypedElement(external_array_type),
                buffer_or_receiver, base_pointer, external_pointer, index,
                etrue, if_true);
2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707
          }

          Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
          Node* efalse = effect;
          Node* vfalse;
          {
            // Materialize undefined for out-of-bounds loads.
            vfalse = jsgraph()->UndefinedConstant();
          }

          control = graph()->NewNode(common()->Merge(2), if_true, if_false);
          effect =
              graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
          value =
              graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                               vtrue, vfalse, control);
        } else {
          // Perform the actual load.
          value = effect = graph()->NewNode(
2708 2709 2710
              simplified()->LoadTypedElement(external_array_type),
              buffer_or_receiver, base_pointer, external_pointer, index, effect,
              control);
2711
        }
2712
        break;
2713
      }
2714 2715 2716
      case AccessMode::kStoreInLiteral:
        UNREACHABLE();
        break;
2717
      case AccessMode::kStore: {
2718 2719
        // Ensure that the {value} is actually a Number or an Oddball,
        // and truncate it to a Number appropriately.
2720 2721
        value = effect = graph()->NewNode(
            simplified()->SpeculativeToNumber(
2722
                NumberOperationHint::kNumberOrOddball, FeedbackSource()),
2723
            value, effect, control);
2724

2725 2726 2727 2728 2729 2730 2731 2732
        // Introduce the appropriate truncation for {value}. Currently we
        // only need to do this for ClamedUint8Array {receiver}s, as the
        // other truncations are implicit in the StoreTypedElement, but we
        // might want to change that at some point.
        if (external_array_type == kExternalUint8ClampedArray) {
          value = graph()->NewNode(simplified()->NumberToUint8Clamped(), value);
        }

2733
        // Check if we can skip the out-of-bounds store.
2734
        if (keyed_mode.store_mode() == STORE_IGNORE_OUT_OF_BOUNDS) {
2735 2736 2737 2738 2739 2740 2741 2742 2743 2744
          Node* check =
              graph()->NewNode(simplified()->NumberLessThan(), index, length);
          Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
                                          check, control);

          Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
          Node* etrue = effect;
          {
            // Perform the actual store.
            etrue = graph()->NewNode(
2745 2746 2747
                simplified()->StoreTypedElement(external_array_type),
                buffer_or_receiver, base_pointer, external_pointer, index,
                value, etrue, if_true);
2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761
          }

          Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
          Node* efalse = effect;
          {
            // Just ignore the out-of-bounds write.
          }

          control = graph()->NewNode(common()->Merge(2), if_true, if_false);
          effect =
              graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
        } else {
          // Perform the actual store
          effect = graph()->NewNode(
2762 2763 2764
              simplified()->StoreTypedElement(external_array_type),
              buffer_or_receiver, base_pointer, external_pointer, index, value,
              effect, control);
2765
        }
2766
        break;
2767
      }
2768 2769 2770 2771 2772 2773 2774
      case AccessMode::kHas:
        // For has property on a typed array, all we need is a bounds check.
        value = effect =
            graph()->NewNode(simplified()->SpeculativeNumberLessThan(
                                 NumberOperationHint::kSignedSmall),
                             index, length, effect, control);
        break;
2775 2776
    }
  } else {
2777 2778 2779 2780 2781
    // Load the elements for the {receiver}.
    Node* elements = effect = graph()->NewNode(
        simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
        effect, control);

2782 2783
    // Don't try to store to a copy-on-write backing store (unless supported by
    // the store mode).
2784
    if (keyed_mode.access_mode() == AccessMode::kStore &&
2785
        IsSmiOrObjectElementsKind(elements_kind) &&
2786
        !IsCOWHandlingStoreMode(keyed_mode.store_mode())) {
2787 2788 2789 2790 2791
      effect = graph()->NewNode(
          simplified()->CheckMaps(
              CheckMapsFlag::kNone,
              ZoneHandleSet<Map>(factory()->fixed_array_map())),
          elements, effect, control);
2792 2793
    }

2794
    // Check if the {receiver} is a JSArray.
2795
    bool receiver_is_jsarray = HasOnlyJSArrayMaps(broker(), receiver_maps);
2796

2797 2798
    // Load the length of the {receiver}.
    Node* length = effect =
2799
        receiver_is_jsarray
2800 2801 2802 2803 2804 2805 2806 2807
            ? graph()->NewNode(
                  simplified()->LoadField(
                      AccessBuilder::ForJSArrayLength(elements_kind)),
                  receiver, effect, control)
            : graph()->NewNode(
                  simplified()->LoadField(AccessBuilder::ForFixedArrayLength()),
                  elements, effect, control);

2808
    // Check if we might need to grow the {elements} backing store.
2809
    if (keyed_mode.IsStore() && IsGrowStoreMode(keyed_mode.store_mode())) {
2810
      // For growing stores we validate the {index} below.
2811 2812
    } else if (keyed_mode.IsLoad() &&
               keyed_mode.load_mode() == LOAD_IGNORE_OUT_OF_BOUNDS &&
2813
               CanTreatHoleAsUndefined(receiver_maps)) {
2814 2815 2816
      // Check that the {index} is a valid array index, we do the actual
      // bounds check below and just skip the store below if it's out of
      // bounds for the {receiver}.
2817
      index = effect = graph()->NewNode(
2818
          simplified()->CheckBounds(FeedbackSource()), index,
2819
          jsgraph()->Constant(Smi::kMaxValue), effect, control);
2820
    } else {
2821
      // Check that the {index} is in the valid range for the {receiver}.
2822
      index = effect =
2823
          graph()->NewNode(simplified()->CheckBounds(FeedbackSource()), index,
2824
                           length, effect, control);
2825
    }
2826 2827

    // Compute the element access.
2828
    Type element_type = Type::NonInternal();
2829
    MachineType element_machine_type = MachineType::TypeCompressedTagged();
2830
    if (IsDoubleElementsKind(elements_kind)) {
2831 2832
      element_type = Type::Number();
      element_machine_type = MachineType::Float64();
2833
    } else if (IsSmiElementsKind(elements_kind)) {
2834
      element_type = Type::SignedSmall();
2835
      element_machine_type = MachineType::TypeCompressedTaggedSigned();
2836
    }
2837 2838 2839 2840
    ElementAccess element_access = {
        kTaggedBase,       FixedArray::kHeaderSize,
        element_type,      element_machine_type,
        kFullWriteBarrier, LoadSensitivity::kCritical};
2841 2842

    // Access the actual element.
2843
    if (keyed_mode.access_mode() == AccessMode::kLoad) {
2844 2845
      // Compute the real element access type, which includes the hole in case
      // of holey backing stores.
2846
      if (IsHoleyElementsKind(elements_kind)) {
2847 2848
        element_access.type =
            Type::Union(element_type, Type::Hole(), graph()->zone());
2849
      }
2850 2851
      if (elements_kind == HOLEY_ELEMENTS ||
          elements_kind == HOLEY_SMI_ELEMENTS) {
2852
        element_access.machine_type = MachineType::TypeCompressedTagged();
2853
      }
2854 2855

      // Check if we can return undefined for out-of-bounds loads.
2856
      if (keyed_mode.load_mode() == LOAD_IGNORE_OUT_OF_BOUNDS &&
2857 2858 2859
          CanTreatHoleAsUndefined(receiver_maps)) {
        Node* check =
            graph()->NewNode(simplified()->NumberLessThan(), index, length);
2860 2861 2862 2863
        Node* branch = graph()->NewNode(
            common()->Branch(BranchHint::kTrue,
                             IsSafetyCheck::kCriticalSafetyCheck),
            check, control);
2864 2865 2866 2867 2868 2869 2870 2871

        Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
        Node* etrue = effect;
        Node* vtrue;
        {
          // Perform the actual load
          vtrue = etrue =
              graph()->NewNode(simplified()->LoadElement(element_access),
2872
                               elements, index, etrue, if_true);
2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884

          // Handle loading from holey backing stores correctly, by either
          // mapping the hole to undefined if possible, or deoptimizing
          // otherwise.
          if (elements_kind == HOLEY_ELEMENTS ||
              elements_kind == HOLEY_SMI_ELEMENTS) {
            // Turn the hole into undefined.
            vtrue = graph()->NewNode(
                simplified()->ConvertTaggedHoleToUndefined(), vtrue);
          } else if (elements_kind == HOLEY_DOUBLE_ELEMENTS) {
            // Return the signaling NaN hole directly if all uses are
            // truncating.
2885 2886
            vtrue = etrue = graph()->NewNode(
                simplified()->CheckFloat64Hole(
2887
                    CheckFloat64HoleMode::kAllowReturnHole, FeedbackSource()),
2888
                vtrue, etrue, if_true);
2889 2890 2891 2892 2893 2894 2895 2896 2897
          }
        }

        Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
        Node* efalse = effect;
        Node* vfalse;
        {
          // Materialize undefined for out-of-bounds loads.
          vfalse = jsgraph()->UndefinedConstant();
2898
        }
2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909

        control = graph()->NewNode(common()->Merge(2), if_true, if_false);
        effect =
            graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
        value =
            graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                             vtrue, vfalse, control);
      } else {
        // Perform the actual load.
        value = effect =
            graph()->NewNode(simplified()->LoadElement(element_access),
2910
                             elements, index, effect, control);
2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935

        // Handle loading from holey backing stores correctly, by either mapping
        // the hole to undefined if possible, or deoptimizing otherwise.
        if (elements_kind == HOLEY_ELEMENTS ||
            elements_kind == HOLEY_SMI_ELEMENTS) {
          // Check if we are allowed to turn the hole into undefined.
          if (CanTreatHoleAsUndefined(receiver_maps)) {
            // Turn the hole into undefined.
            value = graph()->NewNode(
                simplified()->ConvertTaggedHoleToUndefined(), value);
          } else {
            // Bailout if we see the hole.
            value = effect = graph()->NewNode(
                simplified()->CheckNotTaggedHole(), value, effect, control);
          }
        } else if (elements_kind == HOLEY_DOUBLE_ELEMENTS) {
          // Perform the hole check on the result.
          CheckFloat64HoleMode mode = CheckFloat64HoleMode::kNeverReturnHole;
          // Check if we are allowed to return the hole directly.
          if (CanTreatHoleAsUndefined(receiver_maps)) {
            // Return the signaling NaN hole directly if all uses are
            // truncating.
            mode = CheckFloat64HoleMode::kAllowReturnHole;
          }
          value = effect = graph()->NewNode(
2936
              simplified()->CheckFloat64Hole(mode, FeedbackSource()), value,
2937
              effect, control);
2938 2939
        }
      }
2940
    } else if (keyed_mode.access_mode() == AccessMode::kHas) {
2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959
      // For packed arrays with NoElementsProctector valid, a bound check
      // is equivalent to HasProperty.
      value = effect = graph()->NewNode(simplified()->SpeculativeNumberLessThan(
                                            NumberOperationHint::kSignedSmall),
                                        index, length, effect, control);
      if (IsHoleyElementsKind(elements_kind)) {
        // If the index is in bounds, do a load and hole check.

        Node* branch = graph()->NewNode(common()->Branch(), value, control);

        Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
        Node* efalse = effect;
        Node* vfalse = jsgraph()->FalseConstant();

        element_access.type =
            Type::Union(element_type, Type::Hole(), graph()->zone());

        if (elements_kind == HOLEY_ELEMENTS ||
            elements_kind == HOLEY_SMI_ELEMENTS) {
2960
          element_access.machine_type = MachineType::TypeCompressedTagged();
2961 2962 2963 2964 2965 2966
        }

        Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
        Node* etrue = effect;

        Node* checked = etrue =
2967
            graph()->NewNode(simplified()->CheckBounds(FeedbackSource()), index,
2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997
                             length, etrue, if_true);

        Node* element = etrue =
            graph()->NewNode(simplified()->LoadElement(element_access),
                             elements, checked, etrue, if_true);

        Node* vtrue;
        if (CanTreatHoleAsUndefined(receiver_maps)) {
          if (elements_kind == HOLEY_ELEMENTS ||
              elements_kind == HOLEY_SMI_ELEMENTS) {
            // Check if we are allowed to turn the hole into undefined.
            // Turn the hole into undefined.
            vtrue = graph()->NewNode(simplified()->ReferenceEqual(), element,
                                     jsgraph()->TheHoleConstant());
          } else {
            vtrue =
                graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
          }

          // has == !IsHole
          vtrue = graph()->NewNode(simplified()->BooleanNot(), vtrue);
        } else {
          if (elements_kind == HOLEY_ELEMENTS ||
              elements_kind == HOLEY_SMI_ELEMENTS) {
            // Bailout if we see the hole.
            etrue = graph()->NewNode(simplified()->CheckNotTaggedHole(),
                                     element, etrue, if_true);
          } else {
            etrue = graph()->NewNode(
                simplified()->CheckFloat64Hole(
2998
                    CheckFloat64HoleMode::kNeverReturnHole, FeedbackSource()),
2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011
                element, etrue, if_true);
          }

          vtrue = jsgraph()->TrueConstant();
        }

        control = graph()->NewNode(common()->Merge(2), if_true, if_false);
        effect =
            graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
        value =
            graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                             vtrue, vfalse, control);
      }
3012
    } else {
3013 3014 3015
      DCHECK(keyed_mode.access_mode() == AccessMode::kStore ||
             keyed_mode.access_mode() == AccessMode::kStoreInLiteral);

3016
      if (IsSmiElementsKind(elements_kind)) {
3017
        value = effect = graph()->NewNode(
3018
            simplified()->CheckSmi(FeedbackSource()), value, effect, control);
3019
      } else if (IsDoubleElementsKind(elements_kind)) {
3020
        value = effect =
3021
            graph()->NewNode(simplified()->CheckNumber(FeedbackSource()), value,
3022
                             effect, control);
3023 3024 3025
        // Make sure we do not store signalling NaNs into double arrays.
        value = graph()->NewNode(simplified()->NumberSilenceNaN(), value);
      }
3026 3027

      // Ensure that copy-on-write backing store is writable.
3028
      if (IsSmiOrObjectElementsKind(elements_kind) &&
3029
          keyed_mode.store_mode() == STORE_HANDLE_COW) {
3030 3031 3032
        elements = effect =
            graph()->NewNode(simplified()->EnsureWritableFastElements(),
                             receiver, elements, effect, control);
3033
      } else if (IsGrowStoreMode(keyed_mode.store_mode())) {
3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054
        // Determine the length of the {elements} backing store.
        Node* elements_length = effect = graph()->NewNode(
            simplified()->LoadField(AccessBuilder::ForFixedArrayLength()),
            elements, effect, control);

        // Validate the {index} depending on holeyness:
        //
        // For HOLEY_*_ELEMENTS the {index} must not exceed the {elements}
        // backing store capacity plus the maximum allowed gap, as otherwise
        // the (potential) backing store growth would normalize and thus
        // the elements kind of the {receiver} would change to slow mode.
        //
        // For PACKED_*_ELEMENTS the {index} must be within the range
        // [0,length+1[ to be valid. In case {index} equals {length},
        // the {receiver} will be extended, but kept packed.
        Node* limit =
            IsHoleyElementsKind(elements_kind)
                ? graph()->NewNode(simplified()->NumberAdd(), elements_length,
                                   jsgraph()->Constant(JSObject::kMaxGap))
                : graph()->NewNode(simplified()->NumberAdd(), length,
                                   jsgraph()->OneConstant());
3055
        index = effect =
3056
            graph()->NewNode(simplified()->CheckBounds(FeedbackSource()), index,
3057
                             limit, effect, control);
3058 3059 3060 3061 3062 3063 3064

        // Grow {elements} backing store if necessary.
        GrowFastElementsMode mode =
            IsDoubleElementsKind(elements_kind)
                ? GrowFastElementsMode::kDoubleElements
                : GrowFastElementsMode::kSmiOrObjectElements;
        elements = effect = graph()->NewNode(
3065
            simplified()->MaybeGrowFastElements(mode, FeedbackSource()),
3066
            receiver, elements, index, elements_length, effect, control);
3067

3068 3069 3070
        // If we didn't grow {elements}, it might still be COW, in which case we
        // copy it now.
        if (IsSmiOrObjectElementsKind(elements_kind) &&
3071
            keyed_mode.store_mode() == STORE_AND_GROW_HANDLE_COW) {
3072 3073 3074 3075 3076
          elements = effect =
              graph()->NewNode(simplified()->EnsureWritableFastElements(),
                               receiver, elements, effect, control);
        }

3077
        // Also update the "length" property if {receiver} is a JSArray.
3078
        if (receiver_is_jsarray) {
3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105
          Node* check =
              graph()->NewNode(simplified()->NumberLessThan(), index, length);
          Node* branch = graph()->NewNode(common()->Branch(), check, control);

          Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
          Node* etrue = effect;
          {
            // We don't need to do anything, the {index} is within
            // the valid bounds for the JSArray {receiver}.
          }

          Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
          Node* efalse = effect;
          {
            // Update the JSArray::length field. Since this is observable,
            // there must be no other check after this.
            Node* new_length = graph()->NewNode(
                simplified()->NumberAdd(), index, jsgraph()->OneConstant());
            efalse = graph()->NewNode(
                simplified()->StoreField(
                    AccessBuilder::ForJSArrayLength(elements_kind)),
                receiver, new_length, efalse, if_false);
          }

          control = graph()->NewNode(common()->Merge(2), if_true, if_false);
          effect =
              graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
3106
        }
3107 3108 3109
      }

      // Perform the actual element access.
3110 3111
      effect = graph()->NewNode(simplified()->StoreElement(element_access),
                                elements, index, value, effect, control);
3112 3113 3114 3115 3116
    }
  }

  return ValueEffectControl(value, effect, control);
}
3117

3118 3119 3120 3121
Node* JSNativeContextSpecialization::BuildIndexedStringLoad(
    Node* receiver, Node* index, Node* length, Node** effect, Node** control,
    KeyedAccessLoadMode load_mode) {
  if (load_mode == LOAD_IGNORE_OUT_OF_BOUNDS &&
3122
      dependencies()->DependOnNoElementsProtector()) {
3123
    // Ensure that the {index} is a valid String length.
3124
    index = *effect = graph()->NewNode(
3125
        simplified()->CheckBounds(FeedbackSource()), index,
3126
        jsgraph()->Constant(String::kMaxLength), *effect, *control);
3127 3128 3129 3130 3131 3132

    // Load the single character string from {receiver} or yield
    // undefined if the {index} is not within the valid bounds.
    Node* check =
        graph()->NewNode(simplified()->NumberLessThan(), index, length);
    Node* branch =
3133 3134 3135
        graph()->NewNode(common()->Branch(BranchHint::kTrue,
                                          IsSafetyCheck::kCriticalSafetyCheck),
                         check, *control);
3136

3137
    Node* masked_index = graph()->NewNode(simplified()->PoisonIndex(), index);
3138

3139
    Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
3140
    Node* etrue;
3141 3142 3143
    Node* vtrue = etrue =
        graph()->NewNode(simplified()->StringCharCodeAt(), receiver,
                         masked_index, *effect, if_true);
3144
    vtrue = graph()->NewNode(simplified()->StringFromSingleCharCode(), vtrue);
3145 3146 3147 3148 3149

    Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
    Node* vfalse = jsgraph()->UndefinedConstant();

    *control = graph()->NewNode(common()->Merge(2), if_true, if_false);
3150 3151
    *effect =
        graph()->NewNode(common()->EffectPhi(2), etrue, *effect, *control);
3152 3153 3154 3155
    return graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                            vtrue, vfalse, *control);
  } else {
    // Ensure that {index} is less than {receiver} length.
3156
    index = *effect =
3157
        graph()->NewNode(simplified()->CheckBounds(FeedbackSource()), index,
3158
                         length, *effect, *control);
3159

3160
    Node* masked_index = graph()->NewNode(simplified()->PoisonIndex(), index);
3161

3162
    // Return the character from the {receiver} as single character string.
3163
    Node* value = *effect =
3164 3165
        graph()->NewNode(simplified()->StringCharCodeAt(), receiver,
                         masked_index, *effect, *control);
3166
    value = graph()->NewNode(simplified()->StringFromSingleCharCode(), value);
3167
    return value;
3168 3169 3170
  }
}

3171
Node* JSNativeContextSpecialization::BuildExtendPropertiesBackingStore(
3172
    const MapRef& map, Node* properties, Node* effect, Node* control) {
3173 3174 3175 3176 3177 3178 3179 3180 3181
  // TODO(bmeurer/jkummerow): Property deletions can undo map transitions
  // while keeping the backing store around, meaning that even though the
  // map might believe that objects have no unused property fields, there
  // might actually be some. It would be nice to not create a new backing
  // store in that case (i.e. when properties->length() >= new_length).
  // However, introducing branches and Phi nodes here would make it more
  // difficult for escape analysis to get rid of the backing stores used
  // for intermediate states of chains of property additions. That makes
  // it unclear what the best approach is here.
3182
  DCHECK_EQ(0, map.UnusedPropertyFields());
3183
  // Compute the length of the old {properties} and the new properties.
3184
  int length = map.NextFreePropertyIndex() - map.GetInObjectProperties();
3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198
  int new_length = length + JSObject::kFieldsAdded;
  // Collect the field values from the {properties}.
  ZoneVector<Node*> values(zone());
  values.reserve(new_length);
  for (int i = 0; i < length; ++i) {
    Node* value = effect = graph()->NewNode(
        simplified()->LoadField(AccessBuilder::ForFixedArraySlot(i)),
        properties, effect, control);
    values.push_back(value);
  }
  // Initialize the new fields to undefined.
  for (int i = 0; i < JSObject::kFieldsAdded; ++i) {
    values.push_back(jsgraph()->UndefinedConstant());
  }
3199

3200
  // Compute new length and hash.
3201 3202 3203 3204 3205 3206
  Node* hash;
  if (length == 0) {
    hash = graph()->NewNode(
        common()->Select(MachineRepresentation::kTaggedSigned),
        graph()->NewNode(simplified()->ObjectIsSmi(), properties), properties,
        jsgraph()->SmiConstant(PropertyArray::kNoHashSentinel));
3207 3208
    hash = effect = graph()->NewNode(common()->TypeGuard(Type::SignedSmall()),
                                     hash, effect, control);
3209 3210 3211
    hash =
        graph()->NewNode(simplified()->NumberShiftLeft(), hash,
                         jsgraph()->Constant(PropertyArray::HashField::kShift));
3212 3213
  } else {
    hash = effect = graph()->NewNode(
3214
        simplified()->LoadField(AccessBuilder::ForPropertyArrayLengthAndHash()),
3215
        properties, effect, control);
3216 3217 3218
    hash =
        graph()->NewNode(simplified()->NumberBitwiseAnd(), hash,
                         jsgraph()->Constant(PropertyArray::HashField::kMask));
3219 3220 3221
  }
  Node* new_length_and_hash = graph()->NewNode(
      simplified()->NumberBitwiseOr(), jsgraph()->Constant(new_length), hash);
3222 3223 3224 3225
  // TDOO(jarin): Fix the typer to infer tighter bound for NumberBitwiseOr.
  new_length_and_hash = effect =
      graph()->NewNode(common()->TypeGuard(Type::SignedSmall()),
                       new_length_and_hash, effect, control);
3226 3227

  // Allocate and initialize the new properties.
3228
  AllocationBuilder a(jsgraph(), effect, control);
3229
  a.Allocate(PropertyArray::SizeFor(new_length), AllocationType::kYoung,
3230 3231 3232
             Type::OtherInternal());
  a.Store(AccessBuilder::ForMap(), jsgraph()->PropertyArrayMapConstant());
  a.Store(AccessBuilder::ForPropertyArrayLengthAndHash(), new_length_and_hash);
3233
  for (int i = 0; i < new_length; ++i) {
3234
    a.Store(AccessBuilder::ForFixedArraySlot(i), values[i]);
3235
  }
3236
  return a.Finish();
3237 3238
}

3239
Node* JSNativeContextSpecialization::BuildCheckEqualsName(NameRef const& name,
3240 3241 3242
                                                          Node* value,
                                                          Node* effect,
                                                          Node* control) {
3243
  DCHECK(name.IsUniqueName());
3244
  Operator const* const op =
3245 3246 3247
      name.IsSymbol() ? simplified()->CheckEqualsSymbol()
                      : simplified()->CheckEqualsInternalizedString();
  return graph()->NewNode(op, jsgraph()->Constant(name), value, effect,
3248 3249 3250
                          control);
}

3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266
Node* JSNativeContextSpecialization::AppendExceptionHandling(
    Node* effect, Node* control, Node* merge, Node* phi, Node* effect_phi) {
  DCHECK_EQ(effect, control);
  int input_count = merge->InputCount() + 1;
  Node* if_exception =
      graph()->NewNode(common()->IfException(), effect, control);
  merge->InsertInput(graph()->zone(), 0, if_exception);
  NodeProperties::ChangeOp(merge, common()->Merge(input_count));
  phi->InsertInput(graph()->zone(), 0, if_exception);
  NodeProperties::ChangeOp(
      phi, common()->Phi(MachineRepresentation::kTagged, input_count));
  effect_phi->InsertInput(graph()->zone(), 0, if_exception);
  NodeProperties::ChangeOp(effect_phi, common()->EffectPhi(input_count));
  return graph()->NewNode(common()->IfSuccess(), control);
}

3267
bool JSNativeContextSpecialization::CanTreatHoleAsUndefined(
3268
    ZoneVector<Handle<Map>> const& receiver_maps) {
3269
  // Check if all {receiver_maps} have one of the initial Array.prototype
3270 3271
  // or Object.prototype objects as their prototype (in any of the current
  // native contexts, as the global Array protector works isolate-wide).
3272
  for (Handle<Map> map : receiver_maps) {
3273
    MapRef receiver_map(broker(), map);
3274 3275
    ObjectRef receiver_prototype = receiver_map.prototype();
    if (!receiver_prototype.IsJSObject() ||
3276
        !broker()->IsArrayOrObjectPrototype(receiver_prototype.AsJSObject())) {
3277 3278 3279 3280
      return false;
    }
  }

3281
  // Check if the array prototype chain is intact.
3282
  return dependencies()->DependOnNoElementsProtector();
3283 3284
}

3285
bool JSNativeContextSpecialization::InferReceiverMaps(
3286 3287
    Node* receiver, Node* effect,
    ZoneVector<Handle<Map>>* receiver_maps) const {
3288
  ZoneHandleSet<Map> maps;
3289
  NodeProperties::InferReceiverMapsResult result =
3290 3291
      NodeProperties::InferReceiverMapsUnsafe(broker(), receiver, effect,
                                              &maps);
3292 3293
  if (result == NodeProperties::kReliableReceiverMaps) {
    for (size_t i = 0; i < maps.size(); ++i) {
3294
      receiver_maps->push_back(maps[i]);
3295 3296 3297 3298 3299 3300
    }
    return true;
  } else if (result == NodeProperties::kUnreliableReceiverMaps) {
    // For untrusted receiver maps, we can still use the information
    // if the maps are stable.
    for (size_t i = 0; i < maps.size(); ++i) {
3301 3302
      MapRef map(broker(), maps[i]);
      if (!map.is_stable()) return false;
3303
    }
3304
    for (size_t i = 0; i < maps.size(); ++i) {
3305
      receiver_maps->push_back(maps[i]);
3306
    }
3307
    return true;
3308
  }
3309
  return false;
3310 3311
}

3312
base::Optional<MapRef> JSNativeContextSpecialization::InferReceiverRootMap(
3313
    Node* receiver) const {
3314 3315
  HeapObjectMatcher m(receiver);
  if (m.HasValue()) {
3316 3317
    MapRef map = m.Ref(broker()).map();
    return map.FindRootMap();
3318
  } else if (m.IsJSCreate()) {
3319 3320 3321
    base::Optional<MapRef> initial_map =
        NodeProperties::GetJSCreateMap(broker(), receiver);
    if (initial_map.has_value()) {
3322 3323 3324 3325 3326
      if (!initial_map->FindRootMap().has_value()) {
        return base::nullopt;
      }
      DCHECK(initial_map->equals(*initial_map->FindRootMap()));
      return *initial_map;
3327 3328
    }
  }
3329
  return base::nullopt;
3330
}
3331

3332 3333 3334
Graph* JSNativeContextSpecialization::graph() const {
  return jsgraph()->graph();
}
3335

3336
Isolate* JSNativeContextSpecialization::isolate() const {
3337 3338 3339
  return jsgraph()->isolate();
}

3340 3341 3342 3343
Factory* JSNativeContextSpecialization::factory() const {
  return isolate()->factory();
}

3344
CommonOperatorBuilder* JSNativeContextSpecialization::common() const {
3345 3346 3347
  return jsgraph()->common();
}

3348
JSOperatorBuilder* JSNativeContextSpecialization::javascript() const {
3349 3350 3351
  return jsgraph()->javascript();
}

3352
SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const {
3353 3354 3355
  return jsgraph()->simplified();
}

3356 3357 3358
}  // namespace compiler
}  // namespace internal
}  // namespace v8