Objective-C API: Rename JSValue.h/APIJSValue.h to JSCJSValue.h/JSValue.h
[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 "JITCode.h"
31 #include "JITStubs.h"
32 #include "JSCJSValue.h"
33 #include "JSStack.h"
34 #include "JSString.h"
35 #include "MacroAssembler.h"
36 #include <wtf/AlwaysInline.h>
37 #include <wtf/Vector.h>
38
39 #if ENABLE(JIT)
40
41 namespace JSC {
42     class JSInterfaceJIT : public MacroAssembler {
43     public:
44         // NOTES:
45         //
46         // regT0 has two special meanings.  The return value from a stub
47         // call will always be in regT0, and by default (unless
48         // a register is specified) emitPutVirtualRegister() will store
49         // the value from regT0.
50         //
51         // regT3 is required to be callee-preserved.
52         //
53         // tempRegister2 is has no such dependencies.  It is important that
54         // on x86/x86-64 it is ecx for performance reasons, since the
55         // MacroAssembler will need to plant register swaps if it is not -
56         // however the code will still function correctly.
57 #if CPU(X86_64)
58         static const RegisterID returnValueRegister = X86Registers::eax;
59         static const RegisterID cachedResultRegister = X86Registers::eax;
60         static const RegisterID firstArgumentRegister = X86Registers::edi;
61         
62 #if ENABLE(VALUE_PROFILER)
63         static const RegisterID bucketCounterRegister = X86Registers::r10;
64 #endif
65         
66         static const RegisterID timeoutCheckRegister = X86Registers::r12;
67         static const RegisterID callFrameRegister = X86Registers::r13;
68         static const RegisterID tagTypeNumberRegister = X86Registers::r14;
69         static const RegisterID tagMaskRegister = X86Registers::r15;
70         
71         static const RegisterID regT0 = X86Registers::eax;
72         static const RegisterID regT1 = X86Registers::edx;
73         static const RegisterID regT2 = X86Registers::ecx;
74         static const RegisterID regT3 = X86Registers::ebx;
75         
76         static const FPRegisterID fpRegT0 = X86Registers::xmm0;
77         static const FPRegisterID fpRegT1 = X86Registers::xmm1;
78         static const FPRegisterID fpRegT2 = X86Registers::xmm2;
79         static const FPRegisterID fpRegT3 = X86Registers::xmm3;
80
81         static const RegisterID nonArgGPR1 = X86Registers::eax; // regT0
82 #elif CPU(X86)
83         static const RegisterID returnValueRegister = X86Registers::eax;
84         static const RegisterID cachedResultRegister = X86Registers::eax;
85         // On x86 we always use fastcall conventions = but on
86         // OS X if might make more sense to just use regparm.
87         static const RegisterID firstArgumentRegister = X86Registers::ecx;
88         
89         static const RegisterID bucketCounterRegister = X86Registers::esi;
90         static const RegisterID callFrameRegister = X86Registers::edi;
91         
92         static const RegisterID regT0 = X86Registers::eax;
93         static const RegisterID regT1 = X86Registers::edx;
94         static const RegisterID regT2 = X86Registers::ecx;
95         static const RegisterID regT3 = X86Registers::ebx;
96         
97         static const FPRegisterID fpRegT0 = X86Registers::xmm0;
98         static const FPRegisterID fpRegT1 = X86Registers::xmm1;
99         static const FPRegisterID fpRegT2 = X86Registers::xmm2;
100         static const FPRegisterID fpRegT3 = X86Registers::xmm3;
101 #elif CPU(ARM)
102         static const RegisterID returnValueRegister = ARMRegisters::r0;
103         static const RegisterID cachedResultRegister = ARMRegisters::r0;
104         static const RegisterID firstArgumentRegister = ARMRegisters::r0;
105
106 #if ENABLE(VALUE_PROFILER)
107         static const RegisterID bucketCounterRegister = ARMRegisters::r7;
108 #endif
109
110         static const RegisterID regT0 = ARMRegisters::r0;
111         static const RegisterID regT1 = ARMRegisters::r1;
112         static const RegisterID regT2 = ARMRegisters::r2;
113         static const RegisterID regT3 = ARMRegisters::r4;
114
115         // Update ctiTrampoline in JITStubs.cpp if these values are changed!
116         static const RegisterID callFrameRegister = ARMRegisters::r5;
117         static const RegisterID timeoutCheckRegister = ARMRegisters::r6;
118
119         static const FPRegisterID fpRegT0 = ARMRegisters::d0;
120         static const FPRegisterID fpRegT1 = ARMRegisters::d1;
121         static const FPRegisterID fpRegT2 = ARMRegisters::d2;
122         static const FPRegisterID fpRegT3 = ARMRegisters::d3;
123 #elif CPU(MIPS)
124         static const RegisterID returnValueRegister = MIPSRegisters::v0;
125         static const RegisterID cachedResultRegister = MIPSRegisters::v0;
126         static const RegisterID firstArgumentRegister = MIPSRegisters::a0;
127         
128         // regT0 must be v0 for returning a 32-bit value.
129         static const RegisterID regT0 = MIPSRegisters::v0;
130         
131         // regT1 must be v1 for returning a pair of 32-bit value.
132         static const RegisterID regT1 = MIPSRegisters::v1;
133         
134         static const RegisterID regT2 = MIPSRegisters::t4;
135         
136         // regT3 must be saved in the callee, so use an S register.
137         static const RegisterID regT3 = MIPSRegisters::s2;
138         
139         static const RegisterID callFrameRegister = MIPSRegisters::s0;
140         static const RegisterID timeoutCheckRegister = MIPSRegisters::s1;
141         
142         static const FPRegisterID fpRegT0 = MIPSRegisters::f4;
143         static const FPRegisterID fpRegT1 = MIPSRegisters::f6;
144         static const FPRegisterID fpRegT2 = MIPSRegisters::f8;
145         static const FPRegisterID fpRegT3 = MIPSRegisters::f10;
146 #elif CPU(SH4)
147         static const RegisterID timeoutCheckRegister = SH4Registers::r8;
148         static const RegisterID callFrameRegister = SH4Registers::fp;
149
150         static const RegisterID regT0 = SH4Registers::r0;
151         static const RegisterID regT1 = SH4Registers::r1;
152         static const RegisterID regT2 = SH4Registers::r2;
153         static const RegisterID regT3 = SH4Registers::r10;
154         static const RegisterID regT4 = SH4Registers::r4;
155         static const RegisterID regT5 = SH4Registers::r5;
156         static const RegisterID regT6 = SH4Registers::r6;
157         static const RegisterID regT7 = SH4Registers::r7;
158         static const RegisterID firstArgumentRegister =regT4;
159
160         static const RegisterID returnValueRegister = SH4Registers::r0;
161         static const RegisterID cachedResultRegister = SH4Registers::r0;
162
163         static const FPRegisterID fpRegT0  = SH4Registers::fr0;
164         static const FPRegisterID fpRegT1  = SH4Registers::fr2;
165         static const FPRegisterID fpRegT2  = SH4Registers::fr4;
166         static const FPRegisterID fpRegT3  = SH4Registers::fr6;
167         static const FPRegisterID fpRegT4  = SH4Registers::fr8;
168         static const FPRegisterID fpRegT5  = SH4Registers::fr10;
169         static const FPRegisterID fpRegT6  = SH4Registers::fr12;
170         static const FPRegisterID fpRegT7  = SH4Registers::fr14;
171 #else
172 #error "JIT not supported on this platform."
173 #endif
174
175 #if USE(JSVALUE32_64)
176         // Can't just propogate JSValue::Int32Tag as visual studio doesn't like it
177         static const unsigned Int32Tag = 0xffffffff;
178         COMPILE_ASSERT(Int32Tag == JSValue::Int32Tag, Int32Tag_out_of_sync);
179 #else
180         static const unsigned Int32Tag = TagTypeNumber >> 32;
181 #endif
182         inline Jump emitLoadJSCell(unsigned virtualRegisterIndex, RegisterID payload);
183         inline Jump emitLoadInt32(unsigned virtualRegisterIndex, RegisterID dst);
184         inline Jump emitLoadDouble(unsigned virtualRegisterIndex, FPRegisterID dst, RegisterID scratch);
185
186 #if USE(JSVALUE32_64)
187         inline Jump emitJumpIfNotJSCell(unsigned virtualRegisterIndex);
188         inline Address tagFor(int index, RegisterID base = callFrameRegister);
189 #endif
190
191 #if USE(JSVALUE64)
192         Jump emitJumpIfNotJSCell(RegisterID);
193         Jump emitJumpIfImmediateNumber(RegisterID reg);
194         Jump emitJumpIfNotImmediateNumber(RegisterID reg);
195         void emitFastArithImmToInt(RegisterID reg);
196         void emitFastArithIntToImmNoCheck(RegisterID src, RegisterID dest);
197 #endif
198
199         Jump emitJumpIfNotType(RegisterID baseReg, RegisterID scratchReg, JSType);
200
201         void emitGetFromCallFrameHeaderPtr(JSStack::CallFrameHeaderEntry, RegisterID to, RegisterID from = callFrameRegister);
202         void emitPutToCallFrameHeader(RegisterID from, JSStack::CallFrameHeaderEntry);
203         void emitPutImmediateToCallFrameHeader(void* value, JSStack::CallFrameHeaderEntry);
204         void emitPutCellToCallFrameHeader(RegisterID from, JSStack::CallFrameHeaderEntry);
205
206         void preserveReturnAddressAfterCall(RegisterID);
207         void restoreReturnAddressBeforeReturn(RegisterID);
208         void restoreReturnAddressBeforeReturn(Address);
209         void restoreArgumentReference();
210
211         inline Address payloadFor(int index, RegisterID base = callFrameRegister);
212         inline Address intPayloadFor(int index, RegisterID base = callFrameRegister);
213         inline Address intTagFor(int index, RegisterID base = callFrameRegister);
214         inline Address addressFor(int index, RegisterID base = callFrameRegister);
215     };
216
217     struct ThunkHelpers {
218         static unsigned stringImplFlagsOffset() { return StringImpl::flagsOffset(); }
219         static unsigned stringImpl8BitFlag() { return StringImpl::flagIs8Bit(); }
220         static unsigned stringImplDataOffset() { return StringImpl::dataOffset(); }
221         static unsigned jsStringLengthOffset() { return OBJECT_OFFSETOF(JSString, m_length); }
222         static unsigned jsStringValueOffset() { return OBJECT_OFFSETOF(JSString, m_value); }
223     };
224
225 #if USE(JSVALUE32_64)
226     inline JSInterfaceJIT::Jump JSInterfaceJIT::emitLoadJSCell(unsigned virtualRegisterIndex, RegisterID payload)
227     {
228         loadPtr(payloadFor(virtualRegisterIndex), payload);
229         return emitJumpIfNotJSCell(virtualRegisterIndex);
230     }
231
232     inline JSInterfaceJIT::Jump JSInterfaceJIT::emitJumpIfNotJSCell(unsigned virtualRegisterIndex)
233     {
234         ASSERT(static_cast<int>(virtualRegisterIndex) < FirstConstantRegisterIndex);
235         return branch32(NotEqual, tagFor(virtualRegisterIndex), TrustedImm32(JSValue::CellTag));
236     }
237     
238     inline JSInterfaceJIT::Jump JSInterfaceJIT::emitLoadInt32(unsigned virtualRegisterIndex, RegisterID dst)
239     {
240         ASSERT(static_cast<int>(virtualRegisterIndex) < FirstConstantRegisterIndex);
241         loadPtr(payloadFor(virtualRegisterIndex), dst);
242         return branch32(NotEqual, tagFor(static_cast<int>(virtualRegisterIndex)), TrustedImm32(JSValue::Int32Tag));
243     }
244     
245     inline JSInterfaceJIT::Address JSInterfaceJIT::tagFor(int virtualRegisterIndex, RegisterID base)
246     {
247         ASSERT(virtualRegisterIndex < FirstConstantRegisterIndex);
248         return Address(base, (static_cast<unsigned>(virtualRegisterIndex) * sizeof(Register)) + OBJECT_OFFSETOF(JSValue, u.asBits.tag));
249     }
250     
251     inline JSInterfaceJIT::Address JSInterfaceJIT::payloadFor(int virtualRegisterIndex, RegisterID base)
252     {
253         ASSERT(virtualRegisterIndex < FirstConstantRegisterIndex);
254         return Address(base, (static_cast<unsigned>(virtualRegisterIndex) * sizeof(Register)) + OBJECT_OFFSETOF(JSValue, u.asBits.payload));
255     }
256
257     inline JSInterfaceJIT::Address JSInterfaceJIT::intPayloadFor(int virtualRegisterIndex, RegisterID base)
258     {
259         return payloadFor(virtualRegisterIndex, base);
260     }
261
262     inline JSInterfaceJIT::Address JSInterfaceJIT::intTagFor(int virtualRegisterIndex, RegisterID base)
263     {
264         return tagFor(virtualRegisterIndex, base);
265     }
266
267     inline JSInterfaceJIT::Jump JSInterfaceJIT::emitLoadDouble(unsigned virtualRegisterIndex, FPRegisterID dst, RegisterID scratch)
268     {
269         ASSERT(static_cast<int>(virtualRegisterIndex) < FirstConstantRegisterIndex);
270         loadPtr(tagFor(virtualRegisterIndex), scratch);
271         Jump isDouble = branch32(Below, scratch, TrustedImm32(JSValue::LowestTag));
272         Jump notInt = branch32(NotEqual, scratch, TrustedImm32(JSValue::Int32Tag));
273         loadPtr(payloadFor(virtualRegisterIndex), scratch);
274         convertInt32ToDouble(scratch, dst);
275         Jump done = jump();
276         isDouble.link(this);
277         loadDouble(addressFor(virtualRegisterIndex), dst);
278         done.link(this);
279         return notInt;
280     }
281
282 #endif
283
284 #if USE(JSVALUE64)
285     ALWAYS_INLINE JSInterfaceJIT::Jump JSInterfaceJIT::emitJumpIfNotJSCell(RegisterID reg)
286     {
287         return branchTest64(NonZero, reg, tagMaskRegister);
288     }
289
290     ALWAYS_INLINE JSInterfaceJIT::Jump JSInterfaceJIT::emitJumpIfImmediateNumber(RegisterID reg)
291     {
292         return branchTest64(NonZero, reg, tagTypeNumberRegister);
293     }
294     ALWAYS_INLINE JSInterfaceJIT::Jump JSInterfaceJIT::emitJumpIfNotImmediateNumber(RegisterID reg)
295     {
296         return branchTest64(Zero, reg, tagTypeNumberRegister);
297     }
298     inline JSInterfaceJIT::Jump JSInterfaceJIT::emitLoadJSCell(unsigned virtualRegisterIndex, RegisterID dst)
299     {
300         load64(addressFor(virtualRegisterIndex), dst);
301         return branchTest64(NonZero, dst, tagMaskRegister);
302     }
303     
304     inline JSInterfaceJIT::Jump JSInterfaceJIT::emitLoadInt32(unsigned virtualRegisterIndex, RegisterID dst)
305     {
306         load64(addressFor(virtualRegisterIndex), dst);
307         Jump result = branch64(Below, dst, tagTypeNumberRegister);
308         zeroExtend32ToPtr(dst, dst);
309         return result;
310     }
311
312     inline JSInterfaceJIT::Jump JSInterfaceJIT::emitLoadDouble(unsigned virtualRegisterIndex, FPRegisterID dst, RegisterID scratch)
313     {
314         load64(addressFor(virtualRegisterIndex), scratch);
315         Jump notNumber = emitJumpIfNotImmediateNumber(scratch);
316         Jump notInt = branch64(Below, scratch, tagTypeNumberRegister);
317         convertInt32ToDouble(scratch, dst);
318         Jump done = jump();
319         notInt.link(this);
320         add64(tagTypeNumberRegister, scratch);
321         move64ToDouble(scratch, dst);
322         done.link(this);
323         return notNumber;
324     }
325
326     ALWAYS_INLINE void JSInterfaceJIT::emitFastArithImmToInt(RegisterID)
327     {
328     }
329     
330     // operand is int32_t, must have been zero-extended if register is 64-bit.
331     ALWAYS_INLINE void JSInterfaceJIT::emitFastArithIntToImmNoCheck(RegisterID src, RegisterID dest)
332     {
333         if (src != dest)
334             move(src, dest);
335         or64(tagTypeNumberRegister, dest);
336     }
337 #endif
338
339 #if USE(JSVALUE64)
340     inline JSInterfaceJIT::Address JSInterfaceJIT::payloadFor(int virtualRegisterIndex, RegisterID base)
341     {
342         ASSERT(virtualRegisterIndex < FirstConstantRegisterIndex);
343         return addressFor(virtualRegisterIndex, base);
344     }
345
346     inline JSInterfaceJIT::Address JSInterfaceJIT::intPayloadFor(int virtualRegisterIndex, RegisterID base)
347     {
348         ASSERT(virtualRegisterIndex < FirstConstantRegisterIndex);
349         return Address(base, (static_cast<unsigned>(virtualRegisterIndex) * sizeof(Register)) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload));
350     }
351     inline JSInterfaceJIT::Address JSInterfaceJIT::intTagFor(int virtualRegisterIndex, RegisterID base)
352     {
353         ASSERT(virtualRegisterIndex < FirstConstantRegisterIndex);
354         return Address(base, (static_cast<unsigned>(virtualRegisterIndex) * sizeof(Register)) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag));
355     }
356 #endif
357
358     ALWAYS_INLINE JSInterfaceJIT::Jump JSInterfaceJIT::emitJumpIfNotType(RegisterID baseReg, RegisterID scratchReg, JSType type)
359     {
360         loadPtr(Address(baseReg, JSCell::structureOffset()), scratchReg);
361         return branch8(NotEqual, Address(scratchReg, Structure::typeInfoTypeOffset()), TrustedImm32(type));
362     }
363
364     ALWAYS_INLINE void JSInterfaceJIT::emitGetFromCallFrameHeaderPtr(JSStack::CallFrameHeaderEntry entry, RegisterID to, RegisterID from)
365     {
366         loadPtr(Address(from, entry * sizeof(Register)), to);
367     }
368
369     ALWAYS_INLINE void JSInterfaceJIT::emitPutToCallFrameHeader(RegisterID from, JSStack::CallFrameHeaderEntry entry)
370     {
371 #if USE(JSVALUE32_64)
372         storePtr(from, payloadFor(entry, callFrameRegister));
373 #else
374         store64(from, addressFor(entry, callFrameRegister));
375 #endif
376     }
377
378     ALWAYS_INLINE void JSInterfaceJIT::emitPutImmediateToCallFrameHeader(void* value, JSStack::CallFrameHeaderEntry entry)
379     {
380         storePtr(TrustedImmPtr(value), Address(callFrameRegister, entry * sizeof(Register)));
381     }
382
383     ALWAYS_INLINE void JSInterfaceJIT::emitPutCellToCallFrameHeader(RegisterID from, JSStack::CallFrameHeaderEntry entry)
384     {
385 #if USE(JSVALUE32_64)
386         store32(TrustedImm32(JSValue::CellTag), tagFor(entry, callFrameRegister));
387         store32(from, payloadFor(entry, callFrameRegister));
388 #else
389         store64(from, addressFor(entry, callFrameRegister));
390 #endif
391     }
392
393     inline JSInterfaceJIT::Address JSInterfaceJIT::addressFor(int virtualRegisterIndex, RegisterID base)
394     {
395         ASSERT(virtualRegisterIndex < FirstConstantRegisterIndex);
396         return Address(base, (static_cast<unsigned>(virtualRegisterIndex) * sizeof(Register)));
397     }
398
399 #if CPU(ARM)
400
401     ALWAYS_INLINE void JSInterfaceJIT::preserveReturnAddressAfterCall(RegisterID reg)
402     {
403         move(linkRegister, reg);
404     }
405     
406     ALWAYS_INLINE void JSInterfaceJIT::restoreReturnAddressBeforeReturn(RegisterID reg)
407     {
408         move(reg, linkRegister);
409     }
410     
411     ALWAYS_INLINE void JSInterfaceJIT::restoreReturnAddressBeforeReturn(Address address)
412     {
413         loadPtr(address, linkRegister);
414     }
415 #elif CPU(SH4)
416
417     ALWAYS_INLINE void JSInterfaceJIT::preserveReturnAddressAfterCall(RegisterID reg)
418     {
419         m_assembler.stspr(reg);
420     }
421     
422     ALWAYS_INLINE void JSInterfaceJIT::restoreReturnAddressBeforeReturn(RegisterID reg)
423     {
424         m_assembler.ldspr(reg);
425     }
426     
427     ALWAYS_INLINE void JSInterfaceJIT::restoreReturnAddressBeforeReturn(Address address)
428     {
429         loadPtrLinkReg(address);
430     }
431     
432 #elif CPU(MIPS)
433
434     ALWAYS_INLINE void JSInterfaceJIT::preserveReturnAddressAfterCall(RegisterID reg)
435     {
436         move(returnAddressRegister, reg);
437     }
438     
439     ALWAYS_INLINE void JSInterfaceJIT::restoreReturnAddressBeforeReturn(RegisterID reg)
440     {
441         move(reg, returnAddressRegister);
442     }
443     
444     ALWAYS_INLINE void JSInterfaceJIT::restoreReturnAddressBeforeReturn(Address address)
445     {
446         loadPtr(address, returnAddressRegister);
447     }
448     
449 #else // CPU(X86) || CPU(X86_64)
450
451     ALWAYS_INLINE void JSInterfaceJIT::preserveReturnAddressAfterCall(RegisterID reg)
452     {
453         pop(reg);
454     }
455     
456     ALWAYS_INLINE void JSInterfaceJIT::restoreReturnAddressBeforeReturn(RegisterID reg)
457     {
458         push(reg);
459     }
460     
461     ALWAYS_INLINE void JSInterfaceJIT::restoreReturnAddressBeforeReturn(Address address)
462     {
463         push(address);
464     }
465     
466 #endif
467
468     ALWAYS_INLINE void JSInterfaceJIT::restoreArgumentReference()
469     {
470         move(stackPointerRegister, firstArgumentRegister);
471         poke(callFrameRegister, OBJECT_OFFSETOF(struct JITStackFrame, callFrame) / sizeof(void*));
472     }
473
474 } // namespace JSC
475
476 #endif // ENABLE(JIT)
477
478 #endif // JSInterfaceJIT_h