1/* 2 * Copyright (C) 2011 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "DFGThunks.h" 28 29#if ENABLE(DFG_JIT) 30 31#include "DFGCCallHelpers.h" 32#include "DFGFPRInfo.h" 33#include "DFGGPRInfo.h" 34#include "DFGOSRExitCompiler.h" 35#include "MacroAssembler.h" 36 37namespace JSC { namespace DFG { 38 39MacroAssemblerCodeRef osrExitGenerationThunkGenerator(VM* vm) 40{ 41 MacroAssembler jit; 42 43 size_t scratchSize = sizeof(EncodedJSValue) * (GPRInfo::numberOfRegisters + FPRInfo::numberOfRegisters); 44 ScratchBuffer* scratchBuffer = vm->scratchBufferForSize(scratchSize); 45 EncodedJSValue* buffer = static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()); 46 47 for (unsigned i = 0; i < GPRInfo::numberOfRegisters; ++i) { 48#if USE(JSVALUE64) 49 jit.store64(GPRInfo::toRegister(i), buffer + i); 50#else 51 jit.store32(GPRInfo::toRegister(i), buffer + i); 52#endif 53 } 54 for (unsigned i = 0; i < FPRInfo::numberOfRegisters; ++i) { 55 jit.move(MacroAssembler::TrustedImmPtr(buffer + GPRInfo::numberOfRegisters + i), GPRInfo::regT0); 56 jit.storeDouble(FPRInfo::toRegister(i), GPRInfo::regT0); 57 } 58 59 // Tell GC mark phase how much of the scratch buffer is active during call. 60 jit.move(MacroAssembler::TrustedImmPtr(scratchBuffer->activeLengthPtr()), GPRInfo::regT0); 61 jit.storePtr(MacroAssembler::TrustedImmPtr(scratchSize), GPRInfo::regT0); 62 63 // Set up one argument. 64#if CPU(X86) 65 jit.poke(GPRInfo::callFrameRegister, 0); 66#else 67 jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); 68#endif 69 70 MacroAssembler::Call functionCall = jit.call(); 71 72 jit.move(MacroAssembler::TrustedImmPtr(scratchBuffer->activeLengthPtr()), GPRInfo::regT0); 73 jit.storePtr(MacroAssembler::TrustedImmPtr(0), GPRInfo::regT0); 74 75 for (unsigned i = 0; i < FPRInfo::numberOfRegisters; ++i) { 76 jit.move(MacroAssembler::TrustedImmPtr(buffer + GPRInfo::numberOfRegisters + i), GPRInfo::regT0); 77 jit.loadDouble(GPRInfo::regT0, FPRInfo::toRegister(i)); 78 } 79 for (unsigned i = 0; i < GPRInfo::numberOfRegisters; ++i) { 80#if USE(JSVALUE64) 81 jit.load64(buffer + i, GPRInfo::toRegister(i)); 82#else 83 jit.load32(buffer + i, GPRInfo::toRegister(i)); 84#endif 85 } 86 87 jit.jump(MacroAssembler::AbsoluteAddress(&vm->osrExitJumpDestination)); 88 89 LinkBuffer patchBuffer(*vm, &jit, GLOBAL_THUNK_ID); 90 91 patchBuffer.link(functionCall, compileOSRExit); 92 93 return FINALIZE_CODE(patchBuffer, ("DFG OSR exit generation thunk")); 94} 95 96inline void emitPointerValidation(CCallHelpers& jit, GPRReg pointerGPR) 97{ 98#if !ASSERT_DISABLED 99 CCallHelpers::Jump isNonZero = jit.branchTestPtr(CCallHelpers::NonZero, pointerGPR); 100 jit.breakpoint(); 101 isNonZero.link(&jit); 102 jit.push(pointerGPR); 103 jit.load8(pointerGPR, pointerGPR); 104 jit.pop(pointerGPR); 105#else 106 UNUSED_PARAM(jit); 107 UNUSED_PARAM(pointerGPR); 108#endif 109} 110 111MacroAssemblerCodeRef throwExceptionFromCallSlowPathGenerator(VM* vm) 112{ 113 CCallHelpers jit(vm); 114 115 // We will jump to here if the JIT code thinks it's making a call, but the 116 // linking helper (C++ code) decided to throw an exception instead. We will 117 // have saved the callReturnIndex in the first arguments of JITStackFrame. 118 // Note that the return address will be on the stack at this point, so we 119 // need to remove it and drop it on the floor, since we don't care about it. 120 // Finally note that the call frame register points at the callee frame, so 121 // we need to pop it. 122 jit.preserveReturnAddressAfterCall(GPRInfo::nonPreservedNonReturnGPR); 123 jit.loadPtr( 124 CCallHelpers::Address( 125 GPRInfo::callFrameRegister, 126 static_cast<ptrdiff_t>(sizeof(Register)) * JSStack::CallerFrame), 127 GPRInfo::callFrameRegister); 128#if USE(JSVALUE64) 129 jit.peek64(GPRInfo::nonPreservedNonReturnGPR, JITSTACKFRAME_ARGS_INDEX); 130#else 131 jit.peek(GPRInfo::nonPreservedNonReturnGPR, JITSTACKFRAME_ARGS_INDEX); 132#endif 133 jit.setupArgumentsWithExecState(GPRInfo::nonPreservedNonReturnGPR); 134 jit.move(CCallHelpers::TrustedImmPtr(bitwise_cast<void*>(lookupExceptionHandler)), GPRInfo::nonArgGPR0); 135 emitPointerValidation(jit, GPRInfo::nonArgGPR0); 136 jit.call(GPRInfo::nonArgGPR0); 137 emitPointerValidation(jit, GPRInfo::returnValueGPR2); 138 jit.jump(GPRInfo::returnValueGPR2); 139 140 LinkBuffer patchBuffer(*vm, &jit, GLOBAL_THUNK_ID); 141 return FINALIZE_CODE(patchBuffer, ("DFG throw exception from call slow path thunk")); 142} 143 144static void slowPathFor( 145 CCallHelpers& jit, VM* vm, P_DFGOperation_E slowPathFunction) 146{ 147 jit.preserveReturnAddressAfterCall(GPRInfo::nonArgGPR2); 148 emitPointerValidation(jit, GPRInfo::nonArgGPR2); 149 jit.storePtr( 150 GPRInfo::nonArgGPR2, 151 CCallHelpers::Address( 152 GPRInfo::callFrameRegister, 153 static_cast<ptrdiff_t>(sizeof(Register)) * JSStack::ReturnPC)); 154 jit.storePtr(GPRInfo::callFrameRegister, &vm->topCallFrame); 155#if USE(JSVALUE64) 156 jit.poke64(GPRInfo::nonPreservedNonReturnGPR, JITSTACKFRAME_ARGS_INDEX); 157#else 158 jit.poke(GPRInfo::nonPreservedNonReturnGPR, JITSTACKFRAME_ARGS_INDEX); 159#endif 160 jit.setupArgumentsExecState(); 161 jit.move(CCallHelpers::TrustedImmPtr(bitwise_cast<void*>(slowPathFunction)), GPRInfo::nonArgGPR0); 162 emitPointerValidation(jit, GPRInfo::nonArgGPR0); 163 jit.call(GPRInfo::nonArgGPR0); 164 165 // This slow call will return the address of one of the following: 166 // 1) Exception throwing thunk. 167 // 2) Host call return value returner thingy. 168 // 3) The function to call. 169 jit.loadPtr( 170 CCallHelpers::Address( 171 GPRInfo::callFrameRegister, 172 static_cast<ptrdiff_t>(sizeof(Register)) * JSStack::ReturnPC), 173 GPRInfo::nonPreservedNonReturnGPR); 174 jit.storePtr( 175 CCallHelpers::TrustedImmPtr(0), 176 CCallHelpers::Address( 177 GPRInfo::callFrameRegister, 178 static_cast<ptrdiff_t>(sizeof(Register)) * JSStack::ReturnPC)); 179 emitPointerValidation(jit, GPRInfo::nonPreservedNonReturnGPR); 180 jit.restoreReturnAddressBeforeReturn(GPRInfo::nonPreservedNonReturnGPR); 181 emitPointerValidation(jit, GPRInfo::returnValueGPR); 182 jit.jump(GPRInfo::returnValueGPR); 183} 184 185static MacroAssemblerCodeRef linkForThunkGenerator( 186 VM* vm, CodeSpecializationKind kind) 187{ 188 // The return address is on the stack or in the link register. We will hence 189 // save the return address to the call frame while we make a C++ function call 190 // to perform linking and lazy compilation if necessary. We expect the callee 191 // to be in nonArgGPR0/nonArgGPR1 (payload/tag), the call frame to have already 192 // been adjusted, nonPreservedNonReturnGPR holds the exception handler index, 193 // and all other registers to be available for use. We use JITStackFrame::args 194 // to save important information across calls. 195 196 CCallHelpers jit(vm); 197 198 slowPathFor(jit, vm, kind == CodeForCall ? operationLinkCall : operationLinkConstruct); 199 200 LinkBuffer patchBuffer(*vm, &jit, GLOBAL_THUNK_ID); 201 return FINALIZE_CODE( 202 patchBuffer, 203 ("DFG link %s slow path thunk", kind == CodeForCall ? "call" : "construct")); 204} 205 206MacroAssemblerCodeRef linkCallThunkGenerator(VM* vm) 207{ 208 return linkForThunkGenerator(vm, CodeForCall); 209} 210 211MacroAssemblerCodeRef linkConstructThunkGenerator(VM* vm) 212{ 213 return linkForThunkGenerator(vm, CodeForConstruct); 214} 215 216// For closure optimizations, we only include calls, since if you're using closures for 217// object construction then you're going to lose big time anyway. 218MacroAssemblerCodeRef linkClosureCallThunkGenerator(VM* vm) 219{ 220 CCallHelpers jit(vm); 221 222 slowPathFor(jit, vm, operationLinkClosureCall); 223 224 LinkBuffer patchBuffer(*vm, &jit, GLOBAL_THUNK_ID); 225 return FINALIZE_CODE(patchBuffer, ("DFG link closure call slow path thunk")); 226} 227 228static MacroAssemblerCodeRef virtualForThunkGenerator( 229 VM* vm, CodeSpecializationKind kind) 230{ 231 // The return address is on the stack, or in the link register. We will hence 232 // jump to the callee, or save the return address to the call frame while we 233 // make a C++ function call to the appropriate DFG operation. 234 235 CCallHelpers jit(vm); 236 237 CCallHelpers::JumpList slowCase; 238 239 // FIXME: we should have a story for eliminating these checks. In many cases, 240 // the DFG knows that the value is definitely a cell, or definitely a function. 241 242#if USE(JSVALUE64) 243 slowCase.append( 244 jit.branchTest64( 245 CCallHelpers::NonZero, GPRInfo::nonArgGPR0, GPRInfo::tagMaskRegister)); 246#else 247 slowCase.append( 248 jit.branch32( 249 CCallHelpers::NotEqual, GPRInfo::nonArgGPR1, 250 CCallHelpers::TrustedImm32(JSValue::CellTag))); 251#endif 252 jit.loadPtr(CCallHelpers::Address(GPRInfo::nonArgGPR0, JSCell::structureOffset()), GPRInfo::nonArgGPR2); 253 slowCase.append( 254 jit.branchPtr( 255 CCallHelpers::NotEqual, 256 CCallHelpers::Address(GPRInfo::nonArgGPR2, Structure::classInfoOffset()), 257 CCallHelpers::TrustedImmPtr(&JSFunction::s_info))); 258 259 // Now we know we have a JSFunction. 260 261 jit.loadPtr( 262 CCallHelpers::Address(GPRInfo::nonArgGPR0, JSFunction::offsetOfExecutable()), 263 GPRInfo::nonArgGPR2); 264 slowCase.append( 265 jit.branch32( 266 CCallHelpers::LessThan, 267 CCallHelpers::Address( 268 GPRInfo::nonArgGPR2, ExecutableBase::offsetOfNumParametersFor(kind)), 269 CCallHelpers::TrustedImm32(0))); 270 271 // Now we know that we have a CodeBlock, and we're committed to making a fast 272 // call. 273 274 jit.loadPtr( 275 CCallHelpers::Address(GPRInfo::nonArgGPR0, JSFunction::offsetOfScopeChain()), 276 GPRInfo::nonArgGPR1); 277#if USE(JSVALUE64) 278 jit.store64( 279 GPRInfo::nonArgGPR1, 280 CCallHelpers::Address( 281 GPRInfo::callFrameRegister, 282 static_cast<ptrdiff_t>(sizeof(Register)) * JSStack::ScopeChain)); 283#else 284 jit.storePtr( 285 GPRInfo::nonArgGPR1, 286 CCallHelpers::Address( 287 GPRInfo::callFrameRegister, 288 static_cast<ptrdiff_t>(sizeof(Register)) * JSStack::ScopeChain + 289 OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload))); 290 jit.store32( 291 CCallHelpers::TrustedImm32(JSValue::CellTag), 292 CCallHelpers::Address( 293 GPRInfo::callFrameRegister, 294 static_cast<ptrdiff_t>(sizeof(Register)) * JSStack::ScopeChain + 295 OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag))); 296#endif 297 298 jit.loadPtr( 299 CCallHelpers::Address(GPRInfo::nonArgGPR2, ExecutableBase::offsetOfJITCodeWithArityCheckFor(kind)), 300 GPRInfo::regT0); 301 302 // Make a tail call. This will return back to DFG code. 303 emitPointerValidation(jit, GPRInfo::regT0); 304 jit.jump(GPRInfo::regT0); 305 306 slowCase.link(&jit); 307 308 // Here we don't know anything, so revert to the full slow path. 309 310 slowPathFor(jit, vm, kind == CodeForCall ? operationVirtualCall : operationVirtualConstruct); 311 312 LinkBuffer patchBuffer(*vm, &jit, GLOBAL_THUNK_ID); 313 return FINALIZE_CODE( 314 patchBuffer, 315 ("DFG virtual %s slow path thunk", kind == CodeForCall ? "call" : "construct")); 316} 317 318MacroAssemblerCodeRef virtualCallThunkGenerator(VM* vm) 319{ 320 return virtualForThunkGenerator(vm, CodeForCall); 321} 322 323MacroAssemblerCodeRef virtualConstructThunkGenerator(VM* vm) 324{ 325 return virtualForThunkGenerator(vm, CodeForConstruct); 326} 327 328} } // namespace JSC::DFG 329 330#endif // ENABLE(DFG_JIT) 331