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