aboutsummaryrefslogtreecommitdiffstats
path: root/libyul/backends/evm/EVMCodeTransform.h
blob: f4696e812d1ce9b7095d4bff928f6178561bd1b2 (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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
    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/>.
*/
/**
 * Common code generator for translating Yul / inline assembly to EVM and EVM1.5.
 */

#include <libyul/backends/evm/EVMAssembly.h>

#include <libyul/ASTDataForward.h>

#include <libyul/AsmScope.h>

#include <boost/variant.hpp>
#include <boost/optional.hpp>

namespace dev
{
namespace solidity
{
class ErrorReporter;
namespace assembly
{
struct AsmAnalysisInfo;
}
}
namespace yul
{
class EVMAssembly;

class CodeTransform: public boost::static_visitor<>
{
public:
    /// Create the code transformer.
    /// @param _identifierAccess used to resolve identifiers external to the inline assembly
    CodeTransform(
        yul::AbstractAssembly& _assembly,
        solidity::assembly::AsmAnalysisInfo& _analysisInfo,
        bool _yul = false,
        bool _evm15 = false,
        ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess(),
        bool _useNamedLabelsForFunctions = false
    ): CodeTransform(
        _assembly,
        _analysisInfo,
        _yul,
        _evm15,
        _identifierAccess,
        _useNamedLabelsForFunctions,
        _assembly.stackHeight(),
        std::make_shared<Context>()
    )
    {
    }

protected:
    struct Context
    {
        using Scope = solidity::assembly::Scope;
        std::map<Scope::Label const*, AbstractAssembly::LabelID> labelIDs;
        std::map<Scope::Function const*, AbstractAssembly::LabelID> functionEntryIDs;
        std::map<Scope::Variable const*, int> variableStackHeights;
    };

    CodeTransform(
        yul::AbstractAssembly& _assembly,
        solidity::assembly::AsmAnalysisInfo& _analysisInfo,
        bool _yul,
        bool _evm15,
        ExternalIdentifierAccess const& _identifierAccess,
        bool _useNamedLabelsForFunctions,
        int _stackAdjustment,
        std::shared_ptr<Context> _context
    ):
        m_assembly(_assembly),
        m_info(_analysisInfo),
        m_yul(_yul),
        m_evm15(_evm15),
        m_useNamedLabelsForFunctions(_useNamedLabelsForFunctions),
        m_identifierAccess(_identifierAccess),
        m_stackAdjustment(_stackAdjustment),
        m_context(_context)
    {}

public:
    void operator()(Instruction const& _instruction);
    void operator()(Literal const& _literal);
    void operator()(Identifier const& _identifier);
    void operator()(FunctionalInstruction const& _instr);
    void operator()(FunctionCall const&);
    void operator()(ExpressionStatement const& _statement);
    void operator()(Label const& _label);
    void operator()(StackAssignment const& _assignment);
    void operator()(Assignment const& _assignment);
    void operator()(VariableDeclaration const& _varDecl);
    void operator()(If const& _if);
    void operator()(Switch const& _switch);
    void operator()(FunctionDefinition const&);
    void operator()(ForLoop const&);
    void operator()(Block const& _block);

private:
    AbstractAssembly::LabelID labelFromIdentifier(Identifier const& _identifier);
    /// @returns the label ID corresponding to the given label, allocating a new one if
    /// necessary.
    AbstractAssembly::LabelID labelID(solidity::assembly::Scope::Label const& _label);
    AbstractAssembly::LabelID functionEntryID(YulString _name, solidity::assembly::Scope::Function const& _function);
    /// Generates code for an expression that is supposed to return a single value.
    void visitExpression(Expression const& _expression);

    void visitStatements(std::vector<Statement> const& _statements);

    /// Pops all variables declared in the block and checks that the stack height is equal
    /// to @a _blackStartStackHeight.
    void finalizeBlock(Block const& _block, int _blockStartStackHeight);

    void generateMultiAssignment(std::vector<Identifier> const& _variableNames);
    void generateAssignment(Identifier const& _variableName);

    /// Determines the stack height difference to the given variables. Throws
    /// if it is not yet in scope or the height difference is too large. Returns
    /// the (positive) stack height difference otherwise.
    int variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap) const;

    void expectDeposit(int _deposit, int _oldHeight) const;

    void checkStackHeight(void const* _astElement) const;

    yul::AbstractAssembly& m_assembly;
    solidity::assembly::AsmAnalysisInfo& m_info;
    solidity::assembly::Scope* m_scope = nullptr;
    bool m_yul = false;
    bool m_evm15 = false;
    bool m_useNamedLabelsForFunctions = false;
    ExternalIdentifierAccess m_identifierAccess;
    /// Adjustment between the stack height as determined during the analysis phase
    /// and the stack height in the assembly. This is caused by an initial stack being present
    /// for inline assembly and different stack heights depending on the EVM backend used
    /// (EVM 1.0 or 1.5).
    int m_stackAdjustment = 0;
    std::shared_ptr<Context> m_context;
};

}
}