1 // 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"
29 #include "codegen-inl.h"
30 #include "macro-assembler.h"
31
32 namespace v8 {
33 namespace internal {
34
35 #define __ ACCESS_MASM(masm)
36
Generate_Adaptor(MacroAssembler * masm,CFunctionId id)37 void Builtins::Generate_Adaptor(MacroAssembler* masm, CFunctionId id) {
38 // TODO(428): Don't pass the function in a static variable.
39 ExternalReference passed = ExternalReference::builtin_passed_function();
40 __ movq(kScratchRegister, passed.address(), RelocInfo::EXTERNAL_REFERENCE);
41 __ movq(Operand(kScratchRegister, 0), rdi);
42
43 // The actual argument count has already been loaded into register
44 // rax, but JumpToBuiltin expects rax to contain the number of
45 // arguments including the receiver.
46 __ incq(rax);
47 __ JumpToBuiltin(ExternalReference(id));
48 }
49
50
EnterArgumentsAdaptorFrame(MacroAssembler * masm)51 static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
52 __ push(rbp);
53 __ movq(rbp, rsp);
54
55 // Store the arguments adaptor context sentinel.
56 __ push(Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
57
58 // Push the function on the stack.
59 __ push(rdi);
60
61 // Preserve the number of arguments on the stack. Must preserve both
62 // rax and rbx because these registers are used when copying the
63 // arguments and the receiver.
64 ASSERT(kSmiTagSize == 1);
65 __ lea(rcx, Operand(rax, rax, times_1, kSmiTag));
66 __ push(rcx);
67 }
68
69
LeaveArgumentsAdaptorFrame(MacroAssembler * masm)70 static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) {
71 // Retrieve the number of arguments from the stack. Number is a Smi.
72 __ movq(rbx, Operand(rbp, ArgumentsAdaptorFrameConstants::kLengthOffset));
73
74 // Leave the frame.
75 __ movq(rsp, rbp);
76 __ pop(rbp);
77
78 // Remove caller arguments from the stack.
79 // rbx holds a Smi, so we convery to dword offset by multiplying by 4.
80 ASSERT_EQ(kSmiTagSize, 1 && kSmiTag == 0);
81 ASSERT_EQ(kPointerSize, (1 << kSmiTagSize) * 4);
82 __ pop(rcx);
83 __ lea(rsp, Operand(rsp, rbx, times_4, 1 * kPointerSize)); // 1 ~ receiver
84 __ push(rcx);
85 }
86
87
Generate_ArgumentsAdaptorTrampoline(MacroAssembler * masm)88 void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
89 // ----------- S t a t e -------------
90 // -- rax : actual number of arguments
91 // -- rbx : expected number of arguments
92 // -- rdx : code entry to call
93 // -----------------------------------
94
95 Label invoke, dont_adapt_arguments;
96 __ IncrementCounter(&Counters::arguments_adaptors, 1);
97
98 Label enough, too_few;
99 __ cmpq(rax, rbx);
100 __ j(less, &too_few);
101 __ cmpq(rbx, Immediate(SharedFunctionInfo::kDontAdaptArgumentsSentinel));
102 __ j(equal, &dont_adapt_arguments);
103
104 { // Enough parameters: Actual >= expected.
105 __ bind(&enough);
106 EnterArgumentsAdaptorFrame(masm);
107
108 // Copy receiver and all expected arguments.
109 const int offset = StandardFrameConstants::kCallerSPOffset;
110 __ lea(rax, Operand(rbp, rax, times_pointer_size, offset));
111 __ movq(rcx, Immediate(-1)); // account for receiver
112
113 Label copy;
114 __ bind(©);
115 __ incq(rcx);
116 __ push(Operand(rax, 0));
117 __ subq(rax, Immediate(kPointerSize));
118 __ cmpq(rcx, rbx);
119 __ j(less, ©);
120 __ jmp(&invoke);
121 }
122
123 { // Too few parameters: Actual < expected.
124 __ bind(&too_few);
125 EnterArgumentsAdaptorFrame(masm);
126
127 // Copy receiver and all actual arguments.
128 const int offset = StandardFrameConstants::kCallerSPOffset;
129 __ lea(rdi, Operand(rbp, rax, times_pointer_size, offset));
130 __ movq(rcx, Immediate(-1)); // account for receiver
131
132 Label copy;
133 __ bind(©);
134 __ incq(rcx);
135 __ push(Operand(rdi, 0));
136 __ subq(rdi, Immediate(kPointerSize));
137 __ cmpq(rcx, rax);
138 __ j(less, ©);
139
140 // Fill remaining expected arguments with undefined values.
141 Label fill;
142 __ LoadRoot(kScratchRegister, Heap::kUndefinedValueRootIndex);
143 __ bind(&fill);
144 __ incq(rcx);
145 __ push(kScratchRegister);
146 __ cmpq(rcx, rbx);
147 __ j(less, &fill);
148
149 // Restore function pointer.
150 __ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
151 }
152
153 // Call the entry point.
154 __ bind(&invoke);
155 __ call(rdx);
156
157 // Leave frame and return.
158 LeaveArgumentsAdaptorFrame(masm);
159 __ ret(0);
160
161 // -------------------------------------------
162 // Dont adapt arguments.
163 // -------------------------------------------
164 __ bind(&dont_adapt_arguments);
165 __ jmp(rdx);
166 }
167
168
Generate_FunctionCall(MacroAssembler * masm)169 void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
170 // Stack Layout:
171 // rsp: return address
172 // +1: Argument n
173 // +2: Argument n-1
174 // ...
175 // +n: Argument 1 = receiver
176 // +n+1: Argument 0 = function to call
177 //
178 // rax contains the number of arguments, n, not counting the function.
179 //
180 // 1. Make sure we have at least one argument.
181 { Label done;
182 __ testq(rax, rax);
183 __ j(not_zero, &done);
184 __ pop(rbx);
185 __ Push(Factory::undefined_value());
186 __ push(rbx);
187 __ incq(rax);
188 __ bind(&done);
189 }
190
191 // 2. Get the function to call from the stack.
192 { Label done, non_function, function;
193 // The function to call is at position n+1 on the stack.
194 __ movq(rdi, Operand(rsp, rax, times_pointer_size, +1 * kPointerSize));
195 __ testl(rdi, Immediate(kSmiTagMask));
196 __ j(zero, &non_function);
197 __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
198 __ j(equal, &function);
199
200 // Non-function called: Clear the function to force exception.
201 __ bind(&non_function);
202 __ xor_(rdi, rdi);
203 __ jmp(&done);
204
205 // Function called: Change context eagerly to get the right global object.
206 __ bind(&function);
207 __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
208
209 __ bind(&done);
210 }
211
212 // 3. Make sure first argument is an object; convert if necessary.
213 { Label call_to_object, use_global_receiver, patch_receiver, done;
214 __ movq(rbx, Operand(rsp, rax, times_pointer_size, 0));
215
216 __ testl(rbx, Immediate(kSmiTagMask));
217 __ j(zero, &call_to_object);
218
219 __ CompareRoot(rbx, Heap::kNullValueRootIndex);
220 __ j(equal, &use_global_receiver);
221 __ CompareRoot(rbx, Heap::kUndefinedValueRootIndex);
222 __ j(equal, &use_global_receiver);
223
224 __ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, rcx);
225 __ j(below, &call_to_object);
226 __ CmpInstanceType(rcx, LAST_JS_OBJECT_TYPE);
227 __ j(below_equal, &done);
228
229 __ bind(&call_to_object);
230 __ EnterInternalFrame(); // preserves rax, rbx, rdi
231
232 // Store the arguments count on the stack (smi tagged).
233 ASSERT(kSmiTag == 0);
234 __ shl(rax, Immediate(kSmiTagSize));
235 __ push(rax);
236
237 __ push(rdi); // save edi across the call
238 __ push(rbx);
239 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
240 __ movq(rbx, rax);
241 __ pop(rdi); // restore edi after the call
242
243 // Get the arguments count and untag it.
244 __ pop(rax);
245 __ shr(rax, Immediate(kSmiTagSize));
246
247 __ LeaveInternalFrame();
248 __ jmp(&patch_receiver);
249
250 // Use the global receiver object from the called function as the receiver.
251 __ bind(&use_global_receiver);
252 const int kGlobalIndex =
253 Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
254 __ movq(rbx, FieldOperand(rsi, kGlobalIndex));
255 __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
256
257 __ bind(&patch_receiver);
258 __ movq(Operand(rsp, rax, times_pointer_size, 0), rbx);
259
260 __ bind(&done);
261 }
262
263 // 4. Shift stuff one slot down the stack.
264 { Label loop;
265 __ lea(rcx, Operand(rax, +1)); // +1 ~ copy receiver too
266 __ bind(&loop);
267 __ movq(rbx, Operand(rsp, rcx, times_pointer_size, 0));
268 __ movq(Operand(rsp, rcx, times_pointer_size, 1 * kPointerSize), rbx);
269 __ decq(rcx);
270 __ j(not_zero, &loop);
271 }
272
273 // 5. Remove TOS (copy of last arguments), but keep return address.
274 __ pop(rbx);
275 __ pop(rcx);
276 __ push(rbx);
277 __ decq(rax);
278
279 // 6. Check that function really was a function and get the code to
280 // call from the function and check that the number of expected
281 // arguments matches what we're providing.
282 { Label invoke, trampoline;
283 __ testq(rdi, rdi);
284 __ j(not_zero, &invoke);
285 __ xor_(rbx, rbx);
286 __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
287 __ bind(&trampoline);
288 __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
289 RelocInfo::CODE_TARGET);
290
291 __ bind(&invoke);
292 __ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
293 __ movsxlq(rbx,
294 FieldOperand(rdx, SharedFunctionInfo::kFormalParameterCountOffset));
295 __ movq(rdx, FieldOperand(rdx, SharedFunctionInfo::kCodeOffset));
296 __ lea(rdx, FieldOperand(rdx, Code::kHeaderSize));
297 __ cmpq(rax, rbx);
298 __ j(not_equal, &trampoline);
299 }
300
301 // 7. Jump (tail-call) to the code in register edx without checking arguments.
302 ParameterCount expected(0);
303 __ InvokeCode(rdx, expected, expected, JUMP_FUNCTION);
304 }
305
306
Generate_FunctionApply(MacroAssembler * masm)307 void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
308 // Stack at entry:
309 // rsp: return address
310 // rsp+8: arguments
311 // rsp+16: receiver ("this")
312 // rsp+24: function
313 __ EnterInternalFrame();
314 // Stack frame:
315 // rbp: Old base pointer
316 // rbp[1]: return address
317 // rbp[2]: function arguments
318 // rbp[3]: receiver
319 // rbp[4]: function
320 static const int kArgumentsOffset = 2 * kPointerSize;
321 static const int kReceiverOffset = 3 * kPointerSize;
322 static const int kFunctionOffset = 4 * kPointerSize;
323 __ push(Operand(rbp, kFunctionOffset));
324 __ push(Operand(rbp, kArgumentsOffset));
325 __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
326
327 if (FLAG_check_stack) {
328 // We need to catch preemptions right here, otherwise an unlucky preemption
329 // could show up as a failed apply.
330 Label retry_preemption;
331 Label no_preemption;
332 __ bind(&retry_preemption);
333 ExternalReference stack_guard_limit =
334 ExternalReference::address_of_stack_guard_limit();
335 __ movq(kScratchRegister, stack_guard_limit);
336 __ movq(rcx, rsp);
337 __ subq(rcx, Operand(kScratchRegister, 0));
338 // rcx contains the difference between the stack limit and the stack top.
339 // We use it below to check that there is enough room for the arguments.
340 __ j(above, &no_preemption);
341
342 // Preemption!
343 // Because runtime functions always remove the receiver from the stack, we
344 // have to fake one to avoid underflowing the stack.
345 __ push(rax);
346 __ push(Immediate(Smi::FromInt(0)));
347
348 // Do call to runtime routine.
349 __ CallRuntime(Runtime::kStackGuard, 1);
350 __ pop(rax);
351 __ jmp(&retry_preemption);
352
353 __ bind(&no_preemption);
354
355 Label okay;
356 // Make rdx the space we need for the array when it is unrolled onto the
357 // stack.
358 __ movq(rdx, rax);
359 __ shl(rdx, Immediate(kPointerSizeLog2 - kSmiTagSize));
360 __ cmpq(rcx, rdx);
361 __ j(greater, &okay);
362
363 // Too bad: Out of stack space.
364 __ push(Operand(rbp, kFunctionOffset));
365 __ push(rax);
366 __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION);
367 __ bind(&okay);
368 }
369
370 // Push current index and limit.
371 const int kLimitOffset =
372 StandardFrameConstants::kExpressionsOffset - 1 * kPointerSize;
373 const int kIndexOffset = kLimitOffset - 1 * kPointerSize;
374 __ push(rax); // limit
375 __ push(Immediate(0)); // index
376
377 // Change context eagerly to get the right global object if
378 // necessary.
379 __ movq(rdi, Operand(rbp, kFunctionOffset));
380 __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
381
382 // Compute the receiver.
383 Label call_to_object, use_global_receiver, push_receiver;
384 __ movq(rbx, Operand(rbp, kReceiverOffset));
385 __ testl(rbx, Immediate(kSmiTagMask));
386 __ j(zero, &call_to_object);
387 __ CompareRoot(rbx, Heap::kNullValueRootIndex);
388 __ j(equal, &use_global_receiver);
389 __ CompareRoot(rbx, Heap::kUndefinedValueRootIndex);
390 __ j(equal, &use_global_receiver);
391
392 // If given receiver is already a JavaScript object then there's no
393 // reason for converting it.
394 __ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, rcx);
395 __ j(below, &call_to_object);
396 __ CmpInstanceType(rcx, LAST_JS_OBJECT_TYPE);
397 __ j(below_equal, &push_receiver);
398
399 // Convert the receiver to an object.
400 __ bind(&call_to_object);
401 __ push(rbx);
402 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
403 __ movq(rbx, rax);
404 __ jmp(&push_receiver);
405
406 // Use the current global receiver object as the receiver.
407 __ bind(&use_global_receiver);
408 const int kGlobalOffset =
409 Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
410 __ movq(rbx, FieldOperand(rsi, kGlobalOffset));
411 __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
412
413 // Push the receiver.
414 __ bind(&push_receiver);
415 __ push(rbx);
416
417 // Copy all arguments from the array to the stack.
418 Label entry, loop;
419 __ movq(rax, Operand(rbp, kIndexOffset));
420 __ jmp(&entry);
421 __ bind(&loop);
422 __ movq(rcx, Operand(rbp, kArgumentsOffset)); // load arguments
423 __ push(rcx);
424 __ push(rax);
425
426 // Use inline caching to speed up access to arguments.
427 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
428 __ Call(ic, RelocInfo::CODE_TARGET);
429 // It is important that we do not have a test instruction after the
430 // call. A test instruction after the call is used to indicate that
431 // we have generated an inline version of the keyed load. In this
432 // case, we know that we are not generating a test instruction next.
433
434 // Remove IC arguments from the stack and push the nth argument.
435 __ addq(rsp, Immediate(2 * kPointerSize));
436 __ push(rax);
437
438 // Update the index on the stack and in register rax.
439 __ movq(rax, Operand(rbp, kIndexOffset));
440 __ addq(rax, Immediate(Smi::FromInt(1)));
441 __ movq(Operand(rbp, kIndexOffset), rax);
442
443 __ bind(&entry);
444 __ cmpq(rax, Operand(rbp, kLimitOffset));
445 __ j(not_equal, &loop);
446
447 // Invoke the function.
448 ParameterCount actual(rax);
449 __ shr(rax, Immediate(kSmiTagSize));
450 __ movq(rdi, Operand(rbp, kFunctionOffset));
451 __ InvokeFunction(rdi, actual, CALL_FUNCTION);
452
453 __ LeaveInternalFrame();
454 __ ret(3 * kPointerSize); // remove function, receiver, and arguments
455 }
456
457
Generate_JSConstructCall(MacroAssembler * masm)458 void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
459 // ----------- S t a t e -------------
460 // -- rax: number of arguments
461 // -- rdi: constructor function
462 // -----------------------------------
463
464 Label non_function_call;
465 // Check that function is not a smi.
466 __ testl(rdi, Immediate(kSmiTagMask));
467 __ j(zero, &non_function_call);
468 // Check that function is a JSFunction.
469 __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
470 __ j(not_equal, &non_function_call);
471
472 // Jump to the function-specific construct stub.
473 __ movq(rbx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
474 __ movq(rbx, FieldOperand(rbx, SharedFunctionInfo::kConstructStubOffset));
475 __ lea(rbx, FieldOperand(rbx, Code::kHeaderSize));
476 __ jmp(rbx);
477
478 // edi: called object
479 // eax: number of arguments
480 __ bind(&non_function_call);
481
482 // Set expected number of arguments to zero (not changing eax).
483 __ movq(rbx, Immediate(0));
484 __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
485 __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
486 RelocInfo::CODE_TARGET);
487 }
488
489
Generate_JSConstructStubGeneric(MacroAssembler * masm)490 void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
491 // Enter a construct frame.
492 __ EnterConstructFrame();
493
494 // Store a smi-tagged arguments count on the stack.
495 __ shl(rax, Immediate(kSmiTagSize));
496 __ push(rax);
497
498 // Push the function to invoke on the stack.
499 __ push(rdi);
500
501 // Try to allocate the object without transitioning into C code. If any of the
502 // preconditions is not met, the code bails out to the runtime call.
503 Label rt_call, allocated;
504 if (FLAG_inline_new) {
505 Label undo_allocation;
506
507 #ifdef ENABLE_DEBUGGER_SUPPORT
508 ExternalReference debug_step_in_fp =
509 ExternalReference::debug_step_in_fp_address();
510 __ movq(kScratchRegister, debug_step_in_fp);
511 __ cmpq(Operand(kScratchRegister, 0), Immediate(0));
512 __ j(not_equal, &rt_call);
513 #endif
514
515 // Verified that the constructor is a JSFunction.
516 // Load the initial map and verify that it is in fact a map.
517 // rdi: constructor
518 __ movq(rax, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset));
519 // Will both indicate a NULL and a Smi
520 __ testl(rax, Immediate(kSmiTagMask));
521 __ j(zero, &rt_call);
522 // rdi: constructor
523 // rax: initial map (if proven valid below)
524 __ CmpObjectType(rax, MAP_TYPE, rbx);
525 __ j(not_equal, &rt_call);
526
527 // Check that the constructor is not constructing a JSFunction (see comments
528 // in Runtime_NewObject in runtime.cc). In which case the initial map's
529 // instance type would be JS_FUNCTION_TYPE.
530 // rdi: constructor
531 // rax: initial map
532 __ CmpInstanceType(rax, JS_FUNCTION_TYPE);
533 __ j(equal, &rt_call);
534
535 // Now allocate the JSObject on the heap.
536 __ movzxbq(rdi, FieldOperand(rax, Map::kInstanceSizeOffset));
537 __ shl(rdi, Immediate(kPointerSizeLog2));
538 // rdi: size of new object
539 __ AllocateObjectInNewSpace(rdi,
540 rbx,
541 rdi,
542 no_reg,
543 &rt_call,
544 NO_ALLOCATION_FLAGS);
545 // Allocated the JSObject, now initialize the fields.
546 // rax: initial map
547 // rbx: JSObject (not HeapObject tagged - the actual address).
548 // rdi: start of next object
549 __ movq(Operand(rbx, JSObject::kMapOffset), rax);
550 __ LoadRoot(rcx, Heap::kEmptyFixedArrayRootIndex);
551 __ movq(Operand(rbx, JSObject::kPropertiesOffset), rcx);
552 __ movq(Operand(rbx, JSObject::kElementsOffset), rcx);
553 // Set extra fields in the newly allocated object.
554 // rax: initial map
555 // rbx: JSObject
556 // rdi: start of next object
557 { Label loop, entry;
558 __ LoadRoot(rdx, Heap::kUndefinedValueRootIndex);
559 __ lea(rcx, Operand(rbx, JSObject::kHeaderSize));
560 __ jmp(&entry);
561 __ bind(&loop);
562 __ movq(Operand(rcx, 0), rdx);
563 __ addq(rcx, Immediate(kPointerSize));
564 __ bind(&entry);
565 __ cmpq(rcx, rdi);
566 __ j(less, &loop);
567 }
568
569 // Add the object tag to make the JSObject real, so that we can continue and
570 // jump into the continuation code at any time from now on. Any failures
571 // need to undo the allocation, so that the heap is in a consistent state
572 // and verifiable.
573 // rax: initial map
574 // rbx: JSObject
575 // rdi: start of next object
576 __ or_(rbx, Immediate(kHeapObjectTag));
577
578 // Check if a non-empty properties array is needed.
579 // Allocate and initialize a FixedArray if it is.
580 // rax: initial map
581 // rbx: JSObject
582 // rdi: start of next object
583 // Calculate total properties described map.
584 __ movzxbq(rdx, FieldOperand(rax, Map::kUnusedPropertyFieldsOffset));
585 __ movzxbq(rcx, FieldOperand(rax, Map::kPreAllocatedPropertyFieldsOffset));
586 __ addq(rdx, rcx);
587 // Calculate unused properties past the end of the in-object properties.
588 __ movzxbq(rcx, FieldOperand(rax, Map::kInObjectPropertiesOffset));
589 __ subq(rdx, rcx);
590 // Done if no extra properties are to be allocated.
591 __ j(zero, &allocated);
592 __ Assert(positive, "Property allocation count failed.");
593
594 // Scale the number of elements by pointer size and add the header for
595 // FixedArrays to the start of the next object calculation from above.
596 // rbx: JSObject
597 // rdi: start of next object (will be start of FixedArray)
598 // rdx: number of elements in properties array
599 __ AllocateObjectInNewSpace(FixedArray::kHeaderSize,
600 times_pointer_size,
601 rdx,
602 rdi,
603 rax,
604 no_reg,
605 &undo_allocation,
606 RESULT_CONTAINS_TOP);
607
608 // Initialize the FixedArray.
609 // rbx: JSObject
610 // rdi: FixedArray
611 // rdx: number of elements
612 // rax: start of next object
613 __ LoadRoot(rcx, Heap::kFixedArrayMapRootIndex);
614 __ movq(Operand(rdi, JSObject::kMapOffset), rcx); // setup the map
615 __ movl(Operand(rdi, FixedArray::kLengthOffset), rdx); // and length
616
617 // Initialize the fields to undefined.
618 // rbx: JSObject
619 // rdi: FixedArray
620 // rax: start of next object
621 // rdx: number of elements
622 { Label loop, entry;
623 __ LoadRoot(rdx, Heap::kUndefinedValueRootIndex);
624 __ lea(rcx, Operand(rdi, FixedArray::kHeaderSize));
625 __ jmp(&entry);
626 __ bind(&loop);
627 __ movq(Operand(rcx, 0), rdx);
628 __ addq(rcx, Immediate(kPointerSize));
629 __ bind(&entry);
630 __ cmpq(rcx, rax);
631 __ j(below, &loop);
632 }
633
634 // Store the initialized FixedArray into the properties field of
635 // the JSObject
636 // rbx: JSObject
637 // rdi: FixedArray
638 __ or_(rdi, Immediate(kHeapObjectTag)); // add the heap tag
639 __ movq(FieldOperand(rbx, JSObject::kPropertiesOffset), rdi);
640
641
642 // Continue with JSObject being successfully allocated
643 // rbx: JSObject
644 __ jmp(&allocated);
645
646 // Undo the setting of the new top so that the heap is verifiable. For
647 // example, the map's unused properties potentially do not match the
648 // allocated objects unused properties.
649 // rbx: JSObject (previous new top)
650 __ bind(&undo_allocation);
651 __ UndoAllocationInNewSpace(rbx);
652 }
653
654 // Allocate the new receiver object using the runtime call.
655 // rdi: function (constructor)
656 __ bind(&rt_call);
657 // Must restore rdi (constructor) before calling runtime.
658 __ movq(rdi, Operand(rsp, 0));
659 __ push(rdi);
660 __ CallRuntime(Runtime::kNewObject, 1);
661 __ movq(rbx, rax); // store result in rbx
662
663 // New object allocated.
664 // rbx: newly allocated object
665 __ bind(&allocated);
666 // Retrieve the function from the stack.
667 __ pop(rdi);
668
669 // Retrieve smi-tagged arguments count from the stack.
670 __ movq(rax, Operand(rsp, 0));
671 __ shr(rax, Immediate(kSmiTagSize));
672
673 // Push the allocated receiver to the stack. We need two copies
674 // because we may have to return the original one and the calling
675 // conventions dictate that the called function pops the receiver.
676 __ push(rbx);
677 __ push(rbx);
678
679 // Setup pointer to last argument.
680 __ lea(rbx, Operand(rbp, StandardFrameConstants::kCallerSPOffset));
681
682 // Copy arguments and receiver to the expression stack.
683 Label loop, entry;
684 __ movq(rcx, rax);
685 __ jmp(&entry);
686 __ bind(&loop);
687 __ push(Operand(rbx, rcx, times_pointer_size, 0));
688 __ bind(&entry);
689 __ decq(rcx);
690 __ j(greater_equal, &loop);
691
692 // Call the function.
693 ParameterCount actual(rax);
694 __ InvokeFunction(rdi, actual, CALL_FUNCTION);
695
696 // Restore context from the frame.
697 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
698
699 // If the result is an object (in the ECMA sense), we should get rid
700 // of the receiver and use the result; see ECMA-262 section 13.2.2-7
701 // on page 74.
702 Label use_receiver, exit;
703 // If the result is a smi, it is *not* an object in the ECMA sense.
704 __ testl(rax, Immediate(kSmiTagMask));
705 __ j(zero, &use_receiver);
706
707 // If the type of the result (stored in its map) is less than
708 // FIRST_JS_OBJECT_TYPE, it is not an object in the ECMA sense.
709 __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx);
710 __ j(above_equal, &exit);
711
712 // Throw away the result of the constructor invocation and use the
713 // on-stack receiver as the result.
714 __ bind(&use_receiver);
715 __ movq(rax, Operand(rsp, 0));
716
717 // Restore the arguments count and leave the construct frame.
718 __ bind(&exit);
719 __ movq(rbx, Operand(rsp, kPointerSize)); // get arguments count
720 __ LeaveConstructFrame();
721
722 // Remove caller arguments from the stack and return.
723 ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
724 __ pop(rcx);
725 __ lea(rsp, Operand(rsp, rbx, times_4, 1 * kPointerSize)); // 1 ~ receiver
726 __ push(rcx);
727 __ IncrementCounter(&Counters::constructed_objects, 1);
728 __ ret(0);
729 }
730
731
Generate_JSEntryTrampolineHelper(MacroAssembler * masm,bool is_construct)732 static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
733 bool is_construct) {
734 // Expects five C++ function parameters.
735 // - Address entry (ignored)
736 // - JSFunction* function (
737 // - Object* receiver
738 // - int argc
739 // - Object*** argv
740 // (see Handle::Invoke in execution.cc).
741
742 // Platform specific argument handling. After this, the stack contains
743 // an internal frame and the pushed function and receiver, and
744 // register rax and rbx holds the argument count and argument array,
745 // while rdi holds the function pointer and rsi the context.
746 #ifdef _WIN64
747 // MSVC parameters in:
748 // rcx : entry (ignored)
749 // rdx : function
750 // r8 : receiver
751 // r9 : argc
752 // [rsp+0x20] : argv
753
754 // Clear the context before we push it when entering the JS frame.
755 __ xor_(rsi, rsi);
756 __ EnterInternalFrame();
757
758 // Load the function context into rsi.
759 __ movq(rsi, FieldOperand(rdx, JSFunction::kContextOffset));
760
761 // Push the function and the receiver onto the stack.
762 __ push(rdx);
763 __ push(r8);
764
765 // Load the number of arguments and setup pointer to the arguments.
766 __ movq(rax, r9);
767 // Load the previous frame pointer to access C argument on stack
768 __ movq(kScratchRegister, Operand(rbp, 0));
769 __ movq(rbx, Operand(kScratchRegister, EntryFrameConstants::kArgvOffset));
770 // Load the function pointer into rdi.
771 __ movq(rdi, rdx);
772 #else // !defined(_WIN64)
773 // GCC parameters in:
774 // rdi : entry (ignored)
775 // rsi : function
776 // rdx : receiver
777 // rcx : argc
778 // r8 : argv
779
780 __ movq(rdi, rsi);
781 // rdi : function
782
783 // Clear the context before we push it when entering the JS frame.
784 __ xor_(rsi, rsi);
785 // Enter an internal frame.
786 __ EnterInternalFrame();
787
788 // Push the function and receiver and setup the context.
789 __ push(rdi);
790 __ push(rdx);
791 __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
792
793 // Load the number of arguments and setup pointer to the arguments.
794 __ movq(rax, rcx);
795 __ movq(rbx, r8);
796 #endif // _WIN64
797
798 // Set up the roots register.
799 ExternalReference roots_address = ExternalReference::roots_address();
800 __ movq(r13, roots_address);
801
802 // Current stack contents:
803 // [rsp + 2 * kPointerSize ... ]: Internal frame
804 // [rsp + kPointerSize] : function
805 // [rsp] : receiver
806 // Current register contents:
807 // rax : argc
808 // rbx : argv
809 // rsi : context
810 // rdi : function
811
812 // Copy arguments to the stack in a loop.
813 // Register rbx points to array of pointers to handle locations.
814 // Push the values of these handles.
815 Label loop, entry;
816 __ xor_(rcx, rcx); // Set loop variable to 0.
817 __ jmp(&entry);
818 __ bind(&loop);
819 __ movq(kScratchRegister, Operand(rbx, rcx, times_pointer_size, 0));
820 __ push(Operand(kScratchRegister, 0)); // dereference handle
821 __ addq(rcx, Immediate(1));
822 __ bind(&entry);
823 __ cmpq(rcx, rax);
824 __ j(not_equal, &loop);
825
826 // Invoke the code.
827 if (is_construct) {
828 // Expects rdi to hold function pointer.
829 __ Call(Handle<Code>(Builtins::builtin(Builtins::JSConstructCall)),
830 RelocInfo::CODE_TARGET);
831 } else {
832 ParameterCount actual(rax);
833 // Function must be in rdi.
834 __ InvokeFunction(rdi, actual, CALL_FUNCTION);
835 }
836
837 // Exit the JS frame. Notice that this also removes the empty
838 // context and the function left on the stack by the code
839 // invocation.
840 __ LeaveInternalFrame();
841 // TODO(X64): Is argument correct? Is there a receiver to remove?
842 __ ret(1 * kPointerSize); // remove receiver
843 }
844
845
Generate_JSEntryTrampoline(MacroAssembler * masm)846 void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) {
847 Generate_JSEntryTrampolineHelper(masm, false);
848 }
849
850
Generate_JSConstructEntryTrampoline(MacroAssembler * masm)851 void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
852 Generate_JSEntryTrampolineHelper(masm, true);
853 }
854
855 } } // namespace v8::internal
856