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-call-gen.h"
6 :
7 : #include "src/builtins/builtins-utils-gen.h"
8 : #include "src/builtins/builtins.h"
9 : #include "src/globals.h"
10 : #include "src/heap/heap-inl.h"
11 : #include "src/isolate.h"
12 : #include "src/macro-assembler.h"
13 : #include "src/objects/arguments.h"
14 :
15 : namespace v8 {
16 : namespace internal {
17 :
18 31 : void Builtins::Generate_CallFunction_ReceiverIsNullOrUndefined(
19 : MacroAssembler* masm) {
20 31 : Generate_CallFunction(masm, ConvertReceiverMode::kNullOrUndefined);
21 31 : }
22 :
23 31 : void Builtins::Generate_CallFunction_ReceiverIsNotNullOrUndefined(
24 : MacroAssembler* masm) {
25 31 : Generate_CallFunction(masm, ConvertReceiverMode::kNotNullOrUndefined);
26 31 : }
27 :
28 31 : void Builtins::Generate_CallFunction_ReceiverIsAny(MacroAssembler* masm) {
29 31 : Generate_CallFunction(masm, ConvertReceiverMode::kAny);
30 31 : }
31 :
32 31 : void Builtins::Generate_CallBoundFunction(MacroAssembler* masm) {
33 31 : Generate_CallBoundFunctionImpl(masm);
34 31 : }
35 :
36 31 : void Builtins::Generate_Call_ReceiverIsNullOrUndefined(MacroAssembler* masm) {
37 31 : Generate_Call(masm, ConvertReceiverMode::kNullOrUndefined);
38 31 : }
39 :
40 31 : void Builtins::Generate_Call_ReceiverIsNotNullOrUndefined(
41 : MacroAssembler* masm) {
42 31 : Generate_Call(masm, ConvertReceiverMode::kNotNullOrUndefined);
43 31 : }
44 :
45 31 : void Builtins::Generate_Call_ReceiverIsAny(MacroAssembler* masm) {
46 31 : Generate_Call(masm, ConvertReceiverMode::kAny);
47 31 : }
48 :
49 31 : void Builtins::Generate_CallVarargs(MacroAssembler* masm) {
50 31 : Generate_CallOrConstructVarargs(masm, masm->isolate()->builtins()->Call());
51 31 : }
52 :
53 31 : void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm) {
54 : Generate_CallOrConstructForwardVarargs(masm, CallOrConstructMode::kCall,
55 31 : masm->isolate()->builtins()->Call());
56 31 : }
57 :
58 31 : void Builtins::Generate_CallFunctionForwardVarargs(MacroAssembler* masm) {
59 : Generate_CallOrConstructForwardVarargs(
60 : masm, CallOrConstructMode::kCall,
61 31 : masm->isolate()->builtins()->CallFunction());
62 31 : }
63 :
64 62 : void CallOrConstructBuiltinsAssembler::CallOrConstructWithArrayLike(
65 : Node* target, Node* new_target, Node* arguments_list, Node* context) {
66 62 : VARIABLE(var_elements, MachineRepresentation::kTagged);
67 124 : VARIABLE(var_length, MachineRepresentation::kWord32);
68 62 : Label if_done(this), if_arguments(this), if_array(this),
69 62 : if_holey_array(this, Label::kDeferred),
70 62 : if_runtime(this, Label::kDeferred);
71 :
72 : // Perform appropriate checks on {target} (and {new_target} first).
73 62 : if (new_target == nullptr) {
74 : // Check that {target} is Callable.
75 : Label if_target_callable(this),
76 31 : if_target_not_callable(this, Label::kDeferred);
77 62 : GotoIf(TaggedIsSmi(target), &if_target_not_callable);
78 62 : Branch(IsCallable(target), &if_target_callable, &if_target_not_callable);
79 31 : BIND(&if_target_not_callable);
80 : {
81 : CallRuntime(Runtime::kThrowApplyNonFunction, context, target);
82 31 : Unreachable();
83 : }
84 62 : BIND(&if_target_callable);
85 : } else {
86 : // Check that {target} is a Constructor.
87 : Label if_target_constructor(this),
88 31 : if_target_not_constructor(this, Label::kDeferred);
89 62 : GotoIf(TaggedIsSmi(target), &if_target_not_constructor);
90 : Branch(IsConstructor(target), &if_target_constructor,
91 62 : &if_target_not_constructor);
92 31 : BIND(&if_target_not_constructor);
93 : {
94 : CallRuntime(Runtime::kThrowNotConstructor, context, target);
95 31 : Unreachable();
96 : }
97 31 : BIND(&if_target_constructor);
98 :
99 : // Check that {new_target} is a Constructor.
100 31 : Label if_new_target_constructor(this),
101 31 : if_new_target_not_constructor(this, Label::kDeferred);
102 62 : GotoIf(TaggedIsSmi(new_target), &if_new_target_not_constructor);
103 : Branch(IsConstructor(new_target), &if_new_target_constructor,
104 62 : &if_new_target_not_constructor);
105 31 : BIND(&if_new_target_not_constructor);
106 : {
107 : CallRuntime(Runtime::kThrowNotConstructor, context, new_target);
108 31 : Unreachable();
109 : }
110 62 : BIND(&if_new_target_constructor);
111 : }
112 :
113 124 : GotoIf(TaggedIsSmi(arguments_list), &if_runtime);
114 124 : Node* arguments_list_map = LoadMap(arguments_list);
115 124 : Node* native_context = LoadNativeContext(context);
116 :
117 : // Check if {arguments_list} is an (unmodified) arguments object.
118 : Node* sloppy_arguments_map =
119 124 : LoadContextElement(native_context, Context::SLOPPY_ARGUMENTS_MAP_INDEX);
120 124 : GotoIf(WordEqual(arguments_list_map, sloppy_arguments_map), &if_arguments);
121 : Node* strict_arguments_map =
122 124 : LoadContextElement(native_context, Context::STRICT_ARGUMENTS_MAP_INDEX);
123 124 : GotoIf(WordEqual(arguments_list_map, strict_arguments_map), &if_arguments);
124 :
125 : // Check if {arguments_list} is a fast JSArray.
126 124 : Branch(IsJSArrayMap(arguments_list_map), &if_array, &if_runtime);
127 :
128 62 : BIND(&if_array);
129 : {
130 : // Try to extract the elements from a JSArray object.
131 : var_elements.Bind(
132 62 : LoadObjectField(arguments_list, JSArray::kElementsOffset));
133 : var_length.Bind(LoadAndUntagToWord32ObjectField(arguments_list,
134 124 : JSArray::kLengthOffset));
135 :
136 : // Holey arrays and double backing stores need special treatment.
137 : STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
138 : STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
139 : STATIC_ASSERT(PACKED_ELEMENTS == 2);
140 : STATIC_ASSERT(HOLEY_ELEMENTS == 3);
141 : STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS == 4);
142 : STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == 5);
143 : STATIC_ASSERT(LAST_FAST_ELEMENTS_KIND == HOLEY_DOUBLE_ELEMENTS);
144 :
145 124 : Node* kind = LoadMapElementsKind(arguments_list_map);
146 :
147 124 : GotoIf(Int32GreaterThan(kind, Int32Constant(LAST_FAST_ELEMENTS_KIND)),
148 124 : &if_runtime);
149 186 : Branch(Word32And(kind, Int32Constant(1)), &if_holey_array, &if_done);
150 : }
151 :
152 62 : BIND(&if_holey_array);
153 : {
154 : // For holey JSArrays we need to check that the array prototype chain
155 : // protector is intact and our prototype is the Array.prototype actually.
156 : GotoIfNot(IsPrototypeInitialArrayPrototype(context, arguments_list_map),
157 124 : &if_runtime);
158 124 : Branch(IsArrayProtectorCellInvalid(), &if_runtime, &if_done);
159 : }
160 :
161 62 : BIND(&if_arguments);
162 : {
163 : // Try to extract the elements from an JSArgumentsObject.
164 : Node* length =
165 : LoadObjectField(arguments_list, JSArgumentsObject::kLengthOffset);
166 : Node* elements =
167 : LoadObjectField(arguments_list, JSArgumentsObject::kElementsOffset);
168 124 : Node* elements_length = LoadFixedArrayBaseLength(elements);
169 124 : GotoIfNot(WordEqual(length, elements_length), &if_runtime);
170 62 : var_elements.Bind(elements);
171 124 : var_length.Bind(SmiToWord32(length));
172 62 : Goto(&if_done);
173 : }
174 :
175 62 : BIND(&if_runtime);
176 : {
177 : // Ask the runtime to create the list (actually a FixedArray).
178 : Node* elements =
179 : CallRuntime(Runtime::kCreateListFromArrayLike, context, arguments_list);
180 62 : var_elements.Bind(elements);
181 : var_length.Bind(
182 124 : LoadAndUntagToWord32ObjectField(elements, FixedArray::kLengthOffset));
183 62 : Goto(&if_done);
184 : }
185 :
186 : // Tail call to the appropriate builtin (depending on whether we have
187 : // a {new_target} passed).
188 62 : BIND(&if_done);
189 : {
190 62 : Label if_not_double(this), if_double(this);
191 62 : Node* elements = var_elements.value();
192 62 : Node* length = var_length.value();
193 124 : Node* args_count = Int32Constant(0); // args already on the stack
194 :
195 124 : Branch(IsFixedDoubleArray(elements), &if_double, &if_not_double);
196 :
197 62 : BIND(&if_not_double);
198 62 : if (new_target == nullptr) {
199 31 : Callable callable = CodeFactory::CallVarargs(isolate());
200 31 : TailCallStub(callable, context, target, args_count, elements, length);
201 : } else {
202 31 : Callable callable = CodeFactory::ConstructVarargs(isolate());
203 : TailCallStub(callable, context, target, new_target, args_count, elements,
204 31 : length);
205 : }
206 :
207 62 : BIND(&if_double);
208 : {
209 : // Kind is hardcoded here because CreateListFromArrayLike will only
210 : // produce holey double arrays.
211 : CallOrConstructDoubleVarargs(target, new_target, elements, length,
212 : args_count, context,
213 124 : Int32Constant(HOLEY_DOUBLE_ELEMENTS));
214 62 : }
215 62 : }
216 62 : }
217 :
218 : // Takes a FixedArray of doubles and creates a new FixedArray with those doubles
219 : // boxed as HeapNumbers, then tail calls CallVarargs/ConstructVarargs depending
220 : // on whether {new_target} was passed.
221 124 : void CallOrConstructBuiltinsAssembler::CallOrConstructDoubleVarargs(
222 : Node* target, Node* new_target, Node* elements, Node* length,
223 : Node* args_count, Node* context, Node* kind) {
224 248 : Label if_holey_double(this), if_packed_double(this), if_done(this);
225 :
226 : const ElementsKind new_kind = PACKED_ELEMENTS;
227 : const ParameterMode mode = INTPTR_PARAMETERS;
228 : const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER;
229 248 : Node* intptr_length = ChangeInt32ToIntPtr(length);
230 :
231 : // Allocate a new FixedArray of Objects.
232 : Node* new_elements =
233 : AllocateFixedArray(new_kind, intptr_length, mode,
234 124 : CodeStubAssembler::kAllowLargeObjectAllocation);
235 248 : Branch(Word32Equal(kind, Int32Constant(HOLEY_DOUBLE_ELEMENTS)),
236 248 : &if_holey_double, &if_packed_double);
237 :
238 124 : BIND(&if_holey_double);
239 : {
240 : // Fill the FixedArray with pointers to HeapObjects.
241 : CopyFixedArrayElements(HOLEY_DOUBLE_ELEMENTS, elements, new_kind,
242 : new_elements, intptr_length, intptr_length,
243 124 : barrier_mode);
244 124 : Goto(&if_done);
245 : }
246 :
247 124 : BIND(&if_packed_double);
248 : {
249 : CopyFixedArrayElements(PACKED_DOUBLE_ELEMENTS, elements, new_kind,
250 : new_elements, intptr_length, intptr_length,
251 124 : barrier_mode);
252 124 : Goto(&if_done);
253 : }
254 :
255 124 : BIND(&if_done);
256 : {
257 124 : if (new_target == nullptr) {
258 62 : Callable callable = CodeFactory::CallVarargs(isolate());
259 62 : TailCallStub(callable, context, target, args_count, new_elements, length);
260 : } else {
261 62 : Callable callable = CodeFactory::ConstructVarargs(isolate());
262 : TailCallStub(callable, context, target, new_target, args_count,
263 62 : new_elements, length);
264 : }
265 124 : }
266 124 : }
267 :
268 62 : void CallOrConstructBuiltinsAssembler::CallOrConstructWithSpread(
269 : Node* target, Node* new_target, Node* spread, Node* args_count,
270 : Node* context) {
271 124 : Label if_done(this), if_holey(this), if_runtime(this, Label::kDeferred);
272 :
273 124 : VARIABLE(spread_result, MachineRepresentation::kTagged, spread);
274 :
275 124 : GotoIf(TaggedIsSmi(spread), &if_runtime);
276 124 : Node* spread_map = LoadMap(spread);
277 124 : GotoIfNot(IsJSArrayMap(spread_map), &if_runtime);
278 :
279 : // Check that we have the original ArrayPrototype.
280 124 : GotoIfNot(IsPrototypeInitialArrayPrototype(context, spread_map), &if_runtime);
281 :
282 : // Check that the ArrayPrototype hasn't been modified in a way that would
283 : // affect iteration.
284 124 : Node* protector_cell = LoadRoot(Heap::kArrayIteratorProtectorRootIndex);
285 : DCHECK(isolate()->heap()->array_iterator_protector()->IsPropertyCell());
286 : GotoIfNot(
287 : WordEqual(LoadObjectField(protector_cell, PropertyCell::kValueOffset),
288 62 : SmiConstant(Isolate::kProtectorValid)),
289 62 : &if_runtime);
290 :
291 : // Check that the map of the initial array iterator hasn't changed.
292 124 : Node* native_context = LoadNativeContext(context);
293 124 : Node* arr_it_proto_map = LoadMap(CAST(LoadContextElement(
294 124 : native_context, Context::INITIAL_ARRAY_ITERATOR_PROTOTYPE_INDEX)));
295 : Node* initial_map = LoadContextElement(
296 124 : native_context, Context::INITIAL_ARRAY_ITERATOR_PROTOTYPE_MAP_INDEX);
297 124 : GotoIfNot(WordEqual(arr_it_proto_map, initial_map), &if_runtime);
298 :
299 124 : Node* kind = LoadMapElementsKind(spread_map);
300 :
301 : STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
302 : STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
303 : STATIC_ASSERT(PACKED_ELEMENTS == 2);
304 : STATIC_ASSERT(HOLEY_ELEMENTS == 3);
305 : STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS == 4);
306 : STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == 5);
307 : STATIC_ASSERT(LAST_FAST_ELEMENTS_KIND == HOLEY_DOUBLE_ELEMENTS);
308 :
309 124 : GotoIf(Int32GreaterThan(kind, Int32Constant(LAST_FAST_ELEMENTS_KIND)),
310 124 : &if_runtime);
311 186 : Branch(Word32And(kind, Int32Constant(1)), &if_holey, &if_done);
312 :
313 : // Check the ArrayProtector cell for holey arrays.
314 62 : BIND(&if_holey);
315 124 : { Branch(IsArrayProtectorCellInvalid(), &if_runtime, &if_done); }
316 :
317 62 : BIND(&if_runtime);
318 : {
319 62 : Node* spread_iterable = LoadContextElement(LoadNativeContext(context),
320 186 : Context::SPREAD_ITERABLE_INDEX);
321 : spread_result.Bind(CallJS(CodeFactory::Call(isolate()), context,
322 186 : spread_iterable, UndefinedConstant(), spread));
323 : CSA_ASSERT(this, IsJSArray(spread_result.value()));
324 62 : Goto(&if_done);
325 : }
326 :
327 62 : BIND(&if_done);
328 : {
329 : // The result from if_runtime can be an array of doubles.
330 62 : Label if_not_double(this), if_double(this);
331 : Node* elements =
332 62 : LoadObjectField(spread_result.value(), JSArray::kElementsOffset);
333 : Node* length = LoadAndUntagToWord32ObjectField(spread_result.value(),
334 124 : JSArray::kLengthOffset);
335 :
336 186 : Node* kind = LoadMapElementsKind(LoadMap(elements));
337 : CSA_ASSERT(this, Int32LessThanOrEqual(
338 : kind, Int32Constant(LAST_FAST_ELEMENTS_KIND)));
339 :
340 124 : Branch(Int32GreaterThan(kind, Int32Constant(HOLEY_ELEMENTS)), &if_double,
341 62 : &if_not_double);
342 :
343 62 : BIND(&if_not_double);
344 : {
345 62 : if (new_target == nullptr) {
346 31 : Callable callable = CodeFactory::CallVarargs(isolate());
347 31 : TailCallStub(callable, context, target, args_count, elements, length);
348 : } else {
349 31 : Callable callable = CodeFactory::ConstructVarargs(isolate());
350 : TailCallStub(callable, context, target, new_target, args_count,
351 31 : elements, length);
352 : }
353 : }
354 :
355 62 : BIND(&if_double);
356 : {
357 : CallOrConstructDoubleVarargs(target, new_target, elements, length,
358 62 : args_count, context, kind);
359 62 : }
360 62 : }
361 62 : }
362 :
363 124 : TF_BUILTIN(CallWithArrayLike, CallOrConstructBuiltinsAssembler) {
364 : Node* target = Parameter(CallWithArrayLikeDescriptor::kTarget);
365 : Node* new_target = nullptr;
366 : Node* arguments_list = Parameter(CallWithArrayLikeDescriptor::kArgumentsList);
367 : Node* context = Parameter(CallWithArrayLikeDescriptor::kContext);
368 31 : CallOrConstructWithArrayLike(target, new_target, arguments_list, context);
369 31 : }
370 :
371 124 : TF_BUILTIN(CallWithSpread, CallOrConstructBuiltinsAssembler) {
372 : Node* target = Parameter(CallWithSpreadDescriptor::kTarget);
373 : Node* new_target = nullptr;
374 : Node* spread = Parameter(CallWithSpreadDescriptor::kSpread);
375 : Node* args_count = Parameter(CallWithSpreadDescriptor::kArgumentsCount);
376 : Node* context = Parameter(CallWithSpreadDescriptor::kContext);
377 31 : CallOrConstructWithSpread(target, new_target, spread, args_count, context);
378 31 : }
379 :
380 : } // namespace internal
381 : } // namespace v8
|