/src/solidity/libevmasm/Assembly.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  |  | #pragma once  | 
20  |  |  | 
21  |  | #include <libevmasm/Instruction.h>  | 
22  |  | #include <liblangutil/SourceLocation.h>  | 
23  |  | #include <libevmasm/AssemblyItem.h>  | 
24  |  | #include <libevmasm/LinkerObject.h>  | 
25  |  | #include <libevmasm/Exceptions.h>  | 
26  |  |  | 
27  |  | #include <liblangutil/DebugInfoSelection.h>  | 
28  |  | #include <liblangutil/EVMVersion.h>  | 
29  |  |  | 
30  |  | #include <libsolutil/Common.h>  | 
31  |  | #include <libsolutil/Assertions.h>  | 
32  |  | #include <libsolutil/Keccak256.h>  | 
33  |  |  | 
34  |  | #include <libsolidity/interface/OptimiserSettings.h>  | 
35  |  |  | 
36  |  | #include <json/json.h>  | 
37  |  |  | 
38  |  | #include <iostream>  | 
39  |  | #include <sstream>  | 
40  |  | #include <memory>  | 
41  |  | #include <map>  | 
42  |  | #include <utility>  | 
43  |  |  | 
44  |  | namespace solidity::evmasm  | 
45  |  | { | 
46  |  |  | 
47  |  | using AssemblyPointer = std::shared_ptr<Assembly>;  | 
48  |  |  | 
49  |  | class Assembly  | 
50  |  | { | 
51  |  | public:  | 
52  | 9.10k  |   Assembly(bool _creation, std::string _name): m_creation(_creation), m_name(std::move(_name)) { } | 
53  |  |  | 
54  | 3.48M  |   AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++); } | 
55  | 8.12M  |   AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); } | 
56  |  |   /// Returns a tag identified by the given name. Creates it if it does not yet exist.  | 
57  |  |   AssemblyItem namedTag(std::string const& _name, size_t _params, size_t _returns, std::optional<uint64_t> _sourceID);  | 
58  | 137k  |   AssemblyItem newData(bytes const& _data) { util::h256 h(util::keccak256(util::asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); } | 
59  | 0  |   bytes const& data(util::h256 const& _i) const { return m_data.at(_i); } | 
60  | 4.55k  |   AssemblyItem newSub(AssemblyPointer const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); } | 
61  | 0  |   Assembly const& sub(size_t _sub) const { return *m_subs.at(_sub); } | 
62  | 0  |   Assembly& sub(size_t _sub) { return *m_subs.at(_sub); } | 
63  | 0  |   size_t numSubs() const { return m_subs.size(); } | 
64  | 4.55k  |   AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); } | 
65  |  |   AssemblyItem newPushLibraryAddress(std::string const& _identifier);  | 
66  |  |   AssemblyItem newPushImmutable(std::string const& _identifier);  | 
67  |  |   AssemblyItem newImmutableAssignment(std::string const& _identifier);  | 
68  |  |  | 
69  |  |   AssemblyItem const& append(AssemblyItem _i);  | 
70  | 137k  |   AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); } | 
71  |  |  | 
72  | 4.73M  |   template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; }solidity::evmasm::Assembly& solidity::evmasm::Assembly::operator<< <solidity::evmasm::AssemblyItem>(solidity::evmasm::AssemblyItem const&) Line  | Count  | Source  |  72  | 4.73M  |   template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; } |  
 Unexecuted instantiation: solidity::evmasm::Assembly& solidity::evmasm::Assembly::operator<< <boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<256u, 256u, (boost::multiprecision::cpp_integer_type)0, (boost::multiprecision::cpp_int_check_type)0, void>, (boost::multiprecision::expression_template_option)0> >(boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<256u, 256u, (boost::multiprecision::cpp_integer_type)0, (boost::multiprecision::cpp_int_check_type)0, void>, (boost::multiprecision::expression_template_option)0> const&)  | 
