aboutsummaryrefslogtreecommitdiffstats
path: root/libyul/optimiser/UnusedPruner.cpp
blob: 31aead82c54e88ecbae50a621a134def981c2f73 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/*(
    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/>.
*/
/**
 * Optimisation stage that removes unused variables and functions.
 */

#include <libyul/optimiser/UnusedPruner.h>

#include <libyul/optimiser/NameCollector.h>
#include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/Utilities.h>
#include <libyul/Exceptions.h>
#include <libyul/AsmData.h>

#include <boost/algorithm/cxx11/none_of.hpp>

using namespace std;
using namespace dev;
using namespace yul;

UnusedPruner::UnusedPruner(Block& _ast, set<YulString> const& _externallyUsedFunctions)
{
    ReferencesCounter counter;
    counter(_ast);

    m_references = counter.references();
    for (auto const& f: _externallyUsedFunctions)
        ++m_references[f];
}

void UnusedPruner::operator()(Block& _block)
{
    for (auto&& statement: _block.statements)
        if (statement.type() == typeid(FunctionDefinition))
        {
            FunctionDefinition& funDef = boost::get<FunctionDefinition>(statement);
            if (!used(funDef.name))
            {
                subtractReferences(ReferencesCounter::countReferences(funDef.body));
                statement = Block{std::move(funDef.location), {}};
            }
        }
        else if (statement.type() == typeid(VariableDeclaration))
        {
            VariableDeclaration& varDecl = boost::get<VariableDeclaration>(statement);
            // Multi-variable declarations are special. We can only remove it
            // if all vairables are unused and the right-hand-side is either
            // movable or it return a single value. In the latter case, we
            // replace `let a := f()` by `pop(f())` (in pure Yul, this will be
            // `drop(f())`).
            if (boost::algorithm::none_of(
                varDecl.variables,
                [=](TypedName const& _typedName) { return used(_typedName.name); }
            ))
            {
                if (!varDecl.value)
                    statement = Block{std::move(varDecl.location), {}};
                else if (MovableChecker(*varDecl.value).movable())
                {
                    subtractReferences(ReferencesCounter::countReferences(*varDecl.value));
                    statement = Block{std::move(varDecl.location), {}};
                }
                else if (varDecl.variables.size() == 1)
                    // In pure Yul, this should be replaced by a function call to `drop`
                    // instead of `pop`.
                    statement = ExpressionStatement{varDecl.location, FunctionalInstruction{
                        varDecl.location,
                        solidity::Instruction::POP,
                        {*std::move(varDecl.value)}
                    }};
            }
        }
        else if (statement.type() == typeid(ExpressionStatement))
        {
            ExpressionStatement& exprStmt = boost::get<ExpressionStatement>(statement);
            if (MovableChecker(exprStmt.expression).movable())
            {
                // pop(x) should be movable!
                subtractReferences(ReferencesCounter::countReferences(exprStmt.expression));
                statement = Block{std::move(exprStmt.location), {}};
            }
        }

    removeEmptyBlocks(_block);

    ASTModifier::operator()(_block);
}

void UnusedPruner::runUntilStabilised(Block& _ast, set<YulString> const& _externallyUsedFunctions)
{
    while (true)
    {
        UnusedPruner pruner(_ast, _externallyUsedFunctions);
        pruner(_ast);
        if (!pruner.shouldRunAgain())
            return;
    }
}

bool UnusedPruner::used(YulString _name) const
{
    return m_references.count(_name) && m_references.at(_name) > 0;
}

void UnusedPruner::subtractReferences(map<YulString, size_t> const& _subtrahend)
{
    for (auto const& ref: _subtrahend)
    {
        assertThrow(m_references.count(ref.first), OptimizerException, "");
        assertThrow(m_references.at(ref.first) >= ref.second, OptimizerException, "");
        m_references[ref.first] -= ref.second;
        m_shouldRunAgain = true;
    }
}