OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 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-create-lowering.h" |
| 6 |
| 7 #include "src/code-factory.h" |
| 8 #include "src/compilation-dependencies.h" |
| 9 #include "src/compiler/access-builder.h" |
| 10 #include "src/compiler/common-operator.h" |
| 11 #include "src/compiler/js-graph.h" |
| 12 #include "src/compiler/js-operator.h" |
| 13 #include "src/compiler/linkage.h" |
| 14 #include "src/compiler/node.h" |
| 15 #include "src/compiler/node-properties.h" |
| 16 #include "src/compiler/simplified-operator.h" |
| 17 #include "src/compiler/state-values-utils.h" |
| 18 |
| 19 namespace v8 { |
| 20 namespace internal { |
| 21 namespace compiler { |
| 22 |
| 23 namespace { |
| 24 |
| 25 // A helper class to construct inline allocations on the simplified operator |
| 26 // level. This keeps track of the effect chain for initial stores on a newly |
| 27 // allocated object and also provides helpers for commonly allocated objects. |
| 28 class AllocationBuilder final { |
| 29 public: |
| 30 AllocationBuilder(JSGraph* jsgraph, Node* effect, Node* control) |
| 31 : jsgraph_(jsgraph), |
| 32 allocation_(nullptr), |
| 33 effect_(effect), |
| 34 control_(control) {} |
| 35 |
| 36 // Primitive allocation of static size. |
| 37 void Allocate(int size, PretenureFlag pretenure = NOT_TENURED) { |
| 38 effect_ = graph()->NewNode(common()->BeginRegion(), effect_); |
| 39 allocation_ = |
| 40 graph()->NewNode(simplified()->Allocate(pretenure), |
| 41 jsgraph()->Constant(size), effect_, control_); |
| 42 effect_ = allocation_; |
| 43 } |
| 44 |
| 45 // Primitive store into a field. |
| 46 void Store(const FieldAccess& access, Node* value) { |
| 47 effect_ = graph()->NewNode(simplified()->StoreField(access), allocation_, |
| 48 value, effect_, control_); |
| 49 } |
| 50 |
| 51 // Primitive store into an element. |
| 52 void Store(ElementAccess const& access, Node* index, Node* value) { |
| 53 effect_ = graph()->NewNode(simplified()->StoreElement(access), allocation_, |
| 54 index, value, effect_, control_); |
| 55 } |
| 56 |
| 57 // Compound allocation of a FixedArray. |
| 58 void AllocateArray(int length, Handle<Map> map, |
| 59 PretenureFlag pretenure = NOT_TENURED) { |
| 60 DCHECK(map->instance_type() == FIXED_ARRAY_TYPE || |
| 61 map->instance_type() == FIXED_DOUBLE_ARRAY_TYPE); |
| 62 int size = (map->instance_type() == FIXED_ARRAY_TYPE) |
| 63 ? FixedArray::SizeFor(length) |
| 64 : FixedDoubleArray::SizeFor(length); |
| 65 Allocate(size, pretenure); |
| 66 Store(AccessBuilder::ForMap(), map); |
| 67 Store(AccessBuilder::ForFixedArrayLength(), jsgraph()->Constant(length)); |
| 68 } |
| 69 |
| 70 // Compound store of a constant into a field. |
| 71 void Store(const FieldAccess& access, Handle<Object> value) { |
| 72 Store(access, jsgraph()->Constant(value)); |
| 73 } |
| 74 |
| 75 void FinishAndChange(Node* node) { |
| 76 NodeProperties::SetType(allocation_, NodeProperties::GetType(node)); |
| 77 node->ReplaceInput(0, allocation_); |
| 78 node->ReplaceInput(1, effect_); |
| 79 node->TrimInputCount(2); |
| 80 NodeProperties::ChangeOp(node, common()->FinishRegion()); |
| 81 } |
| 82 |
| 83 Node* Finish() { |
| 84 return graph()->NewNode(common()->FinishRegion(), allocation_, effect_); |
| 85 } |
| 86 |
| 87 protected: |
| 88 JSGraph* jsgraph() { return jsgraph_; } |
| 89 Graph* graph() { return jsgraph_->graph(); } |
| 90 CommonOperatorBuilder* common() { return jsgraph_->common(); } |
| 91 SimplifiedOperatorBuilder* simplified() { return jsgraph_->simplified(); } |
| 92 |
| 93 private: |
| 94 JSGraph* const jsgraph_; |
| 95 Node* allocation_; |
| 96 Node* effect_; |
| 97 Node* control_; |
| 98 }; |
| 99 |
| 100 // Retrieves the frame state holding actual argument values. |
| 101 Node* GetArgumentsFrameState(Node* frame_state) { |
| 102 Node* const outer_state = NodeProperties::GetFrameStateInput(frame_state, 0); |
| 103 FrameStateInfo outer_state_info = OpParameter<FrameStateInfo>(outer_state); |
| 104 return outer_state_info.type() == FrameStateType::kArgumentsAdaptor |
| 105 ? outer_state |
| 106 : frame_state; |
| 107 } |
| 108 |
| 109 // Maximum instance size for which allocations will be inlined. |
| 110 const int kMaxInlineInstanceSize = 64 * kPointerSize; |
| 111 |
| 112 // Checks whether allocation using the given constructor can be inlined. |
| 113 bool IsAllocationInlineable(Handle<JSFunction> constructor) { |
| 114 // TODO(bmeurer): Further relax restrictions on inlining, i.e. |
| 115 // instance type and maybe instance size (inobject properties |
| 116 // are limited anyways by the runtime). |
| 117 return constructor->has_initial_map() && |
| 118 constructor->initial_map()->instance_type() == JS_OBJECT_TYPE && |
| 119 constructor->initial_map()->instance_size() < kMaxInlineInstanceSize; |
| 120 } |
| 121 |
| 122 // When initializing arrays, we'll unfold the loop if the number of |
| 123 // elements is known to be of this type. |
| 124 const int kElementLoopUnrollLimit = 16; |
| 125 |
| 126 // Limits up to which context allocations are inlined. |
| 127 const int kFunctionContextAllocationLimit = 16; |
| 128 const int kBlockContextAllocationLimit = 16; |
| 129 |
| 130 } // namespace |
| 131 |
| 132 Reduction JSCreateLowering::Reduce(Node* node) { |
| 133 switch (node->opcode()) { |
| 134 case IrOpcode::kJSCreate: |
| 135 return ReduceJSCreate(node); |
| 136 case IrOpcode::kJSCreateArguments: |
| 137 return ReduceJSCreateArguments(node); |
| 138 case IrOpcode::kJSCreateArray: |
| 139 return ReduceJSCreateArray(node); |
| 140 case IrOpcode::kJSCreateIterResultObject: |
| 141 return ReduceJSCreateIterResultObject(node); |
| 142 case IrOpcode::kJSCreateFunctionContext: |
| 143 return ReduceJSCreateFunctionContext(node); |
| 144 case IrOpcode::kJSCreateWithContext: |
| 145 return ReduceJSCreateWithContext(node); |
| 146 case IrOpcode::kJSCreateCatchContext: |
| 147 return ReduceJSCreateCatchContext(node); |
| 148 case IrOpcode::kJSCreateBlockContext: |
| 149 return ReduceJSCreateBlockContext(node); |
| 150 default: |
| 151 break; |
| 152 } |
| 153 return NoChange(); |
| 154 } |
| 155 |
| 156 Reduction JSCreateLowering::ReduceJSCreate(Node* node) { |
| 157 DCHECK_EQ(IrOpcode::kJSCreate, node->opcode()); |
| 158 Node* const target = NodeProperties::GetValueInput(node, 0); |
| 159 Type* const target_type = NodeProperties::GetType(target); |
| 160 Node* const new_target = NodeProperties::GetValueInput(node, 1); |
| 161 Node* const effect = NodeProperties::GetEffectInput(node); |
| 162 // TODO(turbofan): Add support for NewTarget passed to JSCreate. |
| 163 if (target != new_target) return NoChange(); |
| 164 // Extract constructor function. |
| 165 if (target_type->IsConstant() && |
| 166 target_type->AsConstant()->Value()->IsJSFunction()) { |
| 167 Handle<JSFunction> constructor = |
| 168 Handle<JSFunction>::cast(target_type->AsConstant()->Value()); |
| 169 DCHECK(constructor->IsConstructor()); |
| 170 // Force completion of inobject slack tracking before |
| 171 // generating code to finalize the instance size. |
| 172 constructor->CompleteInobjectSlackTrackingIfActive(); |
| 173 |
| 174 // TODO(bmeurer): We fall back to the runtime in case we cannot inline |
| 175 // the allocation here, which is sort of expensive. We should think about |
| 176 // a soft fallback to some NewObjectCodeStub. |
| 177 if (IsAllocationInlineable(constructor)) { |
| 178 // Compute instance size from initial map of {constructor}. |
| 179 Handle<Map> initial_map(constructor->initial_map(), isolate()); |
| 180 int const instance_size = initial_map->instance_size(); |
| 181 |
| 182 // Add a dependency on the {initial_map} to make sure that this code is |
| 183 // deoptimized whenever the {initial_map} of the {constructor} changes. |
| 184 dependencies()->AssumeInitialMapCantChange(initial_map); |
| 185 |
| 186 // Emit code to allocate the JSObject instance for the {constructor}. |
| 187 AllocationBuilder a(jsgraph(), effect, graph()->start()); |
| 188 a.Allocate(instance_size); |
| 189 a.Store(AccessBuilder::ForMap(), initial_map); |
| 190 a.Store(AccessBuilder::ForJSObjectProperties(), |
| 191 jsgraph()->EmptyFixedArrayConstant()); |
| 192 a.Store(AccessBuilder::ForJSObjectElements(), |
| 193 jsgraph()->EmptyFixedArrayConstant()); |
| 194 for (int i = 0; i < initial_map->GetInObjectProperties(); ++i) { |
| 195 a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i), |
| 196 jsgraph()->UndefinedConstant()); |
| 197 } |
| 198 a.FinishAndChange(node); |
| 199 return Changed(node); |
| 200 } |
| 201 } |
| 202 return NoChange(); |
| 203 } |
| 204 |
| 205 Reduction JSCreateLowering::ReduceJSCreateArguments(Node* node) { |
| 206 DCHECK_EQ(IrOpcode::kJSCreateArguments, node->opcode()); |
| 207 CreateArgumentsType type = CreateArgumentsTypeOf(node->op()); |
| 208 Node* const frame_state = NodeProperties::GetFrameStateInput(node, 0); |
| 209 Node* const outer_state = frame_state->InputAt(kFrameStateOuterStateInput); |
| 210 FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state); |
| 211 |
| 212 // Use the ArgumentsAccessStub for materializing both mapped and unmapped |
| 213 // arguments object, but only for non-inlined (i.e. outermost) frames. |
| 214 if (outer_state->opcode() != IrOpcode::kFrameState) { |
| 215 if (type != CreateArgumentsType::kRestParameter) { |
| 216 // TODO(bmeurer): Cleanup this mess at some point. |
| 217 int parameter_count = state_info.parameter_count() - 1; |
| 218 int parameter_offset = parameter_count * kPointerSize; |
| 219 int offset = StandardFrameConstants::kCallerSPOffset + parameter_offset; |
| 220 Node* parameter_pointer = graph()->NewNode( |
| 221 machine()->IntAdd(), graph()->NewNode(machine()->LoadFramePointer()), |
| 222 jsgraph()->IntPtrConstant(offset)); |
| 223 Handle<SharedFunctionInfo> shared; |
| 224 if (!state_info.shared_info().ToHandle(&shared)) return NoChange(); |
| 225 bool unmapped = type == CreateArgumentsType::kUnmappedArguments; |
| 226 Callable callable = CodeFactory::ArgumentsAccess( |
| 227 isolate(), unmapped, shared->has_duplicate_parameters()); |
| 228 CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| 229 isolate(), graph()->zone(), callable.descriptor(), 0, |
| 230 CallDescriptor::kNeedsFrameState); |
| 231 const Operator* new_op = common()->Call(desc); |
| 232 Node* stub_code = jsgraph()->HeapConstant(callable.code()); |
| 233 node->InsertInput(graph()->zone(), 0, stub_code); |
| 234 node->InsertInput(graph()->zone(), 2, |
| 235 jsgraph()->Constant(parameter_count)); |
| 236 node->InsertInput(graph()->zone(), 3, parameter_pointer); |
| 237 NodeProperties::ChangeOp(node, new_op); |
| 238 return Changed(node); |
| 239 } else { |
| 240 Callable callable = CodeFactory::FastNewRestParameter(isolate()); |
| 241 CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| 242 isolate(), graph()->zone(), callable.descriptor(), 0, |
| 243 CallDescriptor::kNeedsFrameState); |
| 244 const Operator* new_op = common()->Call(desc); |
| 245 Node* stub_code = jsgraph()->HeapConstant(callable.code()); |
| 246 node->InsertInput(graph()->zone(), 0, stub_code); |
| 247 NodeProperties::ChangeOp(node, new_op); |
| 248 return Changed(node); |
| 249 } |
| 250 } else if (outer_state->opcode() == IrOpcode::kFrameState) { |
| 251 // Use inline allocation for all mapped arguments objects within inlined |
| 252 // (i.e. non-outermost) frames, independent of the object size. |
| 253 if (type == CreateArgumentsType::kMappedArguments) { |
| 254 Handle<SharedFunctionInfo> shared; |
| 255 if (!state_info.shared_info().ToHandle(&shared)) return NoChange(); |
| 256 Node* const callee = NodeProperties::GetValueInput(node, 0); |
| 257 Node* const control = NodeProperties::GetControlInput(node); |
| 258 Node* const context = NodeProperties::GetContextInput(node); |
| 259 Node* effect = NodeProperties::GetEffectInput(node); |
| 260 // TODO(mstarzinger): Duplicate parameters are not handled yet. |
| 261 if (shared->has_duplicate_parameters()) return NoChange(); |
| 262 // Choose the correct frame state and frame state info depending on |
| 263 // whether there conceptually is an arguments adaptor frame in the call |
| 264 // chain. |
| 265 Node* const args_state = GetArgumentsFrameState(frame_state); |
| 266 FrameStateInfo args_state_info = OpParameter<FrameStateInfo>(args_state); |
| 267 // Prepare element backing store to be used by arguments object. |
| 268 bool has_aliased_arguments = false; |
| 269 Node* const elements = AllocateAliasedArguments( |
| 270 effect, control, args_state, context, shared, &has_aliased_arguments); |
| 271 effect = elements->op()->EffectOutputCount() > 0 ? elements : effect; |
| 272 // Load the arguments object map from the current native context. |
| 273 Node* const load_native_context = effect = graph()->NewNode( |
| 274 javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), |
| 275 context, context, effect); |
| 276 Node* const load_arguments_map = effect = graph()->NewNode( |
| 277 simplified()->LoadField(AccessBuilder::ForContextSlot( |
| 278 has_aliased_arguments ? Context::FAST_ALIASED_ARGUMENTS_MAP_INDEX |
| 279 : Context::SLOPPY_ARGUMENTS_MAP_INDEX)), |
| 280 load_native_context, effect, control); |
| 281 // Actually allocate and initialize the arguments object. |
| 282 AllocationBuilder a(jsgraph(), effect, control); |
| 283 Node* properties = jsgraph()->EmptyFixedArrayConstant(); |
| 284 int length = args_state_info.parameter_count() - 1; // Minus receiver. |
| 285 STATIC_ASSERT(Heap::kSloppyArgumentsObjectSize == 5 * kPointerSize); |
| 286 a.Allocate(Heap::kSloppyArgumentsObjectSize); |
| 287 a.Store(AccessBuilder::ForMap(), load_arguments_map); |
| 288 a.Store(AccessBuilder::ForJSObjectProperties(), properties); |
| 289 a.Store(AccessBuilder::ForJSObjectElements(), elements); |
| 290 a.Store(AccessBuilder::ForArgumentsLength(), jsgraph()->Constant(length)); |
| 291 a.Store(AccessBuilder::ForArgumentsCallee(), callee); |
| 292 RelaxControls(node); |
| 293 a.FinishAndChange(node); |
| 294 return Changed(node); |
| 295 } else if (type == CreateArgumentsType::kUnmappedArguments) { |
| 296 // Use inline allocation for all unmapped arguments objects within inlined |
| 297 // (i.e. non-outermost) frames, independent of the object size. |
| 298 Node* const control = NodeProperties::GetControlInput(node); |
| 299 Node* const context = NodeProperties::GetContextInput(node); |
| 300 Node* effect = NodeProperties::GetEffectInput(node); |
| 301 // Choose the correct frame state and frame state info depending on |
| 302 // whether there conceptually is an arguments adaptor frame in the call |
| 303 // chain. |
| 304 Node* const args_state = GetArgumentsFrameState(frame_state); |
| 305 FrameStateInfo args_state_info = OpParameter<FrameStateInfo>(args_state); |
| 306 // Prepare element backing store to be used by arguments object. |
| 307 Node* const elements = AllocateArguments(effect, control, args_state); |
| 308 effect = elements->op()->EffectOutputCount() > 0 ? elements : effect; |
| 309 // Load the arguments object map from the current native context. |
| 310 Node* const load_native_context = effect = graph()->NewNode( |
| 311 javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), |
| 312 context, context, effect); |
| 313 Node* const load_arguments_map = effect = graph()->NewNode( |
| 314 simplified()->LoadField(AccessBuilder::ForContextSlot( |
| 315 Context::STRICT_ARGUMENTS_MAP_INDEX)), |
| 316 load_native_context, effect, control); |
| 317 // Actually allocate and initialize the arguments object. |
| 318 AllocationBuilder a(jsgraph(), effect, control); |
| 319 Node* properties = jsgraph()->EmptyFixedArrayConstant(); |
| 320 int length = args_state_info.parameter_count() - 1; // Minus receiver. |
| 321 STATIC_ASSERT(Heap::kStrictArgumentsObjectSize == 4 * kPointerSize); |
| 322 a.Allocate(Heap::kStrictArgumentsObjectSize); |
| 323 a.Store(AccessBuilder::ForMap(), load_arguments_map); |
| 324 a.Store(AccessBuilder::ForJSObjectProperties(), properties); |
| 325 a.Store(AccessBuilder::ForJSObjectElements(), elements); |
| 326 a.Store(AccessBuilder::ForArgumentsLength(), jsgraph()->Constant(length)); |
| 327 RelaxControls(node); |
| 328 a.FinishAndChange(node); |
| 329 return Changed(node); |
| 330 } else if (type == CreateArgumentsType::kRestParameter) { |
| 331 Handle<SharedFunctionInfo> shared; |
| 332 if (!state_info.shared_info().ToHandle(&shared)) return NoChange(); |
| 333 int start_index = shared->internal_formal_parameter_count(); |
| 334 // Use inline allocation for all unmapped arguments objects within inlined |
| 335 // (i.e. non-outermost) frames, independent of the object size. |
| 336 Node* const control = NodeProperties::GetControlInput(node); |
| 337 Node* const context = NodeProperties::GetContextInput(node); |
| 338 Node* effect = NodeProperties::GetEffectInput(node); |
| 339 // Choose the correct frame state and frame state info depending on |
| 340 // whether there conceptually is an arguments adaptor frame in the call |
| 341 // chain. |
| 342 Node* const args_state = GetArgumentsFrameState(frame_state); |
| 343 FrameStateInfo args_state_info = OpParameter<FrameStateInfo>(args_state); |
| 344 // Prepare element backing store to be used by the rest array. |
| 345 Node* const elements = |
| 346 AllocateRestArguments(effect, control, args_state, start_index); |
| 347 effect = elements->op()->EffectOutputCount() > 0 ? elements : effect; |
| 348 // Load the JSArray object map from the current native context. |
| 349 Node* const load_native_context = effect = graph()->NewNode( |
| 350 javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), |
| 351 context, context, effect); |
| 352 Node* const load_jsarray_map = effect = graph()->NewNode( |
| 353 simplified()->LoadField(AccessBuilder::ForContextSlot( |
| 354 Context::JS_ARRAY_FAST_ELEMENTS_MAP_INDEX)), |
| 355 load_native_context, effect, control); |
| 356 // Actually allocate and initialize the jsarray. |
| 357 AllocationBuilder a(jsgraph(), effect, control); |
| 358 Node* properties = jsgraph()->EmptyFixedArrayConstant(); |
| 359 |
| 360 // -1 to minus receiver |
| 361 int argument_count = args_state_info.parameter_count() - 1; |
| 362 int length = std::max(0, argument_count - start_index); |
| 363 STATIC_ASSERT(JSArray::kSize == 4 * kPointerSize); |
| 364 a.Allocate(JSArray::kSize); |
| 365 a.Store(AccessBuilder::ForMap(), load_jsarray_map); |
| 366 a.Store(AccessBuilder::ForJSObjectProperties(), properties); |
| 367 a.Store(AccessBuilder::ForJSObjectElements(), elements); |
| 368 a.Store(AccessBuilder::ForJSArrayLength(FAST_ELEMENTS), |
| 369 jsgraph()->Constant(length)); |
| 370 RelaxControls(node); |
| 371 a.FinishAndChange(node); |
| 372 return Changed(node); |
| 373 } |
| 374 } |
| 375 |
| 376 return NoChange(); |
| 377 } |
| 378 |
| 379 Reduction JSCreateLowering::ReduceNewArray(Node* node, Node* length, |
| 380 int capacity, |
| 381 Handle<AllocationSite> site) { |
| 382 DCHECK_EQ(IrOpcode::kJSCreateArray, node->opcode()); |
| 383 Node* context = NodeProperties::GetContextInput(node); |
| 384 Node* effect = NodeProperties::GetEffectInput(node); |
| 385 Node* control = NodeProperties::GetControlInput(node); |
| 386 |
| 387 // Extract transition and tenuring feedback from the {site} and add |
| 388 // appropriate code dependencies on the {site} if deoptimization is |
| 389 // enabled. |
| 390 PretenureFlag pretenure = site->GetPretenureMode(); |
| 391 ElementsKind elements_kind = site->GetElementsKind(); |
| 392 DCHECK(IsFastElementsKind(elements_kind)); |
| 393 dependencies()->AssumeTenuringDecision(site); |
| 394 dependencies()->AssumeTransitionStable(site); |
| 395 |
| 396 // Retrieve the initial map for the array from the appropriate native context. |
| 397 Node* native_context = effect = graph()->NewNode( |
| 398 javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), |
| 399 context, context, effect); |
| 400 Node* js_array_map = effect = graph()->NewNode( |
| 401 javascript()->LoadContext(0, Context::ArrayMapIndex(elements_kind), true), |
| 402 native_context, native_context, effect); |
| 403 |
| 404 // Setup elements and properties. |
| 405 Node* elements; |
| 406 if (capacity == 0) { |
| 407 elements = jsgraph()->EmptyFixedArrayConstant(); |
| 408 } else { |
| 409 elements = effect = |
| 410 AllocateElements(effect, control, elements_kind, capacity, pretenure); |
| 411 } |
| 412 Node* properties = jsgraph()->EmptyFixedArrayConstant(); |
| 413 |
| 414 // Perform the allocation of the actual JSArray object. |
| 415 AllocationBuilder a(jsgraph(), effect, control); |
| 416 a.Allocate(JSArray::kSize, pretenure); |
| 417 a.Store(AccessBuilder::ForMap(), js_array_map); |
| 418 a.Store(AccessBuilder::ForJSObjectProperties(), properties); |
| 419 a.Store(AccessBuilder::ForJSObjectElements(), elements); |
| 420 a.Store(AccessBuilder::ForJSArrayLength(elements_kind), length); |
| 421 RelaxControls(node); |
| 422 a.FinishAndChange(node); |
| 423 return Changed(node); |
| 424 } |
| 425 |
| 426 Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) { |
| 427 DCHECK_EQ(IrOpcode::kJSCreateArray, node->opcode()); |
| 428 CreateArrayParameters const& p = CreateArrayParametersOf(node->op()); |
| 429 Node* target = NodeProperties::GetValueInput(node, 0); |
| 430 Node* new_target = NodeProperties::GetValueInput(node, 1); |
| 431 |
| 432 // TODO(bmeurer): Optimize the subclassing case. |
| 433 if (target != new_target) return NoChange(); |
| 434 |
| 435 // Check if we have a feedback {site} on the {node}. |
| 436 Handle<AllocationSite> site = p.site(); |
| 437 if (p.site().is_null()) return NoChange(); |
| 438 |
| 439 // Attempt to inline calls to the Array constructor for the relevant cases |
| 440 // where either no arguments are provided, or exactly one unsigned number |
| 441 // argument is given. |
| 442 if (site->CanInlineCall()) { |
| 443 if (p.arity() == 0) { |
| 444 Node* length = jsgraph()->ZeroConstant(); |
| 445 int capacity = JSArray::kPreallocatedArrayElements; |
| 446 return ReduceNewArray(node, length, capacity, site); |
| 447 } else if (p.arity() == 1) { |
| 448 Node* length = NodeProperties::GetValueInput(node, 2); |
| 449 Type* length_type = NodeProperties::GetType(length); |
| 450 if (length_type->Is(Type::SignedSmall()) && |
| 451 length_type->Min() >= 0 && |
| 452 length_type->Max() <= kElementLoopUnrollLimit) { |
| 453 int capacity = static_cast<int>(length_type->Max()); |
| 454 return ReduceNewArray(node, length, capacity, site); |
| 455 } |
| 456 } |
| 457 } |
| 458 |
| 459 return NoChange(); |
| 460 } |
| 461 |
| 462 Reduction JSCreateLowering::ReduceJSCreateIterResultObject(Node* node) { |
| 463 DCHECK_EQ(IrOpcode::kJSCreateIterResultObject, node->opcode()); |
| 464 Node* value = NodeProperties::GetValueInput(node, 0); |
| 465 Node* done = NodeProperties::GetValueInput(node, 1); |
| 466 Node* context = NodeProperties::GetContextInput(node); |
| 467 Node* effect = NodeProperties::GetEffectInput(node); |
| 468 |
| 469 // Load the JSIteratorResult map for the {context}. |
| 470 Node* native_context = effect = graph()->NewNode( |
| 471 javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), |
| 472 context, context, effect); |
| 473 Node* iterator_result_map = effect = graph()->NewNode( |
| 474 javascript()->LoadContext(0, Context::ITERATOR_RESULT_MAP_INDEX, true), |
| 475 native_context, native_context, effect); |
| 476 |
| 477 // Emit code to allocate the JSIteratorResult instance. |
| 478 AllocationBuilder a(jsgraph(), effect, graph()->start()); |
| 479 a.Allocate(JSIteratorResult::kSize); |
| 480 a.Store(AccessBuilder::ForMap(), iterator_result_map); |
| 481 a.Store(AccessBuilder::ForJSObjectProperties(), |
| 482 jsgraph()->EmptyFixedArrayConstant()); |
| 483 a.Store(AccessBuilder::ForJSObjectElements(), |
| 484 jsgraph()->EmptyFixedArrayConstant()); |
| 485 a.Store(AccessBuilder::ForJSIteratorResultValue(), value); |
| 486 a.Store(AccessBuilder::ForJSIteratorResultDone(), done); |
| 487 STATIC_ASSERT(JSIteratorResult::kSize == 5 * kPointerSize); |
| 488 a.FinishAndChange(node); |
| 489 return Changed(node); |
| 490 } |
| 491 |
| 492 Reduction JSCreateLowering::ReduceJSCreateFunctionContext(Node* node) { |
| 493 DCHECK_EQ(IrOpcode::kJSCreateFunctionContext, node->opcode()); |
| 494 int slot_count = OpParameter<int>(node->op()); |
| 495 Node* const closure = NodeProperties::GetValueInput(node, 0); |
| 496 |
| 497 // Use inline allocation for function contexts up to a size limit. |
| 498 if (slot_count < kFunctionContextAllocationLimit) { |
| 499 // JSCreateFunctionContext[slot_count < limit]](fun) |
| 500 Node* effect = NodeProperties::GetEffectInput(node); |
| 501 Node* control = NodeProperties::GetControlInput(node); |
| 502 Node* context = NodeProperties::GetContextInput(node); |
| 503 Node* extension = jsgraph()->TheHoleConstant(); |
| 504 Node* native_context = effect = graph()->NewNode( |
| 505 javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), |
| 506 context, context, effect); |
| 507 AllocationBuilder a(jsgraph(), effect, control); |
| 508 STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 4); // Ensure fully covered. |
| 509 int context_length = slot_count + Context::MIN_CONTEXT_SLOTS; |
| 510 a.AllocateArray(context_length, factory()->function_context_map()); |
| 511 a.Store(AccessBuilder::ForContextSlot(Context::CLOSURE_INDEX), closure); |
| 512 a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context); |
| 513 a.Store(AccessBuilder::ForContextSlot(Context::EXTENSION_INDEX), extension); |
| 514 a.Store(AccessBuilder::ForContextSlot(Context::NATIVE_CONTEXT_INDEX), |
| 515 native_context); |
| 516 for (int i = Context::MIN_CONTEXT_SLOTS; i < context_length; ++i) { |
| 517 a.Store(AccessBuilder::ForContextSlot(i), jsgraph()->UndefinedConstant()); |
| 518 } |
| 519 RelaxControls(node); |
| 520 a.FinishAndChange(node); |
| 521 return Changed(node); |
| 522 } |
| 523 |
| 524 return NoChange(); |
| 525 } |
| 526 |
| 527 Reduction JSCreateLowering::ReduceJSCreateWithContext(Node* node) { |
| 528 DCHECK_EQ(IrOpcode::kJSCreateWithContext, node->opcode()); |
| 529 Node* object = NodeProperties::GetValueInput(node, 0); |
| 530 Node* closure = NodeProperties::GetValueInput(node, 1); |
| 531 Node* effect = NodeProperties::GetEffectInput(node); |
| 532 Node* control = NodeProperties::GetControlInput(node); |
| 533 Node* context = NodeProperties::GetContextInput(node); |
| 534 Node* native_context = effect = graph()->NewNode( |
| 535 javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), |
| 536 context, context, effect); |
| 537 AllocationBuilder a(jsgraph(), effect, control); |
| 538 STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 4); // Ensure fully covered. |
| 539 a.AllocateArray(Context::MIN_CONTEXT_SLOTS, factory()->with_context_map()); |
| 540 a.Store(AccessBuilder::ForContextSlot(Context::CLOSURE_INDEX), closure); |
| 541 a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context); |
| 542 a.Store(AccessBuilder::ForContextSlot(Context::EXTENSION_INDEX), object); |
| 543 a.Store(AccessBuilder::ForContextSlot(Context::NATIVE_CONTEXT_INDEX), |
| 544 native_context); |
| 545 RelaxControls(node); |
| 546 a.FinishAndChange(node); |
| 547 return Changed(node); |
| 548 } |
| 549 |
| 550 Reduction JSCreateLowering::ReduceJSCreateCatchContext(Node* node) { |
| 551 DCHECK_EQ(IrOpcode::kJSCreateCatchContext, node->opcode()); |
| 552 Handle<String> name = OpParameter<Handle<String>>(node); |
| 553 Node* exception = NodeProperties::GetValueInput(node, 0); |
| 554 Node* closure = NodeProperties::GetValueInput(node, 1); |
| 555 Node* effect = NodeProperties::GetEffectInput(node); |
| 556 Node* control = NodeProperties::GetControlInput(node); |
| 557 Node* context = NodeProperties::GetContextInput(node); |
| 558 Node* native_context = effect = graph()->NewNode( |
| 559 javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), |
| 560 context, context, effect); |
| 561 AllocationBuilder a(jsgraph(), effect, control); |
| 562 STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 4); // Ensure fully covered. |
| 563 a.AllocateArray(Context::MIN_CONTEXT_SLOTS + 1, |
| 564 factory()->catch_context_map()); |
| 565 a.Store(AccessBuilder::ForContextSlot(Context::CLOSURE_INDEX), closure); |
| 566 a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context); |
| 567 a.Store(AccessBuilder::ForContextSlot(Context::EXTENSION_INDEX), name); |
| 568 a.Store(AccessBuilder::ForContextSlot(Context::NATIVE_CONTEXT_INDEX), |
| 569 native_context); |
| 570 a.Store(AccessBuilder::ForContextSlot(Context::THROWN_OBJECT_INDEX), |
| 571 exception); |
| 572 RelaxControls(node); |
| 573 a.FinishAndChange(node); |
| 574 return Changed(node); |
| 575 } |
| 576 |
| 577 Reduction JSCreateLowering::ReduceJSCreateBlockContext(Node* node) { |
| 578 DCHECK_EQ(IrOpcode::kJSCreateBlockContext, node->opcode()); |
| 579 Handle<ScopeInfo> scope_info = OpParameter<Handle<ScopeInfo>>(node); |
| 580 int const context_length = scope_info->ContextLength(); |
| 581 Node* const closure = NodeProperties::GetValueInput(node, 0); |
| 582 |
| 583 // Use inline allocation for block contexts up to a size limit. |
| 584 if (context_length < kBlockContextAllocationLimit) { |
| 585 // JSCreateBlockContext[scope[length < limit]](fun) |
| 586 Node* effect = NodeProperties::GetEffectInput(node); |
| 587 Node* control = NodeProperties::GetControlInput(node); |
| 588 Node* context = NodeProperties::GetContextInput(node); |
| 589 Node* extension = jsgraph()->Constant(scope_info); |
| 590 Node* native_context = effect = graph()->NewNode( |
| 591 javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true), |
| 592 context, context, effect); |
| 593 AllocationBuilder a(jsgraph(), effect, control); |
| 594 STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 4); // Ensure fully covered. |
| 595 a.AllocateArray(context_length, factory()->block_context_map()); |
| 596 a.Store(AccessBuilder::ForContextSlot(Context::CLOSURE_INDEX), closure); |
| 597 a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context); |
| 598 a.Store(AccessBuilder::ForContextSlot(Context::EXTENSION_INDEX), extension); |
| 599 a.Store(AccessBuilder::ForContextSlot(Context::NATIVE_CONTEXT_INDEX), |
| 600 native_context); |
| 601 for (int i = Context::MIN_CONTEXT_SLOTS; i < context_length; ++i) { |
| 602 a.Store(AccessBuilder::ForContextSlot(i), jsgraph()->UndefinedConstant()); |
| 603 } |
| 604 RelaxControls(node); |
| 605 a.FinishAndChange(node); |
| 606 return Changed(node); |
| 607 } |
| 608 |
| 609 return NoChange(); |
| 610 } |
| 611 |
| 612 // Helper that allocates a FixedArray holding argument values recorded in the |
| 613 // given {frame_state}. Serves as backing store for JSCreateArguments nodes. |
| 614 Node* JSCreateLowering::AllocateArguments(Node* effect, Node* control, |
| 615 Node* frame_state) { |
| 616 FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state); |
| 617 int argument_count = state_info.parameter_count() - 1; // Minus receiver. |
| 618 if (argument_count == 0) return jsgraph()->EmptyFixedArrayConstant(); |
| 619 |
| 620 // Prepare an iterator over argument values recorded in the frame state. |
| 621 Node* const parameters = frame_state->InputAt(kFrameStateParametersInput); |
| 622 StateValuesAccess parameters_access(parameters); |
| 623 auto parameters_it = ++parameters_access.begin(); |
| 624 |
| 625 // Actually allocate the backing store. |
| 626 AllocationBuilder a(jsgraph(), effect, control); |
| 627 a.AllocateArray(argument_count, factory()->fixed_array_map()); |
| 628 for (int i = 0; i < argument_count; ++i, ++parameters_it) { |
| 629 a.Store(AccessBuilder::ForFixedArraySlot(i), (*parameters_it).node); |
| 630 } |
| 631 return a.Finish(); |
| 632 } |
| 633 |
| 634 // Helper that allocates a FixedArray holding argument values recorded in the |
| 635 // given {frame_state}. Serves as backing store for JSCreateArguments nodes. |
| 636 Node* JSCreateLowering::AllocateRestArguments(Node* effect, Node* control, |
| 637 Node* frame_state, |
| 638 int start_index) { |
| 639 FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state); |
| 640 int argument_count = state_info.parameter_count() - 1; // Minus receiver. |
| 641 int num_elements = std::max(0, argument_count - start_index); |
| 642 if (num_elements == 0) return jsgraph()->EmptyFixedArrayConstant(); |
| 643 |
| 644 // Prepare an iterator over argument values recorded in the frame state. |
| 645 Node* const parameters = frame_state->InputAt(kFrameStateParametersInput); |
| 646 StateValuesAccess parameters_access(parameters); |
| 647 auto parameters_it = ++parameters_access.begin(); |
| 648 |
| 649 // Skip unused arguments. |
| 650 for (int i = 0; i < start_index; i++) { |
| 651 ++parameters_it; |
| 652 } |
| 653 |
| 654 // Actually allocate the backing store. |
| 655 AllocationBuilder a(jsgraph(), effect, control); |
| 656 a.AllocateArray(num_elements, factory()->fixed_array_map()); |
| 657 for (int i = 0; i < num_elements; ++i, ++parameters_it) { |
| 658 a.Store(AccessBuilder::ForFixedArraySlot(i), (*parameters_it).node); |
| 659 } |
| 660 return a.Finish(); |
| 661 } |
| 662 |
| 663 // Helper that allocates a FixedArray serving as a parameter map for values |
| 664 // recorded in the given {frame_state}. Some elements map to slots within the |
| 665 // given {context}. Serves as backing store for JSCreateArguments nodes. |
| 666 Node* JSCreateLowering::AllocateAliasedArguments( |
| 667 Node* effect, Node* control, Node* frame_state, Node* context, |
| 668 Handle<SharedFunctionInfo> shared, bool* has_aliased_arguments) { |
| 669 FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state); |
| 670 int argument_count = state_info.parameter_count() - 1; // Minus receiver. |
| 671 if (argument_count == 0) return jsgraph()->EmptyFixedArrayConstant(); |
| 672 |
| 673 // If there is no aliasing, the arguments object elements are not special in |
| 674 // any way, we can just return an unmapped backing store instead. |
| 675 int parameter_count = shared->internal_formal_parameter_count(); |
| 676 if (parameter_count == 0) { |
| 677 return AllocateArguments(effect, control, frame_state); |
| 678 } |
| 679 |
| 680 // Calculate number of argument values being aliased/mapped. |
| 681 int mapped_count = Min(argument_count, parameter_count); |
| 682 *has_aliased_arguments = true; |
| 683 |
| 684 // Prepare an iterator over argument values recorded in the frame state. |
| 685 Node* const parameters = frame_state->InputAt(kFrameStateParametersInput); |
| 686 StateValuesAccess parameters_access(parameters); |
| 687 auto paratemers_it = ++parameters_access.begin(); |
| 688 |
| 689 // The unmapped argument values recorded in the frame state are stored yet |
| 690 // another indirection away and then linked into the parameter map below, |
| 691 // whereas mapped argument values are replaced with a hole instead. |
| 692 AllocationBuilder aa(jsgraph(), effect, control); |
| 693 aa.AllocateArray(argument_count, factory()->fixed_array_map()); |
| 694 for (int i = 0; i < mapped_count; ++i, ++paratemers_it) { |
| 695 aa.Store(AccessBuilder::ForFixedArraySlot(i), jsgraph()->TheHoleConstant()); |
| 696 } |
| 697 for (int i = mapped_count; i < argument_count; ++i, ++paratemers_it) { |
| 698 aa.Store(AccessBuilder::ForFixedArraySlot(i), (*paratemers_it).node); |
| 699 } |
| 700 Node* arguments = aa.Finish(); |
| 701 |
| 702 // Actually allocate the backing store. |
| 703 AllocationBuilder a(jsgraph(), arguments, control); |
| 704 a.AllocateArray(mapped_count + 2, factory()->sloppy_arguments_elements_map()); |
| 705 a.Store(AccessBuilder::ForFixedArraySlot(0), context); |
| 706 a.Store(AccessBuilder::ForFixedArraySlot(1), arguments); |
| 707 for (int i = 0; i < mapped_count; ++i) { |
| 708 int idx = Context::MIN_CONTEXT_SLOTS + parameter_count - 1 - i; |
| 709 a.Store(AccessBuilder::ForFixedArraySlot(i + 2), jsgraph()->Constant(idx)); |
| 710 } |
| 711 return a.Finish(); |
| 712 } |
| 713 |
| 714 Node* JSCreateLowering::AllocateElements(Node* effect, Node* control, |
| 715 ElementsKind elements_kind, |
| 716 int capacity, |
| 717 PretenureFlag pretenure) { |
| 718 DCHECK_LE(1, capacity); |
| 719 DCHECK_LE(capacity, JSArray::kInitialMaxFastElementArray); |
| 720 |
| 721 Handle<Map> elements_map = IsFastDoubleElementsKind(elements_kind) |
| 722 ? factory()->fixed_double_array_map() |
| 723 : factory()->fixed_array_map(); |
| 724 ElementAccess access = IsFastDoubleElementsKind(elements_kind) |
| 725 ? AccessBuilder::ForFixedDoubleArrayElement() |
| 726 : AccessBuilder::ForFixedArrayElement(); |
| 727 Node* value = |
| 728 IsFastDoubleElementsKind(elements_kind) |
| 729 ? jsgraph()->Float64Constant(bit_cast<double>(kHoleNanInt64)) |
| 730 : jsgraph()->TheHoleConstant(); |
| 731 |
| 732 // Actually allocate the backing store. |
| 733 AllocationBuilder a(jsgraph(), effect, control); |
| 734 a.AllocateArray(capacity, elements_map, pretenure); |
| 735 for (int i = 0; i < capacity; ++i) { |
| 736 Node* index = jsgraph()->Constant(i); |
| 737 a.Store(access, index, value); |
| 738 } |
| 739 return a.Finish(); |
| 740 } |
| 741 |
| 742 Factory* JSCreateLowering::factory() const { return isolate()->factory(); } |
| 743 |
| 744 Graph* JSCreateLowering::graph() const { return jsgraph()->graph(); } |
| 745 |
| 746 Isolate* JSCreateLowering::isolate() const { return jsgraph()->isolate(); } |
| 747 |
| 748 JSOperatorBuilder* JSCreateLowering::javascript() const { |
| 749 return jsgraph()->javascript(); |
| 750 } |
| 751 |
| 752 CommonOperatorBuilder* JSCreateLowering::common() const { |
| 753 return jsgraph()->common(); |
| 754 } |
| 755 |
| 756 SimplifiedOperatorBuilder* JSCreateLowering::simplified() const { |
| 757 return jsgraph()->simplified(); |
| 758 } |
| 759 |
| 760 MachineOperatorBuilder* JSCreateLowering::machine() const { |
| 761 return jsgraph()->machine(); |
| 762 } |
| 763 |
| 764 } // namespace compiler |
| 765 } // namespace internal |
| 766 } // namespace v8 |
OLD | NEW |