Clean up register naming
[WebKit-https.git] / Source / JavaScriptCore / jit / RegisterPreservationWrapperGenerator.cpp
1 /*
2  * Copyright (C) 2013, 2014 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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "RegisterPreservationWrapperGenerator.h"
28
29 #if ENABLE(JIT)
30
31 #include "AssemblyHelpers.h"
32 #include "LinkBuffer.h"
33 #include "JSCInlines.h"
34 #include "StackAlignment.h"
35
36 namespace JSC {
37
38 RegisterSet registersToPreserve()
39 {
40     RegisterSet calleeSaves = RegisterSet::calleeSaveRegisters();
41     
42     // No need to preserve FP since that always gets preserved anyway.
43     calleeSaves.clear(GPRInfo::callFrameRegister);
44     
45     return calleeSaves;
46 }
47
48 ptrdiff_t registerPreservationOffset()
49 {
50     unsigned numberOfCalleeSaves = registersToPreserve().numberOfSetRegisters();
51     
52     // Need to preserve the old return PC.
53     unsigned numberOfValuesToSave = numberOfCalleeSaves + 1;
54     
55     // Alignment. Preserve the same alignment invariants that the caller imposed.
56     unsigned numberOfNewStackSlots =
57         WTF::roundUpToMultipleOf(stackAlignmentRegisters(), numberOfValuesToSave);
58     
59     return sizeof(Register) * numberOfNewStackSlots;
60 }
61
62 MacroAssemblerCodeRef generateRegisterPreservationWrapper(VM& vm, ExecutableBase* executable, MacroAssemblerCodePtr target)
63 {
64 #if ENABLE(FTL_JIT)
65     // We shouldn't ever be generating wrappers for native functions.
66     RegisterSet toSave = registersToPreserve();
67     ptrdiff_t offset = registerPreservationOffset();
68
69     ASSERT(!toSave.get(GPRInfo::regT1));
70     ASSERT(!toSave.get(GPRInfo::regT2));
71     ASSERT(!toSave.get(GPRInfo::regT3));
72     
73     AssemblyHelpers jit(&vm, 0);
74     
75     jit.preserveReturnAddressAfterCall(GPRInfo::regT1);
76     jit.load32(
77         AssemblyHelpers::Address(
78             AssemblyHelpers::stackPointerRegister,
79             (JSStack::ArgumentCount - JSStack::CallerFrameAndPCSize) * sizeof(Register) + PayloadOffset),
80         GPRInfo::regT2);
81     
82     // Place the stack pointer where we want it to be.
83     jit.subPtr(AssemblyHelpers::TrustedImm32(offset), AssemblyHelpers::stackPointerRegister);
84     
85     // Compute the number of things we will be copying.
86     jit.add32(
87         AssemblyHelpers::TrustedImm32(
88             JSStack::CallFrameHeaderSize - JSStack::CallerFrameAndPCSize),
89         GPRInfo::regT2);
90
91     jit.move(AssemblyHelpers::stackPointerRegister, GPRInfo::regT3);
92     
93     AssemblyHelpers::Label loop = jit.label();
94     jit.sub32(AssemblyHelpers::TrustedImm32(1), GPRInfo::regT2);
95     jit.load64(AssemblyHelpers::Address(GPRInfo::regT3, offset), GPRInfo::regT0);
96     jit.store64(GPRInfo::regT0, GPRInfo::regT3);
97     jit.addPtr(AssemblyHelpers::TrustedImm32(sizeof(Register)), GPRInfo::regT3);
98     jit.branchTest32(AssemblyHelpers::NonZero, GPRInfo::regT2).linkTo(loop, &jit);
99
100     // At this point regT3 + offset points to where we save things.
101     ptrdiff_t currentOffset = 0;
102     jit.storePtr(GPRInfo::regT1, AssemblyHelpers::Address(GPRInfo::regT3, currentOffset));
103     
104     for (GPRReg gpr = AssemblyHelpers::firstRegister(); gpr <= AssemblyHelpers::lastRegister(); gpr = static_cast<GPRReg>(gpr + 1)) {
105         if (!toSave.get(gpr))
106             continue;
107         currentOffset += sizeof(Register);
108         jit.store64(gpr, AssemblyHelpers::Address(GPRInfo::regT3, currentOffset));
109     }
110     for (FPRReg fpr = AssemblyHelpers::firstFPRegister(); fpr <= AssemblyHelpers::lastFPRegister(); fpr = static_cast<FPRReg>(fpr + 1)) {
111         if (!toSave.get(fpr))
112             continue;
113         currentOffset += sizeof(Register);
114         jit.storeDouble(fpr, AssemblyHelpers::Address(GPRInfo::regT3, currentOffset));
115     }
116     
117     // Assume that there aren't any saved FP registers.
118     
119     // Restore the tag registers.
120     jit.move(AssemblyHelpers::TrustedImm64(TagTypeNumber), GPRInfo::tagTypeNumberRegister);
121     jit.add64(AssemblyHelpers::TrustedImm32(TagMask - TagTypeNumber), GPRInfo::tagTypeNumberRegister, GPRInfo::tagMaskRegister);
122     
123     jit.move(
124         AssemblyHelpers::TrustedImmPtr(
125             vm.getCTIStub(registerRestorationThunkGenerator).code().executableAddress()),
126         GPRInfo::nonArgGPR0);
127     jit.restoreReturnAddressBeforeReturn(GPRInfo::nonArgGPR0);
128     AssemblyHelpers::Jump jump = jit.jump();
129     
130     LinkBuffer linkBuffer(vm, jit, GLOBAL_THUNK_ID);
131     linkBuffer.link(jump, CodeLocationLabel(target));
132
133     if (Options::verboseFTLToJSThunk())
134         dataLog("Need a thunk for calls from FTL to non-FTL version of ", *executable, "\n");
135     
136     return FINALIZE_DFG_CODE(linkBuffer, ("Register preservation wrapper for %s/%s, %p", toCString(executable->hashFor(CodeForCall)).data(), toCString(executable->hashFor(CodeForConstruct)).data(), target.executableAddress()));
137 #else // ENABLE(FTL_JIT)
138     UNUSED_PARAM(vm);
139     UNUSED_PARAM(executable);
140     UNUSED_PARAM(target);
141     // We don't support non-FTL builds for two reasons:
142     // - It just so happens that currently only the FTL bottoms out in this code.
143     // - The code above uses 64-bit instructions. It doesn't necessarily have to; it would be
144     //   easy to change it so that it doesn't. But obviously making that change would be a
145     //   prerequisite to removing this #if.
146     UNREACHABLE_FOR_PLATFORM();
147     return MacroAssemblerCodeRef();
148 #endif // ENABLE(FTL_JIT)
149 }
150
151 static void generateRegisterRestoration(AssemblyHelpers& jit)
152 {
153 #if ENABLE(FTL_JIT)
154     RegisterSet toSave = registersToPreserve();
155     ptrdiff_t offset = registerPreservationOffset();
156     
157     ASSERT(!toSave.get(GPRInfo::regT1));
158     ASSERT(!toSave.get(GPRInfo::regT2));
159     ASSERT(!toSave.get(GPRInfo::regT3));
160
161     // We need to place the stack pointer back to where the caller thought they left it.
162     // But also, in order to recover the registers, we need to figure out how big the
163     // arguments area is.
164     
165     jit.load32(
166         AssemblyHelpers::Address(
167             AssemblyHelpers::stackPointerRegister,
168             (JSStack::ArgumentCount - JSStack::CallerFrameAndPCSize) * sizeof(Register) + PayloadOffset),
169         GPRInfo::regT3);
170     
171     jit.move(GPRInfo::regT3, GPRInfo::regT2);
172     jit.lshift32(AssemblyHelpers::TrustedImm32(3), GPRInfo::regT2);
173     
174     jit.addPtr(AssemblyHelpers::TrustedImm32(offset), AssemblyHelpers::stackPointerRegister);
175     jit.addPtr(AssemblyHelpers::stackPointerRegister, GPRInfo::regT2);
176     
177     // We saved things at:
178     //
179     //     adjSP + (JSStack::CallFrameHeaderSize - JSStack::CallerFrameAndPCSize + NumArgs) * 8
180     //
181     // Where:
182     //
183     //     adjSP = origSP - offset
184     //
185     // regT2 now points at:
186     //
187     //     origSP + NumArgs * 8
188     //   = adjSP + offset + NumArgs * 8
189     // 
190     // So if we subtract offset and then add JSStack::CallFrameHeaderSize and subtract
191     // JSStack::CallerFrameAndPCSize, we'll get the thing we want.
192     ptrdiff_t currentOffset = -offset + sizeof(Register) * (
193         JSStack::CallFrameHeaderSize - JSStack::CallerFrameAndPCSize);
194     jit.loadPtr(AssemblyHelpers::Address(GPRInfo::regT2, currentOffset), GPRInfo::regT1);
195     
196     for (GPRReg gpr = AssemblyHelpers::firstRegister(); gpr <= AssemblyHelpers::lastRegister(); gpr = static_cast<GPRReg>(gpr + 1)) {
197         if (!toSave.get(gpr))
198             continue;
199         currentOffset += sizeof(Register);
200         jit.load64(AssemblyHelpers::Address(GPRInfo::regT2, currentOffset), gpr);
201     }
202     for (FPRReg fpr = AssemblyHelpers::firstFPRegister(); fpr <= AssemblyHelpers::lastFPRegister(); fpr = static_cast<FPRReg>(fpr + 1)) {
203         if (!toSave.get(fpr))
204             continue;
205         currentOffset += sizeof(Register);
206         jit.loadDouble(AssemblyHelpers::Address(GPRInfo::regT2, currentOffset), fpr);
207     }
208     
209     // Thunks like this rely on the ArgumentCount being intact. Pay it forward.
210     jit.store32(
211         GPRInfo::regT3,
212         AssemblyHelpers::Address(
213             AssemblyHelpers::stackPointerRegister,
214             (JSStack::ArgumentCount - JSStack::CallerFrameAndPCSize) * sizeof(Register) + PayloadOffset));
215     
216     if (!ASSERT_DISABLED) {
217         AssemblyHelpers::Jump ok = jit.branchPtr(
218             AssemblyHelpers::Above, GPRInfo::regT1, AssemblyHelpers::TrustedImmPtr(static_cast<size_t>(0x1000)));
219         jit.abortWithReason(RPWUnreasonableJumpTarget);
220         ok.link(&jit);
221     }
222     
223     jit.jump(GPRInfo::regT1);
224 #else // ENABLE(FTL_JIT)
225     UNUSED_PARAM(jit);
226     UNREACHABLE_FOR_PLATFORM();
227 #endif // ENABLE(FTL_JIT)
228 }
229
230 MacroAssemblerCodeRef registerRestorationThunkGenerator(VM* vm)
231 {
232     AssemblyHelpers jit(vm, 0);
233     generateRegisterRestoration(jit);
234     LinkBuffer linkBuffer(*vm, jit, GLOBAL_THUNK_ID);
235     return FINALIZE_CODE(linkBuffer, ("Register restoration thunk"));
236 }
237
238 } // namespace JSC
239
240 #endif // ENABLE(JIT)
241