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