OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 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/builtins/builtins-arguments.h" |
| 6 #include "src/builtins/builtins-utils.h" |
| 7 #include "src/builtins/builtins.h" |
| 8 #include "src/code-factory.h" |
| 9 #include "src/code-stub-assembler.h" |
| 10 #include "src/interface-descriptors.h" |
| 11 |
| 12 namespace v8 { |
| 13 namespace internal { |
| 14 |
| 15 typedef compiler::Node Node; |
| 16 |
| 17 std::tuple<Node*, Node*, Node*> |
| 18 ArgumentsBuiltinsAssembler::GetArgumentsFrameAndCount(Node* function, |
| 19 ParameterMode mode) { |
| 20 CSA_ASSERT(this, HasInstanceType(function, JS_FUNCTION_TYPE)); |
| 21 |
| 22 Variable frame_ptr(this, MachineType::PointerRepresentation()); |
| 23 frame_ptr.Bind(LoadParentFramePointer()); |
| 24 CSA_ASSERT(this, |
| 25 WordEqual(function, |
| 26 LoadBufferObject(frame_ptr.value(), |
| 27 StandardFrameConstants::kFunctionOffset, |
| 28 MachineType::Pointer()))); |
| 29 Variable argument_count(this, ParameterRepresentation(mode)); |
| 30 VariableList list({&frame_ptr, &argument_count}, zone()); |
| 31 Label done_argument_count(this, list); |
| 32 |
| 33 // Determine the number of passed parameters, which is either the count stored |
| 34 // in an arguments adapter frame or fetched from the shared function info. |
| 35 Node* frame_ptr_above = LoadBufferObject( |
| 36 frame_ptr.value(), StandardFrameConstants::kCallerFPOffset, |
| 37 MachineType::Pointer()); |
| 38 Node* shared = |
| 39 LoadObjectField(function, JSFunction::kSharedFunctionInfoOffset); |
| 40 Node* formal_parameter_count = LoadSharedFunctionInfoSpecialField( |
| 41 shared, SharedFunctionInfo::kFormalParameterCountOffset, mode); |
| 42 argument_count.Bind(formal_parameter_count); |
| 43 Node* marker_or_function = LoadBufferObject( |
| 44 frame_ptr_above, CommonFrameConstants::kContextOrFrameTypeOffset); |
| 45 GotoIf(SmiNotEqual(marker_or_function, |
| 46 SmiConstant(StackFrame::ARGUMENTS_ADAPTOR)), |
| 47 &done_argument_count); |
| 48 Node* adapted_parameter_count = LoadBufferObject( |
| 49 frame_ptr_above, ArgumentsAdaptorFrameConstants::kLengthOffset); |
| 50 frame_ptr.Bind(frame_ptr_above); |
| 51 argument_count.Bind(TaggedToParameter(adapted_parameter_count, mode)); |
| 52 Goto(&done_argument_count); |
| 53 |
| 54 Bind(&done_argument_count); |
| 55 return std::tuple<Node*, Node*, Node*>( |
| 56 frame_ptr.value(), argument_count.value(), formal_parameter_count); |
| 57 } |
| 58 |
| 59 std::tuple<Node*, Node*, Node*> |
| 60 ArgumentsBuiltinsAssembler::AllocateArgumentsObject(Node* map, |
| 61 Node* arguments_count, |
| 62 Node* parameter_map_count, |
| 63 ParameterMode mode, |
| 64 int base_size) { |
| 65 // Allocate the parameter object (either a Rest parameter object, a strict |
| 66 // argument object or a sloppy arguments object) and the elements/mapped |
| 67 // arguments together. |
| 68 int elements_offset = base_size; |
| 69 Node* element_count = arguments_count; |
| 70 if (parameter_map_count != nullptr) { |
| 71 base_size += FixedArray::kHeaderSize; |
| 72 element_count = IntPtrOrSmiAdd(element_count, parameter_map_count, mode); |
| 73 } |
| 74 bool empty = IsIntPtrOrSmiConstantZero(arguments_count); |
| 75 DCHECK_IMPLIES(empty, parameter_map_count == nullptr); |
| 76 Node* size = |
| 77 empty ? IntPtrConstant(base_size) |
| 78 : ElementOffsetFromIndex(element_count, FAST_ELEMENTS, mode, |
| 79 base_size + FixedArray::kHeaderSize); |
| 80 Node* result = Allocate(size); |
| 81 Comment("Initialize arguments object"); |
| 82 StoreMapNoWriteBarrier(result, map); |
| 83 Node* empty_fixed_array = LoadRoot(Heap::kEmptyFixedArrayRootIndex); |
| 84 StoreObjectField(result, JSArray::kPropertiesOffset, empty_fixed_array); |
| 85 Node* smi_arguments_count = ParameterToTagged(arguments_count, mode); |
| 86 StoreObjectFieldNoWriteBarrier(result, JSArray::kLengthOffset, |
| 87 smi_arguments_count); |
| 88 Node* arguments = nullptr; |
| 89 if (!empty) { |
| 90 arguments = InnerAllocate(result, elements_offset); |
| 91 StoreObjectFieldNoWriteBarrier(arguments, FixedArray::kLengthOffset, |
| 92 smi_arguments_count); |
| 93 Node* fixed_array_map = LoadRoot(Heap::kFixedArrayMapRootIndex); |
| 94 StoreMapNoWriteBarrier(arguments, fixed_array_map); |
| 95 } |
| 96 Node* parameter_map = nullptr; |
| 97 if (parameter_map_count != nullptr) { |
| 98 Node* parameter_map_offset = ElementOffsetFromIndex( |
| 99 arguments_count, FAST_ELEMENTS, mode, FixedArray::kHeaderSize); |
| 100 parameter_map = InnerAllocate(arguments, parameter_map_offset); |
| 101 StoreObjectFieldNoWriteBarrier(result, JSArray::kElementsOffset, |
| 102 parameter_map); |
| 103 Node* sloppy_elements_map = |
| 104 LoadRoot(Heap::kSloppyArgumentsElementsMapRootIndex); |
| 105 StoreMapNoWriteBarrier(parameter_map, sloppy_elements_map); |
| 106 parameter_map_count = ParameterToTagged(parameter_map_count, mode); |
| 107 StoreObjectFieldNoWriteBarrier(parameter_map, FixedArray::kLengthOffset, |
| 108 parameter_map_count); |
| 109 } else { |
| 110 if (empty) { |
| 111 StoreObjectFieldNoWriteBarrier(result, JSArray::kElementsOffset, |
| 112 empty_fixed_array); |
| 113 } else { |
| 114 StoreObjectFieldNoWriteBarrier(result, JSArray::kElementsOffset, |
| 115 arguments); |
| 116 } |
| 117 } |
| 118 return std::tuple<Node*, Node*, Node*>(result, arguments, parameter_map); |
| 119 } |
| 120 |
| 121 Node* ArgumentsBuiltinsAssembler::ConstructParametersObjectFromArgs( |
| 122 Node* map, Node* frame_ptr, Node* arg_count, Node* first_arg, |
| 123 Node* rest_count, ParameterMode param_mode, int base_size) { |
| 124 // Allocate the parameter object (either a Rest parameter object, a strict |
| 125 // argument object or a sloppy arguments object) and the elements together and |
| 126 // fill in the contents with the arguments above |formal_parameter_count|. |
| 127 Node* result; |
| 128 Node* elements; |
| 129 Node* unused; |
| 130 std::tie(result, elements, unused) = |
| 131 AllocateArgumentsObject(map, rest_count, nullptr, param_mode, base_size); |
| 132 DCHECK(unused == nullptr); |
| 133 CodeStubArguments arguments(this, arg_count, frame_ptr, param_mode); |
| 134 Variable offset(this, MachineType::PointerRepresentation()); |
| 135 offset.Bind(IntPtrConstant(FixedArrayBase::kHeaderSize - kHeapObjectTag)); |
| 136 VariableList list({&offset}, zone()); |
| 137 arguments.ForEach(list, |
| 138 [this, elements, &offset](Node* arg) { |
| 139 StoreNoWriteBarrier(MachineRepresentation::kTagged, |
| 140 elements, offset.value(), arg); |
| 141 Increment(offset, kPointerSize); |
| 142 }, |
| 143 first_arg, nullptr, param_mode); |
| 144 return result; |
| 145 } |
| 146 |
| 147 Node* ArgumentsBuiltinsAssembler::EmitFastNewRestParameter(Node* context, |
| 148 Node* function) { |
| 149 Node* frame_ptr; |
| 150 Node* argument_count; |
| 151 Node* formal_parameter_count; |
| 152 |
| 153 ParameterMode mode = OptimalParameterMode(); |
| 154 Node* zero = IntPtrOrSmiConstant(0, mode); |
| 155 |
| 156 std::tie(frame_ptr, argument_count, formal_parameter_count) = |
| 157 GetArgumentsFrameAndCount(function, mode); |
| 158 |
| 159 Variable result(this, MachineRepresentation::kTagged); |
| 160 Label no_rest_parameters(this), runtime(this, Label::kDeferred), |
| 161 done(this, &result); |
| 162 |
| 163 Node* rest_count = |
| 164 IntPtrOrSmiSub(argument_count, formal_parameter_count, mode); |
| 165 Node* const native_context = LoadNativeContext(context); |
| 166 Node* const array_map = LoadJSArrayElementsMap(FAST_ELEMENTS, native_context); |
| 167 GotoIf(IntPtrOrSmiLessThanOrEqual(rest_count, zero, mode), |
| 168 &no_rest_parameters); |
| 169 |
| 170 GotoIfFixedArraySizeDoesntFitInNewSpace( |
| 171 rest_count, &runtime, JSArray::kSize + FixedArray::kHeaderSize, mode); |
| 172 |
| 173 // Allocate the Rest JSArray and the elements together and fill in the |
| 174 // contents with the arguments above |formal_parameter_count|. |
| 175 result.Bind(ConstructParametersObjectFromArgs( |
| 176 array_map, frame_ptr, argument_count, formal_parameter_count, rest_count, |
| 177 mode, JSArray::kSize)); |
| 178 Goto(&done); |
| 179 |
| 180 Bind(&no_rest_parameters); |
| 181 { |
| 182 Node* arguments; |
| 183 Node* elements; |
| 184 Node* unused; |
| 185 std::tie(arguments, elements, unused) = |
| 186 AllocateArgumentsObject(array_map, zero, nullptr, mode, JSArray::kSize); |
| 187 result.Bind(arguments); |
| 188 Goto(&done); |
| 189 } |
| 190 |
| 191 Bind(&runtime); |
| 192 { |
| 193 result.Bind(CallRuntime(Runtime::kNewRestParameter, context, function)); |
| 194 Goto(&done); |
| 195 } |
| 196 |
| 197 Bind(&done); |
| 198 return result.value(); |
| 199 } |
| 200 |
| 201 TF_BUILTIN(FastNewRestParameter, ArgumentsBuiltinsAssembler) { |
| 202 Node* function = Parameter(FastNewArgumentsDescriptor::kFunction); |
| 203 Node* context = Parameter(FastNewArgumentsDescriptor::kContext); |
| 204 Return(EmitFastNewRestParameter(context, function)); |
| 205 } |
| 206 |
| 207 Node* ArgumentsBuiltinsAssembler::EmitFastNewStrictArguments(Node* context, |
| 208 Node* function) { |
| 209 Variable result(this, MachineRepresentation::kTagged); |
| 210 Label done(this, &result), empty(this), runtime(this, Label::kDeferred); |
| 211 |
| 212 Node* frame_ptr; |
| 213 Node* argument_count; |
| 214 Node* formal_parameter_count; |
| 215 |
| 216 ParameterMode mode = OptimalParameterMode(); |
| 217 Node* zero = IntPtrOrSmiConstant(0, mode); |
| 218 |
| 219 std::tie(frame_ptr, argument_count, formal_parameter_count) = |
| 220 GetArgumentsFrameAndCount(function, mode); |
| 221 |
| 222 GotoIfFixedArraySizeDoesntFitInNewSpace( |
| 223 argument_count, &runtime, |
| 224 JSStrictArgumentsObject::kSize + FixedArray::kHeaderSize, mode); |
| 225 |
| 226 Node* const native_context = LoadNativeContext(context); |
| 227 Node* const map = |
| 228 LoadContextElement(native_context, Context::STRICT_ARGUMENTS_MAP_INDEX); |
| 229 GotoIf(WordEqual(argument_count, zero), &empty); |
| 230 |
| 231 result.Bind(ConstructParametersObjectFromArgs( |
| 232 map, frame_ptr, argument_count, zero, argument_count, mode, |
| 233 JSStrictArgumentsObject::kSize)); |
| 234 Goto(&done); |
| 235 |
| 236 Bind(&empty); |
| 237 { |
| 238 Node* arguments; |
| 239 Node* elements; |
| 240 Node* unused; |
| 241 std::tie(arguments, elements, unused) = AllocateArgumentsObject( |
| 242 map, zero, nullptr, mode, JSStrictArgumentsObject::kSize); |
| 243 result.Bind(arguments); |
| 244 Goto(&done); |
| 245 } |
| 246 |
| 247 Bind(&runtime); |
| 248 { |
| 249 result.Bind(CallRuntime(Runtime::kNewStrictArguments, context, function)); |
| 250 Goto(&done); |
| 251 } |
| 252 |
| 253 Bind(&done); |
| 254 return result.value(); |
| 255 } |
| 256 |
| 257 TF_BUILTIN(FastNewStrictArguments, ArgumentsBuiltinsAssembler) { |
| 258 Node* function = Parameter(FastNewArgumentsDescriptor::kFunction); |
| 259 Node* context = Parameter(FastNewArgumentsDescriptor::kContext); |
| 260 Return(EmitFastNewStrictArguments(context, function)); |
| 261 } |
| 262 |
| 263 Node* ArgumentsBuiltinsAssembler::EmitFastNewSloppyArguments(Node* context, |
| 264 Node* function) { |
| 265 Node* frame_ptr; |
| 266 Node* argument_count; |
| 267 Node* formal_parameter_count; |
| 268 Variable result(this, MachineRepresentation::kTagged); |
| 269 |
| 270 ParameterMode mode = OptimalParameterMode(); |
| 271 Node* zero = IntPtrOrSmiConstant(0, mode); |
| 272 |
| 273 Label done(this, &result), empty(this), no_parameters(this), |
| 274 runtime(this, Label::kDeferred); |
| 275 |
| 276 std::tie(frame_ptr, argument_count, formal_parameter_count) = |
| 277 GetArgumentsFrameAndCount(function, mode); |
| 278 |
| 279 GotoIf(WordEqual(argument_count, zero), &empty); |
| 280 |
| 281 GotoIf(WordEqual(formal_parameter_count, zero), &no_parameters); |
| 282 |
| 283 { |
| 284 Comment("Mapped parameter JSSloppyArgumentsObject"); |
| 285 |
| 286 Node* mapped_count = |
| 287 IntPtrOrSmiMin(argument_count, formal_parameter_count, mode); |
| 288 |
| 289 Node* parameter_map_size = |
| 290 IntPtrOrSmiAdd(mapped_count, IntPtrOrSmiConstant(2, mode), mode); |
| 291 |
| 292 // Verify that the overall allocation will fit in new space. |
| 293 Node* elements_allocated = |
| 294 IntPtrOrSmiAdd(argument_count, parameter_map_size, mode); |
| 295 GotoIfFixedArraySizeDoesntFitInNewSpace( |
| 296 elements_allocated, &runtime, |
| 297 JSSloppyArgumentsObject::kSize + FixedArray::kHeaderSize * 2, mode); |
| 298 |
| 299 Node* const native_context = LoadNativeContext(context); |
| 300 Node* const map = LoadContextElement( |
| 301 native_context, Context::FAST_ALIASED_ARGUMENTS_MAP_INDEX); |
| 302 Node* argument_object; |
| 303 Node* elements; |
| 304 Node* map_array; |
| 305 std::tie(argument_object, elements, map_array) = |
| 306 AllocateArgumentsObject(map, argument_count, parameter_map_size, mode, |
| 307 JSSloppyArgumentsObject::kSize); |
| 308 StoreObjectFieldNoWriteBarrier( |
| 309 argument_object, JSSloppyArgumentsObject::kCalleeOffset, function); |
| 310 StoreFixedArrayElement(map_array, 0, context, SKIP_WRITE_BARRIER); |
| 311 StoreFixedArrayElement(map_array, 1, elements, SKIP_WRITE_BARRIER); |
| 312 |
| 313 Comment("Fill in non-mapped parameters"); |
| 314 Node* argument_offset = |
| 315 ElementOffsetFromIndex(argument_count, FAST_ELEMENTS, mode, |
| 316 FixedArray::kHeaderSize - kHeapObjectTag); |
| 317 Node* mapped_offset = |
| 318 ElementOffsetFromIndex(mapped_count, FAST_ELEMENTS, mode, |
| 319 FixedArray::kHeaderSize - kHeapObjectTag); |
| 320 CodeStubArguments arguments(this, argument_count, frame_ptr, mode); |
| 321 Variable current_argument(this, MachineType::PointerRepresentation()); |
| 322 current_argument.Bind(arguments.AtIndexPtr(argument_count, mode)); |
| 323 VariableList var_list1({¤t_argument}, zone()); |
| 324 mapped_offset = BuildFastLoop( |
| 325 var_list1, argument_offset, mapped_offset, |
| 326 [this, elements, ¤t_argument](Node* offset) { |
| 327 Increment(current_argument, kPointerSize); |
| 328 Node* arg = LoadBufferObject(current_argument.value(), 0); |
| 329 StoreNoWriteBarrier(MachineRepresentation::kTagged, elements, offset, |
| 330 arg); |
| 331 }, |
| 332 -kPointerSize, INTPTR_PARAMETERS); |
| 333 |
| 334 // Copy the parameter slots and the holes in the arguments. |
| 335 // We need to fill in mapped_count slots. They index the context, |
| 336 // where parameters are stored in reverse order, at |
| 337 // MIN_CONTEXT_SLOTS .. MIN_CONTEXT_SLOTS+argument_count-1 |
| 338 // The mapped parameter thus need to get indices |
| 339 // MIN_CONTEXT_SLOTS+parameter_count-1 .. |
| 340 // MIN_CONTEXT_SLOTS+argument_count-mapped_count |
| 341 // We loop from right to left. |
| 342 Comment("Fill in mapped parameters"); |
| 343 Variable context_index(this, OptimalParameterRepresentation()); |
| 344 context_index.Bind(IntPtrOrSmiSub( |
| 345 IntPtrOrSmiAdd(IntPtrOrSmiConstant(Context::MIN_CONTEXT_SLOTS, mode), |
| 346 formal_parameter_count, mode), |
| 347 mapped_count, mode)); |
| 348 Node* the_hole = TheHoleConstant(); |
| 349 VariableList var_list2({&context_index}, zone()); |
| 350 const int kParameterMapHeaderSize = |
| 351 FixedArray::kHeaderSize + 2 * kPointerSize; |
| 352 Node* adjusted_map_array = IntPtrAdd( |
| 353 BitcastTaggedToWord(map_array), |
| 354 IntPtrConstant(kParameterMapHeaderSize - FixedArray::kHeaderSize)); |
| 355 Node* zero_offset = ElementOffsetFromIndex( |
| 356 zero, FAST_ELEMENTS, mode, FixedArray::kHeaderSize - kHeapObjectTag); |
| 357 BuildFastLoop(var_list2, mapped_offset, zero_offset, |
| 358 [this, the_hole, elements, adjusted_map_array, &context_index, |
| 359 mode](Node* offset) { |
| 360 StoreNoWriteBarrier(MachineRepresentation::kTagged, |
| 361 elements, offset, the_hole); |
| 362 StoreNoWriteBarrier( |
| 363 MachineRepresentation::kTagged, adjusted_map_array, |
| 364 offset, ParameterToTagged(context_index.value(), mode)); |
| 365 Increment(context_index, 1, mode); |
| 366 }, |
| 367 -kPointerSize, INTPTR_PARAMETERS); |
| 368 |
| 369 result.Bind(argument_object); |
| 370 Goto(&done); |
| 371 } |
| 372 |
| 373 Bind(&no_parameters); |
| 374 { |
| 375 Comment("No parameters JSSloppyArgumentsObject"); |
| 376 GotoIfFixedArraySizeDoesntFitInNewSpace( |
| 377 argument_count, &runtime, |
| 378 JSSloppyArgumentsObject::kSize + FixedArray::kHeaderSize, mode); |
| 379 Node* const native_context = LoadNativeContext(context); |
| 380 Node* const map = |
| 381 LoadContextElement(native_context, Context::SLOPPY_ARGUMENTS_MAP_INDEX); |
| 382 result.Bind(ConstructParametersObjectFromArgs( |
| 383 map, frame_ptr, argument_count, zero, argument_count, mode, |
| 384 JSSloppyArgumentsObject::kSize)); |
| 385 StoreObjectFieldNoWriteBarrier( |
| 386 result.value(), JSSloppyArgumentsObject::kCalleeOffset, function); |
| 387 Goto(&done); |
| 388 } |
| 389 |
| 390 Bind(&empty); |
| 391 { |
| 392 Comment("Empty JSSloppyArgumentsObject"); |
| 393 Node* const native_context = LoadNativeContext(context); |
| 394 Node* const map = |
| 395 LoadContextElement(native_context, Context::SLOPPY_ARGUMENTS_MAP_INDEX); |
| 396 Node* arguments; |
| 397 Node* elements; |
| 398 Node* unused; |
| 399 std::tie(arguments, elements, unused) = AllocateArgumentsObject( |
| 400 map, zero, nullptr, mode, JSSloppyArgumentsObject::kSize); |
| 401 result.Bind(arguments); |
| 402 StoreObjectFieldNoWriteBarrier( |
| 403 result.value(), JSSloppyArgumentsObject::kCalleeOffset, function); |
| 404 Goto(&done); |
| 405 } |
| 406 |
| 407 Bind(&runtime); |
| 408 { |
| 409 result.Bind(CallRuntime(Runtime::kNewSloppyArguments, context, function)); |
| 410 Goto(&done); |
| 411 } |
| 412 |
| 413 Bind(&done); |
| 414 return result.value(); |
| 415 } |
| 416 |
| 417 TF_BUILTIN(FastNewSloppyArguments, ArgumentsBuiltinsAssembler) { |
| 418 Node* function = Parameter(FastNewArgumentsDescriptor::kFunction); |
| 419 Node* context = Parameter(FastNewArgumentsDescriptor::kContext); |
| 420 Return(EmitFastNewSloppyArguments(context, function)); |
| 421 } |
| 422 |
| 423 } // namespace internal |
| 424 } // namespace v8 |
OLD | NEW |