1// Copyright 2012 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_ARM_CODE_STUBS_ARM_H_
29#define V8_ARM_CODE_STUBS_ARM_H_
30
31#include "ic-inl.h"
32
33namespace v8 {
34namespace internal {
35
36
37void ArrayNativeCode(MacroAssembler* masm, Label* call_generic_code);
38
39
40// Compute a transcendental math function natively, or call the
41// TranscendentalCache runtime function.
42class TranscendentalCacheStub: public PlatformCodeStub {
43 public:
44  enum ArgumentType {
45    TAGGED = 0 << TranscendentalCache::kTranscendentalTypeBits,
46    UNTAGGED = 1 << TranscendentalCache::kTranscendentalTypeBits
47  };
48
49  TranscendentalCacheStub(TranscendentalCache::Type type,
50                          ArgumentType argument_type)
51      : type_(type), argument_type_(argument_type) { }
52  void Generate(MacroAssembler* masm);
53 private:
54  TranscendentalCache::Type type_;
55  ArgumentType argument_type_;
56  void GenerateCallCFunction(MacroAssembler* masm, Register scratch);
57
58  Major MajorKey() { return TranscendentalCache; }
59  int MinorKey() { return type_ | argument_type_; }
60  Runtime::FunctionId RuntimeFunction();
61};
62
63
64class StoreBufferOverflowStub: public PlatformCodeStub {
65 public:
66  explicit StoreBufferOverflowStub(SaveFPRegsMode save_fp)
67      : save_doubles_(save_fp) {}
68
69  void Generate(MacroAssembler* masm);
70
71  static void GenerateFixedRegStubsAheadOfTime(Isolate* isolate);
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
82class StringHelper : public AllStatic {
83 public:
84  // Generate code for copying characters using a simple loop. This should only
85  // be used in places where the number of characters is small and the
86  // additional setup and checking in GenerateCopyCharactersLong adds too much
87  // overhead. Copying of overlapping regions is not supported.
88  // Dest register ends at the position after the last character written.
89  static void GenerateCopyCharacters(MacroAssembler* masm,
90                                     Register dest,
91                                     Register src,
92                                     Register count,
93                                     Register scratch,
94                                     bool ascii);
95
96  // Generate code for copying a large number of characters. This function
97  // is allowed to spend extra time setting up conditions to make copying
98  // faster. Copying of overlapping regions is not supported.
99  // Dest register ends at the position after the last character written.
100  static void GenerateCopyCharactersLong(MacroAssembler* masm,
101                                         Register dest,
102                                         Register src,
103                                         Register count,
104                                         Register scratch1,
105                                         Register scratch2,
106                                         Register scratch3,
107                                         Register scratch4,
108                                         int flags);
109
110
111  // Probe the string table for a two character string. If the string is
112  // not found by probing a jump to the label not_found is performed. This jump
113  // does not guarantee that the string is not in the string table. If the
114  // string is found the code falls through with the string in register r0.
115  // Contents of both c1 and c2 registers are modified. At the exit c1 is
116  // guaranteed to contain halfword with low and high bytes equal to
117  // initial contents of c1 and c2 respectively.
118  static void GenerateTwoCharacterStringTableProbe(MacroAssembler* masm,
119                                                   Register c1,
120                                                   Register c2,
121                                                   Register scratch1,
122                                                   Register scratch2,
123                                                   Register scratch3,
124                                                   Register scratch4,
125                                                   Register scratch5,
126                                                   Label* not_found);
127
128  // Generate string hash.
129  static void GenerateHashInit(MacroAssembler* masm,
130                               Register hash,
131                               Register character);
132
133  static void GenerateHashAddCharacter(MacroAssembler* masm,
134                                       Register hash,
135                                       Register character);
136
137  static void GenerateHashGetHash(MacroAssembler* masm,
138                                  Register hash);
139
140 private:
141  DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper);
142};
143
144
145class StringAddStub: public PlatformCodeStub {
146 public:
147  explicit StringAddStub(StringAddFlags flags) : flags_(flags) {}
148
149 private:
150  Major MajorKey() { return StringAdd; }
151  int MinorKey() { return flags_; }
152
153  void Generate(MacroAssembler* masm);
154
155  void GenerateConvertArgument(MacroAssembler* masm,
156                               int stack_offset,
157                               Register arg,
158                               Register scratch1,
159                               Register scratch2,
160                               Register scratch3,
161                               Register scratch4,
162                               Label* slow);
163
164  void GenerateRegisterArgsPush(MacroAssembler* masm);
165  void GenerateRegisterArgsPop(MacroAssembler* masm);
166
167  const StringAddFlags flags_;
168};
169
170
171class SubStringStub: public PlatformCodeStub {
172 public:
173  SubStringStub() {}
174
175 private:
176  Major MajorKey() { return SubString; }
177  int MinorKey() { return 0; }
178
179  void Generate(MacroAssembler* masm);
180};
181
182
183
184class StringCompareStub: public PlatformCodeStub {
185 public:
186  StringCompareStub() { }
187
188  // Compares two flat ASCII strings and returns result in r0.
189  static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
190                                              Register left,
191                                              Register right,
192                                              Register scratch1,
193                                              Register scratch2,
194                                              Register scratch3,
195                                              Register scratch4);
196
197  // Compares two flat ASCII strings for equality and returns result
198  // in r0.
199  static void GenerateFlatAsciiStringEquals(MacroAssembler* masm,
200                                            Register left,
201                                            Register right,
202                                            Register scratch1,
203                                            Register scratch2,
204                                            Register scratch3);
205
206 private:
207  virtual Major MajorKey() { return StringCompare; }
208  virtual int MinorKey() { return 0; }
209  virtual void Generate(MacroAssembler* masm);
210
211  static void GenerateAsciiCharsCompareLoop(MacroAssembler* masm,
212                                            Register left,
213                                            Register right,
214                                            Register length,
215                                            Register scratch1,
216                                            Register scratch2,
217                                            Label* chars_not_equal);
218};
219
220
221// This stub can convert a signed int32 to a heap number (double).  It does
222// not work for int32s that are in Smi range!  No GC occurs during this stub
223// so you don't have to set up the frame.
224class WriteInt32ToHeapNumberStub : public PlatformCodeStub {
225 public:
226  WriteInt32ToHeapNumberStub(Register the_int,
227                             Register the_heap_number,
228                             Register scratch)
229      : the_int_(the_int),
230        the_heap_number_(the_heap_number),
231        scratch_(scratch) { }
232
233  static void GenerateFixedRegStubsAheadOfTime(Isolate* isolate);
234
235 private:
236  Register the_int_;
237  Register the_heap_number_;
238  Register scratch_;
239
240  // Minor key encoding in 16 bits.
241  class IntRegisterBits: public BitField<int, 0, 4> {};
242  class HeapNumberRegisterBits: public BitField<int, 4, 4> {};
243  class ScratchRegisterBits: public BitField<int, 8, 4> {};
244
245  Major MajorKey() { return WriteInt32ToHeapNumber; }
246  int MinorKey() {
247    // Encode the parameters in a unique 16 bit value.
248    return IntRegisterBits::encode(the_int_.code())
249           | HeapNumberRegisterBits::encode(the_heap_number_.code())
250           | ScratchRegisterBits::encode(scratch_.code());
251  }
252
253  void Generate(MacroAssembler* masm);
254};
255
256
257class RecordWriteStub: public PlatformCodeStub {
258 public:
259  RecordWriteStub(Register object,
260                  Register value,
261                  Register address,
262                  RememberedSetAction remembered_set_action,
263                  SaveFPRegsMode fp_mode)
264      : object_(object),
265        value_(value),
266        address_(address),
267        remembered_set_action_(remembered_set_action),
268        save_fp_regs_mode_(fp_mode),
269        regs_(object,   // An input reg.
270              address,  // An input reg.
271              value) {  // One scratch reg.
272  }
273
274  enum Mode {
275    STORE_BUFFER_ONLY,
276    INCREMENTAL,
277    INCREMENTAL_COMPACTION
278  };
279
280  virtual bool SometimesSetsUpAFrame() { return false; }
281
282  static void PatchBranchIntoNop(MacroAssembler* masm, int pos) {
283    masm->instr_at_put(pos, (masm->instr_at(pos) & ~B27) | (B24 | B20));
284    ASSERT(Assembler::IsTstImmediate(masm->instr_at(pos)));
285  }
286
287  static void PatchNopIntoBranch(MacroAssembler* masm, int pos) {
288    masm->instr_at_put(pos, (masm->instr_at(pos) & ~(B24 | B20)) | B27);
289    ASSERT(Assembler::IsBranch(masm->instr_at(pos)));
290  }
291
292  static Mode GetMode(Code* stub) {
293    Instr first_instruction = Assembler::instr_at(stub->instruction_start());
294    Instr second_instruction = Assembler::instr_at(stub->instruction_start() +
295                                                   Assembler::kInstrSize);
296
297    if (Assembler::IsBranch(first_instruction)) {
298      return INCREMENTAL;
299    }
300
301    ASSERT(Assembler::IsTstImmediate(first_instruction));
302
303    if (Assembler::IsBranch(second_instruction)) {
304      return INCREMENTAL_COMPACTION;
305    }
306
307    ASSERT(Assembler::IsTstImmediate(second_instruction));
308
309    return STORE_BUFFER_ONLY;
310  }
311
312  static void Patch(Code* stub, Mode mode) {
313    MacroAssembler masm(NULL,
314                        stub->instruction_start(),
315                        stub->instruction_size());
316    switch (mode) {
317      case STORE_BUFFER_ONLY:
318        ASSERT(GetMode(stub) == INCREMENTAL ||
319               GetMode(stub) == INCREMENTAL_COMPACTION);
320        PatchBranchIntoNop(&masm, 0);
321        PatchBranchIntoNop(&masm, Assembler::kInstrSize);
322        break;
323      case INCREMENTAL:
324        ASSERT(GetMode(stub) == STORE_BUFFER_ONLY);
325        PatchNopIntoBranch(&masm, 0);
326        break;
327      case INCREMENTAL_COMPACTION:
328        ASSERT(GetMode(stub) == STORE_BUFFER_ONLY);
329        PatchNopIntoBranch(&masm, Assembler::kInstrSize);
330        break;
331    }
332    ASSERT(GetMode(stub) == mode);
333    CPU::FlushICache(stub->instruction_start(), 2 * Assembler::kInstrSize);
334  }
335
336 private:
337  // This is a helper class for freeing up 3 scratch registers.  The input is
338  // two registers that must be preserved and one scratch register provided by
339  // the caller.
340  class RegisterAllocation {
341   public:
342    RegisterAllocation(Register object,
343                       Register address,
344                       Register scratch0)
345        : object_(object),
346          address_(address),
347          scratch0_(scratch0) {
348      ASSERT(!AreAliased(scratch0, object, address, no_reg));
349      scratch1_ = GetRegisterThatIsNotOneOf(object_, address_, scratch0_);
350    }
351
352    void Save(MacroAssembler* masm) {
353      ASSERT(!AreAliased(object_, address_, scratch1_, scratch0_));
354      // We don't have to save scratch0_ because it was given to us as
355      // a scratch register.
356      masm->push(scratch1_);
357    }
358
359    void Restore(MacroAssembler* masm) {
360      masm->pop(scratch1_);
361    }
362
363    // If we have to call into C then we need to save and restore all caller-
364    // saved registers that were not already preserved.  The scratch registers
365    // will be restored by other means so we don't bother pushing them here.
366    void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) {
367      masm->stm(db_w, sp, (kCallerSaved | lr.bit()) & ~scratch1_.bit());
368      if (mode == kSaveFPRegs) {
369        masm->SaveFPRegs(sp, scratch0_);
370      }
371    }
372
373    inline void RestoreCallerSaveRegisters(MacroAssembler*masm,
374                                           SaveFPRegsMode mode) {
375      if (mode == kSaveFPRegs) {
376        masm->RestoreFPRegs(sp, scratch0_);
377      }
378      masm->ldm(ia_w, sp, (kCallerSaved | lr.bit()) & ~scratch1_.bit());
379    }
380
381    inline Register object() { return object_; }
382    inline Register address() { return address_; }
383    inline Register scratch0() { return scratch0_; }
384    inline Register scratch1() { return scratch1_; }
385
386   private:
387    Register object_;
388    Register address_;
389    Register scratch0_;
390    Register scratch1_;
391
392    friend class RecordWriteStub;
393  };
394
395  enum OnNoNeedToInformIncrementalMarker {
396    kReturnOnNoNeedToInformIncrementalMarker,
397    kUpdateRememberedSetOnNoNeedToInformIncrementalMarker
398  };
399
400  void Generate(MacroAssembler* masm);
401  void GenerateIncremental(MacroAssembler* masm, Mode mode);
402  void CheckNeedsToInformIncrementalMarker(
403      MacroAssembler* masm,
404      OnNoNeedToInformIncrementalMarker on_no_need,
405      Mode mode);
406  void InformIncrementalMarker(MacroAssembler* masm, Mode mode);
407
408  Major MajorKey() { return RecordWrite; }
409
410  int MinorKey() {
411    return ObjectBits::encode(object_.code()) |
412        ValueBits::encode(value_.code()) |
413        AddressBits::encode(address_.code()) |
414        RememberedSetActionBits::encode(remembered_set_action_) |
415        SaveFPRegsModeBits::encode(save_fp_regs_mode_);
416  }
417
418  void Activate(Code* code) {
419    code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code);
420  }
421
422  class ObjectBits: public BitField<int, 0, 4> {};
423  class ValueBits: public BitField<int, 4, 4> {};
424  class AddressBits: public BitField<int, 8, 4> {};
425  class RememberedSetActionBits: public BitField<RememberedSetAction, 12, 1> {};
426  class SaveFPRegsModeBits: public BitField<SaveFPRegsMode, 13, 1> {};
427
428  Register object_;
429  Register value_;
430  Register address_;
431  RememberedSetAction remembered_set_action_;
432  SaveFPRegsMode save_fp_regs_mode_;
433  Label slow_;
434  RegisterAllocation regs_;
435};
436
437
438// Trampoline stub to call into native code. To call safely into native code
439// in the presence of compacting GC (which can move code objects) we need to
440// keep the code which called into native pinned in the memory. Currently the
441// simplest approach is to generate such stub early enough so it can never be
442// moved by GC
443class DirectCEntryStub: public PlatformCodeStub {
444 public:
445  DirectCEntryStub() {}
446  void Generate(MacroAssembler* masm);
447  void GenerateCall(MacroAssembler* masm, Register target);
448
449 private:
450  Major MajorKey() { return DirectCEntry; }
451  int MinorKey() { return 0; }
452
453  bool NeedsImmovableCode() { return true; }
454};
455
456
457class NameDictionaryLookupStub: public PlatformCodeStub {
458 public:
459  enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP };
460
461  explicit NameDictionaryLookupStub(LookupMode mode) : mode_(mode) { }
462
463  void Generate(MacroAssembler* masm);
464
465  static void GenerateNegativeLookup(MacroAssembler* masm,
466                                     Label* miss,
467                                     Label* done,
468                                     Register receiver,
469                                     Register properties,
470                                     Handle<Name> name,
471                                     Register scratch0);
472
473  static void GeneratePositiveLookup(MacroAssembler* masm,
474                                     Label* miss,
475                                     Label* done,
476                                     Register elements,
477                                     Register name,
478                                     Register r0,
479                                     Register r1);
480
481  virtual bool SometimesSetsUpAFrame() { return false; }
482
483 private:
484  static const int kInlinedProbes = 4;
485  static const int kTotalProbes = 20;
486
487  static const int kCapacityOffset =
488      NameDictionary::kHeaderSize +
489      NameDictionary::kCapacityIndex * kPointerSize;
490
491  static const int kElementsStartOffset =
492      NameDictionary::kHeaderSize +
493      NameDictionary::kElementsStartIndex * kPointerSize;
494
495  Major MajorKey() { return NameDictionaryLookup; }
496
497  int MinorKey() {
498    return LookupModeBits::encode(mode_);
499  }
500
501  class LookupModeBits: public BitField<LookupMode, 0, 1> {};
502
503  LookupMode mode_;
504};
505
506
507} }  // namespace v8::internal
508
509#endif  // V8_ARM_CODE_STUBS_ARM_H_
510