1 // Copyright 2011 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 #ifndef V8_IA32_CODE_STUBS_IA32_H_ 29 #define V8_IA32_CODE_STUBS_IA32_H_ 30 31 #include "macro-assembler.h" 32 #include "code-stubs.h" 33 #include "ic-inl.h" 34 35 namespace v8 { 36 namespace internal { 37 38 39 void ArrayNativeCode(MacroAssembler* masm, 40 bool construct_call, 41 Label* call_generic_code); 42 43 // Compute a transcendental math function natively, or call the 44 // TranscendentalCache runtime function. 45 class TranscendentalCacheStub: public PlatformCodeStub { 46 public: 47 enum ArgumentType { 48 TAGGED = 0, 49 UNTAGGED = 1 << TranscendentalCache::kTranscendentalTypeBits 50 }; 51 52 TranscendentalCacheStub(TranscendentalCache::Type type, 53 ArgumentType argument_type) 54 : type_(type), argument_type_(argument_type) {} 55 void Generate(MacroAssembler* masm); 56 static void GenerateOperation(MacroAssembler* masm, 57 TranscendentalCache::Type type); 58 private: 59 TranscendentalCache::Type type_; 60 ArgumentType argument_type_; 61 62 Major MajorKey() { return TranscendentalCache; } 63 int MinorKey() { return type_ | argument_type_; } 64 Runtime::FunctionId RuntimeFunction(); 65 }; 66 67 68 class StoreBufferOverflowStub: public PlatformCodeStub { 69 public: 70 explicit StoreBufferOverflowStub(SaveFPRegsMode save_fp) 71 : save_doubles_(save_fp) { 72 ASSERT(CpuFeatures::IsSafeForSnapshot(SSE2) || save_fp == kDontSaveFPRegs); 73 } 74 75 void Generate(MacroAssembler* masm); 76 77 static void GenerateFixedRegStubsAheadOfTime(Isolate* isolate); 78 virtual bool SometimesSetsUpAFrame() { return false; } 79 80 private: 81 SaveFPRegsMode save_doubles_; 82 83 Major MajorKey() { return StoreBufferOverflow; } 84 int MinorKey() { return (save_doubles_ == kSaveFPRegs) ? 1 : 0; } 85 }; 86 87 88 class StringHelper : public AllStatic { 89 public: 90 // Generate code for copying characters using a simple loop. This should only 91 // be used in places where the number of characters is small and the 92 // additional setup and checking in GenerateCopyCharactersREP adds too much 93 // overhead. Copying of overlapping regions is not supported. 94 static void GenerateCopyCharacters(MacroAssembler* masm, 95 Register dest, 96 Register src, 97 Register count, 98 Register scratch, 99 bool ascii); 100 101 // Generate code for copying characters using the rep movs instruction. 102 // Copies ecx characters from esi to edi. Copying of overlapping regions is 103 // not supported. 104 static void GenerateCopyCharactersREP(MacroAssembler* masm, 105 Register dest, // Must be edi. 106 Register src, // Must be esi. 107 Register count, // Must be ecx. 108 Register scratch, // Neither of above. 109 bool ascii); 110 111 // Probe the string table for a two character string. If the string 112 // requires non-standard hashing a jump to the label not_probed is 113 // performed and registers c1 and c2 are preserved. In all other 114 // cases they are clobbered. If the string is not found by probing a 115 // jump to the label not_found is performed. This jump does not 116 // guarantee that the string is not in the string table. If the 117 // string is found the code falls through with the string in 118 // register eax. 119 static void GenerateTwoCharacterStringTableProbe(MacroAssembler* masm, 120 Register c1, 121 Register c2, 122 Register scratch1, 123 Register scratch2, 124 Register scratch3, 125 Label* not_probed, 126 Label* not_found); 127 128 // Generate string hash. 129 static void GenerateHashInit(MacroAssembler* masm, 130 Register hash, 131 Register character, 132 Register scratch); 133 static void GenerateHashAddCharacter(MacroAssembler* masm, 134 Register hash, 135 Register character, 136 Register scratch); 137 static void GenerateHashGetHash(MacroAssembler* masm, 138 Register hash, 139 Register scratch); 140 141 private: 142 DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper); 143 }; 144 145 146 class StringAddStub: public PlatformCodeStub { 147 public: 148 explicit StringAddStub(StringAddFlags flags) : flags_(flags) {} 149 150 private: 151 Major MajorKey() { return StringAdd; } 152 int MinorKey() { return flags_; } 153 154 void Generate(MacroAssembler* masm); 155 156 void GenerateConvertArgument(MacroAssembler* masm, 157 int stack_offset, 158 Register arg, 159 Register scratch1, 160 Register scratch2, 161 Register scratch3, 162 Label* slow); 163 164 void GenerateRegisterArgsPush(MacroAssembler* masm); 165 void GenerateRegisterArgsPop(MacroAssembler* masm, Register temp); 166 167 const StringAddFlags flags_; 168 }; 169 170 171 class SubStringStub: public PlatformCodeStub { 172 public: 173 SubStringStub() {} 174 175 private: 176 Major MajorKey() { return SubString; } 177 int MinorKey() { return 0; } 178 179 void Generate(MacroAssembler* masm); 180 }; 181 182 183 class StringCompareStub: public PlatformCodeStub { 184 public: 185 StringCompareStub() { } 186 187 // Compares two flat ASCII strings and returns result in eax. 188 static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm, 189 Register left, 190 Register right, 191 Register scratch1, 192 Register scratch2, 193 Register scratch3); 194 195 // Compares two flat ASCII strings for equality and returns result 196 // in eax. 197 static void GenerateFlatAsciiStringEquals(MacroAssembler* masm, 198 Register left, 199 Register right, 200 Register scratch1, 201 Register scratch2); 202 203 private: 204 virtual Major MajorKey() { return StringCompare; } 205 virtual int MinorKey() { return 0; } 206 virtual void Generate(MacroAssembler* masm); 207 208 static void GenerateAsciiCharsCompareLoop( 209 MacroAssembler* masm, 210 Register left, 211 Register right, 212 Register length, 213 Register scratch, 214 Label* chars_not_equal, 215 Label::Distance chars_not_equal_near = Label::kFar); 216 }; 217 218 219 class NameDictionaryLookupStub: public PlatformCodeStub { 220 public: 221 enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP }; 222 223 NameDictionaryLookupStub(Register dictionary, 224 Register result, 225 Register index, 226 LookupMode mode) 227 : dictionary_(dictionary), result_(result), index_(index), mode_(mode) { } 228 229 void Generate(MacroAssembler* masm); 230 231 static void GenerateNegativeLookup(MacroAssembler* masm, 232 Label* miss, 233 Label* done, 234 Register properties, 235 Handle<Name> name, 236 Register r0); 237 238 static void GeneratePositiveLookup(MacroAssembler* masm, 239 Label* miss, 240 Label* done, 241 Register elements, 242 Register name, 243 Register r0, 244 Register r1); 245 246 virtual bool SometimesSetsUpAFrame() { return false; } 247 248 private: 249 static const int kInlinedProbes = 4; 250 static const int kTotalProbes = 20; 251 252 static const int kCapacityOffset = 253 NameDictionary::kHeaderSize + 254 NameDictionary::kCapacityIndex * kPointerSize; 255 256 static const int kElementsStartOffset = 257 NameDictionary::kHeaderSize + 258 NameDictionary::kElementsStartIndex * kPointerSize; 259 260 Major MajorKey() { return NameDictionaryLookup; } 261 262 int MinorKey() { 263 return DictionaryBits::encode(dictionary_.code()) | 264 ResultBits::encode(result_.code()) | 265 IndexBits::encode(index_.code()) | 266 LookupModeBits::encode(mode_); 267 } 268 269 class DictionaryBits: public BitField<int, 0, 3> {}; 270 class ResultBits: public BitField<int, 3, 3> {}; 271 class IndexBits: public BitField<int, 6, 3> {}; 272 class LookupModeBits: public BitField<LookupMode, 9, 1> {}; 273 274 Register dictionary_; 275 Register result_; 276 Register index_; 277 LookupMode mode_; 278 }; 279 280 281 class RecordWriteStub: public PlatformCodeStub { 282 public: 283 RecordWriteStub(Register object, 284 Register value, 285 Register address, 286 RememberedSetAction remembered_set_action, 287 SaveFPRegsMode fp_mode) 288 : object_(object), 289 value_(value), 290 address_(address), 291 remembered_set_action_(remembered_set_action), 292 save_fp_regs_mode_(fp_mode), 293 regs_(object, // An input reg. 294 address, // An input reg. 295 value) { // One scratch reg. 296 ASSERT(CpuFeatures::IsSafeForSnapshot(SSE2) || fp_mode == kDontSaveFPRegs); 297 } 298 299 enum Mode { 300 STORE_BUFFER_ONLY, 301 INCREMENTAL, 302 INCREMENTAL_COMPACTION 303 }; 304 305 virtual bool SometimesSetsUpAFrame() { return false; } 306 307 static const byte kTwoByteNopInstruction = 0x3c; // Cmpb al, #imm8. 308 static const byte kTwoByteJumpInstruction = 0xeb; // Jmp #imm8. 309 310 static const byte kFiveByteNopInstruction = 0x3d; // Cmpl eax, #imm32. 311 static const byte kFiveByteJumpInstruction = 0xe9; // Jmp #imm32. 312 313 static Mode GetMode(Code* stub) { 314 byte first_instruction = stub->instruction_start()[0]; 315 byte second_instruction = stub->instruction_start()[2]; 316 317 if (first_instruction == kTwoByteJumpInstruction) { 318 return INCREMENTAL; 319 } 320 321 ASSERT(first_instruction == kTwoByteNopInstruction); 322 323 if (second_instruction == kFiveByteJumpInstruction) { 324 return INCREMENTAL_COMPACTION; 325 } 326 327 ASSERT(second_instruction == kFiveByteNopInstruction); 328 329 return STORE_BUFFER_ONLY; 330 } 331 332 static void Patch(Code* stub, Mode mode) { 333 switch (mode) { 334 case STORE_BUFFER_ONLY: 335 ASSERT(GetMode(stub) == INCREMENTAL || 336 GetMode(stub) == INCREMENTAL_COMPACTION); 337 stub->instruction_start()[0] = kTwoByteNopInstruction; 338 stub->instruction_start()[2] = kFiveByteNopInstruction; 339 break; 340 case INCREMENTAL: 341 ASSERT(GetMode(stub) == STORE_BUFFER_ONLY); 342 stub->instruction_start()[0] = kTwoByteJumpInstruction; 343 break; 344 case INCREMENTAL_COMPACTION: 345 ASSERT(GetMode(stub) == STORE_BUFFER_ONLY); 346 stub->instruction_start()[0] = kTwoByteNopInstruction; 347 stub->instruction_start()[2] = kFiveByteJumpInstruction; 348 break; 349 } 350 ASSERT(GetMode(stub) == mode); 351 CPU::FlushICache(stub->instruction_start(), 7); 352 } 353 354 private: 355 // This is a helper class for freeing up 3 scratch registers, where the third 356 // is always ecx (needed for shift operations). The input is two registers 357 // that must be preserved and one scratch register provided by the caller. 358 class RegisterAllocation { 359 public: 360 RegisterAllocation(Register object, 361 Register address, 362 Register scratch0) 363 : object_orig_(object), 364 address_orig_(address), 365 scratch0_orig_(scratch0), 366 object_(object), 367 address_(address), 368 scratch0_(scratch0) { 369 ASSERT(!AreAliased(scratch0, object, address, no_reg)); 370 scratch1_ = GetRegThatIsNotEcxOr(object_, address_, scratch0_); 371 if (scratch0.is(ecx)) { 372 scratch0_ = GetRegThatIsNotEcxOr(object_, address_, scratch1_); 373 } 374 if (object.is(ecx)) { 375 object_ = GetRegThatIsNotEcxOr(address_, scratch0_, scratch1_); 376 } 377 if (address.is(ecx)) { 378 address_ = GetRegThatIsNotEcxOr(object_, scratch0_, scratch1_); 379 } 380 ASSERT(!AreAliased(scratch0_, object_, address_, ecx)); 381 } 382 383 void Save(MacroAssembler* masm) { 384 ASSERT(!address_orig_.is(object_)); 385 ASSERT(object_.is(object_orig_) || address_.is(address_orig_)); 386 ASSERT(!AreAliased(object_, address_, scratch1_, scratch0_)); 387 ASSERT(!AreAliased(object_orig_, address_, scratch1_, scratch0_)); 388 ASSERT(!AreAliased(object_, address_orig_, scratch1_, scratch0_)); 389 // We don't have to save scratch0_orig_ because it was given to us as 390 // a scratch register. But if we had to switch to a different reg then 391 // we should save the new scratch0_. 392 if (!scratch0_.is(scratch0_orig_)) masm->push(scratch0_); 393 if (!ecx.is(scratch0_orig_) && 394 !ecx.is(object_orig_) && 395 !ecx.is(address_orig_)) { 396 masm->push(ecx); 397 } 398 masm->push(scratch1_); 399 if (!address_.is(address_orig_)) { 400 masm->push(address_); 401 masm->mov(address_, address_orig_); 402 } 403 if (!object_.is(object_orig_)) { 404 masm->push(object_); 405 masm->mov(object_, object_orig_); 406 } 407 } 408 409 void Restore(MacroAssembler* masm) { 410 // These will have been preserved the entire time, so we just need to move 411 // them back. Only in one case is the orig_ reg different from the plain 412 // one, since only one of them can alias with ecx. 413 if (!object_.is(object_orig_)) { 414 masm->mov(object_orig_, object_); 415 masm->pop(object_); 416 } 417 if (!address_.is(address_orig_)) { 418 masm->mov(address_orig_, address_); 419 masm->pop(address_); 420 } 421 masm->pop(scratch1_); 422 if (!ecx.is(scratch0_orig_) && 423 !ecx.is(object_orig_) && 424 !ecx.is(address_orig_)) { 425 masm->pop(ecx); 426 } 427 if (!scratch0_.is(scratch0_orig_)) masm->pop(scratch0_); 428 } 429 430 // If we have to call into C then we need to save and restore all caller- 431 // saved registers that were not already preserved. The caller saved 432 // registers are eax, ecx and edx. The three scratch registers (incl. ecx) 433 // will be restored by other means so we don't bother pushing them here. 434 void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) { 435 if (!scratch0_.is(eax) && !scratch1_.is(eax)) masm->push(eax); 436 if (!scratch0_.is(edx) && !scratch1_.is(edx)) masm->push(edx); 437 if (mode == kSaveFPRegs) { 438 CpuFeatureScope scope(masm, SSE2); 439 masm->sub(esp, 440 Immediate(kDoubleSize * (XMMRegister::kNumRegisters - 1))); 441 // Save all XMM registers except XMM0. 442 for (int i = XMMRegister::kNumRegisters - 1; i > 0; i--) { 443 XMMRegister reg = XMMRegister::from_code(i); 444 masm->movsd(Operand(esp, (i - 1) * kDoubleSize), reg); 445 } 446 } 447 } 448 449 inline void RestoreCallerSaveRegisters(MacroAssembler*masm, 450 SaveFPRegsMode mode) { 451 if (mode == kSaveFPRegs) { 452 CpuFeatureScope scope(masm, SSE2); 453 // Restore all XMM registers except XMM0. 454 for (int i = XMMRegister::kNumRegisters - 1; i > 0; i--) { 455 XMMRegister reg = XMMRegister::from_code(i); 456 masm->movsd(reg, Operand(esp, (i - 1) * kDoubleSize)); 457 } 458 masm->add(esp, 459 Immediate(kDoubleSize * (XMMRegister::kNumRegisters - 1))); 460 } 461 if (!scratch0_.is(edx) && !scratch1_.is(edx)) masm->pop(edx); 462 if (!scratch0_.is(eax) && !scratch1_.is(eax)) masm->pop(eax); 463 } 464 465 inline Register object() { return object_; } 466 inline Register address() { return address_; } 467 inline Register scratch0() { return scratch0_; } 468 inline Register scratch1() { return scratch1_; } 469 470 private: 471 Register object_orig_; 472 Register address_orig_; 473 Register scratch0_orig_; 474 Register object_; 475 Register address_; 476 Register scratch0_; 477 Register scratch1_; 478 // Third scratch register is always ecx. 479 480 Register GetRegThatIsNotEcxOr(Register r1, 481 Register r2, 482 Register r3) { 483 for (int i = 0; i < Register::NumAllocatableRegisters(); i++) { 484 Register candidate = Register::FromAllocationIndex(i); 485 if (candidate.is(ecx)) continue; 486 if (candidate.is(r1)) continue; 487 if (candidate.is(r2)) continue; 488 if (candidate.is(r3)) continue; 489 return candidate; 490 } 491 UNREACHABLE(); 492 return no_reg; 493 } 494 friend class RecordWriteStub; 495 }; 496 497 enum OnNoNeedToInformIncrementalMarker { 498 kReturnOnNoNeedToInformIncrementalMarker, 499 kUpdateRememberedSetOnNoNeedToInformIncrementalMarker 500 } 501 ; 502 void Generate(MacroAssembler* masm); 503 void GenerateIncremental(MacroAssembler* masm, Mode mode); 504 void CheckNeedsToInformIncrementalMarker( 505 MacroAssembler* masm, 506 OnNoNeedToInformIncrementalMarker on_no_need, 507 Mode mode); 508 void InformIncrementalMarker(MacroAssembler* masm, Mode mode); 509 510 Major MajorKey() { return RecordWrite; } 511 512 int MinorKey() { 513 return ObjectBits::encode(object_.code()) | 514 ValueBits::encode(value_.code()) | 515 AddressBits::encode(address_.code()) | 516 RememberedSetActionBits::encode(remembered_set_action_) | 517 SaveFPRegsModeBits::encode(save_fp_regs_mode_); 518 } 519 520 void Activate(Code* code) { 521 code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code); 522 } 523 524 class ObjectBits: public BitField<int, 0, 3> {}; 525 class ValueBits: public BitField<int, 3, 3> {}; 526 class AddressBits: public BitField<int, 6, 3> {}; 527 class RememberedSetActionBits: public BitField<RememberedSetAction, 9, 1> {}; 528 class SaveFPRegsModeBits: public BitField<SaveFPRegsMode, 10, 1> {}; 529 530 Register object_; 531 Register value_; 532 Register address_; 533 RememberedSetAction remembered_set_action_; 534 SaveFPRegsMode save_fp_regs_mode_; 535 RegisterAllocation regs_; 536 }; 537 538 539 } } // namespace v8::internal 540 541 #endif // V8_IA32_CODE_STUBS_IA32_H_ 542