/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 | | } |