Transition op_new_* JITStubs to JIT operations.
[WebKit-https.git] / Source / JavaScriptCore / jit / JSInterfaceJIT.h
1 /*
2  * Copyright (C) 2010 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #ifndef JSInterfaceJIT_h
27 #define JSInterfaceJIT_h
28
29 #include "BytecodeConventions.h"
30 #include "CCallHelpers.h"
31 #include "JITCode.h"
32 #include "JITOperations.h"
33 #include "JITStubs.h"
34 #include "JSCJSValue.h"
35 #include "JSStack.h"
36 #include "JSString.h"
37 #include "MacroAssembler.h"
38 #include <wtf/Vector.h>
39
40 #if ENABLE(JIT)
41
42 namespace JSC {
43     class JSInterfaceJIT : public CCallHelpers {
44     public:
45         JSInterfaceJIT(VM* vm, CodeBlock* codeBlock = 0)
46             : CCallHelpers(vm, codeBlock)
47         {
48         }
49         
50         // NOTES:
51         //
52         // regT0 has two special meanings.  The return value from a stub
53         // call will always be in regT0, and by default (unless
54         // a register is specified) emitPutVirtualRegister() will store
55         // the value from regT0.
56         //
57         // regT3 is required to be callee-preserved.
58         //
59         // tempRegister2 is has no such dependencies.  It is important that
60         // on x86/x86-64 it is ecx for performance reasons, since the
61         // MacroAssembler will need to plant register swaps if it is not -
62         // however the code will still function correctly.
63 #if CPU(X86_64)
64         static const RegisterID returnValueRegister = X86Registers::eax;
65         static const RegisterID cachedResultRegister = X86Registers::eax;
66 #if !OS(WINDOWS)
67         static const RegisterID firstArgumentRegister = X86Registers::edi;
68         static const RegisterID secondArgumentRegister = X86Registers::esi;
69 #else
70         static const RegisterID firstArgumentRegister = X86Registers::ecx;
71         static const RegisterID secondArgumentRegister = X86Registers::edx;
72         static const RegisterID thirdArgumentRegister = X86Registers::r8;
73 #endif
74
75         static const RegisterID callFrameRegister = X86Registers::r13;
76         static const RegisterID tagTypeNumberRegister = X86Registers::r14;
77         static const RegisterID tagMaskRegister = X86Registers::r15;
78
79         static const RegisterID regT0 = X86Registers::eax;
80         static const RegisterID regT1 = X86Registers::edx;
81         static const RegisterID regT2 = X86Registers::ecx;
82         static const RegisterID regT3 = X86Registers::ebx;
83         static const RegisterID regT4 = X86Registers::r10;
84
85         static const FPRegisterID fpRegT0 = X86Registers::xmm0;
86         static const FPRegisterID fpRegT1 = X86Registers::xmm1;
87         static const FPRegisterID fpRegT2 = X86Registers::xmm2;
88         static const FPRegisterID fpRegT3 = X86Registers::xmm3;
89
90         static const RegisterID nonArgGPR1 = X86Registers::eax; // regT0
91 #elif CPU(X86)
92         static const RegisterID returnValueRegister = X86Registers::eax;
93         static const RegisterID returnValue2Register = X86Registers::edx;
94         static const RegisterID cachedResultRegister = X86Registers::eax;
95         // On x86 we always use fastcall conventions = but on
96         // OS X if might make more sense to just use regparm.
97         static const RegisterID firstArgumentRegister = X86Registers::ecx;
98         static const RegisterID secondArgumentRegister = X86Registers::edx;
99         
100         static const RegisterID callFrameRegister = X86Registers::edi;
101         
102         static const RegisterID regT0 = X86Registers::eax;
103         static const RegisterID regT1 = X86Registers::edx;
104         static const RegisterID regT2 = X86Registers::ecx;
105         static const RegisterID regT3 = X86Registers::ebx;
106         static const RegisterID regT4 = X86Registers::esi;
107         
108         static const FPRegisterID fpRegT0 = X86Registers::xmm0;
109         static const FPRegisterID fpRegT1 = X86Registers::xmm1;
110         static const FPRegisterID fpRegT2 = X86Registers::xmm2;
111         static const FPRegisterID fpRegT3 = X86Registers::xmm3;
112 #elif CPU(ARM)
113         static const RegisterID returnValueRegister = ARMRegisters::r0;
114         static const RegisterID returnValue2Register = ARMRegisters::r1;
115         static const RegisterID cachedResultRegister = ARMRegisters::r0;
116         static const RegisterID firstArgumentRegister = ARMRegisters::r0;
117         static const RegisterID secondArgumentRegister = ARMRegisters::r1;
118
119 #if ENABLE(VALUE_PROFILER)
120 #endif
121
122         static const RegisterID regT0 = ARMRegisters::r0;
123         static const RegisterID regT1 = ARMRegisters::r1;
124         static const RegisterID regT2 = ARMRegisters::r2;
125         static const RegisterID regT3 = ARMRegisters::r4;
126         static const RegisterID regT4 = ARMRegisters::r7;
127
128         // Update ctiTrampoline in JITStubs.cpp if these values are changed!
129         static const RegisterID callFrameRegister = ARMRegisters::r5;
130
131         static const FPRegisterID fpRegT0 = ARMRegisters::d0;
132         static const FPRegisterID fpRegT1 = ARMRegisters::d1;
133         static const FPRegisterID fpRegT2 = ARMRegisters::d2;
134         static const FPRegisterID fpRegT3 = ARMRegisters::d3;
135 #elif CPU(MIPS)
136         static const RegisterID returnValueRegister = MIPSRegisters::v0;
137         static const RegisterID returnValue2Register = MIPSRegisters::v1;
138         static const RegisterID cachedResultRegister = MIPSRegisters::v0;
139         static const RegisterID firstArgumentRegister = MIPSRegisters::a0;
140         static const RegisterID secondArgumentRegister = MIPSRegisters::a1;
141         
142         // regT0 must be v0 for returning a 32-bit value.
143         static const RegisterID regT0 = MIPSRegisters::v0;
144         
145         // regT1 must be v1 for returning a pair of 32-bit value.
146         static const RegisterID regT1 = MIPSRegisters::v1;
147         
148         static const RegisterID regT2 = MIPSRegisters::t4;
149         
150         // regT3 must be saved in the callee, so use an S register.
151         static const RegisterID regT3 = MIPSRegisters::s2;
152         
153         static const RegisterID regT4 = MIPSRegisters::s3;
154
155         static const RegisterID callFrameRegister = MIPSRegisters::s0;
156         
157         static const FPRegisterID fpRegT0 = MIPSRegisters::f4;
158         static const FPRegisterID fpRegT1 = MIPSRegisters::f6;
159         static const FPRegisterID fpRegT2 = MIPSRegisters::f8;
160         static const FPRegisterID fpRegT3 = MIPSRegisters::f10;
161 #elif CPU(SH4)
162         static const RegisterID callFrameRegister = SH4Registers::fp;
163
164         static const RegisterID regT0 = SH4Registers::r0;
165         static const RegisterID regT1 = SH4Registers::r1;
166         static const RegisterID regT2 = SH4Registers::r2;
167         static const RegisterID regT3 = SH4Registers::r10;
168         static const RegisterID regT4 = SH4Registers::r4;
169         static const RegisterID regT5 = SH4Registers::r5;
170         static const RegisterID regT6 = SH4Registers::r6;
171         static const RegisterID regT7 = SH4Registers::r7;
172         static const RegisterID firstArgumentRegister = regT4;
173         static const RegisterID secondArgumentRegister = regT5;
174
175         static const RegisterID returnValueRegister = SH4Registers::r0;
176         static const RegisterID returnValue2Register = SH4Registers::r1;
177         static const RegisterID cachedResultRegister = SH4Registers::r0;
178
179         static const FPRegisterID fpRegT0 = SH4Registers::dr0;
180         static const FPRegisterID fpRegT1 = SH4Registers::dr2;
181         static const FPRegisterID fpRegT2 = SH4Registers::dr4;
182         static const FPRegisterID fpRegT3 = SH4Registers::dr6;
183         static const FPRegisterID fpRegT4 = SH4Registers::dr8;
184         static const FPRegisterID fpRegT5 = SH4Registers::dr10;
185 #else
186 #error "JIT not supported on this platform."
187 #endif
188
189 #if USE(JSVALUE32_64)
190         // Can't just propogate JSValue::Int32Tag as visual studio doesn't like it
191         static const unsigned Int32Tag = 0xffffffff;
192         COMPILE_ASSERT(Int32Tag == JSValue::Int32Tag, Int32Tag_out_of_sync);
193 #else
194         static const unsigned Int32Tag = static_cast<unsigned>(TagTypeNumber >> 32);
195 #endif
196         inline Jump emitLoadJSCell(unsigned virtualRegisterIndex, RegisterID payload);
197         inline Jump emitLoadInt32(unsigned virtualRegisterIndex, RegisterID dst);
198         inline Jump emitLoadDouble(unsigned virtualRegisterIndex, FPRegisterID dst, RegisterID scratch);
199
200 #if USE(JSVALUE32_64)
201         inline Jump emitJumpIfNotJSCell(unsigned virtualRegisterIndex);
202         inline Address tagFor(int index, RegisterID base = callFrameRegister);
203 #endif
204
205 #if USE(JSVALUE64)
206         Jump emitJumpIfNotJSCell(RegisterID);
207         Jump emitJumpIfImmediateNumber(RegisterID reg);
208         Jump emitJumpIfNotImmediateNumber(RegisterID reg);
209         void emitFastArithImmToInt(RegisterID reg);
210         void emitFastArithIntToImmNoCheck(RegisterID src, RegisterID dest);
211 #endif
212
213         Jump emitJumpIfNotType(RegisterID baseReg, RegisterID scratchReg, JSType);
214
215         void emitGetFromCallFrameHeaderPtr(JSStack::CallFrameHeaderEntry, RegisterID to, RegisterID from = callFrameRegister);
216         void emitPutToCallFrameHeader(RegisterID from, JSStack::CallFrameHeaderEntry);
217         void emitPutImmediateToCallFrameHeader(void* value, JSStack::CallFrameHeaderEntry);
218         void emitPutCellToCallFrameHeader(RegisterID from, JSStack::CallFrameHeaderEntry);
219
220         void restoreArgumentReference();
221
222         inline Address payloadFor(int index, RegisterID base = callFrameRegister);
223         inline Address intPayloadFor(int index, RegisterID base = callFrameRegister);
224         inline Address intTagFor(int index, RegisterID base = callFrameRegister);
225         inline Address addressFor(int index, RegisterID base = callFrameRegister);
226     };
227
228     struct ThunkHelpers {
229         static unsigned jsStringLengthOffset() { return OBJECT_OFFSETOF(JSString, m_length); }
230         static unsigned jsStringValueOffset() { return OBJECT_OFFSETOF(JSString, m_value); }
231     };
232
233 #if USE(JSVALUE32_64)
234     inline JSInterfaceJIT::Jump JSInterfaceJIT::emitLoadJSCell(unsigned virtualRegisterIndex, RegisterID payload)
235     {
236         loadPtr(payloadFor(virtualRegisterIndex), payload);
237         return emitJumpIfNotJSCell(virtualRegisterIndex);
238     }
239
240     inline JSInterfaceJIT::Jump JSInterfaceJIT::emitJumpIfNotJSCell(unsigned virtualRegisterIndex)
241     {
242         ASSERT(static_cast<int>(virtualRegisterIndex) < FirstConstantRegisterIndex);
243         return branch32(NotEqual, tagFor(virtualRegisterIndex), TrustedImm32(JSValue::CellTag));
244     }
245     
246     inline JSInterfaceJIT::Jump JSInterfaceJIT::emitLoadInt32(unsigned virtualRegisterIndex, RegisterID dst)
247     {
248         ASSERT(static_cast<int>(virtualRegisterIndex) < FirstConstantRegisterIndex);
249         loadPtr(payloadFor(virtualRegisterIndex), dst);
250         return branch32(NotEqual, tagFor(static_cast<int>(virtualRegisterIndex)), TrustedImm32(JSValue::Int32Tag));
251     }
252     
253     inline JSInterfaceJIT::Address JSInterfaceJIT::tagFor(int virtualRegisterIndex, RegisterID base)
254     {
255         ASSERT(virtualRegisterIndex < FirstConstantRegisterIndex);
256         return Address(base, (static_cast<unsigned>(virtualRegisterIndex) * sizeof(Register)) + OBJECT_OFFSETOF(JSValue, u.asBits.tag));
257     }
258     
259     inline JSInterfaceJIT::Address JSInterfaceJIT::payloadFor(int virtualRegisterIndex, RegisterID base)
260     {
261         ASSERT(virtualRegisterIndex < FirstConstantRegisterIndex);
262         return Address(base, (static_cast<unsigned>(virtualRegisterIndex) * sizeof(Register)) + OBJECT_OFFSETOF(JSValue, u.asBits.payload));
263     }
264
265     inline JSInterfaceJIT::Address JSInterfaceJIT::intPayloadFor(int virtualRegisterIndex, RegisterID base)
266     {
267         return payloadFor(virtualRegisterIndex, base);
268     }
269
270     inline JSInterfaceJIT::Address JSInterfaceJIT::intTagFor(int virtualRegisterIndex, RegisterID base)
271     {
272         return tagFor(virtualRegisterIndex, base);
273     }
274
275     inline JSInterfaceJIT::Jump JSInterfaceJIT::emitLoadDouble(unsigned virtualRegisterIndex, FPRegisterID dst, RegisterID scratch)
276     {
277         ASSERT(static_cast<int>(virtualRegisterIndex) < FirstConstantRegisterIndex);
278         loadPtr(tagFor(virtualRegisterIndex), scratch);
279         Jump isDouble = branch32(Below, scratch, TrustedImm32(JSValue::LowestTag));
280         Jump notInt = branch32(NotEqual, scratch, TrustedImm32(JSValue::Int32Tag));
281         loadPtr(payloadFor(virtualRegisterIndex), scratch);
282         convertInt32ToDouble(scratch, dst);
283         Jump done = jump();
284         isDouble.link(this);
285         loadDouble(addressFor(virtualRegisterIndex), dst);
286         done.link(this);
287         return notInt;
288     }
289
290 #endif
291
292 #if USE(JSVALUE64)
293     ALWAYS_INLINE JSInterfaceJIT::Jump JSInterfaceJIT::emitJumpIfNotJSCell(RegisterID reg)
294     {
295         return branchTest64(NonZero, reg, tagMaskRegister);
296     }
297
298     ALWAYS_INLINE JSInterfaceJIT::Jump JSInterfaceJIT::emitJumpIfImmediateNumber(RegisterID reg)
299     {
300         return branchTest64(NonZero, reg, tagTypeNumberRegister);
301     }
302     ALWAYS_INLINE JSInterfaceJIT::Jump JSInterfaceJIT::emitJumpIfNotImmediateNumber(RegisterID reg)
303     {
304         return branchTest64(Zero, reg, tagTypeNumberRegister);
305     }
306     inline JSInterfaceJIT::Jump JSInterfaceJIT::emitLoadJSCell(unsigned virtualRegisterIndex, RegisterID dst)
307     {
308         load64(addressFor(virtualRegisterIndex), dst);
309         return branchTest64(NonZero, dst, tagMaskRegister);
310     }
311     
312     inline JSInterfaceJIT::Jump JSInterfaceJIT::emitLoadInt32(unsigned virtualRegisterIndex, RegisterID dst)
313     {
314         load64(addressFor(virtualRegisterIndex), dst);
315         Jump result = branch64(Below, dst, tagTypeNumberRegister);
316         zeroExtend32ToPtr(dst, dst);
317         return result;
318     }
319
320     inline JSInterfaceJIT::Jump JSInterfaceJIT::emitLoadDouble(unsigned virtualRegisterIndex, FPRegisterID dst, RegisterID scratch)
321     {
322         load64(addressFor(virtualRegisterIndex), scratch);
323         Jump notNumber = emitJumpIfNotImmediateNumber(scratch);
324         Jump notInt = branch64(Below, scratch, tagTypeNumberRegister);
325         convertInt32ToDouble(scratch, dst);
326         Jump done = jump();
327         notInt.link(this);
328         add64(tagTypeNumberRegister, scratch);
329         move64ToDouble(scratch, dst);
330         done.link(this);
331         return notNumber;
332     }
333
334     ALWAYS_INLINE void JSInterfaceJIT::emitFastArithImmToInt(RegisterID)
335     {
336     }
337     
338     // operand is int32_t, must have been zero-extended if register is 64-bit.
339     ALWAYS_INLINE void JSInterfaceJIT::emitFastArithIntToImmNoCheck(RegisterID src, RegisterID dest)
340     {
341         if (src != dest)
342             move(src, dest);
343         or64(tagTypeNumberRegister, dest);
344     }
345 #endif
346
347 #if USE(JSVALUE64)
348     inline JSInterfaceJIT::Address JSInterfaceJIT::payloadFor(int virtualRegisterIndex, RegisterID base)
349     {
350         ASSERT(virtualRegisterIndex < FirstConstantRegisterIndex);
351         return addressFor(virtualRegisterIndex, base);
352     }
353
354     inline JSInterfaceJIT::Address JSInterfaceJIT::intPayloadFor(int virtualRegisterIndex, RegisterID base)
355     {
356         ASSERT(virtualRegisterIndex < FirstConstantRegisterIndex);
357         return Address(base, (static_cast<unsigned>(virtualRegisterIndex) * sizeof(Register)) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload));
358     }
359     inline JSInterfaceJIT::Address JSInterfaceJIT::intTagFor(int virtualRegisterIndex, RegisterID base)
360     {
361         ASSERT(virtualRegisterIndex < FirstConstantRegisterIndex);
362         return Address(base, (static_cast<unsigned>(virtualRegisterIndex) * sizeof(Register)) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag));
363     }
364 #endif
365
366     ALWAYS_INLINE JSInterfaceJIT::Jump JSInterfaceJIT::emitJumpIfNotType(RegisterID baseReg, RegisterID scratchReg, JSType type)
367     {
368         loadPtr(Address(baseReg, JSCell::structureOffset()), scratchReg);
369         return branch8(NotEqual, Address(scratchReg, Structure::typeInfoTypeOffset()), TrustedImm32(type));
370     }
371
372     ALWAYS_INLINE void JSInterfaceJIT::emitGetFromCallFrameHeaderPtr(JSStack::CallFrameHeaderEntry entry, RegisterID to, RegisterID from)
373     {
374         loadPtr(Address(from, entry * sizeof(Register)), to);
375     }
376
377     ALWAYS_INLINE void JSInterfaceJIT::emitPutToCallFrameHeader(RegisterID from, JSStack::CallFrameHeaderEntry entry)
378     {
379 #if USE(JSVALUE32_64)
380         storePtr(from, payloadFor(entry, callFrameRegister));
381 #else
382         store64(from, addressFor(entry, callFrameRegister));
383 #endif
384     }
385
386     ALWAYS_INLINE void JSInterfaceJIT::emitPutImmediateToCallFrameHeader(void* value, JSStack::CallFrameHeaderEntry entry)
387     {
388         storePtr(TrustedImmPtr(value), Address(callFrameRegister, entry * sizeof(Register)));
389     }
390
391     ALWAYS_INLINE void JSInterfaceJIT::emitPutCellToCallFrameHeader(RegisterID from, JSStack::CallFrameHeaderEntry entry)
392     {
393 #if USE(JSVALUE32_64)
394         store32(TrustedImm32(JSValue::CellTag), tagFor(entry, callFrameRegister));
395         store32(from, payloadFor(entry, callFrameRegister));
396 #else
397         store64(from, addressFor(entry, callFrameRegister));
398 #endif
399     }
400
401     inline JSInterfaceJIT::Address JSInterfaceJIT::addressFor(int virtualRegisterIndex, RegisterID base)
402     {
403         ASSERT(virtualRegisterIndex < FirstConstantRegisterIndex);
404         return Address(base, (static_cast<unsigned>(virtualRegisterIndex) * sizeof(Register)));
405     }
406
407     ALWAYS_INLINE void JSInterfaceJIT::restoreArgumentReference()
408     {
409         move(stackPointerRegister, firstArgumentRegister);
410         poke(callFrameRegister, OBJECT_OFFSETOF(struct JITStackFrame, callFrame) / sizeof(void*));
411     }
412
413 } // namespace JSC
414
415 #endif // ENABLE(JIT)
416
417 #endif // JSInterfaceJIT_h