1 // Copyright 2011 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_X64_CODE_STUBS_X64_H_ 6 #define V8_X64_CODE_STUBS_X64_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 // Generate code for copying characters using the rep movs instruction. 18 // Copies rcx characters from rsi to rdi. Copying of overlapping regions is 19 // not supported. 20 static void GenerateCopyCharacters(MacroAssembler* masm, 21 Register dest, 22 Register src, 23 Register count, 24 String::Encoding encoding); 25 26 // Compares two flat one-byte strings and returns result in rax. 27 static void GenerateCompareFlatOneByteStrings( 28 MacroAssembler* masm, Register left, Register right, Register scratch1, 29 Register scratch2, Register scratch3, Register scratch4); 30 31 // Compares two flat one-byte strings for equality and returns result in rax. 32 static void GenerateFlatOneByteStringEquals(MacroAssembler* masm, 33 Register left, Register right, 34 Register scratch1, 35 Register scratch2); 36 37 private: 38 static void GenerateOneByteCharsCompareLoop( 39 MacroAssembler* masm, Register left, Register right, Register length, 40 Register scratch, Label* chars_not_equal, 41 Label::Distance near_jump = Label::kFar); 42 43 DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper); 44 }; 45 46 47 class NameDictionaryLookupStub: public PlatformCodeStub { 48 public: 49 enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP }; 50 NameDictionaryLookupStub(Isolate * isolate,Register dictionary,Register result,Register index,LookupMode mode)51 NameDictionaryLookupStub(Isolate* isolate, Register dictionary, 52 Register result, Register index, LookupMode mode) 53 : PlatformCodeStub(isolate) { 54 minor_key_ = DictionaryBits::encode(dictionary.code()) | 55 ResultBits::encode(result.code()) | 56 IndexBits::encode(index.code()) | LookupModeBits::encode(mode); 57 } 58 59 static void GenerateNegativeLookup(MacroAssembler* masm, 60 Label* miss, 61 Label* done, 62 Register properties, 63 Handle<Name> name, 64 Register r0); 65 66 static void GeneratePositiveLookup(MacroAssembler* masm, 67 Label* miss, 68 Label* done, 69 Register elements, 70 Register name, 71 Register r0, 72 Register r1); 73 SometimesSetsUpAFrame()74 bool SometimesSetsUpAFrame() override { return false; } 75 76 private: 77 static const int kInlinedProbes = 4; 78 static const int kTotalProbes = 20; 79 80 static const int kCapacityOffset = 81 NameDictionary::kHeaderSize + 82 NameDictionary::kCapacityIndex * kPointerSize; 83 84 static const int kElementsStartOffset = 85 NameDictionary::kHeaderSize + 86 NameDictionary::kElementsStartIndex * kPointerSize; 87 dictionary()88 Register dictionary() const { 89 return Register::from_code(DictionaryBits::decode(minor_key_)); 90 } 91 result()92 Register result() const { 93 return Register::from_code(ResultBits::decode(minor_key_)); 94 } 95 index()96 Register index() const { 97 return Register::from_code(IndexBits::decode(minor_key_)); 98 } 99 mode()100 LookupMode mode() const { return LookupModeBits::decode(minor_key_); } 101 102 class DictionaryBits: public BitField<int, 0, 4> {}; 103 class ResultBits: public BitField<int, 4, 4> {}; 104 class IndexBits: public BitField<int, 8, 4> {}; 105 class LookupModeBits: public BitField<LookupMode, 12, 1> {}; 106 107 DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); 108 DEFINE_PLATFORM_CODE_STUB(NameDictionaryLookup, PlatformCodeStub); 109 }; 110 111 112 class RecordWriteStub: public PlatformCodeStub { 113 public: RecordWriteStub(Isolate * isolate,Register object,Register value,Register address,RememberedSetAction remembered_set_action,SaveFPRegsMode fp_mode)114 RecordWriteStub(Isolate* isolate, Register object, Register value, 115 Register address, RememberedSetAction remembered_set_action, 116 SaveFPRegsMode fp_mode) 117 : PlatformCodeStub(isolate), 118 regs_(object, // An input reg. 119 address, // An input reg. 120 value) { // One scratch reg. 121 minor_key_ = ObjectBits::encode(object.code()) | 122 ValueBits::encode(value.code()) | 123 AddressBits::encode(address.code()) | 124 RememberedSetActionBits::encode(remembered_set_action) | 125 SaveFPRegsModeBits::encode(fp_mode); 126 } 127 RecordWriteStub(uint32_t key,Isolate * isolate)128 RecordWriteStub(uint32_t key, Isolate* isolate) 129 : PlatformCodeStub(key, isolate), regs_(object(), address(), value()) {} 130 131 enum Mode { 132 STORE_BUFFER_ONLY, 133 INCREMENTAL, 134 INCREMENTAL_COMPACTION 135 }; 136 SometimesSetsUpAFrame()137 bool SometimesSetsUpAFrame() override { return false; } 138 139 static const byte kTwoByteNopInstruction = 0x3c; // Cmpb al, #imm8. 140 static const byte kTwoByteJumpInstruction = 0xeb; // Jmp #imm8. 141 142 static const byte kFiveByteNopInstruction = 0x3d; // Cmpl eax, #imm32. 143 static const byte kFiveByteJumpInstruction = 0xe9; // Jmp #imm32. 144 GetMode(Code * stub)145 static Mode GetMode(Code* stub) { 146 byte first_instruction = stub->instruction_start()[0]; 147 byte second_instruction = stub->instruction_start()[2]; 148 149 if (first_instruction == kTwoByteJumpInstruction) { 150 return INCREMENTAL; 151 } 152 153 DCHECK(first_instruction == kTwoByteNopInstruction); 154 155 if (second_instruction == kFiveByteJumpInstruction) { 156 return INCREMENTAL_COMPACTION; 157 } 158 159 DCHECK(second_instruction == kFiveByteNopInstruction); 160 161 return STORE_BUFFER_ONLY; 162 } 163 Patch(Code * stub,Mode mode)164 static void Patch(Code* stub, Mode mode) { 165 switch (mode) { 166 case STORE_BUFFER_ONLY: 167 DCHECK(GetMode(stub) == INCREMENTAL || 168 GetMode(stub) == INCREMENTAL_COMPACTION); 169 stub->instruction_start()[0] = kTwoByteNopInstruction; 170 stub->instruction_start()[2] = kFiveByteNopInstruction; 171 break; 172 case INCREMENTAL: 173 DCHECK(GetMode(stub) == STORE_BUFFER_ONLY); 174 stub->instruction_start()[0] = kTwoByteJumpInstruction; 175 break; 176 case INCREMENTAL_COMPACTION: 177 DCHECK(GetMode(stub) == STORE_BUFFER_ONLY); 178 stub->instruction_start()[0] = kTwoByteNopInstruction; 179 stub->instruction_start()[2] = kFiveByteJumpInstruction; 180 break; 181 } 182 DCHECK(GetMode(stub) == mode); 183 Assembler::FlushICache(stub->GetIsolate(), stub->instruction_start(), 7); 184 } 185 186 DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); 187 188 private: 189 // This is a helper class for freeing up 3 scratch registers, where the third 190 // is always rcx (needed for shift operations). The input is two registers 191 // that must be preserved and one scratch register provided by the caller. 192 class RegisterAllocation { 193 public: RegisterAllocation(Register object,Register address,Register scratch0)194 RegisterAllocation(Register object, 195 Register address, 196 Register scratch0) 197 : object_orig_(object), 198 address_orig_(address), 199 scratch0_orig_(scratch0), 200 object_(object), 201 address_(address), 202 scratch0_(scratch0) { 203 DCHECK(!AreAliased(scratch0, object, address, no_reg)); 204 scratch1_ = GetRegThatIsNotRcxOr(object_, address_, scratch0_); 205 if (scratch0.is(rcx)) { 206 scratch0_ = GetRegThatIsNotRcxOr(object_, address_, scratch1_); 207 } 208 if (object.is(rcx)) { 209 object_ = GetRegThatIsNotRcxOr(address_, scratch0_, scratch1_); 210 } 211 if (address.is(rcx)) { 212 address_ = GetRegThatIsNotRcxOr(object_, scratch0_, scratch1_); 213 } 214 DCHECK(!AreAliased(scratch0_, object_, address_, rcx)); 215 } 216 Save(MacroAssembler * masm)217 void Save(MacroAssembler* masm) { 218 DCHECK(!address_orig_.is(object_)); 219 DCHECK(object_.is(object_orig_) || address_.is(address_orig_)); 220 DCHECK(!AreAliased(object_, address_, scratch1_, scratch0_)); 221 DCHECK(!AreAliased(object_orig_, address_, scratch1_, scratch0_)); 222 DCHECK(!AreAliased(object_, address_orig_, scratch1_, scratch0_)); 223 // We don't have to save scratch0_orig_ because it was given to us as 224 // a scratch register. But if we had to switch to a different reg then 225 // we should save the new scratch0_. 226 if (!scratch0_.is(scratch0_orig_)) masm->Push(scratch0_); 227 if (!rcx.is(scratch0_orig_) && 228 !rcx.is(object_orig_) && 229 !rcx.is(address_orig_)) { 230 masm->Push(rcx); 231 } 232 masm->Push(scratch1_); 233 if (!address_.is(address_orig_)) { 234 masm->Push(address_); 235 masm->movp(address_, address_orig_); 236 } 237 if (!object_.is(object_orig_)) { 238 masm->Push(object_); 239 masm->movp(object_, object_orig_); 240 } 241 } 242 Restore(MacroAssembler * masm)243 void Restore(MacroAssembler* masm) { 244 // These will have been preserved the entire time, so we just need to move 245 // them back. Only in one case is the orig_ reg different from the plain 246 // one, since only one of them can alias with rcx. 247 if (!object_.is(object_orig_)) { 248 masm->movp(object_orig_, object_); 249 masm->Pop(object_); 250 } 251 if (!address_.is(address_orig_)) { 252 masm->movp(address_orig_, address_); 253 masm->Pop(address_); 254 } 255 masm->Pop(scratch1_); 256 if (!rcx.is(scratch0_orig_) && 257 !rcx.is(object_orig_) && 258 !rcx.is(address_orig_)) { 259 masm->Pop(rcx); 260 } 261 if (!scratch0_.is(scratch0_orig_)) masm->Pop(scratch0_); 262 } 263 264 // If we have to call into C then we need to save and restore all caller- 265 // saved registers that were not already preserved. 266 267 // The three scratch registers (incl. rcx) will be restored by other means 268 // so we don't bother pushing them here. Rbx, rbp and r12-15 are callee 269 // save and don't need to be preserved. SaveCallerSaveRegisters(MacroAssembler * masm,SaveFPRegsMode mode)270 void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) { 271 masm->PushCallerSaved(mode, scratch0_, scratch1_, rcx); 272 } 273 RestoreCallerSaveRegisters(MacroAssembler * masm,SaveFPRegsMode mode)274 inline void RestoreCallerSaveRegisters(MacroAssembler*masm, 275 SaveFPRegsMode mode) { 276 masm->PopCallerSaved(mode, scratch0_, scratch1_, rcx); 277 } 278 object()279 inline Register object() { return object_; } address()280 inline Register address() { return address_; } scratch0()281 inline Register scratch0() { return scratch0_; } scratch1()282 inline Register scratch1() { return scratch1_; } 283 284 private: 285 Register object_orig_; 286 Register address_orig_; 287 Register scratch0_orig_; 288 Register object_; 289 Register address_; 290 Register scratch0_; 291 Register scratch1_; 292 // Third scratch register is always rcx. 293 GetRegThatIsNotRcxOr(Register r1,Register r2,Register r3)294 Register GetRegThatIsNotRcxOr(Register r1, 295 Register r2, 296 Register r3) { 297 for (int i = 0; i < Register::kNumRegisters; i++) { 298 Register candidate = Register::from_code(i); 299 if (candidate.IsAllocatable()) { 300 if (candidate.is(rcx)) continue; 301 if (candidate.is(r1)) continue; 302 if (candidate.is(r2)) continue; 303 if (candidate.is(r3)) continue; 304 return candidate; 305 } 306 } 307 UNREACHABLE(); 308 return no_reg; 309 } 310 friend class RecordWriteStub; 311 }; 312 313 enum OnNoNeedToInformIncrementalMarker { 314 kReturnOnNoNeedToInformIncrementalMarker, 315 kUpdateRememberedSetOnNoNeedToInformIncrementalMarker 316 }; 317 MajorKey()318 Major MajorKey() const final { return RecordWrite; } 319 320 void Generate(MacroAssembler* masm) override; 321 void GenerateIncremental(MacroAssembler* masm, Mode mode); 322 void CheckNeedsToInformIncrementalMarker( 323 MacroAssembler* masm, 324 OnNoNeedToInformIncrementalMarker on_no_need, 325 Mode mode); 326 void InformIncrementalMarker(MacroAssembler* masm); 327 Activate(Code * code)328 void Activate(Code* code) override { 329 code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code); 330 } 331 object()332 Register object() const { 333 return Register::from_code(ObjectBits::decode(minor_key_)); 334 } 335 value()336 Register value() const { 337 return Register::from_code(ValueBits::decode(minor_key_)); 338 } 339 address()340 Register address() const { 341 return Register::from_code(AddressBits::decode(minor_key_)); 342 } 343 remembered_set_action()344 RememberedSetAction remembered_set_action() const { 345 return RememberedSetActionBits::decode(minor_key_); 346 } 347 save_fp_regs_mode()348 SaveFPRegsMode save_fp_regs_mode() const { 349 return SaveFPRegsModeBits::decode(minor_key_); 350 } 351 352 class ObjectBits: public BitField<int, 0, 4> {}; 353 class ValueBits: public BitField<int, 4, 4> {}; 354 class AddressBits: public BitField<int, 8, 4> {}; 355 class RememberedSetActionBits: public BitField<RememberedSetAction, 12, 1> {}; 356 class SaveFPRegsModeBits: public BitField<SaveFPRegsMode, 13, 1> {}; 357 358 Label slow_; 359 RegisterAllocation regs_; 360 361 DISALLOW_COPY_AND_ASSIGN(RecordWriteStub); 362 }; 363 364 365 } // namespace internal 366 } // namespace v8 367 368 #endif // V8_X64_CODE_STUBS_X64_H_ 369