Home | History | Annotate | Download | only in x64
      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