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