blob: f8a85de98c8f8c1f7ea7fab3fd68081ff248fb6c [file] [log] [blame]
Ben Murdoch8b112d22011-06-08 16:22:53 +01001// Copyright 2011 the V8 project authors. All rights reserved.
Steve Blocka7e24c12009-10-30 11:49:00 +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
Leon Clarkef7060e22010-06-03 12:02:55 +010030#if defined(V8_TARGET_ARCH_IA32)
31
Ben Murdoch8b112d22011-06-08 16:22:53 +010032#include "codegen.h"
Ben Murdochb0fe1622011-05-05 13:52:32 +010033#include "deoptimizer.h"
34#include "full-codegen.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000035
36namespace v8 {
37namespace internal {
38
39
40#define __ ACCESS_MASM(masm)
41
42
Leon Clarkee46be812010-01-19 14:06:41 +000043void Builtins::Generate_Adaptor(MacroAssembler* masm,
44 CFunctionId id,
45 BuiltinExtraArguments extra_args) {
46 // ----------- S t a t e -------------
47 // -- eax : number of arguments excluding receiver
48 // -- edi : called function (only guaranteed when
49 // extra_args requires it)
50 // -- esi : context
51 // -- esp[0] : return address
52 // -- esp[4] : last argument
53 // -- ...
54 // -- esp[4 * argc] : first argument (argc == eax)
55 // -- esp[4 * (argc +1)] : receiver
56 // -----------------------------------
Steve Blocka7e24c12009-10-30 11:49:00 +000057
Leon Clarkee46be812010-01-19 14:06:41 +000058 // Insert extra arguments.
59 int num_extra_args = 0;
60 if (extra_args == NEEDS_CALLED_FUNCTION) {
61 num_extra_args = 1;
62 Register scratch = ebx;
63 __ pop(scratch); // Save return address.
64 __ push(edi);
65 __ push(scratch); // Restore return address.
66 } else {
67 ASSERT(extra_args == NO_EXTRA_ARGUMENTS);
68 }
69
Steve Block6ded16b2010-05-10 14:33:55 +010070 // JumpToExternalReference expects eax to contain the number of arguments
Leon Clarkee46be812010-01-19 14:06:41 +000071 // including the receiver and the extra arguments.
72 __ add(Operand(eax), Immediate(num_extra_args + 1));
Steve Block44f0eee2011-05-26 01:26:41 +010073 __ JumpToExternalReference(ExternalReference(id, masm->isolate()));
Steve Blocka7e24c12009-10-30 11:49:00 +000074}
75
76
77void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
78 // ----------- S t a t e -------------
79 // -- eax: number of arguments
80 // -- edi: constructor function
81 // -----------------------------------
82
83 Label non_function_call;
84 // Check that function is not a smi.
Ben Murdoch3fb3ca82011-12-02 17:19:32 +000085 __ JumpIfSmi(edi, &non_function_call);
Steve Blocka7e24c12009-10-30 11:49:00 +000086 // Check that function is a JSFunction.
87 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
88 __ j(not_equal, &non_function_call);
89
90 // Jump to the function-specific construct stub.
91 __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
92 __ mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kConstructStubOffset));
93 __ lea(ebx, FieldOperand(ebx, Code::kHeaderSize));
94 __ jmp(Operand(ebx));
95
96 // edi: called object
97 // eax: number of arguments
98 __ bind(&non_function_call);
Steve Blocka7e24c12009-10-30 11:49:00 +000099 // Set expected number of arguments to zero (not changing eax).
100 __ Set(ebx, Immediate(0));
101 __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
Steve Block44f0eee2011-05-26 01:26:41 +0100102 Handle<Code> arguments_adaptor =
103 masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
Ben Murdoch257744e2011-11-30 15:57:28 +0000104 __ SetCallKind(ecx, CALL_AS_METHOD);
Steve Block44f0eee2011-05-26 01:26:41 +0100105 __ jmp(arguments_adaptor, RelocInfo::CODE_TARGET);
Steve Blocka7e24c12009-10-30 11:49:00 +0000106}
107
108
Leon Clarkee46be812010-01-19 14:06:41 +0000109static void Generate_JSConstructStubHelper(MacroAssembler* masm,
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100110 bool is_api_function,
111 bool count_constructions) {
112 // Should never count constructions for api objects.
113 ASSERT(!is_api_function || !count_constructions);
114
Steve Blocka7e24c12009-10-30 11:49:00 +0000115 // Enter a construct frame.
116 __ EnterConstructFrame();
117
118 // Store a smi-tagged arguments count on the stack.
Leon Clarkee46be812010-01-19 14:06:41 +0000119 __ SmiTag(eax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000120 __ push(eax);
121
122 // Push the function to invoke on the stack.
123 __ push(edi);
124
125 // Try to allocate the object without transitioning into C code. If any of the
126 // preconditions is not met, the code bails out to the runtime call.
127 Label rt_call, allocated;
128 if (FLAG_inline_new) {
129 Label undo_allocation;
130#ifdef ENABLE_DEBUGGER_SUPPORT
131 ExternalReference debug_step_in_fp =
Steve Block44f0eee2011-05-26 01:26:41 +0100132 ExternalReference::debug_step_in_fp_address(masm->isolate());
Steve Blocka7e24c12009-10-30 11:49:00 +0000133 __ cmp(Operand::StaticVariable(debug_step_in_fp), Immediate(0));
134 __ j(not_equal, &rt_call);
135#endif
136
137 // Verified that the constructor is a JSFunction.
138 // Load the initial map and verify that it is in fact a map.
139 // edi: constructor
140 __ mov(eax, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
141 // Will both indicate a NULL and a Smi
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000142 __ JumpIfSmi(eax, &rt_call);
Steve Blocka7e24c12009-10-30 11:49:00 +0000143 // edi: constructor
144 // eax: initial map (if proven valid below)
145 __ CmpObjectType(eax, MAP_TYPE, ebx);
146 __ j(not_equal, &rt_call);
147
148 // Check that the constructor is not constructing a JSFunction (see comments
149 // in Runtime_NewObject in runtime.cc). In which case the initial map's
150 // instance type would be JS_FUNCTION_TYPE.
151 // edi: constructor
152 // eax: initial map
153 __ CmpInstanceType(eax, JS_FUNCTION_TYPE);
154 __ j(equal, &rt_call);
155
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100156 if (count_constructions) {
157 Label allocate;
158 // Decrease generous allocation count.
159 __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
160 __ dec_b(FieldOperand(ecx, SharedFunctionInfo::kConstructionCountOffset));
161 __ j(not_zero, &allocate);
162
163 __ push(eax);
164 __ push(edi);
165
166 __ push(edi); // constructor
167 // The call will replace the stub, so the countdown is only done once.
168 __ CallRuntime(Runtime::kFinalizeInstanceSize, 1);
169
170 __ pop(edi);
171 __ pop(eax);
172
173 __ bind(&allocate);
174 }
175
Steve Blocka7e24c12009-10-30 11:49:00 +0000176 // Now allocate the JSObject on the heap.
177 // edi: constructor
178 // eax: initial map
179 __ movzx_b(edi, FieldOperand(eax, Map::kInstanceSizeOffset));
180 __ shl(edi, kPointerSizeLog2);
181 __ AllocateInNewSpace(edi, ebx, edi, no_reg, &rt_call, NO_ALLOCATION_FLAGS);
182 // Allocated the JSObject, now initialize the fields.
183 // eax: initial map
184 // ebx: JSObject
185 // edi: start of next object
186 __ mov(Operand(ebx, JSObject::kMapOffset), eax);
Steve Block44f0eee2011-05-26 01:26:41 +0100187 Factory* factory = masm->isolate()->factory();
188 __ mov(ecx, factory->empty_fixed_array());
Steve Blocka7e24c12009-10-30 11:49:00 +0000189 __ mov(Operand(ebx, JSObject::kPropertiesOffset), ecx);
190 __ mov(Operand(ebx, JSObject::kElementsOffset), ecx);
191 // Set extra fields in the newly allocated object.
192 // eax: initial map
193 // ebx: JSObject
194 // edi: start of next object
195 { Label loop, entry;
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100196 // To allow for truncation.
197 if (count_constructions) {
Steve Block44f0eee2011-05-26 01:26:41 +0100198 __ mov(edx, factory->one_pointer_filler_map());
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100199 } else {
Steve Block44f0eee2011-05-26 01:26:41 +0100200 __ mov(edx, factory->undefined_value());
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100201 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000202 __ lea(ecx, Operand(ebx, JSObject::kHeaderSize));
203 __ jmp(&entry);
204 __ bind(&loop);
205 __ mov(Operand(ecx, 0), edx);
206 __ add(Operand(ecx), Immediate(kPointerSize));
207 __ bind(&entry);
208 __ cmp(ecx, Operand(edi));
209 __ j(less, &loop);
210 }
211
212 // Add the object tag to make the JSObject real, so that we can continue and
213 // jump into the continuation code at any time from now on. Any failures
214 // need to undo the allocation, so that the heap is in a consistent state
215 // and verifiable.
216 // eax: initial map
217 // ebx: JSObject
218 // edi: start of next object
219 __ or_(Operand(ebx), Immediate(kHeapObjectTag));
220
221 // Check if a non-empty properties array is needed.
222 // Allocate and initialize a FixedArray if it is.
223 // eax: initial map
224 // ebx: JSObject
225 // edi: start of next object
226 // Calculate the total number of properties described by the map.
227 __ movzx_b(edx, FieldOperand(eax, Map::kUnusedPropertyFieldsOffset));
228 __ movzx_b(ecx, FieldOperand(eax, Map::kPreAllocatedPropertyFieldsOffset));
229 __ add(edx, Operand(ecx));
230 // Calculate unused properties past the end of the in-object properties.
231 __ movzx_b(ecx, FieldOperand(eax, Map::kInObjectPropertiesOffset));
232 __ sub(edx, Operand(ecx));
233 // Done if no extra properties are to be allocated.
234 __ j(zero, &allocated);
235 __ Assert(positive, "Property allocation count failed.");
236
237 // Scale the number of elements by pointer size and add the header for
238 // FixedArrays to the start of the next object calculation from above.
239 // ebx: JSObject
240 // edi: start of next object (will be start of FixedArray)
241 // edx: number of elements in properties array
242 __ AllocateInNewSpace(FixedArray::kHeaderSize,
243 times_pointer_size,
244 edx,
245 edi,
246 ecx,
247 no_reg,
248 &undo_allocation,
249 RESULT_CONTAINS_TOP);
250
251 // Initialize the FixedArray.
252 // ebx: JSObject
253 // edi: FixedArray
254 // edx: number of elements
255 // ecx: start of next object
Steve Block44f0eee2011-05-26 01:26:41 +0100256 __ mov(eax, factory->fixed_array_map());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100257 __ mov(Operand(edi, FixedArray::kMapOffset), eax); // setup the map
258 __ SmiTag(edx);
259 __ mov(Operand(edi, FixedArray::kLengthOffset), edx); // and length
Steve Blocka7e24c12009-10-30 11:49:00 +0000260
261 // Initialize the fields to undefined.
262 // ebx: JSObject
263 // edi: FixedArray
264 // ecx: start of next object
265 { Label loop, entry;
Steve Block44f0eee2011-05-26 01:26:41 +0100266 __ mov(edx, factory->undefined_value());
Steve Blocka7e24c12009-10-30 11:49:00 +0000267 __ lea(eax, Operand(edi, FixedArray::kHeaderSize));
268 __ jmp(&entry);
269 __ bind(&loop);
270 __ mov(Operand(eax, 0), edx);
271 __ add(Operand(eax), Immediate(kPointerSize));
272 __ bind(&entry);
273 __ cmp(eax, Operand(ecx));
274 __ j(below, &loop);
275 }
276
277 // Store the initialized FixedArray into the properties field of
278 // the JSObject
279 // ebx: JSObject
280 // edi: FixedArray
281 __ or_(Operand(edi), Immediate(kHeapObjectTag)); // add the heap tag
282 __ mov(FieldOperand(ebx, JSObject::kPropertiesOffset), edi);
283
284
285 // Continue with JSObject being successfully allocated
286 // ebx: JSObject
287 __ jmp(&allocated);
288
289 // Undo the setting of the new top so that the heap is verifiable. For
290 // example, the map's unused properties potentially do not match the
291 // allocated objects unused properties.
292 // ebx: JSObject (previous new top)
293 __ bind(&undo_allocation);
294 __ UndoAllocationInNewSpace(ebx);
295 }
296
297 // Allocate the new receiver object using the runtime call.
298 __ bind(&rt_call);
299 // Must restore edi (constructor) before calling runtime.
300 __ mov(edi, Operand(esp, 0));
301 // edi: function (constructor)
302 __ push(edi);
303 __ CallRuntime(Runtime::kNewObject, 1);
304 __ mov(ebx, Operand(eax)); // store result in ebx
305
306 // New object allocated.
307 // ebx: newly allocated object
308 __ bind(&allocated);
309 // Retrieve the function from the stack.
310 __ pop(edi);
311
312 // Retrieve smi-tagged arguments count from the stack.
313 __ mov(eax, Operand(esp, 0));
Leon Clarkee46be812010-01-19 14:06:41 +0000314 __ SmiUntag(eax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000315
316 // Push the allocated receiver to the stack. We need two copies
317 // because we may have to return the original one and the calling
318 // conventions dictate that the called function pops the receiver.
319 __ push(ebx);
320 __ push(ebx);
321
322 // Setup pointer to last argument.
323 __ lea(ebx, Operand(ebp, StandardFrameConstants::kCallerSPOffset));
324
325 // Copy arguments and receiver to the expression stack.
326 Label loop, entry;
327 __ mov(ecx, Operand(eax));
328 __ jmp(&entry);
329 __ bind(&loop);
330 __ push(Operand(ebx, ecx, times_4, 0));
331 __ bind(&entry);
332 __ dec(ecx);
333 __ j(greater_equal, &loop);
334
335 // Call the function.
Leon Clarkee46be812010-01-19 14:06:41 +0000336 if (is_api_function) {
337 __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
Steve Block44f0eee2011-05-26 01:26:41 +0100338 Handle<Code> code =
339 masm->isolate()->builtins()->HandleApiCallConstruct();
Leon Clarkee46be812010-01-19 14:06:41 +0000340 ParameterCount expected(0);
Ben Murdoch257744e2011-11-30 15:57:28 +0000341 __ InvokeCode(code, expected, expected, RelocInfo::CODE_TARGET,
342 CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
Leon Clarkee46be812010-01-19 14:06:41 +0000343 } else {
344 ParameterCount actual(eax);
Ben Murdoch257744e2011-11-30 15:57:28 +0000345 __ InvokeFunction(edi, actual, CALL_FUNCTION,
346 NullCallWrapper(), CALL_AS_METHOD);
Leon Clarkee46be812010-01-19 14:06:41 +0000347 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000348
349 // Restore context from the frame.
350 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
351
352 // If the result is an object (in the ECMA sense), we should get rid
353 // of the receiver and use the result; see ECMA-262 section 13.2.2-7
354 // on page 74.
355 Label use_receiver, exit;
356
357 // If the result is a smi, it is *not* an object in the ECMA sense.
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000358 __ JumpIfSmi(eax, &use_receiver);
Steve Blocka7e24c12009-10-30 11:49:00 +0000359
360 // If the type of the result (stored in its map) is less than
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000361 // FIRST_SPEC_OBJECT_TYPE, it is not an object in the ECMA sense.
362 __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
Ben Murdoch257744e2011-11-30 15:57:28 +0000363 __ j(above_equal, &exit);
Steve Blocka7e24c12009-10-30 11:49:00 +0000364
365 // Throw away the result of the constructor invocation and use the
366 // on-stack receiver as the result.
367 __ bind(&use_receiver);
368 __ mov(eax, Operand(esp, 0));
369
370 // Restore the arguments count and leave the construct frame.
371 __ bind(&exit);
372 __ mov(ebx, Operand(esp, kPointerSize)); // get arguments count
373 __ LeaveConstructFrame();
374
375 // Remove caller arguments from the stack and return.
376 ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
377 __ pop(ecx);
378 __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver
379 __ push(ecx);
Steve Block44f0eee2011-05-26 01:26:41 +0100380 __ IncrementCounter(masm->isolate()->counters()->constructed_objects(), 1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000381 __ ret(0);
382}
383
384
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100385void Builtins::Generate_JSConstructStubCountdown(MacroAssembler* masm) {
386 Generate_JSConstructStubHelper(masm, false, true);
387}
388
389
Leon Clarkee46be812010-01-19 14:06:41 +0000390void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100391 Generate_JSConstructStubHelper(masm, false, false);
Leon Clarkee46be812010-01-19 14:06:41 +0000392}
393
394
395void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100396 Generate_JSConstructStubHelper(masm, true, false);
Leon Clarkee46be812010-01-19 14:06:41 +0000397}
398
399
Steve Blocka7e24c12009-10-30 11:49:00 +0000400static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
401 bool is_construct) {
402 // Clear the context before we push it when entering the JS frame.
Steve Block9fac8402011-05-12 15:51:54 +0100403 __ Set(esi, Immediate(0));
Steve Blocka7e24c12009-10-30 11:49:00 +0000404
405 // Enter an internal frame.
406 __ EnterInternalFrame();
407
408 // Load the previous frame pointer (ebx) to access C arguments
409 __ mov(ebx, Operand(ebp, 0));
410
411 // Get the function from the frame and setup the context.
412 __ mov(ecx, Operand(ebx, EntryFrameConstants::kFunctionArgOffset));
413 __ mov(esi, FieldOperand(ecx, JSFunction::kContextOffset));
414
415 // Push the function and the receiver onto the stack.
416 __ push(ecx);
417 __ push(Operand(ebx, EntryFrameConstants::kReceiverArgOffset));
418
419 // Load the number of arguments and setup pointer to the arguments.
420 __ mov(eax, Operand(ebx, EntryFrameConstants::kArgcOffset));
421 __ mov(ebx, Operand(ebx, EntryFrameConstants::kArgvOffset));
422
423 // Copy arguments to the stack in a loop.
424 Label loop, entry;
Steve Block9fac8402011-05-12 15:51:54 +0100425 __ Set(ecx, Immediate(0));
Steve Blocka7e24c12009-10-30 11:49:00 +0000426 __ jmp(&entry);
427 __ bind(&loop);
428 __ mov(edx, Operand(ebx, ecx, times_4, 0)); // push parameter from argv
429 __ push(Operand(edx, 0)); // dereference handle
430 __ inc(Operand(ecx));
431 __ bind(&entry);
432 __ cmp(ecx, Operand(eax));
433 __ j(not_equal, &loop);
434
435 // Get the function from the stack and call it.
436 __ mov(edi, Operand(esp, eax, times_4, +1 * kPointerSize)); // +1 ~ receiver
437
438 // Invoke the code.
439 if (is_construct) {
Steve Block44f0eee2011-05-26 01:26:41 +0100440 __ call(masm->isolate()->builtins()->JSConstructCall(),
Steve Blocka7e24c12009-10-30 11:49:00 +0000441 RelocInfo::CODE_TARGET);
442 } else {
443 ParameterCount actual(eax);
Ben Murdoch257744e2011-11-30 15:57:28 +0000444 __ InvokeFunction(edi, actual, CALL_FUNCTION,
445 NullCallWrapper(), CALL_AS_METHOD);
Steve Blocka7e24c12009-10-30 11:49:00 +0000446 }
447
448 // Exit the JS frame. Notice that this also removes the empty
449 // context and the function left on the stack by the code
450 // invocation.
451 __ LeaveInternalFrame();
452 __ ret(1 * kPointerSize); // remove receiver
453}
454
455
456void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) {
457 Generate_JSEntryTrampolineHelper(masm, false);
458}
459
460
461void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
462 Generate_JSEntryTrampolineHelper(masm, true);
463}
464
465
Iain Merrick75681382010-08-19 15:07:18 +0100466void Builtins::Generate_LazyCompile(MacroAssembler* masm) {
467 // Enter an internal frame.
468 __ EnterInternalFrame();
469
Ben Murdoch257744e2011-11-30 15:57:28 +0000470 // Push a copy of the function.
Iain Merrick75681382010-08-19 15:07:18 +0100471 __ push(edi);
Ben Murdoch257744e2011-11-30 15:57:28 +0000472 // Push call kind information.
473 __ push(ecx);
Iain Merrick75681382010-08-19 15:07:18 +0100474
475 __ push(edi); // Function is also the parameter to the runtime call.
476 __ CallRuntime(Runtime::kLazyCompile, 1);
Ben Murdoch257744e2011-11-30 15:57:28 +0000477
478 // Restore call kind information.
479 __ pop(ecx);
480 // Restore receiver.
Iain Merrick75681382010-08-19 15:07:18 +0100481 __ pop(edi);
482
483 // Tear down temporary frame.
484 __ LeaveInternalFrame();
485
486 // Do a tail-call of the compiled function.
Ben Murdoch257744e2011-11-30 15:57:28 +0000487 __ lea(eax, FieldOperand(eax, Code::kHeaderSize));
488 __ jmp(Operand(eax));
Iain Merrick75681382010-08-19 15:07:18 +0100489}
490
491
Ben Murdochb0fe1622011-05-05 13:52:32 +0100492void Builtins::Generate_LazyRecompile(MacroAssembler* masm) {
493 // Enter an internal frame.
494 __ EnterInternalFrame();
495
496 // Push a copy of the function onto the stack.
497 __ push(edi);
Ben Murdoch257744e2011-11-30 15:57:28 +0000498 // Push call kind information.
499 __ push(ecx);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100500
501 __ push(edi); // Function is also the parameter to the runtime call.
502 __ CallRuntime(Runtime::kLazyRecompile, 1);
503
Ben Murdoch257744e2011-11-30 15:57:28 +0000504 // Restore call kind information.
505 __ pop(ecx);
506 // Restore receiver.
Ben Murdochb0fe1622011-05-05 13:52:32 +0100507 __ pop(edi);
Ben Murdoch257744e2011-11-30 15:57:28 +0000508
509 // Tear down temporary frame.
Ben Murdochb0fe1622011-05-05 13:52:32 +0100510 __ LeaveInternalFrame();
511
512 // Do a tail-call of the compiled function.
Ben Murdoch257744e2011-11-30 15:57:28 +0000513 __ lea(eax, FieldOperand(eax, Code::kHeaderSize));
514 __ jmp(Operand(eax));
Ben Murdochb0fe1622011-05-05 13:52:32 +0100515}
516
517
518static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm,
519 Deoptimizer::BailoutType type) {
520 // Enter an internal frame.
521 __ EnterInternalFrame();
522
523 // Pass the function and deoptimization type to the runtime system.
524 __ push(Immediate(Smi::FromInt(static_cast<int>(type))));
525 __ CallRuntime(Runtime::kNotifyDeoptimized, 1);
526
527 // Tear down temporary frame.
528 __ LeaveInternalFrame();
529
530 // Get the full codegen state from the stack and untag it.
531 __ mov(ecx, Operand(esp, 1 * kPointerSize));
532 __ SmiUntag(ecx);
533
534 // Switch on the state.
Ben Murdoch257744e2011-11-30 15:57:28 +0000535 Label not_no_registers, not_tos_eax;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100536 __ cmp(ecx, FullCodeGenerator::NO_REGISTERS);
Ben Murdoch257744e2011-11-30 15:57:28 +0000537 __ j(not_equal, &not_no_registers, Label::kNear);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100538 __ ret(1 * kPointerSize); // Remove state.
539
540 __ bind(&not_no_registers);
541 __ mov(eax, Operand(esp, 2 * kPointerSize));
542 __ cmp(ecx, FullCodeGenerator::TOS_REG);
Ben Murdoch257744e2011-11-30 15:57:28 +0000543 __ j(not_equal, &not_tos_eax, Label::kNear);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100544 __ ret(2 * kPointerSize); // Remove state, eax.
545
546 __ bind(&not_tos_eax);
547 __ Abort("no cases left");
548}
549
550
551void Builtins::Generate_NotifyDeoptimized(MacroAssembler* masm) {
552 Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::EAGER);
553}
554
555
556void Builtins::Generate_NotifyLazyDeoptimized(MacroAssembler* masm) {
557 Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::LAZY);
558}
559
560
561void Builtins::Generate_NotifyOSR(MacroAssembler* masm) {
562 // TODO(kasperl): Do we need to save/restore the XMM registers too?
563
564 // For now, we are relying on the fact that Runtime::NotifyOSR
565 // doesn't do any garbage collection which allows us to save/restore
566 // the registers without worrying about which of them contain
567 // pointers. This seems a bit fragile.
568 __ pushad();
569 __ EnterInternalFrame();
570 __ CallRuntime(Runtime::kNotifyOSR, 0);
571 __ LeaveInternalFrame();
572 __ popad();
573 __ ret(0);
574}
575
576
Steve Blocka7e24c12009-10-30 11:49:00 +0000577void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
Steve Block44f0eee2011-05-26 01:26:41 +0100578 Factory* factory = masm->isolate()->factory();
579
Steve Blocka7e24c12009-10-30 11:49:00 +0000580 // 1. Make sure we have at least one argument.
581 { Label done;
582 __ test(eax, Operand(eax));
Ben Murdoch257744e2011-11-30 15:57:28 +0000583 __ j(not_zero, &done);
Steve Blocka7e24c12009-10-30 11:49:00 +0000584 __ pop(ebx);
Steve Block44f0eee2011-05-26 01:26:41 +0100585 __ push(Immediate(factory->undefined_value()));
Steve Blocka7e24c12009-10-30 11:49:00 +0000586 __ push(ebx);
587 __ inc(eax);
588 __ bind(&done);
589 }
590
Andrei Popescu402d9372010-02-26 13:31:12 +0000591 // 2. Get the function to call (passed as receiver) from the stack, check
592 // if it is a function.
593 Label non_function;
594 // 1 ~ return address.
595 __ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize));
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000596 __ JumpIfSmi(edi, &non_function);
Andrei Popescu402d9372010-02-26 13:31:12 +0000597 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
Ben Murdoch257744e2011-11-30 15:57:28 +0000598 __ j(not_equal, &non_function);
Steve Blocka7e24c12009-10-30 11:49:00 +0000599
Steve Blocka7e24c12009-10-30 11:49:00 +0000600
Andrei Popescu402d9372010-02-26 13:31:12 +0000601 // 3a. Patch the first argument if necessary when calling a function.
602 Label shift_arguments;
603 { Label convert_to_object, use_global_receiver, patch_receiver;
604 // Change context eagerly in case we need the global receiver.
Steve Blocka7e24c12009-10-30 11:49:00 +0000605 __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
606
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100607 // Do not transform the receiver for strict mode functions.
608 __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
609 __ test_b(FieldOperand(ebx, SharedFunctionInfo::kStrictModeByteOffset),
610 1 << SharedFunctionInfo::kStrictModeBitWithinByte);
611 __ j(not_equal, &shift_arguments);
612
Ben Murdoch257744e2011-11-30 15:57:28 +0000613 // Do not transform the receiver for natives (shared already in ebx).
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000614 __ test_b(FieldOperand(ebx, SharedFunctionInfo::kNativeByteOffset),
615 1 << SharedFunctionInfo::kNativeBitWithinByte);
Ben Murdoch257744e2011-11-30 15:57:28 +0000616 __ j(not_equal, &shift_arguments);
617
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100618 // Compute the receiver in non-strict mode.
Andrei Popescu402d9372010-02-26 13:31:12 +0000619 __ mov(ebx, Operand(esp, eax, times_4, 0)); // First argument.
Ben Murdoch257744e2011-11-30 15:57:28 +0000620
621 // Call ToObject on the receiver if it is not an object, or use the
622 // global object if it is null or undefined.
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000623 __ JumpIfSmi(ebx, &convert_to_object);
Steve Block44f0eee2011-05-26 01:26:41 +0100624 __ cmp(ebx, factory->null_value());
Steve Blocka7e24c12009-10-30 11:49:00 +0000625 __ j(equal, &use_global_receiver);
Steve Block44f0eee2011-05-26 01:26:41 +0100626 __ cmp(ebx, factory->undefined_value());
Steve Blocka7e24c12009-10-30 11:49:00 +0000627 __ j(equal, &use_global_receiver);
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000628 STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE);
629 __ CmpObjectType(ebx, FIRST_SPEC_OBJECT_TYPE, ecx);
Ben Murdoch257744e2011-11-30 15:57:28 +0000630 __ j(above_equal, &shift_arguments);
Steve Blocka7e24c12009-10-30 11:49:00 +0000631
Andrei Popescu402d9372010-02-26 13:31:12 +0000632 __ bind(&convert_to_object);
633 __ EnterInternalFrame(); // In order to preserve argument count.
Leon Clarkee46be812010-01-19 14:06:41 +0000634 __ SmiTag(eax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000635 __ push(eax);
636
Steve Blocka7e24c12009-10-30 11:49:00 +0000637 __ push(ebx);
638 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
639 __ mov(ebx, eax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000640
Steve Blocka7e24c12009-10-30 11:49:00 +0000641 __ pop(eax);
Leon Clarkee46be812010-01-19 14:06:41 +0000642 __ SmiUntag(eax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000643 __ LeaveInternalFrame();
Andrei Popescu402d9372010-02-26 13:31:12 +0000644 // Restore the function to edi.
645 __ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize));
Steve Blocka7e24c12009-10-30 11:49:00 +0000646 __ jmp(&patch_receiver);
647
Andrei Popescu402d9372010-02-26 13:31:12 +0000648 // Use the global receiver object from the called function as the
649 // receiver.
Steve Blocka7e24c12009-10-30 11:49:00 +0000650 __ bind(&use_global_receiver);
651 const int kGlobalIndex =
652 Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
653 __ mov(ebx, FieldOperand(esi, kGlobalIndex));
Steve Blockd0582a62009-12-15 09:54:21 +0000654 __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalContextOffset));
655 __ mov(ebx, FieldOperand(ebx, kGlobalIndex));
Steve Blocka7e24c12009-10-30 11:49:00 +0000656 __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
657
658 __ bind(&patch_receiver);
659 __ mov(Operand(esp, eax, times_4, 0), ebx);
660
Andrei Popescu402d9372010-02-26 13:31:12 +0000661 __ jmp(&shift_arguments);
Steve Blocka7e24c12009-10-30 11:49:00 +0000662 }
663
Andrei Popescu402d9372010-02-26 13:31:12 +0000664 // 3b. Patch the first argument when calling a non-function. The
665 // CALL_NON_FUNCTION builtin expects the non-function callee as
666 // receiver, so overwrite the first argument which will ultimately
667 // become the receiver.
668 __ bind(&non_function);
669 __ mov(Operand(esp, eax, times_4, 0), edi);
670 // Clear edi to indicate a non-function being called.
Steve Block9fac8402011-05-12 15:51:54 +0100671 __ Set(edi, Immediate(0));
Leon Clarkee46be812010-01-19 14:06:41 +0000672
Andrei Popescu402d9372010-02-26 13:31:12 +0000673 // 4. Shift arguments and return address one slot down on the stack
674 // (overwriting the original receiver). Adjust argument count to make
675 // the original first argument the new receiver.
676 __ bind(&shift_arguments);
Steve Blocka7e24c12009-10-30 11:49:00 +0000677 { Label loop;
Leon Clarkee46be812010-01-19 14:06:41 +0000678 __ mov(ecx, eax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000679 __ bind(&loop);
680 __ mov(ebx, Operand(esp, ecx, times_4, 0));
681 __ mov(Operand(esp, ecx, times_4, kPointerSize), ebx);
682 __ dec(ecx);
Andrei Popescu402d9372010-02-26 13:31:12 +0000683 __ j(not_sign, &loop); // While non-negative (to copy return address).
Leon Clarkee46be812010-01-19 14:06:41 +0000684 __ pop(ebx); // Discard copy of return address.
685 __ dec(eax); // One fewer argument (first argument is new receiver).
Steve Blocka7e24c12009-10-30 11:49:00 +0000686 }
687
Andrei Popescu402d9372010-02-26 13:31:12 +0000688 // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
689 { Label function;
690 __ test(edi, Operand(edi));
Ben Murdoch257744e2011-11-30 15:57:28 +0000691 __ j(not_zero, &function);
Steve Block9fac8402011-05-12 15:51:54 +0100692 __ Set(ebx, Immediate(0));
Andrei Popescu402d9372010-02-26 13:31:12 +0000693 __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
Ben Murdoch257744e2011-11-30 15:57:28 +0000694 __ SetCallKind(ecx, CALL_AS_METHOD);
Steve Block44f0eee2011-05-26 01:26:41 +0100695 __ jmp(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
Andrei Popescu402d9372010-02-26 13:31:12 +0000696 RelocInfo::CODE_TARGET);
697 __ bind(&function);
Steve Blocka7e24c12009-10-30 11:49:00 +0000698 }
699
Andrei Popescu402d9372010-02-26 13:31:12 +0000700 // 5b. Get the code to call from the function and check that the number of
701 // expected arguments matches what we're providing. If so, jump
702 // (tail-call) to the code in register edx without checking arguments.
703 __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
704 __ mov(ebx,
705 FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
Steve Block791712a2010-08-27 10:21:07 +0100706 __ mov(edx, FieldOperand(edi, JSFunction::kCodeEntryOffset));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100707 __ SmiUntag(ebx);
Ben Murdoch257744e2011-11-30 15:57:28 +0000708 __ SetCallKind(ecx, CALL_AS_METHOD);
Andrei Popescu402d9372010-02-26 13:31:12 +0000709 __ cmp(eax, Operand(ebx));
Steve Block44f0eee2011-05-26 01:26:41 +0100710 __ j(not_equal,
711 masm->isolate()->builtins()->ArgumentsAdaptorTrampoline());
Andrei Popescu402d9372010-02-26 13:31:12 +0000712
Steve Blocka7e24c12009-10-30 11:49:00 +0000713 ParameterCount expected(0);
Ben Murdoch257744e2011-11-30 15:57:28 +0000714 __ InvokeCode(Operand(edx), expected, expected, JUMP_FUNCTION,
715 NullCallWrapper(), CALL_AS_METHOD);
Steve Blocka7e24c12009-10-30 11:49:00 +0000716}
717
718
719void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
720 __ EnterInternalFrame();
721
722 __ push(Operand(ebp, 4 * kPointerSize)); // push this
723 __ push(Operand(ebp, 2 * kPointerSize)); // push arguments
724 __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
725
Steve Blockd0582a62009-12-15 09:54:21 +0000726 // Check the stack for overflow. We are not trying need to catch
727 // interruptions (e.g. debug break and preemption) here, so the "real stack
728 // limit" is checked.
729 Label okay;
730 ExternalReference real_stack_limit =
Steve Block44f0eee2011-05-26 01:26:41 +0100731 ExternalReference::address_of_real_stack_limit(masm->isolate());
Steve Blockd0582a62009-12-15 09:54:21 +0000732 __ mov(edi, Operand::StaticVariable(real_stack_limit));
733 // Make ecx the space we have left. The stack might already be overflowed
734 // here which will cause ecx to become negative.
735 __ mov(ecx, Operand(esp));
736 __ sub(ecx, Operand(edi));
737 // Make edx the space we need for the array when it is unrolled onto the
738 // stack.
739 __ mov(edx, Operand(eax));
740 __ shl(edx, kPointerSizeLog2 - kSmiTagSize);
741 // Check if the arguments will overflow the stack.
742 __ cmp(ecx, Operand(edx));
Ben Murdoch257744e2011-11-30 15:57:28 +0000743 __ j(greater, &okay); // Signed comparison.
Steve Blocka7e24c12009-10-30 11:49:00 +0000744
Steve Blockd0582a62009-12-15 09:54:21 +0000745 // Out of stack space.
746 __ push(Operand(ebp, 4 * kPointerSize)); // push this
747 __ push(eax);
748 __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION);
749 __ bind(&okay);
750 // End of stack check.
Steve Blocka7e24c12009-10-30 11:49:00 +0000751
752 // Push current index and limit.
753 const int kLimitOffset =
754 StandardFrameConstants::kExpressionsOffset - 1 * kPointerSize;
755 const int kIndexOffset = kLimitOffset - 1 * kPointerSize;
756 __ push(eax); // limit
757 __ push(Immediate(0)); // index
758
759 // Change context eagerly to get the right global object if
760 // necessary.
761 __ mov(edi, Operand(ebp, 4 * kPointerSize));
762 __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
763
764 // Compute the receiver.
765 Label call_to_object, use_global_receiver, push_receiver;
766 __ mov(ebx, Operand(ebp, 3 * kPointerSize));
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100767
768 // Do not transform the receiver for strict mode functions.
769 __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
770 __ test_b(FieldOperand(ecx, SharedFunctionInfo::kStrictModeByteOffset),
771 1 << SharedFunctionInfo::kStrictModeBitWithinByte);
772 __ j(not_equal, &push_receiver);
773
Ben Murdoch257744e2011-11-30 15:57:28 +0000774 Factory* factory = masm->isolate()->factory();
775
776 // Do not transform the receiver for natives (shared already in ecx).
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000777 __ test_b(FieldOperand(ecx, SharedFunctionInfo::kNativeByteOffset),
778 1 << SharedFunctionInfo::kNativeBitWithinByte);
Ben Murdoch257744e2011-11-30 15:57:28 +0000779 __ j(not_equal, &push_receiver);
780
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100781 // Compute the receiver in non-strict mode.
Ben Murdoch257744e2011-11-30 15:57:28 +0000782 // Call ToObject on the receiver if it is not an object, or use the
783 // global object if it is null or undefined.
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000784 __ JumpIfSmi(ebx, &call_to_object);
Steve Block44f0eee2011-05-26 01:26:41 +0100785 __ cmp(ebx, factory->null_value());
Steve Blocka7e24c12009-10-30 11:49:00 +0000786 __ j(equal, &use_global_receiver);
Steve Block44f0eee2011-05-26 01:26:41 +0100787 __ cmp(ebx, factory->undefined_value());
Steve Blocka7e24c12009-10-30 11:49:00 +0000788 __ j(equal, &use_global_receiver);
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000789 STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE);
790 __ CmpObjectType(ebx, FIRST_SPEC_OBJECT_TYPE, ecx);
Ben Murdoch257744e2011-11-30 15:57:28 +0000791 __ j(above_equal, &push_receiver);
Steve Blocka7e24c12009-10-30 11:49:00 +0000792
Steve Blocka7e24c12009-10-30 11:49:00 +0000793 __ bind(&call_to_object);
794 __ push(ebx);
795 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
796 __ mov(ebx, Operand(eax));
797 __ jmp(&push_receiver);
798
799 // Use the current global receiver object as the receiver.
800 __ bind(&use_global_receiver);
801 const int kGlobalOffset =
802 Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
803 __ mov(ebx, FieldOperand(esi, kGlobalOffset));
Steve Blockd0582a62009-12-15 09:54:21 +0000804 __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalContextOffset));
805 __ mov(ebx, FieldOperand(ebx, kGlobalOffset));
Steve Blocka7e24c12009-10-30 11:49:00 +0000806 __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
807
808 // Push the receiver.
809 __ bind(&push_receiver);
810 __ push(ebx);
811
812 // Copy all arguments from the array to the stack.
813 Label entry, loop;
814 __ mov(eax, Operand(ebp, kIndexOffset));
815 __ jmp(&entry);
816 __ bind(&loop);
Andrei Popescu402d9372010-02-26 13:31:12 +0000817 __ mov(edx, Operand(ebp, 2 * kPointerSize)); // load arguments
Steve Blocka7e24c12009-10-30 11:49:00 +0000818
819 // Use inline caching to speed up access to arguments.
Steve Block44f0eee2011-05-26 01:26:41 +0100820 Handle<Code> ic = masm->isolate()->builtins()->KeyedLoadIC_Initialize();
Steve Blocka7e24c12009-10-30 11:49:00 +0000821 __ call(ic, RelocInfo::CODE_TARGET);
822 // It is important that we do not have a test instruction after the
823 // call. A test instruction after the call is used to indicate that
824 // we have generated an inline version of the keyed load. In this
825 // case, we know that we are not generating a test instruction next.
826
Andrei Popescu402d9372010-02-26 13:31:12 +0000827 // Push the nth argument.
Steve Blocka7e24c12009-10-30 11:49:00 +0000828 __ push(eax);
829
830 // Update the index on the stack and in register eax.
831 __ mov(eax, Operand(ebp, kIndexOffset));
832 __ add(Operand(eax), Immediate(1 << kSmiTagSize));
833 __ mov(Operand(ebp, kIndexOffset), eax);
834
835 __ bind(&entry);
836 __ cmp(eax, Operand(ebp, kLimitOffset));
837 __ j(not_equal, &loop);
838
839 // Invoke the function.
840 ParameterCount actual(eax);
Leon Clarkee46be812010-01-19 14:06:41 +0000841 __ SmiUntag(eax);
Steve Blocka7e24c12009-10-30 11:49:00 +0000842 __ mov(edi, Operand(ebp, 4 * kPointerSize));
Ben Murdoch257744e2011-11-30 15:57:28 +0000843 __ InvokeFunction(edi, actual, CALL_FUNCTION,
844 NullCallWrapper(), CALL_AS_METHOD);
Steve Blocka7e24c12009-10-30 11:49:00 +0000845
846 __ LeaveInternalFrame();
847 __ ret(3 * kPointerSize); // remove this, receiver, and arguments
848}
849
850
Steve Blocka7e24c12009-10-30 11:49:00 +0000851// Number of empty elements to allocate for an empty array.
852static const int kPreallocatedArrayElements = 4;
853
854
855// Allocate an empty JSArray. The allocated array is put into the result
856// register. If the parameter initial_capacity is larger than zero an elements
857// backing store is allocated with this size and filled with the hole values.
858// Otherwise the elements backing store is set to the empty FixedArray.
859static void AllocateEmptyJSArray(MacroAssembler* masm,
860 Register array_function,
861 Register result,
862 Register scratch1,
863 Register scratch2,
864 Register scratch3,
865 int initial_capacity,
866 Label* gc_required) {
867 ASSERT(initial_capacity >= 0);
868
869 // Load the initial map from the array function.
870 __ mov(scratch1, FieldOperand(array_function,
871 JSFunction::kPrototypeOrInitialMapOffset));
872
873 // Allocate the JSArray object together with space for a fixed array with the
874 // requested elements.
875 int size = JSArray::kSize;
876 if (initial_capacity > 0) {
877 size += FixedArray::SizeFor(initial_capacity);
878 }
879 __ AllocateInNewSpace(size,
880 result,
881 scratch2,
882 scratch3,
883 gc_required,
884 TAG_OBJECT);
885
886 // Allocated the JSArray. Now initialize the fields except for the elements
887 // array.
888 // result: JSObject
889 // scratch1: initial map
890 // scratch2: start of next object
891 __ mov(FieldOperand(result, JSObject::kMapOffset), scratch1);
Steve Block44f0eee2011-05-26 01:26:41 +0100892 Factory* factory = masm->isolate()->factory();
Steve Blocka7e24c12009-10-30 11:49:00 +0000893 __ mov(FieldOperand(result, JSArray::kPropertiesOffset),
Steve Block44f0eee2011-05-26 01:26:41 +0100894 factory->empty_fixed_array());
Steve Blocka7e24c12009-10-30 11:49:00 +0000895 // Field JSArray::kElementsOffset is initialized later.
896 __ mov(FieldOperand(result, JSArray::kLengthOffset), Immediate(0));
897
898 // If no storage is requested for the elements array just set the empty
899 // fixed array.
900 if (initial_capacity == 0) {
901 __ mov(FieldOperand(result, JSArray::kElementsOffset),
Steve Block44f0eee2011-05-26 01:26:41 +0100902 factory->empty_fixed_array());
Steve Blocka7e24c12009-10-30 11:49:00 +0000903 return;
904 }
905
906 // Calculate the location of the elements array and set elements array member
907 // of the JSArray.
908 // result: JSObject
909 // scratch2: start of next object
910 __ lea(scratch1, Operand(result, JSArray::kSize));
911 __ mov(FieldOperand(result, JSArray::kElementsOffset), scratch1);
912
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100913 // Initialize the FixedArray and fill it with holes. FixedArray length is
Steve Blocka7e24c12009-10-30 11:49:00 +0000914 // stored as a smi.
915 // result: JSObject
916 // scratch1: elements array
917 // scratch2: start of next object
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100918 __ mov(FieldOperand(scratch1, FixedArray::kMapOffset),
Steve Block44f0eee2011-05-26 01:26:41 +0100919 factory->fixed_array_map());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100920 __ mov(FieldOperand(scratch1, FixedArray::kLengthOffset),
921 Immediate(Smi::FromInt(initial_capacity)));
Steve Blocka7e24c12009-10-30 11:49:00 +0000922
923 // Fill the FixedArray with the hole value. Inline the code if short.
924 // Reconsider loop unfolding if kPreallocatedArrayElements gets changed.
925 static const int kLoopUnfoldLimit = 4;
926 ASSERT(kPreallocatedArrayElements <= kLoopUnfoldLimit);
927 if (initial_capacity <= kLoopUnfoldLimit) {
928 // Use a scratch register here to have only one reloc info when unfolding
929 // the loop.
Steve Block44f0eee2011-05-26 01:26:41 +0100930 __ mov(scratch3, factory->the_hole_value());
Steve Blocka7e24c12009-10-30 11:49:00 +0000931 for (int i = 0; i < initial_capacity; i++) {
932 __ mov(FieldOperand(scratch1,
933 FixedArray::kHeaderSize + i * kPointerSize),
934 scratch3);
935 }
936 } else {
937 Label loop, entry;
938 __ jmp(&entry);
939 __ bind(&loop);
Steve Block44f0eee2011-05-26 01:26:41 +0100940 __ mov(Operand(scratch1, 0), factory->the_hole_value());
Steve Blocka7e24c12009-10-30 11:49:00 +0000941 __ add(Operand(scratch1), Immediate(kPointerSize));
942 __ bind(&entry);
943 __ cmp(scratch1, Operand(scratch2));
944 __ j(below, &loop);
945 }
946}
947
948
949// Allocate a JSArray with the number of elements stored in a register. The
950// register array_function holds the built-in Array function and the register
951// array_size holds the size of the array as a smi. The allocated array is put
952// into the result register and beginning and end of the FixedArray elements
953// storage is put into registers elements_array and elements_array_end (see
954// below for when that is not the case). If the parameter fill_with_holes is
955// true the allocated elements backing store is filled with the hole values
956// otherwise it is left uninitialized. When the backing store is filled the
957// register elements_array is scratched.
958static void AllocateJSArray(MacroAssembler* masm,
959 Register array_function, // Array function.
Steve Block6ded16b2010-05-10 14:33:55 +0100960 Register array_size, // As a smi, cannot be 0.
Steve Blocka7e24c12009-10-30 11:49:00 +0000961 Register result,
962 Register elements_array,
963 Register elements_array_end,
964 Register scratch,
965 bool fill_with_hole,
966 Label* gc_required) {
Steve Block6ded16b2010-05-10 14:33:55 +0100967 ASSERT(scratch.is(edi)); // rep stos destination
968 ASSERT(!fill_with_hole || array_size.is(ecx)); // rep stos count
Leon Clarkef7060e22010-06-03 12:02:55 +0100969 ASSERT(!fill_with_hole || !result.is(eax)); // result is never eax
Steve Blocka7e24c12009-10-30 11:49:00 +0000970
971 // Load the initial map from the array function.
972 __ mov(elements_array,
973 FieldOperand(array_function,
974 JSFunction::kPrototypeOrInitialMapOffset));
975
Steve Blocka7e24c12009-10-30 11:49:00 +0000976 // Allocate the JSArray object together with space for a FixedArray with the
977 // requested elements.
Steve Blocka7e24c12009-10-30 11:49:00 +0000978 ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
979 __ AllocateInNewSpace(JSArray::kSize + FixedArray::kHeaderSize,
980 times_half_pointer_size, // array_size is a smi.
981 array_size,
982 result,
983 elements_array_end,
984 scratch,
985 gc_required,
986 TAG_OBJECT);
987
988 // Allocated the JSArray. Now initialize the fields except for the elements
989 // array.
990 // result: JSObject
991 // elements_array: initial map
992 // elements_array_end: start of next object
993 // array_size: size of array (smi)
Steve Blocka7e24c12009-10-30 11:49:00 +0000994 __ mov(FieldOperand(result, JSObject::kMapOffset), elements_array);
Steve Block44f0eee2011-05-26 01:26:41 +0100995 Factory* factory = masm->isolate()->factory();
996 __ mov(elements_array, factory->empty_fixed_array());
Steve Blocka7e24c12009-10-30 11:49:00 +0000997 __ mov(FieldOperand(result, JSArray::kPropertiesOffset), elements_array);
998 // Field JSArray::kElementsOffset is initialized later.
999 __ mov(FieldOperand(result, JSArray::kLengthOffset), array_size);
1000
1001 // Calculate the location of the elements array and set elements array member
1002 // of the JSArray.
1003 // result: JSObject
1004 // elements_array_end: start of next object
1005 // array_size: size of array (smi)
1006 __ lea(elements_array, Operand(result, JSArray::kSize));
1007 __ mov(FieldOperand(result, JSArray::kElementsOffset), elements_array);
1008
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001009 // Initialize the fixed array. FixedArray length is stored as a smi.
Steve Blocka7e24c12009-10-30 11:49:00 +00001010 // result: JSObject
1011 // elements_array: elements array
1012 // elements_array_end: start of next object
1013 // array_size: size of array (smi)
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001014 __ mov(FieldOperand(elements_array, FixedArray::kMapOffset),
Steve Block44f0eee2011-05-26 01:26:41 +01001015 factory->fixed_array_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00001016 // For non-empty JSArrays the length of the FixedArray and the JSArray is the
1017 // same.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001018 __ mov(FieldOperand(elements_array, FixedArray::kLengthOffset), array_size);
Steve Blocka7e24c12009-10-30 11:49:00 +00001019
1020 // Fill the allocated FixedArray with the hole value if requested.
1021 // result: JSObject
1022 // elements_array: elements array
Steve Blocka7e24c12009-10-30 11:49:00 +00001023 if (fill_with_hole) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001024 __ SmiUntag(array_size);
Steve Block6ded16b2010-05-10 14:33:55 +01001025 __ lea(edi, Operand(elements_array,
1026 FixedArray::kHeaderSize - kHeapObjectTag));
Steve Block44f0eee2011-05-26 01:26:41 +01001027 __ mov(eax, factory->the_hole_value());
Steve Block6ded16b2010-05-10 14:33:55 +01001028 __ cld();
Leon Clarkef7060e22010-06-03 12:02:55 +01001029 // Do not use rep stos when filling less than kRepStosThreshold
1030 // words.
1031 const int kRepStosThreshold = 16;
1032 Label loop, entry, done;
1033 __ cmp(ecx, kRepStosThreshold);
1034 __ j(below, &loop); // Note: ecx > 0.
Steve Block6ded16b2010-05-10 14:33:55 +01001035 __ rep_stos();
Leon Clarkef7060e22010-06-03 12:02:55 +01001036 __ jmp(&done);
1037 __ bind(&loop);
1038 __ stos();
1039 __ bind(&entry);
1040 __ cmp(edi, Operand(elements_array_end));
1041 __ j(below, &loop);
1042 __ bind(&done);
Steve Blocka7e24c12009-10-30 11:49:00 +00001043 }
1044}
1045
1046
1047// Create a new array for the built-in Array function. This function allocates
1048// the JSArray object and the FixedArray elements array and initializes these.
1049// If the Array cannot be constructed in native code the runtime is called. This
1050// function assumes the following state:
1051// edi: constructor (built-in Array function)
1052// eax: argc
1053// esp[0]: return address
1054// esp[4]: last argument
1055// This function is used for both construct and normal calls of Array. Whether
1056// it is a construct call or not is indicated by the construct_call parameter.
1057// The only difference between handling a construct call and a normal call is
1058// that for a construct call the constructor function in edi needs to be
1059// preserved for entering the generic code. In both cases argc in eax needs to
1060// be preserved.
1061static void ArrayNativeCode(MacroAssembler* masm,
1062 bool construct_call,
Steve Blockd0582a62009-12-15 09:54:21 +00001063 Label* call_generic_code) {
Steve Block6ded16b2010-05-10 14:33:55 +01001064 Label argc_one_or_more, argc_two_or_more, prepare_generic_code_call,
1065 empty_array, not_empty_array;
Steve Blocka7e24c12009-10-30 11:49:00 +00001066
1067 // Push the constructor and argc. No need to tag argc as a smi, as there will
1068 // be no garbage collection with this on the stack.
1069 int push_count = 0;
1070 if (construct_call) {
1071 push_count++;
1072 __ push(edi);
1073 }
1074 push_count++;
1075 __ push(eax);
1076
1077 // Check for array construction with zero arguments.
1078 __ test(eax, Operand(eax));
1079 __ j(not_zero, &argc_one_or_more);
1080
Steve Block6ded16b2010-05-10 14:33:55 +01001081 __ bind(&empty_array);
Steve Blocka7e24c12009-10-30 11:49:00 +00001082 // Handle construction of an empty array.
1083 AllocateEmptyJSArray(masm,
1084 edi,
1085 eax,
1086 ebx,
1087 ecx,
1088 edi,
1089 kPreallocatedArrayElements,
1090 &prepare_generic_code_call);
Steve Block44f0eee2011-05-26 01:26:41 +01001091 __ IncrementCounter(masm->isolate()->counters()->array_function_native(), 1);
Steve Blocka7e24c12009-10-30 11:49:00 +00001092 __ pop(ebx);
1093 if (construct_call) {
1094 __ pop(edi);
1095 }
1096 __ ret(kPointerSize);
1097
1098 // Check for one argument. Bail out if argument is not smi or if it is
1099 // negative.
1100 __ bind(&argc_one_or_more);
1101 __ cmp(eax, 1);
1102 __ j(not_equal, &argc_two_or_more);
1103 ASSERT(kSmiTag == 0);
Steve Block6ded16b2010-05-10 14:33:55 +01001104 __ mov(ecx, Operand(esp, (push_count + 1) * kPointerSize));
1105 __ test(ecx, Operand(ecx));
1106 __ j(not_zero, &not_empty_array);
1107
1108 // The single argument passed is zero, so we jump to the code above used to
1109 // handle the case of no arguments passed. To adapt the stack for that we move
1110 // the return address and the pushed constructor (if pushed) one stack slot up
1111 // thereby removing the passed argument. Argc is also on the stack - at the
1112 // bottom - and it needs to be changed from 1 to 0 to have the call into the
1113 // runtime system work in case a GC is required.
1114 for (int i = push_count; i > 0; i--) {
1115 __ mov(eax, Operand(esp, i * kPointerSize));
1116 __ mov(Operand(esp, (i + 1) * kPointerSize), eax);
1117 }
1118 __ add(Operand(esp), Immediate(2 * kPointerSize)); // Drop two stack slots.
1119 __ push(Immediate(0)); // Treat this as a call with argc of zero.
1120 __ jmp(&empty_array);
1121
1122 __ bind(&not_empty_array);
1123 __ test(ecx, Immediate(kIntptrSignBit | kSmiTagMask));
Steve Blocka7e24c12009-10-30 11:49:00 +00001124 __ j(not_zero, &prepare_generic_code_call);
1125
1126 // Handle construction of an empty array of a certain size. Get the size from
1127 // the stack and bail out if size is to large to actually allocate an elements
1128 // array.
Steve Block6ded16b2010-05-10 14:33:55 +01001129 __ cmp(ecx, JSObject::kInitialMaxFastElementArray << kSmiTagSize);
Steve Blocka7e24c12009-10-30 11:49:00 +00001130 __ j(greater_equal, &prepare_generic_code_call);
1131
1132 // edx: array_size (smi)
1133 // edi: constructor
Steve Block6ded16b2010-05-10 14:33:55 +01001134 // esp[0]: argc (cannot be 0 here)
Steve Blocka7e24c12009-10-30 11:49:00 +00001135 // esp[4]: constructor (only if construct_call)
1136 // esp[8]: return address
1137 // esp[C]: argument
1138 AllocateJSArray(masm,
1139 edi,
Steve Block6ded16b2010-05-10 14:33:55 +01001140 ecx,
Steve Blocka7e24c12009-10-30 11:49:00 +00001141 ebx,
Leon Clarkef7060e22010-06-03 12:02:55 +01001142 eax,
Steve Block6ded16b2010-05-10 14:33:55 +01001143 edx,
Steve Blocka7e24c12009-10-30 11:49:00 +00001144 edi,
1145 true,
1146 &prepare_generic_code_call);
Steve Block44f0eee2011-05-26 01:26:41 +01001147 Counters* counters = masm->isolate()->counters();
1148 __ IncrementCounter(counters->array_function_native(), 1);
Leon Clarkef7060e22010-06-03 12:02:55 +01001149 __ mov(eax, ebx);
Steve Blocka7e24c12009-10-30 11:49:00 +00001150 __ pop(ebx);
1151 if (construct_call) {
1152 __ pop(edi);
1153 }
1154 __ ret(2 * kPointerSize);
1155
1156 // Handle construction of an array from a list of arguments.
1157 __ bind(&argc_two_or_more);
1158 ASSERT(kSmiTag == 0);
Leon Clarkee46be812010-01-19 14:06:41 +00001159 __ SmiTag(eax); // Convet argc to a smi.
Steve Blocka7e24c12009-10-30 11:49:00 +00001160 // eax: array_size (smi)
1161 // edi: constructor
1162 // esp[0] : argc
1163 // esp[4]: constructor (only if construct_call)
1164 // esp[8] : return address
1165 // esp[C] : last argument
1166 AllocateJSArray(masm,
1167 edi,
1168 eax,
1169 ebx,
1170 ecx,
1171 edx,
1172 edi,
1173 false,
1174 &prepare_generic_code_call);
Steve Block44f0eee2011-05-26 01:26:41 +01001175 __ IncrementCounter(counters->array_function_native(), 1);
Steve Blocka7e24c12009-10-30 11:49:00 +00001176 __ mov(eax, ebx);
1177 __ pop(ebx);
1178 if (construct_call) {
1179 __ pop(edi);
1180 }
1181 __ push(eax);
1182 // eax: JSArray
1183 // ebx: argc
1184 // edx: elements_array_end (untagged)
1185 // esp[0]: JSArray
1186 // esp[4]: return address
1187 // esp[8]: last argument
1188
1189 // Location of the last argument
1190 __ lea(edi, Operand(esp, 2 * kPointerSize));
1191
1192 // Location of the first array element (Parameter fill_with_holes to
1193 // AllocateJSArrayis false, so the FixedArray is returned in ecx).
1194 __ lea(edx, Operand(ecx, FixedArray::kHeaderSize - kHeapObjectTag));
1195
1196 // ebx: argc
1197 // edx: location of the first array element
1198 // edi: location of the last argument
1199 // esp[0]: JSArray
1200 // esp[4]: return address
1201 // esp[8]: last argument
1202 Label loop, entry;
1203 __ mov(ecx, ebx);
1204 __ jmp(&entry);
1205 __ bind(&loop);
1206 __ mov(eax, Operand(edi, ecx, times_pointer_size, 0));
1207 __ mov(Operand(edx, 0), eax);
1208 __ add(Operand(edx), Immediate(kPointerSize));
1209 __ bind(&entry);
1210 __ dec(ecx);
1211 __ j(greater_equal, &loop);
1212
1213 // Remove caller arguments from the stack and return.
1214 // ebx: argc
1215 // esp[0]: JSArray
1216 // esp[4]: return address
1217 // esp[8]: last argument
1218 __ pop(eax);
1219 __ pop(ecx);
1220 __ lea(esp, Operand(esp, ebx, times_pointer_size, 1 * kPointerSize));
1221 __ push(ecx);
1222 __ ret(0);
1223
1224 // Restore argc and constructor before running the generic code.
1225 __ bind(&prepare_generic_code_call);
1226 __ pop(eax);
1227 if (construct_call) {
1228 __ pop(edi);
1229 }
1230 __ jmp(call_generic_code);
1231}
1232
1233
1234void Builtins::Generate_ArrayCode(MacroAssembler* masm) {
1235 // ----------- S t a t e -------------
1236 // -- eax : argc
1237 // -- esp[0] : return address
1238 // -- esp[4] : last argument
1239 // -----------------------------------
Kristian Monsen25f61362010-05-21 11:50:48 +01001240 Label generic_array_code;
Steve Blocka7e24c12009-10-30 11:49:00 +00001241
1242 // Get the Array function.
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001243 __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, edi);
Steve Blocka7e24c12009-10-30 11:49:00 +00001244
1245 if (FLAG_debug_code) {
1246 // Initial map for the builtin Array function shoud be a map.
1247 __ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
1248 // Will both indicate a NULL and a Smi.
1249 __ test(ebx, Immediate(kSmiTagMask));
1250 __ Assert(not_zero, "Unexpected initial map for Array function");
1251 __ CmpObjectType(ebx, MAP_TYPE, ecx);
1252 __ Assert(equal, "Unexpected initial map for Array function");
1253 }
1254
1255 // Run the native code for the Array function called as a normal function.
1256 ArrayNativeCode(masm, false, &generic_array_code);
1257
1258 // Jump to the generic array code in case the specialized code cannot handle
1259 // the construction.
1260 __ bind(&generic_array_code);
Steve Block44f0eee2011-05-26 01:26:41 +01001261 Handle<Code> array_code =
1262 masm->isolate()->builtins()->ArrayCodeGeneric();
Steve Blocka7e24c12009-10-30 11:49:00 +00001263 __ jmp(array_code, RelocInfo::CODE_TARGET);
1264}
1265
1266
1267void Builtins::Generate_ArrayConstructCode(MacroAssembler* masm) {
1268 // ----------- S t a t e -------------
1269 // -- eax : argc
1270 // -- edi : constructor
1271 // -- esp[0] : return address
1272 // -- esp[4] : last argument
1273 // -----------------------------------
1274 Label generic_constructor;
1275
1276 if (FLAG_debug_code) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +01001277 // The array construct code is only set for the global and natives
1278 // builtin Array functions which always have maps.
1279
Steve Blocka7e24c12009-10-30 11:49:00 +00001280 // Initial map for the builtin Array function should be a map.
1281 __ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
1282 // Will both indicate a NULL and a Smi.
1283 __ test(ebx, Immediate(kSmiTagMask));
1284 __ Assert(not_zero, "Unexpected initial map for Array function");
1285 __ CmpObjectType(ebx, MAP_TYPE, ecx);
1286 __ Assert(equal, "Unexpected initial map for Array function");
1287 }
1288
1289 // Run the native code for the Array function called as constructor.
1290 ArrayNativeCode(masm, true, &generic_constructor);
1291
1292 // Jump to the generic construct code in case the specialized code cannot
1293 // handle the construction.
1294 __ bind(&generic_constructor);
Steve Block44f0eee2011-05-26 01:26:41 +01001295 Handle<Code> generic_construct_stub =
1296 masm->isolate()->builtins()->JSConstructStubGeneric();
Steve Blocka7e24c12009-10-30 11:49:00 +00001297 __ jmp(generic_construct_stub, RelocInfo::CODE_TARGET);
1298}
1299
1300
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001301void Builtins::Generate_StringConstructCode(MacroAssembler* masm) {
1302 // ----------- S t a t e -------------
1303 // -- eax : number of arguments
1304 // -- edi : constructor function
1305 // -- esp[0] : return address
1306 // -- esp[(argc - n) * 4] : arg[n] (zero-based)
1307 // -- esp[(argc + 1) * 4] : receiver
1308 // -----------------------------------
Steve Block44f0eee2011-05-26 01:26:41 +01001309 Counters* counters = masm->isolate()->counters();
1310 __ IncrementCounter(counters->string_ctor_calls(), 1);
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001311
1312 if (FLAG_debug_code) {
1313 __ LoadGlobalFunction(Context::STRING_FUNCTION_INDEX, ecx);
1314 __ cmp(edi, Operand(ecx));
1315 __ Assert(equal, "Unexpected String function");
1316 }
1317
1318 // Load the first argument into eax and get rid of the rest
1319 // (including the receiver).
1320 Label no_arguments;
1321 __ test(eax, Operand(eax));
1322 __ j(zero, &no_arguments);
1323 __ mov(ebx, Operand(esp, eax, times_pointer_size, 0));
1324 __ pop(ecx);
1325 __ lea(esp, Operand(esp, eax, times_pointer_size, kPointerSize));
1326 __ push(ecx);
1327 __ mov(eax, ebx);
1328
1329 // Lookup the argument in the number to string cache.
1330 Label not_cached, argument_is_string;
1331 NumberToStringStub::GenerateLookupNumberStringCache(
1332 masm,
1333 eax, // Input.
1334 ebx, // Result.
1335 ecx, // Scratch 1.
1336 edx, // Scratch 2.
1337 false, // Input is known to be smi?
1338 &not_cached);
Steve Block44f0eee2011-05-26 01:26:41 +01001339 __ IncrementCounter(counters->string_ctor_cached_number(), 1);
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001340 __ bind(&argument_is_string);
1341 // ----------- S t a t e -------------
1342 // -- ebx : argument converted to string
1343 // -- edi : constructor function
1344 // -- esp[0] : return address
1345 // -----------------------------------
1346
1347 // Allocate a JSValue and put the tagged pointer into eax.
1348 Label gc_required;
1349 __ AllocateInNewSpace(JSValue::kSize,
1350 eax, // Result.
1351 ecx, // New allocation top (we ignore it).
1352 no_reg,
1353 &gc_required,
1354 TAG_OBJECT);
1355
1356 // Set the map.
1357 __ LoadGlobalFunctionInitialMap(edi, ecx);
1358 if (FLAG_debug_code) {
1359 __ cmpb(FieldOperand(ecx, Map::kInstanceSizeOffset),
1360 JSValue::kSize >> kPointerSizeLog2);
1361 __ Assert(equal, "Unexpected string wrapper instance size");
1362 __ cmpb(FieldOperand(ecx, Map::kUnusedPropertyFieldsOffset), 0);
1363 __ Assert(equal, "Unexpected unused properties of string wrapper");
1364 }
1365 __ mov(FieldOperand(eax, HeapObject::kMapOffset), ecx);
1366
1367 // Set properties and elements.
Steve Block44f0eee2011-05-26 01:26:41 +01001368 Factory* factory = masm->isolate()->factory();
1369 __ Set(ecx, Immediate(factory->empty_fixed_array()));
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001370 __ mov(FieldOperand(eax, JSObject::kPropertiesOffset), ecx);
1371 __ mov(FieldOperand(eax, JSObject::kElementsOffset), ecx);
1372
1373 // Set the value.
1374 __ mov(FieldOperand(eax, JSValue::kValueOffset), ebx);
1375
1376 // Ensure the object is fully initialized.
1377 STATIC_ASSERT(JSValue::kSize == 4 * kPointerSize);
1378
1379 // We're done. Return.
1380 __ ret(0);
1381
1382 // The argument was not found in the number to string cache. Check
1383 // if it's a string already before calling the conversion builtin.
1384 Label convert_argument;
1385 __ bind(&not_cached);
1386 STATIC_ASSERT(kSmiTag == 0);
Ben Murdoch3fb3ca82011-12-02 17:19:32 +00001387 __ JumpIfSmi(eax, &convert_argument);
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001388 Condition is_string = masm->IsObjectStringType(eax, ebx, ecx);
1389 __ j(NegateCondition(is_string), &convert_argument);
1390 __ mov(ebx, eax);
Steve Block44f0eee2011-05-26 01:26:41 +01001391 __ IncrementCounter(counters->string_ctor_string_value(), 1);
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001392 __ jmp(&argument_is_string);
1393
1394 // Invoke the conversion builtin and put the result into ebx.
1395 __ bind(&convert_argument);
Steve Block44f0eee2011-05-26 01:26:41 +01001396 __ IncrementCounter(counters->string_ctor_conversions(), 1);
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001397 __ EnterInternalFrame();
1398 __ push(edi); // Preserve the function.
1399 __ push(eax);
1400 __ InvokeBuiltin(Builtins::TO_STRING, CALL_FUNCTION);
1401 __ pop(edi);
1402 __ LeaveInternalFrame();
1403 __ mov(ebx, eax);
1404 __ jmp(&argument_is_string);
1405
1406 // Load the empty string into ebx, remove the receiver from the
1407 // stack, and jump back to the case where the argument is a string.
1408 __ bind(&no_arguments);
Steve Block44f0eee2011-05-26 01:26:41 +01001409 __ Set(ebx, Immediate(factory->empty_string()));
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001410 __ pop(ecx);
1411 __ lea(esp, Operand(esp, kPointerSize));
1412 __ push(ecx);
1413 __ jmp(&argument_is_string);
1414
1415 // At this point the argument is already a string. Call runtime to
1416 // create a string wrapper.
1417 __ bind(&gc_required);
Steve Block44f0eee2011-05-26 01:26:41 +01001418 __ IncrementCounter(counters->string_ctor_gc_required(), 1);
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001419 __ EnterInternalFrame();
1420 __ push(ebx);
1421 __ CallRuntime(Runtime::kNewStringWrapper, 1);
1422 __ LeaveInternalFrame();
1423 __ ret(0);
1424}
1425
1426
Steve Blocka7e24c12009-10-30 11:49:00 +00001427static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
1428 __ push(ebp);
1429 __ mov(ebp, Operand(esp));
1430
1431 // Store the arguments adaptor context sentinel.
1432 __ push(Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
1433
1434 // Push the function on the stack.
1435 __ push(edi);
1436
Ben Murdoch257744e2011-11-30 15:57:28 +00001437 // Preserve the number of arguments on the stack. Must preserve eax,
1438 // ebx and ecx because these registers are used when copying the
Steve Blocka7e24c12009-10-30 11:49:00 +00001439 // arguments and the receiver.
1440 ASSERT(kSmiTagSize == 1);
Ben Murdoch257744e2011-11-30 15:57:28 +00001441 __ lea(edi, Operand(eax, eax, times_1, kSmiTag));
1442 __ push(edi);
Steve Blocka7e24c12009-10-30 11:49:00 +00001443}
1444
1445
1446static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) {
1447 // Retrieve the number of arguments from the stack.
1448 __ mov(ebx, Operand(ebp, ArgumentsAdaptorFrameConstants::kLengthOffset));
1449
1450 // Leave the frame.
1451 __ leave();
1452
1453 // Remove caller arguments from the stack.
1454 ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
1455 __ pop(ecx);
1456 __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver
1457 __ push(ecx);
1458}
1459
1460
1461void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
1462 // ----------- S t a t e -------------
1463 // -- eax : actual number of arguments
1464 // -- ebx : expected number of arguments
Ben Murdoch257744e2011-11-30 15:57:28 +00001465 // -- ecx : call kind information
Steve Blocka7e24c12009-10-30 11:49:00 +00001466 // -- edx : code entry to call
1467 // -----------------------------------
1468
1469 Label invoke, dont_adapt_arguments;
Steve Block44f0eee2011-05-26 01:26:41 +01001470 __ IncrementCounter(masm->isolate()->counters()->arguments_adaptors(), 1);
Steve Blocka7e24c12009-10-30 11:49:00 +00001471
1472 Label enough, too_few;
1473 __ cmp(eax, Operand(ebx));
1474 __ j(less, &too_few);
1475 __ cmp(ebx, SharedFunctionInfo::kDontAdaptArgumentsSentinel);
1476 __ j(equal, &dont_adapt_arguments);
1477
1478 { // Enough parameters: Actual >= expected.
1479 __ bind(&enough);
1480 EnterArgumentsAdaptorFrame(masm);
1481
1482 // Copy receiver and all expected arguments.
1483 const int offset = StandardFrameConstants::kCallerSPOffset;
1484 __ lea(eax, Operand(ebp, eax, times_4, offset));
Ben Murdoch257744e2011-11-30 15:57:28 +00001485 __ mov(edi, -1); // account for receiver
Steve Blocka7e24c12009-10-30 11:49:00 +00001486
1487 Label copy;
1488 __ bind(&copy);
Ben Murdoch257744e2011-11-30 15:57:28 +00001489 __ inc(edi);
Steve Blocka7e24c12009-10-30 11:49:00 +00001490 __ push(Operand(eax, 0));
1491 __ sub(Operand(eax), Immediate(kPointerSize));
Ben Murdoch257744e2011-11-30 15:57:28 +00001492 __ cmp(edi, Operand(ebx));
Steve Blocka7e24c12009-10-30 11:49:00 +00001493 __ j(less, &copy);
1494 __ jmp(&invoke);
1495 }
1496
1497 { // Too few parameters: Actual < expected.
1498 __ bind(&too_few);
1499 EnterArgumentsAdaptorFrame(masm);
1500
1501 // Copy receiver and all actual arguments.
1502 const int offset = StandardFrameConstants::kCallerSPOffset;
1503 __ lea(edi, Operand(ebp, eax, times_4, offset));
Ben Murdoch257744e2011-11-30 15:57:28 +00001504 // ebx = expected - actual.
1505 __ sub(ebx, Operand(eax));
1506 // eax = -actual - 1
1507 __ neg(eax);
1508 __ sub(Operand(eax), Immediate(1));
Steve Blocka7e24c12009-10-30 11:49:00 +00001509
1510 Label copy;
1511 __ bind(&copy);
Ben Murdoch257744e2011-11-30 15:57:28 +00001512 __ inc(eax);
Steve Blocka7e24c12009-10-30 11:49:00 +00001513 __ push(Operand(edi, 0));
1514 __ sub(Operand(edi), Immediate(kPointerSize));
Ben Murdoch257744e2011-11-30 15:57:28 +00001515 __ test(eax, Operand(eax));
1516 __ j(not_zero, &copy);
Steve Blocka7e24c12009-10-30 11:49:00 +00001517
1518 // Fill remaining expected arguments with undefined values.
1519 Label fill;
1520 __ bind(&fill);
Ben Murdoch257744e2011-11-30 15:57:28 +00001521 __ inc(eax);
Steve Block44f0eee2011-05-26 01:26:41 +01001522 __ push(Immediate(masm->isolate()->factory()->undefined_value()));
Ben Murdoch257744e2011-11-30 15:57:28 +00001523 __ cmp(eax, Operand(ebx));
Steve Blocka7e24c12009-10-30 11:49:00 +00001524 __ j(less, &fill);
Steve Blocka7e24c12009-10-30 11:49:00 +00001525 }
1526
1527 // Call the entry point.
1528 __ bind(&invoke);
Ben Murdoch257744e2011-11-30 15:57:28 +00001529 // Restore function pointer.
1530 __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
Steve Blocka7e24c12009-10-30 11:49:00 +00001531 __ call(Operand(edx));
1532
1533 // Leave frame and return.
1534 LeaveArgumentsAdaptorFrame(masm);
1535 __ ret(0);
1536
1537 // -------------------------------------------
1538 // Dont adapt arguments.
1539 // -------------------------------------------
1540 __ bind(&dont_adapt_arguments);
1541 __ jmp(Operand(edx));
1542}
1543
1544
Ben Murdochb0fe1622011-05-05 13:52:32 +01001545void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) {
Ben Murdoch8b112d22011-06-08 16:22:53 +01001546 CpuFeatures::TryForceFeatureScope scope(SSE2);
1547 if (!CpuFeatures::IsSupported(SSE2)) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01001548 __ Abort("Unreachable code: Cannot optimize without SSE2 support.");
1549 return;
1550 }
1551
1552 // Get the loop depth of the stack guard check. This is recorded in
1553 // a test(eax, depth) instruction right after the call.
1554 Label stack_check;
1555 __ mov(ebx, Operand(esp, 0)); // return address
1556 if (FLAG_debug_code) {
1557 __ cmpb(Operand(ebx, 0), Assembler::kTestAlByte);
1558 __ Assert(equal, "test eax instruction not found after loop stack check");
1559 }
1560 __ movzx_b(ebx, Operand(ebx, 1)); // depth
1561
1562 // Get the loop nesting level at which we allow OSR from the
1563 // unoptimized code and check if we want to do OSR yet. If not we
1564 // should perform a stack guard check so we can get interrupts while
1565 // waiting for on-stack replacement.
1566 __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
1567 __ mov(ecx, FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset));
1568 __ mov(ecx, FieldOperand(ecx, SharedFunctionInfo::kCodeOffset));
1569 __ cmpb(ebx, FieldOperand(ecx, Code::kAllowOSRAtLoopNestingLevelOffset));
1570 __ j(greater, &stack_check);
1571
1572 // Pass the function to optimize as the argument to the on-stack
1573 // replacement runtime function.
1574 __ EnterInternalFrame();
1575 __ push(eax);
1576 __ CallRuntime(Runtime::kCompileForOnStackReplacement, 1);
1577 __ LeaveInternalFrame();
1578
1579 // If the result was -1 it means that we couldn't optimize the
1580 // function. Just return and continue in the unoptimized version.
Ben Murdoch257744e2011-11-30 15:57:28 +00001581 Label skip;
Ben Murdochb0fe1622011-05-05 13:52:32 +01001582 __ cmp(Operand(eax), Immediate(Smi::FromInt(-1)));
Ben Murdoch257744e2011-11-30 15:57:28 +00001583 __ j(not_equal, &skip, Label::kNear);
Ben Murdochb0fe1622011-05-05 13:52:32 +01001584 __ ret(0);
1585
1586 // If we decide not to perform on-stack replacement we perform a
1587 // stack guard check to enable interrupts.
1588 __ bind(&stack_check);
Ben Murdoch257744e2011-11-30 15:57:28 +00001589 Label ok;
Ben Murdochb0fe1622011-05-05 13:52:32 +01001590 ExternalReference stack_limit =
Steve Block44f0eee2011-05-26 01:26:41 +01001591 ExternalReference::address_of_stack_limit(masm->isolate());
Ben Murdochb0fe1622011-05-05 13:52:32 +01001592 __ cmp(esp, Operand::StaticVariable(stack_limit));
Ben Murdoch257744e2011-11-30 15:57:28 +00001593 __ j(above_equal, &ok, Label::kNear);
Ben Murdochb0fe1622011-05-05 13:52:32 +01001594 StackCheckStub stub;
1595 __ TailCallStub(&stub);
1596 __ Abort("Unreachable code: returned from tail call.");
1597 __ bind(&ok);
1598 __ ret(0);
1599
1600 __ bind(&skip);
1601 // Untag the AST id and push it on the stack.
1602 __ SmiUntag(eax);
1603 __ push(eax);
1604
1605 // Generate the code for doing the frame-to-frame translation using
1606 // the deoptimizer infrastructure.
1607 Deoptimizer::EntryGenerator generator(masm, Deoptimizer::OSR);
1608 generator.Generate();
1609}
1610
1611
Steve Blocka7e24c12009-10-30 11:49:00 +00001612#undef __
Steve Block44f0eee2011-05-26 01:26:41 +01001613}
1614} // namespace v8::internal
Leon Clarkef7060e22010-06-03 12:02:55 +01001615
1616#endif // V8_TARGET_ARCH_IA32