1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/ia32/codegen-ia32.h"
6 
7 #if V8_TARGET_ARCH_IA32
8 
9 #include "src/codegen.h"
10 #include "src/heap/heap.h"
11 #include "src/macro-assembler.h"
12 
13 namespace v8 {
14 namespace internal {
15 
16 
17 // -------------------------------------------------------------------------
18 // Platform-specific RuntimeCallHelper functions.
19 
BeforeCall(MacroAssembler * masm) const20 void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
21   masm->EnterFrame(StackFrame::INTERNAL);
22   DCHECK(!masm->has_frame());
23   masm->set_has_frame(true);
24 }
25 
26 
AfterCall(MacroAssembler * masm) const27 void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
28   masm->LeaveFrame(StackFrame::INTERNAL);
29   DCHECK(masm->has_frame());
30   masm->set_has_frame(false);
31 }
32 
33 
34 #define __ masm.
35 
36 
CreateSqrtFunction(Isolate * isolate)37 UnaryMathFunctionWithIsolate CreateSqrtFunction(Isolate* isolate) {
38   size_t actual_size;
39   // Allocate buffer in executable space.
40   byte* buffer =
41       static_cast<byte*>(base::OS::Allocate(1 * KB, &actual_size, true));
42   if (buffer == nullptr) return nullptr;
43   MacroAssembler masm(isolate, buffer, static_cast<int>(actual_size),
44                       CodeObjectRequired::kNo);
45   // esp[1 * kPointerSize]: raw double input
46   // esp[0 * kPointerSize]: return address
47   // Move double input into registers.
48   {
49     __ movsd(xmm0, Operand(esp, 1 * kPointerSize));
50     __ sqrtsd(xmm0, xmm0);
51     __ movsd(Operand(esp, 1 * kPointerSize), xmm0);
52     // Load result into floating point register as return value.
53     __ fld_d(Operand(esp, 1 * kPointerSize));
54     __ Ret();
55   }
56 
57   CodeDesc desc;
58   masm.GetCode(&desc);
59   DCHECK(!RelocInfo::RequiresRelocation(desc));
60 
61   Assembler::FlushICache(isolate, buffer, actual_size);
62   base::OS::ProtectCode(buffer, actual_size);
63   return FUNCTION_CAST<UnaryMathFunctionWithIsolate>(buffer);
64 }
65 
66 
67 // Helper functions for CreateMemMoveFunction.
68 #undef __
69 #define __ ACCESS_MASM(masm)
70 
71 enum Direction { FORWARD, BACKWARD };
72 enum Alignment { MOVE_ALIGNED, MOVE_UNALIGNED };
73 
74 // Expects registers:
75 // esi - source, aligned if alignment == ALIGNED
76 // edi - destination, always aligned
77 // ecx - count (copy size in bytes)
78 // edx - loop count (number of 64 byte chunks)
MemMoveEmitMainLoop(MacroAssembler * masm,Label * move_last_15,Direction direction,Alignment alignment)79 void MemMoveEmitMainLoop(MacroAssembler* masm,
80                          Label* move_last_15,
81                          Direction direction,
82                          Alignment alignment) {
83   Register src = esi;
84   Register dst = edi;
85   Register count = ecx;
86   Register loop_count = edx;
87   Label loop, move_last_31, move_last_63;
88   __ cmp(loop_count, 0);
89   __ j(equal, &move_last_63);
90   __ bind(&loop);
91   // Main loop. Copy in 64 byte chunks.
92   if (direction == BACKWARD) __ sub(src, Immediate(0x40));
93   __ movdq(alignment == MOVE_ALIGNED, xmm0, Operand(src, 0x00));
94   __ movdq(alignment == MOVE_ALIGNED, xmm1, Operand(src, 0x10));
95   __ movdq(alignment == MOVE_ALIGNED, xmm2, Operand(src, 0x20));
96   __ movdq(alignment == MOVE_ALIGNED, xmm3, Operand(src, 0x30));
97   if (direction == FORWARD) __ add(src, Immediate(0x40));
98   if (direction == BACKWARD) __ sub(dst, Immediate(0x40));
99   __ movdqa(Operand(dst, 0x00), xmm0);
100   __ movdqa(Operand(dst, 0x10), xmm1);
101   __ movdqa(Operand(dst, 0x20), xmm2);
102   __ movdqa(Operand(dst, 0x30), xmm3);
103   if (direction == FORWARD) __ add(dst, Immediate(0x40));
104   __ dec(loop_count);
105   __ j(not_zero, &loop);
106   // At most 63 bytes left to copy.
107   __ bind(&move_last_63);
108   __ test(count, Immediate(0x20));
109   __ j(zero, &move_last_31);
110   if (direction == BACKWARD) __ sub(src, Immediate(0x20));
111   __ movdq(alignment == MOVE_ALIGNED, xmm0, Operand(src, 0x00));
112   __ movdq(alignment == MOVE_ALIGNED, xmm1, Operand(src, 0x10));
113   if (direction == FORWARD) __ add(src, Immediate(0x20));
114   if (direction == BACKWARD) __ sub(dst, Immediate(0x20));
115   __ movdqa(Operand(dst, 0x00), xmm0);
116   __ movdqa(Operand(dst, 0x10), xmm1);
117   if (direction == FORWARD) __ add(dst, Immediate(0x20));
118   // At most 31 bytes left to copy.
119   __ bind(&move_last_31);
120   __ test(count, Immediate(0x10));
121   __ j(zero, move_last_15);
122   if (direction == BACKWARD) __ sub(src, Immediate(0x10));
123   __ movdq(alignment == MOVE_ALIGNED, xmm0, Operand(src, 0));
124   if (direction == FORWARD) __ add(src, Immediate(0x10));
125   if (direction == BACKWARD) __ sub(dst, Immediate(0x10));
126   __ movdqa(Operand(dst, 0), xmm0);
127   if (direction == FORWARD) __ add(dst, Immediate(0x10));
128 }
129 
130 
MemMoveEmitPopAndReturn(MacroAssembler * masm)131 void MemMoveEmitPopAndReturn(MacroAssembler* masm) {
132   __ pop(esi);
133   __ pop(edi);
134   __ ret(0);
135 }
136 
137 
138 #undef __
139 #define __ masm.
140 
141 
142 class LabelConverter {
143  public:
LabelConverter(byte * buffer)144   explicit LabelConverter(byte* buffer) : buffer_(buffer) {}
address(Label * l) const145   int32_t address(Label* l) const {
146     return reinterpret_cast<int32_t>(buffer_) + l->pos();
147   }
148  private:
149   byte* buffer_;
150 };
151 
152 
CreateMemMoveFunction(Isolate * isolate)153 MemMoveFunction CreateMemMoveFunction(Isolate* isolate) {
154   size_t actual_size;
155   // Allocate buffer in executable space.
156   byte* buffer =
157       static_cast<byte*>(base::OS::Allocate(1 * KB, &actual_size, true));
158   if (buffer == nullptr) return nullptr;
159   MacroAssembler masm(isolate, buffer, static_cast<int>(actual_size),
160                       CodeObjectRequired::kNo);
161   LabelConverter conv(buffer);
162 
163   // Generated code is put into a fixed, unmovable buffer, and not into
164   // the V8 heap. We can't, and don't, refer to any relocatable addresses
165   // (e.g. the JavaScript nan-object).
166 
167   // 32-bit C declaration function calls pass arguments on stack.
168 
169   // Stack layout:
170   // esp[12]: Third argument, size.
171   // esp[8]: Second argument, source pointer.
172   // esp[4]: First argument, destination pointer.
173   // esp[0]: return address
174 
175   const int kDestinationOffset = 1 * kPointerSize;
176   const int kSourceOffset = 2 * kPointerSize;
177   const int kSizeOffset = 3 * kPointerSize;
178 
179   // When copying up to this many bytes, use special "small" handlers.
180   const size_t kSmallCopySize = 8;
181   // When copying up to this many bytes, use special "medium" handlers.
182   const size_t kMediumCopySize = 63;
183   // When non-overlapping region of src and dst is less than this,
184   // use a more careful implementation (slightly slower).
185   const size_t kMinMoveDistance = 16;
186   // Note that these values are dictated by the implementation below,
187   // do not just change them and hope things will work!
188 
189   int stack_offset = 0;  // Update if we change the stack height.
190 
191   Label backward, backward_much_overlap;
192   Label forward_much_overlap, small_size, medium_size, pop_and_return;
193   __ push(edi);
194   __ push(esi);
195   stack_offset += 2 * kPointerSize;
196   Register dst = edi;
197   Register src = esi;
198   Register count = ecx;
199   Register loop_count = edx;
200   __ mov(dst, Operand(esp, stack_offset + kDestinationOffset));
201   __ mov(src, Operand(esp, stack_offset + kSourceOffset));
202   __ mov(count, Operand(esp, stack_offset + kSizeOffset));
203 
204   __ cmp(dst, src);
205   __ j(equal, &pop_and_return);
206 
207   __ prefetch(Operand(src, 0), 1);
208   __ cmp(count, kSmallCopySize);
209   __ j(below_equal, &small_size);
210   __ cmp(count, kMediumCopySize);
211   __ j(below_equal, &medium_size);
212   __ cmp(dst, src);
213   __ j(above, &backward);
214 
215   {
216     // |dst| is a lower address than |src|. Copy front-to-back.
217     Label unaligned_source, move_last_15, skip_last_move;
218     __ mov(eax, src);
219     __ sub(eax, dst);
220     __ cmp(eax, kMinMoveDistance);
221     __ j(below, &forward_much_overlap);
222     // Copy first 16 bytes.
223     __ movdqu(xmm0, Operand(src, 0));
224     __ movdqu(Operand(dst, 0), xmm0);
225     // Determine distance to alignment: 16 - (dst & 0xF).
226     __ mov(edx, dst);
227     __ and_(edx, 0xF);
228     __ neg(edx);
229     __ add(edx, Immediate(16));
230     __ add(dst, edx);
231     __ add(src, edx);
232     __ sub(count, edx);
233     // dst is now aligned. Main copy loop.
234     __ mov(loop_count, count);
235     __ shr(loop_count, 6);
236     // Check if src is also aligned.
237     __ test(src, Immediate(0xF));
238     __ j(not_zero, &unaligned_source);
239     // Copy loop for aligned source and destination.
240     MemMoveEmitMainLoop(&masm, &move_last_15, FORWARD, MOVE_ALIGNED);
241     // At most 15 bytes to copy. Copy 16 bytes at end of string.
242     __ bind(&move_last_15);
243     __ and_(count, 0xF);
244     __ j(zero, &skip_last_move, Label::kNear);
245     __ movdqu(xmm0, Operand(src, count, times_1, -0x10));
246     __ movdqu(Operand(dst, count, times_1, -0x10), xmm0);
247     __ bind(&skip_last_move);
248     MemMoveEmitPopAndReturn(&masm);
249 
250     // Copy loop for unaligned source and aligned destination.
251     __ bind(&unaligned_source);
252     MemMoveEmitMainLoop(&masm, &move_last_15, FORWARD, MOVE_UNALIGNED);
253     __ jmp(&move_last_15);
254 
255     // Less than kMinMoveDistance offset between dst and src.
256     Label loop_until_aligned, last_15_much_overlap;
257     __ bind(&loop_until_aligned);
258     __ mov_b(eax, Operand(src, 0));
259     __ inc(src);
260     __ mov_b(Operand(dst, 0), eax);
261     __ inc(dst);
262     __ dec(count);
263     __ bind(&forward_much_overlap);  // Entry point into this block.
264     __ test(dst, Immediate(0xF));
265     __ j(not_zero, &loop_until_aligned);
266     // dst is now aligned, src can't be. Main copy loop.
267     __ mov(loop_count, count);
268     __ shr(loop_count, 6);
269     MemMoveEmitMainLoop(&masm, &last_15_much_overlap,
270                         FORWARD, MOVE_UNALIGNED);
271     __ bind(&last_15_much_overlap);
272     __ and_(count, 0xF);
273     __ j(zero, &pop_and_return);
274     __ cmp(count, kSmallCopySize);
275     __ j(below_equal, &small_size);
276     __ jmp(&medium_size);
277   }
278 
279   {
280     // |dst| is a higher address than |src|. Copy backwards.
281     Label unaligned_source, move_first_15, skip_last_move;
282     __ bind(&backward);
283     // |dst| and |src| always point to the end of what's left to copy.
284     __ add(dst, count);
285     __ add(src, count);
286     __ mov(eax, dst);
287     __ sub(eax, src);
288     __ cmp(eax, kMinMoveDistance);
289     __ j(below, &backward_much_overlap);
290     // Copy last 16 bytes.
291     __ movdqu(xmm0, Operand(src, -0x10));
292     __ movdqu(Operand(dst, -0x10), xmm0);
293     // Find distance to alignment: dst & 0xF
294     __ mov(edx, dst);
295     __ and_(edx, 0xF);
296     __ sub(dst, edx);
297     __ sub(src, edx);
298     __ sub(count, edx);
299     // dst is now aligned. Main copy loop.
300     __ mov(loop_count, count);
301     __ shr(loop_count, 6);
302     // Check if src is also aligned.
303     __ test(src, Immediate(0xF));
304     __ j(not_zero, &unaligned_source);
305     // Copy loop for aligned source and destination.
306     MemMoveEmitMainLoop(&masm, &move_first_15, BACKWARD, MOVE_ALIGNED);
307     // At most 15 bytes to copy. Copy 16 bytes at beginning of string.
308     __ bind(&move_first_15);
309     __ and_(count, 0xF);
310     __ j(zero, &skip_last_move, Label::kNear);
311     __ sub(src, count);
312     __ sub(dst, count);
313     __ movdqu(xmm0, Operand(src, 0));
314     __ movdqu(Operand(dst, 0), xmm0);
315     __ bind(&skip_last_move);
316     MemMoveEmitPopAndReturn(&masm);
317 
318     // Copy loop for unaligned source and aligned destination.
319     __ bind(&unaligned_source);
320     MemMoveEmitMainLoop(&masm, &move_first_15, BACKWARD, MOVE_UNALIGNED);
321     __ jmp(&move_first_15);
322 
323     // Less than kMinMoveDistance offset between dst and src.
324     Label loop_until_aligned, first_15_much_overlap;
325     __ bind(&loop_until_aligned);
326     __ dec(src);
327     __ dec(dst);
328     __ mov_b(eax, Operand(src, 0));
329     __ mov_b(Operand(dst, 0), eax);
330     __ dec(count);
331     __ bind(&backward_much_overlap);  // Entry point into this block.
332     __ test(dst, Immediate(0xF));
333     __ j(not_zero, &loop_until_aligned);
334     // dst is now aligned, src can't be. Main copy loop.
335     __ mov(loop_count, count);
336     __ shr(loop_count, 6);
337     MemMoveEmitMainLoop(&masm, &first_15_much_overlap,
338                         BACKWARD, MOVE_UNALIGNED);
339     __ bind(&first_15_much_overlap);
340     __ and_(count, 0xF);
341     __ j(zero, &pop_and_return);
342     // Small/medium handlers expect dst/src to point to the beginning.
343     __ sub(dst, count);
344     __ sub(src, count);
345     __ cmp(count, kSmallCopySize);
346     __ j(below_equal, &small_size);
347     __ jmp(&medium_size);
348   }
349   {
350     // Special handlers for 9 <= copy_size < 64. No assumptions about
351     // alignment or move distance, so all reads must be unaligned and
352     // must happen before any writes.
353     Label medium_handlers, f9_16, f17_32, f33_48, f49_63;
354 
355     __ bind(&f9_16);
356     __ movsd(xmm0, Operand(src, 0));
357     __ movsd(xmm1, Operand(src, count, times_1, -8));
358     __ movsd(Operand(dst, 0), xmm0);
359     __ movsd(Operand(dst, count, times_1, -8), xmm1);
360     MemMoveEmitPopAndReturn(&masm);
361 
362     __ bind(&f17_32);
363     __ movdqu(xmm0, Operand(src, 0));
364     __ movdqu(xmm1, Operand(src, count, times_1, -0x10));
365     __ movdqu(Operand(dst, 0x00), xmm0);
366     __ movdqu(Operand(dst, count, times_1, -0x10), xmm1);
367     MemMoveEmitPopAndReturn(&masm);
368 
369     __ bind(&f33_48);
370     __ movdqu(xmm0, Operand(src, 0x00));
371     __ movdqu(xmm1, Operand(src, 0x10));
372     __ movdqu(xmm2, Operand(src, count, times_1, -0x10));
373     __ movdqu(Operand(dst, 0x00), xmm0);
374     __ movdqu(Operand(dst, 0x10), xmm1);
375     __ movdqu(Operand(dst, count, times_1, -0x10), xmm2);
376     MemMoveEmitPopAndReturn(&masm);
377 
378     __ bind(&f49_63);
379     __ movdqu(xmm0, Operand(src, 0x00));
380     __ movdqu(xmm1, Operand(src, 0x10));
381     __ movdqu(xmm2, Operand(src, 0x20));
382     __ movdqu(xmm3, Operand(src, count, times_1, -0x10));
383     __ movdqu(Operand(dst, 0x00), xmm0);
384     __ movdqu(Operand(dst, 0x10), xmm1);
385     __ movdqu(Operand(dst, 0x20), xmm2);
386     __ movdqu(Operand(dst, count, times_1, -0x10), xmm3);
387     MemMoveEmitPopAndReturn(&masm);
388 
389     __ bind(&medium_handlers);
390     __ dd(conv.address(&f9_16));
391     __ dd(conv.address(&f17_32));
392     __ dd(conv.address(&f33_48));
393     __ dd(conv.address(&f49_63));
394 
395     __ bind(&medium_size);  // Entry point into this block.
396     __ mov(eax, count);
397     __ dec(eax);
398     __ shr(eax, 4);
399     if (FLAG_debug_code) {
400       Label ok;
401       __ cmp(eax, 3);
402       __ j(below_equal, &ok);
403       __ int3();
404       __ bind(&ok);
405     }
406     __ mov(eax, Operand(eax, times_4, conv.address(&medium_handlers)));
407     __ jmp(eax);
408   }
409   {
410     // Specialized copiers for copy_size <= 8 bytes.
411     Label small_handlers, f0, f1, f2, f3, f4, f5_8;
412     __ bind(&f0);
413     MemMoveEmitPopAndReturn(&masm);
414 
415     __ bind(&f1);
416     __ mov_b(eax, Operand(src, 0));
417     __ mov_b(Operand(dst, 0), eax);
418     MemMoveEmitPopAndReturn(&masm);
419 
420     __ bind(&f2);
421     __ mov_w(eax, Operand(src, 0));
422     __ mov_w(Operand(dst, 0), eax);
423     MemMoveEmitPopAndReturn(&masm);
424 
425     __ bind(&f3);
426     __ mov_w(eax, Operand(src, 0));
427     __ mov_b(edx, Operand(src, 2));
428     __ mov_w(Operand(dst, 0), eax);
429     __ mov_b(Operand(dst, 2), edx);
430     MemMoveEmitPopAndReturn(&masm);
431 
432     __ bind(&f4);
433     __ mov(eax, Operand(src, 0));
434     __ mov(Operand(dst, 0), eax);
435     MemMoveEmitPopAndReturn(&masm);
436 
437     __ bind(&f5_8);
438     __ mov(eax, Operand(src, 0));
439     __ mov(edx, Operand(src, count, times_1, -4));
440     __ mov(Operand(dst, 0), eax);
441     __ mov(Operand(dst, count, times_1, -4), edx);
442     MemMoveEmitPopAndReturn(&masm);
443 
444     __ bind(&small_handlers);
445     __ dd(conv.address(&f0));
446     __ dd(conv.address(&f1));
447     __ dd(conv.address(&f2));
448     __ dd(conv.address(&f3));
449     __ dd(conv.address(&f4));
450     __ dd(conv.address(&f5_8));
451     __ dd(conv.address(&f5_8));
452     __ dd(conv.address(&f5_8));
453     __ dd(conv.address(&f5_8));
454 
455     __ bind(&small_size);  // Entry point into this block.
456     if (FLAG_debug_code) {
457       Label ok;
458       __ cmp(count, 8);
459       __ j(below_equal, &ok);
460       __ int3();
461       __ bind(&ok);
462     }
463     __ mov(eax, Operand(count, times_4, conv.address(&small_handlers)));
464     __ jmp(eax);
465   }
466 
467   __ bind(&pop_and_return);
468   MemMoveEmitPopAndReturn(&masm);
469 
470   CodeDesc desc;
471   masm.GetCode(&desc);
472   DCHECK(!RelocInfo::RequiresRelocation(desc));
473   Assembler::FlushICache(isolate, buffer, actual_size);
474   base::OS::ProtectCode(buffer, actual_size);
475   // TODO(jkummerow): It would be nice to register this code creation event
476   // with the PROFILE / GDBJIT system.
477   return FUNCTION_CAST<MemMoveFunction>(buffer);
478 }
479 
480 
481 #undef __
482 
483 // -------------------------------------------------------------------------
484 // Code generators
485 
486 #define __ ACCESS_MASM(masm)
487 
488 
GenerateMapChangeElementsTransition(MacroAssembler * masm,Register receiver,Register key,Register value,Register target_map,AllocationSiteMode mode,Label * allocation_memento_found)489 void ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
490     MacroAssembler* masm,
491     Register receiver,
492     Register key,
493     Register value,
494     Register target_map,
495     AllocationSiteMode mode,
496     Label* allocation_memento_found) {
497   Register scratch = edi;
498   DCHECK(!AreAliased(receiver, key, value, target_map, scratch));
499 
500   if (mode == TRACK_ALLOCATION_SITE) {
501     DCHECK(allocation_memento_found != NULL);
502     __ JumpIfJSArrayHasAllocationMemento(
503         receiver, scratch, allocation_memento_found);
504   }
505 
506   // Set transitioned map.
507   __ mov(FieldOperand(receiver, HeapObject::kMapOffset), target_map);
508   __ RecordWriteField(receiver,
509                       HeapObject::kMapOffset,
510                       target_map,
511                       scratch,
512                       kDontSaveFPRegs,
513                       EMIT_REMEMBERED_SET,
514                       OMIT_SMI_CHECK);
515 }
516 
517 
GenerateSmiToDouble(MacroAssembler * masm,Register receiver,Register key,Register value,Register target_map,AllocationSiteMode mode,Label * fail)518 void ElementsTransitionGenerator::GenerateSmiToDouble(
519     MacroAssembler* masm,
520     Register receiver,
521     Register key,
522     Register value,
523     Register target_map,
524     AllocationSiteMode mode,
525     Label* fail) {
526   // Return address is on the stack.
527   DCHECK(receiver.is(edx));
528   DCHECK(key.is(ecx));
529   DCHECK(value.is(eax));
530   DCHECK(target_map.is(ebx));
531 
532   Label loop, entry, convert_hole, gc_required, only_change_map;
533 
534   if (mode == TRACK_ALLOCATION_SITE) {
535     __ JumpIfJSArrayHasAllocationMemento(edx, edi, fail);
536   }
537 
538   // Check for empty arrays, which only require a map transition and no changes
539   // to the backing store.
540   __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
541   __ cmp(edi, Immediate(masm->isolate()->factory()->empty_fixed_array()));
542   __ j(equal, &only_change_map);
543 
544   __ push(eax);
545   __ push(ebx);
546   __ push(esi);
547 
548   __ mov(edi, FieldOperand(edi, FixedArray::kLengthOffset));
549 
550   // Allocate new FixedDoubleArray.
551   // edx: receiver
552   // edi: length of source FixedArray (smi-tagged)
553   AllocationFlags flags = static_cast<AllocationFlags>(DOUBLE_ALIGNMENT);
554   __ Allocate(FixedDoubleArray::kHeaderSize, times_8, edi,
555               REGISTER_VALUE_IS_SMI, eax, ebx, no_reg, &gc_required, flags);
556 
557   // eax: destination FixedDoubleArray
558   // edi: number of elements
559   // edx: receiver
560   __ mov(FieldOperand(eax, HeapObject::kMapOffset),
561          Immediate(masm->isolate()->factory()->fixed_double_array_map()));
562   __ mov(FieldOperand(eax, FixedDoubleArray::kLengthOffset), edi);
563   __ mov(esi, FieldOperand(edx, JSObject::kElementsOffset));
564   // Replace receiver's backing store with newly created FixedDoubleArray.
565   __ mov(FieldOperand(edx, JSObject::kElementsOffset), eax);
566   __ mov(ebx, eax);
567   __ RecordWriteField(edx,
568                       JSObject::kElementsOffset,
569                       ebx,
570                       edi,
571                       kDontSaveFPRegs,
572                       EMIT_REMEMBERED_SET,
573                       OMIT_SMI_CHECK);
574 
575   __ mov(edi, FieldOperand(esi, FixedArray::kLengthOffset));
576 
577   // Prepare for conversion loop.
578   ExternalReference canonical_the_hole_nan_reference =
579       ExternalReference::address_of_the_hole_nan();
580   XMMRegister the_hole_nan = xmm1;
581   __ movsd(the_hole_nan,
582            Operand::StaticVariable(canonical_the_hole_nan_reference));
583   __ jmp(&entry);
584 
585   // Call into runtime if GC is required.
586   __ bind(&gc_required);
587 
588   // Restore registers before jumping into runtime.
589   __ pop(esi);
590   __ pop(ebx);
591   __ pop(eax);
592   __ jmp(fail);
593 
594   // Convert and copy elements
595   // esi: source FixedArray
596   __ bind(&loop);
597   __ mov(ebx, FieldOperand(esi, edi, times_2, FixedArray::kHeaderSize));
598   // ebx: current element from source
599   // edi: index of current element
600   __ JumpIfNotSmi(ebx, &convert_hole);
601 
602   // Normal smi, convert it to double and store.
603   __ SmiUntag(ebx);
604   __ Cvtsi2sd(xmm0, ebx);
605   __ movsd(FieldOperand(eax, edi, times_4, FixedDoubleArray::kHeaderSize),
606            xmm0);
607   __ jmp(&entry);
608 
609   // Found hole, store hole_nan_as_double instead.
610   __ bind(&convert_hole);
611 
612   if (FLAG_debug_code) {
613     __ cmp(ebx, masm->isolate()->factory()->the_hole_value());
614     __ Assert(equal, kObjectFoundInSmiOnlyArray);
615   }
616 
617   __ movsd(FieldOperand(eax, edi, times_4, FixedDoubleArray::kHeaderSize),
618            the_hole_nan);
619 
620   __ bind(&entry);
621   __ sub(edi, Immediate(Smi::FromInt(1)));
622   __ j(not_sign, &loop);
623 
624   // Restore registers.
625   __ pop(esi);
626   __ pop(ebx);
627   __ pop(eax);
628 
629   __ bind(&only_change_map);
630   // eax: value
631   // ebx: target map
632   // Set transitioned map.
633   __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx);
634   __ RecordWriteField(edx,
635                       HeapObject::kMapOffset,
636                       ebx,
637                       edi,
638                       kDontSaveFPRegs,
639                       OMIT_REMEMBERED_SET,
640                       OMIT_SMI_CHECK);
641 }
642 
643 
GenerateDoubleToObject(MacroAssembler * masm,Register receiver,Register key,Register value,Register target_map,AllocationSiteMode mode,Label * fail)644 void ElementsTransitionGenerator::GenerateDoubleToObject(
645     MacroAssembler* masm,
646     Register receiver,
647     Register key,
648     Register value,
649     Register target_map,
650     AllocationSiteMode mode,
651     Label* fail) {
652   // Return address is on the stack.
653   DCHECK(receiver.is(edx));
654   DCHECK(key.is(ecx));
655   DCHECK(value.is(eax));
656   DCHECK(target_map.is(ebx));
657 
658   Label loop, entry, convert_hole, gc_required, only_change_map, success;
659 
660   if (mode == TRACK_ALLOCATION_SITE) {
661     __ JumpIfJSArrayHasAllocationMemento(edx, edi, fail);
662   }
663 
664   // Check for empty arrays, which only require a map transition and no changes
665   // to the backing store.
666   __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
667   __ cmp(edi, Immediate(masm->isolate()->factory()->empty_fixed_array()));
668   __ j(equal, &only_change_map);
669 
670   __ push(esi);
671   __ push(eax);
672   __ push(edx);
673   __ push(ebx);
674 
675   __ mov(ebx, FieldOperand(edi, FixedDoubleArray::kLengthOffset));
676 
677   // Allocate new FixedArray.
678   // ebx: length of source FixedDoubleArray (smi-tagged)
679   __ lea(edi, Operand(ebx, times_2, FixedArray::kHeaderSize));
680   __ Allocate(edi, eax, esi, no_reg, &gc_required, NO_ALLOCATION_FLAGS);
681 
682   // eax: destination FixedArray
683   // ebx: number of elements
684   __ mov(FieldOperand(eax, HeapObject::kMapOffset),
685          Immediate(masm->isolate()->factory()->fixed_array_map()));
686   __ mov(FieldOperand(eax, FixedArray::kLengthOffset), ebx);
687   __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
688 
689   // Allocating heap numbers in the loop below can fail and cause a jump to
690   // gc_required. We can't leave a partly initialized FixedArray behind,
691   // so pessimistically fill it with holes now.
692   Label initialization_loop, initialization_loop_entry;
693   __ jmp(&initialization_loop_entry, Label::kNear);
694   __ bind(&initialization_loop);
695   __ mov(FieldOperand(eax, ebx, times_2, FixedArray::kHeaderSize),
696          masm->isolate()->factory()->the_hole_value());
697   __ bind(&initialization_loop_entry);
698   __ sub(ebx, Immediate(Smi::FromInt(1)));
699   __ j(not_sign, &initialization_loop);
700 
701   __ mov(ebx, FieldOperand(edi, FixedDoubleArray::kLengthOffset));
702   __ jmp(&entry);
703 
704   // ebx: target map
705   // edx: receiver
706   // Set transitioned map.
707   __ bind(&only_change_map);
708   __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx);
709   __ RecordWriteField(edx,
710                       HeapObject::kMapOffset,
711                       ebx,
712                       edi,
713                       kDontSaveFPRegs,
714                       OMIT_REMEMBERED_SET,
715                       OMIT_SMI_CHECK);
716   __ jmp(&success);
717 
718   // Call into runtime if GC is required.
719   __ bind(&gc_required);
720   __ pop(ebx);
721   __ pop(edx);
722   __ pop(eax);
723   __ pop(esi);
724   __ jmp(fail);
725 
726   // Box doubles into heap numbers.
727   // edi: source FixedDoubleArray
728   // eax: destination FixedArray
729   __ bind(&loop);
730   // ebx: index of current element (smi-tagged)
731   uint32_t offset = FixedDoubleArray::kHeaderSize + sizeof(kHoleNanLower32);
732   __ cmp(FieldOperand(edi, ebx, times_4, offset), Immediate(kHoleNanUpper32));
733   __ j(equal, &convert_hole);
734 
735   // Non-hole double, copy value into a heap number.
736   __ AllocateHeapNumber(edx, esi, no_reg, &gc_required);
737   // edx: new heap number
738   __ movsd(xmm0,
739            FieldOperand(edi, ebx, times_4, FixedDoubleArray::kHeaderSize));
740   __ movsd(FieldOperand(edx, HeapNumber::kValueOffset), xmm0);
741   __ mov(FieldOperand(eax, ebx, times_2, FixedArray::kHeaderSize), edx);
742   __ mov(esi, ebx);
743   __ RecordWriteArray(eax,
744                       edx,
745                       esi,
746                       kDontSaveFPRegs,
747                       EMIT_REMEMBERED_SET,
748                       OMIT_SMI_CHECK);
749   __ jmp(&entry, Label::kNear);
750 
751   // Replace the-hole NaN with the-hole pointer.
752   __ bind(&convert_hole);
753   __ mov(FieldOperand(eax, ebx, times_2, FixedArray::kHeaderSize),
754          masm->isolate()->factory()->the_hole_value());
755 
756   __ bind(&entry);
757   __ sub(ebx, Immediate(Smi::FromInt(1)));
758   __ j(not_sign, &loop);
759 
760   __ pop(ebx);
761   __ pop(edx);
762   // ebx: target map
763   // edx: receiver
764   // Set transitioned map.
765   __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx);
766   __ RecordWriteField(edx,
767                       HeapObject::kMapOffset,
768                       ebx,
769                       edi,
770                       kDontSaveFPRegs,
771                       OMIT_REMEMBERED_SET,
772                       OMIT_SMI_CHECK);
773   // Replace receiver's backing store with newly created and filled FixedArray.
774   __ mov(FieldOperand(edx, JSObject::kElementsOffset), eax);
775   __ RecordWriteField(edx,
776                       JSObject::kElementsOffset,
777                       eax,
778                       edi,
779                       kDontSaveFPRegs,
780                       EMIT_REMEMBERED_SET,
781                       OMIT_SMI_CHECK);
782 
783   // Restore registers.
784   __ pop(eax);
785   __ pop(esi);
786 
787   __ bind(&success);
788 }
789 
790 
Generate(MacroAssembler * masm,Factory * factory,Register string,Register index,Register result,Label * call_runtime)791 void StringCharLoadGenerator::Generate(MacroAssembler* masm,
792                                        Factory* factory,
793                                        Register string,
794                                        Register index,
795                                        Register result,
796                                        Label* call_runtime) {
797   // Fetch the instance type of the receiver into result register.
798   __ mov(result, FieldOperand(string, HeapObject::kMapOffset));
799   __ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
800 
801   // We need special handling for indirect strings.
802   Label check_sequential;
803   __ test(result, Immediate(kIsIndirectStringMask));
804   __ j(zero, &check_sequential, Label::kNear);
805 
806   // Dispatch on the indirect string shape: slice or cons.
807   Label cons_string;
808   __ test(result, Immediate(kSlicedNotConsMask));
809   __ j(zero, &cons_string, Label::kNear);
810 
811   // Handle slices.
812   Label indirect_string_loaded;
813   __ mov(result, FieldOperand(string, SlicedString::kOffsetOffset));
814   __ SmiUntag(result);
815   __ add(index, result);
816   __ mov(string, FieldOperand(string, SlicedString::kParentOffset));
817   __ jmp(&indirect_string_loaded, Label::kNear);
818 
819   // Handle cons strings.
820   // Check whether the right hand side is the empty string (i.e. if
821   // this is really a flat string in a cons string). If that is not
822   // the case we would rather go to the runtime system now to flatten
823   // the string.
824   __ bind(&cons_string);
825   __ cmp(FieldOperand(string, ConsString::kSecondOffset),
826          Immediate(factory->empty_string()));
827   __ j(not_equal, call_runtime);
828   __ mov(string, FieldOperand(string, ConsString::kFirstOffset));
829 
830   __ bind(&indirect_string_loaded);
831   __ mov(result, FieldOperand(string, HeapObject::kMapOffset));
832   __ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
833 
834   // Distinguish sequential and external strings. Only these two string
835   // representations can reach here (slices and flat cons strings have been
836   // reduced to the underlying sequential or external string).
837   Label seq_string;
838   __ bind(&check_sequential);
839   STATIC_ASSERT(kSeqStringTag == 0);
840   __ test(result, Immediate(kStringRepresentationMask));
841   __ j(zero, &seq_string, Label::kNear);
842 
843   // Handle external strings.
844   Label one_byte_external, done;
845   if (FLAG_debug_code) {
846     // Assert that we do not have a cons or slice (indirect strings) here.
847     // Sequential strings have already been ruled out.
848     __ test(result, Immediate(kIsIndirectStringMask));
849     __ Assert(zero, kExternalStringExpectedButNotFound);
850   }
851   // Rule out short external strings.
852   STATIC_ASSERT(kShortExternalStringTag != 0);
853   __ test_b(result, Immediate(kShortExternalStringMask));
854   __ j(not_zero, call_runtime);
855   // Check encoding.
856   STATIC_ASSERT(kTwoByteStringTag == 0);
857   __ test_b(result, Immediate(kStringEncodingMask));
858   __ mov(result, FieldOperand(string, ExternalString::kResourceDataOffset));
859   __ j(not_equal, &one_byte_external, Label::kNear);
860   // Two-byte string.
861   __ movzx_w(result, Operand(result, index, times_2, 0));
862   __ jmp(&done, Label::kNear);
863   __ bind(&one_byte_external);
864   // One-byte string.
865   __ movzx_b(result, Operand(result, index, times_1, 0));
866   __ jmp(&done, Label::kNear);
867 
868   // Dispatch on the encoding: one-byte or two-byte.
869   Label one_byte;
870   __ bind(&seq_string);
871   STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
872   STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
873   __ test(result, Immediate(kStringEncodingMask));
874   __ j(not_zero, &one_byte, Label::kNear);
875 
876   // Two-byte string.
877   // Load the two-byte character code into the result register.
878   __ movzx_w(result, FieldOperand(string,
879                                   index,
880                                   times_2,
881                                   SeqTwoByteString::kHeaderSize));
882   __ jmp(&done, Label::kNear);
883 
884   // One-byte string.
885   // Load the byte into the result register.
886   __ bind(&one_byte);
887   __ movzx_b(result, FieldOperand(string,
888                                   index,
889                                   times_1,
890                                   SeqOneByteString::kHeaderSize));
891   __ bind(&done);
892 }
893 
894 #undef __
895 
896 
CodeAgingHelper(Isolate * isolate)897 CodeAgingHelper::CodeAgingHelper(Isolate* isolate) {
898   USE(isolate);
899   DCHECK(young_sequence_.length() == kNoCodeAgeSequenceLength);
900   CodePatcher patcher(isolate, young_sequence_.start(),
901                       young_sequence_.length());
902   patcher.masm()->push(ebp);
903   patcher.masm()->mov(ebp, esp);
904   patcher.masm()->push(esi);
905   patcher.masm()->push(edi);
906 }
907 
908 
909 #ifdef DEBUG
IsOld(byte * candidate) const910 bool CodeAgingHelper::IsOld(byte* candidate) const {
911   return *candidate == kCallOpcode;
912 }
913 #endif
914 
915 
IsYoungSequence(Isolate * isolate,byte * sequence)916 bool Code::IsYoungSequence(Isolate* isolate, byte* sequence) {
917   bool result = isolate->code_aging_helper()->IsYoung(sequence);
918   DCHECK(result || isolate->code_aging_helper()->IsOld(sequence));
919   return result;
920 }
921 
922 
GetCodeAgeAndParity(Isolate * isolate,byte * sequence,Age * age,MarkingParity * parity)923 void Code::GetCodeAgeAndParity(Isolate* isolate, byte* sequence, Age* age,
924                                MarkingParity* parity) {
925   if (IsYoungSequence(isolate, sequence)) {
926     *age = kNoAgeCodeAge;
927     *parity = NO_MARKING_PARITY;
928   } else {
929     sequence++;  // Skip the kCallOpcode byte
930     Address target_address = sequence + *reinterpret_cast<int*>(sequence) +
931         Assembler::kCallTargetAddressOffset;
932     Code* stub = GetCodeFromTargetAddress(target_address);
933     GetCodeAgeAndParity(stub, age, parity);
934   }
935 }
936 
937 
PatchPlatformCodeAge(Isolate * isolate,byte * sequence,Code::Age age,MarkingParity parity)938 void Code::PatchPlatformCodeAge(Isolate* isolate,
939                                 byte* sequence,
940                                 Code::Age age,
941                                 MarkingParity parity) {
942   uint32_t young_length = isolate->code_aging_helper()->young_sequence_length();
943   if (age == kNoAgeCodeAge) {
944     isolate->code_aging_helper()->CopyYoungSequenceTo(sequence);
945     Assembler::FlushICache(isolate, sequence, young_length);
946   } else {
947     Code* stub = GetCodeAgeStub(isolate, age, parity);
948     CodePatcher patcher(isolate, sequence, young_length);
949     patcher.masm()->call(stub->instruction_start(), RelocInfo::NONE32);
950   }
951 }
952 
953 
954 }  // namespace internal
955 }  // namespace v8
956 
957 #endif  // V8_TARGET_ARCH_IA32
958