73  |  |  | 
74  |  |   /// Pushes the final size of the current assembly itself. Use this when the code is modified  | 
75  |  |   /// after compilation and CODESIZE is not an option.  | 
76  | 0  |   void appendProgramSize() { append(AssemblyItem(PushProgramSize)); } | 
77  | 0  |   void appendLibraryAddress(std::string const& _identifier) { append(newPushLibraryAddress(_identifier)); } | 
78  | 0  |   void appendImmutable(std::string const& _identifier) { append(newPushImmutable(_identifier)); } | 
79  | 0  |   void appendImmutableAssignment(std::string const& _identifier) { append(newImmutableAssignment(_identifier)); } | 
80  |  |  | 
81  |  |   void appendVerbatim(bytes _data, size_t _arguments, size_t _returnVariables)  | 
82  | 0  |   { | 
83  | 0  |     append(AssemblyItem(std::move(_data), _arguments, _returnVariables));  | 
84  | 0  |   }  | 
85  |  |  | 
86  | 8.03k  |   AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; } | 
87  | 4.02M  |   AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; } | 
88  | 0  |   AssemblyItem appendJump(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMP); return ret; } | 
89  | 120k  |   AssemblyItem appendJumpI(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMPI); return ret; } | 
90  |  |  | 
91  |  |   /// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)  | 
92  |  |   /// on the stack. @returns the pushsub assembly item.  | 
93  | 0  |   AssemblyItem appendSubroutine(AssemblyPointer const& _assembly) { auto sub = newSub(_assembly); append(newPushSubSize(size_t(sub.data()))); return sub; } | 
94  | 4.55k  |   void pushSubroutineSize(size_t _subRoutine) { append(newPushSubSize(_subRoutine)); } | 
95  |  |   /// Pushes the offset of the subroutine.  | 
96  | 4.55k  |   void pushSubroutineOffset(size_t _subRoutine) { append(AssemblyItem(PushSub, _subRoutine)); } | 
97  |  |  | 
98  |  |   /// Appends @a _data literally to the very end of the bytecode.  | 
99  | 4.55k  |   void appendToAuxiliaryData(bytes const& _data) { m_auxiliaryData += _data; } | 
100  |  |  | 
101  |  |   /// Returns the assembly items.  | 
102  | 0  |   AssemblyItems const& items() const { return m_items; } | 
103  |  |  | 
104  |  |   /// Returns the mutable assembly items. Use with care!  | 
105  | 0  |   AssemblyItems& items() { return m_items; } | 
106  |  |  | 
107  | 36.2M  |   int deposit() const { return m_deposit; } | 
108  | 6.35M  |   void adjustDeposit(int _adjustment) { m_deposit += _adjustment; assertThrow(m_deposit >= 0, InvalidDeposit, ""); } | 
109  | 1.19M  |   void setDeposit(int _deposit) { m_deposit = _deposit; assertThrow(m_deposit >= 0, InvalidDeposit, ""); } | 
110  | 0  |   std::string const& name() const { return m_name; } | 
111  |  |  | 
112  |  |   /// Changes the source location used for each appended item.  | 
113  | 47.6M  |   void setSourceLocation(langutil::SourceLocation const& _location) { m_currentSourceLocation = _location; } | 
114  | 152k  |   langutil::SourceLocation const& currentSourceLocation() const { return m_currentSourceLocation; } | 
115  |  |  | 
116  |  |   /// Assembles the assembly into bytecode. The assembly should not be modified after this call, since the assembled version is cached.  | 
117  |  |   LinkerObject const& assemble() const;  | 
118  |  |  | 
119  |  |   struct OptimiserSettings  | 
120  |  |   { | 
121  |  |     bool runInliner = false;  | 
122  |  |     bool runJumpdestRemover = false;  | 
123  |  |     bool runPeephole = false;  | 
124  |  |     bool runDeduplicate = false;  | 
125  |  |     bool runCSE = false;  | 
126  |  |     bool runConstantOptimiser = false;  | 
127  |  |     langutil::EVMVersion evmVersion;  | 
128  |  |     /// This specifies an estimate on how often each opcode in this assembly will be executed,  | 
129  |  |     /// i.e. use a small value to optimise for size and a large value to optimise for runtime gas usage.  | 
130  |  |     size_t expectedExecutionsPerDeployment = frontend::OptimiserSettings{}.expectedExecutionsPerDeployment; | 
131  |  |   };  | 
132  |  |  | 
133  |  |   /// Modify and return the current assembly such that creation and execution gas usage  | 
134  |  |   /// is optimised according to the settings in @a _settings.  | 
135  |  |   Assembly& optimise(OptimiserSettings const& _settings);  | 
136  |  |  | 
137  |  |   /// Create a text representation of the assembly.  | 
138  |  |   std::string assemblyString(  | 
139  |  |     langutil::DebugInfoSelection const& _debugInfoSelection = langutil::DebugInfoSelection::Default(),  | 
140  |  |     StringMap const& _sourceCodes = StringMap()  | 
141  |  |   ) const;  | 
142  |  |   void assemblyStream(  | 
143  |  |     std::ostream& _out,  | 
144  |  |     langutil::DebugInfoSelection const& _debugInfoSelection = langutil::DebugInfoSelection::Default(),  | 
145  |  |     std::string const& _prefix = "",  | 
146  |  |     StringMap const& _sourceCodes = StringMap()  | 
147  |  |   ) const;  | 
148  |  |  | 
149  |  |   /// Create a JSON representation of the assembly.  | 
150  |  |   Json::Value assemblyJSON(  | 
151  |  |     std::map<std::string, unsigned> const& _sourceIndices = std::map<std::string, unsigned>(),  | 
152  |  |     bool _includeSourceList = true  | 
153  |  |   ) const;  | 
154  |  |  | 
155  |  |   /// Mark this assembly as invalid. Calling ``assemble`` on it will throw.  | 
156  | 0  |   void markAsInvalid() { m_invalid = true; } | 
157  |  |  | 
158  |  |   std::vector<size_t> decodeSubPath(size_t _subObjectId) const;  | 
159  |  |   size_t encodeSubPath(std::vector<size_t> const& _subPath);  | 
160  |  |  | 
161  | 0  |   bool isCreation() const { return m_creation; } | 
162  |  |  | 
163  |  | protected:  | 
164  |  |   /// Does the same operations as @a optimise, but should only be applied to a sub and  | 
165  |  |   /// returns the replaced tags. Also takes an argument containing the tags of this assembly  | 
166  |  |   /// that are referenced in a super-assembly.  | 
167  |  |   std::map<u256, u256> const& optimiseInternal(OptimiserSettings const& _settings, std::set<size_t> _tagsReferencedFromOutside);  | 
168  |  |  | 
169  |  |   unsigned codeSize(unsigned subTagSize) const;  | 
170  |  |  | 
171  |  | private:  | 
172  |  |   bool m_invalid = false;  | 
173  |  |  | 
174  |  |   Assembly const* subAssemblyById(size_t _subId) const;  | 
175  |  |  | 
176  |  | protected:  | 
177  |  |   /// 0 is reserved for exception  | 
178  |  |   unsigned m_usedTags = 1;  | 
179  |  |  | 
180  |  |   struct NamedTagInfo  | 
181  |  |   { | 
182  |  |     size_t id;  | 
183  |  |     std::optional<size_t> sourceID;  | 
184  |  |     size_t params;  | 
185  |  |     size_t returns;  | 
186  |  |   };  | 
187  |  |  | 
188  |  |   std::map<std::string, NamedTagInfo> m_namedTags;  | 
189  |  |   AssemblyItems m_items;  | 
190  |  |   std::map<util::h256, bytes> m_data;  | 
191  |  |   /// Data that is appended to the very end of the contract.  | 
192  |  |   bytes m_auxiliaryData;  | 
193  |  |   std::vector<std::shared_ptr<Assembly>> m_subs;  | 
194  |  |   std::map<util::h256, std::string> m_strings;  | 
195  |  |   std::map<util::h256, std::string> m_libraries; ///< Identifiers of libraries to be linked.  | 
196  |  |   std::map<util::h256, std::string> m_immutables; ///< Identifiers of immutables.  | 
197  |  |  | 
198  |  |   /// Map from a vector representing a path to a particular sub assembly to sub assembly id.  | 
199  |  |   /// This map is used only for sub-assemblies which are not direct sub-assemblies (where path is having more than one value).  | 
200  |  |   std::map<std::vector<size_t>, size_t> m_subPaths;  | 
201  |  |  | 
202  |  |   /// Contains the tag replacements relevant for super-assemblies.  | 
203  |  |   /// If set, it means the optimizer has run and we will not run it again.  | 
204  |  |   std::optional<std::map<u256, u256>> m_tagReplacements;  | 
205  |  |  | 
206  |  |   mutable LinkerObject m_assembledObject;  | 
207  |  |   mutable std::vector<size_t> m_tagPositionsInBytecode;  | 
208  |  |  | 
209  |  |   int m_deposit = 0;  | 
210  |  |   /// True, if the assembly contains contract creation code.  | 
211  |  |   bool const m_creation = false;  | 
212  |  |   /// Internal name of the assembly object, only used with the Yul backend  | 
213  |  |   /// currently  | 
214  |  |   std::string m_name;  | 
215  |  |  | 
216  |  |   langutil::SourceLocation m_currentSourceLocation;  | 
217  |  |  | 
218  |  | public:  | 
219  |  |   size_t m_currentModifierDepth = 0;  | 
220  |  | };  | 
221  |  |  | 
222  |  | inline std::ostream& operator<<(std::ostream& _out, Assembly const& _a)  | 
223  | 0  | { | 
224  | 0  |   _a.assemblyStream(_out);  | 
225  | 0  |   return _out;  | 
226  | 0  | }  | 
227  |  |  | 
228  |  | }  |