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/OptimiserSettings.h>
27 #include <libsolidity/interface/DebugSettings.h>
28 
29 #include <libsolidity/codegen/MultiUseYulFunctionCollector.h>
30 #include <libsolidity/codegen/ir/Common.h>
31 
32 #include <liblangutil/CharStreamProvider.h>
33 #include <liblangutil/DebugInfoSelection.h>
34 #include <liblangutil/EVMVersion.h>
35 
36 #include <libsolutil/Common.h>
37 
38 #include <set>
39 #include <string>
40 #include <memory>
41 #include <vector>
42 
43 namespace solidity::frontend
44 {
45 
46 class YulUtilFunctions;
47 class ABIFunctions;
48 
49 struct AscendingFunctionIDCompare
50 {
operatorAscendingFunctionIDCompare51 	bool operator()(FunctionDefinition const* _f1, FunctionDefinition const* _f2) const
52 	{
53 		// NULLs always first.
54 		if (_f1 != nullptr && _f2 != nullptr)
55 			return _f1->id() < _f2->id();
56 		else
57 			return _f1 == nullptr;
58 	}
59 };
60 
61 using DispatchSet = std::set<FunctionDefinition const*, AscendingFunctionIDCompare>;
62 using InternalDispatchMap = std::map<YulArity, DispatchSet>;
63 
64 /**
65  * Class that contains contextual information during IR generation.
66  */
67 class IRGenerationContext
68 {
69 public:
70 	enum class ExecutionContext { Creation, Deployed };
71 
IRGenerationContext(langutil::EVMVersion _evmVersion,ExecutionContext _executionContext,RevertStrings _revertStrings,OptimiserSettings _optimiserSettings,std::map<std::string,unsigned> _sourceIndices,langutil::DebugInfoSelection const & _debugInfoSelection,langutil::CharStreamProvider const * _soliditySourceProvider)72 	IRGenerationContext(
73 		langutil::EVMVersion _evmVersion,
74 		ExecutionContext _executionContext,
75 		RevertStrings _revertStrings,
76 		OptimiserSettings _optimiserSettings,
77 		std::map<std::string, unsigned> _sourceIndices,
78 		langutil::DebugInfoSelection const& _debugInfoSelection,
79 		langutil::CharStreamProvider const* _soliditySourceProvider
80 	):
81 		m_evmVersion(_evmVersion),
82 		m_executionContext(_executionContext),
83 		m_revertStrings(_revertStrings),
84 		m_optimiserSettings(std::move(_optimiserSettings)),
85 		m_sourceIndices(std::move(_sourceIndices)),
86 		m_debugInfoSelection(_debugInfoSelection),
87 		m_soliditySourceProvider(_soliditySourceProvider)
88 	{}
89 
functionCollector()90 	MultiUseYulFunctionCollector& functionCollector() { return m_functions; }
91 
92 	/// Adds a Solidity function to the function generation queue and returns the name of the
93 	/// corresponding Yul function.
94 	std::string enqueueFunctionForCodeGeneration(FunctionDefinition const& _function);
95 
96 	/// Pops one item from the function generation queue. Must not be called if the queue is empty.
97 	FunctionDefinition const* dequeueFunctionForCodeGeneration();
98 
functionGenerationQueueEmpty()99 	bool functionGenerationQueueEmpty() { return m_functionGenerationQueue.empty(); }
100 
101 	/// Sets the most derived contract (the one currently being compiled)>
setMostDerivedContract(ContractDefinition const & _mostDerivedContract)102 	void setMostDerivedContract(ContractDefinition const& _mostDerivedContract)
103 	{
104 		m_mostDerivedContract = &_mostDerivedContract;
105 	}
106 	ContractDefinition const& mostDerivedContract() const;
107 
108 
109 	IRVariable const& addLocalVariable(VariableDeclaration const& _varDecl);
isLocalVariable(VariableDeclaration const & _varDecl)110 	bool isLocalVariable(VariableDeclaration const& _varDecl) const { return m_localVariables.count(&_varDecl); }
111 	IRVariable const& localVariable(VariableDeclaration const& _varDecl);
112 	void resetLocalVariables();
113 
114 	/// Registers an immutable variable of the contract.
115 	/// Should only be called at construction time.
116 	void registerImmutableVariable(VariableDeclaration const& _varDecl);
117 	/// @returns the reserved memory for storing the value of the
118 	/// immutable @a _variable during contract creation.
119 	size_t immutableMemoryOffset(VariableDeclaration const& _variable) const;
120 	/// @returns the reserved memory and resets it to mark it as used.
121 	/// Intended to be used only once for initializing the free memory pointer
122 	/// to after the area used for immutables.
123 	size_t reservedMemory();
124 
125 	void addStateVariable(VariableDeclaration const& _varDecl, u256 _storageOffset, unsigned _byteOffset);
isStateVariable(VariableDeclaration const & _varDecl)126 	bool isStateVariable(VariableDeclaration const& _varDecl) const { return m_stateVariables.count(&_varDecl); }
storageLocationOfStateVariable(VariableDeclaration const & _varDecl)127 	std::pair<u256, unsigned> storageLocationOfStateVariable(VariableDeclaration const& _varDecl) const
128 	{
129 		solAssert(isStateVariable(_varDecl), "");
130 		return m_stateVariables.at(&_varDecl);
131 	}
132 
133 	std::string newYulVariable();
134 
135 	void initializeInternalDispatch(InternalDispatchMap _internalDispatchMap);
136 	InternalDispatchMap consumeInternalDispatchMap();
internalDispatchClean()137 	bool internalDispatchClean() const { return m_internalDispatchMap.empty(); }
138 
139 	/// Notifies the context that a function call that needs to go through internal dispatch was
140 	/// encountered while visiting the AST. This ensures that the corresponding dispatch function
141 	/// gets added to the dispatch map even if there are no entries in it (which may happen if
142 	/// the code contains a call to an uninitialized function variable).
143 	void internalFunctionCalledThroughDispatch(YulArity const& _arity);
144 
145 	/// Adds a function to the internal dispatch.
146 	void addToInternalDispatch(FunctionDefinition const& _function);
147 
148 	/// @returns a new copy of the utility function generator (but using the same function set).
149 	YulUtilFunctions utils();
150 
evmVersion()151 	langutil::EVMVersion evmVersion() const { return m_evmVersion; }
executionContext()152 	ExecutionContext executionContext() const { return m_executionContext; }
153 
setArithmetic(Arithmetic _value)154 	void setArithmetic(Arithmetic _value) { m_arithmetic = _value; }
arithmetic()155 	Arithmetic arithmetic() const { return m_arithmetic; }
156 
157 	ABIFunctions abiFunctions();
158 
revertStrings()159 	RevertStrings revertStrings() const { return m_revertStrings; }
160 
subObjectsCreated()161 	std::set<ContractDefinition const*, ASTNode::CompareByID>& subObjectsCreated() { return m_subObjects; }
162 
inlineAssemblySeen()163 	bool inlineAssemblySeen() const { return m_inlineAssemblySeen; }
setInlineAssemblySeen()164 	void setInlineAssemblySeen() { m_inlineAssemblySeen = true; }
165 
166 	/// @returns the runtime ID to be used for the function in the dispatch routine
167 	/// and for internal function pointers.
168 	/// @param _requirePresent if false, generates a new ID if not yet done.
169 	uint64_t internalFunctionID(FunctionDefinition const& _function, bool _requirePresent);
170 	/// Copies the internal function IDs from the @a _other. For use in transferring
171 	/// function IDs from constructor code to deployed code.
172 	void copyFunctionIDsFrom(IRGenerationContext const& _other);
173 
sourceIndices()174 	std::map<std::string, unsigned> const& sourceIndices() const { return m_sourceIndices; }
markSourceUsed(std::string const & _name)175 	void markSourceUsed(std::string const& _name) { m_usedSourceNames.insert(_name); }
usedSourceNames()176 	std::set<std::string> const& usedSourceNames() const { return m_usedSourceNames; }
177 
immutableRegistered(VariableDeclaration const & _varDecl)178 	bool immutableRegistered(VariableDeclaration const& _varDecl) const { return m_immutableVariables.count(&_varDecl); }
179 
debugInfoSelection()180 	langutil::DebugInfoSelection debugInfoSelection() const { return m_debugInfoSelection; }
soliditySourceProvider()181 	langutil::CharStreamProvider const* soliditySourceProvider() const { return m_soliditySourceProvider; }
182 
183 private:
184 	langutil::EVMVersion m_evmVersion;
185 	ExecutionContext m_executionContext;
186 	RevertStrings m_revertStrings;
187 	OptimiserSettings m_optimiserSettings;
188 	std::map<std::string, unsigned> m_sourceIndices;
189 	std::set<std::string> m_usedSourceNames;
190 	ContractDefinition const* m_mostDerivedContract = nullptr;
191 	std::map<VariableDeclaration const*, IRVariable> m_localVariables;
192 	/// Memory offsets reserved for the values of immutable variables during contract creation.
193 	/// This map is empty in the runtime context.
194 	std::map<VariableDeclaration const*, size_t> m_immutableVariables;
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 inline assembly block was seen.
206 	bool m_inlineAssemblySeen = 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 irrelevant here (hence std::set rather than std::queue) as
213 	/// long as the order of Yul functions in the generated code is deterministic and the same on
214 	/// all platforms - which is a property guaranteed by MultiUseYulFunctionCollector.
215 	DispatchSet m_functionGenerationQueue;
216 
217 	/// Collection of functions that need to be callable via internal dispatch.
218 	/// Note that having a key with an empty set of functions is a valid situation. It means that
219 	/// the code contains a call via a pointer even though a specific function is never assigned to it.
220 	/// It will fail at runtime but the code must still compile.
221 	InternalDispatchMap m_internalDispatchMap;
222 	/// Map used by @a internalFunctionID.
223 	std::map<int64_t, uint64_t> m_functionIDs;
224 
225 	std::set<ContractDefinition const*, ASTNode::CompareByID> m_subObjects;
226 
227 	langutil::DebugInfoSelection m_debugInfoSelection = {};
228 	langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr;
229 };
230 
231 }
232