/src/solidity/libevmasm/ConstantOptimiser.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  |  | /** @file ConstantOptimiser.cpp  | 
19  |  |  * @author Christian <c@ethdev.com>  | 
20  |  |  * @date 2015  | 
21  |  |  */  | 
22  |  |  | 
23  |  | #pragma once  | 
24  |  |  | 
25  |  | #include <libevmasm/Exceptions.h>  | 
26  |  |  | 
27  |  | #include <liblangutil/EVMVersion.h>  | 
28  |  |  | 
29  |  | #include <libsolutil/Numeric.h>  | 
30  |  | #include <libsolutil/Assertions.h>  | 
31  |  |  | 
32  |  | #include <vector>  | 
33  |  |  | 
34  |  | namespace solidity::evmasm  | 
35  |  | { | 
36  |  |  | 
37  |  | class AssemblyItem;  | 
38  |  | using AssemblyItems = std::vector<AssemblyItem>;  | 
39  |  | class Assembly;  | 
40  |  |  | 
41  |  | /**  | 
42  |  |  * Abstract base class for one way to change how constants are represented in the code.  | 
43  |  |  */  | 
44  |  | class ConstantOptimisationMethod  | 
45  |  | { | 
46  |  | public:  | 
47  |  |   /// Tries to optimised how constants are represented in the source code and modifies  | 
48  |  |   /// @a _assembly.  | 
49  |  |   /// @returns zero if no optimisations could be performed.  | 
50  |  |   static unsigned optimiseConstants(  | 
51  |  |     bool _isCreation,  | 
52  |  |     size_t _runs,  | 
53  |  |     langutil::EVMVersion _evmVersion,  | 
54  |  |     Assembly& _assembly  | 
55  |  |   );  | 
56  |  |  | 
57  |  | protected:  | 
58  |  |   /// This is the public API for the optimiser methods, but it doesn't need to be exposed to the caller.  | 
59  |  |  | 
60  |  |   struct Params  | 
61  |  |   { | 
62  |  |     bool isCreation; ///< Whether this is called during contract creation or runtime.  | 
63  |  |     size_t runs; ///< Estimated number of calls per opcode oven the lifetime of the contract.  | 
64  |  |     size_t multiplicity; ///< Number of times the constant appears in the code.  | 
65  |  |     langutil::EVMVersion evmVersion; ///< Version of the EVM  | 
66  |  |   };  | 
67  |  |  | 
68  |  |   explicit ConstantOptimisationMethod(Params const& _params, u256 const& _value):  | 
69  | 53.5k  |     m_params(_params), m_value(_value) {} | 
70  | 53.5k  |   virtual ~ConstantOptimisationMethod() = default;  | 
71  |  |   virtual bigint gasNeeded() const = 0;  | 
72  |  |   /// Executes the method, potentially appending to the assembly and returns a vector of  | 
73  |  |   /// assembly items the constant should be relpaced with in one sweep.  | 
74  |  |   /// If the vector is empty, the constants will not be deleted.  | 
75  |  |   virtual AssemblyItems execute(Assembly& _assembly) const = 0;  | 
76  |  |  | 
77  |  | protected:  | 
78  |  |   /// @returns the run gas for the given items ignoring special gas costs  | 
79  |  |   static bigint simpleRunGas(AssemblyItems const& _items);  | 
80  |  |   /// @returns the gas needed to store the given data literally  | 
81  |  |   bigint dataGas(bytes const& _data) const;  | 
82  |  |   static size_t bytesRequired(AssemblyItems const& _items);  | 
83  |  |   /// @returns the combined estimated gas usage taking @a m_params into account.  | 
84  |  |   bigint combineGas(  | 
85  |  |     bigint const& _runGas,  | 
86  |  |     bigint const& _repeatedDataGas,  | 
87  |  |     bigint const& _uniqueDataGas  | 
88  |  |   ) const  | 
89  | 860k  |   { | 
90  |  |     // _runGas is not multiplied by _multiplicity because the runs are "per opcode"  | 
91  | 860k  |     return m_params.runs * _runGas + m_params.multiplicity * _repeatedDataGas + _uniqueDataGas;  | 
92  | 860k  |   }  | 
93  |  |  | 
94  |  |   /// Replaces all constants i by the code given in @a _replacement[i].  | 
95  |  |   static void replaceConstants(AssemblyItems& _items, std::map<u256, AssemblyItems> const& _replacements);  | 
96  |  |  | 
97  |  |   Params m_params;  | 
98  |  |   u256 const& m_value;  | 
99  |  | };  | 
100  |  |  | 
101  |  | /**  | 
102  |  |  * Optimisation method that pushes the constant to the stack literally. This is the default method,  | 
103  |  |  * i.e. executing it does not alter the Assembly.  | 
104  |  |  */  | 
105  |  | class LiteralMethod: public ConstantOptimisationMethod  | 
106  |  | { | 
107  |  | public:  | 
108  |  |   explicit LiteralMethod(Params const& _params, u256 const& _value):  | 
109  | 17.8k  |     ConstantOptimisationMethod(_params, _value) {} | 
110  |  |   bigint gasNeeded() const override;  | 
111  | 0  |   AssemblyItems execute(Assembly&) const override { return AssemblyItems{}; } | 
112  |  | };  | 
113  |  |  | 
114  |  | /**  | 
115  |  |  * Method that stores the data in the .data section of the code and copies it to the stack.  | 
116  |  |  */  | 
117  |  | class CodeCopyMethod: public ConstantOptimisationMethod  | 
118  |  | { | 
119  |  | public:  | 
120  |  |   explicit CodeCopyMethod(Params const& _params, u256 const& _value):  | 
121  | 17.8k  |     ConstantOptimisationMethod(_params, _value) {} | 
122  |  |   bigint gasNeeded() const override;  | 
123  |  |   AssemblyItems execute(Assembly& _assembly) const override;  | 
124  |  |  | 
125  |  | protected:  | 
126  |  |   static AssemblyItems const& copyRoutine();  | 
127  |  | };  | 
128  |  |  | 
129  |  | /**  | 
130  |  |  * Method that tries to compute the constant.  | 
131  |  |  */  | 
132  |  | class ComputeMethod: public ConstantOptimisationMethod  | 
133  |  | { | 
134  |  | public:  | 
135  |  |   explicit ComputeMethod(Params const& _params, u256 const& _value):  | 
136  |  |     ConstantOptimisationMethod(_params, _value)  | 
137  | 17.8k  |   { | 
138  | 17.8k  |     m_routine = findRepresentation(m_value);  | 
139  | 17.8k  |     assertThrow(  | 
140  | 17.8k  |       checkRepresentation(m_value, m_routine),  | 
141  | 17.8k  |       OptimizerException,  | 
142  | 17.8k  |       "Invalid constant expression created."  | 
143  | 17.8k  |     );  | 
144  | 17.8k  |   }  | 
145  |  |  | 
146  | 17.8k  |   bigint gasNeeded() const override { return gasNeeded(m_routine); } | 
147  |  |   AssemblyItems execute(Assembly&) const override  | 
148  | 9.30k  |   { | 
149  | 9.30k  |     return m_routine;  | 
150  | 9.30k  |   }  | 
151  |  |  | 
152  |  | protected:  | 
153  |  |   /// Tries to recursively find a way to compute @a _value.  | 
154  |  |   AssemblyItems findRepresentation(u256 const& _value);  | 
155  |  |   /// Recomputes the value from the calculated representation and checks for correctness.  | 
156  |  |   bool checkRepresentation(u256 const& _value, AssemblyItems const& _routine) const;  | 
157  |  |   bigint gasNeeded(AssemblyItems const& _routine) const;  | 
158  |  |  | 
159  |  |   /// Counter for the complexity of optimization, will stop when it reaches zero.  | 
160  |  |   size_t m_maxSteps = 10000;  | 
161  |  |   AssemblyItems m_routine;  | 
162  |  | };  | 
163  |  |  | 
164  |  | }  |