/* 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 . */ // SPDX-License-Identifier: GPL-3.0 #include #include #include 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 ProgramCache::countRoundEntries() const { map 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; }