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
34namespace v8 {
35namespace internal {
36
37
38// Compute a transcendental math function natively, or call the
39// TranscendentalCache runtime function.
40class TranscendentalCacheStub: public CodeStub {
41 public:
42  enum ArgumentType {
43    TAGGED = 0,
44    UNTAGGED = 1 << TranscendentalCache::kTranscendentalTypeBits
45  };
46
47  explicit TranscendentalCacheStub(TranscendentalCache::Type type,
48                                   ArgumentType argument_type)
49      : type_(type), argument_type_(argument_type) {}
50  void Generate(MacroAssembler* masm);
51  static void GenerateOperation(MacroAssembler* masm,
52                                TranscendentalCache::Type type);
53 private:
54  TranscendentalCache::Type type_;
55  ArgumentType argument_type_;
56
57  Major MajorKey() { return TranscendentalCache; }
58  int MinorKey() { return type_ | argument_type_; }
59  Runtime::FunctionId RuntimeFunction();
60};
61
62
63class StoreBufferOverflowStub: public CodeStub {
64 public:
65  explicit StoreBufferOverflowStub(SaveFPRegsMode save_fp)
66      : save_doubles_(save_fp) { }
67
68  void Generate(MacroAssembler* masm);
69
70  virtual bool IsPregenerated() { return true; }
71  static void GenerateFixedRegStubsAheadOfTime();
72  virtual bool SometimesSetsUpAFrame() { return false; }
73
74 private:
75  SaveFPRegsMode save_doubles_;
76
77  Major MajorKey() { return StoreBufferOverflow; }
78  int MinorKey() { return (save_doubles_ == kSaveFPRegs) ? 1 : 0; }
79};
80
81
82// Flag that indicates how to generate code for the stub GenericBinaryOpStub.
83enum GenericBinaryFlags {
84  NO_GENERIC_BINARY_FLAGS = 0,
85  NO_SMI_CODE_IN_STUB = 1 << 0  // Omit smi code in stub.
86};
87
88
89class UnaryOpStub: public CodeStub {
90 public:
91  UnaryOpStub(Token::Value op,
92              UnaryOverwriteMode mode,
93              UnaryOpIC::TypeInfo operand_type = UnaryOpIC::UNINITIALIZED)
94      : op_(op),
95        mode_(mode),
96        operand_type_(operand_type) {
97  }
98
99 private:
100  Token::Value op_;
101  UnaryOverwriteMode mode_;
102
103  // Operand type information determined at runtime.
104  UnaryOpIC::TypeInfo operand_type_;
105
106  virtual void PrintName(StringStream* stream);
107
108  class ModeBits: public BitField<UnaryOverwriteMode, 0, 1> {};
109  class OpBits: public BitField<Token::Value, 1, 7> {};
110  class OperandTypeInfoBits: public BitField<UnaryOpIC::TypeInfo, 8, 3> {};
111
112  Major MajorKey() { return UnaryOp; }
113  int MinorKey() {
114    return ModeBits::encode(mode_)
115           | OpBits::encode(op_)
116           | OperandTypeInfoBits::encode(operand_type_);
117  }
118
119  // Note: A lot of the helper functions below will vanish when we use virtual
120  // function instead of switch more often.
121  void Generate(MacroAssembler* masm);
122
123  void GenerateTypeTransition(MacroAssembler* masm);
124
125  void GenerateSmiStub(MacroAssembler* masm);
126  void GenerateSmiStubSub(MacroAssembler* masm);
127  void GenerateSmiStubBitNot(MacroAssembler* masm);
128  void GenerateSmiCodeSub(MacroAssembler* masm,
129                          Label* non_smi,
130                          Label* slow,
131                          Label::Distance non_smi_near = Label::kFar,
132                          Label::Distance slow_near = Label::kFar);
133  void GenerateSmiCodeBitNot(MacroAssembler* masm,
134                             Label* non_smi,
135                             Label::Distance non_smi_near);
136
137  void GenerateHeapNumberStub(MacroAssembler* masm);
138  void GenerateHeapNumberStubSub(MacroAssembler* masm);
139  void GenerateHeapNumberStubBitNot(MacroAssembler* masm);
140  void GenerateHeapNumberCodeSub(MacroAssembler* masm, Label* slow);
141  void GenerateHeapNumberCodeBitNot(MacroAssembler* masm, Label* slow);
142
143  void GenerateGenericStub(MacroAssembler* masm);
144  void GenerateGenericStubSub(MacroAssembler* masm);
145  void GenerateGenericStubBitNot(MacroAssembler* masm);
146  void GenerateGenericCodeFallback(MacroAssembler* masm);
147
148  virtual int GetCodeKind() { return Code::UNARY_OP_IC; }
149
150  virtual InlineCacheState GetICState() {
151    return UnaryOpIC::ToState(operand_type_);
152  }
153
154  virtual void FinishCode(Handle<Code> code) {
155    code->set_unary_op_type(operand_type_);
156  }
157};
158
159
160class BinaryOpStub: public CodeStub {
161 public:
162  BinaryOpStub(Token::Value op, OverwriteMode mode)
163      : op_(op),
164        mode_(mode),
165        operands_type_(BinaryOpIC::UNINITIALIZED),
166        result_type_(BinaryOpIC::UNINITIALIZED) {
167    ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
168  }
169
170  BinaryOpStub(
171      int key,
172      BinaryOpIC::TypeInfo operands_type,
173      BinaryOpIC::TypeInfo result_type = BinaryOpIC::UNINITIALIZED)
174      : op_(OpBits::decode(key)),
175        mode_(ModeBits::decode(key)),
176        operands_type_(operands_type),
177        result_type_(result_type) { }
178
179 private:
180  enum SmiCodeGenerateHeapNumberResults {
181    ALLOW_HEAPNUMBER_RESULTS,
182    NO_HEAPNUMBER_RESULTS
183  };
184
185  Token::Value op_;
186  OverwriteMode mode_;
187
188  // Operand type information determined at runtime.
189  BinaryOpIC::TypeInfo operands_type_;
190  BinaryOpIC::TypeInfo result_type_;
191
192  virtual void PrintName(StringStream* stream);
193
194  // Minor key encoding in 15 bits RRRTTTOOOOOOOMM.
195  class ModeBits: public BitField<OverwriteMode, 0, 2> {};
196  class OpBits: public BitField<Token::Value, 2, 7> {};
197  class OperandTypeInfoBits: public BitField<BinaryOpIC::TypeInfo, 9, 3> {};
198  class ResultTypeInfoBits: public BitField<BinaryOpIC::TypeInfo, 12, 3> {};
199
200  Major MajorKey() { return BinaryOp; }
201  int MinorKey() {
202    return OpBits::encode(op_)
203           | ModeBits::encode(mode_)
204           | OperandTypeInfoBits::encode(operands_type_)
205           | ResultTypeInfoBits::encode(result_type_);
206  }
207
208  void Generate(MacroAssembler* masm);
209  void GenerateGeneric(MacroAssembler* masm);
210  void GenerateSmiCode(MacroAssembler* masm,
211                       Label* slow,
212                       SmiCodeGenerateHeapNumberResults heapnumber_results);
213  void GenerateFloatingPointCode(MacroAssembler* masm,
214                                 Label* allocation_failure,
215                                 Label* non_numeric_failure);
216  void GenerateStringAddCode(MacroAssembler* masm);
217  void GenerateCallRuntimeCode(MacroAssembler* masm);
218  void GenerateLoadArguments(MacroAssembler* masm);
219  void GenerateReturn(MacroAssembler* masm);
220  void GenerateUninitializedStub(MacroAssembler* masm);
221  void GenerateSmiStub(MacroAssembler* masm);
222  void GenerateInt32Stub(MacroAssembler* masm);
223  void GenerateHeapNumberStub(MacroAssembler* masm);
224  void GenerateOddballStub(MacroAssembler* masm);
225  void GenerateStringStub(MacroAssembler* masm);
226  void GenerateBothStringStub(MacroAssembler* masm);
227  void GenerateGenericStub(MacroAssembler* masm);
228
229  void GenerateHeapResultAllocation(MacroAssembler* masm, Label* alloc_failure);
230  void GenerateRegisterArgsPush(MacroAssembler* masm);
231  void GenerateTypeTransition(MacroAssembler* masm);
232  void GenerateTypeTransitionWithSavedArgs(MacroAssembler* masm);
233
234  virtual int GetCodeKind() { return Code::BINARY_OP_IC; }
235
236  virtual InlineCacheState GetICState() {
237    return BinaryOpIC::ToState(operands_type_);
238  }
239
240  virtual void FinishCode(Handle<Code> code) {
241    code->set_binary_op_type(operands_type_);
242    code->set_binary_op_result_type(result_type_);
243  }
244
245  friend class CodeGenerator;
246};
247
248
249class StringHelper : public AllStatic {
250 public:
251  // Generate code for copying characters using a simple loop. This should only
252  // be used in places where the number of characters is small and the
253  // additional setup and checking in GenerateCopyCharactersREP adds too much
254  // overhead. Copying of overlapping regions is not supported.
255  static void GenerateCopyCharacters(MacroAssembler* masm,
256                                     Register dest,
257                                     Register src,
258                                     Register count,
259                                     bool ascii);
260
261  // Generate code for copying characters using the rep movs instruction.
262  // Copies rcx characters from rsi to rdi. Copying of overlapping regions is
263  // not supported.
264  static void GenerateCopyCharactersREP(MacroAssembler* masm,
265                                        Register dest,     // Must be rdi.
266                                        Register src,      // Must be rsi.
267                                        Register count,    // Must be rcx.
268                                        bool ascii);
269
270
271  // Probe the symbol table for a two character string. If the string is
272  // not found by probing a jump to the label not_found is performed. This jump
273  // does not guarantee that the string is not in the symbol table. If the
274  // string is found the code falls through with the string in register rax.
275  static void GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
276                                                   Register c1,
277                                                   Register c2,
278                                                   Register scratch1,
279                                                   Register scratch2,
280                                                   Register scratch3,
281                                                   Register scratch4,
282                                                   Label* not_found);
283
284  // Generate string hash.
285  static void GenerateHashInit(MacroAssembler* masm,
286                               Register hash,
287                               Register character,
288                               Register scratch);
289  static void GenerateHashAddCharacter(MacroAssembler* masm,
290                                       Register hash,
291                                       Register character,
292                                       Register scratch);
293  static void GenerateHashGetHash(MacroAssembler* masm,
294                                  Register hash,
295                                  Register scratch);
296
297 private:
298  DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper);
299};
300
301
302// Flag that indicates how to generate code for the stub StringAddStub.
303enum StringAddFlags {
304  NO_STRING_ADD_FLAGS = 0,
305  // Omit left string check in stub (left is definitely a string).
306  NO_STRING_CHECK_LEFT_IN_STUB = 1 << 0,
307  // Omit right string check in stub (right is definitely a string).
308  NO_STRING_CHECK_RIGHT_IN_STUB = 1 << 1,
309  // Omit both string checks in stub.
310  NO_STRING_CHECK_IN_STUB =
311      NO_STRING_CHECK_LEFT_IN_STUB | NO_STRING_CHECK_RIGHT_IN_STUB
312};
313
314
315class StringAddStub: public CodeStub {
316 public:
317  explicit StringAddStub(StringAddFlags flags) : flags_(flags) {}
318
319 private:
320  Major MajorKey() { return StringAdd; }
321  int MinorKey() { return flags_; }
322
323  void Generate(MacroAssembler* masm);
324
325  void GenerateConvertArgument(MacroAssembler* masm,
326                               int stack_offset,
327                               Register arg,
328                               Register scratch1,
329                               Register scratch2,
330                               Register scratch3,
331                               Label* slow);
332
333  const StringAddFlags flags_;
334};
335
336
337class SubStringStub: public CodeStub {
338 public:
339  SubStringStub() {}
340
341 private:
342  Major MajorKey() { return SubString; }
343  int MinorKey() { return 0; }
344
345  void Generate(MacroAssembler* masm);
346};
347
348
349class StringCompareStub: public CodeStub {
350 public:
351  StringCompareStub() {}
352
353  // Compares two flat ASCII strings and returns result in rax.
354  static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
355                                              Register left,
356                                              Register right,
357                                              Register scratch1,
358                                              Register scratch2,
359                                              Register scratch3,
360                                              Register scratch4);
361
362  // Compares two flat ASCII strings for equality and returns result
363  // in rax.
364  static void GenerateFlatAsciiStringEquals(MacroAssembler* masm,
365                                            Register left,
366                                            Register right,
367                                            Register scratch1,
368                                            Register scratch2);
369
370 private:
371  virtual Major MajorKey() { return StringCompare; }
372  virtual int MinorKey() { return 0; }
373  virtual void Generate(MacroAssembler* masm);
374
375  static void GenerateAsciiCharsCompareLoop(
376      MacroAssembler* masm,
377      Register left,
378      Register right,
379      Register length,
380      Register scratch,
381      Label* chars_not_equal,
382      Label::Distance near_jump = Label::kFar);
383};
384
385
386class NumberToStringStub: public CodeStub {
387 public:
388  NumberToStringStub() { }
389
390  // Generate code to do a lookup in the number string cache. If the number in
391  // the register object is found in the cache the generated code falls through
392  // with the result in the result register. The object and the result register
393  // can be the same. If the number is not found in the cache the code jumps to
394  // the label not_found with only the content of register object unchanged.
395  static void GenerateLookupNumberStringCache(MacroAssembler* masm,
396                                              Register object,
397                                              Register result,
398                                              Register scratch1,
399                                              Register scratch2,
400                                              bool object_is_smi,
401                                              Label* not_found);
402
403 private:
404  static void GenerateConvertHashCodeToIndex(MacroAssembler* masm,
405                                             Register hash,
406                                             Register mask);
407
408  Major MajorKey() { return NumberToString; }
409  int MinorKey() { return 0; }
410
411  void Generate(MacroAssembler* masm);
412};
413
414
415class StringDictionaryLookupStub: public CodeStub {
416 public:
417  enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP };
418
419  StringDictionaryLookupStub(Register dictionary,
420                             Register result,
421                             Register index,
422                             LookupMode mode)
423      : dictionary_(dictionary), result_(result), index_(index), mode_(mode) { }
424
425  void Generate(MacroAssembler* masm);
426
427  static void GenerateNegativeLookup(MacroAssembler* masm,
428                                     Label* miss,
429                                     Label* done,
430                                     Register properties,
431                                     Handle<String> name,
432                                     Register r0);
433
434  static void GeneratePositiveLookup(MacroAssembler* masm,
435                                     Label* miss,
436                                     Label* done,
437                                     Register elements,
438                                     Register name,
439                                     Register r0,
440                                     Register r1);
441
442  virtual bool SometimesSetsUpAFrame() { return false; }
443
444 private:
445  static const int kInlinedProbes = 4;
446  static const int kTotalProbes = 20;
447
448  static const int kCapacityOffset =
449      StringDictionary::kHeaderSize +
450      StringDictionary::kCapacityIndex * kPointerSize;
451
452  static const int kElementsStartOffset =
453      StringDictionary::kHeaderSize +
454      StringDictionary::kElementsStartIndex * kPointerSize;
455
456  Major MajorKey() { return StringDictionaryLookup; }
457
458  int MinorKey() {
459    return DictionaryBits::encode(dictionary_.code()) |
460        ResultBits::encode(result_.code()) |
461        IndexBits::encode(index_.code()) |
462        LookupModeBits::encode(mode_);
463  }
464
465  class DictionaryBits: public BitField<int, 0, 4> {};
466  class ResultBits: public BitField<int, 4, 4> {};
467  class IndexBits: public BitField<int, 8, 4> {};
468  class LookupModeBits: public BitField<LookupMode, 12, 1> {};
469
470  Register dictionary_;
471  Register result_;
472  Register index_;
473  LookupMode mode_;
474};
475
476
477class RecordWriteStub: public CodeStub {
478 public:
479  RecordWriteStub(Register object,
480                  Register value,
481                  Register address,
482                  RememberedSetAction remembered_set_action,
483                  SaveFPRegsMode fp_mode)
484      : object_(object),
485        value_(value),
486        address_(address),
487        remembered_set_action_(remembered_set_action),
488        save_fp_regs_mode_(fp_mode),
489        regs_(object,   // An input reg.
490              address,  // An input reg.
491              value) {  // One scratch reg.
492  }
493
494  enum Mode {
495    STORE_BUFFER_ONLY,
496    INCREMENTAL,
497    INCREMENTAL_COMPACTION
498  };
499
500  virtual bool IsPregenerated();
501  static void GenerateFixedRegStubsAheadOfTime();
502  virtual bool SometimesSetsUpAFrame() { return false; }
503
504  static const byte kTwoByteNopInstruction = 0x3c;  // Cmpb al, #imm8.
505  static const byte kTwoByteJumpInstruction = 0xeb;  // Jmp #imm8.
506
507  static const byte kFiveByteNopInstruction = 0x3d;  // Cmpl eax, #imm32.
508  static const byte kFiveByteJumpInstruction = 0xe9;  // Jmp #imm32.
509
510  static Mode GetMode(Code* stub) {
511    byte first_instruction = stub->instruction_start()[0];
512    byte second_instruction = stub->instruction_start()[2];
513
514    if (first_instruction == kTwoByteJumpInstruction) {
515      return INCREMENTAL;
516    }
517
518    ASSERT(first_instruction == kTwoByteNopInstruction);
519
520    if (second_instruction == kFiveByteJumpInstruction) {
521      return INCREMENTAL_COMPACTION;
522    }
523
524    ASSERT(second_instruction == kFiveByteNopInstruction);
525
526    return STORE_BUFFER_ONLY;
527  }
528
529  static void Patch(Code* stub, Mode mode) {
530    switch (mode) {
531      case STORE_BUFFER_ONLY:
532        ASSERT(GetMode(stub) == INCREMENTAL ||
533               GetMode(stub) == INCREMENTAL_COMPACTION);
534        stub->instruction_start()[0] = kTwoByteNopInstruction;
535        stub->instruction_start()[2] = kFiveByteNopInstruction;
536        break;
537      case INCREMENTAL:
538        ASSERT(GetMode(stub) == STORE_BUFFER_ONLY);
539        stub->instruction_start()[0] = kTwoByteJumpInstruction;
540        break;
541      case INCREMENTAL_COMPACTION:
542        ASSERT(GetMode(stub) == STORE_BUFFER_ONLY);
543        stub->instruction_start()[0] = kTwoByteNopInstruction;
544        stub->instruction_start()[2] = kFiveByteJumpInstruction;
545        break;
546    }
547    ASSERT(GetMode(stub) == mode);
548    CPU::FlushICache(stub->instruction_start(), 7);
549  }
550
551 private:
552  // This is a helper class for freeing up 3 scratch registers, where the third
553  // is always rcx (needed for shift operations).  The input is two registers
554  // that must be preserved and one scratch register provided by the caller.
555  class RegisterAllocation {
556   public:
557    RegisterAllocation(Register object,
558                       Register address,
559                       Register scratch0)
560        : object_orig_(object),
561          address_orig_(address),
562          scratch0_orig_(scratch0),
563          object_(object),
564          address_(address),
565          scratch0_(scratch0) {
566      ASSERT(!AreAliased(scratch0, object, address, no_reg));
567      scratch1_ = GetRegThatIsNotRcxOr(object_, address_, scratch0_);
568      if (scratch0.is(rcx)) {
569        scratch0_ = GetRegThatIsNotRcxOr(object_, address_, scratch1_);
570      }
571      if (object.is(rcx)) {
572        object_ = GetRegThatIsNotRcxOr(address_, scratch0_, scratch1_);
573      }
574      if (address.is(rcx)) {
575        address_ = GetRegThatIsNotRcxOr(object_, scratch0_, scratch1_);
576      }
577      ASSERT(!AreAliased(scratch0_, object_, address_, rcx));
578    }
579
580    void Save(MacroAssembler* masm) {
581      ASSERT(!address_orig_.is(object_));
582      ASSERT(object_.is(object_orig_) || address_.is(address_orig_));
583      ASSERT(!AreAliased(object_, address_, scratch1_, scratch0_));
584      ASSERT(!AreAliased(object_orig_, address_, scratch1_, scratch0_));
585      ASSERT(!AreAliased(object_, address_orig_, scratch1_, scratch0_));
586      // We don't have to save scratch0_orig_ because it was given to us as
587      // a scratch register.  But if we had to switch to a different reg then
588      // we should save the new scratch0_.
589      if (!scratch0_.is(scratch0_orig_)) masm->push(scratch0_);
590      if (!rcx.is(scratch0_orig_) &&
591          !rcx.is(object_orig_) &&
592          !rcx.is(address_orig_)) {
593        masm->push(rcx);
594      }
595      masm->push(scratch1_);
596      if (!address_.is(address_orig_)) {
597        masm->push(address_);
598        masm->movq(address_, address_orig_);
599      }
600      if (!object_.is(object_orig_)) {
601        masm->push(object_);
602        masm->movq(object_, object_orig_);
603      }
604    }
605
606    void Restore(MacroAssembler* masm) {
607      // These will have been preserved the entire time, so we just need to move
608      // them back.  Only in one case is the orig_ reg different from the plain
609      // one, since only one of them can alias with rcx.
610      if (!object_.is(object_orig_)) {
611        masm->movq(object_orig_, object_);
612        masm->pop(object_);
613      }
614      if (!address_.is(address_orig_)) {
615        masm->movq(address_orig_, address_);
616        masm->pop(address_);
617      }
618      masm->pop(scratch1_);
619      if (!rcx.is(scratch0_orig_) &&
620          !rcx.is(object_orig_) &&
621          !rcx.is(address_orig_)) {
622        masm->pop(rcx);
623      }
624      if (!scratch0_.is(scratch0_orig_)) masm->pop(scratch0_);
625    }
626
627    // If we have to call into C then we need to save and restore all caller-
628    // saved registers that were not already preserved.
629
630    // The three scratch registers (incl. rcx) will be restored by other means
631    // so we don't bother pushing them here.  Rbx, rbp and r12-15 are callee
632    // save and don't need to be preserved.
633    void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) {
634      masm->PushCallerSaved(mode, scratch0_, scratch1_, rcx);
635    }
636
637    inline void RestoreCallerSaveRegisters(MacroAssembler*masm,
638                                           SaveFPRegsMode mode) {
639      masm->PopCallerSaved(mode, scratch0_, scratch1_, rcx);
640    }
641
642    inline Register object() { return object_; }
643    inline Register address() { return address_; }
644    inline Register scratch0() { return scratch0_; }
645    inline Register scratch1() { return scratch1_; }
646
647   private:
648    Register object_orig_;
649    Register address_orig_;
650    Register scratch0_orig_;
651    Register object_;
652    Register address_;
653    Register scratch0_;
654    Register scratch1_;
655    // Third scratch register is always rcx.
656
657    Register GetRegThatIsNotRcxOr(Register r1,
658                                  Register r2,
659                                  Register r3) {
660      for (int i = 0; i < Register::kNumAllocatableRegisters; i++) {
661        Register candidate = Register::FromAllocationIndex(i);
662        if (candidate.is(rcx)) continue;
663        if (candidate.is(r1)) continue;
664        if (candidate.is(r2)) continue;
665        if (candidate.is(r3)) continue;
666        return candidate;
667      }
668      UNREACHABLE();
669      return no_reg;
670    }
671    friend class RecordWriteStub;
672  };
673
674  enum OnNoNeedToInformIncrementalMarker {
675    kReturnOnNoNeedToInformIncrementalMarker,
676    kUpdateRememberedSetOnNoNeedToInformIncrementalMarker
677  };
678
679  void Generate(MacroAssembler* masm);
680  void GenerateIncremental(MacroAssembler* masm, Mode mode);
681  void CheckNeedsToInformIncrementalMarker(
682      MacroAssembler* masm,
683      OnNoNeedToInformIncrementalMarker on_no_need,
684      Mode mode);
685  void InformIncrementalMarker(MacroAssembler* masm, Mode mode);
686
687  Major MajorKey() { return RecordWrite; }
688
689  int MinorKey() {
690    return ObjectBits::encode(object_.code()) |
691        ValueBits::encode(value_.code()) |
692        AddressBits::encode(address_.code()) |
693        RememberedSetActionBits::encode(remembered_set_action_) |
694        SaveFPRegsModeBits::encode(save_fp_regs_mode_);
695  }
696
697  void Activate(Code* code) {
698    code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code);
699  }
700
701  class ObjectBits: public BitField<int, 0, 4> {};
702  class ValueBits: public BitField<int, 4, 4> {};
703  class AddressBits: public BitField<int, 8, 4> {};
704  class RememberedSetActionBits: public BitField<RememberedSetAction, 12, 1> {};
705  class SaveFPRegsModeBits: public BitField<SaveFPRegsMode, 13, 1> {};
706
707  Register object_;
708  Register value_;
709  Register address_;
710  RememberedSetAction remembered_set_action_;
711  SaveFPRegsMode save_fp_regs_mode_;
712  Label slow_;
713  RegisterAllocation regs_;
714};
715
716
717} }  // namespace v8::internal
718
719#endif  // V8_X64_CODE_STUBS_X64_H_
720