Coverage Report

Created: 2022-08-24 06:31

/src/solidity/libevmasm/CommonSubexpressionEliminator.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
 * @file CommonSubexpressionEliminator.h
20
 * @author Christian <c@ethdev.com>
21
 * @date 2015
22
 * Optimizer step for common subexpression elimination and stack reorganisation.
23
 */
24
25
#pragma once
26
27
#include <map>
28
#include <ostream>
29
#include <set>
30
#include <tuple>
31
#include <unordered_map>
32
#include <vector>
33
#include <libsolutil/CommonIO.h>
34
#include <libsolutil/Exceptions.h>
35
#include <libevmasm/ExpressionClasses.h>
36
#include <libevmasm/SemanticInformation.h>
37
#include <libevmasm/KnownState.h>
38
39
namespace langutil
40
{
41
struct SourceLocation;
42
}
43
44
namespace solidity::evmasm
45
{
46
47
class AssemblyItem;
48
using AssemblyItems = std::vector<AssemblyItem>;
49
50
/**
51
 * Optimizer step that performs common subexpression elimination and stack reorganisation,
52
 * i.e. it tries to infer equality among expressions and compute the values of two expressions
53
 * known to be equal only once.
54
 *
55
 * The general workings are that for each assembly item that is fed into the eliminator, an
56
 * equivalence class is derived from the operation and the equivalence class of its arguments.
57
 * DUPi, SWAPi and some arithmetic instructions are used to infer equivalences while these
58
 * classes are determined.
59
 *
60
 * When the list of optimized items is requested, they are generated in a bottom-up fashion,
61
 * adding code for equivalence classes that were not yet computed.
62
 */
63
class CommonSubexpressionEliminator
64
{
65
public:
66
  using Id = ExpressionClasses::Id;
67
  using StoreOperation = KnownState::StoreOperation;
68
69
0
  explicit CommonSubexpressionEliminator(KnownState const& _state): m_initialState(_state), m_state(_state) {}
70
71
  /// Feeds AssemblyItems into the eliminator and @returns the iterator pointing at the first
72
  /// item that must be fed into a new instance of the eliminator.
73
  /// @param _msizeImportant if false, do not consider modification of MSIZE a side-effect
74
  template <class AssemblyItemIterator>
75
  AssemblyItemIterator feedItems(AssemblyItemIterator _iterator, AssemblyItemIterator _end, bool _msizeImportant);
76
77
  /// @returns the resulting items after optimization.
78
  AssemblyItems getOptimizedItems();
79
80
private:
81
  /// Feeds the item into the system for analysis.
82
  void feedItem(AssemblyItem const& _item, bool _copyItem = false);
83
84
  /// Tries to optimize the item that breaks the basic block at the end.
85
  void optimizeBreakingItem();
86
87
  KnownState m_initialState;
88
  KnownState m_state;
89
  /// Keeps information about which storage or memory slots were written to at which sequence
90
  /// number with what instruction.
91
  std::vector<StoreOperation> m_storeOperations;
92
93
  /// The item that breaks the basic block, can be nullptr.
94
  /// It is usually appended to the block but can be optimized in some cases.
95
  AssemblyItem const* m_breakingItem = nullptr;
96
};
97
98
/**
99
 * Unit that generates code from current stack layout, target stack layout and information about
100
 * the equivalence classes.
101
 */
102
class CSECodeGenerator
103
{
104
public:
105
  using StoreOperation = CommonSubexpressionEliminator::StoreOperation;
106
  using StoreOperations = std::vector<StoreOperation>;
107
  using Id = ExpressionClasses::Id;
108
109
  /// Initializes the code generator with the given classes and store operations.
110
  /// The store operations have to be sorted by sequence number in ascending order.
111
  CSECodeGenerator(ExpressionClasses& _expressionClasses, StoreOperations const& _storeOperations);
112
113
  /// @returns the assembly items generated from the given requirements
114
  /// @param _initialSequenceNumber starting sequence number, do not generate sequenced operations
115
  /// before this number.
116
  /// @param _initialStack current contents of the stack (up to stack height of zero)
117
  /// @param _targetStackContents final contents of the stack, by stack height relative to initial
118
  /// @note should only be called once on each object.
119
  AssemblyItems generateCode(
120
    unsigned _initialSequenceNumber,
121
    int _initialStackHeight,
122
    std::map<int, Id> const& _initialStack,
123
    std::map<int, Id> const& _targetStackContents
124
  );
125
126
private:
127
  /// Recursively discovers all dependencies to @a m_requests.
128
  void addDependencies(Id _c);
129
130
  /// Produce code that generates the given element if it is not yet present.
131
  /// @param _allowSequenced indicates that sequence-constrained operations are allowed
132
  void generateClassElement(Id _c, bool _allowSequenced = false);
133
  /// @returns the position of the representative of the given id on the stack.
134
  /// @note throws an exception if it is not on the stack.
135
  int classElementPosition(Id _id) const;
136
137
  /// @returns true if the copy of @a _element can be removed from stack position _fromPosition
138
  /// - in general or, if given, while computing @a _result.
139
  bool canBeRemoved(Id _element, Id _result = Id(-1), int _fromPosition = c_invalidPosition);
140
141
  /// Appends code to remove the topmost stack element if it can be removed.
142
  bool removeStackTopIfPossible();
143
144
  /// Appends a dup instruction to m_generatedItems to retrieve the element at the given stack position.
145
  void appendDup(int _fromPosition, langutil::SourceLocation const& _location);
146
  /// Appends a swap instruction to m_generatedItems to retrieve the element at the given stack position.
147
  /// @note this might also remove the last item if it exactly the same swap instruction.
148
  void appendOrRemoveSwap(int _fromPosition, langutil::SourceLocation const& _location);
149
  /// Appends the given assembly item.
150
  void appendItem(AssemblyItem const& _item);
151
152
  static int const c_invalidPosition = -0x7fffffff;
153
154
  AssemblyItems m_generatedItems;
155
  /// Current height of the stack relative to the start.
156
  int m_stackHeight = 0;
157
  /// If (b, a) is in m_requests then b is needed to compute a.
158
  std::unordered_multimap<Id, Id> m_neededBy;
159
  /// Current content of the stack.
160
  std::map<int, Id> m_stack;
161
  /// Current positions of equivalence classes, equal to the empty set if already deleted.
162
  std::unordered_map<Id, std::set<int>> m_classPositions;
163
164
  /// The actual equivalence class items and how to compute them.
165
  ExpressionClasses& m_expressionClasses;
166
  /// Keeps information about which storage or memory slots were written to by which operations.
167
  /// The operations are sorted ascendingly by sequence number.
168
  std::map<std::pair<StoreOperation::Target, Id>, StoreOperations> m_storeOperations;
169
  /// The set of equivalence classes that should be present on the stack at the end.
170
  std::set<Id> m_finalClasses;
171
  std::map<int, Id> m_targetStack;
172
};
173
174
template <class AssemblyItemIterator>
175
AssemblyItemIterator CommonSubexpressionEliminator::feedItems(
176
  AssemblyItemIterator _iterator,
177
  AssemblyItemIterator _end,
178
  bool _msizeImportant
179
)
180
0
{
181
0
  assertThrow(!m_breakingItem, OptimizerException, "Invalid use of CommonSubexpressionEliminator.");
182
0
  unsigned const maxChunkSize = 2000;
183
0
  unsigned chunkSize = 0;
184
0
  for (
185
0
    ;
186
0
    _iterator != _end && !SemanticInformation::breaksCSEAnalysisBlock(*_iterator, _msizeImportant) && chunkSize < maxChunkSize;
187
0
    ++_iterator, ++chunkSize
188
0
  )
189
0
    feedItem(*_iterator);
190
0
  if (_iterator != _end && chunkSize < maxChunkSize)
191
0
    m_breakingItem = &(*_iterator++);
192
0
  return _iterator;
193
0
}
194
195
}