blob: 31f4f4b12ea0228ae536c18a73c5d9d9df0083cb [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
jkummerow@chromium.org93a47f42013-07-02 14:43:41 +000030#if V8_TARGET_ARCH_IA32
ricow@chromium.org65fae842010-08-25 15:26:24 +000031
32#include "bootstrapper.h"
vegorov@chromium.org7304bca2011-05-16 12:14:13 +000033#include "code-stubs.h"
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000034#include "isolate.h"
vegorov@chromium.org7304bca2011-05-16 12:14:13 +000035#include "jsregexp.h"
ricow@chromium.org65fae842010-08-25 15:26:24 +000036#include "regexp-macro-assembler.h"
danno@chromium.org94b0d6f2013-02-04 13:33:20 +000037#include "runtime.h"
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +000038#include "stub-cache.h"
erikcorry0ad885c2011-11-21 13:51:57 +000039#include "codegen.h"
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +000040#include "runtime.h"
ricow@chromium.org65fae842010-08-25 15:26:24 +000041
42namespace v8 {
43namespace internal {
44
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000045
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000046void FastCloneShallowArrayStub::InitializeInterfaceDescriptor(
47 Isolate* isolate,
48 CodeStubInterfaceDescriptor* descriptor) {
49 static Register registers[] = { eax, ebx, ecx };
50 descriptor->register_param_count_ = 3;
51 descriptor->register_params_ = registers;
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000052 descriptor->deoptimization_handler_ =
53 Runtime::FunctionForId(Runtime::kCreateArrayLiteralShallow)->entry;
54}
55
56
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +000057void FastCloneShallowObjectStub::InitializeInterfaceDescriptor(
58 Isolate* isolate,
59 CodeStubInterfaceDescriptor* descriptor) {
60 static Register registers[] = { eax, ebx, ecx, edx };
61 descriptor->register_param_count_ = 4;
62 descriptor->register_params_ = registers;
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +000063 descriptor->deoptimization_handler_ =
64 Runtime::FunctionForId(Runtime::kCreateObjectLiteralShallow)->entry;
65}
66
67
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000068void KeyedLoadFastElementStub::InitializeInterfaceDescriptor(
69 Isolate* isolate,
70 CodeStubInterfaceDescriptor* descriptor) {
71 static Register registers[] = { edx, ecx };
72 descriptor->register_param_count_ = 2;
73 descriptor->register_params_ = registers;
74 descriptor->deoptimization_handler_ =
mstarzinger@chromium.orge3b8d0f2013-02-01 09:06:41 +000075 FUNCTION_ADDR(KeyedLoadIC_MissFromStubFailure);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000076}
77
78
ulan@chromium.org57ff8812013-05-10 08:16:55 +000079void LoadFieldStub::InitializeInterfaceDescriptor(
80 Isolate* isolate,
81 CodeStubInterfaceDescriptor* descriptor) {
82 static Register registers[] = { edx };
83 descriptor->register_param_count_ = 1;
84 descriptor->register_params_ = registers;
ulan@chromium.org57ff8812013-05-10 08:16:55 +000085 descriptor->deoptimization_handler_ = NULL;
86}
87
88
89void KeyedLoadFieldStub::InitializeInterfaceDescriptor(
90 Isolate* isolate,
91 CodeStubInterfaceDescriptor* descriptor) {
92 static Register registers[] = { edx };
93 descriptor->register_param_count_ = 1;
94 descriptor->register_params_ = registers;
ulan@chromium.org57ff8812013-05-10 08:16:55 +000095 descriptor->deoptimization_handler_ = NULL;
96}
97
98
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +000099void KeyedStoreFastElementStub::InitializeInterfaceDescriptor(
100 Isolate* isolate,
101 CodeStubInterfaceDescriptor* descriptor) {
102 static Register registers[] = { edx, ecx, eax };
103 descriptor->register_param_count_ = 3;
104 descriptor->register_params_ = registers;
105 descriptor->deoptimization_handler_ =
106 FUNCTION_ADDR(KeyedStoreIC_MissFromStubFailure);
107}
108
109
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000110void TransitionElementsKindStub::InitializeInterfaceDescriptor(
111 Isolate* isolate,
112 CodeStubInterfaceDescriptor* descriptor) {
113 static Register registers[] = { eax, ebx };
114 descriptor->register_param_count_ = 2;
115 descriptor->register_params_ = registers;
116 descriptor->deoptimization_handler_ =
117 Runtime::FunctionForId(Runtime::kTransitionElementsKind)->entry;
118}
119
120
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000121static void InitializeArrayConstructorDescriptor(
122 Isolate* isolate,
123 CodeStubInterfaceDescriptor* descriptor,
124 int constant_stack_parameter_count) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000125 // register state
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000126 // eax -- number of arguments
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000127 // edi -- function
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000128 // ebx -- type info cell with elements kind
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000129 static Register registers[] = { edi, ebx };
130 descriptor->register_param_count_ = 2;
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000131
132 if (constant_stack_parameter_count != 0) {
133 // stack param count needs (constructor pointer, and single argument)
134 descriptor->stack_parameter_count_ = &eax;
135 }
136 descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000137 descriptor->register_params_ = registers;
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +0000138 descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000139 descriptor->deoptimization_handler_ =
ulan@chromium.orgdfe53072013-06-06 14:14:51 +0000140 Runtime::FunctionForId(Runtime::kArrayConstructor)->entry;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000141}
142
143
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +0000144static void InitializeInternalArrayConstructorDescriptor(
145 Isolate* isolate,
146 CodeStubInterfaceDescriptor* descriptor,
147 int constant_stack_parameter_count) {
148 // register state
149 // eax -- number of arguments
150 // edi -- constructor function
151 static Register registers[] = { edi };
152 descriptor->register_param_count_ = 1;
153
154 if (constant_stack_parameter_count != 0) {
155 // stack param count needs (constructor pointer, and single argument)
156 descriptor->stack_parameter_count_ = &eax;
157 }
158 descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
159 descriptor->register_params_ = registers;
160 descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
161 descriptor->deoptimization_handler_ =
ulan@chromium.orgdfe53072013-06-06 14:14:51 +0000162 Runtime::FunctionForId(Runtime::kInternalArrayConstructor)->entry;
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +0000163}
164
165
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000166void ArrayNoArgumentConstructorStub::InitializeInterfaceDescriptor(
167 Isolate* isolate,
168 CodeStubInterfaceDescriptor* descriptor) {
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000169 InitializeArrayConstructorDescriptor(isolate, descriptor, 0);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000170}
171
172
173void ArraySingleArgumentConstructorStub::InitializeInterfaceDescriptor(
174 Isolate* isolate,
175 CodeStubInterfaceDescriptor* descriptor) {
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000176 InitializeArrayConstructorDescriptor(isolate, descriptor, 1);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000177}
178
179
180void ArrayNArgumentsConstructorStub::InitializeInterfaceDescriptor(
181 Isolate* isolate,
182 CodeStubInterfaceDescriptor* descriptor) {
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000183 InitializeArrayConstructorDescriptor(isolate, descriptor, -1);
184}
185
186
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +0000187void InternalArrayNoArgumentConstructorStub::InitializeInterfaceDescriptor(
188 Isolate* isolate,
189 CodeStubInterfaceDescriptor* descriptor) {
190 InitializeInternalArrayConstructorDescriptor(isolate, descriptor, 0);
191}
192
193
194void InternalArraySingleArgumentConstructorStub::InitializeInterfaceDescriptor(
195 Isolate* isolate,
196 CodeStubInterfaceDescriptor* descriptor) {
197 InitializeInternalArrayConstructorDescriptor(isolate, descriptor, 1);
198}
199
200
201void InternalArrayNArgumentsConstructorStub::InitializeInterfaceDescriptor(
202 Isolate* isolate,
203 CodeStubInterfaceDescriptor* descriptor) {
204 InitializeInternalArrayConstructorDescriptor(isolate, descriptor, -1);
205}
206
207
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000208void CompareNilICStub::InitializeInterfaceDescriptor(
209 Isolate* isolate,
210 CodeStubInterfaceDescriptor* descriptor) {
211 static Register registers[] = { eax };
212 descriptor->register_param_count_ = 1;
213 descriptor->register_params_ = registers;
214 descriptor->deoptimization_handler_ =
215 FUNCTION_ADDR(CompareNilIC_Miss);
svenpanne@chromium.orga53e8e02013-05-24 12:35:50 +0000216 descriptor->SetMissHandler(
217 ExternalReference(IC_Utility(IC::kCompareNilIC_Miss), isolate));
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000218}
219
rossberg@chromium.orgb99c7542013-05-31 11:40:45 +0000220void ToBooleanStub::InitializeInterfaceDescriptor(
221 Isolate* isolate,
222 CodeStubInterfaceDescriptor* descriptor) {
223 static Register registers[] = { eax };
224 descriptor->register_param_count_ = 1;
225 descriptor->register_params_ = registers;
226 descriptor->deoptimization_handler_ =
227 FUNCTION_ADDR(ToBooleanIC_Miss);
228 descriptor->SetMissHandler(
229 ExternalReference(IC_Utility(IC::kToBooleanIC_Miss), isolate));
230}
231
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000232
mstarzinger@chromium.orge0e1b0d2013-07-08 08:38:06 +0000233void UnaryOpStub::InitializeInterfaceDescriptor(
234 Isolate* isolate,
235 CodeStubInterfaceDescriptor* descriptor) {
236 static Register registers[] = { eax };
237 descriptor->register_param_count_ = 1;
238 descriptor->register_params_ = registers;
239 descriptor->deoptimization_handler_ =
240 FUNCTION_ADDR(UnaryOpIC_Miss);
241}
242
243
244void StoreGlobalStub::InitializeInterfaceDescriptor(
245 Isolate* isolate,
246 CodeStubInterfaceDescriptor* descriptor) {
247 static Register registers[] = { edx, ecx, eax };
248 descriptor->register_param_count_ = 3;
249 descriptor->register_params_ = registers;
250 descriptor->deoptimization_handler_ =
251 FUNCTION_ADDR(StoreIC_MissFromStubFailure);
252}
253
254
ricow@chromium.org65fae842010-08-25 15:26:24 +0000255#define __ ACCESS_MASM(masm)
whesse@chromium.org7a392b32011-01-31 11:30:36 +0000256
ulan@chromium.org77ca49a2013-04-22 09:43:56 +0000257
258void HydrogenCodeStub::GenerateLightweightMiss(MacroAssembler* masm) {
259 // Update the static counter each time a new code stub is generated.
260 Isolate* isolate = masm->isolate();
261 isolate->counters()->code_stubs()->Increment();
262
263 CodeStubInterfaceDescriptor* descriptor = GetInterfaceDescriptor(isolate);
264 int param_count = descriptor->register_param_count_;
265 {
266 // Call the runtime system in a fresh internal frame.
267 FrameScope scope(masm, StackFrame::INTERNAL);
268 ASSERT(descriptor->register_param_count_ == 0 ||
269 eax.is(descriptor->register_params_[param_count - 1]));
270 // Push arguments
271 for (int i = 0; i < param_count; ++i) {
272 __ push(descriptor->register_params_[i]);
273 }
svenpanne@chromium.orga53e8e02013-05-24 12:35:50 +0000274 ExternalReference miss = descriptor->miss_handler();
ulan@chromium.org77ca49a2013-04-22 09:43:56 +0000275 __ CallExternalReference(miss, descriptor->register_param_count_);
276 }
277
278 __ ret(0);
279}
280
281
whesse@chromium.org7a392b32011-01-31 11:30:36 +0000282void ToNumberStub::Generate(MacroAssembler* masm) {
283 // The ToNumber stub takes one argument in eax.
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000284 Label check_heap_number, call_builtin;
whesse@chromium.org7b260152011-06-20 15:33:18 +0000285 __ JumpIfNotSmi(eax, &check_heap_number, Label::kNear);
whesse@chromium.org7a392b32011-01-31 11:30:36 +0000286 __ ret(0);
287
288 __ bind(&check_heap_number);
289 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000290 Factory* factory = masm->isolate()->factory();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000291 __ cmp(ebx, Immediate(factory->heap_number_map()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000292 __ j(not_equal, &call_builtin, Label::kNear);
whesse@chromium.org7a392b32011-01-31 11:30:36 +0000293 __ ret(0);
294
295 __ bind(&call_builtin);
296 __ pop(ecx); // Pop return address.
297 __ push(eax);
298 __ push(ecx); // Push return address.
299 __ InvokeBuiltin(Builtins::TO_NUMBER, JUMP_FUNCTION);
300}
301
302
ricow@chromium.org65fae842010-08-25 15:26:24 +0000303void FastNewClosureStub::Generate(MacroAssembler* masm) {
304 // Create a new closure from the given function info in new
305 // space. Set the context to the current context in esi.
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000306 Counters* counters = masm->isolate()->counters();
307
ricow@chromium.org65fae842010-08-25 15:26:24 +0000308 Label gc;
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000309 __ Allocate(JSFunction::kSize, eax, ebx, ecx, &gc, TAG_OBJECT);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000310
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000311 __ IncrementCounter(counters->fast_new_closure_total(), 1);
312
ricow@chromium.org65fae842010-08-25 15:26:24 +0000313 // Get the function info from the stack.
314 __ mov(edx, Operand(esp, 1 * kPointerSize));
315
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000316 int map_index = Context::FunctionMapIndex(language_mode_, is_generator_);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000317
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000318 // Compute the function map in the current native context and set that
ricow@chromium.org65fae842010-08-25 15:26:24 +0000319 // as the map of the allocated object.
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000320 __ mov(ecx, Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
321 __ mov(ecx, FieldOperand(ecx, GlobalObject::kNativeContextOffset));
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000322 __ mov(ebx, Operand(ecx, Context::SlotOffset(map_index)));
323 __ mov(FieldOperand(eax, JSObject::kMapOffset), ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000324
325 // Initialize the rest of the function. We don't have to update the
326 // write barrier because the allocated object is in new space.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000327 Factory* factory = masm->isolate()->factory();
328 __ mov(ebx, Immediate(factory->empty_fixed_array()));
ricow@chromium.org65fae842010-08-25 15:26:24 +0000329 __ mov(FieldOperand(eax, JSObject::kPropertiesOffset), ebx);
330 __ mov(FieldOperand(eax, JSObject::kElementsOffset), ebx);
331 __ mov(FieldOperand(eax, JSFunction::kPrototypeOrInitialMapOffset),
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000332 Immediate(factory->the_hole_value()));
ricow@chromium.org65fae842010-08-25 15:26:24 +0000333 __ mov(FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset), edx);
334 __ mov(FieldOperand(eax, JSFunction::kContextOffset), esi);
335 __ mov(FieldOperand(eax, JSFunction::kLiteralsOffset), ebx);
336
337 // Initialize the code pointer in the function to be the one
338 // found in the shared function info object.
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000339 // But first check if there is an optimized version for our context.
340 Label check_optimized;
341 Label install_unoptimized;
342 if (FLAG_cache_optimized_code) {
343 __ mov(ebx, FieldOperand(edx, SharedFunctionInfo::kOptimizedCodeMapOffset));
344 __ test(ebx, ebx);
345 __ j(not_zero, &check_optimized, Label::kNear);
346 }
347 __ bind(&install_unoptimized);
348 __ mov(FieldOperand(eax, JSFunction::kNextFunctionLinkOffset),
349 Immediate(factory->undefined_value()));
ricow@chromium.org65fae842010-08-25 15:26:24 +0000350 __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
351 __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
352 __ mov(FieldOperand(eax, JSFunction::kCodeEntryOffset), edx);
353
354 // Return and remove the on-stack parameter.
355 __ ret(1 * kPointerSize);
356
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000357 __ bind(&check_optimized);
358
359 __ IncrementCounter(counters->fast_new_closure_try_optimized(), 1);
360
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000361 // ecx holds native context, ebx points to fixed array of 3-element entries
362 // (native context, optimized code, literals).
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000363 // Map must never be empty, so check the first elements.
364 Label install_optimized;
365 // Speculatively move code object into edx.
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +0000366 __ mov(edx, FieldOperand(ebx, SharedFunctionInfo::kFirstCodeSlot));
367 __ cmp(ecx, FieldOperand(ebx, SharedFunctionInfo::kFirstContextSlot));
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000368 __ j(equal, &install_optimized);
369
370 // Iterate through the rest of map backwards. edx holds an index as a Smi.
371 Label loop;
372 Label restore;
373 __ mov(edx, FieldOperand(ebx, FixedArray::kLengthOffset));
374 __ bind(&loop);
375 // Do not double check first entry.
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +0000376 __ cmp(edx, Immediate(Smi::FromInt(SharedFunctionInfo::kSecondEntryIndex)));
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000377 __ j(equal, &restore);
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +0000378 __ sub(edx, Immediate(Smi::FromInt(SharedFunctionInfo::kEntryLength)));
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000379 __ cmp(ecx, CodeGenerator::FixedArrayElementOperand(ebx, edx, 0));
380 __ j(not_equal, &loop, Label::kNear);
381 // Hit: fetch the optimized code.
382 __ mov(edx, CodeGenerator::FixedArrayElementOperand(ebx, edx, 1));
383
384 __ bind(&install_optimized);
385 __ IncrementCounter(counters->fast_new_closure_install_optimized(), 1);
386
387 // TODO(fschneider): Idea: store proper code pointers in the optimized code
388 // map and either unmangle them on marking or do nothing as the whole map is
389 // discarded on major GC anyway.
390 __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
391 __ mov(FieldOperand(eax, JSFunction::kCodeEntryOffset), edx);
392
393 // Now link a function into a list of optimized functions.
394 __ mov(edx, ContextOperand(ecx, Context::OPTIMIZED_FUNCTIONS_LIST));
395
396 __ mov(FieldOperand(eax, JSFunction::kNextFunctionLinkOffset), edx);
397 // No need for write barrier as JSFunction (eax) is in the new space.
398
399 __ mov(ContextOperand(ecx, Context::OPTIMIZED_FUNCTIONS_LIST), eax);
400 // Store JSFunction (eax) into edx before issuing write barrier as
401 // it clobbers all the registers passed.
402 __ mov(edx, eax);
403 __ RecordWriteContextSlot(
404 ecx,
405 Context::SlotOffset(Context::OPTIMIZED_FUNCTIONS_LIST),
406 edx,
407 ebx,
408 kDontSaveFPRegs);
409
410 // Return and remove the on-stack parameter.
411 __ ret(1 * kPointerSize);
412
413 __ bind(&restore);
414 // Restore SharedFunctionInfo into edx.
415 __ mov(edx, Operand(esp, 1 * kPointerSize));
416 __ jmp(&install_unoptimized);
417
ricow@chromium.org65fae842010-08-25 15:26:24 +0000418 // Create a new closure through the slower runtime call.
419 __ bind(&gc);
420 __ pop(ecx); // Temporarily remove return address.
421 __ pop(edx);
422 __ push(esi);
423 __ push(edx);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000424 __ push(Immediate(factory->false_value()));
ricow@chromium.org65fae842010-08-25 15:26:24 +0000425 __ push(ecx); // Restore return address.
vegorov@chromium.org21b5e952010-11-23 10:24:40 +0000426 __ TailCallRuntime(Runtime::kNewClosure, 3, 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000427}
428
429
430void FastNewContextStub::Generate(MacroAssembler* masm) {
431 // Try to allocate the context in new space.
432 Label gc;
ager@chromium.org0ee099b2011-01-25 14:06:47 +0000433 int length = slots_ + Context::MIN_CONTEXT_SLOTS;
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000434 __ Allocate((length * kPointerSize) + FixedArray::kHeaderSize,
435 eax, ebx, ecx, &gc, TAG_OBJECT);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000436
437 // Get the function from the stack.
438 __ mov(ecx, Operand(esp, 1 * kPointerSize));
439
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000440 // Set up the object header.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000441 Factory* factory = masm->isolate()->factory();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000442 __ mov(FieldOperand(eax, HeapObject::kMapOffset),
443 factory->function_context_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +0000444 __ mov(FieldOperand(eax, Context::kLengthOffset),
ager@chromium.org0ee099b2011-01-25 14:06:47 +0000445 Immediate(Smi::FromInt(length)));
ricow@chromium.org65fae842010-08-25 15:26:24 +0000446
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000447 // Set up the fixed slots.
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000448 __ Set(ebx, Immediate(0)); // Set to NULL.
ricow@chromium.org65fae842010-08-25 15:26:24 +0000449 __ mov(Operand(eax, Context::SlotOffset(Context::CLOSURE_INDEX)), ecx);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000450 __ mov(Operand(eax, Context::SlotOffset(Context::PREVIOUS_INDEX)), esi);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000451 __ mov(Operand(eax, Context::SlotOffset(Context::EXTENSION_INDEX)), ebx);
452
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000453 // Copy the global object from the previous context.
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000454 __ mov(ebx, Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
455 __ mov(Operand(eax, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)), ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000456
457 // Initialize the rest of the slots to undefined.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000458 __ mov(ebx, factory->undefined_value());
ager@chromium.org0ee099b2011-01-25 14:06:47 +0000459 for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
ricow@chromium.org65fae842010-08-25 15:26:24 +0000460 __ mov(Operand(eax, Context::SlotOffset(i)), ebx);
461 }
462
463 // Return and remove the on-stack parameter.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000464 __ mov(esi, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000465 __ ret(1 * kPointerSize);
466
467 // Need to collect. Call into runtime system.
468 __ bind(&gc);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000469 __ TailCallRuntime(Runtime::kNewFunctionContext, 1, 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000470}
471
472
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000473void FastNewBlockContextStub::Generate(MacroAssembler* masm) {
474 // Stack layout on entry:
475 //
476 // [esp + (1 * kPointerSize)]: function
477 // [esp + (2 * kPointerSize)]: serialized scope info
478
479 // Try to allocate the context in new space.
480 Label gc;
481 int length = slots_ + Context::MIN_CONTEXT_SLOTS;
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000482 __ Allocate(FixedArray::SizeFor(length), eax, ebx, ecx, &gc, TAG_OBJECT);
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000483
484 // Get the function or sentinel from the stack.
485 __ mov(ecx, Operand(esp, 1 * kPointerSize));
486
487 // Get the serialized scope info from the stack.
488 __ mov(ebx, Operand(esp, 2 * kPointerSize));
489
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000490 // Set up the object header.
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000491 Factory* factory = masm->isolate()->factory();
492 __ mov(FieldOperand(eax, HeapObject::kMapOffset),
493 factory->block_context_map());
494 __ mov(FieldOperand(eax, Context::kLengthOffset),
495 Immediate(Smi::FromInt(length)));
496
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000497 // If this block context is nested in the native context we get a smi
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000498 // sentinel instead of a function. The block context should get the
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000499 // canonical empty function of the native context as its closure which
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000500 // we still have to look up.
501 Label after_sentinel;
502 __ JumpIfNotSmi(ecx, &after_sentinel, Label::kNear);
503 if (FLAG_debug_code) {
504 const char* message = "Expected 0 as a Smi sentinel";
505 __ cmp(ecx, 0);
506 __ Assert(equal, message);
507 }
508 __ mov(ecx, GlobalObjectOperand());
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000509 __ mov(ecx, FieldOperand(ecx, GlobalObject::kNativeContextOffset));
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000510 __ mov(ecx, ContextOperand(ecx, Context::CLOSURE_INDEX));
511 __ bind(&after_sentinel);
512
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000513 // Set up the fixed slots.
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000514 __ mov(ContextOperand(eax, Context::CLOSURE_INDEX), ecx);
515 __ mov(ContextOperand(eax, Context::PREVIOUS_INDEX), esi);
516 __ mov(ContextOperand(eax, Context::EXTENSION_INDEX), ebx);
517
518 // Copy the global object from the previous context.
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000519 __ mov(ebx, ContextOperand(esi, Context::GLOBAL_OBJECT_INDEX));
520 __ mov(ContextOperand(eax, Context::GLOBAL_OBJECT_INDEX), ebx);
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000521
522 // Initialize the rest of the slots to the hole value.
523 if (slots_ == 1) {
524 __ mov(ContextOperand(eax, Context::MIN_CONTEXT_SLOTS),
525 factory->the_hole_value());
526 } else {
527 __ mov(ebx, factory->the_hole_value());
528 for (int i = 0; i < slots_; i++) {
529 __ mov(ContextOperand(eax, i + Context::MIN_CONTEXT_SLOTS), ebx);
530 }
531 }
532
533 // Return and remove the on-stack parameters.
534 __ mov(esi, eax);
535 __ ret(2 * kPointerSize);
536
537 // Need to collect. Call into runtime system.
538 __ bind(&gc);
539 __ TailCallRuntime(Runtime::kPushBlockContext, 2, 1);
540}
541
542
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000543void StoreBufferOverflowStub::Generate(MacroAssembler* masm) {
544 // We don't allow a GC during a store buffer overflow so there is no need to
545 // store the registers in any particular way, but we do have to store and
546 // restore them.
547 __ pushad();
548 if (save_doubles_ == kSaveFPRegs) {
ulan@chromium.org750145a2013-03-07 15:14:13 +0000549 CpuFeatureScope scope(masm, SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000550 __ sub(esp, Immediate(kDoubleSize * XMMRegister::kNumRegisters));
551 for (int i = 0; i < XMMRegister::kNumRegisters; i++) {
552 XMMRegister reg = XMMRegister::from_code(i);
553 __ movdbl(Operand(esp, i * kDoubleSize), reg);
554 }
555 }
556 const int argument_count = 1;
557
558 AllowExternalCallThatCantCauseGC scope(masm);
559 __ PrepareCallCFunction(argument_count, ecx);
560 __ mov(Operand(esp, 0 * kPointerSize),
ulan@chromium.org32d7dba2013-04-24 10:59:06 +0000561 Immediate(ExternalReference::isolate_address(masm->isolate())));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000562 __ CallCFunction(
563 ExternalReference::store_buffer_overflow_function(masm->isolate()),
564 argument_count);
565 if (save_doubles_ == kSaveFPRegs) {
ulan@chromium.org750145a2013-03-07 15:14:13 +0000566 CpuFeatureScope scope(masm, SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000567 for (int i = 0; i < XMMRegister::kNumRegisters; i++) {
568 XMMRegister reg = XMMRegister::from_code(i);
569 __ movdbl(reg, Operand(esp, i * kDoubleSize));
570 }
571 __ add(esp, Immediate(kDoubleSize * XMMRegister::kNumRegisters));
572 }
573 __ popad();
574 __ ret(0);
575}
576
577
ricow@chromium.org65fae842010-08-25 15:26:24 +0000578class FloatingPointHelper : public AllStatic {
579 public:
ricow@chromium.org65fae842010-08-25 15:26:24 +0000580 enum ArgLocation {
581 ARGS_ON_STACK,
582 ARGS_IN_REGISTERS
583 };
584
585 // Code pattern for loading a floating point value. Input value must
586 // be either a smi or a heap number object (fp value). Requirements:
587 // operand in register number. Returns operand as floating point number
588 // on FPU stack.
589 static void LoadFloatOperand(MacroAssembler* masm, Register number);
590
591 // Code pattern for loading floating point values. Input values must
592 // be either smi or heap number objects (fp values). Requirements:
593 // operand_1 on TOS+1 or in edx, operand_2 on TOS+2 or in eax.
594 // Returns operands as floating point numbers on FPU stack.
595 static void LoadFloatOperands(MacroAssembler* masm,
596 Register scratch,
597 ArgLocation arg_location = ARGS_ON_STACK);
598
599 // Similar to LoadFloatOperand but assumes that both operands are smis.
600 // Expects operands in edx, eax.
601 static void LoadFloatSmis(MacroAssembler* masm, Register scratch);
602
603 // Test if operands are smi or number objects (fp). Requirements:
604 // operand_1 in eax, operand_2 in edx; falls through on float
605 // operands, jumps to the non_float label otherwise.
606 static void CheckFloatOperands(MacroAssembler* masm,
607 Label* non_float,
608 Register scratch);
609
610 // Takes the operands in edx and eax and loads them as integers in eax
611 // and ecx.
ricow@chromium.org65fae842010-08-25 15:26:24 +0000612 static void LoadUnknownsAsIntegers(MacroAssembler* masm,
613 bool use_sse3,
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +0000614 BinaryOpIC::TypeInfo left_type,
615 BinaryOpIC::TypeInfo right_type,
ricow@chromium.org65fae842010-08-25 15:26:24 +0000616 Label* operand_conversion_failure);
617
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000618 // Assumes that operands are smis or heap numbers and loads them
619 // into xmm0 and xmm1. Operands are in edx and eax.
ricow@chromium.org65fae842010-08-25 15:26:24 +0000620 // Leaves operands unchanged.
621 static void LoadSSE2Operands(MacroAssembler* masm);
622
623 // Test if operands are numbers (smi or HeapNumber objects), and load
624 // them into xmm0 and xmm1 if they are. Jump to label not_numbers if
625 // either operand is not a number. Operands are in edx and eax.
626 // Leaves operands unchanged.
627 static void LoadSSE2Operands(MacroAssembler* masm, Label* not_numbers);
628
629 // Similar to LoadSSE2Operands but assumes that both operands are smis.
630 // Expects operands in edx, eax.
631 static void LoadSSE2Smis(MacroAssembler* masm, Register scratch);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000632
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +0000633 // Checks that |operand| has an int32 value. If |int32_result| is different
634 // from |scratch|, it will contain that int32 value.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000635 static void CheckSSE2OperandIsInt32(MacroAssembler* masm,
636 Label* non_int32,
637 XMMRegister operand,
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +0000638 Register int32_result,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000639 Register scratch,
640 XMMRegister xmm_scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000641};
642
643
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000644// Get the integer part of a heap number. Surprisingly, all this bit twiddling
645// is faster than using the built-in instructions on floating point registers.
646// Trashes edi and ebx. Dest is ecx. Source cannot be ecx or one of the
647// trashed registers.
648static void IntegerConvert(MacroAssembler* masm,
649 Register source,
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000650 bool use_sse3,
651 Label* conversion_failure) {
652 ASSERT(!source.is(ecx) && !source.is(edi) && !source.is(ebx));
653 Label done, right_exponent, normal_exponent;
654 Register scratch = ebx;
655 Register scratch2 = edi;
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000656 // Get exponent word.
657 __ mov(scratch, FieldOperand(source, HeapNumber::kExponentOffset));
658 // Get exponent alone in scratch2.
659 __ mov(scratch2, scratch);
660 __ and_(scratch2, HeapNumber::kExponentMask);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000661 __ shr(scratch2, HeapNumber::kExponentShift);
662 __ sub(scratch2, Immediate(HeapNumber::kExponentBias));
663 // Load ecx with zero. We use this either for the final shift or
664 // for the answer.
665 __ xor_(ecx, ecx);
666 // If the exponent is above 83, the number contains no significant
667 // bits in the range 0..2^31, so the result is zero.
668 static const uint32_t kResultIsZeroExponent = 83;
669 __ cmp(scratch2, Immediate(kResultIsZeroExponent));
670 __ j(above, &done);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000671 if (use_sse3) {
ulan@chromium.org750145a2013-03-07 15:14:13 +0000672 CpuFeatureScope scope(masm, SSE3);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000673 // Check whether the exponent is too big for a 64 bit signed integer.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000674 static const uint32_t kTooBigExponent = 63;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000675 __ cmp(scratch2, Immediate(kTooBigExponent));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000676 __ j(greater_equal, conversion_failure);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000677 // Load x87 register with heap number.
678 __ fld_d(FieldOperand(source, HeapNumber::kValueOffset));
679 // Reserve space for 64 bit answer.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000680 __ sub(esp, Immediate(sizeof(uint64_t))); // Nolint.
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000681 // Do conversion, which cannot fail because we checked the exponent.
682 __ fisttp_d(Operand(esp, 0));
683 __ mov(ecx, Operand(esp, 0)); // Load low word of answer into ecx.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000684 __ add(esp, Immediate(sizeof(uint64_t))); // Nolint.
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000685 } else {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000686 // Check whether the exponent matches a 32 bit signed int that cannot be
687 // represented by a Smi. A non-smi 32 bit integer is 1.xxx * 2^30 so the
688 // exponent is 30 (biased). This is the exponent that we are fastest at and
689 // also the highest exponent we can handle here.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000690 const uint32_t non_smi_exponent = 30;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000691 __ cmp(scratch2, Immediate(non_smi_exponent));
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000692 // If we have a match of the int32-but-not-Smi exponent then skip some
693 // logic.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000694 __ j(equal, &right_exponent, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000695 // If the exponent is higher than that then go to slow case. This catches
696 // numbers that don't fit in a signed int32, infinities and NaNs.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000697 __ j(less, &normal_exponent, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000698
699 {
700 // Handle a big exponent. The only reason we have this code is that the
701 // >>> operator has a tendency to generate numbers with an exponent of 31.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000702 const uint32_t big_non_smi_exponent = 31;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000703 __ cmp(scratch2, Immediate(big_non_smi_exponent));
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000704 __ j(not_equal, conversion_failure);
705 // We have the big exponent, typically from >>>. This means the number is
706 // in the range 2^31 to 2^32 - 1. Get the top bits of the mantissa.
707 __ mov(scratch2, scratch);
708 __ and_(scratch2, HeapNumber::kMantissaMask);
709 // Put back the implicit 1.
710 __ or_(scratch2, 1 << HeapNumber::kExponentShift);
711 // Shift up the mantissa bits to take up the space the exponent used to
712 // take. We just orred in the implicit bit so that took care of one and
713 // we want to use the full unsigned range so we subtract 1 bit from the
714 // shift distance.
715 const int big_shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 1;
716 __ shl(scratch2, big_shift_distance);
717 // Get the second half of the double.
718 __ mov(ecx, FieldOperand(source, HeapNumber::kMantissaOffset));
719 // Shift down 21 bits to get the most significant 11 bits or the low
720 // mantissa word.
721 __ shr(ecx, 32 - big_shift_distance);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000722 __ or_(ecx, scratch2);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000723 // We have the answer in ecx, but we may need to negate it.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000724 __ test(scratch, scratch);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000725 __ j(positive, &done, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000726 __ neg(ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +0000727 __ jmp(&done, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000728 }
729
730 __ bind(&normal_exponent);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000731 // Exponent word in scratch, exponent in scratch2. Zero in ecx.
732 // We know that 0 <= exponent < 30.
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000733 __ mov(ecx, Immediate(30));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000734 __ sub(ecx, scratch2);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000735
736 __ bind(&right_exponent);
737 // Here ecx is the shift, scratch is the exponent word.
738 // Get the top bits of the mantissa.
739 __ and_(scratch, HeapNumber::kMantissaMask);
740 // Put back the implicit 1.
741 __ or_(scratch, 1 << HeapNumber::kExponentShift);
742 // Shift up the mantissa bits to take up the space the exponent used to
743 // take. We have kExponentShift + 1 significant bits int he low end of the
744 // word. Shift them to the top bits.
745 const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
746 __ shl(scratch, shift_distance);
747 // Get the second half of the double. For some exponents we don't
748 // actually need this because the bits get shifted out again, but
749 // it's probably slower to test than just to do it.
750 __ mov(scratch2, FieldOperand(source, HeapNumber::kMantissaOffset));
751 // Shift down 22 bits to get the most significant 10 bits or the low
752 // mantissa word.
753 __ shr(scratch2, 32 - shift_distance);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000754 __ or_(scratch2, scratch);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000755 // Move down according to the exponent.
756 __ shr_cl(scratch2);
757 // Now the unsigned answer is in scratch2. We need to move it to ecx and
758 // we may need to fix the sign.
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000759 Label negative;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000760 __ xor_(ecx, ecx);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000761 __ cmp(ecx, FieldOperand(source, HeapNumber::kExponentOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000762 __ j(greater, &negative, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000763 __ mov(ecx, scratch2);
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000764 __ jmp(&done, Label::kNear);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000765 __ bind(&negative);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000766 __ sub(ecx, scratch2);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000767 }
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000768 __ bind(&done);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000769}
770
771
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +0000772// Uses SSE2 to convert the heap number in |source| to an integer. Jumps to
773// |conversion_failure| if the heap number did not contain an int32 value.
774// Result is in ecx. Trashes ebx, xmm0, and xmm1.
775static void ConvertHeapNumberToInt32(MacroAssembler* masm,
776 Register source,
777 Label* conversion_failure) {
778 __ movdbl(xmm0, FieldOperand(source, HeapNumber::kValueOffset));
779 FloatingPointHelper::CheckSSE2OperandIsInt32(
780 masm, conversion_failure, xmm0, ecx, ebx, xmm1);
781}
782
783
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000784void BinaryOpStub::Initialize() {
785 platform_specific_bit_ = CpuFeatures::IsSupported(SSE3);
786}
787
788
danno@chromium.org40cb8782011-05-25 07:58:50 +0000789void BinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000790 __ pop(ecx); // Save return address.
791 __ push(edx);
792 __ push(eax);
793 // Left and right arguments are now on top.
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000794 __ push(Immediate(Smi::FromInt(MinorKey())));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000795
796 __ push(ecx); // Push return address.
797
798 // Patch the caller to an appropriate specialized stub and return the
799 // operation result to the caller of the stub.
800 __ TailCallExternalReference(
danno@chromium.org40cb8782011-05-25 07:58:50 +0000801 ExternalReference(IC_Utility(IC::kBinaryOp_Patch),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000802 masm->isolate()),
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000803 3,
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000804 1);
805}
806
807
808// Prepare for a type transition runtime call when the args are already on
809// the stack, under the return address.
danno@chromium.org40cb8782011-05-25 07:58:50 +0000810void BinaryOpStub::GenerateTypeTransitionWithSavedArgs(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000811 __ pop(ecx); // Save return address.
812 // Left and right arguments are already on top of the stack.
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000813 __ push(Immediate(Smi::FromInt(MinorKey())));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000814
815 __ push(ecx); // Push return address.
816
817 // Patch the caller to an appropriate specialized stub and return the
818 // operation result to the caller of the stub.
819 __ TailCallExternalReference(
danno@chromium.org40cb8782011-05-25 07:58:50 +0000820 ExternalReference(IC_Utility(IC::kBinaryOp_Patch),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000821 masm->isolate()),
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000822 3,
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000823 1);
824}
825
826
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +0000827static void BinaryOpStub_GenerateRegisterArgsPop(MacroAssembler* masm) {
828 __ pop(ecx);
829 __ pop(eax);
830 __ pop(edx);
831 __ push(ecx);
832}
833
834
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000835static void BinaryOpStub_GenerateSmiCode(
danno@chromium.org40cb8782011-05-25 07:58:50 +0000836 MacroAssembler* masm,
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000837 Label* slow,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000838 BinaryOpStub::SmiCodeGenerateHeapNumberResults allow_heapnumber_results,
839 Token::Value op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000840 // 1. Move arguments into edx, eax except for DIV and MOD, which need the
841 // dividend in eax and edx free for the division. Use eax, ebx for those.
842 Comment load_comment(masm, "-- Load arguments");
843 Register left = edx;
844 Register right = eax;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000845 if (op == Token::DIV || op == Token::MOD) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000846 left = eax;
847 right = ebx;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000848 __ mov(ebx, eax);
849 __ mov(eax, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000850 }
851
852
853 // 2. Prepare the smi check of both operands by oring them together.
854 Comment smi_check_comment(masm, "-- Smi check arguments");
855 Label not_smis;
856 Register combined = ecx;
857 ASSERT(!left.is(combined) && !right.is(combined));
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000858 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000859 case Token::BIT_OR:
860 // Perform the operation into eax and smi check the result. Preserve
861 // eax in case the result is not a smi.
862 ASSERT(!left.is(ecx) && !right.is(ecx));
863 __ mov(ecx, right);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000864 __ or_(right, left); // Bitwise or is commutative.
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000865 combined = right;
866 break;
867
868 case Token::BIT_XOR:
869 case Token::BIT_AND:
870 case Token::ADD:
871 case Token::SUB:
872 case Token::MUL:
873 case Token::DIV:
874 case Token::MOD:
875 __ mov(combined, right);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000876 __ or_(combined, left);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000877 break;
878
879 case Token::SHL:
880 case Token::SAR:
881 case Token::SHR:
882 // Move the right operand into ecx for the shift operation, use eax
883 // for the smi check register.
884 ASSERT(!left.is(ecx) && !right.is(ecx));
885 __ mov(ecx, right);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000886 __ or_(right, left);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000887 combined = right;
888 break;
889
890 default:
891 break;
892 }
893
894 // 3. Perform the smi check of the operands.
895 STATIC_ASSERT(kSmiTag == 0); // Adjust zero check if not the case.
whesse@chromium.org7b260152011-06-20 15:33:18 +0000896 __ JumpIfNotSmi(combined, &not_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000897
898 // 4. Operands are both smis, perform the operation leaving the result in
899 // eax and check the result if necessary.
900 Comment perform_smi(masm, "-- Perform smi operation");
901 Label use_fp_on_smis;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000902 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000903 case Token::BIT_OR:
904 // Nothing to do.
905 break;
906
907 case Token::BIT_XOR:
908 ASSERT(right.is(eax));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000909 __ xor_(right, left); // Bitwise xor is commutative.
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000910 break;
911
912 case Token::BIT_AND:
913 ASSERT(right.is(eax));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000914 __ and_(right, left); // Bitwise and is commutative.
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000915 break;
916
917 case Token::SHL:
918 // Remove tags from operands (but keep sign).
919 __ SmiUntag(left);
920 __ SmiUntag(ecx);
921 // Perform the operation.
922 __ shl_cl(left);
923 // Check that the *signed* result fits in a smi.
924 __ cmp(left, 0xc0000000);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000925 __ j(sign, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000926 // Tag the result and store it in register eax.
927 __ SmiTag(left);
928 __ mov(eax, left);
929 break;
930
931 case Token::SAR:
932 // Remove tags from operands (but keep sign).
933 __ SmiUntag(left);
934 __ SmiUntag(ecx);
935 // Perform the operation.
936 __ sar_cl(left);
937 // Tag the result and store it in register eax.
938 __ SmiTag(left);
939 __ mov(eax, left);
940 break;
941
942 case Token::SHR:
943 // Remove tags from operands (but keep sign).
944 __ SmiUntag(left);
945 __ SmiUntag(ecx);
946 // Perform the operation.
947 __ shr_cl(left);
948 // Check that the *unsigned* result fits in a smi.
949 // Neither of the two high-order bits can be set:
950 // - 0x80000000: high bit would be lost when smi tagging.
951 // - 0x40000000: this number would convert to negative when
952 // Smi tagging these two cases can only happen with shifts
953 // by 0 or 1 when handed a valid smi.
954 __ test(left, Immediate(0xc0000000));
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000955 __ j(not_zero, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000956 // Tag the result and store it in register eax.
957 __ SmiTag(left);
958 __ mov(eax, left);
959 break;
960
961 case Token::ADD:
962 ASSERT(right.is(eax));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000963 __ add(right, left); // Addition is commutative.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000964 __ j(overflow, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000965 break;
966
967 case Token::SUB:
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000968 __ sub(left, right);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000969 __ j(overflow, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000970 __ mov(eax, left);
971 break;
972
973 case Token::MUL:
974 // If the smi tag is 0 we can just leave the tag on one operand.
975 STATIC_ASSERT(kSmiTag == 0); // Adjust code below if not the case.
976 // We can't revert the multiplication if the result is not a smi
977 // so save the right operand.
978 __ mov(ebx, right);
979 // Remove tag from one of the operands (but keep sign).
980 __ SmiUntag(right);
981 // Do multiplication.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000982 __ imul(right, left); // Multiplication is commutative.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000983 __ j(overflow, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000984 // Check for negative zero result. Use combined = left | right.
985 __ NegativeZeroTest(right, combined, &use_fp_on_smis);
986 break;
987
988 case Token::DIV:
989 // We can't revert the division if the result is not a smi so
990 // save the left operand.
991 __ mov(edi, left);
992 // Check for 0 divisor.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000993 __ test(right, right);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000994 __ j(zero, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000995 // Sign extend left into edx:eax.
996 ASSERT(left.is(eax));
997 __ cdq();
998 // Divide edx:eax by right.
999 __ idiv(right);
1000 // Check for the corner case of dividing the most negative smi by
1001 // -1. We cannot use the overflow flag, since it is not set by idiv
1002 // instruction.
1003 STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
1004 __ cmp(eax, 0x40000000);
1005 __ j(equal, &use_fp_on_smis);
1006 // Check for negative zero result. Use combined = left | right.
1007 __ NegativeZeroTest(eax, combined, &use_fp_on_smis);
1008 // Check that the remainder is zero.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001009 __ test(edx, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001010 __ j(not_zero, &use_fp_on_smis);
1011 // Tag the result and store it in register eax.
1012 __ SmiTag(eax);
1013 break;
1014
1015 case Token::MOD:
1016 // Check for 0 divisor.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001017 __ test(right, right);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00001018 __ j(zero, &not_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001019
1020 // Sign extend left into edx:eax.
1021 ASSERT(left.is(eax));
1022 __ cdq();
1023 // Divide edx:eax by right.
1024 __ idiv(right);
1025 // Check for negative zero result. Use combined = left | right.
1026 __ NegativeZeroTest(edx, combined, slow);
1027 // Move remainder to register eax.
1028 __ mov(eax, edx);
1029 break;
1030
1031 default:
1032 UNREACHABLE();
1033 }
1034
1035 // 5. Emit return of result in eax. Some operations have registers pushed.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001036 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001037 case Token::ADD:
1038 case Token::SUB:
1039 case Token::MUL:
1040 case Token::DIV:
1041 __ ret(0);
1042 break;
1043 case Token::MOD:
1044 case Token::BIT_OR:
1045 case Token::BIT_AND:
1046 case Token::BIT_XOR:
1047 case Token::SAR:
1048 case Token::SHL:
1049 case Token::SHR:
1050 __ ret(2 * kPointerSize);
1051 break;
1052 default:
1053 UNREACHABLE();
1054 }
1055
1056 // 6. For some operations emit inline code to perform floating point
1057 // operations on known smis (e.g., if the result of the operation
1058 // overflowed the smi range).
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001059 if (allow_heapnumber_results == BinaryOpStub::NO_HEAPNUMBER_RESULTS) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001060 __ bind(&use_fp_on_smis);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001061 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001062 // Undo the effects of some operations, and some register moves.
1063 case Token::SHL:
1064 // The arguments are saved on the stack, and only used from there.
1065 break;
1066 case Token::ADD:
1067 // Revert right = right + left.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001068 __ sub(right, left);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001069 break;
1070 case Token::SUB:
1071 // Revert left = left - right.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001072 __ add(left, right);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001073 break;
1074 case Token::MUL:
1075 // Right was clobbered but a copy is in ebx.
1076 __ mov(right, ebx);
1077 break;
1078 case Token::DIV:
1079 // Left was clobbered but a copy is in edi. Right is in ebx for
1080 // division. They should be in eax, ebx for jump to not_smi.
1081 __ mov(eax, edi);
1082 break;
1083 default:
1084 // No other operators jump to use_fp_on_smis.
1085 break;
1086 }
1087 __ jmp(&not_smis);
1088 } else {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001089 ASSERT(allow_heapnumber_results == BinaryOpStub::ALLOW_HEAPNUMBER_RESULTS);
1090 switch (op) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001091 case Token::SHL:
1092 case Token::SHR: {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001093 Comment perform_float(masm, "-- Perform float operation on smis");
1094 __ bind(&use_fp_on_smis);
1095 // Result we want is in left == edx, so we can put the allocated heap
1096 // number in eax.
1097 __ AllocateHeapNumber(eax, ecx, ebx, slow);
1098 // Store the result in the HeapNumber and return.
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001099 // It's OK to overwrite the arguments on the stack because we
1100 // are about to return.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001101 if (op == Token::SHR) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001102 __ mov(Operand(esp, 1 * kPointerSize), left);
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001103 __ mov(Operand(esp, 2 * kPointerSize), Immediate(0));
1104 __ fild_d(Operand(esp, 1 * kPointerSize));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001105 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001106 } else {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001107 ASSERT_EQ(Token::SHL, op);
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001108 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001109 CpuFeatureScope use_sse2(masm, SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001110 __ cvtsi2sd(xmm0, left);
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001111 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1112 } else {
1113 __ mov(Operand(esp, 1 * kPointerSize), left);
1114 __ fild_s(Operand(esp, 1 * kPointerSize));
1115 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1116 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001117 }
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001118 __ ret(2 * kPointerSize);
1119 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001120 }
1121
1122 case Token::ADD:
1123 case Token::SUB:
1124 case Token::MUL:
1125 case Token::DIV: {
1126 Comment perform_float(masm, "-- Perform float operation on smis");
1127 __ bind(&use_fp_on_smis);
1128 // Restore arguments to edx, eax.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001129 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001130 case Token::ADD:
1131 // Revert right = right + left.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001132 __ sub(right, left);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001133 break;
1134 case Token::SUB:
1135 // Revert left = left - right.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001136 __ add(left, right);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001137 break;
1138 case Token::MUL:
1139 // Right was clobbered but a copy is in ebx.
1140 __ mov(right, ebx);
1141 break;
1142 case Token::DIV:
1143 // Left was clobbered but a copy is in edi. Right is in ebx for
1144 // division.
1145 __ mov(edx, edi);
1146 __ mov(eax, right);
1147 break;
1148 default: UNREACHABLE();
1149 break;
1150 }
1151 __ AllocateHeapNumber(ecx, ebx, no_reg, slow);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001152 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001153 CpuFeatureScope use_sse2(masm, SSE2);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001154 FloatingPointHelper::LoadSSE2Smis(masm, ebx);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001155 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001156 case Token::ADD: __ addsd(xmm0, xmm1); break;
1157 case Token::SUB: __ subsd(xmm0, xmm1); break;
1158 case Token::MUL: __ mulsd(xmm0, xmm1); break;
1159 case Token::DIV: __ divsd(xmm0, xmm1); break;
1160 default: UNREACHABLE();
1161 }
1162 __ movdbl(FieldOperand(ecx, HeapNumber::kValueOffset), xmm0);
1163 } else { // SSE2 not available, use FPU.
1164 FloatingPointHelper::LoadFloatSmis(masm, ebx);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001165 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001166 case Token::ADD: __ faddp(1); break;
1167 case Token::SUB: __ fsubp(1); break;
1168 case Token::MUL: __ fmulp(1); break;
1169 case Token::DIV: __ fdivp(1); break;
1170 default: UNREACHABLE();
1171 }
1172 __ fstp_d(FieldOperand(ecx, HeapNumber::kValueOffset));
1173 }
1174 __ mov(eax, ecx);
1175 __ ret(0);
1176 break;
1177 }
1178
1179 default:
1180 break;
1181 }
1182 }
1183
1184 // 7. Non-smi operands, fall out to the non-smi code with the operands in
1185 // edx and eax.
1186 Comment done_comment(masm, "-- Enter non-smi code");
1187 __ bind(&not_smis);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001188 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001189 case Token::BIT_OR:
1190 case Token::SHL:
1191 case Token::SAR:
1192 case Token::SHR:
1193 // Right operand is saved in ecx and eax was destroyed by the smi
1194 // check.
1195 __ mov(eax, ecx);
1196 break;
1197
1198 case Token::DIV:
1199 case Token::MOD:
1200 // Operands are in eax, ebx at this point.
1201 __ mov(edx, eax);
1202 __ mov(eax, ebx);
1203 break;
1204
1205 default:
1206 break;
1207 }
1208}
1209
1210
danno@chromium.org40cb8782011-05-25 07:58:50 +00001211void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001212 Label right_arg_changed, call_runtime;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001213
1214 switch (op_) {
1215 case Token::ADD:
1216 case Token::SUB:
1217 case Token::MUL:
1218 case Token::DIV:
1219 break;
1220 case Token::MOD:
1221 case Token::BIT_OR:
1222 case Token::BIT_AND:
1223 case Token::BIT_XOR:
1224 case Token::SAR:
1225 case Token::SHL:
1226 case Token::SHR:
1227 GenerateRegisterArgsPush(masm);
1228 break;
1229 default:
1230 UNREACHABLE();
1231 }
1232
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00001233 if (op_ == Token::MOD && encoded_right_arg_.has_value) {
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001234 // It is guaranteed that the value will fit into a Smi, because if it
1235 // didn't, we wouldn't be here, see BinaryOp_Patch.
1236 __ cmp(eax, Immediate(Smi::FromInt(fixed_right_arg_value())));
1237 __ j(not_equal, &right_arg_changed);
1238 }
1239
danno@chromium.org40cb8782011-05-25 07:58:50 +00001240 if (result_type_ == BinaryOpIC::UNINITIALIZED ||
1241 result_type_ == BinaryOpIC::SMI) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001242 BinaryOpStub_GenerateSmiCode(
1243 masm, &call_runtime, NO_HEAPNUMBER_RESULTS, op_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001244 } else {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001245 BinaryOpStub_GenerateSmiCode(
1246 masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS, op_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001247 }
ulan@chromium.org906e2fb2013-05-14 08:14:38 +00001248
1249 // Code falls through if the result is not returned as either a smi or heap
1250 // number.
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001251 __ bind(&right_arg_changed);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001252 switch (op_) {
1253 case Token::ADD:
1254 case Token::SUB:
1255 case Token::MUL:
1256 case Token::DIV:
1257 GenerateTypeTransition(masm);
1258 break;
1259 case Token::MOD:
1260 case Token::BIT_OR:
1261 case Token::BIT_AND:
1262 case Token::BIT_XOR:
1263 case Token::SAR:
1264 case Token::SHL:
1265 case Token::SHR:
1266 GenerateTypeTransitionWithSavedArgs(masm);
1267 break;
1268 default:
1269 UNREACHABLE();
1270 }
ulan@chromium.org906e2fb2013-05-14 08:14:38 +00001271
1272 __ bind(&call_runtime);
1273 switch (op_) {
1274 case Token::ADD:
1275 case Token::SUB:
1276 case Token::MUL:
1277 case Token::DIV:
ulan@chromium.org906e2fb2013-05-14 08:14:38 +00001278 break;
1279 case Token::MOD:
1280 case Token::BIT_OR:
1281 case Token::BIT_AND:
1282 case Token::BIT_XOR:
1283 case Token::SAR:
1284 case Token::SHL:
1285 case Token::SHR:
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001286 BinaryOpStub_GenerateRegisterArgsPop(masm);
ulan@chromium.org906e2fb2013-05-14 08:14:38 +00001287 break;
1288 default:
1289 UNREACHABLE();
1290 }
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001291
1292 {
1293 FrameScope scope(masm, StackFrame::INTERNAL);
1294 __ push(edx);
1295 __ push(eax);
1296 GenerateCallRuntime(masm);
1297 }
1298 __ ret(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001299}
1300
1301
danno@chromium.org40cb8782011-05-25 07:58:50 +00001302void BinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00001303 Label call_runtime;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001304 ASSERT(left_type_ == BinaryOpIC::STRING && right_type_ == BinaryOpIC::STRING);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001305 ASSERT(op_ == Token::ADD);
1306 // If both arguments are strings, call the string add stub.
1307 // Otherwise, do a transition.
1308
1309 // Registers containing left and right operands respectively.
1310 Register left = edx;
1311 Register right = eax;
1312
1313 // Test if left operand is a string.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001314 __ JumpIfSmi(left, &call_runtime, Label::kNear);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001315 __ CmpObjectType(left, FIRST_NONSTRING_TYPE, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001316 __ j(above_equal, &call_runtime, Label::kNear);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001317
1318 // Test if right operand is a string.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001319 __ JumpIfSmi(right, &call_runtime, Label::kNear);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001320 __ CmpObjectType(right, FIRST_NONSTRING_TYPE, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001321 __ j(above_equal, &call_runtime, Label::kNear);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001322
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001323 StringAddStub string_add_stub((StringAddFlags)
1324 (ERECT_FRAME | NO_STRING_CHECK_IN_STUB));
danno@chromium.org160a7b02011-04-18 15:51:38 +00001325 GenerateRegisterArgsPush(masm);
1326 __ TailCallStub(&string_add_stub);
1327
1328 __ bind(&call_runtime);
1329 GenerateTypeTransition(masm);
1330}
1331
1332
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001333static void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm,
1334 Label* alloc_failure,
1335 OverwriteMode mode);
1336
1337
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001338// Input:
1339// edx: left operand (tagged)
1340// eax: right operand (tagged)
1341// Output:
1342// eax: result (tagged)
danno@chromium.org40cb8782011-05-25 07:58:50 +00001343void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001344 Label call_runtime;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001345 ASSERT(Max(left_type_, right_type_) == BinaryOpIC::INT32);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001346
1347 // Floating point case.
1348 switch (op_) {
1349 case Token::ADD:
1350 case Token::SUB:
1351 case Token::MUL:
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001352 case Token::DIV:
1353 case Token::MOD: {
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001354 Label not_floats, not_int32, right_arg_changed;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001355 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001356 CpuFeatureScope use_sse2(masm, SSE2);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001357 // It could be that only SMIs have been seen at either the left
1358 // or the right operand. For precise type feedback, patch the IC
1359 // again if this changes.
1360 // In theory, we would need the same check in the non-SSE2 case,
1361 // but since we don't support Crankshaft on such hardware we can
1362 // afford not to care about precise type feedback.
1363 if (left_type_ == BinaryOpIC::SMI) {
1364 __ JumpIfNotSmi(edx, &not_int32);
1365 }
1366 if (right_type_ == BinaryOpIC::SMI) {
1367 __ JumpIfNotSmi(eax, &not_int32);
1368 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001369 FloatingPointHelper::LoadSSE2Operands(masm, &not_floats);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001370 FloatingPointHelper::CheckSSE2OperandIsInt32(
1371 masm, &not_int32, xmm0, ebx, ecx, xmm2);
1372 FloatingPointHelper::CheckSSE2OperandIsInt32(
1373 masm, &not_int32, xmm1, edi, ecx, xmm2);
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001374 if (op_ == Token::MOD) {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00001375 if (encoded_right_arg_.has_value) {
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001376 __ cmp(edi, Immediate(fixed_right_arg_value()));
1377 __ j(not_equal, &right_arg_changed);
1378 }
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001379 GenerateRegisterArgsPush(masm);
1380 __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
1381 } else {
1382 switch (op_) {
1383 case Token::ADD: __ addsd(xmm0, xmm1); break;
1384 case Token::SUB: __ subsd(xmm0, xmm1); break;
1385 case Token::MUL: __ mulsd(xmm0, xmm1); break;
1386 case Token::DIV: __ divsd(xmm0, xmm1); break;
1387 default: UNREACHABLE();
1388 }
1389 // Check result type if it is currently Int32.
1390 if (result_type_ <= BinaryOpIC::INT32) {
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001391 FloatingPointHelper::CheckSSE2OperandIsInt32(
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001392 masm, &not_int32, xmm0, ecx, ecx, xmm2);
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001393 }
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001394 BinaryOpStub_GenerateHeapResultAllocation(masm, &call_runtime, mode_);
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001395 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1396 __ ret(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001397 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001398 } else { // SSE2 not available, use FPU.
1399 FloatingPointHelper::CheckFloatOperands(masm, &not_floats, ebx);
1400 FloatingPointHelper::LoadFloatOperands(
1401 masm,
1402 ecx,
1403 FloatingPointHelper::ARGS_IN_REGISTERS);
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001404 if (op_ == Token::MOD) {
1405 // The operands are now on the FPU stack, but we don't need them.
1406 __ fstp(0);
1407 __ fstp(0);
1408 GenerateRegisterArgsPush(masm);
1409 __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
1410 } else {
1411 switch (op_) {
1412 case Token::ADD: __ faddp(1); break;
1413 case Token::SUB: __ fsubp(1); break;
1414 case Token::MUL: __ fmulp(1); break;
1415 case Token::DIV: __ fdivp(1); break;
1416 default: UNREACHABLE();
1417 }
1418 Label after_alloc_failure;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001419 BinaryOpStub_GenerateHeapResultAllocation(
1420 masm, &after_alloc_failure, mode_);
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001421 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1422 __ ret(0);
1423 __ bind(&after_alloc_failure);
1424 __ fstp(0); // Pop FPU stack before calling runtime.
1425 __ jmp(&call_runtime);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001426 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001427 }
1428
1429 __ bind(&not_floats);
1430 __ bind(&not_int32);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001431 __ bind(&right_arg_changed);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001432 GenerateTypeTransition(masm);
1433 break;
1434 }
1435
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001436 case Token::BIT_OR:
1437 case Token::BIT_AND:
1438 case Token::BIT_XOR:
1439 case Token::SAR:
1440 case Token::SHL:
1441 case Token::SHR: {
1442 GenerateRegisterArgsPush(masm);
1443 Label not_floats;
1444 Label not_int32;
1445 Label non_smi_result;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001446 bool use_sse3 = platform_specific_bit_;
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001447 FloatingPointHelper::LoadUnknownsAsIntegers(
1448 masm, use_sse3, left_type_, right_type_, &not_floats);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001449 switch (op_) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001450 case Token::BIT_OR: __ or_(eax, ecx); break;
1451 case Token::BIT_AND: __ and_(eax, ecx); break;
1452 case Token::BIT_XOR: __ xor_(eax, ecx); break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001453 case Token::SAR: __ sar_cl(eax); break;
1454 case Token::SHL: __ shl_cl(eax); break;
1455 case Token::SHR: __ shr_cl(eax); break;
1456 default: UNREACHABLE();
1457 }
1458 if (op_ == Token::SHR) {
1459 // Check if result is non-negative and fits in a smi.
1460 __ test(eax, Immediate(0xc0000000));
1461 __ j(not_zero, &call_runtime);
1462 } else {
1463 // Check if result fits in a smi.
1464 __ cmp(eax, 0xc0000000);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001465 __ j(negative, &non_smi_result, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001466 }
1467 // Tag smi result and return.
1468 __ SmiTag(eax);
1469 __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack.
1470
1471 // All ops except SHR return a signed int32 that we load in
1472 // a HeapNumber.
1473 if (op_ != Token::SHR) {
1474 __ bind(&non_smi_result);
1475 // Allocate a heap number if needed.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001476 __ mov(ebx, eax); // ebx: result
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001477 Label skip_allocation;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001478 switch (mode_) {
1479 case OVERWRITE_LEFT:
1480 case OVERWRITE_RIGHT:
1481 // If the operand was an object, we skip the
1482 // allocation of a heap number.
1483 __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
1484 1 * kPointerSize : 2 * kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00001485 __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001486 // Fall through!
1487 case NO_OVERWRITE:
1488 __ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
1489 __ bind(&skip_allocation);
1490 break;
1491 default: UNREACHABLE();
1492 }
1493 // Store the result in the HeapNumber and return.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001494 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001495 CpuFeatureScope use_sse2(masm, SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001496 __ cvtsi2sd(xmm0, ebx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001497 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1498 } else {
1499 __ mov(Operand(esp, 1 * kPointerSize), ebx);
1500 __ fild_s(Operand(esp, 1 * kPointerSize));
1501 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1502 }
1503 __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack.
1504 }
1505
1506 __ bind(&not_floats);
1507 __ bind(&not_int32);
1508 GenerateTypeTransitionWithSavedArgs(masm);
1509 break;
1510 }
1511 default: UNREACHABLE(); break;
1512 }
1513
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001514 // If an allocation fails, or SHR hits a hard case, use the runtime system to
1515 // get the correct result.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001516 __ bind(&call_runtime);
1517
1518 switch (op_) {
1519 case Token::ADD:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001520 case Token::SUB:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001521 case Token::MUL:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001522 case Token::DIV:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001523 break;
1524 case Token::MOD:
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001525 return; // Handled above.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001526 case Token::BIT_OR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001527 case Token::BIT_AND:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001528 case Token::BIT_XOR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001529 case Token::SAR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001530 case Token::SHL:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001531 case Token::SHR:
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001532 BinaryOpStub_GenerateRegisterArgsPop(masm);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001533 break;
1534 default:
1535 UNREACHABLE();
1536 }
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001537
1538 {
1539 FrameScope scope(masm, StackFrame::INTERNAL);
1540 __ push(edx);
1541 __ push(eax);
1542 GenerateCallRuntime(masm);
1543 }
1544 __ ret(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001545}
1546
1547
danno@chromium.org40cb8782011-05-25 07:58:50 +00001548void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
lrn@chromium.org7516f052011-03-30 08:52:27 +00001549 if (op_ == Token::ADD) {
1550 // Handle string addition here, because it is the only operation
1551 // that does not do a ToNumber conversion on the operands.
1552 GenerateAddStrings(masm);
1553 }
1554
danno@chromium.org160a7b02011-04-18 15:51:38 +00001555 Factory* factory = masm->isolate()->factory();
1556
lrn@chromium.org7516f052011-03-30 08:52:27 +00001557 // Convert odd ball arguments to numbers.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001558 Label check, done;
danno@chromium.org160a7b02011-04-18 15:51:38 +00001559 __ cmp(edx, factory->undefined_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001560 __ j(not_equal, &check, Label::kNear);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001561 if (Token::IsBitOp(op_)) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001562 __ xor_(edx, edx);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001563 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00001564 __ mov(edx, Immediate(factory->nan_value()));
lrn@chromium.org7516f052011-03-30 08:52:27 +00001565 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001566 __ jmp(&done, Label::kNear);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001567 __ bind(&check);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001568 __ cmp(eax, factory->undefined_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001569 __ j(not_equal, &done, Label::kNear);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001570 if (Token::IsBitOp(op_)) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001571 __ xor_(eax, eax);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001572 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00001573 __ mov(eax, Immediate(factory->nan_value()));
lrn@chromium.org7516f052011-03-30 08:52:27 +00001574 }
1575 __ bind(&done);
1576
hpayer@chromium.org8432c912013-02-28 15:55:26 +00001577 GenerateNumberStub(masm);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001578}
1579
1580
hpayer@chromium.org8432c912013-02-28 15:55:26 +00001581void BinaryOpStub::GenerateNumberStub(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001582 Label call_runtime;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001583
1584 // Floating point case.
1585 switch (op_) {
1586 case Token::ADD:
1587 case Token::SUB:
1588 case Token::MUL:
1589 case Token::DIV: {
1590 Label not_floats;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001591 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001592 CpuFeatureScope use_sse2(masm, SSE2);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001593
1594 // It could be that only SMIs have been seen at either the left
1595 // or the right operand. For precise type feedback, patch the IC
1596 // again if this changes.
1597 // In theory, we would need the same check in the non-SSE2 case,
1598 // but since we don't support Crankshaft on such hardware we can
1599 // afford not to care about precise type feedback.
1600 if (left_type_ == BinaryOpIC::SMI) {
1601 __ JumpIfNotSmi(edx, &not_floats);
1602 }
1603 if (right_type_ == BinaryOpIC::SMI) {
1604 __ JumpIfNotSmi(eax, &not_floats);
1605 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001606 FloatingPointHelper::LoadSSE2Operands(masm, &not_floats);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001607 if (left_type_ == BinaryOpIC::INT32) {
1608 FloatingPointHelper::CheckSSE2OperandIsInt32(
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001609 masm, &not_floats, xmm0, ecx, ecx, xmm2);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001610 }
1611 if (right_type_ == BinaryOpIC::INT32) {
1612 FloatingPointHelper::CheckSSE2OperandIsInt32(
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001613 masm, &not_floats, xmm1, ecx, ecx, xmm2);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001614 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001615
1616 switch (op_) {
1617 case Token::ADD: __ addsd(xmm0, xmm1); break;
1618 case Token::SUB: __ subsd(xmm0, xmm1); break;
1619 case Token::MUL: __ mulsd(xmm0, xmm1); break;
1620 case Token::DIV: __ divsd(xmm0, xmm1); break;
1621 default: UNREACHABLE();
1622 }
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001623 BinaryOpStub_GenerateHeapResultAllocation(masm, &call_runtime, mode_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001624 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1625 __ ret(0);
1626 } else { // SSE2 not available, use FPU.
1627 FloatingPointHelper::CheckFloatOperands(masm, &not_floats, ebx);
1628 FloatingPointHelper::LoadFloatOperands(
1629 masm,
1630 ecx,
1631 FloatingPointHelper::ARGS_IN_REGISTERS);
1632 switch (op_) {
1633 case Token::ADD: __ faddp(1); break;
1634 case Token::SUB: __ fsubp(1); break;
1635 case Token::MUL: __ fmulp(1); break;
1636 case Token::DIV: __ fdivp(1); break;
1637 default: UNREACHABLE();
1638 }
1639 Label after_alloc_failure;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001640 BinaryOpStub_GenerateHeapResultAllocation(
1641 masm, &after_alloc_failure, mode_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001642 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1643 __ ret(0);
1644 __ bind(&after_alloc_failure);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001645 __ fstp(0); // Pop FPU stack before calling runtime.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001646 __ jmp(&call_runtime);
1647 }
1648
1649 __ bind(&not_floats);
1650 GenerateTypeTransition(masm);
1651 break;
1652 }
1653
1654 case Token::MOD: {
1655 // For MOD we go directly to runtime in the non-smi case.
1656 break;
1657 }
1658 case Token::BIT_OR:
1659 case Token::BIT_AND:
1660 case Token::BIT_XOR:
1661 case Token::SAR:
1662 case Token::SHL:
1663 case Token::SHR: {
1664 GenerateRegisterArgsPush(masm);
1665 Label not_floats;
1666 Label non_smi_result;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001667 // We do not check the input arguments here, as any value is
1668 // unconditionally truncated to an int32 anyway. To get the
1669 // right optimized code, int32 type feedback is just right.
1670 bool use_sse3 = platform_specific_bit_;
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001671 FloatingPointHelper::LoadUnknownsAsIntegers(
1672 masm, use_sse3, left_type_, right_type_, &not_floats);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001673 switch (op_) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001674 case Token::BIT_OR: __ or_(eax, ecx); break;
1675 case Token::BIT_AND: __ and_(eax, ecx); break;
1676 case Token::BIT_XOR: __ xor_(eax, ecx); break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001677 case Token::SAR: __ sar_cl(eax); break;
1678 case Token::SHL: __ shl_cl(eax); break;
1679 case Token::SHR: __ shr_cl(eax); break;
1680 default: UNREACHABLE();
1681 }
1682 if (op_ == Token::SHR) {
1683 // Check if result is non-negative and fits in a smi.
1684 __ test(eax, Immediate(0xc0000000));
1685 __ j(not_zero, &call_runtime);
1686 } else {
1687 // Check if result fits in a smi.
1688 __ cmp(eax, 0xc0000000);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001689 __ j(negative, &non_smi_result, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001690 }
1691 // Tag smi result and return.
1692 __ SmiTag(eax);
1693 __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack.
1694
1695 // All ops except SHR return a signed int32 that we load in
1696 // a HeapNumber.
1697 if (op_ != Token::SHR) {
1698 __ bind(&non_smi_result);
1699 // Allocate a heap number if needed.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001700 __ mov(ebx, eax); // ebx: result
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001701 Label skip_allocation;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001702 switch (mode_) {
1703 case OVERWRITE_LEFT:
1704 case OVERWRITE_RIGHT:
1705 // If the operand was an object, we skip the
1706 // allocation of a heap number.
1707 __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
1708 1 * kPointerSize : 2 * kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00001709 __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001710 // Fall through!
1711 case NO_OVERWRITE:
1712 __ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
1713 __ bind(&skip_allocation);
1714 break;
1715 default: UNREACHABLE();
1716 }
1717 // Store the result in the HeapNumber and return.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001718 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001719 CpuFeatureScope use_sse2(masm, SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001720 __ cvtsi2sd(xmm0, ebx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001721 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1722 } else {
1723 __ mov(Operand(esp, 1 * kPointerSize), ebx);
1724 __ fild_s(Operand(esp, 1 * kPointerSize));
1725 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1726 }
1727 __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack.
1728 }
1729
1730 __ bind(&not_floats);
1731 GenerateTypeTransitionWithSavedArgs(masm);
1732 break;
1733 }
1734 default: UNREACHABLE(); break;
1735 }
1736
1737 // If an allocation fails, or SHR or MOD hit a hard case,
1738 // use the runtime system to get the correct result.
1739 __ bind(&call_runtime);
1740
1741 switch (op_) {
1742 case Token::ADD:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001743 case Token::SUB:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001744 case Token::MUL:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001745 case Token::DIV:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001746 case Token::MOD:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001747 break;
1748 case Token::BIT_OR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001749 case Token::BIT_AND:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001750 case Token::BIT_XOR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001751 case Token::SAR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001752 case Token::SHL:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001753 case Token::SHR:
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001754 BinaryOpStub_GenerateRegisterArgsPop(masm);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001755 break;
1756 default:
1757 UNREACHABLE();
1758 }
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001759
1760 {
1761 FrameScope scope(masm, StackFrame::INTERNAL);
1762 __ push(edx);
1763 __ push(eax);
1764 GenerateCallRuntime(masm);
1765 }
1766 __ ret(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001767}
1768
1769
danno@chromium.org40cb8782011-05-25 07:58:50 +00001770void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001771 Label call_runtime;
1772
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00001773 Counters* counters = masm->isolate()->counters();
1774 __ IncrementCounter(counters->generic_binary_stub_calls(), 1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001775
1776 switch (op_) {
1777 case Token::ADD:
1778 case Token::SUB:
1779 case Token::MUL:
1780 case Token::DIV:
1781 break;
1782 case Token::MOD:
1783 case Token::BIT_OR:
1784 case Token::BIT_AND:
1785 case Token::BIT_XOR:
1786 case Token::SAR:
1787 case Token::SHL:
1788 case Token::SHR:
1789 GenerateRegisterArgsPush(masm);
1790 break;
1791 default:
1792 UNREACHABLE();
1793 }
1794
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001795 BinaryOpStub_GenerateSmiCode(
1796 masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS, op_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001797
1798 // Floating point case.
1799 switch (op_) {
1800 case Token::ADD:
1801 case Token::SUB:
1802 case Token::MUL:
1803 case Token::DIV: {
1804 Label not_floats;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001805 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001806 CpuFeatureScope use_sse2(masm, SSE2);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001807 FloatingPointHelper::LoadSSE2Operands(masm, &not_floats);
1808
1809 switch (op_) {
1810 case Token::ADD: __ addsd(xmm0, xmm1); break;
1811 case Token::SUB: __ subsd(xmm0, xmm1); break;
1812 case Token::MUL: __ mulsd(xmm0, xmm1); break;
1813 case Token::DIV: __ divsd(xmm0, xmm1); break;
1814 default: UNREACHABLE();
1815 }
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001816 BinaryOpStub_GenerateHeapResultAllocation(masm, &call_runtime, mode_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001817 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1818 __ ret(0);
1819 } else { // SSE2 not available, use FPU.
1820 FloatingPointHelper::CheckFloatOperands(masm, &not_floats, ebx);
1821 FloatingPointHelper::LoadFloatOperands(
1822 masm,
1823 ecx,
1824 FloatingPointHelper::ARGS_IN_REGISTERS);
1825 switch (op_) {
1826 case Token::ADD: __ faddp(1); break;
1827 case Token::SUB: __ fsubp(1); break;
1828 case Token::MUL: __ fmulp(1); break;
1829 case Token::DIV: __ fdivp(1); break;
1830 default: UNREACHABLE();
1831 }
1832 Label after_alloc_failure;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001833 BinaryOpStub_GenerateHeapResultAllocation(
1834 masm, &after_alloc_failure, mode_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001835 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1836 __ ret(0);
1837 __ bind(&after_alloc_failure);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001838 __ fstp(0); // Pop FPU stack before calling runtime.
1839 __ jmp(&call_runtime);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001840 }
1841 __ bind(&not_floats);
1842 break;
1843 }
1844 case Token::MOD: {
1845 // For MOD we go directly to runtime in the non-smi case.
1846 break;
1847 }
1848 case Token::BIT_OR:
1849 case Token::BIT_AND:
1850 case Token::BIT_XOR:
1851 case Token::SAR:
1852 case Token::SHL:
1853 case Token::SHR: {
1854 Label non_smi_result;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001855 bool use_sse3 = platform_specific_bit_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001856 FloatingPointHelper::LoadUnknownsAsIntegers(masm,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001857 use_sse3,
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001858 BinaryOpIC::GENERIC,
1859 BinaryOpIC::GENERIC,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001860 &call_runtime);
1861 switch (op_) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001862 case Token::BIT_OR: __ or_(eax, ecx); break;
1863 case Token::BIT_AND: __ and_(eax, ecx); break;
1864 case Token::BIT_XOR: __ xor_(eax, ecx); break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001865 case Token::SAR: __ sar_cl(eax); break;
1866 case Token::SHL: __ shl_cl(eax); break;
1867 case Token::SHR: __ shr_cl(eax); break;
1868 default: UNREACHABLE();
1869 }
1870 if (op_ == Token::SHR) {
1871 // Check if result is non-negative and fits in a smi.
1872 __ test(eax, Immediate(0xc0000000));
1873 __ j(not_zero, &call_runtime);
1874 } else {
1875 // Check if result fits in a smi.
1876 __ cmp(eax, 0xc0000000);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001877 __ j(negative, &non_smi_result, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001878 }
1879 // Tag smi result and return.
1880 __ SmiTag(eax);
1881 __ ret(2 * kPointerSize); // Drop the arguments from the stack.
1882
1883 // All ops except SHR return a signed int32 that we load in
1884 // a HeapNumber.
1885 if (op_ != Token::SHR) {
1886 __ bind(&non_smi_result);
1887 // Allocate a heap number if needed.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001888 __ mov(ebx, eax); // ebx: result
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001889 Label skip_allocation;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001890 switch (mode_) {
1891 case OVERWRITE_LEFT:
1892 case OVERWRITE_RIGHT:
1893 // If the operand was an object, we skip the
1894 // allocation of a heap number.
1895 __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
1896 1 * kPointerSize : 2 * kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00001897 __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001898 // Fall through!
1899 case NO_OVERWRITE:
1900 __ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
1901 __ bind(&skip_allocation);
1902 break;
1903 default: UNREACHABLE();
1904 }
1905 // Store the result in the HeapNumber and return.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001906 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001907 CpuFeatureScope use_sse2(masm, SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001908 __ cvtsi2sd(xmm0, ebx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001909 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1910 } else {
1911 __ mov(Operand(esp, 1 * kPointerSize), ebx);
1912 __ fild_s(Operand(esp, 1 * kPointerSize));
1913 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1914 }
1915 __ ret(2 * kPointerSize);
1916 }
1917 break;
1918 }
1919 default: UNREACHABLE(); break;
1920 }
1921
1922 // If all else fails, use the runtime system to get the correct
1923 // result.
1924 __ bind(&call_runtime);
1925 switch (op_) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001926 case Token::ADD:
ager@chromium.org0ee099b2011-01-25 14:06:47 +00001927 GenerateAddStrings(masm);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001928 // Fall through.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001929 case Token::SUB:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001930 case Token::MUL:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001931 case Token::DIV:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001932 break;
1933 case Token::MOD:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001934 case Token::BIT_OR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001935 case Token::BIT_AND:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001936 case Token::BIT_XOR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001937 case Token::SAR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001938 case Token::SHL:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001939 case Token::SHR:
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001940 BinaryOpStub_GenerateRegisterArgsPop(masm);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001941 break;
1942 default:
1943 UNREACHABLE();
1944 }
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001945
1946 {
1947 FrameScope scope(masm, StackFrame::INTERNAL);
1948 __ push(edx);
1949 __ push(eax);
1950 GenerateCallRuntime(masm);
1951 }
1952 __ ret(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001953}
1954
1955
danno@chromium.org40cb8782011-05-25 07:58:50 +00001956void BinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00001957 ASSERT(op_ == Token::ADD);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001958 Label left_not_string, call_runtime;
ager@chromium.org0ee099b2011-01-25 14:06:47 +00001959
1960 // Registers containing left and right operands respectively.
1961 Register left = edx;
1962 Register right = eax;
1963
1964 // Test if left operand is a string.
whesse@chromium.org7b260152011-06-20 15:33:18 +00001965 __ JumpIfSmi(left, &left_not_string, Label::kNear);
ager@chromium.org0ee099b2011-01-25 14:06:47 +00001966 __ CmpObjectType(left, FIRST_NONSTRING_TYPE, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001967 __ j(above_equal, &left_not_string, Label::kNear);
ager@chromium.org0ee099b2011-01-25 14:06:47 +00001968
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001969 StringAddStub string_add_left_stub((StringAddFlags)
1970 (ERECT_FRAME | NO_STRING_CHECK_LEFT_IN_STUB));
ager@chromium.org0ee099b2011-01-25 14:06:47 +00001971 GenerateRegisterArgsPush(masm);
1972 __ TailCallStub(&string_add_left_stub);
1973
1974 // Left operand is not a string, test right.
1975 __ bind(&left_not_string);
whesse@chromium.org7b260152011-06-20 15:33:18 +00001976 __ JumpIfSmi(right, &call_runtime, Label::kNear);
ager@chromium.org0ee099b2011-01-25 14:06:47 +00001977 __ CmpObjectType(right, FIRST_NONSTRING_TYPE, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001978 __ j(above_equal, &call_runtime, Label::kNear);
ager@chromium.org0ee099b2011-01-25 14:06:47 +00001979
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001980 StringAddStub string_add_right_stub((StringAddFlags)
1981 (ERECT_FRAME | NO_STRING_CHECK_RIGHT_IN_STUB));
ager@chromium.org0ee099b2011-01-25 14:06:47 +00001982 GenerateRegisterArgsPush(masm);
1983 __ TailCallStub(&string_add_right_stub);
1984
1985 // Neither argument is a string.
1986 __ bind(&call_runtime);
1987}
1988
1989
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001990static void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm,
1991 Label* alloc_failure,
1992 OverwriteMode mode) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001993 Label skip_allocation;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001994 switch (mode) {
1995 case OVERWRITE_LEFT: {
1996 // If the argument in edx is already an object, we skip the
1997 // allocation of a heap number.
whesse@chromium.org7b260152011-06-20 15:33:18 +00001998 __ JumpIfNotSmi(edx, &skip_allocation, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001999 // Allocate a heap number for the result. Keep eax and edx intact
2000 // for the possible runtime call.
2001 __ AllocateHeapNumber(ebx, ecx, no_reg, alloc_failure);
2002 // Now edx can be overwritten losing one of the arguments as we are
2003 // now done and will not need it any more.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002004 __ mov(edx, ebx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002005 __ bind(&skip_allocation);
2006 // Use object in edx as a result holder
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002007 __ mov(eax, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002008 break;
2009 }
2010 case OVERWRITE_RIGHT:
2011 // If the argument in eax is already an object, we skip the
2012 // allocation of a heap number.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002013 __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002014 // Fall through!
2015 case NO_OVERWRITE:
2016 // Allocate a heap number for the result. Keep eax and edx intact
2017 // for the possible runtime call.
2018 __ AllocateHeapNumber(ebx, ecx, no_reg, alloc_failure);
2019 // Now eax can be overwritten losing one of the arguments as we are
2020 // now done and will not need it any more.
2021 __ mov(eax, ebx);
2022 __ bind(&skip_allocation);
2023 break;
2024 default: UNREACHABLE();
2025 }
2026}
2027
2028
danno@chromium.org40cb8782011-05-25 07:58:50 +00002029void BinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002030 __ pop(ecx);
2031 __ push(edx);
2032 __ push(eax);
2033 __ push(ecx);
2034}
2035
2036
ricow@chromium.org65fae842010-08-25 15:26:24 +00002037void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
whesse@chromium.org023421e2010-12-21 12:19:12 +00002038 // TAGGED case:
2039 // Input:
2040 // esp[4]: tagged number input argument (should be number).
2041 // esp[0]: return address.
2042 // Output:
2043 // eax: tagged double result.
2044 // UNTAGGED case:
2045 // Input::
2046 // esp[0]: return address.
2047 // xmm1: untagged double input argument
2048 // Output:
2049 // xmm1: untagged double result.
2050
ricow@chromium.org65fae842010-08-25 15:26:24 +00002051 Label runtime_call;
2052 Label runtime_call_clear_stack;
whesse@chromium.org023421e2010-12-21 12:19:12 +00002053 Label skip_cache;
2054 const bool tagged = (argument_type_ == TAGGED);
2055 if (tagged) {
2056 // Test that eax is a number.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002057 Label input_not_smi;
2058 Label loaded;
whesse@chromium.org023421e2010-12-21 12:19:12 +00002059 __ mov(eax, Operand(esp, kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00002060 __ JumpIfNotSmi(eax, &input_not_smi, Label::kNear);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002061 // Input is a smi. Untag and load it onto the FPU stack.
2062 // Then load the low and high words of the double into ebx, edx.
2063 STATIC_ASSERT(kSmiTagSize == 1);
2064 __ sar(eax, 1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002065 __ sub(esp, Immediate(2 * kPointerSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002066 __ mov(Operand(esp, 0), eax);
2067 __ fild_s(Operand(esp, 0));
2068 __ fst_d(Operand(esp, 0));
2069 __ pop(edx);
2070 __ pop(ebx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002071 __ jmp(&loaded, Label::kNear);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002072 __ bind(&input_not_smi);
2073 // Check if input is a HeapNumber.
2074 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002075 Factory* factory = masm->isolate()->factory();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002076 __ cmp(ebx, Immediate(factory->heap_number_map()));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002077 __ j(not_equal, &runtime_call);
2078 // Input is a HeapNumber. Push it on the FPU stack and load its
2079 // low and high words into ebx, edx.
2080 __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
2081 __ mov(edx, FieldOperand(eax, HeapNumber::kExponentOffset));
2082 __ mov(ebx, FieldOperand(eax, HeapNumber::kMantissaOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002083
whesse@chromium.org023421e2010-12-21 12:19:12 +00002084 __ bind(&loaded);
2085 } else { // UNTAGGED.
ulan@chromium.org750145a2013-03-07 15:14:13 +00002086 CpuFeatureScope scope(masm, SSE2);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002087 if (CpuFeatures::IsSupported(SSE4_1)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00002088 CpuFeatureScope sse4_scope(masm, SSE4_1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002089 __ pextrd(edx, xmm1, 0x1); // copy xmm1[63..32] to edx.
whesse@chromium.org023421e2010-12-21 12:19:12 +00002090 } else {
2091 __ pshufd(xmm0, xmm1, 0x1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002092 __ movd(edx, xmm0);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002093 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002094 __ movd(ebx, xmm1);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002095 }
2096
2097 // ST[0] or xmm1 == double value
ricow@chromium.org65fae842010-08-25 15:26:24 +00002098 // ebx = low 32 bits of double value
2099 // edx = high 32 bits of double value
2100 // Compute hash (the shifts are arithmetic):
2101 // h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1);
2102 __ mov(ecx, ebx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002103 __ xor_(ecx, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002104 __ mov(eax, ecx);
2105 __ sar(eax, 16);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002106 __ xor_(ecx, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002107 __ mov(eax, ecx);
2108 __ sar(eax, 8);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002109 __ xor_(ecx, eax);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002110 ASSERT(IsPowerOf2(TranscendentalCache::SubCache::kCacheSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002111 __ and_(ecx,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002112 Immediate(TranscendentalCache::SubCache::kCacheSize - 1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002113
whesse@chromium.org023421e2010-12-21 12:19:12 +00002114 // ST[0] or xmm1 == double value.
ricow@chromium.org65fae842010-08-25 15:26:24 +00002115 // ebx = low 32 bits of double value.
2116 // edx = high 32 bits of double value.
2117 // ecx = TranscendentalCache::hash(double value).
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002118 ExternalReference cache_array =
2119 ExternalReference::transcendental_cache_array_address(masm->isolate());
2120 __ mov(eax, Immediate(cache_array));
2121 int cache_array_index =
2122 type_ * sizeof(masm->isolate()->transcendental_cache()->caches_[0]);
2123 __ mov(eax, Operand(eax, cache_array_index));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002124 // Eax points to the cache for the type type_.
2125 // If NULL, the cache hasn't been initialized yet, so go through runtime.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002126 __ test(eax, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002127 __ j(zero, &runtime_call_clear_stack);
2128#ifdef DEBUG
2129 // Check that the layout of cache elements match expectations.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002130 { TranscendentalCache::SubCache::Element test_elem[2];
ricow@chromium.org65fae842010-08-25 15:26:24 +00002131 char* elem_start = reinterpret_cast<char*>(&test_elem[0]);
2132 char* elem2_start = reinterpret_cast<char*>(&test_elem[1]);
2133 char* elem_in0 = reinterpret_cast<char*>(&(test_elem[0].in[0]));
2134 char* elem_in1 = reinterpret_cast<char*>(&(test_elem[0].in[1]));
2135 char* elem_out = reinterpret_cast<char*>(&(test_elem[0].output));
2136 CHECK_EQ(12, elem2_start - elem_start); // Two uint_32's and a pointer.
2137 CHECK_EQ(0, elem_in0 - elem_start);
2138 CHECK_EQ(kIntSize, elem_in1 - elem_start);
2139 CHECK_EQ(2 * kIntSize, elem_out - elem_start);
2140 }
2141#endif
2142 // Find the address of the ecx'th entry in the cache, i.e., &eax[ecx*12].
2143 __ lea(ecx, Operand(ecx, ecx, times_2, 0));
2144 __ lea(ecx, Operand(eax, ecx, times_4, 0));
2145 // Check if cache matches: Double value is stored in uint32_t[2] array.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002146 Label cache_miss;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002147 __ cmp(ebx, Operand(ecx, 0));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002148 __ j(not_equal, &cache_miss, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002149 __ cmp(edx, Operand(ecx, kIntSize));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002150 __ j(not_equal, &cache_miss, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002151 // Cache hit!
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00002152 Counters* counters = masm->isolate()->counters();
2153 __ IncrementCounter(counters->transcendental_cache_hit(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002154 __ mov(eax, Operand(ecx, 2 * kIntSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002155 if (tagged) {
2156 __ fstp(0);
2157 __ ret(kPointerSize);
2158 } else { // UNTAGGED.
ulan@chromium.org750145a2013-03-07 15:14:13 +00002159 CpuFeatureScope scope(masm, SSE2);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002160 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
2161 __ Ret();
2162 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002163
2164 __ bind(&cache_miss);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00002165 __ IncrementCounter(counters->transcendental_cache_miss(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002166 // Update cache with new value.
2167 // We are short on registers, so use no_reg as scratch.
2168 // This gives slightly larger code.
whesse@chromium.org023421e2010-12-21 12:19:12 +00002169 if (tagged) {
2170 __ AllocateHeapNumber(eax, edi, no_reg, &runtime_call_clear_stack);
2171 } else { // UNTAGGED.
ulan@chromium.org750145a2013-03-07 15:14:13 +00002172 CpuFeatureScope scope(masm, SSE2);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002173 __ AllocateHeapNumber(eax, edi, no_reg, &skip_cache);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002174 __ sub(esp, Immediate(kDoubleSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002175 __ movdbl(Operand(esp, 0), xmm1);
2176 __ fld_d(Operand(esp, 0));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002177 __ add(esp, Immediate(kDoubleSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002178 }
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002179 GenerateOperation(masm, type_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002180 __ mov(Operand(ecx, 0), ebx);
2181 __ mov(Operand(ecx, kIntSize), edx);
2182 __ mov(Operand(ecx, 2 * kIntSize), eax);
2183 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002184 if (tagged) {
2185 __ ret(kPointerSize);
2186 } else { // UNTAGGED.
ulan@chromium.org750145a2013-03-07 15:14:13 +00002187 CpuFeatureScope scope(masm, SSE2);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002188 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
2189 __ Ret();
ricow@chromium.org65fae842010-08-25 15:26:24 +00002190
whesse@chromium.org023421e2010-12-21 12:19:12 +00002191 // Skip cache and return answer directly, only in untagged case.
2192 __ bind(&skip_cache);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002193 __ sub(esp, Immediate(kDoubleSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002194 __ movdbl(Operand(esp, 0), xmm1);
2195 __ fld_d(Operand(esp, 0));
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002196 GenerateOperation(masm, type_);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002197 __ fstp_d(Operand(esp, 0));
2198 __ movdbl(xmm1, Operand(esp, 0));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002199 __ add(esp, Immediate(kDoubleSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002200 // We return the value in xmm1 without adding it to the cache, but
2201 // we cause a scavenging GC so that future allocations will succeed.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002202 {
2203 FrameScope scope(masm, StackFrame::INTERNAL);
2204 // Allocate an unused object bigger than a HeapNumber.
2205 __ push(Immediate(Smi::FromInt(2 * kDoubleSize)));
2206 __ CallRuntimeSaveDoubles(Runtime::kAllocateInNewSpace);
2207 }
whesse@chromium.org023421e2010-12-21 12:19:12 +00002208 __ Ret();
2209 }
2210
2211 // Call runtime, doing whatever allocation and cleanup is necessary.
2212 if (tagged) {
2213 __ bind(&runtime_call_clear_stack);
2214 __ fstp(0);
2215 __ bind(&runtime_call);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002216 ExternalReference runtime =
2217 ExternalReference(RuntimeFunction(), masm->isolate());
2218 __ TailCallExternalReference(runtime, 1, 1);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002219 } else { // UNTAGGED.
ulan@chromium.org750145a2013-03-07 15:14:13 +00002220 CpuFeatureScope scope(masm, SSE2);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002221 __ bind(&runtime_call_clear_stack);
2222 __ bind(&runtime_call);
2223 __ AllocateHeapNumber(eax, edi, no_reg, &skip_cache);
2224 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002225 {
2226 FrameScope scope(masm, StackFrame::INTERNAL);
2227 __ push(eax);
2228 __ CallRuntime(RuntimeFunction(), 1);
2229 }
whesse@chromium.org023421e2010-12-21 12:19:12 +00002230 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
2231 __ Ret();
2232 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002233}
2234
2235
2236Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
2237 switch (type_) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00002238 case TranscendentalCache::SIN: return Runtime::kMath_sin;
2239 case TranscendentalCache::COS: return Runtime::kMath_cos;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00002240 case TranscendentalCache::TAN: return Runtime::kMath_tan;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002241 case TranscendentalCache::LOG: return Runtime::kMath_log;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002242 default:
2243 UNIMPLEMENTED();
2244 return Runtime::kAbort;
2245 }
2246}
2247
2248
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002249void TranscendentalCacheStub::GenerateOperation(
2250 MacroAssembler* masm, TranscendentalCache::Type type) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00002251 // Only free register is edi.
whesse@chromium.org023421e2010-12-21 12:19:12 +00002252 // Input value is on FP stack, and also in ebx/edx.
2253 // Input value is possibly in xmm1.
2254 // Address of result (a newly allocated HeapNumber) may be in eax.
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002255 if (type == TranscendentalCache::SIN ||
2256 type == TranscendentalCache::COS ||
2257 type == TranscendentalCache::TAN) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002258 // Both fsin and fcos require arguments in the range +/-2^63 and
2259 // return NaN for infinities and NaN. They can share all code except
2260 // the actual fsin/fcos operation.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002261 Label in_range, done;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002262 // If argument is outside the range -2^63..2^63, fsin/cos doesn't
2263 // work. We must reduce it to the appropriate range.
2264 __ mov(edi, edx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002265 __ and_(edi, Immediate(0x7ff00000)); // Exponent only.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002266 int supported_exponent_limit =
2267 (63 + HeapNumber::kExponentBias) << HeapNumber::kExponentShift;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002268 __ cmp(edi, Immediate(supported_exponent_limit));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00002269 __ j(below, &in_range, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002270 // Check for infinity and NaN. Both return NaN for sin.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002271 __ cmp(edi, Immediate(0x7ff00000));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002272 Label non_nan_result;
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00002273 __ j(not_equal, &non_nan_result, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002274 // Input is +/-Infinity or NaN. Result is NaN.
2275 __ fstp(0);
2276 // NaN is represented by 0x7ff8000000000000.
2277 __ push(Immediate(0x7ff80000));
2278 __ push(Immediate(0));
2279 __ fld_d(Operand(esp, 0));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002280 __ add(esp, Immediate(2 * kPointerSize));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002281 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002282
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002283 __ bind(&non_nan_result);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002284
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002285 // Use fpmod to restrict argument to the range +/-2*PI.
2286 __ mov(edi, eax); // Save eax before using fnstsw_ax.
2287 __ fldpi();
2288 __ fadd(0);
2289 __ fld(1);
2290 // FPU Stack: input, 2*pi, input.
2291 {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002292 Label no_exceptions;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002293 __ fwait();
2294 __ fnstsw_ax();
2295 // Clear if Illegal Operand or Zero Division exceptions are set.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002296 __ test(eax, Immediate(5));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002297 __ j(zero, &no_exceptions, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002298 __ fnclex();
2299 __ bind(&no_exceptions);
2300 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002301
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002302 // Compute st(0) % st(1)
2303 {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002304 Label partial_remainder_loop;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002305 __ bind(&partial_remainder_loop);
2306 __ fprem1();
2307 __ fwait();
2308 __ fnstsw_ax();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002309 __ test(eax, Immediate(0x400 /* C2 */));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002310 // If C2 is set, computation only has partial result. Loop to
2311 // continue computation.
2312 __ j(not_zero, &partial_remainder_loop);
2313 }
2314 // FPU Stack: input, 2*pi, input % 2*pi
2315 __ fstp(2);
2316 __ fstp(0);
2317 __ mov(eax, edi); // Restore eax (allocated HeapNumber pointer).
2318
2319 // FPU Stack: input % 2*pi
2320 __ bind(&in_range);
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002321 switch (type) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002322 case TranscendentalCache::SIN:
2323 __ fsin();
2324 break;
2325 case TranscendentalCache::COS:
2326 __ fcos();
2327 break;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00002328 case TranscendentalCache::TAN:
2329 // FPTAN calculates tangent onto st(0) and pushes 1.0 onto the
2330 // FP register stack.
2331 __ fptan();
2332 __ fstp(0); // Pop FP register stack.
2333 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002334 default:
2335 UNREACHABLE();
2336 }
2337 __ bind(&done);
2338 } else {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002339 ASSERT(type == TranscendentalCache::LOG);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002340 __ fldln2();
2341 __ fxch();
2342 __ fyl2x();
ricow@chromium.org65fae842010-08-25 15:26:24 +00002343 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002344}
2345
2346
ricow@chromium.org65fae842010-08-25 15:26:24 +00002347// Input: edx, eax are the left and right objects of a bit op.
2348// Output: eax, ecx are left and right integers for a bit op.
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002349// Warning: can clobber inputs even when it jumps to |conversion_failure|!
2350void FloatingPointHelper::LoadUnknownsAsIntegers(
2351 MacroAssembler* masm,
2352 bool use_sse3,
2353 BinaryOpIC::TypeInfo left_type,
2354 BinaryOpIC::TypeInfo right_type,
2355 Label* conversion_failure) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00002356 // Check float operands.
2357 Label arg1_is_object, check_undefined_arg1;
2358 Label arg2_is_object, check_undefined_arg2;
2359 Label load_arg2, done;
2360
2361 // Test if arg1 is a Smi.
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002362 if (left_type == BinaryOpIC::SMI) {
2363 __ JumpIfNotSmi(edx, conversion_failure);
2364 } else {
2365 __ JumpIfNotSmi(edx, &arg1_is_object, Label::kNear);
2366 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002367
2368 __ SmiUntag(edx);
2369 __ jmp(&load_arg2);
2370
2371 // If the argument is undefined it converts to zero (ECMA-262, section 9.5).
2372 __ bind(&check_undefined_arg1);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002373 Factory* factory = masm->isolate()->factory();
2374 __ cmp(edx, factory->undefined_value());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002375 __ j(not_equal, conversion_failure);
2376 __ mov(edx, Immediate(0));
2377 __ jmp(&load_arg2);
2378
2379 __ bind(&arg1_is_object);
2380 __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002381 __ cmp(ebx, factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002382 __ j(not_equal, &check_undefined_arg1);
2383
2384 // Get the untagged integer version of the edx heap number in ecx.
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002385 if (left_type == BinaryOpIC::INT32 && CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00002386 CpuFeatureScope use_sse2(masm, SSE2);
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002387 ConvertHeapNumberToInt32(masm, edx, conversion_failure);
2388 } else {
2389 IntegerConvert(masm, edx, use_sse3, conversion_failure);
2390 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002391 __ mov(edx, ecx);
2392
2393 // Here edx has the untagged integer, eax has a Smi or a heap number.
2394 __ bind(&load_arg2);
2395
2396 // Test if arg2 is a Smi.
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002397 if (right_type == BinaryOpIC::SMI) {
2398 __ JumpIfNotSmi(eax, conversion_failure);
2399 } else {
2400 __ JumpIfNotSmi(eax, &arg2_is_object, Label::kNear);
2401 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002402
2403 __ SmiUntag(eax);
2404 __ mov(ecx, eax);
2405 __ jmp(&done);
2406
2407 // If the argument is undefined it converts to zero (ECMA-262, section 9.5).
2408 __ bind(&check_undefined_arg2);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002409 __ cmp(eax, factory->undefined_value());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002410 __ j(not_equal, conversion_failure);
2411 __ mov(ecx, Immediate(0));
2412 __ jmp(&done);
2413
2414 __ bind(&arg2_is_object);
2415 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002416 __ cmp(ebx, factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002417 __ j(not_equal, &check_undefined_arg2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002418 // Get the untagged integer version of the eax heap number in ecx.
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002419
2420 if (right_type == BinaryOpIC::INT32 && CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00002421 CpuFeatureScope use_sse2(masm, SSE2);
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002422 ConvertHeapNumberToInt32(masm, eax, conversion_failure);
2423 } else {
2424 IntegerConvert(masm, eax, use_sse3, conversion_failure);
2425 }
2426
ricow@chromium.org65fae842010-08-25 15:26:24 +00002427 __ bind(&done);
2428 __ mov(eax, edx);
2429}
2430
2431
ricow@chromium.org65fae842010-08-25 15:26:24 +00002432void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm,
2433 Register number) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002434 Label load_smi, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002435
whesse@chromium.org7b260152011-06-20 15:33:18 +00002436 __ JumpIfSmi(number, &load_smi, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002437 __ fld_d(FieldOperand(number, HeapNumber::kValueOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002438 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002439
2440 __ bind(&load_smi);
2441 __ SmiUntag(number);
2442 __ push(number);
2443 __ fild_s(Operand(esp, 0));
2444 __ pop(number);
2445
2446 __ bind(&done);
2447}
2448
2449
2450void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002451 Label load_smi_edx, load_eax, load_smi_eax, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002452 // Load operand in edx into xmm0.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002453 __ JumpIfSmi(edx, &load_smi_edx, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002454 __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
2455
2456 __ bind(&load_eax);
2457 // Load operand in eax into xmm1.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002458 __ JumpIfSmi(eax, &load_smi_eax, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002459 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002460 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002461
2462 __ bind(&load_smi_edx);
2463 __ SmiUntag(edx); // Untag smi before converting to float.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002464 __ cvtsi2sd(xmm0, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002465 __ SmiTag(edx); // Retag smi for heap number overwriting test.
2466 __ jmp(&load_eax);
2467
2468 __ bind(&load_smi_eax);
2469 __ SmiUntag(eax); // Untag smi before converting to float.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002470 __ cvtsi2sd(xmm1, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002471 __ SmiTag(eax); // Retag smi for heap number overwriting test.
2472
2473 __ bind(&done);
2474}
2475
2476
2477void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm,
2478 Label* not_numbers) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002479 Label load_smi_edx, load_eax, load_smi_eax, load_float_eax, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002480 // Load operand in edx into xmm0, or branch to not_numbers.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002481 __ JumpIfSmi(edx, &load_smi_edx, Label::kNear);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002482 Factory* factory = masm->isolate()->factory();
2483 __ cmp(FieldOperand(edx, HeapObject::kMapOffset), factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002484 __ j(not_equal, not_numbers); // Argument in edx is not a number.
2485 __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
2486 __ bind(&load_eax);
2487 // Load operand in eax into xmm1, or branch to not_numbers.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002488 __ JumpIfSmi(eax, &load_smi_eax, Label::kNear);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002489 __ cmp(FieldOperand(eax, HeapObject::kMapOffset), factory->heap_number_map());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002490 __ j(equal, &load_float_eax, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002491 __ jmp(not_numbers); // Argument in eax is not a number.
2492 __ bind(&load_smi_edx);
2493 __ SmiUntag(edx); // Untag smi before converting to float.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002494 __ cvtsi2sd(xmm0, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002495 __ SmiTag(edx); // Retag smi for heap number overwriting test.
2496 __ jmp(&load_eax);
2497 __ bind(&load_smi_eax);
2498 __ SmiUntag(eax); // Untag smi before converting to float.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002499 __ cvtsi2sd(xmm1, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002500 __ SmiTag(eax); // Retag smi for heap number overwriting test.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002501 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002502 __ bind(&load_float_eax);
2503 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
2504 __ bind(&done);
2505}
2506
2507
2508void FloatingPointHelper::LoadSSE2Smis(MacroAssembler* masm,
2509 Register scratch) {
2510 const Register left = edx;
2511 const Register right = eax;
2512 __ mov(scratch, left);
2513 ASSERT(!scratch.is(right)); // We're about to clobber scratch.
2514 __ SmiUntag(scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002515 __ cvtsi2sd(xmm0, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002516
2517 __ mov(scratch, right);
2518 __ SmiUntag(scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002519 __ cvtsi2sd(xmm1, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002520}
2521
2522
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002523void FloatingPointHelper::CheckSSE2OperandIsInt32(MacroAssembler* masm,
2524 Label* non_int32,
2525 XMMRegister operand,
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002526 Register int32_result,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002527 Register scratch,
2528 XMMRegister xmm_scratch) {
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002529 __ cvttsd2si(int32_result, Operand(operand));
2530 __ cvtsi2sd(xmm_scratch, int32_result);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002531 __ pcmpeqd(xmm_scratch, operand);
ulan@chromium.org4121f232012-12-27 15:57:11 +00002532 __ movmskps(scratch, xmm_scratch);
2533 // Two least significant bits should be both set.
2534 __ not_(scratch);
2535 __ test(scratch, Immediate(3));
2536 __ j(not_zero, non_int32);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002537}
2538
2539
ricow@chromium.org65fae842010-08-25 15:26:24 +00002540void FloatingPointHelper::LoadFloatOperands(MacroAssembler* masm,
2541 Register scratch,
2542 ArgLocation arg_location) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002543 Label load_smi_1, load_smi_2, done_load_1, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002544 if (arg_location == ARGS_IN_REGISTERS) {
2545 __ mov(scratch, edx);
2546 } else {
2547 __ mov(scratch, Operand(esp, 2 * kPointerSize));
2548 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00002549 __ JumpIfSmi(scratch, &load_smi_1, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002550 __ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset));
2551 __ bind(&done_load_1);
2552
2553 if (arg_location == ARGS_IN_REGISTERS) {
2554 __ mov(scratch, eax);
2555 } else {
2556 __ mov(scratch, Operand(esp, 1 * kPointerSize));
2557 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00002558 __ JumpIfSmi(scratch, &load_smi_2, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002559 __ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002560 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002561
2562 __ bind(&load_smi_1);
2563 __ SmiUntag(scratch);
2564 __ push(scratch);
2565 __ fild_s(Operand(esp, 0));
2566 __ pop(scratch);
2567 __ jmp(&done_load_1);
2568
2569 __ bind(&load_smi_2);
2570 __ SmiUntag(scratch);
2571 __ push(scratch);
2572 __ fild_s(Operand(esp, 0));
2573 __ pop(scratch);
2574
2575 __ bind(&done);
2576}
2577
2578
2579void FloatingPointHelper::LoadFloatSmis(MacroAssembler* masm,
2580 Register scratch) {
2581 const Register left = edx;
2582 const Register right = eax;
2583 __ mov(scratch, left);
2584 ASSERT(!scratch.is(right)); // We're about to clobber scratch.
2585 __ SmiUntag(scratch);
2586 __ push(scratch);
2587 __ fild_s(Operand(esp, 0));
2588
2589 __ mov(scratch, right);
2590 __ SmiUntag(scratch);
2591 __ mov(Operand(esp, 0), scratch);
2592 __ fild_s(Operand(esp, 0));
2593 __ pop(scratch);
2594}
2595
2596
2597void FloatingPointHelper::CheckFloatOperands(MacroAssembler* masm,
2598 Label* non_float,
2599 Register scratch) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002600 Label test_other, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002601 // Test if both operands are floats or smi -> scratch=k_is_float;
2602 // Otherwise scratch = k_not_float.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002603 __ JumpIfSmi(edx, &test_other, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002604 __ mov(scratch, FieldOperand(edx, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002605 Factory* factory = masm->isolate()->factory();
2606 __ cmp(scratch, factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002607 __ j(not_equal, non_float); // argument in edx is not a number -> NaN
2608
2609 __ bind(&test_other);
whesse@chromium.org7b260152011-06-20 15:33:18 +00002610 __ JumpIfSmi(eax, &done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002611 __ mov(scratch, FieldOperand(eax, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002612 __ cmp(scratch, factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002613 __ j(not_equal, non_float); // argument in eax is not a number -> NaN
2614
2615 // Fall-through: Both operands are numbers.
2616 __ bind(&done);
2617}
2618
2619
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002620void MathPowStub::Generate(MacroAssembler* masm) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00002621 CpuFeatureScope use_sse2(masm, SSE2);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002622 Factory* factory = masm->isolate()->factory();
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002623 const Register exponent = eax;
2624 const Register base = edx;
2625 const Register scratch = ecx;
2626 const XMMRegister double_result = xmm3;
2627 const XMMRegister double_base = xmm2;
2628 const XMMRegister double_exponent = xmm1;
2629 const XMMRegister double_scratch = xmm4;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002630
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002631 Label call_runtime, done, exponent_not_smi, int_exponent;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002632
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002633 // Save 1 in double_result - we need this several times later on.
2634 __ mov(scratch, Immediate(1));
2635 __ cvtsi2sd(double_result, scratch);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002636
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002637 if (exponent_type_ == ON_STACK) {
2638 Label base_is_smi, unpack_exponent;
2639 // The exponent and base are supplied as arguments on the stack.
2640 // This can only happen if the stub is called from non-optimized code.
2641 // Load input parameters from stack.
2642 __ mov(base, Operand(esp, 2 * kPointerSize));
2643 __ mov(exponent, Operand(esp, 1 * kPointerSize));
2644
2645 __ JumpIfSmi(base, &base_is_smi, Label::kNear);
2646 __ cmp(FieldOperand(base, HeapObject::kMapOffset),
2647 factory->heap_number_map());
2648 __ j(not_equal, &call_runtime);
2649
2650 __ movdbl(double_base, FieldOperand(base, HeapNumber::kValueOffset));
2651 __ jmp(&unpack_exponent, Label::kNear);
2652
2653 __ bind(&base_is_smi);
2654 __ SmiUntag(base);
2655 __ cvtsi2sd(double_base, base);
2656
2657 __ bind(&unpack_exponent);
2658 __ JumpIfNotSmi(exponent, &exponent_not_smi, Label::kNear);
2659 __ SmiUntag(exponent);
2660 __ jmp(&int_exponent);
2661
2662 __ bind(&exponent_not_smi);
2663 __ cmp(FieldOperand(exponent, HeapObject::kMapOffset),
2664 factory->heap_number_map());
2665 __ j(not_equal, &call_runtime);
2666 __ movdbl(double_exponent,
2667 FieldOperand(exponent, HeapNumber::kValueOffset));
2668 } else if (exponent_type_ == TAGGED) {
2669 __ JumpIfNotSmi(exponent, &exponent_not_smi, Label::kNear);
2670 __ SmiUntag(exponent);
2671 __ jmp(&int_exponent);
2672
2673 __ bind(&exponent_not_smi);
2674 __ movdbl(double_exponent,
2675 FieldOperand(exponent, HeapNumber::kValueOffset));
2676 }
2677
2678 if (exponent_type_ != INTEGER) {
2679 Label fast_power;
2680 // Detect integer exponents stored as double.
2681 __ cvttsd2si(exponent, Operand(double_exponent));
2682 // Skip to runtime if possibly NaN (indicated by the indefinite integer).
2683 __ cmp(exponent, Immediate(0x80000000u));
2684 __ j(equal, &call_runtime);
2685 __ cvtsi2sd(double_scratch, exponent);
2686 // Already ruled out NaNs for exponent.
2687 __ ucomisd(double_exponent, double_scratch);
2688 __ j(equal, &int_exponent);
2689
2690 if (exponent_type_ == ON_STACK) {
2691 // Detect square root case. Crankshaft detects constant +/-0.5 at
2692 // compile time and uses DoMathPowHalf instead. We then skip this check
2693 // for non-constant cases of +/-0.5 as these hardly occur.
2694 Label continue_sqrt, continue_rsqrt, not_plus_half;
2695 // Test for 0.5.
2696 // Load double_scratch with 0.5.
2697 __ mov(scratch, Immediate(0x3F000000u));
2698 __ movd(double_scratch, scratch);
2699 __ cvtss2sd(double_scratch, double_scratch);
2700 // Already ruled out NaNs for exponent.
2701 __ ucomisd(double_scratch, double_exponent);
2702 __ j(not_equal, &not_plus_half, Label::kNear);
2703
2704 // Calculates square root of base. Check for the special case of
2705 // Math.pow(-Infinity, 0.5) == Infinity (ECMA spec, 15.8.2.13).
2706 // According to IEEE-754, single-precision -Infinity has the highest
2707 // 9 bits set and the lowest 23 bits cleared.
2708 __ mov(scratch, 0xFF800000u);
2709 __ movd(double_scratch, scratch);
2710 __ cvtss2sd(double_scratch, double_scratch);
2711 __ ucomisd(double_base, double_scratch);
2712 // Comparing -Infinity with NaN results in "unordered", which sets the
2713 // zero flag as if both were equal. However, it also sets the carry flag.
2714 __ j(not_equal, &continue_sqrt, Label::kNear);
2715 __ j(carry, &continue_sqrt, Label::kNear);
2716
2717 // Set result to Infinity in the special case.
2718 __ xorps(double_result, double_result);
2719 __ subsd(double_result, double_scratch);
2720 __ jmp(&done);
2721
2722 __ bind(&continue_sqrt);
2723 // sqrtsd returns -0 when input is -0. ECMA spec requires +0.
2724 __ xorps(double_scratch, double_scratch);
2725 __ addsd(double_scratch, double_base); // Convert -0 to +0.
2726 __ sqrtsd(double_result, double_scratch);
2727 __ jmp(&done);
2728
2729 // Test for -0.5.
2730 __ bind(&not_plus_half);
2731 // Load double_exponent with -0.5 by substracting 1.
2732 __ subsd(double_scratch, double_result);
2733 // Already ruled out NaNs for exponent.
2734 __ ucomisd(double_scratch, double_exponent);
2735 __ j(not_equal, &fast_power, Label::kNear);
2736
2737 // Calculates reciprocal of square root of base. Check for the special
2738 // case of Math.pow(-Infinity, -0.5) == 0 (ECMA spec, 15.8.2.13).
2739 // According to IEEE-754, single-precision -Infinity has the highest
2740 // 9 bits set and the lowest 23 bits cleared.
2741 __ mov(scratch, 0xFF800000u);
2742 __ movd(double_scratch, scratch);
2743 __ cvtss2sd(double_scratch, double_scratch);
2744 __ ucomisd(double_base, double_scratch);
2745 // Comparing -Infinity with NaN results in "unordered", which sets the
2746 // zero flag as if both were equal. However, it also sets the carry flag.
2747 __ j(not_equal, &continue_rsqrt, Label::kNear);
2748 __ j(carry, &continue_rsqrt, Label::kNear);
2749
2750 // Set result to 0 in the special case.
2751 __ xorps(double_result, double_result);
2752 __ jmp(&done);
2753
2754 __ bind(&continue_rsqrt);
2755 // sqrtsd returns -0 when input is -0. ECMA spec requires +0.
2756 __ xorps(double_exponent, double_exponent);
2757 __ addsd(double_exponent, double_base); // Convert -0 to +0.
2758 __ sqrtsd(double_exponent, double_exponent);
2759 __ divsd(double_result, double_exponent);
2760 __ jmp(&done);
2761 }
2762
2763 // Using FPU instructions to calculate power.
2764 Label fast_power_failed;
2765 __ bind(&fast_power);
2766 __ fnclex(); // Clear flags to catch exceptions later.
2767 // Transfer (B)ase and (E)xponent onto the FPU register stack.
2768 __ sub(esp, Immediate(kDoubleSize));
2769 __ movdbl(Operand(esp, 0), double_exponent);
2770 __ fld_d(Operand(esp, 0)); // E
2771 __ movdbl(Operand(esp, 0), double_base);
2772 __ fld_d(Operand(esp, 0)); // B, E
2773
2774 // Exponent is in st(1) and base is in st(0)
2775 // B ^ E = (2^(E * log2(B)) - 1) + 1 = (2^X - 1) + 1 for X = E * log2(B)
2776 // FYL2X calculates st(1) * log2(st(0))
2777 __ fyl2x(); // X
2778 __ fld(0); // X, X
2779 __ frndint(); // rnd(X), X
2780 __ fsub(1); // rnd(X), X-rnd(X)
2781 __ fxch(1); // X - rnd(X), rnd(X)
2782 // F2XM1 calculates 2^st(0) - 1 for -1 < st(0) < 1
2783 __ f2xm1(); // 2^(X-rnd(X)) - 1, rnd(X)
2784 __ fld1(); // 1, 2^(X-rnd(X)) - 1, rnd(X)
danno@chromium.org1f34ad32012-11-26 14:53:56 +00002785 __ faddp(1); // 2^(X-rnd(X)), rnd(X)
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002786 // FSCALE calculates st(0) * 2^st(1)
2787 __ fscale(); // 2^X, rnd(X)
danno@chromium.org1f34ad32012-11-26 14:53:56 +00002788 __ fstp(1); // 2^X
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002789 // Bail out to runtime in case of exceptions in the status word.
2790 __ fnstsw_ax();
2791 __ test_b(eax, 0x5F); // We check for all but precision exception.
2792 __ j(not_zero, &fast_power_failed, Label::kNear);
2793 __ fstp_d(Operand(esp, 0));
2794 __ movdbl(double_result, Operand(esp, 0));
2795 __ add(esp, Immediate(kDoubleSize));
2796 __ jmp(&done);
2797
2798 __ bind(&fast_power_failed);
2799 __ fninit();
2800 __ add(esp, Immediate(kDoubleSize));
2801 __ jmp(&call_runtime);
2802 }
2803
2804 // Calculate power with integer exponent.
2805 __ bind(&int_exponent);
2806 const XMMRegister double_scratch2 = double_exponent;
2807 __ mov(scratch, exponent); // Back up exponent.
2808 __ movsd(double_scratch, double_base); // Back up base.
2809 __ movsd(double_scratch2, double_result); // Load double_exponent with 1.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002810
2811 // Get absolute value of exponent.
verwaest@chromium.org33e09c82012-10-10 17:07:22 +00002812 Label no_neg, while_true, while_false;
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002813 __ test(scratch, scratch);
2814 __ j(positive, &no_neg, Label::kNear);
2815 __ neg(scratch);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002816 __ bind(&no_neg);
2817
ulan@chromium.org56c14af2012-09-20 12:51:09 +00002818 __ j(zero, &while_false, Label::kNear);
2819 __ shr(scratch, 1);
2820 // Above condition means CF==0 && ZF==0. This means that the
2821 // bit that has been shifted out is 0 and the result is not 0.
2822 __ j(above, &while_true, Label::kNear);
2823 __ movsd(double_result, double_scratch);
2824 __ j(zero, &while_false, Label::kNear);
2825
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002826 __ bind(&while_true);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002827 __ shr(scratch, 1);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002828 __ mulsd(double_scratch, double_scratch);
ulan@chromium.org56c14af2012-09-20 12:51:09 +00002829 __ j(above, &while_true, Label::kNear);
2830 __ mulsd(double_result, double_scratch);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002831 __ j(not_zero, &while_true);
2832
ulan@chromium.org56c14af2012-09-20 12:51:09 +00002833 __ bind(&while_false);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002834 // scratch has the original value of the exponent - if the exponent is
2835 // negative, return 1/result.
2836 __ test(exponent, exponent);
2837 __ j(positive, &done);
2838 __ divsd(double_scratch2, double_result);
2839 __ movsd(double_result, double_scratch2);
2840 // Test whether result is zero. Bail out to check for subnormal result.
2841 // Due to subnormals, x^-y == (1/x)^y does not hold in all cases.
2842 __ xorps(double_scratch2, double_scratch2);
2843 __ ucomisd(double_scratch2, double_result); // Result cannot be NaN.
2844 // double_exponent aliased as double_scratch2 has already been overwritten
2845 // and may not have contained the exponent value in the first place when the
2846 // exponent is a smi. We reset it with exponent value before bailing out.
2847 __ j(not_equal, &done);
2848 __ cvtsi2sd(double_exponent, exponent);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002849
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002850 // Returning or bailing out.
2851 Counters* counters = masm->isolate()->counters();
2852 if (exponent_type_ == ON_STACK) {
2853 // The arguments are still on the stack.
2854 __ bind(&call_runtime);
2855 __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002856
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002857 // The stub is called from non-optimized code, which expects the result
2858 // as heap number in exponent.
2859 __ bind(&done);
2860 __ AllocateHeapNumber(eax, scratch, base, &call_runtime);
2861 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), double_result);
2862 __ IncrementCounter(counters->math_pow(), 1);
2863 __ ret(2 * kPointerSize);
2864 } else {
2865 __ bind(&call_runtime);
2866 {
2867 AllowExternalCallThatCantCauseGC scope(masm);
2868 __ PrepareCallCFunction(4, scratch);
2869 __ movdbl(Operand(esp, 0 * kDoubleSize), double_base);
2870 __ movdbl(Operand(esp, 1 * kDoubleSize), double_exponent);
2871 __ CallCFunction(
2872 ExternalReference::power_double_double_function(masm->isolate()), 4);
2873 }
2874 // Return value is in st(0) on ia32.
2875 // Store it into the (fixed) result register.
2876 __ sub(esp, Immediate(kDoubleSize));
2877 __ fstp_d(Operand(esp, 0));
2878 __ movdbl(double_result, Operand(esp, 0));
2879 __ add(esp, Immediate(kDoubleSize));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002880
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002881 __ bind(&done);
2882 __ IncrementCounter(counters->math_pow(), 1);
2883 __ ret(0);
2884 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002885}
2886
2887
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00002888void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
2889 // ----------- S t a t e -------------
2890 // -- ecx : name
2891 // -- edx : receiver
2892 // -- esp[0] : return address
2893 // -----------------------------------
2894 Label miss;
2895
2896 if (kind() == Code::KEYED_LOAD_IC) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00002897 __ cmp(ecx, Immediate(masm->isolate()->factory()->prototype_string()));
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00002898 __ j(not_equal, &miss);
2899 }
2900
2901 StubCompiler::GenerateLoadFunctionPrototype(masm, edx, eax, ebx, &miss);
2902 __ bind(&miss);
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +00002903 StubCompiler::TailCallBuiltin(masm, StubCompiler::MissBuiltin(kind()));
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00002904}
2905
2906
2907void StringLengthStub::Generate(MacroAssembler* masm) {
2908 // ----------- S t a t e -------------
2909 // -- ecx : name
2910 // -- edx : receiver
2911 // -- esp[0] : return address
2912 // -----------------------------------
2913 Label miss;
2914
2915 if (kind() == Code::KEYED_LOAD_IC) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00002916 __ cmp(ecx, Immediate(masm->isolate()->factory()->length_string()));
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00002917 __ j(not_equal, &miss);
2918 }
2919
2920 StubCompiler::GenerateLoadStringLength(masm, edx, eax, ebx, &miss,
2921 support_wrapper_);
2922 __ bind(&miss);
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +00002923 StubCompiler::TailCallBuiltin(masm, StubCompiler::MissBuiltin(kind()));
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00002924}
2925
2926
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00002927void StoreArrayLengthStub::Generate(MacroAssembler* masm) {
2928 // ----------- S t a t e -------------
2929 // -- eax : value
2930 // -- ecx : name
2931 // -- edx : receiver
2932 // -- esp[0] : return address
2933 // -----------------------------------
2934 //
2935 // This accepts as a receiver anything JSArray::SetElementsLength accepts
2936 // (currently anything except for external arrays which means anything with
2937 // elements of FixedArray type). Value must be a number, but only smis are
2938 // accepted as the most common case.
2939
2940 Label miss;
2941
2942 Register receiver = edx;
2943 Register value = eax;
2944 Register scratch = ebx;
2945
ulan@chromium.org750145a2013-03-07 15:14:13 +00002946 if (kind() == Code::KEYED_STORE_IC) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00002947 __ cmp(ecx, Immediate(masm->isolate()->factory()->length_string()));
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00002948 __ j(not_equal, &miss);
2949 }
2950
2951 // Check that the receiver isn't a smi.
2952 __ JumpIfSmi(receiver, &miss);
2953
2954 // Check that the object is a JS array.
2955 __ CmpObjectType(receiver, JS_ARRAY_TYPE, scratch);
2956 __ j(not_equal, &miss);
2957
2958 // Check that elements are FixedArray.
2959 // We rely on StoreIC_ArrayLength below to deal with all types of
2960 // fast elements (including COW).
2961 __ mov(scratch, FieldOperand(receiver, JSArray::kElementsOffset));
2962 __ CmpObjectType(scratch, FIXED_ARRAY_TYPE, scratch);
2963 __ j(not_equal, &miss);
2964
2965 // Check that the array has fast properties, otherwise the length
2966 // property might have been redefined.
2967 __ mov(scratch, FieldOperand(receiver, JSArray::kPropertiesOffset));
2968 __ CompareRoot(FieldOperand(scratch, FixedArray::kMapOffset),
2969 Heap::kHashTableMapRootIndex);
2970 __ j(equal, &miss);
2971
2972 // Check that value is a smi.
2973 __ JumpIfNotSmi(value, &miss);
2974
2975 // Prepare tail call to StoreIC_ArrayLength.
2976 __ pop(scratch);
2977 __ push(receiver);
2978 __ push(value);
2979 __ push(scratch); // return address
2980
2981 ExternalReference ref =
2982 ExternalReference(IC_Utility(IC::kStoreIC_ArrayLength), masm->isolate());
2983 __ TailCallExternalReference(ref, 2, 1);
2984
2985 __ bind(&miss);
2986
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +00002987 StubCompiler::TailCallBuiltin(masm, StubCompiler::MissBuiltin(kind()));
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00002988}
2989
2990
ricow@chromium.org65fae842010-08-25 15:26:24 +00002991void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
2992 // The key is in edx and the parameter count is in eax.
2993
2994 // The displacement is used for skipping the frame pointer on the
2995 // stack. It is the offset of the last parameter (if any) relative
2996 // to the frame pointer.
2997 static const int kDisplacement = 1 * kPointerSize;
2998
2999 // Check that the key is a smi.
3000 Label slow;
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003001 __ JumpIfNotSmi(edx, &slow, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003002
3003 // Check if the calling frame is an arguments adaptor frame.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003004 Label adaptor;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003005 __ mov(ebx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
3006 __ mov(ecx, Operand(ebx, StandardFrameConstants::kContextOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003007 __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003008 __ j(equal, &adaptor, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003009
3010 // Check index against formal parameters count limit passed in
3011 // through register eax. Use unsigned comparison to get negative
3012 // check for free.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003013 __ cmp(edx, eax);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003014 __ j(above_equal, &slow, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003015
3016 // Read the argument from the stack and return it.
3017 STATIC_ASSERT(kSmiTagSize == 1);
3018 STATIC_ASSERT(kSmiTag == 0); // Shifting code depends on these.
3019 __ lea(ebx, Operand(ebp, eax, times_2, 0));
3020 __ neg(edx);
3021 __ mov(eax, Operand(ebx, edx, times_2, kDisplacement));
3022 __ ret(0);
3023
3024 // Arguments adaptor case: Check index against actual arguments
3025 // limit found in the arguments adaptor frame. Use unsigned
3026 // comparison to get negative check for free.
3027 __ bind(&adaptor);
3028 __ mov(ecx, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003029 __ cmp(edx, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003030 __ j(above_equal, &slow, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003031
3032 // Read the argument from the stack and return it.
3033 STATIC_ASSERT(kSmiTagSize == 1);
3034 STATIC_ASSERT(kSmiTag == 0); // Shifting code depends on these.
3035 __ lea(ebx, Operand(ebx, ecx, times_2, 0));
3036 __ neg(edx);
3037 __ mov(eax, Operand(ebx, edx, times_2, kDisplacement));
3038 __ ret(0);
3039
3040 // Slow-case: Handle non-smi or out-of-bounds access to arguments
3041 // by calling the runtime system.
3042 __ bind(&slow);
3043 __ pop(ebx); // Return address.
3044 __ push(edx);
3045 __ push(ebx);
3046 __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
3047}
3048
3049
whesse@chromium.org7b260152011-06-20 15:33:18 +00003050void ArgumentsAccessStub::GenerateNewNonStrictSlow(MacroAssembler* masm) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00003051 // esp[0] : return address
3052 // esp[4] : number of parameters
3053 // esp[8] : receiver displacement
whesse@chromium.org7b260152011-06-20 15:33:18 +00003054 // esp[12] : function
ricow@chromium.org65fae842010-08-25 15:26:24 +00003055
whesse@chromium.org7b260152011-06-20 15:33:18 +00003056 // Check if the calling frame is an arguments adaptor frame.
3057 Label runtime;
3058 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
3059 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003060 __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003061 __ j(not_equal, &runtime, Label::kNear);
3062
3063 // Patch the arguments.length and the parameters pointer.
3064 __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
3065 __ mov(Operand(esp, 1 * kPointerSize), ecx);
3066 __ lea(edx, Operand(edx, ecx, times_2,
3067 StandardFrameConstants::kCallerSPOffset));
3068 __ mov(Operand(esp, 2 * kPointerSize), edx);
3069
3070 __ bind(&runtime);
3071 __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
3072}
3073
3074
3075void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) {
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00003076 Isolate* isolate = masm->isolate();
3077
whesse@chromium.org7b260152011-06-20 15:33:18 +00003078 // esp[0] : return address
3079 // esp[4] : number of parameters (tagged)
3080 // esp[8] : receiver displacement
3081 // esp[12] : function
3082
3083 // ebx = parameter count (tagged)
3084 __ mov(ebx, Operand(esp, 1 * kPointerSize));
3085
3086 // Check if the calling frame is an arguments adaptor frame.
3087 // TODO(rossberg): Factor out some of the bits that are shared with the other
3088 // Generate* functions.
3089 Label runtime;
3090 Label adaptor_frame, try_allocate;
3091 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
3092 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003093 __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003094 __ j(equal, &adaptor_frame, Label::kNear);
3095
3096 // No adaptor, parameter count = argument count.
3097 __ mov(ecx, ebx);
3098 __ jmp(&try_allocate, Label::kNear);
3099
3100 // We have an adaptor frame. Patch the parameters pointer.
3101 __ bind(&adaptor_frame);
3102 __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
3103 __ lea(edx, Operand(edx, ecx, times_2,
3104 StandardFrameConstants::kCallerSPOffset));
3105 __ mov(Operand(esp, 2 * kPointerSize), edx);
3106
3107 // ebx = parameter count (tagged)
3108 // ecx = argument count (tagged)
3109 // esp[4] = parameter count (tagged)
3110 // esp[8] = address of receiver argument
3111 // Compute the mapped parameter count = min(ebx, ecx) in ebx.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003112 __ cmp(ebx, ecx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003113 __ j(less_equal, &try_allocate, Label::kNear);
3114 __ mov(ebx, ecx);
3115
3116 __ bind(&try_allocate);
3117
3118 // Save mapped parameter count.
3119 __ push(ebx);
3120
3121 // Compute the sizes of backing store, parameter map, and arguments object.
3122 // 1. Parameter map, has 2 extra words containing context and backing store.
3123 const int kParameterMapHeaderSize =
3124 FixedArray::kHeaderSize + 2 * kPointerSize;
3125 Label no_parameter_map;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003126 __ test(ebx, ebx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003127 __ j(zero, &no_parameter_map, Label::kNear);
3128 __ lea(ebx, Operand(ebx, times_2, kParameterMapHeaderSize));
3129 __ bind(&no_parameter_map);
3130
3131 // 2. Backing store.
3132 __ lea(ebx, Operand(ebx, ecx, times_2, FixedArray::kHeaderSize));
3133
3134 // 3. Arguments object.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003135 __ add(ebx, Immediate(Heap::kArgumentsObjectSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003136
3137 // Do the allocation of all three objects in one go.
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00003138 __ Allocate(ebx, eax, edx, edi, &runtime, TAG_OBJECT);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003139
3140 // eax = address of new object(s) (tagged)
3141 // ecx = argument count (tagged)
3142 // esp[0] = mapped parameter count (tagged)
3143 // esp[8] = parameter count (tagged)
3144 // esp[12] = address of receiver argument
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00003145 // Get the arguments boilerplate from the current native context into edi.
whesse@chromium.org7b260152011-06-20 15:33:18 +00003146 Label has_mapped_parameters, copy;
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00003147 __ mov(edi, Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
3148 __ mov(edi, FieldOperand(edi, GlobalObject::kNativeContextOffset));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003149 __ mov(ebx, Operand(esp, 0 * kPointerSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003150 __ test(ebx, ebx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003151 __ j(not_zero, &has_mapped_parameters, Label::kNear);
3152 __ mov(edi, Operand(edi,
3153 Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX)));
3154 __ jmp(&copy, Label::kNear);
3155
3156 __ bind(&has_mapped_parameters);
3157 __ mov(edi, Operand(edi,
3158 Context::SlotOffset(Context::ALIASED_ARGUMENTS_BOILERPLATE_INDEX)));
3159 __ bind(&copy);
3160
3161 // eax = address of new object (tagged)
3162 // ebx = mapped parameter count (tagged)
3163 // ecx = argument count (tagged)
3164 // edi = address of boilerplate object (tagged)
3165 // esp[0] = mapped parameter count (tagged)
3166 // esp[8] = parameter count (tagged)
3167 // esp[12] = address of receiver argument
3168 // Copy the JS object part.
3169 for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
3170 __ mov(edx, FieldOperand(edi, i));
3171 __ mov(FieldOperand(eax, i), edx);
3172 }
3173
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00003174 // Set up the callee in-object property.
whesse@chromium.org7b260152011-06-20 15:33:18 +00003175 STATIC_ASSERT(Heap::kArgumentsCalleeIndex == 1);
3176 __ mov(edx, Operand(esp, 4 * kPointerSize));
3177 __ mov(FieldOperand(eax, JSObject::kHeaderSize +
3178 Heap::kArgumentsCalleeIndex * kPointerSize),
3179 edx);
3180
3181 // Use the length (smi tagged) and set that as an in-object property too.
3182 STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0);
3183 __ mov(FieldOperand(eax, JSObject::kHeaderSize +
3184 Heap::kArgumentsLengthIndex * kPointerSize),
3185 ecx);
3186
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00003187 // Set up the elements pointer in the allocated arguments object.
whesse@chromium.org7b260152011-06-20 15:33:18 +00003188 // If we allocated a parameter map, edi will point there, otherwise to the
3189 // backing store.
3190 __ lea(edi, Operand(eax, Heap::kArgumentsObjectSize));
3191 __ mov(FieldOperand(eax, JSObject::kElementsOffset), edi);
3192
3193 // eax = address of new object (tagged)
3194 // ebx = mapped parameter count (tagged)
3195 // ecx = argument count (tagged)
3196 // edi = address of parameter map or backing store (tagged)
3197 // esp[0] = mapped parameter count (tagged)
3198 // esp[8] = parameter count (tagged)
3199 // esp[12] = address of receiver argument
3200 // Free a register.
3201 __ push(eax);
3202
3203 // Initialize parameter map. If there are no mapped arguments, we're done.
3204 Label skip_parameter_map;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003205 __ test(ebx, ebx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003206 __ j(zero, &skip_parameter_map);
3207
3208 __ mov(FieldOperand(edi, FixedArray::kMapOffset),
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00003209 Immediate(isolate->factory()->non_strict_arguments_elements_map()));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003210 __ lea(eax, Operand(ebx, reinterpret_cast<intptr_t>(Smi::FromInt(2))));
3211 __ mov(FieldOperand(edi, FixedArray::kLengthOffset), eax);
3212 __ mov(FieldOperand(edi, FixedArray::kHeaderSize + 0 * kPointerSize), esi);
3213 __ lea(eax, Operand(edi, ebx, times_2, kParameterMapHeaderSize));
3214 __ mov(FieldOperand(edi, FixedArray::kHeaderSize + 1 * kPointerSize), eax);
3215
3216 // Copy the parameter slots and the holes in the arguments.
3217 // We need to fill in mapped_parameter_count slots. They index the context,
3218 // where parameters are stored in reverse order, at
3219 // MIN_CONTEXT_SLOTS .. MIN_CONTEXT_SLOTS+parameter_count-1
3220 // The mapped parameter thus need to get indices
3221 // MIN_CONTEXT_SLOTS+parameter_count-1 ..
3222 // MIN_CONTEXT_SLOTS+parameter_count-mapped_parameter_count
3223 // We loop from right to left.
3224 Label parameters_loop, parameters_test;
3225 __ push(ecx);
3226 __ mov(eax, Operand(esp, 2 * kPointerSize));
3227 __ mov(ebx, Immediate(Smi::FromInt(Context::MIN_CONTEXT_SLOTS)));
3228 __ add(ebx, Operand(esp, 4 * kPointerSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003229 __ sub(ebx, eax);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00003230 __ mov(ecx, isolate->factory()->the_hole_value());
whesse@chromium.org7b260152011-06-20 15:33:18 +00003231 __ mov(edx, edi);
3232 __ lea(edi, Operand(edi, eax, times_2, kParameterMapHeaderSize));
3233 // eax = loop variable (tagged)
3234 // ebx = mapping index (tagged)
3235 // ecx = the hole value
3236 // edx = address of parameter map (tagged)
3237 // edi = address of backing store (tagged)
3238 // esp[0] = argument count (tagged)
3239 // esp[4] = address of new object (tagged)
3240 // esp[8] = mapped parameter count (tagged)
3241 // esp[16] = parameter count (tagged)
3242 // esp[20] = address of receiver argument
3243 __ jmp(&parameters_test, Label::kNear);
3244
3245 __ bind(&parameters_loop);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003246 __ sub(eax, Immediate(Smi::FromInt(1)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003247 __ mov(FieldOperand(edx, eax, times_2, kParameterMapHeaderSize), ebx);
3248 __ mov(FieldOperand(edi, eax, times_2, FixedArray::kHeaderSize), ecx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003249 __ add(ebx, Immediate(Smi::FromInt(1)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003250 __ bind(&parameters_test);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003251 __ test(eax, eax);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003252 __ j(not_zero, &parameters_loop, Label::kNear);
3253 __ pop(ecx);
3254
3255 __ bind(&skip_parameter_map);
3256
3257 // ecx = argument count (tagged)
3258 // edi = address of backing store (tagged)
3259 // esp[0] = address of new object (tagged)
3260 // esp[4] = mapped parameter count (tagged)
3261 // esp[12] = parameter count (tagged)
3262 // esp[16] = address of receiver argument
3263 // Copy arguments header and remaining slots (if there are any).
3264 __ mov(FieldOperand(edi, FixedArray::kMapOffset),
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00003265 Immediate(isolate->factory()->fixed_array_map()));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003266 __ mov(FieldOperand(edi, FixedArray::kLengthOffset), ecx);
3267
3268 Label arguments_loop, arguments_test;
3269 __ mov(ebx, Operand(esp, 1 * kPointerSize));
3270 __ mov(edx, Operand(esp, 4 * kPointerSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003271 __ sub(edx, ebx); // Is there a smarter way to do negative scaling?
3272 __ sub(edx, ebx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003273 __ jmp(&arguments_test, Label::kNear);
3274
3275 __ bind(&arguments_loop);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003276 __ sub(edx, Immediate(kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003277 __ mov(eax, Operand(edx, 0));
3278 __ mov(FieldOperand(edi, ebx, times_2, FixedArray::kHeaderSize), eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003279 __ add(ebx, Immediate(Smi::FromInt(1)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003280
3281 __ bind(&arguments_test);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003282 __ cmp(ebx, ecx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003283 __ j(less, &arguments_loop, Label::kNear);
3284
3285 // Restore.
3286 __ pop(eax); // Address of arguments object.
3287 __ pop(ebx); // Parameter count.
3288
3289 // Return and remove the on-stack parameters.
3290 __ ret(3 * kPointerSize);
3291
3292 // Do the runtime call to allocate the arguments object.
3293 __ bind(&runtime);
3294 __ pop(eax); // Remove saved parameter count.
3295 __ mov(Operand(esp, 1 * kPointerSize), ecx); // Patch argument count.
danno@chromium.org72204d52012-10-31 10:02:10 +00003296 __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003297}
3298
3299
3300void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) {
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00003301 Isolate* isolate = masm->isolate();
3302
whesse@chromium.org7b260152011-06-20 15:33:18 +00003303 // esp[0] : return address
3304 // esp[4] : number of parameters
3305 // esp[8] : receiver displacement
3306 // esp[12] : function
ricow@chromium.org65fae842010-08-25 15:26:24 +00003307
3308 // Check if the calling frame is an arguments adaptor frame.
3309 Label adaptor_frame, try_allocate, runtime;
3310 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
3311 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003312 __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003313 __ j(equal, &adaptor_frame, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003314
3315 // Get the length from the frame.
3316 __ mov(ecx, Operand(esp, 1 * kPointerSize));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003317 __ jmp(&try_allocate, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003318
3319 // Patch the arguments.length and the parameters pointer.
3320 __ bind(&adaptor_frame);
3321 __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
3322 __ mov(Operand(esp, 1 * kPointerSize), ecx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003323 __ lea(edx, Operand(edx, ecx, times_2,
3324 StandardFrameConstants::kCallerSPOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003325 __ mov(Operand(esp, 2 * kPointerSize), edx);
3326
3327 // Try the new space allocation. Start out with computing the size of
3328 // the arguments object and the elements array.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003329 Label add_arguments_object;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003330 __ bind(&try_allocate);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003331 __ test(ecx, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003332 __ j(zero, &add_arguments_object, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003333 __ lea(ecx, Operand(ecx, times_2, FixedArray::kHeaderSize));
3334 __ bind(&add_arguments_object);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003335 __ add(ecx, Immediate(Heap::kArgumentsObjectSizeStrict));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003336
3337 // Do the allocation of both objects in one go.
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00003338 __ Allocate(ecx, eax, edx, ebx, &runtime, TAG_OBJECT);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003339
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00003340 // Get the arguments boilerplate from the current native context.
3341 __ mov(edi, Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
3342 __ mov(edi, FieldOperand(edi, GlobalObject::kNativeContextOffset));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003343 const int offset =
3344 Context::SlotOffset(Context::STRICT_MODE_ARGUMENTS_BOILERPLATE_INDEX);
3345 __ mov(edi, Operand(edi, offset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003346
3347 // Copy the JS object part.
3348 for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
3349 __ mov(ebx, FieldOperand(edi, i));
3350 __ mov(FieldOperand(eax, i), ebx);
3351 }
3352
ricow@chromium.org65fae842010-08-25 15:26:24 +00003353 // Get the length (smi tagged) and set that as an in-object property too.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003354 STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003355 __ mov(ecx, Operand(esp, 1 * kPointerSize));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003356 __ mov(FieldOperand(eax, JSObject::kHeaderSize +
whesse@chromium.org7b260152011-06-20 15:33:18 +00003357 Heap::kArgumentsLengthIndex * kPointerSize),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003358 ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003359
3360 // If there are no actual arguments, we're done.
3361 Label done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003362 __ test(ecx, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003363 __ j(zero, &done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003364
3365 // Get the parameters pointer from the stack.
3366 __ mov(edx, Operand(esp, 2 * kPointerSize));
3367
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00003368 // Set up the elements pointer in the allocated arguments object and
ricow@chromium.org65fae842010-08-25 15:26:24 +00003369 // initialize the header in the elements fixed array.
whesse@chromium.org7b260152011-06-20 15:33:18 +00003370 __ lea(edi, Operand(eax, Heap::kArgumentsObjectSizeStrict));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003371 __ mov(FieldOperand(eax, JSObject::kElementsOffset), edi);
3372 __ mov(FieldOperand(edi, FixedArray::kMapOffset),
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00003373 Immediate(isolate->factory()->fixed_array_map()));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003374
ricow@chromium.org65fae842010-08-25 15:26:24 +00003375 __ mov(FieldOperand(edi, FixedArray::kLengthOffset), ecx);
3376 // Untag the length for the loop below.
3377 __ SmiUntag(ecx);
3378
3379 // Copy the fixed array slots.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003380 Label loop;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003381 __ bind(&loop);
3382 __ mov(ebx, Operand(edx, -1 * kPointerSize)); // Skip receiver.
3383 __ mov(FieldOperand(edi, FixedArray::kHeaderSize), ebx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003384 __ add(edi, Immediate(kPointerSize));
3385 __ sub(edx, Immediate(kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003386 __ dec(ecx);
3387 __ j(not_zero, &loop);
3388
3389 // Return and remove the on-stack parameters.
3390 __ bind(&done);
3391 __ ret(3 * kPointerSize);
3392
3393 // Do the runtime call to allocate the arguments object.
3394 __ bind(&runtime);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003395 __ TailCallRuntime(Runtime::kNewStrictArgumentsFast, 3, 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003396}
3397
3398
3399void RegExpExecStub::Generate(MacroAssembler* masm) {
3400 // Just jump directly to runtime if native RegExp is not selected at compile
3401 // time or if regexp entry in generated code is turned off runtime switch or
3402 // at compilation.
3403#ifdef V8_INTERPRETED_REGEXP
3404 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
3405#else // V8_INTERPRETED_REGEXP
ricow@chromium.org65fae842010-08-25 15:26:24 +00003406
3407 // Stack frame on entry.
3408 // esp[0]: return address
3409 // esp[4]: last_match_info (expected JSArray)
3410 // esp[8]: previous index
3411 // esp[12]: subject string
3412 // esp[16]: JSRegExp object
3413
3414 static const int kLastMatchInfoOffset = 1 * kPointerSize;
3415 static const int kPreviousIndexOffset = 2 * kPointerSize;
3416 static const int kSubjectOffset = 3 * kPointerSize;
3417 static const int kJSRegExpOffset = 4 * kPointerSize;
3418
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003419 Label runtime;
3420 Factory* factory = masm->isolate()->factory();
ricow@chromium.org65fae842010-08-25 15:26:24 +00003421
3422 // Ensure that a RegExp stack is allocated.
3423 ExternalReference address_of_regexp_stack_memory_address =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003424 ExternalReference::address_of_regexp_stack_memory_address(
3425 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003426 ExternalReference address_of_regexp_stack_memory_size =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003427 ExternalReference::address_of_regexp_stack_memory_size(masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003428 __ mov(ebx, Operand::StaticVariable(address_of_regexp_stack_memory_size));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003429 __ test(ebx, ebx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00003430 __ j(zero, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003431
3432 // Check that the first argument is a JSRegExp object.
3433 __ mov(eax, Operand(esp, kJSRegExpOffset));
3434 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003435 __ JumpIfSmi(eax, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003436 __ CmpObjectType(eax, JS_REGEXP_TYPE, ecx);
3437 __ j(not_equal, &runtime);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003438
ricow@chromium.org65fae842010-08-25 15:26:24 +00003439 // Check that the RegExp has been compiled (data contains a fixed array).
3440 __ mov(ecx, FieldOperand(eax, JSRegExp::kDataOffset));
3441 if (FLAG_debug_code) {
3442 __ test(ecx, Immediate(kSmiTagMask));
3443 __ Check(not_zero, "Unexpected type for RegExp data, FixedArray expected");
3444 __ CmpObjectType(ecx, FIXED_ARRAY_TYPE, ebx);
3445 __ Check(equal, "Unexpected type for RegExp data, FixedArray expected");
3446 }
3447
3448 // ecx: RegExp data (FixedArray)
3449 // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP.
3450 __ mov(ebx, FieldOperand(ecx, JSRegExp::kDataTagOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003451 __ cmp(ebx, Immediate(Smi::FromInt(JSRegExp::IRREGEXP)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003452 __ j(not_equal, &runtime);
3453
3454 // ecx: RegExp data (FixedArray)
3455 // Check that the number of captures fit in the static offsets vector buffer.
3456 __ mov(edx, FieldOperand(ecx, JSRegExp::kIrregexpCaptureCountOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003457 // Check (number_of_captures + 1) * 2 <= offsets vector size
3458 // Or number_of_captures * 2 <= offsets vector size - 2
3459 // Multiplying by 2 comes for free since edx is smi-tagged.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003460 STATIC_ASSERT(kSmiTag == 0);
3461 STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003462 STATIC_ASSERT(Isolate::kJSRegexpStaticOffsetsVectorSize >= 2);
3463 __ cmp(edx, Isolate::kJSRegexpStaticOffsetsVectorSize - 2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003464 __ j(above, &runtime);
3465
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003466 // Reset offset for possibly sliced string.
3467 __ Set(edi, Immediate(0));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003468 __ mov(eax, Operand(esp, kSubjectOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003469 __ JumpIfSmi(eax, &runtime);
3470 __ mov(edx, eax); // Make a copy of the original subject string.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003471 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
3472 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003473
3474 // eax: subject string
3475 // edx: subject string
3476 // ebx: subject string instance type
3477 // ecx: RegExp data (FixedArray)
3478 // Handle subject string according to its encoding and representation:
3479 // (1) Sequential two byte? If yes, go to (9).
3480 // (2) Sequential one byte? If yes, go to (6).
3481 // (3) Anything but sequential or cons? If yes, go to (7).
3482 // (4) Cons string. If the string is flat, replace subject with first string.
3483 // Otherwise bailout.
3484 // (5a) Is subject sequential two byte? If yes, go to (9).
3485 // (5b) Is subject external? If yes, go to (8).
3486 // (6) One byte sequential. Load regexp code for one byte.
3487 // (E) Carry on.
3488 /// [...]
3489
3490 // Deferred code at the end of the stub:
3491 // (7) Not a long external string? If yes, go to (10).
3492 // (8) External string. Make it, offset-wise, look like a sequential string.
3493 // (8a) Is the external string one byte? If yes, go to (6).
3494 // (9) Two byte sequential. Load regexp code for one byte. Go to (E).
3495 // (10) Short external string or not a string? If yes, bail out to runtime.
3496 // (11) Sliced string. Replace subject with parent. Go to (5a).
3497
3498 Label seq_one_byte_string /* 6 */, seq_two_byte_string /* 9 */,
3499 external_string /* 8 */, check_underlying /* 5a */,
3500 not_seq_nor_cons /* 7 */, check_code /* E */,
3501 not_long_external /* 10 */;
3502
3503 // (1) Sequential two byte? If yes, go to (9).
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003504 __ and_(ebx, kIsNotStringMask |
3505 kStringRepresentationMask |
3506 kStringEncodingMask |
3507 kShortExternalStringMask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003508 STATIC_ASSERT((kStringTag | kSeqStringTag | kTwoByteStringTag) == 0);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003509 __ j(zero, &seq_two_byte_string); // Go to (9).
3510
3511 // (2) Sequential one byte? If yes, go to (6).
3512 // Any other sequential string must be one byte.
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003513 __ and_(ebx, Immediate(kIsNotStringMask |
3514 kStringRepresentationMask |
3515 kShortExternalStringMask));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003516 __ j(zero, &seq_one_byte_string, Label::kNear); // Go to (6).
ricow@chromium.org65fae842010-08-25 15:26:24 +00003517
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003518 // (3) Anything but sequential or cons? If yes, go to (7).
3519 // We check whether the subject string is a cons, since sequential strings
3520 // have already been covered.
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00003521 STATIC_ASSERT(kConsStringTag < kExternalStringTag);
3522 STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003523 STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
3524 STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003525 __ cmp(ebx, Immediate(kExternalStringTag));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003526 __ j(greater_equal, &not_seq_nor_cons); // Go to (7).
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003527
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003528 // (4) Cons string. Check that it's flat.
3529 // Replace subject with first string and reload instance type.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003530 __ cmp(FieldOperand(eax, ConsString::kSecondOffset), factory->empty_string());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003531 __ j(not_equal, &runtime);
3532 __ mov(eax, FieldOperand(eax, ConsString::kFirstOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003533 __ bind(&check_underlying);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003534 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003535 __ mov(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
3536
3537 // (5a) Is subject sequential two byte? If yes, go to (9).
3538 __ test_b(ebx, kStringRepresentationMask | kStringEncodingMask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003539 STATIC_ASSERT((kSeqStringTag | kTwoByteStringTag) == 0);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003540 __ j(zero, &seq_two_byte_string); // Go to (9).
3541 // (5b) Is subject external? If yes, go to (8).
3542 __ test_b(ebx, kStringRepresentationMask);
3543 // The underlying external string is never a short external string.
3544 STATIC_CHECK(ExternalString::kMaxShortLength < ConsString::kMinLength);
3545 STATIC_CHECK(ExternalString::kMaxShortLength < SlicedString::kMinLength);
3546 __ j(not_zero, &external_string); // Go to (8).
ricow@chromium.org65fae842010-08-25 15:26:24 +00003547
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003548 // eax: sequential subject string (or look-alike, external string)
3549 // edx: original subject string
ricow@chromium.org65fae842010-08-25 15:26:24 +00003550 // ecx: RegExp data (FixedArray)
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003551 // (6) One byte sequential. Load regexp code for one byte.
3552 __ bind(&seq_one_byte_string);
3553 // Load previous index and check range before edx is overwritten. We have
3554 // to use edx instead of eax here because it might have been only made to
3555 // look like a sequential string when it actually is an external string.
3556 __ mov(ebx, Operand(esp, kPreviousIndexOffset));
3557 __ JumpIfNotSmi(ebx, &runtime);
3558 __ cmp(ebx, FieldOperand(edx, String::kLengthOffset));
3559 __ j(above_equal, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003560 __ mov(edx, FieldOperand(ecx, JSRegExp::kDataAsciiCodeOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003561 __ Set(ecx, Immediate(1)); // Type is one byte.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003562
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003563 // (E) Carry on. String handling is done.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003564 __ bind(&check_code);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003565 // edx: irregexp code
ricow@chromium.org65fae842010-08-25 15:26:24 +00003566 // Check that the irregexp code has been generated for the actual string
3567 // encoding. If it has, the field contains a code object otherwise it contains
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00003568 // a smi (code flushing support).
3569 __ JumpIfSmi(edx, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003570
3571 // eax: subject string
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003572 // ebx: previous index (smi)
ricow@chromium.org65fae842010-08-25 15:26:24 +00003573 // edx: code
ulan@chromium.org2efb9002012-01-19 15:36:35 +00003574 // ecx: encoding of subject string (1 if ASCII, 0 if two_byte);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003575 // All checks done. Now push arguments for native regexp code.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003576 Counters* counters = masm->isolate()->counters();
3577 __ IncrementCounter(counters->regexp_entry_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003578
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003579 // Isolates: note we add an additional parameter here (isolate pointer).
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003580 static const int kRegExpExecuteArguments = 9;
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003581 __ EnterApiExitFrame(kRegExpExecuteArguments);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003582
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003583 // Argument 9: Pass current isolate address.
3584 __ mov(Operand(esp, 8 * kPointerSize),
ulan@chromium.org32d7dba2013-04-24 10:59:06 +00003585 Immediate(ExternalReference::isolate_address(masm->isolate())));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003586
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003587 // Argument 8: Indicate that this is a direct call from JavaScript.
3588 __ mov(Operand(esp, 7 * kPointerSize), Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003589
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003590 // Argument 7: Start (high end) of backtracking stack memory area.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003591 __ mov(esi, Operand::StaticVariable(address_of_regexp_stack_memory_address));
3592 __ add(esi, Operand::StaticVariable(address_of_regexp_stack_memory_size));
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003593 __ mov(Operand(esp, 6 * kPointerSize), esi);
3594
3595 // Argument 6: Set the number of capture registers to zero to force global
3596 // regexps to behave as non-global. This does not affect non-global regexps.
3597 __ mov(Operand(esp, 5 * kPointerSize), Immediate(0));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003598
3599 // Argument 5: static offsets vector buffer.
3600 __ mov(Operand(esp, 4 * kPointerSize),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003601 Immediate(ExternalReference::address_of_static_offsets_vector(
3602 masm->isolate())));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003603
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003604 // Argument 2: Previous index.
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003605 __ SmiUntag(ebx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003606 __ mov(Operand(esp, 1 * kPointerSize), ebx);
3607
3608 // Argument 1: Original subject string.
3609 // The original subject is in the previous stack frame. Therefore we have to
3610 // use ebp, which points exactly to one pointer size below the previous esp.
3611 // (Because creating a new stack frame pushes the previous ebp onto the stack
3612 // and thereby moves up esp by one kPointerSize.)
3613 __ mov(esi, Operand(ebp, kSubjectOffset + kPointerSize));
3614 __ mov(Operand(esp, 0 * kPointerSize), esi);
3615
3616 // esi: original subject string
3617 // eax: underlying subject string
3618 // ebx: previous index
ulan@chromium.org2efb9002012-01-19 15:36:35 +00003619 // ecx: encoding of subject string (1 if ASCII 0 if two_byte);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003620 // edx: code
ricow@chromium.org65fae842010-08-25 15:26:24 +00003621 // Argument 4: End of string data
3622 // Argument 3: Start of string data
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003623 // Prepare start and end index of the input.
3624 // Load the length from the original sliced string if that is the case.
3625 __ mov(esi, FieldOperand(esi, String::kLengthOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003626 __ add(esi, edi); // Calculate input end wrt offset.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003627 __ SmiUntag(edi);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003628 __ add(ebx, edi); // Calculate input start wrt offset.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003629
3630 // ebx: start index of the input string
3631 // esi: end index of the input string
3632 Label setup_two_byte, setup_rest;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003633 __ test(ecx, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003634 __ j(zero, &setup_two_byte, Label::kNear);
3635 __ SmiUntag(esi);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003636 __ lea(ecx, FieldOperand(eax, esi, times_1, SeqOneByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003637 __ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003638 __ lea(ecx, FieldOperand(eax, ebx, times_1, SeqOneByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003639 __ mov(Operand(esp, 2 * kPointerSize), ecx); // Argument 3.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003640 __ jmp(&setup_rest, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003641
3642 __ bind(&setup_two_byte);
3643 STATIC_ASSERT(kSmiTag == 0);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003644 STATIC_ASSERT(kSmiTagSize == 1); // esi is smi (powered by 2).
3645 __ lea(ecx, FieldOperand(eax, esi, times_1, SeqTwoByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003646 __ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
3647 __ lea(ecx, FieldOperand(eax, ebx, times_2, SeqTwoByteString::kHeaderSize));
3648 __ mov(Operand(esp, 2 * kPointerSize), ecx); // Argument 3.
3649
3650 __ bind(&setup_rest);
3651
ricow@chromium.org65fae842010-08-25 15:26:24 +00003652 // Locate the code entry and call it.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003653 __ add(edx, Immediate(Code::kHeaderSize - kHeapObjectTag));
3654 __ call(edx);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003655
3656 // Drop arguments and come back to JS mode.
3657 __ LeaveApiExitFrame();
ricow@chromium.org65fae842010-08-25 15:26:24 +00003658
3659 // Check the result.
3660 Label success;
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003661 __ cmp(eax, 1);
3662 // We expect exactly one result since we force the called regexp to behave
3663 // as non-global.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00003664 __ j(equal, &success);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003665 Label failure;
3666 __ cmp(eax, NativeRegExpMacroAssembler::FAILURE);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00003667 __ j(equal, &failure);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003668 __ cmp(eax, NativeRegExpMacroAssembler::EXCEPTION);
3669 // If not exception it can only be retry. Handle that in the runtime system.
3670 __ j(not_equal, &runtime);
3671 // Result must now be exception. If there is no pending exception already a
3672 // stack overflow (on the backtrack stack) was detected in RegExp code but
3673 // haven't created the exception yet. Handle that in the runtime system.
3674 // TODO(592): Rerunning the RegExp to get the stack overflow exception.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00003675 ExternalReference pending_exception(Isolate::kPendingExceptionAddress,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003676 masm->isolate());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003677 __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003678 __ mov(eax, Operand::StaticVariable(pending_exception));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003679 __ cmp(edx, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003680 __ j(equal, &runtime);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003681 // For exception, throw the exception again.
3682
3683 // Clear the pending exception variable.
3684 __ mov(Operand::StaticVariable(pending_exception), edx);
3685
3686 // Special handling of termination exceptions which are uncatchable
3687 // by javascript code.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003688 __ cmp(eax, factory->termination_exception());
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003689 Label throw_termination_exception;
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003690 __ j(equal, &throw_termination_exception, Label::kNear);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003691
3692 // Handle normal exception by following handler chain.
3693 __ Throw(eax);
3694
3695 __ bind(&throw_termination_exception);
ulan@chromium.org65a89c22012-02-14 11:46:07 +00003696 __ ThrowUncatchable(eax);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003697
ricow@chromium.org65fae842010-08-25 15:26:24 +00003698 __ bind(&failure);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003699 // For failure to match, return null.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003700 __ mov(eax, factory->null_value());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003701 __ ret(4 * kPointerSize);
3702
3703 // Load RegExp data.
3704 __ bind(&success);
3705 __ mov(eax, Operand(esp, kJSRegExpOffset));
3706 __ mov(ecx, FieldOperand(eax, JSRegExp::kDataOffset));
3707 __ mov(edx, FieldOperand(ecx, JSRegExp::kIrregexpCaptureCountOffset));
3708 // Calculate number of capture registers (number_of_captures + 1) * 2.
3709 STATIC_ASSERT(kSmiTag == 0);
3710 STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003711 __ add(edx, Immediate(2)); // edx was a smi.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003712
3713 // edx: Number of capture registers
3714 // Load last_match_info which is still known to be a fast case JSArray.
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003715 // Check that the fourth object is a JSArray object.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003716 __ mov(eax, Operand(esp, kLastMatchInfoOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003717 __ JumpIfSmi(eax, &runtime);
3718 __ CmpObjectType(eax, JS_ARRAY_TYPE, ebx);
3719 __ j(not_equal, &runtime);
3720 // Check that the JSArray is in fast case.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003721 __ mov(ebx, FieldOperand(eax, JSArray::kElementsOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003722 __ mov(eax, FieldOperand(ebx, HeapObject::kMapOffset));
3723 __ cmp(eax, factory->fixed_array_map());
3724 __ j(not_equal, &runtime);
3725 // Check that the last match info has space for the capture registers and the
3726 // additional information.
3727 __ mov(eax, FieldOperand(ebx, FixedArray::kLengthOffset));
3728 __ SmiUntag(eax);
3729 __ sub(eax, Immediate(RegExpImpl::kLastMatchOverhead));
3730 __ cmp(edx, eax);
3731 __ j(greater, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003732
3733 // ebx: last_match_info backing store (FixedArray)
3734 // edx: number of capture registers
3735 // Store the capture count.
3736 __ SmiTag(edx); // Number of capture registers to smi.
3737 __ mov(FieldOperand(ebx, RegExpImpl::kLastCaptureCountOffset), edx);
3738 __ SmiUntag(edx); // Number of capture registers back from smi.
3739 // Store last subject and last input.
3740 __ mov(eax, Operand(esp, kSubjectOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003741 __ mov(ecx, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003742 __ mov(FieldOperand(ebx, RegExpImpl::kLastSubjectOffset), eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003743 __ RecordWriteField(ebx,
3744 RegExpImpl::kLastSubjectOffset,
3745 eax,
3746 edi,
3747 kDontSaveFPRegs);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003748 __ mov(eax, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003749 __ mov(FieldOperand(ebx, RegExpImpl::kLastInputOffset), eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003750 __ RecordWriteField(ebx,
3751 RegExpImpl::kLastInputOffset,
3752 eax,
3753 edi,
3754 kDontSaveFPRegs);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003755
3756 // Get the static offsets vector filled by the native regexp code.
3757 ExternalReference address_of_static_offsets_vector =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003758 ExternalReference::address_of_static_offsets_vector(masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003759 __ mov(ecx, Immediate(address_of_static_offsets_vector));
3760
3761 // ebx: last_match_info backing store (FixedArray)
3762 // ecx: offsets vector
3763 // edx: number of capture registers
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003764 Label next_capture, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003765 // Capture register counter starts from number of capture registers and
3766 // counts down until wraping after zero.
3767 __ bind(&next_capture);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003768 __ sub(edx, Immediate(1));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003769 __ j(negative, &done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003770 // Read the value from the static offsets vector buffer.
3771 __ mov(edi, Operand(ecx, edx, times_int_size, 0));
3772 __ SmiTag(edi);
3773 // Store the smi value in the last match info.
3774 __ mov(FieldOperand(ebx,
3775 edx,
3776 times_pointer_size,
3777 RegExpImpl::kFirstCaptureOffset),
3778 edi);
3779 __ jmp(&next_capture);
3780 __ bind(&done);
3781
3782 // Return last match info.
3783 __ mov(eax, Operand(esp, kLastMatchInfoOffset));
3784 __ ret(4 * kPointerSize);
3785
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003786 // Do the runtime call to execute the regexp.
3787 __ bind(&runtime);
3788 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
3789
3790 // Deferred code for string handling.
3791 // (7) Not a long external string? If yes, go to (10).
3792 __ bind(&not_seq_nor_cons);
3793 // Compare flags are still set from (3).
3794 __ j(greater, &not_long_external, Label::kNear); // Go to (10).
3795
3796 // (8) External string. Short external strings have been ruled out.
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003797 __ bind(&external_string);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003798 // Reload instance type.
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003799 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
3800 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
3801 if (FLAG_debug_code) {
3802 // Assert that we do not have a cons or slice (indirect strings) here.
3803 // Sequential strings have already been ruled out.
3804 __ test_b(ebx, kIsIndirectStringMask);
3805 __ Assert(zero, "external string expected, but not found");
3806 }
3807 __ mov(eax, FieldOperand(eax, ExternalString::kResourceDataOffset));
3808 // Move the pointer so that offset-wise, it looks like a sequential string.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003809 STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003810 __ sub(eax, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
3811 STATIC_ASSERT(kTwoByteStringTag == 0);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003812 // (8a) Is the external string one byte? If yes, go to (6).
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003813 __ test_b(ebx, kStringEncodingMask);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003814 __ j(not_zero, &seq_one_byte_string); // Goto (6).
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003815
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003816 // eax: sequential subject string (or look-alike, external string)
3817 // edx: original subject string
3818 // ecx: RegExp data (FixedArray)
3819 // (9) Two byte sequential. Load regexp code for one byte. Go to (E).
3820 __ bind(&seq_two_byte_string);
3821 // Load previous index and check range before edx is overwritten. We have
3822 // to use edx instead of eax here because it might have been only made to
3823 // look like a sequential string when it actually is an external string.
3824 __ mov(ebx, Operand(esp, kPreviousIndexOffset));
3825 __ JumpIfNotSmi(ebx, &runtime);
3826 __ cmp(ebx, FieldOperand(edx, String::kLengthOffset));
3827 __ j(above_equal, &runtime);
3828 __ mov(edx, FieldOperand(ecx, JSRegExp::kDataUC16CodeOffset));
3829 __ Set(ecx, Immediate(0)); // Type is two byte.
3830 __ jmp(&check_code); // Go to (E).
3831
3832 // (10) Not a string or a short external string? If yes, bail out to runtime.
3833 __ bind(&not_long_external);
3834 // Catch non-string subject or short external string.
3835 STATIC_ASSERT(kNotStringTag != 0 && kShortExternalStringTag !=0);
3836 __ test(ebx, Immediate(kIsNotStringMask | kShortExternalStringTag));
3837 __ j(not_zero, &runtime);
3838
3839 // (11) Sliced string. Replace subject with parent. Go to (5a).
3840 // Load offset into edi and replace subject string with parent.
3841 __ mov(edi, FieldOperand(eax, SlicedString::kOffsetOffset));
3842 __ mov(eax, FieldOperand(eax, SlicedString::kParentOffset));
3843 __ jmp(&check_underlying); // Go to (5a).
ricow@chromium.org65fae842010-08-25 15:26:24 +00003844#endif // V8_INTERPRETED_REGEXP
3845}
3846
3847
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003848void RegExpConstructResultStub::Generate(MacroAssembler* masm) {
3849 const int kMaxInlineLength = 100;
3850 Label slowcase;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003851 Label done;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003852 __ mov(ebx, Operand(esp, kPointerSize * 3));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003853 __ JumpIfNotSmi(ebx, &slowcase);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003854 __ cmp(ebx, Immediate(Smi::FromInt(kMaxInlineLength)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003855 __ j(above, &slowcase);
3856 // Smi-tagging is equivalent to multiplying by 2.
3857 STATIC_ASSERT(kSmiTag == 0);
3858 STATIC_ASSERT(kSmiTagSize == 1);
3859 // Allocate RegExpResult followed by FixedArray with size in ebx.
3860 // JSArray: [Map][empty properties][Elements][Length-smi][index][input]
3861 // Elements: [Map][Length][..elements..]
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00003862 __ Allocate(JSRegExpResult::kSize + FixedArray::kHeaderSize,
3863 times_pointer_size,
3864 ebx, // In: Number of elements as a smi
3865 REGISTER_VALUE_IS_SMI,
3866 eax, // Out: Start of allocation (tagged).
3867 ecx, // Out: End of allocation.
3868 edx, // Scratch register
3869 &slowcase,
3870 TAG_OBJECT);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003871 // eax: Start of allocated area, object-tagged.
3872
3873 // Set JSArray map to global.regexp_result_map().
3874 // Set empty properties FixedArray.
3875 // Set elements to point to FixedArray allocated right after the JSArray.
3876 // Interleave operations for better latency.
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00003877 __ mov(edx, ContextOperand(esi, Context::GLOBAL_OBJECT_INDEX));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003878 Factory* factory = masm->isolate()->factory();
3879 __ mov(ecx, Immediate(factory->empty_fixed_array()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003880 __ lea(ebx, Operand(eax, JSRegExpResult::kSize));
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00003881 __ mov(edx, FieldOperand(edx, GlobalObject::kNativeContextOffset));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003882 __ mov(FieldOperand(eax, JSObject::kElementsOffset), ebx);
3883 __ mov(FieldOperand(eax, JSObject::kPropertiesOffset), ecx);
3884 __ mov(edx, ContextOperand(edx, Context::REGEXP_RESULT_MAP_INDEX));
3885 __ mov(FieldOperand(eax, HeapObject::kMapOffset), edx);
3886
3887 // Set input, index and length fields from arguments.
3888 __ mov(ecx, Operand(esp, kPointerSize * 1));
3889 __ mov(FieldOperand(eax, JSRegExpResult::kInputOffset), ecx);
3890 __ mov(ecx, Operand(esp, kPointerSize * 2));
3891 __ mov(FieldOperand(eax, JSRegExpResult::kIndexOffset), ecx);
3892 __ mov(ecx, Operand(esp, kPointerSize * 3));
3893 __ mov(FieldOperand(eax, JSArray::kLengthOffset), ecx);
3894
3895 // Fill out the elements FixedArray.
3896 // eax: JSArray.
3897 // ebx: FixedArray.
3898 // ecx: Number of elements in array, as smi.
3899
3900 // Set map.
3901 __ mov(FieldOperand(ebx, HeapObject::kMapOffset),
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003902 Immediate(factory->fixed_array_map()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003903 // Set length.
3904 __ mov(FieldOperand(ebx, FixedArray::kLengthOffset), ecx);
ulan@chromium.org56c14af2012-09-20 12:51:09 +00003905 // Fill contents of fixed-array with undefined.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003906 __ SmiUntag(ecx);
ulan@chromium.org56c14af2012-09-20 12:51:09 +00003907 __ mov(edx, Immediate(factory->undefined_value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003908 __ lea(ebx, FieldOperand(ebx, FixedArray::kHeaderSize));
ulan@chromium.org56c14af2012-09-20 12:51:09 +00003909 // Fill fixed array elements with undefined.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003910 // eax: JSArray.
3911 // ecx: Number of elements to fill.
3912 // ebx: Start of elements in FixedArray.
ulan@chromium.org56c14af2012-09-20 12:51:09 +00003913 // edx: undefined.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003914 Label loop;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003915 __ test(ecx, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003916 __ bind(&loop);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003917 __ j(less_equal, &done, Label::kNear); // Jump if ecx is negative or zero.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003918 __ sub(ecx, Immediate(1));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003919 __ mov(Operand(ebx, ecx, times_pointer_size, 0), edx);
3920 __ jmp(&loop);
3921
3922 __ bind(&done);
3923 __ ret(3 * kPointerSize);
3924
3925 __ bind(&slowcase);
3926 __ TailCallRuntime(Runtime::kRegExpConstructResult, 3, 1);
3927}
3928
3929
ricow@chromium.org65fae842010-08-25 15:26:24 +00003930void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
3931 Register object,
3932 Register result,
3933 Register scratch1,
3934 Register scratch2,
3935 bool object_is_smi,
3936 Label* not_found) {
3937 // Use of registers. Register result is used as a temporary.
3938 Register number_string_cache = result;
3939 Register mask = scratch1;
3940 Register scratch = scratch2;
3941
3942 // Load the number string cache.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00003943 ExternalReference roots_array_start =
3944 ExternalReference::roots_array_start(masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003945 __ mov(scratch, Immediate(Heap::kNumberStringCacheRootIndex));
3946 __ mov(number_string_cache,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00003947 Operand::StaticArray(scratch, times_pointer_size, roots_array_start));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003948 // Make the hash mask from the length of the number string cache. It
3949 // contains two elements (number and string) for each cache entry.
3950 __ mov(mask, FieldOperand(number_string_cache, FixedArray::kLengthOffset));
3951 __ shr(mask, kSmiTagSize + 1); // Untag length and divide it by two.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003952 __ sub(mask, Immediate(1)); // Make mask.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003953
3954 // Calculate the entry in the number string cache. The hash value in the
3955 // number string cache for smis is just the smi value, and the hash for
3956 // doubles is the xor of the upper and lower words. See
3957 // Heap::GetNumberStringCache.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003958 Label smi_hash_calculated;
3959 Label load_result_from_cache;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003960 if (object_is_smi) {
3961 __ mov(scratch, object);
3962 __ SmiUntag(scratch);
3963 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003964 Label not_smi;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003965 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003966 __ JumpIfNotSmi(object, &not_smi, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003967 __ mov(scratch, object);
3968 __ SmiUntag(scratch);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003969 __ jmp(&smi_hash_calculated, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003970 __ bind(&not_smi);
3971 __ cmp(FieldOperand(object, HeapObject::kMapOffset),
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003972 masm->isolate()->factory()->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003973 __ j(not_equal, not_found);
3974 STATIC_ASSERT(8 == kDoubleSize);
3975 __ mov(scratch, FieldOperand(object, HeapNumber::kValueOffset));
3976 __ xor_(scratch, FieldOperand(object, HeapNumber::kValueOffset + 4));
3977 // Object is heap number and hash is now in scratch. Calculate cache index.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003978 __ and_(scratch, mask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003979 Register index = scratch;
3980 Register probe = mask;
3981 __ mov(probe,
3982 FieldOperand(number_string_cache,
3983 index,
3984 times_twice_pointer_size,
3985 FixedArray::kHeaderSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003986 __ JumpIfSmi(probe, not_found);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00003987 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00003988 CpuFeatureScope fscope(masm, SSE2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003989 __ movdbl(xmm0, FieldOperand(object, HeapNumber::kValueOffset));
3990 __ movdbl(xmm1, FieldOperand(probe, HeapNumber::kValueOffset));
3991 __ ucomisd(xmm0, xmm1);
3992 } else {
3993 __ fld_d(FieldOperand(object, HeapNumber::kValueOffset));
3994 __ fld_d(FieldOperand(probe, HeapNumber::kValueOffset));
3995 __ FCmp();
3996 }
3997 __ j(parity_even, not_found); // Bail out if NaN is involved.
3998 __ j(not_equal, not_found); // The cache did not contain this value.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003999 __ jmp(&load_result_from_cache, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004000 }
4001
4002 __ bind(&smi_hash_calculated);
4003 // Object is smi and hash is now in scratch. Calculate cache index.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004004 __ and_(scratch, mask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004005 Register index = scratch;
4006 // Check if the entry is the smi we are looking for.
4007 __ cmp(object,
4008 FieldOperand(number_string_cache,
4009 index,
4010 times_twice_pointer_size,
4011 FixedArray::kHeaderSize));
4012 __ j(not_equal, not_found);
4013
4014 // Get the result from the cache.
4015 __ bind(&load_result_from_cache);
4016 __ mov(result,
4017 FieldOperand(number_string_cache,
4018 index,
4019 times_twice_pointer_size,
4020 FixedArray::kHeaderSize + kPointerSize));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004021 Counters* counters = masm->isolate()->counters();
4022 __ IncrementCounter(counters->number_to_string_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004023}
4024
4025
4026void NumberToStringStub::Generate(MacroAssembler* masm) {
4027 Label runtime;
4028
4029 __ mov(ebx, Operand(esp, kPointerSize));
4030
4031 // Generate code to lookup number in the number string cache.
4032 GenerateLookupNumberStringCache(masm, ebx, eax, ecx, edx, false, &runtime);
4033 __ ret(1 * kPointerSize);
4034
4035 __ bind(&runtime);
4036 // Handle number to string in the runtime system if not found in the cache.
4037 __ TailCallRuntime(Runtime::kNumberToStringSkipCache, 1, 1);
4038}
4039
4040
4041static int NegativeComparisonResult(Condition cc) {
4042 ASSERT(cc != equal);
4043 ASSERT((cc == less) || (cc == less_equal)
4044 || (cc == greater) || (cc == greater_equal));
4045 return (cc == greater || cc == greater_equal) ? LESS : GREATER;
4046}
4047
ricow@chromium.org65fae842010-08-25 15:26:24 +00004048
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004049static void CheckInputType(MacroAssembler* masm,
4050 Register input,
4051 CompareIC::State expected,
4052 Label* fail) {
4053 Label ok;
4054 if (expected == CompareIC::SMI) {
4055 __ JumpIfNotSmi(input, fail);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004056 } else if (expected == CompareIC::NUMBER) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004057 __ JumpIfSmi(input, &ok);
4058 __ cmp(FieldOperand(input, HeapObject::kMapOffset),
4059 Immediate(masm->isolate()->factory()->heap_number_map()));
4060 __ j(not_equal, fail);
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00004061 }
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004062 // We could be strict about internalized/non-internalized here, but as long as
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004063 // hydrogen doesn't care, the stub doesn't have to care either.
4064 __ bind(&ok);
4065}
4066
4067
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004068static void BranchIfNotInternalizedString(MacroAssembler* masm,
4069 Label* label,
4070 Register object,
4071 Register scratch) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004072 __ JumpIfSmi(object, label);
4073 __ mov(scratch, FieldOperand(object, HeapObject::kMapOffset));
4074 __ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004075 __ and_(scratch, kIsInternalizedMask | kIsNotStringMask);
4076 __ cmp(scratch, kInternalizedTag | kStringTag);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004077 __ j(not_equal, label);
4078}
4079
4080
4081void ICCompareStub::GenerateGeneric(MacroAssembler* masm) {
4082 Label check_unequal_objects;
4083 Condition cc = GetCondition();
4084
4085 Label miss;
4086 CheckInputType(masm, edx, left_, &miss);
4087 CheckInputType(masm, eax, right_, &miss);
4088
4089 // Compare two smis.
4090 Label non_smi, smi_done;
4091 __ mov(ecx, edx);
4092 __ or_(ecx, eax);
4093 __ JumpIfNotSmi(ecx, &non_smi, Label::kNear);
4094 __ sub(edx, eax); // Return on the result of the subtraction.
4095 __ j(no_overflow, &smi_done, Label::kNear);
4096 __ not_(edx); // Correct sign in case of overflow. edx is never 0 here.
4097 __ bind(&smi_done);
4098 __ mov(eax, edx);
4099 __ ret(0);
4100 __ bind(&non_smi);
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00004101
ricow@chromium.org65fae842010-08-25 15:26:24 +00004102 // NOTICE! This code is only reached after a smi-fast-case check, so
4103 // it is certain that at least one operand isn't a smi.
4104
4105 // Identical objects can be compared fast, but there are some tricky cases
4106 // for NaN and undefined.
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00004107 Label generic_heap_number_comparison;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004108 {
4109 Label not_identical;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004110 __ cmp(eax, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004111 __ j(not_equal, &not_identical);
4112
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004113 if (cc != equal) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00004114 // Check for undefined. undefined OP undefined is false even though
4115 // undefined == undefined.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004116 Label check_for_nan;
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004117 __ cmp(edx, masm->isolate()->factory()->undefined_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004118 __ j(not_equal, &check_for_nan, Label::kNear);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004119 __ Set(eax, Immediate(Smi::FromInt(NegativeComparisonResult(cc))));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004120 __ ret(0);
4121 __ bind(&check_for_nan);
4122 }
4123
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00004124 // Test for NaN. Compare heap numbers in a general way,
4125 // to hanlde NaNs correctly.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004126 __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
4127 Immediate(masm->isolate()->factory()->heap_number_map()));
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00004128 __ j(equal, &generic_heap_number_comparison, Label::kNear);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004129 if (cc != equal) {
4130 // Call runtime on identical JSObjects. Otherwise return equal.
4131 __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
4132 __ j(above_equal, &not_identical);
4133 }
4134 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
4135 __ ret(0);
4136
ricow@chromium.org65fae842010-08-25 15:26:24 +00004137
4138 __ bind(&not_identical);
4139 }
4140
4141 // Strict equality can quickly decide whether objects are equal.
4142 // Non-strict object equality is slower, so it is handled later in the stub.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004143 if (cc == equal && strict()) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00004144 Label slow; // Fallthrough label.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004145 Label not_smis;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004146 // If we're doing a strict equality comparison, we don't have to do
4147 // type conversion, so we generate code to do fast comparison for objects
4148 // and oddballs. Non-smi numbers and strings still go through the usual
4149 // slow-case code.
4150 // If either is a Smi (we know that not both are), then they can only
4151 // be equal if the other is a HeapNumber. If so, use the slow case.
4152 STATIC_ASSERT(kSmiTag == 0);
4153 ASSERT_EQ(0, Smi::FromInt(0));
4154 __ mov(ecx, Immediate(kSmiTagMask));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004155 __ and_(ecx, eax);
4156 __ test(ecx, edx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004157 __ j(not_zero, &not_smis, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004158 // One operand is a smi.
4159
4160 // Check whether the non-smi is a heap number.
4161 STATIC_ASSERT(kSmiTagMask == 1);
4162 // ecx still holds eax & kSmiTag, which is either zero or one.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004163 __ sub(ecx, Immediate(0x01));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004164 __ mov(ebx, edx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004165 __ xor_(ebx, eax);
4166 __ and_(ebx, ecx); // ebx holds either 0 or eax ^ edx.
4167 __ xor_(ebx, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004168 // if eax was smi, ebx is now edx, else eax.
4169
4170 // Check if the non-smi operand is a heap number.
4171 __ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004172 Immediate(masm->isolate()->factory()->heap_number_map()));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004173 // If heap number, handle it in the slow case.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004174 __ j(equal, &slow, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004175 // Return non-equal (ebx is not zero)
4176 __ mov(eax, ebx);
4177 __ ret(0);
4178
4179 __ bind(&not_smis);
4180 // If either operand is a JSObject or an oddball value, then they are not
4181 // equal since their pointers are different
4182 // There is no test for undetectability in strict equality.
4183
4184 // Get the type of the first operand.
4185 // If the first object is a JS object, we have done pointer comparison.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004186 Label first_non_object;
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004187 STATIC_ASSERT(LAST_TYPE == LAST_SPEC_OBJECT_TYPE);
4188 __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004189 __ j(below, &first_non_object, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004190
4191 // Return non-zero (eax is not zero)
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004192 Label return_not_equal;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004193 STATIC_ASSERT(kHeapObjectTag != 0);
4194 __ bind(&return_not_equal);
4195 __ ret(0);
4196
4197 __ bind(&first_non_object);
4198 // Check for oddballs: true, false, null, undefined.
4199 __ CmpInstanceType(ecx, ODDBALL_TYPE);
4200 __ j(equal, &return_not_equal);
4201
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004202 __ CmpObjectType(edx, FIRST_SPEC_OBJECT_TYPE, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004203 __ j(above_equal, &return_not_equal);
4204
4205 // Check for oddballs: true, false, null, undefined.
4206 __ CmpInstanceType(ecx, ODDBALL_TYPE);
4207 __ j(equal, &return_not_equal);
4208
4209 // Fall through to the general case.
4210 __ bind(&slow);
4211 }
4212
4213 // Generate the number comparison code.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004214 Label non_number_comparison;
4215 Label unordered;
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00004216 __ bind(&generic_heap_number_comparison);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004217 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00004218 CpuFeatureScope use_sse2(masm, SSE2);
4219 CpuFeatureScope use_cmov(masm, CMOV);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004220
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004221 FloatingPointHelper::LoadSSE2Operands(masm, &non_number_comparison);
4222 __ ucomisd(xmm0, xmm1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004223
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004224 // Don't base result on EFLAGS when a NaN is involved.
4225 __ j(parity_even, &unordered, Label::kNear);
4226 // Return a result of -1, 0, or 1, based on EFLAGS.
4227 __ mov(eax, 0); // equal
4228 __ mov(ecx, Immediate(Smi::FromInt(1)));
4229 __ cmov(above, eax, ecx);
4230 __ mov(ecx, Immediate(Smi::FromInt(-1)));
4231 __ cmov(below, eax, ecx);
4232 __ ret(0);
4233 } else {
4234 FloatingPointHelper::CheckFloatOperands(
4235 masm, &non_number_comparison, ebx);
4236 FloatingPointHelper::LoadFloatOperand(masm, eax);
4237 FloatingPointHelper::LoadFloatOperand(masm, edx);
4238 __ FCmp();
ricow@chromium.org65fae842010-08-25 15:26:24 +00004239
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004240 // Don't base result on EFLAGS when a NaN is involved.
4241 __ j(parity_even, &unordered, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004242
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004243 Label below_label, above_label;
4244 // Return a result of -1, 0, or 1, based on EFLAGS.
4245 __ j(below, &below_label, Label::kNear);
4246 __ j(above, &above_label, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004247
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004248 __ Set(eax, Immediate(0));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004249 __ ret(0);
4250
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004251 __ bind(&below_label);
4252 __ mov(eax, Immediate(Smi::FromInt(-1)));
4253 __ ret(0);
4254
4255 __ bind(&above_label);
4256 __ mov(eax, Immediate(Smi::FromInt(1)));
4257 __ ret(0);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004258 }
4259
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004260 // If one of the numbers was NaN, then the result is always false.
4261 // The cc is never not-equal.
4262 __ bind(&unordered);
4263 ASSERT(cc != not_equal);
4264 if (cc == less || cc == less_equal) {
4265 __ mov(eax, Immediate(Smi::FromInt(1)));
4266 } else {
4267 __ mov(eax, Immediate(Smi::FromInt(-1)));
4268 }
4269 __ ret(0);
4270
4271 // The number comparison code did not provide a valid result.
4272 __ bind(&non_number_comparison);
4273
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004274 // Fast negative check for internalized-to-internalized equality.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004275 Label check_for_strings;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004276 if (cc == equal) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004277 BranchIfNotInternalizedString(masm, &check_for_strings, eax, ecx);
4278 BranchIfNotInternalizedString(masm, &check_for_strings, edx, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004279
4280 // We've already checked for object identity, so if both operands
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004281 // are internalized they aren't equal. Register eax already holds a
ricow@chromium.org65fae842010-08-25 15:26:24 +00004282 // non-zero value, which indicates not equal, so just return.
4283 __ ret(0);
4284 }
4285
4286 __ bind(&check_for_strings);
4287
4288 __ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx,
4289 &check_unequal_objects);
4290
ulan@chromium.org2efb9002012-01-19 15:36:35 +00004291 // Inline comparison of ASCII strings.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004292 if (cc == equal) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00004293 StringCompareStub::GenerateFlatAsciiStringEquals(masm,
ricow@chromium.org65fae842010-08-25 15:26:24 +00004294 edx,
4295 eax,
4296 ecx,
lrn@chromium.org1c092762011-05-09 09:42:16 +00004297 ebx);
4298 } else {
4299 StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
4300 edx,
4301 eax,
4302 ecx,
4303 ebx,
4304 edi);
4305 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00004306#ifdef DEBUG
4307 __ Abort("Unexpected fall-through from string comparison");
4308#endif
4309
4310 __ bind(&check_unequal_objects);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004311 if (cc == equal && !strict()) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00004312 // Non-strict equality. Objects are unequal if
4313 // they are both JSObjects and not undetectable,
4314 // and their pointers are different.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004315 Label not_both_objects;
4316 Label return_unequal;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004317 // At most one is a smi, so we can test for smi by adding the two.
4318 // A smi plus a heap object has the low bit set, a heap object plus
4319 // a heap object has the low bit clear.
4320 STATIC_ASSERT(kSmiTag == 0);
4321 STATIC_ASSERT(kSmiTagMask == 1);
4322 __ lea(ecx, Operand(eax, edx, times_1, 0));
4323 __ test(ecx, Immediate(kSmiTagMask));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004324 __ j(not_zero, &not_both_objects, Label::kNear);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004325 __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004326 __ j(below, &not_both_objects, Label::kNear);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004327 __ CmpObjectType(edx, FIRST_SPEC_OBJECT_TYPE, ebx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004328 __ j(below, &not_both_objects, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004329 // We do not bail out after this point. Both are JSObjects, and
4330 // they are equal if and only if both are undetectable.
4331 // The and of the undetectable flags is 1 if and only if they are equal.
4332 __ test_b(FieldOperand(ecx, Map::kBitFieldOffset),
4333 1 << Map::kIsUndetectable);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004334 __ j(zero, &return_unequal, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004335 __ test_b(FieldOperand(ebx, Map::kBitFieldOffset),
4336 1 << Map::kIsUndetectable);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004337 __ j(zero, &return_unequal, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004338 // The objects are both undetectable, so they both compare as the value
4339 // undefined, and are equal.
4340 __ Set(eax, Immediate(EQUAL));
4341 __ bind(&return_unequal);
4342 // Return non-equal by returning the non-zero object pointer in eax,
4343 // or return equal if we fell through to here.
4344 __ ret(0); // rax, rdx were pushed
4345 __ bind(&not_both_objects);
4346 }
4347
4348 // Push arguments below the return address.
4349 __ pop(ecx);
4350 __ push(edx);
4351 __ push(eax);
4352
4353 // Figure out which native to call and setup the arguments.
4354 Builtins::JavaScript builtin;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004355 if (cc == equal) {
4356 builtin = strict() ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004357 } else {
4358 builtin = Builtins::COMPARE;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004359 __ push(Immediate(Smi::FromInt(NegativeComparisonResult(cc))));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004360 }
4361
4362 // Restore return address on the stack.
4363 __ push(ecx);
4364
4365 // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
4366 // tagged as a small integer.
4367 __ InvokeBuiltin(builtin, JUMP_FUNCTION);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004368
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004369 __ bind(&miss);
4370 GenerateMiss(masm);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004371}
4372
4373
4374void StackCheckStub::Generate(MacroAssembler* masm) {
whesse@chromium.org4a5224e2010-10-20 12:37:07 +00004375 __ TailCallRuntime(Runtime::kStackGuard, 0, 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004376}
4377
4378
yangguo@chromium.org56454712012-02-16 15:33:53 +00004379void InterruptStub::Generate(MacroAssembler* masm) {
4380 __ TailCallRuntime(Runtime::kInterrupt, 0, 1);
4381}
4382
4383
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004384static void GenerateRecordCallTarget(MacroAssembler* masm) {
4385 // Cache the called function in a global property cell. Cache states
4386 // are uninitialized, monomorphic (indicated by a JSFunction), and
4387 // megamorphic.
4388 // ebx : cache cell for call target
4389 // edi : the function to call
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004390 Isolate* isolate = masm->isolate();
4391 Label initialize, done, miss, megamorphic, not_array_function;
4392
4393 // Load the cache state into ecx.
danno@chromium.org41728482013-06-12 22:31:22 +00004394 __ mov(ecx, FieldOperand(ebx, Cell::kValueOffset));
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004395
4396 // A monomorphic cache hit or an already megamorphic state: invoke the
4397 // function without changing the state.
4398 __ cmp(ecx, edi);
4399 __ j(equal, &done);
4400 __ cmp(ecx, Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate)));
4401 __ j(equal, &done);
4402
4403 // Special handling of the Array() function, which caches not only the
4404 // monomorphic Array function but the initial ElementsKind with special
4405 // sentinels
ulan@chromium.org57ff8812013-05-10 08:16:55 +00004406 __ JumpIfNotSmi(ecx, &miss);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00004407 if (FLAG_debug_code) {
4408 Handle<Object> terminal_kind_sentinel =
4409 TypeFeedbackCells::MonomorphicArraySentinel(masm->isolate(),
4410 LAST_FAST_ELEMENTS_KIND);
4411 __ cmp(ecx, Immediate(terminal_kind_sentinel));
4412 __ Assert(less_equal, "Array function sentinel is not an ElementsKind");
4413 }
4414
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004415 // Load the global or builtins object from the current context
4416 __ LoadGlobalContext(ecx);
4417 // Make sure the function is the Array() function
4418 __ cmp(edi, Operand(ecx,
4419 Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX)));
4420 __ j(not_equal, &megamorphic);
4421 __ jmp(&done);
4422
4423 __ bind(&miss);
4424
4425 // A monomorphic miss (i.e, here the cache is not uninitialized) goes
4426 // megamorphic.
4427 __ cmp(ecx, Immediate(TypeFeedbackCells::UninitializedSentinel(isolate)));
4428 __ j(equal, &initialize);
4429 // MegamorphicSentinel is an immortal immovable object (undefined) so no
4430 // write-barrier is needed.
4431 __ bind(&megamorphic);
danno@chromium.org41728482013-06-12 22:31:22 +00004432 __ mov(FieldOperand(ebx, Cell::kValueOffset),
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004433 Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate)));
4434 __ jmp(&done, Label::kNear);
4435
4436 // An uninitialized cache is patched with the function or sentinel to
4437 // indicate the ElementsKind if function is the Array constructor.
4438 __ bind(&initialize);
4439 __ LoadGlobalContext(ecx);
4440 // Make sure the function is the Array() function
4441 __ cmp(edi, Operand(ecx,
4442 Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX)));
4443 __ j(not_equal, &not_array_function);
4444
4445 // The target function is the Array constructor, install a sentinel value in
4446 // the constructor's type info cell that will track the initial ElementsKind
4447 // that should be used for the array when its constructed.
4448 Handle<Object> initial_kind_sentinel =
4449 TypeFeedbackCells::MonomorphicArraySentinel(isolate,
4450 GetInitialFastElementsKind());
danno@chromium.org41728482013-06-12 22:31:22 +00004451 __ mov(FieldOperand(ebx, Cell::kValueOffset),
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004452 Immediate(initial_kind_sentinel));
4453 __ jmp(&done);
4454
4455 __ bind(&not_array_function);
danno@chromium.org41728482013-06-12 22:31:22 +00004456 __ mov(FieldOperand(ebx, Cell::kValueOffset), edi);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004457 // No need for a write barrier here - cells are rescanned.
4458
4459 __ bind(&done);
4460}
4461
4462
ricow@chromium.org65fae842010-08-25 15:26:24 +00004463void CallFunctionStub::Generate(MacroAssembler* masm) {
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004464 // ebx : cache cell for call target
danno@chromium.orgc612e022011-11-10 11:38:15 +00004465 // edi : the function to call
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004466 Isolate* isolate = masm->isolate();
lrn@chromium.org34e60782011-09-15 07:25:40 +00004467 Label slow, non_function;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004468
danno@chromium.org40cb8782011-05-25 07:58:50 +00004469 // The receiver might implicitly be the global object. This is
4470 // indicated by passing the hole as the receiver to the call
4471 // function stub.
4472 if (ReceiverMightBeImplicit()) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004473 Label receiver_ok;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004474 // Get the receiver from the stack.
4475 // +1 ~ return address
ricow@chromium.org65fae842010-08-25 15:26:24 +00004476 __ mov(eax, Operand(esp, (argc_ + 1) * kPointerSize));
danno@chromium.org40cb8782011-05-25 07:58:50 +00004477 // Call as function is indicated with the hole.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004478 __ cmp(eax, isolate->factory()->the_hole_value());
4479 __ j(not_equal, &receiver_ok, Label::kNear);
danno@chromium.org40cb8782011-05-25 07:58:50 +00004480 // Patch the receiver on the stack with the global receiver object.
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004481 __ mov(ecx, GlobalObjectOperand());
4482 __ mov(ecx, FieldOperand(ecx, GlobalObject::kGlobalReceiverOffset));
4483 __ mov(Operand(esp, (argc_ + 1) * kPointerSize), ecx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004484 __ bind(&receiver_ok);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004485 }
4486
ricow@chromium.org65fae842010-08-25 15:26:24 +00004487 // Check that the function really is a JavaScript function.
lrn@chromium.org34e60782011-09-15 07:25:40 +00004488 __ JumpIfSmi(edi, &non_function);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004489 // Goto slow case if we do not have a function.
4490 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00004491 __ j(not_equal, &slow);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004492
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004493 if (RecordCallTarget()) {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00004494 GenerateRecordCallTarget(masm);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004495 }
4496
ricow@chromium.org65fae842010-08-25 15:26:24 +00004497 // Fast-case: Just invoke the function.
4498 ParameterCount actual(argc_);
danno@chromium.org40cb8782011-05-25 07:58:50 +00004499
4500 if (ReceiverMightBeImplicit()) {
4501 Label call_as_function;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004502 __ cmp(eax, isolate->factory()->the_hole_value());
danno@chromium.org40cb8782011-05-25 07:58:50 +00004503 __ j(equal, &call_as_function);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004504 __ InvokeFunction(edi,
4505 actual,
4506 JUMP_FUNCTION,
4507 NullCallWrapper(),
4508 CALL_AS_METHOD);
danno@chromium.org40cb8782011-05-25 07:58:50 +00004509 __ bind(&call_as_function);
4510 }
4511 __ InvokeFunction(edi,
4512 actual,
4513 JUMP_FUNCTION,
4514 NullCallWrapper(),
4515 CALL_AS_FUNCTION);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004516
4517 // Slow-case: Non-function called.
4518 __ bind(&slow);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004519 if (RecordCallTarget()) {
4520 // If there is a call target cache, mark it megamorphic in the
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004521 // non-function case. MegamorphicSentinel is an immortal immovable
4522 // object (undefined) so no write barrier is needed.
danno@chromium.org41728482013-06-12 22:31:22 +00004523 __ mov(FieldOperand(ebx, Cell::kValueOffset),
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004524 Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate)));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004525 }
lrn@chromium.org34e60782011-09-15 07:25:40 +00004526 // Check for function proxy.
4527 __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
4528 __ j(not_equal, &non_function);
4529 __ pop(ecx);
4530 __ push(edi); // put proxy as additional argument under return address
4531 __ push(ecx);
4532 __ Set(eax, Immediate(argc_ + 1));
4533 __ Set(ebx, Immediate(0));
4534 __ SetCallKind(ecx, CALL_AS_FUNCTION);
4535 __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY);
4536 {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004537 Handle<Code> adaptor = isolate->builtins()->ArgumentsAdaptorTrampoline();
lrn@chromium.org34e60782011-09-15 07:25:40 +00004538 __ jmp(adaptor, RelocInfo::CODE_TARGET);
4539 }
4540
ricow@chromium.org65fae842010-08-25 15:26:24 +00004541 // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
4542 // of the original receiver from the call site).
lrn@chromium.org34e60782011-09-15 07:25:40 +00004543 __ bind(&non_function);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004544 __ mov(Operand(esp, (argc_ + 1) * kPointerSize), edi);
4545 __ Set(eax, Immediate(argc_));
4546 __ Set(ebx, Immediate(0));
lrn@chromium.org34e60782011-09-15 07:25:40 +00004547 __ SetCallKind(ecx, CALL_AS_METHOD);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004548 __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004549 Handle<Code> adaptor = isolate->builtins()->ArgumentsAdaptorTrampoline();
ricow@chromium.org65fae842010-08-25 15:26:24 +00004550 __ jmp(adaptor, RelocInfo::CODE_TARGET);
4551}
4552
4553
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004554void CallConstructStub::Generate(MacroAssembler* masm) {
4555 // eax : number of arguments
4556 // ebx : cache cell for call target
4557 // edi : constructor function
4558 Label slow, non_function_call;
4559
4560 // Check that function is not a smi.
4561 __ JumpIfSmi(edi, &non_function_call);
4562 // Check that function is a JSFunction.
4563 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
4564 __ j(not_equal, &slow);
4565
4566 if (RecordCallTarget()) {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00004567 GenerateRecordCallTarget(masm);
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004568 }
4569
4570 // Jump to the function-specific construct stub.
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00004571 Register jmp_reg = ecx;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004572 __ mov(jmp_reg, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
4573 __ mov(jmp_reg, FieldOperand(jmp_reg,
4574 SharedFunctionInfo::kConstructStubOffset));
4575 __ lea(jmp_reg, FieldOperand(jmp_reg, Code::kHeaderSize));
4576 __ jmp(jmp_reg);
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004577
4578 // edi: called object
4579 // eax: number of arguments
4580 // ecx: object map
4581 Label do_call;
4582 __ bind(&slow);
4583 __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
4584 __ j(not_equal, &non_function_call);
4585 __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY_AS_CONSTRUCTOR);
4586 __ jmp(&do_call);
4587
4588 __ bind(&non_function_call);
4589 __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
4590 __ bind(&do_call);
4591 // Set expected number of arguments to zero (not changing eax).
4592 __ Set(ebx, Immediate(0));
4593 Handle<Code> arguments_adaptor =
4594 masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
4595 __ SetCallKind(ecx, CALL_AS_METHOD);
4596 __ jmp(arguments_adaptor, RelocInfo::CODE_TARGET);
4597}
4598
4599
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004600bool CEntryStub::NeedsImmovableCode() {
4601 return false;
4602}
4603
4604
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004605bool CEntryStub::IsPregenerated() {
4606 return (!save_doubles_ || ISOLATE->fp_stubs_generated()) &&
4607 result_size_ == 1;
4608}
4609
4610
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004611void CodeStub::GenerateStubsAheadOfTime(Isolate* isolate) {
4612 CEntryStub::GenerateAheadOfTime(isolate);
4613 StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(isolate);
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00004614 StubFailureTrampolineStub::GenerateAheadOfTime(isolate);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004615 // It is important that the store buffer overflow stubs are generated first.
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004616 RecordWriteStub::GenerateFixedRegStubsAheadOfTime(isolate);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00004617 ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004618}
4619
4620
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004621void CodeStub::GenerateFPStubs(Isolate* isolate) {
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004622 if (CpuFeatures::IsSupported(SSE2)) {
4623 CEntryStub save_doubles(1, kSaveFPRegs);
4624 // Stubs might already be in the snapshot, detect that and don't regenerate,
4625 // which would lead to code stub initialization state being messed up.
4626 Code* save_doubles_code;
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004627 if (!save_doubles.FindCodeInCache(&save_doubles_code, isolate)) {
4628 save_doubles_code = *(save_doubles.GetCode(isolate));
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004629 }
4630 save_doubles_code->set_is_pregenerated(true);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004631 isolate->set_fp_stubs_generated(true);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004632 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004633}
4634
4635
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004636void CEntryStub::GenerateAheadOfTime(Isolate* isolate) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004637 CEntryStub stub(1, kDontSaveFPRegs);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004638 Handle<Code> code = stub.GetCode(isolate);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004639 code->set_is_pregenerated(true);
4640}
4641
4642
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00004643static void JumpIfOOM(MacroAssembler* masm,
4644 Register value,
4645 Register scratch,
4646 Label* oom_label) {
4647 __ mov(scratch, value);
4648 STATIC_ASSERT(Failure::OUT_OF_MEMORY_EXCEPTION == 3);
4649 STATIC_ASSERT(kFailureTag == 3);
4650 __ and_(scratch, 0xf);
4651 __ cmp(scratch, 0xf);
4652 __ j(equal, oom_label);
4653}
4654
4655
ricow@chromium.org65fae842010-08-25 15:26:24 +00004656void CEntryStub::GenerateCore(MacroAssembler* masm,
4657 Label* throw_normal_exception,
4658 Label* throw_termination_exception,
4659 Label* throw_out_of_memory_exception,
4660 bool do_gc,
ager@chromium.org0ee099b2011-01-25 14:06:47 +00004661 bool always_allocate_scope) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00004662 // eax: result parameter for PerformGC, if any
4663 // ebx: pointer to C function (C callee-saved)
4664 // ebp: frame pointer (restored after C call)
4665 // esp: stack pointer (restored after C call)
4666 // edi: number of arguments including receiver (C callee-saved)
4667 // esi: pointer to the first argument (C callee-saved)
4668
4669 // Result returned in eax, or eax+edx if result_size_ is 2.
4670
4671 // Check stack alignment.
4672 if (FLAG_debug_code) {
4673 __ CheckStackAlignment();
4674 }
4675
4676 if (do_gc) {
4677 // Pass failure code returned from last attempt as first argument to
4678 // PerformGC. No need to use PrepareCallCFunction/CallCFunction here as the
4679 // stack alignment is known to be correct. This function takes one argument
4680 // which is passed on the stack, and we know that the stack has been
4681 // prepared to pass at least one argument.
4682 __ mov(Operand(esp, 0 * kPointerSize), eax); // Result.
4683 __ call(FUNCTION_ADDR(Runtime::PerformGC), RelocInfo::RUNTIME_ENTRY);
4684 }
4685
4686 ExternalReference scope_depth =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004687 ExternalReference::heap_always_allocate_scope_depth(masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004688 if (always_allocate_scope) {
4689 __ inc(Operand::StaticVariable(scope_depth));
4690 }
4691
4692 // Call C function.
4693 __ mov(Operand(esp, 0 * kPointerSize), edi); // argc.
4694 __ mov(Operand(esp, 1 * kPointerSize), esi); // argv.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004695 __ mov(Operand(esp, 2 * kPointerSize),
ulan@chromium.org32d7dba2013-04-24 10:59:06 +00004696 Immediate(ExternalReference::isolate_address(masm->isolate())));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004697 __ call(ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004698 // Result is in eax or edx:eax - do not destroy these registers!
4699
4700 if (always_allocate_scope) {
4701 __ dec(Operand::StaticVariable(scope_depth));
4702 }
4703
danno@chromium.orgca29dd82013-04-26 11:59:48 +00004704 // Runtime functions should not return 'the hole'. Allowing it to escape may
4705 // lead to crashes in the IC code later.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004706 if (FLAG_debug_code) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004707 Label okay;
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004708 __ cmp(eax, masm->isolate()->factory()->the_hole_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004709 __ j(not_equal, &okay, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004710 __ int3();
4711 __ bind(&okay);
4712 }
4713
4714 // Check for failure result.
4715 Label failure_returned;
4716 STATIC_ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
4717 __ lea(ecx, Operand(eax, 1));
4718 // Lower 2 bits of ecx are 0 iff eax has failure tag.
4719 __ test(ecx, Immediate(kFailureTagMask));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00004720 __ j(zero, &failure_returned);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004721
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004722 ExternalReference pending_exception_address(
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004723 Isolate::kPendingExceptionAddress, masm->isolate());
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00004724
4725 // Check that there is no pending exception, otherwise we
4726 // should have returned some failure value.
4727 if (FLAG_debug_code) {
4728 __ push(edx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004729 __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004730 Label okay;
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00004731 __ cmp(edx, Operand::StaticVariable(pending_exception_address));
4732 // Cannot use check here as it attempts to generate call into runtime.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004733 __ j(equal, &okay, Label::kNear);
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00004734 __ int3();
4735 __ bind(&okay);
4736 __ pop(edx);
4737 }
4738
ricow@chromium.org65fae842010-08-25 15:26:24 +00004739 // Exit the JavaScript to C++ exit frame.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004740 __ LeaveExitFrame(save_doubles_ == kSaveFPRegs);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004741 __ ret(0);
4742
4743 // Handling of failure.
4744 __ bind(&failure_returned);
4745
4746 Label retry;
4747 // If the returned exception is RETRY_AFTER_GC continue at retry label
4748 STATIC_ASSERT(Failure::RETRY_AFTER_GC == 0);
4749 __ test(eax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004750 __ j(zero, &retry, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004751
4752 // Special handling of out of memory exceptions.
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00004753 JumpIfOOM(masm, eax, ecx, throw_out_of_memory_exception);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004754
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00004755 // Retrieve the pending exception.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004756 __ mov(eax, Operand::StaticVariable(pending_exception_address));
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00004757
4758 // See if we just retrieved an OOM exception.
4759 JumpIfOOM(masm, eax, ecx, throw_out_of_memory_exception);
4760
4761 // Clear the pending exception.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004762 __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004763 __ mov(Operand::StaticVariable(pending_exception_address), edx);
4764
4765 // Special handling of termination exceptions which are uncatchable
4766 // by javascript code.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004767 __ cmp(eax, masm->isolate()->factory()->termination_exception());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004768 __ j(equal, throw_termination_exception);
4769
4770 // Handle normal exception.
4771 __ jmp(throw_normal_exception);
4772
4773 // Retry.
4774 __ bind(&retry);
4775}
4776
4777
ricow@chromium.org65fae842010-08-25 15:26:24 +00004778void CEntryStub::Generate(MacroAssembler* masm) {
4779 // eax: number of arguments including receiver
4780 // ebx: pointer to C function (C callee-saved)
4781 // ebp: frame pointer (restored after C call)
4782 // esp: stack pointer (restored after C call)
4783 // esi: current context (C callee-saved)
4784 // edi: JS function of the caller (C callee-saved)
4785
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00004786 ProfileEntryHookStub::MaybeCallEntryHook(masm);
4787
ricow@chromium.org65fae842010-08-25 15:26:24 +00004788 // NOTE: Invocations of builtins may return failure objects instead
4789 // of a proper result. The builtin entry handles this by performing
4790 // a garbage collection and retrying the builtin (twice).
4791
4792 // Enter the exit frame that transitions from JavaScript to C++.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004793 __ EnterExitFrame(save_doubles_ == kSaveFPRegs);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004794
4795 // eax: result parameter for PerformGC, if any (setup below)
4796 // ebx: pointer to builtin function (C callee-saved)
4797 // ebp: frame pointer (restored after C call)
4798 // esp: stack pointer (restored after C call)
4799 // edi: number of arguments including receiver (C callee-saved)
4800 // esi: argv pointer (C callee-saved)
4801
4802 Label throw_normal_exception;
4803 Label throw_termination_exception;
4804 Label throw_out_of_memory_exception;
4805
4806 // Call into the runtime system.
4807 GenerateCore(masm,
4808 &throw_normal_exception,
4809 &throw_termination_exception,
4810 &throw_out_of_memory_exception,
4811 false,
4812 false);
4813
4814 // Do space-specific GC and retry runtime call.
4815 GenerateCore(masm,
4816 &throw_normal_exception,
4817 &throw_termination_exception,
4818 &throw_out_of_memory_exception,
4819 true,
4820 false);
4821
4822 // Do full GC and retry runtime call one final time.
4823 Failure* failure = Failure::InternalError();
4824 __ mov(eax, Immediate(reinterpret_cast<int32_t>(failure)));
4825 GenerateCore(masm,
4826 &throw_normal_exception,
4827 &throw_termination_exception,
4828 &throw_out_of_memory_exception,
4829 true,
4830 true);
4831
4832 __ bind(&throw_out_of_memory_exception);
ulan@chromium.org65a89c22012-02-14 11:46:07 +00004833 // Set external caught exception to false.
4834 Isolate* isolate = masm->isolate();
4835 ExternalReference external_caught(Isolate::kExternalCaughtExceptionAddress,
4836 isolate);
4837 __ mov(Operand::StaticVariable(external_caught), Immediate(false));
4838
4839 // Set pending exception and eax to out of memory exception.
4840 ExternalReference pending_exception(Isolate::kPendingExceptionAddress,
4841 isolate);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00004842 Label already_have_failure;
4843 JumpIfOOM(masm, eax, ecx, &already_have_failure);
4844 __ mov(eax, reinterpret_cast<int32_t>(Failure::OutOfMemoryException(0x1)));
4845 __ bind(&already_have_failure);
ulan@chromium.org65a89c22012-02-14 11:46:07 +00004846 __ mov(Operand::StaticVariable(pending_exception), eax);
4847 // Fall through to the next label.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004848
4849 __ bind(&throw_termination_exception);
ulan@chromium.org65a89c22012-02-14 11:46:07 +00004850 __ ThrowUncatchable(eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004851
4852 __ bind(&throw_normal_exception);
ulan@chromium.org65a89c22012-02-14 11:46:07 +00004853 __ Throw(eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004854}
4855
4856
4857void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00004858 Label invoke, handler_entry, exit;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004859 Label not_outermost_js, not_outermost_js_2;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004860
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00004861 ProfileEntryHookStub::MaybeCallEntryHook(masm);
4862
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00004863 // Set up frame.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004864 __ push(ebp);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004865 __ mov(ebp, esp);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004866
4867 // Push marker in two places.
4868 int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
4869 __ push(Immediate(Smi::FromInt(marker))); // context slot
4870 __ push(Immediate(Smi::FromInt(marker))); // function slot
4871 // Save callee-saved registers (C calling conventions).
4872 __ push(edi);
4873 __ push(esi);
4874 __ push(ebx);
4875
4876 // Save copies of the top frame descriptor on the stack.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004877 ExternalReference c_entry_fp(Isolate::kCEntryFPAddress, masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004878 __ push(Operand::StaticVariable(c_entry_fp));
4879
ricow@chromium.org65fae842010-08-25 15:26:24 +00004880 // If this is the outermost JS call, set js_entry_sp value.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004881 ExternalReference js_entry_sp(Isolate::kJSEntrySPAddress,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004882 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004883 __ cmp(Operand::StaticVariable(js_entry_sp), Immediate(0));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004884 __ j(not_equal, &not_outermost_js, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004885 __ mov(Operand::StaticVariable(js_entry_sp), ebp);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004886 __ push(Immediate(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME)));
danno@chromium.org2c26cb12012-05-03 09:06:43 +00004887 __ jmp(&invoke, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004888 __ bind(&not_outermost_js);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004889 __ push(Immediate(Smi::FromInt(StackFrame::INNER_JSENTRY_FRAME)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004890
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00004891 // Jump to a faked try block that does the invoke, with a faked catch
4892 // block that sets the pending exception.
4893 __ jmp(&invoke);
4894 __ bind(&handler_entry);
4895 handler_offset_ = handler_entry.pos();
4896 // Caught exception: Store result (exception) in the pending exception
4897 // field in the JSEnv and return a failure sentinel.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004898 ExternalReference pending_exception(Isolate::kPendingExceptionAddress,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004899 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004900 __ mov(Operand::StaticVariable(pending_exception), eax);
4901 __ mov(eax, reinterpret_cast<int32_t>(Failure::Exception()));
4902 __ jmp(&exit);
4903
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00004904 // Invoke: Link this frame into the handler chain. There's only one
4905 // handler block in this code object, so its index is 0.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004906 __ bind(&invoke);
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00004907 __ PushTryHandler(StackHandler::JS_ENTRY, 0);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004908
4909 // Clear any pending exceptions.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004910 __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004911 __ mov(Operand::StaticVariable(pending_exception), edx);
4912
4913 // Fake a receiver (NULL).
4914 __ push(Immediate(0)); // receiver
4915
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00004916 // Invoke the function by calling through JS entry trampoline builtin and
4917 // pop the faked function when we return. Notice that we cannot store a
4918 // reference to the trampoline code directly in this stub, because the
4919 // builtin stubs may not have been generated yet.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004920 if (is_construct) {
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00004921 ExternalReference construct_entry(Builtins::kJSConstructEntryTrampoline,
4922 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004923 __ mov(edx, Immediate(construct_entry));
4924 } else {
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004925 ExternalReference entry(Builtins::kJSEntryTrampoline,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004926 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004927 __ mov(edx, Immediate(entry));
4928 }
4929 __ mov(edx, Operand(edx, 0)); // deref address
4930 __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004931 __ call(edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004932
4933 // Unlink this frame from the handler chain.
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004934 __ PopTryHandler();
ricow@chromium.org65fae842010-08-25 15:26:24 +00004935
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004936 __ bind(&exit);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004937 // Check if the current stack frame is marked as the outermost JS frame.
4938 __ pop(ebx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004939 __ cmp(ebx, Immediate(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004940 __ j(not_equal, &not_outermost_js_2);
4941 __ mov(Operand::StaticVariable(js_entry_sp), Immediate(0));
4942 __ bind(&not_outermost_js_2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004943
4944 // Restore the top frame descriptor from the stack.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004945 __ pop(Operand::StaticVariable(ExternalReference(
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004946 Isolate::kCEntryFPAddress,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004947 masm->isolate())));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004948
4949 // Restore callee-saved registers (C calling conventions).
4950 __ pop(ebx);
4951 __ pop(esi);
4952 __ pop(edi);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004953 __ add(esp, Immediate(2 * kPointerSize)); // remove markers
ricow@chromium.org65fae842010-08-25 15:26:24 +00004954
4955 // Restore frame pointer and return.
4956 __ pop(ebp);
4957 __ ret(0);
4958}
4959
4960
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004961// Generate stub code for instanceof.
4962// This code can patch a call site inlined cache of the instance of check,
4963// which looks like this.
4964//
4965// 81 ff XX XX XX XX cmp edi, <the hole, patched to a map>
4966// 75 0a jne <some near label>
4967// b8 XX XX XX XX mov eax, <the hole, patched to either true or false>
4968//
4969// If call site patching is requested the stack will have the delta from the
4970// return address to the cmp instruction just below the return address. This
4971// also means that call site patching can only take place with arguments in
4972// registers. TOS looks like this when call site patching is requested
4973//
4974// esp[0] : return address
4975// esp[4] : delta from return address to cmp instruction
4976//
ricow@chromium.org65fae842010-08-25 15:26:24 +00004977void InstanceofStub::Generate(MacroAssembler* masm) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004978 // Call site inlining and patching implies arguments in registers.
4979 ASSERT(HasArgsInRegisters() || !HasCallSiteInlineCheck());
4980
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004981 // Fixed register usage throughout the stub.
4982 Register object = eax; // Object (lhs).
4983 Register map = ebx; // Map of the object.
4984 Register function = edx; // Function (rhs).
4985 Register prototype = edi; // Prototype of the function.
4986 Register scratch = ecx;
4987
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004988 // Constants describing the call site code to patch.
4989 static const int kDeltaToCmpImmediate = 2;
4990 static const int kDeltaToMov = 8;
4991 static const int kDeltaToMovImmediate = 9;
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00004992 static const int8_t kCmpEdiOperandByte1 = BitCast<int8_t, uint8_t>(0x3b);
4993 static const int8_t kCmpEdiOperandByte2 = BitCast<int8_t, uint8_t>(0x3d);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004994 static const int8_t kMovEaxImmediateByte = BitCast<int8_t, uint8_t>(0xb8);
4995
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004996 ExternalReference roots_array_start =
4997 ExternalReference::roots_array_start(masm->isolate());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004998
4999 ASSERT_EQ(object.code(), InstanceofStub::left().code());
5000 ASSERT_EQ(function.code(), InstanceofStub::right().code());
5001
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005002 // Get the object and function - they are always both needed.
5003 Label slow, not_js_object;
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005004 if (!HasArgsInRegisters()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005005 __ mov(object, Operand(esp, 2 * kPointerSize));
5006 __ mov(function, Operand(esp, 1 * kPointerSize));
5007 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00005008
5009 // Check that the left hand is a JS object.
whesse@chromium.org7b260152011-06-20 15:33:18 +00005010 __ JumpIfSmi(object, &not_js_object);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005011 __ IsObjectJSObjectType(object, map, scratch, &not_js_object);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005012
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005013 // If there is a call site cache don't look in the global cache, but do the
5014 // real lookup and update the call site cache.
5015 if (!HasCallSiteInlineCheck()) {
5016 // Look up the function and the map in the instanceof cache.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005017 Label miss;
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005018 __ mov(scratch, Immediate(Heap::kInstanceofCacheFunctionRootIndex));
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005019 __ cmp(function, Operand::StaticArray(scratch,
5020 times_pointer_size,
5021 roots_array_start));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005022 __ j(not_equal, &miss, Label::kNear);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005023 __ mov(scratch, Immediate(Heap::kInstanceofCacheMapRootIndex));
5024 __ cmp(map, Operand::StaticArray(
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005025 scratch, times_pointer_size, roots_array_start));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005026 __ j(not_equal, &miss, Label::kNear);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005027 __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
5028 __ mov(eax, Operand::StaticArray(
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005029 scratch, times_pointer_size, roots_array_start));
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005030 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
5031 __ bind(&miss);
5032 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00005033
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005034 // Get the prototype of the function.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005035 __ TryGetFunctionPrototype(function, prototype, scratch, &slow, true);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005036
5037 // Check that the function prototype is a JS object.
whesse@chromium.org7b260152011-06-20 15:33:18 +00005038 __ JumpIfSmi(prototype, &slow);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005039 __ IsObjectJSObjectType(prototype, scratch, scratch, &slow);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005040
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005041 // Update the global instanceof or call site inlined cache with the current
5042 // map and function. The cached answer will be set when it is known below.
5043 if (!HasCallSiteInlineCheck()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005044 __ mov(scratch, Immediate(Heap::kInstanceofCacheMapRootIndex));
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005045 __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_array_start),
5046 map);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005047 __ mov(scratch, Immediate(Heap::kInstanceofCacheFunctionRootIndex));
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005048 __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_array_start),
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005049 function);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005050 } else {
5051 // The constants for the code patching are based on no push instructions
5052 // at the call site.
5053 ASSERT(HasArgsInRegisters());
5054 // Get return address and delta to inlined map check.
5055 __ mov(scratch, Operand(esp, 0 * kPointerSize));
5056 __ sub(scratch, Operand(esp, 1 * kPointerSize));
5057 if (FLAG_debug_code) {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00005058 __ cmpb(Operand(scratch, 0), kCmpEdiOperandByte1);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005059 __ Assert(equal, "InstanceofStub unexpected call site cache (cmp 1)");
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00005060 __ cmpb(Operand(scratch, 1), kCmpEdiOperandByte2);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005061 __ Assert(equal, "InstanceofStub unexpected call site cache (cmp 2)");
5062 }
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00005063 __ mov(scratch, Operand(scratch, kDeltaToCmpImmediate));
5064 __ mov(Operand(scratch, 0), map);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005065 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00005066
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005067 // Loop through the prototype chain of the object looking for the function
5068 // prototype.
5069 __ mov(scratch, FieldOperand(map, Map::kPrototypeOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005070 Label loop, is_instance, is_not_instance;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005071 __ bind(&loop);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005072 __ cmp(scratch, prototype);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005073 __ j(equal, &is_instance, Label::kNear);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005074 Factory* factory = masm->isolate()->factory();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005075 __ cmp(scratch, Immediate(factory->null_value()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005076 __ j(equal, &is_not_instance, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005077 __ mov(scratch, FieldOperand(scratch, HeapObject::kMapOffset));
5078 __ mov(scratch, FieldOperand(scratch, Map::kPrototypeOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005079 __ jmp(&loop);
5080
5081 __ bind(&is_instance);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005082 if (!HasCallSiteInlineCheck()) {
5083 __ Set(eax, Immediate(0));
5084 __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
5085 __ mov(Operand::StaticArray(scratch,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005086 times_pointer_size, roots_array_start), eax);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005087 } else {
5088 // Get return address and delta to inlined map check.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005089 __ mov(eax, factory->true_value());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005090 __ mov(scratch, Operand(esp, 0 * kPointerSize));
5091 __ sub(scratch, Operand(esp, 1 * kPointerSize));
5092 if (FLAG_debug_code) {
5093 __ cmpb(Operand(scratch, kDeltaToMov), kMovEaxImmediateByte);
5094 __ Assert(equal, "InstanceofStub unexpected call site cache (mov)");
5095 }
5096 __ mov(Operand(scratch, kDeltaToMovImmediate), eax);
5097 if (!ReturnTrueFalseObject()) {
5098 __ Set(eax, Immediate(0));
5099 }
5100 }
5101 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005102
5103 __ bind(&is_not_instance);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005104 if (!HasCallSiteInlineCheck()) {
5105 __ Set(eax, Immediate(Smi::FromInt(1)));
5106 __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
5107 __ mov(Operand::StaticArray(
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005108 scratch, times_pointer_size, roots_array_start), eax);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005109 } else {
5110 // Get return address and delta to inlined map check.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005111 __ mov(eax, factory->false_value());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005112 __ mov(scratch, Operand(esp, 0 * kPointerSize));
5113 __ sub(scratch, Operand(esp, 1 * kPointerSize));
5114 if (FLAG_debug_code) {
5115 __ cmpb(Operand(scratch, kDeltaToMov), kMovEaxImmediateByte);
5116 __ Assert(equal, "InstanceofStub unexpected call site cache (mov)");
5117 }
5118 __ mov(Operand(scratch, kDeltaToMovImmediate), eax);
5119 if (!ReturnTrueFalseObject()) {
5120 __ Set(eax, Immediate(Smi::FromInt(1)));
5121 }
5122 }
5123 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005124
5125 Label object_not_null, object_not_null_or_smi;
5126 __ bind(&not_js_object);
5127 // Before null, smi and string value checks, check that the rhs is a function
5128 // as for a non-function rhs an exception needs to be thrown.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005129 __ JumpIfSmi(function, &slow, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005130 __ CmpObjectType(function, JS_FUNCTION_TYPE, scratch);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005131 __ j(not_equal, &slow, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005132
5133 // Null is not instance of anything.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005134 __ cmp(object, factory->null_value());
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005135 __ j(not_equal, &object_not_null, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005136 __ Set(eax, Immediate(Smi::FromInt(1)));
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005137 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005138
5139 __ bind(&object_not_null);
5140 // Smi values is not instance of anything.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005141 __ JumpIfNotSmi(object, &object_not_null_or_smi, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005142 __ Set(eax, Immediate(Smi::FromInt(1)));
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005143 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005144
5145 __ bind(&object_not_null_or_smi);
5146 // String values is not instance of anything.
5147 Condition is_string = masm->IsObjectStringType(object, scratch, scratch);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00005148 __ j(NegateCondition(is_string), &slow, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005149 __ Set(eax, Immediate(Smi::FromInt(1)));
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005150 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005151
5152 // Slow-case: Go through the JavaScript implementation.
5153 __ bind(&slow);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005154 if (!ReturnTrueFalseObject()) {
5155 // Tail call the builtin which returns 0 or 1.
5156 if (HasArgsInRegisters()) {
5157 // Push arguments below return address.
5158 __ pop(scratch);
5159 __ push(object);
5160 __ push(function);
5161 __ push(scratch);
5162 }
5163 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
5164 } else {
5165 // Call the builtin and convert 0/1 to true/false.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005166 {
5167 FrameScope scope(masm, StackFrame::INTERNAL);
5168 __ push(object);
5169 __ push(function);
5170 __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION);
5171 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005172 Label true_value, done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005173 __ test(eax, eax);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005174 __ j(zero, &true_value, Label::kNear);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005175 __ mov(eax, factory->false_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005176 __ jmp(&done, Label::kNear);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005177 __ bind(&true_value);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005178 __ mov(eax, factory->true_value());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005179 __ bind(&done);
5180 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005181 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00005182}
5183
5184
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005185Register InstanceofStub::left() { return eax; }
5186
5187
5188Register InstanceofStub::right() { return edx; }
5189
5190
ricow@chromium.org65fae842010-08-25 15:26:24 +00005191// -------------------------------------------------------------------------
5192// StringCharCodeAtGenerator
5193
5194void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00005195 // If the receiver is a smi trigger the non-string case.
5196 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005197 __ JumpIfSmi(object_, receiver_not_string_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005198
5199 // Fetch the instance type of the receiver into result register.
5200 __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset));
5201 __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
5202 // If the receiver is not a string trigger the non-string case.
5203 __ test(result_, Immediate(kIsNotStringMask));
5204 __ j(not_zero, receiver_not_string_);
5205
5206 // If the index is non-smi trigger the non-smi case.
5207 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005208 __ JumpIfNotSmi(index_, &index_not_smi_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005209 __ bind(&got_smi_index_);
5210
5211 // Check for index out of range.
danno@chromium.orgc612e022011-11-10 11:38:15 +00005212 __ cmp(index_, FieldOperand(object_, String::kLengthOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005213 __ j(above_equal, index_out_of_range_);
5214
danno@chromium.orgc612e022011-11-10 11:38:15 +00005215 __ SmiUntag(index_);
erikcorry0ad885c2011-11-21 13:51:57 +00005216
5217 Factory* factory = masm->isolate()->factory();
5218 StringCharLoadGenerator::Generate(
5219 masm, factory, object_, index_, result_, &call_runtime_);
5220
ricow@chromium.org65fae842010-08-25 15:26:24 +00005221 __ SmiTag(result_);
5222 __ bind(&exit_);
5223}
5224
5225
5226void StringCharCodeAtGenerator::GenerateSlow(
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005227 MacroAssembler* masm,
5228 const RuntimeCallHelper& call_helper) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00005229 __ Abort("Unexpected fallthrough to CharCodeAt slow case");
5230
5231 // Index is not a smi.
5232 __ bind(&index_not_smi_);
5233 // If index is a heap number, try converting it to an integer.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005234 __ CheckMap(index_,
5235 masm->isolate()->factory()->heap_number_map(),
5236 index_not_number_,
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005237 DONT_DO_SMI_CHECK);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005238 call_helper.BeforeCall(masm);
5239 __ push(object_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005240 __ push(index_); // Consumed by runtime conversion function.
5241 if (index_flags_ == STRING_INDEX_IS_NUMBER) {
5242 __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1);
5243 } else {
5244 ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
5245 // NumberToSmi discards numbers that are not exact integers.
5246 __ CallRuntime(Runtime::kNumberToSmi, 1);
5247 }
danno@chromium.orgc612e022011-11-10 11:38:15 +00005248 if (!index_.is(eax)) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00005249 // Save the conversion result before the pop instructions below
5250 // have a chance to overwrite it.
danno@chromium.orgc612e022011-11-10 11:38:15 +00005251 __ mov(index_, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005252 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00005253 __ pop(object_);
5254 // Reload the instance type.
5255 __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset));
5256 __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
5257 call_helper.AfterCall(masm);
5258 // If index is still not a smi, it must be out of range.
5259 STATIC_ASSERT(kSmiTag == 0);
danno@chromium.orgc612e022011-11-10 11:38:15 +00005260 __ JumpIfNotSmi(index_, index_out_of_range_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005261 // Otherwise, return to the fast path.
5262 __ jmp(&got_smi_index_);
5263
5264 // Call runtime. We get here when the receiver is a string and the
5265 // index is a number, but the code of getting the actual character
5266 // is too complex (e.g., when the string needs to be flattened).
5267 __ bind(&call_runtime_);
5268 call_helper.BeforeCall(masm);
5269 __ push(object_);
erikcorry0ad885c2011-11-21 13:51:57 +00005270 __ SmiTag(index_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005271 __ push(index_);
5272 __ CallRuntime(Runtime::kStringCharCodeAt, 2);
5273 if (!result_.is(eax)) {
5274 __ mov(result_, eax);
5275 }
5276 call_helper.AfterCall(masm);
5277 __ jmp(&exit_);
5278
5279 __ Abort("Unexpected fallthrough from CharCodeAt slow case");
5280}
5281
5282
5283// -------------------------------------------------------------------------
5284// StringCharFromCodeGenerator
5285
5286void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
5287 // Fast case of Heap::LookupSingleCharacterStringFromCode.
5288 STATIC_ASSERT(kSmiTag == 0);
5289 STATIC_ASSERT(kSmiShiftSize == 0);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00005290 ASSERT(IsPowerOf2(String::kMaxOneByteCharCode + 1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005291 __ test(code_,
5292 Immediate(kSmiTagMask |
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00005293 ((~String::kMaxOneByteCharCode) << kSmiTagSize)));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005294 __ j(not_zero, &slow_case_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005295
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005296 Factory* factory = masm->isolate()->factory();
5297 __ Set(result_, Immediate(factory->single_character_string_cache()));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005298 STATIC_ASSERT(kSmiTag == 0);
5299 STATIC_ASSERT(kSmiTagSize == 1);
5300 STATIC_ASSERT(kSmiShiftSize == 0);
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005301 // At this point code register contains smi tagged ASCII char code.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005302 __ mov(result_, FieldOperand(result_,
5303 code_, times_half_pointer_size,
5304 FixedArray::kHeaderSize));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005305 __ cmp(result_, factory->undefined_value());
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005306 __ j(equal, &slow_case_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005307 __ bind(&exit_);
5308}
5309
5310
5311void StringCharFromCodeGenerator::GenerateSlow(
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005312 MacroAssembler* masm,
5313 const RuntimeCallHelper& call_helper) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00005314 __ Abort("Unexpected fallthrough to CharFromCode slow case");
5315
5316 __ bind(&slow_case_);
5317 call_helper.BeforeCall(masm);
5318 __ push(code_);
5319 __ CallRuntime(Runtime::kCharFromCode, 1);
5320 if (!result_.is(eax)) {
5321 __ mov(result_, eax);
5322 }
5323 call_helper.AfterCall(masm);
5324 __ jmp(&exit_);
5325
5326 __ Abort("Unexpected fallthrough from CharFromCode slow case");
5327}
5328
5329
ricow@chromium.org65fae842010-08-25 15:26:24 +00005330void StringAddStub::Generate(MacroAssembler* masm) {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005331 Label call_runtime, call_builtin;
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005332 Builtins::JavaScript builtin_id = Builtins::ADD;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005333
5334 // Load the two arguments.
5335 __ mov(eax, Operand(esp, 2 * kPointerSize)); // First argument.
5336 __ mov(edx, Operand(esp, 1 * kPointerSize)); // Second argument.
5337
5338 // Make sure that both arguments are strings if not known in advance.
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00005339 if ((flags_ & NO_STRING_ADD_FLAGS) != 0) {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005340 __ JumpIfSmi(eax, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005341 __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ebx);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005342 __ j(above_equal, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005343
5344 // First argument is a a string, test second.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005345 __ JumpIfSmi(edx, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005346 __ CmpObjectType(edx, FIRST_NONSTRING_TYPE, ebx);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005347 __ j(above_equal, &call_runtime);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005348 } else {
5349 // Here at least one of the arguments is definitely a string.
5350 // We convert the one that is not known to be a string.
5351 if ((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) == 0) {
5352 ASSERT((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) != 0);
5353 GenerateConvertArgument(masm, 2 * kPointerSize, eax, ebx, ecx, edi,
5354 &call_builtin);
5355 builtin_id = Builtins::STRING_ADD_RIGHT;
5356 } else if ((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) == 0) {
5357 ASSERT((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) != 0);
5358 GenerateConvertArgument(masm, 1 * kPointerSize, edx, ebx, ecx, edi,
5359 &call_builtin);
5360 builtin_id = Builtins::STRING_ADD_LEFT;
5361 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00005362 }
5363
5364 // Both arguments are strings.
5365 // eax: first string
5366 // edx: second string
5367 // Check if either of the strings are empty. In that case return the other.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005368 Label second_not_zero_length, both_not_zero_length;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005369 __ mov(ecx, FieldOperand(edx, String::kLengthOffset));
5370 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005371 __ test(ecx, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005372 __ j(not_zero, &second_not_zero_length, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005373 // Second string is empty, result is first string which is already in eax.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005374 Counters* counters = masm->isolate()->counters();
5375 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005376 __ ret(2 * kPointerSize);
5377 __ bind(&second_not_zero_length);
5378 __ mov(ebx, FieldOperand(eax, String::kLengthOffset));
5379 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005380 __ test(ebx, ebx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005381 __ j(not_zero, &both_not_zero_length, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005382 // First string is empty, result is second string which is in edx.
5383 __ mov(eax, edx);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005384 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005385 __ ret(2 * kPointerSize);
5386
5387 // Both strings are non-empty.
5388 // eax: first string
5389 // ebx: length of first string as a smi
5390 // ecx: length of second string as a smi
5391 // edx: second string
5392 // Look at the length of the result of adding the two strings.
5393 Label string_add_flat_result, longer_than_two;
5394 __ bind(&both_not_zero_length);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005395 __ add(ebx, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005396 STATIC_ASSERT(Smi::kMaxValue == String::kMaxLength);
5397 // Handle exceptionally long strings in the runtime system.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005398 __ j(overflow, &call_runtime);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005399 // Use the string table when adding two one character strings, as it
5400 // helps later optimizations to return an internalized string here.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005401 __ cmp(ebx, Immediate(Smi::FromInt(2)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005402 __ j(not_equal, &longer_than_two);
5403
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005404 // Check that both strings are non-external ASCII strings.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005405 __ JumpIfNotBothSequentialAsciiStrings(eax, edx, ebx, ecx, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005406
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005407 // Get the two characters forming the new string.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005408 __ movzx_b(ebx, FieldOperand(eax, SeqOneByteString::kHeaderSize));
5409 __ movzx_b(ecx, FieldOperand(edx, SeqOneByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005410
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005411 // Try to lookup two character string in string table. If it is not found
ricow@chromium.org65fae842010-08-25 15:26:24 +00005412 // just allocate a new one.
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005413 Label make_two_character_string, make_two_character_string_no_reload;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005414 StringHelper::GenerateTwoCharacterStringTableProbe(
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005415 masm, ebx, ecx, eax, edx, edi,
5416 &make_two_character_string_no_reload, &make_two_character_string);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005417 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005418 __ ret(2 * kPointerSize);
5419
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005420 // Allocate a two character string.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005421 __ bind(&make_two_character_string);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005422 // Reload the arguments.
5423 __ mov(eax, Operand(esp, 2 * kPointerSize)); // First argument.
5424 __ mov(edx, Operand(esp, 1 * kPointerSize)); // Second argument.
5425 // Get the two characters forming the new string.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005426 __ movzx_b(ebx, FieldOperand(eax, SeqOneByteString::kHeaderSize));
5427 __ movzx_b(ecx, FieldOperand(edx, SeqOneByteString::kHeaderSize));
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005428 __ bind(&make_two_character_string_no_reload);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005429 __ IncrementCounter(counters->string_add_make_two_char(), 1);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005430 __ AllocateAsciiString(eax, 2, edi, edx, &call_runtime);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005431 // Pack both characters in ebx.
5432 __ shl(ecx, kBitsPerByte);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005433 __ or_(ebx, ecx);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005434 // Set the characters in the new string.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005435 __ mov_w(FieldOperand(eax, SeqOneByteString::kHeaderSize), ebx);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005436 __ IncrementCounter(counters->string_add_native(), 1);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005437 __ ret(2 * kPointerSize);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005438
5439 __ bind(&longer_than_two);
5440 // Check if resulting string will be flat.
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005441 __ cmp(ebx, Immediate(Smi::FromInt(ConsString::kMinLength)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005442 __ j(below, &string_add_flat_result);
5443
5444 // If result is not supposed to be flat allocate a cons string object. If both
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005445 // strings are ASCII the result is an ASCII cons string.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005446 Label non_ascii, allocated, ascii_data;
5447 __ mov(edi, FieldOperand(eax, HeapObject::kMapOffset));
5448 __ movzx_b(ecx, FieldOperand(edi, Map::kInstanceTypeOffset));
5449 __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
5450 __ movzx_b(edi, FieldOperand(edi, Map::kInstanceTypeOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005451 __ and_(ecx, edi);
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +00005452 STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00005453 STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
5454 __ test(ecx, Immediate(kStringEncodingMask));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005455 __ j(zero, &non_ascii);
5456 __ bind(&ascii_data);
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005457 // Allocate an ASCII cons string.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005458 __ AllocateAsciiConsString(ecx, edi, no_reg, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005459 __ bind(&allocated);
5460 // Fill the fields of the cons string.
svenpanne@chromium.orgc859c4f2012-10-15 11:51:39 +00005461 __ AssertSmi(ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005462 __ mov(FieldOperand(ecx, ConsString::kLengthOffset), ebx);
5463 __ mov(FieldOperand(ecx, ConsString::kHashFieldOffset),
5464 Immediate(String::kEmptyHashField));
ulan@chromium.org57ff8812013-05-10 08:16:55 +00005465
5466 Label skip_write_barrier, after_writing;
5467 ExternalReference high_promotion_mode = ExternalReference::
5468 new_space_high_promotion_mode_active_address(masm->isolate());
5469 __ test(Operand::StaticVariable(high_promotion_mode), Immediate(1));
5470 __ j(zero, &skip_write_barrier);
5471
5472 __ mov(FieldOperand(ecx, ConsString::kFirstOffset), eax);
5473 __ RecordWriteField(ecx,
5474 ConsString::kFirstOffset,
5475 eax,
5476 ebx,
5477 kDontSaveFPRegs);
5478 __ mov(FieldOperand(ecx, ConsString::kSecondOffset), edx);
5479 __ RecordWriteField(ecx,
5480 ConsString::kSecondOffset,
5481 edx,
5482 ebx,
5483 kDontSaveFPRegs);
5484 __ jmp(&after_writing);
5485
5486 __ bind(&skip_write_barrier);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005487 __ mov(FieldOperand(ecx, ConsString::kFirstOffset), eax);
5488 __ mov(FieldOperand(ecx, ConsString::kSecondOffset), edx);
ulan@chromium.org57ff8812013-05-10 08:16:55 +00005489
5490 __ bind(&after_writing);
5491
ricow@chromium.org65fae842010-08-25 15:26:24 +00005492 __ mov(eax, ecx);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005493 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005494 __ ret(2 * kPointerSize);
5495 __ bind(&non_ascii);
5496 // At least one of the strings is two-byte. Check whether it happens
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005497 // to contain only one byte characters.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005498 // ecx: first instance type AND second instance type.
5499 // edi: second instance type.
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005500 __ test(ecx, Immediate(kOneByteDataHintMask));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005501 __ j(not_zero, &ascii_data);
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00005502 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
5503 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
5504 __ xor_(edi, ecx);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005505 STATIC_ASSERT(kOneByteStringTag != 0 && kOneByteDataHintTag != 0);
5506 __ and_(edi, kOneByteStringTag | kOneByteDataHintTag);
5507 __ cmp(edi, kOneByteStringTag | kOneByteDataHintTag);
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00005508 __ j(equal, &ascii_data);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005509 // Allocate a two byte cons string.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005510 __ AllocateTwoByteConsString(ecx, edi, no_reg, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005511 __ jmp(&allocated);
5512
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005513 // We cannot encounter sliced strings or cons strings here since:
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005514 STATIC_ASSERT(SlicedString::kMinLength >= ConsString::kMinLength);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005515 // Handle creating a flat result from either external or sequential strings.
5516 // Locate the first characters' locations.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005517 // eax: first string
5518 // ebx: length of resulting flat string as a smi
5519 // edx: second string
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005520 Label first_prepared, second_prepared;
5521 Label first_is_sequential, second_is_sequential;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005522 __ bind(&string_add_flat_result);
5523 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
5524 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005525 // ecx: instance type of first string
5526 STATIC_ASSERT(kSeqStringTag == 0);
5527 __ test_b(ecx, kStringRepresentationMask);
5528 __ j(zero, &first_is_sequential, Label::kNear);
5529 // Rule out short external string and load string resource.
5530 STATIC_ASSERT(kShortExternalStringTag != 0);
5531 __ test_b(ecx, kShortExternalStringMask);
5532 __ j(not_zero, &call_runtime);
5533 __ mov(eax, FieldOperand(eax, ExternalString::kResourceDataOffset));
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005534 STATIC_ASSERT(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005535 __ jmp(&first_prepared, Label::kNear);
5536 __ bind(&first_is_sequential);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005537 __ add(eax, Immediate(SeqOneByteString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005538 __ bind(&first_prepared);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005539
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005540 __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
5541 __ movzx_b(edi, FieldOperand(edi, Map::kInstanceTypeOffset));
5542 // Check whether both strings have same encoding.
5543 // edi: instance type of second string
5544 __ xor_(ecx, edi);
5545 __ test_b(ecx, kStringEncodingMask);
5546 __ j(not_zero, &call_runtime);
5547 STATIC_ASSERT(kSeqStringTag == 0);
5548 __ test_b(edi, kStringRepresentationMask);
5549 __ j(zero, &second_is_sequential, Label::kNear);
5550 // Rule out short external string and load string resource.
5551 STATIC_ASSERT(kShortExternalStringTag != 0);
5552 __ test_b(edi, kShortExternalStringMask);
5553 __ j(not_zero, &call_runtime);
5554 __ mov(edx, FieldOperand(edx, ExternalString::kResourceDataOffset));
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005555 STATIC_ASSERT(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005556 __ jmp(&second_prepared, Label::kNear);
5557 __ bind(&second_is_sequential);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005558 __ add(edx, Immediate(SeqOneByteString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005559 __ bind(&second_prepared);
5560
5561 // Push the addresses of both strings' first characters onto the stack.
5562 __ push(edx);
5563 __ push(eax);
5564
5565 Label non_ascii_string_add_flat_result, call_runtime_drop_two;
5566 // edi: instance type of second string
5567 // First string and second string have the same encoding.
5568 STATIC_ASSERT(kTwoByteStringTag == 0);
5569 __ test_b(edi, kStringEncodingMask);
5570 __ j(zero, &non_ascii_string_add_flat_result);
5571
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005572 // Both strings are ASCII strings.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005573 // ebx: length of resulting flat string as a smi
5574 __ SmiUntag(ebx);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005575 __ AllocateAsciiString(eax, ebx, ecx, edx, edi, &call_runtime_drop_two);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005576 // eax: result string
5577 __ mov(ecx, eax);
5578 // Locate first character of result.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005579 __ add(ecx, Immediate(SeqOneByteString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005580 // Load first argument's length and first character location. Account for
5581 // values currently on the stack when fetching arguments from it.
5582 __ mov(edx, Operand(esp, 4 * kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005583 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
5584 __ SmiUntag(edi);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005585 __ pop(edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005586 // eax: result string
5587 // ecx: first character of result
5588 // edx: first char of first argument
5589 // edi: length of first argument
5590 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005591 // Load second argument's length and first character location. Account for
5592 // values currently on the stack when fetching arguments from it.
5593 __ mov(edx, Operand(esp, 2 * kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005594 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
5595 __ SmiUntag(edi);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005596 __ pop(edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005597 // eax: result string
5598 // ecx: next character of result
5599 // edx: first char of second argument
5600 // edi: length of second argument
5601 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005602 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005603 __ ret(2 * kPointerSize);
5604
5605 // Handle creating a flat two byte result.
5606 // eax: first string - known to be two byte
5607 // ebx: length of resulting flat string as a smi
5608 // edx: second string
5609 __ bind(&non_ascii_string_add_flat_result);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005610 // Both strings are two byte strings.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005611 __ SmiUntag(ebx);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005612 __ AllocateTwoByteString(eax, ebx, ecx, edx, edi, &call_runtime_drop_two);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005613 // eax: result string
5614 __ mov(ecx, eax);
5615 // Locate first character of result.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005616 __ add(ecx, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
5617 // Load second argument's length and first character location. Account for
5618 // values currently on the stack when fetching arguments from it.
5619 __ mov(edx, Operand(esp, 4 * kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005620 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
5621 __ SmiUntag(edi);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005622 __ pop(edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005623 // eax: result string
5624 // ecx: first character of result
5625 // edx: first char of first argument
5626 // edi: length of first argument
5627 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005628 // Load second argument's length and first character location. Account for
5629 // values currently on the stack when fetching arguments from it.
5630 __ mov(edx, Operand(esp, 2 * kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005631 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
5632 __ SmiUntag(edi);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005633 __ pop(edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005634 // eax: result string
5635 // ecx: next character of result
5636 // edx: first char of second argument
5637 // edi: length of second argument
5638 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005639 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005640 __ ret(2 * kPointerSize);
5641
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005642 // Recover stack pointer before jumping to runtime.
5643 __ bind(&call_runtime_drop_two);
5644 __ Drop(2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005645 // Just jump to runtime to add the two strings.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005646 __ bind(&call_runtime);
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00005647 if ((flags_ & ERECT_FRAME) != 0) {
5648 GenerateRegisterArgsPop(masm, ecx);
5649 // Build a frame
5650 {
5651 FrameScope scope(masm, StackFrame::INTERNAL);
5652 GenerateRegisterArgsPush(masm);
5653 __ CallRuntime(Runtime::kStringAdd, 2);
5654 }
5655 __ ret(0);
5656 } else {
5657 __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
5658 }
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005659
5660 if (call_builtin.is_linked()) {
5661 __ bind(&call_builtin);
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00005662 if ((flags_ & ERECT_FRAME) != 0) {
5663 GenerateRegisterArgsPop(masm, ecx);
5664 // Build a frame
5665 {
5666 FrameScope scope(masm, StackFrame::INTERNAL);
5667 GenerateRegisterArgsPush(masm);
5668 __ InvokeBuiltin(builtin_id, CALL_FUNCTION);
5669 }
5670 __ ret(0);
5671 } else {
5672 __ InvokeBuiltin(builtin_id, JUMP_FUNCTION);
5673 }
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005674 }
5675}
5676
5677
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00005678void StringAddStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
5679 __ push(eax);
5680 __ push(edx);
5681}
5682
5683
5684void StringAddStub::GenerateRegisterArgsPop(MacroAssembler* masm,
5685 Register temp) {
5686 __ pop(temp);
5687 __ pop(edx);
5688 __ pop(eax);
5689 __ push(temp);
5690}
5691
5692
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005693void StringAddStub::GenerateConvertArgument(MacroAssembler* masm,
5694 int stack_offset,
5695 Register arg,
5696 Register scratch1,
5697 Register scratch2,
5698 Register scratch3,
5699 Label* slow) {
5700 // First check if the argument is already a string.
5701 Label not_string, done;
whesse@chromium.org7b260152011-06-20 15:33:18 +00005702 __ JumpIfSmi(arg, &not_string);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005703 __ CmpObjectType(arg, FIRST_NONSTRING_TYPE, scratch1);
5704 __ j(below, &done);
5705
5706 // Check the number to string cache.
5707 Label not_cached;
5708 __ bind(&not_string);
5709 // Puts the cached result into scratch1.
5710 NumberToStringStub::GenerateLookupNumberStringCache(masm,
5711 arg,
5712 scratch1,
5713 scratch2,
5714 scratch3,
5715 false,
5716 &not_cached);
5717 __ mov(arg, scratch1);
5718 __ mov(Operand(esp, stack_offset), arg);
5719 __ jmp(&done);
5720
5721 // Check if the argument is a safe string wrapper.
5722 __ bind(&not_cached);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005723 __ JumpIfSmi(arg, slow);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005724 __ CmpObjectType(arg, JS_VALUE_TYPE, scratch1); // map -> scratch1.
5725 __ j(not_equal, slow);
5726 __ test_b(FieldOperand(scratch1, Map::kBitField2Offset),
5727 1 << Map::kStringWrapperSafeForDefaultValueOf);
5728 __ j(zero, slow);
5729 __ mov(arg, FieldOperand(arg, JSValue::kValueOffset));
5730 __ mov(Operand(esp, stack_offset), arg);
5731
5732 __ bind(&done);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005733}
5734
5735
5736void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
5737 Register dest,
5738 Register src,
5739 Register count,
5740 Register scratch,
5741 bool ascii) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005742 Label loop;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005743 __ bind(&loop);
5744 // This loop just copies one character at a time, as it is only used for very
5745 // short strings.
5746 if (ascii) {
5747 __ mov_b(scratch, Operand(src, 0));
5748 __ mov_b(Operand(dest, 0), scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005749 __ add(src, Immediate(1));
5750 __ add(dest, Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005751 } else {
5752 __ mov_w(scratch, Operand(src, 0));
5753 __ mov_w(Operand(dest, 0), scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005754 __ add(src, Immediate(2));
5755 __ add(dest, Immediate(2));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005756 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005757 __ sub(count, Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005758 __ j(not_zero, &loop);
5759}
5760
5761
5762void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm,
5763 Register dest,
5764 Register src,
5765 Register count,
5766 Register scratch,
5767 bool ascii) {
5768 // Copy characters using rep movs of doublewords.
5769 // The destination is aligned on a 4 byte boundary because we are
5770 // copying to the beginning of a newly allocated string.
5771 ASSERT(dest.is(edi)); // rep movs destination
5772 ASSERT(src.is(esi)); // rep movs source
5773 ASSERT(count.is(ecx)); // rep movs count
5774 ASSERT(!scratch.is(dest));
5775 ASSERT(!scratch.is(src));
5776 ASSERT(!scratch.is(count));
5777
5778 // Nothing to do for zero characters.
5779 Label done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005780 __ test(count, count);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005781 __ j(zero, &done);
5782
5783 // Make count the number of bytes to copy.
5784 if (!ascii) {
5785 __ shl(count, 1);
5786 }
5787
5788 // Don't enter the rep movs if there are less than 4 bytes to copy.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005789 Label last_bytes;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005790 __ test(count, Immediate(~3));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005791 __ j(zero, &last_bytes, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005792
5793 // Copy from edi to esi using rep movs instruction.
5794 __ mov(scratch, count);
5795 __ sar(count, 2); // Number of doublewords to copy.
5796 __ cld();
5797 __ rep_movs();
5798
5799 // Find number of bytes left.
5800 __ mov(count, scratch);
5801 __ and_(count, 3);
5802
5803 // Check if there are more bytes to copy.
5804 __ bind(&last_bytes);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005805 __ test(count, count);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005806 __ j(zero, &done);
5807
5808 // Copy remaining characters.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005809 Label loop;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005810 __ bind(&loop);
5811 __ mov_b(scratch, Operand(src, 0));
5812 __ mov_b(Operand(dest, 0), scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005813 __ add(src, Immediate(1));
5814 __ add(dest, Immediate(1));
5815 __ sub(count, Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005816 __ j(not_zero, &loop);
5817
5818 __ bind(&done);
5819}
5820
5821
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005822void StringHelper::GenerateTwoCharacterStringTableProbe(MacroAssembler* masm,
ricow@chromium.org65fae842010-08-25 15:26:24 +00005823 Register c1,
5824 Register c2,
5825 Register scratch1,
5826 Register scratch2,
5827 Register scratch3,
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005828 Label* not_probed,
ricow@chromium.org65fae842010-08-25 15:26:24 +00005829 Label* not_found) {
5830 // Register scratch3 is the general scratch register in this function.
5831 Register scratch = scratch3;
5832
5833 // Make sure that both characters are not digits as such strings has a
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005834 // different hash algorithm. Don't try to look for these in the string table.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005835 Label not_array_index;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005836 __ mov(scratch, c1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005837 __ sub(scratch, Immediate(static_cast<int>('0')));
5838 __ cmp(scratch, Immediate(static_cast<int>('9' - '0')));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005839 __ j(above, &not_array_index, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005840 __ mov(scratch, c2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005841 __ sub(scratch, Immediate(static_cast<int>('0')));
5842 __ cmp(scratch, Immediate(static_cast<int>('9' - '0')));
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005843 __ j(below_equal, not_probed);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005844
5845 __ bind(&not_array_index);
5846 // Calculate the two character string hash.
5847 Register hash = scratch1;
5848 GenerateHashInit(masm, hash, c1, scratch);
5849 GenerateHashAddCharacter(masm, hash, c2, scratch);
5850 GenerateHashGetHash(masm, hash, scratch);
5851
5852 // Collect the two characters in a register.
5853 Register chars = c1;
5854 __ shl(c2, kBitsPerByte);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005855 __ or_(chars, c2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005856
5857 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
5858 // hash: hash of two character string.
5859
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005860 // Load the string table.
5861 Register string_table = c2;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005862 ExternalReference roots_array_start =
5863 ExternalReference::roots_array_start(masm->isolate());
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005864 __ mov(scratch, Immediate(Heap::kStringTableRootIndex));
5865 __ mov(string_table,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005866 Operand::StaticArray(scratch, times_pointer_size, roots_array_start));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005867
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005868 // Calculate capacity mask from the string table capacity.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005869 Register mask = scratch2;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005870 __ mov(mask, FieldOperand(string_table, StringTable::kCapacityOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005871 __ SmiUntag(mask);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005872 __ sub(mask, Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005873
5874 // Registers
5875 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
5876 // hash: hash of two character string
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005877 // string_table: string table
ricow@chromium.org65fae842010-08-25 15:26:24 +00005878 // mask: capacity mask
5879 // scratch: -
5880
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005881 // Perform a number of probes in the string table.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005882 static const int kProbes = 4;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005883 Label found_in_string_table;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005884 Label next_probe[kProbes], next_probe_pop_mask[kProbes];
danno@chromium.org2c456792011-11-11 12:00:53 +00005885 Register candidate = scratch; // Scratch register contains candidate.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005886 for (int i = 0; i < kProbes; i++) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005887 // Calculate entry in string table.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005888 __ mov(scratch, hash);
5889 if (i > 0) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005890 __ add(scratch, Immediate(StringTable::GetProbeOffset(i)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005891 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005892 __ and_(scratch, mask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005893
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005894 // Load the entry from the string table.
5895 STATIC_ASSERT(StringTable::kEntrySize == 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005896 __ mov(candidate,
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005897 FieldOperand(string_table,
ricow@chromium.org65fae842010-08-25 15:26:24 +00005898 scratch,
5899 times_pointer_size,
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005900 StringTable::kElementsStartOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005901
5902 // If entry is undefined no string with this hash can be found.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005903 Factory* factory = masm->isolate()->factory();
5904 __ cmp(candidate, factory->undefined_value());
ricow@chromium.org65fae842010-08-25 15:26:24 +00005905 __ j(equal, not_found);
danno@chromium.org2c456792011-11-11 12:00:53 +00005906 __ cmp(candidate, factory->the_hole_value());
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +00005907 __ j(equal, &next_probe[i]);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005908
5909 // If length is not 2 the string is not a candidate.
5910 __ cmp(FieldOperand(candidate, String::kLengthOffset),
5911 Immediate(Smi::FromInt(2)));
5912 __ j(not_equal, &next_probe[i]);
5913
5914 // As we are out of registers save the mask on the stack and use that
5915 // register as a temporary.
5916 __ push(mask);
5917 Register temp = mask;
5918
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005919 // Check that the candidate is a non-external ASCII string.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005920 __ mov(temp, FieldOperand(candidate, HeapObject::kMapOffset));
5921 __ movzx_b(temp, FieldOperand(temp, Map::kInstanceTypeOffset));
5922 __ JumpIfInstanceTypeIsNotSequentialAscii(
5923 temp, temp, &next_probe_pop_mask[i]);
5924
5925 // Check if the two characters match.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005926 __ mov(temp, FieldOperand(candidate, SeqOneByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005927 __ and_(temp, 0x0000ffff);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005928 __ cmp(chars, temp);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005929 __ j(equal, &found_in_string_table);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005930 __ bind(&next_probe_pop_mask[i]);
5931 __ pop(mask);
5932 __ bind(&next_probe[i]);
5933 }
5934
5935 // No matching 2 character string found by probing.
5936 __ jmp(not_found);
5937
5938 // Scratch register contains result when we fall through to here.
danno@chromium.org2c456792011-11-11 12:00:53 +00005939 Register result = candidate;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005940 __ bind(&found_in_string_table);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005941 __ pop(mask); // Pop saved mask from the stack.
5942 if (!result.is(eax)) {
5943 __ mov(eax, result);
5944 }
5945}
5946
5947
5948void StringHelper::GenerateHashInit(MacroAssembler* masm,
5949 Register hash,
5950 Register character,
5951 Register scratch) {
rossberg@chromium.orgfab14982012-01-05 15:02:15 +00005952 // hash = (seed + character) + ((seed + character) << 10);
5953 if (Serializer::enabled()) {
5954 ExternalReference roots_array_start =
5955 ExternalReference::roots_array_start(masm->isolate());
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00005956 __ mov(scratch, Immediate(Heap::kHashSeedRootIndex));
rossberg@chromium.orgfab14982012-01-05 15:02:15 +00005957 __ mov(scratch, Operand::StaticArray(scratch,
5958 times_pointer_size,
5959 roots_array_start));
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005960 __ SmiUntag(scratch);
rossberg@chromium.orgfab14982012-01-05 15:02:15 +00005961 __ add(scratch, character);
5962 __ mov(hash, scratch);
5963 __ shl(scratch, 10);
5964 __ add(hash, scratch);
5965 } else {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00005966 int32_t seed = masm->isolate()->heap()->HashSeed();
rossberg@chromium.orgfab14982012-01-05 15:02:15 +00005967 __ lea(scratch, Operand(character, seed));
5968 __ shl(scratch, 10);
5969 __ lea(hash, Operand(scratch, character, times_1, seed));
5970 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00005971 // hash ^= hash >> 6;
5972 __ mov(scratch, hash);
danno@chromium.org2c456792011-11-11 12:00:53 +00005973 __ shr(scratch, 6);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005974 __ xor_(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005975}
5976
5977
5978void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
5979 Register hash,
5980 Register character,
5981 Register scratch) {
5982 // hash += character;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005983 __ add(hash, character);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005984 // hash += hash << 10;
5985 __ mov(scratch, hash);
5986 __ shl(scratch, 10);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005987 __ add(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005988 // hash ^= hash >> 6;
5989 __ mov(scratch, hash);
danno@chromium.org2c456792011-11-11 12:00:53 +00005990 __ shr(scratch, 6);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005991 __ xor_(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005992}
5993
5994
5995void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
5996 Register hash,
5997 Register scratch) {
5998 // hash += hash << 3;
5999 __ mov(scratch, hash);
6000 __ shl(scratch, 3);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006001 __ add(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006002 // hash ^= hash >> 11;
6003 __ mov(scratch, hash);
danno@chromium.org2c456792011-11-11 12:00:53 +00006004 __ shr(scratch, 11);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006005 __ xor_(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006006 // hash += hash << 15;
6007 __ mov(scratch, hash);
6008 __ shl(scratch, 15);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006009 __ add(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006010
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00006011 __ and_(hash, String::kHashBitMask);
danno@chromium.org2c456792011-11-11 12:00:53 +00006012
ricow@chromium.org65fae842010-08-25 15:26:24 +00006013 // if (hash == 0) hash = 27;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006014 Label hash_not_zero;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006015 __ j(not_zero, &hash_not_zero, Label::kNear);
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00006016 __ mov(hash, Immediate(StringHasher::kZeroHash));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006017 __ bind(&hash_not_zero);
6018}
6019
6020
6021void SubStringStub::Generate(MacroAssembler* masm) {
6022 Label runtime;
6023
6024 // Stack frame on entry.
6025 // esp[0]: return address
6026 // esp[4]: to
6027 // esp[8]: from
6028 // esp[12]: string
6029
6030 // Make sure first argument is a string.
6031 __ mov(eax, Operand(esp, 3 * kPointerSize));
6032 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006033 __ JumpIfSmi(eax, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006034 Condition is_string = masm->IsObjectStringType(eax, ebx, ebx);
6035 __ j(NegateCondition(is_string), &runtime);
6036
6037 // eax: string
6038 // ebx: instance type
6039
6040 // Calculate length of sub string using the smi values.
ricow@chromium.org65fae842010-08-25 15:26:24 +00006041 __ mov(ecx, Operand(esp, 1 * kPointerSize)); // To index.
whesse@chromium.org7b260152011-06-20 15:33:18 +00006042 __ JumpIfNotSmi(ecx, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006043 __ mov(edx, Operand(esp, 2 * kPointerSize)); // From index.
whesse@chromium.org7b260152011-06-20 15:33:18 +00006044 __ JumpIfNotSmi(edx, &runtime);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006045 __ sub(ecx, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006046 __ cmp(ecx, FieldOperand(eax, String::kLengthOffset));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006047 Label not_original_string;
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00006048 // Shorter than original string's length: an actual substring.
6049 __ j(below, &not_original_string, Label::kNear);
6050 // Longer than original string's length or negative: unsafe arguments.
6051 __ j(above, &runtime);
6052 // Return original string.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006053 Counters* counters = masm->isolate()->counters();
6054 __ IncrementCounter(counters->sub_string_native(), 1);
6055 __ ret(3 * kPointerSize);
6056 __ bind(&not_original_string);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006057
ulan@chromium.org2e04b582013-02-21 14:06:02 +00006058 Label single_char;
6059 __ cmp(ecx, Immediate(Smi::FromInt(1)));
6060 __ j(equal, &single_char);
6061
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006062 // eax: string
6063 // ebx: instance type
6064 // ecx: sub string length (smi)
6065 // edx: from index (smi)
6066 // Deal with different string types: update the index if necessary
6067 // and put the underlying string into edi.
6068 Label underlying_unpacked, sliced_string, seq_or_external_string;
6069 // If the string is not indirect, it can only be sequential or external.
6070 STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
6071 STATIC_ASSERT(kIsIndirectStringMask != 0);
6072 __ test(ebx, Immediate(kIsIndirectStringMask));
6073 __ j(zero, &seq_or_external_string, Label::kNear);
6074
6075 Factory* factory = masm->isolate()->factory();
6076 __ test(ebx, Immediate(kSlicedNotConsMask));
6077 __ j(not_zero, &sliced_string, Label::kNear);
6078 // Cons string. Check whether it is flat, then fetch first part.
6079 // Flat cons strings have an empty second part.
6080 __ cmp(FieldOperand(eax, ConsString::kSecondOffset),
6081 factory->empty_string());
6082 __ j(not_equal, &runtime);
6083 __ mov(edi, FieldOperand(eax, ConsString::kFirstOffset));
6084 // Update instance type.
6085 __ mov(ebx, FieldOperand(edi, HeapObject::kMapOffset));
6086 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
6087 __ jmp(&underlying_unpacked, Label::kNear);
6088
6089 __ bind(&sliced_string);
6090 // Sliced string. Fetch parent and adjust start index by offset.
6091 __ add(edx, FieldOperand(eax, SlicedString::kOffsetOffset));
6092 __ mov(edi, FieldOperand(eax, SlicedString::kParentOffset));
6093 // Update instance type.
6094 __ mov(ebx, FieldOperand(edi, HeapObject::kMapOffset));
6095 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
6096 __ jmp(&underlying_unpacked, Label::kNear);
6097
6098 __ bind(&seq_or_external_string);
6099 // Sequential or external string. Just move string to the expected register.
6100 __ mov(edi, eax);
6101
6102 __ bind(&underlying_unpacked);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006103
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00006104 if (FLAG_string_slices) {
6105 Label copy_routine;
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00006106 // edi: underlying subject string
ricow@chromium.org7ad65222011-12-19 12:13:11 +00006107 // ebx: instance type of underlying subject string
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006108 // edx: adjusted start index (smi)
6109 // ecx: length (smi)
6110 __ cmp(ecx, Immediate(Smi::FromInt(SlicedString::kMinLength)));
6111 // Short slice. Copy instead of slicing.
6112 __ j(less, &copy_routine);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00006113 // Allocate new sliced string. At this point we do not reload the instance
6114 // type including the string encoding because we simply rely on the info
6115 // provided by the original string. It does not matter if the original
6116 // string's encoding is wrong because we always have to recheck encoding of
6117 // the newly created string's parent anyways due to externalized strings.
6118 Label two_byte_slice, set_slice_header;
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +00006119 STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00006120 STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
6121 __ test(ebx, Immediate(kStringEncodingMask));
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00006122 __ j(zero, &two_byte_slice, Label::kNear);
6123 __ AllocateAsciiSlicedString(eax, ebx, no_reg, &runtime);
6124 __ jmp(&set_slice_header, Label::kNear);
6125 __ bind(&two_byte_slice);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00006126 __ AllocateTwoByteSlicedString(eax, ebx, no_reg, &runtime);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00006127 __ bind(&set_slice_header);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00006128 __ mov(FieldOperand(eax, SlicedString::kLengthOffset), ecx);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00006129 __ mov(FieldOperand(eax, SlicedString::kHashFieldOffset),
6130 Immediate(String::kEmptyHashField));
erik.corry@gmail.combbceb572012-03-09 10:52:05 +00006131 __ mov(FieldOperand(eax, SlicedString::kParentOffset), edi);
6132 __ mov(FieldOperand(eax, SlicedString::kOffsetOffset), edx);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006133 __ IncrementCounter(counters->sub_string_native(), 1);
6134 __ ret(3 * kPointerSize);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00006135
6136 __ bind(&copy_routine);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00006137 }
6138
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006139 // edi: underlying subject string
ricow@chromium.org7ad65222011-12-19 12:13:11 +00006140 // ebx: instance type of underlying subject string
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006141 // edx: adjusted start index (smi)
6142 // ecx: length (smi)
6143 // The subject string can only be external or sequential string of either
6144 // encoding at this point.
6145 Label two_byte_sequential, runtime_drop_two, sequential_string;
6146 STATIC_ASSERT(kExternalStringTag != 0);
6147 STATIC_ASSERT(kSeqStringTag == 0);
6148 __ test_b(ebx, kExternalStringTag);
6149 __ j(zero, &sequential_string);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006150
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006151 // Handle external string.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006152 // Rule out short external strings.
6153 STATIC_CHECK(kShortExternalStringTag != 0);
6154 __ test_b(ebx, kShortExternalStringMask);
6155 __ j(not_zero, &runtime);
6156 __ mov(edi, FieldOperand(edi, ExternalString::kResourceDataOffset));
6157 // Move the pointer so that offset-wise, it looks like a sequential string.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006158 STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006159 __ sub(edi, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
6160
6161 __ bind(&sequential_string);
6162 // Stash away (adjusted) index and (underlying) string.
6163 __ push(edx);
6164 __ push(edi);
6165 __ SmiUntag(ecx);
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +00006166 STATIC_ASSERT((kOneByteStringTag & kStringEncodingMask) != 0);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006167 __ test_b(ebx, kStringEncodingMask);
6168 __ j(zero, &two_byte_sequential);
6169
ulan@chromium.org2efb9002012-01-19 15:36:35 +00006170 // Sequential ASCII string. Allocate the result.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006171 __ AllocateAsciiString(eax, ecx, ebx, edx, edi, &runtime_drop_two);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006172
6173 // eax: result string
6174 // ecx: result string length
6175 __ mov(edx, esi); // esi used by following code.
6176 // Locate first character of result.
6177 __ mov(edi, eax);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006178 __ add(edi, Immediate(SeqOneByteString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006179 // Load string argument and locate character of sub string start.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006180 __ pop(esi);
6181 __ pop(ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006182 __ SmiUntag(ebx);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006183 __ lea(esi, FieldOperand(esi, ebx, times_1, SeqOneByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006184
6185 // eax: result string
6186 // ecx: result length
6187 // edx: original value of esi
6188 // edi: first character of result
6189 // esi: character of sub string start
6190 StringHelper::GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, true);
6191 __ mov(esi, edx); // Restore esi.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006192 __ IncrementCounter(counters->sub_string_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006193 __ ret(3 * kPointerSize);
6194
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006195 __ bind(&two_byte_sequential);
6196 // Sequential two-byte string. Allocate the result.
6197 __ AllocateTwoByteString(eax, ecx, ebx, edx, edi, &runtime_drop_two);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006198
6199 // eax: result string
6200 // ecx: result string length
6201 __ mov(edx, esi); // esi used by following code.
6202 // Locate first character of result.
6203 __ mov(edi, eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006204 __ add(edi,
ricow@chromium.org65fae842010-08-25 15:26:24 +00006205 Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
6206 // Load string argument and locate character of sub string start.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006207 __ pop(esi);
6208 __ pop(ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006209 // As from is a smi it is 2 times the value which matches the size of a two
6210 // byte character.
6211 STATIC_ASSERT(kSmiTag == 0);
6212 STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006213 __ lea(esi, FieldOperand(esi, ebx, times_1, SeqTwoByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006214
6215 // eax: result string
6216 // ecx: result length
6217 // edx: original value of esi
6218 // edi: first character of result
6219 // esi: character of sub string start
6220 StringHelper::GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, false);
6221 __ mov(esi, edx); // Restore esi.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006222 __ IncrementCounter(counters->sub_string_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006223 __ ret(3 * kPointerSize);
6224
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006225 // Drop pushed values on the stack before tail call.
6226 __ bind(&runtime_drop_two);
6227 __ Drop(2);
6228
ricow@chromium.org65fae842010-08-25 15:26:24 +00006229 // Just jump to runtime to create the sub string.
6230 __ bind(&runtime);
6231 __ TailCallRuntime(Runtime::kSubString, 3, 1);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00006232
6233 __ bind(&single_char);
6234 // eax: string
6235 // ebx: instance type
6236 // ecx: sub string length (smi)
6237 // edx: from index (smi)
6238 StringCharAtGenerator generator(
6239 eax, edx, ecx, eax, &runtime, &runtime, &runtime, STRING_INDEX_IS_NUMBER);
6240 generator.GenerateFast(masm);
6241 __ ret(3 * kPointerSize);
6242 generator.SkipSlow(masm, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006243}
6244
6245
lrn@chromium.org1c092762011-05-09 09:42:16 +00006246void StringCompareStub::GenerateFlatAsciiStringEquals(MacroAssembler* masm,
6247 Register left,
6248 Register right,
6249 Register scratch1,
6250 Register scratch2) {
6251 Register length = scratch1;
6252
6253 // Compare lengths.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006254 Label strings_not_equal, check_zero_length;
lrn@chromium.org1c092762011-05-09 09:42:16 +00006255 __ mov(length, FieldOperand(left, String::kLengthOffset));
6256 __ cmp(length, FieldOperand(right, String::kLengthOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006257 __ j(equal, &check_zero_length, Label::kNear);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006258 __ bind(&strings_not_equal);
6259 __ Set(eax, Immediate(Smi::FromInt(NOT_EQUAL)));
6260 __ ret(0);
6261
6262 // Check if the length is zero.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006263 Label compare_chars;
lrn@chromium.org1c092762011-05-09 09:42:16 +00006264 __ bind(&check_zero_length);
6265 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006266 __ test(length, length);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006267 __ j(not_zero, &compare_chars, Label::kNear);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006268 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6269 __ ret(0);
6270
6271 // Compare characters.
6272 __ bind(&compare_chars);
6273 GenerateAsciiCharsCompareLoop(masm, left, right, length, scratch2,
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006274 &strings_not_equal, Label::kNear);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006275
6276 // Characters are equal.
6277 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6278 __ ret(0);
6279}
6280
6281
ricow@chromium.org65fae842010-08-25 15:26:24 +00006282void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
6283 Register left,
6284 Register right,
6285 Register scratch1,
6286 Register scratch2,
6287 Register scratch3) {
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006288 Counters* counters = masm->isolate()->counters();
6289 __ IncrementCounter(counters->string_compare_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006290
6291 // Find minimum length.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006292 Label left_shorter;
ricow@chromium.org65fae842010-08-25 15:26:24 +00006293 __ mov(scratch1, FieldOperand(left, String::kLengthOffset));
6294 __ mov(scratch3, scratch1);
6295 __ sub(scratch3, FieldOperand(right, String::kLengthOffset));
6296
6297 Register length_delta = scratch3;
6298
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006299 __ j(less_equal, &left_shorter, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006300 // Right string is shorter. Change scratch1 to be length of right string.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006301 __ sub(scratch1, length_delta);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006302 __ bind(&left_shorter);
6303
6304 Register min_length = scratch1;
6305
6306 // If either length is zero, just compare lengths.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006307 Label compare_lengths;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006308 __ test(min_length, min_length);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006309 __ j(zero, &compare_lengths, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006310
lrn@chromium.org1c092762011-05-09 09:42:16 +00006311 // Compare characters.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006312 Label result_not_equal;
lrn@chromium.org1c092762011-05-09 09:42:16 +00006313 GenerateAsciiCharsCompareLoop(masm, left, right, min_length, scratch2,
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006314 &result_not_equal, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006315
6316 // Compare lengths - strings up to min-length are equal.
6317 __ bind(&compare_lengths);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006318 __ test(length_delta, length_delta);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00006319 Label length_not_equal;
6320 __ j(not_zero, &length_not_equal, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006321
6322 // Result is EQUAL.
6323 STATIC_ASSERT(EQUAL == 0);
6324 STATIC_ASSERT(kSmiTag == 0);
6325 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6326 __ ret(0);
6327
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006328 Label result_greater;
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00006329 Label result_less;
6330 __ bind(&length_not_equal);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006331 __ j(greater, &result_greater, Label::kNear);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00006332 __ jmp(&result_less, Label::kNear);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00006333 __ bind(&result_not_equal);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00006334 __ j(above, &result_greater, Label::kNear);
6335 __ bind(&result_less);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006336
6337 // Result is LESS.
6338 __ Set(eax, Immediate(Smi::FromInt(LESS)));
6339 __ ret(0);
6340
6341 // Result is GREATER.
6342 __ bind(&result_greater);
6343 __ Set(eax, Immediate(Smi::FromInt(GREATER)));
6344 __ ret(0);
6345}
6346
6347
lrn@chromium.org1c092762011-05-09 09:42:16 +00006348void StringCompareStub::GenerateAsciiCharsCompareLoop(
6349 MacroAssembler* masm,
6350 Register left,
6351 Register right,
6352 Register length,
6353 Register scratch,
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006354 Label* chars_not_equal,
6355 Label::Distance chars_not_equal_near) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00006356 // Change index to run from -length to -1 by adding length to string
6357 // start. This means that loop ends when index reaches zero, which
6358 // doesn't need an additional compare.
6359 __ SmiUntag(length);
6360 __ lea(left,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006361 FieldOperand(left, length, times_1, SeqOneByteString::kHeaderSize));
lrn@chromium.org1c092762011-05-09 09:42:16 +00006362 __ lea(right,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006363 FieldOperand(right, length, times_1, SeqOneByteString::kHeaderSize));
lrn@chromium.org1c092762011-05-09 09:42:16 +00006364 __ neg(length);
6365 Register index = length; // index = -length;
6366
6367 // Compare loop.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006368 Label loop;
lrn@chromium.org1c092762011-05-09 09:42:16 +00006369 __ bind(&loop);
6370 __ mov_b(scratch, Operand(left, index, times_1, 0));
6371 __ cmpb(scratch, Operand(right, index, times_1, 0));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006372 __ j(not_equal, chars_not_equal, chars_not_equal_near);
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00006373 __ inc(index);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006374 __ j(not_zero, &loop);
6375}
6376
6377
ricow@chromium.org65fae842010-08-25 15:26:24 +00006378void StringCompareStub::Generate(MacroAssembler* masm) {
6379 Label runtime;
6380
6381 // Stack frame on entry.
6382 // esp[0]: return address
6383 // esp[4]: right string
6384 // esp[8]: left string
6385
6386 __ mov(edx, Operand(esp, 2 * kPointerSize)); // left
6387 __ mov(eax, Operand(esp, 1 * kPointerSize)); // right
6388
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006389 Label not_same;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006390 __ cmp(edx, eax);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006391 __ j(not_equal, &not_same, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006392 STATIC_ASSERT(EQUAL == 0);
6393 STATIC_ASSERT(kSmiTag == 0);
6394 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006395 __ IncrementCounter(masm->isolate()->counters()->string_compare_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006396 __ ret(2 * kPointerSize);
6397
6398 __ bind(&not_same);
6399
ulan@chromium.org2efb9002012-01-19 15:36:35 +00006400 // Check that both objects are sequential ASCII strings.
ricow@chromium.org65fae842010-08-25 15:26:24 +00006401 __ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx, &runtime);
6402
ulan@chromium.org2efb9002012-01-19 15:36:35 +00006403 // Compare flat ASCII strings.
ricow@chromium.org65fae842010-08-25 15:26:24 +00006404 // Drop arguments from the stack.
6405 __ pop(ecx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006406 __ add(esp, Immediate(2 * kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006407 __ push(ecx);
6408 GenerateCompareFlatAsciiStrings(masm, edx, eax, ecx, ebx, edi);
6409
6410 // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
6411 // tagged as a small integer.
6412 __ bind(&runtime);
6413 __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
6414}
6415
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006416
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006417void ICCompareStub::GenerateSmis(MacroAssembler* masm) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006418 ASSERT(state_ == CompareIC::SMI);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006419 Label miss;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006420 __ mov(ecx, edx);
6421 __ or_(ecx, eax);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006422 __ JumpIfNotSmi(ecx, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006423
6424 if (GetCondition() == equal) {
6425 // For equality we do not care about the sign of the result.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006426 __ sub(eax, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006427 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006428 Label done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006429 __ sub(edx, eax);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006430 __ j(no_overflow, &done, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006431 // Correct sign of result in case of overflow.
6432 __ not_(edx);
6433 __ bind(&done);
6434 __ mov(eax, edx);
6435 }
6436 __ ret(0);
6437
6438 __ bind(&miss);
6439 GenerateMiss(masm);
6440}
6441
6442
hpayer@chromium.org8432c912013-02-28 15:55:26 +00006443void ICCompareStub::GenerateNumbers(MacroAssembler* masm) {
6444 ASSERT(state_ == CompareIC::NUMBER);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006445
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006446 Label generic_stub;
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00006447 Label unordered, maybe_undefined1, maybe_undefined2;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006448 Label miss;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006449
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006450 if (left_ == CompareIC::SMI) {
6451 __ JumpIfNotSmi(edx, &miss);
6452 }
6453 if (right_ == CompareIC::SMI) {
6454 __ JumpIfNotSmi(eax, &miss);
6455 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006456
6457 // Inlining the double comparison and falling back to the general compare
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006458 // stub if NaN is involved or SSE2 or CMOV is unsupported.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006459 if (CpuFeatures::IsSupported(SSE2) && CpuFeatures::IsSupported(CMOV)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00006460 CpuFeatureScope scope1(masm, SSE2);
6461 CpuFeatureScope scope2(masm, CMOV);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006462
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006463 // Load left and right operand.
6464 Label done, left, left_smi, right_smi;
6465 __ JumpIfSmi(eax, &right_smi, Label::kNear);
6466 __ cmp(FieldOperand(eax, HeapObject::kMapOffset),
6467 masm->isolate()->factory()->heap_number_map());
6468 __ j(not_equal, &maybe_undefined1, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006469 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006470 __ jmp(&left, Label::kNear);
6471 __ bind(&right_smi);
6472 __ mov(ecx, eax); // Can't clobber eax because we can still jump away.
6473 __ SmiUntag(ecx);
6474 __ cvtsi2sd(xmm1, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006475
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006476 __ bind(&left);
6477 __ JumpIfSmi(edx, &left_smi, Label::kNear);
6478 __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
6479 masm->isolate()->factory()->heap_number_map());
6480 __ j(not_equal, &maybe_undefined2, Label::kNear);
6481 __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
6482 __ jmp(&done);
6483 __ bind(&left_smi);
6484 __ mov(ecx, edx); // Can't clobber edx because we can still jump away.
6485 __ SmiUntag(ecx);
6486 __ cvtsi2sd(xmm0, ecx);
6487
6488 __ bind(&done);
6489 // Compare operands.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006490 __ ucomisd(xmm0, xmm1);
6491
6492 // Don't base result on EFLAGS when a NaN is involved.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00006493 __ j(parity_even, &unordered, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006494
6495 // Return a result of -1, 0, or 1, based on EFLAGS.
6496 // Performing mov, because xor would destroy the flag register.
6497 __ mov(eax, 0); // equal
6498 __ mov(ecx, Immediate(Smi::FromInt(1)));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006499 __ cmov(above, eax, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006500 __ mov(ecx, Immediate(Smi::FromInt(-1)));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006501 __ cmov(below, eax, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006502 __ ret(0);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006503 } else {
6504 __ mov(ecx, edx);
6505 __ and_(ecx, eax);
6506 __ JumpIfSmi(ecx, &generic_stub, Label::kNear);
6507
6508 __ cmp(FieldOperand(eax, HeapObject::kMapOffset),
6509 masm->isolate()->factory()->heap_number_map());
6510 __ j(not_equal, &maybe_undefined1, Label::kNear);
6511 __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
6512 masm->isolate()->factory()->heap_number_map());
6513 __ j(not_equal, &maybe_undefined2, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006514 }
6515
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00006516 __ bind(&unordered);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006517 __ bind(&generic_stub);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006518 ICCompareStub stub(op_, CompareIC::GENERIC, CompareIC::GENERIC,
6519 CompareIC::GENERIC);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00006520 __ jmp(stub.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006521
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00006522 __ bind(&maybe_undefined1);
6523 if (Token::IsOrderedRelationalCompareOp(op_)) {
6524 __ cmp(eax, Immediate(masm->isolate()->factory()->undefined_value()));
6525 __ j(not_equal, &miss);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006526 __ JumpIfSmi(edx, &unordered);
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00006527 __ CmpObjectType(edx, HEAP_NUMBER_TYPE, ecx);
6528 __ j(not_equal, &maybe_undefined2, Label::kNear);
6529 __ jmp(&unordered);
6530 }
6531
6532 __ bind(&maybe_undefined2);
6533 if (Token::IsOrderedRelationalCompareOp(op_)) {
6534 __ cmp(edx, Immediate(masm->isolate()->factory()->undefined_value()));
6535 __ j(equal, &unordered);
6536 }
6537
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006538 __ bind(&miss);
6539 GenerateMiss(masm);
6540}
6541
6542
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006543void ICCompareStub::GenerateInternalizedStrings(MacroAssembler* masm) {
6544 ASSERT(state_ == CompareIC::INTERNALIZED_STRING);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006545 ASSERT(GetCondition() == equal);
6546
6547 // Registers containing left and right operands respectively.
6548 Register left = edx;
6549 Register right = eax;
6550 Register tmp1 = ecx;
6551 Register tmp2 = ebx;
6552
6553 // Check that both operands are heap objects.
6554 Label miss;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006555 __ mov(tmp1, left);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006556 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006557 __ and_(tmp1, right);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006558 __ JumpIfSmi(tmp1, &miss, Label::kNear);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006559
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006560 // Check that both operands are internalized strings.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006561 __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset));
6562 __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset));
6563 __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
6564 __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006565 STATIC_ASSERT(kInternalizedTag != 0);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00006566 __ and_(tmp1, Immediate(kIsNotStringMask | kIsInternalizedMask));
6567 __ cmpb(tmp1, kInternalizedTag | kStringTag);
6568 __ j(not_equal, &miss, Label::kNear);
6569
6570 __ and_(tmp2, Immediate(kIsNotStringMask | kIsInternalizedMask));
6571 __ cmpb(tmp2, kInternalizedTag | kStringTag);
6572 __ j(not_equal, &miss, Label::kNear);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006573
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006574 // Internalized strings are compared by identity.
6575 Label done;
6576 __ cmp(left, right);
6577 // Make sure eax is non-zero. At this point input operands are
6578 // guaranteed to be non-zero.
6579 ASSERT(right.is(eax));
6580 __ j(not_equal, &done, Label::kNear);
6581 STATIC_ASSERT(EQUAL == 0);
6582 STATIC_ASSERT(kSmiTag == 0);
6583 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6584 __ bind(&done);
6585 __ ret(0);
6586
6587 __ bind(&miss);
6588 GenerateMiss(masm);
6589}
6590
6591
6592void ICCompareStub::GenerateUniqueNames(MacroAssembler* masm) {
6593 ASSERT(state_ == CompareIC::UNIQUE_NAME);
6594 ASSERT(GetCondition() == equal);
6595
6596 // Registers containing left and right operands respectively.
6597 Register left = edx;
6598 Register right = eax;
6599 Register tmp1 = ecx;
6600 Register tmp2 = ebx;
6601
6602 // Check that both operands are heap objects.
6603 Label miss;
6604 __ mov(tmp1, left);
6605 STATIC_ASSERT(kSmiTag == 0);
6606 __ and_(tmp1, right);
6607 __ JumpIfSmi(tmp1, &miss, Label::kNear);
6608
6609 // Check that both operands are unique names. This leaves the instance
6610 // types loaded in tmp1 and tmp2.
6611 STATIC_ASSERT(kInternalizedTag != 0);
6612 __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset));
6613 __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset));
6614 __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
6615 __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
6616
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00006617 __ JumpIfNotUniqueName(tmp1, &miss, Label::kNear);
6618 __ JumpIfNotUniqueName(tmp2, &miss, Label::kNear);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006619
6620 // Unique names are compared by identity.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006621 Label done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006622 __ cmp(left, right);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006623 // Make sure eax is non-zero. At this point input operands are
6624 // guaranteed to be non-zero.
6625 ASSERT(right.is(eax));
6626 __ j(not_equal, &done, Label::kNear);
6627 STATIC_ASSERT(EQUAL == 0);
6628 STATIC_ASSERT(kSmiTag == 0);
6629 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6630 __ bind(&done);
6631 __ ret(0);
6632
6633 __ bind(&miss);
6634 GenerateMiss(masm);
6635}
6636
6637
lrn@chromium.org1c092762011-05-09 09:42:16 +00006638void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006639 ASSERT(state_ == CompareIC::STRING);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006640 Label miss;
6641
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00006642 bool equality = Token::IsEqualityOp(op_);
6643
lrn@chromium.org1c092762011-05-09 09:42:16 +00006644 // Registers containing left and right operands respectively.
6645 Register left = edx;
6646 Register right = eax;
6647 Register tmp1 = ecx;
6648 Register tmp2 = ebx;
6649 Register tmp3 = edi;
6650
6651 // Check that both operands are heap objects.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006652 __ mov(tmp1, left);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006653 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006654 __ and_(tmp1, right);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006655 __ JumpIfSmi(tmp1, &miss);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006656
6657 // Check that both operands are strings. This leaves the instance
6658 // types loaded in tmp1 and tmp2.
6659 __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset));
6660 __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset));
6661 __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
6662 __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
6663 __ mov(tmp3, tmp1);
6664 STATIC_ASSERT(kNotStringTag != 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006665 __ or_(tmp3, tmp2);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006666 __ test(tmp3, Immediate(kIsNotStringMask));
6667 __ j(not_zero, &miss);
6668
6669 // Fast check for identical strings.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006670 Label not_same;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006671 __ cmp(left, right);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006672 __ j(not_equal, &not_same, Label::kNear);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006673 STATIC_ASSERT(EQUAL == 0);
6674 STATIC_ASSERT(kSmiTag == 0);
6675 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6676 __ ret(0);
6677
6678 // Handle not identical strings.
6679 __ bind(&not_same);
6680
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006681 // Check that both strings are internalized. If they are, we're done
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00006682 // because we already know they are not identical. But in the case of
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00006683 // non-equality compare, we still need to determine the order. We
6684 // also know they are both strings.
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00006685 if (equality) {
6686 Label do_compare;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006687 STATIC_ASSERT(kInternalizedTag != 0);
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00006688 __ and_(tmp1, tmp2);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006689 __ test(tmp1, Immediate(kIsInternalizedMask));
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00006690 __ j(zero, &do_compare, Label::kNear);
6691 // Make sure eax is non-zero. At this point input operands are
6692 // guaranteed to be non-zero.
6693 ASSERT(right.is(eax));
6694 __ ret(0);
6695 __ bind(&do_compare);
6696 }
lrn@chromium.org1c092762011-05-09 09:42:16 +00006697
6698 // Check that both strings are sequential ASCII.
6699 Label runtime;
lrn@chromium.org1c092762011-05-09 09:42:16 +00006700 __ JumpIfNotBothSequentialAsciiStrings(left, right, tmp1, tmp2, &runtime);
6701
6702 // Compare flat ASCII strings. Returns when done.
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00006703 if (equality) {
6704 StringCompareStub::GenerateFlatAsciiStringEquals(
6705 masm, left, right, tmp1, tmp2);
6706 } else {
6707 StringCompareStub::GenerateCompareFlatAsciiStrings(
6708 masm, left, right, tmp1, tmp2, tmp3);
6709 }
lrn@chromium.org1c092762011-05-09 09:42:16 +00006710
6711 // Handle more complex cases in runtime.
6712 __ bind(&runtime);
6713 __ pop(tmp1); // Return address.
6714 __ push(left);
6715 __ push(right);
6716 __ push(tmp1);
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00006717 if (equality) {
6718 __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
6719 } else {
6720 __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
6721 }
lrn@chromium.org1c092762011-05-09 09:42:16 +00006722
6723 __ bind(&miss);
6724 GenerateMiss(masm);
6725}
6726
6727
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006728void ICCompareStub::GenerateObjects(MacroAssembler* masm) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006729 ASSERT(state_ == CompareIC::OBJECT);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006730 Label miss;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006731 __ mov(ecx, edx);
6732 __ and_(ecx, eax);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006733 __ JumpIfSmi(ecx, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006734
6735 __ CmpObjectType(eax, JS_OBJECT_TYPE, ecx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00006736 __ j(not_equal, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006737 __ CmpObjectType(edx, JS_OBJECT_TYPE, ecx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00006738 __ j(not_equal, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006739
6740 ASSERT(GetCondition() == equal);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006741 __ sub(eax, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006742 __ ret(0);
6743
6744 __ bind(&miss);
6745 GenerateMiss(masm);
6746}
6747
6748
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006749void ICCompareStub::GenerateKnownObjects(MacroAssembler* masm) {
6750 Label miss;
6751 __ mov(ecx, edx);
6752 __ and_(ecx, eax);
6753 __ JumpIfSmi(ecx, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006754
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006755 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
6756 __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
6757 __ cmp(ecx, known_map_);
6758 __ j(not_equal, &miss, Label::kNear);
6759 __ cmp(ebx, known_map_);
6760 __ j(not_equal, &miss, Label::kNear);
6761
6762 __ sub(eax, edx);
6763 __ ret(0);
6764
6765 __ bind(&miss);
6766 GenerateMiss(masm);
6767}
6768
6769
6770void ICCompareStub::GenerateMiss(MacroAssembler* masm) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006771 {
6772 // Call the runtime system in a fresh internal frame.
6773 ExternalReference miss = ExternalReference(IC_Utility(IC::kCompareIC_Miss),
6774 masm->isolate());
6775 FrameScope scope(masm, StackFrame::INTERNAL);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006776 __ push(edx); // Preserve edx and eax.
6777 __ push(eax);
6778 __ push(edx); // And also use them as the arguments.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006779 __ push(eax);
6780 __ push(Immediate(Smi::FromInt(op_)));
6781 __ CallExternalReference(miss, 3);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006782 // Compute the entry point of the rewritten stub.
6783 __ lea(edi, FieldOperand(eax, Code::kHeaderSize));
6784 __ pop(eax);
6785 __ pop(edx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006786 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006787
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006788 // Do a tail call to the rewritten stub.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006789 __ jmp(edi);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006790}
6791
6792
lrn@chromium.org1c092762011-05-09 09:42:16 +00006793// Helper function used to check that the dictionary doesn't contain
6794// the property. This function may return false negatives, so miss_label
6795// must always call a backup property check that is complete.
6796// This function is safe to call if the receiver has fast properties.
ulan@chromium.org750145a2013-03-07 15:14:13 +00006797// Name must be a unique name and receiver must be a heap object.
6798void NameDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm,
6799 Label* miss,
6800 Label* done,
6801 Register properties,
6802 Handle<Name> name,
6803 Register r0) {
6804 ASSERT(name->IsUniqueName());
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006805
6806 // If names of slots in range from 1 to kProbes - 1 for the hash value are
6807 // not equal to the name and kProbes-th slot is not used (its name is the
6808 // undefined value), it guarantees the hash table doesn't contain the
6809 // property. It's true even if some slots represent deleted properties
ulan@chromium.org967e2702012-02-28 09:49:15 +00006810 // (their names are the hole value).
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006811 for (int i = 0; i < kInlinedProbes; i++) {
6812 // Compute the masked index: (hash + i + i * i) & mask.
6813 Register index = r0;
6814 // Capacity is smi 2^n.
6815 __ mov(index, FieldOperand(properties, kCapacityOffset));
6816 __ dec(index);
6817 __ and_(index,
6818 Immediate(Smi::FromInt(name->Hash() +
ulan@chromium.org750145a2013-03-07 15:14:13 +00006819 NameDictionary::GetProbeOffset(i))));
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006820
6821 // Scale the index by multiplying by the entry size.
ulan@chromium.org750145a2013-03-07 15:14:13 +00006822 ASSERT(NameDictionary::kEntrySize == 3);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006823 __ lea(index, Operand(index, index, times_2, 0)); // index *= 3.
6824 Register entity_name = r0;
6825 // Having undefined at this place means the name is not contained.
6826 ASSERT_EQ(kSmiTagSize, 1);
6827 __ mov(entity_name, Operand(properties, index, times_half_pointer_size,
6828 kElementsStartOffset - kHeapObjectTag));
6829 __ cmp(entity_name, masm->isolate()->factory()->undefined_value());
6830 __ j(equal, done);
6831
6832 // Stop if found the property.
ulan@chromium.org750145a2013-03-07 15:14:13 +00006833 __ cmp(entity_name, Handle<Name>(name));
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006834 __ j(equal, miss);
6835
ulan@chromium.org750145a2013-03-07 15:14:13 +00006836 Label good;
ulan@chromium.org967e2702012-02-28 09:49:15 +00006837 // Check for the hole and skip.
6838 __ cmp(entity_name, masm->isolate()->factory()->the_hole_value());
ulan@chromium.org750145a2013-03-07 15:14:13 +00006839 __ j(equal, &good, Label::kNear);
ulan@chromium.org967e2702012-02-28 09:49:15 +00006840
ulan@chromium.org750145a2013-03-07 15:14:13 +00006841 // Check if the entry name is not a unique name.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006842 __ mov(entity_name, FieldOperand(entity_name, HeapObject::kMapOffset));
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00006843 __ JumpIfNotUniqueName(FieldOperand(entity_name, Map::kInstanceTypeOffset),
6844 miss);
ulan@chromium.org750145a2013-03-07 15:14:13 +00006845 __ bind(&good);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006846 }
6847
ulan@chromium.org750145a2013-03-07 15:14:13 +00006848 NameDictionaryLookupStub stub(properties, r0, r0, NEGATIVE_LOOKUP);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006849 __ push(Immediate(Handle<Object>(name)));
6850 __ push(Immediate(name->Hash()));
6851 __ CallStub(&stub);
6852 __ test(r0, r0);
6853 __ j(not_zero, miss);
6854 __ jmp(done);
6855}
6856
6857
ulan@chromium.org750145a2013-03-07 15:14:13 +00006858// Probe the name dictionary in the |elements| register. Jump to the
lrn@chromium.org1c092762011-05-09 09:42:16 +00006859// |done| label if a property with the given name is found leaving the
6860// index into the dictionary in |r0|. Jump to the |miss| label
6861// otherwise.
ulan@chromium.org750145a2013-03-07 15:14:13 +00006862void NameDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm,
6863 Label* miss,
6864 Label* done,
6865 Register elements,
6866 Register name,
6867 Register r0,
6868 Register r1) {
erik.corry@gmail.com6e28b562011-10-27 14:20:17 +00006869 ASSERT(!elements.is(r0));
6870 ASSERT(!elements.is(r1));
6871 ASSERT(!name.is(r0));
6872 ASSERT(!name.is(r1));
6873
ulan@chromium.org750145a2013-03-07 15:14:13 +00006874 __ AssertName(name);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006875
6876 __ mov(r1, FieldOperand(elements, kCapacityOffset));
6877 __ shr(r1, kSmiTagSize); // convert smi to int
6878 __ dec(r1);
6879
6880 // Generate an unrolled loop that performs a few probes before
6881 // giving up. Measurements done on Gmail indicate that 2 probes
6882 // cover ~93% of loads from dictionaries.
6883 for (int i = 0; i < kInlinedProbes; i++) {
6884 // Compute the masked index: (hash + i + i * i) & mask.
ulan@chromium.org750145a2013-03-07 15:14:13 +00006885 __ mov(r0, FieldOperand(name, Name::kHashFieldOffset));
6886 __ shr(r0, Name::kHashShift);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006887 if (i > 0) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00006888 __ add(r0, Immediate(NameDictionary::GetProbeOffset(i)));
lrn@chromium.org1c092762011-05-09 09:42:16 +00006889 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006890 __ and_(r0, r1);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006891
6892 // Scale the index by multiplying by the entry size.
ulan@chromium.org750145a2013-03-07 15:14:13 +00006893 ASSERT(NameDictionary::kEntrySize == 3);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006894 __ lea(r0, Operand(r0, r0, times_2, 0)); // r0 = r0 * 3
6895
6896 // Check if the key is identical to the name.
6897 __ cmp(name, Operand(elements,
6898 r0,
6899 times_4,
6900 kElementsStartOffset - kHeapObjectTag));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00006901 __ j(equal, done);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006902 }
6903
ulan@chromium.org750145a2013-03-07 15:14:13 +00006904 NameDictionaryLookupStub stub(elements, r1, r0, POSITIVE_LOOKUP);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006905 __ push(name);
ulan@chromium.org750145a2013-03-07 15:14:13 +00006906 __ mov(r0, FieldOperand(name, Name::kHashFieldOffset));
6907 __ shr(r0, Name::kHashShift);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006908 __ push(r0);
6909 __ CallStub(&stub);
6910
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006911 __ test(r1, r1);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006912 __ j(zero, miss);
6913 __ jmp(done);
6914}
6915
6916
ulan@chromium.org750145a2013-03-07 15:14:13 +00006917void NameDictionaryLookupStub::Generate(MacroAssembler* masm) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006918 // This stub overrides SometimesSetsUpAFrame() to return false. That means
6919 // we cannot call anything that could cause a GC from this stub.
lrn@chromium.org1c092762011-05-09 09:42:16 +00006920 // Stack frame on entry:
6921 // esp[0 * kPointerSize]: return address.
6922 // esp[1 * kPointerSize]: key's hash.
6923 // esp[2 * kPointerSize]: key.
6924 // Registers:
ulan@chromium.org750145a2013-03-07 15:14:13 +00006925 // dictionary_: NameDictionary to probe.
lrn@chromium.org1c092762011-05-09 09:42:16 +00006926 // result_: used as scratch.
6927 // index_: will hold an index of entry if lookup is successful.
6928 // might alias with result_.
6929 // Returns:
6930 // result_ is zero if lookup failed, non zero otherwise.
6931
6932 Label in_dictionary, maybe_in_dictionary, not_in_dictionary;
6933
6934 Register scratch = result_;
6935
6936 __ mov(scratch, FieldOperand(dictionary_, kCapacityOffset));
6937 __ dec(scratch);
6938 __ SmiUntag(scratch);
6939 __ push(scratch);
6940
6941 // If names of slots in range from 1 to kProbes - 1 for the hash value are
6942 // not equal to the name and kProbes-th slot is not used (its name is the
6943 // undefined value), it guarantees the hash table doesn't contain the
6944 // property. It's true even if some slots represent deleted properties
6945 // (their names are the null value).
6946 for (int i = kInlinedProbes; i < kTotalProbes; i++) {
6947 // Compute the masked index: (hash + i + i * i) & mask.
6948 __ mov(scratch, Operand(esp, 2 * kPointerSize));
6949 if (i > 0) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00006950 __ add(scratch, Immediate(NameDictionary::GetProbeOffset(i)));
lrn@chromium.org1c092762011-05-09 09:42:16 +00006951 }
6952 __ and_(scratch, Operand(esp, 0));
6953
6954 // Scale the index by multiplying by the entry size.
ulan@chromium.org750145a2013-03-07 15:14:13 +00006955 ASSERT(NameDictionary::kEntrySize == 3);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006956 __ lea(index_, Operand(scratch, scratch, times_2, 0)); // index *= 3.
6957
6958 // Having undefined at this place means the name is not contained.
6959 ASSERT_EQ(kSmiTagSize, 1);
6960 __ mov(scratch, Operand(dictionary_,
6961 index_,
6962 times_pointer_size,
6963 kElementsStartOffset - kHeapObjectTag));
6964 __ cmp(scratch, masm->isolate()->factory()->undefined_value());
6965 __ j(equal, &not_in_dictionary);
6966
6967 // Stop if found the property.
6968 __ cmp(scratch, Operand(esp, 3 * kPointerSize));
6969 __ j(equal, &in_dictionary);
6970
6971 if (i != kTotalProbes - 1 && mode_ == NEGATIVE_LOOKUP) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00006972 // If we hit a key that is not a unique name during negative
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006973 // lookup we have to bailout as this key might be equal to the
lrn@chromium.org1c092762011-05-09 09:42:16 +00006974 // key we are looking for.
6975
ulan@chromium.org750145a2013-03-07 15:14:13 +00006976 // Check if the entry name is not a unique name.
lrn@chromium.org1c092762011-05-09 09:42:16 +00006977 __ mov(scratch, FieldOperand(scratch, HeapObject::kMapOffset));
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00006978 __ JumpIfNotUniqueName(FieldOperand(scratch, Map::kInstanceTypeOffset),
6979 &maybe_in_dictionary);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006980 }
6981 }
6982
6983 __ bind(&maybe_in_dictionary);
6984 // If we are doing negative lookup then probing failure should be
6985 // treated as a lookup success. For positive lookup probing failure
6986 // should be treated as lookup failure.
6987 if (mode_ == POSITIVE_LOOKUP) {
6988 __ mov(result_, Immediate(0));
6989 __ Drop(1);
6990 __ ret(2 * kPointerSize);
6991 }
6992
6993 __ bind(&in_dictionary);
6994 __ mov(result_, Immediate(1));
6995 __ Drop(1);
6996 __ ret(2 * kPointerSize);
6997
6998 __ bind(&not_in_dictionary);
6999 __ mov(result_, Immediate(0));
7000 __ Drop(1);
7001 __ ret(2 * kPointerSize);
7002}
7003
7004
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007005struct AheadOfTimeWriteBarrierStubList {
7006 Register object, value, address;
7007 RememberedSetAction action;
7008};
7009
7010
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007011#define REG(Name) { kRegister_ ## Name ## _Code }
7012
7013static const AheadOfTimeWriteBarrierStubList kAheadOfTime[] = {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007014 // Used in RegExpExecStub.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007015 { REG(ebx), REG(eax), REG(edi), EMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007016 // Used in CompileArrayPushCall.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007017 { REG(ebx), REG(ecx), REG(edx), EMIT_REMEMBERED_SET },
7018 { REG(ebx), REG(edi), REG(edx), OMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007019 // Used in CompileStoreGlobal and CallFunctionStub.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007020 { REG(ebx), REG(ecx), REG(edx), OMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007021 // Used in StoreStubCompiler::CompileStoreField and
7022 // KeyedStoreStubCompiler::CompileStoreField via GenerateStoreField.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007023 { REG(edx), REG(ecx), REG(ebx), EMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007024 // GenerateStoreField calls the stub with two different permutations of
7025 // registers. This is the second.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007026 { REG(ebx), REG(ecx), REG(edx), EMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007027 // StoreIC::GenerateNormal via GenerateDictionaryStore
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007028 { REG(ebx), REG(edi), REG(edx), EMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007029 // KeyedStoreIC::GenerateGeneric.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007030 { REG(ebx), REG(edx), REG(ecx), EMIT_REMEMBERED_SET},
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007031 // KeyedStoreStubCompiler::GenerateStoreFastElement.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007032 { REG(edi), REG(ebx), REG(ecx), EMIT_REMEMBERED_SET},
7033 { REG(edx), REG(edi), REG(ebx), EMIT_REMEMBERED_SET},
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00007034 // ElementsTransitionGenerator::GenerateMapChangeElementTransition
7035 // and ElementsTransitionGenerator::GenerateSmiToDouble
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007036 // and ElementsTransitionGenerator::GenerateDoubleToObject
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007037 { REG(edx), REG(ebx), REG(edi), EMIT_REMEMBERED_SET},
7038 { REG(edx), REG(ebx), REG(edi), OMIT_REMEMBERED_SET},
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00007039 // ElementsTransitionGenerator::GenerateDoubleToObject
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007040 { REG(eax), REG(edx), REG(esi), EMIT_REMEMBERED_SET},
7041 { REG(edx), REG(eax), REG(edi), EMIT_REMEMBERED_SET},
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007042 // StoreArrayLiteralElementStub::Generate
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007043 { REG(ebx), REG(eax), REG(ecx), EMIT_REMEMBERED_SET},
ulan@chromium.org57ff8812013-05-10 08:16:55 +00007044 // FastNewClosureStub and StringAddStub::Generate
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00007045 { REG(ecx), REG(edx), REG(ebx), EMIT_REMEMBERED_SET},
ulan@chromium.org57ff8812013-05-10 08:16:55 +00007046 // StringAddStub::Generate
7047 { REG(ecx), REG(eax), REG(ebx), EMIT_REMEMBERED_SET},
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007048 // Null termination.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007049 { REG(no_reg), REG(no_reg), REG(no_reg), EMIT_REMEMBERED_SET}
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007050};
7051
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007052#undef REG
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007053
7054bool RecordWriteStub::IsPregenerated() {
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007055 for (const AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007056 !entry->object.is(no_reg);
7057 entry++) {
7058 if (object_.is(entry->object) &&
7059 value_.is(entry->value) &&
7060 address_.is(entry->address) &&
7061 remembered_set_action_ == entry->action &&
7062 save_fp_regs_mode_ == kDontSaveFPRegs) {
7063 return true;
7064 }
7065 }
7066 return false;
7067}
7068
7069
hpayer@chromium.org8432c912013-02-28 15:55:26 +00007070void StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(
7071 Isolate* isolate) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00007072 StoreBufferOverflowStub stub(kDontSaveFPRegs);
7073 stub.GetCode(isolate)->set_is_pregenerated(true);
7074 if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007075 StoreBufferOverflowStub stub2(kSaveFPRegs);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00007076 stub2.GetCode(isolate)->set_is_pregenerated(true);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007077 }
7078}
7079
7080
hpayer@chromium.org8432c912013-02-28 15:55:26 +00007081void RecordWriteStub::GenerateFixedRegStubsAheadOfTime(Isolate* isolate) {
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00007082 for (const AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007083 !entry->object.is(no_reg);
7084 entry++) {
7085 RecordWriteStub stub(entry->object,
7086 entry->value,
7087 entry->address,
7088 entry->action,
7089 kDontSaveFPRegs);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00007090 stub.GetCode(isolate)->set_is_pregenerated(true);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007091 }
7092}
7093
7094
verwaest@chromium.org33e09c82012-10-10 17:07:22 +00007095bool CodeStub::CanUseFPRegisters() {
7096 return CpuFeatures::IsSupported(SSE2);
7097}
7098
7099
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007100// Takes the input in 3 registers: address_ value_ and object_. A pointer to
7101// the value has just been written into the object, now this stub makes sure
7102// we keep the GC informed. The word in the object where the value has been
7103// written is in the address register.
7104void RecordWriteStub::Generate(MacroAssembler* masm) {
7105 Label skip_to_incremental_noncompacting;
7106 Label skip_to_incremental_compacting;
7107
7108 // The first two instructions are generated with labels so as to get the
7109 // offset fixed up correctly by the bind(Label*) call. We patch it back and
7110 // forth between a compare instructions (a nop in this position) and the
7111 // real branch when we start and stop incremental heap marking.
7112 __ jmp(&skip_to_incremental_noncompacting, Label::kNear);
7113 __ jmp(&skip_to_incremental_compacting, Label::kFar);
7114
7115 if (remembered_set_action_ == EMIT_REMEMBERED_SET) {
7116 __ RememberedSetHelper(object_,
7117 address_,
7118 value_,
7119 save_fp_regs_mode_,
7120 MacroAssembler::kReturnAtEnd);
7121 } else {
7122 __ ret(0);
7123 }
7124
7125 __ bind(&skip_to_incremental_noncompacting);
7126 GenerateIncremental(masm, INCREMENTAL);
7127
7128 __ bind(&skip_to_incremental_compacting);
7129 GenerateIncremental(masm, INCREMENTAL_COMPACTION);
7130
7131 // Initial mode of the stub is expected to be STORE_BUFFER_ONLY.
7132 // Will be checked in IncrementalMarking::ActivateGeneratedStub.
7133 masm->set_byte_at(0, kTwoByteNopInstruction);
7134 masm->set_byte_at(2, kFiveByteNopInstruction);
7135}
7136
7137
7138void RecordWriteStub::GenerateIncremental(MacroAssembler* masm, Mode mode) {
7139 regs_.Save(masm);
7140
7141 if (remembered_set_action_ == EMIT_REMEMBERED_SET) {
7142 Label dont_need_remembered_set;
7143
7144 __ mov(regs_.scratch0(), Operand(regs_.address(), 0));
7145 __ JumpIfNotInNewSpace(regs_.scratch0(), // Value.
7146 regs_.scratch0(),
7147 &dont_need_remembered_set);
7148
7149 __ CheckPageFlag(regs_.object(),
7150 regs_.scratch0(),
7151 1 << MemoryChunk::SCAN_ON_SCAVENGE,
7152 not_zero,
7153 &dont_need_remembered_set);
7154
7155 // First notify the incremental marker if necessary, then update the
7156 // remembered set.
7157 CheckNeedsToInformIncrementalMarker(
7158 masm,
7159 kUpdateRememberedSetOnNoNeedToInformIncrementalMarker,
7160 mode);
7161 InformIncrementalMarker(masm, mode);
7162 regs_.Restore(masm);
7163 __ RememberedSetHelper(object_,
7164 address_,
7165 value_,
7166 save_fp_regs_mode_,
7167 MacroAssembler::kReturnAtEnd);
7168
7169 __ bind(&dont_need_remembered_set);
7170 }
7171
7172 CheckNeedsToInformIncrementalMarker(
7173 masm,
7174 kReturnOnNoNeedToInformIncrementalMarker,
7175 mode);
7176 InformIncrementalMarker(masm, mode);
7177 regs_.Restore(masm);
7178 __ ret(0);
7179}
7180
7181
7182void RecordWriteStub::InformIncrementalMarker(MacroAssembler* masm, Mode mode) {
7183 regs_.SaveCallerSaveRegisters(masm, save_fp_regs_mode_);
7184 int argument_count = 3;
7185 __ PrepareCallCFunction(argument_count, regs_.scratch0());
7186 __ mov(Operand(esp, 0 * kPointerSize), regs_.object());
ulan@chromium.org8e8d8822012-11-23 14:36:46 +00007187 __ mov(Operand(esp, 1 * kPointerSize), regs_.address()); // Slot.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007188 __ mov(Operand(esp, 2 * kPointerSize),
ulan@chromium.org32d7dba2013-04-24 10:59:06 +00007189 Immediate(ExternalReference::isolate_address(masm->isolate())));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007190
7191 AllowExternalCallThatCantCauseGC scope(masm);
7192 if (mode == INCREMENTAL_COMPACTION) {
7193 __ CallCFunction(
7194 ExternalReference::incremental_evacuation_record_write_function(
7195 masm->isolate()),
7196 argument_count);
7197 } else {
7198 ASSERT(mode == INCREMENTAL);
7199 __ CallCFunction(
7200 ExternalReference::incremental_marking_record_write_function(
7201 masm->isolate()),
7202 argument_count);
7203 }
7204 regs_.RestoreCallerSaveRegisters(masm, save_fp_regs_mode_);
7205}
7206
7207
7208void RecordWriteStub::CheckNeedsToInformIncrementalMarker(
7209 MacroAssembler* masm,
7210 OnNoNeedToInformIncrementalMarker on_no_need,
7211 Mode mode) {
7212 Label object_is_black, need_incremental, need_incremental_pop_object;
7213
verwaest@chromium.org33e09c82012-10-10 17:07:22 +00007214 __ mov(regs_.scratch0(), Immediate(~Page::kPageAlignmentMask));
7215 __ and_(regs_.scratch0(), regs_.object());
7216 __ mov(regs_.scratch1(),
7217 Operand(regs_.scratch0(),
7218 MemoryChunk::kWriteBarrierCounterOffset));
7219 __ sub(regs_.scratch1(), Immediate(1));
7220 __ mov(Operand(regs_.scratch0(),
7221 MemoryChunk::kWriteBarrierCounterOffset),
7222 regs_.scratch1());
7223 __ j(negative, &need_incremental);
7224
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007225 // Let's look at the color of the object: If it is not black we don't have
7226 // to inform the incremental marker.
7227 __ JumpIfBlack(regs_.object(),
7228 regs_.scratch0(),
7229 regs_.scratch1(),
7230 &object_is_black,
7231 Label::kNear);
7232
7233 regs_.Restore(masm);
7234 if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) {
7235 __ RememberedSetHelper(object_,
7236 address_,
7237 value_,
7238 save_fp_regs_mode_,
7239 MacroAssembler::kReturnAtEnd);
7240 } else {
7241 __ ret(0);
7242 }
7243
7244 __ bind(&object_is_black);
7245
7246 // Get the value from the slot.
7247 __ mov(regs_.scratch0(), Operand(regs_.address(), 0));
7248
7249 if (mode == INCREMENTAL_COMPACTION) {
7250 Label ensure_not_white;
7251
7252 __ CheckPageFlag(regs_.scratch0(), // Contains value.
7253 regs_.scratch1(), // Scratch.
7254 MemoryChunk::kEvacuationCandidateMask,
7255 zero,
7256 &ensure_not_white,
7257 Label::kNear);
7258
7259 __ CheckPageFlag(regs_.object(),
7260 regs_.scratch1(), // Scratch.
7261 MemoryChunk::kSkipEvacuationSlotsRecordingMask,
7262 not_zero,
7263 &ensure_not_white,
7264 Label::kNear);
7265
7266 __ jmp(&need_incremental);
7267
7268 __ bind(&ensure_not_white);
7269 }
7270
7271 // We need an extra register for this, so we push the object register
7272 // temporarily.
7273 __ push(regs_.object());
7274 __ EnsureNotWhite(regs_.scratch0(), // The value.
7275 regs_.scratch1(), // Scratch.
7276 regs_.object(), // Scratch.
7277 &need_incremental_pop_object,
7278 Label::kNear);
7279 __ pop(regs_.object());
7280
7281 regs_.Restore(masm);
7282 if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) {
7283 __ RememberedSetHelper(object_,
7284 address_,
7285 value_,
7286 save_fp_regs_mode_,
7287 MacroAssembler::kReturnAtEnd);
7288 } else {
7289 __ ret(0);
7290 }
7291
7292 __ bind(&need_incremental_pop_object);
7293 __ pop(regs_.object());
7294
7295 __ bind(&need_incremental);
7296
7297 // Fall through when we need to inform the incremental marker.
7298}
7299
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007300
7301void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) {
7302 // ----------- S t a t e -------------
7303 // -- eax : element value to store
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007304 // -- ecx : element index as smi
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007305 // -- esp[0] : return address
dslomov@chromium.orgb752d402013-06-18 11:54:54 +00007306 // -- esp[4] : array literal index in function
7307 // -- esp[8] : array literal
7308 // clobbers ebx, edx, edi
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007309 // -----------------------------------
7310
7311 Label element_done;
7312 Label double_elements;
7313 Label smi_element;
7314 Label slow_elements;
7315 Label slow_elements_from_double;
7316 Label fast_elements;
7317
dslomov@chromium.orgb752d402013-06-18 11:54:54 +00007318 // Get array literal index, array literal and its map.
7319 __ mov(edx, Operand(esp, 1 * kPointerSize));
7320 __ mov(ebx, Operand(esp, 2 * kPointerSize));
7321 __ mov(edi, FieldOperand(ebx, JSObject::kMapOffset));
7322
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007323 __ CheckFastElements(edi, &double_elements);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007324
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00007325 // Check for FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS elements
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007326 __ JumpIfSmi(eax, &smi_element);
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00007327 __ CheckFastSmiElements(edi, &fast_elements, Label::kNear);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007328
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007329 // Store into the array literal requires a elements transition. Call into
7330 // the runtime.
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007331
7332 __ bind(&slow_elements);
7333 __ pop(edi); // Pop return address and remember to put back later for tail
7334 // call.
7335 __ push(ebx);
7336 __ push(ecx);
7337 __ push(eax);
7338 __ mov(ebx, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
7339 __ push(FieldOperand(ebx, JSFunction::kLiteralsOffset));
7340 __ push(edx);
7341 __ push(edi); // Return return address so that tail call returns to right
7342 // place.
7343 __ TailCallRuntime(Runtime::kStoreArrayLiteralElement, 5, 1);
7344
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007345 __ bind(&slow_elements_from_double);
7346 __ pop(edx);
7347 __ jmp(&slow_elements);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007348
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00007349 // Array literal has ElementsKind of FAST_*_ELEMENTS and value is an object.
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007350 __ bind(&fast_elements);
7351 __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset));
7352 __ lea(ecx, FieldOperand(ebx, ecx, times_half_pointer_size,
7353 FixedArrayBase::kHeaderSize));
7354 __ mov(Operand(ecx, 0), eax);
7355 // Update the write barrier for the array store.
7356 __ RecordWrite(ebx, ecx, eax,
7357 kDontSaveFPRegs,
7358 EMIT_REMEMBERED_SET,
7359 OMIT_SMI_CHECK);
7360 __ ret(0);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007361
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00007362 // Array literal has ElementsKind of FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS,
7363 // and value is Smi.
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007364 __ bind(&smi_element);
7365 __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset));
7366 __ mov(FieldOperand(ebx, ecx, times_half_pointer_size,
7367 FixedArrayBase::kHeaderSize), eax);
7368 __ ret(0);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007369
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00007370 // Array literal has ElementsKind of FAST_*_DOUBLE_ELEMENTS.
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007371 __ bind(&double_elements);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007372
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007373 __ push(edx);
7374 __ mov(edx, FieldOperand(ebx, JSObject::kElementsOffset));
7375 __ StoreNumberToDoubleElements(eax,
7376 edx,
7377 ecx,
7378 edi,
7379 xmm0,
7380 &slow_elements_from_double,
7381 false);
7382 __ pop(edx);
7383 __ ret(0);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007384}
7385
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007386
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00007387void StubFailureTrampolineStub::Generate(MacroAssembler* masm) {
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00007388 CEntryStub ces(1, fp_registers_ ? kSaveFPRegs : kDontSaveFPRegs);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00007389 __ call(ces.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
mmassi@chromium.org2f0efde2013-02-06 14:12:58 +00007390 int parameter_count_offset =
7391 StubFailureTrampolineFrame::kCallerStackParameterCountFrameOffset;
7392 __ mov(ebx, MemOperand(ebp, parameter_count_offset));
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00007393 masm->LeaveFrame(StackFrame::STUB_FAILURE_TRAMPOLINE);
mmassi@chromium.org2f0efde2013-02-06 14:12:58 +00007394 __ pop(ecx);
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00007395 int additional_offset = function_mode_ == JS_FUNCTION_STUB_MODE
7396 ? kPointerSize
7397 : 0;
7398 __ lea(esp, MemOperand(esp, ebx, times_pointer_size, additional_offset));
mmassi@chromium.org2f0efde2013-02-06 14:12:58 +00007399 __ jmp(ecx); // Return to IC Miss stub, continuation still on stack.
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00007400}
7401
7402
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007403void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007404 if (masm->isolate()->function_entry_hook() != NULL) {
7405 // It's always safe to call the entry hook stub, as the hook itself
7406 // is not allowed to call back to V8.
7407 AllowStubCallsScope allow_stub_calls(masm, true);
7408
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007409 ProfileEntryHookStub stub;
7410 masm->CallStub(&stub);
7411 }
7412}
7413
7414
7415void ProfileEntryHookStub::Generate(MacroAssembler* masm) {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007416 // Save volatile registers.
7417 const int kNumSavedRegisters = 3;
7418 __ push(eax);
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007419 __ push(ecx);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007420 __ push(edx);
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007421
7422 // Calculate and push the original stack pointer.
ulan@chromium.orgdfe53072013-06-06 14:14:51 +00007423 __ lea(eax, Operand(esp, (kNumSavedRegisters + 1) * kPointerSize));
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007424 __ push(eax);
7425
ulan@chromium.orgdfe53072013-06-06 14:14:51 +00007426 // Retrieve our return address and use it to calculate the calling
7427 // function's address.
7428 __ mov(eax, Operand(esp, (kNumSavedRegisters + 1) * kPointerSize));
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007429 __ sub(eax, Immediate(Assembler::kCallInstructionLength));
7430 __ push(eax);
7431
7432 // Call the entry hook.
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007433 ASSERT(masm->isolate()->function_entry_hook() != NULL);
7434 __ call(FUNCTION_ADDR(masm->isolate()->function_entry_hook()),
7435 RelocInfo::RUNTIME_ENTRY);
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007436 __ add(esp, Immediate(2 * kPointerSize));
7437
7438 // Restore ecx.
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007439 __ pop(edx);
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007440 __ pop(ecx);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007441 __ pop(eax);
7442
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007443 __ ret(0);
7444}
7445
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007446
7447template<class T>
7448static void CreateArrayDispatch(MacroAssembler* masm) {
7449 int last_index = GetSequenceIndexFromFastElementsKind(
7450 TERMINAL_FAST_ELEMENTS_KIND);
7451 for (int i = 0; i <= last_index; ++i) {
7452 Label next;
7453 ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
7454 __ cmp(edx, kind);
7455 __ j(not_equal, &next);
7456 T stub(kind);
7457 __ TailCallStub(&stub);
7458 __ bind(&next);
7459 }
7460
7461 // If we reached this point there is a problem.
7462 __ Abort("Unexpected ElementsKind in array constructor");
7463}
7464
7465
7466static void CreateArrayDispatchOneArgument(MacroAssembler* masm) {
7467 // ebx - type info cell
7468 // edx - kind
7469 // eax - number of arguments
7470 // edi - constructor?
7471 // esp[0] - return address
7472 // esp[4] - last argument
7473 ASSERT(FAST_SMI_ELEMENTS == 0);
7474 ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
7475 ASSERT(FAST_ELEMENTS == 2);
7476 ASSERT(FAST_HOLEY_ELEMENTS == 3);
7477 ASSERT(FAST_DOUBLE_ELEMENTS == 4);
7478 ASSERT(FAST_HOLEY_DOUBLE_ELEMENTS == 5);
7479
7480 Handle<Object> undefined_sentinel(
7481 masm->isolate()->heap()->undefined_value(),
7482 masm->isolate());
7483
7484 // is the low bit set? If so, we are holey and that is good.
7485 __ test_b(edx, 1);
7486 Label normal_sequence;
7487 __ j(not_zero, &normal_sequence);
7488
7489 // look at the first argument
7490 __ mov(ecx, Operand(esp, kPointerSize));
7491 __ test(ecx, ecx);
7492 __ j(zero, &normal_sequence);
7493
7494 // We are going to create a holey array, but our kind is non-holey.
7495 // Fix kind and retry
7496 __ inc(edx);
7497 __ cmp(ebx, Immediate(undefined_sentinel));
7498 __ j(equal, &normal_sequence);
7499
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007500 // The type cell may have gone megamorphic, don't overwrite if so
7501 __ mov(ecx, FieldOperand(ebx, kPointerSize));
7502 __ JumpIfNotSmi(ecx, &normal_sequence);
7503
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007504 // Save the resulting elements kind in type info
7505 __ SmiTag(edx);
7506 __ mov(FieldOperand(ebx, kPointerSize), edx);
7507 __ SmiUntag(edx);
7508
7509 __ bind(&normal_sequence);
7510 int last_index = GetSequenceIndexFromFastElementsKind(
7511 TERMINAL_FAST_ELEMENTS_KIND);
7512 for (int i = 0; i <= last_index; ++i) {
7513 Label next;
7514 ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
7515 __ cmp(edx, kind);
7516 __ j(not_equal, &next);
7517 ArraySingleArgumentConstructorStub stub(kind);
7518 __ TailCallStub(&stub);
7519 __ bind(&next);
7520 }
7521
7522 // If we reached this point there is a problem.
7523 __ Abort("Unexpected ElementsKind in array constructor");
7524}
7525
7526
7527template<class T>
7528static void ArrayConstructorStubAheadOfTimeHelper(Isolate* isolate) {
7529 int to_index = GetSequenceIndexFromFastElementsKind(
7530 TERMINAL_FAST_ELEMENTS_KIND);
7531 for (int i = 0; i <= to_index; ++i) {
7532 ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007533 T stub(kind);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007534 stub.GetCode(isolate)->set_is_pregenerated(true);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007535 if (AllocationSiteInfo::GetMode(kind) != DONT_TRACK_ALLOCATION_SITE) {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007536 T stub1(kind, CONTEXT_CHECK_REQUIRED, DISABLE_ALLOCATION_SITES);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007537 stub1.GetCode(isolate)->set_is_pregenerated(true);
7538 }
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007539 }
7540}
7541
7542
7543void ArrayConstructorStubBase::GenerateStubsAheadOfTime(Isolate* isolate) {
7544 ArrayConstructorStubAheadOfTimeHelper<ArrayNoArgumentConstructorStub>(
7545 isolate);
7546 ArrayConstructorStubAheadOfTimeHelper<ArraySingleArgumentConstructorStub>(
7547 isolate);
7548 ArrayConstructorStubAheadOfTimeHelper<ArrayNArgumentsConstructorStub>(
7549 isolate);
7550}
7551
7552
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007553void InternalArrayConstructorStubBase::GenerateStubsAheadOfTime(
7554 Isolate* isolate) {
7555 ElementsKind kinds[2] = { FAST_ELEMENTS, FAST_HOLEY_ELEMENTS };
7556 for (int i = 0; i < 2; i++) {
7557 // For internal arrays we only need a few things
7558 InternalArrayNoArgumentConstructorStub stubh1(kinds[i]);
7559 stubh1.GetCode(isolate)->set_is_pregenerated(true);
7560 InternalArraySingleArgumentConstructorStub stubh2(kinds[i]);
7561 stubh2.GetCode(isolate)->set_is_pregenerated(true);
7562 InternalArrayNArgumentsConstructorStub stubh3(kinds[i]);
7563 stubh3.GetCode(isolate)->set_is_pregenerated(true);
7564 }
7565}
7566
7567
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007568void ArrayConstructorStub::Generate(MacroAssembler* masm) {
7569 // ----------- S t a t e -------------
7570 // -- eax : argc (only if argument_count_ == ANY)
7571 // -- ebx : type info cell
7572 // -- edi : constructor
7573 // -- esp[0] : return address
7574 // -- esp[4] : last argument
7575 // -----------------------------------
7576 Handle<Object> undefined_sentinel(
7577 masm->isolate()->heap()->undefined_value(),
7578 masm->isolate());
7579
7580 if (FLAG_debug_code) {
7581 // The array construct code is only set for the global and natives
7582 // builtin Array functions which always have maps.
7583
7584 // Initial map for the builtin Array function should be a map.
7585 __ mov(ecx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
7586 // Will both indicate a NULL and a Smi.
7587 __ test(ecx, Immediate(kSmiTagMask));
7588 __ Assert(not_zero, "Unexpected initial map for Array function");
7589 __ CmpObjectType(ecx, MAP_TYPE, ecx);
7590 __ Assert(equal, "Unexpected initial map for Array function");
7591
danno@chromium.org41728482013-06-12 22:31:22 +00007592 // We should either have undefined in ebx or a valid cell
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007593 Label okay_here;
danno@chromium.org41728482013-06-12 22:31:22 +00007594 Handle<Map> cell_map = masm->isolate()->factory()->cell_map();
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007595 __ cmp(ebx, Immediate(undefined_sentinel));
7596 __ j(equal, &okay_here);
danno@chromium.org41728482013-06-12 22:31:22 +00007597 __ cmp(FieldOperand(ebx, 0), Immediate(cell_map));
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007598 __ Assert(equal, "Expected property cell in register ebx");
7599 __ bind(&okay_here);
7600 }
7601
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007602 Label no_info, switch_ready;
7603 // Get the elements kind and case on that.
7604 __ cmp(ebx, Immediate(undefined_sentinel));
7605 __ j(equal, &no_info);
7606 __ mov(edx, FieldOperand(ebx, Cell::kValueOffset));
7607 __ JumpIfNotSmi(edx, &no_info);
7608 __ SmiUntag(edx);
7609 __ jmp(&switch_ready);
7610 __ bind(&no_info);
7611 __ mov(edx, Immediate(GetInitialFastElementsKind()));
7612 __ bind(&switch_ready);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007613
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007614 if (argument_count_ == ANY) {
7615 Label not_zero_case, not_one_case;
7616 __ test(eax, eax);
7617 __ j(not_zero, &not_zero_case);
7618 CreateArrayDispatch<ArrayNoArgumentConstructorStub>(masm);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007619
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007620 __ bind(&not_zero_case);
7621 __ cmp(eax, 1);
7622 __ j(greater, &not_one_case);
7623 CreateArrayDispatchOneArgument(masm);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007624
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007625 __ bind(&not_one_case);
7626 CreateArrayDispatch<ArrayNArgumentsConstructorStub>(masm);
7627 } else if (argument_count_ == NONE) {
7628 CreateArrayDispatch<ArrayNoArgumentConstructorStub>(masm);
7629 } else if (argument_count_ == ONE) {
7630 CreateArrayDispatchOneArgument(masm);
7631 } else if (argument_count_ == MORE_THAN_ONE) {
7632 CreateArrayDispatch<ArrayNArgumentsConstructorStub>(masm);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007633 } else {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007634 UNREACHABLE();
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007635 }
7636}
7637
7638
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007639void InternalArrayConstructorStub::GenerateCase(
7640 MacroAssembler* masm, ElementsKind kind) {
7641 Label not_zero_case, not_one_case;
7642 Label normal_sequence;
7643
7644 __ test(eax, eax);
7645 __ j(not_zero, &not_zero_case);
7646 InternalArrayNoArgumentConstructorStub stub0(kind);
7647 __ TailCallStub(&stub0);
7648
7649 __ bind(&not_zero_case);
7650 __ cmp(eax, 1);
7651 __ j(greater, &not_one_case);
7652
7653 if (IsFastPackedElementsKind(kind)) {
7654 // We might need to create a holey array
7655 // look at the first argument
7656 __ mov(ecx, Operand(esp, kPointerSize));
7657 __ test(ecx, ecx);
7658 __ j(zero, &normal_sequence);
7659
7660 InternalArraySingleArgumentConstructorStub
7661 stub1_holey(GetHoleyElementsKind(kind));
7662 __ TailCallStub(&stub1_holey);
7663 }
7664
7665 __ bind(&normal_sequence);
7666 InternalArraySingleArgumentConstructorStub stub1(kind);
7667 __ TailCallStub(&stub1);
7668
7669 __ bind(&not_one_case);
7670 InternalArrayNArgumentsConstructorStub stubN(kind);
7671 __ TailCallStub(&stubN);
7672}
7673
7674
7675void InternalArrayConstructorStub::Generate(MacroAssembler* masm) {
7676 // ----------- S t a t e -------------
7677 // -- eax : argc
7678 // -- ebx : type info cell
7679 // -- edi : constructor
7680 // -- esp[0] : return address
7681 // -- esp[4] : last argument
7682 // -----------------------------------
7683
7684 if (FLAG_debug_code) {
7685 // The array construct code is only set for the global and natives
7686 // builtin Array functions which always have maps.
7687
7688 // Initial map for the builtin Array function should be a map.
7689 __ mov(ecx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
7690 // Will both indicate a NULL and a Smi.
7691 __ test(ecx, Immediate(kSmiTagMask));
7692 __ Assert(not_zero, "Unexpected initial map for Array function");
7693 __ CmpObjectType(ecx, MAP_TYPE, ecx);
7694 __ Assert(equal, "Unexpected initial map for Array function");
7695 }
7696
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007697 // Figure out the right elements kind
7698 __ mov(ecx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007699
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007700 // Load the map's "bit field 2" into |result|. We only need the first byte,
7701 // but the following masking takes care of that anyway.
7702 __ mov(ecx, FieldOperand(ecx, Map::kBitField2Offset));
7703 // Retrieve elements_kind from bit field 2.
7704 __ and_(ecx, Map::kElementsKindMask);
7705 __ shr(ecx, Map::kElementsKindShift);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007706
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007707 if (FLAG_debug_code) {
7708 Label done;
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007709 __ cmp(ecx, Immediate(FAST_ELEMENTS));
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007710 __ j(equal, &done);
7711 __ cmp(ecx, Immediate(FAST_HOLEY_ELEMENTS));
7712 __ Assert(equal,
7713 "Invalid ElementsKind for InternalArray or InternalPackedArray");
7714 __ bind(&done);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007715 }
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007716
7717 Label fast_elements_case;
7718 __ cmp(ecx, Immediate(FAST_ELEMENTS));
7719 __ j(equal, &fast_elements_case);
7720 GenerateCase(masm, FAST_HOLEY_ELEMENTS);
7721
7722 __ bind(&fast_elements_case);
7723 GenerateCase(masm, FAST_ELEMENTS);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007724}
7725
7726
ricow@chromium.org65fae842010-08-25 15:26:24 +00007727#undef __
7728
7729} } // namespace v8::internal
7730
7731#endif // V8_TARGET_ARCH_IA32