1// Copyright 2011 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_X64_CODE_STUBS_X64_H_
6#define V8_X64_CODE_STUBS_X64_H_
7
8namespace v8 {
9namespace internal {
10
11
12void ArrayNativeCode(MacroAssembler* masm, Label* call_generic_code);
13
14
15class StringHelper : public AllStatic {
16 public:
17  // Generate code for copying characters using the rep movs instruction.
18  // Copies rcx characters from rsi to rdi. Copying of overlapping regions is
19  // not supported.
20  static void GenerateCopyCharacters(MacroAssembler* masm,
21                                     Register dest,
22                                     Register src,
23                                     Register count,
24                                     String::Encoding encoding);
25
26  // Compares two flat one-byte strings and returns result in rax.
27  static void GenerateCompareFlatOneByteStrings(
28      MacroAssembler* masm, Register left, Register right, Register scratch1,
29      Register scratch2, Register scratch3, Register scratch4);
30
31  // Compares two flat one-byte strings for equality and returns result in rax.
32  static void GenerateFlatOneByteStringEquals(MacroAssembler* masm,
33                                              Register left, Register right,
34                                              Register scratch1,
35                                              Register scratch2);
36
37 private:
38  static void GenerateOneByteCharsCompareLoop(
39      MacroAssembler* masm, Register left, Register right, Register length,
40      Register scratch, Label* chars_not_equal,
41      Label::Distance near_jump = Label::kFar);
42
43  DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper);
44};
45
46
47class NameDictionaryLookupStub: public PlatformCodeStub {
48 public:
49  enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP };
50
51  NameDictionaryLookupStub(Isolate* isolate, Register dictionary,
52                           Register result, Register index, LookupMode mode)
53      : PlatformCodeStub(isolate) {
54    minor_key_ = DictionaryBits::encode(dictionary.code()) |
55                 ResultBits::encode(result.code()) |
56                 IndexBits::encode(index.code()) | LookupModeBits::encode(mode);
57  }
58
59  static void GenerateNegativeLookup(MacroAssembler* masm,
60                                     Label* miss,
61                                     Label* done,
62                                     Register properties,
63                                     Handle<Name> name,
64                                     Register r0);
65
66  static void GeneratePositiveLookup(MacroAssembler* masm,
67                                     Label* miss,
68                                     Label* done,
69                                     Register elements,
70                                     Register name,
71                                     Register r0,
72                                     Register r1);
73
74  virtual bool SometimesSetsUpAFrame() { return false; }
75
76 private:
77  static const int kInlinedProbes = 4;
78  static const int kTotalProbes = 20;
79
80  static const int kCapacityOffset =
81      NameDictionary::kHeaderSize +
82      NameDictionary::kCapacityIndex * kPointerSize;
83
84  static const int kElementsStartOffset =
85      NameDictionary::kHeaderSize +
86      NameDictionary::kElementsStartIndex * kPointerSize;
87
88  Register dictionary() const {
89    return Register::from_code(DictionaryBits::decode(minor_key_));
90  }
91
92  Register result() const {
93    return Register::from_code(ResultBits::decode(minor_key_));
94  }
95
96  Register index() const {
97    return Register::from_code(IndexBits::decode(minor_key_));
98  }
99
100  LookupMode mode() const { return LookupModeBits::decode(minor_key_); }
101
102  class DictionaryBits: public BitField<int, 0, 4> {};
103  class ResultBits: public BitField<int, 4, 4> {};
104  class IndexBits: public BitField<int, 8, 4> {};
105  class LookupModeBits: public BitField<LookupMode, 12, 1> {};
106
107  DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR();
108  DEFINE_PLATFORM_CODE_STUB(NameDictionaryLookup, PlatformCodeStub);
109};
110
111
112class RecordWriteStub: public PlatformCodeStub {
113 public:
114  RecordWriteStub(Isolate* isolate, Register object, Register value,
115                  Register address, RememberedSetAction remembered_set_action,
116                  SaveFPRegsMode fp_mode)
117      : PlatformCodeStub(isolate),
118        regs_(object,   // An input reg.
119              address,  // An input reg.
120              value) {  // One scratch reg.
121    minor_key_ = ObjectBits::encode(object.code()) |
122                 ValueBits::encode(value.code()) |
123                 AddressBits::encode(address.code()) |
124                 RememberedSetActionBits::encode(remembered_set_action) |
125                 SaveFPRegsModeBits::encode(fp_mode);
126  }
127
128  RecordWriteStub(uint32_t key, Isolate* isolate)
129      : PlatformCodeStub(key, isolate), regs_(object(), address(), value()) {}
130
131  enum Mode {
132    STORE_BUFFER_ONLY,
133    INCREMENTAL,
134    INCREMENTAL_COMPACTION
135  };
136
137  virtual bool SometimesSetsUpAFrame() { return false; }
138
139  static const byte kTwoByteNopInstruction = 0x3c;  // Cmpb al, #imm8.
140  static const byte kTwoByteJumpInstruction = 0xeb;  // Jmp #imm8.
141
142  static const byte kFiveByteNopInstruction = 0x3d;  // Cmpl eax, #imm32.
143  static const byte kFiveByteJumpInstruction = 0xe9;  // Jmp #imm32.
144
145  static Mode GetMode(Code* stub) {
146    byte first_instruction = stub->instruction_start()[0];
147    byte second_instruction = stub->instruction_start()[2];
148
149    if (first_instruction == kTwoByteJumpInstruction) {
150      return INCREMENTAL;
151    }
152
153    DCHECK(first_instruction == kTwoByteNopInstruction);
154
155    if (second_instruction == kFiveByteJumpInstruction) {
156      return INCREMENTAL_COMPACTION;
157    }
158
159    DCHECK(second_instruction == kFiveByteNopInstruction);
160
161    return STORE_BUFFER_ONLY;
162  }
163
164  static void Patch(Code* stub, Mode mode) {
165    switch (mode) {
166      case STORE_BUFFER_ONLY:
167        DCHECK(GetMode(stub) == INCREMENTAL ||
168               GetMode(stub) == INCREMENTAL_COMPACTION);
169        stub->instruction_start()[0] = kTwoByteNopInstruction;
170        stub->instruction_start()[2] = kFiveByteNopInstruction;
171        break;
172      case INCREMENTAL:
173        DCHECK(GetMode(stub) == STORE_BUFFER_ONLY);
174        stub->instruction_start()[0] = kTwoByteJumpInstruction;
175        break;
176      case INCREMENTAL_COMPACTION:
177        DCHECK(GetMode(stub) == STORE_BUFFER_ONLY);
178        stub->instruction_start()[0] = kTwoByteNopInstruction;
179        stub->instruction_start()[2] = kFiveByteJumpInstruction;
180        break;
181    }
182    DCHECK(GetMode(stub) == mode);
183    CpuFeatures::FlushICache(stub->instruction_start(), 7);
184  }
185
186  DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR();
187
188 private:
189  // This is a helper class for freeing up 3 scratch registers, where the third
190  // is always rcx (needed for shift operations).  The input is two registers
191  // that must be preserved and one scratch register provided by the caller.
192  class RegisterAllocation {
193   public:
194    RegisterAllocation(Register object,
195                       Register address,
196                       Register scratch0)
197        : object_orig_(object),
198          address_orig_(address),
199          scratch0_orig_(scratch0),
200          object_(object),
201          address_(address),
202          scratch0_(scratch0) {
203      DCHECK(!AreAliased(scratch0, object, address, no_reg));
204      scratch1_ = GetRegThatIsNotRcxOr(object_, address_, scratch0_);
205      if (scratch0.is(rcx)) {
206        scratch0_ = GetRegThatIsNotRcxOr(object_, address_, scratch1_);
207      }
208      if (object.is(rcx)) {
209        object_ = GetRegThatIsNotRcxOr(address_, scratch0_, scratch1_);
210      }
211      if (address.is(rcx)) {
212        address_ = GetRegThatIsNotRcxOr(object_, scratch0_, scratch1_);
213      }
214      DCHECK(!AreAliased(scratch0_, object_, address_, rcx));
215    }
216
217    void Save(MacroAssembler* masm) {
218      DCHECK(!address_orig_.is(object_));
219      DCHECK(object_.is(object_orig_) || address_.is(address_orig_));
220      DCHECK(!AreAliased(object_, address_, scratch1_, scratch0_));
221      DCHECK(!AreAliased(object_orig_, address_, scratch1_, scratch0_));
222      DCHECK(!AreAliased(object_, address_orig_, scratch1_, scratch0_));
223      // We don't have to save scratch0_orig_ because it was given to us as
224      // a scratch register.  But if we had to switch to a different reg then
225      // we should save the new scratch0_.
226      if (!scratch0_.is(scratch0_orig_)) masm->Push(scratch0_);
227      if (!rcx.is(scratch0_orig_) &&
228          !rcx.is(object_orig_) &&
229          !rcx.is(address_orig_)) {
230        masm->Push(rcx);
231      }
232      masm->Push(scratch1_);
233      if (!address_.is(address_orig_)) {
234        masm->Push(address_);
235        masm->movp(address_, address_orig_);
236      }
237      if (!object_.is(object_orig_)) {
238        masm->Push(object_);
239        masm->movp(object_, object_orig_);
240      }
241    }
242
243    void Restore(MacroAssembler* masm) {
244      // These will have been preserved the entire time, so we just need to move
245      // them back.  Only in one case is the orig_ reg different from the plain
246      // one, since only one of them can alias with rcx.
247      if (!object_.is(object_orig_)) {
248        masm->movp(object_orig_, object_);
249        masm->Pop(object_);
250      }
251      if (!address_.is(address_orig_)) {
252        masm->movp(address_orig_, address_);
253        masm->Pop(address_);
254      }
255      masm->Pop(scratch1_);
256      if (!rcx.is(scratch0_orig_) &&
257          !rcx.is(object_orig_) &&
258          !rcx.is(address_orig_)) {
259        masm->Pop(rcx);
260      }
261      if (!scratch0_.is(scratch0_orig_)) masm->Pop(scratch0_);
262    }
263
264    // If we have to call into C then we need to save and restore all caller-
265    // saved registers that were not already preserved.
266
267    // The three scratch registers (incl. rcx) will be restored by other means
268    // so we don't bother pushing them here.  Rbx, rbp and r12-15 are callee
269    // save and don't need to be preserved.
270    void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) {
271      masm->PushCallerSaved(mode, scratch0_, scratch1_, rcx);
272    }
273
274    inline void RestoreCallerSaveRegisters(MacroAssembler*masm,
275                                           SaveFPRegsMode mode) {
276      masm->PopCallerSaved(mode, scratch0_, scratch1_, rcx);
277    }
278
279    inline Register object() { return object_; }
280    inline Register address() { return address_; }
281    inline Register scratch0() { return scratch0_; }
282    inline Register scratch1() { return scratch1_; }
283
284   private:
285    Register object_orig_;
286    Register address_orig_;
287    Register scratch0_orig_;
288    Register object_;
289    Register address_;
290    Register scratch0_;
291    Register scratch1_;
292    // Third scratch register is always rcx.
293
294    Register GetRegThatIsNotRcxOr(Register r1,
295                                  Register r2,
296                                  Register r3) {
297      for (int i = 0; i < Register::NumAllocatableRegisters(); i++) {
298        Register candidate = Register::FromAllocationIndex(i);
299        if (candidate.is(rcx)) continue;
300        if (candidate.is(r1)) continue;
301        if (candidate.is(r2)) continue;
302        if (candidate.is(r3)) continue;
303        return candidate;
304      }
305      UNREACHABLE();
306      return no_reg;
307    }
308    friend class RecordWriteStub;
309  };
310
311  enum OnNoNeedToInformIncrementalMarker {
312    kReturnOnNoNeedToInformIncrementalMarker,
313    kUpdateRememberedSetOnNoNeedToInformIncrementalMarker
314  };
315
316  virtual Major MajorKey() const FINAL OVERRIDE { return RecordWrite; }
317
318  virtual void Generate(MacroAssembler* masm) OVERRIDE;
319  void GenerateIncremental(MacroAssembler* masm, Mode mode);
320  void CheckNeedsToInformIncrementalMarker(
321      MacroAssembler* masm,
322      OnNoNeedToInformIncrementalMarker on_no_need,
323      Mode mode);
324  void InformIncrementalMarker(MacroAssembler* masm);
325
326  void Activate(Code* code) {
327    code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code);
328  }
329
330  Register object() const {
331    return Register::from_code(ObjectBits::decode(minor_key_));
332  }
333
334  Register value() const {
335    return Register::from_code(ValueBits::decode(minor_key_));
336  }
337
338  Register address() const {
339    return Register::from_code(AddressBits::decode(minor_key_));
340  }
341
342  RememberedSetAction remembered_set_action() const {
343    return RememberedSetActionBits::decode(minor_key_);
344  }
345
346  SaveFPRegsMode save_fp_regs_mode() const {
347    return SaveFPRegsModeBits::decode(minor_key_);
348  }
349
350  class ObjectBits: public BitField<int, 0, 4> {};
351  class ValueBits: public BitField<int, 4, 4> {};
352  class AddressBits: public BitField<int, 8, 4> {};
353  class RememberedSetActionBits: public BitField<RememberedSetAction, 12, 1> {};
354  class SaveFPRegsModeBits: public BitField<SaveFPRegsMode, 13, 1> {};
355
356  Label slow_;
357  RegisterAllocation regs_;
358
359  DISALLOW_COPY_AND_ASSIGN(RecordWriteStub);
360};
361
362
363} }  // namespace v8::internal
364
365#endif  // V8_X64_CODE_STUBS_X64_H_
366