• 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 
CreateSqrtFunction(Isolate * isolate)18 UnaryMathFunctionWithIsolate CreateSqrtFunction(Isolate* isolate) {
19   return nullptr;
20 }
21 
22 
23 // -------------------------------------------------------------------------
24 // Platform-specific RuntimeCallHelper functions.
25 
BeforeCall(MacroAssembler * masm) const26 void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
27   masm->EnterFrame(StackFrame::INTERNAL);
28   DCHECK(!masm->has_frame());
29   masm->set_has_frame(true);
30 }
31 
32 
AfterCall(MacroAssembler * masm) const33 void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
34   masm->LeaveFrame(StackFrame::INTERNAL);
35   DCHECK(masm->has_frame());
36   masm->set_has_frame(false);
37 }
38 
39 
40 // -------------------------------------------------------------------------
41 // Code generators
42 
GenerateMapChangeElementsTransition(MacroAssembler * masm,Register receiver,Register key,Register value,Register target_map,AllocationSiteMode mode,Label * allocation_memento_found)43 void ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
44     MacroAssembler* masm,
45     Register receiver,
46     Register key,
47     Register value,
48     Register target_map,
49     AllocationSiteMode mode,
50     Label* allocation_memento_found) {
51   ASM_LOCATION(
52       "ElementsTransitionGenerator::GenerateMapChangeElementsTransition");
53   DCHECK(!AreAliased(receiver, key, value, target_map));
54 
55   if (mode == TRACK_ALLOCATION_SITE) {
56     DCHECK(allocation_memento_found != NULL);
57     __ JumpIfJSArrayHasAllocationMemento(receiver, x10, x11,
58                                          allocation_memento_found);
59   }
60 
61   // Set transitioned map.
62   __ Str(target_map, FieldMemOperand(receiver, HeapObject::kMapOffset));
63   __ RecordWriteField(receiver,
64                       HeapObject::kMapOffset,
65                       target_map,
66                       x10,
67                       kLRHasNotBeenSaved,
68                       kDontSaveFPRegs,
69                       EMIT_REMEMBERED_SET,
70                       OMIT_SMI_CHECK);
71 }
72 
73 
GenerateSmiToDouble(MacroAssembler * masm,Register receiver,Register key,Register value,Register target_map,AllocationSiteMode mode,Label * fail)74 void ElementsTransitionGenerator::GenerateSmiToDouble(
75     MacroAssembler* masm,
76     Register receiver,
77     Register key,
78     Register value,
79     Register target_map,
80     AllocationSiteMode mode,
81     Label* fail) {
82   ASM_LOCATION("ElementsTransitionGenerator::GenerateSmiToDouble");
83   Label gc_required, only_change_map;
84   Register elements = x4;
85   Register length = x5;
86   Register array_size = x6;
87   Register array = x7;
88 
89   Register scratch = x6;
90 
91   // Verify input registers don't conflict with locals.
92   DCHECK(!AreAliased(receiver, key, value, target_map,
93                      elements, length, array_size, array));
94 
95   if (mode == TRACK_ALLOCATION_SITE) {
96     __ JumpIfJSArrayHasAllocationMemento(receiver, x10, x11, fail);
97   }
98 
99   // Check for empty arrays, which only require a map transition and no changes
100   // to the backing store.
101   __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
102   __ JumpIfRoot(elements, Heap::kEmptyFixedArrayRootIndex, &only_change_map);
103 
104   __ Push(lr);
105   __ Ldrsw(length, UntagSmiFieldMemOperand(elements,
106                                            FixedArray::kLengthOffset));
107 
108   // Allocate new FixedDoubleArray.
109   __ Lsl(array_size, length, kDoubleSizeLog2);
110   __ Add(array_size, array_size, FixedDoubleArray::kHeaderSize);
111   __ Allocate(array_size, array, x10, x11, &gc_required, DOUBLE_ALIGNMENT);
112   // Register array is non-tagged heap object.
113 
114   // Set the destination FixedDoubleArray's length and map.
115   Register map_root = array_size;
116   __ LoadRoot(map_root, Heap::kFixedDoubleArrayMapRootIndex);
117   __ SmiTag(x11, length);
118   __ Str(x11, FieldMemOperand(array, FixedDoubleArray::kLengthOffset));
119   __ Str(map_root, FieldMemOperand(array, HeapObject::kMapOffset));
120 
121   __ Str(target_map, FieldMemOperand(receiver, HeapObject::kMapOffset));
122   __ RecordWriteField(receiver, HeapObject::kMapOffset, target_map, scratch,
123                       kLRHasBeenSaved, kDontSaveFPRegs, OMIT_REMEMBERED_SET,
124                       OMIT_SMI_CHECK);
125 
126   // Replace receiver's backing store with newly created FixedDoubleArray.
127   __ Move(x10, array);
128   __ Str(array, FieldMemOperand(receiver, JSObject::kElementsOffset));
129   __ RecordWriteField(receiver, JSObject::kElementsOffset, x10, scratch,
130                       kLRHasBeenSaved, kDontSaveFPRegs, EMIT_REMEMBERED_SET,
131                       OMIT_SMI_CHECK);
132 
133   // Prepare for conversion loop.
134   Register src_elements = x10;
135   Register dst_elements = x11;
136   Register dst_end = x12;
137   __ Add(src_elements, elements, FixedArray::kHeaderSize - kHeapObjectTag);
138   __ Add(dst_elements, array, FixedDoubleArray::kHeaderSize - kHeapObjectTag);
139   __ Add(dst_end, dst_elements, Operand(length, LSL, kDoubleSizeLog2));
140 
141   FPRegister nan_d = d1;
142   __ Fmov(nan_d, rawbits_to_double(kHoleNanInt64));
143 
144   Label entry, done;
145   __ B(&entry);
146 
147   __ Bind(&only_change_map);
148   __ Str(target_map, FieldMemOperand(receiver, HeapObject::kMapOffset));
149   __ RecordWriteField(receiver, HeapObject::kMapOffset, target_map, scratch,
150                       kLRHasNotBeenSaved, kDontSaveFPRegs, OMIT_REMEMBERED_SET,
151                       OMIT_SMI_CHECK);
152   __ B(&done);
153 
154   // Call into runtime if GC is required.
155   __ Bind(&gc_required);
156   __ Pop(lr);
157   __ B(fail);
158 
159   // Iterate over the array, copying and coverting smis to doubles. If an
160   // element is non-smi, write a hole to the destination.
161   {
162     Label loop;
163     __ Bind(&loop);
164     __ Ldr(x13, MemOperand(src_elements, kPointerSize, PostIndex));
165     __ SmiUntagToDouble(d0, x13, kSpeculativeUntag);
166     __ Tst(x13, kSmiTagMask);
167     __ Fcsel(d0, d0, nan_d, eq);
168     __ Str(d0, MemOperand(dst_elements, kDoubleSize, PostIndex));
169 
170     __ Bind(&entry);
171     __ Cmp(dst_elements, dst_end);
172     __ B(lt, &loop);
173   }
174 
175   __ Pop(lr);
176   __ Bind(&done);
177 }
178 
179 
GenerateDoubleToObject(MacroAssembler * masm,Register receiver,Register key,Register value,Register target_map,AllocationSiteMode mode,Label * fail)180 void ElementsTransitionGenerator::GenerateDoubleToObject(
181     MacroAssembler* masm,
182     Register receiver,
183     Register key,
184     Register value,
185     Register target_map,
186     AllocationSiteMode mode,
187     Label* fail) {
188   ASM_LOCATION("ElementsTransitionGenerator::GenerateDoubleToObject");
189   Register elements = x4;
190   Register array_size = x6;
191   Register array = x7;
192   Register length = x5;
193 
194   // Verify input registers don't conflict with locals.
195   DCHECK(!AreAliased(receiver, key, value, target_map,
196                      elements, array_size, array, length));
197 
198   if (mode == TRACK_ALLOCATION_SITE) {
199     __ JumpIfJSArrayHasAllocationMemento(receiver, x10, x11, fail);
200   }
201 
202   // Check for empty arrays, which only require a map transition and no changes
203   // to the backing store.
204   Label only_change_map;
205 
206   __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
207   __ JumpIfRoot(elements, Heap::kEmptyFixedArrayRootIndex, &only_change_map);
208 
209   __ Push(lr);
210   // TODO(all): These registers may not need to be pushed. Examine
211   // RecordWriteStub and check whether it's needed.
212   __ Push(target_map, receiver, key, value);
213   __ Ldrsw(length, UntagSmiFieldMemOperand(elements,
214                                            FixedArray::kLengthOffset));
215   // Allocate new FixedArray.
216   Label gc_required;
217   __ Mov(array_size, FixedDoubleArray::kHeaderSize);
218   __ Add(array_size, array_size, Operand(length, LSL, kPointerSizeLog2));
219   __ Allocate(array_size, array, x10, x11, &gc_required, NO_ALLOCATION_FLAGS);
220 
221   // Set destination FixedDoubleArray's length and map.
222   Register map_root = array_size;
223   __ LoadRoot(map_root, Heap::kFixedArrayMapRootIndex);
224   __ SmiTag(x11, length);
225   __ Str(x11, FieldMemOperand(array, FixedDoubleArray::kLengthOffset));
226   __ Str(map_root, FieldMemOperand(array, HeapObject::kMapOffset));
227 
228   // Prepare for conversion loop.
229   Register src_elements = x10;
230   Register dst_elements = x11;
231   Register dst_end = x12;
232   Register the_hole = x14;
233   __ LoadRoot(the_hole, Heap::kTheHoleValueRootIndex);
234   __ Add(src_elements, elements,
235          FixedDoubleArray::kHeaderSize - kHeapObjectTag);
236   __ Add(dst_elements, array, FixedArray::kHeaderSize - kHeapObjectTag);
237   __ Add(dst_end, dst_elements, Operand(length, LSL, kPointerSizeLog2));
238 
239   // Allocating heap numbers in the loop below can fail and cause a jump to
240   // gc_required. We can't leave a partly initialized FixedArray behind,
241   // so pessimistically fill it with holes now.
242   Label initialization_loop, initialization_loop_entry;
243   __ B(&initialization_loop_entry);
244   __ bind(&initialization_loop);
245   __ Str(the_hole, MemOperand(dst_elements, kPointerSize, PostIndex));
246   __ bind(&initialization_loop_entry);
247   __ Cmp(dst_elements, dst_end);
248   __ B(lt, &initialization_loop);
249 
250   __ Add(dst_elements, array, FixedArray::kHeaderSize - kHeapObjectTag);
251 
252   Register heap_num_map = x15;
253   __ LoadRoot(heap_num_map, Heap::kHeapNumberMapRootIndex);
254 
255   Label entry;
256   __ B(&entry);
257 
258   // Call into runtime if GC is required.
259   __ Bind(&gc_required);
260   __ Pop(value, key, receiver, target_map);
261   __ Pop(lr);
262   __ B(fail);
263 
264   {
265     Label loop, convert_hole;
266     __ Bind(&loop);
267     __ Ldr(x13, MemOperand(src_elements, kPointerSize, PostIndex));
268     __ Cmp(x13, kHoleNanInt64);
269     __ B(eq, &convert_hole);
270 
271     // Non-hole double, copy value into a heap number.
272     Register heap_num = length;
273     Register scratch = array_size;
274     Register scratch2 = elements;
275     __ AllocateHeapNumber(heap_num, &gc_required, scratch, scratch2,
276                           x13, heap_num_map);
277     __ Mov(x13, dst_elements);
278     __ Str(heap_num, MemOperand(dst_elements, kPointerSize, PostIndex));
279     __ RecordWrite(array, x13, heap_num, kLRHasBeenSaved, kDontSaveFPRegs,
280                    EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
281 
282     __ B(&entry);
283 
284     // Replace the-hole NaN with the-hole pointer.
285     __ Bind(&convert_hole);
286     __ Str(the_hole, MemOperand(dst_elements, kPointerSize, PostIndex));
287 
288     __ Bind(&entry);
289     __ Cmp(dst_elements, dst_end);
290     __ B(lt, &loop);
291   }
292 
293   __ Pop(value, key, receiver, target_map);
294   // Replace receiver's backing store with newly created and filled FixedArray.
295   __ Str(array, FieldMemOperand(receiver, JSObject::kElementsOffset));
296   __ RecordWriteField(receiver, JSObject::kElementsOffset, array, x13,
297                       kLRHasBeenSaved, kDontSaveFPRegs, EMIT_REMEMBERED_SET,
298                       OMIT_SMI_CHECK);
299   __ Pop(lr);
300 
301   __ Bind(&only_change_map);
302   __ Str(target_map, FieldMemOperand(receiver, HeapObject::kMapOffset));
303   __ RecordWriteField(receiver, HeapObject::kMapOffset, target_map, x13,
304                       kLRHasNotBeenSaved, kDontSaveFPRegs, OMIT_REMEMBERED_SET,
305                       OMIT_SMI_CHECK);
306 }
307 
308 
CodeAgingHelper(Isolate * isolate)309 CodeAgingHelper::CodeAgingHelper(Isolate* isolate) {
310   USE(isolate);
311   DCHECK(young_sequence_.length() == kNoCodeAgeSequenceLength);
312   // The sequence of instructions that is patched out for aging code is the
313   // following boilerplate stack-building prologue that is found both in
314   // FUNCTION and OPTIMIZED_FUNCTION code:
315   PatchingAssembler patcher(isolate, young_sequence_.start(),
316                             young_sequence_.length() / kInstructionSize);
317   // The young sequence is the frame setup code for FUNCTION code types. It is
318   // generated by FullCodeGenerator::Generate.
319   MacroAssembler::EmitFrameSetupForCodeAgePatching(&patcher);
320 
321 #ifdef DEBUG
322   const int length = kCodeAgeStubEntryOffset / kInstructionSize;
323   DCHECK(old_sequence_.length() >= kCodeAgeStubEntryOffset);
324   PatchingAssembler patcher_old(isolate, old_sequence_.start(), length);
325   MacroAssembler::EmitCodeAgeSequence(&patcher_old, NULL);
326 #endif
327 }
328 
329 
330 #ifdef DEBUG
IsOld(byte * candidate) const331 bool CodeAgingHelper::IsOld(byte* candidate) const {
332   return memcmp(candidate, old_sequence_.start(), kCodeAgeStubEntryOffset) == 0;
333 }
334 #endif
335 
336 
IsYoungSequence(Isolate * isolate,byte * sequence)337 bool Code::IsYoungSequence(Isolate* isolate, byte* sequence) {
338   return MacroAssembler::IsYoungSequence(isolate, sequence);
339 }
340 
341 
GetCodeAgeAndParity(Isolate * isolate,byte * sequence,Age * age,MarkingParity * parity)342 void Code::GetCodeAgeAndParity(Isolate* isolate, byte* sequence, Age* age,
343                                MarkingParity* parity) {
344   if (IsYoungSequence(isolate, sequence)) {
345     *age = kNoAgeCodeAge;
346     *parity = NO_MARKING_PARITY;
347   } else {
348     byte* target = sequence + kCodeAgeStubEntryOffset;
349     Code* stub = GetCodeFromTargetAddress(Memory::Address_at(target));
350     GetCodeAgeAndParity(stub, age, parity);
351   }
352 }
353 
354 
PatchPlatformCodeAge(Isolate * isolate,byte * sequence,Code::Age age,MarkingParity parity)355 void Code::PatchPlatformCodeAge(Isolate* isolate,
356                                 byte* sequence,
357                                 Code::Age age,
358                                 MarkingParity parity) {
359   PatchingAssembler patcher(isolate, sequence,
360                             kNoCodeAgeSequenceLength / kInstructionSize);
361   if (age == kNoAgeCodeAge) {
362     MacroAssembler::EmitFrameSetupForCodeAgePatching(&patcher);
363   } else {
364     Code * stub = GetCodeAgeStub(isolate, age, parity);
365     MacroAssembler::EmitCodeAgeSequence(&patcher, stub);
366   }
367 }
368 
369 
Generate(MacroAssembler * masm,Register string,Register index,Register result,Label * call_runtime)370 void StringCharLoadGenerator::Generate(MacroAssembler* masm,
371                                        Register string,
372                                        Register index,
373                                        Register result,
374                                        Label* call_runtime) {
375   DCHECK(string.Is64Bits() && index.Is32Bits() && result.Is64Bits());
376   // Fetch the instance type of the receiver into result register.
377   __ Ldr(result, FieldMemOperand(string, HeapObject::kMapOffset));
378   __ Ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
379 
380   // We need special handling for indirect strings.
381   Label check_sequential;
382   __ TestAndBranchIfAllClear(result, kIsIndirectStringMask, &check_sequential);
383 
384   // Dispatch on the indirect string shape: slice or cons.
385   Label cons_string;
386   __ TestAndBranchIfAllClear(result, kSlicedNotConsMask, &cons_string);
387 
388   // Handle slices.
389   Label indirect_string_loaded;
390   __ Ldr(result.W(),
391          UntagSmiFieldMemOperand(string, SlicedString::kOffsetOffset));
392   __ Ldr(string, FieldMemOperand(string, SlicedString::kParentOffset));
393   __ Add(index, index, result.W());
394   __ B(&indirect_string_loaded);
395 
396   // Handle cons strings.
397   // Check whether the right hand side is the empty string (i.e. if
398   // this is really a flat string in a cons string). If that is not
399   // the case we would rather go to the runtime system now to flatten
400   // the string.
401   __ Bind(&cons_string);
402   __ Ldr(result, FieldMemOperand(string, ConsString::kSecondOffset));
403   __ JumpIfNotRoot(result, Heap::kempty_stringRootIndex, call_runtime);
404   // Get the first of the two strings and load its instance type.
405   __ Ldr(string, FieldMemOperand(string, ConsString::kFirstOffset));
406 
407   __ Bind(&indirect_string_loaded);
408   __ Ldr(result, FieldMemOperand(string, HeapObject::kMapOffset));
409   __ Ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
410 
411   // Distinguish sequential and external strings. Only these two string
412   // representations can reach here (slices and flat cons strings have been
413   // reduced to the underlying sequential or external string).
414   Label external_string, check_encoding;
415   __ Bind(&check_sequential);
416   STATIC_ASSERT(kSeqStringTag == 0);
417   __ TestAndBranchIfAnySet(result, kStringRepresentationMask, &external_string);
418 
419   // Prepare sequential strings
420   STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
421   __ Add(string, string, SeqTwoByteString::kHeaderSize - kHeapObjectTag);
422   __ B(&check_encoding);
423 
424   // Handle external strings.
425   __ Bind(&external_string);
426   if (FLAG_debug_code) {
427     // Assert that we do not have a cons or slice (indirect strings) here.
428     // Sequential strings have already been ruled out.
429     __ Tst(result, kIsIndirectStringMask);
430     __ Assert(eq, kExternalStringExpectedButNotFound);
431   }
432   // Rule out short external strings.
433   STATIC_ASSERT(kShortExternalStringTag != 0);
434   // TestAndBranchIfAnySet can emit Tbnz. Do not use it because call_runtime
435   // can be bound far away in deferred code.
436   __ Tst(result, kShortExternalStringMask);
437   __ B(ne, call_runtime);
438   __ Ldr(string, FieldMemOperand(string, ExternalString::kResourceDataOffset));
439 
440   Label one_byte, done;
441   __ Bind(&check_encoding);
442   STATIC_ASSERT(kTwoByteStringTag == 0);
443   __ TestAndBranchIfAnySet(result, kStringEncodingMask, &one_byte);
444   // Two-byte string.
445   __ Ldrh(result, MemOperand(string, index, SXTW, 1));
446   __ B(&done);
447   __ Bind(&one_byte);
448   // One-byte string.
449   __ Ldrb(result, MemOperand(string, index, SXTW));
450   __ Bind(&done);
451 }
452 
453 #undef __
454 
455 }  // namespace internal
456 }  // namespace v8
457 
458 #endif  // V8_TARGET_ARCH_ARM64
459