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 UnaryMathFunctionWithIsolate CreateSqrtFunction(Isolate* isolate) { 19 return nullptr; 20 } 21 22 23 // ------------------------------------------------------------------------- 24 // Platform-specific RuntimeCallHelper functions. 25 26 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 33 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 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 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 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 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 331 bool CodeAgingHelper::IsOld(byte* candidate) const { 332 return memcmp(candidate, old_sequence_.start(), kCodeAgeStubEntryOffset) == 0; 333 } 334 #endif 335 336 337 bool Code::IsYoungSequence(Isolate* isolate, byte* sequence) { 338 return MacroAssembler::IsYoungSequence(isolate, sequence); 339 } 340 341 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 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 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