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 #ifndef V8_ARM64_CODE_STUBS_ARM64_H_ 6 #define V8_ARM64_CODE_STUBS_ARM64_H_ 7 8 namespace v8 { 9 namespace internal { 10 11 12 void ArrayNativeCode(MacroAssembler* masm, Label* call_generic_code); 13 14 15 class StringHelper : public AllStatic { 16 public: 17 // Compares two flat one-byte strings and returns result in x0. 18 static void GenerateCompareFlatOneByteStrings( 19 MacroAssembler* masm, Register left, Register right, Register scratch1, 20 Register scratch2, Register scratch3, Register scratch4); 21 22 // Compare two flat one-byte strings for equality and returns result in x0. 23 static void GenerateFlatOneByteStringEquals(MacroAssembler* masm, 24 Register left, Register right, 25 Register scratch1, 26 Register scratch2, 27 Register scratch3); 28 29 private: 30 static void GenerateOneByteCharsCompareLoop( 31 MacroAssembler* masm, Register left, Register right, Register length, 32 Register scratch1, Register scratch2, Label* chars_not_equal); 33 34 DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper); 35 }; 36 37 38 class StoreRegistersStateStub: public PlatformCodeStub { 39 public: StoreRegistersStateStub(Isolate * isolate)40 explicit StoreRegistersStateStub(Isolate* isolate) 41 : PlatformCodeStub(isolate) {} 42 to_be_pushed_lr()43 static Register to_be_pushed_lr() { return ip0; } 44 45 static void GenerateAheadOfTime(Isolate* isolate); 46 47 private: 48 DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); 49 DEFINE_PLATFORM_CODE_STUB(StoreRegistersState, PlatformCodeStub); 50 }; 51 52 53 class RestoreRegistersStateStub: public PlatformCodeStub { 54 public: RestoreRegistersStateStub(Isolate * isolate)55 explicit RestoreRegistersStateStub(Isolate* isolate) 56 : PlatformCodeStub(isolate) {} 57 58 static void GenerateAheadOfTime(Isolate* isolate); 59 60 private: 61 DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); 62 DEFINE_PLATFORM_CODE_STUB(RestoreRegistersState, PlatformCodeStub); 63 }; 64 65 66 class RecordWriteStub: public PlatformCodeStub { 67 public: 68 // Stub to record the write of 'value' at 'address' in 'object'. 69 // Typically 'address' = 'object' + <some offset>. 70 // See MacroAssembler::RecordWriteField() for example. RecordWriteStub(Isolate * isolate,Register object,Register value,Register address,RememberedSetAction remembered_set_action,SaveFPRegsMode fp_mode)71 RecordWriteStub(Isolate* isolate, 72 Register object, 73 Register value, 74 Register address, 75 RememberedSetAction remembered_set_action, 76 SaveFPRegsMode fp_mode) 77 : PlatformCodeStub(isolate), 78 regs_(object, // An input reg. 79 address, // An input reg. 80 value) { // One scratch reg. 81 DCHECK(object.Is64Bits()); 82 DCHECK(value.Is64Bits()); 83 DCHECK(address.Is64Bits()); 84 minor_key_ = ObjectBits::encode(object.code()) | 85 ValueBits::encode(value.code()) | 86 AddressBits::encode(address.code()) | 87 RememberedSetActionBits::encode(remembered_set_action) | 88 SaveFPRegsModeBits::encode(fp_mode); 89 } 90 RecordWriteStub(uint32_t key,Isolate * isolate)91 RecordWriteStub(uint32_t key, Isolate* isolate) 92 : PlatformCodeStub(key, isolate), regs_(object(), address(), value()) {} 93 94 enum Mode { 95 STORE_BUFFER_ONLY, 96 INCREMENTAL, 97 INCREMENTAL_COMPACTION 98 }; 99 SometimesSetsUpAFrame()100 bool SometimesSetsUpAFrame() override { return false; } 101 GetMode(Code * stub)102 static Mode GetMode(Code* stub) { 103 // Find the mode depending on the first two instructions. 104 Instruction* instr1 = 105 reinterpret_cast<Instruction*>(stub->instruction_start()); 106 Instruction* instr2 = instr1->following(); 107 108 if (instr1->IsUncondBranchImm()) { 109 DCHECK(instr2->IsPCRelAddressing() && (instr2->Rd() == xzr.code())); 110 return INCREMENTAL; 111 } 112 113 DCHECK(instr1->IsPCRelAddressing() && (instr1->Rd() == xzr.code())); 114 115 if (instr2->IsUncondBranchImm()) { 116 return INCREMENTAL_COMPACTION; 117 } 118 119 DCHECK(instr2->IsPCRelAddressing()); 120 121 return STORE_BUFFER_ONLY; 122 } 123 124 // We patch the two first instructions of the stub back and forth between an 125 // adr and branch when we start and stop incremental heap marking. 126 // The branch is 127 // b label 128 // The adr is 129 // adr xzr label 130 // so effectively a nop. Patch(Code * stub,Mode mode)131 static void Patch(Code* stub, Mode mode) { 132 // We are going to patch the two first instructions of the stub. 133 PatchingAssembler patcher( 134 stub->GetIsolate(), 135 reinterpret_cast<Instruction*>(stub->instruction_start()), 2); 136 Instruction* instr1 = patcher.InstructionAt(0); 137 Instruction* instr2 = patcher.InstructionAt(kInstructionSize); 138 // Instructions must be either 'adr' or 'b'. 139 DCHECK(instr1->IsPCRelAddressing() || instr1->IsUncondBranchImm()); 140 DCHECK(instr2->IsPCRelAddressing() || instr2->IsUncondBranchImm()); 141 // Retrieve the offsets to the labels. 142 auto offset_to_incremental_noncompacting = 143 static_cast<int32_t>(instr1->ImmPCOffset()); 144 auto offset_to_incremental_compacting = 145 static_cast<int32_t>(instr2->ImmPCOffset()); 146 147 switch (mode) { 148 case STORE_BUFFER_ONLY: 149 DCHECK(GetMode(stub) == INCREMENTAL || 150 GetMode(stub) == INCREMENTAL_COMPACTION); 151 patcher.adr(xzr, offset_to_incremental_noncompacting); 152 patcher.adr(xzr, offset_to_incremental_compacting); 153 break; 154 case INCREMENTAL: 155 DCHECK(GetMode(stub) == STORE_BUFFER_ONLY); 156 patcher.b(offset_to_incremental_noncompacting >> kInstructionSizeLog2); 157 patcher.adr(xzr, offset_to_incremental_compacting); 158 break; 159 case INCREMENTAL_COMPACTION: 160 DCHECK(GetMode(stub) == STORE_BUFFER_ONLY); 161 patcher.adr(xzr, offset_to_incremental_noncompacting); 162 patcher.b(offset_to_incremental_compacting >> kInstructionSizeLog2); 163 break; 164 } 165 DCHECK(GetMode(stub) == mode); 166 } 167 168 DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); 169 170 private: 171 // This is a helper class to manage the registers associated with the stub. 172 // The 'object' and 'address' registers must be preserved. 173 class RegisterAllocation { 174 public: RegisterAllocation(Register object,Register address,Register scratch)175 RegisterAllocation(Register object, 176 Register address, 177 Register scratch) 178 : object_(object), 179 address_(address), 180 scratch0_(scratch), 181 saved_regs_(kCallerSaved), 182 saved_fp_regs_(kCallerSavedFP) { 183 DCHECK(!AreAliased(scratch, object, address)); 184 185 // The SaveCallerSaveRegisters method needs to save caller-saved 186 // registers, but we don't bother saving MacroAssembler scratch registers. 187 saved_regs_.Remove(MacroAssembler::DefaultTmpList()); 188 saved_fp_regs_.Remove(MacroAssembler::DefaultFPTmpList()); 189 190 // We would like to require more scratch registers for this stub, 191 // but the number of registers comes down to the ones used in 192 // FullCodeGen::SetVar(), which is architecture independent. 193 // We allocate 2 extra scratch registers that we'll save on the stack. 194 CPURegList pool_available = GetValidRegistersForAllocation(); 195 CPURegList used_regs(object, address, scratch); 196 pool_available.Remove(used_regs); 197 scratch1_ = Register(pool_available.PopLowestIndex()); 198 scratch2_ = Register(pool_available.PopLowestIndex()); 199 200 // The scratch registers will be restored by other means so we don't need 201 // to save them with the other caller saved registers. 202 saved_regs_.Remove(scratch0_); 203 saved_regs_.Remove(scratch1_); 204 saved_regs_.Remove(scratch2_); 205 } 206 Save(MacroAssembler * masm)207 void Save(MacroAssembler* masm) { 208 // We don't have to save scratch0_ because it was given to us as 209 // a scratch register. 210 masm->Push(scratch1_, scratch2_); 211 } 212 Restore(MacroAssembler * masm)213 void Restore(MacroAssembler* masm) { 214 masm->Pop(scratch2_, scratch1_); 215 } 216 217 // If we have to call into C then we need to save and restore all caller- 218 // saved registers that were not already preserved. SaveCallerSaveRegisters(MacroAssembler * masm,SaveFPRegsMode mode)219 void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) { 220 // TODO(all): This can be very expensive, and it is likely that not every 221 // register will need to be preserved. Can we improve this? 222 masm->PushCPURegList(saved_regs_); 223 if (mode == kSaveFPRegs) { 224 masm->PushCPURegList(saved_fp_regs_); 225 } 226 } 227 RestoreCallerSaveRegisters(MacroAssembler * masm,SaveFPRegsMode mode)228 void RestoreCallerSaveRegisters(MacroAssembler*masm, SaveFPRegsMode mode) { 229 // TODO(all): This can be very expensive, and it is likely that not every 230 // register will need to be preserved. Can we improve this? 231 if (mode == kSaveFPRegs) { 232 masm->PopCPURegList(saved_fp_regs_); 233 } 234 masm->PopCPURegList(saved_regs_); 235 } 236 object()237 Register object() { return object_; } address()238 Register address() { return address_; } scratch0()239 Register scratch0() { return scratch0_; } scratch1()240 Register scratch1() { return scratch1_; } scratch2()241 Register scratch2() { return scratch2_; } 242 243 private: 244 Register object_; 245 Register address_; 246 Register scratch0_; 247 Register scratch1_; 248 Register scratch2_; 249 CPURegList saved_regs_; 250 CPURegList saved_fp_regs_; 251 252 // TODO(all): We should consider moving this somewhere else. GetValidRegistersForAllocation()253 static CPURegList GetValidRegistersForAllocation() { 254 // The list of valid registers for allocation is defined as all the 255 // registers without those with a special meaning. 256 // 257 // The default list excludes registers x26 to x31 because they are 258 // reserved for the following purpose: 259 // - x26 root register 260 // - x27 context pointer register 261 // - x28 jssp 262 // - x29 frame pointer 263 // - x30 link register(lr) 264 // - x31 xzr/stack pointer 265 CPURegList list(CPURegister::kRegister, kXRegSizeInBits, 0, 25); 266 267 // We also remove MacroAssembler's scratch registers. 268 list.Remove(MacroAssembler::DefaultTmpList()); 269 270 return list; 271 } 272 273 friend class RecordWriteStub; 274 }; 275 276 enum OnNoNeedToInformIncrementalMarker { 277 kReturnOnNoNeedToInformIncrementalMarker, 278 kUpdateRememberedSetOnNoNeedToInformIncrementalMarker 279 }; 280 MajorKey()281 inline Major MajorKey() const final { return RecordWrite; } 282 283 void Generate(MacroAssembler* masm) override; 284 void GenerateIncremental(MacroAssembler* masm, Mode mode); 285 void CheckNeedsToInformIncrementalMarker( 286 MacroAssembler* masm, 287 OnNoNeedToInformIncrementalMarker on_no_need, 288 Mode mode); 289 void InformIncrementalMarker(MacroAssembler* masm); 290 Activate(Code * code)291 void Activate(Code* code) override { 292 code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code); 293 } 294 object()295 Register object() const { 296 return Register::from_code(ObjectBits::decode(minor_key_)); 297 } 298 value()299 Register value() const { 300 return Register::from_code(ValueBits::decode(minor_key_)); 301 } 302 address()303 Register address() const { 304 return Register::from_code(AddressBits::decode(minor_key_)); 305 } 306 remembered_set_action()307 RememberedSetAction remembered_set_action() const { 308 return RememberedSetActionBits::decode(minor_key_); 309 } 310 save_fp_regs_mode()311 SaveFPRegsMode save_fp_regs_mode() const { 312 return SaveFPRegsModeBits::decode(minor_key_); 313 } 314 315 class ObjectBits: public BitField<int, 0, 5> {}; 316 class ValueBits: public BitField<int, 5, 5> {}; 317 class AddressBits: public BitField<int, 10, 5> {}; 318 class RememberedSetActionBits: public BitField<RememberedSetAction, 15, 1> {}; 319 class SaveFPRegsModeBits: public BitField<SaveFPRegsMode, 16, 1> {}; 320 321 Label slow_; 322 RegisterAllocation regs_; 323 }; 324 325 326 // Helper to call C++ functions from generated code. The caller must prepare 327 // the exit frame before doing the call with GenerateCall. 328 class DirectCEntryStub: public PlatformCodeStub { 329 public: DirectCEntryStub(Isolate * isolate)330 explicit DirectCEntryStub(Isolate* isolate) : PlatformCodeStub(isolate) {} 331 void GenerateCall(MacroAssembler* masm, Register target); 332 333 private: NeedsImmovableCode()334 bool NeedsImmovableCode() override { return true; } 335 336 DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); 337 DEFINE_PLATFORM_CODE_STUB(DirectCEntry, PlatformCodeStub); 338 }; 339 340 341 class NameDictionaryLookupStub: public PlatformCodeStub { 342 public: 343 enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP }; 344 NameDictionaryLookupStub(Isolate * isolate,LookupMode mode)345 NameDictionaryLookupStub(Isolate* isolate, LookupMode mode) 346 : PlatformCodeStub(isolate) { 347 minor_key_ = LookupModeBits::encode(mode); 348 } 349 350 static void GenerateNegativeLookup(MacroAssembler* masm, 351 Label* miss, 352 Label* done, 353 Register receiver, 354 Register properties, 355 Handle<Name> name, 356 Register scratch0); 357 358 static void GeneratePositiveLookup(MacroAssembler* masm, 359 Label* miss, 360 Label* done, 361 Register elements, 362 Register name, 363 Register scratch1, 364 Register scratch2); 365 SometimesSetsUpAFrame()366 bool SometimesSetsUpAFrame() override { return false; } 367 368 private: 369 static const int kInlinedProbes = 4; 370 static const int kTotalProbes = 20; 371 372 static const int kCapacityOffset = 373 NameDictionary::kHeaderSize + 374 NameDictionary::kCapacityIndex * kPointerSize; 375 376 static const int kElementsStartOffset = 377 NameDictionary::kHeaderSize + 378 NameDictionary::kElementsStartIndex * kPointerSize; 379 mode()380 LookupMode mode() const { return LookupModeBits::decode(minor_key_); } 381 382 class LookupModeBits: public BitField<LookupMode, 0, 1> {}; 383 384 DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); 385 DEFINE_PLATFORM_CODE_STUB(NameDictionaryLookup, PlatformCodeStub); 386 }; 387 388 } // namespace internal 389 } // namespace v8 390 391 #endif // V8_ARM64_CODE_STUBS_ARM64_H_ 392