blob: dc7ffe248cdbeee9cb50159fbf3d25ea3b38d369 [file] [log] [blame]
/*
* Copyright (C) 2013-2023 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(C_LOOP)
#include <JavaScriptCore/FPRInfo.h>
#include <JavaScriptCore/GPRInfo.h>
#include <JavaScriptCore/MacroAssembler.h>
#include <JavaScriptCore/MemoryMode.h>
#include <JavaScriptCore/Reg.h>
#include <JavaScriptCore/Width.h>
#include <wtf/BitSet.h>
#include <wtf/CommaPrinter.h>
namespace JSC {
class ScalarRegisterSet;
using RegisterBitSet = WTF::BitSet<MacroAssembler::numGPRs + MacroAssembler::numFPRs>;
class RegisterAtOffsetList;
struct RegisterSetBuilderHash;
class RegisterSet;
enum IgnoreVectorsTag { IgnoreVectors };
class RegisterSetBuilder final {
friend ScalarRegisterSet;
friend RegisterSet;
public:
constexpr RegisterSetBuilder() { }
inline constexpr RegisterSetBuilder(RegisterSet);
template<typename... Regs>
inline constexpr explicit RegisterSetBuilder(Regs... regs)
{
setMany(regs...);
}
inline constexpr RegisterSetBuilder& add(Reg reg, Width width)
{
ASSERT_UNDER_CONSTEXPR_CONTEXT(!!reg);
m_bits.set(reg.index());
if (width > conservativeWidthWithoutVectors(reg) && conservativeWidth(reg) > conservativeWidthWithoutVectors(reg)) [[unlikely]]
m_upperBits.set(reg.index());
return *this;
}
inline constexpr RegisterSetBuilder& add(JSValueRegs regs, IgnoreVectorsTag)
{
if (regs.tagGPR() != InvalidGPRReg)
add(regs.tagGPR(), IgnoreVectors);
add(regs.payloadGPR(), IgnoreVectors);
return *this;
}
inline constexpr void add(Reg reg, IgnoreVectorsTag)
{
add(reg, conservativeWidthWithoutVectors(reg));
}
inline constexpr RegisterSetBuilder& remove(Reg reg)
{
ASSERT_UNDER_CONSTEXPR_CONTEXT(!!reg);
m_bits.clear(reg.index());
m_upperBits.clear(reg.index());
return *this;
}
inline constexpr RegisterSetBuilder& remove(JSValueRegs regs)
{
if (regs.tagGPR() != InvalidGPRReg)
remove(regs.tagGPR());
remove(regs.payloadGPR());
return *this;
}
inline constexpr bool hasAnyWideRegisters() const { return m_upperBits.count(); }
inline constexpr bool isEmpty() const { return m_bits.isEmpty() && m_upperBits.isEmpty(); }
inline constexpr RegisterSetBuilder& merge(const RegisterSetBuilder& other)
{
m_bits.merge(other.m_bits);
m_upperBits.merge(other.m_upperBits);
return *this;
}
inline constexpr RegisterSetBuilder& filter(const RegisterSetBuilder& other)
{
m_bits.filter(other.m_bits);
m_upperBits.filter(other.m_upperBits);
return *this;
}
inline constexpr RegisterSetBuilder& exclude(const RegisterSetBuilder& other)
{
m_bits.exclude(other.m_bits);
m_upperBits.exclude(other.m_upperBits);
return *this;
}
WARN_UNUSED_RETURN inline constexpr RegisterSet buildAndValidate() const;
WARN_UNUSED_RETURN inline constexpr RegisterSet buildWithLowerBits() const;
WARN_UNUSED_RETURN inline constexpr ScalarRegisterSet buildScalarRegisterSet() const;
inline constexpr size_t numberOfSetRegisters() const;
inline size_t numberOfSetGPRs() const;
inline size_t numberOfSetFPRs() const;
template<typename Func>
inline constexpr void forEachWithWidthAndPreserved(const Func& func) const
{
auto allBits = m_bits;
allBits.merge(m_upperBits);
allBits.forEachSetBit(
[&] (size_t index) {
Reg reg = Reg::fromIndex(index);
Width includedWidth = m_upperBits.get(index) ? conservativeWidth(reg) : conservativeWidthWithoutVectors(reg);
PreservedWidth preservedWidth = PreservesNothing;
if (!m_bits.get(index))
preservedWidth = Preserves64;
func(reg, includedWidth, preservedWidth);
});
}
void dump(PrintStream& out) const
{
CommaPrinter comma;
out.print("["_s);
for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) {
if (!m_bits.get(reg.index()) && !m_upperBits.get(reg.index()))
continue;
out.print(comma, reg);
if (m_bits.get(reg.index()) && (m_upperBits.get(reg.index()) || conservativeWidth(reg) == conservativeWidthWithoutVectors(reg)))
continue;
if (m_bits.get(reg.index()))
out.print("↓");
else
out.print("↑");
}
out.print("]"_s);
}
friend constexpr bool operator==(const RegisterSetBuilder&, const RegisterSetBuilder&) = default;
protected:
inline constexpr void setAny(Reg reg) { ASSERT_UNDER_CONSTEXPR_CONTEXT(!reg.isFPR()); add(reg, IgnoreVectors); }
inline constexpr void setAny(JSValueRegs regs) { add(regs, IgnoreVectors); }
inline constexpr void setAny(const RegisterSetBuilder& set) { merge(set); }
inline constexpr void setMany() { }
template<typename RegType, typename... Regs>
inline constexpr void setMany(RegType reg, Regs... regs)
{
setAny(reg);
setMany(regs...);
}
// These offsets mirror the logic in Reg.h.
static constexpr unsigned gprOffset = 0;
static constexpr unsigned fprOffset = gprOffset + MacroAssembler::numGPRs;
RegisterBitSet m_bits = { };
RegisterBitSet m_upperBits = { };
public:
JS_EXPORT_PRIVATE static RegisterSet allGPRs();
JS_EXPORT_PRIVATE static RegisterSet allFPRs();
JS_EXPORT_PRIVATE static RegisterSet allRegisters();
JS_EXPORT_PRIVATE static RegisterSet allScalarRegisters();
JS_EXPORT_PRIVATE static RegisterSet stackRegisters();
JS_EXPORT_PRIVATE static RegisterSet reservedHardwareRegisters();
JS_EXPORT_PRIVATE static RegisterSet macroClobberedGPRs();
JS_EXPORT_PRIVATE static RegisterSet macroClobberedFPRs();
JS_EXPORT_PRIVATE static RegisterSet runtimeTagRegisters();
JS_EXPORT_PRIVATE static RegisterSet specialRegisters(); // The union of stack, reserved hardware, and runtime registers.
JS_EXPORT_PRIVATE static RegisterSet calleeSaveRegisters();
JS_EXPORT_PRIVATE static RegisterSet vmCalleeSaveRegisters(); // Callee save registers that might be saved and used by any tier.
JS_EXPORT_PRIVATE static RegisterAtOffsetList* vmCalleeSaveRegisterOffsets();
JS_EXPORT_PRIVATE static RegisterSet llintBaselineCalleeSaveRegisters(); // Registers saved and used by the LLInt.
JS_EXPORT_PRIVATE static RegisterSet dfgCalleeSaveRegisters(); // Registers saved and used by the DFG JIT.
JS_EXPORT_PRIVATE static RegisterSet ftlCalleeSaveRegisters(); // Registers that might be saved and used by the FTL JIT.
JS_EXPORT_PRIVATE static RegisterSet stubUnavailableRegisters(); // The union of callee saves and special registers.
JS_EXPORT_PRIVATE static RegisterSet argumentGPRs();
JS_EXPORT_PRIVATE static RegisterSet argumentFPRs();
#if ENABLE(WEBASSEMBLY)
JS_EXPORT_PRIVATE static RegisterSet wasmPinnedRegisters();
JS_EXPORT_PRIVATE static RegisterSet ipintCalleeSaveRegisters(); // Registers saved and used by the IPInt.
JS_EXPORT_PRIVATE static RegisterSet bbqCalleeSaveRegisters(); // Registers saved and used by the BBQ JIT.
#endif
JS_EXPORT_PRIVATE static RegisterSetBuilder registersToSaveForJSCall(RegisterSetBuilder live);
JS_EXPORT_PRIVATE static RegisterSetBuilder registersToSaveForCCall(RegisterSetBuilder live);
};
class RegisterSet final {
friend ScalarRegisterSet;
friend RegisterSetBuilder;
constexpr explicit inline RegisterSet(const RegisterSetBuilder& set)
: m_bits(set.m_bits)
, m_upperBits(set.m_upperBits)
{
m_bits.merge(m_upperBits);
}
public:
constexpr RegisterSet() = default;
template<typename RegType, typename... Regs>
constexpr explicit inline RegisterSet(RegType reg, Regs... regs)
: RegisterSet(regs...)
{
add(reg, IgnoreVectors);
}
inline constexpr bool contains(Reg reg, Width width) const
{
ASSERT_UNDER_CONSTEXPR_CONTEXT(m_bits.count() >= m_upperBits.count());
if (width < conservativeWidth(reg)) [[likely]]
return m_bits.get(reg.index());
if (conservativeWidth(reg) <= conservativeWidthWithoutVectors(reg))
return m_bits.get(reg.index());
return m_bits.get(reg.index()) && m_upperBits.get(reg.index());
}
inline constexpr bool contains(Reg reg, IgnoreVectorsTag) const
{
return contains(reg, conservativeWidthWithoutVectors(reg));
}
inline size_t numberOfSetGPRs() const
{
RegisterBitSet temp = m_bits;
temp.filter(RegisterSetBuilder::allGPRs().m_bits);
return temp.count();
}
inline size_t numberOfSetFPRs() const
{
RegisterBitSet temp = m_bits;
temp.filter(RegisterSetBuilder::allFPRs().m_bits);
return temp.count();
}
inline constexpr size_t numberOfSetRegisters() const
{
ASSERT_UNDER_CONSTEXPR_CONTEXT(m_bits.count() >= m_upperBits.count());
return m_bits.count();
}
inline size_t byteSizeOfSetRegisters() const
{
#if CPU(REGISTER64)
return (m_bits.count() + m_upperBits.count()) * sizeof(CPURegister);
#else
auto effectiveGPRCount = numberOfSetFPRs()
? WTF::roundUpToMultipleOf<2>(numberOfSetGPRs())
: numberOfSetGPRs();
return effectiveGPRCount * bytesForWidth(pointerWidth()) + numberOfSetFPRs() * sizeof(double);
#endif
}
inline constexpr bool subsumes(const RegisterSet& other) const
{
return m_bits.subsumes(other.m_bits) && m_upperBits.subsumes(other.m_upperBits);
}
inline constexpr bool isEmpty() const
{
ASSERT_UNDER_CONSTEXPR_CONTEXT(m_bits.count() >= m_upperBits.count());
return m_bits.isEmpty();
}
inline constexpr RegisterSet& includeWholeRegisterWidth()
{
ASSERT_UNDER_CONSTEXPR_CONTEXT(m_bits.count() >= m_upperBits.count());
m_upperBits.merge(m_bits);
return *this;
}
WARN_UNUSED_RETURN inline constexpr ScalarRegisterSet buildScalarRegisterSet() const;
template<typename Func>
inline constexpr void forEach(const Func& func) const
{
ASSERT_UNDER_CONSTEXPR_CONTEXT(m_bits.count() >= m_upperBits.count());
m_bits.forEachSetBit(
[&] (size_t index) {
ASSERT_UNDER_CONSTEXPR_CONTEXT(m_bits.get(index) >= m_upperBits.get(index));
func(Reg::fromIndex(index));
});
}
template<typename Func>
inline constexpr void forEachWithWidth(const Func& func) const
{
ASSERT_UNDER_CONSTEXPR_CONTEXT(m_bits.count() >= m_upperBits.count());
m_bits.forEachSetBit(
[&] (size_t index) {
ASSERT_UNDER_CONSTEXPR_CONTEXT(m_bits.get(index) >= m_upperBits.get(index));
Reg reg = Reg::fromIndex(index);
Width includedWidth = m_upperBits.get(index) ? conservativeWidth(reg) : conservativeWidthWithoutVectors(reg);
func(reg, includedWidth);
});
}
class iterator {
public:
inline constexpr iterator() { }
inline constexpr iterator(const RegisterBitSet::iterator& iter)
: m_iter(iter)
{
}
inline constexpr Reg reg() const { return Reg::fromIndex(*m_iter); }
inline constexpr Reg operator*() const { return reg(); }
inline constexpr bool isGPR() const { return reg().isGPR(); }
inline constexpr bool isFPR() const { return reg().isFPR(); }
inline constexpr GPRReg gpr() const { return reg().gpr(); }
inline constexpr FPRReg fpr() const { return reg().fpr(); }
iterator& operator++()
{
++m_iter;
return *this;
}
friend constexpr bool operator==(const iterator&, const iterator&) = default;
private:
RegisterBitSet::iterator m_iter;
};
inline constexpr iterator begin() const { return iterator(m_bits.begin()); }
inline constexpr iterator end() const { return iterator(m_bits.end()); }
inline constexpr RegisterSet& add(Reg reg, Width width)
{
ASSERT_UNDER_CONSTEXPR_CONTEXT(!!reg);
m_bits.set(reg.index());
if (width > conservativeWidthWithoutVectors(reg) && conservativeWidth(reg) > conservativeWidthWithoutVectors(reg)) [[unlikely]]
m_upperBits.set(reg.index());
return *this;
}
inline constexpr void add(Reg reg, IgnoreVectorsTag)
{
add(reg, conservativeWidthWithoutVectors(reg));
}
inline constexpr RegisterSet& add(JSValueRegs regs, IgnoreVectorsTag)
{
if (regs.tagGPR() != InvalidGPRReg)
add(regs.tagGPR(), IgnoreVectors);
add(regs.payloadGPR(), IgnoreVectors);
return *this;
}
inline constexpr RegisterSet& remove(Reg reg)
{
ASSERT_UNDER_CONSTEXPR_CONTEXT(!!reg);
m_bits.clear(reg.index());
m_upperBits.clear(reg.index());
return *this;
}
inline constexpr RegisterSet& remove(JSValueRegs regs)
{
if (regs.tagGPR() != InvalidGPRReg)
remove(regs.tagGPR());
remove(regs.payloadGPR());
return *this;
}
inline constexpr bool hasAnyWideRegisters() const { return m_upperBits.count(); }
inline constexpr RegisterSet& merge(const RegisterSet& other)
{
m_bits.merge(other.m_bits);
m_upperBits.merge(other.m_upperBits);
ASSERT_UNDER_CONSTEXPR_CONTEXT(m_bits.count() >= m_upperBits.count());
return *this;
}
void dump(PrintStream& out) const
{
CommaPrinter comma;
out.print("["_s);
for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) {
if (!m_bits.get(reg.index()) && !m_upperBits.get(reg.index()))
continue;
out.print(comma, reg);
if (m_bits.get(reg.index()) && (m_upperBits.get(reg.index()) || conservativeWidth(reg) == conservativeWidthWithoutVectors(reg)))
continue;
if (m_bits.get(reg.index()))
out.print("↓");
else
out.print("↑");
}
out.print("]"_s);
}
friend constexpr bool operator==(const RegisterSet&, const RegisterSet&) = default;
private:
RegisterBitSet m_bits = { };
RegisterBitSet m_upperBits = { };
};
constexpr RegisterSetBuilder::RegisterSetBuilder(RegisterSet set)
: m_bits(set.m_bits)
, m_upperBits(set.m_upperBits)
{ }
constexpr RegisterSet RegisterSetBuilder::buildAndValidate() const
{
#if ASSERT_ENABLED
for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) {
if (!m_bits.get(reg.index()) && !m_upperBits.get(reg.index()))
continue;
ASSERT_UNDER_CONSTEXPR_CONTEXT(!m_upperBits.get(reg.index()) || m_bits.get(reg.index()));
}
#endif
return RegisterSet(*this);
}
constexpr RegisterSet RegisterSetBuilder::buildWithLowerBits() const
{
return RegisterSet(*this);
}
constexpr size_t RegisterSetBuilder::numberOfSetRegisters() const { return buildAndValidate().numberOfSetRegisters(); }
size_t RegisterSetBuilder::numberOfSetGPRs() const { return buildAndValidate().numberOfSetGPRs(); }
size_t RegisterSetBuilder::numberOfSetFPRs() const { return buildAndValidate().numberOfSetFPRs(); }
class ScalarRegisterSet final {
friend RegisterSet;
friend RegisterSetBuilder;
inline constexpr ScalarRegisterSet(const RegisterSet& registers)
: m_bits(registers.m_bits)
{
}
public:
constexpr ScalarRegisterSet() { }
inline constexpr unsigned hash() const { return m_bits.hash(); }
inline uint64_t bitsForDebugging() const { return m_bits.storage()[0]; }
friend constexpr bool operator==(const ScalarRegisterSet&, const ScalarRegisterSet&) = default;
WARN_UNUSED_RETURN inline constexpr RegisterSet toRegisterSet() const
{
RegisterSet result;
m_bits.forEachSetBit(
[&] (size_t index) {
result.add(Reg::fromIndex(index), conservativeWidthWithoutVectors(Reg::fromIndex(index)));
});
return result;
}
inline constexpr void add(Reg reg, IgnoreVectorsTag)
{
ASSERT_UNDER_CONSTEXPR_CONTEXT(!!reg);
m_bits.set(reg.index());
}
inline constexpr void add(JSValueRegs regs, IgnoreVectorsTag)
{
if (regs.tagGPR() != InvalidGPRReg)
add(regs.tagGPR(), IgnoreVectors);
add(regs.payloadGPR(), IgnoreVectors);
}
inline constexpr void remove(Reg reg)
{
ASSERT_UNDER_CONSTEXPR_CONTEXT(!!reg);
m_bits.clear(reg.index());
}
inline constexpr bool contains(Reg reg, IgnoreVectorsTag) const
{
ASSERT_UNDER_CONSTEXPR_CONTEXT(!!reg);
return m_bits.get(reg.index());
}
inline size_t numberOfSetGPRs() const
{
RegisterBitSet temp = m_bits;
temp.filter(RegisterSetBuilder::allGPRs().m_bits);
return temp.count();
}
inline size_t numberOfSetFPRs() const
{
RegisterBitSet temp = m_bits;
temp.filter(RegisterSetBuilder::allFPRs().m_bits);
return temp.count();
}
inline constexpr size_t numberOfSetRegisters() const
{
return m_bits.count();
}
void dump(PrintStream& out) const { toRegisterSet().dump(out); }
private:
RegisterBitSet m_bits;
};
constexpr ScalarRegisterSet RegisterSetBuilder::buildScalarRegisterSet() const
{
return ScalarRegisterSet(buildAndValidate());
}
constexpr ScalarRegisterSet RegisterSet::buildScalarRegisterSet() const
{
return ScalarRegisterSet(*this);
}
struct RegisterSetBuilderHash {
static constexpr unsigned hash(const ScalarRegisterSet& set) { return set.hash(); }
static constexpr bool equal(const ScalarRegisterSet& a, const ScalarRegisterSet& b) { return a == b; }
static constexpr bool safeToCompareToEmptyOrDeleted = false;
};
} // namespace JSC
namespace WTF {
template<typename T> struct DefaultHash;
template<> struct DefaultHash<JSC::ScalarRegisterSet> : JSC::RegisterSetBuilderHash { };
} // namespace WTF
#endif // !ENABLE(C_LOOP)