• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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/arm64/codegen-arm64.h"
6 
7 #if V8_TARGET_ARCH_ARM64
8 
9 #include "src/arm64/simulator-arm64.h"
10 #include "src/codegen.h"
11 #include "src/macro-assembler.h"
12 
13 namespace v8 {
14 namespace internal {
15 
16 #define __ ACCESS_MASM(masm)
17 
18 #if defined(USE_SIMULATOR)
19 byte* fast_exp_arm64_machine_code = nullptr;
fast_exp_simulator(double x,Isolate * isolate)20 double fast_exp_simulator(double x, Isolate* isolate) {
21   Simulator * simulator = Simulator::current(isolate);
22   Simulator::CallArgument args[] = {
23       Simulator::CallArgument(x),
24       Simulator::CallArgument::End()
25   };
26   return simulator->CallDouble(fast_exp_arm64_machine_code, args);
27 }
28 #endif
29 
30 
CreateExpFunction(Isolate * isolate)31 UnaryMathFunctionWithIsolate CreateExpFunction(Isolate* isolate) {
32   // Use the Math.exp implemetation in MathExpGenerator::EmitMathExp() to create
33   // an AAPCS64-compliant exp() function. This will be faster than the C
34   // library's exp() function, but probably less accurate.
35   size_t actual_size;
36   byte* buffer =
37       static_cast<byte*>(base::OS::Allocate(1 * KB, &actual_size, true));
38   if (buffer == nullptr) return nullptr;
39 
40   ExternalReference::InitializeMathExpData();
41   MacroAssembler masm(isolate, buffer, static_cast<int>(actual_size),
42                       CodeObjectRequired::kNo);
43   masm.SetStackPointer(csp);
44 
45   // The argument will be in d0 on entry.
46   DoubleRegister input = d0;
47   // Use other caller-saved registers for all other values.
48   DoubleRegister result = d1;
49   DoubleRegister double_temp1 = d2;
50   DoubleRegister double_temp2 = d3;
51   Register temp1 = x10;
52   Register temp2 = x11;
53   Register temp3 = x12;
54 
55   MathExpGenerator::EmitMathExp(&masm, input, result,
56                                 double_temp1, double_temp2,
57                                 temp1, temp2, temp3);
58   // Move the result to the return register.
59   masm.Fmov(d0, result);
60   masm.Ret();
61 
62   CodeDesc desc;
63   masm.GetCode(&desc);
64   DCHECK(!RelocInfo::RequiresRelocation(desc));
65 
66   Assembler::FlushICache(isolate, buffer, actual_size);
67   base::OS::ProtectCode(buffer, actual_size);
68 
69 #if !defined(USE_SIMULATOR)
70   return FUNCTION_CAST<UnaryMathFunctionWithIsolate>(buffer);
71 #else
72   fast_exp_arm64_machine_code = buffer;
73   return &fast_exp_simulator;
74 #endif
75 }
76 
77 
CreateSqrtFunction(Isolate * isolate)78 UnaryMathFunctionWithIsolate CreateSqrtFunction(Isolate* isolate) {
79   return nullptr;
80 }
81 
82 
83 // -------------------------------------------------------------------------
84 // Platform-specific RuntimeCallHelper functions.
85 
BeforeCall(MacroAssembler * masm) const86 void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
87   masm->EnterFrame(StackFrame::INTERNAL);
88   DCHECK(!masm->has_frame());
89   masm->set_has_frame(true);
90 }
91 
92 
AfterCall(MacroAssembler * masm) const93 void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
94   masm->LeaveFrame(StackFrame::INTERNAL);
95   DCHECK(masm->has_frame());
96   masm->set_has_frame(false);
97 }
98 
99 
100 // -------------------------------------------------------------------------
101 // Code generators
102 
GenerateMapChangeElementsTransition(MacroAssembler * masm,Register receiver,Register key,Register value,Register target_map,AllocationSiteMode mode,Label * allocation_memento_found)103 void ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
104     MacroAssembler* masm,
105     Register receiver,
106     Register key,
107     Register value,
108     Register target_map,
109     AllocationSiteMode mode,
110     Label* allocation_memento_found) {
111   ASM_LOCATION(
112       "ElementsTransitionGenerator::GenerateMapChangeElementsTransition");
113   DCHECK(!AreAliased(receiver, key, value, target_map));
114 
115   if (mode == TRACK_ALLOCATION_SITE) {
116     DCHECK(allocation_memento_found != NULL);
117     __ JumpIfJSArrayHasAllocationMemento(receiver, x10, x11,
118                                          allocation_memento_found);
119   }
120 
121   // Set transitioned map.
122   __ Str(target_map, FieldMemOperand(receiver, HeapObject::kMapOffset));
123   __ RecordWriteField(receiver,
124                       HeapObject::kMapOffset,
125                       target_map,
126                       x10,
127                       kLRHasNotBeenSaved,
128                       kDontSaveFPRegs,
129                       EMIT_REMEMBERED_SET,
130                       OMIT_SMI_CHECK);
131 }
132 
133 
GenerateSmiToDouble(MacroAssembler * masm,Register receiver,Register key,Register value,Register target_map,AllocationSiteMode mode,Label * fail)134 void ElementsTransitionGenerator::GenerateSmiToDouble(
135     MacroAssembler* masm,
136     Register receiver,
137     Register key,
138     Register value,
139     Register target_map,
140     AllocationSiteMode mode,
141     Label* fail) {
142   ASM_LOCATION("ElementsTransitionGenerator::GenerateSmiToDouble");
143   Label gc_required, only_change_map;
144   Register elements = x4;
145   Register length = x5;
146   Register array_size = x6;
147   Register array = x7;
148 
149   Register scratch = x6;
150 
151   // Verify input registers don't conflict with locals.
152   DCHECK(!AreAliased(receiver, key, value, target_map,
153                      elements, length, array_size, array));
154 
155   if (mode == TRACK_ALLOCATION_SITE) {
156     __ JumpIfJSArrayHasAllocationMemento(receiver, x10, x11, fail);
157   }
158 
159   // Check for empty arrays, which only require a map transition and no changes
160   // to the backing store.
161   __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
162   __ JumpIfRoot(elements, Heap::kEmptyFixedArrayRootIndex, &only_change_map);
163 
164   __ Push(lr);
165   __ Ldrsw(length, UntagSmiFieldMemOperand(elements,
166                                            FixedArray::kLengthOffset));
167 
168   // Allocate new FixedDoubleArray.
169   __ Lsl(array_size, length, kDoubleSizeLog2);
170   __ Add(array_size, array_size, FixedDoubleArray::kHeaderSize);
171   __ Allocate(array_size, array, x10, x11, &gc_required, DOUBLE_ALIGNMENT);
172   // Register array is non-tagged heap object.
173 
174   // Set the destination FixedDoubleArray's length and map.
175   Register map_root = array_size;
176   __ LoadRoot(map_root, Heap::kFixedDoubleArrayMapRootIndex);
177   __ SmiTag(x11, length);
178   __ Str(x11, MemOperand(array, FixedDoubleArray::kLengthOffset));
179   __ Str(map_root, MemOperand(array, HeapObject::kMapOffset));
180 
181   __ Str(target_map, FieldMemOperand(receiver, HeapObject::kMapOffset));
182   __ RecordWriteField(receiver, HeapObject::kMapOffset, target_map, scratch,
183                       kLRHasBeenSaved, kDontSaveFPRegs, OMIT_REMEMBERED_SET,
184                       OMIT_SMI_CHECK);
185 
186   // Replace receiver's backing store with newly created FixedDoubleArray.
187   __ Add(x10, array, kHeapObjectTag);
188   __ Str(x10, FieldMemOperand(receiver, JSObject::kElementsOffset));
189   __ RecordWriteField(receiver, JSObject::kElementsOffset, x10,
190                       scratch, kLRHasBeenSaved, kDontSaveFPRegs,
191                       EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
192 
193   // Prepare for conversion loop.
194   Register src_elements = x10;
195   Register dst_elements = x11;
196   Register dst_end = x12;
197   __ Add(src_elements, elements, FixedArray::kHeaderSize - kHeapObjectTag);
198   __ Add(dst_elements, array, FixedDoubleArray::kHeaderSize);
199   __ Add(dst_end, dst_elements, Operand(length, LSL, kDoubleSizeLog2));
200 
201   FPRegister nan_d = d1;
202   __ Fmov(nan_d, rawbits_to_double(kHoleNanInt64));
203 
204   Label entry, done;
205   __ B(&entry);
206 
207   __ Bind(&only_change_map);
208   __ Str(target_map, FieldMemOperand(receiver, HeapObject::kMapOffset));
209   __ RecordWriteField(receiver, HeapObject::kMapOffset, target_map, scratch,
210                       kLRHasNotBeenSaved, kDontSaveFPRegs, OMIT_REMEMBERED_SET,
211                       OMIT_SMI_CHECK);
212   __ B(&done);
213 
214   // Call into runtime if GC is required.
215   __ Bind(&gc_required);
216   __ Pop(lr);
217   __ B(fail);
218 
219   // Iterate over the array, copying and coverting smis to doubles. If an
220   // element is non-smi, write a hole to the destination.
221   {
222     Label loop;
223     __ Bind(&loop);
224     __ Ldr(x13, MemOperand(src_elements, kPointerSize, PostIndex));
225     __ SmiUntagToDouble(d0, x13, kSpeculativeUntag);
226     __ Tst(x13, kSmiTagMask);
227     __ Fcsel(d0, d0, nan_d, eq);
228     __ Str(d0, MemOperand(dst_elements, kDoubleSize, PostIndex));
229 
230     __ Bind(&entry);
231     __ Cmp(dst_elements, dst_end);
232     __ B(lt, &loop);
233   }
234 
235   __ Pop(lr);
236   __ Bind(&done);
237 }
238 
239 
GenerateDoubleToObject(MacroAssembler * masm,Register receiver,Register key,Register value,Register target_map,AllocationSiteMode mode,Label * fail)240 void ElementsTransitionGenerator::GenerateDoubleToObject(
241     MacroAssembler* masm,
242     Register receiver,
243     Register key,
244     Register value,
245     Register target_map,
246     AllocationSiteMode mode,
247     Label* fail) {
248   ASM_LOCATION("ElementsTransitionGenerator::GenerateDoubleToObject");
249   Register elements = x4;
250   Register array_size = x6;
251   Register array = x7;
252   Register length = x5;
253 
254   // Verify input registers don't conflict with locals.
255   DCHECK(!AreAliased(receiver, key, value, target_map,
256                      elements, array_size, array, length));
257 
258   if (mode == TRACK_ALLOCATION_SITE) {
259     __ JumpIfJSArrayHasAllocationMemento(receiver, x10, x11, fail);
260   }
261 
262   // Check for empty arrays, which only require a map transition and no changes
263   // to the backing store.
264   Label only_change_map;
265 
266   __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
267   __ JumpIfRoot(elements, Heap::kEmptyFixedArrayRootIndex, &only_change_map);
268 
269   __ Push(lr);
270   // TODO(all): These registers may not need to be pushed. Examine
271   // RecordWriteStub and check whether it's needed.
272   __ Push(target_map, receiver, key, value);
273   __ Ldrsw(length, UntagSmiFieldMemOperand(elements,
274                                            FixedArray::kLengthOffset));
275   // Allocate new FixedArray.
276   Label gc_required;
277   __ Mov(array_size, FixedDoubleArray::kHeaderSize);
278   __ Add(array_size, array_size, Operand(length, LSL, kPointerSizeLog2));
279   __ Allocate(array_size, array, x10, x11, &gc_required, NO_ALLOCATION_FLAGS);
280 
281   // Set destination FixedDoubleArray's length and map.
282   Register map_root = array_size;
283   __ LoadRoot(map_root, Heap::kFixedArrayMapRootIndex);
284   __ SmiTag(x11, length);
285   __ Str(x11, MemOperand(array, FixedDoubleArray::kLengthOffset));
286   __ Str(map_root, MemOperand(array, HeapObject::kMapOffset));
287 
288   // Prepare for conversion loop.
289   Register src_elements = x10;
290   Register dst_elements = x11;
291   Register dst_end = x12;
292   Register the_hole = x14;
293   __ LoadRoot(the_hole, Heap::kTheHoleValueRootIndex);
294   __ Add(src_elements, elements,
295          FixedDoubleArray::kHeaderSize - kHeapObjectTag);
296   __ Add(dst_elements, array, FixedArray::kHeaderSize);
297   __ Add(dst_end, dst_elements, Operand(length, LSL, kPointerSizeLog2));
298 
299   // Allocating heap numbers in the loop below can fail and cause a jump to
300   // gc_required. We can't leave a partly initialized FixedArray behind,
301   // so pessimistically fill it with holes now.
302   Label initialization_loop, initialization_loop_entry;
303   __ B(&initialization_loop_entry);
304   __ bind(&initialization_loop);
305   __ Str(the_hole, MemOperand(dst_elements, kPointerSize, PostIndex));
306   __ bind(&initialization_loop_entry);
307   __ Cmp(dst_elements, dst_end);
308   __ B(lt, &initialization_loop);
309 
310   __ Add(dst_elements, array, FixedArray::kHeaderSize);
311   __ Add(array, array, kHeapObjectTag);
312 
313   Register heap_num_map = x15;
314   __ LoadRoot(heap_num_map, Heap::kHeapNumberMapRootIndex);
315 
316   Label entry;
317   __ B(&entry);
318 
319   // Call into runtime if GC is required.
320   __ Bind(&gc_required);
321   __ Pop(value, key, receiver, target_map);
322   __ Pop(lr);
323   __ B(fail);
324 
325   {
326     Label loop, convert_hole;
327     __ Bind(&loop);
328     __ Ldr(x13, MemOperand(src_elements, kPointerSize, PostIndex));
329     __ Cmp(x13, kHoleNanInt64);
330     __ B(eq, &convert_hole);
331 
332     // Non-hole double, copy value into a heap number.
333     Register heap_num = length;
334     Register scratch = array_size;
335     Register scratch2 = elements;
336     __ AllocateHeapNumber(heap_num, &gc_required, scratch, scratch2,
337                           x13, heap_num_map);
338     __ Mov(x13, dst_elements);
339     __ Str(heap_num, MemOperand(dst_elements, kPointerSize, PostIndex));
340     __ RecordWrite(array, x13, heap_num, kLRHasBeenSaved, kDontSaveFPRegs,
341                    EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
342 
343     __ B(&entry);
344 
345     // Replace the-hole NaN with the-hole pointer.
346     __ Bind(&convert_hole);
347     __ Str(the_hole, MemOperand(dst_elements, kPointerSize, PostIndex));
348 
349     __ Bind(&entry);
350     __ Cmp(dst_elements, dst_end);
351     __ B(lt, &loop);
352   }
353 
354   __ Pop(value, key, receiver, target_map);
355   // Replace receiver's backing store with newly created and filled FixedArray.
356   __ Str(array, FieldMemOperand(receiver, JSObject::kElementsOffset));
357   __ RecordWriteField(receiver, JSObject::kElementsOffset, array, x13,
358                       kLRHasBeenSaved, kDontSaveFPRegs, EMIT_REMEMBERED_SET,
359                       OMIT_SMI_CHECK);
360   __ Pop(lr);
361 
362   __ Bind(&only_change_map);
363   __ Str(target_map, FieldMemOperand(receiver, HeapObject::kMapOffset));
364   __ RecordWriteField(receiver, HeapObject::kMapOffset, target_map, x13,
365                       kLRHasNotBeenSaved, kDontSaveFPRegs, OMIT_REMEMBERED_SET,
366                       OMIT_SMI_CHECK);
367 }
368 
369 
CodeAgingHelper(Isolate * isolate)370 CodeAgingHelper::CodeAgingHelper(Isolate* isolate) {
371   USE(isolate);
372   DCHECK(young_sequence_.length() == kNoCodeAgeSequenceLength);
373   // The sequence of instructions that is patched out for aging code is the
374   // following boilerplate stack-building prologue that is found both in
375   // FUNCTION and OPTIMIZED_FUNCTION code:
376   PatchingAssembler patcher(isolate, young_sequence_.start(),
377                             young_sequence_.length() / kInstructionSize);
378   // The young sequence is the frame setup code for FUNCTION code types. It is
379   // generated by FullCodeGenerator::Generate.
380   MacroAssembler::EmitFrameSetupForCodeAgePatching(&patcher);
381 
382 #ifdef DEBUG
383   const int length = kCodeAgeStubEntryOffset / kInstructionSize;
384   DCHECK(old_sequence_.length() >= kCodeAgeStubEntryOffset);
385   PatchingAssembler patcher_old(isolate, old_sequence_.start(), length);
386   MacroAssembler::EmitCodeAgeSequence(&patcher_old, NULL);
387 #endif
388 }
389 
390 
391 #ifdef DEBUG
IsOld(byte * candidate) const392 bool CodeAgingHelper::IsOld(byte* candidate) const {
393   return memcmp(candidate, old_sequence_.start(), kCodeAgeStubEntryOffset) == 0;
394 }
395 #endif
396 
397 
IsYoungSequence(Isolate * isolate,byte * sequence)398 bool Code::IsYoungSequence(Isolate* isolate, byte* sequence) {
399   return MacroAssembler::IsYoungSequence(isolate, sequence);
400 }
401 
402 
GetCodeAgeAndParity(Isolate * isolate,byte * sequence,Age * age,MarkingParity * parity)403 void Code::GetCodeAgeAndParity(Isolate* isolate, byte* sequence, Age* age,
404                                MarkingParity* parity) {
405   if (IsYoungSequence(isolate, sequence)) {
406     *age = kNoAgeCodeAge;
407     *parity = NO_MARKING_PARITY;
408   } else {
409     byte* target = sequence + kCodeAgeStubEntryOffset;
410     Code* stub = GetCodeFromTargetAddress(Memory::Address_at(target));
411     GetCodeAgeAndParity(stub, age, parity);
412   }
413 }
414 
415 
PatchPlatformCodeAge(Isolate * isolate,byte * sequence,Code::Age age,MarkingParity parity)416 void Code::PatchPlatformCodeAge(Isolate* isolate,
417                                 byte* sequence,
418                                 Code::Age age,
419                                 MarkingParity parity) {
420   PatchingAssembler patcher(isolate, sequence,
421                             kNoCodeAgeSequenceLength / kInstructionSize);
422   if (age == kNoAgeCodeAge) {
423     MacroAssembler::EmitFrameSetupForCodeAgePatching(&patcher);
424   } else {
425     Code * stub = GetCodeAgeStub(isolate, age, parity);
426     MacroAssembler::EmitCodeAgeSequence(&patcher, stub);
427   }
428 }
429 
430 
Generate(MacroAssembler * masm,Register string,Register index,Register result,Label * call_runtime)431 void StringCharLoadGenerator::Generate(MacroAssembler* masm,
432                                        Register string,
433                                        Register index,
434                                        Register result,
435                                        Label* call_runtime) {
436   DCHECK(string.Is64Bits() && index.Is32Bits() && result.Is64Bits());
437   // Fetch the instance type of the receiver into result register.
438   __ Ldr(result, FieldMemOperand(string, HeapObject::kMapOffset));
439   __ Ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
440 
441   // We need special handling for indirect strings.
442   Label check_sequential;
443   __ TestAndBranchIfAllClear(result, kIsIndirectStringMask, &check_sequential);
444 
445   // Dispatch on the indirect string shape: slice or cons.
446   Label cons_string;
447   __ TestAndBranchIfAllClear(result, kSlicedNotConsMask, &cons_string);
448 
449   // Handle slices.
450   Label indirect_string_loaded;
451   __ Ldr(result.W(),
452          UntagSmiFieldMemOperand(string, SlicedString::kOffsetOffset));
453   __ Ldr(string, FieldMemOperand(string, SlicedString::kParentOffset));
454   __ Add(index, index, result.W());
455   __ B(&indirect_string_loaded);
456 
457   // Handle cons strings.
458   // Check whether the right hand side is the empty string (i.e. if
459   // this is really a flat string in a cons string). If that is not
460   // the case we would rather go to the runtime system now to flatten
461   // the string.
462   __ Bind(&cons_string);
463   __ Ldr(result, FieldMemOperand(string, ConsString::kSecondOffset));
464   __ JumpIfNotRoot(result, Heap::kempty_stringRootIndex, call_runtime);
465   // Get the first of the two strings and load its instance type.
466   __ Ldr(string, FieldMemOperand(string, ConsString::kFirstOffset));
467 
468   __ Bind(&indirect_string_loaded);
469   __ Ldr(result, FieldMemOperand(string, HeapObject::kMapOffset));
470   __ Ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
471 
472   // Distinguish sequential and external strings. Only these two string
473   // representations can reach here (slices and flat cons strings have been
474   // reduced to the underlying sequential or external string).
475   Label external_string, check_encoding;
476   __ Bind(&check_sequential);
477   STATIC_ASSERT(kSeqStringTag == 0);
478   __ TestAndBranchIfAnySet(result, kStringRepresentationMask, &external_string);
479 
480   // Prepare sequential strings
481   STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
482   __ Add(string, string, SeqTwoByteString::kHeaderSize - kHeapObjectTag);
483   __ B(&check_encoding);
484 
485   // Handle external strings.
486   __ Bind(&external_string);
487   if (FLAG_debug_code) {
488     // Assert that we do not have a cons or slice (indirect strings) here.
489     // Sequential strings have already been ruled out.
490     __ Tst(result, kIsIndirectStringMask);
491     __ Assert(eq, kExternalStringExpectedButNotFound);
492   }
493   // Rule out short external strings.
494   STATIC_ASSERT(kShortExternalStringTag != 0);
495   // TestAndBranchIfAnySet can emit Tbnz. Do not use it because call_runtime
496   // can be bound far away in deferred code.
497   __ Tst(result, kShortExternalStringMask);
498   __ B(ne, call_runtime);
499   __ Ldr(string, FieldMemOperand(string, ExternalString::kResourceDataOffset));
500 
501   Label one_byte, done;
502   __ Bind(&check_encoding);
503   STATIC_ASSERT(kTwoByteStringTag == 0);
504   __ TestAndBranchIfAnySet(result, kStringEncodingMask, &one_byte);
505   // Two-byte string.
506   __ Ldrh(result, MemOperand(string, index, SXTW, 1));
507   __ B(&done);
508   __ Bind(&one_byte);
509   // One-byte string.
510   __ Ldrb(result, MemOperand(string, index, SXTW));
511   __ Bind(&done);
512 }
513 
514 
ExpConstant(Register base,int index)515 static MemOperand ExpConstant(Register base, int index) {
516   return MemOperand(base, index * kDoubleSize);
517 }
518 
519 
EmitMathExp(MacroAssembler * masm,DoubleRegister input,DoubleRegister result,DoubleRegister double_temp1,DoubleRegister double_temp2,Register temp1,Register temp2,Register temp3)520 void MathExpGenerator::EmitMathExp(MacroAssembler* masm,
521                                    DoubleRegister input,
522                                    DoubleRegister result,
523                                    DoubleRegister double_temp1,
524                                    DoubleRegister double_temp2,
525                                    Register temp1,
526                                    Register temp2,
527                                    Register temp3) {
528   // TODO(jbramley): There are several instances where fnmsub could be used
529   // instead of fmul and fsub. Doing this changes the result, but since this is
530   // an estimation anyway, does it matter?
531 
532   DCHECK(!AreAliased(input, result,
533                      double_temp1, double_temp2,
534                      temp1, temp2, temp3));
535   DCHECK(ExternalReference::math_exp_constants(0).address() != NULL);
536   DCHECK(!masm->serializer_enabled());  // External references not serializable.
537 
538   Label done;
539   DoubleRegister double_temp3 = result;
540   Register constants = temp3;
541 
542   // The algorithm used relies on some magic constants which are initialized in
543   // ExternalReference::InitializeMathExpData().
544 
545   // Load the address of the start of the array.
546   __ Mov(constants, ExternalReference::math_exp_constants(0));
547 
548   // We have to do a four-way split here:
549   //  - If input <= about -708.4, the output always rounds to zero.
550   //  - If input >= about 709.8, the output always rounds to +infinity.
551   //  - If the input is NaN, the output is NaN.
552   //  - Otherwise, the result needs to be calculated.
553   Label result_is_finite_non_zero;
554   // Assert that we can load offset 0 (the small input threshold) and offset 1
555   // (the large input threshold) with a single ldp.
556   DCHECK(kDRegSize == (ExpConstant(constants, 1).offset() -
557                               ExpConstant(constants, 0).offset()));
558   __ Ldp(double_temp1, double_temp2, ExpConstant(constants, 0));
559 
560   __ Fcmp(input, double_temp1);
561   __ Fccmp(input, double_temp2, NoFlag, hi);
562   // At this point, the condition flags can be in one of five states:
563   //  NZCV
564   //  1000      -708.4 < input < 709.8    result = exp(input)
565   //  0110      input == 709.8            result = +infinity
566   //  0010      input > 709.8             result = +infinity
567   //  0011      input is NaN              result = input
568   //  0000      input <= -708.4           result = +0.0
569 
570   // Continue the common case first. 'mi' tests N == 1.
571   __ B(&result_is_finite_non_zero, mi);
572 
573   // TODO(jbramley): Consider adding a +infinity register for ARM64.
574   __ Ldr(double_temp2, ExpConstant(constants, 2));    // Synthesize +infinity.
575 
576   // Select between +0.0 and +infinity. 'lo' tests C == 0.
577   __ Fcsel(result, fp_zero, double_temp2, lo);
578   // Select between {+0.0 or +infinity} and input. 'vc' tests V == 0.
579   __ Fcsel(result, result, input, vc);
580   __ B(&done);
581 
582   // The rest is magic, as described in InitializeMathExpData().
583   __ Bind(&result_is_finite_non_zero);
584 
585   // Assert that we can load offset 3 and offset 4 with a single ldp.
586   DCHECK(kDRegSize == (ExpConstant(constants, 4).offset() -
587                               ExpConstant(constants, 3).offset()));
588   __ Ldp(double_temp1, double_temp3, ExpConstant(constants, 3));
589   __ Fmadd(double_temp1, double_temp1, input, double_temp3);
590   __ Fmov(temp2.W(), double_temp1.S());
591   __ Fsub(double_temp1, double_temp1, double_temp3);
592 
593   // Assert that we can load offset 5 and offset 6 with a single ldp.
594   DCHECK(kDRegSize == (ExpConstant(constants, 6).offset() -
595                               ExpConstant(constants, 5).offset()));
596   __ Ldp(double_temp2, double_temp3, ExpConstant(constants, 5));
597   // TODO(jbramley): Consider using Fnmsub here.
598   __ Fmul(double_temp1, double_temp1, double_temp2);
599   __ Fsub(double_temp1, double_temp1, input);
600 
601   __ Fmul(double_temp2, double_temp1, double_temp1);
602   __ Fsub(double_temp3, double_temp3, double_temp1);
603   __ Fmul(double_temp3, double_temp3, double_temp2);
604 
605   __ Mov(temp1.W(), Operand(temp2.W(), LSR, 11));
606 
607   __ Ldr(double_temp2, ExpConstant(constants, 7));
608   // TODO(jbramley): Consider using Fnmsub here.
609   __ Fmul(double_temp3, double_temp3, double_temp2);
610   __ Fsub(double_temp3, double_temp3, double_temp1);
611 
612   // The 8th constant is 1.0, so use an immediate move rather than a load.
613   // We can't generate a runtime assertion here as we would need to call Abort
614   // in the runtime and we don't have an Isolate when we generate this code.
615   __ Fmov(double_temp2, 1.0);
616   __ Fadd(double_temp3, double_temp3, double_temp2);
617 
618   __ And(temp2, temp2, 0x7ff);
619   __ Add(temp1, temp1, 0x3ff);
620 
621   // Do the final table lookup.
622   __ Mov(temp3, ExternalReference::math_exp_log_table());
623 
624   __ Add(temp3, temp3, Operand(temp2, LSL, kDRegSizeLog2));
625   __ Ldp(temp2.W(), temp3.W(), MemOperand(temp3));
626   __ Orr(temp1.W(), temp3.W(), Operand(temp1.W(), LSL, 20));
627   __ Bfi(temp2, temp1, 32, 32);
628   __ Fmov(double_temp1, temp2);
629 
630   __ Fmul(result, double_temp3, double_temp1);
631 
632   __ Bind(&done);
633 }
634 
635 #undef __
636 
637 }  // namespace internal
638 }  // namespace v8
639 
640 #endif  // V8_TARGET_ARCH_ARM64
641