blob: 79923010f519928bd2d821fec0f0c7695d1a43e2 [file] [log] [blame]
/*
* Copyright (C) 2013, 2014 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 <JavaScriptCore/MacroAssembler.h>
#include <wtf/Deque.h>
#include <wtf/Vector.h>
namespace WebCore {
#if CPU(ARM64)
static const JSC::MacroAssembler::RegisterID callerSavedRegisters[] = {
JSC::ARM64Registers::x0,
JSC::ARM64Registers::x1,
JSC::ARM64Registers::x2,
JSC::ARM64Registers::x3,
JSC::ARM64Registers::x4,
JSC::ARM64Registers::x5,
JSC::ARM64Registers::x6,
JSC::ARM64Registers::x7,
JSC::ARM64Registers::x8,
JSC::ARM64Registers::x9,
JSC::ARM64Registers::x10,
JSC::ARM64Registers::x11,
JSC::ARM64Registers::x12,
JSC::ARM64Registers::x13,
JSC::ARM64Registers::x14,
};
static const JSC::MacroAssembler::RegisterID calleeSavedRegisters[] = {
JSC::ARM64Registers::x19
};
static const JSC::MacroAssembler::RegisterID tempRegister = JSC::ARM64Registers::x15;
#elif CPU(ARM_THUMB2)
static const JSC::MacroAssembler::RegisterID callerSavedRegisters[] {
JSC::ARMRegisters::r0,
JSC::ARMRegisters::r1,
JSC::ARMRegisters::r2,
JSC::ARMRegisters::r3,
JSC::ARMRegisters::r9,
};
static const JSC::MacroAssembler::RegisterID calleeSavedRegisters[] = {
JSC::ARMRegisters::r4,
JSC::ARMRegisters::r5,
JSC::ARMRegisters::r7,
JSC::ARMRegisters::r8,
JSC::ARMRegisters::r10,
JSC::ARMRegisters::r11,
};
// r6 is also used as addressTempRegister in the macro assembler. It is saved in the prologue and restored in the epilogue.
static const JSC::MacroAssembler::RegisterID tempRegister = JSC::ARMRegisters::r6;
#elif CPU(X86_64)
static const JSC::MacroAssembler::RegisterID callerSavedRegisters[] = {
JSC::X86Registers::eax,
JSC::X86Registers::ecx,
JSC::X86Registers::edx,
JSC::X86Registers::esi,
JSC::X86Registers::edi,
JSC::X86Registers::r8,
JSC::X86Registers::r9,
JSC::X86Registers::r10,
JSC::X86Registers::r11
};
static const JSC::MacroAssembler::RegisterID calleeSavedRegisters[] = {
JSC::X86Registers::r12,
JSC::X86Registers::r13,
JSC::X86Registers::r14,
JSC::X86Registers::r15
};
#else
#error RegisterAllocator has no defined registers for the architecture.
#endif
static const unsigned calleeSavedRegisterCount = WTF_ARRAY_LENGTH(calleeSavedRegisters);
static const unsigned maximumRegisterCount = calleeSavedRegisterCount + WTF_ARRAY_LENGTH(callerSavedRegisters);
typedef Vector<JSC::MacroAssembler::RegisterID, maximumRegisterCount> RegisterVector;
class RegisterAllocator {
public:
RegisterAllocator() { }
~RegisterAllocator();
unsigned availableRegisterCount() const { return m_registers.size(); }
JSC::MacroAssembler::RegisterID allocateRegister()
{
RELEASE_ASSERT(m_registers.size());
JSC::MacroAssembler::RegisterID registerID = m_registers.first();
m_registers.removeFirst();
ASSERT(!m_allocatedRegisters.contains(registerID));
m_allocatedRegisters.append(registerID);
return registerID;
}
void allocateRegister(JSC::MacroAssembler::RegisterID registerID)
{
for (auto it = m_registers.begin(); it != m_registers.end(); ++it) {
if (*it == registerID) {
m_registers.remove(it);
ASSERT(!m_allocatedRegisters.contains(registerID));
m_allocatedRegisters.append(registerID);
return;
}
}
RELEASE_ASSERT_NOT_REACHED();
}
JSC::MacroAssembler::RegisterID allocateRegisterWithPreference(JSC::MacroAssembler::RegisterID preferredRegister)
{
for (auto it = m_registers.begin(); it != m_registers.end(); ++it) {
if (*it == preferredRegister) {
m_registers.remove(it);
ASSERT(!m_allocatedRegisters.contains(preferredRegister));
m_allocatedRegisters.append(preferredRegister);
return preferredRegister;
}
}
return allocateRegister();
}
void deallocateRegister(JSC::MacroAssembler::RegisterID registerID)
{
ASSERT(m_allocatedRegisters.contains(registerID));
// Most allocation/deallocation happen in stack-like order. In the common case, this
// just removes the last item.
m_allocatedRegisters.remove(m_allocatedRegisters.reverseFind(registerID));
for (auto unallocatedRegister : m_registers)
RELEASE_ASSERT(unallocatedRegister != registerID);
m_registers.append(registerID);
}
unsigned reserveCallerSavedRegisters(unsigned count)
{
#ifdef NDEBUG
UNUSED_PARAM(count);
unsigned numberToAllocate = WTF_ARRAY_LENGTH(callerSavedRegisters);
#else
unsigned numberToAllocate = std::min<unsigned>(WTF_ARRAY_LENGTH(callerSavedRegisters), count);
#endif
for (unsigned i = 0; i < numberToAllocate; ++i)
m_registers.append(callerSavedRegisters[i]);
return numberToAllocate;
}
const Vector<JSC::MacroAssembler::RegisterID, calleeSavedRegisterCount>& reserveCalleeSavedRegisters(unsigned count)
{
RELEASE_ASSERT(count <= WTF_ARRAY_LENGTH(calleeSavedRegisters));
RELEASE_ASSERT(!m_reservedCalleeSavedRegisters.size());
for (unsigned i = 0; i < count; ++i) {
JSC::MacroAssembler::RegisterID registerId = calleeSavedRegisters[i];
m_reservedCalleeSavedRegisters.append(registerId);
m_registers.append(registerId);
}
return m_reservedCalleeSavedRegisters;
}
Vector<JSC::MacroAssembler::RegisterID, calleeSavedRegisterCount> restoreCalleeSavedRegisters()
{
Vector<JSC::MacroAssembler::RegisterID, calleeSavedRegisterCount> registers(m_reservedCalleeSavedRegisters);
m_reservedCalleeSavedRegisters.clear();
return registers;
}
const RegisterVector& allocatedRegisters() const { return m_allocatedRegisters; }
static bool isValidRegister(JSC::MacroAssembler::RegisterID registerID)
{
#if CPU(ARM64)
return (registerID >= JSC::ARM64Registers::x0 && registerID <= JSC::ARM64Registers::x14)
|| registerID == JSC::ARM64Registers::x19;
#elif CPU(ARM_THUMB2)
return registerID >= JSC::ARMRegisters::r0 && registerID <= JSC::ARMRegisters::r11 && registerID != JSC::ARMRegisters::r6;
#elif CPU(X86_64)
return (registerID >= JSC::X86Registers::eax && registerID <= JSC::X86Registers::edx)
|| (registerID >= JSC::X86Registers::esi && registerID <= JSC::X86Registers::r15);
#else
#error RegisterAllocator does not define the valid register range for the current architecture.
#endif
}
static bool isCallerSavedRegister(JSC::MacroAssembler::RegisterID registerID)
{
ASSERT(isValidRegister(registerID));
#if CPU(ARM64)
return registerID >= JSC::ARM64Registers::x0 && registerID <= JSC::ARM64Registers::x14;
#elif CPU(ARM_THUMB2)
return (registerID >= JSC::ARMRegisters::r0 && registerID <= JSC::ARMRegisters::r3)
|| registerID == JSC::ARMRegisters::r9;
#elif CPU(X86_64)
return (registerID >= JSC::X86Registers::eax && registerID <= JSC::X86Registers::edx)
|| (registerID >= JSC::X86Registers::esi && registerID <= JSC::X86Registers::r11);
#else
#error RegisterAllocator does not define the valid caller saved register range for the current architecture.
#endif
}
private:
Deque<JSC::MacroAssembler::RegisterID, maximumRegisterCount> m_registers;
RegisterVector m_allocatedRegisters;
Vector<JSC::MacroAssembler::RegisterID, calleeSavedRegisterCount> m_reservedCalleeSavedRegisters;
};
class LocalRegister {
public:
explicit LocalRegister(RegisterAllocator& allocator)
: m_allocator(allocator)
, m_register(allocator.allocateRegister())
{
}
~LocalRegister()
{
m_allocator.deallocateRegister(m_register);
}
operator JSC::MacroAssembler::RegisterID() const
{
return m_register;
}
protected:
explicit LocalRegister(RegisterAllocator& allocator, JSC::MacroAssembler::RegisterID registerID)
: m_allocator(allocator)
, m_register(registerID)
{
}
RegisterAllocator& m_allocator;
JSC::MacroAssembler::RegisterID m_register;
};
class LocalRegisterWithPreference : public LocalRegister {
public:
explicit LocalRegisterWithPreference(RegisterAllocator& allocator, JSC::MacroAssembler::RegisterID preferredRegister)
: LocalRegister(allocator, allocator.allocateRegisterWithPreference(preferredRegister))
{
}
};
inline RegisterAllocator::~RegisterAllocator()
{
RELEASE_ASSERT(m_reservedCalleeSavedRegisters.isEmpty());
}
} // namespace WebCore
#endif // ENABLE(CSS_SELECTOR_JIT)