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_IA32_CODE_STUBS_IA32_H_ 6 #define V8_IA32_CODE_STUBS_IA32_H_ 7 8 #include "src/macro-assembler.h" 9 #include "src/ic-inl.h" 10 11 namespace v8 { 12 namespace internal { 13 14 15 void ArrayNativeCode(MacroAssembler* masm, 16 bool construct_call, 17 Label* call_generic_code); 18 19 20 class StoreBufferOverflowStub: public PlatformCodeStub { 21 public: 22 StoreBufferOverflowStub(Isolate* isolate, SaveFPRegsMode save_fp) 23 : PlatformCodeStub(isolate), save_doubles_(save_fp) { } 24 25 void Generate(MacroAssembler* masm); 26 27 static void GenerateFixedRegStubsAheadOfTime(Isolate* isolate); 28 virtual bool SometimesSetsUpAFrame() { return false; } 29 30 private: 31 SaveFPRegsMode save_doubles_; 32 33 Major MajorKey() { return StoreBufferOverflow; } 34 int MinorKey() { return (save_doubles_ == kSaveFPRegs) ? 1 : 0; } 35 }; 36 37 38 class StringHelper : public AllStatic { 39 public: 40 // Generate code for copying characters using the rep movs instruction. 41 // Copies ecx characters from esi to edi. Copying of overlapping regions is 42 // not supported. 43 static void GenerateCopyCharacters(MacroAssembler* masm, 44 Register dest, 45 Register src, 46 Register count, 47 Register scratch, 48 String::Encoding encoding); 49 50 // Generate string hash. 51 static void GenerateHashInit(MacroAssembler* masm, 52 Register hash, 53 Register character, 54 Register scratch); 55 static void GenerateHashAddCharacter(MacroAssembler* masm, 56 Register hash, 57 Register character, 58 Register scratch); 59 static void GenerateHashGetHash(MacroAssembler* masm, 60 Register hash, 61 Register scratch); 62 63 private: 64 DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper); 65 }; 66 67 68 class SubStringStub: public PlatformCodeStub { 69 public: 70 explicit SubStringStub(Isolate* isolate) : PlatformCodeStub(isolate) {} 71 72 private: 73 Major MajorKey() { return SubString; } 74 int MinorKey() { return 0; } 75 76 void Generate(MacroAssembler* masm); 77 }; 78 79 80 class StringCompareStub: public PlatformCodeStub { 81 public: 82 explicit StringCompareStub(Isolate* isolate) : PlatformCodeStub(isolate) { } 83 84 // Compares two flat ASCII strings and returns result in eax. 85 static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm, 86 Register left, 87 Register right, 88 Register scratch1, 89 Register scratch2, 90 Register scratch3); 91 92 // Compares two flat ASCII strings for equality and returns result 93 // in eax. 94 static void GenerateFlatAsciiStringEquals(MacroAssembler* masm, 95 Register left, 96 Register right, 97 Register scratch1, 98 Register scratch2); 99 100 private: 101 virtual Major MajorKey() { return StringCompare; } 102 virtual int MinorKey() { return 0; } 103 virtual void Generate(MacroAssembler* masm); 104 105 static void GenerateAsciiCharsCompareLoop( 106 MacroAssembler* masm, 107 Register left, 108 Register right, 109 Register length, 110 Register scratch, 111 Label* chars_not_equal, 112 Label::Distance chars_not_equal_near = Label::kFar); 113 }; 114 115 116 class NameDictionaryLookupStub: public PlatformCodeStub { 117 public: 118 enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP }; 119 120 NameDictionaryLookupStub(Isolate* isolate, 121 Register dictionary, 122 Register result, 123 Register index, 124 LookupMode mode) 125 : PlatformCodeStub(isolate), 126 dictionary_(dictionary), result_(result), index_(index), mode_(mode) { } 127 128 void Generate(MacroAssembler* masm); 129 130 static void GenerateNegativeLookup(MacroAssembler* masm, 131 Label* miss, 132 Label* done, 133 Register properties, 134 Handle<Name> name, 135 Register r0); 136 137 static void GeneratePositiveLookup(MacroAssembler* masm, 138 Label* miss, 139 Label* done, 140 Register elements, 141 Register name, 142 Register r0, 143 Register r1); 144 145 virtual bool SometimesSetsUpAFrame() { return false; } 146 147 private: 148 static const int kInlinedProbes = 4; 149 static const int kTotalProbes = 20; 150 151 static const int kCapacityOffset = 152 NameDictionary::kHeaderSize + 153 NameDictionary::kCapacityIndex * kPointerSize; 154 155 static const int kElementsStartOffset = 156 NameDictionary::kHeaderSize + 157 NameDictionary::kElementsStartIndex * kPointerSize; 158 159 Major MajorKey() { return NameDictionaryLookup; } 160 161 int MinorKey() { 162 return DictionaryBits::encode(dictionary_.code()) | 163 ResultBits::encode(result_.code()) | 164 IndexBits::encode(index_.code()) | 165 LookupModeBits::encode(mode_); 166 } 167 168 class DictionaryBits: public BitField<int, 0, 3> {}; 169 class ResultBits: public BitField<int, 3, 3> {}; 170 class IndexBits: public BitField<int, 6, 3> {}; 171 class LookupModeBits: public BitField<LookupMode, 9, 1> {}; 172 173 Register dictionary_; 174 Register result_; 175 Register index_; 176 LookupMode mode_; 177 }; 178 179 180 class RecordWriteStub: public PlatformCodeStub { 181 public: 182 RecordWriteStub(Isolate* isolate, 183 Register object, 184 Register value, 185 Register address, 186 RememberedSetAction remembered_set_action, 187 SaveFPRegsMode fp_mode) 188 : PlatformCodeStub(isolate), 189 object_(object), 190 value_(value), 191 address_(address), 192 remembered_set_action_(remembered_set_action), 193 save_fp_regs_mode_(fp_mode), 194 regs_(object, // An input reg. 195 address, // An input reg. 196 value) { // One scratch reg. 197 } 198 199 enum Mode { 200 STORE_BUFFER_ONLY, 201 INCREMENTAL, 202 INCREMENTAL_COMPACTION 203 }; 204 205 virtual bool SometimesSetsUpAFrame() { return false; } 206 207 static const byte kTwoByteNopInstruction = 0x3c; // Cmpb al, #imm8. 208 static const byte kTwoByteJumpInstruction = 0xeb; // Jmp #imm8. 209 210 static const byte kFiveByteNopInstruction = 0x3d; // Cmpl eax, #imm32. 211 static const byte kFiveByteJumpInstruction = 0xe9; // Jmp #imm32. 212 213 static Mode GetMode(Code* stub) { 214 byte first_instruction = stub->instruction_start()[0]; 215 byte second_instruction = stub->instruction_start()[2]; 216 217 if (first_instruction == kTwoByteJumpInstruction) { 218 return INCREMENTAL; 219 } 220 221 ASSERT(first_instruction == kTwoByteNopInstruction); 222 223 if (second_instruction == kFiveByteJumpInstruction) { 224 return INCREMENTAL_COMPACTION; 225 } 226 227 ASSERT(second_instruction == kFiveByteNopInstruction); 228 229 return STORE_BUFFER_ONLY; 230 } 231 232 static void Patch(Code* stub, Mode mode) { 233 switch (mode) { 234 case STORE_BUFFER_ONLY: 235 ASSERT(GetMode(stub) == INCREMENTAL || 236 GetMode(stub) == INCREMENTAL_COMPACTION); 237 stub->instruction_start()[0] = kTwoByteNopInstruction; 238 stub->instruction_start()[2] = kFiveByteNopInstruction; 239 break; 240 case INCREMENTAL: 241 ASSERT(GetMode(stub) == STORE_BUFFER_ONLY); 242 stub->instruction_start()[0] = kTwoByteJumpInstruction; 243 break; 244 case INCREMENTAL_COMPACTION: 245 ASSERT(GetMode(stub) == STORE_BUFFER_ONLY); 246 stub->instruction_start()[0] = kTwoByteNopInstruction; 247 stub->instruction_start()[2] = kFiveByteJumpInstruction; 248 break; 249 } 250 ASSERT(GetMode(stub) == mode); 251 CPU::FlushICache(stub->instruction_start(), 7); 252 } 253 254 private: 255 // This is a helper class for freeing up 3 scratch registers, where the third 256 // is always ecx (needed for shift operations). The input is two registers 257 // that must be preserved and one scratch register provided by the caller. 258 class RegisterAllocation { 259 public: 260 RegisterAllocation(Register object, 261 Register address, 262 Register scratch0) 263 : object_orig_(object), 264 address_orig_(address), 265 scratch0_orig_(scratch0), 266 object_(object), 267 address_(address), 268 scratch0_(scratch0) { 269 ASSERT(!AreAliased(scratch0, object, address, no_reg)); 270 scratch1_ = GetRegThatIsNotEcxOr(object_, address_, scratch0_); 271 if (scratch0.is(ecx)) { 272 scratch0_ = GetRegThatIsNotEcxOr(object_, address_, scratch1_); 273 } 274 if (object.is(ecx)) { 275 object_ = GetRegThatIsNotEcxOr(address_, scratch0_, scratch1_); 276 } 277 if (address.is(ecx)) { 278 address_ = GetRegThatIsNotEcxOr(object_, scratch0_, scratch1_); 279 } 280 ASSERT(!AreAliased(scratch0_, object_, address_, ecx)); 281 } 282 283 void Save(MacroAssembler* masm) { 284 ASSERT(!address_orig_.is(object_)); 285 ASSERT(object_.is(object_orig_) || address_.is(address_orig_)); 286 ASSERT(!AreAliased(object_, address_, scratch1_, scratch0_)); 287 ASSERT(!AreAliased(object_orig_, address_, scratch1_, scratch0_)); 288 ASSERT(!AreAliased(object_, address_orig_, scratch1_, scratch0_)); 289 // We don't have to save scratch0_orig_ because it was given to us as 290 // a scratch register. But if we had to switch to a different reg then 291 // we should save the new scratch0_. 292 if (!scratch0_.is(scratch0_orig_)) masm->push(scratch0_); 293 if (!ecx.is(scratch0_orig_) && 294 !ecx.is(object_orig_) && 295 !ecx.is(address_orig_)) { 296 masm->push(ecx); 297 } 298 masm->push(scratch1_); 299 if (!address_.is(address_orig_)) { 300 masm->push(address_); 301 masm->mov(address_, address_orig_); 302 } 303 if (!object_.is(object_orig_)) { 304 masm->push(object_); 305 masm->mov(object_, object_orig_); 306 } 307 } 308 309 void Restore(MacroAssembler* masm) { 310 // These will have been preserved the entire time, so we just need to move 311 // them back. Only in one case is the orig_ reg different from the plain 312 // one, since only one of them can alias with ecx. 313 if (!object_.is(object_orig_)) { 314 masm->mov(object_orig_, object_); 315 masm->pop(object_); 316 } 317 if (!address_.is(address_orig_)) { 318 masm->mov(address_orig_, address_); 319 masm->pop(address_); 320 } 321 masm->pop(scratch1_); 322 if (!ecx.is(scratch0_orig_) && 323 !ecx.is(object_orig_) && 324 !ecx.is(address_orig_)) { 325 masm->pop(ecx); 326 } 327 if (!scratch0_.is(scratch0_orig_)) masm->pop(scratch0_); 328 } 329 330 // If we have to call into C then we need to save and restore all caller- 331 // saved registers that were not already preserved. The caller saved 332 // registers are eax, ecx and edx. The three scratch registers (incl. ecx) 333 // will be restored by other means so we don't bother pushing them here. 334 void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) { 335 if (!scratch0_.is(eax) && !scratch1_.is(eax)) masm->push(eax); 336 if (!scratch0_.is(edx) && !scratch1_.is(edx)) masm->push(edx); 337 if (mode == kSaveFPRegs) { 338 masm->sub(esp, 339 Immediate(kDoubleSize * (XMMRegister::kMaxNumRegisters - 1))); 340 // Save all XMM registers except XMM0. 341 for (int i = XMMRegister::kMaxNumRegisters - 1; i > 0; i--) { 342 XMMRegister reg = XMMRegister::from_code(i); 343 masm->movsd(Operand(esp, (i - 1) * kDoubleSize), reg); 344 } 345 } 346 } 347 348 inline void RestoreCallerSaveRegisters(MacroAssembler*masm, 349 SaveFPRegsMode mode) { 350 if (mode == kSaveFPRegs) { 351 // Restore all XMM registers except XMM0. 352 for (int i = XMMRegister::kMaxNumRegisters - 1; i > 0; i--) { 353 XMMRegister reg = XMMRegister::from_code(i); 354 masm->movsd(reg, Operand(esp, (i - 1) * kDoubleSize)); 355 } 356 masm->add(esp, 357 Immediate(kDoubleSize * (XMMRegister::kMaxNumRegisters - 1))); 358 } 359 if (!scratch0_.is(edx) && !scratch1_.is(edx)) masm->pop(edx); 360 if (!scratch0_.is(eax) && !scratch1_.is(eax)) masm->pop(eax); 361 } 362 363 inline Register object() { return object_; } 364 inline Register address() { return address_; } 365 inline Register scratch0() { return scratch0_; } 366 inline Register scratch1() { return scratch1_; } 367 368 private: 369 Register object_orig_; 370 Register address_orig_; 371 Register scratch0_orig_; 372 Register object_; 373 Register address_; 374 Register scratch0_; 375 Register scratch1_; 376 // Third scratch register is always ecx. 377 378 Register GetRegThatIsNotEcxOr(Register r1, 379 Register r2, 380 Register r3) { 381 for (int i = 0; i < Register::NumAllocatableRegisters(); i++) { 382 Register candidate = Register::FromAllocationIndex(i); 383 if (candidate.is(ecx)) continue; 384 if (candidate.is(r1)) continue; 385 if (candidate.is(r2)) continue; 386 if (candidate.is(r3)) continue; 387 return candidate; 388 } 389 UNREACHABLE(); 390 return no_reg; 391 } 392 friend class RecordWriteStub; 393 }; 394 395 enum OnNoNeedToInformIncrementalMarker { 396 kReturnOnNoNeedToInformIncrementalMarker, 397 kUpdateRememberedSetOnNoNeedToInformIncrementalMarker 398 } 399 ; 400 void Generate(MacroAssembler* masm); 401 void GenerateIncremental(MacroAssembler* masm, Mode mode); 402 void CheckNeedsToInformIncrementalMarker( 403 MacroAssembler* masm, 404 OnNoNeedToInformIncrementalMarker on_no_need, 405 Mode mode); 406 void InformIncrementalMarker(MacroAssembler* masm); 407 408 Major MajorKey() { return RecordWrite; } 409 410 int MinorKey() { 411 return ObjectBits::encode(object_.code()) | 412 ValueBits::encode(value_.code()) | 413 AddressBits::encode(address_.code()) | 414 RememberedSetActionBits::encode(remembered_set_action_) | 415 SaveFPRegsModeBits::encode(save_fp_regs_mode_); 416 } 417 418 void Activate(Code* code) { 419 code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code); 420 } 421 422 class ObjectBits: public BitField<int, 0, 3> {}; 423 class ValueBits: public BitField<int, 3, 3> {}; 424 class AddressBits: public BitField<int, 6, 3> {}; 425 class RememberedSetActionBits: public BitField<RememberedSetAction, 9, 1> {}; 426 class SaveFPRegsModeBits: public BitField<SaveFPRegsMode, 10, 1> {}; 427 428 Register object_; 429 Register value_; 430 Register address_; 431 RememberedSetAction remembered_set_action_; 432 SaveFPRegsMode save_fp_regs_mode_; 433 RegisterAllocation regs_; 434 }; 435 436 437 } } // namespace v8::internal 438 439 #endif // V8_IA32_CODE_STUBS_IA32_H_ 440