• 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-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({&current_argument}, zone());
322     mapped_offset = BuildFastLoop(
323         var_list1, argument_offset, mapped_offset,
324         [this, elements, &current_argument](Node* offset) {
325           Increment(&current_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