solidity/tools/yulPhaser/ProgramCache.cpp

155 lines
3.8 KiB
C++

/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
#include <tools/yulPhaser/ProgramCache.h>
#include <libyul/optimiser/Metrics.h>
#include <libyul/optimiser/Suite.h>
using namespace std;
using namespace solidity::yul;
using namespace solidity::phaser;
CacheStats& CacheStats::operator+=(CacheStats const& _other)
{
hits += _other.hits;
misses += _other.misses;
totalCodeSize += _other.totalCodeSize;
for (auto& [round, count]: _other.roundEntryCounts)
if (roundEntryCounts.find(round) != roundEntryCounts.end())
roundEntryCounts.at(round) += count;
else
roundEntryCounts.insert({round, count});
return *this;
}
bool CacheStats::operator==(CacheStats const& _other) const
{
return
hits == _other.hits &&
misses == _other.misses &&
totalCodeSize == _other.totalCodeSize &&
roundEntryCounts == _other.roundEntryCounts;
}
Program ProgramCache::optimiseProgram(
string const& _abbreviatedOptimisationSteps,
size_t _repetitionCount
)
{
string targetOptimisations = _abbreviatedOptimisationSteps;
for (size_t i = 1; i < _repetitionCount; ++i)
targetOptimisations += _abbreviatedOptimisationSteps;
size_t prefixSize = 0;
for (size_t i = 1; i <= targetOptimisations.size(); ++i)
{
auto const& pair = m_entries.find(targetOptimisations.substr(0, i));
if (pair != m_entries.end())
{
pair->second.roundNumber = m_currentRound;
++prefixSize;
++m_hits;
}
else
break;
}
Program intermediateProgram = (
prefixSize == 0 ?
m_program :
m_entries.at(targetOptimisations.substr(0, prefixSize)).program
);
for (size_t i = prefixSize + 1; i <= targetOptimisations.size(); ++i)
{
string stepName = OptimiserSuite::stepAbbreviationToNameMap().at(targetOptimisations[i - 1]);
intermediateProgram.optimise({stepName});
m_entries.insert({targetOptimisations.substr(0, i), {intermediateProgram, m_currentRound}});
++m_misses;
}
return intermediateProgram;
}
void ProgramCache::startRound(size_t _roundNumber)
{
assert(_roundNumber > m_currentRound);
m_currentRound = _roundNumber;
for (auto pair = m_entries.begin(); pair != m_entries.end();)
{
assert(pair->second.roundNumber < m_currentRound);
if (pair->second.roundNumber < m_currentRound - 1)
m_entries.erase(pair++);
else
++pair;
}
}
void ProgramCache::clear()
{
m_entries.clear();
m_currentRound = 0;
}
Program const* ProgramCache::find(string const& _abbreviatedOptimisationSteps) const
{
auto const& pair = m_entries.find(_abbreviatedOptimisationSteps);
if (pair == m_entries.end())
return nullptr;
return &(pair->second.program);
}
CacheStats ProgramCache::gatherStats() const
{
return {
/* hits = */ m_hits,
/* misses = */ m_misses,
/* totalCodeSize = */ calculateTotalCachedCodeSize(),
/* roundEntryCounts = */ countRoundEntries(),
};
}
size_t ProgramCache::calculateTotalCachedCodeSize() const
{
size_t size = 0;
for (auto const& pair: m_entries)
size += pair.second.program.codeSize(CacheStats::StorageWeights);
return size;
}
map<size_t, size_t> ProgramCache::countRoundEntries() const
{
map<size_t, size_t> counts;
for (auto& pair: m_entries)
if (counts.find(pair.second.roundNumber) != counts.end())
++counts.at(pair.second.roundNumber);
else
counts.insert({pair.second.roundNumber, 1});
return counts;
}