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