Bug Summary

File:out/../deps/v8/src/compiler/js-native-context-specialization.cc
Warning:line 1379, column 53
Called C++ object pointer is null

Annotated Source Code

Press '?' to see keyboard shortcuts

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