aboutsummaryrefslogtreecommitdiffstats
path: root/libyul/optimiser/ExpressionSplitter.cpp
blob: 334e33f451420de1fae6c324f6304a6d10e24d4f (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
/*
    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/>.
*/
/**
 * Optimiser component that turns complex expressions into multiple variable
 * declarations.
 */

#include <libyul/optimiser/ExpressionSplitter.h>

#include <libyul/optimiser/ASTWalker.h>

#include <libyul/AsmData.h>
#include <libyul/Dialect.h>

#include <libdevcore/CommonData.h>

#include <boost/range/adaptor/reversed.hpp>

using namespace std;
using namespace dev;
using namespace langutil;
using namespace yul;
using namespace dev::solidity;

void ExpressionSplitter::operator()(FunctionalInstruction& _instruction)
{
    for (auto& arg: _instruction.arguments | boost::adaptors::reversed)
        outlineExpression(arg);
}

void ExpressionSplitter::operator()(FunctionCall& _funCall)
{
    if (BuiltinFunction const* builtin = m_dialect.builtin(_funCall.functionName.name))
        if (builtin->literalArguments)
            // We cannot outline function arguments that have to be literals
            return;

    for (auto& arg: _funCall.arguments | boost::adaptors::reversed)
        outlineExpression(arg);
}

void ExpressionSplitter::operator()(If& _if)
{
    outlineExpression(*_if.condition);
    (*this)(_if.body);
}

void ExpressionSplitter::operator()(Switch& _switch)
{
    outlineExpression(*_switch.expression);
    for (auto& _case: _switch.cases)
        // Do not visit the case expression, nothing to split there.
        (*this)(_case.body);
}

void ExpressionSplitter::operator()(ForLoop& _loop)
{
    (*this)(_loop.pre);
    // Do not visit the condition because we cannot split expressions there.
    (*this)(_loop.post);
    (*this)(_loop.body);
}

void ExpressionSplitter::operator()(Block& _block)
{
    vector<Statement> saved;
    swap(saved, m_statementsToPrefix);

    function<boost::optional<vector<Statement>>(Statement&)> f =
            [&](Statement& _statement) -> boost::optional<vector<Statement>> {
        m_statementsToPrefix.clear();
        visit(_statement);
        if (m_statementsToPrefix.empty())
            return {};
        m_statementsToPrefix.emplace_back(std::move(_statement));
        return std::move(m_statementsToPrefix);
    };
    iterateReplacing(_block.statements, f);

    swap(saved, m_statementsToPrefix);
}

void ExpressionSplitter::outlineExpression(Expression& _expr)
{
    if (_expr.type() == typeid(Identifier))
        return;

    visit(_expr);

    SourceLocation location = locationOf(_expr);
    YulString var = m_nameDispenser.newName({});
    m_statementsToPrefix.emplace_back(VariableDeclaration{
        location,
        {{TypedName{location, var, {}}}},
        make_shared<Expression>(std::move(_expr))
    });
    _expr = Identifier{location, var};
}