From 04079bff6f1f8fb4b45a19a95fade6acd7af300a Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Wed, 7 Oct 2020 13:28:34 +0200 Subject: [PATCH 1/2] Eliminates dead code around GasEstimator - structuralEstimation() - breakToStatementLevel() --- libsolidity/interface/GasEstimator.cpp | 87 -------------------------- libsolidity/interface/GasEstimator.h | 16 ----- solc/CommandLineInterface.cpp | 12 ---- test/libsolidity/GasMeter.cpp | 41 ------------ 4 files changed, 156 deletions(-) diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp index 2b56977ff..8b7e79840 100644 --- a/libsolidity/interface/GasEstimator.cpp +++ b/libsolidity/interface/GasEstimator.cpp @@ -42,93 +42,6 @@ using namespace solidity::evmasm; using namespace solidity::frontend; using namespace solidity::langutil; -GasEstimator::ASTGasConsumptionSelfAccumulated GasEstimator::structuralEstimation( - AssemblyItems const& _items, - vector const& _ast -) const -{ - solAssert(std::count(_ast.begin(), _ast.end(), nullptr) == 0, ""); - map particularCosts; - - ControlFlowGraph cfg(_items); - for (BasicBlock const& block: cfg.optimisedBlocks()) - { - solAssert(!!block.startState, ""); - GasMeter meter(block.startState->copy(), m_evmVersion); - auto const end = _items.begin() + static_cast(block.end); - for (auto iter = _items.begin() + static_cast(block.begin); iter != end; ++iter) - particularCosts[iter->location()] += meter.estimateMax(*iter); - } - - set finestNodes = finestNodesAtLocation(_ast); - ASTGasConsumptionSelfAccumulated gasCosts; - auto onNode = [&](ASTNode const& _node) - { - if (!finestNodes.count(&_node)) - return true; - gasCosts[&_node][0] = gasCosts[&_node][1] = particularCosts[_node.location()]; - return true; - }; - auto onEdge = [&](ASTNode const& _parent, ASTNode const& _child) - { - gasCosts[&_parent][1] += gasCosts[&_child][1]; - }; - ASTReduce folder(onNode, onEdge); - for (ASTNode const* ast: _ast) - ast->accept(folder); - - return gasCosts; -} - -map GasEstimator::breakToStatementLevel( - ASTGasConsumptionSelfAccumulated const& _gasCosts, - vector const& _roots -) -{ - solAssert(std::count(_roots.begin(), _roots.end(), nullptr) == 0, ""); - // first pass: statementDepth[node] is the distance from the deepend statement to node - // in direction of the tree root (or undefined if not possible) - map statementDepth; - auto onNodeFirstPass = [&](ASTNode const& _node) - { - if (dynamic_cast(&_node)) - statementDepth[&_node] = 0; - return true; - }; - auto onEdgeFirstPass = [&](ASTNode const& _parent, ASTNode const& _child) - { - if (statementDepth.count(&_child)) - statementDepth[&_parent] = max(statementDepth[&_parent], statementDepth[&_child] + 1); - }; - ASTReduce firstPass(onNodeFirstPass, onEdgeFirstPass); - for (ASTNode const* node: _roots) - node->accept(firstPass); - - // we use the location of a node if - // - its statement depth is 0 or - // - its statement depth is undefined but the parent's statement depth is at least 1 - map gasCosts; - auto onNodeSecondPass = [&](ASTNode const& _node) - { - return statementDepth.count(&_node); - }; - auto onEdgeSecondPass = [&](ASTNode const& _parent, ASTNode const& _child) - { - bool useNode = false; - if (statementDepth.count(&_child)) - useNode = statementDepth[&_child] == 0; - else - useNode = statementDepth.count(&_parent) && statementDepth.at(&_parent) > 0; - if (useNode) - gasCosts[&_child] = _gasCosts.at(&_child)[1]; - }; - ASTReduce secondPass(onNodeSecondPass, onEdgeSecondPass); - for (ASTNode const* node: _roots) - node->accept(secondPass); - // gasCosts should only contain non-overlapping locations - return gasCosts; -} - GasEstimator::GasConsumption GasEstimator::functionalEstimation( AssemblyItems const& _items, string const& _signature diff --git a/libsolidity/interface/GasEstimator.h b/libsolidity/interface/GasEstimator.h index f814c8b8d..5f564cb0a 100644 --- a/libsolidity/interface/GasEstimator.h +++ b/libsolidity/interface/GasEstimator.h @@ -48,22 +48,6 @@ public: explicit GasEstimator(langutil::EVMVersion _evmVersion): m_evmVersion(_evmVersion) {} - /// Estimates the gas consumption for every assembly item in the given assembly and stores - /// it by source location. - /// @returns a mapping from each AST node to a pair of its particular and syntactically accumulated gas costs. - ASTGasConsumptionSelfAccumulated structuralEstimation( - evmasm::AssemblyItems const& _items, - std::vector const& _ast - ) const; - /// @returns a mapping from nodes with non-overlapping source locations to gas consumptions such that - /// the following source locations are part of the mapping: - /// 1. source locations of statements that do not contain other statements - /// 2. maximal source locations that do not overlap locations coming from the first rule - static ASTGasConsumption breakToStatementLevel( - ASTGasConsumptionSelfAccumulated const& _gasCosts, - std::vector const& _roots - ); - /// @returns the estimated gas consumption by the (public or external) function with the /// given signature. If no signature is given, estimates the maximum gas usage. GasConsumption functionalEstimation( diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 9aa09878e..2d0c6c33f 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -1638,18 +1638,6 @@ void CommandLineInterface::handleAst(string const& _argStr) vector asts; for (auto const& sourceCode: m_sourceCodes) asts.push_back(&m_compiler->ast(sourceCode.first)); - map gasCosts; - for (auto const& contract: m_compiler->contractNames()) - if (m_compiler->compilationSuccessful()) - if (auto const* assemblyItems = m_compiler->runtimeAssemblyItems(contract)) - { - auto ret = GasEstimator::breakToStatementLevel( - GasEstimator(m_evmVersion).structuralEstimation(*assemblyItems, asts), - asts - ); - for (auto const& it: ret) - gasCosts[it.first] += it.second; - } bool legacyFormat = !m_args.count(g_argAstCompactJson); if (m_args.count(g_argOutputDir)) diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index 412dfb9d7..4071f9c98 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -49,14 +49,6 @@ public: m_compiler.setOptimiserSettings(solidity::test::CommonOptions::get().optimize); m_compiler.setEVMVersion(m_evmVersion); BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed"); - - AssemblyItems const* items = m_compiler.runtimeAssemblyItems(m_compiler.lastContractName()); - ASTNode const& sourceUnit = m_compiler.ast(""); - BOOST_REQUIRE(items != nullptr); - m_gasCosts = GasEstimator::breakToStatementLevel( - GasEstimator(solidity::test::CommonOptions::get().evmVersion()).structuralEstimation(*items, vector({&sourceUnit})), - {&sourceUnit} - ); } void testCreationTimeGas(string const& _sourceCode, u256 const& _tolerance = u256(0)) @@ -118,43 +110,10 @@ public: gas += i != 0 ? GasCosts::txDataNonZeroGas(evmVersion) : GasCosts::txDataZeroGas; return gas; } - -protected: - map m_gasCosts; }; BOOST_FIXTURE_TEST_SUITE(GasMeterTests, GasMeterTestFramework) -BOOST_AUTO_TEST_CASE(non_overlapping_filtered_costs) -{ - char const* sourceCode = R"( - contract test { - bytes x; - function f(uint a) public returns (uint b) { - for (; a < 200; ++a) { - x.push(0x09); - b = a * a; - } - return f(a - 1); - } - } - )"; - compile(sourceCode); - for (auto first = m_gasCosts.cbegin(); first != m_gasCosts.cend(); ++first) - { - auto second = first; - for (++second; second != m_gasCosts.cend(); ++second) - if (first->first->location().intersects(second->first->location())) - { - BOOST_CHECK_MESSAGE(false, "Source locations should not overlap!"); - langutil::SourceReferenceFormatter formatter(cout); - - formatter.printSourceLocation(&first->first->location()); - formatter.printSourceLocation(&second->first->location()); - } - } -} - BOOST_AUTO_TEST_CASE(simple_contract) { // Tests a simple "deploy contract" code without constructor. The actual contract is not relevant. From 99d48348de1f6408acdc7790923eedc4ff4cb781 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Wed, 7 Oct 2020 13:38:46 +0200 Subject: [PATCH 2/2] Eliminate dead ASTReduce. --- libsolidity/ast/ASTVisitor.h | 42 ------------------------------------ 1 file changed, 42 deletions(-) diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index c5aeba402..0f2d73348 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -313,46 +313,4 @@ private: std::function m_onEndVisit; }; -/** - * Utility class that visits the AST in depth-first order and calls a function on each node and each edge. - * Child nodes are only visited if the node callback of the parent returns true. - * The node callback of a parent is called before any edge or node callback involving the children. - * The edge callbacks of all children are called before the edge callback of the parent. - * This way, the node callback can be used as an initializing callback and the edge callbacks can be - * used to compute a "reduce" function. - */ -class ASTReduce: public ASTConstVisitor -{ -public: - /** - * Constructs a new ASTReduce object with the given callback functions. - * @param _onNode called for each node, before its child edges and nodes, should return true to descend deeper - * @param _onEdge called for each edge with (parent, child) - */ - ASTReduce( - std::function _onNode, - std::function _onEdge - ): m_onNode(std::move(_onNode)), m_onEdge(std::move(_onEdge)) - { - } - -protected: - bool visitNode(ASTNode const& _node) override - { - m_parents.push_back(&_node); - return m_onNode(_node); - } - void endVisitNode(ASTNode const& _node) override - { - m_parents.pop_back(); - if (!m_parents.empty()) - m_onEdge(*m_parents.back(), _node); - } - -private: - std::vector m_parents; - std::function m_onNode; - std::function m_onEdge; -}; - }