278 lines
10 KiB
C++
278 lines
10 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 "RegisterAllocator.h"
|
|
#include <JavaScriptCore/MacroAssembler.h>
|
|
#include <limits>
|
|
|
|
namespace WebCore {
|
|
|
|
class StackAllocator {
|
|
public:
|
|
class StackReference {
|
|
public:
|
|
StackReference()
|
|
: m_offsetFromTop(std::numeric_limits<unsigned>::max())
|
|
{ }
|
|
explicit StackReference(unsigned offset)
|
|
: m_offsetFromTop(offset)
|
|
{ }
|
|
operator unsigned() const { return m_offsetFromTop; }
|
|
bool isValid() const { return m_offsetFromTop != std::numeric_limits<unsigned>::max(); }
|
|
private:
|
|
unsigned m_offsetFromTop;
|
|
};
|
|
|
|
typedef Vector<StackReference, maximumRegisterCount> StackReferenceVector;
|
|
|
|
StackAllocator(JSC::MacroAssembler& assembler)
|
|
: m_assembler(assembler)
|
|
, m_offsetFromTop(0)
|
|
, m_hasFunctionCallPadding(false)
|
|
{
|
|
}
|
|
|
|
StackAllocator(const StackAllocator&) = default;
|
|
|
|
StackReference stackTop()
|
|
{
|
|
return StackReference(m_offsetFromTop + stackUnitInBytes());
|
|
}
|
|
|
|
~StackAllocator()
|
|
{
|
|
RELEASE_ASSERT(!m_offsetFromTop);
|
|
RELEASE_ASSERT(!m_hasFunctionCallPadding);
|
|
}
|
|
|
|
StackReference allocateUninitialized()
|
|
{
|
|
return allocateUninitialized(1)[0];
|
|
}
|
|
|
|
StackReferenceVector allocateUninitialized(unsigned count)
|
|
{
|
|
RELEASE_ASSERT(!m_hasFunctionCallPadding);
|
|
StackReferenceVector stackReferences;
|
|
unsigned oldOffsetFromTop = m_offsetFromTop;
|
|
#if CPU(ARM64)
|
|
for (unsigned i = 0; i < count - 1; i += 2) {
|
|
m_offsetFromTop += stackUnitInBytes();
|
|
stackReferences.append(StackReference(m_offsetFromTop - stackUnitInBytes() / 2));
|
|
stackReferences.append(StackReference(m_offsetFromTop));
|
|
}
|
|
if (count % 2) {
|
|
m_offsetFromTop += stackUnitInBytes();
|
|
stackReferences.append(StackReference(m_offsetFromTop));
|
|
}
|
|
#else
|
|
for (unsigned i = 0; i < count; ++i) {
|
|
m_offsetFromTop += stackUnitInBytes();
|
|
stackReferences.append(StackReference(m_offsetFromTop));
|
|
}
|
|
#endif
|
|
m_assembler.addPtrNoFlags(JSC::MacroAssembler::TrustedImm32(-(m_offsetFromTop - oldOffsetFromTop)), JSC::MacroAssembler::stackPointerRegister);
|
|
return stackReferences;
|
|
}
|
|
|
|
template<size_t inlineCapacity, typename OverflowHandler>
|
|
StackReferenceVector push(const Vector<JSC::MacroAssembler::RegisterID, inlineCapacity, OverflowHandler>& registerIDs)
|
|
{
|
|
RELEASE_ASSERT(!m_hasFunctionCallPadding);
|
|
|
|
StackReferenceVector stackReferences;
|
|
|
|
if (registerIDs.isEmpty())
|
|
return stackReferences;
|
|
|
|
#if CPU(ARM64)
|
|
unsigned pushRegisterCount = registerIDs.size();
|
|
for (unsigned i = 0; i < pushRegisterCount - 1; i += 2) {
|
|
m_assembler.pushPair(registerIDs[i + 1], registerIDs[i]);
|
|
m_offsetFromTop += stackUnitInBytes();
|
|
stackReferences.append(StackReference(m_offsetFromTop - stackUnitInBytes() / 2));
|
|
stackReferences.append(StackReference(m_offsetFromTop));
|
|
}
|
|
if (pushRegisterCount % 2)
|
|
stackReferences.append(push(registerIDs[pushRegisterCount - 1]));
|
|
#else
|
|
for (auto registerID : registerIDs)
|
|
stackReferences.append(push(registerID));
|
|
#endif
|
|
return stackReferences;
|
|
}
|
|
|
|
StackReference push(JSC::MacroAssembler::RegisterID registerID)
|
|
{
|
|
RELEASE_ASSERT(!m_hasFunctionCallPadding);
|
|
m_assembler.pushToSave(registerID);
|
|
m_offsetFromTop += stackUnitInBytes();
|
|
return StackReference(m_offsetFromTop);
|
|
}
|
|
|
|
template<size_t inlineCapacity, typename OverflowHandler>
|
|
void pop(const StackReferenceVector& stackReferences, const Vector<JSC::MacroAssembler::RegisterID, inlineCapacity, OverflowHandler>& registerIDs)
|
|
{
|
|
RELEASE_ASSERT(!m_hasFunctionCallPadding);
|
|
|
|
unsigned popRegisterCount = registerIDs.size();
|
|
RELEASE_ASSERT(stackReferences.size() == popRegisterCount);
|
|
#if CPU(ARM64)
|
|
ASSERT(m_offsetFromTop >= stackUnitInBytes() * ((popRegisterCount + 1) / 2));
|
|
unsigned popRegisterCountOdd = popRegisterCount % 2;
|
|
if (popRegisterCountOdd)
|
|
pop(stackReferences[popRegisterCount - 1], registerIDs[popRegisterCount - 1]);
|
|
for (unsigned i = popRegisterCount - popRegisterCountOdd; i > 0; i -= 2) {
|
|
RELEASE_ASSERT(stackReferences[i - 1] == m_offsetFromTop);
|
|
RELEASE_ASSERT(stackReferences[i - 2] == m_offsetFromTop - stackUnitInBytes() / 2);
|
|
RELEASE_ASSERT(m_offsetFromTop >= stackUnitInBytes());
|
|
m_offsetFromTop -= stackUnitInBytes();
|
|
m_assembler.popPair(registerIDs[i - 1], registerIDs[i - 2]);
|
|
}
|
|
#else
|
|
ASSERT(m_offsetFromTop >= stackUnitInBytes() * popRegisterCount);
|
|
for (unsigned i = popRegisterCount; i > 0; --i)
|
|
pop(stackReferences[i - 1], registerIDs[i - 1]);
|
|
#endif
|
|
}
|
|
|
|
void pop(StackReference stackReference, JSC::MacroAssembler::RegisterID registerID)
|
|
{
|
|
RELEASE_ASSERT(stackReference == m_offsetFromTop);
|
|
RELEASE_ASSERT(!m_hasFunctionCallPadding);
|
|
RELEASE_ASSERT(m_offsetFromTop >= stackUnitInBytes());
|
|
m_offsetFromTop -= stackUnitInBytes();
|
|
m_assembler.popToRestore(registerID);
|
|
}
|
|
|
|
void popAndDiscardUpTo(StackReference stackReference)
|
|
{
|
|
unsigned positionBeforeStackReference = stackReference - stackUnitInBytes();
|
|
RELEASE_ASSERT(positionBeforeStackReference < m_offsetFromTop);
|
|
|
|
unsigned stackDelta = m_offsetFromTop - positionBeforeStackReference;
|
|
m_assembler.addPtr(JSC::MacroAssembler::TrustedImm32(stackDelta), JSC::MacroAssembler::stackPointerRegister);
|
|
m_offsetFromTop -= stackDelta;
|
|
}
|
|
|
|
void alignStackPreFunctionCall()
|
|
{
|
|
#if CPU(X86_64)
|
|
RELEASE_ASSERT(!m_hasFunctionCallPadding);
|
|
unsigned topAlignment = stackUnitInBytes();
|
|
if ((topAlignment + m_offsetFromTop) % 16) {
|
|
m_hasFunctionCallPadding = true;
|
|
m_assembler.addPtrNoFlags(JSC::MacroAssembler::TrustedImm32(-stackUnitInBytes()), JSC::MacroAssembler::stackPointerRegister);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void unalignStackPostFunctionCall()
|
|
{
|
|
#if CPU(X86_64)
|
|
if (m_hasFunctionCallPadding) {
|
|
m_assembler.addPtrNoFlags(JSC::MacroAssembler::TrustedImm32(stackUnitInBytes()), JSC::MacroAssembler::stackPointerRegister);
|
|
m_hasFunctionCallPadding = false;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void merge(StackAllocator&& stackA, StackAllocator&& stackB)
|
|
{
|
|
RELEASE_ASSERT(stackA.m_offsetFromTop == stackB.m_offsetFromTop);
|
|
RELEASE_ASSERT(stackA.m_hasFunctionCallPadding == stackB.m_hasFunctionCallPadding);
|
|
ASSERT(&stackA.m_assembler == &stackB.m_assembler);
|
|
ASSERT(&m_assembler == &stackB.m_assembler);
|
|
|
|
m_offsetFromTop = stackA.m_offsetFromTop;
|
|
m_hasFunctionCallPadding = stackA.m_hasFunctionCallPadding;
|
|
|
|
stackA.reset();
|
|
stackB.reset();
|
|
}
|
|
|
|
void merge(StackAllocator&& stackA, StackAllocator&& stackB, StackAllocator&& stackC)
|
|
{
|
|
RELEASE_ASSERT(stackA.m_offsetFromTop == stackB.m_offsetFromTop);
|
|
RELEASE_ASSERT(stackA.m_offsetFromTop == stackC.m_offsetFromTop);
|
|
RELEASE_ASSERT(stackA.m_hasFunctionCallPadding == stackB.m_hasFunctionCallPadding);
|
|
RELEASE_ASSERT(stackA.m_hasFunctionCallPadding == stackC.m_hasFunctionCallPadding);
|
|
ASSERT(&stackA.m_assembler == &stackB.m_assembler);
|
|
ASSERT(&stackA.m_assembler == &stackC.m_assembler);
|
|
ASSERT(&m_assembler == &stackB.m_assembler);
|
|
|
|
m_offsetFromTop = stackA.m_offsetFromTop;
|
|
m_hasFunctionCallPadding = stackA.m_hasFunctionCallPadding;
|
|
|
|
stackA.reset();
|
|
stackB.reset();
|
|
stackC.reset();
|
|
}
|
|
|
|
JSC::MacroAssembler::Address addressOf(StackReference stackReference)
|
|
{
|
|
return JSC::MacroAssembler::Address(JSC::MacroAssembler::stackPointerRegister, offsetToStackReference(stackReference));
|
|
}
|
|
|
|
StackAllocator& operator=(const StackAllocator& other)
|
|
{
|
|
RELEASE_ASSERT(&m_assembler == &other.m_assembler);
|
|
m_offsetFromTop = other.m_offsetFromTop;
|
|
m_hasFunctionCallPadding = other.m_hasFunctionCallPadding;
|
|
return *this;
|
|
}
|
|
|
|
|
|
private:
|
|
static unsigned stackUnitInBytes()
|
|
{
|
|
return JSC::MacroAssembler::pushToSaveByteOffset();
|
|
}
|
|
|
|
unsigned offsetToStackReference(StackReference stackReference)
|
|
{
|
|
RELEASE_ASSERT(m_offsetFromTop >= stackReference);
|
|
return m_offsetFromTop - stackReference;
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
m_offsetFromTop = 0;
|
|
m_hasFunctionCallPadding = false;
|
|
}
|
|
|
|
JSC::MacroAssembler& m_assembler;
|
|
unsigned m_offsetFromTop;
|
|
bool m_hasFunctionCallPadding;
|
|
};
|
|
|
|
} // namespace WebCore
|
|
|
|
#endif // ENABLE(CSS_SELECTOR_JIT)
|