diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 175a443a1..052aed121 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -56,6 +56,8 @@ add_library(yul optimiser/BlockDeepener.h optimiser/BlockFlattener.cpp optimiser/BlockFlattener.h + optimiser/BlockHasher.cpp + optimiser/BlockHasher.h optimiser/BlockClassFinder.cpp optimiser/BlockClassFinder.h optimiser/BlockOutliner.cpp diff --git a/libyul/optimiser/BlockHasher.cpp b/libyul/optimiser/BlockHasher.cpp new file mode 100644 index 000000000..4eaebab00 --- /dev/null +++ b/libyul/optimiser/BlockHasher.cpp @@ -0,0 +1,184 @@ +/* + 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 . +*/ +/** + * Optimiser component that calculates hash values for block prefixes. + */ + +#include +#include +#include + +using namespace std; +using namespace dev; +using namespace yul; + +namespace { +static constexpr uint64_t compileTimeLiteralHash(char const* _literal, size_t _N) +{ + return (_N == 0) ? 14695981039346656037u : compileTimeLiteralHash(_literal + 1, _N - 1) ^ 1099511628211u; +} + +template +static constexpr uint64_t compileTimeLiteralHash(char const (&_literal)[N]) +{ + return compileTimeLiteralHash(_literal, N); +} +} + +BlockHasher::State BlockHasher::run(Block const& _block) +{ + BlockHasher::State result; + BlockHasher blockHasher(result); + blockHasher(_block); + return result; +} + +void BlockHasher::operator()(Literal const& _literal) +{ + hash(compileTimeLiteralHash("Literal")); + hash(_literal.value.hash()); + hash(_literal.type.hash()); + hash(static_cast(static_cast>(_literal.kind))); +} + +void BlockHasher::operator()(Identifier const& _identifier) +{ + hash(compileTimeLiteralHash("Identifier")); + auto it = m_variableReferences.find(_identifier.name); + if (it == m_variableReferences.end()) + it = m_variableReferences.emplace(_identifier.name, VariableReference { + m_externalIdentifierCount++, + true + }).first; + + if (it->second.isExternal) + hash(compileTimeLiteralHash("external")); + else + hash(compileTimeLiteralHash("internal")); + hash(it->second.id); +} + +void BlockHasher::operator()(FunctionalInstruction const& _instr) +{ + hash(compileTimeLiteralHash("FunctionalInstruction")); + hash(static_cast>(_instr.instruction)); + hash(_instr.arguments.size()); + ASTWalker::operator()(_instr); +} + +void BlockHasher::operator()(FunctionCall const& _funCall) +{ + hash(compileTimeLiteralHash("FunctionCall")); + hash(_funCall.arguments.size()); + hash(_funCall.functionName.name.hash()); + ASTWalker::operator()(_funCall); +} + +void BlockHasher::operator()(ExpressionStatement const& _statement) +{ + hash(compileTimeLiteralHash("ExpressionStatement")); + ASTWalker::operator()(_statement); +} + +void BlockHasher::operator()(Assignment const& _assignment) +{ + hash(compileTimeLiteralHash("Assignment")); + hash(_assignment.variableNames.size()); + for (auto const& name: _assignment.variableNames) + (*this)(name); + visit(*_assignment.value); +} + +void BlockHasher::operator()(VariableDeclaration const& _varDecl) +{ + hash(compileTimeLiteralHash("VariableDeclaration")); + hash(_varDecl.variables.size()); + for (auto const& var: _varDecl.variables) + { + yulAssert(!m_variableReferences.count(var.name), ""); + m_variableReferences[var.name] = VariableReference{ + m_internalIdentifierCount++, + false + }; + } + ASTWalker::operator()(_varDecl); +} + +void BlockHasher::operator()(If const& _if) +{ + hash(compileTimeLiteralHash("If")); + ASTWalker::operator()(_if); +} + +void BlockHasher::operator()(Switch const& _switch) +{ + hash(compileTimeLiteralHash("Switch")); + hash(_switch.cases.size()); + ASTWalker::operator()(_switch); +} + +void BlockHasher::operator()(FunctionDefinition const& _funDef) +{ + hash(compileTimeLiteralHash("FunctionDefinition")); + ASTWalker::operator()(_funDef); +} + +void BlockHasher::operator()(ForLoop const& _loop) +{ + hash(compileTimeLiteralHash("ForLoop")); + ASTWalker::operator()(_loop); +} + +void BlockHasher::operator()(Break const& _break) +{ + hash(compileTimeLiteralHash("Break")); + ASTWalker::operator()(_break); +} + +void BlockHasher::operator()(Continue const& _continue) +{ + hash(compileTimeLiteralHash("Continue")); + ASTWalker::operator()(_continue); +} + + +void BlockHasher::operator()(Block const& _block) +{ + hash(compileTimeLiteralHash("Block")); + hash(_block.statements.size()); + if (_block.statements.empty()) + return; + + BlockHasher subBlockPrefixHasher(m_state); + BlockPart blockPart { &_block, 0 }; + for (auto const& statement: _block.statements) + { + subBlockPrefixHasher.visit(statement); + ++blockPart.length; + m_state.blockPrefixHashes[blockPart] = subBlockPrefixHasher.m_hash; + m_state.hashClasses[subBlockPrefixHasher.m_hash].emplace_back(blockPart); + } + // propagate external references in subblock + hash(subBlockPrefixHasher.m_hash); + std::vector externalReferences; + for (auto const& variableReference: subBlockPrefixHasher.m_variableReferences) + if (variableReference.second.isExternal) + externalReferences.emplace_back(variableReference.first); + hash(externalReferences.size()); + for (auto& externalReference: externalReferences) + (*this)(Identifier{{}, externalReference}); +} diff --git a/libyul/optimiser/BlockHasher.h b/libyul/optimiser/BlockHasher.h new file mode 100644 index 000000000..ccff74e75 --- /dev/null +++ b/libyul/optimiser/BlockHasher.h @@ -0,0 +1,108 @@ +/* + 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 . +*/ +/** + * Optimiser component that calculates hash values for block prefixes. + */ +#pragma once + +#include +#include +#include +#include + +namespace yul +{ + +struct BlockPart { + Block const* block; + size_t length; + bool operator<(BlockPart const& _rhs) const + { + return std::make_tuple(_rhs.length, _rhs.block) < std::make_tuple(length, block); + } +}; + +/** + * Optimiser component that calculates hash values for block prefixes. + * + * Prerequisite: Disambiguator + */ +class BlockHasher: public ASTWalker +{ +public: + struct State { + std::map blockPrefixHashes; + std::map> hashClasses; + }; + + using ASTWalker::operator(); + + void operator()(Literal const&) override; + void operator()(Identifier const&) override; + void operator()(FunctionalInstruction const& _instr) override; + void operator()(FunctionCall const& _funCall) override; + void operator()(ExpressionStatement const& _statement) override; + void operator()(Assignment const& _assignment) override; + void operator()(VariableDeclaration const& _varDecl) override; + void operator()(If const& _if) override; + void operator()(Switch const& _switch) override; + void operator()(FunctionDefinition const&) override; + void operator()(ForLoop const&) override; + void operator()(Break const&) override; + void operator()(Continue const&) override; + void operator()(Block const& _block) override; + + static State run(Block const& _block); + +private: + State& m_state; + + BlockHasher(State& _state): m_state(_state) {} + + void hash(uint8_t _value) + { + m_hash *= 1099511628211u; + m_hash ^= _value; + } + void hash(uint16_t _value) + { + hash(static_cast(_value & 0xFF)); + hash(static_cast(_value >> 8)); + } + void hash(uint32_t _value) + { + hash(static_cast(_value & 0xFFFF)); + hash(static_cast(_value >> 16)); + } + void hash(uint64_t _value) + { + hash(static_cast(_value & 0xFFFFFFFF)); + hash(static_cast(_value >> 32)); + } + + uint64_t m_hash = 14695981039346656037u; + struct VariableReference { + size_t id = 0; + bool isExternal = false; + }; + std::map m_variableReferences; + size_t m_externalIdentifierCount = 0; + size_t m_internalIdentifierCount = 0; +}; + + +}