/* 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 /** * Optimiser component that calculates hash values for block prefixes. */ #include #include #include using namespace solidity; using namespace solidity::yul; using namespace solidity::util; namespace { static constexpr uint64_t compileTimeLiteralHash(char const* _literal, size_t _n) { return (_n == 0) ? BlockHasher::fnvEmptyHash : (static_cast(_literal[0]) * BlockHasher::fnvPrime) ^ compileTimeLiteralHash(_literal + 1, _n - 1); } template static constexpr uint64_t compileTimeLiteralHash(char const (&_literal)[N]) { return compileTimeLiteralHash(_literal, N); } } std::map BlockHasher::run(Block const& _block) { std::map result; BlockHasher blockHasher(result); blockHasher(_block); return result; } void BlockHasher::operator()(Literal const& _literal) { hash64(compileTimeLiteralHash("Literal")); if (_literal.kind == LiteralKind::Number) hash64(std::hash{}(valueOfNumberLiteral(_literal))); else hash64(_literal.value.hash()); hash64(_literal.type.hash()); hash8(static_cast(_literal.kind)); } void BlockHasher::operator()(Identifier const& _identifier) { hash64(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; m_externalReferences.emplace_back(_identifier.name); } if (it->second.isExternal) hash64(compileTimeLiteralHash("external")); else hash64(compileTimeLiteralHash("internal")); hash64(it->second.id); } void BlockHasher::operator()(FunctionCall const& _funCall) { hash64(compileTimeLiteralHash("FunctionCall")); hash64(_funCall.functionName.name.hash()); hash64(_funCall.arguments.size()); ASTWalker::operator()(_funCall); } void BlockHasher::operator()(ExpressionStatement const& _statement) { hash64(compileTimeLiteralHash("ExpressionStatement")); ASTWalker::operator()(_statement); } void BlockHasher::operator()(Assignment const& _assignment) { hash64(compileTimeLiteralHash("Assignment")); hash64(_assignment.variableNames.size()); for (auto const& name: _assignment.variableNames) (*this)(name); visit(*_assignment.value); } void BlockHasher::operator()(VariableDeclaration const& _varDecl) { hash64(compileTimeLiteralHash("VariableDeclaration")); hash64(_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) { hash64(compileTimeLiteralHash("If")); ASTWalker::operator()(_if); } void BlockHasher::operator()(Switch const& _switch) { hash64(compileTimeLiteralHash("Switch")); hash64(_switch.cases.size()); // Instead of sorting we could consider to combine // the case hashes using a commutative operation here. std::set cases; for (auto const& _case: _switch.cases) cases.insert(&_case); visit(*_switch.expression); for (auto const& _case: cases) { if (_case->value) (*this)(*_case->value); (*this)(_case->body); } } void BlockHasher::operator()(FunctionDefinition const& _funDef) { hash64(compileTimeLiteralHash("FunctionDefinition")); ASTWalker::operator()(_funDef); } void BlockHasher::operator()(ForLoop const& _loop) { yulAssert(_loop.pre.statements.empty(), ""); hash64(compileTimeLiteralHash("ForLoop")); ASTWalker::operator()(_loop); } void BlockHasher::operator()(Break const& _break) { hash64(compileTimeLiteralHash("Break")); ASTWalker::operator()(_break); } void BlockHasher::operator()(Continue const& _continue) { hash64(compileTimeLiteralHash("Continue")); ASTWalker::operator()(_continue); } void BlockHasher::operator()(Leave const& _leaveStatement) { hash64(compileTimeLiteralHash("Leave")); ASTWalker::operator()(_leaveStatement); } void BlockHasher::operator()(Block const& _block) { hash64(compileTimeLiteralHash("Block")); hash64(_block.statements.size()); if (_block.statements.empty()) return; BlockHasher subBlockHasher(m_blockHashes); for (auto const& statement: _block.statements) subBlockHasher.visit(statement); m_blockHashes[&_block] = subBlockHasher.m_hash; hash64(subBlockHasher.m_hash); hash64(subBlockHasher.m_externalReferences.size()); for (auto& externalReference: subBlockHasher.m_externalReferences) (*this)(Identifier{{}, externalReference}); } uint64_t ExpressionHasher::run(Expression const& _e) { ExpressionHasher expressionHasher; expressionHasher.visit(_e); return expressionHasher.m_hash; } void ExpressionHasher::operator()(Literal const& _literal) { hash64(compileTimeLiteralHash("Literal")); if (_literal.kind == LiteralKind::Number) hash64(std::hash{}(valueOfNumberLiteral(_literal))); else hash64(_literal.value.hash()); hash64(_literal.type.hash()); hash8(static_cast(_literal.kind)); } void ExpressionHasher::operator()(Identifier const& _identifier) { hash64(compileTimeLiteralHash("Identifier")); hash64(_identifier.name.hash()); } void ExpressionHasher::operator()(FunctionCall const& _funCall) { hash64(compileTimeLiteralHash("FunctionCall")); hash64(_funCall.functionName.name.hash()); hash64(_funCall.arguments.size()); ASTWalker::operator()(_funCall); }