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  * @author Christian <c@ethdev.com>
20  * @date 2014
21  * Utilities for the solidity compiler.
22  */
23 
24 #pragma once
25 
26 #include <libsolidity/ast/ASTAnnotations.h>
27 #include <libsolidity/ast/ASTForward.h>
28 #include <libsolidity/ast/Types.h>
29 #include <libsolidity/codegen/ABIFunctions.h>
30 
31 #include <libsolidity/interface/DebugSettings.h>
32 #include <libsolidity/interface/OptimiserSettings.h>
33 
34 #include <libevmasm/Assembly.h>
35 #include <libevmasm/Instruction.h>
36 #include <liblangutil/ErrorReporter.h>
37 #include <liblangutil/EVMVersion.h>
38 #include <libsolutil/Common.h>
39 #include <libsolutil/ErrorCodes.h>
40 
41 #include <libyul/AsmAnalysisInfo.h>
42 #include <libyul/backends/evm/EVMDialect.h>
43 
44 #include <functional>
45 #include <ostream>
46 #include <stack>
47 #include <queue>
48 #include <utility>
49 #include <limits>
50 
51 namespace solidity::frontend
52 {
53 
54 class Compiler;
55 
56 /**
57  * Context to be shared by all units that compile the same contract.
58  * It stores the generated bytecode and the position of identifiers in memory and on the stack.
59  */
60 class CompilerContext
61 {
62 public:
63 	explicit CompilerContext(
64 		langutil::EVMVersion _evmVersion,
65 		RevertStrings _revertStrings,
66 		CompilerContext* _runtimeContext = nullptr
67 	):
m_asm(std::make_shared<evmasm::Assembly> ())68 		m_asm(std::make_shared<evmasm::Assembly>()),
69 		m_evmVersion(_evmVersion),
70 		m_revertStrings(_revertStrings),
71 		m_reservedMemory{0},
72 		m_runtimeContext(_runtimeContext),
73 		m_abiFunctions(m_evmVersion, m_revertStrings, m_yulFunctionCollector),
74 		m_yulUtilFunctions(m_evmVersion, m_revertStrings, m_yulFunctionCollector)
75 	{
76 		if (m_runtimeContext)
77 			m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data());
78 	}
79 
evmVersion()80 	langutil::EVMVersion const& evmVersion() const { return m_evmVersion; }
81 
setUseABICoderV2(bool _value)82 	void setUseABICoderV2(bool _value) { m_useABICoderV2 = _value; }
useABICoderV2()83 	bool useABICoderV2() const { return m_useABICoderV2; }
84 
85 	void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset);
86 	void addImmutable(VariableDeclaration const& _declaration);
87 
88 	/// @returns the reserved memory for storing the value of the immutable @a _variable during contract creation.
89 	size_t immutableMemoryOffset(VariableDeclaration const& _variable) const;
90 	/// @returns a list of slot names referring to the stack slots of an immutable variable.
91 	static std::vector<std::string> immutableVariableSlotNames(VariableDeclaration const& _variable);
92 
93 	/// @returns the reserved memory and resets it to mark it as used.
94 	size_t reservedMemory();
95 
96 	void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
97 	void removeVariable(Declaration const& _declaration);
98 	/// Removes all local variables currently allocated above _stackHeight.
99 	void removeVariablesAboveStackHeight(unsigned _stackHeight);
100 	/// Returns the number of currently allocated local variables.
101 	unsigned numberOfLocalVariables() const;
102 
setOtherCompilers(std::map<ContractDefinition const *,std::shared_ptr<Compiler const>> const & _otherCompilers)103 	void setOtherCompilers(std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers) { m_otherCompilers = _otherCompilers; }
104 	std::shared_ptr<evmasm::Assembly> compiledContract(ContractDefinition const& _contract) const;
105 	std::shared_ptr<evmasm::Assembly> compiledContractRuntime(ContractDefinition const& _contract) const;
106 
setStackOffset(int _offset)107 	void setStackOffset(int _offset) { m_asm->setDeposit(_offset); }
adjustStackOffset(int _adjustment)108 	void adjustStackOffset(int _adjustment) { m_asm->adjustDeposit(_adjustment); }
stackHeight()109 	unsigned stackHeight() const { solAssert(m_asm->deposit() >= 0, ""); return unsigned(m_asm->deposit()); }
110 
111 	bool isLocalVariable(Declaration const* _declaration) const;
isStateVariable(Declaration const * _declaration)112 	bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration) != 0; }
113 
114 	/// @returns the entry label of the given function and creates it if it does not exist yet.
115 	evmasm::AssemblyItem functionEntryLabel(Declaration const& _declaration);
116 	/// @returns the entry label of the given function. Might return an AssemblyItem of type
117 	/// UndefinedItem if it does not exist yet.
118 	evmasm::AssemblyItem functionEntryLabelIfExists(Declaration const& _declaration) const;
119 	/// @returns the function that overrides the given declaration from the most derived class just
120 	/// above _base in the current inheritance hierarchy.
121 	FunctionDefinition const& superFunction(FunctionDefinition const& _function, ContractDefinition const& _base);
122 	/// Sets the contract currently being compiled - the most derived one.
setMostDerivedContract(ContractDefinition const & _contract)123 	void setMostDerivedContract(ContractDefinition const& _contract) { m_mostDerivedContract = &_contract; }
124 	ContractDefinition const& mostDerivedContract() const;
125 
setArithmetic(Arithmetic _value)126 	void setArithmetic(Arithmetic _value) { m_arithmetic = _value; }
arithmetic()127 	Arithmetic arithmetic() const { return m_arithmetic; }
128 
129 	/// @returns the next function in the queue of functions that are still to be compiled
130 	/// (i.e. that were referenced during compilation but where we did not yet generate code for).
131 	/// Returns nullptr if the queue is empty. Does not remove the function from the queue,
132 	/// that will only be done by startFunction below.
133 	Declaration const* nextFunctionToCompile() const;
134 	/// Resets function specific members, inserts the function entry label and marks the function
135 	/// as "having code".
136 	void startFunction(Declaration const& _function);
137 
138 	/// Appends a call to the named low-level function and inserts the generator into the
139 	/// list of low-level-functions to be generated, unless it already exists.
140 	/// Note that the generator should not assume that objects are still alive when it is called,
141 	/// unless they are guaranteed to be alive for the whole run of the compiler (AST nodes, for example).
142 	void callLowLevelFunction(
143 		std::string const& _name,
144 		unsigned _inArgs,
145 		unsigned _outArgs,
146 		std::function<void(CompilerContext&)> const& _generator
147 	);
148 
149 	/// Appends a call to a yul function and registers the function as externally used.
150 	void callYulFunction(
151 		std::string const& _name,
152 		unsigned _inArgs,
153 		unsigned _outArgs
154 	);
155 
156 	/// Returns the tag of the named low-level function and inserts the generator into the
157 	/// list of low-level-functions to be generated, unless it already exists.
158 	/// Note that the generator should not assume that objects are still alive when it is called,
159 	/// unless they are guaranteed to be alive for the whole run of the compiler (AST nodes, for example).
160 	evmasm::AssemblyItem lowLevelFunctionTag(
161 		std::string const& _name,
162 		unsigned _inArgs,
163 		unsigned _outArgs,
164 		std::function<void(CompilerContext&)> const& _generator
165 	);
166 	/// Generates the code for missing low-level functions, i.e. calls the generators passed above.
167 	void appendMissingLowLevelFunctions();
abiFunctions()168 	ABIFunctions& abiFunctions() { return m_abiFunctions; }
utilFunctions()169 	YulUtilFunctions& utilFunctions() { return m_yulUtilFunctions; }
170 
171 	/// Appends concatenation of all generated Yul functions to the bytecode
172 	/// and stores the Yul source code to be returned by @a generatedYulUtilityCode.
173 	/// Should be called exactly once on each context.
174 	void appendYulUtilityFunctions(OptimiserSettings const& _optimiserSettings);
appendYulUtilityFunctionsRan()175 	bool appendYulUtilityFunctionsRan() const { return m_appendYulUtilityFunctionsRan; }
generatedYulUtilityCode()176 	std::string const& generatedYulUtilityCode() const { return m_generatedYulUtilityCode; }
yulUtilityFileName()177 	static std::string yulUtilityFileName() { return "#utility.yul"; }
178 
179 	/// Returns the distance of the given local variable from the bottom of the stack (of the current function).
180 	unsigned baseStackOffsetOfVariable(Declaration const& _declaration) const;
181 	/// If supplied by a value returned by @ref baseStackOffsetOfVariable(variable), returns
182 	/// the distance of that variable from the current top of the stack.
183 	unsigned baseToCurrentStackOffset(unsigned _baseOffset) const;
184 	/// Converts an offset relative to the current stack height to a value that can be used later
185 	/// with baseToCurrentStackOffset to point to the same stack element.
186 	unsigned currentToBaseStackOffset(unsigned _offset) const;
187 	/// @returns pair of slot and byte offset of the value inside this slot.
188 	std::pair<u256, unsigned> storageLocationOfVariable(Declaration const& _declaration) const;
189 
190 	/// Appends a JUMPI instruction to a new tag and @returns the tag
appendConditionalJump()191 	evmasm::AssemblyItem appendConditionalJump() { return m_asm->appendJumpI().tag(); }
192 	/// Appends a JUMPI instruction to @a _tag
appendConditionalJumpTo(evmasm::AssemblyItem const & _tag)193 	CompilerContext& appendConditionalJumpTo(evmasm::AssemblyItem const& _tag) { m_asm->appendJumpI(_tag); return *this; }
194 	/// Appends a JUMP to a new tag and @returns the tag
appendJumpToNew()195 	evmasm::AssemblyItem appendJumpToNew() { return m_asm->appendJump().tag(); }
196 	/// Appends a JUMP to a tag already on the stack
197 	CompilerContext& appendJump(evmasm::AssemblyItem::JumpType _jumpType = evmasm::AssemblyItem::JumpType::Ordinary);
198 	/// Appends code to revert with a Panic(uint256) error.
199 	CompilerContext& appendPanic(util::PanicCode _code);
200 	/// Appends code to revert with a Panic(uint256) error if the topmost stack element is nonzero.
201 	CompilerContext& appendConditionalPanic(util::PanicCode _code);
202 	/// Appends a REVERT(0, 0) call
203 	/// @param _message is an optional revert message used in debug mode
204 	CompilerContext& appendRevert(std::string const& _message = "");
205 	/// Appends a conditional REVERT-call, either forwarding the RETURNDATA or providing the
206 	/// empty string. Consumes the condition.
207 	/// If the current EVM version does not support RETURNDATA, uses REVERT but does not forward
208 	/// the data.
209 	/// @param _message is an optional revert message used in debug mode
210 	CompilerContext& appendConditionalRevert(bool _forwardReturnData = false, std::string const& _message = "");
211 	/// Appends a JUMP to a specific tag
212 	CompilerContext& appendJumpTo(
213 		evmasm::AssemblyItem const& _tag,
214 		evmasm::AssemblyItem::JumpType _jumpType = evmasm::AssemblyItem::JumpType::Ordinary
215 	) { *m_asm << _tag.pushTag(); return appendJump(_jumpType); }
216 	/// Appends pushing of a new tag and @returns the new tag.
pushNewTag()217 	evmasm::AssemblyItem pushNewTag() { return m_asm->append(m_asm->newPushTag()).tag(); }
218 	/// @returns a new tag without pushing any opcodes or data
newTag()219 	evmasm::AssemblyItem newTag() { return m_asm->newTag(); }
220 	/// @returns a new tag identified by name.
namedTag(std::string const & _name,size_t _params,size_t _returns,std::optional<uint64_t> _sourceID)221 	evmasm::AssemblyItem namedTag(std::string const& _name, size_t _params, size_t _returns, std::optional<uint64_t> _sourceID)
222 	{
223 		return m_asm->namedTag(_name, _params, _returns, _sourceID);
224 	}
225 	/// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
226 	/// on the stack. @returns the pushsub assembly item.
addSubroutine(evmasm::AssemblyPointer const & _assembly)227 	evmasm::AssemblyItem addSubroutine(evmasm::AssemblyPointer const& _assembly) { return m_asm->appendSubroutine(_assembly); }
228 	/// Pushes the size of the subroutine.
pushSubroutineSize(size_t _subRoutine)229 	void pushSubroutineSize(size_t _subRoutine) { m_asm->pushSubroutineSize(_subRoutine); }
230 	/// Pushes the offset of the subroutine.
pushSubroutineOffset(size_t _subRoutine)231 	void pushSubroutineOffset(size_t _subRoutine) { m_asm->pushSubroutineOffset(_subRoutine); }
232 	/// Pushes the size of the final program
appendProgramSize()233 	void appendProgramSize() { m_asm->appendProgramSize(); }
234 	/// Adds data to the data section, pushes a reference to the stack
appendData(bytes const & _data)235 	evmasm::AssemblyItem appendData(bytes const& _data) { return m_asm->append(_data); }
236 	/// Appends the address (virtual, will be filled in by linker) of a library.
appendLibraryAddress(std::string const & _identifier)237 	void appendLibraryAddress(std::string const& _identifier) { m_asm->appendLibraryAddress(_identifier); }
238 	/// Appends an immutable variable. The value will be filled in by the constructor.
appendImmutable(std::string const & _identifier)239 	void appendImmutable(std::string const& _identifier) { m_asm->appendImmutable(_identifier); }
240 	/// Appends an assignment to an immutable variable. Only valid in creation code.
appendImmutableAssignment(std::string const & _identifier)241 	void appendImmutableAssignment(std::string const& _identifier) { m_asm->appendImmutableAssignment(_identifier); }
242 	/// Appends a zero-address that can be replaced by something else at deploy time (if the
243 	/// position in bytecode is known).
appendDeployTimeAddress()244 	void appendDeployTimeAddress() { m_asm->append(evmasm::PushDeployTimeAddress); }
245 	/// Resets the stack of visited nodes with a new stack having only @c _node
246 	void resetVisitedNodes(ASTNode const* _node);
247 	/// Pops the stack of visited nodes
popVisitedNodes()248 	void popVisitedNodes() { m_visitedNodes.pop(); updateSourceLocation(); }
249 	/// Pushes an ASTNode to the stack of visited nodes
pushVisitedNodes(ASTNode const * _node)250 	void pushVisitedNodes(ASTNode const* _node) { m_visitedNodes.push(_node); updateSourceLocation(); }
251 
252 	/// Append elements to the current instruction list and adjust @a m_stackOffset.
253 	CompilerContext& operator<<(evmasm::AssemblyItem const& _item) { m_asm->append(_item); return *this; }
254 	CompilerContext& operator<<(evmasm::Instruction _instruction) { m_asm->append(_instruction); return *this; }
255 	CompilerContext& operator<<(u256 const& _value) { m_asm->append(_value); return *this; }
256 	CompilerContext& operator<<(bytes const& _data) { m_asm->append(_data); return *this; }
257 
258 	/// Appends inline assembly (strict-EVM dialect for the current version).
259 	/// @param _assembly the assembly text, should be a block.
260 	/// @param _localVariables assigns stack positions to variables with the last one being the stack top
261 	/// @param _externallyUsedFunctions a set of function names that are not to be renamed or removed.
262 	/// @param _system if true, this is a "system-level" assembly where all functions use named labels
263 	///                and the code is marked to be exported as "compiler-generated assembly utility file".
264 	/// @param _optimiserSettings settings for the Yul optimiser, which is run in this function already.
265 	/// @param _sourceName the name of the assembly file to be used for source locations
266 	void appendInlineAssembly(
267 		std::string const& _assembly,
268 		std::vector<std::string> const& _localVariables = std::vector<std::string>(),
269 		std::set<std::string> const& _externallyUsedFunctions = std::set<std::string>(),
270 		bool _system = false,
271 		OptimiserSettings const& _optimiserSettings = OptimiserSettings::none(),
272 		std::string _sourceName = "--CODEGEN--"
273 	);
274 
275 	/// If m_revertStrings is debug, @returns inline assembly code that
276 	/// stores @param _message at the free memory pointer and reverts.
277 	/// Otherwise returns "revert(0, 0)".
278 	std::string revertReasonIfDebug(std::string const& _message = "");
279 
280 	void optimizeYul(yul::Object& _object, yul::EVMDialect const& _dialect, OptimiserSettings const& _optimiserSetting, std::set<yul::YulString> const& _externalIdentifiers = {});
281 
282 	/// Appends arbitrary data to the end of the bytecode.
appendToAuxiliaryData(bytes const & _data)283 	void appendToAuxiliaryData(bytes const& _data) { m_asm->appendToAuxiliaryData(_data); }
284 
285 	/// Run optimisation step.
optimise(OptimiserSettings const & _settings)286 	void optimise(OptimiserSettings const& _settings) { m_asm->optimise(translateOptimiserSettings(_settings)); }
287 
288 	/// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise.
runtimeContext()289 	CompilerContext* runtimeContext() const { return m_runtimeContext; }
290 	/// @returns the identifier of the runtime subroutine.
runtimeSub()291 	size_t runtimeSub() const { return m_runtimeSub; }
292 
293 	/// @returns a const reference to the underlying assembly.
assembly()294 	evmasm::Assembly const& assembly() const { return *m_asm; }
295 	/// @returns a shared pointer to the assembly.
296 	/// Should be avoided except when adding sub-assemblies.
assemblyPtr()297 	std::shared_ptr<evmasm::Assembly> assemblyPtr() const { return m_asm; }
298 
299 	/**
300 	 * Helper class to pop the visited nodes stack when a scope closes
301 	 */
302 	class LocationSetter: public ScopeGuard
303 	{
304 	public:
LocationSetter(CompilerContext & _compilerContext,ASTNode const & _node)305 		LocationSetter(CompilerContext& _compilerContext, ASTNode const& _node):
306 			ScopeGuard([&]{ _compilerContext.popVisitedNodes(); }) { _compilerContext.pushVisitedNodes(&_node); }
307 	};
308 
setModifierDepth(size_t _modifierDepth)309 	void setModifierDepth(size_t _modifierDepth) { m_asm->m_currentModifierDepth = _modifierDepth; }
310 
revertStrings()311 	RevertStrings revertStrings() const { return m_revertStrings; }
312 
313 private:
314 	/// Updates source location set in the assembly.
315 	void updateSourceLocation();
316 
317 	evmasm::Assembly::OptimiserSettings translateOptimiserSettings(OptimiserSettings const& _settings);
318 
319 	/**
320 	 * Helper class that manages function labels and ensures that referenced functions are
321 	 * compiled in a specific order.
322 	 */
323 	struct FunctionCompilationQueue
324 	{
325 		/// @returns the entry label of the given function and creates it if it does not exist yet.
326 		/// @param _context compiler context used to create a new tag if needed
327 		evmasm::AssemblyItem entryLabel(Declaration const& _declaration, CompilerContext& _context);
328 		/// @returns the entry label of the given function. Might return an AssemblyItem of type
329 		/// UndefinedItem if it does not exist yet.
330 		evmasm::AssemblyItem entryLabelIfExists(Declaration const& _declaration) const;
331 
332 		/// @returns the next function in the queue of functions that are still to be compiled
333 		/// (i.e. that were referenced during compilation but where we did not yet generate code for).
334 		/// Returns nullptr if the queue is empty. Does not remove the function from the queue,
335 		/// that will only be done by startFunction below.
336 		Declaration const* nextFunctionToCompile() const;
337 		/// Informs the queue that we are about to compile the given function, i.e. removes
338 		/// the function from the queue of functions to compile.
339 		void startFunction(Declaration const& _function);
340 
341 		/// Labels pointing to the entry points of functions.
342 		std::map<Declaration const*, evmasm::AssemblyItem> m_entryLabels;
343 		/// Set of functions for which we did not yet generate code.
344 		std::set<Declaration const*> m_alreadyCompiledFunctions;
345 		/// Queue of functions that still need to be compiled (important to be a queue to maintain
346 		/// determinism even in the presence of a non-deterministic allocator).
347 		/// Mutable because we will throw out some functions earlier than needed.
348 		mutable std::queue<Declaration const*> m_functionsToCompile;
349 	} m_functionCompilationQueue;
350 
351 	evmasm::AssemblyPointer m_asm;
352 	/// Version of the EVM to compile against.
353 	langutil::EVMVersion m_evmVersion;
354 	RevertStrings const m_revertStrings;
355 	bool m_useABICoderV2 = false;
356 	/// Other already compiled contracts to be used in contract creation calls.
357 	std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> m_otherCompilers;
358 	/// Storage offsets of state variables
359 	std::map<Declaration const*, std::pair<u256, unsigned>> m_stateVariables;
360 	/// Memory offsets reserved for the values of immutable variables during contract creation.
361 	std::map<VariableDeclaration const*, size_t> m_immutableVariables;
362 	/// Total amount of reserved memory. Reserved memory is used to store immutable variables during contract creation.
363 	/// This has to be finalized before initialiseFreeMemoryPointer() is called. That function
364 	/// will reset the optional to verify that.
365 	std::optional<size_t> m_reservedMemory = {0};
366 	/// Offsets of local variables on the stack (relative to stack base).
367 	/// This needs to be a stack because if a modifier contains a local variable and this
368 	/// modifier is applied twice, the position of the variable needs to be restored
369 	/// after the nested modifier is left.
370 	std::map<Declaration const*, std::vector<unsigned>> m_localVariables;
371 	/// The contract currently being compiled. Virtual function lookup starts from this contarct.
372 	ContractDefinition const* m_mostDerivedContract = nullptr;
373 	/// Whether to use checked arithmetic.
374 	Arithmetic m_arithmetic = Arithmetic::Checked;
375 	/// Stack of current visited AST nodes, used for location attachment
376 	std::stack<ASTNode const*> m_visitedNodes;
377 	/// The runtime context if in Creation mode, this is used for generating tags that would be stored into the storage and then used at runtime.
378 	CompilerContext *m_runtimeContext;
379 	/// The index of the runtime subroutine.
380 	size_t m_runtimeSub = std::numeric_limits<size_t>::max();
381 	/// An index of low-level function labels by name.
382 	std::map<std::string, evmasm::AssemblyItem> m_lowLevelFunctions;
383 	/// Collector for yul functions.
384 	MultiUseYulFunctionCollector m_yulFunctionCollector;
385 	/// Set of externally used yul functions.
386 	std::set<std::string> m_externallyUsedYulFunctions;
387 	/// Generated Yul code used as utility. Source references from the bytecode can point here.
388 	/// Produced from @a m_yulFunctionCollector.
389 	std::string m_generatedYulUtilityCode;
390 	/// Container for ABI functions to be generated.
391 	ABIFunctions m_abiFunctions;
392 	/// Container for Yul Util functions to be generated.
393 	YulUtilFunctions m_yulUtilFunctions;
394 	/// The queue of low-level functions to generate.
395 	std::queue<std::tuple<std::string, unsigned, unsigned, std::function<void(CompilerContext&)>>> m_lowLevelFunctionGenerationQueue;
396 	/// Flag to check that appendYulUtilityFunctions() was called exactly once
397 	bool m_appendYulUtilityFunctionsRan = false;
398 };
399 
400 }
401