blob: 35a90a4acae369f9375cb77d8aa960c885bbbb21 [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
Leon Clarkef7060e22010-06-03 12:02:55 +010030#if defined(V8_TARGET_ARCH_IA32)
31
Steve Blocka7e24c12009-10-30 11:49:00 +000032#include "codegen-inl.h"
33
34namespace v8 {
35namespace internal {
36
37
38#define __ ACCESS_MASM(masm)
39
40
Leon Clarkee46be812010-01-19 14:06:41 +000041void Builtins::Generate_Adaptor(MacroAssembler* masm,
42 CFunctionId id,
43 BuiltinExtraArguments extra_args) {
44 // ----------- S t a t e -------------
45 // -- eax : number of arguments excluding receiver
46 // -- edi : called function (only guaranteed when
47 // extra_args requires it)
48 // -- esi : context
49 // -- esp[0] : return address
50 // -- esp[4] : last argument
51 // -- ...
52 // -- esp[4 * argc] : first argument (argc == eax)
53 // -- esp[4 * (argc +1)] : receiver
54 // -----------------------------------
Steve Blocka7e24c12009-10-30 11:49:00 +000055
Leon Clarkee46be812010-01-19 14:06:41 +000056 // Insert extra arguments.
57 int num_extra_args = 0;
58 if (extra_args == NEEDS_CALLED_FUNCTION) {
59 num_extra_args = 1;
60 Register scratch = ebx;
61 __ pop(scratch); // Save return address.
62 __ push(edi);
63 __ push(scratch); // Restore return address.
64 } else {
65 ASSERT(extra_args == NO_EXTRA_ARGUMENTS);
66 }
67
Steve Block6ded16b2010-05-10 14:33:55 +010068 // JumpToExternalReference expects eax to contain the number of arguments
Leon Clarkee46be812010-01-19 14:06:41 +000069 // including the receiver and the extra arguments.
70 __ add(Operand(eax), Immediate(num_extra_args + 1));
Steve Block6ded16b2010-05-10 14:33:55 +010071 __ JumpToExternalReference(ExternalReference(id));
Steve Blocka7e24c12009-10-30 11:49:00 +000072}
73
74
75void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
76 // ----------- S t a t e -------------
77 // -- eax: number of arguments
78 // -- edi: constructor function
79 // -----------------------------------
80
81 Label non_function_call;
82 // Check that function is not a smi.
83 __ test(edi, Immediate(kSmiTagMask));
84 __ j(zero, &non_function_call);
85 // Check that function is a JSFunction.
86 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
87 __ j(not_equal, &non_function_call);
88
89 // Jump to the function-specific construct stub.
90 __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
91 __ mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kConstructStubOffset));
92 __ lea(ebx, FieldOperand(ebx, Code::kHeaderSize));
93 __ jmp(Operand(ebx));
94
95 // edi: called object
96 // eax: number of arguments
97 __ bind(&non_function_call);
Andrei Popescu402d9372010-02-26 13:31:12 +000098 // CALL_NON_FUNCTION expects the non-function constructor as receiver
99 // (instead of the original receiver from the call site). The receiver is
100 // stack element argc+1.
101 __ mov(Operand(esp, eax, times_4, kPointerSize), edi);
Steve Blocka7e24c12009-10-30 11:49:00 +0000102 // Set expected number of arguments to zero (not changing eax).
103 __ Set(ebx, Immediate(0));
104 __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
105 __ jmp(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
106 RelocInfo::CODE_TARGET);
107}
108
109
Leon Clarkee46be812010-01-19 14:06:41 +0000110static void Generate_JSConstructStubHelper(MacroAssembler* masm,
111 bool is_api_function) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000112 // Enter a construct frame.
113 __ EnterConstructFrame();
114
115 // Store a smi-tagged arguments count on the stack.
Leon Clarkee46be812010-01-19 14:06:41 +0000116 __ SmiTag(eax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000117 __ push(eax);
118
119 // Push the function to invoke on the stack.
120 __ push(edi);
121
122 // Try to allocate the object without transitioning into C code. If any of the
123 // preconditions is not met, the code bails out to the runtime call.
124 Label rt_call, allocated;
125 if (FLAG_inline_new) {
126 Label undo_allocation;
127#ifdef ENABLE_DEBUGGER_SUPPORT
128 ExternalReference debug_step_in_fp =
129 ExternalReference::debug_step_in_fp_address();
130 __ cmp(Operand::StaticVariable(debug_step_in_fp), Immediate(0));
131 __ j(not_equal, &rt_call);
132#endif
133
134 // Verified that the constructor is a JSFunction.
135 // Load the initial map and verify that it is in fact a map.
136 // edi: constructor
137 __ mov(eax, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
138 // Will both indicate a NULL and a Smi
139 __ test(eax, Immediate(kSmiTagMask));
140 __ j(zero, &rt_call);
141 // edi: constructor
142 // eax: initial map (if proven valid below)
143 __ CmpObjectType(eax, MAP_TYPE, ebx);
144 __ j(not_equal, &rt_call);
145
146 // Check that the constructor is not constructing a JSFunction (see comments
147 // in Runtime_NewObject in runtime.cc). In which case the initial map's
148 // instance type would be JS_FUNCTION_TYPE.
149 // edi: constructor
150 // eax: initial map
151 __ CmpInstanceType(eax, JS_FUNCTION_TYPE);
152 __ j(equal, &rt_call);
153
154 // Now allocate the JSObject on the heap.
155 // edi: constructor
156 // eax: initial map
157 __ movzx_b(edi, FieldOperand(eax, Map::kInstanceSizeOffset));
158 __ shl(edi, kPointerSizeLog2);
159 __ AllocateInNewSpace(edi, ebx, edi, no_reg, &rt_call, NO_ALLOCATION_FLAGS);
160 // Allocated the JSObject, now initialize the fields.
161 // eax: initial map
162 // ebx: JSObject
163 // edi: start of next object
164 __ mov(Operand(ebx, JSObject::kMapOffset), eax);
165 __ mov(ecx, Factory::empty_fixed_array());
166 __ mov(Operand(ebx, JSObject::kPropertiesOffset), ecx);
167 __ mov(Operand(ebx, JSObject::kElementsOffset), ecx);
168 // Set extra fields in the newly allocated object.
169 // eax: initial map
170 // ebx: JSObject
171 // edi: start of next object
172 { Label loop, entry;
173 __ mov(edx, Factory::undefined_value());
174 __ lea(ecx, Operand(ebx, JSObject::kHeaderSize));
175 __ jmp(&entry);
176 __ bind(&loop);
177 __ mov(Operand(ecx, 0), edx);
178 __ add(Operand(ecx), Immediate(kPointerSize));
179 __ bind(&entry);
180 __ cmp(ecx, Operand(edi));
181 __ j(less, &loop);
182 }
183
184 // Add the object tag to make the JSObject real, so that we can continue and
185 // jump into the continuation code at any time from now on. Any failures
186 // need to undo the allocation, so that the heap is in a consistent state
187 // and verifiable.
188 // eax: initial map
189 // ebx: JSObject
190 // edi: start of next object
191 __ or_(Operand(ebx), Immediate(kHeapObjectTag));
192
193 // Check if a non-empty properties array is needed.
194 // Allocate and initialize a FixedArray if it is.
195 // eax: initial map
196 // ebx: JSObject
197 // edi: start of next object
198 // Calculate the total number of properties described by the map.
199 __ movzx_b(edx, FieldOperand(eax, Map::kUnusedPropertyFieldsOffset));
200 __ movzx_b(ecx, FieldOperand(eax, Map::kPreAllocatedPropertyFieldsOffset));
201 __ add(edx, Operand(ecx));
202 // Calculate unused properties past the end of the in-object properties.
203 __ movzx_b(ecx, FieldOperand(eax, Map::kInObjectPropertiesOffset));
204 __ sub(edx, Operand(ecx));
205 // Done if no extra properties are to be allocated.
206 __ j(zero, &allocated);
207 __ Assert(positive, "Property allocation count failed.");
208
209 // Scale the number of elements by pointer size and add the header for
210 // FixedArrays to the start of the next object calculation from above.
211 // ebx: JSObject
212 // edi: start of next object (will be start of FixedArray)
213 // edx: number of elements in properties array
214 __ AllocateInNewSpace(FixedArray::kHeaderSize,
215 times_pointer_size,
216 edx,
217 edi,
218 ecx,
219 no_reg,
220 &undo_allocation,
221 RESULT_CONTAINS_TOP);
222
223 // Initialize the FixedArray.
224 // ebx: JSObject
225 // edi: FixedArray
226 // edx: number of elements
227 // ecx: start of next object
228 __ mov(eax, Factory::fixed_array_map());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100229 __ mov(Operand(edi, FixedArray::kMapOffset), eax); // setup the map
230 __ SmiTag(edx);
231 __ mov(Operand(edi, FixedArray::kLengthOffset), edx); // and length
Steve Blocka7e24c12009-10-30 11:49:00 +0000232
233 // Initialize the fields to undefined.
234 // ebx: JSObject
235 // edi: FixedArray
236 // ecx: start of next object
237 { Label loop, entry;
238 __ mov(edx, Factory::undefined_value());
239 __ lea(eax, Operand(edi, FixedArray::kHeaderSize));
240 __ jmp(&entry);
241 __ bind(&loop);
242 __ mov(Operand(eax, 0), edx);
243 __ add(Operand(eax), Immediate(kPointerSize));
244 __ bind(&entry);
245 __ cmp(eax, Operand(ecx));
246 __ j(below, &loop);
247 }
248
249 // Store the initialized FixedArray into the properties field of
250 // the JSObject
251 // ebx: JSObject
252 // edi: FixedArray
253 __ or_(Operand(edi), Immediate(kHeapObjectTag)); // add the heap tag
254 __ mov(FieldOperand(ebx, JSObject::kPropertiesOffset), edi);
255
256
257 // Continue with JSObject being successfully allocated
258 // ebx: JSObject
259 __ jmp(&allocated);
260
261 // Undo the setting of the new top so that the heap is verifiable. For
262 // example, the map's unused properties potentially do not match the
263 // allocated objects unused properties.
264 // ebx: JSObject (previous new top)
265 __ bind(&undo_allocation);
266 __ UndoAllocationInNewSpace(ebx);
267 }
268
269 // Allocate the new receiver object using the runtime call.
270 __ bind(&rt_call);
271 // Must restore edi (constructor) before calling runtime.
272 __ mov(edi, Operand(esp, 0));
273 // edi: function (constructor)
274 __ push(edi);
275 __ CallRuntime(Runtime::kNewObject, 1);
276 __ mov(ebx, Operand(eax)); // store result in ebx
277
278 // New object allocated.
279 // ebx: newly allocated object
280 __ bind(&allocated);
281 // Retrieve the function from the stack.
282 __ pop(edi);
283
284 // Retrieve smi-tagged arguments count from the stack.
285 __ mov(eax, Operand(esp, 0));
Leon Clarkee46be812010-01-19 14:06:41 +0000286 __ SmiUntag(eax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000287
288 // Push the allocated receiver to the stack. We need two copies
289 // because we may have to return the original one and the calling
290 // conventions dictate that the called function pops the receiver.
291 __ push(ebx);
292 __ push(ebx);
293
294 // Setup pointer to last argument.
295 __ lea(ebx, Operand(ebp, StandardFrameConstants::kCallerSPOffset));
296
297 // Copy arguments and receiver to the expression stack.
298 Label loop, entry;
299 __ mov(ecx, Operand(eax));
300 __ jmp(&entry);
301 __ bind(&loop);
302 __ push(Operand(ebx, ecx, times_4, 0));
303 __ bind(&entry);
304 __ dec(ecx);
305 __ j(greater_equal, &loop);
306
307 // Call the function.
Leon Clarkee46be812010-01-19 14:06:41 +0000308 if (is_api_function) {
309 __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
310 Handle<Code> code = Handle<Code>(
311 Builtins::builtin(Builtins::HandleApiCallConstruct));
312 ParameterCount expected(0);
313 __ InvokeCode(code, expected, expected,
314 RelocInfo::CODE_TARGET, CALL_FUNCTION);
315 } else {
316 ParameterCount actual(eax);
317 __ InvokeFunction(edi, actual, CALL_FUNCTION);
318 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000319
320 // Restore context from the frame.
321 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
322
323 // If the result is an object (in the ECMA sense), we should get rid
324 // of the receiver and use the result; see ECMA-262 section 13.2.2-7
325 // on page 74.
326 Label use_receiver, exit;
327
328 // If the result is a smi, it is *not* an object in the ECMA sense.
329 __ test(eax, Immediate(kSmiTagMask));
330 __ j(zero, &use_receiver, not_taken);
331
332 // If the type of the result (stored in its map) is less than
333 // FIRST_JS_OBJECT_TYPE, it is not an object in the ECMA sense.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100334 __ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ecx);
335 __ j(above_equal, &exit, not_taken);
Steve Blocka7e24c12009-10-30 11:49:00 +0000336
337 // Throw away the result of the constructor invocation and use the
338 // on-stack receiver as the result.
339 __ bind(&use_receiver);
340 __ mov(eax, Operand(esp, 0));
341
342 // Restore the arguments count and leave the construct frame.
343 __ bind(&exit);
344 __ mov(ebx, Operand(esp, kPointerSize)); // get arguments count
345 __ LeaveConstructFrame();
346
347 // Remove caller arguments from the stack and return.
348 ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
349 __ pop(ecx);
350 __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver
351 __ push(ecx);
352 __ IncrementCounter(&Counters::constructed_objects, 1);
353 __ ret(0);
354}
355
356
Leon Clarkee46be812010-01-19 14:06:41 +0000357void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
358 Generate_JSConstructStubHelper(masm, false);
359}
360
361
362void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
363 Generate_JSConstructStubHelper(masm, true);
364}
365
366
Steve Blocka7e24c12009-10-30 11:49:00 +0000367static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
368 bool is_construct) {
369 // Clear the context before we push it when entering the JS frame.
370 __ xor_(esi, Operand(esi)); // clear esi
371
372 // Enter an internal frame.
373 __ EnterInternalFrame();
374
375 // Load the previous frame pointer (ebx) to access C arguments
376 __ mov(ebx, Operand(ebp, 0));
377
378 // Get the function from the frame and setup the context.
379 __ mov(ecx, Operand(ebx, EntryFrameConstants::kFunctionArgOffset));
380 __ mov(esi, FieldOperand(ecx, JSFunction::kContextOffset));
381
382 // Push the function and the receiver onto the stack.
383 __ push(ecx);
384 __ push(Operand(ebx, EntryFrameConstants::kReceiverArgOffset));
385
386 // Load the number of arguments and setup pointer to the arguments.
387 __ mov(eax, Operand(ebx, EntryFrameConstants::kArgcOffset));
388 __ mov(ebx, Operand(ebx, EntryFrameConstants::kArgvOffset));
389
390 // Copy arguments to the stack in a loop.
391 Label loop, entry;
392 __ xor_(ecx, Operand(ecx)); // clear ecx
393 __ jmp(&entry);
394 __ bind(&loop);
395 __ mov(edx, Operand(ebx, ecx, times_4, 0)); // push parameter from argv
396 __ push(Operand(edx, 0)); // dereference handle
397 __ inc(Operand(ecx));
398 __ bind(&entry);
399 __ cmp(ecx, Operand(eax));
400 __ j(not_equal, &loop);
401
402 // Get the function from the stack and call it.
403 __ mov(edi, Operand(esp, eax, times_4, +1 * kPointerSize)); // +1 ~ receiver
404
405 // Invoke the code.
406 if (is_construct) {
407 __ call(Handle<Code>(Builtins::builtin(Builtins::JSConstructCall)),
408 RelocInfo::CODE_TARGET);
409 } else {
410 ParameterCount actual(eax);
411 __ InvokeFunction(edi, actual, CALL_FUNCTION);
412 }
413
414 // Exit the JS frame. Notice that this also removes the empty
415 // context and the function left on the stack by the code
416 // invocation.
417 __ LeaveInternalFrame();
418 __ ret(1 * kPointerSize); // remove receiver
419}
420
421
422void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) {
423 Generate_JSEntryTrampolineHelper(masm, false);
424}
425
426
427void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
428 Generate_JSEntryTrampolineHelper(masm, true);
429}
430
431
Iain Merrick75681382010-08-19 15:07:18 +0100432void Builtins::Generate_LazyCompile(MacroAssembler* masm) {
433 // Enter an internal frame.
434 __ EnterInternalFrame();
435
436 // Push a copy of the function onto the stack.
437 __ push(edi);
438
439 __ push(edi); // Function is also the parameter to the runtime call.
440 __ CallRuntime(Runtime::kLazyCompile, 1);
441 __ pop(edi);
442
443 // Tear down temporary frame.
444 __ LeaveInternalFrame();
445
446 // Do a tail-call of the compiled function.
447 __ lea(ecx, FieldOperand(eax, Code::kHeaderSize));
448 __ jmp(Operand(ecx));
449}
450
451
Steve Blocka7e24c12009-10-30 11:49:00 +0000452void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
453 // 1. Make sure we have at least one argument.
454 { Label done;
455 __ test(eax, Operand(eax));
456 __ j(not_zero, &done, taken);
457 __ pop(ebx);
458 __ push(Immediate(Factory::undefined_value()));
459 __ push(ebx);
460 __ inc(eax);
461 __ bind(&done);
462 }
463
Andrei Popescu402d9372010-02-26 13:31:12 +0000464 // 2. Get the function to call (passed as receiver) from the stack, check
465 // if it is a function.
466 Label non_function;
467 // 1 ~ return address.
468 __ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize));
469 __ test(edi, Immediate(kSmiTagMask));
470 __ j(zero, &non_function, not_taken);
471 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
472 __ j(not_equal, &non_function, not_taken);
Steve Blocka7e24c12009-10-30 11:49:00 +0000473
Steve Blocka7e24c12009-10-30 11:49:00 +0000474
Andrei Popescu402d9372010-02-26 13:31:12 +0000475 // 3a. Patch the first argument if necessary when calling a function.
476 Label shift_arguments;
477 { Label convert_to_object, use_global_receiver, patch_receiver;
478 // Change context eagerly in case we need the global receiver.
Steve Blocka7e24c12009-10-30 11:49:00 +0000479 __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
480
Andrei Popescu402d9372010-02-26 13:31:12 +0000481 __ mov(ebx, Operand(esp, eax, times_4, 0)); // First argument.
Steve Blocka7e24c12009-10-30 11:49:00 +0000482 __ test(ebx, Immediate(kSmiTagMask));
Andrei Popescu402d9372010-02-26 13:31:12 +0000483 __ j(zero, &convert_to_object);
Steve Blocka7e24c12009-10-30 11:49:00 +0000484
485 __ cmp(ebx, Factory::null_value());
486 __ j(equal, &use_global_receiver);
487 __ cmp(ebx, Factory::undefined_value());
488 __ j(equal, &use_global_receiver);
489
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100490 // We don't use IsObjectJSObjectType here because we jump on success.
Steve Blocka7e24c12009-10-30 11:49:00 +0000491 __ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset));
492 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100493 __ sub(Operand(ecx), Immediate(FIRST_JS_OBJECT_TYPE));
494 __ cmp(ecx, LAST_JS_OBJECT_TYPE - FIRST_JS_OBJECT_TYPE);
Andrei Popescu402d9372010-02-26 13:31:12 +0000495 __ j(below_equal, &shift_arguments);
Steve Blocka7e24c12009-10-30 11:49:00 +0000496
Andrei Popescu402d9372010-02-26 13:31:12 +0000497 __ bind(&convert_to_object);
498 __ EnterInternalFrame(); // In order to preserve argument count.
Leon Clarkee46be812010-01-19 14:06:41 +0000499 __ SmiTag(eax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000500 __ push(eax);
501
Steve Blocka7e24c12009-10-30 11:49:00 +0000502 __ push(ebx);
503 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
504 __ mov(ebx, eax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000505
Steve Blocka7e24c12009-10-30 11:49:00 +0000506 __ pop(eax);
Leon Clarkee46be812010-01-19 14:06:41 +0000507 __ SmiUntag(eax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000508 __ LeaveInternalFrame();
Andrei Popescu402d9372010-02-26 13:31:12 +0000509 // Restore the function to edi.
510 __ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize));
Steve Blocka7e24c12009-10-30 11:49:00 +0000511 __ jmp(&patch_receiver);
512
Andrei Popescu402d9372010-02-26 13:31:12 +0000513 // Use the global receiver object from the called function as the
514 // receiver.
Steve Blocka7e24c12009-10-30 11:49:00 +0000515 __ bind(&use_global_receiver);
516 const int kGlobalIndex =
517 Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
518 __ mov(ebx, FieldOperand(esi, kGlobalIndex));
Steve Blockd0582a62009-12-15 09:54:21 +0000519 __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalContextOffset));
520 __ mov(ebx, FieldOperand(ebx, kGlobalIndex));
Steve Blocka7e24c12009-10-30 11:49:00 +0000521 __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
522
523 __ bind(&patch_receiver);
524 __ mov(Operand(esp, eax, times_4, 0), ebx);
525
Andrei Popescu402d9372010-02-26 13:31:12 +0000526 __ jmp(&shift_arguments);
Steve Blocka7e24c12009-10-30 11:49:00 +0000527 }
528
Andrei Popescu402d9372010-02-26 13:31:12 +0000529 // 3b. Patch the first argument when calling a non-function. The
530 // CALL_NON_FUNCTION builtin expects the non-function callee as
531 // receiver, so overwrite the first argument which will ultimately
532 // become the receiver.
533 __ bind(&non_function);
534 __ mov(Operand(esp, eax, times_4, 0), edi);
535 // Clear edi to indicate a non-function being called.
536 __ xor_(edi, Operand(edi));
Leon Clarkee46be812010-01-19 14:06:41 +0000537
Andrei Popescu402d9372010-02-26 13:31:12 +0000538 // 4. Shift arguments and return address one slot down on the stack
539 // (overwriting the original receiver). Adjust argument count to make
540 // the original first argument the new receiver.
541 __ bind(&shift_arguments);
Steve Blocka7e24c12009-10-30 11:49:00 +0000542 { Label loop;
Leon Clarkee46be812010-01-19 14:06:41 +0000543 __ mov(ecx, eax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000544 __ bind(&loop);
545 __ mov(ebx, Operand(esp, ecx, times_4, 0));
546 __ mov(Operand(esp, ecx, times_4, kPointerSize), ebx);
547 __ dec(ecx);
Andrei Popescu402d9372010-02-26 13:31:12 +0000548 __ j(not_sign, &loop); // While non-negative (to copy return address).
Leon Clarkee46be812010-01-19 14:06:41 +0000549 __ pop(ebx); // Discard copy of return address.
550 __ dec(eax); // One fewer argument (first argument is new receiver).
Steve Blocka7e24c12009-10-30 11:49:00 +0000551 }
552
Andrei Popescu402d9372010-02-26 13:31:12 +0000553 // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
554 { Label function;
555 __ test(edi, Operand(edi));
556 __ j(not_zero, &function, taken);
557 __ xor_(ebx, Operand(ebx));
558 __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
559 __ jmp(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
560 RelocInfo::CODE_TARGET);
561 __ bind(&function);
Steve Blocka7e24c12009-10-30 11:49:00 +0000562 }
563
Andrei Popescu402d9372010-02-26 13:31:12 +0000564 // 5b. Get the code to call from the function and check that the number of
565 // expected arguments matches what we're providing. If so, jump
566 // (tail-call) to the code in register edx without checking arguments.
567 __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
568 __ mov(ebx,
569 FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100570 __ SmiUntag(ebx);
Iain Merrick75681382010-08-19 15:07:18 +0100571 __ mov(edx, FieldOperand(edi, JSFunction::kCodeOffset));
Andrei Popescu402d9372010-02-26 13:31:12 +0000572 __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
573 __ cmp(eax, Operand(ebx));
574 __ j(not_equal, Handle<Code>(builtin(ArgumentsAdaptorTrampoline)));
575
Steve Blocka7e24c12009-10-30 11:49:00 +0000576 ParameterCount expected(0);
577 __ InvokeCode(Operand(edx), expected, expected, JUMP_FUNCTION);
578}
579
580
581void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
582 __ EnterInternalFrame();
583
584 __ push(Operand(ebp, 4 * kPointerSize)); // push this
585 __ push(Operand(ebp, 2 * kPointerSize)); // push arguments
586 __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
587
Steve Blockd0582a62009-12-15 09:54:21 +0000588 // Check the stack for overflow. We are not trying need to catch
589 // interruptions (e.g. debug break and preemption) here, so the "real stack
590 // limit" is checked.
591 Label okay;
592 ExternalReference real_stack_limit =
593 ExternalReference::address_of_real_stack_limit();
594 __ mov(edi, Operand::StaticVariable(real_stack_limit));
595 // Make ecx the space we have left. The stack might already be overflowed
596 // here which will cause ecx to become negative.
597 __ mov(ecx, Operand(esp));
598 __ sub(ecx, Operand(edi));
599 // Make edx the space we need for the array when it is unrolled onto the
600 // stack.
601 __ mov(edx, Operand(eax));
602 __ shl(edx, kPointerSizeLog2 - kSmiTagSize);
603 // Check if the arguments will overflow the stack.
604 __ cmp(ecx, Operand(edx));
605 __ j(greater, &okay, taken); // Signed comparison.
Steve Blocka7e24c12009-10-30 11:49:00 +0000606
Steve Blockd0582a62009-12-15 09:54:21 +0000607 // Out of stack space.
608 __ push(Operand(ebp, 4 * kPointerSize)); // push this
609 __ push(eax);
610 __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION);
611 __ bind(&okay);
612 // End of stack check.
Steve Blocka7e24c12009-10-30 11:49:00 +0000613
614 // Push current index and limit.
615 const int kLimitOffset =
616 StandardFrameConstants::kExpressionsOffset - 1 * kPointerSize;
617 const int kIndexOffset = kLimitOffset - 1 * kPointerSize;
618 __ push(eax); // limit
619 __ push(Immediate(0)); // index
620
621 // Change context eagerly to get the right global object if
622 // necessary.
623 __ mov(edi, Operand(ebp, 4 * kPointerSize));
624 __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
625
626 // Compute the receiver.
627 Label call_to_object, use_global_receiver, push_receiver;
628 __ mov(ebx, Operand(ebp, 3 * kPointerSize));
629 __ test(ebx, Immediate(kSmiTagMask));
630 __ j(zero, &call_to_object);
631 __ cmp(ebx, Factory::null_value());
632 __ j(equal, &use_global_receiver);
633 __ cmp(ebx, Factory::undefined_value());
634 __ j(equal, &use_global_receiver);
635
636 // If given receiver is already a JavaScript object then there's no
637 // reason for converting it.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100638 // We don't use IsObjectJSObjectType here because we jump on success.
Steve Blocka7e24c12009-10-30 11:49:00 +0000639 __ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset));
640 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100641 __ sub(Operand(ecx), Immediate(FIRST_JS_OBJECT_TYPE));
642 __ cmp(ecx, LAST_JS_OBJECT_TYPE - FIRST_JS_OBJECT_TYPE);
643 __ j(below_equal, &push_receiver);
Steve Blocka7e24c12009-10-30 11:49:00 +0000644
645 // Convert the receiver to an object.
646 __ bind(&call_to_object);
647 __ push(ebx);
648 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
649 __ mov(ebx, Operand(eax));
650 __ jmp(&push_receiver);
651
652 // Use the current global receiver object as the receiver.
653 __ bind(&use_global_receiver);
654 const int kGlobalOffset =
655 Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
656 __ mov(ebx, FieldOperand(esi, kGlobalOffset));
Steve Blockd0582a62009-12-15 09:54:21 +0000657 __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalContextOffset));
658 __ mov(ebx, FieldOperand(ebx, kGlobalOffset));
Steve Blocka7e24c12009-10-30 11:49:00 +0000659 __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
660
661 // Push the receiver.
662 __ bind(&push_receiver);
663 __ push(ebx);
664
665 // Copy all arguments from the array to the stack.
666 Label entry, loop;
667 __ mov(eax, Operand(ebp, kIndexOffset));
668 __ jmp(&entry);
669 __ bind(&loop);
Andrei Popescu402d9372010-02-26 13:31:12 +0000670 __ mov(edx, Operand(ebp, 2 * kPointerSize)); // load arguments
Steve Blocka7e24c12009-10-30 11:49:00 +0000671
672 // Use inline caching to speed up access to arguments.
673 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
674 __ call(ic, RelocInfo::CODE_TARGET);
675 // It is important that we do not have a test instruction after the
676 // call. A test instruction after the call is used to indicate that
677 // we have generated an inline version of the keyed load. In this
678 // case, we know that we are not generating a test instruction next.
679
Andrei Popescu402d9372010-02-26 13:31:12 +0000680 // Push the nth argument.
Steve Blocka7e24c12009-10-30 11:49:00 +0000681 __ push(eax);
682
683 // Update the index on the stack and in register eax.
684 __ mov(eax, Operand(ebp, kIndexOffset));
685 __ add(Operand(eax), Immediate(1 << kSmiTagSize));
686 __ mov(Operand(ebp, kIndexOffset), eax);
687
688 __ bind(&entry);
689 __ cmp(eax, Operand(ebp, kLimitOffset));
690 __ j(not_equal, &loop);
691
692 // Invoke the function.
693 ParameterCount actual(eax);
Leon Clarkee46be812010-01-19 14:06:41 +0000694 __ SmiUntag(eax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000695 __ mov(edi, Operand(ebp, 4 * kPointerSize));
696 __ InvokeFunction(edi, actual, CALL_FUNCTION);
697
698 __ LeaveInternalFrame();
699 __ ret(3 * kPointerSize); // remove this, receiver, and arguments
700}
701
702
703// Load the built-in Array function from the current context.
704static void GenerateLoadArrayFunction(MacroAssembler* masm, Register result) {
705 // Load the global context.
706 __ mov(result, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
707 __ mov(result, FieldOperand(result, GlobalObject::kGlobalContextOffset));
708 // Load the Array function from the global context.
709 __ mov(result,
710 Operand(result, Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX)));
711}
712
713
714// Number of empty elements to allocate for an empty array.
715static const int kPreallocatedArrayElements = 4;
716
717
718// Allocate an empty JSArray. The allocated array is put into the result
719// register. If the parameter initial_capacity is larger than zero an elements
720// backing store is allocated with this size and filled with the hole values.
721// Otherwise the elements backing store is set to the empty FixedArray.
722static void AllocateEmptyJSArray(MacroAssembler* masm,
723 Register array_function,
724 Register result,
725 Register scratch1,
726 Register scratch2,
727 Register scratch3,
728 int initial_capacity,
729 Label* gc_required) {
730 ASSERT(initial_capacity >= 0);
731
732 // Load the initial map from the array function.
733 __ mov(scratch1, FieldOperand(array_function,
734 JSFunction::kPrototypeOrInitialMapOffset));
735
736 // Allocate the JSArray object together with space for a fixed array with the
737 // requested elements.
738 int size = JSArray::kSize;
739 if (initial_capacity > 0) {
740 size += FixedArray::SizeFor(initial_capacity);
741 }
742 __ AllocateInNewSpace(size,
743 result,
744 scratch2,
745 scratch3,
746 gc_required,
747 TAG_OBJECT);
748
749 // Allocated the JSArray. Now initialize the fields except for the elements
750 // array.
751 // result: JSObject
752 // scratch1: initial map
753 // scratch2: start of next object
754 __ mov(FieldOperand(result, JSObject::kMapOffset), scratch1);
755 __ mov(FieldOperand(result, JSArray::kPropertiesOffset),
756 Factory::empty_fixed_array());
757 // Field JSArray::kElementsOffset is initialized later.
758 __ mov(FieldOperand(result, JSArray::kLengthOffset), Immediate(0));
759
760 // If no storage is requested for the elements array just set the empty
761 // fixed array.
762 if (initial_capacity == 0) {
763 __ mov(FieldOperand(result, JSArray::kElementsOffset),
764 Factory::empty_fixed_array());
765 return;
766 }
767
768 // Calculate the location of the elements array and set elements array member
769 // of the JSArray.
770 // result: JSObject
771 // scratch2: start of next object
772 __ lea(scratch1, Operand(result, JSArray::kSize));
773 __ mov(FieldOperand(result, JSArray::kElementsOffset), scratch1);
774
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100775 // Initialize the FixedArray and fill it with holes. FixedArray length is
Steve Blocka7e24c12009-10-30 11:49:00 +0000776 // stored as a smi.
777 // result: JSObject
778 // scratch1: elements array
779 // scratch2: start of next object
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100780 __ mov(FieldOperand(scratch1, FixedArray::kMapOffset),
Steve Blocka7e24c12009-10-30 11:49:00 +0000781 Factory::fixed_array_map());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100782 __ mov(FieldOperand(scratch1, FixedArray::kLengthOffset),
783 Immediate(Smi::FromInt(initial_capacity)));
Steve Blocka7e24c12009-10-30 11:49:00 +0000784
785 // Fill the FixedArray with the hole value. Inline the code if short.
786 // Reconsider loop unfolding if kPreallocatedArrayElements gets changed.
787 static const int kLoopUnfoldLimit = 4;
788 ASSERT(kPreallocatedArrayElements <= kLoopUnfoldLimit);
789 if (initial_capacity <= kLoopUnfoldLimit) {
790 // Use a scratch register here to have only one reloc info when unfolding
791 // the loop.
792 __ mov(scratch3, Factory::the_hole_value());
793 for (int i = 0; i < initial_capacity; i++) {
794 __ mov(FieldOperand(scratch1,
795 FixedArray::kHeaderSize + i * kPointerSize),
796 scratch3);
797 }
798 } else {
799 Label loop, entry;
800 __ jmp(&entry);
801 __ bind(&loop);
802 __ mov(Operand(scratch1, 0), Factory::the_hole_value());
803 __ add(Operand(scratch1), Immediate(kPointerSize));
804 __ bind(&entry);
805 __ cmp(scratch1, Operand(scratch2));
806 __ j(below, &loop);
807 }
808}
809
810
811// Allocate a JSArray with the number of elements stored in a register. The
812// register array_function holds the built-in Array function and the register
813// array_size holds the size of the array as a smi. The allocated array is put
814// into the result register and beginning and end of the FixedArray elements
815// storage is put into registers elements_array and elements_array_end (see
816// below for when that is not the case). If the parameter fill_with_holes is
817// true the allocated elements backing store is filled with the hole values
818// otherwise it is left uninitialized. When the backing store is filled the
819// register elements_array is scratched.
820static void AllocateJSArray(MacroAssembler* masm,
821 Register array_function, // Array function.
Steve Block6ded16b2010-05-10 14:33:55 +0100822 Register array_size, // As a smi, cannot be 0.
Steve Blocka7e24c12009-10-30 11:49:00 +0000823 Register result,
824 Register elements_array,
825 Register elements_array_end,
826 Register scratch,
827 bool fill_with_hole,
828 Label* gc_required) {
Steve Block6ded16b2010-05-10 14:33:55 +0100829 ASSERT(scratch.is(edi)); // rep stos destination
830 ASSERT(!fill_with_hole || array_size.is(ecx)); // rep stos count
Leon Clarkef7060e22010-06-03 12:02:55 +0100831 ASSERT(!fill_with_hole || !result.is(eax)); // result is never eax
Steve Blocka7e24c12009-10-30 11:49:00 +0000832
833 // Load the initial map from the array function.
834 __ mov(elements_array,
835 FieldOperand(array_function,
836 JSFunction::kPrototypeOrInitialMapOffset));
837
Steve Blocka7e24c12009-10-30 11:49:00 +0000838 // Allocate the JSArray object together with space for a FixedArray with the
839 // requested elements.
Steve Blocka7e24c12009-10-30 11:49:00 +0000840 ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
841 __ AllocateInNewSpace(JSArray::kSize + FixedArray::kHeaderSize,
842 times_half_pointer_size, // array_size is a smi.
843 array_size,
844 result,
845 elements_array_end,
846 scratch,
847 gc_required,
848 TAG_OBJECT);
849
850 // Allocated the JSArray. Now initialize the fields except for the elements
851 // array.
852 // result: JSObject
853 // elements_array: initial map
854 // elements_array_end: start of next object
855 // array_size: size of array (smi)
Steve Blocka7e24c12009-10-30 11:49:00 +0000856 __ mov(FieldOperand(result, JSObject::kMapOffset), elements_array);
857 __ mov(elements_array, Factory::empty_fixed_array());
858 __ mov(FieldOperand(result, JSArray::kPropertiesOffset), elements_array);
859 // Field JSArray::kElementsOffset is initialized later.
860 __ mov(FieldOperand(result, JSArray::kLengthOffset), array_size);
861
862 // Calculate the location of the elements array and set elements array member
863 // of the JSArray.
864 // result: JSObject
865 // elements_array_end: start of next object
866 // array_size: size of array (smi)
867 __ lea(elements_array, Operand(result, JSArray::kSize));
868 __ mov(FieldOperand(result, JSArray::kElementsOffset), elements_array);
869
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100870 // Initialize the fixed array. FixedArray length is stored as a smi.
Steve Blocka7e24c12009-10-30 11:49:00 +0000871 // result: JSObject
872 // elements_array: elements array
873 // elements_array_end: start of next object
874 // array_size: size of array (smi)
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100875 __ mov(FieldOperand(elements_array, FixedArray::kMapOffset),
Steve Blocka7e24c12009-10-30 11:49:00 +0000876 Factory::fixed_array_map());
Steve Blocka7e24c12009-10-30 11:49:00 +0000877 // For non-empty JSArrays the length of the FixedArray and the JSArray is the
878 // same.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100879 __ mov(FieldOperand(elements_array, FixedArray::kLengthOffset), array_size);
Steve Blocka7e24c12009-10-30 11:49:00 +0000880
881 // Fill the allocated FixedArray with the hole value if requested.
882 // result: JSObject
883 // elements_array: elements array
Steve Blocka7e24c12009-10-30 11:49:00 +0000884 if (fill_with_hole) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100885 __ SmiUntag(array_size);
Steve Block6ded16b2010-05-10 14:33:55 +0100886 __ lea(edi, Operand(elements_array,
887 FixedArray::kHeaderSize - kHeapObjectTag));
Steve Block6ded16b2010-05-10 14:33:55 +0100888 __ mov(eax, Factory::the_hole_value());
Steve Block6ded16b2010-05-10 14:33:55 +0100889 __ cld();
Leon Clarkef7060e22010-06-03 12:02:55 +0100890 // Do not use rep stos when filling less than kRepStosThreshold
891 // words.
892 const int kRepStosThreshold = 16;
893 Label loop, entry, done;
894 __ cmp(ecx, kRepStosThreshold);
895 __ j(below, &loop); // Note: ecx > 0.
Steve Block6ded16b2010-05-10 14:33:55 +0100896 __ rep_stos();
Leon Clarkef7060e22010-06-03 12:02:55 +0100897 __ jmp(&done);
898 __ bind(&loop);
899 __ stos();
900 __ bind(&entry);
901 __ cmp(edi, Operand(elements_array_end));
902 __ j(below, &loop);
903 __ bind(&done);
Steve Blocka7e24c12009-10-30 11:49:00 +0000904 }
905}
906
907
908// Create a new array for the built-in Array function. This function allocates
909// the JSArray object and the FixedArray elements array and initializes these.
910// If the Array cannot be constructed in native code the runtime is called. This
911// function assumes the following state:
912// edi: constructor (built-in Array function)
913// eax: argc
914// esp[0]: return address
915// esp[4]: last argument
916// This function is used for both construct and normal calls of Array. Whether
917// it is a construct call or not is indicated by the construct_call parameter.
918// The only difference between handling a construct call and a normal call is
919// that for a construct call the constructor function in edi needs to be
920// preserved for entering the generic code. In both cases argc in eax needs to
921// be preserved.
922static void ArrayNativeCode(MacroAssembler* masm,
923 bool construct_call,
Steve Blockd0582a62009-12-15 09:54:21 +0000924 Label* call_generic_code) {
Steve Block6ded16b2010-05-10 14:33:55 +0100925 Label argc_one_or_more, argc_two_or_more, prepare_generic_code_call,
926 empty_array, not_empty_array;
Steve Blocka7e24c12009-10-30 11:49:00 +0000927
928 // Push the constructor and argc. No need to tag argc as a smi, as there will
929 // be no garbage collection with this on the stack.
930 int push_count = 0;
931 if (construct_call) {
932 push_count++;
933 __ push(edi);
934 }
935 push_count++;
936 __ push(eax);
937
938 // Check for array construction with zero arguments.
939 __ test(eax, Operand(eax));
940 __ j(not_zero, &argc_one_or_more);
941
Steve Block6ded16b2010-05-10 14:33:55 +0100942 __ bind(&empty_array);
Steve Blocka7e24c12009-10-30 11:49:00 +0000943 // Handle construction of an empty array.
944 AllocateEmptyJSArray(masm,
945 edi,
946 eax,
947 ebx,
948 ecx,
949 edi,
950 kPreallocatedArrayElements,
951 &prepare_generic_code_call);
952 __ IncrementCounter(&Counters::array_function_native, 1);
953 __ pop(ebx);
954 if (construct_call) {
955 __ pop(edi);
956 }
957 __ ret(kPointerSize);
958
959 // Check for one argument. Bail out if argument is not smi or if it is
960 // negative.
961 __ bind(&argc_one_or_more);
962 __ cmp(eax, 1);
963 __ j(not_equal, &argc_two_or_more);
964 ASSERT(kSmiTag == 0);
Steve Block6ded16b2010-05-10 14:33:55 +0100965 __ mov(ecx, Operand(esp, (push_count + 1) * kPointerSize));
966 __ test(ecx, Operand(ecx));
967 __ j(not_zero, &not_empty_array);
968
969 // The single argument passed is zero, so we jump to the code above used to
970 // handle the case of no arguments passed. To adapt the stack for that we move
971 // the return address and the pushed constructor (if pushed) one stack slot up
972 // thereby removing the passed argument. Argc is also on the stack - at the
973 // bottom - and it needs to be changed from 1 to 0 to have the call into the
974 // runtime system work in case a GC is required.
975 for (int i = push_count; i > 0; i--) {
976 __ mov(eax, Operand(esp, i * kPointerSize));
977 __ mov(Operand(esp, (i + 1) * kPointerSize), eax);
978 }
979 __ add(Operand(esp), Immediate(2 * kPointerSize)); // Drop two stack slots.
980 __ push(Immediate(0)); // Treat this as a call with argc of zero.
981 __ jmp(&empty_array);
982
983 __ bind(&not_empty_array);
984 __ test(ecx, Immediate(kIntptrSignBit | kSmiTagMask));
Steve Blocka7e24c12009-10-30 11:49:00 +0000985 __ j(not_zero, &prepare_generic_code_call);
986
987 // Handle construction of an empty array of a certain size. Get the size from
988 // the stack and bail out if size is to large to actually allocate an elements
989 // array.
Steve Block6ded16b2010-05-10 14:33:55 +0100990 __ cmp(ecx, JSObject::kInitialMaxFastElementArray << kSmiTagSize);
Steve Blocka7e24c12009-10-30 11:49:00 +0000991 __ j(greater_equal, &prepare_generic_code_call);
992
993 // edx: array_size (smi)
994 // edi: constructor
Steve Block6ded16b2010-05-10 14:33:55 +0100995 // esp[0]: argc (cannot be 0 here)
Steve Blocka7e24c12009-10-30 11:49:00 +0000996 // esp[4]: constructor (only if construct_call)
997 // esp[8]: return address
998 // esp[C]: argument
999 AllocateJSArray(masm,
1000 edi,
Steve Block6ded16b2010-05-10 14:33:55 +01001001 ecx,
Steve Blocka7e24c12009-10-30 11:49:00 +00001002 ebx,
Leon Clarkef7060e22010-06-03 12:02:55 +01001003 eax,
Steve Block6ded16b2010-05-10 14:33:55 +01001004 edx,
Steve Blocka7e24c12009-10-30 11:49:00 +00001005 edi,
1006 true,
1007 &prepare_generic_code_call);
1008 __ IncrementCounter(&Counters::array_function_native, 1);
Leon Clarkef7060e22010-06-03 12:02:55 +01001009 __ mov(eax, ebx);
Steve Blocka7e24c12009-10-30 11:49:00 +00001010 __ pop(ebx);
1011 if (construct_call) {
1012 __ pop(edi);
1013 }
1014 __ ret(2 * kPointerSize);
1015
1016 // Handle construction of an array from a list of arguments.
1017 __ bind(&argc_two_or_more);
1018 ASSERT(kSmiTag == 0);
Leon Clarkee46be812010-01-19 14:06:41 +00001019 __ SmiTag(eax); // Convet argc to a smi.
Steve Blocka7e24c12009-10-30 11:49:00 +00001020 // eax: array_size (smi)
1021 // edi: constructor
1022 // esp[0] : argc
1023 // esp[4]: constructor (only if construct_call)
1024 // esp[8] : return address
1025 // esp[C] : last argument
1026 AllocateJSArray(masm,
1027 edi,
1028 eax,
1029 ebx,
1030 ecx,
1031 edx,
1032 edi,
1033 false,
1034 &prepare_generic_code_call);
1035 __ IncrementCounter(&Counters::array_function_native, 1);
1036 __ mov(eax, ebx);
1037 __ pop(ebx);
1038 if (construct_call) {
1039 __ pop(edi);
1040 }
1041 __ push(eax);
1042 // eax: JSArray
1043 // ebx: argc
1044 // edx: elements_array_end (untagged)
1045 // esp[0]: JSArray
1046 // esp[4]: return address
1047 // esp[8]: last argument
1048
1049 // Location of the last argument
1050 __ lea(edi, Operand(esp, 2 * kPointerSize));
1051
1052 // Location of the first array element (Parameter fill_with_holes to
1053 // AllocateJSArrayis false, so the FixedArray is returned in ecx).
1054 __ lea(edx, Operand(ecx, FixedArray::kHeaderSize - kHeapObjectTag));
1055
1056 // ebx: argc
1057 // edx: location of the first array element
1058 // edi: location of the last argument
1059 // esp[0]: JSArray
1060 // esp[4]: return address
1061 // esp[8]: last argument
1062 Label loop, entry;
1063 __ mov(ecx, ebx);
1064 __ jmp(&entry);
1065 __ bind(&loop);
1066 __ mov(eax, Operand(edi, ecx, times_pointer_size, 0));
1067 __ mov(Operand(edx, 0), eax);
1068 __ add(Operand(edx), Immediate(kPointerSize));
1069 __ bind(&entry);
1070 __ dec(ecx);
1071 __ j(greater_equal, &loop);
1072
1073 // Remove caller arguments from the stack and return.
1074 // ebx: argc
1075 // esp[0]: JSArray
1076 // esp[4]: return address
1077 // esp[8]: last argument
1078 __ pop(eax);
1079 __ pop(ecx);
1080 __ lea(esp, Operand(esp, ebx, times_pointer_size, 1 * kPointerSize));
1081 __ push(ecx);
1082 __ ret(0);
1083
1084 // Restore argc and constructor before running the generic code.
1085 __ bind(&prepare_generic_code_call);
1086 __ pop(eax);
1087 if (construct_call) {
1088 __ pop(edi);
1089 }
1090 __ jmp(call_generic_code);
1091}
1092
1093
1094void Builtins::Generate_ArrayCode(MacroAssembler* masm) {
1095 // ----------- S t a t e -------------
1096 // -- eax : argc
1097 // -- esp[0] : return address
1098 // -- esp[4] : last argument
1099 // -----------------------------------
Kristian Monsen25f61362010-05-21 11:50:48 +01001100 Label generic_array_code;
Steve Blocka7e24c12009-10-30 11:49:00 +00001101
1102 // Get the Array function.
1103 GenerateLoadArrayFunction(masm, edi);
1104
1105 if (FLAG_debug_code) {
1106 // Initial map for the builtin Array function shoud be a map.
1107 __ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
1108 // Will both indicate a NULL and a Smi.
1109 __ test(ebx, Immediate(kSmiTagMask));
1110 __ Assert(not_zero, "Unexpected initial map for Array function");
1111 __ CmpObjectType(ebx, MAP_TYPE, ecx);
1112 __ Assert(equal, "Unexpected initial map for Array function");
1113 }
1114
1115 // Run the native code for the Array function called as a normal function.
1116 ArrayNativeCode(masm, false, &generic_array_code);
1117
1118 // Jump to the generic array code in case the specialized code cannot handle
1119 // the construction.
1120 __ bind(&generic_array_code);
1121 Code* code = Builtins::builtin(Builtins::ArrayCodeGeneric);
1122 Handle<Code> array_code(code);
1123 __ jmp(array_code, RelocInfo::CODE_TARGET);
1124}
1125
1126
1127void Builtins::Generate_ArrayConstructCode(MacroAssembler* masm) {
1128 // ----------- S t a t e -------------
1129 // -- eax : argc
1130 // -- edi : constructor
1131 // -- esp[0] : return address
1132 // -- esp[4] : last argument
1133 // -----------------------------------
1134 Label generic_constructor;
1135
1136 if (FLAG_debug_code) {
1137 // The array construct code is only set for the builtin Array function which
1138 // does always have a map.
1139 GenerateLoadArrayFunction(masm, ebx);
1140 __ cmp(edi, Operand(ebx));
1141 __ Assert(equal, "Unexpected Array function");
1142 // Initial map for the builtin Array function should be a map.
1143 __ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
1144 // Will both indicate a NULL and a Smi.
1145 __ test(ebx, Immediate(kSmiTagMask));
1146 __ Assert(not_zero, "Unexpected initial map for Array function");
1147 __ CmpObjectType(ebx, MAP_TYPE, ecx);
1148 __ Assert(equal, "Unexpected initial map for Array function");
1149 }
1150
1151 // Run the native code for the Array function called as constructor.
1152 ArrayNativeCode(masm, true, &generic_constructor);
1153
1154 // Jump to the generic construct code in case the specialized code cannot
1155 // handle the construction.
1156 __ bind(&generic_constructor);
1157 Code* code = Builtins::builtin(Builtins::JSConstructStubGeneric);
1158 Handle<Code> generic_construct_stub(code);
1159 __ jmp(generic_construct_stub, RelocInfo::CODE_TARGET);
1160}
1161
1162
1163static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
1164 __ push(ebp);
1165 __ mov(ebp, Operand(esp));
1166
1167 // Store the arguments adaptor context sentinel.
1168 __ push(Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
1169
1170 // Push the function on the stack.
1171 __ push(edi);
1172
1173 // Preserve the number of arguments on the stack. Must preserve both
1174 // eax and ebx because these registers are used when copying the
1175 // arguments and the receiver.
1176 ASSERT(kSmiTagSize == 1);
1177 __ lea(ecx, Operand(eax, eax, times_1, kSmiTag));
1178 __ push(ecx);
1179}
1180
1181
1182static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) {
1183 // Retrieve the number of arguments from the stack.
1184 __ mov(ebx, Operand(ebp, ArgumentsAdaptorFrameConstants::kLengthOffset));
1185
1186 // Leave the frame.
1187 __ leave();
1188
1189 // Remove caller arguments from the stack.
1190 ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
1191 __ pop(ecx);
1192 __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver
1193 __ push(ecx);
1194}
1195
1196
1197void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
1198 // ----------- S t a t e -------------
1199 // -- eax : actual number of arguments
1200 // -- ebx : expected number of arguments
1201 // -- edx : code entry to call
1202 // -----------------------------------
1203
1204 Label invoke, dont_adapt_arguments;
1205 __ IncrementCounter(&Counters::arguments_adaptors, 1);
1206
1207 Label enough, too_few;
1208 __ cmp(eax, Operand(ebx));
1209 __ j(less, &too_few);
1210 __ cmp(ebx, SharedFunctionInfo::kDontAdaptArgumentsSentinel);
1211 __ j(equal, &dont_adapt_arguments);
1212
1213 { // Enough parameters: Actual >= expected.
1214 __ bind(&enough);
1215 EnterArgumentsAdaptorFrame(masm);
1216
1217 // Copy receiver and all expected arguments.
1218 const int offset = StandardFrameConstants::kCallerSPOffset;
1219 __ lea(eax, Operand(ebp, eax, times_4, offset));
1220 __ mov(ecx, -1); // account for receiver
1221
1222 Label copy;
1223 __ bind(&copy);
1224 __ inc(ecx);
1225 __ push(Operand(eax, 0));
1226 __ sub(Operand(eax), Immediate(kPointerSize));
1227 __ cmp(ecx, Operand(ebx));
1228 __ j(less, &copy);
1229 __ jmp(&invoke);
1230 }
1231
1232 { // Too few parameters: Actual < expected.
1233 __ bind(&too_few);
1234 EnterArgumentsAdaptorFrame(masm);
1235
1236 // Copy receiver and all actual arguments.
1237 const int offset = StandardFrameConstants::kCallerSPOffset;
1238 __ lea(edi, Operand(ebp, eax, times_4, offset));
1239 __ mov(ecx, -1); // account for receiver
1240
1241 Label copy;
1242 __ bind(&copy);
1243 __ inc(ecx);
1244 __ push(Operand(edi, 0));
1245 __ sub(Operand(edi), Immediate(kPointerSize));
1246 __ cmp(ecx, Operand(eax));
1247 __ j(less, &copy);
1248
1249 // Fill remaining expected arguments with undefined values.
1250 Label fill;
1251 __ bind(&fill);
1252 __ inc(ecx);
1253 __ push(Immediate(Factory::undefined_value()));
1254 __ cmp(ecx, Operand(ebx));
1255 __ j(less, &fill);
1256
1257 // Restore function pointer.
1258 __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
1259 }
1260
1261 // Call the entry point.
1262 __ bind(&invoke);
1263 __ call(Operand(edx));
1264
1265 // Leave frame and return.
1266 LeaveArgumentsAdaptorFrame(masm);
1267 __ ret(0);
1268
1269 // -------------------------------------------
1270 // Dont adapt arguments.
1271 // -------------------------------------------
1272 __ bind(&dont_adapt_arguments);
1273 __ jmp(Operand(edx));
1274}
1275
1276
1277#undef __
1278
1279} } // namespace v8::internal
Leon Clarkef7060e22010-06-03 12:02:55 +01001280
1281#endif // V8_TARGET_ARCH_IA32