blob: 8099febb7f370865428d48e8c8f9be7aef859e8b [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 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"
Leon Clarkef7060e22010-06-03 12:02:55 +010029
30#if defined(V8_TARGET_ARCH_X64)
31
Steve Blocka7e24c12009-10-30 11:49:00 +000032#include "codegen-inl.h"
33#include "macro-assembler.h"
34
35namespace v8 {
36namespace internal {
37
38#define __ ACCESS_MASM(masm)
39
Steve Blocka7e24c12009-10-30 11:49:00 +000040
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 // -- rax : number of arguments excluding receiver
46 // -- rdi : called function (only guaranteed when
47 // extra_args requires it)
48 // -- rsi : context
49 // -- rsp[0] : return address
50 // -- rsp[8] : last argument
51 // -- ...
52 // -- rsp[8 * argc] : first argument (argc == rax)
53 // -- rsp[8 * (argc +1)] : receiver
54 // -----------------------------------
55
56 // Insert extra arguments.
57 int num_extra_args = 0;
58 if (extra_args == NEEDS_CALLED_FUNCTION) {
59 num_extra_args = 1;
60 __ pop(kScratchRegister); // Save return address.
61 __ push(rdi);
62 __ push(kScratchRegister); // Restore return address.
63 } else {
64 ASSERT(extra_args == NO_EXTRA_ARGUMENTS);
65 }
66
Steve Block6ded16b2010-05-10 14:33:55 +010067 // JumpToExternalReference expects rax to contain the number of arguments
Leon Clarkee46be812010-01-19 14:06:41 +000068 // including the receiver and the extra arguments.
69 __ addq(rax, Immediate(num_extra_args + 1));
Steve Block6ded16b2010-05-10 14:33:55 +010070 __ JumpToExternalReference(ExternalReference(id), 1);
Steve Blocka7e24c12009-10-30 11:49:00 +000071}
72
73
74static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
75 __ push(rbp);
76 __ movq(rbp, rsp);
77
78 // Store the arguments adaptor context sentinel.
Steve Block3ce2e202009-11-05 08:53:23 +000079 __ Push(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
Steve Blocka7e24c12009-10-30 11:49:00 +000080
81 // Push the function on the stack.
82 __ push(rdi);
83
84 // Preserve the number of arguments on the stack. Must preserve both
85 // rax and rbx because these registers are used when copying the
86 // arguments and the receiver.
87 __ Integer32ToSmi(rcx, rax);
88 __ push(rcx);
89}
90
91
92static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) {
93 // Retrieve the number of arguments from the stack. Number is a Smi.
94 __ movq(rbx, Operand(rbp, ArgumentsAdaptorFrameConstants::kLengthOffset));
95
96 // Leave the frame.
97 __ movq(rsp, rbp);
98 __ pop(rbp);
99
100 // Remove caller arguments from the stack.
Steve Blocka7e24c12009-10-30 11:49:00 +0000101 __ pop(rcx);
Steve Block3ce2e202009-11-05 08:53:23 +0000102 SmiIndex index = masm->SmiToIndex(rbx, rbx, kPointerSizeLog2);
103 __ lea(rsp, Operand(rsp, index.reg, index.scale, 1 * kPointerSize));
Steve Blocka7e24c12009-10-30 11:49:00 +0000104 __ push(rcx);
105}
106
107
108void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
109 // ----------- S t a t e -------------
110 // -- rax : actual number of arguments
111 // -- rbx : expected number of arguments
112 // -- rdx : code entry to call
113 // -----------------------------------
114
115 Label invoke, dont_adapt_arguments;
116 __ IncrementCounter(&Counters::arguments_adaptors, 1);
117
118 Label enough, too_few;
119 __ cmpq(rax, rbx);
120 __ j(less, &too_few);
121 __ cmpq(rbx, Immediate(SharedFunctionInfo::kDontAdaptArgumentsSentinel));
122 __ j(equal, &dont_adapt_arguments);
123
124 { // Enough parameters: Actual >= expected.
125 __ bind(&enough);
126 EnterArgumentsAdaptorFrame(masm);
127
128 // Copy receiver and all expected arguments.
129 const int offset = StandardFrameConstants::kCallerSPOffset;
130 __ lea(rax, Operand(rbp, rax, times_pointer_size, offset));
131 __ movq(rcx, Immediate(-1)); // account for receiver
132
133 Label copy;
134 __ bind(&copy);
135 __ incq(rcx);
136 __ push(Operand(rax, 0));
137 __ subq(rax, Immediate(kPointerSize));
138 __ cmpq(rcx, rbx);
139 __ j(less, &copy);
140 __ jmp(&invoke);
141 }
142
143 { // Too few parameters: Actual < expected.
144 __ bind(&too_few);
145 EnterArgumentsAdaptorFrame(masm);
146
147 // Copy receiver and all actual arguments.
148 const int offset = StandardFrameConstants::kCallerSPOffset;
149 __ lea(rdi, Operand(rbp, rax, times_pointer_size, offset));
150 __ movq(rcx, Immediate(-1)); // account for receiver
151
152 Label copy;
153 __ bind(&copy);
154 __ incq(rcx);
155 __ push(Operand(rdi, 0));
156 __ subq(rdi, Immediate(kPointerSize));
157 __ cmpq(rcx, rax);
158 __ j(less, &copy);
159
160 // Fill remaining expected arguments with undefined values.
161 Label fill;
162 __ LoadRoot(kScratchRegister, Heap::kUndefinedValueRootIndex);
163 __ bind(&fill);
164 __ incq(rcx);
165 __ push(kScratchRegister);
166 __ cmpq(rcx, rbx);
167 __ j(less, &fill);
168
169 // Restore function pointer.
170 __ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
171 }
172
173 // Call the entry point.
174 __ bind(&invoke);
175 __ call(rdx);
176
177 // Leave frame and return.
178 LeaveArgumentsAdaptorFrame(masm);
179 __ ret(0);
180
181 // -------------------------------------------
182 // Dont adapt arguments.
183 // -------------------------------------------
184 __ bind(&dont_adapt_arguments);
185 __ jmp(rdx);
186}
187
188
189void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
190 // Stack Layout:
Andrei Popescu402d9372010-02-26 13:31:12 +0000191 // rsp[0]: Return address
192 // rsp[1]: Argument n
193 // rsp[2]: Argument n-1
Steve Blocka7e24c12009-10-30 11:49:00 +0000194 // ...
Andrei Popescu402d9372010-02-26 13:31:12 +0000195 // rsp[n]: Argument 1
196 // rsp[n+1]: Receiver (function to call)
Steve Blocka7e24c12009-10-30 11:49:00 +0000197 //
Andrei Popescu402d9372010-02-26 13:31:12 +0000198 // rax contains the number of arguments, n, not counting the receiver.
Steve Blocka7e24c12009-10-30 11:49:00 +0000199 //
200 // 1. Make sure we have at least one argument.
201 { Label done;
202 __ testq(rax, rax);
203 __ j(not_zero, &done);
204 __ pop(rbx);
205 __ Push(Factory::undefined_value());
206 __ push(rbx);
207 __ incq(rax);
208 __ bind(&done);
209 }
210
Andrei Popescu402d9372010-02-26 13:31:12 +0000211 // 2. Get the function to call (passed as receiver) from the stack, check
212 // if it is a function.
213 Label non_function;
214 // The function to call is at position n+1 on the stack.
215 __ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize));
216 __ JumpIfSmi(rdi, &non_function);
217 __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
218 __ j(not_equal, &non_function);
Steve Blocka7e24c12009-10-30 11:49:00 +0000219
Andrei Popescu402d9372010-02-26 13:31:12 +0000220 // 3a. Patch the first argument if necessary when calling a function.
221 Label shift_arguments;
222 { Label convert_to_object, use_global_receiver, patch_receiver;
223 // Change context eagerly in case we need the global receiver.
Steve Blocka7e24c12009-10-30 11:49:00 +0000224 __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
225
Steve Blocka7e24c12009-10-30 11:49:00 +0000226 __ movq(rbx, Operand(rsp, rax, times_pointer_size, 0));
Andrei Popescu402d9372010-02-26 13:31:12 +0000227 __ JumpIfSmi(rbx, &convert_to_object);
Steve Blocka7e24c12009-10-30 11:49:00 +0000228
229 __ CompareRoot(rbx, Heap::kNullValueRootIndex);
230 __ j(equal, &use_global_receiver);
231 __ CompareRoot(rbx, Heap::kUndefinedValueRootIndex);
232 __ j(equal, &use_global_receiver);
233
234 __ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, rcx);
Andrei Popescu402d9372010-02-26 13:31:12 +0000235 __ j(below, &convert_to_object);
Steve Blocka7e24c12009-10-30 11:49:00 +0000236 __ CmpInstanceType(rcx, LAST_JS_OBJECT_TYPE);
Andrei Popescu402d9372010-02-26 13:31:12 +0000237 __ j(below_equal, &shift_arguments);
Steve Blocka7e24c12009-10-30 11:49:00 +0000238
Andrei Popescu402d9372010-02-26 13:31:12 +0000239 __ bind(&convert_to_object);
240 __ EnterInternalFrame(); // In order to preserve argument count.
Steve Blocka7e24c12009-10-30 11:49:00 +0000241 __ Integer32ToSmi(rax, rax);
242 __ push(rax);
243
Steve Blocka7e24c12009-10-30 11:49:00 +0000244 __ push(rbx);
245 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
246 __ movq(rbx, rax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000247
Steve Blocka7e24c12009-10-30 11:49:00 +0000248 __ pop(rax);
249 __ SmiToInteger32(rax, rax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000250 __ LeaveInternalFrame();
Andrei Popescu402d9372010-02-26 13:31:12 +0000251 // Restore the function to rdi.
252 __ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize));
Steve Blocka7e24c12009-10-30 11:49:00 +0000253 __ jmp(&patch_receiver);
254
Andrei Popescu402d9372010-02-26 13:31:12 +0000255 // Use the global receiver object from the called function as the
256 // receiver.
Steve Blocka7e24c12009-10-30 11:49:00 +0000257 __ bind(&use_global_receiver);
258 const int kGlobalIndex =
259 Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
260 __ movq(rbx, FieldOperand(rsi, kGlobalIndex));
Steve Blockd0582a62009-12-15 09:54:21 +0000261 __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalContextOffset));
262 __ movq(rbx, FieldOperand(rbx, kGlobalIndex));
Steve Blocka7e24c12009-10-30 11:49:00 +0000263 __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
264
265 __ bind(&patch_receiver);
266 __ movq(Operand(rsp, rax, times_pointer_size, 0), rbx);
267
Andrei Popescu402d9372010-02-26 13:31:12 +0000268 __ jmp(&shift_arguments);
Steve Blocka7e24c12009-10-30 11:49:00 +0000269 }
270
Andrei Popescu402d9372010-02-26 13:31:12 +0000271
272 // 3b. Patch the first argument when calling a non-function. The
273 // CALL_NON_FUNCTION builtin expects the non-function callee as
274 // receiver, so overwrite the first argument which will ultimately
275 // become the receiver.
276 __ bind(&non_function);
277 __ movq(Operand(rsp, rax, times_pointer_size, 0), rdi);
278 __ xor_(rdi, rdi);
279
280 // 4. Shift arguments and return address one slot down on the stack
281 // (overwriting the original receiver). Adjust argument count to make
282 // the original first argument the new receiver.
283 __ bind(&shift_arguments);
Steve Blocka7e24c12009-10-30 11:49:00 +0000284 { Label loop;
Andrei Popescu402d9372010-02-26 13:31:12 +0000285 __ movq(rcx, rax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000286 __ bind(&loop);
287 __ movq(rbx, Operand(rsp, rcx, times_pointer_size, 0));
288 __ movq(Operand(rsp, rcx, times_pointer_size, 1 * kPointerSize), rbx);
289 __ decq(rcx);
Andrei Popescu402d9372010-02-26 13:31:12 +0000290 __ j(not_sign, &loop); // While non-negative (to copy return address).
291 __ pop(rbx); // Discard copy of return address.
292 __ decq(rax); // One fewer argument (first argument is new receiver).
Steve Blocka7e24c12009-10-30 11:49:00 +0000293 }
294
Andrei Popescu402d9372010-02-26 13:31:12 +0000295 // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
296 { Label function;
Steve Blocka7e24c12009-10-30 11:49:00 +0000297 __ testq(rdi, rdi);
Andrei Popescu402d9372010-02-26 13:31:12 +0000298 __ j(not_zero, &function);
Steve Blocka7e24c12009-10-30 11:49:00 +0000299 __ xor_(rbx, rbx);
300 __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
Steve Blocka7e24c12009-10-30 11:49:00 +0000301 __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
302 RelocInfo::CODE_TARGET);
Andrei Popescu402d9372010-02-26 13:31:12 +0000303 __ bind(&function);
Steve Blocka7e24c12009-10-30 11:49:00 +0000304 }
305
Andrei Popescu402d9372010-02-26 13:31:12 +0000306 // 5b. Get the code to call from the function and check that the number of
307 // expected arguments matches what we're providing. If so, jump
308 // (tail-call) to the code in register edx without checking arguments.
309 __ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
310 __ movsxlq(rbx,
311 FieldOperand(rdx, SharedFunctionInfo::kFormalParameterCountOffset));
312 __ movq(rdx, FieldOperand(rdx, SharedFunctionInfo::kCodeOffset));
313 __ lea(rdx, FieldOperand(rdx, Code::kHeaderSize));
314 __ cmpq(rax, rbx);
315 __ j(not_equal,
316 Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
317 RelocInfo::CODE_TARGET);
318
Steve Blocka7e24c12009-10-30 11:49:00 +0000319 ParameterCount expected(0);
320 __ InvokeCode(rdx, expected, expected, JUMP_FUNCTION);
321}
322
323
324void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
325 // Stack at entry:
326 // rsp: return address
327 // rsp+8: arguments
328 // rsp+16: receiver ("this")
329 // rsp+24: function
330 __ EnterInternalFrame();
331 // Stack frame:
332 // rbp: Old base pointer
333 // rbp[1]: return address
334 // rbp[2]: function arguments
335 // rbp[3]: receiver
336 // rbp[4]: function
337 static const int kArgumentsOffset = 2 * kPointerSize;
338 static const int kReceiverOffset = 3 * kPointerSize;
339 static const int kFunctionOffset = 4 * kPointerSize;
340 __ push(Operand(rbp, kFunctionOffset));
341 __ push(Operand(rbp, kArgumentsOffset));
342 __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
343
Steve Blockd0582a62009-12-15 09:54:21 +0000344 // Check the stack for overflow. We are not trying need to catch
345 // interruptions (e.g. debug break and preemption) here, so the "real stack
346 // limit" is checked.
347 Label okay;
348 __ LoadRoot(kScratchRegister, Heap::kRealStackLimitRootIndex);
349 __ movq(rcx, rsp);
350 // Make rcx the space we have left. The stack might already be overflowed
351 // here which will cause rcx to become negative.
352 __ subq(rcx, kScratchRegister);
353 // Make rdx the space we need for the array when it is unrolled onto the
354 // stack.
355 __ PositiveSmiTimesPowerOfTwoToInteger64(rdx, rax, kPointerSizeLog2);
356 // Check if the arguments will overflow the stack.
357 __ cmpq(rcx, rdx);
358 __ j(greater, &okay); // Signed comparison.
Steve Blocka7e24c12009-10-30 11:49:00 +0000359
Steve Blockd0582a62009-12-15 09:54:21 +0000360 // Out of stack space.
361 __ push(Operand(rbp, kFunctionOffset));
362 __ push(rax);
363 __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION);
364 __ bind(&okay);
365 // End of stack check.
Steve Blocka7e24c12009-10-30 11:49:00 +0000366
367 // Push current index and limit.
368 const int kLimitOffset =
369 StandardFrameConstants::kExpressionsOffset - 1 * kPointerSize;
370 const int kIndexOffset = kLimitOffset - 1 * kPointerSize;
371 __ push(rax); // limit
372 __ push(Immediate(0)); // index
373
374 // Change context eagerly to get the right global object if
375 // necessary.
376 __ movq(rdi, Operand(rbp, kFunctionOffset));
377 __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
378
379 // Compute the receiver.
380 Label call_to_object, use_global_receiver, push_receiver;
381 __ movq(rbx, Operand(rbp, kReceiverOffset));
382 __ JumpIfSmi(rbx, &call_to_object);
383 __ CompareRoot(rbx, Heap::kNullValueRootIndex);
384 __ j(equal, &use_global_receiver);
385 __ CompareRoot(rbx, Heap::kUndefinedValueRootIndex);
386 __ j(equal, &use_global_receiver);
387
388 // If given receiver is already a JavaScript object then there's no
389 // reason for converting it.
390 __ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, rcx);
391 __ j(below, &call_to_object);
392 __ CmpInstanceType(rcx, LAST_JS_OBJECT_TYPE);
393 __ j(below_equal, &push_receiver);
394
395 // Convert the receiver to an object.
396 __ bind(&call_to_object);
397 __ push(rbx);
398 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
399 __ movq(rbx, rax);
400 __ jmp(&push_receiver);
401
402 // Use the current global receiver object as the receiver.
403 __ bind(&use_global_receiver);
404 const int kGlobalOffset =
405 Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
406 __ movq(rbx, FieldOperand(rsi, kGlobalOffset));
Steve Blockd0582a62009-12-15 09:54:21 +0000407 __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalContextOffset));
408 __ movq(rbx, FieldOperand(rbx, kGlobalOffset));
Steve Blocka7e24c12009-10-30 11:49:00 +0000409 __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
410
411 // Push the receiver.
412 __ bind(&push_receiver);
413 __ push(rbx);
414
415 // Copy all arguments from the array to the stack.
416 Label entry, loop;
417 __ movq(rax, Operand(rbp, kIndexOffset));
418 __ jmp(&entry);
419 __ bind(&loop);
420 __ movq(rcx, Operand(rbp, kArgumentsOffset)); // load arguments
421 __ push(rcx);
422 __ push(rax);
423
424 // Use inline caching to speed up access to arguments.
425 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
426 __ Call(ic, RelocInfo::CODE_TARGET);
427 // It is important that we do not have a test instruction after the
428 // call. A test instruction after the call is used to indicate that
429 // we have generated an inline version of the keyed load. In this
430 // case, we know that we are not generating a test instruction next.
431
432 // Remove IC arguments from the stack and push the nth argument.
433 __ addq(rsp, Immediate(2 * kPointerSize));
434 __ push(rax);
435
436 // Update the index on the stack and in register rax.
437 __ movq(rax, Operand(rbp, kIndexOffset));
Steve Block3ce2e202009-11-05 08:53:23 +0000438 __ SmiAddConstant(rax, rax, Smi::FromInt(1));
Steve Blocka7e24c12009-10-30 11:49:00 +0000439 __ movq(Operand(rbp, kIndexOffset), rax);
440
441 __ bind(&entry);
442 __ cmpq(rax, Operand(rbp, kLimitOffset));
443 __ j(not_equal, &loop);
444
445 // Invoke the function.
446 ParameterCount actual(rax);
447 __ SmiToInteger32(rax, rax);
448 __ movq(rdi, Operand(rbp, kFunctionOffset));
449 __ InvokeFunction(rdi, actual, CALL_FUNCTION);
450
451 __ LeaveInternalFrame();
452 __ ret(3 * kPointerSize); // remove function, receiver, and arguments
453}
454
455
456// Load the built-in Array function from the current context.
457static void GenerateLoadArrayFunction(MacroAssembler* masm, Register result) {
458 // Load the global context.
459 __ movq(result, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
460 __ movq(result, FieldOperand(result, GlobalObject::kGlobalContextOffset));
461 // Load the Array function from the global context.
462 __ movq(result,
463 Operand(result, Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX)));
464}
465
466
467// Number of empty elements to allocate for an empty array.
468static const int kPreallocatedArrayElements = 4;
469
470
471// Allocate an empty JSArray. The allocated array is put into the result
472// register. If the parameter initial_capacity is larger than zero an elements
473// backing store is allocated with this size and filled with the hole values.
474// Otherwise the elements backing store is set to the empty FixedArray.
475static void AllocateEmptyJSArray(MacroAssembler* masm,
476 Register array_function,
477 Register result,
478 Register scratch1,
479 Register scratch2,
480 Register scratch3,
481 int initial_capacity,
482 Label* gc_required) {
483 ASSERT(initial_capacity >= 0);
484
485 // Load the initial map from the array function.
486 __ movq(scratch1, FieldOperand(array_function,
487 JSFunction::kPrototypeOrInitialMapOffset));
488
489 // Allocate the JSArray object together with space for a fixed array with the
490 // requested elements.
491 int size = JSArray::kSize;
492 if (initial_capacity > 0) {
493 size += FixedArray::SizeFor(initial_capacity);
494 }
495 __ AllocateInNewSpace(size,
496 result,
497 scratch2,
498 scratch3,
499 gc_required,
500 TAG_OBJECT);
501
502 // Allocated the JSArray. Now initialize the fields except for the elements
503 // array.
504 // result: JSObject
505 // scratch1: initial map
506 // scratch2: start of next object
507 __ movq(FieldOperand(result, JSObject::kMapOffset), scratch1);
508 __ Move(FieldOperand(result, JSArray::kPropertiesOffset),
509 Factory::empty_fixed_array());
510 // Field JSArray::kElementsOffset is initialized later.
Steve Block3ce2e202009-11-05 08:53:23 +0000511 __ Move(FieldOperand(result, JSArray::kLengthOffset), Smi::FromInt(0));
Steve Blocka7e24c12009-10-30 11:49:00 +0000512
513 // If no storage is requested for the elements array just set the empty
514 // fixed array.
515 if (initial_capacity == 0) {
516 __ Move(FieldOperand(result, JSArray::kElementsOffset),
517 Factory::empty_fixed_array());
518 return;
519 }
520
521 // Calculate the location of the elements array and set elements array member
522 // of the JSArray.
523 // result: JSObject
524 // scratch2: start of next object
525 __ lea(scratch1, Operand(result, JSArray::kSize));
526 __ movq(FieldOperand(result, JSArray::kElementsOffset), scratch1);
527
528 // Initialize the FixedArray and fill it with holes. FixedArray length is not
529 // stored as a smi.
530 // result: JSObject
531 // scratch1: elements array
532 // scratch2: start of next object
533 __ Move(FieldOperand(scratch1, JSObject::kMapOffset),
534 Factory::fixed_array_map());
535 __ movq(FieldOperand(scratch1, Array::kLengthOffset),
536 Immediate(initial_capacity));
537
538 // Fill the FixedArray with the hole value. Inline the code if short.
539 // Reconsider loop unfolding if kPreallocatedArrayElements gets changed.
540 static const int kLoopUnfoldLimit = 4;
541 ASSERT(kPreallocatedArrayElements <= kLoopUnfoldLimit);
542 __ Move(scratch3, Factory::the_hole_value());
543 if (initial_capacity <= kLoopUnfoldLimit) {
544 // Use a scratch register here to have only one reloc info when unfolding
545 // the loop.
546 for (int i = 0; i < initial_capacity; i++) {
547 __ movq(FieldOperand(scratch1,
548 FixedArray::kHeaderSize + i * kPointerSize),
549 scratch3);
550 }
551 } else {
552 Label loop, entry;
553 __ jmp(&entry);
554 __ bind(&loop);
555 __ movq(Operand(scratch1, 0), scratch3);
556 __ addq(scratch1, Immediate(kPointerSize));
557 __ bind(&entry);
558 __ cmpq(scratch1, scratch2);
559 __ j(below, &loop);
560 }
561}
562
563
564// Allocate a JSArray with the number of elements stored in a register. The
565// register array_function holds the built-in Array function and the register
566// array_size holds the size of the array as a smi. The allocated array is put
567// into the result register and beginning and end of the FixedArray elements
568// storage is put into registers elements_array and elements_array_end (see
569// below for when that is not the case). If the parameter fill_with_holes is
570// true the allocated elements backing store is filled with the hole values
571// otherwise it is left uninitialized. When the backing store is filled the
572// register elements_array is scratched.
573static void AllocateJSArray(MacroAssembler* masm,
574 Register array_function, // Array function.
575 Register array_size, // As a smi.
576 Register result,
577 Register elements_array,
578 Register elements_array_end,
579 Register scratch,
580 bool fill_with_hole,
581 Label* gc_required) {
582 Label not_empty, allocated;
583
584 // Load the initial map from the array function.
585 __ movq(elements_array,
586 FieldOperand(array_function,
587 JSFunction::kPrototypeOrInitialMapOffset));
588
589 // Check whether an empty sized array is requested.
Andrei Popescu402d9372010-02-26 13:31:12 +0000590 __ SmiToInteger64(array_size, array_size);
Steve Blocka7e24c12009-10-30 11:49:00 +0000591 __ testq(array_size, array_size);
592 __ j(not_zero, &not_empty);
593
594 // If an empty array is requested allocate a small elements array anyway. This
595 // keeps the code below free of special casing for the empty array.
596 int size = JSArray::kSize + FixedArray::SizeFor(kPreallocatedArrayElements);
597 __ AllocateInNewSpace(size,
598 result,
599 elements_array_end,
600 scratch,
601 gc_required,
602 TAG_OBJECT);
603 __ jmp(&allocated);
604
605 // Allocate the JSArray object together with space for a FixedArray with the
606 // requested elements.
607 __ bind(&not_empty);
608 ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
609 __ AllocateInNewSpace(JSArray::kSize + FixedArray::kHeaderSize,
Andrei Popescu402d9372010-02-26 13:31:12 +0000610 times_pointer_size,
Steve Blocka7e24c12009-10-30 11:49:00 +0000611 array_size,
612 result,
613 elements_array_end,
614 scratch,
615 gc_required,
616 TAG_OBJECT);
617
618 // Allocated the JSArray. Now initialize the fields except for the elements
619 // array.
620 // result: JSObject
621 // elements_array: initial map
622 // elements_array_end: start of next object
Andrei Popescu402d9372010-02-26 13:31:12 +0000623 // array_size: size of array
Steve Blocka7e24c12009-10-30 11:49:00 +0000624 __ bind(&allocated);
625 __ movq(FieldOperand(result, JSObject::kMapOffset), elements_array);
626 __ Move(elements_array, Factory::empty_fixed_array());
627 __ movq(FieldOperand(result, JSArray::kPropertiesOffset), elements_array);
628 // Field JSArray::kElementsOffset is initialized later.
Andrei Popescu402d9372010-02-26 13:31:12 +0000629 __ Integer32ToSmi(scratch, array_size);
630 __ movq(FieldOperand(result, JSArray::kLengthOffset), scratch);
Steve Blocka7e24c12009-10-30 11:49:00 +0000631
632 // Calculate the location of the elements array and set elements array member
633 // of the JSArray.
634 // result: JSObject
635 // elements_array_end: start of next object
Andrei Popescu402d9372010-02-26 13:31:12 +0000636 // array_size: size of array
Steve Blocka7e24c12009-10-30 11:49:00 +0000637 __ lea(elements_array, Operand(result, JSArray::kSize));
638 __ movq(FieldOperand(result, JSArray::kElementsOffset), elements_array);
639
640 // Initialize the fixed array. FixedArray length is not stored as a smi.
641 // result: JSObject
642 // elements_array: elements array
643 // elements_array_end: start of next object
Andrei Popescu402d9372010-02-26 13:31:12 +0000644 // array_size: size of array
Steve Blocka7e24c12009-10-30 11:49:00 +0000645 ASSERT(kSmiTag == 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000646 __ Move(FieldOperand(elements_array, JSObject::kMapOffset),
647 Factory::fixed_array_map());
648 Label not_empty_2, fill_array;
649 __ testq(array_size, array_size);
650 __ j(not_zero, &not_empty_2);
651 // Length of the FixedArray is the number of pre-allocated elements even
652 // though the actual JSArray has length 0.
653 __ movq(FieldOperand(elements_array, Array::kLengthOffset),
654 Immediate(kPreallocatedArrayElements));
655 __ jmp(&fill_array);
656 __ bind(&not_empty_2);
657 // For non-empty JSArrays the length of the FixedArray and the JSArray is the
658 // same.
659 __ movq(FieldOperand(elements_array, Array::kLengthOffset), array_size);
660
661 // Fill the allocated FixedArray with the hole value if requested.
662 // result: JSObject
663 // elements_array: elements array
664 // elements_array_end: start of next object
665 __ bind(&fill_array);
666 if (fill_with_hole) {
667 Label loop, entry;
668 __ Move(scratch, Factory::the_hole_value());
669 __ lea(elements_array, Operand(elements_array,
670 FixedArray::kHeaderSize - kHeapObjectTag));
671 __ jmp(&entry);
672 __ bind(&loop);
673 __ movq(Operand(elements_array, 0), scratch);
674 __ addq(elements_array, Immediate(kPointerSize));
675 __ bind(&entry);
676 __ cmpq(elements_array, elements_array_end);
677 __ j(below, &loop);
678 }
679}
680
681
682// Create a new array for the built-in Array function. This function allocates
683// the JSArray object and the FixedArray elements array and initializes these.
684// If the Array cannot be constructed in native code the runtime is called. This
685// function assumes the following state:
686// rdi: constructor (built-in Array function)
687// rax: argc
688// rsp[0]: return address
689// rsp[8]: last argument
690// This function is used for both construct and normal calls of Array. The only
691// difference between handling a construct call and a normal call is that for a
692// construct call the constructor function in rdi needs to be preserved for
693// entering the generic code. In both cases argc in rax needs to be preserved.
694// Both registers are preserved by this code so no need to differentiate between
695// a construct call and a normal call.
696static void ArrayNativeCode(MacroAssembler* masm,
697 Label *call_generic_code) {
698 Label argc_one_or_more, argc_two_or_more;
699
700 // Check for array construction with zero arguments.
701 __ testq(rax, rax);
702 __ j(not_zero, &argc_one_or_more);
703
704 // Handle construction of an empty array.
705 AllocateEmptyJSArray(masm,
706 rdi,
707 rbx,
708 rcx,
709 rdx,
710 r8,
711 kPreallocatedArrayElements,
712 call_generic_code);
713 __ IncrementCounter(&Counters::array_function_native, 1);
714 __ movq(rax, rbx);
715 __ ret(kPointerSize);
716
717 // Check for one argument. Bail out if argument is not smi or if it is
718 // negative.
719 __ bind(&argc_one_or_more);
720 __ cmpq(rax, Immediate(1));
721 __ j(not_equal, &argc_two_or_more);
722 __ movq(rdx, Operand(rsp, kPointerSize)); // Get the argument from the stack.
Steve Block3ce2e202009-11-05 08:53:23 +0000723 __ JumpIfNotPositiveSmi(rdx, call_generic_code);
Steve Blocka7e24c12009-10-30 11:49:00 +0000724
725 // Handle construction of an empty array of a certain size. Bail out if size
726 // is to large to actually allocate an elements array.
Steve Block3ce2e202009-11-05 08:53:23 +0000727 __ SmiCompare(rdx, Smi::FromInt(JSObject::kInitialMaxFastElementArray));
728 __ j(greater_equal, call_generic_code);
Steve Blocka7e24c12009-10-30 11:49:00 +0000729
730 // rax: argc
731 // rdx: array_size (smi)
732 // rdi: constructor
733 // esp[0]: return address
734 // esp[8]: argument
735 AllocateJSArray(masm,
736 rdi,
737 rdx,
738 rbx,
739 rcx,
740 r8,
741 r9,
742 true,
743 call_generic_code);
744 __ IncrementCounter(&Counters::array_function_native, 1);
745 __ movq(rax, rbx);
746 __ ret(2 * kPointerSize);
747
748 // Handle construction of an array from a list of arguments.
749 __ bind(&argc_two_or_more);
750 __ movq(rdx, rax);
751 __ Integer32ToSmi(rdx, rdx); // Convet argc to a smi.
752 // rax: argc
753 // rdx: array_size (smi)
754 // rdi: constructor
755 // esp[0] : return address
756 // esp[8] : last argument
757 AllocateJSArray(masm,
758 rdi,
759 rdx,
760 rbx,
761 rcx,
762 r8,
763 r9,
764 false,
765 call_generic_code);
766 __ IncrementCounter(&Counters::array_function_native, 1);
767
768 // rax: argc
769 // rbx: JSArray
770 // rcx: elements_array
771 // r8: elements_array_end (untagged)
772 // esp[0]: return address
773 // esp[8]: last argument
774
775 // Location of the last argument
776 __ lea(r9, Operand(rsp, kPointerSize));
777
778 // Location of the first array element (Parameter fill_with_holes to
779 // AllocateJSArrayis false, so the FixedArray is returned in rcx).
780 __ lea(rdx, Operand(rcx, FixedArray::kHeaderSize - kHeapObjectTag));
781
782 // rax: argc
783 // rbx: JSArray
784 // rdx: location of the first array element
785 // r9: location of the last argument
786 // esp[0]: return address
787 // esp[8]: last argument
788 Label loop, entry;
789 __ movq(rcx, rax);
790 __ jmp(&entry);
791 __ bind(&loop);
792 __ movq(kScratchRegister, Operand(r9, rcx, times_pointer_size, 0));
793 __ movq(Operand(rdx, 0), kScratchRegister);
794 __ addq(rdx, Immediate(kPointerSize));
795 __ bind(&entry);
796 __ decq(rcx);
797 __ j(greater_equal, &loop);
798
799 // Remove caller arguments from the stack and return.
800 // rax: argc
801 // rbx: JSArray
802 // esp[0]: return address
803 // esp[8]: last argument
804 __ pop(rcx);
805 __ lea(rsp, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize));
806 __ push(rcx);
807 __ movq(rax, rbx);
808 __ ret(0);
809}
810
811
812void Builtins::Generate_ArrayCode(MacroAssembler* masm) {
813 // ----------- S t a t e -------------
814 // -- rax : argc
815 // -- rsp[0] : return address
816 // -- rsp[8] : last argument
817 // -----------------------------------
818 Label generic_array_code;
819
820 // Get the Array function.
821 GenerateLoadArrayFunction(masm, rdi);
822
823 if (FLAG_debug_code) {
824 // Initial map for the builtin Array function shoud be a map.
825 __ movq(rbx, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset));
826 // Will both indicate a NULL and a Smi.
827 ASSERT(kSmiTag == 0);
Steve Block3ce2e202009-11-05 08:53:23 +0000828 Condition not_smi = NegateCondition(masm->CheckSmi(rbx));
829 __ Check(not_smi, "Unexpected initial map for Array function");
Steve Blocka7e24c12009-10-30 11:49:00 +0000830 __ CmpObjectType(rbx, MAP_TYPE, rcx);
Steve Block3ce2e202009-11-05 08:53:23 +0000831 __ Check(equal, "Unexpected initial map for Array function");
Steve Blocka7e24c12009-10-30 11:49:00 +0000832 }
833
834 // Run the native code for the Array function called as a normal function.
835 ArrayNativeCode(masm, &generic_array_code);
836
837 // Jump to the generic array code in case the specialized code cannot handle
838 // the construction.
839 __ bind(&generic_array_code);
840 Code* code = Builtins::builtin(Builtins::ArrayCodeGeneric);
841 Handle<Code> array_code(code);
842 __ Jump(array_code, RelocInfo::CODE_TARGET);
843}
844
845
846void Builtins::Generate_ArrayConstructCode(MacroAssembler* masm) {
847 // ----------- S t a t e -------------
848 // -- rax : argc
849 // -- rdi : constructor
850 // -- rsp[0] : return address
851 // -- rsp[8] : last argument
852 // -----------------------------------
853 Label generic_constructor;
854
855 if (FLAG_debug_code) {
856 // The array construct code is only set for the builtin Array function which
857 // does always have a map.
858 GenerateLoadArrayFunction(masm, rbx);
859 __ cmpq(rdi, rbx);
Steve Block3ce2e202009-11-05 08:53:23 +0000860 __ Check(equal, "Unexpected Array function");
Steve Blocka7e24c12009-10-30 11:49:00 +0000861 // Initial map for the builtin Array function should be a map.
862 __ movq(rbx, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset));
863 // Will both indicate a NULL and a Smi.
864 ASSERT(kSmiTag == 0);
Steve Block3ce2e202009-11-05 08:53:23 +0000865 Condition not_smi = NegateCondition(masm->CheckSmi(rbx));
866 __ Check(not_smi, "Unexpected initial map for Array function");
Steve Blocka7e24c12009-10-30 11:49:00 +0000867 __ CmpObjectType(rbx, MAP_TYPE, rcx);
Steve Block3ce2e202009-11-05 08:53:23 +0000868 __ Check(equal, "Unexpected initial map for Array function");
Steve Blocka7e24c12009-10-30 11:49:00 +0000869 }
870
871 // Run the native code for the Array function called as constructor.
872 ArrayNativeCode(masm, &generic_constructor);
873
874 // Jump to the generic construct code in case the specialized code cannot
875 // handle the construction.
876 __ bind(&generic_constructor);
877 Code* code = Builtins::builtin(Builtins::JSConstructStubGeneric);
878 Handle<Code> generic_construct_stub(code);
879 __ Jump(generic_construct_stub, RelocInfo::CODE_TARGET);
880}
881
882
883void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
884 // ----------- S t a t e -------------
885 // -- rax: number of arguments
886 // -- rdi: constructor function
887 // -----------------------------------
888
889 Label non_function_call;
890 // Check that function is not a smi.
891 __ JumpIfSmi(rdi, &non_function_call);
892 // Check that function is a JSFunction.
893 __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
894 __ j(not_equal, &non_function_call);
895
896 // Jump to the function-specific construct stub.
897 __ movq(rbx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
898 __ movq(rbx, FieldOperand(rbx, SharedFunctionInfo::kConstructStubOffset));
899 __ lea(rbx, FieldOperand(rbx, Code::kHeaderSize));
900 __ jmp(rbx);
901
902 // edi: called object
903 // eax: number of arguments
904 __ bind(&non_function_call);
Andrei Popescu402d9372010-02-26 13:31:12 +0000905 // CALL_NON_FUNCTION expects the non-function constructor as receiver
906 // (instead of the original receiver from the call site). The receiver is
907 // stack element argc+1.
908 __ movq(Operand(rsp, rax, times_pointer_size, kPointerSize), rdi);
909 // Set expected number of arguments to zero (not changing rax).
Steve Blocka7e24c12009-10-30 11:49:00 +0000910 __ movq(rbx, Immediate(0));
911 __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
912 __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
913 RelocInfo::CODE_TARGET);
914}
915
916
Leon Clarkee46be812010-01-19 14:06:41 +0000917static void Generate_JSConstructStubHelper(MacroAssembler* masm,
918 bool is_api_function) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000919 // Enter a construct frame.
920 __ EnterConstructFrame();
921
922 // Store a smi-tagged arguments count on the stack.
923 __ Integer32ToSmi(rax, rax);
924 __ push(rax);
925
926 // Push the function to invoke on the stack.
927 __ push(rdi);
928
929 // Try to allocate the object without transitioning into C code. If any of the
930 // preconditions is not met, the code bails out to the runtime call.
931 Label rt_call, allocated;
932 if (FLAG_inline_new) {
933 Label undo_allocation;
934
935#ifdef ENABLE_DEBUGGER_SUPPORT
936 ExternalReference debug_step_in_fp =
937 ExternalReference::debug_step_in_fp_address();
938 __ movq(kScratchRegister, debug_step_in_fp);
939 __ cmpq(Operand(kScratchRegister, 0), Immediate(0));
940 __ j(not_equal, &rt_call);
941#endif
942
943 // Verified that the constructor is a JSFunction.
944 // Load the initial map and verify that it is in fact a map.
945 // rdi: constructor
946 __ movq(rax, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset));
947 // Will both indicate a NULL and a Smi
948 ASSERT(kSmiTag == 0);
949 __ JumpIfSmi(rax, &rt_call);
950 // rdi: constructor
951 // rax: initial map (if proven valid below)
952 __ CmpObjectType(rax, MAP_TYPE, rbx);
953 __ j(not_equal, &rt_call);
954
955 // Check that the constructor is not constructing a JSFunction (see comments
956 // in Runtime_NewObject in runtime.cc). In which case the initial map's
957 // instance type would be JS_FUNCTION_TYPE.
958 // rdi: constructor
959 // rax: initial map
960 __ CmpInstanceType(rax, JS_FUNCTION_TYPE);
961 __ j(equal, &rt_call);
962
963 // Now allocate the JSObject on the heap.
964 __ movzxbq(rdi, FieldOperand(rax, Map::kInstanceSizeOffset));
965 __ shl(rdi, Immediate(kPointerSizeLog2));
966 // rdi: size of new object
967 __ AllocateInNewSpace(rdi,
968 rbx,
969 rdi,
970 no_reg,
971 &rt_call,
972 NO_ALLOCATION_FLAGS);
973 // Allocated the JSObject, now initialize the fields.
974 // rax: initial map
975 // rbx: JSObject (not HeapObject tagged - the actual address).
976 // rdi: start of next object
977 __ movq(Operand(rbx, JSObject::kMapOffset), rax);
978 __ LoadRoot(rcx, Heap::kEmptyFixedArrayRootIndex);
979 __ movq(Operand(rbx, JSObject::kPropertiesOffset), rcx);
980 __ movq(Operand(rbx, JSObject::kElementsOffset), rcx);
981 // Set extra fields in the newly allocated object.
982 // rax: initial map
983 // rbx: JSObject
984 // rdi: start of next object
985 { Label loop, entry;
986 __ LoadRoot(rdx, Heap::kUndefinedValueRootIndex);
987 __ lea(rcx, Operand(rbx, JSObject::kHeaderSize));
988 __ jmp(&entry);
989 __ bind(&loop);
990 __ movq(Operand(rcx, 0), rdx);
991 __ addq(rcx, Immediate(kPointerSize));
992 __ bind(&entry);
993 __ cmpq(rcx, rdi);
994 __ j(less, &loop);
995 }
996
997 // Add the object tag to make the JSObject real, so that we can continue and
998 // jump into the continuation code at any time from now on. Any failures
999 // need to undo the allocation, so that the heap is in a consistent state
1000 // and verifiable.
1001 // rax: initial map
1002 // rbx: JSObject
1003 // rdi: start of next object
1004 __ or_(rbx, Immediate(kHeapObjectTag));
1005
1006 // Check if a non-empty properties array is needed.
1007 // Allocate and initialize a FixedArray if it is.
1008 // rax: initial map
1009 // rbx: JSObject
1010 // rdi: start of next object
1011 // Calculate total properties described map.
1012 __ movzxbq(rdx, FieldOperand(rax, Map::kUnusedPropertyFieldsOffset));
1013 __ movzxbq(rcx, FieldOperand(rax, Map::kPreAllocatedPropertyFieldsOffset));
1014 __ addq(rdx, rcx);
1015 // Calculate unused properties past the end of the in-object properties.
1016 __ movzxbq(rcx, FieldOperand(rax, Map::kInObjectPropertiesOffset));
1017 __ subq(rdx, rcx);
1018 // Done if no extra properties are to be allocated.
1019 __ j(zero, &allocated);
1020 __ Assert(positive, "Property allocation count failed.");
1021
1022 // Scale the number of elements by pointer size and add the header for
1023 // FixedArrays to the start of the next object calculation from above.
1024 // rbx: JSObject
1025 // rdi: start of next object (will be start of FixedArray)
1026 // rdx: number of elements in properties array
1027 __ AllocateInNewSpace(FixedArray::kHeaderSize,
1028 times_pointer_size,
1029 rdx,
1030 rdi,
1031 rax,
1032 no_reg,
1033 &undo_allocation,
1034 RESULT_CONTAINS_TOP);
1035
1036 // Initialize the FixedArray.
1037 // rbx: JSObject
1038 // rdi: FixedArray
1039 // rdx: number of elements
1040 // rax: start of next object
1041 __ LoadRoot(rcx, Heap::kFixedArrayMapRootIndex);
1042 __ movq(Operand(rdi, JSObject::kMapOffset), rcx); // setup the map
1043 __ movl(Operand(rdi, FixedArray::kLengthOffset), rdx); // and length
1044
1045 // Initialize the fields to undefined.
1046 // rbx: JSObject
1047 // rdi: FixedArray
1048 // rax: start of next object
1049 // rdx: number of elements
1050 { Label loop, entry;
1051 __ LoadRoot(rdx, Heap::kUndefinedValueRootIndex);
1052 __ lea(rcx, Operand(rdi, FixedArray::kHeaderSize));
1053 __ jmp(&entry);
1054 __ bind(&loop);
1055 __ movq(Operand(rcx, 0), rdx);
1056 __ addq(rcx, Immediate(kPointerSize));
1057 __ bind(&entry);
1058 __ cmpq(rcx, rax);
1059 __ j(below, &loop);
1060 }
1061
1062 // Store the initialized FixedArray into the properties field of
1063 // the JSObject
1064 // rbx: JSObject
1065 // rdi: FixedArray
1066 __ or_(rdi, Immediate(kHeapObjectTag)); // add the heap tag
1067 __ movq(FieldOperand(rbx, JSObject::kPropertiesOffset), rdi);
1068
1069
1070 // Continue with JSObject being successfully allocated
1071 // rbx: JSObject
1072 __ jmp(&allocated);
1073
1074 // Undo the setting of the new top so that the heap is verifiable. For
1075 // example, the map's unused properties potentially do not match the
1076 // allocated objects unused properties.
1077 // rbx: JSObject (previous new top)
1078 __ bind(&undo_allocation);
1079 __ UndoAllocationInNewSpace(rbx);
1080 }
1081
1082 // Allocate the new receiver object using the runtime call.
1083 // rdi: function (constructor)
1084 __ bind(&rt_call);
1085 // Must restore rdi (constructor) before calling runtime.
1086 __ movq(rdi, Operand(rsp, 0));
1087 __ push(rdi);
1088 __ CallRuntime(Runtime::kNewObject, 1);
1089 __ movq(rbx, rax); // store result in rbx
1090
1091 // New object allocated.
1092 // rbx: newly allocated object
1093 __ bind(&allocated);
1094 // Retrieve the function from the stack.
1095 __ pop(rdi);
1096
1097 // Retrieve smi-tagged arguments count from the stack.
1098 __ movq(rax, Operand(rsp, 0));
1099 __ SmiToInteger32(rax, rax);
1100
1101 // Push the allocated receiver to the stack. We need two copies
1102 // because we may have to return the original one and the calling
1103 // conventions dictate that the called function pops the receiver.
1104 __ push(rbx);
1105 __ push(rbx);
1106
1107 // Setup pointer to last argument.
1108 __ lea(rbx, Operand(rbp, StandardFrameConstants::kCallerSPOffset));
1109
1110 // Copy arguments and receiver to the expression stack.
1111 Label loop, entry;
1112 __ movq(rcx, rax);
1113 __ jmp(&entry);
1114 __ bind(&loop);
1115 __ push(Operand(rbx, rcx, times_pointer_size, 0));
1116 __ bind(&entry);
1117 __ decq(rcx);
1118 __ j(greater_equal, &loop);
1119
1120 // Call the function.
Leon Clarkee46be812010-01-19 14:06:41 +00001121 if (is_api_function) {
1122 __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
1123 Handle<Code> code = Handle<Code>(
1124 Builtins::builtin(Builtins::HandleApiCallConstruct));
1125 ParameterCount expected(0);
1126 __ InvokeCode(code, expected, expected,
1127 RelocInfo::CODE_TARGET, CALL_FUNCTION);
1128 } else {
1129 ParameterCount actual(rax);
1130 __ InvokeFunction(rdi, actual, CALL_FUNCTION);
1131 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001132
1133 // Restore context from the frame.
1134 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
1135
1136 // If the result is an object (in the ECMA sense), we should get rid
1137 // of the receiver and use the result; see ECMA-262 section 13.2.2-7
1138 // on page 74.
1139 Label use_receiver, exit;
1140 // If the result is a smi, it is *not* an object in the ECMA sense.
1141 __ JumpIfSmi(rax, &use_receiver);
1142
1143 // If the type of the result (stored in its map) is less than
1144 // FIRST_JS_OBJECT_TYPE, it is not an object in the ECMA sense.
1145 __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx);
1146 __ j(above_equal, &exit);
1147
1148 // Throw away the result of the constructor invocation and use the
1149 // on-stack receiver as the result.
1150 __ bind(&use_receiver);
1151 __ movq(rax, Operand(rsp, 0));
1152
1153 // Restore the arguments count and leave the construct frame.
1154 __ bind(&exit);
1155 __ movq(rbx, Operand(rsp, kPointerSize)); // get arguments count
1156 __ LeaveConstructFrame();
1157
1158 // Remove caller arguments from the stack and return.
Steve Blocka7e24c12009-10-30 11:49:00 +00001159 __ pop(rcx);
Steve Block3ce2e202009-11-05 08:53:23 +00001160 SmiIndex index = masm->SmiToIndex(rbx, rbx, kPointerSizeLog2);
1161 __ lea(rsp, Operand(rsp, index.reg, index.scale, 1 * kPointerSize));
Steve Blocka7e24c12009-10-30 11:49:00 +00001162 __ push(rcx);
1163 __ IncrementCounter(&Counters::constructed_objects, 1);
1164 __ ret(0);
1165}
1166
1167
Leon Clarkee46be812010-01-19 14:06:41 +00001168void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
1169 Generate_JSConstructStubHelper(masm, false);
1170}
1171
1172
1173void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
1174 Generate_JSConstructStubHelper(masm, true);
1175}
1176
1177
Steve Blocka7e24c12009-10-30 11:49:00 +00001178static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
1179 bool is_construct) {
1180 // Expects five C++ function parameters.
1181 // - Address entry (ignored)
1182 // - JSFunction* function (
1183 // - Object* receiver
1184 // - int argc
1185 // - Object*** argv
1186 // (see Handle::Invoke in execution.cc).
1187
1188 // Platform specific argument handling. After this, the stack contains
1189 // an internal frame and the pushed function and receiver, and
1190 // register rax and rbx holds the argument count and argument array,
1191 // while rdi holds the function pointer and rsi the context.
1192#ifdef _WIN64
1193 // MSVC parameters in:
1194 // rcx : entry (ignored)
1195 // rdx : function
1196 // r8 : receiver
1197 // r9 : argc
1198 // [rsp+0x20] : argv
1199
1200 // Clear the context before we push it when entering the JS frame.
1201 __ xor_(rsi, rsi);
1202 __ EnterInternalFrame();
1203
1204 // Load the function context into rsi.
1205 __ movq(rsi, FieldOperand(rdx, JSFunction::kContextOffset));
1206
1207 // Push the function and the receiver onto the stack.
1208 __ push(rdx);
1209 __ push(r8);
1210
1211 // Load the number of arguments and setup pointer to the arguments.
1212 __ movq(rax, r9);
1213 // Load the previous frame pointer to access C argument on stack
1214 __ movq(kScratchRegister, Operand(rbp, 0));
1215 __ movq(rbx, Operand(kScratchRegister, EntryFrameConstants::kArgvOffset));
1216 // Load the function pointer into rdi.
1217 __ movq(rdi, rdx);
Steve Block6ded16b2010-05-10 14:33:55 +01001218#else // _WIN64
Steve Blocka7e24c12009-10-30 11:49:00 +00001219 // GCC parameters in:
1220 // rdi : entry (ignored)
1221 // rsi : function
1222 // rdx : receiver
1223 // rcx : argc
1224 // r8 : argv
1225
1226 __ movq(rdi, rsi);
1227 // rdi : function
1228
1229 // Clear the context before we push it when entering the JS frame.
1230 __ xor_(rsi, rsi);
1231 // Enter an internal frame.
1232 __ EnterInternalFrame();
1233
1234 // Push the function and receiver and setup the context.
1235 __ push(rdi);
1236 __ push(rdx);
1237 __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
1238
1239 // Load the number of arguments and setup pointer to the arguments.
1240 __ movq(rax, rcx);
1241 __ movq(rbx, r8);
1242#endif // _WIN64
1243
1244 // Set up the roots register.
1245 ExternalReference roots_address = ExternalReference::roots_address();
Steve Block6ded16b2010-05-10 14:33:55 +01001246 __ movq(kRootRegister, roots_address);
Steve Blocka7e24c12009-10-30 11:49:00 +00001247
1248 // Current stack contents:
1249 // [rsp + 2 * kPointerSize ... ]: Internal frame
1250 // [rsp + kPointerSize] : function
1251 // [rsp] : receiver
1252 // Current register contents:
1253 // rax : argc
1254 // rbx : argv
1255 // rsi : context
1256 // rdi : function
1257
1258 // Copy arguments to the stack in a loop.
1259 // Register rbx points to array of pointers to handle locations.
1260 // Push the values of these handles.
1261 Label loop, entry;
1262 __ xor_(rcx, rcx); // Set loop variable to 0.
1263 __ jmp(&entry);
1264 __ bind(&loop);
1265 __ movq(kScratchRegister, Operand(rbx, rcx, times_pointer_size, 0));
1266 __ push(Operand(kScratchRegister, 0)); // dereference handle
1267 __ addq(rcx, Immediate(1));
1268 __ bind(&entry);
1269 __ cmpq(rcx, rax);
1270 __ j(not_equal, &loop);
1271
1272 // Invoke the code.
1273 if (is_construct) {
1274 // Expects rdi to hold function pointer.
1275 __ Call(Handle<Code>(Builtins::builtin(Builtins::JSConstructCall)),
1276 RelocInfo::CODE_TARGET);
1277 } else {
1278 ParameterCount actual(rax);
1279 // Function must be in rdi.
1280 __ InvokeFunction(rdi, actual, CALL_FUNCTION);
1281 }
1282
1283 // Exit the JS frame. Notice that this also removes the empty
1284 // context and the function left on the stack by the code
1285 // invocation.
1286 __ LeaveInternalFrame();
1287 // TODO(X64): Is argument correct? Is there a receiver to remove?
1288 __ ret(1 * kPointerSize); // remove receiver
1289}
1290
1291
1292void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) {
1293 Generate_JSEntryTrampolineHelper(masm, false);
1294}
1295
1296
1297void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
1298 Generate_JSEntryTrampolineHelper(masm, true);
1299}
1300
1301} } // namespace v8::internal
Leon Clarkef7060e22010-06-03 12:02:55 +01001302
1303#endif // V8_TARGET_ARCH_X64