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