Coverage Report

Created: 2022-08-24 06:55

/src/solidity/libyul/backends/evm/EVMCodeTransform.h
Line
Count
Source
1
/*
2
  This file is part of solidity.
3
4
  solidity is free software: you can redistribute it and/or modify
5
  it under the terms of the GNU General Public License as published by
6
  the Free Software Foundation, either version 3 of the License, or
7
  (at your option) any later version.
8
9
  solidity is distributed in the hope that it will be useful,
10
  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
  GNU General Public License for more details.
13
14
  You should have received a copy of the GNU General Public License
15
  along with solidity.  If not, see <http://www.gnu.org/licenses/>.
16
*/
17
// SPDX-License-Identifier: GPL-3.0
18
/**
19
 * Code generator for translating Yul / inline assembly to EVM.
20
 */
21
22
#pragma once
23
24
#include <libyul/backends/evm/EVMDialect.h>
25
#include <libyul/backends/evm/VariableReferenceCounter.h>
26
#include <libyul/optimiser/ASTWalker.h>
27
#include <libyul/AST.h>
28
#include <libyul/Scope.h>
29
30
#include <optional>
31
#include <stack>
32
33
namespace solidity::langutil
34
{
35
class ErrorReporter;
36
}
37
38
namespace solidity::yul
39
{
40
41
struct AsmAnalysisInfo;
42
43
struct CodeTransformContext
44
{
45
  std::map<Scope::Function const*, AbstractAssembly::LabelID> functionEntryIDs;
46
  std::map<Scope::Variable const*, size_t> variableStackHeights;
47
  std::map<Scope::Variable const*, unsigned> variableReferences;
48
49
  struct JumpInfo
50
  {
51
    AbstractAssembly::LabelID label;  ///< Jump's LabelID to jump to.
52
    int targetStackHeight;            ///< Stack height after the jump.
53
  };
54
55
  struct ForLoopLabels
56
  {
57
    JumpInfo post; ///< Jump info for jumping to post branch.
58
    JumpInfo done; ///< Jump info for jumping to done branch.
59
  };
60
61
  std::stack<ForLoopLabels> forLoopStack;
62
};
63
64
class CodeTransform
65
{
66
public:
67
  /// Use named labels for functions 1) Yes and check that the names are unique
68
  /// 2) For none of the functions 3) for the first function of each name.
69
  enum class UseNamedLabels { YesAndForceUnique, Never, ForFirstFunctionOfEachName };
70
71
  /// Create the code transformer.
72
  /// @param _identifierAccessCodeGen used to generate code for identifiers external to the inline assembly
73
  /// As a side-effect of its construction, translates the Yul code and appends it to the
74
  /// given assembly.
75
  /// Throws StackTooDeepError if a variable is not accessible or if a function has too
76
  /// many parameters.
77
  CodeTransform(
78
    AbstractAssembly& _assembly,
79
    AsmAnalysisInfo& _analysisInfo,
80
    Block const& _block,
81
    EVMDialect const& _dialect,
82
    BuiltinContext& _builtinContext,
83
    bool _allowStackOpt = false,
84
    ExternalIdentifierAccess::CodeGenerator const& _identifierAccessCodeGen = {},
85
    UseNamedLabels _useNamedLabelsForFunctions = UseNamedLabels::Never
86
  ): CodeTransform(
87
    _assembly,
88
    _analysisInfo,
89
    _block,
90
    _allowStackOpt,
91
    _dialect,
92
    _builtinContext,
93
    _identifierAccessCodeGen,
94
    _useNamedLabelsForFunctions,
95
    nullptr,
96
    {},
97
    std::nullopt
98
  )
99
380k
  {
100
380k
  }
101
102
380k
  std::vector<StackTooDeepError> const& stackErrors() const { return m_stackErrors; }
103
104
protected:
105
  using Context = CodeTransformContext;
106
107
  CodeTransform(
108
    AbstractAssembly& _assembly,
109
    AsmAnalysisInfo& _analysisInfo,
110
    Block const& _block,
111
    bool _allowStackOpt,
112
    EVMDialect const& _dialect,
113
    BuiltinContext& _builtinContext,
114
    ExternalIdentifierAccess::CodeGenerator _identifierAccessCodeGen,
115
    UseNamedLabels _useNamedLabelsForFunctions,
116
    std::shared_ptr<Context> _context,
117
    std::vector<TypedName> _delayedReturnVariables,
118
    std::optional<AbstractAssembly::LabelID> _functionExitLabel
119
  );
120
121
  void decreaseReference(YulString _name, Scope::Variable const& _var);
122
  bool unreferenced(Scope::Variable const& _var) const;
123
  /// Marks slots of variables that are not used anymore
124
  /// and were defined in the current scope for reuse.
125
  /// Also POPs unused topmost stack slots,
126
  /// unless @a _popUnusedSlotsAtStackTop is set to false.
127
  void freeUnusedVariables(bool _popUnusedSlotsAtStackTop = true);
128
  /// Marks the stack slot of @a _var to be reused.
129
  void deleteVariable(Scope::Variable const& _var);
130
131
public:
132
  void operator()(Literal const& _literal);
133
  void operator()(Identifier const& _identifier);
134
  void operator()(FunctionCall const&);
135
  void operator()(ExpressionStatement const& _statement);
136
  void operator()(Assignment const& _assignment);
137
  void operator()(VariableDeclaration const& _varDecl);
138
  void operator()(If const& _if);
139
  void operator()(Switch const& _switch);
140
  void operator()(FunctionDefinition const&);
141
  void operator()(ForLoop const&);
142
  void operator()(Break const&);
143
  void operator()(Continue const&);
144
  void operator()(Leave const&);
145
  void operator()(Block const& _block);
146
147
private:
148
  AbstractAssembly::LabelID labelFromIdentifier(Identifier const& _identifier);
149
  void createFunctionEntryID(FunctionDefinition const& _function);
150
  AbstractAssembly::LabelID functionEntryID(Scope::Function const& _scopeFunction) const;
151
  /// Generates code for an expression that is supposed to return a single value.
152
  void visitExpression(Expression const& _expression);
153
154
  void visitStatements(std::vector<Statement> const& _statements);
155
156
  /// Pops all variables declared in the block and checks that the stack height is equal
157
  /// to @a _blockStartStackHeight.
158
  void finalizeBlock(Block const& _block, std::optional<int> _blockStartStackHeight);
159
160
  void generateMultiAssignment(std::vector<Identifier> const& _variableNames);
161
  void generateAssignment(Identifier const& _variableName);
162
163
  /// Determines the stack height difference to the given variables. Throws
164
  /// if it is not yet in scope or the height difference is too large. Returns
165
  /// the (positive) stack height difference otherwise.
166
  /// @param _forSwap if true, produces stack error if the difference is invalid for a swap
167
  ///                 opcode, otherwise checks for validity for a dup opcode.
168
  size_t variableHeightDiff(Scope::Variable const& _var, YulString _name, bool _forSwap);
169
170
  /// Determines the stack height of the given variable. Throws if the variable is not in scope.
171
  int variableStackHeight(YulString _name) const;
172
173
  void expectDeposit(int _deposit, int _oldHeight) const;
174
175
  /// Stores the stack error in the list of errors, appends an invalid opcode
176
  /// and corrects the stack height to the target stack height.
177
  void stackError(StackTooDeepError _error, int _targetStackSize);
178
179
  /// Ensures stack height is down to @p _targetDepth by appending POP instructions to the output assembly.
180
  /// Returns the number of POP statements that have been appended.
181
  int appendPopUntil(int _targetDepth);
182
183
  /// Allocates stack slots for remaining delayed return values and sets the function exit stack height.
184
  void setupReturnVariablesAndFunctionExit();
185
  bool returnVariablesAndFunctionExitAreSetup() const
186
9.05M
  {
187
9.05M
    return m_functionExitStackHeight.has_value();
188
9.05M
  }
189
  bool isInsideFunction() const
190
8.77M
  {
191
8.77M
    return m_functionExitLabel.has_value();
192
8.77M
  }
193
194
  AbstractAssembly& m_assembly;
195
  AsmAnalysisInfo& m_info;
196
  Scope* m_scope = nullptr;
197
  EVMDialect const& m_dialect;
198
  BuiltinContext& m_builtinContext;
199
  bool const m_allowStackOpt = true;
200
  UseNamedLabels const m_useNamedLabelsForFunctions = UseNamedLabels::Never;
201
  std::set<YulString> m_assignedNamedLabels;
202
  ExternalIdentifierAccess::CodeGenerator m_identifierAccessCodeGen;
203
  std::shared_ptr<Context> m_context;
204
205
  /// Set of variables whose reference counter has reached zero,
206
  /// and whose stack slot will be marked as unused once we reach
207
  /// statement level in the scope where the variable was defined.
208
  std::set<Scope::Variable const*> m_variablesScheduledForDeletion;
209
  std::set<int> m_unusedStackSlots;
210
211
  /// A list of return variables for which no stack slots have been assigned yet.
212
  std::vector<TypedName> m_delayedReturnVariables;
213
214
  /// Function exit label. Used as jump target for ``leave``.
215
  std::optional<AbstractAssembly::LabelID> m_functionExitLabel;
216
  /// The required stack height at the function exit label.
217
  /// This is the minimal stack height covering all return variables. Only set after all
218
  /// return variables were assigned slots.
219
  std::optional<int> m_functionExitStackHeight;
220
221
  std::vector<StackTooDeepError> m_stackErrors;
222
};
223
224
}