blob: 64f36b3cf6151b1b3a591313ff439b1989ae3f75 [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
verwaest@chromium.org662436e2013-08-28 08:41:27 +000046void FastNewClosureStub::InitializeInterfaceDescriptor(
47 Isolate* isolate,
48 CodeStubInterfaceDescriptor* descriptor) {
49 static Register registers[] = { ebx };
50 descriptor->register_param_count_ = 1;
51 descriptor->register_params_ = registers;
52 descriptor->deoptimization_handler_ =
53 Runtime::FunctionForId(Runtime::kNewClosureFromStubFailure)->entry;
54}
55
56
yangguo@chromium.orgc73d55b2013-07-24 08:18:28 +000057void ToNumberStub::InitializeInterfaceDescriptor(
58 Isolate* isolate,
59 CodeStubInterfaceDescriptor* descriptor) {
60 static Register registers[] = { eax };
61 descriptor->register_param_count_ = 1;
62 descriptor->register_params_ = registers;
63 descriptor->deoptimization_handler_ = NULL;
64}
65
66
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000067void FastCloneShallowArrayStub::InitializeInterfaceDescriptor(
68 Isolate* isolate,
69 CodeStubInterfaceDescriptor* descriptor) {
70 static Register registers[] = { eax, ebx, ecx };
71 descriptor->register_param_count_ = 3;
72 descriptor->register_params_ = registers;
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000073 descriptor->deoptimization_handler_ =
74 Runtime::FunctionForId(Runtime::kCreateArrayLiteralShallow)->entry;
75}
76
77
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +000078void FastCloneShallowObjectStub::InitializeInterfaceDescriptor(
79 Isolate* isolate,
80 CodeStubInterfaceDescriptor* descriptor) {
81 static Register registers[] = { eax, ebx, ecx, edx };
82 descriptor->register_param_count_ = 4;
83 descriptor->register_params_ = registers;
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +000084 descriptor->deoptimization_handler_ =
85 Runtime::FunctionForId(Runtime::kCreateObjectLiteralShallow)->entry;
86}
87
88
danno@chromium.orgbee51992013-07-10 14:57:15 +000089void CreateAllocationSiteStub::InitializeInterfaceDescriptor(
90 Isolate* isolate,
91 CodeStubInterfaceDescriptor* descriptor) {
92 static Register registers[] = { ebx };
93 descriptor->register_param_count_ = 1;
94 descriptor->register_params_ = registers;
95 descriptor->deoptimization_handler_ = NULL;
96}
97
98
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000099void KeyedLoadFastElementStub::InitializeInterfaceDescriptor(
100 Isolate* isolate,
101 CodeStubInterfaceDescriptor* descriptor) {
102 static Register registers[] = { edx, ecx };
103 descriptor->register_param_count_ = 2;
104 descriptor->register_params_ = registers;
105 descriptor->deoptimization_handler_ =
mstarzinger@chromium.orge3b8d0f2013-02-01 09:06:41 +0000106 FUNCTION_ADDR(KeyedLoadIC_MissFromStubFailure);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000107}
108
109
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000110void LoadFieldStub::InitializeInterfaceDescriptor(
111 Isolate* isolate,
112 CodeStubInterfaceDescriptor* descriptor) {
113 static Register registers[] = { edx };
114 descriptor->register_param_count_ = 1;
115 descriptor->register_params_ = registers;
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000116 descriptor->deoptimization_handler_ = NULL;
117}
118
119
120void KeyedLoadFieldStub::InitializeInterfaceDescriptor(
121 Isolate* isolate,
122 CodeStubInterfaceDescriptor* descriptor) {
123 static Register registers[] = { edx };
124 descriptor->register_param_count_ = 1;
125 descriptor->register_params_ = registers;
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000126 descriptor->deoptimization_handler_ = NULL;
127}
128
129
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +0000130void KeyedStoreFastElementStub::InitializeInterfaceDescriptor(
131 Isolate* isolate,
132 CodeStubInterfaceDescriptor* descriptor) {
133 static Register registers[] = { edx, ecx, eax };
134 descriptor->register_param_count_ = 3;
135 descriptor->register_params_ = registers;
136 descriptor->deoptimization_handler_ =
137 FUNCTION_ADDR(KeyedStoreIC_MissFromStubFailure);
138}
139
140
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000141void TransitionElementsKindStub::InitializeInterfaceDescriptor(
142 Isolate* isolate,
143 CodeStubInterfaceDescriptor* descriptor) {
144 static Register registers[] = { eax, ebx };
145 descriptor->register_param_count_ = 2;
146 descriptor->register_params_ = registers;
147 descriptor->deoptimization_handler_ =
148 Runtime::FunctionForId(Runtime::kTransitionElementsKind)->entry;
149}
150
151
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000152static void InitializeArrayConstructorDescriptor(
153 Isolate* isolate,
154 CodeStubInterfaceDescriptor* descriptor,
155 int constant_stack_parameter_count) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000156 // register state
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000157 // eax -- number of arguments
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000158 // edi -- function
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000159 // ebx -- type info cell with elements kind
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000160 static Register registers[] = { edi, ebx };
161 descriptor->register_param_count_ = 2;
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000162
163 if (constant_stack_parameter_count != 0) {
164 // stack param count needs (constructor pointer, and single argument)
165 descriptor->stack_parameter_count_ = &eax;
166 }
167 descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000168 descriptor->register_params_ = registers;
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +0000169 descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000170 descriptor->deoptimization_handler_ =
ulan@chromium.orgdfe53072013-06-06 14:14:51 +0000171 Runtime::FunctionForId(Runtime::kArrayConstructor)->entry;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000172}
173
174
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +0000175static void InitializeInternalArrayConstructorDescriptor(
176 Isolate* isolate,
177 CodeStubInterfaceDescriptor* descriptor,
178 int constant_stack_parameter_count) {
179 // register state
180 // eax -- number of arguments
181 // edi -- constructor function
182 static Register registers[] = { edi };
183 descriptor->register_param_count_ = 1;
184
185 if (constant_stack_parameter_count != 0) {
186 // stack param count needs (constructor pointer, and single argument)
187 descriptor->stack_parameter_count_ = &eax;
188 }
189 descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
190 descriptor->register_params_ = registers;
191 descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
192 descriptor->deoptimization_handler_ =
ulan@chromium.orgdfe53072013-06-06 14:14:51 +0000193 Runtime::FunctionForId(Runtime::kInternalArrayConstructor)->entry;
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +0000194}
195
196
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000197void ArrayNoArgumentConstructorStub::InitializeInterfaceDescriptor(
198 Isolate* isolate,
199 CodeStubInterfaceDescriptor* descriptor) {
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000200 InitializeArrayConstructorDescriptor(isolate, descriptor, 0);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000201}
202
203
204void ArraySingleArgumentConstructorStub::InitializeInterfaceDescriptor(
205 Isolate* isolate,
206 CodeStubInterfaceDescriptor* descriptor) {
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000207 InitializeArrayConstructorDescriptor(isolate, descriptor, 1);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000208}
209
210
211void ArrayNArgumentsConstructorStub::InitializeInterfaceDescriptor(
212 Isolate* isolate,
213 CodeStubInterfaceDescriptor* descriptor) {
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000214 InitializeArrayConstructorDescriptor(isolate, descriptor, -1);
215}
216
217
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +0000218void InternalArrayNoArgumentConstructorStub::InitializeInterfaceDescriptor(
219 Isolate* isolate,
220 CodeStubInterfaceDescriptor* descriptor) {
221 InitializeInternalArrayConstructorDescriptor(isolate, descriptor, 0);
222}
223
224
225void InternalArraySingleArgumentConstructorStub::InitializeInterfaceDescriptor(
226 Isolate* isolate,
227 CodeStubInterfaceDescriptor* descriptor) {
228 InitializeInternalArrayConstructorDescriptor(isolate, descriptor, 1);
229}
230
231
232void InternalArrayNArgumentsConstructorStub::InitializeInterfaceDescriptor(
233 Isolate* isolate,
234 CodeStubInterfaceDescriptor* descriptor) {
235 InitializeInternalArrayConstructorDescriptor(isolate, descriptor, -1);
236}
237
238
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000239void CompareNilICStub::InitializeInterfaceDescriptor(
240 Isolate* isolate,
241 CodeStubInterfaceDescriptor* descriptor) {
242 static Register registers[] = { eax };
243 descriptor->register_param_count_ = 1;
244 descriptor->register_params_ = registers;
245 descriptor->deoptimization_handler_ =
246 FUNCTION_ADDR(CompareNilIC_Miss);
svenpanne@chromium.orga53e8e02013-05-24 12:35:50 +0000247 descriptor->SetMissHandler(
248 ExternalReference(IC_Utility(IC::kCompareNilIC_Miss), isolate));
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000249}
250
rossberg@chromium.orgb99c7542013-05-31 11:40:45 +0000251void ToBooleanStub::InitializeInterfaceDescriptor(
252 Isolate* isolate,
253 CodeStubInterfaceDescriptor* descriptor) {
254 static Register registers[] = { eax };
255 descriptor->register_param_count_ = 1;
256 descriptor->register_params_ = registers;
257 descriptor->deoptimization_handler_ =
258 FUNCTION_ADDR(ToBooleanIC_Miss);
259 descriptor->SetMissHandler(
260 ExternalReference(IC_Utility(IC::kToBooleanIC_Miss), isolate));
261}
262
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000263
mstarzinger@chromium.orge0e1b0d2013-07-08 08:38:06 +0000264void StoreGlobalStub::InitializeInterfaceDescriptor(
265 Isolate* isolate,
266 CodeStubInterfaceDescriptor* descriptor) {
267 static Register registers[] = { edx, ecx, eax };
268 descriptor->register_param_count_ = 3;
269 descriptor->register_params_ = registers;
270 descriptor->deoptimization_handler_ =
271 FUNCTION_ADDR(StoreIC_MissFromStubFailure);
272}
273
274
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +0000275void ElementsTransitionAndStoreStub::InitializeInterfaceDescriptor(
276 Isolate* isolate,
277 CodeStubInterfaceDescriptor* descriptor) {
278 static Register registers[] = { eax, ebx, ecx, edx };
279 descriptor->register_param_count_ = 4;
280 descriptor->register_params_ = registers;
281 descriptor->deoptimization_handler_ =
282 FUNCTION_ADDR(ElementsTransitionAndStoreIC_Miss);
283}
284
285
ricow@chromium.org65fae842010-08-25 15:26:24 +0000286#define __ ACCESS_MASM(masm)
whesse@chromium.org7a392b32011-01-31 11:30:36 +0000287
ulan@chromium.org77ca49a2013-04-22 09:43:56 +0000288
289void HydrogenCodeStub::GenerateLightweightMiss(MacroAssembler* masm) {
290 // Update the static counter each time a new code stub is generated.
291 Isolate* isolate = masm->isolate();
292 isolate->counters()->code_stubs()->Increment();
293
294 CodeStubInterfaceDescriptor* descriptor = GetInterfaceDescriptor(isolate);
295 int param_count = descriptor->register_param_count_;
296 {
297 // Call the runtime system in a fresh internal frame.
298 FrameScope scope(masm, StackFrame::INTERNAL);
299 ASSERT(descriptor->register_param_count_ == 0 ||
300 eax.is(descriptor->register_params_[param_count - 1]));
301 // Push arguments
302 for (int i = 0; i < param_count; ++i) {
303 __ push(descriptor->register_params_[i]);
304 }
svenpanne@chromium.orga53e8e02013-05-24 12:35:50 +0000305 ExternalReference miss = descriptor->miss_handler();
ulan@chromium.org77ca49a2013-04-22 09:43:56 +0000306 __ CallExternalReference(miss, descriptor->register_param_count_);
307 }
308
309 __ ret(0);
310}
311
312
ricow@chromium.org65fae842010-08-25 15:26:24 +0000313void FastNewContextStub::Generate(MacroAssembler* masm) {
314 // Try to allocate the context in new space.
315 Label gc;
ager@chromium.org0ee099b2011-01-25 14:06:47 +0000316 int length = slots_ + Context::MIN_CONTEXT_SLOTS;
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000317 __ Allocate((length * kPointerSize) + FixedArray::kHeaderSize,
318 eax, ebx, ecx, &gc, TAG_OBJECT);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000319
320 // Get the function from the stack.
321 __ mov(ecx, Operand(esp, 1 * kPointerSize));
322
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000323 // Set up the object header.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000324 Factory* factory = masm->isolate()->factory();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000325 __ mov(FieldOperand(eax, HeapObject::kMapOffset),
326 factory->function_context_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +0000327 __ mov(FieldOperand(eax, Context::kLengthOffset),
ager@chromium.org0ee099b2011-01-25 14:06:47 +0000328 Immediate(Smi::FromInt(length)));
ricow@chromium.org65fae842010-08-25 15:26:24 +0000329
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000330 // Set up the fixed slots.
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000331 __ Set(ebx, Immediate(0)); // Set to NULL.
ricow@chromium.org65fae842010-08-25 15:26:24 +0000332 __ mov(Operand(eax, Context::SlotOffset(Context::CLOSURE_INDEX)), ecx);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000333 __ mov(Operand(eax, Context::SlotOffset(Context::PREVIOUS_INDEX)), esi);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000334 __ mov(Operand(eax, Context::SlotOffset(Context::EXTENSION_INDEX)), ebx);
335
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000336 // Copy the global object from the previous context.
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000337 __ mov(ebx, Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
338 __ mov(Operand(eax, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)), ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000339
340 // Initialize the rest of the slots to undefined.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000341 __ mov(ebx, factory->undefined_value());
ager@chromium.org0ee099b2011-01-25 14:06:47 +0000342 for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
ricow@chromium.org65fae842010-08-25 15:26:24 +0000343 __ mov(Operand(eax, Context::SlotOffset(i)), ebx);
344 }
345
346 // Return and remove the on-stack parameter.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000347 __ mov(esi, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000348 __ ret(1 * kPointerSize);
349
350 // Need to collect. Call into runtime system.
351 __ bind(&gc);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000352 __ TailCallRuntime(Runtime::kNewFunctionContext, 1, 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000353}
354
355
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000356void FastNewBlockContextStub::Generate(MacroAssembler* masm) {
357 // Stack layout on entry:
358 //
359 // [esp + (1 * kPointerSize)]: function
360 // [esp + (2 * kPointerSize)]: serialized scope info
361
362 // Try to allocate the context in new space.
363 Label gc;
364 int length = slots_ + Context::MIN_CONTEXT_SLOTS;
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000365 __ Allocate(FixedArray::SizeFor(length), eax, ebx, ecx, &gc, TAG_OBJECT);
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000366
367 // Get the function or sentinel from the stack.
368 __ mov(ecx, Operand(esp, 1 * kPointerSize));
369
370 // Get the serialized scope info from the stack.
371 __ mov(ebx, Operand(esp, 2 * kPointerSize));
372
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000373 // Set up the object header.
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000374 Factory* factory = masm->isolate()->factory();
375 __ mov(FieldOperand(eax, HeapObject::kMapOffset),
376 factory->block_context_map());
377 __ mov(FieldOperand(eax, Context::kLengthOffset),
378 Immediate(Smi::FromInt(length)));
379
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000380 // If this block context is nested in the native context we get a smi
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000381 // sentinel instead of a function. The block context should get the
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000382 // canonical empty function of the native context as its closure which
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000383 // we still have to look up.
384 Label after_sentinel;
385 __ JumpIfNotSmi(ecx, &after_sentinel, Label::kNear);
386 if (FLAG_debug_code) {
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000387 __ cmp(ecx, 0);
danno@chromium.org59400602013-08-13 17:09:37 +0000388 __ Assert(equal, kExpected0AsASmiSentinel);
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000389 }
390 __ mov(ecx, GlobalObjectOperand());
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000391 __ mov(ecx, FieldOperand(ecx, GlobalObject::kNativeContextOffset));
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000392 __ mov(ecx, ContextOperand(ecx, Context::CLOSURE_INDEX));
393 __ bind(&after_sentinel);
394
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000395 // Set up the fixed slots.
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000396 __ mov(ContextOperand(eax, Context::CLOSURE_INDEX), ecx);
397 __ mov(ContextOperand(eax, Context::PREVIOUS_INDEX), esi);
398 __ mov(ContextOperand(eax, Context::EXTENSION_INDEX), ebx);
399
400 // Copy the global object from the previous context.
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000401 __ mov(ebx, ContextOperand(esi, Context::GLOBAL_OBJECT_INDEX));
402 __ mov(ContextOperand(eax, Context::GLOBAL_OBJECT_INDEX), ebx);
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000403
404 // Initialize the rest of the slots to the hole value.
405 if (slots_ == 1) {
406 __ mov(ContextOperand(eax, Context::MIN_CONTEXT_SLOTS),
407 factory->the_hole_value());
408 } else {
409 __ mov(ebx, factory->the_hole_value());
410 for (int i = 0; i < slots_; i++) {
411 __ mov(ContextOperand(eax, i + Context::MIN_CONTEXT_SLOTS), ebx);
412 }
413 }
414
415 // Return and remove the on-stack parameters.
416 __ mov(esi, eax);
417 __ ret(2 * kPointerSize);
418
419 // Need to collect. Call into runtime system.
420 __ bind(&gc);
421 __ TailCallRuntime(Runtime::kPushBlockContext, 2, 1);
422}
423
424
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000425void StoreBufferOverflowStub::Generate(MacroAssembler* masm) {
426 // We don't allow a GC during a store buffer overflow so there is no need to
427 // store the registers in any particular way, but we do have to store and
428 // restore them.
429 __ pushad();
430 if (save_doubles_ == kSaveFPRegs) {
ulan@chromium.org750145a2013-03-07 15:14:13 +0000431 CpuFeatureScope scope(masm, SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000432 __ sub(esp, Immediate(kDoubleSize * XMMRegister::kNumRegisters));
433 for (int i = 0; i < XMMRegister::kNumRegisters; i++) {
434 XMMRegister reg = XMMRegister::from_code(i);
435 __ movdbl(Operand(esp, i * kDoubleSize), reg);
436 }
437 }
438 const int argument_count = 1;
439
440 AllowExternalCallThatCantCauseGC scope(masm);
441 __ PrepareCallCFunction(argument_count, ecx);
442 __ mov(Operand(esp, 0 * kPointerSize),
ulan@chromium.org32d7dba2013-04-24 10:59:06 +0000443 Immediate(ExternalReference::isolate_address(masm->isolate())));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000444 __ CallCFunction(
445 ExternalReference::store_buffer_overflow_function(masm->isolate()),
446 argument_count);
447 if (save_doubles_ == kSaveFPRegs) {
ulan@chromium.org750145a2013-03-07 15:14:13 +0000448 CpuFeatureScope scope(masm, SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000449 for (int i = 0; i < XMMRegister::kNumRegisters; i++) {
450 XMMRegister reg = XMMRegister::from_code(i);
451 __ movdbl(reg, Operand(esp, i * kDoubleSize));
452 }
453 __ add(esp, Immediate(kDoubleSize * XMMRegister::kNumRegisters));
454 }
455 __ popad();
456 __ ret(0);
457}
458
459
ricow@chromium.org65fae842010-08-25 15:26:24 +0000460class FloatingPointHelper : public AllStatic {
461 public:
ricow@chromium.org65fae842010-08-25 15:26:24 +0000462 enum ArgLocation {
463 ARGS_ON_STACK,
464 ARGS_IN_REGISTERS
465 };
466
467 // Code pattern for loading a floating point value. Input value must
468 // be either a smi or a heap number object (fp value). Requirements:
469 // operand in register number. Returns operand as floating point number
470 // on FPU stack.
471 static void LoadFloatOperand(MacroAssembler* masm, Register number);
472
473 // Code pattern for loading floating point values. Input values must
474 // be either smi or heap number objects (fp values). Requirements:
475 // operand_1 on TOS+1 or in edx, operand_2 on TOS+2 or in eax.
476 // Returns operands as floating point numbers on FPU stack.
477 static void LoadFloatOperands(MacroAssembler* masm,
478 Register scratch,
479 ArgLocation arg_location = ARGS_ON_STACK);
480
481 // Similar to LoadFloatOperand but assumes that both operands are smis.
482 // Expects operands in edx, eax.
483 static void LoadFloatSmis(MacroAssembler* masm, Register scratch);
484
485 // Test if operands are smi or number objects (fp). Requirements:
486 // operand_1 in eax, operand_2 in edx; falls through on float
487 // operands, jumps to the non_float label otherwise.
488 static void CheckFloatOperands(MacroAssembler* masm,
489 Label* non_float,
490 Register scratch);
491
492 // Takes the operands in edx and eax and loads them as integers in eax
493 // and ecx.
ricow@chromium.org65fae842010-08-25 15:26:24 +0000494 static void LoadUnknownsAsIntegers(MacroAssembler* masm,
495 bool use_sse3,
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +0000496 BinaryOpIC::TypeInfo left_type,
497 BinaryOpIC::TypeInfo right_type,
ricow@chromium.org65fae842010-08-25 15:26:24 +0000498 Label* operand_conversion_failure);
499
ricow@chromium.org65fae842010-08-25 15:26:24 +0000500 // Test if operands are numbers (smi or HeapNumber objects), and load
501 // them into xmm0 and xmm1 if they are. Jump to label not_numbers if
502 // either operand is not a number. Operands are in edx and eax.
503 // Leaves operands unchanged.
504 static void LoadSSE2Operands(MacroAssembler* masm, Label* not_numbers);
505
506 // Similar to LoadSSE2Operands but assumes that both operands are smis.
507 // Expects operands in edx, eax.
508 static void LoadSSE2Smis(MacroAssembler* masm, Register scratch);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000509
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +0000510 // Checks that |operand| has an int32 value. If |int32_result| is different
511 // from |scratch|, it will contain that int32 value.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000512 static void CheckSSE2OperandIsInt32(MacroAssembler* masm,
513 Label* non_int32,
514 XMMRegister operand,
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +0000515 Register int32_result,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000516 Register scratch,
517 XMMRegister xmm_scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000518};
519
520
danno@chromium.org169691d2013-07-15 08:01:13 +0000521void DoubleToIStub::Generate(MacroAssembler* masm) {
522 Register input_reg = this->source();
523 Register final_result_reg = this->destination();
524 ASSERT(is_truncating());
525
526 Label check_negative, process_64_bits, done, done_no_stash;
527
528 int double_offset = offset();
529
530 // Account for return address and saved regs if input is esp.
531 if (input_reg.is(esp)) double_offset += 3 * kPointerSize;
532
533 MemOperand mantissa_operand(MemOperand(input_reg, double_offset));
534 MemOperand exponent_operand(MemOperand(input_reg,
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +0000535 double_offset + kDoubleSize / 2));
danno@chromium.org169691d2013-07-15 08:01:13 +0000536
537 Register scratch1;
538 {
539 Register scratch_candidates[3] = { ebx, edx, edi };
540 for (int i = 0; i < 3; i++) {
541 scratch1 = scratch_candidates[i];
542 if (!final_result_reg.is(scratch1) && !input_reg.is(scratch1)) break;
543 }
544 }
545 // Since we must use ecx for shifts below, use some other register (eax)
546 // to calculate the result if ecx is the requested return register.
547 Register result_reg = final_result_reg.is(ecx) ? eax : final_result_reg;
548 // Save ecx if it isn't the return register and therefore volatile, or if it
549 // is the return register, then save the temp register we use in its stead for
550 // the result.
551 Register save_reg = final_result_reg.is(ecx) ? eax : ecx;
552 __ push(scratch1);
553 __ push(save_reg);
554
555 bool stash_exponent_copy = !input_reg.is(esp);
556 __ mov(scratch1, mantissa_operand);
557 if (CpuFeatures::IsSupported(SSE3)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +0000558 CpuFeatureScope scope(masm, SSE3);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000559 // Load x87 register with heap number.
danno@chromium.org169691d2013-07-15 08:01:13 +0000560 __ fld_d(mantissa_operand);
561 }
562 __ mov(ecx, exponent_operand);
563 if (stash_exponent_copy) __ push(ecx);
564
565 __ and_(ecx, HeapNumber::kExponentMask);
566 __ shr(ecx, HeapNumber::kExponentShift);
567 __ lea(result_reg, MemOperand(ecx, -HeapNumber::kExponentBias));
568 __ cmp(result_reg, Immediate(HeapNumber::kMantissaBits));
569 __ j(below, &process_64_bits);
570
571 // Result is entirely in lower 32-bits of mantissa
572 int delta = HeapNumber::kExponentBias + Double::kPhysicalSignificandSize;
573 if (CpuFeatures::IsSupported(SSE3)) {
574 __ fstp(0);
575 }
576 __ sub(ecx, Immediate(delta));
577 __ xor_(result_reg, result_reg);
578 __ cmp(ecx, Immediate(31));
579 __ j(above, &done);
580 __ shl_cl(scratch1);
581 __ jmp(&check_negative);
582
583 __ bind(&process_64_bits);
584 if (CpuFeatures::IsSupported(SSE3)) {
585 CpuFeatureScope scope(masm, SSE3);
586 if (stash_exponent_copy) {
587 // Already a copy of the exponent on the stack, overwrite it.
588 STATIC_ASSERT(kDoubleSize == 2 * kPointerSize);
589 __ sub(esp, Immediate(kDoubleSize / 2));
590 } else {
591 // Reserve space for 64 bit answer.
592 __ sub(esp, Immediate(kDoubleSize)); // Nolint.
593 }
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000594 // Do conversion, which cannot fail because we checked the exponent.
595 __ fisttp_d(Operand(esp, 0));
danno@chromium.org169691d2013-07-15 08:01:13 +0000596 __ mov(result_reg, Operand(esp, 0)); // Load low word of answer as result
597 __ add(esp, Immediate(kDoubleSize));
598 __ jmp(&done_no_stash);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000599 } else {
danno@chromium.org169691d2013-07-15 08:01:13 +0000600 // Result must be extracted from shifted 32-bit mantissa
601 __ sub(ecx, Immediate(delta));
602 __ neg(ecx);
603 if (stash_exponent_copy) {
604 __ mov(result_reg, MemOperand(esp, 0));
605 } else {
606 __ mov(result_reg, exponent_operand);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000607 }
danno@chromium.org169691d2013-07-15 08:01:13 +0000608 __ and_(result_reg,
609 Immediate(static_cast<uint32_t>(Double::kSignificandMask >> 32)));
610 __ add(result_reg,
611 Immediate(static_cast<uint32_t>(Double::kHiddenBit >> 32)));
612 __ shrd(result_reg, scratch1);
613 __ shr_cl(result_reg);
614 __ test(ecx, Immediate(32));
615 if (CpuFeatures::IsSupported(CMOV)) {
616 CpuFeatureScope use_cmov(masm, CMOV);
617 __ cmov(not_equal, scratch1, result_reg);
618 } else {
619 Label skip_mov;
620 __ j(equal, &skip_mov, Label::kNear);
621 __ mov(scratch1, result_reg);
622 __ bind(&skip_mov);
623 }
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000624 }
danno@chromium.org169691d2013-07-15 08:01:13 +0000625
626 // If the double was negative, negate the integer result.
627 __ bind(&check_negative);
628 __ mov(result_reg, scratch1);
629 __ neg(result_reg);
630 if (stash_exponent_copy) {
631 __ cmp(MemOperand(esp, 0), Immediate(0));
632 } else {
633 __ cmp(exponent_operand, Immediate(0));
634 }
635 if (CpuFeatures::IsSupported(CMOV)) {
636 CpuFeatureScope use_cmov(masm, CMOV);
637 __ cmov(greater, result_reg, scratch1);
638 } else {
639 Label skip_mov;
640 __ j(less_equal, &skip_mov, Label::kNear);
641 __ mov(result_reg, scratch1);
642 __ bind(&skip_mov);
643 }
644
645 // Restore registers
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000646 __ bind(&done);
danno@chromium.org169691d2013-07-15 08:01:13 +0000647 if (stash_exponent_copy) {
648 __ add(esp, Immediate(kDoubleSize / 2));
649 }
650 __ bind(&done_no_stash);
651 if (!final_result_reg.is(result_reg)) {
652 ASSERT(final_result_reg.is(ecx));
653 __ mov(final_result_reg, result_reg);
654 }
655 __ pop(save_reg);
656 __ pop(scratch1);
657 __ ret(0);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000658}
659
660
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +0000661// Uses SSE2 to convert the heap number in |source| to an integer. Jumps to
662// |conversion_failure| if the heap number did not contain an int32 value.
663// Result is in ecx. Trashes ebx, xmm0, and xmm1.
664static void ConvertHeapNumberToInt32(MacroAssembler* masm,
665 Register source,
666 Label* conversion_failure) {
667 __ movdbl(xmm0, FieldOperand(source, HeapNumber::kValueOffset));
668 FloatingPointHelper::CheckSSE2OperandIsInt32(
669 masm, conversion_failure, xmm0, ecx, ebx, xmm1);
670}
671
672
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000673void BinaryOpStub::Initialize() {
674 platform_specific_bit_ = CpuFeatures::IsSupported(SSE3);
675}
676
677
danno@chromium.org40cb8782011-05-25 07:58:50 +0000678void BinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000679 __ pop(ecx); // Save return address.
680 __ push(edx);
681 __ push(eax);
682 // Left and right arguments are now on top.
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000683 __ push(Immediate(Smi::FromInt(MinorKey())));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000684
685 __ push(ecx); // Push return address.
686
687 // Patch the caller to an appropriate specialized stub and return the
688 // operation result to the caller of the stub.
689 __ TailCallExternalReference(
danno@chromium.org40cb8782011-05-25 07:58:50 +0000690 ExternalReference(IC_Utility(IC::kBinaryOp_Patch),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000691 masm->isolate()),
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000692 3,
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000693 1);
694}
695
696
697// Prepare for a type transition runtime call when the args are already on
698// the stack, under the return address.
danno@chromium.org40cb8782011-05-25 07:58:50 +0000699void BinaryOpStub::GenerateTypeTransitionWithSavedArgs(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000700 __ pop(ecx); // Save return address.
701 // Left and right arguments are already on top of the stack.
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000702 __ push(Immediate(Smi::FromInt(MinorKey())));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000703
704 __ push(ecx); // Push return address.
705
706 // Patch the caller to an appropriate specialized stub and return the
707 // operation result to the caller of the stub.
708 __ TailCallExternalReference(
danno@chromium.org40cb8782011-05-25 07:58:50 +0000709 ExternalReference(IC_Utility(IC::kBinaryOp_Patch),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000710 masm->isolate()),
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000711 3,
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000712 1);
713}
714
715
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +0000716static void BinaryOpStub_GenerateRegisterArgsPop(MacroAssembler* masm) {
717 __ pop(ecx);
718 __ pop(eax);
719 __ pop(edx);
720 __ push(ecx);
721}
722
723
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000724static void BinaryOpStub_GenerateSmiCode(
danno@chromium.org40cb8782011-05-25 07:58:50 +0000725 MacroAssembler* masm,
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000726 Label* slow,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000727 BinaryOpStub::SmiCodeGenerateHeapNumberResults allow_heapnumber_results,
728 Token::Value op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000729 // 1. Move arguments into edx, eax except for DIV and MOD, which need the
730 // dividend in eax and edx free for the division. Use eax, ebx for those.
731 Comment load_comment(masm, "-- Load arguments");
732 Register left = edx;
733 Register right = eax;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000734 if (op == Token::DIV || op == Token::MOD) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000735 left = eax;
736 right = ebx;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000737 __ mov(ebx, eax);
738 __ mov(eax, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000739 }
740
741
742 // 2. Prepare the smi check of both operands by oring them together.
743 Comment smi_check_comment(masm, "-- Smi check arguments");
744 Label not_smis;
745 Register combined = ecx;
746 ASSERT(!left.is(combined) && !right.is(combined));
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000747 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000748 case Token::BIT_OR:
749 // Perform the operation into eax and smi check the result. Preserve
750 // eax in case the result is not a smi.
751 ASSERT(!left.is(ecx) && !right.is(ecx));
752 __ mov(ecx, right);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000753 __ or_(right, left); // Bitwise or is commutative.
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000754 combined = right;
755 break;
756
757 case Token::BIT_XOR:
758 case Token::BIT_AND:
759 case Token::ADD:
760 case Token::SUB:
761 case Token::MUL:
762 case Token::DIV:
763 case Token::MOD:
764 __ mov(combined, right);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000765 __ or_(combined, left);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000766 break;
767
768 case Token::SHL:
769 case Token::SAR:
770 case Token::SHR:
771 // Move the right operand into ecx for the shift operation, use eax
772 // for the smi check register.
773 ASSERT(!left.is(ecx) && !right.is(ecx));
774 __ mov(ecx, right);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000775 __ or_(right, left);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000776 combined = right;
777 break;
778
779 default:
780 break;
781 }
782
783 // 3. Perform the smi check of the operands.
784 STATIC_ASSERT(kSmiTag == 0); // Adjust zero check if not the case.
whesse@chromium.org7b260152011-06-20 15:33:18 +0000785 __ JumpIfNotSmi(combined, &not_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000786
787 // 4. Operands are both smis, perform the operation leaving the result in
788 // eax and check the result if necessary.
789 Comment perform_smi(masm, "-- Perform smi operation");
790 Label use_fp_on_smis;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000791 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000792 case Token::BIT_OR:
793 // Nothing to do.
794 break;
795
796 case Token::BIT_XOR:
797 ASSERT(right.is(eax));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000798 __ xor_(right, left); // Bitwise xor is commutative.
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000799 break;
800
801 case Token::BIT_AND:
802 ASSERT(right.is(eax));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000803 __ and_(right, left); // Bitwise and is commutative.
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000804 break;
805
806 case Token::SHL:
807 // Remove tags from operands (but keep sign).
808 __ SmiUntag(left);
809 __ SmiUntag(ecx);
810 // Perform the operation.
811 __ shl_cl(left);
812 // Check that the *signed* result fits in a smi.
813 __ cmp(left, 0xc0000000);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000814 __ j(sign, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000815 // Tag the result and store it in register eax.
816 __ SmiTag(left);
817 __ mov(eax, left);
818 break;
819
820 case Token::SAR:
821 // Remove tags from operands (but keep sign).
822 __ SmiUntag(left);
823 __ SmiUntag(ecx);
824 // Perform the operation.
825 __ sar_cl(left);
826 // Tag the result and store it in register eax.
827 __ SmiTag(left);
828 __ mov(eax, left);
829 break;
830
831 case Token::SHR:
832 // Remove tags from operands (but keep sign).
833 __ SmiUntag(left);
834 __ SmiUntag(ecx);
835 // Perform the operation.
836 __ shr_cl(left);
837 // Check that the *unsigned* result fits in a smi.
838 // Neither of the two high-order bits can be set:
839 // - 0x80000000: high bit would be lost when smi tagging.
840 // - 0x40000000: this number would convert to negative when
841 // Smi tagging these two cases can only happen with shifts
842 // by 0 or 1 when handed a valid smi.
843 __ test(left, Immediate(0xc0000000));
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000844 __ j(not_zero, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000845 // Tag the result and store it in register eax.
846 __ SmiTag(left);
847 __ mov(eax, left);
848 break;
849
850 case Token::ADD:
851 ASSERT(right.is(eax));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000852 __ add(right, left); // Addition is commutative.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000853 __ j(overflow, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000854 break;
855
856 case Token::SUB:
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000857 __ sub(left, right);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000858 __ j(overflow, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000859 __ mov(eax, left);
860 break;
861
862 case Token::MUL:
863 // If the smi tag is 0 we can just leave the tag on one operand.
864 STATIC_ASSERT(kSmiTag == 0); // Adjust code below if not the case.
865 // We can't revert the multiplication if the result is not a smi
866 // so save the right operand.
867 __ mov(ebx, right);
868 // Remove tag from one of the operands (but keep sign).
869 __ SmiUntag(right);
870 // Do multiplication.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000871 __ imul(right, left); // Multiplication is commutative.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000872 __ j(overflow, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000873 // Check for negative zero result. Use combined = left | right.
874 __ NegativeZeroTest(right, combined, &use_fp_on_smis);
875 break;
876
877 case Token::DIV:
878 // We can't revert the division if the result is not a smi so
879 // save the left operand.
880 __ mov(edi, left);
881 // Check for 0 divisor.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000882 __ test(right, right);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000883 __ j(zero, &use_fp_on_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000884 // Sign extend left into edx:eax.
885 ASSERT(left.is(eax));
886 __ cdq();
887 // Divide edx:eax by right.
888 __ idiv(right);
889 // Check for the corner case of dividing the most negative smi by
890 // -1. We cannot use the overflow flag, since it is not set by idiv
891 // instruction.
892 STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
893 __ cmp(eax, 0x40000000);
894 __ j(equal, &use_fp_on_smis);
895 // Check for negative zero result. Use combined = left | right.
896 __ NegativeZeroTest(eax, combined, &use_fp_on_smis);
897 // Check that the remainder is zero.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000898 __ test(edx, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000899 __ j(not_zero, &use_fp_on_smis);
900 // Tag the result and store it in register eax.
901 __ SmiTag(eax);
902 break;
903
904 case Token::MOD:
905 // Check for 0 divisor.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000906 __ test(right, right);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000907 __ j(zero, &not_smis);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000908
909 // Sign extend left into edx:eax.
910 ASSERT(left.is(eax));
911 __ cdq();
912 // Divide edx:eax by right.
913 __ idiv(right);
914 // Check for negative zero result. Use combined = left | right.
915 __ NegativeZeroTest(edx, combined, slow);
916 // Move remainder to register eax.
917 __ mov(eax, edx);
918 break;
919
920 default:
921 UNREACHABLE();
922 }
923
924 // 5. Emit return of result in eax. Some operations have registers pushed.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000925 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000926 case Token::ADD:
927 case Token::SUB:
928 case Token::MUL:
929 case Token::DIV:
930 __ ret(0);
931 break;
932 case Token::MOD:
933 case Token::BIT_OR:
934 case Token::BIT_AND:
935 case Token::BIT_XOR:
936 case Token::SAR:
937 case Token::SHL:
938 case Token::SHR:
939 __ ret(2 * kPointerSize);
940 break;
941 default:
942 UNREACHABLE();
943 }
944
945 // 6. For some operations emit inline code to perform floating point
946 // operations on known smis (e.g., if the result of the operation
947 // overflowed the smi range).
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000948 if (allow_heapnumber_results == BinaryOpStub::NO_HEAPNUMBER_RESULTS) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000949 __ bind(&use_fp_on_smis);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000950 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000951 // Undo the effects of some operations, and some register moves.
952 case Token::SHL:
953 // The arguments are saved on the stack, and only used from there.
954 break;
955 case Token::ADD:
956 // Revert right = right + left.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000957 __ sub(right, left);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000958 break;
959 case Token::SUB:
960 // Revert left = left - right.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000961 __ add(left, right);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000962 break;
963 case Token::MUL:
964 // Right was clobbered but a copy is in ebx.
965 __ mov(right, ebx);
966 break;
967 case Token::DIV:
968 // Left was clobbered but a copy is in edi. Right is in ebx for
969 // division. They should be in eax, ebx for jump to not_smi.
970 __ mov(eax, edi);
971 break;
972 default:
973 // No other operators jump to use_fp_on_smis.
974 break;
975 }
976 __ jmp(&not_smis);
977 } else {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000978 ASSERT(allow_heapnumber_results == BinaryOpStub::ALLOW_HEAPNUMBER_RESULTS);
979 switch (op) {
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000980 case Token::SHL:
981 case Token::SHR: {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000982 Comment perform_float(masm, "-- Perform float operation on smis");
983 __ bind(&use_fp_on_smis);
984 // Result we want is in left == edx, so we can put the allocated heap
985 // number in eax.
986 __ AllocateHeapNumber(eax, ecx, ebx, slow);
987 // Store the result in the HeapNumber and return.
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000988 // It's OK to overwrite the arguments on the stack because we
989 // are about to return.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000990 if (op == Token::SHR) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000991 __ mov(Operand(esp, 1 * kPointerSize), left);
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000992 __ mov(Operand(esp, 2 * kPointerSize), Immediate(0));
993 __ fild_d(Operand(esp, 1 * kPointerSize));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000994 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000995 } else {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000996 ASSERT_EQ(Token::SHL, op);
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000997 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +0000998 CpuFeatureScope use_sse2(masm, SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000999 __ cvtsi2sd(xmm0, left);
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001000 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1001 } else {
1002 __ mov(Operand(esp, 1 * kPointerSize), left);
1003 __ fild_s(Operand(esp, 1 * kPointerSize));
1004 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1005 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001006 }
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001007 __ ret(2 * kPointerSize);
1008 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001009 }
1010
1011 case Token::ADD:
1012 case Token::SUB:
1013 case Token::MUL:
1014 case Token::DIV: {
1015 Comment perform_float(masm, "-- Perform float operation on smis");
1016 __ bind(&use_fp_on_smis);
1017 // Restore arguments to edx, eax.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001018 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001019 case Token::ADD:
1020 // Revert right = right + left.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001021 __ sub(right, left);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001022 break;
1023 case Token::SUB:
1024 // Revert left = left - right.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001025 __ add(left, right);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001026 break;
1027 case Token::MUL:
1028 // Right was clobbered but a copy is in ebx.
1029 __ mov(right, ebx);
1030 break;
1031 case Token::DIV:
1032 // Left was clobbered but a copy is in edi. Right is in ebx for
1033 // division.
1034 __ mov(edx, edi);
1035 __ mov(eax, right);
1036 break;
1037 default: UNREACHABLE();
1038 break;
1039 }
1040 __ AllocateHeapNumber(ecx, ebx, no_reg, slow);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001041 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001042 CpuFeatureScope use_sse2(masm, SSE2);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001043 FloatingPointHelper::LoadSSE2Smis(masm, ebx);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001044 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001045 case Token::ADD: __ addsd(xmm0, xmm1); break;
1046 case Token::SUB: __ subsd(xmm0, xmm1); break;
1047 case Token::MUL: __ mulsd(xmm0, xmm1); break;
1048 case Token::DIV: __ divsd(xmm0, xmm1); break;
1049 default: UNREACHABLE();
1050 }
1051 __ movdbl(FieldOperand(ecx, HeapNumber::kValueOffset), xmm0);
1052 } else { // SSE2 not available, use FPU.
1053 FloatingPointHelper::LoadFloatSmis(masm, ebx);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001054 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001055 case Token::ADD: __ faddp(1); break;
1056 case Token::SUB: __ fsubp(1); break;
1057 case Token::MUL: __ fmulp(1); break;
1058 case Token::DIV: __ fdivp(1); break;
1059 default: UNREACHABLE();
1060 }
1061 __ fstp_d(FieldOperand(ecx, HeapNumber::kValueOffset));
1062 }
1063 __ mov(eax, ecx);
1064 __ ret(0);
1065 break;
1066 }
1067
1068 default:
1069 break;
1070 }
1071 }
1072
1073 // 7. Non-smi operands, fall out to the non-smi code with the operands in
1074 // edx and eax.
1075 Comment done_comment(masm, "-- Enter non-smi code");
1076 __ bind(&not_smis);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001077 switch (op) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001078 case Token::BIT_OR:
1079 case Token::SHL:
1080 case Token::SAR:
1081 case Token::SHR:
1082 // Right operand is saved in ecx and eax was destroyed by the smi
1083 // check.
1084 __ mov(eax, ecx);
1085 break;
1086
1087 case Token::DIV:
1088 case Token::MOD:
1089 // Operands are in eax, ebx at this point.
1090 __ mov(edx, eax);
1091 __ mov(eax, ebx);
1092 break;
1093
1094 default:
1095 break;
1096 }
1097}
1098
1099
danno@chromium.org40cb8782011-05-25 07:58:50 +00001100void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001101 Label right_arg_changed, call_runtime;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001102
1103 switch (op_) {
1104 case Token::ADD:
1105 case Token::SUB:
1106 case Token::MUL:
1107 case Token::DIV:
1108 break;
1109 case Token::MOD:
1110 case Token::BIT_OR:
1111 case Token::BIT_AND:
1112 case Token::BIT_XOR:
1113 case Token::SAR:
1114 case Token::SHL:
1115 case Token::SHR:
1116 GenerateRegisterArgsPush(masm);
1117 break;
1118 default:
1119 UNREACHABLE();
1120 }
1121
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00001122 if (op_ == Token::MOD && encoded_right_arg_.has_value) {
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001123 // It is guaranteed that the value will fit into a Smi, because if it
1124 // didn't, we wouldn't be here, see BinaryOp_Patch.
1125 __ cmp(eax, Immediate(Smi::FromInt(fixed_right_arg_value())));
1126 __ j(not_equal, &right_arg_changed);
1127 }
1128
danno@chromium.org40cb8782011-05-25 07:58:50 +00001129 if (result_type_ == BinaryOpIC::UNINITIALIZED ||
1130 result_type_ == BinaryOpIC::SMI) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001131 BinaryOpStub_GenerateSmiCode(
1132 masm, &call_runtime, NO_HEAPNUMBER_RESULTS, op_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001133 } else {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001134 BinaryOpStub_GenerateSmiCode(
1135 masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS, op_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001136 }
ulan@chromium.org906e2fb2013-05-14 08:14:38 +00001137
1138 // Code falls through if the result is not returned as either a smi or heap
1139 // number.
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001140 __ bind(&right_arg_changed);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001141 switch (op_) {
1142 case Token::ADD:
1143 case Token::SUB:
1144 case Token::MUL:
1145 case Token::DIV:
1146 GenerateTypeTransition(masm);
1147 break;
1148 case Token::MOD:
1149 case Token::BIT_OR:
1150 case Token::BIT_AND:
1151 case Token::BIT_XOR:
1152 case Token::SAR:
1153 case Token::SHL:
1154 case Token::SHR:
1155 GenerateTypeTransitionWithSavedArgs(masm);
1156 break;
1157 default:
1158 UNREACHABLE();
1159 }
ulan@chromium.org906e2fb2013-05-14 08:14:38 +00001160
1161 __ bind(&call_runtime);
1162 switch (op_) {
1163 case Token::ADD:
1164 case Token::SUB:
1165 case Token::MUL:
1166 case Token::DIV:
ulan@chromium.org906e2fb2013-05-14 08:14:38 +00001167 break;
1168 case Token::MOD:
1169 case Token::BIT_OR:
1170 case Token::BIT_AND:
1171 case Token::BIT_XOR:
1172 case Token::SAR:
1173 case Token::SHL:
1174 case Token::SHR:
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001175 BinaryOpStub_GenerateRegisterArgsPop(masm);
ulan@chromium.org906e2fb2013-05-14 08:14:38 +00001176 break;
1177 default:
1178 UNREACHABLE();
1179 }
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001180
1181 {
1182 FrameScope scope(masm, StackFrame::INTERNAL);
1183 __ push(edx);
1184 __ push(eax);
1185 GenerateCallRuntime(masm);
1186 }
1187 __ ret(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001188}
1189
1190
danno@chromium.org40cb8782011-05-25 07:58:50 +00001191void BinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) {
danno@chromium.org160a7b02011-04-18 15:51:38 +00001192 Label call_runtime;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001193 ASSERT(left_type_ == BinaryOpIC::STRING && right_type_ == BinaryOpIC::STRING);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001194 ASSERT(op_ == Token::ADD);
1195 // If both arguments are strings, call the string add stub.
1196 // Otherwise, do a transition.
1197
1198 // Registers containing left and right operands respectively.
1199 Register left = edx;
1200 Register right = eax;
1201
1202 // Test if left operand is a string.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001203 __ JumpIfSmi(left, &call_runtime, Label::kNear);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001204 __ CmpObjectType(left, FIRST_NONSTRING_TYPE, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001205 __ j(above_equal, &call_runtime, Label::kNear);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001206
1207 // Test if right operand is a string.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001208 __ JumpIfSmi(right, &call_runtime, Label::kNear);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001209 __ CmpObjectType(right, FIRST_NONSTRING_TYPE, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001210 __ j(above_equal, &call_runtime, Label::kNear);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001211
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +00001212 StringAddStub string_add_stub(
1213 (StringAddFlags)(STRING_ADD_CHECK_NONE | STRING_ADD_ERECT_FRAME));
danno@chromium.org160a7b02011-04-18 15:51:38 +00001214 GenerateRegisterArgsPush(masm);
1215 __ TailCallStub(&string_add_stub);
1216
1217 __ bind(&call_runtime);
1218 GenerateTypeTransition(masm);
1219}
1220
1221
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001222static void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm,
1223 Label* alloc_failure,
1224 OverwriteMode mode);
1225
1226
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001227// Input:
1228// edx: left operand (tagged)
1229// eax: right operand (tagged)
1230// Output:
1231// eax: result (tagged)
danno@chromium.org40cb8782011-05-25 07:58:50 +00001232void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001233 Label call_runtime;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001234 ASSERT(Max(left_type_, right_type_) == BinaryOpIC::INT32);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001235
1236 // Floating point case.
1237 switch (op_) {
1238 case Token::ADD:
1239 case Token::SUB:
1240 case Token::MUL:
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001241 case Token::DIV:
1242 case Token::MOD: {
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001243 Label not_floats, not_int32, right_arg_changed;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001244 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001245 CpuFeatureScope use_sse2(masm, SSE2);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001246 // It could be that only SMIs have been seen at either the left
1247 // or the right operand. For precise type feedback, patch the IC
1248 // again if this changes.
1249 // In theory, we would need the same check in the non-SSE2 case,
1250 // but since we don't support Crankshaft on such hardware we can
1251 // afford not to care about precise type feedback.
1252 if (left_type_ == BinaryOpIC::SMI) {
1253 __ JumpIfNotSmi(edx, &not_int32);
1254 }
1255 if (right_type_ == BinaryOpIC::SMI) {
1256 __ JumpIfNotSmi(eax, &not_int32);
1257 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001258 FloatingPointHelper::LoadSSE2Operands(masm, &not_floats);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001259 FloatingPointHelper::CheckSSE2OperandIsInt32(
1260 masm, &not_int32, xmm0, ebx, ecx, xmm2);
1261 FloatingPointHelper::CheckSSE2OperandIsInt32(
1262 masm, &not_int32, xmm1, edi, ecx, xmm2);
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001263 if (op_ == Token::MOD) {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00001264 if (encoded_right_arg_.has_value) {
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001265 __ cmp(edi, Immediate(fixed_right_arg_value()));
1266 __ j(not_equal, &right_arg_changed);
1267 }
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001268 GenerateRegisterArgsPush(masm);
1269 __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
1270 } else {
1271 switch (op_) {
1272 case Token::ADD: __ addsd(xmm0, xmm1); break;
1273 case Token::SUB: __ subsd(xmm0, xmm1); break;
1274 case Token::MUL: __ mulsd(xmm0, xmm1); break;
1275 case Token::DIV: __ divsd(xmm0, xmm1); break;
1276 default: UNREACHABLE();
1277 }
1278 // Check result type if it is currently Int32.
1279 if (result_type_ <= BinaryOpIC::INT32) {
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001280 FloatingPointHelper::CheckSSE2OperandIsInt32(
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001281 masm, &not_int32, xmm0, ecx, ecx, xmm2);
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001282 }
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001283 BinaryOpStub_GenerateHeapResultAllocation(masm, &call_runtime, mode_);
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001284 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1285 __ ret(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001286 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001287 } else { // SSE2 not available, use FPU.
1288 FloatingPointHelper::CheckFloatOperands(masm, &not_floats, ebx);
1289 FloatingPointHelper::LoadFloatOperands(
1290 masm,
1291 ecx,
1292 FloatingPointHelper::ARGS_IN_REGISTERS);
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001293 if (op_ == Token::MOD) {
1294 // The operands are now on the FPU stack, but we don't need them.
1295 __ fstp(0);
1296 __ fstp(0);
1297 GenerateRegisterArgsPush(masm);
1298 __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
1299 } else {
1300 switch (op_) {
1301 case Token::ADD: __ faddp(1); break;
1302 case Token::SUB: __ fsubp(1); break;
1303 case Token::MUL: __ fmulp(1); break;
1304 case Token::DIV: __ fdivp(1); break;
1305 default: UNREACHABLE();
1306 }
1307 Label after_alloc_failure;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001308 BinaryOpStub_GenerateHeapResultAllocation(
1309 masm, &after_alloc_failure, mode_);
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001310 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1311 __ ret(0);
1312 __ bind(&after_alloc_failure);
1313 __ fstp(0); // Pop FPU stack before calling runtime.
1314 __ jmp(&call_runtime);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001315 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001316 }
1317
1318 __ bind(&not_floats);
1319 __ bind(&not_int32);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001320 __ bind(&right_arg_changed);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001321 GenerateTypeTransition(masm);
1322 break;
1323 }
1324
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001325 case Token::BIT_OR:
1326 case Token::BIT_AND:
1327 case Token::BIT_XOR:
1328 case Token::SAR:
1329 case Token::SHL:
1330 case Token::SHR: {
1331 GenerateRegisterArgsPush(masm);
1332 Label not_floats;
1333 Label not_int32;
1334 Label non_smi_result;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001335 bool use_sse3 = platform_specific_bit_;
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001336 FloatingPointHelper::LoadUnknownsAsIntegers(
1337 masm, use_sse3, left_type_, right_type_, &not_floats);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001338 switch (op_) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001339 case Token::BIT_OR: __ or_(eax, ecx); break;
1340 case Token::BIT_AND: __ and_(eax, ecx); break;
1341 case Token::BIT_XOR: __ xor_(eax, ecx); break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001342 case Token::SAR: __ sar_cl(eax); break;
1343 case Token::SHL: __ shl_cl(eax); break;
1344 case Token::SHR: __ shr_cl(eax); break;
1345 default: UNREACHABLE();
1346 }
1347 if (op_ == Token::SHR) {
1348 // Check if result is non-negative and fits in a smi.
1349 __ test(eax, Immediate(0xc0000000));
1350 __ j(not_zero, &call_runtime);
1351 } else {
1352 // Check if result fits in a smi.
1353 __ cmp(eax, 0xc0000000);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001354 __ j(negative, &non_smi_result, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001355 }
1356 // Tag smi result and return.
1357 __ SmiTag(eax);
1358 __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack.
1359
1360 // All ops except SHR return a signed int32 that we load in
1361 // a HeapNumber.
1362 if (op_ != Token::SHR) {
1363 __ bind(&non_smi_result);
1364 // Allocate a heap number if needed.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001365 __ mov(ebx, eax); // ebx: result
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001366 Label skip_allocation;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001367 switch (mode_) {
1368 case OVERWRITE_LEFT:
1369 case OVERWRITE_RIGHT:
1370 // If the operand was an object, we skip the
1371 // allocation of a heap number.
1372 __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
1373 1 * kPointerSize : 2 * kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00001374 __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001375 // Fall through!
1376 case NO_OVERWRITE:
1377 __ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
1378 __ bind(&skip_allocation);
1379 break;
1380 default: UNREACHABLE();
1381 }
1382 // Store the result in the HeapNumber and return.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001383 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001384 CpuFeatureScope use_sse2(masm, SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001385 __ cvtsi2sd(xmm0, ebx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001386 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1387 } else {
1388 __ mov(Operand(esp, 1 * kPointerSize), ebx);
1389 __ fild_s(Operand(esp, 1 * kPointerSize));
1390 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1391 }
1392 __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack.
1393 }
1394
1395 __ bind(&not_floats);
1396 __ bind(&not_int32);
1397 GenerateTypeTransitionWithSavedArgs(masm);
1398 break;
1399 }
1400 default: UNREACHABLE(); break;
1401 }
1402
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001403 // If an allocation fails, or SHR hits a hard case, use the runtime system to
1404 // get the correct result.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001405 __ bind(&call_runtime);
1406
1407 switch (op_) {
1408 case Token::ADD:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001409 case Token::SUB:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001410 case Token::MUL:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001411 case Token::DIV:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001412 break;
1413 case Token::MOD:
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001414 return; // Handled above.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001415 case Token::BIT_OR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001416 case Token::BIT_AND:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001417 case Token::BIT_XOR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001418 case Token::SAR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001419 case Token::SHL:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001420 case Token::SHR:
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001421 BinaryOpStub_GenerateRegisterArgsPop(masm);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001422 break;
1423 default:
1424 UNREACHABLE();
1425 }
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001426
1427 {
1428 FrameScope scope(masm, StackFrame::INTERNAL);
1429 __ push(edx);
1430 __ push(eax);
1431 GenerateCallRuntime(masm);
1432 }
1433 __ ret(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001434}
1435
1436
danno@chromium.org40cb8782011-05-25 07:58:50 +00001437void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
lrn@chromium.org7516f052011-03-30 08:52:27 +00001438 if (op_ == Token::ADD) {
1439 // Handle string addition here, because it is the only operation
1440 // that does not do a ToNumber conversion on the operands.
1441 GenerateAddStrings(masm);
1442 }
1443
danno@chromium.org160a7b02011-04-18 15:51:38 +00001444 Factory* factory = masm->isolate()->factory();
1445
lrn@chromium.org7516f052011-03-30 08:52:27 +00001446 // Convert odd ball arguments to numbers.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001447 Label check, done;
danno@chromium.org160a7b02011-04-18 15:51:38 +00001448 __ cmp(edx, factory->undefined_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001449 __ j(not_equal, &check, Label::kNear);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001450 if (Token::IsBitOp(op_)) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001451 __ xor_(edx, edx);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001452 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00001453 __ mov(edx, Immediate(factory->nan_value()));
lrn@chromium.org7516f052011-03-30 08:52:27 +00001454 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001455 __ jmp(&done, Label::kNear);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001456 __ bind(&check);
danno@chromium.org160a7b02011-04-18 15:51:38 +00001457 __ cmp(eax, factory->undefined_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001458 __ j(not_equal, &done, Label::kNear);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001459 if (Token::IsBitOp(op_)) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001460 __ xor_(eax, eax);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001461 } else {
danno@chromium.org160a7b02011-04-18 15:51:38 +00001462 __ mov(eax, Immediate(factory->nan_value()));
lrn@chromium.org7516f052011-03-30 08:52:27 +00001463 }
1464 __ bind(&done);
1465
hpayer@chromium.org8432c912013-02-28 15:55:26 +00001466 GenerateNumberStub(masm);
lrn@chromium.org7516f052011-03-30 08:52:27 +00001467}
1468
1469
hpayer@chromium.org8432c912013-02-28 15:55:26 +00001470void BinaryOpStub::GenerateNumberStub(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001471 Label call_runtime;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001472
1473 // Floating point case.
1474 switch (op_) {
1475 case Token::ADD:
1476 case Token::SUB:
1477 case Token::MUL:
1478 case Token::DIV: {
1479 Label not_floats;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001480 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001481 CpuFeatureScope use_sse2(masm, SSE2);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001482
1483 // It could be that only SMIs have been seen at either the left
1484 // or the right operand. For precise type feedback, patch the IC
1485 // again if this changes.
1486 // In theory, we would need the same check in the non-SSE2 case,
1487 // but since we don't support Crankshaft on such hardware we can
1488 // afford not to care about precise type feedback.
1489 if (left_type_ == BinaryOpIC::SMI) {
1490 __ JumpIfNotSmi(edx, &not_floats);
1491 }
1492 if (right_type_ == BinaryOpIC::SMI) {
1493 __ JumpIfNotSmi(eax, &not_floats);
1494 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001495 FloatingPointHelper::LoadSSE2Operands(masm, &not_floats);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001496 if (left_type_ == BinaryOpIC::INT32) {
1497 FloatingPointHelper::CheckSSE2OperandIsInt32(
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001498 masm, &not_floats, xmm0, ecx, ecx, xmm2);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001499 }
1500 if (right_type_ == BinaryOpIC::INT32) {
1501 FloatingPointHelper::CheckSSE2OperandIsInt32(
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001502 masm, &not_floats, xmm1, ecx, ecx, xmm2);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001503 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001504
1505 switch (op_) {
1506 case Token::ADD: __ addsd(xmm0, xmm1); break;
1507 case Token::SUB: __ subsd(xmm0, xmm1); break;
1508 case Token::MUL: __ mulsd(xmm0, xmm1); break;
1509 case Token::DIV: __ divsd(xmm0, xmm1); break;
1510 default: UNREACHABLE();
1511 }
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001512 BinaryOpStub_GenerateHeapResultAllocation(masm, &call_runtime, mode_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001513 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1514 __ ret(0);
1515 } else { // SSE2 not available, use FPU.
1516 FloatingPointHelper::CheckFloatOperands(masm, &not_floats, ebx);
1517 FloatingPointHelper::LoadFloatOperands(
1518 masm,
1519 ecx,
1520 FloatingPointHelper::ARGS_IN_REGISTERS);
1521 switch (op_) {
1522 case Token::ADD: __ faddp(1); break;
1523 case Token::SUB: __ fsubp(1); break;
1524 case Token::MUL: __ fmulp(1); break;
1525 case Token::DIV: __ fdivp(1); break;
1526 default: UNREACHABLE();
1527 }
1528 Label after_alloc_failure;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001529 BinaryOpStub_GenerateHeapResultAllocation(
1530 masm, &after_alloc_failure, mode_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001531 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1532 __ ret(0);
1533 __ bind(&after_alloc_failure);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001534 __ fstp(0); // Pop FPU stack before calling runtime.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001535 __ jmp(&call_runtime);
1536 }
1537
1538 __ bind(&not_floats);
1539 GenerateTypeTransition(masm);
1540 break;
1541 }
1542
1543 case Token::MOD: {
1544 // For MOD we go directly to runtime in the non-smi case.
1545 break;
1546 }
1547 case Token::BIT_OR:
1548 case Token::BIT_AND:
1549 case Token::BIT_XOR:
1550 case Token::SAR:
1551 case Token::SHL:
1552 case Token::SHR: {
1553 GenerateRegisterArgsPush(masm);
1554 Label not_floats;
1555 Label non_smi_result;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001556 // We do not check the input arguments here, as any value is
1557 // unconditionally truncated to an int32 anyway. To get the
1558 // right optimized code, int32 type feedback is just right.
1559 bool use_sse3 = platform_specific_bit_;
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001560 FloatingPointHelper::LoadUnknownsAsIntegers(
1561 masm, use_sse3, left_type_, right_type_, &not_floats);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001562 switch (op_) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001563 case Token::BIT_OR: __ or_(eax, ecx); break;
1564 case Token::BIT_AND: __ and_(eax, ecx); break;
1565 case Token::BIT_XOR: __ xor_(eax, ecx); break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001566 case Token::SAR: __ sar_cl(eax); break;
1567 case Token::SHL: __ shl_cl(eax); break;
1568 case Token::SHR: __ shr_cl(eax); break;
1569 default: UNREACHABLE();
1570 }
1571 if (op_ == Token::SHR) {
1572 // Check if result is non-negative and fits in a smi.
1573 __ test(eax, Immediate(0xc0000000));
1574 __ j(not_zero, &call_runtime);
1575 } else {
1576 // Check if result fits in a smi.
1577 __ cmp(eax, 0xc0000000);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001578 __ j(negative, &non_smi_result, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001579 }
1580 // Tag smi result and return.
1581 __ SmiTag(eax);
1582 __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack.
1583
1584 // All ops except SHR return a signed int32 that we load in
1585 // a HeapNumber.
1586 if (op_ != Token::SHR) {
1587 __ bind(&non_smi_result);
1588 // Allocate a heap number if needed.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001589 __ mov(ebx, eax); // ebx: result
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001590 Label skip_allocation;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001591 switch (mode_) {
1592 case OVERWRITE_LEFT:
1593 case OVERWRITE_RIGHT:
1594 // If the operand was an object, we skip the
1595 // allocation of a heap number.
1596 __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
1597 1 * kPointerSize : 2 * kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00001598 __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001599 // Fall through!
1600 case NO_OVERWRITE:
1601 __ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
1602 __ bind(&skip_allocation);
1603 break;
1604 default: UNREACHABLE();
1605 }
1606 // Store the result in the HeapNumber and return.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001607 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001608 CpuFeatureScope use_sse2(masm, SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001609 __ cvtsi2sd(xmm0, ebx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001610 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1611 } else {
1612 __ mov(Operand(esp, 1 * kPointerSize), ebx);
1613 __ fild_s(Operand(esp, 1 * kPointerSize));
1614 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1615 }
1616 __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack.
1617 }
1618
1619 __ bind(&not_floats);
1620 GenerateTypeTransitionWithSavedArgs(masm);
1621 break;
1622 }
1623 default: UNREACHABLE(); break;
1624 }
1625
1626 // If an allocation fails, or SHR or MOD hit a hard case,
1627 // use the runtime system to get the correct result.
1628 __ bind(&call_runtime);
1629
1630 switch (op_) {
1631 case Token::ADD:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001632 case Token::SUB:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001633 case Token::MUL:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001634 case Token::DIV:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001635 case Token::MOD:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001636 break;
1637 case Token::BIT_OR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001638 case Token::BIT_AND:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001639 case Token::BIT_XOR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001640 case Token::SAR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001641 case Token::SHL:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001642 case Token::SHR:
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001643 BinaryOpStub_GenerateRegisterArgsPop(masm);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001644 break;
1645 default:
1646 UNREACHABLE();
1647 }
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001648
1649 {
1650 FrameScope scope(masm, StackFrame::INTERNAL);
1651 __ push(edx);
1652 __ push(eax);
1653 GenerateCallRuntime(masm);
1654 }
1655 __ ret(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001656}
1657
1658
danno@chromium.org40cb8782011-05-25 07:58:50 +00001659void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001660 Label call_runtime;
1661
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00001662 Counters* counters = masm->isolate()->counters();
1663 __ IncrementCounter(counters->generic_binary_stub_calls(), 1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001664
1665 switch (op_) {
1666 case Token::ADD:
1667 case Token::SUB:
1668 case Token::MUL:
1669 case Token::DIV:
1670 break;
1671 case Token::MOD:
1672 case Token::BIT_OR:
1673 case Token::BIT_AND:
1674 case Token::BIT_XOR:
1675 case Token::SAR:
1676 case Token::SHL:
1677 case Token::SHR:
1678 GenerateRegisterArgsPush(masm);
1679 break;
1680 default:
1681 UNREACHABLE();
1682 }
1683
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001684 BinaryOpStub_GenerateSmiCode(
1685 masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS, op_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001686
1687 // Floating point case.
1688 switch (op_) {
1689 case Token::ADD:
1690 case Token::SUB:
1691 case Token::MUL:
1692 case Token::DIV: {
1693 Label not_floats;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001694 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001695 CpuFeatureScope use_sse2(masm, SSE2);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001696 FloatingPointHelper::LoadSSE2Operands(masm, &not_floats);
1697
1698 switch (op_) {
1699 case Token::ADD: __ addsd(xmm0, xmm1); break;
1700 case Token::SUB: __ subsd(xmm0, xmm1); break;
1701 case Token::MUL: __ mulsd(xmm0, xmm1); break;
1702 case Token::DIV: __ divsd(xmm0, xmm1); break;
1703 default: UNREACHABLE();
1704 }
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001705 BinaryOpStub_GenerateHeapResultAllocation(masm, &call_runtime, mode_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001706 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1707 __ ret(0);
1708 } else { // SSE2 not available, use FPU.
1709 FloatingPointHelper::CheckFloatOperands(masm, &not_floats, ebx);
1710 FloatingPointHelper::LoadFloatOperands(
1711 masm,
1712 ecx,
1713 FloatingPointHelper::ARGS_IN_REGISTERS);
1714 switch (op_) {
1715 case Token::ADD: __ faddp(1); break;
1716 case Token::SUB: __ fsubp(1); break;
1717 case Token::MUL: __ fmulp(1); break;
1718 case Token::DIV: __ fdivp(1); break;
1719 default: UNREACHABLE();
1720 }
1721 Label after_alloc_failure;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001722 BinaryOpStub_GenerateHeapResultAllocation(
1723 masm, &after_alloc_failure, mode_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001724 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1725 __ ret(0);
1726 __ bind(&after_alloc_failure);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001727 __ fstp(0); // Pop FPU stack before calling runtime.
1728 __ jmp(&call_runtime);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001729 }
1730 __ bind(&not_floats);
1731 break;
1732 }
1733 case Token::MOD: {
1734 // For MOD we go directly to runtime in the non-smi case.
1735 break;
1736 }
1737 case Token::BIT_OR:
1738 case Token::BIT_AND:
1739 case Token::BIT_XOR:
1740 case Token::SAR:
1741 case Token::SHL:
1742 case Token::SHR: {
1743 Label non_smi_result;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001744 bool use_sse3 = platform_specific_bit_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001745 FloatingPointHelper::LoadUnknownsAsIntegers(masm,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001746 use_sse3,
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001747 BinaryOpIC::GENERIC,
1748 BinaryOpIC::GENERIC,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001749 &call_runtime);
1750 switch (op_) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001751 case Token::BIT_OR: __ or_(eax, ecx); break;
1752 case Token::BIT_AND: __ and_(eax, ecx); break;
1753 case Token::BIT_XOR: __ xor_(eax, ecx); break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001754 case Token::SAR: __ sar_cl(eax); break;
1755 case Token::SHL: __ shl_cl(eax); break;
1756 case Token::SHR: __ shr_cl(eax); break;
1757 default: UNREACHABLE();
1758 }
1759 if (op_ == Token::SHR) {
1760 // Check if result is non-negative and fits in a smi.
1761 __ test(eax, Immediate(0xc0000000));
1762 __ j(not_zero, &call_runtime);
1763 } else {
1764 // Check if result fits in a smi.
1765 __ cmp(eax, 0xc0000000);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001766 __ j(negative, &non_smi_result, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001767 }
1768 // Tag smi result and return.
1769 __ SmiTag(eax);
1770 __ ret(2 * kPointerSize); // Drop the arguments from the stack.
1771
1772 // All ops except SHR return a signed int32 that we load in
1773 // a HeapNumber.
1774 if (op_ != Token::SHR) {
1775 __ bind(&non_smi_result);
1776 // Allocate a heap number if needed.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001777 __ mov(ebx, eax); // ebx: result
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001778 Label skip_allocation;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001779 switch (mode_) {
1780 case OVERWRITE_LEFT:
1781 case OVERWRITE_RIGHT:
1782 // If the operand was an object, we skip the
1783 // allocation of a heap number.
1784 __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
1785 1 * kPointerSize : 2 * kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00001786 __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001787 // Fall through!
1788 case NO_OVERWRITE:
1789 __ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
1790 __ bind(&skip_allocation);
1791 break;
1792 default: UNREACHABLE();
1793 }
1794 // Store the result in the HeapNumber and return.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001795 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001796 CpuFeatureScope use_sse2(masm, SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001797 __ cvtsi2sd(xmm0, ebx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001798 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
1799 } else {
1800 __ mov(Operand(esp, 1 * kPointerSize), ebx);
1801 __ fild_s(Operand(esp, 1 * kPointerSize));
1802 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
1803 }
1804 __ ret(2 * kPointerSize);
1805 }
1806 break;
1807 }
1808 default: UNREACHABLE(); break;
1809 }
1810
1811 // If all else fails, use the runtime system to get the correct
1812 // result.
1813 __ bind(&call_runtime);
1814 switch (op_) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001815 case Token::ADD:
ager@chromium.org0ee099b2011-01-25 14:06:47 +00001816 GenerateAddStrings(masm);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001817 // Fall through.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001818 case Token::SUB:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001819 case Token::MUL:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001820 case Token::DIV:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001821 break;
1822 case Token::MOD:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001823 case Token::BIT_OR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001824 case Token::BIT_AND:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001825 case Token::BIT_XOR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001826 case Token::SAR:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001827 case Token::SHL:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001828 case Token::SHR:
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001829 BinaryOpStub_GenerateRegisterArgsPop(masm);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001830 break;
1831 default:
1832 UNREACHABLE();
1833 }
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00001834
1835 {
1836 FrameScope scope(masm, StackFrame::INTERNAL);
1837 __ push(edx);
1838 __ push(eax);
1839 GenerateCallRuntime(masm);
1840 }
1841 __ ret(0);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001842}
1843
1844
danno@chromium.org40cb8782011-05-25 07:58:50 +00001845void BinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00001846 ASSERT(op_ == Token::ADD);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001847 Label left_not_string, call_runtime;
ager@chromium.org0ee099b2011-01-25 14:06:47 +00001848
1849 // Registers containing left and right operands respectively.
1850 Register left = edx;
1851 Register right = eax;
1852
1853 // Test if left operand is a string.
whesse@chromium.org7b260152011-06-20 15:33:18 +00001854 __ JumpIfSmi(left, &left_not_string, Label::kNear);
ager@chromium.org0ee099b2011-01-25 14:06:47 +00001855 __ CmpObjectType(left, FIRST_NONSTRING_TYPE, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001856 __ j(above_equal, &left_not_string, Label::kNear);
ager@chromium.org0ee099b2011-01-25 14:06:47 +00001857
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +00001858 StringAddStub string_add_left_stub(
1859 (StringAddFlags)(STRING_ADD_CHECK_RIGHT | STRING_ADD_ERECT_FRAME));
ager@chromium.org0ee099b2011-01-25 14:06:47 +00001860 GenerateRegisterArgsPush(masm);
1861 __ TailCallStub(&string_add_left_stub);
1862
1863 // Left operand is not a string, test right.
1864 __ bind(&left_not_string);
whesse@chromium.org7b260152011-06-20 15:33:18 +00001865 __ JumpIfSmi(right, &call_runtime, Label::kNear);
ager@chromium.org0ee099b2011-01-25 14:06:47 +00001866 __ CmpObjectType(right, FIRST_NONSTRING_TYPE, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001867 __ j(above_equal, &call_runtime, Label::kNear);
ager@chromium.org0ee099b2011-01-25 14:06:47 +00001868
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +00001869 StringAddStub string_add_right_stub(
1870 (StringAddFlags)(STRING_ADD_CHECK_LEFT | STRING_ADD_ERECT_FRAME));
ager@chromium.org0ee099b2011-01-25 14:06:47 +00001871 GenerateRegisterArgsPush(masm);
1872 __ TailCallStub(&string_add_right_stub);
1873
1874 // Neither argument is a string.
1875 __ bind(&call_runtime);
1876}
1877
1878
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001879static void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm,
1880 Label* alloc_failure,
1881 OverwriteMode mode) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001882 Label skip_allocation;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001883 switch (mode) {
1884 case OVERWRITE_LEFT: {
1885 // If the argument in edx is already an object, we skip the
1886 // allocation of a heap number.
whesse@chromium.org7b260152011-06-20 15:33:18 +00001887 __ JumpIfNotSmi(edx, &skip_allocation, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001888 // Allocate a heap number for the result. Keep eax and edx intact
1889 // for the possible runtime call.
1890 __ AllocateHeapNumber(ebx, ecx, no_reg, alloc_failure);
1891 // Now edx can be overwritten losing one of the arguments as we are
1892 // now done and will not need it any more.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001893 __ mov(edx, ebx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001894 __ bind(&skip_allocation);
1895 // Use object in edx as a result holder
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001896 __ mov(eax, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001897 break;
1898 }
1899 case OVERWRITE_RIGHT:
1900 // If the argument in eax is already an object, we skip the
1901 // allocation of a heap number.
whesse@chromium.org7b260152011-06-20 15:33:18 +00001902 __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001903 // Fall through!
1904 case NO_OVERWRITE:
1905 // Allocate a heap number for the result. Keep eax and edx intact
1906 // for the possible runtime call.
1907 __ AllocateHeapNumber(ebx, ecx, no_reg, alloc_failure);
1908 // Now eax can be overwritten losing one of the arguments as we are
1909 // now done and will not need it any more.
1910 __ mov(eax, ebx);
1911 __ bind(&skip_allocation);
1912 break;
1913 default: UNREACHABLE();
1914 }
1915}
1916
1917
danno@chromium.org40cb8782011-05-25 07:58:50 +00001918void BinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001919 __ pop(ecx);
1920 __ push(edx);
1921 __ push(eax);
1922 __ push(ecx);
1923}
1924
1925
ricow@chromium.org65fae842010-08-25 15:26:24 +00001926void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
whesse@chromium.org023421e2010-12-21 12:19:12 +00001927 // TAGGED case:
1928 // Input:
1929 // esp[4]: tagged number input argument (should be number).
1930 // esp[0]: return address.
1931 // Output:
1932 // eax: tagged double result.
1933 // UNTAGGED case:
1934 // Input::
1935 // esp[0]: return address.
1936 // xmm1: untagged double input argument
1937 // Output:
1938 // xmm1: untagged double result.
1939
ricow@chromium.org65fae842010-08-25 15:26:24 +00001940 Label runtime_call;
1941 Label runtime_call_clear_stack;
whesse@chromium.org023421e2010-12-21 12:19:12 +00001942 Label skip_cache;
1943 const bool tagged = (argument_type_ == TAGGED);
1944 if (tagged) {
1945 // Test that eax is a number.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001946 Label input_not_smi;
1947 Label loaded;
whesse@chromium.org023421e2010-12-21 12:19:12 +00001948 __ mov(eax, Operand(esp, kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00001949 __ JumpIfNotSmi(eax, &input_not_smi, Label::kNear);
whesse@chromium.org023421e2010-12-21 12:19:12 +00001950 // Input is a smi. Untag and load it onto the FPU stack.
1951 // Then load the low and high words of the double into ebx, edx.
1952 STATIC_ASSERT(kSmiTagSize == 1);
1953 __ sar(eax, 1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001954 __ sub(esp, Immediate(2 * kPointerSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00001955 __ mov(Operand(esp, 0), eax);
1956 __ fild_s(Operand(esp, 0));
1957 __ fst_d(Operand(esp, 0));
1958 __ pop(edx);
1959 __ pop(ebx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001960 __ jmp(&loaded, Label::kNear);
whesse@chromium.org023421e2010-12-21 12:19:12 +00001961 __ bind(&input_not_smi);
1962 // Check if input is a HeapNumber.
1963 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00001964 Factory* factory = masm->isolate()->factory();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001965 __ cmp(ebx, Immediate(factory->heap_number_map()));
whesse@chromium.org023421e2010-12-21 12:19:12 +00001966 __ j(not_equal, &runtime_call);
1967 // Input is a HeapNumber. Push it on the FPU stack and load its
1968 // low and high words into ebx, edx.
1969 __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
1970 __ mov(edx, FieldOperand(eax, HeapNumber::kExponentOffset));
1971 __ mov(ebx, FieldOperand(eax, HeapNumber::kMantissaOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00001972
whesse@chromium.org023421e2010-12-21 12:19:12 +00001973 __ bind(&loaded);
1974 } else { // UNTAGGED.
ulan@chromium.org750145a2013-03-07 15:14:13 +00001975 CpuFeatureScope scope(masm, SSE2);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001976 if (CpuFeatures::IsSupported(SSE4_1)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001977 CpuFeatureScope sse4_scope(masm, SSE4_1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001978 __ pextrd(edx, xmm1, 0x1); // copy xmm1[63..32] to edx.
whesse@chromium.org023421e2010-12-21 12:19:12 +00001979 } else {
1980 __ pshufd(xmm0, xmm1, 0x1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001981 __ movd(edx, xmm0);
whesse@chromium.org023421e2010-12-21 12:19:12 +00001982 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001983 __ movd(ebx, xmm1);
whesse@chromium.org023421e2010-12-21 12:19:12 +00001984 }
1985
1986 // ST[0] or xmm1 == double value
ricow@chromium.org65fae842010-08-25 15:26:24 +00001987 // ebx = low 32 bits of double value
1988 // edx = high 32 bits of double value
1989 // Compute hash (the shifts are arithmetic):
1990 // h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1);
1991 __ mov(ecx, ebx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001992 __ xor_(ecx, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001993 __ mov(eax, ecx);
1994 __ sar(eax, 16);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001995 __ xor_(ecx, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001996 __ mov(eax, ecx);
1997 __ sar(eax, 8);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001998 __ xor_(ecx, eax);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001999 ASSERT(IsPowerOf2(TranscendentalCache::SubCache::kCacheSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002000 __ and_(ecx,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002001 Immediate(TranscendentalCache::SubCache::kCacheSize - 1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002002
whesse@chromium.org023421e2010-12-21 12:19:12 +00002003 // ST[0] or xmm1 == double value.
ricow@chromium.org65fae842010-08-25 15:26:24 +00002004 // ebx = low 32 bits of double value.
2005 // edx = high 32 bits of double value.
2006 // ecx = TranscendentalCache::hash(double value).
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002007 ExternalReference cache_array =
2008 ExternalReference::transcendental_cache_array_address(masm->isolate());
2009 __ mov(eax, Immediate(cache_array));
2010 int cache_array_index =
2011 type_ * sizeof(masm->isolate()->transcendental_cache()->caches_[0]);
2012 __ mov(eax, Operand(eax, cache_array_index));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002013 // Eax points to the cache for the type type_.
2014 // If NULL, the cache hasn't been initialized yet, so go through runtime.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002015 __ test(eax, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002016 __ j(zero, &runtime_call_clear_stack);
2017#ifdef DEBUG
2018 // Check that the layout of cache elements match expectations.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002019 { TranscendentalCache::SubCache::Element test_elem[2];
ricow@chromium.org65fae842010-08-25 15:26:24 +00002020 char* elem_start = reinterpret_cast<char*>(&test_elem[0]);
2021 char* elem2_start = reinterpret_cast<char*>(&test_elem[1]);
2022 char* elem_in0 = reinterpret_cast<char*>(&(test_elem[0].in[0]));
2023 char* elem_in1 = reinterpret_cast<char*>(&(test_elem[0].in[1]));
2024 char* elem_out = reinterpret_cast<char*>(&(test_elem[0].output));
2025 CHECK_EQ(12, elem2_start - elem_start); // Two uint_32's and a pointer.
2026 CHECK_EQ(0, elem_in0 - elem_start);
2027 CHECK_EQ(kIntSize, elem_in1 - elem_start);
2028 CHECK_EQ(2 * kIntSize, elem_out - elem_start);
2029 }
2030#endif
2031 // Find the address of the ecx'th entry in the cache, i.e., &eax[ecx*12].
2032 __ lea(ecx, Operand(ecx, ecx, times_2, 0));
2033 __ lea(ecx, Operand(eax, ecx, times_4, 0));
2034 // Check if cache matches: Double value is stored in uint32_t[2] array.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002035 Label cache_miss;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002036 __ cmp(ebx, Operand(ecx, 0));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002037 __ j(not_equal, &cache_miss, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002038 __ cmp(edx, Operand(ecx, kIntSize));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002039 __ j(not_equal, &cache_miss, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002040 // Cache hit!
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00002041 Counters* counters = masm->isolate()->counters();
2042 __ IncrementCounter(counters->transcendental_cache_hit(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002043 __ mov(eax, Operand(ecx, 2 * kIntSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002044 if (tagged) {
2045 __ fstp(0);
2046 __ ret(kPointerSize);
2047 } else { // UNTAGGED.
ulan@chromium.org750145a2013-03-07 15:14:13 +00002048 CpuFeatureScope scope(masm, SSE2);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002049 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
2050 __ Ret();
2051 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002052
2053 __ bind(&cache_miss);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00002054 __ IncrementCounter(counters->transcendental_cache_miss(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002055 // Update cache with new value.
2056 // We are short on registers, so use no_reg as scratch.
2057 // This gives slightly larger code.
whesse@chromium.org023421e2010-12-21 12:19:12 +00002058 if (tagged) {
2059 __ AllocateHeapNumber(eax, edi, no_reg, &runtime_call_clear_stack);
2060 } else { // UNTAGGED.
ulan@chromium.org750145a2013-03-07 15:14:13 +00002061 CpuFeatureScope scope(masm, SSE2);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002062 __ AllocateHeapNumber(eax, edi, no_reg, &skip_cache);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002063 __ sub(esp, Immediate(kDoubleSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002064 __ movdbl(Operand(esp, 0), xmm1);
2065 __ fld_d(Operand(esp, 0));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002066 __ add(esp, Immediate(kDoubleSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002067 }
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002068 GenerateOperation(masm, type_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002069 __ mov(Operand(ecx, 0), ebx);
2070 __ mov(Operand(ecx, kIntSize), edx);
2071 __ mov(Operand(ecx, 2 * kIntSize), eax);
2072 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002073 if (tagged) {
2074 __ ret(kPointerSize);
2075 } else { // UNTAGGED.
ulan@chromium.org750145a2013-03-07 15:14:13 +00002076 CpuFeatureScope scope(masm, SSE2);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002077 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
2078 __ Ret();
ricow@chromium.org65fae842010-08-25 15:26:24 +00002079
whesse@chromium.org023421e2010-12-21 12:19:12 +00002080 // Skip cache and return answer directly, only in untagged case.
2081 __ bind(&skip_cache);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002082 __ sub(esp, Immediate(kDoubleSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002083 __ movdbl(Operand(esp, 0), xmm1);
2084 __ fld_d(Operand(esp, 0));
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002085 GenerateOperation(masm, type_);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002086 __ fstp_d(Operand(esp, 0));
2087 __ movdbl(xmm1, Operand(esp, 0));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002088 __ add(esp, Immediate(kDoubleSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +00002089 // We return the value in xmm1 without adding it to the cache, but
2090 // we cause a scavenging GC so that future allocations will succeed.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002091 {
2092 FrameScope scope(masm, StackFrame::INTERNAL);
2093 // Allocate an unused object bigger than a HeapNumber.
2094 __ push(Immediate(Smi::FromInt(2 * kDoubleSize)));
2095 __ CallRuntimeSaveDoubles(Runtime::kAllocateInNewSpace);
2096 }
whesse@chromium.org023421e2010-12-21 12:19:12 +00002097 __ Ret();
2098 }
2099
2100 // Call runtime, doing whatever allocation and cleanup is necessary.
2101 if (tagged) {
2102 __ bind(&runtime_call_clear_stack);
2103 __ fstp(0);
2104 __ bind(&runtime_call);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002105 ExternalReference runtime =
2106 ExternalReference(RuntimeFunction(), masm->isolate());
2107 __ TailCallExternalReference(runtime, 1, 1);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002108 } else { // UNTAGGED.
ulan@chromium.org750145a2013-03-07 15:14:13 +00002109 CpuFeatureScope scope(masm, SSE2);
whesse@chromium.org023421e2010-12-21 12:19:12 +00002110 __ bind(&runtime_call_clear_stack);
2111 __ bind(&runtime_call);
2112 __ AllocateHeapNumber(eax, edi, no_reg, &skip_cache);
2113 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002114 {
2115 FrameScope scope(masm, StackFrame::INTERNAL);
2116 __ push(eax);
2117 __ CallRuntime(RuntimeFunction(), 1);
2118 }
whesse@chromium.org023421e2010-12-21 12:19:12 +00002119 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
2120 __ Ret();
2121 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002122}
2123
2124
2125Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
2126 switch (type_) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00002127 case TranscendentalCache::SIN: return Runtime::kMath_sin;
2128 case TranscendentalCache::COS: return Runtime::kMath_cos;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00002129 case TranscendentalCache::TAN: return Runtime::kMath_tan;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002130 case TranscendentalCache::LOG: return Runtime::kMath_log;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002131 default:
2132 UNIMPLEMENTED();
2133 return Runtime::kAbort;
2134 }
2135}
2136
2137
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002138void TranscendentalCacheStub::GenerateOperation(
2139 MacroAssembler* masm, TranscendentalCache::Type type) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00002140 // Only free register is edi.
whesse@chromium.org023421e2010-12-21 12:19:12 +00002141 // Input value is on FP stack, and also in ebx/edx.
2142 // Input value is possibly in xmm1.
2143 // Address of result (a newly allocated HeapNumber) may be in eax.
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002144 if (type == TranscendentalCache::SIN ||
2145 type == TranscendentalCache::COS ||
2146 type == TranscendentalCache::TAN) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002147 // Both fsin and fcos require arguments in the range +/-2^63 and
2148 // return NaN for infinities and NaN. They can share all code except
2149 // the actual fsin/fcos operation.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002150 Label in_range, done;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002151 // If argument is outside the range -2^63..2^63, fsin/cos doesn't
2152 // work. We must reduce it to the appropriate range.
2153 __ mov(edi, edx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002154 __ and_(edi, Immediate(0x7ff00000)); // Exponent only.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002155 int supported_exponent_limit =
2156 (63 + HeapNumber::kExponentBias) << HeapNumber::kExponentShift;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002157 __ cmp(edi, Immediate(supported_exponent_limit));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00002158 __ j(below, &in_range, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002159 // Check for infinity and NaN. Both return NaN for sin.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002160 __ cmp(edi, Immediate(0x7ff00000));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002161 Label non_nan_result;
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00002162 __ j(not_equal, &non_nan_result, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002163 // Input is +/-Infinity or NaN. Result is NaN.
2164 __ fstp(0);
2165 // NaN is represented by 0x7ff8000000000000.
2166 __ push(Immediate(0x7ff80000));
2167 __ push(Immediate(0));
2168 __ fld_d(Operand(esp, 0));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002169 __ add(esp, Immediate(2 * kPointerSize));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002170 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002171
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002172 __ bind(&non_nan_result);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002173
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002174 // Use fpmod to restrict argument to the range +/-2*PI.
2175 __ mov(edi, eax); // Save eax before using fnstsw_ax.
2176 __ fldpi();
2177 __ fadd(0);
2178 __ fld(1);
2179 // FPU Stack: input, 2*pi, input.
2180 {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002181 Label no_exceptions;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002182 __ fwait();
2183 __ fnstsw_ax();
2184 // Clear if Illegal Operand or Zero Division exceptions are set.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002185 __ test(eax, Immediate(5));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002186 __ j(zero, &no_exceptions, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002187 __ fnclex();
2188 __ bind(&no_exceptions);
2189 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002190
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002191 // Compute st(0) % st(1)
2192 {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002193 Label partial_remainder_loop;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002194 __ bind(&partial_remainder_loop);
2195 __ fprem1();
2196 __ fwait();
2197 __ fnstsw_ax();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002198 __ test(eax, Immediate(0x400 /* C2 */));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002199 // If C2 is set, computation only has partial result. Loop to
2200 // continue computation.
2201 __ j(not_zero, &partial_remainder_loop);
2202 }
2203 // FPU Stack: input, 2*pi, input % 2*pi
2204 __ fstp(2);
2205 __ fstp(0);
2206 __ mov(eax, edi); // Restore eax (allocated HeapNumber pointer).
2207
2208 // FPU Stack: input % 2*pi
2209 __ bind(&in_range);
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002210 switch (type) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002211 case TranscendentalCache::SIN:
2212 __ fsin();
2213 break;
2214 case TranscendentalCache::COS:
2215 __ fcos();
2216 break;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00002217 case TranscendentalCache::TAN:
2218 // FPTAN calculates tangent onto st(0) and pushes 1.0 onto the
2219 // FP register stack.
2220 __ fptan();
2221 __ fstp(0); // Pop FP register stack.
2222 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002223 default:
2224 UNREACHABLE();
2225 }
2226 __ bind(&done);
2227 } else {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002228 ASSERT(type == TranscendentalCache::LOG);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002229 __ fldln2();
2230 __ fxch();
2231 __ fyl2x();
ricow@chromium.org65fae842010-08-25 15:26:24 +00002232 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002233}
2234
2235
ricow@chromium.org65fae842010-08-25 15:26:24 +00002236// Input: edx, eax are the left and right objects of a bit op.
2237// Output: eax, ecx are left and right integers for a bit op.
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002238// Warning: can clobber inputs even when it jumps to |conversion_failure|!
2239void FloatingPointHelper::LoadUnknownsAsIntegers(
2240 MacroAssembler* masm,
2241 bool use_sse3,
2242 BinaryOpIC::TypeInfo left_type,
2243 BinaryOpIC::TypeInfo right_type,
2244 Label* conversion_failure) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00002245 // Check float operands.
2246 Label arg1_is_object, check_undefined_arg1;
2247 Label arg2_is_object, check_undefined_arg2;
2248 Label load_arg2, done;
2249
2250 // Test if arg1 is a Smi.
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002251 if (left_type == BinaryOpIC::SMI) {
2252 __ JumpIfNotSmi(edx, conversion_failure);
2253 } else {
2254 __ JumpIfNotSmi(edx, &arg1_is_object, Label::kNear);
2255 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002256
2257 __ SmiUntag(edx);
2258 __ jmp(&load_arg2);
2259
2260 // If the argument is undefined it converts to zero (ECMA-262, section 9.5).
2261 __ bind(&check_undefined_arg1);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002262 Factory* factory = masm->isolate()->factory();
2263 __ cmp(edx, factory->undefined_value());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002264 __ j(not_equal, conversion_failure);
2265 __ mov(edx, Immediate(0));
2266 __ jmp(&load_arg2);
2267
2268 __ bind(&arg1_is_object);
2269 __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002270 __ cmp(ebx, factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002271 __ j(not_equal, &check_undefined_arg1);
2272
2273 // Get the untagged integer version of the edx heap number in ecx.
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002274 if (left_type == BinaryOpIC::INT32 && CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00002275 CpuFeatureScope use_sse2(masm, SSE2);
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002276 ConvertHeapNumberToInt32(masm, edx, conversion_failure);
2277 } else {
danno@chromium.org169691d2013-07-15 08:01:13 +00002278 DoubleToIStub stub(edx, ecx, HeapNumber::kValueOffset - kHeapObjectTag,
2279 true);
2280 __ call(stub.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002281 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002282 __ mov(edx, ecx);
2283
2284 // Here edx has the untagged integer, eax has a Smi or a heap number.
2285 __ bind(&load_arg2);
2286
2287 // Test if arg2 is a Smi.
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002288 if (right_type == BinaryOpIC::SMI) {
2289 __ JumpIfNotSmi(eax, conversion_failure);
2290 } else {
2291 __ JumpIfNotSmi(eax, &arg2_is_object, Label::kNear);
2292 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002293
2294 __ SmiUntag(eax);
2295 __ mov(ecx, eax);
2296 __ jmp(&done);
2297
2298 // If the argument is undefined it converts to zero (ECMA-262, section 9.5).
2299 __ bind(&check_undefined_arg2);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002300 __ cmp(eax, factory->undefined_value());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002301 __ j(not_equal, conversion_failure);
2302 __ mov(ecx, Immediate(0));
2303 __ jmp(&done);
2304
2305 __ bind(&arg2_is_object);
2306 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002307 __ cmp(ebx, factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002308 __ j(not_equal, &check_undefined_arg2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002309 // Get the untagged integer version of the eax heap number in ecx.
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002310
2311 if (right_type == BinaryOpIC::INT32 && CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00002312 CpuFeatureScope use_sse2(masm, SSE2);
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002313 ConvertHeapNumberToInt32(masm, eax, conversion_failure);
2314 } else {
danno@chromium.org169691d2013-07-15 08:01:13 +00002315 DoubleToIStub stub(eax, ecx, HeapNumber::kValueOffset - kHeapObjectTag,
2316 true);
2317 __ call(stub.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002318 }
2319
ricow@chromium.org65fae842010-08-25 15:26:24 +00002320 __ bind(&done);
2321 __ mov(eax, edx);
2322}
2323
2324
ricow@chromium.org65fae842010-08-25 15:26:24 +00002325void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm,
2326 Register number) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002327 Label load_smi, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002328
whesse@chromium.org7b260152011-06-20 15:33:18 +00002329 __ JumpIfSmi(number, &load_smi, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002330 __ fld_d(FieldOperand(number, HeapNumber::kValueOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002331 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002332
2333 __ bind(&load_smi);
2334 __ SmiUntag(number);
2335 __ push(number);
2336 __ fild_s(Operand(esp, 0));
2337 __ pop(number);
2338
2339 __ bind(&done);
2340}
2341
2342
ricow@chromium.org65fae842010-08-25 15:26:24 +00002343void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm,
2344 Label* not_numbers) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002345 Label load_smi_edx, load_eax, load_smi_eax, load_float_eax, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002346 // Load operand in edx into xmm0, or branch to not_numbers.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002347 __ JumpIfSmi(edx, &load_smi_edx, Label::kNear);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002348 Factory* factory = masm->isolate()->factory();
2349 __ cmp(FieldOperand(edx, HeapObject::kMapOffset), factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002350 __ j(not_equal, not_numbers); // Argument in edx is not a number.
2351 __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
2352 __ bind(&load_eax);
2353 // Load operand in eax into xmm1, or branch to not_numbers.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002354 __ JumpIfSmi(eax, &load_smi_eax, Label::kNear);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002355 __ cmp(FieldOperand(eax, HeapObject::kMapOffset), factory->heap_number_map());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002356 __ j(equal, &load_float_eax, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002357 __ jmp(not_numbers); // Argument in eax is not a number.
2358 __ bind(&load_smi_edx);
2359 __ SmiUntag(edx); // Untag smi before converting to float.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002360 __ cvtsi2sd(xmm0, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002361 __ SmiTag(edx); // Retag smi for heap number overwriting test.
2362 __ jmp(&load_eax);
2363 __ bind(&load_smi_eax);
2364 __ SmiUntag(eax); // Untag smi before converting to float.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002365 __ cvtsi2sd(xmm1, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002366 __ SmiTag(eax); // Retag smi for heap number overwriting test.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002367 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002368 __ bind(&load_float_eax);
2369 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
2370 __ bind(&done);
2371}
2372
2373
2374void FloatingPointHelper::LoadSSE2Smis(MacroAssembler* masm,
2375 Register scratch) {
2376 const Register left = edx;
2377 const Register right = eax;
2378 __ mov(scratch, left);
2379 ASSERT(!scratch.is(right)); // We're about to clobber scratch.
2380 __ SmiUntag(scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002381 __ cvtsi2sd(xmm0, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002382
2383 __ mov(scratch, right);
2384 __ SmiUntag(scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002385 __ cvtsi2sd(xmm1, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002386}
2387
2388
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002389void FloatingPointHelper::CheckSSE2OperandIsInt32(MacroAssembler* masm,
2390 Label* non_int32,
2391 XMMRegister operand,
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002392 Register int32_result,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002393 Register scratch,
2394 XMMRegister xmm_scratch) {
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00002395 __ cvttsd2si(int32_result, Operand(operand));
2396 __ cvtsi2sd(xmm_scratch, int32_result);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002397 __ pcmpeqd(xmm_scratch, operand);
ulan@chromium.org4121f232012-12-27 15:57:11 +00002398 __ movmskps(scratch, xmm_scratch);
2399 // Two least significant bits should be both set.
2400 __ not_(scratch);
2401 __ test(scratch, Immediate(3));
2402 __ j(not_zero, non_int32);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002403}
2404
2405
ricow@chromium.org65fae842010-08-25 15:26:24 +00002406void FloatingPointHelper::LoadFloatOperands(MacroAssembler* masm,
2407 Register scratch,
2408 ArgLocation arg_location) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002409 Label load_smi_1, load_smi_2, done_load_1, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002410 if (arg_location == ARGS_IN_REGISTERS) {
2411 __ mov(scratch, edx);
2412 } else {
2413 __ mov(scratch, Operand(esp, 2 * kPointerSize));
2414 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00002415 __ JumpIfSmi(scratch, &load_smi_1, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002416 __ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset));
2417 __ bind(&done_load_1);
2418
2419 if (arg_location == ARGS_IN_REGISTERS) {
2420 __ mov(scratch, eax);
2421 } else {
2422 __ mov(scratch, Operand(esp, 1 * kPointerSize));
2423 }
whesse@chromium.org7b260152011-06-20 15:33:18 +00002424 __ JumpIfSmi(scratch, &load_smi_2, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002425 __ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002426 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002427
2428 __ bind(&load_smi_1);
2429 __ SmiUntag(scratch);
2430 __ push(scratch);
2431 __ fild_s(Operand(esp, 0));
2432 __ pop(scratch);
2433 __ jmp(&done_load_1);
2434
2435 __ bind(&load_smi_2);
2436 __ SmiUntag(scratch);
2437 __ push(scratch);
2438 __ fild_s(Operand(esp, 0));
2439 __ pop(scratch);
2440
2441 __ bind(&done);
2442}
2443
2444
2445void FloatingPointHelper::LoadFloatSmis(MacroAssembler* masm,
2446 Register scratch) {
2447 const Register left = edx;
2448 const Register right = eax;
2449 __ mov(scratch, left);
2450 ASSERT(!scratch.is(right)); // We're about to clobber scratch.
2451 __ SmiUntag(scratch);
2452 __ push(scratch);
2453 __ fild_s(Operand(esp, 0));
2454
2455 __ mov(scratch, right);
2456 __ SmiUntag(scratch);
2457 __ mov(Operand(esp, 0), scratch);
2458 __ fild_s(Operand(esp, 0));
2459 __ pop(scratch);
2460}
2461
2462
2463void FloatingPointHelper::CheckFloatOperands(MacroAssembler* masm,
2464 Label* non_float,
2465 Register scratch) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002466 Label test_other, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002467 // Test if both operands are floats or smi -> scratch=k_is_float;
2468 // Otherwise scratch = k_not_float.
whesse@chromium.org7b260152011-06-20 15:33:18 +00002469 __ JumpIfSmi(edx, &test_other, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002470 __ mov(scratch, FieldOperand(edx, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002471 Factory* factory = masm->isolate()->factory();
2472 __ cmp(scratch, factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002473 __ j(not_equal, non_float); // argument in edx is not a number -> NaN
2474
2475 __ bind(&test_other);
whesse@chromium.org7b260152011-06-20 15:33:18 +00002476 __ JumpIfSmi(eax, &done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002477 __ mov(scratch, FieldOperand(eax, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002478 __ cmp(scratch, factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002479 __ j(not_equal, non_float); // argument in eax is not a number -> NaN
2480
2481 // Fall-through: Both operands are numbers.
2482 __ bind(&done);
2483}
2484
2485
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002486void MathPowStub::Generate(MacroAssembler* masm) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00002487 CpuFeatureScope use_sse2(masm, SSE2);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002488 Factory* factory = masm->isolate()->factory();
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002489 const Register exponent = eax;
2490 const Register base = edx;
2491 const Register scratch = ecx;
2492 const XMMRegister double_result = xmm3;
2493 const XMMRegister double_base = xmm2;
2494 const XMMRegister double_exponent = xmm1;
2495 const XMMRegister double_scratch = xmm4;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002496
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002497 Label call_runtime, done, exponent_not_smi, int_exponent;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002498
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002499 // Save 1 in double_result - we need this several times later on.
2500 __ mov(scratch, Immediate(1));
2501 __ cvtsi2sd(double_result, scratch);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002502
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002503 if (exponent_type_ == ON_STACK) {
2504 Label base_is_smi, unpack_exponent;
2505 // The exponent and base are supplied as arguments on the stack.
2506 // This can only happen if the stub is called from non-optimized code.
2507 // Load input parameters from stack.
2508 __ mov(base, Operand(esp, 2 * kPointerSize));
2509 __ mov(exponent, Operand(esp, 1 * kPointerSize));
2510
2511 __ JumpIfSmi(base, &base_is_smi, Label::kNear);
2512 __ cmp(FieldOperand(base, HeapObject::kMapOffset),
2513 factory->heap_number_map());
2514 __ j(not_equal, &call_runtime);
2515
2516 __ movdbl(double_base, FieldOperand(base, HeapNumber::kValueOffset));
2517 __ jmp(&unpack_exponent, Label::kNear);
2518
2519 __ bind(&base_is_smi);
2520 __ SmiUntag(base);
2521 __ cvtsi2sd(double_base, base);
2522
2523 __ bind(&unpack_exponent);
2524 __ JumpIfNotSmi(exponent, &exponent_not_smi, Label::kNear);
2525 __ SmiUntag(exponent);
2526 __ jmp(&int_exponent);
2527
2528 __ bind(&exponent_not_smi);
2529 __ cmp(FieldOperand(exponent, HeapObject::kMapOffset),
2530 factory->heap_number_map());
2531 __ j(not_equal, &call_runtime);
2532 __ movdbl(double_exponent,
2533 FieldOperand(exponent, HeapNumber::kValueOffset));
2534 } else if (exponent_type_ == TAGGED) {
2535 __ JumpIfNotSmi(exponent, &exponent_not_smi, Label::kNear);
2536 __ SmiUntag(exponent);
2537 __ jmp(&int_exponent);
2538
2539 __ bind(&exponent_not_smi);
2540 __ movdbl(double_exponent,
2541 FieldOperand(exponent, HeapNumber::kValueOffset));
2542 }
2543
2544 if (exponent_type_ != INTEGER) {
2545 Label fast_power;
2546 // Detect integer exponents stored as double.
2547 __ cvttsd2si(exponent, Operand(double_exponent));
2548 // Skip to runtime if possibly NaN (indicated by the indefinite integer).
2549 __ cmp(exponent, Immediate(0x80000000u));
2550 __ j(equal, &call_runtime);
2551 __ cvtsi2sd(double_scratch, exponent);
2552 // Already ruled out NaNs for exponent.
2553 __ ucomisd(double_exponent, double_scratch);
2554 __ j(equal, &int_exponent);
2555
2556 if (exponent_type_ == ON_STACK) {
2557 // Detect square root case. Crankshaft detects constant +/-0.5 at
2558 // compile time and uses DoMathPowHalf instead. We then skip this check
2559 // for non-constant cases of +/-0.5 as these hardly occur.
2560 Label continue_sqrt, continue_rsqrt, not_plus_half;
2561 // Test for 0.5.
2562 // Load double_scratch with 0.5.
2563 __ mov(scratch, Immediate(0x3F000000u));
2564 __ movd(double_scratch, scratch);
2565 __ cvtss2sd(double_scratch, double_scratch);
2566 // Already ruled out NaNs for exponent.
2567 __ ucomisd(double_scratch, double_exponent);
2568 __ j(not_equal, &not_plus_half, Label::kNear);
2569
2570 // Calculates square root of base. Check for the special case of
2571 // Math.pow(-Infinity, 0.5) == Infinity (ECMA spec, 15.8.2.13).
2572 // According to IEEE-754, single-precision -Infinity has the highest
2573 // 9 bits set and the lowest 23 bits cleared.
2574 __ mov(scratch, 0xFF800000u);
2575 __ movd(double_scratch, scratch);
2576 __ cvtss2sd(double_scratch, double_scratch);
2577 __ ucomisd(double_base, double_scratch);
2578 // Comparing -Infinity with NaN results in "unordered", which sets the
2579 // zero flag as if both were equal. However, it also sets the carry flag.
2580 __ j(not_equal, &continue_sqrt, Label::kNear);
2581 __ j(carry, &continue_sqrt, Label::kNear);
2582
2583 // Set result to Infinity in the special case.
2584 __ xorps(double_result, double_result);
2585 __ subsd(double_result, double_scratch);
2586 __ jmp(&done);
2587
2588 __ bind(&continue_sqrt);
2589 // sqrtsd returns -0 when input is -0. ECMA spec requires +0.
2590 __ xorps(double_scratch, double_scratch);
2591 __ addsd(double_scratch, double_base); // Convert -0 to +0.
2592 __ sqrtsd(double_result, double_scratch);
2593 __ jmp(&done);
2594
2595 // Test for -0.5.
2596 __ bind(&not_plus_half);
2597 // Load double_exponent with -0.5 by substracting 1.
2598 __ subsd(double_scratch, double_result);
2599 // Already ruled out NaNs for exponent.
2600 __ ucomisd(double_scratch, double_exponent);
2601 __ j(not_equal, &fast_power, Label::kNear);
2602
2603 // Calculates reciprocal of square root of base. Check for the special
2604 // case of Math.pow(-Infinity, -0.5) == 0 (ECMA spec, 15.8.2.13).
2605 // According to IEEE-754, single-precision -Infinity has the highest
2606 // 9 bits set and the lowest 23 bits cleared.
2607 __ mov(scratch, 0xFF800000u);
2608 __ movd(double_scratch, scratch);
2609 __ cvtss2sd(double_scratch, double_scratch);
2610 __ ucomisd(double_base, double_scratch);
2611 // Comparing -Infinity with NaN results in "unordered", which sets the
2612 // zero flag as if both were equal. However, it also sets the carry flag.
2613 __ j(not_equal, &continue_rsqrt, Label::kNear);
2614 __ j(carry, &continue_rsqrt, Label::kNear);
2615
2616 // Set result to 0 in the special case.
2617 __ xorps(double_result, double_result);
2618 __ jmp(&done);
2619
2620 __ bind(&continue_rsqrt);
2621 // sqrtsd returns -0 when input is -0. ECMA spec requires +0.
2622 __ xorps(double_exponent, double_exponent);
2623 __ addsd(double_exponent, double_base); // Convert -0 to +0.
2624 __ sqrtsd(double_exponent, double_exponent);
2625 __ divsd(double_result, double_exponent);
2626 __ jmp(&done);
2627 }
2628
2629 // Using FPU instructions to calculate power.
2630 Label fast_power_failed;
2631 __ bind(&fast_power);
2632 __ fnclex(); // Clear flags to catch exceptions later.
2633 // Transfer (B)ase and (E)xponent onto the FPU register stack.
2634 __ sub(esp, Immediate(kDoubleSize));
2635 __ movdbl(Operand(esp, 0), double_exponent);
2636 __ fld_d(Operand(esp, 0)); // E
2637 __ movdbl(Operand(esp, 0), double_base);
2638 __ fld_d(Operand(esp, 0)); // B, E
2639
2640 // Exponent is in st(1) and base is in st(0)
2641 // B ^ E = (2^(E * log2(B)) - 1) + 1 = (2^X - 1) + 1 for X = E * log2(B)
2642 // FYL2X calculates st(1) * log2(st(0))
2643 __ fyl2x(); // X
2644 __ fld(0); // X, X
2645 __ frndint(); // rnd(X), X
2646 __ fsub(1); // rnd(X), X-rnd(X)
2647 __ fxch(1); // X - rnd(X), rnd(X)
2648 // F2XM1 calculates 2^st(0) - 1 for -1 < st(0) < 1
2649 __ f2xm1(); // 2^(X-rnd(X)) - 1, rnd(X)
2650 __ fld1(); // 1, 2^(X-rnd(X)) - 1, rnd(X)
danno@chromium.org1f34ad32012-11-26 14:53:56 +00002651 __ faddp(1); // 2^(X-rnd(X)), rnd(X)
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002652 // FSCALE calculates st(0) * 2^st(1)
2653 __ fscale(); // 2^X, rnd(X)
danno@chromium.org1f34ad32012-11-26 14:53:56 +00002654 __ fstp(1); // 2^X
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002655 // Bail out to runtime in case of exceptions in the status word.
2656 __ fnstsw_ax();
2657 __ test_b(eax, 0x5F); // We check for all but precision exception.
2658 __ j(not_zero, &fast_power_failed, Label::kNear);
2659 __ fstp_d(Operand(esp, 0));
2660 __ movdbl(double_result, Operand(esp, 0));
2661 __ add(esp, Immediate(kDoubleSize));
2662 __ jmp(&done);
2663
2664 __ bind(&fast_power_failed);
2665 __ fninit();
2666 __ add(esp, Immediate(kDoubleSize));
2667 __ jmp(&call_runtime);
2668 }
2669
2670 // Calculate power with integer exponent.
2671 __ bind(&int_exponent);
2672 const XMMRegister double_scratch2 = double_exponent;
2673 __ mov(scratch, exponent); // Back up exponent.
2674 __ movsd(double_scratch, double_base); // Back up base.
2675 __ movsd(double_scratch2, double_result); // Load double_exponent with 1.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002676
2677 // Get absolute value of exponent.
verwaest@chromium.org33e09c82012-10-10 17:07:22 +00002678 Label no_neg, while_true, while_false;
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002679 __ test(scratch, scratch);
2680 __ j(positive, &no_neg, Label::kNear);
2681 __ neg(scratch);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002682 __ bind(&no_neg);
2683
ulan@chromium.org56c14af2012-09-20 12:51:09 +00002684 __ j(zero, &while_false, Label::kNear);
2685 __ shr(scratch, 1);
2686 // Above condition means CF==0 && ZF==0. This means that the
2687 // bit that has been shifted out is 0 and the result is not 0.
2688 __ j(above, &while_true, Label::kNear);
2689 __ movsd(double_result, double_scratch);
2690 __ j(zero, &while_false, Label::kNear);
2691
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002692 __ bind(&while_true);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002693 __ shr(scratch, 1);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002694 __ mulsd(double_scratch, double_scratch);
ulan@chromium.org56c14af2012-09-20 12:51:09 +00002695 __ j(above, &while_true, Label::kNear);
2696 __ mulsd(double_result, double_scratch);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002697 __ j(not_zero, &while_true);
2698
ulan@chromium.org56c14af2012-09-20 12:51:09 +00002699 __ bind(&while_false);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002700 // scratch has the original value of the exponent - if the exponent is
2701 // negative, return 1/result.
2702 __ test(exponent, exponent);
2703 __ j(positive, &done);
2704 __ divsd(double_scratch2, double_result);
2705 __ movsd(double_result, double_scratch2);
2706 // Test whether result is zero. Bail out to check for subnormal result.
2707 // Due to subnormals, x^-y == (1/x)^y does not hold in all cases.
2708 __ xorps(double_scratch2, double_scratch2);
2709 __ ucomisd(double_scratch2, double_result); // Result cannot be NaN.
2710 // double_exponent aliased as double_scratch2 has already been overwritten
2711 // and may not have contained the exponent value in the first place when the
2712 // exponent is a smi. We reset it with exponent value before bailing out.
2713 __ j(not_equal, &done);
2714 __ cvtsi2sd(double_exponent, exponent);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002715
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002716 // Returning or bailing out.
2717 Counters* counters = masm->isolate()->counters();
2718 if (exponent_type_ == ON_STACK) {
2719 // The arguments are still on the stack.
2720 __ bind(&call_runtime);
2721 __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002722
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002723 // The stub is called from non-optimized code, which expects the result
2724 // as heap number in exponent.
2725 __ bind(&done);
2726 __ AllocateHeapNumber(eax, scratch, base, &call_runtime);
2727 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), double_result);
2728 __ IncrementCounter(counters->math_pow(), 1);
2729 __ ret(2 * kPointerSize);
2730 } else {
2731 __ bind(&call_runtime);
2732 {
2733 AllowExternalCallThatCantCauseGC scope(masm);
2734 __ PrepareCallCFunction(4, scratch);
2735 __ movdbl(Operand(esp, 0 * kDoubleSize), double_base);
2736 __ movdbl(Operand(esp, 1 * kDoubleSize), double_exponent);
2737 __ CallCFunction(
2738 ExternalReference::power_double_double_function(masm->isolate()), 4);
2739 }
2740 // Return value is in st(0) on ia32.
2741 // Store it into the (fixed) result register.
2742 __ sub(esp, Immediate(kDoubleSize));
2743 __ fstp_d(Operand(esp, 0));
2744 __ movdbl(double_result, Operand(esp, 0));
2745 __ add(esp, Immediate(kDoubleSize));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002746
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002747 __ bind(&done);
2748 __ IncrementCounter(counters->math_pow(), 1);
2749 __ ret(0);
2750 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002751}
2752
2753
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00002754void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
2755 // ----------- S t a t e -------------
2756 // -- ecx : name
2757 // -- edx : receiver
2758 // -- esp[0] : return address
2759 // -----------------------------------
2760 Label miss;
2761
2762 if (kind() == Code::KEYED_LOAD_IC) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00002763 __ cmp(ecx, Immediate(masm->isolate()->factory()->prototype_string()));
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00002764 __ j(not_equal, &miss);
2765 }
2766
2767 StubCompiler::GenerateLoadFunctionPrototype(masm, edx, eax, ebx, &miss);
2768 __ bind(&miss);
danno@chromium.orgbee51992013-07-10 14:57:15 +00002769 StubCompiler::TailCallBuiltin(
2770 masm, BaseLoadStoreStubCompiler::MissBuiltin(kind()));
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00002771}
2772
2773
2774void StringLengthStub::Generate(MacroAssembler* masm) {
2775 // ----------- S t a t e -------------
2776 // -- ecx : name
2777 // -- edx : receiver
2778 // -- esp[0] : return address
2779 // -----------------------------------
2780 Label miss;
2781
2782 if (kind() == Code::KEYED_LOAD_IC) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00002783 __ cmp(ecx, Immediate(masm->isolate()->factory()->length_string()));
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00002784 __ j(not_equal, &miss);
2785 }
2786
2787 StubCompiler::GenerateLoadStringLength(masm, edx, eax, ebx, &miss,
2788 support_wrapper_);
2789 __ bind(&miss);
danno@chromium.orgbee51992013-07-10 14:57:15 +00002790 StubCompiler::TailCallBuiltin(
2791 masm, BaseLoadStoreStubCompiler::MissBuiltin(kind()));
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00002792}
2793
2794
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00002795void StoreArrayLengthStub::Generate(MacroAssembler* masm) {
2796 // ----------- S t a t e -------------
2797 // -- eax : value
2798 // -- ecx : name
2799 // -- edx : receiver
2800 // -- esp[0] : return address
2801 // -----------------------------------
2802 //
2803 // This accepts as a receiver anything JSArray::SetElementsLength accepts
2804 // (currently anything except for external arrays which means anything with
2805 // elements of FixedArray type). Value must be a number, but only smis are
2806 // accepted as the most common case.
2807
2808 Label miss;
2809
2810 Register receiver = edx;
2811 Register value = eax;
2812 Register scratch = ebx;
2813
ulan@chromium.org750145a2013-03-07 15:14:13 +00002814 if (kind() == Code::KEYED_STORE_IC) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00002815 __ cmp(ecx, Immediate(masm->isolate()->factory()->length_string()));
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00002816 __ j(not_equal, &miss);
2817 }
2818
2819 // Check that the receiver isn't a smi.
2820 __ JumpIfSmi(receiver, &miss);
2821
2822 // Check that the object is a JS array.
2823 __ CmpObjectType(receiver, JS_ARRAY_TYPE, scratch);
2824 __ j(not_equal, &miss);
2825
2826 // Check that elements are FixedArray.
2827 // We rely on StoreIC_ArrayLength below to deal with all types of
2828 // fast elements (including COW).
2829 __ mov(scratch, FieldOperand(receiver, JSArray::kElementsOffset));
2830 __ CmpObjectType(scratch, FIXED_ARRAY_TYPE, scratch);
2831 __ j(not_equal, &miss);
2832
2833 // Check that the array has fast properties, otherwise the length
2834 // property might have been redefined.
2835 __ mov(scratch, FieldOperand(receiver, JSArray::kPropertiesOffset));
2836 __ CompareRoot(FieldOperand(scratch, FixedArray::kMapOffset),
2837 Heap::kHashTableMapRootIndex);
2838 __ j(equal, &miss);
2839
2840 // Check that value is a smi.
2841 __ JumpIfNotSmi(value, &miss);
2842
2843 // Prepare tail call to StoreIC_ArrayLength.
2844 __ pop(scratch);
2845 __ push(receiver);
2846 __ push(value);
2847 __ push(scratch); // return address
2848
2849 ExternalReference ref =
2850 ExternalReference(IC_Utility(IC::kStoreIC_ArrayLength), masm->isolate());
2851 __ TailCallExternalReference(ref, 2, 1);
2852
2853 __ bind(&miss);
2854
danno@chromium.orgbee51992013-07-10 14:57:15 +00002855 StubCompiler::TailCallBuiltin(
2856 masm, BaseLoadStoreStubCompiler::MissBuiltin(kind()));
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00002857}
2858
2859
ricow@chromium.org65fae842010-08-25 15:26:24 +00002860void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
2861 // The key is in edx and the parameter count is in eax.
2862
2863 // The displacement is used for skipping the frame pointer on the
2864 // stack. It is the offset of the last parameter (if any) relative
2865 // to the frame pointer.
2866 static const int kDisplacement = 1 * kPointerSize;
2867
2868 // Check that the key is a smi.
2869 Label slow;
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00002870 __ JumpIfNotSmi(edx, &slow, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002871
2872 // Check if the calling frame is an arguments adaptor frame.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002873 Label adaptor;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002874 __ mov(ebx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
2875 __ mov(ecx, Operand(ebx, StandardFrameConstants::kContextOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002876 __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002877 __ j(equal, &adaptor, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002878
2879 // Check index against formal parameters count limit passed in
2880 // through register eax. Use unsigned comparison to get negative
2881 // check for free.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002882 __ cmp(edx, eax);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00002883 __ j(above_equal, &slow, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002884
2885 // Read the argument from the stack and return it.
2886 STATIC_ASSERT(kSmiTagSize == 1);
2887 STATIC_ASSERT(kSmiTag == 0); // Shifting code depends on these.
2888 __ lea(ebx, Operand(ebp, eax, times_2, 0));
2889 __ neg(edx);
2890 __ mov(eax, Operand(ebx, edx, times_2, kDisplacement));
2891 __ ret(0);
2892
2893 // Arguments adaptor case: Check index against actual arguments
2894 // limit found in the arguments adaptor frame. Use unsigned
2895 // comparison to get negative check for free.
2896 __ bind(&adaptor);
2897 __ mov(ecx, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002898 __ cmp(edx, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00002899 __ j(above_equal, &slow, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002900
2901 // Read the argument from the stack and return it.
2902 STATIC_ASSERT(kSmiTagSize == 1);
2903 STATIC_ASSERT(kSmiTag == 0); // Shifting code depends on these.
2904 __ lea(ebx, Operand(ebx, ecx, times_2, 0));
2905 __ neg(edx);
2906 __ mov(eax, Operand(ebx, edx, times_2, kDisplacement));
2907 __ ret(0);
2908
2909 // Slow-case: Handle non-smi or out-of-bounds access to arguments
2910 // by calling the runtime system.
2911 __ bind(&slow);
2912 __ pop(ebx); // Return address.
2913 __ push(edx);
2914 __ push(ebx);
2915 __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
2916}
2917
2918
whesse@chromium.org7b260152011-06-20 15:33:18 +00002919void ArgumentsAccessStub::GenerateNewNonStrictSlow(MacroAssembler* masm) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00002920 // esp[0] : return address
2921 // esp[4] : number of parameters
2922 // esp[8] : receiver displacement
whesse@chromium.org7b260152011-06-20 15:33:18 +00002923 // esp[12] : function
ricow@chromium.org65fae842010-08-25 15:26:24 +00002924
whesse@chromium.org7b260152011-06-20 15:33:18 +00002925 // Check if the calling frame is an arguments adaptor frame.
2926 Label runtime;
2927 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
2928 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002929 __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00002930 __ j(not_equal, &runtime, Label::kNear);
2931
2932 // Patch the arguments.length and the parameters pointer.
2933 __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
2934 __ mov(Operand(esp, 1 * kPointerSize), ecx);
2935 __ lea(edx, Operand(edx, ecx, times_2,
2936 StandardFrameConstants::kCallerSPOffset));
2937 __ mov(Operand(esp, 2 * kPointerSize), edx);
2938
2939 __ bind(&runtime);
2940 __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
2941}
2942
2943
2944void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) {
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00002945 Isolate* isolate = masm->isolate();
2946
whesse@chromium.org7b260152011-06-20 15:33:18 +00002947 // esp[0] : return address
2948 // esp[4] : number of parameters (tagged)
2949 // esp[8] : receiver displacement
2950 // esp[12] : function
2951
2952 // ebx = parameter count (tagged)
2953 __ mov(ebx, Operand(esp, 1 * kPointerSize));
2954
2955 // Check if the calling frame is an arguments adaptor frame.
2956 // TODO(rossberg): Factor out some of the bits that are shared with the other
2957 // Generate* functions.
2958 Label runtime;
2959 Label adaptor_frame, try_allocate;
2960 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
2961 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002962 __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00002963 __ j(equal, &adaptor_frame, Label::kNear);
2964
2965 // No adaptor, parameter count = argument count.
2966 __ mov(ecx, ebx);
2967 __ jmp(&try_allocate, Label::kNear);
2968
2969 // We have an adaptor frame. Patch the parameters pointer.
2970 __ bind(&adaptor_frame);
2971 __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
2972 __ lea(edx, Operand(edx, ecx, times_2,
2973 StandardFrameConstants::kCallerSPOffset));
2974 __ mov(Operand(esp, 2 * kPointerSize), edx);
2975
2976 // ebx = parameter count (tagged)
2977 // ecx = argument count (tagged)
2978 // esp[4] = parameter count (tagged)
2979 // esp[8] = address of receiver argument
2980 // Compute the mapped parameter count = min(ebx, ecx) in ebx.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002981 __ cmp(ebx, ecx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00002982 __ j(less_equal, &try_allocate, Label::kNear);
2983 __ mov(ebx, ecx);
2984
2985 __ bind(&try_allocate);
2986
2987 // Save mapped parameter count.
2988 __ push(ebx);
2989
2990 // Compute the sizes of backing store, parameter map, and arguments object.
2991 // 1. Parameter map, has 2 extra words containing context and backing store.
2992 const int kParameterMapHeaderSize =
2993 FixedArray::kHeaderSize + 2 * kPointerSize;
2994 Label no_parameter_map;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002995 __ test(ebx, ebx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00002996 __ j(zero, &no_parameter_map, Label::kNear);
2997 __ lea(ebx, Operand(ebx, times_2, kParameterMapHeaderSize));
2998 __ bind(&no_parameter_map);
2999
3000 // 2. Backing store.
3001 __ lea(ebx, Operand(ebx, ecx, times_2, FixedArray::kHeaderSize));
3002
3003 // 3. Arguments object.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003004 __ add(ebx, Immediate(Heap::kArgumentsObjectSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003005
3006 // Do the allocation of all three objects in one go.
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00003007 __ Allocate(ebx, eax, edx, edi, &runtime, TAG_OBJECT);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003008
3009 // eax = address of new object(s) (tagged)
3010 // ecx = argument count (tagged)
3011 // esp[0] = mapped parameter count (tagged)
3012 // esp[8] = parameter count (tagged)
3013 // esp[12] = address of receiver argument
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00003014 // Get the arguments boilerplate from the current native context into edi.
whesse@chromium.org7b260152011-06-20 15:33:18 +00003015 Label has_mapped_parameters, copy;
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00003016 __ mov(edi, Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
3017 __ mov(edi, FieldOperand(edi, GlobalObject::kNativeContextOffset));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003018 __ mov(ebx, Operand(esp, 0 * kPointerSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003019 __ test(ebx, ebx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003020 __ j(not_zero, &has_mapped_parameters, Label::kNear);
3021 __ mov(edi, Operand(edi,
3022 Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX)));
3023 __ jmp(&copy, Label::kNear);
3024
3025 __ bind(&has_mapped_parameters);
3026 __ mov(edi, Operand(edi,
3027 Context::SlotOffset(Context::ALIASED_ARGUMENTS_BOILERPLATE_INDEX)));
3028 __ bind(&copy);
3029
3030 // eax = address of new object (tagged)
3031 // ebx = mapped parameter count (tagged)
3032 // ecx = argument count (tagged)
3033 // edi = address of boilerplate object (tagged)
3034 // esp[0] = mapped parameter count (tagged)
3035 // esp[8] = parameter count (tagged)
3036 // esp[12] = address of receiver argument
3037 // Copy the JS object part.
3038 for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
3039 __ mov(edx, FieldOperand(edi, i));
3040 __ mov(FieldOperand(eax, i), edx);
3041 }
3042
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00003043 // Set up the callee in-object property.
whesse@chromium.org7b260152011-06-20 15:33:18 +00003044 STATIC_ASSERT(Heap::kArgumentsCalleeIndex == 1);
3045 __ mov(edx, Operand(esp, 4 * kPointerSize));
3046 __ mov(FieldOperand(eax, JSObject::kHeaderSize +
3047 Heap::kArgumentsCalleeIndex * kPointerSize),
3048 edx);
3049
3050 // Use the length (smi tagged) and set that as an in-object property too.
3051 STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0);
3052 __ mov(FieldOperand(eax, JSObject::kHeaderSize +
3053 Heap::kArgumentsLengthIndex * kPointerSize),
3054 ecx);
3055
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00003056 // Set up the elements pointer in the allocated arguments object.
whesse@chromium.org7b260152011-06-20 15:33:18 +00003057 // If we allocated a parameter map, edi will point there, otherwise to the
3058 // backing store.
3059 __ lea(edi, Operand(eax, Heap::kArgumentsObjectSize));
3060 __ mov(FieldOperand(eax, JSObject::kElementsOffset), edi);
3061
3062 // eax = address of new object (tagged)
3063 // ebx = mapped parameter count (tagged)
3064 // ecx = argument count (tagged)
3065 // edi = address of parameter map or backing store (tagged)
3066 // esp[0] = mapped parameter count (tagged)
3067 // esp[8] = parameter count (tagged)
3068 // esp[12] = address of receiver argument
3069 // Free a register.
3070 __ push(eax);
3071
3072 // Initialize parameter map. If there are no mapped arguments, we're done.
3073 Label skip_parameter_map;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003074 __ test(ebx, ebx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003075 __ j(zero, &skip_parameter_map);
3076
3077 __ mov(FieldOperand(edi, FixedArray::kMapOffset),
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00003078 Immediate(isolate->factory()->non_strict_arguments_elements_map()));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003079 __ lea(eax, Operand(ebx, reinterpret_cast<intptr_t>(Smi::FromInt(2))));
3080 __ mov(FieldOperand(edi, FixedArray::kLengthOffset), eax);
3081 __ mov(FieldOperand(edi, FixedArray::kHeaderSize + 0 * kPointerSize), esi);
3082 __ lea(eax, Operand(edi, ebx, times_2, kParameterMapHeaderSize));
3083 __ mov(FieldOperand(edi, FixedArray::kHeaderSize + 1 * kPointerSize), eax);
3084
3085 // Copy the parameter slots and the holes in the arguments.
3086 // We need to fill in mapped_parameter_count slots. They index the context,
3087 // where parameters are stored in reverse order, at
3088 // MIN_CONTEXT_SLOTS .. MIN_CONTEXT_SLOTS+parameter_count-1
3089 // The mapped parameter thus need to get indices
3090 // MIN_CONTEXT_SLOTS+parameter_count-1 ..
3091 // MIN_CONTEXT_SLOTS+parameter_count-mapped_parameter_count
3092 // We loop from right to left.
3093 Label parameters_loop, parameters_test;
3094 __ push(ecx);
3095 __ mov(eax, Operand(esp, 2 * kPointerSize));
3096 __ mov(ebx, Immediate(Smi::FromInt(Context::MIN_CONTEXT_SLOTS)));
3097 __ add(ebx, Operand(esp, 4 * kPointerSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003098 __ sub(ebx, eax);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00003099 __ mov(ecx, isolate->factory()->the_hole_value());
whesse@chromium.org7b260152011-06-20 15:33:18 +00003100 __ mov(edx, edi);
3101 __ lea(edi, Operand(edi, eax, times_2, kParameterMapHeaderSize));
3102 // eax = loop variable (tagged)
3103 // ebx = mapping index (tagged)
3104 // ecx = the hole value
3105 // edx = address of parameter map (tagged)
3106 // edi = address of backing store (tagged)
3107 // esp[0] = argument count (tagged)
3108 // esp[4] = address of new object (tagged)
3109 // esp[8] = mapped parameter count (tagged)
3110 // esp[16] = parameter count (tagged)
3111 // esp[20] = address of receiver argument
3112 __ jmp(&parameters_test, Label::kNear);
3113
3114 __ bind(&parameters_loop);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003115 __ sub(eax, Immediate(Smi::FromInt(1)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003116 __ mov(FieldOperand(edx, eax, times_2, kParameterMapHeaderSize), ebx);
3117 __ mov(FieldOperand(edi, eax, times_2, FixedArray::kHeaderSize), ecx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003118 __ add(ebx, Immediate(Smi::FromInt(1)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003119 __ bind(&parameters_test);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003120 __ test(eax, eax);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003121 __ j(not_zero, &parameters_loop, Label::kNear);
3122 __ pop(ecx);
3123
3124 __ bind(&skip_parameter_map);
3125
3126 // ecx = argument count (tagged)
3127 // edi = address of backing store (tagged)
3128 // esp[0] = address of new object (tagged)
3129 // esp[4] = mapped parameter count (tagged)
3130 // esp[12] = parameter count (tagged)
3131 // esp[16] = address of receiver argument
3132 // Copy arguments header and remaining slots (if there are any).
3133 __ mov(FieldOperand(edi, FixedArray::kMapOffset),
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00003134 Immediate(isolate->factory()->fixed_array_map()));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003135 __ mov(FieldOperand(edi, FixedArray::kLengthOffset), ecx);
3136
3137 Label arguments_loop, arguments_test;
3138 __ mov(ebx, Operand(esp, 1 * kPointerSize));
3139 __ mov(edx, Operand(esp, 4 * kPointerSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003140 __ sub(edx, ebx); // Is there a smarter way to do negative scaling?
3141 __ sub(edx, ebx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003142 __ jmp(&arguments_test, Label::kNear);
3143
3144 __ bind(&arguments_loop);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003145 __ sub(edx, Immediate(kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003146 __ mov(eax, Operand(edx, 0));
3147 __ mov(FieldOperand(edi, ebx, times_2, FixedArray::kHeaderSize), eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003148 __ add(ebx, Immediate(Smi::FromInt(1)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003149
3150 __ bind(&arguments_test);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003151 __ cmp(ebx, ecx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003152 __ j(less, &arguments_loop, Label::kNear);
3153
3154 // Restore.
3155 __ pop(eax); // Address of arguments object.
3156 __ pop(ebx); // Parameter count.
3157
3158 // Return and remove the on-stack parameters.
3159 __ ret(3 * kPointerSize);
3160
3161 // Do the runtime call to allocate the arguments object.
3162 __ bind(&runtime);
3163 __ pop(eax); // Remove saved parameter count.
3164 __ mov(Operand(esp, 1 * kPointerSize), ecx); // Patch argument count.
danno@chromium.org72204d52012-10-31 10:02:10 +00003165 __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003166}
3167
3168
3169void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) {
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00003170 Isolate* isolate = masm->isolate();
3171
whesse@chromium.org7b260152011-06-20 15:33:18 +00003172 // esp[0] : return address
3173 // esp[4] : number of parameters
3174 // esp[8] : receiver displacement
3175 // esp[12] : function
ricow@chromium.org65fae842010-08-25 15:26:24 +00003176
3177 // Check if the calling frame is an arguments adaptor frame.
3178 Label adaptor_frame, try_allocate, runtime;
3179 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
3180 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003181 __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003182 __ j(equal, &adaptor_frame, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003183
3184 // Get the length from the frame.
3185 __ mov(ecx, Operand(esp, 1 * kPointerSize));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003186 __ jmp(&try_allocate, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003187
3188 // Patch the arguments.length and the parameters pointer.
3189 __ bind(&adaptor_frame);
3190 __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
3191 __ mov(Operand(esp, 1 * kPointerSize), ecx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003192 __ lea(edx, Operand(edx, ecx, times_2,
3193 StandardFrameConstants::kCallerSPOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003194 __ mov(Operand(esp, 2 * kPointerSize), edx);
3195
3196 // Try the new space allocation. Start out with computing the size of
3197 // the arguments object and the elements array.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003198 Label add_arguments_object;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003199 __ bind(&try_allocate);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003200 __ test(ecx, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003201 __ j(zero, &add_arguments_object, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003202 __ lea(ecx, Operand(ecx, times_2, FixedArray::kHeaderSize));
3203 __ bind(&add_arguments_object);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003204 __ add(ecx, Immediate(Heap::kArgumentsObjectSizeStrict));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003205
3206 // Do the allocation of both objects in one go.
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00003207 __ Allocate(ecx, eax, edx, ebx, &runtime, TAG_OBJECT);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003208
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00003209 // Get the arguments boilerplate from the current native context.
3210 __ mov(edi, Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
3211 __ mov(edi, FieldOperand(edi, GlobalObject::kNativeContextOffset));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003212 const int offset =
3213 Context::SlotOffset(Context::STRICT_MODE_ARGUMENTS_BOILERPLATE_INDEX);
3214 __ mov(edi, Operand(edi, offset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003215
3216 // Copy the JS object part.
3217 for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
3218 __ mov(ebx, FieldOperand(edi, i));
3219 __ mov(FieldOperand(eax, i), ebx);
3220 }
3221
ricow@chromium.org65fae842010-08-25 15:26:24 +00003222 // Get the length (smi tagged) and set that as an in-object property too.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003223 STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003224 __ mov(ecx, Operand(esp, 1 * kPointerSize));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003225 __ mov(FieldOperand(eax, JSObject::kHeaderSize +
whesse@chromium.org7b260152011-06-20 15:33:18 +00003226 Heap::kArgumentsLengthIndex * kPointerSize),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003227 ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003228
3229 // If there are no actual arguments, we're done.
3230 Label done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003231 __ test(ecx, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003232 __ j(zero, &done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003233
3234 // Get the parameters pointer from the stack.
3235 __ mov(edx, Operand(esp, 2 * kPointerSize));
3236
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00003237 // Set up the elements pointer in the allocated arguments object and
ricow@chromium.org65fae842010-08-25 15:26:24 +00003238 // initialize the header in the elements fixed array.
whesse@chromium.org7b260152011-06-20 15:33:18 +00003239 __ lea(edi, Operand(eax, Heap::kArgumentsObjectSizeStrict));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003240 __ mov(FieldOperand(eax, JSObject::kElementsOffset), edi);
3241 __ mov(FieldOperand(edi, FixedArray::kMapOffset),
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00003242 Immediate(isolate->factory()->fixed_array_map()));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003243
ricow@chromium.org65fae842010-08-25 15:26:24 +00003244 __ mov(FieldOperand(edi, FixedArray::kLengthOffset), ecx);
3245 // Untag the length for the loop below.
3246 __ SmiUntag(ecx);
3247
3248 // Copy the fixed array slots.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003249 Label loop;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003250 __ bind(&loop);
3251 __ mov(ebx, Operand(edx, -1 * kPointerSize)); // Skip receiver.
3252 __ mov(FieldOperand(edi, FixedArray::kHeaderSize), ebx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003253 __ add(edi, Immediate(kPointerSize));
3254 __ sub(edx, Immediate(kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003255 __ dec(ecx);
3256 __ j(not_zero, &loop);
3257
3258 // Return and remove the on-stack parameters.
3259 __ bind(&done);
3260 __ ret(3 * kPointerSize);
3261
3262 // Do the runtime call to allocate the arguments object.
3263 __ bind(&runtime);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003264 __ TailCallRuntime(Runtime::kNewStrictArgumentsFast, 3, 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003265}
3266
3267
3268void RegExpExecStub::Generate(MacroAssembler* masm) {
3269 // Just jump directly to runtime if native RegExp is not selected at compile
3270 // time or if regexp entry in generated code is turned off runtime switch or
3271 // at compilation.
3272#ifdef V8_INTERPRETED_REGEXP
3273 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
3274#else // V8_INTERPRETED_REGEXP
ricow@chromium.org65fae842010-08-25 15:26:24 +00003275
3276 // Stack frame on entry.
3277 // esp[0]: return address
3278 // esp[4]: last_match_info (expected JSArray)
3279 // esp[8]: previous index
3280 // esp[12]: subject string
3281 // esp[16]: JSRegExp object
3282
3283 static const int kLastMatchInfoOffset = 1 * kPointerSize;
3284 static const int kPreviousIndexOffset = 2 * kPointerSize;
3285 static const int kSubjectOffset = 3 * kPointerSize;
3286 static const int kJSRegExpOffset = 4 * kPointerSize;
3287
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003288 Label runtime;
3289 Factory* factory = masm->isolate()->factory();
ricow@chromium.org65fae842010-08-25 15:26:24 +00003290
3291 // Ensure that a RegExp stack is allocated.
3292 ExternalReference address_of_regexp_stack_memory_address =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003293 ExternalReference::address_of_regexp_stack_memory_address(
3294 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003295 ExternalReference address_of_regexp_stack_memory_size =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003296 ExternalReference::address_of_regexp_stack_memory_size(masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003297 __ mov(ebx, Operand::StaticVariable(address_of_regexp_stack_memory_size));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003298 __ test(ebx, ebx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00003299 __ j(zero, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003300
3301 // Check that the first argument is a JSRegExp object.
3302 __ mov(eax, Operand(esp, kJSRegExpOffset));
3303 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003304 __ JumpIfSmi(eax, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003305 __ CmpObjectType(eax, JS_REGEXP_TYPE, ecx);
3306 __ j(not_equal, &runtime);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003307
ricow@chromium.org65fae842010-08-25 15:26:24 +00003308 // Check that the RegExp has been compiled (data contains a fixed array).
3309 __ mov(ecx, FieldOperand(eax, JSRegExp::kDataOffset));
3310 if (FLAG_debug_code) {
3311 __ test(ecx, Immediate(kSmiTagMask));
danno@chromium.org59400602013-08-13 17:09:37 +00003312 __ Check(not_zero, kUnexpectedTypeForRegExpDataFixedArrayExpected);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003313 __ CmpObjectType(ecx, FIXED_ARRAY_TYPE, ebx);
danno@chromium.org59400602013-08-13 17:09:37 +00003314 __ Check(equal, kUnexpectedTypeForRegExpDataFixedArrayExpected);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003315 }
3316
3317 // ecx: RegExp data (FixedArray)
3318 // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP.
3319 __ mov(ebx, FieldOperand(ecx, JSRegExp::kDataTagOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003320 __ cmp(ebx, Immediate(Smi::FromInt(JSRegExp::IRREGEXP)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003321 __ j(not_equal, &runtime);
3322
3323 // ecx: RegExp data (FixedArray)
3324 // Check that the number of captures fit in the static offsets vector buffer.
3325 __ mov(edx, FieldOperand(ecx, JSRegExp::kIrregexpCaptureCountOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003326 // Check (number_of_captures + 1) * 2 <= offsets vector size
3327 // Or number_of_captures * 2 <= offsets vector size - 2
3328 // Multiplying by 2 comes for free since edx is smi-tagged.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003329 STATIC_ASSERT(kSmiTag == 0);
3330 STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003331 STATIC_ASSERT(Isolate::kJSRegexpStaticOffsetsVectorSize >= 2);
3332 __ cmp(edx, Isolate::kJSRegexpStaticOffsetsVectorSize - 2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003333 __ j(above, &runtime);
3334
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003335 // Reset offset for possibly sliced string.
3336 __ Set(edi, Immediate(0));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003337 __ mov(eax, Operand(esp, kSubjectOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003338 __ JumpIfSmi(eax, &runtime);
3339 __ mov(edx, eax); // Make a copy of the original subject string.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003340 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
3341 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003342
3343 // eax: subject string
3344 // edx: subject string
3345 // ebx: subject string instance type
3346 // ecx: RegExp data (FixedArray)
3347 // Handle subject string according to its encoding and representation:
3348 // (1) Sequential two byte? If yes, go to (9).
3349 // (2) Sequential one byte? If yes, go to (6).
3350 // (3) Anything but sequential or cons? If yes, go to (7).
3351 // (4) Cons string. If the string is flat, replace subject with first string.
3352 // Otherwise bailout.
3353 // (5a) Is subject sequential two byte? If yes, go to (9).
3354 // (5b) Is subject external? If yes, go to (8).
3355 // (6) One byte sequential. Load regexp code for one byte.
3356 // (E) Carry on.
3357 /// [...]
3358
3359 // Deferred code at the end of the stub:
3360 // (7) Not a long external string? If yes, go to (10).
3361 // (8) External string. Make it, offset-wise, look like a sequential string.
3362 // (8a) Is the external string one byte? If yes, go to (6).
3363 // (9) Two byte sequential. Load regexp code for one byte. Go to (E).
3364 // (10) Short external string or not a string? If yes, bail out to runtime.
3365 // (11) Sliced string. Replace subject with parent. Go to (5a).
3366
3367 Label seq_one_byte_string /* 6 */, seq_two_byte_string /* 9 */,
3368 external_string /* 8 */, check_underlying /* 5a */,
3369 not_seq_nor_cons /* 7 */, check_code /* E */,
3370 not_long_external /* 10 */;
3371
3372 // (1) Sequential two byte? If yes, go to (9).
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003373 __ and_(ebx, kIsNotStringMask |
3374 kStringRepresentationMask |
3375 kStringEncodingMask |
3376 kShortExternalStringMask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003377 STATIC_ASSERT((kStringTag | kSeqStringTag | kTwoByteStringTag) == 0);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003378 __ j(zero, &seq_two_byte_string); // Go to (9).
3379
3380 // (2) Sequential one byte? If yes, go to (6).
3381 // Any other sequential string must be one byte.
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003382 __ and_(ebx, Immediate(kIsNotStringMask |
3383 kStringRepresentationMask |
3384 kShortExternalStringMask));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003385 __ j(zero, &seq_one_byte_string, Label::kNear); // Go to (6).
ricow@chromium.org65fae842010-08-25 15:26:24 +00003386
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003387 // (3) Anything but sequential or cons? If yes, go to (7).
3388 // We check whether the subject string is a cons, since sequential strings
3389 // have already been covered.
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00003390 STATIC_ASSERT(kConsStringTag < kExternalStringTag);
3391 STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003392 STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
3393 STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003394 __ cmp(ebx, Immediate(kExternalStringTag));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003395 __ j(greater_equal, &not_seq_nor_cons); // Go to (7).
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003396
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003397 // (4) Cons string. Check that it's flat.
3398 // Replace subject with first string and reload instance type.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003399 __ cmp(FieldOperand(eax, ConsString::kSecondOffset), factory->empty_string());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003400 __ j(not_equal, &runtime);
3401 __ mov(eax, FieldOperand(eax, ConsString::kFirstOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003402 __ bind(&check_underlying);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003403 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003404 __ mov(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
3405
3406 // (5a) Is subject sequential two byte? If yes, go to (9).
3407 __ test_b(ebx, kStringRepresentationMask | kStringEncodingMask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003408 STATIC_ASSERT((kSeqStringTag | kTwoByteStringTag) == 0);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003409 __ j(zero, &seq_two_byte_string); // Go to (9).
3410 // (5b) Is subject external? If yes, go to (8).
3411 __ test_b(ebx, kStringRepresentationMask);
3412 // The underlying external string is never a short external string.
3413 STATIC_CHECK(ExternalString::kMaxShortLength < ConsString::kMinLength);
3414 STATIC_CHECK(ExternalString::kMaxShortLength < SlicedString::kMinLength);
3415 __ j(not_zero, &external_string); // Go to (8).
ricow@chromium.org65fae842010-08-25 15:26:24 +00003416
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003417 // eax: sequential subject string (or look-alike, external string)
3418 // edx: original subject string
ricow@chromium.org65fae842010-08-25 15:26:24 +00003419 // ecx: RegExp data (FixedArray)
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003420 // (6) One byte sequential. Load regexp code for one byte.
3421 __ bind(&seq_one_byte_string);
3422 // Load previous index and check range before edx is overwritten. We have
3423 // to use edx instead of eax here because it might have been only made to
3424 // look like a sequential string when it actually is an external string.
3425 __ mov(ebx, Operand(esp, kPreviousIndexOffset));
3426 __ JumpIfNotSmi(ebx, &runtime);
3427 __ cmp(ebx, FieldOperand(edx, String::kLengthOffset));
3428 __ j(above_equal, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003429 __ mov(edx, FieldOperand(ecx, JSRegExp::kDataAsciiCodeOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003430 __ Set(ecx, Immediate(1)); // Type is one byte.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003431
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003432 // (E) Carry on. String handling is done.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003433 __ bind(&check_code);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003434 // edx: irregexp code
ricow@chromium.org65fae842010-08-25 15:26:24 +00003435 // Check that the irregexp code has been generated for the actual string
3436 // encoding. If it has, the field contains a code object otherwise it contains
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00003437 // a smi (code flushing support).
3438 __ JumpIfSmi(edx, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003439
3440 // eax: subject string
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003441 // ebx: previous index (smi)
ricow@chromium.org65fae842010-08-25 15:26:24 +00003442 // edx: code
ulan@chromium.org2efb9002012-01-19 15:36:35 +00003443 // ecx: encoding of subject string (1 if ASCII, 0 if two_byte);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003444 // All checks done. Now push arguments for native regexp code.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003445 Counters* counters = masm->isolate()->counters();
3446 __ IncrementCounter(counters->regexp_entry_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003447
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003448 // Isolates: note we add an additional parameter here (isolate pointer).
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003449 static const int kRegExpExecuteArguments = 9;
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003450 __ EnterApiExitFrame(kRegExpExecuteArguments);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003451
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003452 // Argument 9: Pass current isolate address.
3453 __ mov(Operand(esp, 8 * kPointerSize),
ulan@chromium.org32d7dba2013-04-24 10:59:06 +00003454 Immediate(ExternalReference::isolate_address(masm->isolate())));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003455
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003456 // Argument 8: Indicate that this is a direct call from JavaScript.
3457 __ mov(Operand(esp, 7 * kPointerSize), Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003458
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003459 // Argument 7: Start (high end) of backtracking stack memory area.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003460 __ mov(esi, Operand::StaticVariable(address_of_regexp_stack_memory_address));
3461 __ add(esi, Operand::StaticVariable(address_of_regexp_stack_memory_size));
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003462 __ mov(Operand(esp, 6 * kPointerSize), esi);
3463
3464 // Argument 6: Set the number of capture registers to zero to force global
3465 // regexps to behave as non-global. This does not affect non-global regexps.
3466 __ mov(Operand(esp, 5 * kPointerSize), Immediate(0));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003467
3468 // Argument 5: static offsets vector buffer.
3469 __ mov(Operand(esp, 4 * kPointerSize),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003470 Immediate(ExternalReference::address_of_static_offsets_vector(
3471 masm->isolate())));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003472
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003473 // Argument 2: Previous index.
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003474 __ SmiUntag(ebx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003475 __ mov(Operand(esp, 1 * kPointerSize), ebx);
3476
3477 // Argument 1: Original subject string.
3478 // The original subject is in the previous stack frame. Therefore we have to
3479 // use ebp, which points exactly to one pointer size below the previous esp.
3480 // (Because creating a new stack frame pushes the previous ebp onto the stack
3481 // and thereby moves up esp by one kPointerSize.)
3482 __ mov(esi, Operand(ebp, kSubjectOffset + kPointerSize));
3483 __ mov(Operand(esp, 0 * kPointerSize), esi);
3484
3485 // esi: original subject string
3486 // eax: underlying subject string
3487 // ebx: previous index
ulan@chromium.org2efb9002012-01-19 15:36:35 +00003488 // ecx: encoding of subject string (1 if ASCII 0 if two_byte);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003489 // edx: code
ricow@chromium.org65fae842010-08-25 15:26:24 +00003490 // Argument 4: End of string data
3491 // Argument 3: Start of string data
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003492 // Prepare start and end index of the input.
3493 // Load the length from the original sliced string if that is the case.
3494 __ mov(esi, FieldOperand(esi, String::kLengthOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003495 __ add(esi, edi); // Calculate input end wrt offset.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003496 __ SmiUntag(edi);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003497 __ add(ebx, edi); // Calculate input start wrt offset.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003498
3499 // ebx: start index of the input string
3500 // esi: end index of the input string
3501 Label setup_two_byte, setup_rest;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003502 __ test(ecx, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003503 __ j(zero, &setup_two_byte, Label::kNear);
3504 __ SmiUntag(esi);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003505 __ lea(ecx, FieldOperand(eax, esi, times_1, SeqOneByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003506 __ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003507 __ lea(ecx, FieldOperand(eax, ebx, times_1, SeqOneByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003508 __ mov(Operand(esp, 2 * kPointerSize), ecx); // Argument 3.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003509 __ jmp(&setup_rest, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003510
3511 __ bind(&setup_two_byte);
3512 STATIC_ASSERT(kSmiTag == 0);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003513 STATIC_ASSERT(kSmiTagSize == 1); // esi is smi (powered by 2).
3514 __ lea(ecx, FieldOperand(eax, esi, times_1, SeqTwoByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003515 __ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
3516 __ lea(ecx, FieldOperand(eax, ebx, times_2, SeqTwoByteString::kHeaderSize));
3517 __ mov(Operand(esp, 2 * kPointerSize), ecx); // Argument 3.
3518
3519 __ bind(&setup_rest);
3520
ricow@chromium.org65fae842010-08-25 15:26:24 +00003521 // Locate the code entry and call it.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003522 __ add(edx, Immediate(Code::kHeaderSize - kHeapObjectTag));
3523 __ call(edx);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003524
3525 // Drop arguments and come back to JS mode.
3526 __ LeaveApiExitFrame();
ricow@chromium.org65fae842010-08-25 15:26:24 +00003527
3528 // Check the result.
3529 Label success;
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003530 __ cmp(eax, 1);
3531 // We expect exactly one result since we force the called regexp to behave
3532 // as non-global.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00003533 __ j(equal, &success);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003534 Label failure;
3535 __ cmp(eax, NativeRegExpMacroAssembler::FAILURE);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00003536 __ j(equal, &failure);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003537 __ cmp(eax, NativeRegExpMacroAssembler::EXCEPTION);
3538 // If not exception it can only be retry. Handle that in the runtime system.
3539 __ j(not_equal, &runtime);
3540 // Result must now be exception. If there is no pending exception already a
3541 // stack overflow (on the backtrack stack) was detected in RegExp code but
3542 // haven't created the exception yet. Handle that in the runtime system.
3543 // TODO(592): Rerunning the RegExp to get the stack overflow exception.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00003544 ExternalReference pending_exception(Isolate::kPendingExceptionAddress,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003545 masm->isolate());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003546 __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003547 __ mov(eax, Operand::StaticVariable(pending_exception));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003548 __ cmp(edx, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003549 __ j(equal, &runtime);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003550 // For exception, throw the exception again.
3551
3552 // Clear the pending exception variable.
3553 __ mov(Operand::StaticVariable(pending_exception), edx);
3554
3555 // Special handling of termination exceptions which are uncatchable
3556 // by javascript code.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003557 __ cmp(eax, factory->termination_exception());
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003558 Label throw_termination_exception;
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003559 __ j(equal, &throw_termination_exception, Label::kNear);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003560
3561 // Handle normal exception by following handler chain.
3562 __ Throw(eax);
3563
3564 __ bind(&throw_termination_exception);
ulan@chromium.org65a89c22012-02-14 11:46:07 +00003565 __ ThrowUncatchable(eax);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003566
ricow@chromium.org65fae842010-08-25 15:26:24 +00003567 __ bind(&failure);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00003568 // For failure to match, return null.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003569 __ mov(eax, factory->null_value());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003570 __ ret(4 * kPointerSize);
3571
3572 // Load RegExp data.
3573 __ bind(&success);
3574 __ mov(eax, Operand(esp, kJSRegExpOffset));
3575 __ mov(ecx, FieldOperand(eax, JSRegExp::kDataOffset));
3576 __ mov(edx, FieldOperand(ecx, JSRegExp::kIrregexpCaptureCountOffset));
3577 // Calculate number of capture registers (number_of_captures + 1) * 2.
3578 STATIC_ASSERT(kSmiTag == 0);
3579 STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003580 __ add(edx, Immediate(2)); // edx was a smi.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003581
3582 // edx: Number of capture registers
3583 // Load last_match_info which is still known to be a fast case JSArray.
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003584 // Check that the fourth object is a JSArray object.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003585 __ mov(eax, Operand(esp, kLastMatchInfoOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003586 __ JumpIfSmi(eax, &runtime);
3587 __ CmpObjectType(eax, JS_ARRAY_TYPE, ebx);
3588 __ j(not_equal, &runtime);
3589 // Check that the JSArray is in fast case.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003590 __ mov(ebx, FieldOperand(eax, JSArray::kElementsOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003591 __ mov(eax, FieldOperand(ebx, HeapObject::kMapOffset));
3592 __ cmp(eax, factory->fixed_array_map());
3593 __ j(not_equal, &runtime);
3594 // Check that the last match info has space for the capture registers and the
3595 // additional information.
3596 __ mov(eax, FieldOperand(ebx, FixedArray::kLengthOffset));
3597 __ SmiUntag(eax);
3598 __ sub(eax, Immediate(RegExpImpl::kLastMatchOverhead));
3599 __ cmp(edx, eax);
3600 __ j(greater, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003601
3602 // ebx: last_match_info backing store (FixedArray)
3603 // edx: number of capture registers
3604 // Store the capture count.
3605 __ SmiTag(edx); // Number of capture registers to smi.
3606 __ mov(FieldOperand(ebx, RegExpImpl::kLastCaptureCountOffset), edx);
3607 __ SmiUntag(edx); // Number of capture registers back from smi.
3608 // Store last subject and last input.
3609 __ mov(eax, Operand(esp, kSubjectOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003610 __ mov(ecx, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003611 __ mov(FieldOperand(ebx, RegExpImpl::kLastSubjectOffset), eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003612 __ RecordWriteField(ebx,
3613 RegExpImpl::kLastSubjectOffset,
3614 eax,
3615 edi,
3616 kDontSaveFPRegs);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003617 __ mov(eax, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003618 __ mov(FieldOperand(ebx, RegExpImpl::kLastInputOffset), eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003619 __ RecordWriteField(ebx,
3620 RegExpImpl::kLastInputOffset,
3621 eax,
3622 edi,
3623 kDontSaveFPRegs);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003624
3625 // Get the static offsets vector filled by the native regexp code.
3626 ExternalReference address_of_static_offsets_vector =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003627 ExternalReference::address_of_static_offsets_vector(masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003628 __ mov(ecx, Immediate(address_of_static_offsets_vector));
3629
3630 // ebx: last_match_info backing store (FixedArray)
3631 // ecx: offsets vector
3632 // edx: number of capture registers
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003633 Label next_capture, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003634 // Capture register counter starts from number of capture registers and
3635 // counts down until wraping after zero.
3636 __ bind(&next_capture);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003637 __ sub(edx, Immediate(1));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003638 __ j(negative, &done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003639 // Read the value from the static offsets vector buffer.
3640 __ mov(edi, Operand(ecx, edx, times_int_size, 0));
3641 __ SmiTag(edi);
3642 // Store the smi value in the last match info.
3643 __ mov(FieldOperand(ebx,
3644 edx,
3645 times_pointer_size,
3646 RegExpImpl::kFirstCaptureOffset),
3647 edi);
3648 __ jmp(&next_capture);
3649 __ bind(&done);
3650
3651 // Return last match info.
3652 __ mov(eax, Operand(esp, kLastMatchInfoOffset));
3653 __ ret(4 * kPointerSize);
3654
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003655 // Do the runtime call to execute the regexp.
3656 __ bind(&runtime);
3657 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
3658
3659 // Deferred code for string handling.
3660 // (7) Not a long external string? If yes, go to (10).
3661 __ bind(&not_seq_nor_cons);
3662 // Compare flags are still set from (3).
3663 __ j(greater, &not_long_external, Label::kNear); // Go to (10).
3664
3665 // (8) External string. Short external strings have been ruled out.
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003666 __ bind(&external_string);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003667 // Reload instance type.
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003668 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
3669 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
3670 if (FLAG_debug_code) {
3671 // Assert that we do not have a cons or slice (indirect strings) here.
3672 // Sequential strings have already been ruled out.
3673 __ test_b(ebx, kIsIndirectStringMask);
danno@chromium.org59400602013-08-13 17:09:37 +00003674 __ Assert(zero, kExternalStringExpectedButNotFound);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003675 }
3676 __ mov(eax, FieldOperand(eax, ExternalString::kResourceDataOffset));
3677 // Move the pointer so that offset-wise, it looks like a sequential string.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003678 STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003679 __ sub(eax, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
3680 STATIC_ASSERT(kTwoByteStringTag == 0);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003681 // (8a) Is the external string one byte? If yes, go to (6).
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003682 __ test_b(ebx, kStringEncodingMask);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003683 __ j(not_zero, &seq_one_byte_string); // Goto (6).
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00003684
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00003685 // eax: sequential subject string (or look-alike, external string)
3686 // edx: original subject string
3687 // ecx: RegExp data (FixedArray)
3688 // (9) Two byte sequential. Load regexp code for one byte. Go to (E).
3689 __ bind(&seq_two_byte_string);
3690 // Load previous index and check range before edx is overwritten. We have
3691 // to use edx instead of eax here because it might have been only made to
3692 // look like a sequential string when it actually is an external string.
3693 __ mov(ebx, Operand(esp, kPreviousIndexOffset));
3694 __ JumpIfNotSmi(ebx, &runtime);
3695 __ cmp(ebx, FieldOperand(edx, String::kLengthOffset));
3696 __ j(above_equal, &runtime);
3697 __ mov(edx, FieldOperand(ecx, JSRegExp::kDataUC16CodeOffset));
3698 __ Set(ecx, Immediate(0)); // Type is two byte.
3699 __ jmp(&check_code); // Go to (E).
3700
3701 // (10) Not a string or a short external string? If yes, bail out to runtime.
3702 __ bind(&not_long_external);
3703 // Catch non-string subject or short external string.
3704 STATIC_ASSERT(kNotStringTag != 0 && kShortExternalStringTag !=0);
3705 __ test(ebx, Immediate(kIsNotStringMask | kShortExternalStringTag));
3706 __ j(not_zero, &runtime);
3707
3708 // (11) Sliced string. Replace subject with parent. Go to (5a).
3709 // Load offset into edi and replace subject string with parent.
3710 __ mov(edi, FieldOperand(eax, SlicedString::kOffsetOffset));
3711 __ mov(eax, FieldOperand(eax, SlicedString::kParentOffset));
3712 __ jmp(&check_underlying); // Go to (5a).
ricow@chromium.org65fae842010-08-25 15:26:24 +00003713#endif // V8_INTERPRETED_REGEXP
3714}
3715
3716
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003717void RegExpConstructResultStub::Generate(MacroAssembler* masm) {
3718 const int kMaxInlineLength = 100;
3719 Label slowcase;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003720 Label done;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003721 __ mov(ebx, Operand(esp, kPointerSize * 3));
whesse@chromium.org7b260152011-06-20 15:33:18 +00003722 __ JumpIfNotSmi(ebx, &slowcase);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003723 __ cmp(ebx, Immediate(Smi::FromInt(kMaxInlineLength)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003724 __ j(above, &slowcase);
3725 // Smi-tagging is equivalent to multiplying by 2.
3726 STATIC_ASSERT(kSmiTag == 0);
3727 STATIC_ASSERT(kSmiTagSize == 1);
3728 // Allocate RegExpResult followed by FixedArray with size in ebx.
3729 // JSArray: [Map][empty properties][Elements][Length-smi][index][input]
3730 // Elements: [Map][Length][..elements..]
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00003731 __ Allocate(JSRegExpResult::kSize + FixedArray::kHeaderSize,
3732 times_pointer_size,
3733 ebx, // In: Number of elements as a smi
3734 REGISTER_VALUE_IS_SMI,
3735 eax, // Out: Start of allocation (tagged).
3736 ecx, // Out: End of allocation.
3737 edx, // Scratch register
3738 &slowcase,
3739 TAG_OBJECT);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003740 // eax: Start of allocated area, object-tagged.
3741
3742 // Set JSArray map to global.regexp_result_map().
3743 // Set empty properties FixedArray.
3744 // Set elements to point to FixedArray allocated right after the JSArray.
3745 // Interleave operations for better latency.
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00003746 __ mov(edx, ContextOperand(esi, Context::GLOBAL_OBJECT_INDEX));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003747 Factory* factory = masm->isolate()->factory();
3748 __ mov(ecx, Immediate(factory->empty_fixed_array()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003749 __ lea(ebx, Operand(eax, JSRegExpResult::kSize));
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00003750 __ mov(edx, FieldOperand(edx, GlobalObject::kNativeContextOffset));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003751 __ mov(FieldOperand(eax, JSObject::kElementsOffset), ebx);
3752 __ mov(FieldOperand(eax, JSObject::kPropertiesOffset), ecx);
3753 __ mov(edx, ContextOperand(edx, Context::REGEXP_RESULT_MAP_INDEX));
3754 __ mov(FieldOperand(eax, HeapObject::kMapOffset), edx);
3755
3756 // Set input, index and length fields from arguments.
3757 __ mov(ecx, Operand(esp, kPointerSize * 1));
3758 __ mov(FieldOperand(eax, JSRegExpResult::kInputOffset), ecx);
3759 __ mov(ecx, Operand(esp, kPointerSize * 2));
3760 __ mov(FieldOperand(eax, JSRegExpResult::kIndexOffset), ecx);
3761 __ mov(ecx, Operand(esp, kPointerSize * 3));
3762 __ mov(FieldOperand(eax, JSArray::kLengthOffset), ecx);
3763
3764 // Fill out the elements FixedArray.
3765 // eax: JSArray.
3766 // ebx: FixedArray.
3767 // ecx: Number of elements in array, as smi.
3768
3769 // Set map.
3770 __ mov(FieldOperand(ebx, HeapObject::kMapOffset),
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003771 Immediate(factory->fixed_array_map()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003772 // Set length.
3773 __ mov(FieldOperand(ebx, FixedArray::kLengthOffset), ecx);
ulan@chromium.org56c14af2012-09-20 12:51:09 +00003774 // Fill contents of fixed-array with undefined.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003775 __ SmiUntag(ecx);
ulan@chromium.org56c14af2012-09-20 12:51:09 +00003776 __ mov(edx, Immediate(factory->undefined_value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003777 __ lea(ebx, FieldOperand(ebx, FixedArray::kHeaderSize));
ulan@chromium.org56c14af2012-09-20 12:51:09 +00003778 // Fill fixed array elements with undefined.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003779 // eax: JSArray.
3780 // ecx: Number of elements to fill.
3781 // ebx: Start of elements in FixedArray.
ulan@chromium.org56c14af2012-09-20 12:51:09 +00003782 // edx: undefined.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003783 Label loop;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003784 __ test(ecx, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003785 __ bind(&loop);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003786 __ j(less_equal, &done, Label::kNear); // Jump if ecx is negative or zero.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003787 __ sub(ecx, Immediate(1));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00003788 __ mov(Operand(ebx, ecx, times_pointer_size, 0), edx);
3789 __ jmp(&loop);
3790
3791 __ bind(&done);
3792 __ ret(3 * kPointerSize);
3793
3794 __ bind(&slowcase);
3795 __ TailCallRuntime(Runtime::kRegExpConstructResult, 3, 1);
3796}
3797
3798
ricow@chromium.org65fae842010-08-25 15:26:24 +00003799void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
3800 Register object,
3801 Register result,
3802 Register scratch1,
3803 Register scratch2,
ricow@chromium.org65fae842010-08-25 15:26:24 +00003804 Label* not_found) {
3805 // Use of registers. Register result is used as a temporary.
3806 Register number_string_cache = result;
3807 Register mask = scratch1;
3808 Register scratch = scratch2;
3809
3810 // Load the number string cache.
danno@chromium.org59400602013-08-13 17:09:37 +00003811 __ LoadRoot(number_string_cache, Heap::kNumberStringCacheRootIndex);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003812 // Make the hash mask from the length of the number string cache. It
3813 // contains two elements (number and string) for each cache entry.
3814 __ mov(mask, FieldOperand(number_string_cache, FixedArray::kLengthOffset));
3815 __ shr(mask, kSmiTagSize + 1); // Untag length and divide it by two.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003816 __ sub(mask, Immediate(1)); // Make mask.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003817
3818 // Calculate the entry in the number string cache. The hash value in the
3819 // number string cache for smis is just the smi value, and the hash for
3820 // doubles is the xor of the upper and lower words. See
3821 // Heap::GetNumberStringCache.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003822 Label smi_hash_calculated;
3823 Label load_result_from_cache;
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +00003824 Label not_smi;
3825 STATIC_ASSERT(kSmiTag == 0);
3826 __ JumpIfNotSmi(object, &not_smi, Label::kNear);
3827 __ mov(scratch, object);
3828 __ SmiUntag(scratch);
3829 __ jmp(&smi_hash_calculated, Label::kNear);
3830 __ bind(&not_smi);
3831 __ cmp(FieldOperand(object, HeapObject::kMapOffset),
3832 masm->isolate()->factory()->heap_number_map());
3833 __ j(not_equal, not_found);
3834 STATIC_ASSERT(8 == kDoubleSize);
3835 __ mov(scratch, FieldOperand(object, HeapNumber::kValueOffset));
3836 __ xor_(scratch, FieldOperand(object, HeapNumber::kValueOffset + 4));
3837 // Object is heap number and hash is now in scratch. Calculate cache index.
3838 __ and_(scratch, mask);
3839 Register index = scratch;
3840 Register probe = mask;
3841 __ mov(probe,
3842 FieldOperand(number_string_cache,
3843 index,
3844 times_twice_pointer_size,
3845 FixedArray::kHeaderSize));
3846 __ JumpIfSmi(probe, not_found);
3847 if (CpuFeatures::IsSupported(SSE2)) {
3848 CpuFeatureScope fscope(masm, SSE2);
3849 __ movdbl(xmm0, FieldOperand(object, HeapNumber::kValueOffset));
3850 __ movdbl(xmm1, FieldOperand(probe, HeapNumber::kValueOffset));
3851 __ ucomisd(xmm0, xmm1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003852 } else {
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +00003853 __ fld_d(FieldOperand(object, HeapNumber::kValueOffset));
3854 __ fld_d(FieldOperand(probe, HeapNumber::kValueOffset));
3855 __ FCmp();
ricow@chromium.org65fae842010-08-25 15:26:24 +00003856 }
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +00003857 __ j(parity_even, not_found); // Bail out if NaN is involved.
3858 __ j(not_equal, not_found); // The cache did not contain this value.
3859 __ jmp(&load_result_from_cache, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003860
3861 __ bind(&smi_hash_calculated);
3862 // Object is smi and hash is now in scratch. Calculate cache index.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003863 __ and_(scratch, mask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003864 // Check if the entry is the smi we are looking for.
3865 __ cmp(object,
3866 FieldOperand(number_string_cache,
3867 index,
3868 times_twice_pointer_size,
3869 FixedArray::kHeaderSize));
3870 __ j(not_equal, not_found);
3871
3872 // Get the result from the cache.
3873 __ bind(&load_result_from_cache);
3874 __ mov(result,
3875 FieldOperand(number_string_cache,
3876 index,
3877 times_twice_pointer_size,
3878 FixedArray::kHeaderSize + kPointerSize));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003879 Counters* counters = masm->isolate()->counters();
3880 __ IncrementCounter(counters->number_to_string_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003881}
3882
3883
3884void NumberToStringStub::Generate(MacroAssembler* masm) {
3885 Label runtime;
3886
3887 __ mov(ebx, Operand(esp, kPointerSize));
3888
3889 // Generate code to lookup number in the number string cache.
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +00003890 GenerateLookupNumberStringCache(masm, ebx, eax, ecx, edx, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003891 __ ret(1 * kPointerSize);
3892
3893 __ bind(&runtime);
3894 // Handle number to string in the runtime system if not found in the cache.
3895 __ TailCallRuntime(Runtime::kNumberToStringSkipCache, 1, 1);
3896}
3897
3898
3899static int NegativeComparisonResult(Condition cc) {
3900 ASSERT(cc != equal);
3901 ASSERT((cc == less) || (cc == less_equal)
3902 || (cc == greater) || (cc == greater_equal));
3903 return (cc == greater || cc == greater_equal) ? LESS : GREATER;
3904}
3905
ricow@chromium.org65fae842010-08-25 15:26:24 +00003906
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003907static void CheckInputType(MacroAssembler* masm,
3908 Register input,
3909 CompareIC::State expected,
3910 Label* fail) {
3911 Label ok;
3912 if (expected == CompareIC::SMI) {
3913 __ JumpIfNotSmi(input, fail);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00003914 } else if (expected == CompareIC::NUMBER) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003915 __ JumpIfSmi(input, &ok);
3916 __ cmp(FieldOperand(input, HeapObject::kMapOffset),
3917 Immediate(masm->isolate()->factory()->heap_number_map()));
3918 __ j(not_equal, fail);
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003919 }
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00003920 // We could be strict about internalized/non-internalized here, but as long as
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003921 // hydrogen doesn't care, the stub doesn't have to care either.
3922 __ bind(&ok);
3923}
3924
3925
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00003926static void BranchIfNotInternalizedString(MacroAssembler* masm,
3927 Label* label,
3928 Register object,
3929 Register scratch) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003930 __ JumpIfSmi(object, label);
3931 __ mov(scratch, FieldOperand(object, HeapObject::kMapOffset));
3932 __ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +00003933 STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0);
3934 __ test(scratch, Immediate(kIsNotStringMask | kIsNotInternalizedMask));
3935 __ j(not_zero, label);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003936}
3937
3938
3939void ICCompareStub::GenerateGeneric(MacroAssembler* masm) {
3940 Label check_unequal_objects;
3941 Condition cc = GetCondition();
3942
3943 Label miss;
3944 CheckInputType(masm, edx, left_, &miss);
3945 CheckInputType(masm, eax, right_, &miss);
3946
3947 // Compare two smis.
3948 Label non_smi, smi_done;
3949 __ mov(ecx, edx);
3950 __ or_(ecx, eax);
3951 __ JumpIfNotSmi(ecx, &non_smi, Label::kNear);
3952 __ sub(edx, eax); // Return on the result of the subtraction.
3953 __ j(no_overflow, &smi_done, Label::kNear);
3954 __ not_(edx); // Correct sign in case of overflow. edx is never 0 here.
3955 __ bind(&smi_done);
3956 __ mov(eax, edx);
3957 __ ret(0);
3958 __ bind(&non_smi);
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003959
ricow@chromium.org65fae842010-08-25 15:26:24 +00003960 // NOTICE! This code is only reached after a smi-fast-case check, so
3961 // it is certain that at least one operand isn't a smi.
3962
3963 // Identical objects can be compared fast, but there are some tricky cases
3964 // for NaN and undefined.
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00003965 Label generic_heap_number_comparison;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003966 {
3967 Label not_identical;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003968 __ cmp(eax, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003969 __ j(not_equal, &not_identical);
3970
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003971 if (cc != equal) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00003972 // Check for undefined. undefined OP undefined is false even though
3973 // undefined == undefined.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003974 Label check_for_nan;
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003975 __ cmp(edx, masm->isolate()->factory()->undefined_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003976 __ j(not_equal, &check_for_nan, Label::kNear);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003977 __ Set(eax, Immediate(Smi::FromInt(NegativeComparisonResult(cc))));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003978 __ ret(0);
3979 __ bind(&check_for_nan);
3980 }
3981
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00003982 // Test for NaN. Compare heap numbers in a general way,
3983 // to hanlde NaNs correctly.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003984 __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
3985 Immediate(masm->isolate()->factory()->heap_number_map()));
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00003986 __ j(equal, &generic_heap_number_comparison, Label::kNear);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003987 if (cc != equal) {
3988 // Call runtime on identical JSObjects. Otherwise return equal.
3989 __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
3990 __ j(above_equal, &not_identical);
3991 }
3992 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
3993 __ ret(0);
3994
ricow@chromium.org65fae842010-08-25 15:26:24 +00003995
3996 __ bind(&not_identical);
3997 }
3998
3999 // Strict equality can quickly decide whether objects are equal.
4000 // Non-strict object equality is slower, so it is handled later in the stub.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004001 if (cc == equal && strict()) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00004002 Label slow; // Fallthrough label.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004003 Label not_smis;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004004 // If we're doing a strict equality comparison, we don't have to do
4005 // type conversion, so we generate code to do fast comparison for objects
4006 // and oddballs. Non-smi numbers and strings still go through the usual
4007 // slow-case code.
4008 // If either is a Smi (we know that not both are), then they can only
4009 // be equal if the other is a HeapNumber. If so, use the slow case.
4010 STATIC_ASSERT(kSmiTag == 0);
4011 ASSERT_EQ(0, Smi::FromInt(0));
4012 __ mov(ecx, Immediate(kSmiTagMask));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004013 __ and_(ecx, eax);
4014 __ test(ecx, edx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004015 __ j(not_zero, &not_smis, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004016 // One operand is a smi.
4017
4018 // Check whether the non-smi is a heap number.
4019 STATIC_ASSERT(kSmiTagMask == 1);
4020 // ecx still holds eax & kSmiTag, which is either zero or one.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004021 __ sub(ecx, Immediate(0x01));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004022 __ mov(ebx, edx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004023 __ xor_(ebx, eax);
4024 __ and_(ebx, ecx); // ebx holds either 0 or eax ^ edx.
4025 __ xor_(ebx, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004026 // if eax was smi, ebx is now edx, else eax.
4027
4028 // Check if the non-smi operand is a heap number.
4029 __ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004030 Immediate(masm->isolate()->factory()->heap_number_map()));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004031 // If heap number, handle it in the slow case.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004032 __ j(equal, &slow, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004033 // Return non-equal (ebx is not zero)
4034 __ mov(eax, ebx);
4035 __ ret(0);
4036
4037 __ bind(&not_smis);
4038 // If either operand is a JSObject or an oddball value, then they are not
4039 // equal since their pointers are different
4040 // There is no test for undetectability in strict equality.
4041
4042 // Get the type of the first operand.
4043 // If the first object is a JS object, we have done pointer comparison.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004044 Label first_non_object;
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004045 STATIC_ASSERT(LAST_TYPE == LAST_SPEC_OBJECT_TYPE);
4046 __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004047 __ j(below, &first_non_object, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004048
4049 // Return non-zero (eax is not zero)
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004050 Label return_not_equal;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004051 STATIC_ASSERT(kHeapObjectTag != 0);
4052 __ bind(&return_not_equal);
4053 __ ret(0);
4054
4055 __ bind(&first_non_object);
4056 // Check for oddballs: true, false, null, undefined.
4057 __ CmpInstanceType(ecx, ODDBALL_TYPE);
4058 __ j(equal, &return_not_equal);
4059
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004060 __ CmpObjectType(edx, FIRST_SPEC_OBJECT_TYPE, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004061 __ j(above_equal, &return_not_equal);
4062
4063 // Check for oddballs: true, false, null, undefined.
4064 __ CmpInstanceType(ecx, ODDBALL_TYPE);
4065 __ j(equal, &return_not_equal);
4066
4067 // Fall through to the general case.
4068 __ bind(&slow);
4069 }
4070
4071 // Generate the number comparison code.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004072 Label non_number_comparison;
4073 Label unordered;
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00004074 __ bind(&generic_heap_number_comparison);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004075 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00004076 CpuFeatureScope use_sse2(masm, SSE2);
4077 CpuFeatureScope use_cmov(masm, CMOV);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004078
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004079 FloatingPointHelper::LoadSSE2Operands(masm, &non_number_comparison);
4080 __ ucomisd(xmm0, xmm1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004081
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004082 // Don't base result on EFLAGS when a NaN is involved.
4083 __ j(parity_even, &unordered, Label::kNear);
4084 // Return a result of -1, 0, or 1, based on EFLAGS.
4085 __ mov(eax, 0); // equal
4086 __ mov(ecx, Immediate(Smi::FromInt(1)));
4087 __ cmov(above, eax, ecx);
4088 __ mov(ecx, Immediate(Smi::FromInt(-1)));
4089 __ cmov(below, eax, ecx);
4090 __ ret(0);
4091 } else {
4092 FloatingPointHelper::CheckFloatOperands(
4093 masm, &non_number_comparison, ebx);
4094 FloatingPointHelper::LoadFloatOperand(masm, eax);
4095 FloatingPointHelper::LoadFloatOperand(masm, edx);
4096 __ FCmp();
ricow@chromium.org65fae842010-08-25 15:26:24 +00004097
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004098 // Don't base result on EFLAGS when a NaN is involved.
4099 __ j(parity_even, &unordered, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004100
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004101 Label below_label, above_label;
4102 // Return a result of -1, 0, or 1, based on EFLAGS.
4103 __ j(below, &below_label, Label::kNear);
4104 __ j(above, &above_label, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004105
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004106 __ Set(eax, Immediate(0));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004107 __ ret(0);
4108
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004109 __ bind(&below_label);
4110 __ mov(eax, Immediate(Smi::FromInt(-1)));
4111 __ ret(0);
4112
4113 __ bind(&above_label);
4114 __ mov(eax, Immediate(Smi::FromInt(1)));
4115 __ ret(0);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004116 }
4117
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004118 // If one of the numbers was NaN, then the result is always false.
4119 // The cc is never not-equal.
4120 __ bind(&unordered);
4121 ASSERT(cc != not_equal);
4122 if (cc == less || cc == less_equal) {
4123 __ mov(eax, Immediate(Smi::FromInt(1)));
4124 } else {
4125 __ mov(eax, Immediate(Smi::FromInt(-1)));
4126 }
4127 __ ret(0);
4128
4129 // The number comparison code did not provide a valid result.
4130 __ bind(&non_number_comparison);
4131
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004132 // Fast negative check for internalized-to-internalized equality.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004133 Label check_for_strings;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004134 if (cc == equal) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004135 BranchIfNotInternalizedString(masm, &check_for_strings, eax, ecx);
4136 BranchIfNotInternalizedString(masm, &check_for_strings, edx, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004137
4138 // We've already checked for object identity, so if both operands
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004139 // are internalized they aren't equal. Register eax already holds a
ricow@chromium.org65fae842010-08-25 15:26:24 +00004140 // non-zero value, which indicates not equal, so just return.
4141 __ ret(0);
4142 }
4143
4144 __ bind(&check_for_strings);
4145
4146 __ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx,
4147 &check_unequal_objects);
4148
ulan@chromium.org2efb9002012-01-19 15:36:35 +00004149 // Inline comparison of ASCII strings.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004150 if (cc == equal) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00004151 StringCompareStub::GenerateFlatAsciiStringEquals(masm,
ricow@chromium.org65fae842010-08-25 15:26:24 +00004152 edx,
4153 eax,
4154 ecx,
lrn@chromium.org1c092762011-05-09 09:42:16 +00004155 ebx);
4156 } else {
4157 StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
4158 edx,
4159 eax,
4160 ecx,
4161 ebx,
4162 edi);
4163 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00004164#ifdef DEBUG
danno@chromium.org59400602013-08-13 17:09:37 +00004165 __ Abort(kUnexpectedFallThroughFromStringComparison);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004166#endif
4167
4168 __ bind(&check_unequal_objects);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004169 if (cc == equal && !strict()) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00004170 // Non-strict equality. Objects are unequal if
4171 // they are both JSObjects and not undetectable,
4172 // and their pointers are different.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004173 Label not_both_objects;
4174 Label return_unequal;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004175 // At most one is a smi, so we can test for smi by adding the two.
4176 // A smi plus a heap object has the low bit set, a heap object plus
4177 // a heap object has the low bit clear.
4178 STATIC_ASSERT(kSmiTag == 0);
4179 STATIC_ASSERT(kSmiTagMask == 1);
4180 __ lea(ecx, Operand(eax, edx, times_1, 0));
4181 __ test(ecx, Immediate(kSmiTagMask));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004182 __ j(not_zero, &not_both_objects, Label::kNear);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004183 __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004184 __ j(below, &not_both_objects, Label::kNear);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004185 __ CmpObjectType(edx, FIRST_SPEC_OBJECT_TYPE, ebx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004186 __ j(below, &not_both_objects, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004187 // We do not bail out after this point. Both are JSObjects, and
4188 // they are equal if and only if both are undetectable.
4189 // The and of the undetectable flags is 1 if and only if they are equal.
4190 __ test_b(FieldOperand(ecx, Map::kBitFieldOffset),
4191 1 << Map::kIsUndetectable);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004192 __ j(zero, &return_unequal, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004193 __ test_b(FieldOperand(ebx, Map::kBitFieldOffset),
4194 1 << Map::kIsUndetectable);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004195 __ j(zero, &return_unequal, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004196 // The objects are both undetectable, so they both compare as the value
4197 // undefined, and are equal.
4198 __ Set(eax, Immediate(EQUAL));
4199 __ bind(&return_unequal);
4200 // Return non-equal by returning the non-zero object pointer in eax,
4201 // or return equal if we fell through to here.
4202 __ ret(0); // rax, rdx were pushed
4203 __ bind(&not_both_objects);
4204 }
4205
4206 // Push arguments below the return address.
4207 __ pop(ecx);
4208 __ push(edx);
4209 __ push(eax);
4210
4211 // Figure out which native to call and setup the arguments.
4212 Builtins::JavaScript builtin;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004213 if (cc == equal) {
4214 builtin = strict() ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004215 } else {
4216 builtin = Builtins::COMPARE;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004217 __ push(Immediate(Smi::FromInt(NegativeComparisonResult(cc))));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004218 }
4219
4220 // Restore return address on the stack.
4221 __ push(ecx);
4222
4223 // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
4224 // tagged as a small integer.
4225 __ InvokeBuiltin(builtin, JUMP_FUNCTION);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004226
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004227 __ bind(&miss);
4228 GenerateMiss(masm);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004229}
4230
4231
4232void StackCheckStub::Generate(MacroAssembler* masm) {
whesse@chromium.org4a5224e2010-10-20 12:37:07 +00004233 __ TailCallRuntime(Runtime::kStackGuard, 0, 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004234}
4235
4236
yangguo@chromium.org56454712012-02-16 15:33:53 +00004237void InterruptStub::Generate(MacroAssembler* masm) {
4238 __ TailCallRuntime(Runtime::kInterrupt, 0, 1);
4239}
4240
4241
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004242static void GenerateRecordCallTarget(MacroAssembler* masm) {
4243 // Cache the called function in a global property cell. Cache states
4244 // are uninitialized, monomorphic (indicated by a JSFunction), and
4245 // megamorphic.
4246 // ebx : cache cell for call target
4247 // edi : the function to call
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004248 Isolate* isolate = masm->isolate();
4249 Label initialize, done, miss, megamorphic, not_array_function;
4250
4251 // Load the cache state into ecx.
danno@chromium.org41728482013-06-12 22:31:22 +00004252 __ mov(ecx, FieldOperand(ebx, Cell::kValueOffset));
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004253
4254 // A monomorphic cache hit or an already megamorphic state: invoke the
4255 // function without changing the state.
4256 __ cmp(ecx, edi);
4257 __ j(equal, &done);
4258 __ cmp(ecx, Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate)));
4259 __ j(equal, &done);
4260
danno@chromium.orgbee51992013-07-10 14:57:15 +00004261 // If we came here, we need to see if we are the array function.
4262 // If we didn't have a matching function, and we didn't find the megamorph
4263 // sentinel, then we have in the cell either some other function or an
4264 // AllocationSite. Do a map check on the object in ecx.
4265 Handle<Map> allocation_site_map(
4266 masm->isolate()->heap()->allocation_site_map(),
4267 masm->isolate());
4268 __ cmp(FieldOperand(ecx, 0), Immediate(allocation_site_map));
4269 __ j(not_equal, &miss);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00004270
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004271 // Load the global or builtins object from the current context
4272 __ LoadGlobalContext(ecx);
4273 // Make sure the function is the Array() function
4274 __ cmp(edi, Operand(ecx,
4275 Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX)));
4276 __ j(not_equal, &megamorphic);
4277 __ jmp(&done);
4278
4279 __ bind(&miss);
4280
4281 // A monomorphic miss (i.e, here the cache is not uninitialized) goes
4282 // megamorphic.
4283 __ cmp(ecx, Immediate(TypeFeedbackCells::UninitializedSentinel(isolate)));
4284 __ j(equal, &initialize);
4285 // MegamorphicSentinel is an immortal immovable object (undefined) so no
4286 // write-barrier is needed.
4287 __ bind(&megamorphic);
danno@chromium.org41728482013-06-12 22:31:22 +00004288 __ mov(FieldOperand(ebx, Cell::kValueOffset),
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004289 Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate)));
4290 __ jmp(&done, Label::kNear);
4291
4292 // An uninitialized cache is patched with the function or sentinel to
4293 // indicate the ElementsKind if function is the Array constructor.
4294 __ bind(&initialize);
4295 __ LoadGlobalContext(ecx);
4296 // Make sure the function is the Array() function
4297 __ cmp(edi, Operand(ecx,
4298 Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX)));
4299 __ j(not_equal, &not_array_function);
4300
danno@chromium.orgbee51992013-07-10 14:57:15 +00004301 // The target function is the Array constructor,
4302 // Create an AllocationSite if we don't already have it, store it in the cell
4303 {
4304 FrameScope scope(masm, StackFrame::INTERNAL);
4305
verwaest@chromium.org662436e2013-08-28 08:41:27 +00004306 __ SmiTag(eax);
danno@chromium.orgbee51992013-07-10 14:57:15 +00004307 __ push(eax);
4308 __ push(edi);
4309 __ push(ebx);
4310
4311 CreateAllocationSiteStub create_stub;
4312 __ CallStub(&create_stub);
4313
4314 __ pop(ebx);
4315 __ pop(edi);
4316 __ pop(eax);
verwaest@chromium.org662436e2013-08-28 08:41:27 +00004317 __ SmiUntag(eax);
danno@chromium.orgbee51992013-07-10 14:57:15 +00004318 }
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004319 __ jmp(&done);
4320
4321 __ bind(&not_array_function);
danno@chromium.org41728482013-06-12 22:31:22 +00004322 __ mov(FieldOperand(ebx, Cell::kValueOffset), edi);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004323 // No need for a write barrier here - cells are rescanned.
4324
4325 __ bind(&done);
4326}
4327
4328
ricow@chromium.org65fae842010-08-25 15:26:24 +00004329void CallFunctionStub::Generate(MacroAssembler* masm) {
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004330 // ebx : cache cell for call target
danno@chromium.orgc612e022011-11-10 11:38:15 +00004331 // edi : the function to call
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004332 Isolate* isolate = masm->isolate();
lrn@chromium.org34e60782011-09-15 07:25:40 +00004333 Label slow, non_function;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004334
danno@chromium.org40cb8782011-05-25 07:58:50 +00004335 // The receiver might implicitly be the global object. This is
4336 // indicated by passing the hole as the receiver to the call
4337 // function stub.
4338 if (ReceiverMightBeImplicit()) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004339 Label receiver_ok;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004340 // Get the receiver from the stack.
4341 // +1 ~ return address
ricow@chromium.org65fae842010-08-25 15:26:24 +00004342 __ mov(eax, Operand(esp, (argc_ + 1) * kPointerSize));
danno@chromium.org40cb8782011-05-25 07:58:50 +00004343 // Call as function is indicated with the hole.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004344 __ cmp(eax, isolate->factory()->the_hole_value());
4345 __ j(not_equal, &receiver_ok, Label::kNear);
danno@chromium.org40cb8782011-05-25 07:58:50 +00004346 // Patch the receiver on the stack with the global receiver object.
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004347 __ mov(ecx, GlobalObjectOperand());
4348 __ mov(ecx, FieldOperand(ecx, GlobalObject::kGlobalReceiverOffset));
4349 __ mov(Operand(esp, (argc_ + 1) * kPointerSize), ecx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004350 __ bind(&receiver_ok);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004351 }
4352
ricow@chromium.org65fae842010-08-25 15:26:24 +00004353 // Check that the function really is a JavaScript function.
lrn@chromium.org34e60782011-09-15 07:25:40 +00004354 __ JumpIfSmi(edi, &non_function);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004355 // Goto slow case if we do not have a function.
4356 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00004357 __ j(not_equal, &slow);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004358
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004359 if (RecordCallTarget()) {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00004360 GenerateRecordCallTarget(masm);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004361 }
4362
ricow@chromium.org65fae842010-08-25 15:26:24 +00004363 // Fast-case: Just invoke the function.
4364 ParameterCount actual(argc_);
danno@chromium.org40cb8782011-05-25 07:58:50 +00004365
4366 if (ReceiverMightBeImplicit()) {
4367 Label call_as_function;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004368 __ cmp(eax, isolate->factory()->the_hole_value());
danno@chromium.org40cb8782011-05-25 07:58:50 +00004369 __ j(equal, &call_as_function);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00004370 __ InvokeFunction(edi,
4371 actual,
4372 JUMP_FUNCTION,
4373 NullCallWrapper(),
4374 CALL_AS_METHOD);
danno@chromium.org40cb8782011-05-25 07:58:50 +00004375 __ bind(&call_as_function);
4376 }
4377 __ InvokeFunction(edi,
4378 actual,
4379 JUMP_FUNCTION,
4380 NullCallWrapper(),
4381 CALL_AS_FUNCTION);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004382
4383 // Slow-case: Non-function called.
4384 __ bind(&slow);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004385 if (RecordCallTarget()) {
4386 // If there is a call target cache, mark it megamorphic in the
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004387 // non-function case. MegamorphicSentinel is an immortal immovable
4388 // object (undefined) so no write barrier is needed.
danno@chromium.org41728482013-06-12 22:31:22 +00004389 __ mov(FieldOperand(ebx, Cell::kValueOffset),
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004390 Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate)));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004391 }
lrn@chromium.org34e60782011-09-15 07:25:40 +00004392 // Check for function proxy.
4393 __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
4394 __ j(not_equal, &non_function);
4395 __ pop(ecx);
4396 __ push(edi); // put proxy as additional argument under return address
4397 __ push(ecx);
4398 __ Set(eax, Immediate(argc_ + 1));
4399 __ Set(ebx, Immediate(0));
4400 __ SetCallKind(ecx, CALL_AS_FUNCTION);
4401 __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY);
4402 {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004403 Handle<Code> adaptor = isolate->builtins()->ArgumentsAdaptorTrampoline();
lrn@chromium.org34e60782011-09-15 07:25:40 +00004404 __ jmp(adaptor, RelocInfo::CODE_TARGET);
4405 }
4406
ricow@chromium.org65fae842010-08-25 15:26:24 +00004407 // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
4408 // of the original receiver from the call site).
lrn@chromium.org34e60782011-09-15 07:25:40 +00004409 __ bind(&non_function);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004410 __ mov(Operand(esp, (argc_ + 1) * kPointerSize), edi);
4411 __ Set(eax, Immediate(argc_));
4412 __ Set(ebx, Immediate(0));
lrn@chromium.org34e60782011-09-15 07:25:40 +00004413 __ SetCallKind(ecx, CALL_AS_METHOD);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004414 __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004415 Handle<Code> adaptor = isolate->builtins()->ArgumentsAdaptorTrampoline();
ricow@chromium.org65fae842010-08-25 15:26:24 +00004416 __ jmp(adaptor, RelocInfo::CODE_TARGET);
4417}
4418
4419
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004420void CallConstructStub::Generate(MacroAssembler* masm) {
4421 // eax : number of arguments
4422 // ebx : cache cell for call target
4423 // edi : constructor function
4424 Label slow, non_function_call;
4425
4426 // Check that function is not a smi.
4427 __ JumpIfSmi(edi, &non_function_call);
4428 // Check that function is a JSFunction.
4429 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
4430 __ j(not_equal, &slow);
4431
4432 if (RecordCallTarget()) {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00004433 GenerateRecordCallTarget(masm);
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004434 }
4435
4436 // Jump to the function-specific construct stub.
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00004437 Register jmp_reg = ecx;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004438 __ mov(jmp_reg, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
4439 __ mov(jmp_reg, FieldOperand(jmp_reg,
4440 SharedFunctionInfo::kConstructStubOffset));
4441 __ lea(jmp_reg, FieldOperand(jmp_reg, Code::kHeaderSize));
4442 __ jmp(jmp_reg);
danno@chromium.orgfa458e42012-02-01 10:48:36 +00004443
4444 // edi: called object
4445 // eax: number of arguments
4446 // ecx: object map
4447 Label do_call;
4448 __ bind(&slow);
4449 __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
4450 __ j(not_equal, &non_function_call);
4451 __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY_AS_CONSTRUCTOR);
4452 __ jmp(&do_call);
4453
4454 __ bind(&non_function_call);
4455 __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
4456 __ bind(&do_call);
4457 // Set expected number of arguments to zero (not changing eax).
4458 __ Set(ebx, Immediate(0));
4459 Handle<Code> arguments_adaptor =
4460 masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
4461 __ SetCallKind(ecx, CALL_AS_METHOD);
4462 __ jmp(arguments_adaptor, RelocInfo::CODE_TARGET);
4463}
4464
4465
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00004466bool CEntryStub::NeedsImmovableCode() {
4467 return false;
4468}
4469
4470
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004471bool CEntryStub::IsPregenerated() {
4472 return (!save_doubles_ || ISOLATE->fp_stubs_generated()) &&
4473 result_size_ == 1;
4474}
4475
4476
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004477void CodeStub::GenerateStubsAheadOfTime(Isolate* isolate) {
4478 CEntryStub::GenerateAheadOfTime(isolate);
4479 StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(isolate);
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00004480 StubFailureTrampolineStub::GenerateAheadOfTime(isolate);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004481 // It is important that the store buffer overflow stubs are generated first.
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004482 RecordWriteStub::GenerateFixedRegStubsAheadOfTime(isolate);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00004483 ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate);
danno@chromium.orgbee51992013-07-10 14:57:15 +00004484 CreateAllocationSiteStub::GenerateAheadOfTime(isolate);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004485}
4486
4487
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004488void CodeStub::GenerateFPStubs(Isolate* isolate) {
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004489 if (CpuFeatures::IsSupported(SSE2)) {
4490 CEntryStub save_doubles(1, kSaveFPRegs);
4491 // Stubs might already be in the snapshot, detect that and don't regenerate,
4492 // which would lead to code stub initialization state being messed up.
4493 Code* save_doubles_code;
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004494 if (!save_doubles.FindCodeInCache(&save_doubles_code, isolate)) {
4495 save_doubles_code = *(save_doubles.GetCode(isolate));
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004496 }
4497 save_doubles_code->set_is_pregenerated(true);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004498 isolate->set_fp_stubs_generated(true);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00004499 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004500}
4501
4502
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004503void CEntryStub::GenerateAheadOfTime(Isolate* isolate) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004504 CEntryStub stub(1, kDontSaveFPRegs);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004505 Handle<Code> code = stub.GetCode(isolate);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004506 code->set_is_pregenerated(true);
4507}
4508
4509
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00004510static void JumpIfOOM(MacroAssembler* masm,
4511 Register value,
4512 Register scratch,
4513 Label* oom_label) {
4514 __ mov(scratch, value);
4515 STATIC_ASSERT(Failure::OUT_OF_MEMORY_EXCEPTION == 3);
4516 STATIC_ASSERT(kFailureTag == 3);
4517 __ and_(scratch, 0xf);
4518 __ cmp(scratch, 0xf);
4519 __ j(equal, oom_label);
4520}
4521
4522
ricow@chromium.org65fae842010-08-25 15:26:24 +00004523void CEntryStub::GenerateCore(MacroAssembler* masm,
4524 Label* throw_normal_exception,
4525 Label* throw_termination_exception,
4526 Label* throw_out_of_memory_exception,
4527 bool do_gc,
ager@chromium.org0ee099b2011-01-25 14:06:47 +00004528 bool always_allocate_scope) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00004529 // eax: result parameter for PerformGC, if any
4530 // ebx: pointer to C function (C callee-saved)
4531 // ebp: frame pointer (restored after C call)
4532 // esp: stack pointer (restored after C call)
4533 // edi: number of arguments including receiver (C callee-saved)
4534 // esi: pointer to the first argument (C callee-saved)
4535
4536 // Result returned in eax, or eax+edx if result_size_ is 2.
4537
4538 // Check stack alignment.
4539 if (FLAG_debug_code) {
4540 __ CheckStackAlignment();
4541 }
4542
4543 if (do_gc) {
4544 // Pass failure code returned from last attempt as first argument to
4545 // PerformGC. No need to use PrepareCallCFunction/CallCFunction here as the
4546 // stack alignment is known to be correct. This function takes one argument
4547 // which is passed on the stack, and we know that the stack has been
4548 // prepared to pass at least one argument.
4549 __ mov(Operand(esp, 0 * kPointerSize), eax); // Result.
4550 __ call(FUNCTION_ADDR(Runtime::PerformGC), RelocInfo::RUNTIME_ENTRY);
4551 }
4552
4553 ExternalReference scope_depth =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004554 ExternalReference::heap_always_allocate_scope_depth(masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004555 if (always_allocate_scope) {
4556 __ inc(Operand::StaticVariable(scope_depth));
4557 }
4558
4559 // Call C function.
4560 __ mov(Operand(esp, 0 * kPointerSize), edi); // argc.
4561 __ mov(Operand(esp, 1 * kPointerSize), esi); // argv.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004562 __ mov(Operand(esp, 2 * kPointerSize),
ulan@chromium.org32d7dba2013-04-24 10:59:06 +00004563 Immediate(ExternalReference::isolate_address(masm->isolate())));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004564 __ call(ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004565 // Result is in eax or edx:eax - do not destroy these registers!
4566
4567 if (always_allocate_scope) {
4568 __ dec(Operand::StaticVariable(scope_depth));
4569 }
4570
danno@chromium.orgca29dd82013-04-26 11:59:48 +00004571 // Runtime functions should not return 'the hole'. Allowing it to escape may
4572 // lead to crashes in the IC code later.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004573 if (FLAG_debug_code) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004574 Label okay;
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004575 __ cmp(eax, masm->isolate()->factory()->the_hole_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004576 __ j(not_equal, &okay, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004577 __ int3();
4578 __ bind(&okay);
4579 }
4580
4581 // Check for failure result.
4582 Label failure_returned;
4583 STATIC_ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
4584 __ lea(ecx, Operand(eax, 1));
4585 // Lower 2 bits of ecx are 0 iff eax has failure tag.
4586 __ test(ecx, Immediate(kFailureTagMask));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00004587 __ j(zero, &failure_returned);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004588
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004589 ExternalReference pending_exception_address(
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004590 Isolate::kPendingExceptionAddress, masm->isolate());
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00004591
4592 // Check that there is no pending exception, otherwise we
4593 // should have returned some failure value.
4594 if (FLAG_debug_code) {
4595 __ push(edx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004596 __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004597 Label okay;
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00004598 __ cmp(edx, Operand::StaticVariable(pending_exception_address));
4599 // Cannot use check here as it attempts to generate call into runtime.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004600 __ j(equal, &okay, Label::kNear);
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00004601 __ int3();
4602 __ bind(&okay);
4603 __ pop(edx);
4604 }
4605
ricow@chromium.org65fae842010-08-25 15:26:24 +00004606 // Exit the JavaScript to C++ exit frame.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004607 __ LeaveExitFrame(save_doubles_ == kSaveFPRegs);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004608 __ ret(0);
4609
4610 // Handling of failure.
4611 __ bind(&failure_returned);
4612
4613 Label retry;
4614 // If the returned exception is RETRY_AFTER_GC continue at retry label
4615 STATIC_ASSERT(Failure::RETRY_AFTER_GC == 0);
4616 __ test(eax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004617 __ j(zero, &retry, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004618
4619 // Special handling of out of memory exceptions.
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00004620 JumpIfOOM(masm, eax, ecx, throw_out_of_memory_exception);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004621
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00004622 // Retrieve the pending exception.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004623 __ mov(eax, Operand::StaticVariable(pending_exception_address));
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00004624
4625 // See if we just retrieved an OOM exception.
4626 JumpIfOOM(masm, eax, ecx, throw_out_of_memory_exception);
4627
4628 // Clear the pending exception.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004629 __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004630 __ mov(Operand::StaticVariable(pending_exception_address), edx);
4631
4632 // Special handling of termination exceptions which are uncatchable
4633 // by javascript code.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004634 __ cmp(eax, masm->isolate()->factory()->termination_exception());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004635 __ j(equal, throw_termination_exception);
4636
4637 // Handle normal exception.
4638 __ jmp(throw_normal_exception);
4639
4640 // Retry.
4641 __ bind(&retry);
4642}
4643
4644
ricow@chromium.org65fae842010-08-25 15:26:24 +00004645void CEntryStub::Generate(MacroAssembler* masm) {
4646 // eax: number of arguments including receiver
4647 // ebx: pointer to C function (C callee-saved)
4648 // ebp: frame pointer (restored after C call)
4649 // esp: stack pointer (restored after C call)
4650 // esi: current context (C callee-saved)
4651 // edi: JS function of the caller (C callee-saved)
4652
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00004653 ProfileEntryHookStub::MaybeCallEntryHook(masm);
4654
ricow@chromium.org65fae842010-08-25 15:26:24 +00004655 // NOTE: Invocations of builtins may return failure objects instead
4656 // of a proper result. The builtin entry handles this by performing
4657 // a garbage collection and retrying the builtin (twice).
4658
4659 // Enter the exit frame that transitions from JavaScript to C++.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004660 __ EnterExitFrame(save_doubles_ == kSaveFPRegs);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004661
4662 // eax: result parameter for PerformGC, if any (setup below)
4663 // ebx: pointer to builtin 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: argv pointer (C callee-saved)
4668
4669 Label throw_normal_exception;
4670 Label throw_termination_exception;
4671 Label throw_out_of_memory_exception;
4672
4673 // Call into the runtime system.
4674 GenerateCore(masm,
4675 &throw_normal_exception,
4676 &throw_termination_exception,
4677 &throw_out_of_memory_exception,
4678 false,
4679 false);
4680
4681 // Do space-specific GC and retry runtime call.
4682 GenerateCore(masm,
4683 &throw_normal_exception,
4684 &throw_termination_exception,
4685 &throw_out_of_memory_exception,
4686 true,
4687 false);
4688
4689 // Do full GC and retry runtime call one final time.
4690 Failure* failure = Failure::InternalError();
4691 __ mov(eax, Immediate(reinterpret_cast<int32_t>(failure)));
4692 GenerateCore(masm,
4693 &throw_normal_exception,
4694 &throw_termination_exception,
4695 &throw_out_of_memory_exception,
4696 true,
4697 true);
4698
4699 __ bind(&throw_out_of_memory_exception);
ulan@chromium.org65a89c22012-02-14 11:46:07 +00004700 // Set external caught exception to false.
4701 Isolate* isolate = masm->isolate();
4702 ExternalReference external_caught(Isolate::kExternalCaughtExceptionAddress,
4703 isolate);
4704 __ mov(Operand::StaticVariable(external_caught), Immediate(false));
4705
4706 // Set pending exception and eax to out of memory exception.
4707 ExternalReference pending_exception(Isolate::kPendingExceptionAddress,
4708 isolate);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00004709 Label already_have_failure;
4710 JumpIfOOM(masm, eax, ecx, &already_have_failure);
4711 __ mov(eax, reinterpret_cast<int32_t>(Failure::OutOfMemoryException(0x1)));
4712 __ bind(&already_have_failure);
ulan@chromium.org65a89c22012-02-14 11:46:07 +00004713 __ mov(Operand::StaticVariable(pending_exception), eax);
4714 // Fall through to the next label.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004715
4716 __ bind(&throw_termination_exception);
ulan@chromium.org65a89c22012-02-14 11:46:07 +00004717 __ ThrowUncatchable(eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004718
4719 __ bind(&throw_normal_exception);
ulan@chromium.org65a89c22012-02-14 11:46:07 +00004720 __ Throw(eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004721}
4722
4723
4724void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00004725 Label invoke, handler_entry, exit;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004726 Label not_outermost_js, not_outermost_js_2;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004727
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00004728 ProfileEntryHookStub::MaybeCallEntryHook(masm);
4729
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00004730 // Set up frame.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004731 __ push(ebp);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004732 __ mov(ebp, esp);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004733
4734 // Push marker in two places.
4735 int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
4736 __ push(Immediate(Smi::FromInt(marker))); // context slot
4737 __ push(Immediate(Smi::FromInt(marker))); // function slot
4738 // Save callee-saved registers (C calling conventions).
4739 __ push(edi);
4740 __ push(esi);
4741 __ push(ebx);
4742
4743 // Save copies of the top frame descriptor on the stack.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004744 ExternalReference c_entry_fp(Isolate::kCEntryFPAddress, masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004745 __ push(Operand::StaticVariable(c_entry_fp));
4746
ricow@chromium.org65fae842010-08-25 15:26:24 +00004747 // If this is the outermost JS call, set js_entry_sp value.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004748 ExternalReference js_entry_sp(Isolate::kJSEntrySPAddress,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004749 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004750 __ cmp(Operand::StaticVariable(js_entry_sp), Immediate(0));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004751 __ j(not_equal, &not_outermost_js, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004752 __ mov(Operand::StaticVariable(js_entry_sp), ebp);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004753 __ push(Immediate(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME)));
danno@chromium.org2c26cb12012-05-03 09:06:43 +00004754 __ jmp(&invoke, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004755 __ bind(&not_outermost_js);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004756 __ push(Immediate(Smi::FromInt(StackFrame::INNER_JSENTRY_FRAME)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004757
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00004758 // Jump to a faked try block that does the invoke, with a faked catch
4759 // block that sets the pending exception.
4760 __ jmp(&invoke);
4761 __ bind(&handler_entry);
4762 handler_offset_ = handler_entry.pos();
4763 // Caught exception: Store result (exception) in the pending exception
4764 // field in the JSEnv and return a failure sentinel.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004765 ExternalReference pending_exception(Isolate::kPendingExceptionAddress,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004766 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004767 __ mov(Operand::StaticVariable(pending_exception), eax);
4768 __ mov(eax, reinterpret_cast<int32_t>(Failure::Exception()));
4769 __ jmp(&exit);
4770
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00004771 // Invoke: Link this frame into the handler chain. There's only one
4772 // handler block in this code object, so its index is 0.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004773 __ bind(&invoke);
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00004774 __ PushTryHandler(StackHandler::JS_ENTRY, 0);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004775
4776 // Clear any pending exceptions.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004777 __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004778 __ mov(Operand::StaticVariable(pending_exception), edx);
4779
4780 // Fake a receiver (NULL).
4781 __ push(Immediate(0)); // receiver
4782
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00004783 // Invoke the function by calling through JS entry trampoline builtin and
4784 // pop the faked function when we return. Notice that we cannot store a
4785 // reference to the trampoline code directly in this stub, because the
4786 // builtin stubs may not have been generated yet.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004787 if (is_construct) {
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00004788 ExternalReference construct_entry(Builtins::kJSConstructEntryTrampoline,
4789 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004790 __ mov(edx, Immediate(construct_entry));
4791 } else {
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004792 ExternalReference entry(Builtins::kJSEntryTrampoline,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004793 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004794 __ mov(edx, Immediate(entry));
4795 }
4796 __ mov(edx, Operand(edx, 0)); // deref address
4797 __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004798 __ call(edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004799
4800 // Unlink this frame from the handler chain.
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004801 __ PopTryHandler();
ricow@chromium.org65fae842010-08-25 15:26:24 +00004802
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004803 __ bind(&exit);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00004804 // Check if the current stack frame is marked as the outermost JS frame.
4805 __ pop(ebx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004806 __ cmp(ebx, Immediate(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004807 __ j(not_equal, &not_outermost_js_2);
4808 __ mov(Operand::StaticVariable(js_entry_sp), Immediate(0));
4809 __ bind(&not_outermost_js_2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004810
4811 // Restore the top frame descriptor from the stack.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004812 __ pop(Operand::StaticVariable(ExternalReference(
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00004813 Isolate::kCEntryFPAddress,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00004814 masm->isolate())));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004815
4816 // Restore callee-saved registers (C calling conventions).
4817 __ pop(ebx);
4818 __ pop(esi);
4819 __ pop(edi);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004820 __ add(esp, Immediate(2 * kPointerSize)); // remove markers
ricow@chromium.org65fae842010-08-25 15:26:24 +00004821
4822 // Restore frame pointer and return.
4823 __ pop(ebp);
4824 __ ret(0);
4825}
4826
4827
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004828// Generate stub code for instanceof.
4829// This code can patch a call site inlined cache of the instance of check,
4830// which looks like this.
4831//
4832// 81 ff XX XX XX XX cmp edi, <the hole, patched to a map>
4833// 75 0a jne <some near label>
4834// b8 XX XX XX XX mov eax, <the hole, patched to either true or false>
4835//
4836// If call site patching is requested the stack will have the delta from the
4837// return address to the cmp instruction just below the return address. This
4838// also means that call site patching can only take place with arguments in
4839// registers. TOS looks like this when call site patching is requested
4840//
4841// esp[0] : return address
4842// esp[4] : delta from return address to cmp instruction
4843//
ricow@chromium.org65fae842010-08-25 15:26:24 +00004844void InstanceofStub::Generate(MacroAssembler* masm) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004845 // Call site inlining and patching implies arguments in registers.
4846 ASSERT(HasArgsInRegisters() || !HasCallSiteInlineCheck());
4847
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004848 // Fixed register usage throughout the stub.
4849 Register object = eax; // Object (lhs).
4850 Register map = ebx; // Map of the object.
4851 Register function = edx; // Function (rhs).
4852 Register prototype = edi; // Prototype of the function.
4853 Register scratch = ecx;
4854
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004855 // Constants describing the call site code to patch.
4856 static const int kDeltaToCmpImmediate = 2;
4857 static const int kDeltaToMov = 8;
4858 static const int kDeltaToMovImmediate = 9;
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00004859 static const int8_t kCmpEdiOperandByte1 = BitCast<int8_t, uint8_t>(0x3b);
4860 static const int8_t kCmpEdiOperandByte2 = BitCast<int8_t, uint8_t>(0x3d);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004861 static const int8_t kMovEaxImmediateByte = BitCast<int8_t, uint8_t>(0xb8);
4862
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004863 ASSERT_EQ(object.code(), InstanceofStub::left().code());
4864 ASSERT_EQ(function.code(), InstanceofStub::right().code());
4865
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004866 // Get the object and function - they are always both needed.
4867 Label slow, not_js_object;
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004868 if (!HasArgsInRegisters()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004869 __ mov(object, Operand(esp, 2 * kPointerSize));
4870 __ mov(function, Operand(esp, 1 * kPointerSize));
4871 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00004872
4873 // Check that the left hand is a JS object.
whesse@chromium.org7b260152011-06-20 15:33:18 +00004874 __ JumpIfSmi(object, &not_js_object);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004875 __ IsObjectJSObjectType(object, map, scratch, &not_js_object);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004876
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004877 // If there is a call site cache don't look in the global cache, but do the
4878 // real lookup and update the call site cache.
4879 if (!HasCallSiteInlineCheck()) {
4880 // Look up the function and the map in the instanceof cache.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004881 Label miss;
danno@chromium.org59400602013-08-13 17:09:37 +00004882 __ CompareRoot(function, scratch, Heap::kInstanceofCacheFunctionRootIndex);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004883 __ j(not_equal, &miss, Label::kNear);
danno@chromium.org59400602013-08-13 17:09:37 +00004884 __ CompareRoot(map, scratch, Heap::kInstanceofCacheMapRootIndex);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004885 __ j(not_equal, &miss, Label::kNear);
danno@chromium.org59400602013-08-13 17:09:37 +00004886 __ LoadRoot(eax, Heap::kInstanceofCacheAnswerRootIndex);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004887 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
4888 __ bind(&miss);
4889 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00004890
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004891 // Get the prototype of the function.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00004892 __ TryGetFunctionPrototype(function, prototype, scratch, &slow, true);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004893
4894 // Check that the function prototype is a JS object.
whesse@chromium.org7b260152011-06-20 15:33:18 +00004895 __ JumpIfSmi(prototype, &slow);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004896 __ IsObjectJSObjectType(prototype, scratch, scratch, &slow);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004897
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004898 // Update the global instanceof or call site inlined cache with the current
4899 // map and function. The cached answer will be set when it is known below.
4900 if (!HasCallSiteInlineCheck()) {
danno@chromium.org59400602013-08-13 17:09:37 +00004901 __ StoreRoot(map, scratch, Heap::kInstanceofCacheMapRootIndex);
4902 __ StoreRoot(function, scratch, Heap::kInstanceofCacheFunctionRootIndex);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004903 } else {
4904 // The constants for the code patching are based on no push instructions
4905 // at the call site.
4906 ASSERT(HasArgsInRegisters());
4907 // Get return address and delta to inlined map check.
4908 __ mov(scratch, Operand(esp, 0 * kPointerSize));
4909 __ sub(scratch, Operand(esp, 1 * kPointerSize));
4910 if (FLAG_debug_code) {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00004911 __ cmpb(Operand(scratch, 0), kCmpEdiOperandByte1);
danno@chromium.org59400602013-08-13 17:09:37 +00004912 __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheCmp1);
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00004913 __ cmpb(Operand(scratch, 1), kCmpEdiOperandByte2);
danno@chromium.org59400602013-08-13 17:09:37 +00004914 __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheCmp2);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004915 }
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00004916 __ mov(scratch, Operand(scratch, kDeltaToCmpImmediate));
4917 __ mov(Operand(scratch, 0), map);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004918 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00004919
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004920 // Loop through the prototype chain of the object looking for the function
4921 // prototype.
4922 __ mov(scratch, FieldOperand(map, Map::kPrototypeOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004923 Label loop, is_instance, is_not_instance;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004924 __ bind(&loop);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004925 __ cmp(scratch, prototype);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004926 __ j(equal, &is_instance, Label::kNear);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004927 Factory* factory = masm->isolate()->factory();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004928 __ cmp(scratch, Immediate(factory->null_value()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004929 __ j(equal, &is_not_instance, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004930 __ mov(scratch, FieldOperand(scratch, HeapObject::kMapOffset));
4931 __ mov(scratch, FieldOperand(scratch, Map::kPrototypeOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004932 __ jmp(&loop);
4933
4934 __ bind(&is_instance);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004935 if (!HasCallSiteInlineCheck()) {
danno@chromium.org59400602013-08-13 17:09:37 +00004936 __ mov(eax, Immediate(0));
4937 __ StoreRoot(eax, scratch, Heap::kInstanceofCacheAnswerRootIndex);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004938 } else {
4939 // Get return address and delta to inlined map check.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004940 __ mov(eax, factory->true_value());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004941 __ mov(scratch, Operand(esp, 0 * kPointerSize));
4942 __ sub(scratch, Operand(esp, 1 * kPointerSize));
4943 if (FLAG_debug_code) {
4944 __ cmpb(Operand(scratch, kDeltaToMov), kMovEaxImmediateByte);
danno@chromium.org59400602013-08-13 17:09:37 +00004945 __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheMov);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004946 }
4947 __ mov(Operand(scratch, kDeltaToMovImmediate), eax);
4948 if (!ReturnTrueFalseObject()) {
4949 __ Set(eax, Immediate(0));
4950 }
4951 }
4952 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004953
4954 __ bind(&is_not_instance);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004955 if (!HasCallSiteInlineCheck()) {
danno@chromium.org59400602013-08-13 17:09:37 +00004956 __ mov(eax, Immediate(Smi::FromInt(1)));
4957 __ StoreRoot(eax, scratch, Heap::kInstanceofCacheAnswerRootIndex);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004958 } else {
4959 // Get return address and delta to inlined map check.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004960 __ mov(eax, factory->false_value());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004961 __ mov(scratch, Operand(esp, 0 * kPointerSize));
4962 __ sub(scratch, Operand(esp, 1 * kPointerSize));
4963 if (FLAG_debug_code) {
4964 __ cmpb(Operand(scratch, kDeltaToMov), kMovEaxImmediateByte);
danno@chromium.org59400602013-08-13 17:09:37 +00004965 __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheMov);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004966 }
4967 __ mov(Operand(scratch, kDeltaToMovImmediate), eax);
4968 if (!ReturnTrueFalseObject()) {
4969 __ Set(eax, Immediate(Smi::FromInt(1)));
4970 }
4971 }
4972 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004973
4974 Label object_not_null, object_not_null_or_smi;
4975 __ bind(&not_js_object);
4976 // Before null, smi and string value checks, check that the rhs is a function
4977 // as for a non-function rhs an exception needs to be thrown.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004978 __ JumpIfSmi(function, &slow, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004979 __ CmpObjectType(function, JS_FUNCTION_TYPE, scratch);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004980 __ j(not_equal, &slow, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004981
4982 // Null is not instance of anything.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004983 __ cmp(object, factory->null_value());
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004984 __ j(not_equal, &object_not_null, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004985 __ Set(eax, Immediate(Smi::FromInt(1)));
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004986 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004987
4988 __ bind(&object_not_null);
4989 // Smi values is not instance of anything.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004990 __ JumpIfNotSmi(object, &object_not_null_or_smi, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004991 __ Set(eax, Immediate(Smi::FromInt(1)));
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004992 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004993
4994 __ bind(&object_not_null_or_smi);
4995 // String values is not instance of anything.
4996 Condition is_string = masm->IsObjectStringType(object, scratch, scratch);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00004997 __ j(NegateCondition(is_string), &slow, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00004998 __ Set(eax, Immediate(Smi::FromInt(1)));
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00004999 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005000
5001 // Slow-case: Go through the JavaScript implementation.
5002 __ bind(&slow);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005003 if (!ReturnTrueFalseObject()) {
5004 // Tail call the builtin which returns 0 or 1.
5005 if (HasArgsInRegisters()) {
5006 // Push arguments below return address.
5007 __ pop(scratch);
5008 __ push(object);
5009 __ push(function);
5010 __ push(scratch);
5011 }
5012 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
5013 } else {
5014 // Call the builtin and convert 0/1 to true/false.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005015 {
5016 FrameScope scope(masm, StackFrame::INTERNAL);
5017 __ push(object);
5018 __ push(function);
5019 __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION);
5020 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005021 Label true_value, done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005022 __ test(eax, eax);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005023 __ j(zero, &true_value, Label::kNear);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005024 __ mov(eax, factory->false_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005025 __ jmp(&done, Label::kNear);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005026 __ bind(&true_value);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005027 __ mov(eax, factory->true_value());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005028 __ bind(&done);
5029 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00005030 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00005031}
5032
5033
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00005034Register InstanceofStub::left() { return eax; }
5035
5036
5037Register InstanceofStub::right() { return edx; }
5038
5039
ricow@chromium.org65fae842010-08-25 15:26:24 +00005040// -------------------------------------------------------------------------
5041// StringCharCodeAtGenerator
5042
5043void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00005044 // If the receiver is a smi trigger the non-string case.
5045 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005046 __ JumpIfSmi(object_, receiver_not_string_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005047
5048 // Fetch the instance type of the receiver into result register.
5049 __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset));
5050 __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
5051 // If the receiver is not a string trigger the non-string case.
5052 __ test(result_, Immediate(kIsNotStringMask));
5053 __ j(not_zero, receiver_not_string_);
5054
5055 // If the index is non-smi trigger the non-smi case.
5056 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005057 __ JumpIfNotSmi(index_, &index_not_smi_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005058 __ bind(&got_smi_index_);
5059
5060 // Check for index out of range.
danno@chromium.orgc612e022011-11-10 11:38:15 +00005061 __ cmp(index_, FieldOperand(object_, String::kLengthOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005062 __ j(above_equal, index_out_of_range_);
5063
danno@chromium.orgc612e022011-11-10 11:38:15 +00005064 __ SmiUntag(index_);
erikcorry0ad885c2011-11-21 13:51:57 +00005065
5066 Factory* factory = masm->isolate()->factory();
5067 StringCharLoadGenerator::Generate(
5068 masm, factory, object_, index_, result_, &call_runtime_);
5069
ricow@chromium.org65fae842010-08-25 15:26:24 +00005070 __ SmiTag(result_);
5071 __ bind(&exit_);
5072}
5073
5074
5075void StringCharCodeAtGenerator::GenerateSlow(
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005076 MacroAssembler* masm,
5077 const RuntimeCallHelper& call_helper) {
danno@chromium.org59400602013-08-13 17:09:37 +00005078 __ Abort(kUnexpectedFallthroughToCharCodeAtSlowCase);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005079
5080 // Index is not a smi.
5081 __ bind(&index_not_smi_);
5082 // If index is a heap number, try converting it to an integer.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005083 __ CheckMap(index_,
5084 masm->isolate()->factory()->heap_number_map(),
5085 index_not_number_,
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00005086 DONT_DO_SMI_CHECK);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005087 call_helper.BeforeCall(masm);
5088 __ push(object_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005089 __ push(index_); // Consumed by runtime conversion function.
5090 if (index_flags_ == STRING_INDEX_IS_NUMBER) {
5091 __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1);
5092 } else {
5093 ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
5094 // NumberToSmi discards numbers that are not exact integers.
5095 __ CallRuntime(Runtime::kNumberToSmi, 1);
5096 }
danno@chromium.orgc612e022011-11-10 11:38:15 +00005097 if (!index_.is(eax)) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00005098 // Save the conversion result before the pop instructions below
5099 // have a chance to overwrite it.
danno@chromium.orgc612e022011-11-10 11:38:15 +00005100 __ mov(index_, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005101 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00005102 __ pop(object_);
5103 // Reload the instance type.
5104 __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset));
5105 __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
5106 call_helper.AfterCall(masm);
5107 // If index is still not a smi, it must be out of range.
5108 STATIC_ASSERT(kSmiTag == 0);
danno@chromium.orgc612e022011-11-10 11:38:15 +00005109 __ JumpIfNotSmi(index_, index_out_of_range_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005110 // Otherwise, return to the fast path.
5111 __ jmp(&got_smi_index_);
5112
5113 // Call runtime. We get here when the receiver is a string and the
5114 // index is a number, but the code of getting the actual character
5115 // is too complex (e.g., when the string needs to be flattened).
5116 __ bind(&call_runtime_);
5117 call_helper.BeforeCall(masm);
5118 __ push(object_);
erikcorry0ad885c2011-11-21 13:51:57 +00005119 __ SmiTag(index_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005120 __ push(index_);
5121 __ CallRuntime(Runtime::kStringCharCodeAt, 2);
5122 if (!result_.is(eax)) {
5123 __ mov(result_, eax);
5124 }
5125 call_helper.AfterCall(masm);
5126 __ jmp(&exit_);
5127
danno@chromium.org59400602013-08-13 17:09:37 +00005128 __ Abort(kUnexpectedFallthroughFromCharCodeAtSlowCase);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005129}
5130
5131
5132// -------------------------------------------------------------------------
5133// StringCharFromCodeGenerator
5134
5135void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
5136 // Fast case of Heap::LookupSingleCharacterStringFromCode.
5137 STATIC_ASSERT(kSmiTag == 0);
5138 STATIC_ASSERT(kSmiShiftSize == 0);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00005139 ASSERT(IsPowerOf2(String::kMaxOneByteCharCode + 1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005140 __ test(code_,
5141 Immediate(kSmiTagMask |
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00005142 ((~String::kMaxOneByteCharCode) << kSmiTagSize)));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005143 __ j(not_zero, &slow_case_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005144
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005145 Factory* factory = masm->isolate()->factory();
5146 __ Set(result_, Immediate(factory->single_character_string_cache()));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005147 STATIC_ASSERT(kSmiTag == 0);
5148 STATIC_ASSERT(kSmiTagSize == 1);
5149 STATIC_ASSERT(kSmiShiftSize == 0);
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005150 // At this point code register contains smi tagged ASCII char code.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005151 __ mov(result_, FieldOperand(result_,
5152 code_, times_half_pointer_size,
5153 FixedArray::kHeaderSize));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005154 __ cmp(result_, factory->undefined_value());
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005155 __ j(equal, &slow_case_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005156 __ bind(&exit_);
5157}
5158
5159
5160void StringCharFromCodeGenerator::GenerateSlow(
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005161 MacroAssembler* masm,
5162 const RuntimeCallHelper& call_helper) {
danno@chromium.org59400602013-08-13 17:09:37 +00005163 __ Abort(kUnexpectedFallthroughToCharFromCodeSlowCase);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005164
5165 __ bind(&slow_case_);
5166 call_helper.BeforeCall(masm);
5167 __ push(code_);
5168 __ CallRuntime(Runtime::kCharFromCode, 1);
5169 if (!result_.is(eax)) {
5170 __ mov(result_, eax);
5171 }
5172 call_helper.AfterCall(masm);
5173 __ jmp(&exit_);
5174
danno@chromium.org59400602013-08-13 17:09:37 +00005175 __ Abort(kUnexpectedFallthroughFromCharFromCodeSlowCase);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005176}
5177
5178
ricow@chromium.org65fae842010-08-25 15:26:24 +00005179void StringAddStub::Generate(MacroAssembler* masm) {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005180 Label call_runtime, call_builtin;
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005181 Builtins::JavaScript builtin_id = Builtins::ADD;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005182
5183 // Load the two arguments.
5184 __ mov(eax, Operand(esp, 2 * kPointerSize)); // First argument.
5185 __ mov(edx, Operand(esp, 1 * kPointerSize)); // Second argument.
5186
5187 // Make sure that both arguments are strings if not known in advance.
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +00005188 // Otherwise, at least one of the arguments is definitely a string,
5189 // and we convert the one that is not known to be a string.
5190 if ((flags_ & STRING_ADD_CHECK_BOTH) == STRING_ADD_CHECK_BOTH) {
5191 ASSERT((flags_ & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT);
5192 ASSERT((flags_ & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005193 __ JumpIfSmi(eax, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005194 __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ebx);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005195 __ j(above_equal, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005196
5197 // First argument is a a string, test second.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005198 __ JumpIfSmi(edx, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005199 __ CmpObjectType(edx, FIRST_NONSTRING_TYPE, ebx);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005200 __ j(above_equal, &call_runtime);
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +00005201 } else if ((flags_ & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT) {
5202 ASSERT((flags_ & STRING_ADD_CHECK_RIGHT) == 0);
5203 GenerateConvertArgument(masm, 2 * kPointerSize, eax, ebx, ecx, edi,
5204 &call_builtin);
5205 builtin_id = Builtins::STRING_ADD_RIGHT;
5206 } else if ((flags_ & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT) {
5207 ASSERT((flags_ & STRING_ADD_CHECK_LEFT) == 0);
5208 GenerateConvertArgument(masm, 1 * kPointerSize, edx, ebx, ecx, edi,
5209 &call_builtin);
5210 builtin_id = Builtins::STRING_ADD_LEFT;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005211 }
5212
5213 // Both arguments are strings.
5214 // eax: first string
5215 // edx: second string
5216 // Check if either of the strings are empty. In that case return the other.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005217 Label second_not_zero_length, both_not_zero_length;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005218 __ mov(ecx, FieldOperand(edx, String::kLengthOffset));
5219 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005220 __ test(ecx, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005221 __ j(not_zero, &second_not_zero_length, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005222 // Second string is empty, result is first string which is already in eax.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005223 Counters* counters = masm->isolate()->counters();
5224 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005225 __ ret(2 * kPointerSize);
5226 __ bind(&second_not_zero_length);
5227 __ mov(ebx, FieldOperand(eax, String::kLengthOffset));
5228 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005229 __ test(ebx, ebx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005230 __ j(not_zero, &both_not_zero_length, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005231 // First string is empty, result is second string which is in edx.
5232 __ mov(eax, edx);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005233 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005234 __ ret(2 * kPointerSize);
5235
5236 // Both strings are non-empty.
5237 // eax: first string
5238 // ebx: length of first string as a smi
5239 // ecx: length of second string as a smi
5240 // edx: second string
5241 // Look at the length of the result of adding the two strings.
5242 Label string_add_flat_result, longer_than_two;
5243 __ bind(&both_not_zero_length);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005244 __ add(ebx, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005245 STATIC_ASSERT(Smi::kMaxValue == String::kMaxLength);
5246 // Handle exceptionally long strings in the runtime system.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005247 __ j(overflow, &call_runtime);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005248 // Use the string table when adding two one character strings, as it
5249 // helps later optimizations to return an internalized string here.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005250 __ cmp(ebx, Immediate(Smi::FromInt(2)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005251 __ j(not_equal, &longer_than_two);
5252
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005253 // Check that both strings are non-external ASCII strings.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005254 __ JumpIfNotBothSequentialAsciiStrings(eax, edx, ebx, ecx, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005255
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005256 // Get the two characters forming the new string.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005257 __ movzx_b(ebx, FieldOperand(eax, SeqOneByteString::kHeaderSize));
5258 __ movzx_b(ecx, FieldOperand(edx, SeqOneByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005259
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005260 // Try to lookup two character string in string table. If it is not found
ricow@chromium.org65fae842010-08-25 15:26:24 +00005261 // just allocate a new one.
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005262 Label make_two_character_string, make_two_character_string_no_reload;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005263 StringHelper::GenerateTwoCharacterStringTableProbe(
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005264 masm, ebx, ecx, eax, edx, edi,
5265 &make_two_character_string_no_reload, &make_two_character_string);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005266 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005267 __ ret(2 * kPointerSize);
5268
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005269 // Allocate a two character string.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005270 __ bind(&make_two_character_string);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005271 // Reload the arguments.
5272 __ mov(eax, Operand(esp, 2 * kPointerSize)); // First argument.
5273 __ mov(edx, Operand(esp, 1 * kPointerSize)); // Second argument.
5274 // Get the two characters forming the new string.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005275 __ movzx_b(ebx, FieldOperand(eax, SeqOneByteString::kHeaderSize));
5276 __ movzx_b(ecx, FieldOperand(edx, SeqOneByteString::kHeaderSize));
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005277 __ bind(&make_two_character_string_no_reload);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005278 __ IncrementCounter(counters->string_add_make_two_char(), 1);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005279 __ AllocateAsciiString(eax, 2, edi, edx, &call_runtime);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005280 // Pack both characters in ebx.
5281 __ shl(ecx, kBitsPerByte);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005282 __ or_(ebx, ecx);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005283 // Set the characters in the new string.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005284 __ mov_w(FieldOperand(eax, SeqOneByteString::kHeaderSize), ebx);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005285 __ IncrementCounter(counters->string_add_native(), 1);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005286 __ ret(2 * kPointerSize);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005287
5288 __ bind(&longer_than_two);
5289 // Check if resulting string will be flat.
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005290 __ cmp(ebx, Immediate(Smi::FromInt(ConsString::kMinLength)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005291 __ j(below, &string_add_flat_result);
5292
5293 // If result is not supposed to be flat allocate a cons string object. If both
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005294 // strings are ASCII the result is an ASCII cons string.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005295 Label non_ascii, allocated, ascii_data;
5296 __ mov(edi, FieldOperand(eax, HeapObject::kMapOffset));
5297 __ movzx_b(ecx, FieldOperand(edi, Map::kInstanceTypeOffset));
5298 __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
5299 __ movzx_b(edi, FieldOperand(edi, Map::kInstanceTypeOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005300 __ and_(ecx, edi);
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +00005301 STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00005302 STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
5303 __ test(ecx, Immediate(kStringEncodingMask));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005304 __ j(zero, &non_ascii);
5305 __ bind(&ascii_data);
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005306 // Allocate an ASCII cons string.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005307 __ AllocateAsciiConsString(ecx, edi, no_reg, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005308 __ bind(&allocated);
5309 // Fill the fields of the cons string.
svenpanne@chromium.orgc859c4f2012-10-15 11:51:39 +00005310 __ AssertSmi(ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005311 __ mov(FieldOperand(ecx, ConsString::kLengthOffset), ebx);
5312 __ mov(FieldOperand(ecx, ConsString::kHashFieldOffset),
5313 Immediate(String::kEmptyHashField));
ulan@chromium.org57ff8812013-05-10 08:16:55 +00005314
5315 Label skip_write_barrier, after_writing;
5316 ExternalReference high_promotion_mode = ExternalReference::
5317 new_space_high_promotion_mode_active_address(masm->isolate());
5318 __ test(Operand::StaticVariable(high_promotion_mode), Immediate(1));
5319 __ j(zero, &skip_write_barrier);
5320
5321 __ mov(FieldOperand(ecx, ConsString::kFirstOffset), eax);
5322 __ RecordWriteField(ecx,
5323 ConsString::kFirstOffset,
5324 eax,
5325 ebx,
5326 kDontSaveFPRegs);
5327 __ mov(FieldOperand(ecx, ConsString::kSecondOffset), edx);
5328 __ RecordWriteField(ecx,
5329 ConsString::kSecondOffset,
5330 edx,
5331 ebx,
5332 kDontSaveFPRegs);
5333 __ jmp(&after_writing);
5334
5335 __ bind(&skip_write_barrier);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005336 __ mov(FieldOperand(ecx, ConsString::kFirstOffset), eax);
5337 __ mov(FieldOperand(ecx, ConsString::kSecondOffset), edx);
ulan@chromium.org57ff8812013-05-10 08:16:55 +00005338
5339 __ bind(&after_writing);
5340
ricow@chromium.org65fae842010-08-25 15:26:24 +00005341 __ mov(eax, ecx);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005342 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005343 __ ret(2 * kPointerSize);
5344 __ bind(&non_ascii);
5345 // At least one of the strings is two-byte. Check whether it happens
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005346 // to contain only one byte characters.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005347 // ecx: first instance type AND second instance type.
5348 // edi: second instance type.
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005349 __ test(ecx, Immediate(kOneByteDataHintMask));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005350 __ j(not_zero, &ascii_data);
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00005351 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
5352 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
5353 __ xor_(edi, ecx);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005354 STATIC_ASSERT(kOneByteStringTag != 0 && kOneByteDataHintTag != 0);
5355 __ and_(edi, kOneByteStringTag | kOneByteDataHintTag);
5356 __ cmp(edi, kOneByteStringTag | kOneByteDataHintTag);
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00005357 __ j(equal, &ascii_data);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005358 // Allocate a two byte cons string.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005359 __ AllocateTwoByteConsString(ecx, edi, no_reg, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005360 __ jmp(&allocated);
5361
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005362 // We cannot encounter sliced strings or cons strings here since:
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005363 STATIC_ASSERT(SlicedString::kMinLength >= ConsString::kMinLength);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005364 // Handle creating a flat result from either external or sequential strings.
5365 // Locate the first characters' locations.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005366 // eax: first string
5367 // ebx: length of resulting flat string as a smi
5368 // edx: second string
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005369 Label first_prepared, second_prepared;
5370 Label first_is_sequential, second_is_sequential;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005371 __ bind(&string_add_flat_result);
5372 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
5373 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005374 // ecx: instance type of first string
5375 STATIC_ASSERT(kSeqStringTag == 0);
5376 __ test_b(ecx, kStringRepresentationMask);
5377 __ j(zero, &first_is_sequential, Label::kNear);
5378 // Rule out short external string and load string resource.
5379 STATIC_ASSERT(kShortExternalStringTag != 0);
5380 __ test_b(ecx, kShortExternalStringMask);
5381 __ j(not_zero, &call_runtime);
5382 __ mov(eax, FieldOperand(eax, ExternalString::kResourceDataOffset));
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005383 STATIC_ASSERT(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005384 __ jmp(&first_prepared, Label::kNear);
5385 __ bind(&first_is_sequential);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005386 __ add(eax, Immediate(SeqOneByteString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005387 __ bind(&first_prepared);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005388
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005389 __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
5390 __ movzx_b(edi, FieldOperand(edi, Map::kInstanceTypeOffset));
5391 // Check whether both strings have same encoding.
5392 // edi: instance type of second string
5393 __ xor_(ecx, edi);
5394 __ test_b(ecx, kStringEncodingMask);
5395 __ j(not_zero, &call_runtime);
5396 STATIC_ASSERT(kSeqStringTag == 0);
5397 __ test_b(edi, kStringRepresentationMask);
5398 __ j(zero, &second_is_sequential, Label::kNear);
5399 // Rule out short external string and load string resource.
5400 STATIC_ASSERT(kShortExternalStringTag != 0);
5401 __ test_b(edi, kShortExternalStringMask);
5402 __ j(not_zero, &call_runtime);
5403 __ mov(edx, FieldOperand(edx, ExternalString::kResourceDataOffset));
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005404 STATIC_ASSERT(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005405 __ jmp(&second_prepared, Label::kNear);
5406 __ bind(&second_is_sequential);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005407 __ add(edx, Immediate(SeqOneByteString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005408 __ bind(&second_prepared);
5409
5410 // Push the addresses of both strings' first characters onto the stack.
5411 __ push(edx);
5412 __ push(eax);
5413
5414 Label non_ascii_string_add_flat_result, call_runtime_drop_two;
5415 // edi: instance type of second string
5416 // First string and second string have the same encoding.
5417 STATIC_ASSERT(kTwoByteStringTag == 0);
5418 __ test_b(edi, kStringEncodingMask);
5419 __ j(zero, &non_ascii_string_add_flat_result);
5420
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005421 // Both strings are ASCII strings.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005422 // ebx: length of resulting flat string as a smi
5423 __ SmiUntag(ebx);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005424 __ AllocateAsciiString(eax, ebx, ecx, edx, edi, &call_runtime_drop_two);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005425 // eax: result string
5426 __ mov(ecx, eax);
5427 // Locate first character of result.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005428 __ add(ecx, Immediate(SeqOneByteString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005429 // Load first argument's length and first character location. Account for
5430 // values currently on the stack when fetching arguments from it.
5431 __ mov(edx, Operand(esp, 4 * kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005432 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
5433 __ SmiUntag(edi);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005434 __ pop(edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005435 // eax: result string
5436 // ecx: first character of result
5437 // edx: first char of first argument
5438 // edi: length of first argument
5439 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005440 // Load second argument's length and first character location. Account for
5441 // values currently on the stack when fetching arguments from it.
5442 __ mov(edx, Operand(esp, 2 * kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005443 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
5444 __ SmiUntag(edi);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005445 __ pop(edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005446 // eax: result string
5447 // ecx: next character of result
5448 // edx: first char of second argument
5449 // edi: length of second argument
5450 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005451 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005452 __ ret(2 * kPointerSize);
5453
5454 // Handle creating a flat two byte result.
5455 // eax: first string - known to be two byte
5456 // ebx: length of resulting flat string as a smi
5457 // edx: second string
5458 __ bind(&non_ascii_string_add_flat_result);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005459 // Both strings are two byte strings.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005460 __ SmiUntag(ebx);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005461 __ AllocateTwoByteString(eax, ebx, ecx, edx, edi, &call_runtime_drop_two);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005462 // eax: result string
5463 __ mov(ecx, eax);
5464 // Locate first character of result.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005465 __ add(ecx, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
5466 // Load second argument's length and first character location. Account for
5467 // values currently on the stack when fetching arguments from it.
5468 __ mov(edx, Operand(esp, 4 * kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005469 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
5470 __ SmiUntag(edi);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005471 __ pop(edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005472 // eax: result string
5473 // ecx: first character of result
5474 // edx: first char of first argument
5475 // edi: length of first argument
5476 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005477 // Load second argument's length and first character location. Account for
5478 // values currently on the stack when fetching arguments from it.
5479 __ mov(edx, Operand(esp, 2 * kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005480 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
5481 __ SmiUntag(edi);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005482 __ pop(edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005483 // eax: result string
5484 // ecx: next character of result
5485 // edx: first char of second argument
5486 // edi: length of second argument
5487 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005488 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005489 __ ret(2 * kPointerSize);
5490
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005491 // Recover stack pointer before jumping to runtime.
5492 __ bind(&call_runtime_drop_two);
5493 __ Drop(2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005494 // Just jump to runtime to add the two strings.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005495 __ bind(&call_runtime);
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +00005496 if ((flags_ & STRING_ADD_ERECT_FRAME) != 0) {
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00005497 GenerateRegisterArgsPop(masm, ecx);
5498 // Build a frame
5499 {
5500 FrameScope scope(masm, StackFrame::INTERNAL);
5501 GenerateRegisterArgsPush(masm);
5502 __ CallRuntime(Runtime::kStringAdd, 2);
5503 }
5504 __ ret(0);
5505 } else {
5506 __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
5507 }
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005508
5509 if (call_builtin.is_linked()) {
5510 __ bind(&call_builtin);
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +00005511 if ((flags_ & STRING_ADD_ERECT_FRAME) != 0) {
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00005512 GenerateRegisterArgsPop(masm, ecx);
5513 // Build a frame
5514 {
5515 FrameScope scope(masm, StackFrame::INTERNAL);
5516 GenerateRegisterArgsPush(masm);
5517 __ InvokeBuiltin(builtin_id, CALL_FUNCTION);
5518 }
5519 __ ret(0);
5520 } else {
5521 __ InvokeBuiltin(builtin_id, JUMP_FUNCTION);
5522 }
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005523 }
5524}
5525
5526
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00005527void StringAddStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
5528 __ push(eax);
5529 __ push(edx);
5530}
5531
5532
5533void StringAddStub::GenerateRegisterArgsPop(MacroAssembler* masm,
5534 Register temp) {
5535 __ pop(temp);
5536 __ pop(edx);
5537 __ pop(eax);
5538 __ push(temp);
5539}
5540
5541
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005542void StringAddStub::GenerateConvertArgument(MacroAssembler* masm,
5543 int stack_offset,
5544 Register arg,
5545 Register scratch1,
5546 Register scratch2,
5547 Register scratch3,
5548 Label* slow) {
5549 // First check if the argument is already a string.
5550 Label not_string, done;
whesse@chromium.org7b260152011-06-20 15:33:18 +00005551 __ JumpIfSmi(arg, &not_string);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005552 __ CmpObjectType(arg, FIRST_NONSTRING_TYPE, scratch1);
5553 __ j(below, &done);
5554
5555 // Check the number to string cache.
5556 Label not_cached;
5557 __ bind(&not_string);
5558 // Puts the cached result into scratch1.
5559 NumberToStringStub::GenerateLookupNumberStringCache(masm,
5560 arg,
5561 scratch1,
5562 scratch2,
5563 scratch3,
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005564 &not_cached);
5565 __ mov(arg, scratch1);
5566 __ mov(Operand(esp, stack_offset), arg);
5567 __ jmp(&done);
5568
5569 // Check if the argument is a safe string wrapper.
5570 __ bind(&not_cached);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005571 __ JumpIfSmi(arg, slow);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005572 __ CmpObjectType(arg, JS_VALUE_TYPE, scratch1); // map -> scratch1.
5573 __ j(not_equal, slow);
5574 __ test_b(FieldOperand(scratch1, Map::kBitField2Offset),
5575 1 << Map::kStringWrapperSafeForDefaultValueOf);
5576 __ j(zero, slow);
5577 __ mov(arg, FieldOperand(arg, JSValue::kValueOffset));
5578 __ mov(Operand(esp, stack_offset), arg);
5579
5580 __ bind(&done);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005581}
5582
5583
5584void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
5585 Register dest,
5586 Register src,
5587 Register count,
5588 Register scratch,
5589 bool ascii) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005590 Label loop;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005591 __ bind(&loop);
5592 // This loop just copies one character at a time, as it is only used for very
5593 // short strings.
5594 if (ascii) {
5595 __ mov_b(scratch, Operand(src, 0));
5596 __ mov_b(Operand(dest, 0), scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005597 __ add(src, Immediate(1));
5598 __ add(dest, Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005599 } else {
5600 __ mov_w(scratch, Operand(src, 0));
5601 __ mov_w(Operand(dest, 0), scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005602 __ add(src, Immediate(2));
5603 __ add(dest, Immediate(2));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005604 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005605 __ sub(count, Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005606 __ j(not_zero, &loop);
5607}
5608
5609
5610void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm,
5611 Register dest,
5612 Register src,
5613 Register count,
5614 Register scratch,
5615 bool ascii) {
5616 // Copy characters using rep movs of doublewords.
5617 // The destination is aligned on a 4 byte boundary because we are
5618 // copying to the beginning of a newly allocated string.
5619 ASSERT(dest.is(edi)); // rep movs destination
5620 ASSERT(src.is(esi)); // rep movs source
5621 ASSERT(count.is(ecx)); // rep movs count
5622 ASSERT(!scratch.is(dest));
5623 ASSERT(!scratch.is(src));
5624 ASSERT(!scratch.is(count));
5625
5626 // Nothing to do for zero characters.
5627 Label done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005628 __ test(count, count);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005629 __ j(zero, &done);
5630
5631 // Make count the number of bytes to copy.
5632 if (!ascii) {
5633 __ shl(count, 1);
5634 }
5635
5636 // Don't enter the rep movs if there are less than 4 bytes to copy.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005637 Label last_bytes;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005638 __ test(count, Immediate(~3));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005639 __ j(zero, &last_bytes, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005640
5641 // Copy from edi to esi using rep movs instruction.
5642 __ mov(scratch, count);
5643 __ sar(count, 2); // Number of doublewords to copy.
5644 __ cld();
5645 __ rep_movs();
5646
5647 // Find number of bytes left.
5648 __ mov(count, scratch);
5649 __ and_(count, 3);
5650
5651 // Check if there are more bytes to copy.
5652 __ bind(&last_bytes);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005653 __ test(count, count);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005654 __ j(zero, &done);
5655
5656 // Copy remaining characters.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005657 Label loop;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005658 __ bind(&loop);
5659 __ mov_b(scratch, Operand(src, 0));
5660 __ mov_b(Operand(dest, 0), scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005661 __ add(src, Immediate(1));
5662 __ add(dest, Immediate(1));
5663 __ sub(count, Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005664 __ j(not_zero, &loop);
5665
5666 __ bind(&done);
5667}
5668
5669
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005670void StringHelper::GenerateTwoCharacterStringTableProbe(MacroAssembler* masm,
ricow@chromium.org65fae842010-08-25 15:26:24 +00005671 Register c1,
5672 Register c2,
5673 Register scratch1,
5674 Register scratch2,
5675 Register scratch3,
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005676 Label* not_probed,
ricow@chromium.org65fae842010-08-25 15:26:24 +00005677 Label* not_found) {
5678 // Register scratch3 is the general scratch register in this function.
5679 Register scratch = scratch3;
5680
5681 // Make sure that both characters are not digits as such strings has a
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005682 // different hash algorithm. Don't try to look for these in the string table.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005683 Label not_array_index;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005684 __ mov(scratch, c1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005685 __ sub(scratch, Immediate(static_cast<int>('0')));
5686 __ cmp(scratch, Immediate(static_cast<int>('9' - '0')));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005687 __ j(above, &not_array_index, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005688 __ mov(scratch, c2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005689 __ sub(scratch, Immediate(static_cast<int>('0')));
5690 __ cmp(scratch, Immediate(static_cast<int>('9' - '0')));
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00005691 __ j(below_equal, not_probed);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005692
5693 __ bind(&not_array_index);
5694 // Calculate the two character string hash.
5695 Register hash = scratch1;
5696 GenerateHashInit(masm, hash, c1, scratch);
5697 GenerateHashAddCharacter(masm, hash, c2, scratch);
5698 GenerateHashGetHash(masm, hash, scratch);
5699
5700 // Collect the two characters in a register.
5701 Register chars = c1;
5702 __ shl(c2, kBitsPerByte);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005703 __ or_(chars, c2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005704
5705 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
5706 // hash: hash of two character string.
5707
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005708 // Load the string table.
5709 Register string_table = c2;
danno@chromium.org59400602013-08-13 17:09:37 +00005710 __ LoadRoot(string_table, Heap::kStringTableRootIndex);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005711
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005712 // Calculate capacity mask from the string table capacity.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005713 Register mask = scratch2;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005714 __ mov(mask, FieldOperand(string_table, StringTable::kCapacityOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005715 __ SmiUntag(mask);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005716 __ sub(mask, Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005717
5718 // Registers
5719 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
5720 // hash: hash of two character string
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005721 // string_table: string table
ricow@chromium.org65fae842010-08-25 15:26:24 +00005722 // mask: capacity mask
5723 // scratch: -
5724
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005725 // Perform a number of probes in the string table.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005726 static const int kProbes = 4;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005727 Label found_in_string_table;
ricow@chromium.org65fae842010-08-25 15:26:24 +00005728 Label next_probe[kProbes], next_probe_pop_mask[kProbes];
danno@chromium.org2c456792011-11-11 12:00:53 +00005729 Register candidate = scratch; // Scratch register contains candidate.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005730 for (int i = 0; i < kProbes; i++) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005731 // Calculate entry in string table.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005732 __ mov(scratch, hash);
5733 if (i > 0) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005734 __ add(scratch, Immediate(StringTable::GetProbeOffset(i)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005735 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005736 __ and_(scratch, mask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005737
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005738 // Load the entry from the string table.
5739 STATIC_ASSERT(StringTable::kEntrySize == 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005740 __ mov(candidate,
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005741 FieldOperand(string_table,
ricow@chromium.org65fae842010-08-25 15:26:24 +00005742 scratch,
5743 times_pointer_size,
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005744 StringTable::kElementsStartOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005745
5746 // If entry is undefined no string with this hash can be found.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00005747 Factory* factory = masm->isolate()->factory();
5748 __ cmp(candidate, factory->undefined_value());
ricow@chromium.org65fae842010-08-25 15:26:24 +00005749 __ j(equal, not_found);
danno@chromium.org2c456792011-11-11 12:00:53 +00005750 __ cmp(candidate, factory->the_hole_value());
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +00005751 __ j(equal, &next_probe[i]);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005752
5753 // If length is not 2 the string is not a candidate.
5754 __ cmp(FieldOperand(candidate, String::kLengthOffset),
5755 Immediate(Smi::FromInt(2)));
5756 __ j(not_equal, &next_probe[i]);
5757
5758 // As we are out of registers save the mask on the stack and use that
5759 // register as a temporary.
5760 __ push(mask);
5761 Register temp = mask;
5762
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005763 // Check that the candidate is a non-external ASCII string.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005764 __ mov(temp, FieldOperand(candidate, HeapObject::kMapOffset));
5765 __ movzx_b(temp, FieldOperand(temp, Map::kInstanceTypeOffset));
5766 __ JumpIfInstanceTypeIsNotSequentialAscii(
5767 temp, temp, &next_probe_pop_mask[i]);
5768
5769 // Check if the two characters match.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005770 __ mov(temp, FieldOperand(candidate, SeqOneByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005771 __ and_(temp, 0x0000ffff);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005772 __ cmp(chars, temp);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005773 __ j(equal, &found_in_string_table);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005774 __ bind(&next_probe_pop_mask[i]);
5775 __ pop(mask);
5776 __ bind(&next_probe[i]);
5777 }
5778
5779 // No matching 2 character string found by probing.
5780 __ jmp(not_found);
5781
5782 // Scratch register contains result when we fall through to here.
danno@chromium.org2c456792011-11-11 12:00:53 +00005783 Register result = candidate;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005784 __ bind(&found_in_string_table);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005785 __ pop(mask); // Pop saved mask from the stack.
5786 if (!result.is(eax)) {
5787 __ mov(eax, result);
5788 }
5789}
5790
5791
5792void StringHelper::GenerateHashInit(MacroAssembler* masm,
5793 Register hash,
5794 Register character,
5795 Register scratch) {
rossberg@chromium.orgfab14982012-01-05 15:02:15 +00005796 // hash = (seed + character) + ((seed + character) << 10);
5797 if (Serializer::enabled()) {
danno@chromium.org59400602013-08-13 17:09:37 +00005798 __ LoadRoot(scratch, Heap::kHashSeedRootIndex);
ulan@chromium.org2efb9002012-01-19 15:36:35 +00005799 __ SmiUntag(scratch);
rossberg@chromium.orgfab14982012-01-05 15:02:15 +00005800 __ add(scratch, character);
5801 __ mov(hash, scratch);
5802 __ shl(scratch, 10);
5803 __ add(hash, scratch);
5804 } else {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00005805 int32_t seed = masm->isolate()->heap()->HashSeed();
rossberg@chromium.orgfab14982012-01-05 15:02:15 +00005806 __ lea(scratch, Operand(character, seed));
5807 __ shl(scratch, 10);
5808 __ lea(hash, Operand(scratch, character, times_1, seed));
5809 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00005810 // hash ^= hash >> 6;
5811 __ mov(scratch, hash);
danno@chromium.org2c456792011-11-11 12:00:53 +00005812 __ shr(scratch, 6);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005813 __ xor_(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005814}
5815
5816
5817void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
5818 Register hash,
5819 Register character,
5820 Register scratch) {
5821 // hash += character;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005822 __ add(hash, character);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005823 // hash += hash << 10;
5824 __ mov(scratch, hash);
5825 __ shl(scratch, 10);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005826 __ add(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005827 // hash ^= hash >> 6;
5828 __ mov(scratch, hash);
danno@chromium.org2c456792011-11-11 12:00:53 +00005829 __ shr(scratch, 6);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005830 __ xor_(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005831}
5832
5833
5834void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
5835 Register hash,
5836 Register scratch) {
5837 // hash += hash << 3;
5838 __ mov(scratch, hash);
5839 __ shl(scratch, 3);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005840 __ add(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005841 // hash ^= hash >> 11;
5842 __ mov(scratch, hash);
danno@chromium.org2c456792011-11-11 12:00:53 +00005843 __ shr(scratch, 11);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005844 __ xor_(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005845 // hash += hash << 15;
5846 __ mov(scratch, hash);
5847 __ shl(scratch, 15);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005848 __ add(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005849
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00005850 __ and_(hash, String::kHashBitMask);
danno@chromium.org2c456792011-11-11 12:00:53 +00005851
ricow@chromium.org65fae842010-08-25 15:26:24 +00005852 // if (hash == 0) hash = 27;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005853 Label hash_not_zero;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00005854 __ j(not_zero, &hash_not_zero, Label::kNear);
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00005855 __ mov(hash, Immediate(StringHasher::kZeroHash));
ricow@chromium.org65fae842010-08-25 15:26:24 +00005856 __ bind(&hash_not_zero);
5857}
5858
5859
5860void SubStringStub::Generate(MacroAssembler* masm) {
5861 Label runtime;
5862
5863 // Stack frame on entry.
5864 // esp[0]: return address
5865 // esp[4]: to
5866 // esp[8]: from
5867 // esp[12]: string
5868
5869 // Make sure first argument is a string.
5870 __ mov(eax, Operand(esp, 3 * kPointerSize));
5871 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00005872 __ JumpIfSmi(eax, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005873 Condition is_string = masm->IsObjectStringType(eax, ebx, ebx);
5874 __ j(NegateCondition(is_string), &runtime);
5875
5876 // eax: string
5877 // ebx: instance type
5878
5879 // Calculate length of sub string using the smi values.
ricow@chromium.org65fae842010-08-25 15:26:24 +00005880 __ mov(ecx, Operand(esp, 1 * kPointerSize)); // To index.
whesse@chromium.org7b260152011-06-20 15:33:18 +00005881 __ JumpIfNotSmi(ecx, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005882 __ mov(edx, Operand(esp, 2 * kPointerSize)); // From index.
whesse@chromium.org7b260152011-06-20 15:33:18 +00005883 __ JumpIfNotSmi(edx, &runtime);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005884 __ sub(ecx, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005885 __ cmp(ecx, FieldOperand(eax, String::kLengthOffset));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005886 Label not_original_string;
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00005887 // Shorter than original string's length: an actual substring.
5888 __ j(below, &not_original_string, Label::kNear);
5889 // Longer than original string's length or negative: unsafe arguments.
5890 __ j(above, &runtime);
5891 // Return original string.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005892 Counters* counters = masm->isolate()->counters();
5893 __ IncrementCounter(counters->sub_string_native(), 1);
5894 __ ret(3 * kPointerSize);
5895 __ bind(&not_original_string);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005896
ulan@chromium.org2e04b582013-02-21 14:06:02 +00005897 Label single_char;
5898 __ cmp(ecx, Immediate(Smi::FromInt(1)));
5899 __ j(equal, &single_char);
5900
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005901 // eax: string
5902 // ebx: instance type
5903 // ecx: sub string length (smi)
5904 // edx: from index (smi)
5905 // Deal with different string types: update the index if necessary
5906 // and put the underlying string into edi.
5907 Label underlying_unpacked, sliced_string, seq_or_external_string;
5908 // If the string is not indirect, it can only be sequential or external.
5909 STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
5910 STATIC_ASSERT(kIsIndirectStringMask != 0);
5911 __ test(ebx, Immediate(kIsIndirectStringMask));
5912 __ j(zero, &seq_or_external_string, Label::kNear);
5913
5914 Factory* factory = masm->isolate()->factory();
5915 __ test(ebx, Immediate(kSlicedNotConsMask));
5916 __ j(not_zero, &sliced_string, Label::kNear);
5917 // Cons string. Check whether it is flat, then fetch first part.
5918 // Flat cons strings have an empty second part.
5919 __ cmp(FieldOperand(eax, ConsString::kSecondOffset),
5920 factory->empty_string());
5921 __ j(not_equal, &runtime);
5922 __ mov(edi, FieldOperand(eax, ConsString::kFirstOffset));
5923 // Update instance type.
5924 __ mov(ebx, FieldOperand(edi, HeapObject::kMapOffset));
5925 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
5926 __ jmp(&underlying_unpacked, Label::kNear);
5927
5928 __ bind(&sliced_string);
5929 // Sliced string. Fetch parent and adjust start index by offset.
5930 __ add(edx, FieldOperand(eax, SlicedString::kOffsetOffset));
5931 __ mov(edi, FieldOperand(eax, SlicedString::kParentOffset));
5932 // Update instance type.
5933 __ mov(ebx, FieldOperand(edi, HeapObject::kMapOffset));
5934 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
5935 __ jmp(&underlying_unpacked, Label::kNear);
5936
5937 __ bind(&seq_or_external_string);
5938 // Sequential or external string. Just move string to the expected register.
5939 __ mov(edi, eax);
5940
5941 __ bind(&underlying_unpacked);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005942
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00005943 if (FLAG_string_slices) {
5944 Label copy_routine;
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00005945 // edi: underlying subject string
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005946 // ebx: instance type of underlying subject string
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005947 // edx: adjusted start index (smi)
5948 // ecx: length (smi)
5949 __ cmp(ecx, Immediate(Smi::FromInt(SlicedString::kMinLength)));
5950 // Short slice. Copy instead of slicing.
5951 __ j(less, &copy_routine);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00005952 // Allocate new sliced string. At this point we do not reload the instance
5953 // type including the string encoding because we simply rely on the info
5954 // provided by the original string. It does not matter if the original
5955 // string's encoding is wrong because we always have to recheck encoding of
5956 // the newly created string's parent anyways due to externalized strings.
5957 Label two_byte_slice, set_slice_header;
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +00005958 STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00005959 STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
5960 __ test(ebx, Immediate(kStringEncodingMask));
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00005961 __ j(zero, &two_byte_slice, Label::kNear);
5962 __ AllocateAsciiSlicedString(eax, ebx, no_reg, &runtime);
5963 __ jmp(&set_slice_header, Label::kNear);
5964 __ bind(&two_byte_slice);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00005965 __ AllocateTwoByteSlicedString(eax, ebx, no_reg, &runtime);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00005966 __ bind(&set_slice_header);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00005967 __ mov(FieldOperand(eax, SlicedString::kLengthOffset), ecx);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00005968 __ mov(FieldOperand(eax, SlicedString::kHashFieldOffset),
5969 Immediate(String::kEmptyHashField));
erik.corry@gmail.combbceb572012-03-09 10:52:05 +00005970 __ mov(FieldOperand(eax, SlicedString::kParentOffset), edi);
5971 __ mov(FieldOperand(eax, SlicedString::kOffsetOffset), edx);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005972 __ IncrementCounter(counters->sub_string_native(), 1);
5973 __ ret(3 * kPointerSize);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00005974
5975 __ bind(&copy_routine);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00005976 }
5977
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005978 // edi: underlying subject string
ricow@chromium.org7ad65222011-12-19 12:13:11 +00005979 // ebx: instance type of underlying subject string
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005980 // edx: adjusted start index (smi)
5981 // ecx: length (smi)
5982 // The subject string can only be external or sequential string of either
5983 // encoding at this point.
5984 Label two_byte_sequential, runtime_drop_two, sequential_string;
5985 STATIC_ASSERT(kExternalStringTag != 0);
5986 STATIC_ASSERT(kSeqStringTag == 0);
5987 __ test_b(ebx, kExternalStringTag);
5988 __ j(zero, &sequential_string);
ricow@chromium.org65fae842010-08-25 15:26:24 +00005989
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005990 // Handle external string.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005991 // Rule out short external strings.
5992 STATIC_CHECK(kShortExternalStringTag != 0);
5993 __ test_b(ebx, kShortExternalStringMask);
5994 __ j(not_zero, &runtime);
5995 __ mov(edi, FieldOperand(edi, ExternalString::kResourceDataOffset));
5996 // Move the pointer so that offset-wise, it looks like a sequential string.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00005997 STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005998 __ sub(edi, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
5999
6000 __ bind(&sequential_string);
6001 // Stash away (adjusted) index and (underlying) string.
6002 __ push(edx);
6003 __ push(edi);
6004 __ SmiUntag(ecx);
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +00006005 STATIC_ASSERT((kOneByteStringTag & kStringEncodingMask) != 0);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006006 __ test_b(ebx, kStringEncodingMask);
6007 __ j(zero, &two_byte_sequential);
6008
ulan@chromium.org2efb9002012-01-19 15:36:35 +00006009 // Sequential ASCII string. Allocate the result.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006010 __ AllocateAsciiString(eax, ecx, ebx, edx, edi, &runtime_drop_two);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006011
6012 // eax: result string
6013 // ecx: result string length
6014 __ mov(edx, esi); // esi used by following code.
6015 // Locate first character of result.
6016 __ mov(edi, eax);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006017 __ add(edi, Immediate(SeqOneByteString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006018 // Load string argument and locate character of sub string start.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006019 __ pop(esi);
6020 __ pop(ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006021 __ SmiUntag(ebx);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006022 __ lea(esi, FieldOperand(esi, ebx, times_1, SeqOneByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006023
6024 // eax: result string
6025 // ecx: result length
6026 // edx: original value of esi
6027 // edi: first character of result
6028 // esi: character of sub string start
6029 StringHelper::GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, true);
6030 __ mov(esi, edx); // Restore esi.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006031 __ IncrementCounter(counters->sub_string_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006032 __ ret(3 * kPointerSize);
6033
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006034 __ bind(&two_byte_sequential);
6035 // Sequential two-byte string. Allocate the result.
6036 __ AllocateTwoByteString(eax, ecx, ebx, edx, edi, &runtime_drop_two);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006037
6038 // eax: result string
6039 // ecx: result string length
6040 __ mov(edx, esi); // esi used by following code.
6041 // Locate first character of result.
6042 __ mov(edi, eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006043 __ add(edi,
ricow@chromium.org65fae842010-08-25 15:26:24 +00006044 Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
6045 // Load string argument and locate character of sub string start.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006046 __ pop(esi);
6047 __ pop(ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006048 // As from is a smi it is 2 times the value which matches the size of a two
6049 // byte character.
6050 STATIC_ASSERT(kSmiTag == 0);
6051 STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006052 __ lea(esi, FieldOperand(esi, ebx, times_1, SeqTwoByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006053
6054 // eax: result string
6055 // ecx: result length
6056 // edx: original value of esi
6057 // edi: first character of result
6058 // esi: character of sub string start
6059 StringHelper::GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, false);
6060 __ mov(esi, edx); // Restore esi.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006061 __ IncrementCounter(counters->sub_string_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006062 __ ret(3 * kPointerSize);
6063
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006064 // Drop pushed values on the stack before tail call.
6065 __ bind(&runtime_drop_two);
6066 __ Drop(2);
6067
ricow@chromium.org65fae842010-08-25 15:26:24 +00006068 // Just jump to runtime to create the sub string.
6069 __ bind(&runtime);
6070 __ TailCallRuntime(Runtime::kSubString, 3, 1);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00006071
6072 __ bind(&single_char);
6073 // eax: string
6074 // ebx: instance type
6075 // ecx: sub string length (smi)
6076 // edx: from index (smi)
6077 StringCharAtGenerator generator(
6078 eax, edx, ecx, eax, &runtime, &runtime, &runtime, STRING_INDEX_IS_NUMBER);
6079 generator.GenerateFast(masm);
6080 __ ret(3 * kPointerSize);
6081 generator.SkipSlow(masm, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006082}
6083
6084
lrn@chromium.org1c092762011-05-09 09:42:16 +00006085void StringCompareStub::GenerateFlatAsciiStringEquals(MacroAssembler* masm,
6086 Register left,
6087 Register right,
6088 Register scratch1,
6089 Register scratch2) {
6090 Register length = scratch1;
6091
6092 // Compare lengths.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006093 Label strings_not_equal, check_zero_length;
lrn@chromium.org1c092762011-05-09 09:42:16 +00006094 __ mov(length, FieldOperand(left, String::kLengthOffset));
6095 __ cmp(length, FieldOperand(right, String::kLengthOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006096 __ j(equal, &check_zero_length, Label::kNear);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006097 __ bind(&strings_not_equal);
6098 __ Set(eax, Immediate(Smi::FromInt(NOT_EQUAL)));
6099 __ ret(0);
6100
6101 // Check if the length is zero.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006102 Label compare_chars;
lrn@chromium.org1c092762011-05-09 09:42:16 +00006103 __ bind(&check_zero_length);
6104 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006105 __ test(length, length);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006106 __ j(not_zero, &compare_chars, Label::kNear);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006107 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6108 __ ret(0);
6109
6110 // Compare characters.
6111 __ bind(&compare_chars);
6112 GenerateAsciiCharsCompareLoop(masm, left, right, length, scratch2,
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006113 &strings_not_equal, Label::kNear);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006114
6115 // Characters are equal.
6116 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6117 __ ret(0);
6118}
6119
6120
ricow@chromium.org65fae842010-08-25 15:26:24 +00006121void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
6122 Register left,
6123 Register right,
6124 Register scratch1,
6125 Register scratch2,
6126 Register scratch3) {
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006127 Counters* counters = masm->isolate()->counters();
6128 __ IncrementCounter(counters->string_compare_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006129
6130 // Find minimum length.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006131 Label left_shorter;
ricow@chromium.org65fae842010-08-25 15:26:24 +00006132 __ mov(scratch1, FieldOperand(left, String::kLengthOffset));
6133 __ mov(scratch3, scratch1);
6134 __ sub(scratch3, FieldOperand(right, String::kLengthOffset));
6135
6136 Register length_delta = scratch3;
6137
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006138 __ j(less_equal, &left_shorter, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006139 // Right string is shorter. Change scratch1 to be length of right string.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006140 __ sub(scratch1, length_delta);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006141 __ bind(&left_shorter);
6142
6143 Register min_length = scratch1;
6144
6145 // If either length is zero, just compare lengths.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006146 Label compare_lengths;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006147 __ test(min_length, min_length);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006148 __ j(zero, &compare_lengths, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006149
lrn@chromium.org1c092762011-05-09 09:42:16 +00006150 // Compare characters.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006151 Label result_not_equal;
lrn@chromium.org1c092762011-05-09 09:42:16 +00006152 GenerateAsciiCharsCompareLoop(masm, left, right, min_length, scratch2,
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006153 &result_not_equal, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006154
6155 // Compare lengths - strings up to min-length are equal.
6156 __ bind(&compare_lengths);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006157 __ test(length_delta, length_delta);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00006158 Label length_not_equal;
6159 __ j(not_zero, &length_not_equal, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006160
6161 // Result is EQUAL.
6162 STATIC_ASSERT(EQUAL == 0);
6163 STATIC_ASSERT(kSmiTag == 0);
6164 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6165 __ ret(0);
6166
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006167 Label result_greater;
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00006168 Label result_less;
6169 __ bind(&length_not_equal);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006170 __ j(greater, &result_greater, Label::kNear);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00006171 __ jmp(&result_less, Label::kNear);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00006172 __ bind(&result_not_equal);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00006173 __ j(above, &result_greater, Label::kNear);
6174 __ bind(&result_less);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006175
6176 // Result is LESS.
6177 __ Set(eax, Immediate(Smi::FromInt(LESS)));
6178 __ ret(0);
6179
6180 // Result is GREATER.
6181 __ bind(&result_greater);
6182 __ Set(eax, Immediate(Smi::FromInt(GREATER)));
6183 __ ret(0);
6184}
6185
6186
lrn@chromium.org1c092762011-05-09 09:42:16 +00006187void StringCompareStub::GenerateAsciiCharsCompareLoop(
6188 MacroAssembler* masm,
6189 Register left,
6190 Register right,
6191 Register length,
6192 Register scratch,
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006193 Label* chars_not_equal,
6194 Label::Distance chars_not_equal_near) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00006195 // Change index to run from -length to -1 by adding length to string
6196 // start. This means that loop ends when index reaches zero, which
6197 // doesn't need an additional compare.
6198 __ SmiUntag(length);
6199 __ lea(left,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006200 FieldOperand(left, length, times_1, SeqOneByteString::kHeaderSize));
lrn@chromium.org1c092762011-05-09 09:42:16 +00006201 __ lea(right,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006202 FieldOperand(right, length, times_1, SeqOneByteString::kHeaderSize));
lrn@chromium.org1c092762011-05-09 09:42:16 +00006203 __ neg(length);
6204 Register index = length; // index = -length;
6205
6206 // Compare loop.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006207 Label loop;
lrn@chromium.org1c092762011-05-09 09:42:16 +00006208 __ bind(&loop);
6209 __ mov_b(scratch, Operand(left, index, times_1, 0));
6210 __ cmpb(scratch, Operand(right, index, times_1, 0));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006211 __ j(not_equal, chars_not_equal, chars_not_equal_near);
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00006212 __ inc(index);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006213 __ j(not_zero, &loop);
6214}
6215
6216
ricow@chromium.org65fae842010-08-25 15:26:24 +00006217void StringCompareStub::Generate(MacroAssembler* masm) {
6218 Label runtime;
6219
6220 // Stack frame on entry.
6221 // esp[0]: return address
6222 // esp[4]: right string
6223 // esp[8]: left string
6224
6225 __ mov(edx, Operand(esp, 2 * kPointerSize)); // left
6226 __ mov(eax, Operand(esp, 1 * kPointerSize)); // right
6227
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006228 Label not_same;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006229 __ cmp(edx, eax);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006230 __ j(not_equal, &not_same, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006231 STATIC_ASSERT(EQUAL == 0);
6232 STATIC_ASSERT(kSmiTag == 0);
6233 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00006234 __ IncrementCounter(masm->isolate()->counters()->string_compare_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00006235 __ ret(2 * kPointerSize);
6236
6237 __ bind(&not_same);
6238
ulan@chromium.org2efb9002012-01-19 15:36:35 +00006239 // Check that both objects are sequential ASCII strings.
ricow@chromium.org65fae842010-08-25 15:26:24 +00006240 __ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx, &runtime);
6241
ulan@chromium.org2efb9002012-01-19 15:36:35 +00006242 // Compare flat ASCII strings.
ricow@chromium.org65fae842010-08-25 15:26:24 +00006243 // Drop arguments from the stack.
6244 __ pop(ecx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006245 __ add(esp, Immediate(2 * kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00006246 __ push(ecx);
6247 GenerateCompareFlatAsciiStrings(masm, edx, eax, ecx, ebx, edi);
6248
6249 // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
6250 // tagged as a small integer.
6251 __ bind(&runtime);
6252 __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
6253}
6254
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006255
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006256void ICCompareStub::GenerateSmis(MacroAssembler* masm) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006257 ASSERT(state_ == CompareIC::SMI);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006258 Label miss;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006259 __ mov(ecx, edx);
6260 __ or_(ecx, eax);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006261 __ JumpIfNotSmi(ecx, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006262
6263 if (GetCondition() == equal) {
6264 // For equality we do not care about the sign of the result.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006265 __ sub(eax, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006266 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006267 Label done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006268 __ sub(edx, eax);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006269 __ j(no_overflow, &done, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006270 // Correct sign of result in case of overflow.
6271 __ not_(edx);
6272 __ bind(&done);
6273 __ mov(eax, edx);
6274 }
6275 __ ret(0);
6276
6277 __ bind(&miss);
6278 GenerateMiss(masm);
6279}
6280
6281
hpayer@chromium.org8432c912013-02-28 15:55:26 +00006282void ICCompareStub::GenerateNumbers(MacroAssembler* masm) {
6283 ASSERT(state_ == CompareIC::NUMBER);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006284
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006285 Label generic_stub;
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00006286 Label unordered, maybe_undefined1, maybe_undefined2;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006287 Label miss;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006288
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006289 if (left_ == CompareIC::SMI) {
6290 __ JumpIfNotSmi(edx, &miss);
6291 }
6292 if (right_ == CompareIC::SMI) {
6293 __ JumpIfNotSmi(eax, &miss);
6294 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006295
6296 // Inlining the double comparison and falling back to the general compare
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006297 // stub if NaN is involved or SSE2 or CMOV is unsupported.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00006298 if (CpuFeatures::IsSupported(SSE2) && CpuFeatures::IsSupported(CMOV)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00006299 CpuFeatureScope scope1(masm, SSE2);
6300 CpuFeatureScope scope2(masm, CMOV);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006301
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006302 // Load left and right operand.
6303 Label done, left, left_smi, right_smi;
6304 __ JumpIfSmi(eax, &right_smi, Label::kNear);
6305 __ cmp(FieldOperand(eax, HeapObject::kMapOffset),
6306 masm->isolate()->factory()->heap_number_map());
6307 __ j(not_equal, &maybe_undefined1, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006308 __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006309 __ jmp(&left, Label::kNear);
6310 __ bind(&right_smi);
6311 __ mov(ecx, eax); // Can't clobber eax because we can still jump away.
6312 __ SmiUntag(ecx);
6313 __ cvtsi2sd(xmm1, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006314
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006315 __ bind(&left);
6316 __ JumpIfSmi(edx, &left_smi, Label::kNear);
6317 __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
6318 masm->isolate()->factory()->heap_number_map());
6319 __ j(not_equal, &maybe_undefined2, Label::kNear);
6320 __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
6321 __ jmp(&done);
6322 __ bind(&left_smi);
6323 __ mov(ecx, edx); // Can't clobber edx because we can still jump away.
6324 __ SmiUntag(ecx);
6325 __ cvtsi2sd(xmm0, ecx);
6326
6327 __ bind(&done);
6328 // Compare operands.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006329 __ ucomisd(xmm0, xmm1);
6330
6331 // Don't base result on EFLAGS when a NaN is involved.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00006332 __ j(parity_even, &unordered, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006333
6334 // Return a result of -1, 0, or 1, based on EFLAGS.
6335 // Performing mov, because xor would destroy the flag register.
6336 __ mov(eax, 0); // equal
6337 __ mov(ecx, Immediate(Smi::FromInt(1)));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006338 __ cmov(above, eax, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006339 __ mov(ecx, Immediate(Smi::FromInt(-1)));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006340 __ cmov(below, eax, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006341 __ ret(0);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006342 } else {
6343 __ mov(ecx, edx);
6344 __ and_(ecx, eax);
6345 __ JumpIfSmi(ecx, &generic_stub, Label::kNear);
6346
6347 __ cmp(FieldOperand(eax, HeapObject::kMapOffset),
6348 masm->isolate()->factory()->heap_number_map());
6349 __ j(not_equal, &maybe_undefined1, Label::kNear);
6350 __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
6351 masm->isolate()->factory()->heap_number_map());
6352 __ j(not_equal, &maybe_undefined2, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006353 }
6354
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00006355 __ bind(&unordered);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006356 __ bind(&generic_stub);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006357 ICCompareStub stub(op_, CompareIC::GENERIC, CompareIC::GENERIC,
6358 CompareIC::GENERIC);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00006359 __ jmp(stub.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006360
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00006361 __ bind(&maybe_undefined1);
6362 if (Token::IsOrderedRelationalCompareOp(op_)) {
6363 __ cmp(eax, Immediate(masm->isolate()->factory()->undefined_value()));
6364 __ j(not_equal, &miss);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006365 __ JumpIfSmi(edx, &unordered);
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00006366 __ CmpObjectType(edx, HEAP_NUMBER_TYPE, ecx);
6367 __ j(not_equal, &maybe_undefined2, Label::kNear);
6368 __ jmp(&unordered);
6369 }
6370
6371 __ bind(&maybe_undefined2);
6372 if (Token::IsOrderedRelationalCompareOp(op_)) {
6373 __ cmp(edx, Immediate(masm->isolate()->factory()->undefined_value()));
6374 __ j(equal, &unordered);
6375 }
6376
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006377 __ bind(&miss);
6378 GenerateMiss(masm);
6379}
6380
6381
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006382void ICCompareStub::GenerateInternalizedStrings(MacroAssembler* masm) {
6383 ASSERT(state_ == CompareIC::INTERNALIZED_STRING);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006384 ASSERT(GetCondition() == equal);
6385
6386 // Registers containing left and right operands respectively.
6387 Register left = edx;
6388 Register right = eax;
6389 Register tmp1 = ecx;
6390 Register tmp2 = ebx;
6391
6392 // Check that both operands are heap objects.
6393 Label miss;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006394 __ mov(tmp1, left);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006395 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006396 __ and_(tmp1, right);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006397 __ JumpIfSmi(tmp1, &miss, Label::kNear);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006398
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006399 // Check that both operands are internalized strings.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006400 __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset));
6401 __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset));
6402 __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
6403 __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +00006404 STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0);
6405 __ or_(tmp1, tmp2);
6406 __ test(tmp1, Immediate(kIsNotStringMask | kIsNotInternalizedMask));
6407 __ j(not_zero, &miss, Label::kNear);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006408
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006409 // Internalized strings are compared by identity.
6410 Label done;
6411 __ cmp(left, right);
6412 // Make sure eax is non-zero. At this point input operands are
6413 // guaranteed to be non-zero.
6414 ASSERT(right.is(eax));
6415 __ j(not_equal, &done, Label::kNear);
6416 STATIC_ASSERT(EQUAL == 0);
6417 STATIC_ASSERT(kSmiTag == 0);
6418 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6419 __ bind(&done);
6420 __ ret(0);
6421
6422 __ bind(&miss);
6423 GenerateMiss(masm);
6424}
6425
6426
6427void ICCompareStub::GenerateUniqueNames(MacroAssembler* masm) {
6428 ASSERT(state_ == CompareIC::UNIQUE_NAME);
6429 ASSERT(GetCondition() == equal);
6430
6431 // Registers containing left and right operands respectively.
6432 Register left = edx;
6433 Register right = eax;
6434 Register tmp1 = ecx;
6435 Register tmp2 = ebx;
6436
6437 // Check that both operands are heap objects.
6438 Label miss;
6439 __ mov(tmp1, left);
6440 STATIC_ASSERT(kSmiTag == 0);
6441 __ and_(tmp1, right);
6442 __ JumpIfSmi(tmp1, &miss, Label::kNear);
6443
6444 // Check that both operands are unique names. This leaves the instance
6445 // types loaded in tmp1 and tmp2.
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006446 __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset));
6447 __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset));
6448 __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
6449 __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
6450
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00006451 __ JumpIfNotUniqueName(tmp1, &miss, Label::kNear);
6452 __ JumpIfNotUniqueName(tmp2, &miss, Label::kNear);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006453
6454 // Unique names are compared by identity.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006455 Label done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006456 __ cmp(left, right);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006457 // Make sure eax is non-zero. At this point input operands are
6458 // guaranteed to be non-zero.
6459 ASSERT(right.is(eax));
6460 __ j(not_equal, &done, Label::kNear);
6461 STATIC_ASSERT(EQUAL == 0);
6462 STATIC_ASSERT(kSmiTag == 0);
6463 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6464 __ bind(&done);
6465 __ ret(0);
6466
6467 __ bind(&miss);
6468 GenerateMiss(masm);
6469}
6470
6471
lrn@chromium.org1c092762011-05-09 09:42:16 +00006472void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006473 ASSERT(state_ == CompareIC::STRING);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006474 Label miss;
6475
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00006476 bool equality = Token::IsEqualityOp(op_);
6477
lrn@chromium.org1c092762011-05-09 09:42:16 +00006478 // Registers containing left and right operands respectively.
6479 Register left = edx;
6480 Register right = eax;
6481 Register tmp1 = ecx;
6482 Register tmp2 = ebx;
6483 Register tmp3 = edi;
6484
6485 // Check that both operands are heap objects.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006486 __ mov(tmp1, left);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006487 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006488 __ and_(tmp1, right);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006489 __ JumpIfSmi(tmp1, &miss);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006490
6491 // Check that both operands are strings. This leaves the instance
6492 // types loaded in tmp1 and tmp2.
6493 __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset));
6494 __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset));
6495 __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
6496 __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
6497 __ mov(tmp3, tmp1);
6498 STATIC_ASSERT(kNotStringTag != 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006499 __ or_(tmp3, tmp2);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006500 __ test(tmp3, Immediate(kIsNotStringMask));
6501 __ j(not_zero, &miss);
6502
6503 // Fast check for identical strings.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006504 Label not_same;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006505 __ cmp(left, right);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006506 __ j(not_equal, &not_same, Label::kNear);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006507 STATIC_ASSERT(EQUAL == 0);
6508 STATIC_ASSERT(kSmiTag == 0);
6509 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
6510 __ ret(0);
6511
6512 // Handle not identical strings.
6513 __ bind(&not_same);
6514
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006515 // Check that both strings are internalized. If they are, we're done
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00006516 // because we already know they are not identical. But in the case of
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00006517 // non-equality compare, we still need to determine the order. We
6518 // also know they are both strings.
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00006519 if (equality) {
6520 Label do_compare;
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +00006521 STATIC_ASSERT(kInternalizedTag == 0);
6522 __ or_(tmp1, tmp2);
6523 __ test(tmp1, Immediate(kIsNotInternalizedMask));
6524 __ j(not_zero, &do_compare, Label::kNear);
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00006525 // Make sure eax is non-zero. At this point input operands are
6526 // guaranteed to be non-zero.
6527 ASSERT(right.is(eax));
6528 __ ret(0);
6529 __ bind(&do_compare);
6530 }
lrn@chromium.org1c092762011-05-09 09:42:16 +00006531
6532 // Check that both strings are sequential ASCII.
6533 Label runtime;
lrn@chromium.org1c092762011-05-09 09:42:16 +00006534 __ JumpIfNotBothSequentialAsciiStrings(left, right, tmp1, tmp2, &runtime);
6535
6536 // Compare flat ASCII strings. Returns when done.
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00006537 if (equality) {
6538 StringCompareStub::GenerateFlatAsciiStringEquals(
6539 masm, left, right, tmp1, tmp2);
6540 } else {
6541 StringCompareStub::GenerateCompareFlatAsciiStrings(
6542 masm, left, right, tmp1, tmp2, tmp3);
6543 }
lrn@chromium.org1c092762011-05-09 09:42:16 +00006544
6545 // Handle more complex cases in runtime.
6546 __ bind(&runtime);
6547 __ pop(tmp1); // Return address.
6548 __ push(left);
6549 __ push(right);
6550 __ push(tmp1);
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00006551 if (equality) {
6552 __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
6553 } else {
6554 __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
6555 }
lrn@chromium.org1c092762011-05-09 09:42:16 +00006556
6557 __ bind(&miss);
6558 GenerateMiss(masm);
6559}
6560
6561
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006562void ICCompareStub::GenerateObjects(MacroAssembler* masm) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00006563 ASSERT(state_ == CompareIC::OBJECT);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00006564 Label miss;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006565 __ mov(ecx, edx);
6566 __ and_(ecx, eax);
whesse@chromium.org7b260152011-06-20 15:33:18 +00006567 __ JumpIfSmi(ecx, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006568
6569 __ CmpObjectType(eax, JS_OBJECT_TYPE, ecx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00006570 __ j(not_equal, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006571 __ CmpObjectType(edx, JS_OBJECT_TYPE, ecx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00006572 __ j(not_equal, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006573
6574 ASSERT(GetCondition() == equal);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006575 __ sub(eax, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006576 __ ret(0);
6577
6578 __ bind(&miss);
6579 GenerateMiss(masm);
6580}
6581
6582
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006583void ICCompareStub::GenerateKnownObjects(MacroAssembler* masm) {
6584 Label miss;
6585 __ mov(ecx, edx);
6586 __ and_(ecx, eax);
6587 __ JumpIfSmi(ecx, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006588
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006589 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
6590 __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
6591 __ cmp(ecx, known_map_);
6592 __ j(not_equal, &miss, Label::kNear);
6593 __ cmp(ebx, known_map_);
6594 __ j(not_equal, &miss, Label::kNear);
6595
6596 __ sub(eax, edx);
6597 __ ret(0);
6598
6599 __ bind(&miss);
6600 GenerateMiss(masm);
6601}
6602
6603
6604void ICCompareStub::GenerateMiss(MacroAssembler* masm) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006605 {
6606 // Call the runtime system in a fresh internal frame.
6607 ExternalReference miss = ExternalReference(IC_Utility(IC::kCompareIC_Miss),
6608 masm->isolate());
6609 FrameScope scope(masm, StackFrame::INTERNAL);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006610 __ push(edx); // Preserve edx and eax.
6611 __ push(eax);
6612 __ push(edx); // And also use them as the arguments.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006613 __ push(eax);
6614 __ push(Immediate(Smi::FromInt(op_)));
6615 __ CallExternalReference(miss, 3);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00006616 // Compute the entry point of the rewritten stub.
6617 __ lea(edi, FieldOperand(eax, Code::kHeaderSize));
6618 __ pop(eax);
6619 __ pop(edx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006620 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006621
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006622 // Do a tail call to the rewritten stub.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006623 __ jmp(edi);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00006624}
6625
6626
lrn@chromium.org1c092762011-05-09 09:42:16 +00006627// Helper function used to check that the dictionary doesn't contain
6628// the property. This function may return false negatives, so miss_label
6629// must always call a backup property check that is complete.
6630// This function is safe to call if the receiver has fast properties.
ulan@chromium.org750145a2013-03-07 15:14:13 +00006631// Name must be a unique name and receiver must be a heap object.
6632void NameDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm,
6633 Label* miss,
6634 Label* done,
6635 Register properties,
6636 Handle<Name> name,
6637 Register r0) {
6638 ASSERT(name->IsUniqueName());
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006639
6640 // If names of slots in range from 1 to kProbes - 1 for the hash value are
6641 // not equal to the name and kProbes-th slot is not used (its name is the
6642 // undefined value), it guarantees the hash table doesn't contain the
6643 // property. It's true even if some slots represent deleted properties
ulan@chromium.org967e2702012-02-28 09:49:15 +00006644 // (their names are the hole value).
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006645 for (int i = 0; i < kInlinedProbes; i++) {
6646 // Compute the masked index: (hash + i + i * i) & mask.
6647 Register index = r0;
6648 // Capacity is smi 2^n.
6649 __ mov(index, FieldOperand(properties, kCapacityOffset));
6650 __ dec(index);
6651 __ and_(index,
6652 Immediate(Smi::FromInt(name->Hash() +
ulan@chromium.org750145a2013-03-07 15:14:13 +00006653 NameDictionary::GetProbeOffset(i))));
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006654
6655 // Scale the index by multiplying by the entry size.
ulan@chromium.org750145a2013-03-07 15:14:13 +00006656 ASSERT(NameDictionary::kEntrySize == 3);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006657 __ lea(index, Operand(index, index, times_2, 0)); // index *= 3.
6658 Register entity_name = r0;
6659 // Having undefined at this place means the name is not contained.
6660 ASSERT_EQ(kSmiTagSize, 1);
6661 __ mov(entity_name, Operand(properties, index, times_half_pointer_size,
6662 kElementsStartOffset - kHeapObjectTag));
6663 __ cmp(entity_name, masm->isolate()->factory()->undefined_value());
6664 __ j(equal, done);
6665
6666 // Stop if found the property.
ulan@chromium.org750145a2013-03-07 15:14:13 +00006667 __ cmp(entity_name, Handle<Name>(name));
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006668 __ j(equal, miss);
6669
ulan@chromium.org750145a2013-03-07 15:14:13 +00006670 Label good;
ulan@chromium.org967e2702012-02-28 09:49:15 +00006671 // Check for the hole and skip.
6672 __ cmp(entity_name, masm->isolate()->factory()->the_hole_value());
ulan@chromium.org750145a2013-03-07 15:14:13 +00006673 __ j(equal, &good, Label::kNear);
ulan@chromium.org967e2702012-02-28 09:49:15 +00006674
ulan@chromium.org750145a2013-03-07 15:14:13 +00006675 // Check if the entry name is not a unique name.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006676 __ mov(entity_name, FieldOperand(entity_name, HeapObject::kMapOffset));
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00006677 __ JumpIfNotUniqueName(FieldOperand(entity_name, Map::kInstanceTypeOffset),
6678 miss);
ulan@chromium.org750145a2013-03-07 15:14:13 +00006679 __ bind(&good);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006680 }
6681
ulan@chromium.org750145a2013-03-07 15:14:13 +00006682 NameDictionaryLookupStub stub(properties, r0, r0, NEGATIVE_LOOKUP);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006683 __ push(Immediate(Handle<Object>(name)));
6684 __ push(Immediate(name->Hash()));
6685 __ CallStub(&stub);
6686 __ test(r0, r0);
6687 __ j(not_zero, miss);
6688 __ jmp(done);
6689}
6690
6691
ulan@chromium.org750145a2013-03-07 15:14:13 +00006692// Probe the name dictionary in the |elements| register. Jump to the
lrn@chromium.org1c092762011-05-09 09:42:16 +00006693// |done| label if a property with the given name is found leaving the
6694// index into the dictionary in |r0|. Jump to the |miss| label
6695// otherwise.
ulan@chromium.org750145a2013-03-07 15:14:13 +00006696void NameDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm,
6697 Label* miss,
6698 Label* done,
6699 Register elements,
6700 Register name,
6701 Register r0,
6702 Register r1) {
erik.corry@gmail.com6e28b562011-10-27 14:20:17 +00006703 ASSERT(!elements.is(r0));
6704 ASSERT(!elements.is(r1));
6705 ASSERT(!name.is(r0));
6706 ASSERT(!name.is(r1));
6707
ulan@chromium.org750145a2013-03-07 15:14:13 +00006708 __ AssertName(name);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006709
6710 __ mov(r1, FieldOperand(elements, kCapacityOffset));
6711 __ shr(r1, kSmiTagSize); // convert smi to int
6712 __ dec(r1);
6713
6714 // Generate an unrolled loop that performs a few probes before
6715 // giving up. Measurements done on Gmail indicate that 2 probes
6716 // cover ~93% of loads from dictionaries.
6717 for (int i = 0; i < kInlinedProbes; i++) {
6718 // Compute the masked index: (hash + i + i * i) & mask.
ulan@chromium.org750145a2013-03-07 15:14:13 +00006719 __ mov(r0, FieldOperand(name, Name::kHashFieldOffset));
6720 __ shr(r0, Name::kHashShift);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006721 if (i > 0) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00006722 __ add(r0, Immediate(NameDictionary::GetProbeOffset(i)));
lrn@chromium.org1c092762011-05-09 09:42:16 +00006723 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006724 __ and_(r0, r1);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006725
6726 // Scale the index by multiplying by the entry size.
ulan@chromium.org750145a2013-03-07 15:14:13 +00006727 ASSERT(NameDictionary::kEntrySize == 3);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006728 __ lea(r0, Operand(r0, r0, times_2, 0)); // r0 = r0 * 3
6729
6730 // Check if the key is identical to the name.
6731 __ cmp(name, Operand(elements,
6732 r0,
6733 times_4,
6734 kElementsStartOffset - kHeapObjectTag));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00006735 __ j(equal, done);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006736 }
6737
ulan@chromium.org750145a2013-03-07 15:14:13 +00006738 NameDictionaryLookupStub stub(elements, r1, r0, POSITIVE_LOOKUP);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006739 __ push(name);
ulan@chromium.org750145a2013-03-07 15:14:13 +00006740 __ mov(r0, FieldOperand(name, Name::kHashFieldOffset));
6741 __ shr(r0, Name::kHashShift);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006742 __ push(r0);
6743 __ CallStub(&stub);
6744
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006745 __ test(r1, r1);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006746 __ j(zero, miss);
6747 __ jmp(done);
6748}
6749
6750
ulan@chromium.org750145a2013-03-07 15:14:13 +00006751void NameDictionaryLookupStub::Generate(MacroAssembler* masm) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006752 // This stub overrides SometimesSetsUpAFrame() to return false. That means
6753 // we cannot call anything that could cause a GC from this stub.
lrn@chromium.org1c092762011-05-09 09:42:16 +00006754 // Stack frame on entry:
6755 // esp[0 * kPointerSize]: return address.
6756 // esp[1 * kPointerSize]: key's hash.
6757 // esp[2 * kPointerSize]: key.
6758 // Registers:
ulan@chromium.org750145a2013-03-07 15:14:13 +00006759 // dictionary_: NameDictionary to probe.
lrn@chromium.org1c092762011-05-09 09:42:16 +00006760 // result_: used as scratch.
6761 // index_: will hold an index of entry if lookup is successful.
6762 // might alias with result_.
6763 // Returns:
6764 // result_ is zero if lookup failed, non zero otherwise.
6765
6766 Label in_dictionary, maybe_in_dictionary, not_in_dictionary;
6767
6768 Register scratch = result_;
6769
6770 __ mov(scratch, FieldOperand(dictionary_, kCapacityOffset));
6771 __ dec(scratch);
6772 __ SmiUntag(scratch);
6773 __ push(scratch);
6774
6775 // If names of slots in range from 1 to kProbes - 1 for the hash value are
6776 // not equal to the name and kProbes-th slot is not used (its name is the
6777 // undefined value), it guarantees the hash table doesn't contain the
6778 // property. It's true even if some slots represent deleted properties
6779 // (their names are the null value).
6780 for (int i = kInlinedProbes; i < kTotalProbes; i++) {
6781 // Compute the masked index: (hash + i + i * i) & mask.
6782 __ mov(scratch, Operand(esp, 2 * kPointerSize));
6783 if (i > 0) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00006784 __ add(scratch, Immediate(NameDictionary::GetProbeOffset(i)));
lrn@chromium.org1c092762011-05-09 09:42:16 +00006785 }
6786 __ and_(scratch, Operand(esp, 0));
6787
6788 // Scale the index by multiplying by the entry size.
ulan@chromium.org750145a2013-03-07 15:14:13 +00006789 ASSERT(NameDictionary::kEntrySize == 3);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006790 __ lea(index_, Operand(scratch, scratch, times_2, 0)); // index *= 3.
6791
6792 // Having undefined at this place means the name is not contained.
6793 ASSERT_EQ(kSmiTagSize, 1);
6794 __ mov(scratch, Operand(dictionary_,
6795 index_,
6796 times_pointer_size,
6797 kElementsStartOffset - kHeapObjectTag));
6798 __ cmp(scratch, masm->isolate()->factory()->undefined_value());
6799 __ j(equal, &not_in_dictionary);
6800
6801 // Stop if found the property.
6802 __ cmp(scratch, Operand(esp, 3 * kPointerSize));
6803 __ j(equal, &in_dictionary);
6804
6805 if (i != kTotalProbes - 1 && mode_ == NEGATIVE_LOOKUP) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00006806 // If we hit a key that is not a unique name during negative
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00006807 // lookup we have to bailout as this key might be equal to the
lrn@chromium.org1c092762011-05-09 09:42:16 +00006808 // key we are looking for.
6809
ulan@chromium.org750145a2013-03-07 15:14:13 +00006810 // Check if the entry name is not a unique name.
lrn@chromium.org1c092762011-05-09 09:42:16 +00006811 __ mov(scratch, FieldOperand(scratch, HeapObject::kMapOffset));
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00006812 __ JumpIfNotUniqueName(FieldOperand(scratch, Map::kInstanceTypeOffset),
6813 &maybe_in_dictionary);
lrn@chromium.org1c092762011-05-09 09:42:16 +00006814 }
6815 }
6816
6817 __ bind(&maybe_in_dictionary);
6818 // If we are doing negative lookup then probing failure should be
6819 // treated as a lookup success. For positive lookup probing failure
6820 // should be treated as lookup failure.
6821 if (mode_ == POSITIVE_LOOKUP) {
6822 __ mov(result_, Immediate(0));
6823 __ Drop(1);
6824 __ ret(2 * kPointerSize);
6825 }
6826
6827 __ bind(&in_dictionary);
6828 __ mov(result_, Immediate(1));
6829 __ Drop(1);
6830 __ ret(2 * kPointerSize);
6831
6832 __ bind(&not_in_dictionary);
6833 __ mov(result_, Immediate(0));
6834 __ Drop(1);
6835 __ ret(2 * kPointerSize);
6836}
6837
6838
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006839struct AheadOfTimeWriteBarrierStubList {
6840 Register object, value, address;
6841 RememberedSetAction action;
6842};
6843
6844
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006845#define REG(Name) { kRegister_ ## Name ## _Code }
6846
6847static const AheadOfTimeWriteBarrierStubList kAheadOfTime[] = {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006848 // Used in RegExpExecStub.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006849 { REG(ebx), REG(eax), REG(edi), EMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006850 // Used in CompileArrayPushCall.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006851 { REG(ebx), REG(ecx), REG(edx), EMIT_REMEMBERED_SET },
6852 { REG(ebx), REG(edi), REG(edx), OMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006853 // Used in CompileStoreGlobal and CallFunctionStub.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006854 { REG(ebx), REG(ecx), REG(edx), OMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006855 // Used in StoreStubCompiler::CompileStoreField and
6856 // KeyedStoreStubCompiler::CompileStoreField via GenerateStoreField.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006857 { REG(edx), REG(ecx), REG(ebx), EMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006858 // GenerateStoreField calls the stub with two different permutations of
6859 // registers. This is the second.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006860 { REG(ebx), REG(ecx), REG(edx), EMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006861 // StoreIC::GenerateNormal via GenerateDictionaryStore
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006862 { REG(ebx), REG(edi), REG(edx), EMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006863 // KeyedStoreIC::GenerateGeneric.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006864 { REG(ebx), REG(edx), REG(ecx), EMIT_REMEMBERED_SET},
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006865 // KeyedStoreStubCompiler::GenerateStoreFastElement.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006866 { REG(edi), REG(ebx), REG(ecx), EMIT_REMEMBERED_SET},
6867 { REG(edx), REG(edi), REG(ebx), EMIT_REMEMBERED_SET},
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00006868 // ElementsTransitionGenerator::GenerateMapChangeElementTransition
6869 // and ElementsTransitionGenerator::GenerateSmiToDouble
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006870 // and ElementsTransitionGenerator::GenerateDoubleToObject
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006871 { REG(edx), REG(ebx), REG(edi), EMIT_REMEMBERED_SET},
6872 { REG(edx), REG(ebx), REG(edi), OMIT_REMEMBERED_SET},
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00006873 // ElementsTransitionGenerator::GenerateDoubleToObject
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006874 { REG(eax), REG(edx), REG(esi), EMIT_REMEMBERED_SET},
6875 { REG(edx), REG(eax), REG(edi), EMIT_REMEMBERED_SET},
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00006876 // StoreArrayLiteralElementStub::Generate
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006877 { REG(ebx), REG(eax), REG(ecx), EMIT_REMEMBERED_SET},
ulan@chromium.org57ff8812013-05-10 08:16:55 +00006878 // FastNewClosureStub and StringAddStub::Generate
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00006879 { REG(ecx), REG(edx), REG(ebx), EMIT_REMEMBERED_SET},
ulan@chromium.org57ff8812013-05-10 08:16:55 +00006880 // StringAddStub::Generate
6881 { REG(ecx), REG(eax), REG(ebx), EMIT_REMEMBERED_SET},
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006882 // Null termination.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006883 { REG(no_reg), REG(no_reg), REG(no_reg), EMIT_REMEMBERED_SET}
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006884};
6885
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006886#undef REG
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006887
6888bool RecordWriteStub::IsPregenerated() {
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006889 for (const AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006890 !entry->object.is(no_reg);
6891 entry++) {
6892 if (object_.is(entry->object) &&
6893 value_.is(entry->value) &&
6894 address_.is(entry->address) &&
6895 remembered_set_action_ == entry->action &&
6896 save_fp_regs_mode_ == kDontSaveFPRegs) {
6897 return true;
6898 }
6899 }
6900 return false;
6901}
6902
6903
hpayer@chromium.org8432c912013-02-28 15:55:26 +00006904void StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(
6905 Isolate* isolate) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00006906 StoreBufferOverflowStub stub(kDontSaveFPRegs);
6907 stub.GetCode(isolate)->set_is_pregenerated(true);
6908 if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006909 StoreBufferOverflowStub stub2(kSaveFPRegs);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00006910 stub2.GetCode(isolate)->set_is_pregenerated(true);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006911 }
6912}
6913
6914
hpayer@chromium.org8432c912013-02-28 15:55:26 +00006915void RecordWriteStub::GenerateFixedRegStubsAheadOfTime(Isolate* isolate) {
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00006916 for (const AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006917 !entry->object.is(no_reg);
6918 entry++) {
6919 RecordWriteStub stub(entry->object,
6920 entry->value,
6921 entry->address,
6922 entry->action,
6923 kDontSaveFPRegs);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00006924 stub.GetCode(isolate)->set_is_pregenerated(true);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006925 }
6926}
6927
6928
verwaest@chromium.org33e09c82012-10-10 17:07:22 +00006929bool CodeStub::CanUseFPRegisters() {
6930 return CpuFeatures::IsSupported(SSE2);
6931}
6932
6933
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00006934// Takes the input in 3 registers: address_ value_ and object_. A pointer to
6935// the value has just been written into the object, now this stub makes sure
6936// we keep the GC informed. The word in the object where the value has been
6937// written is in the address register.
6938void RecordWriteStub::Generate(MacroAssembler* masm) {
6939 Label skip_to_incremental_noncompacting;
6940 Label skip_to_incremental_compacting;
6941
6942 // The first two instructions are generated with labels so as to get the
6943 // offset fixed up correctly by the bind(Label*) call. We patch it back and
6944 // forth between a compare instructions (a nop in this position) and the
6945 // real branch when we start and stop incremental heap marking.
6946 __ jmp(&skip_to_incremental_noncompacting, Label::kNear);
6947 __ jmp(&skip_to_incremental_compacting, Label::kFar);
6948
6949 if (remembered_set_action_ == EMIT_REMEMBERED_SET) {
6950 __ RememberedSetHelper(object_,
6951 address_,
6952 value_,
6953 save_fp_regs_mode_,
6954 MacroAssembler::kReturnAtEnd);
6955 } else {
6956 __ ret(0);
6957 }
6958
6959 __ bind(&skip_to_incremental_noncompacting);
6960 GenerateIncremental(masm, INCREMENTAL);
6961
6962 __ bind(&skip_to_incremental_compacting);
6963 GenerateIncremental(masm, INCREMENTAL_COMPACTION);
6964
6965 // Initial mode of the stub is expected to be STORE_BUFFER_ONLY.
6966 // Will be checked in IncrementalMarking::ActivateGeneratedStub.
6967 masm->set_byte_at(0, kTwoByteNopInstruction);
6968 masm->set_byte_at(2, kFiveByteNopInstruction);
6969}
6970
6971
6972void RecordWriteStub::GenerateIncremental(MacroAssembler* masm, Mode mode) {
6973 regs_.Save(masm);
6974
6975 if (remembered_set_action_ == EMIT_REMEMBERED_SET) {
6976 Label dont_need_remembered_set;
6977
6978 __ mov(regs_.scratch0(), Operand(regs_.address(), 0));
6979 __ JumpIfNotInNewSpace(regs_.scratch0(), // Value.
6980 regs_.scratch0(),
6981 &dont_need_remembered_set);
6982
6983 __ CheckPageFlag(regs_.object(),
6984 regs_.scratch0(),
6985 1 << MemoryChunk::SCAN_ON_SCAVENGE,
6986 not_zero,
6987 &dont_need_remembered_set);
6988
6989 // First notify the incremental marker if necessary, then update the
6990 // remembered set.
6991 CheckNeedsToInformIncrementalMarker(
6992 masm,
6993 kUpdateRememberedSetOnNoNeedToInformIncrementalMarker,
6994 mode);
6995 InformIncrementalMarker(masm, mode);
6996 regs_.Restore(masm);
6997 __ RememberedSetHelper(object_,
6998 address_,
6999 value_,
7000 save_fp_regs_mode_,
7001 MacroAssembler::kReturnAtEnd);
7002
7003 __ bind(&dont_need_remembered_set);
7004 }
7005
7006 CheckNeedsToInformIncrementalMarker(
7007 masm,
7008 kReturnOnNoNeedToInformIncrementalMarker,
7009 mode);
7010 InformIncrementalMarker(masm, mode);
7011 regs_.Restore(masm);
7012 __ ret(0);
7013}
7014
7015
7016void RecordWriteStub::InformIncrementalMarker(MacroAssembler* masm, Mode mode) {
7017 regs_.SaveCallerSaveRegisters(masm, save_fp_regs_mode_);
7018 int argument_count = 3;
7019 __ PrepareCallCFunction(argument_count, regs_.scratch0());
7020 __ mov(Operand(esp, 0 * kPointerSize), regs_.object());
ulan@chromium.org8e8d8822012-11-23 14:36:46 +00007021 __ mov(Operand(esp, 1 * kPointerSize), regs_.address()); // Slot.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007022 __ mov(Operand(esp, 2 * kPointerSize),
ulan@chromium.org32d7dba2013-04-24 10:59:06 +00007023 Immediate(ExternalReference::isolate_address(masm->isolate())));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007024
7025 AllowExternalCallThatCantCauseGC scope(masm);
7026 if (mode == INCREMENTAL_COMPACTION) {
7027 __ CallCFunction(
7028 ExternalReference::incremental_evacuation_record_write_function(
7029 masm->isolate()),
7030 argument_count);
7031 } else {
7032 ASSERT(mode == INCREMENTAL);
7033 __ CallCFunction(
7034 ExternalReference::incremental_marking_record_write_function(
7035 masm->isolate()),
7036 argument_count);
7037 }
7038 regs_.RestoreCallerSaveRegisters(masm, save_fp_regs_mode_);
7039}
7040
7041
7042void RecordWriteStub::CheckNeedsToInformIncrementalMarker(
7043 MacroAssembler* masm,
7044 OnNoNeedToInformIncrementalMarker on_no_need,
7045 Mode mode) {
7046 Label object_is_black, need_incremental, need_incremental_pop_object;
7047
verwaest@chromium.org33e09c82012-10-10 17:07:22 +00007048 __ mov(regs_.scratch0(), Immediate(~Page::kPageAlignmentMask));
7049 __ and_(regs_.scratch0(), regs_.object());
7050 __ mov(regs_.scratch1(),
7051 Operand(regs_.scratch0(),
7052 MemoryChunk::kWriteBarrierCounterOffset));
7053 __ sub(regs_.scratch1(), Immediate(1));
7054 __ mov(Operand(regs_.scratch0(),
7055 MemoryChunk::kWriteBarrierCounterOffset),
7056 regs_.scratch1());
7057 __ j(negative, &need_incremental);
7058
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00007059 // Let's look at the color of the object: If it is not black we don't have
7060 // to inform the incremental marker.
7061 __ JumpIfBlack(regs_.object(),
7062 regs_.scratch0(),
7063 regs_.scratch1(),
7064 &object_is_black,
7065 Label::kNear);
7066
7067 regs_.Restore(masm);
7068 if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) {
7069 __ RememberedSetHelper(object_,
7070 address_,
7071 value_,
7072 save_fp_regs_mode_,
7073 MacroAssembler::kReturnAtEnd);
7074 } else {
7075 __ ret(0);
7076 }
7077
7078 __ bind(&object_is_black);
7079
7080 // Get the value from the slot.
7081 __ mov(regs_.scratch0(), Operand(regs_.address(), 0));
7082
7083 if (mode == INCREMENTAL_COMPACTION) {
7084 Label ensure_not_white;
7085
7086 __ CheckPageFlag(regs_.scratch0(), // Contains value.
7087 regs_.scratch1(), // Scratch.
7088 MemoryChunk::kEvacuationCandidateMask,
7089 zero,
7090 &ensure_not_white,
7091 Label::kNear);
7092
7093 __ CheckPageFlag(regs_.object(),
7094 regs_.scratch1(), // Scratch.
7095 MemoryChunk::kSkipEvacuationSlotsRecordingMask,
7096 not_zero,
7097 &ensure_not_white,
7098 Label::kNear);
7099
7100 __ jmp(&need_incremental);
7101
7102 __ bind(&ensure_not_white);
7103 }
7104
7105 // We need an extra register for this, so we push the object register
7106 // temporarily.
7107 __ push(regs_.object());
7108 __ EnsureNotWhite(regs_.scratch0(), // The value.
7109 regs_.scratch1(), // Scratch.
7110 regs_.object(), // Scratch.
7111 &need_incremental_pop_object,
7112 Label::kNear);
7113 __ pop(regs_.object());
7114
7115 regs_.Restore(masm);
7116 if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) {
7117 __ RememberedSetHelper(object_,
7118 address_,
7119 value_,
7120 save_fp_regs_mode_,
7121 MacroAssembler::kReturnAtEnd);
7122 } else {
7123 __ ret(0);
7124 }
7125
7126 __ bind(&need_incremental_pop_object);
7127 __ pop(regs_.object());
7128
7129 __ bind(&need_incremental);
7130
7131 // Fall through when we need to inform the incremental marker.
7132}
7133
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007134
7135void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) {
7136 // ----------- S t a t e -------------
7137 // -- eax : element value to store
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007138 // -- ecx : element index as smi
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007139 // -- esp[0] : return address
dslomov@chromium.orgb752d402013-06-18 11:54:54 +00007140 // -- esp[4] : array literal index in function
7141 // -- esp[8] : array literal
7142 // clobbers ebx, edx, edi
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007143 // -----------------------------------
7144
7145 Label element_done;
7146 Label double_elements;
7147 Label smi_element;
7148 Label slow_elements;
7149 Label slow_elements_from_double;
7150 Label fast_elements;
7151
dslomov@chromium.orgb752d402013-06-18 11:54:54 +00007152 // Get array literal index, array literal and its map.
7153 __ mov(edx, Operand(esp, 1 * kPointerSize));
7154 __ mov(ebx, Operand(esp, 2 * kPointerSize));
7155 __ mov(edi, FieldOperand(ebx, JSObject::kMapOffset));
7156
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007157 __ CheckFastElements(edi, &double_elements);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007158
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00007159 // Check for FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS elements
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007160 __ JumpIfSmi(eax, &smi_element);
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00007161 __ CheckFastSmiElements(edi, &fast_elements, Label::kNear);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007162
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007163 // Store into the array literal requires a elements transition. Call into
7164 // the runtime.
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007165
7166 __ bind(&slow_elements);
7167 __ pop(edi); // Pop return address and remember to put back later for tail
7168 // call.
7169 __ push(ebx);
7170 __ push(ecx);
7171 __ push(eax);
7172 __ mov(ebx, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
7173 __ push(FieldOperand(ebx, JSFunction::kLiteralsOffset));
7174 __ push(edx);
7175 __ push(edi); // Return return address so that tail call returns to right
7176 // place.
7177 __ TailCallRuntime(Runtime::kStoreArrayLiteralElement, 5, 1);
7178
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007179 __ bind(&slow_elements_from_double);
7180 __ pop(edx);
7181 __ jmp(&slow_elements);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007182
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00007183 // Array literal has ElementsKind of FAST_*_ELEMENTS and value is an object.
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007184 __ bind(&fast_elements);
7185 __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset));
7186 __ lea(ecx, FieldOperand(ebx, ecx, times_half_pointer_size,
7187 FixedArrayBase::kHeaderSize));
7188 __ mov(Operand(ecx, 0), eax);
7189 // Update the write barrier for the array store.
7190 __ RecordWrite(ebx, ecx, eax,
7191 kDontSaveFPRegs,
7192 EMIT_REMEMBERED_SET,
7193 OMIT_SMI_CHECK);
7194 __ ret(0);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007195
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00007196 // Array literal has ElementsKind of FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS,
7197 // and value is Smi.
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007198 __ bind(&smi_element);
7199 __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset));
7200 __ mov(FieldOperand(ebx, ecx, times_half_pointer_size,
7201 FixedArrayBase::kHeaderSize), eax);
7202 __ ret(0);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007203
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00007204 // Array literal has ElementsKind of FAST_*_DOUBLE_ELEMENTS.
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007205 __ bind(&double_elements);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007206
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00007207 __ push(edx);
7208 __ mov(edx, FieldOperand(ebx, JSObject::kElementsOffset));
7209 __ StoreNumberToDoubleElements(eax,
7210 edx,
7211 ecx,
7212 edi,
7213 xmm0,
7214 &slow_elements_from_double,
7215 false);
7216 __ pop(edx);
7217 __ ret(0);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00007218}
7219
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007220
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00007221void StubFailureTrampolineStub::Generate(MacroAssembler* masm) {
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00007222 CEntryStub ces(1, fp_registers_ ? kSaveFPRegs : kDontSaveFPRegs);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00007223 __ call(ces.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
mmassi@chromium.org2f0efde2013-02-06 14:12:58 +00007224 int parameter_count_offset =
7225 StubFailureTrampolineFrame::kCallerStackParameterCountFrameOffset;
7226 __ mov(ebx, MemOperand(ebp, parameter_count_offset));
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00007227 masm->LeaveFrame(StackFrame::STUB_FAILURE_TRAMPOLINE);
mmassi@chromium.org2f0efde2013-02-06 14:12:58 +00007228 __ pop(ecx);
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00007229 int additional_offset = function_mode_ == JS_FUNCTION_STUB_MODE
7230 ? kPointerSize
7231 : 0;
7232 __ lea(esp, MemOperand(esp, ebx, times_pointer_size, additional_offset));
mmassi@chromium.org2f0efde2013-02-06 14:12:58 +00007233 __ jmp(ecx); // Return to IC Miss stub, continuation still on stack.
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00007234}
7235
7236
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007237void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007238 if (masm->isolate()->function_entry_hook() != NULL) {
7239 // It's always safe to call the entry hook stub, as the hook itself
7240 // is not allowed to call back to V8.
7241 AllowStubCallsScope allow_stub_calls(masm, true);
7242
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007243 ProfileEntryHookStub stub;
7244 masm->CallStub(&stub);
7245 }
7246}
7247
7248
7249void ProfileEntryHookStub::Generate(MacroAssembler* masm) {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007250 // Save volatile registers.
7251 const int kNumSavedRegisters = 3;
7252 __ push(eax);
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007253 __ push(ecx);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007254 __ push(edx);
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007255
7256 // Calculate and push the original stack pointer.
ulan@chromium.orgdfe53072013-06-06 14:14:51 +00007257 __ lea(eax, Operand(esp, (kNumSavedRegisters + 1) * kPointerSize));
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007258 __ push(eax);
7259
ulan@chromium.orgdfe53072013-06-06 14:14:51 +00007260 // Retrieve our return address and use it to calculate the calling
7261 // function's address.
7262 __ mov(eax, Operand(esp, (kNumSavedRegisters + 1) * kPointerSize));
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007263 __ sub(eax, Immediate(Assembler::kCallInstructionLength));
7264 __ push(eax);
7265
7266 // Call the entry hook.
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007267 ASSERT(masm->isolate()->function_entry_hook() != NULL);
7268 __ call(FUNCTION_ADDR(masm->isolate()->function_entry_hook()),
7269 RelocInfo::RUNTIME_ENTRY);
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007270 __ add(esp, Immediate(2 * kPointerSize));
7271
7272 // Restore ecx.
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007273 __ pop(edx);
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007274 __ pop(ecx);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007275 __ pop(eax);
7276
verwaest@chromium.org753aee42012-07-17 16:15:42 +00007277 __ ret(0);
7278}
7279
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007280
7281template<class T>
7282static void CreateArrayDispatch(MacroAssembler* masm) {
7283 int last_index = GetSequenceIndexFromFastElementsKind(
7284 TERMINAL_FAST_ELEMENTS_KIND);
7285 for (int i = 0; i <= last_index; ++i) {
7286 Label next;
7287 ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
7288 __ cmp(edx, kind);
7289 __ j(not_equal, &next);
7290 T stub(kind);
7291 __ TailCallStub(&stub);
7292 __ bind(&next);
7293 }
7294
7295 // If we reached this point there is a problem.
danno@chromium.org59400602013-08-13 17:09:37 +00007296 __ Abort(kUnexpectedElementsKindInArrayConstructor);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007297}
7298
7299
7300static void CreateArrayDispatchOneArgument(MacroAssembler* masm) {
7301 // ebx - type info cell
7302 // edx - kind
7303 // eax - number of arguments
7304 // edi - constructor?
7305 // esp[0] - return address
7306 // esp[4] - last argument
7307 ASSERT(FAST_SMI_ELEMENTS == 0);
7308 ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
7309 ASSERT(FAST_ELEMENTS == 2);
7310 ASSERT(FAST_HOLEY_ELEMENTS == 3);
7311 ASSERT(FAST_DOUBLE_ELEMENTS == 4);
7312 ASSERT(FAST_HOLEY_DOUBLE_ELEMENTS == 5);
7313
7314 Handle<Object> undefined_sentinel(
7315 masm->isolate()->heap()->undefined_value(),
7316 masm->isolate());
7317
7318 // is the low bit set? If so, we are holey and that is good.
7319 __ test_b(edx, 1);
7320 Label normal_sequence;
7321 __ j(not_zero, &normal_sequence);
7322
7323 // look at the first argument
7324 __ mov(ecx, Operand(esp, kPointerSize));
7325 __ test(ecx, ecx);
7326 __ j(zero, &normal_sequence);
7327
7328 // We are going to create a holey array, but our kind is non-holey.
danno@chromium.orgbee51992013-07-10 14:57:15 +00007329 // Fix kind and retry (only if we have an allocation site in the cell).
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007330 __ inc(edx);
7331 __ cmp(ebx, Immediate(undefined_sentinel));
7332 __ j(equal, &normal_sequence);
danno@chromium.orgbee51992013-07-10 14:57:15 +00007333 __ mov(ecx, FieldOperand(ebx, Cell::kValueOffset));
7334 Handle<Map> allocation_site_map(
7335 masm->isolate()->heap()->allocation_site_map(),
7336 masm->isolate());
7337 __ cmp(FieldOperand(ecx, 0), Immediate(allocation_site_map));
7338 __ j(not_equal, &normal_sequence);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007339
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007340 // Save the resulting elements kind in type info
7341 __ SmiTag(edx);
danno@chromium.orgbee51992013-07-10 14:57:15 +00007342 __ mov(FieldOperand(ecx, AllocationSite::kTransitionInfoOffset), edx);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007343 __ SmiUntag(edx);
7344
7345 __ bind(&normal_sequence);
7346 int last_index = GetSequenceIndexFromFastElementsKind(
7347 TERMINAL_FAST_ELEMENTS_KIND);
7348 for (int i = 0; i <= last_index; ++i) {
7349 Label next;
7350 ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
7351 __ cmp(edx, kind);
7352 __ j(not_equal, &next);
7353 ArraySingleArgumentConstructorStub stub(kind);
7354 __ TailCallStub(&stub);
7355 __ bind(&next);
7356 }
7357
7358 // If we reached this point there is a problem.
danno@chromium.org59400602013-08-13 17:09:37 +00007359 __ Abort(kUnexpectedElementsKindInArrayConstructor);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007360}
7361
7362
7363template<class T>
7364static void ArrayConstructorStubAheadOfTimeHelper(Isolate* isolate) {
7365 int to_index = GetSequenceIndexFromFastElementsKind(
7366 TERMINAL_FAST_ELEMENTS_KIND);
7367 for (int i = 0; i <= to_index; ++i) {
7368 ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007369 T stub(kind);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007370 stub.GetCode(isolate)->set_is_pregenerated(true);
danno@chromium.orgbee51992013-07-10 14:57:15 +00007371 if (AllocationSite::GetMode(kind) != DONT_TRACK_ALLOCATION_SITE) {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007372 T stub1(kind, CONTEXT_CHECK_REQUIRED, DISABLE_ALLOCATION_SITES);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007373 stub1.GetCode(isolate)->set_is_pregenerated(true);
7374 }
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007375 }
7376}
7377
7378
7379void ArrayConstructorStubBase::GenerateStubsAheadOfTime(Isolate* isolate) {
7380 ArrayConstructorStubAheadOfTimeHelper<ArrayNoArgumentConstructorStub>(
7381 isolate);
7382 ArrayConstructorStubAheadOfTimeHelper<ArraySingleArgumentConstructorStub>(
7383 isolate);
7384 ArrayConstructorStubAheadOfTimeHelper<ArrayNArgumentsConstructorStub>(
7385 isolate);
7386}
7387
7388
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007389void InternalArrayConstructorStubBase::GenerateStubsAheadOfTime(
7390 Isolate* isolate) {
7391 ElementsKind kinds[2] = { FAST_ELEMENTS, FAST_HOLEY_ELEMENTS };
7392 for (int i = 0; i < 2; i++) {
7393 // For internal arrays we only need a few things
7394 InternalArrayNoArgumentConstructorStub stubh1(kinds[i]);
7395 stubh1.GetCode(isolate)->set_is_pregenerated(true);
7396 InternalArraySingleArgumentConstructorStub stubh2(kinds[i]);
7397 stubh2.GetCode(isolate)->set_is_pregenerated(true);
7398 InternalArrayNArgumentsConstructorStub stubh3(kinds[i]);
7399 stubh3.GetCode(isolate)->set_is_pregenerated(true);
7400 }
7401}
7402
7403
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007404void ArrayConstructorStub::Generate(MacroAssembler* masm) {
7405 // ----------- S t a t e -------------
7406 // -- eax : argc (only if argument_count_ == ANY)
7407 // -- ebx : type info cell
7408 // -- edi : constructor
7409 // -- esp[0] : return address
7410 // -- esp[4] : last argument
7411 // -----------------------------------
7412 Handle<Object> undefined_sentinel(
7413 masm->isolate()->heap()->undefined_value(),
7414 masm->isolate());
7415
7416 if (FLAG_debug_code) {
7417 // The array construct code is only set for the global and natives
7418 // builtin Array functions which always have maps.
7419
7420 // Initial map for the builtin Array function should be a map.
7421 __ mov(ecx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
7422 // Will both indicate a NULL and a Smi.
7423 __ test(ecx, Immediate(kSmiTagMask));
danno@chromium.org59400602013-08-13 17:09:37 +00007424 __ Assert(not_zero, kUnexpectedInitialMapForArrayFunction);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007425 __ CmpObjectType(ecx, MAP_TYPE, ecx);
danno@chromium.org59400602013-08-13 17:09:37 +00007426 __ Assert(equal, kUnexpectedInitialMapForArrayFunction);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007427
danno@chromium.org41728482013-06-12 22:31:22 +00007428 // We should either have undefined in ebx or a valid cell
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007429 Label okay_here;
danno@chromium.org41728482013-06-12 22:31:22 +00007430 Handle<Map> cell_map = masm->isolate()->factory()->cell_map();
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007431 __ cmp(ebx, Immediate(undefined_sentinel));
7432 __ j(equal, &okay_here);
danno@chromium.org41728482013-06-12 22:31:22 +00007433 __ cmp(FieldOperand(ebx, 0), Immediate(cell_map));
danno@chromium.org59400602013-08-13 17:09:37 +00007434 __ Assert(equal, kExpectedPropertyCellInRegisterEbx);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007435 __ bind(&okay_here);
7436 }
7437
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007438 Label no_info, switch_ready;
7439 // Get the elements kind and case on that.
7440 __ cmp(ebx, Immediate(undefined_sentinel));
7441 __ j(equal, &no_info);
7442 __ mov(edx, FieldOperand(ebx, Cell::kValueOffset));
danno@chromium.orgbee51992013-07-10 14:57:15 +00007443
7444 // The type cell may have undefined in its value.
7445 __ cmp(edx, Immediate(undefined_sentinel));
7446 __ j(equal, &no_info);
7447
7448 // The type cell has either an AllocationSite or a JSFunction
7449 __ cmp(FieldOperand(edx, 0), Immediate(Handle<Map>(
7450 masm->isolate()->heap()->allocation_site_map())));
7451 __ j(not_equal, &no_info);
7452
7453 __ mov(edx, FieldOperand(edx, AllocationSite::kTransitionInfoOffset));
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007454 __ SmiUntag(edx);
7455 __ jmp(&switch_ready);
7456 __ bind(&no_info);
7457 __ mov(edx, Immediate(GetInitialFastElementsKind()));
7458 __ bind(&switch_ready);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007459
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007460 if (argument_count_ == ANY) {
7461 Label not_zero_case, not_one_case;
7462 __ test(eax, eax);
7463 __ j(not_zero, &not_zero_case);
7464 CreateArrayDispatch<ArrayNoArgumentConstructorStub>(masm);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007465
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007466 __ bind(&not_zero_case);
7467 __ cmp(eax, 1);
7468 __ j(greater, &not_one_case);
7469 CreateArrayDispatchOneArgument(masm);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007470
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007471 __ bind(&not_one_case);
7472 CreateArrayDispatch<ArrayNArgumentsConstructorStub>(masm);
7473 } else if (argument_count_ == NONE) {
7474 CreateArrayDispatch<ArrayNoArgumentConstructorStub>(masm);
7475 } else if (argument_count_ == ONE) {
7476 CreateArrayDispatchOneArgument(masm);
7477 } else if (argument_count_ == MORE_THAN_ONE) {
7478 CreateArrayDispatch<ArrayNArgumentsConstructorStub>(masm);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007479 } else {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007480 UNREACHABLE();
danno@chromium.orgca29dd82013-04-26 11:59:48 +00007481 }
7482}
7483
7484
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007485void InternalArrayConstructorStub::GenerateCase(
7486 MacroAssembler* masm, ElementsKind kind) {
7487 Label not_zero_case, not_one_case;
7488 Label normal_sequence;
7489
7490 __ test(eax, eax);
7491 __ j(not_zero, &not_zero_case);
7492 InternalArrayNoArgumentConstructorStub stub0(kind);
7493 __ TailCallStub(&stub0);
7494
7495 __ bind(&not_zero_case);
7496 __ cmp(eax, 1);
7497 __ j(greater, &not_one_case);
7498
7499 if (IsFastPackedElementsKind(kind)) {
7500 // We might need to create a holey array
7501 // look at the first argument
7502 __ mov(ecx, Operand(esp, kPointerSize));
7503 __ test(ecx, ecx);
7504 __ j(zero, &normal_sequence);
7505
7506 InternalArraySingleArgumentConstructorStub
7507 stub1_holey(GetHoleyElementsKind(kind));
7508 __ TailCallStub(&stub1_holey);
7509 }
7510
7511 __ bind(&normal_sequence);
7512 InternalArraySingleArgumentConstructorStub stub1(kind);
7513 __ TailCallStub(&stub1);
7514
7515 __ bind(&not_one_case);
7516 InternalArrayNArgumentsConstructorStub stubN(kind);
7517 __ TailCallStub(&stubN);
7518}
7519
7520
7521void InternalArrayConstructorStub::Generate(MacroAssembler* masm) {
7522 // ----------- S t a t e -------------
7523 // -- eax : argc
7524 // -- ebx : type info cell
7525 // -- edi : constructor
7526 // -- esp[0] : return address
7527 // -- esp[4] : last argument
7528 // -----------------------------------
7529
7530 if (FLAG_debug_code) {
7531 // The array construct code is only set for the global and natives
7532 // builtin Array functions which always have maps.
7533
7534 // Initial map for the builtin Array function should be a map.
7535 __ mov(ecx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
7536 // Will both indicate a NULL and a Smi.
7537 __ test(ecx, Immediate(kSmiTagMask));
danno@chromium.org59400602013-08-13 17:09:37 +00007538 __ Assert(not_zero, kUnexpectedInitialMapForArrayFunction);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007539 __ CmpObjectType(ecx, MAP_TYPE, ecx);
danno@chromium.org59400602013-08-13 17:09:37 +00007540 __ Assert(equal, kUnexpectedInitialMapForArrayFunction);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007541 }
7542
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007543 // Figure out the right elements kind
7544 __ mov(ecx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007545
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007546 // Load the map's "bit field 2" into |result|. We only need the first byte,
7547 // but the following masking takes care of that anyway.
7548 __ mov(ecx, FieldOperand(ecx, Map::kBitField2Offset));
7549 // Retrieve elements_kind from bit field 2.
7550 __ and_(ecx, Map::kElementsKindMask);
7551 __ shr(ecx, Map::kElementsKindShift);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007552
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007553 if (FLAG_debug_code) {
7554 Label done;
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007555 __ cmp(ecx, Immediate(FAST_ELEMENTS));
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007556 __ j(equal, &done);
7557 __ cmp(ecx, Immediate(FAST_HOLEY_ELEMENTS));
7558 __ Assert(equal,
danno@chromium.org59400602013-08-13 17:09:37 +00007559 kInvalidElementsKindForInternalArrayOrInternalPackedArray);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007560 __ bind(&done);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007561 }
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00007562
7563 Label fast_elements_case;
7564 __ cmp(ecx, Immediate(FAST_ELEMENTS));
7565 __ j(equal, &fast_elements_case);
7566 GenerateCase(masm, FAST_HOLEY_ELEMENTS);
7567
7568 __ bind(&fast_elements_case);
7569 GenerateCase(masm, FAST_ELEMENTS);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00007570}
7571
7572
ricow@chromium.org65fae842010-08-25 15:26:24 +00007573#undef __
7574
7575} } // namespace v8::internal
7576
7577#endif // V8_TARGET_ARCH_IA32