| /* |
| * Copyright (C) 2015 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "FTLJSCallVarargs.h" |
| |
| #if ENABLE(FTL_JIT) |
| |
| #include "DFGNode.h" |
| #include "DFGOperations.h" |
| #include "JSCInlines.h" |
| #include "LinkBuffer.h" |
| #include "ScratchRegisterAllocator.h" |
| #include "SetupVarargsFrame.h" |
| |
| namespace JSC { namespace FTL { |
| |
| using namespace DFG; |
| |
| JSCallVarargs::JSCallVarargs() |
| : m_stackmapID(UINT_MAX) |
| , m_node(nullptr) |
| , m_instructionOffset(UINT_MAX) |
| { |
| } |
| |
| JSCallVarargs::JSCallVarargs(unsigned stackmapID, Node* node) |
| : m_stackmapID(stackmapID) |
| , m_node(node) |
| , m_callBase( |
| (node->op() == ConstructVarargs || node->op() == ConstructForwardVarargs) |
| ? CallLinkInfo::ConstructVarargs : CallLinkInfo::CallVarargs, |
| node->origin.semantic) |
| , m_instructionOffset(0) |
| { |
| ASSERT( |
| node->op() == CallVarargs || node->op() == CallForwardVarargs |
| || node->op() == ConstructVarargs || node->op() == ConstructForwardVarargs); |
| } |
| |
| unsigned JSCallVarargs::numSpillSlotsNeeded() |
| { |
| return 4; |
| } |
| |
| void JSCallVarargs::emit(CCallHelpers& jit, int32_t spillSlotsOffset) |
| { |
| // We are passed three pieces of information: |
| // - The callee. |
| // - The arguments object, if it's not a forwarding call. |
| // - The "this" value, if it's a constructor call. |
| |
| CallVarargsData* data = m_node->callVarargsData(); |
| |
| GPRReg calleeGPR = GPRInfo::argumentGPR0; |
| |
| GPRReg argumentsGPR = InvalidGPRReg; |
| GPRReg thisGPR = InvalidGPRReg; |
| |
| bool forwarding = false; |
| |
| switch (m_node->op()) { |
| case CallVarargs: |
| case ConstructVarargs: |
| argumentsGPR = GPRInfo::argumentGPR1; |
| thisGPR = GPRInfo::argumentGPR2; |
| break; |
| case CallForwardVarargs: |
| case ConstructForwardVarargs: |
| thisGPR = GPRInfo::argumentGPR1; |
| forwarding = true; |
| break; |
| default: |
| RELEASE_ASSERT_NOT_REACHED(); |
| break; |
| } |
| |
| const unsigned calleeSpillSlot = 0; |
| const unsigned argumentsSpillSlot = 1; |
| const unsigned thisSpillSlot = 2; |
| const unsigned stackPointerSpillSlot = 3; |
| |
| // Get some scratch registers. |
| RegisterSet usedRegisters; |
| usedRegisters.merge(RegisterSet::stackRegisters()); |
| usedRegisters.merge(RegisterSet::reservedHardwareRegisters()); |
| usedRegisters.merge(RegisterSet::calleeSaveRegisters()); |
| usedRegisters.set(calleeGPR); |
| if (argumentsGPR != InvalidGPRReg) |
| usedRegisters.set(argumentsGPR); |
| ASSERT(thisGPR); |
| usedRegisters.set(thisGPR); |
| ScratchRegisterAllocator allocator(usedRegisters); |
| GPRReg scratchGPR1 = allocator.allocateScratchGPR(); |
| GPRReg scratchGPR2 = allocator.allocateScratchGPR(); |
| GPRReg scratchGPR3 = allocator.allocateScratchGPR(); |
| |
| RELEASE_ASSERT(!allocator.numberOfReusedRegisters()); |
| |
| auto computeUsedStack = [&] (GPRReg targetGPR, unsigned extra) { |
| if (isARM64()) { |
| // Have to do this the weird way because $sp on ARM64 means zero when used in a subtraction. |
| jit.move(CCallHelpers::stackPointerRegister, targetGPR); |
| jit.negPtr(targetGPR); |
| jit.addPtr(GPRInfo::callFrameRegister, targetGPR); |
| } else { |
| jit.move(GPRInfo::callFrameRegister, targetGPR); |
| jit.subPtr(CCallHelpers::stackPointerRegister, targetGPR); |
| } |
| if (extra) |
| jit.subPtr(CCallHelpers::TrustedImm32(extra), targetGPR); |
| jit.urshiftPtr(CCallHelpers::Imm32(3), targetGPR); |
| }; |
| |
| auto callWithExceptionCheck = [&] (void* callee) { |
| jit.move(CCallHelpers::TrustedImmPtr(callee), GPRInfo::nonPreservedNonArgumentGPR); |
| jit.call(GPRInfo::nonPreservedNonArgumentGPR); |
| m_exceptions.append(jit.emitExceptionCheck(AssemblyHelpers::NormalExceptionCheck, AssemblyHelpers::FarJumpWidth)); |
| }; |
| |
| if (isARM64()) { |
| jit.move(CCallHelpers::stackPointerRegister, scratchGPR1); |
| jit.storePtr(scratchGPR1, CCallHelpers::addressFor(spillSlotsOffset + stackPointerSpillSlot)); |
| } else |
| jit.storePtr(CCallHelpers::stackPointerRegister, CCallHelpers::addressFor(spillSlotsOffset + stackPointerSpillSlot)); |
| |
| unsigned extraStack = sizeof(CallerFrameAndPC) + |
| WTF::roundUpToMultipleOf(stackAlignmentBytes(), 5 * sizeof(void*)); |
| |
| if (forwarding) { |
| CCallHelpers::JumpList slowCase; |
| computeUsedStack(scratchGPR2, 0); |
| emitSetupVarargsFrameFastCase(jit, scratchGPR2, scratchGPR1, scratchGPR2, scratchGPR3, m_node->child2()->origin.semantic.inlineCallFrame, data->firstVarArgOffset, slowCase); |
| |
| CCallHelpers::Jump done = jit.jump(); |
| slowCase.link(&jit); |
| jit.subPtr(CCallHelpers::TrustedImm32(extraStack), CCallHelpers::stackPointerRegister); |
| jit.setupArgumentsExecState(); |
| callWithExceptionCheck(bitwise_cast<void*>(operationThrowStackOverflowForVarargs)); |
| jit.abortWithReason(DFGVarargsThrowingPathDidNotThrow); |
| |
| done.link(&jit); |
| jit.move(calleeGPR, GPRInfo::regT0); |
| } else { |
| // Gotta spill the callee, arguments, and this because we will need them later and we will have some |
| // calls that clobber them. |
| jit.store64(calleeGPR, CCallHelpers::addressFor(spillSlotsOffset + calleeSpillSlot)); |
| jit.store64(argumentsGPR, CCallHelpers::addressFor(spillSlotsOffset + argumentsSpillSlot)); |
| jit.store64(thisGPR, CCallHelpers::addressFor(spillSlotsOffset + thisSpillSlot)); |
| |
| computeUsedStack(scratchGPR1, 0); |
| jit.subPtr(CCallHelpers::TrustedImm32(extraStack), CCallHelpers::stackPointerRegister); |
| jit.setupArgumentsWithExecState(argumentsGPR, scratchGPR1, CCallHelpers::TrustedImm32(data->firstVarArgOffset)); |
| callWithExceptionCheck(bitwise_cast<void*>(operationSizeFrameForVarargs)); |
| |
| jit.move(GPRInfo::returnValueGPR, scratchGPR1); |
| computeUsedStack(scratchGPR2, extraStack); |
| jit.load64(CCallHelpers::addressFor(spillSlotsOffset + argumentsSpillSlot), argumentsGPR); |
| emitSetVarargsFrame(jit, scratchGPR1, false, scratchGPR2, scratchGPR2); |
| jit.addPtr(CCallHelpers::TrustedImm32(-extraStack), scratchGPR2, CCallHelpers::stackPointerRegister); |
| jit.setupArgumentsWithExecState(scratchGPR2, argumentsGPR, CCallHelpers::TrustedImm32(data->firstVarArgOffset), scratchGPR1); |
| callWithExceptionCheck(bitwise_cast<void*>(operationSetupVarargsFrame)); |
| |
| jit.move(GPRInfo::returnValueGPR, scratchGPR2); |
| |
| jit.load64(CCallHelpers::addressFor(spillSlotsOffset + thisSpillSlot), thisGPR); |
| jit.load64(CCallHelpers::addressFor(spillSlotsOffset + calleeSpillSlot), GPRInfo::regT0); |
| } |
| |
| jit.addPtr(CCallHelpers::TrustedImm32(sizeof(CallerFrameAndPC)), scratchGPR2, CCallHelpers::stackPointerRegister); |
| |
| jit.store64(thisGPR, CCallHelpers::calleeArgumentSlot(0)); |
| |
| // Henceforth we make the call. The base FTL call machinery expects the callee in regT0 and for the |
| // stack frame to already be set up, which it is. |
| jit.store64(GPRInfo::regT0, CCallHelpers::calleeFrameSlot(JSStack::Callee)); |
| |
| m_callBase.emit(jit); |
| |
| // Undo the damage we've done. |
| if (isARM64()) { |
| GPRReg scratchGPRAtReturn = CCallHelpers::selectScratchGPR(GPRInfo::returnValueGPR); |
| jit.loadPtr(CCallHelpers::addressFor(spillSlotsOffset + stackPointerSpillSlot), scratchGPRAtReturn); |
| jit.move(scratchGPRAtReturn, CCallHelpers::stackPointerRegister); |
| } else |
| jit.loadPtr(CCallHelpers::addressFor(spillSlotsOffset + stackPointerSpillSlot), CCallHelpers::stackPointerRegister); |
| } |
| |
| void JSCallVarargs::link(VM& vm, LinkBuffer& linkBuffer, CodeLocationLabel exceptionHandler) |
| { |
| m_callBase.link(vm, linkBuffer); |
| linkBuffer.link(m_exceptions, exceptionHandler); |
| } |
| |
| } } // namespace JSC::FTL |
| |
| #endif // ENABLE(FTL_JIT) |
| |