blob: 54ef382a39196454b6a545fb5b59e3510af9b0c1 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include "v8.h"
29
30#include "codegen-inl.h"
31
32namespace v8 {
33namespace internal {
34
35
36#define __ ACCESS_MASM(masm)
37
38
Leon Clarkee46be812010-01-19 14:06:41 +000039void Builtins::Generate_Adaptor(MacroAssembler* masm,
40 CFunctionId id,
41 BuiltinExtraArguments extra_args) {
42 // ----------- S t a t e -------------
43 // -- eax : number of arguments excluding receiver
44 // -- edi : called function (only guaranteed when
45 // extra_args requires it)
46 // -- esi : context
47 // -- esp[0] : return address
48 // -- esp[4] : last argument
49 // -- ...
50 // -- esp[4 * argc] : first argument (argc == eax)
51 // -- esp[4 * (argc +1)] : receiver
52 // -----------------------------------
Steve Blocka7e24c12009-10-30 11:49:00 +000053
Leon Clarkee46be812010-01-19 14:06:41 +000054 // Insert extra arguments.
55 int num_extra_args = 0;
56 if (extra_args == NEEDS_CALLED_FUNCTION) {
57 num_extra_args = 1;
58 Register scratch = ebx;
59 __ pop(scratch); // Save return address.
60 __ push(edi);
61 __ push(scratch); // Restore return address.
62 } else {
63 ASSERT(extra_args == NO_EXTRA_ARGUMENTS);
64 }
65
66 // JumpToRuntime expects eax to contain the number of arguments
67 // including the receiver and the extra arguments.
68 __ add(Operand(eax), Immediate(num_extra_args + 1));
Steve Blocka7e24c12009-10-30 11:49:00 +000069 __ JumpToRuntime(ExternalReference(id));
70}
71
72
73void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
74 // ----------- S t a t e -------------
75 // -- eax: number of arguments
76 // -- edi: constructor function
77 // -----------------------------------
78
79 Label non_function_call;
80 // Check that function is not a smi.
81 __ test(edi, Immediate(kSmiTagMask));
82 __ j(zero, &non_function_call);
83 // Check that function is a JSFunction.
84 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
85 __ j(not_equal, &non_function_call);
86
87 // Jump to the function-specific construct stub.
88 __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
89 __ mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kConstructStubOffset));
90 __ lea(ebx, FieldOperand(ebx, Code::kHeaderSize));
91 __ jmp(Operand(ebx));
92
93 // edi: called object
94 // eax: number of arguments
95 __ bind(&non_function_call);
Andrei Popescu402d9372010-02-26 13:31:12 +000096 // CALL_NON_FUNCTION expects the non-function constructor as receiver
97 // (instead of the original receiver from the call site). The receiver is
98 // stack element argc+1.
99 __ mov(Operand(esp, eax, times_4, kPointerSize), edi);
Steve Blocka7e24c12009-10-30 11:49:00 +0000100 // Set expected number of arguments to zero (not changing eax).
101 __ Set(ebx, Immediate(0));
102 __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
103 __ jmp(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
104 RelocInfo::CODE_TARGET);
105}
106
107
Leon Clarkee46be812010-01-19 14:06:41 +0000108static void Generate_JSConstructStubHelper(MacroAssembler* masm,
109 bool is_api_function) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000110 // Enter a construct frame.
111 __ EnterConstructFrame();
112
113 // Store a smi-tagged arguments count on the stack.
Leon Clarkee46be812010-01-19 14:06:41 +0000114 __ SmiTag(eax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000115 __ push(eax);
116
117 // Push the function to invoke on the stack.
118 __ push(edi);
119
120 // Try to allocate the object without transitioning into C code. If any of the
121 // preconditions is not met, the code bails out to the runtime call.
122 Label rt_call, allocated;
123 if (FLAG_inline_new) {
124 Label undo_allocation;
125#ifdef ENABLE_DEBUGGER_SUPPORT
126 ExternalReference debug_step_in_fp =
127 ExternalReference::debug_step_in_fp_address();
128 __ cmp(Operand::StaticVariable(debug_step_in_fp), Immediate(0));
129 __ j(not_equal, &rt_call);
130#endif
131
132 // Verified that the constructor is a JSFunction.
133 // Load the initial map and verify that it is in fact a map.
134 // edi: constructor
135 __ mov(eax, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
136 // Will both indicate a NULL and a Smi
137 __ test(eax, Immediate(kSmiTagMask));
138 __ j(zero, &rt_call);
139 // edi: constructor
140 // eax: initial map (if proven valid below)
141 __ CmpObjectType(eax, MAP_TYPE, ebx);
142 __ j(not_equal, &rt_call);
143
144 // Check that the constructor is not constructing a JSFunction (see comments
145 // in Runtime_NewObject in runtime.cc). In which case the initial map's
146 // instance type would be JS_FUNCTION_TYPE.
147 // edi: constructor
148 // eax: initial map
149 __ CmpInstanceType(eax, JS_FUNCTION_TYPE);
150 __ j(equal, &rt_call);
151
152 // Now allocate the JSObject on the heap.
153 // edi: constructor
154 // eax: initial map
155 __ movzx_b(edi, FieldOperand(eax, Map::kInstanceSizeOffset));
156 __ shl(edi, kPointerSizeLog2);
157 __ AllocateInNewSpace(edi, ebx, edi, no_reg, &rt_call, NO_ALLOCATION_FLAGS);
158 // Allocated the JSObject, now initialize the fields.
159 // eax: initial map
160 // ebx: JSObject
161 // edi: start of next object
162 __ mov(Operand(ebx, JSObject::kMapOffset), eax);
163 __ mov(ecx, Factory::empty_fixed_array());
164 __ mov(Operand(ebx, JSObject::kPropertiesOffset), ecx);
165 __ mov(Operand(ebx, JSObject::kElementsOffset), ecx);
166 // Set extra fields in the newly allocated object.
167 // eax: initial map
168 // ebx: JSObject
169 // edi: start of next object
170 { Label loop, entry;
171 __ mov(edx, Factory::undefined_value());
172 __ lea(ecx, Operand(ebx, JSObject::kHeaderSize));
173 __ jmp(&entry);
174 __ bind(&loop);
175 __ mov(Operand(ecx, 0), edx);
176 __ add(Operand(ecx), Immediate(kPointerSize));
177 __ bind(&entry);
178 __ cmp(ecx, Operand(edi));
179 __ j(less, &loop);
180 }
181
182 // Add the object tag to make the JSObject real, so that we can continue and
183 // jump into the continuation code at any time from now on. Any failures
184 // need to undo the allocation, so that the heap is in a consistent state
185 // and verifiable.
186 // eax: initial map
187 // ebx: JSObject
188 // edi: start of next object
189 __ or_(Operand(ebx), Immediate(kHeapObjectTag));
190
191 // Check if a non-empty properties array is needed.
192 // Allocate and initialize a FixedArray if it is.
193 // eax: initial map
194 // ebx: JSObject
195 // edi: start of next object
196 // Calculate the total number of properties described by the map.
197 __ movzx_b(edx, FieldOperand(eax, Map::kUnusedPropertyFieldsOffset));
198 __ movzx_b(ecx, FieldOperand(eax, Map::kPreAllocatedPropertyFieldsOffset));
199 __ add(edx, Operand(ecx));
200 // Calculate unused properties past the end of the in-object properties.
201 __ movzx_b(ecx, FieldOperand(eax, Map::kInObjectPropertiesOffset));
202 __ sub(edx, Operand(ecx));
203 // Done if no extra properties are to be allocated.
204 __ j(zero, &allocated);
205 __ Assert(positive, "Property allocation count failed.");
206
207 // Scale the number of elements by pointer size and add the header for
208 // FixedArrays to the start of the next object calculation from above.
209 // ebx: JSObject
210 // edi: start of next object (will be start of FixedArray)
211 // edx: number of elements in properties array
212 __ AllocateInNewSpace(FixedArray::kHeaderSize,
213 times_pointer_size,
214 edx,
215 edi,
216 ecx,
217 no_reg,
218 &undo_allocation,
219 RESULT_CONTAINS_TOP);
220
221 // Initialize the FixedArray.
222 // ebx: JSObject
223 // edi: FixedArray
224 // edx: number of elements
225 // ecx: start of next object
226 __ mov(eax, Factory::fixed_array_map());
227 __ mov(Operand(edi, JSObject::kMapOffset), eax); // setup the map
228 __ mov(Operand(edi, Array::kLengthOffset), edx); // and length
229
230 // Initialize the fields to undefined.
231 // ebx: JSObject
232 // edi: FixedArray
233 // ecx: start of next object
234 { Label loop, entry;
235 __ mov(edx, Factory::undefined_value());
236 __ lea(eax, Operand(edi, FixedArray::kHeaderSize));
237 __ jmp(&entry);
238 __ bind(&loop);
239 __ mov(Operand(eax, 0), edx);
240 __ add(Operand(eax), Immediate(kPointerSize));
241 __ bind(&entry);
242 __ cmp(eax, Operand(ecx));
243 __ j(below, &loop);
244 }
245
246 // Store the initialized FixedArray into the properties field of
247 // the JSObject
248 // ebx: JSObject
249 // edi: FixedArray
250 __ or_(Operand(edi), Immediate(kHeapObjectTag)); // add the heap tag
251 __ mov(FieldOperand(ebx, JSObject::kPropertiesOffset), edi);
252
253
254 // Continue with JSObject being successfully allocated
255 // ebx: JSObject
256 __ jmp(&allocated);
257
258 // Undo the setting of the new top so that the heap is verifiable. For
259 // example, the map's unused properties potentially do not match the
260 // allocated objects unused properties.
261 // ebx: JSObject (previous new top)
262 __ bind(&undo_allocation);
263 __ UndoAllocationInNewSpace(ebx);
264 }
265
266 // Allocate the new receiver object using the runtime call.
267 __ bind(&rt_call);
268 // Must restore edi (constructor) before calling runtime.
269 __ mov(edi, Operand(esp, 0));
270 // edi: function (constructor)
271 __ push(edi);
272 __ CallRuntime(Runtime::kNewObject, 1);
273 __ mov(ebx, Operand(eax)); // store result in ebx
274
275 // New object allocated.
276 // ebx: newly allocated object
277 __ bind(&allocated);
278 // Retrieve the function from the stack.
279 __ pop(edi);
280
281 // Retrieve smi-tagged arguments count from the stack.
282 __ mov(eax, Operand(esp, 0));
Leon Clarkee46be812010-01-19 14:06:41 +0000283 __ SmiUntag(eax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000284
285 // Push the allocated receiver to the stack. We need two copies
286 // because we may have to return the original one and the calling
287 // conventions dictate that the called function pops the receiver.
288 __ push(ebx);
289 __ push(ebx);
290
291 // Setup pointer to last argument.
292 __ lea(ebx, Operand(ebp, StandardFrameConstants::kCallerSPOffset));
293
294 // Copy arguments and receiver to the expression stack.
295 Label loop, entry;
296 __ mov(ecx, Operand(eax));
297 __ jmp(&entry);
298 __ bind(&loop);
299 __ push(Operand(ebx, ecx, times_4, 0));
300 __ bind(&entry);
301 __ dec(ecx);
302 __ j(greater_equal, &loop);
303
304 // Call the function.
Leon Clarkee46be812010-01-19 14:06:41 +0000305 if (is_api_function) {
306 __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
307 Handle<Code> code = Handle<Code>(
308 Builtins::builtin(Builtins::HandleApiCallConstruct));
309 ParameterCount expected(0);
310 __ InvokeCode(code, expected, expected,
311 RelocInfo::CODE_TARGET, CALL_FUNCTION);
312 } else {
313 ParameterCount actual(eax);
314 __ InvokeFunction(edi, actual, CALL_FUNCTION);
315 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000316
317 // Restore context from the frame.
318 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
319
320 // If the result is an object (in the ECMA sense), we should get rid
321 // of the receiver and use the result; see ECMA-262 section 13.2.2-7
322 // on page 74.
323 Label use_receiver, exit;
324
325 // If the result is a smi, it is *not* an object in the ECMA sense.
326 __ test(eax, Immediate(kSmiTagMask));
327 __ j(zero, &use_receiver, not_taken);
328
329 // If the type of the result (stored in its map) is less than
330 // FIRST_JS_OBJECT_TYPE, it is not an object in the ECMA sense.
331 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
332 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
333 __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
334 __ j(greater_equal, &exit, not_taken);
335
336 // Throw away the result of the constructor invocation and use the
337 // on-stack receiver as the result.
338 __ bind(&use_receiver);
339 __ mov(eax, Operand(esp, 0));
340
341 // Restore the arguments count and leave the construct frame.
342 __ bind(&exit);
343 __ mov(ebx, Operand(esp, kPointerSize)); // get arguments count
344 __ LeaveConstructFrame();
345
346 // Remove caller arguments from the stack and return.
347 ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
348 __ pop(ecx);
349 __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver
350 __ push(ecx);
351 __ IncrementCounter(&Counters::constructed_objects, 1);
352 __ ret(0);
353}
354
355
Leon Clarkee46be812010-01-19 14:06:41 +0000356void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
357 Generate_JSConstructStubHelper(masm, false);
358}
359
360
361void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
362 Generate_JSConstructStubHelper(masm, true);
363}
364
365
Steve Blocka7e24c12009-10-30 11:49:00 +0000366static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
367 bool is_construct) {
368 // Clear the context before we push it when entering the JS frame.
369 __ xor_(esi, Operand(esi)); // clear esi
370
371 // Enter an internal frame.
372 __ EnterInternalFrame();
373
374 // Load the previous frame pointer (ebx) to access C arguments
375 __ mov(ebx, Operand(ebp, 0));
376
377 // Get the function from the frame and setup the context.
378 __ mov(ecx, Operand(ebx, EntryFrameConstants::kFunctionArgOffset));
379 __ mov(esi, FieldOperand(ecx, JSFunction::kContextOffset));
380
381 // Push the function and the receiver onto the stack.
382 __ push(ecx);
383 __ push(Operand(ebx, EntryFrameConstants::kReceiverArgOffset));
384
385 // Load the number of arguments and setup pointer to the arguments.
386 __ mov(eax, Operand(ebx, EntryFrameConstants::kArgcOffset));
387 __ mov(ebx, Operand(ebx, EntryFrameConstants::kArgvOffset));
388
389 // Copy arguments to the stack in a loop.
390 Label loop, entry;
391 __ xor_(ecx, Operand(ecx)); // clear ecx
392 __ jmp(&entry);
393 __ bind(&loop);
394 __ mov(edx, Operand(ebx, ecx, times_4, 0)); // push parameter from argv
395 __ push(Operand(edx, 0)); // dereference handle
396 __ inc(Operand(ecx));
397 __ bind(&entry);
398 __ cmp(ecx, Operand(eax));
399 __ j(not_equal, &loop);
400
401 // Get the function from the stack and call it.
402 __ mov(edi, Operand(esp, eax, times_4, +1 * kPointerSize)); // +1 ~ receiver
403
404 // Invoke the code.
405 if (is_construct) {
406 __ call(Handle<Code>(Builtins::builtin(Builtins::JSConstructCall)),
407 RelocInfo::CODE_TARGET);
408 } else {
409 ParameterCount actual(eax);
410 __ InvokeFunction(edi, actual, CALL_FUNCTION);
411 }
412
413 // Exit the JS frame. Notice that this also removes the empty
414 // context and the function left on the stack by the code
415 // invocation.
416 __ LeaveInternalFrame();
417 __ ret(1 * kPointerSize); // remove receiver
418}
419
420
421void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) {
422 Generate_JSEntryTrampolineHelper(masm, false);
423}
424
425
426void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
427 Generate_JSEntryTrampolineHelper(masm, true);
428}
429
430
431void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
432 // 1. Make sure we have at least one argument.
433 { Label done;
434 __ test(eax, Operand(eax));
435 __ j(not_zero, &done, taken);
436 __ pop(ebx);
437 __ push(Immediate(Factory::undefined_value()));
438 __ push(ebx);
439 __ inc(eax);
440 __ bind(&done);
441 }
442
Andrei Popescu402d9372010-02-26 13:31:12 +0000443 // 2. Get the function to call (passed as receiver) from the stack, check
444 // if it is a function.
445 Label non_function;
446 // 1 ~ return address.
447 __ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize));
448 __ test(edi, Immediate(kSmiTagMask));
449 __ j(zero, &non_function, not_taken);
450 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
451 __ j(not_equal, &non_function, not_taken);
Steve Blocka7e24c12009-10-30 11:49:00 +0000452
Steve Blocka7e24c12009-10-30 11:49:00 +0000453
Andrei Popescu402d9372010-02-26 13:31:12 +0000454 // 3a. Patch the first argument if necessary when calling a function.
455 Label shift_arguments;
456 { Label convert_to_object, use_global_receiver, patch_receiver;
457 // Change context eagerly in case we need the global receiver.
Steve Blocka7e24c12009-10-30 11:49:00 +0000458 __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
459
Andrei Popescu402d9372010-02-26 13:31:12 +0000460 __ mov(ebx, Operand(esp, eax, times_4, 0)); // First argument.
Steve Blocka7e24c12009-10-30 11:49:00 +0000461 __ test(ebx, Immediate(kSmiTagMask));
Andrei Popescu402d9372010-02-26 13:31:12 +0000462 __ j(zero, &convert_to_object);
Steve Blocka7e24c12009-10-30 11:49:00 +0000463
464 __ cmp(ebx, Factory::null_value());
465 __ j(equal, &use_global_receiver);
466 __ cmp(ebx, Factory::undefined_value());
467 __ j(equal, &use_global_receiver);
468
469 __ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset));
470 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
471 __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
Andrei Popescu402d9372010-02-26 13:31:12 +0000472 __ j(below, &convert_to_object);
Steve Blocka7e24c12009-10-30 11:49:00 +0000473 __ cmp(ecx, LAST_JS_OBJECT_TYPE);
Andrei Popescu402d9372010-02-26 13:31:12 +0000474 __ j(below_equal, &shift_arguments);
Steve Blocka7e24c12009-10-30 11:49:00 +0000475
Andrei Popescu402d9372010-02-26 13:31:12 +0000476 __ bind(&convert_to_object);
477 __ EnterInternalFrame(); // In order to preserve argument count.
Leon Clarkee46be812010-01-19 14:06:41 +0000478 __ SmiTag(eax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000479 __ push(eax);
480
Steve Blocka7e24c12009-10-30 11:49:00 +0000481 __ push(ebx);
482 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
483 __ mov(ebx, eax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000484
Steve Blocka7e24c12009-10-30 11:49:00 +0000485 __ pop(eax);
Leon Clarkee46be812010-01-19 14:06:41 +0000486 __ SmiUntag(eax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000487 __ LeaveInternalFrame();
Andrei Popescu402d9372010-02-26 13:31:12 +0000488 // Restore the function to edi.
489 __ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize));
Steve Blocka7e24c12009-10-30 11:49:00 +0000490 __ jmp(&patch_receiver);
491
Andrei Popescu402d9372010-02-26 13:31:12 +0000492 // Use the global receiver object from the called function as the
493 // receiver.
Steve Blocka7e24c12009-10-30 11:49:00 +0000494 __ bind(&use_global_receiver);
495 const int kGlobalIndex =
496 Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
497 __ mov(ebx, FieldOperand(esi, kGlobalIndex));
Steve Blockd0582a62009-12-15 09:54:21 +0000498 __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalContextOffset));
499 __ mov(ebx, FieldOperand(ebx, kGlobalIndex));
Steve Blocka7e24c12009-10-30 11:49:00 +0000500 __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
501
502 __ bind(&patch_receiver);
503 __ mov(Operand(esp, eax, times_4, 0), ebx);
504
Andrei Popescu402d9372010-02-26 13:31:12 +0000505 __ jmp(&shift_arguments);
Steve Blocka7e24c12009-10-30 11:49:00 +0000506 }
507
Andrei Popescu402d9372010-02-26 13:31:12 +0000508 // 3b. Patch the first argument when calling a non-function. The
509 // CALL_NON_FUNCTION builtin expects the non-function callee as
510 // receiver, so overwrite the first argument which will ultimately
511 // become the receiver.
512 __ bind(&non_function);
513 __ mov(Operand(esp, eax, times_4, 0), edi);
514 // Clear edi to indicate a non-function being called.
515 __ xor_(edi, Operand(edi));
Leon Clarkee46be812010-01-19 14:06:41 +0000516
Andrei Popescu402d9372010-02-26 13:31:12 +0000517 // 4. Shift arguments and return address one slot down on the stack
518 // (overwriting the original receiver). Adjust argument count to make
519 // the original first argument the new receiver.
520 __ bind(&shift_arguments);
Steve Blocka7e24c12009-10-30 11:49:00 +0000521 { Label loop;
Leon Clarkee46be812010-01-19 14:06:41 +0000522 __ mov(ecx, eax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000523 __ bind(&loop);
524 __ mov(ebx, Operand(esp, ecx, times_4, 0));
525 __ mov(Operand(esp, ecx, times_4, kPointerSize), ebx);
526 __ dec(ecx);
Andrei Popescu402d9372010-02-26 13:31:12 +0000527 __ j(not_sign, &loop); // While non-negative (to copy return address).
Leon Clarkee46be812010-01-19 14:06:41 +0000528 __ pop(ebx); // Discard copy of return address.
529 __ dec(eax); // One fewer argument (first argument is new receiver).
Steve Blocka7e24c12009-10-30 11:49:00 +0000530 }
531
Andrei Popescu402d9372010-02-26 13:31:12 +0000532 // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
533 { Label function;
534 __ test(edi, Operand(edi));
535 __ j(not_zero, &function, taken);
536 __ xor_(ebx, Operand(ebx));
537 __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
538 __ jmp(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
539 RelocInfo::CODE_TARGET);
540 __ bind(&function);
Steve Blocka7e24c12009-10-30 11:49:00 +0000541 }
542
Andrei Popescu402d9372010-02-26 13:31:12 +0000543 // 5b. Get the code to call from the function and check that the number of
544 // expected arguments matches what we're providing. If so, jump
545 // (tail-call) to the code in register edx without checking arguments.
546 __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
547 __ mov(ebx,
548 FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
549 __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
550 __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
551 __ cmp(eax, Operand(ebx));
552 __ j(not_equal, Handle<Code>(builtin(ArgumentsAdaptorTrampoline)));
553
Steve Blocka7e24c12009-10-30 11:49:00 +0000554 ParameterCount expected(0);
555 __ InvokeCode(Operand(edx), expected, expected, JUMP_FUNCTION);
556}
557
558
559void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
560 __ EnterInternalFrame();
561
562 __ push(Operand(ebp, 4 * kPointerSize)); // push this
563 __ push(Operand(ebp, 2 * kPointerSize)); // push arguments
564 __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
565
Steve Blockd0582a62009-12-15 09:54:21 +0000566 // Check the stack for overflow. We are not trying need to catch
567 // interruptions (e.g. debug break and preemption) here, so the "real stack
568 // limit" is checked.
569 Label okay;
570 ExternalReference real_stack_limit =
571 ExternalReference::address_of_real_stack_limit();
572 __ mov(edi, Operand::StaticVariable(real_stack_limit));
573 // Make ecx the space we have left. The stack might already be overflowed
574 // here which will cause ecx to become negative.
575 __ mov(ecx, Operand(esp));
576 __ sub(ecx, Operand(edi));
577 // Make edx the space we need for the array when it is unrolled onto the
578 // stack.
579 __ mov(edx, Operand(eax));
580 __ shl(edx, kPointerSizeLog2 - kSmiTagSize);
581 // Check if the arguments will overflow the stack.
582 __ cmp(ecx, Operand(edx));
583 __ j(greater, &okay, taken); // Signed comparison.
Steve Blocka7e24c12009-10-30 11:49:00 +0000584
Steve Blockd0582a62009-12-15 09:54:21 +0000585 // Out of stack space.
586 __ push(Operand(ebp, 4 * kPointerSize)); // push this
587 __ push(eax);
588 __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION);
589 __ bind(&okay);
590 // End of stack check.
Steve Blocka7e24c12009-10-30 11:49:00 +0000591
592 // Push current index and limit.
593 const int kLimitOffset =
594 StandardFrameConstants::kExpressionsOffset - 1 * kPointerSize;
595 const int kIndexOffset = kLimitOffset - 1 * kPointerSize;
596 __ push(eax); // limit
597 __ push(Immediate(0)); // index
598
599 // Change context eagerly to get the right global object if
600 // necessary.
601 __ mov(edi, Operand(ebp, 4 * kPointerSize));
602 __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
603
604 // Compute the receiver.
605 Label call_to_object, use_global_receiver, push_receiver;
606 __ mov(ebx, Operand(ebp, 3 * kPointerSize));
607 __ test(ebx, Immediate(kSmiTagMask));
608 __ j(zero, &call_to_object);
609 __ cmp(ebx, Factory::null_value());
610 __ j(equal, &use_global_receiver);
611 __ cmp(ebx, Factory::undefined_value());
612 __ j(equal, &use_global_receiver);
613
614 // If given receiver is already a JavaScript object then there's no
615 // reason for converting it.
616 __ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset));
617 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
618 __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
619 __ j(less, &call_to_object);
620 __ cmp(ecx, LAST_JS_OBJECT_TYPE);
621 __ j(less_equal, &push_receiver);
622
623 // Convert the receiver to an object.
624 __ bind(&call_to_object);
625 __ push(ebx);
626 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
627 __ mov(ebx, Operand(eax));
628 __ jmp(&push_receiver);
629
630 // Use the current global receiver object as the receiver.
631 __ bind(&use_global_receiver);
632 const int kGlobalOffset =
633 Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
634 __ mov(ebx, FieldOperand(esi, kGlobalOffset));
Steve Blockd0582a62009-12-15 09:54:21 +0000635 __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalContextOffset));
636 __ mov(ebx, FieldOperand(ebx, kGlobalOffset));
Steve Blocka7e24c12009-10-30 11:49:00 +0000637 __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
638
639 // Push the receiver.
640 __ bind(&push_receiver);
641 __ push(ebx);
642
643 // Copy all arguments from the array to the stack.
644 Label entry, loop;
645 __ mov(eax, Operand(ebp, kIndexOffset));
646 __ jmp(&entry);
647 __ bind(&loop);
Andrei Popescu402d9372010-02-26 13:31:12 +0000648 __ mov(edx, Operand(ebp, 2 * kPointerSize)); // load arguments
Steve Blocka7e24c12009-10-30 11:49:00 +0000649
650 // Use inline caching to speed up access to arguments.
651 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
652 __ call(ic, RelocInfo::CODE_TARGET);
653 // It is important that we do not have a test instruction after the
654 // call. A test instruction after the call is used to indicate that
655 // we have generated an inline version of the keyed load. In this
656 // case, we know that we are not generating a test instruction next.
657
Andrei Popescu402d9372010-02-26 13:31:12 +0000658 // Push the nth argument.
Steve Blocka7e24c12009-10-30 11:49:00 +0000659 __ push(eax);
660
661 // Update the index on the stack and in register eax.
662 __ mov(eax, Operand(ebp, kIndexOffset));
663 __ add(Operand(eax), Immediate(1 << kSmiTagSize));
664 __ mov(Operand(ebp, kIndexOffset), eax);
665
666 __ bind(&entry);
667 __ cmp(eax, Operand(ebp, kLimitOffset));
668 __ j(not_equal, &loop);
669
670 // Invoke the function.
671 ParameterCount actual(eax);
Leon Clarkee46be812010-01-19 14:06:41 +0000672 __ SmiUntag(eax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000673 __ mov(edi, Operand(ebp, 4 * kPointerSize));
674 __ InvokeFunction(edi, actual, CALL_FUNCTION);
675
676 __ LeaveInternalFrame();
677 __ ret(3 * kPointerSize); // remove this, receiver, and arguments
678}
679
680
681// Load the built-in Array function from the current context.
682static void GenerateLoadArrayFunction(MacroAssembler* masm, Register result) {
683 // Load the global context.
684 __ mov(result, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
685 __ mov(result, FieldOperand(result, GlobalObject::kGlobalContextOffset));
686 // Load the Array function from the global context.
687 __ mov(result,
688 Operand(result, Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX)));
689}
690
691
692// Number of empty elements to allocate for an empty array.
693static const int kPreallocatedArrayElements = 4;
694
695
696// Allocate an empty JSArray. The allocated array is put into the result
697// register. If the parameter initial_capacity is larger than zero an elements
698// backing store is allocated with this size and filled with the hole values.
699// Otherwise the elements backing store is set to the empty FixedArray.
700static void AllocateEmptyJSArray(MacroAssembler* masm,
701 Register array_function,
702 Register result,
703 Register scratch1,
704 Register scratch2,
705 Register scratch3,
706 int initial_capacity,
707 Label* gc_required) {
708 ASSERT(initial_capacity >= 0);
709
710 // Load the initial map from the array function.
711 __ mov(scratch1, FieldOperand(array_function,
712 JSFunction::kPrototypeOrInitialMapOffset));
713
714 // Allocate the JSArray object together with space for a fixed array with the
715 // requested elements.
716 int size = JSArray::kSize;
717 if (initial_capacity > 0) {
718 size += FixedArray::SizeFor(initial_capacity);
719 }
720 __ AllocateInNewSpace(size,
721 result,
722 scratch2,
723 scratch3,
724 gc_required,
725 TAG_OBJECT);
726
727 // Allocated the JSArray. Now initialize the fields except for the elements
728 // array.
729 // result: JSObject
730 // scratch1: initial map
731 // scratch2: start of next object
732 __ mov(FieldOperand(result, JSObject::kMapOffset), scratch1);
733 __ mov(FieldOperand(result, JSArray::kPropertiesOffset),
734 Factory::empty_fixed_array());
735 // Field JSArray::kElementsOffset is initialized later.
736 __ mov(FieldOperand(result, JSArray::kLengthOffset), Immediate(0));
737
738 // If no storage is requested for the elements array just set the empty
739 // fixed array.
740 if (initial_capacity == 0) {
741 __ mov(FieldOperand(result, JSArray::kElementsOffset),
742 Factory::empty_fixed_array());
743 return;
744 }
745
746 // Calculate the location of the elements array and set elements array member
747 // of the JSArray.
748 // result: JSObject
749 // scratch2: start of next object
750 __ lea(scratch1, Operand(result, JSArray::kSize));
751 __ mov(FieldOperand(result, JSArray::kElementsOffset), scratch1);
752
753 // Initialize the FixedArray and fill it with holes. FixedArray length is not
754 // stored as a smi.
755 // result: JSObject
756 // scratch1: elements array
757 // scratch2: start of next object
758 __ mov(FieldOperand(scratch1, JSObject::kMapOffset),
759 Factory::fixed_array_map());
760 __ mov(FieldOperand(scratch1, Array::kLengthOffset),
761 Immediate(initial_capacity));
762
763 // Fill the FixedArray with the hole value. Inline the code if short.
764 // Reconsider loop unfolding if kPreallocatedArrayElements gets changed.
765 static const int kLoopUnfoldLimit = 4;
766 ASSERT(kPreallocatedArrayElements <= kLoopUnfoldLimit);
767 if (initial_capacity <= kLoopUnfoldLimit) {
768 // Use a scratch register here to have only one reloc info when unfolding
769 // the loop.
770 __ mov(scratch3, Factory::the_hole_value());
771 for (int i = 0; i < initial_capacity; i++) {
772 __ mov(FieldOperand(scratch1,
773 FixedArray::kHeaderSize + i * kPointerSize),
774 scratch3);
775 }
776 } else {
777 Label loop, entry;
778 __ jmp(&entry);
779 __ bind(&loop);
780 __ mov(Operand(scratch1, 0), Factory::the_hole_value());
781 __ add(Operand(scratch1), Immediate(kPointerSize));
782 __ bind(&entry);
783 __ cmp(scratch1, Operand(scratch2));
784 __ j(below, &loop);
785 }
786}
787
788
789// Allocate a JSArray with the number of elements stored in a register. The
790// register array_function holds the built-in Array function and the register
791// array_size holds the size of the array as a smi. The allocated array is put
792// into the result register and beginning and end of the FixedArray elements
793// storage is put into registers elements_array and elements_array_end (see
794// below for when that is not the case). If the parameter fill_with_holes is
795// true the allocated elements backing store is filled with the hole values
796// otherwise it is left uninitialized. When the backing store is filled the
797// register elements_array is scratched.
798static void AllocateJSArray(MacroAssembler* masm,
799 Register array_function, // Array function.
800 Register array_size, // As a smi.
801 Register result,
802 Register elements_array,
803 Register elements_array_end,
804 Register scratch,
805 bool fill_with_hole,
806 Label* gc_required) {
807 Label not_empty, allocated;
808
809 // Load the initial map from the array function.
810 __ mov(elements_array,
811 FieldOperand(array_function,
812 JSFunction::kPrototypeOrInitialMapOffset));
813
814 // Check whether an empty sized array is requested.
815 __ test(array_size, Operand(array_size));
816 __ j(not_zero, &not_empty);
817
818 // If an empty array is requested allocate a small elements array anyway. This
819 // keeps the code below free of special casing for the empty array.
820 int size = JSArray::kSize + FixedArray::SizeFor(kPreallocatedArrayElements);
821 __ AllocateInNewSpace(size,
822 result,
823 elements_array_end,
824 scratch,
825 gc_required,
826 TAG_OBJECT);
827 __ jmp(&allocated);
828
829 // Allocate the JSArray object together with space for a FixedArray with the
830 // requested elements.
831 __ bind(&not_empty);
832 ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
833 __ AllocateInNewSpace(JSArray::kSize + FixedArray::kHeaderSize,
834 times_half_pointer_size, // array_size is a smi.
835 array_size,
836 result,
837 elements_array_end,
838 scratch,
839 gc_required,
840 TAG_OBJECT);
841
842 // Allocated the JSArray. Now initialize the fields except for the elements
843 // array.
844 // result: JSObject
845 // elements_array: initial map
846 // elements_array_end: start of next object
847 // array_size: size of array (smi)
848 __ bind(&allocated);
849 __ mov(FieldOperand(result, JSObject::kMapOffset), elements_array);
850 __ mov(elements_array, Factory::empty_fixed_array());
851 __ mov(FieldOperand(result, JSArray::kPropertiesOffset), elements_array);
852 // Field JSArray::kElementsOffset is initialized later.
853 __ mov(FieldOperand(result, JSArray::kLengthOffset), array_size);
854
855 // Calculate the location of the elements array and set elements array member
856 // of the JSArray.
857 // result: JSObject
858 // elements_array_end: start of next object
859 // array_size: size of array (smi)
860 __ lea(elements_array, Operand(result, JSArray::kSize));
861 __ mov(FieldOperand(result, JSArray::kElementsOffset), elements_array);
862
863 // Initialize the fixed array. FixedArray length is not stored as a smi.
864 // result: JSObject
865 // elements_array: elements array
866 // elements_array_end: start of next object
867 // array_size: size of array (smi)
868 ASSERT(kSmiTag == 0);
Leon Clarkee46be812010-01-19 14:06:41 +0000869 __ SmiUntag(array_size); // Convert from smi to value.
Steve Blocka7e24c12009-10-30 11:49:00 +0000870 __ mov(FieldOperand(elements_array, JSObject::kMapOffset),
871 Factory::fixed_array_map());
872 Label not_empty_2, fill_array;
873 __ test(array_size, Operand(array_size));
874 __ j(not_zero, &not_empty_2);
875 // Length of the FixedArray is the number of pre-allocated elements even
876 // though the actual JSArray has length 0.
877 __ mov(FieldOperand(elements_array, Array::kLengthOffset),
878 Immediate(kPreallocatedArrayElements));
879 __ jmp(&fill_array);
880 __ bind(&not_empty_2);
881 // For non-empty JSArrays the length of the FixedArray and the JSArray is the
882 // same.
883 __ mov(FieldOperand(elements_array, Array::kLengthOffset), array_size);
884
885 // Fill the allocated FixedArray with the hole value if requested.
886 // result: JSObject
887 // elements_array: elements array
888 // elements_array_end: start of next object
889 __ bind(&fill_array);
890 if (fill_with_hole) {
891 Label loop, entry;
892 __ mov(scratch, Factory::the_hole_value());
893 __ lea(elements_array, Operand(elements_array,
894 FixedArray::kHeaderSize - kHeapObjectTag));
895 __ jmp(&entry);
896 __ bind(&loop);
897 __ mov(Operand(elements_array, 0), scratch);
898 __ add(Operand(elements_array), Immediate(kPointerSize));
899 __ bind(&entry);
900 __ cmp(elements_array, Operand(elements_array_end));
901 __ j(below, &loop);
902 }
903}
904
905
906// Create a new array for the built-in Array function. This function allocates
907// the JSArray object and the FixedArray elements array and initializes these.
908// If the Array cannot be constructed in native code the runtime is called. This
909// function assumes the following state:
910// edi: constructor (built-in Array function)
911// eax: argc
912// esp[0]: return address
913// esp[4]: last argument
914// This function is used for both construct and normal calls of Array. Whether
915// it is a construct call or not is indicated by the construct_call parameter.
916// The only difference between handling a construct call and a normal call is
917// that for a construct call the constructor function in edi needs to be
918// preserved for entering the generic code. In both cases argc in eax needs to
919// be preserved.
920static void ArrayNativeCode(MacroAssembler* masm,
921 bool construct_call,
Steve Blockd0582a62009-12-15 09:54:21 +0000922 Label* call_generic_code) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000923 Label argc_one_or_more, argc_two_or_more, prepare_generic_code_call;
924
925 // Push the constructor and argc. No need to tag argc as a smi, as there will
926 // be no garbage collection with this on the stack.
927 int push_count = 0;
928 if (construct_call) {
929 push_count++;
930 __ push(edi);
931 }
932 push_count++;
933 __ push(eax);
934
935 // Check for array construction with zero arguments.
936 __ test(eax, Operand(eax));
937 __ j(not_zero, &argc_one_or_more);
938
939 // Handle construction of an empty array.
940 AllocateEmptyJSArray(masm,
941 edi,
942 eax,
943 ebx,
944 ecx,
945 edi,
946 kPreallocatedArrayElements,
947 &prepare_generic_code_call);
948 __ IncrementCounter(&Counters::array_function_native, 1);
949 __ pop(ebx);
950 if (construct_call) {
951 __ pop(edi);
952 }
953 __ ret(kPointerSize);
954
955 // Check for one argument. Bail out if argument is not smi or if it is
956 // negative.
957 __ bind(&argc_one_or_more);
958 __ cmp(eax, 1);
959 __ j(not_equal, &argc_two_or_more);
960 ASSERT(kSmiTag == 0);
961 __ test(Operand(esp, (push_count + 1) * kPointerSize),
962 Immediate(kIntptrSignBit | kSmiTagMask));
963 __ j(not_zero, &prepare_generic_code_call);
964
965 // Handle construction of an empty array of a certain size. Get the size from
966 // the stack and bail out if size is to large to actually allocate an elements
967 // array.
968 __ mov(edx, Operand(esp, (push_count + 1) * kPointerSize));
969 ASSERT(kSmiTag == 0);
970 __ cmp(edx, JSObject::kInitialMaxFastElementArray << kSmiTagSize);
971 __ j(greater_equal, &prepare_generic_code_call);
972
973 // edx: array_size (smi)
974 // edi: constructor
975 // esp[0]: argc
976 // esp[4]: constructor (only if construct_call)
977 // esp[8]: return address
978 // esp[C]: argument
979 AllocateJSArray(masm,
980 edi,
981 edx,
982 eax,
983 ebx,
984 ecx,
985 edi,
986 true,
987 &prepare_generic_code_call);
988 __ IncrementCounter(&Counters::array_function_native, 1);
989 __ pop(ebx);
990 if (construct_call) {
991 __ pop(edi);
992 }
993 __ ret(2 * kPointerSize);
994
995 // Handle construction of an array from a list of arguments.
996 __ bind(&argc_two_or_more);
997 ASSERT(kSmiTag == 0);
Leon Clarkee46be812010-01-19 14:06:41 +0000998 __ SmiTag(eax); // Convet argc to a smi.
Steve Blocka7e24c12009-10-30 11:49:00 +0000999 // eax: array_size (smi)
1000 // edi: constructor
1001 // esp[0] : argc
1002 // esp[4]: constructor (only if construct_call)
1003 // esp[8] : return address
1004 // esp[C] : last argument
1005 AllocateJSArray(masm,
1006 edi,
1007 eax,
1008 ebx,
1009 ecx,
1010 edx,
1011 edi,
1012 false,
1013 &prepare_generic_code_call);
1014 __ IncrementCounter(&Counters::array_function_native, 1);
1015 __ mov(eax, ebx);
1016 __ pop(ebx);
1017 if (construct_call) {
1018 __ pop(edi);
1019 }
1020 __ push(eax);
1021 // eax: JSArray
1022 // ebx: argc
1023 // edx: elements_array_end (untagged)
1024 // esp[0]: JSArray
1025 // esp[4]: return address
1026 // esp[8]: last argument
1027
1028 // Location of the last argument
1029 __ lea(edi, Operand(esp, 2 * kPointerSize));
1030
1031 // Location of the first array element (Parameter fill_with_holes to
1032 // AllocateJSArrayis false, so the FixedArray is returned in ecx).
1033 __ lea(edx, Operand(ecx, FixedArray::kHeaderSize - kHeapObjectTag));
1034
1035 // ebx: argc
1036 // edx: location of the first array element
1037 // edi: location of the last argument
1038 // esp[0]: JSArray
1039 // esp[4]: return address
1040 // esp[8]: last argument
1041 Label loop, entry;
1042 __ mov(ecx, ebx);
1043 __ jmp(&entry);
1044 __ bind(&loop);
1045 __ mov(eax, Operand(edi, ecx, times_pointer_size, 0));
1046 __ mov(Operand(edx, 0), eax);
1047 __ add(Operand(edx), Immediate(kPointerSize));
1048 __ bind(&entry);
1049 __ dec(ecx);
1050 __ j(greater_equal, &loop);
1051
1052 // Remove caller arguments from the stack and return.
1053 // ebx: argc
1054 // esp[0]: JSArray
1055 // esp[4]: return address
1056 // esp[8]: last argument
1057 __ pop(eax);
1058 __ pop(ecx);
1059 __ lea(esp, Operand(esp, ebx, times_pointer_size, 1 * kPointerSize));
1060 __ push(ecx);
1061 __ ret(0);
1062
1063 // Restore argc and constructor before running the generic code.
1064 __ bind(&prepare_generic_code_call);
1065 __ pop(eax);
1066 if (construct_call) {
1067 __ pop(edi);
1068 }
1069 __ jmp(call_generic_code);
1070}
1071
1072
1073void Builtins::Generate_ArrayCode(MacroAssembler* masm) {
1074 // ----------- S t a t e -------------
1075 // -- eax : argc
1076 // -- esp[0] : return address
1077 // -- esp[4] : last argument
1078 // -----------------------------------
1079 Label generic_array_code, one_or_more_arguments, two_or_more_arguments;
1080
1081 // Get the Array function.
1082 GenerateLoadArrayFunction(masm, edi);
1083
1084 if (FLAG_debug_code) {
1085 // Initial map for the builtin Array function shoud be a map.
1086 __ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
1087 // Will both indicate a NULL and a Smi.
1088 __ test(ebx, Immediate(kSmiTagMask));
1089 __ Assert(not_zero, "Unexpected initial map for Array function");
1090 __ CmpObjectType(ebx, MAP_TYPE, ecx);
1091 __ Assert(equal, "Unexpected initial map for Array function");
1092 }
1093
1094 // Run the native code for the Array function called as a normal function.
1095 ArrayNativeCode(masm, false, &generic_array_code);
1096
1097 // Jump to the generic array code in case the specialized code cannot handle
1098 // the construction.
1099 __ bind(&generic_array_code);
1100 Code* code = Builtins::builtin(Builtins::ArrayCodeGeneric);
1101 Handle<Code> array_code(code);
1102 __ jmp(array_code, RelocInfo::CODE_TARGET);
1103}
1104
1105
1106void Builtins::Generate_ArrayConstructCode(MacroAssembler* masm) {
1107 // ----------- S t a t e -------------
1108 // -- eax : argc
1109 // -- edi : constructor
1110 // -- esp[0] : return address
1111 // -- esp[4] : last argument
1112 // -----------------------------------
1113 Label generic_constructor;
1114
1115 if (FLAG_debug_code) {
1116 // The array construct code is only set for the builtin Array function which
1117 // does always have a map.
1118 GenerateLoadArrayFunction(masm, ebx);
1119 __ cmp(edi, Operand(ebx));
1120 __ Assert(equal, "Unexpected Array function");
1121 // Initial map for the builtin Array function should be a map.
1122 __ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
1123 // Will both indicate a NULL and a Smi.
1124 __ test(ebx, Immediate(kSmiTagMask));
1125 __ Assert(not_zero, "Unexpected initial map for Array function");
1126 __ CmpObjectType(ebx, MAP_TYPE, ecx);
1127 __ Assert(equal, "Unexpected initial map for Array function");
1128 }
1129
1130 // Run the native code for the Array function called as constructor.
1131 ArrayNativeCode(masm, true, &generic_constructor);
1132
1133 // Jump to the generic construct code in case the specialized code cannot
1134 // handle the construction.
1135 __ bind(&generic_constructor);
1136 Code* code = Builtins::builtin(Builtins::JSConstructStubGeneric);
1137 Handle<Code> generic_construct_stub(code);
1138 __ jmp(generic_construct_stub, RelocInfo::CODE_TARGET);
1139}
1140
1141
1142static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
1143 __ push(ebp);
1144 __ mov(ebp, Operand(esp));
1145
1146 // Store the arguments adaptor context sentinel.
1147 __ push(Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
1148
1149 // Push the function on the stack.
1150 __ push(edi);
1151
1152 // Preserve the number of arguments on the stack. Must preserve both
1153 // eax and ebx because these registers are used when copying the
1154 // arguments and the receiver.
1155 ASSERT(kSmiTagSize == 1);
1156 __ lea(ecx, Operand(eax, eax, times_1, kSmiTag));
1157 __ push(ecx);
1158}
1159
1160
1161static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) {
1162 // Retrieve the number of arguments from the stack.
1163 __ mov(ebx, Operand(ebp, ArgumentsAdaptorFrameConstants::kLengthOffset));
1164
1165 // Leave the frame.
1166 __ leave();
1167
1168 // Remove caller arguments from the stack.
1169 ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
1170 __ pop(ecx);
1171 __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver
1172 __ push(ecx);
1173}
1174
1175
1176void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
1177 // ----------- S t a t e -------------
1178 // -- eax : actual number of arguments
1179 // -- ebx : expected number of arguments
1180 // -- edx : code entry to call
1181 // -----------------------------------
1182
1183 Label invoke, dont_adapt_arguments;
1184 __ IncrementCounter(&Counters::arguments_adaptors, 1);
1185
1186 Label enough, too_few;
1187 __ cmp(eax, Operand(ebx));
1188 __ j(less, &too_few);
1189 __ cmp(ebx, SharedFunctionInfo::kDontAdaptArgumentsSentinel);
1190 __ j(equal, &dont_adapt_arguments);
1191
1192 { // Enough parameters: Actual >= expected.
1193 __ bind(&enough);
1194 EnterArgumentsAdaptorFrame(masm);
1195
1196 // Copy receiver and all expected arguments.
1197 const int offset = StandardFrameConstants::kCallerSPOffset;
1198 __ lea(eax, Operand(ebp, eax, times_4, offset));
1199 __ mov(ecx, -1); // account for receiver
1200
1201 Label copy;
1202 __ bind(&copy);
1203 __ inc(ecx);
1204 __ push(Operand(eax, 0));
1205 __ sub(Operand(eax), Immediate(kPointerSize));
1206 __ cmp(ecx, Operand(ebx));
1207 __ j(less, &copy);
1208 __ jmp(&invoke);
1209 }
1210
1211 { // Too few parameters: Actual < expected.
1212 __ bind(&too_few);
1213 EnterArgumentsAdaptorFrame(masm);
1214
1215 // Copy receiver and all actual arguments.
1216 const int offset = StandardFrameConstants::kCallerSPOffset;
1217 __ lea(edi, Operand(ebp, eax, times_4, offset));
1218 __ mov(ecx, -1); // account for receiver
1219
1220 Label copy;
1221 __ bind(&copy);
1222 __ inc(ecx);
1223 __ push(Operand(edi, 0));
1224 __ sub(Operand(edi), Immediate(kPointerSize));
1225 __ cmp(ecx, Operand(eax));
1226 __ j(less, &copy);
1227
1228 // Fill remaining expected arguments with undefined values.
1229 Label fill;
1230 __ bind(&fill);
1231 __ inc(ecx);
1232 __ push(Immediate(Factory::undefined_value()));
1233 __ cmp(ecx, Operand(ebx));
1234 __ j(less, &fill);
1235
1236 // Restore function pointer.
1237 __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
1238 }
1239
1240 // Call the entry point.
1241 __ bind(&invoke);
1242 __ call(Operand(edx));
1243
1244 // Leave frame and return.
1245 LeaveArgumentsAdaptorFrame(masm);
1246 __ ret(0);
1247
1248 // -------------------------------------------
1249 // Dont adapt arguments.
1250 // -------------------------------------------
1251 __ bind(&dont_adapt_arguments);
1252 __ jmp(Operand(edx));
1253}
1254
1255
1256#undef __
1257
1258} } // namespace v8::internal