blob: 8cb4725d56f29bdec00df054d9de341e8b88b600 [file] [log] [blame]
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00001// Copyright 2012 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"
danno@chromium.orgf005df62013-04-30 16:36:45 +000033#include "builtins-decls.h"
vegorov@chromium.org7304bca2011-05-16 12:14:13 +000034#include "code-stubs.h"
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000035#include "isolate.h"
vegorov@chromium.org7304bca2011-05-16 12:14:13 +000036#include "jsregexp.h"
ricow@chromium.org65fae842010-08-25 15:26:24 +000037#include "regexp-macro-assembler.h"
danno@chromium.org94b0d6f2013-02-04 13:33:20 +000038#include "runtime.h"
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +000039#include "stub-cache.h"
erikcorry0ad885c2011-11-21 13:51:57 +000040#include "codegen.h"
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +000041#include "runtime.h"
ricow@chromium.org65fae842010-08-25 15:26:24 +000042
43namespace v8 {
44namespace internal {
45
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000046
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000047void FastCloneShallowArrayStub::InitializeInterfaceDescriptor(
48 Isolate* isolate,
49 CodeStubInterfaceDescriptor* descriptor) {
50 static Register registers[] = { eax, ebx, ecx };
51 descriptor->register_param_count_ = 3;
52 descriptor->register_params_ = registers;
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000053 descriptor->deoptimization_handler_ =
54 Runtime::FunctionForId(Runtime::kCreateArrayLiteralShallow)->entry;
55}
56
57
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +000058void FastCloneShallowObjectStub::InitializeInterfaceDescriptor(
59 Isolate* isolate,
60 CodeStubInterfaceDescriptor* descriptor) {
61 static Register registers[] = { eax, ebx, ecx, edx };
62 descriptor->register_param_count_ = 4;
63 descriptor->register_params_ = registers;
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +000064 descriptor->deoptimization_handler_ =
65 Runtime::FunctionForId(Runtime::kCreateObjectLiteralShallow)->entry;
66}
67
68
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000069void KeyedLoadFastElementStub::InitializeInterfaceDescriptor(
70 Isolate* isolate,
71 CodeStubInterfaceDescriptor* descriptor) {
72 static Register registers[] = { edx, ecx };
73 descriptor->register_param_count_ = 2;
74 descriptor->register_params_ = registers;
75 descriptor->deoptimization_handler_ =
mstarzinger@chromium.orge3b8d0f2013-02-01 09:06:41 +000076 FUNCTION_ADDR(KeyedLoadIC_MissFromStubFailure);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000077}
78
79
ulan@chromium.org57ff8812013-05-10 08:16:55 +000080void LoadFieldStub::InitializeInterfaceDescriptor(
81 Isolate* isolate,
82 CodeStubInterfaceDescriptor* descriptor) {
83 static Register registers[] = { edx };
84 descriptor->register_param_count_ = 1;
85 descriptor->register_params_ = registers;
ulan@chromium.org57ff8812013-05-10 08:16:55 +000086 descriptor->deoptimization_handler_ = NULL;
87}
88
89
90void KeyedLoadFieldStub::InitializeInterfaceDescriptor(
91 Isolate* isolate,
92 CodeStubInterfaceDescriptor* descriptor) {
93 static Register registers[] = { edx };
94 descriptor->register_param_count_ = 1;
95 descriptor->register_params_ = registers;
ulan@chromium.org57ff8812013-05-10 08:16:55 +000096 descriptor->deoptimization_handler_ = NULL;
97}
98
99
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +0000100void KeyedStoreFastElementStub::InitializeInterfaceDescriptor(
101 Isolate* isolate,
102 CodeStubInterfaceDescriptor* descriptor) {
103 static Register registers[] = { edx, ecx, eax };
104 descriptor->register_param_count_ = 3;
105 descriptor->register_params_ = registers;
106 descriptor->deoptimization_handler_ =
107 FUNCTION_ADDR(KeyedStoreIC_MissFromStubFailure);
108}
109
110
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000111void TransitionElementsKindStub::InitializeInterfaceDescriptor(
112 Isolate* isolate,
113 CodeStubInterfaceDescriptor* descriptor) {
114 static Register registers[] = { eax, ebx };
115 descriptor->register_param_count_ = 2;
116 descriptor->register_params_ = registers;
117 descriptor->deoptimization_handler_ =
118 Runtime::FunctionForId(Runtime::kTransitionElementsKind)->entry;
119}
120
121
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000122static void InitializeArrayConstructorDescriptor(
123 Isolate* isolate,
124 CodeStubInterfaceDescriptor* descriptor,
125 int constant_stack_parameter_count) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000126 // register state
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000127 // eax -- number of arguments
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000128 // edi -- function
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000129 // ebx -- type info cell with elements kind
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000130 static Register registers[] = { edi, ebx };
131 descriptor->register_param_count_ = 2;
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000132
133 if (constant_stack_parameter_count != 0) {
134 // stack param count needs (constructor pointer, and single argument)
135 descriptor->stack_parameter_count_ = &eax;
136 }
137 descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000138 descriptor->register_params_ = registers;
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +0000139 descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000140 descriptor->deoptimization_handler_ =
141 FUNCTION_ADDR(ArrayConstructor_StubFailure);
142}
143
144
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +0000145static void InitializeInternalArrayConstructorDescriptor(
146 Isolate* isolate,
147 CodeStubInterfaceDescriptor* descriptor,
148 int constant_stack_parameter_count) {
149 // register state
150 // eax -- number of arguments
151 // edi -- constructor function
152 static Register registers[] = { edi };
153 descriptor->register_param_count_ = 1;
154
155 if (constant_stack_parameter_count != 0) {
156 // stack param count needs (constructor pointer, and single argument)
157 descriptor->stack_parameter_count_ = &eax;
158 }
159 descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
160 descriptor->register_params_ = registers;
161 descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
162 descriptor->deoptimization_handler_ =
163 FUNCTION_ADDR(InternalArrayConstructor_StubFailure);
164}
165
166
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000167void ArrayNoArgumentConstructorStub::InitializeInterfaceDescriptor(
168 Isolate* isolate,
169 CodeStubInterfaceDescriptor* descriptor) {
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000170 InitializeArrayConstructorDescriptor(isolate, descriptor, 0);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000171}
172
173
174void ArraySingleArgumentConstructorStub::InitializeInterfaceDescriptor(
175 Isolate* isolate,
176 CodeStubInterfaceDescriptor* descriptor) {
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000177 InitializeArrayConstructorDescriptor(isolate, descriptor, 1);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000178}
179
180
181void ArrayNArgumentsConstructorStub::InitializeInterfaceDescriptor(
182 Isolate* isolate,
183 CodeStubInterfaceDescriptor* descriptor) {
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000184 InitializeArrayConstructorDescriptor(isolate, descriptor, -1);
185}
186
187
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +0000188void InternalArrayNoArgumentConstructorStub::InitializeInterfaceDescriptor(
189 Isolate* isolate,
190 CodeStubInterfaceDescriptor* descriptor) {
191 InitializeInternalArrayConstructorDescriptor(isolate, descriptor, 0);
192}
193
194
195void InternalArraySingleArgumentConstructorStub::InitializeInterfaceDescriptor(
196 Isolate* isolate,
197 CodeStubInterfaceDescriptor* descriptor) {
198 InitializeInternalArrayConstructorDescriptor(isolate, descriptor, 1);
199}
200
201
202void InternalArrayNArgumentsConstructorStub::InitializeInterfaceDescriptor(
203 Isolate* isolate,
204 CodeStubInterfaceDescriptor* descriptor) {
205 InitializeInternalArrayConstructorDescriptor(isolate, descriptor, -1);
206}
207
208
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000209void CompareNilICStub::InitializeInterfaceDescriptor(
210 Isolate* isolate,
211 CodeStubInterfaceDescriptor* descriptor) {
212 static Register registers[] = { eax };
213 descriptor->register_param_count_ = 1;
214 descriptor->register_params_ = registers;
215 descriptor->deoptimization_handler_ =
216 FUNCTION_ADDR(CompareNilIC_Miss);
svenpanne@chromium.orga53e8e02013-05-24 12:35:50 +0000217 descriptor->SetMissHandler(
218 ExternalReference(IC_Utility(IC::kCompareNilIC_Miss), isolate));
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000219}
220
rossberg@chromium.orgb99c7542013-05-31 11:40:45 +0000221void ToBooleanStub::InitializeInterfaceDescriptor(
222 Isolate* isolate,
223 CodeStubInterfaceDescriptor* descriptor) {
224 static Register registers[] = { eax };
225 descriptor->register_param_count_ = 1;
226 descriptor->register_params_ = registers;
227 descriptor->deoptimization_handler_ =
228 FUNCTION_ADDR(ToBooleanIC_Miss);
229 descriptor->SetMissHandler(
230 ExternalReference(IC_Utility(IC::kToBooleanIC_Miss), isolate));
231}
232
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000233
ricow@chromium.org65fae842010-08-25 15:26:24 +0000234#define __ ACCESS_MASM(masm)
whesse@chromium.org7a392b32011-01-31 11:30:36 +0000235
ulan@chromium.org77ca49a2013-04-22 09:43:56 +0000236
237void HydrogenCodeStub::GenerateLightweightMiss(MacroAssembler* masm) {
238 // Update the static counter each time a new code stub is generated.
239 Isolate* isolate = masm->isolate();
240 isolate->counters()->code_stubs()->Increment();
241
242 CodeStubInterfaceDescriptor* descriptor = GetInterfaceDescriptor(isolate);
243 int param_count = descriptor->register_param_count_;
244 {
245 // Call the runtime system in a fresh internal frame.
246 FrameScope scope(masm, StackFrame::INTERNAL);
247 ASSERT(descriptor->register_param_count_ == 0 ||
248 eax.is(descriptor->register_params_[param_count - 1]));
249 // Push arguments
250 for (int i = 0; i < param_count; ++i) {
251 __ push(descriptor->register_params_[i]);
252 }
svenpanne@chromium.orga53e8e02013-05-24 12:35:50 +0000253 ExternalReference miss = descriptor->miss_handler();
ulan@chromium.org77ca49a2013-04-22 09:43:56 +0000254 __ CallExternalReference(miss, descriptor->register_param_count_);
255 }
256
257 __ ret(0);
258}
259
260
whesse@chromium.org7a392b32011-01-31 11:30:36 +0000261void ToNumberStub::Generate(MacroAssembler* masm) {
262 // The ToNumber stub takes one argument in eax.
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000263 Label check_heap_number, call_builtin;
whesse@chromium.org7b260152011-06-20 15:33:18 +0000264 __ JumpIfNotSmi(eax, &check_heap_number, Label::kNear);
whesse@chromium.org7a392b32011-01-31 11:30:36 +0000265 __ ret(0);
266
267 __ bind(&check_heap_number);
268 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000269 Factory* factory = masm->isolate()->factory();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000270 __ cmp(ebx, Immediate(factory->heap_number_map()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000271 __ j(not_equal, &call_builtin, Label::kNear);
whesse@chromium.org7a392b32011-01-31 11:30:36 +0000272 __ ret(0);
273
274 __ bind(&call_builtin);
275 __ pop(ecx); // Pop return address.
276 __ push(eax);
277 __ push(ecx); // Push return address.
278 __ InvokeBuiltin(Builtins::TO_NUMBER, JUMP_FUNCTION);
279}
280
281
ricow@chromium.org65fae842010-08-25 15:26:24 +0000282void FastNewClosureStub::Generate(MacroAssembler* masm) {
283 // Create a new closure from the given function info in new
284 // space. Set the context to the current context in esi.
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000285 Counters* counters = masm->isolate()->counters();
286
ricow@chromium.org65fae842010-08-25 15:26:24 +0000287 Label gc;
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000288 __ Allocate(JSFunction::kSize, eax, ebx, ecx, &gc, TAG_OBJECT);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000289
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000290 __ IncrementCounter(counters->fast_new_closure_total(), 1);
291
ricow@chromium.org65fae842010-08-25 15:26:24 +0000292 // Get the function info from the stack.
293 __ mov(edx, Operand(esp, 1 * kPointerSize));
294
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000295 int map_index = Context::FunctionMapIndex(language_mode_, is_generator_);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000296
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000297 // Compute the function map in the current native context and set that
ricow@chromium.org65fae842010-08-25 15:26:24 +0000298 // as the map of the allocated object.
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000299 __ mov(ecx, Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
300 __ mov(ecx, FieldOperand(ecx, GlobalObject::kNativeContextOffset));
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000301 __ mov(ebx, Operand(ecx, Context::SlotOffset(map_index)));
302 __ mov(FieldOperand(eax, JSObject::kMapOffset), ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000303
304 // Initialize the rest of the function. We don't have to update the
305 // write barrier because the allocated object is in new space.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000306 Factory* factory = masm->isolate()->factory();
307 __ mov(ebx, Immediate(factory->empty_fixed_array()));
ricow@chromium.org65fae842010-08-25 15:26:24 +0000308 __ mov(FieldOperand(eax, JSObject::kPropertiesOffset), ebx);
309 __ mov(FieldOperand(eax, JSObject::kElementsOffset), ebx);
310 __ mov(FieldOperand(eax, JSFunction::kPrototypeOrInitialMapOffset),
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000311 Immediate(factory->the_hole_value()));
ricow@chromium.org65fae842010-08-25 15:26:24 +0000312 __ mov(FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset), edx);
313 __ mov(FieldOperand(eax, JSFunction::kContextOffset), esi);
314 __ mov(FieldOperand(eax, JSFunction::kLiteralsOffset), ebx);
315
316 // Initialize the code pointer in the function to be the one
317 // found in the shared function info object.
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000318 // But first check if there is an optimized version for our context.
319 Label check_optimized;
320 Label install_unoptimized;
321 if (FLAG_cache_optimized_code) {
322 __ mov(ebx, FieldOperand(edx, SharedFunctionInfo::kOptimizedCodeMapOffset));
323 __ test(ebx, ebx);
324 __ j(not_zero, &check_optimized, Label::kNear);
325 }
326 __ bind(&install_unoptimized);
327 __ mov(FieldOperand(eax, JSFunction::kNextFunctionLinkOffset),
328 Immediate(factory->undefined_value()));
ricow@chromium.org65fae842010-08-25 15:26:24 +0000329 __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
330 __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
331 __ mov(FieldOperand(eax, JSFunction::kCodeEntryOffset), edx);
332
333 // Return and remove the on-stack parameter.
334 __ ret(1 * kPointerSize);
335
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000336 __ bind(&check_optimized);
337
338 __ IncrementCounter(counters->fast_new_closure_try_optimized(), 1);
339
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000340 // ecx holds native context, ebx points to fixed array of 3-element entries
341 // (native context, optimized code, literals).
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000342 // Map must never be empty, so check the first elements.
343 Label install_optimized;
344 // Speculatively move code object into edx.
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +0000345 __ mov(edx, FieldOperand(ebx, SharedFunctionInfo::kFirstCodeSlot));
346 __ cmp(ecx, FieldOperand(ebx, SharedFunctionInfo::kFirstContextSlot));
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000347 __ j(equal, &install_optimized);
348
349 // Iterate through the rest of map backwards. edx holds an index as a Smi.
350 Label loop;
351 Label restore;
352 __ mov(edx, FieldOperand(ebx, FixedArray::kLengthOffset));
353 __ bind(&loop);
354 // Do not double check first entry.
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +0000355 __ cmp(edx, Immediate(Smi::FromInt(SharedFunctionInfo::kSecondEntryIndex)));
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000356 __ j(equal, &restore);
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +0000357 __ sub(edx, Immediate(Smi::FromInt(SharedFunctionInfo::kEntryLength)));
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000358 __ cmp(ecx, CodeGenerator::FixedArrayElementOperand(ebx, edx, 0));
359 __ j(not_equal, &loop, Label::kNear);
360 // Hit: fetch the optimized code.
361 __ mov(edx, CodeGenerator::FixedArrayElementOperand(ebx, edx, 1));
362
363 __ bind(&install_optimized);
364 __ IncrementCounter(counters->fast_new_closure_install_optimized(), 1);
365
366 // TODO(fschneider): Idea: store proper code pointers in the optimized code
367 // map and either unmangle them on marking or do nothing as the whole map is
368 // discarded on major GC anyway.
369 __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
370 __ mov(FieldOperand(eax, JSFunction::kCodeEntryOffset), edx);
371
372 // Now link a function into a list of optimized functions.
373 __ mov(edx, ContextOperand(ecx, Context::OPTIMIZED_FUNCTIONS_LIST));
374
375 __ mov(FieldOperand(eax, JSFunction::kNextFunctionLinkOffset), edx);
376 // No need for write barrier as JSFunction (eax) is in the new space.
377
378 __ mov(ContextOperand(ecx, Context::OPTIMIZED_FUNCTIONS_LIST), eax);
379 // Store JSFunction (eax) into edx before issuing write barrier as
380 // it clobbers all the registers passed.
381 __ mov(edx, eax);
382 __ RecordWriteContextSlot(
383 ecx,
384 Context::SlotOffset(Context::OPTIMIZED_FUNCTIONS_LIST),
385 edx,
386 ebx,
387 kDontSaveFPRegs);
388
389 // Return and remove the on-stack parameter.
390 __ ret(1 * kPointerSize);
391
392 __ bind(&restore);
393 // Restore SharedFunctionInfo into edx.
394 __ mov(edx, Operand(esp, 1 * kPointerSize));
395 __ jmp(&install_unoptimized);
396
ricow@chromium.org65fae842010-08-25 15:26:24 +0000397 // Create a new closure through the slower runtime call.
398 __ bind(&gc);
399 __ pop(ecx); // Temporarily remove return address.
400 __ pop(edx);
401 __ push(esi);
402 __ push(edx);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000403 __ push(Immediate(factory->false_value()));
ricow@chromium.org65fae842010-08-25 15:26:24 +0000404 __ push(ecx); // Restore return address.
vegorov@chromium.org21b5e952010-11-23 10:24:40 +0000405 __ TailCallRuntime(Runtime::kNewClosure, 3, 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000406}
407
408
409void FastNewContextStub::Generate(MacroAssembler* masm) {
410 // Try to allocate the context in new space.
411 Label gc;
ager@chromium.org0ee099b2011-01-25 14:06:47 +0000412 int length = slots_ + Context::MIN_CONTEXT_SLOTS;
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000413 __ Allocate((length * kPointerSize) + FixedArray::kHeaderSize,
414 eax, ebx, ecx, &gc, TAG_OBJECT);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000415
416 // Get the function from the stack.
417 __ mov(ecx, Operand(esp, 1 * kPointerSize));
418
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000419 // Set up the object header.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000420 Factory* factory = masm->isolate()->factory();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000421 __ mov(FieldOperand(eax, HeapObject::kMapOffset),
422 factory->function_context_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +0000423 __ mov(FieldOperand(eax, Context::kLengthOffset),
ager@chromium.org0ee099b2011-01-25 14:06:47 +0000424 Immediate(Smi::FromInt(length)));
ricow@chromium.org65fae842010-08-25 15:26:24 +0000425
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000426 // Set up the fixed slots.
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000427 __ Set(ebx, Immediate(0)); // Set to NULL.
ricow@chromium.org65fae842010-08-25 15:26:24 +0000428 __ mov(Operand(eax, Context::SlotOffset(Context::CLOSURE_INDEX)), ecx);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000429 __ mov(Operand(eax, Context::SlotOffset(Context::PREVIOUS_INDEX)), esi);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000430 __ mov(Operand(eax, Context::SlotOffset(Context::EXTENSION_INDEX)), ebx);
431
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000432 // Copy the global object from the previous context.
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000433 __ mov(ebx, Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
434 __ mov(Operand(eax, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)), ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000435
436 // Initialize the rest of the slots to undefined.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000437 __ mov(ebx, factory->undefined_value());
ager@chromium.org0ee099b2011-01-25 14:06:47 +0000438 for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
ricow@chromium.org65fae842010-08-25 15:26:24 +0000439 __ mov(Operand(eax, Context::SlotOffset(i)), ebx);
440 }
441
442 // Return and remove the on-stack parameter.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000443 __ mov(esi, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000444 __ ret(1 * kPointerSize);
445
446 // Need to collect. Call into runtime system.
447 __ bind(&gc);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000448 __ TailCallRuntime(Runtime::kNewFunctionContext, 1, 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000449}
450
451
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000452void FastNewBlockContextStub::Generate(MacroAssembler* masm) {
453 // Stack layout on entry:
454 //
455 // [esp + (1 * kPointerSize)]: function
456 // [esp + (2 * kPointerSize)]: serialized scope info
457
458 // Try to allocate the context in new space.
459 Label gc;
460 int length = slots_ + Context::MIN_CONTEXT_SLOTS;
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000461 __ Allocate(FixedArray::SizeFor(length), eax, ebx, ecx, &gc, TAG_OBJECT);
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000462
463 // Get the function or sentinel from the stack.
464 __ mov(ecx, Operand(esp, 1 * kPointerSize));
465
466 // Get the serialized scope info from the stack.
467 __ mov(ebx, Operand(esp, 2 * kPointerSize));
468
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000469 // Set up the object header.
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000470 Factory* factory = masm->isolate()->factory();
471 __ mov(FieldOperand(eax, HeapObject::kMapOffset),
472 factory->block_context_map());
473 __ mov(FieldOperand(eax, Context::kLengthOffset),
474 Immediate(Smi::FromInt(length)));
475
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000476 // If this block context is nested in the native context we get a smi
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000477 // sentinel instead of a function. The block context should get the
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000478 // canonical empty function of the native context as its closure which
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000479 // we still have to look up.
480 Label after_sentinel;
481 __ JumpIfNotSmi(ecx, &after_sentinel, Label::kNear);
482 if (FLAG_debug_code) {
483 const char* message = "Expected 0 as a Smi sentinel";
484 __ cmp(ecx, 0);
485 __ Assert(equal, message);
486 }
487 __ mov(ecx, GlobalObjectOperand());
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000488 __ mov(ecx, FieldOperand(ecx, GlobalObject::kNativeContextOffset));
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000489 __ mov(ecx, ContextOperand(ecx, Context::CLOSURE_INDEX));
490 __ bind(&after_sentinel);
491
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000492 // Set up the fixed slots.
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000493 __ mov(ContextOperand(eax, Context::CLOSURE_INDEX), ecx);
494 __ mov(ContextOperand(eax, Context::PREVIOUS_INDEX), esi);
495 __ mov(ContextOperand(eax, Context::EXTENSION_INDEX), ebx);
496
497 // Copy the global object from the previous context.
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000498 __ mov(ebx, ContextOperand(esi, Context::GLOBAL_OBJECT_INDEX));
499 __ mov(ContextOperand(eax, Context::GLOBAL_OBJECT_INDEX), ebx);
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000500
501 // Initialize the rest of the slots to the hole value.
502 if (slots_ == 1) {
503 __ mov(ContextOperand(eax, Context::MIN_CONTEXT_SLOTS),
504 factory->the_hole_value());
505 } else {
506 __ mov(ebx, factory->the_hole_value());
507 for (int i = 0; i < slots_; i++) {
508 __ mov(ContextOperand(eax, i + Context::MIN_CONTEXT_SLOTS), ebx);
509 }
510 }
511
512 // Return and remove the on-stack parameters.
513 __ mov(esi, eax);
514 __ ret(2 * kPointerSize);
515
516 // Need to collect. Call into runtime system.
517 __ bind(&gc);
518 __ TailCallRuntime(Runtime::kPushBlockContext, 2, 1);
519}
520
521
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000522void StoreBufferOverflowStub::Generate(MacroAssembler* masm) {
523 // We don't allow a GC during a store buffer overflow so there is no need to
524 // store the registers in any particular way, but we do have to store and
525 // restore them.
526 __ pushad();
527 if (save_doubles_ == kSaveFPRegs) {
ulan@chromium.org750145a2013-03-07 15:14:13 +0000528 CpuFeatureScope scope(masm, SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000529 __ sub(esp, Immediate(kDoubleSize * XMMRegister::kNumRegisters));
530 for (int i = 0; i < XMMRegister::kNumRegisters; i++) {
531 XMMRegister reg = XMMRegister::from_code(i);
532 __ movdbl(Operand(esp, i * kDoubleSize), reg);
533 }
534 }
535 const int argument_count = 1;
536
537 AllowExternalCallThatCantCauseGC scope(masm);
538 __ PrepareCallCFunction(argument_count, ecx);
539 __ mov(Operand(esp, 0 * kPointerSize),
ulan@chromium.org32d7dba2013-04-24 10:59:06 +0000540 Immediate(ExternalReference::isolate_address(masm->isolate())));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000541 __ CallCFunction(
542 ExternalReference::store_buffer_overflow_function(masm->isolate()),
543 argument_count);
544 if (save_doubles_ == kSaveFPRegs) {
ulan@chromium.org750145a2013-03-07 15:14:13 +0000545 CpuFeatureScope scope(masm, SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000546 for (int i = 0; i < XMMRegister::kNumRegisters; i++) {
547 XMMRegister reg = XMMRegister::from_code(i);
548 __ movdbl(reg, Operand(esp, i * kDoubleSize));
549 }
550 __ add(esp, Immediate(kDoubleSize * XMMRegister::kNumRegisters));
551 }
552 __ popad();
553 __ ret(0);
554}
555
556
ricow@chromium.org65fae842010-08-25 15:26:24 +0000557class FloatingPointHelper : public AllStatic {
558 public:
ricow@chromium.org65fae842010-08-25 15:26:24 +0000559 enum ArgLocation {
560 ARGS_ON_STACK,
561 ARGS_IN_REGISTERS
562 };
563
564 // Code pattern for loading a floating point value. Input value must
565 // be either a smi or a heap number object (fp value). Requirements:
566 // operand in register number. Returns operand as floating point number
567 // on FPU stack.
568 static void LoadFloatOperand(MacroAssembler* masm, Register number);
569
570 // Code pattern for loading floating point values. Input values must
571 // be either smi or heap number objects (fp values). Requirements:
572 // operand_1 on TOS+1 or in edx, operand_2 on TOS+2 or in eax.
573 // Returns operands as floating point numbers on FPU stack.
574 static void LoadFloatOperands(MacroAssembler* masm,
575 Register scratch,
576 ArgLocation arg_location = ARGS_ON_STACK);
577
578 // Similar to LoadFloatOperand but assumes that both operands are smis.
579 // Expects operands in edx, eax.
580 static void LoadFloatSmis(MacroAssembler* masm, Register scratch);
581
582 // Test if operands are smi or number objects (fp). Requirements:
583 // operand_1 in eax, operand_2 in edx; falls through on float
584 // operands, jumps to the non_float label otherwise.
585 static void CheckFloatOperands(MacroAssembler* masm,
586 Label* non_float,
587 Register scratch);
588
589 // Takes the operands in edx and eax and loads them as integers in eax
590 // and ecx.
ricow@chromium.org65fae842010-08-25 15:26:24 +0000591 static void LoadUnknownsAsIntegers(MacroAssembler* masm,
592 bool use_sse3,
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +0000593 BinaryOpIC::TypeInfo left_type,
594 BinaryOpIC::TypeInfo right_type,
ricow@chromium.org65fae842010-08-25 15:26:24 +0000595 Label* operand_conversion_failure);
596
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000597 // 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
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +0000612 // Checks that |operand| has an int32 value. If |int32_result| is different
613 // from |scratch|, it will contain that int32 value.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000614 static void CheckSSE2OperandIsInt32(MacroAssembler* masm,
615 Label* non_int32,
616 XMMRegister operand,
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +0000617 Register int32_result,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000618 Register scratch,
619 XMMRegister xmm_scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000620};
621
622
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000623// Get the integer part of a heap number. Surprisingly, all this bit twiddling
624// is faster than using the built-in instructions on floating point registers.
625// Trashes edi and ebx. Dest is ecx. Source cannot be ecx or one of the
626// trashed registers.
627static void IntegerConvert(MacroAssembler* masm,
628 Register source,
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000629 bool use_sse3,
630 Label* conversion_failure) {
631 ASSERT(!source.is(ecx) && !source.is(edi) && !source.is(ebx));
632 Label done, right_exponent, normal_exponent;
633 Register scratch = ebx;
634 Register scratch2 = edi;
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000635 // Get exponent word.
636 __ mov(scratch, FieldOperand(source, HeapNumber::kExponentOffset));
637 // Get exponent alone in scratch2.
638 __ mov(scratch2, scratch);
639 __ and_(scratch2, HeapNumber::kExponentMask);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000640 __ shr(scratch2, HeapNumber::kExponentShift);
641 __ sub(scratch2, Immediate(HeapNumber::kExponentBias));
642 // Load ecx with zero. We use this either for the final shift or
643 // for the answer.
644 __ xor_(ecx, ecx);
645 // If the exponent is above 83, the number contains no significant
646 // bits in the range 0..2^31, so the result is zero.
647 static const uint32_t kResultIsZeroExponent = 83;
648 __ cmp(scratch2, Immediate(kResultIsZeroExponent));
649 __ j(above, &done);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000650 if (use_sse3) {
ulan@chromium.org750145a2013-03-07 15:14:13 +0000651 CpuFeatureScope scope(masm, SSE3);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000652 // Check whether the exponent is too big for a 64 bit signed integer.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000653 static const uint32_t kTooBigExponent = 63;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000654 __ cmp(scratch2, Immediate(kTooBigExponent));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000655 __ j(greater_equal, conversion_failure);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000656 // Load x87 register with heap number.
657 __ fld_d(FieldOperand(source, HeapNumber::kValueOffset));
658 // Reserve space for 64 bit answer.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000659 __ sub(esp, Immediate(sizeof(uint64_t))); // Nolint.
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000660 // Do conversion, which cannot fail because we checked the exponent.
661 __ fisttp_d(Operand(esp, 0));
662 __ mov(ecx, Operand(esp, 0)); // Load low word of answer into ecx.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000663 __ add(esp, Immediate(sizeof(uint64_t))); // Nolint.
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000664 } else {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000665 // Check whether the exponent matches a 32 bit signed int that cannot be
666 // represented by a Smi. A non-smi 32 bit integer is 1.xxx * 2^30 so the
667 // exponent is 30 (biased). This is the exponent that we are fastest at and
668 // also the highest exponent we can handle here.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000669 const uint32_t non_smi_exponent = 30;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000670 __ cmp(scratch2, Immediate(non_smi_exponent));
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000671 // If we have a match of the int32-but-not-Smi exponent then skip some
672 // logic.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000673 __ j(equal, &right_exponent, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000674 // If the exponent is higher than that then go to slow case. This catches
675 // numbers that don't fit in a signed int32, infinities and NaNs.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000676 __ j(less, &normal_exponent, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000677
678 {
679 // Handle a big exponent. The only reason we have this code is that the
680 // >>> operator has a tendency to generate numbers with an exponent of 31.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000681 const uint32_t big_non_smi_exponent = 31;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000682 __ cmp(scratch2, Immediate(big_non_smi_exponent));
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000683 __ j(not_equal, conversion_failure);
684 // We have the big exponent, typically from >>>. This means the number is
685 // in the range 2^31 to 2^32 - 1. Get the top bits of the mantissa.
686 __ mov(scratch2, scratch);
687 __ and_(scratch2, HeapNumber::kMantissaMask);
688 // Put back the implicit 1.
689 __ or_(scratch2, 1 << HeapNumber::kExponentShift);
690 // Shift up the mantissa bits to take up the space the exponent used to
691 // take. We just orred in the implicit bit so that took care of one and
692 // we want to use the full unsigned range so we subtract 1 bit from the
693 // shift distance.
694 const int big_shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 1;
695 __ shl(scratch2, big_shift_distance);
696 // Get the second half of the double.
697 __ mov(ecx, FieldOperand(source, HeapNumber::kMantissaOffset));
698 // Shift down 21 bits to get the most significant 11 bits or the low
699 // mantissa word.
700 __ shr(ecx, 32 - big_shift_distance);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000701 __ or_(ecx, scratch2);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000702 // We have the answer in ecx, but we may need to negate it.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000703 __ test(scratch, scratch);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000704 __ j(positive, &done, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000705 __ neg(ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000706 __ jmp(&done, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000707 }
708
709 __ bind(&normal_exponent);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000710 // Exponent word in scratch, exponent in scratch2. Zero in ecx.
711 // We know that 0 <= exponent < 30.
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000712 __ mov(ecx, Immediate(30));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000713 __ sub(ecx, scratch2);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000714
715 __ bind(&right_exponent);
716 // Here ecx is the shift, scratch is the exponent word.
717 // Get the top bits of the mantissa.
718 __ and_(scratch, HeapNumber::kMantissaMask);
719 // Put back the implicit 1.
720 __ or_(scratch, 1 << HeapNumber::kExponentShift);
721 // Shift up the mantissa bits to take up the space the exponent used to
722 // take. We have kExponentShift + 1 significant bits int he low end of the
723 // word. Shift them to the top bits.
724 const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
725 __ shl(scratch, shift_distance);
726 // Get the second half of the double. For some exponents we don't
727 // actually need this because the bits get shifted out again, but
728 // it's probably slower to test than just to do it.
729 __ mov(scratch2, FieldOperand(source, HeapNumber::kMantissaOffset));
730 // Shift down 22 bits to get the most significant 10 bits or the low
731 // mantissa word.
732 __ shr(scratch2, 32 - shift_distance);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000733 __ or_(scratch2, scratch);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000734 // Move down according to the exponent.
735 __ shr_cl(scratch2);
736 // Now the unsigned answer is in scratch2. We need to move it to ecx and
737 // we may need to fix the sign.
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000738 Label negative;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000739 __ xor_(ecx, ecx);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000740 __ cmp(ecx, FieldOperand(source, HeapNumber::kExponentOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000741 __ j(greater, &negative, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000742 __ mov(ecx, scratch2);
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000743 __ jmp(&done, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000744 __ bind(&negative);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000745 __ sub(ecx, scratch2);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000746 }
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000747 __ bind(&done);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000748}
749
750
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +0000751// Uses SSE2 to convert the heap number in |source| to an integer. Jumps to
752// |conversion_failure| if the heap number did not contain an int32 value.
753// Result is in ecx. Trashes ebx, xmm0, and xmm1.
754static void ConvertHeapNumberToInt32(MacroAssembler* masm,
755 Register source,
756 Label* conversion_failure) {
757 __ movdbl(xmm0, FieldOperand(source, HeapNumber::kValueOffset));
758 FloatingPointHelper::CheckSSE2OperandIsInt32(
759 masm, conversion_failure, xmm0, ecx, ebx, xmm1);
760}
761
762
whesse@chromium.org030d38e2011-07-13 13:23:34 +0000763void UnaryOpStub::PrintName(StringStream* stream) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000764 const char* op_name = Token::Name(op_);
765 const char* overwrite_name = NULL; // Make g++ happy.
766 switch (mode_) {
767 case UNARY_NO_OVERWRITE: overwrite_name = "Alloc"; break;
768 case UNARY_OVERWRITE: overwrite_name = "Overwrite"; break;
769 }
whesse@chromium.org030d38e2011-07-13 13:23:34 +0000770 stream->Add("UnaryOpStub_%s_%s_%s",
771 op_name,
772 overwrite_name,
773 UnaryOpIC::GetName(operand_type_));
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000774}
775
776
777// TODO(svenpanne): Use virtual functions instead of switch.
danno@chromium.org40cb8782011-05-25 07:58:50 +0000778void UnaryOpStub::Generate(MacroAssembler* masm) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000779 switch (operand_type_) {
danno@chromium.org40cb8782011-05-25 07:58:50 +0000780 case UnaryOpIC::UNINITIALIZED:
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000781 GenerateTypeTransition(masm);
782 break;
danno@chromium.org40cb8782011-05-25 07:58:50 +0000783 case UnaryOpIC::SMI:
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000784 GenerateSmiStub(masm);
785 break;
hpayer@chromium.org8432c912013-02-28 15:55:26 +0000786 case UnaryOpIC::NUMBER:
787 GenerateNumberStub(masm);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000788 break;
danno@chromium.org40cb8782011-05-25 07:58:50 +0000789 case UnaryOpIC::GENERIC:
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000790 GenerateGenericStub(masm);
791 break;
792 }
793}
794
795
danno@chromium.org40cb8782011-05-25 07:58:50 +0000796void UnaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000797 __ pop(ecx); // Save return address.
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000798
799 __ push(eax); // the operand
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000800 __ push(Immediate(Smi::FromInt(op_)));
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000801 __ push(Immediate(Smi::FromInt(mode_)));
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000802 __ push(Immediate(Smi::FromInt(operand_type_)));
803
804 __ push(ecx); // Push return address.
805
806 // Patch the caller to an appropriate specialized stub and return the
807 // operation result to the caller of the stub.
808 __ TailCallExternalReference(
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000809 ExternalReference(IC_Utility(IC::kUnaryOp_Patch), masm->isolate()), 4, 1);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000810}
811
812
813// TODO(svenpanne): Use virtual functions instead of switch.
danno@chromium.org40cb8782011-05-25 07:58:50 +0000814void UnaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000815 switch (op_) {
816 case Token::SUB:
817 GenerateSmiStubSub(masm);
818 break;
819 case Token::BIT_NOT:
820 GenerateSmiStubBitNot(masm);
821 break;
822 default:
823 UNREACHABLE();
824 }
825}
826
827
danno@chromium.org40cb8782011-05-25 07:58:50 +0000828void UnaryOpStub::GenerateSmiStubSub(MacroAssembler* masm) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000829 Label non_smi, undo, slow;
830 GenerateSmiCodeSub(masm, &non_smi, &undo, &slow,
831 Label::kNear, Label::kNear, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000832 __ bind(&undo);
833 GenerateSmiCodeUndo(masm);
834 __ bind(&non_smi);
835 __ bind(&slow);
836 GenerateTypeTransition(masm);
837}
838
839
danno@chromium.org40cb8782011-05-25 07:58:50 +0000840void UnaryOpStub::GenerateSmiStubBitNot(MacroAssembler* masm) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000841 Label non_smi;
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000842 GenerateSmiCodeBitNot(masm, &non_smi);
843 __ bind(&non_smi);
844 GenerateTypeTransition(masm);
845}
846
847
danno@chromium.org40cb8782011-05-25 07:58:50 +0000848void UnaryOpStub::GenerateSmiCodeSub(MacroAssembler* masm,
849 Label* non_smi,
850 Label* undo,
851 Label* slow,
852 Label::Distance non_smi_near,
853 Label::Distance undo_near,
854 Label::Distance slow_near) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000855 // Check whether the value is a smi.
whesse@chromium.org7b260152011-06-20 15:33:18 +0000856 __ JumpIfNotSmi(eax, non_smi, non_smi_near);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000857
858 // We can't handle -0 with smis, so use a type transition for that case.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000859 __ test(eax, eax);
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000860 __ j(zero, slow, slow_near);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000861
862 // Try optimistic subtraction '0 - value', saving operand in eax for undo.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000863 __ mov(edx, eax);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000864 __ Set(eax, Immediate(0));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000865 __ sub(eax, edx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000866 __ j(overflow, undo, undo_near);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000867 __ ret(0);
868}
869
870
danno@chromium.org40cb8782011-05-25 07:58:50 +0000871void UnaryOpStub::GenerateSmiCodeBitNot(
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000872 MacroAssembler* masm,
873 Label* non_smi,
874 Label::Distance non_smi_near) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000875 // Check whether the value is a smi.
whesse@chromium.org7b260152011-06-20 15:33:18 +0000876 __ JumpIfNotSmi(eax, non_smi, non_smi_near);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000877
878 // Flip bits and revert inverted smi-tag.
879 __ not_(eax);
880 __ and_(eax, ~kSmiTagMask);
881 __ ret(0);
882}
883
884
danno@chromium.org40cb8782011-05-25 07:58:50 +0000885void UnaryOpStub::GenerateSmiCodeUndo(MacroAssembler* masm) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000886 __ mov(eax, edx);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000887}
888
889
890// TODO(svenpanne): Use virtual functions instead of switch.
hpayer@chromium.org8432c912013-02-28 15:55:26 +0000891void UnaryOpStub::GenerateNumberStub(MacroAssembler* masm) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000892 switch (op_) {
893 case Token::SUB:
hpayer@chromium.org8432c912013-02-28 15:55:26 +0000894 GenerateNumberStubSub(masm);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000895 break;
896 case Token::BIT_NOT:
hpayer@chromium.org8432c912013-02-28 15:55:26 +0000897 GenerateNumberStubBitNot(masm);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000898 break;
899 default:
900 UNREACHABLE();
901 }
902}
903
904
hpayer@chromium.org8432c912013-02-28 15:55:26 +0000905void UnaryOpStub::GenerateNumberStubSub(MacroAssembler* masm) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000906 Label non_smi, undo, slow, call_builtin;
907 GenerateSmiCodeSub(masm, &non_smi, &undo, &call_builtin, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000908 __ bind(&non_smi);
909 GenerateHeapNumberCodeSub(masm, &slow);
910 __ bind(&undo);
911 GenerateSmiCodeUndo(masm);
912 __ bind(&slow);
913 GenerateTypeTransition(masm);
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000914 __ bind(&call_builtin);
915 GenerateGenericCodeFallback(masm);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000916}
917
918
hpayer@chromium.org8432c912013-02-28 15:55:26 +0000919void UnaryOpStub::GenerateNumberStubBitNot(
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000920 MacroAssembler* masm) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000921 Label non_smi, slow;
922 GenerateSmiCodeBitNot(masm, &non_smi, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000923 __ bind(&non_smi);
924 GenerateHeapNumberCodeBitNot(masm, &slow);
925 __ bind(&slow);
926 GenerateTypeTransition(masm);
927}
928
929
danno@chromium.org40cb8782011-05-25 07:58:50 +0000930void UnaryOpStub::GenerateHeapNumberCodeSub(MacroAssembler* masm,
931 Label* slow) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000932 __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
933 __ cmp(edx, masm->isolate()->factory()->heap_number_map());
934 __ j(not_equal, slow);
935
936 if (mode_ == UNARY_OVERWRITE) {
937 __ xor_(FieldOperand(eax, HeapNumber::kExponentOffset),
938 Immediate(HeapNumber::kSignMask)); // Flip sign.
939 } else {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000940 __ mov(edx, eax);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000941 // edx: operand
942
943 Label slow_allocate_heapnumber, heapnumber_allocated;
944 __ AllocateHeapNumber(eax, ebx, ecx, &slow_allocate_heapnumber);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000945 __ jmp(&heapnumber_allocated, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000946
947 __ bind(&slow_allocate_heapnumber);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000948 {
949 FrameScope scope(masm, StackFrame::INTERNAL);
950 __ push(edx);
951 __ CallRuntime(Runtime::kNumberAlloc, 0);
952 __ pop(edx);
953 }
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000954
955 __ bind(&heapnumber_allocated);
956 // eax: allocated 'empty' number
957 __ mov(ecx, FieldOperand(edx, HeapNumber::kExponentOffset));
958 __ xor_(ecx, HeapNumber::kSignMask); // Flip sign.
959 __ mov(FieldOperand(eax, HeapNumber::kExponentOffset), ecx);
960 __ mov(ecx, FieldOperand(edx, HeapNumber::kMantissaOffset));
961 __ mov(FieldOperand(eax, HeapNumber::kMantissaOffset), ecx);
962 }
963 __ ret(0);
964}
965
966
danno@chromium.org40cb8782011-05-25 07:58:50 +0000967void UnaryOpStub::GenerateHeapNumberCodeBitNot(MacroAssembler* masm,
968 Label* slow) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000969 __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
970 __ cmp(edx, masm->isolate()->factory()->heap_number_map());
971 __ j(not_equal, slow);
972
973 // Convert the heap number in eax to an untagged integer in ecx.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000974 IntegerConvert(masm, eax, CpuFeatures::IsSupported(SSE3), slow);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000975
976 // Do the bitwise operation and check if the result fits in a smi.
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000977 Label try_float;
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000978 __ not_(ecx);
979 __ cmp(ecx, 0xc0000000);
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000980 __ j(sign, &try_float, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000981
982 // Tag the result as a smi and we're done.
983 STATIC_ASSERT(kSmiTagSize == 1);
984 __ lea(eax, Operand(ecx, times_2, kSmiTag));
985 __ ret(0);
986
987 // Try to store the result in a heap number.
988 __ bind(&try_float);
989 if (mode_ == UNARY_NO_OVERWRITE) {
990 Label slow_allocate_heapnumber, heapnumber_allocated;
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +0000991 __ mov(ebx, eax);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000992 __ AllocateHeapNumber(eax, edx, edi, &slow_allocate_heapnumber);
993 __ jmp(&heapnumber_allocated);
994
995 __ bind(&slow_allocate_heapnumber);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000996 {
997 FrameScope scope(masm, StackFrame::INTERNAL);
998 // Push the original HeapNumber on the stack. The integer value can't
999 // be stored since it's untagged and not in the smi range (so we can't
1000 // smi-tag it). We'll recalculate the value after the GC instead.
1001 __ push(ebx);
1002 __ CallRuntime(Runtime::kNumberAlloc, 0);
1003 // New HeapNumber is in eax.
1004 __ pop(edx);
1005 }
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00001006 // IntegerConvert uses ebx and edi as scratch registers.
1007 // This conversion won't go slow-case.
1008 IntegerConvert(masm, edx, CpuFeatures::IsSupported(SSE3), slow);
1009 __ not_(ecx);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00001010
1011 __ bind(&heapnumber_allocated);
1012 }
1013 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001014 CpuFeatureScope use_sse2(masm, SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001015 __ cvtsi2sd(xmm0, ecx);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00001016 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1017 } else {
1018 __ push(ecx);
1019 __ fild_s(Operand(esp, 0));
1020 __ pop(ecx);
1021 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1022 }
1023 __ ret(0);
1024}
1025
1026
1027// TODO(svenpanne): Use virtual functions instead of switch.
danno@chromium.org40cb8782011-05-25 07:58:50 +00001028void UnaryOpStub::GenerateGenericStub(MacroAssembler* masm) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00001029 switch (op_) {
1030 case Token::SUB:
1031 GenerateGenericStubSub(masm);
1032 break;
1033 case Token::BIT_NOT:
1034 GenerateGenericStubBitNot(masm);
1035 break;
1036 default:
1037 UNREACHABLE();
1038 }
1039}
1040
1041
danno@chromium.org40cb8782011-05-25 07:58:50 +00001042void UnaryOpStub::GenerateGenericStubSub(MacroAssembler* masm) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001043 Label non_smi, undo, slow;
1044 GenerateSmiCodeSub(masm, &non_smi, &undo, &slow, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00001045 __ bind(&non_smi);
1046 GenerateHeapNumberCodeSub(masm, &slow);
1047 __ bind(&undo);
1048 GenerateSmiCodeUndo(masm);
1049 __ bind(&slow);
1050 GenerateGenericCodeFallback(masm);
1051}
1052
1053
danno@chromium.org40cb8782011-05-25 07:58:50 +00001054void UnaryOpStub::GenerateGenericStubBitNot(MacroAssembler* masm) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001055 Label non_smi, slow;
1056 GenerateSmiCodeBitNot(masm, &non_smi, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00001057 __ bind(&non_smi);
1058 GenerateHeapNumberCodeBitNot(masm, &slow);
1059 __ bind(&slow);
1060 GenerateGenericCodeFallback(masm);
1061}
1062
1063
danno@chromium.org40cb8782011-05-25 07:58:50 +00001064void UnaryOpStub::GenerateGenericCodeFallback(MacroAssembler* masm) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00001065 // Handle the slow case by jumping to the corresponding JavaScript builtin.
1066 __ pop(ecx); // pop return address.
1067 __ push(eax);
1068 __ push(ecx); // push return address
1069 switch (op_) {
1070 case Token::SUB:
1071 __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_FUNCTION);
1072 break;
1073 case Token::BIT_NOT:
1074 __ InvokeBuiltin(Builtins::BIT_NOT, JUMP_FUNCTION);
1075 break;
1076 default:
1077 UNREACHABLE();
1078 }
1079}
1080
1081
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001082void BinaryOpStub::Initialize() {
1083 platform_specific_bit_ = CpuFeatures::IsSupported(SSE3);
1084}
1085
1086
danno@chromium.org40cb8782011-05-25 07:58:50 +00001087void BinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001088 __ pop(ecx); // Save return address.
1089 __ push(edx);
1090 __ push(eax);
1091 // Left and right arguments are now on top.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001092 __ push(Immediate(Smi::FromInt(MinorKey())));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001093
1094 __ push(ecx); // Push return address.
1095
1096 // Patch the caller to an appropriate specialized stub and return the
1097 // operation result to the caller of the stub.
1098 __ TailCallExternalReference(
danno@chromium.org40cb8782011-05-25 07:58:50 +00001099 ExternalReference(IC_Utility(IC::kBinaryOp_Patch),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001100 masm->isolate()),
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001101 3,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001102 1);
1103}
1104
1105
1106// Prepare for a type transition runtime call when the args are already on
1107// the stack, under the return address.
danno@chromium.org40cb8782011-05-25 07:58:50 +00001108void BinaryOpStub::GenerateTypeTransitionWithSavedArgs(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001109 __ pop(ecx); // Save return address.
1110 // Left and right arguments are already on top of the stack.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001111 __ push(Immediate(Smi::FromInt(MinorKey())));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001112
1113 __ push(ecx); // Push return address.
1114
1115 // Patch the caller to an appropriate specialized stub and return the
1116 // operation result to the caller of the stub.
1117 __ TailCallExternalReference(
danno@chromium.org40cb8782011-05-25 07:58:50 +00001118 ExternalReference(IC_Utility(IC::kBinaryOp_Patch),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001119 masm->isolate()),
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001120 3,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001121 1);
1122}
1123
1124
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001125static void BinaryOpStub_GenerateRegisterArgsPop(MacroAssembler* masm) {
1126 __ pop(ecx);
1127 __ pop(eax);
1128 __ pop(edx);
1129 __ push(ecx);
1130}
1131
1132
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001133static void BinaryOpStub_GenerateSmiCode(
danno@chromium.org40cb8782011-05-25 07:58:50 +00001134 MacroAssembler* masm,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001135 Label* slow,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001136 BinaryOpStub::SmiCodeGenerateHeapNumberResults allow_heapnumber_results,
1137 Token::Value op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001138 // 1. Move arguments into edx, eax except for DIV and MOD, which need the
1139 // dividend in eax and edx free for the division. Use eax, ebx for those.
1140 Comment load_comment(masm, "-- Load arguments");
1141 Register left = edx;
1142 Register right = eax;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001143 if (op == Token::DIV || op == Token::MOD) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001144 left = eax;
1145 right = ebx;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001146 __ mov(ebx, eax);
1147 __ mov(eax, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001148 }
1149
1150
1151 // 2. Prepare the smi check of both operands by oring them together.
1152 Comment smi_check_comment(masm, "-- Smi check arguments");
1153 Label not_smis;
1154 Register combined = ecx;
1155 ASSERT(!left.is(combined) && !right.is(combined));
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001156 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001157 case Token::BIT_OR:
1158 // Perform the operation into eax and smi check the result. Preserve
1159 // eax in case the result is not a smi.
1160 ASSERT(!left.is(ecx) && !right.is(ecx));
1161 __ mov(ecx, right);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001162 __ or_(right, left); // Bitwise or is commutative.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001163 combined = right;
1164 break;
1165
1166 case Token::BIT_XOR:
1167 case Token::BIT_AND:
1168 case Token::ADD:
1169 case Token::SUB:
1170 case Token::MUL:
1171 case Token::DIV:
1172 case Token::MOD:
1173 __ mov(combined, right);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001174 __ or_(combined, left);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001175 break;
1176
1177 case Token::SHL:
1178 case Token::SAR:
1179 case Token::SHR:
1180 // Move the right operand into ecx for the shift operation, use eax
1181 // for the smi check register.
1182 ASSERT(!left.is(ecx) && !right.is(ecx));
1183 __ mov(ecx, right);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001184 __ or_(right, left);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001185 combined = right;
1186 break;
1187
1188 default:
1189 break;
1190 }
1191
1192 // 3. Perform the smi check of the operands.
1193 STATIC_ASSERT(kSmiTag == 0); // Adjust zero check if not the case.
whesse@chromium.org7b260152011-06-20 15:33:18 +00001194 __ JumpIfNotSmi(combined, &not_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001195
1196 // 4. Operands are both smis, perform the operation leaving the result in
1197 // eax and check the result if necessary.
1198 Comment perform_smi(masm, "-- Perform smi operation");
1199 Label use_fp_on_smis;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001200 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001201 case Token::BIT_OR:
1202 // Nothing to do.
1203 break;
1204
1205 case Token::BIT_XOR:
1206 ASSERT(right.is(eax));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001207 __ xor_(right, left); // Bitwise xor is commutative.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001208 break;
1209
1210 case Token::BIT_AND:
1211 ASSERT(right.is(eax));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001212 __ and_(right, left); // Bitwise and is commutative.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001213 break;
1214
1215 case Token::SHL:
1216 // Remove tags from operands (but keep sign).
1217 __ SmiUntag(left);
1218 __ SmiUntag(ecx);
1219 // Perform the operation.
1220 __ shl_cl(left);
1221 // Check that the *signed* result fits in a smi.
1222 __ cmp(left, 0xc0000000);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00001223 __ j(sign, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001224 // Tag the result and store it in register eax.
1225 __ SmiTag(left);
1226 __ mov(eax, left);
1227 break;
1228
1229 case Token::SAR:
1230 // Remove tags from operands (but keep sign).
1231 __ SmiUntag(left);
1232 __ SmiUntag(ecx);
1233 // Perform the operation.
1234 __ sar_cl(left);
1235 // Tag the result and store it in register eax.
1236 __ SmiTag(left);
1237 __ mov(eax, left);
1238 break;
1239
1240 case Token::SHR:
1241 // Remove tags from operands (but keep sign).
1242 __ SmiUntag(left);
1243 __ SmiUntag(ecx);
1244 // Perform the operation.
1245 __ shr_cl(left);
1246 // Check that the *unsigned* result fits in a smi.
1247 // Neither of the two high-order bits can be set:
1248 // - 0x80000000: high bit would be lost when smi tagging.
1249 // - 0x40000000: this number would convert to negative when
1250 // Smi tagging these two cases can only happen with shifts
1251 // by 0 or 1 when handed a valid smi.
1252 __ test(left, Immediate(0xc0000000));
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001253 __ j(not_zero, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001254 // Tag the result and store it in register eax.
1255 __ SmiTag(left);
1256 __ mov(eax, left);
1257 break;
1258
1259 case Token::ADD:
1260 ASSERT(right.is(eax));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001261 __ add(right, left); // Addition is commutative.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00001262 __ j(overflow, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001263 break;
1264
1265 case Token::SUB:
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001266 __ sub(left, right);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00001267 __ j(overflow, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001268 __ mov(eax, left);
1269 break;
1270
1271 case Token::MUL:
1272 // If the smi tag is 0 we can just leave the tag on one operand.
1273 STATIC_ASSERT(kSmiTag == 0); // Adjust code below if not the case.
1274 // We can't revert the multiplication if the result is not a smi
1275 // so save the right operand.
1276 __ mov(ebx, right);
1277 // Remove tag from one of the operands (but keep sign).
1278 __ SmiUntag(right);
1279 // Do multiplication.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001280 __ imul(right, left); // Multiplication is commutative.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00001281 __ j(overflow, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001282 // Check for negative zero result. Use combined = left | right.
1283 __ NegativeZeroTest(right, combined, &use_fp_on_smis);
1284 break;
1285
1286 case Token::DIV:
1287 // We can't revert the division if the result is not a smi so
1288 // save the left operand.
1289 __ mov(edi, left);
1290 // Check for 0 divisor.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001291 __ test(right, right);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00001292 __ j(zero, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001293 // Sign extend left into edx:eax.
1294 ASSERT(left.is(eax));
1295 __ cdq();
1296 // Divide edx:eax by right.
1297 __ idiv(right);
1298 // Check for the corner case of dividing the most negative smi by
1299 // -1. We cannot use the overflow flag, since it is not set by idiv
1300 // instruction.
1301 STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
1302 __ cmp(eax, 0x40000000);
1303 __ j(equal, &use_fp_on_smis);
1304 // Check for negative zero result. Use combined = left | right.
1305 __ NegativeZeroTest(eax, combined, &use_fp_on_smis);
1306 // Check that the remainder is zero.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001307 __ test(edx, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001308 __ j(not_zero, &use_fp_on_smis);
1309 // Tag the result and store it in register eax.
1310 __ SmiTag(eax);
1311 break;
1312
1313 case Token::MOD:
1314 // Check for 0 divisor.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001315 __ test(right, right);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00001316 __ j(zero, &not_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001317
1318 // Sign extend left into edx:eax.
1319 ASSERT(left.is(eax));
1320 __ cdq();
1321 // Divide edx:eax by right.
1322 __ idiv(right);
1323 // Check for negative zero result. Use combined = left | right.
1324 __ NegativeZeroTest(edx, combined, slow);
1325 // Move remainder to register eax.
1326 __ mov(eax, edx);
1327 break;
1328
1329 default:
1330 UNREACHABLE();
1331 }
1332
1333 // 5. Emit return of result in eax. Some operations have registers pushed.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001334 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001335 case Token::ADD:
1336 case Token::SUB:
1337 case Token::MUL:
1338 case Token::DIV:
1339 __ ret(0);
1340 break;
1341 case Token::MOD:
1342 case Token::BIT_OR:
1343 case Token::BIT_AND:
1344 case Token::BIT_XOR:
1345 case Token::SAR:
1346 case Token::SHL:
1347 case Token::SHR:
1348 __ ret(2 * kPointerSize);
1349 break;
1350 default:
1351 UNREACHABLE();
1352 }
1353
1354 // 6. For some operations emit inline code to perform floating point
1355 // operations on known smis (e.g., if the result of the operation
1356 // overflowed the smi range).
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001357 if (allow_heapnumber_results == BinaryOpStub::NO_HEAPNUMBER_RESULTS) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001358 __ bind(&use_fp_on_smis);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001359 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001360 // Undo the effects of some operations, and some register moves.
1361 case Token::SHL:
1362 // The arguments are saved on the stack, and only used from there.
1363 break;
1364 case Token::ADD:
1365 // Revert right = right + left.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001366 __ sub(right, left);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001367 break;
1368 case Token::SUB:
1369 // Revert left = left - right.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001370 __ add(left, right);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001371 break;
1372 case Token::MUL:
1373 // Right was clobbered but a copy is in ebx.
1374 __ mov(right, ebx);
1375 break;
1376 case Token::DIV:
1377 // Left was clobbered but a copy is in edi. Right is in ebx for
1378 // division. They should be in eax, ebx for jump to not_smi.
1379 __ mov(eax, edi);
1380 break;
1381 default:
1382 // No other operators jump to use_fp_on_smis.
1383 break;
1384 }
1385 __ jmp(&not_smis);
1386 } else {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001387 ASSERT(allow_heapnumber_results == BinaryOpStub::ALLOW_HEAPNUMBER_RESULTS);
1388 switch (op) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001389 case Token::SHL:
1390 case Token::SHR: {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001391 Comment perform_float(masm, "-- Perform float operation on smis");
1392 __ bind(&use_fp_on_smis);
1393 // Result we want is in left == edx, so we can put the allocated heap
1394 // number in eax.
1395 __ AllocateHeapNumber(eax, ecx, ebx, slow);
1396 // Store the result in the HeapNumber and return.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001397 // It's OK to overwrite the arguments on the stack because we
1398 // are about to return.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001399 if (op == Token::SHR) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001400 __ mov(Operand(esp, 1 * kPointerSize), left);
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001401 __ mov(Operand(esp, 2 * kPointerSize), Immediate(0));
1402 __ fild_d(Operand(esp, 1 * kPointerSize));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001403 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001404 } else {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001405 ASSERT_EQ(Token::SHL, op);
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001406 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001407 CpuFeatureScope use_sse2(masm, SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001408 __ cvtsi2sd(xmm0, left);
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001409 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1410 } else {
1411 __ mov(Operand(esp, 1 * kPointerSize), left);
1412 __ fild_s(Operand(esp, 1 * kPointerSize));
1413 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1414 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001415 }
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001416 __ ret(2 * kPointerSize);
1417 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001418 }
1419
1420 case Token::ADD:
1421 case Token::SUB:
1422 case Token::MUL:
1423 case Token::DIV: {
1424 Comment perform_float(masm, "-- Perform float operation on smis");
1425 __ bind(&use_fp_on_smis);
1426 // Restore arguments to edx, eax.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001427 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001428 case Token::ADD:
1429 // Revert right = right + left.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001430 __ sub(right, left);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001431 break;
1432 case Token::SUB:
1433 // Revert left = left - right.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001434 __ add(left, right);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001435 break;
1436 case Token::MUL:
1437 // Right was clobbered but a copy is in ebx.
1438 __ mov(right, ebx);
1439 break;
1440 case Token::DIV:
1441 // Left was clobbered but a copy is in edi. Right is in ebx for
1442 // division.
1443 __ mov(edx, edi);
1444 __ mov(eax, right);
1445 break;
1446 default: UNREACHABLE();
1447 break;
1448 }
1449 __ AllocateHeapNumber(ecx, ebx, no_reg, slow);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001450 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001451 CpuFeatureScope use_sse2(masm, SSE2);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001452 FloatingPointHelper::LoadSSE2Smis(masm, ebx);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001453 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001454 case Token::ADD: __ addsd(xmm0, xmm1); break;
1455 case Token::SUB: __ subsd(xmm0, xmm1); break;
1456 case Token::MUL: __ mulsd(xmm0, xmm1); break;
1457 case Token::DIV: __ divsd(xmm0, xmm1); break;
1458 default: UNREACHABLE();
1459 }
1460 __ movdbl(FieldOperand(ecx, HeapNumber::kValueOffset), xmm0);
1461 } else { // SSE2 not available, use FPU.
1462 FloatingPointHelper::LoadFloatSmis(masm, ebx);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001463 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001464 case Token::ADD: __ faddp(1); break;
1465 case Token::SUB: __ fsubp(1); break;
1466 case Token::MUL: __ fmulp(1); break;
1467 case Token::DIV: __ fdivp(1); break;
1468 default: UNREACHABLE();
1469 }
1470 __ fstp_d(FieldOperand(ecx, HeapNumber::kValueOffset));
1471 }
1472 __ mov(eax, ecx);
1473 __ ret(0);
1474 break;
1475 }
1476
1477 default:
1478 break;
1479 }
1480 }
1481
1482 // 7. Non-smi operands, fall out to the non-smi code with the operands in
1483 // edx and eax.
1484 Comment done_comment(masm, "-- Enter non-smi code");
1485 __ bind(&not_smis);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001486 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001487 case Token::BIT_OR:
1488 case Token::SHL:
1489 case Token::SAR:
1490 case Token::SHR:
1491 // Right operand is saved in ecx and eax was destroyed by the smi
1492 // check.
1493 __ mov(eax, ecx);
1494 break;
1495
1496 case Token::DIV:
1497 case Token::MOD:
1498 // Operands are in eax, ebx at this point.
1499 __ mov(edx, eax);
1500 __ mov(eax, ebx);
1501 break;
1502
1503 default:
1504 break;
1505 }
1506}
1507
1508
danno@chromium.org40cb8782011-05-25 07:58:50 +00001509void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001510 Label right_arg_changed, call_runtime;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001511
1512 switch (op_) {
1513 case Token::ADD:
1514 case Token::SUB:
1515 case Token::MUL:
1516 case Token::DIV:
1517 break;
1518 case Token::MOD:
1519 case Token::BIT_OR:
1520 case Token::BIT_AND:
1521 case Token::BIT_XOR:
1522 case Token::SAR:
1523 case Token::SHL:
1524 case Token::SHR:
1525 GenerateRegisterArgsPush(masm);
1526 break;
1527 default:
1528 UNREACHABLE();
1529 }
1530
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001531 if (op_ == Token::MOD && has_fixed_right_arg_) {
1532 // It is guaranteed that the value will fit into a Smi, because if it
1533 // didn't, we wouldn't be here, see BinaryOp_Patch.
1534 __ cmp(eax, Immediate(Smi::FromInt(fixed_right_arg_value())));
1535 __ j(not_equal, &right_arg_changed);
1536 }
1537
danno@chromium.org40cb8782011-05-25 07:58:50 +00001538 if (result_type_ == BinaryOpIC::UNINITIALIZED ||
1539 result_type_ == BinaryOpIC::SMI) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001540 BinaryOpStub_GenerateSmiCode(
1541 masm, &call_runtime, NO_HEAPNUMBER_RESULTS, op_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001542 } else {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001543 BinaryOpStub_GenerateSmiCode(
1544 masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS, op_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001545 }
ulan@chromium.org906e2fb2013-05-14 08:14:38 +00001546
1547 // Code falls through if the result is not returned as either a smi or heap
1548 // number.
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001549 __ bind(&right_arg_changed);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001550 switch (op_) {
1551 case Token::ADD:
1552 case Token::SUB:
1553 case Token::MUL:
1554 case Token::DIV:
1555 GenerateTypeTransition(masm);
1556 break;
1557 case Token::MOD:
1558 case Token::BIT_OR:
1559 case Token::BIT_AND:
1560 case Token::BIT_XOR:
1561 case Token::SAR:
1562 case Token::SHL:
1563 case Token::SHR:
1564 GenerateTypeTransitionWithSavedArgs(masm);
1565 break;
1566 default:
1567 UNREACHABLE();
1568 }
ulan@chromium.org906e2fb2013-05-14 08:14:38 +00001569
1570 __ bind(&call_runtime);
1571 switch (op_) {
1572 case Token::ADD:
1573 case Token::SUB:
1574 case Token::MUL:
1575 case Token::DIV:
ulan@chromium.org906e2fb2013-05-14 08:14:38 +00001576 break;
1577 case Token::MOD:
1578 case Token::BIT_OR:
1579 case Token::BIT_AND:
1580 case Token::BIT_XOR:
1581 case Token::SAR:
1582 case Token::SHL:
1583 case Token::SHR:
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001584 BinaryOpStub_GenerateRegisterArgsPop(masm);
ulan@chromium.org906e2fb2013-05-14 08:14:38 +00001585 break;
1586 default:
1587 UNREACHABLE();
1588 }
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001589
1590 {
1591 FrameScope scope(masm, StackFrame::INTERNAL);
1592 __ push(edx);
1593 __ push(eax);
1594 GenerateCallRuntime(masm);
1595 }
1596 __ ret(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001597}
1598
1599
danno@chromium.org40cb8782011-05-25 07:58:50 +00001600void BinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00001601 Label call_runtime;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001602 ASSERT(left_type_ == BinaryOpIC::STRING && right_type_ == BinaryOpIC::STRING);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001603 ASSERT(op_ == Token::ADD);
1604 // If both arguments are strings, call the string add stub.
1605 // Otherwise, do a transition.
1606
1607 // Registers containing left and right operands respectively.
1608 Register left = edx;
1609 Register right = eax;
1610
1611 // Test if left operand is a string.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001612 __ JumpIfSmi(left, &call_runtime, Label::kNear);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001613 __ CmpObjectType(left, FIRST_NONSTRING_TYPE, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001614 __ j(above_equal, &call_runtime, Label::kNear);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001615
1616 // Test if right operand is a string.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001617 __ JumpIfSmi(right, &call_runtime, Label::kNear);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001618 __ CmpObjectType(right, FIRST_NONSTRING_TYPE, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001619 __ j(above_equal, &call_runtime, Label::kNear);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001620
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001621 StringAddStub string_add_stub((StringAddFlags)
1622 (ERECT_FRAME | NO_STRING_CHECK_IN_STUB));
danno@chromium.org160a7b02011-04-18 15:51:38 +00001623 GenerateRegisterArgsPush(masm);
1624 __ TailCallStub(&string_add_stub);
1625
1626 __ bind(&call_runtime);
1627 GenerateTypeTransition(masm);
1628}
1629
1630
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001631static void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm,
1632 Label* alloc_failure,
1633 OverwriteMode mode);
1634
1635
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001636// Input:
1637// edx: left operand (tagged)
1638// eax: right operand (tagged)
1639// Output:
1640// eax: result (tagged)
danno@chromium.org40cb8782011-05-25 07:58:50 +00001641void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001642 Label call_runtime;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001643 ASSERT(Max(left_type_, right_type_) == BinaryOpIC::INT32);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001644
1645 // Floating point case.
1646 switch (op_) {
1647 case Token::ADD:
1648 case Token::SUB:
1649 case Token::MUL:
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001650 case Token::DIV:
1651 case Token::MOD: {
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001652 Label not_floats, not_int32, right_arg_changed;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001653 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001654 CpuFeatureScope use_sse2(masm, SSE2);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001655 // It could be that only SMIs have been seen at either the left
1656 // or the right operand. For precise type feedback, patch the IC
1657 // again if this changes.
1658 // In theory, we would need the same check in the non-SSE2 case,
1659 // but since we don't support Crankshaft on such hardware we can
1660 // afford not to care about precise type feedback.
1661 if (left_type_ == BinaryOpIC::SMI) {
1662 __ JumpIfNotSmi(edx, &not_int32);
1663 }
1664 if (right_type_ == BinaryOpIC::SMI) {
1665 __ JumpIfNotSmi(eax, &not_int32);
1666 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001667 FloatingPointHelper::LoadSSE2Operands(masm, &not_floats);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001668 FloatingPointHelper::CheckSSE2OperandIsInt32(
1669 masm, &not_int32, xmm0, ebx, ecx, xmm2);
1670 FloatingPointHelper::CheckSSE2OperandIsInt32(
1671 masm, &not_int32, xmm1, edi, ecx, xmm2);
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001672 if (op_ == Token::MOD) {
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001673 if (has_fixed_right_arg_) {
1674 __ cmp(edi, Immediate(fixed_right_arg_value()));
1675 __ j(not_equal, &right_arg_changed);
1676 }
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001677 GenerateRegisterArgsPush(masm);
1678 __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
1679 } else {
1680 switch (op_) {
1681 case Token::ADD: __ addsd(xmm0, xmm1); break;
1682 case Token::SUB: __ subsd(xmm0, xmm1); break;
1683 case Token::MUL: __ mulsd(xmm0, xmm1); break;
1684 case Token::DIV: __ divsd(xmm0, xmm1); break;
1685 default: UNREACHABLE();
1686 }
1687 // Check result type if it is currently Int32.
1688 if (result_type_ <= BinaryOpIC::INT32) {
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001689 FloatingPointHelper::CheckSSE2OperandIsInt32(
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001690 masm, &not_int32, xmm0, ecx, ecx, xmm2);
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001691 }
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001692 BinaryOpStub_GenerateHeapResultAllocation(masm, &call_runtime, mode_);
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001693 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1694 __ ret(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001695 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001696 } else { // SSE2 not available, use FPU.
1697 FloatingPointHelper::CheckFloatOperands(masm, &not_floats, ebx);
1698 FloatingPointHelper::LoadFloatOperands(
1699 masm,
1700 ecx,
1701 FloatingPointHelper::ARGS_IN_REGISTERS);
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001702 if (op_ == Token::MOD) {
1703 // The operands are now on the FPU stack, but we don't need them.
1704 __ fstp(0);
1705 __ fstp(0);
1706 GenerateRegisterArgsPush(masm);
1707 __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
1708 } else {
1709 switch (op_) {
1710 case Token::ADD: __ faddp(1); break;
1711 case Token::SUB: __ fsubp(1); break;
1712 case Token::MUL: __ fmulp(1); break;
1713 case Token::DIV: __ fdivp(1); break;
1714 default: UNREACHABLE();
1715 }
1716 Label after_alloc_failure;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001717 BinaryOpStub_GenerateHeapResultAllocation(
1718 masm, &after_alloc_failure, mode_);
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001719 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1720 __ ret(0);
1721 __ bind(&after_alloc_failure);
1722 __ fstp(0); // Pop FPU stack before calling runtime.
1723 __ jmp(&call_runtime);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001724 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001725 }
1726
1727 __ bind(&not_floats);
1728 __ bind(&not_int32);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001729 __ bind(&right_arg_changed);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001730 GenerateTypeTransition(masm);
1731 break;
1732 }
1733
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001734 case Token::BIT_OR:
1735 case Token::BIT_AND:
1736 case Token::BIT_XOR:
1737 case Token::SAR:
1738 case Token::SHL:
1739 case Token::SHR: {
1740 GenerateRegisterArgsPush(masm);
1741 Label not_floats;
1742 Label not_int32;
1743 Label non_smi_result;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001744 bool use_sse3 = platform_specific_bit_;
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001745 FloatingPointHelper::LoadUnknownsAsIntegers(
1746 masm, use_sse3, left_type_, right_type_, &not_floats);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001747 switch (op_) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001748 case Token::BIT_OR: __ or_(eax, ecx); break;
1749 case Token::BIT_AND: __ and_(eax, ecx); break;
1750 case Token::BIT_XOR: __ xor_(eax, ecx); break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001751 case Token::SAR: __ sar_cl(eax); break;
1752 case Token::SHL: __ shl_cl(eax); break;
1753 case Token::SHR: __ shr_cl(eax); break;
1754 default: UNREACHABLE();
1755 }
1756 if (op_ == Token::SHR) {
1757 // Check if result is non-negative and fits in a smi.
1758 __ test(eax, Immediate(0xc0000000));
1759 __ j(not_zero, &call_runtime);
1760 } else {
1761 // Check if result fits in a smi.
1762 __ cmp(eax, 0xc0000000);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001763 __ j(negative, &non_smi_result, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001764 }
1765 // Tag smi result and return.
1766 __ SmiTag(eax);
1767 __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack.
1768
1769 // All ops except SHR return a signed int32 that we load in
1770 // a HeapNumber.
1771 if (op_ != Token::SHR) {
1772 __ bind(&non_smi_result);
1773 // Allocate a heap number if needed.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001774 __ mov(ebx, eax); // ebx: result
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001775 Label skip_allocation;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001776 switch (mode_) {
1777 case OVERWRITE_LEFT:
1778 case OVERWRITE_RIGHT:
1779 // If the operand was an object, we skip the
1780 // allocation of a heap number.
1781 __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
1782 1 * kPointerSize : 2 * kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00001783 __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001784 // Fall through!
1785 case NO_OVERWRITE:
1786 __ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
1787 __ bind(&skip_allocation);
1788 break;
1789 default: UNREACHABLE();
1790 }
1791 // Store the result in the HeapNumber and return.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001792 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001793 CpuFeatureScope use_sse2(masm, SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001794 __ cvtsi2sd(xmm0, ebx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001795 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1796 } else {
1797 __ mov(Operand(esp, 1 * kPointerSize), ebx);
1798 __ fild_s(Operand(esp, 1 * kPointerSize));
1799 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1800 }
1801 __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack.
1802 }
1803
1804 __ bind(&not_floats);
1805 __ bind(&not_int32);
1806 GenerateTypeTransitionWithSavedArgs(masm);
1807 break;
1808 }
1809 default: UNREACHABLE(); break;
1810 }
1811
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001812 // If an allocation fails, or SHR hits a hard case, use the runtime system to
1813 // get the correct result.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001814 __ bind(&call_runtime);
1815
1816 switch (op_) {
1817 case Token::ADD:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001818 case Token::SUB:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001819 case Token::MUL:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001820 case Token::DIV:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001821 break;
1822 case Token::MOD:
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001823 return; // Handled above.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001824 case Token::BIT_OR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001825 case Token::BIT_AND:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001826 case Token::BIT_XOR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001827 case Token::SAR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001828 case Token::SHL:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001829 case Token::SHR:
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001830 BinaryOpStub_GenerateRegisterArgsPop(masm);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001831 break;
1832 default:
1833 UNREACHABLE();
1834 }
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001835
1836 {
1837 FrameScope scope(masm, StackFrame::INTERNAL);
1838 __ push(edx);
1839 __ push(eax);
1840 GenerateCallRuntime(masm);
1841 }
1842 __ ret(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001843}
1844
1845
danno@chromium.org40cb8782011-05-25 07:58:50 +00001846void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
lrn@chromium.org7516f052011-03-30 08:52:27 +00001847 if (op_ == Token::ADD) {
1848 // Handle string addition here, because it is the only operation
1849 // that does not do a ToNumber conversion on the operands.
1850 GenerateAddStrings(masm);
1851 }
1852
danno@chromium.org160a7b02011-04-18 15:51:38 +00001853 Factory* factory = masm->isolate()->factory();
1854
lrn@chromium.org7516f052011-03-30 08:52:27 +00001855 // Convert odd ball arguments to numbers.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001856 Label check, done;
danno@chromium.org160a7b02011-04-18 15:51:38 +00001857 __ cmp(edx, factory->undefined_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001858 __ j(not_equal, &check, Label::kNear);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001859 if (Token::IsBitOp(op_)) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001860 __ xor_(edx, edx);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001861 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00001862 __ mov(edx, Immediate(factory->nan_value()));
lrn@chromium.org7516f052011-03-30 08:52:27 +00001863 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001864 __ jmp(&done, Label::kNear);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001865 __ bind(&check);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001866 __ cmp(eax, factory->undefined_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001867 __ j(not_equal, &done, Label::kNear);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001868 if (Token::IsBitOp(op_)) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001869 __ xor_(eax, eax);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001870 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00001871 __ mov(eax, Immediate(factory->nan_value()));
lrn@chromium.org7516f052011-03-30 08:52:27 +00001872 }
1873 __ bind(&done);
1874
hpayer@chromium.org8432c912013-02-28 15:55:26 +00001875 GenerateNumberStub(masm);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001876}
1877
1878
hpayer@chromium.org8432c912013-02-28 15:55:26 +00001879void BinaryOpStub::GenerateNumberStub(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001880 Label call_runtime;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001881
1882 // Floating point case.
1883 switch (op_) {
1884 case Token::ADD:
1885 case Token::SUB:
1886 case Token::MUL:
1887 case Token::DIV: {
1888 Label not_floats;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001889 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001890 CpuFeatureScope use_sse2(masm, SSE2);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001891
1892 // It could be that only SMIs have been seen at either the left
1893 // or the right operand. For precise type feedback, patch the IC
1894 // again if this changes.
1895 // In theory, we would need the same check in the non-SSE2 case,
1896 // but since we don't support Crankshaft on such hardware we can
1897 // afford not to care about precise type feedback.
1898 if (left_type_ == BinaryOpIC::SMI) {
1899 __ JumpIfNotSmi(edx, &not_floats);
1900 }
1901 if (right_type_ == BinaryOpIC::SMI) {
1902 __ JumpIfNotSmi(eax, &not_floats);
1903 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001904 FloatingPointHelper::LoadSSE2Operands(masm, &not_floats);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001905 if (left_type_ == BinaryOpIC::INT32) {
1906 FloatingPointHelper::CheckSSE2OperandIsInt32(
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001907 masm, &not_floats, xmm0, ecx, ecx, xmm2);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001908 }
1909 if (right_type_ == BinaryOpIC::INT32) {
1910 FloatingPointHelper::CheckSSE2OperandIsInt32(
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001911 masm, &not_floats, xmm1, ecx, ecx, xmm2);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001912 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001913
1914 switch (op_) {
1915 case Token::ADD: __ addsd(xmm0, xmm1); break;
1916 case Token::SUB: __ subsd(xmm0, xmm1); break;
1917 case Token::MUL: __ mulsd(xmm0, xmm1); break;
1918 case Token::DIV: __ divsd(xmm0, xmm1); break;
1919 default: UNREACHABLE();
1920 }
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001921 BinaryOpStub_GenerateHeapResultAllocation(masm, &call_runtime, mode_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001922 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1923 __ ret(0);
1924 } else { // SSE2 not available, use FPU.
1925 FloatingPointHelper::CheckFloatOperands(masm, &not_floats, ebx);
1926 FloatingPointHelper::LoadFloatOperands(
1927 masm,
1928 ecx,
1929 FloatingPointHelper::ARGS_IN_REGISTERS);
1930 switch (op_) {
1931 case Token::ADD: __ faddp(1); break;
1932 case Token::SUB: __ fsubp(1); break;
1933 case Token::MUL: __ fmulp(1); break;
1934 case Token::DIV: __ fdivp(1); break;
1935 default: UNREACHABLE();
1936 }
1937 Label after_alloc_failure;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001938 BinaryOpStub_GenerateHeapResultAllocation(
1939 masm, &after_alloc_failure, mode_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001940 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1941 __ ret(0);
1942 __ bind(&after_alloc_failure);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001943 __ fstp(0); // Pop FPU stack before calling runtime.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001944 __ jmp(&call_runtime);
1945 }
1946
1947 __ bind(&not_floats);
1948 GenerateTypeTransition(masm);
1949 break;
1950 }
1951
1952 case Token::MOD: {
1953 // For MOD we go directly to runtime in the non-smi case.
1954 break;
1955 }
1956 case Token::BIT_OR:
1957 case Token::BIT_AND:
1958 case Token::BIT_XOR:
1959 case Token::SAR:
1960 case Token::SHL:
1961 case Token::SHR: {
1962 GenerateRegisterArgsPush(masm);
1963 Label not_floats;
1964 Label non_smi_result;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001965 // We do not check the input arguments here, as any value is
1966 // unconditionally truncated to an int32 anyway. To get the
1967 // right optimized code, int32 type feedback is just right.
1968 bool use_sse3 = platform_specific_bit_;
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001969 FloatingPointHelper::LoadUnknownsAsIntegers(
1970 masm, use_sse3, left_type_, right_type_, &not_floats);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001971 switch (op_) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001972 case Token::BIT_OR: __ or_(eax, ecx); break;
1973 case Token::BIT_AND: __ and_(eax, ecx); break;
1974 case Token::BIT_XOR: __ xor_(eax, ecx); break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001975 case Token::SAR: __ sar_cl(eax); break;
1976 case Token::SHL: __ shl_cl(eax); break;
1977 case Token::SHR: __ shr_cl(eax); break;
1978 default: UNREACHABLE();
1979 }
1980 if (op_ == Token::SHR) {
1981 // Check if result is non-negative and fits in a smi.
1982 __ test(eax, Immediate(0xc0000000));
1983 __ j(not_zero, &call_runtime);
1984 } else {
1985 // Check if result fits in a smi.
1986 __ cmp(eax, 0xc0000000);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001987 __ j(negative, &non_smi_result, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001988 }
1989 // Tag smi result and return.
1990 __ SmiTag(eax);
1991 __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack.
1992
1993 // All ops except SHR return a signed int32 that we load in
1994 // a HeapNumber.
1995 if (op_ != Token::SHR) {
1996 __ bind(&non_smi_result);
1997 // Allocate a heap number if needed.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001998 __ mov(ebx, eax); // ebx: result
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001999 Label skip_allocation;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002000 switch (mode_) {
2001 case OVERWRITE_LEFT:
2002 case OVERWRITE_RIGHT:
2003 // If the operand was an object, we skip the
2004 // allocation of a heap number.
2005 __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
2006 1 * kPointerSize : 2 * kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00002007 __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002008 // Fall through!
2009 case NO_OVERWRITE:
2010 __ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
2011 __ bind(&skip_allocation);
2012 break;
2013 default: UNREACHABLE();
2014 }
2015 // Store the result in the HeapNumber and return.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002016 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00002017 CpuFeatureScope use_sse2(masm, SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002018 __ cvtsi2sd(xmm0, ebx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002019 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
2020 } else {
2021 __ mov(Operand(esp, 1 * kPointerSize), ebx);
2022 __ fild_s(Operand(esp, 1 * kPointerSize));
2023 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
2024 }
2025 __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack.
2026 }
2027
2028 __ bind(&not_floats);
2029 GenerateTypeTransitionWithSavedArgs(masm);
2030 break;
2031 }
2032 default: UNREACHABLE(); break;
2033 }
2034
2035 // If an allocation fails, or SHR or MOD hit a hard case,
2036 // use the runtime system to get the correct result.
2037 __ bind(&call_runtime);
2038
2039 switch (op_) {
2040 case Token::ADD:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002041 case Token::SUB:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002042 case Token::MUL:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002043 case Token::DIV:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002044 case Token::MOD:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002045 break;
2046 case Token::BIT_OR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002047 case Token::BIT_AND:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002048 case Token::BIT_XOR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002049 case Token::SAR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002050 case Token::SHL:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002051 case Token::SHR:
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00002052 BinaryOpStub_GenerateRegisterArgsPop(masm);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002053 break;
2054 default:
2055 UNREACHABLE();
2056 }
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00002057
2058 {
2059 FrameScope scope(masm, StackFrame::INTERNAL);
2060 __ push(edx);
2061 __ push(eax);
2062 GenerateCallRuntime(masm);
2063 }
2064 __ ret(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002065}
2066
2067
danno@chromium.org40cb8782011-05-25 07:58:50 +00002068void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002069 Label call_runtime;
2070
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002071 Counters* counters = masm->isolate()->counters();
2072 __ IncrementCounter(counters->generic_binary_stub_calls(), 1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002073
2074 switch (op_) {
2075 case Token::ADD:
2076 case Token::SUB:
2077 case Token::MUL:
2078 case Token::DIV:
2079 break;
2080 case Token::MOD:
2081 case Token::BIT_OR:
2082 case Token::BIT_AND:
2083 case Token::BIT_XOR:
2084 case Token::SAR:
2085 case Token::SHL:
2086 case Token::SHR:
2087 GenerateRegisterArgsPush(masm);
2088 break;
2089 default:
2090 UNREACHABLE();
2091 }
2092
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002093 BinaryOpStub_GenerateSmiCode(
2094 masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS, op_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002095
2096 // Floating point case.
2097 switch (op_) {
2098 case Token::ADD:
2099 case Token::SUB:
2100 case Token::MUL:
2101 case Token::DIV: {
2102 Label not_floats;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002103 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00002104 CpuFeatureScope use_sse2(masm, SSE2);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002105 FloatingPointHelper::LoadSSE2Operands(masm, &not_floats);
2106
2107 switch (op_) {
2108 case Token::ADD: __ addsd(xmm0, xmm1); break;
2109 case Token::SUB: __ subsd(xmm0, xmm1); break;
2110 case Token::MUL: __ mulsd(xmm0, xmm1); break;
2111 case Token::DIV: __ divsd(xmm0, xmm1); break;
2112 default: UNREACHABLE();
2113 }
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002114 BinaryOpStub_GenerateHeapResultAllocation(masm, &call_runtime, mode_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002115 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
2116 __ ret(0);
2117 } else { // SSE2 not available, use FPU.
2118 FloatingPointHelper::CheckFloatOperands(masm, &not_floats, ebx);
2119 FloatingPointHelper::LoadFloatOperands(
2120 masm,
2121 ecx,
2122 FloatingPointHelper::ARGS_IN_REGISTERS);
2123 switch (op_) {
2124 case Token::ADD: __ faddp(1); break;
2125 case Token::SUB: __ fsubp(1); break;
2126 case Token::MUL: __ fmulp(1); break;
2127 case Token::DIV: __ fdivp(1); break;
2128 default: UNREACHABLE();
2129 }
2130 Label after_alloc_failure;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002131 BinaryOpStub_GenerateHeapResultAllocation(
2132 masm, &after_alloc_failure, mode_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002133 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
2134 __ ret(0);
2135 __ bind(&after_alloc_failure);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00002136 __ fstp(0); // Pop FPU stack before calling runtime.
2137 __ jmp(&call_runtime);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002138 }
2139 __ bind(&not_floats);
2140 break;
2141 }
2142 case Token::MOD: {
2143 // For MOD we go directly to runtime in the non-smi case.
2144 break;
2145 }
2146 case Token::BIT_OR:
2147 case Token::BIT_AND:
2148 case Token::BIT_XOR:
2149 case Token::SAR:
2150 case Token::SHL:
2151 case Token::SHR: {
2152 Label non_smi_result;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002153 bool use_sse3 = platform_specific_bit_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002154 FloatingPointHelper::LoadUnknownsAsIntegers(masm,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002155 use_sse3,
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002156 BinaryOpIC::GENERIC,
2157 BinaryOpIC::GENERIC,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002158 &call_runtime);
2159 switch (op_) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002160 case Token::BIT_OR: __ or_(eax, ecx); break;
2161 case Token::BIT_AND: __ and_(eax, ecx); break;
2162 case Token::BIT_XOR: __ xor_(eax, ecx); break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002163 case Token::SAR: __ sar_cl(eax); break;
2164 case Token::SHL: __ shl_cl(eax); break;
2165 case Token::SHR: __ shr_cl(eax); break;
2166 default: UNREACHABLE();
2167 }
2168 if (op_ == Token::SHR) {
2169 // Check if result is non-negative and fits in a smi.
2170 __ test(eax, Immediate(0xc0000000));
2171 __ j(not_zero, &call_runtime);
2172 } else {
2173 // Check if result fits in a smi.
2174 __ cmp(eax, 0xc0000000);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00002175 __ j(negative, &non_smi_result, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002176 }
2177 // Tag smi result and return.
2178 __ SmiTag(eax);
2179 __ ret(2 * kPointerSize); // Drop the arguments from the stack.
2180
2181 // All ops except SHR return a signed int32 that we load in
2182 // a HeapNumber.
2183 if (op_ != Token::SHR) {
2184 __ bind(&non_smi_result);
2185 // Allocate a heap number if needed.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002186 __ mov(ebx, eax); // ebx: result
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002187 Label skip_allocation;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002188 switch (mode_) {
2189 case OVERWRITE_LEFT:
2190 case OVERWRITE_RIGHT:
2191 // If the operand was an object, we skip the
2192 // allocation of a heap number.
2193 __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
2194 1 * kPointerSize : 2 * kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00002195 __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002196 // Fall through!
2197 case NO_OVERWRITE:
2198 __ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
2199 __ bind(&skip_allocation);
2200 break;
2201 default: UNREACHABLE();
2202 }
2203 // Store the result in the HeapNumber and return.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002204 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00002205 CpuFeatureScope use_sse2(masm, SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002206 __ cvtsi2sd(xmm0, ebx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002207 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
2208 } else {
2209 __ mov(Operand(esp, 1 * kPointerSize), ebx);
2210 __ fild_s(Operand(esp, 1 * kPointerSize));
2211 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
2212 }
2213 __ ret(2 * kPointerSize);
2214 }
2215 break;
2216 }
2217 default: UNREACHABLE(); break;
2218 }
2219
2220 // If all else fails, use the runtime system to get the correct
2221 // result.
2222 __ bind(&call_runtime);
2223 switch (op_) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002224 case Token::ADD:
ager@chromium.org0ee099b2011-01-25 14:06:47 +00002225 GenerateAddStrings(masm);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002226 // Fall through.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002227 case Token::SUB:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002228 case Token::MUL:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002229 case Token::DIV:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002230 break;
2231 case Token::MOD:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002232 case Token::BIT_OR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002233 case Token::BIT_AND:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002234 case Token::BIT_XOR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002235 case Token::SAR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002236 case Token::SHL:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002237 case Token::SHR:
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00002238 BinaryOpStub_GenerateRegisterArgsPop(masm);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002239 break;
2240 default:
2241 UNREACHABLE();
2242 }
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00002243
2244 {
2245 FrameScope scope(masm, StackFrame::INTERNAL);
2246 __ push(edx);
2247 __ push(eax);
2248 GenerateCallRuntime(masm);
2249 }
2250 __ ret(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002251}
2252
2253
danno@chromium.org40cb8782011-05-25 07:58:50 +00002254void BinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002255 ASSERT(op_ == Token::ADD);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002256 Label left_not_string, call_runtime;
ager@chromium.org0ee099b2011-01-25 14:06:47 +00002257
2258 // Registers containing left and right operands respectively.
2259 Register left = edx;
2260 Register right = eax;
2261
2262 // Test if left operand is a string.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002263 __ JumpIfSmi(left, &left_not_string, Label::kNear);
ager@chromium.org0ee099b2011-01-25 14:06:47 +00002264 __ CmpObjectType(left, FIRST_NONSTRING_TYPE, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002265 __ j(above_equal, &left_not_string, Label::kNear);
ager@chromium.org0ee099b2011-01-25 14:06:47 +00002266
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00002267 StringAddStub string_add_left_stub((StringAddFlags)
2268 (ERECT_FRAME | NO_STRING_CHECK_LEFT_IN_STUB));
ager@chromium.org0ee099b2011-01-25 14:06:47 +00002269 GenerateRegisterArgsPush(masm);
2270 __ TailCallStub(&string_add_left_stub);
2271
2272 // Left operand is not a string, test right.
2273 __ bind(&left_not_string);
whesse@chromium.org7b260152011-06-20 15:33:18 +00002274 __ JumpIfSmi(right, &call_runtime, Label::kNear);
ager@chromium.org0ee099b2011-01-25 14:06:47 +00002275 __ CmpObjectType(right, FIRST_NONSTRING_TYPE, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002276 __ j(above_equal, &call_runtime, Label::kNear);
ager@chromium.org0ee099b2011-01-25 14:06:47 +00002277
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00002278 StringAddStub string_add_right_stub((StringAddFlags)
2279 (ERECT_FRAME | NO_STRING_CHECK_RIGHT_IN_STUB));
ager@chromium.org0ee099b2011-01-25 14:06:47 +00002280 GenerateRegisterArgsPush(masm);
2281 __ TailCallStub(&string_add_right_stub);
2282
2283 // Neither argument is a string.
2284 __ bind(&call_runtime);
2285}
2286
2287
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002288static void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm,
2289 Label* alloc_failure,
2290 OverwriteMode mode) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002291 Label skip_allocation;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002292 switch (mode) {
2293 case OVERWRITE_LEFT: {
2294 // If the argument in edx is already an object, we skip the
2295 // allocation of a heap number.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002296 __ JumpIfNotSmi(edx, &skip_allocation, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002297 // Allocate a heap number for the result. Keep eax and edx intact
2298 // for the possible runtime call.
2299 __ AllocateHeapNumber(ebx, ecx, no_reg, alloc_failure);
2300 // Now edx can be overwritten losing one of the arguments as we are
2301 // now done and will not need it any more.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002302 __ mov(edx, ebx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002303 __ bind(&skip_allocation);
2304 // Use object in edx as a result holder
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002305 __ mov(eax, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002306 break;
2307 }
2308 case OVERWRITE_RIGHT:
2309 // If the argument in eax is already an object, we skip the
2310 // allocation of a heap number.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002311 __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002312 // Fall through!
2313 case NO_OVERWRITE:
2314 // Allocate a heap number for the result. Keep eax and edx intact
2315 // for the possible runtime call.
2316 __ AllocateHeapNumber(ebx, ecx, no_reg, alloc_failure);
2317 // Now eax can be overwritten losing one of the arguments as we are
2318 // now done and will not need it any more.
2319 __ mov(eax, ebx);
2320 __ bind(&skip_allocation);
2321 break;
2322 default: UNREACHABLE();
2323 }
2324}
2325
2326
danno@chromium.org40cb8782011-05-25 07:58:50 +00002327void BinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002328 __ pop(ecx);
2329 __ push(edx);
2330 __ push(eax);
2331 __ push(ecx);
2332}
2333
2334
ricow@chromium.org65fae842010-08-25 15:26:24 +00002335void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
whesse@chromium.org023421e2010-12-21 12:19:12 +00002336 // TAGGED case:
2337 // Input:
2338 // esp[4]: tagged number input argument (should be number).
2339 // esp[0]: return address.
2340 // Output:
2341 // eax: tagged double result.
2342 // UNTAGGED case:
2343 // Input::
2344 // esp[0]: return address.
2345 // xmm1: untagged double input argument
2346 // Output:
2347 // xmm1: untagged double result.
2348
ricow@chromium.org65fae842010-08-25 15:26:24 +00002349 Label runtime_call;
2350 Label runtime_call_clear_stack;
whesse@chromium.org023421e2010-12-21 12:19:12 +00002351 Label skip_cache;
2352 const bool tagged = (argument_type_ == TAGGED);
2353 if (tagged) {
2354 // Test that eax is a number.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002355 Label input_not_smi;
2356 Label loaded;
whesse@chromium.org023421e2010-12-21 12:19:12 +00002357 __ mov(eax, Operand(esp, kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00002358 __ JumpIfNotSmi(eax, &input_not_smi, Label::kNear);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002359 // Input is a smi. Untag and load it onto the FPU stack.
2360 // Then load the low and high words of the double into ebx, edx.
2361 STATIC_ASSERT(kSmiTagSize == 1);
2362 __ sar(eax, 1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002363 __ sub(esp, Immediate(2 * kPointerSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002364 __ mov(Operand(esp, 0), eax);
2365 __ fild_s(Operand(esp, 0));
2366 __ fst_d(Operand(esp, 0));
2367 __ pop(edx);
2368 __ pop(ebx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002369 __ jmp(&loaded, Label::kNear);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002370 __ bind(&input_not_smi);
2371 // Check if input is a HeapNumber.
2372 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002373 Factory* factory = masm->isolate()->factory();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002374 __ cmp(ebx, Immediate(factory->heap_number_map()));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002375 __ j(not_equal, &runtime_call);
2376 // Input is a HeapNumber. Push it on the FPU stack and load its
2377 // low and high words into ebx, edx.
2378 __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
2379 __ mov(edx, FieldOperand(eax, HeapNumber::kExponentOffset));
2380 __ mov(ebx, FieldOperand(eax, HeapNumber::kMantissaOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002381
whesse@chromium.org023421e2010-12-21 12:19:12 +00002382 __ bind(&loaded);
2383 } else { // UNTAGGED.
ulan@chromium.org750145a2013-03-07 15:14:13 +00002384 CpuFeatureScope scope(masm, SSE2);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002385 if (CpuFeatures::IsSupported(SSE4_1)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00002386 CpuFeatureScope sse4_scope(masm, SSE4_1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002387 __ pextrd(edx, xmm1, 0x1); // copy xmm1[63..32] to edx.
whesse@chromium.org023421e2010-12-21 12:19:12 +00002388 } else {
2389 __ pshufd(xmm0, xmm1, 0x1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002390 __ movd(edx, xmm0);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002391 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002392 __ movd(ebx, xmm1);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002393 }
2394
2395 // ST[0] or xmm1 == double value
ricow@chromium.org65fae842010-08-25 15:26:24 +00002396 // ebx = low 32 bits of double value
2397 // edx = high 32 bits of double value
2398 // Compute hash (the shifts are arithmetic):
2399 // h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1);
2400 __ mov(ecx, ebx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002401 __ xor_(ecx, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002402 __ mov(eax, ecx);
2403 __ sar(eax, 16);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002404 __ xor_(ecx, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002405 __ mov(eax, ecx);
2406 __ sar(eax, 8);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002407 __ xor_(ecx, eax);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002408 ASSERT(IsPowerOf2(TranscendentalCache::SubCache::kCacheSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002409 __ and_(ecx,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002410 Immediate(TranscendentalCache::SubCache::kCacheSize - 1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002411
whesse@chromium.org023421e2010-12-21 12:19:12 +00002412 // ST[0] or xmm1 == double value.
ricow@chromium.org65fae842010-08-25 15:26:24 +00002413 // ebx = low 32 bits of double value.
2414 // edx = high 32 bits of double value.
2415 // ecx = TranscendentalCache::hash(double value).
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002416 ExternalReference cache_array =
2417 ExternalReference::transcendental_cache_array_address(masm->isolate());
2418 __ mov(eax, Immediate(cache_array));
2419 int cache_array_index =
2420 type_ * sizeof(masm->isolate()->transcendental_cache()->caches_[0]);
2421 __ mov(eax, Operand(eax, cache_array_index));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002422 // Eax points to the cache for the type type_.
2423 // If NULL, the cache hasn't been initialized yet, so go through runtime.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002424 __ test(eax, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002425 __ j(zero, &runtime_call_clear_stack);
2426#ifdef DEBUG
2427 // Check that the layout of cache elements match expectations.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002428 { TranscendentalCache::SubCache::Element test_elem[2];
ricow@chromium.org65fae842010-08-25 15:26:24 +00002429 char* elem_start = reinterpret_cast<char*>(&test_elem[0]);
2430 char* elem2_start = reinterpret_cast<char*>(&test_elem[1]);
2431 char* elem_in0 = reinterpret_cast<char*>(&(test_elem[0].in[0]));
2432 char* elem_in1 = reinterpret_cast<char*>(&(test_elem[0].in[1]));
2433 char* elem_out = reinterpret_cast<char*>(&(test_elem[0].output));
2434 CHECK_EQ(12, elem2_start - elem_start); // Two uint_32's and a pointer.
2435 CHECK_EQ(0, elem_in0 - elem_start);
2436 CHECK_EQ(kIntSize, elem_in1 - elem_start);
2437 CHECK_EQ(2 * kIntSize, elem_out - elem_start);
2438 }
2439#endif
2440 // Find the address of the ecx'th entry in the cache, i.e., &eax[ecx*12].
2441 __ lea(ecx, Operand(ecx, ecx, times_2, 0));
2442 __ lea(ecx, Operand(eax, ecx, times_4, 0));
2443 // Check if cache matches: Double value is stored in uint32_t[2] array.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002444 Label cache_miss;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002445 __ cmp(ebx, Operand(ecx, 0));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002446 __ j(not_equal, &cache_miss, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002447 __ cmp(edx, Operand(ecx, kIntSize));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002448 __ j(not_equal, &cache_miss, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002449 // Cache hit!
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00002450 Counters* counters = masm->isolate()->counters();
2451 __ IncrementCounter(counters->transcendental_cache_hit(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002452 __ mov(eax, Operand(ecx, 2 * kIntSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002453 if (tagged) {
2454 __ fstp(0);
2455 __ ret(kPointerSize);
2456 } else { // UNTAGGED.
ulan@chromium.org750145a2013-03-07 15:14:13 +00002457 CpuFeatureScope scope(masm, SSE2);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002458 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
2459 __ Ret();
2460 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002461
2462 __ bind(&cache_miss);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00002463 __ IncrementCounter(counters->transcendental_cache_miss(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002464 // Update cache with new value.
2465 // We are short on registers, so use no_reg as scratch.
2466 // This gives slightly larger code.
whesse@chromium.org023421e2010-12-21 12:19:12 +00002467 if (tagged) {
2468 __ AllocateHeapNumber(eax, edi, no_reg, &runtime_call_clear_stack);
2469 } else { // UNTAGGED.
ulan@chromium.org750145a2013-03-07 15:14:13 +00002470 CpuFeatureScope scope(masm, SSE2);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002471 __ AllocateHeapNumber(eax, edi, no_reg, &skip_cache);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002472 __ sub(esp, Immediate(kDoubleSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002473 __ movdbl(Operand(esp, 0), xmm1);
2474 __ fld_d(Operand(esp, 0));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002475 __ add(esp, Immediate(kDoubleSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002476 }
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002477 GenerateOperation(masm, type_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002478 __ mov(Operand(ecx, 0), ebx);
2479 __ mov(Operand(ecx, kIntSize), edx);
2480 __ mov(Operand(ecx, 2 * kIntSize), eax);
2481 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002482 if (tagged) {
2483 __ ret(kPointerSize);
2484 } else { // UNTAGGED.
ulan@chromium.org750145a2013-03-07 15:14:13 +00002485 CpuFeatureScope scope(masm, SSE2);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002486 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
2487 __ Ret();
ricow@chromium.org65fae842010-08-25 15:26:24 +00002488
whesse@chromium.org023421e2010-12-21 12:19:12 +00002489 // Skip cache and return answer directly, only in untagged case.
2490 __ bind(&skip_cache);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002491 __ sub(esp, Immediate(kDoubleSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002492 __ movdbl(Operand(esp, 0), xmm1);
2493 __ fld_d(Operand(esp, 0));
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002494 GenerateOperation(masm, type_);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002495 __ fstp_d(Operand(esp, 0));
2496 __ movdbl(xmm1, Operand(esp, 0));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002497 __ add(esp, Immediate(kDoubleSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002498 // We return the value in xmm1 without adding it to the cache, but
2499 // we cause a scavenging GC so that future allocations will succeed.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002500 {
2501 FrameScope scope(masm, StackFrame::INTERNAL);
2502 // Allocate an unused object bigger than a HeapNumber.
2503 __ push(Immediate(Smi::FromInt(2 * kDoubleSize)));
2504 __ CallRuntimeSaveDoubles(Runtime::kAllocateInNewSpace);
2505 }
whesse@chromium.org023421e2010-12-21 12:19:12 +00002506 __ Ret();
2507 }
2508
2509 // Call runtime, doing whatever allocation and cleanup is necessary.
2510 if (tagged) {
2511 __ bind(&runtime_call_clear_stack);
2512 __ fstp(0);
2513 __ bind(&runtime_call);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002514 ExternalReference runtime =
2515 ExternalReference(RuntimeFunction(), masm->isolate());
2516 __ TailCallExternalReference(runtime, 1, 1);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002517 } else { // UNTAGGED.
ulan@chromium.org750145a2013-03-07 15:14:13 +00002518 CpuFeatureScope scope(masm, SSE2);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002519 __ bind(&runtime_call_clear_stack);
2520 __ bind(&runtime_call);
2521 __ AllocateHeapNumber(eax, edi, no_reg, &skip_cache);
2522 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002523 {
2524 FrameScope scope(masm, StackFrame::INTERNAL);
2525 __ push(eax);
2526 __ CallRuntime(RuntimeFunction(), 1);
2527 }
whesse@chromium.org023421e2010-12-21 12:19:12 +00002528 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
2529 __ Ret();
2530 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002531}
2532
2533
2534Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
2535 switch (type_) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00002536 case TranscendentalCache::SIN: return Runtime::kMath_sin;
2537 case TranscendentalCache::COS: return Runtime::kMath_cos;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00002538 case TranscendentalCache::TAN: return Runtime::kMath_tan;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002539 case TranscendentalCache::LOG: return Runtime::kMath_log;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002540 default:
2541 UNIMPLEMENTED();
2542 return Runtime::kAbort;
2543 }
2544}
2545
2546
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002547void TranscendentalCacheStub::GenerateOperation(
2548 MacroAssembler* masm, TranscendentalCache::Type type) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00002549 // Only free register is edi.
whesse@chromium.org023421e2010-12-21 12:19:12 +00002550 // Input value is on FP stack, and also in ebx/edx.
2551 // Input value is possibly in xmm1.
2552 // Address of result (a newly allocated HeapNumber) may be in eax.
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002553 if (type == TranscendentalCache::SIN ||
2554 type == TranscendentalCache::COS ||
2555 type == TranscendentalCache::TAN) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002556 // Both fsin and fcos require arguments in the range +/-2^63 and
2557 // return NaN for infinities and NaN. They can share all code except
2558 // the actual fsin/fcos operation.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002559 Label in_range, done;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002560 // If argument is outside the range -2^63..2^63, fsin/cos doesn't
2561 // work. We must reduce it to the appropriate range.
2562 __ mov(edi, edx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002563 __ and_(edi, Immediate(0x7ff00000)); // Exponent only.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002564 int supported_exponent_limit =
2565 (63 + HeapNumber::kExponentBias) << HeapNumber::kExponentShift;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002566 __ cmp(edi, Immediate(supported_exponent_limit));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00002567 __ j(below, &in_range, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002568 // Check for infinity and NaN. Both return NaN for sin.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002569 __ cmp(edi, Immediate(0x7ff00000));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002570 Label non_nan_result;
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00002571 __ j(not_equal, &non_nan_result, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002572 // Input is +/-Infinity or NaN. Result is NaN.
2573 __ fstp(0);
2574 // NaN is represented by 0x7ff8000000000000.
2575 __ push(Immediate(0x7ff80000));
2576 __ push(Immediate(0));
2577 __ fld_d(Operand(esp, 0));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002578 __ add(esp, Immediate(2 * kPointerSize));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002579 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002580
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002581 __ bind(&non_nan_result);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002582
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002583 // Use fpmod to restrict argument to the range +/-2*PI.
2584 __ mov(edi, eax); // Save eax before using fnstsw_ax.
2585 __ fldpi();
2586 __ fadd(0);
2587 __ fld(1);
2588 // FPU Stack: input, 2*pi, input.
2589 {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002590 Label no_exceptions;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002591 __ fwait();
2592 __ fnstsw_ax();
2593 // Clear if Illegal Operand or Zero Division exceptions are set.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002594 __ test(eax, Immediate(5));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002595 __ j(zero, &no_exceptions, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002596 __ fnclex();
2597 __ bind(&no_exceptions);
2598 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002599
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002600 // Compute st(0) % st(1)
2601 {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002602 Label partial_remainder_loop;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002603 __ bind(&partial_remainder_loop);
2604 __ fprem1();
2605 __ fwait();
2606 __ fnstsw_ax();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002607 __ test(eax, Immediate(0x400 /* C2 */));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002608 // If C2 is set, computation only has partial result. Loop to
2609 // continue computation.
2610 __ j(not_zero, &partial_remainder_loop);
2611 }
2612 // FPU Stack: input, 2*pi, input % 2*pi
2613 __ fstp(2);
2614 __ fstp(0);
2615 __ mov(eax, edi); // Restore eax (allocated HeapNumber pointer).
2616
2617 // FPU Stack: input % 2*pi
2618 __ bind(&in_range);
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002619 switch (type) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002620 case TranscendentalCache::SIN:
2621 __ fsin();
2622 break;
2623 case TranscendentalCache::COS:
2624 __ fcos();
2625 break;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00002626 case TranscendentalCache::TAN:
2627 // FPTAN calculates tangent onto st(0) and pushes 1.0 onto the
2628 // FP register stack.
2629 __ fptan();
2630 __ fstp(0); // Pop FP register stack.
2631 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002632 default:
2633 UNREACHABLE();
2634 }
2635 __ bind(&done);
2636 } else {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002637 ASSERT(type == TranscendentalCache::LOG);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002638 __ fldln2();
2639 __ fxch();
2640 __ fyl2x();
ricow@chromium.org65fae842010-08-25 15:26:24 +00002641 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002642}
2643
2644
ricow@chromium.org65fae842010-08-25 15:26:24 +00002645// Input: edx, eax are the left and right objects of a bit op.
2646// Output: eax, ecx are left and right integers for a bit op.
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002647// Warning: can clobber inputs even when it jumps to |conversion_failure|!
2648void FloatingPointHelper::LoadUnknownsAsIntegers(
2649 MacroAssembler* masm,
2650 bool use_sse3,
2651 BinaryOpIC::TypeInfo left_type,
2652 BinaryOpIC::TypeInfo right_type,
2653 Label* conversion_failure) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00002654 // Check float operands.
2655 Label arg1_is_object, check_undefined_arg1;
2656 Label arg2_is_object, check_undefined_arg2;
2657 Label load_arg2, done;
2658
2659 // Test if arg1 is a Smi.
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002660 if (left_type == BinaryOpIC::SMI) {
2661 __ JumpIfNotSmi(edx, conversion_failure);
2662 } else {
2663 __ JumpIfNotSmi(edx, &arg1_is_object, Label::kNear);
2664 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002665
2666 __ SmiUntag(edx);
2667 __ jmp(&load_arg2);
2668
2669 // If the argument is undefined it converts to zero (ECMA-262, section 9.5).
2670 __ bind(&check_undefined_arg1);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002671 Factory* factory = masm->isolate()->factory();
2672 __ cmp(edx, factory->undefined_value());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002673 __ j(not_equal, conversion_failure);
2674 __ mov(edx, Immediate(0));
2675 __ jmp(&load_arg2);
2676
2677 __ bind(&arg1_is_object);
2678 __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002679 __ cmp(ebx, factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002680 __ j(not_equal, &check_undefined_arg1);
2681
2682 // Get the untagged integer version of the edx heap number in ecx.
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002683 if (left_type == BinaryOpIC::INT32 && CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00002684 CpuFeatureScope use_sse2(masm, SSE2);
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002685 ConvertHeapNumberToInt32(masm, edx, conversion_failure);
2686 } else {
2687 IntegerConvert(masm, edx, use_sse3, conversion_failure);
2688 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002689 __ mov(edx, ecx);
2690
2691 // Here edx has the untagged integer, eax has a Smi or a heap number.
2692 __ bind(&load_arg2);
2693
2694 // Test if arg2 is a Smi.
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002695 if (right_type == BinaryOpIC::SMI) {
2696 __ JumpIfNotSmi(eax, conversion_failure);
2697 } else {
2698 __ JumpIfNotSmi(eax, &arg2_is_object, Label::kNear);
2699 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002700
2701 __ SmiUntag(eax);
2702 __ mov(ecx, eax);
2703 __ jmp(&done);
2704
2705 // If the argument is undefined it converts to zero (ECMA-262, section 9.5).
2706 __ bind(&check_undefined_arg2);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002707 __ cmp(eax, factory->undefined_value());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002708 __ j(not_equal, conversion_failure);
2709 __ mov(ecx, Immediate(0));
2710 __ jmp(&done);
2711
2712 __ bind(&arg2_is_object);
2713 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002714 __ cmp(ebx, factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002715 __ j(not_equal, &check_undefined_arg2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002716 // Get the untagged integer version of the eax heap number in ecx.
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002717
2718 if (right_type == BinaryOpIC::INT32 && CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00002719 CpuFeatureScope use_sse2(masm, SSE2);
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002720 ConvertHeapNumberToInt32(masm, eax, conversion_failure);
2721 } else {
2722 IntegerConvert(masm, eax, use_sse3, conversion_failure);
2723 }
2724
ricow@chromium.org65fae842010-08-25 15:26:24 +00002725 __ bind(&done);
2726 __ mov(eax, edx);
2727}
2728
2729
ricow@chromium.org65fae842010-08-25 15:26:24 +00002730void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm,
2731 Register number) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002732 Label load_smi, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002733
whesse@chromium.org7b260152011-06-20 15:33:18 +00002734 __ JumpIfSmi(number, &load_smi, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002735 __ fld_d(FieldOperand(number, HeapNumber::kValueOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002736 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002737
2738 __ bind(&load_smi);
2739 __ SmiUntag(number);
2740 __ push(number);
2741 __ fild_s(Operand(esp, 0));
2742 __ pop(number);
2743
2744 __ bind(&done);
2745}
2746
2747
2748void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002749 Label load_smi_edx, load_eax, load_smi_eax, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002750 // Load operand in edx into xmm0.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002751 __ JumpIfSmi(edx, &load_smi_edx, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002752 __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
2753
2754 __ bind(&load_eax);
2755 // Load operand in eax into xmm1.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002756 __ JumpIfSmi(eax, &load_smi_eax, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002757 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002758 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002759
2760 __ bind(&load_smi_edx);
2761 __ SmiUntag(edx); // Untag smi before converting to float.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002762 __ cvtsi2sd(xmm0, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002763 __ SmiTag(edx); // Retag smi for heap number overwriting test.
2764 __ jmp(&load_eax);
2765
2766 __ bind(&load_smi_eax);
2767 __ SmiUntag(eax); // Untag smi before converting to float.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002768 __ cvtsi2sd(xmm1, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002769 __ SmiTag(eax); // Retag smi for heap number overwriting test.
2770
2771 __ bind(&done);
2772}
2773
2774
2775void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm,
2776 Label* not_numbers) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002777 Label load_smi_edx, load_eax, load_smi_eax, load_float_eax, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002778 // Load operand in edx into xmm0, or branch to not_numbers.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002779 __ JumpIfSmi(edx, &load_smi_edx, Label::kNear);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002780 Factory* factory = masm->isolate()->factory();
2781 __ cmp(FieldOperand(edx, HeapObject::kMapOffset), factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002782 __ j(not_equal, not_numbers); // Argument in edx is not a number.
2783 __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
2784 __ bind(&load_eax);
2785 // Load operand in eax into xmm1, or branch to not_numbers.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002786 __ JumpIfSmi(eax, &load_smi_eax, Label::kNear);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002787 __ cmp(FieldOperand(eax, HeapObject::kMapOffset), factory->heap_number_map());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002788 __ j(equal, &load_float_eax, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002789 __ jmp(not_numbers); // Argument in eax is not a number.
2790 __ bind(&load_smi_edx);
2791 __ SmiUntag(edx); // Untag smi before converting to float.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002792 __ cvtsi2sd(xmm0, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002793 __ SmiTag(edx); // Retag smi for heap number overwriting test.
2794 __ jmp(&load_eax);
2795 __ bind(&load_smi_eax);
2796 __ SmiUntag(eax); // Untag smi before converting to float.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002797 __ cvtsi2sd(xmm1, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002798 __ SmiTag(eax); // Retag smi for heap number overwriting test.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002799 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002800 __ bind(&load_float_eax);
2801 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
2802 __ bind(&done);
2803}
2804
2805
2806void FloatingPointHelper::LoadSSE2Smis(MacroAssembler* masm,
2807 Register scratch) {
2808 const Register left = edx;
2809 const Register right = eax;
2810 __ mov(scratch, left);
2811 ASSERT(!scratch.is(right)); // We're about to clobber scratch.
2812 __ SmiUntag(scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002813 __ cvtsi2sd(xmm0, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002814
2815 __ mov(scratch, right);
2816 __ SmiUntag(scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002817 __ cvtsi2sd(xmm1, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002818}
2819
2820
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002821void FloatingPointHelper::CheckSSE2OperandIsInt32(MacroAssembler* masm,
2822 Label* non_int32,
2823 XMMRegister operand,
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002824 Register int32_result,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002825 Register scratch,
2826 XMMRegister xmm_scratch) {
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002827 __ cvttsd2si(int32_result, Operand(operand));
2828 __ cvtsi2sd(xmm_scratch, int32_result);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002829 __ pcmpeqd(xmm_scratch, operand);
ulan@chromium.org4121f232012-12-27 15:57:11 +00002830 __ movmskps(scratch, xmm_scratch);
2831 // Two least significant bits should be both set.
2832 __ not_(scratch);
2833 __ test(scratch, Immediate(3));
2834 __ j(not_zero, non_int32);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002835}
2836
2837
ricow@chromium.org65fae842010-08-25 15:26:24 +00002838void FloatingPointHelper::LoadFloatOperands(MacroAssembler* masm,
2839 Register scratch,
2840 ArgLocation arg_location) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002841 Label load_smi_1, load_smi_2, done_load_1, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002842 if (arg_location == ARGS_IN_REGISTERS) {
2843 __ mov(scratch, edx);
2844 } else {
2845 __ mov(scratch, Operand(esp, 2 * kPointerSize));
2846 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00002847 __ JumpIfSmi(scratch, &load_smi_1, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002848 __ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset));
2849 __ bind(&done_load_1);
2850
2851 if (arg_location == ARGS_IN_REGISTERS) {
2852 __ mov(scratch, eax);
2853 } else {
2854 __ mov(scratch, Operand(esp, 1 * kPointerSize));
2855 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00002856 __ JumpIfSmi(scratch, &load_smi_2, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002857 __ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002858 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002859
2860 __ bind(&load_smi_1);
2861 __ SmiUntag(scratch);
2862 __ push(scratch);
2863 __ fild_s(Operand(esp, 0));
2864 __ pop(scratch);
2865 __ jmp(&done_load_1);
2866
2867 __ bind(&load_smi_2);
2868 __ SmiUntag(scratch);
2869 __ push(scratch);
2870 __ fild_s(Operand(esp, 0));
2871 __ pop(scratch);
2872
2873 __ bind(&done);
2874}
2875
2876
2877void FloatingPointHelper::LoadFloatSmis(MacroAssembler* masm,
2878 Register scratch) {
2879 const Register left = edx;
2880 const Register right = eax;
2881 __ mov(scratch, left);
2882 ASSERT(!scratch.is(right)); // We're about to clobber scratch.
2883 __ SmiUntag(scratch);
2884 __ push(scratch);
2885 __ fild_s(Operand(esp, 0));
2886
2887 __ mov(scratch, right);
2888 __ SmiUntag(scratch);
2889 __ mov(Operand(esp, 0), scratch);
2890 __ fild_s(Operand(esp, 0));
2891 __ pop(scratch);
2892}
2893
2894
2895void FloatingPointHelper::CheckFloatOperands(MacroAssembler* masm,
2896 Label* non_float,
2897 Register scratch) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002898 Label test_other, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002899 // Test if both operands are floats or smi -> scratch=k_is_float;
2900 // Otherwise scratch = k_not_float.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002901 __ JumpIfSmi(edx, &test_other, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002902 __ mov(scratch, FieldOperand(edx, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002903 Factory* factory = masm->isolate()->factory();
2904 __ cmp(scratch, factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002905 __ j(not_equal, non_float); // argument in edx is not a number -> NaN
2906
2907 __ bind(&test_other);
whesse@chromium.org7b260152011-06-20 15:33:18 +00002908 __ JumpIfSmi(eax, &done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002909 __ mov(scratch, FieldOperand(eax, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002910 __ cmp(scratch, factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002911 __ j(not_equal, non_float); // argument in eax is not a number -> NaN
2912
2913 // Fall-through: Both operands are numbers.
2914 __ bind(&done);
2915}
2916
2917
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002918void MathPowStub::Generate(MacroAssembler* masm) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00002919 CpuFeatureScope use_sse2(masm, SSE2);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002920 Factory* factory = masm->isolate()->factory();
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002921 const Register exponent = eax;
2922 const Register base = edx;
2923 const Register scratch = ecx;
2924 const XMMRegister double_result = xmm3;
2925 const XMMRegister double_base = xmm2;
2926 const XMMRegister double_exponent = xmm1;
2927 const XMMRegister double_scratch = xmm4;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002928
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002929 Label call_runtime, done, exponent_not_smi, int_exponent;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002930
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002931 // Save 1 in double_result - we need this several times later on.
2932 __ mov(scratch, Immediate(1));
2933 __ cvtsi2sd(double_result, scratch);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002934
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002935 if (exponent_type_ == ON_STACK) {
2936 Label base_is_smi, unpack_exponent;
2937 // The exponent and base are supplied as arguments on the stack.
2938 // This can only happen if the stub is called from non-optimized code.
2939 // Load input parameters from stack.
2940 __ mov(base, Operand(esp, 2 * kPointerSize));
2941 __ mov(exponent, Operand(esp, 1 * kPointerSize));
2942
2943 __ JumpIfSmi(base, &base_is_smi, Label::kNear);
2944 __ cmp(FieldOperand(base, HeapObject::kMapOffset),
2945 factory->heap_number_map());
2946 __ j(not_equal, &call_runtime);
2947
2948 __ movdbl(double_base, FieldOperand(base, HeapNumber::kValueOffset));
2949 __ jmp(&unpack_exponent, Label::kNear);
2950
2951 __ bind(&base_is_smi);
2952 __ SmiUntag(base);
2953 __ cvtsi2sd(double_base, base);
2954
2955 __ bind(&unpack_exponent);
2956 __ JumpIfNotSmi(exponent, &exponent_not_smi, Label::kNear);
2957 __ SmiUntag(exponent);
2958 __ jmp(&int_exponent);
2959
2960 __ bind(&exponent_not_smi);
2961 __ cmp(FieldOperand(exponent, HeapObject::kMapOffset),
2962 factory->heap_number_map());
2963 __ j(not_equal, &call_runtime);
2964 __ movdbl(double_exponent,
2965 FieldOperand(exponent, HeapNumber::kValueOffset));
2966 } else if (exponent_type_ == TAGGED) {
2967 __ JumpIfNotSmi(exponent, &exponent_not_smi, Label::kNear);
2968 __ SmiUntag(exponent);
2969 __ jmp(&int_exponent);
2970
2971 __ bind(&exponent_not_smi);
2972 __ movdbl(double_exponent,
2973 FieldOperand(exponent, HeapNumber::kValueOffset));
2974 }
2975
2976 if (exponent_type_ != INTEGER) {
2977 Label fast_power;
2978 // Detect integer exponents stored as double.
2979 __ cvttsd2si(exponent, Operand(double_exponent));
2980 // Skip to runtime if possibly NaN (indicated by the indefinite integer).
2981 __ cmp(exponent, Immediate(0x80000000u));
2982 __ j(equal, &call_runtime);
2983 __ cvtsi2sd(double_scratch, exponent);
2984 // Already ruled out NaNs for exponent.
2985 __ ucomisd(double_exponent, double_scratch);
2986 __ j(equal, &int_exponent);
2987
2988 if (exponent_type_ == ON_STACK) {
2989 // Detect square root case. Crankshaft detects constant +/-0.5 at
2990 // compile time and uses DoMathPowHalf instead. We then skip this check
2991 // for non-constant cases of +/-0.5 as these hardly occur.
2992 Label continue_sqrt, continue_rsqrt, not_plus_half;
2993 // Test for 0.5.
2994 // Load double_scratch with 0.5.
2995 __ mov(scratch, Immediate(0x3F000000u));
2996 __ movd(double_scratch, scratch);
2997 __ cvtss2sd(double_scratch, double_scratch);
2998 // Already ruled out NaNs for exponent.
2999 __ ucomisd(double_scratch, double_exponent);
3000 __ j(not_equal, &not_plus_half, Label::kNear);
3001
3002 // Calculates square root of base. Check for the special case of
3003 // Math.pow(-Infinity, 0.5) == Infinity (ECMA spec, 15.8.2.13).
3004 // According to IEEE-754, single-precision -Infinity has the highest
3005 // 9 bits set and the lowest 23 bits cleared.
3006 __ mov(scratch, 0xFF800000u);
3007 __ movd(double_scratch, scratch);
3008 __ cvtss2sd(double_scratch, double_scratch);
3009 __ ucomisd(double_base, double_scratch);
3010 // Comparing -Infinity with NaN results in "unordered", which sets the
3011 // zero flag as if both were equal. However, it also sets the carry flag.
3012 __ j(not_equal, &continue_sqrt, Label::kNear);
3013 __ j(carry, &continue_sqrt, Label::kNear);
3014
3015 // Set result to Infinity in the special case.
3016 __ xorps(double_result, double_result);
3017 __ subsd(double_result, double_scratch);
3018 __ jmp(&done);
3019
3020 __ bind(&continue_sqrt);
3021 // sqrtsd returns -0 when input is -0. ECMA spec requires +0.
3022 __ xorps(double_scratch, double_scratch);
3023 __ addsd(double_scratch, double_base); // Convert -0 to +0.
3024 __ sqrtsd(double_result, double_scratch);
3025 __ jmp(&done);
3026
3027 // Test for -0.5.
3028 __ bind(&not_plus_half);
3029 // Load double_exponent with -0.5 by substracting 1.
3030 __ subsd(double_scratch, double_result);
3031 // Already ruled out NaNs for exponent.
3032 __ ucomisd(double_scratch, double_exponent);
3033 __ j(not_equal, &fast_power, Label::kNear);
3034
3035 // Calculates reciprocal of square root of base. Check for the special
3036 // case of Math.pow(-Infinity, -0.5) == 0 (ECMA spec, 15.8.2.13).
3037 // According to IEEE-754, single-precision -Infinity has the highest
3038 // 9 bits set and the lowest 23 bits cleared.
3039 __ mov(scratch, 0xFF800000u);
3040 __ movd(double_scratch, scratch);
3041 __ cvtss2sd(double_scratch, double_scratch);
3042 __ ucomisd(double_base, double_scratch);
3043 // Comparing -Infinity with NaN results in "unordered", which sets the
3044 // zero flag as if both were equal. However, it also sets the carry flag.
3045 __ j(not_equal, &continue_rsqrt, Label::kNear);
3046 __ j(carry, &continue_rsqrt, Label::kNear);
3047
3048 // Set result to 0 in the special case.
3049 __ xorps(double_result, double_result);
3050 __ jmp(&done);
3051
3052 __ bind(&continue_rsqrt);
3053 // sqrtsd returns -0 when input is -0. ECMA spec requires +0.
3054 __ xorps(double_exponent, double_exponent);
3055 __ addsd(double_exponent, double_base); // Convert -0 to +0.
3056 __ sqrtsd(double_exponent, double_exponent);
3057 __ divsd(double_result, double_exponent);
3058 __ jmp(&done);
3059 }
3060
3061 // Using FPU instructions to calculate power.
3062 Label fast_power_failed;
3063 __ bind(&fast_power);
3064 __ fnclex(); // Clear flags to catch exceptions later.
3065 // Transfer (B)ase and (E)xponent onto the FPU register stack.
3066 __ sub(esp, Immediate(kDoubleSize));
3067 __ movdbl(Operand(esp, 0), double_exponent);
3068 __ fld_d(Operand(esp, 0)); // E
3069 __ movdbl(Operand(esp, 0), double_base);
3070 __ fld_d(Operand(esp, 0)); // B, E
3071
3072 // Exponent is in st(1) and base is in st(0)
3073 // B ^ E = (2^(E * log2(B)) - 1) + 1 = (2^X - 1) + 1 for X = E * log2(B)
3074 // FYL2X calculates st(1) * log2(st(0))
3075 __ fyl2x(); // X
3076 __ fld(0); // X, X
3077 __ frndint(); // rnd(X), X
3078 __ fsub(1); // rnd(X), X-rnd(X)
3079 __ fxch(1); // X - rnd(X), rnd(X)
3080 // F2XM1 calculates 2^st(0) - 1 for -1 < st(0) < 1
3081 __ f2xm1(); // 2^(X-rnd(X)) - 1, rnd(X)
3082 __ fld1(); // 1, 2^(X-rnd(X)) - 1, rnd(X)
danno@chromium.org1f34ad32012-11-26 14:53:56 +00003083 __ faddp(1); // 2^(X-rnd(X)), rnd(X)
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003084 // FSCALE calculates st(0) * 2^st(1)
3085 __ fscale(); // 2^X, rnd(X)
danno@chromium.org1f34ad32012-11-26 14:53:56 +00003086 __ fstp(1); // 2^X
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003087 // Bail out to runtime in case of exceptions in the status word.
3088 __ fnstsw_ax();
3089 __ test_b(eax, 0x5F); // We check for all but precision exception.
3090 __ j(not_zero, &fast_power_failed, Label::kNear);
3091 __ fstp_d(Operand(esp, 0));
3092 __ movdbl(double_result, Operand(esp, 0));
3093 __ add(esp, Immediate(kDoubleSize));
3094 __ jmp(&done);
3095
3096 __ bind(&fast_power_failed);
3097 __ fninit();
3098 __ add(esp, Immediate(kDoubleSize));
3099 __ jmp(&call_runtime);
3100 }
3101
3102 // Calculate power with integer exponent.
3103 __ bind(&int_exponent);
3104 const XMMRegister double_scratch2 = double_exponent;
3105 __ mov(scratch, exponent); // Back up exponent.
3106 __ movsd(double_scratch, double_base); // Back up base.
3107 __ movsd(double_scratch2, double_result); // Load double_exponent with 1.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003108
3109 // Get absolute value of exponent.
verwaest@chromium.org33e09c82012-10-10 17:07:22 +00003110 Label no_neg, while_true, while_false;
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003111 __ test(scratch, scratch);
3112 __ j(positive, &no_neg, Label::kNear);
3113 __ neg(scratch);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003114 __ bind(&no_neg);
3115
ulan@chromium.org56c14af2012-09-20 12:51:09 +00003116 __ j(zero, &while_false, Label::kNear);
3117 __ shr(scratch, 1);
3118 // Above condition means CF==0 && ZF==0. This means that the
3119 // bit that has been shifted out is 0 and the result is not 0.
3120 __ j(above, &while_true, Label::kNear);
3121 __ movsd(double_result, double_scratch);
3122 __ j(zero, &while_false, Label::kNear);
3123
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003124 __ bind(&while_true);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003125 __ shr(scratch, 1);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003126 __ mulsd(double_scratch, double_scratch);
ulan@chromium.org56c14af2012-09-20 12:51:09 +00003127 __ j(above, &while_true, Label::kNear);
3128 __ mulsd(double_result, double_scratch);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003129 __ j(not_zero, &while_true);
3130
ulan@chromium.org56c14af2012-09-20 12:51:09 +00003131 __ bind(&while_false);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003132 // scratch has the original value of the exponent - if the exponent is
3133 // negative, return 1/result.
3134 __ test(exponent, exponent);
3135 __ j(positive, &done);
3136 __ divsd(double_scratch2, double_result);
3137 __ movsd(double_result, double_scratch2);
3138 // Test whether result is zero. Bail out to check for subnormal result.
3139 // Due to subnormals, x^-y == (1/x)^y does not hold in all cases.
3140 __ xorps(double_scratch2, double_scratch2);
3141 __ ucomisd(double_scratch2, double_result); // Result cannot be NaN.
3142 // double_exponent aliased as double_scratch2 has already been overwritten
3143 // and may not have contained the exponent value in the first place when the
3144 // exponent is a smi. We reset it with exponent value before bailing out.
3145 __ j(not_equal, &done);
3146 __ cvtsi2sd(double_exponent, exponent);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003147
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003148 // Returning or bailing out.
3149 Counters* counters = masm->isolate()->counters();
3150 if (exponent_type_ == ON_STACK) {
3151 // The arguments are still on the stack.
3152 __ bind(&call_runtime);
3153 __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003154
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003155 // The stub is called from non-optimized code, which expects the result
3156 // as heap number in exponent.
3157 __ bind(&done);
3158 __ AllocateHeapNumber(eax, scratch, base, &call_runtime);
3159 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), double_result);
3160 __ IncrementCounter(counters->math_pow(), 1);
3161 __ ret(2 * kPointerSize);
3162 } else {
3163 __ bind(&call_runtime);
3164 {
3165 AllowExternalCallThatCantCauseGC scope(masm);
3166 __ PrepareCallCFunction(4, scratch);
3167 __ movdbl(Operand(esp, 0 * kDoubleSize), double_base);
3168 __ movdbl(Operand(esp, 1 * kDoubleSize), double_exponent);
3169 __ CallCFunction(
3170 ExternalReference::power_double_double_function(masm->isolate()), 4);
3171 }
3172 // Return value is in st(0) on ia32.
3173 // Store it into the (fixed) result register.
3174 __ sub(esp, Immediate(kDoubleSize));
3175 __ fstp_d(Operand(esp, 0));
3176 __ movdbl(double_result, Operand(esp, 0));
3177 __ add(esp, Immediate(kDoubleSize));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003178
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003179 __ bind(&done);
3180 __ IncrementCounter(counters->math_pow(), 1);
3181 __ ret(0);
3182 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003183}
3184
3185
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00003186void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
3187 // ----------- S t a t e -------------
3188 // -- ecx : name
3189 // -- edx : receiver
3190 // -- esp[0] : return address
3191 // -----------------------------------
3192 Label miss;
3193
3194 if (kind() == Code::KEYED_LOAD_IC) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00003195 __ cmp(ecx, Immediate(masm->isolate()->factory()->prototype_string()));
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00003196 __ j(not_equal, &miss);
3197 }
3198
3199 StubCompiler::GenerateLoadFunctionPrototype(masm, edx, eax, ebx, &miss);
3200 __ bind(&miss);
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +00003201 StubCompiler::TailCallBuiltin(masm, StubCompiler::MissBuiltin(kind()));
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00003202}
3203
3204
3205void StringLengthStub::Generate(MacroAssembler* masm) {
3206 // ----------- S t a t e -------------
3207 // -- ecx : name
3208 // -- edx : receiver
3209 // -- esp[0] : return address
3210 // -----------------------------------
3211 Label miss;
3212
3213 if (kind() == Code::KEYED_LOAD_IC) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00003214 __ cmp(ecx, Immediate(masm->isolate()->factory()->length_string()));
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00003215 __ j(not_equal, &miss);
3216 }
3217
3218 StubCompiler::GenerateLoadStringLength(masm, edx, eax, ebx, &miss,
3219 support_wrapper_);
3220 __ bind(&miss);
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +00003221 StubCompiler::TailCallBuiltin(masm, StubCompiler::MissBuiltin(kind()));
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00003222}
3223
3224
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00003225void StoreArrayLengthStub::Generate(MacroAssembler* masm) {
3226 // ----------- S t a t e -------------
3227 // -- eax : value
3228 // -- ecx : name
3229 // -- edx : receiver
3230 // -- esp[0] : return address
3231 // -----------------------------------
3232 //
3233 // This accepts as a receiver anything JSArray::SetElementsLength accepts
3234 // (currently anything except for external arrays which means anything with
3235 // elements of FixedArray type). Value must be a number, but only smis are
3236 // accepted as the most common case.
3237
3238 Label miss;
3239
3240 Register receiver = edx;
3241 Register value = eax;
3242 Register scratch = ebx;
3243
ulan@chromium.org750145a2013-03-07 15:14:13 +00003244 if (kind() == Code::KEYED_STORE_IC) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00003245 __ cmp(ecx, Immediate(masm->isolate()->factory()->length_string()));
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00003246 __ j(not_equal, &miss);
3247 }
3248
3249 // Check that the receiver isn't a smi.
3250 __ JumpIfSmi(receiver, &miss);
3251
3252 // Check that the object is a JS array.
3253 __ CmpObjectType(receiver, JS_ARRAY_TYPE, scratch);
3254 __ j(not_equal, &miss);
3255
3256 // Check that elements are FixedArray.
3257 // We rely on StoreIC_ArrayLength below to deal with all types of
3258 // fast elements (including COW).
3259 __ mov(scratch, FieldOperand(receiver, JSArray::kElementsOffset));
3260 __ CmpObjectType(scratch, FIXED_ARRAY_TYPE, scratch);
3261 __ j(not_equal, &miss);
3262
3263 // Check that the array has fast properties, otherwise the length
3264 // property might have been redefined.
3265 __ mov(scratch, FieldOperand(receiver, JSArray::kPropertiesOffset));
3266 __ CompareRoot(FieldOperand(scratch, FixedArray::kMapOffset),
3267 Heap::kHashTableMapRootIndex);
3268 __ j(equal, &miss);
3269
3270 // Check that value is a smi.
3271 __ JumpIfNotSmi(value, &miss);
3272
3273 // Prepare tail call to StoreIC_ArrayLength.
3274 __ pop(scratch);
3275 __ push(receiver);
3276 __ push(value);
3277 __ push(scratch); // return address
3278
3279 ExternalReference ref =
3280 ExternalReference(IC_Utility(IC::kStoreIC_ArrayLength), masm->isolate());
3281 __ TailCallExternalReference(ref, 2, 1);
3282
3283 __ bind(&miss);
3284
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +00003285 StubCompiler::TailCallBuiltin(masm, StubCompiler::MissBuiltin(kind()));
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00003286}
3287
3288
ricow@chromium.org65fae842010-08-25 15:26:24 +00003289void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
3290 // The key is in edx and the parameter count is in eax.
3291
3292 // The displacement is used for skipping the frame pointer on the
3293 // stack. It is the offset of the last parameter (if any) relative
3294 // to the frame pointer.
3295 static const int kDisplacement = 1 * kPointerSize;
3296
3297 // Check that the key is a smi.
3298 Label slow;
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003299 __ JumpIfNotSmi(edx, &slow, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003300
3301 // Check if the calling frame is an arguments adaptor frame.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003302 Label adaptor;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003303 __ mov(ebx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
3304 __ mov(ecx, Operand(ebx, StandardFrameConstants::kContextOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003305 __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003306 __ j(equal, &adaptor, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003307
3308 // Check index against formal parameters count limit passed in
3309 // through register eax. Use unsigned comparison to get negative
3310 // check for free.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003311 __ cmp(edx, eax);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003312 __ j(above_equal, &slow, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003313
3314 // Read the argument from the stack and return it.
3315 STATIC_ASSERT(kSmiTagSize == 1);
3316 STATIC_ASSERT(kSmiTag == 0); // Shifting code depends on these.
3317 __ lea(ebx, Operand(ebp, eax, times_2, 0));
3318 __ neg(edx);
3319 __ mov(eax, Operand(ebx, edx, times_2, kDisplacement));
3320 __ ret(0);
3321
3322 // Arguments adaptor case: Check index against actual arguments
3323 // limit found in the arguments adaptor frame. Use unsigned
3324 // comparison to get negative check for free.
3325 __ bind(&adaptor);
3326 __ mov(ecx, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003327 __ cmp(edx, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003328 __ j(above_equal, &slow, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003329
3330 // Read the argument from the stack and return it.
3331 STATIC_ASSERT(kSmiTagSize == 1);
3332 STATIC_ASSERT(kSmiTag == 0); // Shifting code depends on these.
3333 __ lea(ebx, Operand(ebx, ecx, times_2, 0));
3334 __ neg(edx);
3335 __ mov(eax, Operand(ebx, edx, times_2, kDisplacement));
3336 __ ret(0);
3337
3338 // Slow-case: Handle non-smi or out-of-bounds access to arguments
3339 // by calling the runtime system.
3340 __ bind(&slow);
3341 __ pop(ebx); // Return address.
3342 __ push(edx);
3343 __ push(ebx);
3344 __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
3345}
3346
3347
whesse@chromium.org7b260152011-06-20 15:33:18 +00003348void ArgumentsAccessStub::GenerateNewNonStrictSlow(MacroAssembler* masm) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00003349 // esp[0] : return address
3350 // esp[4] : number of parameters
3351 // esp[8] : receiver displacement
whesse@chromium.org7b260152011-06-20 15:33:18 +00003352 // esp[12] : function
ricow@chromium.org65fae842010-08-25 15:26:24 +00003353
whesse@chromium.org7b260152011-06-20 15:33:18 +00003354 // Check if the calling frame is an arguments adaptor frame.
3355 Label runtime;
3356 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
3357 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003358 __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003359 __ j(not_equal, &runtime, Label::kNear);
3360
3361 // Patch the arguments.length and the parameters pointer.
3362 __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
3363 __ mov(Operand(esp, 1 * kPointerSize), ecx);
3364 __ lea(edx, Operand(edx, ecx, times_2,
3365 StandardFrameConstants::kCallerSPOffset));
3366 __ mov(Operand(esp, 2 * kPointerSize), edx);
3367
3368 __ bind(&runtime);
3369 __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
3370}
3371
3372
3373void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) {
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00003374 Isolate* isolate = masm->isolate();
3375
whesse@chromium.org7b260152011-06-20 15:33:18 +00003376 // esp[0] : return address
3377 // esp[4] : number of parameters (tagged)
3378 // esp[8] : receiver displacement
3379 // esp[12] : function
3380
3381 // ebx = parameter count (tagged)
3382 __ mov(ebx, Operand(esp, 1 * kPointerSize));
3383
3384 // Check if the calling frame is an arguments adaptor frame.
3385 // TODO(rossberg): Factor out some of the bits that are shared with the other
3386 // Generate* functions.
3387 Label runtime;
3388 Label adaptor_frame, try_allocate;
3389 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
3390 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003391 __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003392 __ j(equal, &adaptor_frame, Label::kNear);
3393
3394 // No adaptor, parameter count = argument count.
3395 __ mov(ecx, ebx);
3396 __ jmp(&try_allocate, Label::kNear);
3397
3398 // We have an adaptor frame. Patch the parameters pointer.
3399 __ bind(&adaptor_frame);
3400 __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
3401 __ lea(edx, Operand(edx, ecx, times_2,
3402 StandardFrameConstants::kCallerSPOffset));
3403 __ mov(Operand(esp, 2 * kPointerSize), edx);
3404
3405 // ebx = parameter count (tagged)
3406 // ecx = argument count (tagged)
3407 // esp[4] = parameter count (tagged)
3408 // esp[8] = address of receiver argument
3409 // Compute the mapped parameter count = min(ebx, ecx) in ebx.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003410 __ cmp(ebx, ecx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003411 __ j(less_equal, &try_allocate, Label::kNear);
3412 __ mov(ebx, ecx);
3413
3414 __ bind(&try_allocate);
3415
3416 // Save mapped parameter count.
3417 __ push(ebx);
3418
3419 // Compute the sizes of backing store, parameter map, and arguments object.
3420 // 1. Parameter map, has 2 extra words containing context and backing store.
3421 const int kParameterMapHeaderSize =
3422 FixedArray::kHeaderSize + 2 * kPointerSize;
3423 Label no_parameter_map;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003424 __ test(ebx, ebx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003425 __ j(zero, &no_parameter_map, Label::kNear);
3426 __ lea(ebx, Operand(ebx, times_2, kParameterMapHeaderSize));
3427 __ bind(&no_parameter_map);
3428
3429 // 2. Backing store.
3430 __ lea(ebx, Operand(ebx, ecx, times_2, FixedArray::kHeaderSize));
3431
3432 // 3. Arguments object.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003433 __ add(ebx, Immediate(Heap::kArgumentsObjectSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003434
3435 // Do the allocation of all three objects in one go.
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00003436 __ Allocate(ebx, eax, edx, edi, &runtime, TAG_OBJECT);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003437
3438 // eax = address of new object(s) (tagged)
3439 // ecx = argument count (tagged)
3440 // esp[0] = mapped parameter count (tagged)
3441 // esp[8] = parameter count (tagged)
3442 // esp[12] = address of receiver argument
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00003443 // Get the arguments boilerplate from the current native context into edi.
whesse@chromium.org7b260152011-06-20 15:33:18 +00003444 Label has_mapped_parameters, copy;
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00003445 __ mov(edi, Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
3446 __ mov(edi, FieldOperand(edi, GlobalObject::kNativeContextOffset));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003447 __ mov(ebx, Operand(esp, 0 * kPointerSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003448 __ test(ebx, ebx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003449 __ j(not_zero, &has_mapped_parameters, Label::kNear);
3450 __ mov(edi, Operand(edi,
3451 Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX)));
3452 __ jmp(&copy, Label::kNear);
3453
3454 __ bind(&has_mapped_parameters);
3455 __ mov(edi, Operand(edi,
3456 Context::SlotOffset(Context::ALIASED_ARGUMENTS_BOILERPLATE_INDEX)));
3457 __ bind(&copy);
3458
3459 // eax = address of new object (tagged)
3460 // ebx = mapped parameter count (tagged)
3461 // ecx = argument count (tagged)
3462 // edi = address of boilerplate object (tagged)
3463 // esp[0] = mapped parameter count (tagged)
3464 // esp[8] = parameter count (tagged)
3465 // esp[12] = address of receiver argument
3466 // Copy the JS object part.
3467 for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
3468 __ mov(edx, FieldOperand(edi, i));
3469 __ mov(FieldOperand(eax, i), edx);
3470 }
3471
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00003472 // Set up the callee in-object property.
whesse@chromium.org7b260152011-06-20 15:33:18 +00003473 STATIC_ASSERT(Heap::kArgumentsCalleeIndex == 1);
3474 __ mov(edx, Operand(esp, 4 * kPointerSize));
3475 __ mov(FieldOperand(eax, JSObject::kHeaderSize +
3476 Heap::kArgumentsCalleeIndex * kPointerSize),
3477 edx);
3478
3479 // Use the length (smi tagged) and set that as an in-object property too.
3480 STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0);
3481 __ mov(FieldOperand(eax, JSObject::kHeaderSize +
3482 Heap::kArgumentsLengthIndex * kPointerSize),
3483 ecx);
3484
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00003485 // Set up the elements pointer in the allocated arguments object.
whesse@chromium.org7b260152011-06-20 15:33:18 +00003486 // If we allocated a parameter map, edi will point there, otherwise to the
3487 // backing store.
3488 __ lea(edi, Operand(eax, Heap::kArgumentsObjectSize));
3489 __ mov(FieldOperand(eax, JSObject::kElementsOffset), edi);
3490
3491 // eax = address of new object (tagged)
3492 // ebx = mapped parameter count (tagged)
3493 // ecx = argument count (tagged)
3494 // edi = address of parameter map or backing store (tagged)
3495 // esp[0] = mapped parameter count (tagged)
3496 // esp[8] = parameter count (tagged)
3497 // esp[12] = address of receiver argument
3498 // Free a register.
3499 __ push(eax);
3500
3501 // Initialize parameter map. If there are no mapped arguments, we're done.
3502 Label skip_parameter_map;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003503 __ test(ebx, ebx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003504 __ j(zero, &skip_parameter_map);
3505
3506 __ mov(FieldOperand(edi, FixedArray::kMapOffset),
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00003507 Immediate(isolate->factory()->non_strict_arguments_elements_map()));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003508 __ lea(eax, Operand(ebx, reinterpret_cast<intptr_t>(Smi::FromInt(2))));
3509 __ mov(FieldOperand(edi, FixedArray::kLengthOffset), eax);
3510 __ mov(FieldOperand(edi, FixedArray::kHeaderSize + 0 * kPointerSize), esi);
3511 __ lea(eax, Operand(edi, ebx, times_2, kParameterMapHeaderSize));
3512 __ mov(FieldOperand(edi, FixedArray::kHeaderSize + 1 * kPointerSize), eax);
3513
3514 // Copy the parameter slots and the holes in the arguments.
3515 // We need to fill in mapped_parameter_count slots. They index the context,
3516 // where parameters are stored in reverse order, at
3517 // MIN_CONTEXT_SLOTS .. MIN_CONTEXT_SLOTS+parameter_count-1
3518 // The mapped parameter thus need to get indices
3519 // MIN_CONTEXT_SLOTS+parameter_count-1 ..
3520 // MIN_CONTEXT_SLOTS+parameter_count-mapped_parameter_count
3521 // We loop from right to left.
3522 Label parameters_loop, parameters_test;
3523 __ push(ecx);
3524 __ mov(eax, Operand(esp, 2 * kPointerSize));
3525 __ mov(ebx, Immediate(Smi::FromInt(Context::MIN_CONTEXT_SLOTS)));
3526 __ add(ebx, Operand(esp, 4 * kPointerSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003527 __ sub(ebx, eax);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00003528 __ mov(ecx, isolate->factory()->the_hole_value());
whesse@chromium.org7b260152011-06-20 15:33:18 +00003529 __ mov(edx, edi);
3530 __ lea(edi, Operand(edi, eax, times_2, kParameterMapHeaderSize));
3531 // eax = loop variable (tagged)
3532 // ebx = mapping index (tagged)
3533 // ecx = the hole value
3534 // edx = address of parameter map (tagged)
3535 // edi = address of backing store (tagged)
3536 // esp[0] = argument count (tagged)
3537 // esp[4] = address of new object (tagged)
3538 // esp[8] = mapped parameter count (tagged)
3539 // esp[16] = parameter count (tagged)
3540 // esp[20] = address of receiver argument
3541 __ jmp(&parameters_test, Label::kNear);
3542
3543 __ bind(&parameters_loop);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003544 __ sub(eax, Immediate(Smi::FromInt(1)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003545 __ mov(FieldOperand(edx, eax, times_2, kParameterMapHeaderSize), ebx);
3546 __ mov(FieldOperand(edi, eax, times_2, FixedArray::kHeaderSize), ecx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003547 __ add(ebx, Immediate(Smi::FromInt(1)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003548 __ bind(&parameters_test);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003549 __ test(eax, eax);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003550 __ j(not_zero, &parameters_loop, Label::kNear);
3551 __ pop(ecx);
3552
3553 __ bind(&skip_parameter_map);
3554
3555 // ecx = argument count (tagged)
3556 // edi = address of backing store (tagged)
3557 // esp[0] = address of new object (tagged)
3558 // esp[4] = mapped parameter count (tagged)
3559 // esp[12] = parameter count (tagged)
3560 // esp[16] = address of receiver argument
3561 // Copy arguments header and remaining slots (if there are any).
3562 __ mov(FieldOperand(edi, FixedArray::kMapOffset),
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00003563 Immediate(isolate->factory()->fixed_array_map()));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003564 __ mov(FieldOperand(edi, FixedArray::kLengthOffset), ecx);
3565
3566 Label arguments_loop, arguments_test;
3567 __ mov(ebx, Operand(esp, 1 * kPointerSize));
3568 __ mov(edx, Operand(esp, 4 * kPointerSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003569 __ sub(edx, ebx); // Is there a smarter way to do negative scaling?
3570 __ sub(edx, ebx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003571 __ jmp(&arguments_test, Label::kNear);
3572
3573 __ bind(&arguments_loop);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003574 __ sub(edx, Immediate(kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003575 __ mov(eax, Operand(edx, 0));
3576 __ mov(FieldOperand(edi, ebx, times_2, FixedArray::kHeaderSize), eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003577 __ add(ebx, Immediate(Smi::FromInt(1)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003578
3579 __ bind(&arguments_test);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003580 __ cmp(ebx, ecx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003581 __ j(less, &arguments_loop, Label::kNear);
3582
3583 // Restore.
3584 __ pop(eax); // Address of arguments object.
3585 __ pop(ebx); // Parameter count.
3586
3587 // Return and remove the on-stack parameters.
3588 __ ret(3 * kPointerSize);
3589
3590 // Do the runtime call to allocate the arguments object.
3591 __ bind(&runtime);
3592 __ pop(eax); // Remove saved parameter count.
3593 __ mov(Operand(esp, 1 * kPointerSize), ecx); // Patch argument count.
danno@chromium.org72204d52012-10-31 10:02:10 +00003594 __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003595}
3596
3597
3598void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) {
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00003599 Isolate* isolate = masm->isolate();
3600
whesse@chromium.org7b260152011-06-20 15:33:18 +00003601 // esp[0] : return address
3602 // esp[4] : number of parameters
3603 // esp[8] : receiver displacement
3604 // esp[12] : function
ricow@chromium.org65fae842010-08-25 15:26:24 +00003605
3606 // Check if the calling frame is an arguments adaptor frame.
3607 Label adaptor_frame, try_allocate, runtime;
3608 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
3609 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003610 __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003611 __ j(equal, &adaptor_frame, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003612
3613 // Get the length from the frame.
3614 __ mov(ecx, Operand(esp, 1 * kPointerSize));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003615 __ jmp(&try_allocate, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003616
3617 // Patch the arguments.length and the parameters pointer.
3618 __ bind(&adaptor_frame);
3619 __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
3620 __ mov(Operand(esp, 1 * kPointerSize), ecx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003621 __ lea(edx, Operand(edx, ecx, times_2,
3622 StandardFrameConstants::kCallerSPOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003623 __ mov(Operand(esp, 2 * kPointerSize), edx);
3624
3625 // Try the new space allocation. Start out with computing the size of
3626 // the arguments object and the elements array.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003627 Label add_arguments_object;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003628 __ bind(&try_allocate);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003629 __ test(ecx, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003630 __ j(zero, &add_arguments_object, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003631 __ lea(ecx, Operand(ecx, times_2, FixedArray::kHeaderSize));
3632 __ bind(&add_arguments_object);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003633 __ add(ecx, Immediate(Heap::kArgumentsObjectSizeStrict));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003634
3635 // Do the allocation of both objects in one go.
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00003636 __ Allocate(ecx, eax, edx, ebx, &runtime, TAG_OBJECT);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003637
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00003638 // Get the arguments boilerplate from the current native context.
3639 __ mov(edi, Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
3640 __ mov(edi, FieldOperand(edi, GlobalObject::kNativeContextOffset));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003641 const int offset =
3642 Context::SlotOffset(Context::STRICT_MODE_ARGUMENTS_BOILERPLATE_INDEX);
3643 __ mov(edi, Operand(edi, offset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003644
3645 // Copy the JS object part.
3646 for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
3647 __ mov(ebx, FieldOperand(edi, i));
3648 __ mov(FieldOperand(eax, i), ebx);
3649 }
3650
ricow@chromium.org65fae842010-08-25 15:26:24 +00003651 // Get the length (smi tagged) and set that as an in-object property too.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003652 STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003653 __ mov(ecx, Operand(esp, 1 * kPointerSize));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003654 __ mov(FieldOperand(eax, JSObject::kHeaderSize +
whesse@chromium.org7b260152011-06-20 15:33:18 +00003655 Heap::kArgumentsLengthIndex * kPointerSize),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003656 ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003657
3658 // If there are no actual arguments, we're done.
3659 Label done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003660 __ test(ecx, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003661 __ j(zero, &done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003662
3663 // Get the parameters pointer from the stack.
3664 __ mov(edx, Operand(esp, 2 * kPointerSize));
3665
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00003666 // Set up the elements pointer in the allocated arguments object and
ricow@chromium.org65fae842010-08-25 15:26:24 +00003667 // initialize the header in the elements fixed array.
whesse@chromium.org7b260152011-06-20 15:33:18 +00003668 __ lea(edi, Operand(eax, Heap::kArgumentsObjectSizeStrict));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003669 __ mov(FieldOperand(eax, JSObject::kElementsOffset), edi);
3670 __ mov(FieldOperand(edi, FixedArray::kMapOffset),
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00003671 Immediate(isolate->factory()->fixed_array_map()));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003672
ricow@chromium.org65fae842010-08-25 15:26:24 +00003673 __ mov(FieldOperand(edi, FixedArray::kLengthOffset), ecx);
3674 // Untag the length for the loop below.
3675 __ SmiUntag(ecx);
3676
3677 // Copy the fixed array slots.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003678 Label loop;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003679 __ bind(&loop);
3680 __ mov(ebx, Operand(edx, -1 * kPointerSize)); // Skip receiver.
3681 __ mov(FieldOperand(edi, FixedArray::kHeaderSize), ebx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003682 __ add(edi, Immediate(kPointerSize));
3683 __ sub(edx, Immediate(kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003684 __ dec(ecx);
3685 __ j(not_zero, &loop);
3686
3687 // Return and remove the on-stack parameters.
3688 __ bind(&done);
3689 __ ret(3 * kPointerSize);
3690
3691 // Do the runtime call to allocate the arguments object.
3692 __ bind(&runtime);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003693 __ TailCallRuntime(Runtime::kNewStrictArgumentsFast, 3, 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003694}
3695
3696
3697void RegExpExecStub::Generate(MacroAssembler* masm) {
3698 // Just jump directly to runtime if native RegExp is not selected at compile
3699 // time or if regexp entry in generated code is turned off runtime switch or
3700 // at compilation.
3701#ifdef V8_INTERPRETED_REGEXP
3702 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
3703#else // V8_INTERPRETED_REGEXP
ricow@chromium.org65fae842010-08-25 15:26:24 +00003704
3705 // Stack frame on entry.
3706 // esp[0]: return address
3707 // esp[4]: last_match_info (expected JSArray)
3708 // esp[8]: previous index
3709 // esp[12]: subject string
3710 // esp[16]: JSRegExp object
3711
3712 static const int kLastMatchInfoOffset = 1 * kPointerSize;
3713 static const int kPreviousIndexOffset = 2 * kPointerSize;
3714 static const int kSubjectOffset = 3 * kPointerSize;
3715 static const int kJSRegExpOffset = 4 * kPointerSize;
3716
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003717 Label runtime;
3718 Factory* factory = masm->isolate()->factory();
ricow@chromium.org65fae842010-08-25 15:26:24 +00003719
3720 // Ensure that a RegExp stack is allocated.
3721 ExternalReference address_of_regexp_stack_memory_address =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003722 ExternalReference::address_of_regexp_stack_memory_address(
3723 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003724 ExternalReference address_of_regexp_stack_memory_size =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003725 ExternalReference::address_of_regexp_stack_memory_size(masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003726 __ mov(ebx, Operand::StaticVariable(address_of_regexp_stack_memory_size));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003727 __ test(ebx, ebx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00003728 __ j(zero, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003729
3730 // Check that the first argument is a JSRegExp object.
3731 __ mov(eax, Operand(esp, kJSRegExpOffset));
3732 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003733 __ JumpIfSmi(eax, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003734 __ CmpObjectType(eax, JS_REGEXP_TYPE, ecx);
3735 __ j(not_equal, &runtime);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003736
ricow@chromium.org65fae842010-08-25 15:26:24 +00003737 // Check that the RegExp has been compiled (data contains a fixed array).
3738 __ mov(ecx, FieldOperand(eax, JSRegExp::kDataOffset));
3739 if (FLAG_debug_code) {
3740 __ test(ecx, Immediate(kSmiTagMask));
3741 __ Check(not_zero, "Unexpected type for RegExp data, FixedArray expected");
3742 __ CmpObjectType(ecx, FIXED_ARRAY_TYPE, ebx);
3743 __ Check(equal, "Unexpected type for RegExp data, FixedArray expected");
3744 }
3745
3746 // ecx: RegExp data (FixedArray)
3747 // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP.
3748 __ mov(ebx, FieldOperand(ecx, JSRegExp::kDataTagOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003749 __ cmp(ebx, Immediate(Smi::FromInt(JSRegExp::IRREGEXP)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003750 __ j(not_equal, &runtime);
3751
3752 // ecx: RegExp data (FixedArray)
3753 // Check that the number of captures fit in the static offsets vector buffer.
3754 __ mov(edx, FieldOperand(ecx, JSRegExp::kIrregexpCaptureCountOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003755 // Check (number_of_captures + 1) * 2 <= offsets vector size
3756 // Or number_of_captures * 2 <= offsets vector size - 2
3757 // Multiplying by 2 comes for free since edx is smi-tagged.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003758 STATIC_ASSERT(kSmiTag == 0);
3759 STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003760 STATIC_ASSERT(Isolate::kJSRegexpStaticOffsetsVectorSize >= 2);
3761 __ cmp(edx, Isolate::kJSRegexpStaticOffsetsVectorSize - 2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003762 __ j(above, &runtime);
3763
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003764 // Reset offset for possibly sliced string.
3765 __ Set(edi, Immediate(0));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003766 __ mov(eax, Operand(esp, kSubjectOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003767 __ JumpIfSmi(eax, &runtime);
3768 __ mov(edx, eax); // Make a copy of the original subject string.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003769 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
3770 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003771
3772 // eax: subject string
3773 // edx: subject string
3774 // ebx: subject string instance type
3775 // ecx: RegExp data (FixedArray)
3776 // Handle subject string according to its encoding and representation:
3777 // (1) Sequential two byte? If yes, go to (9).
3778 // (2) Sequential one byte? If yes, go to (6).
3779 // (3) Anything but sequential or cons? If yes, go to (7).
3780 // (4) Cons string. If the string is flat, replace subject with first string.
3781 // Otherwise bailout.
3782 // (5a) Is subject sequential two byte? If yes, go to (9).
3783 // (5b) Is subject external? If yes, go to (8).
3784 // (6) One byte sequential. Load regexp code for one byte.
3785 // (E) Carry on.
3786 /// [...]
3787
3788 // Deferred code at the end of the stub:
3789 // (7) Not a long external string? If yes, go to (10).
3790 // (8) External string. Make it, offset-wise, look like a sequential string.
3791 // (8a) Is the external string one byte? If yes, go to (6).
3792 // (9) Two byte sequential. Load regexp code for one byte. Go to (E).
3793 // (10) Short external string or not a string? If yes, bail out to runtime.
3794 // (11) Sliced string. Replace subject with parent. Go to (5a).
3795
3796 Label seq_one_byte_string /* 6 */, seq_two_byte_string /* 9 */,
3797 external_string /* 8 */, check_underlying /* 5a */,
3798 not_seq_nor_cons /* 7 */, check_code /* E */,
3799 not_long_external /* 10 */;
3800
3801 // (1) Sequential two byte? If yes, go to (9).
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003802 __ and_(ebx, kIsNotStringMask |
3803 kStringRepresentationMask |
3804 kStringEncodingMask |
3805 kShortExternalStringMask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003806 STATIC_ASSERT((kStringTag | kSeqStringTag | kTwoByteStringTag) == 0);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003807 __ j(zero, &seq_two_byte_string); // Go to (9).
3808
3809 // (2) Sequential one byte? If yes, go to (6).
3810 // Any other sequential string must be one byte.
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003811 __ and_(ebx, Immediate(kIsNotStringMask |
3812 kStringRepresentationMask |
3813 kShortExternalStringMask));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003814 __ j(zero, &seq_one_byte_string, Label::kNear); // Go to (6).
ricow@chromium.org65fae842010-08-25 15:26:24 +00003815
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003816 // (3) Anything but sequential or cons? If yes, go to (7).
3817 // We check whether the subject string is a cons, since sequential strings
3818 // have already been covered.
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00003819 STATIC_ASSERT(kConsStringTag < kExternalStringTag);
3820 STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003821 STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
3822 STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003823 __ cmp(ebx, Immediate(kExternalStringTag));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003824 __ j(greater_equal, &not_seq_nor_cons); // Go to (7).
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003825
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003826 // (4) Cons string. Check that it's flat.
3827 // Replace subject with first string and reload instance type.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003828 __ cmp(FieldOperand(eax, ConsString::kSecondOffset), factory->empty_string());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003829 __ j(not_equal, &runtime);
3830 __ mov(eax, FieldOperand(eax, ConsString::kFirstOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003831 __ bind(&check_underlying);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003832 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003833 __ mov(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
3834
3835 // (5a) Is subject sequential two byte? If yes, go to (9).
3836 __ test_b(ebx, kStringRepresentationMask | kStringEncodingMask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003837 STATIC_ASSERT((kSeqStringTag | kTwoByteStringTag) == 0);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003838 __ j(zero, &seq_two_byte_string); // Go to (9).
3839 // (5b) Is subject external? If yes, go to (8).
3840 __ test_b(ebx, kStringRepresentationMask);
3841 // The underlying external string is never a short external string.
3842 STATIC_CHECK(ExternalString::kMaxShortLength < ConsString::kMinLength);
3843 STATIC_CHECK(ExternalString::kMaxShortLength < SlicedString::kMinLength);
3844 __ j(not_zero, &external_string); // Go to (8).
ricow@chromium.org65fae842010-08-25 15:26:24 +00003845
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003846 // eax: sequential subject string (or look-alike, external string)
3847 // edx: original subject string
ricow@chromium.org65fae842010-08-25 15:26:24 +00003848 // ecx: RegExp data (FixedArray)
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003849 // (6) One byte sequential. Load regexp code for one byte.
3850 __ bind(&seq_one_byte_string);
3851 // Load previous index and check range before edx is overwritten. We have
3852 // to use edx instead of eax here because it might have been only made to
3853 // look like a sequential string when it actually is an external string.
3854 __ mov(ebx, Operand(esp, kPreviousIndexOffset));
3855 __ JumpIfNotSmi(ebx, &runtime);
3856 __ cmp(ebx, FieldOperand(edx, String::kLengthOffset));
3857 __ j(above_equal, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003858 __ mov(edx, FieldOperand(ecx, JSRegExp::kDataAsciiCodeOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003859 __ Set(ecx, Immediate(1)); // Type is one byte.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003860
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003861 // (E) Carry on. String handling is done.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003862 __ bind(&check_code);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003863 // edx: irregexp code
ricow@chromium.org65fae842010-08-25 15:26:24 +00003864 // Check that the irregexp code has been generated for the actual string
3865 // encoding. If it has, the field contains a code object otherwise it contains
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00003866 // a smi (code flushing support).
3867 __ JumpIfSmi(edx, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003868
3869 // eax: subject string
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003870 // ebx: previous index (smi)
ricow@chromium.org65fae842010-08-25 15:26:24 +00003871 // edx: code
ulan@chromium.org2efb9002012-01-19 15:36:35 +00003872 // ecx: encoding of subject string (1 if ASCII, 0 if two_byte);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003873 // All checks done. Now push arguments for native regexp code.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003874 Counters* counters = masm->isolate()->counters();
3875 __ IncrementCounter(counters->regexp_entry_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003876
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003877 // Isolates: note we add an additional parameter here (isolate pointer).
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003878 static const int kRegExpExecuteArguments = 9;
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003879 __ EnterApiExitFrame(kRegExpExecuteArguments);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003880
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003881 // Argument 9: Pass current isolate address.
3882 __ mov(Operand(esp, 8 * kPointerSize),
ulan@chromium.org32d7dba2013-04-24 10:59:06 +00003883 Immediate(ExternalReference::isolate_address(masm->isolate())));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003884
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003885 // Argument 8: Indicate that this is a direct call from JavaScript.
3886 __ mov(Operand(esp, 7 * kPointerSize), Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003887
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003888 // Argument 7: Start (high end) of backtracking stack memory area.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003889 __ mov(esi, Operand::StaticVariable(address_of_regexp_stack_memory_address));
3890 __ add(esi, Operand::StaticVariable(address_of_regexp_stack_memory_size));
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003891 __ mov(Operand(esp, 6 * kPointerSize), esi);
3892
3893 // Argument 6: Set the number of capture registers to zero to force global
3894 // regexps to behave as non-global. This does not affect non-global regexps.
3895 __ mov(Operand(esp, 5 * kPointerSize), Immediate(0));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003896
3897 // Argument 5: static offsets vector buffer.
3898 __ mov(Operand(esp, 4 * kPointerSize),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003899 Immediate(ExternalReference::address_of_static_offsets_vector(
3900 masm->isolate())));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003901
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003902 // Argument 2: Previous index.
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003903 __ SmiUntag(ebx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003904 __ mov(Operand(esp, 1 * kPointerSize), ebx);
3905
3906 // Argument 1: Original subject string.
3907 // The original subject is in the previous stack frame. Therefore we have to
3908 // use ebp, which points exactly to one pointer size below the previous esp.
3909 // (Because creating a new stack frame pushes the previous ebp onto the stack
3910 // and thereby moves up esp by one kPointerSize.)
3911 __ mov(esi, Operand(ebp, kSubjectOffset + kPointerSize));
3912 __ mov(Operand(esp, 0 * kPointerSize), esi);
3913
3914 // esi: original subject string
3915 // eax: underlying subject string
3916 // ebx: previous index
ulan@chromium.org2efb9002012-01-19 15:36:35 +00003917 // ecx: encoding of subject string (1 if ASCII 0 if two_byte);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003918 // edx: code
ricow@chromium.org65fae842010-08-25 15:26:24 +00003919 // Argument 4: End of string data
3920 // Argument 3: Start of string data
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003921 // Prepare start and end index of the input.
3922 // Load the length from the original sliced string if that is the case.
3923 __ mov(esi, FieldOperand(esi, String::kLengthOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003924 __ add(esi, edi); // Calculate input end wrt offset.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003925 __ SmiUntag(edi);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003926 __ add(ebx, edi); // Calculate input start wrt offset.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003927
3928 // ebx: start index of the input string
3929 // esi: end index of the input string
3930 Label setup_two_byte, setup_rest;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003931 __ test(ecx, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003932 __ j(zero, &setup_two_byte, Label::kNear);
3933 __ SmiUntag(esi);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003934 __ lea(ecx, FieldOperand(eax, esi, times_1, SeqOneByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003935 __ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003936 __ lea(ecx, FieldOperand(eax, ebx, times_1, SeqOneByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003937 __ mov(Operand(esp, 2 * kPointerSize), ecx); // Argument 3.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003938 __ jmp(&setup_rest, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003939
3940 __ bind(&setup_two_byte);
3941 STATIC_ASSERT(kSmiTag == 0);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003942 STATIC_ASSERT(kSmiTagSize == 1); // esi is smi (powered by 2).
3943 __ lea(ecx, FieldOperand(eax, esi, times_1, SeqTwoByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003944 __ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
3945 __ lea(ecx, FieldOperand(eax, ebx, times_2, SeqTwoByteString::kHeaderSize));
3946 __ mov(Operand(esp, 2 * kPointerSize), ecx); // Argument 3.
3947
3948 __ bind(&setup_rest);
3949
ricow@chromium.org65fae842010-08-25 15:26:24 +00003950 // Locate the code entry and call it.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003951 __ add(edx, Immediate(Code::kHeaderSize - kHeapObjectTag));
3952 __ call(edx);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003953
3954 // Drop arguments and come back to JS mode.
3955 __ LeaveApiExitFrame();
ricow@chromium.org65fae842010-08-25 15:26:24 +00003956
3957 // Check the result.
3958 Label success;
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003959 __ cmp(eax, 1);
3960 // We expect exactly one result since we force the called regexp to behave
3961 // as non-global.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00003962 __ j(equal, &success);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003963 Label failure;
3964 __ cmp(eax, NativeRegExpMacroAssembler::FAILURE);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00003965 __ j(equal, &failure);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003966 __ cmp(eax, NativeRegExpMacroAssembler::EXCEPTION);
3967 // If not exception it can only be retry. Handle that in the runtime system.
3968 __ j(not_equal, &runtime);
3969 // Result must now be exception. If there is no pending exception already a
3970 // stack overflow (on the backtrack stack) was detected in RegExp code but
3971 // haven't created the exception yet. Handle that in the runtime system.
3972 // TODO(592): Rerunning the RegExp to get the stack overflow exception.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00003973 ExternalReference pending_exception(Isolate::kPendingExceptionAddress,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003974 masm->isolate());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003975 __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003976 __ mov(eax, Operand::StaticVariable(pending_exception));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003977 __ cmp(edx, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003978 __ j(equal, &runtime);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003979 // For exception, throw the exception again.
3980
3981 // Clear the pending exception variable.
3982 __ mov(Operand::StaticVariable(pending_exception), edx);
3983
3984 // Special handling of termination exceptions which are uncatchable
3985 // by javascript code.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003986 __ cmp(eax, factory->termination_exception());
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003987 Label throw_termination_exception;
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003988 __ j(equal, &throw_termination_exception, Label::kNear);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003989
3990 // Handle normal exception by following handler chain.
3991 __ Throw(eax);
3992
3993 __ bind(&throw_termination_exception);
ulan@chromium.org65a89c22012-02-14 11:46:07 +00003994 __ ThrowUncatchable(eax);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003995
ricow@chromium.org65fae842010-08-25 15:26:24 +00003996 __ bind(&failure);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003997 // For failure to match, return null.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003998 __ mov(eax, factory->null_value());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003999 __ ret(4 * kPointerSize);
4000
4001 // Load RegExp data.
4002 __ bind(&success);
4003 __ mov(eax, Operand(esp, kJSRegExpOffset));
4004 __ mov(ecx, FieldOperand(eax, JSRegExp::kDataOffset));
4005 __ mov(edx, FieldOperand(ecx, JSRegExp::kIrregexpCaptureCountOffset));
4006 // Calculate number of capture registers (number_of_captures + 1) * 2.
4007 STATIC_ASSERT(kSmiTag == 0);
4008 STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004009 __ add(edx, Immediate(2)); // edx was a smi.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004010
4011 // edx: Number of capture registers
4012 // Load last_match_info which is still known to be a fast case JSArray.
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00004013 // Check that the fourth object is a JSArray object.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004014 __ mov(eax, Operand(esp, kLastMatchInfoOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00004015 __ JumpIfSmi(eax, &runtime);
4016 __ CmpObjectType(eax, JS_ARRAY_TYPE, ebx);
4017 __ j(not_equal, &runtime);
4018 // Check that the JSArray is in fast case.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004019 __ mov(ebx, FieldOperand(eax, JSArray::kElementsOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00004020 __ mov(eax, FieldOperand(ebx, HeapObject::kMapOffset));
4021 __ cmp(eax, factory->fixed_array_map());
4022 __ j(not_equal, &runtime);
4023 // Check that the last match info has space for the capture registers and the
4024 // additional information.
4025 __ mov(eax, FieldOperand(ebx, FixedArray::kLengthOffset));
4026 __ SmiUntag(eax);
4027 __ sub(eax, Immediate(RegExpImpl::kLastMatchOverhead));
4028 __ cmp(edx, eax);
4029 __ j(greater, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004030
4031 // ebx: last_match_info backing store (FixedArray)
4032 // edx: number of capture registers
4033 // Store the capture count.
4034 __ SmiTag(edx); // Number of capture registers to smi.
4035 __ mov(FieldOperand(ebx, RegExpImpl::kLastCaptureCountOffset), edx);
4036 __ SmiUntag(edx); // Number of capture registers back from smi.
4037 // Store last subject and last input.
4038 __ mov(eax, Operand(esp, kSubjectOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00004039 __ mov(ecx, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004040 __ mov(FieldOperand(ebx, RegExpImpl::kLastSubjectOffset), eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004041 __ RecordWriteField(ebx,
4042 RegExpImpl::kLastSubjectOffset,
4043 eax,
4044 edi,
4045 kDontSaveFPRegs);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00004046 __ mov(eax, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004047 __ mov(FieldOperand(ebx, RegExpImpl::kLastInputOffset), eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004048 __ RecordWriteField(ebx,
4049 RegExpImpl::kLastInputOffset,
4050 eax,
4051 edi,
4052 kDontSaveFPRegs);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004053
4054 // Get the static offsets vector filled by the native regexp code.
4055 ExternalReference address_of_static_offsets_vector =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004056 ExternalReference::address_of_static_offsets_vector(masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004057 __ mov(ecx, Immediate(address_of_static_offsets_vector));
4058
4059 // ebx: last_match_info backing store (FixedArray)
4060 // ecx: offsets vector
4061 // edx: number of capture registers
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004062 Label next_capture, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004063 // Capture register counter starts from number of capture registers and
4064 // counts down until wraping after zero.
4065 __ bind(&next_capture);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004066 __ sub(edx, Immediate(1));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004067 __ j(negative, &done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004068 // Read the value from the static offsets vector buffer.
4069 __ mov(edi, Operand(ecx, edx, times_int_size, 0));
4070 __ SmiTag(edi);
4071 // Store the smi value in the last match info.
4072 __ mov(FieldOperand(ebx,
4073 edx,
4074 times_pointer_size,
4075 RegExpImpl::kFirstCaptureOffset),
4076 edi);
4077 __ jmp(&next_capture);
4078 __ bind(&done);
4079
4080 // Return last match info.
4081 __ mov(eax, Operand(esp, kLastMatchInfoOffset));
4082 __ ret(4 * kPointerSize);
4083
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00004084 // Do the runtime call to execute the regexp.
4085 __ bind(&runtime);
4086 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
4087
4088 // Deferred code for string handling.
4089 // (7) Not a long external string? If yes, go to (10).
4090 __ bind(&not_seq_nor_cons);
4091 // Compare flags are still set from (3).
4092 __ j(greater, &not_long_external, Label::kNear); // Go to (10).
4093
4094 // (8) External string. Short external strings have been ruled out.
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004095 __ bind(&external_string);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00004096 // Reload instance type.
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004097 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
4098 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
4099 if (FLAG_debug_code) {
4100 // Assert that we do not have a cons or slice (indirect strings) here.
4101 // Sequential strings have already been ruled out.
4102 __ test_b(ebx, kIsIndirectStringMask);
4103 __ Assert(zero, "external string expected, but not found");
4104 }
4105 __ mov(eax, FieldOperand(eax, ExternalString::kResourceDataOffset));
4106 // Move the pointer so that offset-wise, it looks like a sequential string.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004107 STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004108 __ sub(eax, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
4109 STATIC_ASSERT(kTwoByteStringTag == 0);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00004110 // (8a) Is the external string one byte? If yes, go to (6).
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004111 __ test_b(ebx, kStringEncodingMask);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00004112 __ j(not_zero, &seq_one_byte_string); // Goto (6).
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00004113
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00004114 // eax: sequential subject string (or look-alike, external string)
4115 // edx: original subject string
4116 // ecx: RegExp data (FixedArray)
4117 // (9) Two byte sequential. Load regexp code for one byte. Go to (E).
4118 __ bind(&seq_two_byte_string);
4119 // Load previous index and check range before edx is overwritten. We have
4120 // to use edx instead of eax here because it might have been only made to
4121 // look like a sequential string when it actually is an external string.
4122 __ mov(ebx, Operand(esp, kPreviousIndexOffset));
4123 __ JumpIfNotSmi(ebx, &runtime);
4124 __ cmp(ebx, FieldOperand(edx, String::kLengthOffset));
4125 __ j(above_equal, &runtime);
4126 __ mov(edx, FieldOperand(ecx, JSRegExp::kDataUC16CodeOffset));
4127 __ Set(ecx, Immediate(0)); // Type is two byte.
4128 __ jmp(&check_code); // Go to (E).
4129
4130 // (10) Not a string or a short external string? If yes, bail out to runtime.
4131 __ bind(&not_long_external);
4132 // Catch non-string subject or short external string.
4133 STATIC_ASSERT(kNotStringTag != 0 && kShortExternalStringTag !=0);
4134 __ test(ebx, Immediate(kIsNotStringMask | kShortExternalStringTag));
4135 __ j(not_zero, &runtime);
4136
4137 // (11) Sliced string. Replace subject with parent. Go to (5a).
4138 // Load offset into edi and replace subject string with parent.
4139 __ mov(edi, FieldOperand(eax, SlicedString::kOffsetOffset));
4140 __ mov(eax, FieldOperand(eax, SlicedString::kParentOffset));
4141 __ jmp(&check_underlying); // Go to (5a).
ricow@chromium.org65fae842010-08-25 15:26:24 +00004142#endif // V8_INTERPRETED_REGEXP
4143}
4144
4145
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004146void RegExpConstructResultStub::Generate(MacroAssembler* masm) {
4147 const int kMaxInlineLength = 100;
4148 Label slowcase;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004149 Label done;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004150 __ mov(ebx, Operand(esp, kPointerSize * 3));
whesse@chromium.org7b260152011-06-20 15:33:18 +00004151 __ JumpIfNotSmi(ebx, &slowcase);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004152 __ cmp(ebx, Immediate(Smi::FromInt(kMaxInlineLength)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004153 __ j(above, &slowcase);
4154 // Smi-tagging is equivalent to multiplying by 2.
4155 STATIC_ASSERT(kSmiTag == 0);
4156 STATIC_ASSERT(kSmiTagSize == 1);
4157 // Allocate RegExpResult followed by FixedArray with size in ebx.
4158 // JSArray: [Map][empty properties][Elements][Length-smi][index][input]
4159 // Elements: [Map][Length][..elements..]
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00004160 __ Allocate(JSRegExpResult::kSize + FixedArray::kHeaderSize,
4161 times_pointer_size,
4162 ebx, // In: Number of elements as a smi
4163 REGISTER_VALUE_IS_SMI,
4164 eax, // Out: Start of allocation (tagged).
4165 ecx, // Out: End of allocation.
4166 edx, // Scratch register
4167 &slowcase,
4168 TAG_OBJECT);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004169 // eax: Start of allocated area, object-tagged.
4170
4171 // Set JSArray map to global.regexp_result_map().
4172 // Set empty properties FixedArray.
4173 // Set elements to point to FixedArray allocated right after the JSArray.
4174 // Interleave operations for better latency.
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00004175 __ mov(edx, ContextOperand(esi, Context::GLOBAL_OBJECT_INDEX));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004176 Factory* factory = masm->isolate()->factory();
4177 __ mov(ecx, Immediate(factory->empty_fixed_array()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004178 __ lea(ebx, Operand(eax, JSRegExpResult::kSize));
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00004179 __ mov(edx, FieldOperand(edx, GlobalObject::kNativeContextOffset));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004180 __ mov(FieldOperand(eax, JSObject::kElementsOffset), ebx);
4181 __ mov(FieldOperand(eax, JSObject::kPropertiesOffset), ecx);
4182 __ mov(edx, ContextOperand(edx, Context::REGEXP_RESULT_MAP_INDEX));
4183 __ mov(FieldOperand(eax, HeapObject::kMapOffset), edx);
4184
4185 // Set input, index and length fields from arguments.
4186 __ mov(ecx, Operand(esp, kPointerSize * 1));
4187 __ mov(FieldOperand(eax, JSRegExpResult::kInputOffset), ecx);
4188 __ mov(ecx, Operand(esp, kPointerSize * 2));
4189 __ mov(FieldOperand(eax, JSRegExpResult::kIndexOffset), ecx);
4190 __ mov(ecx, Operand(esp, kPointerSize * 3));
4191 __ mov(FieldOperand(eax, JSArray::kLengthOffset), ecx);
4192
4193 // Fill out the elements FixedArray.
4194 // eax: JSArray.
4195 // ebx: FixedArray.
4196 // ecx: Number of elements in array, as smi.
4197
4198 // Set map.
4199 __ mov(FieldOperand(ebx, HeapObject::kMapOffset),
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004200 Immediate(factory->fixed_array_map()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004201 // Set length.
4202 __ mov(FieldOperand(ebx, FixedArray::kLengthOffset), ecx);
ulan@chromium.org56c14af2012-09-20 12:51:09 +00004203 // Fill contents of fixed-array with undefined.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004204 __ SmiUntag(ecx);
ulan@chromium.org56c14af2012-09-20 12:51:09 +00004205 __ mov(edx, Immediate(factory->undefined_value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004206 __ lea(ebx, FieldOperand(ebx, FixedArray::kHeaderSize));
ulan@chromium.org56c14af2012-09-20 12:51:09 +00004207 // Fill fixed array elements with undefined.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004208 // eax: JSArray.
4209 // ecx: Number of elements to fill.
4210 // ebx: Start of elements in FixedArray.
ulan@chromium.org56c14af2012-09-20 12:51:09 +00004211 // edx: undefined.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004212 Label loop;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004213 __ test(ecx, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004214 __ bind(&loop);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004215 __ j(less_equal, &done, Label::kNear); // Jump if ecx is negative or zero.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004216 __ sub(ecx, Immediate(1));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004217 __ mov(Operand(ebx, ecx, times_pointer_size, 0), edx);
4218 __ jmp(&loop);
4219
4220 __ bind(&done);
4221 __ ret(3 * kPointerSize);
4222
4223 __ bind(&slowcase);
4224 __ TailCallRuntime(Runtime::kRegExpConstructResult, 3, 1);
4225}
4226
4227
ricow@chromium.org65fae842010-08-25 15:26:24 +00004228void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
4229 Register object,
4230 Register result,
4231 Register scratch1,
4232 Register scratch2,
4233 bool object_is_smi,
4234 Label* not_found) {
4235 // Use of registers. Register result is used as a temporary.
4236 Register number_string_cache = result;
4237 Register mask = scratch1;
4238 Register scratch = scratch2;
4239
4240 // Load the number string cache.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004241 ExternalReference roots_array_start =
4242 ExternalReference::roots_array_start(masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004243 __ mov(scratch, Immediate(Heap::kNumberStringCacheRootIndex));
4244 __ mov(number_string_cache,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004245 Operand::StaticArray(scratch, times_pointer_size, roots_array_start));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004246 // Make the hash mask from the length of the number string cache. It
4247 // contains two elements (number and string) for each cache entry.
4248 __ mov(mask, FieldOperand(number_string_cache, FixedArray::kLengthOffset));
4249 __ shr(mask, kSmiTagSize + 1); // Untag length and divide it by two.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004250 __ sub(mask, Immediate(1)); // Make mask.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004251
4252 // Calculate the entry in the number string cache. The hash value in the
4253 // number string cache for smis is just the smi value, and the hash for
4254 // doubles is the xor of the upper and lower words. See
4255 // Heap::GetNumberStringCache.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004256 Label smi_hash_calculated;
4257 Label load_result_from_cache;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004258 if (object_is_smi) {
4259 __ mov(scratch, object);
4260 __ SmiUntag(scratch);
4261 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004262 Label not_smi;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004263 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00004264 __ JumpIfNotSmi(object, &not_smi, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004265 __ mov(scratch, object);
4266 __ SmiUntag(scratch);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004267 __ jmp(&smi_hash_calculated, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004268 __ bind(&not_smi);
4269 __ cmp(FieldOperand(object, HeapObject::kMapOffset),
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004270 masm->isolate()->factory()->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004271 __ j(not_equal, not_found);
4272 STATIC_ASSERT(8 == kDoubleSize);
4273 __ mov(scratch, FieldOperand(object, HeapNumber::kValueOffset));
4274 __ xor_(scratch, FieldOperand(object, HeapNumber::kValueOffset + 4));
4275 // Object is heap number and hash is now in scratch. Calculate cache index.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004276 __ and_(scratch, mask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004277 Register index = scratch;
4278 Register probe = mask;
4279 __ mov(probe,
4280 FieldOperand(number_string_cache,
4281 index,
4282 times_twice_pointer_size,
4283 FixedArray::kHeaderSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00004284 __ JumpIfSmi(probe, not_found);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004285 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00004286 CpuFeatureScope fscope(masm, SSE2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004287 __ movdbl(xmm0, FieldOperand(object, HeapNumber::kValueOffset));
4288 __ movdbl(xmm1, FieldOperand(probe, HeapNumber::kValueOffset));
4289 __ ucomisd(xmm0, xmm1);
4290 } else {
4291 __ fld_d(FieldOperand(object, HeapNumber::kValueOffset));
4292 __ fld_d(FieldOperand(probe, HeapNumber::kValueOffset));
4293 __ FCmp();
4294 }
4295 __ j(parity_even, not_found); // Bail out if NaN is involved.
4296 __ j(not_equal, not_found); // The cache did not contain this value.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004297 __ jmp(&load_result_from_cache, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004298 }
4299
4300 __ bind(&smi_hash_calculated);
4301 // Object is smi and hash is now in scratch. Calculate cache index.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004302 __ and_(scratch, mask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004303 Register index = scratch;
4304 // Check if the entry is the smi we are looking for.
4305 __ cmp(object,
4306 FieldOperand(number_string_cache,
4307 index,
4308 times_twice_pointer_size,
4309 FixedArray::kHeaderSize));
4310 __ j(not_equal, not_found);
4311
4312 // Get the result from the cache.
4313 __ bind(&load_result_from_cache);
4314 __ mov(result,
4315 FieldOperand(number_string_cache,
4316 index,
4317 times_twice_pointer_size,
4318 FixedArray::kHeaderSize + kPointerSize));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004319 Counters* counters = masm->isolate()->counters();
4320 __ IncrementCounter(counters->number_to_string_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004321}
4322
4323
4324void NumberToStringStub::Generate(MacroAssembler* masm) {
4325 Label runtime;
4326
4327 __ mov(ebx, Operand(esp, kPointerSize));
4328
4329 // Generate code to lookup number in the number string cache.
4330 GenerateLookupNumberStringCache(masm, ebx, eax, ecx, edx, false, &runtime);
4331 __ ret(1 * kPointerSize);
4332
4333 __ bind(&runtime);
4334 // Handle number to string in the runtime system if not found in the cache.
4335 __ TailCallRuntime(Runtime::kNumberToStringSkipCache, 1, 1);
4336}
4337
4338
4339static int NegativeComparisonResult(Condition cc) {
4340 ASSERT(cc != equal);
4341 ASSERT((cc == less) || (cc == less_equal)
4342 || (cc == greater) || (cc == greater_equal));
4343 return (cc == greater || cc == greater_equal) ? LESS : GREATER;
4344}
4345
ricow@chromium.org65fae842010-08-25 15:26:24 +00004346
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004347static void CheckInputType(MacroAssembler* masm,
4348 Register input,
4349 CompareIC::State expected,
4350 Label* fail) {
4351 Label ok;
4352 if (expected == CompareIC::SMI) {
4353 __ JumpIfNotSmi(input, fail);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004354 } else if (expected == CompareIC::NUMBER) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004355 __ JumpIfSmi(input, &ok);
4356 __ cmp(FieldOperand(input, HeapObject::kMapOffset),
4357 Immediate(masm->isolate()->factory()->heap_number_map()));
4358 __ j(not_equal, fail);
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00004359 }
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004360 // We could be strict about internalized/non-internalized here, but as long as
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004361 // hydrogen doesn't care, the stub doesn't have to care either.
4362 __ bind(&ok);
4363}
4364
4365
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004366static void BranchIfNotInternalizedString(MacroAssembler* masm,
4367 Label* label,
4368 Register object,
4369 Register scratch) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004370 __ JumpIfSmi(object, label);
4371 __ mov(scratch, FieldOperand(object, HeapObject::kMapOffset));
4372 __ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004373 __ and_(scratch, kIsInternalizedMask | kIsNotStringMask);
4374 __ cmp(scratch, kInternalizedTag | kStringTag);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004375 __ j(not_equal, label);
4376}
4377
4378
4379void ICCompareStub::GenerateGeneric(MacroAssembler* masm) {
4380 Label check_unequal_objects;
4381 Condition cc = GetCondition();
4382
4383 Label miss;
4384 CheckInputType(masm, edx, left_, &miss);
4385 CheckInputType(masm, eax, right_, &miss);
4386
4387 // Compare two smis.
4388 Label non_smi, smi_done;
4389 __ mov(ecx, edx);
4390 __ or_(ecx, eax);
4391 __ JumpIfNotSmi(ecx, &non_smi, Label::kNear);
4392 __ sub(edx, eax); // Return on the result of the subtraction.
4393 __ j(no_overflow, &smi_done, Label::kNear);
4394 __ not_(edx); // Correct sign in case of overflow. edx is never 0 here.
4395 __ bind(&smi_done);
4396 __ mov(eax, edx);
4397 __ ret(0);
4398 __ bind(&non_smi);
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00004399
ricow@chromium.org65fae842010-08-25 15:26:24 +00004400 // NOTICE! This code is only reached after a smi-fast-case check, so
4401 // it is certain that at least one operand isn't a smi.
4402
4403 // Identical objects can be compared fast, but there are some tricky cases
4404 // for NaN and undefined.
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00004405 Label generic_heap_number_comparison;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004406 {
4407 Label not_identical;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004408 __ cmp(eax, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004409 __ j(not_equal, &not_identical);
4410
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004411 if (cc != equal) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00004412 // Check for undefined. undefined OP undefined is false even though
4413 // undefined == undefined.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004414 Label check_for_nan;
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004415 __ cmp(edx, masm->isolate()->factory()->undefined_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004416 __ j(not_equal, &check_for_nan, Label::kNear);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004417 __ Set(eax, Immediate(Smi::FromInt(NegativeComparisonResult(cc))));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004418 __ ret(0);
4419 __ bind(&check_for_nan);
4420 }
4421
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00004422 // Test for NaN. Compare heap numbers in a general way,
4423 // to hanlde NaNs correctly.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004424 __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
4425 Immediate(masm->isolate()->factory()->heap_number_map()));
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00004426 __ j(equal, &generic_heap_number_comparison, Label::kNear);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004427 if (cc != equal) {
4428 // Call runtime on identical JSObjects. Otherwise return equal.
4429 __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
4430 __ j(above_equal, &not_identical);
4431 }
4432 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
4433 __ ret(0);
4434
ricow@chromium.org65fae842010-08-25 15:26:24 +00004435
4436 __ bind(&not_identical);
4437 }
4438
4439 // Strict equality can quickly decide whether objects are equal.
4440 // Non-strict object equality is slower, so it is handled later in the stub.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004441 if (cc == equal && strict()) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00004442 Label slow; // Fallthrough label.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004443 Label not_smis;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004444 // If we're doing a strict equality comparison, we don't have to do
4445 // type conversion, so we generate code to do fast comparison for objects
4446 // and oddballs. Non-smi numbers and strings still go through the usual
4447 // slow-case code.
4448 // If either is a Smi (we know that not both are), then they can only
4449 // be equal if the other is a HeapNumber. If so, use the slow case.
4450 STATIC_ASSERT(kSmiTag == 0);
4451 ASSERT_EQ(0, Smi::FromInt(0));
4452 __ mov(ecx, Immediate(kSmiTagMask));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004453 __ and_(ecx, eax);
4454 __ test(ecx, edx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004455 __ j(not_zero, &not_smis, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004456 // One operand is a smi.
4457
4458 // Check whether the non-smi is a heap number.
4459 STATIC_ASSERT(kSmiTagMask == 1);
4460 // ecx still holds eax & kSmiTag, which is either zero or one.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004461 __ sub(ecx, Immediate(0x01));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004462 __ mov(ebx, edx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004463 __ xor_(ebx, eax);
4464 __ and_(ebx, ecx); // ebx holds either 0 or eax ^ edx.
4465 __ xor_(ebx, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004466 // if eax was smi, ebx is now edx, else eax.
4467
4468 // Check if the non-smi operand is a heap number.
4469 __ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004470 Immediate(masm->isolate()->factory()->heap_number_map()));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004471 // If heap number, handle it in the slow case.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004472 __ j(equal, &slow, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004473 // Return non-equal (ebx is not zero)
4474 __ mov(eax, ebx);
4475 __ ret(0);
4476
4477 __ bind(&not_smis);
4478 // If either operand is a JSObject or an oddball value, then they are not
4479 // equal since their pointers are different
4480 // There is no test for undetectability in strict equality.
4481
4482 // Get the type of the first operand.
4483 // If the first object is a JS object, we have done pointer comparison.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004484 Label first_non_object;
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004485 STATIC_ASSERT(LAST_TYPE == LAST_SPEC_OBJECT_TYPE);
4486 __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004487 __ j(below, &first_non_object, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004488
4489 // Return non-zero (eax is not zero)
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004490 Label return_not_equal;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004491 STATIC_ASSERT(kHeapObjectTag != 0);
4492 __ bind(&return_not_equal);
4493 __ ret(0);
4494
4495 __ bind(&first_non_object);
4496 // Check for oddballs: true, false, null, undefined.
4497 __ CmpInstanceType(ecx, ODDBALL_TYPE);
4498 __ j(equal, &return_not_equal);
4499
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004500 __ CmpObjectType(edx, FIRST_SPEC_OBJECT_TYPE, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004501 __ j(above_equal, &return_not_equal);
4502
4503 // Check for oddballs: true, false, null, undefined.
4504 __ CmpInstanceType(ecx, ODDBALL_TYPE);
4505 __ j(equal, &return_not_equal);
4506
4507 // Fall through to the general case.
4508 __ bind(&slow);
4509 }
4510
4511 // Generate the number comparison code.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004512 Label non_number_comparison;
4513 Label unordered;
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00004514 __ bind(&generic_heap_number_comparison);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004515 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00004516 CpuFeatureScope use_sse2(masm, SSE2);
4517 CpuFeatureScope use_cmov(masm, CMOV);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004518
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004519 FloatingPointHelper::LoadSSE2Operands(masm, &non_number_comparison);
4520 __ ucomisd(xmm0, xmm1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004521
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004522 // Don't base result on EFLAGS when a NaN is involved.
4523 __ j(parity_even, &unordered, Label::kNear);
4524 // Return a result of -1, 0, or 1, based on EFLAGS.
4525 __ mov(eax, 0); // equal
4526 __ mov(ecx, Immediate(Smi::FromInt(1)));
4527 __ cmov(above, eax, ecx);
4528 __ mov(ecx, Immediate(Smi::FromInt(-1)));
4529 __ cmov(below, eax, ecx);
4530 __ ret(0);
4531 } else {
4532 FloatingPointHelper::CheckFloatOperands(
4533 masm, &non_number_comparison, ebx);
4534 FloatingPointHelper::LoadFloatOperand(masm, eax);
4535 FloatingPointHelper::LoadFloatOperand(masm, edx);
4536 __ FCmp();
ricow@chromium.org65fae842010-08-25 15:26:24 +00004537
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004538 // Don't base result on EFLAGS when a NaN is involved.
4539 __ j(parity_even, &unordered, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004540
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004541 Label below_label, above_label;
4542 // Return a result of -1, 0, or 1, based on EFLAGS.
4543 __ j(below, &below_label, Label::kNear);
4544 __ j(above, &above_label, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004545
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004546 __ Set(eax, Immediate(0));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004547 __ ret(0);
4548
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004549 __ bind(&below_label);
4550 __ mov(eax, Immediate(Smi::FromInt(-1)));
4551 __ ret(0);
4552
4553 __ bind(&above_label);
4554 __ mov(eax, Immediate(Smi::FromInt(1)));
4555 __ ret(0);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004556 }
4557
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004558 // If one of the numbers was NaN, then the result is always false.
4559 // The cc is never not-equal.
4560 __ bind(&unordered);
4561 ASSERT(cc != not_equal);
4562 if (cc == less || cc == less_equal) {
4563 __ mov(eax, Immediate(Smi::FromInt(1)));
4564 } else {
4565 __ mov(eax, Immediate(Smi::FromInt(-1)));
4566 }
4567 __ ret(0);
4568
4569 // The number comparison code did not provide a valid result.
4570 __ bind(&non_number_comparison);
4571
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004572 // Fast negative check for internalized-to-internalized equality.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004573 Label check_for_strings;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004574 if (cc == equal) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004575 BranchIfNotInternalizedString(masm, &check_for_strings, eax, ecx);
4576 BranchIfNotInternalizedString(masm, &check_for_strings, edx, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004577
4578 // We've already checked for object identity, so if both operands
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004579 // are internalized they aren't equal. Register eax already holds a
ricow@chromium.org65fae842010-08-25 15:26:24 +00004580 // non-zero value, which indicates not equal, so just return.
4581 __ ret(0);
4582 }
4583
4584 __ bind(&check_for_strings);
4585
4586 __ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx,
4587 &check_unequal_objects);
4588
ulan@chromium.org2efb9002012-01-19 15:36:35 +00004589 // Inline comparison of ASCII strings.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004590 if (cc == equal) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00004591 StringCompareStub::GenerateFlatAsciiStringEquals(masm,
ricow@chromium.org65fae842010-08-25 15:26:24 +00004592 edx,
4593 eax,
4594 ecx,
lrn@chromium.org1c092762011-05-09 09:42:16 +00004595 ebx);
4596 } else {
4597 StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
4598 edx,
4599 eax,
4600 ecx,
4601 ebx,
4602 edi);
4603 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00004604#ifdef DEBUG
4605 __ Abort("Unexpected fall-through from string comparison");
4606#endif
4607
4608 __ bind(&check_unequal_objects);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004609 if (cc == equal && !strict()) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00004610 // Non-strict equality. Objects are unequal if
4611 // they are both JSObjects and not undetectable,
4612 // and their pointers are different.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004613 Label not_both_objects;
4614 Label return_unequal;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004615 // At most one is a smi, so we can test for smi by adding the two.
4616 // A smi plus a heap object has the low bit set, a heap object plus
4617 // a heap object has the low bit clear.
4618 STATIC_ASSERT(kSmiTag == 0);
4619 STATIC_ASSERT(kSmiTagMask == 1);
4620 __ lea(ecx, Operand(eax, edx, times_1, 0));
4621 __ test(ecx, Immediate(kSmiTagMask));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004622 __ j(not_zero, &not_both_objects, Label::kNear);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004623 __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004624 __ j(below, &not_both_objects, Label::kNear);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004625 __ CmpObjectType(edx, FIRST_SPEC_OBJECT_TYPE, ebx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004626 __ j(below, &not_both_objects, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004627 // We do not bail out after this point. Both are JSObjects, and
4628 // they are equal if and only if both are undetectable.
4629 // The and of the undetectable flags is 1 if and only if they are equal.
4630 __ test_b(FieldOperand(ecx, Map::kBitFieldOffset),
4631 1 << Map::kIsUndetectable);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004632 __ j(zero, &return_unequal, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004633 __ test_b(FieldOperand(ebx, Map::kBitFieldOffset),
4634 1 << Map::kIsUndetectable);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004635 __ j(zero, &return_unequal, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004636 // The objects are both undetectable, so they both compare as the value
4637 // undefined, and are equal.
4638 __ Set(eax, Immediate(EQUAL));
4639 __ bind(&return_unequal);
4640 // Return non-equal by returning the non-zero object pointer in eax,
4641 // or return equal if we fell through to here.
4642 __ ret(0); // rax, rdx were pushed
4643 __ bind(&not_both_objects);
4644 }
4645
4646 // Push arguments below the return address.
4647 __ pop(ecx);
4648 __ push(edx);
4649 __ push(eax);
4650
4651 // Figure out which native to call and setup the arguments.
4652 Builtins::JavaScript builtin;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004653 if (cc == equal) {
4654 builtin = strict() ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004655 } else {
4656 builtin = Builtins::COMPARE;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004657 __ push(Immediate(Smi::FromInt(NegativeComparisonResult(cc))));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004658 }
4659
4660 // Restore return address on the stack.
4661 __ push(ecx);
4662
4663 // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
4664 // tagged as a small integer.
4665 __ InvokeBuiltin(builtin, JUMP_FUNCTION);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004666
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004667 __ bind(&miss);
4668 GenerateMiss(masm);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004669}
4670
4671
4672void StackCheckStub::Generate(MacroAssembler* masm) {
whesse@chromium.org4a5224e2010-10-20 12:37:07 +00004673 __ TailCallRuntime(Runtime::kStackGuard, 0, 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004674}
4675
4676
yangguo@chromium.org56454712012-02-16 15:33:53 +00004677void InterruptStub::Generate(MacroAssembler* masm) {
4678 __ TailCallRuntime(Runtime::kInterrupt, 0, 1);
4679}
4680
4681
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004682static void GenerateRecordCallTargetNoArray(MacroAssembler* masm) {
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004683 // Cache the called function in a global property cell. Cache states
4684 // are uninitialized, monomorphic (indicated by a JSFunction), and
4685 // megamorphic.
4686 // ebx : cache cell for call target
4687 // edi : the function to call
4688 Isolate* isolate = masm->isolate();
4689 Label initialize, done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004690
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004691 // Load the cache state into ecx.
4692 __ mov(ecx, FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004693
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004694 // A monomorphic cache hit or an already megamorphic state: invoke the
4695 // function without changing the state.
4696 __ cmp(ecx, edi);
4697 __ j(equal, &done, Label::kNear);
4698 __ cmp(ecx, Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate)));
4699 __ j(equal, &done, Label::kNear);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004700
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004701 // A monomorphic miss (i.e, here the cache is not uninitialized) goes
4702 // megamorphic.
4703 __ cmp(ecx, Immediate(TypeFeedbackCells::UninitializedSentinel(isolate)));
4704 __ j(equal, &initialize, Label::kNear);
4705 // MegamorphicSentinel is an immortal immovable object (undefined) so no
4706 // write-barrier is needed.
4707 __ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
4708 Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate)));
4709 __ jmp(&done, Label::kNear);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004710
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004711 // An uninitialized cache is patched with the function.
4712 __ bind(&initialize);
4713 __ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset), edi);
4714 // No need for a write barrier here - cells are rescanned.
4715
4716 __ bind(&done);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004717}
4718
4719
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004720static void GenerateRecordCallTarget(MacroAssembler* masm) {
4721 // Cache the called function in a global property cell. Cache states
4722 // are uninitialized, monomorphic (indicated by a JSFunction), and
4723 // megamorphic.
4724 // ebx : cache cell for call target
4725 // edi : the function to call
4726 ASSERT(FLAG_optimize_constructed_arrays);
4727 Isolate* isolate = masm->isolate();
4728 Label initialize, done, miss, megamorphic, not_array_function;
4729
4730 // Load the cache state into ecx.
4731 __ mov(ecx, FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset));
4732
4733 // A monomorphic cache hit or an already megamorphic state: invoke the
4734 // function without changing the state.
4735 __ cmp(ecx, edi);
4736 __ j(equal, &done);
4737 __ cmp(ecx, Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate)));
4738 __ j(equal, &done);
4739
4740 // Special handling of the Array() function, which caches not only the
4741 // monomorphic Array function but the initial ElementsKind with special
4742 // sentinels
4743 Handle<Object> terminal_kind_sentinel =
4744 TypeFeedbackCells::MonomorphicArraySentinel(isolate,
4745 LAST_FAST_ELEMENTS_KIND);
ulan@chromium.org57ff8812013-05-10 08:16:55 +00004746 __ JumpIfNotSmi(ecx, &miss);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004747 __ cmp(ecx, Immediate(terminal_kind_sentinel));
4748 __ j(above, &miss);
4749 // Load the global or builtins object from the current context
4750 __ LoadGlobalContext(ecx);
4751 // Make sure the function is the Array() function
4752 __ cmp(edi, Operand(ecx,
4753 Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX)));
4754 __ j(not_equal, &megamorphic);
4755 __ jmp(&done);
4756
4757 __ bind(&miss);
4758
4759 // A monomorphic miss (i.e, here the cache is not uninitialized) goes
4760 // megamorphic.
4761 __ cmp(ecx, Immediate(TypeFeedbackCells::UninitializedSentinel(isolate)));
4762 __ j(equal, &initialize);
4763 // MegamorphicSentinel is an immortal immovable object (undefined) so no
4764 // write-barrier is needed.
4765 __ bind(&megamorphic);
4766 __ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
4767 Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate)));
4768 __ jmp(&done, Label::kNear);
4769
4770 // An uninitialized cache is patched with the function or sentinel to
4771 // indicate the ElementsKind if function is the Array constructor.
4772 __ bind(&initialize);
4773 __ LoadGlobalContext(ecx);
4774 // Make sure the function is the Array() function
4775 __ cmp(edi, Operand(ecx,
4776 Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX)));
4777 __ j(not_equal, &not_array_function);
4778
4779 // The target function is the Array constructor, install a sentinel value in
4780 // the constructor's type info cell that will track the initial ElementsKind
4781 // that should be used for the array when its constructed.
4782 Handle<Object> initial_kind_sentinel =
4783 TypeFeedbackCells::MonomorphicArraySentinel(isolate,
4784 GetInitialFastElementsKind());
4785 __ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
4786 Immediate(initial_kind_sentinel));
4787 __ jmp(&done);
4788
4789 __ bind(&not_array_function);
4790 __ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset), edi);
4791 // No need for a write barrier here - cells are rescanned.
4792
4793 __ bind(&done);
4794}
4795
4796
ricow@chromium.org65fae842010-08-25 15:26:24 +00004797void CallFunctionStub::Generate(MacroAssembler* masm) {
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004798 // ebx : cache cell for call target
danno@chromium.orgc612e022011-11-10 11:38:15 +00004799 // edi : the function to call
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004800 Isolate* isolate = masm->isolate();
lrn@chromium.org34e60782011-09-15 07:25:40 +00004801 Label slow, non_function;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004802
danno@chromium.org40cb8782011-05-25 07:58:50 +00004803 // The receiver might implicitly be the global object. This is
4804 // indicated by passing the hole as the receiver to the call
4805 // function stub.
4806 if (ReceiverMightBeImplicit()) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004807 Label receiver_ok;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004808 // Get the receiver from the stack.
4809 // +1 ~ return address
ricow@chromium.org65fae842010-08-25 15:26:24 +00004810 __ mov(eax, Operand(esp, (argc_ + 1) * kPointerSize));
danno@chromium.org40cb8782011-05-25 07:58:50 +00004811 // Call as function is indicated with the hole.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004812 __ cmp(eax, isolate->factory()->the_hole_value());
4813 __ j(not_equal, &receiver_ok, Label::kNear);
danno@chromium.org40cb8782011-05-25 07:58:50 +00004814 // Patch the receiver on the stack with the global receiver object.
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004815 __ mov(ecx, GlobalObjectOperand());
4816 __ mov(ecx, FieldOperand(ecx, GlobalObject::kGlobalReceiverOffset));
4817 __ mov(Operand(esp, (argc_ + 1) * kPointerSize), ecx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004818 __ bind(&receiver_ok);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004819 }
4820
ricow@chromium.org65fae842010-08-25 15:26:24 +00004821 // Check that the function really is a JavaScript function.
lrn@chromium.org34e60782011-09-15 07:25:40 +00004822 __ JumpIfSmi(edi, &non_function);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004823 // Goto slow case if we do not have a function.
4824 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00004825 __ j(not_equal, &slow);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004826
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004827 if (RecordCallTarget()) {
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00004828 if (FLAG_optimize_constructed_arrays) {
4829 GenerateRecordCallTarget(masm);
4830 } else {
4831 GenerateRecordCallTargetNoArray(masm);
4832 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004833 }
4834
ricow@chromium.org65fae842010-08-25 15:26:24 +00004835 // Fast-case: Just invoke the function.
4836 ParameterCount actual(argc_);
danno@chromium.org40cb8782011-05-25 07:58:50 +00004837
4838 if (ReceiverMightBeImplicit()) {
4839 Label call_as_function;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004840 __ cmp(eax, isolate->factory()->the_hole_value());
danno@chromium.org40cb8782011-05-25 07:58:50 +00004841 __ j(equal, &call_as_function);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004842 __ InvokeFunction(edi,
4843 actual,
4844 JUMP_FUNCTION,
4845 NullCallWrapper(),
4846 CALL_AS_METHOD);
danno@chromium.org40cb8782011-05-25 07:58:50 +00004847 __ bind(&call_as_function);
4848 }
4849 __ InvokeFunction(edi,
4850 actual,
4851 JUMP_FUNCTION,
4852 NullCallWrapper(),
4853 CALL_AS_FUNCTION);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004854
4855 // Slow-case: Non-function called.
4856 __ bind(&slow);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004857 if (RecordCallTarget()) {
4858 // If there is a call target cache, mark it megamorphic in the
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004859 // non-function case. MegamorphicSentinel is an immortal immovable
4860 // object (undefined) so no write barrier is needed.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004861 __ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004862 Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate)));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004863 }
lrn@chromium.org34e60782011-09-15 07:25:40 +00004864 // Check for function proxy.
4865 __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
4866 __ j(not_equal, &non_function);
4867 __ pop(ecx);
4868 __ push(edi); // put proxy as additional argument under return address
4869 __ push(ecx);
4870 __ Set(eax, Immediate(argc_ + 1));
4871 __ Set(ebx, Immediate(0));
4872 __ SetCallKind(ecx, CALL_AS_FUNCTION);
4873 __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY);
4874 {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004875 Handle<Code> adaptor = isolate->builtins()->ArgumentsAdaptorTrampoline();
lrn@chromium.org34e60782011-09-15 07:25:40 +00004876 __ jmp(adaptor, RelocInfo::CODE_TARGET);
4877 }
4878
ricow@chromium.org65fae842010-08-25 15:26:24 +00004879 // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
4880 // of the original receiver from the call site).
lrn@chromium.org34e60782011-09-15 07:25:40 +00004881 __ bind(&non_function);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004882 __ mov(Operand(esp, (argc_ + 1) * kPointerSize), edi);
4883 __ Set(eax, Immediate(argc_));
4884 __ Set(ebx, Immediate(0));
lrn@chromium.org34e60782011-09-15 07:25:40 +00004885 __ SetCallKind(ecx, CALL_AS_METHOD);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004886 __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004887 Handle<Code> adaptor = isolate->builtins()->ArgumentsAdaptorTrampoline();
ricow@chromium.org65fae842010-08-25 15:26:24 +00004888 __ jmp(adaptor, RelocInfo::CODE_TARGET);
4889}
4890
4891
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004892void CallConstructStub::Generate(MacroAssembler* masm) {
4893 // eax : number of arguments
4894 // ebx : cache cell for call target
4895 // edi : constructor function
4896 Label slow, non_function_call;
4897
4898 // Check that function is not a smi.
4899 __ JumpIfSmi(edi, &non_function_call);
4900 // Check that function is a JSFunction.
4901 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
4902 __ j(not_equal, &slow);
4903
4904 if (RecordCallTarget()) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004905 if (FLAG_optimize_constructed_arrays) {
4906 GenerateRecordCallTarget(masm);
4907 } else {
4908 GenerateRecordCallTargetNoArray(masm);
4909 }
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004910 }
4911
4912 // Jump to the function-specific construct stub.
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004913 Register jmp_reg = FLAG_optimize_constructed_arrays ? ecx : ebx;
4914 __ mov(jmp_reg, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
4915 __ mov(jmp_reg, FieldOperand(jmp_reg,
4916 SharedFunctionInfo::kConstructStubOffset));
4917 __ lea(jmp_reg, FieldOperand(jmp_reg, Code::kHeaderSize));
4918 __ jmp(jmp_reg);
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004919
4920 // edi: called object
4921 // eax: number of arguments
4922 // ecx: object map
4923 Label do_call;
4924 __ bind(&slow);
4925 __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
4926 __ j(not_equal, &non_function_call);
4927 __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY_AS_CONSTRUCTOR);
4928 __ jmp(&do_call);
4929
4930 __ bind(&non_function_call);
4931 __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
4932 __ bind(&do_call);
4933 // Set expected number of arguments to zero (not changing eax).
4934 __ Set(ebx, Immediate(0));
4935 Handle<Code> arguments_adaptor =
4936 masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
4937 __ SetCallKind(ecx, CALL_AS_METHOD);
4938 __ jmp(arguments_adaptor, RelocInfo::CODE_TARGET);
4939}
4940
4941
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004942bool CEntryStub::NeedsImmovableCode() {
4943 return false;
4944}
4945
4946
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004947bool CEntryStub::IsPregenerated() {
4948 return (!save_doubles_ || ISOLATE->fp_stubs_generated()) &&
4949 result_size_ == 1;
4950}
4951
4952
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004953void CodeStub::GenerateStubsAheadOfTime(Isolate* isolate) {
4954 CEntryStub::GenerateAheadOfTime(isolate);
4955 StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(isolate);
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00004956 StubFailureTrampolineStub::GenerateAheadOfTime(isolate);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004957 // It is important that the store buffer overflow stubs are generated first.
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004958 RecordWriteStub::GenerateFixedRegStubsAheadOfTime(isolate);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00004959 if (FLAG_optimize_constructed_arrays) {
4960 ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate);
4961 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004962}
4963
4964
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004965void CodeStub::GenerateFPStubs(Isolate* isolate) {
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004966 if (CpuFeatures::IsSupported(SSE2)) {
4967 CEntryStub save_doubles(1, kSaveFPRegs);
4968 // Stubs might already be in the snapshot, detect that and don't regenerate,
4969 // which would lead to code stub initialization state being messed up.
4970 Code* save_doubles_code;
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004971 if (!save_doubles.FindCodeInCache(&save_doubles_code, isolate)) {
4972 save_doubles_code = *(save_doubles.GetCode(isolate));
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004973 }
4974 save_doubles_code->set_is_pregenerated(true);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004975 isolate->set_fp_stubs_generated(true);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004976 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004977}
4978
4979
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004980void CEntryStub::GenerateAheadOfTime(Isolate* isolate) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004981 CEntryStub stub(1, kDontSaveFPRegs);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004982 Handle<Code> code = stub.GetCode(isolate);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004983 code->set_is_pregenerated(true);
4984}
4985
4986
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00004987static void JumpIfOOM(MacroAssembler* masm,
4988 Register value,
4989 Register scratch,
4990 Label* oom_label) {
4991 __ mov(scratch, value);
4992 STATIC_ASSERT(Failure::OUT_OF_MEMORY_EXCEPTION == 3);
4993 STATIC_ASSERT(kFailureTag == 3);
4994 __ and_(scratch, 0xf);
4995 __ cmp(scratch, 0xf);
4996 __ j(equal, oom_label);
4997}
4998
4999
ricow@chromium.org65fae842010-08-25 15:26:24 +00005000void CEntryStub::GenerateCore(MacroAssembler* masm,
5001 Label* throw_normal_exception,
5002 Label* throw_termination_exception,
5003 Label* throw_out_of_memory_exception,
5004 bool do_gc,
ager@chromium.org0ee099b2011-01-25 14:06:47 +00005005 bool always_allocate_scope) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00005006 // eax: result parameter for PerformGC, if any
5007 // ebx: pointer to C function (C callee-saved)
5008 // ebp: frame pointer (restored after C call)
5009 // esp: stack pointer (restored after C call)
5010 // edi: number of arguments including receiver (C callee-saved)
5011 // esi: pointer to the first argument (C callee-saved)
5012
5013 // Result returned in eax, or eax+edx if result_size_ is 2.
5014
5015 // Check stack alignment.
5016 if (FLAG_debug_code) {
5017 __ CheckStackAlignment();
5018 }
5019
5020 if (do_gc) {
5021 // Pass failure code returned from last attempt as first argument to
5022 // PerformGC. No need to use PrepareCallCFunction/CallCFunction here as the
5023 // stack alignment is known to be correct. This function takes one argument
5024 // which is passed on the stack, and we know that the stack has been
5025 // prepared to pass at least one argument.
5026 __ mov(Operand(esp, 0 * kPointerSize), eax); // Result.
5027 __ call(FUNCTION_ADDR(Runtime::PerformGC), RelocInfo::RUNTIME_ENTRY);
5028 }
5029
5030 ExternalReference scope_depth =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005031 ExternalReference::heap_always_allocate_scope_depth(masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00005032 if (always_allocate_scope) {
5033 __ inc(Operand::StaticVariable(scope_depth));
5034 }
5035
5036 // Call C function.
5037 __ mov(Operand(esp, 0 * kPointerSize), edi); // argc.
5038 __ mov(Operand(esp, 1 * kPointerSize), esi); // argv.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005039 __ mov(Operand(esp, 2 * kPointerSize),
ulan@chromium.org32d7dba2013-04-24 10:59:06 +00005040 Immediate(ExternalReference::isolate_address(masm->isolate())));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005041 __ call(ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005042 // Result is in eax or edx:eax - do not destroy these registers!
5043
5044 if (always_allocate_scope) {
5045 __ dec(Operand::StaticVariable(scope_depth));
5046 }
5047
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005048 // Runtime functions should not return 'the hole'. Allowing it to escape may
5049 // lead to crashes in the IC code later.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005050 if (FLAG_debug_code) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005051 Label okay;
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005052 __ cmp(eax, masm->isolate()->factory()->the_hole_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005053 __ j(not_equal, &okay, Label::kNear);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005054 // TODO(wingo): Currently SuspendJSGeneratorObject returns the hole. Change
5055 // to return another sentinel like a harmony symbol.
5056 __ cmp(ebx, Immediate(ExternalReference(
5057 Runtime::kSuspendJSGeneratorObject, masm->isolate())));
5058 __ j(equal, &okay, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005059 __ int3();
5060 __ bind(&okay);
5061 }
5062
5063 // Check for failure result.
5064 Label failure_returned;
5065 STATIC_ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
5066 __ lea(ecx, Operand(eax, 1));
5067 // Lower 2 bits of ecx are 0 iff eax has failure tag.
5068 __ test(ecx, Immediate(kFailureTagMask));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005069 __ j(zero, &failure_returned);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005070
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005071 ExternalReference pending_exception_address(
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00005072 Isolate::kPendingExceptionAddress, masm->isolate());
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00005073
5074 // Check that there is no pending exception, otherwise we
5075 // should have returned some failure value.
5076 if (FLAG_debug_code) {
5077 __ push(edx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005078 __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005079 Label okay;
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00005080 __ cmp(edx, Operand::StaticVariable(pending_exception_address));
5081 // Cannot use check here as it attempts to generate call into runtime.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005082 __ j(equal, &okay, Label::kNear);
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00005083 __ int3();
5084 __ bind(&okay);
5085 __ pop(edx);
5086 }
5087
ricow@chromium.org65fae842010-08-25 15:26:24 +00005088 // Exit the JavaScript to C++ exit frame.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005089 __ LeaveExitFrame(save_doubles_ == kSaveFPRegs);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005090 __ ret(0);
5091
5092 // Handling of failure.
5093 __ bind(&failure_returned);
5094
5095 Label retry;
5096 // If the returned exception is RETRY_AFTER_GC continue at retry label
5097 STATIC_ASSERT(Failure::RETRY_AFTER_GC == 0);
5098 __ test(eax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005099 __ j(zero, &retry, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005100
5101 // Special handling of out of memory exceptions.
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00005102 JumpIfOOM(masm, eax, ecx, throw_out_of_memory_exception);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005103
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00005104 // Retrieve the pending exception.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005105 __ mov(eax, Operand::StaticVariable(pending_exception_address));
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00005106
5107 // See if we just retrieved an OOM exception.
5108 JumpIfOOM(masm, eax, ecx, throw_out_of_memory_exception);
5109
5110 // Clear the pending exception.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005111 __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005112 __ mov(Operand::StaticVariable(pending_exception_address), edx);
5113
5114 // Special handling of termination exceptions which are uncatchable
5115 // by javascript code.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005116 __ cmp(eax, masm->isolate()->factory()->termination_exception());
ricow@chromium.org65fae842010-08-25 15:26:24 +00005117 __ j(equal, throw_termination_exception);
5118
5119 // Handle normal exception.
5120 __ jmp(throw_normal_exception);
5121
5122 // Retry.
5123 __ bind(&retry);
5124}
5125
5126
ricow@chromium.org65fae842010-08-25 15:26:24 +00005127void CEntryStub::Generate(MacroAssembler* masm) {
5128 // eax: number of arguments including receiver
5129 // ebx: pointer to C function (C callee-saved)
5130 // ebp: frame pointer (restored after C call)
5131 // esp: stack pointer (restored after C call)
5132 // esi: current context (C callee-saved)
5133 // edi: JS function of the caller (C callee-saved)
5134
5135 // NOTE: Invocations of builtins may return failure objects instead
5136 // of a proper result. The builtin entry handles this by performing
5137 // a garbage collection and retrying the builtin (twice).
5138
5139 // Enter the exit frame that transitions from JavaScript to C++.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005140 __ EnterExitFrame(save_doubles_ == kSaveFPRegs);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005141
5142 // eax: result parameter for PerformGC, if any (setup below)
5143 // ebx: pointer to builtin function (C callee-saved)
5144 // ebp: frame pointer (restored after C call)
5145 // esp: stack pointer (restored after C call)
5146 // edi: number of arguments including receiver (C callee-saved)
5147 // esi: argv pointer (C callee-saved)
5148
5149 Label throw_normal_exception;
5150 Label throw_termination_exception;
5151 Label throw_out_of_memory_exception;
5152
5153 // Call into the runtime system.
5154 GenerateCore(masm,
5155 &throw_normal_exception,
5156 &throw_termination_exception,
5157 &throw_out_of_memory_exception,
5158 false,
5159 false);
5160
5161 // Do space-specific GC and retry runtime call.
5162 GenerateCore(masm,
5163 &throw_normal_exception,
5164 &throw_termination_exception,
5165 &throw_out_of_memory_exception,
5166 true,
5167 false);
5168
5169 // Do full GC and retry runtime call one final time.
5170 Failure* failure = Failure::InternalError();
5171 __ mov(eax, Immediate(reinterpret_cast<int32_t>(failure)));
5172 GenerateCore(masm,
5173 &throw_normal_exception,
5174 &throw_termination_exception,
5175 &throw_out_of_memory_exception,
5176 true,
5177 true);
5178
5179 __ bind(&throw_out_of_memory_exception);
ulan@chromium.org65a89c22012-02-14 11:46:07 +00005180 // Set external caught exception to false.
5181 Isolate* isolate = masm->isolate();
5182 ExternalReference external_caught(Isolate::kExternalCaughtExceptionAddress,
5183 isolate);
5184 __ mov(Operand::StaticVariable(external_caught), Immediate(false));
5185
5186 // Set pending exception and eax to out of memory exception.
5187 ExternalReference pending_exception(Isolate::kPendingExceptionAddress,
5188 isolate);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00005189 Label already_have_failure;
5190 JumpIfOOM(masm, eax, ecx, &already_have_failure);
5191 __ mov(eax, reinterpret_cast<int32_t>(Failure::OutOfMemoryException(0x1)));
5192 __ bind(&already_have_failure);
ulan@chromium.org65a89c22012-02-14 11:46:07 +00005193 __ mov(Operand::StaticVariable(pending_exception), eax);
5194 // Fall through to the next label.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005195
5196 __ bind(&throw_termination_exception);
ulan@chromium.org65a89c22012-02-14 11:46:07 +00005197 __ ThrowUncatchable(eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005198
5199 __ bind(&throw_normal_exception);
ulan@chromium.org65a89c22012-02-14 11:46:07 +00005200 __ Throw(eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005201}
5202
5203
5204void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00005205 Label invoke, handler_entry, exit;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005206 Label not_outermost_js, not_outermost_js_2;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005207
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00005208 // Set up frame.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005209 __ push(ebp);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005210 __ mov(ebp, esp);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005211
5212 // Push marker in two places.
5213 int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
5214 __ push(Immediate(Smi::FromInt(marker))); // context slot
5215 __ push(Immediate(Smi::FromInt(marker))); // function slot
5216 // Save callee-saved registers (C calling conventions).
5217 __ push(edi);
5218 __ push(esi);
5219 __ push(ebx);
5220
5221 // Save copies of the top frame descriptor on the stack.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00005222 ExternalReference c_entry_fp(Isolate::kCEntryFPAddress, masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00005223 __ push(Operand::StaticVariable(c_entry_fp));
5224
ricow@chromium.org65fae842010-08-25 15:26:24 +00005225 // If this is the outermost JS call, set js_entry_sp value.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00005226 ExternalReference js_entry_sp(Isolate::kJSEntrySPAddress,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005227 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00005228 __ cmp(Operand::StaticVariable(js_entry_sp), Immediate(0));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005229 __ j(not_equal, &not_outermost_js, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005230 __ mov(Operand::StaticVariable(js_entry_sp), ebp);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005231 __ push(Immediate(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME)));
danno@chromium.org2c26cb12012-05-03 09:06:43 +00005232 __ jmp(&invoke, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005233 __ bind(&not_outermost_js);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005234 __ push(Immediate(Smi::FromInt(StackFrame::INNER_JSENTRY_FRAME)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005235
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00005236 // Jump to a faked try block that does the invoke, with a faked catch
5237 // block that sets the pending exception.
5238 __ jmp(&invoke);
5239 __ bind(&handler_entry);
5240 handler_offset_ = handler_entry.pos();
5241 // Caught exception: Store result (exception) in the pending exception
5242 // field in the JSEnv and return a failure sentinel.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00005243 ExternalReference pending_exception(Isolate::kPendingExceptionAddress,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005244 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00005245 __ mov(Operand::StaticVariable(pending_exception), eax);
5246 __ mov(eax, reinterpret_cast<int32_t>(Failure::Exception()));
5247 __ jmp(&exit);
5248
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00005249 // Invoke: Link this frame into the handler chain. There's only one
5250 // handler block in this code object, so its index is 0.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005251 __ bind(&invoke);
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00005252 __ PushTryHandler(StackHandler::JS_ENTRY, 0);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005253
5254 // Clear any pending exceptions.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005255 __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005256 __ mov(Operand::StaticVariable(pending_exception), edx);
5257
5258 // Fake a receiver (NULL).
5259 __ push(Immediate(0)); // receiver
5260
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00005261 // Invoke the function by calling through JS entry trampoline builtin and
5262 // pop the faked function when we return. Notice that we cannot store a
5263 // reference to the trampoline code directly in this stub, because the
5264 // builtin stubs may not have been generated yet.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005265 if (is_construct) {
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00005266 ExternalReference construct_entry(Builtins::kJSConstructEntryTrampoline,
5267 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00005268 __ mov(edx, Immediate(construct_entry));
5269 } else {
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005270 ExternalReference entry(Builtins::kJSEntryTrampoline,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005271 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00005272 __ mov(edx, Immediate(entry));
5273 }
5274 __ mov(edx, Operand(edx, 0)); // deref address
5275 __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005276 __ call(edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005277
5278 // Unlink this frame from the handler chain.
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005279 __ PopTryHandler();
ricow@chromium.org65fae842010-08-25 15:26:24 +00005280
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005281 __ bind(&exit);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005282 // Check if the current stack frame is marked as the outermost JS frame.
5283 __ pop(ebx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005284 __ cmp(ebx, Immediate(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005285 __ j(not_equal, &not_outermost_js_2);
5286 __ mov(Operand::StaticVariable(js_entry_sp), Immediate(0));
5287 __ bind(&not_outermost_js_2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005288
5289 // Restore the top frame descriptor from the stack.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005290 __ pop(Operand::StaticVariable(ExternalReference(
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00005291 Isolate::kCEntryFPAddress,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00005292 masm->isolate())));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005293
5294 // Restore callee-saved registers (C calling conventions).
5295 __ pop(ebx);
5296 __ pop(esi);
5297 __ pop(edi);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005298 __ add(esp, Immediate(2 * kPointerSize)); // remove markers
ricow@chromium.org65fae842010-08-25 15:26:24 +00005299
5300 // Restore frame pointer and return.
5301 __ pop(ebp);
5302 __ ret(0);
5303}
5304
5305
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005306// Generate stub code for instanceof.
5307// This code can patch a call site inlined cache of the instance of check,
5308// which looks like this.
5309//
5310// 81 ff XX XX XX XX cmp edi, <the hole, patched to a map>
5311// 75 0a jne <some near label>
5312// b8 XX XX XX XX mov eax, <the hole, patched to either true or false>
5313//
5314// If call site patching is requested the stack will have the delta from the
5315// return address to the cmp instruction just below the return address. This
5316// also means that call site patching can only take place with arguments in
5317// registers. TOS looks like this when call site patching is requested
5318//
5319// esp[0] : return address
5320// esp[4] : delta from return address to cmp instruction
5321//
ricow@chromium.org65fae842010-08-25 15:26:24 +00005322void InstanceofStub::Generate(MacroAssembler* masm) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005323 // Call site inlining and patching implies arguments in registers.
5324 ASSERT(HasArgsInRegisters() || !HasCallSiteInlineCheck());
5325
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005326 // Fixed register usage throughout the stub.
5327 Register object = eax; // Object (lhs).
5328 Register map = ebx; // Map of the object.
5329 Register function = edx; // Function (rhs).
5330 Register prototype = edi; // Prototype of the function.
5331 Register scratch = ecx;
5332
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005333 // Constants describing the call site code to patch.
5334 static const int kDeltaToCmpImmediate = 2;
5335 static const int kDeltaToMov = 8;
5336 static const int kDeltaToMovImmediate = 9;
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00005337 static const int8_t kCmpEdiOperandByte1 = BitCast<int8_t, uint8_t>(0x3b);
5338 static const int8_t kCmpEdiOperandByte2 = BitCast<int8_t, uint8_t>(0x3d);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005339 static const int8_t kMovEaxImmediateByte = BitCast<int8_t, uint8_t>(0xb8);
5340
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005341 ExternalReference roots_array_start =
5342 ExternalReference::roots_array_start(masm->isolate());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005343
5344 ASSERT_EQ(object.code(), InstanceofStub::left().code());
5345 ASSERT_EQ(function.code(), InstanceofStub::right().code());
5346
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005347 // Get the object and function - they are always both needed.
5348 Label slow, not_js_object;
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005349 if (!HasArgsInRegisters()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005350 __ mov(object, Operand(esp, 2 * kPointerSize));
5351 __ mov(function, Operand(esp, 1 * kPointerSize));
5352 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00005353
5354 // Check that the left hand is a JS object.
whesse@chromium.org7b260152011-06-20 15:33:18 +00005355 __ JumpIfSmi(object, &not_js_object);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005356 __ IsObjectJSObjectType(object, map, scratch, &not_js_object);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005357
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005358 // If there is a call site cache don't look in the global cache, but do the
5359 // real lookup and update the call site cache.
5360 if (!HasCallSiteInlineCheck()) {
5361 // Look up the function and the map in the instanceof cache.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005362 Label miss;
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005363 __ mov(scratch, Immediate(Heap::kInstanceofCacheFunctionRootIndex));
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005364 __ cmp(function, Operand::StaticArray(scratch,
5365 times_pointer_size,
5366 roots_array_start));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005367 __ j(not_equal, &miss, Label::kNear);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005368 __ mov(scratch, Immediate(Heap::kInstanceofCacheMapRootIndex));
5369 __ cmp(map, Operand::StaticArray(
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005370 scratch, times_pointer_size, roots_array_start));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005371 __ j(not_equal, &miss, Label::kNear);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005372 __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
5373 __ mov(eax, Operand::StaticArray(
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005374 scratch, times_pointer_size, roots_array_start));
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005375 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
5376 __ bind(&miss);
5377 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00005378
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005379 // Get the prototype of the function.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005380 __ TryGetFunctionPrototype(function, prototype, scratch, &slow, true);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005381
5382 // Check that the function prototype is a JS object.
whesse@chromium.org7b260152011-06-20 15:33:18 +00005383 __ JumpIfSmi(prototype, &slow);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005384 __ IsObjectJSObjectType(prototype, scratch, scratch, &slow);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005385
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005386 // Update the global instanceof or call site inlined cache with the current
5387 // map and function. The cached answer will be set when it is known below.
5388 if (!HasCallSiteInlineCheck()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005389 __ mov(scratch, Immediate(Heap::kInstanceofCacheMapRootIndex));
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005390 __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_array_start),
5391 map);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005392 __ mov(scratch, Immediate(Heap::kInstanceofCacheFunctionRootIndex));
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005393 __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_array_start),
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005394 function);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005395 } else {
5396 // The constants for the code patching are based on no push instructions
5397 // at the call site.
5398 ASSERT(HasArgsInRegisters());
5399 // Get return address and delta to inlined map check.
5400 __ mov(scratch, Operand(esp, 0 * kPointerSize));
5401 __ sub(scratch, Operand(esp, 1 * kPointerSize));
5402 if (FLAG_debug_code) {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00005403 __ cmpb(Operand(scratch, 0), kCmpEdiOperandByte1);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005404 __ Assert(equal, "InstanceofStub unexpected call site cache (cmp 1)");
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00005405 __ cmpb(Operand(scratch, 1), kCmpEdiOperandByte2);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005406 __ Assert(equal, "InstanceofStub unexpected call site cache (cmp 2)");
5407 }
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00005408 __ mov(scratch, Operand(scratch, kDeltaToCmpImmediate));
5409 __ mov(Operand(scratch, 0), map);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005410 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00005411
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005412 // Loop through the prototype chain of the object looking for the function
5413 // prototype.
5414 __ mov(scratch, FieldOperand(map, Map::kPrototypeOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005415 Label loop, is_instance, is_not_instance;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005416 __ bind(&loop);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005417 __ cmp(scratch, prototype);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005418 __ j(equal, &is_instance, Label::kNear);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005419 Factory* factory = masm->isolate()->factory();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005420 __ cmp(scratch, Immediate(factory->null_value()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005421 __ j(equal, &is_not_instance, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005422 __ mov(scratch, FieldOperand(scratch, HeapObject::kMapOffset));
5423 __ mov(scratch, FieldOperand(scratch, Map::kPrototypeOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005424 __ jmp(&loop);
5425
5426 __ bind(&is_instance);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005427 if (!HasCallSiteInlineCheck()) {
5428 __ Set(eax, Immediate(0));
5429 __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
5430 __ mov(Operand::StaticArray(scratch,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005431 times_pointer_size, roots_array_start), eax);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005432 } else {
5433 // Get return address and delta to inlined map check.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005434 __ mov(eax, factory->true_value());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005435 __ mov(scratch, Operand(esp, 0 * kPointerSize));
5436 __ sub(scratch, Operand(esp, 1 * kPointerSize));
5437 if (FLAG_debug_code) {
5438 __ cmpb(Operand(scratch, kDeltaToMov), kMovEaxImmediateByte);
5439 __ Assert(equal, "InstanceofStub unexpected call site cache (mov)");
5440 }
5441 __ mov(Operand(scratch, kDeltaToMovImmediate), eax);
5442 if (!ReturnTrueFalseObject()) {
5443 __ Set(eax, Immediate(0));
5444 }
5445 }
5446 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005447
5448 __ bind(&is_not_instance);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005449 if (!HasCallSiteInlineCheck()) {
5450 __ Set(eax, Immediate(Smi::FromInt(1)));
5451 __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
5452 __ mov(Operand::StaticArray(
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005453 scratch, times_pointer_size, roots_array_start), eax);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005454 } else {
5455 // Get return address and delta to inlined map check.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005456 __ mov(eax, factory->false_value());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005457 __ mov(scratch, Operand(esp, 0 * kPointerSize));
5458 __ sub(scratch, Operand(esp, 1 * kPointerSize));
5459 if (FLAG_debug_code) {
5460 __ cmpb(Operand(scratch, kDeltaToMov), kMovEaxImmediateByte);
5461 __ Assert(equal, "InstanceofStub unexpected call site cache (mov)");
5462 }
5463 __ mov(Operand(scratch, kDeltaToMovImmediate), eax);
5464 if (!ReturnTrueFalseObject()) {
5465 __ Set(eax, Immediate(Smi::FromInt(1)));
5466 }
5467 }
5468 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005469
5470 Label object_not_null, object_not_null_or_smi;
5471 __ bind(&not_js_object);
5472 // Before null, smi and string value checks, check that the rhs is a function
5473 // as for a non-function rhs an exception needs to be thrown.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005474 __ JumpIfSmi(function, &slow, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005475 __ CmpObjectType(function, JS_FUNCTION_TYPE, scratch);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005476 __ j(not_equal, &slow, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005477
5478 // Null is not instance of anything.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005479 __ cmp(object, factory->null_value());
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005480 __ j(not_equal, &object_not_null, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005481 __ Set(eax, Immediate(Smi::FromInt(1)));
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005482 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005483
5484 __ bind(&object_not_null);
5485 // Smi values is not instance of anything.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005486 __ JumpIfNotSmi(object, &object_not_null_or_smi, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005487 __ Set(eax, Immediate(Smi::FromInt(1)));
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005488 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005489
5490 __ bind(&object_not_null_or_smi);
5491 // String values is not instance of anything.
5492 Condition is_string = masm->IsObjectStringType(object, scratch, scratch);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005493 __ j(NegateCondition(is_string), &slow, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005494 __ Set(eax, Immediate(Smi::FromInt(1)));
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005495 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005496
5497 // Slow-case: Go through the JavaScript implementation.
5498 __ bind(&slow);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005499 if (!ReturnTrueFalseObject()) {
5500 // Tail call the builtin which returns 0 or 1.
5501 if (HasArgsInRegisters()) {
5502 // Push arguments below return address.
5503 __ pop(scratch);
5504 __ push(object);
5505 __ push(function);
5506 __ push(scratch);
5507 }
5508 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
5509 } else {
5510 // Call the builtin and convert 0/1 to true/false.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005511 {
5512 FrameScope scope(masm, StackFrame::INTERNAL);
5513 __ push(object);
5514 __ push(function);
5515 __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION);
5516 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005517 Label true_value, done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005518 __ test(eax, eax);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005519 __ j(zero, &true_value, Label::kNear);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005520 __ mov(eax, factory->false_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005521 __ jmp(&done, Label::kNear);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005522 __ bind(&true_value);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005523 __ mov(eax, factory->true_value());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005524 __ bind(&done);
5525 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005526 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00005527}
5528
5529
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005530Register InstanceofStub::left() { return eax; }
5531
5532
5533Register InstanceofStub::right() { return edx; }
5534
5535
ricow@chromium.org65fae842010-08-25 15:26:24 +00005536// -------------------------------------------------------------------------
5537// StringCharCodeAtGenerator
5538
5539void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00005540 // If the receiver is a smi trigger the non-string case.
5541 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005542 __ JumpIfSmi(object_, receiver_not_string_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005543
5544 // Fetch the instance type of the receiver into result register.
5545 __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset));
5546 __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
5547 // If the receiver is not a string trigger the non-string case.
5548 __ test(result_, Immediate(kIsNotStringMask));
5549 __ j(not_zero, receiver_not_string_);
5550
5551 // If the index is non-smi trigger the non-smi case.
5552 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005553 __ JumpIfNotSmi(index_, &index_not_smi_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005554 __ bind(&got_smi_index_);
5555
5556 // Check for index out of range.
danno@chromium.orgc612e022011-11-10 11:38:15 +00005557 __ cmp(index_, FieldOperand(object_, String::kLengthOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005558 __ j(above_equal, index_out_of_range_);
5559
danno@chromium.orgc612e022011-11-10 11:38:15 +00005560 __ SmiUntag(index_);
erikcorry0ad885c2011-11-21 13:51:57 +00005561
5562 Factory* factory = masm->isolate()->factory();
5563 StringCharLoadGenerator::Generate(
5564 masm, factory, object_, index_, result_, &call_runtime_);
5565
ricow@chromium.org65fae842010-08-25 15:26:24 +00005566 __ SmiTag(result_);
5567 __ bind(&exit_);
5568}
5569
5570
5571void StringCharCodeAtGenerator::GenerateSlow(
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005572 MacroAssembler* masm,
5573 const RuntimeCallHelper& call_helper) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00005574 __ Abort("Unexpected fallthrough to CharCodeAt slow case");
5575
5576 // Index is not a smi.
5577 __ bind(&index_not_smi_);
5578 // If index is a heap number, try converting it to an integer.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005579 __ CheckMap(index_,
5580 masm->isolate()->factory()->heap_number_map(),
5581 index_not_number_,
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005582 DONT_DO_SMI_CHECK);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005583 call_helper.BeforeCall(masm);
5584 __ push(object_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005585 __ push(index_); // Consumed by runtime conversion function.
5586 if (index_flags_ == STRING_INDEX_IS_NUMBER) {
5587 __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1);
5588 } else {
5589 ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
5590 // NumberToSmi discards numbers that are not exact integers.
5591 __ CallRuntime(Runtime::kNumberToSmi, 1);
5592 }
danno@chromium.orgc612e022011-11-10 11:38:15 +00005593 if (!index_.is(eax)) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00005594 // Save the conversion result before the pop instructions below
5595 // have a chance to overwrite it.
danno@chromium.orgc612e022011-11-10 11:38:15 +00005596 __ mov(index_, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005597 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00005598 __ pop(object_);
5599 // Reload the instance type.
5600 __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset));
5601 __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
5602 call_helper.AfterCall(masm);
5603 // If index is still not a smi, it must be out of range.
5604 STATIC_ASSERT(kSmiTag == 0);
danno@chromium.orgc612e022011-11-10 11:38:15 +00005605 __ JumpIfNotSmi(index_, index_out_of_range_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005606 // Otherwise, return to the fast path.
5607 __ jmp(&got_smi_index_);
5608
5609 // Call runtime. We get here when the receiver is a string and the
5610 // index is a number, but the code of getting the actual character
5611 // is too complex (e.g., when the string needs to be flattened).
5612 __ bind(&call_runtime_);
5613 call_helper.BeforeCall(masm);
5614 __ push(object_);
erikcorry0ad885c2011-11-21 13:51:57 +00005615 __ SmiTag(index_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005616 __ push(index_);
5617 __ CallRuntime(Runtime::kStringCharCodeAt, 2);
5618 if (!result_.is(eax)) {
5619 __ mov(result_, eax);
5620 }
5621 call_helper.AfterCall(masm);
5622 __ jmp(&exit_);
5623
5624 __ Abort("Unexpected fallthrough from CharCodeAt slow case");
5625}
5626
5627
5628// -------------------------------------------------------------------------
5629// StringCharFromCodeGenerator
5630
5631void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
5632 // Fast case of Heap::LookupSingleCharacterStringFromCode.
5633 STATIC_ASSERT(kSmiTag == 0);
5634 STATIC_ASSERT(kSmiShiftSize == 0);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00005635 ASSERT(IsPowerOf2(String::kMaxOneByteCharCode + 1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005636 __ test(code_,
5637 Immediate(kSmiTagMask |
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00005638 ((~String::kMaxOneByteCharCode) << kSmiTagSize)));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005639 __ j(not_zero, &slow_case_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005640
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005641 Factory* factory = masm->isolate()->factory();
5642 __ Set(result_, Immediate(factory->single_character_string_cache()));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005643 STATIC_ASSERT(kSmiTag == 0);
5644 STATIC_ASSERT(kSmiTagSize == 1);
5645 STATIC_ASSERT(kSmiShiftSize == 0);
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005646 // At this point code register contains smi tagged ASCII char code.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005647 __ mov(result_, FieldOperand(result_,
5648 code_, times_half_pointer_size,
5649 FixedArray::kHeaderSize));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005650 __ cmp(result_, factory->undefined_value());
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005651 __ j(equal, &slow_case_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005652 __ bind(&exit_);
5653}
5654
5655
5656void StringCharFromCodeGenerator::GenerateSlow(
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005657 MacroAssembler* masm,
5658 const RuntimeCallHelper& call_helper) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00005659 __ Abort("Unexpected fallthrough to CharFromCode slow case");
5660
5661 __ bind(&slow_case_);
5662 call_helper.BeforeCall(masm);
5663 __ push(code_);
5664 __ CallRuntime(Runtime::kCharFromCode, 1);
5665 if (!result_.is(eax)) {
5666 __ mov(result_, eax);
5667 }
5668 call_helper.AfterCall(masm);
5669 __ jmp(&exit_);
5670
5671 __ Abort("Unexpected fallthrough from CharFromCode slow case");
5672}
5673
5674
ricow@chromium.org65fae842010-08-25 15:26:24 +00005675void StringAddStub::Generate(MacroAssembler* masm) {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005676 Label call_runtime, call_builtin;
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005677 Builtins::JavaScript builtin_id = Builtins::ADD;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005678
5679 // Load the two arguments.
5680 __ mov(eax, Operand(esp, 2 * kPointerSize)); // First argument.
5681 __ mov(edx, Operand(esp, 1 * kPointerSize)); // Second argument.
5682
5683 // Make sure that both arguments are strings if not known in advance.
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00005684 if ((flags_ & NO_STRING_ADD_FLAGS) != 0) {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005685 __ JumpIfSmi(eax, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005686 __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ebx);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005687 __ j(above_equal, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005688
5689 // First argument is a a string, test second.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005690 __ JumpIfSmi(edx, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005691 __ CmpObjectType(edx, FIRST_NONSTRING_TYPE, ebx);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005692 __ j(above_equal, &call_runtime);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005693 } else {
5694 // Here at least one of the arguments is definitely a string.
5695 // We convert the one that is not known to be a string.
5696 if ((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) == 0) {
5697 ASSERT((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) != 0);
5698 GenerateConvertArgument(masm, 2 * kPointerSize, eax, ebx, ecx, edi,
5699 &call_builtin);
5700 builtin_id = Builtins::STRING_ADD_RIGHT;
5701 } else if ((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) == 0) {
5702 ASSERT((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) != 0);
5703 GenerateConvertArgument(masm, 1 * kPointerSize, edx, ebx, ecx, edi,
5704 &call_builtin);
5705 builtin_id = Builtins::STRING_ADD_LEFT;
5706 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00005707 }
5708
5709 // Both arguments are strings.
5710 // eax: first string
5711 // edx: second string
5712 // Check if either of the strings are empty. In that case return the other.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005713 Label second_not_zero_length, both_not_zero_length;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005714 __ mov(ecx, FieldOperand(edx, String::kLengthOffset));
5715 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005716 __ test(ecx, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005717 __ j(not_zero, &second_not_zero_length, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005718 // Second string is empty, result is first string which is already in eax.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005719 Counters* counters = masm->isolate()->counters();
5720 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005721 __ ret(2 * kPointerSize);
5722 __ bind(&second_not_zero_length);
5723 __ mov(ebx, FieldOperand(eax, String::kLengthOffset));
5724 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005725 __ test(ebx, ebx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005726 __ j(not_zero, &both_not_zero_length, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005727 // First string is empty, result is second string which is in edx.
5728 __ mov(eax, edx);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005729 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005730 __ ret(2 * kPointerSize);
5731
5732 // Both strings are non-empty.
5733 // eax: first string
5734 // ebx: length of first string as a smi
5735 // ecx: length of second string as a smi
5736 // edx: second string
5737 // Look at the length of the result of adding the two strings.
5738 Label string_add_flat_result, longer_than_two;
5739 __ bind(&both_not_zero_length);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005740 __ add(ebx, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005741 STATIC_ASSERT(Smi::kMaxValue == String::kMaxLength);
5742 // Handle exceptionally long strings in the runtime system.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005743 __ j(overflow, &call_runtime);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005744 // Use the string table when adding two one character strings, as it
5745 // helps later optimizations to return an internalized string here.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005746 __ cmp(ebx, Immediate(Smi::FromInt(2)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005747 __ j(not_equal, &longer_than_two);
5748
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005749 // Check that both strings are non-external ASCII strings.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005750 __ JumpIfNotBothSequentialAsciiStrings(eax, edx, ebx, ecx, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005751
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005752 // Get the two characters forming the new string.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005753 __ movzx_b(ebx, FieldOperand(eax, SeqOneByteString::kHeaderSize));
5754 __ movzx_b(ecx, FieldOperand(edx, SeqOneByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005755
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005756 // Try to lookup two character string in string table. If it is not found
ricow@chromium.org65fae842010-08-25 15:26:24 +00005757 // just allocate a new one.
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005758 Label make_two_character_string, make_two_character_string_no_reload;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005759 StringHelper::GenerateTwoCharacterStringTableProbe(
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005760 masm, ebx, ecx, eax, edx, edi,
5761 &make_two_character_string_no_reload, &make_two_character_string);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005762 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005763 __ ret(2 * kPointerSize);
5764
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005765 // Allocate a two character string.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005766 __ bind(&make_two_character_string);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005767 // Reload the arguments.
5768 __ mov(eax, Operand(esp, 2 * kPointerSize)); // First argument.
5769 __ mov(edx, Operand(esp, 1 * kPointerSize)); // Second argument.
5770 // Get the two characters forming the new string.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005771 __ movzx_b(ebx, FieldOperand(eax, SeqOneByteString::kHeaderSize));
5772 __ movzx_b(ecx, FieldOperand(edx, SeqOneByteString::kHeaderSize));
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005773 __ bind(&make_two_character_string_no_reload);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005774 __ IncrementCounter(counters->string_add_make_two_char(), 1);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005775 __ AllocateAsciiString(eax, 2, edi, edx, &call_runtime);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005776 // Pack both characters in ebx.
5777 __ shl(ecx, kBitsPerByte);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005778 __ or_(ebx, ecx);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005779 // Set the characters in the new string.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005780 __ mov_w(FieldOperand(eax, SeqOneByteString::kHeaderSize), ebx);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005781 __ IncrementCounter(counters->string_add_native(), 1);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005782 __ ret(2 * kPointerSize);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005783
5784 __ bind(&longer_than_two);
5785 // Check if resulting string will be flat.
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005786 __ cmp(ebx, Immediate(Smi::FromInt(ConsString::kMinLength)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005787 __ j(below, &string_add_flat_result);
5788
5789 // If result is not supposed to be flat allocate a cons string object. If both
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005790 // strings are ASCII the result is an ASCII cons string.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005791 Label non_ascii, allocated, ascii_data;
5792 __ mov(edi, FieldOperand(eax, HeapObject::kMapOffset));
5793 __ movzx_b(ecx, FieldOperand(edi, Map::kInstanceTypeOffset));
5794 __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
5795 __ movzx_b(edi, FieldOperand(edi, Map::kInstanceTypeOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005796 __ and_(ecx, edi);
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +00005797 STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00005798 STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
5799 __ test(ecx, Immediate(kStringEncodingMask));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005800 __ j(zero, &non_ascii);
5801 __ bind(&ascii_data);
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005802 // Allocate an ASCII cons string.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005803 __ AllocateAsciiConsString(ecx, edi, no_reg, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005804 __ bind(&allocated);
5805 // Fill the fields of the cons string.
svenpanne@chromium.orgc859c4f2012-10-15 11:51:39 +00005806 __ AssertSmi(ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005807 __ mov(FieldOperand(ecx, ConsString::kLengthOffset), ebx);
5808 __ mov(FieldOperand(ecx, ConsString::kHashFieldOffset),
5809 Immediate(String::kEmptyHashField));
ulan@chromium.org57ff8812013-05-10 08:16:55 +00005810
5811 Label skip_write_barrier, after_writing;
5812 ExternalReference high_promotion_mode = ExternalReference::
5813 new_space_high_promotion_mode_active_address(masm->isolate());
5814 __ test(Operand::StaticVariable(high_promotion_mode), Immediate(1));
5815 __ j(zero, &skip_write_barrier);
5816
5817 __ mov(FieldOperand(ecx, ConsString::kFirstOffset), eax);
5818 __ RecordWriteField(ecx,
5819 ConsString::kFirstOffset,
5820 eax,
5821 ebx,
5822 kDontSaveFPRegs);
5823 __ mov(FieldOperand(ecx, ConsString::kSecondOffset), edx);
5824 __ RecordWriteField(ecx,
5825 ConsString::kSecondOffset,
5826 edx,
5827 ebx,
5828 kDontSaveFPRegs);
5829 __ jmp(&after_writing);
5830
5831 __ bind(&skip_write_barrier);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005832 __ mov(FieldOperand(ecx, ConsString::kFirstOffset), eax);
5833 __ mov(FieldOperand(ecx, ConsString::kSecondOffset), edx);
ulan@chromium.org57ff8812013-05-10 08:16:55 +00005834
5835 __ bind(&after_writing);
5836
ricow@chromium.org65fae842010-08-25 15:26:24 +00005837 __ mov(eax, ecx);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005838 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005839 __ ret(2 * kPointerSize);
5840 __ bind(&non_ascii);
5841 // At least one of the strings is two-byte. Check whether it happens
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005842 // to contain only one byte characters.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005843 // ecx: first instance type AND second instance type.
5844 // edi: second instance type.
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005845 __ test(ecx, Immediate(kOneByteDataHintMask));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005846 __ j(not_zero, &ascii_data);
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00005847 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
5848 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
5849 __ xor_(edi, ecx);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005850 STATIC_ASSERT(kOneByteStringTag != 0 && kOneByteDataHintTag != 0);
5851 __ and_(edi, kOneByteStringTag | kOneByteDataHintTag);
5852 __ cmp(edi, kOneByteStringTag | kOneByteDataHintTag);
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00005853 __ j(equal, &ascii_data);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005854 // Allocate a two byte cons string.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005855 __ AllocateTwoByteConsString(ecx, edi, no_reg, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005856 __ jmp(&allocated);
5857
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005858 // We cannot encounter sliced strings or cons strings here since:
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005859 STATIC_ASSERT(SlicedString::kMinLength >= ConsString::kMinLength);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005860 // Handle creating a flat result from either external or sequential strings.
5861 // Locate the first characters' locations.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005862 // eax: first string
5863 // ebx: length of resulting flat string as a smi
5864 // edx: second string
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005865 Label first_prepared, second_prepared;
5866 Label first_is_sequential, second_is_sequential;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005867 __ bind(&string_add_flat_result);
5868 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
5869 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005870 // ecx: instance type of first string
5871 STATIC_ASSERT(kSeqStringTag == 0);
5872 __ test_b(ecx, kStringRepresentationMask);
5873 __ j(zero, &first_is_sequential, Label::kNear);
5874 // Rule out short external string and load string resource.
5875 STATIC_ASSERT(kShortExternalStringTag != 0);
5876 __ test_b(ecx, kShortExternalStringMask);
5877 __ j(not_zero, &call_runtime);
5878 __ mov(eax, FieldOperand(eax, ExternalString::kResourceDataOffset));
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005879 STATIC_ASSERT(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005880 __ jmp(&first_prepared, Label::kNear);
5881 __ bind(&first_is_sequential);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005882 __ add(eax, Immediate(SeqOneByteString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005883 __ bind(&first_prepared);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005884
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005885 __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
5886 __ movzx_b(edi, FieldOperand(edi, Map::kInstanceTypeOffset));
5887 // Check whether both strings have same encoding.
5888 // edi: instance type of second string
5889 __ xor_(ecx, edi);
5890 __ test_b(ecx, kStringEncodingMask);
5891 __ j(not_zero, &call_runtime);
5892 STATIC_ASSERT(kSeqStringTag == 0);
5893 __ test_b(edi, kStringRepresentationMask);
5894 __ j(zero, &second_is_sequential, Label::kNear);
5895 // Rule out short external string and load string resource.
5896 STATIC_ASSERT(kShortExternalStringTag != 0);
5897 __ test_b(edi, kShortExternalStringMask);
5898 __ j(not_zero, &call_runtime);
5899 __ mov(edx, FieldOperand(edx, ExternalString::kResourceDataOffset));
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005900 STATIC_ASSERT(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005901 __ jmp(&second_prepared, Label::kNear);
5902 __ bind(&second_is_sequential);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005903 __ add(edx, Immediate(SeqOneByteString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005904 __ bind(&second_prepared);
5905
5906 // Push the addresses of both strings' first characters onto the stack.
5907 __ push(edx);
5908 __ push(eax);
5909
5910 Label non_ascii_string_add_flat_result, call_runtime_drop_two;
5911 // edi: instance type of second string
5912 // First string and second string have the same encoding.
5913 STATIC_ASSERT(kTwoByteStringTag == 0);
5914 __ test_b(edi, kStringEncodingMask);
5915 __ j(zero, &non_ascii_string_add_flat_result);
5916
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005917 // Both strings are ASCII strings.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005918 // ebx: length of resulting flat string as a smi
5919 __ SmiUntag(ebx);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005920 __ AllocateAsciiString(eax, ebx, ecx, edx, edi, &call_runtime_drop_two);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005921 // eax: result string
5922 __ mov(ecx, eax);
5923 // Locate first character of result.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005924 __ add(ecx, Immediate(SeqOneByteString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005925 // Load first argument's length and first character location. Account for
5926 // values currently on the stack when fetching arguments from it.
5927 __ mov(edx, Operand(esp, 4 * kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005928 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
5929 __ SmiUntag(edi);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005930 __ pop(edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005931 // eax: result string
5932 // ecx: first character of result
5933 // edx: first char of first argument
5934 // edi: length of first argument
5935 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005936 // Load second argument's length and first character location. Account for
5937 // values currently on the stack when fetching arguments from it.
5938 __ mov(edx, Operand(esp, 2 * kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005939 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
5940 __ SmiUntag(edi);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005941 __ pop(edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005942 // eax: result string
5943 // ecx: next character of result
5944 // edx: first char of second argument
5945 // edi: length of second argument
5946 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005947 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005948 __ ret(2 * kPointerSize);
5949
5950 // Handle creating a flat two byte result.
5951 // eax: first string - known to be two byte
5952 // ebx: length of resulting flat string as a smi
5953 // edx: second string
5954 __ bind(&non_ascii_string_add_flat_result);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005955 // Both strings are two byte strings.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005956 __ SmiUntag(ebx);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005957 __ AllocateTwoByteString(eax, ebx, ecx, edx, edi, &call_runtime_drop_two);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005958 // eax: result string
5959 __ mov(ecx, eax);
5960 // Locate first character of result.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005961 __ add(ecx, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
5962 // Load second argument's length and first character location. Account for
5963 // values currently on the stack when fetching arguments from it.
5964 __ mov(edx, Operand(esp, 4 * kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005965 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
5966 __ SmiUntag(edi);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005967 __ pop(edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005968 // eax: result string
5969 // ecx: first character of result
5970 // edx: first char of first argument
5971 // edi: length of first argument
5972 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005973 // Load second argument's length and first character location. Account for
5974 // values currently on the stack when fetching arguments from it.
5975 __ mov(edx, Operand(esp, 2 * kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005976 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
5977 __ SmiUntag(edi);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005978 __ pop(edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005979 // eax: result string
5980 // ecx: next character of result
5981 // edx: first char of second argument
5982 // edi: length of second argument
5983 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005984 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005985 __ ret(2 * kPointerSize);
5986
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005987 // Recover stack pointer before jumping to runtime.
5988 __ bind(&call_runtime_drop_two);
5989 __ Drop(2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005990 // Just jump to runtime to add the two strings.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005991 __ bind(&call_runtime);
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00005992 if ((flags_ & ERECT_FRAME) != 0) {
5993 GenerateRegisterArgsPop(masm, ecx);
5994 // Build a frame
5995 {
5996 FrameScope scope(masm, StackFrame::INTERNAL);
5997 GenerateRegisterArgsPush(masm);
5998 __ CallRuntime(Runtime::kStringAdd, 2);
5999 }
6000 __ ret(0);
6001 } else {
6002 __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
6003 }
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00006004
6005 if (call_builtin.is_linked()) {
6006 __ bind(&call_builtin);
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00006007 if ((flags_ & ERECT_FRAME) != 0) {
6008 GenerateRegisterArgsPop(masm, ecx);
6009 // Build a frame
6010 {
6011 FrameScope scope(masm, StackFrame::INTERNAL);
6012 GenerateRegisterArgsPush(masm);
6013 __ InvokeBuiltin(builtin_id, CALL_FUNCTION);
6014 }
6015 __ ret(0);
6016 } else {
6017 __ InvokeBuiltin(builtin_id, JUMP_FUNCTION);
6018 }
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00006019 }
6020}
6021
6022
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00006023void StringAddStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
6024 __ push(eax);
6025 __ push(edx);
6026}
6027
6028
6029void StringAddStub::GenerateRegisterArgsPop(MacroAssembler* masm,
6030 Register temp) {
6031 __ pop(temp);
6032 __ pop(edx);
6033 __ pop(eax);
6034 __ push(temp);
6035}
6036
6037
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00006038void StringAddStub::GenerateConvertArgument(MacroAssembler* masm,
6039 int stack_offset,
6040 Register arg,
6041 Register scratch1,
6042 Register scratch2,
6043 Register scratch3,
6044 Label* slow) {
6045 // First check if the argument is already a string.
6046 Label not_string, done;
whesse@chromium.org7b260152011-06-20 15:33:18 +00006047 __ JumpIfSmi(arg, &not_string);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00006048 __ CmpObjectType(arg, FIRST_NONSTRING_TYPE, scratch1);
6049 __ j(below, &done);
6050
6051 // Check the number to string cache.
6052 Label not_cached;
6053 __ bind(&not_string);
6054 // Puts the cached result into scratch1.
6055 NumberToStringStub::GenerateLookupNumberStringCache(masm,
6056 arg,
6057 scratch1,
6058 scratch2,
6059 scratch3,
6060 false,
6061 &not_cached);
6062 __ mov(arg, scratch1);
6063 __ mov(Operand(esp, stack_offset), arg);
6064 __ jmp(&done);
6065
6066 // Check if the argument is a safe string wrapper.
6067 __ bind(&not_cached);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006068 __ JumpIfSmi(arg, slow);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00006069 __ CmpObjectType(arg, JS_VALUE_TYPE, scratch1); // map -> scratch1.
6070 __ j(not_equal, slow);
6071 __ test_b(FieldOperand(scratch1, Map::kBitField2Offset),
6072 1 << Map::kStringWrapperSafeForDefaultValueOf);
6073 __ j(zero, slow);
6074 __ mov(arg, FieldOperand(arg, JSValue::kValueOffset));
6075 __ mov(Operand(esp, stack_offset), arg);
6076
6077 __ bind(&done);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006078}
6079
6080
6081void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
6082 Register dest,
6083 Register src,
6084 Register count,
6085 Register scratch,
6086 bool ascii) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006087 Label loop;
ricow@chromium.org65fae842010-08-25 15:26:24 +00006088 __ bind(&loop);
6089 // This loop just copies one character at a time, as it is only used for very
6090 // short strings.
6091 if (ascii) {
6092 __ mov_b(scratch, Operand(src, 0));
6093 __ mov_b(Operand(dest, 0), scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006094 __ add(src, Immediate(1));
6095 __ add(dest, Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006096 } else {
6097 __ mov_w(scratch, Operand(src, 0));
6098 __ mov_w(Operand(dest, 0), scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006099 __ add(src, Immediate(2));
6100 __ add(dest, Immediate(2));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006101 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006102 __ sub(count, Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006103 __ j(not_zero, &loop);
6104}
6105
6106
6107void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm,
6108 Register dest,
6109 Register src,
6110 Register count,
6111 Register scratch,
6112 bool ascii) {
6113 // Copy characters using rep movs of doublewords.
6114 // The destination is aligned on a 4 byte boundary because we are
6115 // copying to the beginning of a newly allocated string.
6116 ASSERT(dest.is(edi)); // rep movs destination
6117 ASSERT(src.is(esi)); // rep movs source
6118 ASSERT(count.is(ecx)); // rep movs count
6119 ASSERT(!scratch.is(dest));
6120 ASSERT(!scratch.is(src));
6121 ASSERT(!scratch.is(count));
6122
6123 // Nothing to do for zero characters.
6124 Label done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006125 __ test(count, count);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006126 __ j(zero, &done);
6127
6128 // Make count the number of bytes to copy.
6129 if (!ascii) {
6130 __ shl(count, 1);
6131 }
6132
6133 // Don't enter the rep movs if there are less than 4 bytes to copy.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006134 Label last_bytes;
ricow@chromium.org65fae842010-08-25 15:26:24 +00006135 __ test(count, Immediate(~3));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006136 __ j(zero, &last_bytes, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006137
6138 // Copy from edi to esi using rep movs instruction.
6139 __ mov(scratch, count);
6140 __ sar(count, 2); // Number of doublewords to copy.
6141 __ cld();
6142 __ rep_movs();
6143
6144 // Find number of bytes left.
6145 __ mov(count, scratch);
6146 __ and_(count, 3);
6147
6148 // Check if there are more bytes to copy.
6149 __ bind(&last_bytes);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006150 __ test(count, count);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006151 __ j(zero, &done);
6152
6153 // Copy remaining characters.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006154 Label loop;
ricow@chromium.org65fae842010-08-25 15:26:24 +00006155 __ bind(&loop);
6156 __ mov_b(scratch, Operand(src, 0));
6157 __ mov_b(Operand(dest, 0), scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006158 __ add(src, Immediate(1));
6159 __ add(dest, Immediate(1));
6160 __ sub(count, Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006161 __ j(not_zero, &loop);
6162
6163 __ bind(&done);
6164}
6165
6166
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006167void StringHelper::GenerateTwoCharacterStringTableProbe(MacroAssembler* masm,
ricow@chromium.org65fae842010-08-25 15:26:24 +00006168 Register c1,
6169 Register c2,
6170 Register scratch1,
6171 Register scratch2,
6172 Register scratch3,
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00006173 Label* not_probed,
ricow@chromium.org65fae842010-08-25 15:26:24 +00006174 Label* not_found) {
6175 // Register scratch3 is the general scratch register in this function.
6176 Register scratch = scratch3;
6177
6178 // Make sure that both characters are not digits as such strings has a
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006179 // different hash algorithm. Don't try to look for these in the string table.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006180 Label not_array_index;
ricow@chromium.org65fae842010-08-25 15:26:24 +00006181 __ mov(scratch, c1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006182 __ sub(scratch, Immediate(static_cast<int>('0')));
6183 __ cmp(scratch, Immediate(static_cast<int>('9' - '0')));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006184 __ j(above, &not_array_index, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006185 __ mov(scratch, c2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006186 __ sub(scratch, Immediate(static_cast<int>('0')));
6187 __ cmp(scratch, Immediate(static_cast<int>('9' - '0')));
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00006188 __ j(below_equal, not_probed);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006189
6190 __ bind(&not_array_index);
6191 // Calculate the two character string hash.
6192 Register hash = scratch1;
6193 GenerateHashInit(masm, hash, c1, scratch);
6194 GenerateHashAddCharacter(masm, hash, c2, scratch);
6195 GenerateHashGetHash(masm, hash, scratch);
6196
6197 // Collect the two characters in a register.
6198 Register chars = c1;
6199 __ shl(c2, kBitsPerByte);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006200 __ or_(chars, c2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006201
6202 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
6203 // hash: hash of two character string.
6204
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006205 // Load the string table.
6206 Register string_table = c2;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006207 ExternalReference roots_array_start =
6208 ExternalReference::roots_array_start(masm->isolate());
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006209 __ mov(scratch, Immediate(Heap::kStringTableRootIndex));
6210 __ mov(string_table,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006211 Operand::StaticArray(scratch, times_pointer_size, roots_array_start));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006212
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006213 // Calculate capacity mask from the string table capacity.
ricow@chromium.org65fae842010-08-25 15:26:24 +00006214 Register mask = scratch2;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006215 __ mov(mask, FieldOperand(string_table, StringTable::kCapacityOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006216 __ SmiUntag(mask);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006217 __ sub(mask, Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006218
6219 // Registers
6220 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
6221 // hash: hash of two character string
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006222 // string_table: string table
ricow@chromium.org65fae842010-08-25 15:26:24 +00006223 // mask: capacity mask
6224 // scratch: -
6225
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006226 // Perform a number of probes in the string table.
ricow@chromium.org65fae842010-08-25 15:26:24 +00006227 static const int kProbes = 4;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006228 Label found_in_string_table;
ricow@chromium.org65fae842010-08-25 15:26:24 +00006229 Label next_probe[kProbes], next_probe_pop_mask[kProbes];
danno@chromium.org2c456792011-11-11 12:00:53 +00006230 Register candidate = scratch; // Scratch register contains candidate.
ricow@chromium.org65fae842010-08-25 15:26:24 +00006231 for (int i = 0; i < kProbes; i++) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006232 // Calculate entry in string table.
ricow@chromium.org65fae842010-08-25 15:26:24 +00006233 __ mov(scratch, hash);
6234 if (i > 0) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006235 __ add(scratch, Immediate(StringTable::GetProbeOffset(i)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006236 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006237 __ and_(scratch, mask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006238
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006239 // Load the entry from the string table.
6240 STATIC_ASSERT(StringTable::kEntrySize == 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006241 __ mov(candidate,
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006242 FieldOperand(string_table,
ricow@chromium.org65fae842010-08-25 15:26:24 +00006243 scratch,
6244 times_pointer_size,
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006245 StringTable::kElementsStartOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006246
6247 // If entry is undefined no string with this hash can be found.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006248 Factory* factory = masm->isolate()->factory();
6249 __ cmp(candidate, factory->undefined_value());
ricow@chromium.org65fae842010-08-25 15:26:24 +00006250 __ j(equal, not_found);
danno@chromium.org2c456792011-11-11 12:00:53 +00006251 __ cmp(candidate, factory->the_hole_value());
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +00006252 __ j(equal, &next_probe[i]);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006253
6254 // If length is not 2 the string is not a candidate.
6255 __ cmp(FieldOperand(candidate, String::kLengthOffset),
6256 Immediate(Smi::FromInt(2)));
6257 __ j(not_equal, &next_probe[i]);
6258
6259 // As we are out of registers save the mask on the stack and use that
6260 // register as a temporary.
6261 __ push(mask);
6262 Register temp = mask;
6263
ulan@chromium.org2efb9002012-01-19 15:36:35 +00006264 // Check that the candidate is a non-external ASCII string.
ricow@chromium.org65fae842010-08-25 15:26:24 +00006265 __ mov(temp, FieldOperand(candidate, HeapObject::kMapOffset));
6266 __ movzx_b(temp, FieldOperand(temp, Map::kInstanceTypeOffset));
6267 __ JumpIfInstanceTypeIsNotSequentialAscii(
6268 temp, temp, &next_probe_pop_mask[i]);
6269
6270 // Check if the two characters match.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006271 __ mov(temp, FieldOperand(candidate, SeqOneByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006272 __ and_(temp, 0x0000ffff);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006273 __ cmp(chars, temp);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006274 __ j(equal, &found_in_string_table);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006275 __ bind(&next_probe_pop_mask[i]);
6276 __ pop(mask);
6277 __ bind(&next_probe[i]);
6278 }
6279
6280 // No matching 2 character string found by probing.
6281 __ jmp(not_found);
6282
6283 // Scratch register contains result when we fall through to here.
danno@chromium.org2c456792011-11-11 12:00:53 +00006284 Register result = candidate;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006285 __ bind(&found_in_string_table);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006286 __ pop(mask); // Pop saved mask from the stack.
6287 if (!result.is(eax)) {
6288 __ mov(eax, result);
6289 }
6290}
6291
6292
6293void StringHelper::GenerateHashInit(MacroAssembler* masm,
6294 Register hash,
6295 Register character,
6296 Register scratch) {
rossberg@chromium.orgfab14982012-01-05 15:02:15 +00006297 // hash = (seed + character) + ((seed + character) << 10);
6298 if (Serializer::enabled()) {
6299 ExternalReference roots_array_start =
6300 ExternalReference::roots_array_start(masm->isolate());
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00006301 __ mov(scratch, Immediate(Heap::kHashSeedRootIndex));
rossberg@chromium.orgfab14982012-01-05 15:02:15 +00006302 __ mov(scratch, Operand::StaticArray(scratch,
6303 times_pointer_size,
6304 roots_array_start));
ulan@chromium.org2efb9002012-01-19 15:36:35 +00006305 __ SmiUntag(scratch);
rossberg@chromium.orgfab14982012-01-05 15:02:15 +00006306 __ add(scratch, character);
6307 __ mov(hash, scratch);
6308 __ shl(scratch, 10);
6309 __ add(hash, scratch);
6310 } else {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00006311 int32_t seed = masm->isolate()->heap()->HashSeed();
rossberg@chromium.orgfab14982012-01-05 15:02:15 +00006312 __ lea(scratch, Operand(character, seed));
6313 __ shl(scratch, 10);
6314 __ lea(hash, Operand(scratch, character, times_1, seed));
6315 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00006316 // hash ^= hash >> 6;
6317 __ mov(scratch, hash);
danno@chromium.org2c456792011-11-11 12:00:53 +00006318 __ shr(scratch, 6);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006319 __ xor_(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006320}
6321
6322
6323void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
6324 Register hash,
6325 Register character,
6326 Register scratch) {
6327 // hash += character;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006328 __ add(hash, character);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006329 // hash += hash << 10;
6330 __ mov(scratch, hash);
6331 __ shl(scratch, 10);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006332 __ add(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006333 // hash ^= hash >> 6;
6334 __ mov(scratch, hash);
danno@chromium.org2c456792011-11-11 12:00:53 +00006335 __ shr(scratch, 6);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006336 __ xor_(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006337}
6338
6339
6340void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
6341 Register hash,
6342 Register scratch) {
6343 // hash += hash << 3;
6344 __ mov(scratch, hash);
6345 __ shl(scratch, 3);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006346 __ add(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006347 // hash ^= hash >> 11;
6348 __ mov(scratch, hash);
danno@chromium.org2c456792011-11-11 12:00:53 +00006349 __ shr(scratch, 11);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006350 __ xor_(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006351 // hash += hash << 15;
6352 __ mov(scratch, hash);
6353 __ shl(scratch, 15);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006354 __ add(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006355
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00006356 __ and_(hash, String::kHashBitMask);
danno@chromium.org2c456792011-11-11 12:00:53 +00006357
ricow@chromium.org65fae842010-08-25 15:26:24 +00006358 // if (hash == 0) hash = 27;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006359 Label hash_not_zero;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006360 __ j(not_zero, &hash_not_zero, Label::kNear);
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00006361 __ mov(hash, Immediate(StringHasher::kZeroHash));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006362 __ bind(&hash_not_zero);
6363}
6364
6365
6366void SubStringStub::Generate(MacroAssembler* masm) {
6367 Label runtime;
6368
6369 // Stack frame on entry.
6370 // esp[0]: return address
6371 // esp[4]: to
6372 // esp[8]: from
6373 // esp[12]: string
6374
6375 // Make sure first argument is a string.
6376 __ mov(eax, Operand(esp, 3 * kPointerSize));
6377 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006378 __ JumpIfSmi(eax, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006379 Condition is_string = masm->IsObjectStringType(eax, ebx, ebx);
6380 __ j(NegateCondition(is_string), &runtime);
6381
6382 // eax: string
6383 // ebx: instance type
6384
6385 // Calculate length of sub string using the smi values.
ricow@chromium.org65fae842010-08-25 15:26:24 +00006386 __ mov(ecx, Operand(esp, 1 * kPointerSize)); // To index.
whesse@chromium.org7b260152011-06-20 15:33:18 +00006387 __ JumpIfNotSmi(ecx, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006388 __ mov(edx, Operand(esp, 2 * kPointerSize)); // From index.
whesse@chromium.org7b260152011-06-20 15:33:18 +00006389 __ JumpIfNotSmi(edx, &runtime);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006390 __ sub(ecx, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006391 __ cmp(ecx, FieldOperand(eax, String::kLengthOffset));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006392 Label not_original_string;
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00006393 // Shorter than original string's length: an actual substring.
6394 __ j(below, &not_original_string, Label::kNear);
6395 // Longer than original string's length or negative: unsafe arguments.
6396 __ j(above, &runtime);
6397 // Return original string.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006398 Counters* counters = masm->isolate()->counters();
6399 __ IncrementCounter(counters->sub_string_native(), 1);
6400 __ ret(3 * kPointerSize);
6401 __ bind(&not_original_string);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006402
ulan@chromium.org2e04b582013-02-21 14:06:02 +00006403 Label single_char;
6404 __ cmp(ecx, Immediate(Smi::FromInt(1)));
6405 __ j(equal, &single_char);
6406
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006407 // eax: string
6408 // ebx: instance type
6409 // ecx: sub string length (smi)
6410 // edx: from index (smi)
6411 // Deal with different string types: update the index if necessary
6412 // and put the underlying string into edi.
6413 Label underlying_unpacked, sliced_string, seq_or_external_string;
6414 // If the string is not indirect, it can only be sequential or external.
6415 STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
6416 STATIC_ASSERT(kIsIndirectStringMask != 0);
6417 __ test(ebx, Immediate(kIsIndirectStringMask));
6418 __ j(zero, &seq_or_external_string, Label::kNear);
6419
6420 Factory* factory = masm->isolate()->factory();
6421 __ test(ebx, Immediate(kSlicedNotConsMask));
6422 __ j(not_zero, &sliced_string, Label::kNear);
6423 // Cons string. Check whether it is flat, then fetch first part.
6424 // Flat cons strings have an empty second part.
6425 __ cmp(FieldOperand(eax, ConsString::kSecondOffset),
6426 factory->empty_string());
6427 __ j(not_equal, &runtime);
6428 __ mov(edi, FieldOperand(eax, ConsString::kFirstOffset));
6429 // Update instance type.
6430 __ mov(ebx, FieldOperand(edi, HeapObject::kMapOffset));
6431 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
6432 __ jmp(&underlying_unpacked, Label::kNear);
6433
6434 __ bind(&sliced_string);
6435 // Sliced string. Fetch parent and adjust start index by offset.
6436 __ add(edx, FieldOperand(eax, SlicedString::kOffsetOffset));
6437 __ mov(edi, FieldOperand(eax, SlicedString::kParentOffset));
6438 // Update instance type.
6439 __ mov(ebx, FieldOperand(edi, HeapObject::kMapOffset));
6440 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
6441 __ jmp(&underlying_unpacked, Label::kNear);
6442
6443 __ bind(&seq_or_external_string);
6444 // Sequential or external string. Just move string to the expected register.
6445 __ mov(edi, eax);
6446
6447 __ bind(&underlying_unpacked);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006448
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00006449 if (FLAG_string_slices) {
6450 Label copy_routine;
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00006451 // edi: underlying subject string
ricow@chromium.org7ad65222011-12-19 12:13:11 +00006452 // ebx: instance type of underlying subject string
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006453 // edx: adjusted start index (smi)
6454 // ecx: length (smi)
6455 __ cmp(ecx, Immediate(Smi::FromInt(SlicedString::kMinLength)));
6456 // Short slice. Copy instead of slicing.
6457 __ j(less, &copy_routine);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00006458 // Allocate new sliced string. At this point we do not reload the instance
6459 // type including the string encoding because we simply rely on the info
6460 // provided by the original string. It does not matter if the original
6461 // string's encoding is wrong because we always have to recheck encoding of
6462 // the newly created string's parent anyways due to externalized strings.
6463 Label two_byte_slice, set_slice_header;
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +00006464 STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00006465 STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
6466 __ test(ebx, Immediate(kStringEncodingMask));
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00006467 __ j(zero, &two_byte_slice, Label::kNear);
6468 __ AllocateAsciiSlicedString(eax, ebx, no_reg, &runtime);
6469 __ jmp(&set_slice_header, Label::kNear);
6470 __ bind(&two_byte_slice);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00006471 __ AllocateTwoByteSlicedString(eax, ebx, no_reg, &runtime);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00006472 __ bind(&set_slice_header);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00006473 __ mov(FieldOperand(eax, SlicedString::kLengthOffset), ecx);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00006474 __ mov(FieldOperand(eax, SlicedString::kHashFieldOffset),
6475 Immediate(String::kEmptyHashField));
erik.corry@gmail.combbceb572012-03-09 10:52:05 +00006476 __ mov(FieldOperand(eax, SlicedString::kParentOffset), edi);
6477 __ mov(FieldOperand(eax, SlicedString::kOffsetOffset), edx);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006478 __ IncrementCounter(counters->sub_string_native(), 1);
6479 __ ret(3 * kPointerSize);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00006480
6481 __ bind(&copy_routine);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00006482 }
6483
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006484 // edi: underlying subject string
ricow@chromium.org7ad65222011-12-19 12:13:11 +00006485 // ebx: instance type of underlying subject string
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006486 // edx: adjusted start index (smi)
6487 // ecx: length (smi)
6488 // The subject string can only be external or sequential string of either
6489 // encoding at this point.
6490 Label two_byte_sequential, runtime_drop_two, sequential_string;
6491 STATIC_ASSERT(kExternalStringTag != 0);
6492 STATIC_ASSERT(kSeqStringTag == 0);
6493 __ test_b(ebx, kExternalStringTag);
6494 __ j(zero, &sequential_string);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006495
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006496 // Handle external string.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006497 // Rule out short external strings.
6498 STATIC_CHECK(kShortExternalStringTag != 0);
6499 __ test_b(ebx, kShortExternalStringMask);
6500 __ j(not_zero, &runtime);
6501 __ mov(edi, FieldOperand(edi, ExternalString::kResourceDataOffset));
6502 // Move the pointer so that offset-wise, it looks like a sequential string.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006503 STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006504 __ sub(edi, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
6505
6506 __ bind(&sequential_string);
6507 // Stash away (adjusted) index and (underlying) string.
6508 __ push(edx);
6509 __ push(edi);
6510 __ SmiUntag(ecx);
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +00006511 STATIC_ASSERT((kOneByteStringTag & kStringEncodingMask) != 0);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006512 __ test_b(ebx, kStringEncodingMask);
6513 __ j(zero, &two_byte_sequential);
6514
ulan@chromium.org2efb9002012-01-19 15:36:35 +00006515 // Sequential ASCII string. Allocate the result.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006516 __ AllocateAsciiString(eax, ecx, ebx, edx, edi, &runtime_drop_two);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006517
6518 // eax: result string
6519 // ecx: result string length
6520 __ mov(edx, esi); // esi used by following code.
6521 // Locate first character of result.
6522 __ mov(edi, eax);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006523 __ add(edi, Immediate(SeqOneByteString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006524 // Load string argument and locate character of sub string start.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006525 __ pop(esi);
6526 __ pop(ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006527 __ SmiUntag(ebx);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006528 __ lea(esi, FieldOperand(esi, ebx, times_1, SeqOneByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006529
6530 // eax: result string
6531 // ecx: result length
6532 // edx: original value of esi
6533 // edi: first character of result
6534 // esi: character of sub string start
6535 StringHelper::GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, true);
6536 __ mov(esi, edx); // Restore esi.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006537 __ IncrementCounter(counters->sub_string_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006538 __ ret(3 * kPointerSize);
6539
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006540 __ bind(&two_byte_sequential);
6541 // Sequential two-byte string. Allocate the result.
6542 __ AllocateTwoByteString(eax, ecx, ebx, edx, edi, &runtime_drop_two);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006543
6544 // eax: result string
6545 // ecx: result string length
6546 __ mov(edx, esi); // esi used by following code.
6547 // Locate first character of result.
6548 __ mov(edi, eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006549 __ add(edi,
ricow@chromium.org65fae842010-08-25 15:26:24 +00006550 Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
6551 // Load string argument and locate character of sub string start.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006552 __ pop(esi);
6553 __ pop(ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006554 // As from is a smi it is 2 times the value which matches the size of a two
6555 // byte character.
6556 STATIC_ASSERT(kSmiTag == 0);
6557 STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006558 __ lea(esi, FieldOperand(esi, ebx, times_1, SeqTwoByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006559
6560 // eax: result string
6561 // ecx: result length
6562 // edx: original value of esi
6563 // edi: first character of result
6564 // esi: character of sub string start
6565 StringHelper::GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, false);
6566 __ mov(esi, edx); // Restore esi.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006567 __ IncrementCounter(counters->sub_string_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006568 __ ret(3 * kPointerSize);
6569
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006570 // Drop pushed values on the stack before tail call.
6571 __ bind(&runtime_drop_two);
6572 __ Drop(2);
6573
ricow@chromium.org65fae842010-08-25 15:26:24 +00006574 // Just jump to runtime to create the sub string.
6575 __ bind(&runtime);
6576 __ TailCallRuntime(Runtime::kSubString, 3, 1);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00006577
6578 __ bind(&single_char);
6579 // eax: string
6580 // ebx: instance type
6581 // ecx: sub string length (smi)
6582 // edx: from index (smi)
6583 StringCharAtGenerator generator(
6584 eax, edx, ecx, eax, &runtime, &runtime, &runtime, STRING_INDEX_IS_NUMBER);
6585 generator.GenerateFast(masm);
6586 __ ret(3 * kPointerSize);
6587 generator.SkipSlow(masm, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006588}
6589
6590
lrn@chromium.org1c092762011-05-09 09:42:16 +00006591void StringCompareStub::GenerateFlatAsciiStringEquals(MacroAssembler* masm,
6592 Register left,
6593 Register right,
6594 Register scratch1,
6595 Register scratch2) {
6596 Register length = scratch1;
6597
6598 // Compare lengths.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006599 Label strings_not_equal, check_zero_length;
lrn@chromium.org1c092762011-05-09 09:42:16 +00006600 __ mov(length, FieldOperand(left, String::kLengthOffset));
6601 __ cmp(length, FieldOperand(right, String::kLengthOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006602 __ j(equal, &check_zero_length, Label::kNear);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006603 __ bind(&strings_not_equal);
6604 __ Set(eax, Immediate(Smi::FromInt(NOT_EQUAL)));
6605 __ ret(0);
6606
6607 // Check if the length is zero.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006608 Label compare_chars;
lrn@chromium.org1c092762011-05-09 09:42:16 +00006609 __ bind(&check_zero_length);
6610 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006611 __ test(length, length);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006612 __ j(not_zero, &compare_chars, Label::kNear);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006613 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6614 __ ret(0);
6615
6616 // Compare characters.
6617 __ bind(&compare_chars);
6618 GenerateAsciiCharsCompareLoop(masm, left, right, length, scratch2,
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006619 &strings_not_equal, Label::kNear);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006620
6621 // Characters are equal.
6622 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6623 __ ret(0);
6624}
6625
6626
ricow@chromium.org65fae842010-08-25 15:26:24 +00006627void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
6628 Register left,
6629 Register right,
6630 Register scratch1,
6631 Register scratch2,
6632 Register scratch3) {
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006633 Counters* counters = masm->isolate()->counters();
6634 __ IncrementCounter(counters->string_compare_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006635
6636 // Find minimum length.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006637 Label left_shorter;
ricow@chromium.org65fae842010-08-25 15:26:24 +00006638 __ mov(scratch1, FieldOperand(left, String::kLengthOffset));
6639 __ mov(scratch3, scratch1);
6640 __ sub(scratch3, FieldOperand(right, String::kLengthOffset));
6641
6642 Register length_delta = scratch3;
6643
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006644 __ j(less_equal, &left_shorter, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006645 // Right string is shorter. Change scratch1 to be length of right string.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006646 __ sub(scratch1, length_delta);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006647 __ bind(&left_shorter);
6648
6649 Register min_length = scratch1;
6650
6651 // If either length is zero, just compare lengths.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006652 Label compare_lengths;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006653 __ test(min_length, min_length);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006654 __ j(zero, &compare_lengths, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006655
lrn@chromium.org1c092762011-05-09 09:42:16 +00006656 // Compare characters.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006657 Label result_not_equal;
lrn@chromium.org1c092762011-05-09 09:42:16 +00006658 GenerateAsciiCharsCompareLoop(masm, left, right, min_length, scratch2,
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006659 &result_not_equal, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006660
6661 // Compare lengths - strings up to min-length are equal.
6662 __ bind(&compare_lengths);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006663 __ test(length_delta, length_delta);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00006664 Label length_not_equal;
6665 __ j(not_zero, &length_not_equal, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006666
6667 // Result is EQUAL.
6668 STATIC_ASSERT(EQUAL == 0);
6669 STATIC_ASSERT(kSmiTag == 0);
6670 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6671 __ ret(0);
6672
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006673 Label result_greater;
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00006674 Label result_less;
6675 __ bind(&length_not_equal);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006676 __ j(greater, &result_greater, Label::kNear);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00006677 __ jmp(&result_less, Label::kNear);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00006678 __ bind(&result_not_equal);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00006679 __ j(above, &result_greater, Label::kNear);
6680 __ bind(&result_less);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006681
6682 // Result is LESS.
6683 __ Set(eax, Immediate(Smi::FromInt(LESS)));
6684 __ ret(0);
6685
6686 // Result is GREATER.
6687 __ bind(&result_greater);
6688 __ Set(eax, Immediate(Smi::FromInt(GREATER)));
6689 __ ret(0);
6690}
6691
6692
lrn@chromium.org1c092762011-05-09 09:42:16 +00006693void StringCompareStub::GenerateAsciiCharsCompareLoop(
6694 MacroAssembler* masm,
6695 Register left,
6696 Register right,
6697 Register length,
6698 Register scratch,
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006699 Label* chars_not_equal,
6700 Label::Distance chars_not_equal_near) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00006701 // Change index to run from -length to -1 by adding length to string
6702 // start. This means that loop ends when index reaches zero, which
6703 // doesn't need an additional compare.
6704 __ SmiUntag(length);
6705 __ lea(left,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006706 FieldOperand(left, length, times_1, SeqOneByteString::kHeaderSize));
lrn@chromium.org1c092762011-05-09 09:42:16 +00006707 __ lea(right,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006708 FieldOperand(right, length, times_1, SeqOneByteString::kHeaderSize));
lrn@chromium.org1c092762011-05-09 09:42:16 +00006709 __ neg(length);
6710 Register index = length; // index = -length;
6711
6712 // Compare loop.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006713 Label loop;
lrn@chromium.org1c092762011-05-09 09:42:16 +00006714 __ bind(&loop);
6715 __ mov_b(scratch, Operand(left, index, times_1, 0));
6716 __ cmpb(scratch, Operand(right, index, times_1, 0));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006717 __ j(not_equal, chars_not_equal, chars_not_equal_near);
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00006718 __ inc(index);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006719 __ j(not_zero, &loop);
6720}
6721
6722
ricow@chromium.org65fae842010-08-25 15:26:24 +00006723void StringCompareStub::Generate(MacroAssembler* masm) {
6724 Label runtime;
6725
6726 // Stack frame on entry.
6727 // esp[0]: return address
6728 // esp[4]: right string
6729 // esp[8]: left string
6730
6731 __ mov(edx, Operand(esp, 2 * kPointerSize)); // left
6732 __ mov(eax, Operand(esp, 1 * kPointerSize)); // right
6733
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006734 Label not_same;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006735 __ cmp(edx, eax);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006736 __ j(not_equal, &not_same, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006737 STATIC_ASSERT(EQUAL == 0);
6738 STATIC_ASSERT(kSmiTag == 0);
6739 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006740 __ IncrementCounter(masm->isolate()->counters()->string_compare_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006741 __ ret(2 * kPointerSize);
6742
6743 __ bind(&not_same);
6744
ulan@chromium.org2efb9002012-01-19 15:36:35 +00006745 // Check that both objects are sequential ASCII strings.
ricow@chromium.org65fae842010-08-25 15:26:24 +00006746 __ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx, &runtime);
6747
ulan@chromium.org2efb9002012-01-19 15:36:35 +00006748 // Compare flat ASCII strings.
ricow@chromium.org65fae842010-08-25 15:26:24 +00006749 // Drop arguments from the stack.
6750 __ pop(ecx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006751 __ add(esp, Immediate(2 * kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006752 __ push(ecx);
6753 GenerateCompareFlatAsciiStrings(masm, edx, eax, ecx, ebx, edi);
6754
6755 // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
6756 // tagged as a small integer.
6757 __ bind(&runtime);
6758 __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
6759}
6760
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006761
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006762void ICCompareStub::GenerateSmis(MacroAssembler* masm) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006763 ASSERT(state_ == CompareIC::SMI);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006764 Label miss;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006765 __ mov(ecx, edx);
6766 __ or_(ecx, eax);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006767 __ JumpIfNotSmi(ecx, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006768
6769 if (GetCondition() == equal) {
6770 // For equality we do not care about the sign of the result.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006771 __ sub(eax, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006772 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006773 Label done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006774 __ sub(edx, eax);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006775 __ j(no_overflow, &done, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006776 // Correct sign of result in case of overflow.
6777 __ not_(edx);
6778 __ bind(&done);
6779 __ mov(eax, edx);
6780 }
6781 __ ret(0);
6782
6783 __ bind(&miss);
6784 GenerateMiss(masm);
6785}
6786
6787
hpayer@chromium.org8432c912013-02-28 15:55:26 +00006788void ICCompareStub::GenerateNumbers(MacroAssembler* masm) {
6789 ASSERT(state_ == CompareIC::NUMBER);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006790
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006791 Label generic_stub;
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00006792 Label unordered, maybe_undefined1, maybe_undefined2;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006793 Label miss;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006794
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006795 if (left_ == CompareIC::SMI) {
6796 __ JumpIfNotSmi(edx, &miss);
6797 }
6798 if (right_ == CompareIC::SMI) {
6799 __ JumpIfNotSmi(eax, &miss);
6800 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006801
6802 // Inlining the double comparison and falling back to the general compare
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006803 // stub if NaN is involved or SSE2 or CMOV is unsupported.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006804 if (CpuFeatures::IsSupported(SSE2) && CpuFeatures::IsSupported(CMOV)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00006805 CpuFeatureScope scope1(masm, SSE2);
6806 CpuFeatureScope scope2(masm, CMOV);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006807
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006808 // Load left and right operand.
6809 Label done, left, left_smi, right_smi;
6810 __ JumpIfSmi(eax, &right_smi, Label::kNear);
6811 __ cmp(FieldOperand(eax, HeapObject::kMapOffset),
6812 masm->isolate()->factory()->heap_number_map());
6813 __ j(not_equal, &maybe_undefined1, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006814 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006815 __ jmp(&left, Label::kNear);
6816 __ bind(&right_smi);
6817 __ mov(ecx, eax); // Can't clobber eax because we can still jump away.
6818 __ SmiUntag(ecx);
6819 __ cvtsi2sd(xmm1, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006820
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006821 __ bind(&left);
6822 __ JumpIfSmi(edx, &left_smi, Label::kNear);
6823 __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
6824 masm->isolate()->factory()->heap_number_map());
6825 __ j(not_equal, &maybe_undefined2, Label::kNear);
6826 __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
6827 __ jmp(&done);
6828 __ bind(&left_smi);
6829 __ mov(ecx, edx); // Can't clobber edx because we can still jump away.
6830 __ SmiUntag(ecx);
6831 __ cvtsi2sd(xmm0, ecx);
6832
6833 __ bind(&done);
6834 // Compare operands.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006835 __ ucomisd(xmm0, xmm1);
6836
6837 // Don't base result on EFLAGS when a NaN is involved.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00006838 __ j(parity_even, &unordered, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006839
6840 // Return a result of -1, 0, or 1, based on EFLAGS.
6841 // Performing mov, because xor would destroy the flag register.
6842 __ mov(eax, 0); // equal
6843 __ mov(ecx, Immediate(Smi::FromInt(1)));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006844 __ cmov(above, eax, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006845 __ mov(ecx, Immediate(Smi::FromInt(-1)));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006846 __ cmov(below, eax, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006847 __ ret(0);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006848 } else {
6849 __ mov(ecx, edx);
6850 __ and_(ecx, eax);
6851 __ JumpIfSmi(ecx, &generic_stub, Label::kNear);
6852
6853 __ cmp(FieldOperand(eax, HeapObject::kMapOffset),
6854 masm->isolate()->factory()->heap_number_map());
6855 __ j(not_equal, &maybe_undefined1, Label::kNear);
6856 __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
6857 masm->isolate()->factory()->heap_number_map());
6858 __ j(not_equal, &maybe_undefined2, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006859 }
6860
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00006861 __ bind(&unordered);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006862 __ bind(&generic_stub);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006863 ICCompareStub stub(op_, CompareIC::GENERIC, CompareIC::GENERIC,
6864 CompareIC::GENERIC);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00006865 __ jmp(stub.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006866
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00006867 __ bind(&maybe_undefined1);
6868 if (Token::IsOrderedRelationalCompareOp(op_)) {
6869 __ cmp(eax, Immediate(masm->isolate()->factory()->undefined_value()));
6870 __ j(not_equal, &miss);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006871 __ JumpIfSmi(edx, &unordered);
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00006872 __ CmpObjectType(edx, HEAP_NUMBER_TYPE, ecx);
6873 __ j(not_equal, &maybe_undefined2, Label::kNear);
6874 __ jmp(&unordered);
6875 }
6876
6877 __ bind(&maybe_undefined2);
6878 if (Token::IsOrderedRelationalCompareOp(op_)) {
6879 __ cmp(edx, Immediate(masm->isolate()->factory()->undefined_value()));
6880 __ j(equal, &unordered);
6881 }
6882
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006883 __ bind(&miss);
6884 GenerateMiss(masm);
6885}
6886
6887
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006888void ICCompareStub::GenerateInternalizedStrings(MacroAssembler* masm) {
6889 ASSERT(state_ == CompareIC::INTERNALIZED_STRING);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006890 ASSERT(GetCondition() == equal);
6891
6892 // Registers containing left and right operands respectively.
6893 Register left = edx;
6894 Register right = eax;
6895 Register tmp1 = ecx;
6896 Register tmp2 = ebx;
6897
6898 // Check that both operands are heap objects.
6899 Label miss;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006900 __ mov(tmp1, left);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006901 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006902 __ and_(tmp1, right);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006903 __ JumpIfSmi(tmp1, &miss, Label::kNear);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006904
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006905 // Check that both operands are internalized strings.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006906 __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset));
6907 __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset));
6908 __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
6909 __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006910 STATIC_ASSERT(kInternalizedTag != 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006911 __ and_(tmp1, tmp2);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006912 __ test(tmp1, Immediate(kIsInternalizedMask));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006913 __ j(zero, &miss, Label::kNear);
6914
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006915 // Internalized strings are compared by identity.
6916 Label done;
6917 __ cmp(left, right);
6918 // Make sure eax is non-zero. At this point input operands are
6919 // guaranteed to be non-zero.
6920 ASSERT(right.is(eax));
6921 __ j(not_equal, &done, Label::kNear);
6922 STATIC_ASSERT(EQUAL == 0);
6923 STATIC_ASSERT(kSmiTag == 0);
6924 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6925 __ bind(&done);
6926 __ ret(0);
6927
6928 __ bind(&miss);
6929 GenerateMiss(masm);
6930}
6931
6932
6933void ICCompareStub::GenerateUniqueNames(MacroAssembler* masm) {
6934 ASSERT(state_ == CompareIC::UNIQUE_NAME);
6935 ASSERT(GetCondition() == equal);
6936
6937 // Registers containing left and right operands respectively.
6938 Register left = edx;
6939 Register right = eax;
6940 Register tmp1 = ecx;
6941 Register tmp2 = ebx;
6942
6943 // Check that both operands are heap objects.
6944 Label miss;
6945 __ mov(tmp1, left);
6946 STATIC_ASSERT(kSmiTag == 0);
6947 __ and_(tmp1, right);
6948 __ JumpIfSmi(tmp1, &miss, Label::kNear);
6949
6950 // Check that both operands are unique names. This leaves the instance
6951 // types loaded in tmp1 and tmp2.
6952 STATIC_ASSERT(kInternalizedTag != 0);
6953 __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset));
6954 __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset));
6955 __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
6956 __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
6957
6958 Label succeed1;
6959 __ test(tmp1, Immediate(kIsInternalizedMask));
6960 __ j(not_zero, &succeed1);
6961 __ cmpb(tmp1, static_cast<uint8_t>(SYMBOL_TYPE));
6962 __ j(not_equal, &miss);
6963 __ bind(&succeed1);
6964
6965 Label succeed2;
6966 __ test(tmp2, Immediate(kIsInternalizedMask));
6967 __ j(not_zero, &succeed2);
6968 __ cmpb(tmp2, static_cast<uint8_t>(SYMBOL_TYPE));
6969 __ j(not_equal, &miss);
6970 __ bind(&succeed2);
6971
6972 // Unique names are compared by identity.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006973 Label done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006974 __ cmp(left, right);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006975 // Make sure eax is non-zero. At this point input operands are
6976 // guaranteed to be non-zero.
6977 ASSERT(right.is(eax));
6978 __ j(not_equal, &done, Label::kNear);
6979 STATIC_ASSERT(EQUAL == 0);
6980 STATIC_ASSERT(kSmiTag == 0);
6981 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6982 __ bind(&done);
6983 __ ret(0);
6984
6985 __ bind(&miss);
6986 GenerateMiss(masm);
6987}
6988
6989
lrn@chromium.org1c092762011-05-09 09:42:16 +00006990void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006991 ASSERT(state_ == CompareIC::STRING);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006992 Label miss;
6993
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00006994 bool equality = Token::IsEqualityOp(op_);
6995
lrn@chromium.org1c092762011-05-09 09:42:16 +00006996 // Registers containing left and right operands respectively.
6997 Register left = edx;
6998 Register right = eax;
6999 Register tmp1 = ecx;
7000 Register tmp2 = ebx;
7001 Register tmp3 = edi;
7002
7003 // Check that both operands are heap objects.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007004 __ mov(tmp1, left);
lrn@chromium.org1c092762011-05-09 09:42:16 +00007005 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007006 __ and_(tmp1, right);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007007 __ JumpIfSmi(tmp1, &miss);
lrn@chromium.org1c092762011-05-09 09:42:16 +00007008
7009 // Check that both operands are strings. This leaves the instance
7010 // types loaded in tmp1 and tmp2.
7011 __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset));
7012 __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset));
7013 __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
7014 __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
7015 __ mov(tmp3, tmp1);
7016 STATIC_ASSERT(kNotStringTag != 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007017 __ or_(tmp3, tmp2);
lrn@chromium.org1c092762011-05-09 09:42:16 +00007018 __ test(tmp3, Immediate(kIsNotStringMask));
7019 __ j(not_zero, &miss);
7020
7021 // Fast check for identical strings.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007022 Label not_same;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007023 __ cmp(left, right);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007024 __ j(not_equal, &not_same, Label::kNear);
lrn@chromium.org1c092762011-05-09 09:42:16 +00007025 STATIC_ASSERT(EQUAL == 0);
7026 STATIC_ASSERT(kSmiTag == 0);
7027 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
7028 __ ret(0);
7029
7030 // Handle not identical strings.
7031 __ bind(&not_same);
7032
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00007033 // Check that both strings are internalized. If they are, we're done
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00007034 // because we already know they are not identical. But in the case of
7035 // non-equality compare, we still need to determine the order.
7036 if (equality) {
7037 Label do_compare;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00007038 STATIC_ASSERT(kInternalizedTag != 0);
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00007039 __ and_(tmp1, tmp2);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00007040 __ test(tmp1, Immediate(kIsInternalizedMask));
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00007041 __ j(zero, &do_compare, Label::kNear);
7042 // Make sure eax is non-zero. At this point input operands are
7043 // guaranteed to be non-zero.
7044 ASSERT(right.is(eax));
7045 __ ret(0);
7046 __ bind(&do_compare);
7047 }
lrn@chromium.org1c092762011-05-09 09:42:16 +00007048
7049 // Check that both strings are sequential ASCII.
7050 Label runtime;
lrn@chromium.org1c092762011-05-09 09:42:16 +00007051 __ JumpIfNotBothSequentialAsciiStrings(left, right, tmp1, tmp2, &runtime);
7052
7053 // Compare flat ASCII strings. Returns when done.
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00007054 if (equality) {
7055 StringCompareStub::GenerateFlatAsciiStringEquals(
7056 masm, left, right, tmp1, tmp2);
7057 } else {
7058 StringCompareStub::GenerateCompareFlatAsciiStrings(
7059 masm, left, right, tmp1, tmp2, tmp3);
7060 }
lrn@chromium.org1c092762011-05-09 09:42:16 +00007061
7062 // Handle more complex cases in runtime.
7063 __ bind(&runtime);
7064 __ pop(tmp1); // Return address.
7065 __ push(left);
7066 __ push(right);
7067 __ push(tmp1);
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00007068 if (equality) {
7069 __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
7070 } else {
7071 __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
7072 }
lrn@chromium.org1c092762011-05-09 09:42:16 +00007073
7074 __ bind(&miss);
7075 GenerateMiss(masm);
7076}
7077
7078
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007079void ICCompareStub::GenerateObjects(MacroAssembler* masm) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00007080 ASSERT(state_ == CompareIC::OBJECT);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00007081 Label miss;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007082 __ mov(ecx, edx);
7083 __ and_(ecx, eax);
whesse@chromium.org7b260152011-06-20 15:33:18 +00007084 __ JumpIfSmi(ecx, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007085
7086 __ CmpObjectType(eax, JS_OBJECT_TYPE, ecx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00007087 __ j(not_equal, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007088 __ CmpObjectType(edx, JS_OBJECT_TYPE, ecx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00007089 __ j(not_equal, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007090
7091 ASSERT(GetCondition() == equal);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007092 __ sub(eax, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007093 __ ret(0);
7094
7095 __ bind(&miss);
7096 GenerateMiss(masm);
7097}
7098
7099
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00007100void ICCompareStub::GenerateKnownObjects(MacroAssembler* masm) {
7101 Label miss;
7102 __ mov(ecx, edx);
7103 __ and_(ecx, eax);
7104 __ JumpIfSmi(ecx, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007105
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00007106 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
7107 __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
7108 __ cmp(ecx, known_map_);
7109 __ j(not_equal, &miss, Label::kNear);
7110 __ cmp(ebx, known_map_);
7111 __ j(not_equal, &miss, Label::kNear);
7112
7113 __ sub(eax, edx);
7114 __ ret(0);
7115
7116 __ bind(&miss);
7117 GenerateMiss(masm);
7118}
7119
7120
7121void ICCompareStub::GenerateMiss(MacroAssembler* masm) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007122 {
7123 // Call the runtime system in a fresh internal frame.
7124 ExternalReference miss = ExternalReference(IC_Utility(IC::kCompareIC_Miss),
7125 masm->isolate());
7126 FrameScope scope(masm, StackFrame::INTERNAL);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00007127 __ push(edx); // Preserve edx and eax.
7128 __ push(eax);
7129 __ push(edx); // And also use them as the arguments.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007130 __ push(eax);
7131 __ push(Immediate(Smi::FromInt(op_)));
7132 __ CallExternalReference(miss, 3);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00007133 // Compute the entry point of the rewritten stub.
7134 __ lea(edi, FieldOperand(eax, Code::kHeaderSize));
7135 __ pop(eax);
7136 __ pop(edx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007137 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007138
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007139 // Do a tail call to the rewritten stub.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007140 __ jmp(edi);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00007141}
7142
7143
lrn@chromium.org1c092762011-05-09 09:42:16 +00007144// Helper function used to check that the dictionary doesn't contain
7145// the property. This function may return false negatives, so miss_label
7146// must always call a backup property check that is complete.
7147// This function is safe to call if the receiver has fast properties.
ulan@chromium.org750145a2013-03-07 15:14:13 +00007148// Name must be a unique name and receiver must be a heap object.
7149void NameDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm,
7150 Label* miss,
7151 Label* done,
7152 Register properties,
7153 Handle<Name> name,
7154 Register r0) {
7155 ASSERT(name->IsUniqueName());
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007156
7157 // If names of slots in range from 1 to kProbes - 1 for the hash value are
7158 // not equal to the name and kProbes-th slot is not used (its name is the
7159 // undefined value), it guarantees the hash table doesn't contain the
7160 // property. It's true even if some slots represent deleted properties
ulan@chromium.org967e2702012-02-28 09:49:15 +00007161 // (their names are the hole value).
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007162 for (int i = 0; i < kInlinedProbes; i++) {
7163 // Compute the masked index: (hash + i + i * i) & mask.
7164 Register index = r0;
7165 // Capacity is smi 2^n.
7166 __ mov(index, FieldOperand(properties, kCapacityOffset));
7167 __ dec(index);
7168 __ and_(index,
7169 Immediate(Smi::FromInt(name->Hash() +
ulan@chromium.org750145a2013-03-07 15:14:13 +00007170 NameDictionary::GetProbeOffset(i))));
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007171
7172 // Scale the index by multiplying by the entry size.
ulan@chromium.org750145a2013-03-07 15:14:13 +00007173 ASSERT(NameDictionary::kEntrySize == 3);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007174 __ lea(index, Operand(index, index, times_2, 0)); // index *= 3.
7175 Register entity_name = r0;
7176 // Having undefined at this place means the name is not contained.
7177 ASSERT_EQ(kSmiTagSize, 1);
7178 __ mov(entity_name, Operand(properties, index, times_half_pointer_size,
7179 kElementsStartOffset - kHeapObjectTag));
7180 __ cmp(entity_name, masm->isolate()->factory()->undefined_value());
7181 __ j(equal, done);
7182
7183 // Stop if found the property.
ulan@chromium.org750145a2013-03-07 15:14:13 +00007184 __ cmp(entity_name, Handle<Name>(name));
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007185 __ j(equal, miss);
7186
ulan@chromium.org750145a2013-03-07 15:14:13 +00007187 Label good;
ulan@chromium.org967e2702012-02-28 09:49:15 +00007188 // Check for the hole and skip.
7189 __ cmp(entity_name, masm->isolate()->factory()->the_hole_value());
ulan@chromium.org750145a2013-03-07 15:14:13 +00007190 __ j(equal, &good, Label::kNear);
ulan@chromium.org967e2702012-02-28 09:49:15 +00007191
ulan@chromium.org750145a2013-03-07 15:14:13 +00007192 // Check if the entry name is not a unique name.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007193 __ mov(entity_name, FieldOperand(entity_name, HeapObject::kMapOffset));
7194 __ test_b(FieldOperand(entity_name, Map::kInstanceTypeOffset),
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00007195 kIsInternalizedMask);
ulan@chromium.org750145a2013-03-07 15:14:13 +00007196 __ j(not_zero, &good);
7197 __ cmpb(FieldOperand(entity_name, Map::kInstanceTypeOffset),
7198 static_cast<uint8_t>(SYMBOL_TYPE));
7199 __ j(not_equal, miss);
7200 __ bind(&good);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007201 }
7202
ulan@chromium.org750145a2013-03-07 15:14:13 +00007203 NameDictionaryLookupStub stub(properties, r0, r0, NEGATIVE_LOOKUP);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007204 __ push(Immediate(Handle<Object>(name)));
7205 __ push(Immediate(name->Hash()));
7206 __ CallStub(&stub);
7207 __ test(r0, r0);
7208 __ j(not_zero, miss);
7209 __ jmp(done);
7210}
7211
7212
ulan@chromium.org750145a2013-03-07 15:14:13 +00007213// Probe the name dictionary in the |elements| register. Jump to the
lrn@chromium.org1c092762011-05-09 09:42:16 +00007214// |done| label if a property with the given name is found leaving the
7215// index into the dictionary in |r0|. Jump to the |miss| label
7216// otherwise.
ulan@chromium.org750145a2013-03-07 15:14:13 +00007217void NameDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm,
7218 Label* miss,
7219 Label* done,
7220 Register elements,
7221 Register name,
7222 Register r0,
7223 Register r1) {
erik.corry@gmail.com6e28b562011-10-27 14:20:17 +00007224 ASSERT(!elements.is(r0));
7225 ASSERT(!elements.is(r1));
7226 ASSERT(!name.is(r0));
7227 ASSERT(!name.is(r1));
7228
ulan@chromium.org750145a2013-03-07 15:14:13 +00007229 __ AssertName(name);
lrn@chromium.org1c092762011-05-09 09:42:16 +00007230
7231 __ mov(r1, FieldOperand(elements, kCapacityOffset));
7232 __ shr(r1, kSmiTagSize); // convert smi to int
7233 __ dec(r1);
7234
7235 // Generate an unrolled loop that performs a few probes before
7236 // giving up. Measurements done on Gmail indicate that 2 probes
7237 // cover ~93% of loads from dictionaries.
7238 for (int i = 0; i < kInlinedProbes; i++) {
7239 // Compute the masked index: (hash + i + i * i) & mask.
ulan@chromium.org750145a2013-03-07 15:14:13 +00007240 __ mov(r0, FieldOperand(name, Name::kHashFieldOffset));
7241 __ shr(r0, Name::kHashShift);
lrn@chromium.org1c092762011-05-09 09:42:16 +00007242 if (i > 0) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00007243 __ add(r0, Immediate(NameDictionary::GetProbeOffset(i)));
lrn@chromium.org1c092762011-05-09 09:42:16 +00007244 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007245 __ and_(r0, r1);
lrn@chromium.org1c092762011-05-09 09:42:16 +00007246
7247 // Scale the index by multiplying by the entry size.
ulan@chromium.org750145a2013-03-07 15:14:13 +00007248 ASSERT(NameDictionary::kEntrySize == 3);
lrn@chromium.org1c092762011-05-09 09:42:16 +00007249 __ lea(r0, Operand(r0, r0, times_2, 0)); // r0 = r0 * 3
7250
7251 // Check if the key is identical to the name.
7252 __ cmp(name, Operand(elements,
7253 r0,
7254 times_4,
7255 kElementsStartOffset - kHeapObjectTag));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00007256 __ j(equal, done);
lrn@chromium.org1c092762011-05-09 09:42:16 +00007257 }
7258
ulan@chromium.org750145a2013-03-07 15:14:13 +00007259 NameDictionaryLookupStub stub(elements, r1, r0, POSITIVE_LOOKUP);
lrn@chromium.org1c092762011-05-09 09:42:16 +00007260 __ push(name);
ulan@chromium.org750145a2013-03-07 15:14:13 +00007261 __ mov(r0, FieldOperand(name, Name::kHashFieldOffset));
7262 __ shr(r0, Name::kHashShift);
lrn@chromium.org1c092762011-05-09 09:42:16 +00007263 __ push(r0);
7264 __ CallStub(&stub);
7265
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007266 __ test(r1, r1);
lrn@chromium.org1c092762011-05-09 09:42:16 +00007267 __ j(zero, miss);
7268 __ jmp(done);
7269}
7270
7271
ulan@chromium.org750145a2013-03-07 15:14:13 +00007272void NameDictionaryLookupStub::Generate(MacroAssembler* masm) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007273 // This stub overrides SometimesSetsUpAFrame() to return false. That means
7274 // we cannot call anything that could cause a GC from this stub.
lrn@chromium.org1c092762011-05-09 09:42:16 +00007275 // Stack frame on entry:
7276 // esp[0 * kPointerSize]: return address.
7277 // esp[1 * kPointerSize]: key's hash.
7278 // esp[2 * kPointerSize]: key.
7279 // Registers:
ulan@chromium.org750145a2013-03-07 15:14:13 +00007280 // dictionary_: NameDictionary to probe.
lrn@chromium.org1c092762011-05-09 09:42:16 +00007281 // result_: used as scratch.
7282 // index_: will hold an index of entry if lookup is successful.
7283 // might alias with result_.
7284 // Returns:
7285 // result_ is zero if lookup failed, non zero otherwise.
7286
7287 Label in_dictionary, maybe_in_dictionary, not_in_dictionary;
7288
7289 Register scratch = result_;
7290
7291 __ mov(scratch, FieldOperand(dictionary_, kCapacityOffset));
7292 __ dec(scratch);
7293 __ SmiUntag(scratch);
7294 __ push(scratch);
7295
7296 // If names of slots in range from 1 to kProbes - 1 for the hash value are
7297 // not equal to the name and kProbes-th slot is not used (its name is the
7298 // undefined value), it guarantees the hash table doesn't contain the
7299 // property. It's true even if some slots represent deleted properties
7300 // (their names are the null value).
7301 for (int i = kInlinedProbes; i < kTotalProbes; i++) {
7302 // Compute the masked index: (hash + i + i * i) & mask.
7303 __ mov(scratch, Operand(esp, 2 * kPointerSize));
7304 if (i > 0) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00007305 __ add(scratch, Immediate(NameDictionary::GetProbeOffset(i)));
lrn@chromium.org1c092762011-05-09 09:42:16 +00007306 }
7307 __ and_(scratch, Operand(esp, 0));
7308
7309 // Scale the index by multiplying by the entry size.
ulan@chromium.org750145a2013-03-07 15:14:13 +00007310 ASSERT(NameDictionary::kEntrySize == 3);
lrn@chromium.org1c092762011-05-09 09:42:16 +00007311 __ lea(index_, Operand(scratch, scratch, times_2, 0)); // index *= 3.
7312
7313 // Having undefined at this place means the name is not contained.
7314 ASSERT_EQ(kSmiTagSize, 1);
7315 __ mov(scratch, Operand(dictionary_,
7316 index_,
7317 times_pointer_size,
7318 kElementsStartOffset - kHeapObjectTag));
7319 __ cmp(scratch, masm->isolate()->factory()->undefined_value());
7320 __ j(equal, &not_in_dictionary);
7321
7322 // Stop if found the property.
7323 __ cmp(scratch, Operand(esp, 3 * kPointerSize));
7324 __ j(equal, &in_dictionary);
7325
7326 if (i != kTotalProbes - 1 && mode_ == NEGATIVE_LOOKUP) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00007327 // If we hit a key that is not a unique name during negative
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00007328 // lookup we have to bailout as this key might be equal to the
lrn@chromium.org1c092762011-05-09 09:42:16 +00007329 // key we are looking for.
7330
ulan@chromium.org750145a2013-03-07 15:14:13 +00007331 // Check if the entry name is not a unique name.
7332 Label cont;
lrn@chromium.org1c092762011-05-09 09:42:16 +00007333 __ mov(scratch, FieldOperand(scratch, HeapObject::kMapOffset));
7334 __ test_b(FieldOperand(scratch, Map::kInstanceTypeOffset),
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00007335 kIsInternalizedMask);
ulan@chromium.org750145a2013-03-07 15:14:13 +00007336 __ j(not_zero, &cont);
7337 __ cmpb(FieldOperand(scratch, Map::kInstanceTypeOffset),
7338 static_cast<uint8_t>(SYMBOL_TYPE));
7339 __ j(not_equal, &maybe_in_dictionary);
7340 __ bind(&cont);
lrn@chromium.org1c092762011-05-09 09:42:16 +00007341 }
7342 }
7343
7344 __ bind(&maybe_in_dictionary);
7345 // If we are doing negative lookup then probing failure should be
7346 // treated as a lookup success. For positive lookup probing failure
7347 // should be treated as lookup failure.
7348 if (mode_ == POSITIVE_LOOKUP) {
7349 __ mov(result_, Immediate(0));
7350 __ Drop(1);
7351 __ ret(2 * kPointerSize);
7352 }
7353
7354 __ bind(&in_dictionary);
7355 __ mov(result_, Immediate(1));
7356 __ Drop(1);
7357 __ ret(2 * kPointerSize);
7358
7359 __ bind(&not_in_dictionary);
7360 __ mov(result_, Immediate(0));
7361 __ Drop(1);
7362 __ ret(2 * kPointerSize);
7363}
7364
7365
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007366struct AheadOfTimeWriteBarrierStubList {
7367 Register object, value, address;
7368 RememberedSetAction action;
7369};
7370
7371
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007372#define REG(Name) { kRegister_ ## Name ## _Code }
7373
7374static const AheadOfTimeWriteBarrierStubList kAheadOfTime[] = {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007375 // Used in RegExpExecStub.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007376 { REG(ebx), REG(eax), REG(edi), EMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007377 // Used in CompileArrayPushCall.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007378 { REG(ebx), REG(ecx), REG(edx), EMIT_REMEMBERED_SET },
7379 { REG(ebx), REG(edi), REG(edx), OMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007380 // Used in CompileStoreGlobal and CallFunctionStub.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007381 { REG(ebx), REG(ecx), REG(edx), OMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007382 // Used in StoreStubCompiler::CompileStoreField and
7383 // KeyedStoreStubCompiler::CompileStoreField via GenerateStoreField.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007384 { REG(edx), REG(ecx), REG(ebx), EMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007385 // GenerateStoreField calls the stub with two different permutations of
7386 // registers. This is the second.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007387 { REG(ebx), REG(ecx), REG(edx), EMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007388 // StoreIC::GenerateNormal via GenerateDictionaryStore
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007389 { REG(ebx), REG(edi), REG(edx), EMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007390 // KeyedStoreIC::GenerateGeneric.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007391 { REG(ebx), REG(edx), REG(ecx), EMIT_REMEMBERED_SET},
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007392 // KeyedStoreStubCompiler::GenerateStoreFastElement.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007393 { REG(edi), REG(ebx), REG(ecx), EMIT_REMEMBERED_SET},
7394 { REG(edx), REG(edi), REG(ebx), EMIT_REMEMBERED_SET},
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00007395 // ElementsTransitionGenerator::GenerateMapChangeElementTransition
7396 // and ElementsTransitionGenerator::GenerateSmiToDouble
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007397 // and ElementsTransitionGenerator::GenerateDoubleToObject
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007398 { REG(edx), REG(ebx), REG(edi), EMIT_REMEMBERED_SET},
7399 { REG(edx), REG(ebx), REG(edi), OMIT_REMEMBERED_SET},
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007400 // ElementsTransitionGenerator::GenerateDoubleToObject
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007401 { REG(eax), REG(edx), REG(esi), EMIT_REMEMBERED_SET},
7402 { REG(edx), REG(eax), REG(edi), EMIT_REMEMBERED_SET},
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007403 // StoreArrayLiteralElementStub::Generate
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007404 { REG(ebx), REG(eax), REG(ecx), EMIT_REMEMBERED_SET},
ulan@chromium.org57ff8812013-05-10 08:16:55 +00007405 // FastNewClosureStub and StringAddStub::Generate
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00007406 { REG(ecx), REG(edx), REG(ebx), EMIT_REMEMBERED_SET},
ulan@chromium.org57ff8812013-05-10 08:16:55 +00007407 // StringAddStub::Generate
7408 { REG(ecx), REG(eax), REG(ebx), EMIT_REMEMBERED_SET},
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007409 // Null termination.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007410 { REG(no_reg), REG(no_reg), REG(no_reg), EMIT_REMEMBERED_SET}
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007411};
7412
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007413#undef REG
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007414
7415bool RecordWriteStub::IsPregenerated() {
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007416 for (const AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007417 !entry->object.is(no_reg);
7418 entry++) {
7419 if (object_.is(entry->object) &&
7420 value_.is(entry->value) &&
7421 address_.is(entry->address) &&
7422 remembered_set_action_ == entry->action &&
7423 save_fp_regs_mode_ == kDontSaveFPRegs) {
7424 return true;
7425 }
7426 }
7427 return false;
7428}
7429
7430
hpayer@chromium.org8432c912013-02-28 15:55:26 +00007431void StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(
7432 Isolate* isolate) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00007433 StoreBufferOverflowStub stub(kDontSaveFPRegs);
7434 stub.GetCode(isolate)->set_is_pregenerated(true);
7435 if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007436 StoreBufferOverflowStub stub2(kSaveFPRegs);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00007437 stub2.GetCode(isolate)->set_is_pregenerated(true);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007438 }
7439}
7440
7441
hpayer@chromium.org8432c912013-02-28 15:55:26 +00007442void RecordWriteStub::GenerateFixedRegStubsAheadOfTime(Isolate* isolate) {
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007443 for (const AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007444 !entry->object.is(no_reg);
7445 entry++) {
7446 RecordWriteStub stub(entry->object,
7447 entry->value,
7448 entry->address,
7449 entry->action,
7450 kDontSaveFPRegs);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00007451 stub.GetCode(isolate)->set_is_pregenerated(true);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007452 }
7453}
7454
7455
verwaest@chromium.org33e09c82012-10-10 17:07:22 +00007456bool CodeStub::CanUseFPRegisters() {
7457 return CpuFeatures::IsSupported(SSE2);
7458}
7459
7460
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007461// Takes the input in 3 registers: address_ value_ and object_. A pointer to
7462// the value has just been written into the object, now this stub makes sure
7463// we keep the GC informed. The word in the object where the value has been
7464// written is in the address register.
7465void RecordWriteStub::Generate(MacroAssembler* masm) {
7466 Label skip_to_incremental_noncompacting;
7467 Label skip_to_incremental_compacting;
7468
7469 // The first two instructions are generated with labels so as to get the
7470 // offset fixed up correctly by the bind(Label*) call. We patch it back and
7471 // forth between a compare instructions (a nop in this position) and the
7472 // real branch when we start and stop incremental heap marking.
7473 __ jmp(&skip_to_incremental_noncompacting, Label::kNear);
7474 __ jmp(&skip_to_incremental_compacting, Label::kFar);
7475
7476 if (remembered_set_action_ == EMIT_REMEMBERED_SET) {
7477 __ RememberedSetHelper(object_,
7478 address_,
7479 value_,
7480 save_fp_regs_mode_,
7481 MacroAssembler::kReturnAtEnd);
7482 } else {
7483 __ ret(0);
7484 }
7485
7486 __ bind(&skip_to_incremental_noncompacting);
7487 GenerateIncremental(masm, INCREMENTAL);
7488
7489 __ bind(&skip_to_incremental_compacting);
7490 GenerateIncremental(masm, INCREMENTAL_COMPACTION);
7491
7492 // Initial mode of the stub is expected to be STORE_BUFFER_ONLY.
7493 // Will be checked in IncrementalMarking::ActivateGeneratedStub.
7494 masm->set_byte_at(0, kTwoByteNopInstruction);
7495 masm->set_byte_at(2, kFiveByteNopInstruction);
7496}
7497
7498
7499void RecordWriteStub::GenerateIncremental(MacroAssembler* masm, Mode mode) {
7500 regs_.Save(masm);
7501
7502 if (remembered_set_action_ == EMIT_REMEMBERED_SET) {
7503 Label dont_need_remembered_set;
7504
7505 __ mov(regs_.scratch0(), Operand(regs_.address(), 0));
7506 __ JumpIfNotInNewSpace(regs_.scratch0(), // Value.
7507 regs_.scratch0(),
7508 &dont_need_remembered_set);
7509
7510 __ CheckPageFlag(regs_.object(),
7511 regs_.scratch0(),
7512 1 << MemoryChunk::SCAN_ON_SCAVENGE,
7513 not_zero,
7514 &dont_need_remembered_set);
7515
7516 // First notify the incremental marker if necessary, then update the
7517 // remembered set.
7518 CheckNeedsToInformIncrementalMarker(
7519 masm,
7520 kUpdateRememberedSetOnNoNeedToInformIncrementalMarker,
7521 mode);
7522 InformIncrementalMarker(masm, mode);
7523 regs_.Restore(masm);
7524 __ RememberedSetHelper(object_,
7525 address_,
7526 value_,
7527 save_fp_regs_mode_,
7528 MacroAssembler::kReturnAtEnd);
7529
7530 __ bind(&dont_need_remembered_set);
7531 }
7532
7533 CheckNeedsToInformIncrementalMarker(
7534 masm,
7535 kReturnOnNoNeedToInformIncrementalMarker,
7536 mode);
7537 InformIncrementalMarker(masm, mode);
7538 regs_.Restore(masm);
7539 __ ret(0);
7540}
7541
7542
7543void RecordWriteStub::InformIncrementalMarker(MacroAssembler* masm, Mode mode) {
7544 regs_.SaveCallerSaveRegisters(masm, save_fp_regs_mode_);
7545 int argument_count = 3;
7546 __ PrepareCallCFunction(argument_count, regs_.scratch0());
7547 __ mov(Operand(esp, 0 * kPointerSize), regs_.object());
ulan@chromium.org8e8d8822012-11-23 14:36:46 +00007548 __ mov(Operand(esp, 1 * kPointerSize), regs_.address()); // Slot.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007549 __ mov(Operand(esp, 2 * kPointerSize),
ulan@chromium.org32d7dba2013-04-24 10:59:06 +00007550 Immediate(ExternalReference::isolate_address(masm->isolate())));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007551
7552 AllowExternalCallThatCantCauseGC scope(masm);
7553 if (mode == INCREMENTAL_COMPACTION) {
7554 __ CallCFunction(
7555 ExternalReference::incremental_evacuation_record_write_function(
7556 masm->isolate()),
7557 argument_count);
7558 } else {
7559 ASSERT(mode == INCREMENTAL);
7560 __ CallCFunction(
7561 ExternalReference::incremental_marking_record_write_function(
7562 masm->isolate()),
7563 argument_count);
7564 }
7565 regs_.RestoreCallerSaveRegisters(masm, save_fp_regs_mode_);
7566}
7567
7568
7569void RecordWriteStub::CheckNeedsToInformIncrementalMarker(
7570 MacroAssembler* masm,
7571 OnNoNeedToInformIncrementalMarker on_no_need,
7572 Mode mode) {
7573 Label object_is_black, need_incremental, need_incremental_pop_object;
7574
verwaest@chromium.org33e09c82012-10-10 17:07:22 +00007575 __ mov(regs_.scratch0(), Immediate(~Page::kPageAlignmentMask));
7576 __ and_(regs_.scratch0(), regs_.object());
7577 __ mov(regs_.scratch1(),
7578 Operand(regs_.scratch0(),
7579 MemoryChunk::kWriteBarrierCounterOffset));
7580 __ sub(regs_.scratch1(), Immediate(1));
7581 __ mov(Operand(regs_.scratch0(),
7582 MemoryChunk::kWriteBarrierCounterOffset),
7583 regs_.scratch1());
7584 __ j(negative, &need_incremental);
7585
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007586 // Let's look at the color of the object: If it is not black we don't have
7587 // to inform the incremental marker.
7588 __ JumpIfBlack(regs_.object(),
7589 regs_.scratch0(),
7590 regs_.scratch1(),
7591 &object_is_black,
7592 Label::kNear);
7593
7594 regs_.Restore(masm);
7595 if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) {
7596 __ RememberedSetHelper(object_,
7597 address_,
7598 value_,
7599 save_fp_regs_mode_,
7600 MacroAssembler::kReturnAtEnd);
7601 } else {
7602 __ ret(0);
7603 }
7604
7605 __ bind(&object_is_black);
7606
7607 // Get the value from the slot.
7608 __ mov(regs_.scratch0(), Operand(regs_.address(), 0));
7609
7610 if (mode == INCREMENTAL_COMPACTION) {
7611 Label ensure_not_white;
7612
7613 __ CheckPageFlag(regs_.scratch0(), // Contains value.
7614 regs_.scratch1(), // Scratch.
7615 MemoryChunk::kEvacuationCandidateMask,
7616 zero,
7617 &ensure_not_white,
7618 Label::kNear);
7619
7620 __ CheckPageFlag(regs_.object(),
7621 regs_.scratch1(), // Scratch.
7622 MemoryChunk::kSkipEvacuationSlotsRecordingMask,
7623 not_zero,
7624 &ensure_not_white,
7625 Label::kNear);
7626
7627 __ jmp(&need_incremental);
7628
7629 __ bind(&ensure_not_white);
7630 }
7631
7632 // We need an extra register for this, so we push the object register
7633 // temporarily.
7634 __ push(regs_.object());
7635 __ EnsureNotWhite(regs_.scratch0(), // The value.
7636 regs_.scratch1(), // Scratch.
7637 regs_.object(), // Scratch.
7638 &need_incremental_pop_object,
7639 Label::kNear);
7640 __ pop(regs_.object());
7641
7642 regs_.Restore(masm);
7643 if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) {
7644 __ RememberedSetHelper(object_,
7645 address_,
7646 value_,
7647 save_fp_regs_mode_,
7648 MacroAssembler::kReturnAtEnd);
7649 } else {
7650 __ ret(0);
7651 }
7652
7653 __ bind(&need_incremental_pop_object);
7654 __ pop(regs_.object());
7655
7656 __ bind(&need_incremental);
7657
7658 // Fall through when we need to inform the incremental marker.
7659}
7660
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007661
7662void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) {
7663 // ----------- S t a t e -------------
7664 // -- eax : element value to store
7665 // -- ebx : array literal
7666 // -- edi : map of array literal
7667 // -- ecx : element index as smi
7668 // -- edx : array literal index in function
7669 // -- esp[0] : return address
7670 // -----------------------------------
7671
7672 Label element_done;
7673 Label double_elements;
7674 Label smi_element;
7675 Label slow_elements;
7676 Label slow_elements_from_double;
7677 Label fast_elements;
7678
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007679 __ CheckFastElements(edi, &double_elements);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007680
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00007681 // Check for FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS elements
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007682 __ JumpIfSmi(eax, &smi_element);
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00007683 __ CheckFastSmiElements(edi, &fast_elements, Label::kNear);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007684
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007685 // Store into the array literal requires a elements transition. Call into
7686 // the runtime.
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007687
7688 __ bind(&slow_elements);
7689 __ pop(edi); // Pop return address and remember to put back later for tail
7690 // call.
7691 __ push(ebx);
7692 __ push(ecx);
7693 __ push(eax);
7694 __ mov(ebx, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
7695 __ push(FieldOperand(ebx, JSFunction::kLiteralsOffset));
7696 __ push(edx);
7697 __ push(edi); // Return return address so that tail call returns to right
7698 // place.
7699 __ TailCallRuntime(Runtime::kStoreArrayLiteralElement, 5, 1);
7700
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007701 __ bind(&slow_elements_from_double);
7702 __ pop(edx);
7703 __ jmp(&slow_elements);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007704
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00007705 // Array literal has ElementsKind of FAST_*_ELEMENTS and value is an object.
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007706 __ bind(&fast_elements);
7707 __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset));
7708 __ lea(ecx, FieldOperand(ebx, ecx, times_half_pointer_size,
7709 FixedArrayBase::kHeaderSize));
7710 __ mov(Operand(ecx, 0), eax);
7711 // Update the write barrier for the array store.
7712 __ RecordWrite(ebx, ecx, eax,
7713 kDontSaveFPRegs,
7714 EMIT_REMEMBERED_SET,
7715 OMIT_SMI_CHECK);
7716 __ ret(0);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007717
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00007718 // Array literal has ElementsKind of FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS,
7719 // and value is Smi.
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007720 __ bind(&smi_element);
7721 __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset));
7722 __ mov(FieldOperand(ebx, ecx, times_half_pointer_size,
7723 FixedArrayBase::kHeaderSize), eax);
7724 __ ret(0);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007725
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00007726 // Array literal has ElementsKind of FAST_*_DOUBLE_ELEMENTS.
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007727 __ bind(&double_elements);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007728
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007729 __ push(edx);
7730 __ mov(edx, FieldOperand(ebx, JSObject::kElementsOffset));
7731 __ StoreNumberToDoubleElements(eax,
7732 edx,
7733 ecx,
7734 edi,
7735 xmm0,
7736 &slow_elements_from_double,
7737 false);
7738 __ pop(edx);
7739 __ ret(0);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007740}
7741
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007742
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00007743void StubFailureTrampolineStub::Generate(MacroAssembler* masm) {
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00007744 CEntryStub ces(1, fp_registers_ ? kSaveFPRegs : kDontSaveFPRegs);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00007745 __ call(ces.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
mmassi@chromium.org2f0efde2013-02-06 14:12:58 +00007746 int parameter_count_offset =
7747 StubFailureTrampolineFrame::kCallerStackParameterCountFrameOffset;
7748 __ mov(ebx, MemOperand(ebp, parameter_count_offset));
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00007749 masm->LeaveFrame(StackFrame::STUB_FAILURE_TRAMPOLINE);
mmassi@chromium.org2f0efde2013-02-06 14:12:58 +00007750 __ pop(ecx);
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00007751 int additional_offset = function_mode_ == JS_FUNCTION_STUB_MODE
7752 ? kPointerSize
7753 : 0;
7754 __ lea(esp, MemOperand(esp, ebx, times_pointer_size, additional_offset));
mmassi@chromium.org2f0efde2013-02-06 14:12:58 +00007755 __ jmp(ecx); // Return to IC Miss stub, continuation still on stack.
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00007756}
7757
7758
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007759void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) {
7760 if (entry_hook_ != NULL) {
7761 ProfileEntryHookStub stub;
7762 masm->CallStub(&stub);
7763 }
7764}
7765
7766
7767void ProfileEntryHookStub::Generate(MacroAssembler* masm) {
7768 // Ecx is the only volatile register we must save.
7769 __ push(ecx);
7770
7771 // Calculate and push the original stack pointer.
7772 __ lea(eax, Operand(esp, kPointerSize));
7773 __ push(eax);
7774
7775 // Calculate and push the function address.
7776 __ mov(eax, Operand(eax, 0));
7777 __ sub(eax, Immediate(Assembler::kCallInstructionLength));
7778 __ push(eax);
7779
7780 // Call the entry hook.
7781 int32_t hook_location = reinterpret_cast<int32_t>(&entry_hook_);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00007782 __ call(Operand(hook_location, RelocInfo::NONE32));
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007783 __ add(esp, Immediate(2 * kPointerSize));
7784
7785 // Restore ecx.
7786 __ pop(ecx);
7787 __ ret(0);
7788}
7789
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007790
7791template<class T>
7792static void CreateArrayDispatch(MacroAssembler* masm) {
7793 int last_index = GetSequenceIndexFromFastElementsKind(
7794 TERMINAL_FAST_ELEMENTS_KIND);
7795 for (int i = 0; i <= last_index; ++i) {
7796 Label next;
7797 ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
7798 __ cmp(edx, kind);
7799 __ j(not_equal, &next);
7800 T stub(kind);
7801 __ TailCallStub(&stub);
7802 __ bind(&next);
7803 }
7804
7805 // If we reached this point there is a problem.
7806 __ Abort("Unexpected ElementsKind in array constructor");
7807}
7808
7809
7810static void CreateArrayDispatchOneArgument(MacroAssembler* masm) {
7811 // ebx - type info cell
7812 // edx - kind
7813 // eax - number of arguments
7814 // edi - constructor?
7815 // esp[0] - return address
7816 // esp[4] - last argument
7817 ASSERT(FAST_SMI_ELEMENTS == 0);
7818 ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
7819 ASSERT(FAST_ELEMENTS == 2);
7820 ASSERT(FAST_HOLEY_ELEMENTS == 3);
7821 ASSERT(FAST_DOUBLE_ELEMENTS == 4);
7822 ASSERT(FAST_HOLEY_DOUBLE_ELEMENTS == 5);
7823
7824 Handle<Object> undefined_sentinel(
7825 masm->isolate()->heap()->undefined_value(),
7826 masm->isolate());
7827
7828 // is the low bit set? If so, we are holey and that is good.
7829 __ test_b(edx, 1);
7830 Label normal_sequence;
7831 __ j(not_zero, &normal_sequence);
7832
7833 // look at the first argument
7834 __ mov(ecx, Operand(esp, kPointerSize));
7835 __ test(ecx, ecx);
7836 __ j(zero, &normal_sequence);
7837
7838 // We are going to create a holey array, but our kind is non-holey.
7839 // Fix kind and retry
7840 __ inc(edx);
7841 __ cmp(ebx, Immediate(undefined_sentinel));
7842 __ j(equal, &normal_sequence);
7843
7844 // Save the resulting elements kind in type info
7845 __ SmiTag(edx);
7846 __ mov(FieldOperand(ebx, kPointerSize), edx);
7847 __ SmiUntag(edx);
7848
7849 __ bind(&normal_sequence);
7850 int last_index = GetSequenceIndexFromFastElementsKind(
7851 TERMINAL_FAST_ELEMENTS_KIND);
7852 for (int i = 0; i <= last_index; ++i) {
7853 Label next;
7854 ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
7855 __ cmp(edx, kind);
7856 __ j(not_equal, &next);
7857 ArraySingleArgumentConstructorStub stub(kind);
7858 __ TailCallStub(&stub);
7859 __ bind(&next);
7860 }
7861
7862 // If we reached this point there is a problem.
7863 __ Abort("Unexpected ElementsKind in array constructor");
7864}
7865
7866
7867template<class T>
7868static void ArrayConstructorStubAheadOfTimeHelper(Isolate* isolate) {
7869 int to_index = GetSequenceIndexFromFastElementsKind(
7870 TERMINAL_FAST_ELEMENTS_KIND);
7871 for (int i = 0; i <= to_index; ++i) {
7872 ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007873 T stub(kind, false);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007874 stub.GetCode(isolate)->set_is_pregenerated(true);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007875 if (AllocationSiteInfo::GetMode(kind) != DONT_TRACK_ALLOCATION_SITE) {
7876 T stub1(kind, true);
7877 stub1.GetCode(isolate)->set_is_pregenerated(true);
7878 }
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007879 }
7880}
7881
7882
7883void ArrayConstructorStubBase::GenerateStubsAheadOfTime(Isolate* isolate) {
7884 ArrayConstructorStubAheadOfTimeHelper<ArrayNoArgumentConstructorStub>(
7885 isolate);
7886 ArrayConstructorStubAheadOfTimeHelper<ArraySingleArgumentConstructorStub>(
7887 isolate);
7888 ArrayConstructorStubAheadOfTimeHelper<ArrayNArgumentsConstructorStub>(
7889 isolate);
7890}
7891
7892
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007893void InternalArrayConstructorStubBase::GenerateStubsAheadOfTime(
7894 Isolate* isolate) {
7895 ElementsKind kinds[2] = { FAST_ELEMENTS, FAST_HOLEY_ELEMENTS };
7896 for (int i = 0; i < 2; i++) {
7897 // For internal arrays we only need a few things
7898 InternalArrayNoArgumentConstructorStub stubh1(kinds[i]);
7899 stubh1.GetCode(isolate)->set_is_pregenerated(true);
7900 InternalArraySingleArgumentConstructorStub stubh2(kinds[i]);
7901 stubh2.GetCode(isolate)->set_is_pregenerated(true);
7902 InternalArrayNArgumentsConstructorStub stubh3(kinds[i]);
7903 stubh3.GetCode(isolate)->set_is_pregenerated(true);
7904 }
7905}
7906
7907
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007908void ArrayConstructorStub::Generate(MacroAssembler* masm) {
7909 // ----------- S t a t e -------------
7910 // -- eax : argc (only if argument_count_ == ANY)
7911 // -- ebx : type info cell
7912 // -- edi : constructor
7913 // -- esp[0] : return address
7914 // -- esp[4] : last argument
7915 // -----------------------------------
7916 Handle<Object> undefined_sentinel(
7917 masm->isolate()->heap()->undefined_value(),
7918 masm->isolate());
7919
7920 if (FLAG_debug_code) {
7921 // The array construct code is only set for the global and natives
7922 // builtin Array functions which always have maps.
7923
7924 // Initial map for the builtin Array function should be a map.
7925 __ mov(ecx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
7926 // Will both indicate a NULL and a Smi.
7927 __ test(ecx, Immediate(kSmiTagMask));
7928 __ Assert(not_zero, "Unexpected initial map for Array function");
7929 __ CmpObjectType(ecx, MAP_TYPE, ecx);
7930 __ Assert(equal, "Unexpected initial map for Array function");
7931
7932 // We should either have undefined in ebx or a valid jsglobalpropertycell
7933 Label okay_here;
7934 Handle<Map> global_property_cell_map(
7935 masm->isolate()->heap()->global_property_cell_map());
7936 __ cmp(ebx, Immediate(undefined_sentinel));
7937 __ j(equal, &okay_here);
7938 __ cmp(FieldOperand(ebx, 0), Immediate(global_property_cell_map));
7939 __ Assert(equal, "Expected property cell in register ebx");
7940 __ bind(&okay_here);
7941 }
7942
7943 if (FLAG_optimize_constructed_arrays) {
7944 Label no_info, switch_ready;
7945 // Get the elements kind and case on that.
7946 __ cmp(ebx, Immediate(undefined_sentinel));
7947 __ j(equal, &no_info);
ulan@chromium.org57ff8812013-05-10 08:16:55 +00007948 __ mov(edx, FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset));
7949 __ JumpIfNotSmi(edx, &no_info);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007950 __ SmiUntag(edx);
7951 __ jmp(&switch_ready);
7952 __ bind(&no_info);
7953 __ mov(edx, Immediate(GetInitialFastElementsKind()));
7954 __ bind(&switch_ready);
7955
7956 if (argument_count_ == ANY) {
7957 Label not_zero_case, not_one_case;
7958 __ test(eax, eax);
7959 __ j(not_zero, &not_zero_case);
7960 CreateArrayDispatch<ArrayNoArgumentConstructorStub>(masm);
7961
7962 __ bind(&not_zero_case);
7963 __ cmp(eax, 1);
7964 __ j(greater, &not_one_case);
7965 CreateArrayDispatchOneArgument(masm);
7966
7967 __ bind(&not_one_case);
7968 CreateArrayDispatch<ArrayNArgumentsConstructorStub>(masm);
7969 } else if (argument_count_ == NONE) {
7970 CreateArrayDispatch<ArrayNoArgumentConstructorStub>(masm);
7971 } else if (argument_count_ == ONE) {
7972 CreateArrayDispatchOneArgument(masm);
7973 } else if (argument_count_ == MORE_THAN_ONE) {
7974 CreateArrayDispatch<ArrayNArgumentsConstructorStub>(masm);
7975 } else {
7976 UNREACHABLE();
7977 }
7978 } else {
7979 Label generic_constructor;
7980 // Run the native code for the Array function called as constructor.
7981 ArrayNativeCode(masm, true, &generic_constructor);
7982
7983 // Jump to the generic construct code in case the specialized code cannot
7984 // handle the construction.
7985 __ bind(&generic_constructor);
7986 Handle<Code> generic_construct_stub =
7987 masm->isolate()->builtins()->JSConstructStubGeneric();
7988 __ jmp(generic_construct_stub, RelocInfo::CODE_TARGET);
7989 }
7990}
7991
7992
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007993void InternalArrayConstructorStub::GenerateCase(
7994 MacroAssembler* masm, ElementsKind kind) {
7995 Label not_zero_case, not_one_case;
7996 Label normal_sequence;
7997
7998 __ test(eax, eax);
7999 __ j(not_zero, &not_zero_case);
8000 InternalArrayNoArgumentConstructorStub stub0(kind);
8001 __ TailCallStub(&stub0);
8002
8003 __ bind(&not_zero_case);
8004 __ cmp(eax, 1);
8005 __ j(greater, &not_one_case);
8006
8007 if (IsFastPackedElementsKind(kind)) {
8008 // We might need to create a holey array
8009 // look at the first argument
8010 __ mov(ecx, Operand(esp, kPointerSize));
8011 __ test(ecx, ecx);
8012 __ j(zero, &normal_sequence);
8013
8014 InternalArraySingleArgumentConstructorStub
8015 stub1_holey(GetHoleyElementsKind(kind));
8016 __ TailCallStub(&stub1_holey);
8017 }
8018
8019 __ bind(&normal_sequence);
8020 InternalArraySingleArgumentConstructorStub stub1(kind);
8021 __ TailCallStub(&stub1);
8022
8023 __ bind(&not_one_case);
8024 InternalArrayNArgumentsConstructorStub stubN(kind);
8025 __ TailCallStub(&stubN);
8026}
8027
8028
8029void InternalArrayConstructorStub::Generate(MacroAssembler* masm) {
8030 // ----------- S t a t e -------------
8031 // -- eax : argc
8032 // -- ebx : type info cell
8033 // -- edi : constructor
8034 // -- esp[0] : return address
8035 // -- esp[4] : last argument
8036 // -----------------------------------
8037
8038 if (FLAG_debug_code) {
8039 // The array construct code is only set for the global and natives
8040 // builtin Array functions which always have maps.
8041
8042 // Initial map for the builtin Array function should be a map.
8043 __ mov(ecx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
8044 // Will both indicate a NULL and a Smi.
8045 __ test(ecx, Immediate(kSmiTagMask));
8046 __ Assert(not_zero, "Unexpected initial map for Array function");
8047 __ CmpObjectType(ecx, MAP_TYPE, ecx);
8048 __ Assert(equal, "Unexpected initial map for Array function");
8049 }
8050
8051 if (FLAG_optimize_constructed_arrays) {
8052 // Figure out the right elements kind
8053 __ mov(ecx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
8054
8055 // Load the map's "bit field 2" into |result|. We only need the first byte,
8056 // but the following masking takes care of that anyway.
8057 __ mov(ecx, FieldOperand(ecx, Map::kBitField2Offset));
8058 // Retrieve elements_kind from bit field 2.
8059 __ and_(ecx, Map::kElementsKindMask);
8060 __ shr(ecx, Map::kElementsKindShift);
8061
8062 if (FLAG_debug_code) {
8063 Label done;
8064 __ cmp(ecx, Immediate(FAST_ELEMENTS));
8065 __ j(equal, &done);
8066 __ cmp(ecx, Immediate(FAST_HOLEY_ELEMENTS));
8067 __ Assert(equal,
8068 "Invalid ElementsKind for InternalArray or InternalPackedArray");
8069 __ bind(&done);
8070 }
8071
8072 Label fast_elements_case;
8073 __ cmp(ecx, Immediate(FAST_ELEMENTS));
8074 __ j(equal, &fast_elements_case);
8075 GenerateCase(masm, FAST_HOLEY_ELEMENTS);
8076
8077 __ bind(&fast_elements_case);
8078 GenerateCase(masm, FAST_ELEMENTS);
8079 } else {
8080 Label generic_constructor;
8081 // Run the native code for the Array function called as constructor.
8082 ArrayNativeCode(masm, true, &generic_constructor);
8083
8084 // Jump to the generic construct code in case the specialized code cannot
8085 // handle the construction.
8086 __ bind(&generic_constructor);
8087 Handle<Code> generic_construct_stub =
8088 masm->isolate()->builtins()->JSConstructStubGeneric();
8089 __ jmp(generic_construct_stub, RelocInfo::CODE_TARGET);
8090 }
8091}
8092
8093
ricow@chromium.org65fae842010-08-25 15:26:24 +00008094#undef __
8095
8096} } // namespace v8::internal
8097
8098#endif // V8_TARGET_ARCH_IA32