]>
Commit | Line | Data |
---|---|---|
81345200 A |
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 | AssemblyHelpers jit(&vm, 0); | |
70 | ||
71 | jit.preserveReturnAddressAfterCall(GPRInfo::regT1); | |
72 | jit.load32( | |
73 | AssemblyHelpers::Address( | |
74 | AssemblyHelpers::stackPointerRegister, | |
75 | (JSStack::ArgumentCount - JSStack::CallerFrameAndPCSize) * sizeof(Register) + PayloadOffset), | |
76 | GPRInfo::regT2); | |
77 | ||
78 | // Place the stack pointer where we want it to be. | |
79 | jit.subPtr(AssemblyHelpers::TrustedImm32(offset), AssemblyHelpers::stackPointerRegister); | |
80 | ||
81 | // Compute the number of things we will be copying. | |
82 | jit.add32( | |
83 | AssemblyHelpers::TrustedImm32( | |
84 | JSStack::CallFrameHeaderSize - JSStack::CallerFrameAndPCSize), | |
85 | GPRInfo::regT2); | |
86 | ||
87 | ASSERT(!toSave.get(GPRInfo::regT4)); | |
88 | jit.move(AssemblyHelpers::stackPointerRegister, GPRInfo::regT4); | |
89 | ||
90 | AssemblyHelpers::Label loop = jit.label(); | |
91 | jit.sub32(AssemblyHelpers::TrustedImm32(1), GPRInfo::regT2); | |
92 | jit.load64(AssemblyHelpers::Address(GPRInfo::regT4, offset), GPRInfo::regT0); | |
93 | jit.store64(GPRInfo::regT0, GPRInfo::regT4); | |
94 | jit.addPtr(AssemblyHelpers::TrustedImm32(sizeof(Register)), GPRInfo::regT4); | |
95 | jit.branchTest32(AssemblyHelpers::NonZero, GPRInfo::regT2).linkTo(loop, &jit); | |
96 | ||
97 | // At this point regT4 + offset points to where we save things. | |
98 | ptrdiff_t currentOffset = 0; | |
99 | jit.storePtr(GPRInfo::regT1, AssemblyHelpers::Address(GPRInfo::regT4, currentOffset)); | |
100 | ||
101 | for (GPRReg gpr = AssemblyHelpers::firstRegister(); gpr <= AssemblyHelpers::lastRegister(); gpr = static_cast<GPRReg>(gpr + 1)) { | |
102 | if (!toSave.get(gpr)) | |
103 | continue; | |
104 | currentOffset += sizeof(Register); | |
105 | jit.store64(gpr, AssemblyHelpers::Address(GPRInfo::regT4, currentOffset)); | |
106 | } | |
107 | for (FPRReg fpr = AssemblyHelpers::firstFPRegister(); fpr <= AssemblyHelpers::lastFPRegister(); fpr = static_cast<FPRReg>(fpr + 1)) { | |
108 | if (!toSave.get(fpr)) | |
109 | continue; | |
110 | currentOffset += sizeof(Register); | |
111 | jit.storeDouble(fpr, AssemblyHelpers::Address(GPRInfo::regT4, currentOffset)); | |
112 | } | |
113 | ||
114 | // Assume that there aren't any saved FP registers. | |
115 | ||
116 | // Restore the tag registers. | |
117 | jit.move(AssemblyHelpers::TrustedImm64(TagTypeNumber), GPRInfo::tagTypeNumberRegister); | |
118 | jit.add64(AssemblyHelpers::TrustedImm32(TagMask - TagTypeNumber), GPRInfo::tagTypeNumberRegister, GPRInfo::tagMaskRegister); | |
119 | ||
120 | jit.move( | |
121 | AssemblyHelpers::TrustedImmPtr( | |
122 | vm.getCTIStub(registerRestorationThunkGenerator).code().executableAddress()), | |
123 | GPRInfo::nonArgGPR0); | |
124 | jit.restoreReturnAddressBeforeReturn(GPRInfo::nonArgGPR0); | |
125 | AssemblyHelpers::Jump jump = jit.jump(); | |
126 | ||
127 | LinkBuffer linkBuffer(vm, jit, GLOBAL_THUNK_ID); | |
128 | linkBuffer.link(jump, CodeLocationLabel(target)); | |
129 | ||
130 | if (Options::verboseFTLToJSThunk()) | |
131 | dataLog("Need a thunk for calls from FTL to non-FTL version of ", *executable, "\n"); | |
132 | ||
133 | return FINALIZE_DFG_CODE(linkBuffer, ("Register preservation wrapper for %s/%s, %p", toCString(executable->hashFor(CodeForCall)).data(), toCString(executable->hashFor(CodeForConstruct)).data(), target.executableAddress())); | |
134 | #else // ENABLE(FTL_JIT) | |
135 | UNUSED_PARAM(vm); | |
136 | UNUSED_PARAM(executable); | |
137 | UNUSED_PARAM(target); | |
138 | // We don't support non-FTL builds for two reasons: | |
139 | // - It just so happens that currently only the FTL bottoms out in this code. | |
140 | // - The code above uses 64-bit instructions. It doesn't necessarily have to; it would be | |
141 | // easy to change it so that it doesn't. But obviously making that change would be a | |
142 | // prerequisite to removing this #if. | |
143 | UNREACHABLE_FOR_PLATFORM(); | |
144 | return MacroAssemblerCodeRef(); | |
145 | #endif // ENABLE(FTL_JIT) | |
146 | } | |
147 | ||
148 | static void generateRegisterRestoration(AssemblyHelpers& jit) | |
149 | { | |
150 | #if ENABLE(FTL_JIT) | |
151 | RegisterSet toSave = registersToPreserve(); | |
152 | ptrdiff_t offset = registerPreservationOffset(); | |
153 | ||
154 | ASSERT(!toSave.get(GPRInfo::regT4)); | |
155 | ||
156 | // We need to place the stack pointer back to where the caller thought they left it. | |
157 | // But also, in order to recover the registers, we need to figure out how big the | |
158 | // arguments area is. | |
159 | ||
160 | jit.load32( | |
161 | AssemblyHelpers::Address( | |
162 | AssemblyHelpers::stackPointerRegister, | |
163 | (JSStack::ArgumentCount - JSStack::CallerFrameAndPCSize) * sizeof(Register) + PayloadOffset), | |
164 | GPRInfo::regT4); | |
165 | ||
166 | jit.move(GPRInfo::regT4, GPRInfo::regT2); | |
167 | jit.lshift32(AssemblyHelpers::TrustedImm32(3), GPRInfo::regT2); | |
168 | ||
169 | jit.addPtr(AssemblyHelpers::TrustedImm32(offset), AssemblyHelpers::stackPointerRegister); | |
170 | jit.addPtr(AssemblyHelpers::stackPointerRegister, GPRInfo::regT2); | |
171 | ||
172 | // We saved things at: | |
173 | // | |
174 | // adjSP + (JSStack::CallFrameHeaderSize - JSStack::CallerFrameAndPCSize + NumArgs) * 8 | |
175 | // | |
176 | // Where: | |
177 | // | |
178 | // adjSP = origSP - offset | |
179 | // | |
180 | // regT2 now points at: | |
181 | // | |
182 | // origSP + NumArgs * 8 | |
183 | // = adjSP + offset + NumArgs * 8 | |
184 | // | |
185 | // So if we subtract offset and then add JSStack::CallFrameHeaderSize and subtract | |
186 | // JSStack::CallerFrameAndPCSize, we'll get the thing we want. | |
187 | ptrdiff_t currentOffset = -offset + sizeof(Register) * ( | |
188 | JSStack::CallFrameHeaderSize - JSStack::CallerFrameAndPCSize); | |
189 | jit.loadPtr(AssemblyHelpers::Address(GPRInfo::regT2, currentOffset), GPRInfo::regT1); | |
190 | ||
191 | for (GPRReg gpr = AssemblyHelpers::firstRegister(); gpr <= AssemblyHelpers::lastRegister(); gpr = static_cast<GPRReg>(gpr + 1)) { | |
192 | if (!toSave.get(gpr)) | |
193 | continue; | |
194 | currentOffset += sizeof(Register); | |
195 | jit.load64(AssemblyHelpers::Address(GPRInfo::regT2, currentOffset), gpr); | |
196 | } | |
197 | for (FPRReg fpr = AssemblyHelpers::firstFPRegister(); fpr <= AssemblyHelpers::lastFPRegister(); fpr = static_cast<FPRReg>(fpr + 1)) { | |
198 | if (!toSave.get(fpr)) | |
199 | continue; | |
200 | currentOffset += sizeof(Register); | |
201 | jit.loadDouble(AssemblyHelpers::Address(GPRInfo::regT2, currentOffset), fpr); | |
202 | } | |
203 | ||
204 | // Thunks like this rely on the ArgumentCount being intact. Pay it forward. | |
205 | jit.store32( | |
206 | GPRInfo::regT4, | |
207 | AssemblyHelpers::Address( | |
208 | AssemblyHelpers::stackPointerRegister, | |
209 | (JSStack::ArgumentCount - JSStack::CallerFrameAndPCSize) * sizeof(Register) + PayloadOffset)); | |
210 | ||
211 | if (!ASSERT_DISABLED) { | |
212 | AssemblyHelpers::Jump ok = jit.branchPtr( | |
213 | AssemblyHelpers::Above, GPRInfo::regT1, AssemblyHelpers::TrustedImmPtr(static_cast<size_t>(0x1000))); | |
214 | jit.abortWithReason(RPWUnreasonableJumpTarget); | |
215 | ok.link(&jit); | |
216 | } | |
217 | ||
218 | jit.jump(GPRInfo::regT1); | |
219 | #else // ENABLE(FTL_JIT) | |
220 | UNUSED_PARAM(jit); | |
221 | UNREACHABLE_FOR_PLATFORM(); | |
222 | #endif // ENABLE(FTL_JIT) | |
223 | } | |
224 | ||
225 | MacroAssemblerCodeRef registerRestorationThunkGenerator(VM* vm) | |
226 | { | |
227 | AssemblyHelpers jit(vm, 0); | |
228 | generateRegisterRestoration(jit); | |
229 | LinkBuffer linkBuffer(*vm, jit, GLOBAL_THUNK_ID); | |
230 | return FINALIZE_CODE(linkBuffer, ("Register restoration thunk")); | |
231 | } | |
232 | ||
233 | } // namespace JSC | |
234 | ||
235 | #endif // ENABLE(JIT) | |
236 |