1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 #include "v8.h"
29
30 #if defined(V8_TARGET_ARCH_IA32)
31
32 #include "ic-inl.h"
33 #include "codegen.h"
34 #include "stub-cache.h"
35
36 namespace v8 {
37 namespace internal {
38
39 #define __ ACCESS_MASM(masm)
40
41
ProbeTable(Isolate * isolate,MacroAssembler * masm,Code::Flags flags,StubCache::Table table,Register name,Register receiver,Register offset,Register extra)42 static void ProbeTable(Isolate* isolate,
43 MacroAssembler* masm,
44 Code::Flags flags,
45 StubCache::Table table,
46 Register name,
47 Register receiver,
48 // Number of the cache entry pointer-size scaled.
49 Register offset,
50 Register extra) {
51 ExternalReference key_offset(isolate->stub_cache()->key_reference(table));
52 ExternalReference value_offset(isolate->stub_cache()->value_reference(table));
53 ExternalReference map_offset(isolate->stub_cache()->map_reference(table));
54
55 Label miss;
56
57 // Multiply by 3 because there are 3 fields per entry (name, code, map).
58 __ lea(offset, Operand(offset, offset, times_2, 0));
59
60 if (extra.is_valid()) {
61 // Get the code entry from the cache.
62 __ mov(extra, Operand::StaticArray(offset, times_1, value_offset));
63
64 // Check that the key in the entry matches the name.
65 __ cmp(name, Operand::StaticArray(offset, times_1, key_offset));
66 __ j(not_equal, &miss);
67
68 // Check the map matches.
69 __ mov(offset, Operand::StaticArray(offset, times_1, map_offset));
70 __ cmp(offset, FieldOperand(receiver, HeapObject::kMapOffset));
71 __ j(not_equal, &miss);
72
73 // Check that the flags match what we're looking for.
74 __ mov(offset, FieldOperand(extra, Code::kFlagsOffset));
75 __ and_(offset, ~Code::kFlagsNotUsedInLookup);
76 __ cmp(offset, flags);
77 __ j(not_equal, &miss);
78
79 #ifdef DEBUG
80 if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) {
81 __ jmp(&miss);
82 } else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) {
83 __ jmp(&miss);
84 }
85 #endif
86
87 // Jump to the first instruction in the code stub.
88 __ add(extra, Immediate(Code::kHeaderSize - kHeapObjectTag));
89 __ jmp(extra);
90
91 __ bind(&miss);
92 } else {
93 // Save the offset on the stack.
94 __ push(offset);
95
96 // Check that the key in the entry matches the name.
97 __ cmp(name, Operand::StaticArray(offset, times_1, key_offset));
98 __ j(not_equal, &miss);
99
100 // Check the map matches.
101 __ mov(offset, Operand::StaticArray(offset, times_1, map_offset));
102 __ cmp(offset, FieldOperand(receiver, HeapObject::kMapOffset));
103 __ j(not_equal, &miss);
104
105 // Restore offset register.
106 __ mov(offset, Operand(esp, 0));
107
108 // Get the code entry from the cache.
109 __ mov(offset, Operand::StaticArray(offset, times_1, value_offset));
110
111 // Check that the flags match what we're looking for.
112 __ mov(offset, FieldOperand(offset, Code::kFlagsOffset));
113 __ and_(offset, ~Code::kFlagsNotUsedInLookup);
114 __ cmp(offset, flags);
115 __ j(not_equal, &miss);
116
117 #ifdef DEBUG
118 if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) {
119 __ jmp(&miss);
120 } else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) {
121 __ jmp(&miss);
122 }
123 #endif
124
125 // Restore offset and re-load code entry from cache.
126 __ pop(offset);
127 __ mov(offset, Operand::StaticArray(offset, times_1, value_offset));
128
129 // Jump to the first instruction in the code stub.
130 __ add(offset, Immediate(Code::kHeaderSize - kHeapObjectTag));
131 __ jmp(offset);
132
133 // Pop at miss.
134 __ bind(&miss);
135 __ pop(offset);
136 }
137 }
138
139
140 // Helper function used to check that the dictionary doesn't contain
141 // the property. This function may return false negatives, so miss_label
142 // must always call a backup property check that is complete.
143 // This function is safe to call if the receiver has fast properties.
144 // Name must be a symbol and receiver must be a heap object.
GenerateDictionaryNegativeLookup(MacroAssembler * masm,Label * miss_label,Register receiver,Handle<String> name,Register r0,Register r1)145 static void GenerateDictionaryNegativeLookup(MacroAssembler* masm,
146 Label* miss_label,
147 Register receiver,
148 Handle<String> name,
149 Register r0,
150 Register r1) {
151 ASSERT(name->IsSymbol());
152 Counters* counters = masm->isolate()->counters();
153 __ IncrementCounter(counters->negative_lookups(), 1);
154 __ IncrementCounter(counters->negative_lookups_miss(), 1);
155
156 __ mov(r0, FieldOperand(receiver, HeapObject::kMapOffset));
157
158 const int kInterceptorOrAccessCheckNeededMask =
159 (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded);
160
161 // Bail out if the receiver has a named interceptor or requires access checks.
162 __ test_b(FieldOperand(r0, Map::kBitFieldOffset),
163 kInterceptorOrAccessCheckNeededMask);
164 __ j(not_zero, miss_label);
165
166 // Check that receiver is a JSObject.
167 __ CmpInstanceType(r0, FIRST_SPEC_OBJECT_TYPE);
168 __ j(below, miss_label);
169
170 // Load properties array.
171 Register properties = r0;
172 __ mov(properties, FieldOperand(receiver, JSObject::kPropertiesOffset));
173
174 // Check that the properties array is a dictionary.
175 __ cmp(FieldOperand(properties, HeapObject::kMapOffset),
176 Immediate(masm->isolate()->factory()->hash_table_map()));
177 __ j(not_equal, miss_label);
178
179 Label done;
180 StringDictionaryLookupStub::GenerateNegativeLookup(masm,
181 miss_label,
182 &done,
183 properties,
184 name,
185 r1);
186 __ bind(&done);
187 __ DecrementCounter(counters->negative_lookups_miss(), 1);
188 }
189
190
GenerateProbe(MacroAssembler * masm,Code::Flags flags,Register receiver,Register name,Register scratch,Register extra,Register extra2,Register extra3)191 void StubCache::GenerateProbe(MacroAssembler* masm,
192 Code::Flags flags,
193 Register receiver,
194 Register name,
195 Register scratch,
196 Register extra,
197 Register extra2,
198 Register extra3) {
199 Label miss;
200
201 // Assert that code is valid. The multiplying code relies on the entry size
202 // being 12.
203 ASSERT(sizeof(Entry) == 12);
204
205 // Assert the flags do not name a specific type.
206 ASSERT(Code::ExtractTypeFromFlags(flags) == 0);
207
208 // Assert that there are no register conflicts.
209 ASSERT(!scratch.is(receiver));
210 ASSERT(!scratch.is(name));
211 ASSERT(!extra.is(receiver));
212 ASSERT(!extra.is(name));
213 ASSERT(!extra.is(scratch));
214
215 // Assert scratch and extra registers are valid, and extra2/3 are unused.
216 ASSERT(!scratch.is(no_reg));
217 ASSERT(extra2.is(no_reg));
218 ASSERT(extra3.is(no_reg));
219
220 Register offset = scratch;
221 scratch = no_reg;
222
223 Counters* counters = masm->isolate()->counters();
224 __ IncrementCounter(counters->megamorphic_stub_cache_probes(), 1);
225
226 // Check that the receiver isn't a smi.
227 __ JumpIfSmi(receiver, &miss);
228
229 // Get the map of the receiver and compute the hash.
230 __ mov(offset, FieldOperand(name, String::kHashFieldOffset));
231 __ add(offset, FieldOperand(receiver, HeapObject::kMapOffset));
232 __ xor_(offset, flags);
233 // We mask out the last two bits because they are not part of the hash and
234 // they are always 01 for maps. Also in the two 'and' instructions below.
235 __ and_(offset, (kPrimaryTableSize - 1) << kHeapObjectTagSize);
236 // ProbeTable expects the offset to be pointer scaled, which it is, because
237 // the heap object tag size is 2 and the pointer size log 2 is also 2.
238 ASSERT(kHeapObjectTagSize == kPointerSizeLog2);
239
240 // Probe the primary table.
241 ProbeTable(isolate(), masm, flags, kPrimary, name, receiver, offset, extra);
242
243 // Primary miss: Compute hash for secondary probe.
244 __ mov(offset, FieldOperand(name, String::kHashFieldOffset));
245 __ add(offset, FieldOperand(receiver, HeapObject::kMapOffset));
246 __ xor_(offset, flags);
247 __ and_(offset, (kPrimaryTableSize - 1) << kHeapObjectTagSize);
248 __ sub(offset, name);
249 __ add(offset, Immediate(flags));
250 __ and_(offset, (kSecondaryTableSize - 1) << kHeapObjectTagSize);
251
252 // Probe the secondary table.
253 ProbeTable(
254 isolate(), masm, flags, kSecondary, name, receiver, offset, extra);
255
256 // Cache miss: Fall-through and let caller handle the miss by
257 // entering the runtime system.
258 __ bind(&miss);
259 __ IncrementCounter(counters->megamorphic_stub_cache_misses(), 1);
260 }
261
262
GenerateLoadGlobalFunctionPrototype(MacroAssembler * masm,int index,Register prototype)263 void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm,
264 int index,
265 Register prototype) {
266 __ LoadGlobalFunction(index, prototype);
267 __ LoadGlobalFunctionInitialMap(prototype, prototype);
268 // Load the prototype from the initial map.
269 __ mov(prototype, FieldOperand(prototype, Map::kPrototypeOffset));
270 }
271
272
GenerateDirectLoadGlobalFunctionPrototype(MacroAssembler * masm,int index,Register prototype,Label * miss)273 void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype(
274 MacroAssembler* masm,
275 int index,
276 Register prototype,
277 Label* miss) {
278 // Check we're still in the same context.
279 __ cmp(Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)),
280 masm->isolate()->global());
281 __ j(not_equal, miss);
282 // Get the global function with the given index.
283 Handle<JSFunction> function(
284 JSFunction::cast(masm->isolate()->global_context()->get(index)));
285 // Load its initial map. The global functions all have initial maps.
286 __ Set(prototype, Immediate(Handle<Map>(function->initial_map())));
287 // Load the prototype from the initial map.
288 __ mov(prototype, FieldOperand(prototype, Map::kPrototypeOffset));
289 }
290
291
GenerateLoadArrayLength(MacroAssembler * masm,Register receiver,Register scratch,Label * miss_label)292 void StubCompiler::GenerateLoadArrayLength(MacroAssembler* masm,
293 Register receiver,
294 Register scratch,
295 Label* miss_label) {
296 // Check that the receiver isn't a smi.
297 __ JumpIfSmi(receiver, miss_label);
298
299 // Check that the object is a JS array.
300 __ CmpObjectType(receiver, JS_ARRAY_TYPE, scratch);
301 __ j(not_equal, miss_label);
302
303 // Load length directly from the JS array.
304 __ mov(eax, FieldOperand(receiver, JSArray::kLengthOffset));
305 __ ret(0);
306 }
307
308
309 // Generate code to check if an object is a string. If the object is
310 // a string, the map's instance type is left in the scratch register.
GenerateStringCheck(MacroAssembler * masm,Register receiver,Register scratch,Label * smi,Label * non_string_object)311 static void GenerateStringCheck(MacroAssembler* masm,
312 Register receiver,
313 Register scratch,
314 Label* smi,
315 Label* non_string_object) {
316 // Check that the object isn't a smi.
317 __ JumpIfSmi(receiver, smi);
318
319 // Check that the object is a string.
320 __ mov(scratch, FieldOperand(receiver, HeapObject::kMapOffset));
321 __ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
322 STATIC_ASSERT(kNotStringTag != 0);
323 __ test(scratch, Immediate(kNotStringTag));
324 __ j(not_zero, non_string_object);
325 }
326
327
GenerateLoadStringLength(MacroAssembler * masm,Register receiver,Register scratch1,Register scratch2,Label * miss,bool support_wrappers)328 void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm,
329 Register receiver,
330 Register scratch1,
331 Register scratch2,
332 Label* miss,
333 bool support_wrappers) {
334 Label check_wrapper;
335
336 // Check if the object is a string leaving the instance type in the
337 // scratch register.
338 GenerateStringCheck(masm, receiver, scratch1, miss,
339 support_wrappers ? &check_wrapper : miss);
340
341 // Load length from the string and convert to a smi.
342 __ mov(eax, FieldOperand(receiver, String::kLengthOffset));
343 __ ret(0);
344
345 if (support_wrappers) {
346 // Check if the object is a JSValue wrapper.
347 __ bind(&check_wrapper);
348 __ cmp(scratch1, JS_VALUE_TYPE);
349 __ j(not_equal, miss);
350
351 // Check if the wrapped value is a string and load the length
352 // directly if it is.
353 __ mov(scratch2, FieldOperand(receiver, JSValue::kValueOffset));
354 GenerateStringCheck(masm, scratch2, scratch1, miss, miss);
355 __ mov(eax, FieldOperand(scratch2, String::kLengthOffset));
356 __ ret(0);
357 }
358 }
359
360
GenerateLoadFunctionPrototype(MacroAssembler * masm,Register receiver,Register scratch1,Register scratch2,Label * miss_label)361 void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm,
362 Register receiver,
363 Register scratch1,
364 Register scratch2,
365 Label* miss_label) {
366 __ TryGetFunctionPrototype(receiver, scratch1, scratch2, miss_label);
367 __ mov(eax, scratch1);
368 __ ret(0);
369 }
370
371
372 // Load a fast property out of a holder object (src). In-object properties
373 // are loaded directly otherwise the property is loaded from the properties
374 // fixed array.
GenerateFastPropertyLoad(MacroAssembler * masm,Register dst,Register src,Handle<JSObject> holder,int index)375 void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm,
376 Register dst,
377 Register src,
378 Handle<JSObject> holder,
379 int index) {
380 // Adjust for the number of properties stored in the holder.
381 index -= holder->map()->inobject_properties();
382 if (index < 0) {
383 // Get the property straight out of the holder.
384 int offset = holder->map()->instance_size() + (index * kPointerSize);
385 __ mov(dst, FieldOperand(src, offset));
386 } else {
387 // Calculate the offset into the properties array.
388 int offset = index * kPointerSize + FixedArray::kHeaderSize;
389 __ mov(dst, FieldOperand(src, JSObject::kPropertiesOffset));
390 __ mov(dst, FieldOperand(dst, offset));
391 }
392 }
393
394
PushInterceptorArguments(MacroAssembler * masm,Register receiver,Register holder,Register name,Handle<JSObject> holder_obj)395 static void PushInterceptorArguments(MacroAssembler* masm,
396 Register receiver,
397 Register holder,
398 Register name,
399 Handle<JSObject> holder_obj) {
400 __ push(name);
401 Handle<InterceptorInfo> interceptor(holder_obj->GetNamedInterceptor());
402 ASSERT(!masm->isolate()->heap()->InNewSpace(*interceptor));
403 Register scratch = name;
404 __ mov(scratch, Immediate(interceptor));
405 __ push(scratch);
406 __ push(receiver);
407 __ push(holder);
408 __ push(FieldOperand(scratch, InterceptorInfo::kDataOffset));
409 }
410
411
CompileCallLoadPropertyWithInterceptor(MacroAssembler * masm,Register receiver,Register holder,Register name,Handle<JSObject> holder_obj)412 static void CompileCallLoadPropertyWithInterceptor(
413 MacroAssembler* masm,
414 Register receiver,
415 Register holder,
416 Register name,
417 Handle<JSObject> holder_obj) {
418 PushInterceptorArguments(masm, receiver, holder, name, holder_obj);
419 __ CallExternalReference(
420 ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorOnly),
421 masm->isolate()),
422 5);
423 }
424
425
426 // Number of pointers to be reserved on stack for fast API call.
427 static const int kFastApiCallArguments = 3;
428
429
430 // Reserves space for the extra arguments to API function in the
431 // caller's frame.
432 //
433 // These arguments are set by CheckPrototypes and GenerateFastApiCall.
ReserveSpaceForFastApiCall(MacroAssembler * masm,Register scratch)434 static void ReserveSpaceForFastApiCall(MacroAssembler* masm, Register scratch) {
435 // ----------- S t a t e -------------
436 // -- esp[0] : return address
437 // -- esp[4] : last argument in the internal frame of the caller
438 // -----------------------------------
439 __ pop(scratch);
440 for (int i = 0; i < kFastApiCallArguments; i++) {
441 __ push(Immediate(Smi::FromInt(0)));
442 }
443 __ push(scratch);
444 }
445
446
447 // Undoes the effects of ReserveSpaceForFastApiCall.
FreeSpaceForFastApiCall(MacroAssembler * masm,Register scratch)448 static void FreeSpaceForFastApiCall(MacroAssembler* masm, Register scratch) {
449 // ----------- S t a t e -------------
450 // -- esp[0] : return address.
451 // -- esp[4] : last fast api call extra argument.
452 // -- ...
453 // -- esp[kFastApiCallArguments * 4] : first fast api call extra argument.
454 // -- esp[kFastApiCallArguments * 4 + 4] : last argument in the internal
455 // frame.
456 // -----------------------------------
457 __ pop(scratch);
458 __ add(esp, Immediate(kPointerSize * kFastApiCallArguments));
459 __ push(scratch);
460 }
461
462
463 // Generates call to API function.
GenerateFastApiCall(MacroAssembler * masm,const CallOptimization & optimization,int argc)464 static void GenerateFastApiCall(MacroAssembler* masm,
465 const CallOptimization& optimization,
466 int argc) {
467 // ----------- S t a t e -------------
468 // -- esp[0] : return address
469 // -- esp[4] : object passing the type check
470 // (last fast api call extra argument,
471 // set by CheckPrototypes)
472 // -- esp[8] : api function
473 // (first fast api call extra argument)
474 // -- esp[12] : api call data
475 // -- esp[16] : last argument
476 // -- ...
477 // -- esp[(argc + 3) * 4] : first argument
478 // -- esp[(argc + 4) * 4] : receiver
479 // -----------------------------------
480 // Get the function and setup the context.
481 Handle<JSFunction> function = optimization.constant_function();
482 __ LoadHeapObject(edi, function);
483 __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
484
485 // Pass the additional arguments.
486 __ mov(Operand(esp, 2 * kPointerSize), edi);
487 Handle<CallHandlerInfo> api_call_info = optimization.api_call_info();
488 Handle<Object> call_data(api_call_info->data());
489 if (masm->isolate()->heap()->InNewSpace(*call_data)) {
490 __ mov(ecx, api_call_info);
491 __ mov(ebx, FieldOperand(ecx, CallHandlerInfo::kDataOffset));
492 __ mov(Operand(esp, 3 * kPointerSize), ebx);
493 } else {
494 __ mov(Operand(esp, 3 * kPointerSize), Immediate(call_data));
495 }
496
497 // Prepare arguments.
498 __ lea(eax, Operand(esp, 3 * kPointerSize));
499
500 const int kApiArgc = 1; // API function gets reference to the v8::Arguments.
501
502 // Allocate the v8::Arguments structure in the arguments' space since
503 // it's not controlled by GC.
504 const int kApiStackSpace = 4;
505
506 __ PrepareCallApiFunction(kApiArgc + kApiStackSpace);
507
508 __ mov(ApiParameterOperand(1), eax); // v8::Arguments::implicit_args_.
509 __ add(eax, Immediate(argc * kPointerSize));
510 __ mov(ApiParameterOperand(2), eax); // v8::Arguments::values_.
511 __ Set(ApiParameterOperand(3), Immediate(argc)); // v8::Arguments::length_.
512 // v8::Arguments::is_construct_call_.
513 __ Set(ApiParameterOperand(4), Immediate(0));
514
515 // v8::InvocationCallback's argument.
516 __ lea(eax, ApiParameterOperand(1));
517 __ mov(ApiParameterOperand(0), eax);
518
519 // Function address is a foreign pointer outside V8's heap.
520 Address function_address = v8::ToCData<Address>(api_call_info->callback());
521 __ CallApiFunctionAndReturn(function_address,
522 argc + kFastApiCallArguments + 1);
523 }
524
525
526 class CallInterceptorCompiler BASE_EMBEDDED {
527 public:
CallInterceptorCompiler(StubCompiler * stub_compiler,const ParameterCount & arguments,Register name,Code::ExtraICState extra_state)528 CallInterceptorCompiler(StubCompiler* stub_compiler,
529 const ParameterCount& arguments,
530 Register name,
531 Code::ExtraICState extra_state)
532 : stub_compiler_(stub_compiler),
533 arguments_(arguments),
534 name_(name),
535 extra_state_(extra_state) {}
536
Compile(MacroAssembler * masm,Handle<JSObject> object,Handle<JSObject> holder,Handle<String> name,LookupResult * lookup,Register receiver,Register scratch1,Register scratch2,Register scratch3,Label * miss)537 void Compile(MacroAssembler* masm,
538 Handle<JSObject> object,
539 Handle<JSObject> holder,
540 Handle<String> name,
541 LookupResult* lookup,
542 Register receiver,
543 Register scratch1,
544 Register scratch2,
545 Register scratch3,
546 Label* miss) {
547 ASSERT(holder->HasNamedInterceptor());
548 ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined());
549
550 // Check that the receiver isn't a smi.
551 __ JumpIfSmi(receiver, miss);
552
553 CallOptimization optimization(lookup);
554 if (optimization.is_constant_call()) {
555 CompileCacheable(masm, object, receiver, scratch1, scratch2, scratch3,
556 holder, lookup, name, optimization, miss);
557 } else {
558 CompileRegular(masm, object, receiver, scratch1, scratch2, scratch3,
559 name, holder, miss);
560 }
561 }
562
563 private:
CompileCacheable(MacroAssembler * masm,Handle<JSObject> object,Register receiver,Register scratch1,Register scratch2,Register scratch3,Handle<JSObject> interceptor_holder,LookupResult * lookup,Handle<String> name,const CallOptimization & optimization,Label * miss_label)564 void CompileCacheable(MacroAssembler* masm,
565 Handle<JSObject> object,
566 Register receiver,
567 Register scratch1,
568 Register scratch2,
569 Register scratch3,
570 Handle<JSObject> interceptor_holder,
571 LookupResult* lookup,
572 Handle<String> name,
573 const CallOptimization& optimization,
574 Label* miss_label) {
575 ASSERT(optimization.is_constant_call());
576 ASSERT(!lookup->holder()->IsGlobalObject());
577
578 int depth1 = kInvalidProtoDepth;
579 int depth2 = kInvalidProtoDepth;
580 bool can_do_fast_api_call = false;
581 if (optimization.is_simple_api_call() &&
582 !lookup->holder()->IsGlobalObject()) {
583 depth1 = optimization.GetPrototypeDepthOfExpectedType(
584 object, interceptor_holder);
585 if (depth1 == kInvalidProtoDepth) {
586 depth2 = optimization.GetPrototypeDepthOfExpectedType(
587 interceptor_holder, Handle<JSObject>(lookup->holder()));
588 }
589 can_do_fast_api_call =
590 depth1 != kInvalidProtoDepth || depth2 != kInvalidProtoDepth;
591 }
592
593 Counters* counters = masm->isolate()->counters();
594 __ IncrementCounter(counters->call_const_interceptor(), 1);
595
596 if (can_do_fast_api_call) {
597 __ IncrementCounter(counters->call_const_interceptor_fast_api(), 1);
598 ReserveSpaceForFastApiCall(masm, scratch1);
599 }
600
601 // Check that the maps from receiver to interceptor's holder
602 // haven't changed and thus we can invoke interceptor.
603 Label miss_cleanup;
604 Label* miss = can_do_fast_api_call ? &miss_cleanup : miss_label;
605 Register holder =
606 stub_compiler_->CheckPrototypes(object, receiver, interceptor_holder,
607 scratch1, scratch2, scratch3,
608 name, depth1, miss);
609
610 // Invoke an interceptor and if it provides a value,
611 // branch to |regular_invoke|.
612 Label regular_invoke;
613 LoadWithInterceptor(masm, receiver, holder, interceptor_holder,
614 ®ular_invoke);
615
616 // Interceptor returned nothing for this property. Try to use cached
617 // constant function.
618
619 // Check that the maps from interceptor's holder to constant function's
620 // holder haven't changed and thus we can use cached constant function.
621 if (*interceptor_holder != lookup->holder()) {
622 stub_compiler_->CheckPrototypes(interceptor_holder, receiver,
623 Handle<JSObject>(lookup->holder()),
624 scratch1, scratch2, scratch3,
625 name, depth2, miss);
626 } else {
627 // CheckPrototypes has a side effect of fetching a 'holder'
628 // for API (object which is instanceof for the signature). It's
629 // safe to omit it here, as if present, it should be fetched
630 // by the previous CheckPrototypes.
631 ASSERT(depth2 == kInvalidProtoDepth);
632 }
633
634 // Invoke function.
635 if (can_do_fast_api_call) {
636 GenerateFastApiCall(masm, optimization, arguments_.immediate());
637 } else {
638 CallKind call_kind = CallICBase::Contextual::decode(extra_state_)
639 ? CALL_AS_FUNCTION
640 : CALL_AS_METHOD;
641 __ InvokeFunction(optimization.constant_function(), arguments_,
642 JUMP_FUNCTION, NullCallWrapper(), call_kind);
643 }
644
645 // Deferred code for fast API call case---clean preallocated space.
646 if (can_do_fast_api_call) {
647 __ bind(&miss_cleanup);
648 FreeSpaceForFastApiCall(masm, scratch1);
649 __ jmp(miss_label);
650 }
651
652 // Invoke a regular function.
653 __ bind(®ular_invoke);
654 if (can_do_fast_api_call) {
655 FreeSpaceForFastApiCall(masm, scratch1);
656 }
657 }
658
CompileRegular(MacroAssembler * masm,Handle<JSObject> object,Register receiver,Register scratch1,Register scratch2,Register scratch3,Handle<String> name,Handle<JSObject> interceptor_holder,Label * miss_label)659 void CompileRegular(MacroAssembler* masm,
660 Handle<JSObject> object,
661 Register receiver,
662 Register scratch1,
663 Register scratch2,
664 Register scratch3,
665 Handle<String> name,
666 Handle<JSObject> interceptor_holder,
667 Label* miss_label) {
668 Register holder =
669 stub_compiler_->CheckPrototypes(object, receiver, interceptor_holder,
670 scratch1, scratch2, scratch3,
671 name, miss_label);
672
673 FrameScope scope(masm, StackFrame::INTERNAL);
674 // Save the name_ register across the call.
675 __ push(name_);
676
677 PushInterceptorArguments(masm, receiver, holder, name_, interceptor_holder);
678
679 __ CallExternalReference(
680 ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForCall),
681 masm->isolate()),
682 5);
683
684 // Restore the name_ register.
685 __ pop(name_);
686
687 // Leave the internal frame.
688 }
689
LoadWithInterceptor(MacroAssembler * masm,Register receiver,Register holder,Handle<JSObject> holder_obj,Label * interceptor_succeeded)690 void LoadWithInterceptor(MacroAssembler* masm,
691 Register receiver,
692 Register holder,
693 Handle<JSObject> holder_obj,
694 Label* interceptor_succeeded) {
695 {
696 FrameScope scope(masm, StackFrame::INTERNAL);
697 __ push(holder); // Save the holder.
698 __ push(name_); // Save the name.
699
700 CompileCallLoadPropertyWithInterceptor(masm,
701 receiver,
702 holder,
703 name_,
704 holder_obj);
705
706 __ pop(name_); // Restore the name.
707 __ pop(receiver); // Restore the holder.
708 // Leave the internal frame.
709 }
710
711 __ cmp(eax, masm->isolate()->factory()->no_interceptor_result_sentinel());
712 __ j(not_equal, interceptor_succeeded);
713 }
714
715 StubCompiler* stub_compiler_;
716 const ParameterCount& arguments_;
717 Register name_;
718 Code::ExtraICState extra_state_;
719 };
720
721
GenerateLoadMiss(MacroAssembler * masm,Code::Kind kind)722 void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) {
723 ASSERT(kind == Code::LOAD_IC || kind == Code::KEYED_LOAD_IC);
724 Handle<Code> code = (kind == Code::LOAD_IC)
725 ? masm->isolate()->builtins()->LoadIC_Miss()
726 : masm->isolate()->builtins()->KeyedLoadIC_Miss();
727 __ jmp(code, RelocInfo::CODE_TARGET);
728 }
729
730
GenerateKeyedLoadMissForceGeneric(MacroAssembler * masm)731 void StubCompiler::GenerateKeyedLoadMissForceGeneric(MacroAssembler* masm) {
732 Handle<Code> code =
733 masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric();
734 __ jmp(code, RelocInfo::CODE_TARGET);
735 }
736
737
738 // Both name_reg and receiver_reg are preserved on jumps to miss_label,
739 // but may be destroyed if store is successful.
GenerateStoreField(MacroAssembler * masm,Handle<JSObject> object,int index,Handle<Map> transition,Register receiver_reg,Register name_reg,Register scratch,Label * miss_label)740 void StubCompiler::GenerateStoreField(MacroAssembler* masm,
741 Handle<JSObject> object,
742 int index,
743 Handle<Map> transition,
744 Register receiver_reg,
745 Register name_reg,
746 Register scratch,
747 Label* miss_label) {
748 // Check that the map of the object hasn't changed.
749 CompareMapMode mode = transition.is_null() ? ALLOW_ELEMENT_TRANSITION_MAPS
750 : REQUIRE_EXACT_MAP;
751 __ CheckMap(receiver_reg, Handle<Map>(object->map()),
752 miss_label, DO_SMI_CHECK, mode);
753
754 // Perform global security token check if needed.
755 if (object->IsJSGlobalProxy()) {
756 __ CheckAccessGlobalProxy(receiver_reg, scratch, miss_label);
757 }
758
759 // Stub never generated for non-global objects that require access
760 // checks.
761 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
762
763 // Perform map transition for the receiver if necessary.
764 if (!transition.is_null() && (object->map()->unused_property_fields() == 0)) {
765 // The properties must be extended before we can store the value.
766 // We jump to a runtime call that extends the properties array.
767 __ pop(scratch); // Return address.
768 __ push(receiver_reg);
769 __ push(Immediate(transition));
770 __ push(eax);
771 __ push(scratch);
772 __ TailCallExternalReference(
773 ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage),
774 masm->isolate()),
775 3,
776 1);
777 return;
778 }
779
780 if (!transition.is_null()) {
781 // Update the map of the object; no write barrier updating is
782 // needed because the map is never in new space.
783 __ mov(FieldOperand(receiver_reg, HeapObject::kMapOffset),
784 Immediate(transition));
785 }
786
787 // Adjust for the number of properties stored in the object. Even in the
788 // face of a transition we can use the old map here because the size of the
789 // object and the number of in-object properties is not going to change.
790 index -= object->map()->inobject_properties();
791
792 if (index < 0) {
793 // Set the property straight into the object.
794 int offset = object->map()->instance_size() + (index * kPointerSize);
795 __ mov(FieldOperand(receiver_reg, offset), eax);
796
797 // Update the write barrier for the array address.
798 // Pass the value being stored in the now unused name_reg.
799 __ mov(name_reg, eax);
800 __ RecordWriteField(receiver_reg,
801 offset,
802 name_reg,
803 scratch,
804 kDontSaveFPRegs);
805 } else {
806 // Write to the properties array.
807 int offset = index * kPointerSize + FixedArray::kHeaderSize;
808 // Get the properties array (optimistically).
809 __ mov(scratch, FieldOperand(receiver_reg, JSObject::kPropertiesOffset));
810 __ mov(FieldOperand(scratch, offset), eax);
811
812 // Update the write barrier for the array address.
813 // Pass the value being stored in the now unused name_reg.
814 __ mov(name_reg, eax);
815 __ RecordWriteField(scratch,
816 offset,
817 name_reg,
818 receiver_reg,
819 kDontSaveFPRegs);
820 }
821
822 // Return the value (register eax).
823 __ ret(0);
824 }
825
826
827 // Generate code to check that a global property cell is empty. Create
828 // the property cell at compilation time if no cell exists for the
829 // property.
GenerateCheckPropertyCell(MacroAssembler * masm,Handle<GlobalObject> global,Handle<String> name,Register scratch,Label * miss)830 static void GenerateCheckPropertyCell(MacroAssembler* masm,
831 Handle<GlobalObject> global,
832 Handle<String> name,
833 Register scratch,
834 Label* miss) {
835 Handle<JSGlobalPropertyCell> cell =
836 GlobalObject::EnsurePropertyCell(global, name);
837 ASSERT(cell->value()->IsTheHole());
838 Handle<Oddball> the_hole = masm->isolate()->factory()->the_hole_value();
839 if (Serializer::enabled()) {
840 __ mov(scratch, Immediate(cell));
841 __ cmp(FieldOperand(scratch, JSGlobalPropertyCell::kValueOffset),
842 Immediate(the_hole));
843 } else {
844 __ cmp(Operand::Cell(cell), Immediate(the_hole));
845 }
846 __ j(not_equal, miss);
847 }
848
849
850 // Calls GenerateCheckPropertyCell for each global object in the prototype chain
851 // from object to (but not including) holder.
GenerateCheckPropertyCells(MacroAssembler * masm,Handle<JSObject> object,Handle<JSObject> holder,Handle<String> name,Register scratch,Label * miss)852 static void GenerateCheckPropertyCells(MacroAssembler* masm,
853 Handle<JSObject> object,
854 Handle<JSObject> holder,
855 Handle<String> name,
856 Register scratch,
857 Label* miss) {
858 Handle<JSObject> current = object;
859 while (!current.is_identical_to(holder)) {
860 if (current->IsGlobalObject()) {
861 GenerateCheckPropertyCell(masm,
862 Handle<GlobalObject>::cast(current),
863 name,
864 scratch,
865 miss);
866 }
867 current = Handle<JSObject>(JSObject::cast(current->GetPrototype()));
868 }
869 }
870
871 #undef __
872 #define __ ACCESS_MASM(masm())
873
874
CheckPrototypes(Handle<JSObject> object,Register object_reg,Handle<JSObject> holder,Register holder_reg,Register scratch1,Register scratch2,Handle<String> name,int save_at_depth,Label * miss)875 Register StubCompiler::CheckPrototypes(Handle<JSObject> object,
876 Register object_reg,
877 Handle<JSObject> holder,
878 Register holder_reg,
879 Register scratch1,
880 Register scratch2,
881 Handle<String> name,
882 int save_at_depth,
883 Label* miss) {
884 // Make sure there's no overlap between holder and object registers.
885 ASSERT(!scratch1.is(object_reg) && !scratch1.is(holder_reg));
886 ASSERT(!scratch2.is(object_reg) && !scratch2.is(holder_reg)
887 && !scratch2.is(scratch1));
888
889 // Keep track of the current object in register reg.
890 Register reg = object_reg;
891 Handle<JSObject> current = object;
892 int depth = 0;
893
894 if (save_at_depth == depth) {
895 __ mov(Operand(esp, kPointerSize), reg);
896 }
897
898 // Traverse the prototype chain and check the maps in the prototype chain for
899 // fast and global objects or do negative lookup for normal objects.
900 while (!current.is_identical_to(holder)) {
901 ++depth;
902
903 // Only global objects and objects that do not require access
904 // checks are allowed in stubs.
905 ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded());
906
907 Handle<JSObject> prototype(JSObject::cast(current->GetPrototype()));
908 if (!current->HasFastProperties() &&
909 !current->IsJSGlobalObject() &&
910 !current->IsJSGlobalProxy()) {
911 if (!name->IsSymbol()) {
912 name = factory()->LookupSymbol(name);
913 }
914 ASSERT(current->property_dictionary()->FindEntry(*name) ==
915 StringDictionary::kNotFound);
916
917 GenerateDictionaryNegativeLookup(masm(), miss, reg, name,
918 scratch1, scratch2);
919
920 __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
921 reg = holder_reg; // From now on the object will be in holder_reg.
922 __ mov(reg, FieldOperand(scratch1, Map::kPrototypeOffset));
923 } else {
924 bool in_new_space = heap()->InNewSpace(*prototype);
925 Handle<Map> current_map(current->map());
926 if (in_new_space) {
927 // Save the map in scratch1 for later.
928 __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
929 }
930 __ CheckMap(reg, current_map, miss, DONT_DO_SMI_CHECK,
931 ALLOW_ELEMENT_TRANSITION_MAPS);
932
933 // Check access rights to the global object. This has to happen after
934 // the map check so that we know that the object is actually a global
935 // object.
936 if (current->IsJSGlobalProxy()) {
937 __ CheckAccessGlobalProxy(reg, scratch2, miss);
938 }
939 reg = holder_reg; // From now on the object will be in holder_reg.
940
941 if (in_new_space) {
942 // The prototype is in new space; we cannot store a reference to it
943 // in the code. Load it from the map.
944 __ mov(reg, FieldOperand(scratch1, Map::kPrototypeOffset));
945 } else {
946 // The prototype is in old space; load it directly.
947 __ mov(reg, prototype);
948 }
949 }
950
951 if (save_at_depth == depth) {
952 __ mov(Operand(esp, kPointerSize), reg);
953 }
954
955 // Go to the next object in the prototype chain.
956 current = prototype;
957 }
958 ASSERT(current.is_identical_to(holder));
959
960 // Log the check depth.
961 LOG(isolate(), IntEvent("check-maps-depth", depth + 1));
962
963 // Check the holder map.
964 __ CheckMap(reg, Handle<Map>(holder->map()),
965 miss, DONT_DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS);
966
967 // Perform security check for access to the global object.
968 ASSERT(holder->IsJSGlobalProxy() || !holder->IsAccessCheckNeeded());
969 if (holder->IsJSGlobalProxy()) {
970 __ CheckAccessGlobalProxy(reg, scratch1, miss);
971 }
972
973 // If we've skipped any global objects, it's not enough to verify that
974 // their maps haven't changed. We also need to check that the property
975 // cell for the property is still empty.
976 GenerateCheckPropertyCells(masm(), object, holder, name, scratch1, miss);
977
978 // Return the register containing the holder.
979 return reg;
980 }
981
982
GenerateLoadField(Handle<JSObject> object,Handle<JSObject> holder,Register receiver,Register scratch1,Register scratch2,Register scratch3,int index,Handle<String> name,Label * miss)983 void StubCompiler::GenerateLoadField(Handle<JSObject> object,
984 Handle<JSObject> holder,
985 Register receiver,
986 Register scratch1,
987 Register scratch2,
988 Register scratch3,
989 int index,
990 Handle<String> name,
991 Label* miss) {
992 // Check that the receiver isn't a smi.
993 __ JumpIfSmi(receiver, miss);
994
995 // Check the prototype chain.
996 Register reg = CheckPrototypes(
997 object, receiver, holder, scratch1, scratch2, scratch3, name, miss);
998
999 // Get the value from the properties.
1000 GenerateFastPropertyLoad(masm(), eax, reg, holder, index);
1001 __ ret(0);
1002 }
1003
1004
GenerateLoadCallback(Handle<JSObject> object,Handle<JSObject> holder,Register receiver,Register name_reg,Register scratch1,Register scratch2,Register scratch3,Handle<AccessorInfo> callback,Handle<String> name,Label * miss)1005 void StubCompiler::GenerateLoadCallback(Handle<JSObject> object,
1006 Handle<JSObject> holder,
1007 Register receiver,
1008 Register name_reg,
1009 Register scratch1,
1010 Register scratch2,
1011 Register scratch3,
1012 Handle<AccessorInfo> callback,
1013 Handle<String> name,
1014 Label* miss) {
1015 // Check that the receiver isn't a smi.
1016 __ JumpIfSmi(receiver, miss);
1017
1018 // Check that the maps haven't changed.
1019 Register reg = CheckPrototypes(object, receiver, holder, scratch1,
1020 scratch2, scratch3, name, miss);
1021
1022 // Insert additional parameters into the stack frame above return address.
1023 ASSERT(!scratch3.is(reg));
1024 __ pop(scratch3); // Get return address to place it below.
1025
1026 __ push(receiver); // receiver
1027 __ mov(scratch2, esp);
1028 ASSERT(!scratch2.is(reg));
1029 __ push(reg); // holder
1030 // Push data from AccessorInfo.
1031 if (isolate()->heap()->InNewSpace(callback->data())) {
1032 __ mov(scratch1, Immediate(callback));
1033 __ push(FieldOperand(scratch1, AccessorInfo::kDataOffset));
1034 } else {
1035 __ push(Immediate(Handle<Object>(callback->data())));
1036 }
1037
1038 // Save a pointer to where we pushed the arguments pointer.
1039 // This will be passed as the const AccessorInfo& to the C++ callback.
1040 __ push(scratch2);
1041
1042 __ push(name_reg); // name
1043 __ mov(ebx, esp); // esp points to reference to name (handler).
1044
1045 __ push(scratch3); // Restore return address.
1046
1047 // 3 elements array for v8::Arguments::values_, handler for name and pointer
1048 // to the values (it considered as smi in GC).
1049 const int kStackSpace = 5;
1050 const int kApiArgc = 2;
1051
1052 __ PrepareCallApiFunction(kApiArgc);
1053 __ mov(ApiParameterOperand(0), ebx); // name.
1054 __ add(ebx, Immediate(kPointerSize));
1055 __ mov(ApiParameterOperand(1), ebx); // arguments pointer.
1056
1057 // Emitting a stub call may try to allocate (if the code is not
1058 // already generated). Do not allow the assembler to perform a
1059 // garbage collection but instead return the allocation failure
1060 // object.
1061 Address getter_address = v8::ToCData<Address>(callback->getter());
1062 __ CallApiFunctionAndReturn(getter_address, kStackSpace);
1063 }
1064
1065
GenerateLoadConstant(Handle<JSObject> object,Handle<JSObject> holder,Register receiver,Register scratch1,Register scratch2,Register scratch3,Handle<JSFunction> value,Handle<String> name,Label * miss)1066 void StubCompiler::GenerateLoadConstant(Handle<JSObject> object,
1067 Handle<JSObject> holder,
1068 Register receiver,
1069 Register scratch1,
1070 Register scratch2,
1071 Register scratch3,
1072 Handle<JSFunction> value,
1073 Handle<String> name,
1074 Label* miss) {
1075 // Check that the receiver isn't a smi.
1076 __ JumpIfSmi(receiver, miss);
1077
1078 // Check that the maps haven't changed.
1079 CheckPrototypes(
1080 object, receiver, holder, scratch1, scratch2, scratch3, name, miss);
1081
1082 // Return the constant value.
1083 __ LoadHeapObject(eax, value);
1084 __ ret(0);
1085 }
1086
1087
GenerateLoadInterceptor(Handle<JSObject> object,Handle<JSObject> interceptor_holder,LookupResult * lookup,Register receiver,Register name_reg,Register scratch1,Register scratch2,Register scratch3,Handle<String> name,Label * miss)1088 void StubCompiler::GenerateLoadInterceptor(Handle<JSObject> object,
1089 Handle<JSObject> interceptor_holder,
1090 LookupResult* lookup,
1091 Register receiver,
1092 Register name_reg,
1093 Register scratch1,
1094 Register scratch2,
1095 Register scratch3,
1096 Handle<String> name,
1097 Label* miss) {
1098 ASSERT(interceptor_holder->HasNamedInterceptor());
1099 ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined());
1100
1101 // Check that the receiver isn't a smi.
1102 __ JumpIfSmi(receiver, miss);
1103
1104 // So far the most popular follow ups for interceptor loads are FIELD
1105 // and CALLBACKS, so inline only them, other cases may be added
1106 // later.
1107 bool compile_followup_inline = false;
1108 if (lookup->IsFound() && lookup->IsCacheable()) {
1109 if (lookup->type() == FIELD) {
1110 compile_followup_inline = true;
1111 } else if (lookup->type() == CALLBACKS &&
1112 lookup->GetCallbackObject()->IsAccessorInfo()) {
1113 compile_followup_inline =
1114 AccessorInfo::cast(lookup->GetCallbackObject())->getter() != NULL;
1115 }
1116 }
1117
1118 if (compile_followup_inline) {
1119 // Compile the interceptor call, followed by inline code to load the
1120 // property from further up the prototype chain if the call fails.
1121 // Check that the maps haven't changed.
1122 Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder,
1123 scratch1, scratch2, scratch3,
1124 name, miss);
1125 ASSERT(holder_reg.is(receiver) || holder_reg.is(scratch1));
1126
1127 // Preserve the receiver register explicitly whenever it is different from
1128 // the holder and it is needed should the interceptor return without any
1129 // result. The CALLBACKS case needs the receiver to be passed into C++ code,
1130 // the FIELD case might cause a miss during the prototype check.
1131 bool must_perfrom_prototype_check = *interceptor_holder != lookup->holder();
1132 bool must_preserve_receiver_reg = !receiver.is(holder_reg) &&
1133 (lookup->type() == CALLBACKS || must_perfrom_prototype_check);
1134
1135 // Save necessary data before invoking an interceptor.
1136 // Requires a frame to make GC aware of pushed pointers.
1137 {
1138 FrameScope frame_scope(masm(), StackFrame::INTERNAL);
1139
1140 if (must_preserve_receiver_reg) {
1141 __ push(receiver);
1142 }
1143 __ push(holder_reg);
1144 __ push(name_reg);
1145
1146 // Invoke an interceptor. Note: map checks from receiver to
1147 // interceptor's holder has been compiled before (see a caller
1148 // of this method.)
1149 CompileCallLoadPropertyWithInterceptor(masm(),
1150 receiver,
1151 holder_reg,
1152 name_reg,
1153 interceptor_holder);
1154
1155 // Check if interceptor provided a value for property. If it's
1156 // the case, return immediately.
1157 Label interceptor_failed;
1158 __ cmp(eax, factory()->no_interceptor_result_sentinel());
1159 __ j(equal, &interceptor_failed);
1160 frame_scope.GenerateLeaveFrame();
1161 __ ret(0);
1162
1163 // Clobber registers when generating debug-code to provoke errors.
1164 __ bind(&interceptor_failed);
1165 if (FLAG_debug_code) {
1166 __ mov(receiver, Immediate(BitCast<int32_t>(kZapValue)));
1167 __ mov(holder_reg, Immediate(BitCast<int32_t>(kZapValue)));
1168 __ mov(name_reg, Immediate(BitCast<int32_t>(kZapValue)));
1169 }
1170
1171 __ pop(name_reg);
1172 __ pop(holder_reg);
1173 if (must_preserve_receiver_reg) {
1174 __ pop(receiver);
1175 }
1176
1177 // Leave the internal frame.
1178 }
1179
1180 // Check that the maps from interceptor's holder to lookup's holder
1181 // haven't changed. And load lookup's holder into holder_reg.
1182 if (must_perfrom_prototype_check) {
1183 holder_reg = CheckPrototypes(interceptor_holder,
1184 holder_reg,
1185 Handle<JSObject>(lookup->holder()),
1186 scratch1,
1187 scratch2,
1188 scratch3,
1189 name,
1190 miss);
1191 }
1192
1193 if (lookup->type() == FIELD) {
1194 // We found FIELD property in prototype chain of interceptor's holder.
1195 // Retrieve a field from field's holder.
1196 GenerateFastPropertyLoad(masm(), eax, holder_reg,
1197 Handle<JSObject>(lookup->holder()),
1198 lookup->GetFieldIndex());
1199 __ ret(0);
1200 } else {
1201 // We found CALLBACKS property in prototype chain of interceptor's
1202 // holder.
1203 ASSERT(lookup->type() == CALLBACKS);
1204 Handle<AccessorInfo> callback(
1205 AccessorInfo::cast(lookup->GetCallbackObject()));
1206 ASSERT(callback->getter() != NULL);
1207
1208 // Tail call to runtime.
1209 // Important invariant in CALLBACKS case: the code above must be
1210 // structured to never clobber |receiver| register.
1211 __ pop(scratch2); // return address
1212 __ push(receiver);
1213 __ push(holder_reg);
1214 __ mov(holder_reg, Immediate(callback));
1215 __ push(FieldOperand(holder_reg, AccessorInfo::kDataOffset));
1216 __ push(holder_reg);
1217 __ push(name_reg);
1218 __ push(scratch2); // restore return address
1219
1220 ExternalReference ref =
1221 ExternalReference(IC_Utility(IC::kLoadCallbackProperty),
1222 masm()->isolate());
1223 __ TailCallExternalReference(ref, 5, 1);
1224 }
1225 } else { // !compile_followup_inline
1226 // Call the runtime system to load the interceptor.
1227 // Check that the maps haven't changed.
1228 Register holder_reg =
1229 CheckPrototypes(object, receiver, interceptor_holder,
1230 scratch1, scratch2, scratch3, name, miss);
1231 __ pop(scratch2); // save old return address
1232 PushInterceptorArguments(masm(), receiver, holder_reg,
1233 name_reg, interceptor_holder);
1234 __ push(scratch2); // restore old return address
1235
1236 ExternalReference ref =
1237 ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForLoad),
1238 isolate());
1239 __ TailCallExternalReference(ref, 5, 1);
1240 }
1241 }
1242
1243
GenerateNameCheck(Handle<String> name,Label * miss)1244 void CallStubCompiler::GenerateNameCheck(Handle<String> name, Label* miss) {
1245 if (kind_ == Code::KEYED_CALL_IC) {
1246 __ cmp(ecx, Immediate(name));
1247 __ j(not_equal, miss);
1248 }
1249 }
1250
1251
GenerateGlobalReceiverCheck(Handle<JSObject> object,Handle<JSObject> holder,Handle<String> name,Label * miss)1252 void CallStubCompiler::GenerateGlobalReceiverCheck(Handle<JSObject> object,
1253 Handle<JSObject> holder,
1254 Handle<String> name,
1255 Label* miss) {
1256 ASSERT(holder->IsGlobalObject());
1257
1258 // Get the number of arguments.
1259 const int argc = arguments().immediate();
1260
1261 // Get the receiver from the stack.
1262 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
1263
1264
1265 // Check that the maps haven't changed.
1266 __ JumpIfSmi(edx, miss);
1267 CheckPrototypes(object, edx, holder, ebx, eax, edi, name, miss);
1268 }
1269
1270
GenerateLoadFunctionFromCell(Handle<JSGlobalPropertyCell> cell,Handle<JSFunction> function,Label * miss)1271 void CallStubCompiler::GenerateLoadFunctionFromCell(
1272 Handle<JSGlobalPropertyCell> cell,
1273 Handle<JSFunction> function,
1274 Label* miss) {
1275 // Get the value from the cell.
1276 if (Serializer::enabled()) {
1277 __ mov(edi, Immediate(cell));
1278 __ mov(edi, FieldOperand(edi, JSGlobalPropertyCell::kValueOffset));
1279 } else {
1280 __ mov(edi, Operand::Cell(cell));
1281 }
1282
1283 // Check that the cell contains the same function.
1284 if (isolate()->heap()->InNewSpace(*function)) {
1285 // We can't embed a pointer to a function in new space so we have
1286 // to verify that the shared function info is unchanged. This has
1287 // the nice side effect that multiple closures based on the same
1288 // function can all use this call IC. Before we load through the
1289 // function, we have to verify that it still is a function.
1290 __ JumpIfSmi(edi, miss);
1291 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ebx);
1292 __ j(not_equal, miss);
1293
1294 // Check the shared function info. Make sure it hasn't changed.
1295 __ cmp(FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset),
1296 Immediate(Handle<SharedFunctionInfo>(function->shared())));
1297 } else {
1298 __ cmp(edi, Immediate(function));
1299 }
1300 __ j(not_equal, miss);
1301 }
1302
1303
GenerateMissBranch()1304 void CallStubCompiler::GenerateMissBranch() {
1305 Handle<Code> code =
1306 isolate()->stub_cache()->ComputeCallMiss(arguments().immediate(),
1307 kind_,
1308 extra_state_);
1309 __ jmp(code, RelocInfo::CODE_TARGET);
1310 }
1311
1312
CompileCallField(Handle<JSObject> object,Handle<JSObject> holder,int index,Handle<String> name)1313 Handle<Code> CallStubCompiler::CompileCallField(Handle<JSObject> object,
1314 Handle<JSObject> holder,
1315 int index,
1316 Handle<String> name) {
1317 // ----------- S t a t e -------------
1318 // -- ecx : name
1319 // -- esp[0] : return address
1320 // -- esp[(argc - n) * 4] : arg[n] (zero-based)
1321 // -- ...
1322 // -- esp[(argc + 1) * 4] : receiver
1323 // -----------------------------------
1324 Label miss;
1325
1326 GenerateNameCheck(name, &miss);
1327
1328 // Get the receiver from the stack.
1329 const int argc = arguments().immediate();
1330 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
1331
1332 // Check that the receiver isn't a smi.
1333 __ JumpIfSmi(edx, &miss);
1334
1335 // Do the right check and compute the holder register.
1336 Register reg = CheckPrototypes(object, edx, holder, ebx, eax, edi,
1337 name, &miss);
1338
1339 GenerateFastPropertyLoad(masm(), edi, reg, holder, index);
1340
1341 // Check that the function really is a function.
1342 __ JumpIfSmi(edi, &miss);
1343 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ebx);
1344 __ j(not_equal, &miss);
1345
1346 // Patch the receiver on the stack with the global proxy if
1347 // necessary.
1348 if (object->IsGlobalObject()) {
1349 __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset));
1350 __ mov(Operand(esp, (argc + 1) * kPointerSize), edx);
1351 }
1352
1353 // Invoke the function.
1354 CallKind call_kind = CallICBase::Contextual::decode(extra_state_)
1355 ? CALL_AS_FUNCTION
1356 : CALL_AS_METHOD;
1357 __ InvokeFunction(edi, arguments(), JUMP_FUNCTION,
1358 NullCallWrapper(), call_kind);
1359
1360 // Handle call cache miss.
1361 __ bind(&miss);
1362 GenerateMissBranch();
1363
1364 // Return the generated code.
1365 return GetCode(FIELD, name);
1366 }
1367
1368
CompileArrayPushCall(Handle<Object> object,Handle<JSObject> holder,Handle<JSGlobalPropertyCell> cell,Handle<JSFunction> function,Handle<String> name)1369 Handle<Code> CallStubCompiler::CompileArrayPushCall(
1370 Handle<Object> object,
1371 Handle<JSObject> holder,
1372 Handle<JSGlobalPropertyCell> cell,
1373 Handle<JSFunction> function,
1374 Handle<String> name) {
1375 // ----------- S t a t e -------------
1376 // -- ecx : name
1377 // -- esp[0] : return address
1378 // -- esp[(argc - n) * 4] : arg[n] (zero-based)
1379 // -- ...
1380 // -- esp[(argc + 1) * 4] : receiver
1381 // -----------------------------------
1382
1383 // If object is not an array, bail out to regular call.
1384 if (!object->IsJSArray() || !cell.is_null()) {
1385 return Handle<Code>::null();
1386 }
1387
1388 Label miss;
1389
1390 GenerateNameCheck(name, &miss);
1391
1392 // Get the receiver from the stack.
1393 const int argc = arguments().immediate();
1394 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
1395
1396 // Check that the receiver isn't a smi.
1397 __ JumpIfSmi(edx, &miss);
1398
1399 CheckPrototypes(Handle<JSObject>::cast(object), edx, holder, ebx, eax, edi,
1400 name, &miss);
1401
1402 if (argc == 0) {
1403 // Noop, return the length.
1404 __ mov(eax, FieldOperand(edx, JSArray::kLengthOffset));
1405 __ ret((argc + 1) * kPointerSize);
1406 } else {
1407 Label call_builtin;
1408
1409 if (argc == 1) { // Otherwise fall through to call builtin.
1410 Label attempt_to_grow_elements, with_write_barrier;
1411
1412 // Get the elements array of the object.
1413 __ mov(edi, FieldOperand(edx, JSArray::kElementsOffset));
1414
1415 // Check that the elements are in fast mode and writable.
1416 __ cmp(FieldOperand(edi, HeapObject::kMapOffset),
1417 Immediate(factory()->fixed_array_map()));
1418 __ j(not_equal, &call_builtin);
1419
1420 // Get the array's length into eax and calculate new length.
1421 __ mov(eax, FieldOperand(edx, JSArray::kLengthOffset));
1422 STATIC_ASSERT(kSmiTagSize == 1);
1423 STATIC_ASSERT(kSmiTag == 0);
1424 __ add(eax, Immediate(Smi::FromInt(argc)));
1425
1426 // Get the elements' length into ecx.
1427 __ mov(ecx, FieldOperand(edi, FixedArray::kLengthOffset));
1428
1429 // Check if we could survive without allocation.
1430 __ cmp(eax, ecx);
1431 __ j(greater, &attempt_to_grow_elements);
1432
1433 // Check if value is a smi.
1434 __ mov(ecx, Operand(esp, argc * kPointerSize));
1435 __ JumpIfNotSmi(ecx, &with_write_barrier);
1436
1437 // Save new length.
1438 __ mov(FieldOperand(edx, JSArray::kLengthOffset), eax);
1439
1440 // Store the value.
1441 __ mov(FieldOperand(edi,
1442 eax,
1443 times_half_pointer_size,
1444 FixedArray::kHeaderSize - argc * kPointerSize),
1445 ecx);
1446
1447 __ ret((argc + 1) * kPointerSize);
1448
1449 __ bind(&with_write_barrier);
1450
1451 __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
1452
1453 if (FLAG_smi_only_arrays && !FLAG_trace_elements_transitions) {
1454 Label fast_object, not_fast_object;
1455 __ CheckFastObjectElements(ebx, ¬_fast_object, Label::kNear);
1456 __ jmp(&fast_object);
1457 // In case of fast smi-only, convert to fast object, otherwise bail out.
1458 __ bind(¬_fast_object);
1459 __ CheckFastSmiOnlyElements(ebx, &call_builtin);
1460 // edi: elements array
1461 // edx: receiver
1462 // ebx: map
1463 __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS,
1464 FAST_ELEMENTS,
1465 ebx,
1466 edi,
1467 &call_builtin);
1468 ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm());
1469 // Restore edi.
1470 __ mov(edi, FieldOperand(edx, JSArray::kElementsOffset));
1471 __ bind(&fast_object);
1472 } else {
1473 __ CheckFastObjectElements(ebx, &call_builtin);
1474 }
1475
1476 // Save new length.
1477 __ mov(FieldOperand(edx, JSArray::kLengthOffset), eax);
1478
1479 // Store the value.
1480 __ lea(edx, FieldOperand(edi,
1481 eax, times_half_pointer_size,
1482 FixedArray::kHeaderSize - argc * kPointerSize));
1483 __ mov(Operand(edx, 0), ecx);
1484
1485 __ RecordWrite(edi, edx, ecx, kDontSaveFPRegs, EMIT_REMEMBERED_SET,
1486 OMIT_SMI_CHECK);
1487
1488 __ ret((argc + 1) * kPointerSize);
1489
1490 __ bind(&attempt_to_grow_elements);
1491 if (!FLAG_inline_new) {
1492 __ jmp(&call_builtin);
1493 }
1494
1495 __ mov(ebx, Operand(esp, argc * kPointerSize));
1496 // Growing elements that are SMI-only requires special handling in case
1497 // the new element is non-Smi. For now, delegate to the builtin.
1498 Label no_fast_elements_check;
1499 __ JumpIfSmi(ebx, &no_fast_elements_check);
1500 __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
1501 __ CheckFastObjectElements(ecx, &call_builtin, Label::kFar);
1502 __ bind(&no_fast_elements_check);
1503
1504 // We could be lucky and the elements array could be at the top of
1505 // new-space. In this case we can just grow it in place by moving the
1506 // allocation pointer up.
1507
1508 ExternalReference new_space_allocation_top =
1509 ExternalReference::new_space_allocation_top_address(isolate());
1510 ExternalReference new_space_allocation_limit =
1511 ExternalReference::new_space_allocation_limit_address(isolate());
1512
1513 const int kAllocationDelta = 4;
1514 // Load top.
1515 __ mov(ecx, Operand::StaticVariable(new_space_allocation_top));
1516
1517 // Check if it's the end of elements.
1518 __ lea(edx, FieldOperand(edi,
1519 eax, times_half_pointer_size,
1520 FixedArray::kHeaderSize - argc * kPointerSize));
1521 __ cmp(edx, ecx);
1522 __ j(not_equal, &call_builtin);
1523 __ add(ecx, Immediate(kAllocationDelta * kPointerSize));
1524 __ cmp(ecx, Operand::StaticVariable(new_space_allocation_limit));
1525 __ j(above, &call_builtin);
1526
1527 // We fit and could grow elements.
1528 __ mov(Operand::StaticVariable(new_space_allocation_top), ecx);
1529
1530 // Push the argument...
1531 __ mov(Operand(edx, 0), ebx);
1532 // ... and fill the rest with holes.
1533 for (int i = 1; i < kAllocationDelta; i++) {
1534 __ mov(Operand(edx, i * kPointerSize),
1535 Immediate(factory()->the_hole_value()));
1536 }
1537
1538 // We know the elements array is in new space so we don't need the
1539 // remembered set, but we just pushed a value onto it so we may have to
1540 // tell the incremental marker to rescan the object that we just grew. We
1541 // don't need to worry about the holes because they are in old space and
1542 // already marked black.
1543 __ RecordWrite(edi, edx, ebx, kDontSaveFPRegs, OMIT_REMEMBERED_SET);
1544
1545 // Restore receiver to edx as finish sequence assumes it's here.
1546 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
1547
1548 // Increment element's and array's sizes.
1549 __ add(FieldOperand(edi, FixedArray::kLengthOffset),
1550 Immediate(Smi::FromInt(kAllocationDelta)));
1551
1552 // NOTE: This only happen in new-space, where we don't
1553 // care about the black-byte-count on pages. Otherwise we should
1554 // update that too if the object is black.
1555
1556 __ mov(FieldOperand(edx, JSArray::kLengthOffset), eax);
1557
1558 __ ret((argc + 1) * kPointerSize);
1559 }
1560
1561 __ bind(&call_builtin);
1562 __ TailCallExternalReference(
1563 ExternalReference(Builtins::c_ArrayPush, isolate()),
1564 argc + 1,
1565 1);
1566 }
1567
1568 __ bind(&miss);
1569 GenerateMissBranch();
1570
1571 // Return the generated code.
1572 return GetCode(function);
1573 }
1574
1575
CompileArrayPopCall(Handle<Object> object,Handle<JSObject> holder,Handle<JSGlobalPropertyCell> cell,Handle<JSFunction> function,Handle<String> name)1576 Handle<Code> CallStubCompiler::CompileArrayPopCall(
1577 Handle<Object> object,
1578 Handle<JSObject> holder,
1579 Handle<JSGlobalPropertyCell> cell,
1580 Handle<JSFunction> function,
1581 Handle<String> name) {
1582 // ----------- S t a t e -------------
1583 // -- ecx : name
1584 // -- esp[0] : return address
1585 // -- esp[(argc - n) * 4] : arg[n] (zero-based)
1586 // -- ...
1587 // -- esp[(argc + 1) * 4] : receiver
1588 // -----------------------------------
1589
1590 // If object is not an array, bail out to regular call.
1591 if (!object->IsJSArray() || !cell.is_null()) {
1592 return Handle<Code>::null();
1593 }
1594
1595 Label miss, return_undefined, call_builtin;
1596
1597 GenerateNameCheck(name, &miss);
1598
1599 // Get the receiver from the stack.
1600 const int argc = arguments().immediate();
1601 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
1602
1603 // Check that the receiver isn't a smi.
1604 __ JumpIfSmi(edx, &miss);
1605 CheckPrototypes(Handle<JSObject>::cast(object), edx, holder, ebx, eax, edi,
1606 name, &miss);
1607
1608 // Get the elements array of the object.
1609 __ mov(ebx, FieldOperand(edx, JSArray::kElementsOffset));
1610
1611 // Check that the elements are in fast mode and writable.
1612 __ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
1613 Immediate(factory()->fixed_array_map()));
1614 __ j(not_equal, &call_builtin);
1615
1616 // Get the array's length into ecx and calculate new length.
1617 __ mov(ecx, FieldOperand(edx, JSArray::kLengthOffset));
1618 __ sub(ecx, Immediate(Smi::FromInt(1)));
1619 __ j(negative, &return_undefined);
1620
1621 // Get the last element.
1622 STATIC_ASSERT(kSmiTagSize == 1);
1623 STATIC_ASSERT(kSmiTag == 0);
1624 __ mov(eax, FieldOperand(ebx,
1625 ecx, times_half_pointer_size,
1626 FixedArray::kHeaderSize));
1627 __ cmp(eax, Immediate(factory()->the_hole_value()));
1628 __ j(equal, &call_builtin);
1629
1630 // Set the array's length.
1631 __ mov(FieldOperand(edx, JSArray::kLengthOffset), ecx);
1632
1633 // Fill with the hole.
1634 __ mov(FieldOperand(ebx,
1635 ecx, times_half_pointer_size,
1636 FixedArray::kHeaderSize),
1637 Immediate(factory()->the_hole_value()));
1638 __ ret((argc + 1) * kPointerSize);
1639
1640 __ bind(&return_undefined);
1641 __ mov(eax, Immediate(factory()->undefined_value()));
1642 __ ret((argc + 1) * kPointerSize);
1643
1644 __ bind(&call_builtin);
1645 __ TailCallExternalReference(
1646 ExternalReference(Builtins::c_ArrayPop, isolate()),
1647 argc + 1,
1648 1);
1649
1650 __ bind(&miss);
1651 GenerateMissBranch();
1652
1653 // Return the generated code.
1654 return GetCode(function);
1655 }
1656
1657
CompileStringCharCodeAtCall(Handle<Object> object,Handle<JSObject> holder,Handle<JSGlobalPropertyCell> cell,Handle<JSFunction> function,Handle<String> name)1658 Handle<Code> CallStubCompiler::CompileStringCharCodeAtCall(
1659 Handle<Object> object,
1660 Handle<JSObject> holder,
1661 Handle<JSGlobalPropertyCell> cell,
1662 Handle<JSFunction> function,
1663 Handle<String> name) {
1664 // ----------- S t a t e -------------
1665 // -- ecx : function name
1666 // -- esp[0] : return address
1667 // -- esp[(argc - n) * 4] : arg[n] (zero-based)
1668 // -- ...
1669 // -- esp[(argc + 1) * 4] : receiver
1670 // -----------------------------------
1671
1672 // If object is not a string, bail out to regular call.
1673 if (!object->IsString() || !cell.is_null()) {
1674 return Handle<Code>::null();
1675 }
1676
1677 const int argc = arguments().immediate();
1678
1679 Label miss;
1680 Label name_miss;
1681 Label index_out_of_range;
1682 Label* index_out_of_range_label = &index_out_of_range;
1683
1684 if (kind_ == Code::CALL_IC &&
1685 (CallICBase::StringStubState::decode(extra_state_) ==
1686 DEFAULT_STRING_STUB)) {
1687 index_out_of_range_label = &miss;
1688 }
1689
1690 GenerateNameCheck(name, &name_miss);
1691
1692 // Check that the maps starting from the prototype haven't changed.
1693 GenerateDirectLoadGlobalFunctionPrototype(masm(),
1694 Context::STRING_FUNCTION_INDEX,
1695 eax,
1696 &miss);
1697 ASSERT(!object.is_identical_to(holder));
1698 CheckPrototypes(Handle<JSObject>(JSObject::cast(object->GetPrototype())),
1699 eax, holder, ebx, edx, edi, name, &miss);
1700
1701 Register receiver = ebx;
1702 Register index = edi;
1703 Register result = eax;
1704 __ mov(receiver, Operand(esp, (argc + 1) * kPointerSize));
1705 if (argc > 0) {
1706 __ mov(index, Operand(esp, (argc - 0) * kPointerSize));
1707 } else {
1708 __ Set(index, Immediate(factory()->undefined_value()));
1709 }
1710
1711 StringCharCodeAtGenerator generator(receiver,
1712 index,
1713 result,
1714 &miss, // When not a string.
1715 &miss, // When not a number.
1716 index_out_of_range_label,
1717 STRING_INDEX_IS_NUMBER);
1718 generator.GenerateFast(masm());
1719 __ ret((argc + 1) * kPointerSize);
1720
1721 StubRuntimeCallHelper call_helper;
1722 generator.GenerateSlow(masm(), call_helper);
1723
1724 if (index_out_of_range.is_linked()) {
1725 __ bind(&index_out_of_range);
1726 __ Set(eax, Immediate(factory()->nan_value()));
1727 __ ret((argc + 1) * kPointerSize);
1728 }
1729
1730 __ bind(&miss);
1731 // Restore function name in ecx.
1732 __ Set(ecx, Immediate(name));
1733 __ bind(&name_miss);
1734 GenerateMissBranch();
1735
1736 // Return the generated code.
1737 return GetCode(function);
1738 }
1739
1740
CompileStringCharAtCall(Handle<Object> object,Handle<JSObject> holder,Handle<JSGlobalPropertyCell> cell,Handle<JSFunction> function,Handle<String> name)1741 Handle<Code> CallStubCompiler::CompileStringCharAtCall(
1742 Handle<Object> object,
1743 Handle<JSObject> holder,
1744 Handle<JSGlobalPropertyCell> cell,
1745 Handle<JSFunction> function,
1746 Handle<String> name) {
1747 // ----------- S t a t e -------------
1748 // -- ecx : function name
1749 // -- esp[0] : return address
1750 // -- esp[(argc - n) * 4] : arg[n] (zero-based)
1751 // -- ...
1752 // -- esp[(argc + 1) * 4] : receiver
1753 // -----------------------------------
1754
1755 // If object is not a string, bail out to regular call.
1756 if (!object->IsString() || !cell.is_null()) {
1757 return Handle<Code>::null();
1758 }
1759
1760 const int argc = arguments().immediate();
1761
1762 Label miss;
1763 Label name_miss;
1764 Label index_out_of_range;
1765 Label* index_out_of_range_label = &index_out_of_range;
1766
1767 if (kind_ == Code::CALL_IC &&
1768 (CallICBase::StringStubState::decode(extra_state_) ==
1769 DEFAULT_STRING_STUB)) {
1770 index_out_of_range_label = &miss;
1771 }
1772
1773 GenerateNameCheck(name, &name_miss);
1774
1775 // Check that the maps starting from the prototype haven't changed.
1776 GenerateDirectLoadGlobalFunctionPrototype(masm(),
1777 Context::STRING_FUNCTION_INDEX,
1778 eax,
1779 &miss);
1780 ASSERT(!object.is_identical_to(holder));
1781 CheckPrototypes(Handle<JSObject>(JSObject::cast(object->GetPrototype())),
1782 eax, holder, ebx, edx, edi, name, &miss);
1783
1784 Register receiver = eax;
1785 Register index = edi;
1786 Register scratch = edx;
1787 Register result = eax;
1788 __ mov(receiver, Operand(esp, (argc + 1) * kPointerSize));
1789 if (argc > 0) {
1790 __ mov(index, Operand(esp, (argc - 0) * kPointerSize));
1791 } else {
1792 __ Set(index, Immediate(factory()->undefined_value()));
1793 }
1794
1795 StringCharAtGenerator generator(receiver,
1796 index,
1797 scratch,
1798 result,
1799 &miss, // When not a string.
1800 &miss, // When not a number.
1801 index_out_of_range_label,
1802 STRING_INDEX_IS_NUMBER);
1803 generator.GenerateFast(masm());
1804 __ ret((argc + 1) * kPointerSize);
1805
1806 StubRuntimeCallHelper call_helper;
1807 generator.GenerateSlow(masm(), call_helper);
1808
1809 if (index_out_of_range.is_linked()) {
1810 __ bind(&index_out_of_range);
1811 __ Set(eax, Immediate(factory()->empty_string()));
1812 __ ret((argc + 1) * kPointerSize);
1813 }
1814
1815 __ bind(&miss);
1816 // Restore function name in ecx.
1817 __ Set(ecx, Immediate(name));
1818 __ bind(&name_miss);
1819 GenerateMissBranch();
1820
1821 // Return the generated code.
1822 return GetCode(function);
1823 }
1824
1825
CompileStringFromCharCodeCall(Handle<Object> object,Handle<JSObject> holder,Handle<JSGlobalPropertyCell> cell,Handle<JSFunction> function,Handle<String> name)1826 Handle<Code> CallStubCompiler::CompileStringFromCharCodeCall(
1827 Handle<Object> object,
1828 Handle<JSObject> holder,
1829 Handle<JSGlobalPropertyCell> cell,
1830 Handle<JSFunction> function,
1831 Handle<String> name) {
1832 // ----------- S t a t e -------------
1833 // -- ecx : function name
1834 // -- esp[0] : return address
1835 // -- esp[(argc - n) * 4] : arg[n] (zero-based)
1836 // -- ...
1837 // -- esp[(argc + 1) * 4] : receiver
1838 // -----------------------------------
1839
1840 const int argc = arguments().immediate();
1841
1842 // If the object is not a JSObject or we got an unexpected number of
1843 // arguments, bail out to the regular call.
1844 if (!object->IsJSObject() || argc != 1) {
1845 return Handle<Code>::null();
1846 }
1847
1848 Label miss;
1849 GenerateNameCheck(name, &miss);
1850
1851 if (cell.is_null()) {
1852 __ mov(edx, Operand(esp, 2 * kPointerSize));
1853 STATIC_ASSERT(kSmiTag == 0);
1854 __ JumpIfSmi(edx, &miss);
1855 CheckPrototypes(Handle<JSObject>::cast(object), edx, holder, ebx, eax, edi,
1856 name, &miss);
1857 } else {
1858 ASSERT(cell->value() == *function);
1859 GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name,
1860 &miss);
1861 GenerateLoadFunctionFromCell(cell, function, &miss);
1862 }
1863
1864 // Load the char code argument.
1865 Register code = ebx;
1866 __ mov(code, Operand(esp, 1 * kPointerSize));
1867
1868 // Check the code is a smi.
1869 Label slow;
1870 STATIC_ASSERT(kSmiTag == 0);
1871 __ JumpIfNotSmi(code, &slow);
1872
1873 // Convert the smi code to uint16.
1874 __ and_(code, Immediate(Smi::FromInt(0xffff)));
1875
1876 StringCharFromCodeGenerator generator(code, eax);
1877 generator.GenerateFast(masm());
1878 __ ret(2 * kPointerSize);
1879
1880 StubRuntimeCallHelper call_helper;
1881 generator.GenerateSlow(masm(), call_helper);
1882
1883 // Tail call the full function. We do not have to patch the receiver
1884 // because the function makes no use of it.
1885 __ bind(&slow);
1886 CallKind call_kind = CallICBase::Contextual::decode(extra_state_)
1887 ? CALL_AS_FUNCTION
1888 : CALL_AS_METHOD;
1889 __ InvokeFunction(function, arguments(), JUMP_FUNCTION,
1890 NullCallWrapper(), call_kind);
1891
1892 __ bind(&miss);
1893 // ecx: function name.
1894 GenerateMissBranch();
1895
1896 // Return the generated code.
1897 return cell.is_null() ? GetCode(function) : GetCode(NORMAL, name);
1898 }
1899
1900
CompileMathFloorCall(Handle<Object> object,Handle<JSObject> holder,Handle<JSGlobalPropertyCell> cell,Handle<JSFunction> function,Handle<String> name)1901 Handle<Code> CallStubCompiler::CompileMathFloorCall(
1902 Handle<Object> object,
1903 Handle<JSObject> holder,
1904 Handle<JSGlobalPropertyCell> cell,
1905 Handle<JSFunction> function,
1906 Handle<String> name) {
1907 // ----------- S t a t e -------------
1908 // -- ecx : name
1909 // -- esp[0] : return address
1910 // -- esp[(argc - n) * 4] : arg[n] (zero-based)
1911 // -- ...
1912 // -- esp[(argc + 1) * 4] : receiver
1913 // -----------------------------------
1914
1915 if (!CpuFeatures::IsSupported(SSE2)) {
1916 return Handle<Code>::null();
1917 }
1918
1919 CpuFeatures::Scope use_sse2(SSE2);
1920
1921 const int argc = arguments().immediate();
1922
1923 // If the object is not a JSObject or we got an unexpected number of
1924 // arguments, bail out to the regular call.
1925 if (!object->IsJSObject() || argc != 1) {
1926 return Handle<Code>::null();
1927 }
1928
1929 Label miss;
1930 GenerateNameCheck(name, &miss);
1931
1932 if (cell.is_null()) {
1933 __ mov(edx, Operand(esp, 2 * kPointerSize));
1934
1935 STATIC_ASSERT(kSmiTag == 0);
1936 __ JumpIfSmi(edx, &miss);
1937
1938 CheckPrototypes(Handle<JSObject>::cast(object), edx, holder, ebx, eax, edi,
1939 name, &miss);
1940 } else {
1941 ASSERT(cell->value() == *function);
1942 GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name,
1943 &miss);
1944 GenerateLoadFunctionFromCell(cell, function, &miss);
1945 }
1946
1947 // Load the (only) argument into eax.
1948 __ mov(eax, Operand(esp, 1 * kPointerSize));
1949
1950 // Check if the argument is a smi.
1951 Label smi;
1952 STATIC_ASSERT(kSmiTag == 0);
1953 __ JumpIfSmi(eax, &smi);
1954
1955 // Check if the argument is a heap number and load its value into xmm0.
1956 Label slow;
1957 __ CheckMap(eax, factory()->heap_number_map(), &slow, DONT_DO_SMI_CHECK);
1958 __ movdbl(xmm0, FieldOperand(eax, HeapNumber::kValueOffset));
1959
1960 // Check if the argument is strictly positive. Note this also
1961 // discards NaN.
1962 __ xorpd(xmm1, xmm1);
1963 __ ucomisd(xmm0, xmm1);
1964 __ j(below_equal, &slow);
1965
1966 // Do a truncating conversion.
1967 __ cvttsd2si(eax, Operand(xmm0));
1968
1969 // Check if the result fits into a smi. Note this also checks for
1970 // 0x80000000 which signals a failed conversion.
1971 Label wont_fit_into_smi;
1972 __ test(eax, Immediate(0xc0000000));
1973 __ j(not_zero, &wont_fit_into_smi);
1974
1975 // Smi tag and return.
1976 __ SmiTag(eax);
1977 __ bind(&smi);
1978 __ ret(2 * kPointerSize);
1979
1980 // Check if the argument is < 2^kMantissaBits.
1981 Label already_round;
1982 __ bind(&wont_fit_into_smi);
1983 __ LoadPowerOf2(xmm1, ebx, HeapNumber::kMantissaBits);
1984 __ ucomisd(xmm0, xmm1);
1985 __ j(above_equal, &already_round);
1986
1987 // Save a copy of the argument.
1988 __ movaps(xmm2, xmm0);
1989
1990 // Compute (argument + 2^kMantissaBits) - 2^kMantissaBits.
1991 __ addsd(xmm0, xmm1);
1992 __ subsd(xmm0, xmm1);
1993
1994 // Compare the argument and the tentative result to get the right mask:
1995 // if xmm2 < xmm0:
1996 // xmm2 = 1...1
1997 // else:
1998 // xmm2 = 0...0
1999 __ cmpltsd(xmm2, xmm0);
2000
2001 // Subtract 1 if the argument was less than the tentative result.
2002 __ LoadPowerOf2(xmm1, ebx, 0);
2003 __ andpd(xmm1, xmm2);
2004 __ subsd(xmm0, xmm1);
2005
2006 // Return a new heap number.
2007 __ AllocateHeapNumber(eax, ebx, edx, &slow);
2008 __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
2009 __ ret(2 * kPointerSize);
2010
2011 // Return the argument (when it's an already round heap number).
2012 __ bind(&already_round);
2013 __ mov(eax, Operand(esp, 1 * kPointerSize));
2014 __ ret(2 * kPointerSize);
2015
2016 // Tail call the full function. We do not have to patch the receiver
2017 // because the function makes no use of it.
2018 __ bind(&slow);
2019 __ InvokeFunction(function, arguments(), JUMP_FUNCTION,
2020 NullCallWrapper(), CALL_AS_METHOD);
2021
2022 __ bind(&miss);
2023 // ecx: function name.
2024 GenerateMissBranch();
2025
2026 // Return the generated code.
2027 return cell.is_null() ? GetCode(function) : GetCode(NORMAL, name);
2028 }
2029
2030
CompileMathAbsCall(Handle<Object> object,Handle<JSObject> holder,Handle<JSGlobalPropertyCell> cell,Handle<JSFunction> function,Handle<String> name)2031 Handle<Code> CallStubCompiler::CompileMathAbsCall(
2032 Handle<Object> object,
2033 Handle<JSObject> holder,
2034 Handle<JSGlobalPropertyCell> cell,
2035 Handle<JSFunction> function,
2036 Handle<String> name) {
2037 // ----------- S t a t e -------------
2038 // -- ecx : name
2039 // -- esp[0] : return address
2040 // -- esp[(argc - n) * 4] : arg[n] (zero-based)
2041 // -- ...
2042 // -- esp[(argc + 1) * 4] : receiver
2043 // -----------------------------------
2044
2045 const int argc = arguments().immediate();
2046
2047 // If the object is not a JSObject or we got an unexpected number of
2048 // arguments, bail out to the regular call.
2049 if (!object->IsJSObject() || argc != 1) {
2050 return Handle<Code>::null();
2051 }
2052
2053 Label miss;
2054 GenerateNameCheck(name, &miss);
2055
2056 if (cell.is_null()) {
2057 __ mov(edx, Operand(esp, 2 * kPointerSize));
2058
2059 STATIC_ASSERT(kSmiTag == 0);
2060 __ JumpIfSmi(edx, &miss);
2061
2062 CheckPrototypes(Handle<JSObject>::cast(object), edx, holder, ebx, eax, edi,
2063 name, &miss);
2064 } else {
2065 ASSERT(cell->value() == *function);
2066 GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name,
2067 &miss);
2068 GenerateLoadFunctionFromCell(cell, function, &miss);
2069 }
2070
2071 // Load the (only) argument into eax.
2072 __ mov(eax, Operand(esp, 1 * kPointerSize));
2073
2074 // Check if the argument is a smi.
2075 Label not_smi;
2076 STATIC_ASSERT(kSmiTag == 0);
2077 __ JumpIfNotSmi(eax, ¬_smi);
2078
2079 // Set ebx to 1...1 (== -1) if the argument is negative, or to 0...0
2080 // otherwise.
2081 __ mov(ebx, eax);
2082 __ sar(ebx, kBitsPerInt - 1);
2083
2084 // Do bitwise not or do nothing depending on ebx.
2085 __ xor_(eax, ebx);
2086
2087 // Add 1 or do nothing depending on ebx.
2088 __ sub(eax, ebx);
2089
2090 // If the result is still negative, go to the slow case.
2091 // This only happens for the most negative smi.
2092 Label slow;
2093 __ j(negative, &slow);
2094
2095 // Smi case done.
2096 __ ret(2 * kPointerSize);
2097
2098 // Check if the argument is a heap number and load its exponent and
2099 // sign into ebx.
2100 __ bind(¬_smi);
2101 __ CheckMap(eax, factory()->heap_number_map(), &slow, DONT_DO_SMI_CHECK);
2102 __ mov(ebx, FieldOperand(eax, HeapNumber::kExponentOffset));
2103
2104 // Check the sign of the argument. If the argument is positive,
2105 // just return it.
2106 Label negative_sign;
2107 __ test(ebx, Immediate(HeapNumber::kSignMask));
2108 __ j(not_zero, &negative_sign);
2109 __ ret(2 * kPointerSize);
2110
2111 // If the argument is negative, clear the sign, and return a new
2112 // number.
2113 __ bind(&negative_sign);
2114 __ and_(ebx, ~HeapNumber::kSignMask);
2115 __ mov(ecx, FieldOperand(eax, HeapNumber::kMantissaOffset));
2116 __ AllocateHeapNumber(eax, edi, edx, &slow);
2117 __ mov(FieldOperand(eax, HeapNumber::kExponentOffset), ebx);
2118 __ mov(FieldOperand(eax, HeapNumber::kMantissaOffset), ecx);
2119 __ ret(2 * kPointerSize);
2120
2121 // Tail call the full function. We do not have to patch the receiver
2122 // because the function makes no use of it.
2123 __ bind(&slow);
2124 __ InvokeFunction(function, arguments(), JUMP_FUNCTION,
2125 NullCallWrapper(), CALL_AS_METHOD);
2126
2127 __ bind(&miss);
2128 // ecx: function name.
2129 GenerateMissBranch();
2130
2131 // Return the generated code.
2132 return cell.is_null() ? GetCode(function) : GetCode(NORMAL, name);
2133 }
2134
2135
CompileFastApiCall(const CallOptimization & optimization,Handle<Object> object,Handle<JSObject> holder,Handle<JSGlobalPropertyCell> cell,Handle<JSFunction> function,Handle<String> name)2136 Handle<Code> CallStubCompiler::CompileFastApiCall(
2137 const CallOptimization& optimization,
2138 Handle<Object> object,
2139 Handle<JSObject> holder,
2140 Handle<JSGlobalPropertyCell> cell,
2141 Handle<JSFunction> function,
2142 Handle<String> name) {
2143 ASSERT(optimization.is_simple_api_call());
2144 // Bail out if object is a global object as we don't want to
2145 // repatch it to global receiver.
2146 if (object->IsGlobalObject()) return Handle<Code>::null();
2147 if (!cell.is_null()) return Handle<Code>::null();
2148 if (!object->IsJSObject()) return Handle<Code>::null();
2149 int depth = optimization.GetPrototypeDepthOfExpectedType(
2150 Handle<JSObject>::cast(object), holder);
2151 if (depth == kInvalidProtoDepth) return Handle<Code>::null();
2152
2153 Label miss, miss_before_stack_reserved;
2154
2155 GenerateNameCheck(name, &miss_before_stack_reserved);
2156
2157 // Get the receiver from the stack.
2158 const int argc = arguments().immediate();
2159 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
2160
2161 // Check that the receiver isn't a smi.
2162 __ JumpIfSmi(edx, &miss_before_stack_reserved);
2163
2164 Counters* counters = isolate()->counters();
2165 __ IncrementCounter(counters->call_const(), 1);
2166 __ IncrementCounter(counters->call_const_fast_api(), 1);
2167
2168 // Allocate space for v8::Arguments implicit values. Must be initialized
2169 // before calling any runtime function.
2170 __ sub(esp, Immediate(kFastApiCallArguments * kPointerSize));
2171
2172 // Check that the maps haven't changed and find a Holder as a side effect.
2173 CheckPrototypes(Handle<JSObject>::cast(object), edx, holder, ebx, eax, edi,
2174 name, depth, &miss);
2175
2176 // Move the return address on top of the stack.
2177 __ mov(eax, Operand(esp, 3 * kPointerSize));
2178 __ mov(Operand(esp, 0 * kPointerSize), eax);
2179
2180 // esp[2 * kPointerSize] is uninitialized, esp[3 * kPointerSize] contains
2181 // duplicate of return address and will be overwritten.
2182 GenerateFastApiCall(masm(), optimization, argc);
2183
2184 __ bind(&miss);
2185 __ add(esp, Immediate(kFastApiCallArguments * kPointerSize));
2186
2187 __ bind(&miss_before_stack_reserved);
2188 GenerateMissBranch();
2189
2190 // Return the generated code.
2191 return GetCode(function);
2192 }
2193
2194
CompileCallConstant(Handle<Object> object,Handle<JSObject> holder,Handle<JSFunction> function,Handle<String> name,CheckType check)2195 Handle<Code> CallStubCompiler::CompileCallConstant(Handle<Object> object,
2196 Handle<JSObject> holder,
2197 Handle<JSFunction> function,
2198 Handle<String> name,
2199 CheckType check) {
2200 // ----------- S t a t e -------------
2201 // -- ecx : name
2202 // -- esp[0] : return address
2203 // -- esp[(argc - n) * 4] : arg[n] (zero-based)
2204 // -- ...
2205 // -- esp[(argc + 1) * 4] : receiver
2206 // -----------------------------------
2207
2208 if (HasCustomCallGenerator(function)) {
2209 Handle<Code> code = CompileCustomCall(object, holder,
2210 Handle<JSGlobalPropertyCell>::null(),
2211 function, name);
2212 // A null handle means bail out to the regular compiler code below.
2213 if (!code.is_null()) return code;
2214 }
2215
2216 Label miss;
2217 GenerateNameCheck(name, &miss);
2218
2219 // Get the receiver from the stack.
2220 const int argc = arguments().immediate();
2221 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
2222
2223 // Check that the receiver isn't a smi.
2224 if (check != NUMBER_CHECK) {
2225 __ JumpIfSmi(edx, &miss);
2226 }
2227
2228 // Make sure that it's okay not to patch the on stack receiver
2229 // unless we're doing a receiver map check.
2230 ASSERT(!object->IsGlobalObject() || check == RECEIVER_MAP_CHECK);
2231 switch (check) {
2232 case RECEIVER_MAP_CHECK:
2233 __ IncrementCounter(isolate()->counters()->call_const(), 1);
2234
2235 // Check that the maps haven't changed.
2236 CheckPrototypes(Handle<JSObject>::cast(object), edx, holder, ebx, eax,
2237 edi, name, &miss);
2238
2239 // Patch the receiver on the stack with the global proxy if
2240 // necessary.
2241 if (object->IsGlobalObject()) {
2242 __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset));
2243 __ mov(Operand(esp, (argc + 1) * kPointerSize), edx);
2244 }
2245 break;
2246
2247 case STRING_CHECK:
2248 if (function->IsBuiltin() || !function->shared()->is_classic_mode()) {
2249 // Check that the object is a string or a symbol.
2250 __ CmpObjectType(edx, FIRST_NONSTRING_TYPE, eax);
2251 __ j(above_equal, &miss);
2252 // Check that the maps starting from the prototype haven't changed.
2253 GenerateDirectLoadGlobalFunctionPrototype(
2254 masm(), Context::STRING_FUNCTION_INDEX, eax, &miss);
2255 CheckPrototypes(
2256 Handle<JSObject>(JSObject::cast(object->GetPrototype())),
2257 eax, holder, ebx, edx, edi, name, &miss);
2258 } else {
2259 // Calling non-strict non-builtins with a value as the receiver
2260 // requires boxing.
2261 __ jmp(&miss);
2262 }
2263 break;
2264
2265 case NUMBER_CHECK:
2266 if (function->IsBuiltin() || !function->shared()->is_classic_mode()) {
2267 Label fast;
2268 // Check that the object is a smi or a heap number.
2269 __ JumpIfSmi(edx, &fast);
2270 __ CmpObjectType(edx, HEAP_NUMBER_TYPE, eax);
2271 __ j(not_equal, &miss);
2272 __ bind(&fast);
2273 // Check that the maps starting from the prototype haven't changed.
2274 GenerateDirectLoadGlobalFunctionPrototype(
2275 masm(), Context::NUMBER_FUNCTION_INDEX, eax, &miss);
2276 CheckPrototypes(
2277 Handle<JSObject>(JSObject::cast(object->GetPrototype())),
2278 eax, holder, ebx, edx, edi, name, &miss);
2279 } else {
2280 // Calling non-strict non-builtins with a value as the receiver
2281 // requires boxing.
2282 __ jmp(&miss);
2283 }
2284 break;
2285
2286 case BOOLEAN_CHECK:
2287 if (function->IsBuiltin() || !function->shared()->is_classic_mode()) {
2288 Label fast;
2289 // Check that the object is a boolean.
2290 __ cmp(edx, factory()->true_value());
2291 __ j(equal, &fast);
2292 __ cmp(edx, factory()->false_value());
2293 __ j(not_equal, &miss);
2294 __ bind(&fast);
2295 // Check that the maps starting from the prototype haven't changed.
2296 GenerateDirectLoadGlobalFunctionPrototype(
2297 masm(), Context::BOOLEAN_FUNCTION_INDEX, eax, &miss);
2298 CheckPrototypes(
2299 Handle<JSObject>(JSObject::cast(object->GetPrototype())),
2300 eax, holder, ebx, edx, edi, name, &miss);
2301 } else {
2302 // Calling non-strict non-builtins with a value as the receiver
2303 // requires boxing.
2304 __ jmp(&miss);
2305 }
2306 break;
2307 }
2308
2309 CallKind call_kind = CallICBase::Contextual::decode(extra_state_)
2310 ? CALL_AS_FUNCTION
2311 : CALL_AS_METHOD;
2312 __ InvokeFunction(function, arguments(), JUMP_FUNCTION,
2313 NullCallWrapper(), call_kind);
2314
2315 // Handle call cache miss.
2316 __ bind(&miss);
2317 GenerateMissBranch();
2318
2319 // Return the generated code.
2320 return GetCode(function);
2321 }
2322
2323
CompileCallInterceptor(Handle<JSObject> object,Handle<JSObject> holder,Handle<String> name)2324 Handle<Code> CallStubCompiler::CompileCallInterceptor(Handle<JSObject> object,
2325 Handle<JSObject> holder,
2326 Handle<String> name) {
2327 // ----------- S t a t e -------------
2328 // -- ecx : name
2329 // -- esp[0] : return address
2330 // -- esp[(argc - n) * 4] : arg[n] (zero-based)
2331 // -- ...
2332 // -- esp[(argc + 1) * 4] : receiver
2333 // -----------------------------------
2334 Label miss;
2335
2336 GenerateNameCheck(name, &miss);
2337
2338 // Get the number of arguments.
2339 const int argc = arguments().immediate();
2340
2341 LookupResult lookup(isolate());
2342 LookupPostInterceptor(holder, name, &lookup);
2343
2344 // Get the receiver from the stack.
2345 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
2346
2347 CallInterceptorCompiler compiler(this, arguments(), ecx, extra_state_);
2348 compiler.Compile(masm(), object, holder, name, &lookup, edx, ebx, edi, eax,
2349 &miss);
2350
2351 // Restore receiver.
2352 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
2353
2354 // Check that the function really is a function.
2355 __ JumpIfSmi(eax, &miss);
2356 __ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx);
2357 __ j(not_equal, &miss);
2358
2359 // Patch the receiver on the stack with the global proxy if
2360 // necessary.
2361 if (object->IsGlobalObject()) {
2362 __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset));
2363 __ mov(Operand(esp, (argc + 1) * kPointerSize), edx);
2364 }
2365
2366 // Invoke the function.
2367 __ mov(edi, eax);
2368 CallKind call_kind = CallICBase::Contextual::decode(extra_state_)
2369 ? CALL_AS_FUNCTION
2370 : CALL_AS_METHOD;
2371 __ InvokeFunction(edi, arguments(), JUMP_FUNCTION,
2372 NullCallWrapper(), call_kind);
2373
2374 // Handle load cache miss.
2375 __ bind(&miss);
2376 GenerateMissBranch();
2377
2378 // Return the generated code.
2379 return GetCode(INTERCEPTOR, name);
2380 }
2381
2382
CompileCallGlobal(Handle<JSObject> object,Handle<GlobalObject> holder,Handle<JSGlobalPropertyCell> cell,Handle<JSFunction> function,Handle<String> name)2383 Handle<Code> CallStubCompiler::CompileCallGlobal(
2384 Handle<JSObject> object,
2385 Handle<GlobalObject> holder,
2386 Handle<JSGlobalPropertyCell> cell,
2387 Handle<JSFunction> function,
2388 Handle<String> name) {
2389 // ----------- S t a t e -------------
2390 // -- ecx : name
2391 // -- esp[0] : return address
2392 // -- esp[(argc - n) * 4] : arg[n] (zero-based)
2393 // -- ...
2394 // -- esp[(argc + 1) * 4] : receiver
2395 // -----------------------------------
2396
2397 if (HasCustomCallGenerator(function)) {
2398 Handle<Code> code = CompileCustomCall(object, holder, cell, function, name);
2399 // A null handle means bail out to the regular compiler code below.
2400 if (!code.is_null()) return code;
2401 }
2402
2403 Label miss;
2404 GenerateNameCheck(name, &miss);
2405
2406 // Get the number of arguments.
2407 const int argc = arguments().immediate();
2408 GenerateGlobalReceiverCheck(object, holder, name, &miss);
2409 GenerateLoadFunctionFromCell(cell, function, &miss);
2410
2411 // Patch the receiver on the stack with the global proxy.
2412 if (object->IsGlobalObject()) {
2413 __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset));
2414 __ mov(Operand(esp, (argc + 1) * kPointerSize), edx);
2415 }
2416
2417 // Set up the context (function already in edi).
2418 __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
2419
2420 // Jump to the cached code (tail call).
2421 Counters* counters = isolate()->counters();
2422 __ IncrementCounter(counters->call_global_inline(), 1);
2423 ParameterCount expected(function->shared()->formal_parameter_count());
2424 CallKind call_kind = CallICBase::Contextual::decode(extra_state_)
2425 ? CALL_AS_FUNCTION
2426 : CALL_AS_METHOD;
2427 // We call indirectly through the code field in the function to
2428 // allow recompilation to take effect without changing any of the
2429 // call sites.
2430 __ InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset),
2431 expected, arguments(), JUMP_FUNCTION,
2432 NullCallWrapper(), call_kind);
2433
2434 // Handle call cache miss.
2435 __ bind(&miss);
2436 __ IncrementCounter(counters->call_global_inline_miss(), 1);
2437 GenerateMissBranch();
2438
2439 // Return the generated code.
2440 return GetCode(NORMAL, name);
2441 }
2442
2443
CompileStoreField(Handle<JSObject> object,int index,Handle<Map> transition,Handle<String> name)2444 Handle<Code> StoreStubCompiler::CompileStoreField(Handle<JSObject> object,
2445 int index,
2446 Handle<Map> transition,
2447 Handle<String> name) {
2448 // ----------- S t a t e -------------
2449 // -- eax : value
2450 // -- ecx : name
2451 // -- edx : receiver
2452 // -- esp[0] : return address
2453 // -----------------------------------
2454 Label miss;
2455
2456 // Generate store field code. Trashes the name register.
2457 GenerateStoreField(masm(), object, index, transition, edx, ecx, ebx, &miss);
2458
2459 // Handle store cache miss.
2460 __ bind(&miss);
2461 __ mov(ecx, Immediate(name)); // restore name
2462 Handle<Code> ic = isolate()->builtins()->StoreIC_Miss();
2463 __ jmp(ic, RelocInfo::CODE_TARGET);
2464
2465 // Return the generated code.
2466 return GetCode(transition.is_null() ? FIELD : MAP_TRANSITION, name);
2467 }
2468
2469
CompileStoreCallback(Handle<JSObject> object,Handle<AccessorInfo> callback,Handle<String> name)2470 Handle<Code> StoreStubCompiler::CompileStoreCallback(
2471 Handle<JSObject> object,
2472 Handle<AccessorInfo> callback,
2473 Handle<String> name) {
2474 // ----------- S t a t e -------------
2475 // -- eax : value
2476 // -- ecx : name
2477 // -- edx : receiver
2478 // -- esp[0] : return address
2479 // -----------------------------------
2480 Label miss;
2481
2482 // Check that the map of the object hasn't changed.
2483 __ CheckMap(edx, Handle<Map>(object->map()),
2484 &miss, DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS);
2485
2486 // Perform global security token check if needed.
2487 if (object->IsJSGlobalProxy()) {
2488 __ CheckAccessGlobalProxy(edx, ebx, &miss);
2489 }
2490
2491 // Stub never generated for non-global objects that require access
2492 // checks.
2493 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
2494
2495 __ pop(ebx); // remove the return address
2496 __ push(edx); // receiver
2497 __ push(Immediate(callback)); // callback info
2498 __ push(ecx); // name
2499 __ push(eax); // value
2500 __ push(ebx); // restore return address
2501
2502 // Do tail-call to the runtime system.
2503 ExternalReference store_callback_property =
2504 ExternalReference(IC_Utility(IC::kStoreCallbackProperty), isolate());
2505 __ TailCallExternalReference(store_callback_property, 4, 1);
2506
2507 // Handle store cache miss.
2508 __ bind(&miss);
2509 Handle<Code> ic = isolate()->builtins()->StoreIC_Miss();
2510 __ jmp(ic, RelocInfo::CODE_TARGET);
2511
2512 // Return the generated code.
2513 return GetCode(CALLBACKS, name);
2514 }
2515
2516
CompileStoreInterceptor(Handle<JSObject> receiver,Handle<String> name)2517 Handle<Code> StoreStubCompiler::CompileStoreInterceptor(
2518 Handle<JSObject> receiver,
2519 Handle<String> name) {
2520 // ----------- S t a t e -------------
2521 // -- eax : value
2522 // -- ecx : name
2523 // -- edx : receiver
2524 // -- esp[0] : return address
2525 // -----------------------------------
2526 Label miss;
2527
2528 // Check that the map of the object hasn't changed.
2529 __ CheckMap(edx, Handle<Map>(receiver->map()),
2530 &miss, DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS);
2531
2532 // Perform global security token check if needed.
2533 if (receiver->IsJSGlobalProxy()) {
2534 __ CheckAccessGlobalProxy(edx, ebx, &miss);
2535 }
2536
2537 // Stub never generated for non-global objects that require access
2538 // checks.
2539 ASSERT(receiver->IsJSGlobalProxy() || !receiver->IsAccessCheckNeeded());
2540
2541 __ pop(ebx); // remove the return address
2542 __ push(edx); // receiver
2543 __ push(ecx); // name
2544 __ push(eax); // value
2545 __ push(Immediate(Smi::FromInt(strict_mode_)));
2546 __ push(ebx); // restore return address
2547
2548 // Do tail-call to the runtime system.
2549 ExternalReference store_ic_property =
2550 ExternalReference(IC_Utility(IC::kStoreInterceptorProperty), isolate());
2551 __ TailCallExternalReference(store_ic_property, 4, 1);
2552
2553 // Handle store cache miss.
2554 __ bind(&miss);
2555 Handle<Code> ic = isolate()->builtins()->StoreIC_Miss();
2556 __ jmp(ic, RelocInfo::CODE_TARGET);
2557
2558 // Return the generated code.
2559 return GetCode(INTERCEPTOR, name);
2560 }
2561
2562
CompileStoreGlobal(Handle<GlobalObject> object,Handle<JSGlobalPropertyCell> cell,Handle<String> name)2563 Handle<Code> StoreStubCompiler::CompileStoreGlobal(
2564 Handle<GlobalObject> object,
2565 Handle<JSGlobalPropertyCell> cell,
2566 Handle<String> name) {
2567 // ----------- S t a t e -------------
2568 // -- eax : value
2569 // -- ecx : name
2570 // -- edx : receiver
2571 // -- esp[0] : return address
2572 // -----------------------------------
2573 Label miss;
2574
2575 // Check that the map of the global has not changed.
2576 __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
2577 Immediate(Handle<Map>(object->map())));
2578 __ j(not_equal, &miss);
2579
2580 // Compute the cell operand to use.
2581 __ mov(ebx, Immediate(cell));
2582 Operand cell_operand = FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset);
2583
2584 // Check that the value in the cell is not the hole. If it is, this
2585 // cell could have been deleted and reintroducing the global needs
2586 // to update the property details in the property dictionary of the
2587 // global object. We bail out to the runtime system to do that.
2588 __ cmp(cell_operand, factory()->the_hole_value());
2589 __ j(equal, &miss);
2590
2591 // Store the value in the cell.
2592 __ mov(cell_operand, eax);
2593 // No write barrier here, because cells are always rescanned.
2594
2595 // Return the value (register eax).
2596 Counters* counters = isolate()->counters();
2597 __ IncrementCounter(counters->named_store_global_inline(), 1);
2598 __ ret(0);
2599
2600 // Handle store cache miss.
2601 __ bind(&miss);
2602 __ IncrementCounter(counters->named_store_global_inline_miss(), 1);
2603 Handle<Code> ic = isolate()->builtins()->StoreIC_Miss();
2604 __ jmp(ic, RelocInfo::CODE_TARGET);
2605
2606 // Return the generated code.
2607 return GetCode(NORMAL, name);
2608 }
2609
2610
CompileStoreField(Handle<JSObject> object,int index,Handle<Map> transition,Handle<String> name)2611 Handle<Code> KeyedStoreStubCompiler::CompileStoreField(Handle<JSObject> object,
2612 int index,
2613 Handle<Map> transition,
2614 Handle<String> name) {
2615 // ----------- S t a t e -------------
2616 // -- eax : value
2617 // -- ecx : key
2618 // -- edx : receiver
2619 // -- esp[0] : return address
2620 // -----------------------------------
2621 Label miss;
2622
2623 Counters* counters = isolate()->counters();
2624 __ IncrementCounter(counters->keyed_store_field(), 1);
2625
2626 // Check that the name has not changed.
2627 __ cmp(ecx, Immediate(name));
2628 __ j(not_equal, &miss);
2629
2630 // Generate store field code. Trashes the name register.
2631 GenerateStoreField(masm(), object, index, transition, edx, ecx, ebx, &miss);
2632
2633 // Handle store cache miss.
2634 __ bind(&miss);
2635 __ DecrementCounter(counters->keyed_store_field(), 1);
2636 Handle<Code> ic = isolate()->builtins()->KeyedStoreIC_Miss();
2637 __ jmp(ic, RelocInfo::CODE_TARGET);
2638
2639 // Return the generated code.
2640 return GetCode(transition.is_null() ? FIELD : MAP_TRANSITION, name);
2641 }
2642
2643
CompileStoreElement(Handle<Map> receiver_map)2644 Handle<Code> KeyedStoreStubCompiler::CompileStoreElement(
2645 Handle<Map> receiver_map) {
2646 // ----------- S t a t e -------------
2647 // -- eax : value
2648 // -- ecx : key
2649 // -- edx : receiver
2650 // -- esp[0] : return address
2651 // -----------------------------------
2652 ElementsKind elements_kind = receiver_map->elements_kind();
2653 bool is_jsarray = receiver_map->instance_type() == JS_ARRAY_TYPE;
2654 Handle<Code> stub =
2655 KeyedStoreElementStub(is_jsarray, elements_kind, grow_mode_).GetCode();
2656
2657 __ DispatchMap(edx, receiver_map, stub, DO_SMI_CHECK);
2658
2659 Handle<Code> ic = isolate()->builtins()->KeyedStoreIC_Miss();
2660 __ jmp(ic, RelocInfo::CODE_TARGET);
2661
2662 // Return the generated code.
2663 return GetCode(NORMAL, factory()->empty_string());
2664 }
2665
2666
CompileStorePolymorphic(MapHandleList * receiver_maps,CodeHandleList * handler_stubs,MapHandleList * transitioned_maps)2667 Handle<Code> KeyedStoreStubCompiler::CompileStorePolymorphic(
2668 MapHandleList* receiver_maps,
2669 CodeHandleList* handler_stubs,
2670 MapHandleList* transitioned_maps) {
2671 // ----------- S t a t e -------------
2672 // -- eax : value
2673 // -- ecx : key
2674 // -- edx : receiver
2675 // -- esp[0] : return address
2676 // -----------------------------------
2677 Label miss;
2678 __ JumpIfSmi(edx, &miss, Label::kNear);
2679 __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
2680 // ebx: receiver->map().
2681 for (int i = 0; i < receiver_maps->length(); ++i) {
2682 __ cmp(edi, receiver_maps->at(i));
2683 if (transitioned_maps->at(i).is_null()) {
2684 __ j(equal, handler_stubs->at(i));
2685 } else {
2686 Label next_map;
2687 __ j(not_equal, &next_map, Label::kNear);
2688 __ mov(ebx, Immediate(transitioned_maps->at(i)));
2689 __ jmp(handler_stubs->at(i), RelocInfo::CODE_TARGET);
2690 __ bind(&next_map);
2691 }
2692 }
2693 __ bind(&miss);
2694 Handle<Code> miss_ic = isolate()->builtins()->KeyedStoreIC_Miss();
2695 __ jmp(miss_ic, RelocInfo::CODE_TARGET);
2696
2697 // Return the generated code.
2698 return GetCode(NORMAL, factory()->empty_string(), MEGAMORPHIC);
2699 }
2700
2701
CompileLoadNonexistent(Handle<String> name,Handle<JSObject> object,Handle<JSObject> last)2702 Handle<Code> LoadStubCompiler::CompileLoadNonexistent(Handle<String> name,
2703 Handle<JSObject> object,
2704 Handle<JSObject> last) {
2705 // ----------- S t a t e -------------
2706 // -- eax : receiver
2707 // -- ecx : name
2708 // -- esp[0] : return address
2709 // -----------------------------------
2710 Label miss;
2711
2712 // Check that the receiver isn't a smi.
2713 __ JumpIfSmi(eax, &miss);
2714
2715 ASSERT(last->IsGlobalObject() || last->HasFastProperties());
2716
2717 // Check the maps of the full prototype chain. Also check that
2718 // global property cells up to (but not including) the last object
2719 // in the prototype chain are empty.
2720 CheckPrototypes(object, eax, last, ebx, edx, edi, name, &miss);
2721
2722 // If the last object in the prototype chain is a global object,
2723 // check that the global property cell is empty.
2724 if (last->IsGlobalObject()) {
2725 GenerateCheckPropertyCell(
2726 masm(), Handle<GlobalObject>::cast(last), name, edx, &miss);
2727 }
2728
2729 // Return undefined if maps of the full prototype chain are still the
2730 // same and no global property with this name contains a value.
2731 __ mov(eax, isolate()->factory()->undefined_value());
2732 __ ret(0);
2733
2734 __ bind(&miss);
2735 GenerateLoadMiss(masm(), Code::LOAD_IC);
2736
2737 // Return the generated code.
2738 return GetCode(NONEXISTENT, factory()->empty_string());
2739 }
2740
2741
CompileLoadField(Handle<JSObject> object,Handle<JSObject> holder,int index,Handle<String> name)2742 Handle<Code> LoadStubCompiler::CompileLoadField(Handle<JSObject> object,
2743 Handle<JSObject> holder,
2744 int index,
2745 Handle<String> name) {
2746 // ----------- S t a t e -------------
2747 // -- eax : receiver
2748 // -- ecx : name
2749 // -- esp[0] : return address
2750 // -----------------------------------
2751 Label miss;
2752
2753 GenerateLoadField(object, holder, eax, ebx, edx, edi, index, name, &miss);
2754 __ bind(&miss);
2755 GenerateLoadMiss(masm(), Code::LOAD_IC);
2756
2757 // Return the generated code.
2758 return GetCode(FIELD, name);
2759 }
2760
2761
CompileLoadCallback(Handle<String> name,Handle<JSObject> object,Handle<JSObject> holder,Handle<AccessorInfo> callback)2762 Handle<Code> LoadStubCompiler::CompileLoadCallback(
2763 Handle<String> name,
2764 Handle<JSObject> object,
2765 Handle<JSObject> holder,
2766 Handle<AccessorInfo> callback) {
2767 // ----------- S t a t e -------------
2768 // -- eax : receiver
2769 // -- ecx : name
2770 // -- esp[0] : return address
2771 // -----------------------------------
2772 Label miss;
2773
2774 GenerateLoadCallback(object, holder, eax, ecx, ebx, edx, edi, callback,
2775 name, &miss);
2776 __ bind(&miss);
2777 GenerateLoadMiss(masm(), Code::LOAD_IC);
2778
2779 // Return the generated code.
2780 return GetCode(CALLBACKS, name);
2781 }
2782
2783
CompileLoadConstant(Handle<JSObject> object,Handle<JSObject> holder,Handle<JSFunction> value,Handle<String> name)2784 Handle<Code> LoadStubCompiler::CompileLoadConstant(Handle<JSObject> object,
2785 Handle<JSObject> holder,
2786 Handle<JSFunction> value,
2787 Handle<String> name) {
2788 // ----------- S t a t e -------------
2789 // -- eax : receiver
2790 // -- ecx : name
2791 // -- esp[0] : return address
2792 // -----------------------------------
2793 Label miss;
2794
2795 GenerateLoadConstant(object, holder, eax, ebx, edx, edi, value, name, &miss);
2796 __ bind(&miss);
2797 GenerateLoadMiss(masm(), Code::LOAD_IC);
2798
2799 // Return the generated code.
2800 return GetCode(CONSTANT_FUNCTION, name);
2801 }
2802
2803
CompileLoadInterceptor(Handle<JSObject> receiver,Handle<JSObject> holder,Handle<String> name)2804 Handle<Code> LoadStubCompiler::CompileLoadInterceptor(Handle<JSObject> receiver,
2805 Handle<JSObject> holder,
2806 Handle<String> name) {
2807 // ----------- S t a t e -------------
2808 // -- eax : receiver
2809 // -- ecx : name
2810 // -- esp[0] : return address
2811 // -----------------------------------
2812 Label miss;
2813
2814 LookupResult lookup(isolate());
2815 LookupPostInterceptor(holder, name, &lookup);
2816
2817 // TODO(368): Compile in the whole chain: all the interceptors in
2818 // prototypes and ultimate answer.
2819 GenerateLoadInterceptor(receiver, holder, &lookup, eax, ecx, edx, ebx, edi,
2820 name, &miss);
2821
2822 __ bind(&miss);
2823 GenerateLoadMiss(masm(), Code::LOAD_IC);
2824
2825 // Return the generated code.
2826 return GetCode(INTERCEPTOR, name);
2827 }
2828
2829
CompileLoadGlobal(Handle<JSObject> object,Handle<GlobalObject> holder,Handle<JSGlobalPropertyCell> cell,Handle<String> name,bool is_dont_delete)2830 Handle<Code> LoadStubCompiler::CompileLoadGlobal(
2831 Handle<JSObject> object,
2832 Handle<GlobalObject> holder,
2833 Handle<JSGlobalPropertyCell> cell,
2834 Handle<String> name,
2835 bool is_dont_delete) {
2836 // ----------- S t a t e -------------
2837 // -- eax : receiver
2838 // -- ecx : name
2839 // -- esp[0] : return address
2840 // -----------------------------------
2841 Label miss;
2842
2843 // Check that the maps haven't changed.
2844 __ JumpIfSmi(eax, &miss);
2845 CheckPrototypes(object, eax, holder, ebx, edx, edi, name, &miss);
2846
2847 // Get the value from the cell.
2848 if (Serializer::enabled()) {
2849 __ mov(ebx, Immediate(cell));
2850 __ mov(ebx, FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset));
2851 } else {
2852 __ mov(ebx, Operand::Cell(cell));
2853 }
2854
2855 // Check for deleted property if property can actually be deleted.
2856 if (!is_dont_delete) {
2857 __ cmp(ebx, factory()->the_hole_value());
2858 __ j(equal, &miss);
2859 } else if (FLAG_debug_code) {
2860 __ cmp(ebx, factory()->the_hole_value());
2861 __ Check(not_equal, "DontDelete cells can't contain the hole");
2862 }
2863
2864 Counters* counters = isolate()->counters();
2865 __ IncrementCounter(counters->named_load_global_stub(), 1);
2866 __ mov(eax, ebx);
2867 __ ret(0);
2868
2869 __ bind(&miss);
2870 __ IncrementCounter(counters->named_load_global_stub_miss(), 1);
2871 GenerateLoadMiss(masm(), Code::LOAD_IC);
2872
2873 // Return the generated code.
2874 return GetCode(NORMAL, name);
2875 }
2876
2877
CompileLoadField(Handle<String> name,Handle<JSObject> receiver,Handle<JSObject> holder,int index)2878 Handle<Code> KeyedLoadStubCompiler::CompileLoadField(Handle<String> name,
2879 Handle<JSObject> receiver,
2880 Handle<JSObject> holder,
2881 int index) {
2882 // ----------- S t a t e -------------
2883 // -- eax : key
2884 // -- edx : receiver
2885 // -- esp[0] : return address
2886 // -----------------------------------
2887 Label miss;
2888
2889 Counters* counters = isolate()->counters();
2890 __ IncrementCounter(counters->keyed_load_field(), 1);
2891
2892 // Check that the name has not changed.
2893 __ cmp(eax, Immediate(name));
2894 __ j(not_equal, &miss);
2895
2896 GenerateLoadField(receiver, holder, edx, ebx, ecx, edi, index, name, &miss);
2897
2898 __ bind(&miss);
2899 __ DecrementCounter(counters->keyed_load_field(), 1);
2900 GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
2901
2902 // Return the generated code.
2903 return GetCode(FIELD, name);
2904 }
2905
2906
CompileLoadCallback(Handle<String> name,Handle<JSObject> receiver,Handle<JSObject> holder,Handle<AccessorInfo> callback)2907 Handle<Code> KeyedLoadStubCompiler::CompileLoadCallback(
2908 Handle<String> name,
2909 Handle<JSObject> receiver,
2910 Handle<JSObject> holder,
2911 Handle<AccessorInfo> callback) {
2912 // ----------- S t a t e -------------
2913 // -- eax : key
2914 // -- edx : receiver
2915 // -- esp[0] : return address
2916 // -----------------------------------
2917 Label miss;
2918
2919 Counters* counters = isolate()->counters();
2920 __ IncrementCounter(counters->keyed_load_callback(), 1);
2921
2922 // Check that the name has not changed.
2923 __ cmp(eax, Immediate(name));
2924 __ j(not_equal, &miss);
2925
2926 GenerateLoadCallback(receiver, holder, edx, eax, ebx, ecx, edi, callback,
2927 name, &miss);
2928
2929 __ bind(&miss);
2930 __ DecrementCounter(counters->keyed_load_callback(), 1);
2931 GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
2932
2933 // Return the generated code.
2934 return GetCode(CALLBACKS, name);
2935 }
2936
2937
CompileLoadConstant(Handle<String> name,Handle<JSObject> receiver,Handle<JSObject> holder,Handle<JSFunction> value)2938 Handle<Code> KeyedLoadStubCompiler::CompileLoadConstant(
2939 Handle<String> name,
2940 Handle<JSObject> receiver,
2941 Handle<JSObject> holder,
2942 Handle<JSFunction> value) {
2943 // ----------- S t a t e -------------
2944 // -- eax : key
2945 // -- edx : receiver
2946 // -- esp[0] : return address
2947 // -----------------------------------
2948 Label miss;
2949
2950 Counters* counters = isolate()->counters();
2951 __ IncrementCounter(counters->keyed_load_constant_function(), 1);
2952
2953 // Check that the name has not changed.
2954 __ cmp(eax, Immediate(name));
2955 __ j(not_equal, &miss);
2956
2957 GenerateLoadConstant(
2958 receiver, holder, edx, ebx, ecx, edi, value, name, &miss);
2959 __ bind(&miss);
2960 __ DecrementCounter(counters->keyed_load_constant_function(), 1);
2961 GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
2962
2963 // Return the generated code.
2964 return GetCode(CONSTANT_FUNCTION, name);
2965 }
2966
2967
CompileLoadInterceptor(Handle<JSObject> receiver,Handle<JSObject> holder,Handle<String> name)2968 Handle<Code> KeyedLoadStubCompiler::CompileLoadInterceptor(
2969 Handle<JSObject> receiver,
2970 Handle<JSObject> holder,
2971 Handle<String> name) {
2972 // ----------- S t a t e -------------
2973 // -- eax : key
2974 // -- edx : receiver
2975 // -- esp[0] : return address
2976 // -----------------------------------
2977 Label miss;
2978
2979 Counters* counters = isolate()->counters();
2980 __ IncrementCounter(counters->keyed_load_interceptor(), 1);
2981
2982 // Check that the name has not changed.
2983 __ cmp(eax, Immediate(name));
2984 __ j(not_equal, &miss);
2985
2986 LookupResult lookup(isolate());
2987 LookupPostInterceptor(holder, name, &lookup);
2988 GenerateLoadInterceptor(receiver, holder, &lookup, edx, eax, ecx, ebx, edi,
2989 name, &miss);
2990 __ bind(&miss);
2991 __ DecrementCounter(counters->keyed_load_interceptor(), 1);
2992 GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
2993
2994 // Return the generated code.
2995 return GetCode(INTERCEPTOR, name);
2996 }
2997
2998
CompileLoadArrayLength(Handle<String> name)2999 Handle<Code> KeyedLoadStubCompiler::CompileLoadArrayLength(
3000 Handle<String> name) {
3001 // ----------- S t a t e -------------
3002 // -- eax : key
3003 // -- edx : receiver
3004 // -- esp[0] : return address
3005 // -----------------------------------
3006 Label miss;
3007
3008 Counters* counters = isolate()->counters();
3009 __ IncrementCounter(counters->keyed_load_array_length(), 1);
3010
3011 // Check that the name has not changed.
3012 __ cmp(eax, Immediate(name));
3013 __ j(not_equal, &miss);
3014
3015 GenerateLoadArrayLength(masm(), edx, ecx, &miss);
3016 __ bind(&miss);
3017 __ DecrementCounter(counters->keyed_load_array_length(), 1);
3018 GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
3019
3020 // Return the generated code.
3021 return GetCode(CALLBACKS, name);
3022 }
3023
3024
CompileLoadStringLength(Handle<String> name)3025 Handle<Code> KeyedLoadStubCompiler::CompileLoadStringLength(
3026 Handle<String> name) {
3027 // ----------- S t a t e -------------
3028 // -- eax : key
3029 // -- edx : receiver
3030 // -- esp[0] : return address
3031 // -----------------------------------
3032 Label miss;
3033
3034 Counters* counters = isolate()->counters();
3035 __ IncrementCounter(counters->keyed_load_string_length(), 1);
3036
3037 // Check that the name has not changed.
3038 __ cmp(eax, Immediate(name));
3039 __ j(not_equal, &miss);
3040
3041 GenerateLoadStringLength(masm(), edx, ecx, ebx, &miss, true);
3042 __ bind(&miss);
3043 __ DecrementCounter(counters->keyed_load_string_length(), 1);
3044 GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
3045
3046 // Return the generated code.
3047 return GetCode(CALLBACKS, name);
3048 }
3049
3050
CompileLoadFunctionPrototype(Handle<String> name)3051 Handle<Code> KeyedLoadStubCompiler::CompileLoadFunctionPrototype(
3052 Handle<String> name) {
3053 // ----------- S t a t e -------------
3054 // -- eax : key
3055 // -- edx : receiver
3056 // -- esp[0] : return address
3057 // -----------------------------------
3058 Label miss;
3059
3060 Counters* counters = isolate()->counters();
3061 __ IncrementCounter(counters->keyed_load_function_prototype(), 1);
3062
3063 // Check that the name has not changed.
3064 __ cmp(eax, Immediate(name));
3065 __ j(not_equal, &miss);
3066
3067 GenerateLoadFunctionPrototype(masm(), edx, ecx, ebx, &miss);
3068 __ bind(&miss);
3069 __ DecrementCounter(counters->keyed_load_function_prototype(), 1);
3070 GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
3071
3072 // Return the generated code.
3073 return GetCode(CALLBACKS, name);
3074 }
3075
3076
CompileLoadElement(Handle<Map> receiver_map)3077 Handle<Code> KeyedLoadStubCompiler::CompileLoadElement(
3078 Handle<Map> receiver_map) {
3079 // ----------- S t a t e -------------
3080 // -- eax : key
3081 // -- edx : receiver
3082 // -- esp[0] : return address
3083 // -----------------------------------
3084
3085 ElementsKind elements_kind = receiver_map->elements_kind();
3086 Handle<Code> stub = KeyedLoadElementStub(elements_kind).GetCode();
3087
3088 __ DispatchMap(edx, receiver_map, stub, DO_SMI_CHECK);
3089
3090 GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
3091
3092 // Return the generated code.
3093 return GetCode(NORMAL, factory()->empty_string());
3094 }
3095
3096
CompileLoadPolymorphic(MapHandleList * receiver_maps,CodeHandleList * handler_ics)3097 Handle<Code> KeyedLoadStubCompiler::CompileLoadPolymorphic(
3098 MapHandleList* receiver_maps,
3099 CodeHandleList* handler_ics) {
3100 // ----------- S t a t e -------------
3101 // -- eax : key
3102 // -- edx : receiver
3103 // -- esp[0] : return address
3104 // -----------------------------------
3105 Label miss;
3106 __ JumpIfSmi(edx, &miss);
3107
3108 Register map_reg = ebx;
3109 __ mov(map_reg, FieldOperand(edx, HeapObject::kMapOffset));
3110 int receiver_count = receiver_maps->length();
3111 for (int current = 0; current < receiver_count; ++current) {
3112 __ cmp(map_reg, receiver_maps->at(current));
3113 __ j(equal, handler_ics->at(current));
3114 }
3115
3116 __ bind(&miss);
3117 GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
3118
3119 // Return the generated code.
3120 return GetCode(NORMAL, factory()->empty_string(), MEGAMORPHIC);
3121 }
3122
3123
3124 // Specialized stub for constructing objects from functions which only have only
3125 // simple assignments of the form this.x = ...; in their body.
CompileConstructStub(Handle<JSFunction> function)3126 Handle<Code> ConstructStubCompiler::CompileConstructStub(
3127 Handle<JSFunction> function) {
3128 // ----------- S t a t e -------------
3129 // -- eax : argc
3130 // -- edi : constructor
3131 // -- esp[0] : return address
3132 // -- esp[4] : last argument
3133 // -----------------------------------
3134 Label generic_stub_call;
3135 #ifdef ENABLE_DEBUGGER_SUPPORT
3136 // Check to see whether there are any break points in the function code. If
3137 // there are jump to the generic constructor stub which calls the actual
3138 // code for the function thereby hitting the break points.
3139 __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
3140 __ mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kDebugInfoOffset));
3141 __ cmp(ebx, factory()->undefined_value());
3142 __ j(not_equal, &generic_stub_call);
3143 #endif
3144
3145 // Load the initial map and verify that it is in fact a map.
3146 __ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
3147 // Will both indicate a NULL and a Smi.
3148 __ JumpIfSmi(ebx, &generic_stub_call);
3149 __ CmpObjectType(ebx, MAP_TYPE, ecx);
3150 __ j(not_equal, &generic_stub_call);
3151
3152 #ifdef DEBUG
3153 // Cannot construct functions this way.
3154 // edi: constructor
3155 // ebx: initial map
3156 __ CmpInstanceType(ebx, JS_FUNCTION_TYPE);
3157 __ Assert(not_equal, "Function constructed by construct stub.");
3158 #endif
3159
3160 // Now allocate the JSObject on the heap by moving the new space allocation
3161 // top forward.
3162 // edi: constructor
3163 // ebx: initial map
3164 __ movzx_b(ecx, FieldOperand(ebx, Map::kInstanceSizeOffset));
3165 __ shl(ecx, kPointerSizeLog2);
3166 __ AllocateInNewSpace(ecx, edx, ecx, no_reg,
3167 &generic_stub_call, NO_ALLOCATION_FLAGS);
3168
3169 // Allocated the JSObject, now initialize the fields and add the heap tag.
3170 // ebx: initial map
3171 // edx: JSObject (untagged)
3172 __ mov(Operand(edx, JSObject::kMapOffset), ebx);
3173 __ mov(ebx, factory()->empty_fixed_array());
3174 __ mov(Operand(edx, JSObject::kPropertiesOffset), ebx);
3175 __ mov(Operand(edx, JSObject::kElementsOffset), ebx);
3176
3177 // Push the allocated object to the stack. This is the object that will be
3178 // returned (after it is tagged).
3179 __ push(edx);
3180
3181 // eax: argc
3182 // edx: JSObject (untagged)
3183 // Load the address of the first in-object property into edx.
3184 __ lea(edx, Operand(edx, JSObject::kHeaderSize));
3185 // Calculate the location of the first argument. The stack contains the
3186 // allocated object and the return address on top of the argc arguments.
3187 __ lea(ecx, Operand(esp, eax, times_4, 1 * kPointerSize));
3188
3189 // Use edi for holding undefined which is used in several places below.
3190 __ mov(edi, factory()->undefined_value());
3191
3192 // eax: argc
3193 // ecx: first argument
3194 // edx: first in-object property of the JSObject
3195 // edi: undefined
3196 // Fill the initialized properties with a constant value or a passed argument
3197 // depending on the this.x = ...; assignment in the function.
3198 Handle<SharedFunctionInfo> shared(function->shared());
3199 for (int i = 0; i < shared->this_property_assignments_count(); i++) {
3200 if (shared->IsThisPropertyAssignmentArgument(i)) {
3201 // Check if the argument assigned to the property is actually passed.
3202 // If argument is not passed the property is set to undefined,
3203 // otherwise find it on the stack.
3204 int arg_number = shared->GetThisPropertyAssignmentArgument(i);
3205 __ mov(ebx, edi);
3206 __ cmp(eax, arg_number);
3207 if (CpuFeatures::IsSupported(CMOV)) {
3208 CpuFeatures::Scope use_cmov(CMOV);
3209 __ cmov(above, ebx, Operand(ecx, arg_number * -kPointerSize));
3210 } else {
3211 Label not_passed;
3212 __ j(below_equal, ¬_passed);
3213 __ mov(ebx, Operand(ecx, arg_number * -kPointerSize));
3214 __ bind(¬_passed);
3215 }
3216 // Store value in the property.
3217 __ mov(Operand(edx, i * kPointerSize), ebx);
3218 } else {
3219 // Set the property to the constant value.
3220 Handle<Object> constant(shared->GetThisPropertyAssignmentConstant(i));
3221 __ mov(Operand(edx, i * kPointerSize), Immediate(constant));
3222 }
3223 }
3224
3225 // Fill the unused in-object property fields with undefined.
3226 ASSERT(function->has_initial_map());
3227 for (int i = shared->this_property_assignments_count();
3228 i < function->initial_map()->inobject_properties();
3229 i++) {
3230 __ mov(Operand(edx, i * kPointerSize), edi);
3231 }
3232
3233 // Move argc to ebx and retrieve and tag the JSObject to return.
3234 __ mov(ebx, eax);
3235 __ pop(eax);
3236 __ or_(eax, Immediate(kHeapObjectTag));
3237
3238 // Remove caller arguments and receiver from the stack and return.
3239 __ pop(ecx);
3240 __ lea(esp, Operand(esp, ebx, times_pointer_size, 1 * kPointerSize));
3241 __ push(ecx);
3242 Counters* counters = isolate()->counters();
3243 __ IncrementCounter(counters->constructed_objects(), 1);
3244 __ IncrementCounter(counters->constructed_objects_stub(), 1);
3245 __ ret(0);
3246
3247 // Jump to the generic stub in case the specialized code cannot handle the
3248 // construction.
3249 __ bind(&generic_stub_call);
3250 Handle<Code> code = isolate()->builtins()->JSConstructStubGeneric();
3251 __ jmp(code, RelocInfo::CODE_TARGET);
3252
3253 // Return the generated code.
3254 return GetCode();
3255 }
3256
3257
3258 #undef __
3259 #define __ ACCESS_MASM(masm)
3260
3261
GenerateLoadDictionaryElement(MacroAssembler * masm)3262 void KeyedLoadStubCompiler::GenerateLoadDictionaryElement(
3263 MacroAssembler* masm) {
3264 // ----------- S t a t e -------------
3265 // -- eax : key
3266 // -- edx : receiver
3267 // -- esp[0] : return address
3268 // -----------------------------------
3269 Label slow, miss_force_generic;
3270
3271 // This stub is meant to be tail-jumped to, the receiver must already
3272 // have been verified by the caller to not be a smi.
3273 __ JumpIfNotSmi(eax, &miss_force_generic);
3274 __ mov(ebx, eax);
3275 __ SmiUntag(ebx);
3276 __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset));
3277
3278 // Push receiver on the stack to free up a register for the dictionary
3279 // probing.
3280 __ push(edx);
3281 __ LoadFromNumberDictionary(&slow,
3282 ecx,
3283 eax,
3284 ebx,
3285 edx,
3286 edi,
3287 eax);
3288 // Pop receiver before returning.
3289 __ pop(edx);
3290 __ ret(0);
3291
3292 __ bind(&slow);
3293 __ pop(edx);
3294
3295 // ----------- S t a t e -------------
3296 // -- eax : value
3297 // -- ecx : key
3298 // -- edx : receiver
3299 // -- esp[0] : return address
3300 // -----------------------------------
3301
3302 Handle<Code> slow_ic =
3303 masm->isolate()->builtins()->KeyedLoadIC_Slow();
3304 __ jmp(slow_ic, RelocInfo::CODE_TARGET);
3305
3306 __ bind(&miss_force_generic);
3307 // ----------- S t a t e -------------
3308 // -- eax : value
3309 // -- ecx : key
3310 // -- edx : receiver
3311 // -- esp[0] : return address
3312 // -----------------------------------
3313
3314 Handle<Code> miss_force_generic_ic =
3315 masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric();
3316 __ jmp(miss_force_generic_ic, RelocInfo::CODE_TARGET);
3317 }
3318
3319
GenerateLoadExternalArray(MacroAssembler * masm,ElementsKind elements_kind)3320 void KeyedLoadStubCompiler::GenerateLoadExternalArray(
3321 MacroAssembler* masm,
3322 ElementsKind elements_kind) {
3323 // ----------- S t a t e -------------
3324 // -- eax : key
3325 // -- edx : receiver
3326 // -- esp[0] : return address
3327 // -----------------------------------
3328 Label miss_force_generic, failed_allocation, slow;
3329
3330 // This stub is meant to be tail-jumped to, the receiver must already
3331 // have been verified by the caller to not be a smi.
3332
3333 // Check that the key is a smi.
3334 __ JumpIfNotSmi(eax, &miss_force_generic);
3335
3336 // Check that the index is in range.
3337 __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset));
3338 __ cmp(eax, FieldOperand(ebx, ExternalArray::kLengthOffset));
3339 // Unsigned comparison catches both negative and too-large values.
3340 __ j(above_equal, &miss_force_generic);
3341 __ mov(ebx, FieldOperand(ebx, ExternalArray::kExternalPointerOffset));
3342 // ebx: base pointer of external storage
3343 switch (elements_kind) {
3344 case EXTERNAL_BYTE_ELEMENTS:
3345 __ SmiUntag(eax); // Untag the index.
3346 __ movsx_b(eax, Operand(ebx, eax, times_1, 0));
3347 break;
3348 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
3349 case EXTERNAL_PIXEL_ELEMENTS:
3350 __ SmiUntag(eax); // Untag the index.
3351 __ movzx_b(eax, Operand(ebx, eax, times_1, 0));
3352 break;
3353 case EXTERNAL_SHORT_ELEMENTS:
3354 __ movsx_w(eax, Operand(ebx, eax, times_1, 0));
3355 break;
3356 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
3357 __ movzx_w(eax, Operand(ebx, eax, times_1, 0));
3358 break;
3359 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
3360 case EXTERNAL_INT_ELEMENTS:
3361 __ mov(ecx, Operand(ebx, eax, times_2, 0));
3362 break;
3363 case EXTERNAL_FLOAT_ELEMENTS:
3364 __ fld_s(Operand(ebx, eax, times_2, 0));
3365 break;
3366 case EXTERNAL_DOUBLE_ELEMENTS:
3367 __ fld_d(Operand(ebx, eax, times_4, 0));
3368 break;
3369 default:
3370 UNREACHABLE();
3371 break;
3372 }
3373
3374 // For integer array types:
3375 // ecx: value
3376 // For floating-point array type:
3377 // FP(0): value
3378
3379 if (elements_kind == EXTERNAL_INT_ELEMENTS ||
3380 elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS) {
3381 // For the Int and UnsignedInt array types, we need to see whether
3382 // the value can be represented in a Smi. If not, we need to convert
3383 // it to a HeapNumber.
3384 Label box_int;
3385 if (elements_kind == EXTERNAL_INT_ELEMENTS) {
3386 __ cmp(ecx, 0xC0000000);
3387 __ j(sign, &box_int);
3388 } else {
3389 ASSERT_EQ(EXTERNAL_UNSIGNED_INT_ELEMENTS, elements_kind);
3390 // The test is different for unsigned int values. Since we need
3391 // the value to be in the range of a positive smi, we can't
3392 // handle either of the top two bits being set in the value.
3393 __ test(ecx, Immediate(0xC0000000));
3394 __ j(not_zero, &box_int);
3395 }
3396
3397 __ mov(eax, ecx);
3398 __ SmiTag(eax);
3399 __ ret(0);
3400
3401 __ bind(&box_int);
3402
3403 // Allocate a HeapNumber for the int and perform int-to-double
3404 // conversion.
3405 if (elements_kind == EXTERNAL_INT_ELEMENTS) {
3406 __ push(ecx);
3407 __ fild_s(Operand(esp, 0));
3408 __ pop(ecx);
3409 } else {
3410 ASSERT_EQ(EXTERNAL_UNSIGNED_INT_ELEMENTS, elements_kind);
3411 // Need to zero-extend the value.
3412 // There's no fild variant for unsigned values, so zero-extend
3413 // to a 64-bit int manually.
3414 __ push(Immediate(0));
3415 __ push(ecx);
3416 __ fild_d(Operand(esp, 0));
3417 __ pop(ecx);
3418 __ pop(ecx);
3419 }
3420 // FP(0): value
3421 __ AllocateHeapNumber(ecx, ebx, edi, &failed_allocation);
3422 // Set the value.
3423 __ mov(eax, ecx);
3424 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
3425 __ ret(0);
3426 } else if (elements_kind == EXTERNAL_FLOAT_ELEMENTS ||
3427 elements_kind == EXTERNAL_DOUBLE_ELEMENTS) {
3428 // For the floating-point array type, we need to always allocate a
3429 // HeapNumber.
3430 __ AllocateHeapNumber(ecx, ebx, edi, &failed_allocation);
3431 // Set the value.
3432 __ mov(eax, ecx);
3433 __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
3434 __ ret(0);
3435 } else {
3436 __ SmiTag(eax);
3437 __ ret(0);
3438 }
3439
3440 // If we fail allocation of the HeapNumber, we still have a value on
3441 // top of the FPU stack. Remove it.
3442 __ bind(&failed_allocation);
3443 __ fstp(0);
3444 // Fall through to slow case.
3445
3446 // Slow case: Jump to runtime.
3447 __ bind(&slow);
3448 Counters* counters = masm->isolate()->counters();
3449 __ IncrementCounter(counters->keyed_load_external_array_slow(), 1);
3450
3451 // ----------- S t a t e -------------
3452 // -- eax : key
3453 // -- edx : receiver
3454 // -- esp[0] : return address
3455 // -----------------------------------
3456
3457 Handle<Code> ic = masm->isolate()->builtins()->KeyedLoadIC_Slow();
3458 __ jmp(ic, RelocInfo::CODE_TARGET);
3459
3460 // ----------- S t a t e -------------
3461 // -- eax : key
3462 // -- edx : receiver
3463 // -- esp[0] : return address
3464 // -----------------------------------
3465
3466 // Miss case: Jump to runtime.
3467 __ bind(&miss_force_generic);
3468 Handle<Code> miss_ic =
3469 masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric();
3470 __ jmp(miss_ic, RelocInfo::CODE_TARGET);
3471 }
3472
3473
GenerateStoreExternalArray(MacroAssembler * masm,ElementsKind elements_kind)3474 void KeyedStoreStubCompiler::GenerateStoreExternalArray(
3475 MacroAssembler* masm,
3476 ElementsKind elements_kind) {
3477 // ----------- S t a t e -------------
3478 // -- eax : key
3479 // -- edx : receiver
3480 // -- esp[0] : return address
3481 // -----------------------------------
3482 Label miss_force_generic, slow, check_heap_number;
3483
3484 // This stub is meant to be tail-jumped to, the receiver must already
3485 // have been verified by the caller to not be a smi.
3486
3487 // Check that the key is a smi.
3488 __ JumpIfNotSmi(ecx, &miss_force_generic);
3489
3490 // Check that the index is in range.
3491 __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
3492 __ cmp(ecx, FieldOperand(edi, ExternalArray::kLengthOffset));
3493 // Unsigned comparison catches both negative and too-large values.
3494 __ j(above_equal, &slow);
3495
3496 // Handle both smis and HeapNumbers in the fast path. Go to the
3497 // runtime for all other kinds of values.
3498 // eax: value
3499 // edx: receiver
3500 // ecx: key
3501 // edi: elements array
3502 if (elements_kind == EXTERNAL_PIXEL_ELEMENTS) {
3503 __ JumpIfNotSmi(eax, &slow);
3504 } else {
3505 __ JumpIfNotSmi(eax, &check_heap_number);
3506 }
3507
3508 // smi case
3509 __ mov(ebx, eax); // Preserve the value in eax as the return value.
3510 __ SmiUntag(ebx);
3511 __ mov(edi, FieldOperand(edi, ExternalArray::kExternalPointerOffset));
3512 // edi: base pointer of external storage
3513 switch (elements_kind) {
3514 case EXTERNAL_PIXEL_ELEMENTS:
3515 __ ClampUint8(ebx);
3516 __ SmiUntag(ecx);
3517 __ mov_b(Operand(edi, ecx, times_1, 0), ebx);
3518 break;
3519 case EXTERNAL_BYTE_ELEMENTS:
3520 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
3521 __ SmiUntag(ecx);
3522 __ mov_b(Operand(edi, ecx, times_1, 0), ebx);
3523 break;
3524 case EXTERNAL_SHORT_ELEMENTS:
3525 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
3526 __ mov_w(Operand(edi, ecx, times_1, 0), ebx);
3527 break;
3528 case EXTERNAL_INT_ELEMENTS:
3529 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
3530 __ mov(Operand(edi, ecx, times_2, 0), ebx);
3531 break;
3532 case EXTERNAL_FLOAT_ELEMENTS:
3533 case EXTERNAL_DOUBLE_ELEMENTS:
3534 // Need to perform int-to-float conversion.
3535 __ push(ebx);
3536 __ fild_s(Operand(esp, 0));
3537 __ pop(ebx);
3538 if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
3539 __ fstp_s(Operand(edi, ecx, times_2, 0));
3540 } else { // elements_kind == EXTERNAL_DOUBLE_ELEMENTS.
3541 __ fstp_d(Operand(edi, ecx, times_4, 0));
3542 }
3543 break;
3544 default:
3545 UNREACHABLE();
3546 break;
3547 }
3548 __ ret(0); // Return the original value.
3549
3550 // TODO(danno): handle heap number -> pixel array conversion
3551 if (elements_kind != EXTERNAL_PIXEL_ELEMENTS) {
3552 __ bind(&check_heap_number);
3553 // eax: value
3554 // edx: receiver
3555 // ecx: key
3556 // edi: elements array
3557 __ cmp(FieldOperand(eax, HeapObject::kMapOffset),
3558 Immediate(masm->isolate()->factory()->heap_number_map()));
3559 __ j(not_equal, &slow);
3560
3561 // The WebGL specification leaves the behavior of storing NaN and
3562 // +/-Infinity into integer arrays basically undefined. For more
3563 // reproducible behavior, convert these to zero.
3564 __ mov(edi, FieldOperand(edi, ExternalArray::kExternalPointerOffset));
3565 // edi: base pointer of external storage
3566 if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
3567 __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
3568 __ fstp_s(Operand(edi, ecx, times_2, 0));
3569 __ ret(0);
3570 } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) {
3571 __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
3572 __ fstp_d(Operand(edi, ecx, times_4, 0));
3573 __ ret(0);
3574 } else {
3575 // Perform float-to-int conversion with truncation (round-to-zero)
3576 // behavior.
3577
3578 // For the moment we make the slow call to the runtime on
3579 // processors that don't support SSE2. The code in IntegerConvert
3580 // (code-stubs-ia32.cc) is roughly what is needed here though the
3581 // conversion failure case does not need to be handled.
3582 if (CpuFeatures::IsSupported(SSE2)) {
3583 if (elements_kind != EXTERNAL_INT_ELEMENTS &&
3584 elements_kind != EXTERNAL_UNSIGNED_INT_ELEMENTS) {
3585 ASSERT(CpuFeatures::IsSupported(SSE2));
3586 CpuFeatures::Scope scope(SSE2);
3587 __ cvttsd2si(ebx, FieldOperand(eax, HeapNumber::kValueOffset));
3588 // ecx: untagged integer value
3589 switch (elements_kind) {
3590 case EXTERNAL_PIXEL_ELEMENTS:
3591 __ ClampUint8(ebx);
3592 // Fall through.
3593 case EXTERNAL_BYTE_ELEMENTS:
3594 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
3595 __ SmiUntag(ecx);
3596 __ mov_b(Operand(edi, ecx, times_1, 0), ebx);
3597 break;
3598 case EXTERNAL_SHORT_ELEMENTS:
3599 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
3600 __ mov_w(Operand(edi, ecx, times_1, 0), ebx);
3601 break;
3602 default:
3603 UNREACHABLE();
3604 break;
3605 }
3606 } else {
3607 if (CpuFeatures::IsSupported(SSE3)) {
3608 CpuFeatures::Scope scope(SSE3);
3609 // fisttp stores values as signed integers. To represent the
3610 // entire range of int and unsigned int arrays, store as a
3611 // 64-bit int and discard the high 32 bits.
3612 // If the value is NaN or +/-infinity, the result is 0x80000000,
3613 // which is automatically zero when taken mod 2^n, n < 32.
3614 __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
3615 __ sub(esp, Immediate(2 * kPointerSize));
3616 __ fisttp_d(Operand(esp, 0));
3617 __ pop(ebx);
3618 __ add(esp, Immediate(kPointerSize));
3619 } else {
3620 ASSERT(CpuFeatures::IsSupported(SSE2));
3621 CpuFeatures::Scope scope(SSE2);
3622 // We can easily implement the correct rounding behavior for the
3623 // range [0, 2^31-1]. For the time being, to keep this code simple,
3624 // make the slow runtime call for values outside this range.
3625 // Note: we could do better for signed int arrays.
3626 __ movd(xmm0, FieldOperand(eax, HeapNumber::kValueOffset));
3627 // We will need the key if we have to make the slow runtime call.
3628 __ push(ebx);
3629 __ LoadPowerOf2(xmm1, ebx, 31);
3630 __ pop(ebx);
3631 __ ucomisd(xmm1, xmm0);
3632 __ j(above_equal, &slow);
3633 __ cvttsd2si(ebx, Operand(xmm0));
3634 }
3635 // ebx: untagged integer value
3636 __ mov(Operand(edi, ecx, times_2, 0), ebx);
3637 }
3638 __ ret(0); // Return original value.
3639 }
3640 }
3641 }
3642
3643 // Slow case: call runtime.
3644 __ bind(&slow);
3645 Counters* counters = masm->isolate()->counters();
3646 __ IncrementCounter(counters->keyed_store_external_array_slow(), 1);
3647
3648 // ----------- S t a t e -------------
3649 // -- eax : value
3650 // -- ecx : key
3651 // -- edx : receiver
3652 // -- esp[0] : return address
3653 // -----------------------------------
3654
3655 Handle<Code> ic = masm->isolate()->builtins()->KeyedStoreIC_Slow();
3656 __ jmp(ic, RelocInfo::CODE_TARGET);
3657
3658 // ----------- S t a t e -------------
3659 // -- eax : value
3660 // -- ecx : key
3661 // -- edx : receiver
3662 // -- esp[0] : return address
3663 // -----------------------------------
3664
3665 __ bind(&miss_force_generic);
3666 Handle<Code> miss_ic =
3667 masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
3668 __ jmp(miss_ic, RelocInfo::CODE_TARGET);
3669 }
3670
3671
GenerateLoadFastElement(MacroAssembler * masm)3672 void KeyedLoadStubCompiler::GenerateLoadFastElement(MacroAssembler* masm) {
3673 // ----------- S t a t e -------------
3674 // -- eax : key
3675 // -- edx : receiver
3676 // -- esp[0] : return address
3677 // -----------------------------------
3678 Label miss_force_generic;
3679
3680 // This stub is meant to be tail-jumped to, the receiver must already
3681 // have been verified by the caller to not be a smi.
3682
3683 // Check that the key is a smi.
3684 __ JumpIfNotSmi(eax, &miss_force_generic);
3685
3686 // Get the elements array.
3687 __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset));
3688 __ AssertFastElements(ecx);
3689
3690 // Check that the key is within bounds.
3691 __ cmp(eax, FieldOperand(ecx, FixedArray::kLengthOffset));
3692 __ j(above_equal, &miss_force_generic);
3693
3694 // Load the result and make sure it's not the hole.
3695 __ mov(ebx, Operand(ecx, eax, times_2,
3696 FixedArray::kHeaderSize - kHeapObjectTag));
3697 __ cmp(ebx, masm->isolate()->factory()->the_hole_value());
3698 __ j(equal, &miss_force_generic);
3699 __ mov(eax, ebx);
3700 __ ret(0);
3701
3702 __ bind(&miss_force_generic);
3703 Handle<Code> miss_ic =
3704 masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric();
3705 __ jmp(miss_ic, RelocInfo::CODE_TARGET);
3706 }
3707
3708
GenerateLoadFastDoubleElement(MacroAssembler * masm)3709 void KeyedLoadStubCompiler::GenerateLoadFastDoubleElement(
3710 MacroAssembler* masm) {
3711 // ----------- S t a t e -------------
3712 // -- eax : key
3713 // -- edx : receiver
3714 // -- esp[0] : return address
3715 // -----------------------------------
3716 Label miss_force_generic, slow_allocate_heapnumber;
3717
3718 // This stub is meant to be tail-jumped to, the receiver must already
3719 // have been verified by the caller to not be a smi.
3720
3721 // Check that the key is a smi.
3722 __ JumpIfNotSmi(eax, &miss_force_generic);
3723
3724 // Get the elements array.
3725 __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset));
3726 __ AssertFastElements(ecx);
3727
3728 // Check that the key is within bounds.
3729 __ cmp(eax, FieldOperand(ecx, FixedDoubleArray::kLengthOffset));
3730 __ j(above_equal, &miss_force_generic);
3731
3732 // Check for the hole
3733 uint32_t offset = FixedDoubleArray::kHeaderSize + sizeof(kHoleNanLower32);
3734 __ cmp(FieldOperand(ecx, eax, times_4, offset), Immediate(kHoleNanUpper32));
3735 __ j(equal, &miss_force_generic);
3736
3737 // Always allocate a heap number for the result.
3738 if (CpuFeatures::IsSupported(SSE2)) {
3739 CpuFeatures::Scope use_sse2(SSE2);
3740 __ movdbl(xmm0, FieldOperand(ecx, eax, times_4,
3741 FixedDoubleArray::kHeaderSize));
3742 } else {
3743 __ fld_d(FieldOperand(ecx, eax, times_4, FixedDoubleArray::kHeaderSize));
3744 }
3745 __ AllocateHeapNumber(ecx, ebx, edi, &slow_allocate_heapnumber);
3746 // Set the value.
3747 if (CpuFeatures::IsSupported(SSE2)) {
3748 CpuFeatures::Scope use_sse2(SSE2);
3749 __ movdbl(FieldOperand(ecx, HeapNumber::kValueOffset), xmm0);
3750 } else {
3751 __ fstp_d(FieldOperand(ecx, HeapNumber::kValueOffset));
3752 }
3753 __ mov(eax, ecx);
3754 __ ret(0);
3755
3756 __ bind(&slow_allocate_heapnumber);
3757 // A value was pushed on the floating point stack before the allocation, if
3758 // the allocation fails it needs to be removed.
3759 if (!CpuFeatures::IsSupported(SSE2)) {
3760 __ fstp(0);
3761 }
3762 Handle<Code> slow_ic =
3763 masm->isolate()->builtins()->KeyedLoadIC_Slow();
3764 __ jmp(slow_ic, RelocInfo::CODE_TARGET);
3765
3766 __ bind(&miss_force_generic);
3767 Handle<Code> miss_ic =
3768 masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric();
3769 __ jmp(miss_ic, RelocInfo::CODE_TARGET);
3770 }
3771
3772
GenerateStoreFastElement(MacroAssembler * masm,bool is_js_array,ElementsKind elements_kind,KeyedAccessGrowMode grow_mode)3773 void KeyedStoreStubCompiler::GenerateStoreFastElement(
3774 MacroAssembler* masm,
3775 bool is_js_array,
3776 ElementsKind elements_kind,
3777 KeyedAccessGrowMode grow_mode) {
3778 // ----------- S t a t e -------------
3779 // -- eax : value
3780 // -- ecx : key
3781 // -- edx : receiver
3782 // -- esp[0] : return address
3783 // -----------------------------------
3784 Label miss_force_generic, grow, slow, transition_elements_kind;
3785 Label check_capacity, prepare_slow, finish_store, commit_backing_store;
3786
3787 // This stub is meant to be tail-jumped to, the receiver must already
3788 // have been verified by the caller to not be a smi.
3789
3790 // Check that the key is a smi.
3791 __ JumpIfNotSmi(ecx, &miss_force_generic);
3792
3793 if (elements_kind == FAST_SMI_ONLY_ELEMENTS) {
3794 __ JumpIfNotSmi(eax, &transition_elements_kind);
3795 }
3796
3797 // Get the elements array and make sure it is a fast element array, not 'cow'.
3798 __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
3799 if (is_js_array) {
3800 // Check that the key is within bounds.
3801 __ cmp(ecx, FieldOperand(edx, JSArray::kLengthOffset)); // smis.
3802 if (grow_mode == ALLOW_JSARRAY_GROWTH) {
3803 __ j(above_equal, &grow);
3804 } else {
3805 __ j(above_equal, &miss_force_generic);
3806 }
3807 } else {
3808 // Check that the key is within bounds.
3809 __ cmp(ecx, FieldOperand(edi, FixedArray::kLengthOffset)); // smis.
3810 __ j(above_equal, &miss_force_generic);
3811 }
3812
3813 __ cmp(FieldOperand(edi, HeapObject::kMapOffset),
3814 Immediate(masm->isolate()->factory()->fixed_array_map()));
3815 __ j(not_equal, &miss_force_generic);
3816
3817 __ bind(&finish_store);
3818 if (elements_kind == FAST_SMI_ONLY_ELEMENTS) {
3819 // ecx is a smi, use times_half_pointer_size instead of
3820 // times_pointer_size
3821 __ mov(FieldOperand(edi,
3822 ecx,
3823 times_half_pointer_size,
3824 FixedArray::kHeaderSize), eax);
3825 } else {
3826 ASSERT(elements_kind == FAST_ELEMENTS);
3827 // Do the store and update the write barrier.
3828 // ecx is a smi, use times_half_pointer_size instead of
3829 // times_pointer_size
3830 __ lea(ecx, FieldOperand(edi,
3831 ecx,
3832 times_half_pointer_size,
3833 FixedArray::kHeaderSize));
3834 __ mov(Operand(ecx, 0), eax);
3835 // Make sure to preserve the value in register eax.
3836 __ mov(ebx, eax);
3837 __ RecordWrite(edi, ecx, ebx, kDontSaveFPRegs);
3838 }
3839
3840 // Done.
3841 __ ret(0);
3842
3843 // Handle store cache miss, replacing the ic with the generic stub.
3844 __ bind(&miss_force_generic);
3845 Handle<Code> ic_force_generic =
3846 masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
3847 __ jmp(ic_force_generic, RelocInfo::CODE_TARGET);
3848
3849 // Handle transition to other elements kinds without using the generic stub.
3850 __ bind(&transition_elements_kind);
3851 Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss();
3852 __ jmp(ic_miss, RelocInfo::CODE_TARGET);
3853
3854 if (is_js_array && grow_mode == ALLOW_JSARRAY_GROWTH) {
3855 // Handle transition requiring the array to grow.
3856 __ bind(&grow);
3857
3858 // Make sure the array is only growing by a single element, anything else
3859 // must be handled by the runtime. Flags are already set by previous
3860 // compare.
3861 __ j(not_equal, &miss_force_generic);
3862
3863 // Check for the empty array, and preallocate a small backing store if
3864 // possible.
3865 __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
3866 __ cmp(edi, Immediate(masm->isolate()->factory()->empty_fixed_array()));
3867 __ j(not_equal, &check_capacity);
3868
3869 int size = FixedArray::SizeFor(JSArray::kPreallocatedArrayElements);
3870 __ AllocateInNewSpace(size, edi, ebx, ecx, &prepare_slow, TAG_OBJECT);
3871 // Restore the key, which is known to be the array length.
3872
3873 // eax: value
3874 // ecx: key
3875 // edx: receiver
3876 // edi: elements
3877 // Make sure that the backing store can hold additional elements.
3878 __ mov(FieldOperand(edi, JSObject::kMapOffset),
3879 Immediate(masm->isolate()->factory()->fixed_array_map()));
3880 __ mov(FieldOperand(edi, FixedArray::kLengthOffset),
3881 Immediate(Smi::FromInt(JSArray::kPreallocatedArrayElements)));
3882 __ mov(ebx, Immediate(masm->isolate()->factory()->the_hole_value()));
3883 for (int i = 1; i < JSArray::kPreallocatedArrayElements; ++i) {
3884 __ mov(FieldOperand(edi, FixedArray::SizeFor(i)), ebx);
3885 }
3886
3887 // Store the element at index zero.
3888 __ mov(FieldOperand(edi, FixedArray::SizeFor(0)), eax);
3889
3890 // Install the new backing store in the JSArray.
3891 __ mov(FieldOperand(edx, JSObject::kElementsOffset), edi);
3892 __ RecordWriteField(edx, JSObject::kElementsOffset, edi, ebx,
3893 kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
3894
3895 // Increment the length of the array.
3896 __ mov(FieldOperand(edx, JSArray::kLengthOffset),
3897 Immediate(Smi::FromInt(1)));
3898 __ ret(0);
3899
3900 __ bind(&check_capacity);
3901 __ cmp(FieldOperand(edi, HeapObject::kMapOffset),
3902 Immediate(masm->isolate()->factory()->fixed_cow_array_map()));
3903 __ j(equal, &miss_force_generic);
3904
3905 // eax: value
3906 // ecx: key
3907 // edx: receiver
3908 // edi: elements
3909 // Make sure that the backing store can hold additional elements.
3910 __ cmp(ecx, FieldOperand(edi, FixedArray::kLengthOffset));
3911 __ j(above_equal, &slow);
3912
3913 // Grow the array and finish the store.
3914 __ add(FieldOperand(edx, JSArray::kLengthOffset),
3915 Immediate(Smi::FromInt(1)));
3916 __ jmp(&finish_store);
3917
3918 __ bind(&prepare_slow);
3919 // Restore the key, which is known to be the array length.
3920 __ mov(ecx, Immediate(0));
3921
3922 __ bind(&slow);
3923 Handle<Code> ic_slow = masm->isolate()->builtins()->KeyedStoreIC_Slow();
3924 __ jmp(ic_slow, RelocInfo::CODE_TARGET);
3925 }
3926 }
3927
3928
GenerateStoreFastDoubleElement(MacroAssembler * masm,bool is_js_array,KeyedAccessGrowMode grow_mode)3929 void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(
3930 MacroAssembler* masm,
3931 bool is_js_array,
3932 KeyedAccessGrowMode grow_mode) {
3933 // ----------- S t a t e -------------
3934 // -- eax : value
3935 // -- ecx : key
3936 // -- edx : receiver
3937 // -- esp[0] : return address
3938 // -----------------------------------
3939 Label miss_force_generic, transition_elements_kind, grow, slow;
3940 Label check_capacity, prepare_slow, finish_store, commit_backing_store;
3941
3942 // This stub is meant to be tail-jumped to, the receiver must already
3943 // have been verified by the caller to not be a smi.
3944
3945 // Check that the key is a smi.
3946 __ JumpIfNotSmi(ecx, &miss_force_generic);
3947
3948 // Get the elements array.
3949 __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
3950 __ AssertFastElements(edi);
3951
3952 if (is_js_array) {
3953 // Check that the key is within bounds.
3954 __ cmp(ecx, FieldOperand(edx, JSArray::kLengthOffset)); // smis.
3955 if (grow_mode == ALLOW_JSARRAY_GROWTH) {
3956 __ j(above_equal, &grow);
3957 } else {
3958 __ j(above_equal, &miss_force_generic);
3959 }
3960 } else {
3961 // Check that the key is within bounds.
3962 __ cmp(ecx, FieldOperand(edi, FixedArray::kLengthOffset)); // smis.
3963 __ j(above_equal, &miss_force_generic);
3964 }
3965
3966 __ bind(&finish_store);
3967 __ StoreNumberToDoubleElements(eax, edi, ecx, edx, xmm0,
3968 &transition_elements_kind, true);
3969 __ ret(0);
3970
3971 // Handle store cache miss, replacing the ic with the generic stub.
3972 __ bind(&miss_force_generic);
3973 Handle<Code> ic_force_generic =
3974 masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
3975 __ jmp(ic_force_generic, RelocInfo::CODE_TARGET);
3976
3977 // Handle transition to other elements kinds without using the generic stub.
3978 __ bind(&transition_elements_kind);
3979 Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss();
3980 __ jmp(ic_miss, RelocInfo::CODE_TARGET);
3981
3982 if (is_js_array && grow_mode == ALLOW_JSARRAY_GROWTH) {
3983 // Handle transition requiring the array to grow.
3984 __ bind(&grow);
3985
3986 // Make sure the array is only growing by a single element, anything else
3987 // must be handled by the runtime. Flags are already set by previous
3988 // compare.
3989 __ j(not_equal, &miss_force_generic);
3990
3991 // Transition on values that can't be stored in a FixedDoubleArray.
3992 Label value_is_smi;
3993 __ JumpIfSmi(eax, &value_is_smi);
3994 __ cmp(FieldOperand(eax, HeapObject::kMapOffset),
3995 Immediate(Handle<Map>(masm->isolate()->heap()->heap_number_map())));
3996 __ j(not_equal, &transition_elements_kind);
3997 __ bind(&value_is_smi);
3998
3999 // Check for the empty array, and preallocate a small backing store if
4000 // possible.
4001 __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
4002 __ cmp(edi, Immediate(masm->isolate()->factory()->empty_fixed_array()));
4003 __ j(not_equal, &check_capacity);
4004
4005 int size = FixedDoubleArray::SizeFor(JSArray::kPreallocatedArrayElements);
4006 __ AllocateInNewSpace(size, edi, ebx, ecx, &prepare_slow, TAG_OBJECT);
4007 // Restore the key, which is known to be the array length.
4008 __ mov(ecx, Immediate(0));
4009
4010 // eax: value
4011 // ecx: key
4012 // edx: receiver
4013 // edi: elements
4014 // Initialize the new FixedDoubleArray. Leave elements unitialized for
4015 // efficiency, they are guaranteed to be initialized before use.
4016 __ mov(FieldOperand(edi, JSObject::kMapOffset),
4017 Immediate(masm->isolate()->factory()->fixed_double_array_map()));
4018 __ mov(FieldOperand(edi, FixedDoubleArray::kLengthOffset),
4019 Immediate(Smi::FromInt(JSArray::kPreallocatedArrayElements)));
4020
4021 // Install the new backing store in the JSArray.
4022 __ mov(FieldOperand(edx, JSObject::kElementsOffset), edi);
4023 __ RecordWriteField(edx, JSObject::kElementsOffset, edi, ebx,
4024 kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
4025
4026 // Increment the length of the array.
4027 __ add(FieldOperand(edx, JSArray::kLengthOffset),
4028 Immediate(Smi::FromInt(1)));
4029 __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
4030 __ jmp(&finish_store);
4031
4032 __ bind(&check_capacity);
4033 // eax: value
4034 // ecx: key
4035 // edx: receiver
4036 // edi: elements
4037 // Make sure that the backing store can hold additional elements.
4038 __ cmp(ecx, FieldOperand(edi, FixedDoubleArray::kLengthOffset));
4039 __ j(above_equal, &slow);
4040
4041 // Grow the array and finish the store.
4042 __ add(FieldOperand(edx, JSArray::kLengthOffset),
4043 Immediate(Smi::FromInt(1)));
4044 __ jmp(&finish_store);
4045
4046 __ bind(&prepare_slow);
4047 // Restore the key, which is known to be the array length.
4048 __ mov(ecx, Immediate(0));
4049
4050 __ bind(&slow);
4051 Handle<Code> ic_slow = masm->isolate()->builtins()->KeyedStoreIC_Slow();
4052 __ jmp(ic_slow, RelocInfo::CODE_TARGET);
4053 }
4054 }
4055
4056
4057 #undef __
4058
4059 } } // namespace v8::internal
4060
4061 #endif // V8_TARGET_ARCH_IA32
4062