blob: 09ab8ad1cbb99812c2fc96ce68565fbffaef7136 [file] [log] [blame]
/*
* Copyright (C) 2013-2021 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.
*/
#pragma once
#if ENABLE(FTL_JIT)
#include "MacroAssemblerCodeRef.h"
#include "RegisterSet.h"
namespace JSC { namespace FTL {
// This is used for creating some sanity in slow-path calls out of the FTL's inline
// caches. The idea is that we don't want all of the register save/restore stuff to
// be generated at each IC site. Instead, the IC slow path call site will just save
// the registers needed for the arguments. It will arrange for there to be enough
// space on top of stack to save the remaining registers and the return PC. Then it
// will call a shared thunk that will save the remaining registers. That thunk needs
// to know the stack offset at which things get saved along with the call target.
// Note that the offset is *not including* the return PC that would be pushed on X86.
class SlowPathCallKey {
public:
// Keep it within 2 bits.
enum class Type : uint8_t {
// For HashTables.
Empty,
Deleted,
Direct,
Indirect,
};
SlowPathCallKey() = default;
SlowPathCallKey(const RegisterSet& set, FunctionPtr<CFunctionPtrTag> callTarget, uint8_t numberOfUsedArgumentRegistersIfClobberingCheckIsEnabled, size_t offset, int32_t indirectOffset)
: m_numberOfUsedArgumentRegistersIfClobberingCheckIsEnabled(numberOfUsedArgumentRegistersIfClobberingCheckIsEnabled)
, m_offset(offset)
, m_usedRegisters(set)
{
if (callTarget) {
m_type = static_cast<unsigned>(Type::Direct);
ASSERT(Type::Direct == this->type());
m_callTarget = callTarget.retagged<OperationPtrTag>();
ASSERT(!indirectOffset);
} else {
m_type = static_cast<unsigned>(Type::Indirect);
ASSERT(Type::Indirect == this->type());
m_indirectOffset = indirectOffset;
}
ASSERT(offset == m_offset);
}
FunctionPtr<OperationPtrTag> callTarget() const
{
if (type() == Type::Direct)
return m_callTarget;
return nullptr;
}
size_t offset() const { return m_offset; }
const RegisterSet& usedRegisters() const { return m_usedRegisters; }
RegisterSet argumentRegistersIfClobberingCheckIsEnabled() const
{
RELEASE_ASSERT(Options::clobberAllRegsInFTLICSlowPath());
RegisterSet argumentRegisters;
for (uint8_t i = 0; i < numberOfUsedArgumentRegistersIfClobberingCheckIsEnabled(); ++i)
argumentRegisters.set(GPRInfo::toArgumentRegister(i));
return argumentRegisters;
}
int32_t indirectOffset() const
{
if (type() == Type::Indirect)
return m_indirectOffset;
return 0;
}
SlowPathCallKey withCallTarget(FunctionPtr<CFunctionPtrTag> callTarget)
{
return SlowPathCallKey(usedRegisters(), callTarget, numberOfUsedArgumentRegistersIfClobberingCheckIsEnabled(), offset(), indirectOffset());
}
void dump(PrintStream&) const;
enum EmptyValueTag { EmptyValue };
enum DeletedValueTag { DeletedValue };
SlowPathCallKey(EmptyValueTag)
: m_type(static_cast<unsigned>(Type::Empty))
{
ASSERT(Type::Empty == this->type());
}
SlowPathCallKey(DeletedValueTag)
: m_type(static_cast<unsigned>(Type::Deleted))
{
ASSERT(Type::Deleted == this->type());
}
bool isEmptyValue() const { return type() == Type::Empty; }
bool isDeletedValue() const { return type() == Type::Deleted; }
bool operator==(const SlowPathCallKey& other) const
{
return m_offset == other.m_offset
&& m_usedRegisters == other.m_usedRegisters
&& numberOfUsedArgumentRegistersIfClobberingCheckIsEnabled() == other.numberOfUsedArgumentRegistersIfClobberingCheckIsEnabled()
&& type() == other.type()
&& callTarget() == other.callTarget()
&& indirectOffset() == other.indirectOffset();
}
unsigned hash() const
{
// m_numberOfUsedArgumentRegistersIfClobberingCheckIsEnabled is intentionally not included because it will always be 0
// unless Options::clobberAllRegsInFTLICSlowPath() is set, and Options::clobberAllRegsInFTLICSlowPath() is only set in debugging use cases.
return PtrHash<void*>::hash(callTarget().executableAddress()) + m_offset + m_usedRegisters.hash() + indirectOffset() + static_cast<unsigned>(type());
}
private:
static_assert(NUMBER_OF_ARGUMENT_REGISTERS <= std::numeric_limits<uint8_t>::max());
uint8_t numberOfUsedArgumentRegistersIfClobberingCheckIsEnabled() const { return m_numberOfUsedArgumentRegistersIfClobberingCheckIsEnabled; }
Type type() const { return static_cast<Type>(m_type); }
union {
FunctionPtr<OperationPtrTag> m_callTarget { };
int32_t m_indirectOffset;
};
size_t m_numberOfUsedArgumentRegistersIfClobberingCheckIsEnabled : 8 { 0 };
size_t m_type : 2 { static_cast<size_t>(Type::Empty) };
size_t m_offset : 54 { 0 };
RegisterSet m_usedRegisters;
};
struct SlowPathCallKeyHash {
static unsigned hash(const SlowPathCallKey& key) { return key.hash(); }
static bool equal(const SlowPathCallKey& a, const SlowPathCallKey& b) { return a == b; }
static constexpr bool safeToCompareToEmptyOrDeleted = false;
};
} } // namespace JSC::FTL
namespace WTF {
template<typename T> struct DefaultHash;
template<> struct DefaultHash<JSC::FTL::SlowPathCallKey> : JSC::FTL::SlowPathCallKeyHash { };
template<typename T> struct HashTraits;
template<> struct HashTraits<JSC::FTL::SlowPathCallKey> : public CustomHashTraits<JSC::FTL::SlowPathCallKey> { };
} // namespace WTF
#endif // ENABLE(FTL_JIT)