blob: 27b127d904b362a198fc6573fce2ddd96274735d [file] [log] [blame]
ager@chromium.org0ee099b2011-01-25 14:06:47 +00001// Copyright 2011 the V8 project authors. All rights reserved.
ricow@chromium.org65fae842010-08-25 15:26:24 +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#if defined(V8_TARGET_ARCH_IA32)
31
32#include "bootstrapper.h"
vegorov@chromium.org7304bca2011-05-16 12:14:13 +000033#include "code-stubs.h"
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000034#include "isolate.h"
vegorov@chromium.org7304bca2011-05-16 12:14:13 +000035#include "jsregexp.h"
ricow@chromium.org65fae842010-08-25 15:26:24 +000036#include "regexp-macro-assembler.h"
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +000037#include "stub-cache.h"
erikcorry0ad885c2011-11-21 13:51:57 +000038#include "codegen.h"
ricow@chromium.org65fae842010-08-25 15:26:24 +000039
40namespace v8 {
41namespace internal {
42
43#define __ ACCESS_MASM(masm)
whesse@chromium.org7a392b32011-01-31 11:30:36 +000044
45void ToNumberStub::Generate(MacroAssembler* masm) {
46 // The ToNumber stub takes one argument in eax.
karlklose@chromium.org83a47282011-05-11 11:54:09 +000047 Label check_heap_number, call_builtin;
whesse@chromium.org7b260152011-06-20 15:33:18 +000048 __ JumpIfNotSmi(eax, &check_heap_number, Label::kNear);
whesse@chromium.org7a392b32011-01-31 11:30:36 +000049 __ ret(0);
50
51 __ bind(&check_heap_number);
52 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +000053 Factory* factory = masm->isolate()->factory();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +000054 __ cmp(ebx, Immediate(factory->heap_number_map()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +000055 __ j(not_equal, &call_builtin, Label::kNear);
whesse@chromium.org7a392b32011-01-31 11:30:36 +000056 __ ret(0);
57
58 __ bind(&call_builtin);
59 __ pop(ecx); // Pop return address.
60 __ push(eax);
61 __ push(ecx); // Push return address.
62 __ InvokeBuiltin(Builtins::TO_NUMBER, JUMP_FUNCTION);
63}
64
65
ricow@chromium.org65fae842010-08-25 15:26:24 +000066void FastNewClosureStub::Generate(MacroAssembler* masm) {
67 // Create a new closure from the given function info in new
68 // space. Set the context to the current context in esi.
69 Label gc;
70 __ AllocateInNewSpace(JSFunction::kSize, eax, ebx, ecx, &gc, TAG_OBJECT);
71
72 // Get the function info from the stack.
73 __ mov(edx, Operand(esp, 1 * kPointerSize));
74
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000075 int map_index = strict_mode_ == kStrictMode
76 ? Context::STRICT_MODE_FUNCTION_MAP_INDEX
77 : Context::FUNCTION_MAP_INDEX;
78
ricow@chromium.org65fae842010-08-25 15:26:24 +000079 // Compute the function map in the current global context and set that
80 // as the map of the allocated object.
81 __ mov(ecx, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
82 __ mov(ecx, FieldOperand(ecx, GlobalObject::kGlobalContextOffset));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000083 __ mov(ecx, Operand(ecx, Context::SlotOffset(map_index)));
ricow@chromium.org65fae842010-08-25 15:26:24 +000084 __ mov(FieldOperand(eax, JSObject::kMapOffset), ecx);
85
86 // Initialize the rest of the function. We don't have to update the
87 // write barrier because the allocated object is in new space.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +000088 Factory* factory = masm->isolate()->factory();
89 __ mov(ebx, Immediate(factory->empty_fixed_array()));
ricow@chromium.org65fae842010-08-25 15:26:24 +000090 __ mov(FieldOperand(eax, JSObject::kPropertiesOffset), ebx);
91 __ mov(FieldOperand(eax, JSObject::kElementsOffset), ebx);
92 __ mov(FieldOperand(eax, JSFunction::kPrototypeOrInitialMapOffset),
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +000093 Immediate(factory->the_hole_value()));
ricow@chromium.org65fae842010-08-25 15:26:24 +000094 __ mov(FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset), edx);
95 __ mov(FieldOperand(eax, JSFunction::kContextOffset), esi);
96 __ mov(FieldOperand(eax, JSFunction::kLiteralsOffset), ebx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000097 __ mov(FieldOperand(eax, JSFunction::kNextFunctionLinkOffset),
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +000098 Immediate(factory->undefined_value()));
ricow@chromium.org65fae842010-08-25 15:26:24 +000099
100 // Initialize the code pointer in the function to be the one
101 // found in the shared function info object.
102 __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
103 __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
104 __ mov(FieldOperand(eax, JSFunction::kCodeEntryOffset), edx);
105
106 // Return and remove the on-stack parameter.
107 __ ret(1 * kPointerSize);
108
109 // Create a new closure through the slower runtime call.
110 __ bind(&gc);
111 __ pop(ecx); // Temporarily remove return address.
112 __ pop(edx);
113 __ push(esi);
114 __ push(edx);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000115 __ push(Immediate(factory->false_value()));
ricow@chromium.org65fae842010-08-25 15:26:24 +0000116 __ push(ecx); // Restore return address.
vegorov@chromium.org21b5e952010-11-23 10:24:40 +0000117 __ TailCallRuntime(Runtime::kNewClosure, 3, 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000118}
119
120
121void FastNewContextStub::Generate(MacroAssembler* masm) {
122 // Try to allocate the context in new space.
123 Label gc;
ager@chromium.org0ee099b2011-01-25 14:06:47 +0000124 int length = slots_ + Context::MIN_CONTEXT_SLOTS;
125 __ AllocateInNewSpace((length * kPointerSize) + FixedArray::kHeaderSize,
ricow@chromium.org65fae842010-08-25 15:26:24 +0000126 eax, ebx, ecx, &gc, TAG_OBJECT);
127
128 // Get the function from the stack.
129 __ mov(ecx, Operand(esp, 1 * kPointerSize));
130
131 // Setup the object header.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000132 Factory* factory = masm->isolate()->factory();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000133 __ mov(FieldOperand(eax, HeapObject::kMapOffset),
134 factory->function_context_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +0000135 __ mov(FieldOperand(eax, Context::kLengthOffset),
ager@chromium.org0ee099b2011-01-25 14:06:47 +0000136 Immediate(Smi::FromInt(length)));
ricow@chromium.org65fae842010-08-25 15:26:24 +0000137
138 // Setup the fixed slots.
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000139 __ Set(ebx, Immediate(0)); // Set to NULL.
ricow@chromium.org65fae842010-08-25 15:26:24 +0000140 __ mov(Operand(eax, Context::SlotOffset(Context::CLOSURE_INDEX)), ecx);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000141 __ mov(Operand(eax, Context::SlotOffset(Context::PREVIOUS_INDEX)), esi);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000142 __ mov(Operand(eax, Context::SlotOffset(Context::EXTENSION_INDEX)), ebx);
143
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000144 // Copy the global object from the previous context.
145 __ mov(ebx, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
ricow@chromium.org65fae842010-08-25 15:26:24 +0000146 __ mov(Operand(eax, Context::SlotOffset(Context::GLOBAL_INDEX)), ebx);
147
148 // Initialize the rest of the slots to undefined.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000149 __ mov(ebx, factory->undefined_value());
ager@chromium.org0ee099b2011-01-25 14:06:47 +0000150 for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
ricow@chromium.org65fae842010-08-25 15:26:24 +0000151 __ mov(Operand(eax, Context::SlotOffset(i)), ebx);
152 }
153
154 // Return and remove the on-stack parameter.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000155 __ mov(esi, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000156 __ ret(1 * kPointerSize);
157
158 // Need to collect. Call into runtime system.
159 __ bind(&gc);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000160 __ TailCallRuntime(Runtime::kNewFunctionContext, 1, 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000161}
162
163
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000164void FastNewBlockContextStub::Generate(MacroAssembler* masm) {
165 // Stack layout on entry:
166 //
167 // [esp + (1 * kPointerSize)]: function
168 // [esp + (2 * kPointerSize)]: serialized scope info
169
170 // Try to allocate the context in new space.
171 Label gc;
172 int length = slots_ + Context::MIN_CONTEXT_SLOTS;
173 __ AllocateInNewSpace(FixedArray::SizeFor(length),
174 eax, ebx, ecx, &gc, TAG_OBJECT);
175
176 // Get the function or sentinel from the stack.
177 __ mov(ecx, Operand(esp, 1 * kPointerSize));
178
179 // Get the serialized scope info from the stack.
180 __ mov(ebx, Operand(esp, 2 * kPointerSize));
181
182 // Setup the object header.
183 Factory* factory = masm->isolate()->factory();
184 __ mov(FieldOperand(eax, HeapObject::kMapOffset),
185 factory->block_context_map());
186 __ mov(FieldOperand(eax, Context::kLengthOffset),
187 Immediate(Smi::FromInt(length)));
188
189 // If this block context is nested in the global context we get a smi
190 // sentinel instead of a function. The block context should get the
191 // canonical empty function of the global context as its closure which
192 // we still have to look up.
193 Label after_sentinel;
194 __ JumpIfNotSmi(ecx, &after_sentinel, Label::kNear);
195 if (FLAG_debug_code) {
196 const char* message = "Expected 0 as a Smi sentinel";
197 __ cmp(ecx, 0);
198 __ Assert(equal, message);
199 }
200 __ mov(ecx, GlobalObjectOperand());
201 __ mov(ecx, FieldOperand(ecx, GlobalObject::kGlobalContextOffset));
202 __ mov(ecx, ContextOperand(ecx, Context::CLOSURE_INDEX));
203 __ bind(&after_sentinel);
204
205 // Setup the fixed slots.
206 __ mov(ContextOperand(eax, Context::CLOSURE_INDEX), ecx);
207 __ mov(ContextOperand(eax, Context::PREVIOUS_INDEX), esi);
208 __ mov(ContextOperand(eax, Context::EXTENSION_INDEX), ebx);
209
210 // Copy the global object from the previous context.
211 __ mov(ebx, ContextOperand(esi, Context::GLOBAL_INDEX));
212 __ mov(ContextOperand(eax, Context::GLOBAL_INDEX), ebx);
213
214 // Initialize the rest of the slots to the hole value.
215 if (slots_ == 1) {
216 __ mov(ContextOperand(eax, Context::MIN_CONTEXT_SLOTS),
217 factory->the_hole_value());
218 } else {
219 __ mov(ebx, factory->the_hole_value());
220 for (int i = 0; i < slots_; i++) {
221 __ mov(ContextOperand(eax, i + Context::MIN_CONTEXT_SLOTS), ebx);
222 }
223 }
224
225 // Return and remove the on-stack parameters.
226 __ mov(esi, eax);
227 __ ret(2 * kPointerSize);
228
229 // Need to collect. Call into runtime system.
230 __ bind(&gc);
231 __ TailCallRuntime(Runtime::kPushBlockContext, 2, 1);
232}
233
234
erikcorry0ad885c2011-11-21 13:51:57 +0000235static void GenerateFastCloneShallowArrayCommon(
236 MacroAssembler* masm,
237 int length,
238 FastCloneShallowArrayStub::Mode mode,
239 Label* fail) {
240 // Registers on entry:
ricow@chromium.org65fae842010-08-25 15:26:24 +0000241 //
erikcorry0ad885c2011-11-21 13:51:57 +0000242 // ecx: boilerplate literal array.
243 ASSERT(mode != FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000244
245 // All sizes here are multiples of kPointerSize.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +0000246 int elements_size = 0;
erikcorry0ad885c2011-11-21 13:51:57 +0000247 if (length > 0) {
248 elements_size = mode == FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS
249 ? FixedDoubleArray::SizeFor(length)
250 : FixedArray::SizeFor(length);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +0000251 }
ricow@chromium.org65fae842010-08-25 15:26:24 +0000252 int size = JSArray::kSize + elements_size;
253
ricow@chromium.org65fae842010-08-25 15:26:24 +0000254 // Allocate both the JS array and the elements array in one big
255 // allocation. This avoids multiple limit checks.
erikcorry0ad885c2011-11-21 13:51:57 +0000256 __ AllocateInNewSpace(size, eax, ebx, edx, fail, TAG_OBJECT);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000257
258 // Copy the JS array part.
259 for (int i = 0; i < JSArray::kSize; i += kPointerSize) {
erikcorry0ad885c2011-11-21 13:51:57 +0000260 if ((i != JSArray::kElementsOffset) || (length == 0)) {
ricow@chromium.org65fae842010-08-25 15:26:24 +0000261 __ mov(ebx, FieldOperand(ecx, i));
262 __ mov(FieldOperand(eax, i), ebx);
263 }
264 }
265
erikcorry0ad885c2011-11-21 13:51:57 +0000266 if (length > 0) {
ricow@chromium.org65fae842010-08-25 15:26:24 +0000267 // Get hold of the elements array of the boilerplate and setup the
268 // elements pointer in the resulting object.
269 __ mov(ecx, FieldOperand(ecx, JSArray::kElementsOffset));
270 __ lea(edx, Operand(eax, JSArray::kSize));
271 __ mov(FieldOperand(eax, JSArray::kElementsOffset), edx);
272
273 // Copy the elements array.
erikcorry0ad885c2011-11-21 13:51:57 +0000274 if (mode == FastCloneShallowArrayStub::CLONE_ELEMENTS) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +0000275 for (int i = 0; i < elements_size; i += kPointerSize) {
276 __ mov(ebx, FieldOperand(ecx, i));
277 __ mov(FieldOperand(edx, i), ebx);
278 }
279 } else {
erikcorry0ad885c2011-11-21 13:51:57 +0000280 ASSERT(mode == FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +0000281 int i;
282 for (i = 0; i < FixedDoubleArray::kHeaderSize; i += kPointerSize) {
283 __ mov(ebx, FieldOperand(ecx, i));
284 __ mov(FieldOperand(edx, i), ebx);
285 }
286 while (i < elements_size) {
287 __ fld_d(FieldOperand(ecx, i));
288 __ fstp_d(FieldOperand(edx, i));
289 i += kDoubleSize;
290 }
291 ASSERT(i == elements_size);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000292 }
293 }
erikcorry0ad885c2011-11-21 13:51:57 +0000294}
ricow@chromium.org65fae842010-08-25 15:26:24 +0000295
erikcorry0ad885c2011-11-21 13:51:57 +0000296
297void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) {
298 // Stack layout on entry:
299 //
300 // [esp + kPointerSize]: constant elements.
301 // [esp + (2 * kPointerSize)]: literal index.
302 // [esp + (3 * kPointerSize)]: literals array.
303
304 // Load boilerplate object into ecx and check if we need to create a
305 // boilerplate.
306 __ mov(ecx, Operand(esp, 3 * kPointerSize));
307 __ mov(eax, Operand(esp, 2 * kPointerSize));
308 STATIC_ASSERT(kPointerSize == 4);
309 STATIC_ASSERT(kSmiTagSize == 1);
310 STATIC_ASSERT(kSmiTag == 0);
311 __ mov(ecx, FieldOperand(ecx, eax, times_half_pointer_size,
312 FixedArray::kHeaderSize));
313 Factory* factory = masm->isolate()->factory();
314 __ cmp(ecx, factory->undefined_value());
315 Label slow_case;
316 __ j(equal, &slow_case);
317
318 FastCloneShallowArrayStub::Mode mode = mode_;
319 // ecx is boilerplate object.
320 if (mode == CLONE_ANY_ELEMENTS) {
321 Label double_elements, check_fast_elements;
322 __ mov(ebx, FieldOperand(ecx, JSArray::kElementsOffset));
323 __ CheckMap(ebx, factory->fixed_cow_array_map(),
324 &check_fast_elements, DONT_DO_SMI_CHECK);
325 GenerateFastCloneShallowArrayCommon(masm, 0,
326 COPY_ON_WRITE_ELEMENTS, &slow_case);
327 __ ret(3 * kPointerSize);
328
329 __ bind(&check_fast_elements);
330 __ CheckMap(ebx, factory->fixed_array_map(),
331 &double_elements, DONT_DO_SMI_CHECK);
332 GenerateFastCloneShallowArrayCommon(masm, length_,
333 CLONE_ELEMENTS, &slow_case);
334 __ ret(3 * kPointerSize);
335
336 __ bind(&double_elements);
337 mode = CLONE_DOUBLE_ELEMENTS;
338 // Fall through to generate the code to handle double elements.
339 }
340
341 if (FLAG_debug_code) {
342 const char* message;
343 Handle<Map> expected_map;
344 if (mode == CLONE_ELEMENTS) {
345 message = "Expected (writable) fixed array";
346 expected_map = factory->fixed_array_map();
347 } else if (mode == CLONE_DOUBLE_ELEMENTS) {
348 message = "Expected (writable) fixed double array";
349 expected_map = factory->fixed_double_array_map();
350 } else {
351 ASSERT(mode == COPY_ON_WRITE_ELEMENTS);
352 message = "Expected copy-on-write fixed array";
353 expected_map = factory->fixed_cow_array_map();
354 }
355 __ push(ecx);
356 __ mov(ecx, FieldOperand(ecx, JSArray::kElementsOffset));
357 __ cmp(FieldOperand(ecx, HeapObject::kMapOffset), expected_map);
358 __ Assert(equal, message);
359 __ pop(ecx);
360 }
361
362 GenerateFastCloneShallowArrayCommon(masm, length_, mode, &slow_case);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000363 // Return and remove the on-stack parameters.
364 __ ret(3 * kPointerSize);
365
366 __ bind(&slow_case);
367 __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1);
368}
369
370
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000371// The stub expects its argument on the stack and returns its result in tos_:
372// zero for false, and a non-zero value for true.
ricow@chromium.org65fae842010-08-25 15:26:24 +0000373void ToBooleanStub::Generate(MacroAssembler* masm) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000374 // This stub overrides SometimesSetsUpAFrame() to return false. That means
375 // we cannot call anything that could cause a GC from this stub.
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000376 Label patch;
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000377 Factory* factory = masm->isolate()->factory();
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000378 const Register argument = eax;
lrn@chromium.orgac2828d2011-06-23 06:29:21 +0000379 const Register map = edx;
380
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000381 if (!types_.IsEmpty()) {
382 __ mov(argument, Operand(esp, 1 * kPointerSize));
383 }
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000384
385 // undefined -> false
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000386 CheckOddball(masm, UNDEFINED, Heap::kUndefinedValueRootIndex, false);
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000387
388 // Boolean -> its value
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000389 CheckOddball(masm, BOOLEAN, Heap::kFalseValueRootIndex, false);
390 CheckOddball(masm, BOOLEAN, Heap::kTrueValueRootIndex, true);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000391
lrn@chromium.orgac2828d2011-06-23 06:29:21 +0000392 // 'null' -> false.
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000393 CheckOddball(masm, NULL_TYPE, Heap::kNullValueRootIndex, false);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000394
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000395 if (types_.Contains(SMI)) {
396 // Smis: 0 -> false, all other -> true
397 Label not_smi;
398 __ JumpIfNotSmi(argument, &not_smi, Label::kNear);
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000399 // argument contains the correct return value already.
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000400 if (!tos_.is(argument)) {
401 __ mov(tos_, argument);
402 }
403 __ ret(1 * kPointerSize);
404 __ bind(&not_smi);
vegorov@chromium.org7943d462011-08-01 11:41:52 +0000405 } else if (types_.NeedsMap()) {
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000406 // If we need a map later and have a Smi -> patch.
407 __ JumpIfSmi(argument, &patch, Label::kNear);
408 }
ricow@chromium.org65fae842010-08-25 15:26:24 +0000409
vegorov@chromium.org7943d462011-08-01 11:41:52 +0000410 if (types_.NeedsMap()) {
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000411 __ mov(map, FieldOperand(argument, HeapObject::kMapOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +0000412
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000413 if (types_.CanBeUndetectable()) {
414 __ test_b(FieldOperand(map, Map::kBitFieldOffset),
415 1 << Map::kIsUndetectable);
416 // Undetectable -> false.
417 Label not_undetectable;
418 __ j(zero, &not_undetectable, Label::kNear);
419 __ Set(tos_, Immediate(0));
420 __ ret(1 * kPointerSize);
421 __ bind(&not_undetectable);
422 }
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000423 }
ricow@chromium.org65fae842010-08-25 15:26:24 +0000424
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000425 if (types_.Contains(SPEC_OBJECT)) {
426 // spec object -> true.
427 Label not_js_object;
428 __ CmpInstanceType(map, FIRST_SPEC_OBJECT_TYPE);
429 __ j(below, &not_js_object, Label::kNear);
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000430 // argument contains the correct return value already.
431 if (!tos_.is(argument)) {
432 __ Set(tos_, Immediate(1));
433 }
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000434 __ ret(1 * kPointerSize);
435 __ bind(&not_js_object);
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000436 }
ricow@chromium.org65fae842010-08-25 15:26:24 +0000437
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000438 if (types_.Contains(STRING)) {
439 // String value -> false iff empty.
440 Label not_string;
441 __ CmpInstanceType(map, FIRST_NONSTRING_TYPE);
442 __ j(above_equal, &not_string, Label::kNear);
443 __ mov(tos_, FieldOperand(argument, String::kLengthOffset));
444 __ ret(1 * kPointerSize); // the string length is OK as the return value
445 __ bind(&not_string);
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000446 }
447
448 if (types_.Contains(HEAP_NUMBER)) {
449 // heap number -> false iff +0, -0, or NaN.
450 Label not_heap_number, false_result;
451 __ cmp(map, factory->heap_number_map());
452 __ j(not_equal, &not_heap_number, Label::kNear);
453 __ fldz();
454 __ fld_d(FieldOperand(argument, HeapNumber::kValueOffset));
455 __ FCmp();
456 __ j(zero, &false_result, Label::kNear);
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000457 // argument contains the correct return value already.
458 if (!tos_.is(argument)) {
459 __ Set(tos_, Immediate(1));
460 }
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000461 __ ret(1 * kPointerSize);
462 __ bind(&false_result);
463 __ Set(tos_, Immediate(0));
464 __ ret(1 * kPointerSize);
465 __ bind(&not_heap_number);
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000466 }
467
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000468 __ bind(&patch);
469 GenerateTypeTransition(masm);
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000470}
471
472
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000473void StoreBufferOverflowStub::Generate(MacroAssembler* masm) {
474 // We don't allow a GC during a store buffer overflow so there is no need to
475 // store the registers in any particular way, but we do have to store and
476 // restore them.
477 __ pushad();
478 if (save_doubles_ == kSaveFPRegs) {
479 CpuFeatures::Scope scope(SSE2);
480 __ sub(esp, Immediate(kDoubleSize * XMMRegister::kNumRegisters));
481 for (int i = 0; i < XMMRegister::kNumRegisters; i++) {
482 XMMRegister reg = XMMRegister::from_code(i);
483 __ movdbl(Operand(esp, i * kDoubleSize), reg);
484 }
485 }
486 const int argument_count = 1;
487
488 AllowExternalCallThatCantCauseGC scope(masm);
489 __ PrepareCallCFunction(argument_count, ecx);
490 __ mov(Operand(esp, 0 * kPointerSize),
491 Immediate(ExternalReference::isolate_address()));
492 __ CallCFunction(
493 ExternalReference::store_buffer_overflow_function(masm->isolate()),
494 argument_count);
495 if (save_doubles_ == kSaveFPRegs) {
496 CpuFeatures::Scope scope(SSE2);
497 for (int i = 0; i < XMMRegister::kNumRegisters; i++) {
498 XMMRegister reg = XMMRegister::from_code(i);
499 __ movdbl(reg, Operand(esp, i * kDoubleSize));
500 }
501 __ add(esp, Immediate(kDoubleSize * XMMRegister::kNumRegisters));
502 }
503 __ popad();
504 __ ret(0);
505}
506
507
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000508void ToBooleanStub::CheckOddball(MacroAssembler* masm,
509 Type type,
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +0000510 Heap::RootListIndex value,
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000511 bool result) {
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000512 const Register argument = eax;
513 if (types_.Contains(type)) {
514 // If we see an expected oddball, return its ToBoolean value tos_.
515 Label different_value;
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +0000516 __ CompareRoot(argument, value);
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000517 __ j(not_equal, &different_value, Label::kNear);
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000518 if (!result) {
519 // If we have to return zero, there is no way around clearing tos_.
520 __ Set(tos_, Immediate(0));
521 } else if (!tos_.is(argument)) {
522 // If we have to return non-zero, we can re-use the argument if it is the
523 // same register as the result, because we never see Smi-zero here.
524 __ Set(tos_, Immediate(1));
525 }
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000526 __ ret(1 * kPointerSize);
527 __ bind(&different_value);
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000528 }
529}
530
531
532void ToBooleanStub::GenerateTypeTransition(MacroAssembler* masm) {
533 __ pop(ecx); // Get return address, operand is now on top of stack.
534 __ push(Immediate(Smi::FromInt(tos_.code())));
535 __ push(Immediate(Smi::FromInt(types_.ToByte())));
536 __ push(ecx); // Push return address.
537 // Patch the caller to an appropriate specialized stub and return the
538 // operation result to the caller of the stub.
539 __ TailCallExternalReference(
540 ExternalReference(IC_Utility(IC::kToBoolean_Patch), masm->isolate()),
541 3,
542 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000543}
544
545
ricow@chromium.org65fae842010-08-25 15:26:24 +0000546class FloatingPointHelper : public AllStatic {
547 public:
ricow@chromium.org65fae842010-08-25 15:26:24 +0000548 enum ArgLocation {
549 ARGS_ON_STACK,
550 ARGS_IN_REGISTERS
551 };
552
553 // Code pattern for loading a floating point value. Input value must
554 // be either a smi or a heap number object (fp value). Requirements:
555 // operand in register number. Returns operand as floating point number
556 // on FPU stack.
557 static void LoadFloatOperand(MacroAssembler* masm, Register number);
558
559 // Code pattern for loading floating point values. Input values must
560 // be either smi or heap number objects (fp values). Requirements:
561 // operand_1 on TOS+1 or in edx, operand_2 on TOS+2 or in eax.
562 // Returns operands as floating point numbers on FPU stack.
563 static void LoadFloatOperands(MacroAssembler* masm,
564 Register scratch,
565 ArgLocation arg_location = ARGS_ON_STACK);
566
567 // Similar to LoadFloatOperand but assumes that both operands are smis.
568 // Expects operands in edx, eax.
569 static void LoadFloatSmis(MacroAssembler* masm, Register scratch);
570
571 // Test if operands are smi or number objects (fp). Requirements:
572 // operand_1 in eax, operand_2 in edx; falls through on float
573 // operands, jumps to the non_float label otherwise.
574 static void CheckFloatOperands(MacroAssembler* masm,
575 Label* non_float,
576 Register scratch);
577
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000578 // Checks that the two floating point numbers on top of the FPU stack
579 // have int32 values.
580 static void CheckFloatOperandsAreInt32(MacroAssembler* masm,
581 Label* non_int32);
582
ricow@chromium.org65fae842010-08-25 15:26:24 +0000583 // Takes the operands in edx and eax and loads them as integers in eax
584 // and ecx.
ricow@chromium.org65fae842010-08-25 15:26:24 +0000585 static void LoadUnknownsAsIntegers(MacroAssembler* masm,
586 bool use_sse3,
587 Label* operand_conversion_failure);
588
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000589 // Must only be called after LoadUnknownsAsIntegers. Assumes that the
590 // operands are pushed on the stack, and that their conversions to int32
591 // are in eax and ecx. Checks that the original numbers were in the int32
592 // range.
593 static void CheckLoadedIntegersWereInt32(MacroAssembler* masm,
594 bool use_sse3,
595 Label* not_int32);
596
597 // Assumes that operands are smis or heap numbers and loads them
598 // into xmm0 and xmm1. Operands are in edx and eax.
ricow@chromium.org65fae842010-08-25 15:26:24 +0000599 // Leaves operands unchanged.
600 static void LoadSSE2Operands(MacroAssembler* masm);
601
602 // Test if operands are numbers (smi or HeapNumber objects), and load
603 // them into xmm0 and xmm1 if they are. Jump to label not_numbers if
604 // either operand is not a number. Operands are in edx and eax.
605 // Leaves operands unchanged.
606 static void LoadSSE2Operands(MacroAssembler* masm, Label* not_numbers);
607
608 // Similar to LoadSSE2Operands but assumes that both operands are smis.
609 // Expects operands in edx, eax.
610 static void LoadSSE2Smis(MacroAssembler* masm, Register scratch);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000611
612 // Checks that the two floating point numbers loaded into xmm0 and xmm1
613 // have int32 values.
614 static void CheckSSE2OperandsAreInt32(MacroAssembler* masm,
615 Label* non_int32,
616 Register scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000617};
618
619
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000620// Get the integer part of a heap number. Surprisingly, all this bit twiddling
621// is faster than using the built-in instructions on floating point registers.
622// Trashes edi and ebx. Dest is ecx. Source cannot be ecx or one of the
623// trashed registers.
624static void IntegerConvert(MacroAssembler* masm,
625 Register source,
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000626 bool use_sse3,
627 Label* conversion_failure) {
628 ASSERT(!source.is(ecx) && !source.is(edi) && !source.is(ebx));
629 Label done, right_exponent, normal_exponent;
630 Register scratch = ebx;
631 Register scratch2 = edi;
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000632 // Get exponent word.
633 __ mov(scratch, FieldOperand(source, HeapNumber::kExponentOffset));
634 // Get exponent alone in scratch2.
635 __ mov(scratch2, scratch);
636 __ and_(scratch2, HeapNumber::kExponentMask);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000637 if (use_sse3) {
638 CpuFeatures::Scope scope(SSE3);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000639 // Check whether the exponent is too big for a 64 bit signed integer.
640 static const uint32_t kTooBigExponent =
641 (HeapNumber::kExponentBias + 63) << HeapNumber::kExponentShift;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000642 __ cmp(scratch2, Immediate(kTooBigExponent));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000643 __ j(greater_equal, conversion_failure);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000644 // Load x87 register with heap number.
645 __ fld_d(FieldOperand(source, HeapNumber::kValueOffset));
646 // Reserve space for 64 bit answer.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000647 __ sub(esp, Immediate(sizeof(uint64_t))); // Nolint.
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000648 // Do conversion, which cannot fail because we checked the exponent.
649 __ fisttp_d(Operand(esp, 0));
650 __ mov(ecx, Operand(esp, 0)); // Load low word of answer into ecx.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000651 __ add(esp, Immediate(sizeof(uint64_t))); // Nolint.
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000652 } else {
653 // Load ecx with zero. We use this either for the final shift or
654 // for the answer.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000655 __ xor_(ecx, ecx);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000656 // Check whether the exponent matches a 32 bit signed int that cannot be
657 // represented by a Smi. A non-smi 32 bit integer is 1.xxx * 2^30 so the
658 // exponent is 30 (biased). This is the exponent that we are fastest at and
659 // also the highest exponent we can handle here.
660 const uint32_t non_smi_exponent =
661 (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000662 __ cmp(scratch2, Immediate(non_smi_exponent));
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000663 // If we have a match of the int32-but-not-Smi exponent then skip some
664 // logic.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000665 __ j(equal, &right_exponent, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000666 // If the exponent is higher than that then go to slow case. This catches
667 // numbers that don't fit in a signed int32, infinities and NaNs.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000668 __ j(less, &normal_exponent, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000669
670 {
671 // Handle a big exponent. The only reason we have this code is that the
672 // >>> operator has a tendency to generate numbers with an exponent of 31.
673 const uint32_t big_non_smi_exponent =
674 (HeapNumber::kExponentBias + 31) << HeapNumber::kExponentShift;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000675 __ cmp(scratch2, Immediate(big_non_smi_exponent));
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000676 __ j(not_equal, conversion_failure);
677 // We have the big exponent, typically from >>>. This means the number is
678 // in the range 2^31 to 2^32 - 1. Get the top bits of the mantissa.
679 __ mov(scratch2, scratch);
680 __ and_(scratch2, HeapNumber::kMantissaMask);
681 // Put back the implicit 1.
682 __ or_(scratch2, 1 << HeapNumber::kExponentShift);
683 // Shift up the mantissa bits to take up the space the exponent used to
684 // take. We just orred in the implicit bit so that took care of one and
685 // we want to use the full unsigned range so we subtract 1 bit from the
686 // shift distance.
687 const int big_shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 1;
688 __ shl(scratch2, big_shift_distance);
689 // Get the second half of the double.
690 __ mov(ecx, FieldOperand(source, HeapNumber::kMantissaOffset));
691 // Shift down 21 bits to get the most significant 11 bits or the low
692 // mantissa word.
693 __ shr(ecx, 32 - big_shift_distance);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000694 __ or_(ecx, scratch2);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000695 // We have the answer in ecx, but we may need to negate it.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000696 __ test(scratch, scratch);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000697 __ j(positive, &done, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000698 __ neg(ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000699 __ jmp(&done, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000700 }
701
702 __ bind(&normal_exponent);
703 // Exponent word in scratch, exponent part of exponent word in scratch2.
704 // Zero in ecx.
705 // We know the exponent is smaller than 30 (biased). If it is less than
706 // 0 (biased) then the number is smaller in magnitude than 1.0 * 2^0, ie
707 // it rounds to zero.
708 const uint32_t zero_exponent =
709 (HeapNumber::kExponentBias + 0) << HeapNumber::kExponentShift;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000710 __ sub(scratch2, Immediate(zero_exponent));
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000711 // ecx already has a Smi zero.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000712 __ j(less, &done, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000713
714 // We have a shifted exponent between 0 and 30 in scratch2.
715 __ shr(scratch2, HeapNumber::kExponentShift);
716 __ mov(ecx, Immediate(30));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000717 __ sub(ecx, scratch2);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000718
719 __ bind(&right_exponent);
720 // Here ecx is the shift, scratch is the exponent word.
721 // Get the top bits of the mantissa.
722 __ and_(scratch, HeapNumber::kMantissaMask);
723 // Put back the implicit 1.
724 __ or_(scratch, 1 << HeapNumber::kExponentShift);
725 // Shift up the mantissa bits to take up the space the exponent used to
726 // take. We have kExponentShift + 1 significant bits int he low end of the
727 // word. Shift them to the top bits.
728 const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
729 __ shl(scratch, shift_distance);
730 // Get the second half of the double. For some exponents we don't
731 // actually need this because the bits get shifted out again, but
732 // it's probably slower to test than just to do it.
733 __ mov(scratch2, FieldOperand(source, HeapNumber::kMantissaOffset));
734 // Shift down 22 bits to get the most significant 10 bits or the low
735 // mantissa word.
736 __ shr(scratch2, 32 - shift_distance);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000737 __ or_(scratch2, scratch);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000738 // Move down according to the exponent.
739 __ shr_cl(scratch2);
740 // Now the unsigned answer is in scratch2. We need to move it to ecx and
741 // we may need to fix the sign.
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000742 Label negative;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000743 __ xor_(ecx, ecx);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000744 __ cmp(ecx, FieldOperand(source, HeapNumber::kExponentOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000745 __ j(greater, &negative, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000746 __ mov(ecx, scratch2);
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000747 __ jmp(&done, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000748 __ bind(&negative);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000749 __ sub(ecx, scratch2);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000750 __ bind(&done);
751 }
752}
753
754
whesse@chromium.org030d38e2011-07-13 13:23:34 +0000755void UnaryOpStub::PrintName(StringStream* stream) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000756 const char* op_name = Token::Name(op_);
757 const char* overwrite_name = NULL; // Make g++ happy.
758 switch (mode_) {
759 case UNARY_NO_OVERWRITE: overwrite_name = "Alloc"; break;
760 case UNARY_OVERWRITE: overwrite_name = "Overwrite"; break;
761 }
whesse@chromium.org030d38e2011-07-13 13:23:34 +0000762 stream->Add("UnaryOpStub_%s_%s_%s",
763 op_name,
764 overwrite_name,
765 UnaryOpIC::GetName(operand_type_));
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000766}
767
768
769// TODO(svenpanne): Use virtual functions instead of switch.
danno@chromium.org40cb8782011-05-25 07:58:50 +0000770void UnaryOpStub::Generate(MacroAssembler* masm) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000771 switch (operand_type_) {
danno@chromium.org40cb8782011-05-25 07:58:50 +0000772 case UnaryOpIC::UNINITIALIZED:
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000773 GenerateTypeTransition(masm);
774 break;
danno@chromium.org40cb8782011-05-25 07:58:50 +0000775 case UnaryOpIC::SMI:
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000776 GenerateSmiStub(masm);
777 break;
danno@chromium.org40cb8782011-05-25 07:58:50 +0000778 case UnaryOpIC::HEAP_NUMBER:
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000779 GenerateHeapNumberStub(masm);
780 break;
danno@chromium.org40cb8782011-05-25 07:58:50 +0000781 case UnaryOpIC::GENERIC:
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000782 GenerateGenericStub(masm);
783 break;
784 }
785}
786
787
danno@chromium.org40cb8782011-05-25 07:58:50 +0000788void UnaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000789 __ pop(ecx); // Save return address.
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000790
791 __ push(eax); // the operand
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000792 __ push(Immediate(Smi::FromInt(op_)));
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000793 __ push(Immediate(Smi::FromInt(mode_)));
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000794 __ push(Immediate(Smi::FromInt(operand_type_)));
795
796 __ push(ecx); // Push return address.
797
798 // Patch the caller to an appropriate specialized stub and return the
799 // operation result to the caller of the stub.
800 __ TailCallExternalReference(
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000801 ExternalReference(IC_Utility(IC::kUnaryOp_Patch), masm->isolate()), 4, 1);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000802}
803
804
805// TODO(svenpanne): Use virtual functions instead of switch.
danno@chromium.org40cb8782011-05-25 07:58:50 +0000806void UnaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000807 switch (op_) {
808 case Token::SUB:
809 GenerateSmiStubSub(masm);
810 break;
811 case Token::BIT_NOT:
812 GenerateSmiStubBitNot(masm);
813 break;
814 default:
815 UNREACHABLE();
816 }
817}
818
819
danno@chromium.org40cb8782011-05-25 07:58:50 +0000820void UnaryOpStub::GenerateSmiStubSub(MacroAssembler* masm) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000821 Label non_smi, undo, slow;
822 GenerateSmiCodeSub(masm, &non_smi, &undo, &slow,
823 Label::kNear, Label::kNear, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000824 __ bind(&undo);
825 GenerateSmiCodeUndo(masm);
826 __ bind(&non_smi);
827 __ bind(&slow);
828 GenerateTypeTransition(masm);
829}
830
831
danno@chromium.org40cb8782011-05-25 07:58:50 +0000832void UnaryOpStub::GenerateSmiStubBitNot(MacroAssembler* masm) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000833 Label non_smi;
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000834 GenerateSmiCodeBitNot(masm, &non_smi);
835 __ bind(&non_smi);
836 GenerateTypeTransition(masm);
837}
838
839
danno@chromium.org40cb8782011-05-25 07:58:50 +0000840void UnaryOpStub::GenerateSmiCodeSub(MacroAssembler* masm,
841 Label* non_smi,
842 Label* undo,
843 Label* slow,
844 Label::Distance non_smi_near,
845 Label::Distance undo_near,
846 Label::Distance slow_near) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000847 // Check whether the value is a smi.
whesse@chromium.org7b260152011-06-20 15:33:18 +0000848 __ JumpIfNotSmi(eax, non_smi, non_smi_near);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000849
850 // We can't handle -0 with smis, so use a type transition for that case.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000851 __ test(eax, eax);
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000852 __ j(zero, slow, slow_near);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000853
854 // Try optimistic subtraction '0 - value', saving operand in eax for undo.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000855 __ mov(edx, eax);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000856 __ Set(eax, Immediate(0));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000857 __ sub(eax, edx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000858 __ j(overflow, undo, undo_near);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000859 __ ret(0);
860}
861
862
danno@chromium.org40cb8782011-05-25 07:58:50 +0000863void UnaryOpStub::GenerateSmiCodeBitNot(
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000864 MacroAssembler* masm,
865 Label* non_smi,
866 Label::Distance non_smi_near) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000867 // Check whether the value is a smi.
whesse@chromium.org7b260152011-06-20 15:33:18 +0000868 __ JumpIfNotSmi(eax, non_smi, non_smi_near);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000869
870 // Flip bits and revert inverted smi-tag.
871 __ not_(eax);
872 __ and_(eax, ~kSmiTagMask);
873 __ ret(0);
874}
875
876
danno@chromium.org40cb8782011-05-25 07:58:50 +0000877void UnaryOpStub::GenerateSmiCodeUndo(MacroAssembler* masm) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000878 __ mov(eax, edx);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000879}
880
881
882// TODO(svenpanne): Use virtual functions instead of switch.
danno@chromium.org40cb8782011-05-25 07:58:50 +0000883void UnaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000884 switch (op_) {
885 case Token::SUB:
886 GenerateHeapNumberStubSub(masm);
887 break;
888 case Token::BIT_NOT:
889 GenerateHeapNumberStubBitNot(masm);
890 break;
891 default:
892 UNREACHABLE();
893 }
894}
895
896
danno@chromium.org40cb8782011-05-25 07:58:50 +0000897void UnaryOpStub::GenerateHeapNumberStubSub(MacroAssembler* masm) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000898 Label non_smi, undo, slow, call_builtin;
899 GenerateSmiCodeSub(masm, &non_smi, &undo, &call_builtin, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000900 __ bind(&non_smi);
901 GenerateHeapNumberCodeSub(masm, &slow);
902 __ bind(&undo);
903 GenerateSmiCodeUndo(masm);
904 __ bind(&slow);
905 GenerateTypeTransition(masm);
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000906 __ bind(&call_builtin);
907 GenerateGenericCodeFallback(masm);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000908}
909
910
danno@chromium.org40cb8782011-05-25 07:58:50 +0000911void UnaryOpStub::GenerateHeapNumberStubBitNot(
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000912 MacroAssembler* masm) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000913 Label non_smi, slow;
914 GenerateSmiCodeBitNot(masm, &non_smi, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000915 __ bind(&non_smi);
916 GenerateHeapNumberCodeBitNot(masm, &slow);
917 __ bind(&slow);
918 GenerateTypeTransition(masm);
919}
920
921
danno@chromium.org40cb8782011-05-25 07:58:50 +0000922void UnaryOpStub::GenerateHeapNumberCodeSub(MacroAssembler* masm,
923 Label* slow) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000924 __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
925 __ cmp(edx, masm->isolate()->factory()->heap_number_map());
926 __ j(not_equal, slow);
927
928 if (mode_ == UNARY_OVERWRITE) {
929 __ xor_(FieldOperand(eax, HeapNumber::kExponentOffset),
930 Immediate(HeapNumber::kSignMask)); // Flip sign.
931 } else {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000932 __ mov(edx, eax);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000933 // edx: operand
934
935 Label slow_allocate_heapnumber, heapnumber_allocated;
936 __ AllocateHeapNumber(eax, ebx, ecx, &slow_allocate_heapnumber);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000937 __ jmp(&heapnumber_allocated, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000938
939 __ bind(&slow_allocate_heapnumber);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000940 {
941 FrameScope scope(masm, StackFrame::INTERNAL);
942 __ push(edx);
943 __ CallRuntime(Runtime::kNumberAlloc, 0);
944 __ pop(edx);
945 }
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000946
947 __ bind(&heapnumber_allocated);
948 // eax: allocated 'empty' number
949 __ mov(ecx, FieldOperand(edx, HeapNumber::kExponentOffset));
950 __ xor_(ecx, HeapNumber::kSignMask); // Flip sign.
951 __ mov(FieldOperand(eax, HeapNumber::kExponentOffset), ecx);
952 __ mov(ecx, FieldOperand(edx, HeapNumber::kMantissaOffset));
953 __ mov(FieldOperand(eax, HeapNumber::kMantissaOffset), ecx);
954 }
955 __ ret(0);
956}
957
958
danno@chromium.org40cb8782011-05-25 07:58:50 +0000959void UnaryOpStub::GenerateHeapNumberCodeBitNot(MacroAssembler* masm,
960 Label* slow) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000961 __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
962 __ cmp(edx, masm->isolate()->factory()->heap_number_map());
963 __ j(not_equal, slow);
964
965 // Convert the heap number in eax to an untagged integer in ecx.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000966 IntegerConvert(masm, eax, CpuFeatures::IsSupported(SSE3), slow);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000967
968 // Do the bitwise operation and check if the result fits in a smi.
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000969 Label try_float;
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000970 __ not_(ecx);
971 __ cmp(ecx, 0xc0000000);
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000972 __ j(sign, &try_float, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000973
974 // Tag the result as a smi and we're done.
975 STATIC_ASSERT(kSmiTagSize == 1);
976 __ lea(eax, Operand(ecx, times_2, kSmiTag));
977 __ ret(0);
978
979 // Try to store the result in a heap number.
980 __ bind(&try_float);
981 if (mode_ == UNARY_NO_OVERWRITE) {
982 Label slow_allocate_heapnumber, heapnumber_allocated;
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +0000983 __ mov(ebx, eax);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000984 __ AllocateHeapNumber(eax, edx, edi, &slow_allocate_heapnumber);
985 __ jmp(&heapnumber_allocated);
986
987 __ bind(&slow_allocate_heapnumber);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000988 {
989 FrameScope scope(masm, StackFrame::INTERNAL);
990 // Push the original HeapNumber on the stack. The integer value can't
991 // be stored since it's untagged and not in the smi range (so we can't
992 // smi-tag it). We'll recalculate the value after the GC instead.
993 __ push(ebx);
994 __ CallRuntime(Runtime::kNumberAlloc, 0);
995 // New HeapNumber is in eax.
996 __ pop(edx);
997 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +0000998 // IntegerConvert uses ebx and edi as scratch registers.
999 // This conversion won't go slow-case.
1000 IntegerConvert(masm, edx, CpuFeatures::IsSupported(SSE3), slow);
1001 __ not_(ecx);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00001002
1003 __ bind(&heapnumber_allocated);
1004 }
1005 if (CpuFeatures::IsSupported(SSE2)) {
1006 CpuFeatures::Scope use_sse2(SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001007 __ cvtsi2sd(xmm0, ecx);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00001008 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1009 } else {
1010 __ push(ecx);
1011 __ fild_s(Operand(esp, 0));
1012 __ pop(ecx);
1013 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1014 }
1015 __ ret(0);
1016}
1017
1018
1019// TODO(svenpanne): Use virtual functions instead of switch.
danno@chromium.org40cb8782011-05-25 07:58:50 +00001020void UnaryOpStub::GenerateGenericStub(MacroAssembler* masm) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00001021 switch (op_) {
1022 case Token::SUB:
1023 GenerateGenericStubSub(masm);
1024 break;
1025 case Token::BIT_NOT:
1026 GenerateGenericStubBitNot(masm);
1027 break;
1028 default:
1029 UNREACHABLE();
1030 }
1031}
1032
1033
danno@chromium.org40cb8782011-05-25 07:58:50 +00001034void UnaryOpStub::GenerateGenericStubSub(MacroAssembler* masm) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001035 Label non_smi, undo, slow;
1036 GenerateSmiCodeSub(masm, &non_smi, &undo, &slow, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00001037 __ bind(&non_smi);
1038 GenerateHeapNumberCodeSub(masm, &slow);
1039 __ bind(&undo);
1040 GenerateSmiCodeUndo(masm);
1041 __ bind(&slow);
1042 GenerateGenericCodeFallback(masm);
1043}
1044
1045
danno@chromium.org40cb8782011-05-25 07:58:50 +00001046void UnaryOpStub::GenerateGenericStubBitNot(MacroAssembler* masm) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001047 Label non_smi, slow;
1048 GenerateSmiCodeBitNot(masm, &non_smi, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00001049 __ bind(&non_smi);
1050 GenerateHeapNumberCodeBitNot(masm, &slow);
1051 __ bind(&slow);
1052 GenerateGenericCodeFallback(masm);
1053}
1054
1055
danno@chromium.org40cb8782011-05-25 07:58:50 +00001056void UnaryOpStub::GenerateGenericCodeFallback(MacroAssembler* masm) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00001057 // Handle the slow case by jumping to the corresponding JavaScript builtin.
1058 __ pop(ecx); // pop return address.
1059 __ push(eax);
1060 __ push(ecx); // push return address
1061 switch (op_) {
1062 case Token::SUB:
1063 __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_FUNCTION);
1064 break;
1065 case Token::BIT_NOT:
1066 __ InvokeBuiltin(Builtins::BIT_NOT, JUMP_FUNCTION);
1067 break;
1068 default:
1069 UNREACHABLE();
1070 }
1071}
1072
1073
danno@chromium.org40cb8782011-05-25 07:58:50 +00001074void BinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001075 __ pop(ecx); // Save return address.
1076 __ push(edx);
1077 __ push(eax);
1078 // Left and right arguments are now on top.
1079 // Push this stub's key. Although the operation and the type info are
1080 // encoded into the key, the encoding is opaque, so push them too.
1081 __ push(Immediate(Smi::FromInt(MinorKey())));
1082 __ push(Immediate(Smi::FromInt(op_)));
1083 __ push(Immediate(Smi::FromInt(operands_type_)));
1084
1085 __ push(ecx); // Push return address.
1086
1087 // Patch the caller to an appropriate specialized stub and return the
1088 // operation result to the caller of the stub.
1089 __ TailCallExternalReference(
danno@chromium.org40cb8782011-05-25 07:58:50 +00001090 ExternalReference(IC_Utility(IC::kBinaryOp_Patch),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001091 masm->isolate()),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001092 5,
1093 1);
1094}
1095
1096
1097// Prepare for a type transition runtime call when the args are already on
1098// the stack, under the return address.
danno@chromium.org40cb8782011-05-25 07:58:50 +00001099void BinaryOpStub::GenerateTypeTransitionWithSavedArgs(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001100 __ pop(ecx); // Save return address.
1101 // Left and right arguments are already on top of the stack.
1102 // Push this stub's key. Although the operation and the type info are
1103 // encoded into the key, the encoding is opaque, so push them too.
1104 __ push(Immediate(Smi::FromInt(MinorKey())));
1105 __ push(Immediate(Smi::FromInt(op_)));
1106 __ push(Immediate(Smi::FromInt(operands_type_)));
1107
1108 __ push(ecx); // Push return address.
1109
1110 // Patch the caller to an appropriate specialized stub and return the
1111 // operation result to the caller of the stub.
1112 __ TailCallExternalReference(
danno@chromium.org40cb8782011-05-25 07:58:50 +00001113 ExternalReference(IC_Utility(IC::kBinaryOp_Patch),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001114 masm->isolate()),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001115 5,
1116 1);
1117}
1118
1119
danno@chromium.org40cb8782011-05-25 07:58:50 +00001120void BinaryOpStub::Generate(MacroAssembler* masm) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001121 // Explicitly allow generation of nested stubs. It is safe here because
1122 // generation code does not use any raw pointers.
1123 AllowStubCallsScope allow_stub_calls(masm, true);
1124
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001125 switch (operands_type_) {
danno@chromium.org40cb8782011-05-25 07:58:50 +00001126 case BinaryOpIC::UNINITIALIZED:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001127 GenerateTypeTransition(masm);
1128 break;
danno@chromium.org40cb8782011-05-25 07:58:50 +00001129 case BinaryOpIC::SMI:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001130 GenerateSmiStub(masm);
1131 break;
danno@chromium.org40cb8782011-05-25 07:58:50 +00001132 case BinaryOpIC::INT32:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001133 GenerateInt32Stub(masm);
1134 break;
danno@chromium.org40cb8782011-05-25 07:58:50 +00001135 case BinaryOpIC::HEAP_NUMBER:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001136 GenerateHeapNumberStub(masm);
1137 break;
danno@chromium.org40cb8782011-05-25 07:58:50 +00001138 case BinaryOpIC::ODDBALL:
lrn@chromium.org7516f052011-03-30 08:52:27 +00001139 GenerateOddballStub(masm);
1140 break;
danno@chromium.org40cb8782011-05-25 07:58:50 +00001141 case BinaryOpIC::BOTH_STRING:
danno@chromium.org160a7b02011-04-18 15:51:38 +00001142 GenerateBothStringStub(masm);
1143 break;
danno@chromium.org40cb8782011-05-25 07:58:50 +00001144 case BinaryOpIC::STRING:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001145 GenerateStringStub(masm);
1146 break;
danno@chromium.org40cb8782011-05-25 07:58:50 +00001147 case BinaryOpIC::GENERIC:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001148 GenerateGeneric(masm);
1149 break;
1150 default:
1151 UNREACHABLE();
1152 }
1153}
1154
1155
whesse@chromium.org030d38e2011-07-13 13:23:34 +00001156void BinaryOpStub::PrintName(StringStream* stream) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001157 const char* op_name = Token::Name(op_);
1158 const char* overwrite_name;
1159 switch (mode_) {
1160 case NO_OVERWRITE: overwrite_name = "Alloc"; break;
1161 case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break;
1162 case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break;
1163 default: overwrite_name = "UnknownOverwrite"; break;
1164 }
whesse@chromium.org030d38e2011-07-13 13:23:34 +00001165 stream->Add("BinaryOpStub_%s_%s_%s",
1166 op_name,
1167 overwrite_name,
1168 BinaryOpIC::GetName(operands_type_));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001169}
1170
1171
danno@chromium.org40cb8782011-05-25 07:58:50 +00001172void BinaryOpStub::GenerateSmiCode(
1173 MacroAssembler* masm,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001174 Label* slow,
1175 SmiCodeGenerateHeapNumberResults allow_heapnumber_results) {
1176 // 1. Move arguments into edx, eax except for DIV and MOD, which need the
1177 // dividend in eax and edx free for the division. Use eax, ebx for those.
1178 Comment load_comment(masm, "-- Load arguments");
1179 Register left = edx;
1180 Register right = eax;
1181 if (op_ == Token::DIV || op_ == Token::MOD) {
1182 left = eax;
1183 right = ebx;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001184 __ mov(ebx, eax);
1185 __ mov(eax, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001186 }
1187
1188
1189 // 2. Prepare the smi check of both operands by oring them together.
1190 Comment smi_check_comment(masm, "-- Smi check arguments");
1191 Label not_smis;
1192 Register combined = ecx;
1193 ASSERT(!left.is(combined) && !right.is(combined));
1194 switch (op_) {
1195 case Token::BIT_OR:
1196 // Perform the operation into eax and smi check the result. Preserve
1197 // eax in case the result is not a smi.
1198 ASSERT(!left.is(ecx) && !right.is(ecx));
1199 __ mov(ecx, right);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001200 __ or_(right, left); // Bitwise or is commutative.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001201 combined = right;
1202 break;
1203
1204 case Token::BIT_XOR:
1205 case Token::BIT_AND:
1206 case Token::ADD:
1207 case Token::SUB:
1208 case Token::MUL:
1209 case Token::DIV:
1210 case Token::MOD:
1211 __ mov(combined, right);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001212 __ or_(combined, left);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001213 break;
1214
1215 case Token::SHL:
1216 case Token::SAR:
1217 case Token::SHR:
1218 // Move the right operand into ecx for the shift operation, use eax
1219 // for the smi check register.
1220 ASSERT(!left.is(ecx) && !right.is(ecx));
1221 __ mov(ecx, right);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001222 __ or_(right, left);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001223 combined = right;
1224 break;
1225
1226 default:
1227 break;
1228 }
1229
1230 // 3. Perform the smi check of the operands.
1231 STATIC_ASSERT(kSmiTag == 0); // Adjust zero check if not the case.
whesse@chromium.org7b260152011-06-20 15:33:18 +00001232 __ JumpIfNotSmi(combined, &not_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001233
1234 // 4. Operands are both smis, perform the operation leaving the result in
1235 // eax and check the result if necessary.
1236 Comment perform_smi(masm, "-- Perform smi operation");
1237 Label use_fp_on_smis;
1238 switch (op_) {
1239 case Token::BIT_OR:
1240 // Nothing to do.
1241 break;
1242
1243 case Token::BIT_XOR:
1244 ASSERT(right.is(eax));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001245 __ xor_(right, left); // Bitwise xor is commutative.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001246 break;
1247
1248 case Token::BIT_AND:
1249 ASSERT(right.is(eax));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001250 __ and_(right, left); // Bitwise and is commutative.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001251 break;
1252
1253 case Token::SHL:
1254 // Remove tags from operands (but keep sign).
1255 __ SmiUntag(left);
1256 __ SmiUntag(ecx);
1257 // Perform the operation.
1258 __ shl_cl(left);
1259 // Check that the *signed* result fits in a smi.
1260 __ cmp(left, 0xc0000000);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00001261 __ j(sign, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001262 // Tag the result and store it in register eax.
1263 __ SmiTag(left);
1264 __ mov(eax, left);
1265 break;
1266
1267 case Token::SAR:
1268 // Remove tags from operands (but keep sign).
1269 __ SmiUntag(left);
1270 __ SmiUntag(ecx);
1271 // Perform the operation.
1272 __ sar_cl(left);
1273 // Tag the result and store it in register eax.
1274 __ SmiTag(left);
1275 __ mov(eax, left);
1276 break;
1277
1278 case Token::SHR:
1279 // Remove tags from operands (but keep sign).
1280 __ SmiUntag(left);
1281 __ SmiUntag(ecx);
1282 // Perform the operation.
1283 __ shr_cl(left);
1284 // Check that the *unsigned* result fits in a smi.
1285 // Neither of the two high-order bits can be set:
1286 // - 0x80000000: high bit would be lost when smi tagging.
1287 // - 0x40000000: this number would convert to negative when
1288 // Smi tagging these two cases can only happen with shifts
1289 // by 0 or 1 when handed a valid smi.
1290 __ test(left, Immediate(0xc0000000));
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001291 __ j(not_zero, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001292 // Tag the result and store it in register eax.
1293 __ SmiTag(left);
1294 __ mov(eax, left);
1295 break;
1296
1297 case Token::ADD:
1298 ASSERT(right.is(eax));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001299 __ add(right, left); // Addition is commutative.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00001300 __ j(overflow, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001301 break;
1302
1303 case Token::SUB:
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001304 __ sub(left, right);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00001305 __ j(overflow, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001306 __ mov(eax, left);
1307 break;
1308
1309 case Token::MUL:
1310 // If the smi tag is 0 we can just leave the tag on one operand.
1311 STATIC_ASSERT(kSmiTag == 0); // Adjust code below if not the case.
1312 // We can't revert the multiplication if the result is not a smi
1313 // so save the right operand.
1314 __ mov(ebx, right);
1315 // Remove tag from one of the operands (but keep sign).
1316 __ SmiUntag(right);
1317 // Do multiplication.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001318 __ imul(right, left); // Multiplication is commutative.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00001319 __ j(overflow, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001320 // Check for negative zero result. Use combined = left | right.
1321 __ NegativeZeroTest(right, combined, &use_fp_on_smis);
1322 break;
1323
1324 case Token::DIV:
1325 // We can't revert the division if the result is not a smi so
1326 // save the left operand.
1327 __ mov(edi, left);
1328 // Check for 0 divisor.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001329 __ test(right, right);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00001330 __ j(zero, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001331 // Sign extend left into edx:eax.
1332 ASSERT(left.is(eax));
1333 __ cdq();
1334 // Divide edx:eax by right.
1335 __ idiv(right);
1336 // Check for the corner case of dividing the most negative smi by
1337 // -1. We cannot use the overflow flag, since it is not set by idiv
1338 // instruction.
1339 STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
1340 __ cmp(eax, 0x40000000);
1341 __ j(equal, &use_fp_on_smis);
1342 // Check for negative zero result. Use combined = left | right.
1343 __ NegativeZeroTest(eax, combined, &use_fp_on_smis);
1344 // Check that the remainder is zero.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001345 __ test(edx, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001346 __ j(not_zero, &use_fp_on_smis);
1347 // Tag the result and store it in register eax.
1348 __ SmiTag(eax);
1349 break;
1350
1351 case Token::MOD:
1352 // Check for 0 divisor.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001353 __ test(right, right);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00001354 __ j(zero, &not_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001355
1356 // Sign extend left into edx:eax.
1357 ASSERT(left.is(eax));
1358 __ cdq();
1359 // Divide edx:eax by right.
1360 __ idiv(right);
1361 // Check for negative zero result. Use combined = left | right.
1362 __ NegativeZeroTest(edx, combined, slow);
1363 // Move remainder to register eax.
1364 __ mov(eax, edx);
1365 break;
1366
1367 default:
1368 UNREACHABLE();
1369 }
1370
1371 // 5. Emit return of result in eax. Some operations have registers pushed.
1372 switch (op_) {
1373 case Token::ADD:
1374 case Token::SUB:
1375 case Token::MUL:
1376 case Token::DIV:
1377 __ ret(0);
1378 break;
1379 case Token::MOD:
1380 case Token::BIT_OR:
1381 case Token::BIT_AND:
1382 case Token::BIT_XOR:
1383 case Token::SAR:
1384 case Token::SHL:
1385 case Token::SHR:
1386 __ ret(2 * kPointerSize);
1387 break;
1388 default:
1389 UNREACHABLE();
1390 }
1391
1392 // 6. For some operations emit inline code to perform floating point
1393 // operations on known smis (e.g., if the result of the operation
1394 // overflowed the smi range).
1395 if (allow_heapnumber_results == NO_HEAPNUMBER_RESULTS) {
1396 __ bind(&use_fp_on_smis);
1397 switch (op_) {
1398 // Undo the effects of some operations, and some register moves.
1399 case Token::SHL:
1400 // The arguments are saved on the stack, and only used from there.
1401 break;
1402 case Token::ADD:
1403 // Revert right = right + left.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001404 __ sub(right, left);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001405 break;
1406 case Token::SUB:
1407 // Revert left = left - right.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001408 __ add(left, right);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001409 break;
1410 case Token::MUL:
1411 // Right was clobbered but a copy is in ebx.
1412 __ mov(right, ebx);
1413 break;
1414 case Token::DIV:
1415 // Left was clobbered but a copy is in edi. Right is in ebx for
1416 // division. They should be in eax, ebx for jump to not_smi.
1417 __ mov(eax, edi);
1418 break;
1419 default:
1420 // No other operators jump to use_fp_on_smis.
1421 break;
1422 }
1423 __ jmp(&not_smis);
1424 } else {
1425 ASSERT(allow_heapnumber_results == ALLOW_HEAPNUMBER_RESULTS);
1426 switch (op_) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001427 case Token::SHL:
1428 case Token::SHR: {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001429 Comment perform_float(masm, "-- Perform float operation on smis");
1430 __ bind(&use_fp_on_smis);
1431 // Result we want is in left == edx, so we can put the allocated heap
1432 // number in eax.
1433 __ AllocateHeapNumber(eax, ecx, ebx, slow);
1434 // Store the result in the HeapNumber and return.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001435 // It's OK to overwrite the arguments on the stack because we
1436 // are about to return.
1437 if (op_ == Token::SHR) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001438 __ mov(Operand(esp, 1 * kPointerSize), left);
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001439 __ mov(Operand(esp, 2 * kPointerSize), Immediate(0));
1440 __ fild_d(Operand(esp, 1 * kPointerSize));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001441 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001442 } else {
1443 ASSERT_EQ(Token::SHL, op_);
1444 if (CpuFeatures::IsSupported(SSE2)) {
1445 CpuFeatures::Scope use_sse2(SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001446 __ cvtsi2sd(xmm0, left);
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001447 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1448 } else {
1449 __ mov(Operand(esp, 1 * kPointerSize), left);
1450 __ fild_s(Operand(esp, 1 * kPointerSize));
1451 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1452 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001453 }
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001454 __ ret(2 * kPointerSize);
1455 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001456 }
1457
1458 case Token::ADD:
1459 case Token::SUB:
1460 case Token::MUL:
1461 case Token::DIV: {
1462 Comment perform_float(masm, "-- Perform float operation on smis");
1463 __ bind(&use_fp_on_smis);
1464 // Restore arguments to edx, eax.
1465 switch (op_) {
1466 case Token::ADD:
1467 // Revert right = right + left.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001468 __ sub(right, left);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001469 break;
1470 case Token::SUB:
1471 // Revert left = left - right.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001472 __ add(left, right);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001473 break;
1474 case Token::MUL:
1475 // Right was clobbered but a copy is in ebx.
1476 __ mov(right, ebx);
1477 break;
1478 case Token::DIV:
1479 // Left was clobbered but a copy is in edi. Right is in ebx for
1480 // division.
1481 __ mov(edx, edi);
1482 __ mov(eax, right);
1483 break;
1484 default: UNREACHABLE();
1485 break;
1486 }
1487 __ AllocateHeapNumber(ecx, ebx, no_reg, slow);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001488 if (CpuFeatures::IsSupported(SSE2)) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001489 CpuFeatures::Scope use_sse2(SSE2);
1490 FloatingPointHelper::LoadSSE2Smis(masm, ebx);
1491 switch (op_) {
1492 case Token::ADD: __ addsd(xmm0, xmm1); break;
1493 case Token::SUB: __ subsd(xmm0, xmm1); break;
1494 case Token::MUL: __ mulsd(xmm0, xmm1); break;
1495 case Token::DIV: __ divsd(xmm0, xmm1); break;
1496 default: UNREACHABLE();
1497 }
1498 __ movdbl(FieldOperand(ecx, HeapNumber::kValueOffset), xmm0);
1499 } else { // SSE2 not available, use FPU.
1500 FloatingPointHelper::LoadFloatSmis(masm, ebx);
1501 switch (op_) {
1502 case Token::ADD: __ faddp(1); break;
1503 case Token::SUB: __ fsubp(1); break;
1504 case Token::MUL: __ fmulp(1); break;
1505 case Token::DIV: __ fdivp(1); break;
1506 default: UNREACHABLE();
1507 }
1508 __ fstp_d(FieldOperand(ecx, HeapNumber::kValueOffset));
1509 }
1510 __ mov(eax, ecx);
1511 __ ret(0);
1512 break;
1513 }
1514
1515 default:
1516 break;
1517 }
1518 }
1519
1520 // 7. Non-smi operands, fall out to the non-smi code with the operands in
1521 // edx and eax.
1522 Comment done_comment(masm, "-- Enter non-smi code");
1523 __ bind(&not_smis);
1524 switch (op_) {
1525 case Token::BIT_OR:
1526 case Token::SHL:
1527 case Token::SAR:
1528 case Token::SHR:
1529 // Right operand is saved in ecx and eax was destroyed by the smi
1530 // check.
1531 __ mov(eax, ecx);
1532 break;
1533
1534 case Token::DIV:
1535 case Token::MOD:
1536 // Operands are in eax, ebx at this point.
1537 __ mov(edx, eax);
1538 __ mov(eax, ebx);
1539 break;
1540
1541 default:
1542 break;
1543 }
1544}
1545
1546
danno@chromium.org40cb8782011-05-25 07:58:50 +00001547void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001548 Label call_runtime;
1549
1550 switch (op_) {
1551 case Token::ADD:
1552 case Token::SUB:
1553 case Token::MUL:
1554 case Token::DIV:
1555 break;
1556 case Token::MOD:
1557 case Token::BIT_OR:
1558 case Token::BIT_AND:
1559 case Token::BIT_XOR:
1560 case Token::SAR:
1561 case Token::SHL:
1562 case Token::SHR:
1563 GenerateRegisterArgsPush(masm);
1564 break;
1565 default:
1566 UNREACHABLE();
1567 }
1568
danno@chromium.org40cb8782011-05-25 07:58:50 +00001569 if (result_type_ == BinaryOpIC::UNINITIALIZED ||
1570 result_type_ == BinaryOpIC::SMI) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001571 GenerateSmiCode(masm, &call_runtime, NO_HEAPNUMBER_RESULTS);
1572 } else {
1573 GenerateSmiCode(masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS);
1574 }
1575 __ bind(&call_runtime);
1576 switch (op_) {
1577 case Token::ADD:
1578 case Token::SUB:
1579 case Token::MUL:
1580 case Token::DIV:
1581 GenerateTypeTransition(masm);
1582 break;
1583 case Token::MOD:
1584 case Token::BIT_OR:
1585 case Token::BIT_AND:
1586 case Token::BIT_XOR:
1587 case Token::SAR:
1588 case Token::SHL:
1589 case Token::SHR:
1590 GenerateTypeTransitionWithSavedArgs(masm);
1591 break;
1592 default:
1593 UNREACHABLE();
1594 }
1595}
1596
1597
danno@chromium.org40cb8782011-05-25 07:58:50 +00001598void BinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
1599 ASSERT(operands_type_ == BinaryOpIC::STRING);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001600 ASSERT(op_ == Token::ADD);
ager@chromium.org0ee099b2011-01-25 14:06:47 +00001601 // Try to add arguments as strings, otherwise, transition to the generic
danno@chromium.org40cb8782011-05-25 07:58:50 +00001602 // BinaryOpIC type.
ager@chromium.org0ee099b2011-01-25 14:06:47 +00001603 GenerateAddStrings(masm);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001604 GenerateTypeTransition(masm);
1605}
1606
1607
danno@chromium.org40cb8782011-05-25 07:58:50 +00001608void BinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00001609 Label call_runtime;
danno@chromium.org40cb8782011-05-25 07:58:50 +00001610 ASSERT(operands_type_ == BinaryOpIC::BOTH_STRING);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001611 ASSERT(op_ == Token::ADD);
1612 // If both arguments are strings, call the string add stub.
1613 // Otherwise, do a transition.
1614
1615 // Registers containing left and right operands respectively.
1616 Register left = edx;
1617 Register right = eax;
1618
1619 // Test if left operand is a string.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001620 __ JumpIfSmi(left, &call_runtime, Label::kNear);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001621 __ CmpObjectType(left, FIRST_NONSTRING_TYPE, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001622 __ j(above_equal, &call_runtime, Label::kNear);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001623
1624 // Test if right operand is a string.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001625 __ JumpIfSmi(right, &call_runtime, Label::kNear);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001626 __ CmpObjectType(right, FIRST_NONSTRING_TYPE, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001627 __ j(above_equal, &call_runtime, Label::kNear);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001628
1629 StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
1630 GenerateRegisterArgsPush(masm);
1631 __ TailCallStub(&string_add_stub);
1632
1633 __ bind(&call_runtime);
1634 GenerateTypeTransition(masm);
1635}
1636
1637
danno@chromium.org40cb8782011-05-25 07:58:50 +00001638void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001639 Label call_runtime;
danno@chromium.org40cb8782011-05-25 07:58:50 +00001640 ASSERT(operands_type_ == BinaryOpIC::INT32);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001641
1642 // Floating point case.
1643 switch (op_) {
1644 case Token::ADD:
1645 case Token::SUB:
1646 case Token::MUL:
1647 case Token::DIV: {
1648 Label not_floats;
1649 Label not_int32;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001650 if (CpuFeatures::IsSupported(SSE2)) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001651 CpuFeatures::Scope use_sse2(SSE2);
1652 FloatingPointHelper::LoadSSE2Operands(masm, &not_floats);
1653 FloatingPointHelper::CheckSSE2OperandsAreInt32(masm, &not_int32, ecx);
1654 switch (op_) {
1655 case Token::ADD: __ addsd(xmm0, xmm1); break;
1656 case Token::SUB: __ subsd(xmm0, xmm1); break;
1657 case Token::MUL: __ mulsd(xmm0, xmm1); break;
1658 case Token::DIV: __ divsd(xmm0, xmm1); break;
1659 default: UNREACHABLE();
1660 }
1661 // Check result type if it is currently Int32.
danno@chromium.org40cb8782011-05-25 07:58:50 +00001662 if (result_type_ <= BinaryOpIC::INT32) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001663 __ cvttsd2si(ecx, Operand(xmm0));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001664 __ cvtsi2sd(xmm2, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001665 __ ucomisd(xmm0, xmm2);
1666 __ j(not_zero, &not_int32);
1667 __ j(carry, &not_int32);
1668 }
1669 GenerateHeapResultAllocation(masm, &call_runtime);
1670 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1671 __ ret(0);
1672 } else { // SSE2 not available, use FPU.
1673 FloatingPointHelper::CheckFloatOperands(masm, &not_floats, ebx);
1674 FloatingPointHelper::LoadFloatOperands(
1675 masm,
1676 ecx,
1677 FloatingPointHelper::ARGS_IN_REGISTERS);
1678 FloatingPointHelper::CheckFloatOperandsAreInt32(masm, &not_int32);
1679 switch (op_) {
1680 case Token::ADD: __ faddp(1); break;
1681 case Token::SUB: __ fsubp(1); break;
1682 case Token::MUL: __ fmulp(1); break;
1683 case Token::DIV: __ fdivp(1); break;
1684 default: UNREACHABLE();
1685 }
1686 Label after_alloc_failure;
1687 GenerateHeapResultAllocation(masm, &after_alloc_failure);
1688 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1689 __ ret(0);
1690 __ bind(&after_alloc_failure);
1691 __ ffree();
1692 __ jmp(&call_runtime);
1693 }
1694
1695 __ bind(&not_floats);
1696 __ bind(&not_int32);
1697 GenerateTypeTransition(masm);
1698 break;
1699 }
1700
1701 case Token::MOD: {
1702 // For MOD we go directly to runtime in the non-smi case.
1703 break;
1704 }
1705 case Token::BIT_OR:
1706 case Token::BIT_AND:
1707 case Token::BIT_XOR:
1708 case Token::SAR:
1709 case Token::SHL:
1710 case Token::SHR: {
1711 GenerateRegisterArgsPush(masm);
1712 Label not_floats;
1713 Label not_int32;
1714 Label non_smi_result;
1715 /* {
1716 CpuFeatures::Scope use_sse2(SSE2);
1717 FloatingPointHelper::LoadSSE2Operands(masm, &not_floats);
1718 FloatingPointHelper::CheckSSE2OperandsAreInt32(masm, &not_int32, ecx);
1719 }*/
1720 FloatingPointHelper::LoadUnknownsAsIntegers(masm,
1721 use_sse3_,
1722 &not_floats);
1723 FloatingPointHelper::CheckLoadedIntegersWereInt32(masm, use_sse3_,
1724 &not_int32);
1725 switch (op_) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001726 case Token::BIT_OR: __ or_(eax, ecx); break;
1727 case Token::BIT_AND: __ and_(eax, ecx); break;
1728 case Token::BIT_XOR: __ xor_(eax, ecx); break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001729 case Token::SAR: __ sar_cl(eax); break;
1730 case Token::SHL: __ shl_cl(eax); break;
1731 case Token::SHR: __ shr_cl(eax); break;
1732 default: UNREACHABLE();
1733 }
1734 if (op_ == Token::SHR) {
1735 // Check if result is non-negative and fits in a smi.
1736 __ test(eax, Immediate(0xc0000000));
1737 __ j(not_zero, &call_runtime);
1738 } else {
1739 // Check if result fits in a smi.
1740 __ cmp(eax, 0xc0000000);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001741 __ j(negative, &non_smi_result, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001742 }
1743 // Tag smi result and return.
1744 __ SmiTag(eax);
1745 __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack.
1746
1747 // All ops except SHR return a signed int32 that we load in
1748 // a HeapNumber.
1749 if (op_ != Token::SHR) {
1750 __ bind(&non_smi_result);
1751 // Allocate a heap number if needed.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001752 __ mov(ebx, eax); // ebx: result
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001753 Label skip_allocation;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001754 switch (mode_) {
1755 case OVERWRITE_LEFT:
1756 case OVERWRITE_RIGHT:
1757 // If the operand was an object, we skip the
1758 // allocation of a heap number.
1759 __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
1760 1 * kPointerSize : 2 * kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00001761 __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001762 // Fall through!
1763 case NO_OVERWRITE:
1764 __ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
1765 __ bind(&skip_allocation);
1766 break;
1767 default: UNREACHABLE();
1768 }
1769 // Store the result in the HeapNumber and return.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001770 if (CpuFeatures::IsSupported(SSE2)) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001771 CpuFeatures::Scope use_sse2(SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001772 __ cvtsi2sd(xmm0, ebx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001773 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1774 } else {
1775 __ mov(Operand(esp, 1 * kPointerSize), ebx);
1776 __ fild_s(Operand(esp, 1 * kPointerSize));
1777 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1778 }
1779 __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack.
1780 }
1781
1782 __ bind(&not_floats);
1783 __ bind(&not_int32);
1784 GenerateTypeTransitionWithSavedArgs(masm);
1785 break;
1786 }
1787 default: UNREACHABLE(); break;
1788 }
1789
1790 // If an allocation fails, or SHR or MOD hit a hard case,
1791 // use the runtime system to get the correct result.
1792 __ bind(&call_runtime);
1793
1794 switch (op_) {
1795 case Token::ADD:
1796 GenerateRegisterArgsPush(masm);
1797 __ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION);
1798 break;
1799 case Token::SUB:
1800 GenerateRegisterArgsPush(masm);
1801 __ InvokeBuiltin(Builtins::SUB, JUMP_FUNCTION);
1802 break;
1803 case Token::MUL:
1804 GenerateRegisterArgsPush(masm);
1805 __ InvokeBuiltin(Builtins::MUL, JUMP_FUNCTION);
1806 break;
1807 case Token::DIV:
1808 GenerateRegisterArgsPush(masm);
1809 __ InvokeBuiltin(Builtins::DIV, JUMP_FUNCTION);
1810 break;
1811 case Token::MOD:
1812 GenerateRegisterArgsPush(masm);
1813 __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
1814 break;
1815 case Token::BIT_OR:
1816 __ InvokeBuiltin(Builtins::BIT_OR, JUMP_FUNCTION);
1817 break;
1818 case Token::BIT_AND:
1819 __ InvokeBuiltin(Builtins::BIT_AND, JUMP_FUNCTION);
1820 break;
1821 case Token::BIT_XOR:
1822 __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_FUNCTION);
1823 break;
1824 case Token::SAR:
1825 __ InvokeBuiltin(Builtins::SAR, JUMP_FUNCTION);
1826 break;
1827 case Token::SHL:
1828 __ InvokeBuiltin(Builtins::SHL, JUMP_FUNCTION);
1829 break;
1830 case Token::SHR:
1831 __ InvokeBuiltin(Builtins::SHR, JUMP_FUNCTION);
1832 break;
1833 default:
1834 UNREACHABLE();
1835 }
1836}
1837
1838
danno@chromium.org40cb8782011-05-25 07:58:50 +00001839void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
lrn@chromium.org7516f052011-03-30 08:52:27 +00001840 if (op_ == Token::ADD) {
1841 // Handle string addition here, because it is the only operation
1842 // that does not do a ToNumber conversion on the operands.
1843 GenerateAddStrings(masm);
1844 }
1845
danno@chromium.org160a7b02011-04-18 15:51:38 +00001846 Factory* factory = masm->isolate()->factory();
1847
lrn@chromium.org7516f052011-03-30 08:52:27 +00001848 // Convert odd ball arguments to numbers.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001849 Label check, done;
danno@chromium.org160a7b02011-04-18 15:51:38 +00001850 __ cmp(edx, factory->undefined_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001851 __ j(not_equal, &check, Label::kNear);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001852 if (Token::IsBitOp(op_)) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001853 __ xor_(edx, edx);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001854 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00001855 __ mov(edx, Immediate(factory->nan_value()));
lrn@chromium.org7516f052011-03-30 08:52:27 +00001856 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001857 __ jmp(&done, Label::kNear);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001858 __ bind(&check);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001859 __ cmp(eax, factory->undefined_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001860 __ j(not_equal, &done, Label::kNear);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001861 if (Token::IsBitOp(op_)) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001862 __ xor_(eax, eax);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001863 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00001864 __ mov(eax, Immediate(factory->nan_value()));
lrn@chromium.org7516f052011-03-30 08:52:27 +00001865 }
1866 __ bind(&done);
1867
1868 GenerateHeapNumberStub(masm);
1869}
1870
1871
danno@chromium.org40cb8782011-05-25 07:58:50 +00001872void BinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001873 Label call_runtime;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001874
1875 // Floating point case.
1876 switch (op_) {
1877 case Token::ADD:
1878 case Token::SUB:
1879 case Token::MUL:
1880 case Token::DIV: {
1881 Label not_floats;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001882 if (CpuFeatures::IsSupported(SSE2)) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001883 CpuFeatures::Scope use_sse2(SSE2);
1884 FloatingPointHelper::LoadSSE2Operands(masm, &not_floats);
1885
1886 switch (op_) {
1887 case Token::ADD: __ addsd(xmm0, xmm1); break;
1888 case Token::SUB: __ subsd(xmm0, xmm1); break;
1889 case Token::MUL: __ mulsd(xmm0, xmm1); break;
1890 case Token::DIV: __ divsd(xmm0, xmm1); break;
1891 default: UNREACHABLE();
1892 }
1893 GenerateHeapResultAllocation(masm, &call_runtime);
1894 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1895 __ ret(0);
1896 } else { // SSE2 not available, use FPU.
1897 FloatingPointHelper::CheckFloatOperands(masm, &not_floats, ebx);
1898 FloatingPointHelper::LoadFloatOperands(
1899 masm,
1900 ecx,
1901 FloatingPointHelper::ARGS_IN_REGISTERS);
1902 switch (op_) {
1903 case Token::ADD: __ faddp(1); break;
1904 case Token::SUB: __ fsubp(1); break;
1905 case Token::MUL: __ fmulp(1); break;
1906 case Token::DIV: __ fdivp(1); break;
1907 default: UNREACHABLE();
1908 }
1909 Label after_alloc_failure;
1910 GenerateHeapResultAllocation(masm, &after_alloc_failure);
1911 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1912 __ ret(0);
1913 __ bind(&after_alloc_failure);
1914 __ ffree();
1915 __ jmp(&call_runtime);
1916 }
1917
1918 __ bind(&not_floats);
1919 GenerateTypeTransition(masm);
1920 break;
1921 }
1922
1923 case Token::MOD: {
1924 // For MOD we go directly to runtime in the non-smi case.
1925 break;
1926 }
1927 case Token::BIT_OR:
1928 case Token::BIT_AND:
1929 case Token::BIT_XOR:
1930 case Token::SAR:
1931 case Token::SHL:
1932 case Token::SHR: {
1933 GenerateRegisterArgsPush(masm);
1934 Label not_floats;
1935 Label non_smi_result;
1936 FloatingPointHelper::LoadUnknownsAsIntegers(masm,
1937 use_sse3_,
1938 &not_floats);
1939 switch (op_) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001940 case Token::BIT_OR: __ or_(eax, ecx); break;
1941 case Token::BIT_AND: __ and_(eax, ecx); break;
1942 case Token::BIT_XOR: __ xor_(eax, ecx); break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001943 case Token::SAR: __ sar_cl(eax); break;
1944 case Token::SHL: __ shl_cl(eax); break;
1945 case Token::SHR: __ shr_cl(eax); break;
1946 default: UNREACHABLE();
1947 }
1948 if (op_ == Token::SHR) {
1949 // Check if result is non-negative and fits in a smi.
1950 __ test(eax, Immediate(0xc0000000));
1951 __ j(not_zero, &call_runtime);
1952 } else {
1953 // Check if result fits in a smi.
1954 __ cmp(eax, 0xc0000000);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001955 __ j(negative, &non_smi_result, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001956 }
1957 // Tag smi result and return.
1958 __ SmiTag(eax);
1959 __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack.
1960
1961 // All ops except SHR return a signed int32 that we load in
1962 // a HeapNumber.
1963 if (op_ != Token::SHR) {
1964 __ bind(&non_smi_result);
1965 // Allocate a heap number if needed.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001966 __ mov(ebx, eax); // ebx: result
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001967 Label skip_allocation;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001968 switch (mode_) {
1969 case OVERWRITE_LEFT:
1970 case OVERWRITE_RIGHT:
1971 // If the operand was an object, we skip the
1972 // allocation of a heap number.
1973 __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
1974 1 * kPointerSize : 2 * kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00001975 __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001976 // Fall through!
1977 case NO_OVERWRITE:
1978 __ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
1979 __ bind(&skip_allocation);
1980 break;
1981 default: UNREACHABLE();
1982 }
1983 // Store the result in the HeapNumber and return.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001984 if (CpuFeatures::IsSupported(SSE2)) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001985 CpuFeatures::Scope use_sse2(SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001986 __ cvtsi2sd(xmm0, ebx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001987 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1988 } else {
1989 __ mov(Operand(esp, 1 * kPointerSize), ebx);
1990 __ fild_s(Operand(esp, 1 * kPointerSize));
1991 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1992 }
1993 __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack.
1994 }
1995
1996 __ bind(&not_floats);
1997 GenerateTypeTransitionWithSavedArgs(masm);
1998 break;
1999 }
2000 default: UNREACHABLE(); break;
2001 }
2002
2003 // If an allocation fails, or SHR or MOD hit a hard case,
2004 // use the runtime system to get the correct result.
2005 __ bind(&call_runtime);
2006
2007 switch (op_) {
2008 case Token::ADD:
2009 GenerateRegisterArgsPush(masm);
2010 __ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION);
2011 break;
2012 case Token::SUB:
2013 GenerateRegisterArgsPush(masm);
2014 __ InvokeBuiltin(Builtins::SUB, JUMP_FUNCTION);
2015 break;
2016 case Token::MUL:
2017 GenerateRegisterArgsPush(masm);
2018 __ InvokeBuiltin(Builtins::MUL, JUMP_FUNCTION);
2019 break;
2020 case Token::DIV:
2021 GenerateRegisterArgsPush(masm);
2022 __ InvokeBuiltin(Builtins::DIV, JUMP_FUNCTION);
2023 break;
2024 case Token::MOD:
2025 GenerateRegisterArgsPush(masm);
2026 __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
2027 break;
2028 case Token::BIT_OR:
2029 __ InvokeBuiltin(Builtins::BIT_OR, JUMP_FUNCTION);
2030 break;
2031 case Token::BIT_AND:
2032 __ InvokeBuiltin(Builtins::BIT_AND, JUMP_FUNCTION);
2033 break;
2034 case Token::BIT_XOR:
2035 __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_FUNCTION);
2036 break;
2037 case Token::SAR:
2038 __ InvokeBuiltin(Builtins::SAR, JUMP_FUNCTION);
2039 break;
2040 case Token::SHL:
2041 __ InvokeBuiltin(Builtins::SHL, JUMP_FUNCTION);
2042 break;
2043 case Token::SHR:
2044 __ InvokeBuiltin(Builtins::SHR, JUMP_FUNCTION);
2045 break;
2046 default:
2047 UNREACHABLE();
2048 }
2049}
2050
2051
danno@chromium.org40cb8782011-05-25 07:58:50 +00002052void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002053 Label call_runtime;
2054
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002055 Counters* counters = masm->isolate()->counters();
2056 __ IncrementCounter(counters->generic_binary_stub_calls(), 1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002057
2058 switch (op_) {
2059 case Token::ADD:
2060 case Token::SUB:
2061 case Token::MUL:
2062 case Token::DIV:
2063 break;
2064 case Token::MOD:
2065 case Token::BIT_OR:
2066 case Token::BIT_AND:
2067 case Token::BIT_XOR:
2068 case Token::SAR:
2069 case Token::SHL:
2070 case Token::SHR:
2071 GenerateRegisterArgsPush(masm);
2072 break;
2073 default:
2074 UNREACHABLE();
2075 }
2076
2077 GenerateSmiCode(masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS);
2078
2079 // Floating point case.
2080 switch (op_) {
2081 case Token::ADD:
2082 case Token::SUB:
2083 case Token::MUL:
2084 case Token::DIV: {
2085 Label not_floats;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002086 if (CpuFeatures::IsSupported(SSE2)) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002087 CpuFeatures::Scope use_sse2(SSE2);
2088 FloatingPointHelper::LoadSSE2Operands(masm, &not_floats);
2089
2090 switch (op_) {
2091 case Token::ADD: __ addsd(xmm0, xmm1); break;
2092 case Token::SUB: __ subsd(xmm0, xmm1); break;
2093 case Token::MUL: __ mulsd(xmm0, xmm1); break;
2094 case Token::DIV: __ divsd(xmm0, xmm1); break;
2095 default: UNREACHABLE();
2096 }
2097 GenerateHeapResultAllocation(masm, &call_runtime);
2098 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
2099 __ ret(0);
2100 } else { // SSE2 not available, use FPU.
2101 FloatingPointHelper::CheckFloatOperands(masm, &not_floats, ebx);
2102 FloatingPointHelper::LoadFloatOperands(
2103 masm,
2104 ecx,
2105 FloatingPointHelper::ARGS_IN_REGISTERS);
2106 switch (op_) {
2107 case Token::ADD: __ faddp(1); break;
2108 case Token::SUB: __ fsubp(1); break;
2109 case Token::MUL: __ fmulp(1); break;
2110 case Token::DIV: __ fdivp(1); break;
2111 default: UNREACHABLE();
2112 }
2113 Label after_alloc_failure;
2114 GenerateHeapResultAllocation(masm, &after_alloc_failure);
2115 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
2116 __ ret(0);
2117 __ bind(&after_alloc_failure);
2118 __ ffree();
2119 __ jmp(&call_runtime);
2120 }
2121 __ bind(&not_floats);
2122 break;
2123 }
2124 case Token::MOD: {
2125 // For MOD we go directly to runtime in the non-smi case.
2126 break;
2127 }
2128 case Token::BIT_OR:
2129 case Token::BIT_AND:
2130 case Token::BIT_XOR:
2131 case Token::SAR:
2132 case Token::SHL:
2133 case Token::SHR: {
2134 Label non_smi_result;
2135 FloatingPointHelper::LoadUnknownsAsIntegers(masm,
2136 use_sse3_,
2137 &call_runtime);
2138 switch (op_) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002139 case Token::BIT_OR: __ or_(eax, ecx); break;
2140 case Token::BIT_AND: __ and_(eax, ecx); break;
2141 case Token::BIT_XOR: __ xor_(eax, ecx); break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002142 case Token::SAR: __ sar_cl(eax); break;
2143 case Token::SHL: __ shl_cl(eax); break;
2144 case Token::SHR: __ shr_cl(eax); break;
2145 default: UNREACHABLE();
2146 }
2147 if (op_ == Token::SHR) {
2148 // Check if result is non-negative and fits in a smi.
2149 __ test(eax, Immediate(0xc0000000));
2150 __ j(not_zero, &call_runtime);
2151 } else {
2152 // Check if result fits in a smi.
2153 __ cmp(eax, 0xc0000000);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00002154 __ j(negative, &non_smi_result, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002155 }
2156 // Tag smi result and return.
2157 __ SmiTag(eax);
2158 __ ret(2 * kPointerSize); // Drop the arguments from the stack.
2159
2160 // All ops except SHR return a signed int32 that we load in
2161 // a HeapNumber.
2162 if (op_ != Token::SHR) {
2163 __ bind(&non_smi_result);
2164 // Allocate a heap number if needed.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002165 __ mov(ebx, eax); // ebx: result
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002166 Label skip_allocation;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002167 switch (mode_) {
2168 case OVERWRITE_LEFT:
2169 case OVERWRITE_RIGHT:
2170 // If the operand was an object, we skip the
2171 // allocation of a heap number.
2172 __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
2173 1 * kPointerSize : 2 * kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00002174 __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002175 // Fall through!
2176 case NO_OVERWRITE:
2177 __ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
2178 __ bind(&skip_allocation);
2179 break;
2180 default: UNREACHABLE();
2181 }
2182 // Store the result in the HeapNumber and return.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002183 if (CpuFeatures::IsSupported(SSE2)) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002184 CpuFeatures::Scope use_sse2(SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002185 __ cvtsi2sd(xmm0, ebx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002186 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
2187 } else {
2188 __ mov(Operand(esp, 1 * kPointerSize), ebx);
2189 __ fild_s(Operand(esp, 1 * kPointerSize));
2190 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
2191 }
2192 __ ret(2 * kPointerSize);
2193 }
2194 break;
2195 }
2196 default: UNREACHABLE(); break;
2197 }
2198
2199 // If all else fails, use the runtime system to get the correct
2200 // result.
2201 __ bind(&call_runtime);
2202 switch (op_) {
2203 case Token::ADD: {
ager@chromium.org0ee099b2011-01-25 14:06:47 +00002204 GenerateAddStrings(masm);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002205 GenerateRegisterArgsPush(masm);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002206 __ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION);
2207 break;
2208 }
2209 case Token::SUB:
2210 GenerateRegisterArgsPush(masm);
2211 __ InvokeBuiltin(Builtins::SUB, JUMP_FUNCTION);
2212 break;
2213 case Token::MUL:
2214 GenerateRegisterArgsPush(masm);
2215 __ InvokeBuiltin(Builtins::MUL, JUMP_FUNCTION);
2216 break;
2217 case Token::DIV:
2218 GenerateRegisterArgsPush(masm);
2219 __ InvokeBuiltin(Builtins::DIV, JUMP_FUNCTION);
2220 break;
2221 case Token::MOD:
2222 __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
2223 break;
2224 case Token::BIT_OR:
2225 __ InvokeBuiltin(Builtins::BIT_OR, JUMP_FUNCTION);
2226 break;
2227 case Token::BIT_AND:
2228 __ InvokeBuiltin(Builtins::BIT_AND, JUMP_FUNCTION);
2229 break;
2230 case Token::BIT_XOR:
2231 __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_FUNCTION);
2232 break;
2233 case Token::SAR:
2234 __ InvokeBuiltin(Builtins::SAR, JUMP_FUNCTION);
2235 break;
2236 case Token::SHL:
2237 __ InvokeBuiltin(Builtins::SHL, JUMP_FUNCTION);
2238 break;
2239 case Token::SHR:
2240 __ InvokeBuiltin(Builtins::SHR, JUMP_FUNCTION);
2241 break;
2242 default:
2243 UNREACHABLE();
2244 }
2245}
2246
2247
danno@chromium.org40cb8782011-05-25 07:58:50 +00002248void BinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002249 ASSERT(op_ == Token::ADD);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002250 Label left_not_string, call_runtime;
ager@chromium.org0ee099b2011-01-25 14:06:47 +00002251
2252 // Registers containing left and right operands respectively.
2253 Register left = edx;
2254 Register right = eax;
2255
2256 // Test if left operand is a string.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002257 __ JumpIfSmi(left, &left_not_string, Label::kNear);
ager@chromium.org0ee099b2011-01-25 14:06:47 +00002258 __ CmpObjectType(left, FIRST_NONSTRING_TYPE, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002259 __ j(above_equal, &left_not_string, Label::kNear);
ager@chromium.org0ee099b2011-01-25 14:06:47 +00002260
2261 StringAddStub string_add_left_stub(NO_STRING_CHECK_LEFT_IN_STUB);
2262 GenerateRegisterArgsPush(masm);
2263 __ TailCallStub(&string_add_left_stub);
2264
2265 // Left operand is not a string, test right.
2266 __ bind(&left_not_string);
whesse@chromium.org7b260152011-06-20 15:33:18 +00002267 __ JumpIfSmi(right, &call_runtime, Label::kNear);
ager@chromium.org0ee099b2011-01-25 14:06:47 +00002268 __ CmpObjectType(right, FIRST_NONSTRING_TYPE, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002269 __ j(above_equal, &call_runtime, Label::kNear);
ager@chromium.org0ee099b2011-01-25 14:06:47 +00002270
2271 StringAddStub string_add_right_stub(NO_STRING_CHECK_RIGHT_IN_STUB);
2272 GenerateRegisterArgsPush(masm);
2273 __ TailCallStub(&string_add_right_stub);
2274
2275 // Neither argument is a string.
2276 __ bind(&call_runtime);
2277}
2278
2279
danno@chromium.org40cb8782011-05-25 07:58:50 +00002280void BinaryOpStub::GenerateHeapResultAllocation(
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002281 MacroAssembler* masm,
2282 Label* alloc_failure) {
2283 Label skip_allocation;
2284 OverwriteMode mode = mode_;
2285 switch (mode) {
2286 case OVERWRITE_LEFT: {
2287 // If the argument in edx is already an object, we skip the
2288 // allocation of a heap number.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002289 __ JumpIfNotSmi(edx, &skip_allocation, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002290 // Allocate a heap number for the result. Keep eax and edx intact
2291 // for the possible runtime call.
2292 __ AllocateHeapNumber(ebx, ecx, no_reg, alloc_failure);
2293 // Now edx can be overwritten losing one of the arguments as we are
2294 // now done and will not need it any more.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002295 __ mov(edx, ebx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002296 __ bind(&skip_allocation);
2297 // Use object in edx as a result holder
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002298 __ mov(eax, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002299 break;
2300 }
2301 case OVERWRITE_RIGHT:
2302 // If the argument in eax is already an object, we skip the
2303 // allocation of a heap number.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002304 __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002305 // Fall through!
2306 case NO_OVERWRITE:
2307 // Allocate a heap number for the result. Keep eax and edx intact
2308 // for the possible runtime call.
2309 __ AllocateHeapNumber(ebx, ecx, no_reg, alloc_failure);
2310 // Now eax can be overwritten losing one of the arguments as we are
2311 // now done and will not need it any more.
2312 __ mov(eax, ebx);
2313 __ bind(&skip_allocation);
2314 break;
2315 default: UNREACHABLE();
2316 }
2317}
2318
2319
danno@chromium.org40cb8782011-05-25 07:58:50 +00002320void BinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002321 __ pop(ecx);
2322 __ push(edx);
2323 __ push(eax);
2324 __ push(ecx);
2325}
2326
2327
ricow@chromium.org65fae842010-08-25 15:26:24 +00002328void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
whesse@chromium.org023421e2010-12-21 12:19:12 +00002329 // TAGGED case:
2330 // Input:
2331 // esp[4]: tagged number input argument (should be number).
2332 // esp[0]: return address.
2333 // Output:
2334 // eax: tagged double result.
2335 // UNTAGGED case:
2336 // Input::
2337 // esp[0]: return address.
2338 // xmm1: untagged double input argument
2339 // Output:
2340 // xmm1: untagged double result.
2341
ricow@chromium.org65fae842010-08-25 15:26:24 +00002342 Label runtime_call;
2343 Label runtime_call_clear_stack;
whesse@chromium.org023421e2010-12-21 12:19:12 +00002344 Label skip_cache;
2345 const bool tagged = (argument_type_ == TAGGED);
2346 if (tagged) {
2347 // Test that eax is a number.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002348 Label input_not_smi;
2349 Label loaded;
whesse@chromium.org023421e2010-12-21 12:19:12 +00002350 __ mov(eax, Operand(esp, kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00002351 __ JumpIfNotSmi(eax, &input_not_smi, Label::kNear);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002352 // Input is a smi. Untag and load it onto the FPU stack.
2353 // Then load the low and high words of the double into ebx, edx.
2354 STATIC_ASSERT(kSmiTagSize == 1);
2355 __ sar(eax, 1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002356 __ sub(esp, Immediate(2 * kPointerSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002357 __ mov(Operand(esp, 0), eax);
2358 __ fild_s(Operand(esp, 0));
2359 __ fst_d(Operand(esp, 0));
2360 __ pop(edx);
2361 __ pop(ebx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002362 __ jmp(&loaded, Label::kNear);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002363 __ bind(&input_not_smi);
2364 // Check if input is a HeapNumber.
2365 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002366 Factory* factory = masm->isolate()->factory();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002367 __ cmp(ebx, Immediate(factory->heap_number_map()));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002368 __ j(not_equal, &runtime_call);
2369 // Input is a HeapNumber. Push it on the FPU stack and load its
2370 // low and high words into ebx, edx.
2371 __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
2372 __ mov(edx, FieldOperand(eax, HeapNumber::kExponentOffset));
2373 __ mov(ebx, FieldOperand(eax, HeapNumber::kMantissaOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002374
whesse@chromium.org023421e2010-12-21 12:19:12 +00002375 __ bind(&loaded);
2376 } else { // UNTAGGED.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002377 if (CpuFeatures::IsSupported(SSE4_1)) {
whesse@chromium.org023421e2010-12-21 12:19:12 +00002378 CpuFeatures::Scope sse4_scope(SSE4_1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002379 __ pextrd(edx, xmm1, 0x1); // copy xmm1[63..32] to edx.
whesse@chromium.org023421e2010-12-21 12:19:12 +00002380 } else {
2381 __ pshufd(xmm0, xmm1, 0x1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002382 __ movd(edx, xmm0);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002383 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002384 __ movd(ebx, xmm1);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002385 }
2386
2387 // ST[0] or xmm1 == double value
ricow@chromium.org65fae842010-08-25 15:26:24 +00002388 // ebx = low 32 bits of double value
2389 // edx = high 32 bits of double value
2390 // Compute hash (the shifts are arithmetic):
2391 // h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1);
2392 __ mov(ecx, ebx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002393 __ xor_(ecx, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002394 __ mov(eax, ecx);
2395 __ sar(eax, 16);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002396 __ xor_(ecx, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002397 __ mov(eax, ecx);
2398 __ sar(eax, 8);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002399 __ xor_(ecx, eax);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002400 ASSERT(IsPowerOf2(TranscendentalCache::SubCache::kCacheSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002401 __ and_(ecx,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002402 Immediate(TranscendentalCache::SubCache::kCacheSize - 1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002403
whesse@chromium.org023421e2010-12-21 12:19:12 +00002404 // ST[0] or xmm1 == double value.
ricow@chromium.org65fae842010-08-25 15:26:24 +00002405 // ebx = low 32 bits of double value.
2406 // edx = high 32 bits of double value.
2407 // ecx = TranscendentalCache::hash(double value).
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002408 ExternalReference cache_array =
2409 ExternalReference::transcendental_cache_array_address(masm->isolate());
2410 __ mov(eax, Immediate(cache_array));
2411 int cache_array_index =
2412 type_ * sizeof(masm->isolate()->transcendental_cache()->caches_[0]);
2413 __ mov(eax, Operand(eax, cache_array_index));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002414 // Eax points to the cache for the type type_.
2415 // If NULL, the cache hasn't been initialized yet, so go through runtime.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002416 __ test(eax, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002417 __ j(zero, &runtime_call_clear_stack);
2418#ifdef DEBUG
2419 // Check that the layout of cache elements match expectations.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002420 { TranscendentalCache::SubCache::Element test_elem[2];
ricow@chromium.org65fae842010-08-25 15:26:24 +00002421 char* elem_start = reinterpret_cast<char*>(&test_elem[0]);
2422 char* elem2_start = reinterpret_cast<char*>(&test_elem[1]);
2423 char* elem_in0 = reinterpret_cast<char*>(&(test_elem[0].in[0]));
2424 char* elem_in1 = reinterpret_cast<char*>(&(test_elem[0].in[1]));
2425 char* elem_out = reinterpret_cast<char*>(&(test_elem[0].output));
2426 CHECK_EQ(12, elem2_start - elem_start); // Two uint_32's and a pointer.
2427 CHECK_EQ(0, elem_in0 - elem_start);
2428 CHECK_EQ(kIntSize, elem_in1 - elem_start);
2429 CHECK_EQ(2 * kIntSize, elem_out - elem_start);
2430 }
2431#endif
2432 // Find the address of the ecx'th entry in the cache, i.e., &eax[ecx*12].
2433 __ lea(ecx, Operand(ecx, ecx, times_2, 0));
2434 __ lea(ecx, Operand(eax, ecx, times_4, 0));
2435 // Check if cache matches: Double value is stored in uint32_t[2] array.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002436 Label cache_miss;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002437 __ cmp(ebx, Operand(ecx, 0));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002438 __ j(not_equal, &cache_miss, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002439 __ cmp(edx, Operand(ecx, kIntSize));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002440 __ j(not_equal, &cache_miss, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002441 // Cache hit!
2442 __ mov(eax, Operand(ecx, 2 * kIntSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002443 if (tagged) {
2444 __ fstp(0);
2445 __ ret(kPointerSize);
2446 } else { // UNTAGGED.
2447 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
2448 __ Ret();
2449 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002450
2451 __ bind(&cache_miss);
2452 // Update cache with new value.
2453 // We are short on registers, so use no_reg as scratch.
2454 // This gives slightly larger code.
whesse@chromium.org023421e2010-12-21 12:19:12 +00002455 if (tagged) {
2456 __ AllocateHeapNumber(eax, edi, no_reg, &runtime_call_clear_stack);
2457 } else { // UNTAGGED.
2458 __ AllocateHeapNumber(eax, edi, no_reg, &skip_cache);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002459 __ sub(esp, Immediate(kDoubleSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002460 __ movdbl(Operand(esp, 0), xmm1);
2461 __ fld_d(Operand(esp, 0));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002462 __ add(esp, Immediate(kDoubleSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002463 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002464 GenerateOperation(masm);
2465 __ mov(Operand(ecx, 0), ebx);
2466 __ mov(Operand(ecx, kIntSize), edx);
2467 __ mov(Operand(ecx, 2 * kIntSize), eax);
2468 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002469 if (tagged) {
2470 __ ret(kPointerSize);
2471 } else { // UNTAGGED.
2472 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
2473 __ Ret();
ricow@chromium.org65fae842010-08-25 15:26:24 +00002474
whesse@chromium.org023421e2010-12-21 12:19:12 +00002475 // Skip cache and return answer directly, only in untagged case.
2476 __ bind(&skip_cache);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002477 __ sub(esp, Immediate(kDoubleSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002478 __ movdbl(Operand(esp, 0), xmm1);
2479 __ fld_d(Operand(esp, 0));
2480 GenerateOperation(masm);
2481 __ fstp_d(Operand(esp, 0));
2482 __ movdbl(xmm1, Operand(esp, 0));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002483 __ add(esp, Immediate(kDoubleSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002484 // We return the value in xmm1 without adding it to the cache, but
2485 // we cause a scavenging GC so that future allocations will succeed.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002486 {
2487 FrameScope scope(masm, StackFrame::INTERNAL);
2488 // Allocate an unused object bigger than a HeapNumber.
2489 __ push(Immediate(Smi::FromInt(2 * kDoubleSize)));
2490 __ CallRuntimeSaveDoubles(Runtime::kAllocateInNewSpace);
2491 }
whesse@chromium.org023421e2010-12-21 12:19:12 +00002492 __ Ret();
2493 }
2494
2495 // Call runtime, doing whatever allocation and cleanup is necessary.
2496 if (tagged) {
2497 __ bind(&runtime_call_clear_stack);
2498 __ fstp(0);
2499 __ bind(&runtime_call);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002500 ExternalReference runtime =
2501 ExternalReference(RuntimeFunction(), masm->isolate());
2502 __ TailCallExternalReference(runtime, 1, 1);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002503 } else { // UNTAGGED.
2504 __ bind(&runtime_call_clear_stack);
2505 __ bind(&runtime_call);
2506 __ AllocateHeapNumber(eax, edi, no_reg, &skip_cache);
2507 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002508 {
2509 FrameScope scope(masm, StackFrame::INTERNAL);
2510 __ push(eax);
2511 __ CallRuntime(RuntimeFunction(), 1);
2512 }
whesse@chromium.org023421e2010-12-21 12:19:12 +00002513 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
2514 __ Ret();
2515 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002516}
2517
2518
2519Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
2520 switch (type_) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00002521 case TranscendentalCache::SIN: return Runtime::kMath_sin;
2522 case TranscendentalCache::COS: return Runtime::kMath_cos;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002523 case TranscendentalCache::LOG: return Runtime::kMath_log;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002524 default:
2525 UNIMPLEMENTED();
2526 return Runtime::kAbort;
2527 }
2528}
2529
2530
2531void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) {
2532 // Only free register is edi.
whesse@chromium.org023421e2010-12-21 12:19:12 +00002533 // Input value is on FP stack, and also in ebx/edx.
2534 // Input value is possibly in xmm1.
2535 // Address of result (a newly allocated HeapNumber) may be in eax.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002536 if (type_ == TranscendentalCache::SIN || type_ == TranscendentalCache::COS) {
2537 // Both fsin and fcos require arguments in the range +/-2^63 and
2538 // return NaN for infinities and NaN. They can share all code except
2539 // the actual fsin/fcos operation.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002540 Label in_range, done;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002541 // If argument is outside the range -2^63..2^63, fsin/cos doesn't
2542 // work. We must reduce it to the appropriate range.
2543 __ mov(edi, edx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002544 __ and_(edi, Immediate(0x7ff00000)); // Exponent only.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002545 int supported_exponent_limit =
2546 (63 + HeapNumber::kExponentBias) << HeapNumber::kExponentShift;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002547 __ cmp(edi, Immediate(supported_exponent_limit));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00002548 __ j(below, &in_range, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002549 // Check for infinity and NaN. Both return NaN for sin.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002550 __ cmp(edi, Immediate(0x7ff00000));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002551 Label non_nan_result;
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00002552 __ j(not_equal, &non_nan_result, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002553 // Input is +/-Infinity or NaN. Result is NaN.
2554 __ fstp(0);
2555 // NaN is represented by 0x7ff8000000000000.
2556 __ push(Immediate(0x7ff80000));
2557 __ push(Immediate(0));
2558 __ fld_d(Operand(esp, 0));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002559 __ add(esp, Immediate(2 * kPointerSize));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002560 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002561
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002562 __ bind(&non_nan_result);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002563
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002564 // Use fpmod to restrict argument to the range +/-2*PI.
2565 __ mov(edi, eax); // Save eax before using fnstsw_ax.
2566 __ fldpi();
2567 __ fadd(0);
2568 __ fld(1);
2569 // FPU Stack: input, 2*pi, input.
2570 {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002571 Label no_exceptions;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002572 __ fwait();
2573 __ fnstsw_ax();
2574 // Clear if Illegal Operand or Zero Division exceptions are set.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002575 __ test(eax, Immediate(5));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002576 __ j(zero, &no_exceptions, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002577 __ fnclex();
2578 __ bind(&no_exceptions);
2579 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002580
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002581 // Compute st(0) % st(1)
2582 {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002583 Label partial_remainder_loop;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002584 __ bind(&partial_remainder_loop);
2585 __ fprem1();
2586 __ fwait();
2587 __ fnstsw_ax();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002588 __ test(eax, Immediate(0x400 /* C2 */));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002589 // If C2 is set, computation only has partial result. Loop to
2590 // continue computation.
2591 __ j(not_zero, &partial_remainder_loop);
2592 }
2593 // FPU Stack: input, 2*pi, input % 2*pi
2594 __ fstp(2);
2595 __ fstp(0);
2596 __ mov(eax, edi); // Restore eax (allocated HeapNumber pointer).
2597
2598 // FPU Stack: input % 2*pi
2599 __ bind(&in_range);
2600 switch (type_) {
2601 case TranscendentalCache::SIN:
2602 __ fsin();
2603 break;
2604 case TranscendentalCache::COS:
2605 __ fcos();
2606 break;
2607 default:
2608 UNREACHABLE();
2609 }
2610 __ bind(&done);
2611 } else {
2612 ASSERT(type_ == TranscendentalCache::LOG);
2613 __ fldln2();
2614 __ fxch();
2615 __ fyl2x();
ricow@chromium.org65fae842010-08-25 15:26:24 +00002616 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002617}
2618
2619
ricow@chromium.org65fae842010-08-25 15:26:24 +00002620// Input: edx, eax are the left and right objects of a bit op.
2621// Output: eax, ecx are left and right integers for a bit op.
ricow@chromium.org65fae842010-08-25 15:26:24 +00002622void FloatingPointHelper::LoadUnknownsAsIntegers(MacroAssembler* masm,
2623 bool use_sse3,
2624 Label* conversion_failure) {
2625 // Check float operands.
2626 Label arg1_is_object, check_undefined_arg1;
2627 Label arg2_is_object, check_undefined_arg2;
2628 Label load_arg2, done;
2629
2630 // Test if arg1 is a Smi.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00002631 __ JumpIfNotSmi(edx, &arg1_is_object, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002632
2633 __ SmiUntag(edx);
2634 __ jmp(&load_arg2);
2635
2636 // If the argument is undefined it converts to zero (ECMA-262, section 9.5).
2637 __ bind(&check_undefined_arg1);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002638 Factory* factory = masm->isolate()->factory();
2639 __ cmp(edx, factory->undefined_value());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002640 __ j(not_equal, conversion_failure);
2641 __ mov(edx, Immediate(0));
2642 __ jmp(&load_arg2);
2643
2644 __ bind(&arg1_is_object);
2645 __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002646 __ cmp(ebx, factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002647 __ j(not_equal, &check_undefined_arg1);
2648
2649 // Get the untagged integer version of the edx heap number in ecx.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00002650 IntegerConvert(masm, edx, use_sse3, conversion_failure);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002651 __ mov(edx, ecx);
2652
2653 // Here edx has the untagged integer, eax has a Smi or a heap number.
2654 __ bind(&load_arg2);
2655
2656 // Test if arg2 is a Smi.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00002657 __ JumpIfNotSmi(eax, &arg2_is_object, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002658
2659 __ SmiUntag(eax);
2660 __ mov(ecx, eax);
2661 __ jmp(&done);
2662
2663 // If the argument is undefined it converts to zero (ECMA-262, section 9.5).
2664 __ bind(&check_undefined_arg2);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002665 __ cmp(eax, factory->undefined_value());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002666 __ j(not_equal, conversion_failure);
2667 __ mov(ecx, Immediate(0));
2668 __ jmp(&done);
2669
2670 __ bind(&arg2_is_object);
2671 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002672 __ cmp(ebx, factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002673 __ j(not_equal, &check_undefined_arg2);
2674
2675 // Get the untagged integer version of the eax heap number in ecx.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00002676 IntegerConvert(masm, eax, use_sse3, conversion_failure);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002677 __ bind(&done);
2678 __ mov(eax, edx);
2679}
2680
2681
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002682void FloatingPointHelper::CheckLoadedIntegersWereInt32(MacroAssembler* masm,
2683 bool use_sse3,
2684 Label* not_int32) {
2685 return;
2686}
2687
2688
ricow@chromium.org65fae842010-08-25 15:26:24 +00002689void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm,
2690 Register number) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002691 Label load_smi, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002692
whesse@chromium.org7b260152011-06-20 15:33:18 +00002693 __ JumpIfSmi(number, &load_smi, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002694 __ fld_d(FieldOperand(number, HeapNumber::kValueOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002695 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002696
2697 __ bind(&load_smi);
2698 __ SmiUntag(number);
2699 __ push(number);
2700 __ fild_s(Operand(esp, 0));
2701 __ pop(number);
2702
2703 __ bind(&done);
2704}
2705
2706
2707void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002708 Label load_smi_edx, load_eax, load_smi_eax, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002709 // Load operand in edx into xmm0.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002710 __ JumpIfSmi(edx, &load_smi_edx, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002711 __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
2712
2713 __ bind(&load_eax);
2714 // Load operand in eax into xmm1.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002715 __ JumpIfSmi(eax, &load_smi_eax, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002716 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002717 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002718
2719 __ bind(&load_smi_edx);
2720 __ SmiUntag(edx); // Untag smi before converting to float.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002721 __ cvtsi2sd(xmm0, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002722 __ SmiTag(edx); // Retag smi for heap number overwriting test.
2723 __ jmp(&load_eax);
2724
2725 __ bind(&load_smi_eax);
2726 __ SmiUntag(eax); // Untag smi before converting to float.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002727 __ cvtsi2sd(xmm1, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002728 __ SmiTag(eax); // Retag smi for heap number overwriting test.
2729
2730 __ bind(&done);
2731}
2732
2733
2734void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm,
2735 Label* not_numbers) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002736 Label load_smi_edx, load_eax, load_smi_eax, load_float_eax, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002737 // Load operand in edx into xmm0, or branch to not_numbers.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002738 __ JumpIfSmi(edx, &load_smi_edx, Label::kNear);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002739 Factory* factory = masm->isolate()->factory();
2740 __ cmp(FieldOperand(edx, HeapObject::kMapOffset), factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002741 __ j(not_equal, not_numbers); // Argument in edx is not a number.
2742 __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
2743 __ bind(&load_eax);
2744 // Load operand in eax into xmm1, or branch to not_numbers.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002745 __ JumpIfSmi(eax, &load_smi_eax, Label::kNear);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002746 __ cmp(FieldOperand(eax, HeapObject::kMapOffset), factory->heap_number_map());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002747 __ j(equal, &load_float_eax, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002748 __ jmp(not_numbers); // Argument in eax is not a number.
2749 __ bind(&load_smi_edx);
2750 __ SmiUntag(edx); // Untag smi before converting to float.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002751 __ cvtsi2sd(xmm0, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002752 __ SmiTag(edx); // Retag smi for heap number overwriting test.
2753 __ jmp(&load_eax);
2754 __ bind(&load_smi_eax);
2755 __ SmiUntag(eax); // Untag smi before converting to float.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002756 __ cvtsi2sd(xmm1, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002757 __ SmiTag(eax); // Retag smi for heap number overwriting test.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002758 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002759 __ bind(&load_float_eax);
2760 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
2761 __ bind(&done);
2762}
2763
2764
2765void FloatingPointHelper::LoadSSE2Smis(MacroAssembler* masm,
2766 Register scratch) {
2767 const Register left = edx;
2768 const Register right = eax;
2769 __ mov(scratch, left);
2770 ASSERT(!scratch.is(right)); // We're about to clobber scratch.
2771 __ SmiUntag(scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002772 __ cvtsi2sd(xmm0, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002773
2774 __ mov(scratch, right);
2775 __ SmiUntag(scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002776 __ cvtsi2sd(xmm1, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002777}
2778
2779
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002780void FloatingPointHelper::CheckSSE2OperandsAreInt32(MacroAssembler* masm,
2781 Label* non_int32,
2782 Register scratch) {
2783 __ cvttsd2si(scratch, Operand(xmm0));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002784 __ cvtsi2sd(xmm2, scratch);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002785 __ ucomisd(xmm0, xmm2);
2786 __ j(not_zero, non_int32);
2787 __ j(carry, non_int32);
2788 __ cvttsd2si(scratch, Operand(xmm1));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002789 __ cvtsi2sd(xmm2, scratch);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002790 __ ucomisd(xmm1, xmm2);
2791 __ j(not_zero, non_int32);
2792 __ j(carry, non_int32);
2793}
2794
2795
ricow@chromium.org65fae842010-08-25 15:26:24 +00002796void FloatingPointHelper::LoadFloatOperands(MacroAssembler* masm,
2797 Register scratch,
2798 ArgLocation arg_location) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002799 Label load_smi_1, load_smi_2, done_load_1, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002800 if (arg_location == ARGS_IN_REGISTERS) {
2801 __ mov(scratch, edx);
2802 } else {
2803 __ mov(scratch, Operand(esp, 2 * kPointerSize));
2804 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00002805 __ JumpIfSmi(scratch, &load_smi_1, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002806 __ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset));
2807 __ bind(&done_load_1);
2808
2809 if (arg_location == ARGS_IN_REGISTERS) {
2810 __ mov(scratch, eax);
2811 } else {
2812 __ mov(scratch, Operand(esp, 1 * kPointerSize));
2813 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00002814 __ JumpIfSmi(scratch, &load_smi_2, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002815 __ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002816 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002817
2818 __ bind(&load_smi_1);
2819 __ SmiUntag(scratch);
2820 __ push(scratch);
2821 __ fild_s(Operand(esp, 0));
2822 __ pop(scratch);
2823 __ jmp(&done_load_1);
2824
2825 __ bind(&load_smi_2);
2826 __ SmiUntag(scratch);
2827 __ push(scratch);
2828 __ fild_s(Operand(esp, 0));
2829 __ pop(scratch);
2830
2831 __ bind(&done);
2832}
2833
2834
2835void FloatingPointHelper::LoadFloatSmis(MacroAssembler* masm,
2836 Register scratch) {
2837 const Register left = edx;
2838 const Register right = eax;
2839 __ mov(scratch, left);
2840 ASSERT(!scratch.is(right)); // We're about to clobber scratch.
2841 __ SmiUntag(scratch);
2842 __ push(scratch);
2843 __ fild_s(Operand(esp, 0));
2844
2845 __ mov(scratch, right);
2846 __ SmiUntag(scratch);
2847 __ mov(Operand(esp, 0), scratch);
2848 __ fild_s(Operand(esp, 0));
2849 __ pop(scratch);
2850}
2851
2852
2853void FloatingPointHelper::CheckFloatOperands(MacroAssembler* masm,
2854 Label* non_float,
2855 Register scratch) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002856 Label test_other, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002857 // Test if both operands are floats or smi -> scratch=k_is_float;
2858 // Otherwise scratch = k_not_float.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002859 __ JumpIfSmi(edx, &test_other, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002860 __ mov(scratch, FieldOperand(edx, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002861 Factory* factory = masm->isolate()->factory();
2862 __ cmp(scratch, factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002863 __ j(not_equal, non_float); // argument in edx is not a number -> NaN
2864
2865 __ bind(&test_other);
whesse@chromium.org7b260152011-06-20 15:33:18 +00002866 __ JumpIfSmi(eax, &done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002867 __ mov(scratch, FieldOperand(eax, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002868 __ cmp(scratch, factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002869 __ j(not_equal, non_float); // argument in eax is not a number -> NaN
2870
2871 // Fall-through: Both operands are numbers.
2872 __ bind(&done);
2873}
2874
2875
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002876void FloatingPointHelper::CheckFloatOperandsAreInt32(MacroAssembler* masm,
2877 Label* non_int32) {
2878 return;
2879}
2880
2881
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002882void MathPowStub::Generate(MacroAssembler* masm) {
2883 // Registers are used as follows:
2884 // edx = base
2885 // eax = exponent
2886 // ecx = temporary, result
2887
2888 CpuFeatures::Scope use_sse2(SSE2);
2889 Label allocate_return, call_runtime;
2890
2891 // Load input parameters.
2892 __ mov(edx, Operand(esp, 2 * kPointerSize));
2893 __ mov(eax, Operand(esp, 1 * kPointerSize));
2894
2895 // Save 1 in xmm3 - we need this several times later on.
2896 __ mov(ecx, Immediate(1));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002897 __ cvtsi2sd(xmm3, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002898
2899 Label exponent_nonsmi;
2900 Label base_nonsmi;
2901 // If the exponent is a heap number go to that specific case.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002902 __ JumpIfNotSmi(eax, &exponent_nonsmi);
2903 __ JumpIfNotSmi(edx, &base_nonsmi);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002904
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002905 // Optimized version when both exponent and base are smis.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002906 Label powi;
2907 __ SmiUntag(edx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002908 __ cvtsi2sd(xmm0, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002909 __ jmp(&powi);
2910 // exponent is smi and base is a heapnumber.
2911 __ bind(&base_nonsmi);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002912 Factory* factory = masm->isolate()->factory();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002913 __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002914 factory->heap_number_map());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002915 __ j(not_equal, &call_runtime);
2916
2917 __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
2918
2919 // Optimized version of pow if exponent is a smi.
2920 // xmm0 contains the base.
2921 __ bind(&powi);
2922 __ SmiUntag(eax);
2923
2924 // Save exponent in base as we need to check if exponent is negative later.
2925 // We know that base and exponent are in different registers.
2926 __ mov(edx, eax);
2927
2928 // Get absolute value of exponent.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002929 Label no_neg;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002930 __ cmp(eax, 0);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002931 __ j(greater_equal, &no_neg, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002932 __ neg(eax);
2933 __ bind(&no_neg);
2934
2935 // Load xmm1 with 1.
2936 __ movsd(xmm1, xmm3);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002937 Label while_true;
2938 Label no_multiply;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002939
2940 __ bind(&while_true);
2941 __ shr(eax, 1);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002942 __ j(not_carry, &no_multiply, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002943 __ mulsd(xmm1, xmm0);
2944 __ bind(&no_multiply);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002945 __ mulsd(xmm0, xmm0);
2946 __ j(not_zero, &while_true);
2947
2948 // base has the original value of the exponent - if the exponent is
2949 // negative return 1/result.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002950 __ test(edx, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002951 __ j(positive, &allocate_return);
2952 // Special case if xmm1 has reached infinity.
2953 __ mov(ecx, Immediate(0x7FB00000));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002954 __ movd(xmm0, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002955 __ cvtss2sd(xmm0, xmm0);
2956 __ ucomisd(xmm0, xmm1);
2957 __ j(equal, &call_runtime);
2958 __ divsd(xmm3, xmm1);
2959 __ movsd(xmm1, xmm3);
2960 __ jmp(&allocate_return);
2961
2962 // exponent (or both) is a heapnumber - no matter what we should now work
2963 // on doubles.
2964 __ bind(&exponent_nonsmi);
2965 __ cmp(FieldOperand(eax, HeapObject::kMapOffset),
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002966 factory->heap_number_map());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002967 __ j(not_equal, &call_runtime);
2968 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
2969 // Test if exponent is nan.
2970 __ ucomisd(xmm1, xmm1);
2971 __ j(parity_even, &call_runtime);
2972
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002973 Label base_not_smi;
2974 Label handle_special_cases;
whesse@chromium.org7b260152011-06-20 15:33:18 +00002975 __ JumpIfNotSmi(edx, &base_not_smi, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002976 __ SmiUntag(edx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002977 __ cvtsi2sd(xmm0, edx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002978 __ jmp(&handle_special_cases, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002979
2980 __ bind(&base_not_smi);
2981 __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002982 factory->heap_number_map());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002983 __ j(not_equal, &call_runtime);
2984 __ mov(ecx, FieldOperand(edx, HeapNumber::kExponentOffset));
2985 __ and_(ecx, HeapNumber::kExponentMask);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002986 __ cmp(ecx, Immediate(HeapNumber::kExponentMask));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002987 // base is NaN or +/-Infinity
2988 __ j(greater_equal, &call_runtime);
2989 __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
2990
2991 // base is in xmm0 and exponent is in xmm1.
2992 __ bind(&handle_special_cases);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002993 Label not_minus_half;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002994 // Test for -0.5.
2995 // Load xmm2 with -0.5.
2996 __ mov(ecx, Immediate(0xBF000000));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002997 __ movd(xmm2, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002998 __ cvtss2sd(xmm2, xmm2);
2999 // xmm2 now has -0.5.
3000 __ ucomisd(xmm2, xmm1);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003001 __ j(not_equal, &not_minus_half, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003002
3003 // Calculates reciprocal of square root.
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00003004 // sqrtsd returns -0 when input is -0. ECMA spec requires +0.
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +00003005 __ xorps(xmm1, xmm1);
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00003006 __ addsd(xmm1, xmm0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003007 __ sqrtsd(xmm1, xmm1);
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00003008 __ divsd(xmm3, xmm1);
3009 __ movsd(xmm1, xmm3);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003010 __ jmp(&allocate_return);
3011
3012 // Test for 0.5.
3013 __ bind(&not_minus_half);
3014 // Load xmm2 with 0.5.
3015 // Since xmm3 is 1 and xmm2 is -0.5 this is simply xmm2 + xmm3.
3016 __ addsd(xmm2, xmm3);
3017 // xmm2 now has 0.5.
3018 __ ucomisd(xmm2, xmm1);
3019 __ j(not_equal, &call_runtime);
3020 // Calculates square root.
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00003021 // sqrtsd returns -0 when input is -0. ECMA spec requires +0.
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +00003022 __ xorps(xmm1, xmm1);
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00003023 __ addsd(xmm1, xmm0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003024 __ sqrtsd(xmm1, xmm1);
3025
3026 __ bind(&allocate_return);
3027 __ AllocateHeapNumber(ecx, eax, edx, &call_runtime);
3028 __ movdbl(FieldOperand(ecx, HeapNumber::kValueOffset), xmm1);
3029 __ mov(eax, ecx);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003030 __ ret(2 * kPointerSize);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003031
3032 __ bind(&call_runtime);
3033 __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1);
3034}
3035
3036
ricow@chromium.org65fae842010-08-25 15:26:24 +00003037void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
3038 // The key is in edx and the parameter count is in eax.
3039
3040 // The displacement is used for skipping the frame pointer on the
3041 // stack. It is the offset of the last parameter (if any) relative
3042 // to the frame pointer.
3043 static const int kDisplacement = 1 * kPointerSize;
3044
3045 // Check that the key is a smi.
3046 Label slow;
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003047 __ JumpIfNotSmi(edx, &slow, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003048
3049 // Check if the calling frame is an arguments adaptor frame.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003050 Label adaptor;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003051 __ mov(ebx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
3052 __ mov(ecx, Operand(ebx, StandardFrameConstants::kContextOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003053 __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003054 __ j(equal, &adaptor, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003055
3056 // Check index against formal parameters count limit passed in
3057 // through register eax. Use unsigned comparison to get negative
3058 // check for free.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003059 __ cmp(edx, eax);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003060 __ j(above_equal, &slow, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003061
3062 // Read the argument from the stack and return it.
3063 STATIC_ASSERT(kSmiTagSize == 1);
3064 STATIC_ASSERT(kSmiTag == 0); // Shifting code depends on these.
3065 __ lea(ebx, Operand(ebp, eax, times_2, 0));
3066 __ neg(edx);
3067 __ mov(eax, Operand(ebx, edx, times_2, kDisplacement));
3068 __ ret(0);
3069
3070 // Arguments adaptor case: Check index against actual arguments
3071 // limit found in the arguments adaptor frame. Use unsigned
3072 // comparison to get negative check for free.
3073 __ bind(&adaptor);
3074 __ mov(ecx, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003075 __ cmp(edx, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003076 __ j(above_equal, &slow, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003077
3078 // Read the argument from the stack and return it.
3079 STATIC_ASSERT(kSmiTagSize == 1);
3080 STATIC_ASSERT(kSmiTag == 0); // Shifting code depends on these.
3081 __ lea(ebx, Operand(ebx, ecx, times_2, 0));
3082 __ neg(edx);
3083 __ mov(eax, Operand(ebx, edx, times_2, kDisplacement));
3084 __ ret(0);
3085
3086 // Slow-case: Handle non-smi or out-of-bounds access to arguments
3087 // by calling the runtime system.
3088 __ bind(&slow);
3089 __ pop(ebx); // Return address.
3090 __ push(edx);
3091 __ push(ebx);
3092 __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
3093}
3094
3095
whesse@chromium.org7b260152011-06-20 15:33:18 +00003096void ArgumentsAccessStub::GenerateNewNonStrictSlow(MacroAssembler* masm) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00003097 // esp[0] : return address
3098 // esp[4] : number of parameters
3099 // esp[8] : receiver displacement
whesse@chromium.org7b260152011-06-20 15:33:18 +00003100 // esp[12] : function
ricow@chromium.org65fae842010-08-25 15:26:24 +00003101
whesse@chromium.org7b260152011-06-20 15:33:18 +00003102 // Check if the calling frame is an arguments adaptor frame.
3103 Label runtime;
3104 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
3105 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003106 __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003107 __ j(not_equal, &runtime, Label::kNear);
3108
3109 // Patch the arguments.length and the parameters pointer.
3110 __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
3111 __ mov(Operand(esp, 1 * kPointerSize), ecx);
3112 __ lea(edx, Operand(edx, ecx, times_2,
3113 StandardFrameConstants::kCallerSPOffset));
3114 __ mov(Operand(esp, 2 * kPointerSize), edx);
3115
3116 __ bind(&runtime);
3117 __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
3118}
3119
3120
3121void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) {
3122 // esp[0] : return address
3123 // esp[4] : number of parameters (tagged)
3124 // esp[8] : receiver displacement
3125 // esp[12] : function
3126
3127 // ebx = parameter count (tagged)
3128 __ mov(ebx, Operand(esp, 1 * kPointerSize));
3129
3130 // Check if the calling frame is an arguments adaptor frame.
3131 // TODO(rossberg): Factor out some of the bits that are shared with the other
3132 // Generate* functions.
3133 Label runtime;
3134 Label adaptor_frame, try_allocate;
3135 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
3136 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003137 __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003138 __ j(equal, &adaptor_frame, Label::kNear);
3139
3140 // No adaptor, parameter count = argument count.
3141 __ mov(ecx, ebx);
3142 __ jmp(&try_allocate, Label::kNear);
3143
3144 // We have an adaptor frame. Patch the parameters pointer.
3145 __ bind(&adaptor_frame);
3146 __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
3147 __ lea(edx, Operand(edx, ecx, times_2,
3148 StandardFrameConstants::kCallerSPOffset));
3149 __ mov(Operand(esp, 2 * kPointerSize), edx);
3150
3151 // ebx = parameter count (tagged)
3152 // ecx = argument count (tagged)
3153 // esp[4] = parameter count (tagged)
3154 // esp[8] = address of receiver argument
3155 // Compute the mapped parameter count = min(ebx, ecx) in ebx.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003156 __ cmp(ebx, ecx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003157 __ j(less_equal, &try_allocate, Label::kNear);
3158 __ mov(ebx, ecx);
3159
3160 __ bind(&try_allocate);
3161
3162 // Save mapped parameter count.
3163 __ push(ebx);
3164
3165 // Compute the sizes of backing store, parameter map, and arguments object.
3166 // 1. Parameter map, has 2 extra words containing context and backing store.
3167 const int kParameterMapHeaderSize =
3168 FixedArray::kHeaderSize + 2 * kPointerSize;
3169 Label no_parameter_map;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003170 __ test(ebx, ebx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003171 __ j(zero, &no_parameter_map, Label::kNear);
3172 __ lea(ebx, Operand(ebx, times_2, kParameterMapHeaderSize));
3173 __ bind(&no_parameter_map);
3174
3175 // 2. Backing store.
3176 __ lea(ebx, Operand(ebx, ecx, times_2, FixedArray::kHeaderSize));
3177
3178 // 3. Arguments object.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003179 __ add(ebx, Immediate(Heap::kArgumentsObjectSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003180
3181 // Do the allocation of all three objects in one go.
3182 __ AllocateInNewSpace(ebx, eax, edx, edi, &runtime, TAG_OBJECT);
3183
3184 // eax = address of new object(s) (tagged)
3185 // ecx = argument count (tagged)
3186 // esp[0] = mapped parameter count (tagged)
3187 // esp[8] = parameter count (tagged)
3188 // esp[12] = address of receiver argument
3189 // Get the arguments boilerplate from the current (global) context into edi.
3190 Label has_mapped_parameters, copy;
3191 __ mov(edi, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
3192 __ mov(edi, FieldOperand(edi, GlobalObject::kGlobalContextOffset));
3193 __ mov(ebx, Operand(esp, 0 * kPointerSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003194 __ test(ebx, ebx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003195 __ j(not_zero, &has_mapped_parameters, Label::kNear);
3196 __ mov(edi, Operand(edi,
3197 Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX)));
3198 __ jmp(&copy, Label::kNear);
3199
3200 __ bind(&has_mapped_parameters);
3201 __ mov(edi, Operand(edi,
3202 Context::SlotOffset(Context::ALIASED_ARGUMENTS_BOILERPLATE_INDEX)));
3203 __ bind(&copy);
3204
3205 // eax = address of new object (tagged)
3206 // ebx = mapped parameter count (tagged)
3207 // ecx = argument count (tagged)
3208 // edi = address of boilerplate object (tagged)
3209 // esp[0] = mapped parameter count (tagged)
3210 // esp[8] = parameter count (tagged)
3211 // esp[12] = address of receiver argument
3212 // Copy the JS object part.
3213 for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
3214 __ mov(edx, FieldOperand(edi, i));
3215 __ mov(FieldOperand(eax, i), edx);
3216 }
3217
3218 // Setup the callee in-object property.
3219 STATIC_ASSERT(Heap::kArgumentsCalleeIndex == 1);
3220 __ mov(edx, Operand(esp, 4 * kPointerSize));
3221 __ mov(FieldOperand(eax, JSObject::kHeaderSize +
3222 Heap::kArgumentsCalleeIndex * kPointerSize),
3223 edx);
3224
3225 // Use the length (smi tagged) and set that as an in-object property too.
3226 STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0);
3227 __ mov(FieldOperand(eax, JSObject::kHeaderSize +
3228 Heap::kArgumentsLengthIndex * kPointerSize),
3229 ecx);
3230
3231 // Setup the elements pointer in the allocated arguments object.
3232 // If we allocated a parameter map, edi will point there, otherwise to the
3233 // backing store.
3234 __ lea(edi, Operand(eax, Heap::kArgumentsObjectSize));
3235 __ mov(FieldOperand(eax, JSObject::kElementsOffset), edi);
3236
3237 // eax = address of new object (tagged)
3238 // ebx = mapped parameter count (tagged)
3239 // ecx = argument count (tagged)
3240 // edi = address of parameter map or backing store (tagged)
3241 // esp[0] = mapped parameter count (tagged)
3242 // esp[8] = parameter count (tagged)
3243 // esp[12] = address of receiver argument
3244 // Free a register.
3245 __ push(eax);
3246
3247 // Initialize parameter map. If there are no mapped arguments, we're done.
3248 Label skip_parameter_map;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003249 __ test(ebx, ebx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003250 __ j(zero, &skip_parameter_map);
3251
3252 __ mov(FieldOperand(edi, FixedArray::kMapOffset),
3253 Immediate(FACTORY->non_strict_arguments_elements_map()));
3254 __ lea(eax, Operand(ebx, reinterpret_cast<intptr_t>(Smi::FromInt(2))));
3255 __ mov(FieldOperand(edi, FixedArray::kLengthOffset), eax);
3256 __ mov(FieldOperand(edi, FixedArray::kHeaderSize + 0 * kPointerSize), esi);
3257 __ lea(eax, Operand(edi, ebx, times_2, kParameterMapHeaderSize));
3258 __ mov(FieldOperand(edi, FixedArray::kHeaderSize + 1 * kPointerSize), eax);
3259
3260 // Copy the parameter slots and the holes in the arguments.
3261 // We need to fill in mapped_parameter_count slots. They index the context,
3262 // where parameters are stored in reverse order, at
3263 // MIN_CONTEXT_SLOTS .. MIN_CONTEXT_SLOTS+parameter_count-1
3264 // The mapped parameter thus need to get indices
3265 // MIN_CONTEXT_SLOTS+parameter_count-1 ..
3266 // MIN_CONTEXT_SLOTS+parameter_count-mapped_parameter_count
3267 // We loop from right to left.
3268 Label parameters_loop, parameters_test;
3269 __ push(ecx);
3270 __ mov(eax, Operand(esp, 2 * kPointerSize));
3271 __ mov(ebx, Immediate(Smi::FromInt(Context::MIN_CONTEXT_SLOTS)));
3272 __ add(ebx, Operand(esp, 4 * kPointerSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003273 __ sub(ebx, eax);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003274 __ mov(ecx, FACTORY->the_hole_value());
3275 __ mov(edx, edi);
3276 __ lea(edi, Operand(edi, eax, times_2, kParameterMapHeaderSize));
3277 // eax = loop variable (tagged)
3278 // ebx = mapping index (tagged)
3279 // ecx = the hole value
3280 // edx = address of parameter map (tagged)
3281 // edi = address of backing store (tagged)
3282 // esp[0] = argument count (tagged)
3283 // esp[4] = address of new object (tagged)
3284 // esp[8] = mapped parameter count (tagged)
3285 // esp[16] = parameter count (tagged)
3286 // esp[20] = address of receiver argument
3287 __ jmp(&parameters_test, Label::kNear);
3288
3289 __ bind(&parameters_loop);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003290 __ sub(eax, Immediate(Smi::FromInt(1)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003291 __ mov(FieldOperand(edx, eax, times_2, kParameterMapHeaderSize), ebx);
3292 __ mov(FieldOperand(edi, eax, times_2, FixedArray::kHeaderSize), ecx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003293 __ add(ebx, Immediate(Smi::FromInt(1)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003294 __ bind(&parameters_test);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003295 __ test(eax, eax);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003296 __ j(not_zero, &parameters_loop, Label::kNear);
3297 __ pop(ecx);
3298
3299 __ bind(&skip_parameter_map);
3300
3301 // ecx = argument count (tagged)
3302 // edi = address of backing store (tagged)
3303 // esp[0] = address of new object (tagged)
3304 // esp[4] = mapped parameter count (tagged)
3305 // esp[12] = parameter count (tagged)
3306 // esp[16] = address of receiver argument
3307 // Copy arguments header and remaining slots (if there are any).
3308 __ mov(FieldOperand(edi, FixedArray::kMapOffset),
3309 Immediate(FACTORY->fixed_array_map()));
3310 __ mov(FieldOperand(edi, FixedArray::kLengthOffset), ecx);
3311
3312 Label arguments_loop, arguments_test;
3313 __ mov(ebx, Operand(esp, 1 * kPointerSize));
3314 __ mov(edx, Operand(esp, 4 * kPointerSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003315 __ sub(edx, ebx); // Is there a smarter way to do negative scaling?
3316 __ sub(edx, ebx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003317 __ jmp(&arguments_test, Label::kNear);
3318
3319 __ bind(&arguments_loop);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003320 __ sub(edx, Immediate(kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003321 __ mov(eax, Operand(edx, 0));
3322 __ mov(FieldOperand(edi, ebx, times_2, FixedArray::kHeaderSize), eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003323 __ add(ebx, Immediate(Smi::FromInt(1)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003324
3325 __ bind(&arguments_test);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003326 __ cmp(ebx, ecx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003327 __ j(less, &arguments_loop, Label::kNear);
3328
3329 // Restore.
3330 __ pop(eax); // Address of arguments object.
3331 __ pop(ebx); // Parameter count.
3332
3333 // Return and remove the on-stack parameters.
3334 __ ret(3 * kPointerSize);
3335
3336 // Do the runtime call to allocate the arguments object.
3337 __ bind(&runtime);
3338 __ pop(eax); // Remove saved parameter count.
3339 __ mov(Operand(esp, 1 * kPointerSize), ecx); // Patch argument count.
3340 __ TailCallRuntime(Runtime::kNewStrictArgumentsFast, 3, 1);
3341}
3342
3343
3344void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) {
3345 // esp[0] : return address
3346 // esp[4] : number of parameters
3347 // esp[8] : receiver displacement
3348 // esp[12] : function
ricow@chromium.org65fae842010-08-25 15:26:24 +00003349
3350 // Check if the calling frame is an arguments adaptor frame.
3351 Label adaptor_frame, try_allocate, runtime;
3352 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
3353 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003354 __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003355 __ j(equal, &adaptor_frame, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003356
3357 // Get the length from the frame.
3358 __ mov(ecx, Operand(esp, 1 * kPointerSize));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003359 __ jmp(&try_allocate, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003360
3361 // Patch the arguments.length and the parameters pointer.
3362 __ bind(&adaptor_frame);
3363 __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
3364 __ mov(Operand(esp, 1 * kPointerSize), ecx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003365 __ lea(edx, Operand(edx, ecx, times_2,
3366 StandardFrameConstants::kCallerSPOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003367 __ mov(Operand(esp, 2 * kPointerSize), edx);
3368
3369 // Try the new space allocation. Start out with computing the size of
3370 // the arguments object and the elements array.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003371 Label add_arguments_object;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003372 __ bind(&try_allocate);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003373 __ test(ecx, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003374 __ j(zero, &add_arguments_object, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003375 __ lea(ecx, Operand(ecx, times_2, FixedArray::kHeaderSize));
3376 __ bind(&add_arguments_object);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003377 __ add(ecx, Immediate(Heap::kArgumentsObjectSizeStrict));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003378
3379 // Do the allocation of both objects in one go.
3380 __ AllocateInNewSpace(ecx, eax, edx, ebx, &runtime, TAG_OBJECT);
3381
3382 // Get the arguments boilerplate from the current (global) context.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003383 __ mov(edi, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
3384 __ mov(edi, FieldOperand(edi, GlobalObject::kGlobalContextOffset));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003385 const int offset =
3386 Context::SlotOffset(Context::STRICT_MODE_ARGUMENTS_BOILERPLATE_INDEX);
3387 __ mov(edi, Operand(edi, offset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003388
3389 // Copy the JS object part.
3390 for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
3391 __ mov(ebx, FieldOperand(edi, i));
3392 __ mov(FieldOperand(eax, i), ebx);
3393 }
3394
ricow@chromium.org65fae842010-08-25 15:26:24 +00003395 // Get the length (smi tagged) and set that as an in-object property too.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003396 STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003397 __ mov(ecx, Operand(esp, 1 * kPointerSize));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003398 __ mov(FieldOperand(eax, JSObject::kHeaderSize +
whesse@chromium.org7b260152011-06-20 15:33:18 +00003399 Heap::kArgumentsLengthIndex * kPointerSize),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003400 ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003401
3402 // If there are no actual arguments, we're done.
3403 Label done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003404 __ test(ecx, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003405 __ j(zero, &done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003406
3407 // Get the parameters pointer from the stack.
3408 __ mov(edx, Operand(esp, 2 * kPointerSize));
3409
3410 // Setup the elements pointer in the allocated arguments object and
3411 // initialize the header in the elements fixed array.
whesse@chromium.org7b260152011-06-20 15:33:18 +00003412 __ lea(edi, Operand(eax, Heap::kArgumentsObjectSizeStrict));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003413 __ mov(FieldOperand(eax, JSObject::kElementsOffset), edi);
3414 __ mov(FieldOperand(edi, FixedArray::kMapOffset),
whesse@chromium.org7b260152011-06-20 15:33:18 +00003415 Immediate(FACTORY->fixed_array_map()));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003416
ricow@chromium.org65fae842010-08-25 15:26:24 +00003417 __ mov(FieldOperand(edi, FixedArray::kLengthOffset), ecx);
3418 // Untag the length for the loop below.
3419 __ SmiUntag(ecx);
3420
3421 // Copy the fixed array slots.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003422 Label loop;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003423 __ bind(&loop);
3424 __ mov(ebx, Operand(edx, -1 * kPointerSize)); // Skip receiver.
3425 __ mov(FieldOperand(edi, FixedArray::kHeaderSize), ebx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003426 __ add(edi, Immediate(kPointerSize));
3427 __ sub(edx, Immediate(kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003428 __ dec(ecx);
3429 __ j(not_zero, &loop);
3430
3431 // Return and remove the on-stack parameters.
3432 __ bind(&done);
3433 __ ret(3 * kPointerSize);
3434
3435 // Do the runtime call to allocate the arguments object.
3436 __ bind(&runtime);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003437 __ TailCallRuntime(Runtime::kNewStrictArgumentsFast, 3, 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003438}
3439
3440
3441void RegExpExecStub::Generate(MacroAssembler* masm) {
3442 // Just jump directly to runtime if native RegExp is not selected at compile
3443 // time or if regexp entry in generated code is turned off runtime switch or
3444 // at compilation.
3445#ifdef V8_INTERPRETED_REGEXP
3446 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
3447#else // V8_INTERPRETED_REGEXP
ricow@chromium.org65fae842010-08-25 15:26:24 +00003448
3449 // Stack frame on entry.
3450 // esp[0]: return address
3451 // esp[4]: last_match_info (expected JSArray)
3452 // esp[8]: previous index
3453 // esp[12]: subject string
3454 // esp[16]: JSRegExp object
3455
3456 static const int kLastMatchInfoOffset = 1 * kPointerSize;
3457 static const int kPreviousIndexOffset = 2 * kPointerSize;
3458 static const int kSubjectOffset = 3 * kPointerSize;
3459 static const int kJSRegExpOffset = 4 * kPointerSize;
3460
3461 Label runtime, invoke_regexp;
3462
3463 // Ensure that a RegExp stack is allocated.
3464 ExternalReference address_of_regexp_stack_memory_address =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003465 ExternalReference::address_of_regexp_stack_memory_address(
3466 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003467 ExternalReference address_of_regexp_stack_memory_size =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003468 ExternalReference::address_of_regexp_stack_memory_size(masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003469 __ mov(ebx, Operand::StaticVariable(address_of_regexp_stack_memory_size));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003470 __ test(ebx, ebx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00003471 __ j(zero, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003472
3473 // Check that the first argument is a JSRegExp object.
3474 __ mov(eax, Operand(esp, kJSRegExpOffset));
3475 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003476 __ JumpIfSmi(eax, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003477 __ CmpObjectType(eax, JS_REGEXP_TYPE, ecx);
3478 __ j(not_equal, &runtime);
3479 // Check that the RegExp has been compiled (data contains a fixed array).
3480 __ mov(ecx, FieldOperand(eax, JSRegExp::kDataOffset));
3481 if (FLAG_debug_code) {
3482 __ test(ecx, Immediate(kSmiTagMask));
3483 __ Check(not_zero, "Unexpected type for RegExp data, FixedArray expected");
3484 __ CmpObjectType(ecx, FIXED_ARRAY_TYPE, ebx);
3485 __ Check(equal, "Unexpected type for RegExp data, FixedArray expected");
3486 }
3487
3488 // ecx: RegExp data (FixedArray)
3489 // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP.
3490 __ mov(ebx, FieldOperand(ecx, JSRegExp::kDataTagOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003491 __ cmp(ebx, Immediate(Smi::FromInt(JSRegExp::IRREGEXP)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003492 __ j(not_equal, &runtime);
3493
3494 // ecx: RegExp data (FixedArray)
3495 // Check that the number of captures fit in the static offsets vector buffer.
3496 __ mov(edx, FieldOperand(ecx, JSRegExp::kIrregexpCaptureCountOffset));
3497 // Calculate number of capture registers (number_of_captures + 1) * 2. This
3498 // uses the asumption that smis are 2 * their untagged value.
3499 STATIC_ASSERT(kSmiTag == 0);
3500 STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003501 __ add(edx, Immediate(2)); // edx was a smi.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003502 // Check that the static offsets vector buffer is large enough.
3503 __ cmp(edx, OffsetsVector::kStaticOffsetsVectorSize);
3504 __ j(above, &runtime);
3505
3506 // ecx: RegExp data (FixedArray)
3507 // edx: Number of capture registers
3508 // Check that the second argument is a string.
3509 __ mov(eax, Operand(esp, kSubjectOffset));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003510 __ JumpIfSmi(eax, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003511 Condition is_string = masm->IsObjectStringType(eax, ebx, ebx);
3512 __ j(NegateCondition(is_string), &runtime);
3513 // Get the length of the string to ebx.
3514 __ mov(ebx, FieldOperand(eax, String::kLengthOffset));
3515
3516 // ebx: Length of subject string as a smi
3517 // ecx: RegExp data (FixedArray)
3518 // edx: Number of capture registers
3519 // Check that the third argument is a positive smi less than the subject
3520 // string length. A negative value will be greater (unsigned comparison).
3521 __ mov(eax, Operand(esp, kPreviousIndexOffset));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003522 __ JumpIfNotSmi(eax, &runtime);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003523 __ cmp(eax, ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003524 __ j(above_equal, &runtime);
3525
3526 // ecx: RegExp data (FixedArray)
3527 // edx: Number of capture registers
3528 // Check that the fourth object is a JSArray object.
3529 __ mov(eax, Operand(esp, kLastMatchInfoOffset));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003530 __ JumpIfSmi(eax, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003531 __ CmpObjectType(eax, JS_ARRAY_TYPE, ebx);
3532 __ j(not_equal, &runtime);
3533 // Check that the JSArray is in fast case.
3534 __ mov(ebx, FieldOperand(eax, JSArray::kElementsOffset));
3535 __ mov(eax, FieldOperand(ebx, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003536 Factory* factory = masm->isolate()->factory();
3537 __ cmp(eax, factory->fixed_array_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003538 __ j(not_equal, &runtime);
3539 // Check that the last match info has space for the capture registers and the
3540 // additional information.
3541 __ mov(eax, FieldOperand(ebx, FixedArray::kLengthOffset));
3542 __ SmiUntag(eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003543 __ add(edx, Immediate(RegExpImpl::kLastMatchOverhead));
3544 __ cmp(edx, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003545 __ j(greater, &runtime);
3546
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003547 // Reset offset for possibly sliced string.
3548 __ Set(edi, Immediate(0));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003549 // ecx: RegExp data (FixedArray)
3550 // Check the representation and encoding of the subject string.
3551 Label seq_ascii_string, seq_two_byte_string, check_code;
3552 __ mov(eax, Operand(esp, kSubjectOffset));
3553 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
3554 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
3555 // First check for flat two byte string.
3556 __ and_(ebx,
3557 kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask);
3558 STATIC_ASSERT((kStringTag | kSeqStringTag | kTwoByteStringTag) == 0);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003559 __ j(zero, &seq_two_byte_string, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003560 // Any other flat string must be a flat ascii string.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003561 __ and_(ebx, Immediate(kIsNotStringMask | kStringRepresentationMask));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003562 __ j(zero, &seq_ascii_string, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003563
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003564 // Check for flat cons string or sliced string.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003565 // A flat cons string is a cons string where the second part is the empty
3566 // string. In that case the subject string is just the first part of the cons
3567 // string. Also in this case the first part of the cons string is known to be
3568 // a sequential string or an external string.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003569 // In the case of a sliced string its offset has to be taken into account.
3570 Label cons_string, check_encoding;
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00003571 STATIC_ASSERT(kConsStringTag < kExternalStringTag);
3572 STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003573 __ cmp(ebx, Immediate(kExternalStringTag));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003574 __ j(less, &cons_string);
3575 __ j(equal, &runtime);
3576
3577 // String is sliced.
3578 __ mov(edi, FieldOperand(eax, SlicedString::kOffsetOffset));
3579 __ mov(eax, FieldOperand(eax, SlicedString::kParentOffset));
3580 // edi: offset of sliced string, smi-tagged.
3581 // eax: parent string.
3582 __ jmp(&check_encoding, Label::kNear);
3583 // String is a cons string, check whether it is flat.
3584 __ bind(&cons_string);
3585 __ cmp(FieldOperand(eax, ConsString::kSecondOffset), factory->empty_string());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003586 __ j(not_equal, &runtime);
3587 __ mov(eax, FieldOperand(eax, ConsString::kFirstOffset));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003588 __ bind(&check_encoding);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003589 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003590 // eax: first part of cons string or parent of sliced string.
3591 // ebx: map of first part of cons string or map of parent of sliced string.
3592 // Is first part of cons or parent of slice a flat two byte string?
ricow@chromium.org65fae842010-08-25 15:26:24 +00003593 __ test_b(FieldOperand(ebx, Map::kInstanceTypeOffset),
3594 kStringRepresentationMask | kStringEncodingMask);
3595 STATIC_ASSERT((kSeqStringTag | kTwoByteStringTag) == 0);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003596 __ j(zero, &seq_two_byte_string, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003597 // Any other flat string must be ascii.
3598 __ test_b(FieldOperand(ebx, Map::kInstanceTypeOffset),
3599 kStringRepresentationMask);
3600 __ j(not_zero, &runtime);
3601
3602 __ bind(&seq_ascii_string);
3603 // eax: subject string (flat ascii)
3604 // ecx: RegExp data (FixedArray)
3605 __ mov(edx, FieldOperand(ecx, JSRegExp::kDataAsciiCodeOffset));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003606 __ Set(ecx, Immediate(1)); // Type is ascii.
3607 __ jmp(&check_code, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003608
3609 __ bind(&seq_two_byte_string);
3610 // eax: subject string (flat two byte)
3611 // ecx: RegExp data (FixedArray)
3612 __ mov(edx, FieldOperand(ecx, JSRegExp::kDataUC16CodeOffset));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003613 __ Set(ecx, Immediate(0)); // Type is two byte.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003614
3615 __ bind(&check_code);
3616 // Check that the irregexp code has been generated for the actual string
3617 // encoding. If it has, the field contains a code object otherwise it contains
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00003618 // a smi (code flushing support).
3619 __ JumpIfSmi(edx, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003620
3621 // eax: subject string
3622 // edx: code
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003623 // ecx: encoding of subject string (1 if ascii, 0 if two_byte);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003624 // Load used arguments before starting to push arguments for call to native
3625 // RegExp code to avoid handling changing stack height.
3626 __ mov(ebx, Operand(esp, kPreviousIndexOffset));
3627 __ SmiUntag(ebx); // Previous index from smi.
3628
3629 // eax: subject string
3630 // ebx: previous index
3631 // edx: code
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003632 // ecx: encoding of subject string (1 if ascii 0 if two_byte);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003633 // All checks done. Now push arguments for native regexp code.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003634 Counters* counters = masm->isolate()->counters();
3635 __ IncrementCounter(counters->regexp_entry_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003636
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003637 // Isolates: note we add an additional parameter here (isolate pointer).
3638 static const int kRegExpExecuteArguments = 8;
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003639 __ EnterApiExitFrame(kRegExpExecuteArguments);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003640
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003641 // Argument 8: Pass current isolate address.
3642 __ mov(Operand(esp, 7 * kPointerSize),
3643 Immediate(ExternalReference::isolate_address()));
3644
ricow@chromium.org65fae842010-08-25 15:26:24 +00003645 // Argument 7: Indicate that this is a direct call from JavaScript.
3646 __ mov(Operand(esp, 6 * kPointerSize), Immediate(1));
3647
3648 // Argument 6: Start (high end) of backtracking stack memory area.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003649 __ mov(esi, Operand::StaticVariable(address_of_regexp_stack_memory_address));
3650 __ add(esi, Operand::StaticVariable(address_of_regexp_stack_memory_size));
3651 __ mov(Operand(esp, 5 * kPointerSize), esi);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003652
3653 // Argument 5: static offsets vector buffer.
3654 __ mov(Operand(esp, 4 * kPointerSize),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003655 Immediate(ExternalReference::address_of_static_offsets_vector(
3656 masm->isolate())));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003657
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003658 // Argument 2: Previous index.
3659 __ mov(Operand(esp, 1 * kPointerSize), ebx);
3660
3661 // Argument 1: Original subject string.
3662 // The original subject is in the previous stack frame. Therefore we have to
3663 // use ebp, which points exactly to one pointer size below the previous esp.
3664 // (Because creating a new stack frame pushes the previous ebp onto the stack
3665 // and thereby moves up esp by one kPointerSize.)
3666 __ mov(esi, Operand(ebp, kSubjectOffset + kPointerSize));
3667 __ mov(Operand(esp, 0 * kPointerSize), esi);
3668
3669 // esi: original subject string
3670 // eax: underlying subject string
3671 // ebx: previous index
3672 // ecx: encoding of subject string (1 if ascii 0 if two_byte);
3673 // edx: code
ricow@chromium.org65fae842010-08-25 15:26:24 +00003674 // Argument 4: End of string data
3675 // Argument 3: Start of string data
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003676 // Prepare start and end index of the input.
3677 // Load the length from the original sliced string if that is the case.
3678 __ mov(esi, FieldOperand(esi, String::kLengthOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003679 __ add(esi, edi); // Calculate input end wrt offset.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003680 __ SmiUntag(edi);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003681 __ add(ebx, edi); // Calculate input start wrt offset.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003682
3683 // ebx: start index of the input string
3684 // esi: end index of the input string
3685 Label setup_two_byte, setup_rest;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003686 __ test(ecx, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003687 __ j(zero, &setup_two_byte, Label::kNear);
3688 __ SmiUntag(esi);
3689 __ lea(ecx, FieldOperand(eax, esi, times_1, SeqAsciiString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003690 __ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
3691 __ lea(ecx, FieldOperand(eax, ebx, times_1, SeqAsciiString::kHeaderSize));
3692 __ mov(Operand(esp, 2 * kPointerSize), ecx); // Argument 3.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003693 __ jmp(&setup_rest, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003694
3695 __ bind(&setup_two_byte);
3696 STATIC_ASSERT(kSmiTag == 0);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003697 STATIC_ASSERT(kSmiTagSize == 1); // esi is smi (powered by 2).
3698 __ lea(ecx, FieldOperand(eax, esi, times_1, SeqTwoByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003699 __ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
3700 __ lea(ecx, FieldOperand(eax, ebx, times_2, SeqTwoByteString::kHeaderSize));
3701 __ mov(Operand(esp, 2 * kPointerSize), ecx); // Argument 3.
3702
3703 __ bind(&setup_rest);
3704
ricow@chromium.org65fae842010-08-25 15:26:24 +00003705 // Locate the code entry and call it.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003706 __ add(edx, Immediate(Code::kHeaderSize - kHeapObjectTag));
3707 __ call(edx);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003708
3709 // Drop arguments and come back to JS mode.
3710 __ LeaveApiExitFrame();
ricow@chromium.org65fae842010-08-25 15:26:24 +00003711
3712 // Check the result.
3713 Label success;
3714 __ cmp(eax, NativeRegExpMacroAssembler::SUCCESS);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00003715 __ j(equal, &success);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003716 Label failure;
3717 __ cmp(eax, NativeRegExpMacroAssembler::FAILURE);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00003718 __ j(equal, &failure);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003719 __ cmp(eax, NativeRegExpMacroAssembler::EXCEPTION);
3720 // If not exception it can only be retry. Handle that in the runtime system.
3721 __ j(not_equal, &runtime);
3722 // Result must now be exception. If there is no pending exception already a
3723 // stack overflow (on the backtrack stack) was detected in RegExp code but
3724 // haven't created the exception yet. Handle that in the runtime system.
3725 // TODO(592): Rerunning the RegExp to get the stack overflow exception.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00003726 ExternalReference pending_exception(Isolate::kPendingExceptionAddress,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003727 masm->isolate());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003728 __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003729 __ mov(eax, Operand::StaticVariable(pending_exception));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003730 __ cmp(edx, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003731 __ j(equal, &runtime);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003732 // For exception, throw the exception again.
3733
3734 // Clear the pending exception variable.
3735 __ mov(Operand::StaticVariable(pending_exception), edx);
3736
3737 // Special handling of termination exceptions which are uncatchable
3738 // by javascript code.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003739 __ cmp(eax, factory->termination_exception());
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003740 Label throw_termination_exception;
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003741 __ j(equal, &throw_termination_exception, Label::kNear);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003742
3743 // Handle normal exception by following handler chain.
3744 __ Throw(eax);
3745
3746 __ bind(&throw_termination_exception);
3747 __ ThrowUncatchable(TERMINATION, eax);
3748
ricow@chromium.org65fae842010-08-25 15:26:24 +00003749 __ bind(&failure);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003750 // For failure to match, return null.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003751 __ mov(eax, factory->null_value());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003752 __ ret(4 * kPointerSize);
3753
3754 // Load RegExp data.
3755 __ bind(&success);
3756 __ mov(eax, Operand(esp, kJSRegExpOffset));
3757 __ mov(ecx, FieldOperand(eax, JSRegExp::kDataOffset));
3758 __ mov(edx, FieldOperand(ecx, JSRegExp::kIrregexpCaptureCountOffset));
3759 // Calculate number of capture registers (number_of_captures + 1) * 2.
3760 STATIC_ASSERT(kSmiTag == 0);
3761 STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003762 __ add(edx, Immediate(2)); // edx was a smi.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003763
3764 // edx: Number of capture registers
3765 // Load last_match_info which is still known to be a fast case JSArray.
3766 __ mov(eax, Operand(esp, kLastMatchInfoOffset));
3767 __ mov(ebx, FieldOperand(eax, JSArray::kElementsOffset));
3768
3769 // ebx: last_match_info backing store (FixedArray)
3770 // edx: number of capture registers
3771 // Store the capture count.
3772 __ SmiTag(edx); // Number of capture registers to smi.
3773 __ mov(FieldOperand(ebx, RegExpImpl::kLastCaptureCountOffset), edx);
3774 __ SmiUntag(edx); // Number of capture registers back from smi.
3775 // Store last subject and last input.
3776 __ mov(eax, Operand(esp, kSubjectOffset));
3777 __ mov(FieldOperand(ebx, RegExpImpl::kLastSubjectOffset), eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003778 __ RecordWriteField(ebx,
3779 RegExpImpl::kLastSubjectOffset,
3780 eax,
3781 edi,
3782 kDontSaveFPRegs);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003783 __ mov(eax, Operand(esp, kSubjectOffset));
3784 __ mov(FieldOperand(ebx, RegExpImpl::kLastInputOffset), eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003785 __ RecordWriteField(ebx,
3786 RegExpImpl::kLastInputOffset,
3787 eax,
3788 edi,
3789 kDontSaveFPRegs);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003790
3791 // Get the static offsets vector filled by the native regexp code.
3792 ExternalReference address_of_static_offsets_vector =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003793 ExternalReference::address_of_static_offsets_vector(masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003794 __ mov(ecx, Immediate(address_of_static_offsets_vector));
3795
3796 // ebx: last_match_info backing store (FixedArray)
3797 // ecx: offsets vector
3798 // edx: number of capture registers
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003799 Label next_capture, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003800 // Capture register counter starts from number of capture registers and
3801 // counts down until wraping after zero.
3802 __ bind(&next_capture);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003803 __ sub(edx, Immediate(1));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003804 __ j(negative, &done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003805 // Read the value from the static offsets vector buffer.
3806 __ mov(edi, Operand(ecx, edx, times_int_size, 0));
3807 __ SmiTag(edi);
3808 // Store the smi value in the last match info.
3809 __ mov(FieldOperand(ebx,
3810 edx,
3811 times_pointer_size,
3812 RegExpImpl::kFirstCaptureOffset),
3813 edi);
3814 __ jmp(&next_capture);
3815 __ bind(&done);
3816
3817 // Return last match info.
3818 __ mov(eax, Operand(esp, kLastMatchInfoOffset));
3819 __ ret(4 * kPointerSize);
3820
3821 // Do the runtime call to execute the regexp.
3822 __ bind(&runtime);
3823 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
3824#endif // V8_INTERPRETED_REGEXP
3825}
3826
3827
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003828void RegExpConstructResultStub::Generate(MacroAssembler* masm) {
3829 const int kMaxInlineLength = 100;
3830 Label slowcase;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003831 Label done;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003832 __ mov(ebx, Operand(esp, kPointerSize * 3));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003833 __ JumpIfNotSmi(ebx, &slowcase);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003834 __ cmp(ebx, Immediate(Smi::FromInt(kMaxInlineLength)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003835 __ j(above, &slowcase);
3836 // Smi-tagging is equivalent to multiplying by 2.
3837 STATIC_ASSERT(kSmiTag == 0);
3838 STATIC_ASSERT(kSmiTagSize == 1);
3839 // Allocate RegExpResult followed by FixedArray with size in ebx.
3840 // JSArray: [Map][empty properties][Elements][Length-smi][index][input]
3841 // Elements: [Map][Length][..elements..]
3842 __ AllocateInNewSpace(JSRegExpResult::kSize + FixedArray::kHeaderSize,
3843 times_half_pointer_size,
3844 ebx, // In: Number of elements (times 2, being a smi)
3845 eax, // Out: Start of allocation (tagged).
3846 ecx, // Out: End of allocation.
3847 edx, // Scratch register
3848 &slowcase,
3849 TAG_OBJECT);
3850 // eax: Start of allocated area, object-tagged.
3851
3852 // Set JSArray map to global.regexp_result_map().
3853 // Set empty properties FixedArray.
3854 // Set elements to point to FixedArray allocated right after the JSArray.
3855 // Interleave operations for better latency.
3856 __ mov(edx, ContextOperand(esi, Context::GLOBAL_INDEX));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003857 Factory* factory = masm->isolate()->factory();
3858 __ mov(ecx, Immediate(factory->empty_fixed_array()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003859 __ lea(ebx, Operand(eax, JSRegExpResult::kSize));
3860 __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalContextOffset));
3861 __ mov(FieldOperand(eax, JSObject::kElementsOffset), ebx);
3862 __ mov(FieldOperand(eax, JSObject::kPropertiesOffset), ecx);
3863 __ mov(edx, ContextOperand(edx, Context::REGEXP_RESULT_MAP_INDEX));
3864 __ mov(FieldOperand(eax, HeapObject::kMapOffset), edx);
3865
3866 // Set input, index and length fields from arguments.
3867 __ mov(ecx, Operand(esp, kPointerSize * 1));
3868 __ mov(FieldOperand(eax, JSRegExpResult::kInputOffset), ecx);
3869 __ mov(ecx, Operand(esp, kPointerSize * 2));
3870 __ mov(FieldOperand(eax, JSRegExpResult::kIndexOffset), ecx);
3871 __ mov(ecx, Operand(esp, kPointerSize * 3));
3872 __ mov(FieldOperand(eax, JSArray::kLengthOffset), ecx);
3873
3874 // Fill out the elements FixedArray.
3875 // eax: JSArray.
3876 // ebx: FixedArray.
3877 // ecx: Number of elements in array, as smi.
3878
3879 // Set map.
3880 __ mov(FieldOperand(ebx, HeapObject::kMapOffset),
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003881 Immediate(factory->fixed_array_map()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003882 // Set length.
3883 __ mov(FieldOperand(ebx, FixedArray::kLengthOffset), ecx);
3884 // Fill contents of fixed-array with the-hole.
3885 __ SmiUntag(ecx);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003886 __ mov(edx, Immediate(factory->the_hole_value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003887 __ lea(ebx, FieldOperand(ebx, FixedArray::kHeaderSize));
3888 // Fill fixed array elements with hole.
3889 // eax: JSArray.
3890 // ecx: Number of elements to fill.
3891 // ebx: Start of elements in FixedArray.
3892 // edx: the hole.
3893 Label loop;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003894 __ test(ecx, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003895 __ bind(&loop);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003896 __ j(less_equal, &done, Label::kNear); // Jump if ecx is negative or zero.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003897 __ sub(ecx, Immediate(1));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003898 __ mov(Operand(ebx, ecx, times_pointer_size, 0), edx);
3899 __ jmp(&loop);
3900
3901 __ bind(&done);
3902 __ ret(3 * kPointerSize);
3903
3904 __ bind(&slowcase);
3905 __ TailCallRuntime(Runtime::kRegExpConstructResult, 3, 1);
3906}
3907
3908
ricow@chromium.org65fae842010-08-25 15:26:24 +00003909void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
3910 Register object,
3911 Register result,
3912 Register scratch1,
3913 Register scratch2,
3914 bool object_is_smi,
3915 Label* not_found) {
3916 // Use of registers. Register result is used as a temporary.
3917 Register number_string_cache = result;
3918 Register mask = scratch1;
3919 Register scratch = scratch2;
3920
3921 // Load the number string cache.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00003922 ExternalReference roots_array_start =
3923 ExternalReference::roots_array_start(masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003924 __ mov(scratch, Immediate(Heap::kNumberStringCacheRootIndex));
3925 __ mov(number_string_cache,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00003926 Operand::StaticArray(scratch, times_pointer_size, roots_array_start));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003927 // Make the hash mask from the length of the number string cache. It
3928 // contains two elements (number and string) for each cache entry.
3929 __ mov(mask, FieldOperand(number_string_cache, FixedArray::kLengthOffset));
3930 __ shr(mask, kSmiTagSize + 1); // Untag length and divide it by two.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003931 __ sub(mask, Immediate(1)); // Make mask.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003932
3933 // Calculate the entry in the number string cache. The hash value in the
3934 // number string cache for smis is just the smi value, and the hash for
3935 // doubles is the xor of the upper and lower words. See
3936 // Heap::GetNumberStringCache.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003937 Label smi_hash_calculated;
3938 Label load_result_from_cache;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003939 if (object_is_smi) {
3940 __ mov(scratch, object);
3941 __ SmiUntag(scratch);
3942 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003943 Label not_smi;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003944 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003945 __ JumpIfNotSmi(object, &not_smi, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003946 __ mov(scratch, object);
3947 __ SmiUntag(scratch);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003948 __ jmp(&smi_hash_calculated, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003949 __ bind(&not_smi);
3950 __ cmp(FieldOperand(object, HeapObject::kMapOffset),
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003951 masm->isolate()->factory()->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003952 __ j(not_equal, not_found);
3953 STATIC_ASSERT(8 == kDoubleSize);
3954 __ mov(scratch, FieldOperand(object, HeapNumber::kValueOffset));
3955 __ xor_(scratch, FieldOperand(object, HeapNumber::kValueOffset + 4));
3956 // Object is heap number and hash is now in scratch. Calculate cache index.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003957 __ and_(scratch, mask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003958 Register index = scratch;
3959 Register probe = mask;
3960 __ mov(probe,
3961 FieldOperand(number_string_cache,
3962 index,
3963 times_twice_pointer_size,
3964 FixedArray::kHeaderSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003965 __ JumpIfSmi(probe, not_found);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003966 if (CpuFeatures::IsSupported(SSE2)) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00003967 CpuFeatures::Scope fscope(SSE2);
3968 __ movdbl(xmm0, FieldOperand(object, HeapNumber::kValueOffset));
3969 __ movdbl(xmm1, FieldOperand(probe, HeapNumber::kValueOffset));
3970 __ ucomisd(xmm0, xmm1);
3971 } else {
3972 __ fld_d(FieldOperand(object, HeapNumber::kValueOffset));
3973 __ fld_d(FieldOperand(probe, HeapNumber::kValueOffset));
3974 __ FCmp();
3975 }
3976 __ j(parity_even, not_found); // Bail out if NaN is involved.
3977 __ j(not_equal, not_found); // The cache did not contain this value.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003978 __ jmp(&load_result_from_cache, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003979 }
3980
3981 __ bind(&smi_hash_calculated);
3982 // Object is smi and hash is now in scratch. Calculate cache index.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003983 __ and_(scratch, mask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003984 Register index = scratch;
3985 // Check if the entry is the smi we are looking for.
3986 __ cmp(object,
3987 FieldOperand(number_string_cache,
3988 index,
3989 times_twice_pointer_size,
3990 FixedArray::kHeaderSize));
3991 __ j(not_equal, not_found);
3992
3993 // Get the result from the cache.
3994 __ bind(&load_result_from_cache);
3995 __ mov(result,
3996 FieldOperand(number_string_cache,
3997 index,
3998 times_twice_pointer_size,
3999 FixedArray::kHeaderSize + kPointerSize));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004000 Counters* counters = masm->isolate()->counters();
4001 __ IncrementCounter(counters->number_to_string_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004002}
4003
4004
4005void NumberToStringStub::Generate(MacroAssembler* masm) {
4006 Label runtime;
4007
4008 __ mov(ebx, Operand(esp, kPointerSize));
4009
4010 // Generate code to lookup number in the number string cache.
4011 GenerateLookupNumberStringCache(masm, ebx, eax, ecx, edx, false, &runtime);
4012 __ ret(1 * kPointerSize);
4013
4014 __ bind(&runtime);
4015 // Handle number to string in the runtime system if not found in the cache.
4016 __ TailCallRuntime(Runtime::kNumberToStringSkipCache, 1, 1);
4017}
4018
4019
4020static int NegativeComparisonResult(Condition cc) {
4021 ASSERT(cc != equal);
4022 ASSERT((cc == less) || (cc == less_equal)
4023 || (cc == greater) || (cc == greater_equal));
4024 return (cc == greater || cc == greater_equal) ? LESS : GREATER;
4025}
4026
4027void CompareStub::Generate(MacroAssembler* masm) {
4028 ASSERT(lhs_.is(no_reg) && rhs_.is(no_reg));
4029
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004030 Label check_unequal_objects;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004031
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00004032 // Compare two smis if required.
4033 if (include_smi_compare_) {
4034 Label non_smi, smi_done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004035 __ mov(ecx, edx);
4036 __ or_(ecx, eax);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004037 __ JumpIfNotSmi(ecx, &non_smi, Label::kNear);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004038 __ sub(edx, eax); // Return on the result of the subtraction.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004039 __ j(no_overflow, &smi_done, Label::kNear);
whesse@chromium.org4a5224e2010-10-20 12:37:07 +00004040 __ not_(edx); // Correct sign in case of overflow. edx is never 0 here.
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00004041 __ bind(&smi_done);
4042 __ mov(eax, edx);
4043 __ ret(0);
4044 __ bind(&non_smi);
4045 } else if (FLAG_debug_code) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004046 __ mov(ecx, edx);
4047 __ or_(ecx, eax);
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00004048 __ test(ecx, Immediate(kSmiTagMask));
4049 __ Assert(not_zero, "Unexpected smi operands.");
4050 }
4051
ricow@chromium.org65fae842010-08-25 15:26:24 +00004052 // NOTICE! This code is only reached after a smi-fast-case check, so
4053 // it is certain that at least one operand isn't a smi.
4054
4055 // Identical objects can be compared fast, but there are some tricky cases
4056 // for NaN and undefined.
4057 {
4058 Label not_identical;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004059 __ cmp(eax, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004060 __ j(not_equal, &not_identical);
4061
4062 if (cc_ != equal) {
4063 // Check for undefined. undefined OP undefined is false even though
4064 // undefined == undefined.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004065 Label check_for_nan;
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004066 __ cmp(edx, masm->isolate()->factory()->undefined_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004067 __ j(not_equal, &check_for_nan, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004068 __ Set(eax, Immediate(Smi::FromInt(NegativeComparisonResult(cc_))));
4069 __ ret(0);
4070 __ bind(&check_for_nan);
4071 }
4072
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004073 // Test for NaN. Sadly, we can't just compare to factory->nan_value(),
ricow@chromium.org65fae842010-08-25 15:26:24 +00004074 // so we do the second best thing - test it ourselves.
4075 // Note: if cc_ != equal, never_nan_nan_ is not used.
4076 if (never_nan_nan_ && (cc_ == equal)) {
4077 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
4078 __ ret(0);
4079 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004080 Label heap_number;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004081 __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004082 Immediate(masm->isolate()->factory()->heap_number_map()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004083 __ j(equal, &heap_number, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004084 if (cc_ != equal) {
4085 // Call runtime on identical JSObjects. Otherwise return equal.
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004086 __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004087 __ j(above_equal, &not_identical);
4088 }
4089 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
4090 __ ret(0);
4091
4092 __ bind(&heap_number);
4093 // It is a heap number, so return non-equal if it's NaN and equal if
4094 // it's not NaN.
4095 // The representation of NaN values has all exponent bits (52..62) set,
4096 // and not all mantissa bits (0..51) clear.
4097 // We only accept QNaNs, which have bit 51 set.
4098 // Read top bits of double representation (second word of value).
4099
4100 // Value is a QNaN if value & kQuietNaNMask == kQuietNaNMask, i.e.,
4101 // all bits in the mask are set. We only need to check the word
4102 // that contains the exponent and high bit of the mantissa.
4103 STATIC_ASSERT(((kQuietNaNHighBitsMask << 1) & 0x80000000u) != 0);
4104 __ mov(edx, FieldOperand(edx, HeapNumber::kExponentOffset));
lrn@chromium.org5d00b602011-01-05 09:51:43 +00004105 __ Set(eax, Immediate(0));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004106 // Shift value and mask so kQuietNaNHighBitsMask applies to topmost
4107 // bits.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004108 __ add(edx, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004109 __ cmp(edx, kQuietNaNHighBitsMask << 1);
4110 if (cc_ == equal) {
4111 STATIC_ASSERT(EQUAL != 1);
4112 __ setcc(above_equal, eax);
4113 __ ret(0);
4114 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004115 Label nan;
4116 __ j(above_equal, &nan, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004117 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
4118 __ ret(0);
4119 __ bind(&nan);
4120 __ Set(eax, Immediate(Smi::FromInt(NegativeComparisonResult(cc_))));
4121 __ ret(0);
4122 }
4123 }
4124
4125 __ bind(&not_identical);
4126 }
4127
4128 // Strict equality can quickly decide whether objects are equal.
4129 // Non-strict object equality is slower, so it is handled later in the stub.
4130 if (cc_ == equal && strict_) {
4131 Label slow; // Fallthrough label.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004132 Label not_smis;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004133 // If we're doing a strict equality comparison, we don't have to do
4134 // type conversion, so we generate code to do fast comparison for objects
4135 // and oddballs. Non-smi numbers and strings still go through the usual
4136 // slow-case code.
4137 // If either is a Smi (we know that not both are), then they can only
4138 // be equal if the other is a HeapNumber. If so, use the slow case.
4139 STATIC_ASSERT(kSmiTag == 0);
4140 ASSERT_EQ(0, Smi::FromInt(0));
4141 __ mov(ecx, Immediate(kSmiTagMask));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004142 __ and_(ecx, eax);
4143 __ test(ecx, edx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004144 __ j(not_zero, &not_smis, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004145 // One operand is a smi.
4146
4147 // Check whether the non-smi is a heap number.
4148 STATIC_ASSERT(kSmiTagMask == 1);
4149 // ecx still holds eax & kSmiTag, which is either zero or one.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004150 __ sub(ecx, Immediate(0x01));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004151 __ mov(ebx, edx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004152 __ xor_(ebx, eax);
4153 __ and_(ebx, ecx); // ebx holds either 0 or eax ^ edx.
4154 __ xor_(ebx, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004155 // if eax was smi, ebx is now edx, else eax.
4156
4157 // Check if the non-smi operand is a heap number.
4158 __ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004159 Immediate(masm->isolate()->factory()->heap_number_map()));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004160 // If heap number, handle it in the slow case.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004161 __ j(equal, &slow, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004162 // Return non-equal (ebx is not zero)
4163 __ mov(eax, ebx);
4164 __ ret(0);
4165
4166 __ bind(&not_smis);
4167 // If either operand is a JSObject or an oddball value, then they are not
4168 // equal since their pointers are different
4169 // There is no test for undetectability in strict equality.
4170
4171 // Get the type of the first operand.
4172 // If the first object is a JS object, we have done pointer comparison.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004173 Label first_non_object;
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004174 STATIC_ASSERT(LAST_TYPE == LAST_SPEC_OBJECT_TYPE);
4175 __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004176 __ j(below, &first_non_object, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004177
4178 // Return non-zero (eax is not zero)
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004179 Label return_not_equal;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004180 STATIC_ASSERT(kHeapObjectTag != 0);
4181 __ bind(&return_not_equal);
4182 __ ret(0);
4183
4184 __ bind(&first_non_object);
4185 // Check for oddballs: true, false, null, undefined.
4186 __ CmpInstanceType(ecx, ODDBALL_TYPE);
4187 __ j(equal, &return_not_equal);
4188
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004189 __ CmpObjectType(edx, FIRST_SPEC_OBJECT_TYPE, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004190 __ j(above_equal, &return_not_equal);
4191
4192 // Check for oddballs: true, false, null, undefined.
4193 __ CmpInstanceType(ecx, ODDBALL_TYPE);
4194 __ j(equal, &return_not_equal);
4195
4196 // Fall through to the general case.
4197 __ bind(&slow);
4198 }
4199
4200 // Generate the number comparison code.
4201 if (include_number_compare_) {
4202 Label non_number_comparison;
4203 Label unordered;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004204 if (CpuFeatures::IsSupported(SSE2)) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00004205 CpuFeatures::Scope use_sse2(SSE2);
4206 CpuFeatures::Scope use_cmov(CMOV);
4207
4208 FloatingPointHelper::LoadSSE2Operands(masm, &non_number_comparison);
4209 __ ucomisd(xmm0, xmm1);
4210
4211 // Don't base result on EFLAGS when a NaN is involved.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004212 __ j(parity_even, &unordered, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004213 // Return a result of -1, 0, or 1, based on EFLAGS.
4214 __ mov(eax, 0); // equal
4215 __ mov(ecx, Immediate(Smi::FromInt(1)));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004216 __ cmov(above, eax, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004217 __ mov(ecx, Immediate(Smi::FromInt(-1)));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004218 __ cmov(below, eax, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004219 __ ret(0);
4220 } else {
4221 FloatingPointHelper::CheckFloatOperands(
4222 masm, &non_number_comparison, ebx);
4223 FloatingPointHelper::LoadFloatOperand(masm, eax);
4224 FloatingPointHelper::LoadFloatOperand(masm, edx);
4225 __ FCmp();
4226
4227 // Don't base result on EFLAGS when a NaN is involved.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004228 __ j(parity_even, &unordered, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004229
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004230 Label below_label, above_label;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004231 // Return a result of -1, 0, or 1, based on EFLAGS.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004232 __ j(below, &below_label, Label::kNear);
4233 __ j(above, &above_label, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004234
lrn@chromium.org5d00b602011-01-05 09:51:43 +00004235 __ Set(eax, Immediate(0));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004236 __ ret(0);
4237
4238 __ bind(&below_label);
4239 __ mov(eax, Immediate(Smi::FromInt(-1)));
4240 __ ret(0);
4241
4242 __ bind(&above_label);
4243 __ mov(eax, Immediate(Smi::FromInt(1)));
4244 __ ret(0);
4245 }
4246
4247 // If one of the numbers was NaN, then the result is always false.
4248 // The cc is never not-equal.
4249 __ bind(&unordered);
4250 ASSERT(cc_ != not_equal);
4251 if (cc_ == less || cc_ == less_equal) {
4252 __ mov(eax, Immediate(Smi::FromInt(1)));
4253 } else {
4254 __ mov(eax, Immediate(Smi::FromInt(-1)));
4255 }
4256 __ ret(0);
4257
4258 // The number comparison code did not provide a valid result.
4259 __ bind(&non_number_comparison);
4260 }
4261
4262 // Fast negative check for symbol-to-symbol equality.
4263 Label check_for_strings;
4264 if (cc_ == equal) {
4265 BranchIfNonSymbol(masm, &check_for_strings, eax, ecx);
4266 BranchIfNonSymbol(masm, &check_for_strings, edx, ecx);
4267
4268 // We've already checked for object identity, so if both operands
4269 // are symbols they aren't equal. Register eax already holds a
4270 // non-zero value, which indicates not equal, so just return.
4271 __ ret(0);
4272 }
4273
4274 __ bind(&check_for_strings);
4275
4276 __ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx,
4277 &check_unequal_objects);
4278
4279 // Inline comparison of ascii strings.
lrn@chromium.org1c092762011-05-09 09:42:16 +00004280 if (cc_ == equal) {
4281 StringCompareStub::GenerateFlatAsciiStringEquals(masm,
ricow@chromium.org65fae842010-08-25 15:26:24 +00004282 edx,
4283 eax,
4284 ecx,
lrn@chromium.org1c092762011-05-09 09:42:16 +00004285 ebx);
4286 } else {
4287 StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
4288 edx,
4289 eax,
4290 ecx,
4291 ebx,
4292 edi);
4293 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00004294#ifdef DEBUG
4295 __ Abort("Unexpected fall-through from string comparison");
4296#endif
4297
4298 __ bind(&check_unequal_objects);
4299 if (cc_ == equal && !strict_) {
4300 // Non-strict equality. Objects are unequal if
4301 // they are both JSObjects and not undetectable,
4302 // and their pointers are different.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004303 Label not_both_objects;
4304 Label return_unequal;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004305 // At most one is a smi, so we can test for smi by adding the two.
4306 // A smi plus a heap object has the low bit set, a heap object plus
4307 // a heap object has the low bit clear.
4308 STATIC_ASSERT(kSmiTag == 0);
4309 STATIC_ASSERT(kSmiTagMask == 1);
4310 __ lea(ecx, Operand(eax, edx, times_1, 0));
4311 __ test(ecx, Immediate(kSmiTagMask));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004312 __ j(not_zero, &not_both_objects, Label::kNear);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004313 __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004314 __ j(below, &not_both_objects, Label::kNear);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004315 __ CmpObjectType(edx, FIRST_SPEC_OBJECT_TYPE, ebx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004316 __ j(below, &not_both_objects, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004317 // We do not bail out after this point. Both are JSObjects, and
4318 // they are equal if and only if both are undetectable.
4319 // The and of the undetectable flags is 1 if and only if they are equal.
4320 __ test_b(FieldOperand(ecx, Map::kBitFieldOffset),
4321 1 << Map::kIsUndetectable);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004322 __ j(zero, &return_unequal, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004323 __ test_b(FieldOperand(ebx, Map::kBitFieldOffset),
4324 1 << Map::kIsUndetectable);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004325 __ j(zero, &return_unequal, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004326 // The objects are both undetectable, so they both compare as the value
4327 // undefined, and are equal.
4328 __ Set(eax, Immediate(EQUAL));
4329 __ bind(&return_unequal);
4330 // Return non-equal by returning the non-zero object pointer in eax,
4331 // or return equal if we fell through to here.
4332 __ ret(0); // rax, rdx were pushed
4333 __ bind(&not_both_objects);
4334 }
4335
4336 // Push arguments below the return address.
4337 __ pop(ecx);
4338 __ push(edx);
4339 __ push(eax);
4340
4341 // Figure out which native to call and setup the arguments.
4342 Builtins::JavaScript builtin;
4343 if (cc_ == equal) {
4344 builtin = strict_ ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
4345 } else {
4346 builtin = Builtins::COMPARE;
4347 __ push(Immediate(Smi::FromInt(NegativeComparisonResult(cc_))));
4348 }
4349
4350 // Restore return address on the stack.
4351 __ push(ecx);
4352
4353 // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
4354 // tagged as a small integer.
4355 __ InvokeBuiltin(builtin, JUMP_FUNCTION);
4356}
4357
4358
4359void CompareStub::BranchIfNonSymbol(MacroAssembler* masm,
4360 Label* label,
4361 Register object,
4362 Register scratch) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00004363 __ JumpIfSmi(object, label);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004364 __ mov(scratch, FieldOperand(object, HeapObject::kMapOffset));
4365 __ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
4366 __ and_(scratch, kIsSymbolMask | kIsNotStringMask);
4367 __ cmp(scratch, kSymbolTag | kStringTag);
4368 __ j(not_equal, label);
4369}
4370
4371
4372void StackCheckStub::Generate(MacroAssembler* masm) {
whesse@chromium.org4a5224e2010-10-20 12:37:07 +00004373 __ TailCallRuntime(Runtime::kStackGuard, 0, 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004374}
4375
4376
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00004377void CallFunctionStub::FinishCode(Handle<Code> code) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004378 code->set_has_function_cache(RecordCallTarget());
4379}
4380
4381
4382void CallFunctionStub::Clear(Heap* heap, Address address) {
4383 ASSERT(Memory::uint8_at(address + kPointerSize) == Assembler::kTestEaxByte);
4384 // 1 ~ size of the test eax opcode.
4385 Object* cell = Memory::Object_at(address + kPointerSize + 1);
4386 // Low-level because clearing happens during GC.
4387 reinterpret_cast<JSGlobalPropertyCell*>(cell)->set_value(
4388 RawUninitializedSentinel(heap));
4389}
4390
4391
4392Object* CallFunctionStub::GetCachedValue(Address address) {
4393 ASSERT(Memory::uint8_at(address + kPointerSize) == Assembler::kTestEaxByte);
4394 // 1 ~ size of the test eax opcode.
4395 Object* cell = Memory::Object_at(address + kPointerSize + 1);
4396 return JSGlobalPropertyCell::cast(cell)->value();
4397}
4398
4399
ricow@chromium.org65fae842010-08-25 15:26:24 +00004400void CallFunctionStub::Generate(MacroAssembler* masm) {
danno@chromium.orgc612e022011-11-10 11:38:15 +00004401 // edi : the function to call
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004402 Isolate* isolate = masm->isolate();
lrn@chromium.org34e60782011-09-15 07:25:40 +00004403 Label slow, non_function;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004404
danno@chromium.org40cb8782011-05-25 07:58:50 +00004405 // The receiver might implicitly be the global object. This is
4406 // indicated by passing the hole as the receiver to the call
4407 // function stub.
4408 if (ReceiverMightBeImplicit()) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004409 Label receiver_ok;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004410 // Get the receiver from the stack.
4411 // +1 ~ return address
ricow@chromium.org65fae842010-08-25 15:26:24 +00004412 __ mov(eax, Operand(esp, (argc_ + 1) * kPointerSize));
danno@chromium.org40cb8782011-05-25 07:58:50 +00004413 // Call as function is indicated with the hole.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004414 __ cmp(eax, isolate->factory()->the_hole_value());
4415 __ j(not_equal, &receiver_ok, Label::kNear);
danno@chromium.org40cb8782011-05-25 07:58:50 +00004416 // Patch the receiver on the stack with the global receiver object.
4417 __ mov(ebx, GlobalObjectOperand());
4418 __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
4419 __ mov(Operand(esp, (argc_ + 1) * kPointerSize), ebx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004420 __ bind(&receiver_ok);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004421 }
4422
ricow@chromium.org65fae842010-08-25 15:26:24 +00004423 // Check that the function really is a JavaScript function.
lrn@chromium.org34e60782011-09-15 07:25:40 +00004424 __ JumpIfSmi(edi, &non_function);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004425 // Goto slow case if we do not have a function.
4426 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00004427 __ j(not_equal, &slow);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004428
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004429 if (RecordCallTarget()) {
4430 // Cache the called function in a global property cell in the
4431 // instruction stream after the call. Cache states are uninitialized,
4432 // monomorphic (indicated by a JSFunction), and megamorphic.
4433 Label initialize, call;
4434 // Load the cache cell address into ebx and the cache state into ecx.
4435 __ mov(ebx, Operand(esp, 0)); // Return address.
4436 __ mov(ebx, Operand(ebx, 1)); // 1 ~ sizeof 'test eax' opcode in bytes.
4437 __ mov(ecx, FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset));
4438
4439 // A monomorphic cache hit or an already megamorphic state: invoke the
4440 // function without changing the state.
4441 __ cmp(ecx, edi);
4442 __ j(equal, &call, Label::kNear);
4443 __ cmp(ecx, Immediate(MegamorphicSentinel(isolate)));
4444 __ j(equal, &call, Label::kNear);
4445
4446 // A monomorphic miss (i.e, here the cache is not uninitialized) goes
4447 // megamorphic.
4448 __ cmp(ecx, Immediate(UninitializedSentinel(isolate)));
4449 __ j(equal, &initialize, Label::kNear);
4450 // MegamorphicSentinel is a root so no write-barrier is needed.
4451 __ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
4452 Immediate(MegamorphicSentinel(isolate)));
4453 __ jmp(&call, Label::kNear);
4454
4455 // An uninitialized cache is patched with the function.
4456 __ bind(&initialize);
4457 __ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset), edi);
4458 __ mov(ecx, edi);
4459 __ RecordWriteField(ebx,
4460 JSGlobalPropertyCell::kValueOffset,
4461 ecx,
4462 edx,
4463 kDontSaveFPRegs,
4464 OMIT_REMEMBERED_SET, // Cells are rescanned.
4465 OMIT_SMI_CHECK);
4466
4467 __ bind(&call);
4468 }
4469
ricow@chromium.org65fae842010-08-25 15:26:24 +00004470 // Fast-case: Just invoke the function.
4471 ParameterCount actual(argc_);
danno@chromium.org40cb8782011-05-25 07:58:50 +00004472
4473 if (ReceiverMightBeImplicit()) {
4474 Label call_as_function;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004475 __ cmp(eax, isolate->factory()->the_hole_value());
danno@chromium.org40cb8782011-05-25 07:58:50 +00004476 __ j(equal, &call_as_function);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004477 __ InvokeFunction(edi,
4478 actual,
4479 JUMP_FUNCTION,
4480 NullCallWrapper(),
4481 CALL_AS_METHOD);
danno@chromium.org40cb8782011-05-25 07:58:50 +00004482 __ bind(&call_as_function);
4483 }
4484 __ InvokeFunction(edi,
4485 actual,
4486 JUMP_FUNCTION,
4487 NullCallWrapper(),
4488 CALL_AS_FUNCTION);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004489
4490 // Slow-case: Non-function called.
4491 __ bind(&slow);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004492 if (RecordCallTarget()) {
4493 // If there is a call target cache, mark it megamorphic in the
4494 // non-function case.
4495 __ mov(ebx, Operand(esp, 0));
4496 __ mov(ebx, Operand(ebx, 1));
4497 __ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
4498 Immediate(MegamorphicSentinel(isolate)));
4499 }
lrn@chromium.org34e60782011-09-15 07:25:40 +00004500 // Check for function proxy.
4501 __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
4502 __ j(not_equal, &non_function);
4503 __ pop(ecx);
4504 __ push(edi); // put proxy as additional argument under return address
4505 __ push(ecx);
4506 __ Set(eax, Immediate(argc_ + 1));
4507 __ Set(ebx, Immediate(0));
4508 __ SetCallKind(ecx, CALL_AS_FUNCTION);
4509 __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY);
4510 {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004511 Handle<Code> adaptor = isolate->builtins()->ArgumentsAdaptorTrampoline();
lrn@chromium.org34e60782011-09-15 07:25:40 +00004512 __ jmp(adaptor, RelocInfo::CODE_TARGET);
4513 }
4514
ricow@chromium.org65fae842010-08-25 15:26:24 +00004515 // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
4516 // of the original receiver from the call site).
lrn@chromium.org34e60782011-09-15 07:25:40 +00004517 __ bind(&non_function);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004518 __ mov(Operand(esp, (argc_ + 1) * kPointerSize), edi);
4519 __ Set(eax, Immediate(argc_));
4520 __ Set(ebx, Immediate(0));
lrn@chromium.org34e60782011-09-15 07:25:40 +00004521 __ SetCallKind(ecx, CALL_AS_METHOD);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004522 __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004523 Handle<Code> adaptor = isolate->builtins()->ArgumentsAdaptorTrampoline();
ricow@chromium.org65fae842010-08-25 15:26:24 +00004524 __ jmp(adaptor, RelocInfo::CODE_TARGET);
4525}
4526
4527
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004528bool CEntryStub::NeedsImmovableCode() {
4529 return false;
4530}
4531
4532
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004533bool CEntryStub::IsPregenerated() {
4534 return (!save_doubles_ || ISOLATE->fp_stubs_generated()) &&
4535 result_size_ == 1;
4536}
4537
4538
4539void CodeStub::GenerateStubsAheadOfTime() {
4540 CEntryStub::GenerateAheadOfTime();
4541 StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime();
4542 // It is important that the store buffer overflow stubs are generated first.
4543 RecordWriteStub::GenerateFixedRegStubsAheadOfTime();
4544}
4545
4546
4547void CodeStub::GenerateFPStubs() {
4548 CEntryStub save_doubles(1, kSaveFPRegs);
4549 Handle<Code> code = save_doubles.GetCode();
4550 code->set_is_pregenerated(true);
4551 code->GetIsolate()->set_fp_stubs_generated(true);
4552}
4553
4554
4555void CEntryStub::GenerateAheadOfTime() {
4556 CEntryStub stub(1, kDontSaveFPRegs);
4557 Handle<Code> code = stub.GetCode();
4558 code->set_is_pregenerated(true);
4559}
4560
4561
ricow@chromium.org65fae842010-08-25 15:26:24 +00004562void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00004563 __ Throw(eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004564}
4565
4566
ricow@chromium.org65fae842010-08-25 15:26:24 +00004567void CEntryStub::GenerateCore(MacroAssembler* masm,
4568 Label* throw_normal_exception,
4569 Label* throw_termination_exception,
4570 Label* throw_out_of_memory_exception,
4571 bool do_gc,
ager@chromium.org0ee099b2011-01-25 14:06:47 +00004572 bool always_allocate_scope) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00004573 // eax: result parameter for PerformGC, if any
4574 // ebx: pointer to C function (C callee-saved)
4575 // ebp: frame pointer (restored after C call)
4576 // esp: stack pointer (restored after C call)
4577 // edi: number of arguments including receiver (C callee-saved)
4578 // esi: pointer to the first argument (C callee-saved)
4579
4580 // Result returned in eax, or eax+edx if result_size_ is 2.
4581
4582 // Check stack alignment.
4583 if (FLAG_debug_code) {
4584 __ CheckStackAlignment();
4585 }
4586
4587 if (do_gc) {
4588 // Pass failure code returned from last attempt as first argument to
4589 // PerformGC. No need to use PrepareCallCFunction/CallCFunction here as the
4590 // stack alignment is known to be correct. This function takes one argument
4591 // which is passed on the stack, and we know that the stack has been
4592 // prepared to pass at least one argument.
4593 __ mov(Operand(esp, 0 * kPointerSize), eax); // Result.
4594 __ call(FUNCTION_ADDR(Runtime::PerformGC), RelocInfo::RUNTIME_ENTRY);
4595 }
4596
4597 ExternalReference scope_depth =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004598 ExternalReference::heap_always_allocate_scope_depth(masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004599 if (always_allocate_scope) {
4600 __ inc(Operand::StaticVariable(scope_depth));
4601 }
4602
4603 // Call C function.
4604 __ mov(Operand(esp, 0 * kPointerSize), edi); // argc.
4605 __ mov(Operand(esp, 1 * kPointerSize), esi); // argv.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004606 __ mov(Operand(esp, 2 * kPointerSize),
4607 Immediate(ExternalReference::isolate_address()));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004608 __ call(ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004609 // Result is in eax or edx:eax - do not destroy these registers!
4610
4611 if (always_allocate_scope) {
4612 __ dec(Operand::StaticVariable(scope_depth));
4613 }
4614
4615 // Make sure we're not trying to return 'the hole' from the runtime
4616 // call as this may lead to crashes in the IC code later.
4617 if (FLAG_debug_code) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004618 Label okay;
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004619 __ cmp(eax, masm->isolate()->factory()->the_hole_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004620 __ j(not_equal, &okay, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004621 __ int3();
4622 __ bind(&okay);
4623 }
4624
4625 // Check for failure result.
4626 Label failure_returned;
4627 STATIC_ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
4628 __ lea(ecx, Operand(eax, 1));
4629 // Lower 2 bits of ecx are 0 iff eax has failure tag.
4630 __ test(ecx, Immediate(kFailureTagMask));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00004631 __ j(zero, &failure_returned);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004632
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004633 ExternalReference pending_exception_address(
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004634 Isolate::kPendingExceptionAddress, masm->isolate());
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00004635
4636 // Check that there is no pending exception, otherwise we
4637 // should have returned some failure value.
4638 if (FLAG_debug_code) {
4639 __ push(edx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004640 __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004641 Label okay;
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00004642 __ cmp(edx, Operand::StaticVariable(pending_exception_address));
4643 // Cannot use check here as it attempts to generate call into runtime.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004644 __ j(equal, &okay, Label::kNear);
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00004645 __ int3();
4646 __ bind(&okay);
4647 __ pop(edx);
4648 }
4649
ricow@chromium.org65fae842010-08-25 15:26:24 +00004650 // Exit the JavaScript to C++ exit frame.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004651 __ LeaveExitFrame(save_doubles_ == kSaveFPRegs);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004652 __ ret(0);
4653
4654 // Handling of failure.
4655 __ bind(&failure_returned);
4656
4657 Label retry;
4658 // If the returned exception is RETRY_AFTER_GC continue at retry label
4659 STATIC_ASSERT(Failure::RETRY_AFTER_GC == 0);
4660 __ test(eax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004661 __ j(zero, &retry, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004662
4663 // Special handling of out of memory exceptions.
4664 __ cmp(eax, reinterpret_cast<int32_t>(Failure::OutOfMemoryException()));
4665 __ j(equal, throw_out_of_memory_exception);
4666
4667 // Retrieve the pending exception and clear the variable.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004668 __ mov(eax, Operand::StaticVariable(pending_exception_address));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004669 __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004670 __ mov(Operand::StaticVariable(pending_exception_address), edx);
4671
4672 // Special handling of termination exceptions which are uncatchable
4673 // by javascript code.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004674 __ cmp(eax, masm->isolate()->factory()->termination_exception());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004675 __ j(equal, throw_termination_exception);
4676
4677 // Handle normal exception.
4678 __ jmp(throw_normal_exception);
4679
4680 // Retry.
4681 __ bind(&retry);
4682}
4683
4684
4685void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm,
4686 UncatchableExceptionType type) {
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00004687 __ ThrowUncatchable(type, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004688}
4689
4690
4691void CEntryStub::Generate(MacroAssembler* masm) {
4692 // eax: number of arguments including receiver
4693 // ebx: pointer to C function (C callee-saved)
4694 // ebp: frame pointer (restored after C call)
4695 // esp: stack pointer (restored after C call)
4696 // esi: current context (C callee-saved)
4697 // edi: JS function of the caller (C callee-saved)
4698
4699 // NOTE: Invocations of builtins may return failure objects instead
4700 // of a proper result. The builtin entry handles this by performing
4701 // a garbage collection and retrying the builtin (twice).
4702
4703 // Enter the exit frame that transitions from JavaScript to C++.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004704 __ EnterExitFrame(save_doubles_ == kSaveFPRegs);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004705
4706 // eax: result parameter for PerformGC, if any (setup below)
4707 // ebx: pointer to builtin function (C callee-saved)
4708 // ebp: frame pointer (restored after C call)
4709 // esp: stack pointer (restored after C call)
4710 // edi: number of arguments including receiver (C callee-saved)
4711 // esi: argv pointer (C callee-saved)
4712
4713 Label throw_normal_exception;
4714 Label throw_termination_exception;
4715 Label throw_out_of_memory_exception;
4716
4717 // Call into the runtime system.
4718 GenerateCore(masm,
4719 &throw_normal_exception,
4720 &throw_termination_exception,
4721 &throw_out_of_memory_exception,
4722 false,
4723 false);
4724
4725 // Do space-specific GC and retry runtime call.
4726 GenerateCore(masm,
4727 &throw_normal_exception,
4728 &throw_termination_exception,
4729 &throw_out_of_memory_exception,
4730 true,
4731 false);
4732
4733 // Do full GC and retry runtime call one final time.
4734 Failure* failure = Failure::InternalError();
4735 __ mov(eax, Immediate(reinterpret_cast<int32_t>(failure)));
4736 GenerateCore(masm,
4737 &throw_normal_exception,
4738 &throw_termination_exception,
4739 &throw_out_of_memory_exception,
4740 true,
4741 true);
4742
4743 __ bind(&throw_out_of_memory_exception);
4744 GenerateThrowUncatchable(masm, OUT_OF_MEMORY);
4745
4746 __ bind(&throw_termination_exception);
4747 GenerateThrowUncatchable(masm, TERMINATION);
4748
4749 __ bind(&throw_normal_exception);
4750 GenerateThrowTOS(masm);
4751}
4752
4753
4754void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00004755 Label invoke, handler_entry, exit;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004756 Label not_outermost_js, not_outermost_js_2;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004757
4758 // Setup frame.
4759 __ push(ebp);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004760 __ mov(ebp, esp);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004761
4762 // Push marker in two places.
4763 int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
4764 __ push(Immediate(Smi::FromInt(marker))); // context slot
4765 __ push(Immediate(Smi::FromInt(marker))); // function slot
4766 // Save callee-saved registers (C calling conventions).
4767 __ push(edi);
4768 __ push(esi);
4769 __ push(ebx);
4770
4771 // Save copies of the top frame descriptor on the stack.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004772 ExternalReference c_entry_fp(Isolate::kCEntryFPAddress, masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004773 __ push(Operand::StaticVariable(c_entry_fp));
4774
ricow@chromium.org65fae842010-08-25 15:26:24 +00004775 // If this is the outermost JS call, set js_entry_sp value.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004776 ExternalReference js_entry_sp(Isolate::kJSEntrySPAddress,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004777 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004778 __ cmp(Operand::StaticVariable(js_entry_sp), Immediate(0));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004779 __ j(not_equal, &not_outermost_js, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004780 __ mov(Operand::StaticVariable(js_entry_sp), ebp);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004781 __ push(Immediate(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME)));
4782 Label cont;
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004783 __ jmp(&cont, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004784 __ bind(&not_outermost_js);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004785 __ push(Immediate(Smi::FromInt(StackFrame::INNER_JSENTRY_FRAME)));
4786 __ bind(&cont);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004787
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00004788 // Jump to a faked try block that does the invoke, with a faked catch
4789 // block that sets the pending exception.
4790 __ jmp(&invoke);
4791 __ bind(&handler_entry);
4792 handler_offset_ = handler_entry.pos();
4793 // Caught exception: Store result (exception) in the pending exception
4794 // field in the JSEnv and return a failure sentinel.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004795 ExternalReference pending_exception(Isolate::kPendingExceptionAddress,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004796 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004797 __ mov(Operand::StaticVariable(pending_exception), eax);
4798 __ mov(eax, reinterpret_cast<int32_t>(Failure::Exception()));
4799 __ jmp(&exit);
4800
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00004801 // Invoke: Link this frame into the handler chain. There's only one
4802 // handler block in this code object, so its index is 0.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004803 __ bind(&invoke);
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00004804 __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER, 0);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004805
4806 // Clear any pending exceptions.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004807 __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004808 __ mov(Operand::StaticVariable(pending_exception), edx);
4809
4810 // Fake a receiver (NULL).
4811 __ push(Immediate(0)); // receiver
4812
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00004813 // Invoke the function by calling through JS entry trampoline builtin and
4814 // pop the faked function when we return. Notice that we cannot store a
4815 // reference to the trampoline code directly in this stub, because the
4816 // builtin stubs may not have been generated yet.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004817 if (is_construct) {
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00004818 ExternalReference construct_entry(Builtins::kJSConstructEntryTrampoline,
4819 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004820 __ mov(edx, Immediate(construct_entry));
4821 } else {
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004822 ExternalReference entry(Builtins::kJSEntryTrampoline,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004823 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004824 __ mov(edx, Immediate(entry));
4825 }
4826 __ mov(edx, Operand(edx, 0)); // deref address
4827 __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004828 __ call(edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004829
4830 // Unlink this frame from the handler chain.
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004831 __ PopTryHandler();
ricow@chromium.org65fae842010-08-25 15:26:24 +00004832
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004833 __ bind(&exit);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004834 // Check if the current stack frame is marked as the outermost JS frame.
4835 __ pop(ebx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004836 __ cmp(ebx, Immediate(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004837 __ j(not_equal, &not_outermost_js_2);
4838 __ mov(Operand::StaticVariable(js_entry_sp), Immediate(0));
4839 __ bind(&not_outermost_js_2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004840
4841 // Restore the top frame descriptor from the stack.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004842 __ pop(Operand::StaticVariable(ExternalReference(
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004843 Isolate::kCEntryFPAddress,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004844 masm->isolate())));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004845
4846 // Restore callee-saved registers (C calling conventions).
4847 __ pop(ebx);
4848 __ pop(esi);
4849 __ pop(edi);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004850 __ add(esp, Immediate(2 * kPointerSize)); // remove markers
ricow@chromium.org65fae842010-08-25 15:26:24 +00004851
4852 // Restore frame pointer and return.
4853 __ pop(ebp);
4854 __ ret(0);
4855}
4856
4857
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004858// Generate stub code for instanceof.
4859// This code can patch a call site inlined cache of the instance of check,
4860// which looks like this.
4861//
4862// 81 ff XX XX XX XX cmp edi, <the hole, patched to a map>
4863// 75 0a jne <some near label>
4864// b8 XX XX XX XX mov eax, <the hole, patched to either true or false>
4865//
4866// If call site patching is requested the stack will have the delta from the
4867// return address to the cmp instruction just below the return address. This
4868// also means that call site patching can only take place with arguments in
4869// registers. TOS looks like this when call site patching is requested
4870//
4871// esp[0] : return address
4872// esp[4] : delta from return address to cmp instruction
4873//
ricow@chromium.org65fae842010-08-25 15:26:24 +00004874void InstanceofStub::Generate(MacroAssembler* masm) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004875 // Call site inlining and patching implies arguments in registers.
4876 ASSERT(HasArgsInRegisters() || !HasCallSiteInlineCheck());
4877
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004878 // Fixed register usage throughout the stub.
4879 Register object = eax; // Object (lhs).
4880 Register map = ebx; // Map of the object.
4881 Register function = edx; // Function (rhs).
4882 Register prototype = edi; // Prototype of the function.
4883 Register scratch = ecx;
4884
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004885 // Constants describing the call site code to patch.
4886 static const int kDeltaToCmpImmediate = 2;
4887 static const int kDeltaToMov = 8;
4888 static const int kDeltaToMovImmediate = 9;
4889 static const int8_t kCmpEdiImmediateByte1 = BitCast<int8_t, uint8_t>(0x81);
4890 static const int8_t kCmpEdiImmediateByte2 = BitCast<int8_t, uint8_t>(0xff);
4891 static const int8_t kMovEaxImmediateByte = BitCast<int8_t, uint8_t>(0xb8);
4892
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004893 ExternalReference roots_array_start =
4894 ExternalReference::roots_array_start(masm->isolate());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004895
4896 ASSERT_EQ(object.code(), InstanceofStub::left().code());
4897 ASSERT_EQ(function.code(), InstanceofStub::right().code());
4898
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004899 // Get the object and function - they are always both needed.
4900 Label slow, not_js_object;
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004901 if (!HasArgsInRegisters()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004902 __ mov(object, Operand(esp, 2 * kPointerSize));
4903 __ mov(function, Operand(esp, 1 * kPointerSize));
4904 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00004905
4906 // Check that the left hand is a JS object.
whesse@chromium.org7b260152011-06-20 15:33:18 +00004907 __ JumpIfSmi(object, &not_js_object);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004908 __ IsObjectJSObjectType(object, map, scratch, &not_js_object);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004909
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004910 // If there is a call site cache don't look in the global cache, but do the
4911 // real lookup and update the call site cache.
4912 if (!HasCallSiteInlineCheck()) {
4913 // Look up the function and the map in the instanceof cache.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004914 Label miss;
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004915 __ mov(scratch, Immediate(Heap::kInstanceofCacheFunctionRootIndex));
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004916 __ cmp(function, Operand::StaticArray(scratch,
4917 times_pointer_size,
4918 roots_array_start));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004919 __ j(not_equal, &miss, Label::kNear);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004920 __ mov(scratch, Immediate(Heap::kInstanceofCacheMapRootIndex));
4921 __ cmp(map, Operand::StaticArray(
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004922 scratch, times_pointer_size, roots_array_start));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004923 __ j(not_equal, &miss, Label::kNear);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004924 __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
4925 __ mov(eax, Operand::StaticArray(
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004926 scratch, times_pointer_size, roots_array_start));
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004927 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
4928 __ bind(&miss);
4929 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00004930
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004931 // Get the prototype of the function.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004932 __ TryGetFunctionPrototype(function, prototype, scratch, &slow, true);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004933
4934 // Check that the function prototype is a JS object.
whesse@chromium.org7b260152011-06-20 15:33:18 +00004935 __ JumpIfSmi(prototype, &slow);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004936 __ IsObjectJSObjectType(prototype, scratch, scratch, &slow);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004937
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004938 // Update the global instanceof or call site inlined cache with the current
4939 // map and function. The cached answer will be set when it is known below.
4940 if (!HasCallSiteInlineCheck()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004941 __ mov(scratch, Immediate(Heap::kInstanceofCacheMapRootIndex));
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004942 __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_array_start),
4943 map);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004944 __ mov(scratch, Immediate(Heap::kInstanceofCacheFunctionRootIndex));
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004945 __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_array_start),
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004946 function);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004947 } else {
4948 // The constants for the code patching are based on no push instructions
4949 // at the call site.
4950 ASSERT(HasArgsInRegisters());
4951 // Get return address and delta to inlined map check.
4952 __ mov(scratch, Operand(esp, 0 * kPointerSize));
4953 __ sub(scratch, Operand(esp, 1 * kPointerSize));
4954 if (FLAG_debug_code) {
4955 __ cmpb(Operand(scratch, 0), kCmpEdiImmediateByte1);
4956 __ Assert(equal, "InstanceofStub unexpected call site cache (cmp 1)");
4957 __ cmpb(Operand(scratch, 1), kCmpEdiImmediateByte2);
4958 __ Assert(equal, "InstanceofStub unexpected call site cache (cmp 2)");
4959 }
4960 __ mov(Operand(scratch, kDeltaToCmpImmediate), map);
4961 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00004962
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004963 // Loop through the prototype chain of the object looking for the function
4964 // prototype.
4965 __ mov(scratch, FieldOperand(map, Map::kPrototypeOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004966 Label loop, is_instance, is_not_instance;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004967 __ bind(&loop);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004968 __ cmp(scratch, prototype);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004969 __ j(equal, &is_instance, Label::kNear);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004970 Factory* factory = masm->isolate()->factory();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004971 __ cmp(scratch, Immediate(factory->null_value()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004972 __ j(equal, &is_not_instance, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004973 __ mov(scratch, FieldOperand(scratch, HeapObject::kMapOffset));
4974 __ mov(scratch, FieldOperand(scratch, Map::kPrototypeOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004975 __ jmp(&loop);
4976
4977 __ bind(&is_instance);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004978 if (!HasCallSiteInlineCheck()) {
4979 __ Set(eax, Immediate(0));
4980 __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
4981 __ mov(Operand::StaticArray(scratch,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004982 times_pointer_size, roots_array_start), eax);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004983 } else {
4984 // Get return address and delta to inlined map check.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004985 __ mov(eax, factory->true_value());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004986 __ mov(scratch, Operand(esp, 0 * kPointerSize));
4987 __ sub(scratch, Operand(esp, 1 * kPointerSize));
4988 if (FLAG_debug_code) {
4989 __ cmpb(Operand(scratch, kDeltaToMov), kMovEaxImmediateByte);
4990 __ Assert(equal, "InstanceofStub unexpected call site cache (mov)");
4991 }
4992 __ mov(Operand(scratch, kDeltaToMovImmediate), eax);
4993 if (!ReturnTrueFalseObject()) {
4994 __ Set(eax, Immediate(0));
4995 }
4996 }
4997 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004998
4999 __ bind(&is_not_instance);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005000 if (!HasCallSiteInlineCheck()) {
5001 __ Set(eax, Immediate(Smi::FromInt(1)));
5002 __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
5003 __ mov(Operand::StaticArray(
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005004 scratch, times_pointer_size, roots_array_start), eax);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005005 } else {
5006 // Get return address and delta to inlined map check.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005007 __ mov(eax, factory->false_value());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005008 __ mov(scratch, Operand(esp, 0 * kPointerSize));
5009 __ sub(scratch, Operand(esp, 1 * kPointerSize));
5010 if (FLAG_debug_code) {
5011 __ cmpb(Operand(scratch, kDeltaToMov), kMovEaxImmediateByte);
5012 __ Assert(equal, "InstanceofStub unexpected call site cache (mov)");
5013 }
5014 __ mov(Operand(scratch, kDeltaToMovImmediate), eax);
5015 if (!ReturnTrueFalseObject()) {
5016 __ Set(eax, Immediate(Smi::FromInt(1)));
5017 }
5018 }
5019 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005020
5021 Label object_not_null, object_not_null_or_smi;
5022 __ bind(&not_js_object);
5023 // Before null, smi and string value checks, check that the rhs is a function
5024 // as for a non-function rhs an exception needs to be thrown.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005025 __ JumpIfSmi(function, &slow, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005026 __ CmpObjectType(function, JS_FUNCTION_TYPE, scratch);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005027 __ j(not_equal, &slow, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005028
5029 // Null is not instance of anything.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005030 __ cmp(object, factory->null_value());
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005031 __ j(not_equal, &object_not_null, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005032 __ Set(eax, Immediate(Smi::FromInt(1)));
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005033 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005034
5035 __ bind(&object_not_null);
5036 // Smi values is not instance of anything.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005037 __ JumpIfNotSmi(object, &object_not_null_or_smi, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005038 __ Set(eax, Immediate(Smi::FromInt(1)));
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005039 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005040
5041 __ bind(&object_not_null_or_smi);
5042 // String values is not instance of anything.
5043 Condition is_string = masm->IsObjectStringType(object, scratch, scratch);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005044 __ j(NegateCondition(is_string), &slow, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005045 __ Set(eax, Immediate(Smi::FromInt(1)));
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005046 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005047
5048 // Slow-case: Go through the JavaScript implementation.
5049 __ bind(&slow);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005050 if (!ReturnTrueFalseObject()) {
5051 // Tail call the builtin which returns 0 or 1.
5052 if (HasArgsInRegisters()) {
5053 // Push arguments below return address.
5054 __ pop(scratch);
5055 __ push(object);
5056 __ push(function);
5057 __ push(scratch);
5058 }
5059 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
5060 } else {
5061 // Call the builtin and convert 0/1 to true/false.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005062 {
5063 FrameScope scope(masm, StackFrame::INTERNAL);
5064 __ push(object);
5065 __ push(function);
5066 __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION);
5067 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005068 Label true_value, done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005069 __ test(eax, eax);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005070 __ j(zero, &true_value, Label::kNear);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005071 __ mov(eax, factory->false_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005072 __ jmp(&done, Label::kNear);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005073 __ bind(&true_value);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005074 __ mov(eax, factory->true_value());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005075 __ bind(&done);
5076 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005077 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00005078}
5079
5080
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005081Register InstanceofStub::left() { return eax; }
5082
5083
5084Register InstanceofStub::right() { return edx; }
5085
5086
ricow@chromium.org65fae842010-08-25 15:26:24 +00005087int CompareStub::MinorKey() {
5088 // Encode the three parameters in a unique 16 bit value. To avoid duplicate
5089 // stubs the never NaN NaN condition is only taken into account if the
5090 // condition is equals.
5091 ASSERT(static_cast<unsigned>(cc_) < (1 << 12));
5092 ASSERT(lhs_.is(no_reg) && rhs_.is(no_reg));
5093 return ConditionField::encode(static_cast<unsigned>(cc_))
5094 | RegisterField::encode(false) // lhs_ and rhs_ are not used
5095 | StrictField::encode(strict_)
5096 | NeverNanNanField::encode(cc_ == equal ? never_nan_nan_ : false)
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00005097 | IncludeNumberCompareField::encode(include_number_compare_)
5098 | IncludeSmiCompareField::encode(include_smi_compare_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005099}
5100
5101
5102// Unfortunately you have to run without snapshots to see most of these
5103// names in the profile since most compare stubs end up in the snapshot.
whesse@chromium.org030d38e2011-07-13 13:23:34 +00005104void CompareStub::PrintName(StringStream* stream) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00005105 ASSERT(lhs_.is(no_reg) && rhs_.is(no_reg));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005106 const char* cc_name;
5107 switch (cc_) {
5108 case less: cc_name = "LT"; break;
5109 case greater: cc_name = "GT"; break;
5110 case less_equal: cc_name = "LE"; break;
5111 case greater_equal: cc_name = "GE"; break;
5112 case equal: cc_name = "EQ"; break;
5113 case not_equal: cc_name = "NE"; break;
5114 default: cc_name = "UnknownCondition"; break;
5115 }
whesse@chromium.org030d38e2011-07-13 13:23:34 +00005116 bool is_equality = cc_ == equal || cc_ == not_equal;
5117 stream->Add("CompareStub_%s", cc_name);
5118 if (strict_ && is_equality) stream->Add("_STRICT");
5119 if (never_nan_nan_ && is_equality) stream->Add("_NO_NAN");
5120 if (!include_number_compare_) stream->Add("_NO_NUMBER");
5121 if (!include_smi_compare_) stream->Add("_NO_SMI");
ricow@chromium.org65fae842010-08-25 15:26:24 +00005122}
5123
5124
5125// -------------------------------------------------------------------------
5126// StringCharCodeAtGenerator
5127
5128void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00005129 // If the receiver is a smi trigger the non-string case.
5130 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005131 __ JumpIfSmi(object_, receiver_not_string_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005132
5133 // Fetch the instance type of the receiver into result register.
5134 __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset));
5135 __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
5136 // If the receiver is not a string trigger the non-string case.
5137 __ test(result_, Immediate(kIsNotStringMask));
5138 __ j(not_zero, receiver_not_string_);
5139
5140 // If the index is non-smi trigger the non-smi case.
5141 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005142 __ JumpIfNotSmi(index_, &index_not_smi_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005143 __ bind(&got_smi_index_);
5144
5145 // Check for index out of range.
danno@chromium.orgc612e022011-11-10 11:38:15 +00005146 __ cmp(index_, FieldOperand(object_, String::kLengthOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005147 __ j(above_equal, index_out_of_range_);
5148
danno@chromium.orgc612e022011-11-10 11:38:15 +00005149 __ SmiUntag(index_);
erikcorry0ad885c2011-11-21 13:51:57 +00005150
5151 Factory* factory = masm->isolate()->factory();
5152 StringCharLoadGenerator::Generate(
5153 masm, factory, object_, index_, result_, &call_runtime_);
5154
ricow@chromium.org65fae842010-08-25 15:26:24 +00005155 __ SmiTag(result_);
5156 __ bind(&exit_);
5157}
5158
5159
5160void StringCharCodeAtGenerator::GenerateSlow(
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005161 MacroAssembler* masm,
5162 const RuntimeCallHelper& call_helper) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00005163 __ Abort("Unexpected fallthrough to CharCodeAt slow case");
5164
5165 // Index is not a smi.
5166 __ bind(&index_not_smi_);
5167 // If index is a heap number, try converting it to an integer.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005168 __ CheckMap(index_,
5169 masm->isolate()->factory()->heap_number_map(),
5170 index_not_number_,
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005171 DONT_DO_SMI_CHECK);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005172 call_helper.BeforeCall(masm);
5173 __ push(object_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005174 __ push(index_); // Consumed by runtime conversion function.
5175 if (index_flags_ == STRING_INDEX_IS_NUMBER) {
5176 __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1);
5177 } else {
5178 ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
5179 // NumberToSmi discards numbers that are not exact integers.
5180 __ CallRuntime(Runtime::kNumberToSmi, 1);
5181 }
danno@chromium.orgc612e022011-11-10 11:38:15 +00005182 if (!index_.is(eax)) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00005183 // Save the conversion result before the pop instructions below
5184 // have a chance to overwrite it.
danno@chromium.orgc612e022011-11-10 11:38:15 +00005185 __ mov(index_, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005186 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00005187 __ pop(object_);
5188 // Reload the instance type.
5189 __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset));
5190 __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
5191 call_helper.AfterCall(masm);
5192 // If index is still not a smi, it must be out of range.
5193 STATIC_ASSERT(kSmiTag == 0);
danno@chromium.orgc612e022011-11-10 11:38:15 +00005194 __ JumpIfNotSmi(index_, index_out_of_range_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005195 // Otherwise, return to the fast path.
5196 __ jmp(&got_smi_index_);
5197
5198 // Call runtime. We get here when the receiver is a string and the
5199 // index is a number, but the code of getting the actual character
5200 // is too complex (e.g., when the string needs to be flattened).
5201 __ bind(&call_runtime_);
5202 call_helper.BeforeCall(masm);
5203 __ push(object_);
erikcorry0ad885c2011-11-21 13:51:57 +00005204 __ SmiTag(index_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005205 __ push(index_);
5206 __ CallRuntime(Runtime::kStringCharCodeAt, 2);
5207 if (!result_.is(eax)) {
5208 __ mov(result_, eax);
5209 }
5210 call_helper.AfterCall(masm);
5211 __ jmp(&exit_);
5212
5213 __ Abort("Unexpected fallthrough from CharCodeAt slow case");
5214}
5215
5216
5217// -------------------------------------------------------------------------
5218// StringCharFromCodeGenerator
5219
5220void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
5221 // Fast case of Heap::LookupSingleCharacterStringFromCode.
5222 STATIC_ASSERT(kSmiTag == 0);
5223 STATIC_ASSERT(kSmiShiftSize == 0);
5224 ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
5225 __ test(code_,
5226 Immediate(kSmiTagMask |
5227 ((~String::kMaxAsciiCharCode) << kSmiTagSize)));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005228 __ j(not_zero, &slow_case_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005229
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005230 Factory* factory = masm->isolate()->factory();
5231 __ Set(result_, Immediate(factory->single_character_string_cache()));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005232 STATIC_ASSERT(kSmiTag == 0);
5233 STATIC_ASSERT(kSmiTagSize == 1);
5234 STATIC_ASSERT(kSmiShiftSize == 0);
5235 // At this point code register contains smi tagged ascii char code.
5236 __ mov(result_, FieldOperand(result_,
5237 code_, times_half_pointer_size,
5238 FixedArray::kHeaderSize));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005239 __ cmp(result_, factory->undefined_value());
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005240 __ j(equal, &slow_case_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005241 __ bind(&exit_);
5242}
5243
5244
5245void StringCharFromCodeGenerator::GenerateSlow(
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005246 MacroAssembler* masm,
5247 const RuntimeCallHelper& call_helper) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00005248 __ Abort("Unexpected fallthrough to CharFromCode slow case");
5249
5250 __ bind(&slow_case_);
5251 call_helper.BeforeCall(masm);
5252 __ push(code_);
5253 __ CallRuntime(Runtime::kCharFromCode, 1);
5254 if (!result_.is(eax)) {
5255 __ mov(result_, eax);
5256 }
5257 call_helper.AfterCall(masm);
5258 __ jmp(&exit_);
5259
5260 __ Abort("Unexpected fallthrough from CharFromCode slow case");
5261}
5262
5263
5264// -------------------------------------------------------------------------
5265// StringCharAtGenerator
5266
5267void StringCharAtGenerator::GenerateFast(MacroAssembler* masm) {
5268 char_code_at_generator_.GenerateFast(masm);
5269 char_from_code_generator_.GenerateFast(masm);
5270}
5271
5272
5273void StringCharAtGenerator::GenerateSlow(
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005274 MacroAssembler* masm,
5275 const RuntimeCallHelper& call_helper) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00005276 char_code_at_generator_.GenerateSlow(masm, call_helper);
5277 char_from_code_generator_.GenerateSlow(masm, call_helper);
5278}
5279
5280
5281void StringAddStub::Generate(MacroAssembler* masm) {
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005282 Label string_add_runtime, call_builtin;
5283 Builtins::JavaScript builtin_id = Builtins::ADD;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005284
5285 // Load the two arguments.
5286 __ mov(eax, Operand(esp, 2 * kPointerSize)); // First argument.
5287 __ mov(edx, Operand(esp, 1 * kPointerSize)); // Second argument.
5288
5289 // Make sure that both arguments are strings if not known in advance.
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005290 if (flags_ == NO_STRING_ADD_FLAGS) {
whesse@chromium.org7b260152011-06-20 15:33:18 +00005291 __ JumpIfSmi(eax, &string_add_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005292 __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ebx);
5293 __ j(above_equal, &string_add_runtime);
5294
5295 // First argument is a a string, test second.
whesse@chromium.org7b260152011-06-20 15:33:18 +00005296 __ JumpIfSmi(edx, &string_add_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005297 __ CmpObjectType(edx, FIRST_NONSTRING_TYPE, ebx);
5298 __ j(above_equal, &string_add_runtime);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005299 } else {
5300 // Here at least one of the arguments is definitely a string.
5301 // We convert the one that is not known to be a string.
5302 if ((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) == 0) {
5303 ASSERT((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) != 0);
5304 GenerateConvertArgument(masm, 2 * kPointerSize, eax, ebx, ecx, edi,
5305 &call_builtin);
5306 builtin_id = Builtins::STRING_ADD_RIGHT;
5307 } else if ((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) == 0) {
5308 ASSERT((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) != 0);
5309 GenerateConvertArgument(masm, 1 * kPointerSize, edx, ebx, ecx, edi,
5310 &call_builtin);
5311 builtin_id = Builtins::STRING_ADD_LEFT;
5312 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00005313 }
5314
5315 // Both arguments are strings.
5316 // eax: first string
5317 // edx: second string
5318 // Check if either of the strings are empty. In that case return the other.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005319 Label second_not_zero_length, both_not_zero_length;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005320 __ mov(ecx, FieldOperand(edx, String::kLengthOffset));
5321 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005322 __ test(ecx, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005323 __ j(not_zero, &second_not_zero_length, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005324 // Second string is empty, result is first string which is already in eax.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005325 Counters* counters = masm->isolate()->counters();
5326 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005327 __ ret(2 * kPointerSize);
5328 __ bind(&second_not_zero_length);
5329 __ mov(ebx, FieldOperand(eax, String::kLengthOffset));
5330 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005331 __ test(ebx, ebx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005332 __ j(not_zero, &both_not_zero_length, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005333 // First string is empty, result is second string which is in edx.
5334 __ mov(eax, edx);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005335 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005336 __ ret(2 * kPointerSize);
5337
5338 // Both strings are non-empty.
5339 // eax: first string
5340 // ebx: length of first string as a smi
5341 // ecx: length of second string as a smi
5342 // edx: second string
5343 // Look at the length of the result of adding the two strings.
5344 Label string_add_flat_result, longer_than_two;
5345 __ bind(&both_not_zero_length);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005346 __ add(ebx, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005347 STATIC_ASSERT(Smi::kMaxValue == String::kMaxLength);
5348 // Handle exceptionally long strings in the runtime system.
5349 __ j(overflow, &string_add_runtime);
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +00005350 // Use the symbol table when adding two one character strings, as it
5351 // helps later optimizations to return a symbol here.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005352 __ cmp(ebx, Immediate(Smi::FromInt(2)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005353 __ j(not_equal, &longer_than_two);
5354
5355 // Check that both strings are non-external ascii strings.
5356 __ JumpIfNotBothSequentialAsciiStrings(eax, edx, ebx, ecx,
5357 &string_add_runtime);
5358
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005359 // Get the two characters forming the new string.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005360 __ movzx_b(ebx, FieldOperand(eax, SeqAsciiString::kHeaderSize));
5361 __ movzx_b(ecx, FieldOperand(edx, SeqAsciiString::kHeaderSize));
5362
5363 // Try to lookup two character string in symbol table. If it is not found
5364 // just allocate a new one.
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005365 Label make_two_character_string, make_two_character_string_no_reload;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005366 StringHelper::GenerateTwoCharacterSymbolTableProbe(
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005367 masm, ebx, ecx, eax, edx, edi,
5368 &make_two_character_string_no_reload, &make_two_character_string);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005369 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005370 __ ret(2 * kPointerSize);
5371
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005372 // Allocate a two character string.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005373 __ bind(&make_two_character_string);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005374 // Reload the arguments.
5375 __ mov(eax, Operand(esp, 2 * kPointerSize)); // First argument.
5376 __ mov(edx, Operand(esp, 1 * kPointerSize)); // Second argument.
5377 // Get the two characters forming the new string.
5378 __ movzx_b(ebx, FieldOperand(eax, SeqAsciiString::kHeaderSize));
5379 __ movzx_b(ecx, FieldOperand(edx, SeqAsciiString::kHeaderSize));
5380 __ bind(&make_two_character_string_no_reload);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005381 __ IncrementCounter(counters->string_add_make_two_char(), 1);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005382 __ AllocateAsciiString(eax, // Result.
5383 2, // Length.
5384 edi, // Scratch 1.
5385 edx, // Scratch 2.
5386 &string_add_runtime);
5387 // Pack both characters in ebx.
5388 __ shl(ecx, kBitsPerByte);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005389 __ or_(ebx, ecx);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005390 // Set the characters in the new string.
5391 __ mov_w(FieldOperand(eax, SeqAsciiString::kHeaderSize), ebx);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005392 __ IncrementCounter(counters->string_add_native(), 1);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005393 __ ret(2 * kPointerSize);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005394
5395 __ bind(&longer_than_two);
5396 // Check if resulting string will be flat.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005397 __ cmp(ebx, Immediate(Smi::FromInt(String::kMinNonFlatLength)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005398 __ j(below, &string_add_flat_result);
5399
5400 // If result is not supposed to be flat allocate a cons string object. If both
5401 // strings are ascii the result is an ascii cons string.
5402 Label non_ascii, allocated, ascii_data;
5403 __ mov(edi, FieldOperand(eax, HeapObject::kMapOffset));
5404 __ movzx_b(ecx, FieldOperand(edi, Map::kInstanceTypeOffset));
5405 __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
5406 __ movzx_b(edi, FieldOperand(edi, Map::kInstanceTypeOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005407 __ and_(ecx, edi);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00005408 STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0);
5409 STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
5410 __ test(ecx, Immediate(kStringEncodingMask));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005411 __ j(zero, &non_ascii);
5412 __ bind(&ascii_data);
5413 // Allocate an acsii cons string.
5414 __ AllocateAsciiConsString(ecx, edi, no_reg, &string_add_runtime);
5415 __ bind(&allocated);
5416 // Fill the fields of the cons string.
5417 if (FLAG_debug_code) __ AbortIfNotSmi(ebx);
5418 __ mov(FieldOperand(ecx, ConsString::kLengthOffset), ebx);
5419 __ mov(FieldOperand(ecx, ConsString::kHashFieldOffset),
5420 Immediate(String::kEmptyHashField));
5421 __ mov(FieldOperand(ecx, ConsString::kFirstOffset), eax);
5422 __ mov(FieldOperand(ecx, ConsString::kSecondOffset), edx);
5423 __ mov(eax, ecx);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005424 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005425 __ ret(2 * kPointerSize);
5426 __ bind(&non_ascii);
5427 // At least one of the strings is two-byte. Check whether it happens
5428 // to contain only ascii characters.
5429 // ecx: first instance type AND second instance type.
5430 // edi: second instance type.
5431 __ test(ecx, Immediate(kAsciiDataHintMask));
5432 __ j(not_zero, &ascii_data);
5433 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
5434 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005435 __ xor_(edi, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005436 STATIC_ASSERT(kAsciiStringTag != 0 && kAsciiDataHintTag != 0);
5437 __ and_(edi, kAsciiStringTag | kAsciiDataHintTag);
5438 __ cmp(edi, kAsciiStringTag | kAsciiDataHintTag);
5439 __ j(equal, &ascii_data);
5440 // Allocate a two byte cons string.
fschneider@chromium.org1805e212011-09-05 10:49:12 +00005441 __ AllocateTwoByteConsString(ecx, edi, no_reg, &string_add_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005442 __ jmp(&allocated);
5443
5444 // Handle creating a flat result. First check that both strings are not
5445 // external strings.
5446 // eax: first string
5447 // ebx: length of resulting flat string as a smi
5448 // edx: second string
5449 __ bind(&string_add_flat_result);
5450 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
5451 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
5452 __ and_(ecx, kStringRepresentationMask);
5453 __ cmp(ecx, kExternalStringTag);
5454 __ j(equal, &string_add_runtime);
5455 __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
5456 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
5457 __ and_(ecx, kStringRepresentationMask);
5458 __ cmp(ecx, kExternalStringTag);
5459 __ j(equal, &string_add_runtime);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005460 // We cannot encounter sliced strings here since:
5461 STATIC_ASSERT(SlicedString::kMinLength >= String::kMinNonFlatLength);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005462 // Now check if both strings are ascii strings.
5463 // eax: first string
5464 // ebx: length of resulting flat string as a smi
5465 // edx: second string
5466 Label non_ascii_string_add_flat_result;
fschneider@chromium.org1805e212011-09-05 10:49:12 +00005467 STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0);
5468 STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005469 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
fschneider@chromium.org1805e212011-09-05 10:49:12 +00005470 __ test_b(FieldOperand(ecx, Map::kInstanceTypeOffset), kStringEncodingMask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005471 __ j(zero, &non_ascii_string_add_flat_result);
5472 __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
fschneider@chromium.org1805e212011-09-05 10:49:12 +00005473 __ test_b(FieldOperand(ecx, Map::kInstanceTypeOffset), kStringEncodingMask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005474 __ j(zero, &string_add_runtime);
5475
ricow@chromium.org65fae842010-08-25 15:26:24 +00005476 // Both strings are ascii strings. As they are short they are both flat.
5477 // ebx: length of resulting flat string as a smi
5478 __ SmiUntag(ebx);
5479 __ AllocateAsciiString(eax, ebx, ecx, edx, edi, &string_add_runtime);
5480 // eax: result string
5481 __ mov(ecx, eax);
5482 // Locate first character of result.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005483 __ add(ecx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005484 // Load first argument and locate first character.
5485 __ mov(edx, Operand(esp, 2 * kPointerSize));
5486 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
5487 __ SmiUntag(edi);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005488 __ add(edx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005489 // eax: result string
5490 // ecx: first character of result
5491 // edx: first char of first argument
5492 // edi: length of first argument
5493 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
5494 // Load second argument and locate first character.
5495 __ mov(edx, Operand(esp, 1 * kPointerSize));
5496 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
5497 __ SmiUntag(edi);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005498 __ add(edx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005499 // eax: result string
5500 // ecx: next character of result
5501 // edx: first char of second argument
5502 // edi: length of second argument
5503 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005504 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005505 __ ret(2 * kPointerSize);
5506
5507 // Handle creating a flat two byte result.
5508 // eax: first string - known to be two byte
5509 // ebx: length of resulting flat string as a smi
5510 // edx: second string
5511 __ bind(&non_ascii_string_add_flat_result);
5512 __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
fschneider@chromium.org1805e212011-09-05 10:49:12 +00005513 __ test_b(FieldOperand(ecx, Map::kInstanceTypeOffset), kStringEncodingMask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005514 __ j(not_zero, &string_add_runtime);
5515 // Both strings are two byte strings. As they are short they are both
5516 // flat.
5517 __ SmiUntag(ebx);
5518 __ AllocateTwoByteString(eax, ebx, ecx, edx, edi, &string_add_runtime);
5519 // eax: result string
5520 __ mov(ecx, eax);
5521 // Locate first character of result.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005522 __ add(ecx,
ricow@chromium.org65fae842010-08-25 15:26:24 +00005523 Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
5524 // Load first argument and locate first character.
5525 __ mov(edx, Operand(esp, 2 * kPointerSize));
5526 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
5527 __ SmiUntag(edi);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005528 __ add(edx,
ricow@chromium.org65fae842010-08-25 15:26:24 +00005529 Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
5530 // eax: result string
5531 // ecx: first character of result
5532 // edx: first char of first argument
5533 // edi: length of first argument
5534 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
5535 // Load second argument and locate first character.
5536 __ mov(edx, Operand(esp, 1 * kPointerSize));
5537 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
5538 __ SmiUntag(edi);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005539 __ add(edx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005540 // eax: result string
5541 // ecx: next character of result
5542 // edx: first char of second argument
5543 // edi: length of second argument
5544 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005545 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005546 __ ret(2 * kPointerSize);
5547
5548 // Just jump to runtime to add the two strings.
5549 __ bind(&string_add_runtime);
5550 __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005551
5552 if (call_builtin.is_linked()) {
5553 __ bind(&call_builtin);
5554 __ InvokeBuiltin(builtin_id, JUMP_FUNCTION);
5555 }
5556}
5557
5558
5559void StringAddStub::GenerateConvertArgument(MacroAssembler* masm,
5560 int stack_offset,
5561 Register arg,
5562 Register scratch1,
5563 Register scratch2,
5564 Register scratch3,
5565 Label* slow) {
5566 // First check if the argument is already a string.
5567 Label not_string, done;
whesse@chromium.org7b260152011-06-20 15:33:18 +00005568 __ JumpIfSmi(arg, &not_string);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005569 __ CmpObjectType(arg, FIRST_NONSTRING_TYPE, scratch1);
5570 __ j(below, &done);
5571
5572 // Check the number to string cache.
5573 Label not_cached;
5574 __ bind(&not_string);
5575 // Puts the cached result into scratch1.
5576 NumberToStringStub::GenerateLookupNumberStringCache(masm,
5577 arg,
5578 scratch1,
5579 scratch2,
5580 scratch3,
5581 false,
5582 &not_cached);
5583 __ mov(arg, scratch1);
5584 __ mov(Operand(esp, stack_offset), arg);
5585 __ jmp(&done);
5586
5587 // Check if the argument is a safe string wrapper.
5588 __ bind(&not_cached);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005589 __ JumpIfSmi(arg, slow);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005590 __ CmpObjectType(arg, JS_VALUE_TYPE, scratch1); // map -> scratch1.
5591 __ j(not_equal, slow);
5592 __ test_b(FieldOperand(scratch1, Map::kBitField2Offset),
5593 1 << Map::kStringWrapperSafeForDefaultValueOf);
5594 __ j(zero, slow);
5595 __ mov(arg, FieldOperand(arg, JSValue::kValueOffset));
5596 __ mov(Operand(esp, stack_offset), arg);
5597
5598 __ bind(&done);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005599}
5600
5601
5602void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
5603 Register dest,
5604 Register src,
5605 Register count,
5606 Register scratch,
5607 bool ascii) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005608 Label loop;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005609 __ bind(&loop);
5610 // This loop just copies one character at a time, as it is only used for very
5611 // short strings.
5612 if (ascii) {
5613 __ mov_b(scratch, Operand(src, 0));
5614 __ mov_b(Operand(dest, 0), scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005615 __ add(src, Immediate(1));
5616 __ add(dest, Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005617 } else {
5618 __ mov_w(scratch, Operand(src, 0));
5619 __ mov_w(Operand(dest, 0), scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005620 __ add(src, Immediate(2));
5621 __ add(dest, Immediate(2));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005622 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005623 __ sub(count, Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005624 __ j(not_zero, &loop);
5625}
5626
5627
5628void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm,
5629 Register dest,
5630 Register src,
5631 Register count,
5632 Register scratch,
5633 bool ascii) {
5634 // Copy characters using rep movs of doublewords.
5635 // The destination is aligned on a 4 byte boundary because we are
5636 // copying to the beginning of a newly allocated string.
5637 ASSERT(dest.is(edi)); // rep movs destination
5638 ASSERT(src.is(esi)); // rep movs source
5639 ASSERT(count.is(ecx)); // rep movs count
5640 ASSERT(!scratch.is(dest));
5641 ASSERT(!scratch.is(src));
5642 ASSERT(!scratch.is(count));
5643
5644 // Nothing to do for zero characters.
5645 Label done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005646 __ test(count, count);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005647 __ j(zero, &done);
5648
5649 // Make count the number of bytes to copy.
5650 if (!ascii) {
5651 __ shl(count, 1);
5652 }
5653
5654 // Don't enter the rep movs if there are less than 4 bytes to copy.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005655 Label last_bytes;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005656 __ test(count, Immediate(~3));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005657 __ j(zero, &last_bytes, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005658
5659 // Copy from edi to esi using rep movs instruction.
5660 __ mov(scratch, count);
5661 __ sar(count, 2); // Number of doublewords to copy.
5662 __ cld();
5663 __ rep_movs();
5664
5665 // Find number of bytes left.
5666 __ mov(count, scratch);
5667 __ and_(count, 3);
5668
5669 // Check if there are more bytes to copy.
5670 __ bind(&last_bytes);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005671 __ test(count, count);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005672 __ j(zero, &done);
5673
5674 // Copy remaining characters.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005675 Label loop;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005676 __ bind(&loop);
5677 __ mov_b(scratch, Operand(src, 0));
5678 __ mov_b(Operand(dest, 0), scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005679 __ add(src, Immediate(1));
5680 __ add(dest, Immediate(1));
5681 __ sub(count, Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005682 __ j(not_zero, &loop);
5683
5684 __ bind(&done);
5685}
5686
5687
5688void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
5689 Register c1,
5690 Register c2,
5691 Register scratch1,
5692 Register scratch2,
5693 Register scratch3,
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005694 Label* not_probed,
ricow@chromium.org65fae842010-08-25 15:26:24 +00005695 Label* not_found) {
5696 // Register scratch3 is the general scratch register in this function.
5697 Register scratch = scratch3;
5698
5699 // Make sure that both characters are not digits as such strings has a
5700 // different hash algorithm. Don't try to look for these in the symbol table.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005701 Label not_array_index;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005702 __ mov(scratch, c1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005703 __ sub(scratch, Immediate(static_cast<int>('0')));
5704 __ cmp(scratch, Immediate(static_cast<int>('9' - '0')));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005705 __ j(above, &not_array_index, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005706 __ mov(scratch, c2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005707 __ sub(scratch, Immediate(static_cast<int>('0')));
5708 __ cmp(scratch, Immediate(static_cast<int>('9' - '0')));
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005709 __ j(below_equal, not_probed);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005710
5711 __ bind(&not_array_index);
5712 // Calculate the two character string hash.
5713 Register hash = scratch1;
5714 GenerateHashInit(masm, hash, c1, scratch);
5715 GenerateHashAddCharacter(masm, hash, c2, scratch);
5716 GenerateHashGetHash(masm, hash, scratch);
5717
5718 // Collect the two characters in a register.
5719 Register chars = c1;
5720 __ shl(c2, kBitsPerByte);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005721 __ or_(chars, c2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005722
5723 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
5724 // hash: hash of two character string.
5725
5726 // Load the symbol table.
5727 Register symbol_table = c2;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005728 ExternalReference roots_array_start =
5729 ExternalReference::roots_array_start(masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00005730 __ mov(scratch, Immediate(Heap::kSymbolTableRootIndex));
5731 __ mov(symbol_table,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005732 Operand::StaticArray(scratch, times_pointer_size, roots_array_start));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005733
5734 // Calculate capacity mask from the symbol table capacity.
5735 Register mask = scratch2;
5736 __ mov(mask, FieldOperand(symbol_table, SymbolTable::kCapacityOffset));
5737 __ SmiUntag(mask);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005738 __ sub(mask, Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005739
5740 // Registers
5741 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
5742 // hash: hash of two character string
5743 // symbol_table: symbol table
5744 // mask: capacity mask
5745 // scratch: -
5746
5747 // Perform a number of probes in the symbol table.
5748 static const int kProbes = 4;
5749 Label found_in_symbol_table;
5750 Label next_probe[kProbes], next_probe_pop_mask[kProbes];
danno@chromium.org2c456792011-11-11 12:00:53 +00005751 Register candidate = scratch; // Scratch register contains candidate.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005752 for (int i = 0; i < kProbes; i++) {
5753 // Calculate entry in symbol table.
5754 __ mov(scratch, hash);
5755 if (i > 0) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005756 __ add(scratch, Immediate(SymbolTable::GetProbeOffset(i)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005757 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005758 __ and_(scratch, mask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005759
5760 // Load the entry from the symbol table.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005761 STATIC_ASSERT(SymbolTable::kEntrySize == 1);
5762 __ mov(candidate,
5763 FieldOperand(symbol_table,
5764 scratch,
5765 times_pointer_size,
5766 SymbolTable::kElementsStartOffset));
5767
5768 // If entry is undefined no string with this hash can be found.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005769 Factory* factory = masm->isolate()->factory();
5770 __ cmp(candidate, factory->undefined_value());
ricow@chromium.org65fae842010-08-25 15:26:24 +00005771 __ j(equal, not_found);
danno@chromium.org2c456792011-11-11 12:00:53 +00005772 __ cmp(candidate, factory->the_hole_value());
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +00005773 __ j(equal, &next_probe[i]);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005774
5775 // If length is not 2 the string is not a candidate.
5776 __ cmp(FieldOperand(candidate, String::kLengthOffset),
5777 Immediate(Smi::FromInt(2)));
5778 __ j(not_equal, &next_probe[i]);
5779
5780 // As we are out of registers save the mask on the stack and use that
5781 // register as a temporary.
5782 __ push(mask);
5783 Register temp = mask;
5784
5785 // Check that the candidate is a non-external ascii string.
5786 __ mov(temp, FieldOperand(candidate, HeapObject::kMapOffset));
5787 __ movzx_b(temp, FieldOperand(temp, Map::kInstanceTypeOffset));
5788 __ JumpIfInstanceTypeIsNotSequentialAscii(
5789 temp, temp, &next_probe_pop_mask[i]);
5790
5791 // Check if the two characters match.
5792 __ mov(temp, FieldOperand(candidate, SeqAsciiString::kHeaderSize));
5793 __ and_(temp, 0x0000ffff);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005794 __ cmp(chars, temp);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005795 __ j(equal, &found_in_symbol_table);
5796 __ bind(&next_probe_pop_mask[i]);
5797 __ pop(mask);
5798 __ bind(&next_probe[i]);
5799 }
5800
5801 // No matching 2 character string found by probing.
5802 __ jmp(not_found);
5803
5804 // Scratch register contains result when we fall through to here.
danno@chromium.org2c456792011-11-11 12:00:53 +00005805 Register result = candidate;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005806 __ bind(&found_in_symbol_table);
5807 __ pop(mask); // Pop saved mask from the stack.
5808 if (!result.is(eax)) {
5809 __ mov(eax, result);
5810 }
5811}
5812
5813
5814void StringHelper::GenerateHashInit(MacroAssembler* masm,
5815 Register hash,
5816 Register character,
5817 Register scratch) {
5818 // hash = character + (character << 10);
5819 __ mov(hash, character);
5820 __ shl(hash, 10);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005821 __ add(hash, character);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005822 // hash ^= hash >> 6;
5823 __ mov(scratch, hash);
danno@chromium.org2c456792011-11-11 12:00:53 +00005824 __ shr(scratch, 6);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005825 __ xor_(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005826}
5827
5828
5829void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
5830 Register hash,
5831 Register character,
5832 Register scratch) {
5833 // hash += character;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005834 __ add(hash, character);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005835 // hash += hash << 10;
5836 __ mov(scratch, hash);
5837 __ shl(scratch, 10);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005838 __ add(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005839 // hash ^= hash >> 6;
5840 __ mov(scratch, hash);
danno@chromium.org2c456792011-11-11 12:00:53 +00005841 __ shr(scratch, 6);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005842 __ xor_(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005843}
5844
5845
5846void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
5847 Register hash,
5848 Register scratch) {
5849 // hash += hash << 3;
5850 __ mov(scratch, hash);
5851 __ shl(scratch, 3);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005852 __ add(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005853 // hash ^= hash >> 11;
5854 __ mov(scratch, hash);
danno@chromium.org2c456792011-11-11 12:00:53 +00005855 __ shr(scratch, 11);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005856 __ xor_(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005857 // hash += hash << 15;
5858 __ mov(scratch, hash);
5859 __ shl(scratch, 15);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005860 __ add(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005861
danno@chromium.org2c456792011-11-11 12:00:53 +00005862 uint32_t kHashShiftCutOffMask = (1 << (32 - String::kHashShift)) - 1;
5863 __ and_(hash, kHashShiftCutOffMask);
5864
ricow@chromium.org65fae842010-08-25 15:26:24 +00005865 // if (hash == 0) hash = 27;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005866 Label hash_not_zero;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005867 __ test(hash, hash);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005868 __ j(not_zero, &hash_not_zero, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005869 __ mov(hash, Immediate(27));
5870 __ bind(&hash_not_zero);
5871}
5872
5873
5874void SubStringStub::Generate(MacroAssembler* masm) {
5875 Label runtime;
5876
5877 // Stack frame on entry.
5878 // esp[0]: return address
5879 // esp[4]: to
5880 // esp[8]: from
5881 // esp[12]: string
5882
5883 // Make sure first argument is a string.
5884 __ mov(eax, Operand(esp, 3 * kPointerSize));
5885 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005886 __ JumpIfSmi(eax, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005887 Condition is_string = masm->IsObjectStringType(eax, ebx, ebx);
5888 __ j(NegateCondition(is_string), &runtime);
5889
5890 // eax: string
5891 // ebx: instance type
5892
5893 // Calculate length of sub string using the smi values.
5894 Label result_longer_than_two;
5895 __ mov(ecx, Operand(esp, 1 * kPointerSize)); // To index.
whesse@chromium.org7b260152011-06-20 15:33:18 +00005896 __ JumpIfNotSmi(ecx, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005897 __ mov(edx, Operand(esp, 2 * kPointerSize)); // From index.
whesse@chromium.org7b260152011-06-20 15:33:18 +00005898 __ JumpIfNotSmi(edx, &runtime);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005899 __ sub(ecx, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005900 __ cmp(ecx, FieldOperand(eax, String::kLengthOffset));
5901 Label return_eax;
5902 __ j(equal, &return_eax);
5903 // Special handling of sub-strings of length 1 and 2. One character strings
5904 // are handled in the runtime system (looked up in the single character
5905 // cache). Two character strings are looked for in the symbol cache.
5906 __ SmiUntag(ecx); // Result length is no longer smi.
5907 __ cmp(ecx, 2);
5908 __ j(greater, &result_longer_than_two);
5909 __ j(less, &runtime);
5910
5911 // Sub string of length 2 requested.
5912 // eax: string
5913 // ebx: instance type
5914 // ecx: sub string length (value is 2)
5915 // edx: from index (smi)
5916 __ JumpIfInstanceTypeIsNotSequentialAscii(ebx, ebx, &runtime);
5917
5918 // Get the two characters forming the sub string.
5919 __ SmiUntag(edx); // From index is no longer smi.
5920 __ movzx_b(ebx, FieldOperand(eax, edx, times_1, SeqAsciiString::kHeaderSize));
5921 __ movzx_b(ecx,
5922 FieldOperand(eax, edx, times_1, SeqAsciiString::kHeaderSize + 1));
5923
5924 // Try to lookup two character string in symbol table.
5925 Label make_two_character_string;
5926 StringHelper::GenerateTwoCharacterSymbolTableProbe(
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005927 masm, ebx, ecx, eax, edx, edi,
5928 &make_two_character_string, &make_two_character_string);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005929 __ ret(3 * kPointerSize);
5930
5931 __ bind(&make_two_character_string);
5932 // Setup registers for allocating the two character string.
5933 __ mov(eax, Operand(esp, 3 * kPointerSize));
5934 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
5935 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
5936 __ Set(ecx, Immediate(2));
5937
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00005938 if (FLAG_string_slices) {
5939 Label copy_routine;
5940 // If coming from the make_two_character_string path, the string
5941 // is too short to be sliced anyways.
5942 STATIC_ASSERT(2 < SlicedString::kMinLength);
5943 __ jmp(&copy_routine);
5944 __ bind(&result_longer_than_two);
5945
5946 // eax: string
5947 // ebx: instance type
5948 // ecx: sub string length
5949 // edx: from index (smi)
erikcorry0ad885c2011-11-21 13:51:57 +00005950 Label allocate_slice, sliced_string, seq_or_external_string;
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00005951 __ cmp(ecx, SlicedString::kMinLength);
5952 // Short slice. Copy instead of slicing.
5953 __ j(less, &copy_routine);
erikcorry0ad885c2011-11-21 13:51:57 +00005954 // If the string is not indirect, it can only be sequential or external.
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00005955 STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
5956 STATIC_ASSERT(kIsIndirectStringMask != 0);
5957 __ test(ebx, Immediate(kIsIndirectStringMask));
erikcorry0ad885c2011-11-21 13:51:57 +00005958 __ j(zero, &seq_or_external_string, Label::kNear);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00005959
5960 Factory* factory = masm->isolate()->factory();
5961 __ test(ebx, Immediate(kSlicedNotConsMask));
5962 __ j(not_zero, &sliced_string, Label::kNear);
5963 // Cons string. Check whether it is flat, then fetch first part.
5964 __ cmp(FieldOperand(eax, ConsString::kSecondOffset),
5965 factory->empty_string());
5966 __ j(not_equal, &runtime);
5967 __ mov(edi, FieldOperand(eax, ConsString::kFirstOffset));
5968 __ jmp(&allocate_slice, Label::kNear);
5969
5970 __ bind(&sliced_string);
5971 // Sliced string. Fetch parent and correct start index by offset.
5972 __ add(edx, FieldOperand(eax, SlicedString::kOffsetOffset));
5973 __ mov(edi, FieldOperand(eax, SlicedString::kParentOffset));
5974 __ jmp(&allocate_slice, Label::kNear);
5975
erikcorry0ad885c2011-11-21 13:51:57 +00005976 __ bind(&seq_or_external_string);
5977 // Sequential or external string. Just move string to the correct register.
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00005978 __ mov(edi, eax);
5979
5980 __ bind(&allocate_slice);
5981 // edi: underlying subject string
5982 // ebx: instance type of original subject string
5983 // edx: offset
5984 // ecx: length
5985 // Allocate new sliced string. At this point we do not reload the instance
5986 // type including the string encoding because we simply rely on the info
5987 // provided by the original string. It does not matter if the original
5988 // string's encoding is wrong because we always have to recheck encoding of
5989 // the newly created string's parent anyways due to externalized strings.
5990 Label two_byte_slice, set_slice_header;
fschneider@chromium.org1805e212011-09-05 10:49:12 +00005991 STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0);
5992 STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
5993 __ test(ebx, Immediate(kStringEncodingMask));
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00005994 __ j(zero, &two_byte_slice, Label::kNear);
5995 __ AllocateAsciiSlicedString(eax, ebx, no_reg, &runtime);
5996 __ jmp(&set_slice_header, Label::kNear);
5997 __ bind(&two_byte_slice);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00005998 __ AllocateTwoByteSlicedString(eax, ebx, no_reg, &runtime);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00005999 __ bind(&set_slice_header);
6000 __ mov(FieldOperand(eax, SlicedString::kOffsetOffset), edx);
6001 __ SmiTag(ecx);
6002 __ mov(FieldOperand(eax, SlicedString::kLengthOffset), ecx);
6003 __ mov(FieldOperand(eax, SlicedString::kParentOffset), edi);
6004 __ mov(FieldOperand(eax, SlicedString::kHashFieldOffset),
6005 Immediate(String::kEmptyHashField));
6006 __ jmp(&return_eax);
6007
6008 __ bind(&copy_routine);
6009 } else {
6010 __ bind(&result_longer_than_two);
6011 }
6012
ricow@chromium.org65fae842010-08-25 15:26:24 +00006013 // eax: string
6014 // ebx: instance type
6015 // ecx: result string length
6016 // Check for flat ascii string
6017 Label non_ascii_flat;
6018 __ JumpIfInstanceTypeIsNotSequentialAscii(ebx, ebx, &non_ascii_flat);
6019
6020 // Allocate the result.
6021 __ AllocateAsciiString(eax, ecx, ebx, edx, edi, &runtime);
6022
6023 // eax: result string
6024 // ecx: result string length
6025 __ mov(edx, esi); // esi used by following code.
6026 // Locate first character of result.
6027 __ mov(edi, eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006028 __ add(edi, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006029 // Load string argument and locate character of sub string start.
6030 __ mov(esi, Operand(esp, 3 * kPointerSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006031 __ add(esi, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006032 __ mov(ebx, Operand(esp, 2 * kPointerSize)); // from
6033 __ SmiUntag(ebx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006034 __ add(esi, ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006035
6036 // eax: result string
6037 // ecx: result length
6038 // edx: original value of esi
6039 // edi: first character of result
6040 // esi: character of sub string start
6041 StringHelper::GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, true);
6042 __ mov(esi, edx); // Restore esi.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006043 Counters* counters = masm->isolate()->counters();
6044 __ IncrementCounter(counters->sub_string_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006045 __ ret(3 * kPointerSize);
6046
6047 __ bind(&non_ascii_flat);
6048 // eax: string
6049 // ebx: instance type & kStringRepresentationMask | kStringEncodingMask
6050 // ecx: result string length
6051 // Check for flat two byte string
6052 __ cmp(ebx, kSeqStringTag | kTwoByteStringTag);
6053 __ j(not_equal, &runtime);
6054
6055 // Allocate the result.
6056 __ AllocateTwoByteString(eax, ecx, ebx, edx, edi, &runtime);
6057
6058 // eax: result string
6059 // ecx: result string length
6060 __ mov(edx, esi); // esi used by following code.
6061 // Locate first character of result.
6062 __ mov(edi, eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006063 __ add(edi,
ricow@chromium.org65fae842010-08-25 15:26:24 +00006064 Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
6065 // Load string argument and locate character of sub string start.
6066 __ mov(esi, Operand(esp, 3 * kPointerSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006067 __ add(esi, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006068 __ mov(ebx, Operand(esp, 2 * kPointerSize)); // from
6069 // As from is a smi it is 2 times the value which matches the size of a two
6070 // byte character.
6071 STATIC_ASSERT(kSmiTag == 0);
6072 STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006073 __ add(esi, ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006074
6075 // eax: result string
6076 // ecx: result length
6077 // edx: original value of esi
6078 // edi: first character of result
6079 // esi: character of sub string start
6080 StringHelper::GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, false);
6081 __ mov(esi, edx); // Restore esi.
6082
6083 __ bind(&return_eax);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006084 __ IncrementCounter(counters->sub_string_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006085 __ ret(3 * kPointerSize);
6086
6087 // Just jump to runtime to create the sub string.
6088 __ bind(&runtime);
6089 __ TailCallRuntime(Runtime::kSubString, 3, 1);
6090}
6091
6092
lrn@chromium.org1c092762011-05-09 09:42:16 +00006093void StringCompareStub::GenerateFlatAsciiStringEquals(MacroAssembler* masm,
6094 Register left,
6095 Register right,
6096 Register scratch1,
6097 Register scratch2) {
6098 Register length = scratch1;
6099
6100 // Compare lengths.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006101 Label strings_not_equal, check_zero_length;
lrn@chromium.org1c092762011-05-09 09:42:16 +00006102 __ mov(length, FieldOperand(left, String::kLengthOffset));
6103 __ cmp(length, FieldOperand(right, String::kLengthOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006104 __ j(equal, &check_zero_length, Label::kNear);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006105 __ bind(&strings_not_equal);
6106 __ Set(eax, Immediate(Smi::FromInt(NOT_EQUAL)));
6107 __ ret(0);
6108
6109 // Check if the length is zero.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006110 Label compare_chars;
lrn@chromium.org1c092762011-05-09 09:42:16 +00006111 __ bind(&check_zero_length);
6112 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006113 __ test(length, length);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006114 __ j(not_zero, &compare_chars, Label::kNear);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006115 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6116 __ ret(0);
6117
6118 // Compare characters.
6119 __ bind(&compare_chars);
6120 GenerateAsciiCharsCompareLoop(masm, left, right, length, scratch2,
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006121 &strings_not_equal, Label::kNear);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006122
6123 // Characters are equal.
6124 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6125 __ ret(0);
6126}
6127
6128
ricow@chromium.org65fae842010-08-25 15:26:24 +00006129void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
6130 Register left,
6131 Register right,
6132 Register scratch1,
6133 Register scratch2,
6134 Register scratch3) {
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006135 Counters* counters = masm->isolate()->counters();
6136 __ IncrementCounter(counters->string_compare_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006137
6138 // Find minimum length.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006139 Label left_shorter;
ricow@chromium.org65fae842010-08-25 15:26:24 +00006140 __ mov(scratch1, FieldOperand(left, String::kLengthOffset));
6141 __ mov(scratch3, scratch1);
6142 __ sub(scratch3, FieldOperand(right, String::kLengthOffset));
6143
6144 Register length_delta = scratch3;
6145
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006146 __ j(less_equal, &left_shorter, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006147 // Right string is shorter. Change scratch1 to be length of right string.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006148 __ sub(scratch1, length_delta);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006149 __ bind(&left_shorter);
6150
6151 Register min_length = scratch1;
6152
6153 // If either length is zero, just compare lengths.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006154 Label compare_lengths;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006155 __ test(min_length, min_length);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006156 __ j(zero, &compare_lengths, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006157
lrn@chromium.org1c092762011-05-09 09:42:16 +00006158 // Compare characters.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006159 Label result_not_equal;
lrn@chromium.org1c092762011-05-09 09:42:16 +00006160 GenerateAsciiCharsCompareLoop(masm, left, right, min_length, scratch2,
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006161 &result_not_equal, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006162
6163 // Compare lengths - strings up to min-length are equal.
6164 __ bind(&compare_lengths);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006165 __ test(length_delta, length_delta);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006166 __ j(not_zero, &result_not_equal, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006167
6168 // Result is EQUAL.
6169 STATIC_ASSERT(EQUAL == 0);
6170 STATIC_ASSERT(kSmiTag == 0);
6171 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6172 __ ret(0);
6173
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006174 Label result_greater;
ricow@chromium.org65fae842010-08-25 15:26:24 +00006175 __ bind(&result_not_equal);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006176 __ j(greater, &result_greater, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006177
6178 // Result is LESS.
6179 __ Set(eax, Immediate(Smi::FromInt(LESS)));
6180 __ ret(0);
6181
6182 // Result is GREATER.
6183 __ bind(&result_greater);
6184 __ Set(eax, Immediate(Smi::FromInt(GREATER)));
6185 __ ret(0);
6186}
6187
6188
lrn@chromium.org1c092762011-05-09 09:42:16 +00006189void StringCompareStub::GenerateAsciiCharsCompareLoop(
6190 MacroAssembler* masm,
6191 Register left,
6192 Register right,
6193 Register length,
6194 Register scratch,
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006195 Label* chars_not_equal,
6196 Label::Distance chars_not_equal_near) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00006197 // Change index to run from -length to -1 by adding length to string
6198 // start. This means that loop ends when index reaches zero, which
6199 // doesn't need an additional compare.
6200 __ SmiUntag(length);
6201 __ lea(left,
6202 FieldOperand(left, length, times_1, SeqAsciiString::kHeaderSize));
6203 __ lea(right,
6204 FieldOperand(right, length, times_1, SeqAsciiString::kHeaderSize));
6205 __ neg(length);
6206 Register index = length; // index = -length;
6207
6208 // Compare loop.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006209 Label loop;
lrn@chromium.org1c092762011-05-09 09:42:16 +00006210 __ bind(&loop);
6211 __ mov_b(scratch, Operand(left, index, times_1, 0));
6212 __ cmpb(scratch, Operand(right, index, times_1, 0));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006213 __ j(not_equal, chars_not_equal, chars_not_equal_near);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006214 __ add(index, Immediate(1));
lrn@chromium.org1c092762011-05-09 09:42:16 +00006215 __ j(not_zero, &loop);
6216}
6217
6218
ricow@chromium.org65fae842010-08-25 15:26:24 +00006219void StringCompareStub::Generate(MacroAssembler* masm) {
6220 Label runtime;
6221
6222 // Stack frame on entry.
6223 // esp[0]: return address
6224 // esp[4]: right string
6225 // esp[8]: left string
6226
6227 __ mov(edx, Operand(esp, 2 * kPointerSize)); // left
6228 __ mov(eax, Operand(esp, 1 * kPointerSize)); // right
6229
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006230 Label not_same;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006231 __ cmp(edx, eax);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006232 __ j(not_equal, &not_same, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006233 STATIC_ASSERT(EQUAL == 0);
6234 STATIC_ASSERT(kSmiTag == 0);
6235 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006236 __ IncrementCounter(masm->isolate()->counters()->string_compare_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006237 __ ret(2 * kPointerSize);
6238
6239 __ bind(&not_same);
6240
6241 // Check that both objects are sequential ascii strings.
6242 __ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx, &runtime);
6243
6244 // Compare flat ascii strings.
6245 // Drop arguments from the stack.
6246 __ pop(ecx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006247 __ add(esp, Immediate(2 * kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006248 __ push(ecx);
6249 GenerateCompareFlatAsciiStrings(masm, edx, eax, ecx, ebx, edi);
6250
6251 // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
6252 // tagged as a small integer.
6253 __ bind(&runtime);
6254 __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
6255}
6256
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006257
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006258void ICCompareStub::GenerateSmis(MacroAssembler* masm) {
6259 ASSERT(state_ == CompareIC::SMIS);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006260 Label miss;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006261 __ mov(ecx, edx);
6262 __ or_(ecx, eax);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006263 __ JumpIfNotSmi(ecx, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006264
6265 if (GetCondition() == equal) {
6266 // For equality we do not care about the sign of the result.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006267 __ sub(eax, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006268 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006269 Label done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006270 __ sub(edx, eax);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006271 __ j(no_overflow, &done, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006272 // Correct sign of result in case of overflow.
6273 __ not_(edx);
6274 __ bind(&done);
6275 __ mov(eax, edx);
6276 }
6277 __ ret(0);
6278
6279 __ bind(&miss);
6280 GenerateMiss(masm);
6281}
6282
6283
6284void ICCompareStub::GenerateHeapNumbers(MacroAssembler* masm) {
6285 ASSERT(state_ == CompareIC::HEAP_NUMBERS);
6286
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006287 Label generic_stub;
6288 Label unordered;
6289 Label miss;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006290 __ mov(ecx, edx);
6291 __ and_(ecx, eax);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006292 __ JumpIfSmi(ecx, &generic_stub, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006293
6294 __ CmpObjectType(eax, HEAP_NUMBER_TYPE, ecx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00006295 __ j(not_equal, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006296 __ CmpObjectType(edx, HEAP_NUMBER_TYPE, ecx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00006297 __ j(not_equal, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006298
6299 // Inlining the double comparison and falling back to the general compare
6300 // stub if NaN is involved or SS2 or CMOV is unsupported.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006301 if (CpuFeatures::IsSupported(SSE2) && CpuFeatures::IsSupported(CMOV)) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006302 CpuFeatures::Scope scope1(SSE2);
6303 CpuFeatures::Scope scope2(CMOV);
6304
6305 // Load left and right operand
6306 __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
6307 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
6308
6309 // Compare operands
6310 __ ucomisd(xmm0, xmm1);
6311
6312 // Don't base result on EFLAGS when a NaN is involved.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00006313 __ j(parity_even, &unordered, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006314
6315 // Return a result of -1, 0, or 1, based on EFLAGS.
6316 // Performing mov, because xor would destroy the flag register.
6317 __ mov(eax, 0); // equal
6318 __ mov(ecx, Immediate(Smi::FromInt(1)));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006319 __ cmov(above, eax, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006320 __ mov(ecx, Immediate(Smi::FromInt(-1)));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006321 __ cmov(below, eax, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006322 __ ret(0);
6323
6324 __ bind(&unordered);
6325 }
6326
6327 CompareStub stub(GetCondition(), strict(), NO_COMPARE_FLAGS);
6328 __ bind(&generic_stub);
6329 __ jmp(stub.GetCode(), RelocInfo::CODE_TARGET);
6330
6331 __ bind(&miss);
6332 GenerateMiss(masm);
6333}
6334
6335
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006336void ICCompareStub::GenerateSymbols(MacroAssembler* masm) {
6337 ASSERT(state_ == CompareIC::SYMBOLS);
6338 ASSERT(GetCondition() == equal);
6339
6340 // Registers containing left and right operands respectively.
6341 Register left = edx;
6342 Register right = eax;
6343 Register tmp1 = ecx;
6344 Register tmp2 = ebx;
6345
6346 // Check that both operands are heap objects.
6347 Label miss;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006348 __ mov(tmp1, left);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006349 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006350 __ and_(tmp1, right);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006351 __ JumpIfSmi(tmp1, &miss, Label::kNear);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006352
6353 // Check that both operands are symbols.
6354 __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset));
6355 __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset));
6356 __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
6357 __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
6358 STATIC_ASSERT(kSymbolTag != 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006359 __ and_(tmp1, tmp2);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006360 __ test(tmp1, Immediate(kIsSymbolMask));
6361 __ j(zero, &miss, Label::kNear);
6362
6363 // Symbols are compared by identity.
6364 Label done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006365 __ cmp(left, right);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006366 // Make sure eax is non-zero. At this point input operands are
6367 // guaranteed to be non-zero.
6368 ASSERT(right.is(eax));
6369 __ j(not_equal, &done, Label::kNear);
6370 STATIC_ASSERT(EQUAL == 0);
6371 STATIC_ASSERT(kSmiTag == 0);
6372 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6373 __ bind(&done);
6374 __ ret(0);
6375
6376 __ bind(&miss);
6377 GenerateMiss(masm);
6378}
6379
6380
lrn@chromium.org1c092762011-05-09 09:42:16 +00006381void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
6382 ASSERT(state_ == CompareIC::STRINGS);
6383 ASSERT(GetCondition() == equal);
6384 Label miss;
6385
6386 // Registers containing left and right operands respectively.
6387 Register left = edx;
6388 Register right = eax;
6389 Register tmp1 = ecx;
6390 Register tmp2 = ebx;
6391 Register tmp3 = edi;
6392
6393 // Check that both operands are heap objects.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006394 __ mov(tmp1, left);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006395 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006396 __ and_(tmp1, right);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006397 __ JumpIfSmi(tmp1, &miss);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006398
6399 // Check that both operands are strings. This leaves the instance
6400 // types loaded in tmp1 and tmp2.
6401 __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset));
6402 __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset));
6403 __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
6404 __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
6405 __ mov(tmp3, tmp1);
6406 STATIC_ASSERT(kNotStringTag != 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006407 __ or_(tmp3, tmp2);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006408 __ test(tmp3, Immediate(kIsNotStringMask));
6409 __ j(not_zero, &miss);
6410
6411 // Fast check for identical strings.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006412 Label not_same;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006413 __ cmp(left, right);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006414 __ j(not_equal, &not_same, Label::kNear);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006415 STATIC_ASSERT(EQUAL == 0);
6416 STATIC_ASSERT(kSmiTag == 0);
6417 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6418 __ ret(0);
6419
6420 // Handle not identical strings.
6421 __ bind(&not_same);
6422
6423 // Check that both strings are symbols. If they are, we're done
6424 // because we already know they are not identical.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006425 Label do_compare;
lrn@chromium.org1c092762011-05-09 09:42:16 +00006426 STATIC_ASSERT(kSymbolTag != 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006427 __ and_(tmp1, tmp2);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006428 __ test(tmp1, Immediate(kIsSymbolMask));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006429 __ j(zero, &do_compare, Label::kNear);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006430 // Make sure eax is non-zero. At this point input operands are
6431 // guaranteed to be non-zero.
6432 ASSERT(right.is(eax));
6433 __ ret(0);
6434
6435 // Check that both strings are sequential ASCII.
6436 Label runtime;
6437 __ bind(&do_compare);
6438 __ JumpIfNotBothSequentialAsciiStrings(left, right, tmp1, tmp2, &runtime);
6439
6440 // Compare flat ASCII strings. Returns when done.
6441 StringCompareStub::GenerateFlatAsciiStringEquals(
6442 masm, left, right, tmp1, tmp2);
6443
6444 // Handle more complex cases in runtime.
6445 __ bind(&runtime);
6446 __ pop(tmp1); // Return address.
6447 __ push(left);
6448 __ push(right);
6449 __ push(tmp1);
6450 __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
6451
6452 __ bind(&miss);
6453 GenerateMiss(masm);
6454}
6455
6456
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006457void ICCompareStub::GenerateObjects(MacroAssembler* masm) {
6458 ASSERT(state_ == CompareIC::OBJECTS);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006459 Label miss;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006460 __ mov(ecx, edx);
6461 __ and_(ecx, eax);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006462 __ JumpIfSmi(ecx, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006463
6464 __ CmpObjectType(eax, JS_OBJECT_TYPE, ecx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00006465 __ j(not_equal, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006466 __ CmpObjectType(edx, JS_OBJECT_TYPE, ecx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00006467 __ j(not_equal, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006468
6469 ASSERT(GetCondition() == equal);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006470 __ sub(eax, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006471 __ ret(0);
6472
6473 __ bind(&miss);
6474 GenerateMiss(masm);
6475}
6476
6477
6478void ICCompareStub::GenerateMiss(MacroAssembler* masm) {
6479 // Save the registers.
6480 __ pop(ecx);
6481 __ push(edx);
6482 __ push(eax);
6483 __ push(ecx);
6484
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006485 {
6486 // Call the runtime system in a fresh internal frame.
6487 ExternalReference miss = ExternalReference(IC_Utility(IC::kCompareIC_Miss),
6488 masm->isolate());
6489 FrameScope scope(masm, StackFrame::INTERNAL);
6490 __ push(edx);
6491 __ push(eax);
6492 __ push(Immediate(Smi::FromInt(op_)));
6493 __ CallExternalReference(miss, 3);
6494 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006495
6496 // Compute the entry point of the rewritten stub.
6497 __ lea(edi, FieldOperand(eax, Code::kHeaderSize));
6498
6499 // Restore registers.
6500 __ pop(ecx);
6501 __ pop(eax);
6502 __ pop(edx);
6503 __ push(ecx);
6504
6505 // Do a tail call to the rewritten stub.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006506 __ jmp(edi);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006507}
6508
6509
lrn@chromium.org1c092762011-05-09 09:42:16 +00006510// Helper function used to check that the dictionary doesn't contain
6511// the property. This function may return false negatives, so miss_label
6512// must always call a backup property check that is complete.
6513// This function is safe to call if the receiver has fast properties.
6514// Name must be a symbol and receiver must be a heap object.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006515void StringDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm,
6516 Label* miss,
6517 Label* done,
6518 Register properties,
6519 Handle<String> name,
6520 Register r0) {
6521 ASSERT(name->IsSymbol());
6522
6523 // If names of slots in range from 1 to kProbes - 1 for the hash value are
6524 // not equal to the name and kProbes-th slot is not used (its name is the
6525 // undefined value), it guarantees the hash table doesn't contain the
6526 // property. It's true even if some slots represent deleted properties
6527 // (their names are the null value).
6528 for (int i = 0; i < kInlinedProbes; i++) {
6529 // Compute the masked index: (hash + i + i * i) & mask.
6530 Register index = r0;
6531 // Capacity is smi 2^n.
6532 __ mov(index, FieldOperand(properties, kCapacityOffset));
6533 __ dec(index);
6534 __ and_(index,
6535 Immediate(Smi::FromInt(name->Hash() +
6536 StringDictionary::GetProbeOffset(i))));
6537
6538 // Scale the index by multiplying by the entry size.
6539 ASSERT(StringDictionary::kEntrySize == 3);
6540 __ lea(index, Operand(index, index, times_2, 0)); // index *= 3.
6541 Register entity_name = r0;
6542 // Having undefined at this place means the name is not contained.
6543 ASSERT_EQ(kSmiTagSize, 1);
6544 __ mov(entity_name, Operand(properties, index, times_half_pointer_size,
6545 kElementsStartOffset - kHeapObjectTag));
6546 __ cmp(entity_name, masm->isolate()->factory()->undefined_value());
6547 __ j(equal, done);
6548
6549 // Stop if found the property.
6550 __ cmp(entity_name, Handle<String>(name));
6551 __ j(equal, miss);
6552
6553 // Check if the entry name is not a symbol.
6554 __ mov(entity_name, FieldOperand(entity_name, HeapObject::kMapOffset));
6555 __ test_b(FieldOperand(entity_name, Map::kInstanceTypeOffset),
6556 kIsSymbolMask);
6557 __ j(zero, miss);
6558 }
6559
6560 StringDictionaryLookupStub stub(properties,
6561 r0,
6562 r0,
6563 StringDictionaryLookupStub::NEGATIVE_LOOKUP);
6564 __ push(Immediate(Handle<Object>(name)));
6565 __ push(Immediate(name->Hash()));
6566 __ CallStub(&stub);
6567 __ test(r0, r0);
6568 __ j(not_zero, miss);
6569 __ jmp(done);
6570}
6571
6572
lrn@chromium.org1c092762011-05-09 09:42:16 +00006573// Probe the string dictionary in the |elements| register. Jump to the
6574// |done| label if a property with the given name is found leaving the
6575// index into the dictionary in |r0|. Jump to the |miss| label
6576// otherwise.
6577void StringDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm,
6578 Label* miss,
6579 Label* done,
6580 Register elements,
6581 Register name,
6582 Register r0,
6583 Register r1) {
erik.corry@gmail.com6e28b562011-10-27 14:20:17 +00006584 ASSERT(!elements.is(r0));
6585 ASSERT(!elements.is(r1));
6586 ASSERT(!name.is(r0));
6587 ASSERT(!name.is(r1));
6588
lrn@chromium.org1c092762011-05-09 09:42:16 +00006589 // Assert that name contains a string.
6590 if (FLAG_debug_code) __ AbortIfNotString(name);
6591
6592 __ mov(r1, FieldOperand(elements, kCapacityOffset));
6593 __ shr(r1, kSmiTagSize); // convert smi to int
6594 __ dec(r1);
6595
6596 // Generate an unrolled loop that performs a few probes before
6597 // giving up. Measurements done on Gmail indicate that 2 probes
6598 // cover ~93% of loads from dictionaries.
6599 for (int i = 0; i < kInlinedProbes; i++) {
6600 // Compute the masked index: (hash + i + i * i) & mask.
6601 __ mov(r0, FieldOperand(name, String::kHashFieldOffset));
6602 __ shr(r0, String::kHashShift);
6603 if (i > 0) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006604 __ add(r0, Immediate(StringDictionary::GetProbeOffset(i)));
lrn@chromium.org1c092762011-05-09 09:42:16 +00006605 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006606 __ and_(r0, r1);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006607
6608 // Scale the index by multiplying by the entry size.
6609 ASSERT(StringDictionary::kEntrySize == 3);
6610 __ lea(r0, Operand(r0, r0, times_2, 0)); // r0 = r0 * 3
6611
6612 // Check if the key is identical to the name.
6613 __ cmp(name, Operand(elements,
6614 r0,
6615 times_4,
6616 kElementsStartOffset - kHeapObjectTag));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00006617 __ j(equal, done);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006618 }
6619
6620 StringDictionaryLookupStub stub(elements,
6621 r1,
6622 r0,
6623 POSITIVE_LOOKUP);
6624 __ push(name);
6625 __ mov(r0, FieldOperand(name, String::kHashFieldOffset));
6626 __ shr(r0, String::kHashShift);
6627 __ push(r0);
6628 __ CallStub(&stub);
6629
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006630 __ test(r1, r1);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006631 __ j(zero, miss);
6632 __ jmp(done);
6633}
6634
6635
6636void StringDictionaryLookupStub::Generate(MacroAssembler* masm) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006637 // This stub overrides SometimesSetsUpAFrame() to return false. That means
6638 // we cannot call anything that could cause a GC from this stub.
lrn@chromium.org1c092762011-05-09 09:42:16 +00006639 // Stack frame on entry:
6640 // esp[0 * kPointerSize]: return address.
6641 // esp[1 * kPointerSize]: key's hash.
6642 // esp[2 * kPointerSize]: key.
6643 // Registers:
6644 // dictionary_: StringDictionary to probe.
6645 // result_: used as scratch.
6646 // index_: will hold an index of entry if lookup is successful.
6647 // might alias with result_.
6648 // Returns:
6649 // result_ is zero if lookup failed, non zero otherwise.
6650
6651 Label in_dictionary, maybe_in_dictionary, not_in_dictionary;
6652
6653 Register scratch = result_;
6654
6655 __ mov(scratch, FieldOperand(dictionary_, kCapacityOffset));
6656 __ dec(scratch);
6657 __ SmiUntag(scratch);
6658 __ push(scratch);
6659
6660 // If names of slots in range from 1 to kProbes - 1 for the hash value are
6661 // not equal to the name and kProbes-th slot is not used (its name is the
6662 // undefined value), it guarantees the hash table doesn't contain the
6663 // property. It's true even if some slots represent deleted properties
6664 // (their names are the null value).
6665 for (int i = kInlinedProbes; i < kTotalProbes; i++) {
6666 // Compute the masked index: (hash + i + i * i) & mask.
6667 __ mov(scratch, Operand(esp, 2 * kPointerSize));
6668 if (i > 0) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006669 __ add(scratch, Immediate(StringDictionary::GetProbeOffset(i)));
lrn@chromium.org1c092762011-05-09 09:42:16 +00006670 }
6671 __ and_(scratch, Operand(esp, 0));
6672
6673 // Scale the index by multiplying by the entry size.
6674 ASSERT(StringDictionary::kEntrySize == 3);
6675 __ lea(index_, Operand(scratch, scratch, times_2, 0)); // index *= 3.
6676
6677 // Having undefined at this place means the name is not contained.
6678 ASSERT_EQ(kSmiTagSize, 1);
6679 __ mov(scratch, Operand(dictionary_,
6680 index_,
6681 times_pointer_size,
6682 kElementsStartOffset - kHeapObjectTag));
6683 __ cmp(scratch, masm->isolate()->factory()->undefined_value());
6684 __ j(equal, &not_in_dictionary);
6685
6686 // Stop if found the property.
6687 __ cmp(scratch, Operand(esp, 3 * kPointerSize));
6688 __ j(equal, &in_dictionary);
6689
6690 if (i != kTotalProbes - 1 && mode_ == NEGATIVE_LOOKUP) {
6691 // If we hit a non symbol key during negative lookup
6692 // we have to bailout as this key might be equal to the
6693 // key we are looking for.
6694
6695 // Check if the entry name is not a symbol.
6696 __ mov(scratch, FieldOperand(scratch, HeapObject::kMapOffset));
6697 __ test_b(FieldOperand(scratch, Map::kInstanceTypeOffset),
6698 kIsSymbolMask);
6699 __ j(zero, &maybe_in_dictionary);
6700 }
6701 }
6702
6703 __ bind(&maybe_in_dictionary);
6704 // If we are doing negative lookup then probing failure should be
6705 // treated as a lookup success. For positive lookup probing failure
6706 // should be treated as lookup failure.
6707 if (mode_ == POSITIVE_LOOKUP) {
6708 __ mov(result_, Immediate(0));
6709 __ Drop(1);
6710 __ ret(2 * kPointerSize);
6711 }
6712
6713 __ bind(&in_dictionary);
6714 __ mov(result_, Immediate(1));
6715 __ Drop(1);
6716 __ ret(2 * kPointerSize);
6717
6718 __ bind(&not_in_dictionary);
6719 __ mov(result_, Immediate(0));
6720 __ Drop(1);
6721 __ ret(2 * kPointerSize);
6722}
6723
6724
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006725struct AheadOfTimeWriteBarrierStubList {
6726 Register object, value, address;
6727 RememberedSetAction action;
6728};
6729
6730
6731struct AheadOfTimeWriteBarrierStubList kAheadOfTime[] = {
6732 // Used in RegExpExecStub.
6733 { ebx, eax, edi, EMIT_REMEMBERED_SET },
6734 // Used in CompileArrayPushCall.
6735 { ebx, ecx, edx, EMIT_REMEMBERED_SET },
6736 { ebx, edi, edx, OMIT_REMEMBERED_SET },
6737 // Used in CompileStoreGlobal and CallFunctionStub.
6738 { ebx, ecx, edx, OMIT_REMEMBERED_SET },
6739 // Used in StoreStubCompiler::CompileStoreField and
6740 // KeyedStoreStubCompiler::CompileStoreField via GenerateStoreField.
6741 { edx, ecx, ebx, EMIT_REMEMBERED_SET },
6742 // GenerateStoreField calls the stub with two different permutations of
6743 // registers. This is the second.
6744 { ebx, ecx, edx, EMIT_REMEMBERED_SET },
6745 // StoreIC::GenerateNormal via GenerateDictionaryStore
6746 { ebx, edi, edx, EMIT_REMEMBERED_SET },
6747 // KeyedStoreIC::GenerateGeneric.
6748 { ebx, edx, ecx, EMIT_REMEMBERED_SET},
6749 // KeyedStoreStubCompiler::GenerateStoreFastElement.
6750 { edi, edx, ecx, EMIT_REMEMBERED_SET},
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006751 // ElementsTransitionGenerator::GenerateSmiOnlyToObject
6752 // and ElementsTransitionGenerator::GenerateSmiOnlyToDouble
6753 // and ElementsTransitionGenerator::GenerateDoubleToObject
6754 { edx, ebx, edi, EMIT_REMEMBERED_SET},
6755 // ElementsTransitionGenerator::GenerateDoubleToObject
6756 { eax, edx, esi, EMIT_REMEMBERED_SET},
6757 { edx, eax, edi, EMIT_REMEMBERED_SET},
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006758 // StoreArrayLiteralElementStub::Generate
6759 { ebx, eax, ecx, EMIT_REMEMBERED_SET},
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006760 // Null termination.
6761 { no_reg, no_reg, no_reg, EMIT_REMEMBERED_SET}
6762};
6763
6764
6765bool RecordWriteStub::IsPregenerated() {
6766 for (AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime;
6767 !entry->object.is(no_reg);
6768 entry++) {
6769 if (object_.is(entry->object) &&
6770 value_.is(entry->value) &&
6771 address_.is(entry->address) &&
6772 remembered_set_action_ == entry->action &&
6773 save_fp_regs_mode_ == kDontSaveFPRegs) {
6774 return true;
6775 }
6776 }
6777 return false;
6778}
6779
6780
6781void StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime() {
6782 StoreBufferOverflowStub stub1(kDontSaveFPRegs);
6783 stub1.GetCode()->set_is_pregenerated(true);
6784
6785 CpuFeatures::TryForceFeatureScope scope(SSE2);
6786 if (CpuFeatures::IsSupported(SSE2)) {
6787 StoreBufferOverflowStub stub2(kSaveFPRegs);
6788 stub2.GetCode()->set_is_pregenerated(true);
6789 }
6790}
6791
6792
6793void RecordWriteStub::GenerateFixedRegStubsAheadOfTime() {
6794 for (AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime;
6795 !entry->object.is(no_reg);
6796 entry++) {
6797 RecordWriteStub stub(entry->object,
6798 entry->value,
6799 entry->address,
6800 entry->action,
6801 kDontSaveFPRegs);
6802 stub.GetCode()->set_is_pregenerated(true);
6803 }
6804}
6805
6806
6807// Takes the input in 3 registers: address_ value_ and object_. A pointer to
6808// the value has just been written into the object, now this stub makes sure
6809// we keep the GC informed. The word in the object where the value has been
6810// written is in the address register.
6811void RecordWriteStub::Generate(MacroAssembler* masm) {
6812 Label skip_to_incremental_noncompacting;
6813 Label skip_to_incremental_compacting;
6814
6815 // The first two instructions are generated with labels so as to get the
6816 // offset fixed up correctly by the bind(Label*) call. We patch it back and
6817 // forth between a compare instructions (a nop in this position) and the
6818 // real branch when we start and stop incremental heap marking.
6819 __ jmp(&skip_to_incremental_noncompacting, Label::kNear);
6820 __ jmp(&skip_to_incremental_compacting, Label::kFar);
6821
6822 if (remembered_set_action_ == EMIT_REMEMBERED_SET) {
6823 __ RememberedSetHelper(object_,
6824 address_,
6825 value_,
6826 save_fp_regs_mode_,
6827 MacroAssembler::kReturnAtEnd);
6828 } else {
6829 __ ret(0);
6830 }
6831
6832 __ bind(&skip_to_incremental_noncompacting);
6833 GenerateIncremental(masm, INCREMENTAL);
6834
6835 __ bind(&skip_to_incremental_compacting);
6836 GenerateIncremental(masm, INCREMENTAL_COMPACTION);
6837
6838 // Initial mode of the stub is expected to be STORE_BUFFER_ONLY.
6839 // Will be checked in IncrementalMarking::ActivateGeneratedStub.
6840 masm->set_byte_at(0, kTwoByteNopInstruction);
6841 masm->set_byte_at(2, kFiveByteNopInstruction);
6842}
6843
6844
6845void RecordWriteStub::GenerateIncremental(MacroAssembler* masm, Mode mode) {
6846 regs_.Save(masm);
6847
6848 if (remembered_set_action_ == EMIT_REMEMBERED_SET) {
6849 Label dont_need_remembered_set;
6850
6851 __ mov(regs_.scratch0(), Operand(regs_.address(), 0));
6852 __ JumpIfNotInNewSpace(regs_.scratch0(), // Value.
6853 regs_.scratch0(),
6854 &dont_need_remembered_set);
6855
6856 __ CheckPageFlag(regs_.object(),
6857 regs_.scratch0(),
6858 1 << MemoryChunk::SCAN_ON_SCAVENGE,
6859 not_zero,
6860 &dont_need_remembered_set);
6861
6862 // First notify the incremental marker if necessary, then update the
6863 // remembered set.
6864 CheckNeedsToInformIncrementalMarker(
6865 masm,
6866 kUpdateRememberedSetOnNoNeedToInformIncrementalMarker,
6867 mode);
6868 InformIncrementalMarker(masm, mode);
6869 regs_.Restore(masm);
6870 __ RememberedSetHelper(object_,
6871 address_,
6872 value_,
6873 save_fp_regs_mode_,
6874 MacroAssembler::kReturnAtEnd);
6875
6876 __ bind(&dont_need_remembered_set);
6877 }
6878
6879 CheckNeedsToInformIncrementalMarker(
6880 masm,
6881 kReturnOnNoNeedToInformIncrementalMarker,
6882 mode);
6883 InformIncrementalMarker(masm, mode);
6884 regs_.Restore(masm);
6885 __ ret(0);
6886}
6887
6888
6889void RecordWriteStub::InformIncrementalMarker(MacroAssembler* masm, Mode mode) {
6890 regs_.SaveCallerSaveRegisters(masm, save_fp_regs_mode_);
6891 int argument_count = 3;
6892 __ PrepareCallCFunction(argument_count, regs_.scratch0());
6893 __ mov(Operand(esp, 0 * kPointerSize), regs_.object());
6894 if (mode == INCREMENTAL_COMPACTION) {
6895 __ mov(Operand(esp, 1 * kPointerSize), regs_.address()); // Slot.
6896 } else {
6897 ASSERT(mode == INCREMENTAL);
6898 __ mov(regs_.scratch0(), Operand(regs_.address(), 0));
6899 __ mov(Operand(esp, 1 * kPointerSize), regs_.scratch0()); // Value.
6900 }
6901 __ mov(Operand(esp, 2 * kPointerSize),
6902 Immediate(ExternalReference::isolate_address()));
6903
6904 AllowExternalCallThatCantCauseGC scope(masm);
6905 if (mode == INCREMENTAL_COMPACTION) {
6906 __ CallCFunction(
6907 ExternalReference::incremental_evacuation_record_write_function(
6908 masm->isolate()),
6909 argument_count);
6910 } else {
6911 ASSERT(mode == INCREMENTAL);
6912 __ CallCFunction(
6913 ExternalReference::incremental_marking_record_write_function(
6914 masm->isolate()),
6915 argument_count);
6916 }
6917 regs_.RestoreCallerSaveRegisters(masm, save_fp_regs_mode_);
6918}
6919
6920
6921void RecordWriteStub::CheckNeedsToInformIncrementalMarker(
6922 MacroAssembler* masm,
6923 OnNoNeedToInformIncrementalMarker on_no_need,
6924 Mode mode) {
6925 Label object_is_black, need_incremental, need_incremental_pop_object;
6926
6927 // Let's look at the color of the object: If it is not black we don't have
6928 // to inform the incremental marker.
6929 __ JumpIfBlack(regs_.object(),
6930 regs_.scratch0(),
6931 regs_.scratch1(),
6932 &object_is_black,
6933 Label::kNear);
6934
6935 regs_.Restore(masm);
6936 if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) {
6937 __ RememberedSetHelper(object_,
6938 address_,
6939 value_,
6940 save_fp_regs_mode_,
6941 MacroAssembler::kReturnAtEnd);
6942 } else {
6943 __ ret(0);
6944 }
6945
6946 __ bind(&object_is_black);
6947
6948 // Get the value from the slot.
6949 __ mov(regs_.scratch0(), Operand(regs_.address(), 0));
6950
6951 if (mode == INCREMENTAL_COMPACTION) {
6952 Label ensure_not_white;
6953
6954 __ CheckPageFlag(regs_.scratch0(), // Contains value.
6955 regs_.scratch1(), // Scratch.
6956 MemoryChunk::kEvacuationCandidateMask,
6957 zero,
6958 &ensure_not_white,
6959 Label::kNear);
6960
6961 __ CheckPageFlag(regs_.object(),
6962 regs_.scratch1(), // Scratch.
6963 MemoryChunk::kSkipEvacuationSlotsRecordingMask,
6964 not_zero,
6965 &ensure_not_white,
6966 Label::kNear);
6967
6968 __ jmp(&need_incremental);
6969
6970 __ bind(&ensure_not_white);
6971 }
6972
6973 // We need an extra register for this, so we push the object register
6974 // temporarily.
6975 __ push(regs_.object());
6976 __ EnsureNotWhite(regs_.scratch0(), // The value.
6977 regs_.scratch1(), // Scratch.
6978 regs_.object(), // Scratch.
6979 &need_incremental_pop_object,
6980 Label::kNear);
6981 __ pop(regs_.object());
6982
6983 regs_.Restore(masm);
6984 if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) {
6985 __ RememberedSetHelper(object_,
6986 address_,
6987 value_,
6988 save_fp_regs_mode_,
6989 MacroAssembler::kReturnAtEnd);
6990 } else {
6991 __ ret(0);
6992 }
6993
6994 __ bind(&need_incremental_pop_object);
6995 __ pop(regs_.object());
6996
6997 __ bind(&need_incremental);
6998
6999 // Fall through when we need to inform the incremental marker.
7000}
7001
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007002
7003void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) {
7004 // ----------- S t a t e -------------
7005 // -- eax : element value to store
7006 // -- ebx : array literal
7007 // -- edi : map of array literal
7008 // -- ecx : element index as smi
7009 // -- edx : array literal index in function
7010 // -- esp[0] : return address
7011 // -----------------------------------
7012
7013 Label element_done;
7014 Label double_elements;
7015 Label smi_element;
7016 Label slow_elements;
7017 Label slow_elements_from_double;
7018 Label fast_elements;
7019
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007020 __ CheckFastElements(edi, &double_elements);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007021
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007022 // FAST_SMI_ONLY_ELEMENTS or FAST_ELEMENTS
7023 __ JumpIfSmi(eax, &smi_element);
7024 __ CheckFastSmiOnlyElements(edi, &fast_elements, Label::kNear);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007025
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007026 // Store into the array literal requires a elements transition. Call into
7027 // the runtime.
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007028
7029 __ bind(&slow_elements);
7030 __ pop(edi); // Pop return address and remember to put back later for tail
7031 // call.
7032 __ push(ebx);
7033 __ push(ecx);
7034 __ push(eax);
7035 __ mov(ebx, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
7036 __ push(FieldOperand(ebx, JSFunction::kLiteralsOffset));
7037 __ push(edx);
7038 __ push(edi); // Return return address so that tail call returns to right
7039 // place.
7040 __ TailCallRuntime(Runtime::kStoreArrayLiteralElement, 5, 1);
7041
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007042 __ bind(&slow_elements_from_double);
7043 __ pop(edx);
7044 __ jmp(&slow_elements);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007045
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007046 // Array literal has ElementsKind of FAST_ELEMENTS and value is an object.
7047 __ bind(&fast_elements);
7048 __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset));
7049 __ lea(ecx, FieldOperand(ebx, ecx, times_half_pointer_size,
7050 FixedArrayBase::kHeaderSize));
7051 __ mov(Operand(ecx, 0), eax);
7052 // Update the write barrier for the array store.
7053 __ RecordWrite(ebx, ecx, eax,
7054 kDontSaveFPRegs,
7055 EMIT_REMEMBERED_SET,
7056 OMIT_SMI_CHECK);
7057 __ ret(0);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007058
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007059 // Array literal has ElementsKind of FAST_SMI_ONLY_ELEMENTS or
7060 // FAST_ELEMENTS, and value is Smi.
7061 __ bind(&smi_element);
7062 __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset));
7063 __ mov(FieldOperand(ebx, ecx, times_half_pointer_size,
7064 FixedArrayBase::kHeaderSize), eax);
7065 __ ret(0);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007066
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007067 // Array literal has ElementsKind of FAST_DOUBLE_ELEMENTS.
7068 __ bind(&double_elements);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007069
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007070 __ push(edx);
7071 __ mov(edx, FieldOperand(ebx, JSObject::kElementsOffset));
7072 __ StoreNumberToDoubleElements(eax,
7073 edx,
7074 ecx,
7075 edi,
7076 xmm0,
7077 &slow_elements_from_double,
7078 false);
7079 __ pop(edx);
7080 __ ret(0);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007081}
7082
ricow@chromium.org65fae842010-08-25 15:26:24 +00007083#undef __
7084
7085} } // namespace v8::internal
7086
7087#endif // V8_TARGET_ARCH_IA32