/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 | | } |