Coverage Report

Created: 2022-08-24 06:43

/src/solidity/libsolidity/codegen/CompilerContext.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
 * @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
  ):
68
    m_asm(std::make_shared<evmasm::Assembly>(_runtimeContext != nullptr, std::string{})),
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
19.9k
  {
76
19.9k
    if (m_runtimeContext)
77
9.97k
      m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data());
78
19.9k
  }
79
80
19.6k
  langutil::EVMVersion const& evmVersion() const { return m_evmVersion; }
81
82
42.5k
  void setUseABICoderV2(bool _value) { m_useABICoderV2 = _value; }
83
27.2k
  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
103
19.7k
  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
107
33.0k
  void setStackOffset(int _offset) { m_asm->setDeposit(_offset); }
108
74.4k
  void adjustStackOffset(int _adjustment) { m_asm->adjustDeposit(_adjustment); }
109
191k
  unsigned stackHeight() const { solAssert(m_asm->deposit() >= 0, ""); return unsigned(m_asm->deposit()); }
110
111
  bool isLocalVariable(Declaration const* _declaration) const;
112
5.07k
  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.
123
19.7k
  void setMostDerivedContract(ContractDefinition const& _contract) { m_mostDerivedContract = &_contract; }
124
  ContractDefinition const& mostDerivedContract() const;
125
126
11.9k
  void setArithmetic(Arithmetic _value) { m_arithmetic = _value; }
127
24.4k
  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();
168
13.4k
  ABIFunctions& abiFunctions() { return m_abiFunctions; }
169
23.5k
  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);
175
19.7k
  bool appendYulUtilityFunctionsRan() const { return m_appendYulUtilityFunctionsRan; }
176
0
  std::string const& generatedYulUtilityCode() const { return m_generatedYulUtilityCode; }
177
12.2k
  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
191
15.7k
  evmasm::AssemblyItem appendConditionalJump() { return m_asm->appendJumpI().tag(); }
192
  /// Appends a JUMPI instruction to @a _tag
193
22.4k
  CompilerContext& appendConditionalJumpTo(evmasm::AssemblyItem const& _tag) { m_asm->appendJumpI(_tag); return *this; }
194
  /// Appends a JUMP to a new tag and @returns the tag
195
10.2k
  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
57.6k
  ) { *m_asm << _tag.pushTag(); return appendJump(_jumpType); }
216
  /// Appends pushing of a new tag and @returns the new tag.
217
49.1k
  evmasm::AssemblyItem pushNewTag() { return m_asm->append(m_asm->newPushTag()).tag(); }
218
  /// @returns a new tag without pushing any opcodes or data
219
39.2k
  evmasm::AssemblyItem newTag() { return m_asm->newTag(); }
220
  /// @returns a new tag identified by name.
221
  evmasm::AssemblyItem namedTag(std::string const& _name, size_t _params, size_t _returns, std::optional<uint64_t> _sourceID)
222
49.5k
  {
223
49.5k
    return m_asm->namedTag(_name, _params, _returns, _sourceID);
224
49.5k
  }
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.
227
143
  evmasm::AssemblyItem addSubroutine(evmasm::AssemblyPointer const& _assembly) { return m_asm->appendSubroutine(_assembly); }
228
  /// Pushes the size of the subroutine.
229
10.1k
  void pushSubroutineSize(size_t _subRoutine) { m_asm->pushSubroutineSize(_subRoutine); }
230
  /// Pushes the offset of the subroutine.
231
9.89k
  void pushSubroutineOffset(size_t _subRoutine) { m_asm->pushSubroutineOffset(_subRoutine); }
232
  /// Pushes the size of the final program
233
1.02k
  void appendProgramSize() { m_asm->appendProgramSize(); }
234
  /// Adds data to the data section, pushes a reference to the stack
235
542
  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.
237
102
  void appendLibraryAddress(std::string const& _identifier) { m_asm->appendLibraryAddress(_identifier); }
238
  /// Appends an immutable variable. The value will be filled in by the constructor.
239
80
  void appendImmutable(std::string const& _identifier) { m_asm->appendImmutable(_identifier); }
240
  /// Appends an assignment to an immutable variable. Only valid in creation code.
241
374
  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).
244
217
  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
248
213k
  void popVisitedNodes() { m_visitedNodes.pop(); updateSourceLocation(); }
249
  /// Pushes an ASTNode to the stack of visited nodes
250
213k
  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
199k
  CompilerContext& operator<<(evmasm::AssemblyItem const& _item) { m_asm->append(_item); return *this; }
254
704k
  CompilerContext& operator<<(evmasm::Instruction _instruction) { m_asm->append(_instruction); return *this; }
255
257k
  CompilerContext& operator<<(u256 const& _value) { m_asm->append(_value); return *this; }
256
0
  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.
283
9.97k
  void appendToAuxiliaryData(bytes const& _data) { m_asm->appendToAuxiliaryData(_data); }
284
285
  /// Run optimisation step.
286
9.89k
  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.
289
24.3k
  CompilerContext* runtimeContext() const { return m_runtimeContext; }
290
  /// @returns the identifier of the runtime subroutine.
291
39.8k
  size_t runtimeSub() const { return m_runtimeSub; }
292
293
  /// @returns a const reference to the underlying assembly.
294
0
  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.
297
20.6k
  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:
305
    LocationSetter(CompilerContext& _compilerContext, ASTNode const& _node):
306
199k
      ScopeGuard([&]{ _compilerContext.popVisitedNodes(); }) { _compilerContext.pushVisitedNodes(&_node); }
307
  };
308
309
45.4k
  void setModifierDepth(size_t _modifierDepth) { m_asm->m_currentModifierDepth = _modifierDepth; }
310
311
511
  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
}