OLD | NEW |
| (Empty) |
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*> | |
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*> | |
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 | |
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 | |
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 | |
202 TF_BUILTIN(FastNewRestParameter, ArgumentsBuiltinsAssembler) { | |
203 Node* function = Parameter(FastNewArgumentsDescriptor::kFunction); | |
204 Node* context = Parameter(FastNewArgumentsDescriptor::kContext); | |
205 Return(EmitFastNewRestParameter(context, function)); | |
206 } | |
207 | |
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 | |
258 TF_BUILTIN(FastNewStrictArguments, ArgumentsBuiltinsAssembler) { | |
259 Node* function = Parameter(FastNewArgumentsDescriptor::kFunction); | |
260 Node* context = Parameter(FastNewArgumentsDescriptor::kContext); | |
261 Return(EmitFastNewStrictArguments(context, function)); | |
262 } | |
263 | |
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({¤t_argument}, zone()); | |
325 mapped_offset = BuildFastLoop( | |
326 var_list1, argument_offset, mapped_offset, | |
327 [this, elements, ¤t_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 | |
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 | |
OLD | NEW |