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