• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
35 namespace v8 {
36 namespace internal {
37 
38 
39 void 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.
45 class TranscendentalCacheStub: public PlatformCodeStub {
46  public:
47   enum ArgumentType {
48     TAGGED = 0,
49     UNTAGGED = 1 << TranscendentalCache::kTranscendentalTypeBits
50   };
51 
TranscendentalCacheStub(TranscendentalCache::Type type,ArgumentType argument_type)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 
MajorKey()62   Major MajorKey() { return TranscendentalCache; }
MinorKey()63   int MinorKey() { return type_ | argument_type_; }
64   Runtime::FunctionId RuntimeFunction();
65 };
66 
67 
68 class StoreBufferOverflowStub: public PlatformCodeStub {
69  public:
StoreBufferOverflowStub(SaveFPRegsMode save_fp)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);
SometimesSetsUpAFrame()78   virtual bool SometimesSetsUpAFrame() { return false; }
79 
80  private:
81   SaveFPRegsMode save_doubles_;
82 
MajorKey()83   Major MajorKey() { return StoreBufferOverflow; }
MinorKey()84   int MinorKey() { return (save_doubles_ == kSaveFPRegs) ? 1 : 0; }
85 };
86 
87 
88 class 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 
146 class StringAddStub: public PlatformCodeStub {
147  public:
StringAddStub(StringAddFlags flags)148   explicit StringAddStub(StringAddFlags flags) : flags_(flags) {}
149 
150  private:
MajorKey()151   Major MajorKey() { return StringAdd; }
MinorKey()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 
171 class SubStringStub: public PlatformCodeStub {
172  public:
SubStringStub()173   SubStringStub() {}
174 
175  private:
MajorKey()176   Major MajorKey() { return SubString; }
MinorKey()177   int MinorKey() { return 0; }
178 
179   void Generate(MacroAssembler* masm);
180 };
181 
182 
183 class StringCompareStub: public PlatformCodeStub {
184  public:
StringCompareStub()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:
MajorKey()204   virtual Major MajorKey() { return StringCompare; }
MinorKey()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 
219 class NameDictionaryLookupStub: public PlatformCodeStub {
220  public:
221   enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP };
222 
NameDictionaryLookupStub(Register dictionary,Register result,Register index,LookupMode mode)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 
SometimesSetsUpAFrame()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 
MajorKey()260   Major MajorKey() { return NameDictionaryLookup; }
261 
MinorKey()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 
281 class RecordWriteStub: public PlatformCodeStub {
282  public:
RecordWriteStub(Register object,Register value,Register address,RememberedSetAction remembered_set_action,SaveFPRegsMode fp_mode)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 
SometimesSetsUpAFrame()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 
GetMode(Code * stub)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 
Patch(Code * stub,Mode mode)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:
RegisterAllocation(Register object,Register address,Register scratch0)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 
Save(MacroAssembler * masm)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 
Restore(MacroAssembler * masm)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.
SaveCallerSaveRegisters(MacroAssembler * masm,SaveFPRegsMode mode)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 
RestoreCallerSaveRegisters(MacroAssembler * masm,SaveFPRegsMode mode)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 
object()465     inline Register object() { return object_; }
address()466     inline Register address() { return address_; }
scratch0()467     inline Register scratch0() { return scratch0_; }
scratch1()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 
GetRegThatIsNotEcxOr(Register r1,Register r2,Register r3)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 
MajorKey()510   Major MajorKey() { return RecordWrite; }
511 
MinorKey()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 
Activate(Code * code)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