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