• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "codegen.h"
33 #include "heap.h"
34 #include "macro-assembler.h"
35 
36 namespace v8 {
37 namespace internal {
38 
39 
40 // -------------------------------------------------------------------------
41 // Platform-specific RuntimeCallHelper functions.
42 
BeforeCall(MacroAssembler * masm) const43 void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
44   masm->EnterFrame(StackFrame::INTERNAL);
45   ASSERT(!masm->has_frame());
46   masm->set_has_frame(true);
47 }
48 
49 
AfterCall(MacroAssembler * masm) const50 void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
51   masm->LeaveFrame(StackFrame::INTERNAL);
52   ASSERT(masm->has_frame());
53   masm->set_has_frame(false);
54 }
55 
56 
57 #define __ masm.
58 
59 
CreateTranscendentalFunction(TranscendentalCache::Type type)60 UnaryMathFunction CreateTranscendentalFunction(TranscendentalCache::Type type) {
61   size_t actual_size;
62   // Allocate buffer in executable space.
63   byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB,
64                                                  &actual_size,
65                                                  true));
66   if (buffer == NULL) {
67     // Fallback to library function if function cannot be created.
68     switch (type) {
69       case TranscendentalCache::SIN: return &sin;
70       case TranscendentalCache::COS: return &cos;
71       case TranscendentalCache::TAN: return &tan;
72       case TranscendentalCache::LOG: return &log;
73       default: UNIMPLEMENTED();
74     }
75   }
76 
77   MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
78   // esp[1 * kPointerSize]: raw double input
79   // esp[0 * kPointerSize]: return address
80   // Move double input into registers.
81 
82   __ push(ebx);
83   __ push(edx);
84   __ push(edi);
85   __ fld_d(Operand(esp, 4 * kPointerSize));
86   __ mov(ebx, Operand(esp, 4 * kPointerSize));
87   __ mov(edx, Operand(esp, 5 * kPointerSize));
88   TranscendentalCacheStub::GenerateOperation(&masm, type);
89   // The return value is expected to be on ST(0) of the FPU stack.
90   __ pop(edi);
91   __ pop(edx);
92   __ pop(ebx);
93   __ Ret();
94 
95   CodeDesc desc;
96   masm.GetCode(&desc);
97   ASSERT(desc.reloc_size == 0);
98 
99   CPU::FlushICache(buffer, actual_size);
100   OS::ProtectCode(buffer, actual_size);
101   return FUNCTION_CAST<UnaryMathFunction>(buffer);
102 }
103 
104 
CreateSqrtFunction()105 UnaryMathFunction CreateSqrtFunction() {
106   size_t actual_size;
107   // Allocate buffer in executable space.
108   byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB,
109                                                  &actual_size,
110                                                  true));
111   // If SSE2 is not available, we can use libc's implementation to ensure
112   // consistency since code by fullcodegen's calls into runtime in that case.
113   if (buffer == NULL || !CpuFeatures::IsSupported(SSE2)) return &sqrt;
114   MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
115   // esp[1 * kPointerSize]: raw double input
116   // esp[0 * kPointerSize]: return address
117   // Move double input into registers.
118   {
119     CpuFeatures::Scope use_sse2(SSE2);
120     __ movdbl(xmm0, Operand(esp, 1 * kPointerSize));
121     __ sqrtsd(xmm0, xmm0);
122     __ movdbl(Operand(esp, 1 * kPointerSize), xmm0);
123     // Load result into floating point register as return value.
124     __ fld_d(Operand(esp, 1 * kPointerSize));
125     __ Ret();
126   }
127 
128   CodeDesc desc;
129   masm.GetCode(&desc);
130   ASSERT(desc.reloc_size == 0);
131 
132   CPU::FlushICache(buffer, actual_size);
133   OS::ProtectCode(buffer, actual_size);
134   return FUNCTION_CAST<UnaryMathFunction>(buffer);
135 }
136 
137 
MemCopyWrapper(void * dest,const void * src,size_t size)138 static void MemCopyWrapper(void* dest, const void* src, size_t size) {
139   memcpy(dest, src, size);
140 }
141 
142 
CreateMemCopyFunction()143 OS::MemCopyFunction CreateMemCopyFunction() {
144   size_t actual_size;
145   // Allocate buffer in executable space.
146   byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB,
147                                                  &actual_size,
148                                                  true));
149   if (buffer == NULL) return &MemCopyWrapper;
150   MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
151 
152   // Generated code is put into a fixed, unmovable, buffer, and not into
153   // the V8 heap. We can't, and don't, refer to any relocatable addresses
154   // (e.g. the JavaScript nan-object).
155 
156   // 32-bit C declaration function calls pass arguments on stack.
157 
158   // Stack layout:
159   // esp[12]: Third argument, size.
160   // esp[8]: Second argument, source pointer.
161   // esp[4]: First argument, destination pointer.
162   // esp[0]: return address
163 
164   const int kDestinationOffset = 1 * kPointerSize;
165   const int kSourceOffset = 2 * kPointerSize;
166   const int kSizeOffset = 3 * kPointerSize;
167 
168   int stack_offset = 0;  // Update if we change the stack height.
169 
170   if (FLAG_debug_code) {
171     __ cmp(Operand(esp, kSizeOffset + stack_offset),
172            Immediate(OS::kMinComplexMemCopy));
173     Label ok;
174     __ j(greater_equal, &ok);
175     __ int3();
176     __ bind(&ok);
177   }
178   if (CpuFeatures::IsSupported(SSE2)) {
179     CpuFeatures::Scope enable(SSE2);
180     __ push(edi);
181     __ push(esi);
182     stack_offset += 2 * kPointerSize;
183     Register dst = edi;
184     Register src = esi;
185     Register count = ecx;
186     __ mov(dst, Operand(esp, stack_offset + kDestinationOffset));
187     __ mov(src, Operand(esp, stack_offset + kSourceOffset));
188     __ mov(count, Operand(esp, stack_offset + kSizeOffset));
189 
190 
191     __ movdqu(xmm0, Operand(src, 0));
192     __ movdqu(Operand(dst, 0), xmm0);
193     __ mov(edx, dst);
194     __ and_(edx, 0xF);
195     __ neg(edx);
196     __ add(edx, Immediate(16));
197     __ add(dst, edx);
198     __ add(src, edx);
199     __ sub(count, edx);
200 
201     // edi is now aligned. Check if esi is also aligned.
202     Label unaligned_source;
203     __ test(src, Immediate(0x0F));
204     __ j(not_zero, &unaligned_source);
205     {
206       // Copy loop for aligned source and destination.
207       __ mov(edx, count);
208       Register loop_count = ecx;
209       Register count = edx;
210       __ shr(loop_count, 5);
211       {
212         // Main copy loop.
213         Label loop;
214         __ bind(&loop);
215         __ prefetch(Operand(src, 0x20), 1);
216         __ movdqa(xmm0, Operand(src, 0x00));
217         __ movdqa(xmm1, Operand(src, 0x10));
218         __ add(src, Immediate(0x20));
219 
220         __ movdqa(Operand(dst, 0x00), xmm0);
221         __ movdqa(Operand(dst, 0x10), xmm1);
222         __ add(dst, Immediate(0x20));
223 
224         __ dec(loop_count);
225         __ j(not_zero, &loop);
226       }
227 
228       // At most 31 bytes to copy.
229       Label move_less_16;
230       __ test(count, Immediate(0x10));
231       __ j(zero, &move_less_16);
232       __ movdqa(xmm0, Operand(src, 0));
233       __ add(src, Immediate(0x10));
234       __ movdqa(Operand(dst, 0), xmm0);
235       __ add(dst, Immediate(0x10));
236       __ bind(&move_less_16);
237 
238       // At most 15 bytes to copy. Copy 16 bytes at end of string.
239       __ and_(count, 0xF);
240       __ movdqu(xmm0, Operand(src, count, times_1, -0x10));
241       __ movdqu(Operand(dst, count, times_1, -0x10), xmm0);
242 
243       __ mov(eax, Operand(esp, stack_offset + kDestinationOffset));
244       __ pop(esi);
245       __ pop(edi);
246       __ ret(0);
247     }
248     __ Align(16);
249     {
250       // Copy loop for unaligned source and aligned destination.
251       // If source is not aligned, we can't read it as efficiently.
252       __ bind(&unaligned_source);
253       __ mov(edx, ecx);
254       Register loop_count = ecx;
255       Register count = edx;
256       __ shr(loop_count, 5);
257       {
258         // Main copy loop
259         Label loop;
260         __ bind(&loop);
261         __ prefetch(Operand(src, 0x20), 1);
262         __ movdqu(xmm0, Operand(src, 0x00));
263         __ movdqu(xmm1, Operand(src, 0x10));
264         __ add(src, Immediate(0x20));
265 
266         __ movdqa(Operand(dst, 0x00), xmm0);
267         __ movdqa(Operand(dst, 0x10), xmm1);
268         __ add(dst, Immediate(0x20));
269 
270         __ dec(loop_count);
271         __ j(not_zero, &loop);
272       }
273 
274       // At most 31 bytes to copy.
275       Label move_less_16;
276       __ test(count, Immediate(0x10));
277       __ j(zero, &move_less_16);
278       __ movdqu(xmm0, Operand(src, 0));
279       __ add(src, Immediate(0x10));
280       __ movdqa(Operand(dst, 0), xmm0);
281       __ add(dst, Immediate(0x10));
282       __ bind(&move_less_16);
283 
284       // At most 15 bytes to copy. Copy 16 bytes at end of string.
285       __ and_(count, 0x0F);
286       __ movdqu(xmm0, Operand(src, count, times_1, -0x10));
287       __ movdqu(Operand(dst, count, times_1, -0x10), xmm0);
288 
289       __ mov(eax, Operand(esp, stack_offset + kDestinationOffset));
290       __ pop(esi);
291       __ pop(edi);
292       __ ret(0);
293     }
294 
295   } else {
296     // SSE2 not supported. Unlikely to happen in practice.
297     __ push(edi);
298     __ push(esi);
299     stack_offset += 2 * kPointerSize;
300     __ cld();
301     Register dst = edi;
302     Register src = esi;
303     Register count = ecx;
304     __ mov(dst, Operand(esp, stack_offset + kDestinationOffset));
305     __ mov(src, Operand(esp, stack_offset + kSourceOffset));
306     __ mov(count, Operand(esp, stack_offset + kSizeOffset));
307 
308     // Copy the first word.
309     __ mov(eax, Operand(src, 0));
310     __ mov(Operand(dst, 0), eax);
311 
312     // Increment src,dstso that dst is aligned.
313     __ mov(edx, dst);
314     __ and_(edx, 0x03);
315     __ neg(edx);
316     __ add(edx, Immediate(4));  // edx = 4 - (dst & 3)
317     __ add(dst, edx);
318     __ add(src, edx);
319     __ sub(count, edx);
320     // edi is now aligned, ecx holds number of remaning bytes to copy.
321 
322     __ mov(edx, count);
323     count = edx;
324     __ shr(ecx, 2);  // Make word count instead of byte count.
325     __ rep_movs();
326 
327     // At most 3 bytes left to copy. Copy 4 bytes at end of string.
328     __ and_(count, 3);
329     __ mov(eax, Operand(src, count, times_1, -4));
330     __ mov(Operand(dst, count, times_1, -4), eax);
331 
332     __ mov(eax, Operand(esp, stack_offset + kDestinationOffset));
333     __ pop(esi);
334     __ pop(edi);
335     __ ret(0);
336   }
337 
338   CodeDesc desc;
339   masm.GetCode(&desc);
340   ASSERT(desc.reloc_size == 0);
341 
342   CPU::FlushICache(buffer, actual_size);
343   OS::ProtectCode(buffer, actual_size);
344   return FUNCTION_CAST<OS::MemCopyFunction>(buffer);
345 }
346 
347 #undef __
348 
349 // -------------------------------------------------------------------------
350 // Code generators
351 
352 #define __ ACCESS_MASM(masm)
353 
GenerateSmiOnlyToObject(MacroAssembler * masm)354 void ElementsTransitionGenerator::GenerateSmiOnlyToObject(
355     MacroAssembler* masm) {
356   // ----------- S t a t e -------------
357   //  -- eax    : value
358   //  -- ebx    : target map
359   //  -- ecx    : key
360   //  -- edx    : receiver
361   //  -- esp[0] : return address
362   // -----------------------------------
363   // Set transitioned map.
364   __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx);
365   __ RecordWriteField(edx,
366                       HeapObject::kMapOffset,
367                       ebx,
368                       edi,
369                       kDontSaveFPRegs,
370                       EMIT_REMEMBERED_SET,
371                       OMIT_SMI_CHECK);
372 }
373 
374 
GenerateSmiOnlyToDouble(MacroAssembler * masm,Label * fail)375 void ElementsTransitionGenerator::GenerateSmiOnlyToDouble(
376     MacroAssembler* masm, Label* fail) {
377   // ----------- S t a t e -------------
378   //  -- eax    : value
379   //  -- ebx    : target map
380   //  -- ecx    : key
381   //  -- edx    : receiver
382   //  -- esp[0] : return address
383   // -----------------------------------
384   Label loop, entry, convert_hole, gc_required, only_change_map;
385 
386   // Check for empty arrays, which only require a map transition and no changes
387   // to the backing store.
388   __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
389   __ cmp(edi, Immediate(masm->isolate()->factory()->empty_fixed_array()));
390   __ j(equal, &only_change_map);
391 
392   __ push(eax);
393   __ push(ebx);
394 
395   __ mov(edi, FieldOperand(edi, FixedArray::kLengthOffset));
396 
397   // Allocate new FixedDoubleArray.
398   // edx: receiver
399   // edi: length of source FixedArray (smi-tagged)
400   __ lea(esi, Operand(edi, times_4, FixedDoubleArray::kHeaderSize));
401   __ AllocateInNewSpace(esi, eax, ebx, no_reg, &gc_required, TAG_OBJECT);
402 
403   // eax: destination FixedDoubleArray
404   // edi: number of elements
405   // edx: receiver
406   __ mov(FieldOperand(eax, HeapObject::kMapOffset),
407          Immediate(masm->isolate()->factory()->fixed_double_array_map()));
408   __ mov(FieldOperand(eax, FixedDoubleArray::kLengthOffset), edi);
409   __ mov(esi, FieldOperand(edx, JSObject::kElementsOffset));
410   // Replace receiver's backing store with newly created FixedDoubleArray.
411   __ mov(FieldOperand(edx, JSObject::kElementsOffset), eax);
412   __ mov(ebx, eax);
413   __ RecordWriteField(edx,
414                       JSObject::kElementsOffset,
415                       ebx,
416                       edi,
417                       kDontSaveFPRegs,
418                       EMIT_REMEMBERED_SET,
419                       OMIT_SMI_CHECK);
420 
421   __ mov(edi, FieldOperand(esi, FixedArray::kLengthOffset));
422 
423   // Prepare for conversion loop.
424   ExternalReference canonical_the_hole_nan_reference =
425       ExternalReference::address_of_the_hole_nan();
426   XMMRegister the_hole_nan = xmm1;
427   if (CpuFeatures::IsSupported(SSE2)) {
428     CpuFeatures::Scope use_sse2(SSE2);
429     __ movdbl(the_hole_nan,
430               Operand::StaticVariable(canonical_the_hole_nan_reference));
431   }
432   __ jmp(&entry);
433 
434   // Call into runtime if GC is required.
435   __ bind(&gc_required);
436   // Restore registers before jumping into runtime.
437   __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
438   __ pop(ebx);
439   __ pop(eax);
440   __ jmp(fail);
441 
442   // Convert and copy elements
443   // esi: source FixedArray
444   __ bind(&loop);
445   __ mov(ebx, FieldOperand(esi, edi, times_2, FixedArray::kHeaderSize));
446   // ebx: current element from source
447   // edi: index of current element
448   __ JumpIfNotSmi(ebx, &convert_hole);
449 
450   // Normal smi, convert it to double and store.
451   __ SmiUntag(ebx);
452   if (CpuFeatures::IsSupported(SSE2)) {
453     CpuFeatures::Scope fscope(SSE2);
454     __ cvtsi2sd(xmm0, ebx);
455     __ movdbl(FieldOperand(eax, edi, times_4, FixedDoubleArray::kHeaderSize),
456               xmm0);
457   } else {
458     __ push(ebx);
459     __ fild_s(Operand(esp, 0));
460     __ pop(ebx);
461     __ fstp_d(FieldOperand(eax, edi, times_4, FixedDoubleArray::kHeaderSize));
462   }
463   __ jmp(&entry);
464 
465   // Found hole, store hole_nan_as_double instead.
466   __ bind(&convert_hole);
467 
468   if (FLAG_debug_code) {
469     __ cmp(ebx, masm->isolate()->factory()->the_hole_value());
470     __ Assert(equal, "object found in smi-only array");
471   }
472 
473   if (CpuFeatures::IsSupported(SSE2)) {
474     CpuFeatures::Scope use_sse2(SSE2);
475     __ movdbl(FieldOperand(eax, edi, times_4, FixedDoubleArray::kHeaderSize),
476               the_hole_nan);
477   } else {
478     __ fld_d(Operand::StaticVariable(canonical_the_hole_nan_reference));
479     __ fstp_d(FieldOperand(eax, edi, times_4, FixedDoubleArray::kHeaderSize));
480   }
481 
482   __ bind(&entry);
483   __ sub(edi, Immediate(Smi::FromInt(1)));
484   __ j(not_sign, &loop);
485 
486   __ pop(ebx);
487   __ pop(eax);
488 
489   // Restore esi.
490   __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
491 
492   __ bind(&only_change_map);
493   // eax: value
494   // ebx: target map
495   // Set transitioned map.
496   __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx);
497   __ RecordWriteField(edx,
498                       HeapObject::kMapOffset,
499                       ebx,
500                       edi,
501                       kDontSaveFPRegs,
502                       OMIT_REMEMBERED_SET,
503                       OMIT_SMI_CHECK);
504 }
505 
506 
GenerateDoubleToObject(MacroAssembler * masm,Label * fail)507 void ElementsTransitionGenerator::GenerateDoubleToObject(
508     MacroAssembler* masm, Label* fail) {
509   // ----------- S t a t e -------------
510   //  -- eax    : value
511   //  -- ebx    : target map
512   //  -- ecx    : key
513   //  -- edx    : receiver
514   //  -- esp[0] : return address
515   // -----------------------------------
516   Label loop, entry, convert_hole, gc_required, only_change_map, success;
517 
518   // Check for empty arrays, which only require a map transition and no changes
519   // to the backing store.
520   __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
521   __ cmp(edi, Immediate(masm->isolate()->factory()->empty_fixed_array()));
522   __ j(equal, &only_change_map);
523 
524   __ push(eax);
525   __ push(edx);
526   __ push(ebx);
527 
528   __ mov(ebx, FieldOperand(edi, FixedDoubleArray::kLengthOffset));
529 
530   // Allocate new FixedArray.
531   // ebx: length of source FixedDoubleArray (smi-tagged)
532   __ lea(edi, Operand(ebx, times_2, FixedArray::kHeaderSize));
533   __ AllocateInNewSpace(edi, eax, esi, no_reg, &gc_required, TAG_OBJECT);
534 
535   // eax: destination FixedArray
536   // ebx: number of elements
537   __ mov(FieldOperand(eax, HeapObject::kMapOffset),
538          Immediate(masm->isolate()->factory()->fixed_array_map()));
539   __ mov(FieldOperand(eax, FixedArray::kLengthOffset), ebx);
540   __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
541 
542   __ jmp(&entry);
543 
544   // ebx: target map
545   // edx: receiver
546   // Set transitioned map.
547   __ bind(&only_change_map);
548   __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx);
549   __ RecordWriteField(edx,
550                       HeapObject::kMapOffset,
551                       ebx,
552                       edi,
553                       kDontSaveFPRegs,
554                       OMIT_REMEMBERED_SET,
555                       OMIT_SMI_CHECK);
556   __ jmp(&success);
557 
558   // Call into runtime if GC is required.
559   __ bind(&gc_required);
560   __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
561   __ pop(ebx);
562   __ pop(edx);
563   __ pop(eax);
564   __ jmp(fail);
565 
566   // Box doubles into heap numbers.
567   // edi: source FixedDoubleArray
568   // eax: destination FixedArray
569   __ bind(&loop);
570   // ebx: index of current element (smi-tagged)
571   uint32_t offset = FixedDoubleArray::kHeaderSize + sizeof(kHoleNanLower32);
572   __ cmp(FieldOperand(edi, ebx, times_4, offset), Immediate(kHoleNanUpper32));
573   __ j(equal, &convert_hole);
574 
575   // Non-hole double, copy value into a heap number.
576   __ AllocateHeapNumber(edx, esi, no_reg, &gc_required);
577   // edx: new heap number
578   if (CpuFeatures::IsSupported(SSE2)) {
579     CpuFeatures::Scope fscope(SSE2);
580     __ movdbl(xmm0,
581               FieldOperand(edi, ebx, times_4, FixedDoubleArray::kHeaderSize));
582     __ movdbl(FieldOperand(edx, HeapNumber::kValueOffset), xmm0);
583   } else {
584     __ mov(esi, FieldOperand(edi, ebx, times_4, FixedDoubleArray::kHeaderSize));
585     __ mov(FieldOperand(edx, HeapNumber::kValueOffset), esi);
586     __ mov(esi, FieldOperand(edi, ebx, times_4, offset));
587     __ mov(FieldOperand(edx, HeapNumber::kValueOffset + kPointerSize), esi);
588   }
589   __ mov(FieldOperand(eax, ebx, times_2, FixedArray::kHeaderSize), edx);
590   __ mov(esi, ebx);
591   __ RecordWriteArray(eax,
592                       edx,
593                       esi,
594                       kDontSaveFPRegs,
595                       EMIT_REMEMBERED_SET,
596                       OMIT_SMI_CHECK);
597   __ jmp(&entry, Label::kNear);
598 
599   // Replace the-hole NaN with the-hole pointer.
600   __ bind(&convert_hole);
601   __ mov(FieldOperand(eax, ebx, times_2, FixedArray::kHeaderSize),
602          masm->isolate()->factory()->the_hole_value());
603 
604   __ bind(&entry);
605   __ sub(ebx, Immediate(Smi::FromInt(1)));
606   __ j(not_sign, &loop);
607 
608   __ pop(ebx);
609   __ pop(edx);
610   // ebx: target map
611   // edx: receiver
612   // Set transitioned map.
613   __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx);
614   __ RecordWriteField(edx,
615                       HeapObject::kMapOffset,
616                       ebx,
617                       edi,
618                       kDontSaveFPRegs,
619                       OMIT_REMEMBERED_SET,
620                       OMIT_SMI_CHECK);
621   // Replace receiver's backing store with newly created and filled FixedArray.
622   __ mov(FieldOperand(edx, JSObject::kElementsOffset), eax);
623   __ RecordWriteField(edx,
624                       JSObject::kElementsOffset,
625                       eax,
626                       edi,
627                       kDontSaveFPRegs,
628                       EMIT_REMEMBERED_SET,
629                       OMIT_SMI_CHECK);
630 
631   // Restore registers.
632   __ pop(eax);
633   __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
634 
635   __ bind(&success);
636 }
637 
638 
Generate(MacroAssembler * masm,Factory * factory,Register string,Register index,Register result,Label * call_runtime)639 void StringCharLoadGenerator::Generate(MacroAssembler* masm,
640                                        Factory* factory,
641                                        Register string,
642                                        Register index,
643                                        Register result,
644                                        Label* call_runtime) {
645   // Fetch the instance type of the receiver into result register.
646   __ mov(result, FieldOperand(string, HeapObject::kMapOffset));
647   __ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
648 
649   // We need special handling for indirect strings.
650   Label check_sequential;
651   __ test(result, Immediate(kIsIndirectStringMask));
652   __ j(zero, &check_sequential, Label::kNear);
653 
654   // Dispatch on the indirect string shape: slice or cons.
655   Label cons_string;
656   __ test(result, Immediate(kSlicedNotConsMask));
657   __ j(zero, &cons_string, Label::kNear);
658 
659   // Handle slices.
660   Label indirect_string_loaded;
661   __ mov(result, FieldOperand(string, SlicedString::kOffsetOffset));
662   __ SmiUntag(result);
663   __ add(index, result);
664   __ mov(string, FieldOperand(string, SlicedString::kParentOffset));
665   __ jmp(&indirect_string_loaded, Label::kNear);
666 
667   // Handle cons strings.
668   // Check whether the right hand side is the empty string (i.e. if
669   // this is really a flat string in a cons string). If that is not
670   // the case we would rather go to the runtime system now to flatten
671   // the string.
672   __ bind(&cons_string);
673   __ cmp(FieldOperand(string, ConsString::kSecondOffset),
674          Immediate(factory->empty_string()));
675   __ j(not_equal, call_runtime);
676   __ mov(string, FieldOperand(string, ConsString::kFirstOffset));
677 
678   __ bind(&indirect_string_loaded);
679   __ mov(result, FieldOperand(string, HeapObject::kMapOffset));
680   __ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
681 
682   // Distinguish sequential and external strings. Only these two string
683   // representations can reach here (slices and flat cons strings have been
684   // reduced to the underlying sequential or external string).
685   Label seq_string;
686   __ bind(&check_sequential);
687   STATIC_ASSERT(kSeqStringTag == 0);
688   __ test(result, Immediate(kStringRepresentationMask));
689   __ j(zero, &seq_string, Label::kNear);
690 
691   // Handle external strings.
692   Label ascii_external, done;
693   if (FLAG_debug_code) {
694     // Assert that we do not have a cons or slice (indirect strings) here.
695     // Sequential strings have already been ruled out.
696     __ test(result, Immediate(kIsIndirectStringMask));
697     __ Assert(zero, "external string expected, but not found");
698   }
699   // Rule out short external strings.
700   STATIC_CHECK(kShortExternalStringTag != 0);
701   __ test_b(result, kShortExternalStringMask);
702   __ j(not_zero, call_runtime);
703   // Check encoding.
704   STATIC_ASSERT(kTwoByteStringTag == 0);
705   __ test_b(result, kStringEncodingMask);
706   __ mov(result, FieldOperand(string, ExternalString::kResourceDataOffset));
707   __ j(not_equal, &ascii_external, Label::kNear);
708   // Two-byte string.
709   __ movzx_w(result, Operand(result, index, times_2, 0));
710   __ jmp(&done, Label::kNear);
711   __ bind(&ascii_external);
712   // Ascii string.
713   __ movzx_b(result, Operand(result, index, times_1, 0));
714   __ jmp(&done, Label::kNear);
715 
716   // Dispatch on the encoding: ASCII or two-byte.
717   Label ascii;
718   __ bind(&seq_string);
719   STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0);
720   STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
721   __ test(result, Immediate(kStringEncodingMask));
722   __ j(not_zero, &ascii, Label::kNear);
723 
724   // Two-byte string.
725   // Load the two-byte character code into the result register.
726   __ movzx_w(result, FieldOperand(string,
727                                   index,
728                                   times_2,
729                                   SeqTwoByteString::kHeaderSize));
730   __ jmp(&done, Label::kNear);
731 
732   // Ascii string.
733   // Load the byte into the result register.
734   __ bind(&ascii);
735   __ movzx_b(result, FieldOperand(string,
736                                   index,
737                                   times_1,
738                                   SeqAsciiString::kHeaderSize));
739   __ bind(&done);
740 }
741 
742 #undef __
743 
744 } }  // namespace v8::internal
745 
746 #endif  // V8_TARGET_ARCH_IA32
747