/src/solidity/libsolidity/codegen/ir/IRGenerationContext.h
Line | Count | Source (jump to first uncovered line) |
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 | | * Class that contains contextual information during IR generation. |
20 | | */ |
21 | | |
22 | | #pragma once |
23 | | |
24 | | #include <libsolidity/ast/AST.h> |
25 | | #include <libsolidity/codegen/ir/IRVariable.h> |
26 | | #include <libsolidity/interface/DebugSettings.h> |
27 | | |
28 | | #include <libsolidity/codegen/MultiUseYulFunctionCollector.h> |
29 | | #include <libsolidity/codegen/ir/Common.h> |
30 | | |
31 | | #include <liblangutil/CharStreamProvider.h> |
32 | | #include <liblangutil/DebugInfoSelection.h> |
33 | | #include <liblangutil/EVMVersion.h> |
34 | | |
35 | | #include <libsolutil/Common.h> |
36 | | |
37 | | #include <set> |
38 | | #include <string> |
39 | | #include <memory> |
40 | | #include <deque> |
41 | | |
42 | | namespace solidity::frontend |
43 | | { |
44 | | |
45 | | class YulUtilFunctions; |
46 | | class ABIFunctions; |
47 | | |
48 | | using DispatchQueue = std::deque<FunctionDefinition const*>; |
49 | | using InternalDispatchMap = std::map<YulArity, DispatchQueue>; |
50 | | |
51 | | /** |
52 | | * Class that contains contextual information during IR generation. |
53 | | */ |
54 | | class IRGenerationContext |
55 | | { |
56 | | public: |
57 | | enum class ExecutionContext { Creation, Deployed }; |
58 | | |
59 | | IRGenerationContext( |
60 | | langutil::EVMVersion _evmVersion, |
61 | | std::optional<uint8_t> _eofVersion, |
62 | | ExecutionContext _executionContext, |
63 | | RevertStrings _revertStrings, |
64 | | std::map<std::string, unsigned> _sourceIndices, |
65 | | langutil::DebugInfoSelection const& _debugInfoSelection, |
66 | | langutil::CharStreamProvider const* _soliditySourceProvider |
67 | | ): |
68 | | m_evmVersion(_evmVersion), |
69 | | m_eofVersion(_eofVersion), |
70 | | m_executionContext(_executionContext), |
71 | | m_revertStrings(_revertStrings), |
72 | | m_sourceIndices(std::move(_sourceIndices)), |
73 | | m_debugInfoSelection(_debugInfoSelection), |
74 | | m_soliditySourceProvider(_soliditySourceProvider) |
75 | 14.2k | {} |
76 | | |
77 | 44.8k | MultiUseYulFunctionCollector& functionCollector() { return m_functions; } |
78 | | |
79 | | /// Adds a Solidity function to the function generation queue and returns the name of the |
80 | | /// corresponding Yul function. |
81 | | std::string enqueueFunctionForCodeGeneration(FunctionDefinition const& _function); |
82 | | |
83 | | /// Pops one item from the function generation queue. Must not be called if the queue is empty. |
84 | | FunctionDefinition const* dequeueFunctionForCodeGeneration(); |
85 | | |
86 | 41.9k | bool functionGenerationQueueEmpty() { return m_functionGenerationQueue.empty(); } |
87 | | |
88 | | /// Sets the most derived contract (the one currently being compiled)> |
89 | | void setMostDerivedContract(ContractDefinition const& _mostDerivedContract) |
90 | 9.52k | { |
91 | 9.52k | m_mostDerivedContract = &_mostDerivedContract; |
92 | 9.52k | } |
93 | | ContractDefinition const& mostDerivedContract() const; |
94 | | |
95 | | |
96 | | IRVariable const& addLocalVariable(VariableDeclaration const& _varDecl); |
97 | 21.5k | bool isLocalVariable(VariableDeclaration const& _varDecl) const { return m_localVariables.count(&_varDecl); } |
98 | | IRVariable const& localVariable(VariableDeclaration const& _varDecl); |
99 | | void resetLocalVariables(); |
100 | | |
101 | | /// Registers an immutable variable of the contract. |
102 | | /// Should only be called at construction time. |
103 | | void registerImmutableVariable(VariableDeclaration const& _varDecl); |
104 | | void registerLibraryAddressImmutable(); |
105 | | size_t libraryAddressImmutableOffset() const; |
106 | | size_t libraryAddressImmutableOffsetRelative() const; |
107 | | /// @returns the reserved memory for storing the value of the |
108 | | /// immutable @a _variable during contract creation. |
109 | | size_t immutableMemoryOffset(VariableDeclaration const& _variable) const; |
110 | | size_t immutableMemoryOffsetRelative(VariableDeclaration const& _variable) const; |
111 | | /// @returns the reserved memory and resets it to mark it as used. |
112 | | /// Intended to be used only once for initializing the free memory pointer |
113 | | /// to after the area used for immutables. |
114 | | size_t reservedMemory(); |
115 | | size_t reservedMemorySize() const; |
116 | | |
117 | | void addStateVariable(VariableDeclaration const& _varDecl, u256 _storageOffset, unsigned _byteOffset); |
118 | 13.2k | bool isStateVariable(VariableDeclaration const& _varDecl) const { return m_stateVariables.count(&_varDecl); } |
119 | | std::pair<u256, unsigned> storageLocationOfStateVariable(VariableDeclaration const& _varDecl) const |
120 | 6.79k | { |
121 | 6.79k | solAssert(isStateVariable(_varDecl), ""); |
122 | 6.79k | return m_stateVariables.at(&_varDecl); |
123 | 6.79k | } |
124 | | |
125 | | std::string newYulVariable(); |
126 | | |
127 | | void initializeInternalDispatch(InternalDispatchMap _internalDispatchMap); |
128 | | InternalDispatchMap consumeInternalDispatchMap(); |
129 | 23.8k | bool internalDispatchClean() const { return m_internalDispatchMap.empty(); } |
130 | | |
131 | | /// Notifies the context that a function call that needs to go through internal dispatch was |
132 | | /// encountered while visiting the AST. This ensures that the corresponding dispatch function |
133 | | /// gets added to the dispatch map even if there are no entries in it (which may happen if |
134 | | /// the code contains a call to an uninitialized function variable). |
135 | | void internalFunctionCalledThroughDispatch(YulArity const& _arity); |
136 | | |
137 | | /// Adds a function to the internal dispatch. |
138 | | void addToInternalDispatch(FunctionDefinition const& _function); |
139 | | |
140 | | /// @returns a new copy of the utility function generator (but using the same function set). |
141 | | YulUtilFunctions utils(); |
142 | | |
143 | 2.47k | langutil::EVMVersion evmVersion() const { return m_evmVersion; } |
144 | 11.5k | std::optional<uint8_t> eofVersion() const { return m_eofVersion; } |
145 | 23 | ExecutionContext executionContext() const { return m_executionContext; } |
146 | | |
147 | 193 | void setArithmetic(Arithmetic _value) { m_arithmetic = _value; } |
148 | 12.3k | Arithmetic arithmetic() const { return m_arithmetic; } |
149 | | |
150 | | ABIFunctions abiFunctions(); |
151 | | |
152 | 20.5k | RevertStrings revertStrings() const { return m_revertStrings; } |
153 | | |
154 | 9.52k | util::UniqueVector<ContractDefinition const*> const& subObjectsCreated() const { return m_subObjects; } |
155 | 90 | void addSubObject(ContractDefinition const* _contractDefinition) { m_subObjects.pushBack(_contractDefinition); } |
156 | | |
157 | 9.52k | bool memoryUnsafeInlineAssemblySeen() const { return m_memoryUnsafeInlineAssemblySeen; } |
158 | 64 | void setMemoryUnsafeInlineAssemblySeen() { m_memoryUnsafeInlineAssemblySeen = true; } |
159 | | |
160 | 130k | std::map<std::string, unsigned> const& sourceIndices() const { return m_sourceIndices; } |
161 | 111k | void markSourceUsed(std::string const& _name) { m_usedSourceNames.insert(_name); } |
162 | 9.52k | std::set<std::string> const& usedSourceNames() const { return m_usedSourceNames; } |
163 | | |
164 | 0 | bool immutableRegistered(VariableDeclaration const& _varDecl) const { return m_immutableVariables.count(&_varDecl); } |
165 | | |
166 | 135k | langutil::DebugInfoSelection debugInfoSelection() const { return m_debugInfoSelection; } |
167 | 121k | langutil::CharStreamProvider const* soliditySourceProvider() const { return m_soliditySourceProvider; } |
168 | 4.76k | std::map<VariableDeclaration const*, size_t> const& immutableVariables() const { return m_immutableVariables; } |
169 | | void setImmutableVariables(std::map<VariableDeclaration const*, size_t> _immutableVariables) |
170 | 0 | { |
171 | 0 | solAssert(m_eofVersion.has_value()); |
172 | 0 | solAssert(m_executionContext == ExecutionContext::Deployed); |
173 | 0 | m_immutableVariables = std::move(_immutableVariables); |
174 | 0 | } |
175 | | void setLibraryAddressImmutableOffset(size_t _libraryAddressImmutableOffset) |
176 | 0 | { |
177 | 0 | solAssert(m_eofVersion.has_value()); |
178 | 0 | solAssert(m_executionContext == ExecutionContext::Deployed); |
179 | 0 | m_libraryAddressImmutableOffset = _libraryAddressImmutableOffset; |
180 | 0 | } |
181 | | |
182 | | private: |
183 | | langutil::EVMVersion m_evmVersion; |
184 | | std::optional<uint8_t> m_eofVersion; |
185 | | ExecutionContext m_executionContext; |
186 | | RevertStrings m_revertStrings; |
187 | | std::map<std::string, unsigned> m_sourceIndices; |
188 | | std::set<std::string> m_usedSourceNames; |
189 | | ContractDefinition const* m_mostDerivedContract = nullptr; |
190 | | std::map<VariableDeclaration const*, IRVariable> m_localVariables; |
191 | | /// Memory offsets reserved for the values of immutable variables during contract creation. |
192 | | /// This map is empty in the legacy runtime context and may be not empty in EOF runtime context. |
193 | | std::map<VariableDeclaration const*, size_t> m_immutableVariables; |
194 | | std::optional<size_t> m_libraryAddressImmutableOffset; |
195 | | /// Total amount of reserved memory. Reserved memory is used to store |
196 | | /// immutable variables during contract creation. |
197 | | std::optional<size_t> m_reservedMemory = {0}; |
198 | | /// Storage offsets of state variables |
199 | | std::map<VariableDeclaration const*, std::pair<u256, unsigned>> m_stateVariables; |
200 | | MultiUseYulFunctionCollector m_functions; |
201 | | size_t m_varCounter = 0; |
202 | | /// Whether to use checked or wrapping arithmetic. |
203 | | Arithmetic m_arithmetic = Arithmetic::Checked; |
204 | | |
205 | | /// Flag indicating whether any memory-unsafe inline assembly block was seen. |
206 | | bool m_memoryUnsafeInlineAssemblySeen = false; |
207 | | |
208 | | /// Function definitions queued for code generation. They're the Solidity functions whose calls |
209 | | /// were discovered by the IR generator during AST traversal. |
210 | | /// Note that the queue gets filled in a lazy way - new definitions can be added while the |
211 | | /// collected ones get removed and traversed. |
212 | | /// The order and duplicates are relevant here |
213 | | /// (see: IRGenerationContext::[enqueue|dequeue]FunctionForCodeGeneration) |
214 | | DispatchQueue m_functionGenerationQueue; |
215 | | |
216 | | /// Collection of functions that need to be callable via internal dispatch. |
217 | | /// Note that having a key with an empty set of functions is a valid situation. It means that |
218 | | /// the code contains a call via a pointer even though a specific function is never assigned to it. |
219 | | /// It will fail at runtime but the code must still compile. |
220 | | InternalDispatchMap m_internalDispatchMap; |
221 | | |
222 | | util::UniqueVector<ContractDefinition const*> m_subObjects; |
223 | | |
224 | | langutil::DebugInfoSelection m_debugInfoSelection = {}; |
225 | | langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr; |
226 | | }; |
227 | | |
228 | | } |