blob: 55e10f311956668ff64896595e3dd460cdb95847 [file] [log] [blame]
ager@chromium.org9258b6b2008-09-11 09:11:10 +00001// Copyright 2006-2008 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// 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#include "debug.h"
32#include "runtime.h"
33
34namespace v8 { namespace internal {
35
36
37#define __ masm->
38
39
kasperl@chromium.orgb9123622008-09-17 14:05:56 +000040void Builtins::Generate_Adaptor(MacroAssembler* masm, CFunctionId id) {
41 // TODO(1238487): Don't pass the function in a static variable.
42 ExternalReference passed = ExternalReference::builtin_passed_function();
43 __ mov(Operand::StaticVariable(passed), edi);
44
45 // The actual argument count has already been loaded into register
46 // eax, but JumpToBuiltin expects eax to contain the number of
47 // arguments including the receiver.
48 __ inc(eax);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000049 __ JumpToBuiltin(ExternalReference(id));
50}
51
52
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000053void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +000054 // ----------- S t a t e -------------
55 // -- eax: number of arguments
56 // -- edi: constructor function
57 // -----------------------------------
58
ager@chromium.org7c537e22008-10-16 08:43:32 +000059 // Enter a construct frame.
60 __ EnterConstructFrame();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000061
62 // Store a smi-tagged arguments count on the stack.
63 __ shl(eax, kSmiTagSize);
64 __ push(eax);
65
66 // Push the function to invoke on the stack.
67 __ push(edi);
68
69 // Try to allocate the object without transitioning into C code. If any of the
70 // preconditions is not met, the code bails out to the runtime call.
71 Label rt_call, allocated;
72 if (FLAG_inline_new) {
73 Label undo_allocation;
74 ExternalReference debug_step_in_fp =
75 ExternalReference::debug_step_in_fp_address();
76 __ cmp(Operand::StaticVariable(debug_step_in_fp), Immediate(0));
77 __ j(not_equal, &rt_call);
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 __ mov(eax, FieldOperand(edi, JSFunction::kMapOffset));
83 __ movzx_b(eax, FieldOperand(eax, Map::kInstanceTypeOffset));
84 __ cmp(eax, JS_FUNCTION_TYPE);
85 __ j(not_equal, &rt_call);
86
87 // Verified that the constructor is a JSFunction.
88 // Load the initial map and verify that it is in fact a map.
89 // edi: constructor
90 __ mov(eax, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
91 // Will both indicate a NULL and a Smi
92 __ test(eax, Immediate(kSmiTagMask));
93 __ j(zero, &rt_call);
94 // edi: constructor
95 // eax: initial map (if proven valid below)
96 __ mov(ebx, FieldOperand(eax, JSFunction::kMapOffset));
97 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
98 __ cmp(ebx, MAP_TYPE);
99 __ j(not_equal, &rt_call);
100
101 // Check that the constructor is not constructing a JSFunction (see comments
102 // in Runtime_NewObject in runtime.cc). In which case the initial map's
103 // instance type would be JS_FUNCTION_TYPE.
104 // edi: constructor
105 // eax: initial map
106 __ movzx_b(ebx, FieldOperand(eax, Map::kInstanceTypeOffset));
107 __ cmp(ebx, JS_FUNCTION_TYPE);
108 __ j(equal, &rt_call);
109
110 // Now allocate the JSObject on the heap.
111 // edi: constructor
112 // eax: initial map
113 __ movzx_b(edi, FieldOperand(eax, Map::kInstanceSizeOffset));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000114 __ shl(edi, kPointerSizeLog2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000115 // Make sure that the maximum heap object size will never cause us
116 // problem here, because it is always greater than the maximum
117 // instance size that can be represented in a byte.
118 ASSERT(Heap::MaxHeapObjectSize() >= (1 << kBitsPerByte));
119 ExternalReference new_space_allocation_top =
120 ExternalReference::new_space_allocation_top_address();
121 __ mov(ebx, Operand::StaticVariable(new_space_allocation_top));
122 __ add(edi, Operand(ebx)); // Calculate new top
123 ExternalReference new_space_allocation_limit =
124 ExternalReference::new_space_allocation_limit_address();
125 __ cmp(edi, Operand::StaticVariable(new_space_allocation_limit));
126 __ j(greater_equal, &rt_call);
127 // Allocated the JSObject, now initialize the fields.
128 // eax: initial map
129 // ebx: JSObject
130 // edi: start of next object
131 __ mov(Operand(ebx, JSObject::kMapOffset), eax);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000132 __ mov(ecx, Factory::empty_fixed_array());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000133 __ mov(Operand(ebx, JSObject::kPropertiesOffset), ecx);
134 __ mov(Operand(ebx, JSObject::kElementsOffset), ecx);
135 // Set extra fields in the newly allocated object.
136 // eax: initial map
137 // ebx: JSObject
138 // edi: start of next object
139 { Label loop, entry;
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000140 __ mov(edx, Factory::undefined_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000141 __ lea(ecx, Operand(ebx, JSObject::kHeaderSize));
142 __ jmp(&entry);
143 __ bind(&loop);
144 __ mov(Operand(ecx, 0), edx);
145 __ add(Operand(ecx), Immediate(kPointerSize));
146 __ bind(&entry);
147 __ cmp(ecx, Operand(edi));
148 __ j(less, &loop);
149 }
150
151 // Mostly done with the JSObject. Add the heap tag and store the new top, so
152 // that we can continue and jump into the continuation code at any time from
153 // now on. Any failures need to undo the setting of the new top, so that the
154 // heap is in a consistent state and verifiable.
155 // eax: initial map
156 // ebx: JSObject
157 // edi: start of next object
158 __ or_(Operand(ebx), Immediate(kHeapObjectTag));
159 __ mov(Operand::StaticVariable(new_space_allocation_top), edi);
160
161 // Check if a properties array should be setup and allocate one if needed.
162 // Otherwise initialize the properties to the empty_fixed_array as well.
163 // eax: initial map
164 // ebx: JSObject
165 // edi: start of next object
166 __ movzx_b(edx, FieldOperand(eax, Map::kUnusedPropertyFieldsOffset));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000167 __ movzx_b(ecx, FieldOperand(eax, Map::kInObjectPropertiesOffset));
168 // Calculate unused properties past the end of the in-object properties.
169 __ sub(edx, Operand(ecx));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000170 __ test(edx, Operand(edx));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000171 // Done if no extra properties are to be allocated.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000172 __ j(zero, &allocated);
173
174 // Scale the number of elements by pointer size and add the header for
175 // FixedArrays to the start of the next object calculation from above.
176 // eax: initial map
177 // ebx: JSObject
178 // edi: start of next object (will be start of FixedArray)
179 // edx: number of elements in properties array
180 ASSERT(Heap::MaxHeapObjectSize() >
181 (FixedArray::kHeaderSize + 255*kPointerSize));
182 __ lea(ecx, Operand(edi, edx, times_4, FixedArray::kHeaderSize));
183 __ cmp(ecx, Operand::StaticVariable(new_space_allocation_limit));
184 __ j(greater_equal, &undo_allocation);
185 __ mov(Operand::StaticVariable(new_space_allocation_top), ecx);
186
187 // Initialize the FixedArray.
188 // ebx: JSObject
189 // edi: FixedArray
190 // edx: number of elements
191 // ecx: start of next object
192 __ mov(eax, Factory::fixed_array_map());
193 __ mov(Operand(edi, JSObject::kMapOffset), eax); // setup the map
194 __ mov(Operand(edi, Array::kLengthOffset), edx); // and length
195
196 // Initialize the fields to undefined.
197 // ebx: JSObject
198 // edi: FixedArray
199 // ecx: start of next object
200 { Label loop, entry;
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000201 __ mov(edx, Factory::undefined_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000202 __ lea(eax, Operand(edi, FixedArray::kHeaderSize));
203 __ jmp(&entry);
204 __ bind(&loop);
205 __ mov(Operand(eax, 0), edx);
206 __ add(Operand(eax), Immediate(kPointerSize));
207 __ bind(&entry);
208 __ cmp(eax, Operand(ecx));
209 __ j(less, &loop);
210 }
211
212 // Store the initialized FixedArray into the properties field of
213 // the JSObject
214 // ebx: JSObject
215 // edi: FixedArray
216 __ or_(Operand(edi), Immediate(kHeapObjectTag)); // add the heap tag
217 __ mov(FieldOperand(ebx, JSObject::kPropertiesOffset), edi);
218
219
220 // Continue with JSObject being successfully allocated
221 // ebx: JSObject
222 __ jmp(&allocated);
223
224 // Undo the setting of the new top so that the heap is verifiable. For
225 // example, the map's unused properties potentially do not match the
226 // allocated objects unused properties.
227 // ebx: JSObject (previous new top)
228 __ bind(&undo_allocation);
229 __ xor_(Operand(ebx), Immediate(kHeapObjectTag)); // clear the heap tag
230 __ mov(Operand::StaticVariable(new_space_allocation_top), ebx);
231 }
232
233 // Allocate the new receiver object using the runtime call.
234 // edi: function (constructor)
235 __ bind(&rt_call);
236 // Must restore edi (constructor) before calling runtime.
237 __ mov(edi, Operand(esp, 0));
238 __ push(edi);
239 __ CallRuntime(Runtime::kNewObject, 1);
240 __ mov(ebx, Operand(eax)); // store result in ebx
241
242 // New object allocated.
243 // ebx: newly allocated object
244 __ bind(&allocated);
245 // Retrieve the function from the stack.
246 __ pop(edi);
247
248 // Retrieve smi-tagged arguments count from the stack.
249 __ mov(eax, Operand(esp, 0));
250 __ shr(eax, kSmiTagSize);
251
252 // Push the allocated receiver to the stack. We need two copies
253 // because we may have to return the original one and the calling
254 // conventions dictate that the called function pops the receiver.
255 __ push(ebx);
256 __ push(ebx);
257
258 // Setup pointer to last argument.
259 __ lea(ebx, Operand(ebp, StandardFrameConstants::kCallerSPOffset));
260
261 // Copy arguments and receiver to the expression stack.
262 Label loop, entry;
263 __ mov(ecx, Operand(eax));
264 __ jmp(&entry);
265 __ bind(&loop);
266 __ push(Operand(ebx, ecx, times_4, 0));
267 __ bind(&entry);
268 __ dec(ecx);
269 __ j(greater_equal, &loop);
270
271 // Call the function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000272 ParameterCount actual(eax);
273 __ InvokeFunction(edi, actual, CALL_FUNCTION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000274
275 // Restore context from the frame.
276 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
277
278 // If the result is an object (in the ECMA sense), we should get rid
279 // of the receiver and use the result; see ECMA-262 section 13.2.2-7
280 // on page 74.
281 Label use_receiver, exit;
282
283 // If the result is a smi, it is *not* an object in the ECMA sense.
284 __ test(eax, Immediate(kSmiTagMask));
285 __ j(zero, &use_receiver, not_taken);
286
287 // If the type of the result (stored in its map) is less than
ager@chromium.orgc27e4e72008-09-04 13:52:27 +0000288 // FIRST_JS_OBJECT_TYPE, it is not an object in the ECMA sense.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000289 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
290 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
ager@chromium.orgc27e4e72008-09-04 13:52:27 +0000291 __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000292 __ j(greater_equal, &exit, not_taken);
293
294 // Throw away the result of the constructor invocation and use the
295 // on-stack receiver as the result.
296 __ bind(&use_receiver);
297 __ mov(eax, Operand(esp, 0));
298
ager@chromium.org7c537e22008-10-16 08:43:32 +0000299 // Restore the arguments count and leave the construct frame.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000300 __ bind(&exit);
301 __ mov(ebx, Operand(esp, kPointerSize)); // get arguments count
ager@chromium.org7c537e22008-10-16 08:43:32 +0000302 __ LeaveConstructFrame();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000303
304 // Remove caller arguments from the stack and return.
305 ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
306 __ pop(ecx);
307 __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver
308 __ push(ecx);
309 __ ret(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000310}
311
312
313static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
314 bool is_construct) {
315 // Clear the context before we push it when entering the JS frame.
316 __ xor_(esi, Operand(esi)); // clear esi
317
318 // Enter an internal frame.
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000319 __ EnterInternalFrame();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000320
321 // Load the previous frame pointer (ebx) to access C arguments
322 __ mov(ebx, Operand(ebp, 0));
323
324 // Get the function from the frame and setup the context.
325 __ mov(ecx, Operand(ebx, EntryFrameConstants::kFunctionArgOffset));
326 __ mov(esi, FieldOperand(ecx, JSFunction::kContextOffset));
327
328 // Push the function and the receiver onto the stack.
329 __ push(ecx);
330 __ push(Operand(ebx, EntryFrameConstants::kReceiverArgOffset));
331
332 // Load the number of arguments and setup pointer to the arguments.
333 __ mov(eax, Operand(ebx, EntryFrameConstants::kArgcOffset));
334 __ mov(ebx, Operand(ebx, EntryFrameConstants::kArgvOffset));
335
336 // Copy arguments to the stack in a loop.
337 Label loop, entry;
338 __ xor_(ecx, Operand(ecx)); // clear ecx
339 __ jmp(&entry);
340 __ bind(&loop);
341 __ mov(edx, Operand(ebx, ecx, times_4, 0)); // push parameter from argv
342 __ push(Operand(edx, 0)); // dereference handle
343 __ inc(Operand(ecx));
344 __ bind(&entry);
345 __ cmp(ecx, Operand(eax));
346 __ j(not_equal, &loop);
347
348 // Get the function from the stack and call it.
349 __ mov(edi, Operand(esp, eax, times_4, +1 * kPointerSize)); // +1 ~ receiver
350
351 // Invoke the code.
352 if (is_construct) {
353 __ call(Handle<Code>(Builtins::builtin(Builtins::JSConstructCall)),
ager@chromium.org236ad962008-09-25 09:45:57 +0000354 RelocInfo::CODE_TARGET);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000355 } else {
356 ParameterCount actual(eax);
357 __ InvokeFunction(edi, actual, CALL_FUNCTION);
358 }
359
360 // Exit the JS frame. Notice that this also removes the empty
361 // context and the function left on the stack by the code
362 // invocation.
ager@chromium.org236ad962008-09-25 09:45:57 +0000363 __ LeaveInternalFrame();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000364 __ ret(1 * kPointerSize); // remove receiver
365}
366
367
368void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) {
369 Generate_JSEntryTrampolineHelper(masm, false);
370}
371
372
373void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
374 Generate_JSEntryTrampolineHelper(masm, true);
375}
376
377
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000378void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
379 // 1. Make sure we have at least one argument.
380 { Label done;
381 __ test(eax, Operand(eax));
382 __ j(not_zero, &done, taken);
383 __ pop(ebx);
384 __ push(Immediate(Factory::undefined_value()));
385 __ push(ebx);
386 __ inc(eax);
387 __ bind(&done);
388 }
389
390 // 2. Get the function to call from the stack.
391 { Label done, non_function, function;
392 // +1 ~ return address.
393 __ mov(edi, Operand(esp, eax, times_4, +1 * kPointerSize));
394 __ test(edi, Immediate(kSmiTagMask));
395 __ j(zero, &non_function, not_taken);
396 __ mov(ecx, FieldOperand(edi, HeapObject::kMapOffset)); // get the map
397 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
398 __ cmp(ecx, JS_FUNCTION_TYPE);
399 __ j(equal, &function, taken);
400
401 // Non-function called: Clear the function to force exception.
402 __ bind(&non_function);
403 __ xor_(edi, Operand(edi));
404 __ jmp(&done);
405
406 // Function called: Change context eagerly to get the right global object.
407 __ bind(&function);
408 __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
409
410 __ bind(&done);
411 }
412
413 // 3. Make sure first argument is an object; convert if necessary.
414 { Label call_to_object, use_global_receiver, patch_receiver, done;
415 __ mov(ebx, Operand(esp, eax, times_4, 0));
416
417 __ test(ebx, Immediate(kSmiTagMask));
418 __ j(zero, &call_to_object);
419
420 __ cmp(ebx, Factory::null_value());
421 __ j(equal, &use_global_receiver);
422 __ cmp(ebx, Factory::undefined_value());
423 __ j(equal, &use_global_receiver);
424
425 __ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset));
426 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
427 __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
428 __ j(less, &call_to_object);
429 __ cmp(ecx, LAST_JS_OBJECT_TYPE);
430 __ j(less_equal, &done);
431
432 __ bind(&call_to_object);
433 __ EnterInternalFrame(); // preserves eax, ebx, edi
434
435 // Store the arguments count on the stack (smi tagged).
436 ASSERT(kSmiTag == 0);
437 __ shl(eax, kSmiTagSize);
438 __ push(eax);
439
440 __ push(edi); // save edi across the call
441 __ push(ebx);
442 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000443 __ mov(ebx, eax);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000444 __ pop(edi); // restore edi after the call
445
446 // Get the arguments count and untag it.
447 __ pop(eax);
448 __ shr(eax, kSmiTagSize);
449
ager@chromium.org236ad962008-09-25 09:45:57 +0000450 __ LeaveInternalFrame();
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000451 __ jmp(&patch_receiver);
452
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000453 // Use the global receiver object from the called function as the receiver.
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000454 __ bind(&use_global_receiver);
455 const int kGlobalIndex =
456 Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
457 __ mov(ebx, FieldOperand(esi, kGlobalIndex));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000458 __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000459
460 __ bind(&patch_receiver);
461 __ mov(Operand(esp, eax, times_4, 0), ebx);
462
463 __ bind(&done);
464 }
465
466 // 4. Shift stuff one slot down the stack.
467 { Label loop;
468 __ lea(ecx, Operand(eax, +1)); // +1 ~ copy receiver too
469 __ bind(&loop);
470 __ mov(ebx, Operand(esp, ecx, times_4, 0));
471 __ mov(Operand(esp, ecx, times_4, kPointerSize), ebx);
472 __ dec(ecx);
473 __ j(not_zero, &loop);
474 }
475
476 // 5. Remove TOS (copy of last arguments), but keep return address.
477 __ pop(ebx);
478 __ pop(ecx);
479 __ push(ebx);
480 __ dec(eax);
481
482 // 6. Check that function really was a function and get the code to
483 // call from the function and check that the number of expected
484 // arguments matches what we're providing.
485 { Label invoke;
486 __ test(edi, Operand(edi));
487 __ j(not_zero, &invoke, taken);
488 __ xor_(ebx, Operand(ebx));
489 __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
ager@chromium.org236ad962008-09-25 09:45:57 +0000490 __ jmp(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
491 RelocInfo::CODE_TARGET);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000492
493 __ bind(&invoke);
494 __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
495 __ mov(ebx,
496 FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
497 __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
498 __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
499 __ cmp(eax, Operand(ebx));
500 __ j(not_equal, Handle<Code>(builtin(ArgumentsAdaptorTrampoline)));
501 }
502
503 // 7. Jump (tail-call) to the code in register edx without checking arguments.
504 ParameterCount expected(0);
505 __ InvokeCode(Operand(edx), expected, expected, JUMP_FUNCTION);
506}
507
508
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000509void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000510 __ EnterInternalFrame();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000511
512 __ push(Operand(ebp, 4 * kPointerSize)); // push this
513 __ push(Operand(ebp, 2 * kPointerSize)); // push arguments
514 __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
515
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000516 if (FLAG_check_stack) {
517 // We need to catch preemptions right here, otherwise an unlucky preemption
518 // could show up as a failed apply.
519 ExternalReference stack_guard_limit =
520 ExternalReference::address_of_stack_guard_limit();
521 Label retry_preemption;
522 Label no_preemption;
523 __ bind(&retry_preemption);
524 __ mov(edi, Operand::StaticVariable(stack_guard_limit));
525 __ cmp(esp, Operand(edi));
526 __ j(above, &no_preemption, taken);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000527
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000528 // Preemption!
529 // Because builtins always remove the receiver from the stack, we
530 // have to fake one to avoid underflowing the stack.
531 __ push(eax);
532 __ push(Immediate(Smi::FromInt(0)));
533
534 // Do call to runtime routine.
535 __ CallRuntime(Runtime::kStackGuard, 1);
536 __ pop(eax);
537 __ jmp(&retry_preemption);
538
539 __ bind(&no_preemption);
540
541 Label okay;
542 // Make ecx the space we have left.
543 __ mov(ecx, Operand(esp));
544 __ sub(ecx, Operand(edi));
545 // Make edx the space we need for the array when it is unrolled onto the
546 // stack.
547 __ mov(edx, Operand(eax));
548 __ shl(edx, kPointerSizeLog2 - kSmiTagSize);
549 __ cmp(ecx, Operand(edx));
550 __ j(greater, &okay, taken);
551
552 // Too bad: Out of stack space.
553 __ push(Operand(ebp, 4 * kPointerSize)); // push this
554 __ push(eax);
555 __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION);
556 __ bind(&okay);
557 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000558
559 // Push current index and limit.
560 const int kLimitOffset =
561 StandardFrameConstants::kExpressionsOffset - 1 * kPointerSize;
562 const int kIndexOffset = kLimitOffset - 1 * kPointerSize;
563 __ push(eax); // limit
564 __ push(Immediate(0)); // index
565
566 // Change context eagerly to get the right global object if
567 // necessary.
568 __ mov(edi, Operand(ebp, 4 * kPointerSize));
569 __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
570
571 // Compute the receiver.
572 Label call_to_object, use_global_receiver, push_receiver;
573 __ mov(ebx, Operand(ebp, 3 * kPointerSize));
574 __ test(ebx, Immediate(kSmiTagMask));
575 __ j(zero, &call_to_object);
576 __ cmp(ebx, Factory::null_value());
577 __ j(equal, &use_global_receiver);
578 __ cmp(ebx, Factory::undefined_value());
579 __ j(equal, &use_global_receiver);
580
581 // If given receiver is already a JavaScript object then there's no
582 // reason for converting it.
583 __ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset));
584 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
585 __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
586 __ j(less, &call_to_object);
587 __ cmp(ecx, LAST_JS_OBJECT_TYPE);
588 __ j(less_equal, &push_receiver);
589
590 // Convert the receiver to an object.
591 __ bind(&call_to_object);
592 __ push(ebx);
593 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
594 __ mov(ebx, Operand(eax));
595 __ jmp(&push_receiver);
596
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000597 // Use the current global receiver object as the receiver.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000598 __ bind(&use_global_receiver);
599 const int kGlobalOffset =
600 Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
601 __ mov(ebx, FieldOperand(esi, kGlobalOffset));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000602 __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000603
604 // Push the receiver.
605 __ bind(&push_receiver);
606 __ push(ebx);
607
608 // Copy all arguments from the array to the stack.
609 Label entry, loop;
610 __ mov(eax, Operand(ebp, kIndexOffset));
611 __ jmp(&entry);
612 __ bind(&loop);
613 __ mov(ecx, Operand(ebp, 2 * kPointerSize)); // load arguments
614 __ push(ecx);
615 __ push(eax);
616
617 // Use inline caching to speed up access to arguments.
618 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
ager@chromium.org236ad962008-09-25 09:45:57 +0000619 __ call(ic, RelocInfo::CODE_TARGET);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000620
621 // Remove IC arguments from the stack and push the nth argument.
622 __ add(Operand(esp), Immediate(2 * kPointerSize));
623 __ push(eax);
624
625 // Update the index on the stack and in register eax.
626 __ mov(eax, Operand(ebp, kIndexOffset));
627 __ add(Operand(eax), Immediate(1 << kSmiTagSize));
628 __ mov(Operand(ebp, kIndexOffset), eax);
629
630 __ bind(&entry);
631 __ cmp(eax, Operand(ebp, kLimitOffset));
632 __ j(not_equal, &loop);
633
634 // Invoke the function.
635 ParameterCount actual(eax);
636 __ shr(eax, kSmiTagSize);
637 __ mov(edi, Operand(ebp, 4 * kPointerSize));
638 __ InvokeFunction(edi, actual, CALL_FUNCTION);
639
ager@chromium.org236ad962008-09-25 09:45:57 +0000640 __ LeaveInternalFrame();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000641 __ ret(3 * kPointerSize); // remove this, receiver, and arguments
642}
643
644
645static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
646 __ push(ebp);
647 __ mov(ebp, Operand(esp));
648
649 // Store the arguments adaptor context sentinel.
650 __ push(Immediate(ArgumentsAdaptorFrame::SENTINEL));
651
652 // Push the function on the stack.
653 __ push(edi);
654
655 // Preserve the number of arguments on the stack. Must preserve both
656 // eax and ebx because these registers are used when copying the
657 // arguments and the receiver.
658 ASSERT(kSmiTagSize == 1);
659 __ lea(ecx, Operand(eax, eax, times_1, kSmiTag));
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000660 __ push(ecx);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000661}
662
663
ager@chromium.org7c537e22008-10-16 08:43:32 +0000664static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000665 // Retrieve the number of arguments from the stack.
666 __ mov(ebx, Operand(ebp, ArgumentsAdaptorFrameConstants::kLengthOffset));
667
668 // Leave the frame.
669 __ leave();
670
671 // Remove caller arguments from the stack.
672 ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
673 __ pop(ecx);
674 __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver
675 __ push(ecx);
676}
677
678
679void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
680 // ----------- S t a t e -------------
681 // -- eax : actual number of arguments
682 // -- ebx : expected number of arguments
683 // -- edx : code entry to call
684 // -----------------------------------
685
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000686 Label invoke, dont_adapt_arguments;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000687 __ IncrementCounter(&Counters::arguments_adaptors, 1);
688
689 Label enough, too_few;
690 __ cmp(eax, Operand(ebx));
691 __ j(less, &too_few);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000692 __ cmp(ebx, SharedFunctionInfo::kDontAdaptArgumentsSentinel);
693 __ j(equal, &dont_adapt_arguments);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000694
695 { // Enough parameters: Actual >= expected.
696 __ bind(&enough);
697 EnterArgumentsAdaptorFrame(masm);
698
699 // Copy receiver and all expected arguments.
700 const int offset = StandardFrameConstants::kCallerSPOffset;
701 __ lea(eax, Operand(ebp, eax, times_4, offset));
702 __ mov(ecx, -1); // account for receiver
703
704 Label copy;
705 __ bind(&copy);
706 __ inc(ecx);
707 __ push(Operand(eax, 0));
708 __ sub(Operand(eax), Immediate(kPointerSize));
709 __ cmp(ecx, Operand(ebx));
710 __ j(less, &copy);
711 __ jmp(&invoke);
712 }
713
714 { // Too few parameters: Actual < expected.
715 __ bind(&too_few);
716 EnterArgumentsAdaptorFrame(masm);
717
718 // Copy receiver and all actual arguments.
719 const int offset = StandardFrameConstants::kCallerSPOffset;
720 __ lea(edi, Operand(ebp, eax, times_4, offset));
721 __ mov(ecx, -1); // account for receiver
722
723 Label copy;
724 __ bind(&copy);
725 __ inc(ecx);
726 __ push(Operand(edi, 0));
727 __ sub(Operand(edi), Immediate(kPointerSize));
728 __ cmp(ecx, Operand(eax));
729 __ j(less, &copy);
730
731 // Fill remaining expected arguments with undefined values.
732 Label fill;
733 __ bind(&fill);
734 __ inc(ecx);
735 __ push(Immediate(Factory::undefined_value()));
736 __ cmp(ecx, Operand(ebx));
737 __ j(less, &fill);
738
739 // Restore function pointer.
740 __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
741 }
742
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000743 // Call the entry point.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000744 __ bind(&invoke);
745 __ call(Operand(edx));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000746
ager@chromium.org7c537e22008-10-16 08:43:32 +0000747 // Leave frame and return.
748 LeaveArgumentsAdaptorFrame(masm);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000749 __ ret(0);
750
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000751 // -------------------------------------------
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000752 // Dont adapt arguments.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000753 // -------------------------------------------
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000754 __ bind(&dont_adapt_arguments);
755 __ jmp(Operand(edx));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000756}
757
758
759static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
760 RegList pointer_regs,
761 bool convert_call_to_jmp) {
762 // Save the content of all general purpose registers in memory. This copy in
763 // memory is later pushed onto the JS expression stack for the fake JS frame
764 // generated and also to the C frame generated on top of that. In the JS
765 // frame ONLY the registers containing pointers will be pushed on the
766 // expression stack. This causes the GC to update these pointers so that
767 // they will have the correct value when returning from the debugger.
768 __ SaveRegistersToMemory(kJSCallerSaved);
769
770 // Enter an internal frame.
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000771 __ EnterInternalFrame();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000772
773 // Store the registers containing object pointers on the expression stack to
774 // make sure that these are correctly updated during GC.
775 __ PushRegistersFromMemory(pointer_regs);
776
777#ifdef DEBUG
778 __ RecordComment("// Calling from debug break to runtime - come in - over");
779#endif
780 __ Set(eax, Immediate(0)); // no arguments
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000781 __ mov(ebx, Immediate(ExternalReference::debug_break()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000782
783 CEntryDebugBreakStub ceb;
784 __ CallStub(&ceb);
785
786 // Restore the register values containing object pointers from the expression
787 // stack in the reverse order as they where pushed.
788 __ PopRegistersToMemory(pointer_regs);
789
790 // Get rid of the internal frame.
ager@chromium.org236ad962008-09-25 09:45:57 +0000791 __ LeaveInternalFrame();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000792
793 // If this call did not replace a call but patched other code then there will
794 // be an unwanted return address left on the stack. Here we get rid of that.
795 if (convert_call_to_jmp) {
796 __ pop(eax);
797 }
798
799 // Finally restore all registers.
800 __ RestoreRegistersFromMemory(kJSCallerSaved);
801
802 // Now that the break point has been handled, resume normal execution by
803 // jumping to the target address intended by the caller and that was
804 // overwritten by the address of DebugBreakXXX.
805 ExternalReference after_break_target =
806 ExternalReference(Debug_Address::AfterBreakTarget());
807 __ jmp(Operand::StaticVariable(after_break_target));
808}
809
810
811void Builtins::Generate_LoadIC_DebugBreak(MacroAssembler* masm) {
812 // Register state for IC load call (from ic-ia32.cc).
813 // ----------- S t a t e -------------
814 // -- ecx : name
815 // -----------------------------------
816 Generate_DebugBreakCallHelper(masm, ecx.bit(), false);
817}
818
819
820void Builtins::Generate_StoreIC_DebugBreak(MacroAssembler* masm) {
821 // REgister state for IC store call (from ic-ia32.cc).
822 // ----------- S t a t e -------------
823 // -- eax : value
824 // -- ecx : name
825 // -----------------------------------
826 Generate_DebugBreakCallHelper(masm, eax.bit() | ecx.bit(), false);
827}
828
829
830void Builtins::Generate_KeyedLoadIC_DebugBreak(MacroAssembler* masm) {
831 // Register state for keyed IC load call (from ic-ia32.cc).
832 // ----------- S t a t e -------------
833 // No registers used on entry.
834 // -----------------------------------
835 Generate_DebugBreakCallHelper(masm, 0, false);
836}
837
838
839void Builtins::Generate_KeyedStoreIC_DebugBreak(MacroAssembler* masm) {
840 // Register state for keyed IC load call (from ic-ia32.cc).
841 // ----------- S t a t e -------------
842 // -- eax : value
843 // -----------------------------------
844 // Register eax contains an object that needs to be pushed on the
845 // expression stack of the fake JS frame.
846 Generate_DebugBreakCallHelper(masm, eax.bit(), false);
847}
848
849
850void Builtins::Generate_CallIC_DebugBreak(MacroAssembler* masm) {
851 // Register state for keyed IC call call (from ic-ia32.cc)
852 // ----------- S t a t e -------------
853 // -- eax: number of arguments
854 // -----------------------------------
855 // The number of arguments in eax is not smi encoded.
856 Generate_DebugBreakCallHelper(masm, 0, false);
857}
858
859
860void Builtins::Generate_ConstructCall_DebugBreak(MacroAssembler* masm) {
v8.team.kasperl727e9952008-09-02 14:56:44 +0000861 // Register state just before return from JS function (from codegen-ia32.cc).
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000862 // eax is the actual number of arguments not encoded as a smi see comment
863 // above IC call.
864 // ----------- S t a t e -------------
865 // -- eax: number of arguments
866 // -----------------------------------
867 // The number of arguments in eax is not smi encoded.
868 Generate_DebugBreakCallHelper(masm, 0, false);
869}
870
871
872void Builtins::Generate_Return_DebugBreak(MacroAssembler* masm) {
873 // Register state just before return from JS function (from codegen-ia32.cc).
874 // ----------- S t a t e -------------
875 // -- eax: return value
876 // -----------------------------------
877 Generate_DebugBreakCallHelper(masm, eax.bit(), true);
878}
879
880
881void Builtins::Generate_Return_DebugBreakEntry(MacroAssembler* masm) {
882 // OK to clobber ebx as we are returning from a JS function in the code
883 // generated by Ia32CodeGenerator::ExitJSFrame.
884 ExternalReference debug_break_return =
885 ExternalReference(Debug_Address::DebugBreakReturn());
886 __ mov(ebx, Operand::StaticVariable(debug_break_return));
887 __ add(Operand(ebx), Immediate(Code::kHeaderSize - kHeapObjectTag));
888 __ jmp(Operand(ebx));
889}
890
891
892void Builtins::Generate_StubNoRegisters_DebugBreak(MacroAssembler* masm) {
893 // Register state for stub CallFunction (from CallFunctionStub in ic-ia32.cc).
894 // ----------- S t a t e -------------
895 // No registers used on entry.
896 // -----------------------------------
897 Generate_DebugBreakCallHelper(masm, 0, false);
898}
899
900#undef __
901
902} } // namespace v8::internal