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