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_IA32_CODE_STUBS_IA32_H_
29#define V8_IA32_CODE_STUBS_IA32_H_
30
31#include "macro-assembler.h"
32#include "code-stubs.h"
33#include "ic-inl.h"
34
35namespace v8 {
36namespace internal {
37
38
39void ArrayNativeCode(MacroAssembler* masm,
40                     bool construct_call,
41                     Label* call_generic_code);
42
43// Compute a transcendental math function natively, or call the
44// TranscendentalCache runtime function.
45class TranscendentalCacheStub: public PlatformCodeStub {
46 public:
47  enum ArgumentType {
48    TAGGED = 0,
49    UNTAGGED = 1 << TranscendentalCache::kTranscendentalTypeBits
50  };
51
52  TranscendentalCacheStub(TranscendentalCache::Type type,
53                          ArgumentType argument_type)
54      : type_(type), argument_type_(argument_type) {}
55  void Generate(MacroAssembler* masm);
56  static void GenerateOperation(MacroAssembler* masm,
57                                TranscendentalCache::Type type);
58 private:
59  TranscendentalCache::Type type_;
60  ArgumentType argument_type_;
61
62  Major MajorKey() { return TranscendentalCache; }
63  int MinorKey() { return type_ | argument_type_; }
64  Runtime::FunctionId RuntimeFunction();
65};
66
67
68class StoreBufferOverflowStub: public PlatformCodeStub {
69 public:
70  explicit StoreBufferOverflowStub(SaveFPRegsMode save_fp)
71      : save_doubles_(save_fp) {
72    ASSERT(CpuFeatures::IsSafeForSnapshot(SSE2) || save_fp == kDontSaveFPRegs);
73  }
74
75  void Generate(MacroAssembler* masm);
76
77  static void GenerateFixedRegStubsAheadOfTime(Isolate* isolate);
78  virtual bool SometimesSetsUpAFrame() { return false; }
79
80 private:
81  SaveFPRegsMode save_doubles_;
82
83  Major MajorKey() { return StoreBufferOverflow; }
84  int MinorKey() { return (save_doubles_ == kSaveFPRegs) ? 1 : 0; }
85};
86
87
88class StringHelper : public AllStatic {
89 public:
90  // Generate code for copying characters using a simple loop. This should only
91  // be used in places where the number of characters is small and the
92  // additional setup and checking in GenerateCopyCharactersREP adds too much
93  // overhead. Copying of overlapping regions is not supported.
94  static void GenerateCopyCharacters(MacroAssembler* masm,
95                                     Register dest,
96                                     Register src,
97                                     Register count,
98                                     Register scratch,
99                                     bool ascii);
100
101  // Generate code for copying characters using the rep movs instruction.
102  // Copies ecx characters from esi to edi. Copying of overlapping regions is
103  // not supported.
104  static void GenerateCopyCharactersREP(MacroAssembler* masm,
105                                        Register dest,     // Must be edi.
106                                        Register src,      // Must be esi.
107                                        Register count,    // Must be ecx.
108                                        Register scratch,  // Neither of above.
109                                        bool ascii);
110
111  // Probe the string table for a two character string. If the string
112  // requires non-standard hashing a jump to the label not_probed is
113  // performed and registers c1 and c2 are preserved. In all other
114  // cases they are clobbered. If the string is not found by probing a
115  // jump to the label not_found is performed. This jump does not
116  // guarantee that the string is not in the string table. If the
117  // string is found the code falls through with the string in
118  // register eax.
119  static void GenerateTwoCharacterStringTableProbe(MacroAssembler* masm,
120                                                   Register c1,
121                                                   Register c2,
122                                                   Register scratch1,
123                                                   Register scratch2,
124                                                   Register scratch3,
125                                                   Label* not_probed,
126                                                   Label* not_found);
127
128  // Generate string hash.
129  static void GenerateHashInit(MacroAssembler* masm,
130                               Register hash,
131                               Register character,
132                               Register scratch);
133  static void GenerateHashAddCharacter(MacroAssembler* masm,
134                                       Register hash,
135                                       Register character,
136                                       Register scratch);
137  static void GenerateHashGetHash(MacroAssembler* masm,
138                                  Register hash,
139                                  Register scratch);
140
141 private:
142  DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper);
143};
144
145
146class StringAddStub: public PlatformCodeStub {
147 public:
148  explicit StringAddStub(StringAddFlags flags) : flags_(flags) {}
149
150 private:
151  Major MajorKey() { return StringAdd; }
152  int MinorKey() { return flags_; }
153
154  void Generate(MacroAssembler* masm);
155
156  void GenerateConvertArgument(MacroAssembler* masm,
157                               int stack_offset,
158                               Register arg,
159                               Register scratch1,
160                               Register scratch2,
161                               Register scratch3,
162                               Label* slow);
163
164  void GenerateRegisterArgsPush(MacroAssembler* masm);
165  void GenerateRegisterArgsPop(MacroAssembler* masm, Register temp);
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
183class StringCompareStub: public PlatformCodeStub {
184 public:
185  StringCompareStub() { }
186
187  // Compares two flat ASCII strings and returns result in eax.
188  static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
189                                              Register left,
190                                              Register right,
191                                              Register scratch1,
192                                              Register scratch2,
193                                              Register scratch3);
194
195  // Compares two flat ASCII strings for equality and returns result
196  // in eax.
197  static void GenerateFlatAsciiStringEquals(MacroAssembler* masm,
198                                            Register left,
199                                            Register right,
200                                            Register scratch1,
201                                            Register scratch2);
202
203 private:
204  virtual Major MajorKey() { return StringCompare; }
205  virtual int MinorKey() { return 0; }
206  virtual void Generate(MacroAssembler* masm);
207
208  static void GenerateAsciiCharsCompareLoop(
209      MacroAssembler* masm,
210      Register left,
211      Register right,
212      Register length,
213      Register scratch,
214      Label* chars_not_equal,
215      Label::Distance chars_not_equal_near = Label::kFar);
216};
217
218
219class NameDictionaryLookupStub: public PlatformCodeStub {
220 public:
221  enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP };
222
223  NameDictionaryLookupStub(Register dictionary,
224                           Register result,
225                           Register index,
226                           LookupMode mode)
227      : dictionary_(dictionary), result_(result), index_(index), mode_(mode) { }
228
229  void Generate(MacroAssembler* masm);
230
231  static void GenerateNegativeLookup(MacroAssembler* masm,
232                                     Label* miss,
233                                     Label* done,
234                                     Register properties,
235                                     Handle<Name> name,
236                                     Register r0);
237
238  static void GeneratePositiveLookup(MacroAssembler* masm,
239                                     Label* miss,
240                                     Label* done,
241                                     Register elements,
242                                     Register name,
243                                     Register r0,
244                                     Register r1);
245
246  virtual bool SometimesSetsUpAFrame() { return false; }
247
248 private:
249  static const int kInlinedProbes = 4;
250  static const int kTotalProbes = 20;
251
252  static const int kCapacityOffset =
253      NameDictionary::kHeaderSize +
254      NameDictionary::kCapacityIndex * kPointerSize;
255
256  static const int kElementsStartOffset =
257      NameDictionary::kHeaderSize +
258      NameDictionary::kElementsStartIndex * kPointerSize;
259
260  Major MajorKey() { return NameDictionaryLookup; }
261
262  int MinorKey() {
263    return DictionaryBits::encode(dictionary_.code()) |
264        ResultBits::encode(result_.code()) |
265        IndexBits::encode(index_.code()) |
266        LookupModeBits::encode(mode_);
267  }
268
269  class DictionaryBits: public BitField<int, 0, 3> {};
270  class ResultBits: public BitField<int, 3, 3> {};
271  class IndexBits: public BitField<int, 6, 3> {};
272  class LookupModeBits: public BitField<LookupMode, 9, 1> {};
273
274  Register dictionary_;
275  Register result_;
276  Register index_;
277  LookupMode mode_;
278};
279
280
281class RecordWriteStub: public PlatformCodeStub {
282 public:
283  RecordWriteStub(Register object,
284                  Register value,
285                  Register address,
286                  RememberedSetAction remembered_set_action,
287                  SaveFPRegsMode fp_mode)
288      : object_(object),
289        value_(value),
290        address_(address),
291        remembered_set_action_(remembered_set_action),
292        save_fp_regs_mode_(fp_mode),
293        regs_(object,   // An input reg.
294              address,  // An input reg.
295              value) {  // One scratch reg.
296    ASSERT(CpuFeatures::IsSafeForSnapshot(SSE2) || fp_mode == kDontSaveFPRegs);
297  }
298
299  enum Mode {
300    STORE_BUFFER_ONLY,
301    INCREMENTAL,
302    INCREMENTAL_COMPACTION
303  };
304
305  virtual bool SometimesSetsUpAFrame() { return false; }
306
307  static const byte kTwoByteNopInstruction = 0x3c;  // Cmpb al, #imm8.
308  static const byte kTwoByteJumpInstruction = 0xeb;  // Jmp #imm8.
309
310  static const byte kFiveByteNopInstruction = 0x3d;  // Cmpl eax, #imm32.
311  static const byte kFiveByteJumpInstruction = 0xe9;  // Jmp #imm32.
312
313  static Mode GetMode(Code* stub) {
314    byte first_instruction = stub->instruction_start()[0];
315    byte second_instruction = stub->instruction_start()[2];
316
317    if (first_instruction == kTwoByteJumpInstruction) {
318      return INCREMENTAL;
319    }
320
321    ASSERT(first_instruction == kTwoByteNopInstruction);
322
323    if (second_instruction == kFiveByteJumpInstruction) {
324      return INCREMENTAL_COMPACTION;
325    }
326
327    ASSERT(second_instruction == kFiveByteNopInstruction);
328
329    return STORE_BUFFER_ONLY;
330  }
331
332  static void Patch(Code* stub, Mode mode) {
333    switch (mode) {
334      case STORE_BUFFER_ONLY:
335        ASSERT(GetMode(stub) == INCREMENTAL ||
336               GetMode(stub) == INCREMENTAL_COMPACTION);
337        stub->instruction_start()[0] = kTwoByteNopInstruction;
338        stub->instruction_start()[2] = kFiveByteNopInstruction;
339        break;
340      case INCREMENTAL:
341        ASSERT(GetMode(stub) == STORE_BUFFER_ONLY);
342        stub->instruction_start()[0] = kTwoByteJumpInstruction;
343        break;
344      case INCREMENTAL_COMPACTION:
345        ASSERT(GetMode(stub) == STORE_BUFFER_ONLY);
346        stub->instruction_start()[0] = kTwoByteNopInstruction;
347        stub->instruction_start()[2] = kFiveByteJumpInstruction;
348        break;
349    }
350    ASSERT(GetMode(stub) == mode);
351    CPU::FlushICache(stub->instruction_start(), 7);
352  }
353
354 private:
355  // This is a helper class for freeing up 3 scratch registers, where the third
356  // is always ecx (needed for shift operations).  The input is two registers
357  // that must be preserved and one scratch register provided by the caller.
358  class RegisterAllocation {
359   public:
360    RegisterAllocation(Register object,
361                       Register address,
362                       Register scratch0)
363        : object_orig_(object),
364          address_orig_(address),
365          scratch0_orig_(scratch0),
366          object_(object),
367          address_(address),
368          scratch0_(scratch0) {
369      ASSERT(!AreAliased(scratch0, object, address, no_reg));
370      scratch1_ = GetRegThatIsNotEcxOr(object_, address_, scratch0_);
371      if (scratch0.is(ecx)) {
372        scratch0_ = GetRegThatIsNotEcxOr(object_, address_, scratch1_);
373      }
374      if (object.is(ecx)) {
375        object_ = GetRegThatIsNotEcxOr(address_, scratch0_, scratch1_);
376      }
377      if (address.is(ecx)) {
378        address_ = GetRegThatIsNotEcxOr(object_, scratch0_, scratch1_);
379      }
380      ASSERT(!AreAliased(scratch0_, object_, address_, ecx));
381    }
382
383    void Save(MacroAssembler* masm) {
384      ASSERT(!address_orig_.is(object_));
385      ASSERT(object_.is(object_orig_) || address_.is(address_orig_));
386      ASSERT(!AreAliased(object_, address_, scratch1_, scratch0_));
387      ASSERT(!AreAliased(object_orig_, address_, scratch1_, scratch0_));
388      ASSERT(!AreAliased(object_, address_orig_, scratch1_, scratch0_));
389      // We don't have to save scratch0_orig_ because it was given to us as
390      // a scratch register.  But if we had to switch to a different reg then
391      // we should save the new scratch0_.
392      if (!scratch0_.is(scratch0_orig_)) masm->push(scratch0_);
393      if (!ecx.is(scratch0_orig_) &&
394          !ecx.is(object_orig_) &&
395          !ecx.is(address_orig_)) {
396        masm->push(ecx);
397      }
398      masm->push(scratch1_);
399      if (!address_.is(address_orig_)) {
400        masm->push(address_);
401        masm->mov(address_, address_orig_);
402      }
403      if (!object_.is(object_orig_)) {
404        masm->push(object_);
405        masm->mov(object_, object_orig_);
406      }
407    }
408
409    void Restore(MacroAssembler* masm) {
410      // These will have been preserved the entire time, so we just need to move
411      // them back.  Only in one case is the orig_ reg different from the plain
412      // one, since only one of them can alias with ecx.
413      if (!object_.is(object_orig_)) {
414        masm->mov(object_orig_, object_);
415        masm->pop(object_);
416      }
417      if (!address_.is(address_orig_)) {
418        masm->mov(address_orig_, address_);
419        masm->pop(address_);
420      }
421      masm->pop(scratch1_);
422      if (!ecx.is(scratch0_orig_) &&
423          !ecx.is(object_orig_) &&
424          !ecx.is(address_orig_)) {
425        masm->pop(ecx);
426      }
427      if (!scratch0_.is(scratch0_orig_)) masm->pop(scratch0_);
428    }
429
430    // If we have to call into C then we need to save and restore all caller-
431    // saved registers that were not already preserved.  The caller saved
432    // registers are eax, ecx and edx.  The three scratch registers (incl. ecx)
433    // will be restored by other means so we don't bother pushing them here.
434    void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) {
435      if (!scratch0_.is(eax) && !scratch1_.is(eax)) masm->push(eax);
436      if (!scratch0_.is(edx) && !scratch1_.is(edx)) masm->push(edx);
437      if (mode == kSaveFPRegs) {
438        CpuFeatureScope scope(masm, SSE2);
439        masm->sub(esp,
440                  Immediate(kDoubleSize * (XMMRegister::kNumRegisters - 1)));
441        // Save all XMM registers except XMM0.
442        for (int i = XMMRegister::kNumRegisters - 1; i > 0; i--) {
443          XMMRegister reg = XMMRegister::from_code(i);
444          masm->movsd(Operand(esp, (i - 1) * kDoubleSize), reg);
445        }
446      }
447    }
448
449    inline void RestoreCallerSaveRegisters(MacroAssembler*masm,
450                                           SaveFPRegsMode mode) {
451      if (mode == kSaveFPRegs) {
452        CpuFeatureScope scope(masm, SSE2);
453        // Restore all XMM registers except XMM0.
454        for (int i = XMMRegister::kNumRegisters - 1; i > 0; i--) {
455          XMMRegister reg = XMMRegister::from_code(i);
456          masm->movsd(reg, Operand(esp, (i - 1) * kDoubleSize));
457        }
458        masm->add(esp,
459                  Immediate(kDoubleSize * (XMMRegister::kNumRegisters - 1)));
460      }
461      if (!scratch0_.is(edx) && !scratch1_.is(edx)) masm->pop(edx);
462      if (!scratch0_.is(eax) && !scratch1_.is(eax)) masm->pop(eax);
463    }
464
465    inline Register object() { return object_; }
466    inline Register address() { return address_; }
467    inline Register scratch0() { return scratch0_; }
468    inline Register scratch1() { return scratch1_; }
469
470   private:
471    Register object_orig_;
472    Register address_orig_;
473    Register scratch0_orig_;
474    Register object_;
475    Register address_;
476    Register scratch0_;
477    Register scratch1_;
478    // Third scratch register is always ecx.
479
480    Register GetRegThatIsNotEcxOr(Register r1,
481                                  Register r2,
482                                  Register r3) {
483      for (int i = 0; i < Register::NumAllocatableRegisters(); i++) {
484        Register candidate = Register::FromAllocationIndex(i);
485        if (candidate.is(ecx)) continue;
486        if (candidate.is(r1)) continue;
487        if (candidate.is(r2)) continue;
488        if (candidate.is(r3)) continue;
489        return candidate;
490      }
491      UNREACHABLE();
492      return no_reg;
493    }
494    friend class RecordWriteStub;
495  };
496
497  enum OnNoNeedToInformIncrementalMarker {
498    kReturnOnNoNeedToInformIncrementalMarker,
499    kUpdateRememberedSetOnNoNeedToInformIncrementalMarker
500  }
501;
502  void Generate(MacroAssembler* masm);
503  void GenerateIncremental(MacroAssembler* masm, Mode mode);
504  void CheckNeedsToInformIncrementalMarker(
505      MacroAssembler* masm,
506      OnNoNeedToInformIncrementalMarker on_no_need,
507      Mode mode);
508  void InformIncrementalMarker(MacroAssembler* masm, Mode mode);
509
510  Major MajorKey() { return RecordWrite; }
511
512  int MinorKey() {
513    return ObjectBits::encode(object_.code()) |
514        ValueBits::encode(value_.code()) |
515        AddressBits::encode(address_.code()) |
516        RememberedSetActionBits::encode(remembered_set_action_) |
517        SaveFPRegsModeBits::encode(save_fp_regs_mode_);
518  }
519
520  void Activate(Code* code) {
521    code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code);
522  }
523
524  class ObjectBits: public BitField<int, 0, 3> {};
525  class ValueBits: public BitField<int, 3, 3> {};
526  class AddressBits: public BitField<int, 6, 3> {};
527  class RememberedSetActionBits: public BitField<RememberedSetAction, 9, 1> {};
528  class SaveFPRegsModeBits: public BitField<SaveFPRegsMode, 10, 1> {};
529
530  Register object_;
531  Register value_;
532  Register address_;
533  RememberedSetAction remembered_set_action_;
534  SaveFPRegsMode save_fp_regs_mode_;
535  RegisterAllocation regs_;
536};
537
538
539} }  // namespace v8::internal
540
541#endif  // V8_IA32_CODE_STUBS_IA32_H_
542