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