| 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 | |