mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			155 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			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;
 | 
						|
}
 |