Skip to content
Permalink
Browse files
[Wasm] Reduce the amount of memory used by the Air register coloring …
…allocator

https://bugs.webkit.org/show_bug.cgi?id=212106

Reviewed by Yusuke Suzuki.

Changed InterferenceEdge to be a templated class so we can instantiate an unsigned
short version to cut memory in half for code that has less than 2^16 temps.
Through instrumentation, my testing showed that almost all compilations use the
16bit implementation.  Although this change is for all B3/Air compilations at O2,
Wasm compilations are usally larger and therefore get the greatest benefit.

This allowed increasing the default value for the option webAssemblyBBQFallbackSize,
with a small increase in memory usage.

* b3/air/AirAllocateRegistersByGraphColoring.cpp:
* runtime/OptionsList.h:


Canonical link: https://commits.webkit.org/226077@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@263146 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
msaboff committed Jun 17, 2020
1 parent 9f1ccc5 commit 28c3e585116ab925dcd6b181b6d28edbe997f696
Showing 3 changed files with 158 additions and 104 deletions.
@@ -1,3 +1,22 @@
2020-06-17 Michael Saboff <msaboff@apple.com>

[Wasm] Reduce the amount of memory used by the Air register coloring allocator
https://bugs.webkit.org/show_bug.cgi?id=212106

Reviewed by Yusuke Suzuki.

Changed InterferenceEdge to be a templated class so we can instantiate an unsigned
short version to cut memory in half for code that has less than 2^16 temps.
Through instrumentation, my testing showed that almost all compilations use the
16bit implementation. Although this change is for all B3/Air compilations at O2,
Wasm compilations are usally larger and therefore get the greatest benefit.

This allowed increasing the default value for the option webAssemblyBBQFallbackSize,
with a small increase in memory usage.

* b3/air/AirAllocateRegistersByGraphColoring.cpp:
* runtime/OptionsList.h:

2020-06-16 Yusuke Suzuki <ysuzuki@apple.com>

[JSC] Check NullSetterFunction under strict-mode context since structure / PropertyCondition are unaware of this
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015-2019 Apple Inc. All rights reserved.
* Copyright (C) 2015-2020 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -47,9 +47,101 @@ static constexpr bool debug = false;
static constexpr bool traceDebug = false;
static constexpr bool reportStats = false;

// Interference edges are not directed. An edge between any two Tmps is represented
// by the concatenated values of the smallest Tmp followed by the bigger Tmp.
// We have a templated version to support 16 bit and a 32 bit implementations
// since the number of entries in an interference table is O(N^2).
// In most cases, we use the 16 bit flavor, thus saving half the memory
// with a single implementation.
template <typename IndexImplType, typename PairStorageType>
class InterferenceEdge {
static_assert(sizeof(IndexImplType) * 2 == sizeof(PairStorageType));
const unsigned ShiftAmount = sizeof(IndexImplType) * 8;
const PairStorageType IndexMask = std::numeric_limits<IndexImplType>::max();

protected:
using IndexType = unsigned;

public:
struct InterferenceEdgeHash {
static unsigned hash(const InterferenceEdge<IndexImplType, PairStorageType>& key) { return key.hash(); }
static bool equal(const InterferenceEdge<IndexImplType, PairStorageType>& a, const InterferenceEdge<IndexImplType, PairStorageType>& b) { return a == b; }
static constexpr bool safeToCompareToEmptyOrDeleted = true;
};
typedef SimpleClassHashTraits<InterferenceEdge<IndexImplType, PairStorageType>> InterferenceEdgeHashTraits;

typedef HashSet<InterferenceEdge<IndexImplType, PairStorageType>, InterferenceEdgeHash, InterferenceEdgeHashTraits> InterferenceSet;

InterferenceEdge()
{
}

InterferenceEdge(IndexType a, IndexType b)
{
ASSERT(a);
ASSERT(b);
ASSERT(a < std::numeric_limits<IndexImplType>::max());
ASSERT(b < std::numeric_limits<IndexImplType>::max());
ASSERT_WITH_MESSAGE(a != b, "A Tmp can never interfere with itself. Doing so would force it to be the superposition of two registers.");

if (b < a)
std::swap(a, b);
m_value = static_cast<PairStorageType>(a) << ShiftAmount | b;
}

InterferenceEdge(const InterferenceEdge<IndexImplType, PairStorageType>& other)
: m_value(other.m_value)
{
}


InterferenceEdge(WTF::HashTableDeletedValueType)
: m_value(std::numeric_limits<PairStorageType>::max())
{
}

IndexType first() const
{
return m_value >> ShiftAmount & IndexMask;
}

IndexType second() const
{
return m_value & IndexMask;
}

bool operator==(const InterferenceEdge& other) const
{
return m_value == other.m_value;
}

InterferenceEdge& operator=(const InterferenceEdge& other)
{
m_value = other.m_value;
return *this;
}
bool isHashTableDeletedValue() const
{
return *this == InterferenceEdge(WTF::HashTableDeletedValue);
}

unsigned hash() const
{
return WTF::IntHash<PairStorageType>::hash(m_value);
}

void dump(PrintStream& out) const
{
out.print(first(), "<=>", second());
}

private:
PairStorageType m_value { 0 };
};

