Coverage Report

Created: 2022-08-24 06:55

/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
0
    m_params(_params), m_value(_value) {}
70
0
  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
0
  {
90
    // _runGas is not multiplied by _multiplicity because the runs are "per opcode"
91
0
    return m_params.runs * _runGas + m_params.multiplicity * _repeatedDataGas + _uniqueDataGas;
92
0
  }
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
0
    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
0
    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
0
  {
138
0
    m_routine = findRepresentation(m_value);
139
0
    assertThrow(
140
0
      checkRepresentation(m_value, m_routine),
141
0
      OptimizerException,
142
0
      "Invalid constant expression created."
143
0
    );
144
0
  }
145
146
0
  bigint gasNeeded() const override { return gasNeeded(m_routine); }
147
  AssemblyItems execute(Assembly&) const override
148
0
  {
149
0
    return m_routine;
150
0
  }
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
}