1// Copyright 2012 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_ARM_CODE_STUBS_ARM_H_
6#define V8_ARM_CODE_STUBS_ARM_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 a large number of characters. This function
18  // is allowed to spend extra time setting up conditions to make copying
19  // faster. Copying of overlapping regions is not supported.
20  // Dest register ends at the position after the last character written.
21  static void GenerateCopyCharacters(MacroAssembler* masm,
22                                     Register dest,
23                                     Register src,
24                                     Register count,
25                                     Register scratch,
26                                     String::Encoding encoding);
27
28  // Compares two flat one-byte strings and returns result in r0.
29  static void GenerateCompareFlatOneByteStrings(
30      MacroAssembler* masm, Register left, Register right, Register scratch1,
31      Register scratch2, Register scratch3, Register scratch4);
32
33  // Compares two flat one-byte strings for equality and returns result in r0.
34  static void GenerateFlatOneByteStringEquals(MacroAssembler* masm,
35                                              Register left, Register right,
36                                              Register scratch1,
37                                              Register scratch2,
38                                              Register scratch3);
39
40 private:
41  static void GenerateOneByteCharsCompareLoop(
42      MacroAssembler* masm, Register left, Register right, Register length,
43      Register scratch1, Register scratch2, Label* chars_not_equal);
44
45  DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper);
46};
47
48
49// This stub can convert a signed int32 to a heap number (double).  It does
50// not work for int32s that are in Smi range!  No GC occurs during this stub
51// so you don't have to set up the frame.
52class WriteInt32ToHeapNumberStub : public PlatformCodeStub {
53 public:
54  WriteInt32ToHeapNumberStub(Isolate* isolate, Register the_int,
55                             Register the_heap_number, Register scratch)
56      : PlatformCodeStub(isolate) {
57    minor_key_ = IntRegisterBits::encode(the_int.code()) |
58                 HeapNumberRegisterBits::encode(the_heap_number.code()) |
59                 ScratchRegisterBits::encode(scratch.code());
60  }
61
62  static void GenerateFixedRegStubsAheadOfTime(Isolate* isolate);
63
64 private:
65  Register the_int() const {
66    return Register::from_code(IntRegisterBits::decode(minor_key_));
67  }
68
69  Register the_heap_number() const {
70    return Register::from_code(HeapNumberRegisterBits::decode(minor_key_));
71  }
72
73  Register scratch() const {
74    return Register::from_code(ScratchRegisterBits::decode(minor_key_));
75  }
76
77  // Minor key encoding in 16 bits.
78  class IntRegisterBits: public BitField<int, 0, 4> {};
79  class HeapNumberRegisterBits: public BitField<int, 4, 4> {};
80  class ScratchRegisterBits: public BitField<int, 8, 4> {};
81
82  DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR();
83  DEFINE_PLATFORM_CODE_STUB(WriteInt32ToHeapNumber, PlatformCodeStub);
84};
85
86
87class RecordWriteStub: public PlatformCodeStub {
88 public:
89  RecordWriteStub(Isolate* isolate,
90                  Register object,
91                  Register value,
92                  Register address,
93                  RememberedSetAction remembered_set_action,
94                  SaveFPRegsMode fp_mode)
95      : PlatformCodeStub(isolate),
96        regs_(object,   // An input reg.
97              address,  // An input reg.
98              value) {  // One scratch reg.
99    minor_key_ = ObjectBits::encode(object.code()) |
100                 ValueBits::encode(value.code()) |
101                 AddressBits::encode(address.code()) |
102                 RememberedSetActionBits::encode(remembered_set_action) |
103                 SaveFPRegsModeBits::encode(fp_mode);
104  }
105
106  RecordWriteStub(uint32_t key, Isolate* isolate)
107      : PlatformCodeStub(key, isolate), regs_(object(), address(), value()) {}
108
109  enum Mode {
110    STORE_BUFFER_ONLY,
111    INCREMENTAL,
112    INCREMENTAL_COMPACTION
113  };
114
115  virtual bool SometimesSetsUpAFrame() { return false; }
116
117  static void PatchBranchIntoNop(MacroAssembler* masm, int pos) {
118    masm->instr_at_put(pos, (masm->instr_at(pos) & ~B27) | (B24 | B20));
119    DCHECK(Assembler::IsTstImmediate(masm->instr_at(pos)));
120  }
121
122  static void PatchNopIntoBranch(MacroAssembler* masm, int pos) {
123    masm->instr_at_put(pos, (masm->instr_at(pos) & ~(B24 | B20)) | B27);
124    DCHECK(Assembler::IsBranch(masm->instr_at(pos)));
125  }
126
127  static Mode GetMode(Code* stub) {
128    Instr first_instruction = Assembler::instr_at(stub->instruction_start());
129    Instr second_instruction = Assembler::instr_at(stub->instruction_start() +
130                                                   Assembler::kInstrSize);
131
132    if (Assembler::IsBranch(first_instruction)) {
133      return INCREMENTAL;
134    }
135
136    DCHECK(Assembler::IsTstImmediate(first_instruction));
137
138    if (Assembler::IsBranch(second_instruction)) {
139      return INCREMENTAL_COMPACTION;
140    }
141
142    DCHECK(Assembler::IsTstImmediate(second_instruction));
143
144    return STORE_BUFFER_ONLY;
145  }
146
147  static void Patch(Code* stub, Mode mode) {
148    MacroAssembler masm(NULL,
149                        stub->instruction_start(),
150                        stub->instruction_size());
151    switch (mode) {
152      case STORE_BUFFER_ONLY:
153        DCHECK(GetMode(stub) == INCREMENTAL ||
154               GetMode(stub) == INCREMENTAL_COMPACTION);
155        PatchBranchIntoNop(&masm, 0);
156        PatchBranchIntoNop(&masm, Assembler::kInstrSize);
157        break;
158      case INCREMENTAL:
159        DCHECK(GetMode(stub) == STORE_BUFFER_ONLY);
160        PatchNopIntoBranch(&masm, 0);
161        break;
162      case INCREMENTAL_COMPACTION:
163        DCHECK(GetMode(stub) == STORE_BUFFER_ONLY);
164        PatchNopIntoBranch(&masm, Assembler::kInstrSize);
165        break;
166    }
167    DCHECK(GetMode(stub) == mode);
168    CpuFeatures::FlushICache(stub->instruction_start(),
169                             2 * Assembler::kInstrSize);
170  }
171
172  DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR();
173
174 private:
175  // This is a helper class for freeing up 3 scratch registers.  The input is
176  // two registers that must be preserved and one scratch register provided by
177  // the caller.
178  class RegisterAllocation {
179   public:
180    RegisterAllocation(Register object,
181                       Register address,
182                       Register scratch0)
183        : object_(object),
184          address_(address),
185          scratch0_(scratch0) {
186      DCHECK(!AreAliased(scratch0, object, address, no_reg));
187      scratch1_ = GetRegisterThatIsNotOneOf(object_, address_, scratch0_);
188    }
189
190    void Save(MacroAssembler* masm) {
191      DCHECK(!AreAliased(object_, address_, scratch1_, scratch0_));
192      // We don't have to save scratch0_ because it was given to us as
193      // a scratch register.
194      masm->push(scratch1_);
195    }
196
197    void Restore(MacroAssembler* masm) {
198      masm->pop(scratch1_);
199    }
200
201    // If we have to call into C then we need to save and restore all caller-
202    // saved registers that were not already preserved.  The scratch registers
203    // will be restored by other means so we don't bother pushing them here.
204    void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) {
205      masm->stm(db_w, sp, (kCallerSaved | lr.bit()) & ~scratch1_.bit());
206      if (mode == kSaveFPRegs) {
207        masm->SaveFPRegs(sp, scratch0_);
208      }
209    }
210
211    inline void RestoreCallerSaveRegisters(MacroAssembler*masm,
212                                           SaveFPRegsMode mode) {
213      if (mode == kSaveFPRegs) {
214        masm->RestoreFPRegs(sp, scratch0_);
215      }
216      masm->ldm(ia_w, sp, (kCallerSaved | lr.bit()) & ~scratch1_.bit());
217    }
218
219    inline Register object() { return object_; }
220    inline Register address() { return address_; }
221    inline Register scratch0() { return scratch0_; }
222    inline Register scratch1() { return scratch1_; }
223
224   private:
225    Register object_;
226    Register address_;
227    Register scratch0_;
228    Register scratch1_;
229
230    friend class RecordWriteStub;
231  };
232
233  enum OnNoNeedToInformIncrementalMarker {
234    kReturnOnNoNeedToInformIncrementalMarker,
235    kUpdateRememberedSetOnNoNeedToInformIncrementalMarker
236  };
237
238  virtual inline Major MajorKey() const FINAL OVERRIDE { return RecordWrite; }
239
240  virtual void Generate(MacroAssembler* masm) OVERRIDE;
241  void GenerateIncremental(MacroAssembler* masm, Mode mode);
242  void CheckNeedsToInformIncrementalMarker(
243      MacroAssembler* masm,
244      OnNoNeedToInformIncrementalMarker on_no_need,
245      Mode mode);
246  void InformIncrementalMarker(MacroAssembler* masm);
247
248  void Activate(Code* code) {
249    code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code);
250  }
251
252  Register object() const {
253    return Register::from_code(ObjectBits::decode(minor_key_));
254  }
255
256  Register value() const {
257    return Register::from_code(ValueBits::decode(minor_key_));
258  }
259
260  Register address() const {
261    return Register::from_code(AddressBits::decode(minor_key_));
262  }
263
264  RememberedSetAction remembered_set_action() const {
265    return RememberedSetActionBits::decode(minor_key_);
266  }
267
268  SaveFPRegsMode save_fp_regs_mode() const {
269    return SaveFPRegsModeBits::decode(minor_key_);
270  }
271
272  class ObjectBits: public BitField<int, 0, 4> {};
273  class ValueBits: public BitField<int, 4, 4> {};
274  class AddressBits: public BitField<int, 8, 4> {};
275  class RememberedSetActionBits: public BitField<RememberedSetAction, 12, 1> {};
276  class SaveFPRegsModeBits: public BitField<SaveFPRegsMode, 13, 1> {};
277
278  Label slow_;
279  RegisterAllocation regs_;
280
281  DISALLOW_COPY_AND_ASSIGN(RecordWriteStub);
282};
283
284
285// Trampoline stub to call into native code. To call safely into native code
286// in the presence of compacting GC (which can move code objects) we need to
287// keep the code which called into native pinned in the memory. Currently the
288// simplest approach is to generate such stub early enough so it can never be
289// moved by GC
290class DirectCEntryStub: public PlatformCodeStub {
291 public:
292  explicit DirectCEntryStub(Isolate* isolate) : PlatformCodeStub(isolate) {}
293  void GenerateCall(MacroAssembler* masm, Register target);
294
295 private:
296  bool NeedsImmovableCode() { return true; }
297
298  DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR();
299  DEFINE_PLATFORM_CODE_STUB(DirectCEntry, PlatformCodeStub);
300};
301
302
303class NameDictionaryLookupStub: public PlatformCodeStub {
304 public:
305  enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP };
306
307  NameDictionaryLookupStub(Isolate* isolate, LookupMode mode)
308      : PlatformCodeStub(isolate) {
309    minor_key_ = LookupModeBits::encode(mode);
310  }
311
312  static void GenerateNegativeLookup(MacroAssembler* masm,
313                                     Label* miss,
314                                     Label* done,
315                                     Register receiver,
316                                     Register properties,
317                                     Handle<Name> name,
318                                     Register scratch0);
319
320  static void GeneratePositiveLookup(MacroAssembler* masm,
321                                     Label* miss,
322                                     Label* done,
323                                     Register elements,
324                                     Register name,
325                                     Register r0,
326                                     Register r1);
327
328  virtual bool SometimesSetsUpAFrame() { return false; }
329
330 private:
331  static const int kInlinedProbes = 4;
332  static const int kTotalProbes = 20;
333
334  static const int kCapacityOffset =
335      NameDictionary::kHeaderSize +
336      NameDictionary::kCapacityIndex * kPointerSize;
337
338  static const int kElementsStartOffset =
339      NameDictionary::kHeaderSize +
340      NameDictionary::kElementsStartIndex * kPointerSize;
341
342  LookupMode mode() const { return LookupModeBits::decode(minor_key_); }
343
344  class LookupModeBits: public BitField<LookupMode, 0, 1> {};
345
346  DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR();
347  DEFINE_PLATFORM_CODE_STUB(NameDictionaryLookup, PlatformCodeStub);
348};
349
350} }  // namespace v8::internal
351
352#endif  // V8_ARM_CODE_STUBS_ARM_H_
353