1 | /* |
2 | * Copyright (C) 2010-2019 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 | #include "config.h" |
27 | #include "ThunkGenerators.h" |
28 | |
29 | #include "CodeBlock.h" |
30 | #include "DFGSpeculativeJIT.h" |
31 | #include "JITExceptions.h" |
32 | #include "JITOperations.h" |
33 | #include "JSArray.h" |
34 | #include "JSBoundFunction.h" |
35 | #include "JSCInlines.h" |
36 | #include "MathCommon.h" |
37 | #include "MaxFrameExtentForSlowPathCall.h" |
38 | #include "SpecializedThunkJIT.h" |
39 | #include <wtf/InlineASM.h> |
40 | #include <wtf/StringPrintStream.h> |
41 | #include <wtf/text/StringImpl.h> |
42 | |
43 | #if ENABLE(JIT) |
44 | |
45 | namespace JSC { |
46 | |
47 | template<typename TagType> |
48 | inline void emitPointerValidation(CCallHelpers& jit, GPRReg pointerGPR, TagType tag) |
49 | { |
50 | if (ASSERT_DISABLED) |
51 | return; |
52 | CCallHelpers::Jump isNonZero = jit.branchTestPtr(CCallHelpers::NonZero, pointerGPR); |
53 | jit.abortWithReason(TGInvalidPointer); |
54 | isNonZero.link(&jit); |
55 | jit.pushToSave(pointerGPR); |
56 | jit.untagPtr(tag, pointerGPR); |
57 | jit.load8(pointerGPR, pointerGPR); |
58 | jit.popToRestore(pointerGPR); |
59 | } |
60 | |
61 | // We will jump here if the JIT code tries to make a call, but the |
62 | // linking helper (C++ code) decides to throw an exception instead. |
63 | MacroAssemblerCodeRef<JITThunkPtrTag> throwExceptionFromCallSlowPathGenerator(VM* vm) |
64 | { |
65 | CCallHelpers jit; |
66 | |
67 | // The call pushed a return address, so we need to pop it back off to re-align the stack, |
68 | // even though we won't use it. |
69 | jit.preserveReturnAddressAfterCall(GPRInfo::nonPreservedNonReturnGPR); |
70 | |
71 | jit.copyCalleeSavesToEntryFrameCalleeSavesBuffer(vm->topEntryFrame); |
72 | |
73 | jit.setupArguments<decltype(lookupExceptionHandler)>(CCallHelpers::TrustedImmPtr(vm), GPRInfo::callFrameRegister); |
74 | jit.move(CCallHelpers::TrustedImmPtr(tagCFunctionPtr<OperationPtrTag>(lookupExceptionHandler)), GPRInfo::nonArgGPR0); |
75 | emitPointerValidation(jit, GPRInfo::nonArgGPR0, OperationPtrTag); |
76 | jit.call(GPRInfo::nonArgGPR0, OperationPtrTag); |
77 | jit.jumpToExceptionHandler(*vm); |
78 | |
79 | LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID); |
80 | return FINALIZE_CODE(patchBuffer, JITThunkPtrTag, "Throw exception from call slow path thunk" ); |
81 | } |
82 | |
83 | static void slowPathFor(CCallHelpers& jit, VM* vm, Sprt_JITOperation_ECli slowPathFunction) |
84 | { |
85 | jit.sanitizeStackInline(*vm, GPRInfo::nonArgGPR0); |
86 | jit.emitFunctionPrologue(); |
87 | jit.storePtr(GPRInfo::callFrameRegister, &vm->topCallFrame); |
88 | #if OS(WINDOWS) && CPU(X86_64) |
89 | // Windows X86_64 needs some space pointed to by arg0 for return types larger than 64 bits. |
90 | // Other argument values are shift by 1. Use space on the stack for our two return values. |
91 | // Moving the stack down maxFrameExtentForSlowPathCall bytes gives us room for our 3 arguments |
92 | // and space for the 16 byte return area. |
93 | jit.addPtr(CCallHelpers::TrustedImm32(-static_cast<int32_t>(maxFrameExtentForSlowPathCall)), CCallHelpers::stackPointerRegister); |
94 | jit.move(GPRInfo::regT2, GPRInfo::argumentGPR2); |
95 | jit.addPtr(CCallHelpers::TrustedImm32(32), CCallHelpers::stackPointerRegister, GPRInfo::argumentGPR0); |
96 | jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR1); |
97 | jit.move(CCallHelpers::TrustedImmPtr(tagCFunctionPtr<OperationPtrTag>(slowPathFunction)), GPRInfo::nonArgGPR0); |
98 | emitPointerValidation(jit, GPRInfo::nonArgGPR0, OperationPtrTag); |
99 | jit.call(GPRInfo::nonArgGPR0, OperationPtrTag); |
100 | jit.loadPtr(CCallHelpers::Address(GPRInfo::returnValueGPR, 8), GPRInfo::returnValueGPR2); |
101 | jit.loadPtr(CCallHelpers::Address(GPRInfo::returnValueGPR), GPRInfo::returnValueGPR); |
102 | jit.addPtr(CCallHelpers::TrustedImm32(maxFrameExtentForSlowPathCall), CCallHelpers::stackPointerRegister); |
103 | #else |
104 | if (maxFrameExtentForSlowPathCall) |
105 | jit.addPtr(CCallHelpers::TrustedImm32(-maxFrameExtentForSlowPathCall), CCallHelpers::stackPointerRegister); |
106 | jit.setupArguments<decltype(slowPathFunction)>(GPRInfo::regT2); |
107 | jit.move(CCallHelpers::TrustedImmPtr(tagCFunctionPtr<OperationPtrTag>(slowPathFunction)), GPRInfo::nonArgGPR0); |
108 | emitPointerValidation(jit, GPRInfo::nonArgGPR0, OperationPtrTag); |
109 | jit.call(GPRInfo::nonArgGPR0, OperationPtrTag); |
110 | if (maxFrameExtentForSlowPathCall) |
111 | jit.addPtr(CCallHelpers::TrustedImm32(maxFrameExtentForSlowPathCall), CCallHelpers::stackPointerRegister); |
112 | #endif |
113 | |
114 | // This slow call will return the address of one of the following: |
115 | // 1) Exception throwing thunk. |
116 | // 2) Host call return value returner thingy. |
117 | // 3) The function to call. |
118 | // The second return value GPR will hold a non-zero value for tail calls. |
119 | |
120 | emitPointerValidation(jit, GPRInfo::returnValueGPR, JSEntryPtrTag); |
121 | jit.emitFunctionEpilogue(); |
122 | jit.untagReturnAddress(); |
123 | |
124 | RELEASE_ASSERT(reinterpret_cast<void*>(KeepTheFrame) == reinterpret_cast<void*>(0)); |
125 | CCallHelpers::Jump doNotTrash = jit.branchTestPtr(CCallHelpers::Zero, GPRInfo::returnValueGPR2); |
126 | |
127 | jit.preserveReturnAddressAfterCall(GPRInfo::nonPreservedNonReturnGPR); |
128 | jit.prepareForTailCallSlow(GPRInfo::returnValueGPR); |
129 | |
130 | doNotTrash.link(&jit); |
131 | jit.jump(GPRInfo::returnValueGPR, JSEntryPtrTag); |
132 | } |
133 | |
134 | MacroAssemblerCodeRef<JITThunkPtrTag> linkCallThunkGenerator(VM* vm) |
135 | { |
136 | // The return address is on the stack or in the link register. We will hence |
137 | // save the return address to the call frame while we make a C++ function call |
138 | // to perform linking and lazy compilation if necessary. We expect the callee |
139 | // to be in regT0/regT1 (payload/tag), the CallFrame to have already |
140 | // been adjusted, and all other registers to be available for use. |
141 | CCallHelpers jit; |
142 | |
143 | slowPathFor(jit, vm, operationLinkCall); |
144 | |
145 | LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID); |
146 | return FINALIZE_CODE(patchBuffer, JITThunkPtrTag, "Link call slow path thunk" ); |
147 | } |
148 | |
149 | // For closure optimizations, we only include calls, since if you're using closures for |
150 | // object construction then you're going to lose big time anyway. |
151 | MacroAssemblerCodeRef<JITThunkPtrTag> linkPolymorphicCallThunkGenerator(VM* vm) |
152 | { |
153 | CCallHelpers jit; |
154 | |
155 | slowPathFor(jit, vm, operationLinkPolymorphicCall); |
156 | |
157 | LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID); |
158 | return FINALIZE_CODE(patchBuffer, JITThunkPtrTag, "Link polymorphic call slow path thunk" ); |
159 | } |
160 | |
161 | // FIXME: We should distinguish between a megamorphic virtual call vs. a slow |
162 | // path virtual call so that we can enable fast tail calls for megamorphic |
163 | // virtual calls by using the shuffler. |
164 | // https://bugs.webkit.org/show_bug.cgi?id=148831 |
165 | MacroAssemblerCodeRef<JITStubRoutinePtrTag> virtualThunkFor(VM* vm, CallLinkInfo& callLinkInfo) |
166 | { |
167 | // The callee is in regT0 (for JSVALUE32_64, the tag is in regT1). |
168 | // The return address is on the stack, or in the link register. We will hence |
169 | // jump to the callee, or save the return address to the call frame while we |
170 | // make a C++ function call to the appropriate JIT operation. |
171 | |
172 | CCallHelpers jit; |
173 | |
174 | CCallHelpers::JumpList slowCase; |
175 | |
176 | // This is a slow path execution, and regT2 contains the CallLinkInfo. Count the |
177 | // slow path execution for the profiler. |
178 | jit.add32( |
179 | CCallHelpers::TrustedImm32(1), |
180 | CCallHelpers::Address(GPRInfo::regT2, CallLinkInfo::offsetOfSlowPathCount())); |
181 | |
182 | // FIXME: we should have a story for eliminating these checks. In many cases, |
183 | // the DFG knows that the value is definitely a cell, or definitely a function. |
184 | |
185 | #if USE(JSVALUE64) |
186 | GPRReg tagMaskRegister = GPRInfo::tagMaskRegister; |
187 | if (callLinkInfo.isTailCall()) { |
188 | // Tail calls could have clobbered the GPRInfo::tagMaskRegister because they |
189 | // restore callee saved registers before getthing here. So, let's materialize |
190 | // the TagMask in a temp register and use the temp instead. |
191 | tagMaskRegister = GPRInfo::regT4; |
192 | jit.move(CCallHelpers::TrustedImm64(TagMask), tagMaskRegister); |
193 | } |
194 | slowCase.append( |
195 | jit.branchTest64(CCallHelpers::NonZero, GPRInfo::regT0, tagMaskRegister)); |
196 | #else |
197 | slowCase.append(jit.branchIfNotCell(GPRInfo::regT1)); |
198 | #endif |
199 | auto notJSFunction = jit.branchIfNotType(GPRInfo::regT0, JSFunctionType); |
200 | |
201 | // Now we know we have a JSFunction. |
202 | |
203 | jit.loadPtr( |
204 | CCallHelpers::Address(GPRInfo::regT0, JSFunction::offsetOfExecutable()), |
205 | GPRInfo::regT4); |
206 | jit.loadPtr( |
207 | CCallHelpers::Address( |
208 | GPRInfo::regT4, ExecutableBase::offsetOfJITCodeWithArityCheckFor( |
209 | callLinkInfo.specializationKind())), |
210 | GPRInfo::regT4); |
211 | slowCase.append(jit.branchTestPtr(CCallHelpers::Zero, GPRInfo::regT4)); |
212 | |
213 | // Now we know that we have a CodeBlock, and we're committed to making a fast |
214 | // call. |
215 | |
216 | // Make a tail call. This will return back to JIT code. |
217 | JSInterfaceJIT::Label callCode(jit.label()); |
218 | emitPointerValidation(jit, GPRInfo::regT4, JSEntryPtrTag); |
219 | if (callLinkInfo.isTailCall()) { |
220 | jit.preserveReturnAddressAfterCall(GPRInfo::regT0); |
221 | jit.prepareForTailCallSlow(GPRInfo::regT4); |
222 | } |
223 | jit.jump(GPRInfo::regT4, JSEntryPtrTag); |
224 | |
225 | notJSFunction.link(&jit); |
226 | slowCase.append(jit.branchIfNotType(GPRInfo::regT0, InternalFunctionType)); |
227 | void* executableAddress = vm->getCTIInternalFunctionTrampolineFor(callLinkInfo.specializationKind()).executableAddress(); |
228 | jit.move(CCallHelpers::TrustedImmPtr(executableAddress), GPRInfo::regT4); |
229 | jit.jump().linkTo(callCode, &jit); |
230 | |
231 | slowCase.link(&jit); |
232 | |
233 | // Here we don't know anything, so revert to the full slow path. |
234 | slowPathFor(jit, vm, operationVirtualCall); |
235 | |
236 | LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID); |
237 | return FINALIZE_CODE( |
238 | patchBuffer, JITStubRoutinePtrTag, |
239 | "Virtual %s slow path thunk" , |
240 | callLinkInfo.callMode() == CallMode::Regular ? "call" : callLinkInfo.callMode() == CallMode::Tail ? "tail call" : "construct" ); |
241 | } |
242 | |
243 | enum ThunkEntryType { EnterViaCall, EnterViaJumpWithSavedTags, EnterViaJumpWithoutSavedTags }; |
244 | enum class ThunkFunctionType { JSFunction, InternalFunction }; |
245 | |
246 | static MacroAssemblerCodeRef<JITThunkPtrTag> nativeForGenerator(VM* vm, ThunkFunctionType thunkFunctionType, CodeSpecializationKind kind, ThunkEntryType entryType = EnterViaCall) |
247 | { |
248 | // FIXME: This should be able to log ShadowChicken prologue packets. |
249 | // https://bugs.webkit.org/show_bug.cgi?id=155689 |
250 | |
251 | int executableOffsetToFunction = NativeExecutable::offsetOfNativeFunctionFor(kind); |
252 | |
253 | JSInterfaceJIT jit(vm); |
254 | |
255 | switch (entryType) { |
256 | case EnterViaCall: |
257 | jit.emitFunctionPrologue(); |
258 | break; |
259 | case EnterViaJumpWithSavedTags: |
260 | #if USE(JSVALUE64) |
261 | // We're coming from a specialized thunk that has saved the prior tag registers' contents. |
262 | // Restore them now. |
263 | jit.popPair(JSInterfaceJIT::tagTypeNumberRegister, JSInterfaceJIT::tagMaskRegister); |
264 | #endif |
265 | break; |
266 | case EnterViaJumpWithoutSavedTags: |
267 | jit.move(JSInterfaceJIT::framePointerRegister, JSInterfaceJIT::stackPointerRegister); |
268 | break; |
269 | } |
270 | |
271 | jit.emitPutToCallFrameHeader(0, CallFrameSlot::codeBlock); |
272 | jit.storePtr(JSInterfaceJIT::callFrameRegister, &vm->topCallFrame); |
273 | |
274 | #if CPU(X86) |
275 | // Calling convention: f(ecx, edx, ...); |
276 | // Host function signature: f(ExecState*); |
277 | jit.move(JSInterfaceJIT::callFrameRegister, X86Registers::ecx); |
278 | |
279 | jit.subPtr(JSInterfaceJIT::TrustedImm32(8), JSInterfaceJIT::stackPointerRegister); // Align stack after prologue. |
280 | |
281 | // call the function |
282 | jit.emitGetFromCallFrameHeaderPtr(CallFrameSlot::callee, JSInterfaceJIT::regT1); |
283 | if (thunkFunctionType == ThunkFunctionType::JSFunction) { |
284 | jit.loadPtr(JSInterfaceJIT::Address(JSInterfaceJIT::regT1, JSFunction::offsetOfExecutable()), JSInterfaceJIT::regT1); |
285 | jit.call(JSInterfaceJIT::Address(JSInterfaceJIT::regT1, executableOffsetToFunction), JSEntryPtrTag); |
286 | } else |
287 | jit.call(JSInterfaceJIT::Address(JSInterfaceJIT::regT1, InternalFunction::offsetOfNativeFunctionFor(kind)), JSEntryPtrTag); |
288 | |
289 | jit.addPtr(JSInterfaceJIT::TrustedImm32(8), JSInterfaceJIT::stackPointerRegister); |
290 | |
291 | #elif CPU(X86_64) |
292 | #if !OS(WINDOWS) |
293 | // Calling convention: f(edi, esi, edx, ecx, ...); |
294 | // Host function signature: f(ExecState*); |
295 | jit.move(JSInterfaceJIT::callFrameRegister, X86Registers::edi); |
296 | |
297 | jit.emitGetFromCallFrameHeaderPtr(CallFrameSlot::callee, X86Registers::esi); |
298 | if (thunkFunctionType == ThunkFunctionType::JSFunction) { |
299 | jit.loadPtr(JSInterfaceJIT::Address(X86Registers::esi, JSFunction::offsetOfExecutable()), X86Registers::r9); |
300 | jit.loadPtr(JSInterfaceJIT::Address(X86Registers::r9, executableOffsetToFunction), X86Registers::r9); |
301 | } else |
302 | jit.loadPtr(JSInterfaceJIT::Address(X86Registers::esi, InternalFunction::offsetOfNativeFunctionFor(kind)), X86Registers::r9); |
303 | jit.call(X86Registers::r9, JSEntryPtrTag); |
304 | |
305 | #else |
306 | // Calling convention: f(ecx, edx, r8, r9, ...); |
307 | // Host function signature: f(ExecState*); |
308 | jit.move(JSInterfaceJIT::callFrameRegister, X86Registers::ecx); |
309 | |
310 | // Leave space for the callee parameter home addresses. |
311 | // At this point the stack is aligned to 16 bytes, but if this changes at some point, we need to emit code to align it. |
312 | jit.subPtr(JSInterfaceJIT::TrustedImm32(4 * sizeof(int64_t)), JSInterfaceJIT::stackPointerRegister); |
313 | |
314 | jit.emitGetFromCallFrameHeaderPtr(CallFrameSlot::callee, X86Registers::edx); |
315 | if (thunkFunctionType == ThunkFunctionType::JSFunction) { |
316 | jit.loadPtr(JSInterfaceJIT::Address(X86Registers::edx, JSFunction::offsetOfExecutable()), X86Registers::r9); |
317 | jit.call(JSInterfaceJIT::Address(X86Registers::r9, executableOffsetToFunction), JSEntryPtrTag); |
318 | } else |
319 | jit.call(JSInterfaceJIT::Address(X86Registers::edx, InternalFunction::offsetOfNativeFunctionFor(kind)), JSEntryPtrTag); |
320 | |
321 | jit.addPtr(JSInterfaceJIT::TrustedImm32(4 * sizeof(int64_t)), JSInterfaceJIT::stackPointerRegister); |
322 | #endif |
323 | |
324 | #elif CPU(ARM64) |
325 | COMPILE_ASSERT(ARM64Registers::x0 != JSInterfaceJIT::regT3, T3_not_trampled_by_arg_0); |
326 | COMPILE_ASSERT(ARM64Registers::x1 != JSInterfaceJIT::regT3, T3_not_trampled_by_arg_1); |
327 | COMPILE_ASSERT(ARM64Registers::x2 != JSInterfaceJIT::regT3, T3_not_trampled_by_arg_2); |
328 | |
329 | // Host function signature: f(ExecState*); |
330 | jit.move(JSInterfaceJIT::callFrameRegister, ARM64Registers::x0); |
331 | |
332 | jit.emitGetFromCallFrameHeaderPtr(CallFrameSlot::callee, ARM64Registers::x1); |
333 | if (thunkFunctionType == ThunkFunctionType::JSFunction) { |
334 | jit.loadPtr(JSInterfaceJIT::Address(ARM64Registers::x1, JSFunction::offsetOfExecutable()), ARM64Registers::x2); |
335 | jit.loadPtr(JSInterfaceJIT::Address(ARM64Registers::x2, executableOffsetToFunction), ARM64Registers::x2); |
336 | } else |
337 | jit.loadPtr(JSInterfaceJIT::Address(ARM64Registers::x1, InternalFunction::offsetOfNativeFunctionFor(kind)), ARM64Registers::x2); |
338 | jit.call(ARM64Registers::x2, JSEntryPtrTag); |
339 | |
340 | #elif CPU(ARM_THUMB2) || CPU(MIPS) |
341 | #if CPU(MIPS) |
342 | // Allocate stack space for (unused) 16 bytes (8-byte aligned) for 4 arguments. |
343 | jit.subPtr(JSInterfaceJIT::TrustedImm32(16), JSInterfaceJIT::stackPointerRegister); |
344 | #endif |
345 | |
346 | // Calling convention is f(argumentGPR0, argumentGPR1, ...). |
347 | // Host function signature is f(ExecState*). |
348 | jit.move(JSInterfaceJIT::callFrameRegister, JSInterfaceJIT::argumentGPR0); |
349 | |
350 | jit.emitGetFromCallFrameHeaderPtr(CallFrameSlot::callee, JSInterfaceJIT::argumentGPR1); |
351 | if (thunkFunctionType == ThunkFunctionType::JSFunction) { |
352 | jit.loadPtr(JSInterfaceJIT::Address(JSInterfaceJIT::argumentGPR1, JSFunction::offsetOfExecutable()), JSInterfaceJIT::regT2); |
353 | jit.call(JSInterfaceJIT::Address(JSInterfaceJIT::regT2, executableOffsetToFunction), JSEntryPtrTag); |
354 | } else |
355 | jit.call(JSInterfaceJIT::Address(JSInterfaceJIT::argumentGPR1, InternalFunction::offsetOfNativeFunctionFor(kind)), JSEntryPtrTag); |
356 | |
357 | #if CPU(MIPS) |
358 | // Restore stack space |
359 | jit.addPtr(JSInterfaceJIT::TrustedImm32(16), JSInterfaceJIT::stackPointerRegister); |
360 | #endif |
361 | #else |
362 | #error "JIT not supported on this platform." |
363 | UNUSED_PARAM(executableOffsetToFunction); |
364 | abortWithReason(TGNotSupported); |
365 | #endif |
366 | |
367 | // Check for an exception |
368 | #if USE(JSVALUE64) |
369 | jit.load64(vm->addressOfException(), JSInterfaceJIT::regT2); |
370 | JSInterfaceJIT::Jump exceptionHandler = jit.branchTest64(JSInterfaceJIT::NonZero, JSInterfaceJIT::regT2); |
371 | #else |
372 | JSInterfaceJIT::Jump exceptionHandler = jit.branch32( |
373 | JSInterfaceJIT::NotEqual, |
374 | JSInterfaceJIT::AbsoluteAddress(vm->addressOfException()), |
375 | JSInterfaceJIT::TrustedImm32(0)); |
376 | #endif |
377 | |
378 | jit.emitFunctionEpilogue(); |
379 | // Return. |
380 | jit.ret(); |
381 | |
382 | // Handle an exception |
383 | exceptionHandler.link(&jit); |
384 | |
385 | jit.copyCalleeSavesToEntryFrameCalleeSavesBuffer(vm->topEntryFrame); |
386 | jit.storePtr(JSInterfaceJIT::callFrameRegister, &vm->topCallFrame); |
387 | |
388 | #if CPU(X86) && USE(JSVALUE32_64) |
389 | jit.subPtr(JSInterfaceJIT::TrustedImm32(4), JSInterfaceJIT::stackPointerRegister); |
390 | jit.move(JSInterfaceJIT::callFrameRegister, JSInterfaceJIT::regT0); |
391 | jit.push(JSInterfaceJIT::regT0); |
392 | #else |
393 | #if OS(WINDOWS) |
394 | // Allocate space on stack for the 4 parameter registers. |
395 | jit.subPtr(JSInterfaceJIT::TrustedImm32(4 * sizeof(int64_t)), JSInterfaceJIT::stackPointerRegister); |
396 | #endif |
397 | jit.move(JSInterfaceJIT::callFrameRegister, JSInterfaceJIT::argumentGPR0); |
398 | #endif |
399 | jit.move(JSInterfaceJIT::TrustedImmPtr(tagCFunctionPtr<OperationPtrTag>(operationVMHandleException)), JSInterfaceJIT::regT3); |
400 | jit.call(JSInterfaceJIT::regT3, OperationPtrTag); |
401 | #if CPU(X86) && USE(JSVALUE32_64) |
402 | jit.addPtr(JSInterfaceJIT::TrustedImm32(8), JSInterfaceJIT::stackPointerRegister); |
403 | #elif OS(WINDOWS) |
404 | jit.addPtr(JSInterfaceJIT::TrustedImm32(4 * sizeof(int64_t)), JSInterfaceJIT::stackPointerRegister); |
405 | #endif |
406 | |
407 | jit.jumpToExceptionHandler(*vm); |
408 | |
409 | LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID); |
410 | return FINALIZE_CODE(patchBuffer, JITThunkPtrTag, "%s %s%s trampoline" , thunkFunctionType == ThunkFunctionType::JSFunction ? "native" : "internal" , entryType == EnterViaJumpWithSavedTags ? "Tail With Saved Tags " : entryType == EnterViaJumpWithoutSavedTags ? "Tail Without Saved Tags " : "" , toCString(kind).data()); |
411 | } |
412 | |
413 | MacroAssemblerCodeRef<JITThunkPtrTag> nativeCallGenerator(VM* vm) |
414 | { |
415 | return nativeForGenerator(vm, ThunkFunctionType::JSFunction, CodeForCall); |
416 | } |
417 | |
418 | MacroAssemblerCodeRef<JITThunkPtrTag> nativeTailCallGenerator(VM* vm) |
419 | { |
420 | return nativeForGenerator(vm, ThunkFunctionType::JSFunction, CodeForCall, EnterViaJumpWithSavedTags); |
421 | } |
422 | |
423 | MacroAssemblerCodeRef<JITThunkPtrTag> nativeTailCallWithoutSavedTagsGenerator(VM* vm) |
424 | { |
425 | return nativeForGenerator(vm, ThunkFunctionType::JSFunction, CodeForCall, EnterViaJumpWithoutSavedTags); |
426 | } |
427 | |
428 | MacroAssemblerCodeRef<JITThunkPtrTag> nativeConstructGenerator(VM* vm) |
429 | { |
430 | return nativeForGenerator(vm, ThunkFunctionType::JSFunction, CodeForConstruct); |
431 | } |
432 | |
433 | MacroAssemblerCodeRef<JITThunkPtrTag> internalFunctionCallGenerator(VM* vm) |
434 | { |
435 | return nativeForGenerator(vm, ThunkFunctionType::InternalFunction, CodeForCall); |
436 | } |
437 | |
438 | MacroAssemblerCodeRef<JITThunkPtrTag> internalFunctionConstructGenerator(VM* vm) |
439 | { |
440 | return nativeForGenerator(vm, ThunkFunctionType::InternalFunction, CodeForConstruct); |
441 | } |
442 | |
443 | MacroAssemblerCodeRef<JITThunkPtrTag> arityFixupGenerator(VM* vm) |
444 | { |
445 | JSInterfaceJIT jit(vm); |
446 | |
447 | // We enter with fixup count in argumentGPR0 |
448 | // We have the guarantee that a0, a1, a2, t3, t4 and t5 (or t0 for Windows) are all distinct :-) |
449 | #if USE(JSVALUE64) |
450 | #if OS(WINDOWS) |
451 | const GPRReg extraTemp = JSInterfaceJIT::regT0; |
452 | #else |
453 | const GPRReg = JSInterfaceJIT::regT5; |
454 | #endif |
455 | # if CPU(X86_64) |
456 | jit.pop(JSInterfaceJIT::regT4); |
457 | # endif |
458 | jit.tagReturnAddress(); |
459 | #if CPU(ARM64E) |
460 | jit.loadPtr(JSInterfaceJIT::Address(GPRInfo::callFrameRegister, CallFrame::returnPCOffset()), GPRInfo::regT3); |
461 | jit.addPtr(JSInterfaceJIT::TrustedImm32(sizeof(CallerFrameAndPC)), GPRInfo::callFrameRegister, extraTemp); |
462 | jit.untagPtr(extraTemp, GPRInfo::regT3); |
463 | PtrTag tempReturnPCTag = static_cast<PtrTag>(random()); |
464 | jit.move(JSInterfaceJIT::TrustedImmPtr(tempReturnPCTag), extraTemp); |
465 | jit.tagPtr(extraTemp, GPRInfo::regT3); |
466 | jit.storePtr(GPRInfo::regT3, JSInterfaceJIT::Address(GPRInfo::callFrameRegister, CallFrame::returnPCOffset())); |
467 | #endif |
468 | jit.move(JSInterfaceJIT::callFrameRegister, JSInterfaceJIT::regT3); |
469 | jit.load32(JSInterfaceJIT::addressFor(CallFrameSlot::argumentCount), JSInterfaceJIT::argumentGPR2); |
470 | jit.add32(JSInterfaceJIT::TrustedImm32(CallFrame::headerSizeInRegisters), JSInterfaceJIT::argumentGPR2); |
471 | |
472 | // Check to see if we have extra slots we can use |
473 | jit.move(JSInterfaceJIT::argumentGPR0, JSInterfaceJIT::argumentGPR1); |
474 | jit.and32(JSInterfaceJIT::TrustedImm32(stackAlignmentRegisters() - 1), JSInterfaceJIT::argumentGPR1); |
475 | JSInterfaceJIT::Jump = jit.branchTest32(MacroAssembler::Zero, JSInterfaceJIT::argumentGPR1); |
476 | jit.move(JSInterfaceJIT::TrustedImm64(ValueUndefined), extraTemp); |
477 | JSInterfaceJIT::Label (jit.label()); |
478 | jit.store64(extraTemp, MacroAssembler::BaseIndex(JSInterfaceJIT::callFrameRegister, JSInterfaceJIT::argumentGPR2, JSInterfaceJIT::TimesEight)); |
479 | jit.add32(JSInterfaceJIT::TrustedImm32(1), JSInterfaceJIT::argumentGPR2); |
480 | jit.branchSub32(JSInterfaceJIT::NonZero, JSInterfaceJIT::TrustedImm32(1), JSInterfaceJIT::argumentGPR1).linkTo(fillExtraSlots, &jit); |
481 | jit.and32(JSInterfaceJIT::TrustedImm32(-stackAlignmentRegisters()), JSInterfaceJIT::argumentGPR0); |
482 | JSInterfaceJIT::Jump done = jit.branchTest32(MacroAssembler::Zero, JSInterfaceJIT::argumentGPR0); |
483 | noExtraSlot.link(&jit); |
484 | |
485 | jit.neg64(JSInterfaceJIT::argumentGPR0); |
486 | |
487 | // Adjust call frame register and stack pointer to account for missing args. |
488 | // We need to change the stack pointer first before performing copy/fill loops. |
489 | // This stack space below the stack pointer is considered unused by OS. Therefore, |
490 | // OS may corrupt this space when constructing a signal stack. |
491 | jit.move(JSInterfaceJIT::argumentGPR0, extraTemp); |
492 | jit.lshift64(JSInterfaceJIT::TrustedImm32(3), extraTemp); |
493 | jit.addPtr(extraTemp, JSInterfaceJIT::callFrameRegister); |
494 | jit.untagReturnAddress(); |
495 | jit.addPtr(extraTemp, JSInterfaceJIT::stackPointerRegister); |
496 | jit.tagReturnAddress(); |
497 | |
498 | // Move current frame down argumentGPR0 number of slots |
499 | JSInterfaceJIT::Label copyLoop(jit.label()); |
500 | jit.load64(JSInterfaceJIT::regT3, extraTemp); |
501 | jit.store64(extraTemp, MacroAssembler::BaseIndex(JSInterfaceJIT::regT3, JSInterfaceJIT::argumentGPR0, JSInterfaceJIT::TimesEight)); |
502 | jit.addPtr(JSInterfaceJIT::TrustedImm32(8), JSInterfaceJIT::regT3); |
503 | jit.branchSub32(MacroAssembler::NonZero, JSInterfaceJIT::TrustedImm32(1), JSInterfaceJIT::argumentGPR2).linkTo(copyLoop, &jit); |
504 | |
505 | // Fill in argumentGPR0 missing arg slots with undefined |
506 | jit.move(JSInterfaceJIT::argumentGPR0, JSInterfaceJIT::argumentGPR2); |
507 | jit.move(JSInterfaceJIT::TrustedImm64(ValueUndefined), extraTemp); |
508 | JSInterfaceJIT::Label fillUndefinedLoop(jit.label()); |
509 | jit.store64(extraTemp, MacroAssembler::BaseIndex(JSInterfaceJIT::regT3, JSInterfaceJIT::argumentGPR0, JSInterfaceJIT::TimesEight)); |
510 | jit.addPtr(JSInterfaceJIT::TrustedImm32(8), JSInterfaceJIT::regT3); |
511 | jit.branchAdd32(MacroAssembler::NonZero, JSInterfaceJIT::TrustedImm32(1), JSInterfaceJIT::argumentGPR2).linkTo(fillUndefinedLoop, &jit); |
512 | |
513 | done.link(&jit); |
514 | |
515 | #if CPU(ARM64E) |
516 | jit.loadPtr(JSInterfaceJIT::Address(GPRInfo::callFrameRegister, CallFrame::returnPCOffset()), GPRInfo::regT3); |
517 | jit.move(JSInterfaceJIT::TrustedImmPtr(tempReturnPCTag), extraTemp); |
518 | jit.untagPtr(extraTemp, GPRInfo::regT3); |
519 | jit.addPtr(JSInterfaceJIT::TrustedImm32(sizeof(CallerFrameAndPC)), GPRInfo::callFrameRegister, extraTemp); |
520 | jit.tagPtr(extraTemp, GPRInfo::regT3); |
521 | jit.storePtr(GPRInfo::regT3, JSInterfaceJIT::Address(GPRInfo::callFrameRegister, CallFrame::returnPCOffset())); |
522 | #endif |
523 | |
524 | # if CPU(X86_64) |
525 | jit.push(JSInterfaceJIT::regT4); |
526 | # endif |
527 | jit.ret(); |
528 | #else // USE(JSVALUE64) section above, USE(JSVALUE32_64) section below. |
529 | # if CPU(X86) |
530 | jit.pop(JSInterfaceJIT::regT4); |
531 | # endif |
532 | jit.move(JSInterfaceJIT::callFrameRegister, JSInterfaceJIT::regT3); |
533 | jit.load32(JSInterfaceJIT::addressFor(CallFrameSlot::argumentCount), JSInterfaceJIT::argumentGPR2); |
534 | jit.add32(JSInterfaceJIT::TrustedImm32(CallFrame::headerSizeInRegisters), JSInterfaceJIT::argumentGPR2); |
535 | |
536 | // Check to see if we have extra slots we can use |
537 | jit.move(JSInterfaceJIT::argumentGPR0, JSInterfaceJIT::argumentGPR1); |
538 | jit.and32(JSInterfaceJIT::TrustedImm32(stackAlignmentRegisters() - 1), JSInterfaceJIT::argumentGPR1); |
539 | JSInterfaceJIT::Jump noExtraSlot = jit.branchTest32(MacroAssembler::Zero, JSInterfaceJIT::argumentGPR1); |
540 | JSInterfaceJIT::Label fillExtraSlots(jit.label()); |
541 | jit.move(JSInterfaceJIT::TrustedImm32(0), JSInterfaceJIT::regT5); |
542 | jit.store32(JSInterfaceJIT::regT5, MacroAssembler::BaseIndex(JSInterfaceJIT::callFrameRegister, JSInterfaceJIT::argumentGPR2, JSInterfaceJIT::TimesEight, PayloadOffset)); |
543 | jit.move(JSInterfaceJIT::TrustedImm32(JSValue::UndefinedTag), JSInterfaceJIT::regT5); |
544 | jit.store32(JSInterfaceJIT::regT5, MacroAssembler::BaseIndex(JSInterfaceJIT::callFrameRegister, JSInterfaceJIT::argumentGPR2, JSInterfaceJIT::TimesEight, TagOffset)); |
545 | jit.add32(JSInterfaceJIT::TrustedImm32(1), JSInterfaceJIT::argumentGPR2); |
546 | jit.branchSub32(JSInterfaceJIT::NonZero, JSInterfaceJIT::TrustedImm32(1), JSInterfaceJIT::argumentGPR1).linkTo(fillExtraSlots, &jit); |
547 | jit.and32(JSInterfaceJIT::TrustedImm32(-stackAlignmentRegisters()), JSInterfaceJIT::argumentGPR0); |
548 | JSInterfaceJIT::Jump done = jit.branchTest32(MacroAssembler::Zero, JSInterfaceJIT::argumentGPR0); |
549 | noExtraSlot.link(&jit); |
550 | |
551 | jit.neg32(JSInterfaceJIT::argumentGPR0); |
552 | |
553 | // Adjust call frame register and stack pointer to account for missing args. |
554 | // We need to change the stack pointer first before performing copy/fill loops. |
555 | // This stack space below the stack pointer is considered unused by OS. Therefore, |
556 | // OS may corrupt this space when constructing a signal stack. |
557 | jit.move(JSInterfaceJIT::argumentGPR0, JSInterfaceJIT::regT5); |
558 | jit.lshift32(JSInterfaceJIT::TrustedImm32(3), JSInterfaceJIT::regT5); |
559 | jit.addPtr(JSInterfaceJIT::regT5, JSInterfaceJIT::callFrameRegister); |
560 | jit.untagReturnAddress(); |
561 | jit.addPtr(JSInterfaceJIT::regT5, JSInterfaceJIT::stackPointerRegister); |
562 | jit.tagReturnAddress(); |
563 | |
564 | // Move current frame down argumentGPR0 number of slots |
565 | JSInterfaceJIT::Label copyLoop(jit.label()); |
566 | jit.load32(MacroAssembler::Address(JSInterfaceJIT::regT3, PayloadOffset), JSInterfaceJIT::regT5); |
567 | jit.store32(JSInterfaceJIT::regT5, MacroAssembler::BaseIndex(JSInterfaceJIT::regT3, JSInterfaceJIT::argumentGPR0, JSInterfaceJIT::TimesEight, PayloadOffset)); |
568 | jit.load32(MacroAssembler::Address(JSInterfaceJIT::regT3, TagOffset), JSInterfaceJIT::regT5); |
569 | jit.store32(JSInterfaceJIT::regT5, MacroAssembler::BaseIndex(JSInterfaceJIT::regT3, JSInterfaceJIT::argumentGPR0, JSInterfaceJIT::TimesEight, TagOffset)); |
570 | jit.addPtr(JSInterfaceJIT::TrustedImm32(8), JSInterfaceJIT::regT3); |
571 | jit.branchSub32(MacroAssembler::NonZero, JSInterfaceJIT::TrustedImm32(1), JSInterfaceJIT::argumentGPR2).linkTo(copyLoop, &jit); |
572 | |
573 | // Fill in argumentGPR0 missing arg slots with undefined |
574 | jit.move(JSInterfaceJIT::argumentGPR0, JSInterfaceJIT::argumentGPR2); |
575 | JSInterfaceJIT::Label fillUndefinedLoop(jit.label()); |
576 | jit.move(JSInterfaceJIT::TrustedImm32(0), JSInterfaceJIT::regT5); |
577 | jit.store32(JSInterfaceJIT::regT5, MacroAssembler::BaseIndex(JSInterfaceJIT::regT3, JSInterfaceJIT::argumentGPR0, JSInterfaceJIT::TimesEight, PayloadOffset)); |
578 | jit.move(JSInterfaceJIT::TrustedImm32(JSValue::UndefinedTag), JSInterfaceJIT::regT5); |
579 | jit.store32(JSInterfaceJIT::regT5, MacroAssembler::BaseIndex(JSInterfaceJIT::regT3, JSInterfaceJIT::argumentGPR0, JSInterfaceJIT::TimesEight, TagOffset)); |
580 | |
581 | jit.addPtr(JSInterfaceJIT::TrustedImm32(8), JSInterfaceJIT::regT3); |
582 | jit.branchAdd32(MacroAssembler::NonZero, JSInterfaceJIT::TrustedImm32(1), JSInterfaceJIT::argumentGPR2).linkTo(fillUndefinedLoop, &jit); |
583 | |
584 | done.link(&jit); |
585 | |
586 | # if CPU(X86) |
587 | jit.push(JSInterfaceJIT::regT4); |
588 | # endif |
589 | jit.ret(); |
590 | #endif // End of USE(JSVALUE32_64) section. |
591 | |
592 | LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID); |
593 | return FINALIZE_CODE(patchBuffer, JITThunkPtrTag, "fixup arity" ); |
594 | } |
595 | |
596 | MacroAssemblerCodeRef<JITThunkPtrTag> unreachableGenerator(VM* vm) |
597 | { |
598 | JSInterfaceJIT jit(vm); |
599 | |
600 | jit.breakpoint(); |
601 | |
602 | LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID); |
603 | return FINALIZE_CODE(patchBuffer, JITThunkPtrTag, "unreachable thunk" ); |
604 | } |
605 | |
606 | MacroAssemblerCodeRef<JITThunkPtrTag> stringGetByValGenerator(VM* vm) |
607 | { |
608 | // regT0 is JSString*, and regT1 (64bit) or regT2 (32bit) is int index. |
609 | // Return regT0 = result JSString* if succeeds. Otherwise, return regT0 = 0. |
610 | #if USE(JSVALUE64) |
611 | GPRReg stringGPR = GPRInfo::regT0; |
612 | GPRReg indexGPR = GPRInfo::regT1; |
613 | GPRReg scratchGPR = GPRInfo::regT2; |
614 | #else |
615 | GPRReg stringGPR = GPRInfo::regT0; |
616 | GPRReg indexGPR = GPRInfo::regT2; |
617 | GPRReg scratchGPR = GPRInfo::regT1; |
618 | #endif |
619 | |
620 | JSInterfaceJIT jit(vm); |
621 | JSInterfaceJIT::JumpList failures; |
622 | jit.tagReturnAddress(); |
623 | |
624 | // Load string length to regT2, and start the process of loading the data pointer into regT0 |
625 | jit.loadPtr(JSInterfaceJIT::Address(stringGPR, JSString::offsetOfValue()), stringGPR); |
626 | failures.append(jit.branchIfRopeStringImpl(stringGPR)); |
627 | jit.load32(JSInterfaceJIT::Address(stringGPR, StringImpl::lengthMemoryOffset()), scratchGPR); |
628 | |
629 | // Do an unsigned compare to simultaneously filter negative indices as well as indices that are too large |
630 | failures.append(jit.branch32(JSInterfaceJIT::AboveOrEqual, indexGPR, scratchGPR)); |
631 | |
632 | // Load the character |
633 | JSInterfaceJIT::JumpList is16Bit; |
634 | JSInterfaceJIT::JumpList cont8Bit; |
635 | // Load the string flags |
636 | jit.load32(JSInterfaceJIT::Address(stringGPR, StringImpl::flagsOffset()), scratchGPR); |
637 | jit.loadPtr(JSInterfaceJIT::Address(stringGPR, StringImpl::dataOffset()), stringGPR); |
638 | is16Bit.append(jit.branchTest32(JSInterfaceJIT::Zero, scratchGPR, JSInterfaceJIT::TrustedImm32(StringImpl::flagIs8Bit()))); |
639 | jit.load8(JSInterfaceJIT::BaseIndex(stringGPR, indexGPR, JSInterfaceJIT::TimesOne, 0), stringGPR); |
640 | cont8Bit.append(jit.jump()); |
641 | is16Bit.link(&jit); |
642 | jit.load16(JSInterfaceJIT::BaseIndex(stringGPR, indexGPR, JSInterfaceJIT::TimesTwo, 0), stringGPR); |
643 | cont8Bit.link(&jit); |
644 | |
645 | failures.append(jit.branch32(JSInterfaceJIT::Above, stringGPR, JSInterfaceJIT::TrustedImm32(maxSingleCharacterString))); |
646 | jit.move(JSInterfaceJIT::TrustedImmPtr(vm->smallStrings.singleCharacterStrings()), indexGPR); |
647 | jit.loadPtr(JSInterfaceJIT::BaseIndex(indexGPR, stringGPR, JSInterfaceJIT::ScalePtr, 0), stringGPR); |
648 | jit.ret(); |
649 | |
650 | failures.link(&jit); |
651 | jit.move(JSInterfaceJIT::TrustedImm32(0), stringGPR); |
652 | jit.ret(); |
653 | |
654 | LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID); |
655 | return FINALIZE_CODE(patchBuffer, JITThunkPtrTag, "String get_by_val stub" ); |
656 | } |
657 | |
658 | static void stringCharLoad(SpecializedThunkJIT& jit) |
659 | { |
660 | // load string |
661 | jit.loadJSStringArgument(SpecializedThunkJIT::ThisArgument, SpecializedThunkJIT::regT0); |
662 | |
663 | // Load string length to regT2, and start the process of loading the data pointer into regT0 |
664 | jit.loadPtr(MacroAssembler::Address(SpecializedThunkJIT::regT0, JSString::offsetOfValue()), SpecializedThunkJIT::regT0); |
665 | jit.appendFailure(jit.branchIfRopeStringImpl(SpecializedThunkJIT::regT0)); |
666 | jit.load32(MacroAssembler::Address(SpecializedThunkJIT::regT0, StringImpl::lengthMemoryOffset()), SpecializedThunkJIT::regT2); |
667 | |
668 | // load index |
669 | jit.loadInt32Argument(0, SpecializedThunkJIT::regT1); // regT1 contains the index |
670 | |
671 | // Do an unsigned compare to simultaneously filter negative indices as well as indices that are too large |
672 | jit.appendFailure(jit.branch32(MacroAssembler::AboveOrEqual, SpecializedThunkJIT::regT1, SpecializedThunkJIT::regT2)); |
673 | |
674 | // Load the character |
675 | SpecializedThunkJIT::JumpList is16Bit; |
676 | SpecializedThunkJIT::JumpList cont8Bit; |
677 | // Load the string flags |
678 | jit.loadPtr(MacroAssembler::Address(SpecializedThunkJIT::regT0, StringImpl::flagsOffset()), SpecializedThunkJIT::regT2); |
679 | jit.loadPtr(MacroAssembler::Address(SpecializedThunkJIT::regT0, StringImpl::dataOffset()), SpecializedThunkJIT::regT0); |
680 | is16Bit.append(jit.branchTest32(MacroAssembler::Zero, SpecializedThunkJIT::regT2, MacroAssembler::TrustedImm32(StringImpl::flagIs8Bit()))); |
681 | jit.load8(MacroAssembler::BaseIndex(SpecializedThunkJIT::regT0, SpecializedThunkJIT::regT1, MacroAssembler::TimesOne, 0), SpecializedThunkJIT::regT0); |
682 | cont8Bit.append(jit.jump()); |
683 | is16Bit.link(&jit); |
684 | jit.load16(MacroAssembler::BaseIndex(SpecializedThunkJIT::regT0, SpecializedThunkJIT::regT1, MacroAssembler::TimesTwo, 0), SpecializedThunkJIT::regT0); |
685 | cont8Bit.link(&jit); |
686 | } |
687 | |
688 | static void charToString(SpecializedThunkJIT& jit, VM* vm, MacroAssembler::RegisterID src, MacroAssembler::RegisterID dst, MacroAssembler::RegisterID scratch) |
689 | { |
690 | jit.appendFailure(jit.branch32(MacroAssembler::Above, src, MacroAssembler::TrustedImm32(maxSingleCharacterString))); |
691 | jit.move(MacroAssembler::TrustedImmPtr(vm->smallStrings.singleCharacterStrings()), scratch); |
692 | jit.loadPtr(MacroAssembler::BaseIndex(scratch, src, MacroAssembler::ScalePtr, 0), dst); |
693 | jit.appendFailure(jit.branchTestPtr(MacroAssembler::Zero, dst)); |
694 | } |
695 | |
696 | MacroAssemblerCodeRef<JITThunkPtrTag> charCodeAtThunkGenerator(VM* vm) |
697 | { |
698 | SpecializedThunkJIT jit(vm, 1); |
699 | stringCharLoad(jit); |
700 | jit.returnInt32(SpecializedThunkJIT::regT0); |
701 | return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "charCodeAt" ); |
702 | } |
703 | |
704 | MacroAssemblerCodeRef<JITThunkPtrTag> charAtThunkGenerator(VM* vm) |
705 | { |
706 | SpecializedThunkJIT jit(vm, 1); |
707 | stringCharLoad(jit); |
708 | charToString(jit, vm, SpecializedThunkJIT::regT0, SpecializedThunkJIT::regT0, SpecializedThunkJIT::regT1); |
709 | jit.returnJSCell(SpecializedThunkJIT::regT0); |
710 | return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "charAt" ); |
711 | } |
712 | |
713 | MacroAssemblerCodeRef<JITThunkPtrTag> fromCharCodeThunkGenerator(VM* vm) |
714 | { |
715 | SpecializedThunkJIT jit(vm, 1); |
716 | // load char code |
717 | jit.loadInt32Argument(0, SpecializedThunkJIT::regT0); |
718 | charToString(jit, vm, SpecializedThunkJIT::regT0, SpecializedThunkJIT::regT0, SpecializedThunkJIT::regT1); |
719 | jit.returnJSCell(SpecializedThunkJIT::regT0); |
720 | return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "fromCharCode" ); |
721 | } |
722 | |
723 | MacroAssemblerCodeRef<JITThunkPtrTag> clz32ThunkGenerator(VM* vm) |
724 | { |
725 | SpecializedThunkJIT jit(vm, 1); |
726 | MacroAssembler::Jump nonIntArgJump; |
727 | jit.loadInt32Argument(0, SpecializedThunkJIT::regT0, nonIntArgJump); |
728 | |
729 | SpecializedThunkJIT::Label convertedArgumentReentry(&jit); |
730 | jit.countLeadingZeros32(SpecializedThunkJIT::regT0, SpecializedThunkJIT::regT1); |
731 | jit.returnInt32(SpecializedThunkJIT::regT1); |
732 | |
733 | if (jit.supportsFloatingPointTruncate()) { |
734 | nonIntArgJump.link(&jit); |
735 | jit.loadDoubleArgument(0, SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0); |
736 | jit.branchTruncateDoubleToInt32(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0, SpecializedThunkJIT::BranchIfTruncateSuccessful).linkTo(convertedArgumentReentry, &jit); |
737 | jit.appendFailure(jit.jump()); |
738 | } else |
739 | jit.appendFailure(nonIntArgJump); |
740 | |
741 | return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "clz32" ); |
742 | } |
743 | |
744 | MacroAssemblerCodeRef<JITThunkPtrTag> sqrtThunkGenerator(VM* vm) |
745 | { |
746 | SpecializedThunkJIT jit(vm, 1); |
747 | if (!jit.supportsFloatingPointSqrt()) |
748 | return MacroAssemblerCodeRef<JITThunkPtrTag>::createSelfManagedCodeRef(vm->jitStubs->ctiNativeCall(vm)); |
749 | |
750 | jit.loadDoubleArgument(0, SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0); |
751 | jit.sqrtDouble(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT0); |
752 | jit.returnDouble(SpecializedThunkJIT::fpRegT0); |
753 | return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "sqrt" ); |
754 | } |
755 | |
756 | |
757 | #define UnaryDoubleOpWrapper(function) function##Wrapper |
758 | enum MathThunkCallingConvention { }; |
759 | typedef MathThunkCallingConvention(*MathThunk)(MathThunkCallingConvention); |
760 | |
761 | #if CPU(X86_64) && COMPILER(GCC_COMPATIBLE) && (OS(DARWIN) || OS(LINUX)) |
762 | |
763 | #define defineUnaryDoubleOpWrapper(function) \ |
764 | asm( \ |
765 | ".text\n" \ |
766 | ".globl " SYMBOL_STRING(function##Thunk) "\n" \ |
767 | HIDE_SYMBOL(function##Thunk) "\n" \ |
768 | SYMBOL_STRING(function##Thunk) ":" "\n" \ |
769 | "pushq %rax\n" \ |
770 | "call " GLOBAL_REFERENCE(function) "\n" \ |
771 | "popq %rcx\n" \ |
772 | "ret\n" \ |
773 | );\ |
774 | extern "C" { \ |
775 | MathThunkCallingConvention function##Thunk(MathThunkCallingConvention); \ |
776 | } \ |
777 | static MathThunk UnaryDoubleOpWrapper(function) = &function##Thunk; |
778 | |
779 | #elif CPU(X86) && COMPILER(GCC_COMPATIBLE) && OS(LINUX) && defined(__PIC__) |
780 | #define defineUnaryDoubleOpWrapper(function) \ |
781 | asm( \ |
782 | ".text\n" \ |
783 | ".globl " SYMBOL_STRING(function##Thunk) "\n" \ |
784 | HIDE_SYMBOL(function##Thunk) "\n" \ |
785 | SYMBOL_STRING(function##Thunk) ":" "\n" \ |
786 | "pushl %ebx\n" \ |
787 | "subl $20, %esp\n" \ |
788 | "movsd %xmm0, (%esp) \n" \ |
789 | "call __x86.get_pc_thunk.bx\n" \ |
790 | "addl $_GLOBAL_OFFSET_TABLE_, %ebx\n" \ |
791 | "call " GLOBAL_REFERENCE(function) "\n" \ |
792 | "fstpl (%esp) \n" \ |
793 | "movsd (%esp), %xmm0 \n" \ |
794 | "addl $20, %esp\n" \ |
795 | "popl %ebx\n" \ |
796 | "ret\n" \ |
797 | );\ |
798 | extern "C" { \ |
799 | MathThunkCallingConvention function##Thunk(MathThunkCallingConvention); \ |
800 | } \ |
801 | static MathThunk UnaryDoubleOpWrapper(function) = &function##Thunk; |
802 | |
803 | #elif CPU(X86) && COMPILER(GCC_COMPATIBLE) && (OS(DARWIN) || OS(LINUX)) |
804 | #define defineUnaryDoubleOpWrapper(function) \ |
805 | asm( \ |
806 | ".text\n" \ |
807 | ".globl " SYMBOL_STRING(function##Thunk) "\n" \ |
808 | HIDE_SYMBOL(function##Thunk) "\n" \ |
809 | SYMBOL_STRING(function##Thunk) ":" "\n" \ |
810 | "subl $20, %esp\n" \ |
811 | "movsd %xmm0, (%esp) \n" \ |
812 | "call " GLOBAL_REFERENCE(function) "\n" \ |
813 | "fstpl (%esp) \n" \ |
814 | "movsd (%esp), %xmm0 \n" \ |
815 | "addl $20, %esp\n" \ |
816 | "ret\n" \ |
817 | );\ |
818 | extern "C" { \ |
819 | MathThunkCallingConvention function##Thunk(MathThunkCallingConvention); \ |
820 | } \ |
821 | static MathThunk UnaryDoubleOpWrapper(function) = &function##Thunk; |
822 | |
823 | #elif CPU(ARM_THUMB2) && COMPILER(GCC_COMPATIBLE) && PLATFORM(IOS_FAMILY) |
824 | |
825 | #define defineUnaryDoubleOpWrapper(function) \ |
826 | asm( \ |
827 | ".text\n" \ |
828 | ".align 2\n" \ |
829 | ".globl " SYMBOL_STRING(function##Thunk) "\n" \ |
830 | HIDE_SYMBOL(function##Thunk) "\n" \ |
831 | ".thumb\n" \ |
832 | ".thumb_func " THUMB_FUNC_PARAM(function##Thunk) "\n" \ |
833 | SYMBOL_STRING(function##Thunk) ":" "\n" \ |
834 | "push {lr}\n" \ |
835 | "vmov r0, r1, d0\n" \ |
836 | "blx " GLOBAL_REFERENCE(function) "\n" \ |
837 | "vmov d0, r0, r1\n" \ |
838 | "pop {lr}\n" \ |
839 | "bx lr\n" \ |
840 | ); \ |
841 | extern "C" { \ |
842 | MathThunkCallingConvention function##Thunk(MathThunkCallingConvention); \ |
843 | } \ |
844 | static MathThunk UnaryDoubleOpWrapper(function) = &function##Thunk; |
845 | |
846 | #elif CPU(ARM64) |
847 | |
848 | #define defineUnaryDoubleOpWrapper(function) \ |
849 | asm( \ |
850 | ".text\n" \ |
851 | ".align 2\n" \ |
852 | ".globl " SYMBOL_STRING(function##Thunk) "\n" \ |
853 | HIDE_SYMBOL(function##Thunk) "\n" \ |
854 | SYMBOL_STRING(function##Thunk) ":" "\n" \ |
855 | "b " GLOBAL_REFERENCE(function) "\n" \ |
856 | ".previous" \ |
857 | ); \ |
858 | extern "C" { \ |
859 | MathThunkCallingConvention function##Thunk(MathThunkCallingConvention); \ |
860 | } \ |
861 | static MathThunk UnaryDoubleOpWrapper(function) = &function##Thunk; |
862 | |
863 | #elif CPU(X86) && COMPILER(MSVC) && OS(WINDOWS) |
864 | |
865 | // MSVC does not accept floor, etc, to be called directly from inline assembly, so we need to wrap these functions. |
866 | static double (_cdecl *floorFunction)(double) = floor; |
867 | static double (_cdecl *ceilFunction)(double) = ceil; |
868 | static double (_cdecl *truncFunction)(double) = trunc; |
869 | static double (_cdecl *expFunction)(double) = exp; |
870 | static double (_cdecl *logFunction)(double) = log; |
871 | static double (_cdecl *jsRoundFunction)(double) = jsRound; |
872 | |
873 | #define defineUnaryDoubleOpWrapper(function) \ |
874 | extern "C" __declspec(naked) MathThunkCallingConvention function##Thunk(MathThunkCallingConvention) \ |
875 | { \ |
876 | __asm \ |
877 | { \ |
878 | __asm sub esp, 20 \ |
879 | __asm movsd mmword ptr [esp], xmm0 \ |
880 | __asm call function##Function \ |
881 | __asm fstp qword ptr [esp] \ |
882 | __asm movsd xmm0, mmword ptr [esp] \ |
883 | __asm add esp, 20 \ |
884 | __asm ret \ |
885 | } \ |
886 | } \ |
887 | static MathThunk UnaryDoubleOpWrapper(function) = &function##Thunk; |
888 | |
889 | #else |
890 | |
891 | #define defineUnaryDoubleOpWrapper(function) \ |
892 | static MathThunk UnaryDoubleOpWrapper(function) = 0 |
893 | #endif |
894 | |
895 | defineUnaryDoubleOpWrapper(jsRound); |
896 | defineUnaryDoubleOpWrapper(exp); |
897 | defineUnaryDoubleOpWrapper(log); |
898 | defineUnaryDoubleOpWrapper(floor); |
899 | defineUnaryDoubleOpWrapper(ceil); |
900 | defineUnaryDoubleOpWrapper(trunc); |
901 | |
902 | static const double halfConstant = 0.5; |
903 | |
904 | MacroAssemblerCodeRef<JITThunkPtrTag> floorThunkGenerator(VM* vm) |
905 | { |
906 | SpecializedThunkJIT jit(vm, 1); |
907 | MacroAssembler::Jump nonIntJump; |
908 | if (!UnaryDoubleOpWrapper(floor) || !jit.supportsFloatingPoint()) |
909 | return MacroAssemblerCodeRef<JITThunkPtrTag>::createSelfManagedCodeRef(vm->jitStubs->ctiNativeCall(vm)); |
910 | jit.loadInt32Argument(0, SpecializedThunkJIT::regT0, nonIntJump); |
911 | jit.returnInt32(SpecializedThunkJIT::regT0); |
912 | nonIntJump.link(&jit); |
913 | jit.loadDoubleArgument(0, SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0); |
914 | |
915 | if (jit.supportsFloatingPointRounding()) { |
916 | SpecializedThunkJIT::JumpList doubleResult; |
917 | jit.floorDouble(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT0); |
918 | jit.branchConvertDoubleToInt32(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0, doubleResult, SpecializedThunkJIT::fpRegT1); |
919 | jit.returnInt32(SpecializedThunkJIT::regT0); |
920 | doubleResult.link(&jit); |
921 | jit.returnDouble(SpecializedThunkJIT::fpRegT0); |
922 | return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "floor" ); |
923 | } |
924 | |
925 | SpecializedThunkJIT::Jump intResult; |
926 | SpecializedThunkJIT::JumpList doubleResult; |
927 | if (jit.supportsFloatingPointTruncate()) { |
928 | jit.moveZeroToDouble(SpecializedThunkJIT::fpRegT1); |
929 | doubleResult.append(jit.branchDouble(MacroAssembler::DoubleEqual, SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT1)); |
930 | SpecializedThunkJIT::JumpList slowPath; |
931 | // Handle the negative doubles in the slow path for now. |
932 | slowPath.append(jit.branchDouble(MacroAssembler::DoubleLessThanOrUnordered, SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT1)); |
933 | slowPath.append(jit.branchTruncateDoubleToInt32(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0)); |
934 | intResult = jit.jump(); |
935 | slowPath.link(&jit); |
936 | } |
937 | jit.callDoubleToDoublePreservingReturn(UnaryDoubleOpWrapper(floor)); |
938 | jit.branchConvertDoubleToInt32(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0, doubleResult, SpecializedThunkJIT::fpRegT1); |
939 | if (jit.supportsFloatingPointTruncate()) |
940 | intResult.link(&jit); |
941 | jit.returnInt32(SpecializedThunkJIT::regT0); |
942 | doubleResult.link(&jit); |
943 | jit.returnDouble(SpecializedThunkJIT::fpRegT0); |
944 | return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "floor" ); |
945 | } |
946 | |
947 | MacroAssemblerCodeRef<JITThunkPtrTag> ceilThunkGenerator(VM* vm) |
948 | { |
949 | SpecializedThunkJIT jit(vm, 1); |
950 | if (!UnaryDoubleOpWrapper(ceil) || !jit.supportsFloatingPoint()) |
951 | return MacroAssemblerCodeRef<JITThunkPtrTag>::createSelfManagedCodeRef(vm->jitStubs->ctiNativeCall(vm)); |
952 | MacroAssembler::Jump nonIntJump; |
953 | jit.loadInt32Argument(0, SpecializedThunkJIT::regT0, nonIntJump); |
954 | jit.returnInt32(SpecializedThunkJIT::regT0); |
955 | nonIntJump.link(&jit); |
956 | jit.loadDoubleArgument(0, SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0); |
957 | if (jit.supportsFloatingPointRounding()) |
958 | jit.ceilDouble(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT0); |
959 | else |
960 | jit.callDoubleToDoublePreservingReturn(UnaryDoubleOpWrapper(ceil)); |
961 | |
962 | SpecializedThunkJIT::JumpList doubleResult; |
963 | jit.branchConvertDoubleToInt32(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0, doubleResult, SpecializedThunkJIT::fpRegT1); |
964 | jit.returnInt32(SpecializedThunkJIT::regT0); |
965 | doubleResult.link(&jit); |
966 | jit.returnDouble(SpecializedThunkJIT::fpRegT0); |
967 | return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "ceil" ); |
968 | } |
969 | |
970 | MacroAssemblerCodeRef<JITThunkPtrTag> truncThunkGenerator(VM* vm) |
971 | { |
972 | SpecializedThunkJIT jit(vm, 1); |
973 | if (!UnaryDoubleOpWrapper(trunc) || !jit.supportsFloatingPoint()) |
974 | return MacroAssemblerCodeRef<JITThunkPtrTag>::createSelfManagedCodeRef(vm->jitStubs->ctiNativeCall(vm)); |
975 | MacroAssembler::Jump nonIntJump; |
976 | jit.loadInt32Argument(0, SpecializedThunkJIT::regT0, nonIntJump); |
977 | jit.returnInt32(SpecializedThunkJIT::regT0); |
978 | nonIntJump.link(&jit); |
979 | jit.loadDoubleArgument(0, SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0); |
980 | if (jit.supportsFloatingPointRounding()) |
981 | jit.roundTowardZeroDouble(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT0); |
982 | else |
983 | jit.callDoubleToDoublePreservingReturn(UnaryDoubleOpWrapper(trunc)); |
984 | |
985 | SpecializedThunkJIT::JumpList doubleResult; |
986 | jit.branchConvertDoubleToInt32(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0, doubleResult, SpecializedThunkJIT::fpRegT1); |
987 | jit.returnInt32(SpecializedThunkJIT::regT0); |
988 | doubleResult.link(&jit); |
989 | jit.returnDouble(SpecializedThunkJIT::fpRegT0); |
990 | return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "trunc" ); |
991 | } |
992 | |
993 | MacroAssemblerCodeRef<JITThunkPtrTag> roundThunkGenerator(VM* vm) |
994 | { |
995 | SpecializedThunkJIT jit(vm, 1); |
996 | if (!UnaryDoubleOpWrapper(jsRound) || !jit.supportsFloatingPoint()) |
997 | return MacroAssemblerCodeRef<JITThunkPtrTag>::createSelfManagedCodeRef(vm->jitStubs->ctiNativeCall(vm)); |
998 | MacroAssembler::Jump nonIntJump; |
999 | jit.loadInt32Argument(0, SpecializedThunkJIT::regT0, nonIntJump); |
1000 | jit.returnInt32(SpecializedThunkJIT::regT0); |
1001 | nonIntJump.link(&jit); |
1002 | jit.loadDoubleArgument(0, SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0); |
1003 | SpecializedThunkJIT::Jump intResult; |
1004 | SpecializedThunkJIT::JumpList doubleResult; |
1005 | if (jit.supportsFloatingPointTruncate()) { |
1006 | jit.moveZeroToDouble(SpecializedThunkJIT::fpRegT1); |
1007 | doubleResult.append(jit.branchDouble(MacroAssembler::DoubleEqual, SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT1)); |
1008 | SpecializedThunkJIT::JumpList slowPath; |
1009 | // Handle the negative doubles in the slow path for now. |
1010 | slowPath.append(jit.branchDouble(MacroAssembler::DoubleLessThanOrUnordered, SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT1)); |
1011 | jit.loadDouble(MacroAssembler::TrustedImmPtr(&halfConstant), SpecializedThunkJIT::fpRegT1); |
1012 | jit.addDouble(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT1); |
1013 | slowPath.append(jit.branchTruncateDoubleToInt32(SpecializedThunkJIT::fpRegT1, SpecializedThunkJIT::regT0)); |
1014 | intResult = jit.jump(); |
1015 | slowPath.link(&jit); |
1016 | } |
1017 | jit.callDoubleToDoublePreservingReturn(UnaryDoubleOpWrapper(jsRound)); |
1018 | jit.branchConvertDoubleToInt32(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0, doubleResult, SpecializedThunkJIT::fpRegT1); |
1019 | if (jit.supportsFloatingPointTruncate()) |
1020 | intResult.link(&jit); |
1021 | jit.returnInt32(SpecializedThunkJIT::regT0); |
1022 | doubleResult.link(&jit); |
1023 | jit.returnDouble(SpecializedThunkJIT::fpRegT0); |
1024 | return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "round" ); |
1025 | } |
1026 | |
1027 | MacroAssemblerCodeRef<JITThunkPtrTag> expThunkGenerator(VM* vm) |
1028 | { |
1029 | if (!UnaryDoubleOpWrapper(exp)) |
1030 | return MacroAssemblerCodeRef<JITThunkPtrTag>::createSelfManagedCodeRef(vm->jitStubs->ctiNativeCall(vm)); |
1031 | SpecializedThunkJIT jit(vm, 1); |
1032 | if (!jit.supportsFloatingPoint()) |
1033 | return MacroAssemblerCodeRef<JITThunkPtrTag>::createSelfManagedCodeRef(vm->jitStubs->ctiNativeCall(vm)); |
1034 | jit.loadDoubleArgument(0, SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0); |
1035 | jit.callDoubleToDoublePreservingReturn(UnaryDoubleOpWrapper(exp)); |
1036 | jit.returnDouble(SpecializedThunkJIT::fpRegT0); |
1037 | return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "exp" ); |
1038 | } |
1039 | |
1040 | MacroAssemblerCodeRef<JITThunkPtrTag> logThunkGenerator(VM* vm) |
1041 | { |
1042 | if (!UnaryDoubleOpWrapper(log)) |
1043 | return MacroAssemblerCodeRef<JITThunkPtrTag>::createSelfManagedCodeRef(vm->jitStubs->ctiNativeCall(vm)); |
1044 | SpecializedThunkJIT jit(vm, 1); |
1045 | if (!jit.supportsFloatingPoint()) |
1046 | return MacroAssemblerCodeRef<JITThunkPtrTag>::createSelfManagedCodeRef(vm->jitStubs->ctiNativeCall(vm)); |
1047 | jit.loadDoubleArgument(0, SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0); |
1048 | jit.callDoubleToDoublePreservingReturn(UnaryDoubleOpWrapper(log)); |
1049 | jit.returnDouble(SpecializedThunkJIT::fpRegT0); |
1050 | return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "log" ); |
1051 | } |
1052 | |
1053 | MacroAssemblerCodeRef<JITThunkPtrTag> absThunkGenerator(VM* vm) |
1054 | { |
1055 | SpecializedThunkJIT jit(vm, 1); |
1056 | if (!jit.supportsFloatingPointAbs()) |
1057 | return MacroAssemblerCodeRef<JITThunkPtrTag>::createSelfManagedCodeRef(vm->jitStubs->ctiNativeCall(vm)); |
1058 | |
1059 | #if USE(JSVALUE64) |
1060 | unsigned virtualRegisterIndex = CallFrame::argumentOffset(0); |
1061 | jit.load64(AssemblyHelpers::addressFor(virtualRegisterIndex), GPRInfo::regT0); |
1062 | auto notInteger = jit.branchIfNotInt32(GPRInfo::regT0); |
1063 | |
1064 | // Abs Int32. |
1065 | jit.rshift32(GPRInfo::regT0, MacroAssembler::TrustedImm32(31), GPRInfo::regT1); |
1066 | jit.add32(GPRInfo::regT1, GPRInfo::regT0); |
1067 | jit.xor32(GPRInfo::regT1, GPRInfo::regT0); |
1068 | |
1069 | // IntMin cannot be inverted. |
1070 | MacroAssembler::Jump integerIsIntMin = jit.branchTest32(MacroAssembler::Signed, GPRInfo::regT0); |
1071 | |
1072 | // Box and finish. |
1073 | jit.or64(GPRInfo::tagTypeNumberRegister, GPRInfo::regT0); |
1074 | MacroAssembler::Jump doneWithIntegers = jit.jump(); |
1075 | |
1076 | // Handle Doubles. |
1077 | notInteger.link(&jit); |
1078 | jit.appendFailure(jit.branchIfNotNumber(GPRInfo::regT0)); |
1079 | jit.unboxDoubleWithoutAssertions(GPRInfo::regT0, GPRInfo::regT0, FPRInfo::fpRegT0); |
1080 | MacroAssembler::Label absFPR0Label = jit.label(); |
1081 | jit.absDouble(FPRInfo::fpRegT0, FPRInfo::fpRegT1); |
1082 | jit.boxDouble(FPRInfo::fpRegT1, GPRInfo::regT0); |
1083 | |
1084 | // Tail. |
1085 | doneWithIntegers.link(&jit); |
1086 | jit.returnJSValue(GPRInfo::regT0); |
1087 | |
1088 | // We know the value of regT0 is IntMin. We could load that value from memory but |
1089 | // it is simpler to just convert it. |
1090 | integerIsIntMin.link(&jit); |
1091 | jit.convertInt32ToDouble(GPRInfo::regT0, FPRInfo::fpRegT0); |
1092 | jit.jump().linkTo(absFPR0Label, &jit); |
1093 | #else |
1094 | MacroAssembler::Jump nonIntJump; |
1095 | jit.loadInt32Argument(0, SpecializedThunkJIT::regT0, nonIntJump); |
1096 | jit.rshift32(SpecializedThunkJIT::regT0, MacroAssembler::TrustedImm32(31), SpecializedThunkJIT::regT1); |
1097 | jit.add32(SpecializedThunkJIT::regT1, SpecializedThunkJIT::regT0); |
1098 | jit.xor32(SpecializedThunkJIT::regT1, SpecializedThunkJIT::regT0); |
1099 | jit.appendFailure(jit.branchTest32(MacroAssembler::Signed, SpecializedThunkJIT::regT0)); |
1100 | jit.returnInt32(SpecializedThunkJIT::regT0); |
1101 | nonIntJump.link(&jit); |
1102 | // Shame about the double int conversion here. |
1103 | jit.loadDoubleArgument(0, SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0); |
1104 | jit.absDouble(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::fpRegT1); |
1105 | jit.returnDouble(SpecializedThunkJIT::fpRegT1); |
1106 | #endif |
1107 | return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "abs" ); |
1108 | } |
1109 | |
1110 | MacroAssemblerCodeRef<JITThunkPtrTag> imulThunkGenerator(VM* vm) |
1111 | { |
1112 | SpecializedThunkJIT jit(vm, 2); |
1113 | MacroAssembler::Jump nonIntArg0Jump; |
1114 | jit.loadInt32Argument(0, SpecializedThunkJIT::regT0, nonIntArg0Jump); |
1115 | SpecializedThunkJIT::Label doneLoadingArg0(&jit); |
1116 | MacroAssembler::Jump nonIntArg1Jump; |
1117 | jit.loadInt32Argument(1, SpecializedThunkJIT::regT1, nonIntArg1Jump); |
1118 | SpecializedThunkJIT::Label doneLoadingArg1(&jit); |
1119 | jit.mul32(SpecializedThunkJIT::regT1, SpecializedThunkJIT::regT0); |
1120 | jit.returnInt32(SpecializedThunkJIT::regT0); |
1121 | |
1122 | if (jit.supportsFloatingPointTruncate()) { |
1123 | nonIntArg0Jump.link(&jit); |
1124 | jit.loadDoubleArgument(0, SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0); |
1125 | jit.branchTruncateDoubleToInt32(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT0, SpecializedThunkJIT::BranchIfTruncateSuccessful).linkTo(doneLoadingArg0, &jit); |
1126 | jit.appendFailure(jit.jump()); |
1127 | } else |
1128 | jit.appendFailure(nonIntArg0Jump); |
1129 | |
1130 | if (jit.supportsFloatingPointTruncate()) { |
1131 | nonIntArg1Jump.link(&jit); |
1132 | jit.loadDoubleArgument(1, SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT1); |
1133 | jit.branchTruncateDoubleToInt32(SpecializedThunkJIT::fpRegT0, SpecializedThunkJIT::regT1, SpecializedThunkJIT::BranchIfTruncateSuccessful).linkTo(doneLoadingArg1, &jit); |
1134 | jit.appendFailure(jit.jump()); |
1135 | } else |
1136 | jit.appendFailure(nonIntArg1Jump); |
1137 | |
1138 | return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "imul" ); |
1139 | } |
1140 | |
1141 | MacroAssemblerCodeRef<JITThunkPtrTag> randomThunkGenerator(VM* vm) |
1142 | { |
1143 | SpecializedThunkJIT jit(vm, 0); |
1144 | if (!jit.supportsFloatingPoint()) |
1145 | return MacroAssemblerCodeRef<JITThunkPtrTag>::createSelfManagedCodeRef(vm->jitStubs->ctiNativeCall(vm)); |
1146 | |
1147 | #if USE(JSVALUE64) |
1148 | jit.emitRandomThunk(*vm, SpecializedThunkJIT::regT0, SpecializedThunkJIT::regT1, SpecializedThunkJIT::regT2, SpecializedThunkJIT::regT3, SpecializedThunkJIT::fpRegT0); |
1149 | jit.returnDouble(SpecializedThunkJIT::fpRegT0); |
1150 | |
1151 | return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "random" ); |
1152 | #else |
1153 | return MacroAssemblerCodeRef<JITThunkPtrTag>::createSelfManagedCodeRef(vm->jitStubs->ctiNativeCall(vm)); |
1154 | #endif |
1155 | } |
1156 | |
1157 | MacroAssemblerCodeRef<JITThunkPtrTag> boundThisNoArgsFunctionCallGenerator(VM* vm) |
1158 | { |
1159 | CCallHelpers jit; |
1160 | |
1161 | jit.emitFunctionPrologue(); |
1162 | |
1163 | // Set up our call frame. |
1164 | jit.storePtr(CCallHelpers::TrustedImmPtr(nullptr), CCallHelpers::addressFor(CallFrameSlot::codeBlock)); |
1165 | jit.store32(CCallHelpers::TrustedImm32(0), CCallHelpers::tagFor(CallFrameSlot::argumentCount)); |
1166 | |
1167 | unsigned = 0; |
1168 | if (unsigned stackMisalignment = sizeof(CallerFrameAndPC) % stackAlignmentBytes()) |
1169 | extraStackNeeded = stackAlignmentBytes() - stackMisalignment; |
1170 | |
1171 | // We need to forward all of the arguments that we were passed. We aren't allowed to do a tail |
1172 | // call here as far as I can tell. At least not so long as the generic path doesn't do a tail |
1173 | // call, since that would be way too weird. |
1174 | |
1175 | // The formula for the number of stack bytes needed given some number of parameters (including |
1176 | // this) is: |
1177 | // |
1178 | // stackAlign((numParams + CallFrameHeaderSize) * sizeof(Register) - sizeof(CallerFrameAndPC)) |
1179 | // |
1180 | // Probably we want to write this as: |
1181 | // |
1182 | // stackAlign((numParams + (CallFrameHeaderSize - CallerFrameAndPCSize)) * sizeof(Register)) |
1183 | // |
1184 | // That's really all there is to this. We have all the registers we need to do it. |
1185 | |
1186 | jit.load32(CCallHelpers::payloadFor(CallFrameSlot::argumentCount), GPRInfo::regT1); |
1187 | jit.add32(CCallHelpers::TrustedImm32(CallFrame::headerSizeInRegisters - CallerFrameAndPC::sizeInRegisters), GPRInfo::regT1, GPRInfo::regT2); |
1188 | jit.lshift32(CCallHelpers::TrustedImm32(3), GPRInfo::regT2); |
1189 | jit.add32(CCallHelpers::TrustedImm32(stackAlignmentBytes() - 1), GPRInfo::regT2); |
1190 | jit.and32(CCallHelpers::TrustedImm32(-stackAlignmentBytes()), GPRInfo::regT2); |
1191 | |
1192 | if (extraStackNeeded) |
1193 | jit.add32(CCallHelpers::TrustedImm32(extraStackNeeded), GPRInfo::regT2); |
1194 | |
1195 | // At this point regT1 has the actual argument count and regT2 has the amount of stack we will |
1196 | // need. |
1197 | |
1198 | jit.subPtr(GPRInfo::regT2, CCallHelpers::stackPointerRegister); |
1199 | |
1200 | // Do basic callee frame setup, including 'this'. |
1201 | |
1202 | jit.loadCell(CCallHelpers::addressFor(CallFrameSlot::callee), GPRInfo::regT3); |
1203 | |
1204 | jit.store32(GPRInfo::regT1, CCallHelpers::calleeFramePayloadSlot(CallFrameSlot::argumentCount)); |
1205 | |
1206 | JSValueRegs valueRegs = JSValueRegs::withTwoAvailableRegs(GPRInfo::regT0, GPRInfo::regT2); |
1207 | jit.loadValue(CCallHelpers::Address(GPRInfo::regT3, JSBoundFunction::offsetOfBoundThis()), valueRegs); |
1208 | jit.storeValue(valueRegs, CCallHelpers::calleeArgumentSlot(0)); |
1209 | |
1210 | jit.loadPtr(CCallHelpers::Address(GPRInfo::regT3, JSBoundFunction::offsetOfTargetFunction()), GPRInfo::regT3); |
1211 | jit.storeCell(GPRInfo::regT3, CCallHelpers::calleeFrameSlot(CallFrameSlot::callee)); |
1212 | |
1213 | // OK, now we can start copying. This is a simple matter of copying parameters from the caller's |
1214 | // frame to the callee's frame. Note that we know that regT1 (the argument count) must be at |
1215 | // least 1. |
1216 | jit.sub32(CCallHelpers::TrustedImm32(1), GPRInfo::regT1); |
1217 | CCallHelpers::Jump done = jit.branchTest32(CCallHelpers::Zero, GPRInfo::regT1); |
1218 | |
1219 | CCallHelpers::Label loop = jit.label(); |
1220 | jit.sub32(CCallHelpers::TrustedImm32(1), GPRInfo::regT1); |
1221 | jit.loadValue(CCallHelpers::addressFor(virtualRegisterForArgument(1)).indexedBy(GPRInfo::regT1, CCallHelpers::TimesEight), valueRegs); |
1222 | jit.storeValue(valueRegs, CCallHelpers::calleeArgumentSlot(1).indexedBy(GPRInfo::regT1, CCallHelpers::TimesEight)); |
1223 | jit.branchTest32(CCallHelpers::NonZero, GPRInfo::regT1).linkTo(loop, &jit); |
1224 | |
1225 | done.link(&jit); |
1226 | |
1227 | jit.loadPtr( |
1228 | CCallHelpers::Address(GPRInfo::regT3, JSFunction::offsetOfExecutable()), |
1229 | GPRInfo::regT0); |
1230 | jit.loadPtr( |
1231 | CCallHelpers::Address( |
1232 | GPRInfo::regT0, ExecutableBase::offsetOfJITCodeWithArityCheckFor(CodeForCall)), |
1233 | GPRInfo::regT0); |
1234 | CCallHelpers::Jump noCode = jit.branchTestPtr(CCallHelpers::Zero, GPRInfo::regT0); |
1235 | |
1236 | emitPointerValidation(jit, GPRInfo::regT0, JSEntryPtrTag); |
1237 | jit.call(GPRInfo::regT0, JSEntryPtrTag); |
1238 | |
1239 | jit.emitFunctionEpilogue(); |
1240 | jit.ret(); |
1241 | |
1242 | LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID); |
1243 | linkBuffer.link(noCode, CodeLocationLabel<JITThunkPtrTag>(vm->jitStubs->ctiNativeTailCallWithoutSavedTags(vm))); |
1244 | return FINALIZE_CODE( |
1245 | linkBuffer, JITThunkPtrTag, "Specialized thunk for bound function calls with no arguments" ); |
1246 | } |
1247 | |
1248 | } // namespace JSC |
1249 | |
1250 | #endif // ENABLE(JIT) |
1251 | |