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