blob: adc2d50d5b1fc0abd9994515afd83e8d62184fd0 [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
machenbach@chromium.org3d079fe2013-09-25 08:19:55 +000067void NumberToStringStub::InitializeInterfaceDescriptor(
68 Isolate* isolate,
69 CodeStubInterfaceDescriptor* descriptor) {
70 static Register registers[] = { eax };
71 descriptor->register_param_count_ = 1;
72 descriptor->register_params_ = registers;
bmeurer@chromium.orge7a07452013-10-21 13:27:29 +000073 descriptor->deoptimization_handler_ =
74 Runtime::FunctionForId(Runtime::kNumberToString)->entry;
machenbach@chromium.org3d079fe2013-09-25 08:19:55 +000075}
76
77
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000078void FastCloneShallowArrayStub::InitializeInterfaceDescriptor(
79 Isolate* isolate,
80 CodeStubInterfaceDescriptor* descriptor) {
81 static Register registers[] = { eax, ebx, ecx };
82 descriptor->register_param_count_ = 3;
83 descriptor->register_params_ = registers;
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000084 descriptor->deoptimization_handler_ =
verwaest@chromium.org057bd502013-11-06 12:03:29 +000085 Runtime::FunctionForId(Runtime::kCreateArrayLiteral)->entry;
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000086}
87
88
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +000089void FastCloneShallowObjectStub::InitializeInterfaceDescriptor(
90 Isolate* isolate,
91 CodeStubInterfaceDescriptor* descriptor) {
92 static Register registers[] = { eax, ebx, ecx, edx };
93 descriptor->register_param_count_ = 4;
94 descriptor->register_params_ = registers;
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +000095 descriptor->deoptimization_handler_ =
machenbach@chromium.org528ce022013-09-23 14:09:36 +000096 Runtime::FunctionForId(Runtime::kCreateObjectLiteral)->entry;
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +000097}
98
99
danno@chromium.orgbee51992013-07-10 14:57:15 +0000100void CreateAllocationSiteStub::InitializeInterfaceDescriptor(
101 Isolate* isolate,
102 CodeStubInterfaceDescriptor* descriptor) {
103 static Register registers[] = { ebx };
104 descriptor->register_param_count_ = 1;
105 descriptor->register_params_ = registers;
106 descriptor->deoptimization_handler_ = NULL;
107}
108
109
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000110void KeyedLoadFastElementStub::InitializeInterfaceDescriptor(
111 Isolate* isolate,
112 CodeStubInterfaceDescriptor* descriptor) {
113 static Register registers[] = { edx, ecx };
114 descriptor->register_param_count_ = 2;
115 descriptor->register_params_ = registers;
116 descriptor->deoptimization_handler_ =
mstarzinger@chromium.orge3b8d0f2013-02-01 09:06:41 +0000117 FUNCTION_ADDR(KeyedLoadIC_MissFromStubFailure);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000118}
119
120
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000121void LoadFieldStub::InitializeInterfaceDescriptor(
122 Isolate* isolate,
123 CodeStubInterfaceDescriptor* descriptor) {
124 static Register registers[] = { edx };
125 descriptor->register_param_count_ = 1;
126 descriptor->register_params_ = registers;
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000127 descriptor->deoptimization_handler_ = NULL;
128}
129
130
131void KeyedLoadFieldStub::InitializeInterfaceDescriptor(
132 Isolate* isolate,
133 CodeStubInterfaceDescriptor* descriptor) {
134 static Register registers[] = { edx };
135 descriptor->register_param_count_ = 1;
136 descriptor->register_params_ = registers;
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000137 descriptor->deoptimization_handler_ = NULL;
138}
139
140
jkummerow@chromium.org7bd87f02013-03-20 18:06:29 +0000141void KeyedStoreFastElementStub::InitializeInterfaceDescriptor(
142 Isolate* isolate,
143 CodeStubInterfaceDescriptor* descriptor) {
144 static Register registers[] = { edx, ecx, eax };
145 descriptor->register_param_count_ = 3;
146 descriptor->register_params_ = registers;
147 descriptor->deoptimization_handler_ =
148 FUNCTION_ADDR(KeyedStoreIC_MissFromStubFailure);
149}
150
151
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000152void TransitionElementsKindStub::InitializeInterfaceDescriptor(
153 Isolate* isolate,
154 CodeStubInterfaceDescriptor* descriptor) {
155 static Register registers[] = { eax, ebx };
156 descriptor->register_param_count_ = 2;
157 descriptor->register_params_ = registers;
158 descriptor->deoptimization_handler_ =
159 Runtime::FunctionForId(Runtime::kTransitionElementsKind)->entry;
160}
161
162
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000163static void InitializeArrayConstructorDescriptor(
164 Isolate* isolate,
165 CodeStubInterfaceDescriptor* descriptor,
166 int constant_stack_parameter_count) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000167 // register state
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000168 // eax -- number of arguments
ulan@chromium.org57ff8812013-05-10 08:16:55 +0000169 // edi -- function
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000170 // ebx -- type info cell with elements kind
machenbach@chromium.org0cc09502013-11-13 12:20:55 +0000171 static Register registers_variable_args[] = { edi, ebx, eax };
172 static Register registers_no_args[] = { edi, ebx };
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000173
machenbach@chromium.org0cc09502013-11-13 12:20:55 +0000174 if (constant_stack_parameter_count == 0) {
175 descriptor->register_param_count_ = 2;
176 descriptor->register_params_ = registers_no_args;
177 } else {
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000178 // stack param count needs (constructor pointer, and single argument)
machenbach@chromium.org0cc09502013-11-13 12:20:55 +0000179 descriptor->handler_arguments_mode_ = PASS_ARGUMENTS;
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +0000180 descriptor->stack_parameter_count_ = eax;
machenbach@chromium.org0cc09502013-11-13 12:20:55 +0000181 descriptor->register_param_count_ = 3;
182 descriptor->register_params_ = registers_variable_args;
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000183 }
machenbach@chromium.org0cc09502013-11-13 12:20:55 +0000184
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000185 descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +0000186 descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000187 descriptor->deoptimization_handler_ =
ulan@chromium.orgdfe53072013-06-06 14:14:51 +0000188 Runtime::FunctionForId(Runtime::kArrayConstructor)->entry;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000189}
190
191
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +0000192static void InitializeInternalArrayConstructorDescriptor(
193 Isolate* isolate,
194 CodeStubInterfaceDescriptor* descriptor,
195 int constant_stack_parameter_count) {
196 // register state
197 // eax -- number of arguments
198 // edi -- constructor function
machenbach@chromium.org0cc09502013-11-13 12:20:55 +0000199 static Register registers_variable_args[] = { edi, eax };
200 static Register registers_no_args[] = { edi };
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +0000201
machenbach@chromium.org0cc09502013-11-13 12:20:55 +0000202 if (constant_stack_parameter_count == 0) {
203 descriptor->register_param_count_ = 1;
204 descriptor->register_params_ = registers_no_args;
205 } else {
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +0000206 // stack param count needs (constructor pointer, and single argument)
machenbach@chromium.org0cc09502013-11-13 12:20:55 +0000207 descriptor->handler_arguments_mode_ = PASS_ARGUMENTS;
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +0000208 descriptor->stack_parameter_count_ = eax;
machenbach@chromium.org0cc09502013-11-13 12:20:55 +0000209 descriptor->register_param_count_ = 2;
210 descriptor->register_params_ = registers_variable_args;
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +0000211 }
machenbach@chromium.org0cc09502013-11-13 12:20:55 +0000212
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +0000213 descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +0000214 descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
215 descriptor->deoptimization_handler_ =
ulan@chromium.orgdfe53072013-06-06 14:14:51 +0000216 Runtime::FunctionForId(Runtime::kInternalArrayConstructor)->entry;
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +0000217}
218
219
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000220void ArrayNoArgumentConstructorStub::InitializeInterfaceDescriptor(
221 Isolate* isolate,
222 CodeStubInterfaceDescriptor* descriptor) {
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000223 InitializeArrayConstructorDescriptor(isolate, descriptor, 0);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000224}
225
226
227void ArraySingleArgumentConstructorStub::InitializeInterfaceDescriptor(
228 Isolate* isolate,
229 CodeStubInterfaceDescriptor* descriptor) {
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000230 InitializeArrayConstructorDescriptor(isolate, descriptor, 1);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000231}
232
233
234void ArrayNArgumentsConstructorStub::InitializeInterfaceDescriptor(
235 Isolate* isolate,
236 CodeStubInterfaceDescriptor* descriptor) {
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000237 InitializeArrayConstructorDescriptor(isolate, descriptor, -1);
238}
239
240
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +0000241void InternalArrayNoArgumentConstructorStub::InitializeInterfaceDescriptor(
242 Isolate* isolate,
243 CodeStubInterfaceDescriptor* descriptor) {
244 InitializeInternalArrayConstructorDescriptor(isolate, descriptor, 0);
245}
246
247
248void InternalArraySingleArgumentConstructorStub::InitializeInterfaceDescriptor(
249 Isolate* isolate,
250 CodeStubInterfaceDescriptor* descriptor) {
251 InitializeInternalArrayConstructorDescriptor(isolate, descriptor, 1);
252}
253
254
255void InternalArrayNArgumentsConstructorStub::InitializeInterfaceDescriptor(
256 Isolate* isolate,
257 CodeStubInterfaceDescriptor* descriptor) {
258 InitializeInternalArrayConstructorDescriptor(isolate, descriptor, -1);
259}
260
261
danno@chromium.orgca29dd82013-04-26 11:59:48 +0000262void CompareNilICStub::InitializeInterfaceDescriptor(
263 Isolate* isolate,
264 CodeStubInterfaceDescriptor* descriptor) {
265 static Register registers[] = { eax };
266 descriptor->register_param_count_ = 1;
267 descriptor->register_params_ = registers;
268 descriptor->deoptimization_handler_ =
269 FUNCTION_ADDR(CompareNilIC_Miss);
svenpanne@chromium.orga53e8e02013-05-24 12:35:50 +0000270 descriptor->SetMissHandler(
271 ExternalReference(IC_Utility(IC::kCompareNilIC_Miss), isolate));
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000272}
273
rossberg@chromium.orgb99c7542013-05-31 11:40:45 +0000274void ToBooleanStub::InitializeInterfaceDescriptor(
275 Isolate* isolate,
276 CodeStubInterfaceDescriptor* descriptor) {
277 static Register registers[] = { eax };
278 descriptor->register_param_count_ = 1;
279 descriptor->register_params_ = registers;
280 descriptor->deoptimization_handler_ =
281 FUNCTION_ADDR(ToBooleanIC_Miss);
282 descriptor->SetMissHandler(
283 ExternalReference(IC_Utility(IC::kToBooleanIC_Miss), isolate));
284}
285
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000286
mstarzinger@chromium.orge0e1b0d2013-07-08 08:38:06 +0000287void StoreGlobalStub::InitializeInterfaceDescriptor(
288 Isolate* isolate,
289 CodeStubInterfaceDescriptor* descriptor) {
290 static Register registers[] = { edx, ecx, eax };
291 descriptor->register_param_count_ = 3;
292 descriptor->register_params_ = registers;
293 descriptor->deoptimization_handler_ =
294 FUNCTION_ADDR(StoreIC_MissFromStubFailure);
295}
296
297
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +0000298void ElementsTransitionAndStoreStub::InitializeInterfaceDescriptor(
299 Isolate* isolate,
300 CodeStubInterfaceDescriptor* descriptor) {
301 static Register registers[] = { eax, ebx, ecx, edx };
302 descriptor->register_param_count_ = 4;
303 descriptor->register_params_ = registers;
304 descriptor->deoptimization_handler_ =
305 FUNCTION_ADDR(ElementsTransitionAndStoreIC_Miss);
306}
307
308
jkummerow@chromium.org25b0e212013-10-04 15:38:52 +0000309void BinaryOpStub::InitializeInterfaceDescriptor(
310 Isolate* isolate,
311 CodeStubInterfaceDescriptor* descriptor) {
312 static Register registers[] = { edx, eax };
313 descriptor->register_param_count_ = 2;
314 descriptor->register_params_ = registers;
315 descriptor->deoptimization_handler_ = FUNCTION_ADDR(BinaryOpIC_Miss);
316 descriptor->SetMissHandler(
317 ExternalReference(IC_Utility(IC::kBinaryOpIC_Miss), isolate));
318}
319
320
machenbach@chromium.org0cc09502013-11-13 12:20:55 +0000321void NewStringAddStub::InitializeInterfaceDescriptor(
322 Isolate* isolate,
323 CodeStubInterfaceDescriptor* descriptor) {
324 static Register registers[] = { edx, eax };
325 descriptor->register_param_count_ = 2;
326 descriptor->register_params_ = registers;
327 descriptor->deoptimization_handler_ =
328 Runtime::FunctionForId(Runtime::kStringAdd)->entry;
329}
330
331
ricow@chromium.org65fae842010-08-25 15:26:24 +0000332#define __ ACCESS_MASM(masm)
whesse@chromium.org7a392b32011-01-31 11:30:36 +0000333
ulan@chromium.org77ca49a2013-04-22 09:43:56 +0000334
335void HydrogenCodeStub::GenerateLightweightMiss(MacroAssembler* masm) {
336 // Update the static counter each time a new code stub is generated.
337 Isolate* isolate = masm->isolate();
338 isolate->counters()->code_stubs()->Increment();
339
340 CodeStubInterfaceDescriptor* descriptor = GetInterfaceDescriptor(isolate);
341 int param_count = descriptor->register_param_count_;
342 {
343 // Call the runtime system in a fresh internal frame.
344 FrameScope scope(masm, StackFrame::INTERNAL);
345 ASSERT(descriptor->register_param_count_ == 0 ||
346 eax.is(descriptor->register_params_[param_count - 1]));
347 // Push arguments
348 for (int i = 0; i < param_count; ++i) {
349 __ push(descriptor->register_params_[i]);
350 }
svenpanne@chromium.orga53e8e02013-05-24 12:35:50 +0000351 ExternalReference miss = descriptor->miss_handler();
ulan@chromium.org77ca49a2013-04-22 09:43:56 +0000352 __ CallExternalReference(miss, descriptor->register_param_count_);
353 }
354
355 __ ret(0);
356}
357
358
ricow@chromium.org65fae842010-08-25 15:26:24 +0000359void FastNewContextStub::Generate(MacroAssembler* masm) {
360 // Try to allocate the context in new space.
361 Label gc;
ager@chromium.org0ee099b2011-01-25 14:06:47 +0000362 int length = slots_ + Context::MIN_CONTEXT_SLOTS;
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000363 __ Allocate((length * kPointerSize) + FixedArray::kHeaderSize,
364 eax, ebx, ecx, &gc, TAG_OBJECT);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000365
366 // Get the function from the stack.
367 __ mov(ecx, Operand(esp, 1 * kPointerSize));
368
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000369 // Set up the object header.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000370 Factory* factory = masm->isolate()->factory();
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000371 __ mov(FieldOperand(eax, HeapObject::kMapOffset),
372 factory->function_context_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +0000373 __ mov(FieldOperand(eax, Context::kLengthOffset),
ager@chromium.org0ee099b2011-01-25 14:06:47 +0000374 Immediate(Smi::FromInt(length)));
ricow@chromium.org65fae842010-08-25 15:26:24 +0000375
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000376 // Set up the fixed slots.
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000377 __ Set(ebx, Immediate(0)); // Set to NULL.
ricow@chromium.org65fae842010-08-25 15:26:24 +0000378 __ mov(Operand(eax, Context::SlotOffset(Context::CLOSURE_INDEX)), ecx);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000379 __ mov(Operand(eax, Context::SlotOffset(Context::PREVIOUS_INDEX)), esi);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000380 __ mov(Operand(eax, Context::SlotOffset(Context::EXTENSION_INDEX)), ebx);
381
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000382 // Copy the global object from the previous context.
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000383 __ mov(ebx, Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
384 __ mov(Operand(eax, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)), ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000385
386 // Initialize the rest of the slots to undefined.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000387 __ mov(ebx, factory->undefined_value());
ager@chromium.org0ee099b2011-01-25 14:06:47 +0000388 for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
ricow@chromium.org65fae842010-08-25 15:26:24 +0000389 __ mov(Operand(eax, Context::SlotOffset(i)), ebx);
390 }
391
392 // Return and remove the on-stack parameter.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000393 __ mov(esi, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000394 __ ret(1 * kPointerSize);
395
396 // Need to collect. Call into runtime system.
397 __ bind(&gc);
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000398 __ TailCallRuntime(Runtime::kNewFunctionContext, 1, 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000399}
400
401
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000402void FastNewBlockContextStub::Generate(MacroAssembler* masm) {
403 // Stack layout on entry:
404 //
405 // [esp + (1 * kPointerSize)]: function
406 // [esp + (2 * kPointerSize)]: serialized scope info
407
408 // Try to allocate the context in new space.
409 Label gc;
410 int length = slots_ + Context::MIN_CONTEXT_SLOTS;
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +0000411 __ Allocate(FixedArray::SizeFor(length), eax, ebx, ecx, &gc, TAG_OBJECT);
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000412
413 // Get the function or sentinel from the stack.
414 __ mov(ecx, Operand(esp, 1 * kPointerSize));
415
416 // Get the serialized scope info from the stack.
417 __ mov(ebx, Operand(esp, 2 * kPointerSize));
418
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000419 // Set up the object header.
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000420 Factory* factory = masm->isolate()->factory();
421 __ mov(FieldOperand(eax, HeapObject::kMapOffset),
422 factory->block_context_map());
423 __ mov(FieldOperand(eax, Context::kLengthOffset),
424 Immediate(Smi::FromInt(length)));
425
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000426 // If this block context is nested in the native context we get a smi
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000427 // sentinel instead of a function. The block context should get the
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000428 // canonical empty function of the native context as its closure which
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000429 // we still have to look up.
430 Label after_sentinel;
431 __ JumpIfNotSmi(ecx, &after_sentinel, Label::kNear);
432 if (FLAG_debug_code) {
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000433 __ cmp(ecx, 0);
danno@chromium.org59400602013-08-13 17:09:37 +0000434 __ Assert(equal, kExpected0AsASmiSentinel);
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000435 }
436 __ mov(ecx, GlobalObjectOperand());
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000437 __ mov(ecx, FieldOperand(ecx, GlobalObject::kNativeContextOffset));
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000438 __ mov(ecx, ContextOperand(ecx, Context::CLOSURE_INDEX));
439 __ bind(&after_sentinel);
440
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000441 // Set up the fixed slots.
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000442 __ mov(ContextOperand(eax, Context::CLOSURE_INDEX), ecx);
443 __ mov(ContextOperand(eax, Context::PREVIOUS_INDEX), esi);
444 __ mov(ContextOperand(eax, Context::EXTENSION_INDEX), ebx);
445
446 // Copy the global object from the previous context.
yangguo@chromium.org46839fb2012-08-28 09:06:19 +0000447 __ mov(ebx, ContextOperand(esi, Context::GLOBAL_OBJECT_INDEX));
448 __ mov(ContextOperand(eax, Context::GLOBAL_OBJECT_INDEX), ebx);
svenpanne@chromium.orga8bb4d92011-10-10 13:20:40 +0000449
450 // Initialize the rest of the slots to the hole value.
451 if (slots_ == 1) {
452 __ mov(ContextOperand(eax, Context::MIN_CONTEXT_SLOTS),
453 factory->the_hole_value());
454 } else {
455 __ mov(ebx, factory->the_hole_value());
456 for (int i = 0; i < slots_; i++) {
457 __ mov(ContextOperand(eax, i + Context::MIN_CONTEXT_SLOTS), ebx);
458 }
459 }
460
461 // Return and remove the on-stack parameters.
462 __ mov(esi, eax);
463 __ ret(2 * kPointerSize);
464
465 // Need to collect. Call into runtime system.
466 __ bind(&gc);
467 __ TailCallRuntime(Runtime::kPushBlockContext, 2, 1);
468}
469
470
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000471void StoreBufferOverflowStub::Generate(MacroAssembler* masm) {
472 // We don't allow a GC during a store buffer overflow so there is no need to
473 // store the registers in any particular way, but we do have to store and
474 // restore them.
475 __ pushad();
476 if (save_doubles_ == kSaveFPRegs) {
ulan@chromium.org750145a2013-03-07 15:14:13 +0000477 CpuFeatureScope scope(masm, SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000478 __ sub(esp, Immediate(kDoubleSize * XMMRegister::kNumRegisters));
479 for (int i = 0; i < XMMRegister::kNumRegisters; i++) {
480 XMMRegister reg = XMMRegister::from_code(i);
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +0000481 __ movsd(Operand(esp, i * kDoubleSize), reg);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000482 }
483 }
484 const int argument_count = 1;
485
486 AllowExternalCallThatCantCauseGC scope(masm);
487 __ PrepareCallCFunction(argument_count, ecx);
488 __ mov(Operand(esp, 0 * kPointerSize),
ulan@chromium.org32d7dba2013-04-24 10:59:06 +0000489 Immediate(ExternalReference::isolate_address(masm->isolate())));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000490 __ CallCFunction(
491 ExternalReference::store_buffer_overflow_function(masm->isolate()),
492 argument_count);
493 if (save_doubles_ == kSaveFPRegs) {
ulan@chromium.org750145a2013-03-07 15:14:13 +0000494 CpuFeatureScope scope(masm, SSE2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000495 for (int i = 0; i < XMMRegister::kNumRegisters; i++) {
496 XMMRegister reg = XMMRegister::from_code(i);
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +0000497 __ movsd(reg, Operand(esp, i * kDoubleSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000498 }
499 __ add(esp, Immediate(kDoubleSize * XMMRegister::kNumRegisters));
500 }
501 __ popad();
502 __ ret(0);
503}
504
505
ricow@chromium.org65fae842010-08-25 15:26:24 +0000506class FloatingPointHelper : public AllStatic {
507 public:
ricow@chromium.org65fae842010-08-25 15:26:24 +0000508 enum ArgLocation {
509 ARGS_ON_STACK,
510 ARGS_IN_REGISTERS
511 };
512
513 // Code pattern for loading a floating point value. Input value must
514 // be either a smi or a heap number object (fp value). Requirements:
515 // operand in register number. Returns operand as floating point number
516 // on FPU stack.
517 static void LoadFloatOperand(MacroAssembler* masm, Register number);
518
ricow@chromium.org65fae842010-08-25 15:26:24 +0000519 // Test if operands are smi or number objects (fp). Requirements:
520 // operand_1 in eax, operand_2 in edx; falls through on float
521 // operands, jumps to the non_float label otherwise.
522 static void CheckFloatOperands(MacroAssembler* masm,
523 Label* non_float,
524 Register scratch);
525
ricow@chromium.org65fae842010-08-25 15:26:24 +0000526 // Test if operands are numbers (smi or HeapNumber objects), and load
527 // them into xmm0 and xmm1 if they are. Jump to label not_numbers if
528 // either operand is not a number. Operands are in edx and eax.
529 // Leaves operands unchanged.
530 static void LoadSSE2Operands(MacroAssembler* masm, Label* not_numbers);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000531};
532
533
danno@chromium.org169691d2013-07-15 08:01:13 +0000534void DoubleToIStub::Generate(MacroAssembler* masm) {
535 Register input_reg = this->source();
536 Register final_result_reg = this->destination();
537 ASSERT(is_truncating());
538
539 Label check_negative, process_64_bits, done, done_no_stash;
540
541 int double_offset = offset();
542
543 // Account for return address and saved regs if input is esp.
544 if (input_reg.is(esp)) double_offset += 3 * kPointerSize;
545
546 MemOperand mantissa_operand(MemOperand(input_reg, double_offset));
547 MemOperand exponent_operand(MemOperand(input_reg,
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +0000548 double_offset + kDoubleSize / 2));
danno@chromium.org169691d2013-07-15 08:01:13 +0000549
550 Register scratch1;
551 {
552 Register scratch_candidates[3] = { ebx, edx, edi };
553 for (int i = 0; i < 3; i++) {
554 scratch1 = scratch_candidates[i];
555 if (!final_result_reg.is(scratch1) && !input_reg.is(scratch1)) break;
556 }
557 }
558 // Since we must use ecx for shifts below, use some other register (eax)
559 // to calculate the result if ecx is the requested return register.
560 Register result_reg = final_result_reg.is(ecx) ? eax : final_result_reg;
561 // Save ecx if it isn't the return register and therefore volatile, or if it
562 // is the return register, then save the temp register we use in its stead for
563 // the result.
564 Register save_reg = final_result_reg.is(ecx) ? eax : ecx;
565 __ push(scratch1);
566 __ push(save_reg);
567
568 bool stash_exponent_copy = !input_reg.is(esp);
569 __ mov(scratch1, mantissa_operand);
570 if (CpuFeatures::IsSupported(SSE3)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +0000571 CpuFeatureScope scope(masm, SSE3);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000572 // Load x87 register with heap number.
danno@chromium.org169691d2013-07-15 08:01:13 +0000573 __ fld_d(mantissa_operand);
574 }
575 __ mov(ecx, exponent_operand);
576 if (stash_exponent_copy) __ push(ecx);
577
578 __ and_(ecx, HeapNumber::kExponentMask);
579 __ shr(ecx, HeapNumber::kExponentShift);
580 __ lea(result_reg, MemOperand(ecx, -HeapNumber::kExponentBias));
581 __ cmp(result_reg, Immediate(HeapNumber::kMantissaBits));
582 __ j(below, &process_64_bits);
583
584 // Result is entirely in lower 32-bits of mantissa
585 int delta = HeapNumber::kExponentBias + Double::kPhysicalSignificandSize;
586 if (CpuFeatures::IsSupported(SSE3)) {
587 __ fstp(0);
588 }
589 __ sub(ecx, Immediate(delta));
590 __ xor_(result_reg, result_reg);
591 __ cmp(ecx, Immediate(31));
592 __ j(above, &done);
593 __ shl_cl(scratch1);
594 __ jmp(&check_negative);
595
596 __ bind(&process_64_bits);
597 if (CpuFeatures::IsSupported(SSE3)) {
598 CpuFeatureScope scope(masm, SSE3);
599 if (stash_exponent_copy) {
600 // Already a copy of the exponent on the stack, overwrite it.
601 STATIC_ASSERT(kDoubleSize == 2 * kPointerSize);
602 __ sub(esp, Immediate(kDoubleSize / 2));
603 } else {
604 // Reserve space for 64 bit answer.
605 __ sub(esp, Immediate(kDoubleSize)); // Nolint.
606 }
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000607 // Do conversion, which cannot fail because we checked the exponent.
608 __ fisttp_d(Operand(esp, 0));
danno@chromium.org169691d2013-07-15 08:01:13 +0000609 __ mov(result_reg, Operand(esp, 0)); // Load low word of answer as result
610 __ add(esp, Immediate(kDoubleSize));
611 __ jmp(&done_no_stash);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000612 } else {
danno@chromium.org169691d2013-07-15 08:01:13 +0000613 // Result must be extracted from shifted 32-bit mantissa
614 __ sub(ecx, Immediate(delta));
615 __ neg(ecx);
616 if (stash_exponent_copy) {
617 __ mov(result_reg, MemOperand(esp, 0));
618 } else {
619 __ mov(result_reg, exponent_operand);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000620 }
danno@chromium.org169691d2013-07-15 08:01:13 +0000621 __ and_(result_reg,
622 Immediate(static_cast<uint32_t>(Double::kSignificandMask >> 32)));
623 __ add(result_reg,
624 Immediate(static_cast<uint32_t>(Double::kHiddenBit >> 32)));
625 __ shrd(result_reg, scratch1);
626 __ shr_cl(result_reg);
627 __ test(ecx, Immediate(32));
628 if (CpuFeatures::IsSupported(CMOV)) {
629 CpuFeatureScope use_cmov(masm, CMOV);
630 __ cmov(not_equal, scratch1, result_reg);
631 } else {
632 Label skip_mov;
633 __ j(equal, &skip_mov, Label::kNear);
634 __ mov(scratch1, result_reg);
635 __ bind(&skip_mov);
636 }
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000637 }
danno@chromium.org169691d2013-07-15 08:01:13 +0000638
639 // If the double was negative, negate the integer result.
640 __ bind(&check_negative);
641 __ mov(result_reg, scratch1);
642 __ neg(result_reg);
643 if (stash_exponent_copy) {
644 __ cmp(MemOperand(esp, 0), Immediate(0));
645 } else {
646 __ cmp(exponent_operand, Immediate(0));
647 }
648 if (CpuFeatures::IsSupported(CMOV)) {
649 CpuFeatureScope use_cmov(masm, CMOV);
650 __ cmov(greater, result_reg, scratch1);
651 } else {
652 Label skip_mov;
653 __ j(less_equal, &skip_mov, Label::kNear);
654 __ mov(result_reg, scratch1);
655 __ bind(&skip_mov);
656 }
657
658 // Restore registers
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000659 __ bind(&done);
danno@chromium.org169691d2013-07-15 08:01:13 +0000660 if (stash_exponent_copy) {
661 __ add(esp, Immediate(kDoubleSize / 2));
662 }
663 __ bind(&done_no_stash);
664 if (!final_result_reg.is(result_reg)) {
665 ASSERT(final_result_reg.is(ecx));
666 __ mov(final_result_reg, result_reg);
667 }
668 __ pop(save_reg);
669 __ pop(scratch1);
670 __ ret(0);
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000671}
672
673
ricow@chromium.org65fae842010-08-25 15:26:24 +0000674void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
whesse@chromium.org023421e2010-12-21 12:19:12 +0000675 // TAGGED case:
676 // Input:
677 // esp[4]: tagged number input argument (should be number).
678 // esp[0]: return address.
679 // Output:
680 // eax: tagged double result.
681 // UNTAGGED case:
682 // Input::
683 // esp[0]: return address.
684 // xmm1: untagged double input argument
685 // Output:
686 // xmm1: untagged double result.
687
ricow@chromium.org65fae842010-08-25 15:26:24 +0000688 Label runtime_call;
689 Label runtime_call_clear_stack;
whesse@chromium.org023421e2010-12-21 12:19:12 +0000690 Label skip_cache;
691 const bool tagged = (argument_type_ == TAGGED);
692 if (tagged) {
693 // Test that eax is a number.
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000694 Label input_not_smi;
695 Label loaded;
whesse@chromium.org023421e2010-12-21 12:19:12 +0000696 __ mov(eax, Operand(esp, kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +0000697 __ JumpIfNotSmi(eax, &input_not_smi, Label::kNear);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000698 // Input is a smi. Untag and load it onto the FPU stack.
699 // Then load the low and high words of the double into ebx, edx.
700 STATIC_ASSERT(kSmiTagSize == 1);
701 __ sar(eax, 1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000702 __ sub(esp, Immediate(2 * kPointerSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +0000703 __ mov(Operand(esp, 0), eax);
704 __ fild_s(Operand(esp, 0));
705 __ fst_d(Operand(esp, 0));
706 __ pop(edx);
707 __ pop(ebx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000708 __ jmp(&loaded, Label::kNear);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000709 __ bind(&input_not_smi);
710 // Check if input is a HeapNumber.
711 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000712 Factory* factory = masm->isolate()->factory();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000713 __ cmp(ebx, Immediate(factory->heap_number_map()));
whesse@chromium.org023421e2010-12-21 12:19:12 +0000714 __ j(not_equal, &runtime_call);
715 // Input is a HeapNumber. Push it on the FPU stack and load its
716 // low and high words into ebx, edx.
717 __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
718 __ mov(edx, FieldOperand(eax, HeapNumber::kExponentOffset));
719 __ mov(ebx, FieldOperand(eax, HeapNumber::kMantissaOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +0000720
whesse@chromium.org023421e2010-12-21 12:19:12 +0000721 __ bind(&loaded);
722 } else { // UNTAGGED.
ulan@chromium.org750145a2013-03-07 15:14:13 +0000723 CpuFeatureScope scope(masm, SSE2);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000724 if (CpuFeatures::IsSupported(SSE4_1)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +0000725 CpuFeatureScope sse4_scope(masm, SSE4_1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000726 __ pextrd(edx, xmm1, 0x1); // copy xmm1[63..32] to edx.
whesse@chromium.org023421e2010-12-21 12:19:12 +0000727 } else {
728 __ pshufd(xmm0, xmm1, 0x1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000729 __ movd(edx, xmm0);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000730 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000731 __ movd(ebx, xmm1);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000732 }
733
734 // ST[0] or xmm1 == double value
ricow@chromium.org65fae842010-08-25 15:26:24 +0000735 // ebx = low 32 bits of double value
736 // edx = high 32 bits of double value
737 // Compute hash (the shifts are arithmetic):
738 // h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1);
739 __ mov(ecx, ebx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000740 __ xor_(ecx, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000741 __ mov(eax, ecx);
742 __ sar(eax, 16);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000743 __ xor_(ecx, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000744 __ mov(eax, ecx);
745 __ sar(eax, 8);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000746 __ xor_(ecx, eax);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000747 ASSERT(IsPowerOf2(TranscendentalCache::SubCache::kCacheSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000748 __ and_(ecx,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000749 Immediate(TranscendentalCache::SubCache::kCacheSize - 1));
ricow@chromium.org65fae842010-08-25 15:26:24 +0000750
whesse@chromium.org023421e2010-12-21 12:19:12 +0000751 // ST[0] or xmm1 == double value.
ricow@chromium.org65fae842010-08-25 15:26:24 +0000752 // ebx = low 32 bits of double value.
753 // edx = high 32 bits of double value.
754 // ecx = TranscendentalCache::hash(double value).
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000755 ExternalReference cache_array =
756 ExternalReference::transcendental_cache_array_address(masm->isolate());
757 __ mov(eax, Immediate(cache_array));
758 int cache_array_index =
759 type_ * sizeof(masm->isolate()->transcendental_cache()->caches_[0]);
760 __ mov(eax, Operand(eax, cache_array_index));
ricow@chromium.org65fae842010-08-25 15:26:24 +0000761 // Eax points to the cache for the type type_.
762 // If NULL, the cache hasn't been initialized yet, so go through runtime.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000763 __ test(eax, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000764 __ j(zero, &runtime_call_clear_stack);
765#ifdef DEBUG
766 // Check that the layout of cache elements match expectations.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000767 { TranscendentalCache::SubCache::Element test_elem[2];
ricow@chromium.org65fae842010-08-25 15:26:24 +0000768 char* elem_start = reinterpret_cast<char*>(&test_elem[0]);
769 char* elem2_start = reinterpret_cast<char*>(&test_elem[1]);
770 char* elem_in0 = reinterpret_cast<char*>(&(test_elem[0].in[0]));
771 char* elem_in1 = reinterpret_cast<char*>(&(test_elem[0].in[1]));
772 char* elem_out = reinterpret_cast<char*>(&(test_elem[0].output));
773 CHECK_EQ(12, elem2_start - elem_start); // Two uint_32's and a pointer.
774 CHECK_EQ(0, elem_in0 - elem_start);
775 CHECK_EQ(kIntSize, elem_in1 - elem_start);
776 CHECK_EQ(2 * kIntSize, elem_out - elem_start);
777 }
778#endif
779 // Find the address of the ecx'th entry in the cache, i.e., &eax[ecx*12].
780 __ lea(ecx, Operand(ecx, ecx, times_2, 0));
781 __ lea(ecx, Operand(eax, ecx, times_4, 0));
782 // Check if cache matches: Double value is stored in uint32_t[2] array.
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000783 Label cache_miss;
ricow@chromium.org65fae842010-08-25 15:26:24 +0000784 __ cmp(ebx, Operand(ecx, 0));
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000785 __ j(not_equal, &cache_miss, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000786 __ cmp(edx, Operand(ecx, kIntSize));
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000787 __ j(not_equal, &cache_miss, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000788 // Cache hit!
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000789 Counters* counters = masm->isolate()->counters();
790 __ IncrementCounter(counters->transcendental_cache_hit(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000791 __ mov(eax, Operand(ecx, 2 * kIntSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +0000792 if (tagged) {
793 __ fstp(0);
794 __ ret(kPointerSize);
795 } else { // UNTAGGED.
ulan@chromium.org750145a2013-03-07 15:14:13 +0000796 CpuFeatureScope scope(masm, SSE2);
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +0000797 __ movsd(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
whesse@chromium.org023421e2010-12-21 12:19:12 +0000798 __ Ret();
799 }
ricow@chromium.org65fae842010-08-25 15:26:24 +0000800
801 __ bind(&cache_miss);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000802 __ IncrementCounter(counters->transcendental_cache_miss(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000803 // Update cache with new value.
804 // We are short on registers, so use no_reg as scratch.
805 // This gives slightly larger code.
whesse@chromium.org023421e2010-12-21 12:19:12 +0000806 if (tagged) {
807 __ AllocateHeapNumber(eax, edi, no_reg, &runtime_call_clear_stack);
808 } else { // UNTAGGED.
ulan@chromium.org750145a2013-03-07 15:14:13 +0000809 CpuFeatureScope scope(masm, SSE2);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000810 __ AllocateHeapNumber(eax, edi, no_reg, &skip_cache);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000811 __ sub(esp, Immediate(kDoubleSize));
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +0000812 __ movsd(Operand(esp, 0), xmm1);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000813 __ fld_d(Operand(esp, 0));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000814 __ add(esp, Immediate(kDoubleSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +0000815 }
ulan@chromium.org9a21ec42012-03-06 08:42:24 +0000816 GenerateOperation(masm, type_);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000817 __ mov(Operand(ecx, 0), ebx);
818 __ mov(Operand(ecx, kIntSize), edx);
819 __ mov(Operand(ecx, 2 * kIntSize), eax);
820 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
whesse@chromium.org023421e2010-12-21 12:19:12 +0000821 if (tagged) {
822 __ ret(kPointerSize);
823 } else { // UNTAGGED.
ulan@chromium.org750145a2013-03-07 15:14:13 +0000824 CpuFeatureScope scope(masm, SSE2);
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +0000825 __ movsd(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
whesse@chromium.org023421e2010-12-21 12:19:12 +0000826 __ Ret();
ricow@chromium.org65fae842010-08-25 15:26:24 +0000827
whesse@chromium.org023421e2010-12-21 12:19:12 +0000828 // Skip cache and return answer directly, only in untagged case.
829 __ bind(&skip_cache);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000830 __ sub(esp, Immediate(kDoubleSize));
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +0000831 __ movsd(Operand(esp, 0), xmm1);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000832 __ fld_d(Operand(esp, 0));
ulan@chromium.org9a21ec42012-03-06 08:42:24 +0000833 GenerateOperation(masm, type_);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000834 __ fstp_d(Operand(esp, 0));
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +0000835 __ movsd(xmm1, Operand(esp, 0));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000836 __ add(esp, Immediate(kDoubleSize));
whesse@chromium.org023421e2010-12-21 12:19:12 +0000837 // We return the value in xmm1 without adding it to the cache, but
838 // we cause a scavenging GC so that future allocations will succeed.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000839 {
840 FrameScope scope(masm, StackFrame::INTERNAL);
841 // Allocate an unused object bigger than a HeapNumber.
842 __ push(Immediate(Smi::FromInt(2 * kDoubleSize)));
843 __ CallRuntimeSaveDoubles(Runtime::kAllocateInNewSpace);
844 }
whesse@chromium.org023421e2010-12-21 12:19:12 +0000845 __ Ret();
846 }
847
848 // Call runtime, doing whatever allocation and cleanup is necessary.
849 if (tagged) {
850 __ bind(&runtime_call_clear_stack);
851 __ fstp(0);
852 __ bind(&runtime_call);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000853 ExternalReference runtime =
854 ExternalReference(RuntimeFunction(), masm->isolate());
855 __ TailCallExternalReference(runtime, 1, 1);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000856 } else { // UNTAGGED.
ulan@chromium.org750145a2013-03-07 15:14:13 +0000857 CpuFeatureScope scope(masm, SSE2);
whesse@chromium.org023421e2010-12-21 12:19:12 +0000858 __ bind(&runtime_call_clear_stack);
859 __ bind(&runtime_call);
860 __ AllocateHeapNumber(eax, edi, no_reg, &skip_cache);
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +0000861 __ movsd(FieldOperand(eax, HeapNumber::kValueOffset), xmm1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000862 {
863 FrameScope scope(masm, StackFrame::INTERNAL);
864 __ push(eax);
865 __ CallRuntime(RuntimeFunction(), 1);
866 }
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +0000867 __ movsd(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
whesse@chromium.org023421e2010-12-21 12:19:12 +0000868 __ Ret();
869 }
ricow@chromium.org65fae842010-08-25 15:26:24 +0000870}
871
872
873Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
874 switch (type_) {
ricow@chromium.org65fae842010-08-25 15:26:24 +0000875 case TranscendentalCache::SIN: return Runtime::kMath_sin;
876 case TranscendentalCache::COS: return Runtime::kMath_cos;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000877 case TranscendentalCache::TAN: return Runtime::kMath_tan;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000878 case TranscendentalCache::LOG: return Runtime::kMath_log;
ricow@chromium.org65fae842010-08-25 15:26:24 +0000879 default:
880 UNIMPLEMENTED();
881 return Runtime::kAbort;
882 }
883}
884
885
ulan@chromium.org9a21ec42012-03-06 08:42:24 +0000886void TranscendentalCacheStub::GenerateOperation(
887 MacroAssembler* masm, TranscendentalCache::Type type) {
ricow@chromium.org65fae842010-08-25 15:26:24 +0000888 // Only free register is edi.
whesse@chromium.org023421e2010-12-21 12:19:12 +0000889 // Input value is on FP stack, and also in ebx/edx.
890 // Input value is possibly in xmm1.
891 // Address of result (a newly allocated HeapNumber) may be in eax.
ulan@chromium.org9a21ec42012-03-06 08:42:24 +0000892 if (type == TranscendentalCache::SIN ||
893 type == TranscendentalCache::COS ||
894 type == TranscendentalCache::TAN) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000895 // Both fsin and fcos require arguments in the range +/-2^63 and
896 // return NaN for infinities and NaN. They can share all code except
897 // the actual fsin/fcos operation.
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000898 Label in_range, done;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000899 // If argument is outside the range -2^63..2^63, fsin/cos doesn't
900 // work. We must reduce it to the appropriate range.
901 __ mov(edi, edx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000902 __ and_(edi, Immediate(0x7ff00000)); // Exponent only.
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000903 int supported_exponent_limit =
904 (63 + HeapNumber::kExponentBias) << HeapNumber::kExponentShift;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000905 __ cmp(edi, Immediate(supported_exponent_limit));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000906 __ j(below, &in_range, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000907 // Check for infinity and NaN. Both return NaN for sin.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000908 __ cmp(edi, Immediate(0x7ff00000));
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000909 Label non_nan_result;
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000910 __ j(not_equal, &non_nan_result, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000911 // Input is +/-Infinity or NaN. Result is NaN.
912 __ fstp(0);
913 // NaN is represented by 0x7ff8000000000000.
914 __ push(Immediate(0x7ff80000));
915 __ push(Immediate(0));
916 __ fld_d(Operand(esp, 0));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000917 __ add(esp, Immediate(2 * kPointerSize));
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000918 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000919
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000920 __ bind(&non_nan_result);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000921
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000922 // Use fpmod to restrict argument to the range +/-2*PI.
923 __ mov(edi, eax); // Save eax before using fnstsw_ax.
924 __ fldpi();
925 __ fadd(0);
926 __ fld(1);
927 // FPU Stack: input, 2*pi, input.
928 {
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000929 Label no_exceptions;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000930 __ fwait();
931 __ fnstsw_ax();
932 // Clear if Illegal Operand or Zero Division exceptions are set.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000933 __ test(eax, Immediate(5));
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000934 __ j(zero, &no_exceptions, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000935 __ fnclex();
936 __ bind(&no_exceptions);
937 }
ricow@chromium.org65fae842010-08-25 15:26:24 +0000938
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000939 // Compute st(0) % st(1)
940 {
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000941 Label partial_remainder_loop;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000942 __ bind(&partial_remainder_loop);
943 __ fprem1();
944 __ fwait();
945 __ fnstsw_ax();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000946 __ test(eax, Immediate(0x400 /* C2 */));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000947 // If C2 is set, computation only has partial result. Loop to
948 // continue computation.
949 __ j(not_zero, &partial_remainder_loop);
950 }
951 // FPU Stack: input, 2*pi, input % 2*pi
952 __ fstp(2);
953 __ fstp(0);
954 __ mov(eax, edi); // Restore eax (allocated HeapNumber pointer).
955
956 // FPU Stack: input % 2*pi
957 __ bind(&in_range);
ulan@chromium.org9a21ec42012-03-06 08:42:24 +0000958 switch (type) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000959 case TranscendentalCache::SIN:
960 __ fsin();
961 break;
962 case TranscendentalCache::COS:
963 __ fcos();
964 break;
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000965 case TranscendentalCache::TAN:
966 // FPTAN calculates tangent onto st(0) and pushes 1.0 onto the
967 // FP register stack.
968 __ fptan();
969 __ fstp(0); // Pop FP register stack.
970 break;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000971 default:
972 UNREACHABLE();
973 }
974 __ bind(&done);
975 } else {
ulan@chromium.org9a21ec42012-03-06 08:42:24 +0000976 ASSERT(type == TranscendentalCache::LOG);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000977 __ fldln2();
978 __ fxch();
979 __ fyl2x();
ricow@chromium.org65fae842010-08-25 15:26:24 +0000980 }
ricow@chromium.org65fae842010-08-25 15:26:24 +0000981}
982
983
ricow@chromium.org65fae842010-08-25 15:26:24 +0000984void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm,
985 Register number) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000986 Label load_smi, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +0000987
whesse@chromium.org7b260152011-06-20 15:33:18 +0000988 __ JumpIfSmi(number, &load_smi, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000989 __ fld_d(FieldOperand(number, HeapNumber::kValueOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +0000990 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +0000991
992 __ bind(&load_smi);
993 __ SmiUntag(number);
994 __ push(number);
995 __ fild_s(Operand(esp, 0));
996 __ pop(number);
997
998 __ bind(&done);
999}
1000
1001
ricow@chromium.org65fae842010-08-25 15:26:24 +00001002void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm,
1003 Label* not_numbers) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001004 Label load_smi_edx, load_eax, load_smi_eax, load_float_eax, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00001005 // Load operand in edx into xmm0, or branch to not_numbers.
whesse@chromium.org7b260152011-06-20 15:33:18 +00001006 __ JumpIfSmi(edx, &load_smi_edx, Label::kNear);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00001007 Factory* factory = masm->isolate()->factory();
1008 __ cmp(FieldOperand(edx, HeapObject::kMapOffset), factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00001009 __ j(not_equal, not_numbers); // Argument in edx is not a number.
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +00001010 __ movsd(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00001011 __ bind(&load_eax);
1012 // Load operand in eax into xmm1, or branch to not_numbers.
whesse@chromium.org7b260152011-06-20 15:33:18 +00001013 __ JumpIfSmi(eax, &load_smi_eax, Label::kNear);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00001014 __ cmp(FieldOperand(eax, HeapObject::kMapOffset), factory->heap_number_map());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001015 __ j(equal, &load_float_eax, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001016 __ jmp(not_numbers); // Argument in eax is not a number.
1017 __ bind(&load_smi_edx);
1018 __ SmiUntag(edx); // Untag smi before converting to float.
machenbach@chromium.org528ce022013-09-23 14:09:36 +00001019 __ Cvtsi2sd(xmm0, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001020 __ SmiTag(edx); // Retag smi for heap number overwriting test.
1021 __ jmp(&load_eax);
1022 __ bind(&load_smi_eax);
1023 __ SmiUntag(eax); // Untag smi before converting to float.
machenbach@chromium.org528ce022013-09-23 14:09:36 +00001024 __ Cvtsi2sd(xmm1, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001025 __ SmiTag(eax); // Retag smi for heap number overwriting test.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001026 __ jmp(&done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001027 __ bind(&load_float_eax);
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +00001028 __ movsd(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00001029 __ bind(&done);
1030}
1031
1032
ricow@chromium.org65fae842010-08-25 15:26:24 +00001033void FloatingPointHelper::CheckFloatOperands(MacroAssembler* masm,
1034 Label* non_float,
1035 Register scratch) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001036 Label test_other, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00001037 // Test if both operands are floats or smi -> scratch=k_is_float;
1038 // Otherwise scratch = k_not_float.
whesse@chromium.org7b260152011-06-20 15:33:18 +00001039 __ JumpIfSmi(edx, &test_other, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001040 __ mov(scratch, FieldOperand(edx, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00001041 Factory* factory = masm->isolate()->factory();
1042 __ cmp(scratch, factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00001043 __ j(not_equal, non_float); // argument in edx is not a number -> NaN
1044
1045 __ bind(&test_other);
whesse@chromium.org7b260152011-06-20 15:33:18 +00001046 __ JumpIfSmi(eax, &done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001047 __ mov(scratch, FieldOperand(eax, HeapObject::kMapOffset));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00001048 __ cmp(scratch, factory->heap_number_map());
ricow@chromium.org65fae842010-08-25 15:26:24 +00001049 __ j(not_equal, non_float); // argument in eax is not a number -> NaN
1050
1051 // Fall-through: Both operands are numbers.
1052 __ bind(&done);
1053}
1054
1055
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001056void MathPowStub::Generate(MacroAssembler* masm) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001057 CpuFeatureScope use_sse2(masm, SSE2);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00001058 Factory* factory = masm->isolate()->factory();
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001059 const Register exponent = eax;
1060 const Register base = edx;
1061 const Register scratch = ecx;
1062 const XMMRegister double_result = xmm3;
1063 const XMMRegister double_base = xmm2;
1064 const XMMRegister double_exponent = xmm1;
1065 const XMMRegister double_scratch = xmm4;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001066
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001067 Label call_runtime, done, exponent_not_smi, int_exponent;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001068
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001069 // Save 1 in double_result - we need this several times later on.
1070 __ mov(scratch, Immediate(1));
machenbach@chromium.org528ce022013-09-23 14:09:36 +00001071 __ Cvtsi2sd(double_result, scratch);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001072
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001073 if (exponent_type_ == ON_STACK) {
1074 Label base_is_smi, unpack_exponent;
1075 // The exponent and base are supplied as arguments on the stack.
1076 // This can only happen if the stub is called from non-optimized code.
1077 // Load input parameters from stack.
1078 __ mov(base, Operand(esp, 2 * kPointerSize));
1079 __ mov(exponent, Operand(esp, 1 * kPointerSize));
1080
1081 __ JumpIfSmi(base, &base_is_smi, Label::kNear);
1082 __ cmp(FieldOperand(base, HeapObject::kMapOffset),
1083 factory->heap_number_map());
1084 __ j(not_equal, &call_runtime);
1085
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +00001086 __ movsd(double_base, FieldOperand(base, HeapNumber::kValueOffset));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001087 __ jmp(&unpack_exponent, Label::kNear);
1088
1089 __ bind(&base_is_smi);
1090 __ SmiUntag(base);
machenbach@chromium.org528ce022013-09-23 14:09:36 +00001091 __ Cvtsi2sd(double_base, base);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001092
1093 __ bind(&unpack_exponent);
1094 __ JumpIfNotSmi(exponent, &exponent_not_smi, Label::kNear);
1095 __ SmiUntag(exponent);
1096 __ jmp(&int_exponent);
1097
1098 __ bind(&exponent_not_smi);
1099 __ cmp(FieldOperand(exponent, HeapObject::kMapOffset),
1100 factory->heap_number_map());
1101 __ j(not_equal, &call_runtime);
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +00001102 __ movsd(double_exponent,
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001103 FieldOperand(exponent, HeapNumber::kValueOffset));
1104 } else if (exponent_type_ == TAGGED) {
1105 __ JumpIfNotSmi(exponent, &exponent_not_smi, Label::kNear);
1106 __ SmiUntag(exponent);
1107 __ jmp(&int_exponent);
1108
1109 __ bind(&exponent_not_smi);
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +00001110 __ movsd(double_exponent,
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001111 FieldOperand(exponent, HeapNumber::kValueOffset));
1112 }
1113
1114 if (exponent_type_ != INTEGER) {
jkummerow@chromium.org8fa5bd92013-09-02 11:45:09 +00001115 Label fast_power, try_arithmetic_simplification;
1116 __ DoubleToI(exponent, double_exponent, double_scratch,
1117 TREAT_MINUS_ZERO_AS_ZERO, &try_arithmetic_simplification);
1118 __ jmp(&int_exponent);
1119
1120 __ bind(&try_arithmetic_simplification);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001121 // Skip to runtime if possibly NaN (indicated by the indefinite integer).
jkummerow@chromium.org8fa5bd92013-09-02 11:45:09 +00001122 __ cvttsd2si(exponent, Operand(double_exponent));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001123 __ cmp(exponent, Immediate(0x80000000u));
1124 __ j(equal, &call_runtime);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001125
1126 if (exponent_type_ == ON_STACK) {
1127 // Detect square root case. Crankshaft detects constant +/-0.5 at
1128 // compile time and uses DoMathPowHalf instead. We then skip this check
1129 // for non-constant cases of +/-0.5 as these hardly occur.
1130 Label continue_sqrt, continue_rsqrt, not_plus_half;
1131 // Test for 0.5.
1132 // Load double_scratch with 0.5.
1133 __ mov(scratch, Immediate(0x3F000000u));
1134 __ movd(double_scratch, scratch);
1135 __ cvtss2sd(double_scratch, double_scratch);
1136 // Already ruled out NaNs for exponent.
1137 __ ucomisd(double_scratch, double_exponent);
1138 __ j(not_equal, &not_plus_half, Label::kNear);
1139
1140 // Calculates square root of base. Check for the special case of
1141 // Math.pow(-Infinity, 0.5) == Infinity (ECMA spec, 15.8.2.13).
1142 // According to IEEE-754, single-precision -Infinity has the highest
1143 // 9 bits set and the lowest 23 bits cleared.
1144 __ mov(scratch, 0xFF800000u);
1145 __ movd(double_scratch, scratch);
1146 __ cvtss2sd(double_scratch, double_scratch);
1147 __ ucomisd(double_base, double_scratch);
1148 // Comparing -Infinity with NaN results in "unordered", which sets the
1149 // zero flag as if both were equal. However, it also sets the carry flag.
1150 __ j(not_equal, &continue_sqrt, Label::kNear);
1151 __ j(carry, &continue_sqrt, Label::kNear);
1152
1153 // Set result to Infinity in the special case.
1154 __ xorps(double_result, double_result);
1155 __ subsd(double_result, double_scratch);
1156 __ jmp(&done);
1157
1158 __ bind(&continue_sqrt);
1159 // sqrtsd returns -0 when input is -0. ECMA spec requires +0.
1160 __ xorps(double_scratch, double_scratch);
1161 __ addsd(double_scratch, double_base); // Convert -0 to +0.
1162 __ sqrtsd(double_result, double_scratch);
1163 __ jmp(&done);
1164
1165 // Test for -0.5.
1166 __ bind(&not_plus_half);
1167 // Load double_exponent with -0.5 by substracting 1.
1168 __ subsd(double_scratch, double_result);
1169 // Already ruled out NaNs for exponent.
1170 __ ucomisd(double_scratch, double_exponent);
1171 __ j(not_equal, &fast_power, Label::kNear);
1172
1173 // Calculates reciprocal of square root of base. Check for the special
1174 // case of Math.pow(-Infinity, -0.5) == 0 (ECMA spec, 15.8.2.13).
1175 // According to IEEE-754, single-precision -Infinity has the highest
1176 // 9 bits set and the lowest 23 bits cleared.
1177 __ mov(scratch, 0xFF800000u);
1178 __ movd(double_scratch, scratch);
1179 __ cvtss2sd(double_scratch, double_scratch);
1180 __ ucomisd(double_base, double_scratch);
1181 // Comparing -Infinity with NaN results in "unordered", which sets the
1182 // zero flag as if both were equal. However, it also sets the carry flag.
1183 __ j(not_equal, &continue_rsqrt, Label::kNear);
1184 __ j(carry, &continue_rsqrt, Label::kNear);
1185
1186 // Set result to 0 in the special case.
1187 __ xorps(double_result, double_result);
1188 __ jmp(&done);
1189
1190 __ bind(&continue_rsqrt);
1191 // sqrtsd returns -0 when input is -0. ECMA spec requires +0.
1192 __ xorps(double_exponent, double_exponent);
1193 __ addsd(double_exponent, double_base); // Convert -0 to +0.
1194 __ sqrtsd(double_exponent, double_exponent);
1195 __ divsd(double_result, double_exponent);
1196 __ jmp(&done);
1197 }
1198
1199 // Using FPU instructions to calculate power.
1200 Label fast_power_failed;
1201 __ bind(&fast_power);
1202 __ fnclex(); // Clear flags to catch exceptions later.
1203 // Transfer (B)ase and (E)xponent onto the FPU register stack.
1204 __ sub(esp, Immediate(kDoubleSize));
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +00001205 __ movsd(Operand(esp, 0), double_exponent);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001206 __ fld_d(Operand(esp, 0)); // E
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +00001207 __ movsd(Operand(esp, 0), double_base);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001208 __ fld_d(Operand(esp, 0)); // B, E
1209
1210 // Exponent is in st(1) and base is in st(0)
1211 // B ^ E = (2^(E * log2(B)) - 1) + 1 = (2^X - 1) + 1 for X = E * log2(B)
1212 // FYL2X calculates st(1) * log2(st(0))
1213 __ fyl2x(); // X
1214 __ fld(0); // X, X
1215 __ frndint(); // rnd(X), X
1216 __ fsub(1); // rnd(X), X-rnd(X)
1217 __ fxch(1); // X - rnd(X), rnd(X)
1218 // F2XM1 calculates 2^st(0) - 1 for -1 < st(0) < 1
1219 __ f2xm1(); // 2^(X-rnd(X)) - 1, rnd(X)
1220 __ fld1(); // 1, 2^(X-rnd(X)) - 1, rnd(X)
danno@chromium.org1f34ad32012-11-26 14:53:56 +00001221 __ faddp(1); // 2^(X-rnd(X)), rnd(X)
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001222 // FSCALE calculates st(0) * 2^st(1)
1223 __ fscale(); // 2^X, rnd(X)
danno@chromium.org1f34ad32012-11-26 14:53:56 +00001224 __ fstp(1); // 2^X
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001225 // Bail out to runtime in case of exceptions in the status word.
1226 __ fnstsw_ax();
1227 __ test_b(eax, 0x5F); // We check for all but precision exception.
1228 __ j(not_zero, &fast_power_failed, Label::kNear);
1229 __ fstp_d(Operand(esp, 0));
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +00001230 __ movsd(double_result, Operand(esp, 0));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001231 __ add(esp, Immediate(kDoubleSize));
1232 __ jmp(&done);
1233
1234 __ bind(&fast_power_failed);
1235 __ fninit();
1236 __ add(esp, Immediate(kDoubleSize));
1237 __ jmp(&call_runtime);
1238 }
1239
1240 // Calculate power with integer exponent.
1241 __ bind(&int_exponent);
1242 const XMMRegister double_scratch2 = double_exponent;
1243 __ mov(scratch, exponent); // Back up exponent.
1244 __ movsd(double_scratch, double_base); // Back up base.
1245 __ movsd(double_scratch2, double_result); // Load double_exponent with 1.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001246
1247 // Get absolute value of exponent.
verwaest@chromium.org33e09c82012-10-10 17:07:22 +00001248 Label no_neg, while_true, while_false;
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001249 __ test(scratch, scratch);
1250 __ j(positive, &no_neg, Label::kNear);
1251 __ neg(scratch);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001252 __ bind(&no_neg);
1253
ulan@chromium.org56c14af2012-09-20 12:51:09 +00001254 __ j(zero, &while_false, Label::kNear);
1255 __ shr(scratch, 1);
1256 // Above condition means CF==0 && ZF==0. This means that the
1257 // bit that has been shifted out is 0 and the result is not 0.
1258 __ j(above, &while_true, Label::kNear);
1259 __ movsd(double_result, double_scratch);
1260 __ j(zero, &while_false, Label::kNear);
1261
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001262 __ bind(&while_true);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001263 __ shr(scratch, 1);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001264 __ mulsd(double_scratch, double_scratch);
ulan@chromium.org56c14af2012-09-20 12:51:09 +00001265 __ j(above, &while_true, Label::kNear);
1266 __ mulsd(double_result, double_scratch);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001267 __ j(not_zero, &while_true);
1268
ulan@chromium.org56c14af2012-09-20 12:51:09 +00001269 __ bind(&while_false);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001270 // scratch has the original value of the exponent - if the exponent is
1271 // negative, return 1/result.
1272 __ test(exponent, exponent);
1273 __ j(positive, &done);
1274 __ divsd(double_scratch2, double_result);
1275 __ movsd(double_result, double_scratch2);
1276 // Test whether result is zero. Bail out to check for subnormal result.
1277 // Due to subnormals, x^-y == (1/x)^y does not hold in all cases.
1278 __ xorps(double_scratch2, double_scratch2);
1279 __ ucomisd(double_scratch2, double_result); // Result cannot be NaN.
1280 // double_exponent aliased as double_scratch2 has already been overwritten
1281 // and may not have contained the exponent value in the first place when the
1282 // exponent is a smi. We reset it with exponent value before bailing out.
1283 __ j(not_equal, &done);
machenbach@chromium.org528ce022013-09-23 14:09:36 +00001284 __ Cvtsi2sd(double_exponent, exponent);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001285
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001286 // Returning or bailing out.
1287 Counters* counters = masm->isolate()->counters();
1288 if (exponent_type_ == ON_STACK) {
1289 // The arguments are still on the stack.
1290 __ bind(&call_runtime);
1291 __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001292
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001293 // The stub is called from non-optimized code, which expects the result
1294 // as heap number in exponent.
1295 __ bind(&done);
1296 __ AllocateHeapNumber(eax, scratch, base, &call_runtime);
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +00001297 __ movsd(FieldOperand(eax, HeapNumber::kValueOffset), double_result);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001298 __ IncrementCounter(counters->math_pow(), 1);
1299 __ ret(2 * kPointerSize);
1300 } else {
1301 __ bind(&call_runtime);
1302 {
1303 AllowExternalCallThatCantCauseGC scope(masm);
1304 __ PrepareCallCFunction(4, scratch);
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +00001305 __ movsd(Operand(esp, 0 * kDoubleSize), double_base);
1306 __ movsd(Operand(esp, 1 * kDoubleSize), double_exponent);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001307 __ CallCFunction(
1308 ExternalReference::power_double_double_function(masm->isolate()), 4);
1309 }
1310 // Return value is in st(0) on ia32.
1311 // Store it into the (fixed) result register.
1312 __ sub(esp, Immediate(kDoubleSize));
1313 __ fstp_d(Operand(esp, 0));
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +00001314 __ movsd(double_result, Operand(esp, 0));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001315 __ add(esp, Immediate(kDoubleSize));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001316
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001317 __ bind(&done);
1318 __ IncrementCounter(counters->math_pow(), 1);
1319 __ ret(0);
1320 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001321}
1322
1323
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00001324void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
1325 // ----------- S t a t e -------------
1326 // -- ecx : name
1327 // -- edx : receiver
1328 // -- esp[0] : return address
1329 // -----------------------------------
1330 Label miss;
1331
1332 if (kind() == Code::KEYED_LOAD_IC) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00001333 __ cmp(ecx, Immediate(masm->isolate()->factory()->prototype_string()));
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00001334 __ j(not_equal, &miss);
1335 }
1336
1337 StubCompiler::GenerateLoadFunctionPrototype(masm, edx, eax, ebx, &miss);
1338 __ bind(&miss);
danno@chromium.orgbee51992013-07-10 14:57:15 +00001339 StubCompiler::TailCallBuiltin(
1340 masm, BaseLoadStoreStubCompiler::MissBuiltin(kind()));
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00001341}
1342
1343
1344void StringLengthStub::Generate(MacroAssembler* masm) {
1345 // ----------- S t a t e -------------
1346 // -- ecx : name
1347 // -- edx : receiver
1348 // -- esp[0] : return address
1349 // -----------------------------------
1350 Label miss;
1351
1352 if (kind() == Code::KEYED_LOAD_IC) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00001353 __ cmp(ecx, Immediate(masm->isolate()->factory()->length_string()));
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00001354 __ j(not_equal, &miss);
1355 }
1356
machenbach@chromium.org528ce022013-09-23 14:09:36 +00001357 StubCompiler::GenerateLoadStringLength(masm, edx, eax, ebx, &miss);
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00001358 __ bind(&miss);
danno@chromium.orgbee51992013-07-10 14:57:15 +00001359 StubCompiler::TailCallBuiltin(
1360 masm, BaseLoadStoreStubCompiler::MissBuiltin(kind()));
mvstanton@chromium.org6bec0092013-01-23 13:46:53 +00001361}
1362
1363
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00001364void StoreArrayLengthStub::Generate(MacroAssembler* masm) {
1365 // ----------- S t a t e -------------
1366 // -- eax : value
1367 // -- ecx : name
1368 // -- edx : receiver
1369 // -- esp[0] : return address
1370 // -----------------------------------
1371 //
1372 // This accepts as a receiver anything JSArray::SetElementsLength accepts
1373 // (currently anything except for external arrays which means anything with
1374 // elements of FixedArray type). Value must be a number, but only smis are
1375 // accepted as the most common case.
1376
1377 Label miss;
1378
1379 Register receiver = edx;
1380 Register value = eax;
1381 Register scratch = ebx;
1382
ulan@chromium.org750145a2013-03-07 15:14:13 +00001383 if (kind() == Code::KEYED_STORE_IC) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00001384 __ cmp(ecx, Immediate(masm->isolate()->factory()->length_string()));
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00001385 __ j(not_equal, &miss);
1386 }
1387
1388 // Check that the receiver isn't a smi.
1389 __ JumpIfSmi(receiver, &miss);
1390
1391 // Check that the object is a JS array.
1392 __ CmpObjectType(receiver, JS_ARRAY_TYPE, scratch);
1393 __ j(not_equal, &miss);
1394
1395 // Check that elements are FixedArray.
1396 // We rely on StoreIC_ArrayLength below to deal with all types of
1397 // fast elements (including COW).
1398 __ mov(scratch, FieldOperand(receiver, JSArray::kElementsOffset));
1399 __ CmpObjectType(scratch, FIXED_ARRAY_TYPE, scratch);
1400 __ j(not_equal, &miss);
1401
1402 // Check that the array has fast properties, otherwise the length
1403 // property might have been redefined.
1404 __ mov(scratch, FieldOperand(receiver, JSArray::kPropertiesOffset));
1405 __ CompareRoot(FieldOperand(scratch, FixedArray::kMapOffset),
1406 Heap::kHashTableMapRootIndex);
1407 __ j(equal, &miss);
1408
1409 // Check that value is a smi.
1410 __ JumpIfNotSmi(value, &miss);
1411
1412 // Prepare tail call to StoreIC_ArrayLength.
1413 __ pop(scratch);
1414 __ push(receiver);
1415 __ push(value);
1416 __ push(scratch); // return address
1417
1418 ExternalReference ref =
1419 ExternalReference(IC_Utility(IC::kStoreIC_ArrayLength), masm->isolate());
1420 __ TailCallExternalReference(ref, 2, 1);
1421
1422 __ bind(&miss);
1423
danno@chromium.orgbee51992013-07-10 14:57:15 +00001424 StubCompiler::TailCallBuiltin(
1425 masm, BaseLoadStoreStubCompiler::MissBuiltin(kind()));
mvstanton@chromium.orgd16d8532013-01-25 13:29:10 +00001426}
1427
1428
ricow@chromium.org65fae842010-08-25 15:26:24 +00001429void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
1430 // The key is in edx and the parameter count is in eax.
1431
1432 // The displacement is used for skipping the frame pointer on the
1433 // stack. It is the offset of the last parameter (if any) relative
1434 // to the frame pointer.
1435 static const int kDisplacement = 1 * kPointerSize;
1436
1437 // Check that the key is a smi.
1438 Label slow;
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001439 __ JumpIfNotSmi(edx, &slow, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001440
1441 // Check if the calling frame is an arguments adaptor frame.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001442 Label adaptor;
ricow@chromium.org65fae842010-08-25 15:26:24 +00001443 __ mov(ebx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
1444 __ mov(ecx, Operand(ebx, StandardFrameConstants::kContextOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001445 __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001446 __ j(equal, &adaptor, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001447
1448 // Check index against formal parameters count limit passed in
1449 // through register eax. Use unsigned comparison to get negative
1450 // check for free.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001451 __ cmp(edx, eax);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001452 __ j(above_equal, &slow, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001453
1454 // Read the argument from the stack and return it.
1455 STATIC_ASSERT(kSmiTagSize == 1);
1456 STATIC_ASSERT(kSmiTag == 0); // Shifting code depends on these.
1457 __ lea(ebx, Operand(ebp, eax, times_2, 0));
1458 __ neg(edx);
1459 __ mov(eax, Operand(ebx, edx, times_2, kDisplacement));
1460 __ ret(0);
1461
1462 // Arguments adaptor case: Check index against actual arguments
1463 // limit found in the arguments adaptor frame. Use unsigned
1464 // comparison to get negative check for free.
1465 __ bind(&adaptor);
1466 __ mov(ecx, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001467 __ cmp(edx, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001468 __ j(above_equal, &slow, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001469
1470 // Read the argument from the stack and return it.
1471 STATIC_ASSERT(kSmiTagSize == 1);
1472 STATIC_ASSERT(kSmiTag == 0); // Shifting code depends on these.
1473 __ lea(ebx, Operand(ebx, ecx, times_2, 0));
1474 __ neg(edx);
1475 __ mov(eax, Operand(ebx, edx, times_2, kDisplacement));
1476 __ ret(0);
1477
1478 // Slow-case: Handle non-smi or out-of-bounds access to arguments
1479 // by calling the runtime system.
1480 __ bind(&slow);
1481 __ pop(ebx); // Return address.
1482 __ push(edx);
1483 __ push(ebx);
1484 __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
1485}
1486
1487
whesse@chromium.org7b260152011-06-20 15:33:18 +00001488void ArgumentsAccessStub::GenerateNewNonStrictSlow(MacroAssembler* masm) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00001489 // esp[0] : return address
1490 // esp[4] : number of parameters
1491 // esp[8] : receiver displacement
whesse@chromium.org7b260152011-06-20 15:33:18 +00001492 // esp[12] : function
ricow@chromium.org65fae842010-08-25 15:26:24 +00001493
whesse@chromium.org7b260152011-06-20 15:33:18 +00001494 // Check if the calling frame is an arguments adaptor frame.
1495 Label runtime;
1496 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
1497 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001498 __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00001499 __ j(not_equal, &runtime, Label::kNear);
1500
1501 // Patch the arguments.length and the parameters pointer.
1502 __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
1503 __ mov(Operand(esp, 1 * kPointerSize), ecx);
1504 __ lea(edx, Operand(edx, ecx, times_2,
1505 StandardFrameConstants::kCallerSPOffset));
1506 __ mov(Operand(esp, 2 * kPointerSize), edx);
1507
1508 __ bind(&runtime);
1509 __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
1510}
1511
1512
1513void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) {
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001514 Isolate* isolate = masm->isolate();
1515
whesse@chromium.org7b260152011-06-20 15:33:18 +00001516 // esp[0] : return address
1517 // esp[4] : number of parameters (tagged)
1518 // esp[8] : receiver displacement
1519 // esp[12] : function
1520
1521 // ebx = parameter count (tagged)
1522 __ mov(ebx, Operand(esp, 1 * kPointerSize));
1523
1524 // Check if the calling frame is an arguments adaptor frame.
1525 // TODO(rossberg): Factor out some of the bits that are shared with the other
1526 // Generate* functions.
1527 Label runtime;
1528 Label adaptor_frame, try_allocate;
1529 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
1530 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001531 __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00001532 __ j(equal, &adaptor_frame, Label::kNear);
1533
1534 // No adaptor, parameter count = argument count.
1535 __ mov(ecx, ebx);
1536 __ jmp(&try_allocate, Label::kNear);
1537
1538 // We have an adaptor frame. Patch the parameters pointer.
1539 __ bind(&adaptor_frame);
1540 __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
1541 __ lea(edx, Operand(edx, ecx, times_2,
1542 StandardFrameConstants::kCallerSPOffset));
1543 __ mov(Operand(esp, 2 * kPointerSize), edx);
1544
1545 // ebx = parameter count (tagged)
1546 // ecx = argument count (tagged)
1547 // esp[4] = parameter count (tagged)
1548 // esp[8] = address of receiver argument
1549 // Compute the mapped parameter count = min(ebx, ecx) in ebx.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001550 __ cmp(ebx, ecx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00001551 __ j(less_equal, &try_allocate, Label::kNear);
1552 __ mov(ebx, ecx);
1553
1554 __ bind(&try_allocate);
1555
1556 // Save mapped parameter count.
1557 __ push(ebx);
1558
1559 // Compute the sizes of backing store, parameter map, and arguments object.
1560 // 1. Parameter map, has 2 extra words containing context and backing store.
1561 const int kParameterMapHeaderSize =
1562 FixedArray::kHeaderSize + 2 * kPointerSize;
1563 Label no_parameter_map;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001564 __ test(ebx, ebx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00001565 __ j(zero, &no_parameter_map, Label::kNear);
1566 __ lea(ebx, Operand(ebx, times_2, kParameterMapHeaderSize));
1567 __ bind(&no_parameter_map);
1568
1569 // 2. Backing store.
1570 __ lea(ebx, Operand(ebx, ecx, times_2, FixedArray::kHeaderSize));
1571
1572 // 3. Arguments object.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001573 __ add(ebx, Immediate(Heap::kArgumentsObjectSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00001574
1575 // Do the allocation of all three objects in one go.
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00001576 __ Allocate(ebx, eax, edx, edi, &runtime, TAG_OBJECT);
whesse@chromium.org7b260152011-06-20 15:33:18 +00001577
1578 // eax = address of new object(s) (tagged)
1579 // ecx = argument count (tagged)
1580 // esp[0] = mapped parameter count (tagged)
1581 // esp[8] = parameter count (tagged)
1582 // esp[12] = address of receiver argument
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00001583 // Get the arguments boilerplate from the current native context into edi.
whesse@chromium.org7b260152011-06-20 15:33:18 +00001584 Label has_mapped_parameters, copy;
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00001585 __ mov(edi, Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
1586 __ mov(edi, FieldOperand(edi, GlobalObject::kNativeContextOffset));
whesse@chromium.org7b260152011-06-20 15:33:18 +00001587 __ mov(ebx, Operand(esp, 0 * kPointerSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001588 __ test(ebx, ebx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00001589 __ j(not_zero, &has_mapped_parameters, Label::kNear);
1590 __ mov(edi, Operand(edi,
1591 Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX)));
1592 __ jmp(&copy, Label::kNear);
1593
1594 __ bind(&has_mapped_parameters);
1595 __ mov(edi, Operand(edi,
1596 Context::SlotOffset(Context::ALIASED_ARGUMENTS_BOILERPLATE_INDEX)));
1597 __ bind(&copy);
1598
1599 // eax = address of new object (tagged)
1600 // ebx = mapped parameter count (tagged)
1601 // ecx = argument count (tagged)
1602 // edi = address of boilerplate object (tagged)
1603 // esp[0] = mapped parameter count (tagged)
1604 // esp[8] = parameter count (tagged)
1605 // esp[12] = address of receiver argument
1606 // Copy the JS object part.
1607 for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
1608 __ mov(edx, FieldOperand(edi, i));
1609 __ mov(FieldOperand(eax, i), edx);
1610 }
1611
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00001612 // Set up the callee in-object property.
whesse@chromium.org7b260152011-06-20 15:33:18 +00001613 STATIC_ASSERT(Heap::kArgumentsCalleeIndex == 1);
1614 __ mov(edx, Operand(esp, 4 * kPointerSize));
1615 __ mov(FieldOperand(eax, JSObject::kHeaderSize +
1616 Heap::kArgumentsCalleeIndex * kPointerSize),
1617 edx);
1618
1619 // Use the length (smi tagged) and set that as an in-object property too.
1620 STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0);
1621 __ mov(FieldOperand(eax, JSObject::kHeaderSize +
1622 Heap::kArgumentsLengthIndex * kPointerSize),
1623 ecx);
1624
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00001625 // Set up the elements pointer in the allocated arguments object.
whesse@chromium.org7b260152011-06-20 15:33:18 +00001626 // If we allocated a parameter map, edi will point there, otherwise to the
1627 // backing store.
1628 __ lea(edi, Operand(eax, Heap::kArgumentsObjectSize));
1629 __ mov(FieldOperand(eax, JSObject::kElementsOffset), edi);
1630
1631 // eax = address of new object (tagged)
1632 // ebx = mapped parameter count (tagged)
1633 // ecx = argument count (tagged)
1634 // edi = address of parameter map or backing store (tagged)
1635 // esp[0] = mapped parameter count (tagged)
1636 // esp[8] = parameter count (tagged)
1637 // esp[12] = address of receiver argument
1638 // Free a register.
1639 __ push(eax);
1640
1641 // Initialize parameter map. If there are no mapped arguments, we're done.
1642 Label skip_parameter_map;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001643 __ test(ebx, ebx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00001644 __ j(zero, &skip_parameter_map);
1645
1646 __ mov(FieldOperand(edi, FixedArray::kMapOffset),
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001647 Immediate(isolate->factory()->non_strict_arguments_elements_map()));
whesse@chromium.org7b260152011-06-20 15:33:18 +00001648 __ lea(eax, Operand(ebx, reinterpret_cast<intptr_t>(Smi::FromInt(2))));
1649 __ mov(FieldOperand(edi, FixedArray::kLengthOffset), eax);
1650 __ mov(FieldOperand(edi, FixedArray::kHeaderSize + 0 * kPointerSize), esi);
1651 __ lea(eax, Operand(edi, ebx, times_2, kParameterMapHeaderSize));
1652 __ mov(FieldOperand(edi, FixedArray::kHeaderSize + 1 * kPointerSize), eax);
1653
1654 // Copy the parameter slots and the holes in the arguments.
1655 // We need to fill in mapped_parameter_count slots. They index the context,
1656 // where parameters are stored in reverse order, at
1657 // MIN_CONTEXT_SLOTS .. MIN_CONTEXT_SLOTS+parameter_count-1
1658 // The mapped parameter thus need to get indices
1659 // MIN_CONTEXT_SLOTS+parameter_count-1 ..
1660 // MIN_CONTEXT_SLOTS+parameter_count-mapped_parameter_count
1661 // We loop from right to left.
1662 Label parameters_loop, parameters_test;
1663 __ push(ecx);
1664 __ mov(eax, Operand(esp, 2 * kPointerSize));
1665 __ mov(ebx, Immediate(Smi::FromInt(Context::MIN_CONTEXT_SLOTS)));
1666 __ add(ebx, Operand(esp, 4 * kPointerSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001667 __ sub(ebx, eax);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001668 __ mov(ecx, isolate->factory()->the_hole_value());
whesse@chromium.org7b260152011-06-20 15:33:18 +00001669 __ mov(edx, edi);
1670 __ lea(edi, Operand(edi, eax, times_2, kParameterMapHeaderSize));
1671 // eax = loop variable (tagged)
1672 // ebx = mapping index (tagged)
1673 // ecx = the hole value
1674 // edx = address of parameter map (tagged)
1675 // edi = address of backing store (tagged)
1676 // esp[0] = argument count (tagged)
1677 // esp[4] = address of new object (tagged)
1678 // esp[8] = mapped parameter count (tagged)
1679 // esp[16] = parameter count (tagged)
1680 // esp[20] = address of receiver argument
1681 __ jmp(&parameters_test, Label::kNear);
1682
1683 __ bind(&parameters_loop);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001684 __ sub(eax, Immediate(Smi::FromInt(1)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00001685 __ mov(FieldOperand(edx, eax, times_2, kParameterMapHeaderSize), ebx);
1686 __ mov(FieldOperand(edi, eax, times_2, FixedArray::kHeaderSize), ecx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001687 __ add(ebx, Immediate(Smi::FromInt(1)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00001688 __ bind(&parameters_test);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001689 __ test(eax, eax);
whesse@chromium.org7b260152011-06-20 15:33:18 +00001690 __ j(not_zero, &parameters_loop, Label::kNear);
1691 __ pop(ecx);
1692
1693 __ bind(&skip_parameter_map);
1694
1695 // ecx = argument count (tagged)
1696 // edi = address of backing store (tagged)
1697 // esp[0] = address of new object (tagged)
1698 // esp[4] = mapped parameter count (tagged)
1699 // esp[12] = parameter count (tagged)
1700 // esp[16] = address of receiver argument
1701 // Copy arguments header and remaining slots (if there are any).
1702 __ mov(FieldOperand(edi, FixedArray::kMapOffset),
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001703 Immediate(isolate->factory()->fixed_array_map()));
whesse@chromium.org7b260152011-06-20 15:33:18 +00001704 __ mov(FieldOperand(edi, FixedArray::kLengthOffset), ecx);
1705
1706 Label arguments_loop, arguments_test;
1707 __ mov(ebx, Operand(esp, 1 * kPointerSize));
1708 __ mov(edx, Operand(esp, 4 * kPointerSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001709 __ sub(edx, ebx); // Is there a smarter way to do negative scaling?
1710 __ sub(edx, ebx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00001711 __ jmp(&arguments_test, Label::kNear);
1712
1713 __ bind(&arguments_loop);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001714 __ sub(edx, Immediate(kPointerSize));
whesse@chromium.org7b260152011-06-20 15:33:18 +00001715 __ mov(eax, Operand(edx, 0));
1716 __ mov(FieldOperand(edi, ebx, times_2, FixedArray::kHeaderSize), eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001717 __ add(ebx, Immediate(Smi::FromInt(1)));
whesse@chromium.org7b260152011-06-20 15:33:18 +00001718
1719 __ bind(&arguments_test);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001720 __ cmp(ebx, ecx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00001721 __ j(less, &arguments_loop, Label::kNear);
1722
1723 // Restore.
1724 __ pop(eax); // Address of arguments object.
1725 __ pop(ebx); // Parameter count.
1726
1727 // Return and remove the on-stack parameters.
1728 __ ret(3 * kPointerSize);
1729
1730 // Do the runtime call to allocate the arguments object.
1731 __ bind(&runtime);
1732 __ pop(eax); // Remove saved parameter count.
1733 __ mov(Operand(esp, 1 * kPointerSize), ecx); // Patch argument count.
danno@chromium.org72204d52012-10-31 10:02:10 +00001734 __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
whesse@chromium.org7b260152011-06-20 15:33:18 +00001735}
1736
1737
1738void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) {
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001739 Isolate* isolate = masm->isolate();
1740
whesse@chromium.org7b260152011-06-20 15:33:18 +00001741 // esp[0] : return address
1742 // esp[4] : number of parameters
1743 // esp[8] : receiver displacement
1744 // esp[12] : function
ricow@chromium.org65fae842010-08-25 15:26:24 +00001745
1746 // Check if the calling frame is an arguments adaptor frame.
1747 Label adaptor_frame, try_allocate, runtime;
1748 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
1749 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001750 __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001751 __ j(equal, &adaptor_frame, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001752
1753 // Get the length from the frame.
1754 __ mov(ecx, Operand(esp, 1 * kPointerSize));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001755 __ jmp(&try_allocate, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001756
1757 // Patch the arguments.length and the parameters pointer.
1758 __ bind(&adaptor_frame);
1759 __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
1760 __ mov(Operand(esp, 1 * kPointerSize), ecx);
whesse@chromium.org7b260152011-06-20 15:33:18 +00001761 __ lea(edx, Operand(edx, ecx, times_2,
1762 StandardFrameConstants::kCallerSPOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00001763 __ mov(Operand(esp, 2 * kPointerSize), edx);
1764
1765 // Try the new space allocation. Start out with computing the size of
1766 // the arguments object and the elements array.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001767 Label add_arguments_object;
ricow@chromium.org65fae842010-08-25 15:26:24 +00001768 __ bind(&try_allocate);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001769 __ test(ecx, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001770 __ j(zero, &add_arguments_object, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001771 __ lea(ecx, Operand(ecx, times_2, FixedArray::kHeaderSize));
1772 __ bind(&add_arguments_object);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001773 __ add(ecx, Immediate(Heap::kArgumentsObjectSizeStrict));
ricow@chromium.org65fae842010-08-25 15:26:24 +00001774
1775 // Do the allocation of both objects in one go.
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00001776 __ Allocate(ecx, eax, edx, ebx, &runtime, TAG_OBJECT);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001777
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00001778 // Get the arguments boilerplate from the current native context.
1779 __ mov(edi, Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
1780 __ mov(edi, FieldOperand(edi, GlobalObject::kNativeContextOffset));
whesse@chromium.org7b260152011-06-20 15:33:18 +00001781 const int offset =
1782 Context::SlotOffset(Context::STRICT_MODE_ARGUMENTS_BOILERPLATE_INDEX);
1783 __ mov(edi, Operand(edi, offset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00001784
1785 // Copy the JS object part.
1786 for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
1787 __ mov(ebx, FieldOperand(edi, i));
1788 __ mov(FieldOperand(eax, i), ebx);
1789 }
1790
ricow@chromium.org65fae842010-08-25 15:26:24 +00001791 // Get the length (smi tagged) and set that as an in-object property too.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001792 STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001793 __ mov(ecx, Operand(esp, 1 * kPointerSize));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001794 __ mov(FieldOperand(eax, JSObject::kHeaderSize +
whesse@chromium.org7b260152011-06-20 15:33:18 +00001795 Heap::kArgumentsLengthIndex * kPointerSize),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001796 ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001797
1798 // If there are no actual arguments, we're done.
1799 Label done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001800 __ test(ecx, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001801 __ j(zero, &done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001802
1803 // Get the parameters pointer from the stack.
1804 __ mov(edx, Operand(esp, 2 * kPointerSize));
1805
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00001806 // Set up the elements pointer in the allocated arguments object and
ricow@chromium.org65fae842010-08-25 15:26:24 +00001807 // initialize the header in the elements fixed array.
whesse@chromium.org7b260152011-06-20 15:33:18 +00001808 __ lea(edi, Operand(eax, Heap::kArgumentsObjectSizeStrict));
ricow@chromium.org65fae842010-08-25 15:26:24 +00001809 __ mov(FieldOperand(eax, JSObject::kElementsOffset), edi);
1810 __ mov(FieldOperand(edi, FixedArray::kMapOffset),
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00001811 Immediate(isolate->factory()->fixed_array_map()));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001812
ricow@chromium.org65fae842010-08-25 15:26:24 +00001813 __ mov(FieldOperand(edi, FixedArray::kLengthOffset), ecx);
1814 // Untag the length for the loop below.
1815 __ SmiUntag(ecx);
1816
1817 // Copy the fixed array slots.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00001818 Label loop;
ricow@chromium.org65fae842010-08-25 15:26:24 +00001819 __ bind(&loop);
1820 __ mov(ebx, Operand(edx, -1 * kPointerSize)); // Skip receiver.
1821 __ mov(FieldOperand(edi, FixedArray::kHeaderSize), ebx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001822 __ add(edi, Immediate(kPointerSize));
1823 __ sub(edx, Immediate(kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00001824 __ dec(ecx);
1825 __ j(not_zero, &loop);
1826
1827 // Return and remove the on-stack parameters.
1828 __ bind(&done);
1829 __ ret(3 * kPointerSize);
1830
1831 // Do the runtime call to allocate the arguments object.
1832 __ bind(&runtime);
whesse@chromium.org7b260152011-06-20 15:33:18 +00001833 __ TailCallRuntime(Runtime::kNewStrictArgumentsFast, 3, 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001834}
1835
1836
1837void RegExpExecStub::Generate(MacroAssembler* masm) {
1838 // Just jump directly to runtime if native RegExp is not selected at compile
1839 // time or if regexp entry in generated code is turned off runtime switch or
1840 // at compilation.
1841#ifdef V8_INTERPRETED_REGEXP
1842 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
1843#else // V8_INTERPRETED_REGEXP
ricow@chromium.org65fae842010-08-25 15:26:24 +00001844
1845 // Stack frame on entry.
1846 // esp[0]: return address
1847 // esp[4]: last_match_info (expected JSArray)
1848 // esp[8]: previous index
1849 // esp[12]: subject string
1850 // esp[16]: JSRegExp object
1851
1852 static const int kLastMatchInfoOffset = 1 * kPointerSize;
1853 static const int kPreviousIndexOffset = 2 * kPointerSize;
1854 static const int kSubjectOffset = 3 * kPointerSize;
1855 static const int kJSRegExpOffset = 4 * kPointerSize;
1856
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00001857 Label runtime;
1858 Factory* factory = masm->isolate()->factory();
ricow@chromium.org65fae842010-08-25 15:26:24 +00001859
1860 // Ensure that a RegExp stack is allocated.
1861 ExternalReference address_of_regexp_stack_memory_address =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001862 ExternalReference::address_of_regexp_stack_memory_address(
1863 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00001864 ExternalReference address_of_regexp_stack_memory_size =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001865 ExternalReference::address_of_regexp_stack_memory_size(masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00001866 __ mov(ebx, Operand::StaticVariable(address_of_regexp_stack_memory_size));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001867 __ test(ebx, ebx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00001868 __ j(zero, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001869
1870 // Check that the first argument is a JSRegExp object.
1871 __ mov(eax, Operand(esp, kJSRegExpOffset));
1872 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00001873 __ JumpIfSmi(eax, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001874 __ CmpObjectType(eax, JS_REGEXP_TYPE, ecx);
1875 __ j(not_equal, &runtime);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00001876
ricow@chromium.org65fae842010-08-25 15:26:24 +00001877 // Check that the RegExp has been compiled (data contains a fixed array).
1878 __ mov(ecx, FieldOperand(eax, JSRegExp::kDataOffset));
1879 if (FLAG_debug_code) {
1880 __ test(ecx, Immediate(kSmiTagMask));
danno@chromium.org59400602013-08-13 17:09:37 +00001881 __ Check(not_zero, kUnexpectedTypeForRegExpDataFixedArrayExpected);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001882 __ CmpObjectType(ecx, FIXED_ARRAY_TYPE, ebx);
danno@chromium.org59400602013-08-13 17:09:37 +00001883 __ Check(equal, kUnexpectedTypeForRegExpDataFixedArrayExpected);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001884 }
1885
1886 // ecx: RegExp data (FixedArray)
1887 // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP.
1888 __ mov(ebx, FieldOperand(ecx, JSRegExp::kDataTagOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001889 __ cmp(ebx, Immediate(Smi::FromInt(JSRegExp::IRREGEXP)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00001890 __ j(not_equal, &runtime);
1891
1892 // ecx: RegExp data (FixedArray)
1893 // Check that the number of captures fit in the static offsets vector buffer.
1894 __ mov(edx, FieldOperand(ecx, JSRegExp::kIrregexpCaptureCountOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00001895 // Check (number_of_captures + 1) * 2 <= offsets vector size
1896 // Or number_of_captures * 2 <= offsets vector size - 2
1897 // Multiplying by 2 comes for free since edx is smi-tagged.
ricow@chromium.org65fae842010-08-25 15:26:24 +00001898 STATIC_ASSERT(kSmiTag == 0);
1899 STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00001900 STATIC_ASSERT(Isolate::kJSRegexpStaticOffsetsVectorSize >= 2);
1901 __ cmp(edx, Isolate::kJSRegexpStaticOffsetsVectorSize - 2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001902 __ j(above, &runtime);
1903
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001904 // Reset offset for possibly sliced string.
1905 __ Set(edi, Immediate(0));
ricow@chromium.org65fae842010-08-25 15:26:24 +00001906 __ mov(eax, Operand(esp, kSubjectOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00001907 __ JumpIfSmi(eax, &runtime);
1908 __ mov(edx, eax); // Make a copy of the original subject string.
ricow@chromium.org65fae842010-08-25 15:26:24 +00001909 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
1910 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00001911
1912 // eax: subject string
1913 // edx: subject string
1914 // ebx: subject string instance type
1915 // ecx: RegExp data (FixedArray)
1916 // Handle subject string according to its encoding and representation:
1917 // (1) Sequential two byte? If yes, go to (9).
1918 // (2) Sequential one byte? If yes, go to (6).
1919 // (3) Anything but sequential or cons? If yes, go to (7).
1920 // (4) Cons string. If the string is flat, replace subject with first string.
1921 // Otherwise bailout.
1922 // (5a) Is subject sequential two byte? If yes, go to (9).
1923 // (5b) Is subject external? If yes, go to (8).
1924 // (6) One byte sequential. Load regexp code for one byte.
1925 // (E) Carry on.
1926 /// [...]
1927
1928 // Deferred code at the end of the stub:
1929 // (7) Not a long external string? If yes, go to (10).
1930 // (8) External string. Make it, offset-wise, look like a sequential string.
1931 // (8a) Is the external string one byte? If yes, go to (6).
1932 // (9) Two byte sequential. Load regexp code for one byte. Go to (E).
1933 // (10) Short external string or not a string? If yes, bail out to runtime.
1934 // (11) Sliced string. Replace subject with parent. Go to (5a).
1935
1936 Label seq_one_byte_string /* 6 */, seq_two_byte_string /* 9 */,
1937 external_string /* 8 */, check_underlying /* 5a */,
1938 not_seq_nor_cons /* 7 */, check_code /* E */,
1939 not_long_external /* 10 */;
1940
1941 // (1) Sequential two byte? If yes, go to (9).
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00001942 __ and_(ebx, kIsNotStringMask |
1943 kStringRepresentationMask |
1944 kStringEncodingMask |
1945 kShortExternalStringMask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001946 STATIC_ASSERT((kStringTag | kSeqStringTag | kTwoByteStringTag) == 0);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00001947 __ j(zero, &seq_two_byte_string); // Go to (9).
1948
1949 // (2) Sequential one byte? If yes, go to (6).
1950 // Any other sequential string must be one byte.
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00001951 __ and_(ebx, Immediate(kIsNotStringMask |
1952 kStringRepresentationMask |
1953 kShortExternalStringMask));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00001954 __ j(zero, &seq_one_byte_string, Label::kNear); // Go to (6).
ricow@chromium.org65fae842010-08-25 15:26:24 +00001955
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00001956 // (3) Anything but sequential or cons? If yes, go to (7).
1957 // We check whether the subject string is a cons, since sequential strings
1958 // have already been covered.
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00001959 STATIC_ASSERT(kConsStringTag < kExternalStringTag);
1960 STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00001961 STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
1962 STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001963 __ cmp(ebx, Immediate(kExternalStringTag));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00001964 __ j(greater_equal, &not_seq_nor_cons); // Go to (7).
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00001965
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00001966 // (4) Cons string. Check that it's flat.
1967 // Replace subject with first string and reload instance type.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00001968 __ cmp(FieldOperand(eax, ConsString::kSecondOffset), factory->empty_string());
ricow@chromium.org65fae842010-08-25 15:26:24 +00001969 __ j(not_equal, &runtime);
1970 __ mov(eax, FieldOperand(eax, ConsString::kFirstOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00001971 __ bind(&check_underlying);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001972 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00001973 __ mov(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
1974
1975 // (5a) Is subject sequential two byte? If yes, go to (9).
1976 __ test_b(ebx, kStringRepresentationMask | kStringEncodingMask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001977 STATIC_ASSERT((kSeqStringTag | kTwoByteStringTag) == 0);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00001978 __ j(zero, &seq_two_byte_string); // Go to (9).
1979 // (5b) Is subject external? If yes, go to (8).
1980 __ test_b(ebx, kStringRepresentationMask);
1981 // The underlying external string is never a short external string.
1982 STATIC_CHECK(ExternalString::kMaxShortLength < ConsString::kMinLength);
1983 STATIC_CHECK(ExternalString::kMaxShortLength < SlicedString::kMinLength);
1984 __ j(not_zero, &external_string); // Go to (8).
ricow@chromium.org65fae842010-08-25 15:26:24 +00001985
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00001986 // eax: sequential subject string (or look-alike, external string)
1987 // edx: original subject string
ricow@chromium.org65fae842010-08-25 15:26:24 +00001988 // ecx: RegExp data (FixedArray)
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00001989 // (6) One byte sequential. Load regexp code for one byte.
1990 __ bind(&seq_one_byte_string);
1991 // Load previous index and check range before edx is overwritten. We have
1992 // to use edx instead of eax here because it might have been only made to
1993 // look like a sequential string when it actually is an external string.
1994 __ mov(ebx, Operand(esp, kPreviousIndexOffset));
1995 __ JumpIfNotSmi(ebx, &runtime);
1996 __ cmp(ebx, FieldOperand(edx, String::kLengthOffset));
1997 __ j(above_equal, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00001998 __ mov(edx, FieldOperand(ecx, JSRegExp::kDataAsciiCodeOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00001999 __ Set(ecx, Immediate(1)); // Type is one byte.
ricow@chromium.org65fae842010-08-25 15:26:24 +00002000
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00002001 // (E) Carry on. String handling is done.
ricow@chromium.org65fae842010-08-25 15:26:24 +00002002 __ bind(&check_code);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00002003 // edx: irregexp code
ricow@chromium.org65fae842010-08-25 15:26:24 +00002004 // Check that the irregexp code has been generated for the actual string
2005 // encoding. If it has, the field contains a code object otherwise it contains
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00002006 // a smi (code flushing support).
2007 __ JumpIfSmi(edx, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002008
2009 // eax: subject string
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00002010 // ebx: previous index (smi)
ricow@chromium.org65fae842010-08-25 15:26:24 +00002011 // edx: code
ulan@chromium.org2efb9002012-01-19 15:36:35 +00002012 // ecx: encoding of subject string (1 if ASCII, 0 if two_byte);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002013 // All checks done. Now push arguments for native regexp code.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002014 Counters* counters = masm->isolate()->counters();
2015 __ IncrementCounter(counters->regexp_entry_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002016
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002017 // Isolates: note we add an additional parameter here (isolate pointer).
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00002018 static const int kRegExpExecuteArguments = 9;
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00002019 __ EnterApiExitFrame(kRegExpExecuteArguments);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002020
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00002021 // Argument 9: Pass current isolate address.
2022 __ mov(Operand(esp, 8 * kPointerSize),
ulan@chromium.org32d7dba2013-04-24 10:59:06 +00002023 Immediate(ExternalReference::isolate_address(masm->isolate())));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002024
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00002025 // Argument 8: Indicate that this is a direct call from JavaScript.
2026 __ mov(Operand(esp, 7 * kPointerSize), Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002027
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00002028 // Argument 7: Start (high end) of backtracking stack memory area.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00002029 __ mov(esi, Operand::StaticVariable(address_of_regexp_stack_memory_address));
2030 __ add(esi, Operand::StaticVariable(address_of_regexp_stack_memory_size));
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00002031 __ mov(Operand(esp, 6 * kPointerSize), esi);
2032
2033 // Argument 6: Set the number of capture registers to zero to force global
2034 // regexps to behave as non-global. This does not affect non-global regexps.
2035 __ mov(Operand(esp, 5 * kPointerSize), Immediate(0));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002036
2037 // Argument 5: static offsets vector buffer.
2038 __ mov(Operand(esp, 4 * kPointerSize),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002039 Immediate(ExternalReference::address_of_static_offsets_vector(
2040 masm->isolate())));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002041
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00002042 // Argument 2: Previous index.
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00002043 __ SmiUntag(ebx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00002044 __ mov(Operand(esp, 1 * kPointerSize), ebx);
2045
2046 // Argument 1: Original subject string.
2047 // The original subject is in the previous stack frame. Therefore we have to
2048 // use ebp, which points exactly to one pointer size below the previous esp.
2049 // (Because creating a new stack frame pushes the previous ebp onto the stack
2050 // and thereby moves up esp by one kPointerSize.)
2051 __ mov(esi, Operand(ebp, kSubjectOffset + kPointerSize));
2052 __ mov(Operand(esp, 0 * kPointerSize), esi);
2053
2054 // esi: original subject string
2055 // eax: underlying subject string
2056 // ebx: previous index
ulan@chromium.org2efb9002012-01-19 15:36:35 +00002057 // ecx: encoding of subject string (1 if ASCII 0 if two_byte);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00002058 // edx: code
ricow@chromium.org65fae842010-08-25 15:26:24 +00002059 // Argument 4: End of string data
2060 // Argument 3: Start of string data
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00002061 // Prepare start and end index of the input.
2062 // Load the length from the original sliced string if that is the case.
2063 __ mov(esi, FieldOperand(esi, String::kLengthOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002064 __ add(esi, edi); // Calculate input end wrt offset.
ricow@chromium.org65fae842010-08-25 15:26:24 +00002065 __ SmiUntag(edi);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002066 __ add(ebx, edi); // Calculate input start wrt offset.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00002067
2068 // ebx: start index of the input string
2069 // esi: end index of the input string
2070 Label setup_two_byte, setup_rest;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002071 __ test(ecx, ecx);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00002072 __ j(zero, &setup_two_byte, Label::kNear);
2073 __ SmiUntag(esi);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002074 __ lea(ecx, FieldOperand(eax, esi, times_1, SeqOneByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002075 __ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002076 __ lea(ecx, FieldOperand(eax, ebx, times_1, SeqOneByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002077 __ mov(Operand(esp, 2 * kPointerSize), ecx); // Argument 3.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002078 __ jmp(&setup_rest, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002079
2080 __ bind(&setup_two_byte);
2081 STATIC_ASSERT(kSmiTag == 0);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00002082 STATIC_ASSERT(kSmiTagSize == 1); // esi is smi (powered by 2).
2083 __ lea(ecx, FieldOperand(eax, esi, times_1, SeqTwoByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002084 __ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
2085 __ lea(ecx, FieldOperand(eax, ebx, times_2, SeqTwoByteString::kHeaderSize));
2086 __ mov(Operand(esp, 2 * kPointerSize), ecx); // Argument 3.
2087
2088 __ bind(&setup_rest);
2089
ricow@chromium.org65fae842010-08-25 15:26:24 +00002090 // Locate the code entry and call it.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002091 __ add(edx, Immediate(Code::kHeaderSize - kHeapObjectTag));
2092 __ call(edx);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00002093
2094 // Drop arguments and come back to JS mode.
machenbach@chromium.org528ce022013-09-23 14:09:36 +00002095 __ LeaveApiExitFrame(true);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002096
2097 // Check the result.
2098 Label success;
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00002099 __ cmp(eax, 1);
2100 // We expect exactly one result since we force the called regexp to behave
2101 // as non-global.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00002102 __ j(equal, &success);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002103 Label failure;
2104 __ cmp(eax, NativeRegExpMacroAssembler::FAILURE);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00002105 __ j(equal, &failure);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002106 __ cmp(eax, NativeRegExpMacroAssembler::EXCEPTION);
2107 // If not exception it can only be retry. Handle that in the runtime system.
2108 __ j(not_equal, &runtime);
2109 // Result must now be exception. If there is no pending exception already a
2110 // stack overflow (on the backtrack stack) was detected in RegExp code but
2111 // haven't created the exception yet. Handle that in the runtime system.
2112 // TODO(592): Rerunning the RegExp to get the stack overflow exception.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00002113 ExternalReference pending_exception(Isolate::kPendingExceptionAddress,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002114 masm->isolate());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002115 __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00002116 __ mov(eax, Operand::StaticVariable(pending_exception));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002117 __ cmp(edx, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002118 __ j(equal, &runtime);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00002119 // For exception, throw the exception again.
2120
2121 // Clear the pending exception variable.
2122 __ mov(Operand::StaticVariable(pending_exception), edx);
2123
2124 // Special handling of termination exceptions which are uncatchable
2125 // by javascript code.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002126 __ cmp(eax, factory->termination_exception());
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00002127 Label throw_termination_exception;
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00002128 __ j(equal, &throw_termination_exception, Label::kNear);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00002129
2130 // Handle normal exception by following handler chain.
2131 __ Throw(eax);
2132
2133 __ bind(&throw_termination_exception);
ulan@chromium.org65a89c22012-02-14 11:46:07 +00002134 __ ThrowUncatchable(eax);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00002135
ricow@chromium.org65fae842010-08-25 15:26:24 +00002136 __ bind(&failure);
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +00002137 // For failure to match, return null.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002138 __ mov(eax, factory->null_value());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002139 __ ret(4 * kPointerSize);
2140
2141 // Load RegExp data.
2142 __ bind(&success);
2143 __ mov(eax, Operand(esp, kJSRegExpOffset));
2144 __ mov(ecx, FieldOperand(eax, JSRegExp::kDataOffset));
2145 __ mov(edx, FieldOperand(ecx, JSRegExp::kIrregexpCaptureCountOffset));
2146 // Calculate number of capture registers (number_of_captures + 1) * 2.
2147 STATIC_ASSERT(kSmiTag == 0);
2148 STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002149 __ add(edx, Immediate(2)); // edx was a smi.
ricow@chromium.org65fae842010-08-25 15:26:24 +00002150
2151 // edx: Number of capture registers
2152 // Load last_match_info which is still known to be a fast case JSArray.
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00002153 // Check that the fourth object is a JSArray object.
ricow@chromium.org65fae842010-08-25 15:26:24 +00002154 __ mov(eax, Operand(esp, kLastMatchInfoOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00002155 __ JumpIfSmi(eax, &runtime);
2156 __ CmpObjectType(eax, JS_ARRAY_TYPE, ebx);
2157 __ j(not_equal, &runtime);
2158 // Check that the JSArray is in fast case.
ricow@chromium.org65fae842010-08-25 15:26:24 +00002159 __ mov(ebx, FieldOperand(eax, JSArray::kElementsOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00002160 __ mov(eax, FieldOperand(ebx, HeapObject::kMapOffset));
2161 __ cmp(eax, factory->fixed_array_map());
2162 __ j(not_equal, &runtime);
2163 // Check that the last match info has space for the capture registers and the
2164 // additional information.
2165 __ mov(eax, FieldOperand(ebx, FixedArray::kLengthOffset));
2166 __ SmiUntag(eax);
2167 __ sub(eax, Immediate(RegExpImpl::kLastMatchOverhead));
2168 __ cmp(edx, eax);
2169 __ j(greater, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002170
2171 // ebx: last_match_info backing store (FixedArray)
2172 // edx: number of capture registers
2173 // Store the capture count.
2174 __ SmiTag(edx); // Number of capture registers to smi.
2175 __ mov(FieldOperand(ebx, RegExpImpl::kLastCaptureCountOffset), edx);
2176 __ SmiUntag(edx); // Number of capture registers back from smi.
2177 // Store last subject and last input.
2178 __ mov(eax, Operand(esp, kSubjectOffset));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00002179 __ mov(ecx, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002180 __ mov(FieldOperand(ebx, RegExpImpl::kLastSubjectOffset), eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002181 __ RecordWriteField(ebx,
2182 RegExpImpl::kLastSubjectOffset,
2183 eax,
2184 edi,
2185 kDontSaveFPRegs);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00002186 __ mov(eax, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002187 __ mov(FieldOperand(ebx, RegExpImpl::kLastInputOffset), eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002188 __ RecordWriteField(ebx,
2189 RegExpImpl::kLastInputOffset,
2190 eax,
2191 edi,
2192 kDontSaveFPRegs);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002193
2194 // Get the static offsets vector filled by the native regexp code.
2195 ExternalReference address_of_static_offsets_vector =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002196 ExternalReference::address_of_static_offsets_vector(masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00002197 __ mov(ecx, Immediate(address_of_static_offsets_vector));
2198
2199 // ebx: last_match_info backing store (FixedArray)
2200 // ecx: offsets vector
2201 // edx: number of capture registers
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002202 Label next_capture, done;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002203 // Capture register counter starts from number of capture registers and
2204 // counts down until wraping after zero.
2205 __ bind(&next_capture);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002206 __ sub(edx, Immediate(1));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002207 __ j(negative, &done, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002208 // Read the value from the static offsets vector buffer.
2209 __ mov(edi, Operand(ecx, edx, times_int_size, 0));
2210 __ SmiTag(edi);
2211 // Store the smi value in the last match info.
2212 __ mov(FieldOperand(ebx,
2213 edx,
2214 times_pointer_size,
2215 RegExpImpl::kFirstCaptureOffset),
2216 edi);
2217 __ jmp(&next_capture);
2218 __ bind(&done);
2219
2220 // Return last match info.
2221 __ mov(eax, Operand(esp, kLastMatchInfoOffset));
2222 __ ret(4 * kPointerSize);
2223
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00002224 // Do the runtime call to execute the regexp.
2225 __ bind(&runtime);
2226 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
2227
2228 // Deferred code for string handling.
2229 // (7) Not a long external string? If yes, go to (10).
2230 __ bind(&not_seq_nor_cons);
2231 // Compare flags are still set from (3).
2232 __ j(greater, &not_long_external, Label::kNear); // Go to (10).
2233
2234 // (8) External string. Short external strings have been ruled out.
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00002235 __ bind(&external_string);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00002236 // Reload instance type.
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00002237 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
2238 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
2239 if (FLAG_debug_code) {
2240 // Assert that we do not have a cons or slice (indirect strings) here.
2241 // Sequential strings have already been ruled out.
2242 __ test_b(ebx, kIsIndirectStringMask);
danno@chromium.org59400602013-08-13 17:09:37 +00002243 __ Assert(zero, kExternalStringExpectedButNotFound);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00002244 }
2245 __ mov(eax, FieldOperand(eax, ExternalString::kResourceDataOffset));
2246 // Move the pointer so that offset-wise, it looks like a sequential string.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002247 STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00002248 __ sub(eax, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
2249 STATIC_ASSERT(kTwoByteStringTag == 0);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00002250 // (8a) Is the external string one byte? If yes, go to (6).
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00002251 __ test_b(ebx, kStringEncodingMask);
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00002252 __ j(not_zero, &seq_one_byte_string); // Goto (6).
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +00002253
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +00002254 // eax: sequential subject string (or look-alike, external string)
2255 // edx: original subject string
2256 // ecx: RegExp data (FixedArray)
2257 // (9) Two byte sequential. Load regexp code for one byte. Go to (E).
2258 __ bind(&seq_two_byte_string);
2259 // Load previous index and check range before edx is overwritten. We have
2260 // to use edx instead of eax here because it might have been only made to
2261 // look like a sequential string when it actually is an external string.
2262 __ mov(ebx, Operand(esp, kPreviousIndexOffset));
2263 __ JumpIfNotSmi(ebx, &runtime);
2264 __ cmp(ebx, FieldOperand(edx, String::kLengthOffset));
2265 __ j(above_equal, &runtime);
2266 __ mov(edx, FieldOperand(ecx, JSRegExp::kDataUC16CodeOffset));
2267 __ Set(ecx, Immediate(0)); // Type is two byte.
2268 __ jmp(&check_code); // Go to (E).
2269
2270 // (10) Not a string or a short external string? If yes, bail out to runtime.
2271 __ bind(&not_long_external);
2272 // Catch non-string subject or short external string.
2273 STATIC_ASSERT(kNotStringTag != 0 && kShortExternalStringTag !=0);
2274 __ test(ebx, Immediate(kIsNotStringMask | kShortExternalStringTag));
2275 __ j(not_zero, &runtime);
2276
2277 // (11) Sliced string. Replace subject with parent. Go to (5a).
2278 // Load offset into edi and replace subject string with parent.
2279 __ mov(edi, FieldOperand(eax, SlicedString::kOffsetOffset));
2280 __ mov(eax, FieldOperand(eax, SlicedString::kParentOffset));
2281 __ jmp(&check_underlying); // Go to (5a).
ricow@chromium.org65fae842010-08-25 15:26:24 +00002282#endif // V8_INTERPRETED_REGEXP
2283}
2284
2285
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002286void RegExpConstructResultStub::Generate(MacroAssembler* masm) {
2287 const int kMaxInlineLength = 100;
2288 Label slowcase;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002289 Label done;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002290 __ mov(ebx, Operand(esp, kPointerSize * 3));
whesse@chromium.org7b260152011-06-20 15:33:18 +00002291 __ JumpIfNotSmi(ebx, &slowcase);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002292 __ cmp(ebx, Immediate(Smi::FromInt(kMaxInlineLength)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002293 __ j(above, &slowcase);
2294 // Smi-tagging is equivalent to multiplying by 2.
2295 STATIC_ASSERT(kSmiTag == 0);
2296 STATIC_ASSERT(kSmiTagSize == 1);
2297 // Allocate RegExpResult followed by FixedArray with size in ebx.
2298 // JSArray: [Map][empty properties][Elements][Length-smi][index][input]
2299 // Elements: [Map][Length][..elements..]
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00002300 __ Allocate(JSRegExpResult::kSize + FixedArray::kHeaderSize,
2301 times_pointer_size,
2302 ebx, // In: Number of elements as a smi
2303 REGISTER_VALUE_IS_SMI,
2304 eax, // Out: Start of allocation (tagged).
2305 ecx, // Out: End of allocation.
2306 edx, // Scratch register
2307 &slowcase,
2308 TAG_OBJECT);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002309 // eax: Start of allocated area, object-tagged.
2310
2311 // Set JSArray map to global.regexp_result_map().
2312 // Set empty properties FixedArray.
2313 // Set elements to point to FixedArray allocated right after the JSArray.
2314 // Interleave operations for better latency.
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00002315 __ mov(edx, ContextOperand(esi, Context::GLOBAL_OBJECT_INDEX));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002316 Factory* factory = masm->isolate()->factory();
2317 __ mov(ecx, Immediate(factory->empty_fixed_array()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002318 __ lea(ebx, Operand(eax, JSRegExpResult::kSize));
yangguo@chromium.org46839fb2012-08-28 09:06:19 +00002319 __ mov(edx, FieldOperand(edx, GlobalObject::kNativeContextOffset));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002320 __ mov(FieldOperand(eax, JSObject::kElementsOffset), ebx);
2321 __ mov(FieldOperand(eax, JSObject::kPropertiesOffset), ecx);
2322 __ mov(edx, ContextOperand(edx, Context::REGEXP_RESULT_MAP_INDEX));
2323 __ mov(FieldOperand(eax, HeapObject::kMapOffset), edx);
2324
2325 // Set input, index and length fields from arguments.
2326 __ mov(ecx, Operand(esp, kPointerSize * 1));
2327 __ mov(FieldOperand(eax, JSRegExpResult::kInputOffset), ecx);
2328 __ mov(ecx, Operand(esp, kPointerSize * 2));
2329 __ mov(FieldOperand(eax, JSRegExpResult::kIndexOffset), ecx);
2330 __ mov(ecx, Operand(esp, kPointerSize * 3));
2331 __ mov(FieldOperand(eax, JSArray::kLengthOffset), ecx);
2332
2333 // Fill out the elements FixedArray.
2334 // eax: JSArray.
2335 // ebx: FixedArray.
2336 // ecx: Number of elements in array, as smi.
2337
2338 // Set map.
2339 __ mov(FieldOperand(ebx, HeapObject::kMapOffset),
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002340 Immediate(factory->fixed_array_map()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002341 // Set length.
2342 __ mov(FieldOperand(ebx, FixedArray::kLengthOffset), ecx);
ulan@chromium.org56c14af2012-09-20 12:51:09 +00002343 // Fill contents of fixed-array with undefined.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002344 __ SmiUntag(ecx);
ulan@chromium.org56c14af2012-09-20 12:51:09 +00002345 __ mov(edx, Immediate(factory->undefined_value()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002346 __ lea(ebx, FieldOperand(ebx, FixedArray::kHeaderSize));
ulan@chromium.org56c14af2012-09-20 12:51:09 +00002347 // Fill fixed array elements with undefined.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002348 // eax: JSArray.
2349 // ecx: Number of elements to fill.
2350 // ebx: Start of elements in FixedArray.
ulan@chromium.org56c14af2012-09-20 12:51:09 +00002351 // edx: undefined.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002352 Label loop;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002353 __ test(ecx, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002354 __ bind(&loop);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002355 __ j(less_equal, &done, Label::kNear); // Jump if ecx is negative or zero.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002356 __ sub(ecx, Immediate(1));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002357 __ mov(Operand(ebx, ecx, times_pointer_size, 0), edx);
2358 __ jmp(&loop);
2359
2360 __ bind(&done);
2361 __ ret(3 * kPointerSize);
2362
2363 __ bind(&slowcase);
2364 __ TailCallRuntime(Runtime::kRegExpConstructResult, 3, 1);
2365}
2366
2367
ricow@chromium.org65fae842010-08-25 15:26:24 +00002368static int NegativeComparisonResult(Condition cc) {
2369 ASSERT(cc != equal);
2370 ASSERT((cc == less) || (cc == less_equal)
2371 || (cc == greater) || (cc == greater_equal));
2372 return (cc == greater || cc == greater_equal) ? LESS : GREATER;
2373}
2374
ricow@chromium.org65fae842010-08-25 15:26:24 +00002375
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002376static void CheckInputType(MacroAssembler* masm,
2377 Register input,
2378 CompareIC::State expected,
2379 Label* fail) {
2380 Label ok;
2381 if (expected == CompareIC::SMI) {
2382 __ JumpIfNotSmi(input, fail);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00002383 } else if (expected == CompareIC::NUMBER) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002384 __ JumpIfSmi(input, &ok);
2385 __ cmp(FieldOperand(input, HeapObject::kMapOffset),
2386 Immediate(masm->isolate()->factory()->heap_number_map()));
2387 __ j(not_equal, fail);
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00002388 }
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00002389 // We could be strict about internalized/non-internalized here, but as long as
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002390 // hydrogen doesn't care, the stub doesn't have to care either.
2391 __ bind(&ok);
2392}
2393
2394
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00002395static void BranchIfNotInternalizedString(MacroAssembler* masm,
2396 Label* label,
2397 Register object,
2398 Register scratch) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002399 __ JumpIfSmi(object, label);
2400 __ mov(scratch, FieldOperand(object, HeapObject::kMapOffset));
2401 __ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +00002402 STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0);
2403 __ test(scratch, Immediate(kIsNotStringMask | kIsNotInternalizedMask));
2404 __ j(not_zero, label);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002405}
2406
2407
2408void ICCompareStub::GenerateGeneric(MacroAssembler* masm) {
2409 Label check_unequal_objects;
2410 Condition cc = GetCondition();
2411
2412 Label miss;
2413 CheckInputType(masm, edx, left_, &miss);
2414 CheckInputType(masm, eax, right_, &miss);
2415
2416 // Compare two smis.
2417 Label non_smi, smi_done;
2418 __ mov(ecx, edx);
2419 __ or_(ecx, eax);
2420 __ JumpIfNotSmi(ecx, &non_smi, Label::kNear);
2421 __ sub(edx, eax); // Return on the result of the subtraction.
2422 __ j(no_overflow, &smi_done, Label::kNear);
2423 __ not_(edx); // Correct sign in case of overflow. edx is never 0 here.
2424 __ bind(&smi_done);
2425 __ mov(eax, edx);
2426 __ ret(0);
2427 __ bind(&non_smi);
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00002428
ricow@chromium.org65fae842010-08-25 15:26:24 +00002429 // NOTICE! This code is only reached after a smi-fast-case check, so
2430 // it is certain that at least one operand isn't a smi.
2431
2432 // Identical objects can be compared fast, but there are some tricky cases
2433 // for NaN and undefined.
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00002434 Label generic_heap_number_comparison;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002435 {
2436 Label not_identical;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002437 __ cmp(eax, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002438 __ j(not_equal, &not_identical);
2439
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002440 if (cc != equal) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00002441 // Check for undefined. undefined OP undefined is false even though
2442 // undefined == undefined.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002443 Label check_for_nan;
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002444 __ cmp(edx, masm->isolate()->factory()->undefined_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002445 __ j(not_equal, &check_for_nan, Label::kNear);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002446 __ Set(eax, Immediate(Smi::FromInt(NegativeComparisonResult(cc))));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002447 __ ret(0);
2448 __ bind(&check_for_nan);
2449 }
2450
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00002451 // Test for NaN. Compare heap numbers in a general way,
2452 // to hanlde NaNs correctly.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002453 __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
2454 Immediate(masm->isolate()->factory()->heap_number_map()));
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00002455 __ j(equal, &generic_heap_number_comparison, Label::kNear);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002456 if (cc != equal) {
2457 // Call runtime on identical JSObjects. Otherwise return equal.
2458 __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
2459 __ j(above_equal, &not_identical);
2460 }
2461 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
2462 __ ret(0);
2463
ricow@chromium.org65fae842010-08-25 15:26:24 +00002464
2465 __ bind(&not_identical);
2466 }
2467
2468 // Strict equality can quickly decide whether objects are equal.
2469 // Non-strict object equality is slower, so it is handled later in the stub.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002470 if (cc == equal && strict()) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00002471 Label slow; // Fallthrough label.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002472 Label not_smis;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002473 // If we're doing a strict equality comparison, we don't have to do
2474 // type conversion, so we generate code to do fast comparison for objects
2475 // and oddballs. Non-smi numbers and strings still go through the usual
2476 // slow-case code.
2477 // If either is a Smi (we know that not both are), then they can only
2478 // be equal if the other is a HeapNumber. If so, use the slow case.
2479 STATIC_ASSERT(kSmiTag == 0);
2480 ASSERT_EQ(0, Smi::FromInt(0));
2481 __ mov(ecx, Immediate(kSmiTagMask));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002482 __ and_(ecx, eax);
2483 __ test(ecx, edx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002484 __ j(not_zero, &not_smis, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002485 // One operand is a smi.
2486
2487 // Check whether the non-smi is a heap number.
2488 STATIC_ASSERT(kSmiTagMask == 1);
2489 // ecx still holds eax & kSmiTag, which is either zero or one.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002490 __ sub(ecx, Immediate(0x01));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002491 __ mov(ebx, edx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002492 __ xor_(ebx, eax);
2493 __ and_(ebx, ecx); // ebx holds either 0 or eax ^ edx.
2494 __ xor_(ebx, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002495 // if eax was smi, ebx is now edx, else eax.
2496
2497 // Check if the non-smi operand is a heap number.
2498 __ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00002499 Immediate(masm->isolate()->factory()->heap_number_map()));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002500 // If heap number, handle it in the slow case.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00002501 __ j(equal, &slow, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002502 // Return non-equal (ebx is not zero)
2503 __ mov(eax, ebx);
2504 __ ret(0);
2505
2506 __ bind(&not_smis);
2507 // If either operand is a JSObject or an oddball value, then they are not
2508 // equal since their pointers are different
2509 // There is no test for undetectability in strict equality.
2510
2511 // Get the type of the first operand.
2512 // If the first object is a JS object, we have done pointer comparison.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002513 Label first_non_object;
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002514 STATIC_ASSERT(LAST_TYPE == LAST_SPEC_OBJECT_TYPE);
2515 __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002516 __ j(below, &first_non_object, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002517
2518 // Return non-zero (eax is not zero)
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002519 Label return_not_equal;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002520 STATIC_ASSERT(kHeapObjectTag != 0);
2521 __ bind(&return_not_equal);
2522 __ ret(0);
2523
2524 __ bind(&first_non_object);
2525 // Check for oddballs: true, false, null, undefined.
2526 __ CmpInstanceType(ecx, ODDBALL_TYPE);
2527 __ j(equal, &return_not_equal);
2528
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002529 __ CmpObjectType(edx, FIRST_SPEC_OBJECT_TYPE, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002530 __ j(above_equal, &return_not_equal);
2531
2532 // Check for oddballs: true, false, null, undefined.
2533 __ CmpInstanceType(ecx, ODDBALL_TYPE);
2534 __ j(equal, &return_not_equal);
2535
2536 // Fall through to the general case.
2537 __ bind(&slow);
2538 }
2539
2540 // Generate the number comparison code.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002541 Label non_number_comparison;
2542 Label unordered;
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00002543 __ bind(&generic_heap_number_comparison);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002544 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00002545 CpuFeatureScope use_sse2(masm, SSE2);
2546 CpuFeatureScope use_cmov(masm, CMOV);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002547
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002548 FloatingPointHelper::LoadSSE2Operands(masm, &non_number_comparison);
2549 __ ucomisd(xmm0, xmm1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002550
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002551 // Don't base result on EFLAGS when a NaN is involved.
2552 __ j(parity_even, &unordered, Label::kNear);
2553 // Return a result of -1, 0, or 1, based on EFLAGS.
2554 __ mov(eax, 0); // equal
2555 __ mov(ecx, Immediate(Smi::FromInt(1)));
2556 __ cmov(above, eax, ecx);
2557 __ mov(ecx, Immediate(Smi::FromInt(-1)));
2558 __ cmov(below, eax, ecx);
2559 __ ret(0);
2560 } else {
2561 FloatingPointHelper::CheckFloatOperands(
2562 masm, &non_number_comparison, ebx);
2563 FloatingPointHelper::LoadFloatOperand(masm, eax);
2564 FloatingPointHelper::LoadFloatOperand(masm, edx);
2565 __ FCmp();
ricow@chromium.org65fae842010-08-25 15:26:24 +00002566
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002567 // Don't base result on EFLAGS when a NaN is involved.
2568 __ j(parity_even, &unordered, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002569
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002570 Label below_label, above_label;
2571 // Return a result of -1, 0, or 1, based on EFLAGS.
2572 __ j(below, &below_label, Label::kNear);
2573 __ j(above, &above_label, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002574
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002575 __ Set(eax, Immediate(0));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002576 __ ret(0);
2577
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002578 __ bind(&below_label);
2579 __ mov(eax, Immediate(Smi::FromInt(-1)));
2580 __ ret(0);
2581
2582 __ bind(&above_label);
2583 __ mov(eax, Immediate(Smi::FromInt(1)));
2584 __ ret(0);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002585 }
2586
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002587 // If one of the numbers was NaN, then the result is always false.
2588 // The cc is never not-equal.
2589 __ bind(&unordered);
2590 ASSERT(cc != not_equal);
2591 if (cc == less || cc == less_equal) {
2592 __ mov(eax, Immediate(Smi::FromInt(1)));
2593 } else {
2594 __ mov(eax, Immediate(Smi::FromInt(-1)));
2595 }
2596 __ ret(0);
2597
2598 // The number comparison code did not provide a valid result.
2599 __ bind(&non_number_comparison);
2600
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00002601 // Fast negative check for internalized-to-internalized equality.
ricow@chromium.org65fae842010-08-25 15:26:24 +00002602 Label check_for_strings;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002603 if (cc == equal) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00002604 BranchIfNotInternalizedString(masm, &check_for_strings, eax, ecx);
2605 BranchIfNotInternalizedString(masm, &check_for_strings, edx, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002606
2607 // We've already checked for object identity, so if both operands
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00002608 // are internalized they aren't equal. Register eax already holds a
ricow@chromium.org65fae842010-08-25 15:26:24 +00002609 // non-zero value, which indicates not equal, so just return.
2610 __ ret(0);
2611 }
2612
2613 __ bind(&check_for_strings);
2614
2615 __ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx,
2616 &check_unequal_objects);
2617
ulan@chromium.org2efb9002012-01-19 15:36:35 +00002618 // Inline comparison of ASCII strings.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002619 if (cc == equal) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00002620 StringCompareStub::GenerateFlatAsciiStringEquals(masm,
ricow@chromium.org65fae842010-08-25 15:26:24 +00002621 edx,
2622 eax,
2623 ecx,
lrn@chromium.org1c092762011-05-09 09:42:16 +00002624 ebx);
2625 } else {
2626 StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
2627 edx,
2628 eax,
2629 ecx,
2630 ebx,
2631 edi);
2632 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00002633#ifdef DEBUG
danno@chromium.org59400602013-08-13 17:09:37 +00002634 __ Abort(kUnexpectedFallThroughFromStringComparison);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002635#endif
2636
2637 __ bind(&check_unequal_objects);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002638 if (cc == equal && !strict()) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00002639 // Non-strict equality. Objects are unequal if
2640 // they are both JSObjects and not undetectable,
2641 // and their pointers are different.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002642 Label not_both_objects;
2643 Label return_unequal;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002644 // At most one is a smi, so we can test for smi by adding the two.
2645 // A smi plus a heap object has the low bit set, a heap object plus
2646 // a heap object has the low bit clear.
2647 STATIC_ASSERT(kSmiTag == 0);
2648 STATIC_ASSERT(kSmiTagMask == 1);
2649 __ lea(ecx, Operand(eax, edx, times_1, 0));
2650 __ test(ecx, Immediate(kSmiTagMask));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002651 __ j(not_zero, &not_both_objects, Label::kNear);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002652 __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002653 __ j(below, &not_both_objects, Label::kNear);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002654 __ CmpObjectType(edx, FIRST_SPEC_OBJECT_TYPE, ebx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002655 __ j(below, &not_both_objects, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002656 // We do not bail out after this point. Both are JSObjects, and
2657 // they are equal if and only if both are undetectable.
2658 // The and of the undetectable flags is 1 if and only if they are equal.
2659 __ test_b(FieldOperand(ecx, Map::kBitFieldOffset),
2660 1 << Map::kIsUndetectable);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002661 __ j(zero, &return_unequal, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002662 __ test_b(FieldOperand(ebx, Map::kBitFieldOffset),
2663 1 << Map::kIsUndetectable);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00002664 __ j(zero, &return_unequal, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002665 // The objects are both undetectable, so they both compare as the value
2666 // undefined, and are equal.
2667 __ Set(eax, Immediate(EQUAL));
2668 __ bind(&return_unequal);
2669 // Return non-equal by returning the non-zero object pointer in eax,
2670 // or return equal if we fell through to here.
2671 __ ret(0); // rax, rdx were pushed
2672 __ bind(&not_both_objects);
2673 }
2674
2675 // Push arguments below the return address.
2676 __ pop(ecx);
2677 __ push(edx);
2678 __ push(eax);
2679
2680 // Figure out which native to call and setup the arguments.
2681 Builtins::JavaScript builtin;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002682 if (cc == equal) {
2683 builtin = strict() ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002684 } else {
2685 builtin = Builtins::COMPARE;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002686 __ push(Immediate(Smi::FromInt(NegativeComparisonResult(cc))));
ricow@chromium.org65fae842010-08-25 15:26:24 +00002687 }
2688
2689 // Restore return address on the stack.
2690 __ push(ecx);
2691
2692 // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
2693 // tagged as a small integer.
2694 __ InvokeBuiltin(builtin, JUMP_FUNCTION);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002695
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00002696 __ bind(&miss);
2697 GenerateMiss(masm);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002698}
2699
2700
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00002701static void GenerateRecordCallTarget(MacroAssembler* masm) {
2702 // Cache the called function in a global property cell. Cache states
2703 // are uninitialized, monomorphic (indicated by a JSFunction), and
2704 // megamorphic.
machenbach@chromium.org528ce022013-09-23 14:09:36 +00002705 // eax : number of arguments to the construct function
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00002706 // ebx : cache cell for call target
2707 // edi : the function to call
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00002708 Isolate* isolate = masm->isolate();
2709 Label initialize, done, miss, megamorphic, not_array_function;
2710
2711 // Load the cache state into ecx.
danno@chromium.org41728482013-06-12 22:31:22 +00002712 __ mov(ecx, FieldOperand(ebx, Cell::kValueOffset));
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00002713
2714 // A monomorphic cache hit or an already megamorphic state: invoke the
2715 // function without changing the state.
2716 __ cmp(ecx, edi);
2717 __ j(equal, &done);
2718 __ cmp(ecx, Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate)));
2719 __ j(equal, &done);
2720
danno@chromium.orgbee51992013-07-10 14:57:15 +00002721 // If we came here, we need to see if we are the array function.
2722 // If we didn't have a matching function, and we didn't find the megamorph
2723 // sentinel, then we have in the cell either some other function or an
2724 // AllocationSite. Do a map check on the object in ecx.
machenbach@chromium.org528ce022013-09-23 14:09:36 +00002725 Handle<Map> allocation_site_map =
2726 masm->isolate()->factory()->allocation_site_map();
danno@chromium.orgbee51992013-07-10 14:57:15 +00002727 __ cmp(FieldOperand(ecx, 0), Immediate(allocation_site_map));
2728 __ j(not_equal, &miss);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00002729
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00002730 // Load the global or builtins object from the current context
2731 __ LoadGlobalContext(ecx);
2732 // Make sure the function is the Array() function
2733 __ cmp(edi, Operand(ecx,
2734 Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX)));
2735 __ j(not_equal, &megamorphic);
2736 __ jmp(&done);
2737
2738 __ bind(&miss);
2739
2740 // A monomorphic miss (i.e, here the cache is not uninitialized) goes
2741 // megamorphic.
2742 __ cmp(ecx, Immediate(TypeFeedbackCells::UninitializedSentinel(isolate)));
2743 __ j(equal, &initialize);
2744 // MegamorphicSentinel is an immortal immovable object (undefined) so no
2745 // write-barrier is needed.
2746 __ bind(&megamorphic);
danno@chromium.org41728482013-06-12 22:31:22 +00002747 __ mov(FieldOperand(ebx, Cell::kValueOffset),
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00002748 Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate)));
2749 __ jmp(&done, Label::kNear);
2750
2751 // An uninitialized cache is patched with the function or sentinel to
2752 // indicate the ElementsKind if function is the Array constructor.
2753 __ bind(&initialize);
2754 __ LoadGlobalContext(ecx);
2755 // Make sure the function is the Array() function
2756 __ cmp(edi, Operand(ecx,
2757 Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX)));
2758 __ j(not_equal, &not_array_function);
2759
danno@chromium.orgbee51992013-07-10 14:57:15 +00002760 // The target function is the Array constructor,
2761 // Create an AllocationSite if we don't already have it, store it in the cell
2762 {
2763 FrameScope scope(masm, StackFrame::INTERNAL);
2764
machenbach@chromium.org528ce022013-09-23 14:09:36 +00002765 // Arguments register must be smi-tagged to call out.
verwaest@chromium.org662436e2013-08-28 08:41:27 +00002766 __ SmiTag(eax);
danno@chromium.orgbee51992013-07-10 14:57:15 +00002767 __ push(eax);
2768 __ push(edi);
2769 __ push(ebx);
2770
2771 CreateAllocationSiteStub create_stub;
2772 __ CallStub(&create_stub);
2773
2774 __ pop(ebx);
2775 __ pop(edi);
2776 __ pop(eax);
verwaest@chromium.org662436e2013-08-28 08:41:27 +00002777 __ SmiUntag(eax);
danno@chromium.orgbee51992013-07-10 14:57:15 +00002778 }
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00002779 __ jmp(&done);
2780
2781 __ bind(&not_array_function);
danno@chromium.org41728482013-06-12 22:31:22 +00002782 __ mov(FieldOperand(ebx, Cell::kValueOffset), edi);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00002783 // No need for a write barrier here - cells are rescanned.
2784
2785 __ bind(&done);
2786}
2787
2788
ricow@chromium.org65fae842010-08-25 15:26:24 +00002789void CallFunctionStub::Generate(MacroAssembler* masm) {
danno@chromium.orgfa458e42012-02-01 10:48:36 +00002790 // ebx : cache cell for call target
danno@chromium.orgc612e022011-11-10 11:38:15 +00002791 // edi : the function to call
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002792 Isolate* isolate = masm->isolate();
lrn@chromium.org34e60782011-09-15 07:25:40 +00002793 Label slow, non_function;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002794
danno@chromium.org40cb8782011-05-25 07:58:50 +00002795 // The receiver might implicitly be the global object. This is
2796 // indicated by passing the hole as the receiver to the call
2797 // function stub.
2798 if (ReceiverMightBeImplicit()) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002799 Label receiver_ok;
ricow@chromium.org65fae842010-08-25 15:26:24 +00002800 // Get the receiver from the stack.
2801 // +1 ~ return address
ricow@chromium.org65fae842010-08-25 15:26:24 +00002802 __ mov(eax, Operand(esp, (argc_ + 1) * kPointerSize));
danno@chromium.org40cb8782011-05-25 07:58:50 +00002803 // Call as function is indicated with the hole.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002804 __ cmp(eax, isolate->factory()->the_hole_value());
2805 __ j(not_equal, &receiver_ok, Label::kNear);
danno@chromium.org40cb8782011-05-25 07:58:50 +00002806 // Patch the receiver on the stack with the global receiver object.
danno@chromium.orgfa458e42012-02-01 10:48:36 +00002807 __ mov(ecx, GlobalObjectOperand());
2808 __ mov(ecx, FieldOperand(ecx, GlobalObject::kGlobalReceiverOffset));
2809 __ mov(Operand(esp, (argc_ + 1) * kPointerSize), ecx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002810 __ bind(&receiver_ok);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002811 }
2812
ricow@chromium.org65fae842010-08-25 15:26:24 +00002813 // Check that the function really is a JavaScript function.
lrn@chromium.org34e60782011-09-15 07:25:40 +00002814 __ JumpIfSmi(edi, &non_function);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002815 // Goto slow case if we do not have a function.
2816 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00002817 __ j(not_equal, &slow);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002818
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002819 if (RecordCallTarget()) {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00002820 GenerateRecordCallTarget(masm);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002821 }
2822
ricow@chromium.org65fae842010-08-25 15:26:24 +00002823 // Fast-case: Just invoke the function.
2824 ParameterCount actual(argc_);
danno@chromium.org40cb8782011-05-25 07:58:50 +00002825
2826 if (ReceiverMightBeImplicit()) {
2827 Label call_as_function;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002828 __ cmp(eax, isolate->factory()->the_hole_value());
danno@chromium.org40cb8782011-05-25 07:58:50 +00002829 __ j(equal, &call_as_function);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002830 __ InvokeFunction(edi,
2831 actual,
2832 JUMP_FUNCTION,
2833 NullCallWrapper(),
2834 CALL_AS_METHOD);
danno@chromium.org40cb8782011-05-25 07:58:50 +00002835 __ bind(&call_as_function);
2836 }
2837 __ InvokeFunction(edi,
2838 actual,
2839 JUMP_FUNCTION,
2840 NullCallWrapper(),
2841 CALL_AS_FUNCTION);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002842
2843 // Slow-case: Non-function called.
2844 __ bind(&slow);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002845 if (RecordCallTarget()) {
2846 // If there is a call target cache, mark it megamorphic in the
danno@chromium.orgfa458e42012-02-01 10:48:36 +00002847 // non-function case. MegamorphicSentinel is an immortal immovable
2848 // object (undefined) so no write barrier is needed.
danno@chromium.org41728482013-06-12 22:31:22 +00002849 __ mov(FieldOperand(ebx, Cell::kValueOffset),
danno@chromium.orgfa458e42012-02-01 10:48:36 +00002850 Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate)));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002851 }
lrn@chromium.org34e60782011-09-15 07:25:40 +00002852 // Check for function proxy.
2853 __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
2854 __ j(not_equal, &non_function);
2855 __ pop(ecx);
2856 __ push(edi); // put proxy as additional argument under return address
2857 __ push(ecx);
2858 __ Set(eax, Immediate(argc_ + 1));
2859 __ Set(ebx, Immediate(0));
2860 __ SetCallKind(ecx, CALL_AS_FUNCTION);
2861 __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY);
2862 {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002863 Handle<Code> adaptor = isolate->builtins()->ArgumentsAdaptorTrampoline();
lrn@chromium.org34e60782011-09-15 07:25:40 +00002864 __ jmp(adaptor, RelocInfo::CODE_TARGET);
2865 }
2866
ricow@chromium.org65fae842010-08-25 15:26:24 +00002867 // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
2868 // of the original receiver from the call site).
lrn@chromium.org34e60782011-09-15 07:25:40 +00002869 __ bind(&non_function);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002870 __ mov(Operand(esp, (argc_ + 1) * kPointerSize), edi);
2871 __ Set(eax, Immediate(argc_));
2872 __ Set(ebx, Immediate(0));
lrn@chromium.org34e60782011-09-15 07:25:40 +00002873 __ SetCallKind(ecx, CALL_AS_METHOD);
ricow@chromium.org65fae842010-08-25 15:26:24 +00002874 __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002875 Handle<Code> adaptor = isolate->builtins()->ArgumentsAdaptorTrampoline();
ricow@chromium.org65fae842010-08-25 15:26:24 +00002876 __ jmp(adaptor, RelocInfo::CODE_TARGET);
2877}
2878
2879
danno@chromium.orgfa458e42012-02-01 10:48:36 +00002880void CallConstructStub::Generate(MacroAssembler* masm) {
2881 // eax : number of arguments
2882 // ebx : cache cell for call target
2883 // edi : constructor function
2884 Label slow, non_function_call;
2885
2886 // Check that function is not a smi.
2887 __ JumpIfSmi(edi, &non_function_call);
2888 // Check that function is a JSFunction.
2889 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
2890 __ j(not_equal, &slow);
2891
2892 if (RecordCallTarget()) {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00002893 GenerateRecordCallTarget(masm);
danno@chromium.orgfa458e42012-02-01 10:48:36 +00002894 }
2895
2896 // Jump to the function-specific construct stub.
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00002897 Register jmp_reg = ecx;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00002898 __ mov(jmp_reg, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
2899 __ mov(jmp_reg, FieldOperand(jmp_reg,
2900 SharedFunctionInfo::kConstructStubOffset));
2901 __ lea(jmp_reg, FieldOperand(jmp_reg, Code::kHeaderSize));
2902 __ jmp(jmp_reg);
danno@chromium.orgfa458e42012-02-01 10:48:36 +00002903
2904 // edi: called object
2905 // eax: number of arguments
2906 // ecx: object map
2907 Label do_call;
2908 __ bind(&slow);
2909 __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
2910 __ j(not_equal, &non_function_call);
2911 __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY_AS_CONSTRUCTOR);
2912 __ jmp(&do_call);
2913
2914 __ bind(&non_function_call);
2915 __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
2916 __ bind(&do_call);
2917 // Set expected number of arguments to zero (not changing eax).
2918 __ Set(ebx, Immediate(0));
2919 Handle<Code> arguments_adaptor =
2920 masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
2921 __ SetCallKind(ecx, CALL_AS_METHOD);
2922 __ jmp(arguments_adaptor, RelocInfo::CODE_TARGET);
2923}
2924
2925
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002926bool CEntryStub::NeedsImmovableCode() {
2927 return false;
2928}
2929
2930
dslomov@chromium.orge97852d2013-09-12 09:02:59 +00002931bool CEntryStub::IsPregenerated(Isolate* isolate) {
2932 return (!save_doubles_ || isolate->fp_stubs_generated()) &&
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002933 result_size_ == 1;
2934}
2935
2936
hpayer@chromium.org8432c912013-02-28 15:55:26 +00002937void CodeStub::GenerateStubsAheadOfTime(Isolate* isolate) {
2938 CEntryStub::GenerateAheadOfTime(isolate);
2939 StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(isolate);
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00002940 StubFailureTrampolineStub::GenerateAheadOfTime(isolate);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002941 // It is important that the store buffer overflow stubs are generated first.
hpayer@chromium.org8432c912013-02-28 15:55:26 +00002942 RecordWriteStub::GenerateFixedRegStubsAheadOfTime(isolate);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00002943 ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate);
danno@chromium.orgbee51992013-07-10 14:57:15 +00002944 CreateAllocationSiteStub::GenerateAheadOfTime(isolate);
mvstanton@chromium.org182d2db2013-10-10 11:03:05 +00002945 if (Serializer::enabled()) {
2946 PlatformFeatureScope sse2(SSE2);
2947 BinaryOpStub::GenerateAheadOfTime(isolate);
2948 } else {
2949 BinaryOpStub::GenerateAheadOfTime(isolate);
2950 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002951}
2952
2953
hpayer@chromium.org8432c912013-02-28 15:55:26 +00002954void CodeStub::GenerateFPStubs(Isolate* isolate) {
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00002955 if (CpuFeatures::IsSupported(SSE2)) {
2956 CEntryStub save_doubles(1, kSaveFPRegs);
2957 // Stubs might already be in the snapshot, detect that and don't regenerate,
2958 // which would lead to code stub initialization state being messed up.
2959 Code* save_doubles_code;
hpayer@chromium.org8432c912013-02-28 15:55:26 +00002960 if (!save_doubles.FindCodeInCache(&save_doubles_code, isolate)) {
2961 save_doubles_code = *(save_doubles.GetCode(isolate));
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00002962 }
2963 save_doubles_code->set_is_pregenerated(true);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00002964 isolate->set_fp_stubs_generated(true);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00002965 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002966}
2967
2968
hpayer@chromium.org8432c912013-02-28 15:55:26 +00002969void CEntryStub::GenerateAheadOfTime(Isolate* isolate) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002970 CEntryStub stub(1, kDontSaveFPRegs);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00002971 Handle<Code> code = stub.GetCode(isolate);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002972 code->set_is_pregenerated(true);
2973}
2974
2975
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00002976static void JumpIfOOM(MacroAssembler* masm,
2977 Register value,
2978 Register scratch,
2979 Label* oom_label) {
2980 __ mov(scratch, value);
2981 STATIC_ASSERT(Failure::OUT_OF_MEMORY_EXCEPTION == 3);
2982 STATIC_ASSERT(kFailureTag == 3);
2983 __ and_(scratch, 0xf);
2984 __ cmp(scratch, 0xf);
2985 __ j(equal, oom_label);
2986}
2987
2988
ricow@chromium.org65fae842010-08-25 15:26:24 +00002989void CEntryStub::GenerateCore(MacroAssembler* masm,
2990 Label* throw_normal_exception,
2991 Label* throw_termination_exception,
2992 Label* throw_out_of_memory_exception,
2993 bool do_gc,
ager@chromium.org0ee099b2011-01-25 14:06:47 +00002994 bool always_allocate_scope) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00002995 // eax: result parameter for PerformGC, if any
2996 // ebx: pointer to C function (C callee-saved)
2997 // ebp: frame pointer (restored after C call)
2998 // esp: stack pointer (restored after C call)
2999 // edi: number of arguments including receiver (C callee-saved)
3000 // esi: pointer to the first argument (C callee-saved)
3001
3002 // Result returned in eax, or eax+edx if result_size_ is 2.
3003
3004 // Check stack alignment.
3005 if (FLAG_debug_code) {
3006 __ CheckStackAlignment();
3007 }
3008
3009 if (do_gc) {
3010 // Pass failure code returned from last attempt as first argument to
3011 // PerformGC. No need to use PrepareCallCFunction/CallCFunction here as the
3012 // stack alignment is known to be correct. This function takes one argument
3013 // which is passed on the stack, and we know that the stack has been
3014 // prepared to pass at least one argument.
machenbach@chromium.org528ce022013-09-23 14:09:36 +00003015 __ mov(Operand(esp, 1 * kPointerSize),
3016 Immediate(ExternalReference::isolate_address(masm->isolate())));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003017 __ mov(Operand(esp, 0 * kPointerSize), eax); // Result.
3018 __ call(FUNCTION_ADDR(Runtime::PerformGC), RelocInfo::RUNTIME_ENTRY);
3019 }
3020
3021 ExternalReference scope_depth =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003022 ExternalReference::heap_always_allocate_scope_depth(masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003023 if (always_allocate_scope) {
3024 __ inc(Operand::StaticVariable(scope_depth));
3025 }
3026
3027 // Call C function.
3028 __ mov(Operand(esp, 0 * kPointerSize), edi); // argc.
3029 __ mov(Operand(esp, 1 * kPointerSize), esi); // argv.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003030 __ mov(Operand(esp, 2 * kPointerSize),
ulan@chromium.org32d7dba2013-04-24 10:59:06 +00003031 Immediate(ExternalReference::isolate_address(masm->isolate())));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003032 __ call(ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003033 // Result is in eax or edx:eax - do not destroy these registers!
3034
3035 if (always_allocate_scope) {
3036 __ dec(Operand::StaticVariable(scope_depth));
3037 }
3038
danno@chromium.orgca29dd82013-04-26 11:59:48 +00003039 // Runtime functions should not return 'the hole'. Allowing it to escape may
3040 // lead to crashes in the IC code later.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003041 if (FLAG_debug_code) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003042 Label okay;
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003043 __ cmp(eax, masm->isolate()->factory()->the_hole_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003044 __ j(not_equal, &okay, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003045 __ int3();
3046 __ bind(&okay);
3047 }
3048
3049 // Check for failure result.
3050 Label failure_returned;
3051 STATIC_ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
3052 __ lea(ecx, Operand(eax, 1));
3053 // Lower 2 bits of ecx are 0 iff eax has failure tag.
3054 __ test(ecx, Immediate(kFailureTagMask));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00003055 __ j(zero, &failure_returned);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003056
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003057 ExternalReference pending_exception_address(
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00003058 Isolate::kPendingExceptionAddress, masm->isolate());
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00003059
3060 // Check that there is no pending exception, otherwise we
3061 // should have returned some failure value.
3062 if (FLAG_debug_code) {
3063 __ push(edx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003064 __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003065 Label okay;
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00003066 __ cmp(edx, Operand::StaticVariable(pending_exception_address));
3067 // Cannot use check here as it attempts to generate call into runtime.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003068 __ j(equal, &okay, Label::kNear);
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +00003069 __ int3();
3070 __ bind(&okay);
3071 __ pop(edx);
3072 }
3073
ricow@chromium.org65fae842010-08-25 15:26:24 +00003074 // Exit the JavaScript to C++ exit frame.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003075 __ LeaveExitFrame(save_doubles_ == kSaveFPRegs);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003076 __ ret(0);
3077
3078 // Handling of failure.
3079 __ bind(&failure_returned);
3080
3081 Label retry;
3082 // If the returned exception is RETRY_AFTER_GC continue at retry label
3083 STATIC_ASSERT(Failure::RETRY_AFTER_GC == 0);
3084 __ test(eax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003085 __ j(zero, &retry, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003086
3087 // Special handling of out of memory exceptions.
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00003088 JumpIfOOM(masm, eax, ecx, throw_out_of_memory_exception);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003089
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00003090 // Retrieve the pending exception.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003091 __ mov(eax, Operand::StaticVariable(pending_exception_address));
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00003092
3093 // See if we just retrieved an OOM exception.
3094 JumpIfOOM(masm, eax, ecx, throw_out_of_memory_exception);
3095
3096 // Clear the pending exception.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003097 __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003098 __ mov(Operand::StaticVariable(pending_exception_address), edx);
3099
3100 // Special handling of termination exceptions which are uncatchable
3101 // by javascript code.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003102 __ cmp(eax, masm->isolate()->factory()->termination_exception());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003103 __ j(equal, throw_termination_exception);
3104
3105 // Handle normal exception.
3106 __ jmp(throw_normal_exception);
3107
3108 // Retry.
3109 __ bind(&retry);
3110}
3111
3112
ricow@chromium.org65fae842010-08-25 15:26:24 +00003113void CEntryStub::Generate(MacroAssembler* masm) {
3114 // eax: number of arguments including receiver
3115 // ebx: pointer to C function (C callee-saved)
3116 // ebp: frame pointer (restored after C call)
3117 // esp: stack pointer (restored after C call)
3118 // esi: current context (C callee-saved)
3119 // edi: JS function of the caller (C callee-saved)
3120
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00003121 ProfileEntryHookStub::MaybeCallEntryHook(masm);
3122
ricow@chromium.org65fae842010-08-25 15:26:24 +00003123 // NOTE: Invocations of builtins may return failure objects instead
3124 // of a proper result. The builtin entry handles this by performing
3125 // a garbage collection and retrying the builtin (twice).
3126
3127 // Enter the exit frame that transitions from JavaScript to C++.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003128 __ EnterExitFrame(save_doubles_ == kSaveFPRegs);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003129
3130 // eax: result parameter for PerformGC, if any (setup below)
3131 // ebx: pointer to builtin function (C callee-saved)
3132 // ebp: frame pointer (restored after C call)
3133 // esp: stack pointer (restored after C call)
3134 // edi: number of arguments including receiver (C callee-saved)
3135 // esi: argv pointer (C callee-saved)
3136
3137 Label throw_normal_exception;
3138 Label throw_termination_exception;
3139 Label throw_out_of_memory_exception;
3140
3141 // Call into the runtime system.
3142 GenerateCore(masm,
3143 &throw_normal_exception,
3144 &throw_termination_exception,
3145 &throw_out_of_memory_exception,
3146 false,
3147 false);
3148
3149 // Do space-specific GC and retry runtime call.
3150 GenerateCore(masm,
3151 &throw_normal_exception,
3152 &throw_termination_exception,
3153 &throw_out_of_memory_exception,
3154 true,
3155 false);
3156
3157 // Do full GC and retry runtime call one final time.
3158 Failure* failure = Failure::InternalError();
3159 __ mov(eax, Immediate(reinterpret_cast<int32_t>(failure)));
3160 GenerateCore(masm,
3161 &throw_normal_exception,
3162 &throw_termination_exception,
3163 &throw_out_of_memory_exception,
3164 true,
3165 true);
3166
3167 __ bind(&throw_out_of_memory_exception);
ulan@chromium.org65a89c22012-02-14 11:46:07 +00003168 // Set external caught exception to false.
3169 Isolate* isolate = masm->isolate();
3170 ExternalReference external_caught(Isolate::kExternalCaughtExceptionAddress,
3171 isolate);
3172 __ mov(Operand::StaticVariable(external_caught), Immediate(false));
3173
3174 // Set pending exception and eax to out of memory exception.
3175 ExternalReference pending_exception(Isolate::kPendingExceptionAddress,
3176 isolate);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00003177 Label already_have_failure;
3178 JumpIfOOM(masm, eax, ecx, &already_have_failure);
3179 __ mov(eax, reinterpret_cast<int32_t>(Failure::OutOfMemoryException(0x1)));
3180 __ bind(&already_have_failure);
ulan@chromium.org65a89c22012-02-14 11:46:07 +00003181 __ mov(Operand::StaticVariable(pending_exception), eax);
3182 // Fall through to the next label.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003183
3184 __ bind(&throw_termination_exception);
ulan@chromium.org65a89c22012-02-14 11:46:07 +00003185 __ ThrowUncatchable(eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003186
3187 __ bind(&throw_normal_exception);
ulan@chromium.org65a89c22012-02-14 11:46:07 +00003188 __ Throw(eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003189}
3190
3191
3192void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00003193 Label invoke, handler_entry, exit;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003194 Label not_outermost_js, not_outermost_js_2;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003195
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00003196 ProfileEntryHookStub::MaybeCallEntryHook(masm);
3197
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00003198 // Set up frame.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003199 __ push(ebp);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003200 __ mov(ebp, esp);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003201
3202 // Push marker in two places.
3203 int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
3204 __ push(Immediate(Smi::FromInt(marker))); // context slot
3205 __ push(Immediate(Smi::FromInt(marker))); // function slot
3206 // Save callee-saved registers (C calling conventions).
3207 __ push(edi);
3208 __ push(esi);
3209 __ push(ebx);
3210
3211 // Save copies of the top frame descriptor on the stack.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00003212 ExternalReference c_entry_fp(Isolate::kCEntryFPAddress, masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003213 __ push(Operand::StaticVariable(c_entry_fp));
3214
ricow@chromium.org65fae842010-08-25 15:26:24 +00003215 // If this is the outermost JS call, set js_entry_sp value.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00003216 ExternalReference js_entry_sp(Isolate::kJSEntrySPAddress,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003217 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003218 __ cmp(Operand::StaticVariable(js_entry_sp), Immediate(0));
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003219 __ j(not_equal, &not_outermost_js, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003220 __ mov(Operand::StaticVariable(js_entry_sp), ebp);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00003221 __ push(Immediate(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME)));
danno@chromium.org2c26cb12012-05-03 09:06:43 +00003222 __ jmp(&invoke, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003223 __ bind(&not_outermost_js);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00003224 __ push(Immediate(Smi::FromInt(StackFrame::INNER_JSENTRY_FRAME)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003225
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00003226 // Jump to a faked try block that does the invoke, with a faked catch
3227 // block that sets the pending exception.
3228 __ jmp(&invoke);
3229 __ bind(&handler_entry);
3230 handler_offset_ = handler_entry.pos();
3231 // Caught exception: Store result (exception) in the pending exception
3232 // field in the JSEnv and return a failure sentinel.
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00003233 ExternalReference pending_exception(Isolate::kPendingExceptionAddress,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003234 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003235 __ mov(Operand::StaticVariable(pending_exception), eax);
3236 __ mov(eax, reinterpret_cast<int32_t>(Failure::Exception()));
3237 __ jmp(&exit);
3238
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00003239 // Invoke: Link this frame into the handler chain. There's only one
3240 // handler block in this code object, so its index is 0.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003241 __ bind(&invoke);
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +00003242 __ PushTryHandler(StackHandler::JS_ENTRY, 0);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003243
3244 // Clear any pending exceptions.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003245 __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003246 __ mov(Operand::StaticVariable(pending_exception), edx);
3247
3248 // Fake a receiver (NULL).
3249 __ push(Immediate(0)); // receiver
3250
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00003251 // Invoke the function by calling through JS entry trampoline builtin and
3252 // pop the faked function when we return. Notice that we cannot store a
3253 // reference to the trampoline code directly in this stub, because the
3254 // builtin stubs may not have been generated yet.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003255 if (is_construct) {
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00003256 ExternalReference construct_entry(Builtins::kJSConstructEntryTrampoline,
3257 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003258 __ mov(edx, Immediate(construct_entry));
3259 } else {
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003260 ExternalReference entry(Builtins::kJSEntryTrampoline,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003261 masm->isolate());
ricow@chromium.org65fae842010-08-25 15:26:24 +00003262 __ mov(edx, Immediate(entry));
3263 }
3264 __ mov(edx, Operand(edx, 0)); // deref address
3265 __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003266 __ call(edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003267
3268 // Unlink this frame from the handler chain.
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00003269 __ PopTryHandler();
ricow@chromium.org65fae842010-08-25 15:26:24 +00003270
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00003271 __ bind(&exit);
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00003272 // Check if the current stack frame is marked as the outermost JS frame.
3273 __ pop(ebx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003274 __ cmp(ebx, Immediate(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003275 __ j(not_equal, &not_outermost_js_2);
3276 __ mov(Operand::StaticVariable(js_entry_sp), Immediate(0));
3277 __ bind(&not_outermost_js_2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003278
3279 // Restore the top frame descriptor from the stack.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003280 __ pop(Operand::StaticVariable(ExternalReference(
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00003281 Isolate::kCEntryFPAddress,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003282 masm->isolate())));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003283
3284 // Restore callee-saved registers (C calling conventions).
3285 __ pop(ebx);
3286 __ pop(esi);
3287 __ pop(edi);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003288 __ add(esp, Immediate(2 * kPointerSize)); // remove markers
ricow@chromium.org65fae842010-08-25 15:26:24 +00003289
3290 // Restore frame pointer and return.
3291 __ pop(ebp);
3292 __ ret(0);
3293}
3294
3295
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003296// Generate stub code for instanceof.
3297// This code can patch a call site inlined cache of the instance of check,
3298// which looks like this.
3299//
3300// 81 ff XX XX XX XX cmp edi, <the hole, patched to a map>
3301// 75 0a jne <some near label>
3302// b8 XX XX XX XX mov eax, <the hole, patched to either true or false>
3303//
3304// If call site patching is requested the stack will have the delta from the
3305// return address to the cmp instruction just below the return address. This
3306// also means that call site patching can only take place with arguments in
3307// registers. TOS looks like this when call site patching is requested
3308//
3309// esp[0] : return address
3310// esp[4] : delta from return address to cmp instruction
3311//
ricow@chromium.org65fae842010-08-25 15:26:24 +00003312void InstanceofStub::Generate(MacroAssembler* masm) {
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003313 // Call site inlining and patching implies arguments in registers.
3314 ASSERT(HasArgsInRegisters() || !HasCallSiteInlineCheck());
3315
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003316 // Fixed register usage throughout the stub.
3317 Register object = eax; // Object (lhs).
3318 Register map = ebx; // Map of the object.
3319 Register function = edx; // Function (rhs).
3320 Register prototype = edi; // Prototype of the function.
3321 Register scratch = ecx;
3322
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003323 // Constants describing the call site code to patch.
3324 static const int kDeltaToCmpImmediate = 2;
3325 static const int kDeltaToMov = 8;
3326 static const int kDeltaToMovImmediate = 9;
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00003327 static const int8_t kCmpEdiOperandByte1 = BitCast<int8_t, uint8_t>(0x3b);
3328 static const int8_t kCmpEdiOperandByte2 = BitCast<int8_t, uint8_t>(0x3d);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003329 static const int8_t kMovEaxImmediateByte = BitCast<int8_t, uint8_t>(0xb8);
3330
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003331 ASSERT_EQ(object.code(), InstanceofStub::left().code());
3332 ASSERT_EQ(function.code(), InstanceofStub::right().code());
3333
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003334 // Get the object and function - they are always both needed.
3335 Label slow, not_js_object;
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003336 if (!HasArgsInRegisters()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003337 __ mov(object, Operand(esp, 2 * kPointerSize));
3338 __ mov(function, Operand(esp, 1 * kPointerSize));
3339 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00003340
3341 // Check that the left hand is a JS object.
whesse@chromium.org7b260152011-06-20 15:33:18 +00003342 __ JumpIfSmi(object, &not_js_object);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003343 __ IsObjectJSObjectType(object, map, scratch, &not_js_object);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003344
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003345 // If there is a call site cache don't look in the global cache, but do the
3346 // real lookup and update the call site cache.
3347 if (!HasCallSiteInlineCheck()) {
3348 // Look up the function and the map in the instanceof cache.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003349 Label miss;
danno@chromium.org59400602013-08-13 17:09:37 +00003350 __ CompareRoot(function, scratch, Heap::kInstanceofCacheFunctionRootIndex);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003351 __ j(not_equal, &miss, Label::kNear);
danno@chromium.org59400602013-08-13 17:09:37 +00003352 __ CompareRoot(map, scratch, Heap::kInstanceofCacheMapRootIndex);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003353 __ j(not_equal, &miss, Label::kNear);
danno@chromium.org59400602013-08-13 17:09:37 +00003354 __ LoadRoot(eax, Heap::kInstanceofCacheAnswerRootIndex);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003355 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
3356 __ bind(&miss);
3357 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00003358
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003359 // Get the prototype of the function.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00003360 __ TryGetFunctionPrototype(function, prototype, scratch, &slow, true);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003361
3362 // Check that the function prototype is a JS object.
whesse@chromium.org7b260152011-06-20 15:33:18 +00003363 __ JumpIfSmi(prototype, &slow);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003364 __ IsObjectJSObjectType(prototype, scratch, scratch, &slow);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003365
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003366 // Update the global instanceof or call site inlined cache with the current
3367 // map and function. The cached answer will be set when it is known below.
3368 if (!HasCallSiteInlineCheck()) {
danno@chromium.org59400602013-08-13 17:09:37 +00003369 __ StoreRoot(map, scratch, Heap::kInstanceofCacheMapRootIndex);
3370 __ StoreRoot(function, scratch, Heap::kInstanceofCacheFunctionRootIndex);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003371 } else {
3372 // The constants for the code patching are based on no push instructions
3373 // at the call site.
3374 ASSERT(HasArgsInRegisters());
3375 // Get return address and delta to inlined map check.
3376 __ mov(scratch, Operand(esp, 0 * kPointerSize));
3377 __ sub(scratch, Operand(esp, 1 * kPointerSize));
3378 if (FLAG_debug_code) {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00003379 __ cmpb(Operand(scratch, 0), kCmpEdiOperandByte1);
danno@chromium.org59400602013-08-13 17:09:37 +00003380 __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheCmp1);
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00003381 __ cmpb(Operand(scratch, 1), kCmpEdiOperandByte2);
danno@chromium.org59400602013-08-13 17:09:37 +00003382 __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheCmp2);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003383 }
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00003384 __ mov(scratch, Operand(scratch, kDeltaToCmpImmediate));
3385 __ mov(Operand(scratch, 0), map);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003386 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00003387
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003388 // Loop through the prototype chain of the object looking for the function
3389 // prototype.
3390 __ mov(scratch, FieldOperand(map, Map::kPrototypeOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003391 Label loop, is_instance, is_not_instance;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003392 __ bind(&loop);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003393 __ cmp(scratch, prototype);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003394 __ j(equal, &is_instance, Label::kNear);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003395 Factory* factory = masm->isolate()->factory();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003396 __ cmp(scratch, Immediate(factory->null_value()));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003397 __ j(equal, &is_not_instance, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003398 __ mov(scratch, FieldOperand(scratch, HeapObject::kMapOffset));
3399 __ mov(scratch, FieldOperand(scratch, Map::kPrototypeOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003400 __ jmp(&loop);
3401
3402 __ bind(&is_instance);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003403 if (!HasCallSiteInlineCheck()) {
danno@chromium.org59400602013-08-13 17:09:37 +00003404 __ mov(eax, Immediate(0));
3405 __ StoreRoot(eax, scratch, Heap::kInstanceofCacheAnswerRootIndex);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003406 } else {
3407 // Get return address and delta to inlined map check.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003408 __ mov(eax, factory->true_value());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003409 __ mov(scratch, Operand(esp, 0 * kPointerSize));
3410 __ sub(scratch, Operand(esp, 1 * kPointerSize));
3411 if (FLAG_debug_code) {
3412 __ cmpb(Operand(scratch, kDeltaToMov), kMovEaxImmediateByte);
danno@chromium.org59400602013-08-13 17:09:37 +00003413 __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheMov);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003414 }
3415 __ mov(Operand(scratch, kDeltaToMovImmediate), eax);
3416 if (!ReturnTrueFalseObject()) {
3417 __ Set(eax, Immediate(0));
3418 }
3419 }
3420 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003421
3422 __ bind(&is_not_instance);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003423 if (!HasCallSiteInlineCheck()) {
danno@chromium.org59400602013-08-13 17:09:37 +00003424 __ mov(eax, Immediate(Smi::FromInt(1)));
3425 __ StoreRoot(eax, scratch, Heap::kInstanceofCacheAnswerRootIndex);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003426 } else {
3427 // Get return address and delta to inlined map check.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003428 __ mov(eax, factory->false_value());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003429 __ mov(scratch, Operand(esp, 0 * kPointerSize));
3430 __ sub(scratch, Operand(esp, 1 * kPointerSize));
3431 if (FLAG_debug_code) {
3432 __ cmpb(Operand(scratch, kDeltaToMov), kMovEaxImmediateByte);
danno@chromium.org59400602013-08-13 17:09:37 +00003433 __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheMov);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003434 }
3435 __ mov(Operand(scratch, kDeltaToMovImmediate), eax);
3436 if (!ReturnTrueFalseObject()) {
3437 __ Set(eax, Immediate(Smi::FromInt(1)));
3438 }
3439 }
3440 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003441
3442 Label object_not_null, object_not_null_or_smi;
3443 __ bind(&not_js_object);
3444 // Before null, smi and string value checks, check that the rhs is a function
3445 // as for a non-function rhs an exception needs to be thrown.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003446 __ JumpIfSmi(function, &slow, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003447 __ CmpObjectType(function, JS_FUNCTION_TYPE, scratch);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003448 __ j(not_equal, &slow, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003449
3450 // Null is not instance of anything.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003451 __ cmp(object, factory->null_value());
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003452 __ j(not_equal, &object_not_null, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003453 __ Set(eax, Immediate(Smi::FromInt(1)));
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003454 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003455
3456 __ bind(&object_not_null);
3457 // Smi values is not instance of anything.
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003458 __ JumpIfNotSmi(object, &object_not_null_or_smi, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003459 __ Set(eax, Immediate(Smi::FromInt(1)));
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003460 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003461
3462 __ bind(&object_not_null_or_smi);
3463 // String values is not instance of anything.
3464 Condition is_string = masm->IsObjectStringType(object, scratch, scratch);
ricow@chromium.org4668a2c2011-08-29 10:41:00 +00003465 __ j(NegateCondition(is_string), &slow, Label::kNear);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003466 __ Set(eax, Immediate(Smi::FromInt(1)));
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003467 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003468
3469 // Slow-case: Go through the JavaScript implementation.
3470 __ bind(&slow);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003471 if (!ReturnTrueFalseObject()) {
3472 // Tail call the builtin which returns 0 or 1.
3473 if (HasArgsInRegisters()) {
3474 // Push arguments below return address.
3475 __ pop(scratch);
3476 __ push(object);
3477 __ push(function);
3478 __ push(scratch);
3479 }
3480 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
3481 } else {
3482 // Call the builtin and convert 0/1 to true/false.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003483 {
3484 FrameScope scope(masm, StackFrame::INTERNAL);
3485 __ push(object);
3486 __ push(function);
3487 __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION);
3488 }
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003489 Label true_value, done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003490 __ test(eax, eax);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003491 __ j(zero, &true_value, Label::kNear);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003492 __ mov(eax, factory->false_value());
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003493 __ jmp(&done, Label::kNear);
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003494 __ bind(&true_value);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003495 __ mov(eax, factory->true_value());
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003496 __ bind(&done);
3497 __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003498 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00003499}
3500
3501
kmillikin@chromium.orgd2c22f02011-01-10 08:15:37 +00003502Register InstanceofStub::left() { return eax; }
3503
3504
3505Register InstanceofStub::right() { return edx; }
3506
3507
ricow@chromium.org65fae842010-08-25 15:26:24 +00003508// -------------------------------------------------------------------------
3509// StringCharCodeAtGenerator
3510
3511void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00003512 // If the receiver is a smi trigger the non-string case.
3513 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003514 __ JumpIfSmi(object_, receiver_not_string_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003515
3516 // Fetch the instance type of the receiver into result register.
3517 __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset));
3518 __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
3519 // If the receiver is not a string trigger the non-string case.
3520 __ test(result_, Immediate(kIsNotStringMask));
3521 __ j(not_zero, receiver_not_string_);
3522
3523 // If the index is non-smi trigger the non-smi case.
3524 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00003525 __ JumpIfNotSmi(index_, &index_not_smi_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003526 __ bind(&got_smi_index_);
3527
3528 // Check for index out of range.
danno@chromium.orgc612e022011-11-10 11:38:15 +00003529 __ cmp(index_, FieldOperand(object_, String::kLengthOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003530 __ j(above_equal, index_out_of_range_);
3531
danno@chromium.orgc612e022011-11-10 11:38:15 +00003532 __ SmiUntag(index_);
erikcorry0ad885c2011-11-21 13:51:57 +00003533
3534 Factory* factory = masm->isolate()->factory();
3535 StringCharLoadGenerator::Generate(
3536 masm, factory, object_, index_, result_, &call_runtime_);
3537
ricow@chromium.org65fae842010-08-25 15:26:24 +00003538 __ SmiTag(result_);
3539 __ bind(&exit_);
3540}
3541
3542
3543void StringCharCodeAtGenerator::GenerateSlow(
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003544 MacroAssembler* masm,
3545 const RuntimeCallHelper& call_helper) {
danno@chromium.org59400602013-08-13 17:09:37 +00003546 __ Abort(kUnexpectedFallthroughToCharCodeAtSlowCase);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003547
3548 // Index is not a smi.
3549 __ bind(&index_not_smi_);
3550 // If index is a heap number, try converting it to an integer.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003551 __ CheckMap(index_,
3552 masm->isolate()->factory()->heap_number_map(),
3553 index_not_number_,
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +00003554 DONT_DO_SMI_CHECK);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003555 call_helper.BeforeCall(masm);
3556 __ push(object_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003557 __ push(index_); // Consumed by runtime conversion function.
3558 if (index_flags_ == STRING_INDEX_IS_NUMBER) {
3559 __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1);
3560 } else {
3561 ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
3562 // NumberToSmi discards numbers that are not exact integers.
3563 __ CallRuntime(Runtime::kNumberToSmi, 1);
3564 }
danno@chromium.orgc612e022011-11-10 11:38:15 +00003565 if (!index_.is(eax)) {
ricow@chromium.org65fae842010-08-25 15:26:24 +00003566 // Save the conversion result before the pop instructions below
3567 // have a chance to overwrite it.
danno@chromium.orgc612e022011-11-10 11:38:15 +00003568 __ mov(index_, eax);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003569 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00003570 __ pop(object_);
3571 // Reload the instance type.
3572 __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset));
3573 __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
3574 call_helper.AfterCall(masm);
3575 // If index is still not a smi, it must be out of range.
3576 STATIC_ASSERT(kSmiTag == 0);
danno@chromium.orgc612e022011-11-10 11:38:15 +00003577 __ JumpIfNotSmi(index_, index_out_of_range_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003578 // Otherwise, return to the fast path.
3579 __ jmp(&got_smi_index_);
3580
3581 // Call runtime. We get here when the receiver is a string and the
3582 // index is a number, but the code of getting the actual character
3583 // is too complex (e.g., when the string needs to be flattened).
3584 __ bind(&call_runtime_);
3585 call_helper.BeforeCall(masm);
3586 __ push(object_);
erikcorry0ad885c2011-11-21 13:51:57 +00003587 __ SmiTag(index_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003588 __ push(index_);
3589 __ CallRuntime(Runtime::kStringCharCodeAt, 2);
3590 if (!result_.is(eax)) {
3591 __ mov(result_, eax);
3592 }
3593 call_helper.AfterCall(masm);
3594 __ jmp(&exit_);
3595
danno@chromium.org59400602013-08-13 17:09:37 +00003596 __ Abort(kUnexpectedFallthroughFromCharCodeAtSlowCase);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003597}
3598
3599
3600// -------------------------------------------------------------------------
3601// StringCharFromCodeGenerator
3602
3603void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
3604 // Fast case of Heap::LookupSingleCharacterStringFromCode.
3605 STATIC_ASSERT(kSmiTag == 0);
3606 STATIC_ASSERT(kSmiShiftSize == 0);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00003607 ASSERT(IsPowerOf2(String::kMaxOneByteCharCode + 1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003608 __ test(code_,
3609 Immediate(kSmiTagMask |
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00003610 ((~String::kMaxOneByteCharCode) << kSmiTagSize)));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00003611 __ j(not_zero, &slow_case_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003612
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003613 Factory* factory = masm->isolate()->factory();
3614 __ Set(result_, Immediate(factory->single_character_string_cache()));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003615 STATIC_ASSERT(kSmiTag == 0);
3616 STATIC_ASSERT(kSmiTagSize == 1);
3617 STATIC_ASSERT(kSmiShiftSize == 0);
ulan@chromium.org2efb9002012-01-19 15:36:35 +00003618 // At this point code register contains smi tagged ASCII char code.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003619 __ mov(result_, FieldOperand(result_,
3620 code_, times_half_pointer_size,
3621 FixedArray::kHeaderSize));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003622 __ cmp(result_, factory->undefined_value());
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00003623 __ j(equal, &slow_case_);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003624 __ bind(&exit_);
3625}
3626
3627
3628void StringCharFromCodeGenerator::GenerateSlow(
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00003629 MacroAssembler* masm,
3630 const RuntimeCallHelper& call_helper) {
danno@chromium.org59400602013-08-13 17:09:37 +00003631 __ Abort(kUnexpectedFallthroughToCharFromCodeSlowCase);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003632
3633 __ bind(&slow_case_);
3634 call_helper.BeforeCall(masm);
3635 __ push(code_);
3636 __ CallRuntime(Runtime::kCharFromCode, 1);
3637 if (!result_.is(eax)) {
3638 __ mov(result_, eax);
3639 }
3640 call_helper.AfterCall(masm);
3641 __ jmp(&exit_);
3642
danno@chromium.org59400602013-08-13 17:09:37 +00003643 __ Abort(kUnexpectedFallthroughFromCharFromCodeSlowCase);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003644}
3645
3646
ricow@chromium.org65fae842010-08-25 15:26:24 +00003647void StringAddStub::Generate(MacroAssembler* masm) {
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003648 Label call_runtime, call_builtin;
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00003649 Builtins::JavaScript builtin_id = Builtins::ADD;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003650
3651 // Load the two arguments.
3652 __ mov(eax, Operand(esp, 2 * kPointerSize)); // First argument.
3653 __ mov(edx, Operand(esp, 1 * kPointerSize)); // Second argument.
3654
3655 // Make sure that both arguments are strings if not known in advance.
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +00003656 // Otherwise, at least one of the arguments is definitely a string,
3657 // and we convert the one that is not known to be a string.
3658 if ((flags_ & STRING_ADD_CHECK_BOTH) == STRING_ADD_CHECK_BOTH) {
3659 ASSERT((flags_ & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT);
3660 ASSERT((flags_ & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003661 __ JumpIfSmi(eax, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003662 __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ebx);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003663 __ j(above_equal, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003664
3665 // First argument is a a string, test second.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003666 __ JumpIfSmi(edx, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003667 __ CmpObjectType(edx, FIRST_NONSTRING_TYPE, ebx);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003668 __ j(above_equal, &call_runtime);
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +00003669 } else if ((flags_ & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT) {
3670 ASSERT((flags_ & STRING_ADD_CHECK_RIGHT) == 0);
3671 GenerateConvertArgument(masm, 2 * kPointerSize, eax, ebx, ecx, edi,
3672 &call_builtin);
3673 builtin_id = Builtins::STRING_ADD_RIGHT;
3674 } else if ((flags_ & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT) {
3675 ASSERT((flags_ & STRING_ADD_CHECK_LEFT) == 0);
3676 GenerateConvertArgument(masm, 1 * kPointerSize, edx, ebx, ecx, edi,
3677 &call_builtin);
3678 builtin_id = Builtins::STRING_ADD_LEFT;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003679 }
3680
3681 // Both arguments are strings.
3682 // eax: first string
3683 // edx: second string
3684 // Check if either of the strings are empty. In that case return the other.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003685 Label second_not_zero_length, both_not_zero_length;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003686 __ mov(ecx, FieldOperand(edx, String::kLengthOffset));
3687 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003688 __ test(ecx, ecx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003689 __ j(not_zero, &second_not_zero_length, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003690 // Second string is empty, result is first string which is already in eax.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003691 Counters* counters = masm->isolate()->counters();
3692 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003693 __ ret(2 * kPointerSize);
3694 __ bind(&second_not_zero_length);
3695 __ mov(ebx, FieldOperand(eax, String::kLengthOffset));
3696 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003697 __ test(ebx, ebx);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00003698 __ j(not_zero, &both_not_zero_length, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003699 // First string is empty, result is second string which is in edx.
3700 __ mov(eax, edx);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003701 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003702 __ ret(2 * kPointerSize);
3703
3704 // Both strings are non-empty.
3705 // eax: first string
3706 // ebx: length of first string as a smi
3707 // ecx: length of second string as a smi
3708 // edx: second string
3709 // Look at the length of the result of adding the two strings.
3710 Label string_add_flat_result, longer_than_two;
3711 __ bind(&both_not_zero_length);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003712 __ add(ebx, ecx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003713 STATIC_ASSERT(Smi::kMaxValue == String::kMaxLength);
3714 // Handle exceptionally long strings in the runtime system.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003715 __ j(overflow, &call_runtime);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00003716 // Use the string table when adding two one character strings, as it
3717 // helps later optimizations to return an internalized string here.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003718 __ cmp(ebx, Immediate(Smi::FromInt(2)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003719 __ j(not_equal, &longer_than_two);
3720
ulan@chromium.org2efb9002012-01-19 15:36:35 +00003721 // Check that both strings are non-external ASCII strings.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003722 __ JumpIfNotBothSequentialAsciiStrings(eax, edx, ebx, ecx, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003723
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00003724 // Get the two characters forming the new string.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003725 __ movzx_b(ebx, FieldOperand(eax, SeqOneByteString::kHeaderSize));
3726 __ movzx_b(ecx, FieldOperand(edx, SeqOneByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003727
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00003728 // Try to lookup two character string in string table. If it is not found
ricow@chromium.org65fae842010-08-25 15:26:24 +00003729 // just allocate a new one.
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00003730 Label make_two_character_string, make_two_character_string_no_reload;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00003731 StringHelper::GenerateTwoCharacterStringTableProbe(
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00003732 masm, ebx, ecx, eax, edx, edi,
3733 &make_two_character_string_no_reload, &make_two_character_string);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003734 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003735 __ ret(2 * kPointerSize);
3736
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00003737 // Allocate a two character string.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003738 __ bind(&make_two_character_string);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00003739 // Reload the arguments.
3740 __ mov(eax, Operand(esp, 2 * kPointerSize)); // First argument.
3741 __ mov(edx, Operand(esp, 1 * kPointerSize)); // Second argument.
3742 // Get the two characters forming the new string.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003743 __ movzx_b(ebx, FieldOperand(eax, SeqOneByteString::kHeaderSize));
3744 __ movzx_b(ecx, FieldOperand(edx, SeqOneByteString::kHeaderSize));
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00003745 __ bind(&make_two_character_string_no_reload);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003746 __ IncrementCounter(counters->string_add_make_two_char(), 1);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003747 __ AllocateAsciiString(eax, 2, edi, edx, &call_runtime);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00003748 // Pack both characters in ebx.
3749 __ shl(ecx, kBitsPerByte);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003750 __ or_(ebx, ecx);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00003751 // Set the characters in the new string.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003752 __ mov_w(FieldOperand(eax, SeqOneByteString::kHeaderSize), ebx);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003753 __ IncrementCounter(counters->string_add_native(), 1);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00003754 __ ret(2 * kPointerSize);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003755
3756 __ bind(&longer_than_two);
3757 // Check if resulting string will be flat.
ulan@chromium.org2efb9002012-01-19 15:36:35 +00003758 __ cmp(ebx, Immediate(Smi::FromInt(ConsString::kMinLength)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003759 __ j(below, &string_add_flat_result);
3760
3761 // If result is not supposed to be flat allocate a cons string object. If both
ulan@chromium.org2efb9002012-01-19 15:36:35 +00003762 // strings are ASCII the result is an ASCII cons string.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003763 Label non_ascii, allocated, ascii_data;
3764 __ mov(edi, FieldOperand(eax, HeapObject::kMapOffset));
3765 __ movzx_b(ecx, FieldOperand(edi, Map::kInstanceTypeOffset));
3766 __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
3767 __ movzx_b(edi, FieldOperand(edi, Map::kInstanceTypeOffset));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003768 __ and_(ecx, edi);
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +00003769 STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00003770 STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
3771 __ test(ecx, Immediate(kStringEncodingMask));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003772 __ j(zero, &non_ascii);
3773 __ bind(&ascii_data);
ulan@chromium.org2efb9002012-01-19 15:36:35 +00003774 // Allocate an ASCII cons string.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003775 __ AllocateAsciiConsString(ecx, edi, no_reg, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003776 __ bind(&allocated);
3777 // Fill the fields of the cons string.
svenpanne@chromium.orgc859c4f2012-10-15 11:51:39 +00003778 __ AssertSmi(ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003779 __ mov(FieldOperand(ecx, ConsString::kLengthOffset), ebx);
3780 __ mov(FieldOperand(ecx, ConsString::kHashFieldOffset),
3781 Immediate(String::kEmptyHashField));
ulan@chromium.org57ff8812013-05-10 08:16:55 +00003782
3783 Label skip_write_barrier, after_writing;
3784 ExternalReference high_promotion_mode = ExternalReference::
3785 new_space_high_promotion_mode_active_address(masm->isolate());
3786 __ test(Operand::StaticVariable(high_promotion_mode), Immediate(1));
3787 __ j(zero, &skip_write_barrier);
3788
3789 __ mov(FieldOperand(ecx, ConsString::kFirstOffset), eax);
3790 __ RecordWriteField(ecx,
3791 ConsString::kFirstOffset,
3792 eax,
3793 ebx,
3794 kDontSaveFPRegs);
3795 __ mov(FieldOperand(ecx, ConsString::kSecondOffset), edx);
3796 __ RecordWriteField(ecx,
3797 ConsString::kSecondOffset,
3798 edx,
3799 ebx,
3800 kDontSaveFPRegs);
3801 __ jmp(&after_writing);
3802
3803 __ bind(&skip_write_barrier);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003804 __ mov(FieldOperand(ecx, ConsString::kFirstOffset), eax);
3805 __ mov(FieldOperand(ecx, ConsString::kSecondOffset), edx);
ulan@chromium.org57ff8812013-05-10 08:16:55 +00003806
3807 __ bind(&after_writing);
3808
ricow@chromium.org65fae842010-08-25 15:26:24 +00003809 __ mov(eax, ecx);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003810 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003811 __ ret(2 * kPointerSize);
3812 __ bind(&non_ascii);
3813 // At least one of the strings is two-byte. Check whether it happens
danno@chromium.orgca29dd82013-04-26 11:59:48 +00003814 // to contain only one byte characters.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003815 // ecx: first instance type AND second instance type.
3816 // edi: second instance type.
danno@chromium.orgca29dd82013-04-26 11:59:48 +00003817 __ test(ecx, Immediate(kOneByteDataHintMask));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003818 __ j(not_zero, &ascii_data);
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00003819 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
3820 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
3821 __ xor_(edi, ecx);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00003822 STATIC_ASSERT(kOneByteStringTag != 0 && kOneByteDataHintTag != 0);
3823 __ and_(edi, kOneByteStringTag | kOneByteDataHintTag);
3824 __ cmp(edi, kOneByteStringTag | kOneByteDataHintTag);
yangguo@chromium.org46a2a512013-01-18 16:29:40 +00003825 __ j(equal, &ascii_data);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003826 // Allocate a two byte cons string.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003827 __ AllocateTwoByteConsString(ecx, edi, no_reg, &call_runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003828 __ jmp(&allocated);
3829
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003830 // We cannot encounter sliced strings or cons strings here since:
ulan@chromium.org2efb9002012-01-19 15:36:35 +00003831 STATIC_ASSERT(SlicedString::kMinLength >= ConsString::kMinLength);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003832 // Handle creating a flat result from either external or sequential strings.
3833 // Locate the first characters' locations.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003834 // eax: first string
3835 // ebx: length of resulting flat string as a smi
3836 // edx: second string
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003837 Label first_prepared, second_prepared;
3838 Label first_is_sequential, second_is_sequential;
ricow@chromium.org65fae842010-08-25 15:26:24 +00003839 __ bind(&string_add_flat_result);
3840 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
3841 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003842 // ecx: instance type of first string
3843 STATIC_ASSERT(kSeqStringTag == 0);
3844 __ test_b(ecx, kStringRepresentationMask);
3845 __ j(zero, &first_is_sequential, Label::kNear);
3846 // Rule out short external string and load string resource.
3847 STATIC_ASSERT(kShortExternalStringTag != 0);
3848 __ test_b(ecx, kShortExternalStringMask);
3849 __ j(not_zero, &call_runtime);
3850 __ mov(eax, FieldOperand(eax, ExternalString::kResourceDataOffset));
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003851 STATIC_ASSERT(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003852 __ jmp(&first_prepared, Label::kNear);
3853 __ bind(&first_is_sequential);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003854 __ add(eax, Immediate(SeqOneByteString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003855 __ bind(&first_prepared);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003856
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003857 __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
3858 __ movzx_b(edi, FieldOperand(edi, Map::kInstanceTypeOffset));
3859 // Check whether both strings have same encoding.
3860 // edi: instance type of second string
3861 __ xor_(ecx, edi);
3862 __ test_b(ecx, kStringEncodingMask);
3863 __ j(not_zero, &call_runtime);
3864 STATIC_ASSERT(kSeqStringTag == 0);
3865 __ test_b(edi, kStringRepresentationMask);
3866 __ j(zero, &second_is_sequential, Label::kNear);
3867 // Rule out short external string and load string resource.
3868 STATIC_ASSERT(kShortExternalStringTag != 0);
3869 __ test_b(edi, kShortExternalStringMask);
3870 __ j(not_zero, &call_runtime);
3871 __ mov(edx, FieldOperand(edx, ExternalString::kResourceDataOffset));
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003872 STATIC_ASSERT(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003873 __ jmp(&second_prepared, Label::kNear);
3874 __ bind(&second_is_sequential);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003875 __ add(edx, Immediate(SeqOneByteString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003876 __ bind(&second_prepared);
3877
3878 // Push the addresses of both strings' first characters onto the stack.
3879 __ push(edx);
3880 __ push(eax);
3881
3882 Label non_ascii_string_add_flat_result, call_runtime_drop_two;
3883 // edi: instance type of second string
3884 // First string and second string have the same encoding.
3885 STATIC_ASSERT(kTwoByteStringTag == 0);
3886 __ test_b(edi, kStringEncodingMask);
3887 __ j(zero, &non_ascii_string_add_flat_result);
3888
ulan@chromium.org2efb9002012-01-19 15:36:35 +00003889 // Both strings are ASCII strings.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003890 // ebx: length of resulting flat string as a smi
3891 __ SmiUntag(ebx);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003892 __ AllocateAsciiString(eax, ebx, ecx, edx, edi, &call_runtime_drop_two);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003893 // eax: result string
3894 __ mov(ecx, eax);
3895 // Locate first character of result.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00003896 __ add(ecx, Immediate(SeqOneByteString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003897 // Load first argument's length and first character location. Account for
3898 // values currently on the stack when fetching arguments from it.
3899 __ mov(edx, Operand(esp, 4 * kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003900 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
3901 __ SmiUntag(edi);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003902 __ pop(edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003903 // eax: result string
3904 // ecx: first character of result
3905 // edx: first char of first argument
3906 // edi: length of first argument
3907 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003908 // Load second argument's length and first character location. Account for
3909 // values currently on the stack when fetching arguments from it.
3910 __ mov(edx, Operand(esp, 2 * kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003911 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
3912 __ SmiUntag(edi);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003913 __ pop(edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003914 // eax: result string
3915 // ecx: next character of result
3916 // edx: first char of second argument
3917 // edi: length of second argument
3918 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003919 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003920 __ ret(2 * kPointerSize);
3921
3922 // Handle creating a flat two byte result.
3923 // eax: first string - known to be two byte
3924 // ebx: length of resulting flat string as a smi
3925 // edx: second string
3926 __ bind(&non_ascii_string_add_flat_result);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003927 // Both strings are two byte strings.
ricow@chromium.org65fae842010-08-25 15:26:24 +00003928 __ SmiUntag(ebx);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003929 __ AllocateTwoByteString(eax, ebx, ecx, edx, edi, &call_runtime_drop_two);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003930 // eax: result string
3931 __ mov(ecx, eax);
3932 // Locate first character of result.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003933 __ add(ecx, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
3934 // Load second argument's length and first character location. Account for
3935 // values currently on the stack when fetching arguments from it.
3936 __ mov(edx, Operand(esp, 4 * kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003937 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
3938 __ SmiUntag(edi);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003939 __ pop(edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003940 // eax: result string
3941 // ecx: first character of result
3942 // edx: first char of first argument
3943 // edi: length of first argument
3944 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003945 // Load second argument's length and first character location. Account for
3946 // values currently on the stack when fetching arguments from it.
3947 __ mov(edx, Operand(esp, 2 * kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00003948 __ mov(edi, FieldOperand(edx, String::kLengthOffset));
3949 __ SmiUntag(edi);
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003950 __ pop(edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003951 // eax: result string
3952 // ecx: next character of result
3953 // edx: first char of second argument
3954 // edi: length of second argument
3955 StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00003956 __ IncrementCounter(counters->string_add_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003957 __ ret(2 * kPointerSize);
3958
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003959 // Recover stack pointer before jumping to runtime.
3960 __ bind(&call_runtime_drop_two);
3961 __ Drop(2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00003962 // Just jump to runtime to add the two strings.
ricow@chromium.org7ad65222011-12-19 12:13:11 +00003963 __ bind(&call_runtime);
bmeurer@chromium.orge94b5ff2013-10-25 09:22:31 +00003964 __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00003965
3966 if (call_builtin.is_linked()) {
3967 __ bind(&call_builtin);
bmeurer@chromium.orge94b5ff2013-10-25 09:22:31 +00003968 __ InvokeBuiltin(builtin_id, JUMP_FUNCTION);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00003969 }
3970}
3971
3972
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +00003973void StringAddStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
3974 __ push(eax);
3975 __ push(edx);
3976}
3977
3978
3979void StringAddStub::GenerateRegisterArgsPop(MacroAssembler* masm,
3980 Register temp) {
3981 __ pop(temp);
3982 __ pop(edx);
3983 __ pop(eax);
3984 __ push(temp);
3985}
3986
3987
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00003988void StringAddStub::GenerateConvertArgument(MacroAssembler* masm,
3989 int stack_offset,
3990 Register arg,
3991 Register scratch1,
3992 Register scratch2,
3993 Register scratch3,
3994 Label* slow) {
3995 // First check if the argument is already a string.
3996 Label not_string, done;
whesse@chromium.org7b260152011-06-20 15:33:18 +00003997 __ JumpIfSmi(arg, &not_string);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00003998 __ CmpObjectType(arg, FIRST_NONSTRING_TYPE, scratch1);
3999 __ j(below, &done);
4000
4001 // Check the number to string cache.
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00004002 __ bind(&not_string);
4003 // Puts the cached result into scratch1.
machenbach@chromium.org528ce022013-09-23 14:09:36 +00004004 __ LookupNumberStringCache(arg, scratch1, scratch2, scratch3, slow);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00004005 __ mov(arg, scratch1);
4006 __ mov(Operand(esp, stack_offset), arg);
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00004007 __ bind(&done);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004008}
4009
4010
4011void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
4012 Register dest,
4013 Register src,
4014 Register count,
4015 Register scratch,
4016 bool ascii) {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004017 Label loop;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004018 __ bind(&loop);
4019 // This loop just copies one character at a time, as it is only used for very
4020 // short strings.
4021 if (ascii) {
4022 __ mov_b(scratch, Operand(src, 0));
4023 __ mov_b(Operand(dest, 0), scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004024 __ add(src, Immediate(1));
4025 __ add(dest, Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004026 } else {
4027 __ mov_w(scratch, Operand(src, 0));
4028 __ mov_w(Operand(dest, 0), scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004029 __ add(src, Immediate(2));
4030 __ add(dest, Immediate(2));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004031 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004032 __ sub(count, Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004033 __ j(not_zero, &loop);
4034}
4035
4036
4037void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm,
4038 Register dest,
4039 Register src,
4040 Register count,
4041 Register scratch,
4042 bool ascii) {
4043 // Copy characters using rep movs of doublewords.
4044 // The destination is aligned on a 4 byte boundary because we are
4045 // copying to the beginning of a newly allocated string.
4046 ASSERT(dest.is(edi)); // rep movs destination
4047 ASSERT(src.is(esi)); // rep movs source
4048 ASSERT(count.is(ecx)); // rep movs count
4049 ASSERT(!scratch.is(dest));
4050 ASSERT(!scratch.is(src));
4051 ASSERT(!scratch.is(count));
4052
4053 // Nothing to do for zero characters.
4054 Label done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004055 __ test(count, count);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004056 __ j(zero, &done);
4057
4058 // Make count the number of bytes to copy.
4059 if (!ascii) {
4060 __ shl(count, 1);
4061 }
4062
4063 // Don't enter the rep movs if there are less than 4 bytes to copy.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004064 Label last_bytes;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004065 __ test(count, Immediate(~3));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004066 __ j(zero, &last_bytes, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004067
4068 // Copy from edi to esi using rep movs instruction.
4069 __ mov(scratch, count);
4070 __ sar(count, 2); // Number of doublewords to copy.
4071 __ cld();
4072 __ rep_movs();
4073
4074 // Find number of bytes left.
4075 __ mov(count, scratch);
4076 __ and_(count, 3);
4077
4078 // Check if there are more bytes to copy.
4079 __ bind(&last_bytes);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004080 __ test(count, count);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004081 __ j(zero, &done);
4082
4083 // Copy remaining characters.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004084 Label loop;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004085 __ bind(&loop);
4086 __ mov_b(scratch, Operand(src, 0));
4087 __ mov_b(Operand(dest, 0), scratch);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004088 __ add(src, Immediate(1));
4089 __ add(dest, Immediate(1));
4090 __ sub(count, Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004091 __ j(not_zero, &loop);
4092
4093 __ bind(&done);
4094}
4095
4096
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004097void StringHelper::GenerateTwoCharacterStringTableProbe(MacroAssembler* masm,
ricow@chromium.org65fae842010-08-25 15:26:24 +00004098 Register c1,
4099 Register c2,
4100 Register scratch1,
4101 Register scratch2,
4102 Register scratch3,
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00004103 Label* not_probed,
ricow@chromium.org65fae842010-08-25 15:26:24 +00004104 Label* not_found) {
4105 // Register scratch3 is the general scratch register in this function.
4106 Register scratch = scratch3;
4107
4108 // Make sure that both characters are not digits as such strings has a
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004109 // different hash algorithm. Don't try to look for these in the string table.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004110 Label not_array_index;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004111 __ mov(scratch, c1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004112 __ sub(scratch, Immediate(static_cast<int>('0')));
4113 __ cmp(scratch, Immediate(static_cast<int>('9' - '0')));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004114 __ j(above, &not_array_index, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004115 __ mov(scratch, c2);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004116 __ sub(scratch, Immediate(static_cast<int>('0')));
4117 __ cmp(scratch, Immediate(static_cast<int>('9' - '0')));
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +00004118 __ j(below_equal, not_probed);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004119
4120 __ bind(&not_array_index);
4121 // Calculate the two character string hash.
4122 Register hash = scratch1;
4123 GenerateHashInit(masm, hash, c1, scratch);
4124 GenerateHashAddCharacter(masm, hash, c2, scratch);
4125 GenerateHashGetHash(masm, hash, scratch);
4126
4127 // Collect the two characters in a register.
4128 Register chars = c1;
4129 __ shl(c2, kBitsPerByte);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004130 __ or_(chars, c2);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004131
4132 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
4133 // hash: hash of two character string.
4134
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004135 // Load the string table.
4136 Register string_table = c2;
danno@chromium.org59400602013-08-13 17:09:37 +00004137 __ LoadRoot(string_table, Heap::kStringTableRootIndex);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004138
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004139 // Calculate capacity mask from the string table capacity.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004140 Register mask = scratch2;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004141 __ mov(mask, FieldOperand(string_table, StringTable::kCapacityOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004142 __ SmiUntag(mask);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004143 __ sub(mask, Immediate(1));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004144
4145 // Registers
4146 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
4147 // hash: hash of two character string
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004148 // string_table: string table
ricow@chromium.org65fae842010-08-25 15:26:24 +00004149 // mask: capacity mask
4150 // scratch: -
4151
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004152 // Perform a number of probes in the string table.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004153 static const int kProbes = 4;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004154 Label found_in_string_table;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004155 Label next_probe[kProbes], next_probe_pop_mask[kProbes];
danno@chromium.org2c456792011-11-11 12:00:53 +00004156 Register candidate = scratch; // Scratch register contains candidate.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004157 for (int i = 0; i < kProbes; i++) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004158 // Calculate entry in string table.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004159 __ mov(scratch, hash);
4160 if (i > 0) {
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004161 __ add(scratch, Immediate(StringTable::GetProbeOffset(i)));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004162 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004163 __ and_(scratch, mask);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004164
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004165 // Load the entry from the string table.
4166 STATIC_ASSERT(StringTable::kEntrySize == 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004167 __ mov(candidate,
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004168 FieldOperand(string_table,
ricow@chromium.org65fae842010-08-25 15:26:24 +00004169 scratch,
4170 times_pointer_size,
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004171 StringTable::kElementsStartOffset));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004172
4173 // If entry is undefined no string with this hash can be found.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004174 Factory* factory = masm->isolate()->factory();
4175 __ cmp(candidate, factory->undefined_value());
ricow@chromium.org65fae842010-08-25 15:26:24 +00004176 __ j(equal, not_found);
danno@chromium.org2c456792011-11-11 12:00:53 +00004177 __ cmp(candidate, factory->the_hole_value());
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +00004178 __ j(equal, &next_probe[i]);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004179
4180 // If length is not 2 the string is not a candidate.
4181 __ cmp(FieldOperand(candidate, String::kLengthOffset),
4182 Immediate(Smi::FromInt(2)));
4183 __ j(not_equal, &next_probe[i]);
4184
4185 // As we are out of registers save the mask on the stack and use that
4186 // register as a temporary.
4187 __ push(mask);
4188 Register temp = mask;
4189
ulan@chromium.org2efb9002012-01-19 15:36:35 +00004190 // Check that the candidate is a non-external ASCII string.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004191 __ mov(temp, FieldOperand(candidate, HeapObject::kMapOffset));
4192 __ movzx_b(temp, FieldOperand(temp, Map::kInstanceTypeOffset));
4193 __ JumpIfInstanceTypeIsNotSequentialAscii(
4194 temp, temp, &next_probe_pop_mask[i]);
4195
4196 // Check if the two characters match.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004197 __ mov(temp, FieldOperand(candidate, SeqOneByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004198 __ and_(temp, 0x0000ffff);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004199 __ cmp(chars, temp);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004200 __ j(equal, &found_in_string_table);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004201 __ bind(&next_probe_pop_mask[i]);
4202 __ pop(mask);
4203 __ bind(&next_probe[i]);
4204 }
4205
4206 // No matching 2 character string found by probing.
4207 __ jmp(not_found);
4208
4209 // Scratch register contains result when we fall through to here.
danno@chromium.org2c456792011-11-11 12:00:53 +00004210 Register result = candidate;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004211 __ bind(&found_in_string_table);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004212 __ pop(mask); // Pop saved mask from the stack.
4213 if (!result.is(eax)) {
4214 __ mov(eax, result);
4215 }
4216}
4217
4218
4219void StringHelper::GenerateHashInit(MacroAssembler* masm,
4220 Register hash,
4221 Register character,
4222 Register scratch) {
rossberg@chromium.orgfab14982012-01-05 15:02:15 +00004223 // hash = (seed + character) + ((seed + character) << 10);
4224 if (Serializer::enabled()) {
danno@chromium.org59400602013-08-13 17:09:37 +00004225 __ LoadRoot(scratch, Heap::kHashSeedRootIndex);
ulan@chromium.org2efb9002012-01-19 15:36:35 +00004226 __ SmiUntag(scratch);
rossberg@chromium.orgfab14982012-01-05 15:02:15 +00004227 __ add(scratch, character);
4228 __ mov(hash, scratch);
4229 __ shl(scratch, 10);
4230 __ add(hash, scratch);
4231 } else {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00004232 int32_t seed = masm->isolate()->heap()->HashSeed();
rossberg@chromium.orgfab14982012-01-05 15:02:15 +00004233 __ lea(scratch, Operand(character, seed));
4234 __ shl(scratch, 10);
4235 __ lea(hash, Operand(scratch, character, times_1, seed));
4236 }
ricow@chromium.org65fae842010-08-25 15:26:24 +00004237 // hash ^= hash >> 6;
4238 __ mov(scratch, hash);
danno@chromium.org2c456792011-11-11 12:00:53 +00004239 __ shr(scratch, 6);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004240 __ xor_(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004241}
4242
4243
4244void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
4245 Register hash,
4246 Register character,
4247 Register scratch) {
4248 // hash += character;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004249 __ add(hash, character);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004250 // hash += hash << 10;
4251 __ mov(scratch, hash);
4252 __ shl(scratch, 10);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004253 __ add(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004254 // hash ^= hash >> 6;
4255 __ mov(scratch, hash);
danno@chromium.org2c456792011-11-11 12:00:53 +00004256 __ shr(scratch, 6);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004257 __ xor_(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004258}
4259
4260
4261void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
4262 Register hash,
4263 Register scratch) {
4264 // hash += hash << 3;
4265 __ mov(scratch, hash);
4266 __ shl(scratch, 3);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004267 __ add(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004268 // hash ^= hash >> 11;
4269 __ mov(scratch, hash);
danno@chromium.org2c456792011-11-11 12:00:53 +00004270 __ shr(scratch, 11);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004271 __ xor_(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004272 // hash += hash << 15;
4273 __ mov(scratch, hash);
4274 __ shl(scratch, 15);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004275 __ add(hash, scratch);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004276
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00004277 __ and_(hash, String::kHashBitMask);
danno@chromium.org2c456792011-11-11 12:00:53 +00004278
ricow@chromium.org65fae842010-08-25 15:26:24 +00004279 // if (hash == 0) hash = 27;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004280 Label hash_not_zero;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004281 __ j(not_zero, &hash_not_zero, Label::kNear);
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00004282 __ mov(hash, Immediate(StringHasher::kZeroHash));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004283 __ bind(&hash_not_zero);
4284}
4285
4286
4287void SubStringStub::Generate(MacroAssembler* masm) {
4288 Label runtime;
4289
4290 // Stack frame on entry.
4291 // esp[0]: return address
4292 // esp[4]: to
4293 // esp[8]: from
4294 // esp[12]: string
4295
4296 // Make sure first argument is a string.
4297 __ mov(eax, Operand(esp, 3 * kPointerSize));
4298 STATIC_ASSERT(kSmiTag == 0);
whesse@chromium.org7b260152011-06-20 15:33:18 +00004299 __ JumpIfSmi(eax, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004300 Condition is_string = masm->IsObjectStringType(eax, ebx, ebx);
4301 __ j(NegateCondition(is_string), &runtime);
4302
4303 // eax: string
4304 // ebx: instance type
4305
4306 // Calculate length of sub string using the smi values.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004307 __ mov(ecx, Operand(esp, 1 * kPointerSize)); // To index.
whesse@chromium.org7b260152011-06-20 15:33:18 +00004308 __ JumpIfNotSmi(ecx, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004309 __ mov(edx, Operand(esp, 2 * kPointerSize)); // From index.
whesse@chromium.org7b260152011-06-20 15:33:18 +00004310 __ JumpIfNotSmi(edx, &runtime);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004311 __ sub(ecx, edx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004312 __ cmp(ecx, FieldOperand(eax, String::kLengthOffset));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004313 Label not_original_string;
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00004314 // Shorter than original string's length: an actual substring.
4315 __ j(below, &not_original_string, Label::kNear);
4316 // Longer than original string's length or negative: unsafe arguments.
4317 __ j(above, &runtime);
4318 // Return original string.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004319 Counters* counters = masm->isolate()->counters();
4320 __ IncrementCounter(counters->sub_string_native(), 1);
4321 __ ret(3 * kPointerSize);
4322 __ bind(&not_original_string);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004323
ulan@chromium.org2e04b582013-02-21 14:06:02 +00004324 Label single_char;
4325 __ cmp(ecx, Immediate(Smi::FromInt(1)));
4326 __ j(equal, &single_char);
4327
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004328 // eax: string
4329 // ebx: instance type
4330 // ecx: sub string length (smi)
4331 // edx: from index (smi)
4332 // Deal with different string types: update the index if necessary
4333 // and put the underlying string into edi.
4334 Label underlying_unpacked, sliced_string, seq_or_external_string;
4335 // If the string is not indirect, it can only be sequential or external.
4336 STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
4337 STATIC_ASSERT(kIsIndirectStringMask != 0);
4338 __ test(ebx, Immediate(kIsIndirectStringMask));
4339 __ j(zero, &seq_or_external_string, Label::kNear);
4340
4341 Factory* factory = masm->isolate()->factory();
4342 __ test(ebx, Immediate(kSlicedNotConsMask));
4343 __ j(not_zero, &sliced_string, Label::kNear);
4344 // Cons string. Check whether it is flat, then fetch first part.
4345 // Flat cons strings have an empty second part.
4346 __ cmp(FieldOperand(eax, ConsString::kSecondOffset),
4347 factory->empty_string());
4348 __ j(not_equal, &runtime);
4349 __ mov(edi, FieldOperand(eax, ConsString::kFirstOffset));
4350 // Update instance type.
4351 __ mov(ebx, FieldOperand(edi, HeapObject::kMapOffset));
4352 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
4353 __ jmp(&underlying_unpacked, Label::kNear);
4354
4355 __ bind(&sliced_string);
4356 // Sliced string. Fetch parent and adjust start index by offset.
4357 __ add(edx, FieldOperand(eax, SlicedString::kOffsetOffset));
4358 __ mov(edi, FieldOperand(eax, SlicedString::kParentOffset));
4359 // Update instance type.
4360 __ mov(ebx, FieldOperand(edi, HeapObject::kMapOffset));
4361 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
4362 __ jmp(&underlying_unpacked, Label::kNear);
4363
4364 __ bind(&seq_or_external_string);
4365 // Sequential or external string. Just move string to the expected register.
4366 __ mov(edi, eax);
4367
4368 __ bind(&underlying_unpacked);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004369
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00004370 if (FLAG_string_slices) {
4371 Label copy_routine;
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00004372 // edi: underlying subject string
ricow@chromium.org7ad65222011-12-19 12:13:11 +00004373 // ebx: instance type of underlying subject string
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004374 // edx: adjusted start index (smi)
4375 // ecx: length (smi)
4376 __ cmp(ecx, Immediate(Smi::FromInt(SlicedString::kMinLength)));
4377 // Short slice. Copy instead of slicing.
4378 __ j(less, &copy_routine);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00004379 // Allocate new sliced string. At this point we do not reload the instance
4380 // type including the string encoding because we simply rely on the info
4381 // provided by the original string. It does not matter if the original
4382 // string's encoding is wrong because we always have to recheck encoding of
4383 // the newly created string's parent anyways due to externalized strings.
4384 Label two_byte_slice, set_slice_header;
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +00004385 STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00004386 STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
4387 __ test(ebx, Immediate(kStringEncodingMask));
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00004388 __ j(zero, &two_byte_slice, Label::kNear);
4389 __ AllocateAsciiSlicedString(eax, ebx, no_reg, &runtime);
4390 __ jmp(&set_slice_header, Label::kNear);
4391 __ bind(&two_byte_slice);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00004392 __ AllocateTwoByteSlicedString(eax, ebx, no_reg, &runtime);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00004393 __ bind(&set_slice_header);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00004394 __ mov(FieldOperand(eax, SlicedString::kLengthOffset), ecx);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00004395 __ mov(FieldOperand(eax, SlicedString::kHashFieldOffset),
4396 Immediate(String::kEmptyHashField));
erik.corry@gmail.combbceb572012-03-09 10:52:05 +00004397 __ mov(FieldOperand(eax, SlicedString::kParentOffset), edi);
4398 __ mov(FieldOperand(eax, SlicedString::kOffsetOffset), edx);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004399 __ IncrementCounter(counters->sub_string_native(), 1);
4400 __ ret(3 * kPointerSize);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00004401
4402 __ bind(&copy_routine);
yangguo@chromium.org80c42ed2011-08-31 09:03:56 +00004403 }
4404
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004405 // edi: underlying subject string
ricow@chromium.org7ad65222011-12-19 12:13:11 +00004406 // ebx: instance type of underlying subject string
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004407 // edx: adjusted start index (smi)
4408 // ecx: length (smi)
4409 // The subject string can only be external or sequential string of either
4410 // encoding at this point.
4411 Label two_byte_sequential, runtime_drop_two, sequential_string;
4412 STATIC_ASSERT(kExternalStringTag != 0);
4413 STATIC_ASSERT(kSeqStringTag == 0);
4414 __ test_b(ebx, kExternalStringTag);
4415 __ j(zero, &sequential_string);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004416
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004417 // Handle external string.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004418 // Rule out short external strings.
4419 STATIC_CHECK(kShortExternalStringTag != 0);
4420 __ test_b(ebx, kShortExternalStringMask);
4421 __ j(not_zero, &runtime);
4422 __ mov(edi, FieldOperand(edi, ExternalString::kResourceDataOffset));
4423 // Move the pointer so that offset-wise, it looks like a sequential string.
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004424 STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004425 __ sub(edi, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
4426
4427 __ bind(&sequential_string);
4428 // Stash away (adjusted) index and (underlying) string.
4429 __ push(edx);
4430 __ push(edi);
4431 __ SmiUntag(ecx);
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +00004432 STATIC_ASSERT((kOneByteStringTag & kStringEncodingMask) != 0);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004433 __ test_b(ebx, kStringEncodingMask);
4434 __ j(zero, &two_byte_sequential);
4435
ulan@chromium.org2efb9002012-01-19 15:36:35 +00004436 // Sequential ASCII string. Allocate the result.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004437 __ AllocateAsciiString(eax, ecx, ebx, edx, edi, &runtime_drop_two);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004438
4439 // eax: result string
4440 // ecx: result string length
4441 __ mov(edx, esi); // esi used by following code.
4442 // Locate first character of result.
4443 __ mov(edi, eax);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004444 __ add(edi, Immediate(SeqOneByteString::kHeaderSize - kHeapObjectTag));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004445 // Load string argument and locate character of sub string start.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004446 __ pop(esi);
4447 __ pop(ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004448 __ SmiUntag(ebx);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004449 __ lea(esi, FieldOperand(esi, ebx, times_1, SeqOneByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004450
4451 // eax: result string
4452 // ecx: result length
4453 // edx: original value of esi
4454 // edi: first character of result
4455 // esi: character of sub string start
4456 StringHelper::GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, true);
4457 __ mov(esi, edx); // Restore esi.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004458 __ IncrementCounter(counters->sub_string_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004459 __ ret(3 * kPointerSize);
4460
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004461 __ bind(&two_byte_sequential);
4462 // Sequential two-byte string. Allocate the result.
4463 __ AllocateTwoByteString(eax, ecx, ebx, edx, edi, &runtime_drop_two);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004464
4465 // eax: result string
4466 // ecx: result string length
4467 __ mov(edx, esi); // esi used by following code.
4468 // Locate first character of result.
4469 __ mov(edi, eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004470 __ add(edi,
ricow@chromium.org65fae842010-08-25 15:26:24 +00004471 Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
4472 // Load string argument and locate character of sub string start.
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004473 __ pop(esi);
4474 __ pop(ebx);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004475 // As from is a smi it is 2 times the value which matches the size of a two
4476 // byte character.
4477 STATIC_ASSERT(kSmiTag == 0);
4478 STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004479 __ lea(esi, FieldOperand(esi, ebx, times_1, SeqTwoByteString::kHeaderSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004480
4481 // eax: result string
4482 // ecx: result length
4483 // edx: original value of esi
4484 // edi: first character of result
4485 // esi: character of sub string start
4486 StringHelper::GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, false);
4487 __ mov(esi, edx); // Restore esi.
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004488 __ IncrementCounter(counters->sub_string_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004489 __ ret(3 * kPointerSize);
4490
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00004491 // Drop pushed values on the stack before tail call.
4492 __ bind(&runtime_drop_two);
4493 __ Drop(2);
4494
ricow@chromium.org65fae842010-08-25 15:26:24 +00004495 // Just jump to runtime to create the sub string.
4496 __ bind(&runtime);
4497 __ TailCallRuntime(Runtime::kSubString, 3, 1);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00004498
4499 __ bind(&single_char);
4500 // eax: string
4501 // ebx: instance type
4502 // ecx: sub string length (smi)
4503 // edx: from index (smi)
4504 StringCharAtGenerator generator(
4505 eax, edx, ecx, eax, &runtime, &runtime, &runtime, STRING_INDEX_IS_NUMBER);
4506 generator.GenerateFast(masm);
4507 __ ret(3 * kPointerSize);
4508 generator.SkipSlow(masm, &runtime);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004509}
4510
4511
lrn@chromium.org1c092762011-05-09 09:42:16 +00004512void StringCompareStub::GenerateFlatAsciiStringEquals(MacroAssembler* masm,
4513 Register left,
4514 Register right,
4515 Register scratch1,
4516 Register scratch2) {
4517 Register length = scratch1;
4518
4519 // Compare lengths.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004520 Label strings_not_equal, check_zero_length;
lrn@chromium.org1c092762011-05-09 09:42:16 +00004521 __ mov(length, FieldOperand(left, String::kLengthOffset));
4522 __ cmp(length, FieldOperand(right, String::kLengthOffset));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004523 __ j(equal, &check_zero_length, Label::kNear);
lrn@chromium.org1c092762011-05-09 09:42:16 +00004524 __ bind(&strings_not_equal);
4525 __ Set(eax, Immediate(Smi::FromInt(NOT_EQUAL)));
4526 __ ret(0);
4527
4528 // Check if the length is zero.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004529 Label compare_chars;
lrn@chromium.org1c092762011-05-09 09:42:16 +00004530 __ bind(&check_zero_length);
4531 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004532 __ test(length, length);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004533 __ j(not_zero, &compare_chars, Label::kNear);
lrn@chromium.org1c092762011-05-09 09:42:16 +00004534 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
4535 __ ret(0);
4536
4537 // Compare characters.
4538 __ bind(&compare_chars);
4539 GenerateAsciiCharsCompareLoop(masm, left, right, length, scratch2,
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004540 &strings_not_equal, Label::kNear);
lrn@chromium.org1c092762011-05-09 09:42:16 +00004541
4542 // Characters are equal.
4543 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
4544 __ ret(0);
4545}
4546
4547
ricow@chromium.org65fae842010-08-25 15:26:24 +00004548void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
4549 Register left,
4550 Register right,
4551 Register scratch1,
4552 Register scratch2,
4553 Register scratch3) {
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004554 Counters* counters = masm->isolate()->counters();
4555 __ IncrementCounter(counters->string_compare_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004556
4557 // Find minimum length.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004558 Label left_shorter;
ricow@chromium.org65fae842010-08-25 15:26:24 +00004559 __ mov(scratch1, FieldOperand(left, String::kLengthOffset));
4560 __ mov(scratch3, scratch1);
4561 __ sub(scratch3, FieldOperand(right, String::kLengthOffset));
4562
4563 Register length_delta = scratch3;
4564
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004565 __ j(less_equal, &left_shorter, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004566 // Right string is shorter. Change scratch1 to be length of right string.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004567 __ sub(scratch1, length_delta);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004568 __ bind(&left_shorter);
4569
4570 Register min_length = scratch1;
4571
4572 // If either length is zero, just compare lengths.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004573 Label compare_lengths;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004574 __ test(min_length, min_length);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004575 __ j(zero, &compare_lengths, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004576
lrn@chromium.org1c092762011-05-09 09:42:16 +00004577 // Compare characters.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004578 Label result_not_equal;
lrn@chromium.org1c092762011-05-09 09:42:16 +00004579 GenerateAsciiCharsCompareLoop(masm, left, right, min_length, scratch2,
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004580 &result_not_equal, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004581
4582 // Compare lengths - strings up to min-length are equal.
4583 __ bind(&compare_lengths);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004584 __ test(length_delta, length_delta);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00004585 Label length_not_equal;
4586 __ j(not_zero, &length_not_equal, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004587
4588 // Result is EQUAL.
4589 STATIC_ASSERT(EQUAL == 0);
4590 STATIC_ASSERT(kSmiTag == 0);
4591 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
4592 __ ret(0);
4593
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004594 Label result_greater;
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00004595 Label result_less;
4596 __ bind(&length_not_equal);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004597 __ j(greater, &result_greater, Label::kNear);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00004598 __ jmp(&result_less, Label::kNear);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00004599 __ bind(&result_not_equal);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +00004600 __ j(above, &result_greater, Label::kNear);
4601 __ bind(&result_less);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004602
4603 // Result is LESS.
4604 __ Set(eax, Immediate(Smi::FromInt(LESS)));
4605 __ ret(0);
4606
4607 // Result is GREATER.
4608 __ bind(&result_greater);
4609 __ Set(eax, Immediate(Smi::FromInt(GREATER)));
4610 __ ret(0);
4611}
4612
4613
lrn@chromium.org1c092762011-05-09 09:42:16 +00004614void StringCompareStub::GenerateAsciiCharsCompareLoop(
4615 MacroAssembler* masm,
4616 Register left,
4617 Register right,
4618 Register length,
4619 Register scratch,
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004620 Label* chars_not_equal,
4621 Label::Distance chars_not_equal_near) {
lrn@chromium.org1c092762011-05-09 09:42:16 +00004622 // Change index to run from -length to -1 by adding length to string
4623 // start. This means that loop ends when index reaches zero, which
4624 // doesn't need an additional compare.
4625 __ SmiUntag(length);
4626 __ lea(left,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004627 FieldOperand(left, length, times_1, SeqOneByteString::kHeaderSize));
lrn@chromium.org1c092762011-05-09 09:42:16 +00004628 __ lea(right,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004629 FieldOperand(right, length, times_1, SeqOneByteString::kHeaderSize));
lrn@chromium.org1c092762011-05-09 09:42:16 +00004630 __ neg(length);
4631 Register index = length; // index = -length;
4632
4633 // Compare loop.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004634 Label loop;
lrn@chromium.org1c092762011-05-09 09:42:16 +00004635 __ bind(&loop);
4636 __ mov_b(scratch, Operand(left, index, times_1, 0));
4637 __ cmpb(scratch, Operand(right, index, times_1, 0));
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004638 __ j(not_equal, chars_not_equal, chars_not_equal_near);
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00004639 __ inc(index);
lrn@chromium.org1c092762011-05-09 09:42:16 +00004640 __ j(not_zero, &loop);
4641}
4642
4643
ricow@chromium.org65fae842010-08-25 15:26:24 +00004644void StringCompareStub::Generate(MacroAssembler* masm) {
4645 Label runtime;
4646
4647 // Stack frame on entry.
4648 // esp[0]: return address
4649 // esp[4]: right string
4650 // esp[8]: left string
4651
4652 __ mov(edx, Operand(esp, 2 * kPointerSize)); // left
4653 __ mov(eax, Operand(esp, 1 * kPointerSize)); // right
4654
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004655 Label not_same;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004656 __ cmp(edx, eax);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004657 __ j(not_equal, &not_same, Label::kNear);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004658 STATIC_ASSERT(EQUAL == 0);
4659 STATIC_ASSERT(kSmiTag == 0);
4660 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00004661 __ IncrementCounter(masm->isolate()->counters()->string_compare_native(), 1);
ricow@chromium.org65fae842010-08-25 15:26:24 +00004662 __ ret(2 * kPointerSize);
4663
4664 __ bind(&not_same);
4665
ulan@chromium.org2efb9002012-01-19 15:36:35 +00004666 // Check that both objects are sequential ASCII strings.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004667 __ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx, &runtime);
4668
ulan@chromium.org2efb9002012-01-19 15:36:35 +00004669 // Compare flat ASCII strings.
ricow@chromium.org65fae842010-08-25 15:26:24 +00004670 // Drop arguments from the stack.
4671 __ pop(ecx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004672 __ add(esp, Immediate(2 * kPointerSize));
ricow@chromium.org65fae842010-08-25 15:26:24 +00004673 __ push(ecx);
4674 GenerateCompareFlatAsciiStrings(masm, edx, eax, ecx, ebx, edi);
4675
4676 // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
4677 // tagged as a small integer.
4678 __ bind(&runtime);
4679 __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
4680}
4681
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004682
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004683void ICCompareStub::GenerateSmis(MacroAssembler* masm) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004684 ASSERT(state_ == CompareIC::SMI);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004685 Label miss;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004686 __ mov(ecx, edx);
4687 __ or_(ecx, eax);
whesse@chromium.org7b260152011-06-20 15:33:18 +00004688 __ JumpIfNotSmi(ecx, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004689
4690 if (GetCondition() == equal) {
4691 // For equality we do not care about the sign of the result.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004692 __ sub(eax, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004693 } else {
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004694 Label done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004695 __ sub(edx, eax);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004696 __ j(no_overflow, &done, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004697 // Correct sign of result in case of overflow.
4698 __ not_(edx);
4699 __ bind(&done);
4700 __ mov(eax, edx);
4701 }
4702 __ ret(0);
4703
4704 __ bind(&miss);
4705 GenerateMiss(masm);
4706}
4707
4708
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004709void ICCompareStub::GenerateNumbers(MacroAssembler* masm) {
4710 ASSERT(state_ == CompareIC::NUMBER);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004711
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004712 Label generic_stub;
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00004713 Label unordered, maybe_undefined1, maybe_undefined2;
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004714 Label miss;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004715
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004716 if (left_ == CompareIC::SMI) {
4717 __ JumpIfNotSmi(edx, &miss);
4718 }
4719 if (right_ == CompareIC::SMI) {
4720 __ JumpIfNotSmi(eax, &miss);
4721 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004722
4723 // Inlining the double comparison and falling back to the general compare
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004724 // stub if NaN is involved or SSE2 or CMOV is unsupported.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00004725 if (CpuFeatures::IsSupported(SSE2) && CpuFeatures::IsSupported(CMOV)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00004726 CpuFeatureScope scope1(masm, SSE2);
4727 CpuFeatureScope scope2(masm, CMOV);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004728
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004729 // Load left and right operand.
4730 Label done, left, left_smi, right_smi;
4731 __ JumpIfSmi(eax, &right_smi, Label::kNear);
4732 __ cmp(FieldOperand(eax, HeapObject::kMapOffset),
4733 masm->isolate()->factory()->heap_number_map());
4734 __ j(not_equal, &maybe_undefined1, Label::kNear);
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +00004735 __ movsd(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004736 __ jmp(&left, Label::kNear);
4737 __ bind(&right_smi);
4738 __ mov(ecx, eax); // Can't clobber eax because we can still jump away.
4739 __ SmiUntag(ecx);
machenbach@chromium.org528ce022013-09-23 14:09:36 +00004740 __ Cvtsi2sd(xmm1, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004741
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004742 __ bind(&left);
4743 __ JumpIfSmi(edx, &left_smi, Label::kNear);
4744 __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
4745 masm->isolate()->factory()->heap_number_map());
4746 __ j(not_equal, &maybe_undefined2, Label::kNear);
bmeurer@chromium.org0fdb2a62013-10-21 07:19:36 +00004747 __ movsd(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004748 __ jmp(&done);
4749 __ bind(&left_smi);
4750 __ mov(ecx, edx); // Can't clobber edx because we can still jump away.
4751 __ SmiUntag(ecx);
machenbach@chromium.org528ce022013-09-23 14:09:36 +00004752 __ Cvtsi2sd(xmm0, ecx);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004753
4754 __ bind(&done);
4755 // Compare operands.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004756 __ ucomisd(xmm0, xmm1);
4757
4758 // Don't base result on EFLAGS when a NaN is involved.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00004759 __ j(parity_even, &unordered, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004760
4761 // Return a result of -1, 0, or 1, based on EFLAGS.
4762 // Performing mov, because xor would destroy the flag register.
4763 __ mov(eax, 0); // equal
4764 __ mov(ecx, Immediate(Smi::FromInt(1)));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004765 __ cmov(above, eax, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004766 __ mov(ecx, Immediate(Smi::FromInt(-1)));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004767 __ cmov(below, eax, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004768 __ ret(0);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004769 } else {
4770 __ mov(ecx, edx);
4771 __ and_(ecx, eax);
4772 __ JumpIfSmi(ecx, &generic_stub, Label::kNear);
4773
4774 __ cmp(FieldOperand(eax, HeapObject::kMapOffset),
4775 masm->isolate()->factory()->heap_number_map());
4776 __ j(not_equal, &maybe_undefined1, Label::kNear);
4777 __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
4778 masm->isolate()->factory()->heap_number_map());
4779 __ j(not_equal, &maybe_undefined2, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004780 }
4781
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00004782 __ bind(&unordered);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004783 __ bind(&generic_stub);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004784 ICCompareStub stub(op_, CompareIC::GENERIC, CompareIC::GENERIC,
4785 CompareIC::GENERIC);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00004786 __ jmp(stub.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004787
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00004788 __ bind(&maybe_undefined1);
4789 if (Token::IsOrderedRelationalCompareOp(op_)) {
4790 __ cmp(eax, Immediate(masm->isolate()->factory()->undefined_value()));
4791 __ j(not_equal, &miss);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004792 __ JumpIfSmi(edx, &unordered);
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00004793 __ CmpObjectType(edx, HEAP_NUMBER_TYPE, ecx);
4794 __ j(not_equal, &maybe_undefined2, Label::kNear);
4795 __ jmp(&unordered);
4796 }
4797
4798 __ bind(&maybe_undefined2);
4799 if (Token::IsOrderedRelationalCompareOp(op_)) {
4800 __ cmp(edx, Immediate(masm->isolate()->factory()->undefined_value()));
4801 __ j(equal, &unordered);
4802 }
4803
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004804 __ bind(&miss);
4805 GenerateMiss(masm);
4806}
4807
4808
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004809void ICCompareStub::GenerateInternalizedStrings(MacroAssembler* masm) {
4810 ASSERT(state_ == CompareIC::INTERNALIZED_STRING);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004811 ASSERT(GetCondition() == equal);
4812
4813 // Registers containing left and right operands respectively.
4814 Register left = edx;
4815 Register right = eax;
4816 Register tmp1 = ecx;
4817 Register tmp2 = ebx;
4818
4819 // Check that both operands are heap objects.
4820 Label miss;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004821 __ mov(tmp1, left);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004822 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004823 __ and_(tmp1, right);
whesse@chromium.org7b260152011-06-20 15:33:18 +00004824 __ JumpIfSmi(tmp1, &miss, Label::kNear);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004825
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004826 // Check that both operands are internalized strings.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004827 __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset));
4828 __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset));
4829 __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
4830 __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +00004831 STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0);
4832 __ or_(tmp1, tmp2);
4833 __ test(tmp1, Immediate(kIsNotStringMask | kIsNotInternalizedMask));
4834 __ j(not_zero, &miss, Label::kNear);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004835
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004836 // Internalized strings are compared by identity.
4837 Label done;
4838 __ cmp(left, right);
4839 // Make sure eax is non-zero. At this point input operands are
4840 // guaranteed to be non-zero.
4841 ASSERT(right.is(eax));
4842 __ j(not_equal, &done, Label::kNear);
4843 STATIC_ASSERT(EQUAL == 0);
4844 STATIC_ASSERT(kSmiTag == 0);
4845 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
4846 __ bind(&done);
4847 __ ret(0);
4848
4849 __ bind(&miss);
4850 GenerateMiss(masm);
4851}
4852
4853
4854void ICCompareStub::GenerateUniqueNames(MacroAssembler* masm) {
4855 ASSERT(state_ == CompareIC::UNIQUE_NAME);
4856 ASSERT(GetCondition() == equal);
4857
4858 // Registers containing left and right operands respectively.
4859 Register left = edx;
4860 Register right = eax;
4861 Register tmp1 = ecx;
4862 Register tmp2 = ebx;
4863
4864 // Check that both operands are heap objects.
4865 Label miss;
4866 __ mov(tmp1, left);
4867 STATIC_ASSERT(kSmiTag == 0);
4868 __ and_(tmp1, right);
4869 __ JumpIfSmi(tmp1, &miss, Label::kNear);
4870
4871 // Check that both operands are unique names. This leaves the instance
4872 // types loaded in tmp1 and tmp2.
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004873 __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset));
4874 __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset));
4875 __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
4876 __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
4877
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00004878 __ JumpIfNotUniqueName(tmp1, &miss, Label::kNear);
4879 __ JumpIfNotUniqueName(tmp2, &miss, Label::kNear);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004880
4881 // Unique names are compared by identity.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004882 Label done;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004883 __ cmp(left, right);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004884 // Make sure eax is non-zero. At this point input operands are
4885 // guaranteed to be non-zero.
4886 ASSERT(right.is(eax));
4887 __ j(not_equal, &done, Label::kNear);
4888 STATIC_ASSERT(EQUAL == 0);
4889 STATIC_ASSERT(kSmiTag == 0);
4890 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
4891 __ bind(&done);
4892 __ ret(0);
4893
4894 __ bind(&miss);
4895 GenerateMiss(masm);
4896}
4897
4898
lrn@chromium.org1c092762011-05-09 09:42:16 +00004899void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004900 ASSERT(state_ == CompareIC::STRING);
lrn@chromium.org1c092762011-05-09 09:42:16 +00004901 Label miss;
4902
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00004903 bool equality = Token::IsEqualityOp(op_);
4904
lrn@chromium.org1c092762011-05-09 09:42:16 +00004905 // Registers containing left and right operands respectively.
4906 Register left = edx;
4907 Register right = eax;
4908 Register tmp1 = ecx;
4909 Register tmp2 = ebx;
4910 Register tmp3 = edi;
4911
4912 // Check that both operands are heap objects.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004913 __ mov(tmp1, left);
lrn@chromium.org1c092762011-05-09 09:42:16 +00004914 STATIC_ASSERT(kSmiTag == 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004915 __ and_(tmp1, right);
whesse@chromium.org7b260152011-06-20 15:33:18 +00004916 __ JumpIfSmi(tmp1, &miss);
lrn@chromium.org1c092762011-05-09 09:42:16 +00004917
4918 // Check that both operands are strings. This leaves the instance
4919 // types loaded in tmp1 and tmp2.
4920 __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset));
4921 __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset));
4922 __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
4923 __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
4924 __ mov(tmp3, tmp1);
4925 STATIC_ASSERT(kNotStringTag != 0);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004926 __ or_(tmp3, tmp2);
lrn@chromium.org1c092762011-05-09 09:42:16 +00004927 __ test(tmp3, Immediate(kIsNotStringMask));
4928 __ j(not_zero, &miss);
4929
4930 // Fast check for identical strings.
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004931 Label not_same;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004932 __ cmp(left, right);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004933 __ j(not_equal, &not_same, Label::kNear);
lrn@chromium.org1c092762011-05-09 09:42:16 +00004934 STATIC_ASSERT(EQUAL == 0);
4935 STATIC_ASSERT(kSmiTag == 0);
4936 __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
4937 __ ret(0);
4938
4939 // Handle not identical strings.
4940 __ bind(&not_same);
4941
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00004942 // Check that both strings are internalized. If they are, we're done
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00004943 // because we already know they are not identical. But in the case of
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00004944 // non-equality compare, we still need to determine the order. We
4945 // also know they are both strings.
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00004946 if (equality) {
4947 Label do_compare;
jkummerow@chromium.orgba72ec82013-07-22 09:21:20 +00004948 STATIC_ASSERT(kInternalizedTag == 0);
4949 __ or_(tmp1, tmp2);
4950 __ test(tmp1, Immediate(kIsNotInternalizedMask));
4951 __ j(not_zero, &do_compare, Label::kNear);
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00004952 // Make sure eax is non-zero. At this point input operands are
4953 // guaranteed to be non-zero.
4954 ASSERT(right.is(eax));
4955 __ ret(0);
4956 __ bind(&do_compare);
4957 }
lrn@chromium.org1c092762011-05-09 09:42:16 +00004958
4959 // Check that both strings are sequential ASCII.
4960 Label runtime;
lrn@chromium.org1c092762011-05-09 09:42:16 +00004961 __ JumpIfNotBothSequentialAsciiStrings(left, right, tmp1, tmp2, &runtime);
4962
4963 // Compare flat ASCII strings. Returns when done.
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00004964 if (equality) {
4965 StringCompareStub::GenerateFlatAsciiStringEquals(
4966 masm, left, right, tmp1, tmp2);
4967 } else {
4968 StringCompareStub::GenerateCompareFlatAsciiStrings(
4969 masm, left, right, tmp1, tmp2, tmp3);
4970 }
lrn@chromium.org1c092762011-05-09 09:42:16 +00004971
4972 // Handle more complex cases in runtime.
4973 __ bind(&runtime);
4974 __ pop(tmp1); // Return address.
4975 __ push(left);
4976 __ push(right);
4977 __ push(tmp1);
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00004978 if (equality) {
4979 __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
4980 } else {
4981 __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
4982 }
lrn@chromium.org1c092762011-05-09 09:42:16 +00004983
4984 __ bind(&miss);
4985 GenerateMiss(masm);
4986}
4987
4988
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004989void ICCompareStub::GenerateObjects(MacroAssembler* masm) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00004990 ASSERT(state_ == CompareIC::OBJECT);
karlklose@chromium.org83a47282011-05-11 11:54:09 +00004991 Label miss;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00004992 __ mov(ecx, edx);
4993 __ and_(ecx, eax);
whesse@chromium.org7b260152011-06-20 15:33:18 +00004994 __ JumpIfSmi(ecx, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004995
4996 __ CmpObjectType(eax, JS_OBJECT_TYPE, ecx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00004997 __ j(not_equal, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00004998 __ CmpObjectType(edx, JS_OBJECT_TYPE, ecx);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00004999 __ j(not_equal, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005000
5001 ASSERT(GetCondition() == equal);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005002 __ sub(eax, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005003 __ ret(0);
5004
5005 __ bind(&miss);
5006 GenerateMiss(masm);
5007}
5008
5009
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005010void ICCompareStub::GenerateKnownObjects(MacroAssembler* masm) {
5011 Label miss;
5012 __ mov(ecx, edx);
5013 __ and_(ecx, eax);
5014 __ JumpIfSmi(ecx, &miss, Label::kNear);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005015
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005016 __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
5017 __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
5018 __ cmp(ecx, known_map_);
5019 __ j(not_equal, &miss, Label::kNear);
5020 __ cmp(ebx, known_map_);
5021 __ j(not_equal, &miss, Label::kNear);
5022
5023 __ sub(eax, edx);
5024 __ ret(0);
5025
5026 __ bind(&miss);
5027 GenerateMiss(masm);
5028}
5029
5030
5031void ICCompareStub::GenerateMiss(MacroAssembler* masm) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005032 {
5033 // Call the runtime system in a fresh internal frame.
5034 ExternalReference miss = ExternalReference(IC_Utility(IC::kCompareIC_Miss),
5035 masm->isolate());
5036 FrameScope scope(masm, StackFrame::INTERNAL);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005037 __ push(edx); // Preserve edx and eax.
5038 __ push(eax);
5039 __ push(edx); // And also use them as the arguments.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005040 __ push(eax);
5041 __ push(Immediate(Smi::FromInt(op_)));
5042 __ CallExternalReference(miss, 3);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00005043 // Compute the entry point of the rewritten stub.
5044 __ lea(edi, FieldOperand(eax, Code::kHeaderSize));
5045 __ pop(eax);
5046 __ pop(edx);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005047 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005048
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005049 // Do a tail call to the rewritten stub.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005050 __ jmp(edi);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00005051}
5052
5053
lrn@chromium.org1c092762011-05-09 09:42:16 +00005054// Helper function used to check that the dictionary doesn't contain
5055// the property. This function may return false negatives, so miss_label
5056// must always call a backup property check that is complete.
5057// This function is safe to call if the receiver has fast properties.
ulan@chromium.org750145a2013-03-07 15:14:13 +00005058// Name must be a unique name and receiver must be a heap object.
5059void NameDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm,
5060 Label* miss,
5061 Label* done,
5062 Register properties,
5063 Handle<Name> name,
5064 Register r0) {
5065 ASSERT(name->IsUniqueName());
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005066
5067 // If names of slots in range from 1 to kProbes - 1 for the hash value are
5068 // not equal to the name and kProbes-th slot is not used (its name is the
5069 // undefined value), it guarantees the hash table doesn't contain the
5070 // property. It's true even if some slots represent deleted properties
ulan@chromium.org967e2702012-02-28 09:49:15 +00005071 // (their names are the hole value).
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005072 for (int i = 0; i < kInlinedProbes; i++) {
5073 // Compute the masked index: (hash + i + i * i) & mask.
5074 Register index = r0;
5075 // Capacity is smi 2^n.
5076 __ mov(index, FieldOperand(properties, kCapacityOffset));
5077 __ dec(index);
5078 __ and_(index,
5079 Immediate(Smi::FromInt(name->Hash() +
ulan@chromium.org750145a2013-03-07 15:14:13 +00005080 NameDictionary::GetProbeOffset(i))));
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005081
5082 // Scale the index by multiplying by the entry size.
ulan@chromium.org750145a2013-03-07 15:14:13 +00005083 ASSERT(NameDictionary::kEntrySize == 3);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005084 __ lea(index, Operand(index, index, times_2, 0)); // index *= 3.
5085 Register entity_name = r0;
5086 // Having undefined at this place means the name is not contained.
5087 ASSERT_EQ(kSmiTagSize, 1);
5088 __ mov(entity_name, Operand(properties, index, times_half_pointer_size,
5089 kElementsStartOffset - kHeapObjectTag));
5090 __ cmp(entity_name, masm->isolate()->factory()->undefined_value());
5091 __ j(equal, done);
5092
5093 // Stop if found the property.
ulan@chromium.org750145a2013-03-07 15:14:13 +00005094 __ cmp(entity_name, Handle<Name>(name));
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005095 __ j(equal, miss);
5096
ulan@chromium.org750145a2013-03-07 15:14:13 +00005097 Label good;
ulan@chromium.org967e2702012-02-28 09:49:15 +00005098 // Check for the hole and skip.
5099 __ cmp(entity_name, masm->isolate()->factory()->the_hole_value());
ulan@chromium.org750145a2013-03-07 15:14:13 +00005100 __ j(equal, &good, Label::kNear);
ulan@chromium.org967e2702012-02-28 09:49:15 +00005101
ulan@chromium.org750145a2013-03-07 15:14:13 +00005102 // Check if the entry name is not a unique name.
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005103 __ mov(entity_name, FieldOperand(entity_name, HeapObject::kMapOffset));
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00005104 __ JumpIfNotUniqueName(FieldOperand(entity_name, Map::kInstanceTypeOffset),
5105 miss);
ulan@chromium.org750145a2013-03-07 15:14:13 +00005106 __ bind(&good);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005107 }
5108
ulan@chromium.org750145a2013-03-07 15:14:13 +00005109 NameDictionaryLookupStub stub(properties, r0, r0, NEGATIVE_LOOKUP);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005110 __ push(Immediate(Handle<Object>(name)));
5111 __ push(Immediate(name->Hash()));
5112 __ CallStub(&stub);
5113 __ test(r0, r0);
5114 __ j(not_zero, miss);
5115 __ jmp(done);
5116}
5117
5118
ulan@chromium.org750145a2013-03-07 15:14:13 +00005119// Probe the name dictionary in the |elements| register. Jump to the
lrn@chromium.org1c092762011-05-09 09:42:16 +00005120// |done| label if a property with the given name is found leaving the
5121// index into the dictionary in |r0|. Jump to the |miss| label
5122// otherwise.
ulan@chromium.org750145a2013-03-07 15:14:13 +00005123void NameDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm,
5124 Label* miss,
5125 Label* done,
5126 Register elements,
5127 Register name,
5128 Register r0,
5129 Register r1) {
erik.corry@gmail.com6e28b562011-10-27 14:20:17 +00005130 ASSERT(!elements.is(r0));
5131 ASSERT(!elements.is(r1));
5132 ASSERT(!name.is(r0));
5133 ASSERT(!name.is(r1));
5134
ulan@chromium.org750145a2013-03-07 15:14:13 +00005135 __ AssertName(name);
lrn@chromium.org1c092762011-05-09 09:42:16 +00005136
5137 __ mov(r1, FieldOperand(elements, kCapacityOffset));
5138 __ shr(r1, kSmiTagSize); // convert smi to int
5139 __ dec(r1);
5140
5141 // Generate an unrolled loop that performs a few probes before
5142 // giving up. Measurements done on Gmail indicate that 2 probes
5143 // cover ~93% of loads from dictionaries.
5144 for (int i = 0; i < kInlinedProbes; i++) {
5145 // Compute the masked index: (hash + i + i * i) & mask.
ulan@chromium.org750145a2013-03-07 15:14:13 +00005146 __ mov(r0, FieldOperand(name, Name::kHashFieldOffset));
5147 __ shr(r0, Name::kHashShift);
lrn@chromium.org1c092762011-05-09 09:42:16 +00005148 if (i > 0) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00005149 __ add(r0, Immediate(NameDictionary::GetProbeOffset(i)));
lrn@chromium.org1c092762011-05-09 09:42:16 +00005150 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005151 __ and_(r0, r1);
lrn@chromium.org1c092762011-05-09 09:42:16 +00005152
5153 // Scale the index by multiplying by the entry size.
ulan@chromium.org750145a2013-03-07 15:14:13 +00005154 ASSERT(NameDictionary::kEntrySize == 3);
lrn@chromium.org1c092762011-05-09 09:42:16 +00005155 __ lea(r0, Operand(r0, r0, times_2, 0)); // r0 = r0 * 3
5156
5157 // Check if the key is identical to the name.
5158 __ cmp(name, Operand(elements,
5159 r0,
5160 times_4,
5161 kElementsStartOffset - kHeapObjectTag));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +00005162 __ j(equal, done);
lrn@chromium.org1c092762011-05-09 09:42:16 +00005163 }
5164
ulan@chromium.org750145a2013-03-07 15:14:13 +00005165 NameDictionaryLookupStub stub(elements, r1, r0, POSITIVE_LOOKUP);
lrn@chromium.org1c092762011-05-09 09:42:16 +00005166 __ push(name);
ulan@chromium.org750145a2013-03-07 15:14:13 +00005167 __ mov(r0, FieldOperand(name, Name::kHashFieldOffset));
5168 __ shr(r0, Name::kHashShift);
lrn@chromium.org1c092762011-05-09 09:42:16 +00005169 __ push(r0);
5170 __ CallStub(&stub);
5171
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005172 __ test(r1, r1);
lrn@chromium.org1c092762011-05-09 09:42:16 +00005173 __ j(zero, miss);
5174 __ jmp(done);
5175}
5176
5177
ulan@chromium.org750145a2013-03-07 15:14:13 +00005178void NameDictionaryLookupStub::Generate(MacroAssembler* masm) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005179 // This stub overrides SometimesSetsUpAFrame() to return false. That means
5180 // we cannot call anything that could cause a GC from this stub.
lrn@chromium.org1c092762011-05-09 09:42:16 +00005181 // Stack frame on entry:
5182 // esp[0 * kPointerSize]: return address.
5183 // esp[1 * kPointerSize]: key's hash.
5184 // esp[2 * kPointerSize]: key.
5185 // Registers:
ulan@chromium.org750145a2013-03-07 15:14:13 +00005186 // dictionary_: NameDictionary to probe.
lrn@chromium.org1c092762011-05-09 09:42:16 +00005187 // result_: used as scratch.
5188 // index_: will hold an index of entry if lookup is successful.
5189 // might alias with result_.
5190 // Returns:
5191 // result_ is zero if lookup failed, non zero otherwise.
5192
5193 Label in_dictionary, maybe_in_dictionary, not_in_dictionary;
5194
5195 Register scratch = result_;
5196
5197 __ mov(scratch, FieldOperand(dictionary_, kCapacityOffset));
5198 __ dec(scratch);
5199 __ SmiUntag(scratch);
5200 __ push(scratch);
5201
5202 // If names of slots in range from 1 to kProbes - 1 for the hash value are
5203 // not equal to the name and kProbes-th slot is not used (its name is the
5204 // undefined value), it guarantees the hash table doesn't contain the
5205 // property. It's true even if some slots represent deleted properties
5206 // (their names are the null value).
5207 for (int i = kInlinedProbes; i < kTotalProbes; i++) {
5208 // Compute the masked index: (hash + i + i * i) & mask.
5209 __ mov(scratch, Operand(esp, 2 * kPointerSize));
5210 if (i > 0) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00005211 __ add(scratch, Immediate(NameDictionary::GetProbeOffset(i)));
lrn@chromium.org1c092762011-05-09 09:42:16 +00005212 }
5213 __ and_(scratch, Operand(esp, 0));
5214
5215 // Scale the index by multiplying by the entry size.
ulan@chromium.org750145a2013-03-07 15:14:13 +00005216 ASSERT(NameDictionary::kEntrySize == 3);
lrn@chromium.org1c092762011-05-09 09:42:16 +00005217 __ lea(index_, Operand(scratch, scratch, times_2, 0)); // index *= 3.
5218
5219 // Having undefined at this place means the name is not contained.
5220 ASSERT_EQ(kSmiTagSize, 1);
5221 __ mov(scratch, Operand(dictionary_,
5222 index_,
5223 times_pointer_size,
5224 kElementsStartOffset - kHeapObjectTag));
5225 __ cmp(scratch, masm->isolate()->factory()->undefined_value());
5226 __ j(equal, &not_in_dictionary);
5227
5228 // Stop if found the property.
5229 __ cmp(scratch, Operand(esp, 3 * kPointerSize));
5230 __ j(equal, &in_dictionary);
5231
5232 if (i != kTotalProbes - 1 && mode_ == NEGATIVE_LOOKUP) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00005233 // If we hit a key that is not a unique name during negative
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00005234 // lookup we have to bailout as this key might be equal to the
lrn@chromium.org1c092762011-05-09 09:42:16 +00005235 // key we are looking for.
5236
ulan@chromium.org750145a2013-03-07 15:14:13 +00005237 // Check if the entry name is not a unique name.
lrn@chromium.org1c092762011-05-09 09:42:16 +00005238 __ mov(scratch, FieldOperand(scratch, HeapObject::kMapOffset));
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00005239 __ JumpIfNotUniqueName(FieldOperand(scratch, Map::kInstanceTypeOffset),
5240 &maybe_in_dictionary);
lrn@chromium.org1c092762011-05-09 09:42:16 +00005241 }
5242 }
5243
5244 __ bind(&maybe_in_dictionary);
5245 // If we are doing negative lookup then probing failure should be
5246 // treated as a lookup success. For positive lookup probing failure
5247 // should be treated as lookup failure.
5248 if (mode_ == POSITIVE_LOOKUP) {
5249 __ mov(result_, Immediate(0));
5250 __ Drop(1);
5251 __ ret(2 * kPointerSize);
5252 }
5253
5254 __ bind(&in_dictionary);
5255 __ mov(result_, Immediate(1));
5256 __ Drop(1);
5257 __ ret(2 * kPointerSize);
5258
5259 __ bind(&not_in_dictionary);
5260 __ mov(result_, Immediate(0));
5261 __ Drop(1);
5262 __ ret(2 * kPointerSize);
5263}
5264
5265
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005266struct AheadOfTimeWriteBarrierStubList {
5267 Register object, value, address;
5268 RememberedSetAction action;
5269};
5270
5271
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005272#define REG(Name) { kRegister_ ## Name ## _Code }
5273
5274static const AheadOfTimeWriteBarrierStubList kAheadOfTime[] = {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005275 // Used in RegExpExecStub.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005276 { REG(ebx), REG(eax), REG(edi), EMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005277 // Used in CompileArrayPushCall.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005278 { REG(ebx), REG(ecx), REG(edx), EMIT_REMEMBERED_SET },
5279 { REG(ebx), REG(edi), REG(edx), OMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005280 // Used in StoreStubCompiler::CompileStoreField and
5281 // KeyedStoreStubCompiler::CompileStoreField via GenerateStoreField.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005282 { REG(edx), REG(ecx), REG(ebx), EMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005283 // GenerateStoreField calls the stub with two different permutations of
5284 // registers. This is the second.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005285 { REG(ebx), REG(ecx), REG(edx), EMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005286 // StoreIC::GenerateNormal via GenerateDictionaryStore
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005287 { REG(ebx), REG(edi), REG(edx), EMIT_REMEMBERED_SET },
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005288 // KeyedStoreIC::GenerateGeneric.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005289 { REG(ebx), REG(edx), REG(ecx), EMIT_REMEMBERED_SET},
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005290 // KeyedStoreStubCompiler::GenerateStoreFastElement.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005291 { REG(edi), REG(ebx), REG(ecx), EMIT_REMEMBERED_SET},
5292 { REG(edx), REG(edi), REG(ebx), EMIT_REMEMBERED_SET},
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005293 // ElementsTransitionGenerator::GenerateMapChangeElementTransition
5294 // and ElementsTransitionGenerator::GenerateSmiToDouble
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005295 // and ElementsTransitionGenerator::GenerateDoubleToObject
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005296 { REG(edx), REG(ebx), REG(edi), EMIT_REMEMBERED_SET},
5297 { REG(edx), REG(ebx), REG(edi), OMIT_REMEMBERED_SET},
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00005298 // ElementsTransitionGenerator::GenerateDoubleToObject
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005299 { REG(eax), REG(edx), REG(esi), EMIT_REMEMBERED_SET},
5300 { REG(edx), REG(eax), REG(edi), EMIT_REMEMBERED_SET},
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005301 // StoreArrayLiteralElementStub::Generate
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005302 { REG(ebx), REG(eax), REG(ecx), EMIT_REMEMBERED_SET},
ulan@chromium.org57ff8812013-05-10 08:16:55 +00005303 // FastNewClosureStub and StringAddStub::Generate
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00005304 { REG(ecx), REG(edx), REG(ebx), EMIT_REMEMBERED_SET},
ulan@chromium.org57ff8812013-05-10 08:16:55 +00005305 // StringAddStub::Generate
5306 { REG(ecx), REG(eax), REG(ebx), EMIT_REMEMBERED_SET},
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005307 // Null termination.
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005308 { REG(no_reg), REG(no_reg), REG(no_reg), EMIT_REMEMBERED_SET}
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005309};
5310
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005311#undef REG
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005312
dslomov@chromium.orge97852d2013-09-12 09:02:59 +00005313bool RecordWriteStub::IsPregenerated(Isolate* isolate) {
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005314 for (const AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005315 !entry->object.is(no_reg);
5316 entry++) {
5317 if (object_.is(entry->object) &&
5318 value_.is(entry->value) &&
5319 address_.is(entry->address) &&
5320 remembered_set_action_ == entry->action &&
5321 save_fp_regs_mode_ == kDontSaveFPRegs) {
5322 return true;
5323 }
5324 }
5325 return false;
5326}
5327
5328
hpayer@chromium.org8432c912013-02-28 15:55:26 +00005329void StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(
5330 Isolate* isolate) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00005331 StoreBufferOverflowStub stub(kDontSaveFPRegs);
5332 stub.GetCode(isolate)->set_is_pregenerated(true);
5333 if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005334 StoreBufferOverflowStub stub2(kSaveFPRegs);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00005335 stub2.GetCode(isolate)->set_is_pregenerated(true);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005336 }
5337}
5338
5339
hpayer@chromium.org8432c912013-02-28 15:55:26 +00005340void RecordWriteStub::GenerateFixedRegStubsAheadOfTime(Isolate* isolate) {
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00005341 for (const AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005342 !entry->object.is(no_reg);
5343 entry++) {
5344 RecordWriteStub stub(entry->object,
5345 entry->value,
5346 entry->address,
5347 entry->action,
5348 kDontSaveFPRegs);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00005349 stub.GetCode(isolate)->set_is_pregenerated(true);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005350 }
5351}
5352
5353
verwaest@chromium.org33e09c82012-10-10 17:07:22 +00005354bool CodeStub::CanUseFPRegisters() {
5355 return CpuFeatures::IsSupported(SSE2);
5356}
5357
5358
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005359// Takes the input in 3 registers: address_ value_ and object_. A pointer to
5360// the value has just been written into the object, now this stub makes sure
5361// we keep the GC informed. The word in the object where the value has been
5362// written is in the address register.
5363void RecordWriteStub::Generate(MacroAssembler* masm) {
5364 Label skip_to_incremental_noncompacting;
5365 Label skip_to_incremental_compacting;
5366
5367 // The first two instructions are generated with labels so as to get the
5368 // offset fixed up correctly by the bind(Label*) call. We patch it back and
5369 // forth between a compare instructions (a nop in this position) and the
5370 // real branch when we start and stop incremental heap marking.
5371 __ jmp(&skip_to_incremental_noncompacting, Label::kNear);
5372 __ jmp(&skip_to_incremental_compacting, Label::kFar);
5373
5374 if (remembered_set_action_ == EMIT_REMEMBERED_SET) {
5375 __ RememberedSetHelper(object_,
5376 address_,
5377 value_,
5378 save_fp_regs_mode_,
5379 MacroAssembler::kReturnAtEnd);
5380 } else {
5381 __ ret(0);
5382 }
5383
5384 __ bind(&skip_to_incremental_noncompacting);
5385 GenerateIncremental(masm, INCREMENTAL);
5386
5387 __ bind(&skip_to_incremental_compacting);
5388 GenerateIncremental(masm, INCREMENTAL_COMPACTION);
5389
5390 // Initial mode of the stub is expected to be STORE_BUFFER_ONLY.
5391 // Will be checked in IncrementalMarking::ActivateGeneratedStub.
5392 masm->set_byte_at(0, kTwoByteNopInstruction);
5393 masm->set_byte_at(2, kFiveByteNopInstruction);
5394}
5395
5396
5397void RecordWriteStub::GenerateIncremental(MacroAssembler* masm, Mode mode) {
5398 regs_.Save(masm);
5399
5400 if (remembered_set_action_ == EMIT_REMEMBERED_SET) {
5401 Label dont_need_remembered_set;
5402
5403 __ mov(regs_.scratch0(), Operand(regs_.address(), 0));
5404 __ JumpIfNotInNewSpace(regs_.scratch0(), // Value.
5405 regs_.scratch0(),
5406 &dont_need_remembered_set);
5407
5408 __ CheckPageFlag(regs_.object(),
5409 regs_.scratch0(),
5410 1 << MemoryChunk::SCAN_ON_SCAVENGE,
5411 not_zero,
5412 &dont_need_remembered_set);
5413
5414 // First notify the incremental marker if necessary, then update the
5415 // remembered set.
5416 CheckNeedsToInformIncrementalMarker(
5417 masm,
5418 kUpdateRememberedSetOnNoNeedToInformIncrementalMarker,
5419 mode);
5420 InformIncrementalMarker(masm, mode);
5421 regs_.Restore(masm);
5422 __ RememberedSetHelper(object_,
5423 address_,
5424 value_,
5425 save_fp_regs_mode_,
5426 MacroAssembler::kReturnAtEnd);
5427
5428 __ bind(&dont_need_remembered_set);
5429 }
5430
5431 CheckNeedsToInformIncrementalMarker(
5432 masm,
5433 kReturnOnNoNeedToInformIncrementalMarker,
5434 mode);
5435 InformIncrementalMarker(masm, mode);
5436 regs_.Restore(masm);
5437 __ ret(0);
5438}
5439
5440
5441void RecordWriteStub::InformIncrementalMarker(MacroAssembler* masm, Mode mode) {
5442 regs_.SaveCallerSaveRegisters(masm, save_fp_regs_mode_);
5443 int argument_count = 3;
5444 __ PrepareCallCFunction(argument_count, regs_.scratch0());
5445 __ mov(Operand(esp, 0 * kPointerSize), regs_.object());
ulan@chromium.org8e8d8822012-11-23 14:36:46 +00005446 __ mov(Operand(esp, 1 * kPointerSize), regs_.address()); // Slot.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005447 __ mov(Operand(esp, 2 * kPointerSize),
ulan@chromium.org32d7dba2013-04-24 10:59:06 +00005448 Immediate(ExternalReference::isolate_address(masm->isolate())));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005449
5450 AllowExternalCallThatCantCauseGC scope(masm);
5451 if (mode == INCREMENTAL_COMPACTION) {
5452 __ CallCFunction(
5453 ExternalReference::incremental_evacuation_record_write_function(
5454 masm->isolate()),
5455 argument_count);
5456 } else {
5457 ASSERT(mode == INCREMENTAL);
5458 __ CallCFunction(
5459 ExternalReference::incremental_marking_record_write_function(
5460 masm->isolate()),
5461 argument_count);
5462 }
5463 regs_.RestoreCallerSaveRegisters(masm, save_fp_regs_mode_);
5464}
5465
5466
5467void RecordWriteStub::CheckNeedsToInformIncrementalMarker(
5468 MacroAssembler* masm,
5469 OnNoNeedToInformIncrementalMarker on_no_need,
5470 Mode mode) {
5471 Label object_is_black, need_incremental, need_incremental_pop_object;
5472
verwaest@chromium.org33e09c82012-10-10 17:07:22 +00005473 __ mov(regs_.scratch0(), Immediate(~Page::kPageAlignmentMask));
5474 __ and_(regs_.scratch0(), regs_.object());
5475 __ mov(regs_.scratch1(),
5476 Operand(regs_.scratch0(),
5477 MemoryChunk::kWriteBarrierCounterOffset));
5478 __ sub(regs_.scratch1(), Immediate(1));
5479 __ mov(Operand(regs_.scratch0(),
5480 MemoryChunk::kWriteBarrierCounterOffset),
5481 regs_.scratch1());
5482 __ j(negative, &need_incremental);
5483
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00005484 // Let's look at the color of the object: If it is not black we don't have
5485 // to inform the incremental marker.
5486 __ JumpIfBlack(regs_.object(),
5487 regs_.scratch0(),
5488 regs_.scratch1(),
5489 &object_is_black,
5490 Label::kNear);
5491
5492 regs_.Restore(masm);
5493 if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) {
5494 __ RememberedSetHelper(object_,
5495 address_,
5496 value_,
5497 save_fp_regs_mode_,
5498 MacroAssembler::kReturnAtEnd);
5499 } else {
5500 __ ret(0);
5501 }
5502
5503 __ bind(&object_is_black);
5504
5505 // Get the value from the slot.
5506 __ mov(regs_.scratch0(), Operand(regs_.address(), 0));
5507
5508 if (mode == INCREMENTAL_COMPACTION) {
5509 Label ensure_not_white;
5510
5511 __ CheckPageFlag(regs_.scratch0(), // Contains value.
5512 regs_.scratch1(), // Scratch.
5513 MemoryChunk::kEvacuationCandidateMask,
5514 zero,
5515 &ensure_not_white,
5516 Label::kNear);
5517
5518 __ CheckPageFlag(regs_.object(),
5519 regs_.scratch1(), // Scratch.
5520 MemoryChunk::kSkipEvacuationSlotsRecordingMask,
5521 not_zero,
5522 &ensure_not_white,
5523 Label::kNear);
5524
5525 __ jmp(&need_incremental);
5526
5527 __ bind(&ensure_not_white);
5528 }
5529
5530 // We need an extra register for this, so we push the object register
5531 // temporarily.
5532 __ push(regs_.object());
5533 __ EnsureNotWhite(regs_.scratch0(), // The value.
5534 regs_.scratch1(), // Scratch.
5535 regs_.object(), // Scratch.
5536 &need_incremental_pop_object,
5537 Label::kNear);
5538 __ pop(regs_.object());
5539
5540 regs_.Restore(masm);
5541 if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) {
5542 __ RememberedSetHelper(object_,
5543 address_,
5544 value_,
5545 save_fp_regs_mode_,
5546 MacroAssembler::kReturnAtEnd);
5547 } else {
5548 __ ret(0);
5549 }
5550
5551 __ bind(&need_incremental_pop_object);
5552 __ pop(regs_.object());
5553
5554 __ bind(&need_incremental);
5555
5556 // Fall through when we need to inform the incremental marker.
5557}
5558
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005559
5560void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) {
5561 // ----------- S t a t e -------------
5562 // -- eax : element value to store
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005563 // -- ecx : element index as smi
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005564 // -- esp[0] : return address
dslomov@chromium.orgb752d402013-06-18 11:54:54 +00005565 // -- esp[4] : array literal index in function
5566 // -- esp[8] : array literal
5567 // clobbers ebx, edx, edi
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005568 // -----------------------------------
5569
5570 Label element_done;
5571 Label double_elements;
5572 Label smi_element;
5573 Label slow_elements;
5574 Label slow_elements_from_double;
5575 Label fast_elements;
5576
dslomov@chromium.orgb752d402013-06-18 11:54:54 +00005577 // Get array literal index, array literal and its map.
5578 __ mov(edx, Operand(esp, 1 * kPointerSize));
5579 __ mov(ebx, Operand(esp, 2 * kPointerSize));
5580 __ mov(edi, FieldOperand(ebx, JSObject::kMapOffset));
5581
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00005582 __ CheckFastElements(edi, &double_elements);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005583
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005584 // Check for FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS elements
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00005585 __ JumpIfSmi(eax, &smi_element);
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005586 __ CheckFastSmiElements(edi, &fast_elements, Label::kNear);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005587
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00005588 // Store into the array literal requires a elements transition. Call into
5589 // the runtime.
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005590
5591 __ bind(&slow_elements);
5592 __ pop(edi); // Pop return address and remember to put back later for tail
5593 // call.
5594 __ push(ebx);
5595 __ push(ecx);
5596 __ push(eax);
5597 __ mov(ebx, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
5598 __ push(FieldOperand(ebx, JSFunction::kLiteralsOffset));
5599 __ push(edx);
5600 __ push(edi); // Return return address so that tail call returns to right
5601 // place.
5602 __ TailCallRuntime(Runtime::kStoreArrayLiteralElement, 5, 1);
5603
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00005604 __ bind(&slow_elements_from_double);
5605 __ pop(edx);
5606 __ jmp(&slow_elements);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005607
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005608 // Array literal has ElementsKind of FAST_*_ELEMENTS and value is an object.
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00005609 __ bind(&fast_elements);
5610 __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset));
5611 __ lea(ecx, FieldOperand(ebx, ecx, times_half_pointer_size,
5612 FixedArrayBase::kHeaderSize));
5613 __ mov(Operand(ecx, 0), eax);
5614 // Update the write barrier for the array store.
5615 __ RecordWrite(ebx, ecx, eax,
5616 kDontSaveFPRegs,
5617 EMIT_REMEMBERED_SET,
5618 OMIT_SMI_CHECK);
5619 __ ret(0);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005620
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005621 // Array literal has ElementsKind of FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS,
5622 // and value is Smi.
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00005623 __ bind(&smi_element);
5624 __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset));
5625 __ mov(FieldOperand(ebx, ecx, times_half_pointer_size,
5626 FixedArrayBase::kHeaderSize), eax);
5627 __ ret(0);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005628
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00005629 // Array literal has ElementsKind of FAST_*_DOUBLE_ELEMENTS.
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00005630 __ bind(&double_elements);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005631
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00005632 __ push(edx);
5633 __ mov(edx, FieldOperand(ebx, JSObject::kElementsOffset));
5634 __ StoreNumberToDoubleElements(eax,
5635 edx,
5636 ecx,
5637 edi,
5638 xmm0,
5639 &slow_elements_from_double,
5640 false);
5641 __ pop(edx);
5642 __ ret(0);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00005643}
5644
verwaest@chromium.org753aee42012-07-17 16:15:42 +00005645
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00005646void StubFailureTrampolineStub::Generate(MacroAssembler* masm) {
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00005647 CEntryStub ces(1, fp_registers_ ? kSaveFPRegs : kDontSaveFPRegs);
hpayer@chromium.org8432c912013-02-28 15:55:26 +00005648 __ call(ces.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
mmassi@chromium.org2f0efde2013-02-06 14:12:58 +00005649 int parameter_count_offset =
5650 StubFailureTrampolineFrame::kCallerStackParameterCountFrameOffset;
5651 __ mov(ebx, MemOperand(ebp, parameter_count_offset));
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00005652 masm->LeaveFrame(StackFrame::STUB_FAILURE_TRAMPOLINE);
mmassi@chromium.org2f0efde2013-02-06 14:12:58 +00005653 __ pop(ecx);
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +00005654 int additional_offset = function_mode_ == JS_FUNCTION_STUB_MODE
5655 ? kPointerSize
5656 : 0;
5657 __ lea(esp, MemOperand(esp, ebx, times_pointer_size, additional_offset));
mmassi@chromium.org2f0efde2013-02-06 14:12:58 +00005658 __ jmp(ecx); // Return to IC Miss stub, continuation still on stack.
mstarzinger@chromium.org068ea0a2013-01-30 09:39:44 +00005659}
5660
5661
verwaest@chromium.org753aee42012-07-17 16:15:42 +00005662void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00005663 if (masm->isolate()->function_entry_hook() != NULL) {
5664 // It's always safe to call the entry hook stub, as the hook itself
5665 // is not allowed to call back to V8.
5666 AllowStubCallsScope allow_stub_calls(masm, true);
5667
verwaest@chromium.org753aee42012-07-17 16:15:42 +00005668 ProfileEntryHookStub stub;
5669 masm->CallStub(&stub);
5670 }
5671}
5672
5673
5674void ProfileEntryHookStub::Generate(MacroAssembler* masm) {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00005675 // Save volatile registers.
5676 const int kNumSavedRegisters = 3;
5677 __ push(eax);
verwaest@chromium.org753aee42012-07-17 16:15:42 +00005678 __ push(ecx);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00005679 __ push(edx);
verwaest@chromium.org753aee42012-07-17 16:15:42 +00005680
5681 // Calculate and push the original stack pointer.
ulan@chromium.orgdfe53072013-06-06 14:14:51 +00005682 __ lea(eax, Operand(esp, (kNumSavedRegisters + 1) * kPointerSize));
verwaest@chromium.org753aee42012-07-17 16:15:42 +00005683 __ push(eax);
5684
ulan@chromium.orgdfe53072013-06-06 14:14:51 +00005685 // Retrieve our return address and use it to calculate the calling
5686 // function's address.
5687 __ mov(eax, Operand(esp, (kNumSavedRegisters + 1) * kPointerSize));
verwaest@chromium.org753aee42012-07-17 16:15:42 +00005688 __ sub(eax, Immediate(Assembler::kCallInstructionLength));
5689 __ push(eax);
5690
5691 // Call the entry hook.
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00005692 ASSERT(masm->isolate()->function_entry_hook() != NULL);
5693 __ call(FUNCTION_ADDR(masm->isolate()->function_entry_hook()),
5694 RelocInfo::RUNTIME_ENTRY);
verwaest@chromium.org753aee42012-07-17 16:15:42 +00005695 __ add(esp, Immediate(2 * kPointerSize));
5696
5697 // Restore ecx.
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00005698 __ pop(edx);
verwaest@chromium.org753aee42012-07-17 16:15:42 +00005699 __ pop(ecx);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00005700 __ pop(eax);
5701
verwaest@chromium.org753aee42012-07-17 16:15:42 +00005702 __ ret(0);
5703}
5704
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005705
5706template<class T>
dslomov@chromium.org4a35c5a2013-09-13 07:28:52 +00005707static void CreateArrayDispatch(MacroAssembler* masm,
5708 AllocationSiteOverrideMode mode) {
5709 if (mode == DISABLE_ALLOCATION_SITES) {
5710 T stub(GetInitialFastElementsKind(),
5711 CONTEXT_CHECK_REQUIRED,
5712 mode);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005713 __ TailCallStub(&stub);
dslomov@chromium.org4a35c5a2013-09-13 07:28:52 +00005714 } else if (mode == DONT_OVERRIDE) {
5715 int last_index = GetSequenceIndexFromFastElementsKind(
5716 TERMINAL_FAST_ELEMENTS_KIND);
5717 for (int i = 0; i <= last_index; ++i) {
5718 Label next;
5719 ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
5720 __ cmp(edx, kind);
5721 __ j(not_equal, &next);
5722 T stub(kind);
5723 __ TailCallStub(&stub);
5724 __ bind(&next);
5725 }
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005726
dslomov@chromium.org4a35c5a2013-09-13 07:28:52 +00005727 // If we reached this point there is a problem.
5728 __ Abort(kUnexpectedElementsKindInArrayConstructor);
5729 } else {
5730 UNREACHABLE();
5731 }
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005732}
5733
5734
dslomov@chromium.org4a35c5a2013-09-13 07:28:52 +00005735static void CreateArrayDispatchOneArgument(MacroAssembler* masm,
5736 AllocationSiteOverrideMode mode) {
5737 // ebx - type info cell (if mode != DISABLE_ALLOCATION_SITES)
5738 // edx - kind (if mode != DISABLE_ALLOCATION_SITES)
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005739 // eax - number of arguments
5740 // edi - constructor?
5741 // esp[0] - return address
5742 // esp[4] - last argument
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005743 Label normal_sequence;
dslomov@chromium.org4a35c5a2013-09-13 07:28:52 +00005744 if (mode == DONT_OVERRIDE) {
5745 ASSERT(FAST_SMI_ELEMENTS == 0);
5746 ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
5747 ASSERT(FAST_ELEMENTS == 2);
5748 ASSERT(FAST_HOLEY_ELEMENTS == 3);
5749 ASSERT(FAST_DOUBLE_ELEMENTS == 4);
5750 ASSERT(FAST_HOLEY_DOUBLE_ELEMENTS == 5);
5751
5752 // is the low bit set? If so, we are holey and that is good.
5753 __ test_b(edx, 1);
5754 __ j(not_zero, &normal_sequence);
5755 }
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005756
5757 // look at the first argument
5758 __ mov(ecx, Operand(esp, kPointerSize));
5759 __ test(ecx, ecx);
5760 __ j(zero, &normal_sequence);
5761
dslomov@chromium.org4a35c5a2013-09-13 07:28:52 +00005762 if (mode == DISABLE_ALLOCATION_SITES) {
5763 ElementsKind initial = GetInitialFastElementsKind();
5764 ElementsKind holey_initial = GetHoleyElementsKind(initial);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00005765
dslomov@chromium.org4a35c5a2013-09-13 07:28:52 +00005766 ArraySingleArgumentConstructorStub stub_holey(holey_initial,
5767 CONTEXT_CHECK_REQUIRED,
5768 DISABLE_ALLOCATION_SITES);
5769 __ TailCallStub(&stub_holey);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005770
dslomov@chromium.org4a35c5a2013-09-13 07:28:52 +00005771 __ bind(&normal_sequence);
5772 ArraySingleArgumentConstructorStub stub(initial,
5773 CONTEXT_CHECK_REQUIRED,
5774 DISABLE_ALLOCATION_SITES);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005775 __ TailCallStub(&stub);
dslomov@chromium.org4a35c5a2013-09-13 07:28:52 +00005776 } else if (mode == DONT_OVERRIDE) {
5777 // We are going to create a holey array, but our kind is non-holey.
5778 // Fix kind and retry.
5779 __ inc(edx);
5780 __ mov(ecx, FieldOperand(ebx, Cell::kValueOffset));
5781 if (FLAG_debug_code) {
machenbach@chromium.org528ce022013-09-23 14:09:36 +00005782 Handle<Map> allocation_site_map =
5783 masm->isolate()->factory()->allocation_site_map();
dslomov@chromium.org4a35c5a2013-09-13 07:28:52 +00005784 __ cmp(FieldOperand(ecx, 0), Immediate(allocation_site_map));
5785 __ Assert(equal, kExpectedAllocationSiteInCell);
5786 }
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005787
dslomov@chromium.org4a35c5a2013-09-13 07:28:52 +00005788 // Save the resulting elements kind in type info
5789 __ SmiTag(edx);
5790 __ mov(FieldOperand(ecx, AllocationSite::kTransitionInfoOffset), edx);
5791 __ SmiUntag(edx);
5792
5793 __ bind(&normal_sequence);
5794 int last_index = GetSequenceIndexFromFastElementsKind(
5795 TERMINAL_FAST_ELEMENTS_KIND);
5796 for (int i = 0; i <= last_index; ++i) {
5797 Label next;
5798 ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
5799 __ cmp(edx, kind);
5800 __ j(not_equal, &next);
5801 ArraySingleArgumentConstructorStub stub(kind);
5802 __ TailCallStub(&stub);
5803 __ bind(&next);
5804 }
5805
5806 // If we reached this point there is a problem.
5807 __ Abort(kUnexpectedElementsKindInArrayConstructor);
5808 } else {
5809 UNREACHABLE();
5810 }
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005811}
5812
5813
5814template<class T>
5815static void ArrayConstructorStubAheadOfTimeHelper(Isolate* isolate) {
dslomov@chromium.org4a35c5a2013-09-13 07:28:52 +00005816 ElementsKind initial_kind = GetInitialFastElementsKind();
5817 ElementsKind initial_holey_kind = GetHoleyElementsKind(initial_kind);
5818
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005819 int to_index = GetSequenceIndexFromFastElementsKind(
5820 TERMINAL_FAST_ELEMENTS_KIND);
5821 for (int i = 0; i <= to_index; ++i) {
5822 ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00005823 T stub(kind);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005824 stub.GetCode(isolate)->set_is_pregenerated(true);
dslomov@chromium.org4a35c5a2013-09-13 07:28:52 +00005825 if (AllocationSite::GetMode(kind) != DONT_TRACK_ALLOCATION_SITE ||
5826 (!FLAG_track_allocation_sites &&
5827 (kind == initial_kind || kind == initial_holey_kind))) {
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00005828 T stub1(kind, CONTEXT_CHECK_REQUIRED, DISABLE_ALLOCATION_SITES);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00005829 stub1.GetCode(isolate)->set_is_pregenerated(true);
5830 }
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005831 }
5832}
5833
5834
5835void ArrayConstructorStubBase::GenerateStubsAheadOfTime(Isolate* isolate) {
5836 ArrayConstructorStubAheadOfTimeHelper<ArrayNoArgumentConstructorStub>(
5837 isolate);
5838 ArrayConstructorStubAheadOfTimeHelper<ArraySingleArgumentConstructorStub>(
5839 isolate);
5840 ArrayConstructorStubAheadOfTimeHelper<ArrayNArgumentsConstructorStub>(
5841 isolate);
5842}
5843
5844
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00005845void InternalArrayConstructorStubBase::GenerateStubsAheadOfTime(
5846 Isolate* isolate) {
5847 ElementsKind kinds[2] = { FAST_ELEMENTS, FAST_HOLEY_ELEMENTS };
5848 for (int i = 0; i < 2; i++) {
5849 // For internal arrays we only need a few things
5850 InternalArrayNoArgumentConstructorStub stubh1(kinds[i]);
5851 stubh1.GetCode(isolate)->set_is_pregenerated(true);
5852 InternalArraySingleArgumentConstructorStub stubh2(kinds[i]);
5853 stubh2.GetCode(isolate)->set_is_pregenerated(true);
5854 InternalArrayNArgumentsConstructorStub stubh3(kinds[i]);
5855 stubh3.GetCode(isolate)->set_is_pregenerated(true);
5856 }
5857}
5858
5859
dslomov@chromium.org4a35c5a2013-09-13 07:28:52 +00005860void ArrayConstructorStub::GenerateDispatchToArrayStub(
5861 MacroAssembler* masm,
5862 AllocationSiteOverrideMode mode) {
5863 if (argument_count_ == ANY) {
5864 Label not_zero_case, not_one_case;
5865 __ test(eax, eax);
5866 __ j(not_zero, &not_zero_case);
5867 CreateArrayDispatch<ArrayNoArgumentConstructorStub>(masm, mode);
5868
5869 __ bind(&not_zero_case);
5870 __ cmp(eax, 1);
5871 __ j(greater, &not_one_case);
5872 CreateArrayDispatchOneArgument(masm, mode);
5873
5874 __ bind(&not_one_case);
5875 CreateArrayDispatch<ArrayNArgumentsConstructorStub>(masm, mode);
5876 } else if (argument_count_ == NONE) {
5877 CreateArrayDispatch<ArrayNoArgumentConstructorStub>(masm, mode);
5878 } else if (argument_count_ == ONE) {
5879 CreateArrayDispatchOneArgument(masm, mode);
5880 } else if (argument_count_ == MORE_THAN_ONE) {
5881 CreateArrayDispatch<ArrayNArgumentsConstructorStub>(masm, mode);
5882 } else {
5883 UNREACHABLE();
5884 }
5885}
5886
5887
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005888void ArrayConstructorStub::Generate(MacroAssembler* masm) {
5889 // ----------- S t a t e -------------
5890 // -- eax : argc (only if argument_count_ == ANY)
5891 // -- ebx : type info cell
5892 // -- edi : constructor
5893 // -- esp[0] : return address
5894 // -- esp[4] : last argument
5895 // -----------------------------------
5896 Handle<Object> undefined_sentinel(
5897 masm->isolate()->heap()->undefined_value(),
5898 masm->isolate());
5899
5900 if (FLAG_debug_code) {
5901 // The array construct code is only set for the global and natives
5902 // builtin Array functions which always have maps.
5903
5904 // Initial map for the builtin Array function should be a map.
5905 __ mov(ecx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
5906 // Will both indicate a NULL and a Smi.
5907 __ test(ecx, Immediate(kSmiTagMask));
danno@chromium.org59400602013-08-13 17:09:37 +00005908 __ Assert(not_zero, kUnexpectedInitialMapForArrayFunction);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005909 __ CmpObjectType(ecx, MAP_TYPE, ecx);
danno@chromium.org59400602013-08-13 17:09:37 +00005910 __ Assert(equal, kUnexpectedInitialMapForArrayFunction);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005911
danno@chromium.org41728482013-06-12 22:31:22 +00005912 // We should either have undefined in ebx or a valid cell
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005913 Label okay_here;
danno@chromium.org41728482013-06-12 22:31:22 +00005914 Handle<Map> cell_map = masm->isolate()->factory()->cell_map();
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005915 __ cmp(ebx, Immediate(undefined_sentinel));
5916 __ j(equal, &okay_here);
danno@chromium.org41728482013-06-12 22:31:22 +00005917 __ cmp(FieldOperand(ebx, 0), Immediate(cell_map));
danno@chromium.org59400602013-08-13 17:09:37 +00005918 __ Assert(equal, kExpectedPropertyCellInRegisterEbx);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005919 __ bind(&okay_here);
5920 }
5921
dslomov@chromium.org4a35c5a2013-09-13 07:28:52 +00005922 Label no_info;
5923 // If the type cell is undefined, or contains anything other than an
5924 // AllocationSite, call an array constructor that doesn't use AllocationSites.
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00005925 __ cmp(ebx, Immediate(undefined_sentinel));
5926 __ j(equal, &no_info);
5927 __ mov(edx, FieldOperand(ebx, Cell::kValueOffset));
machenbach@chromium.org528ce022013-09-23 14:09:36 +00005928 __ cmp(FieldOperand(edx, 0), Immediate(
5929 masm->isolate()->factory()->allocation_site_map()));
danno@chromium.orgbee51992013-07-10 14:57:15 +00005930 __ j(not_equal, &no_info);
5931
5932 __ mov(edx, FieldOperand(edx, AllocationSite::kTransitionInfoOffset));
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00005933 __ SmiUntag(edx);
dslomov@chromium.org4a35c5a2013-09-13 07:28:52 +00005934 GenerateDispatchToArrayStub(masm, DONT_OVERRIDE);
5935
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00005936 __ bind(&no_info);
dslomov@chromium.org4a35c5a2013-09-13 07:28:52 +00005937 GenerateDispatchToArrayStub(masm, DISABLE_ALLOCATION_SITES);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00005938}
5939
5940
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00005941void InternalArrayConstructorStub::GenerateCase(
5942 MacroAssembler* masm, ElementsKind kind) {
5943 Label not_zero_case, not_one_case;
5944 Label normal_sequence;
5945
5946 __ test(eax, eax);
5947 __ j(not_zero, &not_zero_case);
5948 InternalArrayNoArgumentConstructorStub stub0(kind);
5949 __ TailCallStub(&stub0);
5950
5951 __ bind(&not_zero_case);
5952 __ cmp(eax, 1);
5953 __ j(greater, &not_one_case);
5954
5955 if (IsFastPackedElementsKind(kind)) {
5956 // We might need to create a holey array
5957 // look at the first argument
5958 __ mov(ecx, Operand(esp, kPointerSize));
5959 __ test(ecx, ecx);
5960 __ j(zero, &normal_sequence);
5961
5962 InternalArraySingleArgumentConstructorStub
5963 stub1_holey(GetHoleyElementsKind(kind));
5964 __ TailCallStub(&stub1_holey);
5965 }
5966
5967 __ bind(&normal_sequence);
5968 InternalArraySingleArgumentConstructorStub stub1(kind);
5969 __ TailCallStub(&stub1);
5970
5971 __ bind(&not_one_case);
5972 InternalArrayNArgumentsConstructorStub stubN(kind);
5973 __ TailCallStub(&stubN);
5974}
5975
5976
5977void InternalArrayConstructorStub::Generate(MacroAssembler* masm) {
5978 // ----------- S t a t e -------------
5979 // -- eax : argc
5980 // -- ebx : type info cell
5981 // -- edi : constructor
5982 // -- esp[0] : return address
5983 // -- esp[4] : last argument
5984 // -----------------------------------
5985
5986 if (FLAG_debug_code) {
5987 // The array construct code is only set for the global and natives
5988 // builtin Array functions which always have maps.
5989
5990 // Initial map for the builtin Array function should be a map.
5991 __ mov(ecx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
5992 // Will both indicate a NULL and a Smi.
5993 __ test(ecx, Immediate(kSmiTagMask));
danno@chromium.org59400602013-08-13 17:09:37 +00005994 __ Assert(not_zero, kUnexpectedInitialMapForArrayFunction);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00005995 __ CmpObjectType(ecx, MAP_TYPE, ecx);
danno@chromium.org59400602013-08-13 17:09:37 +00005996 __ Assert(equal, kUnexpectedInitialMapForArrayFunction);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00005997 }
5998
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00005999 // Figure out the right elements kind
6000 __ mov(ecx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00006001
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00006002 // Load the map's "bit field 2" into |result|. We only need the first byte,
6003 // but the following masking takes care of that anyway.
6004 __ mov(ecx, FieldOperand(ecx, Map::kBitField2Offset));
6005 // Retrieve elements_kind from bit field 2.
6006 __ and_(ecx, Map::kElementsKindMask);
6007 __ shr(ecx, Map::kElementsKindShift);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00006008
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00006009 if (FLAG_debug_code) {
6010 Label done;
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00006011 __ cmp(ecx, Immediate(FAST_ELEMENTS));
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00006012 __ j(equal, &done);
6013 __ cmp(ecx, Immediate(FAST_HOLEY_ELEMENTS));
6014 __ Assert(equal,
danno@chromium.org59400602013-08-13 17:09:37 +00006015 kInvalidElementsKindForInternalArrayOrInternalPackedArray);
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00006016 __ bind(&done);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00006017 }
mstarzinger@chromium.org1510d582013-06-28 14:00:48 +00006018
6019 Label fast_elements_case;
6020 __ cmp(ecx, Immediate(FAST_ELEMENTS));
6021 __ j(equal, &fast_elements_case);
6022 GenerateCase(masm, FAST_HOLEY_ELEMENTS);
6023
6024 __ bind(&fast_elements_case);
6025 GenerateCase(masm, FAST_ELEMENTS);
verwaest@chromium.orgd4be0f02013-06-05 13:39:03 +00006026}
6027
6028
ricow@chromium.org65fae842010-08-25 15:26:24 +00006029#undef __
6030
6031} } // namespace v8::internal
6032
6033#endif // V8_TARGET_ARCH_IA32