blob: 16421e8b242d0805387de2f5632d03a328ba1507 [file] [log] [blame]
/*
* Copyright (C) 2013-2018 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. AND ITS CONTRIBUTORS ``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 ITS 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.
*/
#pragma once
#if ENABLE(CSS_SELECTOR_JIT)
#include "CSSPtrTag.h"
#include "RegisterAllocator.h"
#include "StackAllocator.h"
#include <JavaScriptCore/GPRInfo.h>
#include <JavaScriptCore/MacroAssembler.h>
namespace WebCore {
class FunctionCall {
public:
FunctionCall(JSC::MacroAssembler& assembler, RegisterAllocator& registerAllocator, StackAllocator& stackAllocator, Vector<std::pair<JSC::MacroAssembler::Call, JSC::FunctionPtr<CSSOperationPtrTag>>, 32>& callRegistry)
: m_assembler(assembler)
, m_registerAllocator(registerAllocator)
, m_stackAllocator(stackAllocator)
, m_callRegistry(callRegistry)
, m_argumentCount(0)
, m_firstArgument(JSC::InvalidGPRReg)
, m_secondArgument(JSC::InvalidGPRReg)
{
}
void setFunctionAddress(JSC::FunctionPtr<CSSOperationPtrTag> functionAddress)
{
m_functionAddress = functionAddress;
}
void setOneArgument(const JSC::MacroAssembler::RegisterID& registerID)
{
m_argumentCount = 1;
m_firstArgument = registerID;
}
void setTwoArguments(const JSC::MacroAssembler::RegisterID& firstRegisterID, const JSC::MacroAssembler::RegisterID& secondRegisterID)
{
m_argumentCount = 2;
m_firstArgument = firstRegisterID;
m_secondArgument = secondRegisterID;
}
void call()
{
prepareAndCall();
cleanupPostCall();
}
JSC::MacroAssembler::Jump callAndBranchOnBooleanReturnValue(JSC::MacroAssembler::ResultCondition condition)
{
#if CPU(X86_64)
return callAndBranchOnCondition(condition, JSC::MacroAssembler::TrustedImm32(0xff));
#elif CPU(ARM64) || CPU(ARM)
return callAndBranchOnCondition(condition, JSC::MacroAssembler::TrustedImm32(-1));
#else
#error Missing implementationg for matching boolean return values.
#endif
}
private:
JSC::MacroAssembler::Jump callAndBranchOnCondition(JSC::MacroAssembler::ResultCondition condition, JSC::MacroAssembler::TrustedImm32 mask)
{
prepareAndCall();
m_assembler.test32(JSC::GPRInfo::returnValueGPR, mask);
cleanupPostCall();
return m_assembler.branch(condition);
}
void swapArguments()
{
JSC::MacroAssembler::RegisterID a = m_firstArgument;
JSC::MacroAssembler::RegisterID b = m_secondArgument;
// x86 can swap without a temporary register. On other architectures, we need allocate a temporary register to switch the values.
#if CPU(X86_64)
m_assembler.swap(a, b);
#elif CPU(ARM64) || CPU(ARM_THUMB2)
m_assembler.move(a, tempRegister);
m_assembler.move(b, a);
m_assembler.move(tempRegister, b);
#else
#error Missing implementationg for matching swapping argument registers.
#endif
}
void prepareAndCall()
{
ASSERT(m_functionAddress.executableAddress());
ASSERT(!m_firstArgument || (m_firstArgument && !m_secondArgument) || (m_firstArgument && m_secondArgument));
saveAllocatedCallerSavedRegisters();
m_stackAllocator.alignStackPreFunctionCall();
if (m_argumentCount == 2) {
RELEASE_ASSERT(RegisterAllocator::isValidRegister(m_firstArgument));
RELEASE_ASSERT(RegisterAllocator::isValidRegister(m_secondArgument));
if (m_firstArgument != JSC::GPRInfo::argumentGPR0) {
// If firstArgument is not in argumentGPR0, we need to handle potential conflicts:
// -if secondArgument and firstArgument are in inversted registers, just swap the values.
// -if secondArgument is in argumentGPR0 but firstArgument is not taking argumentGPR1, we can move in order secondArgument->argumentGPR1, firstArgument->argumentGPR0
// -if secondArgument does not take argumentGPR0, firstArgument and secondArgument can be moved safely to destination.
if (m_secondArgument == JSC::GPRInfo::argumentGPR0) {
if (m_firstArgument == JSC::GPRInfo::argumentGPR1)
swapArguments();
else {
m_assembler.move(JSC::GPRInfo::argumentGPR0, JSC::GPRInfo::argumentGPR1);
m_assembler.move(m_firstArgument, JSC::GPRInfo::argumentGPR0);
}
} else {
m_assembler.move(m_firstArgument, JSC::GPRInfo::argumentGPR0);
if (m_secondArgument != JSC::GPRInfo::argumentGPR1)
m_assembler.move(m_secondArgument, JSC::GPRInfo::argumentGPR1);
}
} else {
// We know firstArgument is already in place, we can safely move secondArgument.
if (m_secondArgument != JSC::GPRInfo::argumentGPR1)
m_assembler.move(m_secondArgument, JSC::GPRInfo::argumentGPR1);
}
} else if (m_argumentCount == 1) {
RELEASE_ASSERT(RegisterAllocator::isValidRegister(m_firstArgument));
if (m_firstArgument != JSC::GPRInfo::argumentGPR0)
m_assembler.move(m_firstArgument, JSC::GPRInfo::argumentGPR0);
}
JSC::MacroAssembler::Call call = m_assembler.call(CSSOperationPtrTag);
m_callRegistry.append(std::make_pair(call, m_functionAddress));
}
void cleanupPostCall()
{
m_stackAllocator.unalignStackPostFunctionCall();
restoreAllocatedCallerSavedRegisters();
}
void saveAllocatedCallerSavedRegisters()
{
ASSERT(m_savedRegisterStackReferences.isEmpty());
ASSERT(m_savedRegisters.isEmpty());
const RegisterVector& allocatedRegisters = m_registerAllocator.allocatedRegisters();
for (auto registerID : allocatedRegisters) {
if (RegisterAllocator::isCallerSavedRegister(registerID))
m_savedRegisters.append(registerID);
}
m_savedRegisterStackReferences = m_stackAllocator.push(m_savedRegisters);
}
void restoreAllocatedCallerSavedRegisters()
{
m_stackAllocator.pop(m_savedRegisterStackReferences, m_savedRegisters);
m_savedRegisterStackReferences.clear();
}
JSC::MacroAssembler& m_assembler;
RegisterAllocator& m_registerAllocator;
StackAllocator& m_stackAllocator;
Vector<std::pair<JSC::MacroAssembler::Call, JSC::FunctionPtr<CSSOperationPtrTag>>, 32>& m_callRegistry;
RegisterVector m_savedRegisters;
StackAllocator::StackReferenceVector m_savedRegisterStackReferences;
JSC::FunctionPtr<CSSOperationPtrTag> m_functionAddress;
unsigned m_argumentCount;
JSC::MacroAssembler::RegisterID m_firstArgument;
JSC::MacroAssembler::RegisterID m_secondArgument;
};
} // namespace WebCore
#endif // ENABLE(CSS_SELECTOR_JIT)