271 lines
9.8 KiB
C++
271 lines
9.8 KiB
C++
/*
|
|
* 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)
|