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