--- /dev/null
+/*
+ * 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)
+