// The AbstractColoringAllocator defines all the code that is independant
// from the bank or register and can be shared when allocating registers.
template<typename IndexType, typename TmpMapper>
template<typename IndexType, typename TmpMapper, typename InterferenceEdgeType>
class AbstractColoringAllocator {
public:
AbstractColoringAllocator(Code& code, const Vector<Reg>& regsInPriorityOrder, IndexType lastPrecoloredRegisterIndex, unsigned tmpArraySize, const HashSet<unsigned>& unspillableTmps, const UseCounts<Tmp>& useCounts)
@@ -115,7 +207,7 @@ class AbstractColoringAllocator {
void addEdgeDistinct(IndexType a, IndexType b)
{
ASSERT(a != b);
bool isNewEdge = addInterferenceEdge(InterferenceEdge(a, b));
bool isNewEdge = addInterferenceEdge(InterferenceEdgeType(a, b));
if (isNewEdge) {
if (!isPrecolored(a)) {
ASSERT(!m_adjacencyList[a].contains(b));
@@ -134,7 +226,7 @@ class AbstractColoringAllocator {
bool addEdgeDistinctWithoutDegreeChange(IndexType a, IndexType b)
{
ASSERT(a != b);
bool isNewEdge = addInterferenceEdge(InterferenceEdge(a, b));
bool isNewEdge = addInterferenceEdge(InterferenceEdgeType(a, b));
if (isNewEdge) {
if (!isPrecolored(a)) {
ASSERT(!m_adjacencyList[a].contains(b));
@@ -249,7 +341,7 @@ class AbstractColoringAllocator {
if (!isPrecolored(adjacentTmpIndex)
&& !hasBeenSimplified(adjacentTmpIndex)
&& m_degrees[adjacentTmpIndex] >= registerCount()
&& !hasInterferenceEdge(InterferenceEdge(u, adjacentTmpIndex)))
&& !hasInterferenceEdge(InterferenceEdgeType(u, adjacentTmpIndex)))
return false;
}
return true;
@@ -339,7 +431,7 @@ class AbstractColoringAllocator {
ASSERT(m_spillWorklist.isEmpty());

// Reclaim as much memory as possible.
m_interferenceEdges.clear();
clearInterferenceEdges();

m_degrees.clear();
m_moveList.clear();
@@ -421,6 +513,21 @@ class AbstractColoringAllocator {
m_coloredTmp.clear();
}

bool addInterferenceEdge(InterferenceEdgeType edge)
{
return m_interferenceEdges.add(edge).isNewEntry;
}

bool hasInterferenceEdge(InterferenceEdgeType edge)
{
return m_interferenceEdges.contains(edge);
}

void clearInterferenceEdges()
{
m_interferenceEdges.clear();
}

void dumpInterferenceGraphInDot(PrintStream& out)
{
out.print("graph InterferenceGraph { \n");
@@ -444,86 +551,10 @@ class AbstractColoringAllocator {
out.print("}\n");
}

// Interference edges are not directed. An edge between any two Tmps is represented
// by the concatenated values of the smallest Tmp followed by the bigger Tmp.
class InterferenceEdge {
public:
InterferenceEdge()
{
}

InterferenceEdge(IndexType a, IndexType b)
{
ASSERT(a);
ASSERT(b);
ASSERT_WITH_MESSAGE(a != b, "A Tmp can never interfere with itself. Doing so would force it to be the superposition of two registers.");

if (b < a)
std::swap(a, b);
m_value = static_cast<uint64_t>(a) << 32 | b;
}

InterferenceEdge(WTF::HashTableDeletedValueType)
: m_value(std::numeric_limits<uint64_t>::max())
{
}

IndexType first() const
{
return m_value >> 32 & 0xffffffff;
}

IndexType second() const
{
return m_value & 0xffffffff;
}

bool operator==(const InterferenceEdge& other) const
{
return m_value == other.m_value;
}

bool isHashTableDeletedValue() const
{
return *this == InterferenceEdge(WTF::HashTableDeletedValue);
}

unsigned hash() const
{
return WTF::IntHash<uint64_t>::hash(m_value);
}

void dump(PrintStream& out) const
{
out.print(first(), "<=>", second());
}

private:
uint64_t m_value { 0 };
};

bool addInterferenceEdge(InterferenceEdge edge)
{
return m_interferenceEdges.add(edge).isNewEntry;
}

bool hasInterferenceEdge(InterferenceEdge edge)
{
return m_interferenceEdges.contains(edge);
}

struct InterferenceEdgeHash {
static unsigned hash(const InterferenceEdge& key) { return key.hash(); }
static bool equal(const InterferenceEdge& a, const InterferenceEdge& b) { return a == b; }
static constexpr bool safeToCompareToEmptyOrDeleted = true;
};
typedef SimpleClassHashTraits<InterferenceEdge> InterferenceEdgeHashTraits;

Vector<Reg> m_regsInPriorityOrder;
IndexType m_lastPrecoloredRegisterIndex { 0 };

// The interference graph.
HashSet<InterferenceEdge, InterferenceEdgeHash, InterferenceEdgeHashTraits> m_interferenceEdges;
typename InterferenceEdgeType::InterferenceSet m_interferenceEdges;

Vector<Vector<IndexType, 0, UnsafeVectorOverflow, 4>, 0, UnsafeVectorOverflow> m_adjacencyList;
Vector<IndexType, 0, UnsafeVectorOverflow> m_degrees;
@@ -572,9 +603,9 @@ class AbstractColoringAllocator {
Vector<Tmp, 4> m_pinnedRegs;
};

template <typename IndexType, typename TmpMapper>
class Briggs : public AbstractColoringAllocator<IndexType, TmpMapper> {
using Base = AbstractColoringAllocator<IndexType, TmpMapper>;
template <typename IndexType, typename TmpMapper, typename InterferenceEdge>
class Briggs : public AbstractColoringAllocator<IndexType, TmpMapper, InterferenceEdge> {
using Base = AbstractColoringAllocator<IndexType, TmpMapper, InterferenceEdge>;
protected:
using Base::m_isOnSelectStack;
using Base::m_selectStack;
@@ -594,7 +625,6 @@ class Briggs : public AbstractColoringAllocator<IndexType, TmpMapper> {
using Base::m_lastPrecoloredRegisterIndex;
using Base::m_coloredTmp;
using Base::m_code;
using InterferenceEdge = typename Base::InterferenceEdge;
using Base::m_unspillableTmps;
using Base::hasInterferenceEdge;
using Base::getAlias;
@@ -605,7 +635,6 @@ class Briggs : public AbstractColoringAllocator<IndexType, TmpMapper> {
using Base::forEachAdjacent;
using Base::hasBeenSimplified;
using Base::addToSpill;
using Base::m_interferenceEdges;
using Base::addBias;
using Base::m_pinnedRegs;
using Base::m_regsInPriorityOrder;
@@ -928,9 +957,9 @@ class Briggs : public AbstractColoringAllocator<IndexType, TmpMapper> {
MoveSet m_worklistMoves;
};

template <typename IndexType, typename TmpMapper>
class IRC : public AbstractColoringAllocator<IndexType, TmpMapper> {
using Base = AbstractColoringAllocator<IndexType, TmpMapper>;
template <typename IndexType, typename TmpMapper, typename InterferenceEdge>
class IRC : public AbstractColoringAllocator<IndexType, TmpMapper, InterferenceEdge> {
using Base = AbstractColoringAllocator<IndexType, TmpMapper, InterferenceEdge>;
protected:
using Base::m_isOnSelectStack;
using Base::m_selectStack;
@@ -950,7 +979,6 @@ class IRC : public AbstractColoringAllocator<IndexType, TmpMapper> {
using Base::m_lastPrecoloredRegisterIndex;
using Base::m_coloredTmp;
using Base::m_code;
using InterferenceEdge = typename Base::InterferenceEdge;
using Base::m_unspillableTmps;
using Base::hasInterferenceEdge;
using Base::getAlias;
@@ -961,7 +989,6 @@ class IRC : public AbstractColoringAllocator<IndexType, TmpMapper> {
using Base::forEachAdjacent;
using Base::hasBeenSimplified;
using Base::addToSpill;
using Base::m_interferenceEdges;
using Base::m_adjacencyList;
using Base::dumpInterferenceGraphInDot;
using Base::addBias;
@@ -1371,10 +1398,10 @@ class IRC : public AbstractColoringAllocator<IndexType, TmpMapper> {
};

// This perform all the tasks that are specific to certain register type.
template<Bank bank, template<typename, typename> class AllocatorType>
class ColoringAllocator : public AllocatorType<unsigned, AbsoluteTmpMapper<bank>> {
template<Bank bank, template<typename, typename, typename> class AllocatorType, typename InterferenceEdgeType>
class ColoringAllocator : public AllocatorType<unsigned, AbsoluteTmpMapper<bank>, InterferenceEdgeType> {
using TmpMapper = AbsoluteTmpMapper<bank>;
using Base = AllocatorType<unsigned, TmpMapper>;
using Base = AllocatorType<unsigned, TmpMapper, InterferenceEdgeType>;
using Base::m_isOnSelectStack;
using Base::m_selectStack;
using Base::m_simplifyWorklist;
@@ -1393,12 +1420,10 @@ class ColoringAllocator : public AllocatorType<unsigned, AbsoluteTmpMapper<bank>
using Base::m_lastPrecoloredRegisterIndex;
using Base::m_coloredTmp;
using Base::m_code;
using InterferenceEdge = typename Base::InterferenceEdge;
using Base::m_worklistMoves;
using Base::hasInterferenceEdge;
using Base::getAlias;
using Base::addEdge;
using Base::m_interferenceEdges;
using Base::m_pinnedRegs;
using Base::m_regsInPriorityOrder;

@@ -1557,7 +1582,7 @@ class ColoringAllocator : public AllocatorType<unsigned, AbsoluteTmpMapper<bank>
unsigned leftIndex = TmpMapper::absoluteIndex(leftTmp);
unsigned rightIndex = TmpMapper::absoluteIndex(rightTmp);

return !hasInterferenceEdge(InterferenceEdge(leftIndex, rightIndex));
return !hasInterferenceEdge(InterferenceEdgeType(leftIndex, rightIndex));
}

void addToLowPriorityCoalescingCandidates(Arg left, Arg right)
@@ -1856,11 +1881,21 @@ class GraphColoringRegisterAllocation {
};

if (useIRC()) {
ColoringAllocator<bank, IRC> allocator(m_code, m_tmpWidth, m_useCounts, unspillableTmps);
done = doAllocation(allocator);
if (m_code.numTmps(bank) < std::numeric_limits<uint16_t>::max()) {
ColoringAllocator<bank, IRC, InterferenceEdge<uint16_t, uint32_t>> allocator(m_code, m_tmpWidth, m_useCounts, unspillableTmps);
done = doAllocation(allocator);
} else {
ColoringAllocator<bank, IRC, InterferenceEdge<uint32_t, uint64_t>> allocator(m_code, m_tmpWidth, m_useCounts, unspillableTmps);
done = doAllocation(allocator);
}
} else {
ColoringAllocator<bank, Briggs> allocator(m_code, m_tmpWidth, m_useCounts, unspillableTmps);
done = doAllocation(allocator);
if (m_code.numTmps(bank) < std::numeric_limits<uint16_t>::max()) {
ColoringAllocator<bank, Briggs, InterferenceEdge<uint16_t, uint32_t>> allocator(m_code, m_tmpWidth, m_useCounts, unspillableTmps);
done = doAllocation(allocator);
} else {
ColoringAllocator<bank, Briggs, InterferenceEdge<uint32_t, uint64_t>> allocator(m_code, m_tmpWidth, m_useCounts, unspillableTmps);
done = doAllocation(allocator);
}
}
}
dataLogLnIf(reportStats, "Num iterations = ", numIterations, " for bank: ", bank);

0 comments on commit 28c3e58

Please sign in to comment.