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 virtual bool SometimesSetsUpAFrame() { 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 reinterpret_cast<Instruction*>(stub->instruction_start()), 2); 135 Instruction* instr1 = patcher.InstructionAt(0); 136 Instruction* instr2 = patcher.InstructionAt(kInstructionSize); 137 // Instructions must be either 'adr' or 'b'. 138 DCHECK(instr1->IsPCRelAddressing() || instr1->IsUncondBranchImm()); 139 DCHECK(instr2->IsPCRelAddressing() || instr2->IsUncondBranchImm()); 140 // Retrieve the offsets to the labels. 141 int32_t offset_to_incremental_noncompacting = instr1->ImmPCOffset(); 142 int32_t offset_to_incremental_compacting = instr2->ImmPCOffset(); 143 144 switch (mode) { 145 case STORE_BUFFER_ONLY: 146 DCHECK(GetMode(stub) == INCREMENTAL || 147 GetMode(stub) == INCREMENTAL_COMPACTION); 148 patcher.adr(xzr, offset_to_incremental_noncompacting); 149 patcher.adr(xzr, offset_to_incremental_compacting); 150 break; 151 case INCREMENTAL: 152 DCHECK(GetMode(stub) == STORE_BUFFER_ONLY); 153 patcher.b(offset_to_incremental_noncompacting >> kInstructionSizeLog2); 154 patcher.adr(xzr, offset_to_incremental_compacting); 155 break; 156 case INCREMENTAL_COMPACTION: 157 DCHECK(GetMode(stub) == STORE_BUFFER_ONLY); 158 patcher.adr(xzr, offset_to_incremental_noncompacting); 159 patcher.b(offset_to_incremental_compacting >> kInstructionSizeLog2); 160 break; 161 } 162 DCHECK(GetMode(stub) == mode); 163 } 164 165 DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); 166 167 private: 168 // This is a helper class to manage the registers associated with the stub. 169 // The 'object' and 'address' registers must be preserved. 170 class RegisterAllocation { 171 public: RegisterAllocation(Register object,Register address,Register scratch)172 RegisterAllocation(Register object, 173 Register address, 174 Register scratch) 175 : object_(object), 176 address_(address), 177 scratch0_(scratch), 178 saved_regs_(kCallerSaved), 179 saved_fp_regs_(kCallerSavedFP) { 180 DCHECK(!AreAliased(scratch, object, address)); 181 182 // The SaveCallerSaveRegisters method needs to save caller-saved 183 // registers, but we don't bother saving MacroAssembler scratch registers. 184 saved_regs_.Remove(MacroAssembler::DefaultTmpList()); 185 saved_fp_regs_.Remove(MacroAssembler::DefaultFPTmpList()); 186 187 // We would like to require more scratch registers for this stub, 188 // but the number of registers comes down to the ones used in 189 // FullCodeGen::SetVar(), which is architecture independent. 190 // We allocate 2 extra scratch registers that we'll save on the stack. 191 CPURegList pool_available = GetValidRegistersForAllocation(); 192 CPURegList used_regs(object, address, scratch); 193 pool_available.Remove(used_regs); 194 scratch1_ = Register(pool_available.PopLowestIndex()); 195 scratch2_ = Register(pool_available.PopLowestIndex()); 196 197 // The scratch registers will be restored by other means so we don't need 198 // to save them with the other caller saved registers. 199 saved_regs_.Remove(scratch0_); 200 saved_regs_.Remove(scratch1_); 201 saved_regs_.Remove(scratch2_); 202 } 203 Save(MacroAssembler * masm)204 void Save(MacroAssembler* masm) { 205 // We don't have to save scratch0_ because it was given to us as 206 // a scratch register. 207 masm->Push(scratch1_, scratch2_); 208 } 209 Restore(MacroAssembler * masm)210 void Restore(MacroAssembler* masm) { 211 masm->Pop(scratch2_, scratch1_); 212 } 213 214 // If we have to call into C then we need to save and restore all caller- 215 // saved registers that were not already preserved. SaveCallerSaveRegisters(MacroAssembler * masm,SaveFPRegsMode mode)216 void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) { 217 // TODO(all): This can be very expensive, and it is likely that not every 218 // register will need to be preserved. Can we improve this? 219 masm->PushCPURegList(saved_regs_); 220 if (mode == kSaveFPRegs) { 221 masm->PushCPURegList(saved_fp_regs_); 222 } 223 } 224 RestoreCallerSaveRegisters(MacroAssembler * masm,SaveFPRegsMode mode)225 void RestoreCallerSaveRegisters(MacroAssembler*masm, SaveFPRegsMode mode) { 226 // TODO(all): This can be very expensive, and it is likely that not every 227 // register will need to be preserved. Can we improve this? 228 if (mode == kSaveFPRegs) { 229 masm->PopCPURegList(saved_fp_regs_); 230 } 231 masm->PopCPURegList(saved_regs_); 232 } 233 object()234 Register object() { return object_; } address()235 Register address() { return address_; } scratch0()236 Register scratch0() { return scratch0_; } scratch1()237 Register scratch1() { return scratch1_; } scratch2()238 Register scratch2() { return scratch2_; } 239 240 private: 241 Register object_; 242 Register address_; 243 Register scratch0_; 244 Register scratch1_; 245 Register scratch2_; 246 CPURegList saved_regs_; 247 CPURegList saved_fp_regs_; 248 249 // TODO(all): We should consider moving this somewhere else. GetValidRegistersForAllocation()250 static CPURegList GetValidRegistersForAllocation() { 251 // The list of valid registers for allocation is defined as all the 252 // registers without those with a special meaning. 253 // 254 // The default list excludes registers x26 to x31 because they are 255 // reserved for the following purpose: 256 // - x26 root register 257 // - x27 context pointer register 258 // - x28 jssp 259 // - x29 frame pointer 260 // - x30 link register(lr) 261 // - x31 xzr/stack pointer 262 CPURegList list(CPURegister::kRegister, kXRegSizeInBits, 0, 25); 263 264 // We also remove MacroAssembler's scratch registers. 265 list.Remove(MacroAssembler::DefaultTmpList()); 266 267 return list; 268 } 269 270 friend class RecordWriteStub; 271 }; 272 273 enum OnNoNeedToInformIncrementalMarker { 274 kReturnOnNoNeedToInformIncrementalMarker, 275 kUpdateRememberedSetOnNoNeedToInformIncrementalMarker 276 }; 277 MajorKey()278 virtual inline Major MajorKey() const FINAL OVERRIDE { return RecordWrite; } 279 280 virtual void Generate(MacroAssembler* masm) OVERRIDE; 281 void GenerateIncremental(MacroAssembler* masm, Mode mode); 282 void CheckNeedsToInformIncrementalMarker( 283 MacroAssembler* masm, 284 OnNoNeedToInformIncrementalMarker on_no_need, 285 Mode mode); 286 void InformIncrementalMarker(MacroAssembler* masm); 287 Activate(Code * code)288 void Activate(Code* code) { 289 code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code); 290 } 291 object()292 Register object() const { 293 return Register::from_code(ObjectBits::decode(minor_key_)); 294 } 295 value()296 Register value() const { 297 return Register::from_code(ValueBits::decode(minor_key_)); 298 } 299 address()300 Register address() const { 301 return Register::from_code(AddressBits::decode(minor_key_)); 302 } 303 remembered_set_action()304 RememberedSetAction remembered_set_action() const { 305 return RememberedSetActionBits::decode(minor_key_); 306 } 307 save_fp_regs_mode()308 SaveFPRegsMode save_fp_regs_mode() const { 309 return SaveFPRegsModeBits::decode(minor_key_); 310 } 311 312 class ObjectBits: public BitField<int, 0, 5> {}; 313 class ValueBits: public BitField<int, 5, 5> {}; 314 class AddressBits: public BitField<int, 10, 5> {}; 315 class RememberedSetActionBits: public BitField<RememberedSetAction, 15, 1> {}; 316 class SaveFPRegsModeBits: public BitField<SaveFPRegsMode, 16, 1> {}; 317 318 Label slow_; 319 RegisterAllocation regs_; 320 }; 321 322 323 // Helper to call C++ functions from generated code. The caller must prepare 324 // the exit frame before doing the call with GenerateCall. 325 class DirectCEntryStub: public PlatformCodeStub { 326 public: DirectCEntryStub(Isolate * isolate)327 explicit DirectCEntryStub(Isolate* isolate) : PlatformCodeStub(isolate) {} 328 void GenerateCall(MacroAssembler* masm, Register target); 329 330 private: NeedsImmovableCode()331 bool NeedsImmovableCode() { return true; } 332 333 DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); 334 DEFINE_PLATFORM_CODE_STUB(DirectCEntry, PlatformCodeStub); 335 }; 336 337 338 class NameDictionaryLookupStub: public PlatformCodeStub { 339 public: 340 enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP }; 341 NameDictionaryLookupStub(Isolate * isolate,LookupMode mode)342 NameDictionaryLookupStub(Isolate* isolate, LookupMode mode) 343 : PlatformCodeStub(isolate) { 344 minor_key_ = LookupModeBits::encode(mode); 345 } 346 347 static void GenerateNegativeLookup(MacroAssembler* masm, 348 Label* miss, 349 Label* done, 350 Register receiver, 351 Register properties, 352 Handle<Name> name, 353 Register scratch0); 354 355 static void GeneratePositiveLookup(MacroAssembler* masm, 356 Label* miss, 357 Label* done, 358 Register elements, 359 Register name, 360 Register scratch1, 361 Register scratch2); 362 SometimesSetsUpAFrame()363 virtual bool SometimesSetsUpAFrame() { return false; } 364 365 private: 366 static const int kInlinedProbes = 4; 367 static const int kTotalProbes = 20; 368 369 static const int kCapacityOffset = 370 NameDictionary::kHeaderSize + 371 NameDictionary::kCapacityIndex * kPointerSize; 372 373 static const int kElementsStartOffset = 374 NameDictionary::kHeaderSize + 375 NameDictionary::kElementsStartIndex * kPointerSize; 376 mode()377 LookupMode mode() const { return LookupModeBits::decode(minor_key_); } 378 379 class LookupModeBits: public BitField<LookupMode, 0, 1> {}; 380 381 DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); 382 DEFINE_PLATFORM_CODE_STUB(NameDictionaryLookup, PlatformCodeStub); 383 }; 384 385 } } // namespace v8::internal 386 387 #endif // V8_ARM64_CODE_STUBS_ARM64_H_ 388