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_MIPS_CODE_STUBS_ARM_H_ 29 #define V8_MIPS_CODE_STUBS_ARM_H_ 30 31 #include "ic-inl.h" 32 33 34 namespace v8 { 35 namespace internal { 36 37 38 void ArrayNativeCode(MacroAssembler* masm, Label* call_generic_code); 39 40 41 // Compute a transcendental math function natively, or call the 42 // TranscendentalCache runtime function. 43 class TranscendentalCacheStub: public PlatformCodeStub { 44 public: 45 enum ArgumentType { 46 TAGGED = 0 << TranscendentalCache::kTranscendentalTypeBits, 47 UNTAGGED = 1 << TranscendentalCache::kTranscendentalTypeBits 48 }; 49 TranscendentalCacheStub(TranscendentalCache::Type type,ArgumentType argument_type)50 TranscendentalCacheStub(TranscendentalCache::Type type, 51 ArgumentType argument_type) 52 : type_(type), argument_type_(argument_type) { } 53 void Generate(MacroAssembler* masm); 54 private: 55 TranscendentalCache::Type type_; 56 ArgumentType argument_type_; 57 void GenerateCallCFunction(MacroAssembler* masm, Register scratch); 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 GenerateCopyCharactersLong adds too much 88 // overhead. Copying of overlapping regions is not supported. 89 // Dest register ends at the position after the last character written. 90 static void GenerateCopyCharacters(MacroAssembler* masm, 91 Register dest, 92 Register src, 93 Register count, 94 Register scratch, 95 bool ascii); 96 97 // Generate code for copying a large number of characters. This function 98 // is allowed to spend extra time setting up conditions to make copying 99 // faster. Copying of overlapping regions is not supported. 100 // Dest register ends at the position after the last character written. 101 static void GenerateCopyCharactersLong(MacroAssembler* masm, 102 Register dest, 103 Register src, 104 Register count, 105 Register scratch1, 106 Register scratch2, 107 Register scratch3, 108 Register scratch4, 109 Register scratch5, 110 int flags); 111 112 113 // Probe the string table for a two character string. If the string is 114 // not found by probing a jump to the label not_found is performed. This jump 115 // does not guarantee that the string is not in the string table. If the 116 // string is found the code falls through with the string in register r0. 117 // Contents of both c1 and c2 registers are modified. At the exit c1 is 118 // guaranteed to contain halfword with low and high bytes equal to 119 // initial contents of c1 and c2 respectively. 120 static void GenerateTwoCharacterStringTableProbe(MacroAssembler* masm, 121 Register c1, 122 Register c2, 123 Register scratch1, 124 Register scratch2, 125 Register scratch3, 126 Register scratch4, 127 Register scratch5, 128 Label* not_found); 129 130 // Generate string hash. 131 static void GenerateHashInit(MacroAssembler* masm, 132 Register hash, 133 Register character); 134 135 static void GenerateHashAddCharacter(MacroAssembler* masm, 136 Register hash, 137 Register character); 138 139 static void GenerateHashGetHash(MacroAssembler* masm, 140 Register hash); 141 142 private: 143 DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper); 144 }; 145 146 147 class StringAddStub: public PlatformCodeStub { 148 public: StringAddStub(StringAddFlags flags)149 explicit StringAddStub(StringAddFlags flags) : flags_(flags) {} 150 151 private: MajorKey()152 Major MajorKey() { return StringAdd; } MinorKey()153 int MinorKey() { return flags_; } 154 155 void Generate(MacroAssembler* masm); 156 157 void GenerateConvertArgument(MacroAssembler* masm, 158 int stack_offset, 159 Register arg, 160 Register scratch1, 161 Register scratch2, 162 Register scratch3, 163 Register scratch4, 164 Label* slow); 165 166 void GenerateRegisterArgsPush(MacroAssembler* masm); 167 void GenerateRegisterArgsPop(MacroAssembler* masm); 168 169 const StringAddFlags flags_; 170 }; 171 172 173 class SubStringStub: public PlatformCodeStub { 174 public: SubStringStub()175 SubStringStub() {} 176 177 private: MajorKey()178 Major MajorKey() { return SubString; } MinorKey()179 int MinorKey() { return 0; } 180 181 void Generate(MacroAssembler* masm); 182 }; 183 184 185 class StringCompareStub: public PlatformCodeStub { 186 public: StringCompareStub()187 StringCompareStub() { } 188 189 // Compare two flat ASCII strings and returns result in v0. 190 static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm, 191 Register left, 192 Register right, 193 Register scratch1, 194 Register scratch2, 195 Register scratch3, 196 Register scratch4); 197 198 // Compares two flat ASCII strings for equality and returns result 199 // in v0. 200 static void GenerateFlatAsciiStringEquals(MacroAssembler* masm, 201 Register left, 202 Register right, 203 Register scratch1, 204 Register scratch2, 205 Register scratch3); 206 207 private: MajorKey()208 virtual Major MajorKey() { return StringCompare; } MinorKey()209 virtual int MinorKey() { return 0; } 210 virtual void Generate(MacroAssembler* masm); 211 212 static void GenerateAsciiCharsCompareLoop(MacroAssembler* masm, 213 Register left, 214 Register right, 215 Register length, 216 Register scratch1, 217 Register scratch2, 218 Register scratch3, 219 Label* chars_not_equal); 220 }; 221 222 223 // This stub can convert a signed int32 to a heap number (double). It does 224 // not work for int32s that are in Smi range! No GC occurs during this stub 225 // so you don't have to set up the frame. 226 class WriteInt32ToHeapNumberStub : public PlatformCodeStub { 227 public: WriteInt32ToHeapNumberStub(Register the_int,Register the_heap_number,Register scratch,Register scratch2)228 WriteInt32ToHeapNumberStub(Register the_int, 229 Register the_heap_number, 230 Register scratch, 231 Register scratch2) 232 : the_int_(the_int), 233 the_heap_number_(the_heap_number), 234 scratch_(scratch), 235 sign_(scratch2) { 236 ASSERT(IntRegisterBits::is_valid(the_int_.code())); 237 ASSERT(HeapNumberRegisterBits::is_valid(the_heap_number_.code())); 238 ASSERT(ScratchRegisterBits::is_valid(scratch_.code())); 239 ASSERT(SignRegisterBits::is_valid(sign_.code())); 240 } 241 242 static void GenerateFixedRegStubsAheadOfTime(Isolate* isolate); 243 244 private: 245 Register the_int_; 246 Register the_heap_number_; 247 Register scratch_; 248 Register sign_; 249 250 // Minor key encoding in 16 bits. 251 class IntRegisterBits: public BitField<int, 0, 4> {}; 252 class HeapNumberRegisterBits: public BitField<int, 4, 4> {}; 253 class ScratchRegisterBits: public BitField<int, 8, 4> {}; 254 class SignRegisterBits: public BitField<int, 12, 4> {}; 255 MajorKey()256 Major MajorKey() { return WriteInt32ToHeapNumber; } MinorKey()257 int MinorKey() { 258 // Encode the parameters in a unique 16 bit value. 259 return IntRegisterBits::encode(the_int_.code()) 260 | HeapNumberRegisterBits::encode(the_heap_number_.code()) 261 | ScratchRegisterBits::encode(scratch_.code()) 262 | SignRegisterBits::encode(sign_.code()); 263 } 264 265 void Generate(MacroAssembler* masm); 266 }; 267 268 269 class RecordWriteStub: public PlatformCodeStub { 270 public: RecordWriteStub(Register object,Register value,Register address,RememberedSetAction remembered_set_action,SaveFPRegsMode fp_mode)271 RecordWriteStub(Register object, 272 Register value, 273 Register address, 274 RememberedSetAction remembered_set_action, 275 SaveFPRegsMode fp_mode) 276 : object_(object), 277 value_(value), 278 address_(address), 279 remembered_set_action_(remembered_set_action), 280 save_fp_regs_mode_(fp_mode), 281 regs_(object, // An input reg. 282 address, // An input reg. 283 value) { // One scratch reg. 284 } 285 286 enum Mode { 287 STORE_BUFFER_ONLY, 288 INCREMENTAL, 289 INCREMENTAL_COMPACTION 290 }; 291 SometimesSetsUpAFrame()292 virtual bool SometimesSetsUpAFrame() { return false; } 293 PatchBranchIntoNop(MacroAssembler * masm,int pos)294 static void PatchBranchIntoNop(MacroAssembler* masm, int pos) { 295 const unsigned offset = masm->instr_at(pos) & kImm16Mask; 296 masm->instr_at_put(pos, BNE | (zero_reg.code() << kRsShift) | 297 (zero_reg.code() << kRtShift) | (offset & kImm16Mask)); 298 ASSERT(Assembler::IsBne(masm->instr_at(pos))); 299 } 300 PatchNopIntoBranch(MacroAssembler * masm,int pos)301 static void PatchNopIntoBranch(MacroAssembler* masm, int pos) { 302 const unsigned offset = masm->instr_at(pos) & kImm16Mask; 303 masm->instr_at_put(pos, BEQ | (zero_reg.code() << kRsShift) | 304 (zero_reg.code() << kRtShift) | (offset & kImm16Mask)); 305 ASSERT(Assembler::IsBeq(masm->instr_at(pos))); 306 } 307 GetMode(Code * stub)308 static Mode GetMode(Code* stub) { 309 Instr first_instruction = Assembler::instr_at(stub->instruction_start()); 310 Instr second_instruction = Assembler::instr_at(stub->instruction_start() + 311 2 * Assembler::kInstrSize); 312 313 if (Assembler::IsBeq(first_instruction)) { 314 return INCREMENTAL; 315 } 316 317 ASSERT(Assembler::IsBne(first_instruction)); 318 319 if (Assembler::IsBeq(second_instruction)) { 320 return INCREMENTAL_COMPACTION; 321 } 322 323 ASSERT(Assembler::IsBne(second_instruction)); 324 325 return STORE_BUFFER_ONLY; 326 } 327 Patch(Code * stub,Mode mode)328 static void Patch(Code* stub, Mode mode) { 329 MacroAssembler masm(NULL, 330 stub->instruction_start(), 331 stub->instruction_size()); 332 switch (mode) { 333 case STORE_BUFFER_ONLY: 334 ASSERT(GetMode(stub) == INCREMENTAL || 335 GetMode(stub) == INCREMENTAL_COMPACTION); 336 PatchBranchIntoNop(&masm, 0); 337 PatchBranchIntoNop(&masm, 2 * Assembler::kInstrSize); 338 break; 339 case INCREMENTAL: 340 ASSERT(GetMode(stub) == STORE_BUFFER_ONLY); 341 PatchNopIntoBranch(&masm, 0); 342 break; 343 case INCREMENTAL_COMPACTION: 344 ASSERT(GetMode(stub) == STORE_BUFFER_ONLY); 345 PatchNopIntoBranch(&masm, 2 * Assembler::kInstrSize); 346 break; 347 } 348 ASSERT(GetMode(stub) == mode); 349 CPU::FlushICache(stub->instruction_start(), 4 * Assembler::kInstrSize); 350 } 351 352 private: 353 // This is a helper class for freeing up 3 scratch registers. The input is 354 // two registers that must be preserved and one scratch register provided by 355 // the caller. 356 class RegisterAllocation { 357 public: RegisterAllocation(Register object,Register address,Register scratch0)358 RegisterAllocation(Register object, 359 Register address, 360 Register scratch0) 361 : object_(object), 362 address_(address), 363 scratch0_(scratch0) { 364 ASSERT(!AreAliased(scratch0, object, address, no_reg)); 365 scratch1_ = GetRegisterThatIsNotOneOf(object_, address_, scratch0_); 366 } 367 Save(MacroAssembler * masm)368 void Save(MacroAssembler* masm) { 369 ASSERT(!AreAliased(object_, address_, scratch1_, scratch0_)); 370 // We don't have to save scratch0_ because it was given to us as 371 // a scratch register. 372 masm->push(scratch1_); 373 } 374 Restore(MacroAssembler * masm)375 void Restore(MacroAssembler* masm) { 376 masm->pop(scratch1_); 377 } 378 379 // If we have to call into C then we need to save and restore all caller- 380 // saved registers that were not already preserved. The scratch registers 381 // will be restored by other means so we don't bother pushing them here. SaveCallerSaveRegisters(MacroAssembler * masm,SaveFPRegsMode mode)382 void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) { 383 masm->MultiPush((kJSCallerSaved | ra.bit()) & ~scratch1_.bit()); 384 if (mode == kSaveFPRegs) { 385 masm->MultiPushFPU(kCallerSavedFPU); 386 } 387 } 388 RestoreCallerSaveRegisters(MacroAssembler * masm,SaveFPRegsMode mode)389 inline void RestoreCallerSaveRegisters(MacroAssembler*masm, 390 SaveFPRegsMode mode) { 391 if (mode == kSaveFPRegs) { 392 masm->MultiPopFPU(kCallerSavedFPU); 393 } 394 masm->MultiPop((kJSCallerSaved | ra.bit()) & ~scratch1_.bit()); 395 } 396 object()397 inline Register object() { return object_; } address()398 inline Register address() { return address_; } scratch0()399 inline Register scratch0() { return scratch0_; } scratch1()400 inline Register scratch1() { return scratch1_; } 401 402 private: 403 Register object_; 404 Register address_; 405 Register scratch0_; 406 Register scratch1_; 407 408 friend class RecordWriteStub; 409 }; 410 411 enum OnNoNeedToInformIncrementalMarker { 412 kReturnOnNoNeedToInformIncrementalMarker, 413 kUpdateRememberedSetOnNoNeedToInformIncrementalMarker 414 }; 415 416 void Generate(MacroAssembler* masm); 417 void GenerateIncremental(MacroAssembler* masm, Mode mode); 418 void CheckNeedsToInformIncrementalMarker( 419 MacroAssembler* masm, 420 OnNoNeedToInformIncrementalMarker on_no_need, 421 Mode mode); 422 void InformIncrementalMarker(MacroAssembler* masm, Mode mode); 423 MajorKey()424 Major MajorKey() { return RecordWrite; } 425 MinorKey()426 int MinorKey() { 427 return ObjectBits::encode(object_.code()) | 428 ValueBits::encode(value_.code()) | 429 AddressBits::encode(address_.code()) | 430 RememberedSetActionBits::encode(remembered_set_action_) | 431 SaveFPRegsModeBits::encode(save_fp_regs_mode_); 432 } 433 Activate(Code * code)434 void Activate(Code* code) { 435 code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code); 436 } 437 438 class ObjectBits: public BitField<int, 0, 5> {}; 439 class ValueBits: public BitField<int, 5, 5> {}; 440 class AddressBits: public BitField<int, 10, 5> {}; 441 class RememberedSetActionBits: public BitField<RememberedSetAction, 15, 1> {}; 442 class SaveFPRegsModeBits: public BitField<SaveFPRegsMode, 16, 1> {}; 443 444 Register object_; 445 Register value_; 446 Register address_; 447 RememberedSetAction remembered_set_action_; 448 SaveFPRegsMode save_fp_regs_mode_; 449 Label slow_; 450 RegisterAllocation regs_; 451 }; 452 453 454 // Trampoline stub to call into native code. To call safely into native code 455 // in the presence of compacting GC (which can move code objects) we need to 456 // keep the code which called into native pinned in the memory. Currently the 457 // simplest approach is to generate such stub early enough so it can never be 458 // moved by GC 459 class DirectCEntryStub: public PlatformCodeStub { 460 public: DirectCEntryStub()461 DirectCEntryStub() {} 462 void Generate(MacroAssembler* masm); 463 void GenerateCall(MacroAssembler* masm, Register target); 464 465 private: MajorKey()466 Major MajorKey() { return DirectCEntry; } MinorKey()467 int MinorKey() { return 0; } 468 NeedsImmovableCode()469 bool NeedsImmovableCode() { return true; } 470 }; 471 472 473 class NameDictionaryLookupStub: public PlatformCodeStub { 474 public: 475 enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP }; 476 NameDictionaryLookupStub(LookupMode mode)477 explicit NameDictionaryLookupStub(LookupMode mode) : mode_(mode) { } 478 479 void Generate(MacroAssembler* masm); 480 481 static void GenerateNegativeLookup(MacroAssembler* masm, 482 Label* miss, 483 Label* done, 484 Register receiver, 485 Register properties, 486 Handle<Name> name, 487 Register scratch0); 488 489 static void GeneratePositiveLookup(MacroAssembler* masm, 490 Label* miss, 491 Label* done, 492 Register elements, 493 Register name, 494 Register r0, 495 Register r1); 496 SometimesSetsUpAFrame()497 virtual bool SometimesSetsUpAFrame() { return false; } 498 499 private: 500 static const int kInlinedProbes = 4; 501 static const int kTotalProbes = 20; 502 503 static const int kCapacityOffset = 504 NameDictionary::kHeaderSize + 505 NameDictionary::kCapacityIndex * kPointerSize; 506 507 static const int kElementsStartOffset = 508 NameDictionary::kHeaderSize + 509 NameDictionary::kElementsStartIndex * kPointerSize; 510 MajorKey()511 Major MajorKey() { return NameDictionaryLookup; } 512 MinorKey()513 int MinorKey() { 514 return LookupModeBits::encode(mode_); 515 } 516 517 class LookupModeBits: public BitField<LookupMode, 0, 1> {}; 518 519 LookupMode mode_; 520 }; 521 522 523 } } // namespace v8::internal 524 525 #endif // V8_MIPS_CODE_STUBS_ARM_H_ 526