OLD | NEW |
| (Empty) |
1 // Copyright 2006-2008 the V8 project authors. All rights reserved. | |
2 // Redistribution and use in source and binary forms, with or without | |
3 // modification, are permitted provided that the following conditions are | |
4 // met: | |
5 // | |
6 // * Redistributions of source code must retain the above copyright | |
7 // notice, this list of conditions and the following disclaimer. | |
8 // * Redistributions in binary form must reproduce the above | |
9 // copyright notice, this list of conditions and the following | |
10 // disclaimer in the documentation and/or other materials provided | |
11 // with the distribution. | |
12 // * Neither the name of Google Inc. nor the names of its | |
13 // contributors may be used to endorse or promote products derived | |
14 // from this software without specific prior written permission. | |
15 // | |
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
27 | |
28 #include "v8.h" | |
29 | |
30 #include "codegen-inl.h" | |
31 | |
32 namespace v8 { namespace internal { | |
33 | |
34 | |
35 #define __ ACCESS_MASM(masm) | |
36 | |
37 | |
38 void Builtins::Generate_Adaptor(MacroAssembler* masm, CFunctionId id) { | |
39 // TODO(1238487): Don't pass the function in a static variable. | |
40 ExternalReference passed = ExternalReference::builtin_passed_function(); | |
41 __ mov(Operand::StaticVariable(passed), edi); | |
42 | |
43 // The actual argument count has already been loaded into register | |
44 // eax, but JumpToBuiltin expects eax to contain the number of | |
45 // arguments including the receiver. | |
46 __ inc(eax); | |
47 __ JumpToBuiltin(ExternalReference(id)); | |
48 } | |
49 | |
50 | |
51 void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { | |
52 // ----------- S t a t e ------------- | |
53 // -- eax: number of arguments | |
54 // -- edi: constructor function | |
55 // ----------------------------------- | |
56 | |
57 // Enter a construct frame. | |
58 __ EnterConstructFrame(); | |
59 | |
60 // Store a smi-tagged arguments count on the stack. | |
61 __ shl(eax, kSmiTagSize); | |
62 __ push(eax); | |
63 | |
64 // Push the function to invoke on the stack. | |
65 __ push(edi); | |
66 | |
67 // Try to allocate the object without transitioning into C code. If any of the | |
68 // preconditions is not met, the code bails out to the runtime call. | |
69 Label rt_call, allocated; | |
70 if (FLAG_inline_new) { | |
71 Label undo_allocation; | |
72 #ifdef ENABLE_DEBUGGER_SUPPORT | |
73 ExternalReference debug_step_in_fp = | |
74 ExternalReference::debug_step_in_fp_address(); | |
75 __ cmp(Operand::StaticVariable(debug_step_in_fp), Immediate(0)); | |
76 __ j(not_equal, &rt_call); | |
77 #endif | |
78 // Check that function is not a Smi. | |
79 __ test(edi, Immediate(kSmiTagMask)); | |
80 __ j(zero, &rt_call); | |
81 // Check that function is a JSFunction | |
82 __ CmpObjectType(edi, JS_FUNCTION_TYPE, eax); | |
83 __ j(not_equal, &rt_call); | |
84 | |
85 // Verified that the constructor is a JSFunction. | |
86 // Load the initial map and verify that it is in fact a map. | |
87 // edi: constructor | |
88 __ mov(eax, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset)); | |
89 // Will both indicate a NULL and a Smi | |
90 __ test(eax, Immediate(kSmiTagMask)); | |
91 __ j(zero, &rt_call); | |
92 // edi: constructor | |
93 // eax: initial map (if proven valid below) | |
94 __ CmpObjectType(eax, MAP_TYPE, ebx); | |
95 __ j(not_equal, &rt_call); | |
96 | |
97 // Check that the constructor is not constructing a JSFunction (see comments | |
98 // in Runtime_NewObject in runtime.cc). In which case the initial map's | |
99 // instance type would be JS_FUNCTION_TYPE. | |
100 // edi: constructor | |
101 // eax: initial map | |
102 __ CmpInstanceType(eax, JS_FUNCTION_TYPE); | |
103 __ j(equal, &rt_call); | |
104 | |
105 // Now allocate the JSObject on the heap. | |
106 // edi: constructor | |
107 // eax: initial map | |
108 __ movzx_b(edi, FieldOperand(eax, Map::kInstanceSizeOffset)); | |
109 __ shl(edi, kPointerSizeLog2); | |
110 // Make sure that the maximum heap object size will never cause us | |
111 // problem here, because it is always greater than the maximum | |
112 // instance size that can be represented in a byte. | |
113 ASSERT(Heap::MaxHeapObjectSize() >= (1 << kBitsPerByte)); | |
114 ExternalReference new_space_allocation_top = | |
115 ExternalReference::new_space_allocation_top_address(); | |
116 __ mov(ebx, Operand::StaticVariable(new_space_allocation_top)); | |
117 __ add(edi, Operand(ebx)); // Calculate new top | |
118 ExternalReference new_space_allocation_limit = | |
119 ExternalReference::new_space_allocation_limit_address(); | |
120 __ cmp(edi, Operand::StaticVariable(new_space_allocation_limit)); | |
121 __ j(greater_equal, &rt_call); | |
122 // Allocated the JSObject, now initialize the fields. | |
123 // eax: initial map | |
124 // ebx: JSObject | |
125 // edi: start of next object | |
126 __ mov(Operand(ebx, JSObject::kMapOffset), eax); | |
127 __ mov(ecx, Factory::empty_fixed_array()); | |
128 __ mov(Operand(ebx, JSObject::kPropertiesOffset), ecx); | |
129 __ mov(Operand(ebx, JSObject::kElementsOffset), ecx); | |
130 // Set extra fields in the newly allocated object. | |
131 // eax: initial map | |
132 // ebx: JSObject | |
133 // edi: start of next object | |
134 { Label loop, entry; | |
135 __ mov(edx, Factory::undefined_value()); | |
136 __ lea(ecx, Operand(ebx, JSObject::kHeaderSize)); | |
137 __ jmp(&entry); | |
138 __ bind(&loop); | |
139 __ mov(Operand(ecx, 0), edx); | |
140 __ add(Operand(ecx), Immediate(kPointerSize)); | |
141 __ bind(&entry); | |
142 __ cmp(ecx, Operand(edi)); | |
143 __ j(less, &loop); | |
144 } | |
145 | |
146 // Mostly done with the JSObject. Add the heap tag and store the new top, so | |
147 // that we can continue and jump into the continuation code at any time from | |
148 // now on. Any failures need to undo the setting of the new top, so that the | |
149 // heap is in a consistent state and verifiable. | |
150 // eax: initial map | |
151 // ebx: JSObject | |
152 // edi: start of next object | |
153 __ or_(Operand(ebx), Immediate(kHeapObjectTag)); | |
154 __ mov(Operand::StaticVariable(new_space_allocation_top), edi); | |
155 | |
156 // Check if a properties array should be setup and allocate one if needed. | |
157 // Otherwise initialize the properties to the empty_fixed_array as well. | |
158 // eax: initial map | |
159 // ebx: JSObject | |
160 // edi: start of next object | |
161 __ movzx_b(edx, FieldOperand(eax, Map::kUnusedPropertyFieldsOffset)); | |
162 __ movzx_b(ecx, FieldOperand(eax, Map::kInObjectPropertiesOffset)); | |
163 // Calculate unused properties past the end of the in-object properties. | |
164 __ sub(edx, Operand(ecx)); | |
165 __ test(edx, Operand(edx)); | |
166 // Done if no extra properties are to be allocated. | |
167 __ j(zero, &allocated); | |
168 | |
169 // Scale the number of elements by pointer size and add the header for | |
170 // FixedArrays to the start of the next object calculation from above. | |
171 // eax: initial map | |
172 // ebx: JSObject | |
173 // edi: start of next object (will be start of FixedArray) | |
174 // edx: number of elements in properties array | |
175 ASSERT(Heap::MaxHeapObjectSize() > | |
176 (FixedArray::kHeaderSize + 255*kPointerSize)); | |
177 __ lea(ecx, Operand(edi, edx, times_4, FixedArray::kHeaderSize)); | |
178 __ cmp(ecx, Operand::StaticVariable(new_space_allocation_limit)); | |
179 __ j(greater_equal, &undo_allocation); | |
180 __ mov(Operand::StaticVariable(new_space_allocation_top), ecx); | |
181 | |
182 // Initialize the FixedArray. | |
183 // ebx: JSObject | |
184 // edi: FixedArray | |
185 // edx: number of elements | |
186 // ecx: start of next object | |
187 __ mov(eax, Factory::fixed_array_map()); | |
188 __ mov(Operand(edi, JSObject::kMapOffset), eax); // setup the map | |
189 __ mov(Operand(edi, Array::kLengthOffset), edx); // and length | |
190 | |
191 // Initialize the fields to undefined. | |
192 // ebx: JSObject | |
193 // edi: FixedArray | |
194 // ecx: start of next object | |
195 { Label loop, entry; | |
196 __ mov(edx, Factory::undefined_value()); | |
197 __ lea(eax, Operand(edi, FixedArray::kHeaderSize)); | |
198 __ jmp(&entry); | |
199 __ bind(&loop); | |
200 __ mov(Operand(eax, 0), edx); | |
201 __ add(Operand(eax), Immediate(kPointerSize)); | |
202 __ bind(&entry); | |
203 __ cmp(eax, Operand(ecx)); | |
204 __ j(less, &loop); | |
205 } | |
206 | |
207 // Store the initialized FixedArray into the properties field of | |
208 // the JSObject | |
209 // ebx: JSObject | |
210 // edi: FixedArray | |
211 __ or_(Operand(edi), Immediate(kHeapObjectTag)); // add the heap tag | |
212 __ mov(FieldOperand(ebx, JSObject::kPropertiesOffset), edi); | |
213 | |
214 | |
215 // Continue with JSObject being successfully allocated | |
216 // ebx: JSObject | |
217 __ jmp(&allocated); | |
218 | |
219 // Undo the setting of the new top so that the heap is verifiable. For | |
220 // example, the map's unused properties potentially do not match the | |
221 // allocated objects unused properties. | |
222 // ebx: JSObject (previous new top) | |
223 __ bind(&undo_allocation); | |
224 __ xor_(Operand(ebx), Immediate(kHeapObjectTag)); // clear the heap tag | |
225 __ mov(Operand::StaticVariable(new_space_allocation_top), ebx); | |
226 } | |
227 | |
228 // Allocate the new receiver object using the runtime call. | |
229 // edi: function (constructor) | |
230 __ bind(&rt_call); | |
231 // Must restore edi (constructor) before calling runtime. | |
232 __ mov(edi, Operand(esp, 0)); | |
233 __ push(edi); | |
234 __ CallRuntime(Runtime::kNewObject, 1); | |
235 __ mov(ebx, Operand(eax)); // store result in ebx | |
236 | |
237 // New object allocated. | |
238 // ebx: newly allocated object | |
239 __ bind(&allocated); | |
240 // Retrieve the function from the stack. | |
241 __ pop(edi); | |
242 | |
243 // Retrieve smi-tagged arguments count from the stack. | |
244 __ mov(eax, Operand(esp, 0)); | |
245 __ shr(eax, kSmiTagSize); | |
246 | |
247 // Push the allocated receiver to the stack. We need two copies | |
248 // because we may have to return the original one and the calling | |
249 // conventions dictate that the called function pops the receiver. | |
250 __ push(ebx); | |
251 __ push(ebx); | |
252 | |
253 // Setup pointer to last argument. | |
254 __ lea(ebx, Operand(ebp, StandardFrameConstants::kCallerSPOffset)); | |
255 | |
256 // Copy arguments and receiver to the expression stack. | |
257 Label loop, entry; | |
258 __ mov(ecx, Operand(eax)); | |
259 __ jmp(&entry); | |
260 __ bind(&loop); | |
261 __ push(Operand(ebx, ecx, times_4, 0)); | |
262 __ bind(&entry); | |
263 __ dec(ecx); | |
264 __ j(greater_equal, &loop); | |
265 | |
266 // Call the function. | |
267 ParameterCount actual(eax); | |
268 __ InvokeFunction(edi, actual, CALL_FUNCTION); | |
269 | |
270 // Restore context from the frame. | |
271 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); | |
272 | |
273 // If the result is an object (in the ECMA sense), we should get rid | |
274 // of the receiver and use the result; see ECMA-262 section 13.2.2-7 | |
275 // on page 74. | |
276 Label use_receiver, exit; | |
277 | |
278 // If the result is a smi, it is *not* an object in the ECMA sense. | |
279 __ test(eax, Immediate(kSmiTagMask)); | |
280 __ j(zero, &use_receiver, not_taken); | |
281 | |
282 // If the type of the result (stored in its map) is less than | |
283 // FIRST_JS_OBJECT_TYPE, it is not an object in the ECMA sense. | |
284 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); | |
285 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); | |
286 __ cmp(ecx, FIRST_JS_OBJECT_TYPE); | |
287 __ j(greater_equal, &exit, not_taken); | |
288 | |
289 // Throw away the result of the constructor invocation and use the | |
290 // on-stack receiver as the result. | |
291 __ bind(&use_receiver); | |
292 __ mov(eax, Operand(esp, 0)); | |
293 | |
294 // Restore the arguments count and leave the construct frame. | |
295 __ bind(&exit); | |
296 __ mov(ebx, Operand(esp, kPointerSize)); // get arguments count | |
297 __ LeaveConstructFrame(); | |
298 | |
299 // Remove caller arguments from the stack and return. | |
300 ASSERT(kSmiTagSize == 1 && kSmiTag == 0); | |
301 __ pop(ecx); | |
302 __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver | |
303 __ push(ecx); | |
304 __ ret(0); | |
305 } | |
306 | |
307 | |
308 static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, | |
309 bool is_construct) { | |
310 // Clear the context before we push it when entering the JS frame. | |
311 __ xor_(esi, Operand(esi)); // clear esi | |
312 | |
313 // Enter an internal frame. | |
314 __ EnterInternalFrame(); | |
315 | |
316 // Load the previous frame pointer (ebx) to access C arguments | |
317 __ mov(ebx, Operand(ebp, 0)); | |
318 | |
319 // Get the function from the frame and setup the context. | |
320 __ mov(ecx, Operand(ebx, EntryFrameConstants::kFunctionArgOffset)); | |
321 __ mov(esi, FieldOperand(ecx, JSFunction::kContextOffset)); | |
322 | |
323 // Push the function and the receiver onto the stack. | |
324 __ push(ecx); | |
325 __ push(Operand(ebx, EntryFrameConstants::kReceiverArgOffset)); | |
326 | |
327 // Load the number of arguments and setup pointer to the arguments. | |
328 __ mov(eax, Operand(ebx, EntryFrameConstants::kArgcOffset)); | |
329 __ mov(ebx, Operand(ebx, EntryFrameConstants::kArgvOffset)); | |
330 | |
331 // Copy arguments to the stack in a loop. | |
332 Label loop, entry; | |
333 __ xor_(ecx, Operand(ecx)); // clear ecx | |
334 __ jmp(&entry); | |
335 __ bind(&loop); | |
336 __ mov(edx, Operand(ebx, ecx, times_4, 0)); // push parameter from argv | |
337 __ push(Operand(edx, 0)); // dereference handle | |
338 __ inc(Operand(ecx)); | |
339 __ bind(&entry); | |
340 __ cmp(ecx, Operand(eax)); | |
341 __ j(not_equal, &loop); | |
342 | |
343 // Get the function from the stack and call it. | |
344 __ mov(edi, Operand(esp, eax, times_4, +1 * kPointerSize)); // +1 ~ receiver | |
345 | |
346 // Invoke the code. | |
347 if (is_construct) { | |
348 __ call(Handle<Code>(Builtins::builtin(Builtins::JSConstructCall)), | |
349 RelocInfo::CODE_TARGET); | |
350 } else { | |
351 ParameterCount actual(eax); | |
352 __ InvokeFunction(edi, actual, CALL_FUNCTION); | |
353 } | |
354 | |
355 // Exit the JS frame. Notice that this also removes the empty | |
356 // context and the function left on the stack by the code | |
357 // invocation. | |
358 __ LeaveInternalFrame(); | |
359 __ ret(1 * kPointerSize); // remove receiver | |
360 } | |
361 | |
362 | |
363 void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) { | |
364 Generate_JSEntryTrampolineHelper(masm, false); | |
365 } | |
366 | |
367 | |
368 void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) { | |
369 Generate_JSEntryTrampolineHelper(masm, true); | |
370 } | |
371 | |
372 | |
373 void Builtins::Generate_FunctionCall(MacroAssembler* masm) { | |
374 // 1. Make sure we have at least one argument. | |
375 { Label done; | |
376 __ test(eax, Operand(eax)); | |
377 __ j(not_zero, &done, taken); | |
378 __ pop(ebx); | |
379 __ push(Immediate(Factory::undefined_value())); | |
380 __ push(ebx); | |
381 __ inc(eax); | |
382 __ bind(&done); | |
383 } | |
384 | |
385 // 2. Get the function to call from the stack. | |
386 { Label done, non_function, function; | |
387 // +1 ~ return address. | |
388 __ mov(edi, Operand(esp, eax, times_4, +1 * kPointerSize)); | |
389 __ test(edi, Immediate(kSmiTagMask)); | |
390 __ j(zero, &non_function, not_taken); | |
391 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); | |
392 __ j(equal, &function, taken); | |
393 | |
394 // Non-function called: Clear the function to force exception. | |
395 __ bind(&non_function); | |
396 __ xor_(edi, Operand(edi)); | |
397 __ jmp(&done); | |
398 | |
399 // Function called: Change context eagerly to get the right global object. | |
400 __ bind(&function); | |
401 __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); | |
402 | |
403 __ bind(&done); | |
404 } | |
405 | |
406 // 3. Make sure first argument is an object; convert if necessary. | |
407 { Label call_to_object, use_global_receiver, patch_receiver, done; | |
408 __ mov(ebx, Operand(esp, eax, times_4, 0)); | |
409 | |
410 __ test(ebx, Immediate(kSmiTagMask)); | |
411 __ j(zero, &call_to_object); | |
412 | |
413 __ cmp(ebx, Factory::null_value()); | |
414 __ j(equal, &use_global_receiver); | |
415 __ cmp(ebx, Factory::undefined_value()); | |
416 __ j(equal, &use_global_receiver); | |
417 | |
418 __ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset)); | |
419 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); | |
420 __ cmp(ecx, FIRST_JS_OBJECT_TYPE); | |
421 __ j(less, &call_to_object); | |
422 __ cmp(ecx, LAST_JS_OBJECT_TYPE); | |
423 __ j(less_equal, &done); | |
424 | |
425 __ bind(&call_to_object); | |
426 __ EnterInternalFrame(); // preserves eax, ebx, edi | |
427 | |
428 // Store the arguments count on the stack (smi tagged). | |
429 ASSERT(kSmiTag == 0); | |
430 __ shl(eax, kSmiTagSize); | |
431 __ push(eax); | |
432 | |
433 __ push(edi); // save edi across the call | |
434 __ push(ebx); | |
435 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); | |
436 __ mov(ebx, eax); | |
437 __ pop(edi); // restore edi after the call | |
438 | |
439 // Get the arguments count and untag it. | |
440 __ pop(eax); | |
441 __ shr(eax, kSmiTagSize); | |
442 | |
443 __ LeaveInternalFrame(); | |
444 __ jmp(&patch_receiver); | |
445 | |
446 // Use the global receiver object from the called function as the receiver. | |
447 __ bind(&use_global_receiver); | |
448 const int kGlobalIndex = | |
449 Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; | |
450 __ mov(ebx, FieldOperand(esi, kGlobalIndex)); | |
451 __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset)); | |
452 | |
453 __ bind(&patch_receiver); | |
454 __ mov(Operand(esp, eax, times_4, 0), ebx); | |
455 | |
456 __ bind(&done); | |
457 } | |
458 | |
459 // 4. Shift stuff one slot down the stack. | |
460 { Label loop; | |
461 __ lea(ecx, Operand(eax, +1)); // +1 ~ copy receiver too | |
462 __ bind(&loop); | |
463 __ mov(ebx, Operand(esp, ecx, times_4, 0)); | |
464 __ mov(Operand(esp, ecx, times_4, kPointerSize), ebx); | |
465 __ dec(ecx); | |
466 __ j(not_zero, &loop); | |
467 } | |
468 | |
469 // 5. Remove TOS (copy of last arguments), but keep return address. | |
470 __ pop(ebx); | |
471 __ pop(ecx); | |
472 __ push(ebx); | |
473 __ dec(eax); | |
474 | |
475 // 6. Check that function really was a function and get the code to | |
476 // call from the function and check that the number of expected | |
477 // arguments matches what we're providing. | |
478 { Label invoke; | |
479 __ test(edi, Operand(edi)); | |
480 __ j(not_zero, &invoke, taken); | |
481 __ xor_(ebx, Operand(ebx)); | |
482 __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION); | |
483 __ jmp(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), | |
484 RelocInfo::CODE_TARGET); | |
485 | |
486 __ bind(&invoke); | |
487 __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); | |
488 __ mov(ebx, | |
489 FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset)); | |
490 __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset)); | |
491 __ lea(edx, FieldOperand(edx, Code::kHeaderSize)); | |
492 __ cmp(eax, Operand(ebx)); | |
493 __ j(not_equal, Handle<Code>(builtin(ArgumentsAdaptorTrampoline))); | |
494 } | |
495 | |
496 // 7. Jump (tail-call) to the code in register edx without checking arguments. | |
497 ParameterCount expected(0); | |
498 __ InvokeCode(Operand(edx), expected, expected, JUMP_FUNCTION); | |
499 } | |
500 | |
501 | |
502 void Builtins::Generate_FunctionApply(MacroAssembler* masm) { | |
503 __ EnterInternalFrame(); | |
504 | |
505 __ push(Operand(ebp, 4 * kPointerSize)); // push this | |
506 __ push(Operand(ebp, 2 * kPointerSize)); // push arguments | |
507 __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION); | |
508 | |
509 if (FLAG_check_stack) { | |
510 // We need to catch preemptions right here, otherwise an unlucky preemption | |
511 // could show up as a failed apply. | |
512 ExternalReference stack_guard_limit = | |
513 ExternalReference::address_of_stack_guard_limit(); | |
514 Label retry_preemption; | |
515 Label no_preemption; | |
516 __ bind(&retry_preemption); | |
517 __ mov(edi, Operand::StaticVariable(stack_guard_limit)); | |
518 __ cmp(esp, Operand(edi)); | |
519 __ j(above, &no_preemption, taken); | |
520 | |
521 // Preemption! | |
522 // Because builtins always remove the receiver from the stack, we | |
523 // have to fake one to avoid underflowing the stack. | |
524 __ push(eax); | |
525 __ push(Immediate(Smi::FromInt(0))); | |
526 | |
527 // Do call to runtime routine. | |
528 __ CallRuntime(Runtime::kStackGuard, 1); | |
529 __ pop(eax); | |
530 __ jmp(&retry_preemption); | |
531 | |
532 __ bind(&no_preemption); | |
533 | |
534 Label okay; | |
535 // Make ecx the space we have left. | |
536 __ mov(ecx, Operand(esp)); | |
537 __ sub(ecx, Operand(edi)); | |
538 // Make edx the space we need for the array when it is unrolled onto the | |
539 // stack. | |
540 __ mov(edx, Operand(eax)); | |
541 __ shl(edx, kPointerSizeLog2 - kSmiTagSize); | |
542 __ cmp(ecx, Operand(edx)); | |
543 __ j(greater, &okay, taken); | |
544 | |
545 // Too bad: Out of stack space. | |
546 __ push(Operand(ebp, 4 * kPointerSize)); // push this | |
547 __ push(eax); | |
548 __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION); | |
549 __ bind(&okay); | |
550 } | |
551 | |
552 // Push current index and limit. | |
553 const int kLimitOffset = | |
554 StandardFrameConstants::kExpressionsOffset - 1 * kPointerSize; | |
555 const int kIndexOffset = kLimitOffset - 1 * kPointerSize; | |
556 __ push(eax); // limit | |
557 __ push(Immediate(0)); // index | |
558 | |
559 // Change context eagerly to get the right global object if | |
560 // necessary. | |
561 __ mov(edi, Operand(ebp, 4 * kPointerSize)); | |
562 __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); | |
563 | |
564 // Compute the receiver. | |
565 Label call_to_object, use_global_receiver, push_receiver; | |
566 __ mov(ebx, Operand(ebp, 3 * kPointerSize)); | |
567 __ test(ebx, Immediate(kSmiTagMask)); | |
568 __ j(zero, &call_to_object); | |
569 __ cmp(ebx, Factory::null_value()); | |
570 __ j(equal, &use_global_receiver); | |
571 __ cmp(ebx, Factory::undefined_value()); | |
572 __ j(equal, &use_global_receiver); | |
573 | |
574 // If given receiver is already a JavaScript object then there's no | |
575 // reason for converting it. | |
576 __ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset)); | |
577 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); | |
578 __ cmp(ecx, FIRST_JS_OBJECT_TYPE); | |
579 __ j(less, &call_to_object); | |
580 __ cmp(ecx, LAST_JS_OBJECT_TYPE); | |
581 __ j(less_equal, &push_receiver); | |
582 | |
583 // Convert the receiver to an object. | |
584 __ bind(&call_to_object); | |
585 __ push(ebx); | |
586 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); | |
587 __ mov(ebx, Operand(eax)); | |
588 __ jmp(&push_receiver); | |
589 | |
590 // Use the current global receiver object as the receiver. | |
591 __ bind(&use_global_receiver); | |
592 const int kGlobalOffset = | |
593 Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; | |
594 __ mov(ebx, FieldOperand(esi, kGlobalOffset)); | |
595 __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset)); | |
596 | |
597 // Push the receiver. | |
598 __ bind(&push_receiver); | |
599 __ push(ebx); | |
600 | |
601 // Copy all arguments from the array to the stack. | |
602 Label entry, loop; | |
603 __ mov(eax, Operand(ebp, kIndexOffset)); | |
604 __ jmp(&entry); | |
605 __ bind(&loop); | |
606 __ mov(ecx, Operand(ebp, 2 * kPointerSize)); // load arguments | |
607 __ push(ecx); | |
608 __ push(eax); | |
609 | |
610 // Use inline caching to speed up access to arguments. | |
611 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); | |
612 __ call(ic, RelocInfo::CODE_TARGET); | |
613 // It is important that we do not have a test instruction after the | |
614 // call. A test instruction after the call is used to indicate that | |
615 // we have generated an inline version of the keyed load. In this | |
616 // case, we know that we are not generating a test instruction next. | |
617 | |
618 // Remove IC arguments from the stack and push the nth argument. | |
619 __ add(Operand(esp), Immediate(2 * kPointerSize)); | |
620 __ push(eax); | |
621 | |
622 // Update the index on the stack and in register eax. | |
623 __ mov(eax, Operand(ebp, kIndexOffset)); | |
624 __ add(Operand(eax), Immediate(1 << kSmiTagSize)); | |
625 __ mov(Operand(ebp, kIndexOffset), eax); | |
626 | |
627 __ bind(&entry); | |
628 __ cmp(eax, Operand(ebp, kLimitOffset)); | |
629 __ j(not_equal, &loop); | |
630 | |
631 // Invoke the function. | |
632 ParameterCount actual(eax); | |
633 __ shr(eax, kSmiTagSize); | |
634 __ mov(edi, Operand(ebp, 4 * kPointerSize)); | |
635 __ InvokeFunction(edi, actual, CALL_FUNCTION); | |
636 | |
637 __ LeaveInternalFrame(); | |
638 __ ret(3 * kPointerSize); // remove this, receiver, and arguments | |
639 } | |
640 | |
641 | |
642 static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) { | |
643 __ push(ebp); | |
644 __ mov(ebp, Operand(esp)); | |
645 | |
646 // Store the arguments adaptor context sentinel. | |
647 __ push(Immediate(ArgumentsAdaptorFrame::SENTINEL)); | |
648 | |
649 // Push the function on the stack. | |
650 __ push(edi); | |
651 | |
652 // Preserve the number of arguments on the stack. Must preserve both | |
653 // eax and ebx because these registers are used when copying the | |
654 // arguments and the receiver. | |
655 ASSERT(kSmiTagSize == 1); | |
656 __ lea(ecx, Operand(eax, eax, times_1, kSmiTag)); | |
657 __ push(ecx); | |
658 } | |
659 | |
660 | |
661 static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) { | |
662 // Retrieve the number of arguments from the stack. | |
663 __ mov(ebx, Operand(ebp, ArgumentsAdaptorFrameConstants::kLengthOffset)); | |
664 | |
665 // Leave the frame. | |
666 __ leave(); | |
667 | |
668 // Remove caller arguments from the stack. | |
669 ASSERT(kSmiTagSize == 1 && kSmiTag == 0); | |
670 __ pop(ecx); | |
671 __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver | |
672 __ push(ecx); | |
673 } | |
674 | |
675 | |
676 void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { | |
677 // ----------- S t a t e ------------- | |
678 // -- eax : actual number of arguments | |
679 // -- ebx : expected number of arguments | |
680 // -- edx : code entry to call | |
681 // ----------------------------------- | |
682 | |
683 Label invoke, dont_adapt_arguments; | |
684 __ IncrementCounter(&Counters::arguments_adaptors, 1); | |
685 | |
686 Label enough, too_few; | |
687 __ cmp(eax, Operand(ebx)); | |
688 __ j(less, &too_few); | |
689 __ cmp(ebx, SharedFunctionInfo::kDontAdaptArgumentsSentinel); | |
690 __ j(equal, &dont_adapt_arguments); | |
691 | |
692 { // Enough parameters: Actual >= expected. | |
693 __ bind(&enough); | |
694 EnterArgumentsAdaptorFrame(masm); | |
695 | |
696 // Copy receiver and all expected arguments. | |
697 const int offset = StandardFrameConstants::kCallerSPOffset; | |
698 __ lea(eax, Operand(ebp, eax, times_4, offset)); | |
699 __ mov(ecx, -1); // account for receiver | |
700 | |
701 Label copy; | |
702 __ bind(©); | |
703 __ inc(ecx); | |
704 __ push(Operand(eax, 0)); | |
705 __ sub(Operand(eax), Immediate(kPointerSize)); | |
706 __ cmp(ecx, Operand(ebx)); | |
707 __ j(less, ©); | |
708 __ jmp(&invoke); | |
709 } | |
710 | |
711 { // Too few parameters: Actual < expected. | |
712 __ bind(&too_few); | |
713 EnterArgumentsAdaptorFrame(masm); | |
714 | |
715 // Copy receiver and all actual arguments. | |
716 const int offset = StandardFrameConstants::kCallerSPOffset; | |
717 __ lea(edi, Operand(ebp, eax, times_4, offset)); | |
718 __ mov(ecx, -1); // account for receiver | |
719 | |
720 Label copy; | |
721 __ bind(©); | |
722 __ inc(ecx); | |
723 __ push(Operand(edi, 0)); | |
724 __ sub(Operand(edi), Immediate(kPointerSize)); | |
725 __ cmp(ecx, Operand(eax)); | |
726 __ j(less, ©); | |
727 | |
728 // Fill remaining expected arguments with undefined values. | |
729 Label fill; | |
730 __ bind(&fill); | |
731 __ inc(ecx); | |
732 __ push(Immediate(Factory::undefined_value())); | |
733 __ cmp(ecx, Operand(ebx)); | |
734 __ j(less, &fill); | |
735 | |
736 // Restore function pointer. | |
737 __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); | |
738 } | |
739 | |
740 // Call the entry point. | |
741 __ bind(&invoke); | |
742 __ call(Operand(edx)); | |
743 | |
744 // Leave frame and return. | |
745 LeaveArgumentsAdaptorFrame(masm); | |
746 __ ret(0); | |
747 | |
748 // ------------------------------------------- | |
749 // Dont adapt arguments. | |
750 // ------------------------------------------- | |
751 __ bind(&dont_adapt_arguments); | |
752 __ jmp(Operand(edx)); | |
753 } | |
754 | |
755 | |
756 #undef __ | |
757 | |
758 } } // namespace v8::internal | |
OLD | NEW |