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