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