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