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/v8.h"
6
7#if V8_TARGET_ARCH_X64
8
9#include "src/codegen.h"
10#include "src/macro-assembler.h"
11
12namespace v8 {
13namespace internal {
14
15// -------------------------------------------------------------------------
16// Platform-specific RuntimeCallHelper functions.
17
18void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
19  masm->EnterFrame(StackFrame::INTERNAL);
20  DCHECK(!masm->has_frame());
21  masm->set_has_frame(true);
22}
23
24
25void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
26  masm->LeaveFrame(StackFrame::INTERNAL);
27  DCHECK(masm->has_frame());
28  masm->set_has_frame(false);
29}
30
31
32#define __ masm.
33
34
35UnaryMathFunction CreateExpFunction() {
36  if (!FLAG_fast_math) return &std::exp;
37  size_t actual_size;
38  byte* buffer =
39      static_cast<byte*>(base::OS::Allocate(1 * KB, &actual_size, true));
40  if (buffer == NULL) return &std::exp;
41  ExternalReference::InitializeMathExpData();
42
43  MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
44  // xmm0: raw double input.
45  XMMRegister input = xmm0;
46  XMMRegister result = xmm1;
47  __ pushq(rax);
48  __ pushq(rbx);
49
50  MathExpGenerator::EmitMathExp(&masm, input, result, xmm2, rax, rbx);
51
52  __ popq(rbx);
53  __ popq(rax);
54  __ movsd(xmm0, result);
55  __ Ret();
56
57  CodeDesc desc;
58  masm.GetCode(&desc);
59  DCHECK(!RelocInfo::RequiresRelocation(desc));
60
61  CpuFeatures::FlushICache(buffer, actual_size);
62  base::OS::ProtectCode(buffer, actual_size);
63  return FUNCTION_CAST<UnaryMathFunction>(buffer);
64}
65
66
67UnaryMathFunction CreateSqrtFunction() {
68  size_t actual_size;
69  // Allocate buffer in executable space.
70  byte* buffer =
71      static_cast<byte*>(base::OS::Allocate(1 * KB, &actual_size, true));
72  if (buffer == NULL) return &std::sqrt;
73
74  MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
75  // xmm0: raw double input.
76  // Move double input into registers.
77  __ sqrtsd(xmm0, xmm0);
78  __ Ret();
79
80  CodeDesc desc;
81  masm.GetCode(&desc);
82  DCHECK(!RelocInfo::RequiresRelocation(desc));
83
84  CpuFeatures::FlushICache(buffer, actual_size);
85  base::OS::ProtectCode(buffer, actual_size);
86  return FUNCTION_CAST<UnaryMathFunction>(buffer);
87}
88
89
90#ifdef _WIN64
91typedef double (*ModuloFunction)(double, double);
92// Define custom fmod implementation.
93ModuloFunction CreateModuloFunction() {
94  size_t actual_size;
95  byte* buffer = static_cast<byte*>(
96      base::OS::Allocate(Assembler::kMinimalBufferSize, &actual_size, true));
97  CHECK(buffer);
98  Assembler masm(NULL, buffer, static_cast<int>(actual_size));
99  // Generated code is put into a fixed, unmovable, buffer, and not into
100  // the V8 heap. We can't, and don't, refer to any relocatable addresses
101  // (e.g. the JavaScript nan-object).
102
103  // Windows 64 ABI passes double arguments in xmm0, xmm1 and
104  // returns result in xmm0.
105  // Argument backing space is allocated on the stack above
106  // the return address.
107
108  // Compute x mod y.
109  // Load y and x (use argument backing store as temporary storage).
110  __ movsd(Operand(rsp, kRegisterSize * 2), xmm1);
111  __ movsd(Operand(rsp, kRegisterSize), xmm0);
112  __ fld_d(Operand(rsp, kRegisterSize * 2));
113  __ fld_d(Operand(rsp, kRegisterSize));
114
115  // Clear exception flags before operation.
116  {
117    Label no_exceptions;
118    __ fwait();
119    __ fnstsw_ax();
120    // Clear if Illegal Operand or Zero Division exceptions are set.
121    __ testb(rax, Immediate(5));
122    __ j(zero, &no_exceptions);
123    __ fnclex();
124    __ bind(&no_exceptions);
125  }
126
127  // Compute st(0) % st(1)
128  {
129    Label partial_remainder_loop;
130    __ bind(&partial_remainder_loop);
131    __ fprem();
132    __ fwait();
133    __ fnstsw_ax();
134    __ testl(rax, Immediate(0x400 /* C2 */));
135    // If C2 is set, computation only has partial result. Loop to
136    // continue computation.
137    __ j(not_zero, &partial_remainder_loop);
138  }
139
140  Label valid_result;
141  Label return_result;
142  // If Invalid Operand or Zero Division exceptions are set,
143  // return NaN.
144  __ testb(rax, Immediate(5));
145  __ j(zero, &valid_result);
146  __ fstp(0);  // Drop result in st(0).
147  int64_t kNaNValue = V8_INT64_C(0x7ff8000000000000);
148  __ movq(rcx, kNaNValue);
149  __ movq(Operand(rsp, kRegisterSize), rcx);
150  __ movsd(xmm0, Operand(rsp, kRegisterSize));
151  __ jmp(&return_result);
152
153  // If result is valid, return that.
154  __ bind(&valid_result);
155  __ fstp_d(Operand(rsp, kRegisterSize));
156  __ movsd(xmm0, Operand(rsp, kRegisterSize));
157
158  // Clean up FPU stack and exceptions and return xmm0
159  __ bind(&return_result);
160  __ fstp(0);  // Unload y.
161
162  Label clear_exceptions;
163  __ testb(rax, Immediate(0x3f /* Any Exception*/));
164  __ j(not_zero, &clear_exceptions);
165  __ ret(0);
166  __ bind(&clear_exceptions);
167  __ fnclex();
168  __ ret(0);
169
170  CodeDesc desc;
171  masm.GetCode(&desc);
172  base::OS::ProtectCode(buffer, actual_size);
173  // Call the function from C++ through this pointer.
174  return FUNCTION_CAST<ModuloFunction>(buffer);
175}
176
177#endif
178
179#undef __
180
181// -------------------------------------------------------------------------
182// Code generators
183
184#define __ ACCESS_MASM(masm)
185
186void ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
187    MacroAssembler* masm,
188    Register receiver,
189    Register key,
190    Register value,
191    Register target_map,
192    AllocationSiteMode mode,
193    Label* allocation_memento_found) {
194  // Return address is on the stack.
195  Register scratch = rdi;
196  DCHECK(!AreAliased(receiver, key, value, target_map, scratch));
197
198  if (mode == TRACK_ALLOCATION_SITE) {
199    DCHECK(allocation_memento_found != NULL);
200    __ JumpIfJSArrayHasAllocationMemento(
201        receiver, scratch, allocation_memento_found);
202  }
203
204  // Set transitioned map.
205  __ movp(FieldOperand(receiver, HeapObject::kMapOffset), target_map);
206  __ RecordWriteField(receiver,
207                      HeapObject::kMapOffset,
208                      target_map,
209                      scratch,
210                      kDontSaveFPRegs,
211                      EMIT_REMEMBERED_SET,
212                      OMIT_SMI_CHECK);
213}
214
215
216void ElementsTransitionGenerator::GenerateSmiToDouble(
217    MacroAssembler* masm,
218    Register receiver,
219    Register key,
220    Register value,
221    Register target_map,
222    AllocationSiteMode mode,
223    Label* fail) {
224  // Return address is on the stack.
225  DCHECK(receiver.is(rdx));
226  DCHECK(key.is(rcx));
227  DCHECK(value.is(rax));
228  DCHECK(target_map.is(rbx));
229
230  // The fail label is not actually used since we do not allocate.
231  Label allocated, new_backing_store, only_change_map, done;
232
233  if (mode == TRACK_ALLOCATION_SITE) {
234    __ JumpIfJSArrayHasAllocationMemento(rdx, rdi, fail);
235  }
236
237  // Check for empty arrays, which only require a map transition and no changes
238  // to the backing store.
239  __ movp(r8, FieldOperand(rdx, JSObject::kElementsOffset));
240  __ CompareRoot(r8, Heap::kEmptyFixedArrayRootIndex);
241  __ j(equal, &only_change_map);
242
243  __ SmiToInteger32(r9, FieldOperand(r8, FixedDoubleArray::kLengthOffset));
244  if (kPointerSize == kDoubleSize) {
245    // Check backing store for COW-ness. For COW arrays we have to
246    // allocate a new backing store.
247    __ CompareRoot(FieldOperand(r8, HeapObject::kMapOffset),
248                   Heap::kFixedCOWArrayMapRootIndex);
249    __ j(equal, &new_backing_store);
250  } else {
251    // For x32 port we have to allocate a new backing store as SMI size is
252    // not equal with double size.
253    DCHECK(kDoubleSize == 2 * kPointerSize);
254    __ jmp(&new_backing_store);
255  }
256
257  // Check if the backing store is in new-space. If not, we need to allocate
258  // a new one since the old one is in pointer-space.
259  // If in new space, we can reuse the old backing store because it is
260  // the same size.
261  __ JumpIfNotInNewSpace(r8, rdi, &new_backing_store);
262
263  __ movp(r14, r8);  // Destination array equals source array.
264
265  // r8 : source FixedArray
266  // r9 : elements array length
267  // r14: destination FixedDoubleArray
268  // Set backing store's map
269  __ LoadRoot(rdi, Heap::kFixedDoubleArrayMapRootIndex);
270  __ movp(FieldOperand(r14, HeapObject::kMapOffset), rdi);
271
272  __ bind(&allocated);
273  // Set transitioned map.
274  __ movp(FieldOperand(rdx, HeapObject::kMapOffset), rbx);
275  __ RecordWriteField(rdx,
276                      HeapObject::kMapOffset,
277                      rbx,
278                      rdi,
279                      kDontSaveFPRegs,
280                      EMIT_REMEMBERED_SET,
281                      OMIT_SMI_CHECK);
282
283  // Convert smis to doubles and holes to hole NaNs.  The Array's length
284  // remains unchanged.
285  STATIC_ASSERT(FixedDoubleArray::kLengthOffset == FixedArray::kLengthOffset);
286  STATIC_ASSERT(FixedDoubleArray::kHeaderSize == FixedArray::kHeaderSize);
287
288  Label loop, entry, convert_hole;
289  __ movq(r15, bit_cast<int64_t, uint64_t>(kHoleNanInt64));
290  // r15: the-hole NaN
291  __ jmp(&entry);
292
293  // Allocate new backing store.
294  __ bind(&new_backing_store);
295  __ leap(rdi, Operand(r9, times_8, FixedArray::kHeaderSize));
296  __ Allocate(rdi, r14, r11, r15, fail, TAG_OBJECT);
297  // Set backing store's map
298  __ LoadRoot(rdi, Heap::kFixedDoubleArrayMapRootIndex);
299  __ movp(FieldOperand(r14, HeapObject::kMapOffset), rdi);
300  // Set receiver's backing store.
301  __ movp(FieldOperand(rdx, JSObject::kElementsOffset), r14);
302  __ movp(r11, r14);
303  __ RecordWriteField(rdx,
304                      JSObject::kElementsOffset,
305                      r11,
306                      r15,
307                      kDontSaveFPRegs,
308                      EMIT_REMEMBERED_SET,
309                      OMIT_SMI_CHECK);
310  // Set backing store's length.
311  __ Integer32ToSmi(r11, r9);
312  __ movp(FieldOperand(r14, FixedDoubleArray::kLengthOffset), r11);
313  __ jmp(&allocated);
314
315  __ bind(&only_change_map);
316  // Set transitioned map.
317  __ movp(FieldOperand(rdx, HeapObject::kMapOffset), rbx);
318  __ RecordWriteField(rdx,
319                      HeapObject::kMapOffset,
320                      rbx,
321                      rdi,
322                      kDontSaveFPRegs,
323                      OMIT_REMEMBERED_SET,
324                      OMIT_SMI_CHECK);
325  __ jmp(&done);
326
327  // Conversion loop.
328  __ bind(&loop);
329  __ movp(rbx,
330          FieldOperand(r8, r9, times_pointer_size, FixedArray::kHeaderSize));
331  // r9 : current element's index
332  // rbx: current element (smi-tagged)
333  __ JumpIfNotSmi(rbx, &convert_hole);
334  __ SmiToInteger32(rbx, rbx);
335  __ Cvtlsi2sd(xmm0, rbx);
336  __ movsd(FieldOperand(r14, r9, times_8, FixedDoubleArray::kHeaderSize),
337           xmm0);
338  __ jmp(&entry);
339  __ bind(&convert_hole);
340
341  if (FLAG_debug_code) {
342    __ CompareRoot(rbx, Heap::kTheHoleValueRootIndex);
343    __ Assert(equal, kObjectFoundInSmiOnlyArray);
344  }
345
346  __ movq(FieldOperand(r14, r9, times_8, FixedDoubleArray::kHeaderSize), r15);
347  __ bind(&entry);
348  __ decp(r9);
349  __ j(not_sign, &loop);
350
351  __ bind(&done);
352}
353
354
355void ElementsTransitionGenerator::GenerateDoubleToObject(
356    MacroAssembler* masm,
357    Register receiver,
358    Register key,
359    Register value,
360    Register target_map,
361    AllocationSiteMode mode,
362    Label* fail) {
363  // Return address is on the stack.
364  DCHECK(receiver.is(rdx));
365  DCHECK(key.is(rcx));
366  DCHECK(value.is(rax));
367  DCHECK(target_map.is(rbx));
368
369  Label loop, entry, convert_hole, gc_required, only_change_map;
370
371  if (mode == TRACK_ALLOCATION_SITE) {
372    __ JumpIfJSArrayHasAllocationMemento(rdx, rdi, fail);
373  }
374
375  // Check for empty arrays, which only require a map transition and no changes
376  // to the backing store.
377  __ movp(r8, FieldOperand(rdx, JSObject::kElementsOffset));
378  __ CompareRoot(r8, Heap::kEmptyFixedArrayRootIndex);
379  __ j(equal, &only_change_map);
380
381  __ Push(rax);
382
383  __ movp(r8, FieldOperand(rdx, JSObject::kElementsOffset));
384  __ SmiToInteger32(r9, FieldOperand(r8, FixedDoubleArray::kLengthOffset));
385  // r8 : source FixedDoubleArray
386  // r9 : number of elements
387  __ leap(rdi, Operand(r9, times_pointer_size, FixedArray::kHeaderSize));
388  __ Allocate(rdi, r11, r14, r15, &gc_required, TAG_OBJECT);
389  // r11: destination FixedArray
390  __ LoadRoot(rdi, Heap::kFixedArrayMapRootIndex);
391  __ movp(FieldOperand(r11, HeapObject::kMapOffset), rdi);
392  __ Integer32ToSmi(r14, r9);
393  __ movp(FieldOperand(r11, FixedArray::kLengthOffset), r14);
394
395  // Prepare for conversion loop.
396  __ movq(rsi, bit_cast<int64_t, uint64_t>(kHoleNanInt64));
397  __ LoadRoot(rdi, Heap::kTheHoleValueRootIndex);
398  // rsi: the-hole NaN
399  // rdi: pointer to the-hole
400  __ jmp(&entry);
401
402  // Call into runtime if GC is required.
403  __ bind(&gc_required);
404  __ Pop(rax);
405  __ movp(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
406  __ jmp(fail);
407
408  // Box doubles into heap numbers.
409  __ bind(&loop);
410  __ movq(r14, FieldOperand(r8,
411                            r9,
412                            times_8,
413                            FixedDoubleArray::kHeaderSize));
414  // r9 : current element's index
415  // r14: current element
416  __ cmpq(r14, rsi);
417  __ j(equal, &convert_hole);
418
419  // Non-hole double, copy value into a heap number.
420  __ AllocateHeapNumber(rax, r15, &gc_required);
421  // rax: new heap number
422  __ movq(FieldOperand(rax, HeapNumber::kValueOffset), r14);
423  __ movp(FieldOperand(r11,
424                       r9,
425                       times_pointer_size,
426                       FixedArray::kHeaderSize),
427          rax);
428  __ movp(r15, r9);
429  __ RecordWriteArray(r11,
430                      rax,
431                      r15,
432                      kDontSaveFPRegs,
433                      EMIT_REMEMBERED_SET,
434                      OMIT_SMI_CHECK);
435  __ jmp(&entry, Label::kNear);
436
437  // Replace the-hole NaN with the-hole pointer.
438  __ bind(&convert_hole);
439  __ movp(FieldOperand(r11,
440                       r9,
441                       times_pointer_size,
442                       FixedArray::kHeaderSize),
443          rdi);
444
445  __ bind(&entry);
446  __ decp(r9);
447  __ j(not_sign, &loop);
448
449  // Replace receiver's backing store with newly created and filled FixedArray.
450  __ movp(FieldOperand(rdx, JSObject::kElementsOffset), r11);
451  __ RecordWriteField(rdx,
452                      JSObject::kElementsOffset,
453                      r11,
454                      r15,
455                      kDontSaveFPRegs,
456                      EMIT_REMEMBERED_SET,
457                      OMIT_SMI_CHECK);
458  __ Pop(rax);
459  __ movp(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
460
461  __ bind(&only_change_map);
462  // Set transitioned map.
463  __ movp(FieldOperand(rdx, HeapObject::kMapOffset), rbx);
464  __ RecordWriteField(rdx,
465                      HeapObject::kMapOffset,
466                      rbx,
467                      rdi,
468                      kDontSaveFPRegs,
469                      OMIT_REMEMBERED_SET,
470                      OMIT_SMI_CHECK);
471}
472
473
474void StringCharLoadGenerator::Generate(MacroAssembler* masm,
475                                       Register string,
476                                       Register index,
477                                       Register result,
478                                       Label* call_runtime) {
479  // Fetch the instance type of the receiver into result register.
480  __ movp(result, FieldOperand(string, HeapObject::kMapOffset));
481  __ movzxbl(result, FieldOperand(result, Map::kInstanceTypeOffset));
482
483  // We need special handling for indirect strings.
484  Label check_sequential;
485  __ testb(result, Immediate(kIsIndirectStringMask));
486  __ j(zero, &check_sequential, Label::kNear);
487
488  // Dispatch on the indirect string shape: slice or cons.
489  Label cons_string;
490  __ testb(result, Immediate(kSlicedNotConsMask));
491  __ j(zero, &cons_string, Label::kNear);
492
493  // Handle slices.
494  Label indirect_string_loaded;
495  __ SmiToInteger32(result, FieldOperand(string, SlicedString::kOffsetOffset));
496  __ addp(index, result);
497  __ movp(string, FieldOperand(string, SlicedString::kParentOffset));
498  __ jmp(&indirect_string_loaded, Label::kNear);
499
500  // Handle cons strings.
501  // Check whether the right hand side is the empty string (i.e. if
502  // this is really a flat string in a cons string). If that is not
503  // the case we would rather go to the runtime system now to flatten
504  // the string.
505  __ bind(&cons_string);
506  __ CompareRoot(FieldOperand(string, ConsString::kSecondOffset),
507                 Heap::kempty_stringRootIndex);
508  __ j(not_equal, call_runtime);
509  __ movp(string, FieldOperand(string, ConsString::kFirstOffset));
510
511  __ bind(&indirect_string_loaded);
512  __ movp(result, FieldOperand(string, HeapObject::kMapOffset));
513  __ movzxbl(result, FieldOperand(result, Map::kInstanceTypeOffset));
514
515  // Distinguish sequential and external strings. Only these two string
516  // representations can reach here (slices and flat cons strings have been
517  // reduced to the underlying sequential or external string).
518  Label seq_string;
519  __ bind(&check_sequential);
520  STATIC_ASSERT(kSeqStringTag == 0);
521  __ testb(result, Immediate(kStringRepresentationMask));
522  __ j(zero, &seq_string, Label::kNear);
523
524  // Handle external strings.
525  Label one_byte_external, done;
526  if (FLAG_debug_code) {
527    // Assert that we do not have a cons or slice (indirect strings) here.
528    // Sequential strings have already been ruled out.
529    __ testb(result, Immediate(kIsIndirectStringMask));
530    __ Assert(zero, kExternalStringExpectedButNotFound);
531  }
532  // Rule out short external strings.
533  STATIC_ASSERT(kShortExternalStringTag != 0);
534  __ testb(result, Immediate(kShortExternalStringTag));
535  __ j(not_zero, call_runtime);
536  // Check encoding.
537  STATIC_ASSERT(kTwoByteStringTag == 0);
538  __ testb(result, Immediate(kStringEncodingMask));
539  __ movp(result, FieldOperand(string, ExternalString::kResourceDataOffset));
540  __ j(not_equal, &one_byte_external, Label::kNear);
541  // Two-byte string.
542  __ movzxwl(result, Operand(result, index, times_2, 0));
543  __ jmp(&done, Label::kNear);
544  __ bind(&one_byte_external);
545  // One-byte string.
546  __ movzxbl(result, Operand(result, index, times_1, 0));
547  __ jmp(&done, Label::kNear);
548
549  // Dispatch on the encoding: one-byte or two-byte.
550  Label one_byte;
551  __ bind(&seq_string);
552  STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
553  STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
554  __ testb(result, Immediate(kStringEncodingMask));
555  __ j(not_zero, &one_byte, Label::kNear);
556
557  // Two-byte string.
558  // Load the two-byte character code into the result register.
559  STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
560  __ movzxwl(result, FieldOperand(string,
561                                  index,
562                                  times_2,
563                                  SeqTwoByteString::kHeaderSize));
564  __ jmp(&done, Label::kNear);
565
566  // One-byte string.
567  // Load the byte into the result register.
568  __ bind(&one_byte);
569  __ movzxbl(result, FieldOperand(string,
570                                  index,
571                                  times_1,
572                                  SeqOneByteString::kHeaderSize));
573  __ bind(&done);
574}
575
576
577void MathExpGenerator::EmitMathExp(MacroAssembler* masm,
578                                   XMMRegister input,
579                                   XMMRegister result,
580                                   XMMRegister double_scratch,
581                                   Register temp1,
582                                   Register temp2) {
583  DCHECK(!input.is(result));
584  DCHECK(!input.is(double_scratch));
585  DCHECK(!result.is(double_scratch));
586  DCHECK(!temp1.is(temp2));
587  DCHECK(ExternalReference::math_exp_constants(0).address() != NULL);
588  DCHECK(!masm->serializer_enabled());  // External references not serializable.
589
590  Label done;
591
592  __ Move(kScratchRegister, ExternalReference::math_exp_constants(0));
593  __ movsd(double_scratch, Operand(kScratchRegister, 0 * kDoubleSize));
594  __ xorpd(result, result);
595  __ ucomisd(double_scratch, input);
596  __ j(above_equal, &done);
597  __ ucomisd(input, Operand(kScratchRegister, 1 * kDoubleSize));
598  __ movsd(result, Operand(kScratchRegister, 2 * kDoubleSize));
599  __ j(above_equal, &done);
600  __ movsd(double_scratch, Operand(kScratchRegister, 3 * kDoubleSize));
601  __ movsd(result, Operand(kScratchRegister, 4 * kDoubleSize));
602  __ mulsd(double_scratch, input);
603  __ addsd(double_scratch, result);
604  __ movq(temp2, double_scratch);
605  __ subsd(double_scratch, result);
606  __ movsd(result, Operand(kScratchRegister, 6 * kDoubleSize));
607  __ leaq(temp1, Operand(temp2, 0x1ff800));
608  __ andq(temp2, Immediate(0x7ff));
609  __ shrq(temp1, Immediate(11));
610  __ mulsd(double_scratch, Operand(kScratchRegister, 5 * kDoubleSize));
611  __ Move(kScratchRegister, ExternalReference::math_exp_log_table());
612  __ shlq(temp1, Immediate(52));
613  __ orq(temp1, Operand(kScratchRegister, temp2, times_8, 0));
614  __ Move(kScratchRegister, ExternalReference::math_exp_constants(0));
615  __ subsd(double_scratch, input);
616  __ movsd(input, double_scratch);
617  __ subsd(result, double_scratch);
618  __ mulsd(input, double_scratch);
619  __ mulsd(result, input);
620  __ movq(input, temp1);
621  __ mulsd(result, Operand(kScratchRegister, 7 * kDoubleSize));
622  __ subsd(result, double_scratch);
623  __ addsd(result, Operand(kScratchRegister, 8 * kDoubleSize));
624  __ mulsd(result, input);
625
626  __ bind(&done);
627}
628
629#undef __
630
631
632CodeAgingHelper::CodeAgingHelper() {
633  DCHECK(young_sequence_.length() == kNoCodeAgeSequenceLength);
634  // The sequence of instructions that is patched out for aging code is the
635  // following boilerplate stack-building prologue that is found both in
636  // FUNCTION and OPTIMIZED_FUNCTION code:
637  CodePatcher patcher(young_sequence_.start(), young_sequence_.length());
638  patcher.masm()->pushq(rbp);
639  patcher.masm()->movp(rbp, rsp);
640  patcher.masm()->Push(rsi);
641  patcher.masm()->Push(rdi);
642}
643
644
645#ifdef DEBUG
646bool CodeAgingHelper::IsOld(byte* candidate) const {
647  return *candidate == kCallOpcode;
648}
649#endif
650
651
652bool Code::IsYoungSequence(Isolate* isolate, byte* sequence) {
653  bool result = isolate->code_aging_helper()->IsYoung(sequence);
654  DCHECK(result || isolate->code_aging_helper()->IsOld(sequence));
655  return result;
656}
657
658
659void Code::GetCodeAgeAndParity(Isolate* isolate, byte* sequence, Age* age,
660                               MarkingParity* parity) {
661  if (IsYoungSequence(isolate, sequence)) {
662    *age = kNoAgeCodeAge;
663    *parity = NO_MARKING_PARITY;
664  } else {
665    sequence++;  // Skip the kCallOpcode byte
666    Address target_address = sequence + *reinterpret_cast<int*>(sequence) +
667        Assembler::kCallTargetAddressOffset;
668    Code* stub = GetCodeFromTargetAddress(target_address);
669    GetCodeAgeAndParity(stub, age, parity);
670  }
671}
672
673
674void Code::PatchPlatformCodeAge(Isolate* isolate,
675                                byte* sequence,
676                                Code::Age age,
677                                MarkingParity parity) {
678  uint32_t young_length = isolate->code_aging_helper()->young_sequence_length();
679  if (age == kNoAgeCodeAge) {
680    isolate->code_aging_helper()->CopyYoungSequenceTo(sequence);
681    CpuFeatures::FlushICache(sequence, young_length);
682  } else {
683    Code* stub = GetCodeAgeStub(isolate, age, parity);
684    CodePatcher patcher(sequence, young_length);
685    patcher.masm()->call(stub->instruction_start());
686    patcher.masm()->Nop(
687        kNoCodeAgeSequenceLength - Assembler::kShortCallInstructionLength);
688  }
689}
690
691
692Operand StackArgumentsAccessor::GetArgumentOperand(int index) {
693  DCHECK(index >= 0);
694  int receiver = (receiver_mode_ == ARGUMENTS_CONTAIN_RECEIVER) ? 1 : 0;
695  int displacement_to_last_argument = base_reg_.is(rsp) ?
696      kPCOnStackSize : kFPOnStackSize + kPCOnStackSize;
697  displacement_to_last_argument += extra_displacement_to_last_argument_;
698  if (argument_count_reg_.is(no_reg)) {
699    // argument[0] is at base_reg_ + displacement_to_last_argument +
700    // (argument_count_immediate_ + receiver - 1) * kPointerSize.
701    DCHECK(argument_count_immediate_ + receiver > 0);
702    return Operand(base_reg_, displacement_to_last_argument +
703        (argument_count_immediate_ + receiver - 1 - index) * kPointerSize);
704  } else {
705    // argument[0] is at base_reg_ + displacement_to_last_argument +
706    // argument_count_reg_ * times_pointer_size + (receiver - 1) * kPointerSize.
707    return Operand(base_reg_, argument_count_reg_, times_pointer_size,
708        displacement_to_last_argument + (receiver - 1 - index) * kPointerSize);
709  }
710}
711
712
713} }  // namespace v8::internal
714
715#endif  // V8_TARGET_ARCH_X64
716