• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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*>
GetArgumentsFrameAndCount(Node * function,ParameterMode mode)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*>
AllocateArgumentsObject(Node * map,Node * arguments_count,Node * parameter_map_count,ParameterMode mode,int base_size)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 
ConstructParametersObjectFromArgs(Node * map,Node * frame_ptr,Node * arg_count,Node * first_arg,Node * rest_count,ParameterMode param_mode,int base_size)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 
EmitFastNewRestParameter(Node * context,Node * function)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 
TF_BUILTIN(FastNewRestParameter,ArgumentsBuiltinsAssembler)202 TF_BUILTIN(FastNewRestParameter, ArgumentsBuiltinsAssembler) {
203   Node* function = Parameter(FastNewArgumentsDescriptor::kFunction);
204   Node* context = Parameter(FastNewArgumentsDescriptor::kContext);
205   Return(EmitFastNewRestParameter(context, function));
206 }
207 
EmitFastNewStrictArguments(Node * context,Node * function)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 
TF_BUILTIN(FastNewStrictArguments,ArgumentsBuiltinsAssembler)258 TF_BUILTIN(FastNewStrictArguments, ArgumentsBuiltinsAssembler) {
259   Node* function = Parameter(FastNewArgumentsDescriptor::kFunction);
260   Node* context = Parameter(FastNewArgumentsDescriptor::kContext);
261   Return(EmitFastNewStrictArguments(context, function));
262 }
263 
EmitFastNewSloppyArguments(Node * context,Node * function)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({&current_argument}, zone());
325     mapped_offset = BuildFastLoop(
326         var_list1, argument_offset, mapped_offset,
327         [this, elements, &current_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 
TF_BUILTIN(FastNewSloppyArguments,ArgumentsBuiltinsAssembler)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