| File: | out/../deps/v8/src/compiler/js-native-context-specialization.cc |
| Warning: | line 1379, column 53 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | // Copyright 2015 the V8 project authors. All rights reserved. | |||
| 2 | // Use of this source code is governed by a BSD-style license that can be | |||
| 3 | // found in the LICENSE file. | |||
| 4 | ||||
| 5 | #include "src/compiler/js-native-context-specialization.h" | |||
| 6 | ||||
| 7 | #include "src/api/api-inl.h" | |||
| 8 | #include "src/base/optional.h" | |||
| 9 | #include "src/builtins/accessors.h" | |||
| 10 | #include "src/codegen/code-factory.h" | |||
| 11 | #include "src/codegen/string-constants.h" | |||
| 12 | #include "src/compiler/access-builder.h" | |||
| 13 | #include "src/compiler/access-info.h" | |||
| 14 | #include "src/compiler/allocation-builder-inl.h" | |||
| 15 | #include "src/compiler/allocation-builder.h" | |||
| 16 | #include "src/compiler/compilation-dependencies.h" | |||
| 17 | #include "src/compiler/js-graph.h" | |||
| 18 | #include "src/compiler/js-operator.h" | |||
| 19 | #include "src/compiler/linkage.h" | |||
| 20 | #include "src/compiler/map-inference.h" | |||
| 21 | #include "src/compiler/node-matchers.h" | |||
| 22 | #include "src/compiler/property-access-builder.h" | |||
| 23 | #include "src/compiler/type-cache.h" | |||
| 24 | #include "src/execution/isolate-inl.h" | |||
| 25 | #include "src/objects/feedback-vector.h" | |||
| 26 | #include "src/objects/field-index-inl.h" | |||
| 27 | #include "src/objects/heap-number.h" | |||
| 28 | #include "src/objects/js-array-buffer-inl.h" | |||
| 29 | #include "src/objects/js-array-inl.h" | |||
| 30 | #include "src/objects/templates.h" | |||
| 31 | ||||
| 32 | namespace v8 { | |||
| 33 | namespace internal { | |||
| 34 | namespace compiler { | |||
| 35 | ||||
| 36 | namespace { | |||
| 37 | ||||
| 38 | bool HasNumberMaps(JSHeapBroker* broker, ZoneVector<MapRef> const& maps) { | |||
| 39 | for (MapRef map : maps) { | |||
| 40 | if (map.IsHeapNumberMap()) return true; | |||
| 41 | } | |||
| 42 | return false; | |||
| 43 | } | |||
| 44 | ||||
| 45 | bool HasOnlyJSArrayMaps(JSHeapBroker* broker, ZoneVector<MapRef> const& maps) { | |||
| 46 | for (MapRef map : maps) { | |||
| 47 | if (!map.IsJSArrayMap()) return false; | |||
| 48 | } | |||
| 49 | return true; | |||
| 50 | } | |||
| 51 | ||||
| 52 | } // namespace | |||
| 53 | ||||
| 54 | JSNativeContextSpecialization::JSNativeContextSpecialization( | |||
| 55 | Editor* editor, JSGraph* jsgraph, JSHeapBroker* broker, Flags flags, | |||
| 56 | CompilationDependencies* dependencies, Zone* zone, Zone* shared_zone) | |||
| 57 | : AdvancedReducer(editor), | |||
| 58 | jsgraph_(jsgraph), | |||
| 59 | broker_(broker), | |||
| 60 | flags_(flags), | |||
| 61 | global_object_(broker->target_native_context().global_object().object()), | |||
| 62 | global_proxy_( | |||
| 63 | broker->target_native_context().global_proxy_object().object()), | |||
| 64 | dependencies_(dependencies), | |||
| 65 | zone_(zone), | |||
| 66 | shared_zone_(shared_zone), | |||
| 67 | type_cache_(TypeCache::Get()) {} | |||
| 68 | ||||
| 69 | Reduction JSNativeContextSpecialization::Reduce(Node* node) { | |||
| 70 | switch (node->opcode()) { | |||
| ||||
| 71 | case IrOpcode::kJSAdd: | |||
| 72 | return ReduceJSAdd(node); | |||
| 73 | case IrOpcode::kJSAsyncFunctionEnter: | |||
| 74 | return ReduceJSAsyncFunctionEnter(node); | |||
| 75 | case IrOpcode::kJSAsyncFunctionReject: | |||
| 76 | return ReduceJSAsyncFunctionReject(node); | |||
| 77 | case IrOpcode::kJSAsyncFunctionResolve: | |||
| 78 | return ReduceJSAsyncFunctionResolve(node); | |||
| 79 | case IrOpcode::kJSGetSuperConstructor: | |||
| 80 | return ReduceJSGetSuperConstructor(node); | |||
| 81 | case IrOpcode::kJSInstanceOf: | |||
| 82 | return ReduceJSInstanceOf(node); | |||
| 83 | case IrOpcode::kJSHasInPrototypeChain: | |||
| 84 | return ReduceJSHasInPrototypeChain(node); | |||
| 85 | case IrOpcode::kJSOrdinaryHasInstance: | |||
| 86 | return ReduceJSOrdinaryHasInstance(node); | |||
| 87 | case IrOpcode::kJSPromiseResolve: | |||
| 88 | return ReduceJSPromiseResolve(node); | |||
| 89 | case IrOpcode::kJSResolvePromise: | |||
| 90 | return ReduceJSResolvePromise(node); | |||
| 91 | case IrOpcode::kJSLoadGlobal: | |||
| 92 | return ReduceJSLoadGlobal(node); | |||
| 93 | case IrOpcode::kJSStoreGlobal: | |||
| 94 | return ReduceJSStoreGlobal(node); | |||
| 95 | case IrOpcode::kJSLoadNamed: | |||
| 96 | return ReduceJSLoadNamed(node); | |||
| 97 | case IrOpcode::kJSLoadNamedFromSuper: | |||
| 98 | return ReduceJSLoadNamedFromSuper(node); | |||
| 99 | case IrOpcode::kJSSetNamedProperty: | |||
| 100 | return ReduceJSSetNamedProperty(node); | |||
| 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 | |||
| 128 | base::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 | ||||
| 150 | Reduction 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 | ||||
| 179 | base::Optional<const StringConstantBase*> | |||
| 180 | JSNativeContextSpecialization::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 | ||||
| 202 | namespace { | |||
| 203 | bool 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 | ||||
| 213 | Reduction 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 | ||||
| 250 | Reduction 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 | ||||
| 286 | Reduction 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 | ||||
| 319 | Reduction 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 | ||||
| 356 | Reduction 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 | ||||
| 383 | Reduction 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 | ||||
| 514 | JSNativeContextSpecialization::InferHasInPrototypeChainResult | |||
| 515 | JSNativeContextSpecialization::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 | ||||
| 584 | Reduction 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 | ||||
| 608 | Reduction 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 | |||
| 661 | Reduction 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 | |||
| 696 | Reduction 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 | ||||
| 746 | namespace { | |||
| 747 | ||||
| 748 | FieldAccess 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. | |||
| 772 | Reduction 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 | ||||
| 980 | Reduction 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 | ||||
| 1009 | Reduction 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 | ||||
| 1040 | Reduction 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) { | |||
| 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)) { | |||
| 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) { | |||
| 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( | |||
| 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) { | |||
| 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; | |||
| 1145 | Node* if_exception = nullptr; | |||
| 1146 | if (NodeProperties::IsExceptionalCall(node, &if_exception)) { | |||
| 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) { | |||
| 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
| |||
| 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
| |||
| 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) { | |||
| 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) { | |||
| 1357 | value = effect = control = jsgraph()->Dead(); | |||
| 1358 | } else if (control_count == 1) { | |||
| 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()) { | |||
| 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()); | |||
| ||||
| 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 | ||||
| 1396 | Reduction 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 | ||||
| 1436 | Reduction 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 | ||||
| 1447 | Reduction 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 | ||||
| 1531 | Reduction JSNativeContextSpecialization::ReduceJSSetNamedProperty(Node* node) { | |||
| 1532 | JSSetNamedPropertyNode n(node); | |||
| 1533 | NamedAccess const& p = n.Parameters(); | |||
| 1534 | if (!p.feedback().IsValid()) return NoChange(); | |||
| 1535 | return ReducePropertyAccess(node, nullptr, p.name(broker()), n.value(), | |||
| 1536 | FeedbackSource(p.feedback()), AccessMode::kStore); | |||
| 1537 | } | |||
| 1538 | ||||
| 1539 | Reduction 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 | ||||
| 1549 | Reduction 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 | ||||
| 1577 | namespace { | |||
| 1578 | ||||
| 1579 | base::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 | ||||
| 1592 | void 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. | |||
| 1606 | ElementAccessFeedback const& | |||
| 1607 | JSNativeContextSpecialization::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 | ||||
| 1626 | Reduction 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 | ||||
| 1853 | Reduction 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 | ||||
| 1941 | Reduction 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()) { | |||
| 1960 | case ProcessedFeedback::kInsufficient: | |||
| 1961 | return ReduceEagerDeoptimize( | |||
| 1962 | node, | |||
| 1963 | DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess); | |||
| 1964 | case ProcessedFeedback::kNamedAccess: | |||
| 1965 | return ReduceNamedAccess(node, value, feedback.AsNamedAccess(), | |||
| 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 | ||||
| 1977 | Reduction 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 | ||||
| 1996 | Reduction 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 | ||||
| 2005 | Reduction 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 | ||||
| 2110 | Reduction 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 | ||||
| 2126 | Reduction 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 | ||||
| 2134 | Reduction 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 | ||||
| 2144 | Node* 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 | ||||
| 2193 | void 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 | ||||
| 2226 | Node* 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 | ||||
| 2275 | base::Optional<JSNativeContextSpecialization::ValueEffectControl> | |||
| 2276 | JSNativeContextSpecialization::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 | ||||
| 2329 | JSNativeContextSpecialization::ValueEffectControl | |||
| 2330 | JSNativeContextSpecialization::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 | ||||
| 2348 | base::Optional<JSNativeContextSpecialization::ValueEffectControl> | |||
| 2349 | JSNativeContextSpecialization::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 | ||||
| 2373 | JSNativeContextSpecialization::ValueEffectControl | |||
| 2374 | JSNativeContextSpecialization::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 | ||||
| 2581 | Reduction | |||
| 2582 | JSNativeContextSpecialization::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 | ||||
| 2600 | Reduction 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 | ||||
| 2610 | Reduction 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 | ||||
| 2624 | namespace { | |||
| 2625 | ||||
| 2626 | ExternalArrayType 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 | ||||
| 2641 | JSNativeContextSpecialization::ValueEffectControl | |||
| 2642 | JSNativeContextSpecialization::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 | ||||
| 3245 | Node* 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 | ||||
| 3299 | Node* 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 | ||||
| 3367 | Node* 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 | ||||
| 3379 | bool 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 | ||||
| 3396 | bool 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 | ||||
| 3420 | base::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 | ||||
| 3437 | Node* 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 | ||||
| 3447 | Graph* JSNativeContextSpecialization::graph() const { | |||
| 3448 | return jsgraph()->graph(); | |||
| 3449 | } | |||
| 3450 | ||||
| 3451 | Isolate* JSNativeContextSpecialization::isolate() const { | |||
| 3452 | return jsgraph()->isolate(); | |||
| 3453 | } | |||
| 3454 | ||||
| 3455 | Factory* JSNativeContextSpecialization::factory() const { | |||
| 3456 | return isolate()->factory(); | |||
| 3457 | } | |||
| 3458 | ||||
| 3459 | CommonOperatorBuilder* JSNativeContextSpecialization::common() const { | |||
| 3460 | return jsgraph()->common(); | |||
| 3461 | } | |||
| 3462 | ||||
| 3463 | JSOperatorBuilder* JSNativeContextSpecialization::javascript() const { | |||
| 3464 | return jsgraph()->javascript(); | |||
| 3465 | } | |||
| 3466 | ||||
| 3467 | SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { | |||
| 3468 | return jsgraph()->simplified(); | |||
| 3469 | } | |||
| 3470 | ||||
| 3471 | } // namespace compiler | |||
| 3472 | } // namespace internal | |||
| 3473 | } // namespace v8 |