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